LLVMにはすでにRISC-Vのバックエンドサポートが追加されている。しかし、勉強のために独自のRISC-V実装をLLVMに追加している。
SelectionDAGを追加している。きつねさんLLVM本曰く、SelDAGtoDAGというのは、
LLVM IR から SelectionDAG を経由して MI Layerに落とすパス (SelectionDAGIsel) の定義. ということなので、LLVM IRのコードをどのようにしてRISC-Vの実装に落とし込むかというところになる。
利用するのはTableGenで生成されるMYRISCVXGenDAGISel.inc
である。
用意したファイルは MYRISCVXISelDAGToDAG.cpp
とMYRISCVXSEISelDAGToDAG.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命令は別のレジスタを使って戻ることができる。このようなコードを生成することも可能だ。