Bug 1273858 - Support LexicalEnvironmentObjects during Ion bailout draft
authorTed Campbell <tcampbell@mozilla.com>
Thu, 16 Feb 2017 16:47:17 -0500
changeset 488104 94272a9796776b2f48b6678e9f5f48f9c1086b64
parent 482882 e1a4314f8e6eae8bbc06394c14132a9c5011371b
child 488105 95f186a30c27a9e5216f5873f16a162e6b903a63
push id46423
push userbmo:tcampbell@mozilla.com
push dateWed, 22 Feb 2017 15:41:44 +0000
bugs1273858
milestone54.0a1
Bug 1273858 - Support LexicalEnvironmentObjects during Ion bailout MozReview-Commit-ID: 8BLiJNOcrS2
js/src/jit/BaselineBailouts.cpp
js/src/jit/BaselineJIT.h
js/src/jit/IonBuilder.cpp
js/src/jit/JitFrames.cpp
--- a/js/src/jit/BaselineBailouts.cpp
+++ b/js/src/jit/BaselineBailouts.cpp
@@ -132,16 +132,18 @@ struct BaselineStackBuilder
         header_->copyStackBottom = header_->copyStackTop;
         header_->setR0 = 0;
         header_->valueR0 = UndefinedValue();
         header_->setR1 = 0;
         header_->valueR1 = UndefinedValue();
         header_->resumeFramePtr = nullptr;
         header_->resumeAddr = nullptr;
         header_->resumePC = nullptr;
