/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * vim: set ts=8 sw=4 et tw=78: * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifdef JSGC_GENERATIONAL #ifndef jsgc_storebuffer_h___ #define jsgc_storebuffer_h___ #include "jsgc.h" #include "jsalloc.h" #include "gc/Marking.h" namespace js { namespace gc { /* * Note: this is a stub Nursery that does not actually contain a heap, just a * set of pointers which are "inside" the nursery to implement verification. */ class Nursery { HashSet, SystemAllocPolicy> nursery; public: Nursery() : nursery() {} bool enable() { if (!nursery.initialized()) return nursery.init(); return true; } void disable() { if (!nursery.initialized()) return; nursery.finish(); } bool isInside(void *cell) const { JS_ASSERT((uintptr_t(cell) & 0x3) == 0); return nursery.initialized() && nursery.has(cell); } void insertPointer(void *cell) { nursery.putNew(cell); } }; /* * BufferableRef represents an abstract reference for use in the generational * GC's remembered set. Entries in the store buffer that cannot be represented * with the simple pointer-to-a-pointer scheme must derive from this class and * use the generic store buffer interface. */ class BufferableRef { public: virtual bool match(void *location) = 0; virtual void mark(JSTracer *trc) = 0; }; /* * HashKeyRef represents a reference to a HashTable key. Manual HashTable * barriers should should instantiate this template with their own table/key * type to insert into the generic buffer with putGeneric. */ template class HashKeyRef : public BufferableRef { Map *map; Key key; typedef typename Map::Ptr Ptr; public: HashKeyRef(Map *m, const Key &k) : map(m), key(k) {} bool match(void *location) { Ptr p = map->lookup(key); if (!p) return false; return &p->key == location; } void mark(JSTracer *trc) {} }; /* * The StoreBuffer observes all writes that occur in the system and performs * efficient filtering of them to derive a remembered set for nursery GC. */ class StoreBuffer { /* TODO: profile to find the ideal size for these. */ static const size_t ValueBufferSize = 1 * 1024 * sizeof(Value *); static const size_t CellBufferSize = 2 * 1024 * sizeof(Cell **); static const size_t SlotBufferSize = 2 * 1024 * (sizeof(JSObject *) + sizeof(uint32_t)); static const size_t RelocValueBufferSize = 1 * 1024 * sizeof(Value *); static const size_t RelocCellBufferSize = 1 * 1024 * sizeof(Cell **); static const size_t GenericBufferSize = 1 * 1024 * sizeof(int); static const size_t TotalSize = ValueBufferSize + CellBufferSize + SlotBufferSize + RelocValueBufferSize + RelocCellBufferSize + GenericBufferSize; typedef HashSet, SystemAllocPolicy> EdgeSet; /* * This buffer holds only a single type of edge. Using this buffer is more * efficient than the generic buffer when many writes will be to the same * type of edge: e.g. Value or Cell*. */ template class MonoTypeBuffer { friend class StoreBuffer; StoreBuffer *owner; Nursery *nursery; T *base; /* Pointer to the start of the buffer. */ T *pos; /* Pointer to the current insertion position. */ T *top; /* Pointer to one element after the end. */ MonoTypeBuffer(StoreBuffer *owner, Nursery *nursery) : owner(owner), nursery(nursery), base(NULL), pos(NULL), top(NULL) {} MonoTypeBuffer &operator=(const MonoTypeBuffer& other) MOZ_DELETE; bool enable(uint8_t *region, size_t len); void disable(); bool isEmpty() const { return pos == base; } bool isFull() const { JS_ASSERT(pos <= top); return pos == top; } /* Compaction algorithms. */ void compactNotInSet(); /* * Attempts to reduce the usage of the buffer by removing unnecessary * entries. */ virtual void compact(); /* Add one item to the buffer. */ void put(const T &v); /* For verification. */ bool accumulateEdges(EdgeSet &edges); }; /* * Overrides the MonoTypeBuffer to support pointers that may be moved in * memory outside of the GC's control. */ template class RelocatableMonoTypeBuffer : public MonoTypeBuffer { friend class StoreBuffer; RelocatableMonoTypeBuffer(StoreBuffer *owner, Nursery *nursery) : MonoTypeBuffer(owner, nursery) {} /* Override compaction to filter out removed items. */ void compactMoved(); virtual void compact(); /* Record a removal from the buffer. */ void unput(const T &v); }; class GenericBuffer { friend class StoreBuffer; StoreBuffer *owner; Nursery *nursery; uint8_t *base; /* Pointer to start of buffer. */ uint8_t *pos; /* Pointer to current buffer position. */ uint8_t *top; /* Pointer to one past the last entry. */ GenericBuffer(StoreBuffer *owner, Nursery *nursery) : owner(owner), nursery(nursery) {} GenericBuffer &operator=(const GenericBuffer& other) MOZ_DELETE; bool enable(uint8_t *region, size_t len); void disable(); /* Check if a pointer is present in the buffer. */ bool containsEdge(void *location) const; template void put(const T &t) { /* Check if we have been enabled. */ if (!pos) return; /* Check for overflow. */ if (top - pos < (unsigned)(sizeof(unsigned) + sizeof(T))) { owner->setOverflowed(); return; } *((unsigned *)pos) = sizeof(T); pos += sizeof(unsigned); T *p = (T *)pos; new (p) T(t); pos += sizeof(T); } }; class CellPtrEdge { friend class StoreBuffer; friend class StoreBuffer::MonoTypeBuffer; friend class StoreBuffer::RelocatableMonoTypeBuffer; Cell **edge; CellPtrEdge(Cell **v) : edge(v) {} bool operator==(const CellPtrEdge &other) const { return edge == other.edge; } bool operator!=(const CellPtrEdge &other) const { return edge != other.edge; } void *location() const { return (void *)edge; } bool inRememberedSet(Nursery *n) { return !n->isInside(edge) && n->isInside(*edge); } bool isNullEdge() const { return !*edge; } CellPtrEdge tagged() const { return CellPtrEdge((Cell **)(uintptr_t(edge) | 1)); } CellPtrEdge untagged() const { return CellPtrEdge((Cell **)(uintptr_t(edge) & ~1)); } bool isTagged() const { return bool(uintptr_t(edge) & 1); } }; class ValueEdge { friend class StoreBuffer; friend class StoreBuffer::MonoTypeBuffer; friend class StoreBuffer::RelocatableMonoTypeBuffer; Value *edge; ValueEdge(Value *v) : edge(v) {} bool operator==(const ValueEdge &other) const { return edge == other.edge; } bool operator!=(const ValueEdge &other) const { return edge != other.edge; } void *deref() const { return edge->isGCThing() ? edge->toGCThing() : NULL; } void *location() const { return (void *)edge; } bool inRememberedSet(Nursery *n) { return !n->isInside(edge) && n->isInside(deref()); } bool isNullEdge() const { return !deref(); } ValueEdge tagged() const { return ValueEdge((Value *)(uintptr_t(edge) | 1)); } ValueEdge untagged() const { return ValueEdge((Value *)(uintptr_t(edge) & ~1)); } bool isTagged() const { return bool(uintptr_t(edge) & 1); } }; struct SlotEdge { friend class StoreBuffer; friend class StoreBuffer::MonoTypeBuffer; JSObject *object; uint32_t offset; SlotEdge(JSObject *object, uint32_t offset) : object(object), offset(offset) {} bool operator==(const SlotEdge &other) const { return object == other.object && offset == other.offset; } bool operator!=(const SlotEdge &other) const { return object != other.object || offset != other.offset; } HeapSlot *slotLocation() const { if (object->isDenseArray()) { if (offset >= object->getDenseArrayInitializedLength()) return NULL; return (HeapSlot *)&object->getDenseArrayElement(offset); } if (offset >= object->slotSpan()) return NULL; return &object->getSlotRef(offset); } void *deref() const { HeapSlot *loc = slotLocation(); return (loc && loc->isGCThing()) ? loc->toGCThing() : NULL; } void *location() const { return (void *)slotLocation(); } bool inRememberedSet(Nursery *n) { return !n->isInside(object) && n->isInside(deref()); } bool isNullEdge() const { return !deref(); } }; MonoTypeBuffer bufferVal; MonoTypeBuffer bufferCell; MonoTypeBuffer bufferSlot; RelocatableMonoTypeBuffer bufferRelocVal; RelocatableMonoTypeBuffer bufferRelocCell; GenericBuffer bufferGeneric; Nursery *nursery; void *buffer; bool overflowed; bool enabled; /* For the verifier. */ EdgeSet edgeSet; /* For use by our owned buffers. */ void setOverflowed() { overflowed = true; } public: StoreBuffer(Nursery *n) : bufferVal(this, n), bufferCell(this, n), bufferSlot(this, n), bufferRelocVal(this, n), bufferRelocCell(this, n), bufferGeneric(this, n), nursery(n), buffer(NULL), overflowed(false), enabled(false) {} bool enable(); void disable(); bool isEnabled() { return enabled; } /* Get the overflowed status. */ bool hasOverflowed() const { return overflowed; } /* Insert a single edge into the buffer/remembered set. */ void putValue(Value *v) { bufferVal.put(v); } void putCell(Cell **o) { bufferCell.put(o); } void putSlot(JSObject *obj, uint32_t slot) { bufferSlot.put(SlotEdge(obj, slot)); } /* Insert or update a single edge in the Relocatable buffer. */ void putRelocatableValue(Value *v) { bufferRelocVal.put(v); } void putRelocatableCell(Cell **c) { bufferRelocCell.put(c); } void removeRelocatableValue(Value *v) { bufferRelocVal.unput(v); } void removeRelocatableCell(Cell **c) { bufferRelocCell.unput(c); } /* Insert an entry into the generic buffer. */ template void putGeneric(const T &t) { bufferGeneric.put(t); } /* For the verifier. */ bool coalesceForVerification(); void releaseVerificationData(); bool containsEdgeAt(void *loc) const; }; } /* namespace gc */ } /* namespace js */ #endif /* jsgc_storebuffer_h___ */ #endif /* JSGC_GENERATIONAL */