Transpose the calculation of spill weights such that we are calculating one

register at a time. This turns out to be slightly faster than iterating over
instructions, but more importantly, it allows us to compute spill weights for
new registers created after the spill weight pass has run.

Also compute the allocation hint at the same time as the spill weight. This
allows us to use the spill weight as a cost metric for copies, and choose the
most profitable hint if there is more than one possibility.

The new hints provide a very small (< 0.1%) but universal code size improvement.

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@110631 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Jakob Stoklund Olesen 2010-08-10 00:02:26 +00:00
parent 533a7df02d
commit df30cf9e61
7 changed files with 169 additions and 116 deletions

View File

@ -12,10 +12,35 @@
#define LLVM_CODEGEN_CALCSPILLWEIGHTS_H
#include "llvm/CodeGen/MachineFunctionPass.h"
#include "llvm/ADT/DenseMap.h"
namespace llvm {
class LiveInterval;
class LiveIntervals;
class MachineLoopInfo;
/// VirtRegAuxInfo - Calculate auxiliary information for a virtual
/// register such as its spill weight and allocation hint.
class VirtRegAuxInfo {
MachineFunction &mf_;
LiveIntervals &lis_;
MachineLoopInfo &loops_;
DenseMap<unsigned, float> hint_;
public:
VirtRegAuxInfo(MachineFunction &mf, LiveIntervals &lis,
MachineLoopInfo &loops) :
mf_(mf), lis_(lis), loops_(loops) {}
/// CalculateRegClass - recompute the register class for li from its uses.
/// Since the register class can affect the allocation hint, this function
/// should be called before CalculateWeightAndHint if both are called.
void CalculateRegClass(LiveInterval &li);
/// CalculateWeightAndHint - (re)compute li's spill weight and allocation
/// hint.
void CalculateWeightAndHint(LiveInterval &li);
};
/// CalculateSpillWeights - Compute spill weights for all virtual register
/// live intervals.
@ -27,7 +52,7 @@ namespace llvm {
virtual void getAnalysisUsage(AnalysisUsage &au) const;
virtual bool runOnMachineFunction(MachineFunction &fn);
virtual bool runOnMachineFunction(MachineFunction &fn);
private:
/// Returns true if the given live interval is zero length.

View File

@ -520,6 +520,15 @@ namespace llvm {
///
unsigned getSize() const;
/// Returns true if the live interval is zero length, i.e. no live ranges
/// span instructions. It doesn't pay to spill such an interval.
bool isZeroLength() const {
for (const_iterator i = begin(), e = end(); i != e; ++i)
if (i->end.getPrevIndex() > i->start)
return false;
return true;
}
/// isSpillable - Can this interval be spilled?
bool isSpillable() const {
return weight != HUGE_VALF;

View File

@ -105,6 +105,12 @@ namespace llvm {
return r2iMap_.count(reg);
}
/// isAllocatable - is the physical register reg allocatable in the current
/// function?
bool isAllocatable(unsigned reg) const {
return allocatableRegs_.test(reg);
}
/// getScaledIntervalSize - get the size of an interval in "units,"
/// where every function is composed of one thousand units. This
/// measure scales properly with empty index slots in the function.

View File

@ -41,107 +41,136 @@ bool CalculateSpillWeights::runOnMachineFunction(MachineFunction &fn) {
<< "********** Function: "
<< fn.getFunction()->getName() << '\n');
LiveIntervals *lis = &getAnalysis<LiveIntervals>();
MachineLoopInfo *loopInfo = &getAnalysis<MachineLoopInfo>();
MachineRegisterInfo *mri = &fn.getRegInfo();
SmallSet<unsigned, 4> processed;
for (MachineFunction::iterator mbbi = fn.begin(), mbbe = fn.end();
mbbi != mbbe; ++mbbi) {
MachineBasicBlock* mbb = mbbi;
SlotIndex mbbEnd = lis->getMBBEndIdx(mbb);
MachineLoop* loop = loopInfo->getLoopFor(mbb);
unsigned loopDepth = loop ? loop->getLoopDepth() : 0;
bool isExiting = loop ? loop->isLoopExiting(mbb) : false;
for (MachineBasicBlock::const_iterator mii = mbb->begin(), mie = mbb->end();
mii != mie; ++mii) {
const MachineInstr *mi = mii;
if (mi->isIdentityCopy() || mi->isImplicitDef() || mi->isDebugValue())
continue;
for (unsigned i = 0, e = mi->getNumOperands(); i != e; ++i) {
const MachineOperand &mopi = mi->getOperand(i);
if (!mopi.isReg() || mopi.getReg() == 0)
continue;
unsigned reg = mopi.getReg();
if (!TargetRegisterInfo::isVirtualRegister(mopi.getReg()))
continue;
// Multiple uses of reg by the same instruction. It should not
// contribute to spill weight again.
if (!processed.insert(reg))
continue;
bool hasDef = mopi.isDef();
bool hasUse = !hasDef;
for (unsigned j = i+1; j != e; ++j) {
const MachineOperand &mopj = mi->getOperand(j);
if (!mopj.isReg() || mopj.getReg() != reg)
continue;
hasDef |= mopj.isDef();
hasUse |= mopj.isUse();
if (hasDef && hasUse)
break;
}
LiveInterval &regInt = lis->getInterval(reg);
float weight = lis->getSpillWeight(hasDef, hasUse, loopDepth);
if (hasDef && isExiting) {
// Looks like this is a loop count variable update.
SlotIndex defIdx = lis->getInstructionIndex(mi).getDefIndex();
const LiveRange *dlr =
lis->getInterval(reg).getLiveRangeContaining(defIdx);
if (dlr->end >= mbbEnd)
weight *= 3.0F;
}
regInt.weight += weight;
}
processed.clear();
}
}
for (LiveIntervals::iterator I = lis->begin(), E = lis->end(); I != E; ++I) {
LiveIntervals &lis = getAnalysis<LiveIntervals>();
VirtRegAuxInfo vrai(fn, lis, getAnalysis<MachineLoopInfo>());
for (LiveIntervals::iterator I = lis.begin(), E = lis.end(); I != E; ++I) {
LiveInterval &li = *I->second;
if (TargetRegisterInfo::isVirtualRegister(li.reg)) {
// If the live interval length is essentially zero, i.e. in every live
// range the use follows def immediately, it doesn't make sense to spill
// it and hope it will be easier to allocate for this li.
if (isZeroLengthInterval(&li)) {
li.weight = HUGE_VALF;
continue;
}
bool isLoad = false;
SmallVector<LiveInterval*, 4> spillIs;
if (lis->isReMaterializable(li, spillIs, isLoad)) {
// If all of the definitions of the interval are re-materializable,
// it is a preferred candidate for spilling. If none of the defs are
// loads, then it's potentially very cheap to re-materialize.
// FIXME: this gets much more complicated once we support non-trivial
// re-materialization.
if (isLoad)
li.weight *= 0.9F;
else
li.weight *= 0.5F;
}
// Slightly prefer live interval that has been assigned a preferred reg.
std::pair<unsigned, unsigned> Hint = mri->getRegAllocationHint(li.reg);
if (Hint.first || Hint.second)
li.weight *= 1.01F;
lis->normalizeSpillWeight(li);
}
if (TargetRegisterInfo::isVirtualRegister(li.reg))
vrai.CalculateWeightAndHint(li);
}
return false;
}
/// Returns true if the given live interval is zero length.
bool CalculateSpillWeights::isZeroLengthInterval(LiveInterval *li) const {
for (LiveInterval::Ranges::const_iterator
i = li->ranges.begin(), e = li->ranges.end(); i != e; ++i)
if (i->end.getPrevIndex() > i->start)
return false;
return true;
// Return the preferred allocation register for reg, given a COPY instruction.
static unsigned copyHint(const MachineInstr *mi, unsigned reg,
const TargetRegisterInfo &tri,
const MachineRegisterInfo &mri) {
unsigned sub, hreg, hsub;
if (mi->getOperand(0).getReg() == reg) {
sub = mi->getOperand(0).getSubReg();
hreg = mi->getOperand(1).getReg();
hsub = mi->getOperand(1).getSubReg();
} else {
sub = mi->getOperand(1).getSubReg();
hreg = mi->getOperand(0).getReg();
hsub = mi->getOperand(0).getSubReg();
}
if (!hreg)
return 0;
if (TargetRegisterInfo::isVirtualRegister(hreg))
return sub == hsub ? hreg : 0;
const TargetRegisterClass *rc = mri.getRegClass(reg);
// Only allow physreg hints in rc.
if (sub == 0)
return rc->contains(hreg) ? hreg : 0;
// reg:sub should match the physreg hreg.
return tri.getMatchingSuperReg(hreg, sub, rc);
}
void VirtRegAuxInfo::CalculateWeightAndHint(LiveInterval &li) {
MachineRegisterInfo &mri = mf_.getRegInfo();
const TargetRegisterInfo &tri = *mf_.getTarget().getRegisterInfo();
MachineBasicBlock *mbb = 0;
MachineLoop *loop = 0;
unsigned loopDepth = 0;
bool isExiting = false;
float totalWeight = 0;
SmallPtrSet<MachineInstr*, 8> visited;
// Find the best physreg hist and the best virtreg hint.
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;
for (MachineRegisterInfo::reg_iterator I = mri.reg_begin(li.reg);
MachineInstr *mi = I.skipInstruction();) {
if (mi->isIdentityCopy() || mi->isImplicitDef() || mi->isDebugValue())
continue;
if (!visited.insert(mi))
continue;
// Get loop info for mi.
if (mi->getParent() != mbb) {
mbb = mi->getParent();
loop = loops_.getLoopFor(mbb);
loopDepth = loop ? loop->getLoopDepth() : 0;
isExiting = loop ? loop->isLoopExiting(mbb) : false;
}
// Calculate instr weight.
bool reads, writes;
tie(reads, writes) = mi->readsWritesVirtualRegister(li.reg);
float weight = LiveIntervals::getSpillWeight(writes, reads, loopDepth);
// Give extra weight to what looks like a loop induction variable update.
if (writes && isExiting && lis_.isLiveOutOfMBB(li, mbb))
weight *= 3;
totalWeight += weight;
// Get allocation hints from copies.
if (noHint || !mi->isCopy())
continue;
unsigned hint = copyHint(mi, li.reg, tri, mri);
if (!hint)
continue;
float hweight = hint_[hint] += weight;
if (TargetRegisterInfo::isPhysicalRegister(hint)) {
if (hweight > bestPhys && lis_.isAllocatable(hint))
bestPhys = hweight, hintPhys = hint;
} else {
if (hweight > bestVirt)
bestVirt = hweight, hintVirt = hint;
}
}
hint_.clear();
// Always prefer the physreg hint.
if (unsigned hint = hintPhys ? hintPhys : hintVirt) {
mri.setRegAllocationHint(li.reg, 0, hint);
// Weakly boost the spill weifght of hinted registers.
totalWeight *= 1.01F;
}
// Mark li as unspillable if all live ranges are tiny.
if (li.isZeroLength()) {
li.markNotSpillable();
return;
}
// If all of the definitions of the interval are re-materializable,
// it is a preferred candidate for spilling. If none of the defs are
// loads, then it's potentially very cheap to re-materialize.
// FIXME: this gets much more complicated once we support non-trivial
// re-materialization.
bool isLoad = false;
SmallVector<LiveInterval*, 4> spillIs;
if (lis_.isReMaterializable(li, spillIs, isLoad)) {
if (isLoad)
totalWeight *= 0.9F;
else
totalWeight *= 0.5F;
}
li.weight = totalWeight;
lis_.normalizeSpillWeight(li);
}

View File

@ -515,18 +515,6 @@ void LiveInterval::join(LiveInterval &Other,
}
ComputeJoinedWeight(Other);
// Update regalloc hint if currently there isn't one.
if (TargetRegisterInfo::isVirtualRegister(reg) &&
TargetRegisterInfo::isVirtualRegister(Other.reg)) {
std::pair<unsigned, unsigned> Hint = MRI->getRegAllocationHint(reg);
if (Hint.first == 0 && Hint.second == 0) {
std::pair<unsigned, unsigned> OtherHint =
MRI->getRegAllocationHint(Other.reg);
if (OtherHint.first || OtherHint.second)
MRI->setRegAllocationHint(reg, OtherHint.first, OtherHint.second);
}
}
}
/// MergeRangesInAsValue - Merge all of the intervals in RHS into this live

View File

@ -1044,7 +1044,7 @@ bool SimpleRegisterCoalescing::JoinCopy(CopyRec &TheCopy, bool &Again) {
if (CP.isPhys()) {
DEBUG(dbgs() <<" with physreg %" << tri_->getName(CP.getDstReg()) << "\n");
// Only coalesce to allocatable physreg.
if (!allocatableRegs_[CP.getDstReg()]) {
if (!li_->isAllocatable(CP.getDstReg())) {
DEBUG(dbgs() << "\tRegister is an unallocatable physreg.\n");
return false; // Not coalescable.
}
@ -1093,7 +1093,6 @@ bool SimpleRegisterCoalescing::JoinCopy(CopyRec &TheCopy, bool &Again) {
// happens.
if (li_->hasInterval(CP.getDstReg()) &&
li_->getInterval(CP.getDstReg()).ranges.size() > 1000) {
mri_->setRegAllocationHint(CP.getSrcReg(), 0, CP.getDstReg());
++numAborts;
DEBUG(dbgs()
<< "\tPhysical register live interval too complicated, abort!\n");
@ -1112,7 +1111,6 @@ bool SimpleRegisterCoalescing::JoinCopy(CopyRec &TheCopy, bool &Again) {
ReMaterializeTrivialDef(JoinVInt, CP.getDstReg(), 0, CopyMI))
return true;
mri_->setRegAllocationHint(CP.getSrcReg(), 0, CP.getDstReg());
++numAborts;
DEBUG(dbgs() << "\tMay tie down a physical register, abort!\n");
Again = true; // May be possible to coalesce later.
@ -1693,7 +1691,6 @@ bool SimpleRegisterCoalescing::runOnMachineFunction(MachineFunction &fn) {
<< "********** Function: "
<< ((Value*)mf_->getFunction())->getName() << '\n');
allocatableRegs_ = tri_->getAllocatableSet(fn);
for (TargetRegisterInfo::regclass_iterator I = tri_->regclass_begin(),
E = tri_->regclass_end(); I != E; ++I)
allocatableRCRegs_.insert(std::make_pair(*I,

View File

@ -47,7 +47,6 @@ namespace llvm {
const MachineLoopInfo* loopInfo;
AliasAnalysis *AA;
BitVector allocatableRegs_;
DenseMap<const TargetRegisterClass*, BitVector> allocatableRCRegs_;
/// JoinedCopies - Keep track of copies eliminated due to coalescing.