JitZone.h (12178B)
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_JitZone_h 8 #define jit_JitZone_h 9 10 #include "mozilla/Assertions.h" 11 #include "mozilla/EnumeratedArray.h" 12 #include "mozilla/HashFunctions.h" 13 #include "mozilla/LinkedList.h" 14 #include "mozilla/Maybe.h" 15 #include "mozilla/MemoryReporting.h" 16 17 #include <stddef.h> 18 #include <stdint.h> 19 #include <utility> 20 21 #include "gc/Barrier.h" 22 #include "gc/Marking.h" 23 #include "jit/CacheIRAOT.h" 24 #include "jit/ExecutableAllocator.h" 25 #include "jit/ICStubSpace.h" 26 #include "jit/Invalidation.h" 27 #include "jit/JitScript.h" 28 #include "js/AllocPolicy.h" 29 #include "js/GCHashTable.h" 30 #include "js/HashTable.h" 31 #include "js/TracingAPI.h" 32 #include "js/TypeDecls.h" 33 #include "js/UniquePtr.h" 34 #include "js/Utility.h" 35 #include "threading/ProtectedData.h" 36 37 namespace JS { 38 struct CodeSizes; 39 } 40 41 namespace js { 42 namespace jit { 43 44 enum class CacheKind : uint8_t; 45 class CacheIRStubInfo; 46 class JitCode; 47 class JitScript; 48 49 enum class ICStubEngine : uint8_t { 50 // Baseline IC, see BaselineIC.h. 51 Baseline = 0, 52 53 // Ion IC, see IonIC.h. 54 IonIC 55 }; 56 57 struct CacheIRStubKey : public DefaultHasher<CacheIRStubKey> { 58 struct Lookup { 59 CacheKind kind; 60 ICStubEngine engine; 61 const uint8_t* code; 62 uint32_t length; 63 64 Lookup(CacheKind kind, ICStubEngine engine, const uint8_t* code, 65 uint32_t length) 66 : kind(kind), engine(engine), code(code), length(length) {} 67 }; 68 69 static HashNumber hash(const Lookup& l); 70 static bool match(const CacheIRStubKey& entry, const Lookup& l); 71 72 UniquePtr<CacheIRStubInfo, JS::FreePolicy> stubInfo; 73 74 explicit CacheIRStubKey(CacheIRStubInfo* info) : stubInfo(info) {} 75 CacheIRStubKey(CacheIRStubKey&& other) 76 : stubInfo(std::move(other.stubInfo)) {} 77 78 void operator=(CacheIRStubKey&& other) { 79 stubInfo = std::move(other.stubInfo); 80 } 81 }; 82 83 struct BaselineCacheIRStubCodeMapGCPolicy { 84 static bool traceWeak(JSTracer* trc, CacheIRStubKey*, 85 WeakHeapPtr<JitCode*>* value) { 86 return TraceWeakEdge(trc, value, "traceWeak"); 87 } 88 }; 89 90 enum JitScriptFilter : bool { SkipDyingScripts, IncludeDyingScripts }; 91 92 class JitZone { 93 public: 94 enum class StubKind : uint32_t { 95 StringConcat = 0, 96 RegExpMatcher, 97 RegExpSearcher, 98 RegExpExecMatch, 99 RegExpExecTest, 100 Count 101 }; 102 template <typename Code> 103 using Stubs = 104 mozilla::EnumeratedArray<StubKind, Code, size_t(StubKind::Count)>; 105 106 private: 107 // Allocated space for CacheIR stubs. 108 ICStubSpace stubSpace_; 109 110 // Set of CacheIRStubInfo instances used by Ion stubs in this Zone. 111 using IonCacheIRStubInfoSet = 112 HashSet<CacheIRStubKey, CacheIRStubKey, SystemAllocPolicy>; 113 IonCacheIRStubInfoSet ionCacheIRStubInfoSet_; 114 115 // Map CacheIRStubKey to shared JitCode objects. 116 using BaselineCacheIRStubCodeMap = 117 GCHashMap<CacheIRStubKey, WeakHeapPtr<JitCode*>, CacheIRStubKey, 118 SystemAllocPolicy, BaselineCacheIRStubCodeMapGCPolicy>; 119 BaselineCacheIRStubCodeMap baselineCacheIRStubCodes_; 120 121 // Executable allocator for all code except wasm code. 122 MainThreadData<ExecutableAllocator> execAlloc_; 123 124 // HashMap that maps scripts to compilations inlining those scripts. 125 using InlinedScriptMap = 126 GCHashMap<WeakHeapPtr<BaseScript*>, IonScriptKeyVector, 127 StableCellHasher<WeakHeapPtr<BaseScript*>>, SystemAllocPolicy>; 128 InlinedScriptMap inlinedCompilations_; 129 130 mozilla::LinkedList<JitScript> jitScripts_; 131 132 // The following two fields are a pair of associated scripts. If they are 133 // non-null, we have bailed out from MGuardMultipleShapes. The inner and outer 134 // scripts are either the same script (when no inlining happened) or else the 135 // inner script was inlined into the outer script. 136 // 137 // This is used to distinguish a bailout from MGuardShapeList vs 138 // MGuardMultipleShapes, and for monomorphic inlining we need to track the 139 // outer script that inlined the inner script. 140 WeakHeapPtr<JSScript*> lastStubFoldingBailoutInner_; 141 WeakHeapPtr<JSScript*> lastStubFoldingBailoutOuter_; 142 143 // The JitZone stores stubs to concatenate strings inline and perform RegExp 144 // calls inline. These bake in zone specific pointers and can't be stored in 145 // JitRuntime. They also are dependent on the value of 'initialStringHeap' and 146 // must be flushed when its value changes. 147 // 148 // These are weak pointers. Ion compilations store strong references to stubs 149 // they depend on in WarpSnapshot. 150 Stubs<WeakHeapPtr<JitCode*>> stubs_; 151 152 mozilla::Maybe<IonCompilationId> currentCompilationId_; 153 bool keepJitScripts_ = false; 154 155 // Whether AOT IC loading failed due to OOM; if so, disable 156 // enforcing-AOT checks. 157 bool incompleteAOTICs_ = false; 158 159 gc::Heap initialStringHeap = gc::Heap::Tenured; 160 161 JitCode* generateStringConcatStub(JSContext* cx); 162 JitCode* generateRegExpMatcherStub(JSContext* cx); 163 JitCode* generateRegExpSearcherStub(JSContext* cx); 164 JitCode* generateRegExpExecMatchStub(JSContext* cx); 165 JitCode* generateRegExpExecTestStub(JSContext* cx); 166 167 public: 168 explicit JitZone(JSContext* cx, bool zoneHasNurseryStrings) { 169 setStringsCanBeInNursery(zoneHasNurseryStrings); 170 #ifdef ENABLE_JS_AOT_ICS 171 js::jit::FillAOTICs(cx, this); 172 #endif 173 } 174 ~JitZone() { 175 MOZ_ASSERT(jitScripts_.isEmpty()); 176 MOZ_ASSERT(!keepJitScripts_); 177 } 178 179 void traceWeak(JSTracer* trc, Zone* zone); 180 181 void addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, 182 JS::CodeSizes* code, size_t* jitZone, 183 size_t* cacheIRStubs) const; 184 185 ICStubSpace* stubSpace() { return &stubSpace_; } 186 187 JitCode* getBaselineCacheIRStubCode(const CacheIRStubKey::Lookup& key, 188 CacheIRStubInfo** stubInfo) { 189 auto p = baselineCacheIRStubCodes_.lookup(key); 190 if (p) { 191 *stubInfo = p->key().stubInfo.get(); 192 return p->value(); 193 } 194 *stubInfo = nullptr; 195 return nullptr; 196 } 197 [[nodiscard]] bool putBaselineCacheIRStubCode( 198 const CacheIRStubKey::Lookup& lookup, CacheIRStubKey& key, 199 JitCode* stubCode) { 200 auto p = baselineCacheIRStubCodes_.lookupForAdd(lookup); 201 MOZ_ASSERT(!p); 202 return baselineCacheIRStubCodes_.add(p, std::move(key), stubCode); 203 } 204 205 CacheIRStubInfo* getIonCacheIRStubInfo(const CacheIRStubKey::Lookup& key) { 206 IonCacheIRStubInfoSet::Ptr p = ionCacheIRStubInfoSet_.lookup(key); 207 return p ? p->stubInfo.get() : nullptr; 208 } 209 [[nodiscard]] bool putIonCacheIRStubInfo(const CacheIRStubKey::Lookup& lookup, 210 CacheIRStubKey& key) { 211 IonCacheIRStubInfoSet::AddPtr p = 212 ionCacheIRStubInfoSet_.lookupForAdd(lookup); 213 MOZ_ASSERT(!p); 214 return ionCacheIRStubInfoSet_.add(p, std::move(key)); 215 } 216 void purgeIonCacheIRStubInfo() { ionCacheIRStubInfoSet_.clearAndCompact(); } 217 218 ExecutableAllocator& execAlloc() { return execAlloc_.ref(); } 219 const ExecutableAllocator& execAlloc() const { return execAlloc_.ref(); } 220 221 [[nodiscard]] bool addInlinedCompilation(const IonScriptKey& ionScriptKey, 222 JSScript* inlined); 223 224 IonScriptKeyVector* maybeInlinedCompilations(JSScript* inlined) { 225 auto p = inlinedCompilations_.lookup(inlined); 226 return p ? &p->value() : nullptr; 227 } 228 229 void removeInlinedCompilations(JSScript* inlined) { 230 inlinedCompilations_.remove(inlined); 231 } 232 233 void noteStubFoldingBailout(JSScript* inner, JSScript* outer) { 234 lastStubFoldingBailoutInner_ = inner; 235 lastStubFoldingBailoutOuter_ = outer; 236 } 237 bool hasStubFoldingBailoutData(JSScript* inner) const { 238 return lastStubFoldingBailoutInner_ && 239 lastStubFoldingBailoutInner_.get() == inner && 240 lastStubFoldingBailoutOuter_; 241 } 242 JSScript* stubFoldingBailoutOuter() const { 243 MOZ_ASSERT(lastStubFoldingBailoutInner_); 244 return lastStubFoldingBailoutOuter_.get(); 245 } 246 void clearStubFoldingBailoutData() { 247 lastStubFoldingBailoutInner_ = nullptr; 248 lastStubFoldingBailoutOuter_ = nullptr; 249 } 250 251 void registerJitScript(JitScript* script) { jitScripts_.insertBack(script); } 252 253 // Iterate over all JitScripts in this zone calling |f| on each, allowing |f| 254 // to remove the script. The template parameter |filter| controls whether to 255 // include dying JitScripts during GC sweeping. Be careful when using this not 256 // to let GC things reachable from the JitScript escape - they may be gray. 257 template <JitScriptFilter filter = SkipDyingScripts, typename F> 258 void forEachJitScript(F&& f) { 259 JitScript* script = jitScripts_.getFirst(); 260 while (script) { 261 JitScript* next = script->getNext(); 262 if (filter == IncludeDyingScripts || 263 !gc::IsAboutToBeFinalizedUnbarriered(script->owningScript())) { 264 f(script); 265 } 266 script = next; 267 } 268 } 269 270 // Like forEachJitScript above, but abort if |f| returns false. 271 template <JitScriptFilter filter = SkipDyingScripts, typename F> 272 bool forEachJitScriptFallible(F&& f) { 273 JitScript* script = jitScripts_.getFirst(); 274 while (script) { 275 JitScript* next = script->getNext(); 276 if (filter == IncludeDyingScripts || 277 !gc::IsAboutToBeFinalizedUnbarriered(script->owningScript())) { 278 if (!f(script)) { 279 return false; 280 } 281 } 282 script = next; 283 } 284 return true; 285 } 286 287 bool keepJitScripts() const { return keepJitScripts_; } 288 void setKeepJitScripts(bool keep) { keepJitScripts_ = keep; } 289 290 mozilla::Maybe<IonCompilationId> currentCompilationId() const { 291 return currentCompilationId_; 292 } 293 mozilla::Maybe<IonCompilationId>& currentCompilationIdRef() { 294 return currentCompilationId_; 295 } 296 297 void setIncompleteAOTICs() { incompleteAOTICs_ = true; } 298 bool isIncompleteAOTICs() const { return incompleteAOTICs_; } 299 300 void traceWeak(JSTracer* trc, JS::Realm* realm); 301 302 void discardStubs() { 303 for (WeakHeapPtr<JitCode*>& stubRef : stubs_) { 304 stubRef = nullptr; 305 } 306 } 307 308 bool hasStubs() const { 309 for (const WeakHeapPtr<JitCode*>& stubRef : stubs_) { 310 if (stubRef) { 311 return true; 312 } 313 } 314 return false; 315 } 316 317 void setStringsCanBeInNursery(bool allow) { 318 MOZ_ASSERT(!hasStubs()); 319 initialStringHeap = allow ? gc::Heap::Default : gc::Heap::Tenured; 320 } 321 322 [[nodiscard]] JitCode* ensureStubExists(JSContext* cx, StubKind kind) { 323 if (JitCode* code = stubs_[kind]) { 324 return code; 325 } 326 switch (kind) { 327 case StubKind::StringConcat: 328 stubs_[kind] = generateStringConcatStub(cx); 329 break; 330 case StubKind::RegExpMatcher: 331 stubs_[kind] = generateRegExpMatcherStub(cx); 332 break; 333 case StubKind::RegExpSearcher: 334 stubs_[kind] = generateRegExpSearcherStub(cx); 335 break; 336 case StubKind::RegExpExecMatch: 337 stubs_[kind] = generateRegExpExecMatchStub(cx); 338 break; 339 case StubKind::RegExpExecTest: 340 stubs_[kind] = generateRegExpExecTestStub(cx); 341 break; 342 case StubKind::Count: 343 MOZ_CRASH("Invalid kind"); 344 } 345 return stubs_[kind]; 346 } 347 348 static constexpr size_t offsetOfStringConcatStub() { 349 return offsetof(JitZone, stubs_) + 350 size_t(StubKind::StringConcat) * sizeof(uintptr_t); 351 } 352 static constexpr size_t offsetOfRegExpMatcherStub() { 353 return offsetof(JitZone, stubs_) + 354 size_t(StubKind::RegExpMatcher) * sizeof(uintptr_t); 355 } 356 static constexpr size_t offsetOfRegExpSearcherStub() { 357 return offsetof(JitZone, stubs_) + 358 size_t(StubKind::RegExpSearcher) * sizeof(uintptr_t); 359 } 360 static constexpr size_t offsetOfRegExpExecMatchStub() { 361 return offsetof(JitZone, stubs_) + 362 size_t(StubKind::RegExpExecMatch) * sizeof(uintptr_t); 363 } 364 static constexpr size_t offsetOfRegExpExecTestStub() { 365 return offsetof(JitZone, stubs_) + 366 size_t(StubKind::RegExpExecTest) * sizeof(uintptr_t); 367 } 368 }; 369 370 } // namespace jit 371 } // namespace js 372 373 #endif /* jit_JitZone_h */