Bug 1027897 - IonMonkey: More regalloc dumping improvements r=bhackett
authorDan Gohman <sunfish@mozilla.com>
Mon, 23 Jun 2014 13:42:07 -0700
changeset 190336 c96c8e88e5cf63026d3c8a4c14070d276f582633
parent 190335 359ba79e9b187abe4daf3836f16264440471d8d9
child 190337 a965be2731d43676863a57e994d66275f7d0a4fa
push id27004
push useremorley@mozilla.com
push dateTue, 24 Jun 2014 15:52:34 +0000
treeherdermozilla-central@7b174d47f3cc [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbhackett
bugs1027897
milestone33.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1027897 - IonMonkey: More regalloc dumping improvements r=bhackett
js/src/jit/BacktrackingAllocator.cpp
js/src/jit/BacktrackingAllocator.h
js/src/jit/LIR.cpp
js/src/jit/LIR.h
js/src/jit/LinearScan.cpp
js/src/jit/LiveRangeAllocator.cpp
js/src/jit/LiveRangeAllocator.h
js/src/jit/RegisterAllocator.cpp
js/src/jit/RegisterAllocator.h
--- a/js/src/jit/BacktrackingAllocator.cpp
+++ b/js/src/jit/BacktrackingAllocator.cpp
@@ -75,45 +75,47 @@ BacktrackingAllocator::init()
     return true;
 }
 
 bool
 BacktrackingAllocator::go()
 {
     IonSpew(IonSpew_RegAlloc, "Beginning register allocation");
 
-    IonSpew(IonSpew_RegAlloc, "Beginning liveness analysis");
     if (!buildLivenessInfo())
         return false;
-    IonSpew(IonSpew_RegAlloc, "Liveness analysis complete");
 
     if (!init())
         return false;
 
     if (IonSpewEnabled(IonSpew_RegAlloc))
-        dumpLiveness();
+        dumpFixedRanges();
 
     if (!allocationQueue.reserve(graph.numVirtualRegisters() * 3 / 2))
         return false;
 
+    IonSpew(IonSpew_RegAlloc, "Beginning grouping and queueing registers");
     if (!groupAndQueueRegisters())
         return false;
+    IonSpew(IonSpew_RegAlloc, "Grouping and queueing registers complete");
 
     if (IonSpewEnabled(IonSpew_RegAlloc))
         dumpRegisterGroups();
 
+    IonSpew(IonSpew_RegAlloc, "Beginning main allocation loop");
     // Allocate, spill and split register intervals until finished.
     while (!allocationQueue.empty()) {
         if (mir->shouldCancel("Backtracking Allocation"))
             return false;
 
         QueueItem item = allocationQueue.removeHighest();
         if (item.interval ? !processInterval(item.interval) : !processGroup(item.group))
             return false;
     }
+    IonSpew(IonSpew_RegAlloc, "Main allocation loop complete");
 
     if (IonSpewEnabled(IonSpew_RegAlloc))
         dumpAllocations();
 
     return resolveControlFlow() && reifyAllocations() && populateSafepoints();
 }
 
 static bool
@@ -447,19 +449,18 @@ BacktrackingAllocator::tryAllocateNonFix
     JS_ASSERT(!*success);
     return true;
 }
 
 bool
 BacktrackingAllocator::processInterval(LiveInterval *interval)
 {
     if (IonSpewEnabled(IonSpew_RegAlloc)) {
-        IonSpew(IonSpew_RegAlloc, "Allocating v%u [priority %lu] [weight %lu]: %s",
-                interval->vreg(), computePriority(interval), computeSpillWeight(interval),
-                interval->rangesToString());
+        IonSpew(IonSpew_RegAlloc, "Allocating %s [priority %lu] [weight %lu]",
+                interval->toString(), computePriority(interval), computeSpillWeight(interval));
     }
 
     // An interval can be processed by doing any of the following:
     //
     // - Assigning the interval a register. The interval cannot overlap any
     //   other interval allocated for that physical register.
     //
     // - Spilling the interval, provided it has no register uses.
@@ -709,27 +710,28 @@ BacktrackingAllocator::tryAllocateRegist
     JS_ASSERT_IF(interval->requirement()->kind() == Requirement::FIXED,
                  interval->requirement()->allocation() == LAllocation(r.reg));
 
     for (size_t i = 0; i < interval->numRanges(); i++) {
         AllocatedRange range(interval, interval->getRange(i)), existing;
         if (r.allocations.contains(range, &existing)) {
             if (existing.interval->hasVreg()) {
                 if (IonSpewEnabled(IonSpew_RegAlloc)) {
-                    IonSpew(IonSpew_RegAlloc, "  %s collides with v%u [%u,%u> [weight %lu]",
+                    IonSpew(IonSpew_RegAlloc, "  %s collides with v%u[%u] %s [weight %lu]",
                             r.reg.name(), existing.interval->vreg(),
-                            existing.range->from.pos(), existing.range->to.pos(),
+                            existing.interval->index(),
+                            existing.range->toString(),
                             computeSpillWeight(existing.interval));
                 }
                 if (!*pconflicting || computeSpillWeight(existing.interval) < computeSpillWeight(*pconflicting))
                     *pconflicting = existing.interval;
             } else {
                 if (IonSpewEnabled(IonSpew_RegAlloc)) {
-                    IonSpew(IonSpew_RegAlloc, "  %s collides with fixed use [%u,%u>",
-                            r.reg.name(), existing.range->from.pos(), existing.range->to.pos());
+                    IonSpew(IonSpew_RegAlloc, "  %s collides with fixed use %s",
+                            r.reg.name(), existing.range->toString());
                 }
                 *pfixed = true;
             }
             return true;
         }
     }
 
     IonSpew(IonSpew_RegAlloc, "  allocated to %s", r.reg.name());
@@ -750,18 +752,18 @@ BacktrackingAllocator::tryAllocateRegist
     *success = true;
     return true;
 }
 
 bool
 BacktrackingAllocator::evictInterval(LiveInterval *interval)
 {
     if (IonSpewEnabled(IonSpew_RegAlloc)) {
-        IonSpew(IonSpew_RegAlloc, "  Evicting interval v%u: %s",
-                interval->vreg(), interval->rangesToString());
+        IonSpew(IonSpew_RegAlloc, "  Evicting %s [priority %lu] [weight %lu]",
+                interval->toString(), computePriority(interval), computeSpillWeight(interval));
     }
 
     JS_ASSERT(interval->getAllocation()->isRegister());
 
     AnyRegister reg(interval->getAllocation()->toRegister());
     PhysicalRegister &physical = registers[reg.code()];
     JS_ASSERT(physical.reg == reg && physical.allocatable);
 
@@ -803,20 +805,22 @@ BacktrackingAllocator::distributeUses(Li
     }
 }
 
 bool
 BacktrackingAllocator::split(LiveInterval *interval,
                              const LiveIntervalVector &newIntervals)
 {
     if (IonSpewEnabled(IonSpew_RegAlloc)) {
-        IonSpew(IonSpew_RegAlloc, "    splitting interval v%u %s into:",
-                interval->vreg(), interval->rangesToString());
-        for (size_t i = 0; i < newIntervals.length(); i++)
-            IonSpew(IonSpew_RegAlloc, "      %s", newIntervals[i]->rangesToString());
+        IonSpew(IonSpew_RegAlloc, "    splitting interval %s into:", interval->toString());
+        for (size_t i = 0; i < newIntervals.length(); i++) {
+            IonSpew(IonSpew_RegAlloc, "      %s", newIntervals[i]->toString());
+            JS_ASSERT(newIntervals[i]->start() >= interval->start());
+            JS_ASSERT(newIntervals[i]->end() <= interval->end());
+        }
     }
 
     JS_ASSERT(newIntervals.length() >= 2);
 
     // Find the earliest interval in the new list.
     LiveInterval *first = newIntervals[0];
     for (size_t i = 1; i < newIntervals.length(); i++) {
         if (newIntervals[i]->start() < first->start())
@@ -894,16 +898,18 @@ BacktrackingAllocator::spill(LiveInterva
     }
 }
 
 // Add moves to resolve conflicting assignments between a block and its
 // predecessors. XXX try to common this with LinearScanAllocator.
 bool
 BacktrackingAllocator::resolveControlFlow()
 {
+    IonSpew(IonSpew_RegAlloc, "Resolving control flow (vreg loop)");
+
     // Virtual register number 0 is unused.
     JS_ASSERT(vregs[0u].numIntervals() == 0);
     for (size_t i = 1; i < graph.numVirtualRegisters(); i++) {
         BacktrackingVirtualRegister *reg = &vregs[i];
 
         if (mir->shouldCancel("Backtracking Resolve Control Flow (vreg loop)"))
             return false;
 
@@ -936,16 +942,18 @@ BacktrackingAllocator::resolveControlFlo
                 } else {
                     if (!moveAfter(outputOf(data->ins()), prevInterval, interval, reg->type()))
                         return false;
                 }
             }
         }
     }
 
