diff options
Diffstat (limited to 'js/src/jit/JitcodeMap.h')
-rw-r--r-- | js/src/jit/JitcodeMap.h | 808 |
1 files changed, 808 insertions, 0 deletions
diff --git a/js/src/jit/JitcodeMap.h b/js/src/jit/JitcodeMap.h new file mode 100644 index 0000000000..b4ed8ae7ff --- /dev/null +++ b/js/src/jit/JitcodeMap.h @@ -0,0 +1,808 @@ +/* -*- 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 <stddef.h> // size_t +#include <stdint.h> // 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<BytecodeLocation, 0, SystemAllocPolicy> 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<JitcodeGlobalEntry, JitcodeGlobalEntry::DestroyPolicy>; + +template <typename T, typename... Args> +inline UniqueJitcodeGlobalEntry MakeJitcodeGlobalEntry(JSContext* cx, + Args&&... args) { + UniqueJitcodeGlobalEntry res(js_new<T>(std::forward<Args>(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<ScriptNamePair, 2, SystemAllocPolicy>; + + 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<const IonEntry*>(this); +} + +inline const IonICEntry& JitcodeGlobalEntry::asIonIC() const { + MOZ_ASSERT(isIonIC()); + return *static_cast<const IonICEntry*>(this); +} + +inline const BaselineEntry& JitcodeGlobalEntry::asBaseline() const { + MOZ_ASSERT(isBaseline()); + return *static_cast<const BaselineEntry*>(this); +} + +inline const BaselineInterpreterEntry& +JitcodeGlobalEntry::asBaselineInterpreter() const { + MOZ_ASSERT(isBaselineInterpreter()); + return *static_cast<const BaselineInterpreterEntry*>(this); +} + +inline const DummyEntry& JitcodeGlobalEntry::asDummy() const { + MOZ_ASSERT(isDummy()); + return *static_cast<const DummyEntry*>(this); +} + +inline IonEntry& JitcodeGlobalEntry::asIon() { + MOZ_ASSERT(isIon()); + return *static_cast<IonEntry*>(this); +} + +inline IonICEntry& JitcodeGlobalEntry::asIonIC() { + MOZ_ASSERT(isIonIC()); + return *static_cast<IonICEntry*>(this); +} + +inline BaselineEntry& JitcodeGlobalEntry::asBaseline() { + MOZ_ASSERT(isBaseline()); + return *static_cast<BaselineEntry*>(this); +} + +inline BaselineInterpreterEntry& JitcodeGlobalEntry::asBaselineInterpreter() { + MOZ_ASSERT(isBaselineInterpreter()); + return *static_cast<BaselineInterpreterEntry*>(this); +} + +inline DummyEntry& JitcodeGlobalEntry::asDummy() { + MOZ_ASSERT(isDummy()); + return *static_cast<DummyEntry*>(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<UniqueJitcodeGlobalEntry, 0, SystemAllocPolicy>; + 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<JitCodeRange*, JitCodeRange>; + 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<VarUint32> 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<NativeAndBytecodeDelta> 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<const uint8_t*>(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 */ |