/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * vim: set ts=8 sts=4 et sw=4 tw=99: * 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 "ds/SplayTree.h" #include "jit/CompactBuffer.h" #include "jit/CompileInfo.h" #include "jit/OptimizationTracking.h" #include "jit/shared/CodeGenerator-shared.h" namespace js { namespace jit { /* * The Ion jitcode map implements tables to allow mapping from addresses in ion 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 splay-tree of JitcodeGlobalEntry describings the mapping for * each individual IonCode script 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 JitcodeIonTable; class JitcodeRegionEntry; class JitcodeGlobalEntry { public: enum Kind { INVALID = 0, Ion, Baseline, IonCache, Dummy, Query, LIMIT }; JS_STATIC_ASSERT(LIMIT <= 8); struct BytecodeLocation { JSScript* script; jsbytecode* pc; BytecodeLocation(JSScript* script, jsbytecode* pc) : script(script), pc(pc) {} }; typedef Vector BytecodeLocationVector; typedef Vector ProfileStringVector; struct BaseEntry { void* nativeStartAddr_; void* nativeEndAddr_; Kind kind_; void init() { nativeStartAddr_ = nullptr; nativeEndAddr_ = nullptr; kind_ = INVALID; } void init(Kind kind, void* nativeStartAddr, void* nativeEndAddr) { MOZ_ASSERT(nativeStartAddr); MOZ_ASSERT(nativeEndAddr); MOZ_ASSERT(kind > INVALID && kind < LIMIT); nativeStartAddr_ = nativeStartAddr; nativeEndAddr_ = nativeEndAddr; kind_ = kind; } Kind kind() const { return kind_; } void* nativeStartAddr() const { return nativeStartAddr_; } void* nativeEndAddr() const { return nativeEndAddr_; } bool startsBelowPointer(void* ptr) const { return ((uint8_t*)nativeStartAddr()) <= ((uint8_t*) ptr); } bool endsAbovePointer(void* ptr) const { return ((uint8_t*)nativeEndAddr()) > ((uint8_t*) ptr); } bool containsPointer(void* ptr) const { return startsBelowPointer(ptr) && endsAbovePointer(ptr); } }; struct IonEntry : public BaseEntry { // 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. JitcodeIonTable* regionTable_; // optsRegionTable_ points to the table within the compact // optimizations map indexing all regions that have tracked // optimization attempts. optsTypesTable_ is the tracked typed info // associated with the attempts vectors; it is the same length as the // attempts table. optsAttemptsTable_ is the table indexing those // attempts vectors. // // All pointers point into the same block of memory; the beginning of // the block is optRegionTable_->payloadStart(). const IonTrackedOptimizationsRegionTable* optsRegionTable_; const IonTrackedOptimizationsTypesTable* optsTypesTable_; const IonTrackedOptimizationsAttemptsTable* optsAttemptsTable_; // The types table above records type sets, which have been gathered // into one vector here. IonTrackedTypeVector* optsAllTypes_; struct ScriptNamePair { JSScript* script; char* str; }; struct SizedScriptList { uint32_t size; ScriptNamePair pairs[0]; SizedScriptList(uint32_t sz, JSScript** scrs, char** strs) : size(sz) { for (uint32_t i = 0; i < size; i++) { pairs[i].script = scrs[i]; pairs[i].str = strs[i]; } } static uint32_t AllocSizeFor(uint32_t nscripts) { return sizeof(SizedScriptList) + (nscripts * sizeof(ScriptNamePair)); } }; SizedScriptList* scriptList_; void init(void* nativeStartAddr, void* nativeEndAddr, SizedScriptList* scriptList, JitcodeIonTable* regionTable) { MOZ_ASSERT(scriptList); MOZ_ASSERT(regionTable); BaseEntry::init(Ion, nativeStartAddr, nativeEndAddr); regionTable_ = regionTable; scriptList_ = scriptList; optsRegionTable_ = nullptr; optsTypesTable_ = nullptr; optsAllTypes_ = nullptr; optsAttemptsTable_ = nullptr; } void initTrackedOptimizations(const IonTrackedOptimizationsRegionTable* regionTable, const IonTrackedOptimizationsTypesTable* typesTable, const IonTrackedOptimizationsAttemptsTable* attemptsTable, IonTrackedTypeVector* allTypes) { optsRegionTable_ = regionTable; optsTypesTable_ = typesTable; optsAttemptsTable_ = attemptsTable; optsAllTypes_ = allTypes; } SizedScriptList* sizedScriptList() const { return scriptList_; } unsigned numScripts() const { return scriptList_->size; } JSScript* getScript(unsigned idx) const { MOZ_ASSERT(idx < numScripts()); return sizedScriptList()->pairs[idx].script; } const char* getStr(unsigned idx) const { MOZ_ASSERT(idx < numScripts()); return sizedScriptList()->pairs[idx].str; } void destroy(); JitcodeIonTable* regionTable() const { return regionTable_; } int scriptIndex(JSScript* script) const { unsigned count = numScripts(); for (unsigned i = 0; i < count; i++) { if (getScript(i) == script) return i; } return -1; } 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; bool hasTrackedOptimizations() const { return !!optsRegionTable_; } IonTrackedOptimizationsAttempts trackedOptimizationAttempts(uint8_t index) { MOZ_ASSERT(hasTrackedOptimizations()); return optsAttemptsTable_->entry(index); } IonTrackedOptimizationsTypeInfo trackedOptimizationTypeInfo(uint8_t index) { MOZ_ASSERT(hasTrackedOptimizations()); return optsTypesTable_->entry(index); } const IonTrackedTypeVector* allTrackedTypes() { MOZ_ASSERT(hasTrackedOptimizations()); return optsAllTypes_; } mozilla::Maybe trackedOptimizationIndexAtAddr(void* ptr); }; struct BaselineEntry : public BaseEntry { JSScript* script_; const char* str_; // Last location that caused Ion to abort compilation and the reason // therein, if any. Only actionable aborts are tracked. Internal // errors like OOMs are not. jsbytecode* ionAbortPc_; const char* ionAbortMessage_; void init(void* nativeStartAddr, void* nativeEndAddr, JSScript* script, const char* str) { MOZ_ASSERT(script != nullptr); BaseEntry::init(Baseline, nativeStartAddr, nativeEndAddr); script_ = script; str_ = str; } JSScript* script() const { return script_; } const char* str() const { return str_; } void trackIonAbort(jsbytecode* pc, const char* message) { MOZ_ASSERT(script_->containsPC(pc)); MOZ_ASSERT(message); ionAbortPc_ = pc; ionAbortMessage_ = message; } bool hadIonAbort() const { MOZ_ASSERT(!ionAbortPc_ || ionAbortMessage_); return ionAbortPc_ != nullptr; } void destroy(); 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; }; struct IonCacheEntry : public BaseEntry { void* rejoinAddr_; void init(void* nativeStartAddr, void* nativeEndAddr, void* rejoinAddr) { MOZ_ASSERT(rejoinAddr != nullptr); BaseEntry::init(IonCache, nativeStartAddr, nativeEndAddr); rejoinAddr_ = rejoinAddr; } void* rejoinAddr() const { return rejoinAddr_; } void destroy() {} 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; }; // 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. struct DummyEntry : public BaseEntry { void init(void* nativeStartAddr, void* nativeEndAddr) { BaseEntry::init(Dummy, nativeStartAddr, nativeEndAddr); } void destroy() {} 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; } }; // QueryEntry is never stored in the table, just used for queries // where an instance of JitcodeGlobalEntry is required to do tree // lookups. struct QueryEntry : public BaseEntry { void init(void* addr) { BaseEntry::init(Query, addr, addr); } uint8_t* addr() const { return reinterpret_cast(nativeStartAddr()); } void destroy() {} }; private: union { // Shadowing BaseEntry instance to allow access to base fields // and type extraction. BaseEntry base_; // The most common entry type: describing jitcode generated by // Ion main-line code. IonEntry ion_; // Baseline jitcode. BaselineEntry baseline_; // IonCache stubs. IonCacheEntry ionCache_; // Dummy entries. DummyEntry dummy_; // When doing queries on the SplayTree for particular addresses, // the query addresses are representd using a QueryEntry. QueryEntry query_; }; public: JitcodeGlobalEntry() { base_.init(); } explicit JitcodeGlobalEntry(const IonEntry& ion) { ion_ = ion; } explicit JitcodeGlobalEntry(const BaselineEntry& baseline) { baseline_ = baseline; } explicit JitcodeGlobalEntry(const IonCacheEntry& ionCache) { ionCache_ = ionCache; } explicit JitcodeGlobalEntry(const DummyEntry& dummy) { dummy_ = dummy; } explicit JitcodeGlobalEntry(const QueryEntry& query) { query_ = query; } static JitcodeGlobalEntry MakeQuery(void* ptr) { QueryEntry query; query.init(ptr); return JitcodeGlobalEntry(query); } void destroy() { switch (kind()) { case Ion: ionEntry().destroy(); break; case Baseline: baselineEntry().destroy(); break; case IonCache: ionCacheEntry().destroy(); break; case Dummy: dummyEntry().destroy(); break; case Query: queryEntry().destroy(); break; default: MOZ_CRASH("Invalid JitcodeGlobalEntry kind."); } } void* nativeStartAddr() const { return base_.nativeStartAddr(); } void* nativeEndAddr() const { return base_.nativeEndAddr(); } bool startsBelowPointer(void* ptr) const { return base_.startsBelowPointer(ptr); } bool endsAbovePointer(void* ptr) const { return base_.endsAbovePointer(ptr); } bool containsPointer(void* ptr) const { return base_.containsPointer(ptr); } bool overlapsWith(const JitcodeGlobalEntry& entry) const { // Catch full containment of |entry| within |this|, and partial overlaps. if (containsPointer(entry.nativeStartAddr()) || containsPointer(entry.nativeEndAddr())) return true; // Catch full containment of |this| within |entry|. if (startsBelowPointer(entry.nativeEndAddr()) && endsAbovePointer(entry.nativeStartAddr())) return true; return false; } Kind kind() const { return base_.kind(); } bool isIon() const { return kind() == Ion; } bool isBaseline() const { return kind() == Baseline; } bool isIonCache() const { return kind() == IonCache; } bool isDummy() const { return kind() == Dummy; } bool isQuery() const { return kind() == Query; } IonEntry& ionEntry() { MOZ_ASSERT(isIon()); return ion_; } BaselineEntry& baselineEntry() { MOZ_ASSERT(isBaseline()); return baseline_; } IonCacheEntry& ionCacheEntry() { MOZ_ASSERT(isIonCache()); return ionCache_; } DummyEntry& dummyEntry() { MOZ_ASSERT(isDummy()); return dummy_; } QueryEntry& queryEntry() { MOZ_ASSERT(isQuery()); return query_; } const IonEntry& ionEntry() const { MOZ_ASSERT(isIon()); return ion_; } const BaselineEntry& baselineEntry() const { MOZ_ASSERT(isBaseline()); return baseline_; } const IonCacheEntry& ionCacheEntry() const { MOZ_ASSERT(isIonCache()); return ionCache_; } const DummyEntry& dummyEntry() const { MOZ_ASSERT(isDummy()); return dummy_; } const QueryEntry& queryEntry() const { MOZ_ASSERT(isQuery()); return query_; } // 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. bool callStackAtAddr(JSRuntime* rt, void* ptr, BytecodeLocationVector& results, uint32_t* depth) const { switch (kind()) { case Ion: return ionEntry().callStackAtAddr(rt, ptr, results, depth); case Baseline: return baselineEntry().callStackAtAddr(rt, ptr, results, depth); case IonCache: return ionCacheEntry().callStackAtAddr(rt, ptr, results, depth); case Dummy: return dummyEntry().callStackAtAddr(rt, ptr, results, depth); default: MOZ_CRASH("Invalid JitcodeGlobalEntry kind."); } return false; } uint32_t callStackAtAddr(JSRuntime* rt, void* ptr, const char** results, uint32_t maxResults) const { switch (kind()) { case Ion: return ionEntry().callStackAtAddr(rt, ptr, results, maxResults); case Baseline: return baselineEntry().callStackAtAddr(rt, ptr, results, maxResults); case IonCache: return ionCacheEntry().callStackAtAddr(rt, ptr, results, maxResults); case Dummy: return dummyEntry().callStackAtAddr(rt, ptr, results, maxResults); default: MOZ_CRASH("Invalid JitcodeGlobalEntry kind."); } return false; } // Figure out the number of the (JSScript*, jsbytecode*) pairs that are active // at this location. uint32_t lookupInlineCallDepth(void* ptr); // Compare two global entries. static int compare(const JitcodeGlobalEntry& ent1, const JitcodeGlobalEntry& ent2); // Compute a profiling string for a given script. static char* createScriptString(JSContext* cx, JSScript* script, size_t* length=nullptr); bool hasTrackedOptimizations() const { switch (kind()) { case Ion: return ionEntry().hasTrackedOptimizations(); case Baseline: case IonCache: case Dummy: break; default: MOZ_CRASH("Invalid JitcodeGlobalEntry kind."); } return false; } mozilla::Maybe trackedOptimizationIndexAtAddr(void* addr) { switch (kind()) { case Ion: return ionEntry().trackedOptimizationIndexAtAddr(addr); case Baseline: case IonCache: case Dummy: break; default: MOZ_CRASH("Invalid JitcodeGlobalEntry kind."); } return mozilla::Nothing(); } IonTrackedOptimizationsAttempts trackedOptimizationAttempts(uint8_t index) { return ionEntry().trackedOptimizationAttempts(index); } IonTrackedOptimizationsTypeInfo trackedOptimizationTypeInfo(uint8_t index) { return ionEntry().trackedOptimizationTypeInfo(index); } const IonTrackedTypeVector* allTrackedTypes() { return ionEntry().allTrackedTypes(); } }; /* * Global table of JitcodeGlobalEntry values sorted by native address range. */ class JitcodeGlobalTable { public: typedef SplayTree EntryTree; typedef Vector EntryVector; private: static const size_t LIFO_CHUNK_SIZE = 16 * 1024; LifoAlloc treeAlloc_; EntryTree tree_; EntryVector entries_; public: JitcodeGlobalTable() : treeAlloc_(LIFO_CHUNK_SIZE), tree_(&treeAlloc_), entries_() { // Always checking coherency in DEBUG builds may cause tests to time // out under --baseline-eager or --ion-eager. tree_.disableCheckCoherency(); } ~JitcodeGlobalTable() {} bool empty() const { return tree_.empty(); } bool lookup(void* ptr, JitcodeGlobalEntry* result, JSRuntime* rt); void lookupInfallible(void* ptr, JitcodeGlobalEntry* result, JSRuntime* rt); bool addEntry(const JitcodeGlobalEntry::IonEntry& entry, JSRuntime* rt) { return addEntry(JitcodeGlobalEntry(entry), rt); } bool addEntry(const JitcodeGlobalEntry::BaselineEntry& entry, JSRuntime* rt) { return addEntry(JitcodeGlobalEntry(entry), rt); } bool addEntry(const JitcodeGlobalEntry::IonCacheEntry& entry, JSRuntime* rt) { return addEntry(JitcodeGlobalEntry(entry), rt); } bool addEntry(const JitcodeGlobalEntry::DummyEntry& entry, JSRuntime* rt) { return addEntry(JitcodeGlobalEntry(entry), rt); } void removeEntry(void* startAddr, JSRuntime* rt); private: bool addEntry(const JitcodeGlobalEntry& entry, JSRuntime* rt); }; /* * 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. */ 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 CodeGeneratorShared::NativeToBytecode* entry, const CodeGeneratorShared::NativeToBytecode* end); // Write a run, starting at the given NativeToBytecode entry, into the given buffer writer. static bool WriteRun(CompactBufferWriter& writer, JSScript** scriptList, uint32_t scriptListSize, uint32_t runLength, const CodeGeneratorShared::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: uint32_t count_; const uint8_t* start_; const uint8_t* end_; uint32_t idx_; const uint8_t* cur_; public: ScriptPcIterator(uint32_t count, const uint8_t* start, const uint8_t* end) : count_(count), start_(start), end_(end), 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(scriptDepth_, scriptPcStack_, deltaRun_); } 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_[0]; const uint8_t* payloadEnd() const { return reinterpret_cast(this); } public: explicit JitcodeIonTable(uint32_t numRegions) : numRegions_(numRegions) { for (uint32_t i = 0; i < numRegions; i++) regionOffsets_[i] = 0; } bool makeIonEntry(JSContext* cx, JitCode* code, uint32_t numScripts, JSScript** scripts, JitcodeGlobalEntry::IonEntry& out); 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); } bool regionContainsOffset(uint32_t regionIndex, uint32_t nativeOffset) { MOZ_ASSERT(regionIndex < numRegions()); JitcodeRegionEntry ent = regionEntry(regionIndex); if (nativeOffset < ent.nativeOffset()) return false; if (regionIndex == numRegions_ - 1) return true; return nativeOffset < regionEntry(regionIndex + 1).nativeOffset(); } 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); } static bool WriteIonTable(CompactBufferWriter& writer, JSScript** scriptList, uint32_t scriptListSize, const CodeGeneratorShared::NativeToBytecode* start, const CodeGeneratorShared::NativeToBytecode* end, uint32_t* tableOffsetOut, uint32_t* numRegionsOut); }; } // namespace jit } // namespace js #endif /* jit_JitcodeMap_h */