Bug 1266457 - Update pointers in GC things in two phases when compacting r=terrence
authorJon Coppeard <jcoppeard@mozilla.com>
Thu, 28 Apr 2016 14:25:05 +0100
changeset 295336 a0f19ca7386dc013a93d4e32fdd17d054f1bfede
parent 295335 4fc2268a06becd6eb9d501a9398478642bfac673
child 295337 0e01363bf4fd98972b07fde916d8c15012de3231
push id19006
push userkwierso@gmail.com
push dateFri, 29 Apr 2016 23:06:04 +0000
treeherderfx-team@8471d2d75bf4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersterrence
bugs1266457
milestone49.0a1
Bug 1266457 - Update pointers in GC things in two phases when compacting r=terrence
js/src/gc/GCRuntime.h
js/src/jsgc.cpp
--- a/js/src/gc/GCRuntime.h
+++ b/js/src/gc/GCRuntime.h
@@ -3,16 +3,17 @@
  * 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/. */
 
 #ifndef gc_GCRuntime_h
 #define gc_GCRuntime_h
 
 #include "mozilla/Atomics.h"
+#include "mozilla/EnumSet.h"
 
 #include "jsfriendapi.h"
 #include "jsgc.h"
 
 #include "gc/Heap.h"
 #include "gc/Nursery.h"
 #include "gc/Statistics.h"
 #include "gc/StoreBuffer.h"
@@ -572,16 +573,18 @@ class ChainedIter
     }
 
     operator T() const { return get(); }
     T operator->() const { return get(); }
 };
 
 typedef HashMap<Value*, const char*, DefaultHasher<Value*>, SystemAllocPolicy> RootedValueMap;
 
+using AllocKinds = mozilla::EnumSet<AllocKind>;
+
 class GCRuntime
 {
   public:
     explicit GCRuntime(JSRuntime* rt);
     bool init(uint32_t maxbytes, uint32_t maxNurseryBytes);
     void finishRoots();
     void finish();
 
@@ -965,16 +968,17 @@ class GCRuntime
     void beginCompactPhase();
     IncrementalProgress compactPhase(JS::gcreason::Reason reason, SliceBudget& sliceBudget);
     void endCompactPhase(JS::gcreason::Reason reason);
     void sweepTypesAfterCompacting(Zone* zone);
     void sweepZoneAfterCompacting(Zone* zone);
     bool relocateArenas(Zone* zone, JS::gcreason::Reason reason, Arena*& relocatedListOut,
                         SliceBudget& sliceBudget);
     void updateTypeDescrObjects(MovingTracer* trc, Zone* zone);
+    void updateCellPointers(MovingTracer* trc, Zone* zone, AllocKinds kinds, size_t bgTaskCount);
     void updateAllCellPointers(MovingTracer* trc, Zone* zone);
     void updatePointersToRelocatedCells(Zone* zone);
     void protectAndHoldArenas(Arena* arenaList);
     void unprotectHeldRelocatedArenas();
     void releaseRelocatedArenas(Arena* arenaList);
     void releaseRelocatedArenasWithoutUnlocking(Arena* arenaList, const AutoLockGC& lock);
     void finishCollection(JS::gcreason::Reason reason);
 
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -177,17 +177,16 @@
  *  - The contents of those arenas are moved to new arenas.
  *  - All references to moved things are updated.
  */
 
 #include "jsgcinlines.h"
 
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/DebugOnly.h"
-#include "mozilla/EnumSet.h"
 #include "mozilla/MacroForEach.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/Move.h"
 
 #include <ctype.h>
 #include <string.h>
 #ifndef XP_WIN
 # include <sys/mman.h>
@@ -367,18 +366,16 @@ const uint32_t Arena::ThingsPerArena[] =
     COUNT(JSString),            /* AllocKind::STRING              */
     COUNT(JSExternalString),    /* AllocKind::EXTERNAL_STRING     */
     COUNT(JS::Symbol),          /* AllocKind::SYMBOL              */
     COUNT(jit::JitCode),        /* AllocKind::JITCODE             */
 };
 
 #undef COUNT
 
