Skip to content

Commit

Permalink
Add logic to greedy reg alloc to avoid bad eviction chains
Browse files Browse the repository at this point in the history
This fixes bugzilla 26810
https://bugs.llvm.org/show_bug.cgi?id=26810

This is intended to prevent sequences like:
movl %ebp, 8(%esp) # 4-byte Spill
movl %ecx, %ebp
movl %ebx, %ecx
movl %edi, %ebx
movl %edx, %edi
cltd
idivl %esi
movl %edi, %edx
movl %ebx, %edi
movl %ecx, %ebx
movl %ebp, %ecx
movl 16(%esp), %ebp # 4 - byte Reload

Such sequences are created in 2 scenarios:

Scenario #1:
vreg0 is evicted from physreg0 by vreg1
Evictee vreg0 is intended for region splitting with split candidate physreg0 (the reg vreg0 was evicted from)
Region splitting creates a local interval because of interference with the evictor vreg1 (normally region spliiting creates 2 interval, the "by reg" and "by stack" intervals. Local interval created when interference occurs.)
one of the split intervals ends up evicting vreg2 from physreg1
Evictee vreg2 is intended for region splitting with split candidate physreg1
one of the split intervals ends up evicting vreg3 from physreg2 etc.. until someone spills

Scenario #2
vreg0 is evicted from physreg0 by vreg1
vreg2 is evicted from physreg2 by vreg3 etc
Evictee vreg0 is intended for region splitting with split candidate physreg1
Region splitting creates a local interval because of interference with the evictor vreg1
one of the split intervals ends up evicting back original evictor vreg1 from physreg0 (the reg vreg0 was evicted from)
Another evictee vreg2 is intended for region splitting with split candidate physreg1
one of the split intervals ends up evicting vreg3 from physreg2 etc.. until someone spills

As compile time was a concern, I've added a flag to control weather we do cost calculations for local intervals we expect to be created (it's on by default for X86 target, off for the rest).

Differential Revision: https://reviews.llvm.org/D35816

Change-Id: Id9411ff7bbb845463d289ba2ae97737a1ee7cc39
llvm-svn: 316295
  • Loading branch information
Marina Yatsina committed Oct 22, 2017
1 parent dac2026 commit f9371d8
Show file tree
Hide file tree
Showing 10 changed files with 881 additions and 20 deletions.
26 changes: 26 additions & 0 deletions llvm/include/llvm/CodeGen/CalcSpillWeights.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,32 @@ class VirtRegMap;

/// \brief (re)compute li's spill weight and allocation hint.
void calculateSpillWeightAndHint(LiveInterval &li);

/// \brief Compute future expected spill weight of a split artifact of li
/// that will span between start and end slot indexes.
/// \param li The live interval to be split.
/// \param start The expected begining of the split artifact. Instructions
/// before start will not affect the weight.
/// \param end The expected end of the split artifact. Instructions
/// after end will not affect the weight.
/// \return The expected spill weight of the split artifact. Returns
/// negative weight for unspillable li.
float futureWeight(LiveInterval &li, SlotIndex start, SlotIndex end);

/// \brief Helper function for weight calculations.
/// (Re)compute li's spill weight and allocation hint, or, for non null
/// start and end - compute future expected spill weight of a split
/// artifact of li that will span between start and end slot indexes.
/// \param li The live interval for which to compute the weight.
/// \param start The expected begining of the split artifact. Instructions
/// before start will not affect the weight. Relevant for
/// weight calculation of future split artifact.
/// \param end The expected end of the split artifact. Instructions
/// after end will not affect the weight. Relevant for
/// weight calculation of future split artifact.
/// \return The spill weight. Returns negative weight for unspillable li.
float weightCalcHelper(LiveInterval &li, SlotIndex *start = nullptr,
SlotIndex *end = nullptr);
};

/// \brief Compute spill weights and allocation hints for all virtual register
Expand Down
5 changes: 5 additions & 0 deletions llvm/include/llvm/CodeGen/LiveIntervalAnalysis.h
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,11 @@ class VirtRegMap;
const MachineBlockFrequencyInfo *MBFI,
const MachineInstr &Instr);

/// Calculate the spill weight to assign to a single instruction.
static float getSpillWeight(bool isDef, bool isUse,
const MachineBlockFrequencyInfo *MBFI,
const MachineBasicBlock *MBB);