+    IonSpew(IonSpew_RegAlloc, "Resolving control flow (block loop)");
+
     for (size_t i = 0; i < graph.numBlocks(); i++) {
         if (mir->shouldCancel("Backtracking Resolve Control Flow (block loop)"))
             return false;
 
         LBlock *successor = graph.getBlock(i);
         MBasicBlock *mSuccessor = successor->mir();
         if (mSuccessor->numPredecessors() < 1)
             continue;
@@ -1191,147 +1199,98 @@ BacktrackingAllocator::populateSafepoint
     }
 
     return true;
 }
 
 void
 BacktrackingAllocator::dumpRegisterGroups()
 {
-    fprintf(stderr, "Register groups:\n");
+#ifdef DEBUG
+    bool any = false;
 
     // Virtual register number 0 is unused.
     JS_ASSERT(!vregs[0u].group());
     for (size_t i = 1; i < graph.numVirtualRegisters(); i++) {
         VirtualRegisterGroup *group = vregs[i].group();
         if (group && i == group->canonicalReg()) {
+            if (!any) {
+                fprintf(stderr, "Register groups:\n");
+                any = true;
+            }
+            fprintf(stderr, " ");
             for (size_t j = 0; j < group->registers.length(); j++)
                 fprintf(stderr, " v%u", group->registers[j]);
             fprintf(stderr, "\n");
         }
     }
+    if (any)
+        fprintf(stderr, "\n");
+#endif
 }
 
 void
