From c4167e27e8a099d50f9513d53892a7c96522b001 Mon Sep 17 00:00:00 2001 From: Jan de Mooij Date: Fri, 3 May 2019 07:42:31 +0000 Subject: [PATCH] Bug 1541404 part 19 - Add BaselineInterpreter class and use it in various places. r=tcampbell I considered adding BaselineInterpreter.{h,cpp} files but there are shared helper functions so this might get awkward. Maybe once the rest of the code is in we can experiment with changes in this area. Differential Revision: https://phabricator.services.mozilla.com/D29158 --HG-- extra : moz-landing-system : lando --- js/src/jit/BaselineCompiler.cpp | 4 +- js/src/jit/BaselineJIT.cpp | 91 +++++++++++++++++++++++++++++---- js/src/jit/BaselineJIT.h | 45 ++++++++++++++++ js/src/jit/Ion.cpp | 5 ++ js/src/jit/Jit.cpp | 26 +++++++--- js/src/jit/JitFrames.cpp | 30 ++++++++--- js/src/jit/JitRealm.h | 6 +++ js/src/vm/JSScript.cpp | 3 ++ js/src/vm/Runtime.cpp | 8 +++ js/src/vm/TypeInference.cpp | 5 ++ 10 files changed, 197 insertions(+), 26 deletions(-) diff --git a/js/src/jit/BaselineCompiler.cpp b/js/src/jit/BaselineCompiler.cpp index a2122f8e7498..1c81a1f4079d 100644 --- a/js/src/jit/BaselineCompiler.cpp +++ b/js/src/jit/BaselineCompiler.cpp @@ -5660,7 +5660,9 @@ bool BaselineCodeGen::emit_JSOP_FINALYIELDRVAL() { template <> void BaselineCompilerCodeGen::emitJumpToInterpretOpLabel() { - MOZ_CRASH("NYI: Interpreter emitJumpToInterpretOpLabel"); + TrampolinePtr code = + cx->runtime()->jitRuntime()->baselineInterpreter().interpretOpAddr(); + masm.jump(code); } template <> diff --git a/js/src/jit/BaselineJIT.cpp b/js/src/jit/BaselineJIT.cpp index 84749d01128b..3dbc082519eb 100644 --- a/js/src/jit/BaselineJIT.cpp +++ b/js/src/jit/BaselineJIT.cpp @@ -151,7 +151,9 @@ JitExecStatus jit::EnterBaselineAtBranch(JSContext* cx, InterpreterFrame* fp, data.jitcode += MacroAssembler::ToggledCallSize(data.jitcode); } } else { - MOZ_CRASH("NYI: Interpreter executeOp code"); + const BaselineInterpreter& interp = + cx->runtime()->jitRuntime()->baselineInterpreter(); + data.jitcode = interp.interpretOpAddr().value; } // Note: keep this in sync with SetEnterJitData. @@ -1080,6 +1082,23 @@ void BaselineScript::toggleTraceLoggerEngine(bool enable) { } #endif +static void ToggleProfilerInstrumentation(JitCode* code, + uint32_t profilerEnterToggleOffset, + uint32_t profilerExitToggleOffset, + bool enable) { + CodeLocationLabel enterToggleLocation(code, + CodeOffset(profilerEnterToggleOffset)); + CodeLocationLabel exitToggleLocation(code, + CodeOffset(profilerExitToggleOffset)); + if (enable) { + Assembler::ToggleToCmp(enterToggleLocation); + Assembler::ToggleToCmp(exitToggleLocation); + } else { + Assembler::ToggleToJmp(enterToggleLocation); + Assembler::ToggleToJmp(exitToggleLocation); + } +} + void BaselineScript::toggleProfilerInstrumentation(bool enable) { if (enable == isProfilerInstrumentationOn()) { return; @@ -1088,22 +1107,49 @@ void BaselineScript::toggleProfilerInstrumentation(bool enable) { JitSpew(JitSpew_BaselineIC, " toggling profiling %s for BaselineScript %p", enable ? "on" : "off", this); - // Toggle the jump - CodeLocationLabel enterToggleLocation(method_, - CodeOffset(profilerEnterToggleOffset_)); - CodeLocationLabel exitToggleLocation(method_, - CodeOffset(profilerExitToggleOffset_)); + ToggleProfilerInstrumentation(method_, profilerEnterToggleOffset_, + profilerExitToggleOffset_, enable); + if (enable) { - Assembler::ToggleToCmp(enterToggleLocation); - Assembler::ToggleToCmp(exitToggleLocation); flags_ |= uint32_t(PROFILER_INSTRUMENTATION_ON); } else { - Assembler::ToggleToJmp(enterToggleLocation); - Assembler::ToggleToJmp(exitToggleLocation); flags_ &= ~uint32_t(PROFILER_INSTRUMENTATION_ON); } } +void BaselineInterpreter::toggleProfilerInstrumentation(bool enable) { + if (!JitOptions.baselineInterpreter) { + return; + } + + AutoWritableJitCode awjc(code_); + ToggleProfilerInstrumentation(code_, profilerEnterToggleOffset_, + profilerExitToggleOffset_, enable); +} + +void BaselineInterpreter::toggleDebuggerInstrumentation(bool enable) { + if (!JitOptions.baselineInterpreter) { + return; + } + + AutoWritableJitCode awjc(code_); + + // Toggle prologue IsDebuggeeCheck code. + CodeLocationLabel debuggeeCheckLocation(code_, + CodeOffset(debuggeeCheckOffset_)); + if (enable) { + Assembler::ToggleToCmp(debuggeeCheckLocation); + } else { + Assembler::ToggleToJmp(debuggeeCheckLocation); + } + + // Toggle DebugTrapHandler calls. + for (uint32_t offset : debugTrapOffsets_) { + CodeLocationLabel trapLocation(code_, CodeOffset(offset)); + Assembler::ToggleCall(trapLocation, enable); + } +} + void ICScript::purgeOptimizedStubs(JSScript* script) { MOZ_ASSERT(script->icScript() == this); @@ -1277,6 +1323,8 @@ void jit::ToggleBaselineProfiling(JSRuntime* runtime, bool enable) { return; } + jrt->baselineInterpreter().toggleProfilerInstrumentation(enable); + for (ZonesIter zone(runtime, SkipAtoms); !zone.done(); zone.next()) { for (auto script = zone->cellIter(); !script.done(); script.next()) { @@ -1365,3 +1413,26 @@ void jit::MarkActiveTypeScripts(Zone* zone) { } } } + +void BaselineInterpreter::init(JitCode* code, uint32_t interpretOpOffset, + uint32_t profilerEnterToggleOffset, + uint32_t profilerExitToggleOffset, + uint32_t debuggeeCheckOffset, + DebugTrapOffsets&& debugTrapOffsets) { + code_ = code; + interpretOpOffset_ = interpretOpOffset; + profilerEnterToggleOffset_ = profilerEnterToggleOffset; + profilerExitToggleOffset_ = profilerExitToggleOffset; + debuggeeCheckOffset_ = debuggeeCheckOffset; + debugTrapOffsets_ = std::move(debugTrapOffsets); +} + +bool jit::GenerateBaselineInterpreter(JSContext* cx, + BaselineInterpreter& interpreter) { + // Temporary JitOptions check to prevent crashes for now. + if (JitOptions.baselineInterpreter) { + MOZ_CRASH("NYI: GenerateBaselineInterpreter"); + } + + return true; +} diff --git a/js/src/jit/BaselineJIT.h b/js/src/jit/BaselineJIT.h index 580c5d23d8b4..05577cd0f0f9 100644 --- a/js/src/jit/BaselineJIT.h +++ b/js/src/jit/BaselineJIT.h @@ -651,6 +651,51 @@ void JitSpewBaselineICStats(JSScript* script, const char* dumpReason); static const unsigned BASELINE_MAX_ARGS_LENGTH = 20000; +// Class storing the generated Baseline Interpreter code for the runtime. +class BaselineInterpreter { + // The interpreter code. + JitCode* code_ = nullptr; + + // Offset of the code to start interpreting a bytecode op. + uint32_t interpretOpOffset_ = 0; + + // The offsets for the toggledJump instructions for profiler instrumentation. + uint32_t profilerEnterToggleOffset_ = 0; + uint32_t profilerExitToggleOffset_ = 0; + + // The offset for the toggledJump instruction for the debugger's + // IsDebuggeeCheck code in the prologue. + uint32_t debuggeeCheckOffset_ = 0; + + // Offsets of toggled calls to the DebugTrapHandler trampoline (for + // breakpoints and stepping). + using DebugTrapOffsets = js::Vector; + DebugTrapOffsets debugTrapOffsets_; + + public: + BaselineInterpreter() = default; + + BaselineInterpreter(const BaselineInterpreter&) = delete; + void operator=(const BaselineInterpreter&) = delete; + + void init(JitCode* code, uint32_t interpretOpOffset, + uint32_t profilerEnterToggleOffset, + uint32_t profilerExitToggleOffset, uint32_t debuggeeCheckOffset, + DebugTrapOffsets&& debugTrapOffsets); + + uint8_t* codeRaw() const { return code_->raw(); } + + TrampolinePtr interpretOpAddr() const { + return TrampolinePtr(codeRaw() + interpretOpOffset_); + } + + void toggleProfilerInstrumentation(bool enable); + void toggleDebuggerInstrumentation(bool enable); +}; + +MOZ_MUST_USE bool GenerateBaselineInterpreter(JSContext* cx, + BaselineInterpreter& interpreter); + } // namespace jit } // namespace js diff --git a/js/src/jit/Ion.cpp b/js/src/jit/Ion.cpp index db0253429d69..8e5e52acfea2 100644 --- a/js/src/jit/Ion.cpp +++ b/js/src/jit/Ion.cpp @@ -166,6 +166,7 @@ JitRuntime::JitRuntime() doubleToInt32ValueStubOffset_(0), debugTrapHandler_(nullptr), baselineDebugModeOSRHandler_(nullptr), + baselineInterpreter_(), trampolineCode_(nullptr), jitcodeGlobalTable_(nullptr), #ifdef DEBUG @@ -213,6 +214,10 @@ bool JitRuntime::initialize(JSContext* cx) { return false; } + if (!GenerateBaselineInterpreter(cx, baselineInterpreter_)) { + return false; + } + return true; } diff --git a/js/src/jit/Jit.cpp b/js/src/jit/Jit.cpp index 267fa956aa28..ed07a7136654 100644 --- a/js/src/jit/Jit.cpp +++ b/js/src/jit/Jit.cpp @@ -20,8 +20,11 @@ using namespace js::jit; static EnterJitStatus JS_HAZ_JSNATIVE_CALLER EnterJit(JSContext* cx, RunState& state, uint8_t* code) { - MOZ_ASSERT(state.script()->hasBaselineScript()); + // We don't want to call the interpreter stub here (because + // C++ -> interpreterStub -> C++ is slower than staying in C++). MOZ_ASSERT(code); + MOZ_ASSERT(code != cx->runtime()->jitRuntime()->interpreterStub().value); + MOZ_ASSERT(IsBaselineEnabled(cx)); if (!CheckRecursionLimit(cx)) { @@ -55,7 +58,11 @@ static EnterJitStatus JS_HAZ_JSNATIVE_CALLER EnterJit(JSContext* cx, if (numActualArgs > BASELINE_MAX_ARGS_LENGTH) { return EnterJitStatus::NotEntered; } - code = script->baselineScript()->method()->raw(); + if (script->hasBaselineScript()) { + code = script->baselineScript()->method()->raw(); + } else { + code = cx->runtime()->jitRuntime()->baselineInterpreter().codeRaw(); + } } constructing = state.asInvoke()->constructing(); @@ -131,11 +138,16 @@ EnterJitStatus js::jit::MaybeEnterJit(JSContext* cx, RunState& state) { uint8_t* code = script->jitCodeRaw(); do { - // Make sure we have a BaselineScript: we don't want to call the - // interpreter stub here. Note that Baseline code contains warm-up - // checks in the prologue to Ion-compile if needed. - if (script->hasBaselineScript()) { - break; + // Make sure we can enter Baseline Interpreter or JIT code. Note that + // the prologue has warm-up checks to tier up if needed. + if (JitOptions.baselineInterpreter) { + if (script->types()) { + break; + } + } else { + if (script->hasBaselineScript()) { + break; + } } script->incWarmUpCounter(); diff --git a/js/src/jit/JitFrames.cpp b/js/src/jit/JitFrames.cpp index d65206d93fa4..612f72f0f809 100644 --- a/js/src/jit/JitFrames.cpp +++ b/js/src/jit/JitFrames.cpp @@ -376,21 +376,35 @@ static bool ProcessTryNotesBaseline(JSContext* cx, const JSJitFrameIter& frame, script->resetWarmUpCounter(); // Resume at the start of the catch block. - PCMappingSlotInfo slotInfo; rfe->kind = ResumeFromException::RESUME_CATCH; - rfe->target = - script->baselineScript()->nativeCodeForPC(script, *pc, &slotInfo); - MOZ_ASSERT(slotInfo.isStackSynced()); + if (frame.baselineFrame()->runningInInterpreter()) { + const BaselineInterpreter& interp = + cx->runtime()->jitRuntime()->baselineInterpreter(); + frame.baselineFrame()->setInterpreterPC(*pc); + rfe->target = interp.interpretOpAddr().value; + } else { + PCMappingSlotInfo slotInfo; + rfe->target = + script->baselineScript()->nativeCodeForPC(script, *pc, &slotInfo); + MOZ_ASSERT(slotInfo.isStackSynced()); + } return true; } case JSTRY_FINALLY: { - PCMappingSlotInfo slotInfo; SettleOnTryNote(cx, tn, frame, ei, rfe, pc); rfe->kind = ResumeFromException::RESUME_FINALLY; - rfe->target = - script->baselineScript()->nativeCodeForPC(script, *pc, &slotInfo); - MOZ_ASSERT(slotInfo.isStackSynced()); + if (frame.baselineFrame()->runningInInterpreter()) { + const BaselineInterpreter& interp = + cx->runtime()->jitRuntime()->baselineInterpreter(); + frame.baselineFrame()->setInterpreterPC(*pc); + rfe->target = interp.interpretOpAddr().value; + } else { + PCMappingSlotInfo slotInfo; + rfe->target = + script->baselineScript()->nativeCodeForPC(script, *pc, &slotInfo); + MOZ_ASSERT(slotInfo.isStackSynced()); + } // Drop the exception instead of leaking cross compartment data. if (!cx->getPendingException( MutableHandleValue::fromMarkedLocation(&rfe->exception))) { diff --git a/js/src/jit/JitRealm.h b/js/src/jit/JitRealm.h index 61cbfe280cfe..f494d2faa99e 100644 --- a/js/src/jit/JitRealm.h +++ b/js/src/jit/JitRealm.h @@ -16,6 +16,7 @@ #include "builtin/TypedObject.h" #include "jit/BaselineICList.h" +#include "jit/BaselineJIT.h" #include "jit/CompileInfo.h" #include "jit/ICStubSpace.h" #include "jit/IonCode.h" @@ -195,6 +196,9 @@ class JitRuntime { WriteOnceData baselineDebugModeOSRHandler_; WriteOnceData baselineDebugModeOSRHandlerNoFrameRegPopAddr_; + // BaselineInterpreter state. + BaselineInterpreter baselineInterpreter_; + // Code for trampolines and VMFunction wrappers. WriteOnceData trampolineCode_; @@ -326,6 +330,8 @@ class JitRuntime { JitCode* getBaselineDebugModeOSRHandler(JSContext* cx); void* getBaselineDebugModeOSRHandlerAddress(JSContext* cx, bool popFrameReg); + BaselineInterpreter& baselineInterpreter() { return baselineInterpreter_; } + TrampolinePtr getGenericBailoutHandler() const { return trampolineCode(bailoutHandlerOffset_); } diff --git a/js/src/vm/JSScript.cpp b/js/src/vm/JSScript.cpp index 260581d732f1..e86c584b6da7 100644 --- a/js/src/vm/JSScript.cpp +++ b/js/src/vm/JSScript.cpp @@ -5431,6 +5431,9 @@ void JSScript::updateJitCodeRaw(JSRuntime* rt) { } else if (hasBaselineScript()) { jitCodeRaw_ = baseline->method()->raw(); jitCodeSkipArgCheck_ = jitCodeRaw_; + } else if (types() && js::jit::JitOptions.baselineInterpreter) { + jitCodeRaw_ = rt->jitRuntime()->baselineInterpreter().codeRaw(); + jitCodeSkipArgCheck_ = jitCodeRaw_; } else { jitCodeRaw_ = rt->jitRuntime()->interpreterStub().value; jitCodeSkipArgCheck_ = jitCodeRaw_; diff --git a/js/src/vm/Runtime.cpp b/js/src/vm/Runtime.cpp index 7dcf8c99daa6..1d729fd3a6c9 100644 --- a/js/src/vm/Runtime.cpp +++ b/js/src/vm/Runtime.cpp @@ -770,6 +770,10 @@ void JSRuntime::clearUsedByHelperThread(Zone* zone) { } void JSRuntime::incrementNumDebuggeeRealms() { + if (numDebuggeeRealms_ == 0) { + jitRuntime()->baselineInterpreter().toggleDebuggerInstrumentation(true); + } + numDebuggeeRealms_++; MOZ_ASSERT(numDebuggeeRealms_ <= numRealms); } @@ -777,6 +781,10 @@ void JSRuntime::incrementNumDebuggeeRealms() { void JSRuntime::decrementNumDebuggeeRealms() { MOZ_ASSERT(numDebuggeeRealms_ > 0); numDebuggeeRealms_--; + + if (numDebuggeeRealms_ == 0) { + jitRuntime()->baselineInterpreter().toggleDebuggerInstrumentation(false); + } } bool js::CurrentThreadCanAccessRuntime(const JSRuntime* rt) { diff --git a/js/src/vm/TypeInference.cpp b/js/src/vm/TypeInference.cpp index 2d5b62b869bd..0ad3850e6314 100644 --- a/js/src/vm/TypeInference.cpp +++ b/js/src/vm/TypeInference.cpp @@ -3555,6 +3555,10 @@ bool JSScript::makeTypes(JSContext* cx) { types_ = new (typeScript) TypeScript(this, std::move(icScript), numTypeSets); + // We have a TypeScript so we can set the script's jitCodeRaw_ pointer to the + // Baseline Interpreter code. + updateJitCodeRaw(cx->runtime()); + #ifdef DEBUG StackTypeSet* typeArray = typeScript->typeArrayDontCheckGeneration(); for (unsigned i = 0; i < numBytecodeTypeSets(); i++) { @@ -4585,6 +4589,7 @@ void JSScript::maybeReleaseTypes() { types_->destroy(zone()); types_ = nullptr; + updateJitCodeRaw(runtimeFromMainThread()); } void TypeScript::destroy(Zone* zone) {