[Regalloc] Generate and store multiple regalloc hints.

MachineRegisterInfo used to allow just one regalloc hint per virtual
register. This patch extends this to a vector of regalloc hints, which is
filled in by common code with sorted copy hints. Such hints will make for
more ID copies that can be removed.

NB! This improvement is currently (and hopefully temporarily) *disabled* by
default, except for SystemZ. The only reason for this is the big impact this
has on tests, which has unfortunately proven unmanageable. It was a long
while since all the tests were updated and just waiting for review (which
didn't happen), but now targets have to enable this themselves
instead. Several targets could get a head-start by downloading the tests
updates from the Phabricator review. Thanks to those who helped, and sorry
you now have to do this step yourselves.

This should be an improvement generally for any target!

The target may still create its own hint, in which case this has highest
priority and is stored first in the vector. If it has target-type, it will
not be recomputed, as per the previous behaviour.

The temporary hook enableMultipleCopyHints() will be removed as soon as all
targets return true.

Review: Quentin Colombet, Ulrich Weigand.
https://reviews.llvm.org/D38128

llvm-svn: 319754
This commit is contained in:
Jonas Paulsson 2017-12-05 10:52:24 +00:00
parent d3b04e3517
commit 86c40db49d
8 changed files with 163 additions and 89 deletions

View File

@ -84,14 +84,15 @@ private:
/// all registers that were disabled are removed from the list. /// all registers that were disabled are removed from the list.
SmallVector<MCPhysReg, 16> UpdatedCSRs; SmallVector<MCPhysReg, 16> UpdatedCSRs;
/// RegAllocHints - This vector records register allocation hints for virtual /// RegAllocHints - This vector records register allocation hints for
/// registers. For each virtual register, it keeps a register and hint type /// virtual registers. For each virtual register, it keeps a pair of hint
/// pair making up the allocation hint. Hint type is target specific except /// type and hints vector making up the allocation hints. Only the first
/// for the value 0 which means the second value of the pair is the preferred /// hint may be target specific, and in that case this is reflected by the
/// register for allocation. For example, if the hint is <0, 1024>, it means /// first member of the pair being non-zero. If the hinted register is
/// the allocator should prefer the physical register allocated to the virtual /// virtual, it means the allocator should prefer the physical register
/// register of the hint. /// allocated to it if any.
IndexedMap<std::pair<unsigned, unsigned>, VirtReg2IndexFunctor> RegAllocHints; IndexedMap<std::pair<unsigned, SmallVector<unsigned, 4>>,
VirtReg2IndexFunctor> RegAllocHints;
/// PhysRegUseDefLists - This is an array of the head of the use/def list for /// PhysRegUseDefLists - This is an array of the head of the use/def list for
/// physical registers. /// physical registers.
@ -706,35 +707,61 @@ public:
void clearVirtRegs(); void clearVirtRegs();
/// setRegAllocationHint - Specify a register allocation hint for the /// setRegAllocationHint - Specify a register allocation hint for the
/// specified virtual register. /// specified virtual register. This is typically used by target, and in case
/// of an earlier hint it will be overwritten.
void setRegAllocationHint(unsigned VReg, unsigned Type, unsigned PrefReg) { void setRegAllocationHint(unsigned VReg, unsigned Type, unsigned PrefReg) {
assert(TargetRegisterInfo::isVirtualRegister(VReg)); assert(TargetRegisterInfo::isVirtualRegister(VReg));
RegAllocHints[VReg].first = Type; RegAllocHints[VReg].first = Type;
RegAllocHints[VReg].second = PrefReg; RegAllocHints[VReg].second.clear();
RegAllocHints[VReg].second.push_back(PrefReg);
} }
/// Specify the preferred register allocation hint for the specified virtual /// addRegAllocationHint - Add a register allocation hint to the hints
/// register. /// vector for VReg.
void addRegAllocationHint(unsigned VReg, unsigned PrefReg) {
assert(TargetRegisterInfo::isVirtualRegister(VReg));
RegAllocHints[VReg].second.push_back(PrefReg);
}
/// Specify the preferred (target independent) register allocation hint for
/// the specified virtual register.
void setSimpleHint(unsigned VReg, unsigned PrefReg) { void setSimpleHint(unsigned VReg, unsigned PrefReg) {
setRegAllocationHint(VReg, /*Type=*/0, PrefReg); setRegAllocationHint(VReg, /*Type=*/0, PrefReg);
} }
void clearSimpleHint(unsigned VReg) {
assert (RegAllocHints[VReg].first == 0 &&
"Expected to clear a non-target hint!");
RegAllocHints[VReg].second.clear();
}
/// getRegAllocationHint - Return the register allocation hint for the /// getRegAllocationHint - Return the register allocation hint for the
/// specified virtual register. /// specified virtual register. If there are many hints, this returns the
/// one with the greatest weight.
std::pair<unsigned, unsigned> std::pair<unsigned, unsigned>
getRegAllocationHint(unsigned VReg) const { getRegAllocationHint(unsigned VReg) const {
assert(TargetRegisterInfo::isVirtualRegister(VReg)); assert(TargetRegisterInfo::isVirtualRegister(VReg));
return RegAllocHints[VReg]; unsigned BestHint = (RegAllocHints[VReg].second.size() ?
RegAllocHints[VReg].second[0] : 0);
return std::pair<unsigned, unsigned>(RegAllocHints[VReg].first, BestHint);
} }
/// getSimpleHint - Return the preferred register allocation hint, or 0 if a /// getSimpleHint - same as getRegAllocationHint except it will only return
/// standard simple hint (Type == 0) is not set. /// a target independent hint.
unsigned getSimpleHint(unsigned VReg) const { unsigned getSimpleHint(unsigned VReg) const {
assert(TargetRegisterInfo::isVirtualRegister(VReg)); assert(TargetRegisterInfo::isVirtualRegister(VReg));
std::pair<unsigned, unsigned> Hint = getRegAllocationHint(VReg); std::pair<unsigned, unsigned> Hint = getRegAllocationHint(VReg);
return Hint.first ? 0 : Hint.second; return Hint.first ? 0 : Hint.second;
} }
/// getRegAllocationHints - Return a reference to the vector of all
/// register allocation hints for VReg.
const std::pair<unsigned, SmallVector<unsigned, 4>>
&getRegAllocationHints(unsigned VReg) const {
assert(TargetRegisterInfo::isVirtualRegister(VReg));
return RegAllocHints[VReg];
}
/// markUsesInDebugValueAsUndef - Mark every DBG_VALUE referencing the /// markUsesInDebugValueAsUndef - Mark every DBG_VALUE referencing the
/// specified register as undefined which causes the DBG_VALUE to be /// specified register as undefined which causes the DBG_VALUE to be
/// deleted during LiveDebugVariables analysis. /// deleted during LiveDebugVariables analysis.

View File

@ -785,11 +785,10 @@ public:
/// as returned from RegisterClassInfo::getOrder(). The hint registers must /// as returned from RegisterClassInfo::getOrder(). The hint registers must
/// come from Order, and they must not be reserved. /// come from Order, and they must not be reserved.
/// ///
/// The default implementation of this function can resolve /// The default implementation of this function will only add target
/// target-independent hints provided to MRI::setRegAllocationHint with /// independent register allocation hints. Targets that override this
/// HintType == 0. Targets that override this function should defer to the /// function should typically call this default implementation as well and
/// default implementation if they have no reason to change the allocation /// expect to see generic copy hints added.
/// order for VirtReg. There may be target-independent hints.
virtual bool getRegAllocationHints(unsigned VirtReg, virtual bool getRegAllocationHints(unsigned VirtReg,
ArrayRef<MCPhysReg> Order, ArrayRef<MCPhysReg> Order,
SmallVectorImpl<MCPhysReg> &Hints, SmallVectorImpl<MCPhysReg> &Hints,
@ -808,6 +807,13 @@ public:
// Do nothing. // Do nothing.
} }
/// The creation of multiple copy hints have been implemented in
/// weightCalcHelper(), but since this affects so many tests for many
/// targets, this is temporarily disabled per default. THIS SHOULD BE
/// "GENERAL GOODNESS" and hopefully all targets will update their tests
/// and enable this soon. This hook should then be removed.
virtual bool enableMultipleCopyHints() const { return false; }
/// Allow the target to reverse allocation order of local live ranges. This /// Allow the target to reverse allocation order of local live ranges. This
/// will generally allocate shorter local live ranges first. For targets with /// will generally allocate shorter local live ranges first. For targets with
/// many registers, this could reduce regalloc compile time by a large /// many registers, this could reduce regalloc compile time by a large

View File

@ -70,13 +70,24 @@ static unsigned copyHint(const MachineInstr *mi, unsigned reg,
return sub == hsub ? hreg : 0; return sub == hsub ? hreg : 0;
const TargetRegisterClass *rc = mri.getRegClass(reg); const TargetRegisterClass *rc = mri.getRegClass(reg);
if (!tri.enableMultipleCopyHints()) {
// Only allow physreg hints in rc.
if (sub == 0)
return rc->contains(hreg) ? hreg : 0;
// Only allow physreg hints in rc. // reg:sub should match the physreg hreg.
if (sub == 0) return tri.getMatchingSuperReg(hreg, sub, rc);
return rc->contains(hreg) ? hreg : 0; }
// reg:sub should match the physreg hreg. unsigned CopiedPReg = (hsub ? tri.getSubReg(hreg, hsub) : hreg);
return tri.getMatchingSuperReg(hreg, sub, rc); if (rc->contains(CopiedPReg))
return CopiedPReg;
// Check if reg:sub matches so that a super register could be hinted.
if (sub)
return tri.getMatchingSuperReg(CopiedPReg, sub, rc);
return 0;
} }
// Check if all values in LI are rematerializable // Check if all values in LI are rematerializable
@ -157,12 +168,7 @@ float VirtRegAuxInfo::weightCalcHelper(LiveInterval &li, SlotIndex *start,
unsigned numInstr = 0; // Number of instructions using li unsigned numInstr = 0; // Number of instructions using li
SmallPtrSet<MachineInstr*, 8> visited; SmallPtrSet<MachineInstr*, 8> visited;
// Find the best physreg hint and the best virtreg hint. std::pair<unsigned, unsigned> TargetHint = mri.getRegAllocationHint(li.reg);
float bestPhys = 0, bestVirt = 0;
unsigned hintPhys = 0, hintVirt = 0;
// Don't recompute a target specific hint.
bool noHint = mri.getRegAllocationHint(li.reg).first != 0;
// Don't recompute spill weight for an unspillable register. // Don't recompute spill weight for an unspillable register.
bool Spillable = li.isSpillable(); bool Spillable = li.isSpillable();
@ -188,6 +194,36 @@ float VirtRegAuxInfo::weightCalcHelper(LiveInterval &li, SlotIndex *start,
numInstr += 2; numInstr += 2;
} }
// CopyHint is a sortable hint derived from a COPY instruction.
struct CopyHint {
unsigned Reg;
float Weight;
bool IsPhys;
unsigned HintOrder;
CopyHint(unsigned R, float W, bool P, unsigned HR) :
Reg(R), Weight(W), IsPhys(P), HintOrder(HR) {}
bool operator<(const CopyHint &rhs) const {
// Always prefer any physreg hint.
if (IsPhys != rhs.IsPhys)
return (IsPhys && !rhs.IsPhys);
if (Weight != rhs.Weight)
return (Weight > rhs.Weight);
// This is just a temporary way to achive NFC for targets that don't
// enable multiple copy hints. HintOrder should be removed when all
// targets return true in enableMultipleCopyHints().
return (HintOrder < rhs.HintOrder);
#if 0 // Should replace the HintOrder check, see above.
// (just for the purpose of maintaining the set)
return Reg < rhs.Reg;
#endif
}
};
std::set<CopyHint> CopyHints;
// Temporary: see comment for HintOrder above.
unsigned CopyHintOrder = 0;
for (MachineRegisterInfo::reg_instr_iterator for (MachineRegisterInfo::reg_instr_iterator
I = mri.reg_instr_begin(li.reg), E = mri.reg_instr_end(); I = mri.reg_instr_begin(li.reg), E = mri.reg_instr_end();
I != E; ) { I != E; ) {
@ -227,7 +263,8 @@ float VirtRegAuxInfo::weightCalcHelper(LiveInterval &li, SlotIndex *start,
} }
// Get allocation hints from copies. // Get allocation hints from copies.
if (noHint || !mi->isCopy()) if (!mi->isCopy() ||
(TargetHint.first != 0 && !tri.enableMultipleCopyHints()))
continue; continue;
unsigned hint = copyHint(mi, li.reg, tri, mri); unsigned hint = copyHint(mi, li.reg, tri, mri);
if (!hint) if (!hint)
@ -237,28 +274,30 @@ float VirtRegAuxInfo::weightCalcHelper(LiveInterval &li, SlotIndex *start,
// //
// FIXME: we probably shouldn't use floats at all. // FIXME: we probably shouldn't use floats at all.
volatile float hweight = Hint[hint] += weight; volatile float hweight = Hint[hint] += weight;
if (TargetRegisterInfo::isPhysicalRegister(hint)) { if (TargetRegisterInfo::isVirtualRegister(hint) || mri.isAllocatable(hint))
if (hweight > bestPhys && mri.isAllocatable(hint)) { CopyHints.insert(CopyHint(hint, hweight, tri.isPhysicalRegister(hint),
bestPhys = hweight; (tri.enableMultipleCopyHints() ? hint : CopyHintOrder++)));
hintPhys = hint;
}
} else {
if (hweight > bestVirt) {
bestVirt = hweight;
hintVirt = hint;
}
}
} }
Hint.clear(); Hint.clear();
// Always prefer the physreg hint. // Pass all the sorted copy hints to mri.
if (updateLI) { if (updateLI && CopyHints.size()) {
if (unsigned hint = hintPhys ? hintPhys : hintVirt) { // Remove a generic hint if previously added by target.
mri.setRegAllocationHint(li.reg, 0, hint); if (TargetHint.first == 0 && TargetHint.second)
// Weakly boost the spill weight of hinted registers. mri.clearSimpleHint(li.reg);
totalWeight *= 1.01F;
for (auto &Hint : CopyHints) {
if (TargetHint.first != 0 && Hint.Reg == TargetHint.second)
// Don't add again the target-type hint.
continue;
mri.addRegAllocationHint(li.reg, Hint.Reg);
if (!tri.enableMultipleCopyHints())
break;
} }
// Weakly boost the spill weight of hinted registers.
totalWeight *= 1.01F;
} }
// If the live interval was already unspillable, leave it that way. // If the live interval was already unspillable, leave it that way.

