FPGA開発日記

カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages , English Version https://fpgadevdiary.hatenadiary.com/

"Creating an LLVM Backend for the Cpu0 Architecture"をやってみる(15. 関数コール2)

f:id:msyksphinz:20181123225150p:plain

Cpu0のバックエンドをLLVMに追加するプロジェクト、9章の後半では、関数コールの様々な方法についてみてみる。

今回も非常にファイル量が多くて、とりあえずLLVMをビルドするためだけにパッチを作って当てているが、LLVM 7.0は未サポートになっている部分が多く、ソースコードを書き直す必要があった。

github.com

そろそろチュートリアルを読んだだけでは訳が分からなくなってきたので、自分で手を動かしてRISC-Vのバックエンドを追加できるように頑張ってみたい。

Cpu0のバックエンドをLLVMに追加するプロジェクト、9章の後半では、さまざまな形の関数コールをサポートする。

今回も非常にファイル量が多くて、とりあえずLLVMをビルドするためだけにパッチを作って当てているが、LLVM 7.0は未サポートになっている部分が多く、ソースコードを書き直す必要があった。

github.com



構造体のサポート

初期の構造体のサポート

関数コール内での構造体のサポートを行う。

def RetCC_Cpu0EABI : CallingConv<[
  // i32 are returned in registers V0, V1, A0, A1
  CCIfType<[i32], CCAssignToReg<[V0, V1, A0, A1]>>
]>;

構造体の帰り値は帰り値が4つのレジスタを超えない場合、V0, V1, A0, A1に格納する。 もし次の4つのレジスタを超える倍、Cpu0は値をメモリに格納して、レジスタにはそのポインタの場所を格納する。

byval構造体

関数コールの最適化

末尾再帰の最適化(Tail call optimization)

幾つかの状況では、関数の呼び出し元と呼び出し先は同じメモリスタックを共有する。この場合再帰的な関数呼び出しでは、漸近的にスタックの必要量が線形になるか、O(n)か定数かO(1)になる。LLVM IRは末尾呼び出しをサポートする。

  • lbdex/input/ch9_2_tailcall.cpp
int factorial(int x)
{
  if (x > 0)
    return x*factorial(x-1);
  else
    return 1;
}

int test_tailcall(int a)
{
  return factorial(a);
}
$ ./bin/clang -target mips-unknown-linux-gnu -c ../lbdex/input/ch9_2_tailcall.cpp -emit-llvm -o ch9_2_tailcall.bc
$ ./bin/llvm-dis ch9_2_tailcall.bc -o -

define dso_local i32 @_Z9factoriali(i32 signext %x) #0 {
entry:
  %retval = alloca i32, align 4
  %x.addr = alloca i32, align 4
  store i32 %x, i32* %x.addr, align 4
  %0 = load i32, i32* %x.addr, align 4
  %cmp = icmp sgt i32 %0, 0
  br i1 %cmp, label %if.then, label %if.else

if.then:                                          ; preds = %entry
  %1 = load i32, i32* %x.addr, align 4
  %2 = load i32, i32* %x.addr, align 4
  %sub = sub nsw i32 %2, 1
  %call = call i32 @_Z9factoriali(i32 signext %sub)
  %mul = mul nsw i32 %1, %call
  store i32 %mul, i32* %retval, align 4
  br label %return

if.else:                                          ; preds = %entry
  store i32 1, i32* %retval, align 4
  br label %return

return:                                           ; preds = %if.else, %if.then
  %3 = load i32, i32* %retval, align 4
  ret i32 %3
}



再帰の最適化

他の機能のサポート

このセクションでは、

  • PICアドレッシングモードの場合の$gpレジスタの呼び出し元保存
    • jsub命令では24ビットのアドレス範囲のみをサポートしている。これをjalrにより32ビットのアドレス範囲まで拡張した。これには、Cpu0が1命令で32ビットのアドレッシングを可能にさせるためにある。そしてjalrをPICモードの動的リンク関数にも使用する。
  • 可変長引数のサポート
  • 動的なスタックの割り当て

をサポートする。