コンパイラ作成(47) バグ修正
バグ2件
最近恒例になってきたバグ修正。今回は2件だよ。
main()
{
print(42);
}
前回、スタックフレームの作成を修正したけど、こういう風に変数が1個も使われてない場合に、sub rsp,0ってコードが出力されてたよ。これでも動くから問題ないんだけど、恰好悪いんで修正することにした。
// 関数の呼び出し int main() { int a = answer(); printf("%d\n",a); } int answer() { // Answer to the Ultimate Question of Life, the Universe, and Everything return 42; }
代入a = answer();がコンパイルできなかったよ。こんな単純な場合のチェックが漏れてたとはちょっとショック。
function
1件目の修正。
# 関数の構文解析 def function() @labelcnt = 0 @lvars = Hash.new @lvarsize = 0; rettype = nil kind, str = @lex.gettoken if kind == TK::RESERVE && str == "int" then rettype = str kind, str = @lex.gettoken end if kind == TK::EOF then return false end if kind != TK::ID then perror "expected identifier" end @funcname = str kind, str = @lex.gettoken if kind != TK::SYMBOL || str != "(" then perror end kind, str = @lex.gettoken if kind != TK::SYMBOL || str != ")" then perror end codegen ".global "+@funcname codegen @funcname+":" codegen " push rbp" codegen " mov rbp, rsp" # 仮のコードを作成 idx = codegen " sub rsp, xx" kind, str = @lex.gettoken if kind != TK::SYMBOL || str != "{" then perror end kind, str = block codegen ".RET_" + @funcname + ":" # 16の倍数になるように揃える size = (@lvarsize+15) / 16 * 16 # 正しいサイズでコードを生成し置き換える if size != 0 then codechange idx," sub rsp, #{size}" codegen " add rsp, #{size}" else codechange idx,nil end codegen " pop rbp" codegen " ret" if @functions[@funcname] != nil then perror "redefinition of \"" + @funcname + "\"" end @functions[@funcname] = [rettype,[]] p @lvars if $opt_d # デバッグ用 codeflush @funcname = nil return true end
sizeが0だったらnilに置き換えるようにした。
codeflush
で、nilのときは出力しないように。
# アセンブリコードの出力 def codeflush() @codebuffer.each do |code| if code != nil then @fout.puts code + "\n" end end @codebuffer = [] end
これで1件目の修正はOK。
codegen_assign
続いて2件目の修正。
# 代入のコード生成 def codegen_assign(el) if el.size != 3 then perror end =begin if !el[2].kind_of?(Array) && el[2].kind == TK::NUMBER then codegen " mov eax, " + el[2].str elsif !el[2].kind_of?(Array) && el[2].kind == TK::ID then v = @lvars[el[2].str] if v == nil then perror "undeclared variable \"" + el[2].str + "\"" end codegen " mov eax, dword ptr [rbp - " + v[1].to_s + "]" else codegen_el el[2] end =end codegen_el [el[2]] if el[0].kind_of?(Array) then perror end if el[0].kind != TK::ID then perror end v = @lvars[el[0].str] if v == nil then perror "undeclared variable \"" + el[0].str + "\"" end codegen " mov dword ptr [rbp - " + v[1].to_s + "], eax" end
代入演算子の右側を求める部分がおかしかったよ。独自に色々やってたけど関数呼出の対応が抜けてた。でよく考えたらcodegen_el [el[2]]の呼び出しで済むことに気付いたよ。10行のコードが消えてすっきり。
動作テスト
ではチェック。まずは1件目から。
~/myc$ myc c.myc print.o ~/myc$ ./c 42 ~/myc$
.intel_syntax noprefix .global main main: push rbp mov rbp, rsp mov eax, 42 mov edi, eax call print .RET_main: pop rbp ret
sub rsp,0はいなくなったよ。続いて2件目。
~/myc$ myc l3.myc ~/myc$ ./l3 42 ~/myc$
ちゃんとコンパイルできるようになったよ。代入周りはまだバグあるかもなあ。テスト項目増やそう。