[INFER] Inline creation of objects for {}, [], Array() and monomorphic scripted new, bug 677006.
authorBrian Hackett <bhackett1024@gmail.com>
Tue, 09 Aug 2011 09:26:08 -0700
changeset 76102 05261f44a8ac1016905fe922f1a347c92e6c77ea
parent 76101 3b40e4462464f75fed8ea71f839de5f2a2ef7108
child 76103 bde71d2d88fbf0eebd289c847395415fb5b4bb84
push id3
push userfelipc@gmail.com
push dateFri, 30 Sep 2011 20:09:13 +0000
bugs677006
milestone8.0a1
[INFER] Inline creation of objects for {}, [], Array() and monomorphic scripted new, bug 677006.
js/src/jsinfer.cpp
js/src/methodjit/BaseAssembler.h
js/src/methodjit/Compiler.cpp
js/src/methodjit/Compiler.h
js/src/methodjit/FastBuiltins.cpp
js/src/methodjit/FrameState-inl.h
js/src/methodjit/FrameState.h
js/src/methodjit/StubCalls.cpp
js/src/methodjit/StubCalls.h
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -1080,25 +1080,40 @@ TypeConstraintCall::newType(JSContext *c
              * specialized natives which operate on properties. :XXX: use
              * better factoring for both this and the compiler code itself
              * which specializes particular natives.
              */
 
             Native native = obj->getFunctionPrivate()->native();
 
             if (native == js::array_push) {
-                for (size_t ind = 0; ind < callsite->argumentCount; ind++) {
+                for (size_t i = 0; i < callsite->argumentCount; i++) {
                     callsite->thisTypes->addSetProperty(cx, script, pc,
-                                                        callsite->argumentTypes[ind], JSID_VOID);
+                                                        callsite->argumentTypes[i], JSID_VOID);
                 }
             }
 
             if (native == js::array_pop)
                 callsite->thisTypes->addGetProperty(cx, script, pc, callsite->returnTypes, JSID_VOID);
 
+            if (native == js_Array) {
+                TypeObject *res = TypeScript::InitObject(cx, script, pc, JSProto_Array);
+                if (!res)
+                    return;
+
+                callsite->returnTypes->addType(cx, Type::ObjectType(res));
+
+                if (callsite->argumentCount >= 2) {
+                    for (unsigned i = 0; i < callsite->argumentCount; i++) {
+                        PropertyAccess(cx, script, pc, res, true,
+                                       callsite->argumentTypes[i], JSID_VOID);
+                    }
+                }
+            }
+
             return;
         }
 
         callee = obj->getFunctionPrivate()->script();
     } else if (type.isTypeObject()) {
         callee = type.typeObject()->functionScript;
         if (!callee)
             return;
--- a/js/src/methodjit/BaseAssembler.h
+++ b/js/src/methodjit/BaseAssembler.h
@@ -1206,16 +1206,92 @@ static const JSC::MacroAssembler::Regist
             return false;
 
         for (unsigned i = 0; i < matches.length(); i++)
             matches[i].linkTo(label(), this);
 
         return true;
     }
 
