Allow performing code generation off thread during compilation, bug 785762. r=dvander
authorBrian Hackett <bhackett1024@gmail.com>
Thu, 15 Nov 2012 07:00:05 -0800
changeset 113381 a5974ecf93c01c4128606d9a183a9799d98c6e12
parent 113380 d2e0669c3baa12b66b133f4d92bf5eea06863546
child 113382 bb8baf35a2733da29046d89e0f22b5c85cc00767
push id23870
push userryanvm@gmail.com
push dateFri, 16 Nov 2012 01:21:36 +0000
treeherdermozilla-central@58ebb638a7ea [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdvander
bugs785762
milestone19.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Allow performing code generation off thread during compilation, bug 785762. r=dvander
js/src/ion/CodeGenerator.cpp
js/src/ion/CodeGenerator.h
js/src/ion/Ion.cpp
js/src/ion/Ion.h
js/src/ion/IonBuilder.cpp
js/src/ion/IonBuilder.h
js/src/ion/IonMacroAssembler.h
js/src/ion/LIR.h
js/src/ion/arm/CodeGenerator-arm.cpp
js/src/ion/arm/CodeGenerator-arm.h
js/src/ion/shared/CodeGenerator-shared.cpp
js/src/ion/shared/CodeGenerator-shared.h
js/src/ion/shared/CodeGenerator-x86-shared.cpp
js/src/ion/shared/CodeGenerator-x86-shared.h
js/src/ion/x64/CodeGenerator-x64.cpp
js/src/ion/x64/CodeGenerator-x64.h
js/src/ion/x86/CodeGenerator-x86.cpp
js/src/ion/x86/CodeGenerator-x86.h
js/src/jsworkers.cpp
js/src/jsworkers.h
--- a/js/src/ion/CodeGenerator.cpp
+++ b/js/src/ion/CodeGenerator.cpp
@@ -25,17 +25,17 @@ using mozilla::DebugOnly;
 namespace js {
 namespace ion {
 
 StringObject *
 MNewStringObject::templateObj() const {
     return &templateObj_->asString();
 }
 
-CodeGenerator::CodeGenerator(MIRGenerator *gen, LIRGraph &graph)
+CodeGenerator::CodeGenerator(MIRGenerator *gen, LIRGraph *graph)
   : CodeGeneratorSpecific(gen, graph)
 {
 }
 
 bool
 CodeGenerator::visitValueToInt32(LValueToInt32 *lir)
 {
     ValueOperand operand = ToValue(lir, LValueToInt32::Input);
@@ -2978,49 +2978,50 @@ CodeGenerator::visitGetArgument(LGetArgu
         masm.loadValue(argPtr, result);
     }
     return true;
 }
 
 bool
 CodeGenerator::generate()
 {
-    AssertCanGC();
-    JSContext *cx = GetIonContext()->cx;
-
-    unsigned slots = graph.localSlotCount() +
-                     (graph.argumentSlotCount() * sizeof(Value) / STACK_SLOT_SIZE);
-    if (!safepoints_.init(slots))
+    if (!safepoints_.init(graph.totalSlotCount()))
         return false;
 
     // Before generating any code, we generate type checks for all parameters.
     // This comes before deoptTable_, because we can't use deopt tables without
     // creating the actual frame.
     if (!generateArgumentsChecks())
         return false;
 
     if (frameClass_ != FrameSizeClass::None()) {
-        deoptTable_ = cx->compartment->ionCompartment()->getBailoutTable(frameClass_);
+        deoptTable_ = GetIonContext()->compartment->ionCompartment()->getBailoutTable(frameClass_);
         if (!deoptTable_)
             return false;
     }
 
     if (!generatePrologue())
         return false;
     if (!generateBody())
         return false;
     if (!generateEpilogue())
         return false;
     if (!generateInvalidateEpilogue())
         return false;
     if (!generateOutOfLineCode())
         return false;
 
-    if (masm.oom())
-        return false;
+    return !masm.oom();
+}
+
+bool
+CodeGenerator::link()
+{
+    AssertCanGC();
+    JSContext *cx = GetIonContext()->cx;
 
     Linker linker(masm);
     IonCode *code = linker.newCode(cx);
     if (!code)
         return false;
 
     // We encode safepoints after the OSI-point offsets have been determined.
     encodeSafepoints();
@@ -3034,17 +3035,17 @@ CodeGenerator::generate()
                            : FrameSizeClass::FromDepth(frameDepth_).frameSize();
 
     // Check to make sure we didn't have a mid-build invalidation. If so, we
     // will trickle to ion::Compile() and return Method_Skipped.
     if (cx->compartment->types.compiledInfo.compilerOutput(cx)->isInvalidated())
         return true;
 
     IonScript *ionScript =
-      IonScript::New(cx, slots, scriptFrameSize, snapshots_.size(),
+      IonScript::New(cx, graph.totalSlotCount(), scriptFrameSize, snapshots_.size(),
                      bailouts_.length(), graph.numConstants(),
                      safepointIndices_.length(), osiIndices_.length(),
                      cacheList_.length(), barrierOffsets_.length(),
                      safepoints_.size(), graph.mir().numScripts());
     SetIonScript(script, executionMode, ionScript);
 
     if (!ionScript)
         return false;
--- a/js/src/ion/CodeGenerator.h
+++ b/js/src/ion/CodeGenerator.h
@@ -31,20 +31,21 @@ class OutOfLineTypeOfV;
 class OutOfLineLoadTypedArray;
 
 class CodeGenerator : public CodeGeneratorSpecific
 {
     bool generateArgumentsChecks();
     bool generateBody();
 
   public:
-    CodeGenerator(MIRGenerator *gen, LIRGraph &graph);
+    CodeGenerator(MIRGenerator *gen, LIRGraph *graph);
 
   public:
     bool generate();
+    bool link();
 
     bool visitLabel(LLabel *lir);
     bool visitNop(LNop *lir);
     bool visitOsiPoint(LOsiPoint *lir);
     bool visitGoto(LGoto *lir);
     bool visitTableSwitch(LTableSwitch *ins);
     bool visitTableSwitchV(LTableSwitchV *ins);
     bool visitParameter(LParameter *lir);
--- a/js/src/ion/Ion.cpp
+++ b/js/src/ion/Ion.cpp
@@ -202,21 +202,28 @@ IonCompartment::IonCompartment(IonRuntim
   : rt(rt),
     flusher_(NULL)
 {
 }
 
 void
 ion::FinishOffThreadBuilder(IonBuilder *builder)
 {
+    // Clean up if compilation did not succeed.
     if (builder->script()->isIonCompilingOffThread()) {
         types::TypeCompartment &types = builder->script()->compartment()->types;
         builder->recompileInfo.compilerOutput(types)->invalidate();
         builder->script()->ion = NULL;
     }
+
+    // The builder is allocated into its LifoAlloc, so destroying that will
+    // destroy the builder and all other data accumulated during compilation,
+    // except any final codegen (which includes an assembler and needs to be
+    // explicitly destroyed).
+    js_delete(builder->backgroundCodegen());
     js_delete(builder->temp().lifoAlloc());
 }
 
 static inline void
 FinishAllOffThreadCompilations(IonCompartment *ion)
 {
     OffThreadCompilationVector &compilations = ion->finishedOffThreadCompilations();
 
@@ -755,17 +762,17 @@ ion::ToggleBarriers(JSCompartment *comp,
         if (script->hasIonScript())
             script->ion->toggleBarriers(needs);
     }
 }
 
 namespace js {
 namespace ion {
 
-LIRGraph *
+CodeGenerator *
 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();
 
@@ -941,17 +948,23 @@ CompileBackEnd(MIRGenerator *mir)
         if (!regalloc.go())
             return NULL;
         IonSpewPass("Allocate Registers", &regalloc);
 
         if (mir->shouldCancel("Allocate Registers"))
             return NULL;
     }
 
-    return lir;
+    CodeGenerator *codegen = js_new<CodeGenerator>(mir, lir);
+    if (!codegen || !codegen->generate()) {
+        js_delete(codegen);
+        return NULL;
+    }
+
+    return codegen;
 }
 
 class AutoDestroyAllocator
 {
     LifoAlloc *alloc;
 
   public:
     AutoDestroyAllocator(LifoAlloc *alloc) : alloc(alloc) {}
@@ -992,34 +1005,37 @@ AttachFinishedCompilations(JSContext *cx
     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->backgroundCompiledLir) {
+        if (CodeGenerator *codegen = builder->backgroundCodegen()) {
             RootedScript script(cx, builder->script());
             IonContext ictx(cx, cx->compartment, &builder->temp());
 
-            CodeGenerator codegen(builder, *builder->backgroundCompiledLir);
+            // Root the assembler until the builder is finished below. As it
+            // was constructed off thread, the assembler has not been rooted
+            // previously, though any GC activity would discard the builder.
+            codegen->masm.constructRoot(cx);
 
             types::AutoEnterTypeInference enterTypes(cx);
 
             ExecutionMode executionMode = builder->info().executionMode();
             types::AutoEnterCompilation enterCompiler(cx, CompilerOutputKind(executionMode));
             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();
+                success = codegen->link();
             }
 
             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.
@@ -1118,31 +1134,28 @@ SequentialCompileContext::compile(IonBui
 
         // The allocator and associated data will be destroyed after being
         // processed in the finishedOffThreadCompilations list.
         autoDestroy.cancel();
 
         return true;
     }
 
-    LIRGraph *lir = CompileBackEnd(builder);
-    if (!lir) {
+    CodeGenerator *codegen = CompileBackEnd(builder);
+    if (!codegen) {
         IonSpew(IonSpew_Abort, "Failed during back-end compilation.");
         return false;
     }
 
-    CodeGenerator codegen(builder, *lir);
-    if (!codegen.generate()) {
-        IonSpew(IonSpew_Abort, "Failed during code generation.");
-        return false;
-    }
+    bool success = codegen->link();
+    js_delete(codegen);
 
     IonSpewEndFunction();
 
-    return true;
+    return success;
 }
 
 bool
 TestIonCompile(JSContext *cx, JSScript *script, JSFunction *fun, jsbytecode *osrPc, bool constructing)
 {
     SequentialCompileContext compileContext;
     if (!IonCompile(cx, script, fun, osrPc, constructing, compileContext)) {
         if (!cx->isExceptionPending())
--- a/js/src/ion/Ion.h
+++ b/js/src/ion/Ion.h
@@ -248,19 +248,19 @@ void Invalidate(JSContext *cx, const Vec
 bool Invalidate(JSContext *cx, JSScript *script, bool resetUses = true);
 
 void MarkFromIon(JSCompartment *comp, Value *vp);
 
 void ToggleBarriers(JSCompartment *comp, bool needs);
 
 class IonBuilder;
 class MIRGenerator;
-class LIRGraph;
+class CodeGenerator;
 
-LIRGraph *CompileBackEnd(MIRGenerator *mir);
+CodeGenerator *CompileBackEnd(MIRGenerator *mir);
 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();
 }
--- a/js/src/ion/IonBuilder.cpp
+++ b/js/src/ion/IonBuilder.cpp
@@ -25,17 +25,17 @@ using namespace js;
 using namespace js::ion;
 
 using mozilla::DebugOnly;
 
 IonBuilder::IonBuilder(JSContext *cx, TempAllocator *temp, MIRGraph *graph,
                        TypeOracle *oracle, CompileInfo *info, size_t inliningDepth, uint32 loopDepth)
   : MIRGenerator(cx->compartment, temp, graph, info),
     recompileInfo(cx->compartment->types.compiledInfo),
-    backgroundCompiledLir(NULL),
+    backgroundCodegen_(NULL),
     cx(cx),
     loopDepth_(loopDepth),
     callerResumePoint_(NULL),
     callerBuilder_(NULL),
     oracle(oracle),
     inliningDepth(inliningDepth),
     failedBoundsCheck_(info->script()->failedBoundsCheck),
     failedShapeGuard_(info->script()->failedShapeGuard),
--- a/js/src/ion/IonBuilder.h
+++ b/js/src/ion/IonBuilder.h
@@ -12,17 +12,17 @@
 // JSScript.
 
 #include "MIR.h"
 #include "MIRGraph.h"
 
 namespace js {
 namespace ion {
 
-class LIRGraph;
+class CodeGenerator;
 
 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.
@@ -434,27 +434,32 @@ class IonBuilder : public MIRGenerator
                            MGetPropertyCache *getPropCache,
                            types::StackTypeSet *types, types::StackTypeSet *barrier,
                            MBasicBlock *bottom,
                            Vector<MDefinition *, 8, IonAllocPolicy> &retvalDefns);
 
     // A builder is inextricably tied to a particular script.
     HeapPtrScript script_;
 
+    // If off thread compilation is successful, the final code generator is
+    // attached here. Code has been generated, but not linked (there is not yet
+    // an IonScript). This is heap allocated, and must be explicitly destroyed.
+    CodeGenerator *backgroundCodegen_;
+
   public:
     // Compilation index for this attempt.
     types::RecompileInfo const recompileInfo;
 
-    // If off thread compilation is successful, final LIR is attached here.
-    LIRGraph *backgroundCompiledLir;
-
     void clearForBackEnd();
 
     Return<JSScript*> script() const { return script_; }
 
+    CodeGenerator *backgroundCodegen() const { return backgroundCodegen_; }
+    void setBackgroundCodegen(CodeGenerator *codegen) { backgroundCodegen_ = codegen; }
+
   private:
     JSContext *cx;
 
     jsbytecode *pc;
     MBasicBlock *current;
     uint32 loopDepth_;
 
     /* Information used for inline-call builders. */
--- a/js/src/ion/IonMacroAssembler.h
+++ b/js/src/ion/IonMacroAssembler.h
@@ -46,17 +46,17 @@ class MacroAssembler : public MacroAssem
             masm_(masm)
         { }
 
         MacroAssembler *masm() const {
             return masm_;
         }
     };
 
-    AutoRooter autoRooter_;
+    mozilla::Maybe<AutoRooter> autoRooter_;
     mozilla::Maybe<IonContext> ionContext_;
     mozilla::Maybe<AutoIonContextAlloc> alloc_;
     bool enoughMemory_;
 
   private:
     // This field is used to manage profiling instrumentation output. If
     // provided and enabled, then instrumentation will be emitted around call
     // sites. The IonInstrumentation instance is hosted inside of
@@ -64,41 +64,48 @@ class MacroAssembler : public MacroAssem
     // actually emitted or not. If NULL, then no instrumentation is emitted.
     IonInstrumentation *sps_;
 
   public:
     // If instrumentation should be emitted, then the sps parameter should be
     // provided, but otherwise it can be safely omitted to prevent all
     // instrumentation from being emitted.
     MacroAssembler(IonInstrumentation *sps = NULL)
-      : autoRooter_(GetIonContext()->cx, thisFromCtor()),
-        enoughMemory_(true),
+      : enoughMemory_(true),
         sps_(sps)
     {
+        JSContext *cx = GetIonContext()->cx;
+        if (cx)
+            constructRoot(cx);
+
         if (!GetIonContext()->temp)
-            alloc_.construct(GetIonContext()->cx);
+            alloc_.construct(cx);
 #ifdef JS_CPU_ARM
         m_buffer.id = GetIonContext()->getNextAssemblerId();
 #endif
     }
 
     // This constructor should only be used when there is no IonContext active
     // (for example, Trampoline-$(ARCH).cpp).
     MacroAssembler(JSContext *cx)
-      : autoRooter_(cx, thisFromCtor()),
-        enoughMemory_(true),
+      : enoughMemory_(true),
         sps_(NULL) // no need for instrumentation in trampolines and such
     {
+        constructRoot(cx);
         ionContext_.construct(cx, cx->compartment, (js::ion::TempAllocator *)NULL);
         alloc_.construct(cx);
 #ifdef JS_CPU_ARM
         m_buffer.id = GetIonContext()->getNextAssemblerId();
 #endif
     }
 
+    void constructRoot(JSContext *cx) {
+        autoRooter_.construct(cx, this);
+    }
+
     MoveResolver &moveResolver() {
         return moveResolver_;
     }
 
     size_t instructionsSize() const {
         return size();
     }
 
@@ -150,21 +157,21 @@ class MacroAssembler : public MacroAssem
     }
 
     void loadStringLength(Register str, Register dest) {
         loadPtr(Address(str, JSString::offsetOfLengthAndFlags()), dest);
         rshiftPtr(Imm32(JSString::LENGTH_SHIFT), dest);
     }
 
     void loadJSContext(const Register &dest) {
-        movePtr(ImmWord(GetIonContext()->cx->runtime), dest);
+        movePtr(ImmWord(GetIonContext()->compartment->rt), dest);
         loadPtr(Address(dest, offsetof(JSRuntime, ionJSContext)), dest);
     }
     void loadIonActivation(const Register &dest) {
-        movePtr(ImmWord(GetIonContext()->cx->runtime), dest);
+        movePtr(ImmWord(GetIonContext()->compartment->rt), dest);
         loadPtr(Address(dest, offsetof(JSRuntime, ionActivation)), dest);
     }
 
     template<typename T>
     void loadTypedOrValue(const T &src, TypedOrValueRegister dest) {
         if (dest.hasValue())
             loadValue(src, dest.valueReg());
         else
@@ -365,17 +372,17 @@ class MacroAssembler : public MacroAssem
         if (key.isRegister())
             branch32(cond, length, key.reg(), label);
         else
             branch32(cond, length, Imm32(key.constant()), label);
     }
 
     void branchTestNeedsBarrier(Condition cond, const Register &scratch, Label *label) {
         JS_ASSERT(cond == Zero || cond == NonZero);
-        JSCompartment *comp = GetIonContext()->cx->compartment;
+        JSCompartment *comp = GetIonContext()->compartment;
         movePtr(ImmWord(comp), scratch);
         Address needsBarrierAddr(scratch, JSCompartment::OffsetOfNeedsBarrier());
         branchTest32(cond, needsBarrierAddr, Imm32(0x1), label);
     }
 
     template <typename T>
     void callPreBarrier(const T &address, MIRType type) {
         JS_ASSERT(type == MIRType_Value || type == MIRType_String || type == MIRType_Object);
--- a/js/src/ion/LIR.h
+++ b/js/src/ion/LIR.h
@@ -1139,16 +1139,19 @@ class LIRGraph
         return localSlotCount_;
     }
     void setArgumentSlotCount(uint32 argumentSlotCount) {
         argumentSlotCount_ = argumentSlotCount;
     }
     uint32 argumentSlotCount() const {
         return argumentSlotCount_;
     }
+    uint32 totalSlotCount() const {
+        return localSlotCount() + (argumentSlotCount() * sizeof(Value) / STACK_SLOT_SIZE);
+    }
     bool addConstantToPool(const Value &v, uint32 *index);
     size_t numConstants() const {
         return constantPool_.length();
     }
     HeapValue *constantPool() {
         return &constantPool_[0];
     }
     const HeapValue &getConstant(size_t index) const {
--- a/js/src/ion/arm/CodeGenerator-arm.cpp
+++ b/js/src/ion/arm/CodeGenerator-arm.cpp
@@ -46,17 +46,17 @@ class DeferredJumpTable : public Deferre
             *jumpData = (void *)(code->raw() + masm->actualOffset(offset));
             jumpData++;
         }
     }
 };
 
 
 // shared
-CodeGeneratorARM::CodeGeneratorARM(MIRGenerator *gen, LIRGraph &graph)
+CodeGeneratorARM::CodeGeneratorARM(MIRGenerator *gen, LIRGraph *graph)
   : CodeGeneratorShared(gen, graph),
     deoptLabel_(NULL)
 {
 }
 
 bool
 CodeGeneratorARM::generatePrologue()
 {
--- a/js/src/ion/arm/CodeGenerator-arm.h
+++ b/js/src/ion/arm/CodeGenerator-arm.h
@@ -117,17 +117,17 @@ class CodeGeneratorARM : public CodeGene
 
     void storeElementTyped(const LAllocation *value, MIRType valueType, MIRType elementType,
                            const Register &elements, const LAllocation *index);
 
   protected:
     void linkAbsoluteLabels();
 
   public:
-    CodeGeneratorARM(MIRGenerator *gen, LIRGraph &graph);
+    CodeGeneratorARM(MIRGenerator *gen, LIRGraph *graph);
 
   public:
     bool visitBox(LBox *box);
     bool visitBoxDouble(LBoxDouble *box);
     bool visitUnbox(LUnbox *unbox);
     bool visitValue(LValue *value);
     bool visitOsrValue(LOsrValue *value);
     bool visitDouble(LDouble *ins);
--- a/js/src/ion/shared/CodeGenerator-shared.cpp
+++ b/js/src/ion/shared/CodeGenerator-shared.cpp
@@ -16,31 +16,31 @@
 using namespace js;
 using namespace js::ion;
 
 using mozilla::DebugOnly;
 
 namespace js {
 namespace ion {
 
-CodeGeneratorShared::CodeGeneratorShared(MIRGenerator *gen, LIRGraph &graph)
+CodeGeneratorShared::CodeGeneratorShared(MIRGenerator *gen, LIRGraph *graph)
   : oolIns(NULL),
     masm(&sps_),
     gen(gen),
-    graph(graph),
+    graph(*graph),
     current(NULL),
     deoptTable_(NULL),
 #ifdef DEBUG
     pushedArgs_(0),
 #endif
     lastOsiPointOffset_(0),
     sps_(&gen->compartment->rt->spsProfiler, &lastPC_),
     osrEntryOffset_(0),
-    frameDepth_(graph.localSlotCount() * sizeof(STACK_SLOT_SIZE) +
-                graph.argumentSlotCount() * sizeof(Value))
+    frameDepth_(graph->localSlotCount() * sizeof(STACK_SLOT_SIZE) +
+                graph->argumentSlotCount() * sizeof(Value))
 {
     frameClass_ = FrameSizeClass::FromDepth(frameDepth_);
 }
 
 bool
 CodeGeneratorShared::generateOutOfLineCode()
 {
     for (size_t i = 0; i < outOfLineCode_.length(); i++) {
--- a/js/src/ion/shared/CodeGenerator-shared.h
+++ b/js/src/ion/shared/CodeGenerator-shared.h
@@ -30,18 +30,20 @@ template <class ArgSeq, class StoreOutpu
 class OutOfLineCallVM;
 class OutOfLineTruncateSlow;
 
 class CodeGeneratorShared : public LInstructionVisitor
 {
     js::Vector<OutOfLineCode *, 0, SystemAllocPolicy> outOfLineCode_;
     OutOfLineCode *oolIns;
 
+  public:
+    MacroAssembler masm;
+
   protected:
-    MacroAssembler masm;
     MIRGenerator *gen;
     LIRGraph &graph;
     LBlock *current;
     SnapshotWriter snapshots_;
     IonCode *deoptTable_;
 #ifdef DEBUG
     uint32 pushedArgs_;
 #endif
@@ -277,17 +279,17 @@ class CodeGeneratorShared : public LInst
 
     void linkAbsoluteLabels() {
     }
 
   private:
     void generateInvalidateEpilogue();
 
   public:
-    CodeGeneratorShared(MIRGenerator *gen, LIRGraph &graph);
+    CodeGeneratorShared(MIRGenerator *gen, LIRGraph *graph);
 
   public:
     template <class ArgSeq, class StoreOutputTo>
     bool visitOutOfLineCallVM(OutOfLineCallVM<ArgSeq, StoreOutputTo> *ool);
 
     bool visitOutOfLineTruncateSlow(OutOfLineTruncateSlow *ool);
 };
 
--- a/js/src/ion/shared/CodeGenerator-x86-shared.cpp
+++ b/js/src/ion/shared/CodeGenerator-x86-shared.cpp
@@ -39,17 +39,17 @@ class DeferredJumpTable : public Deferre
 
             uint32 offset = caseheader->offset();
             *jumpData = (void *)(code->raw() + offset);
             jumpData++;
         }
     }
 };
 
-CodeGeneratorX86Shared::CodeGeneratorX86Shared(MIRGenerator *gen, LIRGraph &graph)
+CodeGeneratorX86Shared::CodeGeneratorX86Shared(MIRGenerator *gen, LIRGraph *graph)
   : CodeGeneratorShared(gen, graph),
     deoptLabel_(NULL)
 {
 }
 
 double
 test(double x, double y)
 {
--- a/js/src/ion/shared/CodeGenerator-x86-shared.h
+++ b/js/src/ion/shared/CodeGenerator-x86-shared.h
@@ -78,17 +78,17 @@ class CodeGeneratorX86Shared : public Co
     // true, and the false block if |cond| is false.
     void emitBranch(Assembler::Condition cond, MBasicBlock *ifTrue, MBasicBlock *ifFalse,
                     NaNCond ifNaN = NaN_Unexpected);
     void emitBranch(Assembler::DoubleCondition cond, MBasicBlock *ifTrue, MBasicBlock *ifFalse);
 
     bool emitTableSwitchDispatch(MTableSwitch *mir, const Register &index, const Register &base);
 
   public:
-    CodeGeneratorX86Shared(MIRGenerator *gen, LIRGraph &graph);
+    CodeGeneratorX86Shared(MIRGenerator *gen, LIRGraph *graph);
 
   public:
     // Instruction visitors.
     virtual bool visitMinMaxD(LMinMaxD *ins);
     virtual bool visitNegD(LNegD *ins);
     virtual bool visitAbsD(LAbsD *ins);
     virtual bool visitSqrtD(LSqrtD *ins);
     virtual bool visitPowHalfD(LPowHalfD *ins);
--- a/js/src/ion/x64/CodeGenerator-x64.cpp
+++ b/js/src/ion/x64/CodeGenerator-x64.cpp
@@ -11,17 +11,17 @@
 #include "ion/MIRGraph.h"
 #include "jsnum.h"
 #include "jsscope.h"
 #include "jsscopeinlines.h"
 
 using namespace js;
 using namespace js::ion;
 
-CodeGeneratorX64::CodeGeneratorX64(MIRGenerator *gen, LIRGraph &graph)
+CodeGeneratorX64::CodeGeneratorX64(MIRGenerator *gen, LIRGraph *graph)
   : CodeGeneratorX86Shared(gen, graph)
 {
 }
 
 ValueOperand
 CodeGeneratorX64::ToValue(LInstruction *ins, size_t pos)
 {
     return ValueOperand(ToRegister(ins->getOperand(pos)));
--- a/js/src/ion/x64/CodeGenerator-x64.h
+++ b/js/src/ion/x64/CodeGenerator-x64.h
@@ -29,17 +29,17 @@ class CodeGeneratorX64 : public CodeGene
     void loadUnboxedValue(Operand source, MIRType type, const LDefinition *dest);
     void storeUnboxedValue(const LAllocation *value, MIRType valueType,
                            Operand dest, MIRType slotType);
 
     void storeElementTyped(const LAllocation *value, MIRType valueType, MIRType elementType,
                            const Register &elements, const LAllocation *index);
 
   public:
-    CodeGeneratorX64(MIRGenerator *gen, LIRGraph &graph);
+    CodeGeneratorX64(MIRGenerator *gen, LIRGraph *graph);
 
   public:
     bool visitValue(LValue *value);
     bool visitOsrValue(LOsrValue *value);
     bool visitBox(LBox *box);
     bool visitUnbox(LUnbox *unbox);
     bool visitDouble(LDouble *ins);
     bool visitLoadSlotV(LLoadSlotV *ins);
--- a/js/src/ion/x86/CodeGenerator-x86.cpp
+++ b/js/src/ion/x86/CodeGenerator-x86.cpp
@@ -13,17 +13,17 @@
 #include "jsscope.h"
 #include "jsscriptinlines.h"
 
 using namespace js;
 using namespace js::ion;
 
 using mozilla::DebugOnly;
 
-CodeGeneratorX86::CodeGeneratorX86(MIRGenerator *gen, LIRGraph &graph)
+CodeGeneratorX86::CodeGeneratorX86(MIRGenerator *gen, LIRGraph *graph)
   : CodeGeneratorX86Shared(gen, graph)
 {
 }
 
 static const uint32 FrameSizes[] = { 128, 256, 512, 1024 };
 
 FrameSizeClass
 FrameSizeClass::FromDepth(uint32 frameDepth)
--- a/js/src/ion/x86/CodeGenerator-x86.h
+++ b/js/src/ion/x86/CodeGenerator-x86.h
@@ -47,17 +47,17 @@ class CodeGeneratorX86 : public CodeGene
 
     void storeElementTyped(const LAllocation *value, MIRType valueType, MIRType elementType,
                            const Register &elements, const LAllocation *index);
 
   protected:
     void linkAbsoluteLabels();
 
   public:
-    CodeGeneratorX86(MIRGenerator *gen, LIRGraph &graph);
+    CodeGeneratorX86(MIRGenerator *gen, LIRGraph *graph);
 
   public:
     bool visitBox(LBox *box);
     bool visitBoxDouble(LBoxDouble *box);
     bool visitUnbox(LUnbox *unbox);
     bool visitValue(LValue *value);
     bool visitOsrValue(LOsrValue *value);
     bool visitDouble(LDouble *ins);
--- a/js/src/jsworkers.cpp
+++ b/js/src/jsworkers.cpp
@@ -278,16 +278,19 @@ WorkerThread::ThreadMain(void *arg)
 }
 
 void
 WorkerThread::threadLoop()
 {
     WorkerThreadState &state = *runtime->workerThreadState;
     state.lock();
 
+    threadData.construct(runtime);
+    js::TlsPerThreadData.set(threadData.addr());
+
     while (true) {
         JS_ASSERT(!ionBuilder);
 
         while (state.ionWorklist.empty()) {
             if (terminate) {
                 state.unlock();
                 return;
             }
@@ -298,17 +301,17 @@ WorkerThread::threadLoop()
 
         ion::ExecutionMode executionMode = ionBuilder->info().executionMode();
         JS_ASSERT(GetIonScript(ionBuilder->script().unsafeGet(), executionMode) == ION_COMPILING_SCRIPT);
 
         state.unlock();
 
         {
             ion::IonContext ictx(NULL, ionBuilder->script()->compartment(), &ionBuilder->temp());
-            ionBuilder->backgroundCompiledLir = ion::CompileBackEnd(ionBuilder);
+            ionBuilder->setBackgroundCodegen(ion::CompileBackEnd(ionBuilder));
         }
 
         state.lock();
 
         FinishOffThreadIonCompile(ionBuilder);
         ionBuilder = NULL;
 
         /*
--- a/js/src/jsworkers.h
+++ b/js/src/jsworkers.h
@@ -77,16 +77,18 @@ class WorkerThreadState
     /* 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;
+
+    mozilla::Maybe<PerThreadData> threadData;
     PRThread *thread;
 
     /* Indicate to an idle thread that it should finish executing. */
     bool terminate;
 
     /* Any builder currently being compiled by Ion on this thread. */
     ion::IonBuilder *ionBuilder;