JitcodeMap.h (28044B)
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_JitcodeMap_h 8 #define jit_JitcodeMap_h 9 10 #include "mozilla/Assertions.h" // MOZ_ASSERT, MOZ_ASSERT_IF, MOZ_CRASH 11 12 #include <stddef.h> // size_t 13 #include <stdint.h> // uint8_t, uint32_t, uint64_t 14 15 #include "ds/AvlTree.h" // AvlTree 16 #include "jit/CompactBuffer.h" // CompactBufferReader, CompactBufferWriter 17 #include "jit/shared/Assembler-shared.h" // CodeOffset 18 #include "js/AllocPolicy.h" // SystemAllocPolicy 19 #include "js/ProfilingFrameIterator.h" // CallStackFrameInfo 20 #include "js/TypeDecls.h" // jsbytecode 21 #include "js/Vector.h" // Vector 22 #include "vm/BytecodeLocation.h" // BytecodeLocation 23 #include "vm/SharedStencil.h" // SharedImmutableScriptData 24 25 class JSTracer; 26 struct JSRuntime; 27 28 namespace JS { 29 class Zone; 30 } // namespace JS 31 32 namespace js { 33 34 class GCMarker; 35 36 namespace jit { 37 38 class InlineScriptTree; 39 40 /* 41 * The jitcode map implements tables to allow mapping from addresses in jitcode 42 * to the list of scripts that are implicitly active in the frame at that point 43 * in the native code. 44 * 45 * To represent this information efficiently, a multi-level table is used. 46 * 47 * At the top level, a global AVL-tree of JitcodeGlobalEntry describing the 48 * mapping for each individual JitCode generated by compiles. The entries are 49 * ordered by their nativeStartAddr. 50 * 51 * Every entry in the table is of fixed size, but there are different entry 52 * types, distinguished by the kind field. 53 */ 54 55 class JitcodeGlobalTable; 56 class JitcodeIonTable; 57 class JitcodeRegionEntry; 58 59 struct NativeToBytecode { 60 CodeOffset nativeOffset; 61 InlineScriptTree* tree; 62 jsbytecode* pc; 63 }; 64 65 // Describes range [start, end) of JIT-generated code. 66 class JitCodeRange { 67 protected: 68 void* const nativeStartAddr_; 69 void* const nativeEndAddr_; 70 71 public: 72 JitCodeRange(void* start, void* end) 73 : nativeStartAddr_(start), nativeEndAddr_(end) { 74 MOZ_ASSERT(start < end); 75 } 76 77 // Comparator used by the AvlTree. 78 static int compare(const JitCodeRange* r1, const JitCodeRange* r2) { 79 // JitCodeRange includes 'start' but excludes 'end'. 80 if (r1->nativeEndAddr_ <= r2->nativeStartAddr_) { 81 return -1; 82 } 83 if (r1->nativeStartAddr_ >= r2->nativeEndAddr_) { 84 return 1; 85 } 86 return 0; 87 } 88 89 void* nativeStartAddr() const { return nativeStartAddr_; } 90 void* nativeEndAddr() const { return nativeEndAddr_; } 91 92 bool containsPointer(void* ptr) const { 93 return nativeStartAddr() <= ptr && ptr < nativeEndAddr(); 94 } 95 }; 96 97 using BytecodeLocationVector = Vector<BytecodeLocation, 0, SystemAllocPolicy>; 98 99 class IonEntry; 100 class IonICEntry; 101 class BaselineEntry; 102 class BaselineInterpreterEntry; 103 class DummyEntry; 104 class RealmIndependentSharedEntry; 105 106 // Base class for all entries. 107 class JitcodeGlobalEntry : public JitCodeRange { 108 protected: 109 JitCode* jitcode_; 110 // If this entry is referenced from the profiler buffer, this is the 111 // position where the most recent sample that references it starts. 112 // Otherwise set to kNoSampleInBuffer. 113 static const uint64_t kNoSampleInBuffer = UINT64_MAX; 114 uint64_t samplePositionInBuffer_ = kNoSampleInBuffer; 115 116 public: 117 enum class Kind : uint8_t { 118 Ion, 119 IonIC, 120 Baseline, 121 BaselineInterpreter, 122 Dummy, 123 RealmIndependentShared, 124 }; 125 126 protected: 127 Kind kind_; 128 129 JitcodeGlobalEntry(Kind kind, JitCode* code, void* nativeStartAddr, 130 void* nativeEndAddr) 131 : JitCodeRange(nativeStartAddr, nativeEndAddr), 132 jitcode_(code), 133 kind_(kind) { 134 MOZ_ASSERT(code); 135 MOZ_ASSERT(nativeStartAddr); 136 MOZ_ASSERT(nativeEndAddr); 137 } 138 139 // Protected destructor to ensure this is called through DestroyPolicy. 140 ~JitcodeGlobalEntry() = default; 141 142 JitcodeGlobalEntry(const JitcodeGlobalEntry& other) = delete; 143 void operator=(const JitcodeGlobalEntry& other) = delete; 144 145 public: 146 struct DestroyPolicy { 147 void operator()(JitcodeGlobalEntry* entry); 148 }; 149 150 void setSamplePositionInBuffer(uint64_t bufferWritePos) { 151 samplePositionInBuffer_ = bufferWritePos; 152 } 153 void setAsExpired() { samplePositionInBuffer_ = kNoSampleInBuffer; } 154 bool isSampled(uint64_t bufferRangeStart) { 155 if (samplePositionInBuffer_ == kNoSampleInBuffer) { 156 return false; 157 } 158 return bufferRangeStart <= samplePositionInBuffer_; 159 } 160 161 Kind kind() const { return kind_; } 162 bool isIon() const { return kind() == Kind::Ion; } 163 bool isIonIC() const { return kind() == Kind::IonIC; } 164 bool isBaseline() const { return kind() == Kind::Baseline; } 165 bool isBaselineInterpreter() const { 166 return kind() == Kind::BaselineInterpreter; 167 } 168 bool isDummy() const { return kind() == Kind::Dummy; } 169 bool isRealmIndependentShared() const { 170 return kind() == Kind::RealmIndependentShared; 171 } 172 173 inline const IonEntry& asIon() const; 174 inline const IonICEntry& asIonIC() const; 175 inline const BaselineEntry& asBaseline() const; 176 inline const BaselineInterpreterEntry& asBaselineInterpreter() const; 177 inline const DummyEntry& asDummy() const; 178 inline const RealmIndependentSharedEntry& asRealmIndependentShared() const; 179 180 inline IonEntry& asIon(); 181 inline IonICEntry& asIonIC(); 182 inline BaselineEntry& asBaseline(); 183 inline BaselineInterpreterEntry& asBaselineInterpreter(); 184 inline DummyEntry& asDummy(); 185 inline RealmIndependentSharedEntry& asRealmIndependentShared(); 186 187 JitCode* jitcode() const { return jitcode_; } 188 JitCode** jitcodePtr() { return &jitcode_; } 189 Zone* zone() const { return jitcode()->zone(); } 190 191 bool traceJitcode(JSTracer* trc); 192 bool isJitcodeMarkedFromAnyThread(JSRuntime* rt); 193 194 bool trace(JSTracer* trc); 195 uint64_t realmID(JSRuntime* rt) const; 196 void* canonicalNativeAddrFor(JSRuntime* rt, void* ptr) const; 197 198 // Read the inline call stack at a given point in the native code and append 199 // into the given results buffer. Innermost script will be appended first, and 200 // outermost appended last. 201 uint32_t callStackAtAddr(JSRuntime* rt, void* ptr, 202 CallStackFrameInfo* results, 203 uint32_t maxResults) const; 204 }; 205 206 using UniqueJitcodeGlobalEntry = 207 UniquePtr<JitcodeGlobalEntry, JitcodeGlobalEntry::DestroyPolicy>; 208 209 template <typename T, typename... Args> 210 inline UniqueJitcodeGlobalEntry MakeJitcodeGlobalEntry(JSContext* cx, 211 Args&&... args) { 212 UniqueJitcodeGlobalEntry res(js_new<T>(std::forward<Args>(args)...)); 213 if (!res) { 214 ReportOutOfMemory(cx); 215 } 216 return res; 217 } 218 219 struct ScriptSourceAndExtent { 220 RefPtr<ScriptSource> scriptSource; 221 uint32_t toStringStart; 222 uint32_t toStringEnd; 223 224 explicit ScriptSourceAndExtent(JSScript* script) 225 : scriptSource(script->scriptSource()), 226 toStringStart(script->toStringStart()), 227 toStringEnd(script->toStringEnd()) {} 228 229 bool matches(JSScript* script) const { 230 return scriptSource == script->scriptSource() && 231 toStringStart == script->toStringStart() && 232 toStringEnd == script->toStringEnd(); 233 } 234 }; 235 236 class IonEntry : public JitcodeGlobalEntry { 237 public: 238 struct ScriptListEntry { 239 ScriptSourceAndExtent sourceAndExtent; 240 UniqueChars str; 241 ScriptListEntry(JSScript* script, UniqueChars str) 242 : sourceAndExtent(script), str(std::move(str)) {} 243 }; 244 245 using ScriptList = Vector<ScriptListEntry, 2, SystemAllocPolicy>; 246 247 private: 248 ScriptList scriptList_; 249 250 // regionTable_ points to the start of the region table within the 251 // packed map for compile represented by this entry. Since the 252 // region table occurs at the tail of the memory region, this pointer 253 // points somewhere inside the region memory space, and not to the start 254 // of the memory space. 255 const JitcodeIonTable* regionTable_; 256 257 uint64_t realmId_; 258 259 public: 260 IonEntry(JitCode* code, void* nativeStartAddr, void* nativeEndAddr, 261 ScriptList&& scriptList, JitcodeIonTable* regionTable, 262 uint64_t realmId) 263 : JitcodeGlobalEntry(Kind::Ion, code, nativeStartAddr, nativeEndAddr), 264 scriptList_(std::move(scriptList)), 265 regionTable_(regionTable), 266 realmId_(realmId) { 267 MOZ_ASSERT(regionTable); 268 } 269 270 ~IonEntry(); 271 272 ScriptList& scriptList() { return scriptList_; } 273 274 size_t numScripts() const { return scriptList_.length(); } 275 276 const ScriptSourceAndExtent& getScriptSource(unsigned idx) const { 277 MOZ_ASSERT(idx < numScripts()); 278 return scriptList_[idx].sourceAndExtent; 279 } 280 281 const char* getStr(unsigned idx) const { 282 MOZ_ASSERT(idx < numScripts()); 283 return scriptList_[idx].str.get(); 284 } 285 286 const JitcodeIonTable* regionTable() const { return regionTable_; } 287 288 void* canonicalNativeAddrFor(void* ptr) const; 289 290 uint32_t callStackAtAddr(void* ptr, CallStackFrameInfo* results, 291 uint32_t maxResults) const; 292 293 uint64_t realmID() const { return realmId_; } 294 295 bool trace(JSTracer* trc); 296 }; 297 298 class IonICEntry : public JitcodeGlobalEntry { 299 // Address for this IC in the IonScript code. Most operations on IonICEntry 300 // use this to forward to the IonEntry. 301 void* rejoinAddr_; 302 303 public: 304 IonICEntry(JitCode* code, void* nativeStartAddr, void* nativeEndAddr, 305 void* rejoinAddr) 306 : JitcodeGlobalEntry(Kind::IonIC, code, nativeStartAddr, nativeEndAddr), 307 rejoinAddr_(rejoinAddr) { 308 MOZ_ASSERT(rejoinAddr_); 309 } 310 311 void* rejoinAddr() const { return rejoinAddr_; } 312 313 void* canonicalNativeAddrFor(void* ptr) const; 314 315 uint32_t callStackAtAddr(JSRuntime* rt, void* ptr, 316 CallStackFrameInfo* results, 317 uint32_t maxResults) const; 318 319 uint64_t realmID(JSRuntime* rt) const; 320 321 bool trace(JSTracer* trc); 322 }; 323 324 class BaselineEntry : public JitcodeGlobalEntry { 325 ScriptSourceAndExtent scriptSource_; 326 UniqueChars str_; 327 uint64_t realmId_; 328 329 public: 330 BaselineEntry(JitCode* code, void* nativeStartAddr, void* nativeEndAddr, 331 JSScript* script, UniqueChars str, uint64_t realmId) 332 : JitcodeGlobalEntry(Kind::Baseline, code, nativeStartAddr, 333 nativeEndAddr), 334 scriptSource_(script), 335 str_(std::move(str)), 336 realmId_(realmId) { 337 MOZ_ASSERT(str_); 338 } 339 340 const ScriptSourceAndExtent& scriptSource() const { return scriptSource_; } 341 342 const char* str() const { return str_.get(); } 343 344 void* canonicalNativeAddrFor(void* ptr) const; 345 346 uint32_t callStackAtAddr(void* ptr, CallStackFrameInfo* results, 347 uint32_t maxResults) const; 348 349 uint64_t realmID() const { return realmId_; } 350 351 bool trace(JSTracer* trc); 352 }; 353 354 class RealmIndependentSharedEntry : public JitcodeGlobalEntry { 355 UniqueChars str_; 356 357 public: 358 RealmIndependentSharedEntry(JitCode* code, void* nativeStartAddr, 359 void* nativeEndAddr, UniqueChars str) 360 : JitcodeGlobalEntry(Kind::RealmIndependentShared, code, nativeStartAddr, 361 nativeEndAddr), 362 str_(std::move(str)) { 363 MOZ_ASSERT(str_); 364 } 365 366 const char* str() const { return str_.get(); } 367 368 void* canonicalNativeAddrFor(void* ptr) const; 369 370 [[nodiscard]] bool callStackAtAddr(void* ptr, BytecodeLocationVector& results, 371 uint32_t* depth) const; 372 373 uint32_t callStackAtAddr(void* ptr, CallStackFrameInfo* results, 374 uint32_t maxResults) const; 375 376 uint64_t realmID() const; 377 }; 378 379 class BaselineInterpreterEntry : public JitcodeGlobalEntry { 380 public: 381 BaselineInterpreterEntry(JitCode* code, void* nativeStartAddr, 382 void* nativeEndAddr) 383 : JitcodeGlobalEntry(Kind::BaselineInterpreter, code, nativeStartAddr, 384 nativeEndAddr) {} 385 386 void* canonicalNativeAddrFor(void* ptr) const; 387 388 uint32_t callStackAtAddr(void* ptr, CallStackFrameInfo* results, 389 uint32_t maxResults) const; 390 391 uint64_t realmID() const; 392 }; 393 394 // Dummy entries are created for jitcode generated when profiling is not 395 // turned on, so that they have representation in the global table if they are 396 // on the stack when profiling is enabled. 397 class DummyEntry : public JitcodeGlobalEntry { 398 public: 399 DummyEntry(JitCode* code, void* nativeStartAddr, void* nativeEndAddr) 400 : JitcodeGlobalEntry(Kind::Dummy, code, nativeStartAddr, nativeEndAddr) {} 401 402 void* canonicalNativeAddrFor(JSRuntime* rt, void* ptr) const { 403 return nullptr; 404 } 405 406 uint32_t callStackAtAddr(JSRuntime* rt, void* ptr, 407 CallStackFrameInfo* results, 408 uint32_t maxResults) const { 409 return 0; 410 } 411 412 uint64_t realmID() const { return 0; } 413 }; 414 415 inline const IonEntry& JitcodeGlobalEntry::asIon() const { 416 MOZ_ASSERT(isIon()); 417 return *static_cast<const IonEntry*>(this); 418 } 419 420 inline const IonICEntry& JitcodeGlobalEntry::asIonIC() const { 421 MOZ_ASSERT(isIonIC()); 422 return *static_cast<const IonICEntry*>(this); 423 } 424 425 inline const BaselineEntry& JitcodeGlobalEntry::asBaseline() const { 426 MOZ_ASSERT(isBaseline()); 427 return *static_cast<const BaselineEntry*>(this); 428 } 429 430 inline const BaselineInterpreterEntry& 431 JitcodeGlobalEntry::asBaselineInterpreter() const { 432 MOZ_ASSERT(isBaselineInterpreter()); 433 return *static_cast<const BaselineInterpreterEntry*>(this); 434 } 435 436 inline const DummyEntry& JitcodeGlobalEntry::asDummy() const { 437 MOZ_ASSERT(isDummy()); 438 return *static_cast<const DummyEntry*>(this); 439 } 440 441 inline const RealmIndependentSharedEntry& 442 JitcodeGlobalEntry::asRealmIndependentShared() const { 443 MOZ_ASSERT(isRealmIndependentShared()); 444 return *static_cast<const RealmIndependentSharedEntry*>(this); 445 } 446 447 inline IonEntry& JitcodeGlobalEntry::asIon() { 448 MOZ_ASSERT(isIon()); 449 return *static_cast<IonEntry*>(this); 450 } 451 452 inline IonICEntry& JitcodeGlobalEntry::asIonIC() { 453 MOZ_ASSERT(isIonIC()); 454 return *static_cast<IonICEntry*>(this); 455 } 456 457 inline BaselineEntry& JitcodeGlobalEntry::asBaseline() { 458 MOZ_ASSERT(isBaseline()); 459 return *static_cast<BaselineEntry*>(this); 460 } 461 462 inline BaselineInterpreterEntry& JitcodeGlobalEntry::asBaselineInterpreter() { 463 MOZ_ASSERT(isBaselineInterpreter()); 464 return *static_cast<BaselineInterpreterEntry*>(this); 465 } 466 467 inline DummyEntry& JitcodeGlobalEntry::asDummy() { 468 MOZ_ASSERT(isDummy()); 469 return *static_cast<DummyEntry*>(this); 470 } 471 472 inline RealmIndependentSharedEntry& 473 JitcodeGlobalEntry::asRealmIndependentShared() { 474 MOZ_ASSERT(isRealmIndependentShared()); 475 return *static_cast<RealmIndependentSharedEntry*>(this); 476 } 477 478 // Global table of JitcodeGlobalEntry entries. 479 class JitcodeGlobalTable { 480 private: 481 // Vector containing (and owning) all entries. This is unsorted and used for 482 // iterating over all entries, because the AvlTree currently doesn't support 483 // modifications while iterating. 484 using EntryVector = Vector<UniqueJitcodeGlobalEntry, 0, SystemAllocPolicy>; 485 EntryVector entries_; 486 487 // AVL tree containing all entries in the Vector above. This is used to 488 // efficiently look up the entry corresponding to a native code address. 489 using EntryTree = AvlTree<JitCodeRange*, JitCodeRange>; 490 static const size_t LIFO_CHUNK_SIZE = 16 * 1024; 491 LifoAlloc alloc_; 492 EntryTree tree_; 493 494 public: 495 JitcodeGlobalTable() 496 : alloc_(LIFO_CHUNK_SIZE, js::BackgroundMallocArena), tree_(&alloc_) {} 497 498 bool empty() const { 499 MOZ_ASSERT(entries_.empty() == tree_.empty()); 500 return entries_.empty(); 501 } 502 503 JitcodeGlobalEntry* lookup(void* ptr) { return lookupInternal(ptr); } 504 505 const JitcodeGlobalEntry* lookupForSampler(void* ptr, JSRuntime* rt, 506 uint64_t samplePosInBuffer); 507 508 [[nodiscard]] bool addEntry(UniqueJitcodeGlobalEntry entry); 509 510 void setAllEntriesAsExpired(); 511 [[nodiscard]] bool markIteratively(GCMarker* marker); 512 void traceWeak(JSRuntime* rt, JSTracer* trc); 513 514 private: 515 JitcodeGlobalEntry* lookupInternal(void* ptr); 516 }; 517 518 // clang-format off 519 /* 520 * Container class for main jitcode table. 521 * The Region table's memory is structured as follows: 522 * 523 * +------------------------------------------------+ | 524 * | Region 1 Run | | 525 * |------------------------------------------------| | 526 * | Region 2 Run | | 527 * | | | 528 * | | | 529 * |------------------------------------------------| | 530 * | Region 3 Run | | 531 * | | | 532 * |------------------------------------------------| |-- Payload 533 * | | | 534 * | ... | | 535 * | | | 536 * |------------------------------------------------| | 537 * | Region M Run | | 538 * | | | 539 * +================================================+ <- RegionTable pointer points here 540 * | uint23_t numRegions = M | | 541 * +------------------------------------------------+ | 542 * | Region 1 | | 543 * | uint32_t entryOffset = size(Payload) | | 544 * +------------------------------------------------+ | 545 * | | |-- Table 546 * | ... | | 547 * | | | 548 * +------------------------------------------------+ | 549 * | Region M | | 550 * | uint32_t entryOffset | | 551 * +------------------------------------------------+ | 552 * 553 * The region table is composed of two sections: a tail section that contains a table of 554 * fixed-size entries containing offsets into the the head section, and a head section that 555 * holds a sequence of variable-sized runs. The table in the tail section serves to 556 * locate the variable-length encoded structures in the head section. 557 * 558 * The entryOffsets in the table indicate the bytes offset to subtract from the regionTable 559 * pointer to arrive at the encoded region in the payload. 560 * 561 * 562 * Variable-length entries in payload 563 * ---------------------------------- 564 * The entryOffsets in the region table's fixed-sized entries refer to a location within the 565 * variable-length payload section. This location contains a compactly encoded "run" of 566 * mappings. 567 * 568 * Each run starts by describing the offset within the native code it starts at, and the 569 * sequence of scripts active at that site. Following that, there are a number of 570 * variable-length entries encoding (nativeOffsetDelta, bytecodeOffsetDelta) pairs for the run. 571 * 572 * VarUint32 nativeOffset; 573 * - The offset from nativeStartAddr in the global table entry at which 574 * the jitcode for this region starts. 575 * 576 * Uint8_t scriptDepth; 577 * - The depth of inlined scripts for this region. 578 * 579 * List<VarUint32> inlineScriptPcStack; 580 * - We encode (2 * scriptDepth) VarUint32s here. Each pair of uint32s are taken 581 * as an index into the scriptList in the global table entry, and a pcOffset 582 * respectively. 583 * 584 * List<NativeAndBytecodeDelta> deltaRun; 585 * - The rest of the entry is a deltaRun that stores a series of variable-length 586 * encoded NativeAndBytecodeDelta datums. 587 */ 588 // clang-format on 589 class JitcodeRegionEntry { 590 private: 591 static const unsigned MAX_RUN_LENGTH = 100; 592 593 public: 594 static void WriteHead(CompactBufferWriter& writer, uint32_t nativeOffset, 595 uint8_t scriptDepth); 596 static void ReadHead(CompactBufferReader& reader, uint32_t* nativeOffset, 597 uint8_t* scriptDepth); 598 599 static void WriteScriptPc(CompactBufferWriter& writer, uint32_t scriptIdx, 600 uint32_t pcOffset); 601 static void ReadScriptPc(CompactBufferReader& reader, uint32_t* scriptIdx, 602 uint32_t* pcOffset); 603 604 static void WriteDelta(CompactBufferWriter& writer, uint32_t nativeDelta, 605 int32_t pcDelta); 606 static void ReadDelta(CompactBufferReader& reader, uint32_t* nativeDelta, 607 int32_t* pcDelta); 608 609 // Given a pointer into an array of NativeToBytecode (and a pointer to the end 610 // of the array), compute the number of entries that would be consume by 611 // outputting a run starting at this one. 612 static uint32_t ExpectedRunLength(const NativeToBytecode* entry, 613 const NativeToBytecode* end); 614 615 // Write a run, starting at the given NativeToBytecode entry, into the given 616 // buffer writer. 617 [[nodiscard]] static bool WriteRun(CompactBufferWriter& writer, 618 const IonEntry::ScriptList& scriptList, 619 uint32_t runLength, 620 const NativeToBytecode* entry); 621 622 // Delta Run entry formats are encoded little-endian: 623 // 624 // byte 0 625 // NNNN-BBB0 626 // Single byte format. nativeDelta in [0, 15], pcDelta in [0, 7] 627 // 628 static const uint32_t ENC1_MASK = 0x1; 629 static const uint32_t ENC1_MASK_VAL = 0x0; 630 631 static const uint32_t ENC1_NATIVE_DELTA_MAX = 0xf; 632 static const unsigned ENC1_NATIVE_DELTA_SHIFT = 4; 633 634 static const uint32_t ENC1_PC_DELTA_MASK = 0x0e; 635 static const int32_t ENC1_PC_DELTA_MAX = 0x7; 636 static const unsigned ENC1_PC_DELTA_SHIFT = 1; 637 638 // byte 1 byte 0 639 // NNNN-NNNN BBBB-BB01 640 // Two-byte format. nativeDelta in [0, 255], pcDelta in [0, 63] 641 // 642 static const uint32_t ENC2_MASK = 0x3; 643 static const uint32_t ENC2_MASK_VAL = 0x1; 644 645 static const uint32_t ENC2_NATIVE_DELTA_MAX = 0xff; 646 static const unsigned ENC2_NATIVE_DELTA_SHIFT = 8; 647 648 static const uint32_t ENC2_PC_DELTA_MASK = 0x00fc; 649 static const int32_t ENC2_PC_DELTA_MAX = 0x3f; 650 static const unsigned ENC2_PC_DELTA_SHIFT = 2; 651 652 // byte 2 byte 1 byte 0 653 // NNNN-NNNN NNNB-BBBB BBBB-B011 654 // Three-byte format. nativeDelta in [0, 2047], pcDelta in [-512, 511] 655 // 656 static const uint32_t ENC3_MASK = 0x7; 657 static const uint32_t ENC3_MASK_VAL = 0x3; 658 659 static const uint32_t ENC3_NATIVE_DELTA_MAX = 0x7ff; 660 static const unsigned ENC3_NATIVE_DELTA_SHIFT = 13; 661 662 static const uint32_t ENC3_PC_DELTA_MASK = 0x001ff8; 663 static const int32_t ENC3_PC_DELTA_MAX = 0x1ff; 664 static const int32_t ENC3_PC_DELTA_MIN = -ENC3_PC_DELTA_MAX - 1; 665 static const unsigned ENC3_PC_DELTA_SHIFT = 3; 666 667 // byte 3 byte 2 byte 1 byte 0 668 // NNNN-NNNN NNNN-NNNN BBBB-BBBB BBBB-B111 669 // Three-byte format. nativeDelta in [0, 65535], 670 // pcDelta in [-4096, 4095] 671 static const uint32_t ENC4_MASK = 0x7; 672 static const uint32_t ENC4_MASK_VAL = 0x7; 673 674 static const uint32_t ENC4_NATIVE_DELTA_MAX = 0xffff; 675 static const unsigned ENC4_NATIVE_DELTA_SHIFT = 16; 676 677 static const uint32_t ENC4_PC_DELTA_MASK = 0x0000fff8; 678 static const int32_t ENC4_PC_DELTA_MAX = 0xfff; 679 static const int32_t ENC4_PC_DELTA_MIN = -ENC4_PC_DELTA_MAX - 1; 680 static const unsigned ENC4_PC_DELTA_SHIFT = 3; 681 682 static bool IsDeltaEncodeable(uint32_t nativeDelta, int32_t pcDelta) { 683 return (nativeDelta <= ENC4_NATIVE_DELTA_MAX) && 684 (pcDelta >= ENC4_PC_DELTA_MIN) && (pcDelta <= ENC4_PC_DELTA_MAX); 685 } 686 687 private: 688 const uint8_t* data_; 689 const uint8_t* end_; 690 691 // Unpacked state from jitcode entry. 692 uint32_t nativeOffset_; 693 uint8_t scriptDepth_; 694 const uint8_t* scriptPcStack_; 695 const uint8_t* deltaRun_; 696 697 void unpack(); 698 699 public: 700 JitcodeRegionEntry(const uint8_t* data, const uint8_t* end) 701 : data_(data), 702 end_(end), 703 nativeOffset_(0), 704 scriptDepth_(0), 705 scriptPcStack_(nullptr), 706 deltaRun_(nullptr) { 707 MOZ_ASSERT(data_ < end_); 708 unpack(); 709 MOZ_ASSERT(scriptPcStack_ < end_); 710 MOZ_ASSERT(deltaRun_ <= end_); 711 } 712 713 uint32_t nativeOffset() const { return nativeOffset_; } 714 uint32_t scriptDepth() const { return scriptDepth_; } 715 716 class ScriptPcIterator { 717 private: 718 const uint8_t* start_; 719 const uint8_t* end_; 720 #ifdef DEBUG 721 uint32_t count_; 722 #endif 723 uint32_t idx_; 724 const uint8_t* cur_; 725 726 public: 727 ScriptPcIterator(const uint8_t* start, const uint8_t* end, uint32_t count) 728 : start_(start), 729 end_(end), 730 #ifdef DEBUG 731 count_(count), 732 #endif 733 idx_(0), 734 cur_(start_) { 735 } 736 737 bool hasMore() const { 738 MOZ_ASSERT((idx_ == count_) == (cur_ == end_)); 739 MOZ_ASSERT((idx_ < count_) == (cur_ < end_)); 740 return cur_ < end_; 741 } 742 743 void readNext(uint32_t* scriptIdxOut, uint32_t* pcOffsetOut) { 744 MOZ_ASSERT(scriptIdxOut); 745 MOZ_ASSERT(pcOffsetOut); 746 MOZ_ASSERT(hasMore()); 747 748 CompactBufferReader reader(cur_, end_); 749 ReadScriptPc(reader, scriptIdxOut, pcOffsetOut); 750 751 cur_ = reader.currentPosition(); 752 MOZ_ASSERT(cur_ <= end_); 753 754 idx_++; 755 MOZ_ASSERT_IF(idx_ == count_, cur_ == end_); 756 } 757 758 void reset() { 759 idx_ = 0; 760 cur_ = start_; 761 } 762 }; 763 764 ScriptPcIterator scriptPcIterator() const { 765 // End of script+pc sequence is the start of the delta run. 766 return ScriptPcIterator(scriptPcStack_, deltaRun_, scriptDepth_); 767 } 768 769 class DeltaIterator { 770 private: 771 const uint8_t* start_; 772 const uint8_t* end_; 773 const uint8_t* cur_; 774 775 public: 776 DeltaIterator(const uint8_t* start, const uint8_t* end) 777 : start_(start), end_(end), cur_(start) {} 778 779 bool hasMore() const { 780 MOZ_ASSERT(cur_ <= end_); 781 return cur_ < end_; 782 } 783 784 void readNext(uint32_t* nativeDeltaOut, int32_t* pcDeltaOut) { 785 MOZ_ASSERT(nativeDeltaOut != nullptr); 786 MOZ_ASSERT(pcDeltaOut != nullptr); 787 788 MOZ_ASSERT(hasMore()); 789 790 CompactBufferReader reader(cur_, end_); 791 ReadDelta(reader, nativeDeltaOut, pcDeltaOut); 792 793 cur_ = reader.currentPosition(); 794 MOZ_ASSERT(cur_ <= end_); 795 } 796 797 void reset() { cur_ = start_; } 798 }; 799 DeltaIterator deltaIterator() const { return DeltaIterator(deltaRun_, end_); } 800 801 uint32_t findPcOffset(uint32_t queryNativeOffset, 802 uint32_t startPcOffset) const; 803 }; 804 805 class JitcodeIonTable { 806 private: 807 /* Variable length payload section "below" here. */ 808 uint32_t numRegions_; 809 uint32_t regionOffsets_[1]; 810 811 const uint8_t* payloadEnd() const { 812 return reinterpret_cast<const uint8_t*>(this); 813 } 814 815 public: 816 JitcodeIonTable() = delete; 817 818 uint32_t numRegions() const { return numRegions_; } 819 820 uint32_t regionOffset(uint32_t regionIndex) const { 821 MOZ_ASSERT(regionIndex < numRegions()); 822 return regionOffsets_[regionIndex]; 823 } 824 825 JitcodeRegionEntry regionEntry(uint32_t regionIndex) const { 826 const uint8_t* regionStart = payloadEnd() - regionOffset(regionIndex); 827 const uint8_t* regionEnd = payloadEnd(); 828 if (regionIndex < numRegions_ - 1) { 829 regionEnd -= regionOffset(regionIndex + 1); 830 } 831 return JitcodeRegionEntry(regionStart, regionEnd); 832 } 833 834 uint32_t findRegionEntry(uint32_t offset) const; 835 836 const uint8_t* payloadStart() const { 837 // The beginning of the payload the beginning of the first region are the 838 // same. 839 return payloadEnd() - regionOffset(0); 840 } 841 842 [[nodiscard]] static bool WriteIonTable( 843 CompactBufferWriter& writer, const IonEntry::ScriptList& scriptList, 844 const NativeToBytecode* start, const NativeToBytecode* end, 845 uint32_t* tableOffsetOut, uint32_t* numRegionsOut); 846 }; 847 848 } // namespace jit 849 } // namespace js 850 851 #endif /* jit_JitcodeMap_h */