Bug 1245767 - Allow combining different gczeal modes. r=terrence

This commit is contained in:
Jan de Mooij 2016-02-05 12:21:43 +01:00
parent a2988de8a0
commit fc1572a57e
16 changed files with 132 additions and 88 deletions

View File

@ -624,10 +624,10 @@ ScheduleGC(JSContext* cx, unsigned argc, Value* vp)
PrepareZoneForGC(args[0].toString()->zone());
}
uint8_t zeal;
uint32_t zealBits;
uint32_t freq;
uint32_t next;
JS_GetGCZeal(cx, &zeal, &freq, &next);
JS_GetGCZealBits(cx, &zealBits, &freq, &next);
args.rval().setInt32(next);
return true;
}

View File

@ -580,7 +580,8 @@ class GCRuntime
void finishRoots();
void finish();
inline int zeal();
inline bool hasZealMode(ZealMode mode);
inline void clearZealMode(ZealMode mode);
inline bool upcomingZealousGC();
inline bool needZealousGC();
@ -636,8 +637,8 @@ class GCRuntime
void onOutOfMallocMemory(const AutoLockGC& lock);
#ifdef JS_GC_ZEAL
const void* addressOfZealMode() { return &zealMode; }
void getZeal(uint8_t* zeal, uint32_t* frequency, uint32_t* nextScheduled);
const void* addressOfZealModeBits() { return &zealModeBits; }
void getZealBits(uint32_t* zealBits, uint32_t* frequency, uint32_t* nextScheduled);
void setZeal(uint8_t zeal, uint32_t frequency);
bool parseAndSetZeal(const char* str);
void setNextScheduled(uint32_t count);
@ -1259,7 +1260,7 @@ class GCRuntime
* zeal_ value 14 performs periodic shrinking collections.
*/
#ifdef JS_GC_ZEAL
int zealMode;
uint32_t zealModeBits;
int zealFrequency;
int nextScheduled;
bool deterministicOnly;
@ -1347,9 +1348,20 @@ class MOZ_RAII AutoEnterIteration {
};
#ifdef JS_GC_ZEAL
inline int
GCRuntime::zeal() {
return zealMode;
inline bool
GCRuntime::hasZealMode(ZealMode mode)
{
static_assert(size_t(ZealMode::Limit) < sizeof(zealModeBits) * 8,
"Zeal modes must fit in zealModeBits");
return zealModeBits & (1 << uint32_t(mode));
}
inline void
GCRuntime::clearZealMode(ZealMode mode)
{
zealModeBits &= ~(1 << uint32_t(mode));
MOZ_ASSERT(!hasZealMode(mode));
}
inline bool
@ -1360,11 +1372,12 @@ GCRuntime::upcomingZealousGC() {
inline bool
GCRuntime::needZealousGC() {
if (nextScheduled > 0 && --nextScheduled == 0) {
if (zealMode == ZealAllocValue ||
zealMode == ZealGenerationalGCValue ||
(zealMode >= ZealIncrementalRootsThenFinish &&
zealMode <= ZealIncrementalMultipleSlices) ||
zealMode == ZealCompactValue)
if (hasZealMode(ZealMode::Alloc) ||
hasZealMode(ZealMode::GenerationalGC) ||
hasZealMode(ZealMode::IncrementalRootsThenFinish) ||
hasZealMode(ZealMode::IncrementalMarkAllThenFinish) ||
hasZealMode(ZealMode::IncrementalMultipleSlices) ||
hasZealMode(ZealMode::Compact))
{
nextScheduled = zealFrequency;
}
@ -1373,7 +1386,8 @@ GCRuntime::needZealousGC() {
return false;
}
#else
inline int GCRuntime::zeal() { return 0; }
inline bool GCRuntime::hasZealMode(ZealMode mode) { return false; }
inline void GCRuntime::clearZealMode(ZealMode mode) { }
inline bool GCRuntime::upcomingZealousGC() { return false; }
inline bool GCRuntime::needZealousGC() { return false; }
#endif

View File

@ -139,7 +139,7 @@ js::Nursery::enable()
setCurrentChunk(0);
currentStart_ = position();
#ifdef JS_GC_ZEAL
if (runtime()->gcZeal() == ZealGenerationalGCValue)
if (runtime()->hasZealMode(ZealMode::GenerationalGC))
enterZealMode();
#endif
}
@ -161,7 +161,7 @@ js::Nursery::isEmpty() const
MOZ_ASSERT(runtime_);
if (!isEnabled())
return true;
MOZ_ASSERT_IF(runtime_->gcZeal() != ZealGenerationalGCValue, currentStart_ == start());
MOZ_ASSERT_IF(!runtime_->hasZealMode(ZealMode::GenerationalGC), currentStart_ == start());
return position() == currentStart_;
}
@ -516,7 +516,7 @@ js::Nursery::collect(JSRuntime* rt, JS::gcreason::Reason reason, ObjectGroupList
// Make sure hashtables have been updated after the collection.
TIME_START(checkHashTables);
#ifdef JS_GC_ZEAL
if (rt->gcZeal() == ZealCheckHashTablesOnMinorGC)
if (rt->hasZealMode(ZealMode::CheckHashTablesOnMinorGC))
CheckHashTablesAfterMovingGC(rt);
#endif
TIME_END(checkHashTables);
@ -678,7 +678,7 @@ js::Nursery::sweep()
for (int i = 0; i < numNurseryChunks_; ++i)
initChunk(i);
if (runtime()->gcZeal() == ZealGenerationalGCValue) {
if (runtime()->hasZealMode(ZealMode::GenerationalGC)) {
MOZ_ASSERT(numActiveChunks_ == numNurseryChunks_);
/* Only reset the alloc point when we are close to the end. */
@ -704,7 +704,7 @@ void
js::Nursery::growAllocableSpace()
{
#ifdef JS_GC_ZEAL
MOZ_ASSERT_IF(runtime()->gcZeal() == ZealGenerationalGCValue,
MOZ_ASSERT_IF(runtime()->hasZealMode(ZealMode::GenerationalGC),
numActiveChunks_ == numNurseryChunks_);
#endif
numActiveChunks_ = Min(numActiveChunks_ * 2, numNurseryChunks_);
@ -714,7 +714,7 @@ void
js::Nursery::shrinkAllocableSpace()
{
#ifdef JS_GC_ZEAL
if (runtime()->gcZeal() == ZealGenerationalGCValue)
if (runtime()->hasZealMode(ZealMode::GenerationalGC))
return;
#endif
numActiveChunks_ = Max(numActiveChunks_ - 1, 1);

View File

@ -381,7 +381,7 @@ gc::VerifyBarriers(JSRuntime* rt, VerifierType type)
void
gc::GCRuntime::maybeVerifyPreBarriers(bool always)
{
if (zealMode != ZealVerifierPreValue)
if (!hasZealMode(ZealMode::VerifierPre))
return;
if (rt->mainThread.suppressGC)

View File

@ -233,7 +233,7 @@ struct Zone : public JS::shadow::Zone,
bool compileBarriers() const { return compileBarriers(needsIncrementalBarrier()); }
bool compileBarriers(bool needsIncrementalBarrier) const {
return needsIncrementalBarrier ||
runtimeFromMainThread()->gcZeal() == js::gc::ZealVerifierPreValue;
runtimeFromMainThread()->hasZealMode(js::gc::ZealMode::VerifierPre);
}
enum ShouldUpdateJit { DontUpdateJit, UpdateJit };

View File

@ -78,9 +78,9 @@ CompileRuntime::addressOfLastCachedNativeIterator()
#ifdef JS_GC_ZEAL
const void*
CompileRuntime::addressOfGCZeal()
CompileRuntime::addressOfGCZealModeBits()
{
return runtime()->gc.addressOfZealMode();
return runtime()->gc.addressOfZealModeBits();
}
#endif

View File

@ -53,7 +53,7 @@ class CompileRuntime
const void* addressOfLastCachedNativeIterator();
#ifdef JS_GC_ZEAL
const void* addressOfGCZeal();
const void* addressOfGCZealModeBits();
#endif
const void* addressOfInterruptUint32();

View File

@ -718,12 +718,12 @@ MacroAssembler::checkAllocatorState(Label* fail)
if (js::gc::TraceEnabled() || MemProfiler::enabled())
jump(fail);
# ifdef JS_GC_ZEAL
#ifdef JS_GC_ZEAL
// Don't execute the inline path if gc zeal or tracing are active.
branch32(Assembler::NotEqual,
AbsoluteAddress(GetJitContext()->runtime->addressOfGCZeal()), Imm32(0),
AbsoluteAddress(GetJitContext()->runtime->addressOfGCZealModeBits()), Imm32(0),
fail);
# endif
#endif
// Don't execute the inline path if the compartment has an object metadata callback,
// as the metadata to use for the object may vary between executions of the op.

View File

@ -625,7 +625,7 @@ PostWriteElementBarrier(JSRuntime* rt, JSObject* obj, size_t index)
if (obj->is<NativeObject>() &&
(obj->as<NativeObject>().getDenseInitializedLength() > MAX_WHOLE_CELL_BUFFER_SIZE
#ifdef JS_GC_ZEAL
|| rt->gcZeal() == gc::ZealElementsBarrier
|| rt->hasZealMode(gc::ZealMode::ElementsBarrier)
#endif
))
{

View File

@ -424,19 +424,29 @@ class TestJSPrincipals : public JSPrincipals
class AutoLeaveZeal
{
JSContext* cx_;
uint8_t zeal_;
uint32_t zealBits_;
uint32_t frequency_;
public:
explicit AutoLeaveZeal(JSContext* cx) : cx_(cx) {
uint32_t dummy;
JS_GetGCZeal(cx_, &zeal_, &frequency_, &dummy);
JS_GetGCZealBits(cx_, &zealBits_, &frequency_, &dummy);
JS_SetGCZeal(cx_, 0, 0);
JS::PrepareForFullGC(JS_GetRuntime(cx_));
JS::GCForReason(JS_GetRuntime(cx_), GC_SHRINK, JS::gcreason::DEBUG_GC);
}
~AutoLeaveZeal() {
JS_SetGCZeal(cx_, zeal_, frequency_);
for (size_t i = 0; i < sizeof(zealBits_) * 8; i++) {
if (zealBits_ & (1 << i))
JS_SetGCZeal(cx_, i, frequency_);
}
#ifdef DEBUG
uint32_t zealBitsAfter, frequencyAfter, dummy;
JS_GetGCZealBits(cx_, &zealBitsAfter, &frequencyAfter, &dummy);
MOZ_ASSERT(zealBitsAfter == zealBits_);
MOZ_ASSERT(frequencyAfter == frequency_);
#endif
}
};
#endif /* JS_GC_ZEAL */

View File

@ -5753,9 +5753,9 @@ JS_AbortIfWrongThread(JSRuntime* rt)
#ifdef JS_GC_ZEAL
JS_PUBLIC_API(void)
JS_GetGCZeal(JSContext* cx, uint8_t* zeal, uint32_t* frequency, uint32_t* nextScheduled)
JS_GetGCZealBits(JSContext* cx, uint32_t* zealBits, uint32_t* frequency, uint32_t* nextScheduled)
{
cx->runtime()->gc.getZeal(zeal, frequency, nextScheduled);
cx->runtime()->gc.getZealBits(zealBits, frequency, nextScheduled);
}
JS_PUBLIC_API(void)

View File

@ -5239,7 +5239,7 @@ JS_NewObjectForConstructor(JSContext* cx, const JSClass* clasp, const JS::CallAr
#define JS_DEFAULT_ZEAL_FREQ 100
extern JS_PUBLIC_API(void)
JS_GetGCZeal(JSContext* cx, uint8_t* zeal, uint32_t* frequency, uint32_t* nextScheduled);
JS_GetGCZealBits(JSContext* cx, uint32_t* zealBits, uint32_t* frequency, uint32_t* nextScheduled);
extern JS_PUBLIC_API(void)
JS_SetGCZeal(JSContext* cx, uint8_t zeal, uint32_t frequency);

View File

@ -1164,7 +1164,7 @@ GCRuntime::GCRuntime(JSRuntime* rt) :
objectsMarkedInDeadZones(0),
poked(false),
#ifdef JS_GC_ZEAL
zealMode(0),
zealModeBits(0),
zealFrequency(0),
nextScheduled(0),
deterministicOnly(false),
@ -1188,16 +1188,20 @@ GCRuntime::GCRuntime(JSRuntime* rt) :
#ifdef JS_GC_ZEAL
void
GCRuntime::getZeal(uint8_t* zeal, uint32_t* frequency, uint32_t* scheduled)
GCRuntime::getZealBits(uint32_t* zealBits, uint32_t* frequency, uint32_t* scheduled)
{
*zeal = zealMode;
*zealBits = zealModeBits;
*frequency = zealFrequency;
*scheduled = nextScheduled;
}
const char* gc::ZealModeHelpText =
" Specifies how zealous the garbage collector should be. Values for level:\n"
" 0: Normal amount of collection\n"
" Specifies how zealous the garbage collector should be. Some of these modes can\n"
" be set simultaneously, e.g. in the shell, gczeal(2); gczeal(4); will activate\n"
" both modes 2 and 4.\n"
" \n"
" Values:\n"
" 0: Normal amount of collection (resets all modes)\n"
" 1: Collect when roots are added or removed\n"
" 2: Collect when every N allocations (default: 100)\n"
" 3: Collect when the window paints (browser only)\n"
@ -1216,19 +1220,35 @@ const char* gc::ZealModeHelpText =
void
GCRuntime::setZeal(uint8_t zeal, uint32_t frequency)
{
MOZ_ASSERT(zeal <= unsigned(ZealMode::Limit));
if (verifyPreData)
VerifyBarriers(rt, PreBarrierVerifier);
if (zealMode == ZealGenerationalGCValue) {
if (zeal == 0 && hasZealMode(ZealMode::GenerationalGC)) {
evictNursery(JS::gcreason::DEBUG_GC);
nursery.leaveZealMode();
}
if (zeal == ZealGenerationalGCValue)
ZealMode zealMode = ZealMode(zeal);
if (zealMode == ZealMode::GenerationalGC)
nursery.enterZealMode();
bool schedule = zeal >= js::gc::ZealAllocValue;
zealMode = zeal;
// Zeal modes 8-10 are mutually exclusive. If we're setting one of those,
// we first reset all of them.
if (zealMode >= ZealMode::IncrementalRootsThenFinish &&
zealMode <= ZealMode::IncrementalMultipleSlices)
{
clearZealMode(ZealMode::IncrementalRootsThenFinish);
clearZealMode(ZealMode::IncrementalMarkAllThenFinish);
clearZealMode(ZealMode::IncrementalMultipleSlices);
}
bool schedule = zealMode >= ZealMode::Alloc;
if (zeal != 0)
zealModeBits |= 1 << unsigned(zeal);
else
zealModeBits = 0;
zealFrequency = frequency;
nextScheduled = schedule ? frequency : 0;
}
@ -1255,7 +1275,7 @@ GCRuntime::parseAndSetZeal(const char* str)
frequency = atoi(p + 1);
}
if (zeal < 0 || zeal > ZealLimit || frequency <= 0) {
if (zeal < 0 || zeal > int(ZealMode::Limit) || frequency <= 0) {
fprintf(stderr, "Format: JS_GC_ZEAL=level[,N]\n");
fputs(ZealModeHelpText, stderr);
return false;
@ -3251,7 +3271,7 @@ GCRuntime::triggerZoneGC(Zone* zone, JS::gcreason::Reason reason)
return false;
#ifdef JS_GC_ZEAL
if (zealMode == ZealAllocValue) {
if (hasZealMode(ZealMode::Alloc)) {
triggerGC(reason);
return true;
}
@ -3280,7 +3300,7 @@ GCRuntime::maybeGC(Zone* zone)
MOZ_ASSERT(CurrentThreadCanAccessRuntime(rt));
#ifdef JS_GC_ZEAL
if (zealMode == ZealAllocValue || zealMode == ZealPokeValue) {
if (hasZealMode(ZealMode::Alloc) || hasZealMode(ZealMode::Poke)) {
JS::PrepareForFullGC(rt);
gc(GC_NORMAL, JS::gcreason::DEBUG_GC);
return true;
@ -3680,7 +3700,7 @@ GCRuntime::shouldReleaseObservedTypes()
bool releaseTypes = false;
#ifdef JS_GC_ZEAL
if (zealMode != 0)
if (zealModeBits != 0)
releaseTypes = true;
#endif
@ -4504,7 +4524,7 @@ GCRuntime::computeNonIncrementalMarkingForValidation()
{
#ifdef JS_GC_ZEAL
MOZ_ASSERT(!markingValidator);
if (isIncremental && zeal() == ZealIncrementalMarkingValidator)
if (isIncremental && hasZealMode(ZealMode::IncrementalMarkingValidator))
markingValidator = js_new<MarkingValidator>(this);
if (markingValidator)
markingValidator->nonIncrementalMark();
@ -6033,7 +6053,7 @@ GCRuntime::incrementalCollectSlice(SliceBudget& budget, JS::gcreason::Reason rea
gc::State initialState = incrementalState;
int zeal = 0;
bool useZeal = false;
#ifdef JS_GC_ZEAL
if (reason == JS::gcreason::DEBUG_GC && !budget.isUnlimited()) {
/*
@ -6041,14 +6061,16 @@ GCRuntime::incrementalCollectSlice(SliceBudget& budget, JS::gcreason::Reason rea
* collection was triggered by runDebugGC() and incremental GC has not
* been cancelled by resetIncrementalGC().
*/
zeal = zealMode;
useZeal = true;
}
#endif
MOZ_ASSERT_IF(isIncrementalGCInProgress(), isIncremental);
isIncremental = !budget.isUnlimited();
if (zeal == ZealIncrementalRootsThenFinish || zeal == ZealIncrementalMarkAllThenFinish) {
if (useZeal && (hasZealMode(ZealMode::IncrementalRootsThenFinish) ||
hasZealMode(ZealMode::IncrementalMarkAllThenFinish)))
{
/*
* Yields between slices occurs at predetermined points in these modes;
* the budget is not used.
@ -6078,7 +6100,7 @@ GCRuntime::incrementalCollectSlice(SliceBudget& budget, JS::gcreason::Reason rea
incrementalState = MARK;
if (isIncremental && zeal == ZealIncrementalRootsThenFinish)
if (isIncremental && useZeal && hasZealMode(ZealMode::IncrementalRootsThenFinish))
break;
MOZ_FALLTHROUGH;
@ -6097,9 +6119,9 @@ GCRuntime::incrementalCollectSlice(SliceBudget& budget, JS::gcreason::Reason rea
MOZ_ASSERT(marker.isDrained());
if (!lastMarkSlice && isIncremental &&
((initialState == MARK && zeal != ZealIncrementalRootsThenFinish) ||
zeal == ZealIncrementalMarkAllThenFinish))
if (!lastMarkSlice && isIncremental && useZeal &&
((initialState == MARK && !hasZealMode(ZealMode::IncrementalRootsThenFinish)) ||
hasZealMode(ZealMode::IncrementalMarkAllThenFinish)))
{
/*
* Yield with the aim of starting the sweep in the next
@ -6124,7 +6146,7 @@ GCRuntime::incrementalCollectSlice(SliceBudget& budget, JS::gcreason::Reason rea
* Always yield here when running in incremental multi-slice zeal
* mode, so RunDebugGC can reset the slice buget.
*/
if (isIncremental && zeal == ZealIncrementalMultipleSlices)
if (isIncremental && useZeal && hasZealMode(ZealMode::IncrementalMultipleSlices))
break;
MOZ_FALLTHROUGH;
@ -6550,12 +6572,10 @@ GCRuntime::notifyDidPaint()
MOZ_ASSERT(CurrentThreadCanAccessRuntime(rt));
#ifdef JS_GC_ZEAL
if (zealMode == ZealFrameVerifierPreValue) {
if (hasZealMode(ZealMode::FrameVerifierPre))
verifyPreBarriers();
return;
}
if (zealMode == ZealFrameGCValue) {
if (hasZealMode(ZealMode::FrameGC)) {
JS::PrepareForFullGC(rt);
gc(GC_NORMAL, JS::gcreason::REFRESH_FRAME);
return;
@ -6929,23 +6949,21 @@ void
GCRuntime::runDebugGC()
{
#ifdef JS_GC_ZEAL
int type = zealMode;
if (rt->mainThread.suppressGC)
return;
if (type == js::gc::ZealGenerationalGCValue)
if (hasZealMode(ZealMode::GenerationalGC))
return minorGC(JS::gcreason::DEBUG_GC);
PrepareForDebugGC(rt);
auto budget = SliceBudget::unlimited();
if (type == ZealIncrementalRootsThenFinish ||
type == ZealIncrementalMarkAllThenFinish ||
type == ZealIncrementalMultipleSlices)
if (hasZealMode(ZealMode::IncrementalRootsThenFinish) ||
hasZealMode(ZealMode::IncrementalMarkAllThenFinish) ||
hasZealMode(ZealMode::IncrementalMultipleSlices))
{
js::gc::State initialState = incrementalState;
if (type == ZealIncrementalMultipleSlices) {
if (hasZealMode(ZealMode::IncrementalMultipleSlices)) {
/*
* Start with a small slice limit and double it every slice. This
* ensure that we get multiple slices, and collection runs to
@ -6969,14 +6987,14 @@ GCRuntime::runDebugGC()
* For multi-slice zeal, reset the slice size when we get to the sweep
* or compact phases.
*/
if (type == ZealIncrementalMultipleSlices) {
if (hasZealMode(ZealMode::IncrementalMultipleSlices)) {
if ((initialState == MARK && incrementalState == SWEEP) ||
(initialState == SWEEP && incrementalState == COMPACT))
{
incrementalLimit = zealFrequency / 2;
}
}
} else if (type == ZealCompactValue) {
} else if (hasZealMode(ZealMode::Compact)) {
gc(GC_SHRINK, JS::gcreason::DEBUG_GC);
} else {
gc(GC_NORMAL, JS::gcreason::DEBUG_GC);

View File

@ -1251,21 +1251,23 @@ CheckValueAfterMovingGC(const JS::Value& value)
#endif // JSGC_HASH_TABLE_CHECKS
const int ZealPokeValue = 1;
const int ZealAllocValue = 2;
const int ZealFrameGCValue = 3;
const int ZealVerifierPreValue = 4;
const int ZealFrameVerifierPreValue = 5;
const int ZealStackRootingValue = 6;
const int ZealGenerationalGCValue = 7;
const int ZealIncrementalRootsThenFinish = 8;
const int ZealIncrementalMarkAllThenFinish = 9;
const int ZealIncrementalMultipleSlices = 10;
const int ZealIncrementalMarkingValidator = 11;
const int ZealElementsBarrier = 12;
const int ZealCheckHashTablesOnMinorGC = 13;
const int ZealCompactValue = 14;
const int ZealLimit = 14;
enum class ZealMode {
Poke = 1,
Alloc = 2,
FrameGC = 3,
VerifierPre = 4,
FrameVerifierPre = 5,
StackRooting = 6,
GenerationalGC = 7,
IncrementalRootsThenFinish = 8,
IncrementalMarkAllThenFinish = 9,
IncrementalMultipleSlices = 10,
IncrementalMarkingValidator = 11,
ElementsBarrier = 12,
CheckHashTablesOnMinorGC = 13,
Compact = 14,
Limit = 14
};
enum VerifierType {
PreBarrierVerifier

View File

@ -33,7 +33,7 @@ GCRuntime::poke()
#ifdef JS_GC_ZEAL
/* Schedule a GC to happen "soon" after a GC poke. */
if (zealMode == ZealPokeValue)
if (hasZealMode(ZealMode::Poke))
nextScheduled = 1;
#endif
}

View File

@ -1092,7 +1092,7 @@ struct JSRuntime : public JS::shadow::Runtime,
/* Garbage collector state has been successfully initialized. */
bool gcInitialized;
int gcZeal() { return gc.zeal(); }
bool hasZealMode(js::gc::ZealMode mode) { return gc.hasZealMode(mode); }
void lockGC() {
assertCanLock(js::GCLock);