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> ...@@ -146,6 +146,11 @@ cl::opt<bool>
cl::desc("Randomize register allocation"), cl::desc("Randomize register allocation"),
cl::init(false)); cl::init(false));
cl::opt<bool>
RepeatRegAlloc("regalloc-repeat",
cl::desc("Repeat register allocation until convergence"),
cl::init(true));
cl::opt<bool> SkipUnimplemented( cl::opt<bool> SkipUnimplemented(
"skip-unimplemented", "skip-unimplemented",
cl::desc("Skip through unimplemented lowering code instead of aborting."), cl::desc("Skip through unimplemented lowering code instead of aborting."),
...@@ -383,6 +388,7 @@ void ClFlags::resetClFlags(ClFlags &OutFlags) { ...@@ -383,6 +388,7 @@ void ClFlags::resetClFlags(ClFlags &OutFlags) {
OutFlags.PhiEdgeSplit = false; OutFlags.PhiEdgeSplit = false;
OutFlags.RandomNopInsertion = false; OutFlags.RandomNopInsertion = false;
OutFlags.RandomRegAlloc = false; OutFlags.RandomRegAlloc = false;
OutFlags.RepeatRegAlloc = false;
OutFlags.ReorderBasicBlocks = false; OutFlags.ReorderBasicBlocks = false;
OutFlags.ReorderFunctions = false; OutFlags.ReorderFunctions = false;
OutFlags.ReorderGlobalVariables = false; OutFlags.ReorderGlobalVariables = false;
...@@ -455,6 +461,7 @@ void ClFlags::getParsedClFlags(ClFlags &OutFlags) { ...@@ -455,6 +461,7 @@ void ClFlags::getParsedClFlags(ClFlags &OutFlags) {
OutFlags.setShouldReorderBasicBlocks(::ReorderBasicBlocks); OutFlags.setShouldReorderBasicBlocks(::ReorderBasicBlocks);
OutFlags.setShouldDoNopInsertion(::ShouldDoNopInsertion); OutFlags.setShouldDoNopInsertion(::ShouldDoNopInsertion);
OutFlags.setShouldRandomizeRegAlloc(::RandomizeRegisterAllocation); OutFlags.setShouldRandomizeRegAlloc(::RandomizeRegisterAllocation);
OutFlags.setShouldRepeatRegAlloc(::RepeatRegAlloc);
OutFlags.setShouldReorderFunctions(::ReorderFunctions); OutFlags.setShouldReorderFunctions(::ReorderFunctions);
OutFlags.setShouldReorderGlobalVariables(::ReorderGlobalVariables); OutFlags.setShouldReorderGlobalVariables(::ReorderGlobalVariables);
OutFlags.setShouldReorderPooledConstants(::ReorderPooledConstants); OutFlags.setShouldReorderPooledConstants(::ReorderPooledConstants);
......
...@@ -106,6 +106,9 @@ public: ...@@ -106,6 +106,9 @@ public:
bool shouldRandomizeRegAlloc() const { return RandomRegAlloc; } bool shouldRandomizeRegAlloc() const { return RandomRegAlloc; }
void setShouldRandomizeRegAlloc(bool NewValue) { RandomRegAlloc = NewValue; } void setShouldRandomizeRegAlloc(bool NewValue) { RandomRegAlloc = NewValue; }
bool shouldRepeatRegAlloc() const { return RepeatRegAlloc; }
void setShouldRepeatRegAlloc(bool NewValue) { RepeatRegAlloc = NewValue; }
bool getSkipUnimplemented() const { return SkipUnimplemented; } bool getSkipUnimplemented() const { return SkipUnimplemented; }
void setSkipUnimplemented(bool NewValue) { SkipUnimplemented = NewValue; } void setSkipUnimplemented(bool NewValue) { SkipUnimplemented = NewValue; }
...@@ -262,6 +265,7 @@ private: ...@@ -262,6 +265,7 @@ private:
bool PhiEdgeSplit; bool PhiEdgeSplit;
bool RandomNopInsertion; bool RandomNopInsertion;
bool RandomRegAlloc; bool RandomRegAlloc;
bool RepeatRegAlloc;
bool ReorderBasicBlocks; bool ReorderBasicBlocks;
bool ReorderFunctions; bool ReorderFunctions;
bool ReorderGlobalVariables; bool ReorderGlobalVariables;
......
...@@ -217,9 +217,10 @@ enum LivenessMode { ...@@ -217,9 +217,10 @@ enum LivenessMode {
enum RegAllocKind { enum RegAllocKind {
RAK_Unknown, RAK_Unknown,
RAK_Global, /// full, global register allocation RAK_Global, /// full, global register allocation
RAK_Phi, /// infinite-weight Variables with active spilling/filling RAK_SecondChance, /// second-chance bin-packing after full regalloc attempt
RAK_InfOnly /// allocation only for infinite-weight Variables RAK_Phi, /// infinite-weight Variables with active spilling/filling
RAK_InfOnly /// allocation only for infinite-weight Variables
}; };
enum VerboseItem { enum VerboseItem {
......
...@@ -277,6 +277,28 @@ void LinearScan::initForInfOnly() { ...@@ -277,6 +277,28 @@ void LinearScan::initForInfOnly() {
Kills.clear(); 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) { void LinearScan::init(RegAllocKind Kind) {
this->Kind = Kind; this->Kind = Kind;
Unhandled.clear(); Unhandled.clear();
...@@ -302,8 +324,13 @@ void LinearScan::init(RegAllocKind Kind) { ...@@ -302,8 +324,13 @@ void LinearScan::init(RegAllocKind Kind) {
case RAK_InfOnly: case RAK_InfOnly:
initForInfOnly(); initForInfOnly();
break; break;
case RAK_SecondChance:
initForSecondChance();
break;
} }
Evicted.clear();
auto CompareRanges = [](const Variable *L, const Variable *R) { auto CompareRanges = [](const Variable *L, const Variable *R) {
InstNumberT Lstart = L->getLiveRange().getStart(); InstNumberT Lstart = L->getLiveRange().getStart();
InstNumberT Rstart = R->getLiveRange().getStart(); InstNumberT Rstart = R->getLiveRange().getStart();
...@@ -319,6 +346,7 @@ void LinearScan::init(RegAllocKind Kind) { ...@@ -319,6 +346,7 @@ void LinearScan::init(RegAllocKind Kind) {
Handled.reserve(Unhandled.size()); Handled.reserve(Unhandled.size());
Inactive.reserve(Unhandled.size()); Inactive.reserve(Unhandled.size());
Active.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 // This is called when Cur must be allocated a register but no registers are
...@@ -663,6 +691,7 @@ void LinearScan::handleNoFreeRegisters(IterationState &Iter) { ...@@ -663,6 +691,7 @@ void LinearScan::handleNoFreeRegisters(IterationState &Iter) {
assert(RegUses[RegNum] >= 0); assert(RegUses[RegNum] >= 0);
Item->setRegNumTmp(Variable::NoRegister); Item->setRegNumTmp(Variable::NoRegister);
moveItem(Active, Index, Handled); moveItem(Active, Index, Handled);
Evicted.push_back(Item);
} }
} }
// Do the same for Inactive. // Do the same for Inactive.
...@@ -680,6 +709,7 @@ void LinearScan::handleNoFreeRegisters(IterationState &Iter) { ...@@ -680,6 +709,7 @@ void LinearScan::handleNoFreeRegisters(IterationState &Iter) {
dumpLiveRangeTrace("Evicting I ", Item); dumpLiveRangeTrace("Evicting I ", Item);
Item->setRegNumTmp(Variable::NoRegister); Item->setRegNumTmp(Variable::NoRegister);
moveItem(Inactive, Index, Handled); moveItem(Inactive, Index, Handled);
Evicted.push_back(Item);
} }
} }
// Assign the register to Cur. // Assign the register to Cur.
......
...@@ -32,6 +32,12 @@ public: ...@@ -32,6 +32,12 @@ public:
explicit LinearScan(Cfg *Func); explicit LinearScan(Cfg *Func);
void init(RegAllocKind Kind); void init(RegAllocKind Kind);
void scan(const llvm::SmallBitVector &RegMask, bool Randomized); 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; void dump(Cfg *Func) const;
// TODO(stichnot): Statically choose the size based on the target being // TODO(stichnot): Statically choose the size based on the target being
...@@ -65,6 +71,7 @@ private: ...@@ -65,6 +71,7 @@ private:
const CfgVector<InstNumberT> &LREnd) const; const CfgVector<InstNumberT> &LREnd) const;
void initForGlobal(); void initForGlobal();
void initForInfOnly(); void initForInfOnly();
void initForSecondChance();
/// Move an item from the From set to the To set. From[Index] is pushed onto /// 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 /// 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 /// effectively swapping it with the last item in From[] and then popping it
...@@ -108,6 +115,7 @@ private: ...@@ -108,6 +115,7 @@ private:
/// faster processing. /// faster processing.
OrderedRanges UnhandledPrecolored; OrderedRanges UnhandledPrecolored;
UnorderedRanges Active, Inactive, Handled; UnorderedRanges Active, Inactive, Handled;
UnorderedRanges Evicted;
CfgVector<InstNumberT> Kills; CfgVector<InstNumberT> Kills;
RegAllocKind Kind = RAK_Unknown; RegAllocKind Kind = RAK_Unknown;
/// RegUses[I] is the number of live ranges (variables) that register I is /// RegUses[I] is the number of live ranges (variables) that register I is
......
...@@ -267,9 +267,15 @@ void TargetLowering::regAlloc(RegAllocKind Kind) { ...@@ -267,9 +267,15 @@ void TargetLowering::regAlloc(RegAllocKind Kind) {
RegInclude |= RegSet_CalleeSave; RegInclude |= RegSet_CalleeSave;
if (hasFramePointer()) if (hasFramePointer())
RegExclude |= RegSet_FramePointer; RegExclude |= RegSet_FramePointer;
LinearScan.init(Kind);
llvm::SmallBitVector RegMask = getRegisterSet(RegInclude, RegExclude); 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() { void TargetLowering::markRedefinitions() {
......
; Shows that the ARM integrated assembler can translate a trivial, ; Shows that the ARM integrated assembler can translate a trivial,
; bundle-aligned function. ; bundle-aligned function.
; REQUIRES: allow_dump
; RUN: %p2i --filetype=asm -i %s --target=arm32 \ ; RUN: %p2i --filetype=asm -i %s --target=arm32 \
; RUN: | FileCheck %s --check-prefix=ASM ; RUN: | FileCheck %s --check-prefix=ASM
; RUN: %p2i --filetype=iasm -i %s --target=arm32 \ ; RUN: %p2i --filetype=iasm -i %s --target=arm32 \
......
...@@ -172,11 +172,11 @@ next2: ...@@ -172,11 +172,11 @@ next2:
; CHECK-LABEL: test_local_forward_then_back ; CHECK-LABEL: test_local_forward_then_back
; CHECK: {{.*}} mov DWORD PTR ; CHECK: {{.*}} mov DWORD PTR
; CHECK-NEXT: {{.*}} mfence ; CHECK-NEXT: {{.*}} mfence
; CHECK-NEXT: 16: {{.*}} mov {{.*}},0x1 ; CHECK-NEXT: [[LABEL:[0-9a-f]+]]: {{.*}} mov {{.*}},0x1
; CHECK-NEXT: {{.*}} cmp ; CHECK-NEXT: {{.*}} cmp
; CHECK-NEXT: {{.*}} jb ; CHECK-NEXT: {{.*}} jb
; CHECK: {{.*}} jne ; CHECK: {{.*}} jne
; CHECK: {{.*}} jmp 16 ; CHECK: {{.*}} jmp [[LABEL]]
; Test that backward local branches also work and are small. ; 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