Let's translate.py! - PyPy Advent Calendar 2011

お前, 誰よ?

改めまして cocoatomo と申します. 技術的な話はこの ID とペンギンアイコンで通しているので, 他の場所で cocoatomo と見たらきっと私です.

プログラムの静的解析に興味があり, PyPy の処理過程のうちでも前半にある Control Flow Graph への変換および注釈付け (メタデータの付与) を勉強中です. 正直, 後半の JIT とかはあんま興味無いです. そこは @chlere さんという優秀な方がいるので, そちらにお任せして色々教えてもらっています.

今回の記事は PyPy Advent Calendar 2011 の4日目として書いています.

翻訳とは?

さて, 表題にある translate.py とは何ぞや? 翻訳? 何を翻訳? と思われたと思います. 実は私にとって「翻訳」は2重の意味があって, PyPy の処理過程の一部である translate.py のことと, 私が行っている PyPy の公式ドキュメントの翻訳です. (しかも translation.html の翻訳なので, 「翻訳」の翻訳です.)

translate.py は ${PYPY_HOME}/pypy/translator/goal/translate.py にあります. 私の翻訳は PyPy - RPython toolchain — PyPy 1.6 documentation に置いてあります. まだ翻訳は完了していませんが, これを読んでいけば PyPy がどんな処理を行っているかのイメージが付くでしょう.

PyPy が行っていることを大雑把に説明すると, RPython という Python っぽい言語で書かれたプログラムを変換して, 型などの情報を付加して, そこから色々な言語のソースコードを出力します. この変換のことを翻訳と名前を付けたようです.

translatorshell.py

さて難しい話は置いておいて, まずは翻訳して遊んでみましょう.

環境

私は楽をするためこんな環境で作業しています.

  • Mac OS X 10.7 Lion (gcc は llvm-gcc)
  • PyPy のソース一式を bitbucket から取得
  • homebrew で pypy をインストール

モジュールまわりで面倒が無いように homebrew に pypy をビルドしてもらって使っています.

翻訳遊び

(flow モデル のあたりを参照しながら読み進めると良いかもしれません.)

$ cd $PYPY_HOME/pypy
$ pypy bin/translatorshell.py

と実行すると, なにやらメッセージとともに ``>>>> `` という PyPy のプロンプトが出てきます. Python と違って ">" が4つあるのが特徴です.

System Message: WARNING/2 (<string>, line 46); backlink

Inline literal start-string without end-string.

ここで適当な関数 succ を定義して, 翻訳処理に掛けます.

>>>> def succ(x): return x + 1
>>>> t = Translation(succ)
>>>> t.view()
http://desmond.yfrog.com/Himg615/scaled.php?tn=0&server=615&filename=fxlg.png&xsize=640&ysize=640

何かウィンドウが出てきてフローチャートみたいなものが表示されました. このウィンドウは Pygame のもので, 変換結果が目で見て分かる形で表示されます.

中身は succ が変換されたものなので見れば, まぁ分かるでしょう. ESC キーを押してウィンドウを閉じてください.

今度はこれにメタデータを付加しましょう.

今は succ には数値が来ることを期待しているので, succ の引数 x が int であることを PyPy に教えましょう.

>>>> t.annotate([int])
>>>> t.view()

最初に annotate メソッドを実行すると, とあるモジュールがコンパイルされるようです. gcc-4.0 という実行ファイルが求められるので, 私は

$ ln -s /usr/bin/gcc /usr/bin/gcc-4.0

とやや適当な対応をしました. とりあえずこれで動いているようです.

http://desmond.yfrog.com/Himg859/scaled.php?tn=0&server=859&filename=41917068.png&xsize=640&ysize=640

t.view() と再度フローチャートを表示すると, さっきと少し変わっているところがあります. x_0, v_0, v_1 という文字の色が変わってリンクになっています. 真ん中の四角の中に inputargs: x_0 とあるので, きっと succ(x) の x のことなのでしょう. クリックしてみます. 何やらメッセージが出ますが SomeInteger という文字列が見付かるでしょうか? これがさっき「succ の引数 x は int である」と教えた結果です. PyPy では "int" という型情報を SomeInteger というクラスで表現しています.

v_0 をクリックするとやはり同じように SomeInteger という文字列を含んだメッセージが出てきます. Backspace を押して最初の画面に戻ってみると, v_0 というのは v_0 = add(x_0, (1)) というものらしいです. 元々のプログラムでは return x + 1 と1つの文で書いていたものが v = x + 1; return v (←注. Python のソースコードとしては正しくない) と2つの文に分割されているようです.

int である x に 1 を加えた結果はもちろん int なのですが, 普通の Python ではこのような推論は行ってくれません. それはそもそも「x が int である」という情報を渡す口が無いからです. (Python3 で関数アノテーション入ったじゃないか, と言われそうですが, あれに推論の機能は無かったはずです. もし間違っていたら是非教えてください.)

PyPy によって int 型だと判明した v_0 を次の四角に渡し, 次の succ__2 という名前の四角で v_1 として受け取られ return されています. もちろん v_1 に来るのは v_0 という int なので v_1 も int 型です. その証拠に v_1 をクリックしてみましょう. v_0 と同じように SomeInteger という文字列がありますね.

蛇足ですが, 元々は同じ v という変数なのに v_0 とか v_1 という名前に分けられているのは 静的単一代入 (SSA) という形式に変換されているからです. この形式では, ある変数への代入は1回しか行えず, 再度代入するときには別の変数に代入しているものと看做します. この形式にしておいた方が何かと処理が楽なので, 変換や最適化の中間表現として採用されます.

さてここまでで succ が PyPy 内部でどう扱われるかを見渡すことができました. 今回は succ という簡単な例でしたが, 是非 for 文や if 文を含む関数をみなさんの手を動かして PyPy で変換してみてください.

まとめ

今回は理論的に掘り下げた話はせず, まずは PyPy に触れてみよう, translation.py で遊んでみよう, という話をしました. 実は別の話を書こうと思って準備をしていたところ, Pygame のウィンドウを出すためにモジュールをコンパイルするところで引っ掛かってしまい, せっかくなのでそこの部分について詳しく書きました.

Advent Calendar の2周目が回ってきたら理論的なところなどを書いてみようと思います.

次の担当は Masahito さん です. よろしくお願いします.

それでは.

Comments

blog comments powered by Disqus

Licenses