Eagerly generate a single copy of Ion stubs and wrappers, bug 786146. r=dvander
authorBrian Hackett <bhackett1024@gmail.com>
Wed, 14 Nov 2012 06:46:31 -0800
changeset 113229 6ba78023b36730c1140355ddb6df4c7f9981b8e7
parent 113228 5a76abc509c7390370e091e04f5a2a5805f25e5f
child 113230 2df964896b603e61b08546695e7e5f229d722bb4
push id23864
push userryanvm@gmail.com
push dateThu, 15 Nov 2012 02:43:16 +0000
treeherdermozilla-central@7845cfa93e3f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdvander
bugs786146
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
Eagerly generate a single copy of Ion stubs and wrappers, bug 786146. r=dvander
js/src/ion/CodeGenerator.cpp
js/src/ion/Ion.cpp
js/src/ion/IonCompartment.h
js/src/ion/IonFrames.cpp
js/src/ion/IonFrames.h
js/src/ion/IonMacroAssembler.h
js/src/ion/VMFunctions.cpp
js/src/ion/VMFunctions.h
js/src/ion/arm/CodeGenerator-arm.cpp
js/src/ion/arm/Trampoline-arm.cpp
js/src/ion/shared/CodeGenerator-shared.cpp
js/src/ion/shared/CodeGenerator-x86-shared.cpp
js/src/ion/x64/CodeGenerator-x64.cpp
js/src/ion/x64/MacroAssembler-x64.h
js/src/ion/x64/Trampoline-x64.cpp
js/src/ion/x86/CodeGenerator-x86.cpp
js/src/ion/x86/Trampoline-x86.cpp
js/src/jsapi.cpp
js/src/jscntxt.h
js/src/jscompartment.cpp
js/src/jscompartment.h
js/src/jsgc.cpp
js/src/jsgcinlines.h
js/src/jsworkers.cpp
--- a/js/src/ion/CodeGenerator.cpp
+++ b/js/src/ion/CodeGenerator.cpp
@@ -199,60 +199,62 @@ CodeGenerator::visitPolyInlineDispatch(L
                 // Don't generate guard for final case
                 masm.jump(target->label());
             }
         }
     }
     return true;
 }
 
+typedef JSFlatString *(*IntToStringFn)(JSContext *, int);
+static const VMFunction IntToStringInfo =
+    FunctionInfo<IntToStringFn>(Int32ToString);
+
 bool
 CodeGenerator::visitIntToString(LIntToString *lir)
 {
     Register input = ToRegister(lir->input());
     Register output = ToRegister(lir->output());
 
-    typedef JSFlatString *(*pf)(JSContext *, int);
-    static const VMFunction IntToStringInfo = FunctionInfo<pf>(Int32ToString);
-
     OutOfLineCode *ool = oolCallVM(IntToStringInfo, lir, (ArgList(), input),
                                    StoreRegisterTo(output));
     if (!ool)
         return false;
 
     masm.branch32(Assembler::AboveOrEqual, input, Imm32(StaticStrings::INT_STATIC_LIMIT),
                   ool->entry());
 
     masm.movePtr(ImmWord(&gen->compartment->rt->staticStrings.intStaticTable), output);
     masm.loadPtr(BaseIndex(output, input, ScalePointer), output);
 
     masm.bind(ool->rejoin());
     return true;
 }
 
+typedef JSObject *(*CloneRegExpObjectFn)(JSContext *, JSObject *, JSObject *);
+static const VMFunction CloneRegExpObjectInfo =
+    FunctionInfo<CloneRegExpObjectFn>(CloneRegExpObject);
+
 bool
 CodeGenerator::visitRegExp(LRegExp *lir)
 {
     JSObject *proto = lir->mir()->getRegExpPrototype();
 
-    typedef JSObject *(*pf)(JSContext *, JSObject *, JSObject *);
-    static const VMFunction CloneRegExpObjectInfo = FunctionInfo<pf>(CloneRegExpObject);
-
     pushArg(ImmGCPtr(proto));
     pushArg(ImmGCPtr(lir->mir()->source()));
     return callVM(CloneRegExpObjectInfo, lir);
 }
 
