зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1072564 - Incrementalize sweeping of type information, r=billm.
This commit is contained in:
Родитель
5ed06f11d9
Коммит
251b70b197
|
@ -227,6 +227,7 @@ class LifoAlloc
|
|||
// Steal allocated chunks from |other|.
|
||||
void steal(LifoAlloc *other) {
|
||||
MOZ_ASSERT(!other->markCount);
|
||||
MOZ_ASSERT(!latest);
|
||||
|
||||
// Copy everything from |other| to |this| except for |peakSize_|, which
|
||||
// requires some care.
|
||||
|
|
|
@ -672,6 +672,7 @@ class GCRuntime
|
|||
*/
|
||||
JS::Zone *zoneGroups;
|
||||
JS::Zone *currentZoneGroup;
|
||||
bool sweepingTypes;
|
||||
unsigned finalizePhase;
|
||||
JS::Zone *sweepZone;
|
||||
unsigned sweepKindIndex;
|
||||
|
|
|
@ -1139,6 +1139,17 @@ ArenaHeader::unsetAllocDuringSweep()
|
|||
auxNextLink = 0;
|
||||
}
|
||||
|
||||
inline void
|
||||
ReleaseArenaList(ArenaHeader *aheader)
|
||||
{
|
||||
ArenaHeader *next;
|
||||
for (; aheader; aheader = next) {
|
||||
// Copy aheader->next before releasing.
|
||||
next = aheader->next;
|
||||
aheader->chunk()->releaseArena(aheader);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
AssertValidColor(const TenuredCell *thing, uint32_t color)
|
||||
{
|
||||
|
|
|
@ -511,16 +511,8 @@ IsAboutToBeFinalizedFromAnyThread(T **thingp)
|
|||
|
||||
Zone *zone = thing->asTenured().zoneFromAnyThread();
|
||||
if (zone->isGCSweeping()) {
|
||||
/*
|
||||
* We should return false for things that have been allocated during
|
||||
* incremental sweeping, but this possibility doesn't occur at the moment
|
||||
* because this function is only called at the very start of the sweeping a
|
||||
* compartment group and during minor gc. Rather than do the extra check,
|
||||
* we just assert that it's not necessary.
|
||||
*/
|
||||
MOZ_ASSERT_IF(!rt->isHeapMinorCollecting(),
|
||||
!thing->asTenured().arenaHeader()->allocatedDuringIncremental);
|
||||
|
||||
if (thing->asTenured().arenaHeader()->allocatedDuringIncremental)
|
||||
return false;
|
||||
return !thing->asTenured().isMarked();
|
||||
}
|
||||
#ifdef JSGC_COMPACTING
|
||||
|
|
|
@ -301,10 +301,9 @@ static const PhaseInfo phases[] = {
|
|||
{ PHASE_SWEEP_BREAKPOINT, "Sweep Breakpoints", PHASE_SWEEP_COMPARTMENTS },
|
||||
{ PHASE_SWEEP_REGEXP, "Sweep Regexps", PHASE_SWEEP_COMPARTMENTS },
|
||||
{ PHASE_SWEEP_MISC, "Sweep Miscellaneous", PHASE_SWEEP_COMPARTMENTS },
|
||||
{ PHASE_DISCARD_ANALYSIS, "Discard Analysis", PHASE_SWEEP_COMPARTMENTS },
|
||||
{ PHASE_DISCARD_TI, "Discard TI", PHASE_DISCARD_ANALYSIS },
|
||||
{ PHASE_FREE_TI_ARENA, "Free TI Arena", PHASE_DISCARD_ANALYSIS },
|
||||
{ PHASE_SWEEP_TYPES, "Sweep Types", PHASE_DISCARD_ANALYSIS },
|
||||
{ PHASE_SWEEP_TYPES, "Sweep type information", PHASE_SWEEP_COMPARTMENTS },
|
||||
{ PHASE_SWEEP_TYPES_BEGIN, "Sweep type tables and compilations", PHASE_SWEEP_TYPES },
|
||||
{ PHASE_SWEEP_TYPES_END, "Free type arena", PHASE_SWEEP_TYPES },
|
||||
{ PHASE_SWEEP_OBJECT, "Sweep Object", PHASE_SWEEP },
|
||||
{ PHASE_SWEEP_STRING, "Sweep String", PHASE_SWEEP },
|
||||
{ PHASE_SWEEP_SCRIPT, "Sweep Script", PHASE_SWEEP },
|
||||
|
|
|
@ -54,10 +54,9 @@ enum Phase {
|
|||
PHASE_SWEEP_BREAKPOINT,
|
||||
PHASE_SWEEP_REGEXP,
|
||||
PHASE_SWEEP_MISC,
|
||||
PHASE_DISCARD_ANALYSIS,
|
||||
PHASE_DISCARD_TI,
|
||||
PHASE_FREE_TI_ARENA,
|
||||
PHASE_SWEEP_TYPES,
|
||||
PHASE_SWEEP_TYPES_BEGIN,
|
||||
PHASE_SWEEP_TYPES_END,
|
||||
PHASE_SWEEP_OBJECT,
|
||||
PHASE_SWEEP_STRING,
|
||||
PHASE_SWEEP_SCRIPT,
|
||||
|
|
|
@ -105,27 +105,15 @@ Zone::onTooMuchMalloc()
|
|||
}
|
||||
|
||||
void
|
||||
Zone::sweepAnalysis(FreeOp *fop, bool releaseTypes)
|
||||
Zone::beginSweepTypes(FreeOp *fop, bool releaseTypes)
|
||||
{
|
||||
// Periodically release observed types for all scripts. This is safe to
|
||||
// do when there are no frames for the zone on the stack.
|
||||
if (active)
|
||||
releaseTypes = false;
|
||||
|
||||
bool oom = false;
|
||||
types.sweep(fop, releaseTypes, &oom);
|
||||
|
||||
// If there was an OOM while sweeping types, the type information needs to
|
||||
// be deoptimized so that it will still correct (i.e. overapproximates the
|
||||
// possible types in the zone), but the constraints might not have been
|
||||
// triggered on the deoptimization or even copied over completely. In this
|
||||
// case, destroy all JIT code and new script information in the zone, the
|
||||
// only things whose correctness depends on the type constraints.
|
||||
if (oom) {
|
||||
setPreservingCode(false);
|
||||
discardJitCode(fop);
|
||||
types.clearAllNewScriptsOnOOM();
|
||||
}
|
||||
types::AutoClearTypeInferenceStateOnOOM oom(this);
|
||||
types.beginSweep(fop, releaseTypes, oom);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -157,7 +157,7 @@ struct Zone : public JS::shadow::Zone,
|
|||
}
|
||||
void reportAllocationOverflow() { js_ReportAllocationOverflow(nullptr); }
|
||||
|
||||
void sweepAnalysis(js::FreeOp *fop, bool releaseTypes);
|
||||
void beginSweepTypes(js::FreeOp *fop, bool releaseTypes);
|
||||
|
||||
bool hasMarkedCompartments();
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ function testMap() {
|
|||
print(m.mode+" "+m.expect);
|
||||
nums.mapPar(function (v) {
|
||||
var x = [];
|
||||
for (var i = 0; i < 45000; i++) {
|
||||
for (var i = 0; i < 20000; i++) {
|
||||
x[i] = {from: v};
|
||||
}
|
||||
return x;
|
||||
|
|
|
@ -2846,7 +2846,7 @@ jit::InvalidateAll(FreeOp *fop, Zone *zone)
|
|||
|
||||
void
|
||||
jit::Invalidate(types::TypeZone &types, FreeOp *fop,
|
||||
const Vector<types::RecompileInfo> &invalid, bool resetUses,
|
||||
const types::RecompileInfoVector &invalid, bool resetUses,
|
||||
bool cancelOffThread)
|
||||
{
|
||||
JitSpew(JitSpew_IonInvalidate, "Start invalidation.");
|
||||
|
@ -2855,23 +2855,24 @@ jit::Invalidate(types::TypeZone &types, FreeOp *fop,
|
|||
// to the traversal which frames have been invalidated.
|
||||
size_t numInvalidations = 0;
|
||||
for (size_t i = 0; i < invalid.length(); i++) {
|
||||
const types::CompilerOutput &co = *invalid[i].compilerOutput(types);
|
||||
if (!co.isValid())
|
||||
const types::CompilerOutput *co = invalid[i].compilerOutput(types);
|
||||
if (!co)
|
||||
continue;
|
||||
MOZ_ASSERT(co->isValid());
|
||||
|
||||
if (cancelOffThread)
|
||||
CancelOffThreadIonCompile(co.script()->compartment(), co.script());
|
||||
CancelOffThreadIonCompile(co->script()->compartment(), co->script());
|
||||
|
||||
if (!co.ion())
|
||||
if (!co->ion())
|
||||
continue;
|
||||
|
||||
JitSpew(JitSpew_IonInvalidate, " Invalidate %s:%u, IonScript %p",
|
||||
co.script()->filename(), co.script()->lineno(), co.ion());
|
||||
co->script()->filename(), co->script()->lineno(), co->ion());
|
||||
|
||||
// Keep the ion script alive during the invalidation and flag this
|
||||
// ionScript as being invalidated. This increment is removed by the
|
||||
// loop after the calls to InvalidateActivation.
|
||||
co.ion()->incref();
|
||||
co->ion()->incref();
|
||||
numInvalidations++;
|
||||
}
|
||||
|
||||
|
@ -2887,19 +2888,20 @@ jit::Invalidate(types::TypeZone &types, FreeOp *fop,
|
|||
// IonScript will be immediately destroyed. Otherwise, it will be held live
|
||||
// until its last invalidated frame is destroyed.
|
||||
for (size_t i = 0; i < invalid.length(); i++) {
|
||||
types::CompilerOutput &co = *invalid[i].compilerOutput(types);
|
||||
if (!co.isValid())
|
||||
types::CompilerOutput *co = invalid[i].compilerOutput(types);
|
||||
if (!co)
|
||||
continue;
|
||||
MOZ_ASSERT(co->isValid());
|
||||
|
||||
ExecutionMode executionMode = co.mode();
|
||||
JSScript *script = co.script();
|
||||
IonScript *ionScript = co.ion();
|
||||
ExecutionMode executionMode = co->mode();
|
||||
JSScript *script = co->script();
|
||||
IonScript *ionScript = co->ion();
|
||||
if (!ionScript)
|
||||
continue;
|
||||
|
||||
SetIonScript(nullptr, script, executionMode, nullptr);
|
||||
ionScript->decref(fop);
|
||||
co.invalidate();
|
||||
co->invalidate();
|
||||
numInvalidations--;
|
||||
|
||||
// Wait for the scripts to get warm again before doing another
|
||||
|
@ -2923,7 +2925,7 @@ jit::Invalidate(types::TypeZone &types, FreeOp *fop,
|
|||
}
|
||||
|
||||
void
|
||||
jit::Invalidate(JSContext *cx, const Vector<types::RecompileInfo> &invalid, bool resetUses,
|
||||
jit::Invalidate(JSContext *cx, const types::RecompileInfoVector &invalid, bool resetUses,
|
||||
bool cancelOffThread)
|
||||
{
|
||||
jit::Invalidate(cx->zone()->types, cx->runtime()->defaultFreeOp(), invalid, resetUses,
|
||||
|
@ -2934,7 +2936,7 @@ bool
|
|||
jit::IonScript::invalidate(JSContext *cx, bool resetUses, const char *reason)
|
||||
{
|
||||
JitSpew(JitSpew_IonInvalidate, " Invalidate IonScript %p: %s", this, reason);
|
||||
Vector<types::RecompileInfo> list(cx);
|
||||
types::RecompileInfoVector list;
|
||||
if (!list.append(recompileInfo()))
|
||||
return false;
|
||||
Invalidate(cx, list, resetUses, true);
|
||||
|
@ -2968,7 +2970,7 @@ jit::Invalidate(JSContext *cx, JSScript *script, ExecutionMode mode, bool resetU
|
|||
js_free(buf);
|
||||
}
|
||||
|
||||
Vector<types::RecompileInfo> scripts(cx);
|
||||
types::RecompileInfoVector scripts;
|
||||
|
||||
switch (mode) {
|
||||
case SequentialExecution:
|
||||
|
|
|
@ -126,9 +126,9 @@ IonExecStatus FastInvoke(JSContext *cx, HandleFunction fun, CallArgs &args);
|
|||
|
||||
// Walk the stack and invalidate active Ion frames for the invalid scripts.
|
||||
void Invalidate(types::TypeZone &types, FreeOp *fop,
|
||||
const Vector<types::RecompileInfo> &invalid, bool resetUses = true,
|
||||
const types::RecompileInfoVector &invalid, bool resetUses = true,
|
||||
bool cancelOffThread = true);
|
||||
void Invalidate(JSContext *cx, const Vector<types::RecompileInfo> &invalid, bool resetUses = true,
|
||||
void Invalidate(JSContext *cx, const types::RecompileInfoVector &invalid, bool resetUses = true,
|
||||
bool cancelOffThread = true);
|
||||
bool Invalidate(JSContext *cx, JSScript *script, ExecutionMode mode, bool resetUses = true,
|
||||
bool cancelOffThread = true);
|
||||
|
|
|
@ -2850,7 +2850,7 @@ jit::AnalyzeNewScriptDefiniteProperties(JSContext *cx, JSFunction *fun,
|
|||
types::TypeObject *type, HandleNativeObject baseobj,
|
||||
Vector<types::TypeNewScript::Initializer> *initializerList)
|
||||
{
|
||||
MOZ_ASSERT(cx->compartment()->activeAnalysis);
|
||||
MOZ_ASSERT(cx->zone()->types.activeAnalysis);
|
||||
|
||||
// When invoking 'new' on the specified script, try to find some properties
|
||||
// which will definitely be added to the created object before it has a
|
||||
|
|
|
@ -4271,11 +4271,10 @@ IonBuilder::inlineScriptedCall(CallInfo &callInfo, JSFunction *target)
|
|||
|
||||
// Improve type information of |this| when not set.
|
||||
if (callInfo.constructing() &&
|
||||
!callInfo.thisArg()->resultTypeSet() &&
|
||||
calleeScript->types)
|
||||
!callInfo.thisArg()->resultTypeSet())
|
||||
{
|
||||
types::StackTypeSet *types = types::TypeScript::ThisTypes(calleeScript);
|
||||
if (!types->unknown()) {
|
||||
if (types && !types->unknown()) {
|
||||
types::TemporaryTypeSet *clonedTypes = types->clone(alloc_->lifoAlloc());
|
||||
if (!clonedTypes)
|
||||
return oom();
|
||||
|
@ -5207,9 +5206,8 @@ IonBuilder::createThisScriptedSingleton(JSFunction *target, MDefinition *callee)
|
|||
if (!templateObject->hasTenuredProto() || templateObject->getProto() != proto)
|
||||
return nullptr;
|
||||
|
||||
if (!target->nonLazyScript()->types)
|
||||
return nullptr;
|
||||
if (!types::TypeScript::ThisTypes(target->nonLazyScript())->hasType(types::Type::ObjectType(templateObject)))
|
||||
types::StackTypeSet *thisTypes = types::TypeScript::ThisTypes(target->nonLazyScript());
|
||||
if (!thisTypes || !thisTypes->hasType(types::Type::ObjectType(templateObject)))
|
||||
return nullptr;
|
||||
|
||||
// Generate an inline path to create a new |this| object with
|
||||
|
@ -5580,6 +5578,9 @@ IonBuilder::testShouldDOMCall(types::TypeSet *inTypes,
|
|||
static bool
|
||||
ArgumentTypesMatch(MDefinition *def, types::StackTypeSet *calleeTypes)
|
||||
{
|
||||
if (!calleeTypes)
|
||||
return false;
|
||||
|
||||
if (def->resultTypeSet()) {
|
||||
MOZ_ASSERT(def->type() == MIRType_Value || def->mightBeType(def->type()));
|
||||
return def->resultTypeSet()->isSubset(calleeTypes);
|
||||
|
@ -5605,9 +5606,6 @@ IonBuilder::testNeedsArgumentCheck(JSFunction *target, CallInfo &callInfo)
|
|||
|
||||
JSScript *targetScript = target->nonLazyScript();
|
||||
|
||||
if (!targetScript->types)
|
||||
return true;
|
||||
|
||||
if (!ArgumentTypesMatch(callInfo.thisArg(), types::TypeScript::ThisTypes(targetScript)))
|
||||
return true;
|
||||
uint32_t expected_args = Min<uint32_t>(callInfo.argc(), target->nargs());
|
||||
|
|
|
@ -100,8 +100,6 @@ JSCompartment::init(JSContext *cx)
|
|||
if (cx)
|
||||
cx->runtime()->dateTimeInfo.updateTimeZoneAdjustment();
|
||||
|
||||
activeAnalysis = false;
|
||||
|
||||
if (!crossCompartmentWrappers.init(0))
|
||||
return false;
|
||||
|
||||
|
|
|
@ -196,8 +196,6 @@ struct JSCompartment
|
|||
*/
|
||||
void adoptWorkerAllocator(js::Allocator *workerAllocator);
|
||||
|
||||
bool activeAnalysis;
|
||||
|
||||
/* Type information about the scripts and objects in this compartment. */
|
||||
js::types::TypeCompartment types;
|
||||
|
||||
|
|
262
js/src/jsgc.cpp
262
js/src/jsgc.cpp
|
@ -352,22 +352,6 @@ struct js::gc::FinalizePhase
|
|||
|
||||
#define PHASE(x, p) { ArrayLength(x), x, p }
|
||||
|
||||
/*
|
||||
* Finalization order for foreground non-incrementally swept things.
|
||||
*/
|
||||
static const AllocKind ImmediatePhaseObjects[] = {
|
||||
FINALIZE_OBJECT0,
|
||||
FINALIZE_OBJECT2,
|
||||
FINALIZE_OBJECT4,
|
||||
FINALIZE_OBJECT8,
|
||||
FINALIZE_OBJECT12,
|
||||
FINALIZE_OBJECT16
|
||||
};
|
||||
|
||||
static const FinalizePhase ImmediateFinalizePhases[] = {
|
||||
PHASE(ImmediatePhaseObjects, gcstats::PHASE_SWEEP_OBJECT)
|
||||
};
|
||||
|
||||
/*
|
||||
* Finalization order for incrementally swept things.
|
||||
*/
|
||||
|
@ -566,11 +550,13 @@ FinalizeTypedArenas(FreeOp *fop,
|
|||
ArenaHeader **src,
|
||||
SortedArenaList &dest,
|
||||
AllocKind thingKind,
|
||||
SliceBudget &budget)
|
||||
SliceBudget &budget,
|
||||
ArenaLists::KeepArenasEnum keepArenas)
|
||||
{
|
||||
/*
|
||||
* Finalize arenas from src list, releasing empty arenas and inserting the
|
||||
* others into the appropriate destination size bins.
|
||||
* Finalize arenas from src list, releasing empty arenas if keepArenas
|
||||
* wasn't specified and inserting the others into the appropriate
|
||||
* destination size bins.
|
||||
*/
|
||||
|
||||
/*
|
||||
|
@ -578,7 +564,7 @@ FinalizeTypedArenas(FreeOp *fop,
|
|||
* but in that case, we want to hold on to the memory in our arena
|
||||
* lists, not offer it up for reuse.
|
||||
*/
|
||||
bool releaseArenas = !InParallelSection();
|
||||
MOZ_ASSERT_IF(InParallelSection(), keepArenas);
|
||||
|
||||
size_t thingSize = Arena::thingSize(thingKind);
|
||||
size_t thingsPerArena = Arena::thingsPerArena(thingSize);
|
||||
|
@ -590,10 +576,10 @@ FinalizeTypedArenas(FreeOp *fop,
|
|||
|
||||
if (nmarked)
|
||||
dest.insertAt(aheader, nfree);
|
||||
else if (releaseArenas)
|
||||
aheader->chunk()->releaseArena(aheader);
|
||||
else
|
||||
else if (keepArenas)
|
||||
aheader->chunk()->recycleArena(aheader, dest, thingKind, thingsPerArena);
|
||||
else
|
||||
aheader->chunk()->releaseArena(aheader);
|
||||
|
||||
budget.step(thingsPerArena);
|
||||
if (budget.isOverBudget())
|
||||
|
@ -612,7 +598,8 @@ FinalizeArenas(FreeOp *fop,
|
|||
ArenaHeader **src,
|
||||
SortedArenaList &dest,
|
||||
AllocKind thingKind,
|
||||
SliceBudget &budget)
|
||||
SliceBudget &budget,
|
||||
ArenaLists::KeepArenasEnum keepArenas)
|
||||
{
|
||||
switch (thingKind) {
|
||||
case FINALIZE_OBJECT0:
|
||||
|
@ -627,33 +614,33 @@ FinalizeArenas(FreeOp *fop,
|
|||
case FINALIZE_OBJECT12_BACKGROUND:
|
||||
case FINALIZE_OBJECT16:
|
||||
case FINALIZE_OBJECT16_BACKGROUND:
|
||||
return FinalizeTypedArenas<JSObject>(fop, src, dest, thingKind, budget);
|
||||
return FinalizeTypedArenas<JSObject>(fop, src, dest, thingKind, budget, keepArenas);
|
||||
case FINALIZE_SCRIPT:
|
||||
return FinalizeTypedArenas<JSScript>(fop, src, dest, thingKind, budget);
|
||||
return FinalizeTypedArenas<JSScript>(fop, src, dest, thingKind, budget, keepArenas);
|
||||
case FINALIZE_LAZY_SCRIPT:
|
||||
return FinalizeTypedArenas<LazyScript>(fop, src, dest, thingKind, budget);
|
||||
return FinalizeTypedArenas<LazyScript>(fop, src, dest, thingKind, budget, keepArenas);
|
||||
case FINALIZE_SHAPE:
|
||||
return FinalizeTypedArenas<Shape>(fop, src, dest, thingKind, budget);
|
||||
return FinalizeTypedArenas<Shape>(fop, src, dest, thingKind, budget, keepArenas);
|
||||
case FINALIZE_ACCESSOR_SHAPE:
|
||||
return FinalizeTypedArenas<AccessorShape>(fop, src, dest, thingKind, budget);
|
||||
return FinalizeTypedArenas<AccessorShape>(fop, src, dest, thingKind, budget, keepArenas);
|
||||
case FINALIZE_BASE_SHAPE:
|
||||
return FinalizeTypedArenas<BaseShape>(fop, src, dest, thingKind, budget);
|
||||
return FinalizeTypedArenas<BaseShape>(fop, src, dest, thingKind, budget, keepArenas);
|
||||
case FINALIZE_TYPE_OBJECT:
|
||||
return FinalizeTypedArenas<types::TypeObject>(fop, src, dest, thingKind, budget);
|
||||
return FinalizeTypedArenas<types::TypeObject>(fop, src, dest, thingKind, budget, keepArenas);
|
||||
case FINALIZE_STRING:
|
||||
return FinalizeTypedArenas<JSString>(fop, src, dest, thingKind, budget);
|
||||
return FinalizeTypedArenas<JSString>(fop, src, dest, thingKind, budget, keepArenas);
|
||||
case FINALIZE_FAT_INLINE_STRING:
|
||||
return FinalizeTypedArenas<JSFatInlineString>(fop, src, dest, thingKind, budget);
|
||||
return FinalizeTypedArenas<JSFatInlineString>(fop, src, dest, thingKind, budget, keepArenas);
|
||||
case FINALIZE_EXTERNAL_STRING:
|
||||
return FinalizeTypedArenas<JSExternalString>(fop, src, dest, thingKind, budget);
|
||||
return FinalizeTypedArenas<JSExternalString>(fop, src, dest, thingKind, budget, keepArenas);
|
||||
case FINALIZE_SYMBOL:
|
||||
return FinalizeTypedArenas<JS::Symbol>(fop, src, dest, thingKind, budget);
|
||||
return FinalizeTypedArenas<JS::Symbol>(fop, src, dest, thingKind, budget, keepArenas);
|
||||
case FINALIZE_JITCODE:
|
||||
{
|
||||
// JitCode finalization may release references on an executable
|
||||
// allocator that is accessed when requesting interrupts.
|
||||
JSRuntime::AutoLockForInterrupt lock(fop->runtime());
|
||||
return FinalizeTypedArenas<jit::JitCode>(fop, src, dest, thingKind, budget);
|
||||
return FinalizeTypedArenas<jit::JitCode>(fop, src, dest, thingKind, budget, keepArenas);
|
||||
}
|
||||
default:
|
||||
MOZ_CRASH("Invalid alloc kind");
|
||||
|
@ -2054,7 +2041,7 @@ ArenaLists::wipeDuringParallelExecution(JSRuntime *rt)
|
|||
|
||||
if (!arenaLists[i].isEmpty()) {
|
||||
purge(thingKind);
|
||||
forceFinalizeNow(&fop, thingKind);
|
||||
forceFinalizeNow(&fop, thingKind, KEEP_ARENAS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2506,18 +2493,18 @@ ArenaLists::finalizeNow(FreeOp *fop, const FinalizePhase& phase)
|
|||
{
|
||||
gcstats::AutoPhase ap(fop->runtime()->gc.stats, phase.statsPhase);
|
||||
for (unsigned i = 0; i < phase.length; ++i)
|
||||
finalizeNow(fop, phase.kinds[i]);
|
||||
finalizeNow(fop, phase.kinds[i], RELEASE_ARENAS, nullptr);
|
||||
}
|
||||
|
||||
void
|
||||
ArenaLists::finalizeNow(FreeOp *fop, AllocKind thingKind)
|
||||
ArenaLists::finalizeNow(FreeOp *fop, AllocKind thingKind, KeepArenasEnum keepArenas, ArenaHeader **empty)
|
||||
{
|
||||
MOZ_ASSERT(!IsBackgroundFinalized(thingKind));
|
||||
forceFinalizeNow(fop, thingKind);
|
||||
forceFinalizeNow(fop, thingKind, keepArenas, empty);
|
||||
}
|
||||
|
||||
void
|
||||
ArenaLists::forceFinalizeNow(FreeOp *fop, AllocKind thingKind)
|
||||
ArenaLists::forceFinalizeNow(FreeOp *fop, AllocKind thingKind, KeepArenasEnum keepArenas, ArenaHeader **empty)
|
||||
{
|
||||
MOZ_ASSERT(backgroundFinalizeState[thingKind] == BFS_DONE);
|
||||
|
||||
|
@ -2530,9 +2517,14 @@ ArenaLists::forceFinalizeNow(FreeOp *fop, AllocKind thingKind)
|
|||
SortedArenaList finalizedSorted(thingsPerArena);
|
||||
|
||||
SliceBudget budget;
|
||||
FinalizeArenas(fop, &arenas, finalizedSorted, thingKind, budget);
|
||||
FinalizeArenas(fop, &arenas, finalizedSorted, thingKind, budget, keepArenas);
|
||||
MOZ_ASSERT(!arenas);
|
||||
|
||||
if (empty) {
|
||||
MOZ_ASSERT(keepArenas == KEEP_ARENAS);
|
||||
finalizedSorted.extractEmpty(empty);
|
||||
}
|
||||
|
||||
arenaLists[thingKind] = finalizedSorted.toArenaList();
|
||||
}
|
||||
|
||||
|
@ -2586,6 +2578,8 @@ ArenaLists::queueForBackgroundSweep(FreeOp *fop, AllocKind thingKind)
|
|||
ArenaLists::backgroundFinalize(FreeOp *fop, ArenaHeader *listHead)
|
||||
{
|
||||
MOZ_ASSERT(listHead);
|
||||
MOZ_ASSERT(!InParallelSection());
|
||||
|
||||
AllocKind thingKind = listHead->getAllocKind();
|
||||
Zone *zone = listHead->zone;
|
||||
|
||||
|
@ -2593,7 +2587,7 @@ ArenaLists::backgroundFinalize(FreeOp *fop, ArenaHeader *listHead)
|
|||
SortedArenaList finalizedSorted(thingsPerArena);
|
||||
|
||||
SliceBudget budget;
|
||||
FinalizeArenas(fop, &listHead, finalizedSorted, thingKind, budget);
|
||||
FinalizeArenas(fop, &listHead, finalizedSorted, thingKind, budget, RELEASE_ARENAS);
|
||||
MOZ_ASSERT(!listHead);
|
||||
|
||||
// When arenas are queued for background finalization, all arenas are moved
|
||||
|
@ -2624,6 +2618,72 @@ ArenaLists::backgroundFinalize(FreeOp *fop, ArenaHeader *listHead)
|
|||
lists->backgroundFinalizeState[thingKind] = BFS_DONE;
|
||||
}
|
||||
|
||||
void
|
||||
ArenaLists::queueForegroundObjectsForSweep(FreeOp *fop)
|
||||
{
|
||||
gcstats::AutoPhase ap(fop->runtime()->gc.stats, gcstats::PHASE_SWEEP_OBJECT);
|
||||
|
||||
#ifdef DEBUG
|
||||
for (size_t i = 0; i < FINALIZE_OBJECT_LIMIT; i++)
|
||||
MOZ_ASSERT(savedObjectArenas[i].isEmpty());
|
||||
MOZ_ASSERT(savedEmptyObjectArenas == nullptr);
|
||||
#endif
|
||||
|
||||
// Foreground finalized objects must be finalized at the beginning of the
|
||||
// sweep phase, before control can return to the mutator. Otherwise,
|
||||
// mutator behavior can resurrect certain objects whose references would
|
||||
// otherwise have been erased by the finalizer.
|
||||
finalizeNow(fop, FINALIZE_OBJECT0, KEEP_ARENAS, &savedEmptyObjectArenas);
|
||||
finalizeNow(fop, FINALIZE_OBJECT2, KEEP_ARENAS, &savedEmptyObjectArenas);
|
||||
finalizeNow(fop, FINALIZE_OBJECT4, KEEP_ARENAS, &savedEmptyObjectArenas);
|
||||
finalizeNow(fop, FINALIZE_OBJECT8, KEEP_ARENAS, &savedEmptyObjectArenas);
|
||||
finalizeNow(fop, FINALIZE_OBJECT12, KEEP_ARENAS, &savedEmptyObjectArenas);
|
||||
finalizeNow(fop, FINALIZE_OBJECT16, KEEP_ARENAS, &savedEmptyObjectArenas);
|
||||
|
||||
// Prevent the arenas from having new objects allocated into them. We need
|
||||
// to know which objects are marked while we incrementally sweep dead
|
||||
// references from type information.
|
||||
savedObjectArenas[FINALIZE_OBJECT0] = arenaLists[FINALIZE_OBJECT0].copyAndClear();
|
||||
savedObjectArenas[FINALIZE_OBJECT2] = arenaLists[FINALIZE_OBJECT2].copyAndClear();
|
||||
savedObjectArenas[FINALIZE_OBJECT4] = arenaLists[FINALIZE_OBJECT4].copyAndClear();
|
||||
savedObjectArenas[FINALIZE_OBJECT8] = arenaLists[FINALIZE_OBJECT8].copyAndClear();
|
||||
savedObjectArenas[FINALIZE_OBJECT12] = arenaLists[FINALIZE_OBJECT12].copyAndClear();
|
||||
savedObjectArenas[FINALIZE_OBJECT16] = arenaLists[FINALIZE_OBJECT16].copyAndClear();
|
||||
}
|
||||
|
||||
void
|
||||
ArenaLists::mergeForegroundSweptObjectArenas()
|
||||
{
|
||||
ReleaseArenaList(savedEmptyObjectArenas);
|
||||
savedEmptyObjectArenas = nullptr;
|
||||
|
||||
mergeSweptArenas(FINALIZE_OBJECT0);
|
||||
mergeSweptArenas(FINALIZE_OBJECT2);
|
||||
mergeSweptArenas(FINALIZE_OBJECT4);
|
||||
mergeSweptArenas(FINALIZE_OBJECT8);
|
||||
mergeSweptArenas(FINALIZE_OBJECT12);
|
||||
mergeSweptArenas(FINALIZE_OBJECT16);
|
||||
}
|
||||
|
||||
inline void
|
||||
ArenaLists::mergeSweptArenas(AllocKind thingKind)
|
||||
{
|
||||
ArenaList *al = &arenaLists[thingKind];
|
||||
ArenaList *saved = &savedObjectArenas[thingKind];
|
||||
|
||||
*al = saved->insertListWithCursorAtEnd(*al);
|
||||
saved->clear();
|
||||
}
|
||||
|
||||
void
|
||||
ArenaLists::queueForegroundThingsForSweep(FreeOp *fop)
|
||||
{
|
||||
gcShapeArenasToUpdate = arenaListsToSweep[FINALIZE_SHAPE];
|
||||
gcAccessorShapeArenasToUpdate = arenaListsToSweep[FINALIZE_ACCESSOR_SHAPE];
|
||||
gcTypeObjectArenasToUpdate = arenaListsToSweep[FINALIZE_TYPE_OBJECT];
|
||||
gcScriptArenasToUpdate = arenaListsToSweep[FINALIZE_SCRIPT];
|
||||
}
|
||||
|
||||
static void *
|
||||
RunLastDitchGC(JSContext *cx, JS::Zone *zone, AllocKind thingKind)
|
||||
{
|
||||
|
@ -4749,9 +4809,10 @@ GCRuntime::beginSweepingZoneGroup()
|
|||
}
|
||||
|
||||
{
|
||||
gcstats::AutoPhase ap(stats, gcstats::PHASE_DISCARD_ANALYSIS);
|
||||
gcstats::AutoPhase ap1(stats, gcstats::PHASE_SWEEP_TYPES);
|
||||
gcstats::AutoPhase ap2(stats, gcstats::PHASE_SWEEP_TYPES_BEGIN);
|
||||
for (GCZoneGroupIter zone(rt); !zone.done(); zone.next()) {
|
||||
zone->sweepAnalysis(&fop, releaseObservedTypes && !zone->isPreservingCode());
|
||||
zone->beginSweepTypes(&fop, releaseObservedTypes && !zone->isPreservingCode());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4799,8 +4860,7 @@ GCRuntime::beginSweepingZoneGroup()
|
|||
|
||||
for (GCZoneGroupIter zone(rt); !zone.done(); zone.next()) {
|
||||
gcstats::AutoSCC scc(stats, zoneGroupIndex);
|
||||
for (unsigned i = 0; i < ArrayLength(ImmediateFinalizePhases); ++i)
|
||||
zone->allocator.arenas.finalizeNow(&fop, ImmediateFinalizePhases[i]);
|
||||
zone->allocator.arenas.queueForegroundObjectsForSweep(&fop);
|
||||
}
|
||||
for (GCZoneGroupIter zone(rt); !zone.done(); zone.next()) {
|
||||
gcstats::AutoSCC scc(stats, zoneGroupIndex);
|
||||
|
@ -4814,12 +4874,11 @@ GCRuntime::beginSweepingZoneGroup()
|
|||
}
|
||||
for (GCZoneGroupIter zone(rt); !zone.done(); zone.next()) {
|
||||
gcstats::AutoSCC scc(stats, zoneGroupIndex);
|
||||
zone->allocator.arenas.gcShapeArenasToSweep =
|
||||
zone->allocator.arenas.arenaListsToSweep[FINALIZE_SHAPE];
|
||||
zone->allocator.arenas.gcAccessorShapeArenasToSweep =
|
||||
zone->allocator.arenas.arenaListsToSweep[FINALIZE_ACCESSOR_SHAPE];
|
||||
zone->allocator.arenas.queueForegroundThingsForSweep(&fop);
|
||||
}
|
||||
|
||||
sweepingTypes = true;
|
||||
|
||||
finalizePhase = 0;
|
||||
sweepZone = currentZoneGroup;
|
||||
sweepKindIndex = 0;
|
||||
|
@ -4888,10 +4947,14 @@ bool
|
|||
ArenaLists::foregroundFinalize(FreeOp *fop, AllocKind thingKind, SliceBudget &sliceBudget,
|
||||
SortedArenaList &sweepList)
|
||||
{
|
||||
MOZ_ASSERT(!InParallelSection());
|
||||
|
||||
if (!arenaListsToSweep[thingKind] && incrementalSweptArenas.isEmpty())
|
||||
return true;
|
||||
|
||||
if (!FinalizeArenas(fop, &arenaListsToSweep[thingKind], sweepList, thingKind, sliceBudget)) {
|
||||
if (!FinalizeArenas(fop, &arenaListsToSweep[thingKind], sweepList,
|
||||
thingKind, sliceBudget, RELEASE_ARENAS))
|
||||
{
|
||||
incrementalSweptArenaKind = thingKind;
|
||||
incrementalSweptArenas = sweepList.toArenaList();
|
||||
return false;
|
||||
|
@ -4915,8 +4978,18 @@ GCRuntime::drainMarkStack(SliceBudget &sliceBudget, gcstats::Phase phase)
|
|||
return marker.drainMarkStack(sliceBudget);
|
||||
}
|
||||
|
||||
// Advance to the next entry in a list of arenas, returning false if the
|
||||
// mutator should resume running.
|
||||
static bool
|
||||
SweepShapes(ArenaHeader **arenasToSweep, size_t thingsPerArena, SliceBudget &sliceBudget)
|
||||
AdvanceArenaList(ArenaHeader **list, AllocKind kind, SliceBudget &sliceBudget)
|
||||
{
|
||||
*list = (*list)->next;
|
||||
sliceBudget.step(Arena::thingsPerArena(Arena::thingSize(kind)));
|
||||
return !sliceBudget.isOverBudget();
|
||||
}
|
||||
|
||||
static bool
|
||||
SweepShapes(ArenaHeader **arenasToSweep, AllocKind kind, SliceBudget &sliceBudget)
|
||||
{
|
||||
while (ArenaHeader *arena = *arenasToSweep) {
|
||||
for (ArenaCellIterUnderGC i(arena); !i.done(); i.next()) {
|
||||
|
@ -4925,10 +4998,8 @@ SweepShapes(ArenaHeader **arenasToSweep, size_t thingsPerArena, SliceBudget &sli
|
|||
shape->sweep();
|
||||
}
|
||||
|
||||
*arenasToSweep = arena->next;
|
||||
sliceBudget.step(thingsPerArena);
|
||||
if (sliceBudget.isOverBudget())
|
||||
return false; /* Yield to the mutator. */
|
||||
if (!AdvanceArenaList(arenasToSweep, kind, sliceBudget))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -4945,6 +5016,64 @@ GCRuntime::sweepPhase(SliceBudget &sliceBudget)
|
|||
return false;
|
||||
|
||||
for (;;) {
|
||||
// Sweep dead type information stored in scripts and type objects, but
|
||||
// don't finalize them yet. We have to sweep dead information from both
|
||||
// live and dead scripts and type objects, so that no dead references
|
||||
// remain in them. Type inference can end up crawling these zones
|
||||
// again, such as for TypeCompartment::markSetsUnknown, and if this
|
||||
// happens after sweeping for the zone group finishes we won't be able
|
||||
// to determine which things in the zone are live.
|
||||
if (sweepingTypes) {
|
||||
gcstats::AutoPhase ap1(stats, gcstats::PHASE_SWEEP_COMPARTMENTS);
|
||||
gcstats::AutoPhase ap2(stats, gcstats::PHASE_SWEEP_TYPES);
|
||||
|
||||
for (; sweepZone; sweepZone = sweepZone->nextNodeInGroup()) {
|
||||
ArenaLists &al = sweepZone->allocator.arenas;
|
||||
|
||||
types::AutoClearTypeInferenceStateOnOOM oom(sweepZone);
|
||||
|
||||
while (ArenaHeader *arena = al.gcScriptArenasToUpdate) {
|
||||
for (ArenaCellIterUnderGC i(arena); !i.done(); i.next()) {
|
||||
JSScript *script = i.get<JSScript>();
|
||||
script->maybeSweepTypes(&oom);
|
||||
}
|
||||
|
||||
if (!AdvanceArenaList(&al.gcScriptArenasToUpdate,
|
||||
FINALIZE_SCRIPT, sliceBudget))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
while (ArenaHeader *arena = al.gcTypeObjectArenasToUpdate) {
|
||||
for (ArenaCellIterUnderGC i(arena); !i.done(); i.next()) {
|
||||
types::TypeObject *object = i.get<types::TypeObject>();
|
||||
object->maybeSweep(&oom);
|
||||
}
|
||||
|
||||
if (!AdvanceArenaList(&al.gcTypeObjectArenasToUpdate,
|
||||
FINALIZE_TYPE_OBJECT, sliceBudget))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Finish sweeping type information in the zone.
|
||||
{
|
||||
gcstats::AutoPhase ap(stats, gcstats::PHASE_SWEEP_TYPES_END);
|
||||
sweepZone->types.endSweep(rt);
|
||||
}
|
||||
|
||||
// Foreground finalized objects have already been finalized,
|
||||
// and now their arenas can be reclaimed by freeing empty ones
|
||||
// and making non-empty ones available for allocation.
|
||||
al.mergeForegroundSweptObjectArenas();
|
||||
}
|
||||
|
||||
sweepZone = currentZoneGroup;
|
||||
sweepingTypes = false;
|
||||
}
|
||||
|
||||
/* Finalize foreground finalized things. */
|
||||
for (; finalizePhase < ArrayLength(IncrementalFinalizePhases) ; ++finalizePhase) {
|
||||
gcstats::AutoPhase ap(stats, IncrementalFinalizePhases[finalizePhase].statsPhase);
|
||||
|
@ -4979,15 +5108,13 @@ GCRuntime::sweepPhase(SliceBudget &sliceBudget)
|
|||
|
||||
for (; sweepZone; sweepZone = sweepZone->nextNodeInGroup()) {
|
||||
Zone *zone = sweepZone;
|
||||
if (!SweepShapes(&zone->allocator.arenas.gcShapeArenasToSweep,
|
||||
Arena::thingsPerArena(Arena::thingSize(FINALIZE_SHAPE)),
|
||||
sliceBudget))
|
||||
if (!SweepShapes(&zone->allocator.arenas.gcShapeArenasToUpdate,
|
||||
FINALIZE_SHAPE, sliceBudget))
|
||||
{
|
||||
return false; /* Yield to the mutator. */
|
||||
}
|
||||
if (!SweepShapes(&zone->allocator.arenas.gcAccessorShapeArenasToSweep,
|
||||
Arena::thingsPerArena(Arena::thingSize(FINALIZE_ACCESSOR_SHAPE)),
|
||||
sliceBudget))
|
||||
if (!SweepShapes(&zone->allocator.arenas.gcAccessorShapeArenasToUpdate,
|
||||
FINALIZE_ACCESSOR_SHAPE, sliceBudget))
|
||||
{
|
||||
return false; /* Yield to the mutator. */
|
||||
}
|
||||
|
@ -6107,12 +6234,14 @@ gc::MergeCompartments(JSCompartment *source, JSCompartment *target)
|
|||
|
||||
source->clearTables();
|
||||
|
||||
// Fixup compartment pointers in source to refer to target.
|
||||
// Fixup compartment pointers in source to refer to target, and make sure
|
||||
// type information generations are in sync.
|
||||
|
||||
for (ZoneCellIter iter(source->zone(), FINALIZE_SCRIPT); !iter.done(); iter.next()) {
|
||||
JSScript *script = iter.get<JSScript>();
|
||||
MOZ_ASSERT(script->compartment() == source);
|
||||
script->compartment_ = target;
|
||||
script->setTypesGeneration(target->zone()->types.generation);
|
||||
}
|
||||
|
||||
for (ZoneCellIter iter(source->zone(), FINALIZE_BASE_SHAPE); !iter.done(); iter.next()) {
|
||||
|
@ -6121,6 +6250,11 @@ gc::MergeCompartments(JSCompartment *source, JSCompartment *target)
|
|||
base->compartment_ = target;
|
||||
}
|
||||
|
||||
for (ZoneCellIter iter(source->zone(), FINALIZE_TYPE_OBJECT); !iter.done(); iter.next()) {
|
||||
types::TypeObject *type = iter.get<types::TypeObject>();
|
||||
type->setGeneration(target->zone()->types.generation);
|
||||
}
|
||||
|
||||
// Fixup zone pointers in source's zone to refer to target's zone.
|
||||
|
||||
for (size_t thingKind = 0; thingKind != FINALIZE_LIMIT; thingKind++) {
|
||||
|
|
|
@ -421,6 +421,12 @@ class ArenaList {
|
|||
check();
|
||||
}
|
||||
|
||||
ArenaList copyAndClear() {
|
||||
ArenaList result = *this;
|
||||
clear();
|
||||
return result;
|
||||
}
|
||||
|
||||
bool isEmpty() const {
|
||||
check();
|
||||
return !head_;
|
||||
|
@ -548,6 +554,16 @@ class SortedArenaList
|
|||
segments[nfree].append(aheader);
|
||||
}
|
||||
|
||||
// Remove all empty arenas, inserting them as a linked list.
|
||||
void extractEmpty(ArenaHeader **empty) {
|
||||
SortedArenaListSegment &segment = segments[thingsPerArena_];
|
||||
if (segment.head) {
|
||||
*segment.tailp = *empty;
|
||||
*empty = segment.head;
|
||||
segment.clear();
|
||||
}
|
||||
}
|
||||
|
||||
// Links up the tail of each non-empty segment to the head of the next
|
||||
// non-empty segment, creating a contiguous list that is returned as an
|
||||
// ArenaList. This is not a destructive operation: neither the head nor tail
|
||||
|
@ -604,9 +620,19 @@ class ArenaLists
|
|||
unsigned incrementalSweptArenaKind;
|
||||
ArenaList incrementalSweptArenas;
|
||||
|
||||
/* Shape arenas to be swept in the foreground. */
|
||||
ArenaHeader *gcShapeArenasToSweep;
|
||||
ArenaHeader *gcAccessorShapeArenasToSweep;
|
||||
// Arena lists which have yet to be swept, but need additional foreground
|
||||
// processing before they are swept.
|
||||
ArenaHeader *gcShapeArenasToUpdate;
|
||||
ArenaHeader *gcAccessorShapeArenasToUpdate;
|
||||
ArenaHeader *gcScriptArenasToUpdate;
|
||||
ArenaHeader *gcTypeObjectArenasToUpdate;
|
||||
|
||||
// While sweeping type information, these lists save the arenas for the
|
||||
// objects which have already been finalized in the foreground (which must
|
||||
// happen at the beginning of the GC), so that type sweeping can determine
|
||||
// which of the object pointers are marked.
|
||||
ArenaList savedObjectArenas[FINALIZE_OBJECT_LIMIT];
|
||||
ArenaHeader *savedEmptyObjectArenas;
|
||||
|
||||
public:
|
||||
ArenaLists() {
|
||||
|
@ -617,8 +643,11 @@ class ArenaLists
|
|||
for (size_t i = 0; i != FINALIZE_LIMIT; ++i)
|
||||
arenaListsToSweep[i] = nullptr;
|
||||
incrementalSweptArenaKind = FINALIZE_LIMIT;
|
||||
gcShapeArenasToSweep = nullptr;
|
||||
gcAccessorShapeArenasToSweep = nullptr;
|
||||
gcShapeArenasToUpdate = nullptr;
|
||||
gcAccessorShapeArenasToUpdate = nullptr;
|
||||
gcScriptArenasToUpdate = nullptr;
|
||||
gcTypeObjectArenasToUpdate = nullptr;
|
||||
savedEmptyObjectArenas = nullptr;
|
||||
}
|
||||
|
||||
~ArenaLists() {
|
||||
|
@ -628,19 +657,13 @@ class ArenaLists
|
|||
* the background finalization is disabled.
|
||||
*/
|
||||
MOZ_ASSERT(backgroundFinalizeState[i] == BFS_DONE);
|
||||
ArenaHeader *next;
|
||||
for (ArenaHeader *aheader = arenaLists[i].head(); aheader; aheader = next) {
|
||||
// Copy aheader->next before releasing.
|
||||
next = aheader->next;
|
||||
aheader->chunk()->releaseArena(aheader);
|
||||
}
|
||||
}
|
||||
ArenaHeader *next;
|
||||
for (ArenaHeader *aheader = incrementalSweptArenas.head(); aheader; aheader = next) {
|
||||
// Copy aheader->next before releasing.
|
||||
next = aheader->next;
|
||||
aheader->chunk()->releaseArena(aheader);
|
||||
ReleaseArenaList(arenaLists[i].head());
|
||||
}
|
||||
ReleaseArenaList(incrementalSweptArenas.head());
|
||||
|
||||
for (size_t i = 0; i < FINALIZE_OBJECT_LIMIT; i++)
|
||||
ReleaseArenaList(savedObjectArenas[i].head());
|
||||
ReleaseArenaList(savedEmptyObjectArenas);
|
||||
}
|
||||
|
||||
static uintptr_t getFreeListOffset(AllocKind thingKind) {
|
||||
|
@ -823,11 +846,10 @@ class ArenaLists
|
|||
ArenaHeader *relocateArenas(ArenaHeader *relocatedList);
|
||||
#endif
|
||||
|
||||
void queueObjectsForSweep(FreeOp *fop);
|
||||
void queueStringsAndSymbolsForSweep(FreeOp *fop);
|
||||
void queueShapesForSweep(FreeOp *fop);
|
||||
void queueScriptsForSweep(FreeOp *fop);
|
||||
void queueJitCodeForSweep(FreeOp *fop);
|
||||
void queueForegroundObjectsForSweep(FreeOp *fop);
|
||||
void queueForegroundThingsForSweep(FreeOp *fop);
|
||||
|
||||
void mergeForegroundSweptObjectArenas();
|
||||
|
||||
bool foregroundFinalize(FreeOp *fop, AllocKind thingKind, SliceBudget &sliceBudget,
|
||||
SortedArenaList &sweepList);
|
||||
|
@ -835,14 +857,25 @@ class ArenaLists
|
|||
|
||||
void wipeDuringParallelExecution(JSRuntime *rt);
|
||||
|
||||
// When finalizing arenas, whether to keep empty arenas on the list or
|
||||
// release them immediately.
|
||||
enum KeepArenasEnum {
|
||||
RELEASE_ARENAS,
|
||||
KEEP_ARENAS
|
||||
};
|
||||
|
||||
private:
|
||||
inline void finalizeNow(FreeOp *fop, const FinalizePhase& phase);
|
||||
inline void queueForForegroundSweep(FreeOp *fop, const FinalizePhase& phase);
|
||||
inline void queueForBackgroundSweep(FreeOp *fop, const FinalizePhase& phase);
|
||||
inline void finalizeNow(FreeOp *fop, AllocKind thingKind);
|
||||
inline void forceFinalizeNow(FreeOp *fop, AllocKind thingKind);
|
||||
|
||||
inline void finalizeNow(FreeOp *fop, AllocKind thingKind,
|
||||
KeepArenasEnum keepArenas, ArenaHeader **empty = nullptr);
|
||||
inline void forceFinalizeNow(FreeOp *fop, AllocKind thingKind,
|
||||
KeepArenasEnum keepArenas, ArenaHeader **empty = nullptr);
|
||||
inline void queueForForegroundSweep(FreeOp *fop, AllocKind thingKind);
|
||||
inline void queueForBackgroundSweep(FreeOp *fop, AllocKind thingKind);
|
||||
inline void mergeSweptArenas(AllocKind thingKind);
|
||||
|
||||
TenuredCell *allocateFromArena(JS::Zone *zone, AllocKind thingKind,
|
||||
AutoMaybeStartBackgroundAllocation &maybeStartBGAlloc);
|
||||
|
|
|
@ -461,7 +461,7 @@ ConstraintTypeSet::addConstraint(JSContext *cx, TypeConstraint *constraint, bool
|
|||
return false;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(cx->compartment()->activeAnalysis);
|
||||
MOZ_ASSERT(cx->zone()->types.activeAnalysis);
|
||||
|
||||
InferSpew(ISpewOps, "addConstraint: %sT%p%s %sC%p%s %s",
|
||||
InferSpewColor(this), this, InferSpewColorReset(),
|
||||
|
@ -555,7 +555,7 @@ TypeSet::addType(Type type, LifoAlloc *alloc)
|
|||
void
|
||||
ConstraintTypeSet::addType(ExclusiveContext *cxArg, Type type)
|
||||
{
|
||||
MOZ_ASSERT(cxArg->compartment()->activeAnalysis);
|
||||
MOZ_ASSERT(cxArg->zone()->types.activeAnalysis);
|
||||
|
||||
if (hasType(type))
|
||||
return;
|
||||
|
@ -924,7 +924,7 @@ TypeScript::FreezeTypeSets(CompilerConstraintList *constraints, JSScript *script
|
|||
TemporaryTypeSet **pBytecodeTypes)
|
||||
{
|
||||
LifoAlloc *alloc = constraints->alloc();
|
||||
StackTypeSet *existing = script->types->typeArray();
|
||||
StackTypeSet *existing = script->types()->typeArray();
|
||||
|
||||
size_t count = NumTypeSets(script);
|
||||
TemporaryTypeSet *types = alloc->newArrayUninitialized<TemporaryTypeSet>(count);
|
||||
|
@ -1192,7 +1192,7 @@ types::FinishCompilation(JSContext *cx, HandleScript script, ExecutionMode execu
|
|||
|
||||
TypeZone &types = cx->zone()->types;
|
||||
if (!types.compilerOutputs) {
|
||||
types.compilerOutputs = cx->new_< Vector<CompilerOutput> >(cx);
|
||||
types.compilerOutputs = cx->new_<TypeZone::CompilerOutputVector>();
|
||||
if (!types.compilerOutputs)
|
||||
return false;
|
||||
}
|
||||
|
@ -1205,10 +1205,12 @@ types::FinishCompilation(JSContext *cx, HandleScript script, ExecutionMode execu
|
|||
#endif
|
||||
|
||||
uint32_t index = types.compilerOutputs->length();
|
||||
if (!types.compilerOutputs->append(co))
|
||||
if (!types.compilerOutputs->append(co)) {
|
||||
js_ReportOutOfMemory(cx);
|
||||
return false;
|
||||
}
|
||||
|
||||
*precompileInfo = RecompileInfo(index);
|
||||
*precompileInfo = RecompileInfo(index, types.generation);
|
||||
|
||||
bool succeeded = true;
|
||||
|
||||
|
@ -1220,7 +1222,10 @@ types::FinishCompilation(JSContext *cx, HandleScript script, ExecutionMode execu
|
|||
|
||||
for (size_t i = 0; i < constraints->numFrozenScripts(); i++) {
|
||||
const CompilerConstraintList::FrozenScript &entry = constraints->frozenScript(i);
|
||||
MOZ_ASSERT(entry.script->types);
|
||||
if (!entry.script->types()) {
|
||||
succeeded = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!CheckFrozenTypeSet(cx, entry.thisTypes, types::TypeScript::ThisTypes(entry.script)))
|
||||
succeeded = false;
|
||||
|
@ -1232,7 +1237,7 @@ types::FinishCompilation(JSContext *cx, HandleScript script, ExecutionMode execu
|
|||
succeeded = false;
|
||||
}
|
||||
for (size_t i = 0; i < entry.script->nTypeSets(); i++) {
|
||||
if (!CheckFrozenTypeSet(cx, &entry.bytecodeTypes[i], &entry.script->types->typeArray()[i]))
|
||||
if (!CheckFrozenTypeSet(cx, &entry.bytecodeTypes[i], &entry.script->types()->typeArray()[i]))
|
||||
succeeded = false;
|
||||
}
|
||||
|
||||
|
@ -1244,7 +1249,7 @@ types::FinishCompilation(JSContext *cx, HandleScript script, ExecutionMode execu
|
|||
|
||||
size_t count = TypeScript::NumTypeSets(entry.script);
|
||||
|
||||
StackTypeSet *array = entry.script->types->typeArray();
|
||||
StackTypeSet *array = entry.script->types()->typeArray();
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
if (!array[i].addConstraint(cx, cx->typeLifoAlloc().new_<TypeConstraintFreezeStack>(entry.script), false))
|
||||
succeeded = false;
|
||||
|
@ -1288,7 +1293,7 @@ types::FinishDefinitePropertiesAnalysis(JSContext *cx, CompilerConstraintList *c
|
|||
for (size_t i = 0; i < constraints->numFrozenScripts(); i++) {
|
||||
const CompilerConstraintList::FrozenScript &entry = constraints->frozenScript(i);
|
||||
JSScript *script = entry.script;
|
||||
MOZ_ASSERT(script->types);
|
||||
MOZ_ASSERT(script->types());
|
||||
|
||||
MOZ_ASSERT(TypeScript::ThisTypes(script)->isSubset(entry.thisTypes));
|
||||
|
||||
|
@ -1299,16 +1304,14 @@ types::FinishDefinitePropertiesAnalysis(JSContext *cx, CompilerConstraintList *c
|
|||
MOZ_ASSERT(TypeScript::ArgTypes(script, j)->isSubset(&entry.argTypes[j]));
|
||||
|
||||
for (size_t j = 0; j < script->nTypeSets(); j++)
|
||||
MOZ_ASSERT(script->types->typeArray()[j].isSubset(&entry.bytecodeTypes[j]));
|
||||
MOZ_ASSERT(script->types()->typeArray()[j].isSubset(&entry.bytecodeTypes[j]));
|
||||
}
|
||||
#endif
|
||||
|
||||
for (size_t i = 0; i < constraints->numFrozenScripts(); i++) {
|
||||
const CompilerConstraintList::FrozenScript &entry = constraints->frozenScript(i);
|
||||
JSScript *script = entry.script;
|
||||
MOZ_ASSERT(script->types);
|
||||
|
||||
if (!script->types)
|
||||
if (!script->types())
|
||||
MOZ_CRASH();
|
||||
|
||||
CheckDefinitePropertiesTypeSet(cx, entry.thisTypes, TypeScript::ThisTypes(script));
|
||||
|
@ -1320,7 +1323,7 @@ types::FinishDefinitePropertiesAnalysis(JSContext *cx, CompilerConstraintList *c
|
|||
CheckDefinitePropertiesTypeSet(cx, &entry.argTypes[j], TypeScript::ArgTypes(script, j));
|
||||
|
||||
for (size_t j = 0; j < script->nTypeSets(); j++)
|
||||
CheckDefinitePropertiesTypeSet(cx, &entry.bytecodeTypes[j], &script->types->typeArray()[j]);
|
||||
CheckDefinitePropertiesTypeSet(cx, &entry.bytecodeTypes[j], &script->types()->typeArray()[j]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2390,20 +2393,24 @@ types::TypeCanHaveExtraIndexedProperties(CompilerConstraintList *constraints,
|
|||
}
|
||||
|
||||
void
|
||||
TypeZone::processPendingRecompiles(FreeOp *fop)
|
||||
TypeZone::processPendingRecompiles(FreeOp *fop, RecompileInfoVector &recompiles)
|
||||
{
|
||||
if (!pendingRecompiles)
|
||||
return;
|
||||
MOZ_ASSERT(!recompiles.empty());
|
||||
|
||||
/* Steal the list of scripts to recompile, else we will try to recursively recompile them. */
|
||||
Vector<RecompileInfo> *pending = pendingRecompiles;
|
||||
pendingRecompiles = nullptr;
|
||||
/*
|
||||
* Steal the list of scripts to recompile, to make sure we don't try to
|
||||
* recursively recompile them.
|
||||
*/
|
||||
RecompileInfoVector pending;
|
||||
for (size_t i = 0; i < recompiles.length(); i++) {
|
||||
if (!pending.append(recompiles[i]))
|
||||
CrashAtUnhandlableOOM("processPendingRecompiles");
|
||||
}
|
||||
recompiles.clear();
|
||||
|
||||
MOZ_ASSERT(!pending->empty());
|
||||
jit::Invalidate(*this, fop, pending);
|
||||
|
||||
jit::Invalidate(*this, fop, *pending);
|
||||
|
||||
fop->delete_(pending);
|
||||
MOZ_ASSERT(recompiles.empty());
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -2418,13 +2425,7 @@ TypeZone::addPendingRecompile(JSContext *cx, const RecompileInfo &info)
|
|||
|
||||
co->setPendingInvalidation();
|
||||
|
||||
if (!pendingRecompiles) {
|
||||
pendingRecompiles = cx->new_< Vector<RecompileInfo> >(cx);
|
||||
if (!pendingRecompiles)
|
||||
CrashAtUnhandlableOOM("Could not update pendingRecompiles");
|
||||
}
|
||||
|
||||
if (!pendingRecompiles->append(info))
|
||||
if (!cx->zone()->types.activeAnalysis->pendingRecompiles.append(info))
|
||||
CrashAtUnhandlableOOM("Could not update pendingRecompiles");
|
||||
}
|
||||
|
||||
|
@ -2476,9 +2477,9 @@ TypeCompartment::markSetsUnknown(JSContext *cx, TypeObject *target)
|
|||
|
||||
for (gc::ZoneCellIter i(cx->zone(), gc::FINALIZE_SCRIPT); !i.done(); i.next()) {
|
||||
JSScript *script = i.get<JSScript>();
|
||||
if (script->types) {
|
||||
if (script->types()) {
|
||||
unsigned count = TypeScript::NumTypeSets(script);
|
||||
StackTypeSet *typeArray = script->types->typeArray();
|
||||
StackTypeSet *typeArray = script->types()->typeArray();
|
||||
for (unsigned i = 0; i < count; i++) {
|
||||
if (typeArray[i].hasType(Type::ObjectType(target)))
|
||||
typeArray[i].addType(cx, Type::AnyObjectType());
|
||||
|
@ -2496,21 +2497,21 @@ TypeCompartment::print(JSContext *cx, bool force)
|
|||
gc::AutoSuppressGC suppressGC(cx);
|
||||
JSAutoRequest request(cx);
|
||||
|
||||
JSCompartment *compartment = this->compartment();
|
||||
AutoEnterAnalysis enter(nullptr, compartment);
|
||||
Zone *zone = compartment()->zone();
|
||||
AutoEnterAnalysis enter(nullptr, zone);
|
||||
|
||||
if (!force && !InferSpewActive(ISpewResult))
|
||||
return;
|
||||
|
||||
for (gc::ZoneCellIter i(compartment->zone(), gc::FINALIZE_SCRIPT); !i.done(); i.next()) {
|
||||
for (gc::ZoneCellIter i(zone, gc::FINALIZE_SCRIPT); !i.done(); i.next()) {
|
||||
// Note: use cx->runtime() instead of cx to work around IsInRequest(cx)
|
||||
// assertion failures when we're called from DestroyContext.
|
||||
RootedScript script(cx->runtime(), i.get<JSScript>());
|
||||
if (script->types)
|
||||
script->types->printTypes(cx, script);
|
||||
if (script->types())
|
||||
script->types()->printTypes(cx, script);
|
||||
}
|
||||
|
||||
for (gc::ZoneCellIter i(compartment->zone(), gc::FINALIZE_TYPE_OBJECT); !i.done(); i.next()) {
|
||||
for (gc::ZoneCellIter i(zone, gc::FINALIZE_TYPE_OBJECT); !i.done(); i.next()) {
|
||||
TypeObject *object = i.get<TypeObject>();
|
||||
object->print();
|
||||
}
|
||||
|
@ -2587,7 +2588,7 @@ void
|
|||
TypeCompartment::setTypeToHomogenousArray(ExclusiveContext *cx,
|
||||
JSObject *obj, Type elementType)
|
||||
{
|
||||
MOZ_ASSERT(cx->compartment()->activeAnalysis);
|
||||
MOZ_ASSERT(cx->zone()->types.activeAnalysis);
|
||||
|
||||
if (!arrayTypeTable) {
|
||||
arrayTypeTable = cx->new_<ArrayTypeTable>();
|
||||
|
@ -3204,7 +3205,7 @@ TypeObject::markUnknown(ExclusiveContext *cx)
|
|||
{
|
||||
AutoEnterAnalysis enter(cx);
|
||||
|
||||
MOZ_ASSERT(cx->compartment()->activeAnalysis);
|
||||
MOZ_ASSERT(cx->zone()->types.activeAnalysis);
|
||||
MOZ_ASSERT(!unknownProperties());
|
||||
|
||||
InferSpew(ISpewOps, "UnknownProperties: %s", TypeObjectString(this));
|
||||
|
@ -3234,7 +3235,7 @@ TypeObject::markUnknown(ExclusiveContext *cx)
|
|||
void
|
||||
TypeObject::maybeClearNewScriptOnOOM()
|
||||
{
|
||||
MOZ_ASSERT(zone()->isGCSweeping());
|
||||
MOZ_ASSERT(zone()->isGCSweepingOrCompacting());
|
||||
|
||||
if (!isMarked())
|
||||
return;
|
||||
|
@ -3457,7 +3458,7 @@ types::AddClearDefiniteFunctionUsesInScript(JSContext *cx, TypeObject *type,
|
|||
TypeObjectKey *calleeKey = Type::ObjectType(calleeScript->functionNonDelazifying()).objectKey();
|
||||
|
||||
unsigned count = TypeScript::NumTypeSets(script);
|
||||
StackTypeSet *typeArray = script->types->typeArray();
|
||||
StackTypeSet *typeArray = script->types()->typeArray();
|
||||
|
||||
for (unsigned i = 0; i < count; i++) {
|
||||
StackTypeSet *types = &typeArray[i];
|
||||
|
@ -3667,7 +3668,7 @@ types::UseNewTypeForClone(JSFunction *fun)
|
|||
bool
|
||||
JSScript::makeTypes(JSContext *cx)
|
||||
{
|
||||
MOZ_ASSERT(!types);
|
||||
MOZ_ASSERT(!types_);
|
||||
|
||||
AutoEnterAnalysis enter(cx);
|
||||
|
||||
|
@ -3678,7 +3679,8 @@ JSScript::makeTypes(JSContext *cx)
|
|||
if (!typeScript)
|
||||
return false;
|
||||
|
||||
types = typeScript;
|
||||
types_ = typeScript;
|
||||
setTypesGeneration(cx->zone()->types.generation);
|
||||
|
||||
#ifdef DEBUG
|
||||
StackTypeSet *typeArray = typeScript->typeArray();
|
||||
|
@ -3734,7 +3736,7 @@ JSFunction::setTypeForScriptedFunction(ExclusiveContext *cx, HandleFunction fun,
|
|||
/* static */ void
|
||||
TypeNewScript::make(JSContext *cx, TypeObject *type, JSFunction *fun)
|
||||
{
|
||||
MOZ_ASSERT(cx->compartment()->activeAnalysis);
|
||||
MOZ_ASSERT(cx->zone()->types.activeAnalysis);
|
||||
MOZ_ASSERT(!type->newScript());
|
||||
|
||||
if (type->unknownProperties())
|
||||
|
@ -3882,6 +3884,12 @@ TypeNewScript::maybeAnalyze(JSContext *cx, TypeObject *type, bool *regenerate, b
|
|||
// whether the new type table was updated and type needs to be refreshed.
|
||||
MOZ_ASSERT(this == type->newScript());
|
||||
|
||||
// Make sure there aren't dead references in preliminaryObjects. This can
|
||||
// clear out the new script information on OOM.
|
||||
type->maybeSweep(nullptr);
|
||||
if (!type->newScript())
|
||||
return true;
|
||||
|
||||
if (regenerate)
|
||||
*regenerate = false;
|
||||
|
||||
|
@ -4177,7 +4185,7 @@ TypeNewScript::trace(JSTracer *trc)
|
|||
}
|
||||
|
||||
void
|
||||
TypeNewScript::sweep(FreeOp *fop)
|
||||
TypeNewScript::sweep()
|
||||
{
|
||||
// preliminaryObjects only holds weak pointers, so clear any objects that
|
||||
// are about to be destroyed.
|
||||
|
@ -4531,8 +4539,15 @@ ExclusiveContext::getSingletonType(const Class *clasp, TaggedProto proto)
|
|||
/////////////////////////////////////////////////////////////////////
|
||||
|
||||
void
|
||||
ConstraintTypeSet::sweep(Zone *zone, bool *oom)
|
||||
ConstraintTypeSet::sweep(Zone *zone, AutoClearTypeInferenceStateOnOOM &oom)
|
||||
{
|
||||
MOZ_ASSERT(zone->isGCSweepingOrCompacting());
|
||||
|
||||
// IsAboutToBeFinalized doesn't work right on tenured objects when called
|
||||
// during a minor collection.
|
||||
MOZ_ASSERT(!zone->runtimeFromMainThread()->isHeapMinorCollecting());
|
||||
MOZ_ASSERT(!zone->runtimeFromMainThread()->isFJMinorCollecting());
|
||||
|
||||
/*
|
||||
* Purge references to type objects that are no longer live. Type sets hold
|
||||
* only weak references. For type sets containing more than one object,
|
||||
|
@ -4555,7 +4570,7 @@ ConstraintTypeSet::sweep(Zone *zone, bool *oom)
|
|||
if (pentry) {
|
||||
*pentry = object;
|
||||
} else {
|
||||
*oom = true;
|
||||
oom.setOOM();
|
||||
flags |= TYPE_FLAG_ANYOBJECT;
|
||||
clearObjects();
|
||||
objectCount = 0;
|
||||
|
@ -4587,7 +4602,7 @@ ConstraintTypeSet::sweep(Zone *zone, bool *oom)
|
|||
copy->next = constraintList;
|
||||
constraintList = copy;
|
||||
} else {
|
||||
*oom = true;
|
||||
oom.setOOM();
|
||||
}
|
||||
}
|
||||
constraint = constraint->next;
|
||||
|
@ -4601,27 +4616,53 @@ TypeObject::clearProperties()
|
|||
propertySet = nullptr;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
bool
|
||||
TypeObject::needsSweep()
|
||||
{
|
||||
return generation() != zone()->types.generation;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void
|
||||
EnsureHasAutoClearTypeInferenceStateOnOOM(AutoClearTypeInferenceStateOnOOM *&oom, Zone *zone,
|
||||
Maybe<AutoClearTypeInferenceStateOnOOM> &fallback)
|
||||
{
|
||||
if (!oom) {
|
||||
if (zone->types.activeAnalysis) {
|
||||
oom = &zone->types.activeAnalysis->oom;
|
||||
} else {
|
||||
fallback.emplace(zone);
|
||||
oom = &fallback.ref();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Before sweeping the arenas themselves, scan all type objects in a
|
||||
* compartment to fixup weak references: property type sets referencing dead
|
||||
* JS and type objects, and singleton JS objects whose type is not referenced
|
||||
* elsewhere. This also releases memory associated with dead type objects,
|
||||
* so that type objects do not need later finalization.
|
||||
* elsewhere. This is done either incrementally as part of the sweep, or on
|
||||
* demand as type objects are accessed before their contents have been swept.
|
||||
*/
|
||||
inline void
|
||||
TypeObject::sweep(FreeOp *fop, bool *oom)
|
||||
void
|
||||
TypeObject::maybeSweep(AutoClearTypeInferenceStateOnOOM *oom)
|
||||
{
|
||||
MOZ_ASSERT(zone()->isGCSweepingOrCompacting());
|
||||
|
||||
if (zone()->isGCSweeping() && !isMarked()) {
|
||||
// Take care of any finalization required for this object.
|
||||
if (newScript())
|
||||
fop->delete_(newScript());
|
||||
if (generation() == zone()->types.generation) {
|
||||
// No sweeping required.
|
||||
return;
|
||||
}
|
||||
|
||||
setGeneration(zone()->types.generation);
|
||||
|
||||
MOZ_ASSERT(zone()->isGCSweepingOrCompacting());
|
||||
MOZ_ASSERT(!zone()->runtimeFromMainThread()->isHeapMinorCollecting());
|
||||
|
||||
Maybe<AutoClearTypeInferenceStateOnOOM> fallbackOOM;
|
||||
EnsureHasAutoClearTypeInferenceStateOnOOM(oom, zone(), fallbackOOM);
|
||||
|
||||
if (newScript())
|
||||
newScript()->sweep(fop);
|
||||
newScript()->sweep();
|
||||
|
||||
LifoAlloc &typeLifoAlloc = zone()->types.typeLifoAlloc;
|
||||
|
||||
|
@ -4656,12 +4697,12 @@ TypeObject::sweep(FreeOp *fop, bool *oom)
|
|||
(typeLifoAlloc, propertySet, propertyCount, prop->id);
|
||||
if (pentry) {
|
||||
*pentry = newProp;
|
||||
newProp->types.sweep(zone(), oom);
|
||||
newProp->types.sweep(zone(), *oom);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
*oom = true;
|
||||
oom->setOOM();
|
||||
addFlags(OBJECT_FLAG_DYNAMIC_MASK | OBJECT_FLAG_UNKNOWN_PROPERTIES);
|
||||
clearProperties();
|
||||
return;
|
||||
|
@ -4677,9 +4718,9 @@ TypeObject::sweep(FreeOp *fop, bool *oom)
|
|||
Property *newProp = typeLifoAlloc.new_<Property>(*prop);
|
||||
if (newProp) {
|
||||
propertySet = (Property **) newProp;
|
||||
newProp->types.sweep(zone(), oom);
|
||||
newProp->types.sweep(zone(), *oom);
|
||||
} else {
|
||||
*oom = true;
|
||||
oom->setOOM();
|
||||
addFlags(OBJECT_FLAG_DYNAMIC_MASK | OBJECT_FLAG_UNKNOWN_PROPERTIES);
|
||||
clearProperties();
|
||||
return;
|
||||
|
@ -4917,17 +4958,51 @@ TypeCompartment::~TypeCompartment()
|
|||
}
|
||||
|
||||
/* static */ void
|
||||
TypeScript::Sweep(FreeOp *fop, JSScript *script, bool *oom)
|
||||
JSScript::maybeSweepTypes(AutoClearTypeInferenceStateOnOOM *oom)
|
||||
{
|
||||
JSCompartment *compartment = script->compartment();
|
||||
MOZ_ASSERT(compartment->zone()->isGCSweepingOrCompacting());
|
||||
if (!types_ || typesGeneration() == zone()->types.generation)
|
||||
return;
|
||||
|
||||
unsigned num = NumTypeSets(script);
|
||||
StackTypeSet *typeArray = script->types->typeArray();
|
||||
setTypesGeneration(zone()->types.generation);
|
||||
|
||||
/* Remove constraints and references to dead objects from the persistent type sets. */
|
||||
MOZ_ASSERT(zone()->isGCSweepingOrCompacting());
|
||||
MOZ_ASSERT(!zone()->runtimeFromMainThread()->isHeapMinorCollecting());
|
||||
|
||||
Maybe<AutoClearTypeInferenceStateOnOOM> fallbackOOM;
|
||||
EnsureHasAutoClearTypeInferenceStateOnOOM(oom, zone(), fallbackOOM);
|
||||
|
||||
TypeZone &types = zone()->types;
|
||||
|
||||
// Destroy all type information attached to the script if desired. We can
|
||||
// only do this if nothing has been compiled for the script, which will be
|
||||
// the case unless the script has been compiled since we started sweeping.
|
||||
if (types.sweepReleaseTypes &&
|
||||
!hasBaselineScript() &&
|
||||
!hasIonScript() &&
|
||||
!hasParallelIonScript())
|
||||
{
|
||||
types_->destroy();
|
||||
types_ = nullptr;
|
||||
|
||||
// Freeze constraints on stack type sets need to be regenerated the
|
||||
// next time the script is analyzed.
|
||||
hasFreezeConstraints_ = false;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned num = TypeScript::NumTypeSets(this);
|
||||
StackTypeSet *typeArray = types_->typeArray();
|
||||
|
||||
// Remove constraints and references to dead objects from stack type sets.
|
||||
for (unsigned i = 0; i < num; i++)
|
||||
typeArray[i].sweep(compartment->zone(), oom);
|
||||
typeArray[i].sweep(zone(), *oom);
|
||||
|
||||
// Update the recompile indexes in any IonScripts still on the script.
|
||||
if (hasIonScript())
|
||||
ionScript()->recompileInfoRef().shouldSweep(types);
|
||||
if (hasParallelIonScript())
|
||||
parallelIonScript()->recompileInfoRef().shouldSweep(types);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -4985,115 +5060,83 @@ TypeObject::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
|
|||
TypeZone::TypeZone(Zone *zone)
|
||||
: zone_(zone),
|
||||
typeLifoAlloc(TYPE_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
|
||||
generation(0),
|
||||
compilerOutputs(nullptr),
|
||||
pendingRecompiles(nullptr)
|
||||
sweepTypeLifoAlloc(TYPE_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
|
||||
sweepCompilerOutputs(nullptr),
|
||||
sweepReleaseTypes(false),
|
||||
activeAnalysis(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
TypeZone::~TypeZone()
|
||||
{
|
||||
js_delete(compilerOutputs);
|
||||
js_delete(pendingRecompiles);
|
||||
js_delete(sweepCompilerOutputs);
|
||||
}
|
||||
|
||||
void
|
||||
TypeZone::sweep(FreeOp *fop, bool releaseTypes, bool *oom)
|
||||
TypeZone::beginSweep(FreeOp *fop, bool releaseTypes, AutoClearTypeInferenceStateOnOOM &oom)
|
||||
{
|
||||
MOZ_ASSERT(zone()->isGCSweepingOrCompacting());
|
||||
MOZ_ASSERT(!sweepCompilerOutputs);
|
||||
MOZ_ASSERT(!sweepReleaseTypes);
|
||||
|
||||
JSRuntime *rt = fop->runtime();
|
||||
sweepReleaseTypes = releaseTypes;
|
||||
|
||||
/*
|
||||
* Clear the analysis pool, but don't release its data yet. While
|
||||
* sweeping types any live data will be allocated into the pool.
|
||||
*/
|
||||
LifoAlloc oldAlloc(typeLifoAlloc.defaultChunkSize());
|
||||
oldAlloc.steal(&typeLifoAlloc);
|
||||
|
||||
/* Sweep and find compressed indexes for each compiler output. */
|
||||
size_t newCompilerOutputCount = 0;
|
||||
// Clear the analysis pool, but don't release its data yet. While sweeping
|
||||
// types any live data will be allocated into the pool.
|
||||
sweepTypeLifoAlloc.steal(&typeLifoAlloc);
|
||||
|
||||
// Sweep any invalid or dead compiler outputs, and keep track of the new
|
||||
// index for remaining live outputs.
|
||||
if (compilerOutputs) {
|
||||
CompilerOutputVector *newCompilerOutputs = nullptr;
|
||||
for (size_t i = 0; i < compilerOutputs->length(); i++) {
|
||||
CompilerOutput &output = (*compilerOutputs)[i];
|
||||
if (output.isValid()) {
|
||||
JSScript *script = output.script();
|
||||
ExecutionMode mode = output.mode();
|
||||
if (IsScriptAboutToBeFinalized(&script)) {
|
||||
jit::GetIonScript(script, output.mode())->recompileInfoRef() = RecompileInfo(uint32_t(-1));
|
||||
jit::GetIonScript(script, mode)->recompileInfoRef() = RecompileInfo();
|
||||
output.invalidate();
|
||||
} else {
|
||||
output.setSweepIndex(newCompilerOutputCount++);
|
||||
CompilerOutput newOutput(script, output.mode());
|
||||
|
||||
if (!newCompilerOutputs)
|
||||
newCompilerOutputs = js_new<CompilerOutputVector>();
|
||||
if (newCompilerOutputs && newCompilerOutputs->append(newOutput)) {
|
||||
output.setSweepIndex(newCompilerOutputs->length() - 1);
|
||||
} else {
|
||||
oom.setOOM();
|
||||
jit::GetIonScript(script, mode)->recompileInfoRef() = RecompileInfo();
|
||||
output.invalidate();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
sweepCompilerOutputs = compilerOutputs;
|
||||
compilerOutputs = newCompilerOutputs;
|
||||
}
|
||||
|
||||
{
|
||||
gcstats::AutoPhase ap2(rt->gc.stats, !rt->isHeapCompacting(),
|
||||
gcstats::PHASE_DISCARD_TI);
|
||||
// All existing RecompileInfos are stale and will be updated to the new
|
||||
// compiler outputs list later during the sweep. Don't worry about overflow
|
||||
// here, since stale indexes will persist only until the sweep finishes.
|
||||
generation++;
|
||||
|
||||
for (ZoneCellIterUnderGC i(zone(), FINALIZE_SCRIPT); !i.done(); i.next()) {
|
||||
JSScript *script = i.get<JSScript>();
|
||||
if (script->types) {
|
||||
types::TypeScript::Sweep(fop, script, oom);
|
||||
for (CompartmentsInZoneIter comp(zone()); !comp.done(); comp.next())
|
||||
comp->types.sweep(fop);
|
||||
}
|
||||
|
||||
if (releaseTypes) {
|
||||
script->types->destroy();
|
||||
script->types = nullptr;
|
||||
void
|
||||
TypeZone::endSweep(JSRuntime *rt)
|
||||
{
|
||||
js_delete(sweepCompilerOutputs);
|
||||
sweepCompilerOutputs = nullptr;
|
||||
|
||||
/*
|
||||
* Freeze constraints on stack type sets need to be
|
||||
* regenerated the next time the script is analyzed.
|
||||
*/
|
||||
script->clearHasFreezeConstraints();
|
||||
sweepReleaseTypes = false;
|
||||
|
||||
MOZ_ASSERT(!script->hasIonScript());
|
||||
MOZ_ASSERT(!script->hasParallelIonScript());
|
||||
} else {
|
||||
/* Update the recompile indexes in any IonScripts still on the script. */
|
||||
if (script->hasIonScript())
|
||||
script->ionScript()->recompileInfoRef().shouldSweep(*this);
|
||||
if (script->hasParallelIonScript())
|
||||
script->parallelIonScript()->recompileInfoRef().shouldSweep(*this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
gcstats::AutoPhase ap2(rt->gc.stats, !rt->isHeapCompacting(),
|
||||
gcstats::PHASE_SWEEP_TYPES);
|
||||
|
||||
for (gc::ZoneCellIterUnderGC iter(zone(), gc::FINALIZE_TYPE_OBJECT);
|
||||
!iter.done(); iter.next())
|
||||
{
|
||||
TypeObject *object = iter.get<TypeObject>();
|
||||
object->sweep(fop, oom);
|
||||
}
|
||||
|
||||
for (CompartmentsInZoneIter comp(zone()); !comp.done(); comp.next())
|
||||
comp->types.sweep(fop);
|
||||
}
|
||||
|
||||
if (compilerOutputs) {
|
||||
size_t sweepIndex = 0;
|
||||
for (size_t i = 0; i < compilerOutputs->length(); i++) {
|
||||
CompilerOutput output = (*compilerOutputs)[i];
|
||||
if (output.isValid()) {
|
||||
MOZ_ASSERT(sweepIndex == output.sweepIndex());
|
||||
output.invalidateSweepIndex();
|
||||
(*compilerOutputs)[sweepIndex++] = output;
|
||||
}
|
||||
}
|
||||
MOZ_ASSERT(sweepIndex == newCompilerOutputCount);
|
||||
JS_ALWAYS_TRUE(compilerOutputs->resize(newCompilerOutputCount));
|
||||
}
|
||||
|
||||
{
|
||||
gcstats::AutoPhase ap2(rt->gc.stats, !rt->isHeapCompacting(),
|
||||
gcstats::PHASE_FREE_TI_ARENA);
|
||||
rt->freeLifoAlloc.transferFrom(&oldAlloc);
|
||||
}
|
||||
rt->freeLifoAlloc.transferFrom(&sweepTypeLifoAlloc);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -5103,7 +5146,17 @@ TypeZone::clearAllNewScriptsOnOOM()
|
|||
!iter.done(); iter.next())
|
||||
{
|
||||
TypeObject *object = iter.get<TypeObject>();
|
||||
object->maybeClearNewScriptOnOOM();
|
||||
if (!IsTypeObjectAboutToBeFinalized(&object))
|
||||
object->maybeClearNewScriptOnOOM();
|
||||
}
|
||||
}
|
||||
|
||||
AutoClearTypeInferenceStateOnOOM::~AutoClearTypeInferenceStateOnOOM()
|
||||
{
|
||||
if (oom) {
|
||||
zone->setPreservingCode(false);
|
||||
zone->discardJitCode(zone->runtimeFromMainThread()->defaultFreeOp());
|
||||
zone->types.clearAllNewScriptsOnOOM();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5111,12 +5164,12 @@ TypeZone::clearAllNewScriptsOnOOM()
|
|||
void
|
||||
TypeScript::printTypes(JSContext *cx, HandleScript script) const
|
||||
{
|
||||
MOZ_ASSERT(script->types == this);
|
||||
MOZ_ASSERT(script->types() == this);
|
||||
|
||||
if (!script->hasBaselineScript())
|
||||
return;
|
||||
|
||||
AutoEnterAnalysis enter(nullptr, script->compartment());
|
||||
AutoEnterAnalysis enter(nullptr, script->zone());
|
||||
|
||||
if (script->functionNonDelazifying())
|
||||
fprintf(stderr, "Function");
|
||||
|
@ -5168,5 +5221,6 @@ TypeScript::printTypes(JSContext *cx, HandleScript script) const
|
|||
void
|
||||
TypeObject::setNewScript(TypeNewScript *newScript)
|
||||
{
|
||||
MOZ_ASSERT(!needsSweep());
|
||||
newScript_ = newScript;
|
||||
}
|
||||
|
|
134
js/src/jsinfer.h
134
js/src/jsinfer.h
|
@ -503,7 +503,12 @@ enum MOZ_ENUM_TYPE(uint32_t) {
|
|||
/* Mask for objects created with unknown properties. */
|
||||
OBJECT_FLAG_UNKNOWN_MASK =
|
||||
OBJECT_FLAG_DYNAMIC_MASK
|
||||
| OBJECT_FLAG_SETS_MARKED_UNKNOWN
|
||||
| OBJECT_FLAG_SETS_MARKED_UNKNOWN,
|
||||
|
||||
// Mask/shift for this type object's generation. If out of sync with the
|
||||
// TypeZone's generation, this TypeObject hasn't been swept yet.
|
||||
OBJECT_FLAG_GENERATION_MASK = 0x02000000,
|
||||
OBJECT_FLAG_GENERATION_SHIFT = 25,
|
||||
};
|
||||
typedef uint32_t TypeObjectFlags;
|
||||
|
||||
|
@ -648,6 +653,29 @@ class TypeSet
|
|||
void clearObjects();
|
||||
};
|
||||
|
||||
// If there is an OOM while sweeping types, the type information is deoptimized
|
||||
// so that it stays correct (i.e. overapproximates the possible types in the
|
||||
// zone), but constraints might not have been triggered on the deoptimization
|
||||
// or even copied over completely. In this case, destroy all JIT code and new
|
||||
// script information in the zone, the only things whose correctness depends on
|
||||
// the type constraints.
|
||||
class AutoClearTypeInferenceStateOnOOM
|
||||
{
|
||||
Zone *zone;
|
||||
bool oom;
|
||||
|
||||
public:
|
||||
AutoClearTypeInferenceStateOnOOM(Zone *zone)
|
||||
: zone(zone), oom(false)
|
||||
{}
|
||||
|
||||
~AutoClearTypeInferenceStateOnOOM();
|
||||
|
||||
void setOOM() {
|
||||
oom = true;
|
||||
}
|
||||
};
|
||||
|
||||
/* Superclass common to stack and heap type sets. */
|
||||
class ConstraintTypeSet : public TypeSet
|
||||
{
|
||||
|
@ -666,7 +694,7 @@ class ConstraintTypeSet : public TypeSet
|
|||
/* Add a new constraint to this set. */
|
||||
bool addConstraint(JSContext *cx, TypeConstraint *constraint, bool callExisting = true);
|
||||
|
||||
inline void sweep(JS::Zone *zone, bool *oom);
|
||||
inline void sweep(JS::Zone *zone, AutoClearTypeInferenceStateOnOOM &oom);
|
||||
};
|
||||
|
||||
class StackTypeSet : public ConstraintTypeSet
|
||||
|
@ -968,7 +996,7 @@ class TypeNewScript
|
|||
}
|
||||
|
||||
void trace(JSTracer *trc);
|
||||
void sweep(FreeOp *fop);
|
||||
void sweep();
|
||||
|
||||
#ifdef JSGC_COMPACTING
|
||||
void fixupAfterMovingGC();
|
||||
|
@ -1072,19 +1100,23 @@ struct TypeObject : public gc::TenuredCell
|
|||
|
||||
public:
|
||||
|
||||
TypeObjectFlags flags() const {
|
||||
TypeObjectFlags flags() {
|
||||
maybeSweep(nullptr);
|
||||
return flags_;
|
||||
}
|
||||
|
||||
void addFlags(TypeObjectFlags flags) {
|
||||
MOZ_ASSERT(!needsSweep());
|
||||
flags_ |= flags;
|
||||
}
|
||||
|
||||
void clearFlags(TypeObjectFlags flags) {
|
||||
MOZ_ASSERT(!needsSweep());
|
||||
flags_ &= ~flags;
|
||||
}
|
||||
|
||||
TypeNewScript *newScript() {
|
||||
maybeSweep(nullptr);
|
||||
return newScript_;
|
||||
}
|
||||
|
||||
|
@ -1155,7 +1187,7 @@ struct TypeObject : public gc::TenuredCell
|
|||
return hasAnyFlags(OBJECT_FLAG_PRE_TENURE) && !unknownProperties();
|
||||
}
|
||||
|
||||
bool hasTenuredProto() const {
|
||||
bool hasTenuredProto() {
|
||||
return !(flags() & OBJECT_FLAG_NURSERY_PROTO);
|
||||
}
|
||||
|
||||
|
@ -1211,7 +1243,23 @@ struct TypeObject : public gc::TenuredCell
|
|||
void print();
|
||||
|
||||
inline void clearProperties();
|
||||
inline void sweep(FreeOp *fop, bool *oom);
|
||||
void maybeSweep(AutoClearTypeInferenceStateOnOOM *oom);
|
||||
|
||||
private:
|
||||
#ifdef DEBUG
|
||||
bool needsSweep();
|
||||
#endif
|
||||
|
||||
uint32_t generation() {
|
||||
return (flags_ & OBJECT_FLAG_GENERATION_MASK) >> OBJECT_FLAG_GENERATION_SHIFT;
|
||||
}
|
||||
|
||||
public:
|
||||
void setGeneration(uint32_t generation) {
|
||||
MOZ_ASSERT(generation <= (OBJECT_FLAG_GENERATION_MASK >> OBJECT_FLAG_GENERATION_SHIFT));
|
||||
flags_ &= ~OBJECT_FLAG_GENERATION_MASK;
|
||||
flags_ |= generation << OBJECT_FLAG_GENERATION_SHIFT;
|
||||
}
|
||||
|
||||
#ifdef JSGC_COMPACTING
|
||||
void fixupAfterMovingGC();
|
||||
|
@ -1219,12 +1267,7 @@ struct TypeObject : public gc::TenuredCell
|
|||
|
||||
size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
|
||||
|
||||
/*
|
||||
* Type objects don't have explicit finalizers. Memory owned by a type
|
||||
* object pending deletion is released when weak references are sweeped
|
||||
* from all the compartment's type objects.
|
||||
*/
|
||||
void finalize(FreeOp *fop) {}
|
||||
inline void finalize(FreeOp *fop);
|
||||
|
||||
static inline ThingRootKind rootKind() { return THING_ROOT_TYPE_OBJECT; }
|
||||
|
||||
|
@ -1241,7 +1284,7 @@ struct TypeObject : public gc::TenuredCell
|
|||
}
|
||||
|
||||
private:
|
||||
inline uint32_t basePropertyCount() const;
|
||||
inline uint32_t basePropertyCount();
|
||||
inline void setBasePropertyCount(uint32_t count);
|
||||
|
||||
static void staticAsserts() {
|
||||
|
@ -1323,11 +1366,11 @@ class TypeScript
|
|||
StackTypeSet typeArray_[1];
|
||||
|
||||
public:
|
||||
/* Array of type type sets for variables and JOF_TYPESET ops. */
|
||||
/* Array of type sets for variables and JOF_TYPESET ops. */
|
||||
StackTypeSet *typeArray() const {
|
||||
// Ensure typeArray_ is the last data member of TypeScript.
|
||||
JS_STATIC_ASSERT(sizeof(TypeScript) ==
|
||||
sizeof(typeArray_) + offsetof(TypeScript, typeArray_));
|
||||
sizeof(typeArray_) + offsetof(TypeScript, typeArray_));
|
||||
return const_cast<StackTypeSet *>(typeArray_);
|
||||
}
|
||||
|
||||
|
@ -1387,7 +1430,6 @@ class TypeScript
|
|||
|
||||
static void Purge(JSContext *cx, HandleScript script);
|
||||
|
||||
static void Sweep(FreeOp *fop, JSScript *script, bool *oom);
|
||||
void destroy();
|
||||
|
||||
size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
|
||||
|
@ -1583,9 +1625,6 @@ class CompilerOutput
|
|||
MOZ_CRASH();
|
||||
sweepIndex_ = index;
|
||||
}
|
||||
void invalidateSweepIndex() {
|
||||
sweepIndex_ = INVALID_SWEEP_INDEX;
|
||||
}
|
||||
uint32_t sweepIndex() {
|
||||
MOZ_ASSERT(sweepIndex_ != INVALID_SWEEP_INDEX);
|
||||
return sweepIndex_;
|
||||
|
@ -1594,26 +1633,33 @@ class CompilerOutput
|
|||
|
||||
class RecompileInfo
|
||||
{
|
||||
uint32_t outputIndex;
|
||||
// Index in the TypeZone's compilerOutputs or sweepCompilerOutputs arrays,
|
||||
// depending on the generation value.
|
||||
uint32_t outputIndex : 31;
|
||||
|
||||
// If out of sync with the TypeZone's generation, this index is for the
|
||||
// zone's sweepCompilerOutputs rather than compilerOutputs.
|
||||
uint32_t generation : 1;
|
||||
|
||||
public:
|
||||
explicit RecompileInfo(uint32_t outputIndex = uint32_t(-1))
|
||||
: outputIndex(outputIndex)
|
||||
RecompileInfo(uint32_t outputIndex, uint32_t generation)
|
||||
: outputIndex(outputIndex), generation(generation)
|
||||
{}
|
||||
|
||||
RecompileInfo()
|
||||
: outputIndex(JS_BITMASK(31)), generation(0)
|
||||
{}
|
||||
|
||||
bool operator == (const RecompileInfo &o) const {
|
||||
return outputIndex == o.outputIndex;
|
||||
}
|
||||
CompilerOutput *compilerOutput(TypeZone &types) const;
|
||||
CompilerOutput *compilerOutput(JSContext *cx) const;
|
||||
bool shouldSweep(TypeZone &types);
|
||||
};
|
||||
|
||||
typedef Vector<RecompileInfo, 0, SystemAllocPolicy> RecompileInfoVector;
|
||||
|
||||
/* Type information for a compartment. */
|
||||
struct TypeCompartment
|
||||
{
|
||||
/* Constraint solving worklist structures. */
|
||||
|
||||
/* Number of scripts in this compartment. */
|
||||
unsigned scriptCount;
|
||||
|
||||
|
@ -1621,7 +1667,6 @@ struct TypeCompartment
|
|||
AllocationSiteTable *allocationSiteTable;
|
||||
|
||||
/* Tables for determining types of singleton/JSON objects. */
|
||||
|
||||
ArrayTypeTable *arrayTypeTable;
|
||||
ObjectTypeTable *objectTypeTable;
|
||||
|
||||
|
@ -1670,37 +1715,56 @@ struct TypeCompartment
|
|||
|
||||
void FixRestArgumentsType(ExclusiveContext *cxArg, ArrayObject *obj);
|
||||
|
||||
struct AutoEnterAnalysis;
|
||||
|
||||
struct TypeZone
|
||||
{
|
||||
JS::Zone *zone_;
|
||||
JS::Zone *zone_;
|
||||
|
||||
/* Pool for type information in this zone. */
|
||||
static const size_t TYPE_LIFO_ALLOC_PRIMARY_CHUNK_SIZE = 8 * 1024;
|
||||
js::LifoAlloc typeLifoAlloc;
|
||||
LifoAlloc typeLifoAlloc;
|
||||
|
||||
// Current generation for sweeping.
|
||||
uint32_t generation : 1;
|
||||
|
||||
/*
|
||||
* All Ion compilations that have occured in this zone, for indexing via
|
||||
* RecompileInfo. This includes both valid and invalid compilations, though
|
||||
* invalidated compilations are swept on GC.
|
||||
*/
|
||||
Vector<CompilerOutput> *compilerOutputs;
|
||||
typedef Vector<CompilerOutput, 4, SystemAllocPolicy> CompilerOutputVector;
|
||||
CompilerOutputVector *compilerOutputs;
|
||||
|
||||
/* Pending recompilations to perform before execution of JIT code can resume. */
|
||||
Vector<RecompileInfo> *pendingRecompiles;
|
||||
// During incremental sweeping, allocator holding the old type information
|
||||
// for the zone.
|
||||
LifoAlloc sweepTypeLifoAlloc;
|
||||
|
||||
// During incremental sweeping, the old compiler outputs for use by
|
||||
// recompile indexes with a stale generation.
|
||||
CompilerOutputVector *sweepCompilerOutputs;
|
||||
|
||||
// During incremental sweeping, whether to try to destroy all type
|
||||
// information attached to scripts.
|
||||
bool sweepReleaseTypes;
|
||||
|
||||
// The topmost AutoEnterAnalysis on the stack, if there is one.
|
||||
AutoEnterAnalysis *activeAnalysis;
|
||||
|
||||
explicit TypeZone(JS::Zone *zone);
|
||||
~TypeZone();
|
||||
|
||||
JS::Zone *zone() const { return zone_; }
|
||||
|
||||
void sweep(FreeOp *fop, bool releaseTypes, bool *oom);
|
||||
void beginSweep(FreeOp *fop, bool releaseTypes, AutoClearTypeInferenceStateOnOOM &oom);
|
||||
void endSweep(JSRuntime *rt);
|
||||
void clearAllNewScriptsOnOOM();
|
||||
|
||||
/* Mark a script as needing recompilation once inference has finished. */
|
||||
void addPendingRecompile(JSContext *cx, const RecompileInfo &info);
|
||||
void addPendingRecompile(JSContext *cx, JSScript *script);
|
||||
|
||||
void processPendingRecompiles(FreeOp *fop);
|
||||
void processPendingRecompiles(FreeOp *fop, RecompileInfoVector &recompiles);
|
||||
};
|
||||
|
||||
enum SpewChannel {
|
||||
|
|
|
@ -48,9 +48,20 @@ CompilerOutput::ion() const
|
|||
inline CompilerOutput*
|
||||
RecompileInfo::compilerOutput(TypeZone &types) const
|
||||
{
|
||||
if (generation != types.generation) {
|
||||
if (!types.sweepCompilerOutputs || outputIndex >= types.sweepCompilerOutputs->length())
|
||||
return nullptr;
|
||||
CompilerOutput *output = &(*types.sweepCompilerOutputs)[outputIndex];
|
||||
if (!output->isValid())
|
||||
return nullptr;
|
||||
output = &(*types.compilerOutputs)[output->sweepIndex()];
|
||||
return output->isValid() ? output : nullptr;
|
||||
}
|
||||
|
||||
if (!types.compilerOutputs || outputIndex >= types.compilerOutputs->length())
|
||||
return nullptr;
|
||||
return &(*types.compilerOutputs)[outputIndex];
|
||||
CompilerOutput *output = &(*types.compilerOutputs)[outputIndex];
|
||||
return output->isValid() ? output : nullptr;
|
||||
}
|
||||
|
||||
inline CompilerOutput*
|
||||
|
@ -66,8 +77,14 @@ RecompileInfo::shouldSweep(TypeZone &types)
|
|||
if (!output || !output->isValid())
|
||||
return true;
|
||||
|
||||
// Update this info for the output's new index in the zone's compiler outputs.
|
||||
outputIndex = output->sweepIndex();
|
||||
// If this info is for a compilation that occurred after sweeping started,
|
||||
// the index is already correct.
|
||||
MOZ_ASSERT_IF(generation == types.generation,
|
||||
outputIndex == output - types.compilerOutputs->begin());
|
||||
|
||||
// Update this info for the output's index in the zone's compiler outputs.
|
||||
outputIndex = output - types.compilerOutputs->begin();
|
||||
generation = types.generation;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -241,44 +258,45 @@ struct AutoEnterAnalysis
|
|||
/* Prevent GC activity in the middle of analysis. */
|
||||
gc::AutoSuppressGC suppressGC;
|
||||
|
||||
// Allow clearing inference info on OOM during incremental sweeping.
|
||||
AutoClearTypeInferenceStateOnOOM oom;
|
||||
|
||||
// Pending recompilations to perform before execution of JIT code can resume.
|
||||
RecompileInfoVector pendingRecompiles;
|
||||
|
||||
FreeOp *freeOp;
|
||||
JSCompartment *compartment;
|
||||
bool oldActiveAnalysis;
|
||||
Zone *zone;
|
||||
|
||||
explicit AutoEnterAnalysis(ExclusiveContext *cx)
|
||||
: suppressGC(cx)
|
||||
: suppressGC(cx), oom(cx->zone())
|
||||
{
|
||||
init(cx->defaultFreeOp(), cx->compartment());
|
||||
init(cx->defaultFreeOp(), cx->zone());
|
||||
}
|
||||
|
||||
AutoEnterAnalysis(FreeOp *fop, JSCompartment *comp)
|
||||
: suppressGC(comp)
|
||||
AutoEnterAnalysis(FreeOp *fop, Zone *zone)
|
||||
: suppressGC(zone->runtimeFromMainThread()), oom(zone)
|
||||
{
|
||||
init(fop, comp);
|
||||
init(fop, zone);
|
||||
}
|
||||
|
||||
~AutoEnterAnalysis()
|
||||
{
|
||||
compartment->activeAnalysis = oldActiveAnalysis;
|
||||
if (this != zone->types.activeAnalysis)
|
||||
return;
|
||||
|
||||
/*
|
||||
* If there are no more type inference activations on the stack,
|
||||
* process any triggered recompilations. Note that we should not be
|
||||
* invoking any scripted code while type inference is running.
|
||||
*/
|
||||
if (!compartment->activeAnalysis) {
|
||||
TypeZone &types = compartment->zone()->types;
|
||||
if (types.pendingRecompiles)
|
||||
types.processPendingRecompiles(freeOp);
|
||||
}
|
||||
zone->types.activeAnalysis = nullptr;
|
||||
|
||||
if (!pendingRecompiles.empty())
|
||||
zone->types.processPendingRecompiles(freeOp, pendingRecompiles);
|
||||
}
|
||||
|
||||
private:
|
||||
void init(FreeOp *fop, JSCompartment *comp) {
|
||||
freeOp = fop;
|
||||
compartment = comp;
|
||||
oldActiveAnalysis = compartment->activeAnalysis;
|
||||
compartment->activeAnalysis = true;
|
||||
void init(FreeOp *fop, Zone *zone) {
|
||||
this->freeOp = fop;
|
||||
this->zone = zone;
|
||||
|
||||
if (!zone->types.activeAnalysis)
|
||||
zone->types.activeAnalysis = this;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -380,7 +398,7 @@ TypeMonitorCall(JSContext *cx, const js::CallArgs &args, bool constructing)
|
|||
{
|
||||
if (args.callee().is<JSFunction>()) {
|
||||
JSFunction *fun = &args.callee().as<JSFunction>();
|
||||
if (fun->isInterpreted() && fun->nonLazyScript()->types)
|
||||
if (fun->isInterpreted() && fun->nonLazyScript()->types())
|
||||
TypeMonitorCallSlow(cx, &args.callee(), args, constructing);
|
||||
}
|
||||
}
|
||||
|
@ -580,7 +598,8 @@ TypeScript::NumTypeSets(JSScript *script)
|
|||
/* static */ inline StackTypeSet *
|
||||
TypeScript::ThisTypes(JSScript *script)
|
||||
{
|
||||
return script->types->typeArray() + script->nTypeSets();
|
||||
TypeScript *types = script->types();
|
||||
return types ? types->typeArray() + script->nTypeSets() : nullptr;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -593,7 +612,8 @@ TypeScript::ThisTypes(JSScript *script)
|
|||
TypeScript::ArgTypes(JSScript *script, unsigned i)
|
||||
{
|
||||
MOZ_ASSERT(i < script->functionNonDelazifying()->nargs());
|
||||
return script->types->typeArray() + script->nTypeSets() + 1 + i;
|
||||
TypeScript *types = script->types();
|
||||
return types ? types->typeArray() + script->nTypeSets() + 1 + i : nullptr;
|
||||
}
|
||||
|
||||
template <typename TYPESET>
|
||||
|
@ -641,9 +661,12 @@ TypeScript::BytecodeTypes(JSScript *script, jsbytecode *pc, uint32_t *bytecodeMa
|
|||
TypeScript::BytecodeTypes(JSScript *script, jsbytecode *pc)
|
||||
{
|
||||
MOZ_ASSERT(CurrentThreadCanAccessRuntime(script->runtimeFromMainThread()));
|
||||
TypeScript *types = script->types();
|
||||
if (!types)
|
||||
return nullptr;
|
||||
uint32_t *hint = script->baselineScript()->bytecodeTypeMap() + script->nTypeSets();
|
||||
return BytecodeTypes(script, pc, script->baselineScript()->bytecodeTypeMap(),
|
||||
hint, script->types->typeArray());
|
||||
hint, types->typeArray());
|
||||
}
|
||||
|
||||
struct AllocationSiteKey : public DefaultHasher<AllocationSiteKey> {
|
||||
|
@ -767,15 +790,16 @@ TypeScript::MonitorAssign(JSContext *cx, HandleObject obj, jsid id)
|
|||
/* static */ inline void
|
||||
TypeScript::SetThis(JSContext *cx, JSScript *script, Type type)
|
||||
{
|
||||
if (!script->types)
|
||||
StackTypeSet *types = ThisTypes(script);
|
||||
if (!types)
|
||||
return;
|
||||
|
||||
if (!ThisTypes(script)->hasType(type)) {
|
||||
if (!types->hasType(type)) {
|
||||
AutoEnterAnalysis enter(cx);
|
||||
|
||||
InferSpew(ISpewOps, "externalType: setThis #%u: %s",
|
||||
script->id(), TypeString(type));
|
||||
ThisTypes(script)->addType(cx, type);
|
||||
types->addType(cx, type);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -788,15 +812,16 @@ TypeScript::SetThis(JSContext *cx, JSScript *script, const js::Value &value)
|
|||
/* static */ inline void
|
||||
TypeScript::SetArgument(JSContext *cx, JSScript *script, unsigned arg, Type type)
|
||||
{
|
||||
if (!script->types)
|
||||
StackTypeSet *types = ArgTypes(script, arg);
|
||||
if (!types)
|
||||
return;
|
||||
|
||||
if (!ArgTypes(script, arg)->hasType(type)) {
|
||||
if (!types->hasType(type)) {
|
||||
AutoEnterAnalysis enter(cx);
|
||||
|
||||
InferSpew(ISpewOps, "externalType: setArg #%u %u: %s",
|
||||
script->id(), arg, TypeString(type));
|
||||
ArgTypes(script, arg)->addType(cx, type);
|
||||
types->addType(cx, type);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1173,11 +1198,19 @@ inline TypeObject::TypeObject(const Class *clasp, TaggedProto proto, TypeObjectF
|
|||
this->proto_ = proto.raw();
|
||||
this->flags_ = initialFlags;
|
||||
|
||||
setGeneration(zone()->types.generation);
|
||||
|
||||
InferSpew(ISpewOps, "newObject: %s", TypeObjectString(this));
|
||||
}
|
||||
|
||||
inline void
|
||||
TypeObject::finalize(FreeOp *fop)
|
||||
{
|
||||
fop->delete_(newScript_.get());
|
||||
}
|
||||
|
||||
inline uint32_t
|
||||
TypeObject::basePropertyCount() const
|
||||
TypeObject::basePropertyCount()
|
||||
{
|
||||
return (flags() & OBJECT_FLAG_PROPERTY_COUNT_MASK) >> OBJECT_FLAG_PROPERTY_COUNT_SHIFT;
|
||||
}
|
||||
|
@ -1194,8 +1227,6 @@ TypeObject::setBasePropertyCount(uint32_t count)
|
|||
inline HeapTypeSet *
|
||||
TypeObject::getProperty(ExclusiveContext *cx, jsid id)
|
||||
{
|
||||
MOZ_ASSERT(cx->compartment()->activeAnalysis);
|
||||
|
||||
MOZ_ASSERT(JSID_IS_VOID(id) || JSID_IS_EMPTY(id) || JSID_IS_STRING(id) || JSID_IS_SYMBOL(id));
|
||||
MOZ_ASSERT_IF(!JSID_IS_EMPTY(id), id == IdToTypeId(id));
|
||||
MOZ_ASSERT(!unknownProperties());
|
||||
|
@ -1282,10 +1313,17 @@ TypeNewScript::writeBarrierPre(TypeNewScript *newScript)
|
|||
|
||||
} } /* namespace js::types */
|
||||
|
||||
inline js::types::TypeScript *
|
||||
JSScript::types()
|
||||
{
|
||||
maybeSweepTypes(nullptr);
|
||||
return types_;
|
||||
}
|
||||
|
||||
inline bool
|
||||
JSScript::ensureHasTypes(JSContext *cx)
|
||||
{
|
||||
return types || makeTypes(cx);
|
||||
return types() || makeTypes(cx);
|
||||
}
|
||||
|
||||
namespace js {
|
||||
|
|
|
@ -804,7 +804,7 @@ NewObjectMetadata(ExclusiveContext *cxArg, JSObject **pmetadata)
|
|||
MOZ_ASSERT(!*pmetadata);
|
||||
if (JSContext *cx = cxArg->maybeJSContext()) {
|
||||
if (MOZ_UNLIKELY((size_t)cx->compartment()->hasObjectMetadataCallback()) &&
|
||||
!cx->compartment()->activeAnalysis)
|
||||
!cx->zone()->types.activeAnalysis)
|
||||
{
|
||||
// Use AutoEnterAnalysis to prohibit both any GC activity under the
|
||||
// callback, and any reentering of JS via Invoke() etc.
|
||||
|
|
|
@ -1001,7 +1001,7 @@ js_Disassemble1(JSContext *cx, HandleScript script, jsbytecode *pc,
|
|||
|
||||
case JOF_OBJECT: {
|
||||
/* Don't call obj.toSource if analysis/inference is active. */
|
||||
if (script->compartment()->activeAnalysis) {
|
||||
if (script->zone()->types.activeAnalysis) {
|
||||
Sprint(sp, " object");
|
||||
break;
|
||||
}
|
||||
|
@ -2051,7 +2051,7 @@ js::StopPCCountProfiling(JSContext *cx)
|
|||
for (ZonesIter zone(rt, SkipAtoms); !zone.done(); zone.next()) {
|
||||
for (ZoneCellIter i(zone, FINALIZE_SCRIPT); !i.done(); i.next()) {
|
||||
JSScript *script = i.get<JSScript>();
|
||||
if (script->hasScriptCounts() && script->types) {
|
||||
if (script->hasScriptCounts() && script->types()) {
|
||||
ScriptAndCounts sac;
|
||||
sac.script = script;
|
||||
sac.scriptCounts.set(script->releaseScriptCounts());
|
||||
|
|
|
@ -2642,7 +2642,7 @@ JSScript::sizeOfData(mozilla::MallocSizeOf mallocSizeOf) const
|
|||
size_t
|
||||
JSScript::sizeOfTypeScript(mozilla::MallocSizeOf mallocSizeOf) const
|
||||
{
|
||||
return types->sizeOfIncludingThis(mallocSizeOf);
|
||||
return types_->sizeOfIncludingThis(mallocSizeOf);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -2675,8 +2675,8 @@ JSScript::finalize(FreeOp *fop)
|
|||
|
||||
fop->runtime()->spsProfiler.onScriptFinalized(this);
|
||||
|
||||
if (types)
|
||||
types->destroy();
|
||||
if (types_)
|
||||
types_->destroy();
|
||||
|
||||
jit::DestroyIonScripts(fop, this);
|
||||
|
||||
|
|
|
@ -784,10 +784,10 @@ class JSScript : public js::gc::TenuredCell
|
|||
|
||||
JSCompartment *compartment_;
|
||||
|
||||
/* Persistent type information retained across GCs. */
|
||||
js::types::TypeScript *types;
|
||||
|
||||
private:
|
||||
/* Persistent type information retained across GCs. */
|
||||
js::types::TypeScript *types_;
|
||||
|
||||
// This script's ScriptSourceObject, or a CCW thereof.
|
||||
//
|
||||
// (When we clone a JSScript into a new compartment, we don't clone its
|
||||
|
@ -970,6 +970,13 @@ class JSScript : public js::gc::TenuredCell
|
|||
bool needsArgsAnalysis_:1;
|
||||
bool needsArgsObj_:1;
|
||||
|
||||
// Generation for this script's TypeScript. If out of sync with the
|
||||
// TypeZone's generation, the TypeScript needs to be swept.
|
||||
//
|
||||
// This should be a uint32 but is instead a bool so that MSVC packs it
|
||||
// correctly.
|
||||
bool typesGeneration_:1;
|
||||
|
||||
//
|
||||
// End of fields. Start methods.
|
||||
//
|
||||
|
@ -1199,7 +1206,6 @@ class JSScript : public js::gc::TenuredCell
|
|||
|
||||
bool hasFreezeConstraints() const { return hasFreezeConstraints_; }
|
||||
void setHasFreezeConstraints() { hasFreezeConstraints_ = true; }
|
||||
void clearHasFreezeConstraints() { hasFreezeConstraints_ = false; }
|
||||
|
||||
bool warnedAboutUndefinedProp() const { return warnedAboutUndefinedProp_; }
|
||||
void setWarnedAboutUndefinedProp() { warnedAboutUndefinedProp_ = true; }
|
||||
|
@ -1259,6 +1265,15 @@ class JSScript : public js::gc::TenuredCell
|
|||
return needsArgsObj() && !strict();
|
||||
}
|
||||
|
||||
uint32_t typesGeneration() const {
|
||||
return (uint32_t) typesGeneration_;
|
||||
}
|
||||
|
||||
void setTypesGeneration(uint32_t generation) {
|
||||
MOZ_ASSERT(generation <= 1);
|
||||
typesGeneration_ = (bool) generation;
|
||||
}
|
||||
|
||||
bool hasAnyIonScript() const {
|
||||
return hasIonScript() || hasParallelIonScript();
|
||||
}
|
||||
|
@ -1429,6 +1444,10 @@ class JSScript : public js::gc::TenuredCell
|
|||
/* Ensure the script has a TypeScript. */
|
||||
inline bool ensureHasTypes(JSContext *cx);
|
||||
|
||||
inline js::types::TypeScript *types();
|
||||
|
||||
void maybeSweepTypes(js::types::AutoClearTypeInferenceStateOnOOM *oom);
|
||||
|
||||
inline js::GlobalObject &global() const;
|
||||
js::GlobalObject &uninlinedGlobal() const;
|
||||
|
||||
|
|
|
@ -210,7 +210,7 @@ class ForkJoinOperation
|
|||
TrafficLight appendCallTargetsToWorklist(uint32_t index, ExecutionStatus *status);
|
||||
TrafficLight appendCallTargetToWorklist(HandleScript script, ExecutionStatus *status);
|
||||
bool addToWorklist(HandleScript script);
|
||||
inline bool hasScript(Vector<types::RecompileInfo> &scripts, JSScript *script);
|
||||
inline bool hasScript(const types::RecompileInfoVector &scripts, JSScript *script);
|
||||
}; // class ForkJoinOperation
|
||||
|
||||
class ForkJoinShared : public ParallelJob, public Monitor
|
||||
|
@ -1155,7 +1155,7 @@ ForkJoinOperation::reportBailoutWarnings()
|
|||
bool
|
||||
ForkJoinOperation::invalidateBailedOutScripts()
|
||||
{
|
||||
Vector<types::RecompileInfo> invalid(cx_);
|
||||
types::RecompileInfoVector invalid;
|
||||
for (uint32_t i = 0; i < bailoutRecords_.length(); i++) {
|
||||
switch (bailoutRecords_[i].cause) {
|
||||
// No bailout.
|
||||
|
@ -1312,10 +1312,10 @@ ForkJoinOperation::recoverFromBailout(ExecutionStatus *status)
|
|||
}
|
||||
|
||||
bool
|
||||
ForkJoinOperation::hasScript(Vector<types::RecompileInfo> &scripts, JSScript *script)
|
||||
ForkJoinOperation::hasScript(const types::RecompileInfoVector &invalid, JSScript *script)
|
||||
{
|
||||
for (uint32_t i = 0; i < scripts.length(); i++) {
|
||||
if (scripts[i] == script->parallelIonScript()->recompileInfo())
|
||||
for (uint32_t i = 0; i < invalid.length(); i++) {
|
||||
if (invalid[i].compilerOutput(cx_)->script() == script)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
|
|
@ -449,7 +449,7 @@ bool
|
|||
js::Invoke(JSContext *cx, CallArgs args, MaybeConstruct construct)
|
||||
{
|
||||
MOZ_ASSERT(args.length() <= ARGS_LENGTH_MAX);
|
||||
MOZ_ASSERT(!cx->compartment()->activeAnalysis);
|
||||
MOZ_ASSERT(!cx->zone()->types.activeAnalysis);
|
||||
|
||||
/* Perform GC if necessary on exit from the function. */
|
||||
AutoGCIfNeeded gcIfNeeded(cx);
|
||||
|
@ -1441,7 +1441,7 @@ Interpret(JSContext *cx, RunState &state)
|
|||
JS_END_MACRO
|
||||
|
||||
gc::MaybeVerifyBarriers(cx, true);
|
||||
MOZ_ASSERT(!cx->compartment()->activeAnalysis);
|
||||
MOZ_ASSERT(!cx->zone()->types.activeAnalysis);
|
||||
|
||||
InterpreterFrame *entryFrame = state.pushInterpreterFrame(cx);
|
||||
if (!entryFrame)
|
||||
|
|
Загрузка…
Ссылка в новой задаче