LiveInterval &getInterval(unsigned Reg) {
if (hasInterval(Reg))
return *VirtRegIntervals[Reg];
Expand Down
5 changes: 5 additions & 0 deletions llvm/include/llvm/Target/TargetSubtargetInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,11 @@ class TargetSubtargetInfo : public MCSubtargetInfo {
/// a finer grain to tune the register allocator.
virtual bool enableRALocalReassignment(CodeGenOpt::Level OptLevel) const;

/// \brief True if the subtarget should consider the cost of local intervals
/// created by a split candidate when choosing the best split candidate. This
/// heuristic may be compile time intensive.
virtual bool enableAdvancedRASplitCost() const;

/// \brief Enable use of alias analysis during code generation (during MI
/// scheduling, DAGCombine, etc.).
virtual bool useAA() const;
Expand Down
65 changes: 55 additions & 10 deletions llvm/lib/CodeGen/CalcSpillWeights.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -133,8 +133,21 @@ static bool isRematerializable(const LiveInterval &LI,
return true;
}

void
VirtRegAuxInfo::calculateSpillWeightAndHint(LiveInterval &li) {
void VirtRegAuxInfo::calculateSpillWeightAndHint(LiveInterval &li) {
float weight = weightCalcHelper(li);
// Check if unspillable.
if (weight < 0)
return;
li.weight = weight;
}

float VirtRegAuxInfo::futureWeight(LiveInterval &li, SlotIndex start,
SlotIndex end) {
return weightCalcHelper(li, &start, &end);
}

float VirtRegAuxInfo::weightCalcHelper(LiveInterval &li, SlotIndex *start,
SlotIndex *end) {
MachineRegisterInfo &mri = MF.getRegInfo();
const TargetRegisterInfo &tri = *MF.getSubtarget().getRegisterInfo();
MachineBasicBlock *mbb = nullptr;
Expand All @@ -154,10 +167,38 @@ VirtRegAuxInfo::calculateSpillWeightAndHint(LiveInterval &li) {
// Don't recompute spill weight for an unspillable register.
bool Spillable = li.isSpillable();

bool localSplitArtifact = start && end;

// Do not update future local split artifacts.
bool updateLI = !localSplitArtifact;

if (localSplitArtifact) {
MachineBasicBlock *localMBB = LIS.getMBBFromIndex(*end);
assert(localMBB == LIS.getMBBFromIndex(*start) &&
"start and end are expected to be in the same basic block");

// Local split artifact will have 2 additional copy instructions and they
// will be in the same BB.
// localLI = COPY other
// ...
// other = COPY localLI
totalWeight += LiveIntervals::getSpillWeight(true, false, &MBFI, localMBB);
totalWeight += LiveIntervals::getSpillWeight(false, true, &MBFI, localMBB);

numInstr += 2;
}

for (MachineRegisterInfo::reg_instr_iterator
I = mri.reg_instr_begin(li.reg), E = mri.reg_instr_end();
I != E; ) {
MachineInstr *mi = &*(I++);

// For local split artifacts, we are interested only in instructions between
// the expected start and end of the range.
SlotIndex si = LIS.getInstructionIndex(*mi);
if (localSplitArtifact && ((si < *start) || (si > *end)))
continue;

numInstr++;
if (mi->isIdentityCopy() || mi->isImplicitDef() || mi->isDebugValue())
continue;
Expand Down Expand Up @@ -212,23 +253,25 @@ VirtRegAuxInfo::calculateSpillWeightAndHint(LiveInterval &li) {
Hint.clear();

// Always prefer the physreg hint.
if (unsigned hint = hintPhys ? hintPhys : hintVirt) {
mri.setRegAllocationHint(li.reg, 0, hint);
// Weakly boost the spill weight of hinted registers.
totalWeight *= 1.01F;
if (updateLI) {
if (unsigned hint = hintPhys ? hintPhys : hintVirt) {
mri.setRegAllocationHint(li.reg, 0, hint);
// Weakly boost the spill weight of hinted registers.
totalWeight *= 1.01F;
}
}

// If the live interval was already unspillable, leave it that way.
if (!Spillable)
return;
return -1.0;

// Mark li as unspillable if all live ranges are tiny and the interval
// is not live at any reg mask. If the interval is live at a reg mask
// spilling may be required.
if (li.isZeroLength(LIS.getSlotIndexes()) &&
if (updateLI && li.isZeroLength(LIS.getSlotIndexes()) &&
!li.isLiveAtIndexes(LIS.getRegMaskSlots())) {
li.markNotSpillable();
return;
return -1.0;
}

// If all of the definitions of the interval are re-materializable,
Expand All @@ -238,5 +281,7 @@ VirtRegAuxInfo::calculateSpillWeightAndHint(LiveInterval &li) {
if (isRematerializable(li, LIS, VRM, *MF.getSubtarget().getInstrInfo()))
totalWeight *= 0.5F;

li.weight = normalize(totalWeight, li.getSize(), numInstr);
if (localSplitArtifact)
return normalize(totalWeight, start->distance(*end), numInstr);
return normalize(totalWeight, li.getSize(), numInstr);
}
8 changes: 7 additions & 1 deletion llvm/lib/CodeGen/LiveIntervalAnalysis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -824,7 +824,13 @@ LiveIntervals::hasPHIKill(const LiveInterval &LI, const VNInfo *VNI) const {
float LiveIntervals::getSpillWeight(bool isDef, bool isUse,
const MachineBlockFrequencyInfo *MBFI,
const MachineInstr &MI) {
BlockFrequency Freq = MBFI->getBlockFreq(MI.getParent());
return getSpillWeight(isDef, isUse, MBFI, MI.getParent());
}

float LiveIntervals::getSpillWeight(bool isDef, bool isUse,
const MachineBlockFrequencyInfo *MBFI,
const MachineBasicBlock *MBB) {
BlockFrequency Freq = MBFI->getBlockFreq(MBB);
const float Scale = 1.0f / MBFI->getEntryFreq();
return (isDef + isUse) * (Freq.getFrequency() * Scale);
}
Expand Down
Loading

0 comments on commit f9371d8

Please sign in to comment.