+typedef bool (*ExecuteRegExpFn)(JSContext *cx, RegExpExecType type, HandleObject regexp,
+                                HandleString string, MutableHandleValue rval);
+static const VMFunction ExecuteRegExpInfo = FunctionInfo<ExecuteRegExpFn>(ExecuteRegExp);
+
 bool
 CodeGenerator::visitRegExpTest(LRegExpTest *lir)
 {
-    typedef bool (*pf)(JSContext *cx, RegExpExecType type, HandleObject regexp,
-                       HandleString string, MutableHandleValue rval);
-    static const VMFunction ExecuteRegExpInfo = FunctionInfo<pf>(ExecuteRegExp);
-
     pushArg(ToRegister(lir->string()));
     pushArg(ToRegister(lir->regexp()));
     pushArg(Imm32(RegExpTest));
     if (!callVM(ExecuteRegExpInfo, lir))
         return false;
 
     Register output = ToRegister(lir->output());
     Label notBool, end;
@@ -260,38 +262,36 @@ CodeGenerator::visitRegExpTest(LRegExpTe
     masm.unboxBoolean(JSReturnOperand, output);
     masm.jump(&end);
     masm.bind(&notBool);
     masm.mov(Imm32(0), output);
     masm.bind(&end);
     return true;
 }
 
+typedef JSObject *(*LambdaFn)(JSContext *, HandleFunction, HandleObject);
+static const VMFunction LambdaInfo =
+    FunctionInfo<LambdaFn>(js::Lambda);
+
 bool
 CodeGenerator::visitLambdaForSingleton(LLambdaForSingleton *lir)
 {
-    typedef JSObject *(*pf)(JSContext *, HandleFunction, HandleObject);
-    static const VMFunction Info = FunctionInfo<pf>(js::Lambda);
-
     pushArg(ToRegister(lir->scopeChain()));
     pushArg(ImmGCPtr(lir->mir()->fun()));
-    return callVM(Info, lir);
+    return callVM(LambdaInfo, lir);
 }
 
 bool
 CodeGenerator::visitLambda(LLambda *lir)
 {
     Register scopeChain = ToRegister(lir->scopeChain());
     Register output = ToRegister(lir->output());
     JSFunction *fun = lir->mir()->fun();
 
-    typedef JSObject *(*pf)(JSContext *, HandleFunction, HandleObject);
-    static const VMFunction Info = FunctionInfo<pf>(js::Lambda);
-
-    OutOfLineCode *ool = oolCallVM(Info, lir, (ArgList(), ImmGCPtr(fun), scopeChain),
+    OutOfLineCode *ool = oolCallVM(LambdaInfo, lir, (ArgList(), ImmGCPtr(fun), scopeChain),
                                    StoreRegisterTo(output));
     if (!ool)
         return false;
 
     JS_ASSERT(gen->compartment == fun->compartment());
     JS_ASSERT(!fun->hasSingletonType());
 
     masm.newGCThing(output, fun, ool->entry());
@@ -770,24 +770,24 @@ CodeGenerator::visitCallDOMNative(LCallD
     // Move the StackPointer back to its original location, unwinding the native exit frame.
     masm.adjustStack(IonDOMMethodExitFrameLayout::Size() - unusedStack);
     JS_ASSERT(masm.framePushed() == initialStack);
 
     dropArguments(call->numStackArgs() + 1);
     return true;
 }
 
+typedef bool (*InvokeFunctionFn)(JSContext *, JSFunction *, uint32, Value *, Value *);
+static const VMFunction InvokeFunctionInfo =
+    FunctionInfo<InvokeFunctionFn>(InvokeFunction);
 
 bool
 CodeGenerator::emitCallInvokeFunction(LInstruction *call, Register calleereg,
                                       uint32 argc, uint32 unusedStack)
 {
-    typedef bool (*pf)(JSContext *, JSFunction *, uint32, Value *, Value *);
-    static const VMFunction InvokeFunctionInfo = FunctionInfo<pf>(InvokeFunction);
-
     // Nestle %esp up to the argument vector.
     // Each path must account for framePushed_ separately, for callVM to be valid.
     masm.freeStack(unusedStack);
 
     pushArg(StackPointer); // argv.
     pushArg(Imm32(argc));  // argc.
     pushArg(calleereg);    // JSFunction *.
 
@@ -821,19 +821,17 @@ CodeGenerator::visitCallGeneric(LCallGen
 
     // Known-target case is handled by LCallKnown.
     JS_ASSERT(!call->hasSingleTarget());
     // Unknown constructor case is handled by LCallConstructor.
     JS_ASSERT(!call->mir()->isConstructing());
 
     // Generate an ArgumentsRectifier.
     IonCompartment *ion = gen->ionCompartment();
-    IonCode *argumentsRectifier = ion->getArgumentsRectifier(GetIonContext()->cx);
-    if (!argumentsRectifier)
-        return false;
+    IonCode *argumentsRectifier = ion->getArgumentsRectifier();
 
     masm.checkStackAlignment();
 
     // Guard that calleereg is actually a function object.
     masm.loadObjClass(calleereg, nargsreg);
     masm.cmpPtr(nargsreg, ImmWord(&js::FunctionClass));
     if (!bailoutIf(Assembler::NotEqual, call->snapshot()))
         return false;
@@ -980,31 +978,32 @@ CodeGenerator::visitCallKnown(LCallKnown
         masm.loadValue(Address(StackPointer, unusedStack), JSReturnOperand);
         masm.bind(&notPrimitive);
     }
 
     dropArguments(call->numStackArgs() + 1);
     return true;
 }
 
+typedef bool (*InvokeConstructorFn)(JSContext *, JSObject *, uint32, Value *, Value *);
+static const VMFunction InvokeConstructorInfo =
+    FunctionInfo<InvokeConstructorFn>(ion::InvokeConstructor);
+
 bool
 CodeGenerator::visitCallConstructor(LCallConstructor *call)
 {
     JS_ASSERT(call->mir()->isConstructing());
 
     // Holds the function object.
     const LAllocation *callee = call->getFunction();
     Register calleereg = ToRegister(callee);
 
     uint32 callargslot = call->argslot();
     uint32 unusedStack = StackOffsetOfPassedArg(callargslot);
 
-    typedef bool (*pf)(JSContext *, JSObject *, uint32, Value *, Value *);
-    static const VMFunction InvokeConstructorInfo = FunctionInfo<pf>(ion::InvokeConstructor);
-
     // Nestle %esp up to the argument vector.
     masm.freeStack(unusedStack);
 
     pushArg(StackPointer);                  // argv.
     pushArg(Imm32(call->numActualArgs()));  // argc.
     pushArg(calleereg);                     // JSFunction *.
 
     if (!callVM(InvokeConstructorInfo, call))
@@ -1018,19 +1017,16 @@ CodeGenerator::visitCallConstructor(LCal
 }
 
 bool
 CodeGenerator::emitCallInvokeFunction(LApplyArgsGeneric *apply, Register extraStackSize)
 {
     Register objreg = ToRegister(apply->getTempObject());
     JS_ASSERT(objreg != extraStackSize);
 
-    typedef bool (*pf)(JSContext *, JSFunction *, uint32, Value *, Value *);
-    static const VMFunction InvokeFunctionInfo = FunctionInfo<pf>(InvokeFunction);
-
     // Push the space used by the arguments.
     masm.movePtr(StackPointer, objreg);
     masm.Push(extraStackSize);
 
     pushArg(objreg);                           // argv.
     pushArg(ToRegister(apply->getArgc()));     // argc.
     pushArg(ToRegister(apply->getFunction())); // JSFunction *.
 
@@ -1192,19 +1188,17 @@ CodeGenerator::visitApplyArgsGeneric(LAp
         }
 
         // Argument fixup needed. Get ready to call the argumentsRectifier.
         {
             masm.bind(&underflow);
 
             // Hardcode the address of the argumentsRectifier code.
             IonCompartment *ion = gen->ionCompartment();
-            IonCode *argumentsRectifier = ion->getArgumentsRectifier(GetIonContext()->cx);
-            if (!argumentsRectifier)
-                return false;
+            IonCode *argumentsRectifier = ion->getArgumentsRectifier();
 
             JS_ASSERT(ArgumentsRectifierReg != objreg);
             masm.movePtr(ImmGCPtr(argumentsRectifier), objreg); // Necessary for GC marking.
             masm.movePtr(Address(objreg, IonCode::offsetOfCode()), objreg);
             masm.movePtr(argcreg, ArgumentsRectifierReg);
         }
 
         masm.bind(&rejoin);
@@ -1330,47 +1324,48 @@ CodeGenerator::visitCheckOverRecursed(LC
 
     // Conditional forward (unlikely) branch to failure.
     masm.branchPtr(Assembler::BelowOrEqual, StackPointer, limitReg, ool->entry());
     masm.bind(ool->rejoin());
 
     return true;
 }
 
+typedef bool (*DefVarOrConstFn)(JSContext *, HandlePropertyName, unsigned, HandleObject);
+static const VMFunction DefVarOrConstInfo =
+    FunctionInfo<DefVarOrConstFn>(DefVarOrConst);
+
 bool
 CodeGenerator::visitDefVar(LDefVar *lir)
 {
     Register scopeChain = ToRegister(lir->getScopeChain());
     Register nameTemp   = ToRegister(lir->nameTemp());
 
-    typedef bool (*pf)(JSContext *, HandlePropertyName, unsigned, HandleObject);
-    static const VMFunction DefVarOrConstInfo = FunctionInfo<pf>(DefVarOrConst);
-
     masm.movePtr(ImmGCPtr(lir->mir()->name()), nameTemp);
 
     pushArg(scopeChain); // JSObject *
     pushArg(Imm32(lir->mir()->attrs())); // unsigned
     pushArg(nameTemp); // PropertyName *
 
     if (!callVM(DefVarOrConstInfo, lir))
         return false;
 
     return true;
 }
 
+typedef bool (*ReportOverRecursedFn)(JSContext *);
+static const VMFunction CheckOverRecursedInfo =
+    FunctionInfo<ReportOverRecursedFn>(CheckOverRecursed);
+
 bool
 CodeGenerator::visitCheckOverRecursedFailure(CheckOverRecursedFailure *ool)
 {
     // The OOL path is hit if the recursion depth has been exceeded.
     // Throw an InternalError for over-recursion.
 
-    typedef bool (*pf)(JSContext *);
-    static const VMFunction CheckOverRecursedInfo =
-        FunctionInfo<pf>(CheckOverRecursed);
-
     // LFunctionEnvironment can appear before LCheckOverRecursed, so we have
     // to save all live registers to avoid crashes if CheckOverRecursed triggers
     // a GC.
     saveLive(ool->lir());
 
     if (!callVM(CheckOverRecursedInfo, ool->lir()))
         return false;
 
@@ -1416,24 +1411,25 @@ class OutOfLineNewArray : public OutOfLi
         return codegen->visitOutOfLineNewArray(this);
     }
 
     LNewArray *lir() const {
         return lir_;
     }
 };
 
+typedef JSObject *(*NewInitArrayFn)(JSContext *, uint32, types::TypeObject *);
+static const VMFunction NewInitArrayInfo =
+    FunctionInfo<NewInitArrayFn>(NewInitArray);
+
 bool
 CodeGenerator::visitNewArrayCallVM(LNewArray *lir)
 {
     Register objReg = ToRegister(lir->output());
 
-    typedef JSObject *(*pf)(JSContext *, uint32, types::TypeObject *);
-    static const VMFunction NewInitArrayInfo = FunctionInfo<pf>(NewInitArray);
-
     JS_ASSERT(!lir->isCall());
     saveLive(lir);
 
     JSObject *templateObject = lir->mir()->templateObject();
     types::TypeObject *type = templateObject->hasSingletonType() ? NULL : templateObject->type();
 
     pushArg(ImmGCPtr(type));
     pushArg(Imm32(lir->mir()->count()));
@@ -1526,29 +1522,29 @@ class OutOfLineNewObject : public OutOfL
         return codegen->visitOutOfLineNewObject(this);
     }
 
     LNewObject *lir() const {
         return lir_;
     }
 };
 
+typedef JSObject *(*NewInitObjectFn)(JSContext *, HandleObject);
+static const VMFunction NewInitObjectInfo = FunctionInfo<NewInitObjectFn>(NewInitObject);
+
 bool
 CodeGenerator::visitNewObjectVMCall(LNewObject *lir)
 {
     Register objReg = ToRegister(lir->output());
 
-    typedef JSObject *(*pf)(JSContext *, HandleObject);
-    static const VMFunction Info = FunctionInfo<pf>(NewInitObject);
-
     JS_ASSERT(!lir->isCall());
     saveLive(lir);
 
     pushArg(ImmGCPtr(lir->mir()->templateObject()));
-    if (!callVM(Info, lir))
+    if (!callVM(NewInitObjectInfo, lir))
         return false;
 
     if (ReturnReg != objReg)
         masm.movePtr(ReturnReg, objReg);
 
     restoreLive(lir);
     return true;
 }
@@ -1578,24 +1574,26 @@ bool
 CodeGenerator::visitOutOfLineNewObject(OutOfLineNewObject *ool)
 {
     if (!visitNewObjectVMCall(ool->lir()))
         return false;
     masm.jump(ool->rejoin());
     return true;
 }
 
+typedef JSObject *(*NewCallObjectFn)(JSContext *, HandleShape,
+                                     HandleTypeObject, HeapSlot *);
+static const VMFunction NewCallObjectInfo =
+    FunctionInfo<NewCallObjectFn>(NewCallObject);
+
 bool
 CodeGenerator::visitNewCallObject(LNewCallObject *lir)
 {
     Register obj = ToRegister(lir->output());
 
-    typedef JSObject *(*pf)(JSContext *, HandleShape, HandleTypeObject, HeapSlot *);
-    static const VMFunction NewCallObjectInfo = FunctionInfo<pf>(NewCallObject);
-
     JSObject *templateObj = lir->mir()->templateObj();
 
     // If we have a template object, we can inline call object creation.
     OutOfLineCode *ool;
     if (lir->slots()->isRegister()) {
         ool = oolCallVM(NewCallObjectInfo, lir,
                         (ArgList(), ImmGCPtr(templateObj->lastProperty()),
                                     ImmGCPtr(templateObj->type()),
@@ -1615,26 +1613,26 @@ CodeGenerator::visitNewCallObject(LNewCa
     masm.initGCThing(obj, templateObj);
 
     if (lir->slots()->isRegister())
         masm.storePtr(ToRegister(lir->slots()), Address(obj, JSObject::offsetOfSlots()));
     masm.bind(ool->rejoin());
     return true;
 }
 
+typedef JSObject *(*NewStringObjectFn)(JSContext *, HandleString);
+static const VMFunction NewStringObjectInfo = FunctionInfo<NewStringObjectFn>(NewStringObject);
+
 bool
 CodeGenerator::visitNewStringObject(LNewStringObject *lir)
 {
     Register input = ToRegister(lir->input());
     Register output = ToRegister(lir->output());
     Register temp = ToRegister(lir->temp());
 
-    typedef JSObject *(*pf)(JSContext *, HandleString);
-    static const VMFunction NewStringObjectInfo = FunctionInfo<pf>(NewStringObject);
-
     StringObject *templateObj = lir->mir()->templateObj();
 
     OutOfLineCode *ool = oolCallVM(NewStringObjectInfo, lir, (ArgList(), input),
                                    StoreRegisterTo(output));
     if (!ool)
         return false;
 
     masm.newGCThing(output, templateObj, ool->entry());
@@ -1644,70 +1642,73 @@ CodeGenerator::visitNewStringObject(LNew
 
     masm.storeValue(JSVAL_TYPE_STRING, input, Address(output, StringObject::offsetOfPrimitiveValue()));
     masm.storeValue(JSVAL_TYPE_INT32, temp, Address(output, StringObject::offsetOfLength()));
 
     masm.bind(ool->rejoin());
     return true;
 }
 
+typedef bool(*InitPropFn)(JSContext *cx, HandleObject obj,
+                          HandlePropertyName name, HandleValue value);
+static const VMFunction InitPropInfo =
+    FunctionInfo<InitPropFn>(InitProp);
+
 bool
 CodeGenerator::visitInitProp(LInitProp *lir)
 {
     Register objReg = ToRegister(lir->getObject());
 
-    typedef bool(*pf)(JSContext *, HandleObject, HandlePropertyName, HandleValue);
-    static const VMFunction InitPropInfo = FunctionInfo<pf>(InitProp);
-
     pushArg(ToValue(lir, LInitProp::ValueIndex));
     pushArg(ImmGCPtr(lir->mir()->propertyName()));
     pushArg(objReg);
 
     return callVM(InitPropInfo, lir);
 }
 
+typedef JSObject *(*NewGCThingFn)(JSContext *cx, gc::AllocKind allocKind, size_t thingSize);
+static const VMFunction NewGCThingInfo =
+    FunctionInfo<NewGCThingFn>(js::ion::NewGCThing);
+
 bool
 CodeGenerator::visitCreateThis(LCreateThis *lir)
 {
     JS_ASSERT(lir->mir()->hasTemplateObject());
 
     JSObject *templateObject = lir->mir()->getTemplateObject();
     gc::AllocKind allocKind = templateObject->getAllocKind();
     int thingSize = (int)gc::Arena::thingSize(allocKind);
     Register objReg = ToRegister(lir->output());
 
-    typedef JSObject *(*pf)(JSContext *cx, gc::AllocKind allocKind, size_t thingSize);
-    static const VMFunction NewGCThingInfo = FunctionInfo<pf>(js::ion::NewGCThing);
-
     OutOfLineCode *ool = oolCallVM(NewGCThingInfo, lir,
                                    (ArgList(), Imm32(allocKind), Imm32(thingSize)),
                                    StoreRegisterTo(objReg));
     if (!ool)
         return false;
 
     // Allocate. If the FreeList is empty, call to VM, which may GC.
     masm.newGCThing(objReg, templateObject, ool->entry());
 
     // Initialize based on the templateObject.
     masm.bind(ool->rejoin());
     masm.initGCThing(objReg, templateObject);
 
     return true;
 }
 
+typedef JSObject *(*CreateThisFn)(JSContext *cx, HandleObject callee, JSObject *proto);
+static const VMFunction CreateThisInfo =
+    FunctionInfo<CreateThisFn>(js_CreateThisForFunctionWithProto);
+
 bool
 CodeGenerator::visitCreateThisVM(LCreateThisVM *lir)
 {
     const LAllocation *proto = lir->getPrototype();
     const LAllocation *callee = lir->getCallee();
 
-    typedef JSObject *(*pf)(JSContext *cx, HandleObject callee, JSObject *proto);
-    static const VMFunction CreateThisInfo =
-        FunctionInfo<pf>(js_CreateThisForFunctionWithProto);
-
     // Push arguments.
     if (proto->isConstant())
         pushArg(ImmGCPtr(&proto->toConstant()->toObject()));
     else
         pushArg(ToRegister(proto));
 
     if (callee->isConstant())
         pushArg(ImmGCPtr(&callee->toConstant()->toObject()));
@@ -1928,27 +1929,29 @@ CodeGenerator::visitModD(LModD *ins)
     masm.setupUnalignedABICall(2, temp);
     masm.passABIArg(lhs);
     masm.passABIArg(rhs);
 
     masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, NumberMod), MacroAssembler::DOUBLE);
     return true;
 }
 
+typedef bool (*BinaryFn)(JSContext *, HandleScript, jsbytecode *,
+                         HandleValue, HandleValue, Value *);
+
+static const VMFunction AddInfo = FunctionInfo<BinaryFn>(js::AddValues);
+static const VMFunction SubInfo = FunctionInfo<BinaryFn>(js::SubValues);
+static const VMFunction MulInfo = FunctionInfo<BinaryFn>(js::MulValues);
+static const VMFunction DivInfo = FunctionInfo<BinaryFn>(js::DivValues);
+static const VMFunction ModInfo = FunctionInfo<BinaryFn>(js::ModValues);
+static const VMFunction UrshInfo = FunctionInfo<BinaryFn>(js::UrshValues);
+
 bool
 CodeGenerator::visitBinaryV(LBinaryV *lir)
 {
-    typedef bool (*pf)(JSContext *, HandleScript, jsbytecode *, HandleValue, HandleValue, Value *);
-    static const VMFunction AddInfo = FunctionInfo<pf>(js::AddValues);
-    static const VMFunction SubInfo = FunctionInfo<pf>(js::SubValues);
-    static const VMFunction MulInfo = FunctionInfo<pf>(js::MulValues);
-    static const VMFunction DivInfo = FunctionInfo<pf>(js::DivValues);
-    static const VMFunction ModInfo = FunctionInfo<pf>(js::ModValues);
-    static const VMFunction UrshInfo = FunctionInfo<pf>(js::UrshValues);
-
     pushArg(ToValue(lir, LBinaryV::RhsInput));
     pushArg(ToValue(lir, LBinaryV::LhsInput));
     pushArg(ImmWord(lir->mirRaw()->toInstruction()->resumePoint()->pc()));
     pushArg(ImmGCPtr(current->mir()->info().script()));
 
     switch (lir->jsop()) {
       case JSOP_ADD:
         return callVM(AddInfo, lir);
@@ -1969,29 +1972,31 @@ CodeGenerator::visitBinaryV(LBinaryV *li
         return callVM(UrshInfo, lir);
 
       default:
         JS_NOT_REACHED("Unexpected binary op");
         return false;
     }
 }
 
+typedef bool (*StringCompareFn)(JSContext *, HandleString, HandleString, JSBool *);
+static const VMFunction stringsEqualInfo =
+    FunctionInfo<StringCompareFn>(ion::StringsEqual<true>);
+static const VMFunction stringsNotEqualInfo =
+    FunctionInfo<StringCompareFn>(ion::StringsEqual<false>);
+
 bool
 CodeGenerator::visitCompareS(LCompareS *lir)
 {
     JSOp op = lir->mir()->jsop();
     Register left = ToRegister(lir->left());
     Register right = ToRegister(lir->right());
     Register output = ToRegister(lir->output());
     Register temp = ToRegister(lir->temp());
 
-    typedef bool (*pf)(JSContext *, HandleString, HandleString, JSBool *);
-    static const VMFunction stringsEqualInfo = FunctionInfo<pf>(ion::StringsEqual<true>);
-    static const VMFunction stringsNotEqualInfo = FunctionInfo<pf>(ion::StringsEqual<false>);
-
     OutOfLineCode *ool = NULL;
     if (op == JSOP_EQ || op == JSOP_STRICTEQ) {
         ool = oolCallVM(stringsEqualInfo, lir, (ArgList(), left, right),  StoreRegisterTo(output));
     } else {
         JS_ASSERT(op == JSOP_NE || op == JSOP_STRICTNE);
         ool = oolCallVM(stringsNotEqualInfo, lir, (ArgList(), left, right), StoreRegisterTo(output));
     }
 
@@ -2025,29 +2030,29 @@ CodeGenerator::visitCompareS(LCompareS *
     masm.rshiftPtr(Imm32(JSString::LENGTH_SHIFT), temp);
     masm.branchPtr(Assembler::Equal, output, temp, ool->entry());
     masm.move32(Imm32(op == JSOP_NE || op == JSOP_STRICTNE), output);
 
     masm.bind(ool->rejoin());
     return true;
 }
 
+typedef bool (*CompareFn)(JSContext *, HandleValue, HandleValue, JSBool *);
+static const VMFunction EqInfo = FunctionInfo<CompareFn>(ion::LooselyEqual<true>);
+static const VMFunction NeInfo = FunctionInfo<CompareFn>(ion::LooselyEqual<false>);
+static const VMFunction StrictEqInfo = FunctionInfo<CompareFn>(ion::StrictlyEqual<true>);
+static const VMFunction StrictNeInfo = FunctionInfo<CompareFn>(ion::StrictlyEqual<false>);
+static const VMFunction LtInfo = FunctionInfo<CompareFn>(ion::LessThan);
+static const VMFunction LeInfo = FunctionInfo<CompareFn>(ion::LessThanOrEqual);
+static const VMFunction GtInfo = FunctionInfo<CompareFn>(ion::GreaterThan);
+static const VMFunction GeInfo = FunctionInfo<CompareFn>(ion::GreaterThanOrEqual);
+
 bool
 CodeGenerator::visitCompareV(LCompareV *lir)
 {
-    typedef bool (*pf)(JSContext *, HandleValue, HandleValue, JSBool *);
-    static const VMFunction EqInfo = FunctionInfo<pf>(ion::LooselyEqual<true>);
-    static const VMFunction NeInfo = FunctionInfo<pf>(ion::LooselyEqual<false>);
-    static const VMFunction StrictEqInfo = FunctionInfo<pf>(ion::StrictlyEqual<true>);
-    static const VMFunction StrictNeInfo = FunctionInfo<pf>(ion::StrictlyEqual<false>);
-    static const VMFunction LtInfo = FunctionInfo<pf>(ion::LessThan);
-    static const VMFunction LeInfo = FunctionInfo<pf>(ion::LessThanOrEqual);
-    static const VMFunction GtInfo = FunctionInfo<pf>(ion::GreaterThan);
-    static const VMFunction GeInfo = FunctionInfo<pf>(ion::GreaterThanOrEqual);
-
     pushArg(ToValue(lir, LBinaryV::RhsInput));
     pushArg(ToValue(lir, LBinaryV::LhsInput));
 
     switch (lir->mir()->jsop()) {
       case JSOP_EQ:
         return callVM(EqInfo, lir);
 
       case JSOP_NE:
@@ -2153,39 +2158,40 @@ CodeGenerator::visitIsNullOrUndefinedAnd
         cond = masm.testNull(cond, value);
     else
         cond = masm.testUndefined(cond, value);
 
     emitBranch(cond, lir->ifTrue(), lir->ifFalse());
     return true;
 }
 
+typedef JSString *(*ConcatStringsFn)(JSContext *, HandleString, HandleString);
+static const VMFunction ConcatStringsInfo = FunctionInfo<ConcatStringsFn>(js_ConcatStrings);
+
 bool
 CodeGenerator::visitConcat(LConcat *lir)
 {
-    typedef JSString *(*pf)(JSContext *, HandleString, HandleString);
-    static const VMFunction js_ConcatStringsInfo = FunctionInfo<pf>(js_ConcatStrings);
-
     pushArg(ToRegister(lir->rhs()));
     pushArg(ToRegister(lir->lhs()));
-    if (!callVM(js_ConcatStringsInfo, lir))
+    if (!callVM(ConcatStringsInfo, lir))
         return false;
     return true;
 }
 
+typedef bool (*EnsureLinearFn)(JSContext *, JSString *);
+static const VMFunction EnsureLinearInfo = FunctionInfo<EnsureLinearFn>(JSString::ensureLinear);
+
 bool
 CodeGenerator::visitCharCodeAt(LCharCodeAt *lir)
 {
     Register str = ToRegister(lir->str());
     Register index = ToRegister(lir->index());
     Register output = ToRegister(lir->output());
 
-    typedef bool (*pf)(JSContext *, JSString *);
-    static const VMFunction ensureLinearInfo = FunctionInfo<pf>(JSString::ensureLinear);
-    OutOfLineCode *ool = oolCallVM(ensureLinearInfo, lir, (ArgList(), str), StoreNothing());
+    OutOfLineCode *ool = oolCallVM(EnsureLinearInfo, lir, (ArgList(), str), StoreNothing());
     if (!ool)
         return false;
 
     Address lengthAndFlagsAddr(str, JSString::offsetOfLengthAndFlags());
     masm.loadPtr(lengthAndFlagsAddr, output);
 
     masm.branchTest32(Assembler::Zero, output, Imm32(JSString::FLAGS_MASK), ool->entry());
     masm.bind(ool->rejoin());
@@ -2193,25 +2199,26 @@ CodeGenerator::visitCharCodeAt(LCharCode
     // getChars
     Address charsAddr(str, JSString::offsetOfChars());
     masm.loadPtr(charsAddr, output);
     masm.load16ZeroExtend(BaseIndex(output, index, TimesTwo, 0), output);
 
     return true;
 }
 
+typedef JSFlatString *(*StringFromCharCodeFn)(JSContext *, int32_t);
+static const VMFunction StringFromCharCodeInfo = FunctionInfo<StringFromCharCodeFn>(ion::StringFromCharCode);
+
 bool
 CodeGenerator::visitFromCharCode(LFromCharCode *lir)
 {
     Register code = ToRegister(lir->code());
     Register output = ToRegister(lir->output());
 
-    typedef JSFlatString *(*pf)(JSContext *, int32_t);
-    static const VMFunction Info = FunctionInfo<pf>(ion::StringFromCharCode);
-    OutOfLineCode *ool = oolCallVM(Info, lir, (ArgList(), code), StoreRegisterTo(output));
+    OutOfLineCode *ool = oolCallVM(StringFromCharCodeInfo, lir, (ArgList(), code), StoreRegisterTo(output));
     if (!ool)
         return false;
 
     // OOL path if code >= UNIT_STATIC_LIMIT.
     masm.branch32(Assembler::AboveOrEqual, code, Imm32(StaticStrings::UNIT_STATIC_LIMIT),
                   ool->entry());
 
     masm.movePtr(ImmWord(&gen->compartment->rt->staticStrings.unitStaticTable), output);
@@ -2446,16 +2453,21 @@ CodeGenerator::visitStoreElementHoleV(LS
         masm.storeValue(value, Address(elements, ToInt32(lir->index()) * sizeof(js::Value)));
     else
         masm.storeValue(value, BaseIndex(elements, ToRegister(lir->index()), TimesEight));
 
     masm.bind(ool->rejoin());
     return true;
 }
 
+typedef bool (*SetObjectElementFn)(JSContext *, HandleObject,
+                                   HandleValue, HandleValue, JSBool strict);
+static const VMFunction SetObjectElementInfo =
+    FunctionInfo<SetObjectElementFn>(SetObjectElement);
+
 bool
 CodeGenerator::visitOutOfLineStoreElementHole(OutOfLineStoreElementHole *ool)
 {
     Register object, elements;
     LInstruction *ins = ool->ins();
     const LAllocation *index;
     MIRType valueType;
     ConstantOrRegister value;
@@ -2515,50 +2527,48 @@ CodeGenerator::visitOutOfLineStoreElemen
     } else {
         // Jump to the inline path where we will store the value.
         masm.jump(ool->rejoinStore());
     }
 
     masm.bind(&callStub);
     saveLive(ins);
 
-    typedef bool (*pf)(JSContext *, HandleObject, HandleValue, HandleValue, JSBool strict);
-    static const VMFunction Info = FunctionInfo<pf>(SetObjectElement);
-
     pushArg(Imm32(current->mir()->strictModeCode()));
     pushArg(value);
     if (index->isConstant())
         pushArg(*index->toConstant());
     else
         pushArg(TypedOrValueRegister(MIRType_Int32, ToAnyRegister(index)));
     pushArg(object);
-    if (!callVM(Info, ins))
+    if (!callVM(SetObjectElementInfo, ins))
         return false;
 
     restoreLive(ins);
     masm.jump(ool->rejoin());
     return true;
 }
 
+typedef bool (*ArrayPopShiftFn)(JSContext *, HandleObject, MutableHandleValue);
+static const VMFunction ArrayPopDenseInfo = FunctionInfo<ArrayPopShiftFn>(ion::ArrayPopDense);
+static const VMFunction ArrayShiftDenseInfo = FunctionInfo<ArrayPopShiftFn>(ion::ArrayShiftDense);
+
 bool
 CodeGenerator::emitArrayPopShift(LInstruction *lir, const MArrayPopShift *mir, Register obj,
                                  Register elementsTemp, Register lengthTemp, TypedOrValueRegister out)
 {
     OutOfLineCode *ool;
-    typedef bool (*pf)(JSContext *, HandleObject, MutableHandleValue);
 
     if (mir->mode() == MArrayPopShift::Pop) {
-        static const VMFunction Info = FunctionInfo<pf>(ion::ArrayPopDense);
-        ool = oolCallVM(Info, lir, (ArgList(), obj), StoreValueTo(out));
+        ool = oolCallVM(ArrayPopDenseInfo, lir, (ArgList(), obj), StoreValueTo(out));
         if (!ool)
             return false;
     } else {
         JS_ASSERT(mir->mode() == MArrayPopShift::Shift);
-        static const VMFunction Info = FunctionInfo<pf>(ion::ArrayShiftDense);
-        ool = oolCallVM(Info, lir, (ArgList(), obj), StoreValueTo(out));
+        ool = oolCallVM(ArrayShiftDenseInfo, lir, (ArgList(), obj), StoreValueTo(out));
         if (!ool)
             return false;
     }
 
     // VM call if a write barrier is necessary.
     masm.branchTestNeedsBarrier(Assembler::NonZero, lengthTemp, ool->entry());
 
     // Load elements and length.
@@ -2631,23 +2641,25 @@ CodeGenerator::visitArrayPopShiftT(LArra
 {
     Register obj = ToRegister(lir->object());
     Register elements = ToRegister(lir->temp0());
     Register length = ToRegister(lir->temp1());
     TypedOrValueRegister out(lir->mir()->type(), ToAnyRegister(lir->output()));
     return emitArrayPopShift(lir, lir->mir(), obj, elements, length, out);
 }
 
+typedef bool (*ArrayPushDenseFn)(JSContext *, HandleObject, HandleValue, uint32_t *);
+static const VMFunction ArrayPushDenseInfo =
+    FunctionInfo<ArrayPushDenseFn>(ion::ArrayPushDense);
+
 bool
 CodeGenerator::emitArrayPush(LInstruction *lir, const MArrayPush *mir, Register obj,
                              ConstantOrRegister value, Register elementsTemp, Register length)
 {
-    typedef bool (*pf)(JSContext *, HandleObject, HandleValue, uint32_t *);
-    static const VMFunction Info = FunctionInfo<pf>(ion::ArrayPushDense);
-    OutOfLineCode *ool = oolCallVM(Info, lir, (ArgList(), obj, value), StoreRegisterTo(length));
+    OutOfLineCode *ool = oolCallVM(ArrayPushDenseInfo, lir, (ArgList(), obj, value), StoreRegisterTo(length));
     if (!ool)
         return false;
 
     // Load elements and length.
     masm.loadPtr(Address(obj, JSObject::offsetOfElements()), elementsTemp);
     masm.load32(Address(elementsTemp, ObjectElements::offsetOfLength()), length);
 
     Int32Key key = Int32Key(length);
@@ -2689,16 +2701,19 @@ CodeGenerator::visitArrayPushT(LArrayPus
     ConstantOrRegister value;
     if (lir->value()->isConstant())
         value = ConstantOrRegister(*lir->value()->toConstant());
     else
         value = TypedOrValueRegister(lir->mir()->value()->type(), ToAnyRegister(lir->value()));
     return emitArrayPush(lir, lir->mir(), obj, value, elementsTemp, length);
 }
 
+typedef JSObject *(*ArrayConcatDenseFn)(JSContext *, HandleObject, HandleObject, HandleObject);
+static const VMFunction ArrayConcatDenseInfo = FunctionInfo<ArrayConcatDenseFn>(ArrayConcatDense);
+
 bool
 CodeGenerator::visitArrayConcat(LArrayConcat *lir)
 {
     Register lhs = ToRegister(lir->lhs());
     Register rhs = ToRegister(lir->rhs());
     Register temp1 = ToRegister(lir->temp1());
     Register temp2 = ToRegister(lir->temp2());
 
@@ -2720,48 +2735,43 @@ CodeGenerator::visitArrayConcat(LArrayCo
     masm.initGCThing(temp1, templateObj);
     masm.jump(&call);
     {
         masm.bind(&fail);
         masm.movePtr(ImmWord((void *)NULL), temp1);
     }
     masm.bind(&call);
 
-    typedef JSObject *(*pf)(JSContext *, HandleObject, HandleObject, HandleObject);
-    static const VMFunction Info = FunctionInfo<pf>(ArrayConcatDense);
-
     pushArg(temp1);
     pushArg(ToRegister(lir->rhs()));
     pushArg(ToRegister(lir->lhs()));
-    return callVM(Info, lir);
+    return callVM(ArrayConcatDenseInfo, lir);
 }
 
+typedef JSObject *(*GetIteratorObjectFn)(JSContext *, HandleObject, uint32_t);
+static const VMFunction GetIteratorObjectInfo = FunctionInfo<GetIteratorObjectFn>(GetIteratorObject);
+
 bool
 CodeGenerator::visitCallIteratorStart(LCallIteratorStart *lir)
 {
-    typedef JSObject *(*pf)(JSContext *, HandleObject, uint32_t);
-    static const VMFunction Info = FunctionInfo<pf>(GetIteratorObject);
-
     pushArg(Imm32(lir->mir()->flags()));
     pushArg(ToRegister(lir->object()));
-    return callVM(Info, lir);
+    return callVM(GetIteratorObjectInfo, lir);
 }
 
 bool
 CodeGenerator::visitIteratorStart(LIteratorStart *lir)
 {
     const Register obj = ToRegister(lir->object());
     const Register output = ToRegister(lir->output());
 
     uint32_t flags = lir->mir()->flags();
 
-    typedef JSObject *(*pf)(JSContext *, HandleObject, uint32_t);
-    static const VMFunction Info = FunctionInfo<pf>(GetIteratorObject);
-
-    OutOfLineCode *ool = oolCallVM(Info, lir, (ArgList(), obj, Imm32(flags)), StoreRegisterTo(output));
+    OutOfLineCode *ool = oolCallVM(GetIteratorObjectInfo, lir,
+                                   (ArgList(), obj, Imm32(flags)), StoreRegisterTo(output));
     if (!ool)
         return false;
 
     const Register temp1 = ToRegister(lir->temp1());
     const Register temp2 = ToRegister(lir->temp2());
     const Register niTemp = ToRegister(lir->temp3()); // Holds the NativeIterator object.
 
     // Iterators other than for-in should use LCallIteratorStart.
@@ -2830,27 +2840,27 @@ LoadNativeIterator(MacroAssembler &masm,
 
     // Test class.
     masm.branchTestObjClass(Assembler::NotEqual, obj, dest, &PropertyIteratorObject::class_, failures);
 
     // Load NativeIterator object.
     masm.loadObjPrivate(obj, JSObject::ITER_CLASS_NFIXED_SLOTS, dest);
 }
 
+typedef bool (*IteratorNextFn)(JSContext *, HandleObject, MutableHandleValue);
+static const VMFunction IteratorNextInfo = FunctionInfo<IteratorNextFn>(js_IteratorNext);
+
 bool
 CodeGenerator::visitIteratorNext(LIteratorNext *lir)
 {
     const Register obj = ToRegister(lir->object());
     const Register temp = ToRegister(lir->temp());
     const ValueOperand output = ToOutValue(lir);
 
-    typedef bool (*pf)(JSContext *, HandleObject, MutableHandleValue);
-    static const VMFunction Info = FunctionInfo<pf>(js_IteratorNext);
-
-    OutOfLineCode *ool = oolCallVM(Info, lir, (ArgList(), obj), StoreValueTo(output));
+    OutOfLineCode *ool = oolCallVM(IteratorNextInfo, lir, (ArgList(), obj), StoreValueTo(output));
     if (!ool)
         return false;
 
     LoadNativeIterator(masm, obj, temp, ool->entry());
 
     masm.branchTest32(Assembler::NonZero, Address(temp, offsetof(NativeIterator, flags)),
                       Imm32(JSITER_FOREACH), ool->entry());
 
@@ -2861,26 +2871,28 @@ CodeGenerator::visitIteratorNext(LIterat
 
     // Increase the cursor.
     masm.addPtr(Imm32(sizeof(JSString *)), Address(temp, offsetof(NativeIterator, props_cursor)));
 
     masm.bind(ool->rejoin());
     return true;
 }
 
+typedef bool (*IteratorMoreFn)(JSContext *, HandleObject, JSBool *);
+static const VMFunction IteratorMoreInfo = FunctionInfo<IteratorMoreFn>(ion::IteratorMore);
+
 bool
 CodeGenerator::visitIteratorMore(LIteratorMore *lir)
 {
     const Register obj = ToRegister(lir->object());
     const Register output = ToRegister(lir->output());
     const Register temp = ToRegister(lir->temp());
 
-    typedef bool (*pf)(JSContext *, HandleObject, JSBool *);
-    static const VMFunction Info = FunctionInfo<pf>(ion::IteratorMore);
-    OutOfLineCode *ool = oolCallVM(Info, lir, (ArgList(), obj), StoreRegisterTo(output));
+    OutOfLineCode *ool = oolCallVM(IteratorMoreInfo, lir,
+                                   (ArgList(), obj), StoreRegisterTo(output));
     if (!ool)
         return false;
 
     LoadNativeIterator(masm, obj, output, ool->entry());
 
     masm.branchTest32(Assembler::NonZero, Address(output, offsetof(NativeIterator, flags)),
                       Imm32(JSITER_FOREACH), ool->entry());
 
@@ -2888,27 +2900,27 @@ CodeGenerator::visitIteratorMore(LIterat
     masm.loadPtr(Address(output, offsetof(NativeIterator, props_end)), temp);
     masm.cmpPtr(Address(output, offsetof(NativeIterator, props_cursor)), temp);
     emitSet(Assembler::LessThan, output);
 
     masm.bind(ool->rejoin());
     return true;
 }
 
+typedef bool (*CloseIteratorFn)(JSContext *, HandleObject);
+static const VMFunction CloseIteratorInfo = FunctionInfo<CloseIteratorFn>(CloseIterator);
+
 bool
 CodeGenerator::visitIteratorEnd(LIteratorEnd *lir)
 {
     const Register obj = ToRegister(lir->object());
     const Register temp1 = ToRegister(lir->temp1());
     const Register temp2 = ToRegister(lir->temp2());
 
-    typedef bool (*pf)(JSContext *, HandleObject);
-    static const VMFunction Info = FunctionInfo<pf>(CloseIterator);
-
-    OutOfLineCode *ool = oolCallVM(Info, lir, (ArgList(), obj), StoreNothing());
+    OutOfLineCode *ool = oolCallVM(CloseIteratorInfo, lir, (ArgList(), obj), StoreNothing());
     if (!ool)
         return false;
 
     LoadNativeIterator(masm, obj, temp1, ool->entry());
 
     masm.branchTest32(Assembler::Zero, Address(temp1, offsetof(NativeIterator, flags)),
                       Imm32(JSITER_ENUMERATE), ool->entry());
 
@@ -2971,17 +2983,17 @@ CodeGenerator::generate()
 
     // 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(cx, frameClass_);
+        deoptTable_ = cx->compartment->ionCompartment()->getBailoutTable(frameClass_);
         if (!deoptTable_)
             return false;
     }
 
     if (!generatePrologue())
         return false;
     if (!generateBody())
         return false;
@@ -3121,55 +3133,50 @@ CodeGenerator::visitOutOfLineUnboxDouble
         if (!bailoutIf(cond, ins->snapshot()))
             return false;
     }
     masm.int32ValueToDouble(value, ToFloatRegister(ins->output()));
     masm.jump(ool->rejoin());
     return true;
 }
 
-typedef bool (*GetPropertyOrNameFn)(JSContext *, HandleObject, HandlePropertyName, Value *);
+typedef bool (*GetPropertyFn)(JSContext *, HandleValue, HandlePropertyName, MutableHandleValue);
+static const VMFunction GetPropertyInfo = FunctionInfo<GetPropertyFn>(GetProperty);
 
 bool
 CodeGenerator::visitCallGetProperty(LCallGetProperty *lir)
 {
-    typedef bool (*pf)(JSContext *, HandleValue, HandlePropertyName, MutableHandleValue);
-    static const VMFunction Info = FunctionInfo<pf>(GetProperty);
-
     pushArg(ImmGCPtr(lir->mir()->name()));
     pushArg(ToValue(lir, LCallGetProperty::Value));
-    return callVM(Info, lir);
+    return callVM(GetPropertyInfo, lir);
 }
 
+typedef bool (*GetOrCallElementFn)(JSContext *, HandleValue, HandleValue, MutableHandleValue);
+static const VMFunction GetElementInfo = FunctionInfo<GetOrCallElementFn>(js::GetElement);
+static const VMFunction CallElementInfo = FunctionInfo<GetOrCallElementFn>(js::CallElement);
+
 bool
 CodeGenerator::visitCallGetElement(LCallGetElement *lir)
 {
-    typedef bool (*pf)(JSContext *, HandleValue, HandleValue, MutableHandleValue);
-    static const VMFunction GetElementInfo = FunctionInfo<pf>(js::GetElement);
-    static const VMFunction CallElementInfo = FunctionInfo<pf>(js::CallElement);
-
     pushArg(ToValue(lir, LCallGetElement::RhsInput));
     pushArg(ToValue(lir, LCallGetElement::LhsInput));
 
     JSOp op = JSOp(*lir->mir()->resumePoint()->pc());
 
     if (op == JSOP_GETELEM) {
         return callVM(GetElementInfo, lir);
     } else {
         JS_ASSERT(op == JSOP_CALLELEM);
         return callVM(CallElementInfo, lir);
     }
 }
 
 bool
 CodeGenerator::visitCallSetElement(LCallSetElement *lir)
 {
-    typedef bool (*pf)(JSContext *, HandleObject, HandleValue, HandleValue, JSBool strict);
-    static const VMFunction SetObjectElementInfo = FunctionInfo<pf>(js::SetObjectElement);
-
     pushArg(Imm32(current->mir()->strictModeCode()));
     pushArg(ToValue(lir, LCallSetElement::Value));
     pushArg(ToValue(lir, LCallSetElement::Index));
     pushArg(ToRegister(lir->getOperand(0)));
     return callVM(SetObjectElementInfo, lir);
 }
 
 bool
@@ -3311,16 +3318,20 @@ CodeGenerator::visitCache(LInstruction *
     CodeOffsetJump jump = masm.jumpWithPatch(ool->repatchEntry());
     CodeOffsetLabel label = masm.labelForPatch();
     masm.bind(ool->rejoin());
 
     ool->setInlineJump(jump, label);
     return true;
 }
 
+typedef bool (*GetNameCacheFn)(JSContext *, size_t, HandleObject, MutableHandleValue);
+static const VMFunction GetNameCacheInfo =
+    FunctionInfo<GetNameCacheFn>(GetNameCache);
+
 bool
 CodeGenerator::visitOutOfLineGetNameCache(OutOfLineCache *ool)
 {
     LGetNameCache *lir = ool->cache()->toGetNameCache();
     const MGetNameCache *mir = lir->mir();
     Register scopeChain = ToRegister(lir->scopeObj());
     RegisterSet liveRegs = lir->safepoint()->liveRegs();
     TypedOrValueRegister output(GetValueOutput(lir));
@@ -3332,31 +3343,32 @@ CodeGenerator::visitOutOfLineGetNameCach
                        masm.labelForPatch(), liveRegs,
                        scopeChain, mir->name(), output);
 
     cache.setScriptedLocation(mir->block()->info().script(), mir->resumePoint()->pc());
     size_t cacheIndex = allocateCache(cache);
 
     saveLive(lir);
 
-    typedef bool (*pf)(JSContext *, size_t, HandleObject, MutableHandleValue);
-    static const VMFunction GetNameCacheInfo = FunctionInfo<pf>(GetNameCache);
-
     pushArg(scopeChain);
     pushArg(Imm32(cacheIndex));
     if (!callVM(GetNameCacheInfo, lir))
         return false;
 
     masm.storeCallResultValue(output);
     restoreLive(lir);
 
     masm.jump(ool->rejoin());
     return true;
 }
 
+typedef bool (*GetPropertyCacheFn)(JSContext *, size_t, HandleObject, MutableHandleValue);
+static const VMFunction GetPropertyCacheInfo =
+    FunctionInfo<GetPropertyCacheFn>(GetPropertyCache);
+
 bool
 CodeGenerator::visitOutOfLineCacheGetProperty(OutOfLineCache *ool)
 {
     RegisterSet liveRegs = ool->cache()->safepoint()->liveRegs();
 
     LInstruction *ins = ool->cache();
     MInstruction *mir = ins->mirRaw()->toInstruction();
 
@@ -3403,32 +3415,32 @@ CodeGenerator::visitOutOfLineCacheGetPro
     if (mir->resumePoint())
         cache.setScriptedLocation(mir->block()->info().script(), mir->resumePoint()->pc());
     else
         cache.setIdempotent();
     size_t cacheIndex = allocateCache(cache);
 
     saveLive(ins);
 
-    typedef bool (*pf)(JSContext *, size_t, HandleObject, MutableHandleValue);
-    static const VMFunction GetPropertyCacheInfo = FunctionInfo<pf>(GetPropertyCache);
-
     pushArg(objReg);
     pushArg(Imm32(cacheIndex));
     if (!callVM(GetPropertyCacheInfo, ins))
         return false;
 
     masm.storeCallResultValue(output);
     restoreLive(ins);
 
     masm.jump(ool->rejoin());
 
     return true;
 }
 
+typedef bool (*GetElementCacheFn)(JSContext *, size_t, HandleObject, HandleValue, MutableHandleValue);
+static const VMFunction GetElementCacheInfo = FunctionInfo<GetElementCacheFn>(GetElementCache);
+
 bool
 CodeGenerator::visitOutOfLineGetElementCache(OutOfLineCache *ool)
 {
     LGetElementCacheV *ins = ool->cache()->toGetElementCacheV();
     const MGetElementCache *mir = ins->mir();
 
     Register obj = ToRegister(ins->object());
     ConstantOrRegister index = TypedOrValueRegister(ToValue(ins, LGetElementCacheV::Index));
@@ -3441,32 +3453,33 @@ CodeGenerator::visitOutOfLineGetElementC
                              obj, index, output,
                              mir->monitoredResult());
 
     cache.setScriptedLocation(mir->block()->info().script(), mir->resumePoint()->pc());
     size_t cacheIndex = allocateCache(cache);
 
     saveLive(ins);
 
-    typedef bool (*pf)(JSContext *, size_t, HandleObject, HandleValue, MutableHandleValue);
-    static const VMFunction Info = FunctionInfo<pf>(GetElementCache);
-
     pushArg(index);
     pushArg(obj);
     pushArg(Imm32(cacheIndex));
-    if (!callVM(Info, ins))
+    if (!callVM(GetElementCacheInfo, ins))
         return false;
 
     masm.storeCallResultValue(output);
     restoreLive(ins);
 
     masm.jump(ool->rejoin());
     return true;
 }
 
+typedef JSObject *(*BindNameCacheFn)(JSContext *, size_t, HandleObject);
+static const VMFunction BindNameCacheInfo =
+    FunctionInfo<BindNameCacheFn>(BindNameCache);
+
 bool
 CodeGenerator::visitOutOfLineBindNameCache(OutOfLineCache *ool)
 {
     LBindNameCache *ins = ool->cache()->toBindNameCache();
     Register scopeChain = ToRegister(ins->scopeChain());
     Register output = ToRegister(ins->output());
 
     RegisterSet liveRegs = ins->safepoint()->liveRegs();
@@ -3475,19 +3488,16 @@ CodeGenerator::visitOutOfLineBindNameCac
     IonCacheBindName cache(ool->getInlineJump(), ool->getInlineLabel(),
                            masm.labelForPatch(), liveRegs,
                            scopeChain, mir->name(), output);
     cache.setScriptedLocation(mir->script(), mir->pc());
     size_t cacheIndex = allocateCache(cache);
 
     saveLive(ins);
 
-    typedef JSObject *(*pf)(JSContext *, size_t, HandleObject);
-    static const VMFunction BindNameCacheInfo = FunctionInfo<pf>(BindNameCache);
-
     pushArg(scopeChain);
     pushArg(Imm32(cacheIndex));
     if (!callVM(BindNameCacheInfo, ins))
         return false;
 
     masm.storeCallResult(output);
     restoreLive(ins);
 
@@ -3513,54 +3523,61 @@ CodeGenerator::getSetPropertyValue(LInst
         return TypedOrValueRegister(ins_->valueType(), ToAnyRegister(ins->getOperand(1)));
       }
       default:
         JS_NOT_REACHED("Bad opcode");
         return ConstantOrRegister(UndefinedValue());
     }
 }
 
+typedef bool (*SetPropertyFn)(JSContext *, HandleObject,
+                              HandlePropertyName, const HandleValue, bool, bool);
+static const VMFunction SetPropertyInfo =
+    FunctionInfo<SetPropertyFn>(SetProperty);
+
 bool
 CodeGenerator::visitCallSetProperty(LCallSetProperty *ins)
 {
     ConstantOrRegister value = getSetPropertyValue(ins);
 
     const Register objReg = ToRegister(ins->getOperand(0));
     bool isSetName = JSOp(*ins->mir()->resumePoint()->pc()) == JSOP_SETNAME;
 
     pushArg(Imm32(isSetName));
     pushArg(Imm32(ins->mir()->strict()));
 
     pushArg(value);
     pushArg(ImmGCPtr(ins->mir()->name()));
     pushArg(objReg);
 
-    typedef bool (*pf)(JSContext *, HandleObject, HandlePropertyName, const HandleValue, bool, bool);
-    static const VMFunction info = FunctionInfo<pf>(SetProperty);
-
-    return callVM(info, ins);
+    return callVM(SetPropertyInfo, ins);
 }
 
+typedef bool (*DeletePropertyFn)(JSContext *, HandleValue, HandlePropertyName, JSBool *);
+static const VMFunction DeletePropertyStrictInfo =
+    FunctionInfo<DeletePropertyFn>(DeleteProperty<true>);
+static const VMFunction DeletePropertyNonStrictInfo =
+    FunctionInfo<DeletePropertyFn>(DeleteProperty<false>);
+
 bool
 CodeGenerator::visitCallDeleteProperty(LCallDeleteProperty *lir)
 {
-    typedef bool (*pf)(JSContext *, HandleValue, HandlePropertyName, JSBool *);
-
     pushArg(ImmGCPtr(lir->mir()->name()));
     pushArg(ToValue(lir, LCallDeleteProperty::Value));
 
-    if (lir->mir()->block()->info().script()->strictModeCode) {
-        static const VMFunction Info = FunctionInfo<pf>(DeleteProperty<true>);
-        return callVM(Info, lir);
-    } else {
-        static const VMFunction Info = FunctionInfo<pf>(DeleteProperty<false>);
-        return callVM(Info, lir);
-    }
+    if (lir->mir()->block()->info().script()->strictModeCode)
+        return callVM(DeletePropertyStrictInfo, lir);
+    else
+        return callVM(DeletePropertyNonStrictInfo, lir);
 }
 
+typedef bool (*SetPropertyCacheFn)(JSContext *, size_t, HandleObject, HandleValue, bool);
+static const VMFunction SetPropertyCacheInfo =
+    FunctionInfo<SetPropertyCacheFn>(ion::SetPropertyCache);
+
 bool
 CodeGenerator::visitOutOfLineSetPropertyCache(OutOfLineCache *ool)
 {
     LInstruction *ins = ool->cache();
 
     Register objReg = ToRegister(ins->getOperand(0));
     RegisterSet liveRegs = ins->safepoint()->liveRegs();
 
@@ -3577,59 +3594,56 @@ CodeGenerator::visitOutOfLineSetProperty
 
     saveLive(ins);
 
     pushArg(Imm32(isSetName));
     pushArg(value);
     pushArg(objReg);
     pushArg(Imm32(cacheIndex));
 
-    typedef bool (*pf)(JSContext *, size_t, HandleObject, HandleValue, bool);
-    static const VMFunction info = FunctionInfo<pf>(ion::SetPropertyCache);
-
-    if (!callVM(info, ool->cache()))
+    if (!callVM(SetPropertyCacheInfo, ool->cache()))
         return false;
 
     restoreLive(ins);
 
     masm.jump(ool->rejoin());
 
     return true;
 }
 
+typedef bool (*ThrowFn)(JSContext *, HandleValue);
+static const VMFunction ThrowInfo = FunctionInfo<ThrowFn>(js::Throw);
+
 bool
 CodeGenerator::visitThrow(LThrow *lir)
 {
-    typedef bool (*pf)(JSContext *, HandleValue);
-    static const VMFunction ThrowInfo = FunctionInfo<pf>(js::Throw);
-
     pushArg(ToValue(lir, LThrow::Value));
     return callVM(ThrowInfo, lir);
 }
 
+typedef bool (*BitNotFn)(JSContext *, HandleValue, int *p);
+static const VMFunction BitNotInfo = FunctionInfo<BitNotFn>(BitNot);
+
 bool
 CodeGenerator::visitBitNotV(LBitNotV *lir)
 {
-    typedef bool (*pf)(JSContext *, HandleValue, int *p);
-    static const VMFunction info = FunctionInfo<pf>(BitNot);
-
     pushArg(ToValue(lir, LBitNotV::Input));
-    return callVM(info, lir);
+    return callVM(BitNotInfo, lir);
 }
 
+typedef bool (*BitopFn)(JSContext *, HandleValue, HandleValue, int *p);
+static const VMFunction BitAndInfo = FunctionInfo<BitopFn>(BitAnd);
+static const VMFunction BitOrInfo = FunctionInfo<BitopFn>(BitOr);
+static const VMFunction BitXorInfo = FunctionInfo<BitopFn>(BitXor);
+static const VMFunction BitLhsInfo = FunctionInfo<BitopFn>(BitLsh);
+static const VMFunction BitRhsInfo = FunctionInfo<BitopFn>(BitRsh);
+
 bool
 CodeGenerator::visitBitOpV(LBitOpV *lir)
 {
-    typedef bool (*pf)(JSContext *, HandleValue, HandleValue, int *p);
-    static const VMFunction BitAndInfo = FunctionInfo<pf>(BitAnd);
-    static const VMFunction BitOrInfo = FunctionInfo<pf>(BitOr);
-    static const VMFunction BitXorInfo = FunctionInfo<pf>(BitXor);
-    static const VMFunction BitLhsInfo = FunctionInfo<pf>(BitLsh);
-    static const VMFunction BitRhsInfo = FunctionInfo<pf>(BitRsh);
-
     pushArg(ToValue(lir, LBitOpV::RhsInput));
     pushArg(ToValue(lir, LBitOpV::LhsInput));
 
     switch (lir->jsop()) {
       case JSOP_BITAND:
         return callVM(BitAndInfo, lir);
       case JSOP_BITOR:
         return callVM(BitOrInfo, lir);
@@ -3708,48 +3722,48 @@ CodeGenerator::visitTypeOfV(LTypeOfV *li
 
     masm.movePtr(ImmGCPtr(rt->atomState.string), output);
 
     masm.bind(&done);
     masm.bind(ool->rejoin());
     return true;
 }
 
+typedef JSString *(*TypeOfFn)(JSContext *, HandleValue);
+static const VMFunction TypeOfInfo = FunctionInfo<TypeOfFn>(TypeOfOperation);
+
 bool
 CodeGenerator::visitOutOfLineTypeOfV(OutOfLineTypeOfV *ool)
 {
-    typedef JSString *(*pf)(JSContext *, HandleValue);
-    static const VMFunction Info = FunctionInfo<pf>(TypeOfOperation);
-
     LTypeOfV *ins = ool->ins();
     saveLive(ins);
 
     pushArg(ToValue(ins, LTypeOfV::Input));
-    if (!callVM(Info, ins))
+    if (!callVM(TypeOfInfo, ins))
         return false;
 
     masm.storeCallResult(ToRegister(ins->output()));
     restoreLive(ins);
 
     masm.jump(ool->rejoin());
     return true;
 }
 
+typedef bool (*ToIdFn)(JSContext *, HandleScript, jsbytecode *, HandleValue, HandleValue,
+                       MutableHandleValue);
+static const VMFunction ToIdInfo = FunctionInfo<ToIdFn>(ToIdOperation);
+
 bool
 CodeGenerator::visitToIdV(LToIdV *lir)
 {
-    typedef bool (*pf)(JSContext *, HandleScript, jsbytecode *, HandleValue, HandleValue,
-                       MutableHandleValue);
-    static const VMFunction Info = FunctionInfo<pf>(ToIdOperation);
-
     pushArg(ToValue(lir, LToIdV::Index));
     pushArg(ToValue(lir, LToIdV::Object));
     pushArg(ImmWord(lir->mir()->resumePoint()->pc()));
     pushArg(ImmGCPtr(current->mir()->info().script()));
-    return callVM(Info, lir);
+    return callVM(ToIdInfo, lir);
 }
 
 bool
 CodeGenerator::visitLoadElementV(LLoadElementV *load)
 {
     Register elements = ToRegister(load->elements());
     const ValueOperand out = ToOutValue(load);
 
@@ -3876,34 +3890,35 @@ CodeGenerator::visitLoadTypedArrayElemen
 
     if (fail.used() && !bailoutFrom(&fail, lir->snapshot()))
         return false;
 
     masm.bind(ool->rejoin());
     return true;
 }
 
+typedef bool (*GetElementMonitoredFn)(JSContext *, HandleValue, HandleValue, MutableHandleValue);
+static const VMFunction GetElementMonitoredInfo =
+    FunctionInfo<GetElementMonitoredFn>(js::GetElementMonitored);
+
 bool
 CodeGenerator::visitOutOfLineLoadTypedArray(OutOfLineLoadTypedArray *ool)
 {
     LLoadTypedArrayElementHole *ins = ool->ins();
     saveLive(ins);
 
     Register object = ToRegister(ins->object());
     ValueOperand out = ToOutValue(ins);
 
-    typedef bool (*pf)(JSContext *, HandleValue, HandleValue, MutableHandleValue);
-    static const VMFunction Info = FunctionInfo<pf>(js::GetElementMonitored);
-
     if (ins->index()->isConstant())
         pushArg(*ins->index()->toConstant());
     else
         pushArg(TypedOrValueRegister(MIRType_Int32, ToAnyRegister(ins->index())));
     pushArg(TypedOrValueRegister(MIRType_Object, AnyRegister(object)));
-    if (!callVM(Info, ins))
+    if (!callVM(GetElementMonitoredInfo, ins))
         return false;
 
     masm.storeCallResultValue(out);
     restoreLive(ins);
 
     masm.jump(ool->rejoin());
     return true;
 }
@@ -4001,22 +4016,22 @@ CodeGenerator::visitClampVToUint8(LClamp
 
     masm.bind(&isZero);
     masm.move32(Imm32(0), output);
 
     masm.bind(&done);
     return true;
 }
 
+typedef bool (*OperatorInFn)(JSContext *, HandleValue, HandleObject, JSBool *);
+static const VMFunction OperatorInInfo = FunctionInfo<OperatorInFn>(OperatorIn);
+
 bool
 CodeGenerator::visitIn(LIn *ins)
 {
-    typedef bool (*pf)(JSContext *, HandleValue, HandleObject, JSBool *);
-    static const VMFunction OperatorInInfo = FunctionInfo<pf>(OperatorIn);
-
     pushArg(ToRegister(ins->rhs()));
     pushArg(ToValue(ins, LIn::LHS));
 
     return callVM(OperatorInInfo, ins);
 }
 
 bool
 CodeGenerator::visitInstanceOfO(LInstanceOfO *ins)
@@ -4027,16 +4042,19 @@ CodeGenerator::visitInstanceOfO(LInstanc
 
 bool
 CodeGenerator::visitInstanceOfV(LInstanceOfV *ins)
 {
     Register rhs = ToRegister(ins->getOperand(LInstanceOfV::RHS));
     return emitInstanceOf(ins, rhs);
 }
 
+typedef bool (*HasInstanceFn)(JSContext *, HandleObject, HandleValue, JSBool *);
+static const VMFunction HasInstanceInfo = FunctionInfo<HasInstanceFn>(js::HasInstance);
+
 bool
 CodeGenerator::emitInstanceOf(LInstruction *ins, Register rhs)
 {
     Register rhsTmp = ToRegister(ins->getTemp(1));
     Register output = ToRegister(ins->getDef(0));
 
     // This temporary is used in other parts of the code.
     // Different names are used so the purpose is clear.
@@ -4046,19 +4064,16 @@ CodeGenerator::emitInstanceOf(LInstructi
     Label boundFunctionCheck;
     Label boundFunctionDone;
     Label done;
     Label loopPrototypeChain;
 
     JS_ASSERT(ins->isInstanceOfO() || ins->isInstanceOfV());
     bool lhsIsValue = ins->isInstanceOfV();
 
-    typedef bool (*pf)(JSContext *, HandleObject, HandleValue, JSBool *);
-    static const VMFunction HasInstanceInfo = FunctionInfo<pf>(js::HasInstance);
-
     // If the lhs is an object, then the ValueOperand that gets sent to
     // HasInstance must be boxed first.  If the lhs is a value, it can
     // be sent directly.  Hence the choice between ToValue and ToTempValue
     // below.  Note that the same check is done below in the generated code
     // and explicit boxing instructions emitted before calling the OOL code
     // if we're handling a LInstanceOfO.
 
     OutOfLineCode *call = oolCallVM(HasInstanceInfo, ins,
@@ -4315,16 +4330,20 @@ CodeGenerator::visitSetDOMProperty(LSetD
     }
     masm.bind(&success);
     masm.adjustStack(IonDOMExitFrameLayout::Size());
 
     JS_ASSERT(masm.framePushed() == initialStack);
     return true;
 }
 
+typedef bool(*SPSFn)(JSContext *, HandleScript);
+static const VMFunction SPSEnterInfo = FunctionInfo<SPSFn>(SPSEnter);
+static const VMFunction SPSExitInfo = FunctionInfo<SPSFn>(SPSExit);
+
 bool
 CodeGenerator::visitFunctionBoundary(LFunctionBoundary *lir)
 {
     AssertCanGC();
     Register temp = ToRegister(lir->temp()->output());
 
     switch (lir->type()) {
         case MFunctionBoundary::Inline_Enter:
@@ -4345,19 +4364,16 @@ CodeGenerator::visitFunctionBoundary(LFu
 
             sps_.leave(masm, temp);
             if (!sps_.enterInlineFrame())
                 return false;
             // fallthrough
 
         case MFunctionBoundary::Enter:
             if (sps_.slowAssertions()) {
-                typedef bool(*pf)(JSContext *, HandleScript);
-                static const VMFunction SPSEnterInfo = FunctionInfo<pf>(SPSEnter);
-
                 saveLive(lir);
                 pushArg(ImmGCPtr(lir->script()));
                 if (!callVM(SPSEnterInfo, lir))
                     return false;
                 restoreLive(lir);
                 sps_.pushManual(lir->script(), masm, temp);
                 return true;
             }
@@ -4369,19 +4385,16 @@ CodeGenerator::visitFunctionBoundary(LFu
             // maintain the state of inline frames currently active and then
             // reenter the caller
             sps_.leaveInlineFrame();
             sps_.reenter(masm, temp);
             return true;
 
         case MFunctionBoundary::Exit:
             if (sps_.slowAssertions()) {
-                typedef bool(*pf)(JSContext *, HandleScript);
-                static const VMFunction SPSExitInfo = FunctionInfo<pf>(SPSExit);
-
                 saveLive(lir);
                 pushArg(ImmGCPtr(lir->script()));
                 // Once we've exited, then we shouldn't emit instrumentation for
                 // the corresponding reenter() because we no longer have a
                 // frame.
                 sps_.skipNextReenter();
                 if (!callVM(SPSExitInfo, lir))
                     return false;
--- a/js/src/ion/Ion.cpp
+++ b/js/src/ion/Ion.cpp
@@ -118,41 +118,97 @@ ion::InitializeIon()
             return false;
         IonTLSInitialized = true;
     }
 #endif
     CheckLogging();
     return true;
 }
 
-IonCompartment::IonCompartment()
+IonRuntime::IonRuntime()
   : execAlloc_(NULL),
     enterJIT_(NULL),
     bailoutHandler_(NULL),
     argumentsRectifier_(NULL),
     invalidator_(NULL),
-    functionWrappers_(NULL),
-    flusher_(NULL)
+    functionWrappers_(NULL)
 {
 }
 
+IonRuntime::~IonRuntime()
+{
+    js_delete(functionWrappers_);
+}
+
 bool
-IonCompartment::initialize(JSContext *cx)
+IonRuntime::initialize(JSContext *cx)
 {
+    AutoEnterAtomsCompartment ac(cx);
+
+    if (!cx->compartment->ensureIonCompartmentExists(cx))
+        return false;
+
+    IonContext ictx(cx, cx->compartment, NULL);
+    AutoFlushCache afc("IonRuntime::initialize");
+
     execAlloc_ = cx->runtime->getExecAlloc(cx);
     if (!execAlloc_)
         return false;
 
     functionWrappers_ = cx->new_<VMWrapperMap>(cx);
     if (!functionWrappers_ || !functionWrappers_->init())
         return false;
 
+    if (!bailoutTables_.reserve(FrameSizeClass::ClassLimit().classId()))
+        return false;
+
+    for (uint32 id = 0;; id++) {
+        FrameSizeClass class_ = FrameSizeClass::FromClass(id);
+        if (class_ == FrameSizeClass::ClassLimit())
+            break;
+        bailoutTables_.infallibleAppend(NULL);
+        bailoutTables_[id] = generateBailoutTable(cx, id);
+        if (!bailoutTables_[id])
+            return false;
+    }
+
+    bailoutHandler_ = generateBailoutHandler(cx);
+    if (!bailoutHandler_)
+        return false;
+
+    argumentsRectifier_ = generateArgumentsRectifier(cx);
+    if (!argumentsRectifier_)
+        return false;
+
+    invalidator_ = generateInvalidator(cx);
+    if (!invalidator_)
+        return false;
+
+    enterJIT_ = generateEnterJIT(cx);
+    if (!enterJIT_)
+        return false;
+
+    preBarrier_ = generatePreBarrier(cx);
+    if (!preBarrier_)
+        return false;
+
+    for (VMFunction *fun = VMFunction::functions; fun; fun = fun->next) {
+        if (!generateVMWrapper(cx, *fun))
+            return false;
+    }
+
     return true;
 }
 
+IonCompartment::IonCompartment(IonRuntime *rt)
+  : rt(rt),
+    flusher_(NULL)
+{
+}
+
 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;
     }
@@ -166,101 +222,56 @@ FinishAllOffThreadCompilations(IonCompar
 
     for (size_t i = 0; i < compilations.length(); i++) {
         IonBuilder *builder = compilations[i];
         FinishOffThreadBuilder(builder);
     }
     compilations.clear();
 }
 
+/* static */ void
+IonRuntime::Mark(JSTracer *trc)
+{
+    for (gc::CellIterUnderGC i(trc->runtime->atomsCompartment, gc::FINALIZE_IONCODE); !i.done(); i.next()) {
+        IonCode *code = i.get<IonCode>();
+        MarkIonCodeRoot(trc, &code, "wrapper");
+    }
+}
+
 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.
-
-    bool runningIonCode = false;
-    for (IonActivationIterator iter(trc->runtime); iter.more(); ++iter) {
-        IonActivation *activation = iter.activation();
-
-        if (activation->compartment() != compartment)
-            continue;
-
-        runningIonCode = true;
-        break;
-    }
-
-    // Don't destroy enterJIT if we are running Ion code. Note that enterJIT is
-    // not used for JM -> Ion calls, so it may be NULL in that case.
-    if (runningIonCode && enterJIT_)
-        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()))
-        bailoutHandler_ = NULL;
-    if (argumentsRectifier_ && !IsIonCodeMarked(argumentsRectifier_.unsafeGet()))
-        argumentsRectifier_ = NULL;
-    if (invalidator_ && !IsIonCodeMarked(invalidator_.unsafeGet()))
-        invalidator_ = NULL;
-    if (preBarrier_ && !IsIonCodeMarked(preBarrier_.unsafeGet()))
-        preBarrier_ = NULL;
-
-    for (size_t i = 0; i < bailoutTables_.length(); i++) {
-        if (bailoutTables_[i] && !IsIonCodeMarked(bailoutTables_[i].unsafeGet()))
-            bailoutTables_[i] = NULL;
-    }
-
-    // Sweep cache of VM function implementations.
-    functionWrappers_->sweep(fop);
 }
 
 IonCode *
 IonCompartment::getBailoutTable(const FrameSizeClass &frameClass)
 {
     JS_ASSERT(frameClass != FrameSizeClass::None());
-    return bailoutTables_[frameClass.classId()];
+    return rt->bailoutTables_[frameClass.classId()];
 }
 
 IonCode *
-IonCompartment::getBailoutTable(JSContext *cx, const FrameSizeClass &frameClass)
+IonCompartment::getVMWrapper(const VMFunction &f)
 {
-    uint32 id = frameClass.classId();
+    typedef MoveResolver::MoveOperand MoveOperand;
 
-    if (id >= bailoutTables_.length()) {
-        size_t numToPush = id - bailoutTables_.length() + 1;
-        if (!bailoutTables_.reserve(bailoutTables_.length() + numToPush))
-            return NULL;
-        for (size_t i = 0; i < numToPush; i++)
-            bailoutTables_.infallibleAppend(NULL);
-    }
+    JS_ASSERT(rt->functionWrappers_);
+    JS_ASSERT(rt->functionWrappers_->initialized());
+    IonRuntime::VMWrapperMap::Ptr p = rt->functionWrappers_->lookup(&f);
+    JS_ASSERT(p);
 
-    if (!bailoutTables_[id])
-        bailoutTables_[id] = generateBailoutTable(cx, id);
-
-    return bailoutTables_[id];
-}
-
-IonCompartment::~IonCompartment()
-{
-    js_delete(functionWrappers_);
+    return p->value;
 }
 
 IonActivation::IonActivation(JSContext *cx, StackFrame *fp)
   : cx_(cx),
     compartment_(cx->compartment),
     prev_(cx->runtime->ionActivation),
     entryfp_(fp),
     bailout_(NULL),
@@ -322,25 +333,18 @@ IonCode::copyFrom(MacroAssembler &masm)
     masm.processCodeLabels(this);
 }
 
 void
 IonCode::trace(JSTracer *trc)
 {
     // Note that we cannot mark invalidated scripts, since we've basically
     // corrupted the code stream by injecting bailouts.
-    if (invalidated()) {
-        // Note that since we're invalidated, we won't mark the precious
-        // invalidator thunk referenced in the epilogue. We don't move
-        // executable code so the actual reference is okay, we just need to
-        // make sure it says alive before we return.
-        IonCompartment *ion = compartment()->ionCompartment();
-        MarkIonCodeUnbarriered(trc, ion->getInvalidationThunkAddr(), "invalidator");
+    if (invalidated())
         return;
-    }
 
     if (jumpRelocTableBytes_) {
         uint8 *start = code_ + jumpRelocTableOffset();
         CompactBufferReader reader(start, start + jumpRelocTableBytes_);
         MacroAssembler::TraceJumpRelocations(trc, this, reader);
     }
     if (dataRelocTableBytes_) {
         uint8 *start = code_ + dataRelocTableOffset();
@@ -994,16 +998,18 @@ AttachFinishedCompilations(JSContext *cx
         IonBuilder *builder = compilations.popCopy();
 
         if (builder->backgroundCompiledLir) {
             RootedScript script(cx, builder->script());
             IonContext ictx(cx, cx->compartment, &builder->temp());
 
             CodeGenerator codegen(builder, *builder->backgroundCompiledLir);
 
+            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());
@@ -1306,23 +1312,16 @@ ion::CanEnterAtBranch(JSContext *cx, Han
         if (status == Method_CantCompile)
             ForbidCompilation(cx, script);
         return status;
     }
 
     if (script->ion->osrPc() != pc)
         return Method_Skipped;
 
-    // This can GC, so afterward, script->ion is not guaranteed to be valid.
-    if (!cx->compartment->ionCompartment()->enterJIT(cx))
-        return Method_Error;
-
-    if (!script->ion)
-        return Method_Skipped;
-
     return Method_Compiled;
 }
 
 MethodStatus
 ion::CanEnter(JSContext *cx, HandleScript script, StackFrame *fp, bool newType)
 {
     JS_ASSERT(ion::IsEnabled(cx));
 
@@ -1359,23 +1358,16 @@ ion::CanEnter(JSContext *cx, HandleScrip
     JSFunction *fun = fp->isFunctionFrame() ? fp->fun() : NULL;
     MethodStatus status = Compile(cx, script, fun, NULL, fp->isConstructing());
     if (status != Method_Compiled) {
         if (status == Method_CantCompile)
             ForbidCompilation(cx, script);
         return status;
     }
 
-    // This can GC, so afterward, script->ion is not guaranteed to be valid.
-    if (!cx->compartment->ionCompartment()->enterJIT(cx))
-        return Method_Error;
-
-    if (!script->ion)
-        return Method_Skipped;
-
     return Method_Compiled;
 }
 
 MethodStatus
 ion::CanEnterUsingFastInvoke(JSContext *cx, HandleScript script, uint32_t numActualArgs)
 {
     JS_ASSERT(ion::IsEnabled(cx));
 
@@ -1388,17 +1380,17 @@ ion::CanEnterUsingFastInvoke(JSContext *
     if (numActualArgs < script->function()->nargs)
         return Method_Skipped;
 
     if (!cx->compartment->ensureIonCompartmentExists(cx))
         return Method_Error;
 
     // This can GC, so afterward, script->ion is not guaranteed to be valid.
     AssertCanGC();
-    if (!cx->compartment->ionCompartment()->enterJIT(cx))
+    if (!cx->compartment->ionCompartment()->enterJIT())
         return Method_Error;
 
     if (!script->ion)
         return Method_Skipped;
 
     return Method_Compiled;
 }
 
@@ -1406,17 +1398,17 @@ static IonExecStatus
 EnterIon(JSContext *cx, StackFrame *fp, void *jitcode)
 {
     AssertCanGC();
     JS_CHECK_RECURSION(cx, return IonExec_Error);
     JS_ASSERT(ion::IsEnabled(cx));
     JS_ASSERT(CheckFrame(fp));
     JS_ASSERT(!fp->script()->ion->bailoutExpected());
 
-    EnterIonCode enter = cx->compartment->ionCompartment()->enterJITInfallible();
+    EnterIonCode enter = cx->compartment->ionCompartment()->enterJIT();
 
     // maxArgc is the maximum of arguments between the number of actual
     // arguments and the number of formal arguments. It accounts for |this|.
     int maxArgc = 0;
     Value *maxArgv = NULL;
     int numActualArgs = 0;
 
     void *calleeToken;
@@ -1580,17 +1572,17 @@ ion::FastInvoke(JSContext *cx, HandleFun
         clearCallingIntoIon = true;
         activation.setEntryFp(fp);
     } else {
         JS_ASSERT(!activation.entryfp());
     }
 
     activation.setPrevPc(cx->regs().pc);
 
-    EnterIonCode enter = cx->compartment->ionCompartment()->enterJITInfallible();
+    EnterIonCode enter = cx->compartment->ionCompartment()->enterJIT();
     void *calleeToken = CalleeToToken(fun);
 
     Value result = Int32Value(args.length());
     JS_ASSERT(args.length() >= fun->nargs);
 
     JSAutoResolveFlags rf(cx, RESOLVE_INFER);
     args.setActive();
     enter(jitcode, args.length() + 1, args.array() - 1, fp, calleeToken, &result);
@@ -1873,16 +1865,18 @@ ion::MarkFromIon(JSCompartment *comp, Va
 }
 
 void
 ion::ForbidCompilation(JSContext *cx, JSScript *script)
 {
     IonSpew(IonSpew_Abort, "Disabling Ion compilation of script %s:%d",
             script->filename, script->lineno);
 
+    CancelOffThreadIonCompile(cx->compartment, script);
+
     if (script->hasIonScript()) {
         // It is only safe to modify script->ion if the script is not currently
         // running, because IonFrameIterator needs to tell what ionScript to
         // use (either the one on the JSScript, or the one hidden in the
         // breadcrumbs Invalidation() leaves). Therefore, if invalidation
         // fails, we cannot disable the script.
         if (!Invalidate(cx, script, false))
             return;
--- a/js/src/ion/IonCompartment.h
+++ b/js/src/ion/IonCompartment.h
@@ -22,144 +22,118 @@ 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
+class IonRuntime
 {
-    typedef WeakCache<const VMFunction *, ReadBarriered<IonCode> > VMWrapperMap;
+    friend class IonCompartment;
 
-    friend class IonActivation;
-
-    // Executable allocator (owned by the runtime).
+    // Executable allocator.
     JSC::ExecutableAllocator *execAlloc_;
 
     // Trampoline for entering JIT code. Contains OSR prologue.
-    ReadBarriered<IonCode> enterJIT_;
+    IonCode *enterJIT_;
 
     // Vector mapping frame class sizes to bailout tables.
-    Vector<ReadBarriered<IonCode>, 4, SystemAllocPolicy> bailoutTables_;
+    Vector<IonCode*, 4, SystemAllocPolicy> bailoutTables_;
 
     // Generic bailout table; used if the bailout table overflows.
-    ReadBarriered<IonCode> bailoutHandler_;
+    IonCode *bailoutHandler_;
 
     // Argument-rectifying thunk, in the case of insufficient arguments passed
     // to a function call site. Pads with |undefined|.
-    ReadBarriered<IonCode> argumentsRectifier_;
+    IonCode *argumentsRectifier_;
 
     // Thunk that invalides an (Ion compiled) caller on the Ion stack.
-    ReadBarriered<IonCode> invalidator_;
+    IonCode *invalidator_;
 
     // Thunk that calls the GC pre barrier.
-    ReadBarriered<IonCode> preBarrier_;
+    IonCode *preBarrier_;
 
     // Map VMFunction addresses to the IonCode of the wrapper.
+    typedef WeakCache<const VMFunction *, IonCode *> VMWrapperMap;
     VMWrapperMap *functionWrappers_;
 
+  private:
+    IonCode *generateEnterJIT(JSContext *cx);
+    IonCode *generateArgumentsRectifier(JSContext *cx);
+    IonCode *generateBailoutTable(JSContext *cx, uint32 frameClass);
+    IonCode *generateBailoutHandler(JSContext *cx);
+    IonCode *generateInvalidator(JSContext *cx);
+    IonCode *generatePreBarrier(JSContext *cx);
+    IonCode *generateVMWrapper(JSContext *cx, const VMFunction &f);
+
+  public:
+    IonRuntime();
+    ~IonRuntime();
+    bool initialize(JSContext *cx);
+
+    static void Mark(JSTracer *trc);
+};
+
+class IonCompartment
+{
+    friend class IonActivation;
+
+    // Ion state for the compartment's runtime.
+    IonRuntime *rt;
+
     // 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_;
 
-  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);
+    IonCode *getVMWrapper(const VMFunction &f);
 
     OffThreadCompilationVector &finishedOffThreadCompilations() {
         return finishedOffThreadCompilations_;
     }
 
   public:
-    bool initialize(JSContext *cx);
-    IonCompartment();
-    ~IonCompartment();
+    IonCompartment(IonRuntime *rt);
 
     void mark(JSTracer *trc, JSCompartment *compartment);
     void sweep(FreeOp *fop);
 
     JSC::ExecutableAllocator *execAlloc() {
-        return execAlloc_;
-    }
-
-    IonCode *getBailoutTable(JSContext *cx, const FrameSizeClass &frameClass);
-    IonCode *getGenericBailoutHandler(JSContext *cx) {
-        if (!bailoutHandler_) {
-            bailoutHandler_ = generateBailoutHandler(cx);
-            if (!bailoutHandler_)
-                return NULL;
-        }
-        return bailoutHandler_;
+        return rt->execAlloc_;
     }
 
-    // Infallible; does not generate a table.
+    IonCode *getGenericBailoutHandler() {
+        return rt->bailoutHandler_;
+    }
+
     IonCode *getBailoutTable(const FrameSizeClass &frameClass);
 
-    // Fallible; generates a thunk and returns the target.
-    IonCode *getArgumentsRectifier(JSContext *cx) {
-        if (!argumentsRectifier_) {
-            argumentsRectifier_ = generateArgumentsRectifier(cx);
-            if (!argumentsRectifier_)
-                return NULL;
-        }
-        return argumentsRectifier_;
-    }
-    IonCode **getArgumentsRectifierAddr() {
-        return argumentsRectifier_.unsafeGet();
+    IonCode *getArgumentsRectifier() {
+        return rt->argumentsRectifier_;
     }
 
-    IonCode *getOrCreateInvalidationThunk(JSContext *cx) {
-        if (!invalidator_) {
-            invalidator_ = generateInvalidator(cx);
-            if (!invalidator_)
-                return NULL;
-        }
-        return invalidator_;
-    }
-    IonCode **getInvalidationThunkAddr() {
-        return invalidator_.unsafeGet();
-    }
-
-    EnterIonCode enterJITInfallible() {
-        JS_ASSERT(enterJIT_);
-        return enterJIT_.get()->as<EnterIonCode>();
+    IonCode *getInvalidationThunk() {
+        return rt->invalidator_;
     }
 
-    EnterIonCode enterJIT(JSContext *cx) {
-        if (!enterJIT_) {
-            enterJIT_ = generateEnterJIT(cx);
-            if (!enterJIT_)
-                return NULL;
-        }
-        return enterJIT_.get()->as<EnterIonCode>();
+    EnterIonCode enterJIT() {
+        return rt->enterJIT_->as<EnterIonCode>();
     }
 
-    IonCode *preBarrier(JSContext *cx) {
-        if (!preBarrier_) {
-            preBarrier_ = generatePreBarrier(cx);
-            if (!preBarrier_)
-                return NULL;
-        }
-        return preBarrier_;
+    IonCode *preBarrier() {
+        return rt->preBarrier_;
     }
+
     AutoFlushCache *flusher() {
         return flusher_;
     }
     void setFlusher(AutoFlushCache *fl) {
         if (!flusher_ || !fl)
             flusher_ = fl;
     }
 
--- a/js/src/ion/IonFrames.cpp
+++ b/js/src/ion/IonFrames.cpp
@@ -633,21 +633,18 @@ MarkIonActivation(JSTracer *trc, const I
             break;
           case IonFrame_OptimizedJS:
             MarkIonJSFrame(trc, frames);
             break;
           case IonFrame_Bailed_JS:
             JS_NOT_REACHED("invalid");
             break;
           case IonFrame_Rectifier:
-          case IonFrame_Bailed_Rectifier: {
-            IonCompartment *ionCompartment = activations.activation()->compartment()->ionCompartment();
-            MarkIonCodeRoot(trc, ionCompartment->getArgumentsRectifierAddr(), "Arguments Rectifier");
+          case IonFrame_Bailed_Rectifier:
             break;
-          }
           case IonFrame_Osr:
             // The callee token will be marked by the callee JS frame;
             // otherwise, it does not need to be marked, since the frame is
             // dead.
             break;
           default:
             JS_NOT_REACHED("unexpected frame type");
             break;
--- a/js/src/ion/IonFrames.h
+++ b/js/src/ion/IonFrames.h
@@ -215,18 +215,19 @@ class FrameSizeClass
 
     static FrameSizeClass None() {
         return FrameSizeClass(NO_FRAME_SIZE_CLASS_ID);
     }
     static FrameSizeClass FromClass(uint32 class_) {
         return FrameSizeClass(class_);
     }
 
-    // These two functions are implemented in specific CodeGenerator-* files.
+    // These functions are implemented in specific CodeGenerator-* files.
     static FrameSizeClass FromDepth(uint32 frameDepth);
+    static FrameSizeClass ClassLimit();
     uint32 frameSize() const;
 
     uint32 classId() const {
         JS_ASSERT(class_ != NO_FRAME_SIZE_CLASS_ID);
         return class_;
     }
 
     bool operator ==(const FrameSizeClass &other) const {
--- a/js/src/ion/IonMacroAssembler.h
+++ b/js/src/ion/IonMacroAssembler.h
@@ -376,28 +376,25 @@ class MacroAssembler : public MacroAssem
         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);
         Label done;
 
-        JSContext *cx = GetIonContext()->cx;
-        IonCode *preBarrier = cx->compartment->ionCompartment()->preBarrier(cx);
-        if (!preBarrier) {
-            enoughMemory_ = false;
-            return;
-        }
-
         if (type == MIRType_Value)
             branchTestGCThing(Assembler::NotEqual, address, &done);
 
         Push(PreBarrierReg);
         computeEffectiveAddress(address, PreBarrierReg);
+
+        JSCompartment *compartment = GetIonContext()->compartment;
+        IonCode *preBarrier = compartment->ionCompartment()->preBarrier();
+
         call(preBarrier);
         Pop(PreBarrierReg);
 
         bind(&done);
     }
 
     template <typename T>
     CodeOffsetLabel patchableCallPreBarrier(const T &address, MIRType type) {
--- a/js/src/ion/VMFunctions.cpp
+++ b/js/src/ion/VMFunctions.cpp
@@ -16,16 +16,32 @@
 #include "jsinterpinlines.h"
 
 using namespace js;
 using namespace js::ion;
 
 namespace js {
 namespace ion {
 
+// Don't explicitly initialize, it's not guaranteed that this initializer will
+// run before the constructors for static VMFunctions.
+/* static */ VMFunction *VMFunction::functions;
+
+void
+VMFunction::addToFunctions()
+{
+    static bool initialized = false;
+    if (!initialized) {
+        initialized = true;
+        functions = NULL;
+    }
+    this->next = functions;
+    functions = this;
+}
+
 static inline bool
 ShouldMonitorReturnType(JSFunction *fun)
 {
     return fun->isInterpreted() &&
            (!fun->script()->hasAnalysis() ||
             !fun->script()->analysis()->ranInference());
 }
 
--- a/js/src/ion/VMFunctions.h
+++ b/js/src/ion/VMFunctions.h
@@ -29,16 +29,20 @@ enum DataType {
 // a value that does not meet this requirement - such as object-or-NULL, or an
 // integer, an optional, final outParam can be specified. In this case, the
 // return type must be boolean to indicate failure.
 //
 // All functions described by VMFunction take a JSContext * as a first
 // argument, and are treated as re-entrant into the VM and therefore fallible.
 struct VMFunction
 {
+    // Global linked list of all VMFunctions.
+    static VMFunction *functions;
+    VMFunction *next;
+
     // Address of the C function.
     void *wrapped;
 
     // Number of arguments expected, excluding JSContext * as an implicit
     // first argument and an outparam as a possible implicit final argument.
     uint32 explicitArgs;
 
     enum ArgProperties {
@@ -163,16 +167,26 @@ struct VMFunction
         outParam(outParam),
         returnType(returnType),
         argumentRootTypes(argRootTypes)
     {
         // Check for valid failure/return type.
         JS_ASSERT_IF(outParam != Type_Void, returnType == Type_Bool);
         JS_ASSERT(returnType == Type_Bool || returnType == Type_Object);
     }
+
+    VMFunction(const VMFunction &o)
+    {
+        *this = o;
+        addToFunctions();
+    }
+
+  private:
+    // Add this to the global list of VMFunctions.
+    void addToFunctions();
 };
 
 template <class> struct TypeToDataType { /* Unexpected return type for a VMFunction. */ };
 template <> struct TypeToDataType<bool> { static const DataType result = Type_Bool; };
 template <> struct TypeToDataType<JSObject *> { static const DataType result = Type_Object; };
 template <> struct TypeToDataType<JSString *> { static const DataType result = Type_Object; };
 template <> struct TypeToDataType<JSFlatString *> { static const DataType result = Type_Object; };
 template <> struct TypeToDataType<HandleObject> { static const DataType result = Type_Handle; };
--- a/js/src/ion/arm/CodeGenerator-arm.cpp
+++ b/js/src/ion/arm/CodeGenerator-arm.cpp
@@ -171,21 +171,19 @@ CodeGeneratorARM::generateOutOfLineCode(
 
     if (deoptLabel_) {
         // All non-table-based bailouts will go here.
         masm.bind(deoptLabel_);
 
         // Push the frame size, so the handler can recover the IonScript.
         masm.ma_mov(Imm32(frameSize()), lr);
 
-        JSContext *cx = GetIonContext()->cx;
-        IonCompartment *ion = cx->compartment->ionCompartment();
-        IonCode *handler = ion->getGenericBailoutHandler(cx);
-        if (!handler)
-            return false;
+        IonCompartment *ion = GetIonContext()->compartment->ionCompartment();
+        IonCode *handler = ion->getGenericBailoutHandler();
+
         masm.branch(handler);
     }
 
     return true;
 }
 
 bool
 CodeGeneratorARM::bailoutIf(Assembler::Condition condition, LSnapshot *snapshot)
@@ -977,45 +975,42 @@ CodeGeneratorARM::emitRoundDouble(const 
 }
 
 bool
 CodeGeneratorARM::visitTruncateDToInt32(LTruncateDToInt32 *ins)
 {
     return emitTruncateDouble(ToFloatRegister(ins->input()), ToRegister(ins->output()));
 }
 
-// The first two size classes are 128 and 256 bytes respectively. After that we
-// increment by 512.
-static const uint32 LAST_FRAME_SIZE = 512;
-static const uint32 LAST_FRAME_INCREMENT = 512;
-static const uint32 FrameSizes[] = { 128, 256, LAST_FRAME_SIZE };
+static const uint32 FrameSizes[] = { 128, 256, 512, 1024 };
 
 FrameSizeClass
 FrameSizeClass::FromDepth(uint32 frameDepth)
 {
     for (uint32 i = 0; i < JS_ARRAY_LENGTH(FrameSizes); i++) {
         if (frameDepth < FrameSizes[i])
             return FrameSizeClass(i);
     }
 
-    uint32 newFrameSize = frameDepth - LAST_FRAME_SIZE;
-    uint32 sizeClass = (newFrameSize / LAST_FRAME_INCREMENT) + 1;
+    return FrameSizeClass::None();
+}
 
-    return FrameSizeClass(JS_ARRAY_LENGTH(FrameSizes) + sizeClass);
+FrameSizeClass
+FrameSizeClass::ClassLimit()
+{
+    return FrameSizeClass(JS_ARRAY_LENGTH(FrameSizes));
 }
+
 uint32
 FrameSizeClass::frameSize() const
 {
     JS_ASSERT(class_ != NO_FRAME_SIZE_CLASS_ID);
+    JS_ASSERT(class_ < JS_ARRAY_LENGTH(FrameSizes));
 
-    if (class_ < JS_ARRAY_LENGTH(FrameSizes))
-        return FrameSizes[class_];
-
-    uint32 step = class_ - JS_ARRAY_LENGTH(FrameSizes);
-    return LAST_FRAME_SIZE + step * LAST_FRAME_INCREMENT;
+    return FrameSizes[class_];
 }
 
 ValueOperand
 CodeGeneratorARM::ToValue(LInstruction *ins, size_t pos)
 {
     Register typeReg = ToRegister(ins->getOperand(pos + TYPE_INDEX));
     Register payloadReg = ToRegister(ins->getOperand(pos + PAYLOAD_INDEX));
     return ValueOperand(typeReg, payloadReg);
@@ -1466,23 +1461,23 @@ CodeGeneratorARM::visitRecompileCheck(LR
 
     // Bailout if the script is hot.
     masm.ma_cmp(tmp, Imm32(lir->mir()->minUses()));
     if (!bailoutIf(Assembler::AboveOrEqual, lir->snapshot()))
         return false;
     return true;
 }
 
+typedef bool (*InterruptCheckFn)(JSContext *);
+static const VMFunction InterruptCheckInfo = FunctionInfo<InterruptCheckFn>(InterruptCheck);
+
 bool
 CodeGeneratorARM::visitInterruptCheck(LInterruptCheck *lir)
 {
-    typedef bool (*pf)(JSContext *);
-    static const VMFunction interruptCheckInfo = FunctionInfo<pf>(InterruptCheck);
-
-    OutOfLineCode *ool = oolCallVM(interruptCheckInfo, lir, (ArgList()), StoreNothing());
+    OutOfLineCode *ool = oolCallVM(InterruptCheckInfo, lir, (ArgList()), StoreNothing());
     if (!ool)
         return false;
 
     void *interrupt = (void*)&gen->compartment->rt->interrupt;
     masm.load32(AbsoluteAddress(interrupt), lr);
     masm.ma_cmp(lr, Imm32(0));
     masm.ma_b(ool->entry(), Assembler::NonZero);
     masm.bind(ool->rejoin());
@@ -1498,23 +1493,19 @@ CodeGeneratorARM::generateInvalidateEpil
     for (size_t i = 0; i < sizeof(void *); i+= Assembler::nopSize())
         masm.nop();
 
     masm.bind(&invalidate_);
 
     // Push the return address of the point that we bailed out at onto the stack
     masm.Push(lr);
 
-    JSContext *cx = GetIonContext()->cx;
-
     // Push the Ion script onto the stack (when we determine what that pointer is).
     invalidateEpilogueData_ = masm.pushWithPatch(ImmWord(uintptr_t(-1)));
-    IonCode *thunk = cx->compartment->ionCompartment()->getOrCreateInvalidationThunk(cx);
-    if (!thunk)
-        return false;
+    IonCode *thunk = GetIonContext()->compartment->ionCompartment()->getInvalidationThunk();
 
     masm.branch(thunk);
 
     // We should never reach this point in JIT code -- the invalidation thunk should
     // pop the invalidated JS frame and return directly to its caller.
     masm.breakpoint();
     return true;
 }
--- a/js/src/ion/arm/Trampoline-arm.cpp
+++ b/js/src/ion/arm/Trampoline-arm.cpp
@@ -65,17 +65,17 @@ struct EnterJITStack
 /*
  * This method generates a trampoline on x86 for a c++ function with
  * the following signature:
  *   void enter(void *code, int argc, Value *argv, StackFrame *fp, CalleeToken
  *              calleeToken, Value *vp)
  *   ...using standard EABI calling convention
  */
 IonCode *
-IonCompartment::generateEnterJIT(JSContext *cx)
+IonRuntime::generateEnterJIT(JSContext *cx)
 {
 
     const Register reg_code  = r0;
     const Register reg_argc  = r1;
     const Register reg_argv  = r2;
     const Register reg_frame = r3;
 
     const Address slot_token(sp, offsetof(EnterJITStack, token));
@@ -194,32 +194,19 @@ IonCompartment::generateEnterJIT(JSConte
     // Restore non-volatile registers and return.
     GenerateReturn(masm, JS_TRUE);
 
     Linker linker(masm);
     return linker.newCode(cx);
 }
 
 IonCode *
-IonCompartment::generateReturnError(JSContext *cx)
+IonRuntime::generateInvalidator(JSContext *cx)
 {
-    MacroAssembler masm(cx);
-    // This is where the stack size is stored on x86. where is it stored here?
-    masm.ma_pop(r0);
-    masm.ma_add(r0, sp, sp);
-
-    GenerateReturn(masm, JS_FALSE);
-    Linker linker(masm);
-    return linker.newCode(cx);
-}
-
-IonCode *
-IonCompartment::generateInvalidator(JSContext *cx)
-{
-    // See large comment in x86's IonCompartment::generateInvalidator.
+    // See large comment in x86's IonRuntime::generateInvalidator.
     AutoIonContextAlloc aica(cx);
     MacroAssembler masm(cx);
     //masm.as_bkpt();
     // At this point, one of two things has happened.
     // 1) Execution has just returned from C code, which left the stack aligned
     // 2) Execution has just returned from Ion code, which left the stack unaligned.
     // The old return address should not matter, but we still want the
     // stack to be aligned, and there is no good reason to automatically align it with
@@ -256,17 +243,17 @@ IonCompartment::generateInvalidator(JSCo
     masm.generateBailoutTail(r1);
     Linker linker(masm);
     IonCode *code = linker.newCode(cx);
     IonSpew(IonSpew_Invalidate, "   invalidation thunk created at %p", (void *) code->raw());
     return code;
 }
 
 IonCode *
-IonCompartment::generateArgumentsRectifier(JSContext *cx)
+IonRuntime::generateArgumentsRectifier(JSContext *cx)
 {
     MacroAssembler masm(cx);
     // ArgumentsRectifierReg contains the |nargs| pushed onto the current frame.
     // Including |this|, there are (|nargs| + 1) arguments to copy.
     JS_ASSERT(ArgumentsRectifierReg == r8);
 
     // Copy number of actual arguments into r0
     masm.ma_ldr(DTRAddr(sp, DtrOffImm(IonRectifierFrameLayout::offsetOfNumActualArgs())), r0);
@@ -443,43 +430,43 @@ GenerateBailoutThunk(MacroAssembler &mas
                           + sizeof(void*) // the size of the "return address" that was dumped on the stack
                           + bailoutFrameSize) // everything else that was pushed on the stack
                     , sp);
     }
     masm.generateBailoutTail(r1);
 }
 
 IonCode *
-IonCompartment::generateBailoutTable(JSContext *cx, uint32 frameClass)
+IonRuntime::generateBailoutTable(JSContext *cx, uint32 frameClass)
 {
     MacroAssembler masm;
 
     Label bailout;
     for (size_t i = 0; i < BAILOUT_TABLE_SIZE; i++)
         masm.ma_bl(&bailout);
     masm.bind(&bailout);
 
     GenerateBailoutThunk(masm, frameClass);
 
     Linker linker(masm);
     return linker.newCode(cx);
 }
 
 IonCode *
-IonCompartment::generateBailoutHandler(JSContext *cx)
+IonRuntime::generateBailoutHandler(JSContext *cx)
 {
     MacroAssembler masm;
     GenerateBailoutThunk(masm, NO_FRAME_SIZE_CLASS_ID);
 
     Linker linker(masm);
     return linker.newCode(cx);
 }
 
 IonCode *
-IonCompartment::generateVMWrapper(JSContext *cx, const VMFunction &f)
+IonRuntime::generateVMWrapper(JSContext *cx, const VMFunction &f)
 {
     typedef MoveResolver::MoveOperand MoveOperand;
 
     JS_ASSERT(functionWrappers_);
     JS_ASSERT(functionWrappers_->initialized());
     VMWrapperMap::AddPtr p = functionWrappers_->lookupForAdd(&f);
     if (p)
         return p->value;
@@ -617,17 +604,17 @@ IonCompartment::generateVMWrapper(JSCont
     // use relookupOrAdd instead of add.
     if (!functionWrappers_->relookupOrAdd(p, &f, wrapper))
         return NULL;
 
     return wrapper;
 }
 
 IonCode *
-IonCompartment::generatePreBarrier(JSContext *cx)
+IonRuntime::generatePreBarrier(JSContext *cx)
 {
     MacroAssembler masm;
 
     RegisterSet save = RegisterSet(GeneralRegisterSet(Registers::VolatileMask),
                                    FloatRegisterSet(FloatRegisters::VolatileMask));
     masm.PushRegsInMask(save);
 
     JS_ASSERT(PreBarrierReg == r1);
--- a/js/src/ion/shared/CodeGenerator-shared.cpp
+++ b/js/src/ion/shared/CodeGenerator-shared.cpp
@@ -214,17 +214,18 @@ CodeGeneratorShared::encode(LSnapshot *s
         uint32 exprStack = mir->stackDepth() - block->info().ninvoke();
         snapshots_.startFrame(fun, script, pc, exprStack);
 
         // Ensure that all snapshot which are encoded can safely be used for
         // bailouts.
         DebugOnly<jsbytecode *> bailPC = pc;
         if (mir->mode() == MResumePoint::ResumeAfter)
           bailPC = GetNextPc(pc);
-        JS_ASSERT(exprStack == js_ReconstructStackDepth(GetIonContext()->cx, script, bailPC));
+        JS_ASSERT_IF(GetIonContext()->cx,
+                     exprStack == js_ReconstructStackDepth(GetIonContext()->cx, script, bailPC));
 
 #ifdef TRACK_SNAPSHOTS
         LInstruction *ins = instruction();
 
         uint32 pcOpcode = 0;
         uint32 lirOpcode = 0;
         uint32 lirId = 0;
         uint32 mirOpcode = 0;
@@ -369,20 +370,19 @@ CodeGeneratorShared::callVM(const VMFunc
     // Stack is:
     //    ... frame ...
     //    [args]
 #ifdef DEBUG
     JS_ASSERT(pushedArgs_ == fun.explicitArgs);
     pushedArgs_ = 0;
 #endif
 
-    // Generate the wrapper of the VM function.
-    JSContext *cx = GetIonContext()->cx;
-    IonCompartment *ion = cx->compartment->ionCompartment();
-    IonCode *wrapper = ion->generateVMWrapper(cx, fun);
+    // Get the wrapper of the VM function.
+    IonCompartment *ion = GetIonContext()->compartment->ionCompartment();
+    IonCode *wrapper = ion->getVMWrapper(fun);
     if (!wrapper)
         return false;
 
     // Call the wrapper function.  The wrapper is in charge to unwind the stack
     // when returning from the call.  Failures are handled with exceptions based
     // on the return value of the C functions.  To guard the outcome of the
     // returned value, use another LIR instruction.
     uint32 callOffset;
--- a/js/src/ion/shared/CodeGenerator-x86-shared.cpp
+++ b/js/src/ion/shared/CodeGenerator-x86-shared.cpp
@@ -288,21 +288,18 @@ CodeGeneratorX86Shared::generateOutOfLin
 
     if (deoptLabel_) {
         // All non-table-based bailouts will go here.
         masm.bind(deoptLabel_);
         
         // Push the frame size, so the handler can recover the IonScript.
         masm.push(Imm32(frameSize()));
 
-        JSContext *cx = GetIonContext()->cx;
-        IonCompartment *ion = cx->compartment->ionCompartment();
-        IonCode *handler = ion->getGenericBailoutHandler(cx);
-        if (!handler)
-            return false;
+        IonCompartment *ion = GetIonContext()->compartment->ionCompartment();
+        IonCode *handler = ion->getGenericBailoutHandler();
 
         masm.jmp(handler->raw(), Relocation::IONCODE);
     }
 
     return true;
 }
 
 class BailoutJump {
@@ -1324,23 +1321,19 @@ CodeGeneratorX86Shared::generateInvalida
     // Ensure that there is enough space in the buffer for the OsiPoint
     // patching to occur. Otherwise, we could overwrite the invalidation
     // epilogue.
     for (size_t i = 0; i < sizeof(void *); i+= Assembler::nopSize())
         masm.nop();
 
     masm.bind(&invalidate_);
 
-    JSContext *cx = GetIonContext()->cx;
-
     // Push the Ion script onto the stack (when we determine what that pointer is).
     invalidateEpilogueData_ = masm.pushWithPatch(ImmWord(uintptr_t(-1)));
-    IonCode *thunk = cx->compartment->ionCompartment()->getOrCreateInvalidationThunk(cx);
-    if (!thunk)
-        return false;
+    IonCode *thunk = GetIonContext()->compartment->ionCompartment()->getInvalidationThunk();
 
     masm.call(thunk);
 
     // We should never reach this point in JIT code -- the invalidation thunk should
     // pop the invalidated JS frame and return directly to its caller.
     masm.breakpoint();
     return true;
 }
--- a/js/src/ion/x64/CodeGenerator-x64.cpp
+++ b/js/src/ion/x64/CodeGenerator-x64.cpp
@@ -48,16 +48,22 @@ CodeGeneratorX64::visitDouble(LDouble *i
 }
 
 FrameSizeClass
 FrameSizeClass::FromDepth(uint32 frameDepth)
 {
     return FrameSizeClass::None();
 }
 
+FrameSizeClass
+FrameSizeClass::ClassLimit()
+{
+    return FrameSizeClass(0);
+}
+
 uint32
 FrameSizeClass::frameSize() const
 {
     JS_NOT_REACHED("x64 does not use frame size classes");
     return 0;
 }
 
 bool
@@ -288,23 +294,23 @@ CodeGeneratorX64::visitRecompileCheck(LR
     Operand addr(ScratchReg, 0);
     masm.addl(Imm32(1), addr);
     masm.cmpl(addr, Imm32(lir->mir()->minUses()));
     if (!bailoutIf(Assembler::AboveOrEqual, lir->snapshot()))
         return false;
     return true;
 }
 
+typedef bool (*InterruptCheckFn)(JSContext *);
+static const VMFunction InterruptCheckInfo = FunctionInfo<InterruptCheckFn>(InterruptCheck);
+
 bool
 CodeGeneratorX64::visitInterruptCheck(LInterruptCheck *lir)
 {
-    typedef bool (*pf)(JSContext *);
-    static const VMFunction interruptCheckInfo = FunctionInfo<pf>(InterruptCheck);
-
-    OutOfLineCode *ool = oolCallVM(interruptCheckInfo, lir, (ArgList()), StoreNothing());
+    OutOfLineCode *ool = oolCallVM(InterruptCheckInfo, lir, (ArgList()), StoreNothing());
     if (!ool)
         return false;
 
     void *interrupt = (void*)&gen->compartment->rt->interrupt;
     masm.movq(ImmWord(interrupt), ScratchReg);
     masm.cmpl(Operand(ScratchReg, 0), Imm32(0));
     masm.j(Assembler::NonZero, ool->entry());
     masm.bind(ool->rejoin());
--- a/js/src/ion/x64/MacroAssembler-x64.h
+++ b/js/src/ion/x64/MacroAssembler-x64.h
@@ -863,17 +863,17 @@ class MacroAssemblerX64 : public MacroAs
     void makeFrameDescriptor(Register frameSizeReg, FrameType type) {
         shlq(Imm32(FRAMESIZE_SHIFT), frameSizeReg);
         orq(Imm32(type), frameSizeReg);
     }
 
     // Save an exit frame (which must be aligned to the stack pointer) to
     // ThreadData::ionTop.
     void linkExitFrame() {
-        mov(ImmWord(GetIonContext()->cx->runtime), ScratchReg);
+        mov(ImmWord(GetIonContext()->compartment->rt), ScratchReg);
         mov(StackPointer, Operand(ScratchReg, offsetof(JSRuntime, ionTop)));
     }
 
     void callWithExitFrame(IonCode *target, Register dynStack) {
         addPtr(Imm32(framePushed()), dynStack);
         makeFrameDescriptor(dynStack, IonFrame_OptimizedJS);
         Push(dynStack);
         call(target);
--- a/js/src/ion/x64/Trampoline-x64.cpp
+++ b/js/src/ion/x64/Trampoline-x64.cpp
@@ -20,17 +20,17 @@ using namespace js;
 using namespace js::ion;
 
 /* This method generates a trampoline on x64 for a c++ function with
  * the following signature:
  *   JSBool blah(void *code, int argc, Value *argv, Value *vp)
  *   ...using standard x64 fastcall calling convention
  */
 IonCode *
-IonCompartment::generateEnterJIT(JSContext *cx)
+IonRuntime::generateEnterJIT(JSContext *cx)
 {
     MacroAssembler masm(cx);
 
     const Register reg_code  = IntArgReg0;
     const Register reg_argc  = IntArgReg1;
     const Register reg_argv  = IntArgReg2;
     JS_ASSERT(OsrFrameReg == IntArgReg3);
 
@@ -175,36 +175,22 @@ IonCompartment::generateEnterJIT(JSConte
     masm.pop(rbp);
     masm.ret();
 
     Linker linker(masm);
     return linker.newCode(cx);
 }
 
 IonCode *
-IonCompartment::generateReturnError(JSContext *cx)
-{
-    MacroAssembler masm(cx);
-
-    masm.pop(r14);              // sizeDescriptor.
-    masm.xorl(Imm32(0x1), r14); // Unmark EntryFrame.
-    masm.addq(r14, rsp);        // Remove arguments.
-    masm.pop(r11);              // Discard |vp|: returning from error.
-
-    Linker linker(masm);
-    return linker.newCode(cx);
-}
-
-IonCode *
-IonCompartment::generateInvalidator(JSContext *cx)
+IonRuntime::generateInvalidator(JSContext *cx)
 {
     AutoIonContextAlloc aica(cx);
     MacroAssembler masm(cx);
 
-    // See explanatory comment in x86's IonCompartment::generateInvalidator.
+    // See explanatory comment in x86's IonRuntime::generateInvalidator.
 
     masm.addq(Imm32(sizeof(uintptr_t)), rsp);
 
     // Push registers such that we can access them from [base + code].
     masm.reserveStack(Registers::Total * sizeof(void *));
     for (uint32 i = 0; i < Registers::Total; i++)
         masm.movq(Register::FromCode(i), Operand(rsp, i * sizeof(void *)));
 
@@ -231,17 +217,17 @@ IonCompartment::generateInvalidator(JSCo
 
     masm.generateBailoutTail(rdx);
 
     Linker linker(masm);
     return linker.newCode(cx);
 }
 
 IonCode *
-IonCompartment::generateArgumentsRectifier(JSContext *cx)
+IonRuntime::generateArgumentsRectifier(JSContext *cx)
 {
     // Do not erase the frame pointer in this function.
 
     MacroAssembler masm(cx);
 
     // ArgumentsRectifierReg contains the |nargs| pushed onto the current frame.
     // Including |this|, there are (|nargs| + 1) arguments to copy.
     JS_ASSERT(ArgumentsRectifierReg == r8);
@@ -354,35 +340,35 @@ GenerateBailoutThunk(JSContext *cx, Macr
     masm.addq(Imm32(BailoutDataSize), rsp);
     masm.pop(rcx);
     masm.lea(Operand(rsp, rcx, TimesOne, sizeof(void *)), rsp);
 
     masm.generateBailoutTail(rdx);
 }
 
 IonCode *
-IonCompartment::generateBailoutTable(JSContext *cx, uint32 frameClass)
+IonRuntime::generateBailoutTable(JSContext *cx, uint32 frameClass)
 {
     JS_NOT_REACHED("x64 does not use bailout tables");
     return NULL;
 }
 
 IonCode *
-IonCompartment::generateBailoutHandler(JSContext *cx)
+IonRuntime::generateBailoutHandler(JSContext *cx)
 {
     MacroAssembler masm;
 
     GenerateBailoutThunk(cx, masm, NO_FRAME_SIZE_CLASS_ID);
 
     Linker linker(masm);
     return linker.newCode(cx);
 }
 
 IonCode *
-IonCompartment::generateVMWrapper(JSContext *cx, const VMFunction &f)
+IonRuntime::generateVMWrapper(JSContext *cx, const VMFunction &f)
 {
     typedef MoveResolver::MoveOperand MoveOperand;
 
     JS_ASSERT(!StackKeptAligned);
     JS_ASSERT(functionWrappers_);
     JS_ASSERT(functionWrappers_->initialized());
     VMWrapperMap::AddPtr p = functionWrappers_->lookupForAdd(&f);
     if (p)
@@ -526,17 +512,17 @@ IonCompartment::generateVMWrapper(JSCont
     // use relookupOrAdd instead of add.
     if (!functionWrappers_->relookupOrAdd(p, &f, wrapper))
         return NULL;
 
     return wrapper;
 }
 
 IonCode *
-IonCompartment::generatePreBarrier(JSContext *cx)
+IonRuntime::generatePreBarrier(JSContext *cx)
 {
     MacroAssembler masm;
 
     RegisterSet regs = RegisterSet(GeneralRegisterSet(Registers::VolatileMask),
                                    FloatRegisterSet(FloatRegisters::VolatileMask));
     masm.PushRegsInMask(regs);
 
     JS_ASSERT(PreBarrierReg == rdx);
--- a/js/src/ion/x86/CodeGenerator-x86.cpp
+++ b/js/src/ion/x86/CodeGenerator-x86.cpp
@@ -18,45 +18,42 @@ using namespace js::ion;
 
 using mozilla::DebugOnly;
 
 CodeGeneratorX86::CodeGeneratorX86(MIRGenerator *gen, LIRGraph &graph)
   : CodeGeneratorX86Shared(gen, graph)
 {
 }
 
-// The first two size classes are 128 and 256 bytes respectively. After that we
-// increment by 512.
-static const uint32 LAST_FRAME_SIZE = 512;
-static const uint32 LAST_FRAME_INCREMENT = 512;
-static const uint32 FrameSizes[] = { 128, 256, LAST_FRAME_SIZE };
+static const uint32 FrameSizes[] = { 128, 256, 512, 1024 };
 
 FrameSizeClass
 FrameSizeClass::FromDepth(uint32 frameDepth)
 {
     for (uint32 i = 0; i < JS_ARRAY_LENGTH(FrameSizes); i++) {
         if (frameDepth < FrameSizes[i])
             return FrameSizeClass(i);
     }
 
-    uint32 newFrameSize = frameDepth - LAST_FRAME_SIZE;
-    uint32 sizeClass = (newFrameSize / LAST_FRAME_INCREMENT) + 1;
+    return FrameSizeClass::None();
+}
 
-    return FrameSizeClass(JS_ARRAY_LENGTH(FrameSizes) + sizeClass);
+FrameSizeClass
+FrameSizeClass::ClassLimit()
+{
+    return FrameSizeClass(JS_ARRAY_LENGTH(FrameSizes));
 }
+
 uint32
 FrameSizeClass::frameSize() const
 {
     JS_ASSERT(class_ != NO_FRAME_SIZE_CLASS_ID);
+    JS_ASSERT(class_ < JS_ARRAY_LENGTH(FrameSizes));
 
-    if (class_ < JS_ARRAY_LENGTH(FrameSizes))
-        return FrameSizes[class_];
-
-    uint32 step = class_ - JS_ARRAY_LENGTH(FrameSizes);
-    return LAST_FRAME_SIZE + step * LAST_FRAME_INCREMENT;
+    return FrameSizes[class_];
 }
 
 ValueOperand
 CodeGeneratorX86::ToValue(LInstruction *ins, size_t pos)
 {
     Register typeReg = ToRegister(ins->getOperand(pos + TYPE_INDEX));
     Register payloadReg = ToRegister(ins->getOperand(pos + PAYLOAD_INDEX));
     return ValueOperand(typeReg, payloadReg);
@@ -293,23 +290,23 @@ CodeGeneratorX86::visitRecompileCheck(LR
     Operand addr(gen->info().script()->addressOfUseCount());
     masm.addl(Imm32(1), addr);
     masm.cmpl(addr, Imm32(lir->mir()->minUses()));
     if (!bailoutIf(Assembler::AboveOrEqual, lir->snapshot()))
         return false;
     return true;
 }
 
+typedef bool (*InterruptCheckFn)(JSContext *);
+static const VMFunction InterruptCheckInfo = FunctionInfo<InterruptCheckFn>(InterruptCheck);
+
 bool
 CodeGeneratorX86::visitInterruptCheck(LInterruptCheck *lir)
 {
-    typedef bool (*pf)(JSContext *);
-    static const VMFunction interruptCheckInfo = FunctionInfo<pf>(InterruptCheck);
-
-    OutOfLineCode *ool = oolCallVM(interruptCheckInfo, lir, (ArgList()), StoreNothing());
+    OutOfLineCode *ool = oolCallVM(InterruptCheckInfo, lir, (ArgList()), StoreNothing());
     if (!ool)
         return false;
 
     void *interrupt = (void*)&gen->compartment->rt->interrupt;
     masm.cmpl(Operand(interrupt), Imm32(0));
     masm.j(Assembler::NonZero, ool->entry());
     masm.bind(ool->rejoin());
     return true;
--- a/js/src/ion/x86/Trampoline-x86.cpp
+++ b/js/src/ion/x86/Trampoline-x86.cpp
@@ -28,17 +28,17 @@ enum EnterJitEbpArgumentOffset {
     ARG_RESULT      = 7 * sizeof(void *)
 };
 
 /*
  * Generates a trampoline for a C++ function with the EnterIonCode signature,
  * using the standard cdecl calling convention.
  */
 IonCode *
-IonCompartment::generateEnterJIT(JSContext *cx)
+IonRuntime::generateEnterJIT(JSContext *cx)
 {
     MacroAssembler masm(cx);
 
     // Save old stack frame pointer, set new stack frame pointer.
     masm.push(ebp);
     masm.movl(esp, ebp);
 
     // Save non-volatile registers. These must be saved by the trampoline,
@@ -156,17 +156,17 @@ IonCompartment::generateEnterJIT(JSConte
     masm.pop(ebp);
     masm.ret();
 
     Linker linker(masm);
     return linker.newCode(cx);
 }
 
 IonCode *
-IonCompartment::generateInvalidator(JSContext *cx)
+IonRuntime::generateInvalidator(JSContext *cx)
 {
     AutoIonContextAlloc aica(cx);
     MacroAssembler masm(cx);
 
     // We do the minimum amount of work in assembly and shunt the rest
     // off to InvalidationBailout. Assembly does:
     //
     // - Pop the return address from the invalidation epilogue call.
@@ -206,17 +206,17 @@ IonCompartment::generateInvalidator(JSCo
 
     Linker linker(masm);
     IonCode *code = linker.newCode(cx);
     IonSpew(IonSpew_Invalidate, "   invalidation thunk created at %p", (void *) code->raw());
     return code;
 }
 
 IonCode *
-IonCompartment::generateArgumentsRectifier(JSContext *cx)
+IonRuntime::generateArgumentsRectifier(JSContext *cx)
 {
     MacroAssembler masm(cx);
 
     // ArgumentsRectifierReg contains the |nargs| pushed onto the current frame.
     // Including |this|, there are (|nargs| + 1) arguments to copy.
     JS_ASSERT(ArgumentsRectifierReg == esi);
 
     // Load the number of |undefined|s to push into %ecx.
@@ -352,44 +352,44 @@ GenerateBailoutThunk(JSContext *cx, Macr
         uint32 frameSize = FrameSizeClass::FromClass(frameClass).frameSize();
         masm.addl(Imm32(BailoutDataSize + sizeof(void *) + frameSize), esp);
     }
 
     masm.generateBailoutTail(edx);
 }
 
 IonCode *
-IonCompartment::generateBailoutTable(JSContext *cx, uint32 frameClass)
+IonRuntime::generateBailoutTable(JSContext *cx, uint32 frameClass)
 {
     MacroAssembler masm;
 
     Label bailout;
     for (size_t i = 0; i < BAILOUT_TABLE_SIZE; i++)
         masm.call(&bailout);
     masm.bind(&bailout);
 
     GenerateBailoutThunk(cx, masm, frameClass);
 
     Linker linker(masm);
     return linker.newCode(cx);
 }
 
 IonCode *
-IonCompartment::generateBailoutHandler(JSContext *cx)
+IonRuntime::generateBailoutHandler(JSContext *cx)
 {
     MacroAssembler masm;
 
     GenerateBailoutThunk(cx, masm, NO_FRAME_SIZE_CLASS_ID);
 
     Linker linker(masm);
     return linker.newCode(cx);
 }
 
 IonCode *
-IonCompartment::generateVMWrapper(JSContext *cx, const VMFunction &f)
+IonRuntime::generateVMWrapper(JSContext *cx, const VMFunction &f)
 {
     AssertCanGC();
     typedef MoveResolver::MoveOperand MoveOperand;
 
     JS_ASSERT(!StackKeptAligned);
     JS_ASSERT(functionWrappers_);
     JS_ASSERT(functionWrappers_->initialized());
     VMWrapperMap::AddPtr p = functionWrappers_->lookupForAdd(&f);
@@ -539,17 +539,17 @@ IonCompartment::generateVMWrapper(JSCont
     // use relookupOrAdd instead of add.
     if (!functionWrappers_->relookupOrAdd(p, &f, wrapper))
         return NULL;
 
     return wrapper;
 }
 
 IonCode *
-IonCompartment::generatePreBarrier(JSContext *cx)
+IonRuntime::generatePreBarrier(JSContext *cx)
 {
     MacroAssembler masm;
 
     RegisterSet save = RegisterSet(GeneralRegisterSet(Registers::VolatileMask),
                                    FloatRegisterSet(FloatRegisters::VolatileMask));
     masm.PushRegsInMask(save);
 
     JS_ASSERT(PreBarrierReg == edx);
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -738,16 +738,17 @@ JSRuntime::JSRuntime(JSUseHelperThreads 
 #endif
     tempLifoAlloc(TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
     freeLifoAlloc(TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
     execAlloc_(NULL),
     bumpAlloc_(NULL),
 #ifdef JS_METHODJIT
     jaegerRuntime_(NULL),
 #endif
+    ionRuntime_(NULL),
     selfHostedGlobal_(NULL),
     nativeStackBase(0),
     nativeStackQuota(0),
     interpreterFrames(NULL),
     cxCallback(NULL),
     destroyCompartmentCallback(NULL),
     compartmentNameCallback(NULL),
     activityCallback(NULL),
@@ -1010,16 +1011,19 @@ JSRuntime::~JSRuntime()
         PR_DestroyLock(gcLock);
 #endif
 
     js_delete(bumpAlloc_);
     js_delete(mathCache_);
 #ifdef JS_METHODJIT
     js_delete(jaegerRuntime_);
 #endif
+#ifdef JS_ION
+    js_delete(ionRuntime_);
+#endif
     js_delete(execAlloc_);  /* Delete after jaegerRuntime_. */
 
     if (ionPcScriptCache)
         js_delete(ionPcScriptCache);
 }
 
 #ifdef JS_THREADSAFE
 void
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -86,16 +86,17 @@ TraceCycleDetectionSet(JSTracer *trc, Ob
 
 namespace mjit {
 class JaegerRuntime;
 }
 
 class MathCache;
 
 namespace ion {
+class IonRuntime;
 class IonActivation;
 }
 
 class WeakMapBase;
 class InterpreterFrames;
 class DebugScopes;
 class WorkerThreadState;
 
@@ -494,22 +495,24 @@ struct JSRuntime : js::RuntimeFriendFiel
      * Both of these allocators are used for regular expression code which is shared at the
      * thread-data level.
      */
     JSC::ExecutableAllocator *execAlloc_;
     WTF::BumpPointerAllocator *bumpAlloc_;
 #ifdef JS_METHODJIT
     js::mjit::JaegerRuntime *jaegerRuntime_;
 #endif
+    js::ion::IonRuntime *ionRuntime_;
 
     JSObject *selfHostedGlobal_;
 
     JSC::ExecutableAllocator *createExecutableAllocator(JSContext *cx);
     WTF::BumpPointerAllocator *createBumpPointerAllocator(JSContext *cx);
     js::mjit::JaegerRuntime *createJaegerRuntime(JSContext *cx);
+    js::ion::IonRuntime *createIonRuntime(JSContext *cx);
 
   public:
     JSC::ExecutableAllocator *getExecAlloc(JSContext *cx) {
         return execAlloc_ ? execAlloc_ : createExecutableAllocator(cx);
     }
     JSC::ExecutableAllocator &execAlloc() {
         JS_ASSERT(execAlloc_);
         return *execAlloc_;
@@ -527,16 +530,19 @@ struct JSRuntime : js::RuntimeFriendFiel
     bool hasJaegerRuntime() const {
         return jaegerRuntime_;
     }
     js::mjit::JaegerRuntime &jaegerRuntime() {
         JS_ASSERT(hasJaegerRuntime());
         return *jaegerRuntime_;
     }
 #endif
+    js::ion::IonRuntime *getIonRuntime(JSContext *cx) {
+        return ionRuntime_ ? ionRuntime_ : createIonRuntime(cx);
+    }
 
     bool initSelfHosting(JSContext *cx);
     void markSelfHostedGlobal(JSTracer *trc);
     bool isSelfHostedGlobal(js::HandleObject global) {
         return global == selfHostedGlobal_;
     }
     JSFunction *getSelfHostedFunction(JSContext *cx, js::Handle<js::PropertyName*> name);
     bool cloneSelfHostedValueById(JSContext *cx, js::HandleId id, js::HandleObject holder,
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -152,32 +152,55 @@ JSCompartment::setNeedsBarrier(bool need
         ionUsingBarriers_ = needs;
     }
 #endif
 
     needsBarrier_ = needs;
 }
 
 #ifdef JS_ION
+ion::IonRuntime *
+JSRuntime::createIonRuntime(JSContext *cx)
+{
+    ionRuntime_ = cx->new_<ion::IonRuntime>();
+
+    if (!ionRuntime_)
+        return NULL;
+
+    if (!ionRuntime_->initialize(cx)) {
+        js_delete(ionRuntime_);
+        ionRuntime_ = NULL;
+
+        if (cx->runtime->atomsCompartment->ionCompartment_) {
+            js_delete(cx->runtime->atomsCompartment->ionCompartment_);
+            cx->runtime->atomsCompartment->ionCompartment_ = NULL;
+        }
+
+        return NULL;
+    }
+
+    return ionRuntime_;
+}
+
 bool
 JSCompartment::ensureIonCompartmentExists(JSContext *cx)
 {
     using namespace js::ion;
     if (ionCompartment_)
         return true;
 
-    /* Set the compartment early, so linking works. */
-    ionCompartment_ = cx->new_<IonCompartment>();
+    IonRuntime *ionRuntime = cx->runtime->getIonRuntime(cx);
+    if (!ionRuntime)
+        return false;
 
-    if (!ionCompartment_ || !ionCompartment_->initialize(cx)) {
-        if (ionCompartment_)
-            delete ionCompartment_;
-        ionCompartment_ = NULL;
+    /* Set the compartment early, so linking works. */
+    ionCompartment_ = cx->new_<IonCompartment>(ionRuntime);
+
+    if (!ionCompartment_)
         return false;
-    }
 
     return true;
 }
 #endif
 
 static bool
 WrapForSameCompartment(JSContext *cx, HandleObject obj, Value *vp)
 {
--- a/js/src/jscompartment.h
+++ b/js/src/jscompartment.h
@@ -117,16 +117,17 @@ class AutoDebugModeGC;
 }
 
 struct JSCompartment
 {
     JSRuntime                    *rt;
     JSPrincipals                 *principals;
 
   private:
+    friend struct JSRuntime;
     friend struct JSContext;
     js::GlobalObject             *global_;
   public:
     // Nb: global_ might be NULL, if (a) it's the atoms compartment, or (b) the
     // compartment's global has been collected.  The latter can happen if e.g.
     // a string in a compartment is rooted but no object is, and thus the
     // global isn't rooted, and thus the global can be finalized while the
     // compartment lives on.
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -2509,18 +2509,25 @@ MarkRuntime(JSTracer *trc, bool useSaved
     }
 
     if (rt->scriptAndCountsVector) {
         ScriptAndCountsVector &vec = *rt->scriptAndCountsVector;
         for (size_t i = 0; i < vec.length(); i++)
             MarkScriptRoot(trc, &vec[i].script, "scriptAndCountsVector");
     }
 
-    if (!IS_GC_MARKING_TRACER(trc) || rt->atomsCompartment->isCollecting())
+    if (!IS_GC_MARKING_TRACER(trc) || rt->atomsCompartment->isCollecting()) {
         MarkAtoms(trc);
+#ifdef JS_ION
+        /* Any Ion wrappers survive until the runtime is being torn down. */
+        if (rt->hasContexts())
+            ion::IonRuntime::Mark(trc);
+#endif
+    }
+
     rt->staticStrings.trace(trc);
 
     for (ContextIter acx(rt); !acx.done(); acx.next())
         acx->mark(trc);
 
     /* We can't use GCCompartmentsIter if we're called from TraceRuntime. */
     for (CompartmentsIter c(rt); !c.done(); c.next()) {
         if (IS_GC_MARKING_TRACER(trc) && !c->isCollecting())
--- a/js/src/jsgcinlines.h
+++ b/js/src/jsgcinlines.h
@@ -460,17 +460,19 @@ class GCCompartmentsIter {
 
 template <typename T>
 inline T *
 NewGCThing(JSContext *cx, js::gc::AllocKind kind, size_t thingSize)
 {
     AssertCanGC();
     JS_ASSERT(thingSize == js::gc::Arena::thingSize(kind));
     JS_ASSERT_IF(cx->compartment == cx->runtime->atomsCompartment,
-                 kind == js::gc::FINALIZE_STRING || kind == js::gc::FINALIZE_SHORT_STRING);
+                 kind == FINALIZE_STRING ||
+                 kind == FINALIZE_SHORT_STRING ||
+                 kind == FINALIZE_IONCODE);
     JS_ASSERT(!cx->runtime->isHeapBusy());
     JS_ASSERT(!cx->runtime->noGCOrAllocationCheck);
 
     /* For testing out of memory conditions */
     JS_OOM_POSSIBLY_FAIL_REPORT(cx);
 
 #ifdef JS_GC_ZEAL
     if (cx->runtime->needZealousGC())
--- a/js/src/jsworkers.cpp
+++ b/js/src/jsworkers.cpp
@@ -15,18 +15,17 @@ using namespace js;
 
 using mozilla::DebugOnly;
 
 #ifdef JS_PARALLEL_COMPILATION
 
 bool
 js::OffThreadCompilationAvailable(JSContext *cx)
 {
-    WorkerThreadState &state = *cx->runtime->workerThreadState;
-    return state.numThreads > 0;
+    return cx->runtime->useHelperThreads();
 }
 
 bool
 js::StartOffThreadIonCompile(JSContext *cx, ion::IonBuilder *builder)
 {
     JSRuntime *rt = cx->runtime;
     if (!rt->workerThreadState) {
         rt->workerThreadState = rt->new_<WorkerThreadState>();
@@ -72,16 +71,18 @@ CompiledScriptMatches(JSCompartment *com
     if (script)
         return target == script;
     return target->compartment() == compartment;
 }
 
 void
 js::CancelOffThreadIonCompile(JSCompartment *compartment, JSScript *script)
 {
+    AutoAssertNoGC nogc;
+
     if (!compartment->rt->workerThreadState)
         return;
 
     WorkerThreadState &state = *compartment->rt->workerThreadState;
 
     ion::IonCompartment *ion = compartment->ionCompartment();
     if (!ion)
         return;