Concurrent Clean : ABCマシン(14) : JavaとCleanのスタック
JavaもCleanもスタックマシンのVMを持つのですが、その中身は随分違います。
まず、Java。
JVMのアセンブリ風言語のjasminでメソッドを記述すると、次のような書き出しで始まります。
.method public static main([Ljava/lang/String;)V .limit stack 3 .limit locals 1
冒頭に、メソッド内で使うスタックサイズとローカル変数領域のサイズを宣言しています。これが、実際に使用するサイズよりも少ないと、クラスローディングの際にエラーになります。呼び出し元の関数のスタックと呼び出し先の関数のスタックは、完全に分断されており、裏から手を伸ばすようなことはできません。
スタックは、関数呼び出しと関数の返値の受け取りに使います。関数が呼び出されると、引数はローカル変数に格納されます。
次に、Clean。
CleanのVMはABCマシンですが、このマシンは2つのスタックを持ち、ローカル変数はありません。
- A-Stack
- ボックス化されたグラフのスタック
- B-Stack
- 非ボックス化された基本型値のスタック
ABCマシンはGRS(項グラフ書き換え系)というシステムを使ったVMですが、A-Stackはそのシステムを直接表現したスタックで、B-Stackは効率性のためにそのシステムをバイパスしたスタックです。
関数呼び出しと関数の返値の受け取りはもちろん、ローカル変数がないので、引数もスタック上で取り扱われます。スタックがローカル変数の代わりもするため、スタックトップだけではなく、スタック上の任意の深さの要素に対しても、直接アクセスできることはJVMにはない特徴です。
-
-
- -
-
さて、同じスタックマシンといっても、このように違いがあるので、ABCコードをJVMに効率よく変換する際には、まっすぐなやり方では上手くいきません。
効率的な実装を行うには、ABCコードの挙動を分析して、スタック上で表現している計算をローカル変数を使った表現に変換してやることが適切です。また、ABCマシンと違い、JVMはボックス化した値と非ボックス化した値を同じスタック上で扱えるので、関数呼び出しの際の値の受け渡しを、そのような特徴に沿った形に適切に変換してやります。
ローカル変数のサイズは任意(だと思う)ので、あまり制約条件を意識しないで変換してやることができると思うので、この変換自体は、ちょっと考えれば書けるものではないかと思います。
-
-
- -
-
で、今後の方針ですが、ひとまず、A-Stack, B-StackをJVMのスタックを使わない方向で実装してしまおうと思います。まずは、ABCマシンをJVM上で表現できることを証明することを目標におきます。その目標を達成したら、上で述べたような効率的な変換を考えようと思います。