Bug 1096026 - Fix bug 1096026, r=terrence a=lmandel
authorBrian Hackett <bhackett1024@gmail.com>
Tue, 18 Nov 2014 08:33:01 -0700
changeset 233958 b34fe7ba8f13ccce223fb8afa1737dd6813cb038
parent 233957 3e38373768ad6cac30296dd363ea50a167c9e344
child 233959 776650aa7757bb1dba0c505ec0792cf14da77f8a
push id4187
push userbhearsum@mozilla.com
push dateFri, 28 Nov 2014 15:29:12 +0000
treeherdermozilla-beta@f23cc6a30c11 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersterrence, lmandel
bugs1096026
milestone35.0a2
Bug 1096026 - Fix bug 1096026, r=terrence a=lmandel
js/src/gc/Nursery.cpp
js/src/gc/Nursery.h
--- a/js/src/gc/Nursery.cpp
+++ b/js/src/gc/Nursery.cpp
@@ -425,24 +425,25 @@ js::Nursery::setSlotsForwardingPointer(H
     MOZ_ASSERT(!isInside(newSlots));
     *reinterpret_cast<HeapSlot **>(oldSlots) = newSlots;
 }
 
 void
 js::Nursery::setElementsForwardingPointer(ObjectElements *oldHeader, ObjectElements *newHeader,
                                           uint32_t nelems)
 {
-    /*
-     * If the JIT has hoisted a zero length pointer, then we do not need to
-     * relocate it because reads and writes to/from this pointer are invalid.
-     */
-    if (nelems - ObjectElements::VALUES_PER_HEADER < 1)
-        return;
     MOZ_ASSERT(isInside(oldHeader));
     MOZ_ASSERT(!isInside(newHeader));
+    if (nelems - ObjectElements::VALUES_PER_HEADER < 1) {
+        if (!forwardedBuffers.initialized() && !forwardedBuffers.init())
+            CrashAtUnhandlableOOM("Nursery::setElementsForwardingPointer");
+        if (!forwardedBuffers.put(oldHeader->elements(), newHeader->elements()))
+            CrashAtUnhandlableOOM("Nursery::setElementsForwardingPointer");
+        return;
+    }
     *reinterpret_cast<HeapSlot **>(oldHeader->elements()) = newHeader->elements();
 }
 
 #ifdef DEBUG
 static bool IsWriteableAddress(void *ptr)
 {
     volatile uint64_t *vPtr = reinterpret_cast<volatile uint64_t *>(ptr);
     *vPtr = *vPtr;
@@ -453,25 +454,29 @@ static bool IsWriteableAddress(void *ptr
 void
 js::Nursery::forwardBufferPointer(HeapSlot **pSlotsElems)
 {
     HeapSlot *old = *pSlotsElems;
 
     if (!isInside(old))
         return;
 
-    /*
-     * If the elements buffer is zero length, the "first" item could be inside
-     * of the next object or past the end of the allocable area.  However,
-     * since we always store the runtime as the last word in the nursery,
-     * isInside will still be true, even if this zero-size allocation abuts the
-     * end of the allocable area. Thus, it is always safe to read the first
-     * word of |old| here.
-     */
-    *pSlotsElems = *reinterpret_cast<HeapSlot **>(old);
+    // The new location for this buffer is either stored inline with it or in
+    // the forwardedBuffers table.
+    do {
+        if (forwardedBuffers.initialized()) {
+            if (ForwardedBufferMap::Ptr p = forwardedBuffers.lookup(old)) {
+                *pSlotsElems = reinterpret_cast<HeapSlot *>(p->value());
+                break;
+            }
+        }
+
+        *pSlotsElems = *reinterpret_cast<HeapSlot **>(old);
+    } while (false);
+
     MOZ_ASSERT(!isInside(*pSlotsElems));
     MOZ_ASSERT(IsWriteableAddress(*pSlotsElems));
 }
 
 // Structure for counting how many times objects of a particular type have been
 // tenured during a minor collection.
 struct TenureCount
 {
@@ -826,16 +831,17 @@ js::Nursery::collect(JSRuntime *rt, JS::
         if (c->innerViews.needsSweepAfterMinorGC())
             c->innerViews.sweepAfterMinorGC(rt);
     }
     TIME_END(sweepArrayBufferViewList);
 
     // Update any slot or element pointers whose destination has been tenured.
     TIME_START(updateJitActivations);
     js::jit::UpdateJitActivationsForMinorGC<Nursery>(&rt->mainThread, &trc);
+    forwardedBuffers.finish();
     TIME_END(updateJitActivations);
 
     // Resize the nursery.
     TIME_START(resize);
     double promotionRate = trc.tenuredSize / double(allocationEnd() - start());
     if (promotionRate > 0.05)
         growAllocableSpace();
     else if (promotionRate < 0.01)
--- a/js/src/gc/Nursery.h
+++ b/js/src/gc/Nursery.h
@@ -200,16 +200,26 @@ class Nursery
     /*
      * The set of externally malloced slots potentially kept live by objects
      * stored in the nursery. Any external slots that do not belong to a
      * tenured thing at the end of a minor GC must be freed.
      */
     typedef HashSet<HeapSlot *, PointerHasher<HeapSlot *, 3>, SystemAllocPolicy> HugeSlotsSet;
     HugeSlotsSet hugeSlots;
 
+    /*
+     * During a collection most hoisted slot and element buffers indicate their
+     * new location with a forwarding pointer at the base. This does not work
+     * for buffers whose length is less than pointer width, or when different
+     * buffers might overlap each other. For these, an entry in the following
+     * table is used.
+     */
+    typedef HashMap<void *, void *, PointerHasher<void *, 1>, SystemAllocPolicy> ForwardedBufferMap;
+    ForwardedBufferMap forwardedBuffers;
+
     /* The maximum number of slots allowed to reside inline in the nursery. */
     static const size_t MaxNurserySlots = 128;
 
     /* The amount of space in the mapped nursery available to allocations. */
     static const size_t NurseryChunkUsableSize = gc::ChunkSize - sizeof(gc::ChunkTrailer);
 
     struct NurseryChunkLayout {
         char data[NurseryChunkUsableSize];