+    /*
+     * Get a free object for the specified GC kind in compartment, writing it
+     * to result and filling it in according to templateObject. Returns a jump
+     * taken if a free thing was not retrieved.
+     */
+    Jump getNewObject(JSContext *cx, RegisterID result, JSObject *templateObject)
+    {
+        unsigned thingKind = templateObject->arenaHeader()->getThingKind();
+
+        JS_ASSERT(thingKind >= gc::FINALIZE_OBJECT0 && thingKind <= gc::FINALIZE_OBJECT_LAST);
+        size_t thingSize = gc::GCThingSizeMap[thingKind];
+
+        JS_ASSERT(cx->typeInferenceEnabled());
+        JS_ASSERT(!templateObject->hasSlotsArray());
+
+#ifdef JS_GC_ZEAL
+        if (cx->runtime->needZealousGC())
+            return jump();
+#endif
+
+        /*
+         * Inline FreeLists::getNext. Only the case where the current freelist
+         * span is not empty is handled.
+         */
+        gc::FreeSpan *list = &cx->compartment->freeLists.lists[thingKind];
+
+        loadPtr(&list->start, result);
+        Jump jump = branchPtr(Assembler::Equal, AbsoluteAddress(&list->end), result);
+
+        addPtr(Imm32(thingSize), result);
+        storePtr(result, &list->start);
+
+        /*
+         * Fill in the blank object. Order doesn't matter here, from here
+         * everything is infallible. Note that this bakes GC thing pointers
+         * into the code without explicitly pinning them. With type inference
+         * enabled, JIT code is collected on GC except when analysis or
+         * compilation is active, in which case type objects won't be collected
+         * but other things may be. The shape held by templateObject *must* be
+         * pinned against GC either by the script or by some type object.
+         */
+
+        /*
+         * Write out the slots pointer before readjusting the result register,
+         * as for dense arrays this is relative to the JSObject itself.
+         */
+        if (templateObject->isDenseArray()) {
+            JS_ASSERT(!templateObject->initializedLength);
+            addPtr(Imm32(-thingSize + sizeof(JSObject)), result);
+            storePtr(result, Address(result, -sizeof(JSObject) + JSObject::offsetOfSlots()));
+            addPtr(Imm32(-sizeof(JSObject)), result);
+        } else {
+            JS_ASSERT(!templateObject->newType);
+            addPtr(Imm32(-thingSize), result);
+            storePtr(ImmPtr(NULL), Address(result, JSObject::offsetOfSlots()));
+        }
+
+        storePtr(ImmPtr(templateObject->lastProp), Address(result, offsetof(JSObject, lastProp)));
+        storePtr(ImmPtr(templateObject->clasp), Address(result, offsetof(JSObject, clasp)));
+        store32(Imm32(templateObject->flags), Address(result, offsetof(JSObject, flags)));
+        store32(Imm32(templateObject->objShape), Address(result, offsetof(JSObject, objShape)));
+        storePtr(ImmPtr(templateObject->newType), Address(result, offsetof(JSObject, newType)));
+        storePtr(ImmPtr(templateObject->parent), Address(result, offsetof(JSObject, parent)));
+        storePtr(ImmPtr(templateObject->privateData), Address(result, offsetof(JSObject, privateData)));
+        storePtr(ImmPtr((void *) templateObject->capacity), Address(result, offsetof(JSObject, capacity)));
+        storePtr(ImmPtr(templateObject->gctype()), Address(result, JSObject::offsetOfType()));
+
+        /* Fixed slots of non-array objects are required to be initialized. */
+        if (!templateObject->isDenseArray()) {
+            for (unsigned i = 0; i < templateObject->numFixedSlots(); i++)
+                storeValue(UndefinedValue(), Address(result, JSObject::getFixedSlotOffset(i)));
+        }
+
+        return jump;
+    }
+
     /* Add the value stored in 'value' to the accumulator 'counter'. */
     void addCounter(const double *value, double *counter, RegisterID scratch)
     {
         loadDouble(value, Registers::FPConversionTemp);
         move(ImmPtr(counter), scratch);
         addDouble(Address(scratch), Registers::FPConversionTemp);
         storeDouble(Registers::FPConversionTemp, Address(scratch));
     }
--- a/js/src/methodjit/Compiler.cpp
+++ b/js/src/methodjit/Compiler.cpp
@@ -780,25 +780,30 @@ mjit::Compiler::generatePrologue()
                           Address(JSFrameReg, StackFrame::offsetOfArgs()));
             hasArgs.linkTo(masm.label(), &masm);
         }
     }
 
     if (isConstructing)
         constructThis();
 