-BacktrackingAllocator::dumpLiveness()
+BacktrackingAllocator::dumpFixedRanges()
 {
 #ifdef DEBUG
-    fprintf(stderr, "Virtual Registers:\n");
-
-    for (size_t blockIndex = 0; blockIndex < graph.numBlocks(); blockIndex++) {
-        LBlock *block = graph.getBlock(blockIndex);
-        MBasicBlock *mir = block->mir();
-
-        fprintf(stderr, "\nBlock %lu", static_cast<unsigned long>(blockIndex));
-        for (size_t i = 0; i < mir->numSuccessors(); i++)
-            fprintf(stderr, " [successor %u]", mir->getSuccessor(i)->id());
-        fprintf(stderr, "\n");
-
-        for (size_t i = 0; i < block->numPhis(); i++) {
-            LPhi *phi = block->getPhi(i);
+    bool any = false;
 
-            // Don't print the inputOf for phi nodes, since it's never used.
-            fprintf(stderr, "[,%u Phi [def v%u] <-",
-                    outputOf(phi).pos(),
-                    phi->getDef(0)->virtualRegister());
-            for (size_t j = 0; j < phi->numOperands(); j++)
-                fprintf(stderr, " %s", phi->getOperand(j)->toString());
-            fprintf(stderr, "]\n");
-        }
-
-        for (LInstructionIterator iter = block->begin(); iter != block->end(); iter++) {
-            LInstruction *ins = *iter;
-
-            fprintf(stderr, "[%u,%u %s]", inputOf(ins).pos(), outputOf(ins).pos(), ins->opName());
-
-            for (size_t i = 0; i < ins->numTemps(); i++) {
-                LDefinition *temp = ins->getTemp(i);
-                if (!temp->isBogusTemp())
-                    fprintf(stderr, " [temp v%u]", temp->virtualRegister());
+    for (size_t i = 0; i < AnyRegister::Total; i++) {
+        if (registers[i].allocatable && fixedIntervals[i]->numRanges() != 0) {
+            if (!any) {
+                fprintf(stderr, "Live ranges by physical register:\n");
+                any = true;
             }
-
-            for (size_t i = 0; i < ins->numDefs(); i++) {
-                LDefinition *def = ins->getDef(i);
-                fprintf(stderr, " [def v%u]", def->virtualRegister());
-            }
-
-            for (LInstruction::InputIterator alloc(*ins); alloc.more(); alloc.next())
-                fprintf(stderr, " [use %s]", alloc->toString());
-
-            fprintf(stderr, "\n");
+            fprintf(stderr, "  %s: %s\n", AnyRegister::FromCode(i).name(), fixedIntervals[i]->toString());
         }
     }
 
-    fprintf(stderr, "\nLive Ranges:\n\n");
-
-    for (size_t i = 0; i < AnyRegister::Total; i++)
-        if (registers[i].allocatable && fixedIntervals[i]->numRanges() != 0)
-            fprintf(stderr, "reg %s: %s\n", AnyRegister::FromCode(i).name(), fixedIntervals[i]->rangesToString());
-
-    // Virtual register number 0 is unused.
-    JS_ASSERT(vregs[0u].numIntervals() == 0);
-    for (size_t i = 1; i < graph.numVirtualRegisters(); i++) {
-        fprintf(stderr, "v%lu:", static_cast<unsigned long>(i));
-        VirtualRegister &vreg = vregs[i];
-        for (size_t j = 0; j < vreg.numIntervals(); j++) {
-            if (j)
-                fprintf(stderr, " /");
-            fprintf(stderr, "%s", vreg.getInterval(j)->rangesToString());
-        }
+    if (any)
         fprintf(stderr, "\n");
-    }
-
-    fprintf(stderr, "\n");
 #endif // DEBUG
 }
 
 #ifdef DEBUG
 struct BacktrackingAllocator::PrintLiveIntervalRange
 {
+    bool &first_;
+
+    explicit PrintLiveIntervalRange(bool first) : first_(first) {}
+
     void operator()(const AllocatedRange &item)
     {
         if (item.range == item.interval->getRange(0)) {
+            if (first_)
+                first_ = false;
+            else
+                fprintf(stderr, " /");
             if (item.interval->hasVreg())
-                fprintf(stderr, "  v%u: %s\n",
-                       item.interval->vreg(),
-                       item.interval->rangesToString());
-            else
-                fprintf(stderr, "  fixed: %s\n",
-                       item.interval->rangesToString());
+                fprintf(stderr, " v%u[%u]", item.interval->vreg(), item.interval->index());
+            fprintf(stderr, "%s", item.interval->rangesToString());
         }
     }
 };
 #endif
 
 void
 BacktrackingAllocator::dumpAllocations()
 {
 #ifdef DEBUG
-    fprintf(stderr, "Allocations:\n");
+    fprintf(stderr, "Allocations by virtual register:\n");
 
-    // Virtual register number 0 is unused.
-    JS_ASSERT(vregs[0u].numIntervals() == 0);
-    for (size_t i = 1; i < graph.numVirtualRegisters(); i++) {
-        fprintf(stderr, "v%lu:", static_cast<unsigned long>(i));
-        VirtualRegister &vreg = vregs[i];
-        for (size_t j = 0; j < vreg.numIntervals(); j++) {
-            if (j)
-                fprintf(stderr, " /");
-            LiveInterval *interval = vreg.getInterval(j);
-            fprintf(stderr, "%s in %s", interval->rangesToString(), interval->getAllocation()->toString());
-        }
-        fprintf(stderr, "\n");
-    }
+    dumpVregs();
 
-    fprintf(stderr, "\n");
+    fprintf(stderr, "Allocations by physical register:\n");
 
     for (size_t i = 0; i < AnyRegister::Total; i++) {
         if (registers[i].allocatable && !registers[i].allocations.empty()) {
-            fprintf(stderr, "reg %s:\n", AnyRegister::FromCode(i).name());
-            registers[i].allocations.forEach(PrintLiveIntervalRange());
+            fprintf(stderr, "  %s:", AnyRegister::FromCode(i).name());
+            bool first = true;
+            registers[i].allocations.forEach(PrintLiveIntervalRange(first));
+            fprintf(stderr, "\n");
         }
     }
 
     fprintf(stderr, "\n");
 #endif // DEBUG
 }
 
 bool
@@ -1527,18 +1486,17 @@ BacktrackingAllocator::trySplitAcrossHot
             break;
         }
     }
     if (!coldCode) {
         IonSpew(IonSpew_RegAlloc, "  interval does not contain cold code");
         return true;
     }
 
-    IonSpew(IonSpew_RegAlloc, "  split across hot range [%u,%u>",
-            hotRange->from.pos(), hotRange->to.pos());
+    IonSpew(IonSpew_RegAlloc, "  split across hot range %s", hotRange->toString());
 
     SplitPositionVector splitPositions;
     if (!splitPositions.append(hotRange->from) || !splitPositions.append(hotRange->to))
         return false;
     *success = true;
     return splitAt(interval, splitPositions);
 }
 
--- a/js/src/jit/BacktrackingAllocator.h
+++ b/js/src/jit/BacktrackingAllocator.h
@@ -214,17 +214,17 @@ class BacktrackingAllocator
                          LiveInterval *spillInterval,
                          CodePosition from, CodePosition to);
 
     bool resolveControlFlow();
     bool reifyAllocations();
     bool populateSafepoints();
 
     void dumpRegisterGroups();
-    void dumpLiveness();
+    void dumpFixedRanges();
     void dumpAllocations();
 
     struct PrintLiveIntervalRange;
 
     bool minimalDef(const LiveInterval *interval, LInstruction *ins);
     bool minimalUse(const LiveInterval *interval, LInstruction *ins);
     bool minimalInterval(const LiveInterval *interval, bool *pfixed = nullptr);
 
--- a/js/src/jit/LIR.cpp
+++ b/js/src/jit/LIR.cpp
@@ -329,16 +329,17 @@ LInstruction::printName(FILE *fp, Opcode
 }
 
 void
 LInstruction::printName(FILE *fp)
 {
     printName(fp, op());
 }
 
+#ifdef DEBUG
 static const char * const TypeChars[] =
 {
     "g",            // GENERAL
     "i",            // INT32
     "o",            // OBJECT
     "s",            // SLOTS
     "f",            // FLOAT32
     "d",            // DOUBLE
@@ -346,32 +347,43 @@ static const char * const TypeChars[] =
     "t",            // TYPE
     "p"             // PAYLOAD
 #elif JS_PUNBOX64
     "x"             // BOX
 #endif
 };
 
 static void
