PyPyにおける静的解析-- 動的言語に静的型を --by cocoatomo第8回ありえるえりあ勉強会2012/01/20121݄20༵ۚ
View Slide
アジェンダ•自己紹介•研究テーマ「静的解析」•PyPy ではどう使われているか•静的解析とは•注釈 (annotation) とは•変数に注釈を付ける121݄20༵ۚ
発表内容•この発表で得られるもの•RPython Toolchain の概観•translatorshell.py での遊び方•背景にある理論•型の見付け方•主に静的解析まわり121݄20༵ۚ
アジェンダ•→自己紹介•研究テーマ「静的解析」•PyPy ではどう使われているか•静的解析とは•注釈 (annotation) とは•変数に注釈を付ける121݄20༵ۚ
お前, 誰よ?•ID: cocoatomo•blog: Elliptium•言語•仕事: Java, 趣味: Python•活動•pypy-ja•Python 公式ドキュメント翻訳121݄20༵ۚ
お前, 誰よ?•理論好き•所属その1: 品川にある浅草っぽい会社•所属その2: 社会人博士•プログラムの検証まわりをやりたいなー, と今年度入学•今日はこっちの活動に関係する話です121݄20༵ۚ
好きなエディタアリエルネットワークでは入社時に好きなエディタを言わされると聞いて...Emacs2006年くらいから CarbonEmacs & Meadow, CocoaEmacs & Emacs (on Windows) と変遷121݄20༵ۚ
良く使うMajor Mode•ReST mode (rst.el)•昔は org-mode でメモ書き•今は Sphinx の影響でもっぱら reST•入力はもちろん DDSKK•AquaSKK も使用•フォント•Inconsolata + Takao•このスライドは Osaka ですが121݄20༵ۚ
アジェンダ•自己紹介•→研究テーマ「静的解析」•PyPy ではどう使われているか•静的解析とは•注釈 (annotation) とは•変数に注釈を付ける121݄20༵ۚ
研究テーマ「プログラムの品質を楽に上げたい」プログラム実行前の検証の自動化その手段として,・実行しなくても分かる情報・部分的に実行して分かる情報が知りたいe.g. Eclipse, FindBugscf. Formal Method (形式手法)121݄20༵ۚ
検証手法•検証と言うと普通は…⋯…⋯↓•テスト•具体値で実行•最も馴染み深い•偽陽性 (false positive) が無い(^-^)•網羅性が悪い(´・ω・`)121݄20༵ۚ
モデル検査•実行パスを全検査•目的: 反例 (エラーを起こす入力値) を見付ける•e.g. Alloy, Java PathFinder (JPF)•網羅性は高い (^-^)•実行パターン数が爆発する(´・ω・`)121݄20༵ۚ
静的解析•記号実行•変数を本当に「変数」のまま実行•最弱事前条件 (Weakest Precondition, WP)•ある地点に到達する (入力値の) 条件を求める•網羅性が高い (^-^)•偽陽性 (false positive) の可能性(´・ω・`)121݄20༵ۚ
参考文献以下の論文の Introduction に簡単にまとめてあります.より詳しい文献への refer もあります.Combining unit-level symbolic execution and system-level concrete execution for testing nasa software (ISSTA ’08)121݄20༵ۚ
発表の注意事項•質問は随時受け付けます•斧を投げる場合は, お手柔らかにお願いします121݄20༵ۚ
アジェンダ•自己紹介•研究テーマ「静的解析」•→PyPy ではどう使われているか•静的解析とは•注釈 (annotation) とは•変数に注釈を付ける121݄20༵ۚ
PyPy では...• 静的解析の手法が使われている• 変数の型の推論• 静的単一情報 (Static Single Information, SSI) 形式への変換• 変数は必ずローカル変数で代入は1回のみ• Decision Procedure• 型情報の伝播に類似が見られる• cf. Nelson-Oppen• → AOT コンパイルや JIT コンパイルの基礎となる!121݄20༵ۚ
RPython Toolchain の概観+---------+---------+| CPython | PyPy |+=========+=========+| Python | Python | 静的解析!!+-------+ +---------+ || C API | | RPython |--, v+-------+-+---------+ | translate| bare C | backend |+---------+---------+backend = C, C with JIT, JVM, .CLI121݄20༵ۚ
Java PathFinder との類似PyPy 制限された Python で Python を実装Java PathFinder Java で JVM を実装 instruction も Java オブジェクト! 意味論が自由に操作できる!!121݄20༵ۚ
翻訳の概観•Control Flow Graph (フローチャート) 形式に変換•変数に注釈 (annotation, 型) を付加•backend の型に変換 (RPython Typing, RTyping)•例外処理の変換•GC 処理の変換•C ソースコードの生成 (backend が C の場合)121݄20༵ۚ
Control Flow Graph121݄20༵ۚ
Control Flow Graph•フローチャート•ブロックを矢印でつなぐ•関数 → FunctionGraph インスタンス•文 → Block インスタンス (複数かも)•pypy/objspace/flow/model.py121݄20༵ۚ
Annotation (注釈)•型のこと (≠ Python の型)• = 値の集合•静的型に変換するために必要!•この情報は JIT Compile でも使われるよ!•動的言語とは言え, 開発者の頭の中には型はある (はず)121݄20༵ۚ
翻訳で遊ぼう!!何はともあれ遊びましょう! (要 PyGame)$ hg clone ssh://[email protected]/pypyja/pypy pypyja$ cd pypyja$ python pypy/bin/translatorshell.py# 色々説明がずらずらと...>>> def succ(x):... return x + 1>>> t = Translation(succ)>>> t.view()121݄20༵ۚ
Control Flow Graph!121݄20༵ۚ
型付けよう!>>> t.annotate([int])>>> t.view()121݄20༵ۚ
型付いた!121݄20༵ۚ
変数をクリック!•x_0 は元は x だった•t.annotate([int]) によって x_0 は int と判明•SomeInteger という注釈が整数を表す•v0 は x + 1 の計算結果•これもやっぱり整数 (SomeInteger)•v0 = x + 1 と return v1 に分離121݄20༵ۚ
もうちょっと遊ぶ>>> def ssi(x, y):... x = x + y... return x... >>> t = Translation(ssi)[flowgraph] (__main__:1)ssi>>> t.annotate([int, int])>>> t.view()121݄20༵ۚ
さてどうなるか?変数 x に注目…⋯…⋯121݄20༵ۚ
こんなグラフに121݄20༵ۚ
変換後は•代入する前の x は x_2 になった.•代入された後の x は v18 になった.•return がある Block に v18 を渡すと v19 に名前が変わっている.•値が変化するとき, 関数をまたぐときは必ず変数名を変える (これが SSI)121݄20༵ۚ
もうちょっと遊ぶ>>> def generalize(x):... if x:... a = 3... else:... a = False... return a...>>> t = Translation(generalize)>>> t.annotate([bool])>>> t.view()121݄20༵ۚ
変換後は•if のところで分岐が作られる•v1 には 3 も False も来る可能性がある•v1 の注釈は SomeInteger•実は SomeBool は SomeInteger•True = 1, False = 0 となっている•上手いこと型の計算をやってくれている!!121݄20༵ۚ
是非遊ぼう難しくないので, 是非遊んでみよう!121݄20༵ۚ
参考記事•Let's translate.py! - PyPy Advent Calendar 2011•http://blog.elliptium.net/2011/12/Let-s-translate-py---PyPy-Advent-Calendar-2011•Mac で PyGame のインストール•http://blog.elliptium.net/2012/01/Mac-PyGame121݄20༵ۚ
アジェンダ•自己紹介•研究テーマ「静的解析」•PyPy ではどう使われているか•→静的解析とは•注釈 (annotation) とは•変数に注釈を付ける121݄20༵ۚ
さてここから理論の話になるわけですが121݄20༵ۚ
さてさて正直, 全速力で解説しないと終わらないわけで121݄20༵ۚ
さてさてさて頑張って追っかけてきてください!121݄20༵ۚ
ググる単語•型の話•導出木•Types and Programming Language•SSI の話•SSA, SSI•Decision Procedure の話•Nelson-Oppen121݄20༵ۚ
発表の下敷き• PyPy ドキュメント• 原文: http://doc.pypy.org/en/latest/• 訳文: http://readthedocs.org/docs/pypyja/en/latest/• translation.html, rtyper.html のあたり• まだ翻訳が足りない → I want you for pypy-ja!• EU report about translation• https://bitbucket.org/pypy/extradoc/raw/tip/eu-report/D05.1_Publish_on_translating_a_very-high-level_description.pdf121݄20༵ۚ
動的言語での静的解析↑「お前は何を言っているんだ!?」↑(画像略)121݄20༵ۚ
静的解析の目標•プログラムが正しく動作することの検証•もしくはその条件を求める•バグの発生条件を求める•プログラム修正の妥当性の検証•などなど...•↑以上を実行せずに解析する121݄20༵ۚ
なんで静的解析が必要なの?RPython Toolchain の目的「変数に型を付け静的型付け言語のプログラムに変換する.そしてその情報から高速化を行う.」↓静的解析が必要121݄20༵ۚ
静的解析の敵•一般的な静的解析を阻む要素•条件分岐 (実行パターンが増える)•ループ (事後条件求めるのが難しい)•多態 (継承, 動的型付けなど)•eval (値の中身で処理が変わる)•などなど...121݄20༵ۚ
どう解析しているのか?•RPython でさえ動的すぎる•変数の型宣言無い•変数に複数回代入できる•クラスの継承がある•→ しょうがないので前から順に解析していく(´・ω・`)•Hindley-Milner 型推論みたいなかっこいいことはできない(´・ω・`)121݄20༵ۚ
アジェンダ•自己紹介•研究テーマ「静的解析」•PyPy ではどう使われているか•静的解析とは•→注釈 (annotation) とは•変数に注釈を付ける121݄20༵ۚ
注釈とは注釈 = 型 = 値の集合Python の型とは関係無いです.型理論そのものについては+ Types and Programming Languages+ プログラミング言語の基礎概念 (五十嵐淳)あたりを読むと良いかも121݄20༵ۚ
Annotator (注釈器)•RPython スクリプトを前から順に解釈•扱う注釈•str, char, float などの組み込み型•オブジェクト•collection (list, dict,...)•定数•Nullable な注釈もある121݄20༵ۚ
注釈の階層構造注釈 = 型 = 値の集合なので集合の包含関係による半順序 (≦) が決まる. その頂上と底辺には Top と Bottom が置かれる. (=「束構造をなす」)Bottom ≦ Char ≦ Str ≦ NullableStr ≦ Top121݄20༵ۚ
こんな感じ121݄20༵ۚ
オブジェクトも同様class A(B): passという継承関係があったときBottom ≦ Instance(A) ≦ Instance(B) ≦ NullableInstance(B) ≦ TopBottom ≦ None ≦ NullableInstance(A) ≦ NullableInstance(B) ≦ Topとなる. (要は is-a の関係)121݄20༵ۚ
補足•Top はどんな値でも入る型•Object (Java), Any (Scala)•primitive のことは横に置く•Bottom はどんな値も入らない型•Nothing (Scala)•≦ は もいるかも121݄20༵ۚ
List だと•List の要素は全て同質 (homogeneous, 要素の型が同一) として扱う•List は互いに包含関係は無い•collection 間の is-a の関係は難しい.•e.g. Java の配列 (における失敗)121݄20༵ۚ
アジェンダ•自己紹介•研究テーマ「静的解析」•PyPy ではどう使われているか•静的解析とは•注釈 (annotation) とは•→変数に注釈を付ける121݄20༵ۚ
注釈付けのルール•ルールとは, ある変数の型情報から別の変数の型情報を導き出す形式的規則•型を知る手掛かり•型導出木に近い雰囲気•1つの文からいくつかルールが出てくる•次で見る通り1つ1つは難しくない•このルールに従って注釈を決めていく121݄20༵ۚ
ルールで使う定義•A: 前に出てきた型階層 (束, lattice)•V: 変数の集合•E: V 上の同値関係•ある変数どうしの型が同じ, という情報•b: V から A への関数•ある変数からその注釈への対応付け121݄20༵ۚ
イメージV = {a, b, c}E = {(a, c)}b = {a: int, b: str, c: int}のような感じ. (Python 風に書いているが, 実装とは無関係です)注釈処理が進むに従って, V, E, b のサイズは大きくなっていく.121݄20༵ۚ
定義 (続き)•(b, E): 状態•要は変数の情報•状態 (b’, E’) は状態 (b, E) より一般的 (general): b(v) ≦ b’(v) かつ E ⊂ E’•型情報は緩く, 同値情報は多く•処理が進むと状態はより一般的になっていく121݄20༵ۚ
極端な状態•最も一般的な状態 (= 有用な情報無し)•b_max(v) = Top,E_max = {(v1, v2) | v1, v2 ∈ V}•全部の変数が object 型•最も一般的でない状態 (= 全ての変数が未初期化)•b_min(v) = Bottom,E_min = {(v, v) | v ∈ V}•注釈付けの開始前の状態121݄20༵ۚ
注釈器の目標•最も一般的でない状態を求めること•基本的には処理の流れに沿って, 初期状態 (b_min, E_min) を拡大させていく•ループ ← 静的解析の敵!•一般化されなくなるまで注釈を計算しつづける121݄20༵ۚ
例: __add__•中置演算子“+”についてのルール•オーバーロードされているので複数ルールがある•z := add(x, y) という状況を考える•(z = x + y でいいんだけど文献に合わせた)121݄20༵ۚ
例: z = x + y その1z := add(x, y),b(x) = Int, Bool ≦ b(y) ≦ Int---------------------------------b.update({z: Int})121݄20༵ۚ
例: z = x + y その2z := add(x, y),Bool ≦ b(x) ≦ Int, b(y) = Int---------------------------------b.update({z: Int})121݄20༵ۚ
例: z = x + y その3z := add(x, y),Bool ≦ b(x) ≦ NonNegInt,Bool ≦ b(y) ≦ NonNegInt---------------------------------b.update({z: NonNegInt})121݄20༵ۚ
例: z = x + y その4z := add(x, y),Char ≦ b(x) ≦ NullableStr,Char ≦ b(y) ≦ NullableStr---------------------------------b.update({z: Str})注. x または y が None である可能性はテストの方で潰しておく必要がある121݄20༵ۚ
案外簡単?ここまでのパターンでの注釈付けは素直list やユーザが作成したクラスのオブジェクトのような他のオブジェクトを格納できる何かが出てくると難しくなる.(= aliasing と呼ばれる)C のポインタでも同様の静的解析上の困難が生まれる121݄20༵ۚ
List の注釈•将来どこで要素が格納されるか分からない•List インスタンスを参照しているところを全て把握していないといけない•List の注釈は難しい! 困った!!•→ ではどうやって対処するか?121݄20༵ۚ
merge 手続きと meta-rule•merge 手続き•情報を追加する手続き•meta-rule•情報を伝播させるルール•Nelson-Oppen: Simplification by cooperating decision procedures121݄20༵ۚ
merge 手続き(再び translatorshell.py を起動)>>> def bool_list():... l = [True]... return l # l <= List(Bool)... >>> t = Translation(bool_list)[flowgraph] (__main__:1)bool_list>>> t.annotate()>>> t.view()121݄20༵ۚ
merge 手続き>>> def merge():... l = [True]... l.append(3)... return l # l <= List(Int)... >>> t = Translation(merge)[flowgraph] (__main__:1)merge>>> t.annotate()>>> t.view()121݄20༵ۚ
ごめんなさい上の2つの例は f = t.compile_c() するとエラーで落ちちゃいます. 原因は分かっていませんm(_ _)m解読してくれる方は pypy-ja へ↓pypy.rpython.error.TyperError: don't know how to convert from > to 121݄20༵ۚ
merge の仕事•代入が発生したときに, 変数の注釈を (必要なら) 一般化する•注釈 a を変数 x に付けるコード•b.update({x: a ∨ b(x)})•E はそのまま•このパターンは簡単•ただし, List に関しては特別扱い121݄20༵ۚ
List 注釈を merge注釈 List(v) を List(w) という注釈が付いている変数に mergeb はそのままE.append((v, w))List の後ろに要素を表す変数 v, w があるのがポイント!!121݄20༵ۚ
list#__getitem__List の要素の変数はこんなふうに使うz := x[y], b(x) = List(v)---------------------------E.append((z’, v))b.update({z: b(z’)})x: List(v) の方の要素型の変更が E にある (z’, v) を通して z の注釈に波及する!!121݄20༵ۚ
meta-rule•(v, w) ∈ E が判明したら以下の2つの作業をする•v の注釈を w に merge•w の注釈を v に merge•merge したことにより, 再度 E に追加があった場合は止まるまで繰り返す121݄20༵ۚ
資料の残り部分•EU report about translation•6.5--6.8 他の構文に対応するルール•6.9 一般化, 停止性, 健全性に関する証明•6.10 動的な取り扱いについて諸々•7 RTyping について121݄20༵ۚ
大丈夫ここまでついて来れた人なら読めます!121݄20༵ۚ
まとめ•RPython Toolchain は動的型付き言語を解析, 変換し, 静的型付け言語のプログラムを生成する•RPython Toolchain には翻訳という処理がある•翻訳では変換の準備として RPython で書いた処理系に型を付ける処理がある•その際, 静的解析の手法が使われている121݄20༵ۚ
おしまいご静聴ありがとうございました121݄20༵ۚ