tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 */