コンパイラ作成(91) 浮動小数点数リテラルの処理

今回の目標

重複したリテラルを一個に纏めるよ。

// double型
double dadd(double a, double b)
{
    printf("dadd: %f %f\n", a, b);
    return a + b;
}

int main()
{
    double x = 12.3, y = -2.8;
    printf("%f %f %f\n", x, y, dadd(5.4, 3.2));
    printf("%d %d %f\n", 12, 7, dadd(5.4, 3.2));
}

それとリテラルに付けるラベル名を.L.floatから.L.doubleに変更するよ。

リテラル管理
  # n番目のリテラルのラベル名
  def literallabel(num,type)
    if type == :string then
      l = ".L.str"
    else
      l = ".L.double"
    end
    return "#{l}.#{num}"
  end

  # リテラルをテーブルに追加
  def addliteral(str, type=:string)
    @literaltable.each_with_index do |x, i|
      if x == [type,str] then return literallabel(i,type) end
    end
    num = @literalcnt
    @literalcnt += 1
    @literaltable << [type,str]
    return literallabel(num,type)
  end

  # リテラルを出力
  def genliterals
    @literaltable.each_with_index do |literal, i|
      if literal[0] == :string then
        codegen (literallabel(i,:string) + ":")
        codegen ("  .asciz  \"" + literal[1] + "\"")
      elsif literal[0] == :double then
        codegen (literallabel(i,:double) + ":")
        ieee754binary = [literal[1].to_f]
                        .pack('G').bytes
                        .map{|n| "%08b" % n}
                        .join.to_i(2).to_s(16)
        codegen "  .quad  #{ieee754binary}H    # #{literal[1]}"
      end
    end
    codeflush
  end

addliteralで登録する前に同じのが既にあるかチェックするようにしたよ。

コード生成部

codegen_elfの浮動小数点数リテラルの処理部。

    elsif operand.kind == TK::FLOAT then
      # 浮動小数点数リテラル
      type = "double"
      label = addliteral operand.str, :double
      codegen "  movsd  xmm8, qword ptr [#{label}]"

codegen_elsの浮動小数点数リテラルの処理部。

    elsif operand.kind == TK::FLOAT then
      # 浮動小数点数リテラル
      type_r = "double"
      label = addliteral operand.str, :double
      str = "qword ptr [#{label}]"

floatをdoubleに変更。これトークンがTK::FLOATなんでそれにつられてfloatにしちゃったんだな。失敗失敗。

動作テスト
~/myc$ myc p25.myc
~/myc$ ./p25
dadd: 5.400000 3.200000
12.300000 -2.800000 8.600000
dadd: 5.400000 3.200000
12 7 8.600000
~/myc$

動作は問題ないね。リテラルが纏められてるかアセンブリコード見てみるよ。

.intel_syntax noprefix
.global dadd
dadd:
  push rbp
  mov  rbp, rsp
  sub  rsp, 16
  movsd  qword ptr [rbp - 8], xmm0
  movsd  qword ptr [rbp - 16], xmm1
  lea  rax, .L.str.0
  mov  rdi, rax
  movsd  xmm8, qword ptr [rbp - 8]
  movsd  xmm0, xmm8
  movsd  xmm8, qword ptr [rbp - 16]
  movsd  xmm1, xmm8
  mov  al, 2
  call printf
  movsd  xmm8, qword ptr [rbp - 8]
  addsd  xmm8, qword ptr [rbp - 16]
  movsd  xmm0, xmm8
.RET_dadd:
  add  rsp, 16
  pop  rbp
  ret
.global main
main:
  push rbp
  mov  rbp, rsp
  sub  rsp, 16
  movsd  xmm8, qword ptr [.L.double.1]
  movsd  qword ptr [rbp - 8], xmm8
  movsd  xmm8, qword ptr [.L.double.2]
  movq   rax, xmm8
  movabs r10, 8000000000000000H
  xor    rax, r10
  movq   xmm8, rax
  movsd  qword ptr [rbp - 16], xmm8
  lea  rax, .L.str.3
  mov  rdi, rax
  movsd  xmm8, qword ptr [rbp - 8]
  movsd  xmm0, xmm8
  movsd  xmm8, qword ptr [rbp - 16]
  movsd  xmm1, xmm8
  sub    rsp, 32
  mov    qword ptr [rsp + 0], rdi
  movsd  qword ptr [rsp + 8], xmm0
  movsd  qword ptr [rsp + 16], xmm1
  movsd  xmm8, qword ptr [.L.double.4]
  movsd  xmm0, xmm8
  movsd  xmm8, qword ptr [.L.double.5]
  movsd  xmm1, xmm8
  call dadd
  movsd  xmm8, xmm0
  mov    rdi, qword ptr [rsp + 0]
  movsd  xmm0, qword ptr [rsp + 8]
  movsd  xmm1, qword ptr [rsp + 16]
  add    rsp, 32
  movsd  xmm2, xmm8
  mov  al, 3
  call printf
  lea  rax, .L.str.6
  mov  rdi, rax
  mov  eax, 12
  mov  esi, eax
  mov  eax, 7
  mov  edx, eax
  sub    rsp, 32
  mov    qword ptr [rsp + 0], rdi
  mov    qword ptr [rsp + 8], rsi
  mov    qword ptr [rsp + 16], rdx
  movsd  xmm8, qword ptr [.L.double.4]
  movsd  xmm0, xmm8
  movsd  xmm8, qword ptr [.L.double.5]
  movsd  xmm1, xmm8
  call dadd
  movsd  xmm8, xmm0
  mov    rdi, qword ptr [rsp + 0]
  mov    rsi, qword ptr [rsp + 8]
  mov    rdx, qword ptr [rsp + 16]
  add    rsp, 32
  movsd  xmm0, xmm8
  mov  al, 1
  call printf
.RET_main:
  add  rsp, 16
  pop  rbp
  ret
.L.str.0:
  .asciz  "dadd: %f %f\n"
.L.double.1:
  .quad  402899999999999aH    # 12.3
.L.double.2:
  .quad  4006666666666666H    # 2.8
.L.str.3:
  .asciz  "%f %f %f\n"
.L.double.4:
  .quad  401599999999999aH    # 5.4
.L.double.5:
  .quad  400999999999999aH    # 3.2
.L.str.6:
  .asciz  "%d %d %f\n"

二回ずつ使われてる5.4と3.2のリテラルが一個になってる。うまくいったよ。さて次回は何やろうかな。int型とdouble型の変換でもやろうかな。