jvm backend in PyPy

PyPy (正確には RPython toolchain) の jvm backend への翻訳処理の設計を知りたくてソースコードを追い掛けてみた.

ややメモ書きっぽい感じだが, ほぼ自分用メモということで許してください.

(以下, パスは全て pypy のルートディレクトリを起点としている.)

入口

pypy/translator/jvm/genjvm.py の GenJvm クラス. たぶんだけど. pydoc はこんな感じ.

""" Master object which guides the JVM backend along.  To use,
create with appropriate parameters and then invoke
generate_source().  *You can not use one of these objects more than
once.* """

バイトコード吐いてるのが pypy/translator/jvm/generator.py の JVMGenerator. 既存の何かを使ってたわけではなく, 自作していたのか…… しかも raise NotImplementedError がそこかしこに見える……

これ作った人すごいなぁ. javac 相当のものを RPython で実装しているし. Antonio Cuni さんの仕事はどこまでなんだろうか?

GenJvm#generate_source

これを呼ぶと処理をしてくれるらしいのでここから読む.

メソッドの実装はこんな感じ.

def generate_source(self):
    """ Creates the sources, and returns a JvmGeneratedSource object
    for manipulating them """
    GenOO.generate_source(self)
    self.jvmsrc.set_jasmin_files(self.db.jasmin_files())
    return self.jvmsrc

Jasmin という jvm アセンブラを使っているようだ. Jasmin home page 処理は, 親クラスである GenOO の generate_source メソッドに処理を投げて, 出来上がった jasmin ファイルを jvmsrc 属性に格納して返しているようだ.

さてこのメインの処理を行っていそうなメソッドを覗いてみよう.

pypy/translator/oosupport/genoo.py を開く.

GenOO#generate_source

def generate_source(self):
    self.ilasm = self.create_assembler()
    self.fix_names()
    self.gen_entrypoint()
    self.gen_pendings()
    self.db.gen_constants(self.ilasm)
    self.ilasm.close()

う〜む, 綺麗だ. 文句の付けようが無い. だいたいどんな処理をやっているのか分かりやすくて良い.

GenJvm#create_assembler

まず処理の主体となるであろうアセンブラを用意しているところから見る. デフォルト実装は raise NotImplementedError となっていて, GenJvm クラスでは以下の実装になっていた.

def create_assembler(self):
    """ Creates and returns a Generator object according to the
    configuration.  Right now, however, there is only one kind of
    generator: JasminGenerator """
    return JasminGenerator(self.db, self.jvmsrc.javadir)

JasminGenerator クラスは pypy/translator/jvm/generator.py にある.

JasminGenerator のコンストラクタはいったん置いといて, 次の処理の中身を見てみる.

GenOO#fix_names

def fix_names(self):
    # it could happen that two distinct graph have the same name;
    # here we assign an unique name to each graph.
    names = set()
    for graph in self.translator.graphs:
        base_name = graph.name
        i = 0
        while graph.name in names:
            graph.name = '%s_%d' % (base_name, i)
            i+=1
        names.add(graph.name)

名前の付け替えをしてるだけらしい. graphs の中には FunctionGraph インスタンスが入っているものと予想. self.translator.graphs の中身を見たかったけど, 型が分からず断念. こういうときは型がすぐに判明する言語が嬉しいなぁ.

GenOO#gen_entrypoint

さて, 戻って次の呼び出し.

def gen_entrypoint(self):
    if self.entrypoint:
        self.entrypoint.set_db(self.db)
        self.db.pending_node(self.entrypoint)
    else:
        self.db.pending_function(self.translator.graphs[0])

文字通り全体のエントリポイントを作成している……のかな. 各インスタンスの構造が分からないと, すぐには分からんなぁ. とりあえず後で詳しく読むことにして, いったんパス.

GenOO#gen_pendings

pending ってことは何かの処理を待ってて, 順々に処理されていくのかな.

def gen_pendings(self):
    n = 0
    while self.db._pending_nodes:
        node = self.db._pending_nodes.pop()
        node.render(self.ilasm)
        self.db._rendered_nodes.add(node)
        n+=1
        if (n%100) == 0:
            total = len(self.db._pending_nodes) + n
            self.log.graphs('Rendered %d/%d (approx. %.2f%%)' %\
                       (n, total, n*100.0/total))

ここが処理の本体っぽいね. node.render(self.ilasm) がキモだろう. 後で詳しく読む.

Database#gen_constants

ここは GenOO.db という属性に設定された pypy/translator/jvm/database.py にある Database クラスのインスタンスのメソッド. 定数生成っぽいから後回しかな.

JasminGenerator#close

これは self.ilasm にある JasminGenerator インスタンスの close メソッドの呼び出し. 処理が終わったことの確認をしてるだけっぽい.

def close(self):
    assert self.curclass is None

再度 GenOO#gen_pendings

さて取り置きしておいた, このメソッドの中の node.render(self.ilasm) を深掘りしていこう.

この node というオブジェクトは self.db._pending_nodes.pop() で取得されていて, この db という属性は pypy/translator/jvm/database.py にある Database クラスのインスタンスだ.

このクラスは pypy/translator/oosupport/database.py にある Database クラスを継承してる. _pending_nodes 属性はこのクラスのコンストラクタで初期化されていて, その実態は Set インスタンスだ. ということは, ここでは pop の順序は不定でも良いという仕様なのか.

この _pending_nodes 属性に push しているところがあるはずで, それは追加する要素を引数に取る GenOO#pending_node メソッドだ.

これを呼んでいるところが GenOO か GenJvm にあるはずなんだが, GenOO#gen_entrypoint の中にしか見付からなかった. ここから先は Database の中身と node の実態を見ていかないと分からないのかもな.

ということで, Database クラスを見る.

Database

ここでは pypy/translator/oosupport/database.py 内の Database クラスと pypy/translator/jvm/database.py 内の Database クラスという同じ名前のクラスが登場してしまうので, 前者を OODatabase, 後者を JVMDatabase と呼ぶことにする.

(書きかけ)

Comments

blog comments powered by Disqus

Licenses