中間コードを考える

バイトコードとも云う。
CPUが直接実行できる機械語(バイナリーコード)の多くはCPUのメーカーやシリーズごとに違っている。その理由はプログラムを載せるメモリは昔からアクセス速度が遅く、かつ小容量で、かつ高価であったので、使用頻度が多い命令ほど短く作られ、また少ない機械語で多くのことができる方がお得だったので、どんどんと入り組んだ進化の道を進んでいったためである。
これに対して中間コードはプログラムが読みやすいように作られている。
命令(4ビット)レジスタ指定(0~14)(4ビット)
命令(4ビット)メモリ指定(0xff)(4ビット) + メモリアドレス
な感じ。
一時期はCPUチップの中に多くのレジスタを配置し、遅いメモリをアクセスするよりも速く処理できることがよしとされた時期もあるが、今ではCPUチップに6Mバイトのレベル3キャッシュまで載るようになっているので、スタック演算マシンでもそれほど遅くなくなってしまったので・・・
今回はスタックマシンとしてバイトコードを作ることにする。
つまり、Add命令にはレジスタ指定もなく、Add(0x01)となり、演算系の命令は0x01~0x0f となり、最大16種類(Not-Operationを含む)の演算が可能になる。
スタック命令は、PUSH(0x11)、POP(0x12)、CALL(0x13)、RETURN(0x14)の4種類とスタック上の任意の位置のデータを転送する命令(0x15)で、データもlong(8byte)のみ操作可とする。
後はIF(0x16)とJUMP(0x17)命令があるのみ。
さてそうなると、スタックに0や1をどうやってセットするのだろうか?
実行直前に0や1を設定済みの外部変数を全てスタックするようにできればいいが、各外部変数にアクセスするための命令が必要だし、外部変数を持ついくつかの中間コードを組み合わせる場合を想定すると実行前に全ての外部変数をスタックに積む必要があり、ダイナミックに中間コードを増強できない可能性が高いので、実行コード、外部変数、スタックを分けてメモリに配置することが多い。
ここではそう面倒なことは考えず、作った中間コード相対アドレス参照モードを用意し、各外部変数を読み書きする命令(0x18,0x19)として実装する。多分、異なる中間コードファイルのコードを実行するCALL(その2:0x1a)も必要だろう。
さて次は例外処理の実装である。
あの try ~ catch ~ finally ~だ。
しかし関数内の関数として実装してしまえば、CALLとRETURNとIFで代用できるが、スタックが進むので、それならforやwhileも同様の実装でいいだろう。
と適当に並べてみた。
あとは、どこで使うのか?
スキリプトの正規化表現と文法の定義テキストは左辺と右辺だけではなく、その右側に対応する中間コードの生成を定義することで、中間コードが勝手に作られるようになる。
 
 
 
 
 




コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA