| //===- MipsInstructionSelector.cpp ------------------------------*- C++ -*-===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| /// \file |
| /// This file implements the targeting of the InstructionSelector class for |
| /// Mips. |
| /// \todo This should be generated by TableGen. |
| //===----------------------------------------------------------------------===// |
| |
| #include "MCTargetDesc/MipsInstPrinter.h" |
| #include "MipsMachineFunction.h" |
| #include "MipsRegisterBankInfo.h" |
| #include "MipsTargetMachine.h" |
| #include "llvm/CodeGen/GlobalISel/InstructionSelectorImpl.h" |
| #include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h" |
| #include "llvm/CodeGen/MachineJumpTableInfo.h" |
| #include "llvm/IR/IntrinsicsMips.h" |
| |
| #define DEBUG_TYPE "mips-isel" |
| |
| using namespace llvm; |
| |
| namespace { |
| |
| #define GET_GLOBALISEL_PREDICATE_BITSET |
| #include "MipsGenGlobalISel.inc" |
| #undef GET_GLOBALISEL_PREDICATE_BITSET |
| |
| class MipsInstructionSelector : public InstructionSelector { |
| public: |
| MipsInstructionSelector(const MipsTargetMachine &TM, const MipsSubtarget &STI, |
| const MipsRegisterBankInfo &RBI); |
| |
| bool select(MachineInstr &I) override; |
| static const char *getName() { return DEBUG_TYPE; } |
| |
| private: |
| bool selectImpl(MachineInstr &I, CodeGenCoverage &CoverageInfo) const; |
| bool isRegInGprb(Register Reg, MachineRegisterInfo &MRI) const; |
| bool isRegInFprb(Register Reg, MachineRegisterInfo &MRI) const; |
| bool materialize32BitImm(Register DestReg, APInt Imm, |
| MachineIRBuilder &B) const; |
| bool selectCopy(MachineInstr &I, MachineRegisterInfo &MRI) const; |
| const TargetRegisterClass * |
| getRegClassForTypeOnBank(Register Reg, MachineRegisterInfo &MRI) const; |
| unsigned selectLoadStoreOpCode(MachineInstr &I, |
| MachineRegisterInfo &MRI) const; |
| bool buildUnalignedStore(MachineInstr &I, unsigned Opc, |
| MachineOperand &BaseAddr, unsigned Offset, |
| MachineMemOperand *MMO) const; |
| bool buildUnalignedLoad(MachineInstr &I, unsigned Opc, Register Dest, |
| MachineOperand &BaseAddr, unsigned Offset, |
| Register TiedDest, MachineMemOperand *MMO) const; |
| |
| const MipsTargetMachine &TM; |
| const MipsSubtarget &STI; |
| const MipsInstrInfo &TII; |
| const MipsRegisterInfo &TRI; |
| const MipsRegisterBankInfo &RBI; |
| |
| #define GET_GLOBALISEL_PREDICATES_DECL |
| #include "MipsGenGlobalISel.inc" |
| #undef GET_GLOBALISEL_PREDICATES_DECL |
| |
| #define GET_GLOBALISEL_TEMPORARIES_DECL |
| #include "MipsGenGlobalISel.inc" |
| #undef GET_GLOBALISEL_TEMPORARIES_DECL |
| }; |
| |
| } // end anonymous namespace |
| |
| #define GET_GLOBALISEL_IMPL |
| #include "MipsGenGlobalISel.inc" |
| #undef GET_GLOBALISEL_IMPL |
| |
| MipsInstructionSelector::MipsInstructionSelector( |
| const MipsTargetMachine &TM, const MipsSubtarget &STI, |
| const MipsRegisterBankInfo &RBI) |
| : InstructionSelector(), TM(TM), STI(STI), TII(*STI.getInstrInfo()), |
| TRI(*STI.getRegisterInfo()), RBI(RBI), |
| |
| #define GET_GLOBALISEL_PREDICATES_INIT |
| #include "MipsGenGlobalISel.inc" |
| #undef GET_GLOBALISEL_PREDICATES_INIT |
| #define GET_GLOBALISEL_TEMPORARIES_INIT |
| #include "MipsGenGlobalISel.inc" |
| #undef GET_GLOBALISEL_TEMPORARIES_INIT |
| { |
| } |
| |
| bool MipsInstructionSelector::isRegInGprb(Register Reg, |
| MachineRegisterInfo &MRI) const { |
| return RBI.getRegBank(Reg, MRI, TRI)->getID() == Mips::GPRBRegBankID; |
| } |
| |
| bool MipsInstructionSelector::isRegInFprb(Register Reg, |
| MachineRegisterInfo &MRI) const { |
| return RBI.getRegBank(Reg, MRI, TRI)->getID() == Mips::FPRBRegBankID; |
| } |
| |
| bool MipsInstructionSelector::selectCopy(MachineInstr &I, |
| MachineRegisterInfo &MRI) const { |
| Register DstReg = I.getOperand(0).getReg(); |
| if (Register::isPhysicalRegister(DstReg)) |
| return true; |
| |
| const TargetRegisterClass *RC = getRegClassForTypeOnBank(DstReg, MRI); |
| if (!RBI.constrainGenericRegister(DstReg, *RC, MRI)) { |
| LLVM_DEBUG(dbgs() << "Failed to constrain " << TII.getName(I.getOpcode()) |
| << " operand\n"); |
| return false; |
| } |
| return true; |
| } |
| |
| const TargetRegisterClass *MipsInstructionSelector::getRegClassForTypeOnBank( |
| Register Reg, MachineRegisterInfo &MRI) const { |
| const LLT Ty = MRI.getType(Reg); |
| const unsigned TySize = Ty.getSizeInBits(); |
| |
| if (isRegInGprb(Reg, MRI)) { |
| assert((Ty.isScalar() || Ty.isPointer()) && TySize == 32 && |
| "Register class not available for LLT, register bank combination"); |
| return &Mips::GPR32RegClass; |
| } |
| |
| if (isRegInFprb(Reg, MRI)) { |
| if (Ty.isScalar()) { |
| assert((TySize == 32 || TySize == 64) && |
| "Register class not available for LLT, register bank combination"); |
| if (TySize == 32) |
| return &Mips::FGR32RegClass; |
| return STI.isFP64bit() ? &Mips::FGR64RegClass : &Mips::AFGR64RegClass; |
| } |
| } |
| |
| llvm_unreachable("Unsupported register bank."); |
| } |
| |
| bool MipsInstructionSelector::materialize32BitImm(Register DestReg, APInt Imm, |
| MachineIRBuilder &B) const { |
| assert(Imm.getBitWidth() == 32 && "Unsupported immediate size."); |
| // Ori zero extends immediate. Used for values with zeros in high 16 bits. |
| if (Imm.getHiBits(16).isZero()) { |
| MachineInstr *Inst = |
| B.buildInstr(Mips::ORi, {DestReg}, {Register(Mips::ZERO)}) |
| .addImm(Imm.getLoBits(16).getLimitedValue()); |
| return constrainSelectedInstRegOperands(*Inst, TII, TRI, RBI); |
| } |
| // Lui places immediate in high 16 bits and sets low 16 bits to zero. |
| if (Imm.getLoBits(16).isZero()) { |
| MachineInstr *Inst = B.buildInstr(Mips::LUi, {DestReg}, {}) |
| .addImm(Imm.getHiBits(16).getLimitedValue()); |
| return constrainSelectedInstRegOperands(*Inst, TII, TRI, RBI); |
| } |
| // ADDiu sign extends immediate. Used for values with 1s in high 17 bits. |
| if (Imm.isSignedIntN(16)) { |
| MachineInstr *Inst = |
| B.buildInstr(Mips::ADDiu, {DestReg}, {Register(Mips::ZERO)}) |
| .addImm(Imm.getLoBits(16).getLimitedValue()); |
| return constrainSelectedInstRegOperands(*Inst, TII, TRI, RBI); |
| } |
| // Values that cannot be materialized with single immediate instruction. |
| Register LUiReg = B.getMRI()->createVirtualRegister(&Mips::GPR32RegClass); |
| MachineInstr *LUi = B.buildInstr(Mips::LUi, {LUiReg}, {}) |
| .addImm(Imm.getHiBits(16).getLimitedValue()); |
| MachineInstr *ORi = B.buildInstr(Mips::ORi, {DestReg}, {LUiReg}) |
| .addImm(Imm.getLoBits(16).getLimitedValue()); |
| if (!constrainSelectedInstRegOperands(*LUi, TII, TRI, RBI)) |
| return false; |
| if (!constrainSelectedInstRegOperands(*ORi, TII, TRI, RBI)) |
| return false; |
| return true; |
| } |
| |
| /// When I.getOpcode() is returned, we failed to select MIPS instruction opcode. |
| unsigned |
| MipsInstructionSelector::selectLoadStoreOpCode(MachineInstr &I, |
| MachineRegisterInfo &MRI) const { |
| const Register ValueReg = I.getOperand(0).getReg(); |
| const LLT Ty = MRI.getType(ValueReg); |
| const unsigned TySize = Ty.getSizeInBits(); |
| const unsigned MemSizeInBytes = (*I.memoperands_begin())->getSize(); |
| unsigned Opc = I.getOpcode(); |
| const bool isStore = Opc == TargetOpcode::G_STORE; |
| |
| if (isRegInGprb(ValueReg, MRI)) { |
| assert(((Ty.isScalar() && TySize == 32) || |
| (Ty.isPointer() && TySize == 32 && MemSizeInBytes == 4)) && |
| "Unsupported register bank, LLT, MemSizeInBytes combination"); |
| (void)TySize; |
| if (isStore) |
| switch (MemSizeInBytes) { |
| case 4: |
| return Mips::SW; |
| case 2: |
| return Mips::SH; |
| case 1: |
| return Mips::SB; |
| default: |
| return Opc; |
| } |
| else |
| // Unspecified extending load is selected into zeroExtending load. |
| switch (MemSizeInBytes) { |
| case 4: |
| return Mips::LW; |
| case 2: |
| return Opc == TargetOpcode::G_SEXTLOAD ? Mips::LH : Mips::LHu; |
| case 1: |
| return Opc == TargetOpcode::G_SEXTLOAD ? Mips::LB : Mips::LBu; |
| default: |
| return Opc; |
| } |
| } |
| |
| if (isRegInFprb(ValueReg, MRI)) { |
| if (Ty.isScalar()) { |
| assert(((TySize == 32 && MemSizeInBytes == 4) || |
| (TySize == 64 && MemSizeInBytes == 8)) && |
| "Unsupported register bank, LLT, MemSizeInBytes combination"); |
| |
| if (MemSizeInBytes == 4) |
| return isStore ? Mips::SWC1 : Mips::LWC1; |
| |
| if (STI.isFP64bit()) |
| return isStore ? Mips::SDC164 : Mips::LDC164; |
| return isStore ? Mips::SDC1 : Mips::LDC1; |
| } |
| |
| if (Ty.isVector()) { |
| assert(STI.hasMSA() && "Vector instructions require target with MSA."); |
| assert((TySize == 128 && MemSizeInBytes == 16) && |
| "Unsupported register bank, LLT, MemSizeInBytes combination"); |
| switch (Ty.getElementType().getSizeInBits()) { |
| case 8: |
| return isStore ? Mips::ST_B : Mips::LD_B; |
| case 16: |
| return isStore ? Mips::ST_H : Mips::LD_H; |
| case 32: |
| return isStore ? Mips::ST_W : Mips::LD_W; |
| case 64: |
| return isStore ? Mips::ST_D : Mips::LD_D; |
| default: |
| return Opc; |
| } |
| } |
| } |
| |
| return Opc; |
| } |
| |
| bool MipsInstructionSelector::buildUnalignedStore( |
| MachineInstr &I, unsigned Opc, MachineOperand &BaseAddr, unsigned Offset, |
| MachineMemOperand *MMO) const { |
| MachineInstr *NewInst = |
| BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(Opc)) |
| .add(I.getOperand(0)) |
| .add(BaseAddr) |
| .addImm(Offset) |
| .addMemOperand(MMO); |
| if (!constrainSelectedInstRegOperands(*NewInst, TII, TRI, RBI)) |
| return false; |
| return true; |
| } |
| |
| bool MipsInstructionSelector::buildUnalignedLoad( |
| MachineInstr &I, unsigned Opc, Register Dest, MachineOperand &BaseAddr, |
| unsigned Offset, Register TiedDest, MachineMemOperand *MMO) const { |
| MachineInstr *NewInst = |
| BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(Opc)) |
| .addDef(Dest) |
| .add(BaseAddr) |
| .addImm(Offset) |
| .addUse(TiedDest) |
| .addMemOperand(*I.memoperands_begin()); |
| if (!constrainSelectedInstRegOperands(*NewInst, TII, TRI, RBI)) |
| return false; |
| return true; |
| } |
| |
| bool MipsInstructionSelector::select(MachineInstr &I) { |
| |
| MachineBasicBlock &MBB = *I.getParent(); |
| MachineFunction &MF = *MBB.getParent(); |
| MachineRegisterInfo &MRI = MF.getRegInfo(); |
| |
| if (!isPreISelGenericOpcode(I.getOpcode())) { |
| if (I.isCopy()) |
| return selectCopy(I, MRI); |
| |
| return true; |
| } |
| |
| if (I.getOpcode() == Mips::G_MUL && |
| isRegInGprb(I.getOperand(0).getReg(), MRI)) { |
| MachineInstr *Mul = BuildMI(MBB, I, I.getDebugLoc(), TII.get(Mips::MUL)) |
| .add(I.getOperand(0)) |
| .add(I.getOperand(1)) |
| .add(I.getOperand(2)); |
| if (!constrainSelectedInstRegOperands(*Mul, TII, TRI, RBI)) |
| return false; |
| Mul->getOperand(3).setIsDead(true); |
| Mul->getOperand(4).setIsDead(true); |
| |
| I.eraseFromParent(); |
| return true; |
| } |
| |
| if (selectImpl(I, *CoverageInfo)) |
| return true; |
| |
| MachineInstr *MI = nullptr; |
| using namespace TargetOpcode; |
| |
| switch (I.getOpcode()) { |
| case G_UMULH: { |
| Register PseudoMULTuReg = MRI.createVirtualRegister(&Mips::ACC64RegClass); |
| MachineInstr *PseudoMULTu, *PseudoMove; |
| |
| PseudoMULTu = BuildMI(MBB, I, I.getDebugLoc(), TII.get(Mips::PseudoMULTu)) |
| .addDef(PseudoMULTuReg) |
| .add(I.getOperand(1)) |
| .add(I.getOperand(2)); |
| if (!constrainSelectedInstRegOperands(*PseudoMULTu, TII, TRI, RBI)) |
| return false; |
| |
| PseudoMove = BuildMI(MBB, I, I.getDebugLoc(), TII.get(Mips::PseudoMFHI)) |
| .addDef(I.getOperand(0).getReg()) |
| .addUse(PseudoMULTuReg); |
| if (!constrainSelectedInstRegOperands(*PseudoMove, TII, TRI, RBI)) |
| return false; |
| |
| I.eraseFromParent(); |
| return true; |
| } |
| case G_PTR_ADD: { |
| MI = BuildMI(MBB, I, I.getDebugLoc(), TII.get(Mips::ADDu)) |
| .add(I.getOperand(0)) |
| .add(I.getOperand(1)) |
| .add(I.getOperand(2)); |
| break; |
| } |
| case G_INTTOPTR: |
| case G_PTRTOINT: { |
| I.setDesc(TII.get(COPY)); |
| return selectCopy(I, MRI); |
| } |
| case G_FRAME_INDEX: { |
| MI = BuildMI(MBB, I, I.getDebugLoc(), TII.get(Mips::ADDiu)) |
| .add(I.getOperand(0)) |
| .add(I.getOperand(1)) |
| .addImm(0); |
| break; |
| } |
| case G_BRCOND: { |
| MI = BuildMI(MBB, I, I.getDebugLoc(), TII.get(Mips::BNE)) |
| .add(I.getOperand(0)) |
| .addUse(Mips::ZERO) |
| .add(I.getOperand(1)); |
| break; |
| } |
| case G_BRJT: { |
| unsigned EntrySize = |
| MF.getJumpTableInfo()->getEntrySize(MF.getDataLayout()); |
| assert(isPowerOf2_32(EntrySize) && |
| "Non-power-of-two jump-table entry size not supported."); |
| |
| Register JTIndex = MRI.createVirtualRegister(&Mips::GPR32RegClass); |
| MachineInstr *SLL = BuildMI(MBB, I, I.getDebugLoc(), TII.get(Mips::SLL)) |
| .addDef(JTIndex) |
| .addUse(I.getOperand(2).getReg()) |
| .addImm(Log2_32(EntrySize)); |
| if (!constrainSelectedInstRegOperands(*SLL, TII, TRI, RBI)) |
| return false; |
| |
| Register DestAddress = MRI.createVirtualRegister(&Mips::GPR32RegClass); |
| MachineInstr *ADDu = BuildMI(MBB, I, I.getDebugLoc(), TII.get(Mips::ADDu)) |
| .addDef(DestAddress) |
| .addUse(I.getOperand(0).getReg()) |
| .addUse(JTIndex); |
| if (!constrainSelectedInstRegOperands(*ADDu, TII, TRI, RBI)) |
| return false; |
| |
| Register Dest = MRI.createVirtualRegister(&Mips::GPR32RegClass); |
| MachineInstr *LW = |
| BuildMI(MBB, I, I.getDebugLoc(), TII.get(Mips::LW)) |
| .addDef(Dest) |
| .addUse(DestAddress) |
| .addJumpTableIndex(I.getOperand(1).getIndex(), MipsII::MO_ABS_LO) |
| .addMemOperand(MF.getMachineMemOperand( |
| MachinePointerInfo(), MachineMemOperand::MOLoad, 4, Align(4))); |
| if (!constrainSelectedInstRegOperands(*LW, TII, TRI, RBI)) |
| return false; |
| |
| if (MF.getTarget().isPositionIndependent()) { |
| Register DestTmp = MRI.createVirtualRegister(&Mips::GPR32RegClass); |
| LW->getOperand(0).setReg(DestTmp); |
| MachineInstr *ADDu = BuildMI(MBB, I, I.getDebugLoc(), TII.get(Mips::ADDu)) |
| .addDef(Dest) |
| .addUse(DestTmp) |
| .addUse(MF.getInfo<MipsFunctionInfo>() |
| ->getGlobalBaseRegForGlobalISel(MF)); |
| if (!constrainSelectedInstRegOperands(*ADDu, TII, TRI, RBI)) |
| return false; |
| } |
| |
| MachineInstr *Branch = |
| BuildMI(MBB, I, I.getDebugLoc(), TII.get(Mips::PseudoIndirectBranch)) |
| .addUse(Dest); |
| if (!constrainSelectedInstRegOperands(*Branch, TII, TRI, RBI)) |
| return false; |
| |
| I.eraseFromParent(); |
| return true; |
| } |
| case G_BRINDIRECT: { |
| MI = BuildMI(MBB, I, I.getDebugLoc(), TII.get(Mips::PseudoIndirectBranch)) |
| .add(I.getOperand(0)); |
| break; |
| } |
| case G_PHI: { |
| const Register DestReg = I.getOperand(0).getReg(); |
| |
| const TargetRegisterClass *DefRC = nullptr; |
| if (Register::isPhysicalRegister(DestReg)) |
| DefRC = TRI.getRegClass(DestReg); |
| else |
| DefRC = getRegClassForTypeOnBank(DestReg, MRI); |
| |
| I.setDesc(TII.get(TargetOpcode::PHI)); |
| return RBI.constrainGenericRegister(DestReg, *DefRC, MRI); |
| } |
| case G_STORE: |
| case G_LOAD: |
| case G_ZEXTLOAD: |
| case G_SEXTLOAD: { |
| auto MMO = *I.memoperands_begin(); |
| MachineOperand BaseAddr = I.getOperand(1); |
| int64_t SignedOffset = 0; |
| // Try to fold load/store + G_PTR_ADD + G_CONSTANT |
| // %SignedOffset:(s32) = G_CONSTANT i32 16_bit_signed_immediate |
| // %Addr:(p0) = G_PTR_ADD %BaseAddr, %SignedOffset |
| // %LoadResult/%StoreSrc = load/store %Addr(p0) |
| // into: |
| // %LoadResult/%StoreSrc = NewOpc %BaseAddr(p0), 16_bit_signed_immediate |
| |
| MachineInstr *Addr = MRI.getVRegDef(I.getOperand(1).getReg()); |
| if (Addr->getOpcode() == G_PTR_ADD) { |
| MachineInstr *Offset = MRI.getVRegDef(Addr->getOperand(2).getReg()); |
| if (Offset->getOpcode() == G_CONSTANT) { |
| APInt OffsetValue = Offset->getOperand(1).getCImm()->getValue(); |
| if (OffsetValue.isSignedIntN(16)) { |
| BaseAddr = Addr->getOperand(1); |
| SignedOffset = OffsetValue.getSExtValue(); |
| } |
| } |
| } |
| |
| // Unaligned memory access |
| if (MMO->getAlign() < MMO->getSize() && |
| !STI.systemSupportsUnalignedAccess()) { |
| if (MMO->getSize() != 4 || !isRegInGprb(I.getOperand(0).getReg(), MRI)) |
| return false; |
| |
| if (I.getOpcode() == G_STORE) { |
| if (!buildUnalignedStore(I, Mips::SWL, BaseAddr, SignedOffset + 3, MMO)) |
| return false; |
| if (!buildUnalignedStore(I, Mips::SWR, BaseAddr, SignedOffset, MMO)) |
| return false; |
| I.eraseFromParent(); |
| return true; |
| } |
| |
| if (I.getOpcode() == G_LOAD) { |
| Register ImplDef = MRI.createVirtualRegister(&Mips::GPR32RegClass); |
| BuildMI(MBB, I, I.getDebugLoc(), TII.get(Mips::IMPLICIT_DEF)) |
| .addDef(ImplDef); |
| Register Tmp = MRI.createVirtualRegister(&Mips::GPR32RegClass); |
| if (!buildUnalignedLoad(I, Mips::LWL, Tmp, BaseAddr, SignedOffset + 3, |
| ImplDef, MMO)) |
| return false; |
| if (!buildUnalignedLoad(I, Mips::LWR, I.getOperand(0).getReg(), |
| BaseAddr, SignedOffset, Tmp, MMO)) |
| return false; |
| I.eraseFromParent(); |
| return true; |
| } |
| |
| return false; |
| } |
| |
| const unsigned NewOpc = selectLoadStoreOpCode(I, MRI); |
| if (NewOpc == I.getOpcode()) |
| return false; |
| |
| MI = BuildMI(MBB, I, I.getDebugLoc(), TII.get(NewOpc)) |
| .add(I.getOperand(0)) |
| .add(BaseAddr) |
| .addImm(SignedOffset) |
| .addMemOperand(MMO); |
| break; |
| } |
| case G_UDIV: |
| case G_UREM: |
| case G_SDIV: |
| case G_SREM: { |
| Register HILOReg = MRI.createVirtualRegister(&Mips::ACC64RegClass); |
| bool IsSigned = I.getOpcode() == G_SREM || I.getOpcode() == G_SDIV; |
| bool IsDiv = I.getOpcode() == G_UDIV || I.getOpcode() == G_SDIV; |
| |
| MachineInstr *PseudoDIV, *PseudoMove; |
| PseudoDIV = BuildMI(MBB, I, I.getDebugLoc(), |
| TII.get(IsSigned ? Mips::PseudoSDIV : Mips::PseudoUDIV)) |
| .addDef(HILOReg) |
| .add(I.getOperand(1)) |
| .add(I.getOperand(2)); |
| if (!constrainSelectedInstRegOperands(*PseudoDIV, TII, TRI, RBI)) |
| return false; |
| |
| PseudoMove = BuildMI(MBB, I, I.getDebugLoc(), |
| TII.get(IsDiv ? Mips::PseudoMFLO : Mips::PseudoMFHI)) |
| .addDef(I.getOperand(0).getReg()) |
| .addUse(HILOReg); |
| if (!constrainSelectedInstRegOperands(*PseudoMove, TII, TRI, RBI)) |
| return false; |
| |
| I.eraseFromParent(); |
| return true; |
| } |
| case G_SELECT: { |
| // Handle operands with pointer type. |
| MI = BuildMI(MBB, I, I.getDebugLoc(), TII.get(Mips::MOVN_I_I)) |
| .add(I.getOperand(0)) |
| .add(I.getOperand(2)) |
| .add(I.getOperand(1)) |
| .add(I.getOperand(3)); |
| break; |
| } |
| case G_UNMERGE_VALUES: { |
| if (I.getNumOperands() != 3) |
| return false; |
| Register Src = I.getOperand(2).getReg(); |
| Register Lo = I.getOperand(0).getReg(); |
| Register Hi = I.getOperand(1).getReg(); |
| if (!isRegInFprb(Src, MRI) || |
| !(isRegInGprb(Lo, MRI) && isRegInGprb(Hi, MRI))) |
| return false; |
| |
| unsigned Opcode = |
| STI.isFP64bit() ? Mips::ExtractElementF64_64 : Mips::ExtractElementF64; |
| |
| MachineInstr *ExtractLo = BuildMI(MBB, I, I.getDebugLoc(), TII.get(Opcode)) |
| .addDef(Lo) |
| .addUse(Src) |
| .addImm(0); |
| if (!constrainSelectedInstRegOperands(*ExtractLo, TII, TRI, RBI)) |
| return false; |
| |
| MachineInstr *ExtractHi = BuildMI(MBB, I, I.getDebugLoc(), TII.get(Opcode)) |
| .addDef(Hi) |
| .addUse(Src) |
| .addImm(1); |
| if (!constrainSelectedInstRegOperands(*ExtractHi, TII, TRI, RBI)) |
| return false; |
| |
| I.eraseFromParent(); |
| return true; |
| } |
| case G_IMPLICIT_DEF: { |
| Register Dst = I.getOperand(0).getReg(); |
| MI = BuildMI(MBB, I, I.getDebugLoc(), TII.get(Mips::IMPLICIT_DEF)) |
| .addDef(Dst); |
| |
| // Set class based on register bank, there can be fpr and gpr implicit def. |
| MRI.setRegClass(Dst, getRegClassForTypeOnBank(Dst, MRI)); |
| break; |
| } |
| case G_CONSTANT: { |
| MachineIRBuilder B(I); |
| if (!materialize32BitImm(I.getOperand(0).getReg(), |
| I.getOperand(1).getCImm()->getValue(), B)) |
| return false; |
| |
| I.eraseFromParent(); |
| return true; |
| } |
| case G_FCONSTANT: { |
| const APFloat &FPimm = I.getOperand(1).getFPImm()->getValueAPF(); |
| APInt APImm = FPimm.bitcastToAPInt(); |
| unsigned Size = MRI.getType(I.getOperand(0).getReg()).getSizeInBits(); |
| |
| if (Size == 32) { |
| Register GPRReg = MRI.createVirtualRegister(&Mips::GPR32RegClass); |
| MachineIRBuilder B(I); |
| if (!materialize32BitImm(GPRReg, APImm, B)) |
| return false; |
| |
| MachineInstrBuilder MTC1 = |
| B.buildInstr(Mips::MTC1, {I.getOperand(0).getReg()}, {GPRReg}); |
| if (!MTC1.constrainAllUses(TII, TRI, RBI)) |
| return false; |
| } |
| if (Size == 64) { |
| Register GPRRegHigh = MRI.createVirtualRegister(&Mips::GPR32RegClass); |
| Register GPRRegLow = MRI.createVirtualRegister(&Mips::GPR32RegClass); |
| MachineIRBuilder B(I); |
| if (!materialize32BitImm(GPRRegHigh, APImm.getHiBits(32).trunc(32), B)) |
| return false; |
| if (!materialize32BitImm(GPRRegLow, APImm.getLoBits(32).trunc(32), B)) |
| return false; |
| |
| MachineInstrBuilder PairF64 = B.buildInstr( |
| STI.isFP64bit() ? Mips::BuildPairF64_64 : Mips::BuildPairF64, |
| {I.getOperand(0).getReg()}, {GPRRegLow, GPRRegHigh}); |
| if (!PairF64.constrainAllUses(TII, TRI, RBI)) |
| return false; |
| } |
| |
| I.eraseFromParent(); |
| return true; |
| } |
| case G_FABS: { |
| unsigned Size = MRI.getType(I.getOperand(0).getReg()).getSizeInBits(); |
| unsigned FABSOpcode = |
| Size == 32 ? Mips::FABS_S |
| : STI.isFP64bit() ? Mips::FABS_D64 : Mips::FABS_D32; |
| MI = BuildMI(MBB, I, I.getDebugLoc(), TII.get(FABSOpcode)) |
| .add(I.getOperand(0)) |
| .add(I.getOperand(1)); |
| break; |
| } |
| case G_FPTOSI: { |
| unsigned FromSize = MRI.getType(I.getOperand(1).getReg()).getSizeInBits(); |
| unsigned ToSize = MRI.getType(I.getOperand(0).getReg()).getSizeInBits(); |
| (void)ToSize; |
| assert((ToSize == 32) && "Unsupported integer size for G_FPTOSI"); |
| assert((FromSize == 32 || FromSize == 64) && |
| "Unsupported floating point size for G_FPTOSI"); |
| |
| unsigned Opcode; |
| if (FromSize == 32) |
| Opcode = Mips::TRUNC_W_S; |
| else |
| Opcode = STI.isFP64bit() ? Mips::TRUNC_W_D64 : Mips::TRUNC_W_D32; |
| Register ResultInFPR = MRI.createVirtualRegister(&Mips::FGR32RegClass); |
| MachineInstr *Trunc = BuildMI(MBB, I, I.getDebugLoc(), TII.get(Opcode)) |
| .addDef(ResultInFPR) |
| .addUse(I.getOperand(1).getReg()); |
| if (!constrainSelectedInstRegOperands(*Trunc, TII, TRI, RBI)) |
| return false; |
| |
| MachineInstr *Move = BuildMI(MBB, I, I.getDebugLoc(), TII.get(Mips::MFC1)) |
| .addDef(I.getOperand(0).getReg()) |
| .addUse(ResultInFPR); |
| if (!constrainSelectedInstRegOperands(*Move, TII, TRI, RBI)) |
| return false; |
| |
| I.eraseFromParent(); |
| return true; |
| } |
| case G_GLOBAL_VALUE: { |
| const llvm::GlobalValue *GVal = I.getOperand(1).getGlobal(); |
| if (MF.getTarget().isPositionIndependent()) { |
| MachineInstr *LWGOT = BuildMI(MBB, I, I.getDebugLoc(), TII.get(Mips::LW)) |
| .addDef(I.getOperand(0).getReg()) |
| .addReg(MF.getInfo<MipsFunctionInfo>() |
| ->getGlobalBaseRegForGlobalISel(MF)) |
| .addGlobalAddress(GVal); |
| // Global Values that don't have local linkage are handled differently |
| // when they are part of call sequence. MipsCallLowering::lowerCall |
| // creates G_GLOBAL_VALUE instruction as part of call sequence and adds |
| // MO_GOT_CALL flag when Callee doesn't have local linkage. |
| if (I.getOperand(1).getTargetFlags() == MipsII::MO_GOT_CALL) |
| LWGOT->getOperand(2).setTargetFlags(MipsII::MO_GOT_CALL); |
| else |
| LWGOT->getOperand(2).setTargetFlags(MipsII::MO_GOT); |
| LWGOT->addMemOperand( |
| MF, MF.getMachineMemOperand(MachinePointerInfo::getGOT(MF), |
| MachineMemOperand::MOLoad, 4, Align(4))); |
| if (!constrainSelectedInstRegOperands(*LWGOT, TII, TRI, RBI)) |
| return false; |
| |
| if (GVal->hasLocalLinkage()) { |
| Register LWGOTDef = MRI.createVirtualRegister(&Mips::GPR32RegClass); |
| LWGOT->getOperand(0).setReg(LWGOTDef); |
| |
| MachineInstr *ADDiu = |
| BuildMI(MBB, I, I.getDebugLoc(), TII.get(Mips::ADDiu)) |
| .addDef(I.getOperand(0).getReg()) |
| .addReg(LWGOTDef) |
| .addGlobalAddress(GVal); |
| ADDiu->getOperand(2).setTargetFlags(MipsII::MO_ABS_LO); |
| if (!constrainSelectedInstRegOperands(*ADDiu, TII, TRI, RBI)) |
| return false; |
| } |
| } else { |
| Register LUiReg = MRI.createVirtualRegister(&Mips::GPR32RegClass); |
| |
| MachineInstr *LUi = BuildMI(MBB, I, I.getDebugLoc(), TII.get(Mips::LUi)) |
| .addDef(LUiReg) |
| .addGlobalAddress(GVal); |
| LUi->getOperand(1).setTargetFlags(MipsII::MO_ABS_HI); |
| if (!constrainSelectedInstRegOperands(*LUi, TII, TRI, RBI)) |
| return false; |
| |
| MachineInstr *ADDiu = |
| BuildMI(MBB, I, I.getDebugLoc(), TII.get(Mips::ADDiu)) |
| .addDef(I.getOperand(0).getReg()) |
| .addUse(LUiReg) |
| .addGlobalAddress(GVal); |
| ADDiu->getOperand(2).setTargetFlags(MipsII::MO_ABS_LO); |
| if (!constrainSelectedInstRegOperands(*ADDiu, TII, TRI, RBI)) |
| return false; |
| } |
| I.eraseFromParent(); |
| return true; |
| } |
| case G_JUMP_TABLE: { |
| if (MF.getTarget().isPositionIndependent()) { |
| MI = BuildMI(MBB, I, I.getDebugLoc(), TII.get(Mips::LW)) |
| .addDef(I.getOperand(0).getReg()) |
| .addReg(MF.getInfo<MipsFunctionInfo>() |
| ->getGlobalBaseRegForGlobalISel(MF)) |
| .addJumpTableIndex(I.getOperand(1).getIndex(), MipsII::MO_GOT) |
| .addMemOperand(MF.getMachineMemOperand( |
| MachinePointerInfo::getGOT(MF), MachineMemOperand::MOLoad, 4, |
| Align(4))); |
| } else { |
| MI = |
| BuildMI(MBB, I, I.getDebugLoc(), TII.get(Mips::LUi)) |
| .addDef(I.getOperand(0).getReg()) |
| .addJumpTableIndex(I.getOperand(1).getIndex(), MipsII::MO_ABS_HI); |
| } |
| break; |
| } |
| case G_ICMP: { |
| struct Instr { |
| unsigned Opcode; |
| Register Def, LHS, RHS; |
| Instr(unsigned Opcode, Register Def, Register LHS, Register RHS) |
| : Opcode(Opcode), Def(Def), LHS(LHS), RHS(RHS){}; |
| |
| bool hasImm() const { |
| if (Opcode == Mips::SLTiu || Opcode == Mips::XORi) |
| return true; |
| return false; |
| } |
| }; |
| |
| SmallVector<struct Instr, 2> Instructions; |
| Register ICMPReg = I.getOperand(0).getReg(); |
| Register Temp = MRI.createVirtualRegister(&Mips::GPR32RegClass); |
| Register LHS = I.getOperand(2).getReg(); |
| Register RHS = I.getOperand(3).getReg(); |
| CmpInst::Predicate Cond = |
| static_cast<CmpInst::Predicate>(I.getOperand(1).getPredicate()); |
| |
| switch (Cond) { |
| case CmpInst::ICMP_EQ: // LHS == RHS -> (LHS ^ RHS) < 1 |
| Instructions.emplace_back(Mips::XOR, Temp, LHS, RHS); |
| Instructions.emplace_back(Mips::SLTiu, ICMPReg, Temp, 1); |
| break; |
| case CmpInst::ICMP_NE: // LHS != RHS -> 0 < (LHS ^ RHS) |
| Instructions.emplace_back(Mips::XOR, Temp, LHS, RHS); |
| Instructions.emplace_back(Mips::SLTu, ICMPReg, Mips::ZERO, Temp); |
| break; |
| case CmpInst::ICMP_UGT: // LHS > RHS -> RHS < LHS |
| Instructions.emplace_back(Mips::SLTu, ICMPReg, RHS, LHS); |
| break; |
| case CmpInst::ICMP_UGE: // LHS >= RHS -> !(LHS < RHS) |
| Instructions.emplace_back(Mips::SLTu, Temp, LHS, RHS); |
| Instructions.emplace_back(Mips::XORi, ICMPReg, Temp, 1); |
| break; |
| case CmpInst::ICMP_ULT: // LHS < RHS -> LHS < RHS |
| Instructions.emplace_back(Mips::SLTu, ICMPReg, LHS, RHS); |
| break; |
| case CmpInst::ICMP_ULE: // LHS <= RHS -> !(RHS < LHS) |
| Instructions.emplace_back(Mips::SLTu, Temp, RHS, LHS); |
| Instructions.emplace_back(Mips::XORi, ICMPReg, Temp, 1); |
| break; |
| case CmpInst::ICMP_SGT: // LHS > RHS -> RHS < LHS |
| Instructions.emplace_back(Mips::SLT, ICMPReg, RHS, LHS); |
| break; |
| case CmpInst::ICMP_SGE: // LHS >= RHS -> !(LHS < RHS) |
| Instructions.emplace_back(Mips::SLT, Temp, LHS, RHS); |
| Instructions.emplace_back(Mips::XORi, ICMPReg, Temp, 1); |
| break; |
| case CmpInst::ICMP_SLT: // LHS < RHS -> LHS < RHS |
| Instructions.emplace_back(Mips::SLT, ICMPReg, LHS, RHS); |
| break; |
| case CmpInst::ICMP_SLE: // LHS <= RHS -> !(RHS < LHS) |
| Instructions.emplace_back(Mips::SLT, Temp, RHS, LHS); |
| Instructions.emplace_back(Mips::XORi, ICMPReg, Temp, 1); |
| break; |
| default: |
| return false; |
| } |
| |
| MachineIRBuilder B(I); |
| for (const struct Instr &Instruction : Instructions) { |
| MachineInstrBuilder MIB = B.buildInstr( |
| Instruction.Opcode, {Instruction.Def}, {Instruction.LHS}); |
| |
| if (Instruction.hasImm()) |
| MIB.addImm(Instruction.RHS); |
| else |
| MIB.addUse(Instruction.RHS); |
| |
| if (!MIB.constrainAllUses(TII, TRI, RBI)) |
| return false; |
| } |
| |
| I.eraseFromParent(); |
| return true; |
| } |
| case G_FCMP: { |
| unsigned MipsFCMPCondCode; |
| bool isLogicallyNegated; |
| switch (CmpInst::Predicate Cond = static_cast<CmpInst::Predicate>( |
| I.getOperand(1).getPredicate())) { |
| case CmpInst::FCMP_UNO: // Unordered |
| case CmpInst::FCMP_ORD: // Ordered (OR) |
| MipsFCMPCondCode = Mips::FCOND_UN; |
| isLogicallyNegated = Cond != CmpInst::FCMP_UNO; |
| break; |
| case CmpInst::FCMP_OEQ: // Equal |
| case CmpInst::FCMP_UNE: // Not Equal (NEQ) |
| MipsFCMPCondCode = Mips::FCOND_OEQ; |
| isLogicallyNegated = Cond != CmpInst::FCMP_OEQ; |
| break; |
| case CmpInst::FCMP_UEQ: // Unordered or Equal |
| case CmpInst::FCMP_ONE: // Ordered or Greater Than or Less Than (OGL) |
| MipsFCMPCondCode = Mips::FCOND_UEQ; |
| isLogicallyNegated = Cond != CmpInst::FCMP_UEQ; |
| break; |
| case CmpInst::FCMP_OLT: // Ordered or Less Than |
| case CmpInst::FCMP_UGE: // Unordered or Greater Than or Equal (UGE) |
| MipsFCMPCondCode = Mips::FCOND_OLT; |
| isLogicallyNegated = Cond != CmpInst::FCMP_OLT; |
| break; |
| case CmpInst::FCMP_ULT: // Unordered or Less Than |
| case CmpInst::FCMP_OGE: // Ordered or Greater Than or Equal (OGE) |
| MipsFCMPCondCode = Mips::FCOND_ULT; |
| isLogicallyNegated = Cond != CmpInst::FCMP_ULT; |
| break; |
| case CmpInst::FCMP_OLE: // Ordered or Less Than or Equal |
| case CmpInst::FCMP_UGT: // Unordered or Greater Than (UGT) |
| MipsFCMPCondCode = Mips::FCOND_OLE; |
| isLogicallyNegated = Cond != CmpInst::FCMP_OLE; |
| break; |
| case CmpInst::FCMP_ULE: // Unordered or Less Than or Equal |
| case CmpInst::FCMP_OGT: // Ordered or Greater Than (OGT) |
| MipsFCMPCondCode = Mips::FCOND_ULE; |
| isLogicallyNegated = Cond != CmpInst::FCMP_ULE; |
| break; |
| default: |
| return false; |
| } |
| |
| // Default compare result in gpr register will be `true`. |
| // We will move `false` (MIPS::Zero) to gpr result when fcmp gives false |
| // using MOVF_I. When orignal predicate (Cond) is logically negated |
| // MipsFCMPCondCode, result is inverted i.e. MOVT_I is used. |
| unsigned MoveOpcode = isLogicallyNegated ? Mips::MOVT_I : Mips::MOVF_I; |
| |
| Register TrueInReg = MRI.createVirtualRegister(&Mips::GPR32RegClass); |
| BuildMI(MBB, I, I.getDebugLoc(), TII.get(Mips::ADDiu)) |
| .addDef(TrueInReg) |
| .addUse(Mips::ZERO) |
| .addImm(1); |
| |
| unsigned Size = MRI.getType(I.getOperand(2).getReg()).getSizeInBits(); |
| unsigned FCMPOpcode = |
| Size == 32 ? Mips::FCMP_S32 |
| : STI.isFP64bit() ? Mips::FCMP_D64 : Mips::FCMP_D32; |
| MachineInstr *FCMP = BuildMI(MBB, I, I.getDebugLoc(), TII.get(FCMPOpcode)) |
| .addUse(I.getOperand(2).getReg()) |
| .addUse(I.getOperand(3).getReg()) |
| .addImm(MipsFCMPCondCode); |
| if (!constrainSelectedInstRegOperands(*FCMP, TII, TRI, RBI)) |
| return false; |
| |
| MachineInstr *Move = BuildMI(MBB, I, I.getDebugLoc(), TII.get(MoveOpcode)) |
| .addDef(I.getOperand(0).getReg()) |
| .addUse(Mips::ZERO) |
| .addUse(Mips::FCC0) |
| .addUse(TrueInReg); |
| if (!constrainSelectedInstRegOperands(*Move, TII, TRI, RBI)) |
| return false; |
| |
| I.eraseFromParent(); |
| return true; |
| } |
| case G_FENCE: { |
| MI = BuildMI(MBB, I, I.getDebugLoc(), TII.get(Mips::SYNC)).addImm(0); |
| break; |
| } |
| case G_VASTART: { |
| MipsFunctionInfo *FuncInfo = MF.getInfo<MipsFunctionInfo>(); |
| int FI = FuncInfo->getVarArgsFrameIndex(); |
| |
| Register LeaReg = MRI.createVirtualRegister(&Mips::GPR32RegClass); |
| MachineInstr *LEA_ADDiu = |
| BuildMI(MBB, I, I.getDebugLoc(), TII.get(Mips::LEA_ADDiu)) |
| .addDef(LeaReg) |
| .addFrameIndex(FI) |
| .addImm(0); |
| if (!constrainSelectedInstRegOperands(*LEA_ADDiu, TII, TRI, RBI)) |
| return false; |
| |
| MachineInstr *Store = BuildMI(MBB, I, I.getDebugLoc(), TII.get(Mips::SW)) |
| .addUse(LeaReg) |
| .addUse(I.getOperand(0).getReg()) |
| .addImm(0); |
| if (!constrainSelectedInstRegOperands(*Store, TII, TRI, RBI)) |
| return false; |
| |
| I.eraseFromParent(); |
| return true; |
| } |
| default: |
| return false; |
| } |
| |
| I.eraseFromParent(); |
| return constrainSelectedInstRegOperands(*MI, TII, TRI, RBI); |
| } |
| |
| namespace llvm { |
| InstructionSelector *createMipsInstructionSelector(const MipsTargetMachine &TM, |
| MipsSubtarget &Subtarget, |
| MipsRegisterBankInfo &RBI) { |
| return new MipsInstructionSelector(TM, Subtarget, RBI); |
| } |
| } // end namespace llvm |