3. 関数呼び出しのDAG生成
次に、ジャンプ先となる関数のアドレスを計算する。PICモードでコンパイルしている場合はGOT経由でのアドレスを計算、そうでない場合はっ直接アドレスの計算を行う。これをGlobalAddressSDNode
, ExternalSymbolSDNode
のタイプに対してそれぞれ行う。
if (GlobalAddressSDNode *G = dyn_cast<GlobalAddressSDNode>(Callee)) { if (IsPICCall) { const GlobalValue *Val = G->getGlobal(); InternalLinkage = Val->hasInternalLinkage(); if (InternalLinkage) Callee = getAddrLocal(G, Ty, DAG); else Callee = getAddrGlobal(G, Ty, DAG, MYRISCVXII::MO_GOT_CALL, Chain, FuncInfo->callPtrInfo(Val)); } else Callee = DAG.getTargetGlobalAddress(G->getGlobal(), DL, getPointerTy(DAG.getDataLayout()), 0, MYRISCVXII::MO_NO_FLAG); GlobalOrExternal = true; } else if (ExternalSymbolSDNode *S = dyn_cast<ExternalSymbolSDNode>(Callee)) { const char *Sym = S->getSymbol(); if (!IsPIC) // static Callee = DAG.getTargetExternalSymbol(Sym, getPointerTy(DAG.getDataLayout()), MYRISCVXII::MO_NO_FLAG); else // PIC Callee = getAddrGlobal(S, Ty, DAG, MYRISCVXII::MO_GOT_CALL, Chain, FuncInfo->callPtrInfo(Sym)); GlobalOrExternal = true; }
最後に、MYRISCVXISD::CALL
ノードを接続して、実際に関数ジャンプを行うノードをDAGに接続する。
Chain = DAG.getNode(MYRISCVXISD::CALL, DL, NodeTys, Ops);
最初に、CALLSEQ_START
ノードを挿入したので、ここまでで関数ジャンプのDAGが終わりであることを示すCALLSEQ_END
ノードを挿入する。
// Create the CALLSEQ_END node. Chain = DAG.getCALLSEQ_END(Chain, NextStackOffsetVal, DAG.getIntPtrConstant(0, DL, true), InFlag, DL);
4. 戻り値取得のDAG生成
戻り値の処理だが、LowerCallResult
関数を作成してここだけ別の関数に切り出する。LowerCallResult
で行わなければならないのは関数から戻ってきたときの戻り値を受け取ってしかるべき場所に格納するという処理だ。
/// LowerCallResult - Lower the result values of a call into the /// appropriate copies out of appropriate physical registers. SDValue MYRISCVXTargetLowering::LowerCallResult(SDValue Chain, SDValue InFlag, CallingConv::ID CallConv, bool IsVarArg, const SmallVectorImpl<ISD::InputArg> &Ins, const SDLoc &DL, SelectionDAG &DAG, SmallVectorImpl<SDValue> &InVals, const SDNode *CallNode, const Type *RetTy) const { ... // Assign locations to each value returned by this call. SmallVector<CCValAssign, 16> RVLocs; CCState CCInfo(CallConv, IsVarArg, DAG.getMachineFunction(), RVLocs, *DAG.getContext()); const ExternalSymbolSDNode *ES = dyn_cast_or_null<const ExternalSymbolSDNode>(CallNode); CCInfo.AnalyzeCallResult(Ins, RetCC_MYRISCVX); ...
AnalyzeCallResult
を呼んで、関数の戻り値を格納するレジスタの解析を行う。AnalyzeFormalArguments()
と構成は似ており、呼び出し規約RetCC_MYRISCVX
を呼び出して解析に使用している。念のためRetCC_MYRISCVX
の中身を確認してみると、RetCC_MYRISCVXEABI
を呼び出しており、RetCC_MYRISCVXEABI
では戻り値レジスタとしてA0とA1レジスタを使用することが明記されている。
llvm-myriscvx/lib/CodeGen/CallingConvLower.cpp
/// Analyze the return values of a call, incorporating info about the passed /// values into this state. void CCState::AnalyzeCallResult(const SmallVectorImpl<ISD::InputArg> &Ins, CCAssignFn Fn) { for (unsigned i = 0, e = Ins.size(); i != e; ++i) { MVT VT = Ins[i].VT; ISD::ArgFlagsTy Flags = Ins[i].Flags; if (Fn(i, VT, VT, CCValAssign::Full, Flags, *this)) { #ifndef NDEBUG dbgs() << "Call result #" << i << " has unhandled type " << EVT(VT).getEVTString() << '\n'; #endif llvm_unreachable(nullptr); } } }
build-myriscvx/lib/Target/MYRISCVX/MYRISCVXGenCallingConv.inc
static bool RetCC_MYRISCVXEABI(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. }
解析後、すべての戻り値(といってもC言語の場合は戻り値は1つだけだが)において、DAGを生成する。getCopyFromReg()
のDAGを生成して物理レジスタからノードにデータをコピーして、その後のDAGに接続できるようにしている。RVLocs[i].getValVT() != RVLocs[i].getLocVT()
は、格納しているレジスタの場所とデータの実体の型が合っていない場合に、BITCAST
ノードを接続して型の拡張を行うためのDAGだ。
最終的に、生成されたDAGは以下のようになった。それぞれのステップで挿入したノードに、赤い四角を書いている。