FPGA開発日記

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

オリジナルLLVM Backendを追加しよう (16. SelDAGtoDAGの実装)

LLVMにはすでにRISC-Vのバックエンドサポートが追加されている。しかし、勉強のために独自のRISC-V実装をLLVMに追加している。

SelectionDAGを追加している。きつねさんLLVM本曰く、SelDAGtoDAGというのは、

LLVM IR から SelectionDAG を経由して MI Layerに落とすパス (SelectionDAGIsel) の定義. ということなので、LLVM IRのコードをどのようにしてRISC-Vの実装に落とし込むかというところになる。

利用するのはTableGenで生成されるMYRISCVXGenDAGISel.incである。

用意したファイルは MYRISCVXISelDAGToDAG.cppMYRISCVXSEISelDAGToDAG.cppなのだが、SEがついているのは32bit用、それ以外は64bit用という理解である。

createMYRISCVXSEISelDagが呼ばれると、以下のような流れでSelectionDAGが生成されていくという理解である。

  • lib/Target/MYRISCVX/MYRISCVXSEISelDAGToDAG.cpp
FunctionPass *llvm::createMYRISCVXSEISelDag(MYRISCVXTargetMachine &TM,
                                            CodeGenOpt::Level OptLevel) {
  return new MYRISCVXSEDAGToDAGISel(TM, OptLevel);
}
  • lib/Target/MYRISCVX/MYRISCVXSEISelDAGToDAG.h
  class MYRISCVXSEDAGToDAGISel : public MYRISCVXDAGToDAGISel {
 public:
    explicit MYRISCVXSEDAGToDAGISel(MYRISCVXTargetMachine &TM, CodeGenOpt::Level OL)
        : MYRISCVXDAGToDAGISel(TM, OL) {}
  • lib/Target/MYRISCVX/MYRISCVXISelDAGToDAG.h
  class MYRISCVXDAGToDAGISel : public SelectionDAGISel {
 public:
    explicit MYRISCVXDAGToDAGISel(MYRISCVXTargetMachine &TM, CodeGenOpt::Level OL)
        : SelectionDAGISel(TM, OL), Subtarget(nullptr) {}

アドレスの生成はSelectAddrを使用するらしい。

  • 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], [SDNPWantParent]>;
  • lib/Target/MYRISCVX/MYRISCVXISelDAGToDAG.cpp
//@SelectAddr {
/// ComplexPattern used on MYRISCVXInstrInfo
/// Used on MYRISCVX Load/Store instructions
bool MYRISCVXDAGToDAGISel::
SelectAddr(SDNode *Parent, SDValue Addr, SDValue &Base, SDValue &Offset) {
...
}

これでコードを生成してみる。

$ ./bin/llc -march=myriscvx64 -relocation-model=pic -filetype=asm ch3.bc -o -

MYRISCVXISD::Retが生成できずにエラーとなった。これは、Return値の設定において特定のレジスタを称しなければならないのだが、それに対応できていない。

'+myriscv?' is not a recognized feature for this target (ignoring feature)
        .text
        .section .mdebug.abiO32
        .previous
        .file   "ch3.cpp"
Selecting: t7: ch = MYRISCVXISD::Ret t4, Register:i32 $ra

LLVM ERROR: Cannot select: t7: ch = MYRISCVXISD::Ret t4, Register:i32 $ra
  t6: i32 = Register $ra
In function: main

MIPSでは、サブルーチンコールとして「jal」→「jr $ra」で戻るのが基本だが、処理を高速化するためにjr命令は別のレジスタを使って戻ることができる。このようなコードを生成することも可能だ。