View File

@ -373,31 +373,36 @@ TargetRegisterInfo::getRegAllocationHints(unsigned VirtReg,
const VirtRegMap *VRM, const VirtRegMap *VRM,
const LiveRegMatrix *Matrix) const { const LiveRegMatrix *Matrix) const {
const MachineRegisterInfo &MRI = MF.getRegInfo(); const MachineRegisterInfo &MRI = MF.getRegInfo();
std::pair<unsigned, unsigned> Hint = MRI.getRegAllocationHint(VirtReg); const std::pair<unsigned, SmallVector<unsigned, 4>> &Hints_MRI =
MRI.getRegAllocationHints(VirtReg);
// Hints with HintType != 0 were set by target-dependent code. // First hint may be a target hint.
// Such targets must provide their own implementation of bool Skip = (Hints_MRI.first != 0);
// TRI::getRegAllocationHints to interpret those hint types. for (auto Reg : Hints_MRI.second) {
assert(Hint.first == 0 && "Target must implement TRI::getRegAllocationHints"); if (Skip) {
Skip = false;
continue;
}
// Target-independent hints are either a physical or a virtual register. // Target-independent hints are either a physical or a virtual register.
unsigned Phys = Hint.second; unsigned Phys = Reg;
if (VRM && isVirtualRegister(Phys)) if (VRM && isVirtualRegister(Phys))
Phys = VRM->getPhys(Phys); Phys = VRM->getPhys(Phys);
// Check that Phys is a valid hint in VirtReg's register class. // Check that Phys is a valid hint in VirtReg's register class.
if (!isPhysicalRegister(Phys)) if (!isPhysicalRegister(Phys))
return false; continue;
if (MRI.isReserved(Phys)) if (MRI.isReserved(Phys))
return false; continue;
// Check that Phys is in the allocation order. We shouldn't heed hints // 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 // from VirtReg's register class if they aren't in the allocation order. The
// target probably has a reason for removing the register. // target probably has a reason for removing the register.
if (!is_contained(Order, Phys)) if (!is_contained(Order, Phys))
return false; continue;
// All clear, tell the register allocator to prefer this register. // All clear, tell the register allocator to prefer this register.
Hints.push_back(Phys); Hints.push_back(Phys);
}
return false; return false;
} }

