michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * vim: set ts=8 sts=4 et sw=4 tw=99: michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "gc/Zone.h" michael@0: michael@0: #include "jsgc.h" michael@0: michael@0: #ifdef JS_ION michael@0: #include "jit/BaselineJIT.h" michael@0: #include "jit/Ion.h" michael@0: #include "jit/JitCompartment.h" michael@0: #endif michael@0: #include "vm/Debugger.h" michael@0: #include "vm/Runtime.h" michael@0: michael@0: #include "jsgcinlines.h" michael@0: michael@0: using namespace js; michael@0: using namespace js::gc; michael@0: michael@0: JS::Zone::Zone(JSRuntime *rt) michael@0: : JS::shadow::Zone(rt, &rt->gcMarker), michael@0: allocator(this), michael@0: ionUsingBarriers_(false), michael@0: active(false), michael@0: gcScheduled(false), michael@0: gcState(NoGC), michael@0: gcPreserveCode(false), michael@0: gcBytes(0), michael@0: gcTriggerBytes(0), michael@0: gcHeapGrowthFactor(3.0), michael@0: isSystem(false), michael@0: usedByExclusiveThread(false), michael@0: scheduledForDestruction(false), michael@0: maybeAlive(true), michael@0: gcMallocBytes(0), michael@0: gcMallocGCTriggered(false), michael@0: gcGrayRoots(), michael@0: data(nullptr), michael@0: types(this) michael@0: #ifdef JS_ION michael@0: , jitZone_(nullptr) michael@0: #endif michael@0: { michael@0: /* Ensure that there are no vtables to mess us up here. */ michael@0: JS_ASSERT(reinterpret_cast(this) == michael@0: static_cast(this)); michael@0: michael@0: setGCMaxMallocBytes(rt->gcMaxMallocBytes * 0.9); michael@0: } michael@0: michael@0: Zone::~Zone() michael@0: { michael@0: if (this == runtimeFromMainThread()->systemZone) michael@0: runtimeFromMainThread()->systemZone = nullptr; michael@0: michael@0: #ifdef JS_ION michael@0: js_delete(jitZone_); michael@0: #endif michael@0: } michael@0: michael@0: void michael@0: Zone::setNeedsBarrier(bool needs, ShouldUpdateIon updateIon) michael@0: { michael@0: #ifdef JS_ION michael@0: if (updateIon == UpdateIon && needs != ionUsingBarriers_) { michael@0: jit::ToggleBarriers(this, needs); michael@0: ionUsingBarriers_ = needs; michael@0: } michael@0: #endif michael@0: michael@0: if (needs && runtimeFromMainThread()->isAtomsZone(this)) michael@0: JS_ASSERT(!runtimeFromMainThread()->exclusiveThreadsPresent()); michael@0: michael@0: JS_ASSERT_IF(needs, canCollect()); michael@0: needsBarrier_ = needs; michael@0: } michael@0: michael@0: void michael@0: Zone::resetGCMallocBytes() michael@0: { michael@0: gcMallocBytes = ptrdiff_t(gcMaxMallocBytes); michael@0: gcMallocGCTriggered = false; michael@0: } michael@0: michael@0: void michael@0: Zone::setGCMaxMallocBytes(size_t value) michael@0: { michael@0: /* michael@0: * For compatibility treat any value that exceeds PTRDIFF_T_MAX to michael@0: * mean that value. michael@0: */ michael@0: gcMaxMallocBytes = (ptrdiff_t(value) >= 0) ? value : size_t(-1) >> 1; michael@0: resetGCMallocBytes(); michael@0: } michael@0: michael@0: void michael@0: Zone::onTooMuchMalloc() michael@0: { michael@0: if (!gcMallocGCTriggered) michael@0: gcMallocGCTriggered = TriggerZoneGC(this, JS::gcreason::TOO_MUCH_MALLOC); michael@0: } michael@0: michael@0: void michael@0: Zone::sweep(FreeOp *fop, bool releaseTypes, bool *oom) michael@0: { michael@0: /* michael@0: * Periodically release observed types for all scripts. This is safe to michael@0: * do when there are no frames for the zone on the stack. michael@0: */ michael@0: if (active) michael@0: releaseTypes = false; michael@0: michael@0: { michael@0: gcstats::AutoPhase ap(fop->runtime()->gcStats, gcstats::PHASE_DISCARD_ANALYSIS); michael@0: types.sweep(fop, releaseTypes, oom); michael@0: } michael@0: michael@0: if (!fop->runtime()->debuggerList.isEmpty()) michael@0: sweepBreakpoints(fop); michael@0: michael@0: active = false; michael@0: } michael@0: michael@0: void michael@0: Zone::sweepBreakpoints(FreeOp *fop) michael@0: { michael@0: /* michael@0: * Sweep all compartments in a zone at the same time, since there is no way michael@0: * to iterate over the scripts belonging to a single compartment in a zone. michael@0: */ michael@0: michael@0: gcstats::AutoPhase ap1(fop->runtime()->gcStats, gcstats::PHASE_SWEEP_TABLES); michael@0: gcstats::AutoPhase ap2(fop->runtime()->gcStats, gcstats::PHASE_SWEEP_TABLES_BREAKPOINT); michael@0: michael@0: JS_ASSERT(isGCSweeping()); michael@0: for (CellIterUnderGC i(this, FINALIZE_SCRIPT); !i.done(); i.next()) { michael@0: JSScript *script = i.get(); michael@0: JS_ASSERT(script->zone()->isGCSweeping()); michael@0: if (!script->hasAnyBreakpointsOrStepMode()) michael@0: continue; michael@0: michael@0: bool scriptGone = IsScriptAboutToBeFinalized(&script); michael@0: JS_ASSERT(script == i.get()); michael@0: for (unsigned i = 0; i < script->length(); i++) { michael@0: BreakpointSite *site = script->getBreakpointSite(script->offsetToPC(i)); michael@0: if (!site) michael@0: continue; michael@0: michael@0: Breakpoint *nextbp; michael@0: for (Breakpoint *bp = site->firstBreakpoint(); bp; bp = nextbp) { michael@0: nextbp = bp->nextInSite(); michael@0: HeapPtrObject& dbgobj = bp->debugger->toJSObjectRef(); michael@0: JS_ASSERT(dbgobj->zone()->isGCSweeping()); michael@0: bool dying = scriptGone || IsObjectAboutToBeFinalized(&dbgobj); michael@0: JS_ASSERT_IF(!dying, bp->getHandler()->isMarked()); michael@0: if (dying) michael@0: bp->destroy(fop); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: Zone::discardJitCode(FreeOp *fop) michael@0: { michael@0: #ifdef JS_ION michael@0: if (!jitZone()) michael@0: return; michael@0: michael@0: if (isPreservingCode()) { michael@0: PurgeJITCaches(this); michael@0: } else { michael@0: michael@0: # ifdef DEBUG michael@0: /* Assert no baseline scripts are marked as active. */ michael@0: for (CellIterUnderGC i(this, FINALIZE_SCRIPT); !i.done(); i.next()) { michael@0: JSScript *script = i.get(); michael@0: JS_ASSERT_IF(script->hasBaselineScript(), !script->baselineScript()->active()); michael@0: } michael@0: # endif michael@0: michael@0: /* Mark baseline scripts on the stack as active. */ michael@0: jit::MarkActiveBaselineScripts(this); michael@0: michael@0: /* Only mark OSI points if code is being discarded. */ michael@0: jit::InvalidateAll(fop, this); michael@0: michael@0: for (CellIterUnderGC i(this, FINALIZE_SCRIPT); !i.done(); i.next()) { michael@0: JSScript *script = i.get(); michael@0: jit::FinishInvalidation(fop, script); michael@0: michael@0: // Preserve JIT code that have been recently used in michael@0: // parallel. Note that we mark their baseline scripts as active as michael@0: // well to preserve them. michael@0: if (script->hasParallelIonScript()) { michael@0: if (jit::ShouldPreserveParallelJITCode(runtimeFromMainThread(), script)) { michael@0: script->parallelIonScript()->purgeCaches(); michael@0: script->baselineScript()->setActive(); michael@0: } else { michael@0: jit::FinishInvalidation(fop, script); michael@0: } michael@0: } michael@0: michael@0: /* michael@0: * Discard baseline script if it's not marked as active. Note that michael@0: * this also resets the active flag. michael@0: */ michael@0: jit::FinishDiscardBaselineScript(fop, script); michael@0: michael@0: /* michael@0: * Use counts for scripts are reset on GC. After discarding code we michael@0: * need to let it warm back up to get information such as which michael@0: * opcodes are setting array holes or accessing getter properties. michael@0: */ michael@0: script->resetUseCount(); michael@0: } michael@0: michael@0: jitZone()->optimizedStubSpace()->free(); michael@0: } michael@0: #endif michael@0: } michael@0: michael@0: uint64_t michael@0: Zone::gcNumber() michael@0: { michael@0: // Zones in use by exclusive threads are not collected, and threads using michael@0: // them cannot access the main runtime's gcNumber without racing. michael@0: return usedByExclusiveThread ? 0 : runtimeFromMainThread()->gcNumber; michael@0: } michael@0: michael@0: #ifdef JS_ION michael@0: js::jit::JitZone * michael@0: Zone::createJitZone(JSContext *cx) michael@0: { michael@0: MOZ_ASSERT(!jitZone_); michael@0: michael@0: if (!cx->runtime()->getJitRuntime(cx)) michael@0: return nullptr; michael@0: michael@0: jitZone_ = cx->new_(); michael@0: return jitZone_; michael@0: } michael@0: #endif michael@0: michael@0: JS::Zone * michael@0: js::ZoneOfObject(const JSObject &obj) michael@0: { michael@0: return obj.zone(); michael@0: } michael@0: michael@0: JS::Zone * michael@0: js::ZoneOfObjectFromAnyThread(const JSObject &obj) michael@0: { michael@0: return obj.zoneFromAnyThread(); michael@0: } michael@0: michael@0: bool michael@0: Zone::hasMarkedCompartments() michael@0: { michael@0: for (CompartmentsInZoneIter comp(this); !comp.done(); comp.next()) { michael@0: if (comp->marked) michael@0: return true; michael@0: } michael@0: return false; michael@0: }