Bug 902174 - Aggressively optimize js::gc::StoreBuffer for size; r=jonco
authorTerrence Cole <terrence@mozilla.com>
Thu, 14 Nov 2013 16:48:33 -0800
changeset 172661 905977682152f1b5960fbd78b526c8ab097396db
parent 172660 ab7ece2fd8059f687d0c60ec188c568916a04877
child 172662 cec708d1ff8635dae224797036e42643c412a592
push id3224
push userlsblakk@mozilla.com
push dateTue, 04 Feb 2014 01:06:49 +0000
treeherdermozilla-beta@60c04d0987f1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjonco
bugs902174
milestone28.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 902174 - Aggressively optimize js::gc::StoreBuffer for size; r=jonco
js/src/gc/GCInternals.h
js/src/gc/StoreBuffer.cpp
js/src/gc/StoreBuffer.h
--- a/js/src/gc/GCInternals.h
+++ b/js/src/gc/GCInternals.h
@@ -6,17 +6,16 @@
 
 #ifndef gc_GCInternals_h
 #define gc_GCInternals_h
 
 #include "jscntxt.h"
 #include "jsworkers.h"
 
 #include "gc/Zone.h"
-
 #include "vm/Runtime.h"
 
 namespace js {
 namespace gc {
 
 void
 MarkPersistentRootedChains(JSTracer *trc);
 
--- a/js/src/gc/StoreBuffer.cpp
+++ b/js/src/gc/StoreBuffer.cpp
@@ -41,31 +41,32 @@ StoreBuffer::SlotEdge::deref() const
 }
 
 JS_ALWAYS_INLINE void *
 StoreBuffer::SlotEdge::location() const
 {
     return (void *)slotLocation();
 }
 
-JS_ALWAYS_INLINE bool
+bool
 StoreBuffer::SlotEdge::inRememberedSet(const Nursery &nursery) const
 {
     return !nursery.isInside(object) && nursery.isInside(deref());
 }
 
 JS_ALWAYS_INLINE bool
 StoreBuffer::SlotEdge::isNullEdge() const
 {
     return !deref();
 }
 
 void
 StoreBuffer::WholeCellEdges::mark(JSTracer *trc)
 {
+    JS_ASSERT(tenured->isTenured());
     JSGCTraceKind kind = GetGCThingTraceKind(tenured);
     if (kind <= JSTRACE_OBJECT) {
         MarkChildren(trc, static_cast<JSObject *>(tenured));
         return;
     }
 #ifdef JS_ION
     JS_ASSERT(kind == JSTRACE_IONCODE);
     static_cast<jit::IonCode *>(tenured)->trace(trc);
@@ -73,69 +74,76 @@ StoreBuffer::WholeCellEdges::mark(JSTrac
     MOZ_ASSUME_UNREACHABLE("Only objects can be in the wholeCellBuffer if IonMonkey is disabled.");
 #endif
 }
 
 /*** MonoTypeBuffer ***/
 
 template <typename T>
 void
-StoreBuffer::MonoTypeBuffer<T>::compactRemoveDuplicates()
+StoreBuffer::MonoTypeBuffer<T>::compactRemoveDuplicates(StoreBuffer *owner)
 {
-    EdgeSet &duplicates = owner->edgeSet;
-    JS_ASSERT(duplicates.empty());
+    EdgeSet duplicates;
+    if (!duplicates.init())
+        return; /* Failure to de-dup is acceptable. */
 
-    LifoAlloc::Enum insert(storage_);
-    for (LifoAlloc::Enum e(storage_); !e.empty(); e.popFront<T>()) {
+    LifoAlloc::Enum insert(*storage_);
+    for (LifoAlloc::Enum e(*storage_); !e.empty(); e.popFront<T>()) {
         T *edge = e.get<T>();
         if (!duplicates.has(edge->location())) {
             insert.updateFront<T>(*edge);
             insert.popFront<T>();
 
             /* Failure to insert will leave the set with duplicates. Oh well. */
             duplicates.put(edge->location());
         }
     }
-    storage_.release(insert.mark());
+    storage_->release(insert.mark());
 
     duplicates.clear();
 }
 
 template <typename T>
 void
-StoreBuffer::MonoTypeBuffer<T>::compact()
+StoreBuffer::MonoTypeBuffer<T>::compact(StoreBuffer *owner)
 {
-    compactRemoveDuplicates();
+    if (!storage_)
+        return;
+
+    compactRemoveDuplicates(owner);
 }
 
 template <typename T>
 void
-StoreBuffer::MonoTypeBuffer<T>::mark(JSTracer *trc)
+StoreBuffer::MonoTypeBuffer<T>::mark(StoreBuffer *owner, JSTracer *trc)
 {
-    ReentrancyGuard g(*this);
+    ReentrancyGuard g(*owner);
+    if (!storage_)
+        return;
 
-    compact();
-    for (LifoAlloc::Enum e(storage_); !e.empty(); e.popFront<T>()) {
+    compact(owner);
+    for (LifoAlloc::Enum e(*storage_); !e.empty(); e.popFront<T>()) {
         T *edge = e.get<T>();
         if (edge->isNullEdge())
             continue;
         edge->mark(trc);
 
     }
 }
 
 /*** RelocatableMonoTypeBuffer ***/
 
 template <typename T>
 void
-StoreBuffer::RelocatableMonoTypeBuffer<T>::compactMoved()
+StoreBuffer::RelocatableMonoTypeBuffer<T>::compactMoved(StoreBuffer *owner)
 {
-    LifoAlloc &storage = this->storage_;
-    EdgeSet &invalidated = this->owner->edgeSet;
-    JS_ASSERT(invalidated.empty());
+    LifoAlloc &storage = *this->storage_;
+    EdgeSet invalidated;
+    if (!invalidated.init())
+        MOZ_CRASH("RelocatableMonoTypeBuffer::compactMoved: Failed to init table.");
 
     /* Collect the set of entries which are currently invalid. */
     for (LifoAlloc::Enum e(storage); !e.empty(); e.popFront<T>()) {
         T *edge = e.get<T>();
         if (edge->isTagged()) {
             if (!invalidated.put(edge->location()))
                 MOZ_CRASH("RelocatableMonoTypeBuffer::compactMoved: Failed to put removal.");
         } else {
@@ -159,30 +167,32 @@ StoreBuffer::RelocatableMonoTypeBuffer<T
 #ifdef DEBUG
     for (LifoAlloc::Enum e(storage); !e.empty(); e.popFront<T>())
         JS_ASSERT(!e.get<T>()->isTagged());
 #endif
 }
 
 template <typename T>
 void
-StoreBuffer::RelocatableMonoTypeBuffer<T>::compact()
+StoreBuffer::RelocatableMonoTypeBuffer<T>::compact(StoreBuffer *owner)
 {
-    compactMoved();
-    StoreBuffer::MonoTypeBuffer<T>::compact();
+    compactMoved(owner);
+    StoreBuffer::MonoTypeBuffer<T>::compact(owner);
 }
 
 /*** GenericBuffer ***/
 
 void
-StoreBuffer::GenericBuffer::mark(JSTracer *trc)
+StoreBuffer::GenericBuffer::mark(StoreBuffer *owner, JSTracer *trc)
 {
-    ReentrancyGuard g(*this);
+    ReentrancyGuard g(*owner);
+    if (!storage_)
+        return;
 
-    for (LifoAlloc::Enum e(storage_); !e.empty();) {
+    for (LifoAlloc::Enum e(*storage_); !e.empty();) {
         unsigned size = *e.get<unsigned>();
         e.popFront<unsigned>();
         BufferableRef *edge = e.get<BufferableRef>(size);
         edge->mark(trc);
         e.popFront(size);
     }
 }
 
@@ -210,57 +220,52 @@ StoreBuffer::SlotEdge::mark(JSTracer *tr
         MarkSlot(trc, &object->getSlotRef(offset), "store buffer edge");
 }
 
 /*** StoreBuffer ***/
 
 bool
 StoreBuffer::enable()
 {
-    if (enabled)
+    if (enabled_)
         return true;
 
-    bufferVal.enable();
-    bufferCell.enable();
-    bufferSlot.enable();
-    bufferWholeCell.enable();
-    bufferRelocVal.enable();
-    bufferRelocCell.enable();
-    bufferGeneric.enable();
+    if (!bufferVal.init() ||
+        !bufferCell.init() ||
+        !bufferSlot.init() ||
+        !bufferWholeCell.init() ||
+        !bufferRelocVal.init() ||
+        !bufferRelocCell.init() ||
+        !bufferGeneric.init())
+    {
+        return false;
+    }
 
-    enabled = true;
+    enabled_ = true;
     return true;
 }
 
 void
 StoreBuffer::disable()
 {
-    if (!enabled)
+    if (!enabled_)
         return;
 
-    aboutToOverflow = false;
+    aboutToOverflow_ = false;
 
-    bufferVal.disable();
-    bufferCell.disable();
-    bufferSlot.disable();
-    bufferWholeCell.disable();
-    bufferRelocVal.disable();
-    bufferRelocCell.disable();
-    bufferGeneric.disable();
-
-    enabled = false;
+    enabled_ = false;
 }
 
 bool
 StoreBuffer::clear()
 {
-    if (!enabled)
+    if (!enabled_)
         return true;
 
-    aboutToOverflow = false;
+    aboutToOverflow_ = false;
 
     bufferVal.clear();
     bufferCell.clear();
     bufferSlot.clear();
     bufferWholeCell.clear();
     bufferRelocVal.clear();
     bufferRelocCell.clear();
     bufferGeneric.clear();
@@ -268,30 +273,30 @@ StoreBuffer::clear()
     return true;
 }
 
 void
 StoreBuffer::mark(JSTracer *trc)
 {
     JS_ASSERT(isEnabled());
 
-    bufferVal.mark(trc);
-    bufferCell.mark(trc);
-    bufferSlot.mark(trc);
-    bufferWholeCell.mark(trc);
-    bufferRelocVal.mark(trc);
-    bufferRelocCell.mark(trc);
-    bufferGeneric.mark(trc);
+    bufferVal.mark(this, trc);
+    bufferCell.mark(this, trc);
+    bufferSlot.mark(this, trc);
+    bufferWholeCell.mark(this, trc);
+    bufferRelocVal.mark(this, trc);
+    bufferRelocCell.mark(this, trc);
+    bufferGeneric.mark(this, trc);
 }
 
 void
 StoreBuffer::setAboutToOverflow()
 {
-    aboutToOverflow = true;
-    runtime->triggerOperationCallback(JSRuntime::TriggerCallbackMainThread);
+    aboutToOverflow_ = true;
+    runtime_->triggerOperationCallback(JSRuntime::TriggerCallbackMainThread);
 }
 
 bool
 StoreBuffer::inParallelSection() const
 {
     return InParallelSection();
 }
 
--- a/js/src/gc/StoreBuffer.h
+++ b/js/src/gc/StoreBuffer.h
@@ -30,16 +30,17 @@ namespace gc {
  * 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 void mark(JSTracer *trc) = 0;
+    bool inRememberedSet(const Nursery &) const { return true; }
 };
 
 /*
  * HashKeyRef represents a reference to a HashMap key. This should normally
  * be used through the HashTableWriteBarrierPost function.
  */
 template <typename Map, typename Key>
 class HashKeyRef : public BufferableRef
@@ -58,160 +59,154 @@ class HashKeyRef : public BufferableRef
         JS_SET_TRACING_LOCATION(trc, (void*)&*p);
         Mark(trc, &key, "HashKeyRef");
         map->rekeyIfMoved(prior, key);
     }
 };
 
 typedef HashSet<void *, PointerHasher<void *, 3>, SystemAllocPolicy> EdgeSet;
 
+/* The size of a single block of store buffer storage space. */
+static const size_t LifoAllocBlockSize = 1 << 16; /* 64KiB */
+
 /*
  * 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
 {
-    /* The size of a single block of store buffer storage space. */
-    static const size_t ChunkSize = 1 << 16; /* 64KiB */
+    friend class mozilla::ReentrancyGuard;
 
     /* The size at which a block is about to overflow. */
-    static const size_t MinAvailableSize = (size_t)(ChunkSize * 1.0 / 8.0);
+    static const size_t MinAvailableSize = (size_t)(LifoAllocBlockSize * 1.0 / 8.0);
 
     /*
      * 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<typename T>
     class MonoTypeBuffer
     {
         friend class StoreBuffer;
-        friend class mozilla::ReentrancyGuard;
 
-        StoreBuffer *owner;
+        LifoAlloc *storage_;
 
-        LifoAlloc storage_;
-        bool enabled_;
-        mozilla::DebugOnly<bool> entered;
-
-        explicit MonoTypeBuffer(StoreBuffer *owner)
-          : owner(owner), storage_(ChunkSize), enabled_(false), entered(false)
-        {}
+        explicit MonoTypeBuffer() : storage_(nullptr) {}
+        ~MonoTypeBuffer() { js_delete(storage_); }
 
         MonoTypeBuffer &operator=(const MonoTypeBuffer& other) MOZ_DELETE;
 
-        void enable() { enabled_ = true; }
-        void disable() { enabled_ = false; clear(); }
-        void clear() { storage_.used() ? storage_.releaseAll() : storage_.freeAll(); }
+        bool init() {
+            storage_ = js_new<LifoAlloc>(LifoAllocBlockSize);
+            return bool(storage_);
+        }
+
+        void clear() {
+            if (!storage_)
+                return;
+
+            storage_->used() ? storage_->releaseAll() : storage_->freeAll();
+        }
 
         bool isAboutToOverflow() const {
-            return !storage_.isEmpty() && storage_.availableInCurrentChunk() < MinAvailableSize;
+            return !storage_->isEmpty() && storage_->availableInCurrentChunk() < MinAvailableSize;
         }
 
         /* Compaction algorithms. */
-        void compactRemoveDuplicates();
+        void compactRemoveDuplicates(StoreBuffer *owner);
 
         /*
          * Attempts to reduce the usage of the buffer by removing unnecessary
          * entries.
          */
-        virtual void compact();
+        virtual void compact(StoreBuffer *owner);
 
         /* Add one item to the buffer. */
-        void put(const T &t) {
-            mozilla::ReentrancyGuard g(*this);
-            JS_ASSERT(CurrentThreadCanAccessRuntime(owner->runtime));
+        void put(StoreBuffer *owner, const T &t) {
+            JS_ASSERT(storage_);
 
-            if (!enabled_)
-                return;
-
-            T *tp = storage_.new_<T>(t);
+            T *tp = storage_->new_<T>(t);
             if (!tp)
                 MOZ_CRASH();
 
             if (isAboutToOverflow()) {
-                compact();
+                compact(owner);
                 if (isAboutToOverflow())
                     owner->setAboutToOverflow();
             }
         }
 
         /* Mark the source of all edges in the store buffer. */
-        void mark(JSTracer *trc);
+        void mark(StoreBuffer *owner, JSTracer *trc);
     };
 
     /*
      * Overrides the MonoTypeBuffer to support pointers that may be moved in
      * memory outside of the GC's control.
      */
     template <typename T>
     class RelocatableMonoTypeBuffer : public MonoTypeBuffer<T>
     {
         friend class StoreBuffer;
 
-        explicit RelocatableMonoTypeBuffer(StoreBuffer *owner)
-          : MonoTypeBuffer<T>(owner)
-        {}
-
         /* Override compaction to filter out removed items. */
-        void compactMoved();
-        virtual void compact();
+        void compactMoved(StoreBuffer *owner);
+        virtual void compact(StoreBuffer *owner) MOZ_OVERRIDE;
 
         /* Record a removal from the buffer. */
-        void unput(const T &v) {
-            JS_ASSERT(CurrentThreadCanAccessRuntime(this->owner->runtime));
-            MonoTypeBuffer<T>::put(v.tagged());
+        void unput(StoreBuffer *owner, const T &v) {
+            MonoTypeBuffer<T>::put(owner, v.tagged());
         }
     };
 
     class GenericBuffer
     {
         friend class StoreBuffer;
-        friend class mozilla::ReentrancyGuard;
+
+        LifoAlloc *storage_;
 
-        StoreBuffer *owner;
-        LifoAlloc storage_;
-        bool enabled_;
-        mozilla::DebugOnly<bool> entered;
-
-        explicit GenericBuffer(StoreBuffer *owner)
-          : owner(owner), storage_(ChunkSize), enabled_(false), entered(false)
-        {}
+        explicit GenericBuffer() : storage_(nullptr) {}
+        ~GenericBuffer() { js_delete(storage_); }
 
         GenericBuffer &operator=(const GenericBuffer& other) MOZ_DELETE;
 
-        void enable() { enabled_ = true; }
-        void disable() { enabled_ = false; clear(); }
-        void clear() { storage_.used() ? storage_.releaseAll() : storage_.freeAll(); }
+        bool init() {
+            storage_ = js_new<LifoAlloc>(LifoAllocBlockSize);
+            return bool(storage_);
+        }
+
+        void clear() {
+            if (!storage_)
+                return;
+
+            storage_->used() ? storage_->releaseAll() : storage_->freeAll();
+        }
 
         bool isAboutToOverflow() const {
-            return !storage_.isEmpty() && storage_.availableInCurrentChunk() < MinAvailableSize;
+            return !storage_->isEmpty() && storage_->availableInCurrentChunk() < MinAvailableSize;
         }
 
         /* Mark all generic edges. */
-        void mark(JSTracer *trc);
+        void mark(StoreBuffer *owner, JSTracer *trc);
 
         template <typename T>
-        void put(const T &t) {
-            mozilla::ReentrancyGuard g(*this);
-            JS_ASSERT(CurrentThreadCanAccessRuntime(owner->runtime));
+        void put(StoreBuffer *owner, const T &t) {
+            JS_ASSERT(storage_);
 
             /* Ensure T is derived from BufferableRef. */
             (void)static_cast<const BufferableRef*>(&t);
 
-            if (!enabled_)
-                return;
-
             unsigned size = sizeof(T);
-            unsigned *sizep = storage_.newPod<unsigned>();
+            unsigned *sizep = storage_->newPod<unsigned>();
             if (!sizep)
                 MOZ_CRASH();
             *sizep = size;
 
-            T *tp = storage_.new_<T>(t);
+            T *tp = storage_->new_<T>(t);
             if (!tp)
                 MOZ_CRASH();
 
             if (isAboutToOverflow())
                 owner->setAboutToOverflow();
         }
     };
 
@@ -294,17 +289,17 @@ class StoreBuffer
         bool operator!=(const SlotEdge &other) const {
             return object != other.object || offset != other.offset || kind != other.kind;
         }
 
         JS_ALWAYS_INLINE HeapSlot *slotLocation() const;
 
         JS_ALWAYS_INLINE void *deref() const;
         JS_ALWAYS_INLINE void *location() const;
-        JS_ALWAYS_INLINE bool inRememberedSet(const Nursery &nursery) const;
+        bool inRememberedSet(const Nursery &nursery) const;
         JS_ALWAYS_INLINE bool isNullEdge() const;
 
         void mark(JSTracer *trc);
     };
 
     class WholeCellEdges
     {
         friend class StoreBuffer;
@@ -341,107 +336,110 @@ class StoreBuffer
         }
 
       private:
         MarkCallback callback;
         void *key;
         void *data;
     };
 
+    template <typename Edge>
+    bool isOkayToUseBuffer(const Edge &edge) const {
+        /*
+         * Disabled store buffers may not have a valid state; e.g. when stored
+         * inline in the ChunkTrailer.
+         */
+        if (!isEnabled())
+            return false;
+
+        /*
+         * The concurrent parsing thread cannot validly insert into the buffer,
+         * but it should not activate the re-entrancy guard either.
+         */
+        if (!CurrentThreadCanAccessRuntime(runtime_)) {
+            JS_ASSERT(!edge.inRememberedSet(nursery_));
+            return false;
+        }
+
+        return true;
+    }
+
+    template <typename Buffer, typename Edge>
+    void put(Buffer &buffer, const Edge &edge) {
+        if (!isOkayToUseBuffer(edge))
+            return;
+        mozilla::ReentrancyGuard g(*this);
+        if (edge.inRememberedSet(nursery_))
+            buffer.put(this, edge);
+    }
+
+    template <typename Buffer, typename Edge>
+    void unput(Buffer &buffer, const Edge &edge) {
+        if (!isOkayToUseBuffer(edge))
+            return;
+        mozilla::ReentrancyGuard g(*this);
+        buffer.unput(this, edge);
+    }
+
     MonoTypeBuffer<ValueEdge> bufferVal;
     MonoTypeBuffer<CellPtrEdge> bufferCell;
     MonoTypeBuffer<SlotEdge> bufferSlot;
     MonoTypeBuffer<WholeCellEdges> bufferWholeCell;
     RelocatableMonoTypeBuffer<ValueEdge> bufferRelocVal;
     RelocatableMonoTypeBuffer<CellPtrEdge> bufferRelocCell;
     GenericBuffer bufferGeneric;
 
-    /* This set is used as temporary storage by the buffers when compacting. */
-    EdgeSet edgeSet;
-
-    JSRuntime *runtime;
+    JSRuntime *runtime_;
     const Nursery &nursery_;
 
-    bool aboutToOverflow;
-    bool enabled;
+    bool aboutToOverflow_;
+    bool enabled_;
+    mozilla::DebugOnly<bool> entered; /* For ReentrancyGuard. */
 
   public:
     explicit StoreBuffer(JSRuntime *rt, const Nursery &nursery)
-      : bufferVal(this), bufferCell(this), bufferSlot(this), bufferWholeCell(this),
-        bufferRelocVal(this), bufferRelocCell(this), bufferGeneric(this),
-        runtime(rt), nursery_(nursery), aboutToOverflow(false), enabled(false)
+      : bufferVal(), bufferCell(), bufferSlot(), bufferWholeCell(),
+        bufferRelocVal(), bufferRelocCell(), bufferGeneric(),
+        runtime_(rt), nursery_(nursery), aboutToOverflow_(false), enabled_(false)
     {
-        edgeSet.init();
     }
 
     bool enable();
     void disable();
-    bool isEnabled() { return enabled; }
+    bool isEnabled() const { return enabled_; }
 
     bool clear();
 
     /* Get the overflowed status. */
-    bool isAboutToOverflow() const { return aboutToOverflow; }
+    bool isAboutToOverflow() const { return aboutToOverflow_; }
 
     /* Insert a single edge into the buffer/remembered set. */
-    void putValue(JS::Value *valuep) {
-        ValueEdge edge(valuep);
-        if (!edge.inRememberedSet(nursery_))
-            return;
-        bufferVal.put(edge);
-    }
-    void putCell(Cell **cellp) {
-        CellPtrEdge edge(cellp);
-        if (!edge.inRememberedSet(nursery_))
-            return;
-        bufferCell.put(edge);
-    }
+    void putValue(JS::Value *valuep) { put(bufferVal, ValueEdge(valuep)); }
+    void putCell(Cell **cellp) { put(bufferCell, CellPtrEdge(cellp)); }
     void putSlot(JSObject *obj, int kind, uint32_t slot, void *target) {
-        SlotEdge edge(obj, kind, slot);
-        /* This is manually inlined because slotLocation cannot be defined here. */
-        if (nursery_.isInside(obj) || !nursery_.isInside(target))
-            return;
-        bufferSlot.put(edge);
+        put(bufferSlot, SlotEdge(obj, kind, slot));
     }
     void putWholeCell(Cell *cell) {
-        bufferWholeCell.put(WholeCellEdges(cell));
+        JS_ASSERT(cell->isTenured());
+        put(bufferWholeCell, WholeCellEdges(cell));
     }
 
     /* Insert or update a single edge in the Relocatable buffer. */
-    void putRelocatableValue(JS::Value *valuep) {
-        ValueEdge edge(valuep);
-        if (!edge.inRememberedSet(nursery_))
-            return;
-        bufferRelocVal.put(edge);
-    }
-    void putRelocatableCell(Cell **cellp) {
-        CellPtrEdge edge(cellp);
-        if (!edge.inRememberedSet(nursery_))
-            return;
-        bufferRelocCell.put(edge);
-    }
-    void removeRelocatableValue(JS::Value *valuep) {
-        ValueEdge edge(valuep);
-        bufferRelocVal.unput(edge);
-    }
-    void removeRelocatableCell(Cell **cellp) {
-        CellPtrEdge edge(cellp);
-        bufferRelocCell.unput(edge);
-    }
+    void putRelocatableValue(JS::Value *valuep) { put(bufferRelocVal, ValueEdge(valuep)); }
+    void putRelocatableCell(Cell **cellp) { put(bufferRelocCell, CellPtrEdge(cellp)); }
+    void removeRelocatableValue(JS::Value *valuep) { unput(bufferRelocVal, ValueEdge(valuep)); }
+    void removeRelocatableCell(Cell **cellp) { unput(bufferRelocCell, CellPtrEdge(cellp)); }
 
     /* Insert an entry into the generic buffer. */
     template <typename T>
-    void putGeneric(const T &t) {
-        bufferGeneric.put(t);
-    }
+    void putGeneric(const T &t) { put(bufferGeneric, t);}
 
     /* Insert or update a callback entry. */
     void putCallback(CallbackRef::MarkCallback callback, Cell *key, void *data) {
-        if (nursery_.isInside(key))
-            bufferGeneric.put(CallbackRef(callback, key, data));
+        put(bufferGeneric, CallbackRef(callback, key, data));
     }
 
     /* Mark the source of all edges in the store buffer. */
     void mark(JSTracer *trc);
 
     /* We cannot call InParallelSection directly because of a circular dependency. */
     bool inParallelSection() const;