Implementing DAG lowering – lowering return values – Instruction Selection
By Peggy Johnston / July 9, 2023 / No Comments / Adding the M88k backend to LLVM, Exams of IT, Implementing the assembler parser, ITCertification Exams
The return values are handled similarly. However, we must extend the target description for them. First, we need to define a new DAG node type called RET_GLUE. This DAG node type is used to glue the return values together, which prevents them from being rearranged, for example, by the instruction scheduler. The definition in M88kInstrInfo.td is as follows:
def retglue : SDNode<“M88kISD::RET_GLUE”, SDTNone, [SDNPHasChain, SDNPOptInGlue, SDNPVariadic]>;
In the same file, we also define a pseudo-instruction to represent a return from a function call, which will be selected for a RET_GLUE node:
let isReturn = 1, isTerminator = 1, isBarrier = 1,
AsmString = “RET” in
def RET : Pseudo<(outs), (ins), [(retglue)]>;
We will expand this pseudo-instruction when we generate the output.
With these definitions in place, we can implement the LowerReturn() method:
- The parameters are the same as for LowerFormalArguments(), only the order is slightly different:
SDValue M88kTargetLowering::LowerReturn(
SDValue Chain, CallingConv::ID CallConv,
bool IsVarArg,
const SmallVectorImpl &Outs,
const SmallVectorImpl &OutVals,
const SDLoc &DL, SelectionDAG &DAG) const {
- First, we call the generated code, this time using the RetCC_M88k calling convention: SmallVector RetLocs;
CCState RetCCInfo(CallConv, IsVarArg,
DAG.getMachineFunction(), RetLocs,
*DAG.getContext());
RetCCInfo.AnalyzeReturn(Outs, RetCC_M88k); - Then, we loop over the locations again. With the simple definition of the calling convention we currently have, this loop will be executed once at most. However, this would change if we would add support for returning 64-bit values, which need to be returned in two registers: SDValue Glue;
SmallVector RetOps(1, Chain);
for (unsigned I = 0, E = RetLocs.size(); I != E; ++I) {
CCValAssign &VA = RetLocs[I]; - After, we copy the return values into the physical registers assigned to the return value. This is mostly similar to handling the arguments, with the exception that the values are glued together using the Glue variable: Register Reg = VA.getLocReg();
Chain = DAG.getCopyToReg(Chain, DL, Reg, OutVals[I],
Glue);
Glue = Chain.getValue(1);
RetOps.push_back(
DAG.getRegister(Reg, VA.getLocVT()));
} - The return value is the chain and the glued register copy operations. The latter is only returned if there is a value to return: RetOps[0] = Chain;
if (Glue.getNode())
RetOps.push_back(Glue); - Finally, we construct a DAG node of the RET_GLUE type, passing in the necessary values: return DAG.getNode(M88kISD::RET_GLUE, DL, MVT::Other,
RetOps);
}
Congratulations! With these definitions, the foundation has been laid for instruction selection.