Bug 813773: Enable IM to IM fastpath for constructing calls, r=nbp,sstangl
☠☠ backed out by 717fb7369ef8 ☠ ☠
authorHannes Verschore <hv1989@gmail.com>
Tue, 27 Nov 2012 22:03:37 +0100
changeset 115447 82e13120f8b9bacad81ac2418a4024dc1525f520
parent 115446 f1a9592618ec97ee953e8f5c4ee664fc598cb203
child 115448 717fb7369ef88fd5f24983392402b4cab39ff46d
push id19346
push userhv1989@gmail.com
push dateSun, 09 Dec 2012 21:57:26 +0000
treeherdermozilla-inbound@82e13120f8b9 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnbp, sstangl
bugs813773
milestone20.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 813773: Enable IM to IM fastpath for constructing calls, r=nbp,sstangl
js/src/ion/CodeGenerator.cpp
js/src/ion/CodeGenerator.h
js/src/ion/IonBuilder.cpp
js/src/ion/LIR-Common.h
js/src/ion/LOpcodes.h
js/src/ion/Lowering.cpp
js/src/ion/VMFunctions.cpp
js/src/ion/VMFunctions.h
--- a/js/src/ion/CodeGenerator.cpp
+++ b/js/src/ion/CodeGenerator.cpp
@@ -828,18 +828,16 @@ CodeGenerator::visitCallGeneric(LCallGen
     Register calleereg = ToRegister(call->getFunction());
     Register objreg    = ToRegister(call->getTempObject());
     Register nargsreg  = ToRegister(call->getNargsReg());
     uint32_t unusedStack = StackOffsetOfPassedArg(call->argslot());
     Label invoke, thunk, makeCall, end;
 
     // Known-target case is handled by LCallKnown.
     JS_ASSERT(!call->hasSingleTarget());
-    // Unknown constructor case is handled by LCallConstructor.
-    JS_ASSERT(!call->mir()->isConstructing());
 
     // Generate an ArgumentsRectifier.
     IonCompartment *ion = gen->ionCompartment();
     IonCode *argumentsRectifier = ion->getArgumentsRectifier();
 
     masm.checkStackAlignment();
 
     // Guard that calleereg is actually a function object.
@@ -900,16 +898,26 @@ CodeGenerator::visitCallGeneric(LCallGen
     masm.jump(&end);
 
     // Handle uncompiled or native functions.
     masm.bind(&invoke);
     if (!emitCallInvokeFunction(call, calleereg, call->numActualArgs(), unusedStack))
         return false;
 
     masm.bind(&end);
+
+    // If the return value of the constructing function is Primitive,
+    // replace the return value with the Object from CreateThis.
+    if (call->mir()->isConstructing()) {
+        Label notPrimitive;
+        masm.branchTestPrimitive(Assembler::NotEqual, JSReturnOperand, &notPrimitive);
+        masm.loadValue(Address(StackPointer, unusedStack), JSReturnOperand);
+        masm.bind(&notPrimitive);
+    }
+
     dropArguments(call->numStackArgs() + 1);
     return true;
 }
 
 bool
 CodeGenerator::visitCallKnown(LCallKnown *call)
 {
     JSContext *cx = GetIonContext()->cx;
@@ -990,49 +998,16 @@ CodeGenerator::visitCallKnown(LCallKnown
         masm.loadValue(Address(StackPointer, unusedStack), JSReturnOperand);
         masm.bind(&notPrimitive);
     }
 
     dropArguments(call->numStackArgs() + 1);
     return true;
 }
 
-typedef bool (*InvokeConstructorFn)(JSContext *, JSObject *, uint32_t, Value *, Value *);
-static const VMFunction InvokeConstructorInfo =
-    FunctionInfo<InvokeConstructorFn>(ion::InvokeConstructor);
-
-bool
-CodeGenerator::visitCallConstructor(LCallConstructor *call)
-{
-    JS_ASSERT(call->mir()->isConstructing());
-
-    // Holds the function object.
-    const LAllocation *callee = call->getFunction();
-    Register calleereg = ToRegister(callee);
-
-    uint32_t callargslot = call->argslot();
-    uint32_t unusedStack = StackOffsetOfPassedArg(callargslot);
-
-    // Nestle %esp up to the argument vector.
-    masm.freeStack(unusedStack);
-
-    pushArg(StackPointer);                  // argv.
-    pushArg(Imm32(call->numActualArgs()));  // argc.
-    pushArg(calleereg);                     // JSFunction *.
-
-    if (!callVM(InvokeConstructorInfo, call))
-        return false;
-
-    // Un-nestle %esp from the argument vector. No prefix was pushed.
-    masm.reserveStack(unusedStack);
-
-    dropArguments(call->numStackArgs() + 1);
-    return true;
-}
-
 bool
 CodeGenerator::emitCallInvokeFunction(LApplyArgsGeneric *apply, Register extraStackSize)
 {
     Register objreg = ToRegister(apply->getTempObject());
     JS_ASSERT(objreg != extraStackSize);
 
     // Push the space used by the arguments.
     masm.movePtr(StackPointer, objreg);
@@ -1897,16 +1872,20 @@ CodeGenerator::visitCreateThis(LCreateTh
     if (callee->isConstant())
         pushArg(ImmGCPtr(&callee->toConstant()->toObject()));
     else
         pushArg(ToRegister(callee));
 
     if (!callVM(CreateThisInfo, lir))
         return false;
 
+    // Box result of VMCall when return type is Value.
+    if (lir->mir()->type() == MIRType_Value)
+        masm.tagValue(JSVAL_TYPE_OBJECT, ReturnReg, GetValueOutput(lir));
+
     masm.bind(&done);
 
     return true;
 }
 
 bool
 CodeGenerator::visitReturnFromCtor(LReturnFromCtor *lir)
 {
--- a/js/src/ion/CodeGenerator.h
+++ b/js/src/ion/CodeGenerator.h
@@ -74,17 +74,16 @@ class CodeGenerator : public CodeGenerat
     bool visitElements(LElements *lir);
     bool visitTypeBarrier(LTypeBarrier *lir);
     bool visitMonitorTypes(LMonitorTypes *lir);
     bool visitCallNative(LCallNative *call);
     bool emitCallInvokeFunction(LInstruction *call, Register callereg,
                                 uint32_t argc, uint32_t unusedStack);
     bool visitCallGeneric(LCallGeneric *call);
     bool visitCallKnown(LCallKnown *call);
-    bool visitCallConstructor(LCallConstructor *call);
     bool emitCallInvokeFunction(LApplyArgsGeneric *apply, Register extraStackSize);
     void emitPushArguments(LApplyArgsGeneric *apply, Register extraStackSpace);
     void emitPopArguments(LApplyArgsGeneric *apply, Register extraStackSize);
     bool visitApplyArgsGeneric(LApplyArgsGeneric *apply);
     bool visitDoubleToInt32(LDoubleToInt32 *lir);
     bool visitNewSlots(LNewSlots *lir);
     bool visitNewArrayCallVM(LNewArray *lir);
     bool visitNewArray(LNewArray *lir);
--- a/js/src/ion/IonBuilder.cpp
+++ b/js/src/ion/IonBuilder.cpp
@@ -3909,32 +3909,45 @@ IonBuilder::createThisScriptedSingleton(
     current->add(createThis);
 
     return createThis;
 }
 
 MDefinition *
 IonBuilder::createThis(HandleFunction target, MDefinition *callee)
 {
+    // Create this for unknown target
+    if (!target)
+        return createThisScripted(callee);
+
+    // Create this for native function
     if (target->isNative()) {
         if (!target->isNativeConstructor())
             return NULL;
         return createThisNative();
     }
 
-    MDefinition *createThis = NULL;
+    // Create this with known prototype.
     RootedObject proto(cx, getSingletonPrototype(target));
 
     // Try baking in the prototype.
-    if (proto)
-        createThis = createThisScriptedSingleton(target, proto, callee);
-
-    // If the prototype could not be hardcoded, emit a GETPROP.
+    if (proto) {
+        MDefinition *createThis = createThisScriptedSingleton(target, proto, callee);
+        if (createThis)
+            return createThis;
+    }
+
+    MDefinition *createThis = createThisScripted(callee);
     if (!createThis)
-        createThis = createThisScripted(callee);
+        return NULL;
+
+    // The native function case is already handled upfront.
+    // Here we can safely remove the native check for MCreateThis.
+    JS_ASSERT(createThis->isCreateThis());
+    createThis->toCreateThis()->removeNativeCheck();
 
     return createThis;
 }
 
 bool
 IonBuilder::jsop_funcall(uint32_t argc)
 {
     // Stack for JSOP_FUNCALL:
@@ -4127,18 +4140,18 @@ IonBuilder::makeCallHelper(HandleFunctio
     // potentially perform rearrangement.
     MPrepareCall *start = new MPrepareCall;
     MPassArg *firstArg = current->peek(-1)->toPassArg();
     firstArg->block()->insertBefore(firstArg, start);
     call->initPrepareCall(start);
 
     MPassArg *thisArg = current->pop()->toPassArg();
 
-    // If the target is known, inline the constructor on the caller-side.
-    if (constructing && target) {
+    // Inline the constructor on the caller-side.
+    if (constructing) {
         MDefinition *callee = current->peek(-1);
         MDefinition *create = createThis(target, callee);
         if (!create) {
             abort("Failure inlining constructor for call.");
             return NULL;
         }
 
         MPassArg *newThis = MPassArg::New(create);
--- a/js/src/ion/LIR-Common.h
+++ b/js/src/ion/LIR-Common.h
@@ -676,34 +676,16 @@ class LCallDOMNative : public LJSCallIns
     const LAllocation *getArgArgc() {
         return getTemp(3)->output();
     }
     const LAllocation *getArgVp() {
         return getTemp(4)->output();
     }
 };
 
-// Generates a polymorphic callsite for |new|, where |this| has not been
-// pre-allocated by the caller.
-class LCallConstructor : public LJSCallInstructionHelper<BOX_PIECES, 1, 0>
-{
-  public:
-    LIR_HEADER(CallConstructor)
-
-    LCallConstructor(const LAllocation &func, uint32_t argslot)
-      : JSCallHelper(argslot)
-    {
-        setOperand(0, func);
-    }
-
-    const LAllocation *getFunction() {
-        return getOperand(0);
-    }
-};
-
 template <size_t defs, size_t ops>
 class LDOMPropertyInstructionHelper : public LCallInstructionHelper<defs, 1 + ops, 3>
 {
   protected:
     LDOMPropertyInstructionHelper(const LDefinition &JSContextReg, const LAllocation &ObjectReg,
                                   const LDefinition &PrivReg, const LDefinition &ValueReg)
     {
         this->setOperand(0, ObjectReg);
--- a/js/src/ion/LOpcodes.h
+++ b/js/src/ion/LOpcodes.h
@@ -30,17 +30,16 @@
     _(NewStringObject)              \
     _(InitProp)                     \
     _(CheckOverRecursed)            \
     _(RecompileCheck)               \
     _(DefVar)                       \
     _(CallKnown)                    \
     _(CallGeneric)                  \
     _(CallNative)                   \
-    _(CallConstructor)              \
     _(ApplyArgsGeneric)             \
     _(StackArgT)                    \
     _(StackArgV)                    \
     _(CreateThis)                   \
     _(CreateThisWithTemplate)       \
     _(ReturnFromCtor)               \
     _(BitNotI)                      \
     _(BitNotV)                      \
--- a/js/src/ion/Lowering.cpp
+++ b/js/src/ion/Lowering.cpp
@@ -299,23 +299,16 @@ LIRGenerator::visitCall(MCall *call)
             return (defineReturn(lir, call) && assignSafepoint(lir, call));
         }
 
         LCallKnown *lir = new LCallKnown(useFixed(call->getFunction(), CallTempReg0),
                                          argslot, tempFixed(CallTempReg2));
         return (defineReturn(lir, call) && assignSafepoint(lir, call));
     }
 
-    // Call unknown constructors.
-    if (call->isConstructing()) {
-        LCallConstructor *lir = new LCallConstructor(useFixed(call->getFunction(),
-                                                     CallTempReg0), argslot);
-        return (defineReturn(lir, call) && assignSafepoint(lir, call));
-    }
-
     // Call anything, using the most generic code.
     LCallGeneric *lir = new LCallGeneric(useFixed(call->getFunction(), CallTempReg0),
         argslot, tempFixed(ArgumentsRectifierReg), tempFixed(CallTempReg2));
     return (assignSnapshot(lir) && defineReturn(lir, call) && assignSafepoint(lir, call));
 }
 
 bool
 LIRGenerator::visitApplyArgs(MApplyArgs *apply)
--- a/js/src/ion/VMFunctions.cpp
+++ b/js/src/ion/VMFunctions.cpp
@@ -88,45 +88,16 @@ InvokeFunction(JSContext *cx, JSFunction
     // Run the function in the interpreter.
     bool ok = Invoke(cx, thisv, fval, argc, argvWithoutThis, rval);
     if (ok && needsMonitor)
         types::TypeScript::Monitor(cx, *rval);
 
     return ok;
 }
 
-bool
-InvokeConstructor(JSContext *cx, JSObject *obj, uint32_t argc, Value *argv, Value *rval)
-{
-    Value fval = ObjectValue(*obj);
-
-    // See the comment in InvokeFunction.
-    bool needsMonitor;
-
-    if (obj->isFunction()) {
-        if (obj->toFunction()->isInterpretedLazy() &&
-            !obj->toFunction()->getOrCreateScript(cx))
-        {
-            return false;
-        }
-        needsMonitor = ShouldMonitorReturnType(obj->toFunction());
-    } else {
-        needsMonitor = true;
-    }
-
-    // Data in the argument vector is arranged for a JIT -> JIT call.
-    Value *argvWithoutThis = argv + 1;
-
-    bool ok = js::InvokeConstructor(cx, fval, argc, argvWithoutThis, rval);
-    if (ok && needsMonitor)
-        types::TypeScript::Monitor(cx, *rval);
-
-    return ok;
-}
-
 JSObject *
 NewGCThing(JSContext *cx, gc::AllocKind allocKind, size_t thingSize)
 {
     return gc::NewGCThing<JSObject>(cx, allocKind, thingSize);
 }
 
 bool
 CheckOverRecursed(JSContext *cx)
--- a/js/src/ion/VMFunctions.h
+++ b/js/src/ion/VMFunctions.h
@@ -406,17 +406,16 @@ class AutoDetectInvalidation
 
     ~AutoDetectInvalidation() {
         if (!disabled_ && ionScript_->invalidated())
             cx_->runtime->setIonReturnOverride(*rval_);
     }
 };
 
 bool InvokeFunction(JSContext *cx, JSFunction *fun, uint32_t argc, Value *argv, Value *rval);
-bool InvokeConstructor(JSContext *cx, JSObject *obj, uint32_t argc, Value *argv, Value *rval);
 JSObject *NewGCThing(JSContext *cx, gc::AllocKind allocKind, size_t thingSize);
 
 bool CheckOverRecursed(JSContext *cx);
 
 bool DefVarOrConst(JSContext *cx, HandlePropertyName dn, unsigned attrs, HandleObject scopeChain);
 bool InitProp(JSContext *cx, HandleObject obj, HandlePropertyName name, HandleValue value);
 
 template<bool Equal>