-    if (debugMode())
+    if (debugMode()) {
+        prepareStubCall(Uses(0));
         INLINE_STUBCALL(stubs::ScriptDebugPrologue, REJOIN_RESUME);
-    else if (Probes::callTrackingActive(cx))
+    } else if (Probes::callTrackingActive(cx)) {
+        prepareStubCall(Uses(0));
         INLINE_STUBCALL(stubs::ScriptProbeOnlyPrologue, REJOIN_RESUME);
+    }
 
     if (cx->typeInferenceEnabled()) {
 #ifdef DEBUG
-        if (script->hasFunction)
+        if (script->hasFunction) {
+            prepareStubCall(Uses(0));
             INLINE_STUBCALL(stubs::AssertArgumentTypes, REJOIN_NONE);
+        }
 #endif
         ensureDoubleArguments();
     }
 
     recompileCheckHelper();
 
     if (outerScript->pcCounters) {
         size_t length = ssa.frameLength(ssa.numFrames() - 1);
@@ -1966,29 +1971,33 @@ mjit::Compiler::generateMethod()
           BEGIN_CASE(JSOP_CALL)
           BEGIN_CASE(JSOP_NEW)
           BEGIN_CASE(JSOP_FUNAPPLY)
           BEGIN_CASE(JSOP_FUNCALL)
           {
             bool callingNew = (op == JSOP_NEW);
 
             bool done = false;
-            if (op == JSOP_CALL && !monitored(PC)) {
+            if ((op == JSOP_CALL || op == JSOP_NEW) && !monitored(PC)) {
                 CompileStatus status = inlineNativeFunction(GET_ARGC(PC), callingNew);
                 if (status == Compile_Okay)
                     done = true;
                 else if (status != Compile_InlineAbort)
                     return status;
             }
             if (!done && inlining()) {
                 CompileStatus status = inlineScriptedFunction(GET_ARGC(PC), callingNew);
                 if (status == Compile_Okay)
                     done = true;
                 else if (status != Compile_InlineAbort)
                     return status;
+                if (script->pcCounters) {
+                    /* Code generated while inlining has been accounted for. */
+                    updatePCCounters(PC, &codeStart, &countersUpdated);
+                }
             }
 
             FrameSize frameSize;
             frameSize.initStatic(frame.totalDepth(), GET_ARGC(PC));
 
             if (!done) {
                 JaegerSpew(JSpew_Insns, " --- SCRIPTED CALL --- \n");
                 inlineCallHelper(GET_ARGC(PC), callingNew, frameSize);
@@ -6201,37 +6210,76 @@ mjit::Compiler::jsop_newinit()
         isArray = false;
         baseobj = globalObj ? script->getObject(fullAtomIndex(PC)) : NULL;
         break;
       default:
         JS_NOT_REACHED("Bad op");
         return false;
     }
 
-    prepareStubCall(Uses(0));
+    void *stub, *stubArg;
+    if (isArray) {
+        stub = JS_FUNC_TO_DATA_PTR(void *, stubs::NewInitArray);
+        stubArg = (void *) count;
+    } else {
+        stub = JS_FUNC_TO_DATA_PTR(void *, stubs::NewInitObject);
+        stubArg = (void *) baseobj;
+    }
 
     /* Don't bake in types for non-compileAndGo scripts. */
     types::TypeObject *type = NULL;
     if (globalObj) {
         type = types::TypeScript::InitObject(cx, script, PC,
                                              isArray ? JSProto_Array : JSProto_Object);
         if (!type)
             return false;
     }
-    masm.storePtr(ImmPtr(type), FrameAddress(offsetof(VMFrame, scratch)));
-
+
+    if (!cx->typeInferenceEnabled() ||
+        !globalObj ||
+        (isArray && count >= gc::GetGCKindSlots(gc::FINALIZE_OBJECT_LAST)) ||
+        (!isArray && !baseobj) ||
+        (!isArray && baseobj->hasSlotsArray())) {
+        prepareStubCall(Uses(0));
+        masm.storePtr(ImmPtr(type), FrameAddress(offsetof(VMFrame, scratch)));
+        masm.move(ImmPtr(stubArg), Registers::ArgReg1);
+        INLINE_STUBCALL(stub, REJOIN_FALLTHROUGH);
+        frame.pushSynced(JSVAL_TYPE_OBJECT);
+
+        frame.extra(frame.peek(-1)).initArray = (*PC == JSOP_NEWARRAY);
+        frame.extra(frame.peek(-1)).initObject = baseobj;
+
+        return true;
+    }
+
+    JSObject *templateObject;
     if (isArray) {
-        masm.move(Imm32(count), Registers::ArgReg1);
-        INLINE_STUBCALL(stubs::NewInitArray, REJOIN_PUSH_OBJECT);
+        templateObject = NewDenseUnallocatedArray(cx, count);
+        if (!templateObject)
+            return false;
+        templateObject->setType(type);
     } else {
-        masm.move(ImmPtr(baseobj), Registers::ArgReg1);
-        INLINE_STUBCALL(stubs::NewInitObject, REJOIN_PUSH_OBJECT);
-    }
-    frame.takeReg(Registers::ReturnReg);
-    frame.pushTypedPayload(JSVAL_TYPE_OBJECT, Registers::ReturnReg);
+        templateObject = CopyInitializerObject(cx, baseobj, type);
+        if (!templateObject)
+            return false;
+    }
+
+    RegisterID result = frame.allocReg();
+    Jump emptyFreeList = masm.getNewObject(cx, result, templateObject);
+
+    stubcc.linkExit(emptyFreeList, Uses(0));
+    stubcc.leave();
+
+    stubcc.masm.storePtr(ImmPtr(type), FrameAddress(offsetof(VMFrame, scratch)));
+    stubcc.masm.move(ImmPtr(stubArg), Registers::ArgReg1);
+    OOL_STUBCALL(stub, REJOIN_FALLTHROUGH);
+
+    frame.pushTypedPayload(JSVAL_TYPE_OBJECT, result);
+
+    stubcc.rejoin(Changes(1));
 
     frame.extra(frame.peek(-1)).initArray = (*PC == JSOP_NEWARRAY);
     frame.extra(frame.peek(-1)).initObject = baseobj;
 
     return true;
 }
 
 bool
@@ -6595,16 +6643,40 @@ mjit::Compiler::leaveBlock()
 //       NULL
 //   call js_CreateThisFromFunctionWithProto(...)
 //
 bool
 mjit::Compiler::constructThis()
 {
     JS_ASSERT(isConstructing);
 
+    if (cx->typeInferenceEnabled()) {
+        jsid id = ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom);
+        types::TypeSet *protoTypes = script->function()->getType(cx)->getProperty(cx, id, false);
+
+        JSObject *proto = protoTypes->getSingleton(cx, true);
+        if (proto) {
+            JSObject *templateObject = js_CreateThisForFunctionWithProto(cx, script->function(), proto);
+
+            RegisterID result = frame.allocReg();
+            Jump emptyFreeList = masm.getNewObject(cx, result, templateObject);
+
+            stubcc.linkExit(emptyFreeList, Uses(0));
+            stubcc.leave();
+
+            stubcc.masm.move(ImmPtr(proto), Registers::ArgReg1);
+            OOL_STUBCALL(stubs::CreateThis, REJOIN_RESUME);
+
+            frame.setThis(result);
+
+            stubcc.rejoin(Changes(1));
+            return true;
+        }
+    }
+
     // Load the callee.
     frame.pushCallee();
 
     // Get callee.prototype.
     if (!jsop_getprop(cx->runtime->atomState.classPrototypeAtom, JSVAL_TYPE_UNKNOWN, false, false))
         return false;
 
     // Reach into the proto Value and grab a register for its data.
--- a/js/src/methodjit/Compiler.h
+++ b/js/src/methodjit/Compiler.h
@@ -750,16 +750,18 @@ class Compiler : public BaseCompiler
     CompileStatus inlineNativeFunction(uint32 argc, bool callingNew);
     CompileStatus inlineScriptedFunction(uint32 argc, bool callingNew);
     CompileStatus compileMathAbsInt(FrameEntry *arg);
     CompileStatus compileMathAbsDouble(FrameEntry *arg);
     CompileStatus compileMathSqrt(FrameEntry *arg);
     CompileStatus compileMathPowSimple(FrameEntry *arg1, FrameEntry *arg2);
     CompileStatus compileArrayPush(FrameEntry *thisv, FrameEntry *arg);
     CompileStatus compileArrayPop(FrameEntry *thisv, bool isPacked);
+    CompileStatus compileArrayWithLength(uint32 argc);
+    CompileStatus compileArrayWithArgs(uint32 argc);
 
     enum RoundingMode { Floor, Round };
     CompileStatus compileRound(FrameEntry *arg, RoundingMode mode);
 
     enum GetCharMode { GetChar, GetCharCode };
     CompileStatus compileGetChar(FrameEntry *thisValue, FrameEntry *arg, GetCharMode mode);
 
     void prepareStubCall(Uses uses);
--- a/js/src/methodjit/FastBuiltins.cpp
+++ b/js/src/methodjit/FastBuiltins.cpp
@@ -434,20 +434,106 @@ mjit::Compiler::compileArrayPop(FrameEnt
         frame.push(UndefinedValue());
     }
 
     stubcc.rejoin(Changes(1));
     return Compile_Okay;
 }
 
 CompileStatus
