| /* -*- 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 "jit/CompactBuffer.h" |
| #include "jit/CompileInfo.h" |
| #include "jit/ExecutableAllocator.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 JitcodeGlobalTable; |
| class JitcodeIonTable; |
| class JitcodeRegionEntry; |
| |
| class JitcodeGlobalEntry; |
| |
| class JitcodeSkiplistTower |
| { |
| public: |
| static const unsigned MAX_HEIGHT = 32; |
| |
| private: |
| uint8_t height_; |
| bool isFree_; |
| JitcodeGlobalEntry* ptrs_[1]; |
| |
| public: |
| explicit JitcodeSkiplistTower(unsigned height) |
| : height_(height), |
| isFree_(false) |
| { |
| MOZ_ASSERT(height >= 1 && height <= MAX_HEIGHT); |
| clearPtrs(); |
| } |
| |
| unsigned height() const { |
| return height_; |
| } |
| |
| JitcodeGlobalEntry** ptrs(unsigned level) { |
| return ptrs_; |
| } |
| |
| JitcodeGlobalEntry* next(unsigned level) const { |
| MOZ_ASSERT(!isFree_); |
| MOZ_ASSERT(level < height()); |
| return ptrs_[level]; |
| } |
| void setNext(unsigned level, JitcodeGlobalEntry* entry) { |
| MOZ_ASSERT(!isFree_); |
| MOZ_ASSERT(level < height()); |
| ptrs_[level] = entry; |
| } |
| |
| // |
| // When stored in a free-list, towers use 'ptrs_[0]' to store a |
| // pointer to the next tower. In this context only, 'ptrs_[0]' |
| // may refer to a |JitcodeSkiplistTower*| instead of a |
| // |JitcodeGlobalEntry*|. |
| // |
| |
| void addToFreeList(JitcodeSkiplistTower** freeList) { |
| JitcodeSkiplistTower* nextFreeTower = *freeList; |
| MOZ_ASSERT_IF(nextFreeTower, nextFreeTower->isFree_ && |
| nextFreeTower->height() == height_); |
| ptrs_[0] = (JitcodeGlobalEntry*) nextFreeTower; |
| isFree_ = true; |
| *freeList = this; |
| } |
| |
| static JitcodeSkiplistTower* PopFromFreeList(JitcodeSkiplistTower** freeList) { |
| if (!*freeList) |
| return nullptr; |
| |
| JitcodeSkiplistTower* tower = *freeList; |
| MOZ_ASSERT(tower->isFree_); |
| JitcodeSkiplistTower* nextFreeTower = (JitcodeSkiplistTower*) tower->ptrs_[0]; |
| tower->clearPtrs(); |
| tower->isFree_ = false; |
| *freeList = nextFreeTower; |
| return tower; |
| } |
| |
| static size_t CalculateSize(unsigned height) { |
| MOZ_ASSERT(height >= 1); |
| return sizeof(JitcodeSkiplistTower) + |
| (sizeof(JitcodeGlobalEntry*) * (height - 1)); |
| } |
| |
| private: |
| void clearPtrs() { |
| for (unsigned i = 0; i < height_; i++) |
| ptrs_[0] = nullptr; |
| } |
| }; |
| |
| class JitcodeGlobalEntry |
| { |
| friend class JitcodeGlobalTable; |
| |
| 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<BytecodeLocation, 0, SystemAllocPolicy> BytecodeLocationVector; |
| typedef Vector<const char*, 0, SystemAllocPolicy> ProfileStringVector; |
| |
| struct BaseEntry |
| { |
| JitCode* jitcode_; |
| void* nativeStartAddr_; |
| void* nativeEndAddr_; |
| uint32_t gen_; |
| Kind kind_ : 7; |
| |
| void init() { |
| jitcode_ = nullptr; |
| nativeStartAddr_ = nullptr; |
| nativeEndAddr_ = nullptr; |
| gen_ = UINT32_MAX; |
| kind_ = INVALID; |
| } |
| |
| void init(Kind kind, JitCode* code, |
| void* nativeStartAddr, void* nativeEndAddr) |
| { |
| MOZ_ASSERT_IF(kind != Query, code); |
| MOZ_ASSERT(nativeStartAddr); |
| MOZ_ASSERT(nativeEndAddr); |
| MOZ_ASSERT(kind > INVALID && kind < LIMIT); |
| jitcode_ = code; |
| nativeStartAddr_ = nativeStartAddr; |
| nativeEndAddr_ = nativeEndAddr; |
| gen_ = UINT32_MAX; |
| kind_ = kind; |
| } |
| |
| uint32_t generation() const { |
| return gen_; |
| } |
| void setGeneration(uint32_t gen) { |
| gen_ = gen; |
| } |
| bool isSampled(uint32_t currentGen, uint32_t lapCount) { |
| if (gen_ == UINT32_MAX || currentGen == UINT32_MAX) |
| return false; |
| MOZ_ASSERT(currentGen >= gen_); |
| return (currentGen - gen_) <= lapCount; |
| } |
| |
| Kind kind() const { |
| return kind_; |
| } |
| JitCode* jitcode() const { |
| return jitcode_; |
| } |
| 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); |
| } |
| |
| template <class ShouldMarkProvider> bool markJitcode(JSTracer* trc); |
| bool isJitcodeMarkedFromAnyThread(); |
| bool isJitcodeAboutToBeFinalized(); |
| }; |
| |
| 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[1]; |
| 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 - 1) * sizeof(ScriptNamePair)); |
| } |
| }; |
| |
| SizedScriptList* scriptList_; |
| |
| void init(JitCode* code, void* nativeStartAddr, void* nativeEndAddr, |
| SizedScriptList* scriptList, JitcodeIonTable* regionTable) |
| { |
| MOZ_ASSERT(scriptList); |
| MOZ_ASSERT(regionTable); |
| BaseEntry::init(Ion, code, 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; |
| } |
| |
| void* canonicalNativeAddrFor(JSRuntime*rt, void* ptr) const; |
| |
| 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; |
| |
| void youngestFrameLocationAtAddr(JSRuntime* rt, void* ptr, |
| JSScript** script, jsbytecode** pc) const; |
| |
| bool hasTrackedOptimizations() const { |
| return !!optsRegionTable_; |
| } |
| |
| const IonTrackedOptimizationsRegionTable* trackedOptimizationsRegionTable() const { |
| MOZ_ASSERT(hasTrackedOptimizations()); |
| return optsRegionTable_; |
| } |
| |
| uint8_t numOptimizationAttempts() const { |
| MOZ_ASSERT(hasTrackedOptimizations()); |
| return optsAttemptsTable_->numEntries(); |
| } |
| |
| 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<uint8_t> trackedOptimizationIndexAtAddr( |
| JSRuntime *rt, |
| void* ptr, |
| uint32_t* entryOffsetOut); |
| |
| void forEachOptimizationAttempt(JSRuntime* rt, uint8_t index, |
| JS::ForEachTrackedOptimizationAttemptOp& op); |
| void forEachOptimizationTypeInfo(JSRuntime* rt, uint8_t index, |
| IonTrackedOptimizationsTypeInfo::ForEachOpAdapter& op); |
| |
| template <class ShouldMarkProvider> bool mark(JSTracer* trc); |
| void sweepChildren(); |
| bool isMarkedFromAnyThread(); |
| }; |
| |
| 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(JitCode* code, void* nativeStartAddr, void* nativeEndAddr, |
| JSScript* script, const char* str) |
| { |
| MOZ_ASSERT(script != nullptr); |
| BaseEntry::init(Baseline, code, 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(); |
| |
| void* canonicalNativeAddrFor(JSRuntime* rt, void* ptr) const; |
| |
| 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; |
| |
| void youngestFrameLocationAtAddr(JSRuntime* rt, void* ptr, |
| JSScript** script, jsbytecode** pc) const; |
| |
| template <class ShouldMarkProvider> bool mark(JSTracer* trc); |
| void sweepChildren(); |
| bool isMarkedFromAnyThread(); |
| }; |
| |
| struct IonCacheEntry : public BaseEntry |
| { |
| void* rejoinAddr_; |
| JS::TrackedOutcome trackedOutcome_; |
| |
| void init(JitCode* code, void* nativeStartAddr, void* nativeEndAddr, |
| void* rejoinAddr, JS::TrackedOutcome trackedOutcome) |
| { |
| MOZ_ASSERT(rejoinAddr != nullptr); |
| BaseEntry::init(IonCache, code, nativeStartAddr, nativeEndAddr); |
| rejoinAddr_ = rejoinAddr; |
| trackedOutcome_ = trackedOutcome; |
| } |
| |
| void* rejoinAddr() const { |
| return rejoinAddr_; |
| } |
| JS::TrackedOutcome trackedOutcome() const { |
| return trackedOutcome_; |
| } |
| |
| void destroy() {} |
| |
| void* canonicalNativeAddrFor(JSRuntime* rt, void* ptr) const; |
| |
| 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; |
| |
| void youngestFrameLocationAtAddr(JSRuntime* rt, void* ptr, |
| JSScript** script, jsbytecode** pc) const; |
| |
| bool hasTrackedOptimizations() const { return true; } |
| mozilla::Maybe<uint8_t> trackedOptimizationIndexAtAddr( |
| JSRuntime *rt, |
| void* ptr, |
| uint32_t* entryOffsetOut); |
| void forEachOptimizationAttempt(JSRuntime* rt, uint8_t index, |
| JS::ForEachTrackedOptimizationAttemptOp& op); |
| void forEachOptimizationTypeInfo(JSRuntime* rt, uint8_t index, |
| IonTrackedOptimizationsTypeInfo::ForEachOpAdapter& op); |
| |
| template <class ShouldMarkProvider> bool mark(JSTracer* trc); |
| void sweepChildren(JSRuntime* rt); |
| bool isMarkedFromAnyThread(JSRuntime* rt); |
| }; |
| |
| // 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(JitCode* code, void* nativeStartAddr, void* nativeEndAddr) { |
| BaseEntry::init(Dummy, code, nativeStartAddr, nativeEndAddr); |
| } |
| |
| void destroy() {} |
| |
| void* canonicalNativeAddrFor(JSRuntime* rt, void* ptr) const { |
| return nullptr; |
| } |
| |
| 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; |
| } |
| |
| void youngestFrameLocationAtAddr(JSRuntime* rt, void* ptr, |
| JSScript** script, jsbytecode** pc) const |
| { |
| *script = nullptr; |
| *pc = nullptr; |
| } |
| }; |
| |
| // 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, nullptr, addr, addr); |
| } |
| uint8_t* addr() const { |
| return reinterpret_cast<uint8_t*>(nativeStartAddr()); |
| } |
| void destroy() {} |
| }; |
| |
| private: |
| JitcodeSkiplistTower* tower_; |
| |
| 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() |
| : tower_(nullptr) |
| { |
| base_.init(); |
| } |
| |
| explicit JitcodeGlobalEntry(const IonEntry& ion) |
| : tower_(nullptr) |
| { |
| ion_ = ion; |
| } |
| |
| explicit JitcodeGlobalEntry(const BaselineEntry& baseline) |
| : tower_(nullptr) |
| { |
| baseline_ = baseline; |
| } |
| |
| explicit JitcodeGlobalEntry(const IonCacheEntry& ionCache) |
| : tower_(nullptr) |
| { |
| ionCache_ = ionCache; |
| } |
| |
| explicit JitcodeGlobalEntry(const DummyEntry& dummy) |
| : tower_(nullptr) |
| { |
| dummy_ = dummy; |
| } |
| |
| explicit JitcodeGlobalEntry(const QueryEntry& query) |
| : tower_(nullptr) |
| { |
| 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."); |
| } |
| } |
| |
| JitCode* jitcode() const { |
| return baseEntry().jitcode(); |
| } |
| void* nativeStartAddr() const { |
| return base_.nativeStartAddr(); |
| } |
| void* nativeEndAddr() const { |
| return base_.nativeEndAddr(); |
| } |
| |
| uint32_t generation() const { |
| return baseEntry().generation(); |
| } |
| void setGeneration(uint32_t gen) { |
| baseEntry().setGeneration(gen); |
| } |
| void setAsExpired() { |
| baseEntry().setGeneration(UINT32_MAX); |
| } |
| bool isSampled(uint32_t currentGen, uint32_t lapCount) { |
| return baseEntry().isSampled(currentGen, lapCount); |
| } |
| |
| 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 isValid() const { |
| return (kind() > INVALID) && (kind() < LIMIT); |
| } |
| 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; |
| } |
| |
| BaseEntry& baseEntry() { |
| MOZ_ASSERT(isValid()); |
| return base_; |
| } |
| 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 BaseEntry& baseEntry() const { |
| MOZ_ASSERT(isValid()); |
| return base_; |
| } |
| 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_; |
| } |
| |
| void* canonicalNativeAddrFor(JSRuntime* rt, void* ptr) const { |
| switch (kind()) { |
| case Ion: |
| return ionEntry().canonicalNativeAddrFor(rt, ptr); |
| case Baseline: |
| return baselineEntry().canonicalNativeAddrFor(rt, ptr); |
| case IonCache: |
| return ionCacheEntry().canonicalNativeAddrFor(rt, ptr); |
| case Dummy: |
| return dummyEntry().canonicalNativeAddrFor(rt, ptr); |
| default: |
| MOZ_CRASH("Invalid JitcodeGlobalEntry kind."); |
| } |
| return nullptr; |
| } |
| |
| // 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; |
| } |
| |
| void youngestFrameLocationAtAddr(JSRuntime* rt, void* ptr, |
| JSScript** script, jsbytecode** pc) const |
| { |
| switch (kind()) { |
| case Ion: |
| return ionEntry().youngestFrameLocationAtAddr(rt, ptr, script, pc); |
| case Baseline: |
| return baselineEntry().youngestFrameLocationAtAddr(rt, ptr, script, pc); |
| case IonCache: |
| return ionCacheEntry().youngestFrameLocationAtAddr(rt, ptr, script, pc); |
| case Dummy: |
| return dummyEntry().youngestFrameLocationAtAddr(rt, ptr, script, pc); |
| default: |
| MOZ_CRASH("Invalid JitcodeGlobalEntry kind."); |
| } |
| } |
| |
| // 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); |
| int compareTo(const JitcodeGlobalEntry& other) { |
| return compare(*this, other); |
| } |
| |
| // 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 IonCache: |
| return ionCacheEntry().hasTrackedOptimizations(); |
| case Baseline: |
| case Dummy: |
| break; |
| default: |
| MOZ_CRASH("Invalid JitcodeGlobalEntry kind."); |
| } |
| return false; |
| } |
| |
| mozilla::Maybe<uint8_t> trackedOptimizationIndexAtAddr( |
| JSRuntime *rt, |
| void* addr, |
| uint32_t* entryOffsetOut) |
| { |
| switch (kind()) { |
| case Ion: |
| return ionEntry().trackedOptimizationIndexAtAddr(rt, addr, entryOffsetOut); |
| case IonCache: |
| return ionCacheEntry().trackedOptimizationIndexAtAddr(rt, addr, entryOffsetOut); |
| case Baseline: |
| case Dummy: |
| break; |
| default: |
| MOZ_CRASH("Invalid JitcodeGlobalEntry kind."); |
| } |
| return mozilla::Nothing(); |
| } |
| |
| void forEachOptimizationAttempt(JSRuntime* rt, uint8_t index, |
| JS::ForEachTrackedOptimizationAttemptOp& op) |
| { |
| switch (kind()) { |
| case Ion: |
| ionEntry().forEachOptimizationAttempt(rt, index, op); |
| return; |
| case IonCache: |
| ionCacheEntry().forEachOptimizationAttempt(rt, index, op); |
| return; |
| case Baseline: |
| case Dummy: |
| break; |
| default: |
| MOZ_CRASH("Invalid JitcodeGlobalEntry kind."); |
| } |
| } |
| |
| void forEachOptimizationTypeInfo(JSRuntime* rt, uint8_t index, |
| IonTrackedOptimizationsTypeInfo::ForEachOpAdapter& op) |
| { |
| switch (kind()) { |
| case Ion: |
| ionEntry().forEachOptimizationTypeInfo(rt, index, op); |
| return; |
| case IonCache: |
| ionCacheEntry().forEachOptimizationTypeInfo(rt, index, op); |
| return; |
| case Baseline: |
| case Dummy: |
| break; |
| default: |
| MOZ_CRASH("Invalid JitcodeGlobalEntry kind."); |
| } |
| } |
| |
| IonTrackedOptimizationsAttempts trackedOptimizationAttempts(uint8_t index) { |
| return ionEntry().trackedOptimizationAttempts(index); |
| } |
| |
| IonTrackedOptimizationsTypeInfo trackedOptimizationTypeInfo(uint8_t index) { |
| return ionEntry().trackedOptimizationTypeInfo(index); |
| } |
| |
| const IonTrackedTypeVector* allTrackedTypes() { |
| return ionEntry().allTrackedTypes(); |
| } |
| |
| Zone* zone() { |
| return baseEntry().jitcode()->zone(); |
| } |
| |
| template <class ShouldMarkProvider> |
| bool mark(JSTracer* trc) { |
| bool markedAny = baseEntry().markJitcode<ShouldMarkProvider>(trc); |
| switch (kind()) { |
| case Ion: |
| markedAny |= ionEntry().mark<ShouldMarkProvider>(trc); |
| break; |
| case Baseline: |
| markedAny |= baselineEntry().mark<ShouldMarkProvider>(trc); |
| break; |
| case IonCache: |
| markedAny |= ionCacheEntry().mark<ShouldMarkProvider>(trc); |
| case Dummy: |
| break; |
| default: |
| MOZ_CRASH("Invalid JitcodeGlobalEntry kind."); |
| } |
| return markedAny; |
| } |
| |
| void sweepChildren(JSRuntime* rt) { |
| switch (kind()) { |
| case Ion: |
| ionEntry().sweepChildren(); |
| break; |
| case Baseline: |
| baselineEntry().sweepChildren(); |
| break; |
| case IonCache: |
| ionCacheEntry().sweepChildren(rt); |
| case Dummy: |
| break; |
| default: |
| MOZ_CRASH("Invalid JitcodeGlobalEntry kind."); |
| } |
| } |
| |
| bool isMarkedFromAnyThread(JSRuntime* rt) { |
| if (!baseEntry().isJitcodeMarkedFromAnyThread()) |
| return false; |
| switch (kind()) { |
| case Ion: |
| return ionEntry().isMarkedFromAnyThread(); |
| case Baseline: |
| return baselineEntry().isMarkedFromAnyThread(); |
| case IonCache: |
| return ionCacheEntry().isMarkedFromAnyThread(rt); |
| case Dummy: |
| break; |
| default: |
| MOZ_CRASH("Invalid JitcodeGlobalEntry kind."); |
| } |
| return true; |
| } |
| |
| // |
| // When stored in a free-list, entries use 'tower_' to store a |
| // pointer to the next entry. In this context only, 'tower_' |
| // may refer to a |JitcodeGlobalEntry*| instead of a |
| // |JitcodeSkiplistTower*|. |
| // |
| |
| void addToFreeList(JitcodeGlobalEntry** freeList) { |
| MOZ_ASSERT(!isValid()); |
| |
| JitcodeGlobalEntry* nextFreeEntry = *freeList; |
| MOZ_ASSERT_IF(nextFreeEntry, !nextFreeEntry->isValid()); |
| |
| tower_ = (JitcodeSkiplistTower*) nextFreeEntry; |
| *freeList = this; |
| } |
| |
| static JitcodeGlobalEntry* PopFromFreeList(JitcodeGlobalEntry** freeList) { |
| if (!*freeList) |
| return nullptr; |
| |
| JitcodeGlobalEntry* entry = *freeList; |
| MOZ_ASSERT(!entry->isValid()); |
| JitcodeGlobalEntry* nextFreeEntry = (JitcodeGlobalEntry*) entry->tower_; |
| entry->tower_ = nullptr; |
| *freeList = nextFreeEntry; |
| return entry; |
| } |
| }; |
| |
| /* |
| * Global table of JitcodeGlobalEntry values sorted by native address range. |
| */ |
| class JitcodeGlobalTable |
| { |
| private: |
| static const size_t LIFO_CHUNK_SIZE = 16 * 1024; |
| |
| LifoAlloc alloc_; |
| JitcodeGlobalEntry* freeEntries_; |
| uint32_t rand_; |
| uint32_t skiplistSize_; |
| |
| JitcodeGlobalEntry* startTower_[JitcodeSkiplistTower::MAX_HEIGHT]; |
| JitcodeSkiplistTower* freeTowers_[JitcodeSkiplistTower::MAX_HEIGHT]; |
| |
| public: |
| JitcodeGlobalTable() |
| : alloc_(LIFO_CHUNK_SIZE), freeEntries_(nullptr), rand_(0), skiplistSize_(0) |
| { |
| for (unsigned i = 0; i < JitcodeSkiplistTower::MAX_HEIGHT; i++) |
| startTower_[i] = nullptr; |
| for (unsigned i = 0; i < JitcodeSkiplistTower::MAX_HEIGHT; i++) |
| freeTowers_[i] = nullptr; |
| } |
| ~JitcodeGlobalTable() {} |
| |
| bool empty() const { |
| return skiplistSize_ == 0; |
| } |
| |
| bool lookup(void* ptr, JitcodeGlobalEntry* result, JSRuntime* rt); |
| bool lookupForSampler(void* ptr, JitcodeGlobalEntry* result, JSRuntime* rt, |
| uint32_t sampleBufferGen); |
| |
| void lookupInfallible(void* ptr, JitcodeGlobalEntry* result, JSRuntime* rt) { |
| mozilla::DebugOnly<bool> success = lookup(ptr, result, rt); |
| MOZ_ASSERT(success); |
| } |
| |
| 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(JitcodeGlobalEntry& entry, JitcodeGlobalEntry** prevTower, JSRuntime* rt); |
| void releaseEntry(JitcodeGlobalEntry& entry, JitcodeGlobalEntry** prevTower, JSRuntime* rt); |
| |
| void setAllEntriesAsExpired(JSRuntime* rt); |
| void markUnconditionally(JSTracer* trc); |
| bool markIteratively(JSTracer* trc); |
| void sweep(JSRuntime* rt); |
| |
| private: |
| bool addEntry(const JitcodeGlobalEntry& entry, JSRuntime* rt); |
| |
| JitcodeGlobalEntry* lookupInternal(void* ptr); |
| |
| // Initialize towerOut such that towerOut[i] (for i in [0, MAX_HEIGHT-1]) |
| // is a JitcodeGlobalEntry that is sorted to be <query, whose successor at |
| // level i is either null, or sorted to be >= query. |
| // |
| // If entry with the given properties does not exist for level i, then |
| // towerOut[i] is initialized to nullptr. |
| void searchInternal(const JitcodeGlobalEntry& query, JitcodeGlobalEntry** towerOut); |
| |
| JitcodeGlobalEntry* searchAtHeight(unsigned level, JitcodeGlobalEntry* start, |
| const JitcodeGlobalEntry& query); |
| |
| // Calculate next random tower height. |
| unsigned generateTowerHeight(); |
| |
| JitcodeSkiplistTower* allocateTower(unsigned height); |
| JitcodeGlobalEntry* allocateEntry(); |
| |
| #ifdef DEBUG |
| void verifySkiplist(); |
| #else |
| void verifySkiplist() {} |
| #endif |
| |
| public: |
| class Range |
| { |
| protected: |
| JitcodeGlobalTable& table_; |
| JitcodeGlobalEntry* cur_; |
| |
| public: |
| explicit Range(JitcodeGlobalTable& table) |
| : table_(table), |
| cur_(table.startTower_[0]) |
| { } |
| |
| JitcodeGlobalEntry* front() const { |
| MOZ_ASSERT(!empty()); |
| return cur_; |
| } |
| |
| bool empty() const { |
| return !cur_; |
| } |
| |
| void popFront() { |
| MOZ_ASSERT(!empty()); |
| cur_ = cur_->tower_->next(0); |
| } |
| }; |
| |
| // An enumerator class that can remove entries as it enumerates. If this |
| // functionality is not needed, use Range instead. |
| class Enum : public Range |
| { |
| JSRuntime* rt_; |
| JitcodeGlobalEntry* next_; |
| JitcodeGlobalEntry* prevTower_[JitcodeSkiplistTower::MAX_HEIGHT]; |
| |
| public: |
| Enum(JitcodeGlobalTable& table, JSRuntime* rt); |
| |
| void popFront(); |
| void removeFront(); |
| }; |
| }; |
| |
| |
| /* |
| * 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. |
| */ |
| 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_[1]; |
| |
| const uint8_t* payloadEnd() const { |
| return reinterpret_cast<const uint8_t*>(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 */ |