-PrintDefinition(FILE *fp, const LDefinition &def)
+PrintDefinition(char *buf, size_t size, const LDefinition &def)
 {
-    fprintf(fp, "[%s", TypeChars[def.type()]);
+    char *cursor = buf;
+    char *end = buf + size;
+
     if (def.virtualRegister())
-        fprintf(fp, ":%d", def.virtualRegister());
-    if (def.policy() == LDefinition::PRESET) {
-        fprintf(fp, " (%s)", def.output()->toString());
-    } else if (def.policy() == LDefinition::MUST_REUSE_INPUT) {
-        fprintf(fp, " (!)");
-    } else if (def.policy() == LDefinition::PASSTHROUGH) {
-        fprintf(fp, " (-)");
-    }
-    fprintf(fp, "]");
+        cursor += JS_snprintf(cursor, end - cursor, "v%u", def.virtualRegister());
+
+    cursor += JS_snprintf(cursor, end - cursor, "<%s>", TypeChars[def.type()]);
+
+    if (def.policy() == LDefinition::PRESET)
+        cursor += JS_snprintf(cursor, end - cursor, ":%s", def.output()->toString());
+    else if (def.policy() == LDefinition::MUST_REUSE_INPUT)
+        cursor += JS_snprintf(cursor, end - cursor, ":tied(%u)", def.getReusedInput());
+    else if (def.policy() == LDefinition::PASSTHROUGH)
+        cursor += JS_snprintf(cursor, end - cursor, ":-");
 }
 
-#ifdef DEBUG
+const char *
+LDefinition::toString() const
+{
+    // Not reentrant!
+    static char buf[40];
+    PrintDefinition(buf, sizeof(buf), *this);
+    return buf;
+}
+
 static void
 PrintUse(char *buf, size_t size, const LUse *use)
 {
     switch (use->policy()) {
       case LUse::REGISTER:
         JS_snprintf(buf, size, "v%d:r", use->virtualRegister());
         break;
       case LUse::FIXED:
@@ -401,20 +413,20 @@ LAllocation::toString() const
     // Not reentrant!
     static char buf[40];
 
     switch (kind()) {
       case LAllocation::CONSTANT_VALUE:
       case LAllocation::CONSTANT_INDEX:
         return "c";
       case LAllocation::GPR:
-        JS_snprintf(buf, sizeof(buf), "=%s", toGeneralReg()->reg().name());
+        JS_snprintf(buf, sizeof(buf), "%s", toGeneralReg()->reg().name());
         return buf;
       case LAllocation::FPU:
-        JS_snprintf(buf, sizeof(buf), "=%s", toFloatReg()->reg().name());
+        JS_snprintf(buf, sizeof(buf), "%s", toFloatReg()->reg().name());
         return buf;
       case LAllocation::STACK_SLOT:
         JS_snprintf(buf, sizeof(buf), "stack:%d", toStackSlot()->slot());
         return buf;
       case LAllocation::ARGUMENT_SLOT:
         JS_snprintf(buf, sizeof(buf), "arg:%d", toArgument()->index());
         return buf;
       case LAllocation::USE:
@@ -428,16 +440,22 @@ LAllocation::toString() const
 
 void
 LAllocation::dump() const
 {
     fprintf(stderr, "%s\n", toString());
 }
 
 void
+LDefinition::dump() const
+{
+    fprintf(stderr, "%s\n", toString());
+}
+
+void
 LInstruction::printOperands(FILE *fp)
 {
     for (size_t i = 0, e = numOperands(); i < e; i++) {
         fprintf(fp, " (%s)", getOperand(i)->toString());
         if (i != numOperands() - 1)
             fprintf(fp, ",");
     }
 }
@@ -460,30 +478,30 @@ LInstruction::assignSnapshot(LSnapshot *
 }
 
 void
 LInstruction::dump(FILE *fp)
 {
     if (numDefs() != 0) {
         fprintf(fp, "{");
         for (size_t i = 0; i < numDefs(); i++) {
-            PrintDefinition(fp, *getDef(i));
+            fprintf(fp, "%s", getDef(i)->toString());
             if (i != numDefs() - 1)
                 fprintf(fp, ", ");
         }
         fprintf(fp, "} <- ");
     }
 
     printName(fp);
     printInfo(fp);
 
     if (numTemps()) {
         fprintf(fp, " t=(");
         for (size_t i = 0; i < numTemps(); i++) {
-            PrintDefinition(fp, *getTemp(i));
+            fprintf(fp, "%s", getTemp(i)->toString());
             if (i != numTemps() - 1)
                 fprintf(fp, ", ");
         }
         fprintf(fp, ")");
     }
 
     if (numSuccessors()) {
         fprintf(fp, " s=(");
@@ -550,14 +568,14 @@ LMoveGroup::addAfter(LAllocation *from, 
 }
 
 void
 LMoveGroup::printOperands(FILE *fp)
 {
     for (size_t i = 0; i < numMoves(); i++) {
         const LMove &move = getMove(i);
         // Use two printfs, as LAllocation::toString is not reentrant.
-        fprintf(fp, "[%s", move.from()->toString());
+        fprintf(fp, " [%s", move.from()->toString());
         fprintf(fp, " -> %s]", move.to()->toString());
         if (i != numMoves() - 1)
-            fprintf(fp, ", ");
+            fprintf(fp, ",");
     }
 }
--- a/js/src/jit/LIR.h
+++ b/js/src/jit/LIR.h
@@ -138,19 +138,16 @@ class LAllocation : public TempObject
         return isConstantValue() || isConstantIndex();
     }
     bool isConstantValue() const {
         return kind() == CONSTANT_VALUE;
     }
     bool isConstantIndex() const {
         return kind() == CONSTANT_INDEX;
     }
-    bool isValue() const {
-        return kind() == CONSTANT_VALUE;
-    }
     bool isGeneralReg() const {
         return kind() == GPR;
     }
     bool isFloatReg() const {
         return kind() == FPU;
     }
     bool isStackSlot() const {
         return kind() == STACK_SLOT;
@@ -548,16 +545,24 @@ class LDefinition
           case MIRType_Pointer:
             return LDefinition::GENERAL;
           case MIRType_ForkJoinContext:
             return LDefinition::GENERAL;
           default:
             MOZ_ASSUME_UNREACHABLE("unexpected type");
         }
     }
+
+#ifdef DEBUG
+    const char *toString() const;
+#else
+    const char *toString() const { return "???"; }
+#endif
+
+    void dump() const;
 };
 
 // Forward declarations of LIR types.
 #define LIROP(op) class L##op;
     LIR_OPCODE_LIST(LIROP)
 #undef LIROP
 
 class LSnapshot;
--- a/js/src/jit/LinearScan.cpp
+++ b/js/src/jit/LinearScan.cpp
@@ -1229,32 +1229,35 @@ LinearScanAllocator::validateAllocations
 
 #endif // DEBUG
 
 bool
 LinearScanAllocator::go()
 {
     IonSpew(IonSpew_RegAlloc, "Beginning register allocation");
 
-    IonSpew(IonSpew_RegAlloc, "Beginning liveness analysis");
     if (!buildLivenessInfo())
         return false;
-    IonSpew(IonSpew_RegAlloc, "Liveness analysis complete");
 
     if (mir->shouldCancel("LSRA Liveness"))
         return false;
 
     IonSpew(IonSpew_RegAlloc, "Beginning preliminary register allocation");
     if (!allocateRegisters())
         return false;
     IonSpew(IonSpew_RegAlloc, "Preliminary register allocation complete");
 
     if (mir->shouldCancel("LSRA Preliminary Regalloc"))
         return false;
 
+    if (IonSpewEnabled(IonSpew_RegAlloc)) {
+        fprintf(stderr, "Allocations by virtual register:\n");
+        dumpVregs();
+    }
+
     IonSpew(IonSpew_RegAlloc, "Beginning control flow resolution");
     if (!resolveControlFlow())
         return false;
     IonSpew(IonSpew_RegAlloc, "Control flow resolution complete");
 
     if (mir->shouldCancel("LSRA Control Flow"))
         return false;
 
--- a/js/src/jit/LiveRangeAllocator.cpp
+++ b/js/src/jit/LiveRangeAllocator.cpp
@@ -32,16 +32,63 @@ Requirement::priority() const
       case Requirement::NONE:
         return 2;
 
       default:
         MOZ_ASSUME_UNREACHABLE("Unknown requirement kind.");
     }
 }
 
+const char *
+Requirement::toString() const
+{
+#ifdef DEBUG
+    // Not reentrant!
+    static char buf[1000];
+
+    char *cursor = buf;
+    char *end = cursor + sizeof(buf);
+
+    int n;
+    switch (kind()) {
+      case NONE:
+        return "none";
+      case REGISTER:
+        n = JS_snprintf(cursor, end - cursor, "r");
+        break;
+      case FIXED:
+        n = JS_snprintf(cursor, end - cursor, "%s", allocation().toString());
+        break;
+      case SAME_AS_OTHER:
+        n = JS_snprintf(cursor, end - cursor, "v%u", virtualRegister());
+        break;
+    }
+    if (n < 0)
+        return "???";
+    cursor += n;
+
+    if (pos() != CodePosition::MIN) {
+        n = JS_snprintf(cursor, end - cursor, "@%u", pos().pos());
+        if (n < 0)
+            return "???";
+        cursor += n;
+    }
+
+    return buf;
+#else
+    return " ???";
+#endif
+}
+
+void
+Requirement::dump() const
+{
+    fprintf(stderr, "%s\n", toString());
+}
+
 bool
 LiveInterval::Range::contains(const Range *other) const
 {
     return from <= other->from && to >= other->to;
 }
 
 void
 LiveInterval::Range::intersect(const Range *other, Range *pre, Range *inside, Range *post) const
@@ -67,16 +114,43 @@ LiveInterval::Range::intersect(const Ran
         *post = Range(other->to, to);
         innerTo = other->to;
     }
 
     if (innerFrom != innerTo)
         *inside = Range(innerFrom, innerTo);
 }
 
