コンパイラ作成(59) char*型変数の代入・参照を修正

今回の目標

引き続きchar*型。

// char*型の変数
int main()
{
    char *p = "Hello, World!";
    puts(p);
}

これ前々回のテストプログラム。コンパイルできてちゃんと動いたんだけどさ、アセンブリコード見てて変なことに気が付いたよ。

.intel_syntax noprefix
.global main
main:
  push rbp
  mov  rbp, rsp
  sub  rsp, 16
  lea  rax, .L.str
  mov  dword ptr [rbp - 8], eax
  mov  eax, dword ptr [rbp - 8]
  mov  edi, eax
  call puts
.RET_main:
  add  rsp, 16
  pop  rbp
  ret
.L.str:
  .asciz  "Hello, World!"

movが32bitになってる。ポインタだから64bitじゃないと拙いよね。で良く考えたら前々回は変数宣言部の修正しかしてないから当たり前だよ。raxに値が残ってて偶然動いてるだけだった。はは、参ったね。

codegen_assign

ここも型を意識してコード生成するようにしたよ。

  # 代入のコード生成
  def codegen_assign(el)
    if el.size != 3 then perror end
    type_r = 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
    type_l = v[0]
    if type_l != type_r then perror end
    if type_l == "char*" then
      codegen "  mov  qword ptr [rbp - " + v[1].to_s + "], rax"
    else
      codegen "  mov  dword ptr [rbp - " + v[1].to_s + "], eax"
    end
    return type_l
  end

変数のHashテーブルから型を参照するようにした。

codegen_elf

ここも同じように修正。

  # 式のコード生成(二項演算の左側被演算子)
  def codegen_elf(operand)
    type = "int"
    if operand.kind_of?(Array) then
      if !operand[0].kind_of?(Array) && operand[0].kind == TK::ID && operand[1].str == "()" then
        type = codegen_func operand
      else
        type = codegen_el operand
      end
    elsif operand.kind == TK::NUMBER then
      codegen "  mov  eax, " + operand.str
    elsif operand.kind == TK::ID then
      v = @lvars[operand.str]
      if v == nil then
        perror "undeclared variable \"" + operand.str + "\""
      end
      type = v[0]
      if type == "char*"
        codegen "  mov  rax, qword ptr [rbp - " + v[1].to_s + "]"
      else
        codegen "  mov  eax, dword ptr [rbp - " + v[1].to_s + "]"
      end
    elsif operand.kind == TK::STRING then
      type = "char*"
      label = addliteral operand.str
      codegen "  lea  rax, "+label
    else
      perror
    end
    return type
  end
動作テスト
~/myc$ myc o13.myc
~/myc$ ./o13
Hello, World!
~/myc$

ここまではこの前と同じ。コンパイル結果を見てみるよ。

.intel_syntax noprefix
.global main
main:
  push rbp
  mov  rbp, rsp
  sub  rsp, 16
  lea  rax, .L.str
  mov  qword ptr [rbp - 8], rax
  mov  rax, qword ptr [rbp - 8]
  mov  rdi, rax
  call puts
.RET_main:
  add  rsp, 16
  pop  rbp
  ret
.L.str:
  .asciz  "Hello, World!"

ちゃんと64bitになってる。動作確認だけじゃなくアセンブリコードもしっかり確かめないと駄目だね。これから気を付けよう。(多分明日になったら忘れてる)