From 7d316d6f96504ef8bcd4ac76ba56aa7ead275b52 Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Wed, 16 Aug 2017 16:37:31 +0200 Subject: [PATCH] Bug 1384683: Implement an higher-level frame iterator that can handle JS jit and wasm frames interleaving; r=jandem, r=luke MozReview-Commit-ID: DFJmBBHNSaa --HG-- extra : rebase_source : 62f1625a6c8615118713aa1345b272dccbda49da --- js/src/builtin/TestingFunctions.cpp | 1 - js/src/gc/RootMarking.cpp | 1 - js/src/jit/Bailouts.cpp | 45 ++-- js/src/jit/BaselineBailouts.cpp | 13 +- js/src/jit/BaselineDebugModeOSR.cpp | 68 ++--- js/src/jit/BaselineDebugModeOSR.h | 19 +- js/src/jit/BaselineFrame.cpp | 8 +- js/src/jit/BaselineFrame.h | 2 +- js/src/jit/BaselineJIT.cpp | 15 +- js/src/jit/BaselineJIT.h | 2 +- js/src/jit/Ion.cpp | 53 ++-- js/src/jit/IonCaches.cpp | 10 +- js/src/jit/JitFrameIterator.cpp | 21 +- js/src/jit/JitFrameIterator.h | 31 ++- js/src/jit/JitFrames-inl.h | 14 +- js/src/jit/JitFrames.cpp | 113 ++++---- js/src/jit/JitFrames.h | 16 +- js/src/jit/SharedIC.cpp | 4 +- js/src/jit/VMFunctions.cpp | 16 +- js/src/jsapi.cpp | 4 +- js/src/jscntxt.cpp | 2 +- js/src/jscntxt.h | 7 +- js/src/jsobj.cpp | 8 +- js/src/vm/Debugger.cpp | 21 +- js/src/vm/Stack-inl.h | 18 +- js/src/vm/Stack.cpp | 400 ++++++++++++++++------------ js/src/vm/Stack.h | 144 ++++++++-- js/src/vm/TraceLogging.cpp | 14 +- js/src/wasm/WasmBuiltins.cpp | 35 +-- js/src/wasm/WasmFrameIter.cpp | 25 +- js/src/wasm/WasmFrameIter.h | 16 +- 31 files changed, 647 insertions(+), 499 deletions(-) diff --git a/js/src/builtin/TestingFunctions.cpp b/js/src/builtin/TestingFunctions.cpp index 90de564abf4f..f903382b397e 100644 --- a/js/src/builtin/TestingFunctions.cpp +++ b/js/src/builtin/TestingFunctions.cpp @@ -31,7 +31,6 @@ #include "irregexp/RegExpParser.h" #endif #include "jit/InlinableNatives.h" -#include "jit/JitFrameIterator.h" #include "js/Debug.h" #include "js/HashTable.h" #include "js/StructuredClone.h" diff --git a/js/src/gc/RootMarking.cpp b/js/src/gc/RootMarking.cpp index 0f2046255ca4..66b7061dd66d 100644 --- a/js/src/gc/RootMarking.cpp +++ b/js/src/gc/RootMarking.cpp @@ -329,7 +329,6 @@ js::gc::GCRuntime::traceRuntimeCommon(JSTracer* trc, TraceOrMarkRuntime traceOrM // Trace active interpreter and JIT stack roots. TraceInterpreterActivations(cx, target, trc); jit::TraceJitActivations(cx, target, trc); - wasm::TraceActivations(cx, target, trc); // Trace legacy C stack roots. AutoGCRooter::traceAll(target, trc); diff --git a/js/src/jit/Bailouts.cpp b/js/src/jit/Bailouts.cpp index 7f6c6a9c2a17..5c4187864fb6 100644 --- a/js/src/jit/Bailouts.cpp +++ b/js/src/jit/Bailouts.cpp @@ -41,19 +41,19 @@ jit::Bailout(BailoutStack* sp, BaselineBailoutInfo** bailoutInfo) JitActivationIterator jitActivations(cx); BailoutFrameInfo bailoutData(jitActivations, sp); - JitFrameIterator iter(jitActivations); - MOZ_ASSERT(!iter.ionScript()->invalidated()); - CommonFrameLayout* currentFramePtr = iter.current(); + JitFrameIterator frame(jitActivations->asJit()); + MOZ_ASSERT(!frame.ionScript()->invalidated()); + CommonFrameLayout* currentFramePtr = frame.current(); TraceLoggerThread* logger = TraceLoggerForCurrentThread(cx); TraceLogTimestamp(logger, TraceLogger_Bailout); - JitSpew(JitSpew_IonBailouts, "Took bailout! Snapshot offset: %d", iter.snapshotOffset()); + JitSpew(JitSpew_IonBailouts, "Took bailout! Snapshot offset: %d", frame.snapshotOffset()); MOZ_ASSERT(IsBaselineEnabled(cx)); *bailoutInfo = nullptr; - uint32_t retval = BailoutIonToBaseline(cx, bailoutData.activation(), iter, false, bailoutInfo, + uint32_t retval = BailoutIonToBaseline(cx, bailoutData.activation(), frame, false, bailoutInfo, /* excInfo = */ nullptr); MOZ_ASSERT(retval == BAILOUT_RETURN_OK || retval == BAILOUT_RETURN_FATAL_ERROR || @@ -61,7 +61,7 @@ jit::Bailout(BailoutStack* sp, BaselineBailoutInfo** bailoutInfo) MOZ_ASSERT_IF(retval == BAILOUT_RETURN_OK, *bailoutInfo != nullptr); if (retval != BAILOUT_RETURN_OK) { - JSScript* script = iter.script(); + JSScript* script = frame.script(); probes::ExitScript(cx, script, script->functionNonDelazifying(), /* popProfilerFrame = */ false); } @@ -74,8 +74,8 @@ jit::Bailout(BailoutStack* sp, BaselineBailoutInfo** bailoutInfo) // invalidated (see InvalidateActivation), we remove references to it and // increment the reference counter for each activation that appear on the // stack. As the bailed frame is one of them, we have to decrement it now. - if (iter.ionScript()->invalidated()) - iter.ionScript()->decrementInvalidationCount(cx->runtime()->defaultFreeOp()); + if (frame.ionScript()->invalidated()) + frame.ionScript()->decrementInvalidationCount(cx->runtime()->defaultFreeOp()); // NB: Commentary on how |lastProfilingFrame| is set from bailouts. // @@ -113,21 +113,22 @@ jit::InvalidationBailout(InvalidationBailoutStack* sp, size_t* frameSizeOut, JitActivationIterator jitActivations(cx); BailoutFrameInfo bailoutData(jitActivations, sp); - JitFrameIterator iter(jitActivations); - CommonFrameLayout* currentFramePtr = iter.current(); + JitFrameIterator frame(jitActivations->asJit()); + CommonFrameLayout* currentFramePtr = frame.current(); TraceLoggerThread* logger = TraceLoggerForCurrentThread(cx); TraceLogTimestamp(logger, TraceLogger_Invalidation); - JitSpew(JitSpew_IonBailouts, "Took invalidation bailout! Snapshot offset: %d", iter.snapshotOffset()); + JitSpew(JitSpew_IonBailouts, "Took invalidation bailout! Snapshot offset: %d", + frame.snapshotOffset()); // Note: the frame size must be computed before we return from this function. - *frameSizeOut = iter.frameSize(); + *frameSizeOut = frame.frameSize(); MOZ_ASSERT(IsBaselineEnabled(cx)); *bailoutInfo = nullptr; - uint32_t retval = BailoutIonToBaseline(cx, bailoutData.activation(), iter, true, bailoutInfo, + uint32_t retval = BailoutIonToBaseline(cx, bailoutData.activation(), frame, true, bailoutInfo, /* excInfo = */ nullptr); MOZ_ASSERT(retval == BAILOUT_RETURN_OK || retval == BAILOUT_RETURN_FATAL_ERROR || @@ -146,21 +147,21 @@ jit::InvalidationBailout(InvalidationBailoutStack* sp, size_t* frameSizeOut, // However, if the bailout was during argument check, then a // pseudostack frame would not have been pushed in the first // place, so don't pop anything in that case. - JSScript* script = iter.script(); + JSScript* script = frame.script(); probes::ExitScript(cx, script, script->functionNonDelazifying(), /* popProfilerFrame = */ false); #ifdef JS_JITSPEW - JitFrameLayout* frame = iter.jsFrame(); + JitFrameLayout* layout = frame.jsFrame(); JitSpew(JitSpew_IonInvalidate, "Bailout failed (%s)", (retval == BAILOUT_RETURN_FATAL_ERROR) ? "Fatal Error" : "Over Recursion"); - JitSpew(JitSpew_IonInvalidate, " calleeToken %p", (void*) frame->calleeToken()); - JitSpew(JitSpew_IonInvalidate, " frameSize %u", unsigned(frame->prevFrameLocalSize())); - JitSpew(JitSpew_IonInvalidate, " ra %p", (void*) frame->returnAddress()); + JitSpew(JitSpew_IonInvalidate, " calleeToken %p", (void*) layout->calleeToken()); + JitSpew(JitSpew_IonInvalidate, " frameSize %u", unsigned(layout->prevFrameLocalSize())); + JitSpew(JitSpew_IonInvalidate, " ra %p", (void*) layout->returnAddress()); #endif } - iter.ionScript()->decrementInvalidationCount(cx->runtime()->defaultFreeOp()); + frame.ionScript()->decrementInvalidationCount(cx->runtime()->defaultFreeOp()); // Make the frame being bailed out the top profiled frame. if (cx->runtime()->jitRuntime()->isProfilerInstrumentationEnabled(cx->runtime())) @@ -202,8 +203,8 @@ jit::ExceptionHandlerBailout(JSContext* cx, const InlineFrameIterator& frame, JitActivationIterator jitActivations(cx); BailoutFrameInfo bailoutData(jitActivations, frame.frame()); - JitFrameIterator iter(jitActivations); - CommonFrameLayout* currentFramePtr = iter.current(); + JitFrameIterator frameView(jitActivations->asJit()); + CommonFrameLayout* currentFramePtr = frameView.current(); BaselineBailoutInfo* bailoutInfo = nullptr; uint32_t retval; @@ -213,7 +214,7 @@ jit::ExceptionHandlerBailout(JSContext* cx, const InlineFrameIterator& frame, // exception handling code further. AutoEnterOOMUnsafeRegion oomUnsafe; - retval = BailoutIonToBaseline(cx, bailoutData.activation(), iter, true, + retval = BailoutIonToBaseline(cx, bailoutData.activation(), frameView, true, &bailoutInfo, &excInfo); if (retval == BAILOUT_RETURN_FATAL_ERROR && cx->isThrowingOutOfMemory()) oomUnsafe.crash("ExceptionHandlerBailout"); diff --git a/js/src/jit/BaselineBailouts.cpp b/js/src/jit/BaselineBailouts.cpp index 3f6837426285..fc08c9b03e4a 100644 --- a/js/src/jit/BaselineBailouts.cpp +++ b/js/src/jit/BaselineBailouts.cpp @@ -85,7 +85,7 @@ class BufferPointer */ struct BaselineStackBuilder { - JitFrameIterator& iter_; + const JitFrameIterator& iter_; JitFrameLayout* frame_; static size_t HeaderSize() { @@ -99,7 +99,7 @@ struct BaselineStackBuilder size_t framePushed_; - BaselineStackBuilder(JitFrameIterator& iter, size_t initialSize) + BaselineStackBuilder(const JitFrameIterator& iter, size_t initialSize) : iter_(iter), frame_(static_cast(iter.current())), bufferTotal_(initialSize), @@ -428,10 +428,10 @@ struct BaselineStackBuilder class SnapshotIteratorForBailout : public SnapshotIterator { JitActivation* activation_; - JitFrameIterator& iter_; + const JitFrameIterator& iter_; public: - SnapshotIteratorForBailout(JitActivation* activation, JitFrameIterator& iter) + SnapshotIteratorForBailout(JitActivation* activation, const JitFrameIterator& iter) : SnapshotIterator(iter, activation->bailoutData()->machineState()), activation_(activation), iter_(iter) @@ -1493,8 +1493,9 @@ InitFromBailout(JSContext* cx, HandleScript caller, jsbytecode* callerPC, } uint32_t -jit::BailoutIonToBaseline(JSContext* cx, JitActivation* activation, JitFrameIterator& iter, - bool invalidate, BaselineBailoutInfo** bailoutInfo, +jit::BailoutIonToBaseline(JSContext* cx, JitActivation* activation, + const JitFrameIterator& iter, bool invalidate, + BaselineBailoutInfo** bailoutInfo, const ExceptionBailoutInfo* excInfo) { MOZ_ASSERT(bailoutInfo != nullptr); diff --git a/js/src/jit/BaselineDebugModeOSR.cpp b/js/src/jit/BaselineDebugModeOSR.cpp index 31324eda46fb..2410701d922a 100644 --- a/js/src/jit/BaselineDebugModeOSR.cpp +++ b/js/src/jit/BaselineDebugModeOSR.cpp @@ -188,37 +188,38 @@ CollectJitStackScripts(JSContext* cx, const Debugger::ExecutionObservableSet& ob { ICStub* prevFrameStubPtr = nullptr; bool needsRecompileHandler = false; - for (JitFrameIterator iter(activation); !iter.done(); ++iter) { - switch (iter.type()) { + for (OnlyJSJitFrameIter iter(activation); !iter.done(); ++iter) { + const JitFrameIterator& frame = iter.frame(); + switch (frame.type()) { case JitFrame_BaselineJS: { - JSScript* script = iter.script(); + JSScript* script = frame.script(); if (!obs.shouldRecompileOrInvalidate(script)) { prevFrameStubPtr = nullptr; break; } - BaselineFrame* frame = iter.baselineFrame(); + BaselineFrame* baselineFrame = frame.baselineFrame(); - if (BaselineDebugModeOSRInfo* info = frame->getDebugModeOSRInfo()) { + if (BaselineDebugModeOSRInfo* info = baselineFrame->getDebugModeOSRInfo()) { // If patching a previously patched yet unpopped frame, we can // use the BaselineDebugModeOSRInfo on the frame directly to - // patch. Indeed, we cannot use iter.returnAddressToFp(), as + // patch. Indeed, we cannot use frame.returnAddressToFp(), as // it points into the debug mode OSR handler and cannot be // used to look up a corresponding ICEntry. // // See cases F and G in PatchBaselineFramesForDebugMode. if (!entries.append(DebugModeOSREntry(script, info))) return false; - } else if (frame->isHandlingException()) { + } else if (baselineFrame->isHandlingException()) { // We are in the middle of handling an exception and the frame // must have an override pc. - uint32_t offset = script->pcToOffset(frame->overridePc()); + uint32_t offset = script->pcToOffset(baselineFrame->overridePc()); if (!entries.append(DebugModeOSREntry(script, offset))) return false; } else { // The frame must be settled on a pc with an ICEntry. - uint8_t* retAddr = iter.returnAddressToFp(); + uint8_t* retAddr = frame.returnAddressToFp(); BaselineICEntry& icEntry = script->baselineScript()->icEntryFromReturnAddress(retAddr); if (!entries.append(DebugModeOSREntry(script, icEntry))) return false; @@ -237,11 +238,11 @@ CollectJitStackScripts(JSContext* cx, const Debugger::ExecutionObservableSet& ob case JitFrame_BaselineStub: prevFrameStubPtr = - reinterpret_cast(iter.fp())->maybeStubPtr(); + reinterpret_cast(frame.fp())->maybeStubPtr(); break; case JitFrame_IonJS: { - InlineFrameIterator inlineIter(cx, &iter); + InlineFrameIterator inlineIter(cx, &frame); while (true) { if (obs.shouldRecompileOrInvalidate(inlineIter.script())) { if (!entries.append(DebugModeOSREntry(inlineIter.script()))) @@ -383,12 +384,13 @@ PatchBaselineFramesForDebugMode(JSContext* cx, const CooperatingContext& target, CommonFrameLayout* prev = nullptr; size_t entryIndex = *start; - for (JitFrameIterator iter(activation); !iter.done(); ++iter) { - switch (iter.type()) { + for (OnlyJSJitFrameIter iter(activation); !iter.done(); ++iter) { + const JitFrameIterator& frame = iter.frame(); + switch (frame.type()) { case JitFrame_BaselineJS: { // If the script wasn't recompiled or is not observed, there's // nothing to patch. - if (!obs.shouldRecompileOrInvalidate(iter.script())) + if (!obs.shouldRecompileOrInvalidate(frame.script())) break; DebugModeOSREntry& entry = entries[entryIndex]; @@ -402,7 +404,7 @@ PatchBaselineFramesForDebugMode(JSContext* cx, const CooperatingContext& target, uint32_t pcOffset = entry.pcOffset; jsbytecode* pc = script->offsetToPC(pcOffset); - MOZ_ASSERT(script == iter.script()); + MOZ_ASSERT(script == frame.script()); MOZ_ASSERT(pcOffset < script->length()); BaselineScript* bl = script->baselineScript(); @@ -420,7 +422,7 @@ PatchBaselineFramesForDebugMode(JSContext* cx, const CooperatingContext& target, // directly to the IC resume address. uint8_t* retAddr = bl->returnAddressForIC(bl->icEntryFromPCOffset(pcOffset)); SpewPatchBaselineFrame(prev->returnAddress(), retAddr, script, kind, pc); - DebugModeOSRVolatileJitFrameIterator::forwardLiveIterators( + DebugModeOSRVolatileJitFrameIter::forwardLiveIterators( target, prev->returnAddress(), retAddr); prev->setReturnAddress(retAddr); entryIndex++; @@ -440,8 +442,8 @@ PatchBaselineFramesForDebugMode(JSContext* cx, const CooperatingContext& target, // // If profiling is on, JitProfilingFrameIterator requires a // valid return address. - MOZ_ASSERT(iter.baselineFrame()->isHandlingException()); - MOZ_ASSERT(iter.baselineFrame()->overridePc() == pc); + MOZ_ASSERT(frame.baselineFrame()->isHandlingException()); + MOZ_ASSERT(frame.baselineFrame()->overridePc() == pc); uint8_t* retAddr; if (cx->runtime()->geckoProfiler().enabled()) retAddr = bl->nativeCodeForPC(script, pc); @@ -449,7 +451,7 @@ PatchBaselineFramesForDebugMode(JSContext* cx, const CooperatingContext& target, retAddr = nullptr; SpewPatchBaselineFrameFromExceptionHandler(prev->returnAddress(), retAddr, script, pc); - DebugModeOSRVolatileJitFrameIterator::forwardLiveIterators( + DebugModeOSRVolatileJitFrameIter::forwardLiveIterators( target, prev->returnAddress(), retAddr); prev->setReturnAddress(retAddr); entryIndex++; @@ -461,7 +463,7 @@ PatchBaselineFramesForDebugMode(JSContext* cx, const CooperatingContext& target, // We undo a previous recompile by handling cases B, C, D, E, I or J // like normal, except that we retrieve the pc information via // the previous OSR debug info stashed on the frame. - BaselineDebugModeOSRInfo* info = iter.baselineFrame()->getDebugModeOSRInfo(); + BaselineDebugModeOSRInfo* info = frame.baselineFrame()->getDebugModeOSRInfo(); if (info) { MOZ_ASSERT(info->pc == pc); MOZ_ASSERT(info->frameKind == kind); @@ -475,7 +477,7 @@ PatchBaselineFramesForDebugMode(JSContext* cx, const CooperatingContext& target, // We will have allocated a new recompile info, so delete the // existing one. - iter.baselineFrame()->deleteDebugModeOSRInfo(); + frame.baselineFrame()->deleteDebugModeOSRInfo(); } // The RecompileInfo must already be allocated so that this @@ -568,15 +570,15 @@ PatchBaselineFramesForDebugMode(JSContext* cx, const CooperatingContext& target, MOZ_ASSERT(handlerAddr); prev->setReturnAddress(reinterpret_cast(handlerAddr)); - iter.baselineFrame()->setDebugModeOSRInfo(recompInfo); - iter.baselineFrame()->setOverridePc(recompInfo->pc); + frame.baselineFrame()->setDebugModeOSRInfo(recompInfo); + frame.baselineFrame()->setOverridePc(recompInfo->pc); entryIndex++; break; } case JitFrame_BaselineStub: { - JitFrameIterator prev(iter); + JitFrameIterator prev(iter.frame()); ++prev; BaselineFrame* prevFrame = prev.baselineFrame(); if (!obs.shouldRecompileOrInvalidate(prevFrame->script())) @@ -589,7 +591,7 @@ PatchBaselineFramesForDebugMode(JSContext* cx, const CooperatingContext& target, break; BaselineStubFrameLayout* layout = - reinterpret_cast(iter.fp()); + reinterpret_cast(frame.fp()); MOZ_ASSERT(layout->maybeStubPtr() == entry.oldStub); // Patch baseline stub frames for case A above. @@ -620,7 +622,7 @@ PatchBaselineFramesForDebugMode(JSContext* cx, const CooperatingContext& target, case JitFrame_IonJS: { // Nothing to patch. - InlineFrameIterator inlineIter(cx, &iter); + InlineFrameIterator inlineIter(cx, &frame); while (true) { if (obs.shouldRecompileOrInvalidate(inlineIter.script())) entryIndex++; @@ -634,7 +636,7 @@ PatchBaselineFramesForDebugMode(JSContext* cx, const CooperatingContext& target, default:; } - prev = iter.current(); + prev = frame.current(); } *start = entryIndex; @@ -1173,12 +1175,10 @@ JitRuntime::generateBaselineDebugModeOSRHandler(JSContext* cx, uint32_t* noFrame } /* static */ void -DebugModeOSRVolatileJitFrameIterator::forwardLiveIterators(const CooperatingContext& cx, - uint8_t* oldAddr, uint8_t* newAddr) +DebugModeOSRVolatileJitFrameIter::forwardLiveIterators(const CooperatingContext& cx, + uint8_t* oldAddr, uint8_t* newAddr) { - DebugModeOSRVolatileJitFrameIterator* iter; - for (iter = cx.context()->liveVolatileJitFrameIterators_; iter; iter = iter->prev) { - if (iter->returnAddressToFp_ == oldAddr) - iter->returnAddressToFp_ = newAddr; - } + DebugModeOSRVolatileJitFrameIter* iter; + for (iter = cx.context()->liveVolatileJitFrameIter_; iter; iter = iter->prev) + iter->asJSJit().exchangeReturnAddressIfMatch(oldAddr, newAddr); } diff --git a/js/src/jit/BaselineDebugModeOSR.h b/js/src/jit/BaselineDebugModeOSR.h index bf8bbb3f7145..f440c18c1c73 100644 --- a/js/src/jit/BaselineDebugModeOSR.h +++ b/js/src/jit/BaselineDebugModeOSR.h @@ -82,24 +82,25 @@ class DebugModeOSRVolatileStub }; // -// A JitFrameIterator that updates itself in case of recompilation of an -// on-stack baseline script. +// A JitFrameIter that updates internal JitFrameIterator in case of +// recompilation of an on-stack baseline script. // -class DebugModeOSRVolatileJitFrameIterator : public JitFrameIterator + +class DebugModeOSRVolatileJitFrameIter : public JitFrameIter { - DebugModeOSRVolatileJitFrameIterator** stack; - DebugModeOSRVolatileJitFrameIterator* prev; + DebugModeOSRVolatileJitFrameIter** stack; + DebugModeOSRVolatileJitFrameIter* prev; public: - explicit DebugModeOSRVolatileJitFrameIterator(JSContext* cx) - : JitFrameIterator(cx) + explicit DebugModeOSRVolatileJitFrameIter(JSContext* cx) + : JitFrameIter(cx->activation()) { - stack = &cx->liveVolatileJitFrameIterators_.ref(); + stack = &cx->liveVolatileJitFrameIter_.ref(); prev = *stack; *stack = this; } - ~DebugModeOSRVolatileJitFrameIterator() { + ~DebugModeOSRVolatileJitFrameIter() { MOZ_ASSERT(*stack == this); *stack = prev; } diff --git a/js/src/jit/BaselineFrame.cpp b/js/src/jit/BaselineFrame.cpp index 346f28bdd63d..e987e6012264 100644 --- a/js/src/jit/BaselineFrame.cpp +++ b/js/src/jit/BaselineFrame.cpp @@ -28,7 +28,7 @@ TraceLocals(BaselineFrame* frame, JSTracer* trc, unsigned start, unsigned end) } void -BaselineFrame::trace(JSTracer* trc, JitFrameIterator& frameIterator) +BaselineFrame::trace(JSTracer* trc, const JitFrameIterator& frameIterator) { replaceCalleeToken(TraceCalleeToken(trc, calleeToken())); @@ -142,10 +142,10 @@ BaselineFrame::initForOsr(InterpreterFrame* fp, uint32_t numStackValues) // debugger, wants a valid return address, but it's okay to just pick one. // In debug mode there's always at least 1 ICEntry (since there are always // debug prologue/epilogue calls). - JitFrameIterator iter(cx); - MOZ_ASSERT(iter.returnAddress() == nullptr); + JitFrameIterator frame(cx); + MOZ_ASSERT(frame.returnAddress() == nullptr); BaselineScript* baseline = fp->script()->baselineScript(); - iter.current()->setReturnAddress(baseline->returnAddressForIC(baseline->icEntry(0))); + frame.current()->setReturnAddress(baseline->returnAddressForIC(baseline->icEntry(0))); if (!Debugger::handleBaselineOsr(cx, fp, this)) return false; diff --git a/js/src/jit/BaselineFrame.h b/js/src/jit/BaselineFrame.h index 9a30cdcfc36b..2f1057bd48aa 100644 --- a/js/src/jit/BaselineFrame.h +++ b/js/src/jit/BaselineFrame.h @@ -370,7 +370,7 @@ class BaselineFrame flags_ &= ~HAS_OVERRIDE_PC; } - void trace(JSTracer* trc, JitFrameIterator& frame); + void trace(JSTracer* trc, const JitFrameIterator& frame); bool isGlobalFrame() const { return script()->isGlobalCode(); diff --git a/js/src/jit/BaselineJIT.cpp b/js/src/jit/BaselineJIT.cpp index efe1d26658f3..cbc46b7c2ec2 100644 --- a/js/src/jit/BaselineJIT.cpp +++ b/js/src/jit/BaselineJIT.cpp @@ -1229,14 +1229,15 @@ jit::ToggleBaselineTraceLoggerEngine(JSRuntime* runtime, bool enable) static void MarkActiveBaselineScripts(JSContext* cx, const JitActivationIterator& activation) { - for (jit::JitFrameIterator iter(activation); !iter.done(); ++iter) { - switch (iter.type()) { + for (OnlyJSJitFrameIter iter(activation); !iter.done(); ++iter) { + const JitFrameIterator& frame = iter.frame(); + switch (frame.type()) { case JitFrame_BaselineJS: - iter.script()->baselineScript()->setActive(); + frame.script()->baselineScript()->setActive(); break; case JitFrame_Exit: - if (iter.exitFrame()->is()) { - LazyLinkExitFrameLayout* ll = iter.exitFrame()->as(); + if (frame.exitFrame()->is()) { + LazyLinkExitFrameLayout* ll = frame.exitFrame()->as(); ScriptFromCalleeToken(ll->jsFrame()->calleeToken())->baselineScript()->setActive(); } break; @@ -1244,8 +1245,8 @@ MarkActiveBaselineScripts(JSContext* cx, const JitActivationIterator& activation case JitFrame_IonJS: { // Keep the baseline script around, since bailouts from the ion // jitcode might need to re-enter into the baseline jitcode. - iter.script()->baselineScript()->setActive(); - for (InlineFrameIterator inlineIter(cx, &iter); inlineIter.more(); ++inlineIter) + frame.script()->baselineScript()->setActive(); + for (InlineFrameIterator inlineIter(cx, &frame); inlineIter.more(); ++inlineIter) inlineIter.script()->baselineScript()->setActive(); break; } diff --git a/js/src/jit/BaselineJIT.h b/js/src/jit/BaselineJIT.h index db5d3ae26bec..df5bc574cf08 100644 --- a/js/src/jit/BaselineJIT.h +++ b/js/src/jit/BaselineJIT.h @@ -630,7 +630,7 @@ struct BaselineBailoutInfo }; uint32_t -BailoutIonToBaseline(JSContext* cx, JitActivation* activation, JitFrameIterator& iter, +BailoutIonToBaseline(JSContext* cx, JitActivation* activation, const JitFrameIterator& iter, bool invalidate, BaselineBailoutInfo** bailoutInfo, const ExceptionBailoutInfo* exceptionInfo); diff --git a/js/src/jit/Ion.cpp b/js/src/jit/Ion.cpp index 667ee8a027c0..025d0e4bb046 100644 --- a/js/src/jit/Ion.cpp +++ b/js/src/jit/Ion.cpp @@ -591,8 +591,8 @@ jit::LazyLinkTopActivation() { // First frame should be an exit frame. JSContext* cx = TlsContext.get(); - JitFrameIterator it(cx); - LazyLinkExitFrameLayout* ll = it.exitFrame()->as(); + JitFrameIterator frame(cx); + LazyLinkExitFrameLayout* ll = frame.exitFrame()->as(); RootedScript calleeScript(cx, ScriptFromCalleeToken(ll->jsFrame()->calleeToken())); LinkIonScript(cx, calleeScript); @@ -2971,64 +2971,65 @@ InvalidateActivation(FreeOp* fop, const JitActivationIterator& activations, bool size_t frameno = 1; - for (JitFrameIterator it(activations); !it.done(); ++it, ++frameno) { - MOZ_ASSERT_IF(frameno == 1, it.isExitFrame() || it.type() == JitFrame_Bailout); + for (OnlyJSJitFrameIter iter(activations); !iter.done(); ++iter, ++frameno) { + const JitFrameIterator& frame = iter.frame(); + MOZ_ASSERT_IF(frameno == 1, frame.isExitFrame() || frame.type() == JitFrame_Bailout); #ifdef JS_JITSPEW - switch (it.type()) { + switch (frame.type()) { case JitFrame_Exit: - JitSpew(JitSpew_IonInvalidate, "#%zu exit frame @ %p", frameno, it.fp()); + JitSpew(JitSpew_IonInvalidate, "#%zu exit frame @ %p", frameno, frame.fp()); break; case JitFrame_BaselineJS: case JitFrame_IonJS: case JitFrame_Bailout: { - MOZ_ASSERT(it.isScripted()); + MOZ_ASSERT(frame.isScripted()); const char* type = "Unknown"; - if (it.isIonJS()) + if (frame.isIonJS()) type = "Optimized"; - else if (it.isBaselineJS()) + else if (frame.isBaselineJS()) type = "Baseline"; - else if (it.isBailoutJS()) + else if (frame.isBailoutJS()) type = "Bailing"; JitSpew(JitSpew_IonInvalidate, "#%zu %s JS frame @ %p, %s:%zu (fun: %p, script: %p, pc %p)", - frameno, type, it.fp(), it.script()->maybeForwardedFilename(), - it.script()->lineno(), it.maybeCallee(), (JSScript*)it.script(), - it.returnAddressToFp()); + frameno, type, frame.fp(), frame.script()->maybeForwardedFilename(), + frame.script()->lineno(), frame.maybeCallee(), (JSScript*)frame.script(), + frame.returnAddressToFp()); break; } case JitFrame_BaselineStub: - JitSpew(JitSpew_IonInvalidate, "#%zu baseline stub frame @ %p", frameno, it.fp()); + JitSpew(JitSpew_IonInvalidate, "#%zu baseline stub frame @ %p", frameno, frame.fp()); break; case JitFrame_Rectifier: - JitSpew(JitSpew_IonInvalidate, "#%zu rectifier frame @ %p", frameno, it.fp()); + JitSpew(JitSpew_IonInvalidate, "#%zu rectifier frame @ %p", frameno, frame.fp()); break; case JitFrame_IonICCall: - JitSpew(JitSpew_IonInvalidate, "#%zu ion IC call frame @ %p", frameno, it.fp()); + JitSpew(JitSpew_IonInvalidate, "#%zu ion IC call frame @ %p", frameno, frame.fp()); break; case JitFrame_Entry: - JitSpew(JitSpew_IonInvalidate, "#%zu entry frame @ %p", frameno, it.fp()); + JitSpew(JitSpew_IonInvalidate, "#%zu entry frame @ %p", frameno, frame.fp()); break; } #endif // JS_JITSPEW - if (!it.isIonScripted()) + if (!frame.isIonScripted()) continue; bool calledFromLinkStub = false; JitCode* lazyLinkStub = fop->runtime()->jitRuntime()->lazyLinkStub(); - if (it.returnAddressToFp() >= lazyLinkStub->raw() && - it.returnAddressToFp() < lazyLinkStub->rawEnd()) + if (frame.returnAddressToFp() >= lazyLinkStub->raw() && + frame.returnAddressToFp() < lazyLinkStub->rawEnd()) { calledFromLinkStub = true; } // See if the frame has already been invalidated. - if (!calledFromLinkStub && it.checkInvalidation()) + if (!calledFromLinkStub && frame.checkInvalidation()) continue; - JSScript* script = it.script(); + JSScript* script = frame.script(); if (!script->hasIonScript()) continue; @@ -3084,7 +3085,7 @@ InvalidateActivation(FreeOp* fop, const JitActivationIterator& activations, bool // Don't adjust OSI points in the linkStub (which don't exist), or in a // bailout path. - if (calledFromLinkStub || it.isBailoutJS()) + if (calledFromLinkStub || frame.isBailoutJS()) continue; // Write the delta (from the return address offset to the @@ -3094,10 +3095,10 @@ InvalidateActivation(FreeOp* fop, const JitActivationIterator& activations, bool // a uint32, which is checked during safepoint index // construction. AutoWritableJitCode awjc(ionCode); - const SafepointIndex* si = ionScript->getSafepointIndex(it.returnAddressToFp()); - CodeLocationLabel dataLabelToMunge(it.returnAddressToFp()); + const SafepointIndex* si = ionScript->getSafepointIndex(frame.returnAddressToFp()); + CodeLocationLabel dataLabelToMunge(frame.returnAddressToFp()); ptrdiff_t delta = ionScript->invalidateEpilogueDataOffset() - - (it.returnAddressToFp() - ionCode->raw()); + (frame.returnAddressToFp() - ionCode->raw()); Assembler::PatchWrite_Imm32(dataLabelToMunge, Imm32(delta)); CodeLocationLabel osiPatchPoint = SafepointReader::InvalidationPatchPoint(ionScript, si); diff --git a/js/src/jit/IonCaches.cpp b/js/src/jit/IonCaches.cpp index 192d7e211ed3..890b2ef3963c 100644 --- a/js/src/jit/IonCaches.cpp +++ b/js/src/jit/IonCaches.cpp @@ -89,14 +89,14 @@ CodeOffsetJump::fixup(MacroAssembler* masm) void* jit::GetReturnAddressToIonCode(JSContext* cx) { - JitFrameIterator iter(cx); - MOZ_ASSERT(iter.type() == JitFrame_Exit, + JitFrameIterator frame(cx); + MOZ_ASSERT(frame.type() == JitFrame_Exit, "An exit frame is expected as update functions are called with a VMFunction."); - void* returnAddr = iter.returnAddress(); + void* returnAddr = frame.returnAddress(); #ifdef DEBUG - ++iter; - MOZ_ASSERT(iter.isIonJS()); + ++frame; + MOZ_ASSERT(frame.isIonJS()); #endif return returnAddr; } diff --git a/js/src/jit/JitFrameIterator.cpp b/js/src/jit/JitFrameIterator.cpp index 7ff40a4784ef..08ff36c4cbd3 100644 --- a/js/src/jit/JitFrameIterator.cpp +++ b/js/src/jit/JitFrameIterator.cpp @@ -14,16 +14,6 @@ using namespace js; using namespace js::jit; -JitFrameIterator::JitFrameIterator() - : current_(nullptr), - type_(JitFrame_Exit), - returnAddressToFp_(nullptr), - frameSize_(0), - cachedSafepointIndex_(nullptr), - activation_(nullptr) -{ -} - JitFrameIterator::JitFrameIterator(const JitActivation* activation) : current_(activation->exitFP()), type_(JitFrame_Exit), @@ -44,11 +34,6 @@ JitFrameIterator::JitFrameIterator(JSContext* cx) { } -JitFrameIterator::JitFrameIterator(const ActivationIterator& activations) - : JitFrameIterator(activations->asJit()) -{ -} - bool JitFrameIterator::checkInvalidation() const { @@ -164,7 +149,7 @@ JitFrameIterator::prevFp() const return current_ + current()->prevFrameLocalSize() + current()->headerSize(); } -JitFrameIterator& +void JitFrameIterator::operator++() { MOZ_ASSERT(type_ != JitFrame_Entry); @@ -176,14 +161,12 @@ JitFrameIterator::operator++() // since the entry and first frames overlap. if (current()->prevType() == JitFrame_Entry) { type_ = JitFrame_Entry; - return *this; + return; } type_ = current()->prevType(); returnAddressToFp_ = current()->returnAddress(); current_ = prevFp(); - - return *this; } uintptr_t* diff --git a/js/src/jit/JitFrameIterator.h b/js/src/jit/JitFrameIterator.h index db2e268da48c..ba6d2665b00a 100644 --- a/js/src/jit/JitFrameIterator.h +++ b/js/src/jit/JitFrameIterator.h @@ -16,10 +16,6 @@ #include "js/ProfilingFrameIterator.h" -namespace js { - class ActivationIterator; -} // namespace js - namespace js { namespace jit { @@ -83,8 +79,17 @@ class JitActivation; // Iterate over the JIT stack to assert that all invariants are respected. // - Check that all entry frames are aligned on JitStackAlignment. // - Check that all rectifier frames keep the JitStackAlignment. + void AssertJitStackInvariants(JSContext* cx); +// A JitFrameIterator can iterate over a linear frame group of JS jit frames +// only. It will stop at the first frame that is not of the same kind, or at +// the end of an activation. +// +// If you want to handle every kind of frames (including wasm frames), use +// JitFrameIter. If you want to skip interleaved frames of other kinds, use +// OnlyJSJitFrameIter. + class JitFrameIterator { protected: @@ -99,12 +104,16 @@ class JitFrameIterator void dumpBaseline() const; - explicit JitFrameIterator(const JitActivation* activation); - public: - explicit JitFrameIterator(); + // See comment above the class. + explicit JitFrameIterator(const JitActivation* activation); explicit JitFrameIterator(JSContext* cx); - explicit JitFrameIterator(const ActivationIterator& activations); + + // Used only by DebugModeOSRVolatileJSJitFrameIter. + void exchangeReturnAddressIfMatch(uint8_t* oldAddr, uint8_t* newAddr) { + if (returnAddressToFp_ == oldAddr) + returnAddressToFp_ = newAddr; + } // Current frame information. FrameType type() const { @@ -199,10 +208,10 @@ class JitFrameIterator // Functions used to iterate on frames. When prevType is JitFrame_Entry, // the current frame is the last frame. - inline bool done() const { + bool done() const { return type_ == JitFrame_Entry; } - JitFrameIterator& operator++(); + void operator++(); // Returns the IonScript associated with this JS frame. IonScript* ionScript() const; @@ -260,7 +269,7 @@ class JitFrameIterator #ifdef DEBUG bool verifyReturnAddressUsingNativeToBytecodeMap(); #else - inline bool verifyReturnAddressUsingNativeToBytecodeMap() { return true; } + bool verifyReturnAddressUsingNativeToBytecodeMap() { return true; } #endif }; diff --git a/js/src/jit/JitFrames-inl.h b/js/src/jit/JitFrames-inl.h index 9598791aea0f..d2bfccced33f 100644 --- a/js/src/jit/JitFrames-inl.h +++ b/js/src/jit/JitFrames-inl.h @@ -29,13 +29,13 @@ SafepointIndex::resolve() inline BaselineFrame* GetTopBaselineFrame(JSContext* cx) { - JitFrameIterator iter(cx); - MOZ_ASSERT(iter.type() == JitFrame_Exit); - ++iter; - if (iter.isBaselineStub()) - ++iter; - MOZ_ASSERT(iter.isBaselineJS()); - return iter.baselineFrame(); + JitFrameIterator frame(cx); + MOZ_ASSERT(frame.type() == JitFrame_Exit); + ++frame; + if (frame.isBaselineStub()) + ++frame; + MOZ_ASSERT(frame.isBaselineJS()); + return frame.baselineFrame(); } } // namespace jit diff --git a/js/src/jit/JitFrames.cpp b/js/src/jit/JitFrames.cpp index 2f24cd92d36f..8d166615b142 100644 --- a/js/src/jit/JitFrames.cpp +++ b/js/src/jit/JitFrames.cpp @@ -625,17 +625,21 @@ HandleException(ResumeFromException* rfe) // JitFrameIterators cache the previous frame's return address when // iterating, we need a variant here that is automatically updated should // on-stack recompilation occur. - DebugModeOSRVolatileJitFrameIterator iter(cx); - while (!iter.isEntry()) { + DebugModeOSRVolatileJitFrameIter iter(cx); + while (!iter.isJSJit() || !iter.asJSJit().isEntry()) { + while (!iter.isJSJit()) + ++iter; + const JitFrameIterator& frame = iter.asJSJit(); + bool overrecursed = false; - if (iter.isIonJS()) { + if (frame.isIonJS()) { // Search each inlined frame for live iterator objects, and close // them. - InlineFrameIterator frames(cx, &iter); + InlineFrameIterator frames(cx, &frame); // Invalidation state will be the same for all inlined scripts in the frame. IonScript* ionScript = nullptr; - bool invalidated = iter.checkInvalidation(&ionScript); + bool invalidated = frame.checkInvalidation(&ionScript); #ifdef JS_TRACE_LOGGING if (logger && cx->compartment()->isDebuggee() && logger->enabled()) { @@ -672,11 +676,11 @@ HandleException(ResumeFromException* rfe) ++frames; } - activation->removeIonFrameRecovery(iter.jsFrame()); + activation->removeIonFrameRecovery(frame.jsFrame()); if (invalidated) ionScript->decrementInvalidationCount(cx->runtime()->defaultFreeOp()); - } else if (iter.isBaselineJS()) { + } else if (frame.isBaselineJS()) { // Set a flag on the frame to signal to DebugModeOSR that we're // handling an exception. Also ensure the frame has an override // pc. We clear the frame's override pc when we leave this block, @@ -690,16 +694,16 @@ HandleException(ResumeFromException* rfe) // FinishBailoutToBaseline will set the pc to the resume pc // and clear it before it returns to JIT code. jsbytecode* pc; - iter.baselineScriptAndPc(nullptr, &pc); - AutoBaselineHandlingException handlingException(iter.baselineFrame(), pc); + frame.baselineScriptAndPc(nullptr, &pc); + AutoBaselineHandlingException handlingException(frame.baselineFrame(), pc); - HandleExceptionBaseline(cx, iter, rfe, pc); + HandleExceptionBaseline(cx, frame, rfe, pc); // If we are propagating an exception through a frame with // on-stack recompile info, we should free the allocated // RecompileInfo struct before we leave this block, as we will not // be returning to the recompile handler. - AutoDeleteDebugModeOSRInfo deleteDebugModeOSRInfo(iter.baselineFrame()); + AutoDeleteDebugModeOSRInfo deleteDebugModeOSRInfo(frame.baselineFrame()); if (rfe->kind != ResumeFromException::RESUME_ENTRY_FRAME && rfe->kind != ResumeFromException::RESUME_FORCED_RETURN) @@ -711,7 +715,7 @@ HandleException(ResumeFromException* rfe) TraceLogStopEvent(logger, TraceLogger_Scripts); // Unwind profiler pseudo-stack - JSScript* script = iter.script(); + JSScript* script = frame.script(); probes::ExitScript(cx, script, script->functionNonDelazifying(), /* popProfilerFrame = */ false); @@ -719,7 +723,7 @@ HandleException(ResumeFromException* rfe) return; } - JitFrameLayout* current = iter.isScripted() ? iter.jsFrame() : nullptr; + JitFrameLayout* current = frame.isScripted() ? frame.jsFrame() : nullptr; ++iter; @@ -738,7 +742,7 @@ HandleException(ResumeFromException* rfe) } } - rfe->stackPointer = iter.fp(); + rfe->stackPointer = iter.asJSJit().fp(); } // Turns a JitFrameLayout into an ExitFrameLayout. Note that it has to be a @@ -1257,10 +1261,8 @@ TraceRectifierFrame(JSTracer* trc, const JitFrameIterator& frame) } static void -TraceJitActivation(JSTracer* trc, const JitActivationIterator& activations) +TraceJitActivation(JSTracer* trc, JitActivation* activation) { - JitActivation* activation = activations->asJit(); - #ifdef CHECK_OSIPOINT_REGISTERS if (JitOptions.checkOsiPointRegisters) { // GC can modify spilled registers, breaking our register checks. @@ -1273,7 +1275,7 @@ TraceJitActivation(JSTracer* trc, const JitActivationIterator& activations) activation->traceRematerializedFrames(trc); activation->traceIonRecovery(trc); - for (JitFrameIterator frames(activations); !frames.done(); ++frames) { + for (JitFrameIterator frames(activation); !frames.done(); ++frames) { switch (frames.type()) { case JitFrame_Exit: TraceJitExitFrame(trc, frames); @@ -1302,11 +1304,22 @@ TraceJitActivation(JSTracer* trc, const JitActivationIterator& activations) } } +static void +TraceWasmActivation(JSTracer* trc, WasmActivation* act) +{ + for (wasm::WasmFrameIter iter(act); !iter.done(); ++iter) + iter.instance()->trace(trc); +} + void TraceJitActivations(JSContext* cx, const CooperatingContext& target, JSTracer* trc) { - for (JitActivationIterator activations(cx, target); !activations.done(); ++activations) - TraceJitActivation(trc, activations); + for (ActivationIterator activations(cx, target); !activations.done(); ++activations) { + if (activations->isJit()) + TraceJitActivation(trc, activations->asJit()); + if (activations->isWasm()) + TraceWasmActivation(trc, activations->asWasm()); + } } void @@ -1316,9 +1329,9 @@ UpdateJitActivationsForMinorGC(JSRuntime* rt, JSTracer* trc) JSContext* cx = TlsContext.get(); for (const CooperatingContext& target : rt->cooperatingContexts()) { for (JitActivationIterator activations(cx, target); !activations.done(); ++activations) { - for (JitFrameIterator frames(activations); !frames.done(); ++frames) { - if (frames.type() == JitFrame_IonJS) - UpdateIonJSFrameForMinorGC(trc, frames); + for (OnlyJSJitFrameIter iter(activations); !iter.done(); ++iter) { + if (iter.frame().type() == JitFrame_IonJS) + UpdateIonJSFrameForMinorGC(trc, iter.frame()); } } } @@ -1331,28 +1344,30 @@ GetPcScript(JSContext* cx, JSScript** scriptRes, jsbytecode** pcRes) // Recover the return address so that we can look it up in the // PcScriptCache, as script/pc computation is expensive. - JitActivationIterator iter(cx); - JitFrameIterator it(iter); + JitActivationIterator actIter(cx); + OnlyJSJitFrameIter it(actIter); uint8_t* retAddr; - if (it.isExitFrame()) { + if (it.frame().isExitFrame()) { ++it; // Skip rectifier frames. - if (it.isRectifier()) { + if (it.frame().isRectifier()) { ++it; - MOZ_ASSERT(it.isBaselineStub() || it.isBaselineJS() || it.isIonJS()); + MOZ_ASSERT(it.frame().isBaselineStub() || + it.frame().isBaselineJS() || + it.frame().isIonJS()); } // Skip Baseline/Ion stub and IC call frames. - if (it.isBaselineStub()) { + if (it.frame().isBaselineStub()) { ++it; - MOZ_ASSERT(it.isBaselineJS()); - } else if (it.isIonICCall()) { + MOZ_ASSERT(it.frame().isBaselineJS()); + } else if (it.frame().isIonICCall()) { ++it; - MOZ_ASSERT(it.isIonJS()); + MOZ_ASSERT(it.frame().isIonJS()); } - MOZ_ASSERT(it.isBaselineJS() || it.isIonJS()); + MOZ_ASSERT(it.frame().isBaselineJS() || it.frame().isIonJS()); // Don't use the return address if the BaselineFrame has an override pc. // The override pc is cheap to get, so we won't benefit from the cache, @@ -1360,15 +1375,15 @@ GetPcScript(JSContext* cx, JSScript** scriptRes, jsbytecode** pcRes) // Moreover, sometimes when an override pc is present during exception // handling, the return address is set to nullptr as a sanity check, // since we do not return to the frame that threw the exception. - if (!it.isBaselineJS() || !it.baselineFrame()->hasOverridePc()) { - retAddr = it.returnAddressToFp(); + if (!it.frame().isBaselineJS() || !it.frame().baselineFrame()->hasOverridePc()) { + retAddr = it.frame().returnAddressToFp(); MOZ_ASSERT(retAddr); } else { retAddr = nullptr; } } else { - MOZ_ASSERT(it.isBailoutJS()); - retAddr = it.returnAddress(); + MOZ_ASSERT(it.frame().isBailoutJS()); + retAddr = it.frame().returnAddress(); } uint32_t hash; @@ -1388,13 +1403,13 @@ GetPcScript(JSContext* cx, JSScript** scriptRes, jsbytecode** pcRes) // Lookup failed: undertake expensive process to recover the innermost inlined frame. jsbytecode* pc = nullptr; - if (it.isIonJS() || it.isBailoutJS()) { - InlineFrameIterator ifi(cx, &it); + if (it.frame().isIonJS() || it.frame().isBailoutJS()) { + InlineFrameIterator ifi(cx, &it.frame()); *scriptRes = ifi.script(); pc = ifi.pc(); } else { - MOZ_ASSERT(it.isBaselineJS()); - it.baselineScriptAndPc(scriptRes, &pc); + MOZ_ASSERT(it.frame().isBaselineJS()); + it.frame().baselineScriptAndPc(scriptRes, &pc); } if (pcRes) @@ -2659,7 +2674,8 @@ JitProfilingFrameIterator::moveToNextFrame(CommonFrameLayout* frame) } if (prevType == JitFrame_Entry) { - // No previous frame, set to null to indicate that JitFrameIterator is done() + // No previous frame, set to null to indicate that OnlyJSJitFrameIter is + // done(). returnAddressToFp_ = nullptr; fp_ = nullptr; type_ = JitFrame_Entry; @@ -2694,11 +2710,12 @@ void AssertJitStackInvariants(JSContext* cx) { for (JitActivationIterator activations(cx); !activations.done(); ++activations) { - JitFrameIterator frames(activations); + JitFrameIter iter(activations.activation()); size_t prevFrameSize = 0; size_t frameSize = 0; bool isScriptedCallee = false; - for (; !frames.done(); ++frames) { + for (; !iter.done(); ++iter) { + const JitFrameIterator& frames = iter.asJSJit(); size_t calleeFp = reinterpret_cast(frames.fp()); size_t callerFp = reinterpret_cast(frames.prevFp()); MOZ_ASSERT(callerFp >= calleeFp); @@ -2751,14 +2768,12 @@ AssertJitStackInvariants(JSContext* cx) "The baseline stub restores the stack alignment"); } - isScriptedCallee = false - || frames.isScripted() - || frames.type() == JitFrame_Rectifier; + isScriptedCallee = frames.isScripted() || frames.type() == JitFrame_Rectifier; } - MOZ_RELEASE_ASSERT(frames.type() == JitFrame_Entry, + MOZ_RELEASE_ASSERT(iter.asJSJit().type() == JitFrame_Entry, "The first frame of a Jit activation should be an entry frame"); - MOZ_RELEASE_ASSERT(reinterpret_cast(frames.fp()) % JitStackAlignment == 0, + MOZ_RELEASE_ASSERT(reinterpret_cast(iter.asJSJit().fp()) % JitStackAlignment == 0, "The entry frame should be properly aligned"); } } diff --git a/js/src/jit/JitFrames.h b/js/src/jit/JitFrames.h index b55efc96bf26..034581d1e1b0 100644 --- a/js/src/jit/JitFrames.h +++ b/js/src/jit/JitFrames.h @@ -311,17 +311,17 @@ MakeFrameDescriptor(uint32_t frameSize, FrameType type, uint32_t headerSize) inline JSScript* GetTopJitJSScript(JSContext* cx) { - JitFrameIterator iter(cx); - MOZ_ASSERT(iter.type() == JitFrame_Exit); - ++iter; + JitFrameIterator frame(cx); + MOZ_ASSERT(frame.type() == JitFrame_Exit); + ++frame; - if (iter.isBaselineStub()) { - ++iter; - MOZ_ASSERT(iter.isBaselineJS()); + if (frame.isBaselineStub()) { + ++frame; + MOZ_ASSERT(frame.isBaselineJS()); } - MOZ_ASSERT(iter.isScripted()); - return iter.script(); + MOZ_ASSERT(frame.isScripted()); + return frame.script(); } #ifdef JS_CODEGEN_MIPS32 diff --git a/js/src/jit/SharedIC.cpp b/js/src/jit/SharedIC.cpp index 7dbae9bfd024..42637d16e44a 100644 --- a/js/src/jit/SharedIC.cpp +++ b/js/src/jit/SharedIC.cpp @@ -664,8 +664,8 @@ HandleScript SharedStubInfo::outerScript(JSContext* cx) { if (!outerScript_) { - js::jit::JitActivationIterator iter(cx); - JitFrameIterator it(iter); + js::jit::JitActivationIterator actIter(cx); + JitFrameIterator it(actIter->asJit()); MOZ_ASSERT(it.isExitFrame()); ++it; MOZ_ASSERT(it.isIonJS()); diff --git a/js/src/jit/VMFunctions.cpp b/js/src/jit/VMFunctions.cpp index 5d0ea21032dc..059c28862434 100644 --- a/js/src/jit/VMFunctions.cpp +++ b/js/src/jit/VMFunctions.cpp @@ -365,10 +365,10 @@ ArrayPushDense(JSContext* cx, HandleObject obj, HandleValue v, uint32_t* length) // possible the SetOrExtendAnyBoxedOrUnboxedDenseElements call already // invalidated the IonScript. JitFrameIterator::ionScript works when the // script is invalidated so we use that instead. - JitFrameIterator it(cx); - MOZ_ASSERT(it.type() == JitFrame_Exit); - ++it; - IonScript* ionScript = it.ionScript(); + JitFrameIterator frame(cx); + MOZ_ASSERT(frame.type() == JitFrame_Exit); + ++frame; + IonScript* ionScript = frame.ionScript(); JS::AutoValueArray<3> argv(cx); AutoDetectInvalidation adi(cx, argv[0], ionScript); @@ -1256,12 +1256,12 @@ RecompileImpl(JSContext* cx, bool force) { MOZ_ASSERT(cx->currentlyRunningInJit()); JitActivationIterator activations(cx); - JitFrameIterator iter(activations); + JitFrameIterator frame(activations->asJit()); - MOZ_ASSERT(iter.type() == JitFrame_Exit); - ++iter; + MOZ_ASSERT(frame.type() == JitFrame_Exit); + ++frame; - RootedScript script(cx, iter.script()); + RootedScript script(cx, frame.script()); MOZ_ASSERT(script->hasIonScript()); if (!IsIonEnabled(cx)) diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index aeb28ef2a627..309693317705 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -7402,8 +7402,8 @@ GetScriptedCallerActivationFast(JSContext* cx, Activation** activation) *activation = activationIter.activation(); if (activationIter->isJit()) { - for (jit::JitFrameIterator iter(activationIter); !iter.done(); ++iter) { - if (iter.isScripted() && !iter.script()->selfHosted()) + for (OnlyJSJitFrameIter iter(activationIter); !iter.done(); ++iter) { + if (iter.frame().isScripted() && !iter.frame().script()->selfHosted()) return true; } } else if (activationIter->isInterpreter()) { diff --git a/js/src/jscntxt.cpp b/js/src/jscntxt.cpp index 867dc3ba149a..bccdae2845a9 100644 --- a/js/src/jscntxt.cpp +++ b/js/src/jscntxt.cpp @@ -1330,7 +1330,7 @@ JSContext::JSContext(JSRuntime* runtime, const JS::ContextOptions& options) throwing(false), overRecursed_(false), propagatingForcedReturn_(false), - liveVolatileJitFrameIterators_(nullptr), + liveVolatileJitFrameIter_(nullptr), reportGranularity(JS_DEFAULT_JITREPORT_GRANULARITY), resolvingList(nullptr), #ifdef DEBUG diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index 699069f736e2..96bf9d30d152 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -33,7 +33,7 @@ class AutoCompartment; namespace jit { class JitContext; -class DebugModeOSRVolatileJitFrameIterator; +class DebugModeOSRVolatileJitFrameIter; } // namespace jit typedef HashSet ShapeSet; @@ -300,7 +300,7 @@ struct JSContext : public JS::RootingContext, } friend class JS::AutoSaveExceptionState; - friend class js::jit::DebugModeOSRVolatileJitFrameIterator; + friend class js::jit::DebugModeOSRVolatileJitFrameIter; friend void js::ReportOverRecursed(JSContext*, unsigned errorNumber); // Returns to the embedding to allow other cooperative threads to run. We @@ -636,7 +636,8 @@ struct JSContext : public JS::RootingContext, // A stack of live iterators that need to be updated in case of debug mode // OSR. - js::ThreadLocalData liveVolatileJitFrameIterators_; + js::ThreadLocalData + liveVolatileJitFrameIter_; public: js::ThreadLocalData reportGranularity; /* see vm/Probes.h */ diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index ba1d60b4916f..ba363baa3cd4 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -3649,7 +3649,7 @@ js::DumpInterpreterFrame(JSContext* cx, FILE* fp, InterpreterFrame* start) return; } } else { - while (!i.done() && !i.isJit() && i.interpFrame() != start) + while (!i.done() && !i.isJSJit() && i.interpFrame() != start) ++i; if (i.done()) { @@ -3660,7 +3660,7 @@ js::DumpInterpreterFrame(JSContext* cx, FILE* fp, InterpreterFrame* start) } for (; !i.done(); ++i) { - if (i.isJit()) + if (i.isJSJit()) fprintf(fp, "JIT frame\n"); else fprintf(fp, "InterpreterFrame at %p\n", (void*) i.interpFrame()); @@ -3686,7 +3686,7 @@ js::DumpInterpreterFrame(JSContext* cx, FILE* fp, InterpreterFrame* start) } if (i.isFunctionFrame()) MaybeDumpValue("this", i.thisArgument(cx), fp); - if (!i.isJit()) { + if (!i.isJSJit()) { fprintf(fp, " rval: "); dumpValue(i.interpFrame()->returnValue(), fp); fputc('\n', fp); @@ -3695,7 +3695,7 @@ js::DumpInterpreterFrame(JSContext* cx, FILE* fp, InterpreterFrame* start) fprintf(fp, " flags:"); if (i.isConstructing()) fprintf(fp, " constructing"); - if (!i.isJit() && i.interpFrame()->isDebuggerEvalFrame()) + if (!i.isJSJit() && i.interpFrame()->isDebuggerEvalFrame()) fprintf(fp, " debugger eval"); if (i.isEvalFrame()) fprintf(fp, " eval"); diff --git a/js/src/vm/Debugger.cpp b/js/src/vm/Debugger.cpp index fb8252934159..2a28ebb6d6b8 100644 --- a/js/src/vm/Debugger.cpp +++ b/js/src/vm/Debugger.cpp @@ -2630,14 +2630,15 @@ UpdateExecutionObservabilityOfScriptsInZone(JSContext* cx, Zone* zone, if (actIter->compartment()->zone() != zone) continue; - for (JitFrameIterator iter(actIter); !iter.done(); ++iter) { - switch (iter.type()) { + for (OnlyJSJitFrameIter iter(actIter); !iter.done(); ++iter) { + const jit::JitFrameIterator& frame = iter.frame(); + switch (frame.type()) { case JitFrame_BaselineJS: - MarkBaselineScriptActiveIfObservable(iter.script(), obs); + MarkBaselineScriptActiveIfObservable(frame.script(), obs); break; case JitFrame_IonJS: - MarkBaselineScriptActiveIfObservable(iter.script(), obs); - for (InlineFrameIterator inlineIter(cx, &iter); inlineIter.more(); ++inlineIter) + MarkBaselineScriptActiveIfObservable(frame.script(), obs); + for (InlineFrameIterator inlineIter(cx, &frame); inlineIter.more(); ++inlineIter) MarkBaselineScriptActiveIfObservable(inlineIter.script(), obs); break; default:; @@ -7642,11 +7643,11 @@ UpdateFrameIterPc(FrameIter& iter) while (activationIter.activation() != activation) ++activationIter; - jit::JitFrameIterator jitIter(activationIter); - while (!jitIter.isIonJS() || jitIter.jsFrame() != jsFrame) + OnlyJSJitFrameIter jitIter(activationIter); + while (!jitIter.frame().isIonJS() || jitIter.frame().jsFrame() != jsFrame) ++jitIter; - jit::InlineFrameIterator ionInlineIter(cx, &jitIter); + jit::InlineFrameIterator ionInlineIter(cx, &jitIter.frame()); while (ionInlineIter.frameNo() != frame->frameNo()) ++ionInlineIter; @@ -8185,10 +8186,10 @@ static void DebuggerFrame_trace(JSTracer* trc, JSObject* obj) { OnStepHandler* onStepHandler = obj->as().onStepHandler(); - if (onStepHandler) + if (onStepHandler) onStepHandler->trace(trc); OnPopHandler* onPopHandler = obj->as().onPopHandler(); - if (onPopHandler) + if (onPopHandler) onPopHandler->trace(trc); } diff --git a/js/src/vm/Stack-inl.h b/js/src/vm/Stack-inl.h index 85fc08249574..c7796dfb5060 100644 --- a/js/src/vm/Stack-inl.h +++ b/js/src/vm/Stack-inl.h @@ -389,16 +389,16 @@ FrameIter::unaliasedForEachActual(JSContext* cx, Op op) { switch (data_.state_) { case DONE: - case WASM: break; case INTERP: interpFrame()->unaliasedForEachActual(op); return; case JIT: - if (data_.jitFrames_.isIonJS()) { - jit::MaybeReadFallback recover(cx, activation()->asJit(), &data_.jitFrames_); + MOZ_ASSERT(isJSJit()); + if (jsJitFrame().isIonJS()) { + jit::MaybeReadFallback recover(cx, activation()->asJit(), &jsJitFrame()); ionInlineFrames_.unaliasedForEachActual(cx, op, jit::ReadFrame_Actuals, recover); - } else if (data_.jitFrames_.isBailoutJS()) { + } else if (jsJitFrame().isBailoutJS()) { // :TODO: (Bug 1070962) If we are introspecting the frame which is // being bailed, then we might be in the middle of recovering // instructions. Stacking computeInstructionResults implies that we @@ -408,8 +408,8 @@ FrameIter::unaliasedForEachActual(JSContext* cx, Op op) jit::MaybeReadFallback fallback; ionInlineFrames_.unaliasedForEachActual(cx, op, jit::ReadFrame_Actuals, fallback); } else { - MOZ_ASSERT(data_.jitFrames_.isBaselineJS()); - data_.jitFrames_.unaliasedForEachActual(op, jit::ReadFrame_Actuals); + MOZ_ASSERT(jsJitFrame().isBaselineJS()); + jsJitFrame().unaliasedForEachActual(op, jit::ReadFrame_Actuals); } return; } @@ -1034,11 +1034,11 @@ FrameIter::hasCachedSavedFrame() const if (hasUsableAbstractFramePtr()) return abstractFramePtr().hasCachedSavedFrame(); - MOZ_ASSERT(data_.jitFrames_.isIonScripted()); + MOZ_ASSERT(jsJitFrame().isIonScripted()); // SavedFrame caching is done at the physical frame granularity (rather than // for each inlined frame) for ion. Therefore, it is impossible to have a // cached SavedFrame if this frame is not a physical frame. - return isPhysicalIonFrame() && data_.jitFrames_.current()->hasCachedSavedFrame(); + return isPhysicalIonFrame() && jsJitFrame().current()->hasCachedSavedFrame(); } inline void @@ -1052,7 +1052,7 @@ FrameIter::setHasCachedSavedFrame() } MOZ_ASSERT(isPhysicalIonFrame()); - data_.jitFrames_.current()->setHasCachedSavedFrame(); + jsJitFrame().current()->setHasCachedSavedFrame(); } } /* namespace js */ diff --git a/js/src/vm/Stack.cpp b/js/src/vm/Stack.cpp index acb44ed4a98d..f78d2043c8cb 100644 --- a/js/src/vm/Stack.cpp +++ b/js/src/vm/Stack.cpp @@ -490,6 +490,98 @@ InterpreterStack::pushExecuteFrame(JSContext* cx, HandleScript script, const Val /*****************************************************************************/ +JitFrameIter::JitFrameIter(const JitFrameIter& another) +{ + *this = another; +} + +JitFrameIter& +JitFrameIter::operator=(const JitFrameIter& another) +{ + MOZ_ASSERT(this != &another); + + if (isSome()) + iter_.destroy(); + if (!another.isSome()) + return *this; + + if (another.isJSJit()) { + iter_.construct(another.asJSJit()); + } else { + MOZ_ASSERT(another.isWasm()); + iter_.construct(another.asWasm()); + } + + return *this; +} + +JitFrameIter::JitFrameIter(Activation* act) +{ + MOZ_ASSERT(act->isJit() || act->isWasm()); + if (act->isJit()) { + iter_.construct(act->asJit()); + } else { + MOZ_ASSERT(act->isWasm()); + iter_.construct(act->asWasm()); + } +} + +void +JitFrameIter::skipNonScriptedJSFrames() +{ + if (isJSJit()) { + // Stop at the first scripted frame. + jit::JitFrameIterator& frames = asJSJit(); + while (!frames.isScripted() && !frames.done()) + ++frames; + } +} + +bool +JitFrameIter::done() const +{ + if (!isSome()) + return true; + if (isJSJit()) + return asJSJit().done(); + if (isWasm()) + return asWasm().done(); + MOZ_CRASH("unhandled case"); +} + +void +JitFrameIter::operator++() +{ + MOZ_ASSERT(isSome()); + if (isJSJit()) { + ++asJSJit(); + return; + } + if (isWasm()) { + ++asWasm(); + return; + } + MOZ_CRASH("unhandled case"); +} + +OnlyJSJitFrameIter::OnlyJSJitFrameIter(Activation* act) + : JitFrameIter(act) +{ + settle(); +} + +OnlyJSJitFrameIter::OnlyJSJitFrameIter(JSContext* cx) + : OnlyJSJitFrameIter(cx->activation()) +{ +} + +OnlyJSJitFrameIter::OnlyJSJitFrameIter(const ActivationIterator& iter) + : OnlyJSJitFrameIter(iter->asJit()) +{ +} + +/*****************************************************************************/ + void FrameIter::popActivation() { @@ -533,35 +625,18 @@ FrameIter::settleOnActivation() } } - if (activation->isJit()) { - data_.jitFrames_ = jit::JitFrameIterator(data_.activations_); - - // Stop at the first scripted frame. - while (!data_.jitFrames_.isScripted() && !data_.jitFrames_.done()) - ++data_.jitFrames_; - - // It's possible to have an JitActivation with no scripted frames, - // for instance if we hit an over-recursion during bailout. + if (activation->isJit() || activation->isWasm()) { + data_.jitFrames_ = JitFrameIter(activation); + data_.jitFrames_.skipNonScriptedJSFrames(); if (data_.jitFrames_.done()) { + // It's possible to have an JitActivation with no scripted + // frames, for instance if we hit an over-recursion during + // bailout. ++data_.activations_; continue; } - - nextJitFrame(); data_.state_ = JIT; - return; - } - - if (activation->isWasm()) { - data_.wasmFrames_ = wasm::WasmFrameIter(data_.activations_->asWasm()); - - if (data_.wasmFrames_.done()) { - ++data_.activations_; - continue; - } - - data_.pc_ = nullptr; - data_.state_ = WASM; + nextJitFrame(); return; } @@ -596,9 +671,7 @@ FrameIter::Data::Data(JSContext* cx, DebuggerEvalOption debuggerEvalOption, pc_(nullptr), interpFrames_(nullptr), activations_(cx), - jitFrames_(), - ionInlineFrameNo_(0), - wasmFrames_() + ionInlineFrameNo_(0) { } @@ -611,9 +684,7 @@ FrameIter::Data::Data(JSContext* cx, const CooperatingContext& target, pc_(nullptr), interpFrames_(nullptr), activations_(cx, target), - jitFrames_(), - ionInlineFrameNo_(0), - wasmFrames_() + ionInlineFrameNo_(0) { } @@ -626,8 +697,7 @@ FrameIter::Data::Data(const FrameIter::Data& other) interpFrames_(other.interpFrames_), activations_(other.activations_), jitFrames_(other.jitFrames_), - ionInlineFrameNo_(other.ionInlineFrameNo_), - wasmFrames_(other.wasmFrames_) + ionInlineFrameNo_(other.ionInlineFrameNo_) { } @@ -660,18 +730,16 @@ FrameIter::FrameIter(JSContext* cx, DebuggerEvalOption debuggerEvalOption, FrameIter::FrameIter(const FrameIter& other) : data_(other.data_), - ionInlineFrames_(other.data_.cx_, - data_.jitFrames_.isIonScripted() ? &other.ionInlineFrames_ : nullptr) + ionInlineFrames_(other.data_.cx_, isIonScripted() ? &other.ionInlineFrames_ : nullptr) { } FrameIter::FrameIter(const Data& data) : data_(data), - ionInlineFrames_(data.cx_, data_.jitFrames_.isIonScripted() ? &data_.jitFrames_ : nullptr) + ionInlineFrames_(data.cx_, isIonScripted() ? &jsJitFrame() : nullptr) { MOZ_ASSERT(data.cx_); - - if (data_.jitFrames_.isIonScripted()) { + if (isIonScripted()) { while (ionInlineFrames_.frameNo() != data.ionInlineFrameNo_) ++ionInlineFrames_; } @@ -680,47 +748,44 @@ FrameIter::FrameIter(const Data& data) void FrameIter::nextJitFrame() { - if (data_.jitFrames_.isIonScripted()) { - ionInlineFrames_.resetOn(&data_.jitFrames_); - data_.pc_ = ionInlineFrames_.pc(); - } else { - MOZ_ASSERT(data_.jitFrames_.isBaselineJS()); - data_.jitFrames_.baselineScriptAndPc(nullptr, &data_.pc_); + MOZ_ASSERT(data_.jitFrames_.isSome()); + + if (isJSJit()) { + if (jsJitFrame().isIonScripted()) { + ionInlineFrames_.resetOn(&jsJitFrame()); + data_.pc_ = ionInlineFrames_.pc(); + } else { + MOZ_ASSERT(jsJitFrame().isBaselineJS()); + jsJitFrame().baselineScriptAndPc(nullptr, &data_.pc_); + } + return; } + + MOZ_ASSERT(isWasm()); + data_.pc_ = nullptr; } void FrameIter::popJitFrame() { MOZ_ASSERT(data_.state_ == JIT); + MOZ_ASSERT(data_.jitFrames_.isSome()); - if (data_.jitFrames_.isIonScripted() && ionInlineFrames_.more()) { + if (isJSJit() && jsJitFrame().isIonScripted() && ionInlineFrames_.more()) { ++ionInlineFrames_; data_.pc_ = ionInlineFrames_.pc(); return; } ++data_.jitFrames_; - while (!data_.jitFrames_.done() && !data_.jitFrames_.isScripted()) - ++data_.jitFrames_; + data_.jitFrames_.skipNonScriptedJSFrames(); if (!data_.jitFrames_.done()) { nextJitFrame(); - return; - } - - popActivation(); -} - -void -FrameIter::popWasmFrame() -{ - MOZ_ASSERT(data_.state_ == WASM); - - ++data_.wasmFrames_; - data_.pc_ = nullptr; - if (data_.wasmFrames_.done()) + } else { + data_.jitFrames_.reset(); popActivation(); + } } FrameIter& @@ -741,8 +806,6 @@ FrameIter::operator++() while (!hasUsableAbstractFramePtr() || abstractFramePtr() != eifPrev) { if (data_.state_ == JIT) popJitFrame(); - else if (data_.state_ == WASM) - popWasmFrame(); else popInterpreterFrame(); } @@ -754,9 +817,6 @@ FrameIter::operator++() case JIT: popJitFrame(); break; - case WASM: - popWasmFrame(); - break; } return *this; } @@ -768,7 +828,7 @@ FrameIter::copyData() const if (!data) return nullptr; - if (data && data_.jitFrames_.isIonScripted()) + if (data && isIonScripted()) data->ionInlineFrameNo_ = ionInlineFrames_.frameNo(); return data; } @@ -788,11 +848,12 @@ FrameIter::rawFramePtr() const switch (data_.state_) { case DONE: return nullptr; - case JIT: - return data_.jitFrames_.fp(); case INTERP: return interpFrame(); - case WASM: + case JIT: + if (isJSJit()) + return jsJitFrame().fp(); + MOZ_ASSERT(isWasm()); return nullptr; } MOZ_CRASH("Unexpected state"); @@ -806,7 +867,6 @@ FrameIter::compartment() const break; case INTERP: case JIT: - case WASM: return data_.activations_->compartment(); } MOZ_CRASH("Unexpected state"); @@ -821,11 +881,13 @@ FrameIter::isEvalFrame() const case INTERP: return interpFrame()->isEvalFrame(); case JIT: - if (data_.jitFrames_.isBaselineJS()) - return data_.jitFrames_.baselineFrame()->isEvalFrame(); - MOZ_ASSERT(!script()->isForEval()); - return false; - case WASM: + if (isJSJit()) { + if (jsJitFrame().isBaselineJS()) + return jsJitFrame().baselineFrame()->isEvalFrame(); + MOZ_ASSERT(!script()->isForEval()); + return false; + } + MOZ_ASSERT(isWasm()); return false; } MOZ_CRASH("Unexpected state"); @@ -841,10 +903,12 @@ FrameIter::isFunctionFrame() const case INTERP: return interpFrame()->isFunctionFrame(); case JIT: - if (data_.jitFrames_.isBaselineJS()) - return data_.jitFrames_.baselineFrame()->isFunctionFrame(); - return script()->functionNonDelazifying(); - case WASM: + if (isJSJit()) { + if (jsJitFrame().isBaselineJS()) + return jsJitFrame().baselineFrame()->isFunctionFrame(); + return script()->functionNonDelazifying(); + } + MOZ_ASSERT(isWasm()); return false; } MOZ_CRASH("Unexpected state"); @@ -858,11 +922,10 @@ FrameIter::functionDisplayAtom() const break; case INTERP: case JIT: + if (isWasm()) + return wasmFrame().functionDisplayAtom(); MOZ_ASSERT(isFunctionFrame()); return calleeTemplate()->displayAtom(); - case WASM: - MOZ_ASSERT(isWasm()); - return data_.wasmFrames_.functionDisplayAtom(); } MOZ_CRASH("Unexpected state"); @@ -873,7 +936,6 @@ FrameIter::scriptSource() const { switch (data_.state_) { case DONE: - case WASM: break; case INTERP: case JIT: @@ -891,9 +953,9 @@ FrameIter::filename() const break; case INTERP: case JIT: + if (isWasm()) + return wasmFrame().filename(); return script()->filename(); - case WASM: - return data_.wasmFrames_.filename(); } MOZ_CRASH("Unexpected state"); @@ -906,12 +968,11 @@ FrameIter::displayURL() const case DONE: break; case INTERP: - case JIT: { + case JIT: + if (isWasm()) + return wasmFrame().displayURL(); ScriptSource* ss = script()->scriptSource(); return ss->hasDisplayURL() ? ss->displayURL() : nullptr; - } - case WASM: - return data_.wasmFrames_.displayURL(); } MOZ_CRASH("Unexpected state"); } @@ -924,11 +985,12 @@ FrameIter::computeLine(uint32_t* column) const break; case INTERP: case JIT: + if (isWasm()) { + if (column) + *column = 0; + return wasmFrame().lineOrBytecode(); + } return PCToLineNumber(script(), pc(), column); - case WASM: - if (column) - *column = 0; - return data_.wasmFrames_.lineOrBytecode(); } MOZ_CRASH("Unexpected state"); @@ -942,9 +1004,9 @@ FrameIter::mutedErrors() const break; case INTERP: case JIT: + if (isWasm()) + return wasmFrame().mutedErrors(); return script()->mutedErrors(); - case WASM: - return data_.wasmFrames_.mutedErrors(); } MOZ_CRASH("Unexpected state"); } @@ -954,13 +1016,13 @@ FrameIter::isConstructing() const { switch (data_.state_) { case DONE: - case WASM: break; case JIT: - if (data_.jitFrames_.isIonScripted()) + MOZ_ASSERT(isJSJit()); + if (jsJitFrame().isIonScripted()) return ionInlineFrames_.isConstructing(); - MOZ_ASSERT(data_.jitFrames_.isBaselineJS()); - return data_.jitFrames_.isConstructing(); + MOZ_ASSERT(jsJitFrame().isBaselineJS()); + return jsJitFrame().isConstructing(); case INTERP: return interpFrame()->isConstructing(); } @@ -972,7 +1034,7 @@ bool FrameIter::ensureHasRematerializedFrame(JSContext* cx) { MOZ_ASSERT(isIon()); - return !!activation()->asJit()->getRematerializedFrame(cx, data_.jitFrames_); + return !!activation()->asJit()->getRematerializedFrame(cx, jsJitFrame()); } bool @@ -982,17 +1044,18 @@ FrameIter::hasUsableAbstractFramePtr() const case DONE: return false; case JIT: - if (data_.jitFrames_.isBaselineJS()) - return true; + if (isJSJit()) { + if (jsJitFrame().isBaselineJS()) + return true; - MOZ_ASSERT(data_.jitFrames_.isIonScripted()); - return !!activation()->asJit()->lookupRematerializedFrame(data_.jitFrames_.fp(), - ionInlineFrames_.frameNo()); - break; + MOZ_ASSERT(jsJitFrame().isIonScripted()); + return !!activation()->asJit()->lookupRematerializedFrame(jsJitFrame().fp(), + ionInlineFrames_.frameNo()); + } + MOZ_ASSERT(isWasm()); + return wasmFrame().debugEnabled(); case INTERP: return true; - case WASM: - return data_.wasmFrames_.debugEnabled(); } MOZ_CRASH("Unexpected state"); } @@ -1005,20 +1068,20 @@ FrameIter::abstractFramePtr() const case DONE: break; case JIT: { - if (data_.jitFrames_.isBaselineJS()) - return data_.jitFrames_.baselineFrame(); - - MOZ_ASSERT(data_.jitFrames_.isIonScripted()); - return activation()->asJit()->lookupRematerializedFrame(data_.jitFrames_.fp(), - ionInlineFrames_.frameNo()); - break; + if (isJSJit()) { + if (jsJitFrame().isBaselineJS()) + return jsJitFrame().baselineFrame(); + MOZ_ASSERT(isIonScripted()); + return activation()->asJit()->lookupRematerializedFrame(jsJitFrame().fp(), + ionInlineFrames_.frameNo()); + } + MOZ_ASSERT(isWasm()); + MOZ_ASSERT(wasmFrame().debugEnabled()); + return wasmFrame().debugFrame(); } case INTERP: MOZ_ASSERT(interpFrame()); return AbstractFramePtr(interpFrame()); - case WASM: - MOZ_ASSERT(data_.wasmFrames_.debugEnabled()); - return data_.wasmFrames_.debugFrame(); } MOZ_CRASH("Unexpected state"); } @@ -1027,7 +1090,6 @@ void FrameIter::updatePcQuadratic() { switch (data_.state_) { - case WASM: case DONE: break; case INTERP: { @@ -1045,8 +1107,8 @@ FrameIter::updatePcQuadratic() return; } case JIT: - if (data_.jitFrames_.isBaselineJS()) { - jit::BaselineFrame* frame = data_.jitFrames_.baselineFrame(); + if (jsJitFrame().isBaselineJS()) { + jit::BaselineFrame* frame = jsJitFrame().baselineFrame(); jit::JitActivation* activation = data_.activations_->asJit(); // activation's exitFP may be invalid, so create a new @@ -1056,13 +1118,13 @@ FrameIter::updatePcQuadratic() ++data_.activations_; // Look for the current frame. - data_.jitFrames_ = jit::JitFrameIterator(data_.activations_); - while (!data_.jitFrames_.isBaselineJS() || data_.jitFrames_.baselineFrame() != frame) + data_.jitFrames_ = JitFrameIter(data_.activations_->asJit()); + while (!jsJitFrame().isBaselineJS() || jsJitFrame().baselineFrame() != frame) ++data_.jitFrames_; // Update the pc. - MOZ_ASSERT(data_.jitFrames_.baselineFrame() == frame); - data_.jitFrames_.baselineScriptAndPc(nullptr, &data_.pc_); + MOZ_ASSERT(jsJitFrame().baselineFrame() == frame); + jsJitFrame().baselineScriptAndPc(nullptr, &data_.pc_); return; } break; @@ -1073,17 +1135,16 @@ FrameIter::updatePcQuadratic() void FrameIter::wasmUpdateBytecodeOffset() { - MOZ_RELEASE_ASSERT(data_.state_ == WASM, "Unexpected state"); + MOZ_RELEASE_ASSERT(isWasm(), "Unexpected state"); - wasm::DebugFrame* frame = data_.wasmFrames_.debugFrame(); - WasmActivation* activation = data_.activations_->asWasm(); + wasm::DebugFrame* frame = wasmFrame().debugFrame(); // Relookup the current frame, updating the bytecode offset in the process. - data_.wasmFrames_ = wasm::WasmFrameIter(activation); - while (data_.wasmFrames_.debugFrame() != frame) - ++data_.wasmFrames_; + data_.jitFrames_ = JitFrameIter(data_.activations_->asWasm()); + while (wasmFrame().debugFrame() != frame) + ++data_.jitFrames_; - MOZ_ASSERT(data_.wasmFrames_.debugFrame() == frame); + MOZ_ASSERT(wasmFrame().debugFrame() == frame); } JSFunction* @@ -1091,15 +1152,14 @@ FrameIter::calleeTemplate() const { switch (data_.state_) { case DONE: - case WASM: break; case INTERP: MOZ_ASSERT(isFunctionFrame()); return &interpFrame()->callee(); case JIT: - if (data_.jitFrames_.isBaselineJS()) - return data_.jitFrames_.callee(); - MOZ_ASSERT(data_.jitFrames_.isIonScripted()); + if (jsJitFrame().isBaselineJS()) + return jsJitFrame().callee(); + MOZ_ASSERT(jsJitFrame().isIonScripted()); return ionInlineFrames_.calleeTemplate(); } MOZ_CRASH("Unexpected state"); @@ -1110,16 +1170,15 @@ FrameIter::callee(JSContext* cx) const { switch (data_.state_) { case DONE: - case WASM: break; case INTERP: return calleeTemplate(); case JIT: - if (data_.jitFrames_.isIonScripted()) { - jit::MaybeReadFallback recover(cx, activation()->asJit(), &data_.jitFrames_); + if (isIonScripted()) { + jit::MaybeReadFallback recover(cx, activation()->asJit(), &jsJitFrame()); return ionInlineFrames_.callee(recover); } - MOZ_ASSERT(data_.jitFrames_.isBaselineJS()); + MOZ_ASSERT(jsJitFrame().isBaselineJS()); return calleeTemplate(); } MOZ_CRASH("Unexpected state"); @@ -1161,17 +1220,15 @@ FrameIter::numActualArgs() const { switch (data_.state_) { case DONE: - case WASM: break; case INTERP: MOZ_ASSERT(isFunctionFrame()); return interpFrame()->numActualArgs(); case JIT: - if (data_.jitFrames_.isIonScripted()) + if (isIonScripted()) return ionInlineFrames_.numActualArgs(); - - MOZ_ASSERT(data_.jitFrames_.isBaselineJS()); - return data_.jitFrames_.numActualArgs(); + MOZ_ASSERT(jsJitFrame().isBaselineJS()); + return jsJitFrame().numActualArgs(); } MOZ_CRASH("Unexpected state"); } @@ -1195,15 +1252,17 @@ FrameIter::environmentChain(JSContext* cx) const case DONE: break; case JIT: - if (data_.jitFrames_.isIonScripted()) { - jit::MaybeReadFallback recover(cx, activation()->asJit(), &data_.jitFrames_); - return ionInlineFrames_.environmentChain(recover); + if (isJSJit()) { + if (isIonScripted()) { + jit::MaybeReadFallback recover(cx, activation()->asJit(), &jsJitFrame()); + return ionInlineFrames_.environmentChain(recover); + } + return jsJitFrame().baselineFrame()->environmentChain(); } - return data_.jitFrames_.baselineFrame()->environmentChain(); + MOZ_ASSERT(isWasm()); + return wasmFrame().debugFrame()->environmentChain(); case INTERP: return interpFrame()->environmentChain(); - case WASM: - return data_.wasmFrames_.debugFrame()->environmentChain(); } MOZ_CRASH("Unexpected state"); } @@ -1239,14 +1298,13 @@ FrameIter::thisArgument(JSContext* cx) const switch (data_.state_) { case DONE: - case WASM: break; case JIT: - if (data_.jitFrames_.isIonScripted()) { - jit::MaybeReadFallback recover(cx, activation()->asJit(), &data_.jitFrames_); + if (isIonScripted()) { + jit::MaybeReadFallback recover(cx, activation()->asJit(), &jsJitFrame()); return ionInlineFrames_.thisArgument(recover); } - return data_.jitFrames_.baselineFrame()->thisArgument(); + return jsJitFrame().baselineFrame()->thisArgument(); case INTERP: return interpFrame()->thisArgument(); } @@ -1258,13 +1316,12 @@ FrameIter::newTarget() const { switch (data_.state_) { case DONE: - case WASM: break; case INTERP: return interpFrame()->newTarget(); case JIT: - MOZ_ASSERT(data_.jitFrames_.isBaselineJS()); - return data_.jitFrames_.baselineFrame()->newTarget(); + MOZ_ASSERT(jsJitFrame().isBaselineJS()); + return jsJitFrame().baselineFrame()->newTarget(); } MOZ_CRASH("Unexpected state"); } @@ -1274,11 +1331,10 @@ FrameIter::returnValue() const { switch (data_.state_) { case DONE: - case WASM: break; case JIT: - if (data_.jitFrames_.isBaselineJS()) - return data_.jitFrames_.baselineFrame()->returnValue(); + if (jsJitFrame().isBaselineJS()) + return jsJitFrame().baselineFrame()->returnValue(); break; case INTERP: return interpFrame()->returnValue(); @@ -1291,11 +1347,10 @@ FrameIter::setReturnValue(const Value& v) { switch (data_.state_) { case DONE: - case WASM: break; case JIT: - if (data_.jitFrames_.isBaselineJS()) { - data_.jitFrames_.baselineFrame()->setReturnValue(v); + if (jsJitFrame().isBaselineJS()) { + jsJitFrame().baselineFrame()->setReturnValue(v); return; } break; @@ -1311,15 +1366,14 @@ FrameIter::numFrameSlots() const { switch (data_.state_) { case DONE: - case WASM: break; case JIT: { - if (data_.jitFrames_.isIonScripted()) { + if (isIonScripted()) { return ionInlineFrames_.snapshotIterator().numAllocations() - - ionInlineFrames_.script()->nfixed(); + ionInlineFrames_.script()->nfixed(); } - jit::BaselineFrame* frame = data_.jitFrames_.baselineFrame(); - return frame->numValueSlots() - data_.jitFrames_.script()->nfixed(); + jit::BaselineFrame* frame = jsJitFrame().baselineFrame(); + return frame->numValueSlots() - jsJitFrame().script()->nfixed(); } case INTERP: MOZ_ASSERT(data_.interpFrames_.sp() >= interpFrame()->base()); @@ -1333,17 +1387,15 @@ FrameIter::frameSlotValue(size_t index) const { switch (data_.state_) { case DONE: - case WASM: break; case JIT: - if (data_.jitFrames_.isIonScripted()) { + if (isIonScripted()) { jit::SnapshotIterator si(ionInlineFrames_.snapshotIterator()); index += ionInlineFrames_.script()->nfixed(); return si.maybeReadAllocByIndex(index); } - - index += data_.jitFrames_.script()->nfixed(); - return *data_.jitFrames_.baselineFrame()->valueSlot(index); + index += jsJitFrame().script()->nfixed(); + return *jsJitFrame().baselineFrame()->valueSlot(index); case INTERP: return interpFrame()->base()[index]; } diff --git a/js/src/vm/Stack.h b/js/src/vm/Stack.h index 4f10c01bb49c..6b064ebdfa91 100644 --- a/js/src/vm/Stack.h +++ b/js/src/vm/Stack.h @@ -10,6 +10,7 @@ #include "mozilla/Atomics.h" #include "mozilla/HashFunctions.h" #include "mozilla/Maybe.h" +#include "mozilla/MaybeOneOf.h" #include "mozilla/MemoryReporting.h" #include "mozilla/Variant.h" @@ -1771,6 +1772,86 @@ class WasmActivation : public Activation void unwindExitFP(wasm::Frame* exitFP); }; +// A JitFrameIter can iterate over all kind of frames emitted by our code +// generators, be they composed of JS jit frames or wasm frames, interleaved or +// not, in any order. +// +// In the following class: +// - code generated for JS is referred to as JSJit. +// - code generated for wasm is referred to as Wasm. +// Also, Jit refers to any one of them. +// +// JitFrameIter uses JitFrameIterator to iterate over JSJit code or a +// WasmFrameIter to iterate over wasm code; only one of them is active at the +// time. When a sub-iterator is done, the JitFrameIter knows how to stop, move +// onto the next activation or move onto another kind of Jit code. +// +// For ease of use, there is also OnlyJSJitFrameIter, which skips all the +// non-JSJit frames. +// +// Note it is allowed to get a handle to the internal frame iterator via +// asJSJit() and asWasm(), but the user has to be careful not to have those be +// used after JitFrameIter leaves the scope or the operator++ is called. +// +// TODO(bug 1360211) In particular, this can handle the transition from wasm to +// ion and from ion to wasm, since these will be interleaved in the same +// JitActivation. +class JitFrameIter +{ + protected: + mozilla::MaybeOneOf iter_; + + public: + JitFrameIter() : iter_() {} + explicit JitFrameIter(Activation* activation); + + explicit JitFrameIter(const JitFrameIter& another); + JitFrameIter& operator=(const JitFrameIter& another); + + bool isSome() const { return !iter_.empty(); } + void reset() { MOZ_ASSERT(isSome()); iter_.destroy(); } + + bool isJSJit() const { return isSome() && iter_.constructed(); } + jit::JitFrameIterator& asJSJit() { return iter_.ref(); } + const jit::JitFrameIterator& asJSJit() const { return iter_.ref(); } + + bool isWasm() const { return isSome() && iter_.constructed(); } + wasm::WasmFrameIter& asWasm() { return iter_.ref(); } + const wasm::WasmFrameIter& asWasm() const { return iter_.ref(); } + + // Operations common to all frame iterators. + bool done() const; + void operator++(); + + // Operations which have an effect only on JIT frames. + void skipNonScriptedJSFrames(); +}; + +// A JitFrameIter that skips all the non-JSJit frames, skipping interleaved +// frames of any another kind. + +class OnlyJSJitFrameIter : public JitFrameIter +{ + void settle() { + while (!done() && !isJSJit()) + ++(*this); + } + + public: + explicit OnlyJSJitFrameIter(Activation* act); + explicit OnlyJSJitFrameIter(JSContext* cx); + explicit OnlyJSJitFrameIter(const ActivationIterator& cx); + + void operator++() { + JitFrameIter::operator++(); + settle(); + } + + const jit::JitFrameIterator& frame() const { + return asJSJit(); + } +}; + // A FrameIter walks over a context's stack of JS script activations, // abstracting over whether the JS scripts were running in the interpreter or // different modes of compiled code. @@ -1793,7 +1874,12 @@ class FrameIter public: enum DebuggerEvalOption { FOLLOW_DEBUGGER_EVAL_PREV_LINK, IGNORE_DEBUGGER_EVAL_PREV_LINK }; - enum State { DONE, INTERP, JIT, WASM }; + + enum State { + DONE, // when there are no more frames nor activations to unwind. + INTERP, // interpreter activation on the stack + JIT // jit or wasm activations on the stack + }; // Unlike ScriptFrameIter itself, ScriptFrameIter::Data can be allocated on // the heap, so this structure should not contain any GC things. @@ -1810,9 +1896,8 @@ class FrameIter InterpreterFrameIterator interpFrames_; ActivationIterator activations_; - jit::JitFrameIterator jitFrames_; + JitFrameIter jitFrames_; unsigned ionInlineFrameNo_; - wasm::WasmFrameIter wasmFrames_; Data(JSContext* cx, DebuggerEvalOption debuggerEvalOption, JSPrincipals* principals); Data(JSContext* cx, const CooperatingContext& target, DebuggerEvalOption debuggerEvalOption); @@ -1838,9 +1923,19 @@ class FrameIter JSCompartment* compartment() const; Activation* activation() const { return data_.activations_.activation(); } - bool isInterp() const { MOZ_ASSERT(!done()); return data_.state_ == INTERP; } - bool isJit() const { MOZ_ASSERT(!done()); return data_.state_ == JIT; } - bool isWasm() const { MOZ_ASSERT(!done()); return data_.state_ == WASM; } + bool isInterp() const { + MOZ_ASSERT(!done()); + return data_.state_ == INTERP; + } + bool isJSJit() const { + MOZ_ASSERT(!done()); + return data_.state_ == JIT && data_.jitFrames_.isJSJit(); + } + bool isWasm() const { + MOZ_ASSERT(!done()); + return data_.state_ == JIT && data_.jitFrames_.isWasm(); + } + inline bool isIon() const; inline bool isBaseline() const; inline bool isPhysicalIonFrame() const; @@ -1953,11 +2048,18 @@ class FrameIter Data data_; jit::InlineFrameIterator ionInlineFrames_; + const jit::JitFrameIterator& jsJitFrame() const { return data_.jitFrames_.asJSJit(); } + const wasm::WasmFrameIter& wasmFrame() const { return data_.jitFrames_.asWasm(); } + + jit::JitFrameIterator& jsJitFrame() { return data_.jitFrames_.asJSJit(); } + wasm::WasmFrameIter& wasmFrame() { return data_.jitFrames_.asWasm(); } + + bool isIonScripted() const { return isJSJit() && jsJitFrame().isIonScripted(); } + void popActivation(); void popInterpreterFrame(); void nextJitFrame(); void popJitFrame(); - void popWasmFrame(); void settleOnActivation(); }; @@ -2118,48 +2220,48 @@ inline JSScript* FrameIter::script() const { MOZ_ASSERT(!done()); + MOZ_ASSERT(hasScript()); if (data_.state_ == INTERP) return interpFrame()->script(); - MOZ_ASSERT(data_.state_ == JIT); - if (data_.jitFrames_.isIonJS()) + if (jsJitFrame().isIonJS()) return ionInlineFrames_.script(); - return data_.jitFrames_.script(); + return jsJitFrame().script(); } inline bool FrameIter::wasmDebugEnabled() const { MOZ_ASSERT(!done()); - MOZ_ASSERT(data_.state_ == WASM); - return data_.wasmFrames_.debugEnabled(); + MOZ_ASSERT(isWasm()); + return wasmFrame().debugEnabled(); } inline wasm::Instance* FrameIter::wasmInstance() const { MOZ_ASSERT(!done()); - MOZ_ASSERT(data_.state_ == WASM && wasmDebugEnabled()); - return data_.wasmFrames_.instance(); + MOZ_ASSERT(isWasm() && wasmDebugEnabled()); + return wasmFrame().instance(); } inline unsigned FrameIter::wasmBytecodeOffset() const { MOZ_ASSERT(!done()); - MOZ_ASSERT(data_.state_ == WASM); - return data_.wasmFrames_.lineOrBytecode(); + MOZ_ASSERT(isWasm()); + return wasmFrame().lineOrBytecode(); } inline bool FrameIter::isIon() const { - return isJit() && data_.jitFrames_.isIonJS(); + return isJSJit() && jsJitFrame().isIonJS(); } inline bool FrameIter::isBaseline() const { - return isJit() && data_.jitFrames_.isBaselineJS(); + return isJSJit() && jsJitFrame().isBaselineJS(); } inline InterpreterFrame* @@ -2172,8 +2274,8 @@ FrameIter::interpFrame() const inline bool FrameIter::isPhysicalIonFrame() const { - return isJit() && - data_.jitFrames_.isIonScripted() && + return isJSJit() && + jsJitFrame().isIonScripted() && ionInlineFrames_.frameNo() == 0; } @@ -2181,7 +2283,7 @@ inline jit::CommonFrameLayout* FrameIter::physicalIonFrame() const { MOZ_ASSERT(isPhysicalIonFrame()); - return data_.jitFrames_.current(); + return jsJitFrame().current(); } } /* namespace js */ diff --git a/js/src/vm/TraceLogging.cpp b/js/src/vm/TraceLogging.cpp index 4eb7907b20d3..4c1f7920a563 100644 --- a/js/src/vm/TraceLogging.cpp +++ b/js/src/vm/TraceLogging.cpp @@ -260,16 +260,16 @@ TraceLoggerThread::enable(JSContext* cx) int32_t engine = 0; if (act->isJit()) { - JitFrameIterator it(iter); + JitFrameIterator frame(iter->asJit()); - while (!it.isScripted() && !it.done()) - ++it; + while (!frame.isScripted() && !frame.done()) + ++frame; - MOZ_ASSERT(!it.done()); - MOZ_ASSERT(it.isIonJS() || it.isBaselineJS()); + MOZ_ASSERT(!frame.done()); + MOZ_ASSERT(frame.isIonJS() || frame.isBaselineJS()); - script = it.script(); - engine = it.isIonJS() ? TraceLogger_IonMonkey : TraceLogger_Baseline; + script = frame.script(); + engine = frame.isIonJS() ? TraceLogger_IonMonkey : TraceLogger_Baseline; } else if (act->isWasm()) { JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TRACELOGGER_ENABLE_FAIL, "not yet supported in wasm code"); diff --git a/js/src/wasm/WasmBuiltins.cpp b/js/src/wasm/WasmBuiltins.cpp index 5bd042e00ff8..0013ccc1c646 100644 --- a/js/src/wasm/WasmBuiltins.cpp +++ b/js/src/wasm/WasmBuiltins.cpp @@ -102,18 +102,18 @@ WasmHandleDebugTrap() MOZ_ASSERT(activation); JSContext* cx = activation->cx(); - WasmFrameIter iter(activation); - MOZ_ASSERT(iter.debugEnabled()); - const CallSite* site = iter.debugTrapCallsite(); + WasmFrameIter frame(activation); + MOZ_ASSERT(frame.debugEnabled()); + const CallSite* site = frame.debugTrapCallsite(); MOZ_ASSERT(site); if (site->kind() == CallSite::EnterFrame) { - if (!iter.instance()->enterFrameTrapsEnabled()) + if (!frame.instance()->enterFrameTrapsEnabled()) return true; - DebugFrame* frame = iter.debugFrame(); - frame->setIsDebuggee(); - frame->observe(cx); + DebugFrame* debugFrame = frame.debugFrame(); + debugFrame->setIsDebuggee(); + debugFrame->observe(cx); // TODO call onEnterFrame - JSTrapStatus status = Debugger::onEnterFrame(cx, frame); + JSTrapStatus status = Debugger::onEnterFrame(cx, debugFrame); if (status == JSTRAP_RETURN) { // Ignoring forced return (JSTRAP_RETURN) -- changing code execution // order is not yet implemented in the wasm baseline. @@ -124,17 +124,17 @@ WasmHandleDebugTrap() return status == JSTRAP_CONTINUE; } if (site->kind() == CallSite::LeaveFrame) { - DebugFrame* frame = iter.debugFrame(); - frame->updateReturnJSValue(); - bool ok = Debugger::onLeaveFrame(cx, frame, nullptr, true); - frame->leave(cx); + DebugFrame* debugFrame = frame.debugFrame(); + debugFrame->updateReturnJSValue(); + bool ok = Debugger::onLeaveFrame(cx, debugFrame, nullptr, true); + debugFrame->leave(cx); return ok; } - DebugFrame* frame = iter.debugFrame(); - DebugState& debug = iter.instance()->debug(); + DebugFrame* debugFrame = frame.debugFrame(); + DebugState& debug = frame.instance()->debug(); MOZ_ASSERT(debug.hasBreakpointTrapAtOffset(site->lineOrBytecode())); - if (debug.stepModeEnabled(frame->funcIndex())) { + if (debug.stepModeEnabled(debugFrame->funcIndex())) { RootedValue result(cx, UndefinedValue()); JSTrapStatus status = Debugger::onSingleStep(cx, &result); if (status == JSTRAP_RETURN) { @@ -176,6 +176,11 @@ WasmHandleThrow() // DebugFrame from being observed again after we just called onLeaveFrame // (which would lead to the frame being re-added to the map of live frames, // right as it becomes trash). + // + // TODO(bug 1360211): when JitActivation and WasmActivation get merged, + // we'll be able to switch to ion / other wasm state from here, and we'll + // need to do things differently. + WasmFrameIter iter(activation, WasmFrameIter::Unwind::True); MOZ_ASSERT(!iter.done()); diff --git a/js/src/wasm/WasmFrameIter.cpp b/js/src/wasm/WasmFrameIter.cpp index b7d9932ab06b..72f45d69d1a3 100644 --- a/js/src/wasm/WasmFrameIter.cpp +++ b/js/src/wasm/WasmFrameIter.cpp @@ -32,18 +32,6 @@ using mozilla::Swap; /*****************************************************************************/ // WasmFrameIter implementation -WasmFrameIter::WasmFrameIter() - : activation_(nullptr), - code_(nullptr), - callsite_(nullptr), - codeRange_(nullptr), - fp_(nullptr), - unwind_(Unwind::False), - unwoundAddressOfReturnAddress_(nullptr) -{ - MOZ_ASSERT(done()); -} - WasmFrameIter::WasmFrameIter(WasmActivation* activation, Unwind unwind) : activation_(activation), code_(nullptr), @@ -95,7 +83,7 @@ WasmFrameIter::operator++() { MOZ_ASSERT(!done()); - // When the iterator is set to Unwind::True, each time the iterator pops a + // When the iterator is set to unwind, each time the iterator pops a // frame, the WasmActivation is updated so that the just-popped frame // is no longer visible. This is necessary since Debugger::onLeaveFrame is // called before popping each frame and, once onLeaveFrame is called for a @@ -1004,17 +992,6 @@ ProfilingFrameIterator::label() const MOZ_CRASH("bad code range kind"); } -void -wasm::TraceActivations(JSContext* cx, const CooperatingContext& target, JSTracer* trc) -{ - for (ActivationIterator iter(cx, target); !iter.done(); ++iter) { - if (iter.activation()->isWasm()) { - for (WasmFrameIter fi(iter.activation()->asWasm()); !fi.done(); ++fi) - fi.instance()->trace(trc); - } - } -} - Instance* wasm::LookupFaultingInstance(WasmActivation* activation, void* pc, void* fp) { diff --git a/js/src/wasm/WasmFrameIter.h b/js/src/wasm/WasmFrameIter.h index fe6f3ad203f3..2f7889332d8f 100644 --- a/js/src/wasm/WasmFrameIter.h +++ b/js/src/wasm/WasmFrameIter.h @@ -41,14 +41,19 @@ struct Frame; struct FuncOffsets; struct CallableOffsets; -// Iterates over the frames of a single WasmActivation, called synchronously -// from C++ in the thread of the asm.js. +// Iterates over a linear group of wasm frames of a single WasmActivation, +// called synchronously from C++ in the wasm thread. It will stop at the first +// frame that is not of the same kind, or at the end of an activation. +// +// If you want to handle every kind of frames (including JS jit frames), use +// JitFrameIter. // // The one exception is that this iterator may be called from the interrupt // callback which may be called asynchronously from asm.js code; in this case, // the backtrace may not be correct. That being said, we try our best printing // an informative message to the user and at least the name of the innermost // function stack frame. + class WasmFrameIter { public: @@ -66,7 +71,7 @@ class WasmFrameIter void popFrame(); public: - explicit WasmFrameIter(); + // See comment above this class definition. explicit WasmFrameIter(WasmActivation* activation, Unwind unwind = Unwind::False); void operator++(); bool done() const; @@ -184,11 +189,6 @@ GenerateFunctionPrologue(jit::MacroAssembler& masm, unsigned framePushed, const void GenerateFunctionEpilogue(jit::MacroAssembler& masm, unsigned framePushed, FuncOffsets* offsets); -// Mark all instance objects live on the stack. - -void -TraceActivations(JSContext* cx, const CooperatingContext& target, JSTracer* trc); - // Given a fault at pc with register fp, return the faulting instance if there // is such a plausible instance, and otherwise null.