tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

Jit.cpp (7994B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
      2 * vim: set ts=8 sts=2 et sw=2 tw=80:
      3 * This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "jit/Jit.h"
      8 
      9 #include "jit/BaselineJIT.h"
     10 #include "jit/CalleeToken.h"
     11 #include "jit/Ion.h"
     12 #include "jit/JitCommon.h"
     13 #include "jit/JitRuntime.h"
     14 #include "js/friend/StackLimits.h"  // js::AutoCheckRecursionLimit
     15 #include "vm/Interpreter.h"
     16 #include "vm/JitActivation.h"
     17 #include "vm/JSContext.h"
     18 #include "vm/PortableBaselineInterpret.h"
     19 #include "vm/Realm.h"
     20 
     21 #include "vm/Activation-inl.h"
     22 #include "vm/JSScript-inl.h"
     23 
     24 using namespace js;
     25 using namespace js::jit;
     26 
     27 static EnterJitStatus JS_HAZ_JSNATIVE_CALLER EnterJit(JSContext* cx,
     28                                                      RunState& state,
     29                                                      uint8_t* code) {
     30  // We don't want to call the interpreter stub here (because
     31  // C++ -> interpreterStub -> C++ is slower than staying in C++).
     32  MOZ_ASSERT(code);
     33 #ifndef ENABLE_PORTABLE_BASELINE_INTERP
     34  MOZ_ASSERT(code != cx->runtime()->jitRuntime()->interpreterStub().value);
     35  MOZ_ASSERT(IsBaselineInterpreterEnabled());
     36 #else
     37  MOZ_ASSERT(IsBaselineInterpreterEnabled() ||
     38             IsPortableBaselineInterpreterEnabled());
     39 #endif
     40 
     41  AutoCheckRecursionLimit recursion(cx);
     42  if (!recursion.check(cx)) {
     43    return EnterJitStatus::Error;
     44  }
     45 
     46  // jit::Bailout(), jit::InvalidationBailout(), and jit::HandleException()
     47  // reset the counter to zero, so assert here it's also zero when we enter
     48  // JIT code.
     49  MOZ_ASSERT(!cx->isInUnsafeRegion());
     50 
     51 #ifdef DEBUG
     52  // Assert we don't GC before entering JIT code. A GC could discard JIT code
     53  // or move the function stored in the CalleeToken (it won't be traced at
     54  // this point). We use Maybe<> here so we can call reset() to call the
     55  // AutoAssertNoGC destructor before we enter JIT code.
     56  mozilla::Maybe<JS::AutoAssertNoGC> nogc;
     57  nogc.emplace(cx);
     58 #endif
     59 
     60  size_t numActualArgs;
     61  bool constructing;
     62  size_t maxArgc;
     63  Value* maxArgv;
     64  JSObject* envChain;
     65  CalleeToken calleeToken;
     66 
     67  if (state.isInvoke()) {
     68    const CallArgs& args = state.asInvoke()->args();
     69    numActualArgs = args.length();
     70 
     71    if (TooManyActualArguments(numActualArgs)) {
     72      // Fall back to the C++ interpreter to avoid running out of stack space.
     73      return EnterJitStatus::NotEntered;
     74    }
     75 
     76    constructing = state.asInvoke()->constructing();
     77 
     78    // Caller must construct |this| before invoking the function.
     79    MOZ_ASSERT_IF(constructing,
     80                  args.thisv().isObject() ||
     81                      args.thisv().isMagic(JS_UNINITIALIZED_LEXICAL));
     82 
     83    maxArgc = args.length();
     84    maxArgv = args.array();
     85    envChain = nullptr;
     86    calleeToken = CalleeToToken(&args.callee().as<JSFunction>(), constructing);
     87  } else {
     88    numActualArgs = 0;
     89    constructing = false;
     90    maxArgc = 0;
     91    maxArgv = nullptr;
     92    envChain = state.asExecute()->environmentChain();
     93    calleeToken = CalleeToToken(state.script());
     94  }
     95 
     96  RootedValue result(cx, Int32Value(numActualArgs));
     97  {
     98    AssertRealmUnchanged aru(cx);
     99    JitActivation activation(cx);
    100 
    101 #ifndef ENABLE_PORTABLE_BASELINE_INTERP
    102    EnterJitCode enter = cx->runtime()->jitRuntime()->enterJit();
    103 
    104 #  ifdef DEBUG
    105    nogc.reset();
    106 #  endif
    107    CALL_GENERATED_CODE(enter, code, maxArgc, maxArgv, /* osrFrame = */ nullptr,
    108                        calleeToken, envChain, /* osrNumStackValues = */ 0,
    109                        result.address());
    110 #else  // !ENABLE_PORTABLE_BASELINE_INTERP
    111    (void)code;
    112 #  ifdef DEBUG
    113    nogc.reset();
    114 #  endif
    115    if (!pbl::PortablebaselineInterpreterStackCheck(cx, state, numActualArgs)) {
    116      return EnterJitStatus::NotEntered;
    117    }
    118    unsigned numFormals =
    119        state.isInvoke() ? state.script()->function()->nargs() : 0;
    120    if (!pbl::PortableBaselineTrampoline(cx, maxArgc, maxArgv, numFormals,
    121                                         calleeToken, envChain,
    122                                         result.address())) {
    123      return EnterJitStatus::Error;
    124    }
    125 #endif  // ENABLE_PORTABLE_BASELINE_INTERP
    126  }
    127 
    128  // Ensure the counter was reset to zero after exiting from JIT code.
    129  MOZ_ASSERT(!cx->isInUnsafeRegion());
    130 
    131  // Release temporary buffer used for OSR into Ion.
    132  if (!IsPortableBaselineInterpreterEnabled()) {
    133    cx->runtime()->jitRuntime()->freeIonOsrTempData();
    134  }
    135 
    136  if (result.isMagic()) {
    137    MOZ_ASSERT(result.isMagic(JS_ION_ERROR));
    138    return EnterJitStatus::Error;
    139  }
    140 
    141  // Jit callers wrap primitive constructor return, except for derived
    142  // class constructors, which are forced to do it themselves.
    143  if (constructing && result.isPrimitive()) {
    144    result = state.asInvoke()->args().thisv();
    145    MOZ_ASSERT(result.isObject());
    146  }
    147 
    148  state.setReturnValue(result);
    149  return EnterJitStatus::Ok;
    150 }
    151 
    152 // Call the per-script interpreter entry trampoline.
    153 bool js::jit::EnterInterpreterEntryTrampoline(uint8_t* code, JSContext* cx,
    154                                              RunState* state) {
    155  using EnterTrampolineCodePtr = bool (*)(JSContext* cx, RunState*);
    156  auto funcPtr = JS_DATA_TO_FUNC_PTR(EnterTrampolineCodePtr, code);
    157  return CALL_GENERATED_2(funcPtr, cx, state);
    158 }
    159 
    160 EnterJitStatus js::jit::MaybeEnterJit(JSContext* cx, RunState& state) {
    161  if (!IsBaselineInterpreterEnabled()
    162 #ifdef ENABLE_PORTABLE_BASELINE_INTERP
    163      && !IsPortableBaselineInterpreterEnabled()
    164 #endif
    165  ) {
    166    // All JITs are disabled.
    167    return EnterJitStatus::NotEntered;
    168  }
    169 
    170  // JITs do not respect the debugger's OnNativeCall hook, so JIT execution is
    171  // disabled if this hook might need to be called.
    172  if (cx->realm()->debuggerObservesNativeCall()) {
    173    return EnterJitStatus::NotEntered;
    174  }
    175 
    176  JSScript* script = state.script();
    177 
    178  uint8_t* code = script->jitCodeRaw();
    179 
    180 #ifdef JS_CACHEIR_SPEW
    181  cx->spewer().enableSpewing();
    182 #endif
    183 
    184  do {
    185    // Make sure we can enter Baseline Interpreter code. Note that the prologue
    186    // has warm-up checks to tier up if needed.
    187    if (script->hasJitScript() && code) {
    188      break;
    189    }
    190 
    191    script->incWarmUpCounter();
    192 
    193 #ifndef ENABLE_PORTABLE_BASELINE_INTERP
    194    // Try to Ion-compile.
    195    if (jit::IsIonEnabled(cx)) {
    196      jit::MethodStatus status = jit::CanEnterIon(cx, state);
    197      if (status == jit::Method_Error) {
    198        return EnterJitStatus::Error;
    199      }
    200      if (status == jit::Method_Compiled) {
    201        code = script->jitCodeRaw();
    202        break;
    203      }
    204    }
    205 
    206    // Try to Baseline-compile.
    207    if (jit::IsBaselineJitEnabled(cx)) {
    208      jit::MethodStatus status =
    209          jit::CanEnterBaselineMethod<BaselineTier::Compiler>(cx, state);
    210      if (status == jit::Method_Error) {
    211        return EnterJitStatus::Error;
    212      }
    213      if (status == jit::Method_Compiled) {
    214        code = script->jitCodeRaw();
    215        break;
    216      }
    217    }
    218 
    219    // Try to enter the Baseline Interpreter.
    220    if (IsBaselineInterpreterEnabled()) {
    221      jit::MethodStatus status =
    222          jit::CanEnterBaselineMethod<BaselineTier::Interpreter>(cx, state);
    223      if (status == jit::Method_Error) {
    224        return EnterJitStatus::Error;
    225      }
    226      if (status == jit::Method_Compiled) {
    227        code = script->jitCodeRaw();
    228        break;
    229      }
    230    }
    231 
    232 #else  // !ENABLE_PORTABLE_BASELINE_INTERP
    233 
    234    // Try to enter the Portable Baseline Interpreter.
    235    if (IsPortableBaselineInterpreterEnabled()) {
    236      jit::MethodStatus status =
    237          pbl::CanEnterPortableBaselineInterpreter(cx, state);
    238      if (status == jit::Method_Error) {
    239        return EnterJitStatus::Error;
    240      }
    241      if (status == jit::Method_Compiled) {
    242        code = script->jitCodeRaw();
    243        break;
    244      }
    245    }
    246 #endif
    247 
    248    return EnterJitStatus::NotEntered;
    249  } while (false);
    250 
    251 #ifdef JS_CACHEIR_SPEW
    252  cx->spewer().disableSpewing();
    253 #endif
    254 
    255  return EnterJit(cx, state, code);
    256 }