Bug 1412653 - Distinguish between call stack used for outer resume points from call stacks used for resuming at one instruction. r=jandem
authorNicolas B. Pierron <nicolas.b.pierron@mozilla.com>
Fri, 08 Dec 2017 16:44:44 +0000
changeset 447568 6596b8c168f52dff267acd243ec9739f0b4bbbaa
parent 447567 f6eb9518ea7491a752f67dbe39e38ac9a9807dfa
child 447569 79b9a988f0fd3d33be9d308b7f8aa58ccf1bf216
push id8527
push userCallek@gmail.com
push dateThu, 11 Jan 2018 21:05:50 +0000
treeherdermozilla-beta@95342d212a7a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem
bugs1412653
milestone59.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 1412653 - Distinguish between call stack used for outer resume points from call stacks used for resuming at one instruction. r=jandem
js/src/jit/IonBuilder.cpp
js/src/jit/IonBuilder.h
js/src/jit/MCallOptimize.cpp
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -3745,26 +3745,26 @@ IonBuilder::inlineScriptedCall(CallInfo&
     if (callInfo.constructing()) {
         MDefinition* thisDefn = createThis(target, callInfo.fun(), callInfo.getNewTarget());
         if (!thisDefn)
             return abort(AbortReason::Alloc);
         callInfo.setThis(thisDefn);
     }
 
     // Capture formals in the outer resume point.
-    MOZ_TRY(callInfo.pushFormals(this, current));
+    MOZ_TRY(callInfo.pushCallStack(this, current));
 
     MResumePoint* outerResumePoint =
         MResumePoint::New(alloc(), current, pc, MResumePoint::Outer);
     if (!outerResumePoint)
         return abort(AbortReason::Alloc);
     current->setOuterResumePoint(outerResumePoint);
 
     // Pop formals again, except leave |fun| on stack for duration of call.
-    callInfo.popFormals(current);
+    callInfo.popCallStack(current);
     current->push(callInfo.fun());
 
     JSScript* calleeScript = target->nonLazyScript();
     BaselineInspector inspector(calleeScript);
 
     // Improve type information of |this| when not set.
     if (callInfo.constructing() &&
         !callInfo.thisArg()->resultTypeSet())
@@ -4411,17 +4411,17 @@ IonBuilder::inlineGenericFallback(JSFunc
     MBasicBlock* fallbackBlock;
     MOZ_TRY_VAR(fallbackBlock, newBlock(dispatchBlock, pc));
     graph().addBlock(fallbackBlock);
 
     // Create a new CallInfo to track modified state within this block.
     CallInfo fallbackInfo(alloc(), pc, callInfo.constructing(), callInfo.ignoresReturnValue());
     if (!fallbackInfo.init(callInfo))
         return abort(AbortReason::Alloc);
-    fallbackInfo.popFormals(fallbackBlock);
+    fallbackInfo.popCallStack(fallbackBlock);
 
     // Generate an MCall, which uses stateful |current|.
     MOZ_TRY(setCurrentAndSpecializePhis(fallbackBlock));
     MOZ_TRY(makeCall(target, fallbackInfo));
 
     // Pass return block to caller as |current|.
     return Ok();
 }
@@ -4468,17 +4468,17 @@ IonBuilder::inlineObjectGroupFallback(Ca
     dispatchBlock->add(undefined);
     dispatchBlock->rewriteAtDepth(-int(callInfo.numFormals()), undefined);
 
     // Construct a block that does nothing but remove formals from the stack.
     // This is effectively changing the entry resume point of the later fallback block.
     MBasicBlock* prepBlock;
     MOZ_TRY_VAR(prepBlock, newBlock(dispatchBlock, pc));
     graph().addBlock(prepBlock);
-    fallbackInfo.popFormals(prepBlock);
+    fallbackInfo.popCallStack(prepBlock);
 
     // Construct a block into which the MGetPropertyCache can be moved.
     // This is subtle: the pc and resume point are those of the MGetPropertyCache!
     InlinePropertyTable* propTable = cache->propTable();
     MResumePoint* priorResumePoint = propTable->takePriorResumePoint();
     MOZ_ASSERT(propTable->pc() != nullptr);
     MOZ_ASSERT(priorResumePoint != nullptr);
     MBasicBlock* getPropBlock;
@@ -4531,17 +4531,17 @@ IonBuilder::inlineCalls(CallInfo& callIn
     MOZ_ASSERT(IsIonInlinablePC(pc));
     MOZ_ASSERT(choiceSet.length() == targets.length());
     MOZ_ASSERT_IF(!maybeCache, targets.length() >= 2);
     MOZ_ASSERT_IF(maybeCache, targets.length() >= 1);
     MOZ_ASSERT_IF(maybeCache, maybeCache->value()->type() == MIRType::Object);
 
     MBasicBlock* dispatchBlock = current;
     callInfo.setImplicitlyUsedUnchecked();
-    MOZ_TRY(callInfo.pushFormals(this, dispatchBlock));
+    MOZ_TRY(callInfo.pushCallStack(this, dispatchBlock));
 
     // Patch any InlinePropertyTable to only contain functions that are
     // inlineable. The InlinePropertyTable will also be patched at the end to
     // exclude native functions that vetoed inlining.
     if (maybeCache) {
         InlinePropertyTable* propTable = maybeCache->propTable();
         propTable->trimToTargets(targets);
         if (propTable->numEntries() == 0)
@@ -4564,17 +4564,17 @@ IonBuilder::inlineCalls(CallInfo& callIn
     jsbytecode* postCall = GetNextPc(pc);
     MBasicBlock* returnBlock;
     MOZ_TRY_VAR(returnBlock, newBlock(stackDepth, postCall));
     graph().addBlock(returnBlock);
     returnBlock->setCallerResumePoint(callerResumePoint_);
 
     // Set up stack, used to manually create a post-call resume point.
     returnBlock->inheritSlots(dispatchBlock);
-    callInfo.popFormals(returnBlock);
+    callInfo.popCallStack(returnBlock);
 
     MPhi* retPhi = MPhi::New(alloc());
     returnBlock->addPhi(retPhi);
     returnBlock->push(retPhi);
 
     // Create a resume point from current stack state.
     if (!returnBlock->initEntrySlots(alloc()))
         return abort(AbortReason::Alloc);
@@ -4627,17 +4627,17 @@ IonBuilder::inlineCalls(CallInfo& callIn
         int funIndex = inlineBlock->entryResumePoint()->stackDepth() - callInfo.numFormals();
         inlineBlock->entryResumePoint()->replaceOperand(funIndex, funcDef);
         inlineBlock->rewriteSlot(funIndex, funcDef);
 
         // Create a new CallInfo to track modified state within the inline block.
         CallInfo inlineInfo(alloc(), pc, callInfo.constructing(), callInfo.ignoresReturnValue());
         if (!inlineInfo.init(callInfo))
             return abort(AbortReason::Alloc);
-        inlineInfo.popFormals(inlineBlock);
+        inlineInfo.popCallStack(inlineBlock);
         inlineInfo.setFun(funcDef);
 
         if (maybeCache) {
             // Assign the 'this' value a TypeSet specialized to the groups that
             // can generate this inlining target.
             MOZ_ASSERT(callInfo.thisArg() == maybeCache->value());
             TemporaryTypeSet* thisTypes =
                 maybeCache->propTable()->buildTypeSetForFunction(alloc(), target);
@@ -5260,16 +5260,30 @@ IonBuilder::jsop_funapplyarray(uint32_t 
     current->push(apply);
     MOZ_TRY(resumeAfter(apply));
 
     TemporaryTypeSet* types = bytecodeTypes(pc);
     return pushTypeBarrier(apply, types, BarrierKind::TypeSet);
 }
 
 AbortReasonOr<Ok>
+CallInfo::savePriorCallStack(MIRGenerator* mir, MBasicBlock* current, size_t peekDepth)
+{
+    MOZ_ASSERT(priorArgs_.empty());
+    if (!priorArgs_.reserve(peekDepth))
+        return mir->abort(AbortReason::Alloc);
+    while (peekDepth) {
+        priorArgs_.infallibleAppend(current->peek(0 - int32_t(peekDepth)));
+        peekDepth--;
+    }
+    return Ok();
+}
+
+
+AbortReasonOr<Ok>
 IonBuilder::jsop_funapplyarguments(uint32_t argc)
 {
     // Stack for JSOP_FUNAPPLY:
     // 1:      Vp
     // 2:      This
     // argc+1: JSFunction*, the 'f' in |f.call()|, in |this| position.
     // argc+2: The native 'apply' function.
 
@@ -5314,16 +5328,17 @@ IonBuilder::jsop_funapplyarguments(uint3
     // When inlining we have the arguments the function gets called with
     // and can optimize even more, by just calling the functions with the args.
     // We also try this path when doing the definite properties analysis, as we
     // can inline the apply() target and don't care about the actual arguments
     // that were passed in.
 
     CallInfo callInfo(alloc(), pc, /* constructing = */ false,
                       /* ignoresReturnValue = */ BytecodeIsPopped(pc));
+    callInfo.savePriorCallStack(this, current, 4);
 
     // Vp
     MDefinition* vp = current->pop();
     vp->setImplicitlyUsedUnchecked();
 
     // Arguments
     if (inliningDepth_) {
         if (!callInfo.setArgs(inlineCallInfo_->argv()))
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -1187,31 +1187,35 @@ class IonBuilder
 };
 
 class CallInfo
 {
     MDefinition* fun_;
     MDefinition* thisArg_;
     MDefinition* newTargetArg_;
     MDefinitionVector args_;
+    // If non-empty, this corresponds to the stack prior any implicit inlining
+    // such as before JSOP_FUNAPPLY.
+    MDefinitionVector priorArgs_;
 
     bool constructing_:1;
 
     // True if the caller does not use the return value.
     bool ignoresReturnValue_:1;
 
     bool setter_:1;
     bool apply_:1;
 
   public:
     CallInfo(TempAllocator& alloc, jsbytecode* pc, bool constructing, bool ignoresReturnValue)
       : fun_(nullptr),
         thisArg_(nullptr),
         newTargetArg_(nullptr),
         args_(alloc),
+        priorArgs_(alloc),
         constructing_(constructing),
         ignoresReturnValue_(ignoresReturnValue),
         setter_(false),
         apply_(JSOp(*pc) == JSOP_FUNAPPLY)
     { }
 
     MOZ_MUST_USE bool init(CallInfo& callInfo) {
         MOZ_ASSERT(constructing_ == callInfo.constructing());
@@ -1245,21 +1249,40 @@ class CallInfo
 
         // Get |this| and |fun|
         setThis(current->pop());
         setFun(current->pop());
 
         return true;
     }
 
-    void popFormals(MBasicBlock* current) {
+    // Before doing any pop to the stack, capture whatever flows into the
+    // instruction, such that we can restore it later.
+    AbortReasonOr<Ok> savePriorCallStack(MIRGenerator* mir, MBasicBlock* current, size_t peekDepth);
+
+    void popPriorCallStack(MBasicBlock* current) {
+        if (priorArgs_.empty())
+            popCallStack(current);
+        else
+            current->popn(priorArgs_.length());
+    }
+
+    AbortReasonOr<Ok> pushPriorCallStack(MIRGenerator* mir, MBasicBlock* current) {
+        if (priorArgs_.empty())
+            return pushCallStack(mir, current);
+        for (MDefinition* def : priorArgs_)
+            current->push(def);
+        return Ok();
+    }
+
+    void popCallStack(MBasicBlock* current) {
         current->popn(numFormals());
     }
 
-    AbortReasonOr<Ok> pushFormals(MIRGenerator* mir, MBasicBlock* current) {
+    AbortReasonOr<Ok> pushCallStack(MIRGenerator* mir, MBasicBlock* current) {
         // Ensure sufficient space in the slots: needed for inlining from FUNAPPLY.
         if (apply_) {
             uint32_t depth = current->stackDepth() + numFormals();
             if (depth > current->nslots()) {
                 if (!current->increaseSlots(depth - current->nslots()))
                     return mir->abort(AbortReason::Alloc);
             }
         }
--- a/js/src/jit/MCallOptimize.cpp
+++ b/js/src/jit/MCallOptimize.cpp
@@ -841,17 +841,17 @@ IonBuilder::inlineArrayPush(CallInfo& ca
         truncate->setRecoveredOnBailout();
 
         current->add(elements);
         current->add(length);
         current->add(truncate);
 
         // Restore the stack, such that resume points are created with the stack
         // as it was before the call.
-        MOZ_TRY(callInfo.pushFormals(this, current));
+        callInfo.pushPriorCallStack(this, current);
     }
 
     MInstruction* ins = nullptr;
     for (uint32_t i = 0; i < callInfo.argc(); i++) {
         MDefinition* value = callInfo.getArg(i);
         if (toDouble) {
             MInstruction* valueDouble = MToDouble::New(alloc(), value);
             current->add(valueDouble);
@@ -874,17 +874,17 @@ IonBuilder::inlineArrayPush(CallInfo& ca
             MOZ_TRY(resumeAt(ins, pc));
             ins->resumePoint()->addStore(alloc(), truncate, lastRp);
             lastRp = ins->resumePoint();
         }
     }
 
     if (callInfo.argc() > 1) {
         // Fix the stack to represent the state after the call execution.
-        callInfo.popFormals(current);
+        callInfo.popPriorCallStack(current);
     }
     current->push(ins);
 
     if (callInfo.argc() > 1) {
         ins = MNop::New(alloc());
         current->add(ins);
     }