Bug 1273858 - Support LexicalEnvironmentObjects during Ion bailout
MozReview-Commit-ID: 8BLiJNOcrS2
--- 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");