Bug 1162303 - Simplify TenuringTracer's implementation; r=jonco
authorTerrence Cole <terrence@mozilla.com>
Thu, 07 May 2015 10:17:45 -0700
changeset 272882 2993c8d2fbb1c01b178065fc24859d9e12979e45
parent 272881 cbfddea9ef08e966db91eca85a5ddf71adf2b07e
child 272883 428de38c2edc791d5eaa39d1f293fc2d17a9c37b
push id4830
push userjlund@mozilla.com
push dateMon, 29 Jun 2015 20:18:48 +0000
treeherdermozilla-beta@4c2175bb0420 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjonco
bugs1162303
milestone40.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 1162303 - Simplify TenuringTracer's implementation; r=jonco
js/src/gc/Barrier.h
js/src/gc/Marking.cpp
js/src/gc/Nursery.h
--- a/js/src/gc/Barrier.h
+++ b/js/src/gc/Barrier.h
@@ -806,43 +806,29 @@ class HeapSlot : public BarrieredBase<Va
         post(owner, kind, slot, v);
     }
 
     /* For users who need to manually barrier the raw types. */
     static void writeBarrierPost(NativeObject* owner, Kind kind, uint32_t slot, const Value& target) {
         reinterpret_cast<HeapSlot*>(const_cast<Value*>(&target))->post(owner, kind, slot, target);
     }
 
+    Value* unsafeGet() { return &value; }
+
   private:
     void post(NativeObject* owner, Kind kind, uint32_t slot, const Value& target) {
         MOZ_ASSERT(preconditionForWriteBarrierPost(owner, kind, slot, target));
         if (this->value.isObject()) {
             gc::Cell* cell = reinterpret_cast<gc::Cell*>(&this->value.toObject());
             if (cell->storeBuffer())
                 cell->storeBuffer()->putSlotFromAnyThread(owner, kind, slot, 1);
         }
     }
 };
 
