//===- ARMInstructionSelector.cpp ----------------------------*- C++ -*-==// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// /// \file /// This file implements the targeting of the InstructionSelector class for ARM. /// \todo This should be generated by TableGen. //===----------------------------------------------------------------------===// #include "ARMRegisterBankInfo.h" #include "ARMSubtarget.h" #include "ARMTargetMachine.h" #include "llvm/CodeGen/GlobalISel/InstructionSelector.h" #include "llvm/CodeGen/MachineRegisterInfo.h" #include "llvm/Support/Debug.h" #define DEBUG_TYPE "arm-isel" using namespace llvm; #ifndef LLVM_BUILD_GLOBAL_ISEL #error "You shouldn't build this" #endif namespace { #define GET_GLOBALISEL_PREDICATE_BITSET #include "ARMGenGlobalISel.inc" #undef GET_GLOBALISEL_PREDICATE_BITSET class ARMInstructionSelector : public InstructionSelector { public: ARMInstructionSelector(const ARMBaseTargetMachine &TM, const ARMSubtarget &STI, const ARMRegisterBankInfo &RBI); bool select(MachineInstr &I) const override; private: bool selectImpl(MachineInstr &I) const; bool selectICmp(MachineInstrBuilder &MIB, const ARMBaseInstrInfo &TII, MachineRegisterInfo &MRI, const TargetRegisterInfo &TRI, const RegisterBankInfo &RBI) const; const ARMBaseInstrInfo &TII; const ARMBaseRegisterInfo &TRI; const ARMBaseTargetMachine &TM; const ARMRegisterBankInfo &RBI; const ARMSubtarget &STI; #define GET_GLOBALISEL_PREDICATES_DECL #include "ARMGenGlobalISel.inc" #undef GET_GLOBALISEL_PREDICATES_DECL // We declare the temporaries used by selectImpl() in the class to minimize the // cost of constructing placeholder values. #define GET_GLOBALISEL_TEMPORARIES_DECL #include "ARMGenGlobalISel.inc" #undef GET_GLOBALISEL_TEMPORARIES_DECL }; } // end anonymous namespace namespace llvm { InstructionSelector * createARMInstructionSelector(const ARMBaseTargetMachine &TM, const ARMSubtarget &STI, const ARMRegisterBankInfo &RBI) { return new ARMInstructionSelector(TM, STI, RBI); } } unsigned zero_reg = 0; #define GET_GLOBALISEL_IMPL #include "ARMGenGlobalISel.inc" #undef GET_GLOBALISEL_IMPL ARMInstructionSelector::ARMInstructionSelector(const ARMBaseTargetMachine &TM, const ARMSubtarget &STI, const ARMRegisterBankInfo &RBI) : InstructionSelector(), TII(*STI.getInstrInfo()), TRI(*STI.getRegisterInfo()), TM(TM), RBI(RBI), STI(STI), #define GET_GLOBALISEL_PREDICATES_INIT #include "ARMGenGlobalISel.inc" #undef GET_GLOBALISEL_PREDICATES_INIT #define GET_GLOBALISEL_TEMPORARIES_INIT #include "ARMGenGlobalISel.inc" #undef GET_GLOBALISEL_TEMPORARIES_INIT { } static bool selectCopy(MachineInstr &I, const TargetInstrInfo &TII, MachineRegisterInfo &MRI, const TargetRegisterInfo &TRI, const RegisterBankInfo &RBI) { unsigned DstReg = I.getOperand(0).getReg(); if (TargetRegisterInfo::isPhysicalRegister(DstReg)) return true; const RegisterBank *RegBank = RBI.getRegBank(DstReg, MRI, TRI); (void)RegBank; assert(RegBank && "Can't get reg bank for virtual register"); const unsigned DstSize = MRI.getType(DstReg).getSizeInBits(); assert((RegBank->getID() == ARM::GPRRegBankID || RegBank->getID() == ARM::FPRRegBankID) && "Unsupported reg bank"); const TargetRegisterClass *RC = &ARM::GPRRegClass; if (RegBank->getID() == ARM::FPRRegBankID) { if (DstSize == 32) RC = &ARM::SPRRegClass; else if (DstSize == 64) RC = &ARM::DPRRegClass; else llvm_unreachable("Unsupported destination size"); } // No need to constrain SrcReg. It will get constrained when // we hit another of its uses or its defs. // Copies do not have constraints. if (!RBI.constrainGenericRegister(DstReg, *RC, MRI)) { DEBUG(dbgs() << "Failed to constrain " << TII.getName(I.getOpcode()) << " operand\n"); return false; } return true; } static bool selectMergeValues(MachineInstrBuilder &MIB, const ARMBaseInstrInfo &TII, MachineRegisterInfo &MRI, const TargetRegisterInfo &TRI, const RegisterBankInfo &RBI) { assert(TII.getSubtarget().hasVFP2() && "Can't select merge without VFP"); // We only support G_MERGE_VALUES as a way to stick together two scalar GPRs // into one DPR. unsigned VReg0 = MIB->getOperand(0).getReg(); (void)VReg0; assert(MRI.getType(VReg0).getSizeInBits() == 64 && RBI.getRegBank(VReg0, MRI, TRI)->getID() == ARM::FPRRegBankID && "Unsupported operand for G_MERGE_VALUES"); unsigned VReg1 = MIB->getOperand(1).getReg(); (void)VReg1; assert(MRI.getType(VReg1).getSizeInBits() == 32 && RBI.getRegBank(VReg1, MRI, TRI)->getID() == ARM::GPRRegBankID && "Unsupported operand for G_MERGE_VALUES"); unsigned VReg2 = MIB->getOperand(2).getReg(); (void)VReg2; assert(MRI.getType(VReg2).getSizeInBits() == 32 && RBI.getRegBank(VReg2, MRI, TRI)->getID() == ARM::GPRRegBankID && "Unsupported operand for G_MERGE_VALUES"); MIB->setDesc(TII.get(ARM::VMOVDRR)); MIB.add(predOps(ARMCC::AL)); return true; } static bool selectUnmergeValues(MachineInstrBuilder &MIB, const ARMBaseInstrInfo &TII, MachineRegisterInfo &MRI, const TargetRegisterInfo &TRI, const RegisterBankInfo &RBI) { assert(TII.getSubtarget().hasVFP2() && "Can't select unmerge without VFP"); // We only support G_UNMERGE_VALUES as a way to break up one DPR into two // GPRs. unsigned VReg0 = MIB->getOperand(0).getReg(); (void)VReg0; assert(MRI.getType(VReg0).getSizeInBits() == 32 && RBI.getRegBank(VReg0, MRI, TRI)->getID() == ARM::GPRRegBankID && "Unsupported operand for G_UNMERGE_VALUES"); unsigned VReg1 = MIB->getOperand(1).getReg(); (void)VReg1; assert(MRI.getType(VReg1).getSizeInBits() == 32 && RBI.getRegBank(VReg1, MRI, TRI)->getID() == ARM::GPRRegBankID && "Unsupported operand for G_UNMERGE_VALUES"); unsigned VReg2 = MIB->getOperand(2).getReg(); (void)VReg2; assert(MRI.getType(VReg2).getSizeInBits() == 64 && RBI.getRegBank(VReg2, MRI, TRI)->getID() == ARM::FPRRegBankID && "Unsupported operand for G_UNMERGE_VALUES"); MIB->setDesc(TII.get(ARM::VMOVRRD)); MIB.add(predOps(ARMCC::AL)); return true; } /// Select the opcode for simple extensions (that translate to a single SXT/UXT /// instruction). Extension operations more complicated than that should not /// invoke this. Returns the original opcode if it doesn't know how to select a /// better one. static unsigned selectSimpleExtOpc(unsigned Opc, unsigned Size) { using namespace TargetOpcode; if (Size != 8 && Size != 16) return Opc; if (Opc == G_SEXT) return Size == 8 ? ARM::SXTB : ARM::SXTH; if (Opc == G_ZEXT) return Size == 8 ? ARM::UXTB : ARM::UXTH; return Opc; } /// Select the opcode for simple loads and stores. For types smaller than 32 /// bits, the value will be zero extended. Returns the original opcode if it /// doesn't know how to select a better one. static unsigned selectLoadStoreOpCode(unsigned Opc, unsigned RegBank, unsigned Size) { bool isStore = Opc == TargetOpcode::G_STORE; if (RegBank == ARM::GPRRegBankID) { switch (Size) { case 1: case 8: return isStore ? ARM::STRBi12 : ARM::LDRBi12; case 16: return isStore ? ARM::STRH : ARM::LDRH; case 32: return isStore ? ARM::STRi12 : ARM::LDRi12; default: return Opc; } } if (RegBank == ARM::FPRRegBankID) { switch (Size) { case 32: return isStore ? ARM::VSTRS : ARM::VLDRS; case 64: return isStore ? ARM::VSTRD : ARM::VLDRD; default: return Opc; } } return Opc; } static ARMCC::CondCodes getComparePred(CmpInst::Predicate Pred) { switch (Pred) { // Needs two compares... case CmpInst::FCMP_ONE: case CmpInst::FCMP_UEQ: default: // AL is our "false" for now. The other two need more compares. return ARMCC::AL; case CmpInst::ICMP_EQ: case CmpInst::FCMP_OEQ: return ARMCC::EQ; case CmpInst::ICMP_SGT: case CmpInst::FCMP_OGT: return ARMCC::GT; case CmpInst::ICMP_SGE: case CmpInst::FCMP_OGE: return ARMCC::GE; case CmpInst::ICMP_UGT: case CmpInst::FCMP_UGT: return ARMCC::HI; case CmpInst::FCMP_OLT: return ARMCC::MI; case CmpInst::ICMP_ULE: case CmpInst::FCMP_OLE: return ARMCC::LS; case CmpInst::FCMP_ORD: return ARMCC::VC; case CmpInst::FCMP_UNO: return ARMCC::VS; case CmpInst::FCMP_UGE: return ARMCC::PL; case CmpInst::ICMP_SLT: case CmpInst::FCMP_ULT: return ARMCC::LT; case CmpInst::ICMP_SLE: case CmpInst::FCMP_ULE: return ARMCC::LE; case CmpInst::FCMP_UNE: case CmpInst::ICMP_NE: return ARMCC::NE; case CmpInst::ICMP_UGE: return ARMCC::HS; case CmpInst::ICMP_ULT: return ARMCC::LO; } } 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(); // 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; // 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; // Move 1 into the result register if the flags say so. auto ResReg = MIB->getOperand(0).getReg(); auto Cond = static_cast(MIB->getOperand(1).getPredicate()); auto ARMCond = getComparePred(Cond); if (ARMCond == ARMCC::AL) return false; 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)) return false; MIB->eraseFromParent(); return true; } bool ARMInstructionSelector::select(MachineInstr &I) const { assert(I.getParent() && "Instruction should be in a basic block!"); assert(I.getParent()->getParent() && "Instruction should be in a function!"); auto &MBB = *I.getParent(); auto &MF = *MBB.getParent(); auto &MRI = MF.getRegInfo(); if (!isPreISelGenericOpcode(I.getOpcode())) { if (I.isCopy()) return selectCopy(I, TII, MRI, TRI, RBI); return true; } if (selectImpl(I)) return true; MachineInstrBuilder MIB{MF, I}; bool isSExt = false; using namespace TargetOpcode; switch (I.getOpcode()) { case G_SEXT: isSExt = true; LLVM_FALLTHROUGH; case G_ZEXT: { LLT DstTy = MRI.getType(I.getOperand(0).getReg()); // FIXME: Smaller destination sizes coming soon! if (DstTy.getSizeInBits() != 32) { DEBUG(dbgs() << "Unsupported destination size for extension"); return false; } LLT SrcTy = MRI.getType(I.getOperand(1).getReg()); unsigned SrcSize = SrcTy.getSizeInBits(); switch (SrcSize) { case 1: { // ZExt boils down to & 0x1; for SExt we also subtract that from 0 I.setDesc(TII.get(ARM::ANDri)); MIB.addImm(1).add(predOps(ARMCC::AL)).add(condCodeOp()); if (isSExt) { unsigned SExtResult = I.getOperand(0).getReg(); // Use a new virtual register for the result of the AND unsigned AndResult = MRI.createVirtualRegister(&ARM::GPRRegClass); I.getOperand(0).setReg(AndResult); auto InsertBefore = std::next(I.getIterator()); auto SubI = BuildMI(MBB, InsertBefore, I.getDebugLoc(), TII.get(ARM::RSBri)) .addDef(SExtResult) .addUse(AndResult) .addImm(0) .add(predOps(ARMCC::AL)) .add(condCodeOp()); if (!constrainSelectedInstRegOperands(*SubI, TII, TRI, RBI)) return false; } break; } case 8: case 16: { unsigned NewOpc = selectSimpleExtOpc(I.getOpcode(), SrcSize); if (NewOpc == I.getOpcode()) return false; I.setDesc(TII.get(NewOpc)); MIB.addImm(0).add(predOps(ARMCC::AL)); break; } default: DEBUG(dbgs() << "Unsupported source size for extension"); return false; } break; } case G_ANYEXT: case G_TRUNC: { // The high bits are undefined, so there's nothing special to do, just // treat it as a copy. auto SrcReg = I.getOperand(1).getReg(); auto DstReg = I.getOperand(0).getReg(); const auto &SrcRegBank = *RBI.getRegBank(SrcReg, MRI, TRI); const auto &DstRegBank = *RBI.getRegBank(DstReg, MRI, TRI); if (SrcRegBank.getID() != DstRegBank.getID()) { DEBUG(dbgs() << "G_TRUNC/G_ANYEXT operands on different register banks\n"); return false; } if (SrcRegBank.getID() != ARM::GPRRegBankID) { DEBUG(dbgs() << "G_TRUNC/G_ANYEXT on non-GPR not supported yet\n"); return false; } I.setDesc(TII.get(COPY)); return selectCopy(I, TII, MRI, TRI, RBI); } case G_ICMP: return selectICmp(MIB, TII, MRI, TRI, RBI); case G_GEP: I.setDesc(TII.get(ARM::ADDrr)); MIB.add(predOps(ARMCC::AL)).add(condCodeOp()); break; case G_FRAME_INDEX: // Add 0 to the given frame index and hope it will eventually be folded into // the user(s). I.setDesc(TII.get(ARM::ADDri)); MIB.addImm(0).add(predOps(ARMCC::AL)).add(condCodeOp()); break; case G_CONSTANT: { unsigned Reg = I.getOperand(0).getReg(); if (MRI.getType(Reg).getSizeInBits() != 32) return false; assert(RBI.getRegBank(Reg, MRI, TRI)->getID() == ARM::GPRRegBankID && "Expected constant to live in a GPR"); I.setDesc(TII.get(ARM::MOVi)); MIB.add(predOps(ARMCC::AL)).add(condCodeOp()); auto &Val = I.getOperand(1); if (Val.isCImm()) { if (Val.getCImm()->getBitWidth() > 32) return false; Val.ChangeToImmediate(Val.getCImm()->getZExtValue()); } if (!Val.isImm()) { return false; } break; } case G_STORE: case G_LOAD: { const auto &MemOp = **I.memoperands_begin(); if (MemOp.getOrdering() != AtomicOrdering::NotAtomic) { DEBUG(dbgs() << "Atomic load/store not supported yet\n"); return false; } unsigned Reg = I.getOperand(0).getReg(); unsigned RegBank = RBI.getRegBank(Reg, MRI, TRI)->getID(); LLT ValTy = MRI.getType(Reg); const auto ValSize = ValTy.getSizeInBits(); assert((ValSize != 64 || TII.getSubtarget().hasVFP2()) && "Don't know how to load/store 64-bit value without VFP"); const auto NewOpc = selectLoadStoreOpCode(I.getOpcode(), RegBank, ValSize); if (NewOpc == G_LOAD || NewOpc == G_STORE) return false; I.setDesc(TII.get(NewOpc)); if (NewOpc == ARM::LDRH || NewOpc == ARM::STRH) // LDRH has a funny addressing mode (there's already a FIXME for it). MIB.addReg(0); MIB.addImm(0).add(predOps(ARMCC::AL)); break; } case G_MERGE_VALUES: { if (!selectMergeValues(MIB, TII, MRI, TRI, RBI)) return false; break; } case G_UNMERGE_VALUES: { if (!selectUnmergeValues(MIB, TII, MRI, TRI, RBI)) return false; break; } default: return false; } return constrainSelectedInstRegOperands(I, TII, TRI, RBI); }