+mjit::Compiler::compileArrayWithLength(uint32 argc)
+{
+    /* Match Array() or Array(n) for constant n. */
+    JS_ASSERT(argc == 0 || argc == 1);
+
+    int32 length = 0;
+    if (argc == 1) {
+        FrameEntry *arg = frame.peek(-1);
+        if (!arg->isConstant() || !arg->getValue().isInt32())
+            return Compile_InlineAbort;
+        length = arg->getValue().toInt32();
+        if (length < 0)
+            return Compile_InlineAbort;
+    }
+
+    types::TypeObject *type = types::TypeScript::InitObject(cx, script, PC, JSProto_Array);
+    if (!type)
+        return Compile_Error;
+
+    JSObject *templateObject = NewDenseUnallocatedArray(cx, length, type->proto);
+    if (!templateObject)
+        return Compile_Error;
+    templateObject->setType(type);
+
+    RegisterID result = frame.allocReg();
+    Jump emptyFreeList = masm.getNewObject(cx, result, templateObject);
+
+    stubcc.linkExit(emptyFreeList, Uses(0));
+    stubcc.leave();
+
+    stubcc.masm.move(Imm32(argc), Registers::ArgReg1);
+    OOL_STUBCALL(stubs::SlowCall, REJOIN_FALLTHROUGH);
+
+    frame.popn(argc + 2);
+    frame.pushTypedPayload(JSVAL_TYPE_OBJECT, result);
+
+    stubcc.rejoin(Changes(1));
+    return Compile_Okay;
+}
+
+CompileStatus
+mjit::Compiler::compileArrayWithArgs(uint32 argc)
+{
+    /*
+     * Match Array(x, y, z) with at least two arguments. Don't inline the case
+     * where a non-number argument is passed, so we don't need to care about
+     * the types of the arguments.
+     */
+    JS_ASSERT(argc >= 2);
+
+    if (argc >= gc::GetGCKindSlots(gc::FINALIZE_OBJECT_LAST))
+        return Compile_InlineAbort;
+
+    types::TypeObject *type = types::TypeScript::InitObject(cx, script, PC, JSProto_Array);
+    if (!type)
+        return Compile_Error;
+
+    JSObject *templateObject = NewDenseUnallocatedArray(cx, argc, type->proto);
+    if (!templateObject)
+        return Compile_Error;
+    templateObject->setType(type);
+
+    JS_ASSERT(templateObject->getDenseArrayCapacity() >= argc);
+
+    RegisterID result = frame.allocReg();
+    Jump emptyFreeList = masm.getNewObject(cx, result, templateObject);
+    stubcc.linkExit(emptyFreeList, Uses(0));
+
+    for (unsigned i = 0; i < argc; i++) {
+        FrameEntry *arg = frame.peek(-argc + i);
+        frame.storeTo(arg, Address(result, JSObject::getFixedSlotOffset(i)), /* popped = */ true);
+    }
+
+    masm.storePtr(ImmPtr((void *) argc), Address(result, offsetof(JSObject, initializedLength)));
+
+    stubcc.leave();
+
+    stubcc.masm.move(Imm32(argc), Registers::ArgReg1);
+    OOL_STUBCALL(stubs::SlowCall, REJOIN_FALLTHROUGH);
+
+    frame.popn(argc + 2);
+    frame.pushTypedPayload(JSVAL_TYPE_OBJECT, result);
+
+    stubcc.rejoin(Changes(1));
+    return Compile_Okay;
+}
+
+CompileStatus
 mjit::Compiler::inlineNativeFunction(uint32 argc, bool callingNew)
 {
-    JS_ASSERT(!callingNew);
-
     if (!cx->typeInferenceEnabled())
         return Compile_InlineAbort;
 
     if (applyTricks == LazyArgsObj)
         return Compile_InlineAbort;
 
     FrameEntry *origCallee = frame.peek(-((int)argc + 2));
     FrameEntry *thisValue = frame.peek(-((int)argc + 1));
@@ -473,16 +559,33 @@ mjit::Compiler::inlineNativeFunction(uin
     if (!native)
         return Compile_InlineAbort;
 
     JSValueType type = knownPushedType(0);
     JSValueType thisType = thisValue->isTypeKnown()
                            ? thisValue->getKnownType()
                            : JSVAL_TYPE_UNKNOWN;
 
+    /*
+     * Note: when adding new natives which operate on properties, add relevant
+     * constraint generation to the behavior of TypeConstraintCall.
+     */
+
+    /* Handle natives that can be called either with or without 'new'. */
+
+    if (native == js_Array && type == JSVAL_TYPE_OBJECT && globalObj) {
+        if (argc == 0 || argc == 1)
+            return compileArrayWithLength(argc);
+        return compileArrayWithArgs(argc);
+    }
+
+    /* Remaining natives must not be called with 'new'. */
+    if (callingNew)
+        return Compile_InlineAbort;
+
     if (argc == 0) {
         if (native == js::array_pop && thisType == JSVAL_TYPE_OBJECT) {
             /*
              * Only inline pop() on dense arrays which have never been used in
              * an iterator --- when popping elements we don't account for
              * suppressing deleted properties in active iterators.
              *
              * Constraints propagating properties directly into the result
--- a/js/src/methodjit/FrameState-inl.h
+++ b/js/src/methodjit/FrameState-inl.h
@@ -1311,21 +1311,33 @@ FrameState::pushThis()
 
 void
 FrameState::learnThisIsObject(bool unsync)
 {
     // If the 'this' object is a copy, this must be an inline frame, in which
     // case we will trigger recompilation if the 'this' entry isn't actually
     // an object (thus, it is OK to modify the backing directly).
     FrameEntry *fe = a->this_;
+    if (!fe->isTracked())
+        addToTracker(fe);
     if (fe->isCopy())
         fe = fe->copyOf();
     learnType(fe, JSVAL_TYPE_OBJECT, unsync);
 }
 
+void
+FrameState::setThis(RegisterID reg)
+{
+    FrameEntry *fe = a->this_;
+    if (!fe->isTracked())
+        addToTracker(fe);
+    JS_ASSERT(!fe->isCopy());
+    learnType(fe, JSVAL_TYPE_OBJECT, reg);
+}
+
 inline void
 FrameState::leaveBlock(uint32 n)
 {
     popn(n);
 }
 
 inline void
 FrameState::enterBlock(uint32 n)
--- a/js/src/methodjit/FrameState.h
+++ b/js/src/methodjit/FrameState.h
@@ -387,16 +387,17 @@ class FrameState
 
     // Pushes a copy of a slot (formal argument, local variable, or stack slot)
     // onto the operation stack.
     void pushLocal(uint32 n);
     void pushArg(uint32 n);
     void pushCallee();
     void pushThis();
     void pushCopyOf(FrameEntry *fe);
+    inline void setThis(RegisterID reg);
     inline void learnThisIsObject(bool unsync = true);
 
     inline FrameEntry *getStack(uint32 slot);
     inline FrameEntry *getLocal(uint32 slot);
     inline FrameEntry *getArg(uint32 slot);
     inline FrameEntry *getSlotEntry(uint32 slot);
 
     /*
--- a/js/src/methodjit/StubCalls.cpp
+++ b/js/src/methodjit/StubCalls.cpp
@@ -1313,53 +1313,53 @@ stubs::Neg(VMFrame &f)
     double d;
     if (!ToNumber(f.cx, f.regs.sp[-1], &d))
         THROW();
     d = -d;
     if (!f.regs.sp[-1].setNumber(d))
         TypeScript::MonitorOverflow(f.cx, f.script(), f.pc());
 }
 
-JSObject * JS_FASTCALL
+void JS_FASTCALL
 stubs::NewInitArray(VMFrame &f, uint32 count)
 {
     JSObject *obj = NewDenseAllocatedArray(f.cx, count);
     if (!obj)
-        THROWV(NULL);
+        THROW();
 
     TypeObject *type = (TypeObject *) f.scratch;
     if (type)
         obj->setType(type);
 
-    obj->setArrayLength(f.cx, count);
-    return obj;
+    f.regs.sp[0].setObject(*obj);
 }
 
-JSObject * JS_FASTCALL
+void JS_FASTCALL
 stubs::NewInitObject(VMFrame &f, JSObject *baseobj)
 {
     JSContext *cx = f.cx;
     TypeObject *type = (TypeObject *) f.scratch;
 
     if (!baseobj) {
         gc::FinalizeKind kind = GuessObjectGCKind(0, false);
         JSObject *obj = NewBuiltinClassInstance(cx, &js_ObjectClass, kind);
         if (!obj)
-            THROWV(NULL);
+            THROW();
         if (type)
             obj->setType(type);
-        return obj;
+        f.regs.sp[0].setObject(*obj);
+        return;
     }
 
     JS_ASSERT(type);
     JSObject *obj = CopyInitializerObject(cx, baseobj, type);
 
     if (!obj)
-        THROWV(NULL);
-    return obj;
+        THROW();
+    f.regs.sp[0].setObject(*obj);
 }
 
 void JS_FASTCALL
 stubs::InitElem(VMFrame &f, uint32 last)
 {
     JSContext *cx = f.cx;
     FrameRegs &regs = f.regs;
 
--- a/js/src/methodjit/StubCalls.h
+++ b/js/src/methodjit/StubCalls.h
@@ -49,18 +49,18 @@ namespace stubs {
 
 typedef enum JSTrapType {
     JSTRAP_NONE = 0,
     JSTRAP_TRAP = 1,
     JSTRAP_SINGLESTEP = 2
 } JSTrapType;
 
 void JS_FASTCALL This(VMFrame &f);
-JSObject * JS_FASTCALL NewInitArray(VMFrame &f, uint32 count);
-JSObject * JS_FASTCALL NewInitObject(VMFrame &f, JSObject *base);
+void JS_FASTCALL NewInitArray(VMFrame &f, uint32 count);
+void JS_FASTCALL NewInitObject(VMFrame &f, JSObject *base);
 void JS_FASTCALL Trap(VMFrame &f, uint32 trapTypes);
 void JS_FASTCALL Debugger(VMFrame &f, jsbytecode *pc);
 void JS_FASTCALL Interrupt(VMFrame &f, jsbytecode *pc);
 void JS_FASTCALL RecompileForInline(VMFrame &f);
 void JS_FASTCALL InitElem(VMFrame &f, uint32 last);
 void JS_FASTCALL InitProp(VMFrame &f, JSAtom *atom);
 void JS_FASTCALL InitMethod(VMFrame &f, JSAtom *atom);