Commit 4001c939 by Jim Stichnoth

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 .
parent c5abdc13
......@@ -146,6 +146,11 @@ cl::opt<bool>
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 @@ void ClFlags::resetClFlags(ClFlags &OutFlags) {
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 @@ void ClFlags::getParsedClFlags(ClFlags &OutFlags) {
OutFlags.setShouldReorderBasicBlocks(::ReorderBasicBlocks);
OutFlags.setShouldDoNopInsertion(::ShouldDoNopInsertion);
OutFlags.setShouldRandomizeRegAlloc(::RandomizeRegisterAllocation);
OutFlags.setShouldRepeatRegAlloc(::RepeatRegAlloc);
OutFlags.setShouldReorderFunctions(::ReorderFunctions);
OutFlags.setShouldReorderGlobalVariables(::ReorderGlobalVariables);
OutFlags.setShouldReorderPooledConstants(::ReorderPooledConstants);
......
......@@ -106,6 +106,9 @@ public:
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 @@ private:
bool PhiEdgeSplit;
bool RandomNopInsertion;
bool RandomRegAlloc;
bool RepeatRegAlloc;
bool ReorderBasicBlocks;
bool ReorderFunctions;
bool ReorderGlobalVariables;
......
......@@ -217,9 +217,10 @@ enum LivenessMode {
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 {
......
......@@ -277,6 +277,28 @@ void LinearScan::initForInfOnly() {
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 @@ void LinearScan::init(RegAllocKind Kind) {
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 @@ void LinearScan::init(RegAllocKind Kind) {
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 @@ void LinearScan::handleNoFreeRegisters(IterationState &Iter) {
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 @@ void LinearScan::handleNoFreeRegisters(IterationState &Iter) {
dumpLiveRangeTrace("Evicting I ", Item);
Item->setRegNumTmp(Variable::NoRegister);
moveItem(Inactive, Index, Handled);
Evicted.push_back(Item);
}
}
// Assign the register to Cur.
......
......@@ -32,6 +32,12 @@ public:
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 @@ private:
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 @@ private:
/// 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
......
......@@ -267,9 +267,15 @@ void TargetLowering::regAlloc(RegAllocKind Kind) {
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() {
......
; Shows that the ARM integrated assembler can translate a trivial,
; bundle-aligned function.
; REQUIRES: allow_dump
; RUN: %p2i --filetype=asm -i %s --target=arm32 \
; RUN: | FileCheck %s --check-prefix=ASM
; RUN: %p2i --filetype=iasm -i %s --target=arm32 \
......
......@@ -172,11 +172,11 @@ next2:
; CHECK-LABEL: test_local_forward_then_back
; CHECK: {{.*}} mov DWORD PTR
; CHECK-NEXT: {{.*}} mfence
; CHECK-NEXT: 16: {{.*}} mov {{.*}},0x1
; CHECK-NEXT: [[LABEL:[0-9a-f]+]]: {{.*}} mov {{.*}},0x1
; CHECK-NEXT: {{.*}} cmp
; CHECK-NEXT: {{.*}} jb
; CHECK: {{.*}} jne
; CHECK: {{.*}} jmp 16
; CHECK: {{.*}} jmp [[LABEL]]
; Test that backward local branches also work and are small.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment