/// \todo This should be generated by TableGen.
//===----------------------------------------------------------------------===//
-#include "MipsInstrInfo.h"
#include "MipsRegisterBankInfo.h"
+#include "MipsInstrInfo.h"
+#include "llvm/CodeGen/GlobalISel/GISelChangeObserver.h"
+#include "llvm/CodeGen/GlobalISel/LegalizationArtifactCombiner.h"
+#include "llvm/CodeGen/GlobalISel/LegalizerHelper.h"
#include "llvm/CodeGen/MachineRegisterInfo.h"
#define GET_TARGET_REGBANK_IMPL
-#define DEBUG_TYPE "registerbankinfo"
-
#include "MipsGenRegisterBank.inc"
namespace llvm {
}
}
+// Instructions where all register operands are floating point.
+static bool isFloatingPointOpcode(unsigned Opc) {
+ switch (Opc) {
+ case TargetOpcode::G_FCONSTANT:
+ case TargetOpcode::G_FADD:
+ case TargetOpcode::G_FSUB:
+ case TargetOpcode::G_FMUL:
+ case TargetOpcode::G_FDIV:
+ case TargetOpcode::G_FABS:
+ case TargetOpcode::G_FSQRT:
+ case TargetOpcode::G_FCEIL:
+ case TargetOpcode::G_FFLOOR:
+ case TargetOpcode::G_FPEXT:
+ case TargetOpcode::G_FPTRUNC:
+ return true;
+ default:
+ return false;
+ }
+}
+
+// Instructions where def operands are floating point registers.
+// Use operands are general purpose.
+static bool isFloatingPointOpcodeDef(unsigned Opc) {
+ switch (Opc) {
+ case TargetOpcode::G_SITOFP:
+ case TargetOpcode::G_UITOFP:
+ case Mips::MTC1:
+ case Mips::BuildPairF64:
+ case Mips::BuildPairF64_64:
+ return true;
+ default:
+ return isFloatingPointOpcode(Opc);
+ }
+}
+
+static bool isAmbiguous(unsigned Opc) {
+ switch (Opc) {
+ case TargetOpcode::G_LOAD:
+ case TargetOpcode::G_STORE:
+ case TargetOpcode::G_PHI:
+ case TargetOpcode::G_SELECT:
+ return true;
+ default:
+ return false;
+ }
+}
+
+void MipsRegisterBankInfo::AmbiguousRegDefUseContainer::addUseDef(
+ Register Reg, const MachineRegisterInfo &MRI) {
+ assert(!MRI.getType(Reg).isPointer() &&
+ "Pointers are gprb, they should not be considered as ambiguous.\n");
+ MachineInstr *DefMI = MRI.getVRegDef(Reg);
+ if (DefMI->getOpcode() == TargetOpcode::COPY &&
+ !TargetRegisterInfo::isPhysicalRegister(DefMI->getOperand(1).getReg()))
+ // Copies from non-physical registers are not supported.
+ return;
+
+ UseDefs.push_back(DefMI);
+}
+
+MipsRegisterBankInfo::AmbiguousRegDefUseContainer::AmbiguousRegDefUseContainer(
+ const MachineInstr *MI) {
+ assert(isAmbiguous(MI->getOpcode()) &&
+ "Not implemented for non Ambiguous opcode.\n");
+
+ const MachineRegisterInfo &MRI = MI->getMF()->getRegInfo();
+
+ if (MI->getOpcode() == TargetOpcode::G_STORE)
+ addUseDef(MI->getOperand(0).getReg(), MRI);
+}
+
+bool MipsRegisterBankInfo::TypeInfoForMF::visit(const MachineInstr *MI) {
+ assert(isAmbiguous(MI->getOpcode()) && "Visiting non-Ambiguous opcode.\n");
+
+ startVisit(MI);
+ AmbiguousRegDefUseContainer DefUseContainer(MI);
+
+ // Visit instructions that DEFINE MI's USE operands.
+ if (visitAdjacentInstrs(MI, DefUseContainer.getUseDefs()))
+ return true;
+
+ return false;
+}
+
+bool MipsRegisterBankInfo::TypeInfoForMF::visitAdjacentInstrs(
+ const MachineInstr *MI, SmallVectorImpl<MachineInstr *> &AdjacentInstrs) {
+ while (!AdjacentInstrs.empty()) {
+ MachineInstr *AdjMI = AdjacentInstrs.pop_back_val();
+
+ if (isFloatingPointOpcodeDef(AdjMI->getOpcode())) {
+ setTypes(MI, InstType::FloatingPoint);
+ return true;
+ }
+
+ // Determine InstType from register bank of phys register that is
+ // use of this copy.
+ if (AdjMI->getOpcode() == TargetOpcode::COPY) {
+ setTypesAccordingToPhysicalRegister(MI, AdjMI, 1);
+ return true;
+ }
+
+ if (isAmbiguous(AdjMI->getOpcode())) {
+ // Chains of ambiguous instructions are not supported.
+ return false;
+ }
+
+ // Defaults to integer instruction. Includes G_MERGE_VALUES and
+ // G_UNMERGE_VALUES.
+ setTypes(MI, InstType::Integer);
+ return true;
+ }
+ return false;
+}
+
+void MipsRegisterBankInfo::TypeInfoForMF::setTypes(const MachineInstr *MI,
+ InstType InstTy) {
+ changeRecordedTypeForInstr(MI, InstTy);
+}
+
+void MipsRegisterBankInfo::TypeInfoForMF::setTypesAccordingToPhysicalRegister(
+ const MachineInstr *MI, const MachineInstr *CopyInst, unsigned Op) {
+ assert((TargetRegisterInfo::isPhysicalRegister(
+ CopyInst->getOperand(Op).getReg())) &&
+ "Copies of non physical registers should not be considered here.\n");
+
+ const MachineFunction &MF = *CopyInst->getMF();
+ const MachineRegisterInfo &MRI = MF.getRegInfo();
+ const TargetRegisterInfo &TRI = *MF.getSubtarget().getRegisterInfo();
+ const RegisterBankInfo &RBI =
+ *CopyInst->getMF()->getSubtarget().getRegBankInfo();
+ const RegisterBank *Bank =
+ RBI.getRegBank(CopyInst->getOperand(Op).getReg(), MRI, TRI);
+
+ if (Bank == &Mips::FPRBRegBank)
+ setTypes(MI, InstType::FloatingPoint);
+ else if (Bank == &Mips::GPRBRegBank)
+ setTypes(MI, InstType::Integer);
+ else
+ llvm_unreachable("Unsupported register bank.\n");
+}
+
+MipsRegisterBankInfo::InstType
+MipsRegisterBankInfo::TypeInfoForMF::determineInstType(const MachineInstr *MI) {
+ visit(MI);
+ return getRecordedTypeForInstr(MI);
+}
+
+void MipsRegisterBankInfo::TypeInfoForMF::cleanupIfNewFunction(
+ llvm::StringRef FunctionName) {
+ if (MFName != FunctionName) {
+ MFName = FunctionName;
+ Types.clear();
+ }
+}
+
const RegisterBankInfo::InstructionMapping &
MipsRegisterBankInfo::getInstrMapping(const MachineInstr &MI) const {
+ static TypeInfoForMF TI;
+
+ // Reset TI internal data when MF changes.
+ TI.cleanupIfNewFunction(MI.getMF()->getName());
+
unsigned Opc = MI.getOpcode();
const MachineFunction &MF = *MI.getParent()->getParent();
const MachineRegisterInfo &MRI = MF.getRegInfo();
unsigned NumOperands = MI.getNumOperands();
const ValueMapping *OperandsMapping = &Mips::ValueMappings[Mips::GPRIdx];
+ unsigned MappingID = DefaultMappingID;
+ const unsigned CustomMappingID = 1;
switch (Opc) {
case G_TRUNC:
case G_MUL:
case G_UMULH:
case G_LOAD:
- case G_STORE:
case G_ZEXTLOAD:
case G_SEXTLOAD:
case G_GEP:
case G_UREM:
OperandsMapping = &Mips::ValueMappings[Mips::GPRIdx];
break;
+ case G_STORE: {
+ unsigned Size = MRI.getType(MI.getOperand(0).getReg()).getSizeInBits();
+ InstType InstTy = InstType::Integer;
+ if (!MRI.getType(MI.getOperand(0).getReg()).isPointer()) {
+ InstTy = TI.determineInstType(&MI);
+ }
+
+ if (InstTy == InstType::FloatingPoint) { // fprb
+ OperandsMapping =
+ getOperandsMapping({Size == 32 ? &Mips::ValueMappings[Mips::SPRIdx]
+ : &Mips::ValueMappings[Mips::DPRIdx],
+ &Mips::ValueMappings[Mips::GPRIdx]});
+ break;
+ } else { // gprb
+ OperandsMapping =
+ getOperandsMapping({Size <= 32 ? &Mips::ValueMappings[Mips::GPRIdx]
+ : &Mips::ValueMappings[Mips::DPRIdx],
+ &Mips::ValueMappings[Mips::GPRIdx]});
+ if (Size == 64)
+ MappingID = CustomMappingID;
+ }
+ break;
+ }
+ case G_MERGE_VALUES: {
+ OperandsMapping = getOperandsMapping({&Mips::ValueMappings[Mips::DPRIdx],
+ &Mips::ValueMappings[Mips::GPRIdx],
+ &Mips::ValueMappings[Mips::GPRIdx]});
+ MappingID = CustomMappingID;
+ break;
+ }
case G_FADD:
case G_FSUB:
case G_FMUL:
return getInvalidInstructionMapping();
}
- return getInstructionMapping(DefaultMappingID, /*Cost=*/1, OperandsMapping,
+ return getInstructionMapping(MappingID, /*Cost=*/1, OperandsMapping,
NumOperands);
}
+
+using InstListTy = GISelWorkList<4>;
+namespace {
+class InstManager : public GISelChangeObserver {
+ InstListTy &InstList;
+
+public:
+ InstManager(InstListTy &Insts) : InstList(Insts) {}
+
+ void createdInstr(MachineInstr &MI) override { InstList.insert(&MI); }
+ void erasingInstr(MachineInstr &MI) override {}
+ void changingInstr(MachineInstr &MI) override {}
+ void changedInstr(MachineInstr &MI) override {}
+};
+} // end anonymous namespace
+
+/// Here we have to narrowScalar s64 operands to s32, combine away
+/// G_MERGE/G_UNMERGE and erase instructions that became dead in the process.
+/// We manually assign 32 bit gprb to register operands of all new instructions
+/// that got created in the process since they will not end up in RegBankSelect
+/// loop. Careful not to delete instruction after MI i.e. MI.getIterator()++.
+void MipsRegisterBankInfo::applyMappingImpl(
+ const OperandsMapper &OpdMapper) const {
+ MachineInstr &MI = OpdMapper.getMI();
+ InstListTy NewInstrs;
+ MachineIRBuilder B(MI);
+ MachineFunction *MF = MI.getMF();
+ MachineRegisterInfo &MRI = OpdMapper.getMRI();
+
+ InstManager NewInstrObserver(NewInstrs);
+ GISelObserverWrapper WrapperObserver(&NewInstrObserver);
+ LegalizerHelper Helper(*MF, WrapperObserver, B);
+ LegalizationArtifactCombiner ArtCombiner(
+ B, MF->getRegInfo(), *MF->getSubtarget().getLegalizerInfo());
+
+ switch (MI.getOpcode()) {
+ case TargetOpcode::G_STORE: {
+ Helper.narrowScalar(MI, 0, LLT::scalar(32));
+ // Handle new instructions.
+ while (!NewInstrs.empty()) {
+ MachineInstr *NewMI = NewInstrs.pop_back_val();
+ // This is new G_UNMERGE that was created during narrowScalar and will
+ // not be considered for regbank selection. RegBankSelect for mips
+ // visits/makes corresponding G_MERGE first. Combine them here.
+ if (NewMI->getOpcode() == TargetOpcode::G_UNMERGE_VALUES) {
+ SmallVector<MachineInstr *, 2> DeadInstrs;
+ ArtCombiner.tryCombineMerges(*NewMI, DeadInstrs);
+ for (MachineInstr *DeadMI : DeadInstrs)
+ DeadMI->eraseFromParent();
+ } else
+ // Manually set register banks for all register operands to 32 bit gprb.
+ for (auto Op : NewMI->operands()) {
+ if (Op.isReg()) {
+ assert(MRI.getType(Op.getReg()).getSizeInBits() == 32 &&
+ "Only 32 bit gprb is handled here.\n");
+ MRI.setRegBank(Op.getReg(), getRegBank(Mips::GPRBRegBankID));
+ }
+ }
+ }
+ return;
+ }
+ default:
+ break;
+ }
+
+ return applyDefaultMapping(OpdMapper);
+}
const InstructionMapping &
getInstrMapping(const MachineInstr &MI) const override;
+
+ void applyMappingImpl(const OperandsMapper &OpdMapper) const override;
+
+private:
+ /// Some instructions are used with both floating point and integer operands.
+ /// We assign InstType to such instructions as it helps us to avoid cross bank
+ /// copies. InstType deppends on context.
+ enum InstType {
+ NotDetermined,
+ /// Connected with instruction that interprets 'bags of bits' as integers.
+ /// Select gprb to avoid cross bank copies.
+ Integer,
+ /// Connected with instruction that interprets 'bags of bits' as floating
+ /// point numbers. Select fprb to avoid cross bank copies.
+ FloatingPoint
+ };
+
+ /// Some generic instructions have operands that can be mapped to either fprb
+ /// or gprb e.g. for G_LOAD we consider only operand 0 as ambiguous, operand 1
+ /// is always gprb since it is a pointer.
+ /// This class provides container for MI's ambiguous:
+ /// UseDefs : MachineInstrs that define MI's ambiguous use operands.
+ class AmbiguousRegDefUseContainer {
+ SmallVector<MachineInstr *, 2> UseDefs;
+
+ void addUseDef(Register Reg, const MachineRegisterInfo &MRI);
+
+ public:
+ AmbiguousRegDefUseContainer(const MachineInstr *MI);
+ SmallVectorImpl<MachineInstr *> &getUseDefs() { return UseDefs; }
+ };
+
+ class TypeInfoForMF {
+ /// MachineFunction name is used to recognise when MF changes.
+ std::string MFName = "";
+ /// Recorded InstTypes for visited instructions.
+ DenseMap<const MachineInstr *, InstType> Types;
+
+ bool visit(const MachineInstr *MI);
+
+ /// Visit MI's adjacent UseDefs.
+ bool visitAdjacentInstrs(const MachineInstr *MI,
+ SmallVectorImpl<MachineInstr *> &AdjacentInstrs);
+
+ void setTypes(const MachineInstr *MI, InstType ITy);
+
+ /// InstType for MI is determined, set it to InstType that corresponds to
+ /// physical regisiter that is operand number Op in CopyInst.
+ void setTypesAccordingToPhysicalRegister(const MachineInstr *MI,
+ const MachineInstr *CopyInst,
+ unsigned Op);
+
+ /// Set default values for MI in order to start visit.
+ void startVisit(const MachineInstr *MI) {
+ Types.try_emplace(MI, InstType::NotDetermined);
+ }
+
+ bool wasVisited(const MachineInstr *MI) const { return Types.count(MI); };
+
+ /// Returns recorded type for instruction.
+ const InstType &getRecordedTypeForInstr(const MachineInstr *MI) const {
+ assert(wasVisited(MI) && "Instruction was not visited!");
+ return Types.find(MI)->getSecond();
+ };
+
+ /// Change recorded type for instruction.
+ void changeRecordedTypeForInstr(const MachineInstr *MI, InstType InstTy) {
+ assert(wasVisited(MI) && "Instruction was not visited!");
+ Types.find(MI)->getSecond() = InstTy;
+ };
+
+ public:
+ InstType determineInstType(const MachineInstr *MI);
+
+ void cleanupIfNewFunction(llvm::StringRef FunctionName);
+ };
};
} // end namespace llvm
#endif
--- /dev/null
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py
+# RUN: llc -O0 -mtriple=mipsel-linux-gnu -run-pass=legalizer -verify-machineinstrs %s -o - | FileCheck %s -check-prefixes=MIPS32
+--- |
+
+ define void @store_i32(i32* %ptr) { entry: ret void }
+ define void @store_i64(i64* %ptr) { entry: ret void }
+ define void @store_float(float* %ptr) { entry: ret void }
+ define void @store_double(double* %ptr) { entry: ret void }
+
+...
+---
+name: store_i32
+alignment: 2
+legalized: true
+tracksRegLiveness: true
+body: |
+ bb.1.entry:
+ liveins: $a0, $a1
+
+ ; MIPS32-LABEL: name: store_i32
+ ; MIPS32: liveins: $a0, $a1
+ ; MIPS32: [[COPY:%[0-9]+]]:_(s32) = COPY $a0
+ ; MIPS32: [[COPY1:%[0-9]+]]:_(p0) = COPY $a1
+ ; MIPS32: G_STORE [[COPY]](s32), [[COPY1]](p0) :: (store 4 into %ir.ptr)
+ ; MIPS32: RetRA
+ %0:_(s32) = COPY $a0
+ %1:_(p0) = COPY $a1
+ G_STORE %0(s32), %1(p0) :: (store 4 into %ir.ptr)
+ RetRA
+
+...
+---
+name: store_i64
+alignment: 2
+legalized: true
+tracksRegLiveness: true
+body: |
+ bb.1.entry:
+ liveins: $a0, $a1, $a2
+
+ ; MIPS32-LABEL: name: store_i64
+ ; MIPS32: liveins: $a0, $a1, $a2
+ ; MIPS32: [[COPY:%[0-9]+]]:_(s32) = COPY $a0
+ ; MIPS32: [[COPY1:%[0-9]+]]:_(s32) = COPY $a1
+ ; MIPS32: [[MV:%[0-9]+]]:_(s64) = G_MERGE_VALUES [[COPY]](s32), [[COPY1]](s32)
+ ; MIPS32: [[COPY2:%[0-9]+]]:_(p0) = COPY $a2
+ ; MIPS32: G_STORE [[MV]](s64), [[COPY2]](p0) :: (store 8 into %ir.ptr)
+ ; MIPS32: RetRA
+ %2:_(s32) = COPY $a0
+ %3:_(s32) = COPY $a1
+ %0:_(s64) = G_MERGE_VALUES %2(s32), %3(s32)
+ %1:_(p0) = COPY $a2
+ G_STORE %0(s64), %1(p0) :: (store 8 into %ir.ptr)
+ RetRA
+
+...
+---
+name: store_float
+alignment: 2
+legalized: true
+tracksRegLiveness: true
+body: |
+ bb.1.entry:
+ liveins: $a1, $f12
+
+ ; MIPS32-LABEL: name: store_float
+ ; MIPS32: liveins: $a1, $f12
+ ; MIPS32: [[COPY:%[0-9]+]]:_(s32) = COPY $f12
+ ; MIPS32: [[COPY1:%[0-9]+]]:_(p0) = COPY $a1
+ ; MIPS32: G_STORE [[COPY]](s32), [[COPY1]](p0) :: (store 4 into %ir.ptr)
+ ; MIPS32: RetRA
+ %0:_(s32) = COPY $f12
+ %1:_(p0) = COPY $a1
+ G_STORE %0(s32), %1(p0) :: (store 4 into %ir.ptr)
+ RetRA
+
+...
+---
+name: store_double
+alignment: 2
+legalized: true
+tracksRegLiveness: true
+body: |
+ bb.1.entry:
+ liveins: $a2, $d6
+
+ ; MIPS32-LABEL: name: store_double
+ ; MIPS32: liveins: $a2, $d6
+ ; MIPS32: [[COPY:%[0-9]+]]:_(s64) = COPY $d6
+ ; MIPS32: [[COPY1:%[0-9]+]]:_(p0) = COPY $a2
+ ; MIPS32: G_STORE [[COPY]](s64), [[COPY1]](p0) :: (store 8 into %ir.ptr)
+ ; MIPS32: RetRA
+ %0:_(s64) = COPY $d6
+ %1:_(p0) = COPY $a2
+ G_STORE %0(s64), %1(p0) :: (store 8 into %ir.ptr)
+ RetRA
+
+...
--- /dev/null
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py
+# RUN: llc -O0 -mtriple=mipsel-linux-gnu -run-pass=regbankselect -verify-machineinstrs %s -o - | FileCheck %s -check-prefixes=MIPS32
+--- |
+
+ define void @store_i32(i32* %ptr) { entry: ret void }
+ define void @store_i64(i64* %ptr) { entry: ret void }
+ define void @store_float(float* %ptr) { entry: ret void }
+ define void @store_double(double* %ptr) { entry: ret void }
+
+...
+---
+name: store_i32
+alignment: 2
+legalized: true
+tracksRegLiveness: true
+body: |
+ bb.1.entry:
+ liveins: $a0, $a1
+
+ ; MIPS32-LABEL: name: store_i32
+ ; MIPS32: liveins: $a0, $a1
+ ; MIPS32: [[COPY:%[0-9]+]]:gprb(s32) = COPY $a0
+ ; MIPS32: [[COPY1:%[0-9]+]]:gprb(p0) = COPY $a1
+ ; MIPS32: G_STORE [[COPY]](s32), [[COPY1]](p0) :: (store 4 into %ir.ptr)
+ ; MIPS32: RetRA
+ %0:_(s32) = COPY $a0
+ %1:_(p0) = COPY $a1
+ G_STORE %0(s32), %1(p0) :: (store 4 into %ir.ptr)
+ RetRA
+
+...
+---
+name: store_i64
+alignment: 2
+legalized: true
+tracksRegLiveness: true
+body: |
+ bb.1.entry:
+ liveins: $a0, $a1, $a2
+
+ ; MIPS32-LABEL: name: store_i64
+ ; MIPS32: liveins: $a0, $a1, $a2
+ ; MIPS32: [[COPY:%[0-9]+]]:gprb(s32) = COPY $a0
+ ; MIPS32: [[COPY1:%[0-9]+]]:gprb(s32) = COPY $a1
+ ; MIPS32: [[COPY2:%[0-9]+]]:gprb(p0) = COPY $a2
+ ; MIPS32: G_STORE [[COPY]](s32), [[COPY2]](p0) :: (store 4 into %ir.ptr, align 8)
+ ; MIPS32: [[C:%[0-9]+]]:gprb(s32) = G_CONSTANT i32 4
+ ; MIPS32: [[GEP:%[0-9]+]]:gprb(p0) = G_GEP [[COPY2]], [[C]](s32)
+ ; MIPS32: G_STORE [[COPY1]](s32), [[GEP]](p0) :: (store 4 into %ir.ptr + 4, align 8)
+ ; MIPS32: RetRA
+ %2:_(s32) = COPY $a0
+ %3:_(s32) = COPY $a1
+ %0:_(s64) = G_MERGE_VALUES %2(s32), %3(s32)
+ %1:_(p0) = COPY $a2
+ G_STORE %0(s64), %1(p0) :: (store 8 into %ir.ptr)
+ RetRA
+
+...
+---
+name: store_float
+alignment: 2
+legalized: true
+tracksRegLiveness: true
+body: |
+ bb.1.entry:
+ liveins: $a1, $f12
+
+ ; MIPS32-LABEL: name: store_float
+ ; MIPS32: liveins: $a1, $f12
+ ; MIPS32: [[COPY:%[0-9]+]]:fprb(s32) = COPY $f12
+ ; MIPS32: [[COPY1:%[0-9]+]]:gprb(p0) = COPY $a1
+ ; MIPS32: G_STORE [[COPY]](s32), [[COPY1]](p0) :: (store 4 into %ir.ptr)
+ ; MIPS32: RetRA
+ %0:_(s32) = COPY $f12
+ %1:_(p0) = COPY $a1
+ G_STORE %0(s32), %1(p0) :: (store 4 into %ir.ptr)
+ RetRA
+
+...
+---
+name: store_double
+alignment: 2
+legalized: true
+tracksRegLiveness: true
+body: |
+ bb.1.entry:
+ liveins: $a2, $d6
+
+ ; MIPS32-LABEL: name: store_double
+ ; MIPS32: liveins: $a2, $d6
+ ; MIPS32: [[COPY:%[0-9]+]]:fprb(s64) = COPY $d6
+ ; MIPS32: [[COPY1:%[0-9]+]]:gprb(p0) = COPY $a2
+ ; MIPS32: G_STORE [[COPY]](s64), [[COPY1]](p0) :: (store 8 into %ir.ptr)
+ ; MIPS32: RetRA
+ %0:_(s64) = COPY $d6
+ %1:_(p0) = COPY $a2
+ G_STORE %0(s64), %1(p0) :: (store 8 into %ir.ptr)
+ RetRA
+
+...