Bug 891808 - IonMonkey: Improve bailing information for JSOP_FUNAPPLY and JSOP_FUNCALL. r=jandem, a=lsblakk
☠☠ backed out by b154fb80409e ☠ ☠
authorHannes Verschore <hv1989@gmail.com>
Mon, 22 Jul 2013 17:27:05 -0400
changeset 143125 6bb15bfb8887
parent 143124 55e0f601dc37
child 143126 5c46eea500a8
push id2663
push userryanvm@gmail.com
push date2013-07-22 21:30 +0000
treeherdermozilla-beta@65afba378972 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem, lsblakk
bugs891808
milestone23.0
Bug 891808 - IonMonkey: Improve bailing information for JSOP_FUNAPPLY and JSOP_FUNCALL. r=jandem, a=lsblakk
js/src/ion/BaselineBailouts.cpp
--- a/js/src/ion/BaselineBailouts.cpp
+++ b/js/src/ion/BaselineBailouts.cpp
@@ -618,19 +618,88 @@ InitFromBailout(JSContext *cx, HandleScr
     }
 
     for (uint32_t i = 0; i < script->nfixed; i++) {
         Value slot = iter.read();
         if (!builder.writeValue(slot, "FixedValue"))
             return false;
     }
 
-    IonSpew(IonSpew_BaselineBailouts, "      pushing %d expression stack slots",
-                                      (int) exprStackSlots);
-    for (uint32_t i = 0; i < exprStackSlots; i++) {
+    // Get the PC
+    jsbytecode *pc = script->code + iter.pcOffset();
+    JSOp op = JSOp(*pc);
+    bool resumeAfter = iter.resumeAfter();
+
+    // Fixup inlined JSOP_FUNCALL and JSOP_FUNAPPLY on the caller side.
+    // On the caller side this must represent like the function wasn't inlined.
+    uint32_t pushedSlots = 0;
+    AutoValueVector funapplyargs(cx);
+    if (iter.moreFrames() &&
+        (op == JSOP_FUNCALL || op == JSOP_FUNAPPLY))
+    {
+        uint32_t inlined_args = 0;
+        if (op == JSOP_FUNCALL)
+            inlined_args = 2 + GET_ARGC(pc) - 1;
+        else
+            inlined_args = 2 + blFrame->numActualArgs();
+
+        JS_ASSERT(exprStackSlots >= inlined_args);
+        pushedSlots = exprStackSlots - inlined_args;
+
+        IonSpew(IonSpew_BaselineBailouts,
+                "      pushing %u expression stack slots before fixup",
+                pushedSlots);
+        for (uint32_t i = 0; i < pushedSlots; i++) {
+            Value v = iter.read();
+            if (!builder.writeValue(v, "StackValue"))
+                return false;
+        }
+
+        if (op == JSOP_FUNCALL) {
+            // When funcall got inlined and the native js_fun_call was bypassed,
+            // the stack state is incorrect. To restore correctly it must look like
+            // js_fun_call was actually called. This means transforming the stack
+            // from |target, this, args| to |js_fun_call, target, this, args|
+            // The js_fun_call is never read, so just pushing undefined now.
+            IonSpew(IonSpew_BaselineBailouts, "      pushing undefined to fixup funcall");
+            if (!builder.writeValue(UndefinedValue(), "StackValue"))
+                return false;
+        }
+
+        if (op == JSOP_FUNAPPLY) {
+            // When funapply gets inlined we take all arguments out of the
+            // arguments array. So the stack state is incorrect. To restore
+            // correctly it must look like js_fun_apply was actually called.
+            // This means transforming the stack from |target, this, arg1, ...|
+            // to |js_fun_apply, target, this, argObject|.
+            // Since the information is never read, we can just push undefined
+            // for all values.
+            IonSpew(IonSpew_BaselineBailouts, "      pushing 4x undefined to fixup funapply");
+            if (!builder.writeValue(UndefinedValue(), "StackValue"))
+                return false;
+            if (!builder.writeValue(UndefinedValue(), "StackValue"))
+                return false;
+            if (!builder.writeValue(UndefinedValue(), "StackValue"))
+                return false;
+            if (!builder.writeValue(UndefinedValue(), "StackValue"))
+                return false;
+
+            // Save the actual arguments. They are needed on the callee side
+            // as the arguments. Else we can't recover them.
+            if (!funapplyargs.resize(inlined_args))
+                return false;
+            for (uint32_t i = 0; i < inlined_args; i++)
+                funapplyargs[i] = iter.read();
+            pushedSlots = exprStackSlots;
+        }
+    }
+
+    IonSpew(IonSpew_BaselineBailouts, "      pushing %u expression stack slots",
+                                      exprStackSlots - pushedSlots);
+    for (uint32_t i = pushedSlots; i < exprStackSlots; i++) {
         Value v;
 
         // If coming from an invalidation bailout, and this is the topmost
         // value, and a value override has been specified, don't read from the
         // iterator. Otherwise, we risk using a garbage value.
         if (!iter.moreFrames() && i == exprStackSlots - 1 && cx->runtime->hasIonReturnOverride()) {
             JS_ASSERT(invalidate);
             iter.skip();
@@ -640,21 +709,16 @@ InitFromBailout(JSContext *cx, HandleScr
             v = iter.read();
         }
         if (!builder.writeValue(v, "StackValue"))
             return false;
     }
 
     size_t endOfBaselineJSFrameStack = builder.framePushed();
 
-    // Get the PC
-    jsbytecode *pc = script->code + iter.pcOffset();
-    JSOp op = JSOp(*pc);
-    bool resumeAfter = iter.resumeAfter();
-
     // If we are resuming at a LOOPENTRY op, resume at the next op to avoid
     // a bailout -> enter Ion -> bailout loop with --ion-eager. See also
     // ThunkToInterpreter.
     if (!resumeAfter) {
         while (true) {
             op = JSOp(*pc);
             if (op == JSOP_GOTO)
                 pc += GET_JUMP_OFFSET(pc);
@@ -882,25 +946,42 @@ InitFromBailout(JSContext *cx, HandleScr
     if (!builder.writePtr(prevFramePtr, "PrevFramePtr"))
         return false;
     prevFramePtr = builder.virtualPointerAtStackOffset(0);
 
     // Write out actual arguments (and thisv), copied from unpacked stack of BaselineJS frame.
     // Arguments are reversed on the BaselineJS frame's stack values.
     JS_ASSERT(isCall);
     unsigned actualArgc = GET_ARGC(pc);
-    if (op == JSOP_FUNAPPLY)
+    if (op == JSOP_FUNAPPLY) {
+        // For FUNAPPLY the arguments are not on the stack anymore,
+        // but they are copied in a vector and are written here.
         actualArgc = blFrame->numActualArgs();
 
-    JS_ASSERT(actualArgc + 2 <= exprStackSlots);
-    for (unsigned i = 0; i < actualArgc + 1; i++) {
-        size_t argSlot = (script->nfixed + exprStackSlots) - (i + 1);
-        if (!builder.writeValue(*blFrame->valueSlot(argSlot), "ArgVal"))
-            return false;
+        JS_ASSERT(actualArgc + 2 <= exprStackSlots);
+        JS_ASSERT(funapplyargs.length() == actualArgc + 2);
+        for (unsigned i = 0; i < actualArgc + 1; i++) {
+            size_t arg = funapplyargs.length() - (i + 1);
+            if (!builder.writeValue(funapplyargs[arg], "ArgVal"))
+                return false;
+        }
+    } else {
+        if (op == JSOP_FUNCALL) {
+            JS_ASSERT(actualArgc > 0);
+            actualArgc--;
+        }
+
+        JS_ASSERT(actualArgc + 2 <= exprStackSlots);
+        for (unsigned i = 0; i < actualArgc + 1; i++) {
+            size_t argSlot = (script->nfixed + exprStackSlots) - (i + 1);
+            if (!builder.writeValue(*blFrame->valueSlot(argSlot), "ArgVal"))
+                return false;
+        }
     }
+
     // In case these arguments need to be copied on the stack again for a rectifier frame,
     // save the framePushed values here for later use.
     size_t endOfBaselineStubArgs = builder.framePushed();
 
     // Calculate frame size for descriptor.
     size_t baselineStubFrameSize = builder.framePushed() - startOfBaselineStubFrame;
     size_t baselineStubFrameDescr = MakeFrameDescriptor((uint32_t) baselineStubFrameSize,
                                                         IonFrame_BaselineStub);