Allow canceling in-progress off thread ion compilations, bug 785761. r=dvander
☠☠ backed out by 54591f4e0f79 ☠ ☠
authorBrian Hackett <bhackett1024@gmail.com>
Thu, 25 Oct 2012 12:00:00 -0700
changeset 111560 4aef9921a9d63ef03d461d8ba824ad05f2485b58
parent 111559 1ca467baad43728aea16b115fde7ab9b18614b07
child 111561 f35ce23ddfc84769cb49f1bbff74d3a37ed338c2
push id93
push usernmatsakis@mozilla.com
push dateWed, 31 Oct 2012 21:26:57 +0000
reviewersdvander
bugs785761
milestone19.0a1
Allow canceling in-progress off thread ion compilations, bug 785761. r=dvander
js/src/ion/AliasAnalysis.cpp
js/src/ion/AliasAnalysis.h
js/src/ion/EdgeCaseAnalysis.cpp
js/src/ion/EdgeCaseAnalysis.h
js/src/ion/Ion.cpp
js/src/ion/IonAnalysis.cpp
js/src/ion/IonAnalysis.h
js/src/ion/LICM.cpp
js/src/ion/LICM.h
js/src/ion/LinearScan.cpp
js/src/ion/LinearScan.h
js/src/ion/Lowering.cpp
js/src/ion/MIRGenerator.h
js/src/ion/MIRGraph.cpp
js/src/ion/ValueNumbering.cpp
js/src/ion/ValueNumbering.h
js/src/jsworkers.cpp
js/src/jsworkers.h
--- a/js/src/ion/AliasAnalysis.cpp
+++ b/js/src/ion/AliasAnalysis.cpp
@@ -6,16 +6,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include <stdio.h>
 
 #include "MIR.h"
 #include "AliasAnalysis.h"
 #include "MIRGraph.h"
 #include "Ion.h"
+#include "IonBuilder.h"
 #include "IonSpewer.h"
 
 using namespace js;
 using namespace js::ion;
 
 // Iterates over the flags in an AliasSet.
 class AliasSetIterator
 {
@@ -42,18 +43,19 @@ class AliasSetIterator
     operator bool() const {
         return !!flags;
     }
     unsigned operator *() const {
         return pos;
     }
 };
 
-AliasAnalysis::AliasAnalysis(MIRGraph &graph)
-  : graph_(graph),
+AliasAnalysis::AliasAnalysis(MIRGenerator *mir, MIRGraph &graph)
+  : mir(mir),
+    graph_(graph),
     loop_(NULL)
 {
 }
 
 // This pass annotates every load instruction with the last store instruction
 // on which it depends. The algorithm is optimistic in that it ignores explicit
 // dependencies and only considers loads and stores.
 //
@@ -80,16 +82,19 @@ AliasAnalysis::analyze()
     }
 
     // Type analysis may have inserted new instructions. Since this pass depends
     // on the instruction number ordering, all instructions are renumbered.
     // We start with 1 because some passes use 0 to denote failure.
     uint32 newId = 1;
 
     for (ReversePostorderIterator block(graph_.rpoBegin()); block != graph_.rpoEnd(); block++) {
+        if (mir->shouldCancel("Alias Analysis (main loop)"))
+            return false;
+
         if (block->isLoopHeader()) {
             IonSpew(IonSpew_Alias, "Processing loop header %d", block->id());
             loop_ = new LoopAliasInfo(loop_, *block);
         }
 
         for (MDefinitionIterator def(*block); def; def++) {
             def->setId(newId++);
 
--- a/js/src/ion/AliasAnalysis.h
+++ b/js/src/ion/AliasAnalysis.h
@@ -50,20 +50,21 @@ class LoopAliasInfo : public TempObject 
     }
     MDefinition *firstInstruction() const {
         return *loopHeader_->begin();
     }
 };
 
 class AliasAnalysis
 {
+    MIRGenerator *mir;
     MIRGraph &graph_;
     LoopAliasInfo *loop_;
 
   public:
-    AliasAnalysis(MIRGraph &graph);
+    AliasAnalysis(MIRGenerator *mir, MIRGraph &graph);
     bool analyze();
 };
 
 } // namespace js
 } // namespace ion
 
 #endif // jsion_alias_analysis_h__
--- a/js/src/ion/EdgeCaseAnalysis.cpp
+++ b/js/src/ion/EdgeCaseAnalysis.cpp
@@ -3,50 +3,57 @@
  *
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include <stdio.h>
 
 #include "Ion.h"
+#include "IonBuilder.h"
 #include "IonSpewer.h"
 #include "EdgeCaseAnalysis.h"
 #include "MIR.h"
 #include "MIRGraph.h"
 
 using namespace js;
 using namespace js::ion;
 
-EdgeCaseAnalysis::EdgeCaseAnalysis(MIRGraph &graph)
-  : graph(graph)
+EdgeCaseAnalysis::EdgeCaseAnalysis(MIRGenerator *mir, MIRGraph &graph)
+  : mir(mir), graph(graph)
 {
 }
 
 bool
 EdgeCaseAnalysis::analyzeLate()
 {
     for (ReversePostorderIterator block(graph.rpoBegin()); block != graph.rpoEnd(); block++) {
+        if (mir->shouldCancel("Analyze Late (first loop)"))
+            return false;
         for (MDefinitionIterator iter(*block); iter; iter++)
             iter->analyzeEdgeCasesForward();
     }
 
     for (PostorderIterator block(graph.poBegin()); block != graph.poEnd(); block++) {
+        if (mir->shouldCancel("Analyze Late (second loop)"))
+            return false;
         for (MInstructionReverseIterator riter(block->rbegin()); riter != block->rend(); riter++)
             riter->analyzeEdgeCasesBackward();
     }
 
     return true;
 }
 
 bool
 EdgeCaseAnalysis::analyzeEarly()
 {
 
     for (PostorderIterator block(graph.poBegin()); block != graph.poEnd(); block++) {
+        if (mir->shouldCancel("Analyze Early (main loop)"))
+            return false;
         for (MInstructionReverseIterator riter(block->rbegin()); riter != block->rend(); riter++)
             riter->analyzeTruncateBackward();
     }
 
     return true;
 }
 
 bool
