blob: 256fb74c1d6c970aa0565d039f572116e692f278 [file] [log] [blame]
//===- 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).isNullValue()) {
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).isNullValue()) {
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