コンパイラ作成(64) printf

今回の目標

前回の続き。

// void*, size_t
extern void *malloc(size_t size);

int main()
{
    char *buffer = malloc(256);
    printf("buffer = %016lx\n",buffer);
}

何回も出てきたprintfだけど、このテストプログラムは上手くコンパイルできないよ。printfの処理はかなり前に作ったんだけど、そっちにはchar*型への対応が入ってないからね。で、この際その処理を削除して通常の関数として処理することにしたよ。その為にはまだちょっとだけ論理が足りないんで今回追加するよ。

reservedword

それでは修正行くよ。

=begin
    @reservedword = [
      "return","goto","if","else","for","while","until","do","break",
      "printf"
    ]
=end
    @reservedword = [
      "return","goto","if","else","for","while","until","do","break"
    ]

printfを削除。これで特別扱いしてた関数は全部削除だよ。

codegen_func

ここもちょっと修正。

  # 関数コールのコード生成
  def codegen_func operand
    rettype = "int"
    f = @functions[operand[0].str]
    if @numuseregs != 0 then
      if @numuseregs % 2 == 1 then codegen "  sub  rsp, 8" end
      (0...@numuseregs).each do |i| codegen "  push #{@regs64[i]}" end
    end
    if f != nil then
      if operand.size - 2 != f[1].size then
        perror "wrong number of parameters"
      end
    end
    (0...operand.size-2).each do |i|
      save = @numuseregs
      @numuseregs = i
      type = codegen_el operand[i+2]
      @numuseregs = save
      if f != nil then
        if f[1][i] == "size_t" then
          codegen "  movsx rax, eax"
          type = f[1][i]
        elsif f[1][i] == "void*" && is_pointer_type?(type) then
          type = f[1][i]
        end
        if type != f[1][i] then perror "incompatible type parameter" end
      end
      if type == "int"
        codegen "  mov  #{@regs32[i]}, eax"
      elsif type == "size_t"
        codegen "  mov  #{@regs64[i]}, rax"
=begin
      elsif type == "char*"
        codegen "  mov  #{@regs64[i]}, rax"
      elsif type == "void*"
        codegen "  mov  #{@regs64[i]}, rax"
=end
      elsif is_pointer_type?(type) then
        codegen "  mov  #{@regs64[i]}, rax"
      else
        perror
      end
    end
    if f == nil then
      codegen "  mov  al, 0"
    end
    codegen "  call " + operand[0].str
    if f != nil then
      rettype = f[0]
    end
    if @numuseregs != 0 then
      (0...@numuseregs).reverse_each do |i| codegen "  pop  #{@regs64[i]}" end
      if @numuseregs % 2 == 1 then codegen "  add  rsp, 8" end
    end
    return rettype
  end

関数宣言されてないときにmov al,0をコード生成するようにした。これはABIのコーリングシーケンスに合わせるため。
kogara324.hatenablog.com

statement

printfの処理を削除。

動作テスト

それじゃテスト。

~/myc$ myc o20.myc
~/myc$ ./o20
buffer = 00000000012e1260
~/myc$ 
.intel_syntax noprefix
.global main
main:
  push rbp
  mov  rbp, rsp
  sub  rsp, 16
  mov  eax, 256
  movsx rax, eax
  mov  rdi, rax
  call malloc
  mov  qword ptr [rbp - 8], rax
  lea  rax, .L.str
  mov  rdi, rax
  mov  rax, qword ptr [rbp - 8]
  mov  rsi, rax
  mov  al, 0
  call printf
.RET_main:
  add  rsp, 16
  pop  rbp
  ret
.L.str:
  .asciz  "buffer = %016lx\n"

ちゃんとポインタ変数が64bitで処理されてるし、alに0がセットされてる。本当はprintfのextern宣言まで行きたかったんだけど今日はここまで。