Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(81)

Unified Diff: src/IceRegAlloc.cpp

Issue 300563003: Subzero: Initial O2 lowering (Closed) Base URL: https://gerrit.chromium.org/gerrit/p/native_client/pnacl-subzero.git@master
Patch Set: Jan's third-round comments Created 6 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « src/IceRegAlloc.h ('k') | src/IceTargetLowering.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: src/IceRegAlloc.cpp
diff --git a/src/IceRegAlloc.cpp b/src/IceRegAlloc.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..f610d9668b6dc8711c642aaad2737c730207e880
--- /dev/null
+++ b/src/IceRegAlloc.cpp
@@ -0,0 +1,461 @@
+//===- subzero/src/IceRegAlloc.cpp - Linear-scan implementation -----------===//
+//
+// The Subzero Code Generator
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements the LinearScan class, which performs the
+// linear-scan register allocation after liveness analysis has been
+// performed.
+//
+//===----------------------------------------------------------------------===//
+
+#include "IceCfg.h"
+#include "IceInst.h"
+#include "IceOperand.h"
+#include "IceRegAlloc.h"
+#include "IceTargetLowering.h"
+
+namespace Ice {
+
+// Implements the linear-scan algorithm. Based on "Linear Scan
+// Register Allocation in the Context of SSA Form and Register
+// Constraints" by Hanspeter Mössenböck and Michael Pfeiffer,
+// ftp://ftp.ssw.uni-linz.ac.at/pub/Papers/Moe02.PDF . This
+// implementation is modified to take affinity into account and allow
+// two interfering variables to share the same register in certain
+// cases.
+//
+// Requires running Cfg::liveness(Liveness_RangesFull) in
+// preparation. Results are assigned to Variable::RegNum for each
+// Variable.
+void LinearScan::scan(const llvm::SmallBitVector &RegMaskFull) {
+ assert(RegMaskFull.any()); // Sanity check
+ Unhandled.clear();
+ Handled.clear();
+ Inactive.clear();
+ Active.clear();
+ Ostream &Str = Func->getContext()->getStrDump();
+ Func->setCurrentNode(NULL);
+
+ // Gather the live ranges of all variables and add them to the
+ // Unhandled set. TODO: Unhandled is a set<> which is based on a
+ // balanced binary tree, so inserting live ranges for N variables is
+ // O(N log N) complexity. N may be proportional to the number of
+ // instructions, thanks to temporary generation during lowering. As
+ // a result, it may be useful to design a better data structure for
+ // storing Func->getVariables().
+ const VarList &Vars = Func->getVariables();
+ for (VarList::const_iterator I = Vars.begin(), E = Vars.end(); I != E; ++I) {
+ Variable *Var = *I;
+ // Explicitly don't consider zero-weight variables, which are
+ // meant to be spill slots.
+ if (Var->getWeight() == RegWeight::Zero)
+ continue;
+ // Don't bother if the variable has a null live range, which means
+ // it was never referenced.
+ if (Var->getLiveRange().isEmpty())
+ continue;
+ Unhandled.insert(LiveRangeWrapper(Var));
+ if (Var->hasReg()) {
+ Var->setRegNumTmp(Var->getRegNum());
+ Var->setLiveRangeInfiniteWeight();
+ }
+ }
+
+ // RegUses[I] is the number of live ranges (variables) that register
+ // I is currently assigned to. It can be greater than 1 as a result
+ // of Variable::AllowRegisterOverlap.
+ std::vector<int> RegUses(RegMaskFull.size());
+ // Unhandled is already set to all ranges in increasing order of
+ // start points.
+ assert(Active.empty());
+ assert(Inactive.empty());
+ assert(Handled.empty());
+ UnorderedRanges::iterator Next;
+
+ while (!Unhandled.empty()) {
+ LiveRangeWrapper Cur = *Unhandled.begin();
+ Unhandled.erase(Unhandled.begin());
+ if (Func->getContext()->isVerbose(IceV_LinearScan)) {
+ Str << "\nConsidering ";
+ Cur.dump(Func);
+ Str << "\n";
+ }
+ const llvm::SmallBitVector RegMask =
+ RegMaskFull &
+ Func->getTarget()->getRegisterSetForType(Cur.Var->getType());
+
+ // Check for precolored ranges. If Cur is precolored, it
+ // definitely gets that register. Previously processed live
+ // ranges would have avoided that register due to it being
+ // precolored. Future processed live ranges won't evict that
+ // register because the live range has infinite weight.
+ if (Cur.Var->hasReg()) {
+ int32_t RegNum = Cur.Var->getRegNum();
+ // RegNumTmp should have already been set above.
+ assert(Cur.Var->getRegNumTmp() == RegNum);
+ if (Func->getContext()->isVerbose(IceV_LinearScan)) {
+ Str << "Precoloring ";
+ Cur.dump(Func);
+ Str << "\n";
+ }
+ Active.push_back(Cur);
+ assert(RegUses[RegNum] >= 0);
+ ++RegUses[RegNum];
+ continue;
+ }
+
+ // Check for active ranges that have expired or become inactive.
+ for (UnorderedRanges::iterator I = Active.begin(), E = Active.end(); I != E;
+ I = Next) {
+ Next = I;
+ ++Next;
+ LiveRangeWrapper Item = *I;
+ bool Moved = false;
+ if (Item.endsBefore(Cur)) {
+ // Move Item from Active to Handled list.
+ if (Func->getContext()->isVerbose(IceV_LinearScan)) {
+ Str << "Expiring ";
+ Item.dump(Func);
+ Str << "\n";
+ }
+ Active.erase(I);
+ Handled.push_back(Item);
+ Moved = true;
+ } else if (!Item.overlapsStart(Cur)) {
+ // Move Item from Active to Inactive list.
+ if (Func->getContext()->isVerbose(IceV_LinearScan)) {
+ Str << "Inactivating ";
+ Item.dump(Func);
+ Str << "\n";
+ }
+ Active.erase(I);
+ Inactive.push_back(Item);
+ Moved = true;
+ }
+ if (Moved) {
+ // Decrement Item from RegUses[].
+ assert(Item.Var->hasRegTmp());
+ int32_t RegNum = Item.Var->getRegNumTmp();
+ --RegUses[RegNum];
+ assert(RegUses[RegNum] >= 0);
+ }
+ }
+
+ // Check for inactive ranges that have expired or reactivated.
+ for (UnorderedRanges::iterator I = Inactive.begin(), E = Inactive.end();
+ I != E; I = Next) {
+ Next = I;
+ ++Next;
+ LiveRangeWrapper Item = *I;
+ if (Item.endsBefore(Cur)) {
+ // Move Item from Inactive to Handled list.
+ if (Func->getContext()->isVerbose(IceV_LinearScan)) {
+ Str << "Expiring ";
+ Item.dump(Func);
+ Str << "\n";
+ }
+ Inactive.erase(I);
+ Handled.push_back(Item);
+ } else if (Item.overlapsStart(Cur)) {
+ // Move Item from Inactive to Active list.
+ if (Func->getContext()->isVerbose(IceV_LinearScan)) {
+ Str << "Reactivating ";
+ Item.dump(Func);
+ Str << "\n";
+ }
+ Inactive.erase(I);
+ Active.push_back(Item);
+ // Increment Item in RegUses[].
+ assert(Item.Var->hasRegTmp());
+ int32_t RegNum = Item.Var->getRegNumTmp();
+ assert(RegUses[RegNum] >= 0);
+ ++RegUses[RegNum];
+ }
+ }
+
+ // Calculate available registers into Free[].
+ llvm::SmallBitVector Free = RegMask;
+ for (SizeT i = 0; i < RegMask.size(); ++i) {
+ if (RegUses[i] > 0)
+ Free[i] = false;
+ }
+
+ // Remove registers from the Free[] list where an Inactive range
+ // overlaps with the current range.
+ for (UnorderedRanges::const_iterator I = Inactive.begin(),
+ E = Inactive.end();
+ I != E; ++I) {
+ LiveRangeWrapper Item = *I;
+ if (Item.overlaps(Cur)) {
+ int32_t RegNum = Item.Var->getRegNumTmp();
+ // Don't assert(Free[RegNum]) because in theory (though
+ // probably never in practice) there could be two inactive
+ // variables that were allowed marked with
+ // AllowRegisterOverlap.
+ Free[RegNum] = false;
+ }
+ }
+
+ // Remove registers from the Free[] list where an Unhandled range
+ // overlaps with the current range and is precolored.
+ // Cur.endsBefore(*I) is an early exit check that turns a
+ // guaranteed O(N^2) algorithm into expected linear complexity.
+ for (OrderedRanges::const_iterator I = Unhandled.begin(),
+ E = Unhandled.end();
+ I != E && !Cur.endsBefore(*I); ++I) {
+ LiveRangeWrapper Item = *I;
+ if (Item.Var->hasReg() && Item.overlaps(Cur)) {
+ Free[Item.Var->getRegNum()] = false; // Note: getRegNum not getRegNumTmp
+ }
+ }
+
+ // Print info about physical register availability.
+ if (Func->getContext()->isVerbose(IceV_LinearScan)) {
+ for (SizeT i = 0; i < RegMask.size(); ++i) {
+ if (RegMask[i]) {
+ Str << Func->getTarget()->getRegName(i, IceType_i32)
+ << "(U=" << RegUses[i] << ",F=" << Free[i] << ") ";
+ }
+ }
+ Str << "\n";
+ }
+
+ Variable *Prefer = Cur.Var->getPreferredRegister();
+ int32_t PreferReg = Prefer && Prefer->hasRegTmp() ? Prefer->getRegNumTmp()
+ : Variable::NoRegister;
+ if (PreferReg != Variable::NoRegister &&
+ (Cur.Var->getRegisterOverlap() || Free[PreferReg])) {
+ // First choice: a preferred register that is either free or is
+ // allowed to overlap with its linked variable.
+ Cur.Var->setRegNumTmp(PreferReg);
+ if (Func->getContext()->isVerbose(IceV_LinearScan)) {
+ Str << "Preferring ";
+ Cur.dump(Func);
+ Str << "\n";
+ }
+ assert(RegUses[PreferReg] >= 0);
+ ++RegUses[PreferReg];
+ Active.push_back(Cur);
+ } else if (Free.any()) {
+ // Second choice: any free register. TODO: After explicit
+ // affinity is considered, is there a strategy better than just
+ // picking the lowest-numbered available register?
+ int32_t RegNum = Free.find_first();
+ Cur.Var->setRegNumTmp(RegNum);
+ if (Func->getContext()->isVerbose(IceV_LinearScan)) {
+ Str << "Allocating ";
+ Cur.dump(Func);
+ Str << "\n";
+ }
+ assert(RegUses[RegNum] >= 0);
+ ++RegUses[RegNum];
+ Active.push_back(Cur);
+ } else {
+ // Fallback: there are no free registers, so we look for the
+ // lowest-weight register and see if Cur has higher weight.
+ std::vector<RegWeight> Weights(RegMask.size());
+ // Check Active ranges.
+ for (UnorderedRanges::const_iterator I = Active.begin(), E = Active.end();
+ I != E; ++I) {
+ LiveRangeWrapper Item = *I;
+ assert(Item.overlaps(Cur));
+ int32_t RegNum = Item.Var->getRegNumTmp();
+ assert(Item.Var->hasRegTmp());
+ Weights[RegNum].addWeight(Item.range().getWeight());
+ }
+ // Same as above, but check Inactive ranges instead of Active.
+ for (UnorderedRanges::const_iterator I = Inactive.begin(),
+ E = Inactive.end();
+ I != E; ++I) {
+ LiveRangeWrapper Item = *I;
+ int32_t RegNum = Item.Var->getRegNumTmp();
+ assert(Item.Var->hasRegTmp());
+ if (Item.overlaps(Cur))
+ Weights[RegNum].addWeight(Item.range().getWeight());
+ }
+ // Check Unhandled ranges that overlap Cur and are precolored.
+ // Cur.endsBefore(*I) is an early exit check that turns a
+ // guaranteed O(N^2) algorithm into expected linear complexity.
+ for (OrderedRanges::const_iterator I = Unhandled.begin(),
+ E = Unhandled.end();
+ I != E && !Cur.endsBefore(*I); ++I) {
+ LiveRangeWrapper Item = *I;
+ int32_t RegNum = Item.Var->getRegNumTmp();
+ if (RegNum < 0)
+ continue;
+ if (Item.overlaps(Cur))
+ Weights[RegNum].setWeight(RegWeight::Inf);
+ }
+
+ // All the weights are now calculated. Find the register with
+ // smallest weight.
+ int32_t MinWeightIndex = RegMask.find_first();
+ // MinWeightIndex must be valid because of the initial
+ // RegMask.any() test.
+ assert(MinWeightIndex >= 0);
+ for (SizeT i = MinWeightIndex + 1; i < Weights.size(); ++i) {
+ if (RegMask[i] && Weights[i] < Weights[MinWeightIndex])
+ MinWeightIndex = i;
+ }
+
+ if (Cur.range().getWeight() <= Weights[MinWeightIndex]) {
+ // Cur doesn't have priority over any other live ranges, so
+ // don't allocate any register to it, and move it to the
+ // Handled state.
+ Handled.push_back(Cur);
+ if (Cur.range().getWeight().isInf()) {
+ Func->setError("Unable to find a physical register for an "
+ "infinite-weight live range");
+ }
+ } else {
+ // Evict all live ranges in Active that register number
+ // MinWeightIndex is assigned to.
+ for (UnorderedRanges::iterator I = Active.begin(), E = Active.end();
+ I != E; I = Next) {
+ Next = I;
+ ++Next;
+ LiveRangeWrapper Item = *I;
+ if (Item.Var->getRegNumTmp() == MinWeightIndex) {
+ if (Func->getContext()->isVerbose(IceV_LinearScan)) {
+ Str << "Evicting ";
+ Item.dump(Func);
+ Str << "\n";
+ }
+ --RegUses[MinWeightIndex];
+ assert(RegUses[MinWeightIndex] >= 0);
+ Item.Var->setRegNumTmp(Variable::NoRegister);
+ Active.erase(I);
+ Handled.push_back(Item);
+ }
+ }
+ // Do the same for Inactive.
+ for (UnorderedRanges::iterator I = Inactive.begin(), E = Inactive.end();
+ I != E; I = Next) {
+ Next = I;
+ ++Next;
+ LiveRangeWrapper Item = *I;
+ if (Item.Var->getRegNumTmp() == MinWeightIndex) {
+ if (Func->getContext()->isVerbose(IceV_LinearScan)) {
+ Str << "Evicting ";
+ Item.dump(Func);
+ Str << "\n";
+ }
+ Item.Var->setRegNumTmp(Variable::NoRegister);
+ Inactive.erase(I);
+ Handled.push_back(Item);
+ }
+ }
+ // Assign the register to Cur.
+ Cur.Var->setRegNumTmp(MinWeightIndex);
+ assert(RegUses[MinWeightIndex] >= 0);
+ ++RegUses[MinWeightIndex];
+ Active.push_back(Cur);
+ if (Func->getContext()->isVerbose(IceV_LinearScan)) {
+ Str << "Allocating ";
+ Cur.dump(Func);
+ Str << "\n";
+ }
+ }
+ }
+ dump(Func);
+ }
+ // Move anything Active or Inactive to Handled for easier handling.
+ for (UnorderedRanges::iterator I = Active.begin(), E = Active.end(); I != E;
+ I = Next) {
+ Next = I;
+ ++Next;
+ Handled.push_back(*I);
+ Active.erase(I);
+ }
+ for (UnorderedRanges::iterator I = Inactive.begin(), E = Inactive.end();
+ I != E; I = Next) {
+ Next = I;
+ ++Next;
+ Handled.push_back(*I);
+ Inactive.erase(I);
+ }
+ dump(Func);
+
+ // Finish up by assigning RegNumTmp->RegNum for each Variable.
+ for (UnorderedRanges::const_iterator I = Handled.begin(), E = Handled.end();
+ I != E; ++I) {
+ LiveRangeWrapper Item = *I;
+ int32_t RegNum = Item.Var->getRegNumTmp();
+ if (Func->getContext()->isVerbose(IceV_LinearScan)) {
+ if (!Item.Var->hasRegTmp()) {
+ Str << "Not assigning ";
+ Item.Var->dump(Func);
+ Str << "\n";
+ } else {
+ Str << (RegNum == Item.Var->getRegNum() ? "Reassigning " : "Assigning ")
+ << Func->getTarget()->getRegName(RegNum, IceType_i32) << "(r"
+ << RegNum << ") to ";
+ Item.Var->dump(Func);
+ Str << "\n";
+ }
+ }
+ Item.Var->setRegNum(Item.Var->getRegNumTmp());
+ }
+
+ // TODO: Consider running register allocation one more time, with
+ // infinite registers, for two reasons. First, evicted live ranges
+ // get a second chance for a register. Second, it allows coalescing
+ // of stack slots. If there is no time budget for the second
+ // register allocation run, each unallocated variable just gets its
+ // own slot.
+ //
+ // Another idea for coalescing stack slots is to initialize the
+ // Unhandled list with just the unallocated variables, saving time
+ // but not offering second-chance opportunities.
+}
+
+// ======================== Dump routines ======================== //
+
+void LiveRangeWrapper::dump(const Cfg *Func) const {
+ Ostream &Str = Func->getContext()->getStrDump();
+ const static size_t BufLen = 30;
+ char buf[BufLen];
+ snprintf(buf, BufLen, "%2d", Var->getRegNumTmp());
+ Str << "R=" << buf << " V=";
+ Var->dump(Func);
+ Str << " Range=" << range();
+}
+
+void LinearScan::dump(Cfg *Func) const {
+ Ostream &Str = Func->getContext()->getStrDump();
+ if (!Func->getContext()->isVerbose(IceV_LinearScan))
+ return;
+ Func->setCurrentNode(NULL);
+ Str << "**** Current regalloc state:\n";
+ Str << "++++++ Handled:\n";
+ for (UnorderedRanges::const_iterator I = Handled.begin(), E = Handled.end();
+ I != E; ++I) {
+ I->dump(Func);
+ Str << "\n";
+ }
+ Str << "++++++ Unhandled:\n";
+ for (OrderedRanges::const_iterator I = Unhandled.begin(), E = Unhandled.end();
+ I != E; ++I) {
+ I->dump(Func);
+ Str << "\n";
+ }
+ Str << "++++++ Active:\n";
+ for (UnorderedRanges::const_iterator I = Active.begin(), E = Active.end();
+ I != E; ++I) {
+ I->dump(Func);
+ Str << "\n";
+ }
+ Str << "++++++ Inactive:\n";
+ for (UnorderedRanges::const_iterator I = Inactive.begin(), E = Inactive.end();
+ I != E; ++I) {
+ I->dump(Func);
+ Str << "\n";
+ }
+}
+
+} // end of namespace Ice
« no previous file with comments | « src/IceRegAlloc.h ('k') | src/IceTargetLowering.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698