コンパイラ作成(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;ってやりたいんだけど、型変換の論理が入ってないんでまだ無理。これもやんなきゃいけないんだけど、次回はネストした関数の呼出かな。