Subzero: Implement "second-chance bin-packing" for register allocation.
If a variable gets a register but is later evicted because of a higher-weight variable, there's a chance that the first variable could have been allocated a register if only its initial choice had been different.
To improve this, we keep track of which variables are evicted, and then allow register allocation to run again, focusing only on those once-evicted variables, and not changing any previous register assignments.
This can iterate until there are no more evictions.
This is more or less what the linear-scan literature describes as "second-chance bin-packing".
BUG= https://code.google.com/p/nativeclient/issues/detail?id=4095
R=jpp@chromium.org
Review URL: https://codereview.chromium.org/1395693005 .
diff --git a/src/IceClFlags.cpp b/src/IceClFlags.cpp
index 5ed9ea0..8fd50c7 100644
--- a/src/IceClFlags.cpp
+++ b/src/IceClFlags.cpp
@@ -146,6 +146,11 @@
cl::desc("Randomize register allocation"),
cl::init(false));
+cl::opt<bool>
+ RepeatRegAlloc("regalloc-repeat",
+ cl::desc("Repeat register allocation until convergence"),
+ cl::init(true));
+
cl::opt<bool> SkipUnimplemented(
"skip-unimplemented",
cl::desc("Skip through unimplemented lowering code instead of aborting."),
@@ -383,6 +388,7 @@
OutFlags.PhiEdgeSplit = false;
OutFlags.RandomNopInsertion = false;
OutFlags.RandomRegAlloc = false;
+ OutFlags.RepeatRegAlloc = false;
OutFlags.ReorderBasicBlocks = false;
OutFlags.ReorderFunctions = false;
OutFlags.ReorderGlobalVariables = false;
@@ -455,6 +461,7 @@
OutFlags.setShouldReorderBasicBlocks(::ReorderBasicBlocks);
OutFlags.setShouldDoNopInsertion(::ShouldDoNopInsertion);
OutFlags.setShouldRandomizeRegAlloc(::RandomizeRegisterAllocation);
+ OutFlags.setShouldRepeatRegAlloc(::RepeatRegAlloc);
OutFlags.setShouldReorderFunctions(::ReorderFunctions);
OutFlags.setShouldReorderGlobalVariables(::ReorderGlobalVariables);
OutFlags.setShouldReorderPooledConstants(::ReorderPooledConstants);
diff --git a/src/IceClFlags.h b/src/IceClFlags.h
index 0aba214..d4d7737 100644
--- a/src/IceClFlags.h
+++ b/src/IceClFlags.h
@@ -106,6 +106,9 @@
bool shouldRandomizeRegAlloc() const { return RandomRegAlloc; }
void setShouldRandomizeRegAlloc(bool NewValue) { RandomRegAlloc = NewValue; }
+ bool shouldRepeatRegAlloc() const { return RepeatRegAlloc; }
+ void setShouldRepeatRegAlloc(bool NewValue) { RepeatRegAlloc = NewValue; }
+
bool getSkipUnimplemented() const { return SkipUnimplemented; }
void setSkipUnimplemented(bool NewValue) { SkipUnimplemented = NewValue; }
@@ -262,6 +265,7 @@
bool PhiEdgeSplit;
bool RandomNopInsertion;
bool RandomRegAlloc;
+ bool RepeatRegAlloc;
bool ReorderBasicBlocks;
bool ReorderFunctions;
bool ReorderGlobalVariables;
diff --git a/src/IceDefs.h b/src/IceDefs.h
index f58ebe9..d73f9e7 100644
--- a/src/IceDefs.h
+++ b/src/IceDefs.h
@@ -217,9 +217,10 @@
enum RegAllocKind {
RAK_Unknown,
- RAK_Global, /// full, global register allocation
- RAK_Phi, /// infinite-weight Variables with active spilling/filling
- RAK_InfOnly /// allocation only for infinite-weight Variables
+ RAK_Global, /// full, global register allocation
+ RAK_SecondChance, /// second-chance bin-packing after full regalloc attempt
+ RAK_Phi, /// infinite-weight Variables with active spilling/filling
+ RAK_InfOnly /// allocation only for infinite-weight Variables
};
enum VerboseItem {
diff --git a/src/IceRegAlloc.cpp b/src/IceRegAlloc.cpp
index 4922848..ea15b1b 100644
--- a/src/IceRegAlloc.cpp
+++ b/src/IceRegAlloc.cpp
@@ -277,6 +277,28 @@
Kills.clear();
}
+void LinearScan::initForSecondChance() {
+ TimerMarker T(TimerStack::TT_initUnhandled, Func);
+ FindPreference = true;
+ FindOverlap = true;
+ const VarList &Vars = Func->getVariables();
+ Unhandled.reserve(Vars.size());
+ UnhandledPrecolored.reserve(Vars.size());
+ for (Variable *Var : Vars) {
+ if (Var->hasReg()) {
+ Var->untrimLiveRange();
+ Var->setRegNumTmp(Var->getRegNum());
+ Var->setMustHaveReg();
+ UnhandledPrecolored.push_back(Var);
+ Unhandled.push_back(Var);
+ }
+ }
+ for (Variable *Var : Evicted) {
+ Var->untrimLiveRange();
+ Unhandled.push_back(Var);
+ }
+}
+
void LinearScan::init(RegAllocKind Kind) {
this->Kind = Kind;
Unhandled.clear();
@@ -302,8 +324,13 @@
case RAK_InfOnly:
initForInfOnly();
break;
+ case RAK_SecondChance:
+ initForSecondChance();
+ break;
}
+ Evicted.clear();
+
auto CompareRanges = [](const Variable *L, const Variable *R) {
InstNumberT Lstart = L->getLiveRange().getStart();
InstNumberT Rstart = R->getLiveRange().getStart();
@@ -319,6 +346,7 @@
Handled.reserve(Unhandled.size());
Inactive.reserve(Unhandled.size());
Active.reserve(Unhandled.size());
+ Evicted.reserve(Unhandled.size());
}
// This is called when Cur must be allocated a register but no registers are
@@ -663,6 +691,7 @@
assert(RegUses[RegNum] >= 0);
Item->setRegNumTmp(Variable::NoRegister);
moveItem(Active, Index, Handled);
+ Evicted.push_back(Item);
}
}
// Do the same for Inactive.
@@ -680,6 +709,7 @@
dumpLiveRangeTrace("Evicting I ", Item);
Item->setRegNumTmp(Variable::NoRegister);
moveItem(Inactive, Index, Handled);
+ Evicted.push_back(Item);
}
}
// Assign the register to Cur.
diff --git a/src/IceRegAlloc.h b/src/IceRegAlloc.h
index d0aa111..8faacc7 100644
--- a/src/IceRegAlloc.h
+++ b/src/IceRegAlloc.h
@@ -32,6 +32,12 @@
explicit LinearScan(Cfg *Func);
void init(RegAllocKind Kind);
void scan(const llvm::SmallBitVector &RegMask, bool Randomized);
+ // Returns the number of times some variable has been assigned a register but
+ // later evicted because of a higher-priority allocation. The idea is that we
+ // can implement "second-chance bin-packing" by rerunning register allocation
+ // until there are no more evictions.
+ SizeT getNumEvictions() const { return Evicted.size(); }
+ bool hasEvictions() const { return !Evicted.empty(); }
void dump(Cfg *Func) const;
// TODO(stichnot): Statically choose the size based on the target being
@@ -65,6 +71,7 @@
const CfgVector<InstNumberT> &LREnd) const;
void initForGlobal();
void initForInfOnly();
+ void initForSecondChance();
/// Move an item from the From set to the To set. From[Index] is pushed onto
/// the end of To[], then the item is efficiently removed from From[] by
/// effectively swapping it with the last item in From[] and then popping it
@@ -108,6 +115,7 @@
/// faster processing.
OrderedRanges UnhandledPrecolored;
UnorderedRanges Active, Inactive, Handled;
+ UnorderedRanges Evicted;
CfgVector<InstNumberT> Kills;
RegAllocKind Kind = RAK_Unknown;
/// RegUses[I] is the number of live ranges (variables) that register I is
diff --git a/src/IceTargetLowering.cpp b/src/IceTargetLowering.cpp
index 8d67e8b..aa1a80c 100644
--- a/src/IceTargetLowering.cpp
+++ b/src/IceTargetLowering.cpp
@@ -267,9 +267,15 @@
RegInclude |= RegSet_CalleeSave;
if (hasFramePointer())
RegExclude |= RegSet_FramePointer;
- LinearScan.init(Kind);
llvm::SmallBitVector RegMask = getRegisterSet(RegInclude, RegExclude);
- LinearScan.scan(RegMask, Ctx->getFlags().shouldRandomizeRegAlloc());
+ bool Repeat = (Kind == RAK_Global && Ctx->getFlags().shouldRepeatRegAlloc());
+ do {
+ LinearScan.init(Kind);
+ LinearScan.scan(RegMask, Ctx->getFlags().shouldRandomizeRegAlloc());
+ if (!LinearScan.hasEvictions())
+ Repeat = false;
+ Kind = RAK_SecondChance;
+ } while (Repeat);
}
void TargetLowering::markRedefinitions() {