--- a/js/src/ion/EdgeCaseAnalysis.h
+++ b/js/src/ion/EdgeCaseAnalysis.h
@@ -10,20 +10,21 @@
 
 namespace js {
 namespace ion {
 
 class MIRGraph;
 
 class EdgeCaseAnalysis
 {
+    MIRGenerator *mir;
     MIRGraph &graph;
 
   public:
-    EdgeCaseAnalysis(MIRGraph &graph);
+    EdgeCaseAnalysis(MIRGenerator *mir, MIRGraph &graph);
     bool analyzeEarly();
     bool analyzeLate();
     static bool AllUsesTruncate(MInstruction *m);
 };
 
 
 } // namespace ion
 } // namespace js
--- a/js/src/ion/Ion.cpp
+++ b/js/src/ion/Ion.cpp
@@ -760,134 +760,191 @@ LIRGraph *
 CompileBackEnd(MIRGenerator *mir)
 {
     IonSpewPass("BuildSSA");
     // Note: don't call AssertGraphCoherency before SplitCriticalEdges,
     // the graph is not in RPO at this point.
 
     MIRGraph &graph = mir->graph();
 
+    if (mir->shouldCancel("Start"))
+        return false;
+
     if (!SplitCriticalEdges(graph))
         return NULL;
     IonSpewPass("Split Critical Edges");
     AssertGraphCoherency(graph);
 
+    if (mir->shouldCancel("Split Critical Edges"))
+        return false;
+
     if (!RenumberBlocks(graph))
         return NULL;
     IonSpewPass("Renumber Blocks");
     AssertGraphCoherency(graph);
 
+    if (mir->shouldCancel("Renumber Blocks"))
+        return false;
+
     if (!BuildDominatorTree(graph))
         return NULL;
     // No spew: graph not changed.
 
+    if (mir->shouldCancel("Dominator Tree"))
+        return false;
+
     // This must occur before any code elimination.
-    if (!EliminatePhis(graph))
+    if (!EliminatePhis(mir, graph))
         return NULL;
     IonSpewPass("Eliminate phis");
     AssertGraphCoherency(graph);
 
+    if (mir->shouldCancel("Eliminate phis"))
+        return false;
+
     if (!BuildPhiReverseMapping(graph))
         return NULL;
     // No spew: graph not changed.
 
+    if (mir->shouldCancel("Phi reverse mapping"))
+        return false;
+
     // This pass also removes copies.
-    if (!ApplyTypeInformation(graph))
+    if (!ApplyTypeInformation(mir, graph))
         return NULL;
     IonSpewPass("Apply types");
     AssertGraphCoherency(graph);
 
+    if (mir->shouldCancel("Apply types"))
+        return false;
+
     // Alias analysis is required for LICM and GVN so that we don't move
     // loads across stores.
     if (js_IonOptions.licm || js_IonOptions.gvn) {
-        AliasAnalysis analysis(graph);
+        AliasAnalysis analysis(mir, graph);
         if (!analysis.analyze())
             return NULL;
         IonSpewPass("Alias analysis");
         AssertGraphCoherency(graph);
+
+        if (mir->shouldCancel("Alias analysis"))
+            return false;
     }
 
     if (js_IonOptions.edgeCaseAnalysis) {
-        EdgeCaseAnalysis edgeCaseAnalysis(graph);
+        EdgeCaseAnalysis edgeCaseAnalysis(mir, graph);
         if (!edgeCaseAnalysis.analyzeEarly())
             return NULL;
         IonSpewPass("Edge Case Analysis (Early)");
         AssertGraphCoherency(graph);
+
+        if (mir->shouldCancel("Edge Case Analysis (Early)"))
+            return false;
     }
 
     if (js_IonOptions.gvn) {
-        ValueNumberer gvn(graph, js_IonOptions.gvnIsOptimistic);
+        ValueNumberer gvn(mir, graph, js_IonOptions.gvnIsOptimistic);
         if (!gvn.analyze())
             return NULL;
         IonSpewPass("GVN");
         AssertGraphCoherency(graph);
+
+        if (mir->shouldCancel("GVN"))
+            return false;
     }
 
     if (js_IonOptions.rangeAnalysis) {
         RangeAnalysis r(graph);
         if (!r.addBetaNobes())
             return NULL;
         IonSpewPass("Beta");
         AssertGraphCoherency(graph);
 
+        if (mir->shouldCancel("RA Beta"))
+            return false;
+
         if (!r.analyze())
             return NULL;
         IonSpewPass("Range Analysis");
         AssertGraphCoherency(graph);
 
+        if (mir->shouldCancel("Range Analysis"))
+            return false;
+
         if (!r.removeBetaNobes())
             return NULL;
         IonSpewPass("De-Beta");
         AssertGraphCoherency(graph);
+
+        if (mir->shouldCancel("RA De-Beta"))
+            return false;
     }
 
-    if (!EliminateDeadCode(graph))
+    if (!EliminateDeadCode(mir, graph))
         return NULL;
     IonSpewPass("DCE");
     AssertGraphCoherency(graph);
 
+    if (mir->shouldCancel("DCE"))
+        return false;
+
     if (js_IonOptions.licm) {
-        LICM licm(graph);
+        LICM licm(mir, graph);
         if (!licm.analyze())
             return NULL;
         IonSpewPass("LICM");
         AssertGraphCoherency(graph);
+
+        if (mir->shouldCancel("LICM"))
+            return false;
     }
 
     if (js_IonOptions.edgeCaseAnalysis) {
-        EdgeCaseAnalysis edgeCaseAnalysis(graph);
+        EdgeCaseAnalysis edgeCaseAnalysis(mir, graph);
         if (!edgeCaseAnalysis.analyzeLate())
             return NULL;
         IonSpewPass("Edge Case Analysis (Late)");
         AssertGraphCoherency(graph);
+
+        if (mir->shouldCancel("Edge Case Analysis (Late)"))
+            return false;
     }
 
     // Note: bounds check elimination has to run after all other passes that
     // move instructions. Since bounds check uses are replaced with the actual
     // index, code motion after this pass could incorrectly move a load or
     // store before its bounds check.
     if (!EliminateRedundantBoundsChecks(graph))
         return NULL;
     IonSpewPass("Bounds Check Elimination");
     AssertGraphCoherency(graph);
 
+    if (mir->shouldCancel("Bounds Check Elimination"))
+        return false;
+
     LIRGraph *lir = mir->temp().lifoAlloc()->new_<LIRGraph>(&graph);
     if (!lir)
         return NULL;
 
     LIRGenerator lirgen(mir, graph, *lir);
     if (!lirgen.generate())
         return NULL;
     IonSpewPass("Generate LIR");
 
+    if (mir->shouldCancel("Generate LIR"))
+        return false;
+
     if (js_IonOptions.lsra) {
-        LinearScanAllocator regalloc(&lirgen, *lir);
+        LinearScanAllocator regalloc(mir, &lirgen, *lir);
         if (!regalloc.go())
             return NULL;
         IonSpewPass("Allocate Registers", &regalloc);
+
+        if (mir->shouldCancel("Allocate Registers"))
+            return false;
     }
 
     return lir;
 }
 
 class AutoDestroyAllocator
 {
     LifoAlloc *alloc;
--- a/js/src/ion/IonAnalysis.cpp
+++ b/js/src/ion/IonAnalysis.cpp
@@ -39,21 +39,24 @@ ion::SplitCriticalEdges(MIRGraph &graph)
     }
     return true;
 }
 
 // Instructions are useless if they are unused and have no side effects.
 // This pass eliminates useless instructions.
 // The graph itself is unchanged.
 bool
