mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1072564 - Incrementalize sweeping of type information, r=billm.
This commit is contained in:
parent
8e53554c0a
commit
363383cd7a
@ -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.
|
||||||
|
@ -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;
|
||||||
|
@ -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)
|
||||||
{
|
{
|
||||||
|
@ -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
|
||||||
|
@ -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 },
|
||||||
|
@ -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,
|
||||||
|
@ -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
|
||||||
|
@ -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();
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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:
|
||||||
|
@ -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);
|
||||||
|
@ -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
|
||||||
|
@ -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());
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
262
js/src/jsgc.cpp
262
js/src/jsgc.cpp
@ -352,22 +352,6 @@ struct js::gc::FinalizePhase
|
|||||||
|
|
||||||
#define PHASE(x, p) { ArrayLength(x), x, p }
|
#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++) {
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
130
js/src/jsinfer.h
130
js/src/jsinfer.h
@ -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 {
|
||||||
|
@ -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 {
|
||||||
|
@ -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.
|
||||||
|
@ -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());
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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)
|
||||||
|
Loading…
Reference in New Issue
Block a user