bool GCMarker::markDelayedChildren(SliceBudget& budget) { GCRuntime& gc = runtime()->gc; gcstats::AutoPhase ap(gc.stats, gc.state() == MARK, gcstats::PHASE_MARK_DELAYED); MOZ_ASSERT(unmarkedArenaStackTop); do { /* * If marking gets delayed at the same arena again, we must repeat * marking of its things. For that we pop arena from the stack and * clear its hasDelayedMarking flag before we begin the marking. */ ArenaHeader* aheader = unmarkedArenaStackTop; MOZ_ASSERT(aheader->hasDelayedMarking); MOZ_ASSERT(markLaterArenas); unmarkedArenaStackTop = aheader->getNextDelayedMarking(); aheader->unsetDelayedMarking(); markLaterArenas--; markDelayedChildren(aheader); budget.step(150); if (budget.isOverBudget()) return false; } while (unmarkedArenaStackTop); MOZ_ASSERT(!markLaterArenas); return true; }
bool GCMarker::drainMarkStack(SliceBudget &budget) { #ifdef DEBUG JSRuntime *rt = runtime; struct AutoCheckCompartment { JSRuntime *runtime; AutoCheckCompartment(JSRuntime *rt) : runtime(rt) { JS_ASSERT(!rt->gcStrictCompartmentChecking); runtime->gcStrictCompartmentChecking = true; } ~AutoCheckCompartment() { runtime->gcStrictCompartmentChecking = false; } } acc(rt); #endif if (budget.isOverBudget()) return false; for (;;) { while (!stack.isEmpty()) { processMarkStackTop(budget); if (budget.isOverBudget()) { saveValueRanges(); return false; } } if (!hasDelayedChildren()) break; /* * Mark children of things that caused too deep recursion during the * above tracing. Don't do this until we're done with everything * else. */ if (!markDelayedChildren(budget)) { saveValueRanges(); return false; } } return true; }
void GCMarker::processMarkStackOther(SliceBudget &budget, uintptr_t tag, uintptr_t addr) { if (tag == TypeTag) { ScanTypeObject(this, reinterpret_cast<types::TypeObject *>(addr)); } else if (tag == SavedValueArrayTag) { JS_ASSERT(!(addr & Cell::CellMask)); JSObject *obj = reinterpret_cast<JSObject *>(addr); HeapValue *vp, *end; if (restoreValueArray(obj, (void **)&vp, (void **)&end)) pushValueArray(obj, vp, end); else pushObject(obj); } else if (tag == IonCodeTag) { MarkChildren(this, reinterpret_cast<ion::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; } } } #if JS_HAS_XML_SUPPORT else { JS_ASSERT(tag == XmlTag); MarkChildren(this, reinterpret_cast<JSXML *>(addr)); } #endif }
inline void GCMarker::processMarkStackTop(SliceBudget &budget) { /* * The function uses explicit goto and implements the scanning of the * object directly. It allows to eliminate the tail recursion and * significantly improve the marking performance, see bug 641025. */ HeapSlot *vp, *end; JSObject *obj; uintptr_t addr = stack.pop(); uintptr_t tag = addr & StackTagMask; addr &= ~StackTagMask; if (tag == ValueArrayTag) { JS_STATIC_ASSERT(ValueArrayTag == 0); JS_ASSERT(!(addr & Cell::CellMask)); obj = reinterpret_cast<JSObject *>(addr); uintptr_t addr2 = stack.pop(); uintptr_t addr3 = stack.pop(); JS_ASSERT(addr2 <= addr3); JS_ASSERT((addr3 - addr2) % sizeof(Value) == 0); vp = reinterpret_cast<HeapSlot *>(addr2); end = reinterpret_cast<HeapSlot *>(addr3); goto scan_value_array; } if (tag == ObjectTag) { obj = reinterpret_cast<JSObject *>(addr); JS_COMPARTMENT_ASSERT(runtime, obj); goto scan_obj; } processMarkStackOther(budget, tag, addr); return; scan_value_array: JS_ASSERT(vp <= end); while (vp != end) { const Value &v = *vp++; if (v.isString()) { JSString *str = v.toString(); JS_COMPARTMENT_ASSERT_STR(runtime, str); JS_ASSERT(str->compartment() == runtime->atomsCompartment || str->compartment() == obj->compartment()); if (str->markIfUnmarked()) ScanString(this, str); } else if (v.isObject()) { JSObject *obj2 = &v.toObject(); JS_COMPARTMENT_ASSERT(runtime, obj2); JS_ASSERT(obj->compartment() == obj2->compartment()); if (obj2->markIfUnmarked(getMarkColor())) { pushValueArray(obj, vp, end); obj = obj2; goto scan_obj; } } } return; scan_obj: { JS_COMPARTMENT_ASSERT(runtime, obj); budget.step(); if (budget.isOverBudget()) { pushObject(obj); return; } types::TypeObject *type = obj->typeFromGC(); PushMarkStack(this, type); Shape *shape = obj->lastProperty(); PushMarkStack(this, shape); /* Call the trace hook if necessary. */ Class *clasp = shape->getObjectClass(); if (clasp->trace) { if (clasp == &ArrayClass) { JS_ASSERT(!shape->isNative()); vp = obj->getDenseArrayElements(); end = vp + obj->getDenseArrayInitializedLength(); goto scan_value_array; } else { JS_ASSERT_IF(runtime->gcMode == JSGC_MODE_INCREMENTAL && runtime->gcIncrementalEnabled, clasp->flags & JSCLASS_IMPLEMENTS_BARRIERS); } clasp->trace(this, obj); } if (!shape->isNative()) return; unsigned nslots = obj->slotSpan(); vp = obj->fixedSlots(); if (obj->slots) { unsigned nfixed = obj->numFixedSlots(); if (nslots > nfixed) { pushValueArray(obj, vp, vp + nfixed); vp = obj->slots; end = vp + (nslots - nfixed); goto scan_value_array; } } JS_ASSERT(nslots <= obj->numFixedSlots()); end = vp + nslots; goto scan_value_array; } }