-ion::EliminateDeadCode(MIRGraph &graph)
+ion::EliminateDeadCode(MIRGenerator *mir, MIRGraph &graph)
 {
     // Traverse in postorder so that we hit uses before definitions.
     // Traverse instruction list backwards for the same reason.
     for (PostorderIterator block = graph.poBegin(); block != graph.poEnd(); block++) {
+        if (mir->shouldCancel("Eliminate Dead Code (main loop)"))
+            return false;
+
         // Remove unused instructions.
         for (MInstructionReverseIterator inst = block->rbegin(); inst != block->rend(); ) {
             if (!inst->isEffectful() && !inst->hasUses() && !inst->isGuard() &&
                 !inst->isControlInstruction()) {
                 inst = block->discardAt(inst);
             } else {
                 inst++;
             }
@@ -102,23 +105,26 @@ IsPhiRedundant(MPhi *phi)
     // another phi.
     if (phi->hasBytecodeUses() && first->isPhi())
         first->toPhi()->setHasBytecodeUses();
 
     return first;
 }
 
 bool
-ion::EliminatePhis(MIRGraph &graph)
+ion::EliminatePhis(MIRGenerator *mir, MIRGraph &graph)
 {
     Vector<MPhi *, 16, SystemAllocPolicy> worklist;
 
     // Add all observable phis to a worklist. We use the "in worklist" bit to
     // mean "this phi is live".
     for (PostorderIterator block = graph.poBegin(); block != graph.poEnd(); block++) {
+        if (mir->shouldCancel("Eliminate Phis (populate loop)"))
+            return false;
+
         MPhiIterator iter = block->phisBegin();
         while (iter != block->phisEnd()) {
             // Flag all as unused, only observable phis would be marked as used
             // when processed by the work list.
             iter->setUnused();
 
             // If the phi is redundant, remove it here.
             if (MDefinition *redundant = IsPhiRedundant(*iter)) {
@@ -134,16 +140,19 @@ ion::EliminatePhis(MIRGraph &graph)
                     return false;
             }
             iter++;
         }
     }
 
     // Iteratively mark all phis reachable from live phis.
     while (!worklist.empty()) {
+        if (mir->shouldCancel("Eliminate Phis (worklist)"))
+            return false;
+
         MPhi *phi = worklist.popCopy();
         JS_ASSERT(phi->isUnused());
         phi->setNotInWorklist();
 
         // The removal of Phis can produce newly redundant phis.
         if (MDefinition *redundant = IsPhiRedundant(phi)) {
             // Add to the worklist the used phis which are impacted.
             for (MUseDefIterator it(phi); it; it++) {
@@ -195,16 +204,17 @@ ion::EliminatePhis(MIRGraph &graph)
 // specialized to return that type.
 //
 // Input adjustment: Each input is asked to apply conversion operations to its
 // inputs. This may include Box, Unbox, or other instruction-specific type
 // conversion operations.
 //
 class TypeAnalyzer
 {
+    MIRGenerator *mir;
     MIRGraph &graph;
     Vector<MPhi *, 0, SystemAllocPolicy> phiWorklist_;
 
     bool addPhiToWorklist(MPhi *phi) {
         if (phi->isInWorklist())
             return true;
         if (!phiWorklist_.append(phi))
             return false;
@@ -221,18 +231,18 @@ class TypeAnalyzer
     bool propagateSpecialization(MPhi *phi);
     bool specializePhis();
     void replaceRedundantPhi(MPhi *phi);
     void adjustPhiInputs(MPhi *phi);
     bool adjustInputs(MDefinition *def);
     bool insertConversions();
 
   public:
-    TypeAnalyzer(MIRGraph &graph)
-      : graph(graph)
+    TypeAnalyzer(MIRGenerator *mir, MIRGraph &graph)
+      : mir(mir), graph(graph)
     { }
 
     bool analyze();
 };
 
 // Try to specialize this phi based on its non-cyclic inputs.
 static MIRType
 GuessPhiType(MPhi *phi)
@@ -310,32 +320,38 @@ TypeAnalyzer::propagateSpecialization(MP
 
     return true;
 }
 
 bool
 TypeAnalyzer::specializePhis()
 {
     for (PostorderIterator block(graph.poBegin()); block != graph.poEnd(); block++) {
+        if (mir->shouldCancel("Specialize Phis (main loop)"))
+            return false;
+
         for (MPhiIterator phi(block->phisBegin()); phi != block->phisEnd(); phi++) {
             MIRType type = GuessPhiType(*phi);
             phi->specialize(type);
             if (type == MIRType_None) {
                 // We tried to guess the type but failed because all operands are
                 // phis we still have to visit. Set the triedToSpecialize flag but
                 // don't propagate the type to other phis, propagateSpecialization
                 // will do that once we know the type of one of the operands.
                 continue;
             }
             if (!propagateSpecialization(*phi))
                 return false;
         }
     }
 
     while (!phiWorklist_.empty()) {
+        if (mir->shouldCancel("Specialize Phis (worklist)"))
+            return false;
+
         MPhi *phi = popPhi();
         if (!propagateSpecialization(phi))
             return false;
     }
 
     return true;
 }
 
@@ -417,16 +433,19 @@ TypeAnalyzer::replaceRedundantPhi(MPhi *
 
 bool
 TypeAnalyzer::insertConversions()
 {
     // Instructions are processed in reverse postorder: all uses are defs are
     // seen before uses. This ensures that output adjustment (which may rewrite
     // inputs of uses) does not conflict with input adjustment.
     for (ReversePostorderIterator block(graph.rpoBegin()); block != graph.rpoEnd(); block++) {
+        if (mir->shouldCancel("Insert Conversions"))
+            return false;
+
         for (MPhiIterator phi(block->phisBegin()); phi != block->phisEnd();) {
             if (phi->type() <= MIRType_Null || phi->type() == MIRType_Magic) {
                 replaceRedundantPhi(*phi);
                 phi = block->discardPhiAt(phi);
             } else {
                 adjustPhiInputs(*phi);
                 phi++;
             }
@@ -445,19 +464,19 @@ TypeAnalyzer::analyze()
     if (!specializePhis())
         return false;
     if (!insertConversions())
         return false;
     return true;
 }
 
 bool
-ion::ApplyTypeInformation(MIRGraph &graph)
+ion::ApplyTypeInformation(MIRGenerator *mir, MIRGraph &graph)
 {
-    TypeAnalyzer analyzer(graph);
+    TypeAnalyzer analyzer(mir, graph);
 
     if (!analyzer.analyze())
         return false;
 
     return true;
 }
 
 bool
--- a/js/src/ion/IonAnalysis.h
+++ b/js/src/ion/IonAnalysis.h
@@ -17,23 +17,23 @@ namespace ion {
 
 class MIRGenerator;
 class MIRGraph;
 
 bool
 SplitCriticalEdges(MIRGraph &graph);
 
 bool
-EliminatePhis(MIRGraph &graph);
+EliminatePhis(MIRGenerator *mir, MIRGraph &graph);
 
 bool
-EliminateDeadCode(MIRGraph &graph);
+EliminateDeadCode(MIRGenerator *mir, MIRGraph &graph);
 
 bool
-ApplyTypeInformation(MIRGraph &graph);
+ApplyTypeInformation(MIRGenerator *mir, MIRGraph &graph);
 
 bool
 RenumberBlocks(MIRGraph &graph);
 
 bool
 BuildDominatorTree(MIRGraph &graph);
 
 bool
--- a/js/src/ion/LICM.cpp
+++ b/js/src/ion/LICM.cpp
@@ -3,16 +3,17 @@
  *
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include <stdio.h>
 
 #include "Ion.h"
+#include "IonBuilder.h"
 #include "IonSpewer.h"
 #include "LICM.h"
 #include "MIR.h"
 #include "MIRGraph.h"
 
 using namespace js;
 using namespace js::ion;
 
@@ -69,18 +70,18 @@ ion::ExtractLinearInequality(MTest *test
     }
 
     *plhs = lsum;
     *prhs = rsum.term;
 
     return true;
 }
 
-LICM::LICM(MIRGraph &graph)
-  : graph(graph)
+LICM::LICM(MIRGenerator *mir, MIRGraph &graph)
+  : mir(mir), graph(graph)
 {
 }
 
 bool
 LICM::analyze()
 {
     IonSpew(IonSpew_LICM, "Beginning LICM pass.");
 
@@ -88,33 +89,34 @@ LICM::analyze()
     for (ReversePostorderIterator i(graph.rpoBegin()); i != graph.rpoEnd(); i++) {
         MBasicBlock *header = *i;
 
         // Skip non-headers and self-loops.
         if (!header->isLoopHeader() || header->numPredecessors() < 2)
             continue;
 
         // Attempt to optimize loop.
-        Loop loop(header->backedge(), header, graph);
+        Loop loop(mir, header->backedge(), header, graph);
 
         Loop::LoopReturn lr = loop.init();
         if (lr == Loop::LoopReturn_Error)
             return false;
         if (lr == Loop::LoopReturn_Skip)
             continue;
 
         if (!loop.optimize())
             return false;
     }
 
     return true;
 }
 
-Loop::Loop(MBasicBlock *footer, MBasicBlock *header, MIRGraph &graph)
-  : graph(graph),
+Loop::Loop(MIRGenerator *mir, MBasicBlock *footer, MBasicBlock *header, MIRGraph &graph)
+  : mir(mir),
+    graph(graph),
     footer_(footer),
     header_(header)
 {
     preLoop_ = header_->getPredecessor(0);
 }
 
 Loop::LoopReturn
 Loop::init()
@@ -176,16 +178,19 @@ bool
 Loop::optimize()
 {
     InstructionQueue invariantInstructions;
     InstructionQueue boundsChecks;
 
     IonSpew(IonSpew_LICM, "These instructions are in the loop: ");
 
     while (!worklist_.empty()) {
+        if (mir->shouldCancel("LICM (worklist)"))
+            return false;
+
         MInstruction *ins = popFromWorklist();
 
         IonSpewHeader(IonSpew_LICM);
 
         if (IonSpewEnabled(IonSpew_LICM)) {
             ins->printName(IonSpewFile);
             fprintf(IonSpewFile, " <- ");
             ins->printOpcode(IonSpewFile);
--- a/js/src/ion/LICM.h
+++ b/js/src/ion/LICM.h
@@ -20,44 +20,46 @@ namespace ion {
 class MIRGraph;
 class MBasicBlock;
 
 typedef Vector<MBasicBlock*, 1, IonAllocPolicy> BlockQueue;
 typedef Vector<MInstruction*, 1, IonAllocPolicy> InstructionQueue;
 
 class LICM
 {
+    MIRGenerator *mir;
     MIRGraph &graph;
 
   public:
-    LICM(MIRGraph &graph);
+    LICM(MIRGenerator *mir, MIRGraph &graph);
     bool analyze();
 };
 
 // Extract a linear inequality holding when a boolean test goes in the
 // specified direction, of the form 'lhs + lhsN <= rhs' (or >=).
 bool
 ExtractLinearInequality(MTest *test, BranchDirection direction,
                         LinearSum *plhs, MDefinition **prhs, bool *plessEqual);
 
 class Loop
 {
+    MIRGenerator *mir;
     MIRGraph &graph;
 
   public:
     // Loop code may return three values:
     enum LoopReturn {
         LoopReturn_Success,
         LoopReturn_Error, // Compilation failure.
         LoopReturn_Skip   // The loop is not suitable for LICM, but there is no error.
     };
 
   public:
     // A loop is constructed on a backedge found in the control flow graph.
-    Loop(MBasicBlock *header, MBasicBlock *footer, MIRGraph &graph);
+    Loop(MIRGenerator *mir, MBasicBlock *header, MBasicBlock *footer, MIRGraph &graph);
 
     // Initializes the loop, finds all blocks and instructions contained in the loop.
     LoopReturn init();
 
     // Identifies hoistable loop invariant instructions and moves them out of the loop.
     bool optimize();
 
   private:
--- a/js/src/ion/LinearScan.cpp
+++ b/js/src/ion/LinearScan.cpp
@@ -3,16 +3,17 @@
  *
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include <limits.h>
 #include "BitSet.h"
 #include "LinearScan.h"
+#include "IonBuilder.h"
 #include "IonSpewer.h"
 #include "LIR-inl.h"
 
 using namespace js;
 using namespace js::ion;
 
 static bool
 UseCompatibleWith(const LUse *use, LAllocation alloc)
@@ -384,16 +385,19 @@ LinearScanAllocator::createDataStructure
     if (!vregs.init(lir->mir(), graph.numVirtualRegisters()))
         return false;
 
     if (!insData.init(lir->mir(), graph.numInstructions()))
         return false;
 
     // Build virtual register objects
     for (size_t i = 0; i < graph.numBlocks(); i++) {
+        if (mir->shouldCancel("LSRA create data structures (main loop)"))
+            return false;
+
         LBlock *block = graph.getBlock(i);
         for (LInstructionIterator ins = block->begin(); ins != block->end(); ins++) {
             for (size_t j = 0; j < ins->numDefs(); j++) {
                 LDefinition *def = ins->getDef(j);
                 if (def->policy() != LDefinition::PASSTHROUGH) {
                     uint32 reg = def->virtualRegister();
                     if (!vregs[reg].init(reg, block, *ins, def, /* isTemp */ false))
                         return false;
@@ -476,16 +480,19 @@ bool
 LinearScanAllocator::buildLivenessInfo()
 {
     Vector<MBasicBlock *, 1, SystemAllocPolicy> loopWorkList;
     BitSet *loopDone = BitSet::New(graph.numBlockIds());
     if (!loopDone)
         return false;
 
     for (size_t i = graph.numBlocks(); i > 0; i--) {
+        if (mir->shouldCancel("LSRA Build Liveness Info (main loop)"))
+            return false;
+
         LBlock *block = graph.getBlock(i - 1);
         MBasicBlock *mblock = block->mir();
 
         BitSet *live = BitSet::New(graph.numVirtualRegisters());
         if (!live)
             return false;
         liveIn[mblock->id()] = live;
 
@@ -788,16 +795,19 @@ LinearScanAllocator::allocateRegisters()
     }
 
     // Iterate through all intervals in ascending start order.
     CodePosition prevPosition = CodePosition::MIN;
     while ((current = unhandled.dequeue()) != NULL) {
         JS_ASSERT(current->getAllocation()->isUse());
         JS_ASSERT(current->numRanges() > 0);
 
+        if (mir->shouldCancel("LSRA Allocate Registers (main loop)"))
+            return false;
+
         CodePosition position = current->start();
         Requirement *req = current->requirement();
         Requirement *hint = current->hint();
 
         IonSpew(IonSpew_RegAlloc, "Processing %d = [%u, %u] (pri=%d)",
                 current->reg() ? current->reg()->reg() : 0, current->start().pos(),
                 current->end().pos(), current->requirement()->priority());
 
@@ -919,16 +929,19 @@ LinearScanAllocator::allocateRegisters()
  * The algorithm is based on the one published in "Linear Scan Register
  * Allocation on SSA Form" by C. Wimmer et al., for which the full citation
  * appears above.
  */
 bool
 LinearScanAllocator::resolveControlFlow()
 {
     for (size_t i = 0; i < graph.numBlocks(); i++) {
+        if (mir->shouldCancel("LSRA Resolve Control Flow (main loop)"))
+            return false;
+
         LBlock *successor = graph.getBlock(i);
         MBasicBlock *mSuccessor = successor->mir();
         if (mSuccessor->numPredecessors() < 1)
             continue;
 
         // Resolve phis to moves
         for (size_t j = 0; j < successor->numPhis(); j++) {
             LPhi *phi = successor->getPhi(j);
@@ -1002,16 +1015,19 @@ LinearScanAllocator::moveInputAlloc(Code
  * corresponding to the appropriate interval.
  */
 bool
 LinearScanAllocator::reifyAllocations()
 {
     // Iterate over each interval, ensuring that definitions are visited before uses.
     for (size_t j = 1; j < graph.numVirtualRegisters(); j++) {
         VirtualRegister *reg = &vregs[j];
+        if (mir->shouldCancel("LSRA Reification (main loop)"))
+            return false;
+
     for (size_t k = 0; k < reg->numIntervals(); k++) {
         LiveInterval *interval = reg->getInterval(k);
         JS_ASSERT(reg == interval->reg());
         if (!interval->numRanges())
             continue;
 
         UsePositionIterator usePos(interval->usesBegin());
         for (; usePos != interval->usesEnd(); usePos++) {
@@ -1967,41 +1983,59 @@ LinearScanAllocator::go()
 {
     IonSpew(IonSpew_RegAlloc, "Beginning register allocation");
 
     IonSpew(IonSpew_RegAlloc, "Beginning creation of initial data structures");
     if (!createDataStructures())
         return false;
     IonSpew(IonSpew_RegAlloc, "Creation of initial data structures completed");
 
+    if (mir->shouldCancel("LSRA Create Data Structures"))
+        return false;
+
     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;
+
     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;
+
     IonSpew(IonSpew_RegAlloc, "Beginning register allocation reification");
     if (!reifyAllocations())
         return false;
     IonSpew(IonSpew_RegAlloc, "Register allocation reification complete");
 
+    if (mir->shouldCancel("LSRA Reification"))
+        return false;
+
     IonSpew(IonSpew_RegAlloc, "Beginning safepoint population.");
     if (!populateSafepoints())
         return false;
     IonSpew(IonSpew_RegAlloc, "Safepoint population complete.");
 
+    if (mir->shouldCancel("LSRA Safepoints"))
+        return false;
+
     IonSpew(IonSpew_RegAlloc, "Register allocation complete");
 
     return true;
 }
 
 void
 LinearScanAllocator::setIntervalRequirement(LiveInterval *interval)
 {
--- a/js/src/ion/LinearScan.h
+++ b/js/src/ion/LinearScan.h
@@ -586,16 +586,17 @@ class LinearScanAllocator
         void enqueueAtHead(LiveInterval *interval);
 
         void assertSorted();
 
         LiveInterval *dequeue();
     };
 
     // Context
+    MIRGenerator *mir;
     LIRGenerator *lir;
     LIRGraph &graph;
 
     // Computed inforamtion
     BitSet **liveIn;
     VirtualRegisterMap vregs;
     InstructionDataMap insData;
     FixedArityList<LiveInterval *, AnyRegister::Total> fixedIntervals;
@@ -680,18 +681,19 @@ class LinearScanAllocator
     CodePosition inputOf(LInstruction *ins) {
         return CodePosition(ins->id(), CodePosition::INPUT);
     }
 #ifdef JS_NUNBOX32
     VirtualRegister *otherHalfOfNunbox(VirtualRegister *vreg);
 #endif
 
   public:
-    LinearScanAllocator(LIRGenerator *lir, LIRGraph &graph)
-      : lir(lir),
+    LinearScanAllocator(MIRGenerator *mir, LIRGenerator *lir, LIRGraph &graph)
+      : mir(mir),
+        lir(lir),
         graph(graph),
         allRegisters_(RegisterSet::All())
     {
         if (FramePointer != InvalidReg && lir->mir()->instrumentedProfiling())
             allRegisters_.take(AnyRegister(FramePointer));
     }
 
     bool go();
--- a/js/src/ion/Lowering.cpp
+++ b/js/src/ion/Lowering.cpp
@@ -2118,16 +2118,19 @@ LIRGenerator::precreatePhi(LBlock *block
     return true;
 }
 
 bool
 LIRGenerator::generate()
 {
     // Create all blocks and prep all phis beforehand.
     for (ReversePostorderIterator block(graph.rpoBegin()); block != graph.rpoEnd(); block++) {
+        if (gen->shouldCancel("Lowering (preparation loop)"))
+            return false;
+
         current = LBlock::New(*block);
         if (!current)
             return false;
         if (!lirGraph_.addBlock(current))
             return false;
         block->assignLir(current);
 
         // For each MIR phi, add LIR phis as appropriate. We'll fill in their
@@ -2138,16 +2141,19 @@ LIRGenerator::generate()
             for (int i = 0; i < numPhis; i++) {
                 if (!precreatePhi(block->lir(), *phi))
                     return false;
             }
         }
     }
 
     for (ReversePostorderIterator block(graph.rpoBegin()); block != graph.rpoEnd(); block++) {
+        if (gen->shouldCancel("Lowering (main loop)"))
+            return false;
+
         if (!visitBlock(*block))
             return false;
     }
 
     if (graph.osrBlock())
         lirGraph_.setOsrBlock(graph.osrBlock()->lir());
 
     lirGraph_.setArgumentSlotCount(maxargslots_);
--- a/js/src/ion/MIRGenerator.h
+++ b/js/src/ion/MIRGenerator.h
@@ -59,25 +59,34 @@ class MIRGenerator
     bool errored() const {
         return error_;
     }
 
     bool instrumentedProfiling() {
         return compartment->rt->spsProfiler.enabled();
     }
 
+    // Whether the main thread is trying to cancel this build.
+    bool shouldCancel(const char *why) {
+        return cancelBuild_;
+    }
+    void cancel() {
+        cancelBuild_ = 1;
+    }
+
   public:
     JSCompartment *compartment;
 
   protected:
     CompileInfo *info_;
     TempAllocator *temp_;
     JSFunction *fun_;
     uint32 nslots_;
     MIRGraph *graph_;
     bool error_;
+    size_t cancelBuild_;
 };
 
 } // namespace ion
 } // namespace js
 
 #endif // jsion_mirgen_h__
 
--- a/js/src/ion/MIRGraph.cpp
+++ b/js/src/ion/MIRGraph.cpp
@@ -17,17 +17,18 @@ using namespace js;
 using namespace js::ion;
 
 MIRGenerator::MIRGenerator(JSCompartment *compartment,
                            TempAllocator *temp, MIRGraph *graph, CompileInfo *info)
   : compartment(compartment),
     info_(info),
     temp_(temp),
     graph_(graph),
-    error_(false)
+    error_(false),
+    cancelBuild_(0)
 { }
 
 bool
 MIRGenerator::abortFmt(const char *message, va_list ap)
 {
     IonSpewVA(IonSpew_Abort, message, ap);
     error_ = true;
     return false;
--- a/js/src/ion/ValueNumbering.cpp
+++ b/js/src/ion/ValueNumbering.cpp
@@ -1,25 +1,27 @@
 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=4 sw=4 et tw=99:
  *
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "Ion.h"
+#include "IonBuilder.h"
 #include "IonSpewer.h"
 #include "CompileInfo.h"
 #include "ValueNumbering.h"
 
 using namespace js;
 using namespace js::ion;
 
-ValueNumberer::ValueNumberer(MIRGraph &graph, bool optimistic)
-  : graph_(graph),
+ValueNumberer::ValueNumberer(MIRGenerator *mir, MIRGraph &graph, bool optimistic)
+  : mir(mir),
+    graph_(graph),
     pessimisticPass_(!optimistic),
     count_(0)
 { }
 
 uint32
 ValueNumberer::lookupValue(MDefinition *ins)
 {
 
@@ -140,16 +142,18 @@ ValueNumberer::computeValueNumbers()
     // any graph with back-edges, but is much faster to perform.
 
     IonSpew(IonSpew_GVN, "Numbering instructions");
 
     if (!values.init())
         return false;
     // Stick a VN object onto every mdefinition
     for (ReversePostorderIterator block(graph_.rpoBegin()); block != graph_.rpoEnd(); block++) {
+        if (mir->shouldCancel("Value Numbering (preparation loop"))
+            return false;
         for (MDefinitionIterator iter(*block); iter; iter++)
             iter->setValueNumberData(new ValueNumberData);
         MControlInstruction *jump = block->lastIns();
         jump->setValueNumberData(new ValueNumberData);
     }
 
     // Assign unique value numbers if pessimistic.
     // It might be productive to do this in the MDefinition constructor or
@@ -184,16 +188,18 @@ ValueNumberer::computeValueNumbers()
                 }
             }
             if (!debugCount)
                 IonSpew(IonSpew_GVN, "\tNone");
             JS_ASSERT(debugCount == count_);
         }
 #endif
         for (ReversePostorderIterator block(graph_.rpoBegin()); block != graph_.rpoEnd(); block++) {
+            if (mir->shouldCancel("Value Numbering (main loop)"))
+                return false;
             for (MDefinitionIterator iter(*block); iter; ) {
 
                 if (!isMarked(*iter)) {
                     iter++;
                     continue;
                 }
 
                 JS_ASSERT_IF(!pessimisticPass_, count_ > 0);
@@ -320,16 +326,18 @@ ValueNumberer::eliminateRedundancies()
         if (block->immediateDominator() == block) {
             if (!worklist.append(block))
                 return false;
         }
     }
 
     // Starting from each self-dominating block, traverse the CFG in pre-order.
     while (!worklist.empty()) {
+        if (mir->shouldCancel("Value Numbering (eliminate loop)"))
+            return false;
         MBasicBlock *block = worklist.popCopy();
 
         IonSpew(IonSpew_GVN, "Looking at block %d", block->id());
 
         // Add all immediate dominators to the front of the worklist.
         for (size_t i = 0; i < block->numImmediatelyDominatedBlocks(); i++) {
             if (!worklist.append(block->getImmediatelyDominatedBlock(i)))
                 return false;
--- a/js/src/ion/ValueNumbering.h
+++ b/js/src/ion/ValueNumbering.h
@@ -72,23 +72,24 @@ class ValueNumberer
     void markBlock(MBasicBlock *block);
     void setClass(MDefinition *toSet, MDefinition *representative);
 
   public:
     static MDefinition *findSplit(MDefinition *);
     void breakClass(MDefinition*);
 
   protected:
+    MIRGenerator *mir;
     MIRGraph &graph_;
     ValueMap values;
     bool pessimisticPass_;
     size_t count_;
 
   public:
-    ValueNumberer(MIRGraph &graph, bool optimistic);
+    ValueNumberer(MIRGenerator *mir, MIRGraph &graph, bool optimistic);
     bool analyze();
 };
 
 class ValueNumberData : public TempObject {
 
     friend void ValueNumberer::breakClass(MDefinition*);
     friend MDefinition *ValueNumberer::findSplit(MDefinition*);
     uint32 number;
--- a/js/src/jsworkers.cpp
+++ b/js/src/jsworkers.cpp
@@ -93,18 +93,22 @@ js::CancelOffThreadIonCompile(JSCompartm
             state.ionWorklist[i--] = state.ionWorklist.back();
             state.ionWorklist.popBack();
         }
     }
 
     /* Wait for in progress entries to finish up. */
     for (size_t i = 0; i < state.numThreads; i++) {
         const WorkerThread &helper = state.threads[i];
-        while (helper.ionScript && CompiledScriptMatches(compartment, script, helper.ionScript))
+        while (helper.ionBuilder &&
+               CompiledScriptMatches(compartment, script, helper.ionBuilder->script()))
+        {
+            helper.ionBuilder->cancel();
             state.wait(WorkerThreadState::MAIN);
+        }
     }
 
     ion::OffThreadCompilationVector &compilations = ion->finishedOffThreadCompilations();
 
     /* Cancel code generation for any completed entries. */
     for (size_t i = 0; i < compilations.length(); i++) {
         ion::IonBuilder *builder = compilations[i];
         if (CompiledScriptMatches(compartment, script, builder->script())) {
@@ -271,42 +275,41 @@ WorkerThread::ThreadMain(void *arg)
 
 void
 WorkerThread::threadLoop()
 {
     WorkerThreadState &state = *runtime->workerThreadState;
     state.lock();
 
     while (true) {
-        JS_ASSERT(!ionScript);
+        JS_ASSERT(!ionBuilder);
 
         while (state.ionWorklist.empty()) {
             if (terminate) {
                 state.unlock();
                 return;
             }
             state.wait(WorkerThreadState::WORKER);
         }
 
-        ion::IonBuilder *builder = state.ionWorklist.popCopy();
-        ionScript = builder->script();
+        ionBuilder = state.ionWorklist.popCopy();
 
-        JS_ASSERT(ionScript->ion == ION_COMPILING_SCRIPT);
+        JS_ASSERT(ionBuilder->script()->ion == ION_COMPILING_SCRIPT);
 
         state.unlock();
 
         {
-            ion::IonContext ictx(NULL, ionScript->compartment(), &builder->temp());
-            builder->backgroundCompiledLir = ion::CompileBackEnd(builder);
+            ion::IonContext ictx(NULL, ionBuilder->script()->compartment(), &ionBuilder->temp());
+            ionBuilder->backgroundCompiledLir = ion::CompileBackEnd(ionBuilder);
         }
 
         state.lock();
 
-        ionScript = NULL;
-        FinishOffThreadIonCompile(builder);
+        FinishOffThreadIonCompile(ionBuilder);
+        ionBuilder = NULL;
 
         /*
          * Notify the main thread in case it is waiting for the compilation to
          * finish.
          */
         state.notify(WorkerThreadState::MAIN);
 
         /*
--- a/js/src/jsworkers.h
+++ b/js/src/jsworkers.h
@@ -82,18 +82,18 @@ class WorkerThreadState
 struct WorkerThread
 {
     JSRuntime *runtime;
     PRThread *thread;
 
     /* Indicate to an idle thread that it should finish executing. */
     bool terminate;
 
-    /* Any script currently being compiled for Ion on this thread. */
-    JSScript *ionScript;
+    /* Any builder currently being compiled by Ion on this thread. */
+    ion::IonBuilder *ionBuilder;
 
     void destroy();
 
     static void ThreadMain(void *arg);
     void threadLoop();
 };
 
 #endif /* JS_THREADSAFE && JS_ION */