mirror of
https://github.com/classilla/tenfourfox.git
synced 2024-09-28 05:55:58 +00:00
1488 lines
48 KiB
C
1488 lines
48 KiB
C
|
/* -*- 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(JSRuntime* rt);
|
||
|
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(JSRuntime* rt);
|
||
|
};
|
||
|
|
||
|
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(JSRuntime* rt);
|
||
|
};
|
||
|
|
||
|
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(rt))
|
||
|
return false;
|
||
|
switch (kind()) {
|
||
|
case Ion:
|
||
|
return ionEntry().isMarkedFromAnyThread(rt);
|
||
|
case Baseline:
|
||
|
return baselineEntry().isMarkedFromAnyThread(rt);
|
||
|
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 */
|