LLVM-C API を使った自作コンパイラでの continue と break の話
対象
#LLVM-C API 初学者
内容
https://github.com/mzuhi5/tiny_c_compiler の続き。
ループ文の中の continue/break の実装は少し不思議な感じがします。四則演算やら他の制御文とは少し毛色が違っていて、実装する時に本当にこんなこと必要なのかな?と思ってました。
continue/break 文は、もちろん繰り返し文の中に置きますが、ブロックの中では他の文も呼び出します。その中で他の繰り返し文も呼ぶことがありますから、前の continue/break の行き先は覚えておかねばなりません。いわば入れ子になった状態の2値をどこかでずっと持ち続けているわけです。
以前アセンブリを吐くコンパイラを作成した時は、リンクリストをそれ用の関数で管理したのですが、ジェネレータの関数も同様に入れ子構造になっているってことで、今回は引数として持たせてしまいました。
gen_while(node){
...
LLVMValueRef start_block = ...;
LLVMValueRef exit_block = ...;
statement(node->rhn, start_block, exit_block);
...
}
statement(node, cont_block, break_block){
...
if(node->id == ND_WHILE){
gen_while(node);
} else if(node->id == ND_BREAK){
LLVMBuildBr(builder, break_block);
...
}疑似コードで書くとこんな感じ。これなら関数がスタックに積まれるのと、パース対象のコードの入れ子構造がマッチするので大丈夫かなと。continue/break をずっと持ち歩かなきゃいけませんけが、小さなコンパイラなので十分かなと。
コードジェネレータが expr() 部分が gen_code() と分けてあるのは、式だけ担当の expr() は continue/break 引数が要らないからってだけです。