OSDN Git Service

Subzero: Implement "second-chance bin-packing" for register allocation.
authorJim Stichnoth <stichnot@chromium.org>
Fri, 9 Oct 2015 21:33:26 +0000 (14:33 -0700)
committerJim Stichnoth <stichnot@chromium.org>
Fri, 9 Oct 2015 21:33:26 +0000 (14:33 -0700)
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 .

src/IceClFlags.cpp
src/IceClFlags.h
src/IceDefs.h
src/IceRegAlloc.cpp
src/IceRegAlloc.h
src/IceTargetLowering.cpp
tests_lit/assembler/arm32/ret.ll
tests_lit/assembler/x86/jump_encodings.ll

index 5ed9ea0..8fd50c7 100644 (file)
@@ -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);
index 0aba214..d4d7737 100644 (file)
@@ -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;
index f58ebe9..d73f9e7 100644 (file)
@@ -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 {
index 4922848..ea15b1b 100644 (file)
@@ -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.
index d0aa111..8faacc7 100644 (file)
@@ -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
index 8d67e8b..aa1a80c 100644 (file)
@@ -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() {
index 27596c9..95fce97 100644 (file)
@@ -1,6 +1,8 @@
 ; 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 \
index e8c9e1d..c8ffdb3 100644 (file)
@@ -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.