Bug 1537909 - Optimise external read barriers r=sfink?
authorJon Coppeard <jcoppeard@mozilla.com>
Mon, 20 May 2019 17:58:16 +0000
changeset 474686 4166b94f0df55a8080998642b8c2b958308c91ff
parent 474685 441a4c70ac221317eaf998f43dc8b2cdcb06cd2b
child 474687 acf1896be423cca5678a9f10dcd793974b204231
push id113168
push userrmaries@mozilla.com
push dateTue, 21 May 2019 16:39:23 +0000
treeherdermozilla-inbound@3c0f78074b72 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssfink
bugs1537909
milestone69.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 1537909 - Optimise external read barriers r=sfink? Add a special path for the external read barrier API where we inline most of the checks and then always perform the barrier if we call into the engine. This also skips dispatching on trace kind since we know the barrier tracer is always a GCMarker. This is kind of hacky and I'm not sure how much it gains us (it's difficult to tell in profiles where GC may occur at different times). What do you think? Differential Revision: https://phabricator.services.mozilla.com/D31803
js/public/HeapAPI.h
js/src/gc/GC.cpp
js/src/gc/Marking.cpp
--- a/js/public/HeapAPI.h
+++ b/js/public/HeapAPI.h
@@ -547,34 +547,30 @@ extern JS_PUBLIC_API bool IsIncrementalB
 extern JS_PUBLIC_API void IncrementalPreWriteBarrier(JSObject* obj);
 
 /*
  * Notify the GC that a reference to a tenured GC cell is about to be
  * overwritten. This method must be called if IsIncrementalBarrierNeeded.
  */
 extern JS_PUBLIC_API void IncrementalPreWriteBarrier(GCCellPtr thing);
 
