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); }