てけもぐ Tech 忘備録

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 引数が要らないからってだけです。