Bug 1830298 - Refactor nursery allocation counts to remove separate counters r=jandem

The aim of this change is to increment at most a single counter on every
nursery allocation.

At the moment nursery allocations are counted in the following ways:
 - on the allocation site, except for optimised JIT code
 - on the zone for interpreter string allocations
 - a global count if the profiler is enabled

The patch changes this so we only count at allocation sites (including the
catch-all unknow sites). There's still an exception for non-string allocations
by optimised JIT code when the profiler is disabled, so we don't count in this
case.

The other counts are then calculated from the allocation site data.

Differential Revision: https://phabricator.services.mozilla.com/D176657
This commit is contained in:
Jon Coppeard 2023-05-02 08:00:10 +00:00
parent e3d0b707b7
commit e936789b18
17 changed files with 219 additions and 150 deletions

View File

@ -42,6 +42,7 @@ enum class TraceKind {
// These trace kinds have a publicly exposed, although opaque, C++ type.
// Note: The order here is determined by our Value packing. Other users
// should sort alphabetically, for consistency.
// Note: Nursery allocatable kinds go first. See js::gc::NurseryTraceKinds.
Object = 0x00,
BigInt = 0x01,
String = 0x02,

View File

@ -65,7 +65,7 @@ void* gc::CellAllocator::AllocateObjectCell(JSContext* cx, AllocKind kind,
if (heap != TenuredHeap && cx->zone()->allocNurseryObjects) {
if (!site) {
site = cx->zone()->unknownAllocSite();
site = cx->zone()->unknownAllocSite(JS::TraceKind::Object);
}
void* obj = rt->gc.tryNewNurseryObject<allowGC>(cx, thingSize, clasp, site);
@ -129,7 +129,7 @@ void* GCRuntime::tryNewNurseryStringCell(JSContext* cx, size_t thingSize,
MOZ_ASSERT(cx->isNurseryAllocAllowed());
MOZ_ASSERT(!cx->zone()->isAtomsZone());
AllocSite* site = cx->zone()->unknownAllocSite();
AllocSite* site = cx->zone()->unknownAllocSite(JS::TraceKind::String);
void* ptr = cx->nursery().allocateString(site, thingSize);
if (ptr) {
return ptr;
@ -197,7 +197,7 @@ void* GCRuntime::tryNewNurseryBigIntCell(JSContext* cx, size_t thingSize,
MOZ_ASSERT(cx->isNurseryAllocAllowed());
MOZ_ASSERT(!cx->zone()->isAtomsZone());
AllocSite* site = cx->zone()->unknownAllocSite();
AllocSite* site = cx->zone()->unknownAllocSite(JS::TraceKind::BigInt);
void* ptr = cx->nursery().allocateBigInt(site, thingSize);
if (ptr) {
return ptr;

View File

@ -376,9 +376,6 @@ class GCRuntime {
const void* addressOfBigIntNurseryCurrentEnd() {
return nursery_.refNoCheck().addressOfCurrentBigIntEnd();
}
uint32_t* addressOfNurseryAllocCount() {
return stats().addressOfAllocsSinceMinorGCNursery();
}
const void* addressOfLastBufferedWholeCell() {
return storeBuffer_.refNoCheck().addressOfLastBufferedWholeCell();

View File

@ -486,6 +486,7 @@ void* js::Nursery::allocateCell(gc::AllocSite* site, size_t size,
// RelocationOverlay.
MOZ_ASSERT(size >= sizeof(RelocationOverlay));
MOZ_ASSERT(size % CellAlignBytes == 0);
MOZ_ASSERT(size_t(kind) < NurseryTraceKinds);
void* ptr = allocate(sizeof(NurseryCellHeader) + size);
if (!ptr) {
@ -506,24 +507,10 @@ void* js::Nursery::allocateCell(gc::AllocSite* site, size_t size,
MOZ_ASSERT_IF(site->isNormal(), site->isInAllocatedList());
}
// We count this regardless of the profiler's state, assuming that it costs
// just as much to count it, as to check the profiler's state and decide not
// to count it.
stats().noteNurseryAlloc();
gcprobes::NurseryAlloc(cell, kind);
return cell;
}
void* js::Nursery::allocateString(gc::AllocSite* site, size_t size) {
MOZ_ASSERT(canAllocateStrings());
void* ptr = allocateCell(site, size, JS::TraceKind::String);
if (ptr) {
site->zone()->nurseryAllocatedStrings++;
}
return ptr;
}
inline void* js::Nursery::allocate(size_t size) {
MOZ_ASSERT(isEnabled());
MOZ_ASSERT(!JS::RuntimeHeapIsBusy());
@ -870,7 +857,7 @@ void js::Nursery::renderProfileJSON(JSONPrinter& json) const {
// and then there's no guarentee.
if (runtime()->geckoProfiler().enabled()) {
json.property("cells_allocated_nursery",
stats().allocsSinceMinorGCNursery());
pretenuringNursery.totalAllocCount());
json.property("cells_allocated_tenured",
stats().allocsSinceMinorGCTenured());
}
@ -1258,7 +1245,7 @@ void js::Nursery::collect(JS::GCOptions options, JS::GCReason reason) {
TimeDuration totalTime = profileDurations_[ProfileKey::Total];
sendTelemetry(reason, totalTime, wasEmpty, promotionRate, sitesPretenured);
stats().endNurseryCollection(reason);
stats().endNurseryCollection(reason); // Calls GCNurseryCollectionCallback.
gcprobes::MinorGCEnd();
timeInChunkAlloc_ = mozilla::TimeDuration();
@ -1575,14 +1562,15 @@ size_t js::Nursery::doPretenuring(JSRuntime* rt, JS::GCReason reason,
// For some tests in JetStream2 and Kraken, the tenuredRate is high but the
// number of allocated strings is low. So we calculate the tenuredRate only
// if the number of string allocations is enough.
bool allocThreshold = zone->nurseryAllocatedStrings > 30000;
uint32_t zoneNurseryStrings =
zone->nurseryAllocCount(JS::TraceKind::String);
bool allocThreshold = zoneNurseryStrings > 30000;
uint64_t zoneTenuredStrings =
zone->stringStats.ref().liveNurseryStrings -
zone->previousGCStringStats.ref().liveNurseryStrings;
double tenuredRate =
allocThreshold
? double(zoneTenuredStrings) / double(zone->nurseryAllocatedStrings)
: 0.0;
allocThreshold ? double(zoneTenuredStrings) / double(zoneNurseryStrings)
: 0.0;
bool disableNurseryStrings =
pretenureStr && zone->allocNurseryStrings &&
tenuredRate > tunables().pretenureStringThreshold();
@ -1620,7 +1608,6 @@ size_t js::Nursery::doPretenuring(JSRuntime* rt, JS::GCReason reason,
numStringsTenured += zoneTenuredStrings;
numBigIntsTenured += zone->tenuredBigInts;
zone->tenuredBigInts = 0;
zone->nurseryAllocatedStrings = 0;
}
session.reset(); // End the minor GC session, if running one.
stats().setStat(gcstats::STAT_NURSERY_STRING_REALMS_DISABLED,

View File

@ -15,6 +15,7 @@
#include "gc/GCParallelTask.h"
#include "gc/Heap.h"
#include "gc/MallocedBlockCache.h"
#include "gc/Pretenuring.h"
#include "js/AllocPolicy.h"
#include "js/Class.h"
#include "js/GCAPI.h"
@ -185,7 +186,10 @@ class alignas(TypicalCacheLineSize) Nursery {
MOZ_ASSERT(canAllocateBigInts());
return allocateCell(site, size, JS::TraceKind::BigInt);
}
void* allocateString(gc::AllocSite* site, size_t size);
void* allocateString(gc::AllocSite* site, size_t size) {
MOZ_ASSERT(canAllocateStrings());
return allocateCell(site, size, JS::TraceKind::String);
}
static size_t nurseryCellHeaderSize() {
return sizeof(gc::NurseryCellHeader);

View File

@ -32,6 +32,7 @@ static constexpr size_t MaxAllocSitesPerMinorGC = 500;
// The maximum number of times to invalidate JIT code for a site. After this we
// leave the site's state as Unknown and don't pretenure allocations.
// Note we use 4 bits to store the invalidation count.
static constexpr size_t MaxInvalidationCount = 5;
// The minimum number of allocated cells needed to determine the survival rate
@ -69,6 +70,9 @@ JS_PUBLIC_API void JS::SetSiteBasedPretenuringEnabled(bool enable) {
SiteBasedPretenuringEnabled = enable;
}
class PretenuringNursery::MaybeGCSession
: public mozilla::Maybe<AutoGCSession> {};
bool PretenuringNursery::canCreateAllocSite() {
MOZ_ASSERT(allocSitesCreated <= MaxAllocSitesPerMinorGC);
return SiteBasedPretenuringEnabled &&
@ -79,13 +83,21 @@ size_t PretenuringNursery::doPretenuring(GCRuntime* gc, JS::GCReason reason,
bool validPromotionRate,
double promotionRate, bool reportInfo,
size_t reportThreshold) {
mozilla::Maybe<AutoGCSession> session;
MaybeGCSession session;
size_t sitesActive = 0;
size_t sitesPretenured = 0;
size_t sitesInvalidated = 0;
size_t zonesWithHighNurserySurvival = 0;
// Zero allocation counts.
totalAllocCount_ = 0;
for (ZonesIter zone(gc, SkipAtoms); !zone.done(); zone.next()) {
for (auto& count : zone->pretenuring.nurseryAllocCounts) {
count = 0;
}
}
// Check whether previously optimized code has changed its behaviour and
// needs to be recompiled so that it can pretenure its allocations.
if (validPromotionRate) {
@ -114,58 +126,22 @@ size_t PretenuringNursery::doPretenuring(GCRuntime* gc, JS::GCReason reason,
MOZ_ASSERT_IF(site->isNormal(),
site->nurseryAllocCount >= site->nurseryTenuredCount);
bool hasPromotionRate = false;
double promotionRate = 0.0;
bool wasInvalidated = false;
if (site->isNormal()) {
sitesActive++;
if (site->nurseryAllocCount > AllocSiteAttentionThreshold) {
promotionRate =
double(site->nurseryTenuredCount) / double(site->nurseryAllocCount);
hasPromotionRate = true;
AllocSite::State prevState = site->state();
site->updateStateOnMinorGC(promotionRate);
AllocSite::State newState = site->state();
if (prevState == AllocSite::State::Unknown &&
newState == AllocSite::State::LongLived) {
sitesPretenured++;
// We can optimize JIT code before we realise that a site should be
// pretenured. Make sure we invalidate any existing optimized code.
if (!session.isSome()) {
session.emplace(gc, JS::HeapState::MinorCollecting);
}
if (site->hasScript()) {
wasInvalidated = site->invalidateScript(gc);
if (wasInvalidated) {
sitesInvalidated++;
}
}
}
}
processSite(gc, site, sitesActive, sitesPretenured, sitesInvalidated,
session, reportInfo, reportThreshold);
}
if (reportInfo && site->allocCount() >= reportThreshold) {
site->printInfo(hasPromotionRate, promotionRate, wasInvalidated);
}
site->resetNurseryAllocations();
site = next;
}
// Catch-all sites don't end up on the list if they are only used from
// optimized JIT code, so process them here.
for (ZonesIter zone(gc, SkipAtoms); !zone.done(); zone.next()) {
reportAndResetCatchAllSite(zone->unknownAllocSite(), reportInfo,
reportThreshold);
reportAndResetCatchAllSite(zone->optimizedAllocSite(), reportInfo,
reportThreshold);
for (auto& site : zone->pretenuring.unknownAllocSites) {
processCatchAllSite(&site, reportInfo, reportThreshold);
}
processCatchAllSite(zone->optimizedAllocSite(), reportInfo,
reportThreshold);
}
if (reportInfo) {
@ -182,13 +158,63 @@ size_t PretenuringNursery::doPretenuring(GCRuntime* gc, JS::GCReason reason,
return sitesPretenured;
}
void PretenuringNursery::reportAndResetCatchAllSite(AllocSite* site,
bool reportInfo,
size_t reportThreshold) {
void PretenuringNursery::processSite(GCRuntime* gc, AllocSite* site,
size_t& sitesActive,
size_t& sitesPretenured,
size_t& sitesInvalidated,
MaybeGCSession& session, bool reportInfo,
size_t reportThreshold) {
sitesActive++;
updateAllocCounts(site);
bool hasPromotionRate = false;
double promotionRate = 0.0;
bool wasInvalidated = false;
if (site->nurseryAllocCount > AllocSiteAttentionThreshold) {
promotionRate =
double(site->nurseryTenuredCount) / double(site->nurseryAllocCount);
hasPromotionRate = true;
AllocSite::State prevState = site->state();
site->updateStateOnMinorGC(promotionRate);
AllocSite::State newState = site->state();
if (prevState == AllocSite::State::Unknown &&
newState == AllocSite::State::LongLived) {
sitesPretenured++;
// We can optimize JIT code before we realise that a site should be
// pretenured. Make sure we invalidate any existing optimized code.
if (!session.isSome()) {
session.emplace(gc, JS::HeapState::MinorCollecting);
}
if (site->hasScript()) {
wasInvalidated = site->invalidateScript(gc);
if (wasInvalidated) {
sitesInvalidated++;
}
}
}
}
if (reportInfo && site->allocCount() >= reportThreshold) {
site->printInfo(hasPromotionRate, promotionRate, wasInvalidated);
}
site->resetNurseryAllocations();
}
void PretenuringNursery::processCatchAllSite(AllocSite* site, bool reportInfo,
size_t reportThreshold) {
if (!site->hasNurseryAllocations()) {
return;
}
updateAllocCounts(site);
if (reportInfo && site->allocCount() >= reportThreshold) {
site->printInfo(false, 0.0, false);
}
@ -196,6 +222,13 @@ void PretenuringNursery::reportAndResetCatchAllSite(AllocSite* site,
site->resetNurseryAllocations();
}
void PretenuringNursery::updateAllocCounts(AllocSite* site) {
JS::TraceKind kind = site->traceKind();
totalAllocCount_ += site->nurseryAllocCount;
PretenuringZone& zone = site->zone()->pretenuring;
zone.nurseryAllocCount(kind) += site->nurseryAllocCount;
}
bool AllocSite::invalidateScript(GCRuntime* gc) {
CancelOffThreadIonCompile(script());
@ -240,12 +273,12 @@ AllocSite::Kind AllocSite::kind() const {
return Kind::Normal;
}
if (this == zone()->unknownAllocSite()) {
return Kind::Unknown;
if (this == zone()->optimizedAllocSite()) {
return Kind::Optimized;
}
MOZ_ASSERT(this == zone()->optimizedAllocSite());
return Kind::Optimized;
MOZ_ASSERT(this == zone()->unknownAllocSite(traceKind()));
return Kind::Unknown;
}
void AllocSite::updateStateOnMinorGC(double promotionRate) {

View File

@ -36,6 +36,10 @@ namespace gc {
class GCRuntime;
class PretenuringNursery;
// Number of trace kinds supportd by the nursery. These are arranged at the
// start of JS::TraceKind.
static constexpr size_t NurseryTraceKinds = 3;
enum class CatchAllAllocSite { Unknown, Optimized };
// Information about an allocation site.
@ -55,7 +59,7 @@ class AllocSite {
static_assert((AllocSite::LONG_LIVED_BIT & int32_t(State::ShortLived)) == 0);
private:
JS::Zone* zone_;
JS::Zone* zone_ = nullptr;
// Word storing JSScript pointer and site state.
//
@ -76,7 +80,11 @@ class AllocSite {
uint32_t nurseryTenuredCount : 24;
// Number of times the script has been invalidated.
uint32_t invalidationCount : 8;
uint32_t invalidationCount : 4;
// The trace kind of the allocation. Only kinds up to NurseryTraceKinds are
// allowed.
uint32_t traceKind_ : 4;
static AllocSite* const EndSentinel;
@ -89,27 +97,47 @@ class AllocSite {
uintptr_t rawScript() const { return scriptAndState & ~STATE_MASK; }
public:
AllocSite() : nurseryTenuredCount(0), invalidationCount(0), traceKind_(0) {}
// Create a dummy site to use for unknown allocations.
explicit AllocSite(JS::Zone* zone)
: zone_(zone), nurseryTenuredCount(0), invalidationCount(0) {}
explicit AllocSite(JS::Zone* zone, JS::TraceKind kind)
: zone_(zone),
nurseryTenuredCount(0),
invalidationCount(0),
traceKind_(uint32_t(kind)) {
MOZ_ASSERT(traceKind_ < NurseryTraceKinds);
}
// Create a site for an opcode in the given script.
AllocSite(JS::Zone* zone, JSScript* script) : AllocSite(zone) {
AllocSite(JS::Zone* zone, JSScript* script, JS::TraceKind kind)
: AllocSite(zone, kind) {
MOZ_ASSERT(script != WasmScript);
setScript(script);
}
void initUnknownSite(JS::Zone* zone, JS::TraceKind kind) {
MOZ_ASSERT(!zone_ && scriptAndState == uintptr_t(State::Unknown));
zone_ = zone;
nurseryTenuredCount = 0;
invalidationCount = 0;
traceKind_ = uint32_t(kind);
MOZ_ASSERT(traceKind_ < NurseryTraceKinds);
}
// Initialize a site to be a wasm site.
void initWasm(JS::Zone* zone) {
MOZ_ASSERT(!zone_ && scriptAndState == uintptr_t(State::Unknown));
zone_ = zone;
setScript(WasmScript);
nurseryTenuredCount = 0;
invalidationCount = 0;
setScript(WasmScript);
traceKind_ = uint32_t(JS::TraceKind::Object);
}
JS::Zone* zone() const { return zone_; }
JS::TraceKind traceKind() const { return JS::TraceKind(traceKind_); }
State state() const { return State(scriptAndState & STATE_MASK); }
// Whether this site has a script associated with it. This is not true if
@ -120,7 +148,7 @@ class AllocSite {
return reinterpret_cast<JSScript*>(rawScript());
}
// Whether this site is not the unknown or optimized site.
// Whether this site is not an unknown or optimized site.
bool isNormal() const { return rawScript() != 0; }
enum class Kind : uint32_t { Normal, Unknown, Optimized };
@ -143,6 +171,7 @@ class AllocSite {
}
uint32_t incAllocCount() { return ++nurseryAllocCount; }
uint32_t* nurseryAllocCountAddress() { return &nurseryAllocCount; }
void incTenuredCount() {
// The nursery is not large enough for this to overflow.
@ -198,13 +227,10 @@ class AllocSite {
// Pretenuring information stored per zone.
class PretenuringZone {
public:
explicit PretenuringZone(JS::Zone* zone)
: unknownAllocSite(zone), optimizedAllocSite(zone) {}
// Catch-all allocation site instance used when the actual site is unknown, or
// when optimized JIT code allocates a GC thing that's not handled by the
// pretenuring system.
AllocSite unknownAllocSite;
AllocSite unknownAllocSites[NurseryTraceKinds];
// Catch-all allocation instance used by optimized JIT code when allocating GC
// things that are handled by the pretenuring system. Allocation counts are
@ -226,6 +252,23 @@ class PretenuringZone {
// get the pretenuring decision wrong.
uint32_t highNurserySurvivalCount = 0;
// Total allocation count by trace kind (ignoring optimized
// allocations). Calculated during nursery collection.
uint32_t nurseryAllocCounts[NurseryTraceKinds] = {0};
explicit PretenuringZone(JS::Zone* zone)
: optimizedAllocSite(zone, JS::TraceKind::Object) {
for (uint32_t i = 0; i < NurseryTraceKinds; i++) {
unknownAllocSites[i].initUnknownSite(zone, JS::TraceKind(i));
}
}
AllocSite& unknownAllocSite(JS::TraceKind kind) {
size_t i = size_t(kind);
MOZ_ASSERT(i < NurseryTraceKinds);
return unknownAllocSites[i];
}
void clearCellCountsInNewlyCreatedArenas() {
allocCountInNewlyCreatedArenas = 0;
survivorCountInNewlyCreatedArenas = 0;
@ -245,6 +288,15 @@ class PretenuringZone {
// state and invalidate JIT code.
bool shouldResetNurseryAllocSites();
bool shouldResetPretenuredAllocSites();
uint32_t& nurseryAllocCount(JS::TraceKind kind) {
size_t i = size_t(kind);
MOZ_ASSERT(i < NurseryTraceKinds);
return nurseryAllocCounts[i];
}
uint32_t nurseryAllocCount(JS::TraceKind kind) const {
return const_cast<PretenuringZone*>(this)->nurseryAllocCount(kind);
}
};
// Pretenuring information stored as part of the the GC nursery.
@ -253,6 +305,8 @@ class PretenuringNursery {
size_t allocSitesCreated = 0;
uint32_t totalAllocCount_ = 0;
public:
PretenuringNursery() : allocatedSites(AllocSite::EndSentinel) {}
@ -275,11 +329,19 @@ class PretenuringNursery {
void maybeStopPretenuring(GCRuntime* gc);
uint32_t totalAllocCount() const { return totalAllocCount_; }
void* addressOfAllocatedSites() { return &allocatedSites; }
private:
void reportAndResetCatchAllSite(AllocSite* site, bool reportInfo,
size_t reportThreshold);
class MaybeGCSession;
void processSite(GCRuntime* gc, AllocSite* site, size_t& sitesActive,
size_t& sitesPretenured, size_t& sitesInvalidated,
MaybeGCSession& session, bool reportInfo,
size_t reportThreshold);
void processCatchAllSite(AllocSite* site, bool reportInfo,
size_t reportThreshold);
void updateAllocCounts(AllocSite* site);
};
} // namespace gc

View File

@ -763,7 +763,7 @@ Statistics::Statistics(GCRuntime* gc)
gcDebugFile(nullptr),
nonincrementalReason_(GCAbortReason::None),
creationTime_(TimeStamp::Now()),
allocsSinceMinorGC({0, 0}),
tenuredAllocsSinceMinorGC(0),
preTotalHeapBytes(0),
postTotalHeapBytes(0),
preCollectedHeapBytes(0),
@ -1151,7 +1151,7 @@ void Statistics::endNurseryCollection(JS::GCReason reason) {
context(), JS::GCNurseryProgress::GC_NURSERY_COLLECTION_END, reason);
}
allocsSinceMinorGC = {0, 0};
tenuredAllocsSinceMinorGC = 0;
}
Statistics::SliceData::SliceData(const SliceBudget& budget,

View File

@ -234,20 +234,12 @@ struct Statistics {
}
bool hasTrigger() const { return recordedTrigger.isSome(); }
void noteNurseryAlloc() { allocsSinceMinorGC.nursery++; }
// tenured allocs don't include nursery evictions.
void setAllocsSinceMinorGCTenured(uint32_t allocs) {
allocsSinceMinorGC.tenured = allocs;
tenuredAllocsSinceMinorGC = allocs;
}
uint32_t allocsSinceMinorGCNursery() { return allocsSinceMinorGC.nursery; }
uint32_t allocsSinceMinorGCTenured() { return allocsSinceMinorGC.tenured; }
uint32_t* addressOfAllocsSinceMinorGCNursery() {
return &allocsSinceMinorGC.nursery;
}
uint32_t allocsSinceMinorGCTenured() { return tenuredAllocsSinceMinorGC; }
void beginNurseryCollection(JS::GCReason reason);
void endNurseryCollection(JS::GCReason reason);
@ -398,10 +390,7 @@ struct Statistics {
* These events cannot be kept in the above array, we need to take their
* address.
*/
struct {
uint32_t nursery;
uint32_t tenured;
} allocsSinceMinorGC;
uint32_t tenuredAllocsSinceMinorGC;
/* Total GC heap size before and after the GC ran. */
size_t preTotalHeapBytes;

View File

@ -159,7 +159,6 @@ JS::Zone::Zone(JSRuntime* rt, Kind kind)
arenas(this),
data(nullptr),
tenuredBigInts(0),
nurseryAllocatedStrings(0),
markedStrings(0),
finalizedStrings(0),
allocNurseryStrings(true),

View File

@ -23,6 +23,7 @@
#include "gc/FindSCCs.h"
#include "gc/GCMarker.h"
#include "gc/NurseryAwareHashMap.h"
#include "gc/Pretenuring.h"
#include "gc/Statistics.h"
#include "gc/ZoneAllocator.h"
#include "js/GCHashTable.h"
@ -172,8 +173,6 @@ class Zone : public js::ZoneAllocator, public js::gc::GraphNodeBase<JS::Zone> {
js::MainThreadData<uint32_t> tenuredBigInts;
js::MainThreadOrIonCompileData<uint64_t> nurseryAllocatedStrings;
// Number of marked/finalized JSStrings/JSFatInlineStrings during major GC.
js::MainThreadOrGCTaskData<size_t> markedStrings;
js::MainThreadOrGCTaskData<size_t> finalizedStrings;
@ -584,12 +583,15 @@ class Zone : public js::ZoneAllocator, public js::gc::GraphNodeBase<JS::Zone> {
// See: https://tc39.es/proposal-weakrefs/#sec-clear-kept-objects
void clearKeptObjects();
js::gc::AllocSite* unknownAllocSite() {
return &pretenuring.unknownAllocSite;
js::gc::AllocSite* unknownAllocSite(JS::TraceKind kind) {
return &pretenuring.unknownAllocSite(kind);
}
js::gc::AllocSite* optimizedAllocSite() {
return &pretenuring.optimizedAllocSite;
}
uint32_t nurseryAllocCount(JS::TraceKind kind) const {
return pretenuring.nurseryAllocCount(kind);
}
#ifdef JSGC_HASH_TABLE_CHECKS
void checkAllCrossCompartmentWrappersAfterMovingGC();

View File

@ -9560,7 +9560,8 @@ AttachDecision InlinableNativeIRGenerator::tryAttachObjectConstructor() {
if (argc_ == 0) {
// TODO: Support pre-tenuring.
gc::AllocSite* site = script()->zone()->unknownAllocSite();
gc::AllocSite* site =
script()->zone()->unknownAllocSite(JS::TraceKind::Object);
MOZ_ASSERT(site);
uint32_t numFixedSlots = templateObj->numUsedFixedSlots();
@ -12740,7 +12741,7 @@ static gc::AllocSite* MaybeCreateAllocSite(jsbytecode* pc,
bool isInlined = frame->icScript()->isInlined();
if (inInterpreter && !isInlined) {
return outerScript->zone()->unknownAllocSite();
return outerScript->zone()->unknownAllocSite(JS::TraceKind::Object);
}
return outerScript->createAllocSite();

View File

@ -166,10 +166,6 @@ const void* CompileZone::addressOfBigIntNurseryCurrentEnd() {
return zone()->runtimeFromAnyThread()->gc.addressOfBigIntNurseryCurrentEnd();
}
uint32_t* CompileZone::addressOfNurseryAllocCount() {
return zone()->runtimeFromAnyThread()->gc.addressOfNurseryAllocCount();
}
void* CompileZone::addressOfNurseryAllocatedSites() {
JSRuntime* rt = zone()->runtimeFromAnyThread();
return rt->gc.nursery().addressOfNurseryAllocatedSites();
@ -185,12 +181,12 @@ bool CompileZone::canNurseryAllocateBigInts() {
zone()->allocNurseryBigInts;
}
uintptr_t CompileZone::nurseryCellHeader(JS::TraceKind traceKind,
gc::CatchAllAllocSite siteKind) {
gc::AllocSite* site = siteKind == gc::CatchAllAllocSite::Optimized
? zone()->optimizedAllocSite()
: zone()->unknownAllocSite();
return gc::NurseryCellHeader::MakeValue(site, traceKind);
gc::AllocSite* CompileZone::catchAllAllocSite(JS::TraceKind traceKind,
gc::CatchAllAllocSite siteKind) {
if (siteKind == gc::CatchAllAllocSite::Optimized) {
return zone()->optimizedAllocSite();
}
return zone()->unknownAllocSite(traceKind);
}
JS::Realm* CompileRealm::realm() { return reinterpret_cast<JS::Realm*>(this); }

View File

@ -114,15 +114,13 @@ class CompileZone {
const void* addressOfStringNurseryCurrentEnd();
const void* addressOfBigIntNurseryCurrentEnd();
uint32_t* addressOfNurseryAllocCount();
void* addressOfNurseryAllocatedSites();
bool canNurseryAllocateStrings();
bool canNurseryAllocateBigInts();
uintptr_t nurseryCellHeader(JS::TraceKind traceKind,
gc::CatchAllAllocSite siteKind);
gc::AllocSite* catchAllAllocSite(JS::TraceKind traceKind,
gc::CatchAllAllocSite siteKind);
};
class JitRealm;

View File

@ -637,7 +637,7 @@ gc::AllocSite* JitScript::createAllocSite(JSScript* script) {
if (!nursery.canCreateAllocSite()) {
// Don't block attaching an optimized stub, but don't process allocations
// for this site.
return script->zone()->unknownAllocSite();
return script->zone()->unknownAllocSite(JS::TraceKind::Object);
}
if (!allocSites_.reserve(allocSites_.length() + 1)) {
@ -651,7 +651,7 @@ gc::AllocSite* JitScript::createAllocSite(JSScript* script) {
return nullptr;
}
new (site) gc::AllocSite(script->zone(), script);
new (site) gc::AllocSite(script->zone(), script, JS::TraceKind::Object);
allocSites_.infallibleAppend(site);

View File

@ -535,10 +535,7 @@ void MacroAssembler::nurseryAllocateString(Register result, Register temp,
// with the nursery's end will always fail in such cases.
CompileZone* zone = realm()->zone();
uint64_t* allocStrsPtr = &zone->zone()->nurseryAllocatedStrings.ref();
inc64(AbsoluteAddress(allocStrsPtr));
size_t thingSize = gc::Arena::thingSize(allocKind);
bumpPointerAllocate(result, temp, fail, zone,
zone->addressOfStringNurseryPosition(),
zone->addressOfStringNurseryCurrentEnd(),
@ -592,26 +589,30 @@ void MacroAssembler::bumpPointerAllocate(Register result, Register temp,
storePtr(result, Address(temp, 0));
subPtr(Imm32(size), result);
if (runtime()->geckoProfiler().enabled()) {
uint32_t* countAddress = zone->addressOfNurseryAllocCount();
CheckedInt<int32_t> counterOffset =
(CheckedInt<uintptr_t>(uintptr_t(countAddress)) -
CheckedInt<uintptr_t>(uintptr_t(posAddr)))
.toChecked<int32_t>();
if (counterOffset.isValid()) {
add32(Imm32(1), Address(temp, counterOffset.value()));
} else {
movePtr(ImmPtr(countAddress), temp);
add32(Imm32(1), Address(temp, 0));
}
}
if (allocSite.is<gc::CatchAllAllocSite>()) {
// No allocation site supplied. This is the case when called from Warp, or
// from places that don't support pretenuring.
gc::CatchAllAllocSite siteKind = allocSite.as<gc::CatchAllAllocSite>();
storePtr(ImmWord(zone->nurseryCellHeader(traceKind, siteKind)),
gc::AllocSite* site = zone->catchAllAllocSite(traceKind, siteKind);
uintptr_t headerWord = gc::NurseryCellHeader::MakeValue(site, traceKind);
storePtr(ImmWord(headerWord),
Address(result, -js::Nursery::nurseryCellHeaderSize()));
// Only update the catch all allocation site if the profiler is
// enabled. This is used to calculate the nursery allocation count.
if (runtime()->geckoProfiler().enabled()) {
uint32_t* countAddress = site->nurseryAllocCountAddress();
CheckedInt<int32_t> counterOffset =
(CheckedInt<uintptr_t>(uintptr_t(countAddress)) -
CheckedInt<uintptr_t>(uintptr_t(posAddr)))
.toChecked<int32_t>();
if (counterOffset.isValid()) {
add32(Imm32(1), Address(temp, counterOffset.value()));
} else {
movePtr(ImmPtr(countAddress), temp);
add32(Imm32(1), Address(temp, 0));
}
}
} else {
// Update allocation site and store pointer in the nursery cell header. This
// is only used from baseline.

View File

@ -52,7 +52,6 @@ struct TypeDefInstanceData {
superTypeVector(nullptr),
shape(nullptr),
clasp(nullptr),
allocSite(nullptr),
allocKind(gc::AllocKind::LIMIT) {}
// The canonicalized pointer to this type definition. This is kept alive by