mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-01 06:35:42 +00:00
Avoid shape regeneration and property cache purging from the GC unless the shape generator overflows (488731, r=jorendorff).
This commit is contained in:
parent
78d790d9f7
commit
c46598451a
@ -104,9 +104,20 @@ FinishThreadData(JSThreadData *data)
|
||||
static void
|
||||
PurgeThreadData(JSContext *cx, JSThreadData *data)
|
||||
{
|
||||
js_PurgeGSNCache(&data->gsnCache);
|
||||
|
||||
if (cx->runtime->gcRegenShapes)
|
||||
js_PurgePropertyCache(cx, &data->propertyCache);
|
||||
|
||||
# ifdef JS_TRACER
|
||||
JSTraceMonitor *tm = &data->traceMonitor;
|
||||
tm->reservedDoublePoolPtr = tm->reservedDoublePool;
|
||||
|
||||
/*
|
||||
* FIXME: bug 506117. We should flush only if (cx->runtime->gcRegenShapes),
|
||||
* but we can't yet, because traces may embed sprop and object references,
|
||||
* and we don't yet mark such embedded refs.
|
||||
*/
|
||||
tm->needFlush = JS_TRUE;
|
||||
|
||||
if (tm->recorder)
|
||||
@ -123,9 +134,6 @@ PurgeThreadData(JSContext *cx, JSThreadData *data)
|
||||
|
||||
/* Destroy eval'ed scripts. */
|
||||
js_DestroyScriptsToGC(cx, data);
|
||||
|
||||
js_PurgeGSNCache(&data->gsnCache);
|
||||
js_PurgePropertyCache(cx, &data->propertyCache);
|
||||
}
|
||||
|
||||
#ifdef JS_THREADSAFE
|
||||
|
@ -389,7 +389,8 @@ struct JSRuntime {
|
||||
*/
|
||||
JSPackedBool gcPoke;
|
||||
JSPackedBool gcRunning;
|
||||
uint16 gcPadding;
|
||||
JSPackedBool gcRegenShapes;
|
||||
uint8 gcPadding;
|
||||
#ifdef JS_GC_ZEAL
|
||||
jsrefcount gcZeal;
|
||||
#endif
|
||||
@ -1539,6 +1540,7 @@ static JS_INLINE uint32
|
||||
js_RegenerateShapeForGC(JSContext *cx)
|
||||
{
|
||||
JS_ASSERT(cx->runtime->gcRunning);
|
||||
JS_ASSERT(cx->runtime->gcRegenShapes);
|
||||
|
||||
/*
|
||||
* Under the GC, compared with js_GenerateShape, we don't need to use
|
||||
|
@ -3599,7 +3599,6 @@ js_GC(JSContext *cx, JSGCInvocationKind gckind)
|
||||
#ifdef JS_TRACER
|
||||
js_PurgeJITOracle();
|
||||
#endif
|
||||
js_PurgeThreads(cx);
|
||||
|
||||
restart:
|
||||
rt->gcNumber++;
|
||||
@ -3611,8 +3610,13 @@ js_GC(JSContext *cx, JSGCInvocationKind gckind)
|
||||
* Same for the protoHazardShape proxy-shape standing in for all object
|
||||
* prototypes having readonly or setter properties.
|
||||
*/
|
||||
rt->shapeGen = 0;
|
||||
rt->protoHazardShape = 0;
|
||||
if (rt->shapeGen & SHAPE_OVERFLOW_BIT) {
|
||||
rt->gcRegenShapes = true;
|
||||
rt->shapeGen = 0;
|
||||
rt->protoHazardShape = 0;
|
||||
}
|
||||
|
||||
js_PurgeThreads(cx);
|
||||
|
||||
/*
|
||||
* Mark phase.
|
||||
@ -3887,7 +3891,7 @@ out:
|
||||
rt->setGCLastBytes(rt->gcBytes);
|
||||
done_running:
|
||||
rt->gcLevel = 0;
|
||||
rt->gcRunning = JS_FALSE;
|
||||
rt->gcRunning = rt->gcRegenShapes = false;
|
||||
|
||||
#ifdef JS_THREADSAFE
|
||||
rt->gcThread = NULL;
|
||||
|
@ -5039,7 +5039,7 @@ js_TraceNativeEnumerators(JSTracer *trc)
|
||||
* re-number completely when tracing is done for the GC.
|
||||
*/
|
||||
rt = trc->context->runtime;
|
||||
if (IS_GC_MARKING_TRACER(trc)) {
|
||||
if (IS_GC_MARKING_TRACER(trc) && rt->gcRegenShapes) {
|
||||
memset(&rt->nativeEnumCache, 0, sizeof rt->nativeEnumCache);
|
||||
#ifdef JS_DUMP_ENUM_CACHE_STATS
|
||||
printf("nativeEnumCache hit rate %g%%\n",
|
||||
@ -5743,23 +5743,22 @@ js_TraceObject(JSTracer *trc, JSObject *obj)
|
||||
MeterEntryCount(scope->entryCount);
|
||||
#endif
|
||||
|
||||
sprop = SCOPE_LAST_PROP(scope);
|
||||
sprop = scope->lastProp;
|
||||
if (sprop) {
|
||||
JS_ASSERT(scope->has(sprop));
|
||||
|
||||
/* Regenerate property cache shape ids if GC'ing. */
|
||||
if (IS_GC_MARKING_TRACER(trc)) {
|
||||
uint32 shape, oldshape;
|
||||
|
||||
shape = js_RegenerateShapeForGC(cx);
|
||||
if (!(sprop->flags & SPROP_MARK)) {
|
||||
oldshape = sprop->shape;
|
||||
sprop->shape = shape;
|
||||
if (IS_GC_MARKING_TRACER(trc) && cx->runtime->gcRegenShapes) {
|
||||
if (!(sprop->flags & SPROP_FLAG_SHAPE_REGEN)) {
|
||||
sprop->shape = js_RegenerateShapeForGC(cx);
|
||||
sprop->flags |= SPROP_FLAG_SHAPE_REGEN;
|
||||
if (scope->shape != oldshape)
|
||||
shape = js_RegenerateShapeForGC(cx);
|
||||
}
|
||||
|
||||
uint32 shape = sprop->shape;
|
||||
if (scope->hasOwnShape()) {
|
||||
shape = js_RegenerateShapeForGC(cx);
|
||||
JS_ASSERT(shape != sprop->shape);
|
||||
}
|
||||
scope->shape = shape;
|
||||
}
|
||||
|
||||
|
@ -1032,12 +1032,14 @@ JSScope::reportReadOnlyScope(JSContext *cx)
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_READ_ONLY, bytes);
|
||||
}
|
||||
|
||||
static inline void
|
||||
js_MakeScopeShapeUnique(JSContext *cx, JSScope *scope)
|
||||
void
|
||||
JSScope::generateOwnShape(JSContext *cx)
|
||||
{
|
||||
if (scope->object)
|
||||
js_LeaveTraceIfGlobalObject(cx, scope->object);
|
||||
scope->shape = js_GenerateShape(cx, false);
|
||||
if (object)
|
||||
js_LeaveTraceIfGlobalObject(cx, object);
|
||||
|
||||
shape = js_GenerateShape(cx, false);
|
||||
setOwnShape();
|
||||
}
|
||||
|
||||
JSScopeProperty *
|
||||
@ -1154,7 +1156,6 @@ JSScope::add(JSContext *cx, jsid id,
|
||||
}
|
||||
setMiddleDelete();
|
||||
}
|
||||
js_MakeScopeShapeUnique(cx, this);
|
||||
|
||||
/*
|
||||
* If we fail later on trying to find or create a new sprop, we will
|
||||
@ -1537,7 +1538,7 @@ JSScope::remove(JSContext *cx, jsid id)
|
||||
} else if (!hadMiddleDelete()) {
|
||||
setMiddleDelete();
|
||||
}
|
||||
js_MakeScopeShapeUnique(cx, this);
|
||||
generateOwnShape(cx);
|
||||
CHECK_ANCESTOR_LINE(this, true);
|
||||
|
||||
/* Last, consider shrinking this->table if its load factor is <= .25. */
|
||||
@ -1567,25 +1568,25 @@ JSScope::clear(JSContext *cx)
|
||||
void
|
||||
JSScope::brandingShapeChange(JSContext *cx, uint32 slot, jsval v)
|
||||
{
|
||||
js_MakeScopeShapeUnique(cx, this);
|
||||
generateOwnShape(cx);
|
||||
}
|
||||
|
||||
void
|
||||
JSScope::deletingShapeChange(JSContext *cx, JSScopeProperty *sprop)
|
||||
{
|
||||
js_MakeScopeShapeUnique(cx, this);
|
||||
generateOwnShape(cx);
|
||||
}
|
||||
|
||||
void
|
||||
JSScope::methodShapeChange(JSContext *cx, uint32 slot, jsval toval)
|
||||
{
|
||||
js_MakeScopeShapeUnique(cx, this);
|
||||
generateOwnShape(cx);
|
||||
}
|
||||
|
||||
void
|
||||
JSScope::protoShapeChange(JSContext *cx)
|
||||
{
|
||||
js_MakeScopeShapeUnique(cx, this);
|
||||
generateOwnShape(cx);
|
||||
}
|
||||
|
||||
void
|
||||
@ -1594,19 +1595,19 @@ JSScope::replacingShapeChange(JSContext *cx, JSScopeProperty *sprop, JSScopeProp
|
||||
if (shape == sprop->shape)
|
||||
shape = newsprop->shape;
|
||||
else
|
||||
js_MakeScopeShapeUnique(cx, this);
|
||||
generateOwnShape(cx);
|
||||
}
|
||||
|
||||
void
|
||||
JSScope::sealingShapeChange(JSContext *cx)
|
||||
{
|
||||
js_MakeScopeShapeUnique(cx, this);
|
||||
generateOwnShape(cx);
|
||||
}
|
||||
|
||||
void
|
||||
JSScope::shadowingShapeChange(JSContext *cx, JSScopeProperty *sprop)
|
||||
{
|
||||
js_MakeScopeShapeUnique(cx, this);
|
||||
generateOwnShape(cx);
|
||||
}
|
||||
|
||||
void
|
||||
@ -1826,10 +1827,12 @@ js_SweepScopeProperties(JSContext *cx)
|
||||
*/
|
||||
if (sprop->flags & SPROP_MARK) {
|
||||
sprop->flags &= ~SPROP_MARK;
|
||||
if (sprop->flags & SPROP_FLAG_SHAPE_REGEN)
|
||||
sprop->flags &= ~SPROP_FLAG_SHAPE_REGEN;
|
||||
else
|
||||
sprop->shape = js_RegenerateShapeForGC(cx);
|
||||
if (rt->gcRegenShapes) {
|
||||
if (sprop->flags & SPROP_FLAG_SHAPE_REGEN)
|
||||
sprop->flags &= ~SPROP_FLAG_SHAPE_REGEN;
|
||||
else
|
||||
sprop->shape = js_RegenerateShapeForGC(cx);
|
||||
}
|
||||
liveCount++;
|
||||
continue;
|
||||
}
|
||||
@ -1869,7 +1872,7 @@ js_SweepScopeProperties(JSContext *cx)
|
||||
sprop->kids = NULL;
|
||||
parent = sprop->parent;
|
||||
|
||||
/* Assert that grandparent has no kids or chunky kids. */
|
||||
/* The grandparent must have either no kids or chunky kids. */
|
||||
JS_ASSERT(!parent || !parent->kids ||
|
||||
KIDS_IS_CHUNKY(parent->kids));
|
||||
if (KIDS_IS_CHUNKY(kids)) {
|
||||
@ -1888,8 +1891,7 @@ js_SweepScopeProperties(JSContext *cx)
|
||||
* re-use by InsertPropertyTreeChild.
|
||||
*/
|
||||
chunk->kids[i] = NULL;
|
||||
if (!InsertPropertyTreeChild(rt, parent, kid,
|
||||
chunk)) {
|
||||
if (!InsertPropertyTreeChild(rt, parent, kid, chunk)) {
|
||||
/*
|
||||
* This can happen only if we failed to add an
|
||||
* entry to the root property hash table.
|
||||
|
@ -219,6 +219,7 @@ struct JSScope {
|
||||
bool createTable(JSContext *cx, bool report);
|
||||
bool changeTable(JSContext *cx, int change);
|
||||
void reportReadOnlyScope(JSContext *cx);
|
||||
void generateOwnShape(JSContext *cx);
|
||||
JSScopeProperty **searchTable(jsid id, bool adding);
|
||||
inline JSScopeProperty **search(jsid id, bool adding);
|
||||
JSScope *createEmptyScope(JSContext *cx, JSClass *clasp);
|
||||
@ -281,16 +282,14 @@ struct JSScope {
|
||||
MIDDLE_DELETE = 0x0001,
|
||||
SEALED = 0x0002,
|
||||
BRANDED = 0x0004,
|
||||
INDEXED_PROPERTIES = 0x0008
|
||||
INDEXED_PROPERTIES = 0x0008,
|
||||
OWN_SHAPE = 0x0010
|
||||
};
|
||||
|
||||
bool hadMiddleDelete() { return flags & MIDDLE_DELETE; }
|
||||
void setMiddleDelete() { flags |= MIDDLE_DELETE; }
|
||||
void clearMiddleDelete() { flags &= ~MIDDLE_DELETE; }
|
||||
|
||||
bool hadIndexedProperties() { return flags & INDEXED_PROPERTIES; }
|
||||
void setIndexedProperties() { flags |= INDEXED_PROPERTIES; }
|
||||
|
||||
/*
|
||||
* Don't define clearSealed, as it can't be done safely because JS_LOCK_OBJ
|
||||
* will avoid taking the lock if the object owns its scope and the scope is
|
||||
@ -306,7 +305,12 @@ struct JSScope {
|
||||
*/
|
||||
bool branded() { return flags & BRANDED; }
|
||||
void setBranded() { flags |= BRANDED; }
|
||||
void clearBranded() { flags &= ~BRANDED; }
|
||||
|
||||
bool hadIndexedProperties() { return flags & INDEXED_PROPERTIES; }
|
||||
void setIndexedProperties() { flags |= INDEXED_PROPERTIES; }
|
||||
|
||||
bool hasOwnShape() { return flags & OWN_SHAPE; }
|
||||
void setOwnShape() { flags |= OWN_SHAPE; }
|
||||
|
||||
bool owned() { return object != NULL; }
|
||||
};
|
||||
|
@ -1606,8 +1606,17 @@ js_DestroyScript(JSContext *cx, JSScript *script)
|
||||
JS_PURGE_GSN_CACHE(cx);
|
||||
|
||||
/*
|
||||
* The GC flushes all property caches, so no need to purge just the
|
||||
* entries for this script.
|
||||
* Worry about purging the property cache and any compiled traces related
|
||||
* to its bytecode if this script is being destroyed from JS_DestroyScript
|
||||
* or equivalent according to a mandatory "New/Destroy" protocol.
|
||||
*
|
||||
* The GC purges all property caches when regenerating shapes upon shape
|
||||
* generator overflow, so no need in that event to purge just the entries
|
||||
* for this script.
|
||||
*
|
||||
* The GC purges trace-JITted code on every GC activation, not just when
|
||||
* regenerating shapes, so we don't have to purge fragments if the GC is
|
||||
* currently running.
|
||||
*
|
||||
* JS_THREADSAFE note: js_PurgePropertyCacheForScript purges only the
|
||||
* current thread's property cache, so a script not owned by a function
|
||||
@ -1623,7 +1632,7 @@ js_DestroyScript(JSContext *cx, JSScript *script)
|
||||
JS_ASSERT_IF(cx->runtime->gcRunning, !script->owner);
|
||||
#endif
|
||||
|
||||
if (!cx->runtime->gcRunning) {
|
||||
if (!cx->runtime->gcRegenShapes) {
|
||||
JSStackFrame *fp = js_GetTopStackFrame(cx);
|
||||
|
||||
if (!(fp && (fp->flags & JSFRAME_EVAL))) {
|
||||
@ -1632,7 +1641,8 @@ js_DestroyScript(JSContext *cx, JSScript *script)
|
||||
#endif
|
||||
js_PurgePropertyCacheForScript(cx, script);
|
||||
#ifdef JS_TRACER
|
||||
js_PurgeScriptFragments(cx, script);
|
||||
if (!cx->runtime->gcRunning)
|
||||
js_PurgeScriptFragments(cx, script);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user