diff options
Diffstat (limited to 'llvm/lib/Target')
| -rw-r--r-- | llvm/lib/Target/ARM/ARMInstructionSelector.cpp | 297 |
1 files changed, 234 insertions, 63 deletions
diff --git a/llvm/lib/Target/ARM/ARMInstructionSelector.cpp b/llvm/lib/Target/ARM/ARMInstructionSelector.cpp index 8d952d21af6..c7f1d483bd4 100644 --- a/llvm/lib/Target/ARM/ARMInstructionSelector.cpp +++ b/llvm/lib/Target/ARM/ARMInstructionSelector.cpp @@ -44,9 +44,12 @@ public: private: bool selectImpl(MachineInstr &I) const; - bool selectICmp(MachineInstrBuilder &MIB, const ARMBaseInstrInfo &TII, - MachineRegisterInfo &MRI, const TargetRegisterInfo &TRI, - const RegisterBankInfo &RBI) const; + template <typename T> struct CmpHelper; + + template <typename T> + bool selectCmp(MachineInstrBuilder &MIB, const ARMBaseInstrInfo &TII, + MachineRegisterInfo &MRI, const TargetRegisterInfo &TRI, + const RegisterBankInfo &RBI) const; bool selectSelect(MachineInstrBuilder &MIB, const ARMBaseInstrInfo &TII, MachineRegisterInfo &MRI, const TargetRegisterInfo &TRI, @@ -253,101 +256,266 @@ static unsigned selectLoadStoreOpCode(unsigned Opc, unsigned RegBank, return Opc; } -static ARMCC::CondCodes getComparePred(CmpInst::Predicate Pred) { +// When lowering comparisons, we sometimes need to perform two compares instead +// of just one. Get the condition codes for both comparisons. If only one is +// needed, the second member of the pair is ARMCC::AL. +static std::pair<ARMCC::CondCodes, ARMCC::CondCodes> +getComparePreds(CmpInst::Predicate Pred) { + std::pair<ARMCC::CondCodes, ARMCC::CondCodes> Preds = {ARMCC::AL, ARMCC::AL}; switch (Pred) { - // Needs two compares... case CmpInst::FCMP_ONE: + Preds = {ARMCC::GT, ARMCC::MI}; + break; case CmpInst::FCMP_UEQ: - default: - // AL is our "false" for now. The other two need more compares. - return ARMCC::AL; + Preds = {ARMCC::EQ, ARMCC::VS}; + break; case CmpInst::ICMP_EQ: case CmpInst::FCMP_OEQ: - return ARMCC::EQ; + Preds.first = ARMCC::EQ; + break; case CmpInst::ICMP_SGT: case CmpInst::FCMP_OGT: - return ARMCC::GT; + Preds.first = ARMCC::GT; + break; case CmpInst::ICMP_SGE: case CmpInst::FCMP_OGE: - return ARMCC::GE; + Preds.first = ARMCC::GE; + break; case CmpInst::ICMP_UGT: case CmpInst::FCMP_UGT: - return ARMCC::HI; + Preds.first = ARMCC::HI; + break; case CmpInst::FCMP_OLT: - return ARMCC::MI; + Preds.first = ARMCC::MI; + break; case CmpInst::ICMP_ULE: case CmpInst::FCMP_OLE: - return ARMCC::LS; + Preds.first = ARMCC::LS; + break; case CmpInst::FCMP_ORD: - return ARMCC::VC; + Preds.first = ARMCC::VC; + break; case CmpInst::FCMP_UNO: - return ARMCC::VS; + Preds.first = ARMCC::VS; + break; case CmpInst::FCMP_UGE: - return ARMCC::PL; + Preds.first = ARMCC::PL; + break; case CmpInst::ICMP_SLT: case CmpInst::FCMP_ULT: - return ARMCC::LT; + Preds.first = ARMCC::LT; + break; case CmpInst::ICMP_SLE: case CmpInst::FCMP_ULE: - return ARMCC::LE; + Preds.first = ARMCC::LE; + break; case CmpInst::FCMP_UNE: case CmpInst::ICMP_NE: - return ARMCC::NE; + Preds.first = ARMCC::NE; + break; case CmpInst::ICMP_UGE: - return ARMCC::HS; + Preds.first = ARMCC::HS; + break; case CmpInst::ICMP_ULT: - return ARMCC::LO; + Preds.first = ARMCC::LO; + break; + default: + break; } + assert(Preds.first != ARMCC::AL && "No comparisons needed?"); + return Preds; } -bool ARMInstructionSelector::selectICmp(MachineInstrBuilder &MIB, - const ARMBaseInstrInfo &TII, - MachineRegisterInfo &MRI, - const TargetRegisterInfo &TRI, - const RegisterBankInfo &RBI) const { - auto &MBB = *MIB->getParent(); - auto InsertBefore = std::next(MIB->getIterator()); - auto &DebugLoc = MIB->getDebugLoc(); +template <typename T> struct ARMInstructionSelector::CmpHelper { + CmpHelper(const ARMInstructionSelector &Selector, MachineInstrBuilder &MIB, + const ARMBaseInstrInfo &TII, MachineRegisterInfo &MRI, + const TargetRegisterInfo &TRI, const RegisterBankInfo &RBI) + : MBB(*MIB->getParent()), InsertBefore(std::next(MIB->getIterator())), + DebugLoc(MIB->getDebugLoc()), TII(TII), MRI(MRI), TRI(TRI), RBI(RBI), + Selector(Selector) {} - // Move 0 into the result register. - auto Mov0I = BuildMI(MBB, InsertBefore, DebugLoc, TII.get(ARM::MOVi)) - .addDef(MRI.createVirtualRegister(&ARM::GPRRegClass)) - .addImm(0) - .add(predOps(ARMCC::AL)) - .add(condCodeOp()); - if (!constrainSelectedInstRegOperands(*Mov0I, TII, TRI, RBI)) - return false; + // The opcode used for performing the comparison. + static const unsigned ComparisonOpcode; - // Perform the comparison. - auto LHSReg = MIB->getOperand(2).getReg(); - auto RHSReg = MIB->getOperand(3).getReg(); - assert(MRI.getType(LHSReg) == MRI.getType(RHSReg) && - MRI.getType(LHSReg).getSizeInBits() == 32 && - MRI.getType(RHSReg).getSizeInBits() == 32 && - "Unsupported types for comparison operation"); - auto CmpI = BuildMI(MBB, InsertBefore, DebugLoc, TII.get(ARM::CMPrr)) - .addUse(LHSReg) - .addUse(RHSReg) - .add(predOps(ARMCC::AL)); - if (!constrainSelectedInstRegOperands(*CmpI, TII, TRI, RBI)) - return false; + // The opcode used for reading the flags set by the comparison. May be + // ARM::INSTRUCTION_LIST_END if we don't need to read the flags. + static const unsigned ReadFlagsOpcode; + + // The opcode used for selecting the result register, based on the value + // of the flags. + static const unsigned SetResultOpcode = ARM::MOVCCi; + + // The assumed register bank ID for the operands. + static const unsigned OperandRegBankID; + + // The assumed register bank ID for the result. + static const unsigned ResultRegBankID = ARM::GPRRegBankID; + + unsigned getZeroRegister() { + unsigned Reg = MRI.createVirtualRegister(&ARM::GPRRegClass); + putConstant(Reg, 0); + return Reg; + } + + void putConstant(unsigned DestReg, unsigned Constant) { + (void)BuildMI(MBB, InsertBefore, DebugLoc, TII.get(ARM::MOVi)) + .addDef(DestReg) + .addImm(Constant) + .add(predOps(ARMCC::AL)) + .add(condCodeOp()); + } + + bool insertComparison(unsigned ResReg, ARMCC::CondCodes Cond, unsigned LHSReg, + unsigned RHSReg, unsigned PrevRes) { + + // Perform the comparison. + auto CmpI = BuildMI(MBB, InsertBefore, DebugLoc, TII.get(ComparisonOpcode)) + .addUse(LHSReg) + .addUse(RHSReg) + .add(predOps(ARMCC::AL)); + if (!Selector.constrainSelectedInstRegOperands(*CmpI, TII, TRI, RBI)) + return false; + + // Read the comparison flags (if necessary). + if (ReadFlagsOpcode != ARM::INSTRUCTION_LIST_END) { + auto ReadI = + BuildMI(MBB, InsertBefore, DebugLoc, TII.get(ReadFlagsOpcode)) + .add(predOps(ARMCC::AL)); + if (!Selector.constrainSelectedInstRegOperands(*ReadI, TII, TRI, RBI)) + return false; + } + + // Select either 1 or the previous result based on the value of the flags. + auto Mov1I = BuildMI(MBB, InsertBefore, DebugLoc, TII.get(SetResultOpcode)) + .addDef(ResReg) + .addUse(PrevRes) + .addImm(1) + .add(predOps(Cond, ARM::CPSR)); + if (!Selector.constrainSelectedInstRegOperands(*Mov1I, TII, TRI, RBI)) + return false; + + return true; + } + + bool validateOpRegs(unsigned LHSReg, unsigned RHSReg) { + return MRI.getType(LHSReg) == MRI.getType(RHSReg) && + validateOpReg(LHSReg, MRI, TRI, RBI) && + validateOpReg(RHSReg, MRI, TRI, RBI); + } + + bool validateResReg(unsigned ResReg) { + if (MRI.getType(ResReg).getSizeInBits() != 1) { + DEBUG(dbgs() << "Unsupported size for comparison operand"); + return false; + } + + if (RBI.getRegBank(ResReg, MRI, TRI)->getID() != ResultRegBankID) { + DEBUG(dbgs() << "Unsupported register bank for comparison operand"); + return false; + } + + return true; + } + +private: + bool validateOpReg(unsigned OpReg, MachineRegisterInfo &MRI, + const TargetRegisterInfo &TRI, + const RegisterBankInfo &RBI) { + if (MRI.getType(OpReg).getSizeInBits() != 32) { + DEBUG(dbgs() << "Unsupported size for comparison operand"); + return false; + } + + if (RBI.getRegBank(OpReg, MRI, TRI)->getID() != OperandRegBankID) { + DEBUG(dbgs() << "Unsupported register bank for comparison operand"); + return false; + } + + return true; + } + + MachineBasicBlock &MBB; + MachineBasicBlock::instr_iterator InsertBefore; + const DebugLoc &DebugLoc; + + const ARMBaseInstrInfo &TII; + MachineRegisterInfo &MRI; + const TargetRegisterInfo &TRI; + const RegisterBankInfo &RBI; + + const ARMInstructionSelector &Selector; +}; + +// Specialize the opcode to be used for comparing different types of operands. +template <> +const unsigned ARMInstructionSelector::CmpHelper<int>::ComparisonOpcode = + ARM::CMPrr; +template <> +const unsigned ARMInstructionSelector::CmpHelper<float>::ComparisonOpcode = + ARM::VCMPS; + +// Specialize the opcode to be used for reading the comparison flags for +// different types of operands. +template <> +const unsigned ARMInstructionSelector::CmpHelper<int>::ReadFlagsOpcode = + ARM::INSTRUCTION_LIST_END; +template <> +const unsigned ARMInstructionSelector::CmpHelper<float>::ReadFlagsOpcode = + ARM::FMSTAT; + +// Specialize the register bank where the operands of the comparison are assumed +// to live. +template <> +const unsigned ARMInstructionSelector::CmpHelper<int>::OperandRegBankID = + ARM::GPRRegBankID; +template <> +const unsigned ARMInstructionSelector::CmpHelper<float>::OperandRegBankID = + ARM::FPRRegBankID; + +template <typename T> +bool ARMInstructionSelector::selectCmp(MachineInstrBuilder &MIB, + const ARMBaseInstrInfo &TII, + MachineRegisterInfo &MRI, + const TargetRegisterInfo &TRI, + const RegisterBankInfo &RBI) const { + auto Helper = CmpHelper<T>(*this, MIB, TII, MRI, TRI, RBI); - // Move 1 into the result register if the flags say so. auto ResReg = MIB->getOperand(0).getReg(); + if (!Helper.validateResReg(ResReg)) + return false; + auto Cond = static_cast<CmpInst::Predicate>(MIB->getOperand(1).getPredicate()); - auto ARMCond = getComparePred(Cond); - if (ARMCond == ARMCC::AL) - return false; + if (Cond == CmpInst::FCMP_TRUE || Cond == CmpInst::FCMP_FALSE) { + Helper.putConstant(ResReg, Cond == CmpInst::FCMP_TRUE ? 1 : 0); + MIB->eraseFromParent(); + return true; + } - auto Mov1I = BuildMI(MBB, InsertBefore, DebugLoc, TII.get(ARM::MOVCCi)) - .addDef(ResReg) - .addUse(Mov0I->getOperand(0).getReg()) - .addImm(1) - .add(predOps(ARMCond, ARM::CPSR)); - if (!constrainSelectedInstRegOperands(*Mov1I, TII, TRI, RBI)) + auto LHSReg = MIB->getOperand(2).getReg(); + auto RHSReg = MIB->getOperand(3).getReg(); + if (!Helper.validateOpRegs(LHSReg, RHSReg)) return false; + auto ARMConds = getComparePreds(Cond); + auto ZeroReg = Helper.getZeroRegister(); + + if (ARMConds.second == ARMCC::AL) { + // Simple case, we only need one comparison and we're done. + if (!Helper.insertComparison(ResReg, ARMConds.first, LHSReg, RHSReg, + ZeroReg)) + return false; + } else { + // Not so simple, we need two successive comparisons. + auto IntermediateRes = MRI.createVirtualRegister(&ARM::GPRRegClass); + if (!Helper.insertComparison(IntermediateRes, ARMConds.first, LHSReg, + RHSReg, ZeroReg)) + return false; + if (!Helper.insertComparison(ResReg, ARMConds.second, LHSReg, RHSReg, + IntermediateRes)) + return false; + } + MIB->eraseFromParent(); return true; } @@ -496,10 +664,13 @@ bool ARMInstructionSelector::select(MachineInstr &I) const { I.setDesc(TII.get(COPY)); return selectCopy(I, TII, MRI, TRI, RBI); } - case G_ICMP: - return selectICmp(MIB, TII, MRI, TRI, RBI); case G_SELECT: return selectSelect(MIB, TII, MRI, TRI, RBI); + case G_ICMP: + return selectCmp<int>(MIB, TII, MRI, TRI, RBI); + case G_FCMP: + assert(TII.getSubtarget().hasVFP2() && "Can't select fcmp without VFP"); + return selectCmp<float>(MIB, TII, MRI, TRI, RBI); case G_GEP: I.setDesc(TII.get(ARM::ADDrr)); MIB.add(predOps(ARMCC::AL)).add(condCodeOp()); |

