Backout 4225ee7e35a0.
authorDavid Anderson <danderson@mozilla.com>
Wed, 22 Aug 2012 22:23:11 -0700
changeset 104997 2a3e2f6288b707c410975b3684c49f7d87a56581
parent 104996 fbba6ea2b0767b8cfff5113838f3528b2b15b1e2
child 104998 a04e6a1fe90c01c3e80679c0540c44bfac6e8d20
push id50
push usershu@rfrn.org
push dateTue, 28 Aug 2012 03:10:20 +0000
milestone17.0a1
backs out4225ee7e35a0dcc37c4db156e878740ec88d33d5
Backout 4225ee7e35a0.
js/src/Makefile.in
js/src/ion/CodeGenerator.cpp
js/src/ion/CompilerRoot.h
js/src/ion/Ion.cpp
js/src/ion/Ion.h
js/src/ion/IonAllocPolicy.h
js/src/ion/IonBuilder.cpp
js/src/ion/IonBuilder.h
js/src/ion/IonCompartment.h
js/src/ion/IonFrames.cpp
js/src/ion/IonSpewer.cpp
js/src/jsapi.cpp
js/src/jsapi.h
js/src/jscntxt.cpp
js/src/jscntxt.h
js/src/jsgc.cpp
js/src/jsinfer.cpp
js/src/jsinferinlines.h
js/src/jslock.h
js/src/jsscript.h
js/src/jsworkers.cpp
js/src/jsworkers.h
js/src/methodjit/Compiler.cpp
js/src/methodjit/Compiler.h
js/src/methodjit/MethodJIT.cpp
js/src/methodjit/MethodJIT.h
js/src/methodjit/MonoIC.cpp
js/src/methodjit/StubCalls.cpp
js/src/methodjit/StubCalls.h
js/src/shell/js.cpp
--- a/js/src/Makefile.in
+++ b/js/src/Makefile.in
@@ -101,17 +101,16 @@ CPPSRCS		= \
 		jsreflect.cpp \
 		jsscope.cpp \
 		jsscript.cpp \
 		jsstr.cpp \
 		jstypedarray.cpp \
 		jsutil.cpp \
 		jswatchpoint.cpp \
 		jsweakmap.cpp \
-		jsworkers.cpp \
 		jswrapper.cpp \
 		jsxml.cpp \
 		prmjtime.cpp \
 		sharkctl.cpp \
 		ArgumentsObject.cpp \
 		ScopeObject.cpp \
 		Debugger.cpp \
 		GlobalObject.cpp \
--- a/js/src/ion/CodeGenerator.cpp
+++ b/js/src/ion/CodeGenerator.cpp
@@ -2798,17 +2798,17 @@ CodeGenerator::generate()
     IonCode *code = linker.newCode(cx);
     if (!code)
         return false;
 
     // We encode safepoints after the OSI-point offsets have been determined.
     encodeSafepoints();
 
     JSScript *script = gen->info().script();
-    JS_ASSERT(!script->hasIonScript());
+    JS_ASSERT(!script->ion);
 
     uint32 scriptFrameSize = frameClass_ == FrameSizeClass::None()
                            ? frameDepth_
                            : FrameSizeClass::FromDepth(frameDepth_).frameSize();
 
     script->ion = IonScript::New(cx, slots, scriptFrameSize, snapshots_.size(),
                                  bailouts_.length(), graph.numConstants(),
                                  safepointIndices_.length(), osiIndices_.length(),
--- a/js/src/ion/CompilerRoot.h
+++ b/js/src/ion/CompilerRoot.h
@@ -30,31 +30,49 @@ class CompilerRoot : public CompilerRoot
     {
         if (ptr)
             setRoot(ptr);
     }
 
   public:
     // Sets the pointer and inserts into root list. The pointer becomes read-only.
     void setRoot(T root) {
-        JS::CompilerRootNode *&rootList = GetIonContext()->temp->rootList();
+        JSRuntime *rt = root->compartment()->rt;
 
         JS_ASSERT(!ptr);
         ptr = root;
-        next = rootList;
-        rootList = this;
+        next = rt->ionCompilerRootList;
+        rt->ionCompilerRootList = this;
     }
 
   public:
     operator T () const { return static_cast<T>(ptr); }
     T operator ->() const { return static_cast<T>(ptr); }
 };
 
 typedef CompilerRoot<JSObject*>   CompilerRootObject;
 typedef CompilerRoot<JSFunction*> CompilerRootFunction;
 typedef CompilerRoot<PropertyName*> CompilerRootPropertyName;
 typedef CompilerRoot<Value> CompilerRootValue;
 
+// Automatically clears the compiler root list when compilation finishes.
+class AutoCompilerRoots
+{
+    JSRuntime *rt_;
+
+  public:
+    AutoCompilerRoots(JSRuntime *rt)
+      : rt_(rt)
+    {
+        JS_ASSERT(rt_->ionCompilerRootList == NULL);
+    }
+
+    ~AutoCompilerRoots()
+    {
+        rt_->ionCompilerRootList = NULL;
+    }
+};
+
 } // namespace ion
 } // namespace js
 
 #endif // jsion_ion_gc_h__
 
--- a/js/src/ion/Ion.cpp
+++ b/js/src/ion/Ion.cpp
@@ -13,17 +13,16 @@
 #include "LIR.h"
 #include "AliasAnalysis.h"
 #include "LICM.h"
 #include "ValueNumbering.h"
 #include "EdgeCaseAnalysis.h"
 #include "RangeAnalysis.h"
 #include "LinearScan.h"
 #include "jscompartment.h"
-#include "jsworkers.h"
 #include "IonCompartment.h"
 #include "CodeGenerator.h"
 
 #if defined(JS_CPU_X86)
 # include "x86/Lowering-x86.h"
 #elif defined(JS_CPU_X64)
 # include "x64/Lowering-x64.h"
 #elif defined(JS_CPU_ARM)
@@ -143,39 +142,16 @@ IonCompartment::initialize(JSContext *cx
     functionWrappers_ = cx->new_<VMWrapperMap>(cx);
     if (!functionWrappers_ || !functionWrappers_->init())
         return false;
 
     return true;
 }
 
 void
