Bug 1072564 - Incrementalize sweeping of type information, r=billm.

This commit is contained in:
Brian Hackett 2014-10-22 07:57:33 -07:00
parent 8e53554c0a
commit 363383cd7a
26 changed files with 728 additions and 399 deletions

View File

@ -227,6 +227,7 @@ class LifoAlloc
// Steal allocated chunks from |other|. // Steal allocated chunks from |other|.
void steal(LifoAlloc *other) { void steal(LifoAlloc *other) {
MOZ_ASSERT(!other->markCount); MOZ_ASSERT(!other->markCount);
MOZ_ASSERT(!latest);
// Copy everything from |other| to |this| except for |peakSize_|, which // Copy everything from |other| to |this| except for |peakSize_|, which
// requires some care. // requires some care.

View File

@ -672,6 +672,7 @@ class GCRuntime
*/ */
JS::Zone *zoneGroups; JS::Zone *zoneGroups;
JS::Zone *currentZoneGroup; JS::Zone *currentZoneGroup;
bool sweepingTypes;
unsigned finalizePhase; unsigned finalizePhase;
JS::Zone *sweepZone; JS::Zone *sweepZone;
unsigned sweepKindIndex; unsigned sweepKindIndex;

View File

@ -1139,6 +1139,17 @@ ArenaHeader::unsetAllocDuringSweep()
auxNextLink = 0; 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 static void
AssertValidColor(const TenuredCell *thing, uint32_t color) AssertValidColor(const TenuredCell *thing, uint32_t color)
{ {

View File

@ -511,16 +511,8 @@ IsAboutToBeFinalizedFromAnyThread(T **thingp)
Zone *zone = thing->asTenured().zoneFromAnyThread(); Zone *zone = thing->asTenured().zoneFromAnyThread();
if (zone->isGCSweeping()) { if (zone->isGCSweeping()) {
/* if (thing->asTenured().arenaHeader()->allocatedDuringIncremental)
* We should return false for things that have been allocated during return false;
* 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);
return !thing->asTenured().isMarked(); return !thing->asTenured().isMarked();
} }
#ifdef JSGC_COMPACTING #ifdef JSGC_COMPACTING

View File

@ -301,10 +301,9 @@ static const PhaseInfo phases[] = {
{ PHASE_SWEEP_BREAKPOINT, "Sweep Breakpoints", PHASE_SWEEP_COMPARTMENTS }, { PHASE_SWEEP_BREAKPOINT, "Sweep Breakpoints", PHASE_SWEEP_COMPARTMENTS },
{ PHASE_SWEEP_REGEXP, "Sweep Regexps", PHASE_SWEEP_COMPARTMENTS }, { PHASE_SWEEP_REGEXP, "Sweep Regexps", PHASE_SWEEP_COMPARTMENTS },
{ PHASE_SWEEP_MISC, "Sweep Miscellaneous", PHASE_SWEEP_COMPARTMENTS }, { PHASE_SWEEP_MISC, "Sweep Miscellaneous", PHASE_SWEEP_COMPARTMENTS },
{ PHASE_DISCARD_ANALYSIS, "Discard Analysis", PHASE_SWEEP_COMPARTMENTS }, { PHASE_SWEEP_TYPES, "Sweep type information", PHASE_SWEEP_COMPARTMENTS },
{ PHASE_DISCARD_TI, "Discard TI", PHASE_DISCARD_ANALYSIS }, { PHASE_SWEEP_TYPES_BEGIN, "Sweep type tables and compilations", PHASE_SWEEP_TYPES },
{ PHASE_FREE_TI_ARENA, "Free TI Arena", PHASE_DISCARD_ANALYSIS }, { PHASE_SWEEP_TYPES_END, "Free type arena", PHASE_SWEEP_TYPES },
{ PHASE_SWEEP_TYPES, "Sweep Types", PHASE_DISCARD_ANALYSIS },
{ PHASE_SWEEP_OBJECT, "Sweep Object", PHASE_SWEEP }, { PHASE_SWEEP_OBJECT, "Sweep Object", PHASE_SWEEP },
{ PHASE_SWEEP_STRING, "Sweep String", PHASE_SWEEP }, { PHASE_SWEEP_STRING, "Sweep String", PHASE_SWEEP },
{ PHASE_SWEEP_SCRIPT, "Sweep Script", PHASE_SWEEP }, { PHASE_SWEEP_SCRIPT, "Sweep Script", PHASE_SWEEP },

View File

@ -54,10 +54,9 @@ enum Phase {
PHASE_SWEEP_BREAKPOINT, PHASE_SWEEP_BREAKPOINT,
PHASE_SWEEP_REGEXP, PHASE_SWEEP_REGEXP,
PHASE_SWEEP_MISC, PHASE_SWEEP_MISC,
PHASE_DISCARD_ANALYSIS,
PHASE_DISCARD_TI,
PHASE_FREE_TI_ARENA,
PHASE_SWEEP_TYPES, PHASE_SWEEP_TYPES,
PHASE_SWEEP_TYPES_BEGIN,
PHASE_SWEEP_TYPES_END,
PHASE_SWEEP_OBJECT, PHASE_SWEEP_OBJECT,
PHASE_SWEEP_STRING, PHASE_SWEEP_STRING,
PHASE_SWEEP_SCRIPT, PHASE_SWEEP_SCRIPT,

View File

@ -105,27 +105,15 @@ Zone::onTooMuchMalloc()
} }
void void
Zone::sweepAnalysis(FreeOp *fop, bool releaseTypes) Zone::beginSweepTypes(FreeOp *fop, bool releaseTypes)
{ {
// Periodically release observed types for all scripts. This is safe to // Periodically release observed types for all scripts. This is safe to
// do when there are no frames for the zone on the stack. // do when there are no frames for the zone on the stack.
if (active) if (active)
releaseTypes = false; releaseTypes = false;
bool oom = false; types::AutoClearTypeInferenceStateOnOOM oom(this);
types.sweep(fop, releaseTypes, &oom); types.beginSweep(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();
}
} }
void void

View File

@ -157,7 +157,7 @@ struct Zone : public JS::shadow::Zone,
} }
void reportAllocationOverflow() { js_ReportAllocationOverflow(nullptr); } void reportAllocationOverflow() { js_ReportAllocationOverflow(nullptr); }
void sweepAnalysis(js::FreeOp *fop, bool releaseTypes); void beginSweepTypes(js::FreeOp *fop, bool releaseTypes);
bool hasMarkedCompartments(); bool hasMarkedCompartments();

View File

@ -11,7 +11,7 @@ function testMap() {
print(m.mode+" "+m.expect); print(m.mode+" "+m.expect);
nums.mapPar(function (v) { nums.mapPar(function (v) {
var x = []; var x = [];
for (var i = 0; i < 45000; i++) { for (var i = 0; i < 20000; i++) {
x[i] = {from: v}; x[i] = {from: v};
} }
return x; return x;

View File

@ -2846,7 +2846,7 @@ jit::InvalidateAll(FreeOp *fop, Zone *zone)
void void
jit::Invalidate(types::TypeZone &types, FreeOp *fop, jit::Invalidate(types::TypeZone &types, FreeOp *fop,
const Vector<types::RecompileInfo> &invalid, bool resetUses, const types::RecompileInfoVector &invalid, bool resetUses,
bool cancelOffThread) bool cancelOffThread)
{ {
JitSpew(JitSpew_IonInvalidate, "Start invalidation."); JitSpew(JitSpew_IonInvalidate, "Start invalidation.");
@ -2855,23 +2855,24 @@ jit::Invalidate(types::TypeZone &types, FreeOp *fop,
// to the traversal which frames have been invalidated. // to the traversal which frames have been invalidated.
size_t numInvalidations = 0; size_t numInvalidations = 0;
for (size_t i = 0; i < invalid.length(); i++) { for (size_t i = 0; i < invalid.length(); i++) {
const types::CompilerOutput &co = *invalid[i].compilerOutput(types); const types::CompilerOutput *co = invalid[i].compilerOutput(types);
if (!co.isValid()) if (!co)
continue; continue;
MOZ_ASSERT(co->isValid());
if (cancelOffThread) if (cancelOffThread)
CancelOffThreadIonCompile(co.script()->compartment(), co.script()); CancelOffThreadIonCompile(co->script()->compartment(), co->script());
if (!co.ion()) if (!co->ion())
continue; continue;
JitSpew(JitSpew_IonInvalidate, " Invalidate %s:%u, IonScript %p", 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 // Keep the ion script alive during the invalidation and flag this
// ionScript as being invalidated. This increment is removed by the // ionScript as being invalidated. This increment is removed by the
// loop after the calls to InvalidateActivation. // loop after the calls to InvalidateActivation.
co.ion()->incref(); co->ion()->incref();
numInvalidations++; numInvalidations++;
} }
@ -2887,19 +2888,20 @@ jit::Invalidate(types::TypeZone &types, FreeOp *fop,
// IonScript will be immediately destroyed. Otherwise, it will be held live // IonScript will be immediately destroyed. Otherwise, it will be held live
// until its last invalidated frame is destroyed. // until its last invalidated frame is destroyed.
for (size_t i = 0; i < invalid.length(); i++) { for (size_t i = 0; i < invalid.length(); i++) {
types::CompilerOutput &co = *invalid[i].compilerOutput(types); types::CompilerOutput *co = invalid[i].compilerOutput(types);
if (!co.isValid()) if (!co)
continue; continue;
MOZ_ASSERT(co->isValid());
ExecutionMode executionMode = co.mode(); ExecutionMode executionMode = co->mode();
JSScript *script = co.script(); JSScript *script = co->script();
IonScript *ionScript = co.ion(); IonScript *ionScript = co->ion();
if (!ionScript) if (!ionScript)
continue; continue;
SetIonScript(nullptr, script, executionMode, nullptr); SetIonScript(nullptr, script, executionMode, nullptr);
ionScript->decref(fop); ionScript->decref(fop);
co.invalidate(); co->invalidate();
numInvalidations--; numInvalidations--;
// Wait for the scripts to get warm again before doing another // Wait for the scripts to get warm again before doing another
@ -2923,7 +2925,7 @@ jit::Invalidate(types::TypeZone &types, FreeOp *fop,
} }
void void
jit::Invalidate(JSContext *cx, const Vector<types::RecompileInfo> &invalid, bool resetUses, jit::Invalidate(JSContext *cx, const types::RecompileInfoVector &invalid, bool resetUses,
bool cancelOffThread) bool cancelOffThread)
{ {
jit::Invalidate(cx->zone()->types, cx->runtime()->defaultFreeOp(), invalid, resetUses, 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) jit::IonScript::invalidate(JSContext *cx, bool resetUses, const char *reason)
{ {
JitSpew(JitSpew_IonInvalidate, " Invalidate IonScript %p: %s", this, reason); JitSpew(JitSpew_IonInvalidate, " Invalidate IonScript %p: %s", this, reason);
Vector<types::RecompileInfo> list(cx); types::RecompileInfoVector list;
if (!list.append(recompileInfo())) if (!list.append(recompileInfo()))
return false; return false;
Invalidate(cx, list, resetUses, true); Invalidate(cx, list, resetUses, true);
@ -2968,7 +2970,7 @@ jit::Invalidate(JSContext *cx, JSScript *script, ExecutionMode mode, bool resetU
js_free(buf); js_free(buf);
} }
Vector<types::RecompileInfo> scripts(cx); types::RecompileInfoVector scripts;
switch (mode) { switch (mode) {
case SequentialExecution: case SequentialExecution:

View File

@ -126,9 +126,9 @@ IonExecStatus FastInvoke(JSContext *cx, HandleFunction fun, CallArgs &args);
// Walk the stack and invalidate active Ion frames for the invalid scripts. // Walk the stack and invalidate active Ion frames for the invalid scripts.
void Invalidate(types::TypeZone &types, FreeOp *fop, 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); 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 cancelOffThread = true);
bool Invalidate(JSContext *cx, JSScript *script, ExecutionMode mode, bool resetUses = true, bool Invalidate(JSContext *cx, JSScript *script, ExecutionMode mode, bool resetUses = true,
bool cancelOffThread = true); bool cancelOffThread = true);

View File

@ -2850,7 +2850,7 @@ jit::AnalyzeNewScriptDefiniteProperties(JSContext *cx, JSFunction *fun,
types::TypeObject *type, HandleNativeObject baseobj, types::TypeObject *type, HandleNativeObject baseobj,
Vector<types::TypeNewScript::Initializer> *initializerList) 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 // 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 // which will definitely be added to the created object before it has a

View File

@ -4271,11 +4271,10 @@ IonBuilder::inlineScriptedCall(CallInfo &callInfo, JSFunction *target)
// Improve type information of |this| when not set. // Improve type information of |this| when not set.
if (callInfo.constructing() && if (callInfo.constructing() &&
!callInfo.thisArg()->resultTypeSet() && !callInfo.thisArg()->resultTypeSet())
calleeScript->types)
{ {
types::StackTypeSet *types = types::TypeScript::ThisTypes(calleeScript); types::StackTypeSet *types = types::TypeScript::ThisTypes(calleeScript);
if (!types->unknown()) { if (types && !types->unknown()) {
types::TemporaryTypeSet *clonedTypes = types->clone(alloc_->lifoAlloc()); types::TemporaryTypeSet *clonedTypes = types->clone(alloc_->lifoAlloc());
if (!clonedTypes) if (!clonedTypes)
return oom(); return oom();
@ -5207,9 +5206,8 @@ IonBuilder::createThisScriptedSingleton(JSFunction *target, MDefinition *callee)
if (!templateObject->hasTenuredProto() || templateObject->getProto() != proto) if (!templateObject->hasTenuredProto() || templateObject->getProto() != proto)
return nullptr; return nullptr;
if (!target->nonLazyScript()->types) types::StackTypeSet *thisTypes = types::TypeScript::ThisTypes(target->nonLazyScript());
return nullptr; if (!thisTypes || !thisTypes->hasType(types::Type::ObjectType(templateObject)))
if (!types::TypeScript::ThisTypes(target->nonLazyScript())->hasType(types::Type::ObjectType(templateObject)))
return nullptr; return nullptr;
// Generate an inline path to create a new |this| object with // Generate an inline path to create a new |this| object with
@ -5580,6 +5578,9 @@ IonBuilder::testShouldDOMCall(types::TypeSet *inTypes,
static bool static bool
ArgumentTypesMatch(MDefinition *def, types::StackTypeSet *calleeTypes) ArgumentTypesMatch(MDefinition *def, types::StackTypeSet *calleeTypes)
{ {
if (!calleeTypes)
return false;
if (def->resultTypeSet()) { if (def->resultTypeSet()) {
MOZ_ASSERT(def->type() == MIRType_Value || def->mightBeType(def->type())); MOZ_ASSERT(def->type() == MIRType_Value || def->mightBeType(def->type()));
return def->resultTypeSet()->isSubset(calleeTypes); return def->resultTypeSet()->isSubset(calleeTypes);
@ -5605,9 +5606,6 @@ IonBuilder::testNeedsArgumentCheck(JSFunction *target, CallInfo &callInfo)
JSScript *targetScript = target->nonLazyScript(); JSScript *targetScript = target->nonLazyScript();
if (!targetScript->types)
return true;
if (!ArgumentTypesMatch(callInfo.thisArg(), types::TypeScript::ThisTypes(targetScript))) if (!ArgumentTypesMatch(callInfo.thisArg(), types::TypeScript::ThisTypes(targetScript)))
return true; return true;
uint32_t expected_args = Min<uint32_t>(callInfo.argc(), target->nargs()); uint32_t expected_args = Min<uint32_t>(callInfo.argc(), target->nargs());

View File

@ -100,8 +100,6 @@ JSCompartment::init(JSContext *cx)
if (cx) if (cx)
cx->runtime()->dateTimeInfo.updateTimeZoneAdjustment(); cx->runtime()->dateTimeInfo.updateTimeZoneAdjustment();
activeAnalysis = false;
if (!crossCompartmentWrappers.init(0)) if (!crossCompartmentWrappers.init(0))
return false; return false;

View File

@ -196,8 +196,6 @@ struct JSCompartment
*/ */
void adoptWorkerAllocator(js::Allocator *workerAllocator); void adoptWorkerAllocator(js::Allocator *workerAllocator);
bool activeAnalysis;
/* Type information about the scripts and objects in this compartment. */ /* Type information about the scripts and objects in this compartment. */
js::types::TypeCompartment types; js::types::TypeCompartment types;

View File

@ -352,22 +352,6 @@ struct js::gc::FinalizePhase
#define PHASE(x, p) { ArrayLength(x), x, p } #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. * Finalization order for incrementally swept things.
*/ */
@ -566,11 +550,13 @@ FinalizeTypedArenas(FreeOp *fop,
ArenaHeader **src, ArenaHeader **src,
SortedArenaList &dest, SortedArenaList &dest,
AllocKind thingKind, AllocKind thingKind,
SliceBudget &budget) SliceBudget &budget,
ArenaLists::KeepArenasEnum keepArenas)
{ {
/* /*
* Finalize arenas from src list, releasing empty arenas and inserting the * Finalize arenas from src list, releasing empty arenas if keepArenas
* others into the appropriate destination size bins. * 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 * but in that case, we want to hold on to the memory in our arena
* lists, not offer it up for reuse. * lists, not offer it up for reuse.
*/ */
bool releaseArenas = !InParallelSection(); MOZ_ASSERT_IF(InParallelSection(), keepArenas);
size_t thingSize = Arena::thingSize(thingKind); size_t thingSize = Arena::thingSize(thingKind);
size_t thingsPerArena = Arena::thingsPerArena(thingSize); size_t thingsPerArena = Arena::thingsPerArena(thingSize);
@ -590,10 +576,10 @@ FinalizeTypedArenas(FreeOp *fop,
if (nmarked) if (nmarked)
dest.insertAt(aheader, nfree); dest.insertAt(aheader, nfree);
else if (releaseArenas) else if (keepArenas)
aheader->chunk()->releaseArena(aheader);
else
aheader->chunk()->recycleArena(aheader, dest, thingKind, thingsPerArena); aheader->chunk()->recycleArena(aheader, dest, thingKind, thingsPerArena);
else
aheader->chunk()->releaseArena(aheader);
budget.step(thingsPerArena); budget.step(thingsPerArena);
if (budget.isOverBudget()) if (budget.isOverBudget())
@ -612,7 +598,8 @@ FinalizeArenas(FreeOp *fop,
ArenaHeader **src, ArenaHeader **src,
SortedArenaList &dest, SortedArenaList &dest,
AllocKind thingKind, AllocKind thingKind,
SliceBudget &budget) SliceBudget &budget,
ArenaLists::KeepArenasEnum keepArenas)
{ {
switch (thingKind) { switch (thingKind) {
case FINALIZE_OBJECT0: case FINALIZE_OBJECT0:
@ -627,33 +614,33 @@ FinalizeArenas(FreeOp *fop,
case FINALIZE_OBJECT12_BACKGROUND: case FINALIZE_OBJECT12_BACKGROUND:
case FINALIZE_OBJECT16: case FINALIZE_OBJECT16:
case FINALIZE_OBJECT16_BACKGROUND: case FINALIZE_OBJECT16_BACKGROUND:
return FinalizeTypedArenas<JSObject>(fop, src, dest, thingKind, budget); return FinalizeTypedArenas<JSObject>(fop, src, dest, thingKind, budget, keepArenas);
case FINALIZE_SCRIPT: case FINALIZE_SCRIPT:
return FinalizeTypedArenas<JSScript>(fop, src, dest, thingKind, budget); return FinalizeTypedArenas<JSScript>(fop, src, dest, thingKind, budget, keepArenas);
case FINALIZE_LAZY_SCRIPT: case FINALIZE_LAZY_SCRIPT:
return FinalizeTypedArenas<LazyScript>(fop, src, dest, thingKind, budget); return FinalizeTypedArenas<LazyScript>(fop, src, dest, thingKind, budget, keepArenas);
case FINALIZE_SHAPE: case FINALIZE_SHAPE:
return FinalizeTypedArenas<Shape>(fop, src, dest, thingKind, budget); return FinalizeTypedArenas<Shape>(fop, src, dest, thingKind, budget, keepArenas);
case FINALIZE_ACCESSOR_SHAPE: 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: 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: 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: 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: 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: case FINALIZE_EXTERNAL_STRING:
return FinalizeTypedArenas<JSExternalString>(fop, src, dest, thingKind, budget); return FinalizeTypedArenas<JSExternalString>(fop, src, dest, thingKind, budget, keepArenas);
case FINALIZE_SYMBOL: 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: case FINALIZE_JITCODE:
{ {
// JitCode finalization may release references on an executable // JitCode finalization may release references on an executable
// allocator that is accessed when requesting interrupts. // allocator that is accessed when requesting interrupts.
JSRuntime::AutoLockForInterrupt lock(fop->runtime()); 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: default:
MOZ_CRASH("Invalid alloc kind"); MOZ_CRASH("Invalid alloc kind");
@ -2054,7 +2041,7 @@ ArenaLists::wipeDuringParallelExecution(JSRuntime *rt)
if (!arenaLists[i].isEmpty()) { if (!arenaLists[i].isEmpty()) {
purge(thingKind); 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); gcstats::AutoPhase ap(fop->runtime()->gc.stats, phase.statsPhase);
for (unsigned i = 0; i < phase.length; ++i) for (unsigned i = 0; i < phase.length; ++i)
finalizeNow(fop, phase.kinds[i]); finalizeNow(fop, phase.kinds[i], RELEASE_ARENAS, nullptr);
} }
void void
ArenaLists::finalizeNow(FreeOp *fop, AllocKind thingKind) ArenaLists::finalizeNow(FreeOp *fop, AllocKind thingKind, KeepArenasEnum keepArenas, ArenaHeader **empty)
{ {
MOZ_ASSERT(!IsBackgroundFinalized(thingKind)); MOZ_ASSERT(!IsBackgroundFinalized(thingKind));
forceFinalizeNow(fop, thingKind); forceFinalizeNow(fop, thingKind, keepArenas, empty);
} }
void void
ArenaLists::forceFinalizeNow(FreeOp *fop, AllocKind thingKind) ArenaLists::forceFinalizeNow(FreeOp *fop, AllocKind thingKind, KeepArenasEnum keepArenas, ArenaHeader **empty)
{ {
MOZ_ASSERT(backgroundFinalizeState[thingKind] == BFS_DONE); MOZ_ASSERT(backgroundFinalizeState[thingKind] == BFS_DONE);
@ -2530,9 +2517,14 @@ ArenaLists::forceFinalizeNow(FreeOp *fop, AllocKind thingKind)
SortedArenaList finalizedSorted(thingsPerArena); SortedArenaList finalizedSorted(thingsPerArena);
SliceBudget budget; SliceBudget budget;
FinalizeArenas(fop, &arenas, finalizedSorted, thingKind, budget); FinalizeArenas(fop, &arenas, finalizedSorted, thingKind, budget, keepArenas);
MOZ_ASSERT(!arenas); MOZ_ASSERT(!arenas);
if (empty) {
MOZ_ASSERT(keepArenas == KEEP_ARENAS);
finalizedSorted.extractEmpty(empty);
}
arenaLists[thingKind] = finalizedSorted.toArenaList(); arenaLists[thingKind] = finalizedSorted.toArenaList();
} }
@ -2586,6 +2578,8 @@ ArenaLists::queueForBackgroundSweep(FreeOp *fop, AllocKind thingKind)
ArenaLists::backgroundFinalize(FreeOp *fop, ArenaHeader *listHead) ArenaLists::backgroundFinalize(FreeOp *fop, ArenaHeader *listHead)
{ {
MOZ_ASSERT(listHead); MOZ_ASSERT(listHead);
MOZ_ASSERT(!InParallelSection());
AllocKind thingKind = listHead->getAllocKind(); AllocKind thingKind = listHead->getAllocKind();
Zone *zone = listHead->zone; Zone *zone = listHead->zone;
@ -2593,7 +2587,7 @@ ArenaLists::backgroundFinalize(FreeOp *fop, ArenaHeader *listHead)
SortedArenaList finalizedSorted(thingsPerArena); SortedArenaList finalizedSorted(thingsPerArena);
SliceBudget budget; SliceBudget budget;
FinalizeArenas(fop, &listHead, finalizedSorted, thingKind, budget); FinalizeArenas(fop, &listHead, finalizedSorted, thingKind, budget, RELEASE_ARENAS);
MOZ_ASSERT(!listHead); MOZ_ASSERT(!listHead);
// When arenas are queued for background finalization, all arenas are moved // 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; 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 * static void *
RunLastDitchGC(JSContext *cx, JS::Zone *zone, AllocKind thingKind) 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()) { 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()) { for (GCZoneGroupIter zone(rt); !zone.done(); zone.next()) {
gcstats::AutoSCC scc(stats, zoneGroupIndex); gcstats::AutoSCC scc(stats, zoneGroupIndex);
for (unsigned i = 0; i < ArrayLength(ImmediateFinalizePhases); ++i) zone->allocator.arenas.queueForegroundObjectsForSweep(&fop);
zone->allocator.arenas.finalizeNow(&fop, ImmediateFinalizePhases[i]);
} }
for (GCZoneGroupIter zone(rt); !zone.done(); zone.next()) { for (GCZoneGroupIter zone(rt); !zone.done(); zone.next()) {
gcstats::AutoSCC scc(stats, zoneGroupIndex); gcstats::AutoSCC scc(stats, zoneGroupIndex);
@ -4814,12 +4874,11 @@ GCRuntime::beginSweepingZoneGroup()
} }
for (GCZoneGroupIter zone(rt); !zone.done(); zone.next()) { for (GCZoneGroupIter zone(rt); !zone.done(); zone.next()) {
gcstats::AutoSCC scc(stats, zoneGroupIndex); gcstats::AutoSCC scc(stats, zoneGroupIndex);
zone->allocator.arenas.gcShapeArenasToSweep = zone->allocator.arenas.queueForegroundThingsForSweep(&fop);
zone->allocator.arenas.arenaListsToSweep[FINALIZE_SHAPE];
zone->allocator.arenas.gcAccessorShapeArenasToSweep =
zone->allocator.arenas.arenaListsToSweep[FINALIZE_ACCESSOR_SHAPE];
} }
sweepingTypes = true;
finalizePhase = 0; finalizePhase = 0;
sweepZone = currentZoneGroup; sweepZone = currentZoneGroup;
sweepKindIndex = 0; sweepKindIndex = 0;
@ -4888,10 +4947,14 @@ bool
ArenaLists::foregroundFinalize(FreeOp *fop, AllocKind thingKind, SliceBudget &sliceBudget, ArenaLists::foregroundFinalize(FreeOp *fop, AllocKind thingKind, SliceBudget &sliceBudget,
SortedArenaList &sweepList) SortedArenaList &sweepList)
{ {
MOZ_ASSERT(!InParallelSection());
if (!arenaListsToSweep[thingKind] && incrementalSweptArenas.isEmpty()) if (!arenaListsToSweep[thingKind] && incrementalSweptArenas.isEmpty())
return true; return true;
if (!FinalizeArenas(fop, &arenaListsToSweep[thingKind], sweepList, thingKind, sliceBudget)) { if (!FinalizeArenas(fop, &arenaListsToSweep[thingKind], sweepList,
thingKind, sliceBudget, RELEASE_ARENAS))
{
incrementalSweptArenaKind = thingKind; incrementalSweptArenaKind = thingKind;
incrementalSweptArenas = sweepList.toArenaList(); incrementalSweptArenas = sweepList.toArenaList();
return false; return false;
@ -4915,8 +4978,18 @@ GCRuntime::drainMarkStack(SliceBudget &sliceBudget, gcstats::Phase phase)
return marker.drainMarkStack(sliceBudget); return marker.drainMarkStack(sliceBudget);
} }
// Advance to the next entry in a list of arenas, returning false if the
// mutator should resume running.
static bool 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) { while (ArenaHeader *arena = *arenasToSweep) {
for (ArenaCellIterUnderGC i(arena); !i.done(); i.next()) { for (ArenaCellIterUnderGC i(arena); !i.done(); i.next()) {
@ -4925,10 +4998,8 @@ SweepShapes(ArenaHeader **arenasToSweep, size_t thingsPerArena, SliceBudget &sli
shape->sweep(); shape->sweep();
} }
*arenasToSweep = arena->next; if (!AdvanceArenaList(arenasToSweep, kind, sliceBudget))
sliceBudget.step(thingsPerArena); return false;
if (sliceBudget.isOverBudget())
return false; /* Yield to the mutator. */
} }
return true; return true;
@ -4945,6 +5016,64 @@ GCRuntime::sweepPhase(SliceBudget &sliceBudget)
return false; return false;
for (;;) { 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. */ /* Finalize foreground finalized things. */
for (; finalizePhase < ArrayLength(IncrementalFinalizePhases) ; ++finalizePhase) { for (; finalizePhase < ArrayLength(IncrementalFinalizePhases) ; ++finalizePhase) {
gcstats::AutoPhase ap(stats, IncrementalFinalizePhases[finalizePhase].statsPhase); gcstats::AutoPhase ap(stats, IncrementalFinalizePhases[finalizePhase].statsPhase);
@ -4979,15 +5108,13 @@ GCRuntime::sweepPhase(SliceBudget &sliceBudget)
for (; sweepZone; sweepZone = sweepZone->nextNodeInGroup()) { for (; sweepZone; sweepZone = sweepZone->nextNodeInGroup()) {
Zone *zone = sweepZone; Zone *zone = sweepZone;
if (!SweepShapes(&zone->allocator.arenas.gcShapeArenasToSweep, if (!SweepShapes(&zone->allocator.arenas.gcShapeArenasToUpdate,
Arena::thingsPerArena(Arena::thingSize(FINALIZE_SHAPE)), FINALIZE_SHAPE, sliceBudget))
sliceBudget))
{ {
return false; /* Yield to the mutator. */ return false; /* Yield to the mutator. */
} }
if (!SweepShapes(&zone->allocator.arenas.gcAccessorShapeArenasToSweep, if (!SweepShapes(&zone->allocator.arenas.gcAccessorShapeArenasToUpdate,
Arena::thingsPerArena(Arena::thingSize(FINALIZE_ACCESSOR_SHAPE)), FINALIZE_ACCESSOR_SHAPE, sliceBudget))
sliceBudget))
{ {
return false; /* Yield to the mutator. */ return false; /* Yield to the mutator. */
} }
@ -6107,12 +6234,14 @@ gc::MergeCompartments(JSCompartment *source, JSCompartment *target)
source->clearTables(); 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()) { for (ZoneCellIter iter(source->zone(), FINALIZE_SCRIPT); !iter.done(); iter.next()) {
JSScript *script = iter.get<JSScript>(); JSScript *script = iter.get<JSScript>();
MOZ_ASSERT(script->compartment() == source); MOZ_ASSERT(script->compartment() == source);
script->compartment_ = target; script->compartment_ = target;
script->setTypesGeneration(target->zone()->types.generation);
} }
for (ZoneCellIter iter(source->zone(), FINALIZE_BASE_SHAPE); !iter.done(); iter.next()) { 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; 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. // Fixup zone pointers in source's zone to refer to target's zone.
for (size_t thingKind = 0; thingKind != FINALIZE_LIMIT; thingKind++) { for (size_t thingKind = 0; thingKind != FINALIZE_LIMIT; thingKind++) {

View File

@ -421,6 +421,12 @@ class ArenaList {
check(); check();
} }
ArenaList copyAndClear() {
ArenaList result = *this;
clear();
return result;
}
bool isEmpty() const { bool isEmpty() const {
check(); check();
return !head_; return !head_;
@ -548,6 +554,16 @@ class SortedArenaList
segments[nfree].append(aheader); 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 // 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 // non-empty segment, creating a contiguous list that is returned as an
// ArenaList. This is not a destructive operation: neither the head nor tail // ArenaList. This is not a destructive operation: neither the head nor tail
@ -604,9 +620,19 @@ class ArenaLists
unsigned incrementalSweptArenaKind; unsigned incrementalSweptArenaKind;
ArenaList incrementalSweptArenas; ArenaList incrementalSweptArenas;
/* Shape arenas to be swept in the foreground. */ // Arena lists which have yet to be swept, but need additional foreground
ArenaHeader *gcShapeArenasToSweep; // processing before they are swept.
ArenaHeader *gcAccessorShapeArenasToSweep; 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: public:
ArenaLists() { ArenaLists() {
@ -617,8 +643,11 @@ class ArenaLists
for (size_t i = 0; i != FINALIZE_LIMIT; ++i) for (size_t i = 0; i != FINALIZE_LIMIT; ++i)
arenaListsToSweep[i] = nullptr; arenaListsToSweep[i] = nullptr;
incrementalSweptArenaKind = FINALIZE_LIMIT; incrementalSweptArenaKind = FINALIZE_LIMIT;
gcShapeArenasToSweep = nullptr; gcShapeArenasToUpdate = nullptr;
gcAccessorShapeArenasToSweep = nullptr; gcAccessorShapeArenasToUpdate = nullptr;
gcScriptArenasToUpdate = nullptr;
gcTypeObjectArenasToUpdate = nullptr;
savedEmptyObjectArenas = nullptr;
} }
~ArenaLists() { ~ArenaLists() {
@ -628,19 +657,13 @@ class ArenaLists
* the background finalization is disabled. * the background finalization is disabled.
*/ */
MOZ_ASSERT(backgroundFinalizeState[i] == BFS_DONE); MOZ_ASSERT(backgroundFinalizeState[i] == BFS_DONE);
ArenaHeader *next; ReleaseArenaList(arenaLists[i].head());
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(incrementalSweptArenas.head());
for (size_t i = 0; i < FINALIZE_OBJECT_LIMIT; i++)
ReleaseArenaList(savedObjectArenas[i].head());
ReleaseArenaList(savedEmptyObjectArenas);
} }
static uintptr_t getFreeListOffset(AllocKind thingKind) { static uintptr_t getFreeListOffset(AllocKind thingKind) {
@ -823,11 +846,10 @@ class ArenaLists
ArenaHeader *relocateArenas(ArenaHeader *relocatedList); ArenaHeader *relocateArenas(ArenaHeader *relocatedList);
#endif #endif
void queueObjectsForSweep(FreeOp *fop); void queueForegroundObjectsForSweep(FreeOp *fop);
void queueStringsAndSymbolsForSweep(FreeOp *fop); void queueForegroundThingsForSweep(FreeOp *fop);
void queueShapesForSweep(FreeOp *fop);
void queueScriptsForSweep(FreeOp *fop); void mergeForegroundSweptObjectArenas();
void queueJitCodeForSweep(FreeOp *fop);
bool foregroundFinalize(FreeOp *fop, AllocKind thingKind, SliceBudget &sliceBudget, bool foregroundFinalize(FreeOp *fop, AllocKind thingKind, SliceBudget &sliceBudget,
SortedArenaList &sweepList); SortedArenaList &sweepList);
@ -835,14 +857,25 @@ class ArenaLists
void wipeDuringParallelExecution(JSRuntime *rt); 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: private:
inline void finalizeNow(FreeOp *fop, const FinalizePhase& phase); inline void finalizeNow(FreeOp *fop, const FinalizePhase& phase);
inline void queueForForegroundSweep(FreeOp *fop, const FinalizePhase& phase); inline void queueForForegroundSweep(FreeOp *fop, const FinalizePhase& phase);
inline void queueForBackgroundSweep(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 queueForForegroundSweep(FreeOp *fop, AllocKind thingKind);
inline void queueForBackgroundSweep(FreeOp *fop, AllocKind thingKind); inline void queueForBackgroundSweep(FreeOp *fop, AllocKind thingKind);
inline void mergeSweptArenas(AllocKind thingKind);
TenuredCell *allocateFromArena(JS::Zone *zone, AllocKind thingKind, TenuredCell *allocateFromArena(JS::Zone *zone, AllocKind thingKind,
AutoMaybeStartBackgroundAllocation &maybeStartBGAlloc); AutoMaybeStartBackgroundAllocation &maybeStartBGAlloc);

View File

@ -461,7 +461,7 @@ ConstraintTypeSet::addConstraint(JSContext *cx, TypeConstraint *constraint, bool
return false; return false;
} }
MOZ_ASSERT(cx->compartment()->activeAnalysis); MOZ_ASSERT(cx->zone()->types.activeAnalysis);
InferSpew(ISpewOps, "addConstraint: %sT%p%s %sC%p%s %s", InferSpew(ISpewOps, "addConstraint: %sT%p%s %sC%p%s %s",
InferSpewColor(this), this, InferSpewColorReset(), InferSpewColor(this), this, InferSpewColorReset(),
@ -555,7 +555,7 @@ TypeSet::addType(Type type, LifoAlloc *alloc)
void void
ConstraintTypeSet::addType(ExclusiveContext *cxArg, Type type) ConstraintTypeSet::addType(ExclusiveContext *cxArg, Type type)
{ {
MOZ_ASSERT(cxArg->compartment()->activeAnalysis); MOZ_ASSERT(cxArg->zone()->types.activeAnalysis);
if (hasType(type)) if (hasType(type))
return; return;
@ -924,7 +924,7 @@ TypeScript::FreezeTypeSets(CompilerConstraintList *constraints, JSScript *script
TemporaryTypeSet **pBytecodeTypes) TemporaryTypeSet **pBytecodeTypes)
{ {
LifoAlloc *alloc = constraints->alloc(); LifoAlloc *alloc = constraints->alloc();
StackTypeSet *existing = script->types->typeArray(); StackTypeSet *existing = script->types()->typeArray();
size_t count = NumTypeSets(script); size_t count = NumTypeSets(script);
TemporaryTypeSet *types = alloc->newArrayUninitialized<TemporaryTypeSet>(count); TemporaryTypeSet *types = alloc->newArrayUninitialized<TemporaryTypeSet>(count);
@ -1192,7 +1192,7 @@ types::FinishCompilation(JSContext *cx, HandleScript script, ExecutionMode execu
TypeZone &types = cx->zone()->types; TypeZone &types = cx->zone()->types;
if (!types.compilerOutputs) { if (!types.compilerOutputs) {
types.compilerOutputs = cx->new_< Vector<CompilerOutput> >(cx); types.compilerOutputs = cx->new_<TypeZone::CompilerOutputVector>();
if (!types.compilerOutputs) if (!types.compilerOutputs)
return false; return false;
} }
@ -1205,10 +1205,12 @@ types::FinishCompilation(JSContext *cx, HandleScript script, ExecutionMode execu
#endif #endif
uint32_t index = types.compilerOutputs->length(); uint32_t index = types.compilerOutputs->length();
if (!types.compilerOutputs->append(co)) if (!types.compilerOutputs->append(co)) {
js_ReportOutOfMemory(cx);
return false; return false;
}
*precompileInfo = RecompileInfo(index); *precompileInfo = RecompileInfo(index, types.generation);
bool succeeded = true; bool succeeded = true;
@ -1220,7 +1222,10 @@ types::FinishCompilation(JSContext *cx, HandleScript script, ExecutionMode execu
for (size_t i = 0; i < constraints->numFrozenScripts(); i++) { for (size_t i = 0; i < constraints->numFrozenScripts(); i++) {
const CompilerConstraintList::FrozenScript &entry = constraints->frozenScript(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))) if (!CheckFrozenTypeSet(cx, entry.thisTypes, types::TypeScript::ThisTypes(entry.script)))
succeeded = false; succeeded = false;
@ -1232,7 +1237,7 @@ types::FinishCompilation(JSContext *cx, HandleScript script, ExecutionMode execu
succeeded = false; succeeded = false;
} }
for (size_t i = 0; i < entry.script->nTypeSets(); i++) { 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; succeeded = false;
} }
@ -1244,7 +1249,7 @@ types::FinishCompilation(JSContext *cx, HandleScript script, ExecutionMode execu
size_t count = TypeScript::NumTypeSets(entry.script); 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++) { for (size_t i = 0; i < count; i++) {
if (!array[i].addConstraint(cx, cx->typeLifoAlloc().new_<TypeConstraintFreezeStack>(entry.script), false)) if (!array[i].addConstraint(cx, cx->typeLifoAlloc().new_<TypeConstraintFreezeStack>(entry.script), false))
succeeded = false; succeeded = false;
@ -1288,7 +1293,7 @@ types::FinishDefinitePropertiesAnalysis(JSContext *cx, CompilerConstraintList *c
for (size_t i = 0; i < constraints->numFrozenScripts(); i++) { for (size_t i = 0; i < constraints->numFrozenScripts(); i++) {
const CompilerConstraintList::FrozenScript &entry = constraints->frozenScript(i); const CompilerConstraintList::FrozenScript &entry = constraints->frozenScript(i);
JSScript *script = entry.script; JSScript *script = entry.script;
MOZ_ASSERT(script->types); MOZ_ASSERT(script->types());
MOZ_ASSERT(TypeScript::ThisTypes(script)->isSubset(entry.thisTypes)); 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])); MOZ_ASSERT(TypeScript::ArgTypes(script, j)->isSubset(&entry.argTypes[j]));
for (size_t j = 0; j < script->nTypeSets(); 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 #endif
for (size_t i = 0; i < constraints->numFrozenScripts(); i++) { for (size_t i = 0; i < constraints->numFrozenScripts(); i++) {
const CompilerConstraintList::FrozenScript &entry = constraints->frozenScript(i); const CompilerConstraintList::FrozenScript &entry = constraints->frozenScript(i);
JSScript *script = entry.script; JSScript *script = entry.script;
MOZ_ASSERT(script->types); if (!script->types())
if (!script->types)
MOZ_CRASH(); MOZ_CRASH();
CheckDefinitePropertiesTypeSet(cx, entry.thisTypes, TypeScript::ThisTypes(script)); 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)); CheckDefinitePropertiesTypeSet(cx, &entry.argTypes[j], TypeScript::ArgTypes(script, j));
for (size_t j = 0; j < script->nTypeSets(); 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 void
TypeZone::processPendingRecompiles(FreeOp *fop) TypeZone::processPendingRecompiles(FreeOp *fop, RecompileInfoVector &recompiles)
{ {
if (!pendingRecompiles) MOZ_ASSERT(!recompiles.empty());
return;
/* Steal the list of scripts to recompile, else we will try to recursively recompile them. */ /*
Vector<RecompileInfo> *pending = pendingRecompiles; * Steal the list of scripts to recompile, to make sure we don't try to
pendingRecompiles = nullptr; * 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); MOZ_ASSERT(recompiles.empty());
fop->delete_(pending);
} }
void void
@ -2418,13 +2425,7 @@ TypeZone::addPendingRecompile(JSContext *cx, const RecompileInfo &info)
co->setPendingInvalidation(); co->setPendingInvalidation();
if (!pendingRecompiles) { if (!cx->zone()->types.activeAnalysis->pendingRecompiles.append(info))
pendingRecompiles = cx->new_< Vector<RecompileInfo> >(cx);
if (!pendingRecompiles)
CrashAtUnhandlableOOM("Could not update pendingRecompiles");
}
if (!pendingRecompiles->append(info))
CrashAtUnhandlableOOM("Could not update pendingRecompiles"); 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()) { for (gc::ZoneCellIter i(cx->zone(), gc::FINALIZE_SCRIPT); !i.done(); i.next()) {
JSScript *script = i.get<JSScript>(); JSScript *script = i.get<JSScript>();
if (script->types) { if (script->types()) {
unsigned count = TypeScript::NumTypeSets(script); unsigned count = TypeScript::NumTypeSets(script);
StackTypeSet *typeArray = script->types->typeArray(); StackTypeSet *typeArray = script->types()->typeArray();
for (unsigned i = 0; i < count; i++) { for (unsigned i = 0; i < count; i++) {
if (typeArray[i].hasType(Type::ObjectType(target))) if (typeArray[i].hasType(Type::ObjectType(target)))
typeArray[i].addType(cx, Type::AnyObjectType()); typeArray[i].addType(cx, Type::AnyObjectType());
@ -2496,21 +2497,21 @@ TypeCompartment::print(JSContext *cx, bool force)
gc::AutoSuppressGC suppressGC(cx); gc::AutoSuppressGC suppressGC(cx);
JSAutoRequest request(cx); JSAutoRequest request(cx);
JSCompartment *compartment = this->compartment(); Zone *zone = compartment()->zone();
AutoEnterAnalysis enter(nullptr, compartment); AutoEnterAnalysis enter(nullptr, zone);
if (!force && !InferSpewActive(ISpewResult)) if (!force && !InferSpewActive(ISpewResult))
return; 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) // Note: use cx->runtime() instead of cx to work around IsInRequest(cx)
// assertion failures when we're called from DestroyContext. // assertion failures when we're called from DestroyContext.
RootedScript script(cx->runtime(), i.get<JSScript>()); RootedScript script(cx->runtime(), i.get<JSScript>());
if (script->types) if (script->types())
script->types->printTypes(cx, script); 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>(); TypeObject *object = i.get<TypeObject>();
object->print(); object->print();
} }
@ -2587,7 +2588,7 @@ void
TypeCompartment::setTypeToHomogenousArray(ExclusiveContext *cx, TypeCompartment::setTypeToHomogenousArray(ExclusiveContext *cx,
JSObject *obj, Type elementType) JSObject *obj, Type elementType)
{ {
MOZ_ASSERT(cx->compartment()->activeAnalysis); MOZ_ASSERT(cx->zone()->types.activeAnalysis);
if (!arrayTypeTable) { if (!arrayTypeTable) {
arrayTypeTable = cx->new_<ArrayTypeTable>(); arrayTypeTable = cx->new_<ArrayTypeTable>();
@ -3204,7 +3205,7 @@ TypeObject::markUnknown(ExclusiveContext *cx)
{ {
AutoEnterAnalysis enter(cx); AutoEnterAnalysis enter(cx);
MOZ_ASSERT(cx->compartment()->activeAnalysis); MOZ_ASSERT(cx->zone()->types.activeAnalysis);
MOZ_ASSERT(!unknownProperties()); MOZ_ASSERT(!unknownProperties());
InferSpew(ISpewOps, "UnknownProperties: %s", TypeObjectString(this)); InferSpew(ISpewOps, "UnknownProperties: %s", TypeObjectString(this));
@ -3234,7 +3235,7 @@ TypeObject::markUnknown(ExclusiveContext *cx)
void void
TypeObject::maybeClearNewScriptOnOOM() TypeObject::maybeClearNewScriptOnOOM()
{ {
MOZ_ASSERT(zone()->isGCSweeping()); MOZ_ASSERT(zone()->isGCSweepingOrCompacting());
if (!isMarked()) if (!isMarked())
return; return;
@ -3457,7 +3458,7 @@ types::AddClearDefiniteFunctionUsesInScript(JSContext *cx, TypeObject *type,
TypeObjectKey *calleeKey = Type::ObjectType(calleeScript->functionNonDelazifying()).objectKey(); TypeObjectKey *calleeKey = Type::ObjectType(calleeScript->functionNonDelazifying()).objectKey();
unsigned count = TypeScript::NumTypeSets(script); unsigned count = TypeScript::NumTypeSets(script);
StackTypeSet *typeArray = script->types->typeArray(); StackTypeSet *typeArray = script->types()->typeArray();
for (unsigned i = 0; i < count; i++) { for (unsigned i = 0; i < count; i++) {
StackTypeSet *types = &typeArray[i]; StackTypeSet *types = &typeArray[i];
@ -3667,7 +3668,7 @@ types::UseNewTypeForClone(JSFunction *fun)
bool bool
JSScript::makeTypes(JSContext *cx) JSScript::makeTypes(JSContext *cx)
{ {
MOZ_ASSERT(!types); MOZ_ASSERT(!types_);
AutoEnterAnalysis enter(cx); AutoEnterAnalysis enter(cx);
@ -3678,7 +3679,8 @@ JSScript::makeTypes(JSContext *cx)
if (!typeScript) if (!typeScript)
return false; return false;
types = typeScript; types_ = typeScript;
setTypesGeneration(cx->zone()->types.generation);
#ifdef DEBUG #ifdef DEBUG
StackTypeSet *typeArray = typeScript->typeArray(); StackTypeSet *typeArray = typeScript->typeArray();
@ -3734,7 +3736,7 @@ JSFunction::setTypeForScriptedFunction(ExclusiveContext *cx, HandleFunction fun,
/* static */ void /* static */ void
TypeNewScript::make(JSContext *cx, TypeObject *type, JSFunction *fun) TypeNewScript::make(JSContext *cx, TypeObject *type, JSFunction *fun)
{ {
MOZ_ASSERT(cx->compartment()->activeAnalysis); MOZ_ASSERT(cx->zone()->types.activeAnalysis);
MOZ_ASSERT(!type->newScript()); MOZ_ASSERT(!type->newScript());
if (type->unknownProperties()) 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. // whether the new type table was updated and type needs to be refreshed.
MOZ_ASSERT(this == type->newScript()); 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) if (regenerate)
*regenerate = false; *regenerate = false;
@ -4177,7 +4185,7 @@ TypeNewScript::trace(JSTracer *trc)
} }
void void
TypeNewScript::sweep(FreeOp *fop) TypeNewScript::sweep()
{ {
// preliminaryObjects only holds weak pointers, so clear any objects that // preliminaryObjects only holds weak pointers, so clear any objects that
// are about to be destroyed. // are about to be destroyed.
@ -4531,8 +4539,15 @@ ExclusiveContext::getSingletonType(const Class *clasp, TaggedProto proto)
///////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////
void 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 * Purge references to type objects that are no longer live. Type sets hold
* only weak references. For type sets containing more than one object, * only weak references. For type sets containing more than one object,
@ -4555,7 +4570,7 @@ ConstraintTypeSet::sweep(Zone *zone, bool *oom)
if (pentry) { if (pentry) {
*pentry = object; *pentry = object;
} else { } else {
*oom = true; oom.setOOM();
flags |= TYPE_FLAG_ANYOBJECT; flags |= TYPE_FLAG_ANYOBJECT;
clearObjects(); clearObjects();
objectCount = 0; objectCount = 0;
@ -4587,7 +4602,7 @@ ConstraintTypeSet::sweep(Zone *zone, bool *oom)
copy->next = constraintList; copy->next = constraintList;
constraintList = copy; constraintList = copy;
} else { } else {
*oom = true; oom.setOOM();
} }
} }
constraint = constraint->next; constraint = constraint->next;
@ -4601,27 +4616,53 @@ TypeObject::clearProperties()
propertySet = nullptr; 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 * Before sweeping the arenas themselves, scan all type objects in a
* compartment to fixup weak references: property type sets referencing dead * compartment to fixup weak references: property type sets referencing dead
* JS and type objects, and singleton JS objects whose type is not referenced * JS and type objects, and singleton JS objects whose type is not referenced
* elsewhere. This also releases memory associated with dead type objects, * elsewhere. This is done either incrementally as part of the sweep, or on
* so that type objects do not need later finalization. * demand as type objects are accessed before their contents have been swept.
*/ */
inline void void
TypeObject::sweep(FreeOp *fop, bool *oom) TypeObject::maybeSweep(AutoClearTypeInferenceStateOnOOM *oom)
{ {
MOZ_ASSERT(zone()->isGCSweepingOrCompacting()); if (generation() == zone()->types.generation) {
// No sweeping required.
if (zone()->isGCSweeping() && !isMarked()) {
// Take care of any finalization required for this object.
if (newScript())
fop->delete_(newScript());
return; return;
} }
setGeneration(zone()->types.generation);
MOZ_ASSERT(zone()->isGCSweepingOrCompacting());
MOZ_ASSERT(!zone()->runtimeFromMainThread()->isHeapMinorCollecting());
Maybe<AutoClearTypeInferenceStateOnOOM> fallbackOOM;
EnsureHasAutoClearTypeInferenceStateOnOOM(oom, zone(), fallbackOOM);
if (newScript()) if (newScript())
newScript()->sweep(fop); newScript()->sweep();
LifoAlloc &typeLifoAlloc = zone()->types.typeLifoAlloc; LifoAlloc &typeLifoAlloc = zone()->types.typeLifoAlloc;
@ -4656,12 +4697,12 @@ TypeObject::sweep(FreeOp *fop, bool *oom)
(typeLifoAlloc, propertySet, propertyCount, prop->id); (typeLifoAlloc, propertySet, propertyCount, prop->id);
if (pentry) { if (pentry) {
*pentry = newProp; *pentry = newProp;
newProp->types.sweep(zone(), oom); newProp->types.sweep(zone(), *oom);
continue; continue;
} }
} }
*oom = true; oom->setOOM();
addFlags(OBJECT_FLAG_DYNAMIC_MASK | OBJECT_FLAG_UNKNOWN_PROPERTIES); addFlags(OBJECT_FLAG_DYNAMIC_MASK | OBJECT_FLAG_UNKNOWN_PROPERTIES);
clearProperties(); clearProperties();
return; return;
@ -4677,9 +4718,9 @@ TypeObject::sweep(FreeOp *fop, bool *oom)
Property *newProp = typeLifoAlloc.new_<Property>(*prop); Property *newProp = typeLifoAlloc.new_<Property>(*prop);
if (newProp) { if (newProp) {
propertySet = (Property **) newProp; propertySet = (Property **) newProp;
newProp->types.sweep(zone(), oom); newProp->types.sweep(zone(), *oom);
} else { } else {
*oom = true; oom->setOOM();
addFlags(OBJECT_FLAG_DYNAMIC_MASK | OBJECT_FLAG_UNKNOWN_PROPERTIES); addFlags(OBJECT_FLAG_DYNAMIC_MASK | OBJECT_FLAG_UNKNOWN_PROPERTIES);
clearProperties(); clearProperties();
return; return;
@ -4917,17 +4958,51 @@ TypeCompartment::~TypeCompartment()
} }
/* static */ void /* static */ void
TypeScript::Sweep(FreeOp *fop, JSScript *script, bool *oom) JSScript::maybeSweepTypes(AutoClearTypeInferenceStateOnOOM *oom)
{ {
JSCompartment *compartment = script->compartment(); if (!types_ || typesGeneration() == zone()->types.generation)
MOZ_ASSERT(compartment->zone()->isGCSweepingOrCompacting()); return;
unsigned num = NumTypeSets(script); setTypesGeneration(zone()->types.generation);
StackTypeSet *typeArray = script->types->typeArray();
/* 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++) 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 void
@ -4985,115 +5060,83 @@ TypeObject::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
TypeZone::TypeZone(Zone *zone) TypeZone::TypeZone(Zone *zone)
: zone_(zone), : zone_(zone),
typeLifoAlloc(TYPE_LIFO_ALLOC_PRIMARY_CHUNK_SIZE), typeLifoAlloc(TYPE_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
generation(0),
compilerOutputs(nullptr), compilerOutputs(nullptr),
pendingRecompiles(nullptr) sweepTypeLifoAlloc(TYPE_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
sweepCompilerOutputs(nullptr),
sweepReleaseTypes(false),
activeAnalysis(nullptr)
{ {
} }
TypeZone::~TypeZone() TypeZone::~TypeZone()
{ {
js_delete(compilerOutputs); js_delete(compilerOutputs);
js_delete(pendingRecompiles); js_delete(sweepCompilerOutputs);
} }
void void
TypeZone::sweep(FreeOp *fop, bool releaseTypes, bool *oom) TypeZone::beginSweep(FreeOp *fop, bool releaseTypes, AutoClearTypeInferenceStateOnOOM &oom)
{ {
MOZ_ASSERT(zone()->isGCSweepingOrCompacting()); 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
* Clear the analysis pool, but don't release its data yet. While // types any live data will be allocated into the pool.
* sweeping types any live data will be allocated into the pool. sweepTypeLifoAlloc.steal(&typeLifoAlloc);
*/
LifoAlloc oldAlloc(typeLifoAlloc.defaultChunkSize());
oldAlloc.steal(&typeLifoAlloc);
/* Sweep and find compressed indexes for each compiler output. */
size_t newCompilerOutputCount = 0;
// Sweep any invalid or dead compiler outputs, and keep track of the new
// index for remaining live outputs.
if (compilerOutputs) { if (compilerOutputs) {
CompilerOutputVector *newCompilerOutputs = nullptr;
for (size_t i = 0; i < compilerOutputs->length(); i++) { for (size_t i = 0; i < compilerOutputs->length(); i++) {
CompilerOutput &output = (*compilerOutputs)[i]; CompilerOutput &output = (*compilerOutputs)[i];
if (output.isValid()) { if (output.isValid()) {
JSScript *script = output.script(); JSScript *script = output.script();
ExecutionMode mode = output.mode();
if (IsScriptAboutToBeFinalized(&script)) { if (IsScriptAboutToBeFinalized(&script)) {
jit::GetIonScript(script, output.mode())->recompileInfoRef() = RecompileInfo(uint32_t(-1)); jit::GetIonScript(script, mode)->recompileInfoRef() = RecompileInfo();
output.invalidate(); output.invalidate();
} else { } else {
output.setSweepIndex(newCompilerOutputCount++); CompilerOutput newOutput(script, output.mode());
}
}
}
}
{ if (!newCompilerOutputs)
gcstats::AutoPhase ap2(rt->gc.stats, !rt->isHeapCompacting(), newCompilerOutputs = js_new<CompilerOutputVector>();
gcstats::PHASE_DISCARD_TI); if (newCompilerOutputs && newCompilerOutputs->append(newOutput)) {
output.setSweepIndex(newCompilerOutputs->length() - 1);
for (ZoneCellIterUnderGC i(zone(), FINALIZE_SCRIPT); !i.done(); i.next()) {
JSScript *script = i.get<JSScript>();
if (script->types) {
types::TypeScript::Sweep(fop, script, oom);
if (releaseTypes) {
script->types->destroy();
script->types = nullptr;
/*
* Freeze constraints on stack type sets need to be
* regenerated the next time the script is analyzed.
*/
script->clearHasFreezeConstraints();
MOZ_ASSERT(!script->hasIonScript());
MOZ_ASSERT(!script->hasParallelIonScript());
} else { } else {
/* Update the recompile indexes in any IonScripts still on the script. */ oom.setOOM();
if (script->hasIonScript()) jit::GetIonScript(script, mode)->recompileInfoRef() = RecompileInfo();
script->ionScript()->recompileInfoRef().shouldSweep(*this); output.invalidate();
if (script->hasParallelIonScript())
script->parallelIonScript()->recompileInfoRef().shouldSweep(*this);
} }
} }
} }
} }
sweepCompilerOutputs = compilerOutputs;
compilerOutputs = newCompilerOutputs;
}
{ // All existing RecompileInfos are stale and will be updated to the new
gcstats::AutoPhase ap2(rt->gc.stats, !rt->isHeapCompacting(), // compiler outputs list later during the sweep. Don't worry about overflow
gcstats::PHASE_SWEEP_TYPES); // here, since stale indexes will persist only until the sweep finishes.
generation++;
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()) for (CompartmentsInZoneIter comp(zone()); !comp.done(); comp.next())
comp->types.sweep(fop); comp->types.sweep(fop);
} }
if (compilerOutputs) { void
size_t sweepIndex = 0; TypeZone::endSweep(JSRuntime *rt)
for (size_t i = 0; i < compilerOutputs->length(); i++) { {
CompilerOutput output = (*compilerOutputs)[i]; js_delete(sweepCompilerOutputs);
if (output.isValid()) { sweepCompilerOutputs = nullptr;
MOZ_ASSERT(sweepIndex == output.sweepIndex());
output.invalidateSweepIndex();
(*compilerOutputs)[sweepIndex++] = output;
}
}
MOZ_ASSERT(sweepIndex == newCompilerOutputCount);
JS_ALWAYS_TRUE(compilerOutputs->resize(newCompilerOutputCount));
}
{ sweepReleaseTypes = false;
gcstats::AutoPhase ap2(rt->gc.stats, !rt->isHeapCompacting(),
gcstats::PHASE_FREE_TI_ARENA); rt->freeLifoAlloc.transferFrom(&sweepTypeLifoAlloc);
rt->freeLifoAlloc.transferFrom(&oldAlloc);
}
} }
void void
@ -5103,20 +5146,30 @@ TypeZone::clearAllNewScriptsOnOOM()
!iter.done(); iter.next()) !iter.done(); iter.next())
{ {
TypeObject *object = iter.get<TypeObject>(); TypeObject *object = iter.get<TypeObject>();
if (!IsTypeObjectAboutToBeFinalized(&object))
object->maybeClearNewScriptOnOOM(); object->maybeClearNewScriptOnOOM();
} }
} }
AutoClearTypeInferenceStateOnOOM::~AutoClearTypeInferenceStateOnOOM()
{
if (oom) {
zone->setPreservingCode(false);
zone->discardJitCode(zone->runtimeFromMainThread()->defaultFreeOp());
zone->types.clearAllNewScriptsOnOOM();
}
}
#ifdef DEBUG #ifdef DEBUG
void void
TypeScript::printTypes(JSContext *cx, HandleScript script) const TypeScript::printTypes(JSContext *cx, HandleScript script) const
{ {
MOZ_ASSERT(script->types == this); MOZ_ASSERT(script->types() == this);
if (!script->hasBaselineScript()) if (!script->hasBaselineScript())
return; return;
AutoEnterAnalysis enter(nullptr, script->compartment()); AutoEnterAnalysis enter(nullptr, script->zone());
if (script->functionNonDelazifying()) if (script->functionNonDelazifying())
fprintf(stderr, "Function"); fprintf(stderr, "Function");
@ -5168,5 +5221,6 @@ TypeScript::printTypes(JSContext *cx, HandleScript script) const
void void
TypeObject::setNewScript(TypeNewScript *newScript) TypeObject::setNewScript(TypeNewScript *newScript)
{ {
MOZ_ASSERT(!needsSweep());
newScript_ = newScript; newScript_ = newScript;
} }

View File

@ -503,7 +503,12 @@ enum MOZ_ENUM_TYPE(uint32_t) {
/* Mask for objects created with unknown properties. */ /* Mask for objects created with unknown properties. */
OBJECT_FLAG_UNKNOWN_MASK = OBJECT_FLAG_UNKNOWN_MASK =
OBJECT_FLAG_DYNAMIC_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; typedef uint32_t TypeObjectFlags;
@ -648,6 +653,29 @@ class TypeSet
void clearObjects(); 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. */ /* Superclass common to stack and heap type sets. */
class ConstraintTypeSet : public TypeSet class ConstraintTypeSet : public TypeSet
{ {
@ -666,7 +694,7 @@ class ConstraintTypeSet : public TypeSet
/* Add a new constraint to this set. */ /* Add a new constraint to this set. */
bool addConstraint(JSContext *cx, TypeConstraint *constraint, bool callExisting = true); 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 class StackTypeSet : public ConstraintTypeSet
@ -968,7 +996,7 @@ class TypeNewScript
} }
void trace(JSTracer *trc); void trace(JSTracer *trc);
void sweep(FreeOp *fop); void sweep();
#ifdef JSGC_COMPACTING #ifdef JSGC_COMPACTING
void fixupAfterMovingGC(); void fixupAfterMovingGC();
@ -1072,19 +1100,23 @@ struct TypeObject : public gc::TenuredCell
public: public:
TypeObjectFlags flags() const { TypeObjectFlags flags() {
maybeSweep(nullptr);
return flags_; return flags_;
} }
void addFlags(TypeObjectFlags flags) { void addFlags(TypeObjectFlags flags) {
MOZ_ASSERT(!needsSweep());
flags_ |= flags; flags_ |= flags;
} }
void clearFlags(TypeObjectFlags flags) { void clearFlags(TypeObjectFlags flags) {
MOZ_ASSERT(!needsSweep());
flags_ &= ~flags; flags_ &= ~flags;
} }
TypeNewScript *newScript() { TypeNewScript *newScript() {
maybeSweep(nullptr);
return newScript_; return newScript_;
} }
@ -1155,7 +1187,7 @@ struct TypeObject : public gc::TenuredCell
return hasAnyFlags(OBJECT_FLAG_PRE_TENURE) && !unknownProperties(); return hasAnyFlags(OBJECT_FLAG_PRE_TENURE) && !unknownProperties();
} }
bool hasTenuredProto() const { bool hasTenuredProto() {
return !(flags() & OBJECT_FLAG_NURSERY_PROTO); return !(flags() & OBJECT_FLAG_NURSERY_PROTO);
} }
@ -1211,7 +1243,23 @@ struct TypeObject : public gc::TenuredCell
void print(); void print();
inline void clearProperties(); 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 #ifdef JSGC_COMPACTING
void fixupAfterMovingGC(); void fixupAfterMovingGC();
@ -1219,12 +1267,7 @@ struct TypeObject : public gc::TenuredCell
size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const; size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
/* inline void finalize(FreeOp *fop);
* 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) {}
static inline ThingRootKind rootKind() { return THING_ROOT_TYPE_OBJECT; } static inline ThingRootKind rootKind() { return THING_ROOT_TYPE_OBJECT; }
@ -1241,7 +1284,7 @@ struct TypeObject : public gc::TenuredCell
} }
private: private:
inline uint32_t basePropertyCount() const; inline uint32_t basePropertyCount();
inline void setBasePropertyCount(uint32_t count); inline void setBasePropertyCount(uint32_t count);
static void staticAsserts() { static void staticAsserts() {
@ -1323,7 +1366,7 @@ class TypeScript
StackTypeSet typeArray_[1]; StackTypeSet typeArray_[1];
public: 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 { StackTypeSet *typeArray() const {
// Ensure typeArray_ is the last data member of TypeScript. // Ensure typeArray_ is the last data member of TypeScript.
JS_STATIC_ASSERT(sizeof(TypeScript) == JS_STATIC_ASSERT(sizeof(TypeScript) ==
@ -1387,7 +1430,6 @@ class TypeScript
static void Purge(JSContext *cx, HandleScript script); static void Purge(JSContext *cx, HandleScript script);
static void Sweep(FreeOp *fop, JSScript *script, bool *oom);
void destroy(); void destroy();
size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const { size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
@ -1583,9 +1625,6 @@ class CompilerOutput
MOZ_CRASH(); MOZ_CRASH();
sweepIndex_ = index; sweepIndex_ = index;
} }
void invalidateSweepIndex() {
sweepIndex_ = INVALID_SWEEP_INDEX;
}
uint32_t sweepIndex() { uint32_t sweepIndex() {
MOZ_ASSERT(sweepIndex_ != INVALID_SWEEP_INDEX); MOZ_ASSERT(sweepIndex_ != INVALID_SWEEP_INDEX);
return sweepIndex_; return sweepIndex_;
@ -1594,26 +1633,33 @@ class CompilerOutput
class RecompileInfo 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: public:
explicit RecompileInfo(uint32_t outputIndex = uint32_t(-1)) RecompileInfo(uint32_t outputIndex, uint32_t generation)
: outputIndex(outputIndex) : 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(TypeZone &types) const;
CompilerOutput *compilerOutput(JSContext *cx) const; CompilerOutput *compilerOutput(JSContext *cx) const;
bool shouldSweep(TypeZone &types); bool shouldSweep(TypeZone &types);
}; };
typedef Vector<RecompileInfo, 0, SystemAllocPolicy> RecompileInfoVector;
/* Type information for a compartment. */ /* Type information for a compartment. */
struct TypeCompartment struct TypeCompartment
{ {
/* Constraint solving worklist structures. */
/* Number of scripts in this compartment. */ /* Number of scripts in this compartment. */
unsigned scriptCount; unsigned scriptCount;
@ -1621,7 +1667,6 @@ struct TypeCompartment
AllocationSiteTable *allocationSiteTable; AllocationSiteTable *allocationSiteTable;
/* Tables for determining types of singleton/JSON objects. */ /* Tables for determining types of singleton/JSON objects. */
ArrayTypeTable *arrayTypeTable; ArrayTypeTable *arrayTypeTable;
ObjectTypeTable *objectTypeTable; ObjectTypeTable *objectTypeTable;
@ -1670,37 +1715,56 @@ struct TypeCompartment
void FixRestArgumentsType(ExclusiveContext *cxArg, ArrayObject *obj); void FixRestArgumentsType(ExclusiveContext *cxArg, ArrayObject *obj);
struct AutoEnterAnalysis;
struct TypeZone struct TypeZone
{ {
JS::Zone *zone_; JS::Zone *zone_;
/* Pool for type information in this zone. */ /* Pool for type information in this zone. */
static const size_t TYPE_LIFO_ALLOC_PRIMARY_CHUNK_SIZE = 8 * 1024; 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 * All Ion compilations that have occured in this zone, for indexing via
* RecompileInfo. This includes both valid and invalid compilations, though * RecompileInfo. This includes both valid and invalid compilations, though
* invalidated compilations are swept on GC. * 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. */ // During incremental sweeping, allocator holding the old type information
Vector<RecompileInfo> *pendingRecompiles; // 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); explicit TypeZone(JS::Zone *zone);
~TypeZone(); ~TypeZone();
JS::Zone *zone() const { return zone_; } 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(); void clearAllNewScriptsOnOOM();
/* Mark a script as needing recompilation once inference has finished. */ /* Mark a script as needing recompilation once inference has finished. */
void addPendingRecompile(JSContext *cx, const RecompileInfo &info); void addPendingRecompile(JSContext *cx, const RecompileInfo &info);
void addPendingRecompile(JSContext *cx, JSScript *script); void addPendingRecompile(JSContext *cx, JSScript *script);
void processPendingRecompiles(FreeOp *fop); void processPendingRecompiles(FreeOp *fop, RecompileInfoVector &recompiles);
}; };
enum SpewChannel { enum SpewChannel {

View File

@ -48,9 +48,20 @@ CompilerOutput::ion() const
inline CompilerOutput* inline CompilerOutput*
RecompileInfo::compilerOutput(TypeZone &types) const 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()) if (!types.compilerOutputs || outputIndex >= types.compilerOutputs->length())
return nullptr; return nullptr;
return &(*types.compilerOutputs)[outputIndex]; CompilerOutput *output = &(*types.compilerOutputs)[outputIndex];
return output->isValid() ? output : nullptr;
} }
inline CompilerOutput* inline CompilerOutput*
@ -66,8 +77,14 @@ RecompileInfo::shouldSweep(TypeZone &types)
if (!output || !output->isValid()) if (!output || !output->isValid())
return true; return true;
// Update this info for the output's new index in the zone's compiler outputs. // If this info is for a compilation that occurred after sweeping started,
outputIndex = output->sweepIndex(); // 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; return false;
} }
@ -241,44 +258,45 @@ struct AutoEnterAnalysis
/* Prevent GC activity in the middle of analysis. */ /* Prevent GC activity in the middle of analysis. */
gc::AutoSuppressGC suppressGC; 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; FreeOp *freeOp;
JSCompartment *compartment; Zone *zone;
bool oldActiveAnalysis;
explicit AutoEnterAnalysis(ExclusiveContext *cx) 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) AutoEnterAnalysis(FreeOp *fop, Zone *zone)
: suppressGC(comp) : suppressGC(zone->runtimeFromMainThread()), oom(zone)
{ {
init(fop, comp); init(fop, zone);
} }
~AutoEnterAnalysis() ~AutoEnterAnalysis()
{ {
compartment->activeAnalysis = oldActiveAnalysis; if (this != zone->types.activeAnalysis)
return;
/* zone->types.activeAnalysis = nullptr;
* If there are no more type inference activations on the stack,
* process any triggered recompilations. Note that we should not be if (!pendingRecompiles.empty())
* invoking any scripted code while type inference is running. zone->types.processPendingRecompiles(freeOp, pendingRecompiles);
*/
if (!compartment->activeAnalysis) {
TypeZone &types = compartment->zone()->types;
if (types.pendingRecompiles)
types.processPendingRecompiles(freeOp);
}
} }
private: private:
void init(FreeOp *fop, JSCompartment *comp) { void init(FreeOp *fop, Zone *zone) {
freeOp = fop; this->freeOp = fop;
compartment = comp; this->zone = zone;
oldActiveAnalysis = compartment->activeAnalysis;
compartment->activeAnalysis = true; 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>()) { if (args.callee().is<JSFunction>()) {
JSFunction *fun = &args.callee().as<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); TypeMonitorCallSlow(cx, &args.callee(), args, constructing);
} }
} }
@ -580,7 +598,8 @@ TypeScript::NumTypeSets(JSScript *script)
/* static */ inline StackTypeSet * /* static */ inline StackTypeSet *
TypeScript::ThisTypes(JSScript *script) 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) TypeScript::ArgTypes(JSScript *script, unsigned i)
{ {
MOZ_ASSERT(i < script->functionNonDelazifying()->nargs()); 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> template <typename TYPESET>
@ -641,9 +661,12 @@ TypeScript::BytecodeTypes(JSScript *script, jsbytecode *pc, uint32_t *bytecodeMa
TypeScript::BytecodeTypes(JSScript *script, jsbytecode *pc) TypeScript::BytecodeTypes(JSScript *script, jsbytecode *pc)
{ {
MOZ_ASSERT(CurrentThreadCanAccessRuntime(script->runtimeFromMainThread())); MOZ_ASSERT(CurrentThreadCanAccessRuntime(script->runtimeFromMainThread()));
TypeScript *types = script->types();
if (!types)
return nullptr;
uint32_t *hint = script->baselineScript()->bytecodeTypeMap() + script->nTypeSets(); uint32_t *hint = script->baselineScript()->bytecodeTypeMap() + script->nTypeSets();
return BytecodeTypes(script, pc, script->baselineScript()->bytecodeTypeMap(), return BytecodeTypes(script, pc, script->baselineScript()->bytecodeTypeMap(),
hint, script->types->typeArray()); hint, types->typeArray());
} }
struct AllocationSiteKey : public DefaultHasher<AllocationSiteKey> { struct AllocationSiteKey : public DefaultHasher<AllocationSiteKey> {
@ -767,15 +790,16 @@ TypeScript::MonitorAssign(JSContext *cx, HandleObject obj, jsid id)
/* static */ inline void /* static */ inline void
TypeScript::SetThis(JSContext *cx, JSScript *script, Type type) TypeScript::SetThis(JSContext *cx, JSScript *script, Type type)
{ {
if (!script->types) StackTypeSet *types = ThisTypes(script);
if (!types)
return; return;
if (!ThisTypes(script)->hasType(type)) { if (!types->hasType(type)) {
AutoEnterAnalysis enter(cx); AutoEnterAnalysis enter(cx);
InferSpew(ISpewOps, "externalType: setThis #%u: %s", InferSpew(ISpewOps, "externalType: setThis #%u: %s",
script->id(), TypeString(type)); 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 /* static */ inline void
TypeScript::SetArgument(JSContext *cx, JSScript *script, unsigned arg, Type type) TypeScript::SetArgument(JSContext *cx, JSScript *script, unsigned arg, Type type)
{ {
if (!script->types) StackTypeSet *types = ArgTypes(script, arg);
if (!types)
return; return;
if (!ArgTypes(script, arg)->hasType(type)) { if (!types->hasType(type)) {
AutoEnterAnalysis enter(cx); AutoEnterAnalysis enter(cx);
InferSpew(ISpewOps, "externalType: setArg #%u %u: %s", InferSpew(ISpewOps, "externalType: setArg #%u %u: %s",
script->id(), arg, TypeString(type)); 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->proto_ = proto.raw();
this->flags_ = initialFlags; this->flags_ = initialFlags;
setGeneration(zone()->types.generation);
InferSpew(ISpewOps, "newObject: %s", TypeObjectString(this)); InferSpew(ISpewOps, "newObject: %s", TypeObjectString(this));
} }
inline void
TypeObject::finalize(FreeOp *fop)
{
fop->delete_(newScript_.get());
}
inline uint32_t inline uint32_t
TypeObject::basePropertyCount() const TypeObject::basePropertyCount()
{ {
return (flags() & OBJECT_FLAG_PROPERTY_COUNT_MASK) >> OBJECT_FLAG_PROPERTY_COUNT_SHIFT; return (flags() & OBJECT_FLAG_PROPERTY_COUNT_MASK) >> OBJECT_FLAG_PROPERTY_COUNT_SHIFT;
} }
@ -1194,8 +1227,6 @@ TypeObject::setBasePropertyCount(uint32_t count)
inline HeapTypeSet * inline HeapTypeSet *
TypeObject::getProperty(ExclusiveContext *cx, jsid id) 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(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_IF(!JSID_IS_EMPTY(id), id == IdToTypeId(id));
MOZ_ASSERT(!unknownProperties()); MOZ_ASSERT(!unknownProperties());
@ -1282,10 +1313,17 @@ TypeNewScript::writeBarrierPre(TypeNewScript *newScript)
} } /* namespace js::types */ } } /* namespace js::types */
inline js::types::TypeScript *
JSScript::types()
{
maybeSweepTypes(nullptr);
return types_;
}
inline bool inline bool
JSScript::ensureHasTypes(JSContext *cx) JSScript::ensureHasTypes(JSContext *cx)
{ {
return types || makeTypes(cx); return types() || makeTypes(cx);
} }
namespace js { namespace js {

View File

@ -804,7 +804,7 @@ NewObjectMetadata(ExclusiveContext *cxArg, JSObject **pmetadata)
MOZ_ASSERT(!*pmetadata); MOZ_ASSERT(!*pmetadata);
if (JSContext *cx = cxArg->maybeJSContext()) { if (JSContext *cx = cxArg->maybeJSContext()) {
if (MOZ_UNLIKELY((size_t)cx->compartment()->hasObjectMetadataCallback()) && 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 // Use AutoEnterAnalysis to prohibit both any GC activity under the
// callback, and any reentering of JS via Invoke() etc. // callback, and any reentering of JS via Invoke() etc.

View File

@ -1001,7 +1001,7 @@ js_Disassemble1(JSContext *cx, HandleScript script, jsbytecode *pc,
case JOF_OBJECT: { case JOF_OBJECT: {
/* Don't call obj.toSource if analysis/inference is active. */ /* Don't call obj.toSource if analysis/inference is active. */
if (script->compartment()->activeAnalysis) { if (script->zone()->types.activeAnalysis) {
Sprint(sp, " object"); Sprint(sp, " object");
break; break;
} }
@ -2051,7 +2051,7 @@ js::StopPCCountProfiling(JSContext *cx)
for (ZonesIter zone(rt, SkipAtoms); !zone.done(); zone.next()) { for (ZonesIter zone(rt, SkipAtoms); !zone.done(); zone.next()) {
for (ZoneCellIter i(zone, FINALIZE_SCRIPT); !i.done(); i.next()) { for (ZoneCellIter i(zone, FINALIZE_SCRIPT); !i.done(); i.next()) {
JSScript *script = i.get<JSScript>(); JSScript *script = i.get<JSScript>();
if (script->hasScriptCounts() && script->types) { if (script->hasScriptCounts() && script->types()) {
ScriptAndCounts sac; ScriptAndCounts sac;
sac.script = script; sac.script = script;
sac.scriptCounts.set(script->releaseScriptCounts()); sac.scriptCounts.set(script->releaseScriptCounts());

View File

@ -2642,7 +2642,7 @@ JSScript::sizeOfData(mozilla::MallocSizeOf mallocSizeOf) const
size_t size_t
JSScript::sizeOfTypeScript(mozilla::MallocSizeOf mallocSizeOf) const 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); fop->runtime()->spsProfiler.onScriptFinalized(this);
if (types) if (types_)
types->destroy(); types_->destroy();
jit::DestroyIonScripts(fop, this); jit::DestroyIonScripts(fop, this);

View File

@ -784,10 +784,10 @@ class JSScript : public js::gc::TenuredCell
JSCompartment *compartment_; JSCompartment *compartment_;
/* Persistent type information retained across GCs. */
js::types::TypeScript *types;
private: private:
/* Persistent type information retained across GCs. */
js::types::TypeScript *types_;
// This script's ScriptSourceObject, or a CCW thereof. // This script's ScriptSourceObject, or a CCW thereof.
// //
// (When we clone a JSScript into a new compartment, we don't clone its // (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 needsArgsAnalysis_:1;
bool needsArgsObj_: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. // End of fields. Start methods.
// //
@ -1199,7 +1206,6 @@ class JSScript : public js::gc::TenuredCell
bool hasFreezeConstraints() const { return hasFreezeConstraints_; } bool hasFreezeConstraints() const { return hasFreezeConstraints_; }
void setHasFreezeConstraints() { hasFreezeConstraints_ = true; } void setHasFreezeConstraints() { hasFreezeConstraints_ = true; }
void clearHasFreezeConstraints() { hasFreezeConstraints_ = false; }
bool warnedAboutUndefinedProp() const { return warnedAboutUndefinedProp_; } bool warnedAboutUndefinedProp() const { return warnedAboutUndefinedProp_; }
void setWarnedAboutUndefinedProp() { warnedAboutUndefinedProp_ = true; } void setWarnedAboutUndefinedProp() { warnedAboutUndefinedProp_ = true; }
@ -1259,6 +1265,15 @@ class JSScript : public js::gc::TenuredCell
return needsArgsObj() && !strict(); 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 { bool hasAnyIonScript() const {
return hasIonScript() || hasParallelIonScript(); return hasIonScript() || hasParallelIonScript();
} }
@ -1429,6 +1444,10 @@ class JSScript : public js::gc::TenuredCell
/* Ensure the script has a TypeScript. */ /* Ensure the script has a TypeScript. */
inline bool ensureHasTypes(JSContext *cx); inline bool ensureHasTypes(JSContext *cx);
inline js::types::TypeScript *types();
void maybeSweepTypes(js::types::AutoClearTypeInferenceStateOnOOM *oom);
inline js::GlobalObject &global() const; inline js::GlobalObject &global() const;
js::GlobalObject &uninlinedGlobal() const; js::GlobalObject &uninlinedGlobal() const;

View File

@ -210,7 +210,7 @@ class ForkJoinOperation
TrafficLight appendCallTargetsToWorklist(uint32_t index, ExecutionStatus *status); TrafficLight appendCallTargetsToWorklist(uint32_t index, ExecutionStatus *status);
TrafficLight appendCallTargetToWorklist(HandleScript script, ExecutionStatus *status); TrafficLight appendCallTargetToWorklist(HandleScript script, ExecutionStatus *status);
bool addToWorklist(HandleScript script); 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 ForkJoinOperation
class ForkJoinShared : public ParallelJob, public Monitor class ForkJoinShared : public ParallelJob, public Monitor
@ -1155,7 +1155,7 @@ ForkJoinOperation::reportBailoutWarnings()
bool bool
ForkJoinOperation::invalidateBailedOutScripts() ForkJoinOperation::invalidateBailedOutScripts()
{ {
Vector<types::RecompileInfo> invalid(cx_); types::RecompileInfoVector invalid;
for (uint32_t i = 0; i < bailoutRecords_.length(); i++) { for (uint32_t i = 0; i < bailoutRecords_.length(); i++) {
switch (bailoutRecords_[i].cause) { switch (bailoutRecords_[i].cause) {
// No bailout. // No bailout.
@ -1312,10 +1312,10 @@ ForkJoinOperation::recoverFromBailout(ExecutionStatus *status)
} }
bool 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++) { for (uint32_t i = 0; i < invalid.length(); i++) {
if (scripts[i] == script->parallelIonScript()->recompileInfo()) if (invalid[i].compilerOutput(cx_)->script() == script)
return true; return true;
} }
return false; return false;

View File

@ -449,7 +449,7 @@ bool
js::Invoke(JSContext *cx, CallArgs args, MaybeConstruct construct) js::Invoke(JSContext *cx, CallArgs args, MaybeConstruct construct)
{ {
MOZ_ASSERT(args.length() <= ARGS_LENGTH_MAX); 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. */ /* Perform GC if necessary on exit from the function. */
AutoGCIfNeeded gcIfNeeded(cx); AutoGCIfNeeded gcIfNeeded(cx);
@ -1441,7 +1441,7 @@ Interpret(JSContext *cx, RunState &state)
JS_END_MACRO JS_END_MACRO
gc::MaybeVerifyBarriers(cx, true); gc::MaybeVerifyBarriers(cx, true);
MOZ_ASSERT(!cx->compartment()->activeAnalysis); MOZ_ASSERT(!cx->zone()->types.activeAnalysis);
InterpreterFrame *entryFrame = state.pushInterpreterFrame(cx); InterpreterFrame *entryFrame = state.pushInterpreterFrame(cx);
if (!entryFrame) if (!entryFrame)