MYRISCVXISelLowering
はLLVM IRからSelectionDAG(データフローグラフ)への変換プロセスになる。
バックエンドのかなり初期の部分で適用される。
ここで必要な実装はMYRISCVXISelLowering.cpp
を実装する必要がある。
ここではLowerReturn
とLowerFormalArguments
を実装した。
llvm-myriscvx/lib/Target/MYRISCVX/MYRISCVXISelLowering.cpp
//@LowerFormalArguments { /// LowerFormalArguments - transform physical registers into virtual registers /// and generate load operations for arguments places on the stack. SDValue MYRISCVXTargetLowering::LowerFormalArguments(SDValue Chain, CallingConv::ID CallConv, bool IsVarArg, const SmallVectorImpl<ISD::InputArg> &Ins, const SDLoc &DL, SelectionDAG &DAG, SmallVectorImpl<SDValue> &InVals) const { return Chain; } // @LowerFormalArguments } //===----------------------------------------------------------------------===// //@ Return Value Calling Convention Implementation //===----------------------------------------------------------------------===// SDValue MYRISCVXTargetLowering::LowerReturn(SDValue Chain, CallingConv::ID CallConv, bool IsVarArg, const SmallVectorImpl<ISD::OutputArg> &Outs, const SmallVectorImpl<SDValue> &OutVals, const SDLoc &DL, SelectionDAG &DAG) const { return DAG.getNode(MYRISCVXISD::Ret, DL, MVT::Other, Chain, DAG.getRegister(MYRISCVX::RA, MVT::i32)); }
ただし、このままではLowerReturn
は正しく動作しない。また、MIPSなどと同様にMYRISCVXにもCalling Conventionが存在し、引数を渡す際にも特定のレジスタを使用する必要がある。
MYRISCVXの場合はRISC-Vと同様に整数の引数は汎用レジスタA0-A7に格納して渡す必要がある。
このCalling Conventionを実現するのがMYRISCVXCallingConv.td
に記述している以下のCCIfType
およびCCAssignToReg
である。
以下の記述では、関数の戻り値が渡される場合に、CCAssignToReg
で示されるレジスタのどれかに引数が格納されるルールが追加されている。
そしてCalling ConventionであるRetCC_MYRISCVX
を定義する。
llvm-myriscvx/lib/Target/MYRISCVX/MYRISCVXCallingConv.td
def RetCC_MYRISCVXEABI : CallingConv<[ // i32 are returned in registers A0, A1 CCIfType<[i32], CCAssignToReg<[A0, A1]>> ]>; def RetCC_MYRISCVX : CallingConv<[ CCDelegateTo<RetCC_MYRISCVXEABI> ]>;
このRetCC_MYRISCVX
はInstruction Selection時のAnalyzeReturn
で使用される。
AnalyzeReturn
はLowerReturn
から呼び出されるのだが、RetCC_MYRISCVX
の実装を見てみると、i32以外の型を使用する(今のところは未サポート)、もしくは該当のレジスタに割り付けが失敗するとTrueを返し、Abortするような仕組みにしている。
llvm-myriscvx/lib/Target/MYRISCVX/MYRISCVXISelLowering.cpp
template<typename Ty> void MYRISCVXTargetLowering::MYRISCVXCC:: analyzeReturn(const SmallVectorImpl<Ty> &RetVals, bool IsSoftFloat, const SDNode *CallNode, const Type *RetTy) const { CCAssignFn *Fn; Fn = RetCC_MYRISCVX; ... if (Fn(I, VT, RegVT, CCValAssign::Full, Flags, this->CCInfo)) { LLVM_DEBUG(dbgs() << "Call result #" << I << " has unhandled type " << EVT(VT).getEVTString() << '\n'); llvm_unreachable(nullptr); } ...
次にReturn命令の扱い方だが、これもMYRISCVXRet
ノードを使用して、ある条件下の下で別のノードを生成するようなルールを追加する。
MYRISCVXInstrInfo.td
に以下を追加した。
Return文(関数の戻り)の際にこの命令が生成される。ただし実装から分かるようにこの命令は疑似命令で、内部はMYRISCVXRet
となる。
llvm-myriscvx/lib/Target/MYRISCVX/MYRISCVXInstrInfo.td
let isReturn=1, isTerminator=1, hasDelaySlot=0, isBarrier=1, hasCtrlDep=1 in def RetRA : MYRISCVXPseudo<(outs), (ins), "", [(MYRISCVXRet)]>;
llvm-myriscvx/lib/Target/MYRISCVX/MYRISCVXInstrFormats.td
//===-------------------------------===// // MYRISCVX Pseudo Instructions Format //===-------------------------------===// class MYRISCVXPseudo<dag outs, dag ins, string asmstr, list<dag> pattern>: MYRISCVXInst<outs, ins, asmstr, pattern, IIPseudo, Pseudo> { let isCodeGenOnly = 1; let isPseudo = 1; }
そして、RetRA
はMYRISCVXSEInstrInfo.cpp
で展開の方法が定義される。
つまり、expandPostRAPseudo
によってRetRA
が指定されると、expandRetRA
によって当該疑似命令を引数RAレジスタのMYRISCVX::Ret
命令に変換する。
llvm-myriscvx/lib/Target/MYRISCVX/MYRISCVXSEInstrInfo.cpp
//@expandPostRAPseudo /// Expand Pseudo instructions into real backend instructions bool MYRISCVXSEInstrInfo::expandPostRAPseudo(MachineInstr &MI) const { //@expandPostRAPseudo-body MachineBasicBlock &MBB = *MI.getParent(); switch (MI.getDesc().getOpcode()) { default: return false; case MYRISCVX::RetRA: expandRetRA(MBB, MI); break; } MBB.erase(MI); return true; } void MYRISCVXSEInstrInfo::expandRetRA(MachineBasicBlock &MBB, MachineBasicBlock::iterator I) const { BuildMI(MBB, I, I->getDebugLoc(), get(MYRISCVX::RET)).addReg(MYRISCVX::RA); }