1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/js/src/gc/Zone.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,269 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- 1.5 + * vim: set ts=8 sts=4 et sw=4 tw=99: 1.6 + * This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "gc/Zone.h" 1.11 + 1.12 +#include "jsgc.h" 1.13 + 1.14 +#ifdef JS_ION 1.15 +#include "jit/BaselineJIT.h" 1.16 +#include "jit/Ion.h" 1.17 +#include "jit/JitCompartment.h" 1.18 +#endif 1.19 +#include "vm/Debugger.h" 1.20 +#include "vm/Runtime.h" 1.21 + 1.22 +#include "jsgcinlines.h" 1.23 + 1.24 +using namespace js; 1.25 +using namespace js::gc; 1.26 + 1.27 +JS::Zone::Zone(JSRuntime *rt) 1.28 + : JS::shadow::Zone(rt, &rt->gcMarker), 1.29 + allocator(this), 1.30 + ionUsingBarriers_(false), 1.31 + active(false), 1.32 + gcScheduled(false), 1.33 + gcState(NoGC), 1.34 + gcPreserveCode(false), 1.35 + gcBytes(0), 1.36 + gcTriggerBytes(0), 1.37 + gcHeapGrowthFactor(3.0), 1.38 + isSystem(false), 1.39 + usedByExclusiveThread(false), 1.40 + scheduledForDestruction(false), 1.41 + maybeAlive(true), 1.42 + gcMallocBytes(0), 1.43 + gcMallocGCTriggered(false), 1.44 + gcGrayRoots(), 1.45 + data(nullptr), 1.46 + types(this) 1.47 +#ifdef JS_ION 1.48 + , jitZone_(nullptr) 1.49 +#endif 1.50 +{ 1.51 + /* Ensure that there are no vtables to mess us up here. */ 1.52 + JS_ASSERT(reinterpret_cast<JS::shadow::Zone *>(this) == 1.53 + static_cast<JS::shadow::Zone *>(this)); 1.54 + 1.55 + setGCMaxMallocBytes(rt->gcMaxMallocBytes * 0.9); 1.56 +} 1.57 + 1.58 +Zone::~Zone() 1.59 +{ 1.60 + if (this == runtimeFromMainThread()->systemZone) 1.61 + runtimeFromMainThread()->systemZone = nullptr; 1.62 + 1.63 +#ifdef JS_ION 1.64 + js_delete(jitZone_); 1.65 +#endif 1.66 +} 1.67 + 1.68 +void 1.69 +Zone::setNeedsBarrier(bool needs, ShouldUpdateIon updateIon) 1.70 +{ 1.71 +#ifdef JS_ION 1.72 + if (updateIon == UpdateIon && needs != ionUsingBarriers_) { 1.73 + jit::ToggleBarriers(this, needs); 1.74 + ionUsingBarriers_ = needs; 1.75 + } 1.76 +#endif 1.77 + 1.78 + if (needs && runtimeFromMainThread()->isAtomsZone(this)) 1.79 + JS_ASSERT(!runtimeFromMainThread()->exclusiveThreadsPresent()); 1.80 + 1.81 + JS_ASSERT_IF(needs, canCollect()); 1.82 + needsBarrier_ = needs; 1.83 +} 1.84 + 1.85 +void 1.86 +Zone::resetGCMallocBytes() 1.87 +{ 1.88 + gcMallocBytes = ptrdiff_t(gcMaxMallocBytes); 1.89 + gcMallocGCTriggered = false; 1.90 +} 1.91 + 1.92 +void 1.93 +Zone::setGCMaxMallocBytes(size_t value) 1.94 +{ 1.95 + /* 1.96 + * For compatibility treat any value that exceeds PTRDIFF_T_MAX to 1.97 + * mean that value. 1.98 + */ 1.99 + gcMaxMallocBytes = (ptrdiff_t(value) >= 0) ? value : size_t(-1) >> 1; 1.100 + resetGCMallocBytes(); 1.101 +} 1.102 + 1.103 +void 1.104 +Zone::onTooMuchMalloc() 1.105 +{ 1.106 + if (!gcMallocGCTriggered) 1.107 + gcMallocGCTriggered = TriggerZoneGC(this, JS::gcreason::TOO_MUCH_MALLOC); 1.108 +} 1.109 + 1.110 +void 1.111 +Zone::sweep(FreeOp *fop, bool releaseTypes, bool *oom) 1.112 +{ 1.113 + /* 1.114 + * Periodically release observed types for all scripts. This is safe to 1.115 + * do when there are no frames for the zone on the stack. 1.116 + */ 1.117 + if (active) 1.118 + releaseTypes = false; 1.119 + 1.120 + { 1.121 + gcstats::AutoPhase ap(fop->runtime()->gcStats, gcstats::PHASE_DISCARD_ANALYSIS); 1.122 + types.sweep(fop, releaseTypes, oom); 1.123 + } 1.124 + 1.125 + if (!fop->runtime()->debuggerList.isEmpty()) 1.126 + sweepBreakpoints(fop); 1.127 + 1.128 + active = false; 1.129 +} 1.130 + 1.131 +void 1.132 +Zone::sweepBreakpoints(FreeOp *fop) 1.133 +{ 1.134 + /* 1.135 + * Sweep all compartments in a zone at the same time, since there is no way 1.136 + * to iterate over the scripts belonging to a single compartment in a zone. 1.137 + */ 1.138 + 1.139 + gcstats::AutoPhase ap1(fop->runtime()->gcStats, gcstats::PHASE_SWEEP_TABLES); 1.140 + gcstats::AutoPhase ap2(fop->runtime()->gcStats, gcstats::PHASE_SWEEP_TABLES_BREAKPOINT); 1.141 + 1.142 + JS_ASSERT(isGCSweeping()); 1.143 + for (CellIterUnderGC i(this, FINALIZE_SCRIPT); !i.done(); i.next()) { 1.144 + JSScript *script = i.get<JSScript>(); 1.145 + JS_ASSERT(script->zone()->isGCSweeping()); 1.146 + if (!script->hasAnyBreakpointsOrStepMode()) 1.147 + continue; 1.148 + 1.149 + bool scriptGone = IsScriptAboutToBeFinalized(&script); 1.150 + JS_ASSERT(script == i.get<JSScript>()); 1.151 + for (unsigned i = 0; i < script->length(); i++) { 1.152 + BreakpointSite *site = script->getBreakpointSite(script->offsetToPC(i)); 1.153 + if (!site) 1.154 + continue; 1.155 + 1.156 + Breakpoint *nextbp; 1.157 + for (Breakpoint *bp = site->firstBreakpoint(); bp; bp = nextbp) { 1.158 + nextbp = bp->nextInSite(); 1.159 + HeapPtrObject& dbgobj = bp->debugger->toJSObjectRef(); 1.160 + JS_ASSERT(dbgobj->zone()->isGCSweeping()); 1.161 + bool dying = scriptGone || IsObjectAboutToBeFinalized(&dbgobj); 1.162 + JS_ASSERT_IF(!dying, bp->getHandler()->isMarked()); 1.163 + if (dying) 1.164 + bp->destroy(fop); 1.165 + } 1.166 + } 1.167 + } 1.168 +} 1.169 + 1.170 +void 1.171 +Zone::discardJitCode(FreeOp *fop) 1.172 +{ 1.173 +#ifdef JS_ION 1.174 + if (!jitZone()) 1.175 + return; 1.176 + 1.177 + if (isPreservingCode()) { 1.178 + PurgeJITCaches(this); 1.179 + } else { 1.180 + 1.181 +# ifdef DEBUG 1.182 + /* Assert no baseline scripts are marked as active. */ 1.183 + for (CellIterUnderGC i(this, FINALIZE_SCRIPT); !i.done(); i.next()) { 1.184 + JSScript *script = i.get<JSScript>(); 1.185 + JS_ASSERT_IF(script->hasBaselineScript(), !script->baselineScript()->active()); 1.186 + } 1.187 +# endif 1.188 + 1.189 + /* Mark baseline scripts on the stack as active. */ 1.190 + jit::MarkActiveBaselineScripts(this); 1.191 + 1.192 + /* Only mark OSI points if code is being discarded. */ 1.193 + jit::InvalidateAll(fop, this); 1.194 + 1.195 + for (CellIterUnderGC i(this, FINALIZE_SCRIPT); !i.done(); i.next()) { 1.196 + JSScript *script = i.get<JSScript>(); 1.197 + jit::FinishInvalidation<SequentialExecution>(fop, script); 1.198 + 1.199 + // Preserve JIT code that have been recently used in 1.200 + // parallel. Note that we mark their baseline scripts as active as 1.201 + // well to preserve them. 1.202 + if (script->hasParallelIonScript()) { 1.203 + if (jit::ShouldPreserveParallelJITCode(runtimeFromMainThread(), script)) { 1.204 + script->parallelIonScript()->purgeCaches(); 1.205 + script->baselineScript()->setActive(); 1.206 + } else { 1.207 + jit::FinishInvalidation<ParallelExecution>(fop, script); 1.208 + } 1.209 + } 1.210 + 1.211 + /* 1.212 + * Discard baseline script if it's not marked as active. Note that 1.213 + * this also resets the active flag. 1.214 + */ 1.215 + jit::FinishDiscardBaselineScript(fop, script); 1.216 + 1.217 + /* 1.218 + * Use counts for scripts are reset on GC. After discarding code we 1.219 + * need to let it warm back up to get information such as which 1.220 + * opcodes are setting array holes or accessing getter properties. 1.221 + */ 1.222 + script->resetUseCount(); 1.223 + } 1.224 + 1.225 + jitZone()->optimizedStubSpace()->free(); 1.226 + } 1.227 +#endif 1.228 +} 1.229 + 1.230 +uint64_t 1.231 +Zone::gcNumber() 1.232 +{ 1.233 + // Zones in use by exclusive threads are not collected, and threads using 1.234 + // them cannot access the main runtime's gcNumber without racing. 1.235 + return usedByExclusiveThread ? 0 : runtimeFromMainThread()->gcNumber; 1.236 +} 1.237 + 1.238 +#ifdef JS_ION 1.239 +js::jit::JitZone * 1.240 +Zone::createJitZone(JSContext *cx) 1.241 +{ 1.242 + MOZ_ASSERT(!jitZone_); 1.243 + 1.244 + if (!cx->runtime()->getJitRuntime(cx)) 1.245 + return nullptr; 1.246 + 1.247 + jitZone_ = cx->new_<js::jit::JitZone>(); 1.248 + return jitZone_; 1.249 +} 1.250 +#endif 1.251 + 1.252 +JS::Zone * 1.253 +js::ZoneOfObject(const JSObject &obj) 1.254 +{ 1.255 + return obj.zone(); 1.256 +} 1.257 + 1.258 +JS::Zone * 1.259 +js::ZoneOfObjectFromAnyThread(const JSObject &obj) 1.260 +{ 1.261 + return obj.zoneFromAnyThread(); 1.262 +} 1.263 + 1.264 +bool 1.265 +Zone::hasMarkedCompartments() 1.266 +{ 1.267 + for (CompartmentsInZoneIter comp(this); !comp.done(); comp.next()) { 1.268 + if (comp->marked) 1.269 + return true; 1.270 + } 1.271 + return false; 1.272 +}