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 }