-using AllocKinds = mozilla::EnumSet<AllocKind>;
-
 struct js::gc::FinalizePhase
 {
     gcstats::Phase statsPhase;
     AllocKinds kinds;
 };
 
 /*
  * Finalization order for GC things swept incrementally on the main thrad.
@@ -2548,88 +2545,48 @@ namespace gc {
 struct ArenaListSegment
 {
     Arena* begin;
     Arena* end;
 };
 
 struct ArenasToUpdate
 {
-    enum UpdateKind {
-        NONE = 0,
-        FOREGROUND = 1,
-        BACKGROUND = 2,
-        ALL = FOREGROUND | BACKGROUND
-    };
-    ArenasToUpdate(Zone* zone, UpdateKind kind);
+    ArenasToUpdate(Zone* zone, AllocKinds kinds);
     bool done() { return kind == AllocKind::LIMIT; }
     ArenaListSegment getArenasToUpdate(AutoLockHelperThreadState& lock, unsigned maxLength);
 
   private:
-    UpdateKind kinds; // Selects which thing kinds to iterate
-    Zone* zone;          // Zone to process
-    AllocKind kind;      // Current alloc kind to process
-    Arena* arena;  // Next arena to process
+    AllocKinds kinds;  // Selects which thing kinds to update
+    Zone* zone;        // Zone to process
+    AllocKind kind;    // Current alloc kind to process
+    Arena* arena;      // Next arena to process
 
     AllocKind nextAllocKind(AllocKind i) { return AllocKind(uint8_t(i) + 1); }
-    UpdateKind updateKind(AllocKind kind);
     bool shouldProcessKind(AllocKind kind);
     Arena* next(AutoLockHelperThreadState& lock);
 };
 
-ArenasToUpdate::UpdateKind
-ArenasToUpdate::updateKind(AllocKind kind)
-{
-    MOZ_ASSERT(IsValidAllocKind(kind));
-
-    // GC things that do not contain pointers to cells we relocate don't need
-    // updating. Note that AllocKind::STRING is the only string kind which can
-    // be a rope and hence contain relocatable pointers.
-    if (kind == AllocKind::FAT_INLINE_STRING ||
-        kind == AllocKind::EXTERNAL_STRING ||
-        kind == AllocKind::SYMBOL)
-    {
-        return NONE;
-    }
-
-    // We try to update as many GC things in parallel as we can, but there are
-    // kinds for which this might not be safe:
-    //  - we assume JSObjects that are foreground finalized are not safe to
-    //    update in parallel
-    //  - updating a shape touches child shapes in fixupShapeTreeAfterMovingGC()
-    if (!js::gc::IsBackgroundFinalized(kind) || IsShapeAllocKind(kind))
-        return FOREGROUND;
-
-    return BACKGROUND;
-}
-
-bool
-ArenasToUpdate::shouldProcessKind(AllocKind kind)
-{
-    return (updateKind(kind) & kinds) != 0;
-}
-
-ArenasToUpdate::ArenasToUpdate(Zone* zone, UpdateKind kinds)
+ArenasToUpdate::ArenasToUpdate(Zone* zone, AllocKinds kinds)
   : kinds(kinds), zone(zone), kind(AllocKind::FIRST), arena(nullptr)
 {
     MOZ_ASSERT(zone->isGCCompacting());
-    MOZ_ASSERT(!(kinds & ~ALL));
 }
 
 Arena*
 ArenasToUpdate::next(AutoLockHelperThreadState& lock)
 {
     // Find the next arena to update.
     //
     // This iterates through the GC thing kinds filtered by shouldProcessKind(),
     // and then through thea arenas of that kind.  All state is held in the
     // object and we just return when we find an arena.
 
     for (; kind < AllocKind::LIMIT; kind = nextAllocKind(kind)) {
-        if (shouldProcessKind(kind)) {
+        if (kinds.contains(kind)) {
             if (!arena)
                 arena = zone->arenas.getFirstArena(kind);
             else
                 arena = arena->next;
             if (arena)
                 return arena;
         }
     }
@@ -2716,28 +2673,56 @@ CellUpdateBackgroundTaskCount()
 {
     if (!CanUseExtraThreads())
         return 0;
 
     size_t targetTaskCount = HelperThreadState().cpuCount / 2;
     return Min(Max(targetTaskCount, MinCellUpdateBackgroundTasks), MaxCellUpdateBackgroundTasks);
 }
 
-void
-GCRuntime::updateAllCellPointers(MovingTracer* trc, Zone* zone)
-{
-    AutoDisableProxyCheck noProxyCheck(rt); // These checks assert when run in parallel.
-
-    size_t bgTaskCount = CellUpdateBackgroundTaskCount();
-
-    ArenasToUpdate
-        fgArenas(zone, bgTaskCount == 0 ? ArenasToUpdate::ALL : ArenasToUpdate::FOREGROUND);
-    ArenasToUpdate
-        bgArenas(zone, bgTaskCount == 0 ? ArenasToUpdate::NONE : ArenasToUpdate::BACKGROUND);
-
+static bool
+CanUpdateKindInBackground(AllocKind kind) {
+    // We try to update as many GC things in parallel as we can, but there are
+    // kinds for which this might not be safe:
+    //  - we assume JSObjects that are foreground finalized are not safe to
+    //    update in parallel
+    //  - updating a shape touches child shapes in fixupShapeTreeAfterMovingGC()
+    if (!js::gc::IsBackgroundFinalized(kind) || IsShapeAllocKind(kind))
+        return false;
+
+    return true;
+}
+
+static AllocKinds
+ForegroundUpdateKinds(AllocKinds kinds)
+{
+    AllocKinds result;
+    for (AllocKind kind : kinds) {
+        if (!CanUpdateKindInBackground(kind))
+            result += kind;
+    }
+    return result;
+}
+
+void
+GCRuntime::updateTypeDescrObjects(MovingTracer* trc, Zone* zone)
+{
+    zone->typeDescrObjects.sweep();
+    for (auto r = zone->typeDescrObjects.all(); !r.empty(); r.popFront())
+        UpdateCellPointers(trc, r.front().get());
+}
+
+void
+GCRuntime::updateCellPointers(MovingTracer* trc, Zone* zone, AllocKinds kinds, size_t bgTaskCount)
+{
+    AllocKinds fgKinds = bgTaskCount == 0 ? kinds : ForegroundUpdateKinds(kinds);
+    AllocKinds bgKinds = kinds - fgKinds;
+
+    ArenasToUpdate fgArenas(zone, fgKinds);
+    ArenasToUpdate bgArenas(zone, bgKinds);
     Maybe<UpdatePointersTask> fgTask;
     Maybe<UpdatePointersTask> bgTasks[MaxCellUpdateBackgroundTasks];
 
     size_t tasksStarted = 0;
 
     {
         AutoLockHelperThreadState lock;
 
@@ -2755,22 +2740,58 @@ GCRuntime::updateAllCellPointers(MovingT
     {
         AutoLockHelperThreadState lock;
 
         for (size_t i = 0; i < tasksStarted; i++)
             joinTask(*bgTasks[i], gcstats::PHASE_COMPACT_UPDATE_CELLS);
     }
 }
 
-void
-GCRuntime::updateTypeDescrObjects(MovingTracer* trc, Zone* zone)
-{
-    zone->typeDescrObjects.sweep();
-    for (auto r = zone->typeDescrObjects.all(); !r.empty(); r.popFront())
-        UpdateCellPointers(trc, r.front().get());
+static const AllocKinds UpdatePhaseMisc {
+    AllocKind::SCRIPT,
+    AllocKind::LAZY_SCRIPT,
+    AllocKind::SHAPE,
+    AllocKind::BASE_SHAPE,
+    AllocKind::ACCESSOR_SHAPE,
+    AllocKind::OBJECT_GROUP,
+    AllocKind::STRING,
+    AllocKind::JITCODE
+};
+
+static const AllocKinds UpdatePhaseObjects {
+    AllocKind::FUNCTION,
+    AllocKind::FUNCTION_EXTENDED,
+    AllocKind::OBJECT0,
+    AllocKind::OBJECT0_BACKGROUND,
+    AllocKind::OBJECT2,
+    AllocKind::OBJECT2_BACKGROUND,
+    AllocKind::OBJECT4,
+    AllocKind::OBJECT4_BACKGROUND,
+    AllocKind::OBJECT8,
+    AllocKind::OBJECT8_BACKGROUND,
+    AllocKind::OBJECT12,
+    AllocKind::OBJECT12_BACKGROUND,
+    AllocKind::OBJECT16,
+    AllocKind::OBJECT16_BACKGROUND
+};
+
+void
+GCRuntime::updateAllCellPointers(MovingTracer* trc, Zone* zone)
+{
+    AutoDisableProxyCheck noProxyCheck(rt); // These checks assert when run in parallel.
+
+    size_t bgTaskCount = CellUpdateBackgroundTaskCount();
+
+    updateCellPointers(trc, zone, UpdatePhaseMisc, bgTaskCount);
+
+    // Update TypeDescrs before all other objects as typed objects access these
+    // objects when we trace them.
+    updateTypeDescrObjects(trc, zone);
+
+    updateCellPointers(trc, zone, UpdatePhaseObjects, bgTaskCount);
 }
 
 /*
  * Update pointers to relocated cells by doing a full heap traversal and sweep.
  *
  * The latter is necessary to update weak references which are not marked as
  * part of the traversal.
  */
@@ -2784,20 +2805,16 @@ GCRuntime::updatePointersToRelocatedCell
     MovingTracer trc(rt);
 
     // Fixup compartment global pointers as these get accessed during marking.
     for (CompartmentsInZoneIter comp(zone); !comp.done(); comp.next())
         comp->fixupAfterMovingGC();
     JSCompartment::fixupCrossCompartmentWrappersAfterMovingGC(&trc);
     rt->spsProfiler.fixupStringsMapAfterMovingGC();
 
-    // Update TypeDescrs before all other objects as typed objects access these
-    // objects when we trace them.
-    updateTypeDescrObjects(&trc, zone);
-
     // Iterate through all cells that can contain relocatable pointers to update
     // them. Since updating each cell is independent we try to parallelize this
     // as much as possible.
     updateAllCellPointers(&trc, zone);
 
     // Mark roots to update them.
     {
         markRuntime(&trc, MarkRuntime);