Implementing DAG lowering – lowering formal arguments – Instruction Selection
By Peggy Johnston / May 22, 2023 / No Comments / Adding the M88k backend to LLVM, Creating the disassembler, Emitting machine instructions, Exams of IT, ITCertification Exams
Let’s turn to another important task performed by the M88kISelLowering class. We defined the rules for the calling convention in the previous section, but we also need to map the physical registers and memory locations to virtual registers used in the DAG. For arguments, this is done in the LowerFormalArguments() method; return values are handled in the LowerReturn() method. First, we must handle the arguments:
- We’ll begin by including the generated source:
include “M88kGenCallingConv.inc”
- The LowerFormalArguments() method takes several parameters. The SDValue class denotes a value associated with a DAG node and is often used when dealing with the DAG. The first parameter, Chain, represents the control flow, and the possible updated Chain is also the return value of the method. The CallConv parameter identifies the used calling convention, and IsVarArg is set to true if a variable argument list is part of the parameters. The arguments that need to be handled are passed in the Ins parameter, together with their location in the DL parameter. The DAG parameter gives us access to the SelectionDAG class. Lastly, the result of the mapping will be stored in the InVals vector argument:
SDValue M88kTargetLowering::LowerFormalArguments(
SDValue Chain, CallingConv::ID CallConv,
bool IsVarArg,
const SmallVectorImpl &Ins,
const SDLoc &DL, SelectionDAG &DAG,
SmallVectorImpl &InVals) const {
- Our first action is to retrieve references to the machine function and the machine register information: MachineFunction &MF = DAG.getMachineFunction();
MachineRegisterInfo &MRI = MF.getRegInfo(); - Next, we must call the generated code. We need to instantiate an object of the CCState class. The CC_M88k parameter value that’s used in the call to the AnalyzeFormalArguments() method is the name of the calling convention we used in the target description. The result is stored in the ArgLocs vector: SmallVector ArgLocs;
CCState CCInfo(CallConv, IsVarArg, MF, ArgLocs,
*DAG.getContext());
CCInfo.AnalyzeFormalArguments(Ins, CC_M88k); - Once the locations of the arguments have been determined, we need to map them to the DAG. Therefore, we must loop over all locations: for (unsigned I = 0, E = ArgLocs.size(); I != E; ++I) {
SDValue ArgValue;
CCValAssign &VA = ArgLocs[I];
EVT LocVT = VA.getLocVT(); - The mapping depends on the determined location. First, we handle arguments assigned to registers. The goal is to copy the physical register to a virtual register. To do so, we need to determine the correct register class. Since we’re only handling 32-bit values, it is easy to do this: if (VA.isRegLoc()) {
const TargetRegisterClass *RC;
switch (LocVT.getSimpleVT().SimpleTy) {
default:
llvm_unreachable(“Unexpected argument type”);
case MVT::i32:
RC = &M88k::GPRRegClass;
break;
} - With the register class stored in the RC variable, we can create the virtual register and copy the value. We also need to declare the physical register as a live-in: Register VReg = MRI.createVirtualRegister(RC);
MRI.addLiveIn(VA.getLocReg(), VReg);
ArgValue =
DAG.getCopyFromReg(Chain, DL, VReg, LocVT); - In the definition of the calling convention, we added the rule that 8-bit and 16-bit values should be promoted to 32-bit, and we need to ensure the promotion here. To do so, a DAG node must be inserted, which makes sure that the value is promoted. After, the value is truncated to the right size. Note that we pass the value of ArgValue as an operand to the DAG node and store the result in the same variable: if (VA.getLocInfo() == CCValAssign::SExt)
ArgValue = DAG.getNode(
ISD::AssertSext, DL, LocVT, ArgValue,
DAG.getValueType(VA.getValVT()));
else if (VA.getLocInfo() == CCValAssign::ZExt)
ArgValue = DAG.getNode(
ISD::AssertZext, DL, LocVT, ArgValue,
DAG.getValueType(VA.getValVT()));
if (VA.getLocInfo() != CCValAssign::Full)
ArgValue = DAG.getNode(ISD::TRUNCATE, DL,
VA.getValVT(), ArgValue); - Lastly, we finish handling the register arguments by adding the DAG node to the result vector: InVals.push_back(ArgValue);
} - The other possible location for a parameter is on the stack. However, we didn’t define any load and store instructions, so we cannot handle this case yet. This ends the loop over all argument locations: } else {
llvm_unreachable(“Not implemented”);
}
} - After that, we may need to add code to handle variable argument lists. Again, we have added some code to remind us that we have not implemented it: assert(!IsVarArg && “No
t implemented”); - Finally, we must return the Chain argument: return Chain;
}