読者です 読者をやめる 読者になる 読者になる

lua, pforth, gforth の VM を読む

よむったってすぐには読めません。
lua の場合 4.0 までがスタック型で 5.0 以降はレジスタ型の VM になった模様(未確認)。で初期のコードがやっぱり読みやすい。lua-1.0 あるいは lua-1.1 のソースを読むとわかりやすい。lua.stx (おそらくシンタックス) をみて、そして、lua-1.1/src/opcode.h を見る。よくよくみると PUSHLOCAL の類と、STORELOCAL の類の opcode がある。PUSHGLOBAL も STOREGLOBAL もある。ということは、、、
どうやら巷のスタック型の VMコンパイルした結果を落とそうと思うと、ローカル変数を数え上げて、その分スタックをずらして変数領域を作り、基本的な演算はスタック操作で行うが結果の変数への出し入れは PUSHLOCAL や STORELOCAL で行う、、、、ということらしい。これなら確かにコンパイラが作ったコードをスタック型の VM でも別に違和感なくバイトコードの出力が出来そう。
ただし、SSA との相性が悪そうなのが気がかり。試したわけじゃないけど、コンパイルした中間言語SSACPS だと、無数にローカル変数ができてしまう。生き死にを勘定しながら最大のローカル変数の数はわかるからできないことはないのか?ローカル変数というよりローカル・レジスタになるね。
そうまでするなら fp なしで、直接 sp からアクセスできるようにしてしまえばよさそうだけど。そうするとだんだんスタックマシンじゃなくなる。

pforth はどうだ?csrc/pf_inner.c を見ればよい。switch/case で作ってあって見やすい。SP_STORE とか RP_STORE とかがあって、要はスタックポインタのアドレスやリターン用のスタックのアドレスを知ることが出来る。スタックをずらしてアドレスを知ればそこからアクセスできる。FORTH プログラマが積極的にこのようなことをするかどうかは知らないが、用意されているということは使うのでしょう。アドレスがわかれば2つあるスタックを上手に使ってフェッチとかしながら器用に使いまわせそうだ(それが FORTH プログラマ?)。

gforth は難しすぎ。だが、gforth-0.2.1 までさかのぼってみればだいぶみやすくなる。ついでに書くと gforth-0.4.0 から各 CPU 対応が明確になっている。alpha 対応をしているので 64bit の long long 対応をしているようだ。alpha のおかげで先駆けて 64bit 対応ができているんだね。primitives.i というのがコードのようだ。vmgen はこのころ明確にはなかったけど、すでに forth で forth を作っている模様。よくみると FTOS なんてのがある。浮動小数点専用のスタック。double にもできるようだ。

sp@ と sp! があるので、これでスタックポインタのアドレスを知ることが出来る。rp@ と rp! もあるようだ。これでローカル変数の定義ができる、、、、のだろう。ヘビーな FORTH プログラマにとっては。すげーな。と感心。もちろん、メタプログラミングをからませて複雑なことをするんだろう。アドレスを使ったメタプログラミングができるのは FORTH だけ!?FORTH 万歳。って今のところ、使いこなせないけどね。これ、はじめたら年単位の勉強になる。

そういや、Retro はどうなっているんだ。とふと気になった。vm/complete/retro.c で一見なにもしてなさそうだったのが、、、よーくみると、、、rxDeviceHandler でやってるんだ。Capabilities の -5 が SP を返して、-6 が RSP を返すようになっている。なんで opcode にいれてないんだ?なんか知らんがハードと opcode を分離したかったんだな。