IonScript.h (19341B)
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_IonScript_h 8 #define jit_IonScript_h 9 10 #include "mozilla/MemoryReporting.h" // MallocSizeOf 11 12 #include <stddef.h> // size_t 13 #include <stdint.h> // uint8_t, uint32_t 14 15 #include "jstypes.h" 16 17 #include "gc/Barrier.h" // HeapPtr{JitCode,Object} 18 #include "jit/IonTypes.h" // IonCompilationId 19 #include "jit/JitCode.h" // JitCode 20 #include "jit/JitOptions.h" // JitOptions 21 #include "js/TypeDecls.h" // jsbytecode 22 #include "util/TrailingArray.h" // TrailingArray 23 24 namespace js { 25 namespace jit { 26 27 class SnapshotWriter; 28 class RecoverWriter; 29 class SafepointWriter; 30 class CodegenSafepointIndex; 31 class SafepointIndex; 32 class OsiIndex; 33 class IonIC; 34 35 // An IonScript attaches Ion-generated information to a JSScript. The header 36 // structure is followed by several arrays of data. These trailing arrays have a 37 // layout based on offsets (bytes from 'this') stored in the IonScript header. 38 // 39 // <IonScript itself> 40 // -- 41 // PreBarriered<Value>[] constantTable() 42 // uint8_t[] runtimeData() 43 // OsiIndex[] osiIndex() 44 // SafepointIndex[] safepointIndex() 45 // uint32_t[] icIndex() 46 // -- 47 // uint8_t[] safepoints() 48 // uint8_t[] snapshots() 49 // uint8_t[] snapshotsRVATable() 50 // uint8_t[] recovers() 51 // 52 // Note: These are arranged in order of descending alignment requirements to 53 // avoid the need for padding. The `runtimeData` uses uint64_t alignement due to 54 // its use of mozilla::AlignedStorage2. 55 class alignas(8) IonScript final : public TrailingArray<IonScript> { 56 private: 57 // Offset (in bytes) from `this` to the start of each trailing array. Each 58 // array ends where following one begins. There is no implicit padding (except 59 // possible at very end). 60 Offset constantTableOffset_ = 0; // JS::Value aligned 61 Offset runtimeDataOffset_ = 0; // uint64_t aligned 62 Offset nurseryObjectsOffset_ = 0; // pointer aligned 63 Offset osiIndexOffset_ = 0; 64 Offset safepointIndexOffset_ = 0; 65 Offset icIndexOffset_ = 0; 66 Offset safepointsOffset_ = 0; 67 Offset snapshotsOffset_ = 0; 68 Offset rvaTableOffset_ = 0; 69 Offset recoversOffset_ = 0; 70 Offset allocBytes_ = 0; 71 72 // Code pointer containing the actual method. 73 HeapPtr<JitCode*> method_ = nullptr; 74 75 // Entrypoint for OSR, or nullptr. 76 jsbytecode* osrPc_ = nullptr; 77 78 // Offset to OSR entrypoint from method_->raw(), or 0. 79 uint32_t osrEntryOffset_ = 0; 80 81 // Offset of the invalidation epilogue (which pushes this IonScript 82 // and calls the invalidation thunk). 83 uint32_t invalidateEpilogueOffset_ = 0; 84 85 // The offset immediately after the IonScript immediate. 86 // NOTE: technically a constant delta from 87 // |invalidateEpilogueOffset_|, so we could hard-code this 88 // per-platform if we want. 89 uint32_t invalidateEpilogueDataOffset_ = 0; 90 91 // Number of bailouts that have occurred for reasons that could be 92 // fixed if we invalidated and recompiled. 93 uint16_t numFixableBailouts_ = 0; 94 95 // Number of bailouts that have occurred for reasons that can't be 96 // fixed by recompiling: for example, bailing out to catch an exception. 97 uint16_t numUnfixableBailouts_ = 0; 98 99 public: 100 enum class LICMState : uint8_t { NeverBailed, Bailed, BailedAndHitFallback }; 101 102 private: 103 // Tracks the state of LICM bailouts. 104 LICMState licmState_ = LICMState::NeverBailed; 105 106 // Flag set if IonScript was compiled with profiling enabled. 107 bool hasProfilingInstrumentation_ = false; 108 109 // If true, this IonScript was active on the stack when we discarded JIT code 110 // and inactive ICScripts. This means we should use the generic ICScripts for 111 // inlined functions when we bail out. 112 bool purgedICScripts_ = false; 113 114 // Number of bytes this function reserves on the stack for slots spilled by 115 // the register allocator. 116 uint32_t localSlotsSize_ = 0; 117 118 // Number of bytes used passed in as formal arguments or |this|. 119 uint32_t argumentSlotsSize_ = 0; 120 121 // Frame size is the value that can be added to the StackPointer along 122 // with the frame prefix to get a valid JitFrameLayout. 123 uint32_t frameSize_ = 0; 124 125 // Number of references from invalidation records. 126 uint32_t invalidationCount_ = 0; 127 128 // Identifier of the compilation which produced this code. 129 IonCompilationId compilationId_; 130 131 // Number of times we tried to enter this script via OSR but failed due to 132 // a LOOPENTRY pc other than osrPc_. 133 uint32_t osrPcMismatchCounter_ = 0; 134 135 #ifdef DEBUG 136 // A hash of the ICScripts used in this compilation. 137 mozilla::HashNumber icHash_ = 0; 138 #endif 139 140 // End of fields. 141 142 private: 143 // Layout helpers 144 Offset constantTableOffset() const { return constantTableOffset_; } 145 Offset runtimeDataOffset() const { return runtimeDataOffset_; } 146 Offset nurseryObjectsOffset() const { return nurseryObjectsOffset_; } 147 Offset osiIndexOffset() const { return osiIndexOffset_; } 148 Offset safepointIndexOffset() const { return safepointIndexOffset_; } 149 Offset icIndexOffset() const { return icIndexOffset_; } 150 Offset safepointsOffset() const { return safepointsOffset_; } 151 Offset snapshotsOffset() const { return snapshotsOffset_; } 152 Offset rvaTableOffset() const { return rvaTableOffset_; } 153 Offset recoversOffset() const { return recoversOffset_; } 154 Offset endOffset() const { return allocBytes_; } 155 156 // Hardcode size of incomplete types. These are verified in Ion.cpp. 157 static constexpr size_t SizeOf_OsiIndex = 2 * sizeof(uint32_t); 158 static constexpr size_t SizeOf_SafepointIndex = 2 * sizeof(uint32_t); 159 160 public: 161 // 162 // Table of constants referenced in snapshots. (JS::Value alignment) 163 // 164 HeapPtr<Value>* constants() { 165 return offsetToPointer<HeapPtr<Value>>(constantTableOffset()); 166 } 167 size_t numConstants() const { 168 return numElements<HeapPtr<Value>>(constantTableOffset(), 169 runtimeDataOffset()); 170 } 171 172 // 173 // IonIC data structures. (uint64_t alignment) 174 // 175 uint8_t* runtimeData() { 176 return offsetToPointer<uint8_t>(runtimeDataOffset()); 177 } 178 size_t runtimeSize() const { 179 return numElements<uint8_t>(runtimeDataOffset(), nurseryObjectsOffset()); 180 } 181 182 // 183 // List of (originally) nursery-allocated objects referenced from JIT code. 184 // (JSObject* alignment) 185 // 186 HeapPtr<JSObject*>* nurseryObjects() { 187 return offsetToPointer<HeapPtr<JSObject*>>(nurseryObjectsOffset()); 188 } 189 size_t numNurseryObjects() const { 190 return numElements<HeapPtr<JSObject*>>(nurseryObjectsOffset(), 191 osiIndexOffset()); 192 } 193 void* addressOfNurseryObject(uint32_t index) { 194 MOZ_ASSERT(index < numNurseryObjects()); 195 return &nurseryObjects()[index]; 196 } 197 198 // 199 // Map OSI-point displacement to snapshot. 200 // 201 OsiIndex* osiIndices() { return offsetToPointer<OsiIndex>(osiIndexOffset()); } 202 const OsiIndex* osiIndices() const { 203 return offsetToPointer<OsiIndex>(osiIndexOffset()); 204 } 205 size_t numOsiIndices() const { 206 return numElements<SizeOf_OsiIndex>(osiIndexOffset(), 207 safepointIndexOffset()); 208 } 209 210 // 211 // Map code displacement to safepoint / OSI-patch-delta. 212 // 213 SafepointIndex* safepointIndices() { 214 return offsetToPointer<SafepointIndex>(safepointIndexOffset()); 215 } 216 const SafepointIndex* safepointIndices() const { 217 return offsetToPointer<SafepointIndex>(safepointIndexOffset()); 218 } 219 size_t numSafepointIndices() const { 220 return numElements<SizeOf_SafepointIndex>(safepointIndexOffset(), 221 icIndexOffset()); 222 } 223 224 // 225 // Offset into `runtimeData` for each (variable-length) IonIC. 226 // 227 uint32_t* icIndex() { return offsetToPointer<uint32_t>(icIndexOffset()); } 228 size_t numICs() const { 229 return numElements<uint32_t>(icIndexOffset(), safepointsOffset()); 230 } 231 232 // 233 // Safepoint table as a CompactBuffer. 234 // 235 const uint8_t* safepoints() const { 236 return offsetToPointer<uint8_t>(safepointsOffset()); 237 } 238 size_t safepointsSize() const { 239 return numElements<uint8_t>(safepointsOffset(), snapshotsOffset()); 240 } 241 242 // 243 // Snapshot and RValueAllocation tables as CompactBuffers. 244 // 245 const uint8_t* snapshots() const { 246 return offsetToPointer<uint8_t>(snapshotsOffset()); 247 } 248 size_t snapshotsListSize() const { 249 return numElements<uint8_t>(snapshotsOffset(), rvaTableOffset()); 250 } 251 size_t snapshotsRVATableSize() const { 252 return numElements<uint8_t>(rvaTableOffset(), recoversOffset()); 253 } 254 255 // 256 // Recover instruction table as a CompactBuffer. 257 // 258 const uint8_t* recovers() const { 259 return offsetToPointer<uint8_t>(recoversOffset()); 260 } 261 size_t recoversSize() const { 262 return numElements<uint8_t>(recoversOffset(), endOffset()); 263 } 264 265 private: 266 IonScript(IonCompilationId compilationId, uint32_t localSlotsSize, 267 uint32_t argumentSlotsSize, uint32_t frameSize); 268 269 public: 270 static IonScript* New(JSContext* cx, IonCompilationId compilationId, 271 uint32_t localSlotsSize, uint32_t argumentSlotsSize, 272 uint32_t frameSize, size_t snapshotsListSize, 273 size_t snapshotsRVATableSize, size_t recoversSize, 274 size_t constants, size_t nurseryObjects, 275 size_t safepointIndices, size_t osiIndices, 276 size_t icEntries, size_t runtimeSize, 277 size_t safepointsSize); 278 279 static void Destroy(JS::GCContext* gcx, IonScript* script); 280 281 void trace(JSTracer* trc); 282 void traceWeak(JSTracer* trc); 283 284 static inline size_t offsetOfInvalidationCount() { 285 return offsetof(IonScript, invalidationCount_); 286 } 287 288 public: 289 JitCode* method() const { return method_; } 290 void setMethod(JitCode* code) { 291 MOZ_ASSERT(!invalidated()); 292 method_ = code; 293 } 294 void setOsrPc(jsbytecode* osrPc) { osrPc_ = osrPc; } 295 jsbytecode* osrPc() const { return osrPc_; } 296 void setOsrEntryOffset(uint32_t offset) { 297 MOZ_ASSERT(!osrEntryOffset_); 298 osrEntryOffset_ = offset; 299 } 300 uint32_t osrEntryOffset() const { return osrEntryOffset_; } 301 bool containsCodeAddress(uint8_t* addr) const { 302 return method()->raw() <= addr && 303 addr <= method()->raw() + method()->instructionsSize(); 304 } 305 bool containsReturnAddress(uint8_t* addr) const { 306 // This accounts for an off by one error caused by the return address of a 307 // bailout sitting outside the range of the containing function. 308 return method()->raw() <= addr && 309 addr <= method()->raw() + method()->instructionsSize(); 310 } 311 void setInvalidationEpilogueOffset(uint32_t offset) { 312 MOZ_ASSERT(!invalidateEpilogueOffset_); 313 invalidateEpilogueOffset_ = offset; 314 } 315 uint32_t invalidateEpilogueOffset() const { 316 MOZ_ASSERT(invalidateEpilogueOffset_); 317 return invalidateEpilogueOffset_; 318 } 319 void setInvalidationEpilogueDataOffset(uint32_t offset) { 320 MOZ_ASSERT(!invalidateEpilogueDataOffset_); 321 invalidateEpilogueDataOffset_ = offset; 322 } 323 uint32_t invalidateEpilogueDataOffset() const { 324 MOZ_ASSERT(invalidateEpilogueDataOffset_); 325 return invalidateEpilogueDataOffset_; 326 } 327 328 uint32_t numFixableBailouts() const { return numFixableBailouts_; } 329 330 void incNumFixableBailouts() { numFixableBailouts_++; } 331 void resetNumFixableBailouts() { numFixableBailouts_ = 0; } 332 void incNumUnfixableBailouts() { numUnfixableBailouts_++; } 333 334 bool shouldInvalidate() const { 335 return numFixableBailouts_ >= JitOptions.frequentBailoutThreshold; 336 } 337 bool shouldInvalidateAndDisable() const { 338 return numUnfixableBailouts_ >= JitOptions.frequentBailoutThreshold * 5; 339 } 340 341 LICMState licmState() const { return licmState_; } 342 void setHadLICMBailout() { 343 if (licmState_ == LICMState::NeverBailed) { 344 licmState_ = LICMState::Bailed; 345 } 346 } 347 void noteBaselineFallback() { 348 if (licmState_ == LICMState::Bailed) { 349 licmState_ = LICMState::BailedAndHitFallback; 350 } 351 } 352 353 void setHasProfilingInstrumentation() { hasProfilingInstrumentation_ = true; } 354 void clearHasProfilingInstrumentation() { 355 hasProfilingInstrumentation_ = false; 356 } 357 bool hasProfilingInstrumentation() const { 358 return hasProfilingInstrumentation_; 359 } 360 361 bool purgedICScripts() const { return purgedICScripts_; } 362 void notePurgedICScripts() { purgedICScripts_ = true; } 363 364 size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const { 365 return mallocSizeOf(this); 366 } 367 HeapPtr<Value>& getConstant(size_t index) { 368 MOZ_ASSERT(index < numConstants()); 369 return constants()[index]; 370 } 371 uint32_t localSlotsSize() const { return localSlotsSize_; } 372 uint32_t argumentSlotsSize() const { return argumentSlotsSize_; } 373 uint32_t frameSize() const { return frameSize_; } 374 const SafepointIndex* getSafepointIndex(uint32_t disp) const; 375 const SafepointIndex* getSafepointIndex(uint8_t* retAddr) const { 376 MOZ_ASSERT(containsCodeAddress(retAddr)); 377 return getSafepointIndex(retAddr - method()->raw()); 378 } 379 const OsiIndex* getOsiIndex(uint32_t disp) const; 380 const OsiIndex* getOsiIndex(uint8_t* retAddr) const; 381 382 IonIC& getICFromIndex(uint32_t index) { 383 MOZ_ASSERT(index < numICs()); 384 uint32_t offset = icIndex()[index]; 385 return getIC(offset); 386 } 387 inline IonIC& getIC(uint32_t offset) { 388 MOZ_ASSERT(offset < runtimeSize()); 389 return *reinterpret_cast<IonIC*>(runtimeData() + offset); 390 } 391 void purgeICs(Zone* zone); 392 void copySnapshots(const SnapshotWriter* writer); 393 void copyRecovers(const RecoverWriter* writer); 394 void copyConstants(const Value* vp); 395 void copySafepointIndices(const CodegenSafepointIndex* si); 396 void copyOsiIndices(const OsiIndex* oi); 397 void copyRuntimeData(const uint8_t* data); 398 void copyICEntries(const uint32_t* icEntries); 399 void copySafepoints(const SafepointWriter* writer); 400 401 bool invalidated() const { return invalidationCount_ != 0; } 402 403 // Invalidate the current compilation. 404 void invalidate(JSContext* cx, JSScript* script, bool resetUses, 405 const char* reason); 406 407 size_t invalidationCount() const { return invalidationCount_; } 408 void incrementInvalidationCount() { invalidationCount_++; } 409 void decrementInvalidationCount(JS::GCContext* gcx) { 410 MOZ_ASSERT(invalidationCount_); 411 invalidationCount_--; 412 if (!invalidationCount_) { 413 Destroy(gcx, this); 414 } 415 } 416 IonCompilationId compilationId() const { return compilationId_; } 417 uint32_t incrOsrPcMismatchCounter() { return ++osrPcMismatchCounter_; } 418 void resetOsrPcMismatchCounter() { osrPcMismatchCounter_ = 0; } 419 420 size_t allocBytes() const { return allocBytes_; } 421 422 static void preWriteBarrier(Zone* zone, IonScript* ionScript); 423 424 #ifdef DEBUG 425 mozilla::HashNumber icHash() const { return icHash_; } 426 void setICHash(mozilla::HashNumber hash) { icHash_ = hash; } 427 #endif 428 }; 429 430 // Execution information for a basic block which may persist after the 431 // accompanying IonScript is destroyed, for use during profiling. 432 struct IonBlockCounts { 433 private: 434 uint32_t id_; 435 436 // Approximate bytecode in the outer (not inlined) script this block 437 // was generated from. 438 uint32_t offset_; 439 440 // File and line of the inner script this block was generated from. 441 char* description_; 442 443 // ids for successors of this block. 444 uint32_t numSuccessors_; 445 uint32_t* successors_; 446 447 // Hit count for this block. 448 uint64_t hitCount_; 449 450 // Text information about the code generated for this block. 451 char* code_; 452 453 public: 454 [[nodiscard]] bool init(uint32_t id, uint32_t offset, char* description, 455 uint32_t numSuccessors) { 456 id_ = id; 457 offset_ = offset; 458 description_ = description; 459 numSuccessors_ = numSuccessors; 460 if (numSuccessors) { 461 successors_ = js_pod_calloc<uint32_t>(numSuccessors); 462 if (!successors_) { 463 return false; 464 } 465 } 466 return true; 467 } 468 469 void destroy() { 470 js_free(description_); 471 js_free(successors_); 472 js_free(code_); 473 } 474 475 uint32_t id() const { return id_; } 476 477 uint32_t offset() const { return offset_; } 478 479 const char* description() const { return description_; } 480 481 size_t numSuccessors() const { return numSuccessors_; } 482 483 void setSuccessor(size_t i, uint32_t id) { 484 MOZ_ASSERT(i < numSuccessors_); 485 successors_[i] = id; 486 } 487 488 uint32_t successor(size_t i) const { 489 MOZ_ASSERT(i < numSuccessors_); 490 return successors_[i]; 491 } 492 493 uint64_t* addressOfHitCount() { return &hitCount_; } 494 495 uint64_t hitCount() const { return hitCount_; } 496 497 void setCode(const char* code) { 498 char* ncode = js_pod_malloc<char>(strlen(code) + 1); 499 if (ncode) { 500 strcpy(ncode, code); 501 code_ = ncode; 502 } 503 } 504 505 const char* code() const { return code_; } 506 507 size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const { 508 return mallocSizeOf(description_) + mallocSizeOf(successors_) + 509 mallocSizeOf(code_); 510 } 511 }; 512 513 // Execution information for a compiled script which may persist after the 514 // IonScript is destroyed, for use during profiling. 515 struct IonScriptCounts { 516 private: 517 // Any previous invalidated compilation(s) for the script. 518 IonScriptCounts* previous_ = nullptr; 519 520 // Information about basic blocks in this script. 521 size_t numBlocks_ = 0; 522 IonBlockCounts* blocks_ = nullptr; 523 524 public: 525 IonScriptCounts() = default; 526 527 ~IonScriptCounts() { 528 for (size_t i = 0; i < numBlocks_; i++) { 529 blocks_[i].destroy(); 530 } 531 js_free(blocks_); 532 // The list can be long in some corner cases (bug 1140084), so 533 // unroll the recursion. 534 IonScriptCounts* victims = previous_; 535 while (victims) { 536 IonScriptCounts* victim = victims; 537 victims = victim->previous_; 538 victim->previous_ = nullptr; 539 js_delete(victim); 540 } 541 } 542 543 [[nodiscard]] bool init(size_t numBlocks) { 544 blocks_ = js_pod_calloc<IonBlockCounts>(numBlocks); 545 if (!blocks_) { 546 return false; 547 } 548 549 numBlocks_ = numBlocks; 550 return true; 551 } 552 553 size_t numBlocks() const { return numBlocks_; } 554 555 IonBlockCounts& block(size_t i) { 556 MOZ_ASSERT(i < numBlocks_); 557 return blocks_[i]; 558 } 559 560 void setPrevious(IonScriptCounts* previous) { previous_ = previous; } 561 562 IonScriptCounts* previous() const { return previous_; } 563 564 size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const { 565 size_t size = 0; 566 auto currCounts = this; 567 do { 568 size += currCounts->sizeOfOneIncludingThis(mallocSizeOf); 569 currCounts = currCounts->previous_; 570 } while (currCounts); 571 return size; 572 } 573 574 size_t sizeOfOneIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const { 575 size_t size = mallocSizeOf(this) + mallocSizeOf(blocks_); 576 for (size_t i = 0; i < numBlocks_; i++) { 577 blocks_[i].sizeOfExcludingThis(mallocSizeOf); 578 } 579 return size; 580 } 581 }; 582 583 } // namespace jit 584 } // namespace js 585 586 namespace JS { 587 588 template <> 589 struct DeletePolicy<js::jit::IonScript> { 590 explicit DeletePolicy(JSRuntime* rt) : rt_(rt) {} 591 void operator()(const js::jit::IonScript* script); 592 593 private: 594 JSRuntime* rt_; 595 }; 596 597 } // namespace JS 598 599 #endif /* jit_IonScript_h */