Index: llvm/trunk/lib/Target/Mips/MipsLegalizerInfo.cpp =================================================================== --- llvm/trunk/lib/Target/Mips/MipsLegalizerInfo.cpp +++ llvm/trunk/lib/Target/Mips/MipsLegalizerInfo.cpp @@ -39,9 +39,13 @@ .legalForTypesWithMemDesc({{s32, p0, 8, 8}, {s32, p0, 16, 8}, {s32, p0, 32, 8}, + {s64, p0, 64, 8}, {p0, p0, 32, 8}}) .minScalar(0, s32); + getActionDefinitionsBuilder(G_MERGE_VALUES) + .legalFor({{s64, s32}}); + getActionDefinitionsBuilder({G_ZEXTLOAD, G_SEXTLOAD}) .legalForTypesWithMemDesc({{s32, p0, 8, 8}, {s32, p0, 16, 8}}) Index: llvm/trunk/lib/Target/Mips/MipsRegisterBankInfo.h =================================================================== --- llvm/trunk/lib/Target/Mips/MipsRegisterBankInfo.h +++ llvm/trunk/lib/Target/Mips/MipsRegisterBankInfo.h @@ -37,6 +37,82 @@ 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 UseDefs; + + void addUseDef(Register Reg, const MachineRegisterInfo &MRI); + + public: + AmbiguousRegDefUseContainer(const MachineInstr *MI); + SmallVectorImpl &getUseDefs() { return UseDefs; } + }; + + class TypeInfoForMF { + /// MachineFunction name is used to recognise when MF changes. + std::string MFName = ""; + /// Recorded InstTypes for visited instructions. + DenseMap Types; + + bool visit(const MachineInstr *MI); + + /// Visit MI's adjacent UseDefs. + bool visitAdjacentInstrs(const MachineInstr *MI, + SmallVectorImpl &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 Index: llvm/trunk/lib/Target/Mips/MipsRegisterBankInfo.cpp =================================================================== --- llvm/trunk/lib/Target/Mips/MipsRegisterBankInfo.cpp +++ llvm/trunk/lib/Target/Mips/MipsRegisterBankInfo.cpp @@ -10,14 +10,15 @@ /// \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 { @@ -91,9 +92,169 @@ } } +// 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 &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(); @@ -106,6 +267,8 @@ unsigned NumOperands = MI.getNumOperands(); const ValueMapping *OperandsMapping = &Mips::ValueMappings[Mips::GPRIdx]; + unsigned MappingID = DefaultMappingID; + const unsigned CustomMappingID = 1; switch (Opc) { case G_TRUNC: @@ -114,7 +277,6 @@ case G_MUL: case G_UMULH: case G_LOAD: - case G_STORE: case G_ZEXTLOAD: case G_SEXTLOAD: case G_GEP: @@ -130,6 +292,36 @@ 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: @@ -218,6 +410,73 @@ 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 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); +} Index: llvm/trunk/test/CodeGen/Mips/GlobalISel/legalizer/store.mir =================================================================== --- llvm/trunk/test/CodeGen/Mips/GlobalISel/legalizer/store.mir +++ llvm/trunk/test/CodeGen/Mips/GlobalISel/legalizer/store.mir @@ -0,0 +1,98 @@ +# 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 + +... Index: llvm/trunk/test/CodeGen/Mips/GlobalISel/llvm-ir/store.ll =================================================================== --- llvm/trunk/test/CodeGen/Mips/GlobalISel/llvm-ir/store.ll +++ llvm/trunk/test/CodeGen/Mips/GlobalISel/llvm-ir/store.ll @@ -0,0 +1,27 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py +; RUN: llc -O0 -mtriple=mipsel-linux-gnu -global-isel -verify-machineinstrs %s -o -| FileCheck %s -check-prefixes=MIPS32 + +define void @store_i32(i32 %val, i32* %ptr) { +; MIPS32-LABEL: store_i32: +; MIPS32: # %bb.0: # %entry +; MIPS32-NEXT: sw $4, 0($5) +; MIPS32-NEXT: jr $ra +; MIPS32-NEXT: nop +entry: + store i32 %val, i32* %ptr + ret void +} + +define void @store_i64(i64 %val, i64* %ptr) { +; MIPS32-LABEL: store_i64: +; MIPS32: # %bb.0: # %entry +; MIPS32-NEXT: sw $4, 0($6) +; MIPS32-NEXT: ori $1, $zero, 4 +; MIPS32-NEXT: addu $1, $6, $1 +; MIPS32-NEXT: sw $5, 0($1) +; MIPS32-NEXT: jr $ra +; MIPS32-NEXT: nop +entry: + store i64 %val, i64* %ptr + ret void +} Index: llvm/trunk/test/CodeGen/Mips/GlobalISel/regbankselect/store.mir =================================================================== --- llvm/trunk/test/CodeGen/Mips/GlobalISel/regbankselect/store.mir +++ llvm/trunk/test/CodeGen/Mips/GlobalISel/regbankselect/store.mir @@ -0,0 +1,100 @@ +# 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 + +...