#include "jit/JitcodeMap.h"
#include "mozilla/ArrayUtils.h"
#include "mozilla/MathAlgorithms.h"
#include "mozilla/Maybe.h"
#include "mozilla/ScopeExit.h"
#include "mozilla/Sprintf.h"
#include "gc/Marking.h"
#include "gc/Statistics.h"
#include "jit/BaselineJIT.h"
#include "jit/JitRealm.h"
#include "jit/JitSpewer.h"
#include "js/Vector.h"
#include "vm/GeckoProfiler.h"
#include "vm/GeckoProfiler-inl.h"
#include "vm/JSScript-inl.h"
#include "vm/TypeInference-inl.h"
using mozilla::Maybe;
namespace js {
namespace jit {
static inline JitcodeRegionEntry RegionAtAddr(
const JitcodeGlobalEntry::IonEntry& entry, void* ptr, uint32_t* ptrOffset) {
MOZ_ASSERT(entry.containsPointer(ptr));
*ptrOffset = reinterpret_cast<uint8_t*>(ptr) -
reinterpret_cast<uint8_t*>(entry.nativeStartAddr());
uint32_t regionIdx = entry.regionTable()->findRegionEntry(*ptrOffset);
MOZ_ASSERT(regionIdx < entry.regionTable()->numRegions());
return entry.regionTable()->regionEntry(regionIdx);
}
void* JitcodeGlobalEntry::IonEntry::canonicalNativeAddrFor(void* ptr) const {
uint32_t ptrOffset;
JitcodeRegionEntry region = RegionAtAddr(*this, ptr, &ptrOffset);
return (void*)(((uint8_t*)nativeStartAddr()) + region.nativeOffset());
}
bool JitcodeGlobalEntry::IonEntry::callStackAtAddr(
void* ptr, BytecodeLocationVector& results, uint32_t* depth) const {
uint32_t ptrOffset;
JitcodeRegionEntry region = RegionAtAddr(*this, ptr, &ptrOffset);
*depth = region.scriptDepth();
JitcodeRegionEntry::ScriptPcIterator locationIter = region.scriptPcIterator();
MOZ_ASSERT(locationIter.hasMore());
bool first = true;
while (locationIter.hasMore()) {
uint32_t scriptIdx, pcOffset;
locationIter.readNext(&scriptIdx, &pcOffset);
if (first) {
pcOffset = region.findPcOffset(ptrOffset, pcOffset);
first = false;
}
JSScript* script = getScript(scriptIdx);
jsbytecode* pc = script->offsetToPC(pcOffset);
if (!results.append(BytecodeLocation(script, pc))) {
return false;
}
}
return true;
}
uint32_t JitcodeGlobalEntry::IonEntry::callStackAtAddr(
void* ptr, const char** results, uint32_t maxResults) const {
MOZ_ASSERT(maxResults >= 1);
uint32_t ptrOffset;
JitcodeRegionEntry region = RegionAtAddr(*this, ptr, &ptrOffset);
JitcodeRegionEntry::ScriptPcIterator locationIter = region.scriptPcIterator();
MOZ_ASSERT(locationIter.hasMore());
uint32_t count = 0;
while (locationIter.hasMore()) {
uint32_t scriptIdx, pcOffset;
locationIter.readNext(&scriptIdx, &pcOffset);
MOZ_ASSERT(getStr(scriptIdx));
results[count++] = getStr(scriptIdx);
if (count >= maxResults) {
break;
}
}
return count;
}
void JitcodeGlobalEntry::IonEntry::youngestFrameLocationAtAddr(
void* ptr, JSScript** script, jsbytecode** pc) const {
uint32_t ptrOffset;
JitcodeRegionEntry region = RegionAtAddr(*this, ptr, &ptrOffset);
JitcodeRegionEntry::ScriptPcIterator locationIter = region.scriptPcIterator();
MOZ_ASSERT(locationIter.hasMore());
uint32_t scriptIdx, pcOffset;
locationIter.readNext(&scriptIdx, &pcOffset);
pcOffset = region.findPcOffset(ptrOffset, pcOffset);
*script = getScript(scriptIdx);
*pc = (*script)->offsetToPC(pcOffset);
}
void JitcodeGlobalEntry::IonEntry::destroy() {
if (regionTable_) {
js_free((void*)(regionTable_->payloadStart()));
}
regionTable_ = nullptr;
for (uint32_t i = 0; i < scriptList_->size; i++) {
js_free(scriptList_->pairs[i].str);
scriptList_->pairs[i].str = nullptr;
}
js_free(scriptList_);
scriptList_ = nullptr;
if (optsRegionTable_) {
MOZ_ASSERT(optsAttemptsTable_);
js_free((void*)optsRegionTable_->payloadStart());
}
optsRegionTable_ = nullptr;
optsTypesTable_ = nullptr;
optsAttemptsTable_ = nullptr;
js_delete(optsAllTypes_);
optsAllTypes_ = nullptr;
}
void* JitcodeGlobalEntry::BaselineEntry::canonicalNativeAddrFor(
void* ptr) const {
return ptr;
}
bool JitcodeGlobalEntry::BaselineEntry::callStackAtAddr(
void* ptr, BytecodeLocationVector& results, uint32_t* depth) const {
MOZ_ASSERT(containsPointer(ptr));
MOZ_ASSERT(script_->hasBaselineScript());
uint8_t* addr = reinterpret_cast<uint8_t*>(ptr);
jsbytecode* pc =
script_->baselineScript()->approximatePcForNativeAddress(script_, addr);
if (!results.append(BytecodeLocation(script_, pc))) {
return false;
}
*depth = 1;
return true;
}
uint32_t JitcodeGlobalEntry::BaselineEntry::callStackAtAddr(
void* ptr, const char** results, uint32_t maxResults) const {
MOZ_ASSERT(containsPointer(ptr));
MOZ_ASSERT(maxResults >= 1);
results[0] = str();
return 1;
}
void JitcodeGlobalEntry::BaselineEntry::youngestFrameLocationAtAddr(
void* ptr, JSScript** script, jsbytecode** pc) const {
uint8_t* addr = reinterpret_cast<uint8_t*>(ptr);
*script = script_;
*pc = script_->baselineScript()->approximatePcForNativeAddress(script_, addr);
}
void JitcodeGlobalEntry::BaselineEntry::destroy() {
if (!str_) {
return;
}
js_free((void*)str_);
str_ = nullptr;
}
static inline JitcodeGlobalEntry& RejoinEntry(
JSRuntime* rt, const JitcodeGlobalEntry::IonCacheEntry& cache, void* ptr) {
MOZ_ASSERT(cache.containsPointer(ptr));
JitRuntime* jitrt = rt->jitRuntime();
JitcodeGlobalEntry& entry =
jitrt->getJitcodeGlobalTable()->lookupInfallible(cache.rejoinAddr());
MOZ_ASSERT(entry.isIon());
return entry;
}
void* JitcodeGlobalEntry::IonCacheEntry::canonicalNativeAddrFor() const {
return nativeStartAddr_;
}
bool JitcodeGlobalEntry::IonCacheEntry::callStackAtAddr(
JSRuntime* rt, void* ptr, BytecodeLocationVector& results,
uint32_t* depth) const {
const JitcodeGlobalEntry& entry = RejoinEntry(rt, *this, ptr);
return entry.callStackAtAddr(rt, rejoinAddr(), results, depth);
}
uint32_t JitcodeGlobalEntry::IonCacheEntry::callStackAtAddr(
JSRuntime* rt, void* ptr, const char** results, uint32_t maxResults) const {
const JitcodeGlobalEntry& entry = RejoinEntry(rt, *this, ptr);
return entry.callStackAtAddr(rt, rejoinAddr(), results, maxResults);
}
void JitcodeGlobalEntry::IonCacheEntry::youngestFrameLocationAtAddr(
JSRuntime* rt, void* ptr, JSScript** script, jsbytecode** pc) const {
const JitcodeGlobalEntry& entry = RejoinEntry(rt, *this, ptr);
return entry.youngestFrameLocationAtAddr(rt, rejoinAddr(), script, pc);
}
static int ComparePointers(const void* a, const void* b) {
const uint8_t* a_ptr = reinterpret_cast<const uint8_t*>(a);
const uint8_t* b_ptr = reinterpret_cast<const uint8_t*>(b);
if (a_ptr < b_ptr) {
return -1;
}
if (a_ptr > b_ptr) {
return 1;
}
return 0;
}
int JitcodeGlobalEntry::compare(const JitcodeGlobalEntry& ent1,
const JitcodeGlobalEntry& ent2) {
MOZ_ASSERT(!(ent1.isQuery() && ent2.isQuery()));
MOZ_ASSERT_IF(!ent1.isQuery() && !ent2.isQuery(), !ent1.overlapsWith(ent2));
if (!ent1.isQuery() && !ent2.isQuery()) {
return ComparePointers(ent1.nativeStartAddr(), ent2.nativeStartAddr());
}
void* ptr = ent1.isQuery() ? ent1.nativeStartAddr() : ent2.nativeStartAddr();
const JitcodeGlobalEntry& ent = ent1.isQuery() ? ent2 : ent1;
int flip = ent1.isQuery() ? 1 : -1;
if (ent.startsBelowPointer(ptr)) {
if (ent.endsAbovePointer(ptr)) {
return 0;
}
return flip * 1;
}
return flip * -1;
}
char* JitcodeGlobalEntry::createScriptString(JSContext* cx, JSScript* script,
size_t* length) {
bool hasName = false;
size_t nameLength = 0;
UniqueChars nameStr;
JSFunction* func = script->functionDelazifying();
if (func && func->displayAtom()) {
nameStr = StringToNewUTF8CharsZ(cx, *func->displayAtom());
if (!nameStr) {
return nullptr;
}
nameLength = strlen(nameStr.get());
hasName = true;
}
const char* filenameStr = script->filename() ? script->filename() : "(null)";
size_t filenameLength = strlen(filenameStr);
bool hasLineAndColumn = false;
size_t lineAndColumnLength = 0;
char lineAndColumnStr[30];
if (hasName || (script->functionNonDelazifying() || script->isForEval())) {
lineAndColumnLength = SprintfLiteral(lineAndColumnStr, "%u:%u",
script->lineno(), script->column());
hasLineAndColumn = true;
}
size_t fullLength = 0;
if (hasName) {
MOZ_ASSERT(hasLineAndColumn);
fullLength = nameLength + 2 + filenameLength + 1 + lineAndColumnLength + 1;
} else if (hasLineAndColumn) {
fullLength = filenameLength + 1 + lineAndColumnLength;
} else {
fullLength = filenameLength;
}
char* str = cx->pod_malloc<char>(fullLength + 1);
if (!str) {
return nullptr;
}
size_t cur = 0;
if (hasName) {
memcpy(str + cur, nameStr.get(), nameLength);
cur += nameLength;
str[cur++] = ' ';
str[cur++] = '(';
}
memcpy(str + cur, filenameStr, filenameLength);
cur += filenameLength;
if (hasLineAndColumn) {
str[cur++] = ':';
memcpy(str + cur, lineAndColumnStr, lineAndColumnLength);
cur += lineAndColumnLength;
}
if (hasName) {
str[cur++] = ')';
}
MOZ_ASSERT(cur == fullLength);
str[cur] = 0;
if (length) {
*length = fullLength;
}
return str;
}
JitcodeGlobalTable::Enum::Enum(JitcodeGlobalTable& table, JSRuntime* rt)
: Range(table), rt_(rt), next_(cur_ ? cur_->tower_->next(0) : nullptr) {
for (int level = JitcodeSkiplistTower::MAX_HEIGHT - 1; level >= 0; level--) {
prevTower_[level] = nullptr;
}
}
void JitcodeGlobalTable::Enum::popFront() {
MOZ_ASSERT(!empty());
if (cur_ != table_.freeEntries_) {
for (int level = cur_->tower_->height() - 1; level >= 0; level--) {
JitcodeGlobalEntry* prevTowerEntry = prevTower_[level];
if (prevTowerEntry) {
if (prevTowerEntry->tower_->next(level) == cur_) {
prevTower_[level] = cur_;
}
} else {
prevTower_[level] = table_.startTower_[level];
}
}
}
cur_ = next_;
if (!empty()) {
next_ = cur_->tower_->next(0);
}
}
void JitcodeGlobalTable::Enum::removeFront() {
MOZ_ASSERT(!empty());
table_.releaseEntry(*cur_, prevTower_, rt_);
}
const JitcodeGlobalEntry& JitcodeGlobalTable::lookupForSamplerInfallible(
void* ptr, JSRuntime* rt, uint64_t samplePosInBuffer) {
JitcodeGlobalEntry* entry = lookupInternal(ptr);
MOZ_ASSERT(entry);
entry->setSamplePositionInBuffer(samplePosInBuffer);
if (entry->isIonCache()) {
JitcodeGlobalEntry& rejoinEntry =
RejoinEntry(rt, entry->ionCacheEntry(), ptr);
rejoinEntry.setSamplePositionInBuffer(samplePosInBuffer);
}
return *entry;
}
JitcodeGlobalEntry* JitcodeGlobalTable::lookupInternal(void* ptr) {
JitcodeGlobalEntry query = JitcodeGlobalEntry::MakeQuery(ptr);
JitcodeGlobalEntry* searchTower[JitcodeSkiplistTower::MAX_HEIGHT];
searchInternal(query, searchTower);
if (searchTower[0] == nullptr) {
if (startTower_[0] == nullptr) {
return nullptr;
}
MOZ_ASSERT(startTower_[0]->compareTo(query) >= 0);
int cmp = startTower_[0]->compareTo(query);
MOZ_ASSERT(cmp >= 0);
return (cmp == 0) ? startTower_[0] : nullptr;
}
JitcodeGlobalEntry* bottom = searchTower[0];
MOZ_ASSERT(bottom->compareTo(query) < 0);
JitcodeGlobalEntry* bottomNext = bottom->tower_->next(0);
if (bottomNext == nullptr) {
return nullptr;
}
int cmp = bottomNext->compareTo(query);
MOZ_ASSERT(cmp >= 0);
return (cmp == 0) ? bottomNext : nullptr;
}
bool JitcodeGlobalTable::addEntry(const JitcodeGlobalEntry& entry) {
MOZ_ASSERT(entry.isIon() || entry.isBaseline() || entry.isIonCache() ||
entry.isDummy());
JitcodeGlobalEntry* searchTower[JitcodeSkiplistTower::MAX_HEIGHT];
searchInternal(entry, searchTower);
JitcodeSkiplistTower* newTower = allocateTower(generateTowerHeight());
if (!newTower) {
return false;
}
JitcodeGlobalEntry* newEntry = allocateEntry();
if (!newEntry) {
return false;
}
*newEntry = entry;
newEntry->tower_ = newTower;
AutoSuppressProfilerSampling suppressSampling(TlsContext.get());
for (int level = newTower->height() - 1; level >= 0; level--) {
JitcodeGlobalEntry* searchTowerEntry = searchTower[level];
if (searchTowerEntry) {
MOZ_ASSERT(searchTowerEntry->compareTo(*newEntry) < 0);
JitcodeGlobalEntry* searchTowerNextEntry =
searchTowerEntry->tower_->next(level);
MOZ_ASSERT_IF(searchTowerNextEntry,
searchTowerNextEntry->compareTo(*newEntry) > 0);
newTower->setNext(level, searchTowerNextEntry);
searchTowerEntry->tower_->setNext(level, newEntry);
} else {
newTower->setNext(level, startTower_[level]);
startTower_[level] = newEntry;
}
}
skiplistSize_++;
if (entry.canHoldNurseryPointers()) {
addToNurseryList(&newEntry->ionEntry());
}
return true;
}
void JitcodeGlobalTable::removeEntry(JitcodeGlobalEntry& entry,
JitcodeGlobalEntry** prevTower) {
MOZ_ASSERT(!TlsContext.get()->isProfilerSamplingEnabled());
if (entry.canHoldNurseryPointers()) {
removeFromNurseryList(&entry.ionEntry());
}
for (int level = entry.tower_->height() - 1; level >= 0; level--) {
JitcodeGlobalEntry* prevTowerEntry = prevTower[level];
if (prevTowerEntry) {
MOZ_ASSERT(prevTowerEntry->tower_->next(level) == &entry);
prevTowerEntry->tower_->setNext(level, entry.tower_->next(level));
} else {
startTower_[level] = entry.tower_->next(level);
}
}
skiplistSize_--;
entry.destroy();
entry.tower_->addToFreeList(&(freeTowers_[entry.tower_->height() - 1]));
entry.tower_ = nullptr;
entry = JitcodeGlobalEntry();
entry.addToFreeList(&freeEntries_);
}
void JitcodeGlobalTable::releaseEntry(JitcodeGlobalEntry& entry,
JitcodeGlobalEntry** prevTower,
JSRuntime* rt) {
#ifdef DEBUG
Maybe<uint64_t> rangeStart = rt->profilerSampleBufferRangeStart();
MOZ_ASSERT_IF(rangeStart, !entry.isSampled(*rangeStart));
#endif
removeEntry(entry, prevTower);
}
void JitcodeGlobalTable::searchInternal(const JitcodeGlobalEntry& query,
JitcodeGlobalEntry** towerOut) {
JitcodeGlobalEntry* cur = nullptr;
for (int level = JitcodeSkiplistTower::MAX_HEIGHT - 1; level >= 0; level--) {
JitcodeGlobalEntry* entry = searchAtHeight(level, cur, query);
MOZ_ASSERT_IF(entry == nullptr, cur == nullptr);
towerOut[level] = entry;
cur = entry;
}
#ifdef DEBUG
for (int level = JitcodeSkiplistTower::MAX_HEIGHT - 1; level >= 0; level--) {
if (towerOut[level] == nullptr) {
MOZ_ASSERT_IF(unsigned(level) < (JitcodeSkiplistTower::MAX_HEIGHT - 1),
towerOut[level + 1] == nullptr);
continue;
}
JitcodeGlobalEntry* cur = towerOut[level];
MOZ_ASSERT(cur->compareTo(query) < 0);
if (!cur->tower_->next(level)) {
continue;
}
JitcodeGlobalEntry* next = cur->tower_->next(level);
MOZ_ASSERT(unsigned(level) < next->tower_->height());
MOZ_ASSERT(next->compareTo(query) >= 0);
}
#endif }
JitcodeGlobalEntry* JitcodeGlobalTable::searchAtHeight(
unsigned level, JitcodeGlobalEntry* start,
const JitcodeGlobalEntry& query) {
JitcodeGlobalEntry* cur = start;
if (start == nullptr) {
cur = startTower_[level];
if (cur == nullptr || cur->compareTo(query) >= 0) {
return nullptr;
}
}
for (;;) {
JitcodeGlobalEntry* next = cur->tower_->next(level);
if (next == nullptr || next->compareTo(query) >= 0) {
return cur;
}
cur = next;
}
}
unsigned JitcodeGlobalTable::generateTowerHeight() {
rand_ ^= mozilla::RotateLeft(rand_, 5) ^ mozilla::RotateLeft(rand_, 24);
rand_ += 0x37798849;
unsigned result = 0;
for (unsigned i = 0; i < JitcodeSkiplistTower::MAX_HEIGHT - 1; i++) {
if ((rand_ >> i) & 0x1) {
break;
}
result++;
}
return result + 1;
}
JitcodeSkiplistTower* JitcodeGlobalTable::allocateTower(unsigned height) {
MOZ_ASSERT(height >= 1);
JitcodeSkiplistTower* tower =
JitcodeSkiplistTower::PopFromFreeList(&freeTowers_[height - 1]);
if (tower) {
return tower;
}
size_t size = JitcodeSkiplistTower::CalculateSize(height);
tower = (JitcodeSkiplistTower*)alloc_.alloc(size);
if (!tower) {
return nullptr;
}
return new (tower) JitcodeSkiplistTower(height);
}
JitcodeGlobalEntry* JitcodeGlobalTable::allocateEntry() {
JitcodeGlobalEntry* entry =
JitcodeGlobalEntry::PopFromFreeList(&freeEntries_);
if (entry) {
return entry;
}
return alloc_.new_<JitcodeGlobalEntry>();
}
#ifdef DEBUG
void JitcodeGlobalTable::verifySkiplist() {
JitcodeGlobalEntry* curTower[JitcodeSkiplistTower::MAX_HEIGHT];
for (unsigned i = 0; i < JitcodeSkiplistTower::MAX_HEIGHT; i++) {
curTower[i] = startTower_[i];
}
uint32_t count = 0;
JitcodeGlobalEntry* curEntry = startTower_[0];
while (curEntry) {
count++;
unsigned curHeight = curEntry->tower_->height();
MOZ_ASSERT(curHeight >= 1);
for (unsigned i = 0; i < JitcodeSkiplistTower::MAX_HEIGHT; i++) {
if (i < curHeight) {
MOZ_ASSERT(curTower[i] == curEntry);
JitcodeGlobalEntry* nextEntry = curEntry->tower_->next(i);
MOZ_ASSERT_IF(nextEntry, curEntry->compareTo(*nextEntry) < 0);
curTower[i] = nextEntry;
} else {
MOZ_ASSERT_IF(curTower[i], curTower[i]->compareTo(*curEntry) > 0);
}
}
curEntry = curEntry->tower_->next(0);
}
MOZ_ASSERT(count == skiplistSize_);
}
#endif
void JitcodeGlobalTable::setAllEntriesAsExpired() {
AutoSuppressProfilerSampling suppressSampling(TlsContext.get());
for (Range r(*this); !r.empty(); r.popFront()) {
auto entry = r.front();
if (entry->canHoldNurseryPointers()) {
removeFromNurseryList(&entry->ionEntry());
}
entry->setAsExpired();
}
}
struct Unconditionally {
template <typename T>
static bool ShouldTrace(JSRuntime* rt, T* thingp) {
return true;
}
};
void JitcodeGlobalTable::traceForMinorGC(JSTracer* trc) {
MOZ_ASSERT(trc->runtime()->geckoProfiler().enabled());
MOZ_ASSERT(JS::RuntimeHeapIsMinorCollecting());
JSContext* cx = trc->runtime()->mainContextFromOwnThread();
AutoSuppressProfilerSampling suppressSampling(cx);
JitcodeGlobalEntry::IonEntry* entry = nurseryEntries_;
while (entry) {
entry->trace<Unconditionally>(trc);
JitcodeGlobalEntry::IonEntry* prev = entry;
entry = entry->nextNursery_;
removeFromNurseryList(prev);
}
}
struct IfUnmarked {
template <typename T>
static bool ShouldTrace(JSRuntime* rt, T* thingp) {
return !IsMarkedUnbarriered(rt, thingp);
}
};
template <>
bool IfUnmarked::ShouldTrace<TypeSet::Type>(JSRuntime* rt,
TypeSet::Type* type) {
return !TypeSet::IsTypeMarked(rt, type);
}
bool JitcodeGlobalTable::markIteratively(GCMarker* marker) {
MOZ_ASSERT(!JS::RuntimeHeapIsMinorCollecting());
AutoSuppressProfilerSampling suppressSampling(TlsContext.get());
Maybe<uint64_t> rangeStart =
marker->runtime()->profilerSampleBufferRangeStart();
bool markedAny = false;
for (Range r(*this); !r.empty(); r.popFront()) {
JitcodeGlobalEntry* entry = r.front();
if (!rangeStart || !entry->isSampled(*rangeStart)) {
if (entry->canHoldNurseryPointers()) {
removeFromNurseryList(&entry->ionEntry());
}
entry->setAsExpired();
if (!entry->baseEntry().isJitcodeMarkedFromAnyThread(marker->runtime())) {
continue;
}
}
if (!entry->zone()->isCollecting() || entry->zone()->isGCFinished()) {
continue;
}
markedAny |= entry->trace<IfUnmarked>(marker);
}
return markedAny;
}
void JitcodeGlobalTable::sweep(JSRuntime* rt) {
AutoSuppressProfilerSampling suppressSampling(rt->mainContextFromOwnThread());
for (Enum e(*this, rt); !e.empty(); e.popFront()) {
JitcodeGlobalEntry* entry = e.front();
if (!entry->zone()->isCollecting() || entry->zone()->isGCFinished()) {
continue;
}
if (entry->baseEntry().isJitcodeAboutToBeFinalized()) {
e.removeFront();
} else {
entry->sweepChildren(rt);
}
}
}
template <class ShouldTraceProvider>
bool JitcodeGlobalEntry::BaseEntry::traceJitcode(JSTracer* trc) {
if (ShouldTraceProvider::ShouldTrace(trc->runtime(), &jitcode_)) {
TraceManuallyBarrieredEdge(trc, &jitcode_,
"jitcodglobaltable-baseentry-jitcode");
return true;
}
return false;
}
bool JitcodeGlobalEntry::BaseEntry::isJitcodeMarkedFromAnyThread(
JSRuntime* rt) {
return IsMarkedUnbarriered(rt, &jitcode_);
}
bool JitcodeGlobalEntry::BaseEntry::isJitcodeAboutToBeFinalized() {
return IsAboutToBeFinalizedUnbarriered(&jitcode_);
}
template <class ShouldTraceProvider>
bool JitcodeGlobalEntry::BaselineEntry::trace(JSTracer* trc) {
if (ShouldTraceProvider::ShouldTrace(trc->runtime(), &script_)) {
TraceManuallyBarrieredEdge(trc, &script_,
"jitcodeglobaltable-baselineentry-script");
return true;
}
return false;
}
void JitcodeGlobalEntry::BaselineEntry::sweepChildren() {
MOZ_ALWAYS_FALSE(IsAboutToBeFinalizedUnbarriered(&script_));
}
bool JitcodeGlobalEntry::BaselineEntry::isMarkedFromAnyThread(JSRuntime* rt) {
return IsMarkedUnbarriered(rt, &script_);
}
template <class ShouldTraceProvider>
bool JitcodeGlobalEntry::IonEntry::trace(JSTracer* trc) {
bool tracedAny = false;
JSRuntime* rt = trc->runtime();
for (unsigned i = 0; i < numScripts(); i++) {
if (ShouldTraceProvider::ShouldTrace(rt,
&sizedScriptList()->pairs[i].script)) {
TraceManuallyBarrieredEdge(trc, &sizedScriptList()->pairs[i].script,
"jitcodeglobaltable-ionentry-script");
tracedAny = true;
}
}
if (!optsAllTypes_) {
return tracedAny;
}
for (IonTrackedTypeWithAddendum* iter = optsAllTypes_->begin();
iter != optsAllTypes_->end(); iter++) {
if (ShouldTraceProvider::ShouldTrace(rt, &iter->type)) {
iter->type.trace(trc);
tracedAny = true;
}
if (iter->hasAllocationSite() &&
ShouldTraceProvider::ShouldTrace(rt, &iter->script)) {
TraceManuallyBarrieredEdge(
trc, &iter->script,
"jitcodeglobaltable-ionentry-type-addendum-script");
tracedAny = true;
} else if (iter->hasConstructor() &&
ShouldTraceProvider::ShouldTrace(rt, &iter->constructor)) {
TraceManuallyBarrieredEdge(
trc, &iter->constructor,
"jitcodeglobaltable-ionentry-type-addendum-constructor");
tracedAny = true;
}
}
return tracedAny;
}
void JitcodeGlobalEntry::IonEntry::sweepChildren() {
for (unsigned i = 0; i < numScripts(); i++) {
MOZ_ALWAYS_FALSE(
IsAboutToBeFinalizedUnbarriered(&sizedScriptList()->pairs[i].script));
}
if (!optsAllTypes_) {
return;
}
for (IonTrackedTypeWithAddendum* iter = optsAllTypes_->begin();
iter != optsAllTypes_->end(); iter++) {
MOZ_ALWAYS_FALSE(TypeSet::IsTypeAboutToBeFinalized(&iter->type));
if (iter->hasAllocationSite()) {
MOZ_ALWAYS_FALSE(IsAboutToBeFinalizedUnbarriered(&iter->script));
} else if (iter->hasConstructor()) {
MOZ_ALWAYS_FALSE(IsAboutToBeFinalizedUnbarriered(&iter->constructor));
}
}
}
bool JitcodeGlobalEntry::IonEntry::isMarkedFromAnyThread(JSRuntime* rt) {
for (unsigned i = 0; i < numScripts(); i++) {
if (!IsMarkedUnbarriered(rt, &sizedScriptList()->pairs[i].script)) {
return false;
}
}
if (!optsAllTypes_) {
return true;
}
for (IonTrackedTypeWithAddendum* iter = optsAllTypes_->begin();
iter != optsAllTypes_->end(); iter++) {
if (!TypeSet::IsTypeMarked(rt, &iter->type)) {
return false;
}
}
return true;
}
template <class ShouldTraceProvider>
bool JitcodeGlobalEntry::IonCacheEntry::trace(JSTracer* trc) {
JitcodeGlobalEntry& entry =
RejoinEntry(trc->runtime(), *this, nativeStartAddr());
return entry.trace<ShouldTraceProvider>(trc);
}
void JitcodeGlobalEntry::IonCacheEntry::sweepChildren(JSRuntime* rt) {
JitcodeGlobalEntry& entry = RejoinEntry(rt, *this, nativeStartAddr());
entry.sweepChildren(rt);
}
bool JitcodeGlobalEntry::IonCacheEntry::isMarkedFromAnyThread(JSRuntime* rt) {
JitcodeGlobalEntry& entry = RejoinEntry(rt, *this, nativeStartAddr());
return entry.isMarkedFromAnyThread(rt);
}
Maybe<uint8_t>
JitcodeGlobalEntry::IonCacheEntry::trackedOptimizationIndexAtAddr(
JSRuntime* rt, void* ptr, uint32_t* entryOffsetOut) {
MOZ_ASSERT(hasTrackedOptimizations());
MOZ_ASSERT(containsPointer(ptr));
JitcodeGlobalEntry& entry = RejoinEntry(rt, *this, ptr);
if (!entry.hasTrackedOptimizations()) {
return mozilla::Nothing();
}
uint32_t mainEntryOffsetOut;
Maybe<uint8_t> maybeIndex = entry.trackedOptimizationIndexAtAddr(
rt, rejoinAddr(), &mainEntryOffsetOut);
if (maybeIndex.isNothing()) {
return mozilla::Nothing();
}
*entryOffsetOut = 0;
return maybeIndex;
}
void JitcodeGlobalEntry::IonCacheEntry::forEachOptimizationAttempt(
JSRuntime* rt, uint8_t index, JS::ForEachTrackedOptimizationAttemptOp& op) {
JitcodeGlobalEntry& entry = RejoinEntry(rt, *this, nativeStartAddr());
if (!entry.hasTrackedOptimizations()) {
return;
}
entry.forEachOptimizationAttempt(rt, index, op);
op(JS::TrackedStrategy::InlineCache_OptimizedStub, trackedOutcome_);
}
void JitcodeGlobalEntry::IonCacheEntry::forEachOptimizationTypeInfo(
JSRuntime* rt, uint8_t index,
IonTrackedOptimizationsTypeInfo::ForEachOpAdapter& op) {
JitcodeGlobalEntry& entry = RejoinEntry(rt, *this, nativeStartAddr());
if (!entry.hasTrackedOptimizations()) {
return;
}
entry.forEachOptimizationTypeInfo(rt, index, op);
}
void JitcodeRegionEntry::WriteHead(CompactBufferWriter& writer,
uint32_t nativeOffset, uint8_t scriptDepth) {
writer.writeUnsigned(nativeOffset);
writer.writeByte(scriptDepth);
}
void JitcodeRegionEntry::ReadHead(CompactBufferReader& reader,
uint32_t* nativeOffset,
uint8_t* scriptDepth) {
*nativeOffset = reader.readUnsigned();
*scriptDepth = reader.readByte();
}
void JitcodeRegionEntry::WriteScriptPc(CompactBufferWriter& writer,
uint32_t scriptIdx, uint32_t pcOffset) {
writer.writeUnsigned(scriptIdx);
writer.writeUnsigned(pcOffset);
}
void JitcodeRegionEntry::ReadScriptPc(CompactBufferReader& reader,
uint32_t* scriptIdx, uint32_t* pcOffset) {
*scriptIdx = reader.readUnsigned();
*pcOffset = reader.readUnsigned();
}
void JitcodeRegionEntry::WriteDelta(CompactBufferWriter& writer,
uint32_t nativeDelta, int32_t pcDelta) {
if (pcDelta >= 0) {
if (pcDelta <= ENC1_PC_DELTA_MAX && nativeDelta <= ENC1_NATIVE_DELTA_MAX) {
uint8_t encVal = ENC1_MASK_VAL | (pcDelta << ENC1_PC_DELTA_SHIFT) |
(nativeDelta << ENC1_NATIVE_DELTA_SHIFT);
writer.writeByte(encVal);
return;
}
if (pcDelta <= ENC2_PC_DELTA_MAX && nativeDelta <= ENC2_NATIVE_DELTA_MAX) {
uint16_t encVal = ENC2_MASK_VAL | (pcDelta << ENC2_PC_DELTA_SHIFT) |
(nativeDelta << ENC2_NATIVE_DELTA_SHIFT);
writer.writeByte(encVal & 0xff);
writer.writeByte((encVal >> 8) & 0xff);
return;
}
}
if (pcDelta >= ENC3_PC_DELTA_MIN && pcDelta <= ENC3_PC_DELTA_MAX &&
nativeDelta <= ENC3_NATIVE_DELTA_MAX) {
uint32_t encVal =
ENC3_MASK_VAL |
((uint32_t(pcDelta) << ENC3_PC_DELTA_SHIFT) & ENC3_PC_DELTA_MASK) |
(nativeDelta << ENC3_NATIVE_DELTA_SHIFT);
writer.writeByte(encVal & 0xff);
writer.writeByte((encVal >> 8) & 0xff);
writer.writeByte((encVal >> 16) & 0xff);
return;
}
if (pcDelta >= ENC4_PC_DELTA_MIN && pcDelta <= ENC4_PC_DELTA_MAX &&
nativeDelta <= ENC4_NATIVE_DELTA_MAX) {
uint32_t encVal =
ENC4_MASK_VAL |
((uint32_t(pcDelta) << ENC4_PC_DELTA_SHIFT) & ENC4_PC_DELTA_MASK) |
(nativeDelta << ENC4_NATIVE_DELTA_SHIFT);
writer.writeByte(encVal & 0xff);
writer.writeByte((encVal >> 8) & 0xff);
writer.writeByte((encVal >> 16) & 0xff);
writer.writeByte((encVal >> 24) & 0xff);
return;
}
MOZ_CRASH("pcDelta/nativeDelta values are too large to encode.");
}
void JitcodeRegionEntry::ReadDelta(CompactBufferReader& reader,
uint32_t* nativeDelta, int32_t* pcDelta) {
const uint32_t firstByte = reader.readByte();
if ((firstByte & ENC1_MASK) == ENC1_MASK_VAL) {
uint32_t encVal = firstByte;
*nativeDelta = encVal >> ENC1_NATIVE_DELTA_SHIFT;
*pcDelta = (encVal & ENC1_PC_DELTA_MASK) >> ENC1_PC_DELTA_SHIFT;
MOZ_ASSERT_IF(*nativeDelta == 0, *pcDelta <= 0);
return;
}
const uint32_t secondByte = reader.readByte();
if ((firstByte & ENC2_MASK) == ENC2_MASK_VAL) {
uint32_t encVal = firstByte | secondByte << 8;
*nativeDelta = encVal >> ENC2_NATIVE_DELTA_SHIFT;
*pcDelta = (encVal & ENC2_PC_DELTA_MASK) >> ENC2_PC_DELTA_SHIFT;
MOZ_ASSERT(*pcDelta != 0);
MOZ_ASSERT_IF(*nativeDelta == 0, *pcDelta <= 0);
return;
}
const uint32_t thirdByte = reader.readByte();
if ((firstByte & ENC3_MASK) == ENC3_MASK_VAL) {
uint32_t encVal = firstByte | secondByte << 8 | thirdByte << 16;
*nativeDelta = encVal >> ENC3_NATIVE_DELTA_SHIFT;
uint32_t pcDeltaU = (encVal & ENC3_PC_DELTA_MASK) >> ENC3_PC_DELTA_SHIFT;
if (pcDeltaU > static_cast<uint32_t>(ENC3_PC_DELTA_MAX)) {
pcDeltaU |= ~ENC3_PC_DELTA_MAX;
}
*pcDelta = pcDeltaU;
MOZ_ASSERT(*pcDelta != 0);
MOZ_ASSERT_IF(*nativeDelta == 0, *pcDelta <= 0);
return;
}
MOZ_ASSERT((firstByte & ENC4_MASK) == ENC4_MASK_VAL);
const uint32_t fourthByte = reader.readByte();
uint32_t encVal =
firstByte | secondByte << 8 | thirdByte << 16 | fourthByte << 24;
*nativeDelta = encVal >> ENC4_NATIVE_DELTA_SHIFT;
uint32_t pcDeltaU = (encVal & ENC4_PC_DELTA_MASK) >> ENC4_PC_DELTA_SHIFT;
if (pcDeltaU > static_cast<uint32_t>(ENC4_PC_DELTA_MAX)) {
pcDeltaU |= ~ENC4_PC_DELTA_MAX;
}
*pcDelta = pcDeltaU;
MOZ_ASSERT(*pcDelta != 0);
MOZ_ASSERT_IF(*nativeDelta == 0, *pcDelta <= 0);
}
uint32_t JitcodeRegionEntry::ExpectedRunLength(const NativeToBytecode* entry,
const NativeToBytecode* end) {
MOZ_ASSERT(entry < end);
uint32_t runLength = 1;
uint32_t curNativeOffset = entry->nativeOffset.offset();
uint32_t curBytecodeOffset = entry->tree->script()->pcToOffset(entry->pc);
for (auto nextEntry = entry + 1; nextEntry != end; nextEntry += 1) {
if (nextEntry->tree != entry->tree) {
break;
}
uint32_t nextNativeOffset = nextEntry->nativeOffset.offset();
uint32_t nextBytecodeOffset =
nextEntry->tree->script()->pcToOffset(nextEntry->pc);
MOZ_ASSERT(nextNativeOffset >= curNativeOffset);
uint32_t nativeDelta = nextNativeOffset - curNativeOffset;
int32_t bytecodeDelta =
int32_t(nextBytecodeOffset) - int32_t(curBytecodeOffset);
if (!IsDeltaEncodeable(nativeDelta, bytecodeDelta)) {
break;
}
runLength++;
if (runLength == MAX_RUN_LENGTH) {
break;
}
curNativeOffset = nextNativeOffset;
curBytecodeOffset = nextBytecodeOffset;
}
return runLength;
}
struct JitcodeMapBufferWriteSpewer {
#ifdef JS_JITSPEW
CompactBufferWriter* writer;
uint32_t startPos;
static const uint32_t DumpMaxBytes = 50;
explicit JitcodeMapBufferWriteSpewer(CompactBufferWriter& w)
: writer(&w), startPos(writer->length()) {}
void spewAndAdvance(const char* name) {
if (writer->oom()) {
return;
}
uint32_t curPos = writer->length();
const uint8_t* start = writer->buffer() + startPos;
const uint8_t* end = writer->buffer() + curPos;
const char* MAP = "0123456789ABCDEF";
uint32_t bytes = end - start;
char buffer[DumpMaxBytes * 3];
for (uint32_t i = 0; i < bytes; i++) {
buffer[i * 3] = MAP[(start[i] >> 4) & 0xf];
buffer[i * 3 + 1] = MAP[(start[i] >> 0) & 0xf];
buffer[i * 3 + 2] = ' ';
}
if (bytes >= DumpMaxBytes) {
buffer[DumpMaxBytes * 3 - 1] = '\0';
} else {
buffer[bytes * 3 - 1] = '\0';
}
JitSpew(JitSpew_Profiling, "%s@%d[%d bytes] - %s", name, int(startPos),
int(bytes), buffer);
startPos = writer->length();
}
#else
explicit JitcodeMapBufferWriteSpewer(CompactBufferWriter& w) {}
void spewAndAdvance(const char* name) {}
#endif };
bool JitcodeRegionEntry::WriteRun(CompactBufferWriter& writer,
JSScript** scriptList,
uint32_t scriptListSize, uint32_t runLength,
const NativeToBytecode* entry) {
MOZ_ASSERT(runLength > 0);
MOZ_ASSERT(runLength <= MAX_RUN_LENGTH);
MOZ_ASSERT(entry->tree->depth() <= 0xff);
uint8_t scriptDepth = entry->tree->depth();
uint32_t regionNativeOffset = entry->nativeOffset.offset();
JitcodeMapBufferWriteSpewer spewer(writer);
JitSpew(JitSpew_Profiling, " Head Info: nativeOffset=%d scriptDepth=%d",
int(regionNativeOffset), int(scriptDepth));
WriteHead(writer, regionNativeOffset, scriptDepth);
spewer.spewAndAdvance(" ");
{
InlineScriptTree* curTree = entry->tree;
jsbytecode* curPc = entry->pc;
for (uint8_t i = 0; i < scriptDepth; i++) {
uint32_t scriptIdx = 0;
for (; scriptIdx < scriptListSize; scriptIdx++) {
if (scriptList[scriptIdx] == curTree->script()) {
break;
}
}
MOZ_ASSERT(scriptIdx < scriptListSize);
uint32_t pcOffset = curTree->script()->pcToOffset(curPc);
JitSpew(JitSpew_Profiling, " Script/PC %d: scriptIdx=%d pcOffset=%d",
int(i), int(scriptIdx), int(pcOffset));
WriteScriptPc(writer, scriptIdx, pcOffset);
spewer.spewAndAdvance(" ");
MOZ_ASSERT_IF(i < scriptDepth - 1, curTree->hasCaller());
curPc = curTree->callerPc();
curTree = curTree->caller();
}
}
uint32_t curNativeOffset = entry->nativeOffset.offset();
uint32_t curBytecodeOffset = entry->tree->script()->pcToOffset(entry->pc);
JitSpew(JitSpew_Profiling,
" Writing Delta Run from nativeOffset=%d bytecodeOffset=%d",
int(curNativeOffset), int(curBytecodeOffset));
for (uint32_t i = 1; i < runLength; i++) {
MOZ_ASSERT(entry[i].tree == entry->tree);
uint32_t nextNativeOffset = entry[i].nativeOffset.offset();
uint32_t nextBytecodeOffset =
entry[i].tree->script()->pcToOffset(entry[i].pc);
MOZ_ASSERT(nextNativeOffset >= curNativeOffset);
uint32_t nativeDelta = nextNativeOffset - curNativeOffset;
int32_t bytecodeDelta =
int32_t(nextBytecodeOffset) - int32_t(curBytecodeOffset);
MOZ_ASSERT(IsDeltaEncodeable(nativeDelta, bytecodeDelta));
JitSpew(JitSpew_Profiling,
" RunEntry native: %d-%d [%d] bytecode: %d-%d [%d]",
int(curNativeOffset), int(nextNativeOffset), int(nativeDelta),
int(curBytecodeOffset), int(nextBytecodeOffset),
int(bytecodeDelta));
WriteDelta(writer, nativeDelta, bytecodeDelta);
if (curBytecodeOffset < nextBytecodeOffset) {
JitSpewStart(JitSpew_Profiling, " OPS: ");
uint32_t curBc = curBytecodeOffset;
while (curBc < nextBytecodeOffset) {
jsbytecode* pc = entry[i].tree->script()->offsetToPC(curBc);
#ifdef JS_JITSPEW
JSOp op = JSOp(*pc);
JitSpewCont(JitSpew_Profiling, "%s ", CodeName[op]);
#endif
curBc += GetBytecodeLength(pc);
}
JitSpewFin(JitSpew_Profiling);
}
spewer.spewAndAdvance(" ");
curNativeOffset = nextNativeOffset;
curBytecodeOffset = nextBytecodeOffset;
}
if (writer.oom()) {
return false;
}
return true;
}
void JitcodeRegionEntry::unpack() {
CompactBufferReader reader(data_, end_);
ReadHead(reader, &nativeOffset_, &scriptDepth_);
MOZ_ASSERT(scriptDepth_ > 0);
scriptPcStack_ = reader.currentPosition();
for (unsigned i = 0; i < scriptDepth_; i++) {
uint32_t scriptIdx, pcOffset;
ReadScriptPc(reader, &scriptIdx, &pcOffset);
}
deltaRun_ = reader.currentPosition();
}
uint32_t JitcodeRegionEntry::findPcOffset(uint32_t queryNativeOffset,
uint32_t startPcOffset) const {
DeltaIterator iter = deltaIterator();
uint32_t curNativeOffset = nativeOffset();
uint32_t curPcOffset = startPcOffset;
while (iter.hasMore()) {
uint32_t nativeDelta;
int32_t pcDelta;
iter.readNext(&nativeDelta, &pcDelta);
if (queryNativeOffset <= curNativeOffset + nativeDelta) {
break;
}
curNativeOffset += nativeDelta;
curPcOffset += pcDelta;
}
return curPcOffset;
}
bool JitcodeIonTable::makeIonEntry(JSContext* cx, JitCode* code,
uint32_t numScripts, JSScript** scripts,
JitcodeGlobalEntry::IonEntry& out) {
typedef JitcodeGlobalEntry::IonEntry::SizedScriptList SizedScriptList;
MOZ_ASSERT(numScripts > 0);
typedef js::Vector<char*, 32, SystemAllocPolicy> ProfilingStringVector;
ProfilingStringVector profilingStrings;
if (!profilingStrings.reserve(numScripts)) {
return false;
}
auto autoFreeProfilingStrings = mozilla::MakeScopeExit([&] {
for (auto elem : profilingStrings) {
js_free(elem);
}
});
for (uint32_t i = 0; i < numScripts; i++) {
char* str = JitcodeGlobalEntry::createScriptString(cx, scripts[i]);
if (!str) {
return false;
}
if (!profilingStrings.append(str)) {
return false;
}
}
void* mem =
(void*)cx->pod_malloc<uint8_t>(SizedScriptList::AllocSizeFor(numScripts));
if (!mem) {
return false;
}
autoFreeProfilingStrings.release();
SizedScriptList* scriptList =
new (mem) SizedScriptList(numScripts, scripts, &profilingStrings[0]);
out.init(code, code->raw(), code->rawEnd(), scriptList, this);
return true;
}
uint32_t JitcodeIonTable::findRegionEntry(uint32_t nativeOffset) const {
static const uint32_t LINEAR_SEARCH_THRESHOLD = 8;
uint32_t regions = numRegions();
MOZ_ASSERT(regions > 0);
if (regions <= LINEAR_SEARCH_THRESHOLD) {
JitcodeRegionEntry previousEntry = regionEntry(0);
for (uint32_t i = 1; i < regions; i++) {
JitcodeRegionEntry nextEntry = regionEntry(i);
MOZ_ASSERT(nextEntry.nativeOffset() >= previousEntry.nativeOffset());
if (nativeOffset <= nextEntry.nativeOffset()) {
return i - 1;
}
previousEntry = nextEntry;
}
return regions - 1;
}
uint32_t idx = 0;
uint32_t count = regions;
while (count > 1) {
uint32_t step = count / 2;
uint32_t mid = idx + step;
JitcodeRegionEntry midEntry = regionEntry(mid);
if (nativeOffset <= midEntry.nativeOffset()) {
count = step;
} else { idx = mid;
count -= step;
}
}
return idx;
}
bool JitcodeIonTable::WriteIonTable(
CompactBufferWriter& writer, JSScript** scriptList, uint32_t scriptListSize,
const NativeToBytecode* start, const NativeToBytecode* end,
uint32_t* tableOffsetOut, uint32_t* numRegionsOut) {
MOZ_ASSERT(tableOffsetOut != nullptr);
MOZ_ASSERT(numRegionsOut != nullptr);
MOZ_ASSERT(writer.length() == 0);
MOZ_ASSERT(scriptListSize > 0);
JitSpew(JitSpew_Profiling,
"Writing native to bytecode map for %s:%u:%u (%zu entries)",
scriptList[0]->filename(), scriptList[0]->lineno(),
scriptList[0]->column(), mozilla::PointerRangeSize(start, end));
JitSpew(JitSpew_Profiling, " ScriptList of size %d", int(scriptListSize));
for (uint32_t i = 0; i < scriptListSize; i++) {
JitSpew(JitSpew_Profiling, " Script %d - %s:%u:%u", int(i),
scriptList[i]->filename(), scriptList[i]->lineno(),
scriptList[i]->column());
}
const NativeToBytecode* curEntry = start;
js::Vector<uint32_t, 32, SystemAllocPolicy> runOffsets;
while (curEntry != end) {
uint32_t runLength = JitcodeRegionEntry::ExpectedRunLength(curEntry, end);
MOZ_ASSERT(runLength > 0);
MOZ_ASSERT(runLength <= uintptr_t(end - curEntry));
JitSpew(JitSpew_Profiling, " Run at entry %d, length %d, buffer offset %d",
int(curEntry - start), int(runLength), int(writer.length()));
if (!runOffsets.append(writer.length())) {
return false;
}
if (!JitcodeRegionEntry::WriteRun(writer, scriptList, scriptListSize,
runLength, curEntry)) {
return false;
}
curEntry += runLength;
}
uint32_t padding = sizeof(uint32_t) - (writer.length() % sizeof(uint32_t));
if (padding == sizeof(uint32_t)) {
padding = 0;
}
JitSpew(JitSpew_Profiling, " Padding %d bytes after run @%d", int(padding),
int(writer.length()));
for (uint32_t i = 0; i < padding; i++) {
writer.writeByte(0);
}
uint32_t tableOffset = writer.length();
JitSpew(JitSpew_Profiling, " Writing numRuns=%d", int(runOffsets.length()));
writer.writeNativeEndianUint32_t(runOffsets.length());
for (uint32_t i = 0; i < runOffsets.length(); i++) {
JitSpew(JitSpew_Profiling, " Run %d offset=%d backOffset=%d @%d", int(i),
int(runOffsets[i]), int(tableOffset - runOffsets[i]),
int(writer.length()));
writer.writeNativeEndianUint32_t(tableOffset - runOffsets[i]);
}
if (writer.oom()) {
return false;
}
*tableOffsetOut = tableOffset;
*numRegionsOut = runOffsets.length();
return true;
}
} }
JS::ProfiledFrameHandle::ProfiledFrameHandle(JSRuntime* rt,
js::jit::JitcodeGlobalEntry& entry,
void* addr, const char* label,
uint32_t depth)
: rt_(rt),
entry_(entry),
addr_(addr),
canonicalAddr_(nullptr),
label_(label),
depth_(depth),
optsIndex_() {
updateHasTrackedOptimizations();
if (!canonicalAddr_) {
MOZ_ASSERT_IF(entry_.isIon(), !hasTrackedOptimizations());
canonicalAddr_ = entry_.canonicalNativeAddrFor(rt_, addr_);
}
}
JS_PUBLIC_API JS::ProfilingFrameIterator::FrameKind
JS::ProfiledFrameHandle::frameKind() const {
if (entry_.isBaseline()) {
return JS::ProfilingFrameIterator::Frame_Baseline;
}
return JS::ProfilingFrameIterator::Frame_Ion;
}
JS_PUBLIC_API JS::ProfiledFrameRange JS::GetProfiledFrames(JSContext* cx,
void* addr) {
JSRuntime* rt = cx->runtime();
js::jit::JitcodeGlobalTable* table =
rt->jitRuntime()->getJitcodeGlobalTable();
js::jit::JitcodeGlobalEntry* entry = table->lookup(addr);
ProfiledFrameRange result(rt, addr, entry);
if (entry) {
result.depth_ = entry->callStackAtAddr(rt, addr, result.labels_,
MOZ_ARRAY_LENGTH(result.labels_));
}
return result;
}
JS::ProfiledFrameHandle JS::ProfiledFrameRange::Iter::operator*() const {
uint32_t depth = range_.depth_ - 1 - index_;
return ProfiledFrameHandle(range_.rt_, *range_.entry_, range_.addr_,
range_.labels_[depth], depth);
}