JitRuntime.h (18395B)
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 #ifndef jit_JitRuntime_h 8 #define jit_JitRuntime_h 9 10 #include "mozilla/Assertions.h" 11 #include "mozilla/Atomics.h" 12 #include "mozilla/EnumeratedArray.h" 13 #include "mozilla/LinkedList.h" 14 15 #include <stddef.h> 16 #include <stdint.h> 17 18 #include "jstypes.h" 19 20 #include "jit/ABIFunctions.h" 21 #include "jit/BaselineICList.h" 22 #include "jit/BaselineJIT.h" 23 #include "jit/CalleeToken.h" 24 #include "jit/InterpreterEntryTrampoline.h" 25 #include "jit/IonCompileTask.h" 26 #include "jit/IonTypes.h" 27 #include "jit/JitCode.h" 28 #include "jit/JitHints.h" 29 #include "jit/shared/Assembler-shared.h" 30 #include "jit/TrampolineNatives.h" 31 #include "js/AllocPolicy.h" 32 #include "js/ProfilingFrameIterator.h" 33 #include "js/TypeDecls.h" 34 #include "js/UniquePtr.h" 35 #include "js/Vector.h" 36 #include "threading/ProtectedData.h" 37 #include "vm/GeckoProfiler.h" 38 #include "vm/Runtime.h" 39 40 class JS_PUBLIC_API JSTracer; 41 42 namespace js { 43 44 class AutoLockHelperThreadState; 45 class GCMarker; 46 enum class ArraySortKind; 47 48 namespace jit { 49 50 class FrameSizeClass; 51 class Label; 52 class MacroAssembler; 53 struct VMFunctionData; 54 55 enum class VMFunctionId; 56 57 enum class BaselineICFallbackKind : uint8_t { 58 #define DEF_ENUM_KIND(kind) kind, 59 IC_BASELINE_FALLBACK_CODE_KIND_LIST(DEF_ENUM_KIND) 60 #undef DEF_ENUM_KIND 61 Count 62 }; 63 64 enum class BailoutReturnKind { 65 GetProp, 66 GetPropSuper, 67 SetProp, 68 GetElem, 69 GetElemSuper, 70 Call, 71 New, 72 Count 73 }; 74 75 // Class storing code and offsets for all Baseline IC fallback trampolines. This 76 // is stored in JitRuntime and generated when creating the JitRuntime. 77 class BaselineICFallbackCode { 78 JitCode* code_ = nullptr; 79 using OffsetArray = 80 mozilla::EnumeratedArray<BaselineICFallbackKind, uint32_t, 81 size_t(BaselineICFallbackKind::Count)>; 82 OffsetArray offsets_ = {}; 83 84 // Keep track of offset into various baseline stubs' code at return 85 // point from called script. 86 using BailoutReturnArray = 87 mozilla::EnumeratedArray<BailoutReturnKind, uint32_t, 88 size_t(BailoutReturnKind::Count)>; 89 BailoutReturnArray bailoutReturnOffsets_ = {}; 90 91 public: 92 BaselineICFallbackCode() = default; 93 BaselineICFallbackCode(const BaselineICFallbackCode&) = delete; 94 void operator=(const BaselineICFallbackCode&) = delete; 95 96 void initOffset(BaselineICFallbackKind kind, uint32_t offset) { 97 offsets_[kind] = offset; 98 } 99 void initCode(JitCode* code) { code_ = code; } 100 void initBailoutReturnOffset(BailoutReturnKind kind, uint32_t offset) { 101 bailoutReturnOffsets_[kind] = offset; 102 } 103 TrampolinePtr addr(BaselineICFallbackKind kind) const { 104 return TrampolinePtr(code_->raw() + offsets_[kind]); 105 } 106 uint8_t* bailoutReturnAddr(BailoutReturnKind kind) const { 107 return code_->raw() + bailoutReturnOffsets_[kind]; 108 } 109 }; 110 111 enum class DebugTrapHandlerKind { Interpreter, Compiler, Count }; 112 113 enum class IonGenericCallKind { Call, Construct, Count }; 114 115 using EnterJitCode = void (*)(void*, unsigned int, Value*, InterpreterFrame*, 116 CalleeToken, JSObject*, size_t, Value*); 117 118 class JitcodeGlobalTable; 119 class PerfSpewerRangeRecorder; 120 121 class JitRuntime { 122 private: 123 MainThreadData<uint64_t> nextCompilationId_{0}; 124 125 // Buffer for OSR from baseline to Ion. To avoid holding on to this for too 126 // long it's also freed in EnterBaseline and EnterJit (after returning from 127 // JIT code). 128 MainThreadData<js::UniquePtr<uint8_t>> ionOsrTempData_{nullptr}; 129 MainThreadData<uint32_t> ionOsrTempDataSize_{0}; 130 131 // List of Ion compile tasks that should be freed. Used to batch multiple 132 // tasks into a single IonFreeTask. 133 MainThreadData<IonFreeCompileTasks> ionFreeTaskBatch_; 134 135 // Shared exception-handler tail. 136 WriteOnceData<uint32_t> exceptionTailOffset_{0}; 137 WriteOnceData<uint32_t> exceptionTailReturnValueCheckOffset_{0}; 138 139 // Shared profiler exit frame tail. 140 WriteOnceData<uint32_t> profilerExitFrameTailOffset_{0}; 141 142 // Trampoline for entering JIT code. 143 WriteOnceData<uint32_t> enterJITOffset_{0}; 144 145 // Generic bailout table; used if the bailout table overflows. 146 WriteOnceData<uint32_t> bailoutHandlerOffset_{0}; 147 148 // Thunk that invalides an (Ion compiled) caller on the Ion stack. 149 WriteOnceData<uint32_t> invalidatorOffset_{0}; 150 151 // Thunk that calls the GC pre barrier. 152 WriteOnceData<uint32_t> valuePreBarrierOffset_{0}; 153 WriteOnceData<uint32_t> stringPreBarrierOffset_{0}; 154 WriteOnceData<uint32_t> objectPreBarrierOffset_{0}; 155 WriteOnceData<uint32_t> shapePreBarrierOffset_{0}; 156 WriteOnceData<uint32_t> wasmAnyRefPreBarrierOffset_{0}; 157 158 // Thunk called to finish compilation of an IonScript. 159 WriteOnceData<uint32_t> lazyLinkStubOffset_{0}; 160 161 // Thunk to enter the interpreter from JIT code. 162 WriteOnceData<uint32_t> interpreterStubOffset_{0}; 163 164 // Thunk to convert the value in R0 to int32 if it's a double. 165 // Note: this stub treats -0 as +0 and may clobber R1.scratchReg(). 166 WriteOnceData<uint32_t> doubleToInt32ValueStubOffset_{0}; 167 168 // Thunk to do a generic call from Ion. 169 mozilla::EnumeratedArray<IonGenericCallKind, WriteOnceData<uint32_t>, 170 size_t(IonGenericCallKind::Count)> 171 ionGenericCallStubOffset_; 172 173 // Thunk used by the debugger for breakpoint and step mode. 174 mozilla::EnumeratedArray<DebugTrapHandlerKind, WriteOnceData<JitCode*>, 175 size_t(DebugTrapHandlerKind::Count)> 176 debugTrapHandlers_; 177 178 // BaselineInterpreter state. 179 BaselineInterpreter baselineInterpreter_; 180 181 // Code for trampolines and VMFunction wrappers. 182 WriteOnceData<JitCode*> trampolineCode_{nullptr}; 183 184 // Thunk that calls into the C++ interpreter from the interpreter 185 // entry trampoline that is generated with --emit-interpreter-entry 186 WriteOnceData<uint32_t> vmInterpreterEntryOffset_{0}; 187 188 // Maps VMFunctionId to the offset of the wrapper code in trampolineCode_. 189 using VMWrapperOffsets = Vector<uint32_t, 0, SystemAllocPolicy>; 190 VMWrapperOffsets functionWrapperOffsets_; 191 192 MainThreadData<BaselineICFallbackCode> baselineICFallbackCode_; 193 194 // Global table of jitcode native address => bytecode address mappings. 195 UnprotectedData<JitcodeGlobalTable*> jitcodeGlobalTable_{nullptr}; 196 197 // Map that stores Jit Hints for each script. 198 MainThreadData<JitHintsMap*> jitHintsMap_{nullptr}; 199 200 // Map used to collect entry trampolines for the Interpreters which is used 201 // for external profiling to identify which functions are being interpreted. 202 MainThreadData<EntryTrampolineMap*> interpreterEntryMap_{nullptr}; 203 204 #ifdef DEBUG 205 // The number of possible bailing places encountered before forcefully bailing 206 // in that place if the counter reaches zero. Note that zero also means 207 // inactive. 208 MainThreadData<uint32_t> ionBailAfterCounter_{0}; 209 210 // Whether the bailAfter mechanism is enabled. Used to avoid generating the 211 // Ion code instrumentation for ionBailAfterCounter_ if the testing function 212 // isn't used. 213 MainThreadData<bool> ionBailAfterEnabled_{false}; 214 #endif 215 216 // Number of Ion compilations which were finished off thread and are 217 // waiting to be lazily linked. This is only set while holding the helper 218 // thread state lock, but may be read from at other times. 219 using NumFinishedOffThreadTasksType = 220 mozilla::Atomic<size_t, mozilla::SequentiallyConsistent>; 221 NumFinishedOffThreadTasksType numFinishedOffThreadTasks_{0}; 222 223 // List of Ion compilation waiting to get linked. 224 using IonCompileTaskList = mozilla::LinkedList<js::jit::IonCompileTask>; 225 MainThreadData<IonCompileTaskList> ionLazyLinkList_; 226 MainThreadData<size_t> ionLazyLinkListSize_{0}; 227 228 // Pointer to trampoline code for each TrampolineNative. The JSFunction has 229 // a JitEntry pointer that points to an item in this array. 230 using TrampolineNativeJitEntryArray = 231 mozilla::EnumeratedArray<TrampolineNative, void*, 232 size_t(TrampolineNative::Count)>; 233 TrampolineNativeJitEntryArray trampolineNativeJitEntries_{}; 234 235 #ifdef DEBUG 236 // Flag that can be set from JIT code to indicate it's invalid to call 237 // arbitrary JS code in a particular region. This is checked in RunScript. 238 MainThreadData<uint32_t> disallowArbitraryCode_{false}; 239 #endif 240 241 bool generateTrampolines(JSContext* cx); 242 bool generateBaselineICFallbackCode(JSContext* cx); 243 244 void generateLazyLinkStub(MacroAssembler& masm); 245 void generateInterpreterStub(MacroAssembler& masm); 246 void generateDoubleToInt32ValueStub(MacroAssembler& masm); 247 void generateProfilerExitFrameTailStub(MacroAssembler& masm, 248 Label* profilerExitTail); 249 void generateExceptionTailStub(MacroAssembler& masm, Label* profilerExitTail, 250 Label* bailoutTail); 251 void generateBailoutTailStub(MacroAssembler& masm, Label* bailoutTail); 252 void generateEnterJIT(JSContext* cx, MacroAssembler& masm); 253 void generateEnterJitShared(MacroAssembler& masm, Register argcReg, 254 Register argvReg, Register calleeTokenReg, 255 Register scratch, Register scratch2, 256 Register scratch3); 257 void generateBailoutHandler(MacroAssembler& masm, Label* bailoutTail); 258 void generateInvalidator(MacroAssembler& masm, Label* bailoutTail); 259 uint32_t generatePreBarrier(JSContext* cx, MacroAssembler& masm, 260 MIRType type); 261 void generateIonGenericCallStub(MacroAssembler& masm, 262 IonGenericCallKind kind); 263 264 // Helper functions for generateIonGenericCallStub 265 void generateIonGenericCallBoundFunction(MacroAssembler& masm, Label* entry, 266 Label* vmCall); 267 void generateIonGenericCallNativeFunction(MacroAssembler& masm, 268 bool isConstructing); 269 void generateIonGenericCallFunCall(MacroAssembler& masm, Label* entry, 270 Label* vmCall); 271 void generateIonGenericCallArgumentsShift(MacroAssembler& masm, Register argc, 272 Register curr, Register end, 273 Register scratch, Label* done); 274 void generateIonGenericHandleUnderflow(MacroAssembler& masm, 275 bool isConstructing, Label* vmCall); 276 277 JitCode* generateDebugTrapHandler(JSContext* cx, DebugTrapHandlerKind kind); 278 279 bool generateVMWrapper(JSContext* cx, MacroAssembler& masm, VMFunctionId id, 280 const VMFunctionData& f, DynFn nativeFun, 281 uint32_t* wrapperOffset); 282 283 bool generateVMWrappers(JSContext* cx, MacroAssembler& masm, 284 PerfSpewerRangeRecorder& rangeRecorder); 285 286 uint32_t startTrampolineCode(MacroAssembler& masm); 287 288 TrampolinePtr trampolineCode(uint32_t offset) const { 289 MOZ_ASSERT(offset > 0); 290 MOZ_ASSERT(offset < trampolineCode_->instructionsSize()); 291 return TrampolinePtr(trampolineCode_->raw() + offset); 292 } 293 294 void generateBaselineInterpreterEntryTrampoline(MacroAssembler& masm); 295 void generateInterpreterEntryTrampoline(MacroAssembler& masm); 296 297 using TrampolineNativeJitEntryOffsets = 298 mozilla::EnumeratedArray<TrampolineNative, uint32_t, 299 size_t(TrampolineNative::Count)>; 300 void generateTrampolineNatives(MacroAssembler& masm, 301 TrampolineNativeJitEntryOffsets& offsets, 302 PerfSpewerRangeRecorder& rangeRecorder); 303 uint32_t generateArraySortTrampoline(MacroAssembler& masm, 304 ArraySortKind kind); 305 306 void bindLabelToOffset(Label* label, uint32_t offset) { 307 MOZ_ASSERT(!trampolineCode_); 308 label->bind(offset); 309 } 310 311 public: 312 JitCode* generateEntryTrampolineForScript(JSContext* cx, JSScript* script); 313 314 JitRuntime() = default; 315 ~JitRuntime(); 316 [[nodiscard]] bool initialize(JSContext* cx); 317 318 static void TraceAtomZoneRoots(JSTracer* trc); 319 [[nodiscard]] static bool MarkJitcodeGlobalTableIteratively(GCMarker* marker); 320 static void TraceWeakJitcodeGlobalTable(JSRuntime* rt, JSTracer* trc); 321 322 const BaselineICFallbackCode& baselineICFallbackCode() const { 323 return baselineICFallbackCode_.ref(); 324 } 325 326 IonCompilationId nextCompilationId() { 327 return IonCompilationId(nextCompilationId_++); 328 } 329 330 [[nodiscard]] bool addIonCompileToFreeTaskBatch(IonCompileTask* task) { 331 return ionFreeTaskBatch_.ref().append(task); 332 } 333 void maybeStartIonFreeTask(bool force); 334 335 UniquePtr<LifoAlloc> tryReuseIonLifoAlloc(); 336 337 #ifdef DEBUG 338 bool disallowArbitraryCode() const { return disallowArbitraryCode_; } 339 void clearDisallowArbitraryCode() { disallowArbitraryCode_ = false; } 340 const void* addressOfDisallowArbitraryCode() const { 341 return &disallowArbitraryCode_.refNoCheck(); 342 } 343 static size_t offsetOfDisallowArbitraryCode() { 344 return offsetof(JitRuntime, disallowArbitraryCode_); 345 } 346 #endif 347 348 uint8_t* allocateIonOsrTempData(size_t size); 349 void freeIonOsrTempData(); 350 351 TrampolinePtr getVMWrapper(VMFunctionId funId) const { 352 MOZ_ASSERT(trampolineCode_); 353 return trampolineCode(functionWrapperOffsets_[size_t(funId)]); 354 } 355 356 bool ensureDebugTrapHandler(JSContext* cx, DebugTrapHandlerKind kind); 357 JitCode* debugTrapHandler(DebugTrapHandlerKind kind) const { 358 MOZ_ASSERT(debugTrapHandlers_[kind]); 359 return debugTrapHandlers_[kind]; 360 } 361 362 BaselineInterpreter& baselineInterpreter() { return baselineInterpreter_; } 363 const BaselineInterpreter& baselineInterpreter() const { 364 return baselineInterpreter_; 365 } 366 367 TrampolinePtr getGenericBailoutHandler() const { 368 return trampolineCode(bailoutHandlerOffset_); 369 } 370 371 TrampolinePtr getExceptionTail() const { 372 return trampolineCode(exceptionTailOffset_); 373 } 374 TrampolinePtr getExceptionTailReturnValueCheck() const { 375 return trampolineCode(exceptionTailReturnValueCheckOffset_); 376 } 377 378 TrampolinePtr getProfilerExitFrameTail() const { 379 return trampolineCode(profilerExitFrameTailOffset_); 380 } 381 382 uint32_t vmInterpreterEntryOffset() { return vmInterpreterEntryOffset_; } 383 384 TrampolinePtr getInvalidationThunk() const { 385 return trampolineCode(invalidatorOffset_); 386 } 387 388 EnterJitCode enterJit() const { 389 return JS_DATA_TO_FUNC_PTR(EnterJitCode, 390 trampolineCode(enterJITOffset_).value); 391 } 392 393 // Return the registers from the native caller frame of the given JIT frame. 394 // Nothing{} if frameStackAddress is NOT pointing at a native-to-JIT entry 395 // frame, or if the information is not accessible/implemented on this 396 // platform. 397 static mozilla::Maybe<::JS::ProfilingFrameIterator::RegisterState> 398 getCppEntryRegisters(JitFrameLayout* frameStackAddress); 399 400 TrampolinePtr preBarrier(MIRType type) const { 401 switch (type) { 402 case MIRType::Value: 403 return trampolineCode(valuePreBarrierOffset_); 404 case MIRType::String: 405 return trampolineCode(stringPreBarrierOffset_); 406 case MIRType::Object: 407 return trampolineCode(objectPreBarrierOffset_); 408 case MIRType::Shape: 409 return trampolineCode(shapePreBarrierOffset_); 410 case MIRType::WasmAnyRef: 411 return trampolineCode(wasmAnyRefPreBarrierOffset_); 412 default: 413 MOZ_CRASH(); 414 } 415 } 416 417 TrampolinePtr lazyLinkStub() const { 418 return trampolineCode(lazyLinkStubOffset_); 419 } 420 TrampolinePtr interpreterStub() const { 421 return trampolineCode(interpreterStubOffset_); 422 } 423 424 TrampolinePtr getDoubleToInt32ValueStub() const { 425 return trampolineCode(doubleToInt32ValueStubOffset_); 426 } 427 428 TrampolinePtr getIonGenericCallStub(IonGenericCallKind kind) const { 429 return trampolineCode(ionGenericCallStubOffset_[kind]); 430 } 431 432 void** trampolineNativeJitEntry(TrampolineNative native) { 433 void** jitEntry = &trampolineNativeJitEntries_[native]; 434 MOZ_ASSERT(*jitEntry >= trampolineCode_->raw()); 435 MOZ_ASSERT(*jitEntry < 436 trampolineCode_->raw() + trampolineCode_->instructionsSize()); 437 return jitEntry; 438 } 439 TrampolineNative trampolineNativeForJitEntry(void** entry) { 440 MOZ_RELEASE_ASSERT(entry >= trampolineNativeJitEntries_.begin()); 441 size_t index = entry - trampolineNativeJitEntries_.begin(); 442 MOZ_RELEASE_ASSERT(index < size_t(TrampolineNative::Count)); 443 return TrampolineNative(index); 444 } 445 446 bool hasJitcodeGlobalTable() const { return jitcodeGlobalTable_ != nullptr; } 447 448 JitcodeGlobalTable* getJitcodeGlobalTable() { 449 MOZ_ASSERT(hasJitcodeGlobalTable()); 450 return jitcodeGlobalTable_; 451 } 452 453 bool hasJitHintsMap() const { return jitHintsMap_ != nullptr; } 454 455 JitHintsMap* getJitHintsMap() { 456 MOZ_ASSERT(hasJitHintsMap()); 457 return jitHintsMap_; 458 } 459 460 bool hasInterpreterEntryMap() const { 461 return interpreterEntryMap_ != nullptr; 462 } 463 464 EntryTrampolineMap* getInterpreterEntryMap() { 465 MOZ_ASSERT(hasInterpreterEntryMap()); 466 return interpreterEntryMap_; 467 } 468 469 bool isProfilerInstrumentationEnabled(JSRuntime* rt) { 470 return rt->geckoProfiler().enabled(); 471 } 472 473 bool isOptimizationTrackingEnabled(JSRuntime* rt) { 474 return isProfilerInstrumentationEnabled(rt); 475 } 476 477 #ifdef DEBUG 478 void* addressOfIonBailAfterCounter() { return &ionBailAfterCounter_; } 479 480 // Set after how many bailing places we should forcefully bail. 481 // Zero disables this feature. 482 void setIonBailAfterCounter(uint32_t after) { ionBailAfterCounter_ = after; } 483 bool ionBailAfterEnabled() const { return ionBailAfterEnabled_; } 484 void setIonBailAfterEnabled(bool enabled) { ionBailAfterEnabled_ = enabled; } 485 #endif 486 487 size_t numFinishedOffThreadTasks() const { 488 return numFinishedOffThreadTasks_; 489 } 490 NumFinishedOffThreadTasksType& numFinishedOffThreadTasksRef( 491 const AutoLockHelperThreadState& locked) { 492 return numFinishedOffThreadTasks_; 493 } 494 495 IonCompileTaskList& ionLazyLinkList(JSRuntime* rt); 496 497 size_t ionLazyLinkListSize() const { return ionLazyLinkListSize_; } 498 499 void ionLazyLinkListRemove(JSRuntime* rt, js::jit::IonCompileTask* task); 500 void ionLazyLinkListAdd(JSRuntime* rt, js::jit::IonCompileTask* task); 501 }; 502 503 } // namespace jit 504 } // namespace js 505 506 #endif /* jit_JitRuntime_h */