-/*
- * Notify the GC that a weak reference to a GC thing has been read.
- * This method must be called if IsIncrementalBarrierNeeded.
- */
-extern JS_PUBLIC_API void IncrementalReadBarrier(GCCellPtr thing);
-
 /**
  * Unsets the gray bit for anything reachable from |thing|. |kind| should not be
  * JS::TraceKind::Shape. |thing| should be non-null. The return value indicates
  * if anything was unmarked.
  */
 extern JS_FRIEND_API bool UnmarkGrayGCThingRecursively(GCCellPtr thing);
 
 }  // namespace JS
 
 namespace js {
 namespace gc {
 
+extern JS_PUBLIC_API void PerformIncrementalReadBarrier(JS::GCCellPtr thing);
+
 static MOZ_ALWAYS_INLINE bool IsIncrementalBarrierNeededOnTenuredGCThing(
     const JS::GCCellPtr thing) {
   MOZ_ASSERT(thing);
   MOZ_ASSERT(!js::gc::IsInsideNursery(thing.asCell()));
 
   // TODO: I'd like to assert !RuntimeHeapIsBusy() here but this gets
   // called while we are tracing the heap, e.g. during memory reporting
   // (see bug 1313318).
@@ -594,22 +590,22 @@ static MOZ_ALWAYS_INLINE void ExposeGCTh
 
   // There's nothing to do for permanent GC things that might be owned by
   // another runtime.
   if (thing.mayBeOwnedByOtherRuntime()) {
     return;
   }
 
   if (IsIncrementalBarrierNeededOnTenuredGCThing(thing)) {
-    JS::IncrementalReadBarrier(thing);
-  } else if (js::gc::detail::TenuredCellIsMarkedGray(thing.asCell())) {
+    PerformIncrementalReadBarrier(thing);
+  } else if (detail::TenuredCellIsMarkedGray(thing.asCell())) {
     JS::UnmarkGrayGCThingRecursively(thing);
   }
 
-  MOZ_ASSERT(!js::gc::detail::TenuredCellIsMarkedGray(thing.asCell()));
+  MOZ_ASSERT(!detail::TenuredCellIsMarkedGray(thing.asCell()));
 }
 
 template <typename T>
 extern JS_PUBLIC_API bool EdgeNeedsSweepUnbarrieredSlow(T* thingp);
 
 static MOZ_ALWAYS_INLINE bool EdgeNeedsSweepUnbarriered(JSObject** objp) {
   // This function does not handle updating nursery pointers. Raw JSObject
   // pointers should be updated separately or replaced with
@@ -641,15 +637,16 @@ namespace JS {
  */
 static MOZ_ALWAYS_INLINE void ExposeObjectToActiveJS(JSObject* obj) {
   MOZ_ASSERT(obj);
   MOZ_ASSERT(!js::gc::EdgeNeedsSweepUnbarrieredSlow(&obj));
   js::gc::ExposeGCThingToActiveJS(GCCellPtr(obj));
 }
 
 static MOZ_ALWAYS_INLINE void ExposeScriptToActiveJS(JSScript* script) {
+  MOZ_ASSERT(script);
   MOZ_ASSERT(!js::gc::EdgeNeedsSweepUnbarrieredSlow(&script));
   js::gc::ExposeGCThingToActiveJS(GCCellPtr(script));
 }
 
 } /* namespace JS */
 
 #endif /* js_HeapAPI_h */
--- a/js/src/gc/GC.cpp
+++ b/js/src/gc/GC.cpp
@@ -8742,25 +8742,16 @@ JS_PUBLIC_API void JS::IncrementalPreWri
 JS_PUBLIC_API void JS::IncrementalPreWriteBarrier(GCCellPtr thing) {
   if (!thing) {
     return;
   }
 
   TenuredCell::writeBarrierPre(&thing.asCell()->asTenured());
 }
 
-JS_PUBLIC_API void JS::IncrementalReadBarrier(GCCellPtr thing) {
-  if (!thing) {
-    return;
-  }
-
-  MOZ_ASSERT(!JS::RuntimeHeapIsMajorCollecting());
-  ApplyGCThingTyped(thing, [](auto t) { t->readBarrier(t); });
-}
-
 JS_PUBLIC_API bool JS::WasIncrementalGC(JSRuntime* rt) {
   return rt->gc.isIncrementalGc();
 }
 
 uint64_t js::gc::NextCellUniqueId(JSRuntime* rt) {
   return rt->gc.nextCellUniqueId();
 }
 
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -733,16 +733,40 @@ void DoMarking(GCMarker* gcmarker, T* th
   SetMaybeAliveFlag(thing);
 }
 
 template <typename T>
 void DoMarking(GCMarker* gcmarker, const T& thing) {
   ApplyGCThingTyped(thing, [gcmarker](auto t) { DoMarking(gcmarker, t); });
 }
 
+JS_PUBLIC_API void js::gc::PerformIncrementalReadBarrier(JS::GCCellPtr thing) {
+  // Optimized marking for read barriers. This is called from
+  // ExposeGCThingToActiveJS which has already checked the prerequisites for
+  // performing a read barrier. This means we can skip a bunch of checks and
+  // call info the tracer directly.
+
+  MOZ_ASSERT(thing);
+  MOZ_ASSERT(!JS::RuntimeHeapIsMajorCollecting());
+
+  TenuredCell* cell = &thing.asCell()->asTenured();
+  Zone* zone = cell->zone();
+  MOZ_ASSERT(zone->needsIncrementalBarrier());
+
+  // Skip disptaching on known tracer type.
+  GCMarker* gcmarker = GCMarker::fromTracer(zone->barrierTracer());
+
+  // Mark the argument, as DoMarking above.
+  ApplyGCThingTyped(thing, [gcmarker](auto thing) {
+                             MOZ_ASSERT(ShouldMark(gcmarker, thing));
+                             CheckTracedThing(gcmarker, thing);
+                             gcmarker->traverse(thing);
+                           });
+}
+
 template <typename T>
 void NoteWeakEdge(GCMarker* gcmarker, T** thingp) {
   // Do per-type marking precondition checks.
   if (!ShouldMark(gcmarker, *thingp)) {
     return;
   }
 
   CheckTracedThing(gcmarker, *thingp);