+const char *
+LiveInterval::Range::toString() const
+{
+#ifdef DEBUG
+    // Not reentrant!
+    static char buf[1000];
+
+    char *cursor = buf;
+    char *end = cursor + sizeof(buf);
+
+    int n = JS_snprintf(cursor, end - cursor, "[%u,%u)", from.pos(), to.pos());
+    if (n < 0)
+        return " ???";
+    cursor += n;
+
+    return buf;
+#else
+    return " ???";
+#endif
+}
+
+void
+LiveInterval::Range::dump() const
+{
+    fprintf(stderr, "%s\n", toString());
+}
+
 bool
 LiveInterval::addRangeAtHead(CodePosition from, CodePosition to)
 {
     JS_ASSERT(from < to);
     JS_ASSERT(ranges_.empty() || from <= ranges_.back().from);
 
     Range newRange(from, to);
 
@@ -501,16 +575,18 @@ AddRegisterToSafepoint(LSafepoint *safep
  * definitions, and recording which registers are live at the top of every
  * block. To deal with loop backedges, variables live at the beginning of
  * a loop gain an interval covering the entire loop.
  */
 template <typename VREG, bool forLSRA>
 bool
 LiveRangeAllocator<VREG, forLSRA>::buildLivenessInfo()
 {
+    IonSpew(IonSpew_RegAlloc, "Beginning liveness analysis");
+
     if (!init())
         return false;
 
     Vector<MBasicBlock *, 1, SystemAllocPolicy> loopWorkList;
     BitSet *loopDone = BitSet::New(alloc(), graph.numBlockIds());
     if (!loopDone)
         return false;
 
@@ -847,19 +923,50 @@ LiveRangeAllocator<VREG, forLSRA>::build
     if (fixedIntervalsUnion->numRanges() == 0) {
         if (!fixedIntervalsUnion->addRangeAtHead(CodePosition(0, CodePosition::INPUT),
                                                  CodePosition(0, CodePosition::OUTPUT)))
         {
             return false;
         }
     }
 
+    IonSpew(IonSpew_RegAlloc, "Liveness analysis complete");
+
+    if (IonSpewEnabled(IonSpew_RegAlloc)) {
+        dumpInstructions();
+
+        fprintf(stderr, "Live ranges by virtual register:\n");
+        dumpVregs();
+    }
+
     return true;
 }
 
+template <typename VREG, bool forLSRA>
+void
+LiveRangeAllocator<VREG, forLSRA>::dumpVregs()
+{
+#ifdef DEBUG
+    // Virtual register number 0 is unused.
+    JS_ASSERT(vregs[0u].numIntervals() == 0);
+    for (uint32_t i = 1; i < graph.numVirtualRegisters(); i++) {
+        fprintf(stderr, "  ");
+        VirtualRegister &vreg = vregs[i];
+        for (size_t j = 0; j < vreg.numIntervals(); j++) {
+            if (j)
+                fprintf(stderr, " / ");
+            fprintf(stderr, "%s", vreg.getInterval(j)->toString());
+        }
+        fprintf(stderr, "\n");
+    }
+
+    fprintf(stderr, "\n");
+#endif
+}
+
 #ifdef DEBUG
 
 void
 LiveInterval::validateRanges()
 {
     Range *prev = nullptr;
 
     for (size_t i = ranges_.length() - 1; i < ranges_.length(); i--) {
@@ -872,39 +979,121 @@ LiveInterval::validateRanges()
 }
 
 #endif // DEBUG
 
 const char *
 LiveInterval::rangesToString() const
 {
 #ifdef DEBUG
-    if (!numRanges())
-        return " empty";
-
     // Not reentrant!
-    static char buf[1000];
+    static char buf[2000];
 
     char *cursor = buf;
     char *end = cursor + sizeof(buf);
 
-    for (size_t i = 0; i < numRanges(); i++) {
+    int n;
+
+    for (size_t i = ranges_.length() - 1; i < ranges_.length(); i--) {
         const LiveInterval::Range *range = getRange(i);
-        int n = JS_snprintf(cursor, end - cursor, " [%u,%u>", range->from.pos(), range->to.pos());
+        n = JS_snprintf(cursor, end - cursor, " %s", range->toString());
         if (n < 0)
             return " ???";
         cursor += n;
     }
 
     return buf;
 #else
     return " ???";
 #endif
 }
 
-void
-LiveInterval::dump()
+static bool
+IsHintInteresting(const Requirement &requirement, const Requirement &hint)
+{
+    if (hint.kind() == Requirement::NONE)
+        return false;
+
+    if (hint.kind() != Requirement::FIXED && hint.kind() != Requirement::REGISTER)
+        return true;
+
+    Requirement merge = requirement;
+    if (!merge.mergeRequirement(hint))
+        return true;
+
+    return merge.kind() != requirement.kind();
+}
+
+const char *
+LiveInterval::toString() const
 {
-    if (hasVreg())
-        fprintf(stderr, "v%u: ", vreg());
-    fprintf(stderr, "index=%u allocation=%s %s\n",
-            index(), getAllocation()->toString(), rangesToString());
+#ifdef DEBUG
+    // Not reentrant!
+    static char buf[2000];
+
+    char *cursor = buf;
+    char *end = cursor + sizeof(buf);
+
+    int n;
+
+    if (hasVreg()) {
+        n = JS_snprintf(cursor, end - cursor, "v%u", vreg());
+        if (n < 0) return "???";
+        cursor += n;
+    }
+
+    n = JS_snprintf(cursor, end - cursor, "[%u]", index());
+    if (n < 0) return "???";
+    cursor += n;
+
+    if (requirement_.kind() != Requirement::NONE || hint_.kind() != Requirement::NONE) {
+        n = JS_snprintf(cursor, end - cursor, " req(");
+        if (n < 0) return "???";
+        cursor += n;
+
+        bool printHint = IsHintInteresting(requirement_, hint_);
+
+        if (requirement_.kind() != Requirement::NONE) {
+            n = JS_snprintf(cursor, end - cursor, "%s%s",
+                            requirement_.toString(),
+                            printHint ? "," : "");
+            if (n < 0) return "???";
+            cursor += n;
+        }
+        if (printHint) {
+            n = JS_snprintf(cursor, end - cursor, "%s?", hint_.toString());
+            if (n < 0) return "???";
+            cursor += n;
+        }
+
+        n = JS_snprintf(cursor, end - cursor, ")");
+        if (n < 0) return "???";
+        cursor += n;
+    }
+
+    if (alloc_.kind() != LAllocation::USE) {
+        n = JS_snprintf(cursor, end - cursor, " has(%s)", alloc_.toString());
+        if (n < 0) return "???";
+        cursor += n;
+    }
+
+    n = JS_snprintf(cursor, end - cursor, "%s", rangesToString());
+    if (n < 0) return "???";
+    cursor += n;
+
+    for (UsePositionIterator usePos(usesBegin()); usePos != usesEnd(); usePos++) {
+        n = JS_snprintf(cursor, end - cursor, " %s@%u",
+                        usePos->use->toString(), usePos->pos.pos());
+        if (n < 0) return "???";
+        cursor += n;
+    }
+
+    return buf;
+#else
+    return "???";
+#endif
 }
+
+void
+LiveInterval::dump() const
+{
+    fprintf(stderr, "%s\n", toString());
+}
--- a/js/src/jit/LiveRangeAllocator.h
+++ b/js/src/jit/LiveRangeAllocator.h
@@ -87,16 +87,41 @@ class Requirement
     }
 
     CodePosition pos() const {
         return position_;
     }
 
     int priority() const;
 
+    bool mergeRequirement(const Requirement &newRequirement) {
+        // Merge newRequirement with any existing requirement, returning false
+        // if the new and old requirements conflict.
+        JS_ASSERT(newRequirement.kind() != Requirement::SAME_AS_OTHER);
+
+        if (newRequirement.kind() == Requirement::FIXED) {
+            if (kind() == Requirement::FIXED)
+                return newRequirement.allocation() == allocation();
+            *this = newRequirement;
+            return true;
+        }
+
+        JS_ASSERT(newRequirement.kind() == Requirement::REGISTER);
+        if (kind() == Requirement::FIXED)
+            return allocation().isRegister();
+
+        *this = newRequirement;
+        return true;
+    }
+
+    // Return a string describing this requirement. This is not re-entrant!
+    const char *toString() const;
+
+    void dump() const;
+
   private:
     Kind kind_;
     LAllocation allocation_;
     CodePosition position_;
 };
 
 struct UsePosition : public TempObject,
                      public InlineForwardListNode<UsePosition>
@@ -214,16 +239,21 @@ class LiveInterval
         }
 
         // Whether this range wholly contains other.
         bool contains(const Range *other) const;
 
         // Intersect this range with other, returning the subranges of this
         // that are before, inside, or after other.
         void intersect(const Range *other, Range *pre, Range *inside, Range *post) const;
+
+        // Return a string describing this range. This is not re-entrant!
+        const char *toString() const;
+
+        void dump() const;
     };
 
   private:
     Vector<Range, 1, IonAllocPolicy> ranges_;
     LAllocation alloc_;
     LiveInterval *spillInterval_;
     uint32_t vreg_;
     uint32_t index_;
