The LLVM IR has functions, which have basic blocks. Basic blocks in turn have instructions. The next logical step is to convert those IR abstract blocks into machine-specific blocks. LLVM code is translated into a machine-specific representation formed from the MachineFunction
, MachineBasicBlock
, and MachineInstr
instances. This representation contains instructions in their most abstract form—that is, having an opcode and a series of operands.
Now the LLVM IR instruction has to be represented in the machine instruction. Machine instructions are instances of the MachineInstr
class. This class is an extremely abstract way of representing machine instructions. In particular, it only keeps track of an opcode number and a set of operands. The opcode number is a simple unsigned integer that has a meaning only for a specific backend.
Let's look at some important functions defined in the MachineInstr.cpp
file:
The MachineInstr
constructor:
MachineInstr::MachineInstr(MachineFunction &MF, const MCInstrDesc &tid, const DebugLoc dl, bool NoImp) : MCID(&tid), Parent(nullptr), Operands(nullptr), NumOperands(0), Flags(0), AsmPrinterFlags(0), NumMemRefs(0), MemRefs(nullptr), debugLoc(dl) { // Reserve space for the expected number of operands. if (unsigned NumOps = MCID->getNumOperands() + MCID->getNumImplicitDefs() + MCID->getNumImplicitUses()) { CapOperands = OperandCapacity::get(NumOps); Operands = MF.allocateOperandArray(CapOperands); } if (!NoImp) addImplicitDefUseOperands(MF); }
This constructor creates an object of MachineInstr
class and adds the implicit operands. It reserves space for the number of operands specified by the MCInstrDesc
class.
One of the important functions is addOperand
. It adds the specified operand to the instruction. If it is an implicit operand, it is added at the end of the operand list. If it is an explicit operand, it is added at the end of the explicit operand list, as shown here:
void MachineInstr::addOperand(MachineFunction &MF, const MachineOperand &Op) { assert(MCID && "Cannot add operands before providing an instr descriptor"); if (&Op >= Operands && &Op < Operands + NumOperands) { MachineOperand CopyOp(Op); return addOperand(MF, CopyOp); } unsigned OpNo = getNumOperands(); bool isImpReg = Op.isReg() && Op.isImplicit(); if (!isImpReg && !isInlineAsm()) { while (OpNo && Operands[OpNo-1].isReg() && Operands[OpNo-1].isImplicit()) { --OpNo; assert(!Operands[OpNo].isTied() && "Cannot move tied operands"); } } #ifndef NDEBUG bool isMetaDataOp = Op.getType() == MachineOperand::MO_Metadata; assert((isImpReg || Op.isRegMask() || MCID->isVariadic() || OpNo < MCID->getNumOperands() || isMetaDataOp) && "Trying to add an operand to a machine instr that is already done!"); #endif MachineRegisterInfo *MRI = getRegInfo(); OperandCapacity OldCap = CapOperands; MachineOperand *OldOperands = Operands; if (!OldOperands || OldCap.getSize() == getNumOperands()) { CapOperands = OldOperands ? OldCap.getNext() : OldCap.get(1); Operands = MF.allocateOperandArray(CapOperands); if (OpNo) moveOperands(Operands, OldOperands, OpNo, MRI); } if (OpNo != NumOperands) moveOperands(Operands + OpNo + 1, OldOperands + OpNo, NumOperands - OpNo, MRI); ++NumOperands; if (OldOperands != Operands && OldOperands) MF.deallocateOperandArray(OldCap, OldOperands); MachineOperand *NewMO = new (Operands + OpNo) MachineOperand(Op); NewMO->ParentMI = this; if (NewMO->isReg()) { NewMO->Contents.Reg.Prev = nullptr; NewMO->TiedTo = 0; if (MRI) MRI->addRegOperandToUseList(NewMO); if (!isImpReg) { if (NewMO->isUse()) { int DefIdx = MCID->getOperandConstraint(OpNo, MCOI::TIED_TO); if (DefIdx != -1) tieOperands(DefIdx, OpNo); } if (MCID->getOperandConstraint(OpNo, MCOI::EARLY_CLOBBER) != -1) NewMO->setIsEarlyClobber(true); } } }
The target architecture has some memory operands as well. To add those memory operands, a function called addMemOperands()
is defined:
void MachineInstr::addMemOperand(MachineFunction &MF, MachineMemOperand *MO) { mmo_iterator OldMemRefs = MemRefs; unsigned OldNumMemRefs = NumMemRefs; unsigned NewNum = NumMemRefs + 1; mmo_iterator NewMemRefs = MF.allocateMemRefsArray(NewNum); std::copy(OldMemRefs, OldMemRefs + OldNumMemRefs, NewMemRefs); NewMemRefs[NewNum - 1] = MO; setMemRefs(NewMemRefs, NewMemRefs + NewNum); }
The setMemRefs()
function is the primary method for setting up a MachineInstr
MemRefs
list.
The MachineInstr
class has an MCID member, with the MCInstrDesc
type for describing the instruction, a uint8_t
flags member, a memory reference member (mmo_iterator
MemRefs
), and a vector member of the std::vector<MachineOperand
> operands. In terms of methods, the MachineInstr
class provides the following:
get**
and set**
functions for information queries, for example, getOpcode()
, getNumOperands()
, and so onisInsideBundle()
isVariadic()
, isReturn()
, isCall()
, and so oneraseFromParent()
ubstituteRegister()
, addRegisterKilled()
, and so onaddOperand()
, setDesc()
, and so onNote that, although the MachineInstr
class provides machine-instruction-creating methods, a dedicated function called BuildMI()
, based on the MachineInstrBuilder
class, is more convenient.
3.142.133.180