summaryrefslogtreecommitdiffstats
path: root/js/src/jit/Jit.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/jit/Jit.cpp')
-rw-r--r--js/src/jit/Jit.cpp262
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);
+}