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."),
OutFlags.PhiEdgeSplit = false;
OutFlags.RandomNopInsertion = false;
OutFlags.RandomRegAlloc = false;
+ OutFlags.RepeatRegAlloc = false;
OutFlags.ReorderBasicBlocks = false;
OutFlags.ReorderFunctions = false;
OutFlags.ReorderGlobalVariables = false;
OutFlags.setShouldReorderBasicBlocks(::ReorderBasicBlocks);
OutFlags.setShouldDoNopInsertion(::ShouldDoNopInsertion);
OutFlags.setShouldRandomizeRegAlloc(::RandomizeRegisterAllocation);
+ OutFlags.setShouldRepeatRegAlloc(::RepeatRegAlloc);
OutFlags.setShouldReorderFunctions(::ReorderFunctions);
OutFlags.setShouldReorderGlobalVariables(::ReorderGlobalVariables);
OutFlags.setShouldReorderPooledConstants(::ReorderPooledConstants);
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; }
bool PhiEdgeSplit;
bool RandomNopInsertion;
bool RandomRegAlloc;
+ bool RepeatRegAlloc;
bool ReorderBasicBlocks;
bool ReorderFunctions;
bool ReorderGlobalVariables;
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 {
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();
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();
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
assert(RegUses[RegNum] >= 0);
Item->setRegNumTmp(Variable::NoRegister);
moveItem(Active, Index, Handled);
+ Evicted.push_back(Item);
}
}
// Do the same for Inactive.
dumpLiveRangeTrace("Evicting I ", Item);
Item->setRegNumTmp(Variable::NoRegister);
moveItem(Inactive, Index, Handled);
+ Evicted.push_back(Item);
}
}
// Assign the register to Cur.
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
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
/// 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
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 \
; 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.