| //===-- DelaySlotFiller.cpp - MBlaze delay slot filler --------------------===// | |
| // | |
| // The LLVM Compiler Infrastructure | |
| // | |
| // This file is distributed under the University of Illinois Open Source | |
| // License. See LICENSE.TXT for details. | |
| // | |
| //===----------------------------------------------------------------------===// | |
| // | |
| // A pass that attempts to fill instructions with delay slots. If no | |
| // instructions can be moved into the delay slot then a NOP is placed there. | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #define DEBUG_TYPE "delay-slot-filler" | |
| #include "MBlaze.h" | |
| #include "MBlazeTargetMachine.h" | |
| #include "llvm/CodeGen/MachineFunctionPass.h" | |
| #include "llvm/CodeGen/MachineInstrBuilder.h" | |
| #include "llvm/Target/TargetInstrInfo.h" | |
| #include "llvm/ADT/Statistic.h" | |
| #include "llvm/Support/CommandLine.h" | |
| #include "llvm/Support/Debug.h" | |
| #include "llvm/Support/ErrorHandling.h" | |
| #include "llvm/Support/raw_ostream.h" | |
| using namespace llvm; | |
| STATISTIC(FilledSlots, "Number of delay slots filled"); | |
| namespace llvm { | |
| cl::opt<bool> DisableDelaySlotFiller( | |
| "disable-mblaze-delay-filler", | |
| cl::init(false), | |
| cl::desc("Disable the MBlaze delay slot filter."), | |
| cl::Hidden); | |
| } | |
| namespace { | |
| struct Filler : public MachineFunctionPass { | |
| TargetMachine &TM; | |
| const TargetInstrInfo *TII; | |
| static char ID; | |
| Filler(TargetMachine &tm) | |
| : MachineFunctionPass(ID), TM(tm), TII(tm.getInstrInfo()) { } | |
| virtual const char *getPassName() const { | |
| return "MBlaze Delay Slot Filler"; | |
| } | |
| bool runOnMachineBasicBlock(MachineBasicBlock &MBB); | |
| bool runOnMachineFunction(MachineFunction &F) { | |
| bool Changed = false; | |
| for (MachineFunction::iterator FI = F.begin(), FE = F.end(); | |
| FI != FE; ++FI) | |
| Changed |= runOnMachineBasicBlock(*FI); | |
| return Changed; | |
| } | |
| }; | |
| char Filler::ID = 0; | |
| } // end of anonymous namespace | |
| static bool hasImmInstruction(MachineBasicBlock::iterator &candidate) { | |
| // Any instruction with an immediate mode operand greater than | |
| // 16-bits requires an implicit IMM instruction. | |
| unsigned numOper = candidate->getNumOperands(); | |
| for (unsigned op = 0; op < numOper; ++op) { | |
| MachineOperand &mop = candidate->getOperand(op); | |
| // The operand requires more than 16-bits to represent. | |
| if (mop.isImm() && (mop.getImm() < -0x8000 || mop.getImm() > 0x7fff)) | |
| return true; | |
| // We must assume that unknown immediate values require more than | |
| // 16-bits to represent. | |
| if (mop.isGlobal() || mop.isSymbol() || mop.isJTI() || mop.isCPI()) | |
| return true; | |
| // FIXME: we could probably check to see if the FP value happens | |
| // to not need an IMM instruction. For now we just always | |
| // assume that FP values do. | |
| if (mop.isFPImm()) | |
| return true; | |
| } | |
| return false; | |
| } | |
| static unsigned getLastRealOperand(MachineBasicBlock::iterator &instr) { | |
| switch (instr->getOpcode()) { | |
| default: return instr->getNumOperands(); | |
| // These instructions have a variable number of operands but the first two | |
| // are the "real" operands that we care about during hazard detection. | |
| case MBlaze::BRLID: | |
| case MBlaze::BRALID: | |
| case MBlaze::BRLD: | |
| case MBlaze::BRALD: | |
| return 2; | |
| } | |
| } | |
| static bool delayHasHazard(MachineBasicBlock::iterator &candidate, | |
| MachineBasicBlock::iterator &slot) { | |
| // Hazard check | |
| MachineBasicBlock::iterator a = candidate; | |
| MachineBasicBlock::iterator b = slot; | |
| MCInstrDesc desc = candidate->getDesc(); | |
| // MBB layout:- | |
| // candidate := a0 = operation(a1, a2) | |
| // ...middle bit... | |
| // slot := b0 = operation(b1, b2) | |
| // Possible hazards:-/ | |
| // 1. a1 or a2 was written during the middle bit | |
| // 2. a0 was read or written during the middle bit | |
| // 3. a0 is one or more of {b0, b1, b2} | |
| // 4. b0 is one or more of {a1, a2} | |
| // 5. a accesses memory, and the middle bit | |
| // contains a store operation. | |
| bool a_is_memory = desc.mayLoad() || desc.mayStore(); | |
| // Determine the number of operands in the slot instruction and in the | |
| // candidate instruction. | |
| const unsigned aend = getLastRealOperand(a); | |
| const unsigned bend = getLastRealOperand(b); | |
| // Check hazards type 1, 2 and 5 by scanning the middle bit | |
| MachineBasicBlock::iterator m = a; | |
| for (++m; m != b; ++m) { | |
| for (unsigned aop = 0; aop<aend; ++aop) { | |
| bool aop_is_reg = a->getOperand(aop).isReg(); | |
| if (!aop_is_reg) continue; | |
| bool aop_is_def = a->getOperand(aop).isDef(); | |
| unsigned aop_reg = a->getOperand(aop).getReg(); | |
| const unsigned mend = getLastRealOperand(m); | |
| for (unsigned mop = 0; mop<mend; ++mop) { | |
| bool mop_is_reg = m->getOperand(mop).isReg(); | |
| if (!mop_is_reg) continue; | |
| bool mop_is_def = m->getOperand(mop).isDef(); | |
| unsigned mop_reg = m->getOperand(mop).getReg(); | |
| if (aop_is_def && (mop_reg == aop_reg)) | |
| return true; // Hazard type 2, because aop = a0 | |
| else if (mop_is_def && (mop_reg == aop_reg)) | |
| return true; // Hazard type 1, because aop in {a1, a2} | |
| } | |
| } | |
| // Check hazard type 5 | |
| if (a_is_memory && m->getDesc().mayStore()) | |
| return true; | |
| } | |
| // Check hazard type 3 & 4 | |
| for (unsigned aop = 0; aop<aend; ++aop) { | |
| if (a->getOperand(aop).isReg()) { | |
| unsigned aop_reg = a->getOperand(aop).getReg(); | |
| for (unsigned bop = 0; bop<bend; ++bop) { | |
| if (b->getOperand(bop).isReg() && !b->getOperand(bop).isImplicit()) { | |
| unsigned bop_reg = b->getOperand(bop).getReg(); | |
| if (aop_reg == bop_reg) | |
| return true; | |
| } | |
| } | |
| } | |
| } | |
| return false; | |
| } | |
| static bool isDelayFiller(MachineBasicBlock &MBB, | |
| MachineBasicBlock::iterator candidate) { | |
| if (candidate == MBB.begin()) | |
| return false; | |
| MCInstrDesc brdesc = (--candidate)->getDesc(); | |
| return (brdesc.hasDelaySlot()); | |
| } | |
| static bool hasUnknownSideEffects(MachineBasicBlock::iterator &I) { | |
| if (!I->hasUnmodeledSideEffects()) | |
| return false; | |
| unsigned op = I->getOpcode(); | |
| if (op == MBlaze::ADDK || op == MBlaze::ADDIK || | |
| op == MBlaze::ADDC || op == MBlaze::ADDIC || | |
| op == MBlaze::ADDKC || op == MBlaze::ADDIKC || | |
| op == MBlaze::RSUBK || op == MBlaze::RSUBIK || | |
| op == MBlaze::RSUBC || op == MBlaze::RSUBIC || | |
| op == MBlaze::RSUBKC || op == MBlaze::RSUBIKC) | |
| return false; | |
| return true; | |
| } | |
| static MachineBasicBlock::iterator | |
| findDelayInstr(MachineBasicBlock &MBB,MachineBasicBlock::iterator slot) { | |
| MachineBasicBlock::iterator I = slot; | |
| while (true) { | |
| if (I == MBB.begin()) | |
| break; | |
| --I; | |
| MCInstrDesc desc = I->getDesc(); | |
| if (desc.hasDelaySlot() || desc.isBranch() || isDelayFiller(MBB,I) || | |
| desc.isCall() || desc.isReturn() || desc.isBarrier() || | |
| hasUnknownSideEffects(I)) | |
| break; | |
| if (hasImmInstruction(I) || delayHasHazard(I,slot)) | |
| continue; | |
| return I; | |
| } | |
| return MBB.end(); | |
| } | |
| /// runOnMachineBasicBlock - Fill in delay slots for the given basic block. | |
| /// Currently, we fill delay slots with NOPs. We assume there is only one | |
| /// delay slot per delayed instruction. | |
| bool Filler::runOnMachineBasicBlock(MachineBasicBlock &MBB) { | |
| bool Changed = false; | |
| for (MachineBasicBlock::iterator I = MBB.begin(); I != MBB.end(); ++I) | |
| if (I->getDesc().hasDelaySlot()) { | |
| MachineBasicBlock::iterator D = MBB.end(); | |
| MachineBasicBlock::iterator J = I; | |
| if (!DisableDelaySlotFiller) | |
| D = findDelayInstr(MBB,I); | |
| ++FilledSlots; | |
| Changed = true; | |
| if (D == MBB.end()) | |
| BuildMI(MBB, ++J, I->getDebugLoc(), TII->get(MBlaze::NOP)); | |
| else | |
| MBB.splice(++J, &MBB, D); | |
| } | |
| return Changed; | |
| } | |
| /// createMBlazeDelaySlotFillerPass - Returns a pass that fills in delay | |
| /// slots in MBlaze MachineFunctions | |
| FunctionPass *llvm::createMBlazeDelaySlotFillerPass(MBlazeTargetMachine &tm) { | |
| return new Filler(tm); | |
| } | |