次に、戻り値に関するCalling Conventionの実装から始める。
以下の記述では、関数の戻り値が渡される場合に、CCAssignToReg
で示されるレジスタのどれかに引数が格納されるルールが追加されている。
そしてCalling ConventionであるRetCC_MYRISCVX
を定義する。
commit:84bc7aa2b1b Add MYRISCVX Calling Convention LP32/STACK32/LP64/STACK64
llvm-myriscvx80/lib/Target/MYRISCVX/MYRISCVXCallingConv.td
//===----------------------------------------------------------------------===// // MYRISCVX LP32/STACK32 Return Convention //===----------------------------------------------------------------------===// def RetCC_LP32 : CallingConv<[ CCIfType<[i32], CCAssignToReg<[A0, A1]>> ]>; def RetCC_STACK32 : CallingConv<[ CCIfType<[i32], CCAssignToStack<4, 4>> ]>; //===----------------------------------------------------------------------===// // MYRISCVX LP64/STACK64 Return Convention //===----------------------------------------------------------------------===// def RetCC_LP64 : CallingConv<[ CCIfType<[i64], CCAssignToReg<[A0, A1]>> ]>; def RetCC_STACK64 : CallingConv<[ CCIfType<[i64], CCAssignToStack<8, 8>> ]>; def RetCC_MYRISCVX : CallingConv<[ CCIfSubtarget<"isABI_STACK64()", CCDelegateTo<RetCC_STACK64>>, CCIfSubtarget<"isABI_LP64 ()", CCDelegateTo<RetCC_LP64>>, CCIfSubtarget<"isABI_STACK32()", CCDelegateTo<RetCC_STACK32>>, CCDelegateTo<RetCC_LP32> ]>;
このRetCC_MYRISCVX
はInstruction Selection時のAnalyzeReturn
で使用される。AnalyzeReturn
はLowerReturn
から呼び出される。
それぞれの中身は、見てみると簡単です。LP32/LP64のCalling Conventionの場合は戻り値をレジスタA0, A1に割り当てる。
これをCCAssignToReg
という命令によって表現している。
また、STACK32/STACK64のCalling Conventionの場合は、CCAssignToStack
という命令を使用してスタックに割り当てる。
文法はCCAssignToStack<Size, Align>
となっており、最初の数字はデータサイズ、次の数字はアドレスアラインを示している。
そして、LP64/STACK64, LP32/STACK32をそれぞれオプションで切り替えるための、大元となるRetCC_MYRISCVX
を定義している。
ちなみに、生成されたMYRISCVXGenCallingConv.inc
の一部を切り取る。
このように、CCIfSubTarget
によりサブターゲットの条件分岐が行われ、さらにCC_LP{32,64}, CC_STACK{32, 64}
の中で引数のチェックが行われている。
build-myriscvx80/lib/Target/MYRISCVX/MYRISCVXGenCallingConv.inc
// おおもとのCalling Convention。内部でCC_STACK32, CC_LP32, CC_STACK64, CC_LP64を条件分岐で使い分ける。 static bool RetCC_MYRISCVX(unsigned ValNo, MVT ValVT, MVT LocVT, CCValAssign::LocInfo LocInfo, ISD::ArgFlagsTy ArgFlags, CCState &State) { if (static_cast<const MYRISCVXSubtarget&>(State.getMachineFunction().getSubtarget()).isABI_STACK64()) { if (!RetCC_STACK64(ValNo, ValVT, LocVT, LocInfo, ArgFlags, State)) return false; } if (static_cast<const MYRISCVXSubtarget&>(State.getMachineFunction().getSubtarget()).isABI_LP64 ()) { if (!RetCC_LP64(ValNo, ValVT, LocVT, LocInfo, ArgFlags, State)) return false; } if (static_cast<const MYRISCVXSubtarget&>(State.getMachineFunction().getSubtarget()).isABI_STACK32()) { if (!RetCC_STACK32(ValNo, ValVT, LocVT, LocInfo, ArgFlags, State)) return false; } if (!RetCC_LP32(ValNo, ValVT, LocVT, LocInfo, ArgFlags, State)) return false; return true; // CC didn't match. } // RetCC_STACK32の実装。 // スタックのアロケーションを行っている。 static bool RetCC_STACK32(unsigned ValNo, MVT ValVT, MVT LocVT, CCValAssign::LocInfo LocInfo, ISD::ArgFlagsTy ArgFlags, CCState &State) { if (LocVT == MVT::i32) { unsigned Offset1 = State.AllocateStack(4, 4); State.addLoc(CCValAssign::getMem(ValNo, ValVT, Offset1, LocVT, LocInfo)); return false; } return true; // CC didn't match. } // RetCC_LP32の実装 static bool RetCC_LP32(unsigned ValNo, MVT ValVT, MVT LocVT, CCValAssign::LocInfo LocInfo, ISD::ArgFlagsTy ArgFlags, CCState &State) { if (LocVT == MVT::i32) { static const MCPhysReg RegList1[] = { MYRISCVX::A0, MYRISCVX::A1 }; if (unsigned Reg = State.AllocateReg(RegList1)) { State.addLoc(CCValAssign::getReg(ValNo, ValVT, Reg, LocVT, LocInfo)); return false; } } return true; // CC didn't match. }
llvm-myriscvx80/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); } ...