@@ -321,33 +351,17 @@ class LiveInterval
     }
     void setRequirement(const Requirement &requirement) {
         // A SAME_AS_OTHER requirement complicates regalloc too much; it
         // should only be used as hint.
         JS_ASSERT(requirement.kind() != Requirement::SAME_AS_OTHER);
         requirement_ = requirement;
     }
     bool addRequirement(const Requirement &newRequirement) {
-        // Merge newRequirement with any existing requirement, returning false
-        // if the new and old requirements conflict.
-        JS_ASSERT(newRequirement.kind() != Requirement::SAME_AS_OTHER);
-
-        if (newRequirement.kind() == Requirement::FIXED) {
-            if (requirement_.kind() == Requirement::FIXED)
-                return newRequirement.allocation() == requirement_.allocation();
-            requirement_ = newRequirement;
-            return true;
-        }
-
-        JS_ASSERT(newRequirement.kind() == Requirement::REGISTER);
-        if (requirement_.kind() == Requirement::FIXED)
-            return requirement_.allocation().isRegister();
-
-        requirement_ = newRequirement;
-        return true;
+        return requirement_.mergeRequirement(newRequirement);
     }
     const Requirement *hint() const {
         return &hint_;
     }
     void setHint(const Requirement &hint) {
         hint_ = hint;
     }
     bool isSpill() const {
@@ -380,17 +394,20 @@ class LiveInterval
 #ifdef DEBUG
     void validateRanges();
 #endif
 
     // Return a string describing the ranges in this LiveInterval. This is
     // not re-entrant!
     const char *rangesToString() const;
 
-    void dump();
+    // Return a string describing this LiveInterval. This is not re-entrant!
+    const char *toString() const;
+
+    void dump() const;
 };
 
 /*
  * Represents all of the register allocation state associated with a virtual
  * register, including all associated intervals and pointers to relevant LIR
  * structures.
  */
 class VirtualRegister
