1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/js/src/gc/StoreBuffer.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,380 @@ 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 +#ifdef JSGC_GENERATIONAL 1.11 + 1.12 +#include "gc/StoreBuffer.h" 1.13 + 1.14 +#include "mozilla/Assertions.h" 1.15 + 1.16 +#include "vm/ArgumentsObject.h" 1.17 +#include "vm/ForkJoin.h" 1.18 + 1.19 +#include "jsgcinlines.h" 1.20 + 1.21 +using namespace js; 1.22 +using namespace js::gc; 1.23 +using mozilla::ReentrancyGuard; 1.24 + 1.25 +/*** Edges ***/ 1.26 + 1.27 +void 1.28 +StoreBuffer::SlotsEdge::mark(JSTracer *trc) 1.29 +{ 1.30 + JSObject *obj = object(); 1.31 + 1.32 + if (trc->runtime()->gcNursery.isInside(obj)) 1.33 + return; 1.34 + 1.35 + if (!obj->isNative()) { 1.36 + const Class *clasp = obj->getClass(); 1.37 + if (clasp) 1.38 + clasp->trace(trc, obj); 1.39 + return; 1.40 + } 1.41 + 1.42 + if (kind() == ElementKind) { 1.43 + int32_t initLen = obj->getDenseInitializedLength(); 1.44 + int32_t clampedStart = Min(start_, initLen); 1.45 + int32_t clampedEnd = Min(start_ + count_, initLen); 1.46 + gc::MarkArraySlots(trc, clampedEnd - clampedStart, 1.47 + obj->getDenseElements() + clampedStart, "element"); 1.48 + } else { 1.49 + int32_t start = Min(uint32_t(start_), obj->slotSpan()); 1.50 + int32_t end = Min(uint32_t(start_) + count_, obj->slotSpan()); 1.51 + MOZ_ASSERT(end >= start); 1.52 + MarkObjectSlots(trc, obj, start, end - start); 1.53 + } 1.54 +} 1.55 + 1.56 +void 1.57 +StoreBuffer::WholeCellEdges::mark(JSTracer *trc) 1.58 +{ 1.59 + JS_ASSERT(edge->isTenured()); 1.60 + JSGCTraceKind kind = GetGCThingTraceKind(edge); 1.61 + if (kind <= JSTRACE_OBJECT) { 1.62 + JSObject *object = static_cast<JSObject *>(edge); 1.63 + if (object->is<ArgumentsObject>()) 1.64 + ArgumentsObject::trace(trc, object); 1.65 + MarkChildren(trc, object); 1.66 + return; 1.67 + } 1.68 +#ifdef JS_ION 1.69 + JS_ASSERT(kind == JSTRACE_JITCODE); 1.70 + static_cast<jit::JitCode *>(edge)->trace(trc); 1.71 +#else 1.72 + MOZ_ASSUME_UNREACHABLE("Only objects can be in the wholeCellBuffer if IonMonkey is disabled."); 1.73 +#endif 1.74 +} 1.75 + 1.76 +void 1.77 +StoreBuffer::CellPtrEdge::mark(JSTracer *trc) 1.78 +{ 1.79 + if (!*edge) 1.80 + return; 1.81 + 1.82 + JS_ASSERT(GetGCThingTraceKind(*edge) == JSTRACE_OBJECT); 1.83 + MarkObjectRoot(trc, reinterpret_cast<JSObject**>(edge), "store buffer edge"); 1.84 +} 1.85 + 1.86 +void 1.87 +StoreBuffer::ValueEdge::mark(JSTracer *trc) 1.88 +{ 1.89 + if (!deref()) 1.90 + return; 1.91 + 1.92 + MarkValueRoot(trc, edge, "store buffer edge"); 1.93 +} 1.94 + 1.95 +/*** MonoTypeBuffer ***/ 1.96 + 1.97 +template <typename T> 1.98 +void 1.99 +StoreBuffer::MonoTypeBuffer<T>::handleOverflow(StoreBuffer *owner) 1.100 +{ 1.101 + if (!owner->isAboutToOverflow()) { 1.102 + /* 1.103 + * Compact the buffer now, and if that fails to free enough space then 1.104 + * trigger a minor collection. 1.105 + */ 1.106 + compact(owner); 1.107 + if (isAboutToOverflow()) 1.108 + owner->setAboutToOverflow(); 1.109 + } else { 1.110 + /* 1.111 + * A minor GC has already been triggered, so there's no point 1.112 + * compacting unless the buffer is totally full. 1.113 + */ 1.114 + if (storage_->availableInCurrentChunk() < sizeof(T)) 1.115 + maybeCompact(owner); 1.116 + } 1.117 +} 1.118 + 1.119 +template <typename T> 1.120 +void 1.121 +StoreBuffer::MonoTypeBuffer<T>::compactRemoveDuplicates(StoreBuffer *owner) 1.122 +{ 1.123 + typedef HashSet<T, typename T::Hasher, SystemAllocPolicy> DedupSet; 1.124 + 1.125 + DedupSet duplicates; 1.126 + if (!duplicates.init()) 1.127 + return; /* Failure to de-dup is acceptable. */ 1.128 + 1.129 + LifoAlloc::Enum insert(*storage_); 1.130 + for (LifoAlloc::Enum e(*storage_); !e.empty(); e.popFront<T>()) { 1.131 + T *edge = e.get<T>(); 1.132 + if (!duplicates.has(*edge)) { 1.133 + insert.updateFront<T>(*edge); 1.134 + insert.popFront<T>(); 1.135 + 1.136 + /* Failure to insert will leave the set with duplicates. Oh well. */ 1.137 + duplicates.put(*edge); 1.138 + } 1.139 + } 1.140 + storage_->release(insert.mark()); 1.141 + 1.142 + duplicates.clear(); 1.143 +} 1.144 + 1.145 +template <typename T> 1.146 +void 1.147 +StoreBuffer::MonoTypeBuffer<T>::compact(StoreBuffer *owner) 1.148 +{ 1.149 + JS_ASSERT(storage_); 1.150 + compactRemoveDuplicates(owner); 1.151 + usedAtLastCompact_ = storage_->used(); 1.152 +} 1.153 + 1.154 +template <typename T> 1.155 +void 1.156 +StoreBuffer::MonoTypeBuffer<T>::maybeCompact(StoreBuffer *owner) 1.157 +{ 1.158 + JS_ASSERT(storage_); 1.159 + if (storage_->used() != usedAtLastCompact_) 1.160 + compact(owner); 1.161 +} 1.162 + 1.163 +template <typename T> 1.164 +void 1.165 +StoreBuffer::MonoTypeBuffer<T>::mark(StoreBuffer *owner, JSTracer *trc) 1.166 +{ 1.167 + JS_ASSERT(owner->isEnabled()); 1.168 + ReentrancyGuard g(*owner); 1.169 + if (!storage_) 1.170 + return; 1.171 + 1.172 + maybeCompact(owner); 1.173 + for (LifoAlloc::Enum e(*storage_); !e.empty(); e.popFront<T>()) { 1.174 + T *edge = e.get<T>(); 1.175 + edge->mark(trc); 1.176 + } 1.177 +} 1.178 + 1.179 +/*** RelocatableMonoTypeBuffer ***/ 1.180 + 1.181 +template <typename T> 1.182 +void 1.183 +StoreBuffer::RelocatableMonoTypeBuffer<T>::compactMoved(StoreBuffer *owner) 1.184 +{ 1.185 + LifoAlloc &storage = *this->storage_; 1.186 + EdgeSet invalidated; 1.187 + if (!invalidated.init()) 1.188 + CrashAtUnhandlableOOM("RelocatableMonoTypeBuffer::compactMoved: Failed to init table."); 1.189 + 1.190 + /* Collect the set of entries which are currently invalid. */ 1.191 + for (LifoAlloc::Enum e(storage); !e.empty(); e.popFront<T>()) { 1.192 + T *edge = e.get<T>(); 1.193 + if (edge->isTagged()) { 1.194 + if (!invalidated.put(edge->untagged().edge)) 1.195 + CrashAtUnhandlableOOM("RelocatableMonoTypeBuffer::compactMoved: Failed to put removal."); 1.196 + } else { 1.197 + invalidated.remove(edge->untagged().edge); 1.198 + } 1.199 + } 1.200 + 1.201 + /* Remove all entries which are in the invalidated set. */ 1.202 + LifoAlloc::Enum insert(storage); 1.203 + for (LifoAlloc::Enum e(storage); !e.empty(); e.popFront<T>()) { 1.204 + T *edge = e.get<T>(); 1.205 + if (!edge->isTagged() && !invalidated.has(edge->untagged().edge)) { 1.206 + insert.updateFront<T>(*edge); 1.207 + insert.popFront<T>(); 1.208 + } 1.209 + } 1.210 + storage.release(insert.mark()); 1.211 + 1.212 + invalidated.clear(); 1.213 + 1.214 +#ifdef DEBUG 1.215 + for (LifoAlloc::Enum e(storage); !e.empty(); e.popFront<T>()) 1.216 + JS_ASSERT(!e.get<T>()->isTagged()); 1.217 +#endif 1.218 +} 1.219 + 1.220 +template <typename T> 1.221 +void 1.222 +StoreBuffer::RelocatableMonoTypeBuffer<T>::compact(StoreBuffer *owner) 1.223 +{ 1.224 + compactMoved(owner); 1.225 + StoreBuffer::MonoTypeBuffer<T>::compact(owner); 1.226 +} 1.227 + 1.228 +/*** GenericBuffer ***/ 1.229 + 1.230 +void 1.231 +StoreBuffer::GenericBuffer::mark(StoreBuffer *owner, JSTracer *trc) 1.232 +{ 1.233 + JS_ASSERT(owner->isEnabled()); 1.234 + ReentrancyGuard g(*owner); 1.235 + if (!storage_) 1.236 + return; 1.237 + 1.238 + for (LifoAlloc::Enum e(*storage_); !e.empty();) { 1.239 + unsigned size = *e.get<unsigned>(); 1.240 + e.popFront<unsigned>(); 1.241 + BufferableRef *edge = e.get<BufferableRef>(size); 1.242 + edge->mark(trc); 1.243 + e.popFront(size); 1.244 + } 1.245 +} 1.246 + 1.247 +/*** StoreBuffer ***/ 1.248 + 1.249 +bool 1.250 +StoreBuffer::enable() 1.251 +{ 1.252 + if (enabled_) 1.253 + return true; 1.254 + 1.255 + if (!bufferVal.init() || 1.256 + !bufferCell.init() || 1.257 + !bufferSlot.init() || 1.258 + !bufferWholeCell.init() || 1.259 + !bufferRelocVal.init() || 1.260 + !bufferRelocCell.init() || 1.261 + !bufferGeneric.init()) 1.262 + { 1.263 + return false; 1.264 + } 1.265 + 1.266 + enabled_ = true; 1.267 + return true; 1.268 +} 1.269 + 1.270 +void 1.271 +StoreBuffer::disable() 1.272 +{ 1.273 + if (!enabled_) 1.274 + return; 1.275 + 1.276 + aboutToOverflow_ = false; 1.277 + 1.278 + enabled_ = false; 1.279 +} 1.280 + 1.281 +bool 1.282 +StoreBuffer::clear() 1.283 +{ 1.284 + if (!enabled_) 1.285 + return true; 1.286 + 1.287 + aboutToOverflow_ = false; 1.288 + 1.289 + bufferVal.clear(); 1.290 + bufferCell.clear(); 1.291 + bufferSlot.clear(); 1.292 + bufferWholeCell.clear(); 1.293 + bufferRelocVal.clear(); 1.294 + bufferRelocCell.clear(); 1.295 + bufferGeneric.clear(); 1.296 + 1.297 + return true; 1.298 +} 1.299 + 1.300 +void 1.301 +StoreBuffer::markAll(JSTracer *trc) 1.302 +{ 1.303 + bufferVal.mark(this, trc); 1.304 + bufferCell.mark(this, trc); 1.305 + bufferSlot.mark(this, trc); 1.306 + bufferWholeCell.mark(this, trc); 1.307 + bufferRelocVal.mark(this, trc); 1.308 + bufferRelocCell.mark(this, trc); 1.309 + bufferGeneric.mark(this, trc); 1.310 +} 1.311 + 1.312 +void 1.313 +StoreBuffer::setAboutToOverflow() 1.314 +{ 1.315 + aboutToOverflow_ = true; 1.316 + runtime_->requestInterrupt(JSRuntime::RequestInterruptMainThread); 1.317 +} 1.318 + 1.319 +bool 1.320 +StoreBuffer::inParallelSection() const 1.321 +{ 1.322 + return InParallelSection(); 1.323 +} 1.324 + 1.325 +void 1.326 +StoreBuffer::addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf, JS::GCSizes 1.327 +*sizes) 1.328 +{ 1.329 + sizes->storeBufferVals += bufferVal.sizeOfExcludingThis(mallocSizeOf); 1.330 + sizes->storeBufferCells += bufferCell.sizeOfExcludingThis(mallocSizeOf); 1.331 + sizes->storeBufferSlots += bufferSlot.sizeOfExcludingThis(mallocSizeOf); 1.332 + sizes->storeBufferWholeCells += bufferWholeCell.sizeOfExcludingThis(mallocSizeOf); 1.333 + sizes->storeBufferRelocVals += bufferRelocVal.sizeOfExcludingThis(mallocSizeOf); 1.334 + sizes->storeBufferRelocCells += bufferRelocCell.sizeOfExcludingThis(mallocSizeOf); 1.335 + sizes->storeBufferGenerics += bufferGeneric.sizeOfExcludingThis(mallocSizeOf); 1.336 +} 1.337 + 1.338 +JS_PUBLIC_API(void) 1.339 +JS::HeapCellPostBarrier(js::gc::Cell **cellp) 1.340 +{ 1.341 + JS_ASSERT(*cellp); 1.342 + JSRuntime *runtime = (*cellp)->runtimeFromMainThread(); 1.343 + runtime->gcStoreBuffer.putRelocatableCell(cellp); 1.344 +} 1.345 + 1.346 +JS_PUBLIC_API(void) 1.347 +JS::HeapCellRelocate(js::gc::Cell **cellp) 1.348 +{ 1.349 + /* Called with old contents of *pp before overwriting. */ 1.350 + JS_ASSERT(*cellp); 1.351 + JSRuntime *runtime = (*cellp)->runtimeFromMainThread(); 1.352 + runtime->gcStoreBuffer.removeRelocatableCell(cellp); 1.353 +} 1.354 + 1.355 +JS_PUBLIC_API(void) 1.356 +JS::HeapValuePostBarrier(JS::Value *valuep) 1.357 +{ 1.358 + JS_ASSERT(valuep->isMarkable()); 1.359 + if (valuep->isString() && StringIsPermanentAtom(valuep->toString())) 1.360 + return; 1.361 + JSRuntime *runtime = static_cast<js::gc::Cell *>(valuep->toGCThing())->runtimeFromMainThread(); 1.362 + runtime->gcStoreBuffer.putRelocatableValue(valuep); 1.363 +} 1.364 + 1.365 +JS_PUBLIC_API(void) 1.366 +JS::HeapValueRelocate(JS::Value *valuep) 1.367 +{ 1.368 + /* Called with old contents of *valuep before overwriting. */ 1.369 + JS_ASSERT(valuep->isMarkable()); 1.370 + if (valuep->isString() && StringIsPermanentAtom(valuep->toString())) 1.371 + return; 1.372 + JSRuntime *runtime = static_cast<js::gc::Cell *>(valuep->toGCThing())->runtimeFromMainThread(); 1.373 + runtime->gcStoreBuffer.removeRelocatableValue(valuep); 1.374 +} 1.375 + 1.376 +template class StoreBuffer::MonoTypeBuffer<StoreBuffer::ValueEdge>; 1.377 +template class StoreBuffer::MonoTypeBuffer<StoreBuffer::CellPtrEdge>; 1.378 +template class StoreBuffer::MonoTypeBuffer<StoreBuffer::SlotsEdge>; 1.379 +template class StoreBuffer::MonoTypeBuffer<StoreBuffer::WholeCellEdges>; 1.380 +template class StoreBuffer::RelocatableMonoTypeBuffer<StoreBuffer::ValueEdge>; 1.381 +template class StoreBuffer::RelocatableMonoTypeBuffer<StoreBuffer::CellPtrEdge>; 1.382 + 1.383 +#endif /* JSGC_GENERATIONAL */