Index: include/llvm/Target/TargetRegisterInfo.h =================================================================== --- include/llvm/Target/TargetRegisterInfo.h +++ include/llvm/Target/TargetRegisterInfo.h @@ -777,7 +777,8 @@ /// Get a list of 'hint' registers that the register allocator should try /// first when allocating a physical register for the virtual register /// VirtReg. These registers are effectively moved to the front of the - /// allocation order. + /// allocation order. If true is returned, regalloc will try to only use + /// hints to the greatest extent possible even if it means spilling. /// /// The Order argument is the allocation order for VirtReg's register class /// as returned from RegisterClassInfo::getOrder(). The hint registers must @@ -788,7 +789,7 @@ /// HintType == 0. Targets that override this function should defer to the /// default implementation if they have no reason to change the allocation /// order for VirtReg. There may be target-independent hints. - virtual void getRegAllocationHints(unsigned VirtReg, + virtual bool getRegAllocationHints(unsigned VirtReg, ArrayRef Order, SmallVectorImpl &Hints, const MachineFunction &MF, Index: lib/CodeGen/AllocationOrder.h =================================================================== --- lib/CodeGen/AllocationOrder.h +++ lib/CodeGen/AllocationOrder.h @@ -32,7 +32,11 @@ ArrayRef Order; int Pos; + // If HardHints is true, *only* Hints will be returned. + bool HardHints; + public: + /// Create a new AllocationOrder for VirtReg. /// @param VirtReg Virtual register to allocate for. /// @param VRM Virtual register map for function. @@ -51,6 +55,8 @@ unsigned next(unsigned Limit = 0) { if (Pos < 0) return Hints.end()[Pos++]; + if (HardHints) + return 0; if (!Limit) Limit = Order.size(); while (Pos < int(Limit)) { @@ -68,6 +74,8 @@ unsigned nextWithDups(unsigned Limit) { if (Pos < 0) return Hints.end()[Pos++]; + if (HardHints) + return 0; if (Pos < int(Limit)) return Order[Pos++]; return 0; Index: lib/CodeGen/AllocationOrder.cpp =================================================================== --- lib/CodeGen/AllocationOrder.cpp +++ lib/CodeGen/AllocationOrder.cpp @@ -31,11 +31,12 @@ const VirtRegMap &VRM, const RegisterClassInfo &RegClassInfo, const LiveRegMatrix *Matrix) - : Pos(0) { + : Pos(0), HardHints(false) { const MachineFunction &MF = VRM.getMachineFunction(); const TargetRegisterInfo *TRI = &VRM.getTargetRegInfo(); Order = RegClassInfo.getOrder(MF.getRegInfo().getRegClass(VirtReg)); - TRI->getRegAllocationHints(VirtReg, Order, Hints, MF, &VRM, Matrix); + if (TRI->getRegAllocationHints(VirtReg, Order, Hints, MF, &VRM, Matrix)) + HardHints = true; rewind(); DEBUG({ Index: lib/CodeGen/TargetRegisterInfo.cpp =================================================================== --- lib/CodeGen/TargetRegisterInfo.cpp +++ lib/CodeGen/TargetRegisterInfo.cpp @@ -360,7 +360,7 @@ } // Compute target-independent register allocator hints to help eliminate copies. -void +bool TargetRegisterInfo::getRegAllocationHints(unsigned VirtReg, ArrayRef Order, SmallVectorImpl &Hints, @@ -382,17 +382,18 @@ // Check that Phys is a valid hint in VirtReg's register class. if (!isPhysicalRegister(Phys)) - return; + return false; if (MRI.isReserved(Phys)) - return; + return false; // Check that Phys is in the allocation order. We shouldn't heed hints // from VirtReg's register class if they aren't in the allocation order. The // target probably has a reason for removing the register. if (!is_contained(Order, Phys)) - return; + return false; // All clear, tell the register allocator to prefer this register. Hints.push_back(Phys); + return false; } bool TargetRegisterInfo::canRealignStack(const MachineFunction &MF) const { Index: lib/Target/ARM/ARMBaseRegisterInfo.h =================================================================== --- lib/Target/ARM/ARMBaseRegisterInfo.h +++ lib/Target/ARM/ARMBaseRegisterInfo.h @@ -143,7 +143,7 @@ unsigned getRegPressureLimit(const TargetRegisterClass *RC, MachineFunction &MF) const override; - void getRegAllocationHints(unsigned VirtReg, + bool getRegAllocationHints(unsigned VirtReg, ArrayRef Order, SmallVectorImpl &Hints, const MachineFunction &MF, Index: lib/Target/ARM/ARMBaseRegisterInfo.cpp =================================================================== --- lib/Target/ARM/ARMBaseRegisterInfo.cpp +++ lib/Target/ARM/ARMBaseRegisterInfo.cpp @@ -280,7 +280,7 @@ } // Resolve the RegPairEven / RegPairOdd register allocator hints. -void +bool ARMBaseRegisterInfo::getRegAllocationHints(unsigned VirtReg, ArrayRef Order, SmallVectorImpl &Hints, @@ -300,7 +300,7 @@ break; default: TargetRegisterInfo::getRegAllocationHints(VirtReg, Order, Hints, MF, VRM); - return; + return false; } // This register should preferably be even (Odd == 0) or odd (Odd == 1). @@ -308,7 +308,7 @@ // the paired register as the first hint. unsigned Paired = Hint.second; if (Paired == 0) - return; + return false; unsigned PairedPhys = 0; if (TargetRegisterInfo::isPhysicalRegister(Paired)) { @@ -331,6 +331,7 @@ continue; Hints.push_back(Reg); } + return false; } void Index: lib/Target/SystemZ/SystemZInstrInfo.cpp =================================================================== --- lib/Target/SystemZ/SystemZInstrInfo.cpp +++ lib/Target/SystemZ/SystemZInstrInfo.cpp @@ -16,6 +16,7 @@ #include "SystemZ.h" #include "SystemZInstrBuilder.h" #include "SystemZSubtarget.h" +#include "llvm/ADT/Statistic.h" #include "llvm/CodeGen/LiveInterval.h" #include "llvm/CodeGen/LiveIntervalAnalysis.h" #include "llvm/CodeGen/LiveVariables.h" @@ -45,6 +46,9 @@ #define GET_INSTRMAP_INFO #include "SystemZGenInstrInfo.inc" +#define DEBUG_TYPE "systemz-II" +STATISTIC(LOCRMuxJumps, "Number of LOCRMux jump-sequences (lower is better)"); + // Return a mask with Count low bits set. static uint64_t allOnes(unsigned int Count) { return Count == 0 ? 0 : (uint64_t(1) << (Count - 1) << 1) - 1; @@ -209,6 +213,8 @@ MI.setDesc(get(LowOpcode)); else if (DestIsHigh && SrcIsHigh) MI.setDesc(get(HighOpcode)); + else + LOCRMuxJumps++; // If we were unable to implement the pseudo with a single instruction, we // need to convert it back into a branch sequence. This cannot be done here Index: lib/Target/SystemZ/SystemZRegisterInfo.h =================================================================== --- lib/Target/SystemZ/SystemZRegisterInfo.h +++ lib/Target/SystemZ/SystemZRegisterInfo.h @@ -42,6 +42,13 @@ return &SystemZ::ADDR64BitRegClass; } + bool getRegAllocationHints(unsigned VirtReg, + ArrayRef Order, + SmallVectorImpl &Hints, + const MachineFunction &MF, + const VirtRegMap *VRM, + const LiveRegMatrix *Matrix) const override; + // Override TargetRegisterInfo.h. bool requiresRegisterScavenging(const MachineFunction &MF) const override { return true; Index: lib/Target/SystemZ/SystemZRegisterInfo.cpp =================================================================== --- lib/Target/SystemZ/SystemZRegisterInfo.cpp +++ lib/Target/SystemZ/SystemZRegisterInfo.cpp @@ -10,8 +10,10 @@ #include "SystemZRegisterInfo.h" #include "SystemZInstrInfo.h" #include "SystemZSubtarget.h" +#include "llvm/ADT/SmallSet.h" #include "llvm/CodeGen/MachineInstrBuilder.h" #include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/CodeGen/VirtRegMap.h" #include "llvm/Target/TargetFrameLowering.h" using namespace llvm; @@ -22,6 +24,113 @@ SystemZRegisterInfo::SystemZRegisterInfo() : SystemZGenRegisterInfo(SystemZ::R14D) {} +// Given that MO is a GRX32 operand, return either GR32 or GRH32 if MO +// somehow belongs in it. Otherwise, return GRX32. +static const TargetRegisterClass *getRC32(MachineOperand &MO, + const VirtRegMap *VRM, + const MachineRegisterInfo *MRI) { + const TargetRegisterClass *RC = MRI->getRegClass(MO.getReg()); + + if (SystemZ::GR32BitRegClass.hasSubClassEq(RC) || + MO.getSubReg() == SystemZ::subreg_l32) + return &SystemZ::GR32BitRegClass; + if (RC == &SystemZ::GRH32BitRegClass || + MO.getSubReg() == SystemZ::subreg_h32) + return &SystemZ::GRH32BitRegClass; + + if (VRM && VRM->hasPhys(MO.getReg())) { + unsigned PhysReg = VRM->getPhys(MO.getReg()); + if (SystemZ::GR32BitRegClass.contains(PhysReg)) + return &SystemZ::GR32BitRegClass; + assert (SystemZ::GRH32BitRegClass.contains(PhysReg) && + "Phys reg not in GR32 or GRH32?"); + return &SystemZ::GRH32BitRegClass; + } + + assert (RC == &SystemZ::GRX32BitRegClass); + return RC; +} + +static const TargetRegisterClass* +getConstrainedRC32_LOCRMux(MachineInstr *LOCRMuxMI, + const VirtRegMap *VRM, + const MachineRegisterInfo *MRI, + const TargetRegisterInfo *TRI) { + assert (LOCRMuxMI->getOpcode() == SystemZ::LOCRMux && + "Should only be called with LOCRMux"); + + MachineOperand &TrueMO = LOCRMuxMI->getOperand(1); + MachineOperand &FalseMO = LOCRMuxMI->getOperand(2); + const TargetRegisterClass *RC = + TRI->getCommonSubClass(getRC32(FalseMO, VRM, MRI), + getRC32(TrueMO, VRM, MRI)); + if (RC && RC != &SystemZ::GRX32BitRegClass) + return RC; + + return nullptr; +} + +static const TargetRegisterClass* +getHintRC_LOCRMux(unsigned Reg, + const VirtRegMap *VRM, + const MachineRegisterInfo *MRI, + const TargetRegisterInfo *TRI, + bool &UsedByLOCRMux, + SmallVector &Worklist) { + assert (MRI->getRegClass(Reg) == &SystemZ::GRX32BitRegClass); + + for (auto &Use : MRI->use_instructions(Reg)) + if (Use.getOpcode() == SystemZ::LOCRMux) { + UsedByLOCRMux = true; + if (const TargetRegisterClass *ConstrainedRC32 = + getConstrainedRC32_LOCRMux(&Use, VRM, MRI, TRI)) + return ConstrainedRC32; + unsigned TrueReg = Use.getOperand(1).getReg(); + unsigned FalseReg = Use.getOperand(2).getReg(); + unsigned OtherReg = (TrueReg == Reg ? FalseReg : TrueReg); + if (MRI->getRegClass(OtherReg) == &SystemZ::GRX32BitRegClass) + Worklist.push_back(OtherReg); + } + + return nullptr; +} + +bool +SystemZRegisterInfo::getRegAllocationHints(unsigned VirtReg, + ArrayRef Order, + SmallVectorImpl &Hints, + const MachineFunction &MF, + const VirtRegMap *VRM, + const LiveRegMatrix *Matrix) const { + const MachineRegisterInfo *MRI = &MF.getRegInfo(); + const TargetRegisterInfo *TRI = MF.getSubtarget().getRegisterInfo(); + if (MRI->getRegClass(VirtReg) == &SystemZ::GRX32BitRegClass) { + const TargetRegisterClass *ConstrainedRC32 = nullptr; + bool UsedByLOCRMux = false; + SmallVector Worklist; + SmallSet DoneRegs; + Worklist.push_back(VirtReg); + while (Worklist.size()) { + unsigned Reg = Worklist.pop_back_val(); + if (!DoneRegs.insert(Reg).second) + continue; + if ((ConstrainedRC32 = + getHintRC_LOCRMux(Reg, VRM, MRI, TRI, UsedByLOCRMux, Worklist))) + break; + } + + if (ConstrainedRC32 != nullptr) { + for (MCPhysReg Reg : Order) + if (ConstrainedRC32->contains(Reg) && !MRI->isReserved(Reg)) + Hints.push_back(Reg); + // Return true to make these hints the only regs available to RA. + return true; + } + } // GRX32 + + return TargetRegisterInfo::getRegAllocationHints(VirtReg, Order, Hints, MF, VRM); +} + const MCPhysReg * SystemZRegisterInfo::getCalleeSavedRegs(const MachineFunction *MF) const { if (MF->getSubtarget().getTargetLowering()->supportSwiftError() && Index: test/CodeGen/SystemZ/cond-move-04.mir =================================================================== --- /dev/null +++ test/CodeGen/SystemZ/cond-move-04.mir @@ -0,0 +1,74 @@ +# RUN: llc -mtriple=s390x-linux-gnu -mcpu=z13 -start-before=greedy %s -o - \ +# RUN: | FileCheck %s +# +# Test that regalloc manages (via regalloc hints) to avoid a LOCRMux jump +# sequence expansion. + +--- | + + declare i8* @foo(i8*, i32 signext, i32 signext) local_unnamed_addr + + define i8* @fun(i8* returned) { + br label %2 + + ;