forked from OSchip/llvm-project
Add support for spreading register allocation.
Add a -linearscan-skip-count argument (default to 0) that tells the allocator to remember the last N registers it allocated and skip them when looking for a register candidate. This tends to spread out register usage and free up post-allocation scheduling at the cost of slightly more register pressure. The primary benefit is the ability to backschedule reloads. This is turned off by default. llvm-svn: 89356
This commit is contained in:
parent
aff01ceb95
commit
f37756d609
|
@ -64,9 +64,30 @@ linearscanRegAlloc("linearscan", "linear scan register allocator",
|
||||||
createLinearScanRegisterAllocator);
|
createLinearScanRegisterAllocator);
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
// When we allocate a register, add it to a fixed-size queue of
|
||||||
|
// registers to skip in subsequent allocations. This trades a small
|
||||||
|
// amount of register pressure and increased spills for flexibility in
|
||||||
|
// the post-pass scheduler.
|
||||||
|
//
|
||||||
|
// Note that in a the number of registers used for reloading spills
|
||||||
|
// will be one greater than the value of this option.
|
||||||
|
//
|
||||||
|
// One big limitation of this is that it doesn't differentiate between
|
||||||
|
// different register classes. So on x86-64, if there is xmm register
|
||||||
|
// pressure, it can caused fewer GPRs to be held in the queue.
|
||||||
|
static cl::opt<unsigned>
|
||||||
|
NumRecentlyUsedRegs("linearscan-skip-count",
|
||||||
|
cl::desc("Number of registers for linearscan to remember to skip."),
|
||||||
|
cl::init(0),
|
||||||
|
cl::Hidden);
|
||||||
|
|
||||||
struct RALinScan : public MachineFunctionPass {
|
struct RALinScan : public MachineFunctionPass {
|
||||||
static char ID;
|
static char ID;
|
||||||
RALinScan() : MachineFunctionPass(&ID) {}
|
RALinScan() : MachineFunctionPass(&ID) {
|
||||||
|
// Initialize the queue to record recently-used registers.
|
||||||
|
if (NumRecentlyUsedRegs > 0)
|
||||||
|
RecentRegs.resize(NumRecentlyUsedRegs, 0);
|
||||||
|
}
|
||||||
|
|
||||||
typedef std::pair<LiveInterval*, LiveInterval::iterator> IntervalPtr;
|
typedef std::pair<LiveInterval*, LiveInterval::iterator> IntervalPtr;
|
||||||
typedef SmallVector<IntervalPtr, 32> IntervalPtrs;
|
typedef SmallVector<IntervalPtr, 32> IntervalPtrs;
|
||||||
|
@ -132,6 +153,18 @@ namespace {
|
||||||
|
|
||||||
std::auto_ptr<Spiller> spiller_;
|
std::auto_ptr<Spiller> spiller_;
|
||||||
|
|
||||||
|
// The queue of recently-used registers.
|
||||||
|
SmallVector<unsigned, 3> RecentRegs;
|
||||||
|
|
||||||
|
// Record that we just picked this register.
|
||||||
|
void recordRecentlyUsed(unsigned reg) {
|
||||||
|
assert(reg != 0 && "Recently used register is NOREG!");
|
||||||
|
if (!RecentRegs.empty()) {
|
||||||
|
std::copy(RecentRegs.begin() + 1, RecentRegs.end(), RecentRegs.begin());
|
||||||
|
RecentRegs.back() = reg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
virtual const char* getPassName() const {
|
virtual const char* getPassName() const {
|
||||||
return "Linear Scan Register Allocator";
|
return "Linear Scan Register Allocator";
|
||||||
|
@ -161,6 +194,12 @@ namespace {
|
||||||
/// runOnMachineFunction - register allocate the whole function
|
/// runOnMachineFunction - register allocate the whole function
|
||||||
bool runOnMachineFunction(MachineFunction&);
|
bool runOnMachineFunction(MachineFunction&);
|
||||||
|
|
||||||
|
// Determine if we skip this register due to its being recently used.
|
||||||
|
bool isRecentlyUsed(unsigned reg) const {
|
||||||
|
return std::find(RecentRegs.begin(), RecentRegs.end(), reg) !=
|
||||||
|
RecentRegs.end();
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// linearScan - the linear scan algorithm
|
/// linearScan - the linear scan algorithm
|
||||||
void linearScan();
|
void linearScan();
|
||||||
|
@ -833,9 +872,15 @@ void RALinScan::findIntervalsToSpill(LiveInterval *cur,
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
struct WeightCompare {
|
struct WeightCompare {
|
||||||
|
private:
|
||||||
|
const RALinScan &Allocator;
|
||||||
|
|
||||||
|
public:
|
||||||
|
WeightCompare(const RALinScan &Alloc) : Allocator(Alloc) {};
|
||||||
|
|
||||||
typedef std::pair<unsigned, float> RegWeightPair;
|
typedef std::pair<unsigned, float> RegWeightPair;
|
||||||
bool operator()(const RegWeightPair &LHS, const RegWeightPair &RHS) const {
|
bool operator()(const RegWeightPair &LHS, const RegWeightPair &RHS) const {
|
||||||
return LHS.second < RHS.second;
|
return LHS.second < RHS.second && !Allocator.isRecentlyUsed(LHS.first);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1079,7 +1124,8 @@ void RALinScan::assignRegOrStackSlotAtInterval(LiveInterval* cur) {
|
||||||
e = RC->allocation_order_end(*mf_); i != e; ++i) {
|
e = RC->allocation_order_end(*mf_); i != e; ++i) {
|
||||||
unsigned reg = *i;
|
unsigned reg = *i;
|
||||||
float regWeight = SpillWeights[reg];
|
float regWeight = SpillWeights[reg];
|
||||||
if (minWeight > regWeight)
|
// Skip recently allocated registers.
|
||||||
|
if (minWeight > regWeight && !isRecentlyUsed(reg))
|
||||||
Found = true;
|
Found = true;
|
||||||
RegsWeights.push_back(std::make_pair(reg, regWeight));
|
RegsWeights.push_back(std::make_pair(reg, regWeight));
|
||||||
}
|
}
|
||||||
|
@ -1097,7 +1143,7 @@ void RALinScan::assignRegOrStackSlotAtInterval(LiveInterval* cur) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sort all potential spill candidates by weight.
|
// Sort all potential spill candidates by weight.
|
||||||
std::sort(RegsWeights.begin(), RegsWeights.end(), WeightCompare());
|
std::sort(RegsWeights.begin(), RegsWeights.end(), WeightCompare(*this));
|
||||||
minReg = RegsWeights[0].first;
|
minReg = RegsWeights[0].first;
|
||||||
minWeight = RegsWeights[0].second;
|
minWeight = RegsWeights[0].second;
|
||||||
if (minWeight == HUGE_VALF) {
|
if (minWeight == HUGE_VALF) {
|
||||||
|
@ -1360,7 +1406,8 @@ unsigned RALinScan::getFreePhysReg(LiveInterval* cur,
|
||||||
// Ignore "downgraded" registers.
|
// Ignore "downgraded" registers.
|
||||||
if (SkipDGRegs && DowngradedRegs.count(Reg))
|
if (SkipDGRegs && DowngradedRegs.count(Reg))
|
||||||
continue;
|
continue;
|
||||||
if (isRegAvail(Reg)) {
|
// Skip recently allocated registers.
|
||||||
|
if (isRegAvail(Reg) && !isRecentlyUsed(Reg)) {
|
||||||
FreeReg = Reg;
|
FreeReg = Reg;
|
||||||
if (FreeReg < inactiveCounts.size())
|
if (FreeReg < inactiveCounts.size())
|
||||||
FreeRegInactiveCount = inactiveCounts[FreeReg];
|
FreeRegInactiveCount = inactiveCounts[FreeReg];
|
||||||
|
@ -1372,9 +1419,12 @@ unsigned RALinScan::getFreePhysReg(LiveInterval* cur,
|
||||||
|
|
||||||
// If there are no free regs, or if this reg has the max inactive count,
|
// If there are no free regs, or if this reg has the max inactive count,
|
||||||
// return this register.
|
// return this register.
|
||||||
if (FreeReg == 0 || FreeRegInactiveCount == MaxInactiveCount)
|
if (FreeReg == 0 || FreeRegInactiveCount == MaxInactiveCount) {
|
||||||
|
// Remember what register we picked so we can skip it next time.
|
||||||
|
if (FreeReg != 0) recordRecentlyUsed(FreeReg);
|
||||||
return FreeReg;
|
return FreeReg;
|
||||||
|
}
|
||||||
|
|
||||||
// Continue scanning the registers, looking for the one with the highest
|
// Continue scanning the registers, looking for the one with the highest
|
||||||
// inactive count. Alkis found that this reduced register pressure very
|
// inactive count. Alkis found that this reduced register pressure very
|
||||||
// slightly on X86 (in rev 1.94 of this file), though this should probably be
|
// slightly on X86 (in rev 1.94 of this file), though this should probably be
|
||||||
|
@ -1393,6 +1443,9 @@ unsigned RALinScan::getFreePhysReg(LiveInterval* cur,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Remember what register we picked so we can skip it next time.
|
||||||
|
recordRecentlyUsed(FreeReg);
|
||||||
|
|
||||||
return FreeReg;
|
return FreeReg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue