Adding a machine code descriptor

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.

How it's done…

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.

How it works…

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:

  • A basic set of get** and set** functions for information queries, for example, getOpcode(), getNumOperands(), and so on
  • Bundle-related operations, for example, isInsideBundle()
  • Checking whether the instruction has certain properties, for example, isVariadic(), isReturn(), isCall(), and so on
  • Machine instruction manipulation, for example, eraseFromParent()
  • Register-related operations, such as ubstituteRegister(), addRegisterKilled(), and so on
  • Machine-instruction-creating methods, for example, addOperand(), setDesc(), and so on

Note that, although the MachineInstr class provides machine-instruction-creating methods, a dedicated function called BuildMI(), based on the MachineInstrBuilder class, is more convenient.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.142.133.180