Cコンパイラゼミ2024 | 2024-07-07のCコンパイラゼミ >>
switch-caseを利用した変なコード
デバッグに苦労したバグ
- map.cをコンパイルするとセグフォが起きる
- map_put2という関数の
map->size++;
の行でセグフォが起きる - インクリメントを手で分解するとエラーが起きなくなる
- ここからインクリメント部分で同じノードを2回挿入している箇所が怪しいとにらむ
- gdbでデバッグをして、analyse.cの
ND_DOT
に対するパターンマッチ部分でエラーが起きているとわかる - printデバッグをすると、
node->lhs
にはND_VARREF
が来ているとわかる ND_VARREF
はanalyseの段階で新しく発生するノードであって、analyse前にこのノードがあるはずがない- インクリメント部分でコピーせずにノードを入れているため、1度目に
ND_IDENT
からND_VARREF
に変換され、2度目にanalyseをすることでエラーが起こっていると判明する
副作用はside effect
また別のプログラムでセグフォが起きて、 1時間くらい延々と格闘した結果が
mov rdi, [rax] # iのアドレスにあるiの値を取得している
の部分をrdiからediに書き換えれば済むというものだった。
このアセンブリの中にmov [rax], edi
というコードがあって、ここでセグフォが起きていた。なのでraxの値がおかしいんじゃないかというところまでは予想が合ってた。でも、raxの値がおかしいのはスタックの管理を間違えたからではなくて、raxのアドレスの計算が間違っていた。何回も違う場所を疑って、結局一つ一つのアセンブリを読む羽目になった。
nop
mov rax, rbp
sub rax, 8 # bのアドレスを取得している
push rax
# gen ND_MUL
# gen ND_ASSIGN_SUB
# gen_ref ND_VARREF
mov rax, rbp
sub rax, 12 # iのアドレスを取得している
push rax
# gen ND_NUM
push 1 # 1 | iのアドレス | bのアドレスの形で格納されてる
# endgen ND_NUM
pop rdi # 1
pop rax # iのアドレス
sub [rax], rdi # i -= 1をしている
mov edi, [rax] # iのアドレスの値を取得している
push rdi # i - 1 | bのアドレスの形で格納されてる
# endgen ND_ASSIGN_SUB
# gen ND_NUM
push 4
# endgen ND_NUM
pop rdi
pop rax # i - 1が入っている
imul rax, rdi # (i - 1) * 4をしている
push rax
# endgen ND_MUL
pop rdi # (i - 1) * 4が入っている
pop rax # bのアドレスが入っている
add rax, rdi # b + (i - 1) * 4をしている
push rax
# endgen ND_ADD
# gen ND_NUM
push 42
# endgen ND_NUM
pop rdi # 42が入っている
pop rax # b + (i - 1) * 4のアドレスが入っている
mov [rax], edi # b + (i - 1) * 4のアドレスに42を代入している
push rdi