@@ -726,14 +743,16 @@ class LiveRangeAllocator : protected Reg
         size_t i = startFrom;
         for (; i < graph.numSafepoints(); i++) {
             LInstruction *ins = graph.getSafepoint(i);
             if (interval->start() <= inputOf(ins))
                 break;
         }
         return i;
     }
+
+    void dumpVregs();
 };
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_LiveRangeAllocator_h */
--- a/js/src/jit/RegisterAllocator.cpp
+++ b/js/src/jit/RegisterAllocator.cpp
@@ -354,40 +354,40 @@ AllocationIntegrityState::addPredecessor
 
     return worklist.append(item);
 }
 
 void
 AllocationIntegrityState::dump()
 {
 #ifdef DEBUG
-    fprintf(stderr, "Register Allocation:\n");
+    fprintf(stderr, "Register Allocation Integrity State:\n");
 
     for (size_t blockIndex = 0; blockIndex < graph.numBlocks(); blockIndex++) {
         LBlock *block = graph.getBlock(blockIndex);
         MBasicBlock *mir = block->mir();
 
         fprintf(stderr, "\nBlock %lu", static_cast<unsigned long>(blockIndex));
         for (size_t i = 0; i < mir->numSuccessors(); i++)
             fprintf(stderr, " [successor %u]", mir->getSuccessor(i)->id());
         fprintf(stderr, "\n");
 
         for (size_t i = 0; i < block->numPhis(); i++) {
             const InstructionInfo &info = blocks[blockIndex].phis[i];
             LPhi *phi = block->getPhi(i);
-            CodePosition output(phi->id(), CodePosition::OUTPUT);
+            CodePosition input(block->getPhi(0)->id(), CodePosition::INPUT);
+            CodePosition output(block->getPhi(block->numPhis() - 1)->id(), CodePosition::OUTPUT);
 
-            // Don't print the inputOf for phi nodes, since it's never used.
-            fprintf(stderr, "[,%u Phi [def v%u %s] <-",
+            fprintf(stderr, "[%u,%u Phi] [def %s] ",
+                    input.pos(),
                     output.pos(),
-                    info.outputs[0].virtualRegister(),
-                    phi->getDef(0)->output()->toString());
+                    phi->getDef(0)->toString());
             for (size_t j = 0; j < phi->numOperands(); j++)
-                fprintf(stderr, " %s", info.inputs[j].toString());
-            fprintf(stderr, "]\n");
+                fprintf(stderr, " [use %s]", info.inputs[j].toString());
+            fprintf(stderr, "\n");
         }
 
         for (LInstructionIterator iter = block->begin(); iter != block->end(); iter++) {
             LInstruction *ins = *iter;
             const InstructionInfo &info = instructions[ins->id()];
 
             CodePosition input(ins->id(), CodePosition::INPUT);
             CodePosition output(ins->id(), CodePosition::OUTPUT);
@@ -403,33 +403,32 @@ AllocationIntegrityState::dump()
                     // Use two printfs, as LAllocation::toString is not reentant.
                     fprintf(stderr, " [%s", group->getMove(i).from()->toString());
                     fprintf(stderr, " -> %s]", group->getMove(i).to()->toString());
                 }
                 fprintf(stderr, "\n");
                 continue;
             }
 
+            for (size_t i = 0; i < ins->numDefs(); i++)
+                fprintf(stderr, " [def %s]", ins->getDef(i)->toString());
+
             for (size_t i = 0; i < ins->numTemps(); i++) {
                 LDefinition *temp = ins->getTemp(i);
                 if (!temp->isBogusTemp())
                     fprintf(stderr, " [temp v%u %s]", info.temps[i].virtualRegister(),
-                           temp->output()->toString());
-            }
-
-            for (size_t i = 0; i < ins->numDefs(); i++) {
-                LDefinition *def = ins->getDef(i);
-                fprintf(stderr, " [def v%u %s]", info.outputs[i].virtualRegister(),
-                       def->output()->toString());
+                           temp->toString());
             }
 
             size_t index = 0;
             for (LInstruction::InputIterator alloc(*ins); alloc.more(); alloc.next()) {
                 fprintf(stderr, " [use %s", info.inputs[index++].toString());
-                fprintf(stderr, " %s]", alloc->toString());
+                if (!alloc->isConstant())
+                    fprintf(stderr, " %s", alloc->toString());
+                fprintf(stderr, "]");
             }
 
             fprintf(stderr, "\n");
         }
     }
 
     // Print discovered allocations at the ends of blocks, in the order they
     // were discovered.
