Bug 969012 - Allow inlining out-of-line slot creation in IonMonkey; r=jandem
authorTerrence Cole <terrence@mozilla.com>
Thu, 06 Mar 2014 14:58:22 -0800
changeset 184708 893b864b4b187390cf5eb2488b979ec706652832
parent 184707 b377204840b9be52ce09de181e0c343a3cad3bcf
child 184709 500bcf9f9d3a5986a096f98c4bf74560a5b2231d
push idunknown
push userunknown
push dateunknown
reviewersjandem
bugs969012
milestone30.0a1
Bug 969012 - Allow inlining out-of-line slot creation in IonMonkey; r=jandem
js/src/jit-test/tests/parallel/closure-nested-branch.js
js/src/jit-test/tests/parallel/closure-nested-compute.js
js/src/jit/CodeGenerator.cpp
js/src/jit/CodeGenerator.h
js/src/jit/Ion.cpp
js/src/jit/IonBuilder.cpp
js/src/jit/IonMacroAssembler.cpp
js/src/jit/IonMacroAssembler.h
js/src/jit/JitCompartment.h
js/src/jit/LIR-Common.h
js/src/jit/LOpcodes.h
js/src/jit/Lowering.cpp
js/src/jit/Lowering.h
js/src/jit/MIR.h
js/src/jit/MOpcodes.h
js/src/jit/ParallelSafetyAnalysis.cpp
js/src/jit/VMFunctions.cpp
js/src/jit/VMFunctions.h
js/src/jsobj.h
js/src/jsobjinlines.h
js/src/vm/ScopeObject.cpp
js/src/vm/ScopeObject.h
--- a/js/src/jit-test/tests/parallel/closure-nested-branch.js
+++ b/js/src/jit-test/tests/parallel/closure-nested-branch.js
@@ -1,22 +1,13 @@
 load(libdir + "parallelarray-helpers.js");
 
 function testClosureCreationAndInvocation() {
   var a = range(0, 64);
   function makeaddv(v) {
-    var u = 1;
-    var t = 2;
-    var s = 3;
-    var r = 4;
-    var q = 5;
-    var p = 6;
-    var o = 7;
-    var n = 8;
-    var m = 9;
     var l = 10;
     var k = 11;
     var j = 12;
     var i = 13;
     var h = 14;
     var g = 15;
     var f = 16;
     var e = 17;
@@ -29,21 +20,16 @@ function testClosureCreationAndInvocatio
             : function (x) {
               switch (x) {
               case 0: return a; case 1: return b;
               case 2: return c; case 3: return d;
               case 4: return e; case 5: return f;
               case 6: return g; case 7: return h;
               case 8: return i; case 9: return j;
               case 10: return k; case 11: return l;
-              case 12: return m; case 13: return n;
-              case 14: return o; case 15: return p;
-              case 16: return q; case 17: return r;
-              case 18: return s; case 19: return t;
-              case 20: return u;
               }
             });
   }
   var m;
   for (var i in MODES) m = a.mapPar(makeaddv, MODES[i]);
   assertEq(m[21](1), 20); // v == 21; x == 1 ==> inner function returns b == 20
 
   var n = a.mapPar(function (v) { return function (x) { return v; }});
--- a/js/src/jit-test/tests/parallel/closure-nested-compute.js
+++ b/js/src/jit-test/tests/parallel/closure-nested-compute.js
@@ -1,22 +1,13 @@
 load(libdir + "parallelarray-helpers.js");
 
 function testClosureCreationAndInvocation() {
   var a = range(0, 64);
   function makeaddv(v) {
-    var u = 1;
-    var t = 2;
-    var s = 3;
-    var r = 4;
-    var q = 5;
-    var p = 6;
-    var o = 7;
-    var n = 8;
-    var m = 9;
     var l = 10;
     var k = 11;
     var j = 12;
     var i = 13;
     var h = 14;
     var g = 15;
     var f = 16;
     var e = 17;
@@ -27,21 +18,16 @@ function testClosureCreationAndInvocatio
     return function (x) {
       switch (x) {
       case 0: return a; case 1: return b;
       case 2: return c; case 3: return d;
       case 4: return e; case 5: return f;
       case 6: return g; case 7: return h;
       case 8: return i; case 9: return j;
       case 10: return k; case 11: return l;
-      case 12: return m; case 13: return n;
-      case 14: return o; case 15: return p;
-      case 16: return q; case 17: return r;
-      case 18: return s; case 19: return t;
-      case 20: return u;
       }
     };
   };
   for (var i in MODES) {
     var m = a.mapPar(makeaddv, MODES[i]);
     assertEq(m[21](1), 20); // v == 21; x == 1 ==> inner function returns b == 20
   }
 }
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -967,18 +967,17 @@ CodeGenerator::visitLambda(LLambda *lir)
 
     OutOfLineCode *ool = oolCallVM(LambdaInfo, lir, (ArgList(), ImmGCPtr(info.fun), scopeChain),
                                    StoreRegisterTo(output));
     if (!ool)
         return false;
 
     JS_ASSERT(!info.singletonType);
 
-    masm.newGCThing(output, tempReg, info.fun, ool->entry(), gc::DefaultHeap);
-    masm.initGCThing(output, tempReg, info.fun);
+    masm.createGCObject(output, tempReg, info.fun, gc::DefaultHeap, ool->entry());
 
     emitLambdaInit(output, scopeChain, info);
 
     masm.bind(ool->rejoin());
     return true;
 }
 
 void
@@ -3332,39 +3331,16 @@ CodeGenerator::visitNewDerivedTypedObjec
     JS_ASSERT(gen->info().executionMode() == SequentialExecution);
 
     pushArg(ToRegister(lir->offset()));
     pushArg(ToRegister(lir->owner()));
     pushArg(ToRegister(lir->type()));
     return callVM(CreateDerivedTypedObjInfo, lir);
 }
 
