LLVM-IR(LLVM中間コード)用コンパイラの作り方 その4

前回の続き

前回の構文の処理で、「命令」と書いていた部分を細かく書く。

構文として、
 命令 = 代入文 または
 命令 = PRINT命令 + 数式 または
 命令 = GOTO命令 + 行番号
などと、定義する。
他にも命令文は増えるのだけど、今は省略。

そして、代入文は
 代入文 = 変数 + 「=」 + 数式
 数式 = 項 + 演算子 + 数式 または 数式 = 項
 項 = 単項演算子 + 項 または
 項 = 「(」 + 数式 + 「)」 または
 項 = 変数 または
 項 = 定数
というように定義する。

例えば、
 A = A + 10という代入文が与えられた場合には、構文解析ソフトは次のように木構造を作る

 代入文 ---+--- 変数(A)
            |
            +--- 数式 ---+--- 演算子(+) ---+--- 変数(A)
                         |                 |
                         +--- なし         +--- 定数(10)

木構造を辿って処理していくと、次のような処理順序になる。
 代入文
 変数(A)
 数式
 演算子(+)
 変数(A)
 定数(10)

それに対してコンパイラのコード生成を作ると、
 最初の処理の代入文のところでは、木構造の情報を参照して代入先の変数の情報を得て、それを代入文という情報とともにスタックに積む(木構造を辿って直下の未処理ノードが無くなったときに取り出して処理する)
 もし変数Aが初出の場合には、変数を生成してゼロで初期化するコードを生成する
 次の処理の変数(A)は直前の代入文のときに処理済みなので何もしない
 その次の数式は、なにもしない
 その次の演算子(+)は、スタックに積む
 次の変数(A)は、変数A(Aとラベルを付けたメモリ)からレジスタ1に値を取り出すコードを生成する
 次の定数(10)は、即値の10をレジスタ2に入れるコードを生成する
 ここで演算子(+)の直下のノードが処理済みになるので、スタックを取り出す
 スタックに積まれたのが演算子(+)なので、レジスタ1とレジスタ2を加算し、結果をレジスタ1に入れるコードを生成する
 さらに未処理ノードが無くなって、次のスタックの代入文を取り出す
 スタックの代入文の処理で、代入先の変数A(Aとラベルを付けたメモリ)にレジスタ1の内容を書き込むコードを生成する。

結果、生成されたコードは、
 変数Aを定義、ゼロで初期化
 変数Aのメモリをレジスタ1に取り出す
 定数10をレジスタ2に入れる
 レジスタ1とレジスタ2を加算し、レジスタ1に入れる
 レジスタ1の内容を変数Aのメモリに書き込む
となる。

具体的にLLVM-IR(LLVM中間コード)でこれを生成すれば、LLVM-IR用のコンパイラができる。
(Javaバイトコードで生成すれば、Javaバイトコード用のコンパイラになる。)


続く




コメントを残す

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

Time limit is exhausted. Please reload CAPTCHA.

× 3 = 15