コンパイラ作成(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で登録する前に同じのが既にあるかチェックするようにしたよ。
コード生成部
elsif operand.kind == TK::FLOAT then # 浮動小数点数リテラル type = "double" label = addliteral operand.str, :double codegen " movsd xmm8, qword ptr [#{label}]"
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型の変換でもやろうかな。