/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- * vim: set ts=8 sts=2 et sw=2 tw=80: * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef jit_JitcodeMap_h #define jit_JitcodeMap_h #include "mozilla/Assertions.h" // MOZ_ASSERT, MOZ_ASSERT_IF, MOZ_CRASH #include // size_t #include // uint8_t, uint32_t, uint64_t #include "ds/AvlTree.h" // AvlTree #include "jit/CompactBuffer.h" // CompactBufferReader, CompactBufferWriter #include "jit/shared/Assembler-shared.h" // CodeOffset #include "js/AllocPolicy.h" // SystemAllocPolicy #include "js/TypeDecls.h" // jsbytecode #include "js/Vector.h" // Vector #include "vm/BytecodeLocation.h" // BytecodeLocation class JSScript; class JSTracer; struct JSRuntime; namespace JS { class Zone; } // namespace JS namespace js { class GCMarker; namespace jit { class InlineScriptTree; /* * The jitcode map implements tables to allow mapping from addresses in jitcode * to the list of (JSScript*, jsbytecode*) pairs that are implicitly active in * the frame at that point in the native code. * * To represent this information efficiently, a multi-level table is used. * * At the top level, a global AVL-tree of JitcodeGlobalEntry describing the * mapping for each individual JitCode generated by compiles. The entries are * ordered by their nativeStartAddr. * * Every entry in the table is of fixed size, but there are different entry * types, distinguished by the kind field. */ class JitcodeGlobalTable; class JitcodeIonTable; class JitcodeRegionEntry; struct NativeToBytecode { CodeOffset nativeOffset; InlineScriptTree* tree; jsbytecode* pc; }; // Describes range [start, end) of JIT-generated code. class JitCodeRange { protected: void* const nativeStartAddr_; void* const nativeEndAddr_; public: JitCodeRange(void* start, void* end) : nativeStartAddr_(start), nativeEndAddr_(end) { MOZ_ASSERT(start < end); } // Comparator used by the AvlTree. static int compare(const JitCodeRange* r1, const JitCodeRange* r2) { // JitCodeRange includes 'start' but excludes 'end'. if (r1->nativeEndAddr_ <= r2->nativeStartAddr_) { return -1; } if (r1->nativeStartAddr_ >= r2->nativeEndAddr_) { return 1; } return 0; } void* nativeStartAddr() const { return nativeStartAddr_; } void* nativeEndAddr() const { return nativeEndAddr_; } bool containsPointer(void* ptr) const { return nativeStartAddr() <= ptr && ptr < nativeEndAddr(); } }; typedef Vector BytecodeLocationVector; class IonEntry; class IonICEntry; class BaselineEntry; class BaselineInterpreterEntry; class DummyEntry; // Base class for all entries. class JitcodeGlobalEntry : public JitCodeRange { protected: JitCode* jitcode_; // If this entry is referenced from the profiler buffer, this is the // position where the most recent sample that references it starts. // Otherwise set to kNoSampleInBuffer. static const uint64_t kNoSampleInBuffer = UINT64_MAX; uint64_t samplePositionInBuffer_ = kNoSampleInBuffer; public: enum class Kind : uint8_t { Ion, IonIC, Baseline, BaselineInterpreter, Dummy }; protected: Kind kind_; JitcodeGlobalEntry(Kind kind, JitCode* code, void* nativeStartAddr, void* nativeEndAddr) : JitCodeRange(nativeStartAddr, nativeEndAddr), jitcode_(code), kind_(kind) { MOZ_ASSERT(code); MOZ_ASSERT(nativeStartAddr); MOZ_ASSERT(nativeEndAddr); } // Protected destructor to ensure this is called through DestroyPolicy. ~JitcodeGlobalEntry() = default; JitcodeGlobalEntry(const JitcodeGlobalEntry& other) = delete; void operator=(const JitcodeGlobalEntry& other) = delete; public: struct DestroyPolicy { void operator()(JitcodeGlobalEntry* entry); }; void setSamplePositionInBuffer(uint64_t bufferWritePos) { samplePositionInBuffer_ = bufferWritePos; } void setAsExpired() { samplePositionInBuffer_ = kNoSampleInBuffer; } bool isSampled(uint64_t bufferRangeStart) { if (samplePositionInBuffer_ == kNoSampleInBuffer) { return false; } return bufferRangeStart <= samplePositionInBuffer_; } Kind kind() const { return kind_; } bool isIon() const { return kind() == Kind::Ion; } bool isIonIC() const { return kind() == Kind::IonIC; } bool isBaseline() const { return kind() == Kind::Baseline; } bool isBaselineInterpreter() const { return kind() == Kind::BaselineInterpreter; } bool isDummy() const { return kind() == Kind::Dummy; } inline const IonEntry& asIon() const; inline const IonICEntry& asIonIC() const; inline const BaselineEntry& asBaseline() const; inline const BaselineInterpreterEntry& asBaselineInterpreter() const; inline const DummyEntry& asDummy() const; inline IonEntry& asIon(); inline IonICEntry& asIonIC(); inline BaselineEntry& asBaseline(); inline BaselineInterpreterEntry& asBaselineInterpreter(); inline DummyEntry& asDummy(); JitCode* jitcode() const { return jitcode_; } JitCode** jitcodePtr() { return &jitcode_; } Zone* zone() const { return jitcode()->zone(); } bool traceJitcode(JSTracer* trc); bool isJitcodeMarkedFromAnyThread(JSRuntime* rt); bool trace(JSTracer* trc); void traceWeak(JSTracer* trc); uint64_t lookupRealmID(JSRuntime* rt, void* ptr) const; void* canonicalNativeAddrFor(JSRuntime* rt, void* ptr) const; // Read the inline call stack at a given point in the native code and append // into the given vector. Innermost (script,pc) pair will be appended first, // and outermost appended last. // // Returns false on memory failure. [[nodiscard]] bool callStackAtAddr(JSRuntime* rt, void* ptr, BytecodeLocationVector& results, uint32_t* depth) const; uint32_t callStackAtAddr(JSRuntime* rt, void* ptr, const char** results, uint32_t maxResults) const; }; using UniqueJitcodeGlobalEntry = UniquePtr; template inline UniqueJitcodeGlobalEntry MakeJitcodeGlobalEntry(JSContext* cx, Args&&... args) { UniqueJitcodeGlobalEntry res(js_new(std::forward(args)...)); if (!res) { ReportOutOfMemory(cx); } return res; } class IonEntry : public JitcodeGlobalEntry { public: struct ScriptNamePair { JSScript* script; UniqueChars str; ScriptNamePair(JSScript* script, UniqueChars str) : script(script), str(std::move(str)) {} }; using ScriptList = Vector; private: ScriptList scriptList_; // regionTable_ points to the start of the region table within the // packed map for compile represented by this entry. Since the // region table occurs at the tail of the memory region, this pointer // points somewhere inside the region memory space, and not to the start // of the memory space. const JitcodeIonTable* regionTable_; public: IonEntry(JitCode* code, void* nativeStartAddr, void* nativeEndAddr, ScriptList&& scriptList, JitcodeIonTable* regionTable) : JitcodeGlobalEntry(Kind::Ion, code, nativeStartAddr, nativeEndAddr), scriptList_(std::move(scriptList)), regionTable_(regionTable) { MOZ_ASSERT(regionTable); } ~IonEntry(); ScriptList& scriptList() { return scriptList_; } size_t numScripts() const { return scriptList_.length(); } JSScript* getScript(unsigned idx) const { MOZ_ASSERT(idx < numScripts()); return scriptList_[idx].script; } const char* getStr(unsigned idx) const { MOZ_ASSERT(idx < numScripts()); return scriptList_[idx].str.get(); } const JitcodeIonTable* regionTable() const { return regionTable_; } void* canonicalNativeAddrFor(void* ptr) const; [[nodiscard]] bool callStackAtAddr(void* ptr, BytecodeLocationVector& results, uint32_t* depth) const; uint32_t callStackAtAddr(void* ptr, const char** results, uint32_t maxResults) const; uint64_t lookupRealmID(void* ptr) const; bool trace(JSTracer* trc); void traceWeak(JSTracer* trc); }; class IonICEntry : public JitcodeGlobalEntry { // Address for this IC in the IonScript code. Most operations on IonICEntry // use this to forward to the IonEntry. void* rejoinAddr_; public: IonICEntry(JitCode* code, void* nativeStartAddr, void* nativeEndAddr, void* rejoinAddr) : JitcodeGlobalEntry(Kind::IonIC, code, nativeStartAddr, nativeEndAddr), rejoinAddr_(rejoinAddr) { MOZ_ASSERT(rejoinAddr_); } void* rejoinAddr() const { return rejoinAddr_; } void* canonicalNativeAddrFor(void* ptr) const; [[nodiscard]] bool callStackAtAddr(JSRuntime* rt, void* ptr, BytecodeLocationVector& results, uint32_t* depth) const; uint32_t callStackAtAddr(JSRuntime* rt, void* ptr, const char** results, uint32_t maxResults) const; uint64_t lookupRealmID(JSRuntime* rt, void* ptr) const; bool trace(JSTracer* trc); void traceWeak(JSTracer* trc); }; class BaselineEntry : public JitcodeGlobalEntry { JSScript* script_; UniqueChars str_; public: BaselineEntry(JitCode* code, void* nativeStartAddr, void* nativeEndAddr, JSScript* script, UniqueChars str) : JitcodeGlobalEntry(Kind::Baseline, code, nativeStartAddr, nativeEndAddr), script_(script), str_(std::move(str)) { MOZ_ASSERT(script_); MOZ_ASSERT(str_); } JSScript* script() const { return script_; } const char* str() const { return str_.get(); } void* canonicalNativeAddrFor(void* ptr) const; [[nodiscard]] bool callStackAtAddr(void* ptr, BytecodeLocationVector& results, uint32_t* depth) const; uint32_t callStackAtAddr(void* ptr, const char** results, uint32_t maxResults) const; uint64_t lookupRealmID() const; bool trace(JSTracer* trc); void traceWeak(JSTracer* trc); }; class BaselineInterpreterEntry : public JitcodeGlobalEntry { public: BaselineInterpreterEntry(JitCode* code, void* nativeStartAddr, void* nativeEndAddr) : JitcodeGlobalEntry(Kind::BaselineInterpreter, code, nativeStartAddr, nativeEndAddr) {} void* canonicalNativeAddrFor(void* ptr) const; [[nodiscard]] bool callStackAtAddr(void* ptr, BytecodeLocationVector& results, uint32_t* depth) const; uint32_t callStackAtAddr(void* ptr, const char** results, uint32_t maxResults) const; uint64_t lookupRealmID() const; }; // Dummy entries are created for jitcode generated when profiling is not // turned on, so that they have representation in the global table if they are // on the stack when profiling is enabled. class DummyEntry : public JitcodeGlobalEntry { public: DummyEntry(JitCode* code, void* nativeStartAddr, void* nativeEndAddr) : JitcodeGlobalEntry(Kind::Dummy, code, nativeStartAddr, nativeEndAddr) {} void* canonicalNativeAddrFor(JSRuntime* rt, void* ptr) const { return nullptr; } [[nodiscard]] bool callStackAtAddr(JSRuntime* rt, void* ptr, BytecodeLocationVector& results, uint32_t* depth) const { return true; } uint32_t callStackAtAddr(JSRuntime* rt, void* ptr, const char** results, uint32_t maxResults) const { return 0; } uint64_t lookupRealmID() const { return 0; } }; inline const IonEntry& JitcodeGlobalEntry::asIon() const { MOZ_ASSERT(isIon()); return *static_cast(this); } inline const IonICEntry& JitcodeGlobalEntry::asIonIC() const { MOZ_ASSERT(isIonIC()); return *static_cast(this); } inline const BaselineEntry& JitcodeGlobalEntry::asBaseline() const { MOZ_ASSERT(isBaseline()); return *static_cast(this); } inline const BaselineInterpreterEntry& JitcodeGlobalEntry::asBaselineInterpreter() const { MOZ_ASSERT(isBaselineInterpreter()); return *static_cast(this); } inline const DummyEntry& JitcodeGlobalEntry::asDummy() const { MOZ_ASSERT(isDummy()); return *static_cast(this); } inline IonEntry& JitcodeGlobalEntry::asIon() { MOZ_ASSERT(isIon()); return *static_cast(this); } inline IonICEntry& JitcodeGlobalEntry::asIonIC() { MOZ_ASSERT(isIonIC()); return *static_cast(this); } inline BaselineEntry& JitcodeGlobalEntry::asBaseline() { MOZ_ASSERT(isBaseline()); return *static_cast(this); } inline BaselineInterpreterEntry& JitcodeGlobalEntry::asBaselineInterpreter() { MOZ_ASSERT(isBaselineInterpreter()); return *static_cast(this); } inline DummyEntry& JitcodeGlobalEntry::asDummy() { MOZ_ASSERT(isDummy()); return *static_cast(this); } // Global table of JitcodeGlobalEntry entries. class JitcodeGlobalTable { private: // Vector containing (and owning) all entries. This is unsorted and used for // iterating over all entries, because the AvlTree currently doesn't support // modifications while iterating. using EntryVector = Vector; EntryVector entries_; // AVL tree containing all entries in the Vector above. This is used to // efficiently look up the entry corresponding to a native code address. using EntryTree = AvlTree; static const size_t LIFO_CHUNK_SIZE = 16 * 1024; LifoAlloc alloc_; EntryTree tree_; public: JitcodeGlobalTable() : alloc_(LIFO_CHUNK_SIZE), tree_(&alloc_) {} bool empty() const { MOZ_ASSERT(entries_.empty() == tree_.empty()); return entries_.empty(); } JitcodeGlobalEntry* lookup(void* ptr) { return lookupInternal(ptr); } const JitcodeGlobalEntry* lookupForSampler(void* ptr, JSRuntime* rt, uint64_t samplePosInBuffer); [[nodiscard]] bool addEntry(UniqueJitcodeGlobalEntry entry); void setAllEntriesAsExpired(); [[nodiscard]] bool markIteratively(GCMarker* marker); void traceWeak(JSRuntime* rt, JSTracer* trc); private: JitcodeGlobalEntry* lookupInternal(void* ptr); }; // clang-format off /* * Container class for main jitcode table. * The Region table's memory is structured as follows: * * +------------------------------------------------+ | * | Region 1 Run | | * |------------------------------------------------| | * | Region 2 Run | | * | | | * | | | * |------------------------------------------------| | * | Region 3 Run | | * | | | * |------------------------------------------------| |-- Payload * | | | * | ... | | * | | | * |------------------------------------------------| | * | Region M Run | | * | | | * +================================================+ <- RegionTable pointer points here * | uint23_t numRegions = M | | * +------------------------------------------------+ | * | Region 1 | | * | uint32_t entryOffset = size(Payload) | | * +------------------------------------------------+ | * | | |-- Table * | ... | | * | | | * +------------------------------------------------+ | * | Region M | | * | uint32_t entryOffset | | * +------------------------------------------------+ | * * The region table is composed of two sections: a tail section that contains a table of * fixed-size entries containing offsets into the the head section, and a head section that * holds a sequence of variable-sized runs. The table in the tail section serves to * locate the variable-length encoded structures in the head section. * * The entryOffsets in the table indicate the bytes offset to subtract from the regionTable * pointer to arrive at the encoded region in the payload. * * * Variable-length entries in payload * ---------------------------------- * The entryOffsets in the region table's fixed-sized entries refer to a location within the * variable-length payload section. This location contains a compactly encoded "run" of * mappings. * * Each run starts by describing the offset within the native code it starts at, and the * sequence of (JSScript*, jsbytecode*) pairs active at that site. Following that, there * are a number of variable-length entries encoding (nativeOffsetDelta, bytecodeOffsetDelta) * pairs for the run. * * VarUint32 nativeOffset; * - The offset from nativeStartAddr in the global table entry at which * the jitcode for this region starts. * * Uint8_t scriptDepth; * - The depth of inlined scripts for this region. * * List inlineScriptPcStack; * - We encode (2 * scriptDepth) VarUint32s here. Each pair of uint32s are taken * as an index into the scriptList in the global table entry, and a pcOffset * respectively. * * List deltaRun; * - The rest of the entry is a deltaRun that stores a series of variable-length * encoded NativeAndBytecodeDelta datums. */ // clang-format on class JitcodeRegionEntry { private: static const unsigned MAX_RUN_LENGTH = 100; public: static void WriteHead(CompactBufferWriter& writer, uint32_t nativeOffset, uint8_t scriptDepth); static void ReadHead(CompactBufferReader& reader, uint32_t* nativeOffset, uint8_t* scriptDepth); static void WriteScriptPc(CompactBufferWriter& writer, uint32_t scriptIdx, uint32_t pcOffset); static void ReadScriptPc(CompactBufferReader& reader, uint32_t* scriptIdx, uint32_t* pcOffset); static void WriteDelta(CompactBufferWriter& writer, uint32_t nativeDelta, int32_t pcDelta); static void ReadDelta(CompactBufferReader& reader, uint32_t* nativeDelta, int32_t* pcDelta); // Given a pointer into an array of NativeToBytecode (and a pointer to the end // of the array), compute the number of entries that would be consume by // outputting a run starting at this one. static uint32_t ExpectedRunLength(const NativeToBytecode* entry, const NativeToBytecode* end); // Write a run, starting at the given NativeToBytecode entry, into the given // buffer writer. [[nodiscard]] static bool WriteRun(CompactBufferWriter& writer, const IonEntry::ScriptList& scriptList, uint32_t runLength, const NativeToBytecode* entry); // Delta Run entry formats are encoded little-endian: // // byte 0 // NNNN-BBB0 // Single byte format. nativeDelta in [0, 15], pcDelta in [0, 7] // static const uint32_t ENC1_MASK = 0x1; static const uint32_t ENC1_MASK_VAL = 0x0; static const uint32_t ENC1_NATIVE_DELTA_MAX = 0xf; static const unsigned ENC1_NATIVE_DELTA_SHIFT = 4; static const uint32_t ENC1_PC_DELTA_MASK = 0x0e; static const int32_t ENC1_PC_DELTA_MAX = 0x7; static const unsigned ENC1_PC_DELTA_SHIFT = 1; // byte 1 byte 0 // NNNN-NNNN BBBB-BB01 // Two-byte format. nativeDelta in [0, 255], pcDelta in [0, 63] // static const uint32_t ENC2_MASK = 0x3; static const uint32_t ENC2_MASK_VAL = 0x1; static const uint32_t ENC2_NATIVE_DELTA_MAX = 0xff; static const unsigned ENC2_NATIVE_DELTA_SHIFT = 8; static const uint32_t ENC2_PC_DELTA_MASK = 0x00fc; static const int32_t ENC2_PC_DELTA_MAX = 0x3f; static const unsigned ENC2_PC_DELTA_SHIFT = 2; // byte 2 byte 1 byte 0 // NNNN-NNNN NNNB-BBBB BBBB-B011 // Three-byte format. nativeDelta in [0, 2047], pcDelta in [-512, 511] // static const uint32_t ENC3_MASK = 0x7; static const uint32_t ENC3_MASK_VAL = 0x3; static const uint32_t ENC3_NATIVE_DELTA_MAX = 0x7ff; static const unsigned ENC3_NATIVE_DELTA_SHIFT = 13; static const uint32_t ENC3_PC_DELTA_MASK = 0x001ff8; static const int32_t ENC3_PC_DELTA_MAX = 0x1ff; static const int32_t ENC3_PC_DELTA_MIN = -ENC3_PC_DELTA_MAX - 1; static const unsigned ENC3_PC_DELTA_SHIFT = 3; // byte 3 byte 2 byte 1 byte 0 // NNNN-NNNN NNNN-NNNN BBBB-BBBB BBBB-B111 // Three-byte format. nativeDelta in [0, 65535], // pcDelta in [-4096, 4095] static const uint32_t ENC4_MASK = 0x7; static const uint32_t ENC4_MASK_VAL = 0x7; static const uint32_t ENC4_NATIVE_DELTA_MAX = 0xffff; static const unsigned ENC4_NATIVE_DELTA_SHIFT = 16; static const uint32_t ENC4_PC_DELTA_MASK = 0x0000fff8; static const int32_t ENC4_PC_DELTA_MAX = 0xfff; static const int32_t ENC4_PC_DELTA_MIN = -ENC4_PC_DELTA_MAX - 1; static const unsigned ENC4_PC_DELTA_SHIFT = 3; static bool IsDeltaEncodeable(uint32_t nativeDelta, int32_t pcDelta) { return (nativeDelta <= ENC4_NATIVE_DELTA_MAX) && (pcDelta >= ENC4_PC_DELTA_MIN) && (pcDelta <= ENC4_PC_DELTA_MAX); } private: const uint8_t* data_; const uint8_t* end_; // Unpacked state from jitcode entry. uint32_t nativeOffset_; uint8_t scriptDepth_; const uint8_t* scriptPcStack_; const uint8_t* deltaRun_; void unpack(); public: JitcodeRegionEntry(const uint8_t* data, const uint8_t* end) : data_(data), end_(end), nativeOffset_(0), scriptDepth_(0), scriptPcStack_(nullptr), deltaRun_(nullptr) { MOZ_ASSERT(data_ < end_); unpack(); MOZ_ASSERT(scriptPcStack_ < end_); MOZ_ASSERT(deltaRun_ <= end_); } uint32_t nativeOffset() const { return nativeOffset_; } uint32_t scriptDepth() const { return scriptDepth_; } class ScriptPcIterator { private: const uint8_t* start_; const uint8_t* end_; #ifdef DEBUG uint32_t count_; #endif uint32_t idx_; const uint8_t* cur_; public: ScriptPcIterator(const uint8_t* start, const uint8_t* end, uint32_t count) : start_(start), end_(end), #ifdef DEBUG count_(count), #endif idx_(0), cur_(start_) { } bool hasMore() const { MOZ_ASSERT((idx_ == count_) == (cur_ == end_)); MOZ_ASSERT((idx_ < count_) == (cur_ < end_)); return cur_ < end_; } void readNext(uint32_t* scriptIdxOut, uint32_t* pcOffsetOut) { MOZ_ASSERT(scriptIdxOut); MOZ_ASSERT(pcOffsetOut); MOZ_ASSERT(hasMore()); CompactBufferReader reader(cur_, end_); ReadScriptPc(reader, scriptIdxOut, pcOffsetOut); cur_ = reader.currentPosition(); MOZ_ASSERT(cur_ <= end_); idx_++; MOZ_ASSERT_IF(idx_ == count_, cur_ == end_); } void reset() { idx_ = 0; cur_ = start_; } }; ScriptPcIterator scriptPcIterator() const { // End of script+pc sequence is the start of the delta run. return ScriptPcIterator(scriptPcStack_, deltaRun_, scriptDepth_); } class DeltaIterator { private: const uint8_t* start_; const uint8_t* end_; const uint8_t* cur_; public: DeltaIterator(const uint8_t* start, const uint8_t* end) : start_(start), end_(end), cur_(start) {} bool hasMore() const { MOZ_ASSERT(cur_ <= end_); return cur_ < end_; } void readNext(uint32_t* nativeDeltaOut, int32_t* pcDeltaOut) { MOZ_ASSERT(nativeDeltaOut != nullptr); MOZ_ASSERT(pcDeltaOut != nullptr); MOZ_ASSERT(hasMore()); CompactBufferReader reader(cur_, end_); ReadDelta(reader, nativeDeltaOut, pcDeltaOut); cur_ = reader.currentPosition(); MOZ_ASSERT(cur_ <= end_); } void reset() { cur_ = start_; } }; DeltaIterator deltaIterator() const { return DeltaIterator(deltaRun_, end_); } uint32_t findPcOffset(uint32_t queryNativeOffset, uint32_t startPcOffset) const; }; class JitcodeIonTable { private: /* Variable length payload section "below" here. */ uint32_t numRegions_; uint32_t regionOffsets_[1]; const uint8_t* payloadEnd() const { return reinterpret_cast(this); } public: JitcodeIonTable() = delete; uint32_t numRegions() const { return numRegions_; } uint32_t regionOffset(uint32_t regionIndex) const { MOZ_ASSERT(regionIndex < numRegions()); return regionOffsets_[regionIndex]; } JitcodeRegionEntry regionEntry(uint32_t regionIndex) const { const uint8_t* regionStart = payloadEnd() - regionOffset(regionIndex); const uint8_t* regionEnd = payloadEnd(); if (regionIndex < numRegions_ - 1) { regionEnd -= regionOffset(regionIndex + 1); } return JitcodeRegionEntry(regionStart, regionEnd); } uint32_t findRegionEntry(uint32_t offset) const; const uint8_t* payloadStart() const { // The beginning of the payload the beginning of the first region are the // same. return payloadEnd() - regionOffset(0); } [[nodiscard]] static bool WriteIonTable( CompactBufferWriter& writer, const IonEntry::ScriptList& scriptList, const NativeToBytecode* start, const NativeToBytecode* end, uint32_t* tableOffsetOut, uint32_t* numRegionsOut); }; } // namespace jit } // namespace js #endif /* jit_JitcodeMap_h */