+        header_->tryPC = nullptr;
+        header_->faultPC = nullptr;
         header_->monitorStub = nullptr;
         header_->numFrames = 0;
         header_->checkGlobalDeclarationConflicts = false;
         return true;
     }
 
     MOZ_MUST_USE bool enlarge() {
         MOZ_ASSERT(buffer_ != nullptr);
@@ -724,24 +726,22 @@ InitFromBailout(JSContext* cx, HandleScr
                     "      Bailout_ArgumentCheck for script with argumentsHasVarBinding!"
                     "Using empty arguments object");
             iter.skip();
         }
     } else {
         Value v = iter.read();
         if (v.isObject()) {
             envChain = &v.toObject();
-            if (fun &&
-                ((fun->needsCallObject() && envChain->is<CallObject>()) ||
-                 (fun->needsNamedLambdaEnvironment() &&
-                  !fun->needsCallObject() &&
-                  envChain->is<LexicalEnvironmentObject>() &&
-                  &envChain->as<LexicalEnvironmentObject>().scope() ==
-                  script->maybeNamedLambdaScope())))
-            {
+
+            // If Ion has updated env slot from UndefinedValue, it will be the
+            // complete initial environment, so we can set the HAS_INITIAL_ENV
+            // flag if needed.
+            if (fun && fun->needsFunctionEnvironmentObjects()) {
+                MOZ_ASSERT(fun->nonLazyScript()->initialEnvironmentShape());
                 MOZ_ASSERT(!fun->needsExtraBodyVarEnvironment());
                 flags |= BaselineFrame::HAS_INITIAL_ENV;
             }
         } else {
             MOZ_ASSERT(v.isUndefined() || v.isMagic(JS_OPTIMIZED_OUT));
 
             // Get env chain from function or script.
             if (fun) {
@@ -1817,16 +1817,18 @@ jit::FinishBailoutToBaseline(BaselineBai
 
     // The current native code pc may not have a corresponding ICEntry, so we
     // store the bytecode pc in the frame for frame iterators. This pc is
     // cleared at the end of this function. If we return false, we don't clear
     // it: the exception handler also needs it and will clear it for us.
     BaselineFrame* topFrame = GetTopBaselineFrame(cx);
     topFrame->setOverridePc(bailoutInfo->resumePC);
 
+    jsbytecode* faultPC = bailoutInfo->faultPC;
+    jsbytecode* tryPC = bailoutInfo->tryPC;
     uint32_t numFrames = bailoutInfo->numFrames;
     MOZ_ASSERT(numFrames > 0);
     BailoutKind bailoutKind = bailoutInfo->bailoutKind;
     bool checkGlobalDeclarationConflicts = bailoutInfo->checkGlobalDeclarationConflicts;
 
     // Free the bailout buffer.
     js_free(bailoutInfo);
     bailoutInfo = nullptr;
@@ -1934,16 +1936,23 @@ jit::FinishBailoutToBaseline(BaselineBai
         // After copying from all the rematerialized frames, remove them from
         // the table to keep the table up to date.
         act->removeRematerializedFrame(outerFp);
 
         if (!ok)
             return false;
     }
 
+    // If we are catching an exception, we need to unwind scopes.
+    // See |SettleOnTryNote|
+    if (cx->isExceptionPending() && faultPC) {
+        EnvironmentIter ei(cx, topFrame, faultPC);
+        UnwindEnvironment(cx, ei, tryPC);
+    }
+
     JitSpew(JitSpew_BaselineBailouts,
             "  Restored outerScript=(%s:%" PRIuSIZE ",%u) innerScript=(%s:%" PRIuSIZE ",%u) (bailoutKind=%u)",
             outerScript->filename(), outerScript->lineno(), outerScript->getWarmUpCount(),
             innerScript->filename(), innerScript->lineno(), innerScript->getWarmUpCount(),
             (unsigned) bailoutKind);
 
     switch (bailoutKind) {
       // Normal bailouts.
--- a/js/src/jit/BaselineJIT.h
+++ b/js/src/jit/BaselineJIT.h
@@ -587,16 +587,20 @@ struct BaselineBailoutInfo
     void* resumeFramePtr;
 
     // The native code address to resume into.
     void* resumeAddr;
 
     // The bytecode pc where we will resume.
     jsbytecode* resumePC;
 
+    // The bytecode pc of try block and fault block.
+    jsbytecode* tryPC;
+    jsbytecode* faultPC;
+
     // If resuming into a TypeMonitor IC chain, this field holds the
     // address of the first stub in that chain.  If this field is
     // set, then the actual jitcode resumed into is the jitcode for
     // the first stub, not the resumeAddr above.  The resumeAddr
     // above, in this case, is pushed onto the stack so that the
     // TypeMonitor chain can tail-return into the main jitcode when done.
     ICStub* monitorStub;
 
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -1165,16 +1165,19 @@ IonBuilder::initEnvironmentChain(MDefini
     } else {
         // For global scripts without a non-syntactic global scope, the env
         // chain is the global lexical env.
         MOZ_ASSERT(!script()->isForEval());
         MOZ_ASSERT(!script()->hasNonSyntacticScope());
         env = constant(ObjectValue(script()->global().lexicalEnvironment()));
     }
 
+    // Update the environment slot from UndefinedValue only after initial
+    // environment is created so that bailout doesn't see a partial env.
+    // See: |InitFromBailout|
     current->setEnvironmentChain(env);
     return Ok();
 }
 
 void
 IonBuilder::initArgumentsObject()
 {
     JitSpew(JitSpew_IonMIR, "%s:%" PRIuSIZE " - Emitting code to initialize arguments object! block=%p",
--- a/js/src/jit/JitFrames.cpp
+++ b/js/src/jit/JitFrames.cpp
@@ -459,18 +459,24 @@ HandleExceptionIon(JSContext* cx, const 
                 // exceptions is slow. Reset the warm-up counter so that if we
                 // catch many exceptions we won't Ion-compile the script.
                 script->resetWarmUpCounter();
 
                 // Bailout at the start of the catch block.
                 jsbytecode* catchPC = script->main() + tn->start + tn->length;
                 ExceptionBailoutInfo excInfo(frame.frameNo(), catchPC, tn->stackDepth);
                 uint32_t retval = ExceptionHandlerBailout(cx, frame, rfe, excInfo, overrecursed);
-                if (retval == BAILOUT_RETURN_OK)
+                if (retval == BAILOUT_RETURN_OK) {
+                    // Record exception locations to allow scope unwinding in
+                    // |FinishBailoutToBaseline|
+                    MOZ_ASSERT(cx->isExceptionPending());
+                    rfe->bailoutInfo->tryPC = UnwindEnvironmentToTryPc(frame.script(), tn);
+                    rfe->bailoutInfo->faultPC = frame.pc();
                     return;
+                }
 
                 // Error on bailout clears pending exception.
                 MOZ_ASSERT(!cx->isExceptionPending());
             }
             break;
 
           default:
             MOZ_CRASH("Unexpected try note");