Implementing DAG lowering – handling legal types and setting operations – Instruction Selection
By Peggy Johnston / April 6, 2023 / No Comments / Creating the disassembler, Emitting machine instructions, Exams of IT, Global instruction selection, ITCertification Exams
Let’s implement the M88kISelLowering class, which is stored in M88kISelLowering.cpp file, first. The constructor configures the legal types and operations:
- The constructor takes references to the TargetMachine and M88kSubtarget classes as parameters. The TargetMachine class is responsible for the general configuration of the target, for example, which passes need to run. An LLVM backend usually targets a CPU family, and the M88kSubtarget class describes the characteristics of the chosen CPU. We’ll look at both classes later in this chapter:
M88kTargetLowering::M88kTargetLowering(
const TargetMachine &TM, const M88kSubtarget &STI)
: TargetLowering(TM), Subtarget(STI) {
- The first action is to declare which machine value type uses which register class. Remember that the register classes are generated from the target description. Here, we only handle 32-bit values: addRegisterClass(MVT::i32, &M88k::GPRRegClass);
- After adding all register classes, we must compute the derived properties of those register classes. We need to query the sub-target for register information, which is mostly generated from the target description:
computeRegisterProperties(Subtarget.getRegisterInfo());
- Next, we must declare which register contains the stack pointer: setStackPointerRegisterToSaveRestore(M88k::R31);
- Boolean values are represented differently on different platforms. For our target, we will declare that a Boolean value is stored in bit 0; the other bits are cleared: setBooleanContents(ZeroOrOneBooleanContent);
- After, we set the alignment of functions. The minimal function alignment is the alignment that is required for correct execution. In addition, we give the preferred alignment: setMinFunctionAlignment(Align(4));
setPrefFunctionAlignment(Align(4)); - Finally, we declare which operations are legal. In the previous chapter, we only defined three logical instructions, and they are legal for 32-bit values: setOperationAction(ISD::AND, MVT::i32, Legal);
setOperationAction(ISD::OR, MVT::i32, Legal);
setOperationAction(ISD::XOR, MVT::i32, Legal); - There are a couple of other actions we can use besides Legal. Promote widens the type, Expand replaces the operation with other operations, LibCall lowers the operation to a library call, and Custom calls the LowerOperation() hook method, which lets you implement your own custom handling. For example, in the M88k architecture, there is no count population instruction, so we request that this operation be expanded into other operations: setOperationAction(ISD::CTPOP, MVT::i32, Expand);
}
Now, let’s review some points to emphasize the connection between the definitions we made so far. In the target description mentioned in the M88kInstrInfo.td file, we defined a machine instruction with the and mnemonic, and we also attached a pattern to it. If we expand the AND multiclass record, and only look at the instruction using three registers, we get the TableGen definition:
let isCommutable = 1 in
def ANDrr : F_LR<0b01000, Func, /comp=/0b0, “and”, [(set i32:$rd, (and GPROpnd:$rs1, GPROpnd:$rs2))]>;
The “and” string is the mnemonic of the instruction. In C++ source code, we use M88k::ANDrr to refer to this instruction. Inside the pattern, the DAG and node type is used. In C++, it is named ISD::AND, and we used it in the call to the setOperationAction() method. During instruction selection, a DAG node of the and type is replaced by the M88k::ANDrr instruction if the pattern matches, which includes the input operands. Thus, when we develop instruction selection, the most important task is for us to define the correct legalization actions and attach the patterns to the instruction definitions.