@@ -438,21 +437,21 @@ AllocationIntegrityState::dump()
     seenOrdered.appendN(IntegrityItem(), seen.count());
 
     for (IntegrityItemSet::Enum iter(seen); !iter.empty(); iter.popFront()) {
         IntegrityItem item = iter.front();
         seenOrdered[item.index] = item;
     }
 
     if (!seenOrdered.empty()) {
-        fprintf(stderr, "\nIntermediate Allocations:\n\n");
+        fprintf(stderr, "Intermediate Allocations:\n");
 
         for (size_t i = 0; i < seenOrdered.length(); i++) {
             IntegrityItem item = seenOrdered[i];
-            fprintf(stderr, "block %u reg v%u alloc %s\n",
+            fprintf(stderr, "  block %u reg v%u alloc %s\n",
                    item.block->mir()->id(), item.vreg, item.alloc.toString());
         }
     }
 
     fprintf(stderr, "\n");
 #endif
 }
 
@@ -508,8 +507,73 @@ RegisterAllocator::getMoveGroupAfter(uin
     data->setMovesAfter(moves);
 
     if (data->ins()->isLabel())
         data->block()->insertAfter(data->block()->getEntryMoveGroup(alloc()), moves);
     else
         data->block()->insertAfter(data->ins(), moves);
     return moves;
 }
+
+void
+RegisterAllocator::dumpInstructions()
+{
+#ifdef DEBUG
+    fprintf(stderr, "Instructions:\n");
+
+    for (size_t blockIndex = 0; blockIndex < graph.numBlocks(); blockIndex++) {
+        LBlock *block = graph.getBlock(blockIndex);
+        MBasicBlock *mir = block->mir();
+
+        fprintf(stderr, "\nBlock %lu", static_cast<unsigned long>(blockIndex));
+        for (size_t i = 0; i < mir->numSuccessors(); i++)
+            fprintf(stderr, " [successor %u]", mir->getSuccessor(i)->id());
+        fprintf(stderr, "\n");
+
+        for (size_t i = 0; i < block->numPhis(); i++) {
+            LPhi *phi = block->getPhi(i);
+
+            fprintf(stderr, "[%u,%u Phi] [def %s]",
+                    inputOf(phi).pos(),
+                    outputOf(phi).pos(),
+                    phi->getDef(0)->toString());
+            for (size_t j = 0; j < phi->numOperands(); j++)
+                fprintf(stderr, " [use %s]", phi->getOperand(j)->toString());
+            fprintf(stderr, "\n");
+        }
+
+        for (LInstructionIterator iter = block->begin(); iter != block->end(); iter++) {
+            LInstruction *ins = *iter;
+
+            fprintf(stderr, "[");
+            if (ins->id() != 0)
+                fprintf(stderr, "%u,%u ", inputOf(ins).pos(), outputOf(ins).pos());
+            fprintf(stderr, "%s]", ins->opName());
+
+            if (ins->isMoveGroup()) {
+                LMoveGroup *group = ins->toMoveGroup();
+                for (int i = group->numMoves() - 1; i >= 0; i--) {
+                    // Use two printfs, as LAllocation::toString is not reentant.
+                    fprintf(stderr, " [%s", group->getMove(i).from()->toString());
+                    fprintf(stderr, " -> %s]", group->getMove(i).to()->toString());
+                }
+                fprintf(stderr, "\n");
+                continue;
+            }
+
+            for (size_t i = 0; i < ins->numDefs(); i++)
+                fprintf(stderr, " [def %s]", ins->getDef(i)->toString());
+
+            for (size_t i = 0; i < ins->numTemps(); i++) {
+                LDefinition *temp = ins->getTemp(i);
+                if (!temp->isBogusTemp())
+                    fprintf(stderr, " [temp %s]", temp->toString());
+            }
+
+            for (LInstruction::InputIterator alloc(*ins); alloc.more(); alloc.next())
+                fprintf(stderr, " [use %s]", alloc->toString());
+
+            fprintf(stderr, "\n");
+        }
+    }
+    fprintf(stderr, "\n");
+#endif // DEBUG
+}
--- a/js/src/jit/RegisterAllocator.h
+++ b/js/src/jit/RegisterAllocator.h
@@ -334,34 +334,46 @@ class RegisterAllocator
     }
 
     bool init();
 
     TempAllocator &alloc() const {
         return mir->alloc();
     }
 
-    static CodePosition outputOf(uint32_t pos) {
+    CodePosition outputOf(uint32_t pos) const {
+        // All phis in a block write their outputs after all of them have
+        // read their inputs. Consequently, it doesn't make sense to talk
+        // about code positions in the middle of a series of phis.
+        if (insData[pos].ins()->isPhi()) {
+            while (insData[pos + 1].ins()->isPhi())
+                ++pos;
+        }
         return CodePosition(pos, CodePosition::OUTPUT);
     }
-    static CodePosition outputOf(const LInstruction *ins) {
+    CodePosition outputOf(const LInstruction *ins) const {
         return outputOf(ins->id());
     }
-    static CodePosition inputOf(uint32_t pos) {
+    CodePosition inputOf(uint32_t pos) const {
+        // All phis in a block read their inputs before any of them write their
+        // outputs. Consequently, it doesn't make sense to talk about code
+        // positions in the middle of a series of phis.
+        if (insData[pos].ins()->isPhi()) {
+            while (pos > 0 && insData[pos - 1].ins()->isPhi())
+                --pos;
+        }
         return CodePosition(pos, CodePosition::INPUT);
     }
-    static CodePosition inputOf(const LInstruction *ins) {
-        // Phi nodes "use" their inputs before the beginning of the block.
-        JS_ASSERT(!ins->isPhi());
+    CodePosition inputOf(const LInstruction *ins) const {
         return inputOf(ins->id());
     }
-    static CodePosition entryOf(const LBlock *block) {
+    CodePosition entryOf(const LBlock *block) {
         return inputOf(block->firstId());
     }
-    static CodePosition exitOf(const LBlock *block) {
+    CodePosition exitOf(const LBlock *block) {
         return outputOf(block->lastId());
     }
 
     LMoveGroup *getInputMoveGroup(uint32_t ins);
     LMoveGroup *getMoveGroupAfter(uint32_t ins);
 
     LMoveGroup *getInputMoveGroup(CodePosition pos) {
         return getInputMoveGroup(pos.ins());
@@ -379,16 +391,18 @@ class RegisterAllocator
             LInstruction *next = insData[outputOf(ins).next()].ins();
             if (!next->isNop() && !next->isOsiPoint())
                 break;
             ins = next;
         }
 
         return outputOf(ins);
     }
+
+    void dumpInstructions();
 };
 
 static inline AnyRegister
 GetFixedRegister(const LDefinition *def, const LUse *use)
 {
     return def->isFloatReg()
            ? AnyRegister(FloatRegister::FromCode(use->registerCode()))
            : AnyRegister(Register::FromCode(use->registerCode()));