-bool
-CodeGenerator::visitNewSlots(LNewSlots *lir)
-{
-    Register temp1 = ToRegister(lir->temp1());
-    Register temp2 = ToRegister(lir->temp2());
-    Register temp3 = ToRegister(lir->temp3());
-    Register output = ToRegister(lir->output());
-
-    masm.mov(ImmPtr(GetIonContext()->runtime), temp1);
-    masm.mov(ImmWord(lir->mir()->nslots()), temp2);
-
-    masm.setupUnalignedABICall(2, temp3);
-    masm.passABIArg(temp1);
-    masm.passABIArg(temp2);
-    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, NewSlots));
-
-    masm.testPtr(output, output);
-    if (!bailoutIf(Assembler::Zero, lir->snapshot()))
-        return false;
-
-    return true;
-}
-
 bool CodeGenerator::visitAtan2D(LAtan2D *lir)
 {
     Register temp = ToRegister(lir->temp());
     FloatRegister y = ToFloatRegister(lir->y());
     FloatRegister x = ToFloatRegister(lir->x());
 
     masm.setupUnalignedABICall(2, temp);
     masm.passABIArg(y, MoveOp::DOUBLE);
@@ -3403,18 +3379,17 @@ CodeGenerator::visitNewArray(LNewArray *
 
     if (lir->mir()->shouldUseVM())
         return visitNewArrayCallVM(lir);
 
     OutOfLineNewArray *ool = new(alloc()) OutOfLineNewArray(lir);
     if (!addOutOfLineCode(ool))
         return false;
 
-    masm.newGCThing(objReg, tempReg, templateObject, ool->entry(), lir->mir()->initialHeap());
-    masm.initGCThing(objReg, tempReg, templateObject);
+    masm.createGCObject(objReg, tempReg, templateObject, lir->mir()->initialHeap(), ool->entry());
 
     masm.bind(ool->rejoin());
     return true;
 }
 
 bool
 CodeGenerator::visitOutOfLineNewArray(OutOfLineNewArray *ool)
 {
@@ -3490,18 +3465,17 @@ CodeGenerator::visitNewObject(LNewObject
 
     if (lir->mir()->shouldUseVM())
         return visitNewObjectVMCall(lir);
 
     OutOfLineNewObject *ool = new(alloc()) OutOfLineNewObject(lir);
     if (!addOutOfLineCode(ool))
         return false;
 
-    masm.newGCThing(objReg, tempReg, templateObject, ool->entry(), lir->mir()->initialHeap());
-    masm.initGCThing(objReg, tempReg, templateObject);
+    masm.createGCObject(objReg, tempReg, templateObject, lir->mir()->initialHeap(), ool->entry());
 
     masm.bind(ool->rejoin());
     return true;
 }
 
 bool
 CodeGenerator::visitOutOfLineNewObject(OutOfLineNewObject *ool)
 {
@@ -3526,91 +3500,64 @@ CodeGenerator::visitNewDeclEnvObject(LNe
     // If we have a template object, we can inline call object creation.
     OutOfLineCode *ool = oolCallVM(NewDeclEnvObjectInfo, lir,
                                    (ArgList(), ImmGCPtr(info.funMaybeLazy()),
                                     Imm32(gc::DefaultHeap)),
                                    StoreRegisterTo(objReg));
     if (!ool)
         return false;
 
-    masm.newGCThing(objReg, tempReg, templateObj, ool->entry(), gc::DefaultHeap);
-    masm.initGCThing(objReg, tempReg, templateObj);
+    masm.createGCObject(objReg, tempReg, templateObj, gc::DefaultHeap, ool->entry());
+
     masm.bind(ool->rejoin());
     return true;
 }
 
-typedef JSObject *(*NewCallObjectFn)(JSContext *, HandleScript, HandleShape,
-                                     HandleTypeObject, HeapSlot *);
+typedef JSObject *(*NewCallObjectFn)(JSContext *, HandleScript, HandleShape, HandleTypeObject);
 static const VMFunction NewCallObjectInfo =
     FunctionInfo<NewCallObjectFn>(NewCallObject);
 
 bool
 CodeGenerator::visitNewCallObject(LNewCallObject *lir)
 {
     Register objReg = ToRegister(lir->output());
     Register tempReg = ToRegister(lir->temp());
 
     JSObject *templateObj = lir->mir()->templateObject();
 
     // If we have a template object, we can inline call object creation.
-    OutOfLineCode *ool;
-    if (lir->slots()->isRegister()) {
-        ool = oolCallVM(NewCallObjectInfo, lir,
-                        (ArgList(), ImmGCPtr(lir->mir()->block()->info().script()),
-                                    ImmGCPtr(templateObj->lastProperty()),
-                                    ImmGCPtr(templateObj->hasSingletonType() ? nullptr : templateObj->type()),
-                                    ToRegister(lir->slots())),
-                        StoreRegisterTo(objReg));
-    } else {
-        ool = oolCallVM(NewCallObjectInfo, lir,
-                        (ArgList(), ImmGCPtr(lir->mir()->block()->info().script()),
-                                    ImmGCPtr(templateObj->lastProperty()),
-                                    ImmGCPtr(templateObj->hasSingletonType() ? nullptr : templateObj->type()),
-                                    ImmPtr(nullptr)),
-                        StoreRegisterTo(objReg));
-    }
+    OutOfLineCode *ool = oolCallVM(NewCallObjectInfo, lir,
+                                   (ArgList(), ImmGCPtr(lir->mir()->block()->info().script()),
+                                               ImmGCPtr(templateObj->lastProperty()),
+                                               ImmGCPtr(templateObj->hasSingletonType() ? nullptr : templateObj->type())),
+                                   StoreRegisterTo(objReg));
     if (!ool)
         return false;
 
     if (lir->mir()->needsSingletonType()) {
         // Objects can only be given singleton types in VM calls.
         masm.jump(ool->entry());
     } else {
-        masm.newGCThing(objReg, tempReg, templateObj, ool->entry(), gc::DefaultHeap);
-        masm.initGCThing(objReg, tempReg, templateObj);
-
-        if (lir->slots()->isRegister())
-            masm.storePtr(ToRegister(lir->slots()), Address(objReg, JSObject::offsetOfSlots()));
+        masm.createGCObject(objReg, tempReg, templateObj, gc::DefaultHeap, ool->entry());
     }
 
     masm.bind(ool->rejoin());
     return true;
 }
 
 bool
 CodeGenerator::visitNewCallObjectPar(LNewCallObjectPar *lir)
 {
     Register resultReg = ToRegister(lir->output());
     Register cxReg = ToRegister(lir->forkJoinContext());
     Register tempReg1 = ToRegister(lir->getTemp0());
     Register tempReg2 = ToRegister(lir->getTemp1());
     JSObject *templateObj = lir->mir()->templateObj();
 
     emitAllocateGCThingPar(lir, resultReg, cxReg, tempReg1, tempReg2, templateObj);
-
-    // NB: !lir->slots()->isRegister() implies that there is no slots
-    // array at all, and the memory is already zeroed when copying
-    // from the template object
-
-    if (lir->slots()->isRegister()) {
-        Register slotsReg = ToRegister(lir->slots());
-        JS_ASSERT(slotsReg != resultReg);
-        masm.storePtr(slotsReg, Address(resultReg, JSObject::offsetOfSlots()));
-    }
-
     return true;
 }
 
 bool
 CodeGenerator::visitNewDenseArrayPar(LNewDenseArrayPar *lir)
 {
     Register cxReg = ToRegister(lir->forkJoinContext());
     Register lengthReg = ToRegister(lir->length());
@@ -3659,18 +3606,17 @@ CodeGenerator::visitNewStringObject(LNew
 
     StringObject *templateObj = lir->mir()->templateObj();
 
     OutOfLineCode *ool = oolCallVM(NewStringObjectInfo, lir, (ArgList(), input),
                                    StoreRegisterTo(output));
     if (!ool)
         return false;
 
-    masm.newGCThing(output, temp, templateObj, ool->entry(), gc::DefaultHeap);
-    masm.initGCThing(output, temp, templateObj);
+    masm.createGCObject(output, temp, templateObj, gc::DefaultHeap, ool->entry());
 
     masm.loadStringLength(input, temp);
 
     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;
@@ -3905,17 +3851,17 @@ CodeGenerator::visitCreateThisWithTempla
 
     OutOfLineCode *ool = oolCallVM(NewGCObjectInfo, lir,
                                    (ArgList(), Imm32(allocKind), Imm32(initialHeap)),
                                    StoreRegisterTo(objReg));
     if (!ool)
         return false;
 
     // Allocate. If the FreeList is empty, call to VM, which may GC.
-    masm.newGCThing(objReg, tempReg, templateObject, ool->entry(), lir->mir()->initialHeap());
+    masm.newGCThing(objReg, tempReg, templateObject, lir->mir()->initialHeap(), ool->entry());
 
     // Initialize based on the templateObject.
     masm.bind(ool->rejoin());
     masm.initGCThing(objReg, tempReg, templateObject);
 
     return true;
 }
 
@@ -4974,16 +4920,80 @@ JitCompartment::generateStringConcatStub
 
 #ifdef JS_ION_PERF
     writePerfSpewerJitCodeProfile(code, "StringConcatStub");
 #endif
 
     return code;
 }
 
+JitCode *
+JitRuntime::generateMallocStub(JSContext *cx)
+{
+    const Register regReturn = CallTempReg0;
+    const Register regNBytes = CallTempReg0;
+    const Register regRuntime = CallTempReg1;
+    const Register regTemp = CallTempReg1;
+
+    MacroAssembler masm(cx);
+
+    RegisterSet regs = RegisterSet::Volatile();
+    regs.takeUnchecked(regNBytes);
+    masm.PushRegsInMask(regs);
+
+    masm.setupUnalignedABICall(2, regTemp);
+    masm.movePtr(ImmPtr(cx->runtime()), regRuntime);
+    masm.passABIArg(regRuntime);
+    masm.passABIArg(regNBytes);
+    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, MallocWrapper));
+    masm.storeCallResult(regReturn);
+
+    masm.PopRegsInMask(regs);
+    masm.ret();
+
+    Linker linker(masm);
+    JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE);
+
+#ifdef JS_ION_PERF
+    writePerfSpewerJitCodeProfile(code, "MallocStub");
+#endif
+
+    return code;
+}
+
+JitCode *
+JitRuntime::generateFreeStub(JSContext *cx)
+{
+    const Register regSlots = CallTempReg0;
+    const Register regTemp = CallTempReg1;
+
+    MacroAssembler masm(cx);
+
+    RegisterSet regs = RegisterSet::Volatile();
+    regs.takeUnchecked(regSlots);
+    masm.PushRegsInMask(regs);
+
+    masm.setupUnalignedABICall(1, regTemp);
+    masm.passABIArg(regSlots);
+    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, js_free));
+
+    masm.PopRegsInMask(regs);
+
+    masm.ret();
+
+    Linker linker(masm);
+    JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE);
+
+#ifdef JS_ION_PERF
+    writePerfSpewerJitCodeProfile(code, "FreeStub");
+#endif
+
+    return code;
+}
+
 typedef bool (*CharCodeAtFn)(JSContext *, HandleString, int32_t, uint32_t *);
 static const VMFunction CharCodeAtInfo = FunctionInfo<CharCodeAtFn>(jit::CharCodeAt);
 
 bool
 CodeGenerator::visitCharCodeAt(LCharCodeAt *lir)
 {
     Register str = ToRegister(lir->str());
     Register index = ToRegister(lir->index());
@@ -5627,19 +5637,17 @@ CodeGenerator::visitArrayConcat(LArrayCo
     masm.load32(Address(temp1, ObjectElements::offsetOfInitializedLength()), temp2);
     masm.branch32(Assembler::NotEqual, Address(temp1, ObjectElements::offsetOfLength()), temp2, &fail);
 
     masm.loadPtr(Address(rhs, JSObject::offsetOfElements()), temp1);
     masm.load32(Address(temp1, ObjectElements::offsetOfInitializedLength()), temp2);
     masm.branch32(Assembler::NotEqual, Address(temp1, ObjectElements::offsetOfLength()), temp2, &fail);
 
     // Try to allocate an object.
-    JSObject *templateObj = lir->mir()->templateObj();
-    masm.newGCThing(temp1, temp2, templateObj, &fail, lir->mir()->initialHeap());
-    masm.initGCThing(temp1, temp2, templateObj);
+    masm.createGCObject(temp1, temp2, lir->mir()->templateObj(), lir->mir()->initialHeap(), &fail);
     masm.jump(&call);
     {
         masm.bind(&fail);
         masm.movePtr(ImmPtr(nullptr), temp1);
     }
     masm.bind(&call);
 
     pushArg(temp1);
@@ -6000,18 +6008,17 @@ CodeGenerator::visitRest(LRest *lir)
     Register numActuals = ToRegister(lir->numActuals());
     Register temp0 = ToRegister(lir->getTemp(0));
     Register temp1 = ToRegister(lir->getTemp(1));
     Register temp2 = ToRegister(lir->getTemp(2));
     unsigned numFormals = lir->mir()->numFormals();
     JSObject *templateObject = lir->mir()->templateObject();
 
     Label joinAlloc, failAlloc;
-    masm.newGCThing(temp2, temp0, templateObject, &failAlloc, gc::DefaultHeap);
-    masm.initGCThing(temp2, temp0, templateObject);
+    masm.createGCObject(temp2, temp0, templateObject, gc::DefaultHeap, &failAlloc);
     masm.jump(&joinAlloc);
     {
         masm.bind(&failAlloc);
         masm.movePtr(ImmPtr(nullptr), temp2);
     }
     masm.bind(&joinAlloc);
 
     return emitRest(lir, temp2, numActuals, temp0, temp1, numFormals, templateObject);
--- a/js/src/jit/CodeGenerator.h
+++ b/js/src/jit/CodeGenerator.h
@@ -126,17 +126,16 @@ class CodeGenerator : public CodeGenerat
     bool visitBail(LBail *lir);
     bool visitGetDynamicName(LGetDynamicName *lir);
     bool visitFilterArgumentsOrEvalS(LFilterArgumentsOrEvalS *lir);
     bool visitFilterArgumentsOrEvalV(LFilterArgumentsOrEvalV *lir);
     bool visitCallDirectEvalS(LCallDirectEvalS *lir);
     bool visitCallDirectEvalV(LCallDirectEvalV *lir);
     bool visitDoubleToInt32(LDoubleToInt32 *lir);
     bool visitFloat32ToInt32(LFloat32ToInt32 *lir);
-    bool visitNewSlots(LNewSlots *lir);
     bool visitNewArrayCallVM(LNewArray *lir);
     bool visitNewArray(LNewArray *lir);
     bool visitOutOfLineNewArray(OutOfLineNewArray *ool);
     bool visitNewObjectVMCall(LNewObject *lir);
     bool visitNewObject(LNewObject *lir);
     bool visitOutOfLineNewObject(OutOfLineNewObject *ool);
     bool visitNewDeclEnvObject(LNewDeclEnvObject *lir);
     bool visitNewCallObject(LNewCallObject *lir);
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -263,16 +263,26 @@ JitRuntime::initialize(JSContext *cx)
     if (!valuePreBarrier_)
         return false;
 
     IonSpew(IonSpew_Codegen, "# Emitting Pre Barrier for Shape");
     shapePreBarrier_ = generatePreBarrier(cx, MIRType_Shape);
     if (!shapePreBarrier_)
         return false;
 
+    IonSpew(IonSpew_Codegen, "# Emitting malloc stub");
+    mallocStub_ = generateMallocStub(cx);
+    if (!mallocStub_)
+        return false;
+
+    IonSpew(IonSpew_Codegen, "# Emitting free stub");
+    freeStub_ = generateFreeStub(cx);
+    if (!freeStub_)
+        return false;
+
     IonSpew(IonSpew_Codegen, "# Emitting VM function wrappers");
     for (VMFunction *fun = VMFunction::functions; fun; fun = fun->next) {
         if (!generateVMWrapper(cx, *fun))
             return false;
     }
 
     return true;
 }
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -4662,49 +4662,41 @@ IonBuilder::createDeclEnvObject(MDefinit
 
 MInstruction *
 IonBuilder::createCallObject(MDefinition *callee, MDefinition *scope)
 {
     // Get a template CallObject that we'll use to generate inline object
     // creation.
     CallObject *templateObj = inspector->templateCallObject();
 
-    // If the CallObject needs dynamic slots, allocate those now.
-    MInstruction *slots;
-    if (templateObj->hasDynamicSlots()) {
-        size_t nslots = JSObject::dynamicSlotsCount(templateObj->numFixedSlots(),
-                                                    templateObj->lastProperty()->slotSpan(templateObj->getClass()),
-                                                    templateObj->getClass());
-        slots = MNewSlots::New(alloc(), nslots);
-    } else {
-        slots = MConstant::New(alloc(), NullValue());
-    }
-    current->add(slots);
-
-    // Allocate the actual object. It is important that no intervening
-    // instructions could potentially bailout, thus leaking the dynamic slots
-    // pointer. Run-once scripts need a singleton type, so always do a VM call
-    // in such cases.
-    MNewCallObject *callObj = MNewCallObject::New(alloc(), templateObj, script()->treatAsRunOnce(), slots);
+    // Allocate the object. Run-once scripts need a singleton type, so always do
+    // a VM call in such cases.
+    MNewCallObject *callObj = MNewCallObject::New(alloc(), templateObj, script()->treatAsRunOnce());
     current->add(callObj);
 
     // Initialize the object's reserved slots. No post barrier is needed here,
     // for the same reason as in createDeclEnvObject.
     current->add(MStoreFixedSlot::New(alloc(), callObj, CallObject::enclosingScopeSlot(), scope));
     current->add(MStoreFixedSlot::New(alloc(), callObj, CallObject::calleeSlot(), callee));
 
     // Initialize argument slots.
+    MSlots *slots = nullptr;
     for (AliasedFormalIter i(script()); i; i++) {
         unsigned slot = i.scopeSlot();
         unsigned formal = i.frameIndex();
         MDefinition *param = current->getSlot(info().argSlotUnchecked(formal));
-        if (slot >= templateObj->numFixedSlots())
+        if (slot >= templateObj->numFixedSlots()) {
+            if (!slots) {
+                slots = MSlots::New(alloc(), callObj);
+                current->add(slots);
+            }
             current->add(MStoreSlot::New(alloc(), slots, slot - templateObj->numFixedSlots(), param));
-        else
+        } else {
             current->add(MStoreFixedSlot::New(alloc(), callObj, slot, param));
+        }
     }
 
     return callObj;
 }
 
 MDefinition *
 IonBuilder::createThisScripted(MDefinition *callee)
 {
@@ -7607,16 +7599,20 @@ IonBuilder::setElemTryTypedStatic(bool *
 
     if (!object->resultTypeSet())
         return true;
     JSObject *tarrObj = object->resultTypeSet()->getSingleton();
     if (!tarrObj)
         return true;
 
     TypedArrayObject *tarr = &tarrObj->as<TypedArrayObject>();
+
+    if (gc::IsInsideNursery(tarr->runtimeFromMainThread(), tarr->viewData()))
+        return true;
+
     ArrayBufferView::ViewType viewType = (ArrayBufferView::ViewType) tarr->type();
 
     MDefinition *ptr = convertShiftToMaskForStaticTypedArray(index, viewType);
     if (!ptr)
         return true;
 
     // Emit StoreTypedArrayElementStatic.
     object->setImplicitlyUsedUnchecked();
--- a/js/src/jit/IonMacroAssembler.cpp
+++ b/js/src/jit/IonMacroAssembler.cpp
@@ -628,84 +628,211 @@ MacroAssembler::clampDoubleToUint8(Float
     {
         move32(Imm32(255), output);
     }
 
     bind(&done);
 #endif
 }
 
+// Inlined version of gc::CheckAllocatorState that checks the bare essentials
+// and bails for anything that cannot be handled with our jit allocators.
 void
-MacroAssembler::newGCThing(Register result, Register temp, gc::AllocKind allocKind, Label *fail,
-                           gc::InitialHeap initialHeap /* = gc::DefaultHeap */)
+MacroAssembler::checkAllocatorState(Label *fail)
 {
-    // Inlined equivalent of js::gc::NewGCThing() without failure case handling.
-
-    int thingSize = int(gc::Arena::thingSize(allocKind));
-
 #ifdef JS_GC_ZEAL
     // Don't execute the inline path if gcZeal is active.
     branch32(Assembler::NotEqual,
              AbsoluteAddress(GetIonContext()->runtime->addressOfGCZeal()), Imm32(0),
              fail);
 #endif
 
     // Don't execute the inline path if the compartment has an object metadata callback,
     // as the metadata to use for the object may vary between executions of the op.
     if (GetIonContext()->compartment->hasObjectMetadataCallback())
         jump(fail);
+}
 
+// Inline version of ShouldNurseryAllocate.
+bool
+MacroAssembler::shouldNurseryAllocate(gc::AllocKind allocKind, gc::InitialHeap initialHeap)
+{
+#ifdef JSGC_GENERATIONAL
+    // Note that Ion elides barriers on writes to objects know to be in the
+    // nursery, so any allocation that can be made into the nursery must be made
+    // into the nursery, even if the nursery is disabled. At runtime these will
+    // take the out-of-line path, which is required to insert a barrier for the
+    // initializing writes.
+    return IsNurseryAllocable(allocKind) && initialHeap != gc::TenuredHeap;
+#else
+    return false;
+#endif
+}
+
+// Inline version of Nursery::allocateObject.
+void
+MacroAssembler::nurseryAllocate(Register result, Register slots, gc::AllocKind allocKind,
+                                size_t nDynamicSlots, gc::InitialHeap initialHeap, Label *fail)
+{
 #ifdef JSGC_GENERATIONAL
-    // Always use nursery allocation if it is possible to do so. The jit
-    // assumes a nursery pointer is returned to avoid barriers.
-    if (allocKind <= gc::FINALIZE_OBJECT_LAST && initialHeap != gc::TenuredHeap) {
-        // Inline Nursery::allocate. No explicit check for nursery.isEnabled()
-        // is needed, as the comparison with the nursery's end will always fail
-        // in such cases.
-        const Nursery &nursery = GetIonContext()->runtime->gcNursery();
-        loadPtr(AbsoluteAddress(nursery.addressOfPosition()), result);
-        computeEffectiveAddress(Address(result, thingSize), temp);
-        branchPtr(Assembler::BelowOrEqual, AbsoluteAddress(nursery.addressOfCurrentEnd()), temp, fail);
-        storePtr(temp, AbsoluteAddress(nursery.addressOfPosition()));
-        return;
-    }
+    JS_ASSERT(IsNurseryAllocable(allocKind));
+    JS_ASSERT(initialHeap != gc::TenuredHeap);
+
+    // This allocation site is requesting too many dynamic slots. Several
+    // allocation sites assume that nursery allocation will succeed to
+    // avoid needing barriers later. Ensure these sites limit their slot
+    // requests appropriately.
+    JS_ASSERT(nDynamicSlots < Nursery::MaxNurserySlots);
+
+    // No explicit check for nursery.isEnabled() is needed, as the comparison
+    // with the nursery's end will always fail in such cases.
+    const Nursery &nursery = GetIonContext()->runtime->gcNursery();
+    Register temp = slots;
+    int thingSize = int(gc::Arena::thingSize(allocKind));
+    int totalSize = thingSize + nDynamicSlots * sizeof(HeapSlot);
+    loadPtr(AbsoluteAddress(nursery.addressOfPosition()), result);
+    computeEffectiveAddress(Address(result, totalSize), temp);
+    branchPtr(Assembler::BelowOrEqual, AbsoluteAddress(nursery.addressOfCurrentEnd()), temp, fail);
+    storePtr(temp, AbsoluteAddress(nursery.addressOfPosition()));
+
+    if (nDynamicSlots)
+        computeEffectiveAddress(Address(result, thingSize), slots);
 #endif // JSGC_GENERATIONAL
-
-    CompileZone *zone = GetIonContext()->compartment->zone();
+}
 
-    // Inline FreeSpan::allocate.
-    // There is always exactly one FreeSpan per allocKind per JSCompartment.
-    // If a FreeSpan is replaced, its members are updated in the freeLists table,
-    // which the code below always re-reads.
+// Inlined version of FreeSpan::allocate.
+void
+MacroAssembler::freeSpanAllocate(Register result, Register temp, gc::AllocKind allocKind, Label *fail)
+{
+    CompileZone *zone = GetIonContext()->compartment->zone();
+    int thingSize = int(gc::Arena::thingSize(allocKind));
+
+    // Load FreeSpan::first of |zone|'s freeLists for |allocKind|. If there is
+    // no room remaining in the span, we bail to finish the allocation. The
+    // interpreter will call |refillFreeLists|, setting up a new FreeSpan so
+    // that we can continue allocating in the jit.
     loadPtr(AbsoluteAddress(zone->addressOfFreeListFirst(allocKind)), result);
     branchPtr(Assembler::BelowOrEqual, AbsoluteAddress(zone->addressOfFreeListLast(allocKind)), result, fail);
     computeEffectiveAddress(Address(result, thingSize), temp);
     storePtr(temp, AbsoluteAddress(zone->addressOfFreeListFirst(allocKind)));
 }
 
 void
-MacroAssembler::newGCThing(Register result, Register temp, JSObject *templateObject, Label *fail,
-                           gc::InitialHeap initialHeap)
+MacroAssembler::callMallocStub(size_t nbytes, Register result, Label *fail)
 {
-    gc::AllocKind allocKind = templateObject->tenuredGetAllocKind();
+    // This register must match the one in JitRuntime::generateMallocStub.
+    const Register regNBytes = CallTempReg0;
+
+    JS_ASSERT(nbytes > 0);
+    JS_ASSERT(nbytes <= INT32_MAX);
+
+    if (regNBytes != result)
+        push(regNBytes);
+    move32(Imm32(nbytes), regNBytes);
+    call(GetIonContext()->runtime->jitRuntime()->mallocStub());
+    if (regNBytes != result) {
+        movePtr(regNBytes, result);
+        pop(regNBytes);
+    }
+    branchTest32(Assembler::Zero, result, result, fail);
+}
+
+void
+MacroAssembler::callFreeStub(Register slots)
+{
+    // This register must match the one in JitRuntime::generateFreeStub.
+    const Register regSlots = CallTempReg0;
+
+    push(regSlots);
+    movePtr(slots, regSlots);
+    call(GetIonContext()->runtime->jitRuntime()->freeStub());
+    pop(regSlots);
+}
+
+// Inlined equivalent of gc::AllocateObject, without failure case handling.
+void
+MacroAssembler::allocateObject(Register result, Register slots, gc::AllocKind allocKind,
+                               uint32_t nDynamicSlots, gc::InitialHeap initialHeap, Label *fail)
+{
     JS_ASSERT(allocKind >= gc::FINALIZE_OBJECT0 && allocKind <= gc::FINALIZE_OBJECT_LAST);
 
-    newGCThing(result, temp, allocKind, fail, initialHeap);
+    checkAllocatorState(fail);
+
+    if (shouldNurseryAllocate(allocKind, initialHeap))
+        return nurseryAllocate(result, slots, allocKind, nDynamicSlots, initialHeap, fail);
+
+    if (!nDynamicSlots)
+        return freeSpanAllocate(result, slots, allocKind, fail);
+
+    callMallocStub(nDynamicSlots * sizeof(HeapValue), slots, fail);
+
+    Label failAlloc;
+    Label success;
+
+    push(slots);
+    freeSpanAllocate(result, slots, allocKind, &failAlloc);
+    pop(slots);
+    jump(&success);
+
+    bind(&failAlloc);
+    pop(slots);
+    callFreeStub(slots);
+    jump(fail);
+
+    bind(&success);
+}
+
+void
+MacroAssembler::newGCThing(Register result, Register temp, JSObject *templateObj,
+                            gc::InitialHeap initialHeap, Label *fail)
+{
+    // This method does not initialize the object: if external slots get
+    // allocated into |temp|, there is no easy way for us to ensure the caller
+    // frees them. Instead just assert this case does not happen.
+    JS_ASSERT(!templateObj->numDynamicSlots());
+
+    gc::AllocKind allocKind = templateObj->tenuredGetAllocKind();
+    JS_ASSERT(allocKind >= gc::FINALIZE_OBJECT0 && allocKind <= gc::FINALIZE_OBJECT_LAST);
+
+    allocateObject(result, temp, allocKind, templateObj->numDynamicSlots(), initialHeap, fail);
+}
+
+void
+MacroAssembler::createGCObject(Register obj, Register temp, JSObject *templateObj,
+                               gc::InitialHeap initialHeap, Label *fail)
+{
+    uint32_t nDynamicSlots = templateObj->numDynamicSlots();
+    gc::AllocKind allocKind = templateObj->tenuredGetAllocKind();
+    JS_ASSERT(allocKind >= gc::FINALIZE_OBJECT0 && allocKind <= gc::FINALIZE_OBJECT_LAST);
+
+    allocateObject(obj, temp, allocKind, nDynamicSlots, initialHeap, fail);
+    initGCThing(obj, temp, templateObj);
+}
+
+
+// Inlined equivalent of gc::AllocateNonObject, without failure case handling.
+// Non-object allocation does not need to worry about slots, so can take a
+// simpler path.
+void
+MacroAssembler::allocateNonObject(Register result, Register temp, gc::AllocKind allocKind, Label *fail)
+{
+    checkAllocatorState(fail);
+    freeSpanAllocate(result, temp, allocKind, fail);
 }
 
 void
 MacroAssembler::newGCString(Register result, Register temp, Label *fail)
 {
-    newGCThing(result, temp, js::gc::FINALIZE_STRING, fail);
+    allocateNonObject(result, temp, js::gc::FINALIZE_STRING, fail);
 }
 
 void
 MacroAssembler::newGCShortString(Register result, Register temp, Label *fail)
 {
-    newGCThing(result, temp, js::gc::FINALIZE_SHORT_STRING, fail);
+    allocateNonObject(result, temp, js::gc::FINALIZE_SHORT_STRING, fail);
 }
 
 void
 MacroAssembler::newGCThingPar(Register result, Register cx, Register tempReg1, Register tempReg2,
                               gc::AllocKind allocKind, Label *fail)
 {
     // Similar to ::newGCThing(), except that it allocates from a custom
     // Allocator in the ForkJoinContext*, rather than being hardcoded to the
@@ -751,16 +878,17 @@ MacroAssembler::newGCThingPar(Register r
 }
 
 void
 MacroAssembler::newGCThingPar(Register result, Register cx, Register tempReg1, Register tempReg2,
                               JSObject *templateObject, Label *fail)
 {
     gc::AllocKind allocKind = templateObject->tenuredGetAllocKind();
     JS_ASSERT(allocKind >= gc::FINALIZE_OBJECT0 && allocKind <= gc::FINALIZE_OBJECT_LAST);
+    JS_ASSERT(!templateObject->numDynamicSlots());
 
     newGCThingPar(result, cx, tempReg1, tempReg2, allocKind, fail);
 }
 
 void
 MacroAssembler::newGCStringPar(Register result, Register cx, Register tempReg1, Register tempReg2,
                                Label *fail)
 {
@@ -770,94 +898,115 @@ MacroAssembler::newGCStringPar(Register 
 void
 MacroAssembler::newGCShortStringPar(Register result, Register cx, Register tempReg1,
                                     Register tempReg2, Label *fail)
 {
     newGCThingPar(result, cx, tempReg1, tempReg2, js::gc::FINALIZE_SHORT_STRING, fail);
 }
 
 void
-MacroAssembler::copySlotsFromTemplate(Register obj, Register temp, const JSObject *templateObj,
+MacroAssembler::copySlotsFromTemplate(Register obj, const JSObject *templateObj,
                                       uint32_t start, uint32_t end)
 {
     uint32_t nfixed = Min(templateObj->numFixedSlots(), end);
     for (unsigned i = start; i < nfixed; i++)
         storeValue(templateObj->getFixedSlot(i), Address(obj, JSObject::getFixedSlotOffset(i)));
 }
 
 void
-MacroAssembler::fillSlotsWithUndefined(Register obj, Register temp, const JSObject *templateObj,
-                                       uint32_t start, uint32_t end)
+MacroAssembler::fillSlotsWithUndefined(Address base, Register temp, uint32_t start, uint32_t end)
 {
 #ifdef JS_NUNBOX32
     // We only have a single spare register, so do the initialization as two
     // strided writes of the tag and body.
     jsval_layout jv = JSVAL_TO_IMPL(UndefinedValue());
-    uint32_t nfixed = Min(templateObj->numFixedSlots(), end);
 
-    mov(ImmWord(jv.s.tag), temp);
-    for (unsigned i = start; i < nfixed; i++)
-        store32(temp, ToType(Address(obj, JSObject::getFixedSlotOffset(i))));
+    Address addr = base;
+    move32(Imm32(jv.s.payload.i32), temp);
+    for (unsigned i = start; i < end; ++i, addr.offset += sizeof(HeapValue))
+        store32(temp, ToPayload(addr));
 
-    mov(ImmWord(jv.s.payload.i32), temp);
-    for (unsigned i = start; i < nfixed; i++)
-        store32(temp, ToPayload(Address(obj, JSObject::getFixedSlotOffset(i))));
+    addr = base;
+    move32(Imm32(jv.s.tag), temp);
+    for (unsigned i = start; i < end; ++i, addr.offset += sizeof(HeapValue))
+        store32(temp, ToType(addr));
 #else
     moveValue(UndefinedValue(), temp);
-    uint32_t nfixed = Min(templateObj->numFixedSlots(), end);
-    for (unsigned i = start; i < nfixed; i++)
-        storePtr(temp, Address(obj, JSObject::getFixedSlotOffset(i)));
+    for (uint32_t i = start; i < end; ++i, base.offset += sizeof(HeapValue))
+        storePtr(temp, base);
 #endif
 }
 
 static uint32_t
 FindStartOfUndefinedSlots(JSObject *templateObj, uint32_t nslots)
 {
     JS_ASSERT(nslots == templateObj->lastProperty()->slotSpan(templateObj->getClass()));
     JS_ASSERT(nslots > 0);
     for (uint32_t first = nslots; first != 0; --first) {
         if (templateObj->getSlot(first - 1) != UndefinedValue())
             return first;
     }
     return 0;
 }
 
 void
-MacroAssembler::initGCSlots(Register obj, Register temp, JSObject *templateObj)
+MacroAssembler::initGCSlots(Register obj, Register slots, JSObject *templateObj)
 {
     // Slots of non-array objects are required to be initialized.
     // Use the values currently in the template object.
     uint32_t nslots = templateObj->lastProperty()->slotSpan(templateObj->getClass());
     if (nslots == 0)
         return;
 
+    uint32_t nfixed = templateObj->numFixedSlots();
+    uint32_t ndynamic = templateObj->numDynamicSlots();
+
     // Attempt to group slot writes such that we minimize the amount of
     // duplicated data we need to embed in code and load into registers. In
     // general, most template object slots will be undefined except for any
     // reserved slots. Since reserved slots come first, we split the object
     // logically into independent non-UndefinedValue writes to the head and
     // duplicated writes of UndefinedValue to the tail. For the majority of
     // objects, the "tail" will be the entire slot range.
     uint32_t startOfUndefined = FindStartOfUndefinedSlots(templateObj, nslots);
-    copySlotsFromTemplate(obj, temp, templateObj, 0, startOfUndefined);
-    fillSlotsWithUndefined(obj, temp, templateObj, startOfUndefined, nslots);
+    JS_ASSERT(startOfUndefined <= nfixed); // Reserved slots must be fixed.
+
+    // Copy over any preserved reserved slots.
+    copySlotsFromTemplate(obj, templateObj, 0, startOfUndefined);
+
+    // Fill the rest of the fixed slots with undefined.
+    fillSlotsWithUndefined(Address(obj, JSObject::getFixedSlotOffset(startOfUndefined)), slots,
+                           startOfUndefined, nfixed);
+
+    if (ndynamic) {
+        // We are short one register to do this elegantly. Borrow the obj
+        // register briefly for our slots base address.
+        push(obj);
+        loadPtr(Address(obj, JSObject::offsetOfSlots()), obj);
+        fillSlotsWithUndefined(Address(obj, 0), slots, 0, ndynamic);
+        pop(obj);
+    }
 }
 
 void
-MacroAssembler::initGCThing(Register obj, Register temp, JSObject *templateObj)
+MacroAssembler::initGCThing(Register obj, Register slots, JSObject *templateObj)
 {
-    // Fast initialization of an empty object returned by NewGCThing().
+    // Fast initialization of an empty object returned by allocateObject().
 
     JS_ASSERT(!templateObj->hasDynamicElements());
 
     storePtr(ImmGCPtr(templateObj->lastProperty()), Address(obj, JSObject::offsetOfShape()));
     storePtr(ImmGCPtr(templateObj->type()), Address(obj, JSObject::offsetOfType()));
-    storePtr(ImmPtr(nullptr), Address(obj, JSObject::offsetOfSlots()));
+    if (templateObj->hasDynamicSlots())
+        storePtr(slots, Address(obj, JSObject::offsetOfSlots()));
+    else
+        storePtr(ImmPtr(nullptr), Address(obj, JSObject::offsetOfSlots()));
 
     if (templateObj->is<ArrayObject>()) {
+        Register temp = slots;
         JS_ASSERT(!templateObj->getDenseInitializedLength());
 
         int elementsOffset = JSObject::offsetOfFixedElements();
 
         computeEffectiveAddress(Address(obj, elementsOffset), temp);
         storePtr(temp, Address(obj, JSObject::offsetOfElements()));
 
         // Fill in the elements header.
@@ -870,17 +1019,17 @@ MacroAssembler::initGCThing(Register obj
         store32(Imm32(templateObj->shouldConvertDoubleElements()
                       ? ObjectElements::CONVERT_DOUBLE_ELEMENTS
                       : 0),
                 Address(obj, elementsOffset + ObjectElements::offsetOfFlags()));
         JS_ASSERT(!templateObj->hasPrivate());
     } else {
         storePtr(ImmPtr(emptyObjectElements), Address(obj, JSObject::offsetOfElements()));
 
-        initGCSlots(obj, temp, templateObj);
+        initGCSlots(obj, slots, templateObj);
 
         if (templateObj->hasPrivate()) {
             uint32_t nfixed = templateObj->numFixedSlots();
             storePtr(ImmPtr(templateObj->getPrivate()),
                      Address(obj, JSObject::getPrivateDataOffset(nfixed)));
         }
     }
 }
--- a/js/src/jit/IonMacroAssembler.h
+++ b/js/src/jit/IonMacroAssembler.h
@@ -781,38 +781,52 @@ class MacroAssembler : public MacroAssem
         bind(&done);
     }
 
     // Emit type case branch on tag matching if the type tag in the definition
     // might actually be that type.
     void branchEqualTypeIfNeeded(MIRType type, MDefinition *maybeDef, Register tag, Label *label);
 
     // Inline allocation.
-    void newGCThing(Register result, Register temp, gc::AllocKind allocKind, Label *fail,
-                    gc::InitialHeap initialHeap = gc::DefaultHeap);
-    void newGCThing(Register result, Register temp, JSObject *templateObject, Label *fail,
-                    gc::InitialHeap initialHeap);
+  private:
+    void checkAllocatorState(Label *fail);
+    bool shouldNurseryAllocate(gc::AllocKind allocKind, gc::InitialHeap initialHeap);
+    void nurseryAllocate(Register result, Register slots, gc::AllocKind allocKind,
+                         size_t nDynamicSlots, gc::InitialHeap initialHeap, Label *fail);
+    void freeSpanAllocate(Register result, Register temp, gc::AllocKind allocKind, Label *fail);
+    void allocateObject(Register result, Register slots, gc::AllocKind allocKind,
+                        uint32_t nDynamicSlots, gc::InitialHeap initialHeap, Label *fail);
+    void allocateNonObject(Register result, Register temp, gc::AllocKind allocKind, Label *fail);
+    void copySlotsFromTemplate(Register obj, const JSObject *templateObj,
+                               uint32_t start, uint32_t end);
+    void fillSlotsWithUndefined(Address addr, Register temp, uint32_t start, uint32_t end);
+    void initGCSlots(Register obj, Register temp, JSObject *templateObj);
+
+  public:
+    void callMallocStub(size_t nbytes, Register result, Label *fail);
+    void callFreeStub(Register slots);
+    void createGCObject(Register result, Register temp, JSObject *templateObj,
+                        gc::InitialHeap initialHeap, Label *fail);
+
+    void newGCThing(Register result, Register temp, JSObject *templateObj,
+                     gc::InitialHeap initialHeap, Label *fail);
+    void initGCThing(Register obj, Register temp, JSObject *templateObj);
+
     void newGCString(Register result, Register temp, Label *fail);
     void newGCShortString(Register result, Register temp, Label *fail);
 
     void newGCThingPar(Register result, Register cx, Register tempReg1, Register tempReg2,
                        gc::AllocKind allocKind, Label *fail);
     void newGCThingPar(Register result, Register cx, Register tempReg1, Register tempReg2,
                        JSObject *templateObject, Label *fail);
     void newGCStringPar(Register result, Register cx, Register tempReg1, Register tempReg2,
                         Label *fail);
     void newGCShortStringPar(Register result, Register cx, Register tempReg1, Register tempReg2,
                              Label *fail);
 
-    void copySlotsFromTemplate(Register obj, Register temp, const JSObject *templateObj,
-                               uint32_t start, uint32_t end);
-    void fillSlotsWithUndefined(Register obj, Register temp, const JSObject *templateObj,
-                                uint32_t start, uint32_t end);
-    void initGCSlots(Register obj, Register temp, JSObject *templateObj);
-    void initGCThing(Register obj, Register temp, JSObject *templateObj);
 
     // Compares two strings for equality based on the JSOP.
     // This checks for identical pointers, atoms and length and fails for everything else.
     void compareStrings(JSOp op, Register left, Register right, Register result,
                         Register temp, Label *fail);
 
     // Checks the flags that signal that parallel code may need to interrupt or
     // abort.  Branches to fail in that case.
--- a/js/src/jit/JitCompartment.h
+++ b/js/src/jit/JitCompartment.h
@@ -181,16 +181,20 @@ class JitRuntime
 
     // Thunk that invalides an (Ion compiled) caller on the Ion stack.
     JitCode *invalidator_;
 
     // Thunk that calls the GC pre barrier.
     JitCode *valuePreBarrier_;
     JitCode *shapePreBarrier_;
 
+    // Thunk to call malloc/free.
+    JitCode *mallocStub_;
+    JitCode *freeStub_;
+
     // Thunk used by the debugger for breakpoint and step mode.
     JitCode *debugTrapHandler_;
 
     // Stub used to inline the ForkJoinGetSlice intrinsic.
     JitCode *forkJoinGetSliceStub_;
 
     // Map VMFunction addresses to the JitCode of the wrapper.
     typedef WeakCache<const VMFunction *, JitCode *> VMWrapperMap;
@@ -216,16 +220,18 @@ class JitRuntime
     JitCode *generateExceptionTailStub(JSContext *cx);
     JitCode *generateBailoutTailStub(JSContext *cx);
     JitCode *generateEnterJIT(JSContext *cx, EnterJitType type);
     JitCode *generateArgumentsRectifier(JSContext *cx, ExecutionMode mode, void **returnAddrOut);
     JitCode *generateBailoutTable(JSContext *cx, uint32_t frameClass);
     JitCode *generateBailoutHandler(JSContext *cx);
     JitCode *generateInvalidator(JSContext *cx);
     JitCode *generatePreBarrier(JSContext *cx, MIRType type);
+    JitCode *generateMallocStub(JSContext *cx);
+    JitCode *generateFreeStub(JSContext *cx);
     JitCode *generateDebugTrapHandler(JSContext *cx);
     JitCode *generateForkJoinGetSliceStub(JSContext *cx);
     JitCode *generateVMWrapper(JSContext *cx, const VMFunction &f);
 
     JSC::ExecutableAllocator *createIonAlloc(JSContext *cx);
 
   public:
     JitRuntime();
@@ -325,16 +331,24 @@ class JitRuntime
     JitCode *valuePreBarrier() const {
         return valuePreBarrier_;
     }
 
     JitCode *shapePreBarrier() const {
         return shapePreBarrier_;
     }
 
+    JitCode *mallocStub() const {
+        return mallocStub_;
+    }
+
+    JitCode *freeStub() const {
+        return freeStub_;
+    }
+
     bool ensureForkJoinGetSliceStubExists(JSContext *cx);
     JitCode *forkJoinGetSliceStub() const {
         return forkJoinGetSliceStub_;
     }
 };
 
 class JitZone
 {
--- a/js/src/jit/LIR-Common.h
+++ b/js/src/jit/LIR-Common.h
@@ -297,42 +297,16 @@ class LGoto : public LControlInstruction
          setSuccessor(0, block);
     }
 
     MBasicBlock *target() const {
         return getSuccessor(0);
     }
 };
 
-class LNewSlots : public LCallInstructionHelper<1, 0, 3>
-{
-  public:
-    LIR_HEADER(NewSlots)
-
-    LNewSlots(const LDefinition &temp1, const LDefinition &temp2, const LDefinition &temp3) {
-        setTemp(0, temp1);
-        setTemp(1, temp2);
-        setTemp(2, temp3);
-    }
-
-    const LDefinition *temp1() {
-        return getTemp(0);
-    }
-    const LDefinition *temp2() {
-        return getTemp(1);
-    }
-    const LDefinition *temp3() {
-        return getTemp(2);
-    }
-
-    MNewSlots *mir() const {
-        return mir_->toNewSlots();
-    }
-};
-
 class LNewArray : public LInstructionHelper<1, 0, 1>
 {
   public:
     LIR_HEADER(NewArray)
 
     LNewArray(const LDefinition &temp) {
         setTemp(0, temp);
     }
@@ -459,92 +433,62 @@ class LNewDeclEnvObject : public LInstru
         return getTemp(0);
     }
 
     MNewDeclEnvObject *mir() const {
         return mir_->toNewDeclEnvObject();
     }
 };
 
-// Allocates a new CallObject. The inputs are:
-//      slots: either a reg representing a HeapSlot *, or a placeholder
-//             meaning that no slots pointer is needed.
+// Allocates a new CallObject.
 //
 // This instruction generates two possible instruction sets:
 //   (1) If the call object is extensible, this is a callVM to create the
 //       call object.
 //   (2) Otherwise, an inline allocation of the call object is attempted.
 //
-class LNewCallObject : public LInstructionHelper<1, 1, 1>
+class LNewCallObject : public LInstructionHelper<1, 0, 1>
 {
   public:
     LIR_HEADER(NewCallObject)
 
-    LNewCallObject(const LAllocation &slots, const LDefinition &temp) {
-        setOperand(0, slots);
+    LNewCallObject(const LDefinition &temp) {
         setTemp(0, temp);
     }
 
     const LDefinition *temp() {
         return getTemp(0);
     }
 
-    const LAllocation *slots() {
-        return getOperand(0);
-    }
     MNewCallObject *mir() const {
         return mir_->toNewCallObject();
     }
 };
 
-class LNewCallObjectPar : public LInstructionHelper<1, 2, 2>
-{
-    LNewCallObjectPar(const LAllocation &cx, const LAllocation &slots,
-                      const LDefinition &temp1, const LDefinition &temp2)
-    {
+class LNewCallObjectPar : public LInstructionHelper<1, 1, 2>
+{
+    LNewCallObjectPar(const LAllocation &cx, const LDefinition &temp1, const LDefinition &temp2) {
         setOperand(0, cx);
-        setOperand(1, slots);
         setTemp(0, temp1);
         setTemp(1, temp2);
     }
 
 public:
     LIR_HEADER(NewCallObjectPar);
 
-    static LNewCallObjectPar *NewWithSlots(TempAllocator &alloc,
-                                           const LAllocation &cx, const LAllocation &slots,
-                                           const LDefinition &temp1, const LDefinition &temp2)
+    static LNewCallObjectPar *New(TempAllocator &alloc, const LAllocation &cx,
+                                  const LDefinition &temp1, const LDefinition &temp2)
     {
-        return new(alloc) LNewCallObjectPar(cx, slots, temp1, temp2);
-    }
-
-    static LNewCallObjectPar *NewSansSlots(TempAllocator &alloc,
-                                           const LAllocation &cx,
-                                           const LDefinition &temp1, const LDefinition &temp2)
-    {
-        LAllocation slots = LConstantIndex::Bogus();
-        return new(alloc) LNewCallObjectPar(cx, slots, temp1, temp2);
+        return new(alloc) LNewCallObjectPar(cx, temp1, temp2);
     }
 
     const LAllocation *forkJoinContext() {
         return getOperand(0);
     }
 
-    const LAllocation *slots() {
-        return getOperand(1);
-    }
-
-    const bool hasDynamicSlots() {
-        // TO INVESTIGATE: Felix tried using isRegister() method here,
-        // but for useFixed(_, CallTempN), isRegister() is false (and
-        // isUse() is true).  So for now ignore that and try to match
-        // the LConstantIndex::Bogus() generated above instead.
-        return slots() && ! slots()->isConstant();
-    }
-
     const MNewCallObjectPar *mir() const {
         return mir_->toNewCallObjectPar();
     }
 
     const LDefinition *getTemp0() {
         return getTemp(0);
     }
 
--- a/js/src/jit/LOpcodes.h
+++ b/js/src/jit/LOpcodes.h
@@ -20,17 +20,16 @@
     _(CloneLiteral)                 \
     _(Parameter)                    \
     _(Callee)                       \
     _(TableSwitch)                  \
     _(TableSwitchV)                 \
     _(Goto)                         \
     _(NewArray)                     \
     _(NewObject)                    \
-    _(NewSlots)                     \
     _(NewDeclEnvObject)             \
     _(NewCallObject)                \
     _(NewStringObject)              \
     _(NewPar)                       \
     _(NewDenseArrayPar)             \
     _(NewCallObjectPar)             \
     _(NewDerivedTypedObject)        \
     _(AbortPar)                     \
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -156,27 +156,16 @@ LIRGenerator::visitDefVar(MDefVar *ins)
 bool
 LIRGenerator::visitDefFun(MDefFun *ins)
 {
     LDefFun *lir = new(alloc()) LDefFun(useRegisterAtStart(ins->scopeChain()));
     return add(lir, ins) && assignSafepoint(lir, ins);
 }
 
 bool
-LIRGenerator::visitNewSlots(MNewSlots *ins)
-{
-    // No safepoint needed, since we don't pass a cx.
-    LNewSlots *lir = new(alloc()) LNewSlots(tempFixed(CallTempReg0), tempFixed(CallTempReg1),
-                                            tempFixed(CallTempReg2));
-    if (!assignSnapshot(lir))
-        return false;
-    return defineReturn(lir, ins);
-}
-
-bool
 LIRGenerator::visitNewArray(MNewArray *ins)
 {
     LNewArray *lir = new(alloc()) LNewArray(temp());
     return define(lir, ins) && assignSafepoint(lir, ins);
 }
 
 bool
 LIRGenerator::visitNewObject(MNewObject *ins)
@@ -190,23 +179,17 @@ LIRGenerator::visitNewDeclEnvObject(MNew
 {
     LNewDeclEnvObject *lir = new(alloc()) LNewDeclEnvObject(temp());
     return define(lir, ins) && assignSafepoint(lir, ins);
 }
 
 bool
 LIRGenerator::visitNewCallObject(MNewCallObject *ins)
 {
-    LAllocation slots;
-    if (ins->slots()->type() == MIRType_Slots)
-        slots = useRegister(ins->slots());
-    else
-        slots = LConstantIndex::Bogus();
-
-    LNewCallObject *lir = new(alloc()) LNewCallObject(slots, temp());
+    LNewCallObject *lir = new(alloc()) LNewCallObject(temp());
     if (!define(lir, ins))
         return false;
 
     if (!assignSafepoint(lir, ins))
         return false;
 
     return true;
 }
@@ -220,27 +203,17 @@ LIRGenerator::visitNewDerivedTypedObject
                                             useRegisterAtStart(ins->offset()));
     return defineReturn(lir, ins) && assignSafepoint(lir, ins);
 }
 
 bool
 LIRGenerator::visitNewCallObjectPar(MNewCallObjectPar *ins)
 {
     const LAllocation &parThreadContext = useRegister(ins->forkJoinContext());
-    const LDefinition &temp1 = temp();
-    const LDefinition &temp2 = temp();
-
-    LNewCallObjectPar *lir;
-    if (ins->slots()->type() == MIRType_Slots) {
-        const LAllocation &slots = useRegister(ins->slots());
-        lir = LNewCallObjectPar::NewWithSlots(alloc(), parThreadContext, slots, temp1, temp2);
-    } else {
-        lir = LNewCallObjectPar::NewSansSlots(alloc(), parThreadContext, temp1, temp2);
-    }
-
+    LNewCallObjectPar *lir = LNewCallObjectPar::New(alloc(), parThreadContext, temp(), temp());
     return define(lir, ins);
 }
 
 bool
 LIRGenerator::visitNewStringObject(MNewStringObject *ins)
 {
     JS_ASSERT(ins->input()->type() == MIRType_String);
 
--- a/js/src/jit/Lowering.h
+++ b/js/src/jit/Lowering.h
@@ -61,17 +61,16 @@ class LIRGenerator : public LIRGenerator
 
     // Visitor hooks are explicit, to give CPU-specific versions a chance to
     // intercept without a bunch of explicit gunk in the .cpp.
     bool visitCloneLiteral(MCloneLiteral *ins);
     bool visitParameter(MParameter *param);
     bool visitCallee(MCallee *callee);
     bool visitGoto(MGoto *ins);
     bool visitTableSwitch(MTableSwitch *tableswitch);
-    bool visitNewSlots(MNewSlots *ins);
     bool visitNewArray(MNewArray *ins);
     bool visitNewObject(MNewObject *ins);
     bool visitNewDeclEnvObject(MNewDeclEnvObject *ins);
     bool visitNewCallObject(MNewCallObject *ins);
     bool visitNewStringObject(MNewStringObject *ins);
     bool visitNewDerivedTypedObject(MNewDerivedTypedObject *ins);
     bool visitNewPar(MNewPar *ins);
     bool visitNewCallObjectPar(MNewCallObjectPar *ins);
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -8962,43 +8962,16 @@ class MPostWriteBarrier : public MBinary
     bool isConsistentFloat32Use(MUse *use) const {
         // During lowering, values that neither have object nor value MIR type
         // are ignored, thus Float32 can show up at this point without any issue.
         return use->index() == 1;
     }
 #endif
 };
 
-class MNewSlots : public MNullaryInstruction
-{
-    unsigned nslots_;
-
-    MNewSlots(unsigned nslots)
-      : nslots_(nslots)
-    {
-        setResultType(MIRType_Slots);
-    }
-
-  public:
-    INSTRUCTION_HEADER(NewSlots)
-
-    static MNewSlots *New(TempAllocator &alloc, unsigned nslots) {
-        return new(alloc) MNewSlots(nslots);
-    }
-    unsigned nslots() const {
-        return nslots_;
-    }
-    AliasSet getAliasSet() const {
-        return AliasSet::None();
-    }
-    bool possiblyCalls() const {
-        return true;
-    }
-};
-
 class MNewDeclEnvObject : public MNullaryInstruction
 {
     CompilerRootObject templateObj_;
 
     MNewDeclEnvObject(JSObject *templateObj)
       : MNullaryInstruction(),
         templateObj_(templateObj)
     {
@@ -9015,78 +8988,70 @@ class MNewDeclEnvObject : public MNullar
     JSObject *templateObj() {
         return templateObj_;
     }
     AliasSet getAliasSet() const {
         return AliasSet::None();
     }
 };
 
-class MNewCallObject : public MUnaryInstruction
+class MNewCallObject : public MNullaryInstruction
 {
     CompilerRootObject templateObj_;
     bool needsSingletonType_;
 
-    MNewCallObject(JSObject *templateObj, bool needsSingletonType, MDefinition *slots)
-      : MUnaryInstruction(slots),
+    MNewCallObject(JSObject *templateObj, bool needsSingletonType)
+      : MNullaryInstruction(),
         templateObj_(templateObj),
         needsSingletonType_(needsSingletonType)
     {
         setResultType(MIRType_Object);
     }
 
   public:
     INSTRUCTION_HEADER(NewCallObject)
 
-    static MNewCallObject *New(TempAllocator &alloc, JSObject *templateObj, bool needsSingletonType,
-                               MDefinition *slots)
-    {
-        return new(alloc) MNewCallObject(templateObj, needsSingletonType, slots);
-    }
-
-    MDefinition *slots() {
-        return getOperand(0);
-    }
+    static MNewCallObject *New(TempAllocator &alloc, JSObject *templateObj, bool needsSingletonType)
+    {
+        return new(alloc) MNewCallObject(templateObj, needsSingletonType);
+    }
+
     JSObject *templateObject() {
         return templateObj_;
     }
     bool needsSingletonType() const {
         return needsSingletonType_;
     }
     AliasSet getAliasSet() const {
         return AliasSet::None();
     }
 };
 
-class MNewCallObjectPar : public MBinaryInstruction
+class MNewCallObjectPar : public MUnaryInstruction
 {
     CompilerRootObject templateObj_;
 
-    MNewCallObjectPar(MDefinition *cx, JSObject *templateObj, MDefinition *slots)
-        : MBinaryInstruction(cx, slots),
+    MNewCallObjectPar(MDefinition *cx, JSObject *templateObj)
+        : MUnaryInstruction(cx),
           templateObj_(templateObj)
     {
         setResultType(MIRType_Object);
     }
 
   public:
     INSTRUCTION_HEADER(NewCallObjectPar);
 
     static MNewCallObjectPar *New(TempAllocator &alloc, MDefinition *cx, MNewCallObject *callObj) {
-        return new(alloc) MNewCallObjectPar(cx, callObj->templateObject(), callObj->slots());
+        return new(alloc) MNewCallObjectPar(cx, callObj->templateObject());
     }
 
     MDefinition *forkJoinContext() const {
         return getOperand(0);
     }
 
-    MDefinition *slots() const {
-        return getOperand(1);
-    }
-
     JSObject *templateObj() const {
         return templateObj_;
     }
 
     AliasSet getAliasSet() const {
         return AliasSet::None();
     }
 };
--- a/js/src/jit/MOpcodes.h
+++ b/js/src/jit/MOpcodes.h
@@ -80,17 +80,16 @@ namespace jit {
     _(GuardObject)                                                          \
     _(GuardString)                                                          \
     _(AssertRange)                                                          \
     _(ToDouble)                                                             \
     _(ToFloat32)                                                            \
     _(ToInt32)                                                              \
     _(TruncateToInt32)                                                      \
     _(ToString)                                                             \
-    _(NewSlots)                                                             \
     _(NewArray)                                                             \
     _(NewObject)                                                            \
     _(NewDeclEnvObject)                                                     \
     _(NewCallObject)                                                        \
     _(NewStringObject)                                                      \
     _(InitElem)                                                             \
     _(InitElemGetterSetter)                                                 \
     _(MutateProto)                                                          \
--- a/js/src/jit/ParallelSafetyAnalysis.cpp
+++ b/js/src/jit/ParallelSafetyAnalysis.cpp
@@ -174,17 +174,16 @@ class ParallelSafetyVisitor : public MIn
     SAFE_OP(Unbox)
     SAFE_OP(GuardObject)
     SAFE_OP(ToDouble)
     SAFE_OP(ToFloat32)
     SAFE_OP(ToInt32)
     SAFE_OP(TruncateToInt32)
     SAFE_OP(MaybeToDoubleElement)
     CUSTOM_OP(ToString)
-    SAFE_OP(NewSlots)
     CUSTOM_OP(NewArray)
     CUSTOM_OP(NewObject)
     CUSTOM_OP(NewCallObject)
     UNSAFE_OP(NewDerivedTypedObject)
     UNSAFE_OP(InitElem)
     UNSAFE_OP(InitElemGetterSetter)
     UNSAFE_OP(MutateProto)
     UNSAFE_OP(InitProp)
@@ -522,16 +521,20 @@ bool
 ParallelSafetyVisitor::visitCreateThisWithTemplate(MCreateThisWithTemplate *ins)
 {
     return replaceWithNewPar(ins, ins->templateObject());
 }
 
 bool
 ParallelSafetyVisitor::visitNewCallObject(MNewCallObject *ins)
 {
+    if (ins->templateObject()->hasDynamicSlots()) {
+        SpewMIR(ins, "call with dynamic slots");
+        return markUnsafe();
+    }
     replace(ins, MNewCallObjectPar::New(alloc(), ForkJoinContext(), ins));
     return true;
 }
 
 bool
 ParallelSafetyVisitor::visitLambda(MLambda *ins)
 {
     if (ins->info().singletonType || ins->info().useNewTypeForClone) {
@@ -636,21 +639,16 @@ ParallelSafetyVisitor::insertWriteGuard(
         break;
 
       case MIRType_Slots:
         switch (valueBeingWritten->op()) {
           case MDefinition::Op_Slots:
             object = valueBeingWritten->toSlots()->object();
             break;
 
-          case MDefinition::Op_NewSlots:
-            // Values produced by new slots will ALWAYS be
-            // thread-local.
-            return true;
-
           default:
             SpewMIR(writeInstruction, "cannot insert write guard for %s",
                     valueBeingWritten->opName());
             return markUnsafe();
         }
         break;
 
       case MIRType_Elements:
--- a/js/src/jit/VMFunctions.cpp
+++ b/js/src/jit/VMFunctions.cpp
@@ -504,36 +504,26 @@ InterruptCheck(JSContext *cx)
     // afterwards which point to the interrupt handler, the next time they are
     // taken the backedges will just be reset again.
     cx->runtime()->jitRuntime()->patchIonBackedges(cx->runtime(),
                                                    JitRuntime::BackedgeLoopHeader);
 
     return CheckForInterrupt(cx);
 }
 
-HeapSlot *
-NewSlots(JSRuntime *rt, unsigned nslots)
+void *
+MallocWrapper(JSRuntime *rt, size_t nbytes)
 {
-    JS_STATIC_ASSERT(sizeof(Value) == sizeof(HeapSlot));
-
-    Value *slots = reinterpret_cast<Value *>(rt->malloc_(nslots * sizeof(Value)));
-    if (!slots)
-        return nullptr;
-
-    for (unsigned i = 0; i < nslots; i++)
-        slots[i] = UndefinedValue();
-
-    return reinterpret_cast<HeapSlot *>(slots);
+    return rt->pod_malloc<uint8_t>(nbytes);
 }
 
 JSObject *
-NewCallObject(JSContext *cx, HandleScript script,
-              HandleShape shape, HandleTypeObject type, HeapSlot *slots)
+NewCallObject(JSContext *cx, HandleScript script, HandleShape shape, HandleTypeObject type)
 {
-    JSObject *obj = CallObject::create(cx, script, shape, type, slots);
+    JSObject *obj = CallObject::create(cx, script, shape, type);
     if (!obj)
         return nullptr;
 
 #ifdef JSGC_GENERATIONAL
     // The JIT creates call objects in the nursery, so elides barriers for
     // the initializing writes. The interpreter, however, may have allocated
     // the call object tenured, so barrier as needed before re-entering.
     if (!IsInsideNursery(cx->runtime(), obj))
--- a/js/src/jit/VMFunctions.h
+++ b/js/src/jit/VMFunctions.h
@@ -612,19 +612,19 @@ JSObject *ArrayConcatDense(JSContext *cx
 bool CharCodeAt(JSContext *cx, HandleString str, int32_t index, uint32_t *code);
 JSFlatString *StringFromCharCode(JSContext *cx, int32_t code);
 
 bool SetProperty(JSContext *cx, HandleObject obj, HandlePropertyName name, HandleValue value,
                  bool strict, jsbytecode *pc);
 
 bool InterruptCheck(JSContext *cx);
 
-HeapSlot *NewSlots(JSRuntime *rt, unsigned nslots);
+void *MallocWrapper(JSRuntime *rt, size_t nbytes);
 JSObject *NewCallObject(JSContext *cx, HandleScript script,
-                        HandleShape shape, HandleTypeObject type, HeapSlot *slots);
+                        HandleShape shape, HandleTypeObject type);
 JSObject *NewStringObject(JSContext *cx, HandleString str);
 
 bool SPSEnter(JSContext *cx, HandleScript script);
 bool SPSExit(JSContext *cx, HandleScript script);
 
 bool OperatorIn(JSContext *cx, HandleValue key, HandleObject obj, bool *out);
 bool OperatorInI(JSContext *cx, uint32_t index, HandleObject obj, bool *out);
 
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -247,18 +247,17 @@ class JSObject : public js::ObjectImpl
     /*
      * Make a non-array object with the specified initial state. This method
      * takes ownership of any extantSlots it is passed.
      */
     static inline JSObject *create(js::ExclusiveContext *cx,
                                    js::gc::AllocKind kind,
                                    js::gc::InitialHeap heap,
                                    js::HandleShape shape,
-                                   js::HandleTypeObject type,
-                                   js::HeapSlot *extantSlots = nullptr);
+                                   js::HandleTypeObject type);
 
     /* Make an array object with the specified initial state. */
     static inline js::ArrayObject *createArray(js::ExclusiveContext *cx,
                                                js::gc::AllocKind kind,
                                                js::gc::InitialHeap heap,
                                                js::HandleShape shape,
                                                js::HandleTypeObject type,
                                                uint32_t length);
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -492,51 +492,40 @@ inline bool JSObject::isVarObj()
 {
     if (is<js::DebugScopeObject>())
         return as<js::DebugScopeObject>().scope().isVarObj();
     return lastProperty()->hasObjectFlag(js::BaseShape::VAROBJ);
 }
 
 /* static */ inline JSObject *
 JSObject::create(js::ExclusiveContext *cx, js::gc::AllocKind kind, js::gc::InitialHeap heap,
-                 js::HandleShape shape, js::HandleTypeObject type,
-                 js::HeapSlot *extantSlots /* = nullptr */)
+                 js::HandleShape shape, js::HandleTypeObject type)
 {
     /*
      * Callers must use dynamicSlotsCount to size the initial slot array of the
      * object. We can't check the allocated capacity of the dynamic slots, but
      * make sure their presence is consistent with the shape.
      */
     JS_ASSERT(shape && type);
     JS_ASSERT(type->clasp() == shape->getObjectClass());
     JS_ASSERT(type->clasp() != &js::ArrayObject::class_);
     JS_ASSERT(js::gc::GetGCKindSlots(kind, type->clasp()) == shape->numFixedSlots());
     JS_ASSERT_IF(type->clasp()->flags & JSCLASS_BACKGROUND_FINALIZE, IsBackgroundFinalized(kind));
     JS_ASSERT_IF(type->clasp()->finalize, heap == js::gc::TenuredHeap);
-    JS_ASSERT_IF(extantSlots, dynamicSlotsCount(shape->numFixedSlots(), shape->slotSpan(),
-                                                type->clasp()));
 
     const js::Class *clasp = type->clasp();
-    size_t nDynamicSlots = 0;
-    if (!extantSlots)
-        nDynamicSlots = dynamicSlotsCount(shape->numFixedSlots(), shape->slotSpan(), clasp);
+    size_t nDynamicSlots = dynamicSlotsCount(shape->numFixedSlots(), shape->slotSpan(), clasp);
 
     JSObject *obj = js::NewGCObject<js::CanGC>(cx, kind, nDynamicSlots, heap);
     if (!obj)
         return nullptr;
 
     obj->shape_.init(shape);
     obj->type_.init(type);
-    if (extantSlots) {
-#ifdef JSGC_GENERATIONAL
-        if (cx->isJSContext())
-            cx->asJSContext()->runtime()->gcNursery.notifyInitialSlots(obj, extantSlots);
-#endif
-        obj->slots = extantSlots;
-    }
+    // Note: slots are created and assigned internally by NewGCObject.
     obj->elements = js::emptyObjectElements;
 
     if (clasp->hasPrivate())
         obj->privateRef(shape->numFixedSlots()) = nullptr;
 
     size_t span = shape->slotSpan();
     if (span)
         obj->initializeSlotRange(0, span);
--- a/js/src/vm/ScopeObject.cpp
+++ b/js/src/vm/ScopeObject.cpp
@@ -136,24 +136,24 @@ ScopeObject::setEnclosingScope(HandleObj
     setFixedSlot(SCOPE_CHAIN_SLOT, ObjectValue(*obj));
 }
 
 /*
  * Construct a bare-bones call object given a shape, type, and slots pointer.
  * The call object must be further initialized to be usable.
  */
 CallObject *
-CallObject::create(JSContext *cx, HandleScript script, HandleShape shape, HandleTypeObject type, HeapSlot *slots)
+CallObject::create(JSContext *cx, HandleScript script, HandleShape shape, HandleTypeObject type)
 {
     gc::AllocKind kind = gc::GetGCObjectKind(shape->numFixedSlots());
     JS_ASSERT(CanBeFinalizedInBackground(kind, &CallObject::class_));
     kind = gc::GetBackgroundAllocKind(kind);
 
     gc::InitialHeap heap = script->treatAsRunOnce() ? gc::TenuredHeap : gc::DefaultHeap;
-    JSObject *obj = JSObject::create(cx, kind, heap, shape, type, slots);
+    JSObject *obj = JSObject::create(cx, kind, heap, shape, type);
     if (!obj)
         return nullptr;
 
     if (script->treatAsRunOnce()) {
         RootedObject nobj(cx, obj);
         if (!JSObject::setSingletonType(cx, nobj))
             return nullptr;
         return &nobj->as<CallObject>();
--- a/js/src/vm/ScopeObject.h
+++ b/js/src/vm/ScopeObject.h
@@ -232,17 +232,17 @@ class CallObject : public ScopeObject
     static CallObject *
     create(JSContext *cx, HandleScript script, HandleObject enclosing, HandleFunction callee);
 
   public:
     static const Class class_;
 
     /* These functions are internal and are exposed only for JITs. */
     static CallObject *
-    create(JSContext *cx, HandleScript script, HandleShape shape, HandleTypeObject type, HeapSlot *slots);
+    create(JSContext *cx, HandleScript script, HandleShape shape, HandleTypeObject type);
 
     static CallObject *
     createTemplateObject(JSContext *cx, HandleScript script, gc::InitialHeap heap);
 
     static const uint32_t RESERVED_SLOTS = 2;
 
     static CallObject *createForFunction(JSContext *cx, HandleObject enclosing, HandleFunction callee);