-ion::FinishOffThreadBuilder(IonBuilder *builder)
-{
-    if (builder->script->isIonCompilingOffThread()) {
-        types::TypeCompartment &types = builder->script->compartment()->types;
-        builder->recompileInfo.compilerOutput(types)->invalidate();
-        builder->script->ion = NULL;
-    }
-    Foreground::delete_(builder->temp().lifoAlloc());
-}
-
-static inline void
-FinishAllOffThreadCompilations(IonCompartment *ion)
-{
-    OffThreadCompilationVector &compilations = ion->finishedOffThreadCompilations();
-
-    for (size_t i = 0; i < compilations.length(); i++) {
-        IonBuilder *builder = compilations[i];
-        FinishOffThreadBuilder(builder);
-    }
-    compilations.clear();
-}
-
-void
 IonCompartment::mark(JSTracer *trc, JSCompartment *compartment)
 {
     // This function marks Ion code objects that must be kept alive if there is
     // any Ion code currently running. These pointers are marked at the start
     // of incremental GC. Entering Ion code in the middle of an incremental GC
     // triggers a read barrier on both these pointers, so they will still be
     // marked in that case.
 
@@ -198,20 +174,16 @@ IonCompartment::mark(JSTracer *trc, JSCo
 
     // These must be available if we could be running JIT code; they are not
     // traced as normal through IonCode or IonScript objects
     if (mustMarkEnterJIT)
         MarkIonCodeRoot(trc, enterJIT_.unsafeGet(), "enterJIT");
 
     // functionWrappers_ are not marked because this is a WeakCache of VM
     // function implementations.
-
-    // Cancel any active or pending off thread compilations.
-    CancelOffThreadIonCompile(compartment, NULL);
-    FinishAllOffThreadCompilations(this);
 }
 
 void
 IonCompartment::sweep(FreeOp *fop)
 {
     if (enterJIT_ && !IsIonCodeMarked(enterJIT_.unsafeGet()))
         enterJIT_ = NULL;
     if (bailoutHandler_ && !IsIonCodeMarked(bailoutHandler_.unsafeGet()))
@@ -723,26 +695,26 @@ ion::ToggleBarriers(JSCompartment *comp,
         if (script->hasIonScript())
             script->ion->toggleBarriers(needs);
     }
 }
 
 namespace js {
 namespace ion {
 
-bool
-CompileBackEnd(IonBuilder *builder)
+static bool
+BuildMIR(IonBuilder &builder, MIRGraph &graph)
 {
+    if (!builder.build())
+        return false;
     IonSpewPass("BuildSSA");
     // Note: don't call AssertGraphCoherency before SplitCriticalEdges,
     // the graph is not in RPO at this point.
 
-    MIRGraph &graph = builder->graph();
-
-    if (!SplitCriticalEdges(builder, graph))
+    if (!SplitCriticalEdges(&builder, graph))
         return false;
     IonSpewPass("Split Critical Edges");
     AssertGraphCoherency(graph);
 
     if (!RenumberBlocks(graph))
         return false;
     IonSpewPass("Renumber Blocks");
     AssertGraphCoherency(graph);
@@ -836,214 +808,102 @@ CompileBackEnd(IonBuilder *builder)
     // 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 false;
     IonSpewPass("Bounds Check Elimination");
     AssertGraphCoherency(graph);
 
-    LIRGraph *lir = builder->temp().lifoAlloc()->new_<LIRGraph>(graph);
-    if (!lir)
-        return false;
+    return true;
+}
 
-    LIRGenerator lirgen(builder, graph, *lir);
+static bool
+GenerateCode(IonBuilder &builder, MIRGraph &graph)
+{
+    LIRGraph lir(graph);
+    LIRGenerator lirgen(&builder, graph, lir);
     if (!lirgen.generate())
         return false;
     IonSpewPass("Generate LIR");
 
     if (js_IonOptions.lsra) {
-        LinearScanAllocator regalloc(&lirgen, *lir);
+        LinearScanAllocator regalloc(&lirgen, lir);
         if (!regalloc.go())
             return false;
         IonSpewPass("Allocate Registers", &regalloc);
     }
 
-    builder->lir = lir;
+    CodeGenerator codegen(&builder, lir);
+    if (!codegen.generate())
+        return false;
+    // No spew: graph not changed.
+
     return true;
 }
 
-class AutoDestroyAllocator
-{
-    LifoAlloc *alloc;
-
-  public:
-    AutoDestroyAllocator(LifoAlloc *alloc) : alloc(alloc) {}
-
-    void cancel()
-    {
-        alloc = NULL;
-    }
-
-    ~AutoDestroyAllocator()
-    {
-        if (alloc)
-            Foreground::delete_(alloc);
-    }
-};
-
 /* static */ bool
-TestCompiler(IonBuilder *builder, MIRGraph *graph, AutoDestroyAllocator &autoDestroy)
+TestCompiler(IonBuilder &builder, MIRGraph &graph)
 {
-    JS_ASSERT(!builder->script->ion);
-    JSContext *cx = GetIonContext()->cx;
-
-    IonSpewNewFunction(graph, builder->script);
-
-    if (!builder->build())
-        return false;
-    builder->clearForBackEnd();
-
-    if (js_IonOptions.parallelCompilation) {
-        builder->script->ion = ION_COMPILING_SCRIPT;
+    IonSpewNewFunction(&graph, builder.script);
 
-        if (!StartOffThreadIonCompile(cx, builder))
-            return false;
-
-        // The allocator and associated data will be destroyed after being
-        // processed in the finishedOffThreadCompilations list.
-        autoDestroy.cancel();
-
-        return true;
-    }
-
-    if (!CompileBackEnd(builder))
+    if (!BuildMIR(builder, graph))
         return false;
 
-    CodeGenerator codegen(builder, *builder->lir);
-    if (!codegen.generate())
+    if (!GenerateCode(builder, graph))
         return false;
 
-    if (builder->script->hasIonScript()) {
-        // After Ion has finished compiling a script, remove any JITScripts it
-        // has to force continued execution in Ion code.
-        mjit::ReleaseScriptCodeFromVM(cx, builder->script);
-    }
-
     IonSpewEndFunction();
 
     return true;
 }
 
-void
-AttachFinishedCompilations(JSContext *cx)
-{
-    IonCompartment *ion = cx->compartment->ionCompartment();
-    if (!ion)
-        return;
-
-    AutoLockWorkerThreadState lock(cx->runtime);
-
-    OffThreadCompilationVector &compilations = ion->finishedOffThreadCompilations();
-
-    // Incorporate any off thread compilations which have finished, failed or
-    // have been cancelled, and destroy JM jitcode for any compilations which
-    // succeeded, to allow entering the Ion code from the interpreter.
-    while (!compilations.empty()) {
-        IonBuilder *builder = compilations.popCopy();
-
-        if (builder->lir) {
-            JSScript *script = builder->script;
-            IonContext ictx(cx, cx->compartment, &builder->temp());
-
-            CodeGenerator codegen(builder, *builder->lir);
-
-            types::AutoEnterCompilation enterCompiler(cx, types::AutoEnterCompilation::Ion);
-            enterCompiler.initExisting(builder->recompileInfo);
-
-            bool success;
-            {
-                // Release the worker thread lock and root the compiler for GC.
-                AutoTempAllocatorRooter root(cx, &builder->temp());
-                AutoUnlockWorkerThreadState unlock(cx->runtime);
-                success = codegen.generate();
-            }
-
-            if (success) {
-                if (script->hasIonScript())
-                    mjit::ReleaseScriptCodeFromVM(cx, script);
-            } else {
-                // Silently ignore OOM during code generation, we're at an
-                // operation callback and can't propagate failures.
-                cx->clearPendingException();
-            }
-        }
-
-        FinishOffThreadBuilder(builder);
-    }
-
-    compilations.clear();
-}
-
-static const size_t BUILDER_LIFO_ALLOC_PRIMARY_CHUNK_SIZE = 1 << 12;
-
-template <bool Compiler(IonBuilder *, MIRGraph *, AutoDestroyAllocator &)>
+template <bool Compiler(IonBuilder &, MIRGraph &)>
 static bool
 IonCompile(JSContext *cx, JSScript *script, JSFunction *fun, jsbytecode *osrPc, bool constructing)
 {
 #if JS_TRACE_LOGGING
     AutoTraceLog logger(TraceLogging::defaultLogger(),
                         TraceLogging::ION_COMPILE_START,
                         TraceLogging::ION_COMPILE_STOP,
                         script);
 #endif
 
-    LifoAlloc *alloc = cx->new_<LifoAlloc>(BUILDER_LIFO_ALLOC_PRIMARY_CHUNK_SIZE);
-    if (!alloc)
-        return false;
-
-    AutoDestroyAllocator autoDestroy(alloc);
-
-    TempAllocator *temp = alloc->new_<TempAllocator>(alloc);
-    if (!temp)
-        return false;
-
-    IonContext ictx(cx, cx->compartment, temp);
+    TempAllocator temp(&cx->tempLifoAlloc());
+    IonContext ictx(cx, cx->compartment, &temp);
 
     if (!cx->compartment->ensureIonCompartmentExists(cx))
         return false;
 
-    MIRGraph *graph = alloc->new_<MIRGraph>(temp);
-    CompileInfo *info = alloc->new_<CompileInfo>(script, fun, osrPc, constructing);
+    MIRGraph graph(&temp);
+    CompileInfo *info = cx->tempLifoAlloc().new_<CompileInfo>(script, fun, osrPc, constructing);
     if (!info)
         return false;
 
     types::AutoEnterTypeInference enter(cx, true);
     TypeInferenceOracle oracle;
 
     if (!oracle.init(cx, script))
         return false;
 
     AutoFlushCache afc("IonCompile");
 
     types::AutoEnterCompilation enterCompiler(cx, types::AutoEnterCompilation::Ion);
     enterCompiler.init(script, false, 0);
-
-    AutoTempAllocatorRooter root(cx, temp);
+    AutoCompilerRoots roots(script->compartment()->rt);
 
-    IonBuilder *builder = alloc->new_<IonBuilder>(cx, temp, graph, &oracle, info);
-    if (!Compiler(builder, graph, autoDestroy)) {
+    IonBuilder builder(cx, &temp, &graph, &oracle, info);
+    if (!Compiler(builder, graph)) {
         IonSpew(IonSpew_Abort, "IM Compilation failed.");
         return false;
     }
 
     return true;
 }
 
-bool
-TestIonCompile(JSContext *cx, JSScript *script, JSFunction *fun, jsbytecode *osrPc, bool constructing)
-{
-    if (!IonCompile<TestCompiler>(cx, script, fun, osrPc, constructing)) {
-        if (!cx->isExceptionPending())
-            ForbidCompilation(script);
-        return false;
-    }
-    return true;
-}
-
 static bool
 CheckFrame(StackFrame *fp)
 {
     if (fp->isEvalFrame()) {
         // Eval frames are not yet supported. Supporting this will require new
         // logic in pushBailoutFrame to deal with linking prev.
         // Additionally, JSOP_DEFVAR support will require baking in isEvalFrame().
         IonSpew(IonSpew_Abort, "eval frame");
@@ -1114,17 +974,17 @@ CheckScriptSize(JSScript *script)
     if (numLocalsAndArgs > MAX_LOCALS_AND_ARGS) {
         IonSpew(IonSpew_Abort, "Too many locals and arguments (%u)", numLocalsAndArgs);
         return false;
     }
 
     return true;
 }
 
-template <bool Compiler(IonBuilder *, MIRGraph *, AutoDestroyAllocator &)>
+template <bool Compiler(IonBuilder &, MIRGraph &)>
 static MethodStatus
 Compile(JSContext *cx, JSScript *script, JSFunction *fun, jsbytecode *osrPc, bool constructing)
 {
     JS_ASSERT(ion::IsEnabled(cx));
     JS_ASSERT_IF(osrPc != NULL, (JSOp)*osrPc == JSOP_LOOPENTRY);
 
     if (cx->compartment->debugMode()) {
         IonSpew(IonSpew_Abort, "debugging");
@@ -1169,20 +1029,16 @@ ion::CanEnterAtBranch(JSContext *cx, JSS
 {
     JS_ASSERT(ion::IsEnabled(cx));
     JS_ASSERT((JSOp)*pc == JSOP_LOOPENTRY);
 
     // Skip if the script has been disabled.
     if (script->ion == ION_DISABLED_SCRIPT)
         return Method_Skipped;
 
-    // Skip if the script is being compiled off thread.
-    if (script->ion == ION_COMPILING_SCRIPT)
-        return Method_Skipped;
-
     // Skip if the code is expected to result in a bailout.
     if (script->ion && script->ion->bailoutExpected())
         return Method_Skipped;
 
     // Optionally ignore on user request.
     if (!js_IonOptions.osr)
         return Method_Skipped;
 
@@ -1218,20 +1074,16 @@ MethodStatus
 ion::CanEnter(JSContext *cx, JSScript *script, StackFrame *fp, bool newType)
 {
     JS_ASSERT(ion::IsEnabled(cx));
 
     // Skip if the script has been disabled.
     if (script->ion == ION_DISABLED_SCRIPT)
         return Method_Skipped;
 
-    // Skip if the script is being compiled off thread.
-    if (script->ion == ION_COMPILING_SCRIPT)
-        return Method_Skipped;
-
     // Skip if the code is expected to result in a bailout.
     if (script->ion && script->ion->bailoutExpected())
         return Method_Skipped;
 
     // If constructing, allocate a new |this| object before building Ion.
     // Creating |this| is done before building Ion because it may change the
     // type information and invalidate compilation results.
     if (fp->isConstructing() && fp->functionThis().isPrimitive()) {
@@ -1522,23 +1374,16 @@ InvalidateActivation(FreeOp *fop, uint8 
     }
 
     IonSpew(IonSpew_Invalidate, "END invalidating activation");
 }
 
 void
 ion::InvalidateAll(FreeOp *fop, JSCompartment *c)
 {
-    if (!c->ionCompartment())
-        return;
-
-    CancelOffThreadIonCompile(c, NULL);
-
-    FinishAllOffThreadCompilations(c->ionCompartment());
-
     for (IonActivationIterator iter(fop->runtime()); iter.more(); ++iter) {
         if (iter.activation()->compartment() == c) {
             AutoFlushCache afc ("InvalidateAll", c->ionCompartment());
             IonSpew(IonSpew_Invalidate, "Invalidating all frames for GC");
             InvalidateActivation(fop, iter.top(), true);
         }
     }
 }
--- a/js/src/ion/Ion.h
+++ b/js/src/ion/Ion.h
@@ -62,21 +62,16 @@ struct IonOptions
     // Default: true
     bool edgeCaseAnalysis;
 
     // Toggles whether Range Analysis is used.
     //
     // Default: false
     bool rangeAnalysis;
 
-    // Toggles whether compilation occurs off the main thread.
-    //
-    // Default: true iff there are at least two CPUs available
-    bool parallelCompilation;
-
     // How many invocations or loop iterations are needed before functions
     // are compiled.
     //
     // Default: 10,240
     uint32 usesBeforeCompile;
 
     // How many invocations or loop iterations are needed before functions
     // are compiled when JM is disabled.
@@ -140,44 +135,40 @@ struct IonOptions
 
     void setEagerCompilation() {
         eagerCompilation = true;
         usesBeforeCompile = usesBeforeCompileNoJaeger = 0;
 
         // Eagerly inline calls to improve test coverage.
         usesBeforeInlining = 0;
         smallFunctionUsesBeforeInlining = 0;
-
-        parallelCompilation = false;
     }
 
     IonOptions()
       : gvn(true),
         gvnIsOptimistic(true),
         licm(true),
         osr(true),
         limitScriptSize(true),
         lsra(true),
         inlining(true),
         edgeCaseAnalysis(true),
         rangeAnalysis(false),
-        parallelCompilation(false),
         usesBeforeCompile(10240),
         usesBeforeCompileNoJaeger(40),
         usesBeforeInlining(usesBeforeCompile),
         maxStackArgs(4096),
         maxInlineDepth(3),
         smallFunctionMaxBytecodeLength(100),
         smallFunctionUsesBeforeInlining(usesBeforeInlining / 4),
         polyInlineMax(4),
         inlineMaxTotalBytecodeLength(800),
         eagerCompilation(false),
         slowCallLimit(512)
-    {
-    }
+    { }
 };
 
 enum MethodStatus
 {
     Method_Error,
     Method_CantCompile,
     Method_Skipped,
     Method_Compiled
@@ -234,23 +225,16 @@ void Invalidate(types::TypeCompartment &
                 const Vector<types::RecompileInfo> &invalid, bool resetUses = true);
 void Invalidate(JSContext *cx, const Vector<types::RecompileInfo> &invalid, bool resetUses = true);
 bool Invalidate(JSContext *cx, JSScript *script, bool resetUses = true);
 
 void MarkFromIon(JSCompartment *comp, Value *vp);
 
 void ToggleBarriers(JSCompartment *comp, bool needs);
 
-class IonBuilder;
-
-bool CompileBackEnd(IonBuilder *builder);
-void AttachFinishedCompilations(JSContext *cx);
-void FinishOffThreadBuilder(IonBuilder *builder);
-bool TestIonCompile(JSContext *cx, JSScript *script, JSFunction *fun, jsbytecode *osrPc, bool constructing);
-
 static inline bool IsEnabled(JSContext *cx)
 {
     return cx->hasRunOption(JSOPTION_ION) && cx->typeInferenceEnabled();
 }
 
 void ForbidCompilation(JSScript *script);
 uint32_t UsesBeforeIonRecompile(JSScript *script, jsbytecode *pc);
 
--- a/js/src/ion/IonAllocPolicy.h
+++ b/js/src/ion/IonAllocPolicy.h
@@ -17,24 +17,20 @@
 namespace js {
 namespace ion {
 
 class TempAllocator
 {
     LifoAlloc *lifoAlloc_;
     void *mark_;
 
-    // Linked list of GCThings rooted by this allocator.
-    JS::CompilerRootNode *rootList_;
-
   public:
     TempAllocator(LifoAlloc *lifoAlloc)
       : lifoAlloc_(lifoAlloc),
-        mark_(lifoAlloc->mark()),
-        rootList_(NULL)
+        mark_(lifoAlloc->mark())
     { }
 
     ~TempAllocator()
     {
         lifoAlloc_->release(mark_);
     }
 
     void *allocateInfallible(size_t bytes)
@@ -52,47 +48,23 @@ class TempAllocator
         return p;
     }
 
     LifoAlloc *lifoAlloc()
     {
         return lifoAlloc_;
     }
 
-    JS::CompilerRootNode *&rootList()
-    {
-        return rootList_;
-    }
-
     bool ensureBallast() {
         // Most infallible Ion allocations are small, so we use a ballast of
         // ~16K for now.
         return lifoAlloc_->ensureUnusedApproximate(16 * 1024);
     }
 };
 
-// Stack allocated rooter for all roots associated with a TempAllocator
-class AutoTempAllocatorRooter : private AutoGCRooter
-{
-  public:
-    explicit AutoTempAllocatorRooter(JSContext *cx, TempAllocator *temp
-                                     JS_GUARD_OBJECT_NOTIFIER_PARAM)
-      : AutoGCRooter(cx, IONALLOC), temp(temp)
-    {
-        JS_GUARD_OBJECT_NOTIFIER_INIT;
-    }
-
-    friend void AutoGCRooter::trace(JSTracer *trc);
-    void trace(JSTracer *trc);
-
-  private:
-    TempAllocator *temp;
-    JS_DECL_USE_GUARD_OBJECT_NOTIFIER
-};
-
 class IonAllocPolicy
 {
   public:
     void *malloc_(size_t bytes) {
         return GetIonContext()->temp->allocate(bytes);
     }
     void *realloc_(void *p, size_t oldBytes, size_t bytes) {
         void *n = malloc_(bytes);
--- a/js/src/ion/IonBuilder.cpp
+++ b/js/src/ion/IonBuilder.cpp
@@ -22,18 +22,16 @@
 
 using namespace js;
 using namespace js::ion;
 
 IonBuilder::IonBuilder(JSContext *cx, TempAllocator *temp, MIRGraph *graph,
                        TypeOracle *oracle, CompileInfo *info, size_t inliningDepth, uint32 loopDepth)
   : MIRGenerator(cx->compartment, temp, graph, info),
     script(info->script()),
-    recompileInfo(cx->compartment->types.compiledInfo),
-    lir(NULL),
     cx(cx),
     loopDepth_(loopDepth),
     callerResumePoint_(NULL),
     callerBuilder_(NULL),
     oracle(oracle),
     inliningDepth(inliningDepth),
     failedBoundsCheck_(script->failedBoundsCheck),
     lazyArguments_(NULL)
--- a/js/src/ion/IonBuilder.h
+++ b/js/src/ion/IonBuilder.h
@@ -12,18 +12,16 @@
 // JSScript.
 
 #include "MIR.h"
 #include "MIRGraph.h"
 
 namespace js {
 namespace ion {
 
-class LIRGraph;
-
 class IonBuilder : public MIRGenerator
 {
     enum ControlStatus {
         ControlStatus_Error,
         ControlStatus_Ended,        // There is no continuation/join point.
         ControlStatus_Joined,       // Created a join node.
         ControlStatus_Jumped,       // Parsing another branch at the same level.
         ControlStatus_None          // No control flow.
@@ -422,22 +420,16 @@ class IonBuilder : public MIRGenerator
                            types::TypeSet *types, types::TypeSet *barrier,
                            MBasicBlock *bottom,
                            Vector<MDefinition *, 8, IonAllocPolicy> &retvalDefns);
 
   public:
     // A builder is inextricably tied to a particular script.
     JSScript * const script;
 
-    // Compilation index for this attempt.
-    types::RecompileInfo const recompileInfo;
-
-    // If off thread compilation is successful, final LIR is attached here.
-    LIRGraph *lir;
-
     void clearForBackEnd();
 
   private:
     JSContext *cx;
 
     jsbytecode *pc;
     MBasicBlock *current;
     uint32 loopDepth_;
--- a/js/src/ion/IonCompartment.h
+++ b/js/src/ion/IonCompartment.h
@@ -18,34 +18,31 @@ namespace js {
 namespace ion {
 
 class FrameSizeClass;
 
 typedef void (*EnterIonCode)(void *code, int argc, Value *argv, StackFrame *fp,
                              CalleeToken calleeToken, Value *vp);
 
 class IonActivation;
-class IonBuilder;
-
-typedef Vector<IonBuilder*, 0, SystemAllocPolicy> OffThreadCompilationVector;
 
 class IonCompartment
 {
     typedef WeakCache<const VMFunction *, ReadBarriered<IonCode> > VMWrapperMap;
 
     friend class IonActivation;
 
     // Executable allocator (owned by the runtime).
     JSC::ExecutableAllocator *execAlloc_;
 
     // Trampoline for entering JIT code. Contains OSR prologue.
     ReadBarriered<IonCode> enterJIT_;
 
     // Vector mapping frame class sizes to bailout tables.
-    Vector<ReadBarriered<IonCode>, 4, SystemAllocPolicy> bailoutTables_;
+    js::Vector<ReadBarriered<IonCode>, 4, SystemAllocPolicy> bailoutTables_;
 
     // Generic bailout table; used if the bailout table overflows.
     ReadBarriered<IonCode> bailoutHandler_;
 
     // Argument-rectifying thunk, in the case of insufficient arguments passed
     // to a function call site. Pads with |undefined|.
     ReadBarriered<IonCode> argumentsRectifier_;
 
@@ -53,41 +50,31 @@ class IonCompartment
     ReadBarriered<IonCode> invalidator_;
 
     // Thunk that calls the GC pre barrier.
     ReadBarriered<IonCode> preBarrier_;
 
     // Map VMFunction addresses to the IonCode of the wrapper.
     VMWrapperMap *functionWrappers_;
 
-    // Any scripts for which off thread compilation has successfully finished,
-    // failed, or been cancelled. All off thread compilations which are started
-    // will eventually appear in this list asynchronously. Protected by the
-    // runtime's analysis lock.
-    OffThreadCompilationVector finishedOffThreadCompilations_;
-
     // Keep track of memoryregions that are going to be flushed.
-    AutoFlushCache *flusher_;
+    js::ion::AutoFlushCache *flusher_;
 
   private:
     IonCode *generateEnterJIT(JSContext *cx);
     IonCode *generateReturnError(JSContext *cx);
     IonCode *generateArgumentsRectifier(JSContext *cx);
     IonCode *generateBailoutTable(JSContext *cx, uint32 frameClass);
     IonCode *generateBailoutHandler(JSContext *cx);
     IonCode *generateInvalidator(JSContext *cx);
     IonCode *generatePreBarrier(JSContext *cx);
 
   public:
     IonCode *generateVMWrapper(JSContext *cx, const VMFunction &f);
 
-    OffThreadCompilationVector &finishedOffThreadCompilations() {
-        return finishedOffThreadCompilations_;
-    }
-
   public:
     bool initialize(JSContext *cx);
     IonCompartment();
     ~IonCompartment();
 
     void mark(JSTracer *trc, JSCompartment *compartment);
     void sweep(FreeOp *fop);
 
--- a/js/src/ion/IonFrames.cpp
+++ b/js/src/ion/IonFrames.cpp
@@ -616,19 +616,20 @@ MarkIonActivation(JSTracer *trc, const I
 void
 ion::MarkIonActivations(JSRuntime *rt, JSTracer *trc)
 {
     for (IonActivationIterator activations(rt); activations.more(); ++activations)
         MarkIonActivation(trc, activations);
 }
 
 void
-ion::AutoTempAllocatorRooter::trace(JSTracer *trc)
+ion::MarkIonCompilerRoots(JSTracer *trc)
 {
-    for (CompilerRootNode *root = temp->rootList(); root != NULL; root = root->next)
+    JSRuntime *rt = trc->runtime;
+    for (CompilerRootNode *root = rt->ionCompilerRootList; root != NULL; root = root->next)
         gc::MarkGCThingRoot(trc, root->address(), "ion-compiler-root");
 }
 
 void
 ion::GetPcScript(JSContext *cx, JSScript **scriptRes, jsbytecode **pcRes)
 {
     JS_ASSERT(cx->fp()->beginsIonActivation());
     IonSpew(IonSpew_Snapshots, "Recover PC & Script from the last frame.");
--- a/js/src/ion/IonSpewer.cpp
+++ b/js/src/ion/IonSpewer.cpp
@@ -2,17 +2,16 @@
  * 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/. */
 
 #ifdef DEBUG
 
-#include "Ion.h"
 #include "IonSpewer.h"
 
 #ifndef ION_SPEW_DIR
 # if defined(_WIN32)
 #  define ION_SPEW_DIR ""
 # else
 #  define ION_SPEW_DIR "/tmp/"
 # endif
@@ -38,39 +37,35 @@ void
 ion::EnableIonDebugLogging()
 {
     ionspewer.init();
 }
 
 void
 ion::IonSpewNewFunction(MIRGraph *graph, JSScript *function)
 {
-    if (!js_IonOptions.parallelCompilation)
-        ionspewer.beginFunction(graph, function);
+    ionspewer.beginFunction(graph, function);
 }
 
 void
 ion::IonSpewPass(const char *pass)
 {
-    if (!js_IonOptions.parallelCompilation)
-        ionspewer.spewPass(pass);
+    ionspewer.spewPass(pass);
 }
 
 void
 ion::IonSpewPass(const char *pass, LinearScanAllocator *ra)
 {
-    if (!js_IonOptions.parallelCompilation)
-        ionspewer.spewPass(pass, ra);
+    ionspewer.spewPass(pass, ra);
 }
 
 void
 ion::IonSpewEndFunction()
 {
-    if (!js_IonOptions.parallelCompilation)
-        ionspewer.endFunction();
+    ionspewer.endFunction();
 }
 
 
 IonSpewer::~IonSpewer()
 {
     if (!inited_)
         return;
 
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -43,17 +43,16 @@
 #include "jsopcode.h"
 #include "jsprobes.h"
 #include "jsproxy.h"
 #include "jsscope.h"
 #include "jsscript.h"
 #include "jsstr.h"
 #include "prmjtime.h"
 #include "jsweakmap.h"
-#include "jsworkers.h"
 #include "jswrapper.h"
 #include "jstypedarray.h"
 #include "jsxml.h"
 
 #include "builtin/Eval.h"
 #include "builtin/MapObject.h"
 #include "builtin/RegExp.h"
 #include "builtin/ParallelArray.h"
@@ -860,16 +859,17 @@ JSRuntime::JSRuntime()
     noGCOrAllocationCheck(0),
 #endif
     inOOMReport(0),
     jitHardening(false),
     ionTop(NULL),
     ionJSContext(NULL),
     ionStackLimit(0),
     ionActivation(NULL),
+    ionCompilerRootList(NULL),
     ionReturnOverride_(MagicValue(JS_ARG_POISON))
 {
     /* Initialize infallibly first, so we can goto bad and JS_DestroyRuntime. */
     JS_INIT_CLIST(&contextList);
     JS_INIT_CLIST(&debuggerList);
 
     PodZero(&debugHooks);
     PodZero(&atomState);
@@ -923,20 +923,16 @@ JSRuntime::init(uint32_t maxbytes)
 
     if (!stackSpace.init())
         return false;
 
     if (!scriptFilenameTable.init())
         return false;
 
 #ifdef JS_THREADSAFE
-    workerThreadState = this->new_<WorkerThreadState>();
-    if (!workerThreadState || !workerThreadState->init(this))
-        return false;
-
     if (!sourceCompressorThread.init())
         return false;
 #endif
 
     if (!evalCache.init())
         return false;
 
     debugScopes = this->new_<DebugScopes>(this);
@@ -957,17 +953,16 @@ JSRuntime::~JSRuntime()
 
     /*
      * Even though all objects in the compartment are dead, we may have keep
      * some filenames around because of gcKeepAtoms.
      */
     FreeScriptFilenames(this);
 
 #ifdef JS_THREADSAFE
-    delete_(workerThreadState);
     sourceCompressorThread.finish();
 #endif
 
 #ifdef DEBUG
     /* Don't hurt everyone in leaky ol' Mozilla with a fatal JS_ASSERT! */
     if (!JS_CLIST_IS_EMPTY(&contextList)) {
         unsigned cxcount = 0;
         for (ContextIter acx(this); !acx.done(); acx.next()) {
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -1059,18 +1059,17 @@ class JS_PUBLIC_API(AutoGCRooter) {
         SHAPERANGE =  -20, /* js::Shape::Range::AutoRooter */
         STACKSHAPE =  -21, /* js::StackShape::AutoRooter */
         STACKBASESHAPE=-22,/* js::StackBaseShape::AutoRooter */
         BINDINGS =    -23, /* js::Bindings::AutoRooter */
         GETTERSETTER =-24, /* js::AutoRooterGetterSetter */
         REGEXPSTATICS=-25, /* js::RegExpStatics::AutoRooter */
         NAMEVECTOR =  -26, /* js::AutoNameVector */
         HASHABLEVALUE=-27,
-        IONMASM =     -28, /* js::ion::MacroAssembler */
-        IONALLOC =    -29  /* js::ion::AutoTempAllocatorRooter */
+        IONMASM =     -28  /* js::ion::MacroAssembler */
     };
 
   private:
     AutoGCRooter ** const stackTop;
 
     /* No copy or assignment semantics. */
     AutoGCRooter(AutoGCRooter &ida) MOZ_DELETE;
     void operator=(AutoGCRooter &ida) MOZ_DELETE;
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -37,18 +37,16 @@
 #include "jsmath.h"
 #include "jsnum.h"
 #include "jsobj.h"
 #include "jsopcode.h"
 #include "jspubtd.h"
 #include "jsscope.h"
 #include "jsscript.h"
 #include "jsstr.h"
-#include "jsworkers.h"
-#include "ion/Ion.h"
 #include "ion/IonFrames.h"
 
 #ifdef JS_METHODJIT
 # include "assembler/assembler/MacroAssembler.h"
 # include "methodjit/MethodJIT.h"
 #endif
 #include "gc/Marking.h"
 #include "js/MemoryMetrics.h"
@@ -386,20 +384,16 @@ js::DestroyContext(JSContext *cx, Destro
 
         /*
          * Dump remaining type inference results first. This printing
          * depends on atoms still existing.
          */
         for (CompartmentsIter c(rt); !c.done(); c.next())
             c->types.print(cx, false);
 
-        /* Off thread ion compilations depend on atoms still existing. */
-        for (CompartmentsIter c(rt); !c.done(); c.next())
-            CancelOffThreadIonCompile(c, NULL);
-
         /* Unpin all common atoms before final GC. */
         FinishCommonAtoms(rt);
 
         /* Clear debugging state to remove GC roots. */
         for (CompartmentsIter c(rt); !c.done(); c.next())
             c->clearTraps(rt->defaultFreeOp());
         JS_ClearAllWatchPoints(cx);
 
@@ -1012,22 +1006,16 @@ js_InvokeOperationCallback(JSContext *cx
 
     /* Reset Ion's stack limit. */
     cx->runtime->ionStackLimit = cx->runtime->nativeStackLimit;
 
     if (rt->gcIsNeeded)
         GCSlice(rt, GC_NORMAL, rt->gcTriggerReason);
 
     /*
-     * A worker thread may have set the callback after finishing an Ion
-     * compilation.
-     */
-    ion::AttachFinishedCompilations(cx);
-
-    /*
      * Important: Additional callbacks can occur inside the callback handler
      * if it re-enters the JS engine. The embedding must ensure that the
      * callback is disconnected before attempting such re-entry.
      */
     JSOperationCallback cb = cx->operationCallback;
     return !cb || cb(cx);
 }
 
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -72,17 +72,16 @@ class MathCache;
 
 namespace ion {
 class IonActivation;
 }
 
 class WeakMapBase;
 class InterpreterFrames;
 class DebugScopes;
-class WorkerThreadState;
 
 /*
  * GetSrcNote cache to avoid O(n^2) growth in finding a source note for a
  * given pc in a script. We use the script->code pointer to tag the cache,
  * instead of the script address itself, so that source notes are always found
  * by offset from the bytecode with which they were generated.
  */
 struct GSNCache {
@@ -785,18 +784,16 @@ struct JSRuntime : js::RuntimeFriendFiel
     void                *data;
 
     /* These combine to interlock the GC and new requests. */
     PRLock              *gcLock;
 
     js::GCHelperThread  gcHelperThread;
 
 #ifdef JS_THREADSAFE
-    js::WorkerThreadState *workerThreadState;
-
     js::SourceCompressorThread sourceCompressorThread;
 #endif
 
   private:
     js::FreeOp          defaultFreeOp_;
 
   public:
     js::FreeOp *defaultFreeOp() {
@@ -892,16 +889,19 @@ struct JSRuntime : js::RuntimeFriendFiel
     // aligned to an Ion exit frame.
     uint8_t             *ionTop;
     JSContext           *ionJSContext;
     uintptr_t            ionStackLimit;
 
     // This points to the most recent Ion activation running on the thread.
     js::ion::IonActivation  *ionActivation;
 
+    // Linked list of GCThings rooted for the current compilation.
+    JS::CompilerRootNode *ionCompilerRootList;
+
   private:
     // In certain cases, we want to optimize certain opcodes to typed instructions,
     // to avoid carrying an extra register to feed into an unbox. Unfortunately,
     // that's not always possible. For example, a GetPropertyCacheT could return a
     // typed double, but if it takes its out-of-line path, it could return an
     // object, and trigger invalidation. The invalidation bailout will consider the
     // return value to be a double, and create a garbage Value.
     //
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -2489,23 +2489,16 @@ AutoGCRooter::trace(JSTracer *trc)
       }
 
       case IONMASM: {
 #ifdef JS_ION
         static_cast<js::ion::MacroAssembler::AutoRooter *>(this)->masm()->trace(trc);
 #endif
         return;
       }
-
-      case IONALLOC: {
-#ifdef JS_ION
-        static_cast<js::ion::AutoTempAllocatorRooter *>(this)->trace(trc);
-#endif
-        return;
-      }
     }
 
     JS_ASSERT(tag >= 0);
     MarkValueRootRange(trc, tag, static_cast<AutoArrayRooter *>(this)->array,
                        "JS::AutoArrayRooter.array");
 }
 
 /* static */ void
@@ -2625,16 +2618,17 @@ MarkRuntime(JSTracer *trc, bool useSaved
         mjit::ExpandInlineFrames(c);
 #endif
 
     rt->stackSpace.markAndClobber(trc);
     rt->debugScopes->mark(trc);
 	
 #ifdef JS_ION
     ion::MarkIonActivations(rt, trc);
+    ion::MarkIonCompilerRoots(trc);
 #endif
 
     for (CompartmentsIter c(rt); !c.done(); c.next())
         c->mark(trc);
 
     /* The embedding can register additional roots here. */
     if (JSTraceDataOp op = rt->gcBlackRootsTraceOp)
         (*op)(trc, rt->gcBlackRootsData);
@@ -2919,17 +2913,17 @@ AssertBackgroundSweepingFinshed(JSRuntim
             JS_ASSERT(!c->arenas.arenaListsToSweep[i]);
             JS_ASSERT(c->arenas.doneBackgroundFinalize(AllocKind(i)));
         }
     }
 }
 #endif
 
 #ifdef JS_THREADSAFE
-unsigned
+static unsigned
 GetCPUCount()
 {
     static unsigned ncpus = 0;
     if (ncpus == 0) {
 # ifdef XP_WIN
         SYSTEM_INFO sysinfo;
         GetSystemInfo(&sysinfo);
         ncpus = unsigned(sysinfo.dwNumberOfProcessors);
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -15,17 +15,16 @@
 #include "jsmath.h"
 #include "jsnum.h"
 #include "jsobj.h"
 #include "jsscript.h"
 #include "jscntxt.h"
 #include "jsscope.h"
 #include "jsstr.h"
 #include "jsiter.h"
-#include "jsworkers.h"
 
 #include "ion/Ion.h"
 #include "ion/IonCompartment.h"
 #include "frontend/TokenStream.h"
 #include "gc/Marking.h"
 #include "js/MemoryMetrics.h"
 #include "methodjit/MethodJIT.h"
 #include "methodjit/Retcon.h"
@@ -2249,19 +2248,16 @@ TypeCompartment::nukeTypes(FreeOp *fop)
 void
 TypeCompartment::addPendingRecompile(JSContext *cx, const RecompileInfo &info)
 {
     CompilerOutput *co = info.compilerOutput(cx);
 
     if (co->pendingRecompilation)
         return;
 
-    if (co->isValid())
-        CancelOffThreadIonCompile(cx->compartment, co->script);
-
     if (!co->isValid()) {
         JS_ASSERT(co->script == NULL);
         return;
     }
 
 #ifdef JS_METHODJIT
     mjit::JITScript *jit = co->script->getJIT(co->constructing, co->barriers);
     bool hasJITCode = jit && jit->chunkDescriptor(co->chunkIndex).chunk;
@@ -2318,18 +2314,16 @@ TypeCompartment::addPendingRecompile(JSC
             if (!chunk)
                 continue;
 
             addPendingRecompile(cx, chunk->recompileInfo);
         }
     }
 
 # ifdef JS_ION
-    CancelOffThreadIonCompile(cx->compartment, script);
-
     if (script->hasIonScript())
         addPendingRecompile(cx, script->ionScript()->recompileInfo());
 # endif
 #endif
 }
 
 void
 TypeCompartment::monitorBytecode(JSContext *cx, JSScript *script, uint32_t offset,
@@ -5850,41 +5844,43 @@ TypeCompartment::sweep(FreeOp *fop)
     pendingCapacity = 0;
 
     sweepCompilerOutputs(fop);
 }
 
 void
 TypeCompartment::sweepCompilerOutputs(FreeOp *fop)
 {
+
     if (constrainedOutputs) {
         bool isCompiling = compiledInfo.outputIndex != RecompileInfo::NoCompilerRunning;
         if (isCompiling && !compartment()->activeAnalysis)
         {
 #if DEBUG
             for (unsigned i = 0; i < constrainedOutputs->length(); i++) {
                 CompilerOutput &co = (*constrainedOutputs)[i];
                 JS_ASSERT(!co.isValid());
             }
 #endif
-
             fop->delete_(constrainedOutputs);
             constrainedOutputs = NULL;
         } else {
             // A Compilation is running and the AutoEnterCompilation class has
             // captured an index into the constrained outputs vector and
             // potentially created multiple types with this index.  Instead, we
             // invalidate all compilations except the one running now.
             size_t len = constrainedOutputs->length();
+            if (isCompiling) {
+                len--;
+                JS_ASSERT(compiledInfo.outputIndex == len);
+            }
             for (unsigned i = 0; i < len; i++) {
-                if (i != compiledInfo.outputIndex) {
-                    CompilerOutput &co = (*constrainedOutputs)[i];
-                    JS_ASSERT(!co.isValid());
-                    co.invalidate();
-                }
+                CompilerOutput &co = (*constrainedOutputs)[i];
+                JS_ASSERT(!co.isValid());
+                co.invalidate();
             }
         }
     }
 
     if (pendingRecompiles) {
         fop->delete_(pendingRecompiles);
         pendingRecompiles = NULL;
     }
--- a/js/src/jsinferinlines.h
+++ b/js/src/jsinferinlines.h
@@ -75,23 +75,20 @@ CompilerOutput::isValid() const
         if (!chunk)
             return false;
         JS_ASSERT(this == chunk->recompileInfo.compilerOutput(types));
         return true;
     }
 #endif
 
     if (isIon()) {
-        if (script->hasIonScript()) {
-            JS_ASSERT(this == script->ion->recompileInfo().compilerOutput(types));
-            return true;
-        }
-        if (script->isIonCompilingOffThread())
-            return true;
-        return false;
+        if (!script->hasIonScript())
+            return false;
+        JS_ASSERT(this == script->ion->recompileInfo().compilerOutput(types));
+        return true;
     }
     return false;
 }
 
 inline CompilerOutput*
 RecompileInfo::compilerOutput(TypeCompartment &types) const
 {
     return &(*types.constrainedOutputs)[outputIndex];
@@ -370,23 +367,16 @@ struct AutoEnterCompilation
         if (info.outputIndex >= RecompileInfo::NoCompilerRunning)
             return false;
 
         if (!cx->compartment->types.constrainedOutputs->append(co))
             return false;
         return true;
     }
 
-    void initExisting(RecompileInfo oldInfo)
-    {
-        // Initialize the active compilation index from that produced during a
-        // previous compilation, for finishing an off thread compilation.
-        info = oldInfo;
-    }
-
     ~AutoEnterCompilation()
     {
         CompilerOutput *co = info.compilerOutput(cx);
         co->pendingRecompilation = false;
         if (!co->isValid())
             co->invalidate();
 
         info.outputIndex = RecompileInfo::NoCompilerRunning;
--- a/js/src/jslock.h
+++ b/js/src/jslock.h
@@ -16,20 +16,16 @@
 # include "prthread.h"
 # include "prinit.h"
 
 # define JS_ATOMIC_INCREMENT(p)      PR_ATOMIC_INCREMENT((int32_t *)(p))
 # define JS_ATOMIC_DECREMENT(p)      PR_ATOMIC_DECREMENT((int32_t *)(p))
 # define JS_ATOMIC_ADD(p,v)          PR_ATOMIC_ADD((int32_t *)(p), (int32_t)(v))
 # define JS_ATOMIC_SET(p,v)          PR_ATOMIC_SET((int32_t *)(p), (int32_t)(v))
 
-namespace js {
-    unsigned GetCPUCount();
-}
-
 #else  /* JS_THREADSAFE */
 
 typedef struct PRThread PRThread;
 typedef struct PRCondVar PRCondVar;
 typedef struct PRLock PRLock;
 
 # define JS_ATOMIC_INCREMENT(p)      (++*(p))
 # define JS_ATOMIC_DECREMENT(p)      (--*(p))
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -546,24 +546,21 @@ struct JSScript : public js::gc::Cell
 
     js::ion::IonScript *ion;          /* Information attached by Ion */
 
 #if defined(JS_METHODJIT) && JS_BITS_PER_WORD == 32
     void *padding_;
 #endif
 
     bool hasIonScript() const {
-        return ion && ion != ION_DISABLED_SCRIPT && ion != ION_COMPILING_SCRIPT;
+        return ion && ion != ION_DISABLED_SCRIPT;
     }
     bool canIonCompile() const {
         return ion != ION_DISABLED_SCRIPT;
     }
-    bool isIonCompilingOffThread() const {
-        return ion == ION_COMPILING_SCRIPT;
-    }
     js::ion::IonScript *ionScript() const {
         JS_ASSERT(hasIonScript());
         return ion;
     }
 
     /*
      * Original compiled function for the script, if it has a function.
      * NULL for global and eval scripts.
deleted file mode 100644
--- a/js/src/jsworkers.cpp
+++ /dev/null
@@ -1,294 +0,0 @@
-/* -*- Mode: c++; c-basic-offset: 4; tab-width: 40; indent-tabs-mode: nil -*- */
-/* vim: set ts=40 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 "jsworkers.h"
-
-#include "ion/IonBuilder.h"
-
-using namespace js;
-
-#ifdef JS_THREADSAFE
-
-bool
-js::StartOffThreadIonCompile(JSContext *cx, ion::IonBuilder *builder)
-{
-    WorkerThreadState &state = *cx->runtime->workerThreadState;
-
-    JS_ASSERT(state.numThreads);
-
-    AutoLockWorkerThreadState lock(cx->runtime);
-
-    if (!state.ionWorklist.append(builder))
-        return false;
-
-    state.notify(WorkerThreadState::WORKER);
-
-    return true;
-}
-
-/*
- * Move an IonBuilder for which compilation has either finished, failed, or
- * been cancelled into the Ion compartment's finished compilations list.
- * All off thread compilations which are started must eventually be finished.
- */
-static void
-FinishOffThreadIonCompile(ion::IonBuilder *builder)
-{
-    JSCompartment *compartment = builder->script->compartment();
-    JS_ASSERT(compartment->rt->workerThreadState->isLocked());
-
-    compartment->ionCompartment()->finishedOffThreadCompilations().append(builder);
-}
-
-static inline bool
-CompiledScriptMatches(JSCompartment *compartment, JSScript *script, JSScript *target)
-{
-    if (script)
-        return target == script;
-    return target->compartment() == compartment;
-}
-
-void
-js::CancelOffThreadIonCompile(JSCompartment *compartment, JSScript *script)
-{
-    WorkerThreadState &state = *compartment->rt->workerThreadState;
-
-    ion::IonCompartment *ion = compartment->ionCompartment();
-    if (!ion)
-        return;
-
-    AutoLockWorkerThreadState lock(compartment->rt);
-
-    /* Cancel any pending entries for which processing hasn't started. */
-    for (size_t i = 0; i < state.ionWorklist.length(); i++) {
-        ion::IonBuilder *builder = state.ionWorklist[i];
-        if (CompiledScriptMatches(compartment, script, builder->script)) {
-            FinishOffThreadIonCompile(builder);
-            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))
-            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)) {
-            ion::FinishOffThreadBuilder(builder);
-            compilations[i--] = compilations.back();
-            compilations.popBack();
-        }
-    }
-}
-
-bool
-WorkerThreadState::init(JSRuntime *rt)
-{
-    workerLock = PR_NewLock();
-    if (!workerLock)
-        return false;
-
-    mainWakeup = PR_NewCondVar(workerLock);
-    if (!mainWakeup)
-        return false;
-
-    helperWakeup = PR_NewCondVar(workerLock);
-    if (!helperWakeup)
-        return false;
-
-    numThreads = GetCPUCount() - 1;
-
-    threads = (WorkerThread*) rt->calloc_(sizeof(WorkerThread) * numThreads);
-    if (!threads) {
-        numThreads = 0;
-        return false;
-    }
-
-    for (size_t i = 0; i < numThreads; i++) {
-        WorkerThread &helper = threads[i];
-        helper.runtime = rt;
-        helper.thread = PR_CreateThread(PR_USER_THREAD,
-                                        WorkerThread::ThreadMain, &helper,
-                                        PR_PRIORITY_NORMAL, PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0);
-        if (!helper.thread) {
-            for (size_t j = 0; j < numThreads; j++)
-                threads[j].destroy();
-            Foreground::free_(threads);
-            threads = NULL;
-            numThreads = 0;
-            return false;
-        }
-    }
-
-    return true;
-}
-
-WorkerThreadState::~WorkerThreadState()
-{
-    /*
-     * Join created threads first, which needs locks and condition variables
-     * to be intact.
-     */
-    if (threads) {
-        for (size_t i = 0; i < numThreads; i++)
-            threads[i].destroy();
-        Foreground::free_(threads);
-    }
-
-    if (workerLock)
-        PR_DestroyLock(workerLock);
-
-    if (mainWakeup)
-        PR_DestroyCondVar(mainWakeup);
-
-    if (helperWakeup)
-        PR_DestroyCondVar(helperWakeup);
-}
-
-void
-WorkerThreadState::lock()
-{
-    JS_ASSERT(!isLocked());
-    PR_Lock(workerLock);
-#ifdef DEBUG
-    lockOwner = PR_GetCurrentThread();
-#endif
-}
-
-void
-WorkerThreadState::unlock()
-{
-    JS_ASSERT(isLocked());
-#ifdef DEBUG
-    lockOwner = NULL;
-#endif
-    PR_Unlock(workerLock);
-}
-
-#ifdef DEBUG
-bool
-WorkerThreadState::isLocked()
-{
-    return lockOwner == PR_GetCurrentThread();
-}
-#endif
-
-void
-WorkerThreadState::wait(CondVar which, uint32_t millis)
-{
-    JS_ASSERT(isLocked());
-#ifdef DEBUG
-    lockOwner = NULL;
-#endif
-    PR_WaitCondVar((which == MAIN) ? mainWakeup : helperWakeup,
-                   millis ? PR_MillisecondsToInterval(millis) : PR_INTERVAL_NO_TIMEOUT);
-#ifdef DEBUG
-    lockOwner = PR_GetCurrentThread();
-#endif
-}
-
-void
-WorkerThreadState::notify(CondVar which)
-{
-    JS_ASSERT(isLocked());
-    PR_NotifyAllCondVar((which == MAIN) ? mainWakeup : helperWakeup);
-}
-
-void
-WorkerThread::destroy()
-{
-    WorkerThreadState &state = *runtime->workerThreadState;
-
-    if (!thread)
-        return;
-
-    terminate = true;
-    {
-        AutoLockWorkerThreadState lock(runtime);
-        state.notify(WorkerThreadState::WORKER);
-    }
-    PR_JoinThread(thread);
-}
-
-/* static */
-void
-WorkerThread::ThreadMain(void *arg)
-{
-    PR_SetCurrentThreadName("Analysis Helper");
-    static_cast<WorkerThread *>(arg)->threadLoop();
-}
-
-void
-WorkerThread::threadLoop()
-{
-    WorkerThreadState &state = *runtime->workerThreadState;
-    state.lock();
-
-    while (true) {
-        JS_ASSERT(!ionScript);
-
-        while (state.ionWorklist.empty()) {
-            state.wait(WorkerThreadState::WORKER);
-            if (terminate) {
-                state.unlock();
-                return;
-            }
-        }
-
-        ion::IonBuilder *builder = state.ionWorklist.popCopy();
-        ionScript = builder->script;
-
-        JS_ASSERT(ionScript->ion == ION_COMPILING_SCRIPT);
-
-        state.unlock();
-
-        {
-            ion::IonContext ictx(NULL, ionScript->compartment(), &builder->temp());
-            ion::CompileBackEnd(builder);
-        }
-
-        state.lock();
-
-        ionScript = NULL;
-        FinishOffThreadIonCompile(builder);
-
-        /*
-         * Notify the main thread in case it is waiting for the compilation to
-         * finish.
-         */
-        state.notify(WorkerThreadState::MAIN);
-
-        /*
-         * Ping the main thread so that the compiled code can be incorporated
-         * at the next operation callback.
-         */
-        runtime->triggerOperationCallback();
-    }
-}
-
-#else /* JS_THREADSAFE */
-
-bool
-js::StartOffThreadIonCompile(JSContext *cx, ion::IonBuilder *builder)
-{
-    JS_NOT_REACHED("Off thread compilation not available in non-THREADSAFE builds");
-    return false;
-}
-
-void
-js::CancelOffThreadIonCompile(JSCompartment *compartment, JSScript *script)
-{
-}
-
-#endif /* JS_THREADSAFE */
deleted file mode 100644
--- a/js/src/jsworkers.h
+++ /dev/null
@@ -1,162 +0,0 @@
-//* -*- Mode: c++; c-basic-offset: 4; tab-width: 40; indent-tabs-mode: nil -*- */
-/* vim: set ts=40 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/. */
-
-/*
- * Definitions for managing off-main-thread work using a shared, per runtime
- * worklist. Worklist items are engine internal, and are distinct from e.g.
- * web workers.
- */
-
-#ifndef jsworkers_h___
-#define jsworkers_h___
-
-#include "jscntxt.h"
-#include "jslock.h"
-
-namespace js {
-
-namespace ion {
-  class IonBuilder;
-}
-
-#ifdef JS_THREADSAFE
-
-struct WorkerThread;
-
-/* Per-runtime state for off thread work items. */
-struct WorkerThreadState
-{
-    /* Available threads. */
-    WorkerThread *threads;
-    size_t numThreads;
-
-    enum CondVar {
-        MAIN,
-        WORKER
-    };
-
-    /* Shared worklist for helper threads. */
-    js::Vector<ion::IonBuilder*, 0, SystemAllocPolicy> ionWorklist;
-
-    WorkerThreadState() { PodZero(this); }
-    ~WorkerThreadState();
-
-    bool init(JSRuntime *rt);
-
-    void lock();
-    void unlock();
-
-#ifdef DEBUG
-    bool isLocked();
-#endif
-
-    void wait(CondVar which, uint32_t timeoutMillis = 0);
-    void notify(CondVar which);
-
-  private:
-
-    /*
-     * Lock protecting all mutable shared state accessed by helper threads, and
-     * used by all condition variables.
-     */
-    PRLock *workerLock;
-
-#ifdef DEBUG
-    PRThread *lockOwner;
-#endif
-
-    /* Condvar to notify the main thread that work has been completed. */
-    PRCondVar *mainWakeup;
-
-    /* Condvar to notify helper threads that they may be able to make progress. */
-    PRCondVar *helperWakeup;
-};
-
-/* Individual helper thread, one allocated per core. */
-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;
-
-    void destroy();
-
-    static void ThreadMain(void *arg);
-    void threadLoop();
-};
-
-#endif /* JS_THREADSAFE */
-
-/* Methods for interacting with worker threads. */
-
-/*
- * Schedule an Ion compilation for a script, given a builder which has been
- * generated and read everything needed from the VM state.
- */
-bool StartOffThreadIonCompile(JSContext *cx, ion::IonBuilder *builder);
-
-/*
- * Cancel a scheduled or in progress Ion compilation for script. If script is
- * NULL, all compilations for the compartment are cancelled.
- */
-void CancelOffThreadIonCompile(JSCompartment *compartment, JSScript *script);
-
-class AutoLockWorkerThreadState
-{
-    JSRuntime *rt;
-    JS_DECL_USE_GUARD_OBJECT_NOTIFIER
-
-  public:
-
-    AutoLockWorkerThreadState(JSRuntime *rt JS_GUARD_OBJECT_NOTIFIER_PARAM)
-      : rt(rt)
-    {
-#ifdef JS_THREADSAFE
-        rt->workerThreadState->lock();
-#endif
-        JS_GUARD_OBJECT_NOTIFIER_INIT;
-    }
-
-    ~AutoLockWorkerThreadState()
-    {
-#ifdef JS_THREADSAFE
-        rt->workerThreadState->unlock();
-#endif
-    }
-};
-
-class AutoUnlockWorkerThreadState
-{
-    JSRuntime *rt;
-    JS_DECL_USE_GUARD_OBJECT_NOTIFIER
-
-  public:
-
-    AutoUnlockWorkerThreadState(JSRuntime *rt JS_GUARD_OBJECT_NOTIFIER_PARAM)
-      : rt(rt)
-    {
-#ifdef JS_THREADSAFE
-        rt->workerThreadState->unlock();
-#endif
-        JS_GUARD_OBJECT_NOTIFIER_INIT;
-    }
-
-    ~AutoUnlockWorkerThreadState()
-    {
-#ifdef JS_THREADSAFE
-        rt->workerThreadState->lock();
-#endif
-    }
-};
-
-} /* namespace js */
-
-#endif // jsworkers_h___
--- a/js/src/methodjit/Compiler.cpp
+++ b/js/src/methodjit/Compiler.cpp
@@ -46,16 +46,22 @@ using namespace js::mjit::ic;
 using namespace js::analyze;
 
 #define RETURN_IF_OOM(retval)                                   \
     JS_BEGIN_MACRO                                              \
         if (oomInVector || masm.oom() || stubcc.masm.oom())     \
             return retval;                                      \
     JS_END_MACRO
 
+/*
+ * Number of times a script must be called or had a backedge before we try to
+ * inline its calls. This number should match IonMonkey's usesBeforeCompile.
+ */
+static const size_t USES_BEFORE_INLINING = 10240;
+
 mjit::Compiler::Compiler(JSContext *cx, JSScript *outerScript,
                          unsigned chunkIndex, bool isConstructing)
   : BaseCompiler(cx),
     outerScript(cx, outerScript),
     chunkIndex(chunkIndex),
     isConstructing(isConstructing),
     outerChunk(outerJIT()->chunkDescriptor(chunkIndex)),
     ssa(cx, outerScript),
@@ -93,16 +99,22 @@ mjit::Compiler::Compiler(JSContext *cx, 
     debugMode_(cx->compartment->debugMode()),
     inlining_(false),
     hasGlobalReallocation(false),
     oomInVector(false),
     overflowICSpace(false),
     gcNumber(cx->runtime->gcNumber),
     pcLengths(NULL)
 {
+    /* Once a script starts getting really hot we will inline calls in it. */
+    if (!debugMode() && cx->typeInferenceEnabled() && globalObj &&
+        (outerScript->getUseCount() >= USES_BEFORE_INLINING ||
+         cx->hasRunOption(JSOPTION_METHODJIT_ALWAYS))) {
+        inlining_ = true;
+    }
 }
 
 CompileStatus
 mjit::Compiler::compile()
 {
     JS_ASSERT(!outerChunkRef().chunk);
 
 #if JS_TRACE_LOGGING
@@ -944,21 +956,16 @@ IonGetsFirstChance(JSContext *cx, JSScri
     // If there's no way this script is going to be Ion compiled, let JM take over.
     if (!script->canIonCompile())
         return false;
 
     // If we cannot enter Ion because bailouts are expected, let JM take over.
     if (script->hasIonScript() && script->ion->bailoutExpected())
         return false;
 
-    // If ion compilation is pending or in progress on another thread, continue
-    // using JM until that compilation finishes.
-    if (script->ion == ION_COMPILING_SCRIPT)
-        return false;
-
     return true;
 #endif
     return false;
 }
 
 CompileStatus
 mjit::CanMethodJIT(JSContext *cx, JSScript *script, jsbytecode *pc,
                    bool construct, CompileRequest request, StackFrame *frame)
@@ -1024,16 +1031,29 @@ mjit::CanMethodJIT(JSContext *cx, JSScri
         jith->setValid(jit);
     } else {
         jit = jith->getValid();
     }
 
     unsigned chunkIndex = jit->chunkIndex(pc);
     ChunkDescriptor &desc = jit->chunkDescriptor(chunkIndex);
 
+    if (jit->mustDestroyEntryChunk) {
+        // We kept this chunk around so that Ion can get info from its caches.
+        // If we end up here, we decided not to use Ion so we can destroy the
+        // chunk now.
+        JS_ASSERT(jit->nchunks == 1);
+        jit->mustDestroyEntryChunk = false;
+
+        if (desc.chunk) {
+            jit->destroyChunk(cx->runtime->defaultFreeOp(), chunkIndex, /* resetUses = */ false);
+            return Compile_Skipped;
+        }
+    }
+
     if (desc.chunk)
         return Compile_Okay;
 
     if (!cx->hasRunOption(JSOPTION_METHODJIT_ALWAYS) &&
         ++desc.counter <= INFER_USES_BEFORE_COMPILE)
     {
         return Compile_Skipped;
     }
@@ -1205,17 +1225,17 @@ mjit::Compiler::generatePrologue()
         }
 
         if (isConstructing && !constructThis())
             return Compile_Error;
     }
 
     CompileStatus status = methodEntryHelper();
     if (status == Compile_Okay)
-        ionCompileHelper();
+        recompileCheckHelper();
 
     return status;
 }
 
 void
 mjit::Compiler::ensureDoubleArguments()
 {
     /* Convert integer arguments which were inferred as (int|double) to doubles. */
@@ -3244,17 +3264,17 @@ mjit::Compiler::generateMethod()
                 interruptCheckHelper();
           END_CASE(JSOP_LOOPHEAD)
 
           BEGIN_CASE(JSOP_LOOPENTRY)
             // Unlike JM, IonMonkey OSR enters loops at the LOOPENTRY op.
             // Insert the recompile check here so that we can immediately
             // enter Ion.
             if (loop)
-                ionCompileHelper();
+                recompileCheckHelper();
           END_CASE(JSOP_LOOPENTRY)
 
           BEGIN_CASE(JSOP_DEBUGGER)
           {
             prepareStubCall(Uses(0));
             masm.move(ImmPtr(PC), Registers::ArgReg1);
             INLINE_STUBCALL(stubs::DebuggerStatement, REJOIN_FALLTHROUGH);
           }
@@ -3946,71 +3966,66 @@ MaybeIonCompileable(JSContext *cx, JSScr
     if (script->isShortRunning())
         *recompileCheckForIon = false;
 
     return true;
 #endif
     return false;
 }
 
-#ifdef JS_ION
 void
-mjit::Compiler::ionCompileHelper()
+mjit::Compiler::recompileCheckHelper()
 {
-    if (debugMode() || !globalObj || !cx->typeInferenceEnabled())
+    if (inlining() || debugMode() || !globalObj || !cx->typeInferenceEnabled())
         return;
 
-    bool recompileCheckForIon = false;
-    if (!MaybeIonCompileable(cx, outerScript, &recompileCheckForIon))
+    bool recompileCheckForIon = true;
+    bool maybeIonCompileable = MaybeIonCompileable(cx, outerScript, &recompileCheckForIon);
+    bool hasFunctionCalls = analysis->hasFunctionCalls();
+
+    // Insert a recompile check if either:
+    // 1) IonMonkey is enabled, to optimize the function when it becomes hot.
+    // 2) The script contains function calls JM can inline.
+    if (!maybeIonCompileable && !hasFunctionCalls)
         return;
 
-    uint32_t minUses = ion::UsesBeforeIonRecompile(outerScript, PC);
-
-    uint32_t *useCountAddress = script->addressOfUseCount();
-    masm.add32(Imm32(1), AbsoluteAddress(useCountAddress));
-
-    // If we don't want to do a recompileCheck for Ion, then this just needs to
-    // increment the useCount so that we know when to recompile this function
-    // from an Ion call.  No need to call out to recompiler stub.
-    if (!recompileCheckForIon)
+    uint32_t minUses = USES_BEFORE_INLINING;
+
+#ifdef JS_ION
+    if (recompileCheckForIon)
+        minUses = ion::UsesBeforeIonRecompile(outerScript, PC);
+#endif
+
+    uint32_t *addr = script->addressOfUseCount();
+    masm.add32(Imm32(1), AbsoluteAddress(addr));
+
+    // If there are no function calls, and we don't want to do a recompileCheck for
+    // Ion, then this just needs to increment the useCount so that we know when to
+    // recompile this function from an Ion call.  No need to call out to recompiler
+    // stub.
+    if (!hasFunctionCalls && !recompileCheckForIon)
         return;
 
-    void *ionScriptAddress = &script->ion;
-
-    // Trigger ion compilation if (a) the script has been used enough times for
-    // this opcode, and (b) the script does not already have ion information
-    // (whether successful, failed, or in progress off thread compilation).
 #if defined(JS_CPU_X86) || defined(JS_CPU_ARM)
-    Jump first = masm.branch32(Assembler::LessThan, AbsoluteAddress(useCountAddress),
-                               Imm32(minUses));
-    Jump second = masm.branch32(Assembler::Equal, AbsoluteAddress(ionScriptAddress),
-                                Imm32(0));
+    Jump jump = masm.branch32(Assembler::GreaterThanOrEqual, AbsoluteAddress(addr),
+                              Imm32(minUses));
 #else
     /* Handle processors that can't load from absolute addresses. */
     RegisterID reg = frame.allocReg();
-    masm.move(ImmPtr(useCountAddress), reg);
-    Jump first = masm.branch32(Assembler::LessThan, Address(reg), Imm32(minUses));
-    masm.move(ImmPtr(ionScriptAddress), reg);
-    Jump second = masm.branchPtr(Assembler::Equal, Address(reg), ImmPtr(NULL));
+    masm.move(ImmPtr(addr), reg);
+    Jump jump = masm.branch32(Assembler::GreaterThanOrEqual, Address(reg, 0),
+                              Imm32(minUses));
     frame.freeReg(reg);
 #endif
-    first.linkTo(masm.label(), &masm);
-
-    stubcc.linkExit(second, Uses(0));
+    stubcc.linkExit(jump, Uses(0));
     stubcc.leave();
 
-    OOL_STUBCALL(stubs::TriggerIonCompile, REJOIN_RESUME);
+    OOL_STUBCALL(stubs::RecompileForInline, REJOIN_RESUME);
     stubcc.rejoin(Changes(0));
 }
-#else /* JS_ION */
-void
-mjit::Compiler::ionCompileHelper()
-{
-}
-#endif /* JS_ION */
 
 CompileStatus
 mjit::Compiler::methodEntryHelper()
 {
     if (debugMode()) {
         prepareStubCall(Uses(0));
         INLINE_STUBCALL(stubs::ScriptDebugPrologue, REJOIN_RESUME);
 
--- a/js/src/methodjit/Compiler.h
+++ b/js/src/methodjit/Compiler.h
@@ -626,17 +626,17 @@ private:
     void jsop_this();
     void emitReturn(FrameEntry *fe);
     void emitFinalReturn(Assembler &masm);
     void loadReturnValue(Assembler *masm, FrameEntry *fe);
     void emitReturnValue(Assembler *masm, FrameEntry *fe);
     void emitInlineReturnValue(FrameEntry *fe);
     void dispatchCall(VoidPtrStubUInt32 stub, uint32_t argc);
     void interruptCheckHelper();
-    void ionCompileHelper();
+    void recompileCheckHelper();
     CompileStatus methodEntryHelper();
     CompileStatus profilingPushHelper();
     void profilingPopHelper();
     void emitUncachedCall(uint32_t argc, bool callingNew);
     void checkCallApplySpeculation(uint32_t argc, FrameEntry *origCallee, FrameEntry *origThis,
                                    MaybeRegisterID origCalleeType, RegisterID origCalleeData,
                                    MaybeRegisterID origThisType, RegisterID origThisData,
                                    Jump *uncachedCallSlowRejoin, CallPatchInfo *uncachedCallPatch);
--- a/js/src/methodjit/MethodJIT.cpp
+++ b/js/src/methodjit/MethodJIT.cpp
@@ -16,17 +16,16 @@
 #include "MonoIC.h"
 #include "PolyIC.h"
 #include "TrampolineCompiler.h"
 #include "jscntxtinlines.h"
 #include "jscompartment.h"
 #include "jsscope.h"
 #include "ion/Ion.h"
 #include "ion/IonCompartment.h"
-#include "methodjit/Retcon.h"
 
 #include "jsgcinlines.h"
 #include "jsinterpinlines.h"
 
 #if JS_TRACE_LOGGING
 #include "TraceLogging.h"
 #endif
 
@@ -1506,26 +1505,16 @@ JSScript::ReleaseCode(FreeOp *fop, JITSc
     if (jith->isValid()) {
         JITScript *jit = jith->getValid();
         jit->destroy(fop);
         fop->free_(jit);
         jith->setEmpty();
     }
 }
 
-void
-mjit::ReleaseScriptCodeFromVM(JSContext *cx, JSScript *script)
-{
-    if (script->hasMJITInfo()) {
-        ExpandInlineFrames(cx->compartment);
-        Recompiler::clearStackReferences(cx->runtime->defaultFreeOp(), script);
-        ReleaseScriptCode(cx->runtime->defaultFreeOp(), script);
-    }
-}
-
 #ifdef JS_METHODJIT_PROFILE_STUBS
 void JS_FASTCALL
 mjit::ProfileStubCall(VMFrame &f)
 {
     JSOp op = JSOp(*f.regs.pc);
     StubCallsForOp[op]++;
 }
 #endif
--- a/js/src/methodjit/MethodJIT.h
+++ b/js/src/methodjit/MethodJIT.h
@@ -790,16 +790,23 @@ struct JITScript
     JSC::ExecutablePool *shimPool;
 
     /*
      * Number of calls made to IonMonkey functions, used to avoid slow
      * JM -> Ion calls.
      */
     uint32_t        ionCalls;
 
+    /*
+     * If set, we decided to keep the JITChunk so that Ion can access its caches.
+     * The chunk has to be destroyed the next time the script runs in JM.
+     * Note that this flag implies nchunks == 1.
+     */
+    bool mustDestroyEntryChunk;
+
 #ifdef JS_MONOIC
     /* Inline cache at function entry for checking this/argument types. */
     JSC::CodeLocationLabel argsCheckStub;
     JSC::CodeLocationLabel argsCheckFallthrough;
     JSC::CodeLocationJump  argsCheckJump;
     JSC::ExecutablePool *argsCheckPool;
     void resetArgsCheck();
 #endif
@@ -897,20 +904,16 @@ ReleaseScriptCode(FreeOp *fop, JSScript 
             if (jith && jith->isValid())
                 JSScript::ReleaseCode(fop, jith);
         }
     }
 
     script->destroyMJITInfo(fop);
 }
 
-/* Can be called at any time. */
-void
-ReleaseScriptCodeFromVM(JSContext *cx, JSScript *script);
-
 // Expand all stack frames inlined by the JIT within a compartment.
 void
 ExpandInlineFrames(JSCompartment *compartment);
 
 // Return all VMFrames in a compartment to the interpreter. This must be
 // followed by destroying all JIT code in the compartment.
 void
 ClearAllFrames(JSCompartment *compartment);
--- a/js/src/methodjit/MonoIC.cpp
+++ b/js/src/methodjit/MonoIC.cpp
@@ -586,17 +586,17 @@ class CallCompiler : public BaseCompiler
         /* Load fun->u.i.script->ion */
         RegisterID ionScript = regs.takeAnyReg().reg();
         Address scriptAddr(funObjReg, JSFunction::offsetOfNativeOrScript());
         masm.loadPtr(scriptAddr, ionScript);
         masm.loadPtr(Address(ionScript, offsetof(JSScript, ion)), ionScript);
 
         /* Guard that the ion pointer is valid. */
         Jump noIonCode = masm.branchPtr(Assembler::BelowOrEqual, ionScript,
-                                        ImmPtr(ION_COMPILING_SCRIPT));
+                                        ImmPtr(ION_DISABLED_SCRIPT));
 
         RegisterID t0 = regs.takeAnyReg().reg();
         RegisterID t1 = Registers::ClobberInCall;
 
         masm.move(ImmPtr(f.regs.pc), t1);
 
         /* Set the CALLING_INTO_ION flag on the existing frame. */
         masm.load32(Address(JSFrameReg, StackFrame::offsetOfFlags()), t0);
--- a/js/src/methodjit/StubCalls.cpp
+++ b/js/src/methodjit/StubCalls.cpp
@@ -766,29 +766,38 @@ stubs::Interrupt(VMFrame &f, jsbytecode 
 {
     gc::MaybeVerifyBarriers(f.cx);
 
     if (!js_HandleExecutionInterrupt(f.cx))
         THROW();
 }
 
 void JS_FASTCALL
-stubs::TriggerIonCompile(VMFrame &f)
+stubs::RecompileForInline(VMFrame &f)
 {
     JSScript *script = f.script();
-    JS_ASSERT(!script->ion);
+
+    ExpandInlineFrames(f.cx->compartment);
+    Recompiler::clearStackReferences(f.cx->runtime->defaultFreeOp(), script);
 
-    jsbytecode *osrPC = f.regs.pc;
-    if (*osrPC != JSOP_LOOPENTRY)
-        osrPC = NULL;
+#ifdef JS_ION
+    if (ion::IsEnabled(f.cx) && f.jit()->nchunks == 1 &&
+        script->canIonCompile() && !script->hasIonScript())
+    {
+        // After returning to the interpreter, IonMonkey will try to compile
+        // this script. Don't destroy the JITChunk immediately so that Ion
+        // still has access to its ICs.
+        JS_ASSERT(!f.jit()->mustDestroyEntryChunk);
+        f.jit()->mustDestroyEntryChunk = true;
+        f.jit()->disableScriptEntry();
+        return;
+    }
+#endif
 
-    if (!ion::TestIonCompile(f.cx, script, script->function(), osrPC, f.fp()->isConstructing())) {
-        if (f.cx->isExceptionPending())
-            THROW();
-    }
+    f.jit()->destroyChunk(f.cx->runtime->defaultFreeOp(), f.chunkIndex(), /* resetUses = */ false);
 }
 
 void JS_FASTCALL
 stubs::Trap(VMFrame &f, uint32_t trapTypes)
 {
     Value rval;
 
     /*
--- a/js/src/methodjit/StubCalls.h
+++ b/js/src/methodjit/StubCalls.h
@@ -21,17 +21,17 @@ typedef enum JSTrapType {
 } JSTrapType;
 
 void JS_FASTCALL This(VMFrame &f);
 void JS_FASTCALL NewInitArray(VMFrame &f, uint32_t count);
 void JS_FASTCALL NewInitObject(VMFrame &f, JSObject *base);
 void JS_FASTCALL Trap(VMFrame &f, uint32_t trapTypes);
 void JS_FASTCALL DebuggerStatement(VMFrame &f, jsbytecode *pc);
 void JS_FASTCALL Interrupt(VMFrame &f, jsbytecode *pc);
-void JS_FASTCALL TriggerIonCompile(VMFrame &f);
+void JS_FASTCALL RecompileForInline(VMFrame &f);
 void JS_FASTCALL InitElem(VMFrame &f, uint32_t last);
 void JS_FASTCALL InitProp(VMFrame &f, PropertyName *name);
 
 void JS_FASTCALL HitStackQuota(VMFrame &f);
 void * JS_FASTCALL FixupArity(VMFrame &f, uint32_t argc);
 void * JS_FASTCALL CompileFunction(VMFrame &f, uint32_t argc);
 void JS_FASTCALL SlowNew(VMFrame &f, uint32_t argc);
 void JS_FASTCALL SlowCall(VMFrame &f, uint32_t argc);
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -4873,34 +4873,17 @@ ProcessArgs(JSContext *cx, JSObject *obj
         if (strcmp(str, "lsra") == 0)
             ion::js_IonOptions.lsra = true;
         else
             return OptionFailure("ion-regalloc", str);
     }
 
     if (op->getBoolOption("ion-eager"))
         ion::js_IonOptions.setEagerCompilation();
-
-#ifdef JS_THREADSAFE
-    if (const char *str = op->getStringOption("ion-parallel-compile")) {
-        if (strcmp(str, "on") == 0) {
-            if (GetCPUCount() <= 1) {
-                fprintf(stderr, "Parallel compilation not available on single core machines");
-                return EXIT_FAILURE;
-            }
-            ion::js_IonOptions.parallelCompilation = true;
-        } else if (strcmp(str, "off") == 0) {
-            ion::js_IonOptions.parallelCompilation = false;
-        } else {
-            return OptionFailure("ion-parallel-compile", str);
-        }
-    }
-#endif /* JS_THREADSAFE */
-
-#endif /* JS_ION */
+#endif
 
     /* |scriptArgs| gets bound on the global before any code is run. */
     if (!BindScriptArgs(cx, obj, op))
         return EXIT_FAILURE;
 
     MultiStringRange filePaths = op->getMultiStringOption('f');
     MultiStringRange codeChunks = op->getMultiStringOption('e');
 
@@ -5096,20 +5079,16 @@ main(int argc, char **argv, char **envp)
         || !op.addStringOption('\0', "ion-osr", "on/off",
                                "On-Stack Replacement (default: on, off to disable)")
         || !op.addStringOption('\0', "ion-limit-script-size", "on/off",
                                "Don't compile very large scripts (default: on, off to disable)")
         || !op.addStringOption('\0', "ion-regalloc", "[mode]",
                                "Specify Ion register allocation:\n"
                                "  lsra: Linear Scan register allocation (default)")
         || !op.addBoolOption('\0', "ion-eager", "Always ion-compile methods")
-#ifdef JS_THREADSAFE
-        || !op.addStringOption('\0', "ion-parallel-compile", "on/off",
-                               "Compile scripts off thread (default: off)")
-#endif
     )
     {
         return EXIT_FAILURE;
     }
 
     op.setArgTerminatesOptions("script", true);
 
     switch (op.parseArgs(argc, argv)) {