コンパイラ作成(87) 引数がdouble型の関数定義
今回の目標
double型の関数だよ。
// double型 double dadd(double a, double b) { return a + b; } int main() { double x = 12.3, y = -2.8, z = dadd(x, y); printf("%f + %f = %f\n", x, y, z); }
引数も返値もdouble型の場合。
function
# レジスタで渡された引数をスタックの領域に移す n = @lvars.size offset = 0 gpr_cnt = xmm_cnt = 0 (0...n).each do |i| offset += parametersize[i] if paratype[i] == "double" then codegen " movsd qword ptr [rbp - #{offset}], xmm#{xmm_cnt}" xmm_cnt += 1 else if parametersize[i] == 4 then codegen " mov dword ptr [rbp - #{offset}], #{@regs32[gpr_cnt]}" else codegen " mov qword ptr [rbp - #{offset}], #{@regs64[gpr_cnt]}" end gpr_cnt += 1 end end
引数の型を見て処理を分けてるよ。そういやここの処理、引数がレジスタから溢れてスタックに入れられてる場合に対応してないなあ。面倒そうなんで放ったらかしにしてたよ。
statement
# return文の処理 kind, str = @lex.gettoken kind, str = expr kind, str f = @functions[@funcname] if f[0] == "double" then codegen " movsd xmm0, xmm8" end codegen " jmp .RET_" + @funcname if kind != TK::SYMBOL || str != ";" then perror "expected ';' after return statement" end
ここも修正が必要。レジスタ割り当てが適切ならこの処理要らないんだよなあ。ていうかmycはレジスタの割り当てしてないからなあ。この辺は遠い将来の課題だな。
動作テスト
そいではテスト。
~/myc$ myc p23.myc ~/myc$ ./p23 12.300000 + -2.800000 = 9.500000 ~/myc$
えーと、計算合ってるよね。もう一個テスト。
// double型 double dimul(double a, int b) { double sum = 0.0; for(int i = 0; i < b; i = i + 1) sum = sum + a; return sum; } int main() { int y = 10; double x = 12.3, z = dimul(x, y); printf("%f * %d = %f\n", x, y, z); }
引数がdouble型とint型のミックスの場合。
~/myc$ myc p24.myc ~/myc$ ./p24 12.300000 * 10 = 123.000000 ~/myc$
これもできたよ。これ本当はreturn a * b;ってやりたいんだけど、型変換の論理が入ってないんでまだ無理。これもやんなきゃいけないんだけど、次回はネストした関数の呼出かな。