Developing the specifics of the machine function pass – Beyond Instruction Selection-2

  1. Finally, the last part is declaring the private methods and closing off the class. The only private method we declare within the M88kDivInstr class is the addZeroDivCheck() method, which inserts the checks for division by zero after any divide instruction. As we will see later, MachineInstr will need to point to specific divide instructions on the M88k target:

private:
void addZeroDivCheck(MachineBasicBlock &MBB, MachineInstr *DivInst);
};

  1. An M88kBuilder class is created next, which is a specialized builder instance that creates M88k-specific instructions. This class keeps an instance of MachineBasicBlock (and a corresponding iterator), and DebugLoc to keep track of the debug location of this builder class. Other necessary instances include the target instruction information, the target register information, and the register bank information of the M88k target:

class M88kBuilder {
MachineBasicBlock *MBB;
MachineBasicBlock::iterator I;
const DebugLoc &DL;
const TargetInstrInfo &TII;
const TargetRegisterInfo &TRI;
const RegisterBankInfo &RBI;

  1. For public methods of the M88kBuilder class, we must implement the constructor for this builder. Upon initialization, our specialized builder requires an instance of the M88kDivInstr pass to initialize the target instruction, register information, and the register bank information, as well as MachineBasicBlock and a debug location:

public:
M88kBuilder(M88kDivInstr &Pass, MachineBasicBlock MBB, const DebugLoc &DL) : MBB(MBB), I(MBB->end()), DL(DL), TII(Pass.TII), TRI(Pass.TRI), RBI(Pass.RBI) {}

  1. Next, a method to set MachineBasicBlock inside the M88k builder is created, and the MachineBasicBlock iterator is also set accordingly: void setMBB(MachineBasicBlock *NewMBB) {
    MBB = NewMBB;
    I = MBB->end();
    }
  2. The constrainInst() function needs to be implemented next and is needed for when MachineInstr instances are processed. For a given MachineInstr, we check if the register class of the MachineInstr instance’s operands can be constrained through the pre-existing function, constrainSelectedInstRegOperands(). As shown here, this machine function pass requires that the register operands of the machine instruction can be constrained: void constrainInst(MachineInstr MI) { if (!constrainSelectedInstRegOperands(MI, TII, TRI, RBI))
    llvm_unreachable(“Could not constrain register operands”);
    }
  3. One of the instructions that this pass inserts is a BCND instruction, as defined in M88kInstrInfo.td, which is a conditional branch on the M88k target. To create this instruction, we require a condition code, which are the CC0 enums that were implemented at the beginning of M88kDivInstr.cpp – that is, a register and MachineBasicBlock. The BCND instruction is simply returned upon creation and after checking if the newly created instruction can be constrained: MachineInstr *bcnd(CC0 Cc, Register Reg, MachineBasicBlock *TargetMBB) {
    MachineInstr MI = BuildMI(MBB, I, DL, TII.get(M88k::BCND))
    .addImm(static_cast(Cc))
    .addReg(Reg)
    .addMBB(TargetMBB);
    constrainInst(MI);
    return MI;
    }
  4. Similarly, we also require a trap instruction for our machine function pass, which is a TRAP503 instruction. This instruction requires a register and raises a trap with vector 503 if the 0-th bit of the register is not set, which will be raised after a zero division. Upon creating the TRAP503 instruction, TRAP503 is checked for constraints before being returned. Moreover, this concludes the class implementation of the M88kBuilder class and completes the previously declared anonymous namespace: MachineInstr *trap503(Register Reg) {
    MachineInstr *MI = BuildMI(*MBB, I, DL, TII.get(M88k::TRAP503)).addReg(Reg);
    constrainInst(MI);
    return MI;
    }
    };
    } // end anonymous namespace
  5. We can now start implementing the functions that perform the actual checks in the machine function pass. First, let’s explore how addZeroDivCheck() is implemented. This function simply inserts a check for division by zero between the current machine instruction, which is expected to point to either DIVSrr or DIVUrr; these are mnemonics for signed and unsigned divisions, respectively. The BCND and TRAP503 instructions are inserted, and the InsertedChecks statistic is incremented to indicate the addition of the two instructions:

void M88kDivInstr::addZeroDivCheck(MachineBasicBlock &MBB,
MachineInstr *DivInst) {
assert(DivInst->getOpcode() == M88k::DIVSrr ||
DivInst->getOpcode() == M88k::DIVUrr && “Unexpected opcode”);
MachineBasicBlock *TailBB = MBB.splitAt(DivInst); M88kBuilder B(this, &MBB, DivInst->getDebugLoc());
B.bcnd(CC0::NE0, DivInst->getOperand(2).getReg(), TailBB);
B.trap503(DivInst->getOperand(2).getReg());
++InsertedChecks;
}

Leave a Reply

Your email address will not be published. Required fields are marked *