FPGA開発日記

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

LLVMのバックエンドを作るための第一歩 (17. ISELDagToDAGの働きについて)

f:id:msyksphinz:20190425001356p:plain

DAG-to-DAGの目的は、ターゲットとは独立したノード情報をターゲット固有のノードに変換する処理を行うことだ。命令を選択するアルゴリズムはSelectionDAGに対して実行される。

DAGToDAGの中で実装しなければならない重要な関数の一つは、アドレスを計算するためのSelectAddrだ。 SelectAddrMYRISCVXInstrInfo.tdで以下のように定義されている。

  • llvm-myriscvx/lib/Target/MYRISCVX/MYRISCVXInstrInfo.td
// MYRISCVX Address Mode! SDNode frameindex could possibily be a match
// since load and store instructions from stack used it.
def addr :
  ComplexPattern<iPTR, 2, "SelectAddr", [frameindex]>;

この"SelectAddr"を実装しなければなりません。ここでは、アドレスを計算するためにBaseOffsetを計算する。 しかし、とりあえずオフセットは使わず、Baseがそのままアドレス、Offsetは0となるように構築する。

  • llvm-myriscvx/lib/Target/MYRISCVX/MYRISCVXISelDAGToDAG.cpp
//@SelectAddr {
/// ComplexPattern used on MYRISCVXInstrInfo
/// Used on MYRISCVX Load/Store instructions
bool MYRISCVXDAGToDAGISel::
SelectAddr(SDValue Addr, SDValue &Base, SDValue &Offset) {
  //@SelectAddr }
  EVT ValTy = Addr.getValueType();
  SDLoc DL(Addr);

  // if Address is FI, get the TargetFrameIndex.
  if (FrameIndexSDNode *FIN = dyn_cast<FrameIndexSDNode>(Addr)) {
    Base   = CurDAG->getTargetFrameIndex(FIN->getIndex(), ValTy);
    Offset = CurDAG->getTargetConstant(0, DL, ValTy);
    return true;
  }

  Base   = Addr;
  Offset = CurDAG->getTargetConstant(0, DL, ValTy);
  return true;
}

ここまでの実装で一度コンパイルを実行してみる。ターゲットをMYRISCVXでアセンブリを実行してみる。

./bin/clang -target mips-unknown-linux-gnu -c ../lbdex/input/ch3.cpp -emit-llvm -o ch3.bc
./bin/llc -march=myriscvx32 -mcpu=simple32 -mattr=+64bit -relocation-model=pic -filetype=asm ch3.bc -o -
        .text
        .section .mdebug.abilp32
        .previous
        .file   "ch3.cpp"
^C
zsh: interrupt  ./bin/llc -march=myriscvx32 -mcpu=simple32 -mattr=+64bit -relocation-model=pi

まだ命令の生成途中でフリーズしてしまう。 これはLLVM IRのstoreノードをしょるすることができないためだ。 これにはスタックの処理を追加する必要があるが、とりあえずここではまだ対処しない。

./bin/llvm-dis ch3.bc -o -
; Function Attrs: noinline norecurse nounwind optnone
define dso_local i32 @main() #0 {
entry:
  %retval = alloca i32, align 4
  store i32 0, i32* %retval, align 4
  ret i32 0
}

とりあえずclangに-O2オプションを付けることでstoreの生成を除去する。

./bin/clang -target mips-unknown-linux-gnu -c ../lbdex/input/ch3.cpp -emit-llvm -O2 -o ch3.o2.bc
./bin/llc -march=myriscvx32 -mcpu=simple32 -mattr=+64bit -relocation-model=pic -filetype=asm ch3.o2.bc -o -
...
main:
        .frame  $x8,0,$x1
        .mask   0x00000000,0
        .set    noreorder
        .set    nomacro
# %bb.0:                                # %entry
        addi    x10, zero, 0
        ret     x1
        .set    macro
        .set    reorder
        .end    main
...

無事に命令を生成することができた。