mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 932982 - Trace type constraints and allow preserving jitcode in GCs without also marking all type information, r=billm, r=jandem
This commit is contained in:
parent
34b77c2df7
commit
6844e7e5ca
5
CLOBBER
5
CLOBBER
@ -21,6 +21,5 @@
|
||||
# Are you updating CLOBBER because you think it's needed for your WebIDL
|
||||
# changes to stick? As of bug 928195, this shouldn't be necessary! Please
|
||||
# don't change CLOBBER for WebIDL changes any more.
|
||||
Bug 928195 rewrote WebIDL build system integration from the ground up. This
|
||||
will hopefully be the last required clobber due to WebIDLs poorly interacting
|
||||
with the build system.
|
||||
|
||||
Bug 932982 apparently requires a clobber or else we get B2G mochitest-2 failures.
|
||||
|
@ -393,7 +393,6 @@ struct CompartmentStats
|
||||
macro(Other, NotLiveGCThing, baselineStubsOptimized) \
|
||||
macro(Other, NotLiveGCThing, ionData) \
|
||||
macro(Other, NotLiveGCThing, typeInferenceTypeScripts) \
|
||||
macro(Other, NotLiveGCThing, typeInferencePendingArrays) \
|
||||
macro(Other, NotLiveGCThing, typeInferenceAllocationSiteTables) \
|
||||
macro(Other, NotLiveGCThing, typeInferenceArrayTypeTables) \
|
||||
macro(Other, NotLiveGCThing, typeInferenceObjectTypeTables) \
|
||||
|
@ -1122,15 +1122,12 @@ gc::MarkCycleCollectorChildren(JSTracer *trc, Shape *shape)
|
||||
static void
|
||||
ScanTypeObject(GCMarker *gcmarker, types::TypeObject *type)
|
||||
{
|
||||
/* Don't mark properties for singletons. They'll be purged by the GC. */
|
||||
if (!type->singleton) {
|
||||
unsigned count = type->getPropertyCount();
|
||||
for (unsigned i = 0; i < count; i++) {
|
||||
types::Property *prop = type->getProperty(i);
|
||||
if (prop && JSID_IS_STRING(prop->id))
|
||||
PushMarkStack(gcmarker, JSID_TO_STRING(prop->id));
|
||||
}
|
||||
}
|
||||
|
||||
if (TaggedProto(type->proto).isObject())
|
||||
PushMarkStack(gcmarker, type->proto);
|
||||
@ -1350,7 +1347,7 @@ GCMarker::restoreValueArray(JSObject *obj, void **vpp, void **endp)
|
||||
}
|
||||
|
||||
void
|
||||
GCMarker::processMarkStackOther(SliceBudget &budget, uintptr_t tag, uintptr_t addr)
|
||||
GCMarker::processMarkStackOther(uintptr_t tag, uintptr_t addr)
|
||||
{
|
||||
if (tag == TypeTag) {
|
||||
ScanTypeObject(this, reinterpret_cast<types::TypeObject *>(addr));
|
||||
@ -1364,35 +1361,6 @@ GCMarker::processMarkStackOther(SliceBudget &budget, uintptr_t tag, uintptr_t ad
|
||||
pushObject(obj);
|
||||
} else if (tag == IonCodeTag) {
|
||||
MarkChildren(this, reinterpret_cast<jit::IonCode *>(addr));
|
||||
} else if (tag == ArenaTag) {
|
||||
ArenaHeader *aheader = reinterpret_cast<ArenaHeader *>(addr);
|
||||
AllocKind thingKind = aheader->getAllocKind();
|
||||
size_t thingSize = Arena::thingSize(thingKind);
|
||||
|
||||
for ( ; aheader; aheader = aheader->next) {
|
||||
Arena *arena = aheader->getArena();
|
||||
FreeSpan firstSpan(aheader->getFirstFreeSpan());
|
||||
const FreeSpan *span = &firstSpan;
|
||||
|
||||
for (uintptr_t thing = arena->thingsStart(thingKind); ; thing += thingSize) {
|
||||
JS_ASSERT(thing <= arena->thingsEnd());
|
||||
if (thing == span->first) {
|
||||
if (!span->hasNext())
|
||||
break;
|
||||
thing = span->last;
|
||||
span = span->nextSpan();
|
||||
} else {
|
||||
JSObject *object = reinterpret_cast<JSObject *>(thing);
|
||||
if (object->hasSingletonType() && object->markIfUnmarked(getMarkColor()))
|
||||
pushObject(object);
|
||||
budget.step();
|
||||
}
|
||||
}
|
||||
if (budget.isOverBudget()) {
|
||||
pushArenaList(aheader);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1430,7 +1398,7 @@ GCMarker::processMarkStackTop(SliceBudget &budget)
|
||||
goto scan_obj;
|
||||
}
|
||||
|
||||
processMarkStackOther(budget, tag, addr);
|
||||
processMarkStackOther(tag, addr);
|
||||
return;
|
||||
|
||||
scan_value_array:
|
||||
|
@ -726,11 +726,6 @@ js::gc::MarkRuntime(JSTracer *trc, bool useSavedRoots)
|
||||
if (IS_GC_MARKING_TRACER(trc) && !zone->isCollecting())
|
||||
continue;
|
||||
|
||||
if (IS_GC_MARKING_TRACER(trc) && zone->isPreservingCode()) {
|
||||
gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_MARK_TYPES);
|
||||
zone->markTypes(trc);
|
||||
}
|
||||
|
||||
/* Do not discard scripts with counts while profiling. */
|
||||
if (rt->profilingScripts && !rt->isHeapMinorCollecting()) {
|
||||
for (CellIterUnderGC i(zone, FINALIZE_SCRIPT); !i.done(); i.next()) {
|
||||
|
@ -276,7 +276,6 @@ static const PhaseInfo phases[] = {
|
||||
{ PHASE_PURGE, "Purge", PHASE_NO_PARENT },
|
||||
{ PHASE_MARK, "Mark", PHASE_NO_PARENT },
|
||||
{ PHASE_MARK_ROOTS, "Mark Roots", PHASE_MARK },
|
||||
{ PHASE_MARK_TYPES, "Mark Types", PHASE_MARK_ROOTS },
|
||||
{ PHASE_MARK_DELAYED, "Mark Delayed", PHASE_MARK },
|
||||
{ PHASE_SWEEP, "Sweep", PHASE_NO_PARENT },
|
||||
{ PHASE_SWEEP_MARK, "Mark During Sweeping", PHASE_SWEEP },
|
||||
|
@ -28,7 +28,6 @@ enum Phase {
|
||||
PHASE_PURGE,
|
||||
PHASE_MARK,
|
||||
PHASE_MARK_ROOTS,
|
||||
PHASE_MARK_TYPES,
|
||||
PHASE_MARK_DELAYED,
|
||||
PHASE_SWEEP,
|
||||
PHASE_SWEEP_MARK,
|
||||
|
@ -80,35 +80,6 @@ Zone::setNeedsBarrier(bool needs, ShouldUpdateIon updateIon)
|
||||
needsBarrier_ = needs;
|
||||
}
|
||||
|
||||
void
|
||||
Zone::markTypes(JSTracer *trc)
|
||||
{
|
||||
/*
|
||||
* Mark all scripts, type objects and singleton JS objects in the
|
||||
* compartment. These can be referred to directly by type sets, which we
|
||||
* cannot modify while code which depends on these type sets is active.
|
||||
*/
|
||||
JS_ASSERT(isPreservingCode());
|
||||
|
||||
for (CellIterUnderGC i(this, FINALIZE_SCRIPT); !i.done(); i.next()) {
|
||||
JSScript *script = i.get<JSScript>();
|
||||
MarkScriptRoot(trc, &script, "mark_types_script");
|
||||
JS_ASSERT(script == i.get<JSScript>());
|
||||
}
|
||||
|
||||
for (size_t thingKind = FINALIZE_OBJECT0; thingKind < FINALIZE_OBJECT_LIMIT; thingKind++) {
|
||||
ArenaHeader *aheader = allocator.arenas.getFirstArena(static_cast<AllocKind>(thingKind));
|
||||
if (aheader)
|
||||
trc->runtime->gcMarker.pushArenaList(aheader);
|
||||
}
|
||||
|
||||
for (CellIterUnderGC i(this, FINALIZE_TYPE_OBJECT); !i.done(); i.next()) {
|
||||
types::TypeObject *type = i.get<types::TypeObject>();
|
||||
MarkTypeObjectRoot(trc, &type, "mark_types_scan");
|
||||
JS_ASSERT(type == i.get<types::TypeObject>());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Zone::resetGCMallocBytes()
|
||||
{
|
||||
@ -144,7 +115,7 @@ Zone::sweep(FreeOp *fop, bool releaseTypes)
|
||||
if (active)
|
||||
releaseTypes = false;
|
||||
|
||||
if (!isPreservingCode()) {
|
||||
{
|
||||
gcstats::AutoPhase ap(fop->runtime()->gcStats, gcstats::PHASE_DISCARD_ANALYSIS);
|
||||
types.sweep(fop, releaseTypes);
|
||||
}
|
||||
|
@ -310,8 +310,6 @@ struct Zone : public JS::shadow::Zone,
|
||||
js_ReportAllocationOverflow(nullptr);
|
||||
}
|
||||
|
||||
void markTypes(JSTracer *trc);
|
||||
|
||||
js::types::TypeZone types;
|
||||
|
||||
void sweep(js::FreeOp *fop, bool releaseTypes);
|
||||
|
@ -5825,7 +5825,7 @@ CodeGenerator::link(JSContext *cx, types::CompilerConstraintList *constraints)
|
||||
safepoints_.size(), callTargets.length(),
|
||||
patchableBackedges_.length());
|
||||
if (!ionScript) {
|
||||
recompileInfo.compilerOutput(cx->compartment()->types)->invalidate();
|
||||
recompileInfo.compilerOutput(cx->zone()->types)->invalidate();
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -5850,7 +5850,7 @@ CodeGenerator::link(JSContext *cx, types::CompilerConstraintList *constraints)
|
||||
// Use js_free instead of IonScript::Destroy: the cache list and
|
||||
// backedge list are still uninitialized.
|
||||
js_free(ionScript);
|
||||
recompileInfo.compilerOutput(cx->compartment()->types)->invalidate();
|
||||
recompileInfo.compilerOutput(cx->zone()->types)->invalidate();
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -5871,7 +5871,7 @@ CodeGenerator::link(JSContext *cx, types::CompilerConstraintList *constraints)
|
||||
/* resetUses */ false, /* cancelOffThread*/ false))
|
||||
{
|
||||
js_free(ionScript);
|
||||
recompileInfo.compilerOutput(cx->compartment()->types)->invalidate();
|
||||
recompileInfo.compilerOutput(cx->zone()->types)->invalidate();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -2481,7 +2481,7 @@ jit::InvalidateAll(FreeOp *fop, Zone *zone)
|
||||
|
||||
|
||||
void
|
||||
jit::Invalidate(types::TypeCompartment &types, FreeOp *fop,
|
||||
jit::Invalidate(types::TypeZone &types, FreeOp *fop,
|
||||
const Vector<types::RecompileInfo> &invalid, bool resetUses,
|
||||
bool cancelOffThread)
|
||||
{
|
||||
@ -2564,7 +2564,7 @@ void
|
||||
jit::Invalidate(JSContext *cx, const Vector<types::RecompileInfo> &invalid, bool resetUses,
|
||||
bool cancelOffThread)
|
||||
{
|
||||
jit::Invalidate(cx->compartment()->types, cx->runtime()->defaultFreeOp(), invalid, resetUses,
|
||||
jit::Invalidate(cx->zone()->types, cx->runtime()->defaultFreeOp(), invalid, resetUses,
|
||||
cancelOffThread);
|
||||
}
|
||||
|
||||
@ -2611,14 +2611,13 @@ FinishInvalidationOf(FreeOp *fop, JSScript *script, IonScript *ionScript, bool p
|
||||
else
|
||||
script->setIonScript(nullptr);
|
||||
|
||||
// If this script has Ion code on the stack, invalidation() will return
|
||||
// true. In this case we have to wait until destroying it.
|
||||
if (!ionScript->invalidated()) {
|
||||
types::TypeCompartment &types = script->compartment()->types;
|
||||
types::TypeZone &types = script->zone()->types;
|
||||
ionScript->recompileInfo().compilerOutput(types)->invalidate();
|
||||
|
||||
// If this script has Ion code on the stack, invalidated() will return
|
||||
// true. In this case we have to wait until destroying it.
|
||||
if (!ionScript->invalidated())
|
||||
jit::IonScript::Destroy(fop, ionScript);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@ -2637,8 +2636,6 @@ jit::FinishDiscardJitCode(FreeOp *fop, JSCompartment *comp)
|
||||
// Free optimized baseline stubs.
|
||||
if (comp->jitCompartment())
|
||||
comp->jitCompartment()->optimizedStubSpace()->free();
|
||||
|
||||
comp->types.clearCompilerOutputs(fop);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -364,7 +364,7 @@ IonExecStatus IonCannon(JSContext *cx, RunState &state);
|
||||
IonExecStatus FastInvoke(JSContext *cx, HandleFunction fun, CallArgs &args);
|
||||
|
||||
// Walk the stack and invalidate active Ion frames for the invalid scripts.
|
||||
void Invalidate(types::TypeCompartment &types, FreeOp *fop,
|
||||
void Invalidate(types::TypeZone &types, FreeOp *fop,
|
||||
const Vector<types::RecompileInfo> &invalid, bool resetUses = true,
|
||||
bool cancelOffThread = true);
|
||||
void Invalidate(JSContext *cx, const Vector<types::RecompileInfo> &invalid, bool resetUses = true,
|
||||
|
@ -6296,7 +6296,7 @@ IonBuilder::getStaticName(JSObject *staticObject, PropertyName *name, bool *psuc
|
||||
types::HeapTypeSetKey property = staticType->property(id);
|
||||
if (!property.maybeTypes() ||
|
||||
!property.maybeTypes()->definiteProperty() ||
|
||||
property.configured(constraints(), staticType))
|
||||
property.configured(constraints()))
|
||||
{
|
||||
// The property has been reconfigured as non-configurable, non-enumerable
|
||||
// or non-writable.
|
||||
@ -6386,7 +6386,7 @@ IonBuilder::setStaticName(JSObject *staticObject, PropertyName *name)
|
||||
types::HeapTypeSetKey property = staticType->property(id);
|
||||
if (!property.maybeTypes() ||
|
||||
!property.maybeTypes()->definiteProperty() ||
|
||||
property.configured(constraints(), staticType))
|
||||
property.configured(constraints()))
|
||||
{
|
||||
// The property has been reconfigured as non-configurable, non-enumerable
|
||||
// or non-writable.
|
||||
@ -7836,7 +7836,7 @@ IonBuilder::getDefiniteSlot(types::TemporaryTypeSet *types, PropertyName *name,
|
||||
*property = type->property(id);
|
||||
return property->maybeTypes() &&
|
||||
property->maybeTypes()->definiteProperty() &&
|
||||
!property->configured(constraints(), type);
|
||||
!property->configured(constraints());
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -526,6 +526,9 @@ struct IonScript
|
||||
const types::RecompileInfo& recompileInfo() const {
|
||||
return recompileInfo_;
|
||||
}
|
||||
types::RecompileInfo& recompileInfoRef() {
|
||||
return recompileInfo_;
|
||||
}
|
||||
uint32_t incrOsrPcMismatchCounter() {
|
||||
return ++osrPcMismatchCounter_;
|
||||
}
|
||||
|
@ -3025,7 +3025,7 @@ jit::PropertyReadIsIdempotent(types::CompilerConstraintList *constraints,
|
||||
|
||||
// Check if the property has been reconfigured or is a getter.
|
||||
types::HeapTypeSetKey property = object->property(NameToId(name));
|
||||
if (property.configured(constraints, object))
|
||||
if (property.configured(constraints))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -48,7 +48,6 @@ JSCompartment::JSCompartment(Zone *zone, const JS::CompartmentOptions &options =
|
||||
#endif
|
||||
global_(nullptr),
|
||||
enterCompartmentDepth(0),
|
||||
lastCodeRelease(0),
|
||||
data(nullptr),
|
||||
objectMetadataCallback(nullptr),
|
||||
lastAnimationTime(0),
|
||||
@ -545,13 +544,6 @@ JSCompartment::sweep(FreeOp *fop, bool releaseTypes)
|
||||
WeakMapBase::sweepCompartment(this);
|
||||
}
|
||||
|
||||
if (zone()->isPreservingCode()) {
|
||||
gcstats::AutoPhase ap2(rt->gcStats, gcstats::PHASE_DISCARD_ANALYSIS);
|
||||
types.sweepShapes(fop);
|
||||
} else {
|
||||
JS_ASSERT(!types.constrainedOutputs);
|
||||
}
|
||||
|
||||
NativeIterator *ni = enumerators->next();
|
||||
while (ni != enumerators) {
|
||||
JSObject *iterObj = ni->iterObj();
|
||||
@ -856,7 +848,6 @@ JSCompartment::clearTraps(FreeOp *fop)
|
||||
|
||||
void
|
||||
JSCompartment::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,
|
||||
size_t *tiPendingArrays,
|
||||
size_t *tiAllocationSiteTables,
|
||||
size_t *tiArrayTypeTables,
|
||||
size_t *tiObjectTypeTables,
|
||||
@ -868,7 +859,7 @@ JSCompartment::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,
|
||||
size_t *baselineStubsOptimized)
|
||||
{
|
||||
*compartmentObject += mallocSizeOf(this);
|
||||
types.addSizeOfExcludingThis(mallocSizeOf, tiPendingArrays, tiAllocationSiteTables,
|
||||
types.addSizeOfExcludingThis(mallocSizeOf, tiAllocationSiteTables,
|
||||
tiArrayTypeTables, tiObjectTypeTables);
|
||||
*shapesCompartmentTables += baseShapes.sizeOfExcludingThis(mallocSizeOf)
|
||||
+ initialShapes.sizeOfExcludingThis(mallocSizeOf)
|
||||
|
@ -187,8 +187,6 @@ struct JSCompartment
|
||||
*/
|
||||
void adoptWorkerAllocator(js::Allocator *workerAllocator);
|
||||
|
||||
|
||||
int64_t lastCodeRelease;
|
||||
bool activeAnalysis;
|
||||
|
||||
/* Type information about the scripts and objects in this compartment. */
|
||||
@ -221,7 +219,6 @@ struct JSCompartment
|
||||
|
||||
public:
|
||||
void addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,
|
||||
size_t *tiPendingArrays,
|
||||
size_t *tiAllocationSiteTables,
|
||||
size_t *tiArrayTypeTables,
|
||||
size_t *tiObjectTypeTables,
|
||||
|
@ -2857,13 +2857,9 @@ ShouldPreserveJITCode(JSCompartment *comp, int64_t currentTime)
|
||||
|
||||
if (rt->alwaysPreserveCode)
|
||||
return true;
|
||||
if (comp->lastAnimationTime + PRMJ_USEC_PER_SEC >= currentTime &&
|
||||
comp->lastCodeRelease + (PRMJ_USEC_PER_SEC * 300) >= currentTime)
|
||||
{
|
||||
if (comp->lastAnimationTime + PRMJ_USEC_PER_SEC >= currentTime)
|
||||
return true;
|
||||
}
|
||||
|
||||
comp->lastCodeRelease = currentTime;
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -3942,12 +3938,12 @@ BeginSweepingZoneGroup(JSRuntime *rt)
|
||||
bool releaseTypes = ReleaseObservedTypes(rt);
|
||||
for (GCCompartmentGroupIter c(rt); !c.done(); c.next()) {
|
||||
gcstats::AutoSCC scc(rt->gcStats, rt->gcZoneGroupIndex);
|
||||
c->sweep(&fop, releaseTypes);
|
||||
c->sweep(&fop, releaseTypes && !c->zone()->isPreservingCode());
|
||||
}
|
||||
|
||||
for (GCZoneGroupIter zone(rt); !zone.done(); zone.next()) {
|
||||
gcstats::AutoSCC scc(rt->gcStats, rt->gcZoneGroupIndex);
|
||||
zone->sweep(&fop, releaseTypes);
|
||||
zone->sweep(&fop, releaseTypes && !zone->isPreservingCode());
|
||||
}
|
||||
}
|
||||
|
||||
@ -5304,10 +5300,6 @@ js::ReleaseAllJITCode(FreeOp *fop)
|
||||
jit::FinishDiscardBaselineScript(fop, script);
|
||||
}
|
||||
}
|
||||
|
||||
/* Sweep now invalidated compiler outputs from each compartment. */
|
||||
for (CompartmentsIter comp(fop->runtime(), SkipAtoms); !comp.done(); comp.next())
|
||||
comp->types.clearCompilerOutputs(fop);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -1091,7 +1091,6 @@ struct GCMarker : public JSTracer {
|
||||
ObjectTag,
|
||||
TypeTag,
|
||||
XmlTag,
|
||||
ArenaTag,
|
||||
SavedValueArrayTag,
|
||||
IonCodeTag,
|
||||
LastTag = IonCodeTag
|
||||
@ -1119,10 +1118,6 @@ struct GCMarker : public JSTracer {
|
||||
pushTaggedPtr(ObjectTag, obj);
|
||||
}
|
||||
|
||||
void pushArenaList(gc::ArenaHeader *firstArena) {
|
||||
pushTaggedPtr(ArenaTag, firstArena);
|
||||
}
|
||||
|
||||
void pushType(types::TypeObject *type) {
|
||||
pushTaggedPtr(TypeTag, type);
|
||||
}
|
||||
@ -1229,7 +1224,7 @@ struct GCMarker : public JSTracer {
|
||||
bool restoreValueArray(JSObject *obj, void **vpp, void **endp);
|
||||
void saveValueRanges();
|
||||
inline void processMarkStackTop(SliceBudget &budget);
|
||||
void processMarkStackOther(SliceBudget &budget, uintptr_t tag, uintptr_t addr);
|
||||
void processMarkStackOther(uintptr_t tag, uintptr_t addr);
|
||||
|
||||
void appendGrayRoot(void *thing, JSGCTraceKind kind);
|
||||
|
||||
|
@ -243,14 +243,6 @@ types::TypeHasProperty(JSContext *cx, TypeObject *obj, jsid id, const Value &val
|
||||
if (id == id___proto__(cx) || id == id_constructor(cx) || id == id_caller(cx))
|
||||
return true;
|
||||
|
||||
/*
|
||||
* If we called in here while resolving a type constraint, we may be in the
|
||||
* middle of resolving a standard class and the type sets will not be updated
|
||||
* until the outer TypeSet::add finishes.
|
||||
*/
|
||||
if (cx->compartment()->types.pendingCount)
|
||||
return true;
|
||||
|
||||
Type type = GetValueType(value);
|
||||
|
||||
AutoEnterAnalysis enter(cx);
|
||||
@ -742,12 +734,12 @@ class TypeCompilerConstraint : public TypeConstraint
|
||||
|
||||
void newType(JSContext *cx, TypeSet *source, Type type) {
|
||||
if (data.invalidateOnNewType(type))
|
||||
cx->compartment()->types.addPendingRecompile(cx, compilation);
|
||||
cx->zone()->types.addPendingRecompile(cx, compilation);
|
||||
}
|
||||
|
||||
void newPropertyState(JSContext *cx, TypeSet *source) {
|
||||
if (data.invalidateOnNewPropertyState(source))
|
||||
cx->compartment()->types.addPendingRecompile(cx, compilation);
|
||||
cx->zone()->types.addPendingRecompile(cx, compilation);
|
||||
}
|
||||
|
||||
void newObjectState(JSContext *cx, TypeObject *object) {
|
||||
@ -755,7 +747,16 @@ class TypeCompilerConstraint : public TypeConstraint
|
||||
// will be sent on changes to its state, so always invalidate any
|
||||
// associated compilations.
|
||||
if (object->unknownProperties() || data.invalidateOnNewObjectState(object))
|
||||
cx->compartment()->types.addPendingRecompile(cx, compilation);
|
||||
cx->zone()->types.addPendingRecompile(cx, compilation);
|
||||
}
|
||||
|
||||
TypeConstraint *sweep(TypeZone &zone) {
|
||||
if (data.shouldSweep() || compilation.shouldSweep(zone))
|
||||
return nullptr;
|
||||
TypeConstraint *res = zone.typeLifoAlloc.new_<TypeCompilerConstraint<T> >(compilation, data);
|
||||
if (!res)
|
||||
zone.setPendingNukeTypes();
|
||||
return res;
|
||||
}
|
||||
};
|
||||
|
||||
@ -908,13 +909,21 @@ class TypeConstraintFreezeStack : public TypeConstraint
|
||||
|
||||
const char *kind() { return "freezeStack"; }
|
||||
|
||||
void newType(JSContext *cx, TypeSet *source, Type type)
|
||||
{
|
||||
void newType(JSContext *cx, TypeSet *source, Type type) {
|
||||
/*
|
||||
* Unlike TypeConstraintFreeze, triggering this constraint once does
|
||||
* not disable it on future changes to the type set.
|
||||
*/
|
||||
cx->compartment()->types.addPendingRecompile(cx, script_);
|
||||
cx->zone()->types.addPendingRecompile(cx, script_);
|
||||
}
|
||||
|
||||
TypeConstraint *sweep(TypeZone &zone) {
|
||||
if (IsScriptAboutToBeFinalized(&script_))
|
||||
return nullptr;
|
||||
TypeConstraint *res = zone.typeLifoAlloc.new_<TypeConstraintFreezeStack>(script_);
|
||||
if (!res)
|
||||
zone.setPendingNukeTypes();
|
||||
return res;
|
||||
}
|
||||
};
|
||||
|
||||
@ -929,15 +938,22 @@ types::FinishCompilation(JSContext *cx, HandleScript script, ExecutionMode execu
|
||||
|
||||
CompilerOutput co(script, executionMode);
|
||||
|
||||
TypeCompartment &types = cx->compartment()->types;
|
||||
if (!types.constrainedOutputs) {
|
||||
types.constrainedOutputs = cx->new_< Vector<CompilerOutput> >(cx);
|
||||
if (!types.constrainedOutputs)
|
||||
TypeZone &types = cx->zone()->types;
|
||||
if (!types.compilerOutputs) {
|
||||
types.compilerOutputs = cx->new_< Vector<CompilerOutput> >(cx);
|
||||
if (!types.compilerOutputs)
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t index = types.constrainedOutputs->length();
|
||||
if (!types.constrainedOutputs->append(co))
|
||||
#ifdef DEBUG
|
||||
for (size_t i = 0; i < types.compilerOutputs->length(); i++) {
|
||||
const CompilerOutput &co = (*types.compilerOutputs)[i];
|
||||
JS_ASSERT_IF(co.isValid(), co.script() != script || co.mode() != executionMode);
|
||||
}
|
||||
#endif
|
||||
|
||||
uint32_t index = types.compilerOutputs->length();
|
||||
if (!types.compilerOutputs->append(co))
|
||||
return false;
|
||||
|
||||
*precompileInfo = RecompileInfo(index);
|
||||
@ -979,8 +995,8 @@ types::FinishCompilation(JSContext *cx, HandleScript script, ExecutionMode execu
|
||||
array[i].add(cx, cx->typeLifoAlloc().new_<TypeConstraintFreezeStack>(entry.script), false);
|
||||
}
|
||||
|
||||
if (!succeeded || types.constrainedOutputs->back().pendingInvalidation()) {
|
||||
types.constrainedOutputs->back().invalidate();
|
||||
if (!succeeded || types.compilerOutputs->back().pendingInvalidation()) {
|
||||
types.compilerOutputs->back().invalidate();
|
||||
script->resetUseCount();
|
||||
return false;
|
||||
}
|
||||
@ -1009,6 +1025,8 @@ class ConstraintDataFreeze
|
||||
? property.maybeTypes()->isSubset(expected)
|
||||
: property.maybeTypes()->empty();
|
||||
}
|
||||
|
||||
bool shouldSweep() { return false; }
|
||||
};
|
||||
|
||||
} /* anonymous namespace */
|
||||
@ -1195,6 +1213,8 @@ class ConstraintDataFreezeObjectFlags
|
||||
{
|
||||
return !invalidateOnNewObjectState(property.object()->maybeType());
|
||||
}
|
||||
|
||||
bool shouldSweep() { return false; }
|
||||
};
|
||||
|
||||
} /* anonymous namespace */
|
||||
@ -1289,6 +1309,8 @@ class ConstraintDataFreezeObjectForInlinedCall
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool shouldSweep() { return false; }
|
||||
};
|
||||
|
||||
// Constraint which triggers recompilation when the template object for a
|
||||
@ -1315,6 +1337,11 @@ class ConstraintDataFreezeObjectForNewScriptTemplate
|
||||
{
|
||||
return !invalidateOnNewObjectState(property.object()->maybeType());
|
||||
}
|
||||
|
||||
bool shouldSweep() {
|
||||
// Note: |templateObject| is only used for equality testing.
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// Constraint which triggers recompilation when the underlying data pointer for
|
||||
@ -1341,6 +1368,11 @@ class ConstraintDataFreezeObjectForTypedArrayBuffer
|
||||
{
|
||||
return !invalidateOnNewObjectState(property.object()->maybeType());
|
||||
}
|
||||
|
||||
bool shouldSweep() {
|
||||
// Note: |viewData| is only used for equality testing.
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
} /* anonymous namespace */
|
||||
@ -1413,10 +1445,7 @@ namespace {
|
||||
class ConstraintDataFreezeConfiguredProperty
|
||||
{
|
||||
public:
|
||||
TypeObjectKey *object;
|
||||
|
||||
ConstraintDataFreezeConfiguredProperty(TypeObjectKey *object)
|
||||
: object(object)
|
||||
ConstraintDataFreezeConfiguredProperty()
|
||||
{}
|
||||
|
||||
const char *kind() { return "freezeConfiguredProperty"; }
|
||||
@ -1430,31 +1459,16 @@ class ConstraintDataFreezeConfiguredProperty
|
||||
bool constraintHolds(JSContext *cx,
|
||||
const HeapTypeSetKey &property, TemporaryTypeSet *expected)
|
||||
{
|
||||
// Everywhere compiled code depends on definite properties associated
|
||||
// with a type object's newScript, we need to make sure there are
|
||||
// constraints in place which will mark those properties as configured
|
||||
// should the definite properties be invalidated.
|
||||
TypeObject *type = object->isSingleObject()
|
||||
? object->asSingleObject()->type()
|
||||
: object->asTypeObject();
|
||||
if (type->flags & OBJECT_FLAG_NEW_SCRIPT_REGENERATE) {
|
||||
type->flags &= ~OBJECT_FLAG_NEW_SCRIPT_REGENERATE;
|
||||
if (type->hasNewScript()) {
|
||||
CheckNewScriptProperties(cx, type, type->newScript()->fun);
|
||||
} else {
|
||||
JS_ASSERT(type->flags & OBJECT_FLAG_ADDENDUM_CLEARED);
|
||||
type->flags &= ~OBJECT_FLAG_NEW_SCRIPT_REGENERATE;
|
||||
}
|
||||
}
|
||||
|
||||
return !property.maybeTypes()->configuredProperty();
|
||||
}
|
||||
|
||||
bool shouldSweep() { return false; }
|
||||
};
|
||||
|
||||
} /* anonymous namespace */
|
||||
|
||||
bool
|
||||
HeapTypeSetKey::configured(CompilerConstraintList *constraints, TypeObjectKey *type)
|
||||
HeapTypeSetKey::configured(CompilerConstraintList *constraints)
|
||||
{
|
||||
if (maybeTypes() && maybeTypes()->configuredProperty())
|
||||
return true;
|
||||
@ -1463,7 +1477,7 @@ HeapTypeSetKey::configured(CompilerConstraintList *constraints, TypeObjectKey *t
|
||||
|
||||
typedef CompilerConstraintInstance<ConstraintDataFreezeConfiguredProperty> T;
|
||||
constraints->add(alloc->new_<T>(alloc, *this,
|
||||
ConstraintDataFreezeConfiguredProperty(type)));
|
||||
ConstraintDataFreezeConfiguredProperty()));
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -1986,7 +2000,7 @@ PrototypeHasIndexedProperty(CompilerConstraintList *constraints, JSObject *obj)
|
||||
if (type->unknownProperties())
|
||||
return true;
|
||||
HeapTypeSetKey index = type->property(JSID_VOID);
|
||||
if (index.configured(constraints, type) || index.isOwnProperty(constraints))
|
||||
if (index.configured(constraints) || index.isOwnProperty(constraints))
|
||||
return true;
|
||||
obj = obj->getProto();
|
||||
} while (obj);
|
||||
@ -2024,27 +2038,8 @@ types::TypeCanHaveExtraIndexedProperties(CompilerConstraintList *constraints,
|
||||
return PrototypeHasIndexedProperty(constraints, proto);
|
||||
}
|
||||
|
||||
bool
|
||||
TypeCompartment::growPendingArray(JSContext *cx)
|
||||
{
|
||||
unsigned newCapacity = js::Max(unsigned(100), pendingCapacity * 2);
|
||||
PendingWork *newArray = js_pod_calloc<PendingWork>(newCapacity);
|
||||
if (!newArray) {
|
||||
cx->compartment()->types.setPendingNukeTypes(cx);
|
||||
return false;
|
||||
}
|
||||
|
||||
PodCopy(newArray, pendingArray, pendingCount);
|
||||
js_free(pendingArray);
|
||||
|
||||
pendingArray = newArray;
|
||||
pendingCapacity = newCapacity;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
TypeCompartment::processPendingRecompiles(FreeOp *fop)
|
||||
TypeZone::processPendingRecompiles(FreeOp *fop)
|
||||
{
|
||||
if (!pendingRecompiles)
|
||||
return;
|
||||
@ -2095,11 +2090,9 @@ TypeZone::nukeTypes(FreeOp *fop)
|
||||
*/
|
||||
JS_ASSERT(pendingNukeTypes);
|
||||
|
||||
for (CompartmentsInZoneIter comp(zone()); !comp.done(); comp.next()) {
|
||||
if (comp->types.pendingRecompiles) {
|
||||
fop->free_(comp->types.pendingRecompiles);
|
||||
comp->types.pendingRecompiles = nullptr;
|
||||
}
|
||||
if (pendingRecompiles) {
|
||||
fop->free_(pendingRecompiles);
|
||||
pendingRecompiles = nullptr;
|
||||
}
|
||||
|
||||
inferenceEnabled = false;
|
||||
@ -2119,7 +2112,7 @@ TypeZone::nukeTypes(FreeOp *fop)
|
||||
}
|
||||
|
||||
void
|
||||
TypeCompartment::addPendingRecompile(JSContext *cx, const RecompileInfo &info)
|
||||
TypeZone::addPendingRecompile(JSContext *cx, const RecompileInfo &info)
|
||||
{
|
||||
CompilerOutput *co = info.compilerOutput(cx);
|
||||
if (!co || !co->isValid() || co->pendingInvalidation())
|
||||
@ -2145,7 +2138,7 @@ TypeCompartment::addPendingRecompile(JSContext *cx, const RecompileInfo &info)
|
||||
}
|
||||
|
||||
void
|
||||
TypeCompartment::addPendingRecompile(JSContext *cx, JSScript *script)
|
||||
TypeZone::addPendingRecompile(JSContext *cx, JSScript *script)
|
||||
{
|
||||
JS_ASSERT(script);
|
||||
|
||||
@ -3146,6 +3139,15 @@ class TypeConstraintClearDefiniteGetterSetter : public TypeConstraint
|
||||
}
|
||||
|
||||
void newType(JSContext *cx, TypeSet *source, Type type) {}
|
||||
|
||||
TypeConstraint *sweep(TypeZone &zone) {
|
||||
if (IsTypeObjectAboutToBeFinalized(&object))
|
||||
return nullptr;
|
||||
TypeConstraint *res = zone.typeLifoAlloc.new_<TypeConstraintClearDefiniteGetterSetter>(object);
|
||||
if (!res)
|
||||
zone.setPendingNukeTypes();
|
||||
return res;
|
||||
}
|
||||
};
|
||||
|
||||
bool
|
||||
@ -3192,6 +3194,15 @@ class TypeConstraintClearDefiniteSingle : public TypeConstraint
|
||||
if (source->baseFlags() || source->getObjectCount() > 1)
|
||||
object->clearAddendum(cx);
|
||||
}
|
||||
|
||||
TypeConstraint *sweep(TypeZone &zone) {
|
||||
if (IsTypeObjectAboutToBeFinalized(&object))
|
||||
return nullptr;
|
||||
TypeConstraint *res = zone.typeLifoAlloc.new_<TypeConstraintClearDefiniteSingle>(object);
|
||||
if (!res)
|
||||
zone.setPendingNukeTypes();
|
||||
return res;
|
||||
}
|
||||
};
|
||||
|
||||
void
|
||||
@ -3941,8 +3952,19 @@ ConstraintTypeSet::sweep(Zone *zone)
|
||||
}
|
||||
}
|
||||
|
||||
/* All constraints are wiped out on each GC. */
|
||||
/*
|
||||
* Type constraints only hold weak references. Copy constraints referring
|
||||
* to data that is still live into the zone's new arena.
|
||||
*/
|
||||
TypeConstraint *constraint = constraintList;
|
||||
constraintList = nullptr;
|
||||
while (constraint) {
|
||||
if (TypeConstraint *copy = constraint->sweep(zone->types)) {
|
||||
copy->next = constraintList;
|
||||
constraintList = copy;
|
||||
}
|
||||
constraint = constraint->next;
|
||||
}
|
||||
}
|
||||
|
||||
inline void
|
||||
@ -3962,18 +3984,6 @@ TypeObject::clearProperties()
|
||||
inline void
|
||||
TypeObject::sweep(FreeOp *fop)
|
||||
{
|
||||
if (singleton) {
|
||||
JS_ASSERT(!hasNewScript());
|
||||
|
||||
/*
|
||||
* All properties can be discarded. We will regenerate them as needed
|
||||
* as code gets reanalyzed.
|
||||
*/
|
||||
clearProperties();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isMarked()) {
|
||||
if (addendum)
|
||||
fop->free_(addendum);
|
||||
@ -3996,6 +4006,14 @@ TypeObject::sweep(FreeOp *fop)
|
||||
for (unsigned i = 0; i < oldCapacity; i++) {
|
||||
Property *prop = oldArray[i];
|
||||
if (prop) {
|
||||
if (singleton && !prop->types.constraintList) {
|
||||
/*
|
||||
* Don't copy over properties of singleton objects which
|
||||
* don't have associated constraints. The contents of these
|
||||
* type sets will be regenerated as necessary.
|
||||
*/
|
||||
continue;
|
||||
}
|
||||
Property *newProp = typeLifoAlloc.new_<Property>(*prop);
|
||||
if (newProp) {
|
||||
Property **pentry =
|
||||
@ -4015,6 +4033,10 @@ TypeObject::sweep(FreeOp *fop)
|
||||
setBasePropertyCount(propertyCount);
|
||||
} else if (propertyCount == 1) {
|
||||
Property *prop = (Property *) propertySet;
|
||||
if (singleton && !prop->types.constraintList) {
|
||||
// Skip, as above.
|
||||
clearProperties();
|
||||
} else {
|
||||
Property *newProp = typeLifoAlloc.new_<Property>(*prop);
|
||||
if (newProp) {
|
||||
propertySet = (Property **) newProp;
|
||||
@ -4023,19 +4045,12 @@ TypeObject::sweep(FreeOp *fop)
|
||||
zone()->types.setPendingNukeTypes();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (basePropertyCount() <= SET_ARRAY_SIZE) {
|
||||
for (unsigned i = 0; i < basePropertyCount(); i++)
|
||||
JS_ASSERT(propertySet[i]);
|
||||
}
|
||||
|
||||
/*
|
||||
* The GC will clear out the constraints ensuring the correctness of the
|
||||
* newScript information, these constraints will need to be regenerated
|
||||
* the next time we compile code which depends on this info.
|
||||
*/
|
||||
if (hasNewScript())
|
||||
flags |= OBJECT_FLAG_NEW_SCRIPT_REGENERATE;
|
||||
}
|
||||
|
||||
void
|
||||
@ -4119,52 +4134,6 @@ TypeCompartment::sweep(FreeOp *fop)
|
||||
e.rekeyFront(key);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* The pending array is reset on GC, it can grow large (75+ KB) and is easy
|
||||
* to reallocate if the compartment becomes active again.
|
||||
*/
|
||||
if (pendingArray)
|
||||
fop->free_(pendingArray);
|
||||
|
||||
pendingArray = nullptr;
|
||||
pendingCapacity = 0;
|
||||
}
|
||||
|
||||
void
|
||||
TypeCompartment::sweepShapes(FreeOp *fop)
|
||||
{
|
||||
/*
|
||||
* Sweep any weak shape references that may be finalized even if a GC is
|
||||
* preserving type information.
|
||||
*/
|
||||
if (objectTypeTable) {
|
||||
for (ObjectTypeTable::Enum e(*objectTypeTable); !e.empty(); e.popFront()) {
|
||||
const ObjectTableKey &key = e.front().key();
|
||||
ObjectTableEntry &entry = e.front().value();
|
||||
|
||||
if (IsShapeAboutToBeFinalized(entry.shape.unsafeGet())) {
|
||||
fop->free_(key.properties);
|
||||
fop->free_(entry.types);
|
||||
e.removeFront();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TypeCompartment::clearCompilerOutputs(FreeOp *fop)
|
||||
{
|
||||
if (constrainedOutputs) {
|
||||
fop->delete_(constrainedOutputs);
|
||||
constrainedOutputs = nullptr;
|
||||
}
|
||||
|
||||
if (pendingRecompiles) {
|
||||
JS_ASSERT(pendingRecompiles->length() == 0);
|
||||
fop->delete_(pendingRecompiles);
|
||||
pendingRecompiles = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@ -4193,7 +4162,6 @@ JSCompartment::sweepNewTypeObjectTable(TypeObjectWithNewScriptSet &table)
|
||||
|
||||
TypeCompartment::~TypeCompartment()
|
||||
{
|
||||
js_free(pendingArray);
|
||||
js_delete(arrayTypeTable);
|
||||
js_delete(objectTypeTable);
|
||||
js_delete(allocationSiteTable);
|
||||
@ -4212,12 +4180,6 @@ TypeScript::Sweep(FreeOp *fop, JSScript *script)
|
||||
/* Remove constraints and references to dead objects from the persistent type sets. */
|
||||
for (unsigned i = 0; i < num; i++)
|
||||
typeArray[i].sweep(compartment->zone());
|
||||
|
||||
/*
|
||||
* Freeze constraints on stack type sets need to be regenerated the next
|
||||
* time the script is analyzed.
|
||||
*/
|
||||
script->clearHasFreezeConstraints();
|
||||
}
|
||||
|
||||
void
|
||||
@ -4234,20 +4196,10 @@ Zone::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, size_t *typePoo
|
||||
|
||||
void
|
||||
TypeCompartment::addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf,
|
||||
size_t *pendingArrays,
|
||||
size_t *allocationSiteTables,
|
||||
size_t *arrayTypeTables,
|
||||
size_t *objectTypeTables)
|
||||
{
|
||||
/* Pending arrays are cleared on GC along with the analysis pool. */
|
||||
*pendingArrays += mallocSizeOf(pendingArray);
|
||||
|
||||
/*
|
||||
* TypeCompartment::pendingRecompiles is non-nullptr only while inference
|
||||
* code is running.
|
||||
*/
|
||||
JS_ASSERT(!pendingRecompiles);
|
||||
|
||||
if (allocationSiteTable)
|
||||
*allocationSiteTables += allocationSiteTable->sizeOfIncludingThis(mallocSizeOf);
|
||||
|
||||
@ -4289,6 +4241,8 @@ TypeObject::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
|
||||
TypeZone::TypeZone(Zone *zone)
|
||||
: zone_(zone),
|
||||
typeLifoAlloc(TYPE_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
|
||||
compilerOutputs(nullptr),
|
||||
pendingRecompiles(nullptr),
|
||||
pendingNukeTypes(false),
|
||||
inferenceEnabled(false)
|
||||
{
|
||||
@ -4296,6 +4250,8 @@ TypeZone::TypeZone(Zone *zone)
|
||||
|
||||
TypeZone::~TypeZone()
|
||||
{
|
||||
js_delete(compilerOutputs);
|
||||
js_delete(pendingRecompiles);
|
||||
}
|
||||
|
||||
void
|
||||
@ -4312,11 +4268,21 @@ TypeZone::sweep(FreeOp *fop, bool releaseTypes)
|
||||
LifoAlloc oldAlloc(typeLifoAlloc.defaultChunkSize());
|
||||
oldAlloc.steal(&typeLifoAlloc);
|
||||
|
||||
/*
|
||||
* Sweep analysis information and everything depending on it from the
|
||||
* compartment, including all remaining mjit code if inference is
|
||||
* enabled in the compartment.
|
||||
*/
|
||||
/* Sweep and find compressed indexes for each compiler output. */
|
||||
size_t newCompilerOutputCount = 0;
|
||||
if (compilerOutputs) {
|
||||
for (size_t i = 0; i < compilerOutputs->length(); i++) {
|
||||
CompilerOutput &output = (*compilerOutputs)[i];
|
||||
if (output.isValid()) {
|
||||
JSScript *script = output.script();
|
||||
if (IsScriptAboutToBeFinalized(&script))
|
||||
output.invalidate();
|
||||
else
|
||||
output.setSweepIndex(newCompilerOutputCount++);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (inferenceEnabled) {
|
||||
gcstats::AutoPhase ap2(rt->gcStats, gcstats::PHASE_DISCARD_TI);
|
||||
|
||||
@ -4328,6 +4294,21 @@ TypeZone::sweep(FreeOp *fop, bool releaseTypes)
|
||||
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();
|
||||
|
||||
JS_ASSERT(!script->hasIonScript());
|
||||
JS_ASSERT(!script->hasParallelIonScript());
|
||||
} else {
|
||||
/* Update the recompile indexes in any IonScripts still on the script. */
|
||||
if (script->hasIonScript())
|
||||
script->ionScript()->recompileInfoRef().shouldSweep(*this);
|
||||
if (script->hasParallelIonScript())
|
||||
script->parallelIonScript()->recompileInfoRef().shouldSweep(*this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -4347,6 +4328,20 @@ TypeZone::sweep(FreeOp *fop, bool releaseTypes)
|
||||
comp->types.sweep(fop);
|
||||
}
|
||||
|
||||
if (compilerOutputs) {
|
||||
size_t sweepIndex = 0;
|
||||
for (size_t i = 0; i < compilerOutputs->length(); i++) {
|
||||
CompilerOutput output = (*compilerOutputs)[i];
|
||||
if (output.isValid()) {
|
||||
JS_ASSERT(sweepIndex == output.sweepIndex());
|
||||
output.setSweepIndex(0);
|
||||
(*compilerOutputs)[sweepIndex++] = output;
|
||||
}
|
||||
}
|
||||
JS_ASSERT(sweepIndex == newCompilerOutputCount);
|
||||
JS_ALWAYS_TRUE(compilerOutputs->resize(newCompilerOutputCount));
|
||||
}
|
||||
|
||||
{
|
||||
gcstats::AutoPhase ap2(rt->gcStats, gcstats::PHASE_CLEAR_SCRIPT_ANALYSIS);
|
||||
for (CellIterUnderGC i(zone(), FINALIZE_SCRIPT); !i.done(); i.next()) {
|
||||
|
122
js/src/jsinfer.h
122
js/src/jsinfer.h
@ -177,7 +177,7 @@ namespace analyze {
|
||||
|
||||
namespace types {
|
||||
|
||||
class TypeCompartment;
|
||||
class TypeZone;
|
||||
class TypeSet;
|
||||
class TypeObjectKey;
|
||||
|
||||
@ -278,28 +278,10 @@ inline Type GetValueType(const Value &val);
|
||||
* Type information about the values observed within scripts and about the
|
||||
* contents of the heap is accumulated as the program executes. Compilation
|
||||
* accumulates constraints relating type information on the heap with the
|
||||
* compilations that should be invalidated when those types change. This data
|
||||
* is periodically cleared to reduce memory usage.
|
||||
*
|
||||
* GCs may clear both analysis information and jitcode. Sometimes GCs will
|
||||
* preserve all information and code, and will not collect any scripts, type
|
||||
* objects or singleton JS objects.
|
||||
*
|
||||
* The following data is cleared by all non-preserving GCs:
|
||||
*
|
||||
* - The ScriptAnalysis for each analyzed script and data from each analysis
|
||||
* pass performed.
|
||||
*
|
||||
* - Property type sets for singleton JS objects.
|
||||
*
|
||||
* - Type constraints and dead references in all type sets.
|
||||
*
|
||||
* The following data is occasionally cleared by non-preserving GCs:
|
||||
*
|
||||
* - TypeScripts and their type sets are occasionally destroyed, per a timer.
|
||||
*
|
||||
* - When a JSScript or TypeObject is swept, type information for its contents
|
||||
* is destroyed.
|
||||
* compilations that should be invalidated when those types change. Type
|
||||
* information and constraints are allocated in the zone's typeLifoAlloc,
|
||||
* and on GC all data referring to live things is copied into a new allocator.
|
||||
* Thus, type set and constraints only hold weak references.
|
||||
*/
|
||||
|
||||
/*
|
||||
@ -334,6 +316,12 @@ public:
|
||||
* state.
|
||||
*/
|
||||
virtual void newObjectState(JSContext *cx, TypeObject *object) {}
|
||||
|
||||
/*
|
||||
* If the data this constraint refers to is still live, copy it into the
|
||||
* zone's new allocator. Type constraints only hold weak references.
|
||||
*/
|
||||
virtual TypeConstraint *sweep(TypeZone &zone) = 0;
|
||||
};
|
||||
|
||||
/* Flags and other state stored in TypeSet::flags */
|
||||
@ -394,13 +382,6 @@ enum {
|
||||
/* If set, addendum information should not be installed on this object. */
|
||||
OBJECT_FLAG_ADDENDUM_CLEARED = 0x2,
|
||||
|
||||
/*
|
||||
* If set, type constraints covering the correctness of the newScript
|
||||
* definite properties need to be regenerated before compiling any jitcode
|
||||
* which depends on this information.
|
||||
*/
|
||||
OBJECT_FLAG_NEW_SCRIPT_REGENERATE = 0x4,
|
||||
|
||||
/*
|
||||
* Whether we have ensured all type sets in the compartment contain
|
||||
* ANYOBJECT instead of this object.
|
||||
@ -869,12 +850,6 @@ struct TypeTypedObject : public TypeObjectAddendum
|
||||
* information is sensitive to changes in the property's type. Future changes
|
||||
* to the property (whether those uncovered by analysis or those occurring
|
||||
* in the VM) will treat these properties like those of any other type object.
|
||||
*
|
||||
* When a GC occurs, we wipe out all analysis information for all the
|
||||
* compartment's scripts, so can destroy all properties on singleton type
|
||||
* objects at the same time. If there is no reference on the stack to the
|
||||
* type object itself, the type object is also destroyed, and the JS object
|
||||
* reverts to having a lazy type.
|
||||
*/
|
||||
|
||||
/* Type information about an object accessed by a script. */
|
||||
@ -1316,7 +1291,7 @@ class HeapTypeSetKey
|
||||
|
||||
void freeze(CompilerConstraintList *constraints);
|
||||
JSValueType knownTypeTag(CompilerConstraintList *constraints);
|
||||
bool configured(CompilerConstraintList *constraints, TypeObjectKey *type);
|
||||
bool configured(CompilerConstraintList *constraints);
|
||||
bool isOwnProperty(CompilerConstraintList *constraints);
|
||||
bool knownSubset(CompilerConstraintList *constraints, const HeapTypeSetKey &other);
|
||||
JSObject *singleton(CompilerConstraintList *constraints);
|
||||
@ -1339,6 +1314,10 @@ class CompilerOutput
|
||||
// Whether this compilation is about to be invalidated.
|
||||
bool pendingInvalidation_ : 1;
|
||||
|
||||
// During sweeping, the list of compiler outputs is compacted and invalidated
|
||||
// outputs are removed. This gives the new index for a valid compiler output.
|
||||
uint32_t sweepIndex_ : 29;
|
||||
|
||||
public:
|
||||
CompilerOutput()
|
||||
: script_(nullptr), mode_(SequentialExecution), pendingInvalidation_(false)
|
||||
@ -1366,6 +1345,15 @@ class CompilerOutput
|
||||
bool pendingInvalidation() {
|
||||
return pendingInvalidation_;
|
||||
}
|
||||
|
||||
void setSweepIndex(uint32_t index) {
|
||||
if (index >= 1 << 29)
|
||||
MOZ_CRASH();
|
||||
sweepIndex_ = index;
|
||||
}
|
||||
uint32_t sweepIndex() {
|
||||
return sweepIndex_;
|
||||
}
|
||||
};
|
||||
|
||||
class RecompileInfo
|
||||
@ -1380,8 +1368,9 @@ class RecompileInfo
|
||||
bool operator == (const RecompileInfo &o) const {
|
||||
return outputIndex == o.outputIndex;
|
||||
}
|
||||
CompilerOutput *compilerOutput(TypeCompartment &types) const;
|
||||
CompilerOutput *compilerOutput(TypeZone &types) const;
|
||||
CompilerOutput *compilerOutput(JSContext *cx) const;
|
||||
bool shouldSweep(TypeZone &types);
|
||||
};
|
||||
|
||||
/* Type information for a compartment. */
|
||||
@ -1389,32 +1378,9 @@ struct TypeCompartment
|
||||
{
|
||||
/* Constraint solving worklist structures. */
|
||||
|
||||
/*
|
||||
* Worklist of types which need to be propagated to constraints. We use a
|
||||
* worklist to avoid blowing the native stack.
|
||||
*/
|
||||
struct PendingWork
|
||||
{
|
||||
TypeConstraint *constraint;
|
||||
ConstraintTypeSet *source;
|
||||
Type type;
|
||||
};
|
||||
PendingWork *pendingArray;
|
||||
unsigned pendingCount;
|
||||
unsigned pendingCapacity;
|
||||
|
||||
/* Whether we are currently resolving the pending worklist. */
|
||||
bool resolving;
|
||||
|
||||
/* Number of scripts in this compartment. */
|
||||
unsigned scriptCount;
|
||||
|
||||
/* Valid & Invalid script referenced by type constraints. */
|
||||
Vector<CompilerOutput> *constrainedOutputs;
|
||||
|
||||
/* Pending recompilations to perform before execution of JIT code can resume. */
|
||||
Vector<RecompileInfo> *pendingRecompiles;
|
||||
|
||||
/* Table for referencing types of objects keyed to an allocation site. */
|
||||
AllocationSiteTable *allocationSiteTable;
|
||||
|
||||
@ -1438,14 +1404,6 @@ struct TypeCompartment
|
||||
|
||||
inline JSCompartment *compartment();
|
||||
|
||||
/* Add a type to register with a list of constraints. */
|
||||
inline void addPending(JSContext *cx, TypeConstraint *constraint,
|
||||
ConstraintTypeSet *source, Type type);
|
||||
bool growPendingArray(JSContext *cx);
|
||||
|
||||
/* Resolve pending type registrations, excluding delayed ones. */
|
||||
inline void resolvePending(JSContext *cx);
|
||||
|
||||
/* Prints results of this compartment if spew is enabled or force is set. */
|
||||
void print(JSContext *cx, bool force);
|
||||
|
||||
@ -1461,26 +1419,16 @@ struct TypeCompartment
|
||||
/* Get or make an object for an allocation site, and add to the allocation site table. */
|
||||
TypeObject *addAllocationSiteTypeObject(JSContext *cx, AllocationSiteKey key);
|
||||
|
||||
void processPendingRecompiles(FreeOp *fop);
|
||||
|
||||
/* Mark all types as needing destruction once inference has 'finished'. */
|
||||
void setPendingNukeTypes(ExclusiveContext *cx);
|
||||
|
||||
/* Mark a script as needing recompilation once inference has finished. */
|
||||
void addPendingRecompile(JSContext *cx, const RecompileInfo &info);
|
||||
void addPendingRecompile(JSContext *cx, JSScript *script);
|
||||
|
||||
/* Mark any type set containing obj as having a generic object type. */
|
||||
void markSetsUnknown(JSContext *cx, TypeObject *obj);
|
||||
|
||||
void sweep(FreeOp *fop);
|
||||
void sweepShapes(FreeOp *fop);
|
||||
void clearCompilerOutputs(FreeOp *fop);
|
||||
|
||||
void finalizeObjects();
|
||||
|
||||
void addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf,
|
||||
size_t *pendingArrays,
|
||||
size_t *allocationSiteTables,
|
||||
size_t *arrayTypeTables,
|
||||
size_t *objectTypeTables);
|
||||
@ -1496,6 +1444,16 @@ struct TypeZone
|
||||
static const size_t TYPE_LIFO_ALLOC_PRIMARY_CHUNK_SIZE = 8 * 1024;
|
||||
js::LifoAlloc typeLifoAlloc;
|
||||
|
||||
/*
|
||||
* All Ion compilations that have occured in this zone, for indexing via
|
||||
* RecompileInfo. This includes both valid and invalid compilations, though
|
||||
* invalidated compilations are swept on GC.
|
||||
*/
|
||||
Vector<CompilerOutput> *compilerOutputs;
|
||||
|
||||
/* Pending recompilations to perform before execution of JIT code can resume. */
|
||||
Vector<RecompileInfo> *pendingRecompiles;
|
||||
|
||||
/*
|
||||
* Bit set if all current types must be marked as unknown, and all scripts
|
||||
* recompiled. Caused by OOM failure within inference operations.
|
||||
@ -1516,6 +1474,12 @@ struct TypeZone
|
||||
/* Mark all types as needing destruction once inference has 'finished'. */
|
||||
void setPendingNukeTypes();
|
||||
|
||||
/* Mark a script as needing recompilation once inference has finished. */
|
||||
void addPendingRecompile(JSContext *cx, const RecompileInfo &info);
|
||||
void addPendingRecompile(JSContext *cx, JSScript *script);
|
||||
|
||||
void processPendingRecompiles(FreeOp *fop);
|
||||
|
||||
void nukeTypes(FreeOp *fop);
|
||||
};
|
||||
|
||||
|
@ -103,17 +103,29 @@ CompilerOutput::ion() const
|
||||
}
|
||||
|
||||
inline CompilerOutput*
|
||||
RecompileInfo::compilerOutput(TypeCompartment &types) const
|
||||
RecompileInfo::compilerOutput(TypeZone &types) const
|
||||
{
|
||||
if (!types.constrainedOutputs || outputIndex >= types.constrainedOutputs->length())
|
||||
if (!types.compilerOutputs || outputIndex >= types.compilerOutputs->length())
|
||||
return nullptr;
|
||||
return &(*types.constrainedOutputs)[outputIndex];
|
||||
return &(*types.compilerOutputs)[outputIndex];
|
||||
}
|
||||
|
||||
inline CompilerOutput*
|
||||
RecompileInfo::compilerOutput(JSContext *cx) const
|
||||
{
|
||||
return compilerOutput(cx->compartment()->types);
|
||||
return compilerOutput(cx->zone()->types);
|
||||
}
|
||||
|
||||
inline bool
|
||||
RecompileInfo::shouldSweep(TypeZone &types)
|
||||
{
|
||||
CompilerOutput *output = compilerOutput(types);
|
||||
if (!output || !output->isValid())
|
||||
return true;
|
||||
|
||||
// Update this info for the output's new index in the zone's compiler outputs.
|
||||
outputIndex = output->sweepIndex();
|
||||
return false;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
@ -287,14 +299,13 @@ struct AutoEnterAnalysis
|
||||
* If there are no more type inference activations on the stack,
|
||||
* process any triggered recompilations. Note that we should not be
|
||||
* invoking any scripted code while type inference is running.
|
||||
* :TODO: assert this.
|
||||
*/
|
||||
if (!compartment->activeAnalysis) {
|
||||
TypeCompartment *types = &compartment->types;
|
||||
if (compartment->zone()->types.pendingNukeTypes)
|
||||
compartment->zone()->types.nukeTypes(freeOp);
|
||||
else if (types->pendingRecompiles)
|
||||
types->processPendingRecompiles(freeOp);
|
||||
TypeZone &types = compartment->zone()->types;
|
||||
if (types.pendingNukeTypes)
|
||||
types.nukeTypes(freeOp);
|
||||
else if (types.pendingRecompiles)
|
||||
types.processPendingRecompiles(freeOp);
|
||||
}
|
||||
}
|
||||
|
||||
@ -848,50 +859,6 @@ TypeCompartment::compartment()
|
||||
return (JSCompartment *)((char *)this - offsetof(JSCompartment, types));
|
||||
}
|
||||
|
||||
inline void
|
||||
TypeCompartment::addPending(JSContext *cx, TypeConstraint *constraint,
|
||||
ConstraintTypeSet *source, Type type)
|
||||
{
|
||||
JS_ASSERT(this == &cx->compartment()->types);
|
||||
JS_ASSERT(!cx->runtime()->isHeapBusy());
|
||||
|
||||
InferSpew(ISpewOps, "pending: %sC%p%s %s",
|
||||
InferSpewColor(constraint), constraint, InferSpewColorReset(),
|
||||
TypeString(type));
|
||||
|
||||
if ((pendingCount == pendingCapacity) && !growPendingArray(cx))
|
||||
return;
|
||||
|
||||
PendingWork &pending = pendingArray[pendingCount++];
|
||||
pending.constraint = constraint;
|
||||
pending.source = source;
|
||||
pending.type = type;
|
||||
}
|
||||
|
||||
inline void
|
||||
TypeCompartment::resolvePending(JSContext *cx)
|
||||
{
|
||||
JS_ASSERT(this == &cx->compartment()->types);
|
||||
|
||||
if (resolving) {
|
||||
/* There is an active call further up resolving the worklist. */
|
||||
return;
|
||||
}
|
||||
|
||||
resolving = true;
|
||||
|
||||
/* Handle all pending type registrations. */
|
||||
while (pendingCount) {
|
||||
const PendingWork &pending = pendingArray[--pendingCount];
|
||||
InferSpew(ISpewOps, "resolve: %sC%p%s %s",
|
||||
InferSpewColor(pending.constraint), pending.constraint,
|
||||
InferSpewColorReset(), TypeString(pending.type));
|
||||
pending.constraint->newType(cx, pending.source, pending.type);
|
||||
}
|
||||
|
||||
resolving = false;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
// TypeSet
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
@ -1215,10 +1182,9 @@ ConstraintTypeSet::addType(ExclusiveContext *cxArg, Type type)
|
||||
if (JSContext *cx = cxArg->maybeJSContext()) {
|
||||
TypeConstraint *constraint = constraintList;
|
||||
while (constraint) {
|
||||
cx->compartment()->types.addPending(cx, constraint, this, type);
|
||||
constraint->newType(cx, this, type);
|
||||
constraint = constraint->next;
|
||||
}
|
||||
cx->compartment()->types.resolvePending(cx);
|
||||
} else {
|
||||
JS_ASSERT(!constraintList);
|
||||
}
|
||||
|
@ -215,7 +215,6 @@ StatsCompartmentCallback(JSRuntime *rt, void *data, JSCompartment *compartment)
|
||||
|
||||
// Measure the compartment object itself, and things hanging off it.
|
||||
compartment->addSizeOfIncludingThis(rtStats->mallocSizeOf_,
|
||||
&cStats.typeInferencePendingArrays,
|
||||
&cStats.typeInferenceAllocationSiteTables,
|
||||
&cStats.typeInferenceArrayTypeTables,
|
||||
&cStats.typeInferenceObjectTypeTables,
|
||||
|
@ -2087,10 +2087,6 @@ ReportCompartmentStats(const JS::CompartmentStats &cStats,
|
||||
cStats.typeInferenceTypeScripts,
|
||||
"Memory used by type sets associated with scripts.");
|
||||
|
||||
ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("type-inference/pending-arrays"),
|
||||
cStats.typeInferencePendingArrays,
|
||||
"Memory used for solving constraints during type inference.");
|
||||
|
||||
ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("type-inference/allocation-site-tables"),
|
||||
cStats.typeInferenceAllocationSiteTables,
|
||||
"Memory indexing type objects associated with allocation sites.");
|
||||
|
Loading…
Reference in New Issue
Block a user