-static inline const Value*
-Valueify(const BarrieredBase<Value>* array)
-{
-    JS_STATIC_ASSERT(sizeof(HeapValue) == sizeof(Value));
-    JS_STATIC_ASSERT(sizeof(HeapSlot) == sizeof(Value));
-    return (const Value*)array;
-}
-
-static inline HeapValue*
-HeapValueify(Value* v)
-{
-    JS_STATIC_ASSERT(sizeof(HeapValue) == sizeof(Value));
-    JS_STATIC_ASSERT(sizeof(HeapSlot) == sizeof(Value));
-    return (HeapValue*)v;
-}
-
 class HeapSlotArray
 {
     HeapSlot* array;
 
     // Whether writes may be performed to the slots in this array. This helps
     // to control how object elements which may be copy on write are used.
 #ifdef DEBUG
     bool allowWrite_;
@@ -851,17 +837,21 @@ class HeapSlotArray
   public:
     explicit HeapSlotArray(HeapSlot* array, bool allowWrite)
       : array(array)
 #ifdef DEBUG
       , allowWrite_(allowWrite)
 #endif
     {}
 
-    operator const Value*() const { return Valueify(array); }
+    operator const Value*() const {
+        JS_STATIC_ASSERT(sizeof(HeapValue) == sizeof(Value));
+        JS_STATIC_ASSERT(sizeof(HeapSlot) == sizeof(Value));
+        return reinterpret_cast<const Value*>(array);
+    }
     operator HeapSlot*() const { MOZ_ASSERT(allowWrite()); return array; }
 
     HeapSlotArray operator +(int offset) const { return HeapSlotArray(array + offset, allowWrite()); }
     HeapSlotArray operator +(uint32_t offset) const { return HeapSlotArray(array + offset, allowWrite()); }
 
   private:
     bool allowWrite() const {
 #ifdef DEBUG
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -423,17 +423,16 @@ template <typename T>
 typename PtrBaseGCType<T>::type*
 ConvertToBase(T* thingp)
 {
     return reinterpret_cast<typename PtrBaseGCType<T>::type*>(thingp);
 }
 
 template <typename T> void DispatchToTracer(JSTracer* trc, T* thingp, const char* name);
 template <typename T> T DoCallback(JS::CallbackTracer* trc, T* thingp, const char* name);
-template <typename T> void DoTenuring(TenuringTracer& mover, T* thingp);
 template <typename T> void DoMarking(GCMarker* gcmarker, T thing);
 
 template <typename T>
 void
 js::TraceEdge(JSTracer* trc, BarrieredBase<T>* thingp, const char* name)
 {
     DispatchToTracer(trc, ConvertToBase(thingp->unsafeGet()), name);
 }
@@ -593,17 +592,17 @@ DispatchToTracer(JSTracer* trc, T* thing
             FOR_EACH_GC_LAYOUT(IS_SAME_TYPE_OR)
             mozilla::IsSame<T, JS::Value>::value ||
             mozilla::IsSame<T, jsid>::value,
             "Only the base cell layout types are allowed into marking/tracing internals");
 #undef IS_SAME_TYPE_OR
     if (trc->isMarkingTracer())
         return DoMarking(static_cast<GCMarker*>(trc), *thingp);
     if (trc->isTenuringTracer())
-        return DoTenuring(*static_cast<TenuringTracer*>(trc), thingp);
+        return static_cast<TenuringTracer*>(trc)->traverse(thingp);
     MOZ_ASSERT(trc->isCallbackTracer());
     DoCallback(trc->asCallbackTracer(), thingp, name);
 }
 
 
 /*** GC Marking Interface *************************************************************************/
 
 template <typename T>
@@ -1720,55 +1719,50 @@ GCMarker::sizeOfExcludingThis(mozilla::M
     for (ZonesIter zone(runtime(), WithAtoms); !zone.done(); zone.next())
         size += zone->gcGrayRoots.sizeOfExcludingThis(mallocSizeOf);
     return size;
 }
 
 
 /*** Tenuring Tracer *****************************************************************************/
 
-template <typename T>
+namespace js {
+template <>
 void
-DoTenuring(TenuringTracer& mover, T* thingp)
+TenuringTracer::traverse(JSObject** objp)
 {
-    // Non-JSObject types are not in the nursery, so do not need to be tenured.
-    MOZ_ASSERT(!IsInsideNursery(*thingp));
+    // We only ever visit the internals of objects after moving them to tenured.
+    MOZ_ASSERT(!nursery().isInside(objp));
+
+    if (IsInsideNursery(*objp) && !nursery().getForwardedPointer(objp))
+        *objp = moveToTenured(*objp);
 }
 
 template <>
 void
-DoTenuring(TenuringTracer& mover, JSObject** objp)
+TenuringTracer::traverse(Value* valp)
 {
-    // Only roots and store buffer entries should be marked via this path; all
-    // internal pointers are marked via collectToFixedPoint.
-    MOZ_ASSERT(!mover.nursery().isInside(objp));
+    if (!valp->isObject())
+        return;
 
-    if (IsInsideNursery(*objp) && !mover.nursery().getForwardedPointer(objp))
-        *objp = mover.moveToTenured(*objp);
+    JSObject *obj = &valp->toObject();
+    traverse(&obj);
+    valp->setObject(*obj);
 }
 
-template <>
-void
-DoTenuring<Value>(TenuringTracer& mover, Value* valp)
-{
-    if (valp->isObject()) {
-        JSObject *obj = &valp->toObject();
-        DoTenuring(mover, &obj);
-        valp->setObject(*obj);
-    } else {
-        MOZ_ASSERT_IF(valp->isMarkable(), !IsInsideNursery(valp->toGCThing()));
-    }
-}
-
-template <>
-void
-DoTenuring<jsid>(TenuringTracer& mover, jsid* idp)
-{
-    MOZ_ASSERT_IF(JSID_IS_GCTHING(*idp), !IsInsideNursery(JSID_TO_GCTHING(*idp).asCell()));
-}
+template <> void js::TenuringTracer::traverse(js::BaseShape**) {}
+template <> void js::TenuringTracer::traverse(js::jit::JitCode**) {}
+template <> void js::TenuringTracer::traverse(JSScript**) {}
+template <> void js::TenuringTracer::traverse(js::LazyScript**) {}
+template <> void js::TenuringTracer::traverse(js::Shape**) {}
+template <> void js::TenuringTracer::traverse(JSString**) {}
+template <> void js::TenuringTracer::traverse(JS::Symbol**) {}
+template <> void js::TenuringTracer::traverse(js::ObjectGroup**) {}
+template <> void js::TenuringTracer::traverse(jsid*) {}
+} // namespace js
 
 template <typename T>
 void
 js::gc::StoreBuffer::MonoTypeBuffer<T>::trace(StoreBuffer* owner, TenuringTracer& mover)
 {
     mozilla::ReentrancyGuard g(*owner);
     MOZ_ASSERT(owner->isEnabled());
     MOZ_ASSERT(stores_.initialized());
@@ -1833,24 +1827,24 @@ js::gc::StoreBuffer::WholeCellEdges::tra
 
 void
 js::gc::StoreBuffer::CellPtrEdge::trace(TenuringTracer& mover) const
 {
     if (!*edge)
         return;
 
     MOZ_ASSERT(GetGCThingTraceKind(*edge) == JSTRACE_OBJECT);
-    DoTenuring(mover, reinterpret_cast<JSObject**>(edge));
+    mover.traverse(reinterpret_cast<JSObject**>(edge));
 }
 
 void
 js::gc::StoreBuffer::ValueEdge::trace(TenuringTracer& mover) const
 {
     if (deref())
-        DoTenuring(mover, edge);
+        mover.traverse(edge);
 }
 
 /* Insert the given relocation entry into the list of things to visit. */
 void
 js::TenuringTracer::insertIntoFixupList(RelocationOverlay* entry) {
     *tail = entry;
     tail = &entry->next_;
     *tail = nullptr;
@@ -1918,117 +1912,89 @@ js::Nursery::collectToFixedPoint(Tenurin
         } else if (!entry.group) {
             entry.group = obj->group();
             entry.count = 1;
         }
     }
 }
 
 // Visit all object children of the object and trace them.
-MOZ_ALWAYS_INLINE void
+void
 js::TenuringTracer::traceObject(JSObject* obj)
 {
     const Class* clasp = obj->getClass();
     if (clasp->trace) {
         if (clasp->trace == InlineTypedObject::obj_trace) {
             TypeDescr* descr = &obj->as<InlineTypedObject>().typeDescr();
             if (descr->hasTraceList())
                 markTraceList(descr->traceList(), obj->as<InlineTypedObject>().inlineTypedMem());
             return;
         }
         if (clasp == &UnboxedPlainObject::class_) {
             JSObject** pexpando = obj->as<UnboxedPlainObject>().addressOfExpando();
             if (*pexpando)
-                markObject(pexpando);
+                traverse(pexpando);
             const UnboxedLayout& layout = obj->as<UnboxedPlainObject>().layoutDontCheckGeneration();
             if (layout.traceList())
                 markTraceList(layout.traceList(), obj->as<UnboxedPlainObject>().data());
             return;
         }
         clasp->trace(this, obj);
     }
 
     MOZ_ASSERT(obj->isNative() == clasp->isNative());
     if (!clasp->isNative())
         return;
     NativeObject* nobj = &obj->as<NativeObject>();
 
     // Note: the contents of copy on write elements pointers are filled in
     // during parsing and cannot contain nursery pointers.
-    if (!nobj->hasEmptyElements() && !nobj->denseElementsAreCopyOnWrite())
-        markSlots(nobj->getDenseElements(), nobj->getDenseInitializedLength());
+    if (!nobj->hasEmptyElements() && !nobj->denseElementsAreCopyOnWrite()) {
+        Value* elems = static_cast<HeapSlot*>(nobj->getDenseElements())->unsafeGet();
+        markSlots(elems, elems + nobj->getDenseInitializedLength());
+    }
 
     HeapSlot* fixedStart;
     HeapSlot* fixedEnd;
     HeapSlot* dynStart;
     HeapSlot* dynEnd;
     nobj->getSlotRange(0, nobj->slotSpan(), &fixedStart, &fixedEnd, &dynStart, &dynEnd);
-    markSlots(fixedStart, fixedEnd);
-    markSlots(dynStart, dynEnd);
+    markSlots(fixedStart->unsafeGet(), fixedEnd->unsafeGet());
+    markSlots(dynStart->unsafeGet(), dynEnd->unsafeGet());
 }
 
-MOZ_ALWAYS_INLINE void
-js::TenuringTracer::markSlots(HeapSlot* vp, uint32_t nslots)
-{
-    markSlots(vp, vp + nslots);
-}
-
-MOZ_ALWAYS_INLINE void
-js::TenuringTracer::markSlots(HeapSlot* vp, HeapSlot* end)
+void
+js::TenuringTracer::markSlots(Value* vp, Value* end)
 {
     for (; vp != end; ++vp)
-        markSlot(vp);
+        traverse(vp);
 }
 
-MOZ_ALWAYS_INLINE void
-js::TenuringTracer::markSlot(HeapSlot* slotp)
-{
-    if (!slotp->isObject())
-        return;
-
-    JSObject* obj = &slotp->toObject();
-    if (markObject(&obj))
-        slotp->unsafeGet()->setObject(*obj);
-}
-
-MOZ_ALWAYS_INLINE void
+void
 js::TenuringTracer::markTraceList(const int32_t* traceList, uint8_t* memory)
 {
     while (*traceList != -1) {
         // Strings are not in the nursery and do not need tracing.
         traceList++;
     }
     traceList++;
     while (*traceList != -1) {
-        JSObject** pobj = reinterpret_cast<JSObject **>(memory + *traceList);
-        markObject(pobj);
+        JSObject** pobj = reinterpret_cast<JSObject**>(memory + *traceList);
+        traverse(pobj);
         traceList++;
     }
     traceList++;
     while (*traceList != -1) {
-        HeapSlot* pslot = reinterpret_cast<HeapSlot *>(memory + *traceList);
-        markSlot(pslot);
+        Value* pslot = reinterpret_cast<Value*>(memory + *traceList);
+        traverse(pslot);
         traceList++;
     }
 }
 
-MOZ_ALWAYS_INLINE bool
-js::TenuringTracer::markObject(JSObject** pobj)
-{
-    if (!IsInsideNursery(*pobj))
-        return false;
-
-    if (nursery().getForwardedPointer(pobj))
-        return true;
-
-    *pobj = moveToTenured(*pobj);
-    return true;
-}
-
-MOZ_ALWAYS_INLINE size_t
+size_t
 js::TenuringTracer::moveObjectToTenured(JSObject* dst, JSObject* src, AllocKind dstKind)
 {
     size_t srcSize = Arena::thingSize(dstKind);
     size_t tenuredSize = srcSize;
 
     /*
      * Arrays do not necessarily have the same AllocKind between src and dst.
      * We deal with this by copying elements manually, possibly re-inlining
@@ -2064,17 +2030,17 @@ js::TenuringTracer::moveObjectToTenured(
         // Objects with JSCLASS_SKIP_NURSERY_FINALIZE need to be handled above
         // to ensure any additional nursery buffers they hold are moved.
         MOZ_ASSERT(!(src->getClass()->flags & JSCLASS_SKIP_NURSERY_FINALIZE));
     }
 
     return tenuredSize;
 }
 
-MOZ_ALWAYS_INLINE size_t
+size_t
 js::TenuringTracer::moveSlotsToTenured(NativeObject* dst, NativeObject* src, AllocKind dstKind)
 {
     /* Fixed slots have already been copied over. */
     if (!src->hasDynamicSlots())
         return 0;
 
     if (!nursery().isInside(src->slots_)) {
         nursery().removeMallocedBuffer(src->slots_);
@@ -2086,17 +2052,17 @@ js::TenuringTracer::moveSlotsToTenured(N
     dst->slots_ = zone->pod_malloc<HeapSlot>(count);
     if (!dst->slots_)
         CrashAtUnhandlableOOM("Failed to allocate slots while tenuring.");
     PodCopy(dst->slots_, src->slots_, count);
     nursery().setSlotsForwardingPointer(src->slots_, dst->slots_, count);
     return count * sizeof(HeapSlot);
 }
 
-MOZ_ALWAYS_INLINE size_t
+size_t
 js::TenuringTracer::moveElementsToTenured(NativeObject* dst, NativeObject* src, AllocKind dstKind)
 {
     if (src->hasEmptyElements() || src->denseElementsAreCopyOnWrite())
         return 0;
 
     Zone* zone = src->zone();
     ObjectElements* srcHeader = src->getElementsHeader();
     ObjectElements* dstHeader;
--- a/js/src/gc/Nursery.h
+++ b/js/src/gc/Nursery.h
@@ -62,32 +62,34 @@ class TenuringTracer : public JSTracer
     // Save and restore all of the runtime state we use during MinorGC.
     bool savedRuntimeNeedBarrier;
 
     TenuringTracer(JSRuntime* rt, Nursery* nursery);
     ~TenuringTracer();
 
   public:
     const Nursery& nursery() const { return nursery_; }
-    Nursery& nursery() { return nursery_; }
-    JSObject* moveToTenured(JSObject* src);
+
+    // Returns true if the pointer was updated.
+    template <typename T> void traverse(T* thingp);
 
     void insertIntoFixupList(gc::RelocationOverlay* entry);
 
   private:
+    Nursery& nursery() { return nursery_; }
+
+    JSObject* moveToTenured(JSObject* src);
     size_t moveObjectToTenured(JSObject* dst, JSObject* src, gc::AllocKind dstKind);
     size_t moveElementsToTenured(NativeObject* dst, NativeObject* src, gc::AllocKind dstKind);
     size_t moveSlotsToTenured(NativeObject* dst, NativeObject* src, gc::AllocKind dstKind);
 
-    MOZ_ALWAYS_INLINE void traceObject(JSObject* src);
-    MOZ_ALWAYS_INLINE void markSlots(HeapSlot* vp, uint32_t nslots);
-    MOZ_ALWAYS_INLINE void markSlots(HeapSlot* vp, HeapSlot* end);
-    MOZ_ALWAYS_INLINE void markSlot(HeapSlot* slotp);
-    MOZ_ALWAYS_INLINE void markTraceList(const int32_t* traceList, uint8_t* memory);
-    MOZ_ALWAYS_INLINE bool markObject(JSObject** pobj);
+    void traceObject(JSObject* src);
+    void markSlots(Value* vp, uint32_t nslots) { markSlots(vp, vp + nslots); }
+    void markSlots(Value* vp, Value* end);
+    void markTraceList(const int32_t* traceList, uint8_t* memory);
 };
 
 class Nursery
 {
   public:
     static const size_t Alignment = gc::ChunkSize;
     static const size_t ChunkShift = gc::ChunkShift;