author | Jan de Mooij <jdemooij@mozilla.com> |
Fri, 19 Jul 2019 09:01:45 +0000 | |
changeset 483627 | 49a2da59aa3e33b3719c7953a98fac6c24b8a514 |
parent 483626 | ad5e0980c2731978f159e17622d6ba0aa1c05ea5 |
child 483628 | 499c8fa689ad7be86250f89d31a1cca5039abb11 |
push id | 36326 |
push user | btara@mozilla.com |
push date | Sun, 21 Jul 2019 21:59:35 +0000 |
treeherder | mozilla-central@48797c5119a4 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | iain |
bugs | 1566330, 1566189 |
milestone | 70.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/js/src/jit/BaselineCodeGen.cpp +++ b/js/src/jit/BaselineCodeGen.cpp @@ -125,19 +125,19 @@ bool BaselineCompilerHandler::init(JSCon bool BaselineCompiler::init() { if (!handler.init(cx)) { return false; } return true; } -bool BaselineCompilerHandler::appendRetAddrEntry(JSContext* cx, - RetAddrEntry::Kind kind, - uint32_t retOffset) { +bool BaselineCompilerHandler::recordCallRetAddr(JSContext* cx, + RetAddrEntry::Kind kind, + uint32_t retOffset) { uint32_t pcOffset = script_->pcToOffset(pc_); // Entries must be sorted by pcOffset for binary search to work. // See BaselineScript::retAddrEntryFromPCOffset. MOZ_ASSERT_IF(!retAddrEntries_.empty(), retAddrEntries_.back().pcOffset() <= pcOffset); // Similarly, entries must be sorted by return offset and this offset must be @@ -148,16 +148,36 @@ bool BaselineCompilerHandler::appendRetA if (!retAddrEntries_.emplaceBack(pcOffset, kind, CodeOffset(retOffset))) { ReportOutOfMemory(cx); return false; } return true; } +bool BaselineInterpreterHandler::recordCallRetAddr(JSContext* cx, + RetAddrEntry::Kind kind, + uint32_t retOffset) { + switch (kind) { + case RetAddrEntry::Kind::DebugPrologue: + callVMOffsets_.debugPrologueOffset = retOffset; + break; + case RetAddrEntry::Kind::DebugEpilogue: + callVMOffsets_.debugEpilogueOffset = retOffset; + break; + case RetAddrEntry::Kind::DebugAfterYield: + callVMOffsets_.debugAfterYieldOffset = retOffset; + break; + default: + break; + } + + return true; +} + bool BaselineCompiler::addPCMappingEntry(bool addIndexEntry) { // Don't add multiple entries for a single pc. size_t nentries = pcMappingEntries_.length(); uint32_t pcOffset = handler.script()->pcToOffset(handler.pc()); if (nentries > 0 && pcMappingEntries_[nentries - 1].pcOffset == pcOffset) { return true; } @@ -290,25 +310,24 @@ MethodStatus BaselineCompiler::compile() } if (pcEntries.oom()) { ReportOutOfMemory(cx); return Method_Error; } UniquePtr<BaselineScript> baselineScript( - BaselineScript::New( - script, bailoutPrologueOffset_.offset(), - warmUpCheckPrologueOffset_.offset(), debugOsrPrologueOffset_.offset(), - debugOsrEpilogueOffset_.offset(), - profilerEnterFrameToggleOffset_.offset(), - profilerExitFrameToggleOffset_.offset(), - handler.retAddrEntries().length(), pcMappingIndexEntries.length(), - pcEntries.length(), script->resumeOffsets().size(), - traceLoggerToggleOffsets_.length()), + BaselineScript::New(script, bailoutPrologueOffset_.offset(), + warmUpCheckPrologueOffset_.offset(), + profilerEnterFrameToggleOffset_.offset(), + profilerExitFrameToggleOffset_.offset(), + handler.retAddrEntries().length(), + pcMappingIndexEntries.length(), pcEntries.length(), + script->resumeOffsets().size(), + traceLoggerToggleOffsets_.length()), JS::DeletePolicy<BaselineScript>(cx->runtime())); if (!baselineScript) { ReportOutOfMemory(cx); return Method_Error; } baselineScript->setMethod(code); baselineScript->setTemplateEnvironment(templateEnv); @@ -768,17 +787,17 @@ bool BaselineCodeGen<Handler>::callVMInt Label ok; masm.branchTest32(Assembler::Zero, frame.addressOfFlags(), Imm32(BaselineFrame::HAS_OVERRIDE_PC), &ok); masm.assumeUnreachable("BaselineFrame shouldn't override pc after VM call"); masm.bind(&ok); } #endif - return handler.appendRetAddrEntry(cx, kind, callOffset); + return handler.recordCallRetAddr(cx, kind, callOffset); } template <typename Handler> template <typename Fn, Fn fn> bool BaselineCodeGen<Handler>::callVM(RetAddrEntry::Kind kind, CallVMPhase phase) { VMFunctionId fnId = VMFunctionToId<Fn, fn>::id; return callVMInternal(fnId, kind, phase); @@ -1150,23 +1169,17 @@ bool BaselineCodeGen<Handler>::emitDebug masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, &done); { masm.loadValue(frame.addressOfReturnValue(), JSReturnOperand); masm.jump(&return_); } masm.bind(&done); return true; }; - if (!emitDebugInstrumentation(ifDebuggee)) { - return false; - } - - debugOsrPrologueOffset_ = CodeOffset(masm.currentOffset()); - - return true; + return emitDebugInstrumentation(ifDebuggee); } template <> void BaselineCompilerCodeGen::emitInitFrameFields(Register nonFunctionEnv) { masm.store32(Imm32(0), frame.addressOfFlags()); if (handler.function()) { Register scratch = R0.scratchReg(); masm.loadFunctionFromCalleeToken(frame.addressOfCalleeToken(), scratch); @@ -1580,18 +1593,18 @@ bool BaselineCompiler::emitDebugTrap() { #ifdef DEBUG // Patchable call offset has to match the pc mapping offset. PCMappingEntry& entry = pcMappingEntries_.back(); MOZ_ASSERT((&offset)->offset() == entry.nativeOffset); #endif // Add a RetAddrEntry for the return offset -> pc mapping. - return handler.appendRetAddrEntry(cx, RetAddrEntry::Kind::DebugTrap, - masm.currentOffset()); + return handler.recordCallRetAddr(cx, RetAddrEntry::Kind::DebugTrap, + masm.currentOffset()); } #ifdef JS_TRACE_LOGGING template <> bool BaselineCompilerCodeGen::emitTraceLoggerEnter() { AllocatableRegisterSet regs(RegisterSet::Volatile()); Register loggerReg = regs.takeAnyGeneral(); Register scriptReg = regs.takeAnyGeneral(); @@ -6172,19 +6185,19 @@ bool BaselineCodeGen<Handler>::emitGener // generator returns. Label genStart, returnTarget; #ifdef JS_USE_LINK_REGISTER masm.call(&genStart); #else masm.callAndPushReturnAddress(&genStart); #endif - // Add a RetAddrEntry so the return offset -> pc mapping works. - if (!handler.appendRetAddrEntry(cx, RetAddrEntry::Kind::IC, - masm.currentOffset())) { + // Record the return address so the return offset -> pc mapping works. + if (!handler.recordCallRetAddr(cx, RetAddrEntry::Kind::IC, + masm.currentOffset())) { return false; } masm.jump(&returnTarget); masm.bind(&genStart); #ifdef JS_USE_LINK_REGISTER masm.pushReturnAddress(); #endif @@ -6770,20 +6783,16 @@ bool BaselineCodeGen<Handler>::emitProlo return false; } return true; } template <typename Handler> bool BaselineCodeGen<Handler>::emitEpilogue() { - // Record the offset of the epilogue, so we can do early return from - // Debugger handlers during on-stack recompile. - debugOsrEpilogueOffset_ = CodeOffset(masm.currentOffset()); - masm.bind(&return_); #ifdef JS_TRACE_LOGGING if (JS::TraceLoggerSupported() && !emitTraceLoggerExit()) { return false; } #endif @@ -7001,17 +7010,18 @@ bool BaselineInterpreterGenerator::emitI if (!opEpilogue(OP, OP##_LENGTH)) { \ return false; \ } \ } OPCODE_LIST(EMIT_OP) #undef EMIT_OP // External entry point to start interpreting bytecode ops. This is used for - // things like exception handling and OSR. + // things like exception handling and OSR. DebugModeOSR patches JIT frames to + // return here from the DebugTrapHandler. masm.bind(handler.interpretOpLabel()); interpretOpOffset_ = masm.currentOffset(); restoreInterpreterPCReg(); masm.jump(handler.interpretOpWithPCRegLabel()); // Second external entry point: this skips the debug trap for the first op // and is used by OSR. interpretOpNoDebugTrapOffset_ = masm.currentOffset(); @@ -7147,17 +7157,18 @@ bool BaselineInterpreterGenerator::gener vtune::MarkStub(code, "BaselineInterpreter"); #endif interpreter.init(code, interpretOpOffset_, interpretOpNoDebugTrapOffset_, profilerEnterFrameToggleOffset_.offset(), profilerExitFrameToggleOffset_.offset(), std::move(handler.debugInstrumentationOffsets()), std::move(debugTrapOffsets_), - std::move(handler.codeCoverageOffsets())); + std::move(handler.codeCoverageOffsets()), + handler.callVMOffsets()); } if (cx->runtime()->geckoProfiler().enabled()) { interpreter.toggleProfilerInstrumentation(true); } if (coverage::IsLCovEnabled()) { interpreter.toggleCodeCoverageInstrumentationUnchecked(true);
--- a/js/src/jit/BaselineCodeGen.h +++ b/js/src/jit/BaselineCodeGen.h @@ -283,24 +283,16 @@ class BaselineCodeGen { // Early Ion bailouts will enter at this address. This is after frame // construction and before environment chain is initialized. CodeOffset bailoutPrologueOffset_; // Baseline Interpreter can enter Baseline Compiler code at this address. This // is right after the warm-up counter check in the prologue. CodeOffset warmUpCheckPrologueOffset_; - // Baseline Debug OSR during prologue will enter at this address. This is - // right after where a debug prologue VM call would have returned. - CodeOffset debugOsrPrologueOffset_; - - // Baseline Debug OSR during epilogue will enter at this address. This is - // right after where a debug epilogue VM call would have returned. - CodeOffset debugOsrEpilogueOffset_; - uint32_t pushedBeforeCall_; #ifdef DEBUG bool inCall_; #endif // Whether any on stack arguments are modified. bool modifiesArguments_; @@ -580,18 +572,18 @@ class BaselineCompilerHandler { uint32_t icEntryIndex() const { return icEntryIndex_; } void moveToNextICEntry() { icEntryIndex_++; } BytecodeAnalysis& analysis() { return analysis_; } RetAddrEntryVector& retAddrEntries() { return retAddrEntries_; } - MOZ_MUST_USE bool appendRetAddrEntry(JSContext* cx, RetAddrEntry::Kind kind, - uint32_t retOffset); + MOZ_MUST_USE bool recordCallRetAddr(JSContext* cx, RetAddrEntry::Kind kind, + uint32_t retOffset); // If a script has more |nslots| than this the stack check must account // for these slots explicitly. bool mustIncludeSlotsInStackCheck() const { static constexpr size_t NumSlotsLimit = 128; return script()->nslots() > NumSlotsLimit; } @@ -673,16 +665,19 @@ class BaselineInterpreterHandler { using CodeOffsetVector = Vector<uint32_t, 0, SystemAllocPolicy>; CodeOffsetVector debugInstrumentationOffsets_; // Offsets of toggled jumps for code coverage instrumentation. CodeOffsetVector codeCoverageOffsets_; Label codeCoverageAtPrologueLabel_; Label codeCoverageAtPCLabel_; + // Offsets of some callVMs for BaselineDebugModeOSR. + BaselineInterpreter::CallVMOffsets callVMOffsets_; + public: using FrameInfoT = InterpreterFrameInfo; explicit BaselineInterpreterHandler(JSContext* cx, MacroAssembler& masm); InterpreterFrameInfo& frame() { return frame_; } Label* interpretOpLabel() { return &interpretOp_; } @@ -701,23 +696,23 @@ class BaselineInterpreterHandler { bool isDefinitelyLastOp() const { return false; } JSScript* maybeScript() const { return nullptr; } JSFunction* maybeFunction() const { return nullptr; } MOZ_MUST_USE bool addDebugInstrumentationOffset(CodeOffset offset) { return debugInstrumentationOffsets_.append(offset.offset()); } - // Interpreter doesn't need to keep track of RetAddrEntries, so this is a - // no-op. - MOZ_MUST_USE bool appendRetAddrEntry(JSContext* cx, RetAddrEntry::Kind kind, - uint32_t retOffset) { - return true; + const BaselineInterpreter::CallVMOffsets& callVMOffsets() const { + return callVMOffsets_; } + MOZ_MUST_USE bool recordCallRetAddr(JSContext* cx, RetAddrEntry::Kind kind, + uint32_t retOffset); + bool maybeIonCompileable() const { return true; } // The interpreter doesn't know the number of slots statically so we always // include them. bool mustIncludeSlotsInStackCheck() const { return true; } JSObject* maybeNoCloneSingletonObject() { return nullptr; } };
--- a/js/src/jit/BaselineDebugModeOSR.cpp +++ b/js/src/jit/BaselineDebugModeOSR.cpp @@ -17,105 +17,51 @@ #include "vm/TypeInference-inl.h" using namespace js; using namespace js::jit; struct DebugModeOSREntry { JSScript* script; BaselineScript* oldBaselineScript; - BaselineDebugModeOSRInfo* recompInfo; uint32_t pcOffset; RetAddrEntry::Kind frameKind; explicit DebugModeOSREntry(JSScript* script) : script(script), oldBaselineScript(script->baselineScript()), - recompInfo(nullptr), pcOffset(uint32_t(-1)), frameKind(RetAddrEntry::Kind::Invalid) {} DebugModeOSREntry(JSScript* script, uint32_t pcOffset) : script(script), oldBaselineScript(script->baselineScript()), - recompInfo(nullptr), pcOffset(pcOffset), frameKind(RetAddrEntry::Kind::Invalid) {} DebugModeOSREntry(JSScript* script, const RetAddrEntry& retAddrEntry) : script(script), oldBaselineScript(script->baselineScript()), - recompInfo(nullptr), pcOffset(retAddrEntry.pcOffset()), frameKind(retAddrEntry.kind()) { #ifdef DEBUG MOZ_ASSERT(pcOffset == retAddrEntry.pcOffset()); MOZ_ASSERT(frameKind == retAddrEntry.kind()); #endif } - DebugModeOSREntry(JSScript* script, BaselineDebugModeOSRInfo* info) - : script(script), - oldBaselineScript(script->baselineScript()), - recompInfo(nullptr), - pcOffset(script->pcToOffset(info->pc)), - frameKind(info->frameKind) { -#ifdef DEBUG - MOZ_ASSERT(pcOffset == script->pcToOffset(info->pc)); - MOZ_ASSERT(frameKind == info->frameKind); -#endif - } - DebugModeOSREntry(DebugModeOSREntry&& other) : script(other.script), oldBaselineScript(other.oldBaselineScript), - recompInfo(other.recompInfo ? other.takeRecompInfo() : nullptr), pcOffset(other.pcOffset), frameKind(other.frameKind) {} - ~DebugModeOSREntry() { - // Note that this is nulled out when the recompInfo is taken by the - // frame. The frame then has the responsibility of freeing the - // recompInfo. - js_delete(recompInfo); - } - - bool needsRecompileInfo() const { - return frameKind == RetAddrEntry::Kind::DebugTrap || - frameKind == RetAddrEntry::Kind::DebugPrologue || - frameKind == RetAddrEntry::Kind::DebugAfterYield || - frameKind == RetAddrEntry::Kind::DebugEpilogue; - } - bool recompiled() const { return oldBaselineScript != script->baselineScript(); } - - BaselineDebugModeOSRInfo* takeRecompInfo() { - MOZ_ASSERT(needsRecompileInfo() && recompInfo); - BaselineDebugModeOSRInfo* tmp = recompInfo; - recompInfo = nullptr; - return tmp; - } - - bool allocateRecompileInfo(JSContext* cx) { - MOZ_ASSERT(script); - MOZ_ASSERT(needsRecompileInfo()); - - // If we are returning to a frame which needs a continuation fixer, - // allocate the recompile info up front so that the patching function - // is infallible. - jsbytecode* pc = script->offsetToPC(pcOffset); - - // XXX: Work around compiler error disallowing using bitfields - // with the template magic of new_. - RetAddrEntry::Kind kind = frameKind; - recompInfo = cx->new_<BaselineDebugModeOSRInfo>(pc, kind); - return !!recompInfo; - } }; typedef Vector<DebugModeOSREntry> DebugModeOSREntryVector; class UniqueScriptOSREntryIter { const DebugModeOSREntryVector& entries_; size_t index_; @@ -147,17 +93,16 @@ class UniqueScriptOSREntryIter { return *this; } }; static bool CollectJitStackScripts(JSContext* cx, const Debugger::ExecutionObservableSet& obs, const ActivationIterator& activation, DebugModeOSREntryVector& entries) { - bool needsRecompileHandler = false; for (OnlyJSJitFrameIter iter(activation); !iter.done(); ++iter) { const JSJitFrameIter& frame = iter.frame(); switch (frame.type()) { case FrameType::BaselineJS: { JSScript* script = frame.script(); if (!obs.shouldRecompileOrInvalidate(script)) { break; @@ -168,28 +113,16 @@ static bool CollectJitStackScripts(JSCon if (baselineFrame->runningInInterpreter()) { // Baseline Interpreter frames for scripts that have a BaselineScript // or IonScript don't need to be patched but they do need to be // invalidated and recompiled. See also CollectInterpreterStackScripts // for C++ interpreter frames. if (!entries.append(DebugModeOSREntry(script))) { return false; } - } else 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 frame.resumePCinCurrentFrame(), as - // it points into the debug mode OSR handler and cannot be - // used to look up a corresponding RetAddrEntry. - // - // See case F in PatchBaselineFramesForDebugMode. - if (!entries.append(DebugModeOSREntry(script, info))) { - return false; - } } else if (baselineFrame->hasOverridePc()) { // If the frame is not settled on a pc with a RetAddrEntry, // overridePc will contain an explicit bytecode offset. We can // (and must) use that. uint32_t offset = script->pcToOffset(baselineFrame->overridePc()); if (!entries.append(DebugModeOSREntry(script, offset))) { return false; } @@ -198,23 +131,16 @@ static bool CollectJitStackScripts(JSCon uint8_t* retAddr = frame.resumePCinCurrentFrame(); RetAddrEntry& retAddrEntry = script->baselineScript()->retAddrEntryFromReturnAddress(retAddr); if (!entries.append(DebugModeOSREntry(script, retAddrEntry))) { return false; } } - if (entries.back().needsRecompileInfo()) { - if (!entries.back().allocateRecompileInfo(cx)) { - return false; - } - - needsRecompileHandler |= true; - } break; } case FrameType::BaselineStub: break; case FrameType::IonJS: { InlineFrameIterator inlineIter(cx, &frame); @@ -231,25 +157,16 @@ static bool CollectJitStackScripts(JSCon } break; } default:; } } - // Initialize the on-stack recompile handler, which may fail, so that - // patching the stack is infallible. - if (needsRecompileHandler) { - JitRuntime* rt = cx->runtime()->jitRuntime(); - if (!rt->getBaselineDebugModeOSRHandlerAddress(cx, true)) { - return false; - } - } - return true; } static bool CollectInterpreterStackScripts( JSContext* cx, const Debugger::ExecutionObservableSet& obs, const ActivationIterator& activation, DebugModeOSREntryVector& entries) { // Collect interpreter frame stacks with IonScript or BaselineScript as // well. These do not need to be patched, but do need to be invalidated @@ -324,37 +241,39 @@ static void PatchBaselineFramesForDebugM // // When toggling debug mode with live baseline scripts on the stack, we // could have entered the VM via the following ways from the baseline // script. // // Off to On: // A. From a "can call" IC stub. // B. From a VM call. - // H. From inside HandleExceptionBaseline - // I. From inside the interrupt handler via the prologue stack check. - // J. From the warmup counter in the prologue. + // C. From inside the interrupt handler via the prologue stack check. + // D. From the warmup counter in the prologue. + // E. From inside HandleExceptionBaseline // // On to Off: // - All the ways above. - // C. From the debug trap handler. - // D. From the debug prologue. - // E. From the debug epilogue. - // G. From GeneratorThrowOrReturn - // K. From a JSOP_AFTERYIELD instruction. + // F. From the debug trap handler. + // G. From the debug prologue. + // H. From the debug epilogue. + // I. From a JSOP_AFTERYIELD instruction. + // J. From GeneratorThrowOrReturn // - // Cycles (On to Off to On)+ or (Off to On to Off)+: - // F. Undo cases B, C, D, E, I or J above on previously patched yet unpopped - // frames. + // In general, we patch the return address from VM calls and ICs to the + // corresponding entry in the recompiled BaselineScript. For entries that are + // not present in the recompiled script (cases F to I above) we switch the + // frame to interpreter mode and resume in the Baseline Interpreter. // - // In general, we patch the return address from the VM call to return to a - // "continuation fixer" to fix up machine state (registers and stack - // state). Specifics on what needs to be done are documented below. + // Specifics on what needs to be done are documented below. // + const BaselineInterpreter& baselineInterp = + cx->runtime()->jitRuntime()->baselineInterpreter(); + CommonFrameLayout* prev = nullptr; size_t entryIndex = *start; for (OnlyJSJitFrameIter iter(activation); !iter.done(); ++iter) { const JSJitFrameIter& frame = iter.frame(); switch (frame.type()) { case FrameType::BaselineJS: { // If the script wasn't recompiled or is not observed, there's @@ -382,167 +301,118 @@ static void PatchBaselineFramesForDebugM uint32_t pcOffset = entry.pcOffset; jsbytecode* pc = script->offsetToPC(pcOffset); MOZ_ASSERT(script == frame.script()); MOZ_ASSERT(pcOffset < script->length()); BaselineScript* bl = script->baselineScript(); RetAddrEntry::Kind kind = entry.frameKind; - - if (kind == RetAddrEntry::Kind::IC || - kind == RetAddrEntry::Kind::CallVM || - kind == RetAddrEntry::Kind::WarmupCounter || - kind == RetAddrEntry::Kind::StackCheck) { - // Cases A, B, I, J above. - // - // For the baseline frame here, we resume right after the CallVM or IC - // returns. - // - // For CallVM (case B) the assumption is that all callVMs which can - // trigger debug mode OSR are the *only* callVMs generated for their - // respective pc locations in the Baseline JIT code. - RetAddrEntry* retAddrEntry = nullptr; - switch (kind) { - case RetAddrEntry::Kind::IC: - case RetAddrEntry::Kind::CallVM: - retAddrEntry = &bl->retAddrEntryFromPCOffset(pcOffset, kind); - break; - case RetAddrEntry::Kind::WarmupCounter: - case RetAddrEntry::Kind::StackCheck: - retAddrEntry = &bl->prologueRetAddrEntry(kind); - break; - default: - MOZ_CRASH("Unexpected kind"); + uint8_t* retAddr = nullptr; + switch (kind) { + case RetAddrEntry::Kind::IC: + case RetAddrEntry::Kind::CallVM: + case RetAddrEntry::Kind::WarmupCounter: + case RetAddrEntry::Kind::StackCheck: { + // Cases A, B, C, D above. + // + // For the baseline frame here, we resume right after the CallVM or + // IC returns. + // + // For CallVM (case B) the assumption is that all callVMs which can + // trigger debug mode OSR are the *only* callVMs generated for their + // respective pc locations in the Baseline JIT code. + RetAddrEntry* retAddrEntry = nullptr; + switch (kind) { + case RetAddrEntry::Kind::IC: + case RetAddrEntry::Kind::CallVM: + retAddrEntry = &bl->retAddrEntryFromPCOffset(pcOffset, kind); + break; + case RetAddrEntry::Kind::WarmupCounter: + case RetAddrEntry::Kind::StackCheck: + retAddrEntry = &bl->prologueRetAddrEntry(kind); + break; + default: + MOZ_CRASH("Unexpected kind"); + } + retAddr = bl->returnAddressForEntry(*retAddrEntry); + SpewPatchBaselineFrame(prev->returnAddress(), retAddr, script, kind, + pc); + break; } - uint8_t* retAddr = bl->returnAddressForEntry(*retAddrEntry); - SpewPatchBaselineFrame(prev->returnAddress(), retAddr, script, kind, - pc); - DebugModeOSRVolatileJitFrameIter::forwardLiveIterators( - cx, prev->returnAddress(), retAddr); - prev->setReturnAddress(retAddr); - entryIndex++; - break; - } - - if (kind == RetAddrEntry::Kind::Invalid) { - // Cases G and H above. - // - // We are recompiling a frame with an override pc. - // This may occur from inside the exception handler, - // by way of an onExceptionUnwind invocation, on a pc - // without a RetAddrEntry. It may also happen if we call - // GeneratorThrowOrReturn and trigger onEnterFrame. - // - // If profiling is off, patch the resume address to nullptr, - // to ensure the old address is not used anywhere. - // If profiling is on, JSJitProfilingFrameIterator requires a - // valid return address. - MOZ_ASSERT(frame.baselineFrame()->overridePc() == pc); - uint8_t* retAddr; - if (cx->runtime()->geckoProfiler().enabled()) { - // Won't actually jump to this address so we can ignore the - // register state in the slot info. - PCMappingSlotInfo unused; - retAddr = bl->nativeCodeForPC(script, pc, &unused); - } else { - retAddr = nullptr; + case RetAddrEntry::Kind::DebugPrologue: + case RetAddrEntry::Kind::DebugEpilogue: + case RetAddrEntry::Kind::DebugTrap: + case RetAddrEntry::Kind::DebugAfterYield: { + // Cases F, G, H, I above. + // + // Resume in the Baseline Interpreter because these callVMs are not + // present in the new BaselineScript if we recompiled without debug + // instrumentation. + frame.baselineFrame()->switchFromJitToInterpreter(pc); + switch (kind) { + case RetAddrEntry::Kind::DebugTrap: + // DebugTrap handling is different from the ones below because + // it's not a callVM but a trampoline call at the start of the + // bytecode op. When we return to the frame we can resume at the + // interpretOp label. + retAddr = baselineInterp.interpretOpAddr().value; + break; + case RetAddrEntry::Kind::DebugPrologue: + retAddr = baselineInterp.retAddrForDebugPrologueCallVM(); + break; + case RetAddrEntry::Kind::DebugEpilogue: + retAddr = baselineInterp.retAddrForDebugEpilogueCallVM(); + break; + case RetAddrEntry::Kind::DebugAfterYield: + retAddr = baselineInterp.retAddrForDebugAfterYieldCallVM(); + break; + default: + MOZ_CRASH("Unexpected kind"); + } + SpewPatchBaselineFrame(prev->returnAddress(), retAddr, script, kind, + pc); + break; } - SpewPatchBaselineFrameFromExceptionHandler(prev->returnAddress(), - retAddr, script, pc); - DebugModeOSRVolatileJitFrameIter::forwardLiveIterators( - cx, prev->returnAddress(), retAddr); - prev->setReturnAddress(retAddr); - entryIndex++; - break; + case RetAddrEntry::Kind::Invalid: { + // Cases E and J above. + // + // We are recompiling a frame with an override pc. + // This may occur from inside the exception handler, + // by way of an onExceptionUnwind invocation, on a pc + // without a RetAddrEntry. It may also happen if we call + // GeneratorThrowOrReturn and trigger onEnterFrame. + // + // If profiling is off, patch the resume address to nullptr, + // to ensure the old address is not used anywhere. + // If profiling is on, JSJitProfilingFrameIterator requires a + // valid return address. + MOZ_ASSERT(frame.baselineFrame()->overridePc() == pc); + uint8_t* retAddr; + if (cx->runtime()->geckoProfiler().enabled()) { + // Won't actually jump to this address so we can ignore the + // register state in the slot info. + PCMappingSlotInfo unused; + retAddr = bl->nativeCodeForPC(script, pc, &unused); + } else { + retAddr = nullptr; + } + SpewPatchBaselineFrameFromExceptionHandler(prev->returnAddress(), + retAddr, script, pc); + break; + } + case RetAddrEntry::Kind::PrologueIC: + case RetAddrEntry::Kind::NonOpCallVM: + // These cannot trigger BaselineDebugModeOSR. + MOZ_CRASH("Unexpected RetAddrEntry Kind"); } - // Case F above. - // - // We undo a previous recompile by handling cases C, D or E like normal, - // except that we retrieve the pc information via the previous OSR debug - // info stashed on the frame. - BaselineDebugModeOSRInfo* info = - frame.baselineFrame()->getDebugModeOSRInfo(); - if (info) { - MOZ_ASSERT(info->pc == pc); - MOZ_ASSERT(info->frameKind == kind); - MOZ_ASSERT(kind == RetAddrEntry::Kind::DebugTrap || - kind == RetAddrEntry::Kind::DebugPrologue || - kind == RetAddrEntry::Kind::DebugAfterYield || - kind == RetAddrEntry::Kind::DebugEpilogue); - - // We will have allocated a new recompile info, so delete the - // existing one. - frame.baselineFrame()->deleteDebugModeOSRInfo(); - } - - // The RecompileInfo must already be allocated so that this - // function may be infallible. - BaselineDebugModeOSRInfo* recompInfo = entry.takeRecompInfo(); - - bool popFrameReg; - switch (kind) { - case RetAddrEntry::Kind::DebugTrap: - // Case C above. - // - // Debug traps are emitted before each op, so we resume at the - // same op. Calling debug trap handlers is done via a toggled - // call to a thunk (DebugTrapHandler) that takes care tearing - // down its own stub frame so we don't need to worry about - // popping the frame reg. - recompInfo->resumeAddr = - bl->nativeCodeForPC(script, pc, &recompInfo->slotInfo); - popFrameReg = false; - break; - - case RetAddrEntry::Kind::DebugPrologue: - // Case D above. - // - // We patch a jump directly to the right place in the prologue - // after popping the frame reg and checking for forced return. - recompInfo->resumeAddr = bl->debugOsrPrologueEntryAddr(); - popFrameReg = true; - break; - - case RetAddrEntry::Kind::DebugAfterYield: - // Case K above. - // - // Resume at the next instruction. - MOZ_ASSERT(*pc == JSOP_AFTERYIELD); - recompInfo->resumeAddr = bl->nativeCodeForPC( - script, pc + JSOP_AFTERYIELD_LENGTH, &recompInfo->slotInfo); - popFrameReg = true; - break; - - default: - // Case E above. - // - // We patch a jump directly to the epilogue after popping the - // frame reg and checking for forced return. - MOZ_ASSERT(kind == RetAddrEntry::Kind::DebugEpilogue); - recompInfo->resumeAddr = bl->debugOsrEpilogueEntryAddr(); - popFrameReg = true; - break; - } - - SpewPatchBaselineFrame(prev->returnAddress(), recompInfo->resumeAddr, - script, kind, recompInfo->pc); - - // The recompile handler must already be created so that this - // function may be infallible. - JitRuntime* rt = cx->runtime()->jitRuntime(); - void* handlerAddr = - rt->getBaselineDebugModeOSRHandlerAddress(cx, popFrameReg); - MOZ_ASSERT(handlerAddr); - - prev->setReturnAddress(reinterpret_cast<uint8_t*>(handlerAddr)); - frame.baselineFrame()->setDebugModeOSRInfo(recompInfo); - frame.baselineFrame()->setOverridePc(recompInfo->pc); - + DebugModeOSRVolatileJitFrameIter::forwardLiveIterators( + cx, prev->returnAddress(), retAddr); + prev->setReturnAddress(retAddr); entryIndex++; break; } case FrameType::IonJS: { // Nothing to patch. InlineFrameIterator inlineIter(cx, &frame); while (true) { @@ -737,231 +607,16 @@ bool jit::RecompileOnStackBaselineScript SkipInterpreterFrameEntries(obs, iter, &processed); } } MOZ_ASSERT(processed == entries.length()); return true; } -void BaselineDebugModeOSRInfo::popValueInto(PCMappingSlotInfo::SlotLocation loc, - Value* vp) { - switch (loc) { - case PCMappingSlotInfo::SlotInR0: - valueR0 = vp[stackAdjust]; - break; - case PCMappingSlotInfo::SlotInR1: - valueR1 = vp[stackAdjust]; - break; - case PCMappingSlotInfo::SlotIgnore: - break; - default: - MOZ_CRASH("Bad slot location"); - } - - stackAdjust++; -} - -static inline bool HasForcedReturn(BaselineDebugModeOSRInfo* info, bool rv) { - RetAddrEntry::Kind kind = info->frameKind; - - // The debug epilogue always checks its resumption value, so we don't need - // to check rv. - if (kind == RetAddrEntry::Kind::DebugEpilogue) { - return true; - } - - // |rv| is the value in ReturnReg. If true, in the case of the prologue or - // after yield, it means a forced return. - if (kind == RetAddrEntry::Kind::DebugPrologue || - kind == RetAddrEntry::Kind::DebugAfterYield) { - return rv; - } - - // N.B. The debug trap handler handles its own forced return, so no - // need to deal with it here. - return false; -} - -static void SyncBaselineDebugModeOSRInfo(BaselineFrame* frame, Value* vp, - bool rv) { - AutoUnsafeCallWithABI unsafe; - BaselineDebugModeOSRInfo* info = frame->debugModeOSRInfo(); - MOZ_ASSERT(info); - MOZ_ASSERT( - frame->script()->baselineScript()->containsCodeAddress(info->resumeAddr)); - - if (HasForcedReturn(info, rv)) { - // Load the frame's rval and overwrite the resume address to go to the - // epilogue. - MOZ_ASSERT(R0 == JSReturnOperand); - info->valueR0 = frame->returnValue(); - info->resumeAddr = - frame->script()->baselineScript()->debugOsrEpilogueEntryAddr(); - return; - } - - // Read stack values and make sure R0 and R1 have the right values. - unsigned numUnsynced = info->slotInfo.numUnsynced(); - MOZ_ASSERT(numUnsynced <= 2); - if (numUnsynced > 0) { - info->popValueInto(info->slotInfo.topSlotLocation(), vp); - } - if (numUnsynced > 1) { - info->popValueInto(info->slotInfo.nextSlotLocation(), vp); - } - - // Scale stackAdjust. - info->stackAdjust *= sizeof(Value); -} - -static void FinishBaselineDebugModeOSR(BaselineFrame* frame) { - AutoUnsafeCallWithABI unsafe; - frame->deleteDebugModeOSRInfo(); - - // We will return to JIT code now so we have to clear the override pc. - frame->clearOverridePc(); -} - -void BaselineFrame::deleteDebugModeOSRInfo() { - js_delete(getDebugModeOSRInfo()); - flags_ &= ~HAS_DEBUG_MODE_OSR_INFO; -} - -JitCode* JitRuntime::getBaselineDebugModeOSRHandler(JSContext* cx) { - if (!baselineDebugModeOSRHandler_) { - MOZ_ASSERT(js::CurrentThreadCanAccessRuntime(cx->runtime())); - AutoAllocInAtomsZone az(cx); - uint32_t offset; - if (JitCode* code = generateBaselineDebugModeOSRHandler(cx, &offset)) { - baselineDebugModeOSRHandler_ = code; - baselineDebugModeOSRHandlerNoFrameRegPopAddr_ = code->raw() + offset; - } - } - - return baselineDebugModeOSRHandler_; -} - -void* JitRuntime::getBaselineDebugModeOSRHandlerAddress(JSContext* cx, - bool popFrameReg) { - if (!getBaselineDebugModeOSRHandler(cx)) { - return nullptr; - } - return popFrameReg ? baselineDebugModeOSRHandler_->raw() - : baselineDebugModeOSRHandlerNoFrameRegPopAddr_.ref(); -} - -static void PushCallVMOutputRegisters(MacroAssembler& masm) { - // callVMs can use several different output registers, depending on the - // type of their outparam. - masm.push(ReturnReg); - masm.push(ReturnDoubleReg); - masm.Push(JSReturnOperand); -} - -static void PopCallVMOutputRegisters(MacroAssembler& masm) { - masm.Pop(JSReturnOperand); - masm.pop(ReturnDoubleReg); - masm.pop(ReturnReg); -} - -static void TakeCallVMOutputRegisters(AllocatableGeneralRegisterSet& regs) { - regs.take(ReturnReg); - regs.take(JSReturnOperand); -} - -static void EmitBaselineDebugModeOSRHandlerTail(MacroAssembler& masm, - Register temp) { - // Push values we need later. - masm.pushValue(Address(temp, offsetof(BaselineDebugModeOSRInfo, valueR0))); - masm.pushValue(Address(temp, offsetof(BaselineDebugModeOSRInfo, valueR1))); - masm.push(BaselineFrameReg); - masm.push(Address(temp, offsetof(BaselineDebugModeOSRInfo, resumeAddr))); - - // Call a stub to free the allocated info. - masm.setupUnalignedABICall(temp); - masm.loadBaselineFramePtr(BaselineFrameReg, temp); - masm.passABIArg(temp); - masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, FinishBaselineDebugModeOSR)); - - // Restore saved values. - AllocatableGeneralRegisterSet jumpRegs(GeneralRegisterSet::All()); - jumpRegs.take(R0); - jumpRegs.take(R1); - jumpRegs.take(BaselineFrameReg); - Register target = jumpRegs.takeAny(); - - masm.pop(target); - masm.pop(BaselineFrameReg); - - masm.popValue(R1); - masm.popValue(R0); - - masm.jump(target); -} - -JitCode* JitRuntime::generateBaselineDebugModeOSRHandler( - JSContext* cx, uint32_t* noFrameRegPopOffsetOut) { - StackMacroAssembler masm(cx); - - AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All()); - regs.take(BaselineFrameReg); - TakeCallVMOutputRegisters(regs); - Register temp = regs.takeAny(); - Register syncedStackStart = regs.takeAny(); - - // Pop the frame reg. - masm.pop(BaselineFrameReg); - - // Not all patched baseline frames are returning from a situation where - // the frame reg is already fixed up. - CodeOffset noFrameRegPopOffset(masm.currentOffset()); - - // Record the stack pointer for syncing. - masm.moveStackPtrTo(syncedStackStart); - PushCallVMOutputRegisters(masm); - masm.push(BaselineFrameReg); - - // Call a stub to fully initialize the info. - masm.setupUnalignedABICall(temp); - masm.loadBaselineFramePtr(BaselineFrameReg, temp); - masm.passABIArg(temp); - masm.passABIArg(syncedStackStart); - masm.passABIArg(ReturnReg); - masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, SyncBaselineDebugModeOSRInfo)); - - // Discard stack values depending on how many were unsynced, as we always - // have a fully synced stack in the recompile handler. We arrive here via - // a callVM, and prepareCallVM in BaselineCompiler always fully syncs the - // stack. - masm.pop(BaselineFrameReg); - PopCallVMOutputRegisters(masm); - masm.loadPtr( - Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfScratchValue()), - temp); - masm.addToStackPtr( - Address(temp, offsetof(BaselineDebugModeOSRInfo, stackAdjust))); - - EmitBaselineDebugModeOSRHandlerTail(masm, temp); - - Linker linker(masm, "BaselineDebugModeOSRHandler"); - JitCode* code = linker.newCode(cx, CodeKind::Other); - if (!code) { - return nullptr; - } - - *noFrameRegPopOffsetOut = noFrameRegPopOffset.offset(); - -#ifdef JS_ION_PERF - writePerfSpewerJitCodeProfile(code, "BaselineDebugModeOSRHandler"); -#endif - - return code; -} - /* static */ void DebugModeOSRVolatileJitFrameIter::forwardLiveIterators(JSContext* cx, uint8_t* oldAddr, uint8_t* newAddr) { DebugModeOSRVolatileJitFrameIter* iter; for (iter = cx->liveVolatileJitFrameIter_; iter; iter = iter->prev) { if (iter->isWasm()) { continue;
--- a/js/src/jit/BaselineDebugModeOSR.h +++ b/js/src/jit/BaselineDebugModeOSR.h @@ -42,42 +42,16 @@ class DebugModeOSRVolatileJitFrameIter : MOZ_ASSERT(*stack == this); *stack = prev; } static void forwardLiveIterators(JSContext* cx, uint8_t* oldAddr, uint8_t* newAddr); }; -// -// Auxiliary info to help the DebugModeOSRHandler fix up state. -// -struct BaselineDebugModeOSRInfo { - uint8_t* resumeAddr; - jsbytecode* pc; - PCMappingSlotInfo slotInfo; - RetAddrEntry::Kind frameKind; - - // Filled in by SyncBaselineDebugModeOSRInfo. - uintptr_t stackAdjust; - Value valueR0; - Value valueR1; - - BaselineDebugModeOSRInfo(jsbytecode* pc, RetAddrEntry::Kind kind) - : resumeAddr(nullptr), - pc(pc), - slotInfo(0), - frameKind(kind), - stackAdjust(0), - valueR0(UndefinedValue()), - valueR1(UndefinedValue()) {} - - void popValueInto(PCMappingSlotInfo::SlotLocation loc, Value* vp); -}; - MOZ_MUST_USE bool RecompileOnStackBaselineScriptsForDebugMode( JSContext* cx, const Debugger::ExecutionObservableSet& obs, Debugger::IsObserving observing); } // namespace jit } // namespace js #endif // jit_BaselineDebugModeOSR_h
--- a/js/src/jit/BaselineFrame.h +++ b/js/src/jit/BaselineFrame.h @@ -8,17 +8,16 @@ #define jit_BaselineFrame_h #include "jit/JitFrames.h" #include "vm/Stack.h" namespace js { namespace jit { -struct BaselineDebugModeOSRInfo; class ICEntry; // The stack looks like this, fp is the frame pointer: // // fp+y arguments // fp+x JitFrameLayout (frame header) // fp => saved frame pointer // fp-x BaselineFrame @@ -46,59 +45,49 @@ class BaselineFrame { PREV_UP_TO_DATE = 1 << 5, // Frame has execution observed by a Debugger. // // See comment above 'isDebuggee' in vm/Realm.h for explanation // of invariants of debuggee compartments, scripts, and frames. DEBUGGEE = 1 << 6, - // Frame has a BaselineRecompileInfo stashed in the scratch value - // slot. See PatchBaselineFramesForDebugMode. - HAS_DEBUG_MODE_OSR_INFO = 1 << 7, - // This flag is intended for use whenever the frame is settled on a // native code address without a corresponding RetAddrEntry. In this // case, the frame contains an explicit bytecode offset for frame // iterators. // // There can also be an override pc if the frame has had its // environment chain unwound to a pc during exception handling that is // different from its current pc. // // This flag should never be set on the top frame while we're // executing JIT code. In debug builds, it is checked before and // after VM calls. - HAS_OVERRIDE_PC = 1 << 8, + HAS_OVERRIDE_PC = 1 << 7, // If set, we're handling an exception for this frame. This is set for // debug mode OSR sanity checking when it handles corner cases which // only arise during exception handling. - HANDLING_EXCEPTION = 1 << 9, + HANDLING_EXCEPTION = 1 << 8, }; protected: // Silence Clang warning about unused private fields. // The fields below are only valid if RUNNING_IN_INTERPRETER. JSScript* interpreterScript_; jsbytecode* interpreterPC_; ICEntry* interpreterICEntry_; JSObject* envChain_; // Environment chain (always initialized). ArgumentsObject* argsObj_; // If HAS_ARGS_OBJ, the arguments object. // We need to split the Value into 2 fields of 32 bits, otherwise the C++ // compiler may add some padding between the fields. - union { - struct { - uint32_t loScratchValue_; - uint32_t hiScratchValue_; - }; - BaselineDebugModeOSRInfo* debugModeOSRInfo_; - }; - + uint32_t loScratchValue_; + uint32_t hiScratchValue_; uint32_t flags_; uint32_t frameSize_; uint32_t loReturnValue_; // If HAS_RVAL, the frame's return value. uint32_t hiReturnValue_; uint32_t overrideOffset_; // If HAS_OVERRIDE_PC, the bytecode offset. #if JS_BITS_PER_WORD == 32 // Ensure frame is 8-byte aligned, see static_assert below. uint32_t padding_; @@ -235,16 +224,26 @@ class BaselineFrame { // Note: we can initialize interpreterICEntry_ to nullptr because it won't // be used anyway (we are going to enter the exception handler). flags_ |= RUNNING_IN_INTERPRETER; interpreterScript_ = script; interpreterPC_ = pc; interpreterICEntry_ = nullptr; } + // Switch a JIT frame on the stack to Interpreter mode. The caller is + // responsible for patching the return address into this frame to a location + // in the interpreter code. + void switchFromJitToInterpreter(jsbytecode* pc) { + MOZ_ASSERT(!runningInInterpreter()); + flags_ |= RUNNING_IN_INTERPRETER; + interpreterScript_ = script(); + setInterpreterPC(pc); + } + bool runningInInterpreter() const { return flags_ & RUNNING_IN_INTERPRETER; } JSScript* interpreterScript() const { MOZ_ASSERT(runningInInterpreter()); return interpreterScript_; } jsbytecode* interpreterPC() const { @@ -305,35 +304,16 @@ class BaselineFrame { bool isDebuggee() const { return flags_ & DEBUGGEE; } void setIsDebuggee() { flags_ |= DEBUGGEE; } inline void unsetIsDebuggee(); bool isHandlingException() const { return flags_ & HANDLING_EXCEPTION; } void setIsHandlingException() { flags_ |= HANDLING_EXCEPTION; } void unsetIsHandlingException() { flags_ &= ~HANDLING_EXCEPTION; } - BaselineDebugModeOSRInfo* debugModeOSRInfo() { - MOZ_ASSERT(flags_ & HAS_DEBUG_MODE_OSR_INFO); - return debugModeOSRInfo_; - } - - BaselineDebugModeOSRInfo* getDebugModeOSRInfo() { - if (flags_ & HAS_DEBUG_MODE_OSR_INFO) { - return debugModeOSRInfo(); - } - return nullptr; - } - - void setDebugModeOSRInfo(BaselineDebugModeOSRInfo* info) { - flags_ |= HAS_DEBUG_MODE_OSR_INFO; - debugModeOSRInfo_ = info; - } - - void deleteDebugModeOSRInfo(); - // See the HAS_OVERRIDE_PC comment. bool hasOverridePc() const { return flags_ & HAS_OVERRIDE_PC; } jsbytecode* overridePc() const { MOZ_ASSERT(hasOverridePc()); return script()->offsetToPC(overrideOffset_); }
--- a/js/src/jit/BaselineJIT.cpp +++ b/js/src/jit/BaselineJIT.cpp @@ -435,18 +435,17 @@ bool jit::BaselineCompileFromBaselineInt } } MOZ_CRASH("Unexpected status"); } BaselineScript* BaselineScript::New( JSScript* jsscript, uint32_t bailoutPrologueOffset, - uint32_t warmUpCheckPrologueOffset, uint32_t debugOsrPrologueOffset, - uint32_t debugOsrEpilogueOffset, uint32_t profilerEnterToggleOffset, + uint32_t warmUpCheckPrologueOffset, uint32_t profilerEnterToggleOffset, uint32_t profilerExitToggleOffset, size_t retAddrEntries, size_t pcMappingIndexEntries, size_t pcMappingSize, size_t resumeEntries, size_t traceLoggerToggleOffsetEntries) { static const unsigned DataAlignment = sizeof(uintptr_t); size_t retAddrEntriesSize = retAddrEntries * sizeof(RetAddrEntry); size_t pcMappingIndexEntriesSize = pcMappingIndexEntries * sizeof(PCMappingIndexEntry); @@ -468,17 +467,16 @@ BaselineScript* BaselineScript::New( BaselineScript* script = jsscript->zone()->pod_malloc_with_extra<BaselineScript, uint8_t>( allocBytes); if (!script) { return nullptr; } new (script) BaselineScript(bailoutPrologueOffset, warmUpCheckPrologueOffset, - debugOsrPrologueOffset, debugOsrEpilogueOffset, profilerEnterToggleOffset, profilerExitToggleOffset); size_t offsetCursor = sizeof(BaselineScript); MOZ_ASSERT(offsetCursor == AlignBytes(sizeof(BaselineScript), DataAlignment)); script->retAddrEntriesOffset_ = offsetCursor; script->retAddrEntries_ = retAddrEntries; offsetCursor += paddedRetAddrEntriesSize; @@ -1115,25 +1113,27 @@ void jit::ToggleBaselineTraceLoggerEngin #endif void BaselineInterpreter::init(JitCode* code, uint32_t interpretOpOffset, uint32_t interpretOpNoDebugTrapOffset, uint32_t profilerEnterToggleOffset, uint32_t profilerExitToggleOffset, CodeOffsetVector&& debugInstrumentationOffsets, CodeOffsetVector&& debugTrapOffsets, - CodeOffsetVector&& codeCoverageOffsets) { + CodeOffsetVector&& codeCoverageOffsets, + const CallVMOffsets& callVMOffsets) { code_ = code; interpretOpOffset_ = interpretOpOffset; interpretOpNoDebugTrapOffset_ = interpretOpNoDebugTrapOffset; profilerEnterToggleOffset_ = profilerEnterToggleOffset; profilerExitToggleOffset_ = profilerExitToggleOffset; debugInstrumentationOffsets_ = std::move(debugInstrumentationOffsets); debugTrapOffsets_ = std::move(debugTrapOffsets); codeCoverageOffsets_ = std::move(codeCoverageOffsets); + callVMOffsets_ = callVMOffsets; } bool jit::GenerateBaselineInterpreter(JSContext* cx, BaselineInterpreter& interpreter) { // Temporary IsBaselineInterpreterEnabled check to not generate the // interpreter code (until it's enabled by default). if (IsBaselineInterpreterEnabled()) { BaselineInterpreterGenerator generator(cx);
--- a/js/src/jit/BaselineJIT.h +++ b/js/src/jit/BaselineJIT.h @@ -210,24 +210,16 @@ struct BaselineScript final { // Early Ion bailouts will enter at this address. This is after frame // construction and before environment chain is initialized. uint32_t bailoutPrologueOffset_; // Baseline Interpreter can enter Baseline Compiler code at this address. This // is right after the warm-up counter check in the prologue. uint32_t warmUpCheckPrologueOffset_; - // Baseline Debug OSR during prologue will enter at this address. This is - // right after where a debug prologue VM call would have returned. - uint32_t debugOsrPrologueOffset_; - - // Baseline Debug OSR during epilogue will enter at this address. This is - // right after where a debug epilogue VM call would have returned. - uint32_t debugOsrEpilogueOffset_; - // The offsets for the toggledJump instructions for profiler instrumentation. uint32_t profilerEnterToggleOffset_; uint32_t profilerExitToggleOffset_; // The offsets and event used for Tracelogger toggling. #ifdef JS_TRACE_LOGGING # ifdef DEBUG bool traceLoggerScriptsEnabled_ = false; @@ -305,35 +297,32 @@ struct BaselineScript final { IonBuilder* pendingBuilder_ = nullptr; ControlFlowGraph* controlFlowGraph_ = nullptr; // Use BaselineScript::New to create new instances. It will properly // allocate trailing objects. BaselineScript(uint32_t bailoutPrologueOffset, uint32_t warmUpCheckPrologueOffset, - uint32_t debugOsrPrologueOffset, - uint32_t debugOsrEpilogueOffset, uint32_t profilerEnterToggleOffset, uint32_t profilerExitToggleOffset) : bailoutPrologueOffset_(bailoutPrologueOffset), warmUpCheckPrologueOffset_(warmUpCheckPrologueOffset), - debugOsrPrologueOffset_(debugOsrPrologueOffset), - debugOsrEpilogueOffset_(debugOsrEpilogueOffset), profilerEnterToggleOffset_(profilerEnterToggleOffset), profilerExitToggleOffset_(profilerExitToggleOffset) {} public: - static BaselineScript* New( - JSScript* jsscript, uint32_t bailoutPrologueOffset, - uint32_t warmUpCheckPrologueOffset, uint32_t debugOsrPrologueOffset, - uint32_t debugOsrEpilogueOffset, uint32_t profilerEnterToggleOffset, - uint32_t profilerExitToggleOffset, size_t retAddrEntries, - size_t pcMappingIndexEntries, size_t pcMappingSize, size_t resumeEntries, - size_t traceLoggerToggleOffsetEntries); + static BaselineScript* New(JSScript* jsscript, uint32_t bailoutPrologueOffset, + uint32_t warmUpCheckPrologueOffset, + uint32_t profilerEnterToggleOffset, + uint32_t profilerExitToggleOffset, + size_t retAddrEntries, + size_t pcMappingIndexEntries, size_t pcMappingSize, + size_t resumeEntries, + size_t traceLoggerToggleOffsetEntries); static void Trace(JSTracer* trc, BaselineScript* script); static void Destroy(FreeOp* fop, BaselineScript* script); static inline size_t offsetOfMethod() { return offsetof(BaselineScript, method_); } @@ -358,22 +347,16 @@ struct BaselineScript final { bool usesEnvironmentChain() const { return flags_ & USES_ENVIRONMENT_CHAIN; } uint8_t* bailoutPrologueEntryAddr() const { return method_->raw() + bailoutPrologueOffset_; } uint8_t* warmUpCheckPrologueAddr() const { return method_->raw() + warmUpCheckPrologueOffset_; } - uint8_t* debugOsrPrologueEntryAddr() const { - return method_->raw() + debugOsrPrologueOffset_; - } - uint8_t* debugOsrEpilogueEntryAddr() const { - return method_->raw() + debugOsrEpilogueOffset_; - } RetAddrEntry* retAddrEntryList() { return (RetAddrEntry*)(reinterpret_cast<uint8_t*>(this) + retAddrEntriesOffset_); } uint8_t** resumeEntryList() { return (uint8_t**)(reinterpret_cast<uint8_t*>(this) + resumeEntriesOffset_); } @@ -654,36 +637,64 @@ class BaselineInterpreter { // Offsets of toggled calls to the DebugTrapHandler trampoline (for // breakpoints and stepping). CodeOffsetVector debugTrapOffsets_; // Offsets of toggled jumps for code coverage. CodeOffsetVector codeCoverageOffsets_; public: + // Offsets of some callVMs for BaselineDebugModeOSR. + struct CallVMOffsets { + uint32_t debugPrologueOffset = 0; + uint32_t debugEpilogueOffset = 0; + uint32_t debugAfterYieldOffset = 0; + }; + + private: + CallVMOffsets callVMOffsets_; + + uint8_t* codeAtOffset(uint32_t offset) const { + MOZ_ASSERT(offset > 0); + MOZ_ASSERT(offset < code_->instructionsSize()); + return codeRaw() + offset; + } + + public: BaselineInterpreter() = default; BaselineInterpreter(const BaselineInterpreter&) = delete; void operator=(const BaselineInterpreter&) = delete; void init(JitCode* code, uint32_t interpretOpOffset, uint32_t interpretOpNoDebugTrapOffset, uint32_t profilerEnterToggleOffset, uint32_t profilerExitToggleOffset, CodeOffsetVector&& debugInstrumentationOffsets, CodeOffsetVector&& debugTrapOffsets, - CodeOffsetVector&& codeCoverageOffsets); + CodeOffsetVector&& codeCoverageOffsets, + const CallVMOffsets& callVMOffsets); uint8_t* codeRaw() const { return code_->raw(); } + uint8_t* retAddrForDebugPrologueCallVM() const { + return codeAtOffset(callVMOffsets_.debugPrologueOffset); + } + uint8_t* retAddrForDebugEpilogueCallVM() const { + return codeAtOffset(callVMOffsets_.debugEpilogueOffset); + } + uint8_t* retAddrForDebugAfterYieldCallVM() const { + return codeAtOffset(callVMOffsets_.debugAfterYieldOffset); + } + TrampolinePtr interpretOpAddr() const { - return TrampolinePtr(codeRaw() + interpretOpOffset_); + return TrampolinePtr(codeAtOffset(interpretOpOffset_)); } TrampolinePtr interpretOpNoDebugTrapAddr() const { - return TrampolinePtr(codeRaw() + interpretOpNoDebugTrapOffset_); + return TrampolinePtr(codeAtOffset(interpretOpNoDebugTrapOffset_)); } void toggleProfilerInstrumentation(bool enable); void toggleDebuggerInstrumentation(bool enable); void toggleCodeCoverageInstrumentationUnchecked(bool enable); void toggleCodeCoverageInstrumentation(bool enable); };
--- a/js/src/jit/Ion.cpp +++ b/js/src/jit/Ion.cpp @@ -166,17 +166,16 @@ JitRuntime::JitRuntime() bailoutHandlerOffset_(0), argumentsRectifierOffset_(0), argumentsRectifierReturnOffset_(0), invalidatorOffset_(0), lazyLinkStubOffset_(0), interpreterStubOffset_(0), doubleToInt32ValueStubOffset_(0), debugTrapHandlers_(), - baselineDebugModeOSRHandler_(nullptr), baselineInterpreter_(), trampolineCode_(nullptr), jitcodeGlobalTable_(nullptr), #ifdef DEBUG ionBailAfter_(0), #endif numFinishedBuilders_(0), ionLazyLinkListSize_(0) {
--- a/js/src/jit/JSJitFrameIter.cpp +++ b/js/src/jit/JSJitFrameIter.cpp @@ -627,23 +627,16 @@ bool JSJitProfilingFrameIterator::tryIni return false; } void JSJitProfilingFrameIterator::fixBaselineReturnAddress() { MOZ_ASSERT(type_ == FrameType::BaselineJS); BaselineFrame* bl = (BaselineFrame*)(fp_ - BaselineFrame::FramePointerOffset - BaselineFrame::Size()); - // Debug mode OSR for Baseline uses a "continuation fixer" and stashes the - // actual return address in an auxiliary structure. - if (BaselineDebugModeOSRInfo* info = bl->getDebugModeOSRInfo()) { - resumePCinCurrentFrame_ = info->resumeAddr; - return; - } - // Certain exception handling cases such as debug OSR or resuming a generator // with .throw() will use BaselineFrame::setOverridePc() to indicate the // effective |pc|. We translate the effective-pc into a Baseline code // address. if (jsbytecode* overridePC = bl->maybeOverridePc()) { JSScript* script = bl->script(); if (bl->runningInInterpreter()) { // The return address won't be used for pc mapping when running in the
--- a/js/src/jit/JitFrames.cpp +++ b/js/src/jit/JitFrames.cpp @@ -696,23 +696,16 @@ void HandleException(ResumeFromException // and clear it before it returns to JIT code. jsbytecode* pc; frame.baselineScriptAndPc(nullptr, &pc); AutoBaselineHandlingException handlingException(frame.baselineFrame(), 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. - auto deleteDebugModeOSRInfo = mozilla::MakeScopeExit( - [=] { frame.baselineFrame()->deleteDebugModeOSRInfo(); }); - if (rfe->kind != ResumeFromException::RESUME_ENTRY_FRAME && rfe->kind != ResumeFromException::RESUME_FORCED_RETURN) { return; } TraceLogStopEvent(logger, TraceLogger_Baseline); TraceLogStopEvent(logger, TraceLogger_Scripts);
--- a/js/src/jit/JitRealm.h +++ b/js/src/jit/JitRealm.h @@ -190,20 +190,16 @@ class JitRuntime { // Note: this stub treats -0 as +0 and may clobber R1.scratchReg(). WriteOnceData<uint32_t> doubleToInt32ValueStubOffset_; // Thunk used by the debugger for breakpoint and step mode. mozilla::EnumeratedArray<DebugTrapHandlerKind, DebugTrapHandlerKind::Count, WriteOnceData<JitCode*>> debugTrapHandlers_; - // Thunk used to fix up on-stack recompile of baseline scripts. - WriteOnceData<JitCode*> baselineDebugModeOSRHandler_; - WriteOnceData<void*> baselineDebugModeOSRHandlerNoFrameRegPopAddr_; - // BaselineInterpreter state. BaselineInterpreter baselineInterpreter_; // Code for trampolines and VMFunction wrappers. WriteOnceData<JitCode*> trampolineCode_; // Map VMFunction addresses to the offset of the wrapper in // trampolineCode_. @@ -262,18 +258,16 @@ class JitRuntime { BailoutTable generateBailoutTable(MacroAssembler& masm, Label* bailoutTail, uint32_t frameClass); void generateBailoutHandler(MacroAssembler& masm, Label* bailoutTail); void generateInvalidator(MacroAssembler& masm, Label* bailoutTail); uint32_t generatePreBarrier(JSContext* cx, MacroAssembler& masm, MIRType type); void generateFreeStub(MacroAssembler& masm); JitCode* generateDebugTrapHandler(JSContext* cx, DebugTrapHandlerKind kind); - JitCode* generateBaselineDebugModeOSRHandler( - JSContext* cx, uint32_t* noFrameRegPopOffsetOut); bool generateVMWrapper(JSContext* cx, MacroAssembler& masm, const VMFunctionData& f, void* nativeFun, uint32_t* wrapperOffset); template <typename IdT> bool generateVMWrappers(JSContext* cx, MacroAssembler& masm, VMWrapperOffsets& offsets); @@ -324,18 +318,16 @@ class JitRuntime { return trampolineCode(functionWrapperOffsets_[size_t(funId)]); } TrampolinePtr getVMWrapper(TailCallVMFunctionId funId) const { MOZ_ASSERT(trampolineCode_); return trampolineCode(tailCallFunctionWrapperOffsets_[size_t(funId)]); } JitCode* debugTrapHandler(JSContext* cx, DebugTrapHandlerKind kind); - JitCode* getBaselineDebugModeOSRHandler(JSContext* cx); - void* getBaselineDebugModeOSRHandlerAddress(JSContext* cx, bool popFrameReg); BaselineInterpreter& baselineInterpreter() { return baselineInterpreter_; } TrampolinePtr getGenericBailoutHandler() const { return trampolineCode(bailoutHandlerOffset_); } TrampolinePtr getExceptionTail() const {
--- a/js/src/jit/VMFunctions.cpp +++ b/js/src/jit/VMFunctions.cpp @@ -911,17 +911,16 @@ bool DebugEpilogue(JSContext* cx, Baseli EnvironmentIter ei(cx, frame, pc); UnwindAllEnvironmentsInFrame(cx, ei); JSScript* script = frame->script(); frame->setOverridePc(script->offsetToPC(0)); if (!ok) { // Pop this frame by updating packedExitFP, so that the exception // handling code will start at the previous frame. - frame->deleteDebugModeOSRInfo(); JitFrameLayout* prefix = frame->framePrefix(); EnsureBareExitFrame(cx->activation()->asJit(), prefix); return false; } // Clear the override pc. This is not necessary for correctness: the frame // will return immediately, but this simplifies the check we emit in debug // builds after each callVM, to ensure this flag is not set.