mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-25 03:05:34 +00:00
Bug 1259850 - Make ZoneCellIter variants to communicate nogc to the hazard analysis, r=jonco
Also create accessors on gc::Zone for a nicer API to ZoneCellIters. MozReview-Commit-ID: AxRTTUfWrND --HG-- extra : rebase_source : cf32560db5a123bdc64178a7567ed36e1ecb5b2b
This commit is contained in:
parent
69331f349a
commit
96d0fa6dcf
@ -95,21 +95,19 @@ js::IterateScripts(JSRuntime* rt, JSCompartment* compartment,
|
||||
void* data, IterateScriptCallback scriptCallback)
|
||||
{
|
||||
MOZ_ASSERT(!rt->mainThread.suppressGC);
|
||||
rt->gc.evictNursery();
|
||||
MOZ_ASSERT(rt->gc.nursery.isEmpty());
|
||||
|
||||
AutoEmptyNursery empty(rt);
|
||||
AutoPrepareForTracing prep(rt, SkipAtoms);
|
||||
|
||||
if (compartment) {
|
||||
for (ZoneCellIterUnderGC i(compartment->zone(), gc::AllocKind::SCRIPT); !i.done(); i.next()) {
|
||||
JSScript* script = i.get<JSScript>();
|
||||
Zone* zone = compartment->zone();
|
||||
for (auto script = zone->cellIter<JSScript>(empty); !script.done(); script.next()) {
|
||||
if (script->compartment() == compartment)
|
||||
scriptCallback(rt, data, script);
|
||||
}
|
||||
} else {
|
||||
for (ZonesIter zone(rt, SkipAtoms); !zone.done(); zone.next()) {
|
||||
for (ZoneCellIterUnderGC i(zone, gc::AllocKind::SCRIPT); !i.done(); i.next())
|
||||
scriptCallback(rt, data, i.get<JSScript>());
|
||||
for (auto script = zone->cellIter<JSScript>(empty); !script.done(); script.next())
|
||||
scriptCallback(rt, data, script);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -117,14 +115,14 @@ js::IterateScripts(JSRuntime* rt, JSCompartment* compartment,
|
||||
void
|
||||
js::IterateGrayObjects(Zone* zone, GCThingCallback cellCallback, void* data)
|
||||
{
|
||||
zone->runtimeFromMainThread()->gc.evictNursery();
|
||||
AutoPrepareForTracing prep(zone->runtimeFromMainThread(), SkipAtoms);
|
||||
JSRuntime* rt = zone->runtimeFromMainThread();
|
||||
AutoEmptyNursery empty(rt);
|
||||
AutoPrepareForTracing prep(rt, SkipAtoms);
|
||||
|
||||
for (auto thingKind : ObjectAllocKinds()) {
|
||||
for (ZoneCellIterUnderGC i(zone, thingKind); !i.done(); i.next()) {
|
||||
JSObject* obj = i.get<JSObject>();
|
||||
for (auto obj = zone->cellIter<JSObject>(thingKind, empty); !obj.done(); obj.next()) {
|
||||
if (obj->asTenured().isMarked(GRAY))
|
||||
cellCallback(data, JS::GCCellPtr(obj));
|
||||
cellCallback(data, JS::GCCellPtr(obj.get()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -761,7 +761,7 @@ Statistics::Statistics(JSRuntime* rt)
|
||||
maxPauseInInterval(0),
|
||||
phaseNestingDepth(0),
|
||||
activeDagSlot(PHASE_DAG_NONE),
|
||||
suspendedPhaseNestingDepth(0),
|
||||
suspended(0),
|
||||
sliceCallback(nullptr),
|
||||
nurseryCollectionCallback(nullptr),
|
||||
aborted(false)
|
||||
@ -1069,7 +1069,7 @@ Statistics::startTimingMutator()
|
||||
return false;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(suspendedPhaseNestingDepth == 0);
|
||||
MOZ_ASSERT(suspended == 0);
|
||||
|
||||
timedGCTime = 0;
|
||||
phaseStartTimes[PHASE_MUTATOR] = 0;
|
||||
@ -1094,6 +1094,35 @@ Statistics::stopTimingMutator(double& mutator_ms, double& gc_ms)
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
Statistics::suspendPhases(Phase suspension)
|
||||
{
|
||||
MOZ_ASSERT(suspension == PHASE_EXPLICIT_SUSPENSION || suspension == PHASE_IMPLICIT_SUSPENSION);
|
||||
while (phaseNestingDepth) {
|
||||
MOZ_ASSERT(suspended < mozilla::ArrayLength(suspendedPhases));
|
||||
Phase parent = phaseNesting[phaseNestingDepth - 1];
|
||||
suspendedPhases[suspended++] = parent;
|
||||
recordPhaseEnd(parent);
|
||||
}
|
||||
suspendedPhases[suspended++] = suspension;
|
||||
}
|
||||
|
||||
void
|
||||
Statistics::resumePhases()
|
||||
{
|
||||
Phase popped = suspendedPhases[--suspended];
|
||||
MOZ_ASSERT(popped == PHASE_EXPLICIT_SUSPENSION || popped == PHASE_IMPLICIT_SUSPENSION);
|
||||
while (suspended &&
|
||||
suspendedPhases[suspended - 1] != PHASE_EXPLICIT_SUSPENSION &&
|
||||
suspendedPhases[suspended - 1] != PHASE_IMPLICIT_SUSPENSION)
|
||||
{
|
||||
Phase resumePhase = suspendedPhases[--suspended];
|
||||
if (resumePhase == PHASE_MUTATOR)
|
||||
timedGCTime += PRMJ_Now() - timedGCStart;
|
||||
beginPhase(resumePhase);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Statistics::beginPhase(Phase phase)
|
||||
{
|
||||
@ -1105,9 +1134,7 @@ Statistics::beginPhase(Phase phase)
|
||||
//
|
||||
// Reuse this mechanism for managing PHASE_MUTATOR.
|
||||
if (parent == PHASE_GC_BEGIN || parent == PHASE_GC_END || parent == PHASE_MUTATOR) {
|
||||
MOZ_ASSERT(suspendedPhaseNestingDepth < mozilla::ArrayLength(suspendedPhases));
|
||||
suspendedPhases[suspendedPhaseNestingDepth++] = parent;
|
||||
recordPhaseEnd(parent);
|
||||
suspendPhases(PHASE_IMPLICIT_SUSPENSION);
|
||||
parent = phaseNestingDepth ? phaseNesting[phaseNestingDepth - 1] : PHASE_NO_PARENT;
|
||||
}
|
||||
|
||||
@ -1154,12 +1181,8 @@ Statistics::endPhase(Phase phase)
|
||||
|
||||
// When emptying the stack, we may need to resume a callback phase
|
||||
// (PHASE_GC_BEGIN/END) or return to timing the mutator (PHASE_MUTATOR).
|
||||
if (phaseNestingDepth == 0 && suspendedPhaseNestingDepth > 0) {
|
||||
Phase resumePhase = suspendedPhases[--suspendedPhaseNestingDepth];
|
||||
if (resumePhase == PHASE_MUTATOR)
|
||||
timedGCTime += PRMJ_Now() - timedGCStart;
|
||||
beginPhase(resumePhase);
|
||||
}
|
||||
if (phaseNestingDepth == 0 && suspended > 0 && suspendedPhases[suspended - 1] == PHASE_IMPLICIT_SUSPENSION)
|
||||
resumePhases();
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -86,6 +86,8 @@ enum Phase : uint8_t {
|
||||
|
||||
PHASE_LIMIT,
|
||||
PHASE_NONE = PHASE_LIMIT,
|
||||
PHASE_EXPLICIT_SUSPENSION = PHASE_LIMIT,
|
||||
PHASE_IMPLICIT_SUSPENSION,
|
||||
PHASE_MULTI_PARENTS
|
||||
};
|
||||
|
||||
@ -169,6 +171,22 @@ struct Statistics
|
||||
void endPhase(Phase phase);
|
||||
void endParallelPhase(Phase phase, const GCParallelTask* task);
|
||||
|
||||
// Occasionally, we may be in the middle of something that is tracked by
|
||||
// this class, and we need to do something unusual (eg evict the nursery)
|
||||
// that doesn't normally nest within the current phase. Suspend the
|
||||
// currently tracked phase stack, at which time the caller is free to do
|
||||
// other tracked operations.
|
||||
//
|
||||
// This also happens internally with PHASE_GC_BEGIN and other "non-GC"
|
||||
// phases. While in these phases, any beginPhase will automatically suspend
|
||||
// the non-GC phase, until that inner stack is complete, at which time it
|
||||
// will automatically resume the non-GC phase. Explicit suspensions do not
|
||||
// get auto-resumed.
|
||||
void suspendPhases(Phase suspension = PHASE_EXPLICIT_SUSPENSION);
|
||||
|
||||
// Resume a suspended stack of phases.
|
||||
void resumePhases();
|
||||
|
||||
void beginSlice(const ZoneGCStats& zoneStats, JSGCInvocationKind gckind,
|
||||
SliceBudget budget, JS::gcreason::Reason reason);
|
||||
void endSlice();
|
||||
@ -318,13 +336,14 @@ struct Statistics
|
||||
size_t activeDagSlot;
|
||||
|
||||
/*
|
||||
* To avoid recursive nesting, we discontinue a callback phase when any
|
||||
* other phases are started. Remember what phase to resume when the inner
|
||||
* phases are complete. (And because GCs can nest within the callbacks any
|
||||
* number of times, we need a whole stack of of phases to resume.)
|
||||
* Certain phases can interrupt the phase stack, eg callback phases. When
|
||||
* this happens, we move the suspended phases over to a sepearate list,
|
||||
* terminated by a dummy PHASE_SUSPENSION phase (so that we can nest
|
||||
* suspensions by suspending multiple stacks with a PHASE_SUSPENSION in
|
||||
* between).
|
||||
*/
|
||||
Phase suspendedPhases[MAX_NESTING];
|
||||
size_t suspendedPhaseNestingDepth;
|
||||
Phase suspendedPhases[MAX_NESTING * 3];
|
||||
size_t suspended;
|
||||
|
||||
/* Sweep times for SCCs of compartments. */
|
||||
Vector<int64_t, 0, SystemAllocPolicy> sccTimes;
|
||||
|
@ -153,13 +153,13 @@ Zone::sweepBreakpoints(FreeOp* fop)
|
||||
*/
|
||||
|
||||
MOZ_ASSERT(isGCSweepingOrCompacting());
|
||||
for (ZoneCellIterUnderGC i(this, AllocKind::SCRIPT); !i.done(); i.next()) {
|
||||
JSScript* script = i.get<JSScript>();
|
||||
for (auto iter = cellIter<JSScript>(); !iter.done(); iter.next()) {
|
||||
JSScript* script = iter;
|
||||
if (!script->hasAnyBreakpointsOrStepMode())
|
||||
continue;
|
||||
|
||||
bool scriptGone = IsAboutToBeFinalizedUnbarriered(&script);
|
||||
MOZ_ASSERT(script == i.get<JSScript>());
|
||||
MOZ_ASSERT(script == iter);
|
||||
for (unsigned i = 0; i < script->length(); i++) {
|
||||
BreakpointSite* site = script->getBreakpointSite(script->offsetToPC(i));
|
||||
if (!site)
|
||||
@ -207,10 +207,8 @@ Zone::discardJitCode(FreeOp* fop)
|
||||
|
||||
#ifdef DEBUG
|
||||
/* Assert no baseline scripts are marked as active. */
|
||||
for (ZoneCellIter i(this, AllocKind::SCRIPT); !i.done(); i.next()) {
|
||||
JSScript* script = i.get<JSScript>();
|
||||
for (auto script = cellIter<JSScript>(); !script.done(); script.next())
|
||||
MOZ_ASSERT_IF(script->hasBaselineScript(), !script->baselineScript()->active());
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Mark baseline scripts on the stack as active. */
|
||||
@ -219,8 +217,7 @@ Zone::discardJitCode(FreeOp* fop)
|
||||
/* Only mark OSI points if code is being discarded. */
|
||||
jit::InvalidateAll(fop, this);
|
||||
|
||||
for (ZoneCellIter i(this, AllocKind::SCRIPT); !i.done(); i.next()) {
|
||||
JSScript* script = i.get<JSScript>();
|
||||
for (auto script = cellIter<JSScript>(); !script.done(); script.next()) {
|
||||
jit::FinishInvalidation(fop, script);
|
||||
|
||||
/*
|
||||
|
@ -84,6 +84,9 @@ using UniqueIdMap = GCHashMap<Cell*,
|
||||
|
||||
extern uint64_t NextCellUniqueId(JSRuntime* rt);
|
||||
|
||||
template <typename T>
|
||||
class ZoneCellIter;
|
||||
|
||||
} // namespace gc
|
||||
} // namespace js
|
||||
|
||||
@ -157,6 +160,13 @@ struct Zone : public JS::shadow::Zone,
|
||||
onTooMuchMalloc();
|
||||
}
|
||||
|
||||
// Iterate over all cells in the zone. See the definition of ZoneCellIter
|
||||
// in jsgcinlines.h for the possible arguments and documentation.
|
||||
template <typename T, typename... Args>
|
||||
js::gc::ZoneCellIter<T> cellIter(Args... args) {
|
||||
return js::gc::ZoneCellIter<T>(const_cast<Zone*>(this), mozilla::Forward<Args>(args)...);
|
||||
}
|
||||
|
||||
bool isTooMuchMalloc() const { return gcMallocBytes <= 0; }
|
||||
void onTooMuchMalloc();
|
||||
|
||||
|
@ -488,6 +488,7 @@ BaselineScript::Trace(JSTracer* trc, BaselineScript* script)
|
||||
void
|
||||
BaselineScript::Destroy(FreeOp* fop, BaselineScript* script)
|
||||
{
|
||||
|
||||
MOZ_ASSERT(!script->hasPendingIonBuilder());
|
||||
|
||||
script->unlinkDependentWasmModules(fop);
|
||||
@ -1171,8 +1172,7 @@ jit::ToggleBaselineProfiling(JSRuntime* runtime, bool enable)
|
||||
return;
|
||||
|
||||
for (ZonesIter zone(runtime, SkipAtoms); !zone.done(); zone.next()) {
|
||||
for (gc::ZoneCellIter i(zone, gc::AllocKind::SCRIPT); !i.done(); i.next()) {
|
||||
JSScript* script = i.get<JSScript>();
|
||||
for (auto script = zone->cellIter<JSScript>(); !script.done(); script.next()) {
|
||||
if (!script->hasBaselineScript())
|
||||
continue;
|
||||
AutoWritableJitCode awjc(script->baselineScript()->method());
|
||||
@ -1186,8 +1186,7 @@ void
|
||||
jit::ToggleBaselineTraceLoggerScripts(JSRuntime* runtime, bool enable)
|
||||
{
|
||||
for (ZonesIter zone(runtime, SkipAtoms); !zone.done(); zone.next()) {
|
||||
for (gc::ZoneCellIter i(zone, gc::AllocKind::SCRIPT); !i.done(); i.next()) {
|
||||
JSScript* script = i.get<JSScript>();
|
||||
for (auto script = zone->cellIter<JSScript>(); !script.done(); script.next()) {
|
||||
if (!script->hasBaselineScript())
|
||||
continue;
|
||||
script->baselineScript()->toggleTraceLoggerScripts(runtime, script, enable);
|
||||
@ -1199,8 +1198,7 @@ void
|
||||
jit::ToggleBaselineTraceLoggerEngine(JSRuntime* runtime, bool enable)
|
||||
{
|
||||
for (ZonesIter zone(runtime, SkipAtoms); !zone.done(); zone.next()) {
|
||||
for (gc::ZoneCellIter i(zone, gc::AllocKind::SCRIPT); !i.done(); i.next()) {
|
||||
JSScript* script = i.get<JSScript>();
|
||||
for (auto script = zone->cellIter<JSScript>(); !script.done(); script.next()) {
|
||||
if (!script->hasBaselineScript())
|
||||
continue;
|
||||
script->baselineScript()->toggleTraceLoggerEngine(enable);
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include "mozilla/ThreadLocal.h"
|
||||
|
||||
#include "jscompartment.h"
|
||||
#include "jsgc.h"
|
||||
#include "jsprf.h"
|
||||
|
||||
#include "gc/Marking.h"
|
||||
@ -589,8 +590,8 @@ JitRuntime::Mark(JSTracer* trc, AutoLockForExclusiveAccess& lock)
|
||||
{
|
||||
MOZ_ASSERT(!trc->runtime()->isHeapMinorCollecting());
|
||||
Zone* zone = trc->runtime()->atomsCompartment(lock)->zone();
|
||||
for (gc::ZoneCellIterUnderGC i(zone, gc::AllocKind::JITCODE); !i.done(); i.next()) {
|
||||
JitCode* code = i.get<JitCode>();
|
||||
for (auto i = zone->cellIter<JitCode>(); !i.done(); i.next()) {
|
||||
JitCode* code = i;
|
||||
TraceRoot(trc, &code, "wrapper");
|
||||
}
|
||||
}
|
||||
@ -1351,8 +1352,7 @@ jit::ToggleBarriers(JS::Zone* zone, bool needs)
|
||||
if (!rt->hasJitRuntime())
|
||||
return;
|
||||
|
||||
for (gc::ZoneCellIterUnderGC i(zone, gc::AllocKind::SCRIPT); !i.done(); i.next()) {
|
||||
JSScript* script = i.get<JSScript>();
|
||||
for (auto script = zone->cellIter<JSScript>(); !script.done(); script.next()) {
|
||||
if (script->hasIonScript())
|
||||
script->ionScript()->toggleBarriers(needs);
|
||||
if (script->hasBaselineScript())
|
||||
|
@ -984,8 +984,8 @@ AddLazyFunctionsForCompartment(JSContext* cx, AutoObjectVector& lazyFunctions, A
|
||||
// want to delazify in that case: this pointer is weak so the JSScript
|
||||
// could be destroyed at the next GC.
|
||||
|
||||
for (gc::ZoneCellIter i(cx->zone(), kind); !i.done(); i.next()) {
|
||||
JSFunction* fun = &i.get<JSObject>()->as<JSFunction>();
|
||||
for (auto i = cx->zone()->cellIter<JSObject>(kind); !i.done(); i.next()) {
|
||||
JSFunction* fun = &i->as<JSFunction>();
|
||||
|
||||
// Sweeping is incremental; take care to not delazify functions that
|
||||
// are about to be finalized. GC things referenced by objects that are
|
||||
@ -1157,8 +1157,7 @@ JSCompartment::clearScriptCounts()
|
||||
void
|
||||
JSCompartment::clearBreakpointsIn(FreeOp* fop, js::Debugger* dbg, HandleObject handler)
|
||||
{
|
||||
for (gc::ZoneCellIter i(zone(), gc::AllocKind::SCRIPT); !i.done(); i.next()) {
|
||||
JSScript* script = i.get<JSScript>();
|
||||
for (auto script = zone()->cellIter<JSScript>(); !script.done(); script.next()) {
|
||||
if (script->compartment() == this && script->hasAnyBreakpointsOrStepMode())
|
||||
script->clearBreakpointsIn(fop, dbg, handler);
|
||||
}
|
||||
|
@ -2381,15 +2381,10 @@ GCRuntime::sweepTypesAfterCompacting(Zone* zone)
|
||||
|
||||
AutoClearTypeInferenceStateOnOOM oom(zone);
|
||||
|
||||
for (ZoneCellIterUnderGC i(zone, AllocKind::SCRIPT); !i.done(); i.next()) {
|
||||
JSScript* script = i.get<JSScript>();
|
||||
for (auto script = zone->cellIter<JSScript>(); !script.done(); script.next())
|
||||
script->maybeSweepTypes(&oom);
|
||||
}
|
||||
|
||||
for (ZoneCellIterUnderGC i(zone, AllocKind::OBJECT_GROUP); !i.done(); i.next()) {
|
||||
ObjectGroup* group = i.get<ObjectGroup>();
|
||||
for (auto group = zone->cellIter<ObjectGroup>(); !group.done(); group.next())
|
||||
group->maybeSweep(&oom);
|
||||
}
|
||||
|
||||
zone->types.endSweep(rt);
|
||||
}
|
||||
@ -3981,10 +3976,11 @@ GCRuntime::checkForCompartmentMismatches()
|
||||
return;
|
||||
|
||||
CompartmentCheckTracer trc(rt);
|
||||
AutoAssertEmptyNursery empty(rt);
|
||||
for (ZonesIter zone(rt, SkipAtoms); !zone.done(); zone.next()) {
|
||||
trc.zone = zone;
|
||||
for (auto thingKind : AllAllocKinds()) {
|
||||
for (ZoneCellIterUnderGC i(zone, thingKind); !i.done(); i.next()) {
|
||||
for (auto i = zone->cellIter<TenuredCell>(thingKind, empty); !i.done(); i.next()) {
|
||||
trc.src = i.getCell();
|
||||
trc.srcKind = MapAllocToTraceKind(thingKind);
|
||||
trc.compartment = DispatchTraceKindTyped(MaybeCompartmentFunctor(),
|
||||
@ -4003,9 +3999,10 @@ RelazifyFunctions(Zone* zone, AllocKind kind)
|
||||
kind == AllocKind::FUNCTION_EXTENDED);
|
||||
|
||||
JSRuntime* rt = zone->runtimeFromMainThread();
|
||||
AutoAssertEmptyNursery empty(rt);
|
||||
|
||||
for (ZoneCellIterUnderGC i(zone, kind); !i.done(); i.next()) {
|
||||
JSFunction* fun = &i.get<JSObject>()->as<JSFunction>();
|
||||
for (auto i = zone->cellIter<JSObject>(kind, empty); !i.done(); i.next()) {
|
||||
JSFunction* fun = &i->as<JSFunction>();
|
||||
if (fun->hasScript())
|
||||
fun->maybeRelazify(rt);
|
||||
}
|
||||
@ -6919,8 +6916,7 @@ gc::MergeCompartments(JSCompartment* source, JSCompartment* target)
|
||||
RootedObject targetStaticGlobalLexicalScope(rt);
|
||||
targetStaticGlobalLexicalScope = &target->maybeGlobal()->lexicalScope().staticBlock();
|
||||
|
||||
for (ZoneCellIter iter(source->zone(), AllocKind::SCRIPT); !iter.done(); iter.next()) {
|
||||
JSScript* script = iter.get<JSScript>();
|
||||
for (auto script = source->zone()->cellIter<JSScript>(); !script.done(); script.next()) {
|
||||
MOZ_ASSERT(script->compartment() == source);
|
||||
script->compartment_ = target;
|
||||
script->setTypesGeneration(target->zone()->types.generation);
|
||||
@ -6952,14 +6948,12 @@ gc::MergeCompartments(JSCompartment* source, JSCompartment* target)
|
||||
}
|
||||
}
|
||||
|
||||
for (ZoneCellIter iter(source->zone(), AllocKind::BASE_SHAPE); !iter.done(); iter.next()) {
|
||||
BaseShape* base = iter.get<BaseShape>();
|
||||
for (auto base = source->zone()->cellIter<BaseShape>(); !base.done(); base.next()) {
|
||||
MOZ_ASSERT(base->compartment() == source);
|
||||
base->compartment_ = target;
|
||||
}
|
||||
|
||||
for (ZoneCellIter iter(source->zone(), AllocKind::OBJECT_GROUP); !iter.done(); iter.next()) {
|
||||
ObjectGroup* group = iter.get<ObjectGroup>();
|
||||
for (auto group = source->zone()->cellIter<ObjectGroup>(); !group.done(); group.next()) {
|
||||
group->setGeneration(target->zone()->types.generation);
|
||||
group->compartment_ = target;
|
||||
|
||||
@ -6981,8 +6975,7 @@ gc::MergeCompartments(JSCompartment* source, JSCompartment* target)
|
||||
|
||||
// After fixing JSFunctions' compartments, we can fix LazyScripts'
|
||||
// enclosing scopes.
|
||||
for (ZoneCellIter iter(source->zone(), AllocKind::LAZY_SCRIPT); !iter.done(); iter.next()) {
|
||||
LazyScript* lazy = iter.get<LazyScript>();
|
||||
for (auto lazy = source->zone()->cellIter<LazyScript>(); !lazy.done(); lazy.next()) {
|
||||
MOZ_ASSERT(lazy->functionNonDelazifying()->compartment() == target);
|
||||
|
||||
// See warning in handleParseWorkload. If we start optimizing global
|
||||
@ -7116,12 +7109,9 @@ js::ReleaseAllJITCode(FreeOp* fop)
|
||||
void
|
||||
js::PurgeJITCaches(Zone* zone)
|
||||
{
|
||||
for (ZoneCellIter i(zone, AllocKind::SCRIPT); !i.done(); i.next()) {
|
||||
JSScript* script = i.get<JSScript>();
|
||||
|
||||
/* Discard Ion caches. */
|
||||
/* Discard Ion caches. */
|
||||
for (auto script = zone->cellIter<JSScript>(); !script.done(); script.next())
|
||||
jit::PurgeCaches(script);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@ -7391,8 +7381,7 @@ js::gc::CheckHashTablesAfterMovingGC(JSRuntime* rt)
|
||||
for (ZonesIter zone(rt, SkipAtoms); !zone.done(); zone.next()) {
|
||||
zone->checkUniqueIdTableAfterMovingGC();
|
||||
|
||||
for (ZoneCellIterUnderGC i(zone, AllocKind::BASE_SHAPE); !i.done(); i.next()) {
|
||||
BaseShape* baseShape = i.get<BaseShape>();
|
||||
for (auto baseShape = zone->cellIter<BaseShape>(); !baseShape.done(); baseShape.next()) {
|
||||
if (baseShape->hasTable())
|
||||
baseShape->table().checkAfterMovingGC();
|
||||
}
|
||||
@ -7870,5 +7859,28 @@ StateName(State state)
|
||||
return names[state];
|
||||
}
|
||||
|
||||
void
|
||||
AutoAssertHeapBusy::checkCondition(JSRuntime *rt)
|
||||
{
|
||||
this->rt = rt;
|
||||
MOZ_ASSERT(rt->isHeapBusy());
|
||||
}
|
||||
|
||||
void
|
||||
AutoAssertEmptyNursery::checkCondition(JSRuntime *rt) {
|
||||
this->rt = rt;
|
||||
MOZ_ASSERT(rt->gc.nursery.isEmpty());
|
||||
}
|
||||
|
||||
AutoEmptyNursery::AutoEmptyNursery(JSRuntime *rt)
|
||||
: AutoAssertEmptyNursery()
|
||||
{
|
||||
MOZ_ASSERT(!rt->mainThread.suppressGC);
|
||||
rt->gc.stats.suspendPhases();
|
||||
rt->gc.evictNursery();
|
||||
rt->gc.stats.resumePhases();
|
||||
checkCondition(rt);
|
||||
}
|
||||
|
||||
} /* namespace gc */
|
||||
} /* namespace js */
|
||||
|
@ -1328,6 +1328,95 @@ struct MOZ_RAII AutoAssertNoNurseryAlloc
|
||||
#endif
|
||||
};
|
||||
|
||||
/*
|
||||
* There are a couple of classes here that serve mostly as "tokens" indicating
|
||||
* that a condition holds. Some functions force the caller to possess such a
|
||||
* token because they would misbehave if the condition were false, and it is
|
||||
* far more clear to make the condition visible at the point where it can be
|
||||
* affected rather than just crashing in an assertion down in the place where
|
||||
* it is relied upon.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Token meaning that the heap is busy and no allocations will be made.
|
||||
*
|
||||
* This class may be instantiated directly if it is known that the condition is
|
||||
* already true, or it can be used as a base class for another RAII class that
|
||||
* causes the condition to become true. Such base classes will use the no-arg
|
||||
* constructor, establish the condition, then call checkCondition() to assert
|
||||
* it and possibly record data needed to re-check the condition during
|
||||
* destruction.
|
||||
*
|
||||
* Ordinarily, you would do something like this with a Maybe<> member that is
|
||||
* emplaced during the constructor, but token-requiring functions want to
|
||||
* require a reference to a base class instance. That said, you can always pass
|
||||
* in the Maybe<> field as the token.
|
||||
*/
|
||||
class MOZ_RAII AutoAssertHeapBusy {
|
||||
protected:
|
||||
JSRuntime* rt;
|
||||
|
||||
// Check that the heap really is busy, and record the rt for the check in
|
||||
// the destructor.
|
||||
void checkCondition(JSRuntime *rt);
|
||||
|
||||
AutoAssertHeapBusy() : rt(nullptr) {
|
||||
}
|
||||
|
||||
public:
|
||||
explicit AutoAssertHeapBusy(JSRuntime* rt) {
|
||||
checkCondition(rt);
|
||||
}
|
||||
|
||||
~AutoAssertHeapBusy() {
|
||||
MOZ_ASSERT(rt); // checkCondition must always be called.
|
||||
checkCondition(rt);
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* A class that serves as a token that the nursery is empty. It descends from
|
||||
* AutoAssertHeapBusy, which means that it additionally requires the heap to be
|
||||
* busy (which is not necessarily linked, but turns out to be true in practice
|
||||
* for all users and simplifies the usage of these classes.)
|
||||
*/
|
||||
class MOZ_RAII AutoAssertEmptyNursery
|
||||
{
|
||||
protected:
|
||||
JSRuntime* rt;
|
||||
|
||||
// Check that the nursery is empty.
|
||||
void checkCondition(JSRuntime *rt);
|
||||
|
||||
// For subclasses that need to empty the nursery in their constructors.
|
||||
AutoAssertEmptyNursery() : rt(nullptr) {
|
||||
}
|
||||
|
||||
public:
|
||||
explicit AutoAssertEmptyNursery(JSRuntime* rt) {
|
||||
checkCondition(rt);
|
||||
}
|
||||
|
||||
~AutoAssertEmptyNursery() {
|
||||
checkCondition(rt);
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Evict the nursery upon construction. Serves as a token indicating that the
|
||||
* nursery is empty. (See AutoAssertEmptyNursery, above.)
|
||||
*
|
||||
* Note that this is very improper subclass of AutoAssertHeapBusy, in that the
|
||||
* heap is *not* busy within the scope of an AutoEmptyNursery. I will most
|
||||
* likely fix this by removing AutoAssertHeapBusy, but that is currently
|
||||
* waiting on jonco's review.
|
||||
*/
|
||||
class MOZ_RAII AutoEmptyNursery : public AutoAssertEmptyNursery
|
||||
{
|
||||
public:
|
||||
explicit AutoEmptyNursery(JSRuntime *rt);
|
||||
};
|
||||
|
||||
const char*
|
||||
StateName(State state);
|
||||
|
||||
|
@ -195,39 +195,71 @@ class ArenaCellIterUnderFinalize : public ArenaCellIterImpl
|
||||
explicit ArenaCellIterUnderFinalize(Arena* arena) : ArenaCellIterImpl(arena) {}
|
||||
};
|
||||
|
||||
class ZoneCellIterImpl
|
||||
{
|
||||
template <typename T>
|
||||
class ZoneCellIter;
|
||||
|
||||
template <>
|
||||
class ZoneCellIter<TenuredCell> {
|
||||
ArenaIter arenaIter;
|
||||
ArenaCellIterImpl cellIter;
|
||||
JS::AutoAssertNoAlloc noAlloc;
|
||||
|
||||
public:
|
||||
ZoneCellIterImpl(JS::Zone* zone, AllocKind kind) {
|
||||
protected:
|
||||
// For use when a subclass wants to insert some setup before init().
|
||||
ZoneCellIter() {}
|
||||
|
||||
void init(JS::Zone* zone, AllocKind kind) {
|
||||
JSRuntime* rt = zone->runtimeFromAnyThread();
|
||||
MOZ_ASSERT(zone);
|
||||
MOZ_ASSERT_IF(IsNurseryAllocable(kind), rt->gc.nursery.isEmpty());
|
||||
|
||||
// If called from outside a GC, ensure that the heap is in a state
|
||||
// that allows us to iterate.
|
||||
if (!rt->isHeapBusy()) {
|
||||
// Assert that no GCs can occur while a ZoneCellIter is live.
|
||||
noAlloc.disallowAlloc(rt);
|
||||
}
|
||||
|
||||
// We have a single-threaded runtime, so there's no need to protect
|
||||
// against other threads iterating or allocating. However, we do have
|
||||
// background finalization; we may have to wait for this to finish if
|
||||
// it's currently active.
|
||||
if (IsBackgroundFinalized(kind) && zone->arenas.needBackgroundFinalizeWait(kind))
|
||||
rt->gc.waitBackgroundSweepEnd();
|
||||
|
||||
arenaIter.init(zone, kind);
|
||||
if (!arenaIter.done())
|
||||
cellIter.init(arenaIter.get());
|
||||
}
|
||||
|
||||
public:
|
||||
ZoneCellIter(JS::Zone* zone, AllocKind kind) {
|
||||
// If we are iterating a nursery-allocated kind then we need to
|
||||
// evict first so that we can see all things.
|
||||
if (IsNurseryAllocable(kind)) {
|
||||
JSRuntime* rt = zone->runtimeFromMainThread();
|
||||
rt->gc.evictNursery();
|
||||
}
|
||||
|
||||
init(zone, kind);
|
||||
}
|
||||
|
||||
ZoneCellIter(JS::Zone* zone, AllocKind kind, const js::gc::AutoAssertEmptyNursery&) {
|
||||
// No need to evict the nursery. (This constructor is known statically
|
||||
// to not GC.)
|
||||
init(zone, kind);
|
||||
}
|
||||
|
||||
bool done() const {
|
||||
return arenaIter.done();
|
||||
}
|
||||
|
||||
template<typename T> T* get() const {
|
||||
template<typename T>
|
||||
T* get() const {
|
||||
MOZ_ASSERT(!done());
|
||||
return cellIter.get<T>();
|
||||
}
|
||||
|
||||
Cell* getCell() const {
|
||||
TenuredCell* getCell() const {
|
||||
MOZ_ASSERT(!done());
|
||||
return cellIter.getCell();
|
||||
}
|
||||
@ -244,44 +276,74 @@ class ZoneCellIterImpl
|
||||
}
|
||||
};
|
||||
|
||||
class ZoneCellIterUnderGC : public ZoneCellIterImpl
|
||||
{
|
||||
// Iterator over the cells in a Zone, where the GC type (JSString, JSObject) is
|
||||
// known, for a single AllocKind. Example usages:
|
||||
//
|
||||
// for (auto obj = zone->cellIter<JSObject>(AllocKind::OBJECT0); !obj.done(); obj.next())
|
||||
// ...
|
||||
//
|
||||
// for (auto script = zone->cellIter<JSScript>(); !script.done(); script.next())
|
||||
// f(script->code());
|
||||
//
|
||||
// As this code demonstrates, you can use 'script' as if it were a JSScript*.
|
||||
// Its actual type is ZoneCellIter<JSScript>, but for most purposes it will
|
||||
// autoconvert to JSScript*.
|
||||
//
|
||||
// Note that in the JSScript case, ZoneCellIter is able to infer the AllocKind
|
||||
// from the type 'JSScript', whereas in the JSObject case, the kind must be
|
||||
// given (because there are multiple AllocKinds for objects).
|
||||
//
|
||||
// Also, the static rooting hazard analysis knows that the JSScript case will
|
||||
// not GC during construction. The JSObject case needs to GC, or more precisely
|
||||
// to empty the nursery and clear out the store buffer, so that it can see all
|
||||
// objects to iterate over (the nursery is not iterable) and remove the
|
||||
// possibility of having pointers from the store buffer to data hanging off
|
||||
// stuff we're iterating over that we are going to delete. (The latter should
|
||||
// not be a problem, since such instances should be using RelocatablePtr do
|
||||
// remove themselves from the store buffer on deletion, but currently for
|
||||
// subtle reasons that isn't good enough.)
|
||||
//
|
||||
// If the iterator is used within a GC, then there is no need to evict the
|
||||
// nursery (again). You may select a variant that will skip the eviction either
|
||||
// by specializing on a GCType that is never allocated in the nursery, or
|
||||
// explicitly by passing in a trailing AutoAssertEmptyNursery argument.
|
||||
//
|
||||
template <typename GCType>
|
||||
class ZoneCellIter : public ZoneCellIter<TenuredCell> {
|
||||
public:
|
||||
ZoneCellIterUnderGC(JS::Zone* zone, AllocKind kind)
|
||||
: ZoneCellIterImpl(zone, kind)
|
||||
// Non-nursery allocated (equivalent to having an entry in
|
||||
// MapTypeToFinalizeKind). The template declaration here is to discard this
|
||||
// constructor overload if MapTypeToFinalizeKind<GCType>::kind does not
|
||||
// exist. Note that there will be no remaining overloads that will work,
|
||||
// which makes sense given that you haven't specified which of the
|
||||
// AllocKinds to use for GCType.
|
||||
//
|
||||
// If we later add a nursery allocable GCType with a single AllocKind, we
|
||||
// will want to add an overload of this constructor that does the right
|
||||
// thing (ie, it empties the nursery before iterating.)
|
||||
explicit ZoneCellIter(JS::Zone* zone) : ZoneCellIter<TenuredCell>() {
|
||||
init(zone, MapTypeToFinalizeKind<GCType>::kind);
|
||||
}
|
||||
|
||||
// Non-nursery allocated, nursery is known to be empty: same behavior as above.
|
||||
ZoneCellIter(JS::Zone* zone, const js::gc::AutoAssertEmptyNursery&) : ZoneCellIter(zone) {
|
||||
}
|
||||
|
||||
// Arbitrary kind, which will be assumed to be nursery allocable (and
|
||||
// therefore the nursery will be emptied before iterating.)
|
||||
ZoneCellIter(JS::Zone* zone, AllocKind kind) : ZoneCellIter<TenuredCell>(zone, kind) {
|
||||
}
|
||||
|
||||
// Arbitrary kind, which will be assumed to be nursery allocable, but the
|
||||
// nursery is known to be empty already: same behavior as non-nursery types.
|
||||
ZoneCellIter(JS::Zone* zone, AllocKind kind, const js::gc::AutoAssertEmptyNursery& empty)
|
||||
: ZoneCellIter<TenuredCell>(zone, kind, empty)
|
||||
{
|
||||
MOZ_ASSERT(zone->runtimeFromAnyThread()->isHeapBusy());
|
||||
}
|
||||
};
|
||||
|
||||
class ZoneCellIter
|
||||
{
|
||||
mozilla::Maybe<ZoneCellIterImpl> impl;
|
||||
JS::AutoAssertNoAlloc noAlloc;
|
||||
|
||||
public:
|
||||
ZoneCellIter(JS::Zone* zone, AllocKind kind) {
|
||||
// If called from outside a GC, ensure that the heap is in a state
|
||||
// that allows us to iterate.
|
||||
JSRuntime* rt = zone->runtimeFromMainThread();
|
||||
if (!rt->isHeapBusy()) {
|
||||
// If we are iterating a nursery-allocated kind then we need to
|
||||
// evict first so that we can see all things.
|
||||
if (IsNurseryAllocable(kind))
|
||||
rt->gc.evictNursery();
|
||||
|
||||
// Assert that no GCs can occur while a ZoneCellIter is live.
|
||||
noAlloc.disallowAlloc(rt);
|
||||
}
|
||||
|
||||
impl.emplace(zone, kind);
|
||||
}
|
||||
|
||||
bool done() const { return impl->done(); }
|
||||
template<typename T>
|
||||
T* get() const { return impl->get<T>(); }
|
||||
Cell* getCell() const { return impl->getCell(); }
|
||||
void next() { impl->next(); }
|
||||
GCType* get() const { return ZoneCellIter<TenuredCell>::get<GCType>(); }
|
||||
operator GCType*() const { return get(); }
|
||||
GCType* operator ->() const { return get(); }
|
||||
};
|
||||
|
||||
class GCZonesIter
|
||||
|
@ -201,8 +201,9 @@ js::DumpPCCounts(JSContext* cx, HandleScript script, Sprinter* sp)
|
||||
void
|
||||
js::DumpCompartmentPCCounts(JSContext* cx)
|
||||
{
|
||||
for (ZoneCellIter i(cx->zone(), gc::AllocKind::SCRIPT); !i.done(); i.next()) {
|
||||
RootedScript script(cx, i.get<JSScript>());
|
||||
RootedScript script(cx);
|
||||
for (auto iter = cx->zone()->cellIter<JSScript>(); !iter.done(); iter.next()) {
|
||||
script = iter;
|
||||
if (script->compartment() != cx->compartment())
|
||||
continue;
|
||||
|
||||
@ -1657,8 +1658,7 @@ js::StopPCCountProfiling(JSContext* cx)
|
||||
return;
|
||||
|
||||
for (ZonesIter zone(rt, SkipAtoms); !zone.done(); zone.next()) {
|
||||
for (ZoneCellIter i(zone, AllocKind::SCRIPT); !i.done(); i.next()) {
|
||||
JSScript* script = i.get<JSScript>();
|
||||
for (auto script = zone->cellIter<JSScript>(); !script.done(); script.next()) {
|
||||
if (script->hasScriptCounts() && script->types()) {
|
||||
if (!vec->append(script))
|
||||
return;
|
||||
@ -2025,8 +2025,7 @@ GenerateLcovInfo(JSContext* cx, JSCompartment* comp, GenericPrinter& out)
|
||||
}
|
||||
Rooted<ScriptVector> topScripts(cx, ScriptVector(cx));
|
||||
for (ZonesIter zone(rt, SkipAtoms); !zone.done(); zone.next()) {
|
||||
for (ZoneCellIter i(zone, AllocKind::SCRIPT); !i.done(); i.next()) {
|
||||
JSScript* script = i.get<JSScript>();
|
||||
for (auto script = zone->cellIter<JSScript>(); !script.done(); script.next()) {
|
||||
if (script->compartment() != comp ||
|
||||
!script->isTopLevel() ||
|
||||
!script->filename())
|
||||
|
@ -2352,8 +2352,8 @@ UpdateExecutionObservabilityOfScriptsInZone(JSContext* cx, Zone* zone,
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
for (gc::ZoneCellIter iter(zone, gc::AllocKind::SCRIPT); !iter.done(); iter.next()) {
|
||||
JSScript* script = iter.get<JSScript>();
|
||||
for (auto iter = zone->cellIter<JSScript>(); !iter.done(); iter.next()) {
|
||||
JSScript* script = iter;
|
||||
if (obs.shouldRecompileOrInvalidate(script) &&
|
||||
!gc::IsAboutToBeFinalizedUnbarriered(&script))
|
||||
{
|
||||
|
@ -1271,8 +1271,6 @@ GlobalHelperThreadState::mergeParseTaskCompartment(JSRuntime* rt, ParseTask* par
|
||||
LeaveParseTaskZone(rt, parseTask);
|
||||
|
||||
{
|
||||
gc::ZoneCellIter iter(parseTask->cx->zone(), gc::AllocKind::OBJECT_GROUP);
|
||||
|
||||
// Generator functions don't have Function.prototype as prototype but a
|
||||
// different function object, so the IdentifyStandardPrototype trick
|
||||
// below won't work. Just special-case it.
|
||||
@ -1288,8 +1286,7 @@ GlobalHelperThreadState::mergeParseTaskCompartment(JSRuntime* rt, ParseTask* par
|
||||
// to the corresponding prototype in the new compartment. This will briefly
|
||||
// create cross compartment pointers, which will be fixed by the
|
||||
// MergeCompartments call below.
|
||||
for (; !iter.done(); iter.next()) {
|
||||
ObjectGroup* group = iter.get<ObjectGroup>();
|
||||
for (auto group = parseTask->cx->zone()->cellIter<ObjectGroup>(); !group.done(); group.next()) {
|
||||
TaggedProto proto(group->proto());
|
||||
if (!proto.isObject())
|
||||
continue;
|
||||
|
@ -1049,7 +1049,7 @@ CallAddPropertyHookDense(ExclusiveContext* cx, HandleNativeObject obj, uint32_t
|
||||
}
|
||||
|
||||
static bool
|
||||
UpdateShapeTypeAndValue(ExclusiveContext* cx, NativeObject* obj, Shape* shape, const Value& value)
|
||||
UpdateShapeTypeAndValue(ExclusiveContext* cx, HandleNativeObject obj, HandleShape shape, const Value& value)
|
||||
{
|
||||
jsid id = shape->propid();
|
||||
if (shape->hasSlot()) {
|
||||
|
@ -2545,16 +2545,15 @@ js::PrintTypes(JSContext* cx, JSCompartment* comp, bool force)
|
||||
if (!force && !InferSpewActive(ISpewResult))
|
||||
return;
|
||||
|
||||
for (gc::ZoneCellIter i(zone, gc::AllocKind::SCRIPT); !i.done(); i.next()) {
|
||||
RootedScript script(cx, i.get<JSScript>());
|
||||
RootedScript script(cx);
|
||||
for (auto iter = zone->cellIter<JSScript>(); !iter.done(); iter.next()) {
|
||||
script = iter;
|
||||
if (script->types())
|
||||
script->types()->printTypes(cx, script);
|
||||
}
|
||||
|
||||
for (gc::ZoneCellIter i(zone, gc::AllocKind::OBJECT_GROUP); !i.done(); i.next()) {
|
||||
ObjectGroup* group = i.get<ObjectGroup>();
|
||||
for (auto group = zone->cellIter<ObjectGroup>(); !group.done(); group.next())
|
||||
group->print();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -4425,10 +4424,8 @@ TypeZone::endSweep(JSRuntime* rt)
|
||||
void
|
||||
TypeZone::clearAllNewScriptsOnOOM()
|
||||
{
|
||||
for (gc::ZoneCellIter iter(zone(), gc::AllocKind::OBJECT_GROUP);
|
||||
!iter.done(); iter.next())
|
||||
{
|
||||
ObjectGroup* group = iter.get<ObjectGroup>();
|
||||
for (auto iter = zone()->cellIter<ObjectGroup>(); !iter.done(); iter.next()) {
|
||||
ObjectGroup* group = iter;
|
||||
if (!IsAboutToBeFinalizedUnbarriered(&group))
|
||||
group->maybeClearNewScriptOnOOM();
|
||||
}
|
||||
@ -4437,8 +4434,9 @@ TypeZone::clearAllNewScriptsOnOOM()
|
||||
AutoClearTypeInferenceStateOnOOM::~AutoClearTypeInferenceStateOnOOM()
|
||||
{
|
||||
if (oom) {
|
||||
JSRuntime* rt = zone->runtimeFromMainThread();
|
||||
zone->setPreservingCode(false);
|
||||
zone->discardJitCode(zone->runtimeFromMainThread()->defaultFreeOp());
|
||||
zone->discardJitCode(rt->defaultFreeOp());
|
||||
zone->types.clearAllNewScriptsOnOOM();
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user