diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
commit | 26a029d407be480d791972afb5975cf62c9360a6 (patch) | |
tree | f435a8308119effd964b339f76abb83a57c29483 /js/src/jit/Jit.cpp | |
parent | Initial commit. (diff) | |
download | firefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz firefox-26a029d407be480d791972afb5975cf62c9360a6.zip |
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'js/src/jit/Jit.cpp')
-rw-r--r-- | js/src/jit/Jit.cpp | 262 |
1 files changed, 262 insertions, 0 deletions
diff --git a/js/src/jit/Jit.cpp b/js/src/jit/Jit.cpp new file mode 100644 index 0000000000..e656156e70 --- /dev/null +++ b/js/src/jit/Jit.cpp @@ -0,0 +1,262 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: set ts=8 sts=2 et sw=2 tw=80: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "jit/Jit.h" + +#include "jit/BaselineJIT.h" +#include "jit/CalleeToken.h" +#include "jit/Ion.h" +#include "jit/JitCommon.h" +#include "jit/JitRuntime.h" +#include "js/friend/StackLimits.h" // js::AutoCheckRecursionLimit +#include "vm/Interpreter.h" +#include "vm/JitActivation.h" +#include "vm/JSContext.h" +#include "vm/PortableBaselineInterpret.h" +#include "vm/Realm.h" + +#include "vm/Activation-inl.h" +#include "vm/JSScript-inl.h" + +using namespace js; +using namespace js::jit; + +static EnterJitStatus JS_HAZ_JSNATIVE_CALLER EnterJit(JSContext* cx, + RunState& state, + uint8_t* code) { + // We don't want to call the interpreter stub here (because + // C++ -> interpreterStub -> C++ is slower than staying in C++). + MOZ_ASSERT(code); +#ifndef ENABLE_PORTABLE_BASELINE_INTERP + MOZ_ASSERT(code != cx->runtime()->jitRuntime()->interpreterStub().value); + MOZ_ASSERT(IsBaselineInterpreterEnabled()); +#else + MOZ_ASSERT(IsBaselineInterpreterEnabled() || + IsPortableBaselineInterpreterEnabled()); +#endif + + AutoCheckRecursionLimit recursion(cx); + if (!recursion.check(cx)) { + return EnterJitStatus::Error; + } + + // jit::Bailout(), jit::InvalidationBailout(), and jit::HandleException() + // reset the counter to zero, so assert here it's also zero when we enter + // JIT code. + MOZ_ASSERT(!cx->isInUnsafeRegion()); + +#ifdef DEBUG + // Assert we don't GC before entering JIT code. A GC could discard JIT code + // or move the function stored in the CalleeToken (it won't be traced at + // this point). We use Maybe<> here so we can call reset() to call the + // AutoAssertNoGC destructor before we enter JIT code. + mozilla::Maybe<JS::AutoAssertNoGC> nogc; + nogc.emplace(cx); +#endif + + JSScript* script = state.script(); + size_t numActualArgs; + bool constructing; + size_t maxArgc; + Value* maxArgv; + JSObject* envChain; + CalleeToken calleeToken; + + unsigned numFormals = 0; + if (state.isInvoke()) { + const CallArgs& args = state.asInvoke()->args(); + numActualArgs = args.length(); + + if (TooManyActualArguments(numActualArgs)) { + // Fall back to the C++ interpreter to avoid running out of stack space. + return EnterJitStatus::NotEntered; + } + + constructing = state.asInvoke()->constructing(); + maxArgc = args.length() + 1; + maxArgv = args.array() - 1; // -1 to include |this| + envChain = nullptr; + calleeToken = CalleeToToken(&args.callee().as<JSFunction>(), constructing); + + numFormals = script->function()->nargs(); + if (numFormals > numActualArgs) { +#ifndef ENABLE_PORTABLE_BASELINE_INTERP + code = cx->runtime()->jitRuntime()->getArgumentsRectifier().value; +#endif + } + } else { + numActualArgs = 0; + constructing = false; + maxArgc = 0; + maxArgv = nullptr; + envChain = state.asExecute()->environmentChain(); + calleeToken = CalleeToToken(state.script()); + } + + // Caller must construct |this| before invoking the function. + MOZ_ASSERT_IF(constructing, maxArgv[0].isObject() || + maxArgv[0].isMagic(JS_UNINITIALIZED_LEXICAL)); + + RootedValue result(cx, Int32Value(numActualArgs)); + { + AssertRealmUnchanged aru(cx); + ActivationEntryMonitor entryMonitor(cx, calleeToken); + JitActivation activation(cx); + +#ifndef ENABLE_PORTABLE_BASELINE_INTERP + EnterJitCode enter = cx->runtime()->jitRuntime()->enterJit(); + +# ifdef DEBUG + nogc.reset(); +# endif + CALL_GENERATED_CODE(enter, code, maxArgc, maxArgv, /* osrFrame = */ nullptr, + calleeToken, envChain, /* osrNumStackValues = */ 0, + result.address()); +#else // !ENABLE_PORTABLE_BASELINE_INTERP + (void)code; +# ifdef DEBUG + nogc.reset(); +# endif + if (!pbl::PortablebaselineInterpreterStackCheck(cx, state, numActualArgs)) { + return EnterJitStatus::NotEntered; + } + if (!pbl::PortableBaselineTrampoline(cx, maxArgc, maxArgv, numFormals, + numActualArgs, calleeToken, envChain, + result.address())) { + return EnterJitStatus::Error; + } +#endif // ENABLE_PORTABLE_BASELINE_INTERP + } + + // Ensure the counter was reset to zero after exiting from JIT code. + MOZ_ASSERT(!cx->isInUnsafeRegion()); + + // Release temporary buffer used for OSR into Ion. + if (!IsPortableBaselineInterpreterEnabled()) { + cx->runtime()->jitRuntime()->freeIonOsrTempData(); + } + + if (result.isMagic()) { + MOZ_ASSERT(result.isMagic(JS_ION_ERROR)); + return EnterJitStatus::Error; + } + + // Jit callers wrap primitive constructor return, except for derived + // class constructors, which are forced to do it themselves. + if (constructing && result.isPrimitive()) { + MOZ_ASSERT(maxArgv[0].isObject()); + result = maxArgv[0]; + } + + state.setReturnValue(result); + return EnterJitStatus::Ok; +} + +// Call the per-script interpreter entry trampoline. +bool js::jit::EnterInterpreterEntryTrampoline(uint8_t* code, JSContext* cx, + RunState* state) { + using EnterTrampolineCodePtr = bool (*)(JSContext* cx, RunState*); + auto funcPtr = JS_DATA_TO_FUNC_PTR(EnterTrampolineCodePtr, code); + return CALL_GENERATED_2(funcPtr, cx, state); +} + +EnterJitStatus js::jit::MaybeEnterJit(JSContext* cx, RunState& state) { + if (!IsBaselineInterpreterEnabled() +#ifdef ENABLE_PORTABLE_BASELINE_INTERP + && !IsPortableBaselineInterpreterEnabled() +#endif + ) { + // All JITs are disabled. + return EnterJitStatus::NotEntered; + } + + // JITs do not respect the debugger's OnNativeCall hook, so JIT execution is + // disabled if this hook might need to be called. + if (cx->realm()->debuggerObservesNativeCall()) { + return EnterJitStatus::NotEntered; + } + + JSScript* script = state.script(); + + uint8_t* code = script->jitCodeRaw(); + +#ifdef JS_CACHEIR_SPEW + cx->spewer().enableSpewing(); +#endif + + do { + // Make sure we can enter Baseline Interpreter code. Note that the prologue + // has warm-up checks to tier up if needed. + if (script->hasJitScript() && code) { + break; + } + + script->incWarmUpCounter(); + +#ifndef ENABLE_PORTABLE_BASELINE_INTERP + // Try to Ion-compile. + if (jit::IsIonEnabled(cx)) { + jit::MethodStatus status = jit::CanEnterIon(cx, state); + if (status == jit::Method_Error) { + return EnterJitStatus::Error; + } + if (status == jit::Method_Compiled) { + code = script->jitCodeRaw(); + break; + } + } + + // Try to Baseline-compile. + if (jit::IsBaselineJitEnabled(cx)) { + jit::MethodStatus status = + jit::CanEnterBaselineMethod<BaselineTier::Compiler>(cx, state); + if (status == jit::Method_Error) { + return EnterJitStatus::Error; + } + if (status == jit::Method_Compiled) { + code = script->jitCodeRaw(); + break; + } + } + + // Try to enter the Baseline Interpreter. + if (IsBaselineInterpreterEnabled()) { + jit::MethodStatus status = + jit::CanEnterBaselineMethod<BaselineTier::Interpreter>(cx, state); + if (status == jit::Method_Error) { + return EnterJitStatus::Error; + } + if (status == jit::Method_Compiled) { + code = script->jitCodeRaw(); + break; + } + } + +#else // !ENABLE_PORTABLE_BASELINE_INTERP + + // Try to enter the Portable Baseline Interpreter. + if (IsPortableBaselineInterpreterEnabled()) { + jit::MethodStatus status = + pbl::CanEnterPortableBaselineInterpreter(cx, state); + if (status == jit::Method_Error) { + return EnterJitStatus::Error; + } + if (status == jit::Method_Compiled) { + code = script->jitCodeRaw(); + break; + } + } +#endif + + return EnterJitStatus::NotEntered; + } while (false); + +#ifdef JS_CACHEIR_SPEW + cx->spewer().disableSpewing(); +#endif + + return EnterJit(cx, state, code); +} |