View File

@ -51,6 +51,8 @@ public:
const VirtRegMap *VRM, const VirtRegMap *VRM,
const LiveRegMatrix *Matrix) const override; const LiveRegMatrix *Matrix) const override;
bool enableMultipleCopyHints() const override { return true; }
// Override TargetRegisterInfo.h. // Override TargetRegisterInfo.h.
bool requiresRegisterScavenging(const MachineFunction &MF) const override { bool requiresRegisterScavenging(const MachineFunction &MF) const override {
return true; return true;

View File

@ -62,16 +62,13 @@ define void @f4() {
; Check an indirect call. In this case the only acceptable choice for ; Check an indirect call. In this case the only acceptable choice for
; the target register is %r1. ; the target register is %r1.
;
; NOTE: the extra copy 'lgr %r1, %r0' is a coalescing failure.
define void @f5(void(i32, i32, i32, i32) *%foo) { define void @f5(void(i32, i32, i32, i32) *%foo) {
; CHECK-LABEL: f5: ; CHECK-LABEL: f5:
; CHECK: lgr %r0, %r2 ; CHECK: lgr %r1, %r2
; CHECK-DAG: lhi %r2, 1 ; CHECK-DAG: lhi %r2, 1
; CHECK-DAG: lhi %r3, 2 ; CHECK-DAG: lhi %r3, 2
; CHECK-DAG: lhi %r4, 3 ; CHECK-DAG: lhi %r4, 3
; CHECK-DAG: lhi %r5, 4 ; CHECK-DAG: lhi %r5, 4
; CHECK: lgr %r1, %r0
; CHECK: br %r1 ; CHECK: br %r1
tail call void %foo(i32 1, i32 2, i32 3, i32 4) tail call void %foo(i32 1, i32 2, i32 3, i32 4)
ret void ret void

View File

@ -39,9 +39,8 @@ declare swiftcc { i16, i8 } @gen(i32)
; in memroy. The caller provides space for the return value and passes ; in memroy. The caller provides space for the return value and passes
; the address in %r2. The first input argument will be in %r3. ; the address in %r2. The first input argument will be in %r3.
; CHECK-LABEL: test2: ; CHECK-LABEL: test2:
; CHECK: lr %[[REG1:r[0-9]+]], %r2 ; CHECK: lr %r3, %r2
; CHECK-DAG: la %r2, 160(%r15) ; CHECK-DAG: la %r2, 160(%r15)
; CHECK-DAG: lr %r3, %[[REG1]]
; CHECK: brasl %r14, gen2 ; CHECK: brasl %r14, gen2
; CHECK: l %r2, 160(%r15) ; CHECK: l %r2, 160(%r15)
; CHECK: a %r2, 164(%r15) ; CHECK: a %r2, 164(%r15)

View File

@ -34,11 +34,11 @@ define float @caller(i8* %error_ref) {
; CHECK: lgr %r[[REG1:[0-9]+]], %r2 ; CHECK: lgr %r[[REG1:[0-9]+]], %r2
; CHECK: lghi %r9, 0 ; CHECK: lghi %r9, 0
; CHECK: brasl %r14, foo ; CHECK: brasl %r14, foo
; CHECK: cgijlh %r9, 0, ; CHECK: %r2, %r9
; CHECK: jlh
; Access part of the error object and save it to error_ref ; Access part of the error object and save it to error_ref
; CHECK: lb %r[[REG2:[0-9]+]], 8(%r9) ; CHECK: lb %r[[REG2:[0-9]+]], 8(%r2)
; CHECK: stc %r[[REG2]], 0(%r[[REG1]]) ; CHECK: stc %r[[REG2]], 0(%r[[REG1]])
; CHECK: lgr %r2, %r9
; CHECK: brasl %r14, free ; CHECK: brasl %r14, free
; CHECK-O0-LABEL: caller: ; CHECK-O0-LABEL: caller:
; CHECK-O0: lghi %r9, 0 ; CHECK-O0: lghi %r9, 0
@ -246,11 +246,10 @@ define float @caller3(i8* %error_ref) {
; CHECK: lhi %r3, 1 ; CHECK: lhi %r3, 1
; CHECK: lghi %r9, 0 ; CHECK: lghi %r9, 0
; CHECK: brasl %r14, foo_sret ; CHECK: brasl %r14, foo_sret
; CHECK: cgijlh %r9, 0, ; CHECK: jlh
; Access part of the error object and save it to error_ref ; Access part of the error object and save it to error_ref
; CHECK: lb %r0, 8(%r9) ; CHECK: lb %r0, 8(%r2)
; CHECK: stc %r0, 0(%r[[REG1]]) ; CHECK: stc %r0, 0(%r[[REG1]])
; CHECK: lgr %r2, %r9
; CHECK: brasl %r14, free ; CHECK: brasl %r14, free
; CHECK-O0-LABEL: caller3: ; CHECK-O0-LABEL: caller3:
@ -296,21 +295,21 @@ define float @caller_with_multiple_swifterror_values(i8* %error_ref, i8* %error_
; The first swifterror value: ; The first swifterror value:
; CHECK: lghi %r9, 0 ; CHECK: lghi %r9, 0
; CHECK: brasl %r14, foo ; CHECK: brasl %r14, foo
; CHECK: cgijlh %r9, 0, ; CHECK: ltgr %r2, %r9
; CHECK: jlh
; Access part of the error object and save it to error_ref ; Access part of the error object and save it to error_ref
; CHECK: lb %r0, 8(%r9) ; CHECK: lb %r0, 8(%r2)
; CHECK: stc %r0, 0(%r[[REG1]]) ; CHECK: stc %r0, 0(%r[[REG1]])
; CHECK: lgr %r2, %r9
; CHECK: brasl %r14, free ; CHECK: brasl %r14, free
; The second swifterror value: ; The second swifterror value:
; CHECK: lghi %r9, 0 ; CHECK: lghi %r9, 0
; CHECK: brasl %r14, foo ; CHECK: brasl %r14, foo
; CHECK: cgijlh %r9, 0, ; CHECK: ltgr %r2, %r9
; CHECK: jlh
; Access part of the error object and save it to error_ref ; Access part of the error object and save it to error_ref
; CHECK: lb %r0, 8(%r9) ; CHECK: lb %r0, 8(%r2)
; CHECK: stc %r0, 0(%r[[REG2]]) ; CHECK: stc %r0, 0(%r[[REG2]])
; CHECK: lgr %r2, %r9
; CHECK: brasl %r14, free ; CHECK: brasl %r14, free
; CHECK-O0-LABEL: caller_with_multiple_swifterror_values: ; CHECK-O0-LABEL: caller_with_multiple_swifterror_values: