Bug 1412912 - Split out AllocKinds.h and inline definitions in ArenaList.h r=sfink
authorJon Coppeard <jcoppeard@mozilla.com>
Wed, 01 Nov 2017 15:36:54 +0000
changeset 440412 dd1780b996ee9f4b919fc457ab1972b34360cee7
parent 440411 9715db005f4aa66acc7be3e77129a6d044f2697c
child 440413 45eedc2acce303672f97a8752bfccfff7e23d9a0
push id8114
push userjlorenzo@mozilla.com
push dateThu, 02 Nov 2017 16:33:21 +0000
treeherdermozilla-beta@73e0d89a540f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssfink
bugs1412912
milestone58.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 1412912 - Split out AllocKinds.h and inline definitions in ArenaList.h r=sfink
js/public/SliceBudget.h
js/src/gc/AllocKind.h
js/src/gc/ArenaList-inl.h
js/src/gc/ArenaList.h
js/src/gc/Heap.h
js/src/jsgcinlines.h
--- a/js/public/SliceBudget.h
+++ b/js/public/SliceBudget.h
@@ -4,16 +4,18 @@
  * 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 js_SliceBudget_h
 #define js_SliceBudget_h
 
 #include <stdint.h>
 
+#include "jstypes.h"
+
 namespace js {
 
 struct JS_PUBLIC_API(TimeBudget)
 {
     int64_t budget;
 
     explicit TimeBudget(int64_t milliseconds) { budget = milliseconds; }
 };
new file mode 100644
--- /dev/null
+++ b/js/src/gc/AllocKind.h
@@ -0,0 +1,197 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * 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/. */
+
+/*
+ * GC-internal definition of GC cell kinds.
+ */
+
+#ifndef gc_AllocKind_h
+#define gc_AllocKind_h
+
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/EnumeratedArray.h"
+#include "mozilla/EnumeratedRange.h"
+
+#include <stdint.h>
+
+#include "js/TraceKind.h"
+
+namespace js {
+namespace gc {
+
+/* The GC allocation kinds. */
+// FIXME: uint8_t would make more sense for the underlying type, but causes
+// miscompilations in GCC (fixed in 4.8.5 and 4.9.3). See also bug 1143966.
+enum class AllocKind {
+    FIRST,
+    OBJECT_FIRST = FIRST,
+    FUNCTION = FIRST,
+    FUNCTION_EXTENDED,
+    OBJECT0,
+    OBJECT0_BACKGROUND,
+    OBJECT2,
+    OBJECT2_BACKGROUND,
+    OBJECT4,
+    OBJECT4_BACKGROUND,
+    OBJECT8,
+    OBJECT8_BACKGROUND,
+    OBJECT12,
+    OBJECT12_BACKGROUND,
+    OBJECT16,
+    OBJECT16_BACKGROUND,
+    OBJECT_LIMIT,
+    OBJECT_LAST = OBJECT_LIMIT - 1,
+    SCRIPT,
+    LAZY_SCRIPT,
+    SHAPE,
+    ACCESSOR_SHAPE,
+    BASE_SHAPE,
+    OBJECT_GROUP,
+    FAT_INLINE_STRING,
+    STRING,
+    EXTERNAL_STRING,
+    FAT_INLINE_ATOM,
+    ATOM,
+    SYMBOL,
+    JITCODE,
+    SCOPE,
+    REGEXP_SHARED,
+    LIMIT,
+    LAST = LIMIT - 1
+};
+
+// Macro to enumerate the different allocation kinds supplying information about
+// the trace kind, C++ type and allocation size.
+#define FOR_EACH_OBJECT_ALLOCKIND(D) \
+ /* AllocKind              TraceKind      TypeName           SizedType */ \
+    D(FUNCTION,            Object,        JSObject,          JSFunction) \
+    D(FUNCTION_EXTENDED,   Object,        JSObject,          FunctionExtended) \
+    D(OBJECT0,             Object,        JSObject,          JSObject_Slots0) \
+    D(OBJECT0_BACKGROUND,  Object,        JSObject,          JSObject_Slots0) \
+    D(OBJECT2,             Object,        JSObject,          JSObject_Slots2) \
+    D(OBJECT2_BACKGROUND,  Object,        JSObject,          JSObject_Slots2) \
+    D(OBJECT4,             Object,        JSObject,          JSObject_Slots4) \
+    D(OBJECT4_BACKGROUND,  Object,        JSObject,          JSObject_Slots4) \
+    D(OBJECT8,             Object,        JSObject,          JSObject_Slots8) \
+    D(OBJECT8_BACKGROUND,  Object,        JSObject,          JSObject_Slots8) \
+    D(OBJECT12,            Object,        JSObject,          JSObject_Slots12) \
+    D(OBJECT12_BACKGROUND, Object,        JSObject,          JSObject_Slots12) \
+    D(OBJECT16,            Object,        JSObject,          JSObject_Slots16) \
+    D(OBJECT16_BACKGROUND, Object,        JSObject,          JSObject_Slots16)
+
+#define FOR_EACH_NONOBJECT_ALLOCKIND(D) \
+ /* AllocKind              TraceKind      TypeName           SizedType */ \
+    D(SCRIPT,              Script,        JSScript,          JSScript) \
+    D(LAZY_SCRIPT,         LazyScript,    js::LazyScript,    js::LazyScript) \
+    D(SHAPE,               Shape,         js::Shape,         js::Shape) \
+    D(ACCESSOR_SHAPE,      Shape,         js::AccessorShape, js::AccessorShape) \
+    D(BASE_SHAPE,          BaseShape,     js::BaseShape,     js::BaseShape) \
+    D(OBJECT_GROUP,        ObjectGroup,   js::ObjectGroup,   js::ObjectGroup) \
+    D(FAT_INLINE_STRING,   String,        JSFatInlineString, JSFatInlineString) \
+    D(STRING,              String,        JSString,          JSString) \
+    D(EXTERNAL_STRING,     String,        JSExternalString,  JSExternalString) \
+    D(FAT_INLINE_ATOM,     String,        js::FatInlineAtom, js::FatInlineAtom) \
+    D(ATOM,                String,        js::NormalAtom,    js::NormalAtom) \
+    D(SYMBOL,              Symbol,        JS::Symbol,        JS::Symbol) \
+    D(JITCODE,             JitCode,       js::jit::JitCode,  js::jit::JitCode) \
+    D(SCOPE,               Scope,         js::Scope,         js::Scope) \
+    D(REGEXP_SHARED,       RegExpShared,  js::RegExpShared,  js::RegExpShared)
+
+#define FOR_EACH_ALLOCKIND(D) \
+    FOR_EACH_OBJECT_ALLOCKIND(D) \
+    FOR_EACH_NONOBJECT_ALLOCKIND(D)
+
+static_assert(int(AllocKind::FIRST) == 0, "Various places depend on AllocKind starting at 0, "
+                                          "please audit them carefully!");
+static_assert(int(AllocKind::OBJECT_FIRST) == 0, "Various places depend on AllocKind::OBJECT_FIRST "
+                                                 "being 0, please audit them carefully!");
+
+inline bool
+IsAllocKind(AllocKind kind)
+{
+    return kind >= AllocKind::FIRST && kind <= AllocKind::LIMIT;
+}
+
+inline bool
+IsValidAllocKind(AllocKind kind)
+{
+    return kind >= AllocKind::FIRST && kind <= AllocKind::LAST;
+}
+
+inline bool
+IsObjectAllocKind(AllocKind kind)
+{
+    return kind >= AllocKind::OBJECT_FIRST && kind <= AllocKind::OBJECT_LAST;
+}
+
+inline bool
+IsShapeAllocKind(AllocKind kind)
+{
+    return kind == AllocKind::SHAPE || kind == AllocKind::ACCESSOR_SHAPE;
+}
+
+// Returns a sequence for use in a range-based for loop,
+// to iterate over all alloc kinds.
+inline decltype(mozilla::MakeEnumeratedRange(AllocKind::FIRST, AllocKind::LIMIT))
+AllAllocKinds()
+{
+    return mozilla::MakeEnumeratedRange(AllocKind::FIRST, AllocKind::LIMIT);
+}
+
+// Returns a sequence for use in a range-based for loop,
+// to iterate over all object alloc kinds.
+inline decltype(mozilla::MakeEnumeratedRange(AllocKind::OBJECT_FIRST, AllocKind::OBJECT_LIMIT))
+ObjectAllocKinds()
+{
+    return mozilla::MakeEnumeratedRange(AllocKind::OBJECT_FIRST, AllocKind::OBJECT_LIMIT);
+}
+
+// Returns a sequence for use in a range-based for loop,
+// to iterate over alloc kinds from |first| to |limit|, exclusive.
+inline decltype(mozilla::MakeEnumeratedRange(AllocKind::FIRST, AllocKind::LIMIT))
+SomeAllocKinds(AllocKind first = AllocKind::FIRST, AllocKind limit = AllocKind::LIMIT)
+{
+    MOZ_ASSERT(IsAllocKind(first), "|first| is not a valid AllocKind!");
+    MOZ_ASSERT(IsAllocKind(limit), "|limit| is not a valid AllocKind!");
+    return mozilla::MakeEnumeratedRange(first, limit);
+}
+
+// AllAllocKindArray<ValueType> gives an enumerated array of ValueTypes,
+// with each index corresponding to a particular alloc kind.
+template<typename ValueType> using AllAllocKindArray =
+    mozilla::EnumeratedArray<AllocKind, AllocKind::LIMIT, ValueType>;
+
+// ObjectAllocKindArray<ValueType> gives an enumerated array of ValueTypes,
+// with each index corresponding to a particular object alloc kind.
+template<typename ValueType> using ObjectAllocKindArray =
+    mozilla::EnumeratedArray<AllocKind, AllocKind::OBJECT_LIMIT, ValueType>;
+
+static inline JS::TraceKind
+MapAllocToTraceKind(AllocKind kind)
+{
+    static const JS::TraceKind map[] = {
+#define EXPAND_ELEMENT(allocKind, traceKind, type, sizedType) \
+        JS::TraceKind::traceKind,
+FOR_EACH_ALLOCKIND(EXPAND_ELEMENT)
+#undef EXPAND_ELEMENT
+    };
+
+    static_assert(MOZ_ARRAY_LENGTH(map) == size_t(AllocKind::LIMIT),
+                  "AllocKind-to-TraceKind mapping must be in sync");
+    return map[size_t(kind)];
+}
+
+/*
+ * This must be an upper bound, but we do not need the least upper bound, so
+ * we just exclude non-background objects.
+ */
+static const size_t MAX_BACKGROUND_FINALIZE_KINDS =
+    size_t(AllocKind::LIMIT) - size_t(AllocKind::OBJECT_LIMIT) / 2;
+
+} /* namespace gc */
+} /* namespace js */
+
+#endif /* gc_AllocKind_h */
new file mode 100644
--- /dev/null
+++ b/js/src/gc/ArenaList-inl.h
@@ -0,0 +1,353 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * 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_ArenaList_inl_h
+#define gc_ArenaList_inl_h
+
+#include "gc/ArenaList.h"
+
+#include "gc/Heap.h"
+
+void
+js::gc::SortedArenaListSegment::append(Arena* arena)
+{
+    MOZ_ASSERT(arena);
+    MOZ_ASSERT_IF(head, head->getAllocKind() == arena->getAllocKind());
+    *tailp = arena;
+    tailp = &arena->next;
+}
+
+inline
+js::gc::ArenaList::ArenaList()
+{
+    clear();
+}
+
+void
+js::gc::ArenaList::copy(const ArenaList& other)
+{
+    other.check();
+    head_ = other.head_;
+    cursorp_ = other.isCursorAtHead() ? &head_ : other.cursorp_;
+    check();
+}
+
+inline
+js::gc::ArenaList::ArenaList(const ArenaList& other)
+{
+    copy(other);
+}
+
+js::gc::ArenaList&
+js::gc::ArenaList::operator=(const ArenaList& other)
+{
+    copy(other);
+    return *this;
+}
+
+inline
+js::gc::ArenaList::ArenaList(const SortedArenaListSegment& segment)
+{
+    head_ = segment.head;
+    cursorp_ = segment.isEmpty() ? &head_ : segment.tailp;
+    check();
+}
+
+// This does checking just of |head_| and |cursorp_|.
+void
+js::gc::ArenaList::check() const
+{
+#ifdef DEBUG
+    // If the list is empty, it must have this form.
+    MOZ_ASSERT_IF(!head_, cursorp_ == &head_);
+
+    // If there's an arena following the cursor, it must not be full.
+    Arena* cursor = *cursorp_;
+    MOZ_ASSERT_IF(cursor, cursor->hasFreeThings());
+#endif
+}
+
+void
+js::gc::ArenaList::clear()
+{
+    head_ = nullptr;
+    cursorp_ = &head_;
+    check();
+}
+
+js::gc::ArenaList
+js::gc::ArenaList::copyAndClear()
+{
+    ArenaList result = *this;
+    clear();
+    return result;
+}
+
+bool
+js::gc::ArenaList::isEmpty() const
+{
+    check();
+    return !head_;
+}
+
+js::gc::Arena*
+js::gc::ArenaList::head() const
+{
+    check();
+    return head_;
+}
+
+bool
+js::gc::ArenaList::isCursorAtHead() const
+{
+    check();
+    return cursorp_ == &head_;
+}
+
+bool
+js::gc::ArenaList::isCursorAtEnd() const
+{
+    check();
+    return !*cursorp_;
+}
+
+void
+js::gc::ArenaList::moveCursorToEnd()
+{
+    while (!isCursorAtEnd())
+        cursorp_ = &(*cursorp_)->next;
+}
+
+js::gc::Arena*
+js::gc::ArenaList::arenaAfterCursor() const
+{
+    check();
+    return *cursorp_;
+}
+
+js::gc::Arena*
+js::gc::ArenaList::takeNextArena()
+{
+    check();
+    Arena* arena = *cursorp_;
+    if (!arena)
+        return nullptr;
+    cursorp_ = &arena->next;
+    check();
+    return arena;
+}
+
+void
+js::gc::ArenaList::insertAtCursor(Arena* a)
+{
+    check();
+    a->next = *cursorp_;
+    *cursorp_ = a;
+    // At this point, the cursor is sitting before |a|. Move it after |a|
+    // if necessary.
+    if (!a->hasFreeThings())
+        cursorp_ = &a->next;
+    check();
+}
+
+void
+js::gc::ArenaList::insertBeforeCursor(Arena* a)
+{
+    check();
+    a->next = *cursorp_;
+    *cursorp_ = a;
+    cursorp_ = &a->next;
+    check();
+}
+
+js::gc::ArenaList&
+js::gc::ArenaList::insertListWithCursorAtEnd(const ArenaList& other)
+{
+    check();
+    other.check();
+    MOZ_ASSERT(other.isCursorAtEnd());
+    if (other.isCursorAtHead())
+        return *this;
+    // Insert the full arenas of |other| after those of |this|.
+    *other.cursorp_ = *cursorp_;
+    *cursorp_ = other.head_;
+    cursorp_ = other.cursorp_;
+    check();
+    return *this;
+}
+
+js::gc::SortedArenaList::SortedArenaList(size_t thingsPerArena)
+{
+    reset(thingsPerArena);
+}
+
+void
+js::gc::SortedArenaList::setThingsPerArena(size_t thingsPerArena)
+{
+    MOZ_ASSERT(thingsPerArena && thingsPerArena <= MaxThingsPerArena);
+    thingsPerArena_ = thingsPerArena;
+}
+
+void
+js::gc::SortedArenaList::reset(size_t thingsPerArena)
+{
+    setThingsPerArena(thingsPerArena);
+    // Initialize the segments.
+    for (size_t i = 0; i <= thingsPerArena; ++i)
+        segments[i].clear();
+}
+
+void
+js::gc::SortedArenaList::insertAt(Arena* arena, size_t nfree)
+{
+    MOZ_ASSERT(nfree <= thingsPerArena_);
+    segments[nfree].append(arena);
+}
+
+void
+js::gc::SortedArenaList::extractEmpty(Arena** empty)
+{
+    SortedArenaListSegment& segment = segments[thingsPerArena_];
+    if (segment.head) {
+        *segment.tailp = *empty;
+        *empty = segment.head;
+        segment.clear();
+    }
+}
+
+js::gc::ArenaList
+js::gc::SortedArenaList::toArenaList()
+{
+    // Link the non-empty segment tails up to the non-empty segment heads.
+    size_t tailIndex = 0;
+    for (size_t headIndex = 1; headIndex <= thingsPerArena_; ++headIndex) {
+        if (headAt(headIndex)) {
+            segments[tailIndex].linkTo(headAt(headIndex));
+            tailIndex = headIndex;
+        }
+    }
+    // Point the tail of the final non-empty segment at null. Note that if
+    // the list is empty, this will just set segments[0].head to null.
+    segments[tailIndex].linkTo(nullptr);
+    // Create an ArenaList with head and cursor set to the head and tail of
+    // the first segment (if that segment is empty, only the head is used).
+    return ArenaList(segments[0]);
+}
+
+js::gc::Arena*
+js::gc::ArenaLists::getFirstArena(AllocKind thingKind) const
+{
+    return arenaLists(thingKind).head();
+}
+
+js::gc::Arena*
+js::gc::ArenaLists::getFirstArenaToSweep(AllocKind thingKind) const
+{
+    return arenaListsToSweep(thingKind);
+}
+
+js::gc::Arena*
+js::gc::ArenaLists::getFirstSweptArena(AllocKind thingKind) const
+{
+    if (thingKind != incrementalSweptArenaKind.ref())
+        return nullptr;
+    return incrementalSweptArenas.ref().head();
+}
+
+js::gc::Arena*
+js::gc::ArenaLists::getArenaAfterCursor(AllocKind thingKind) const
+{
+    return arenaLists(thingKind).arenaAfterCursor();
+}
+
+bool
+js::gc::ArenaLists::arenaListsAreEmpty() const
+{
+    for (auto i : AllAllocKinds()) {
+        /*
+         * The arena cannot be empty if the background finalization is not yet
+         * done.
+         */
+        if (backgroundFinalizeState(i) != BFS_DONE)
+            return false;
+        if (!arenaLists(i).isEmpty())
+            return false;
+    }
+    return true;
+}
+
+void
+js::gc::ArenaLists::unmarkAll()
+{
+    for (auto i : AllAllocKinds()) {
+        /* The background finalization must have stopped at this point. */
+        MOZ_ASSERT(backgroundFinalizeState(i) == BFS_DONE);
+        for (Arena* arena = arenaLists(i).head(); arena; arena = arena->next)
+            arena->unmarkAll();
+    }
+}
+
+bool
+js::gc::ArenaLists::doneBackgroundFinalize(AllocKind kind) const
+{
+    return backgroundFinalizeState(kind) == BFS_DONE;
+}
+
+bool
+js::gc::ArenaLists::needBackgroundFinalizeWait(AllocKind kind) const
+{
+    return backgroundFinalizeState(kind) != BFS_DONE;
+}
+
+void
+js::gc::ArenaLists::purge()
+{
+    for (auto i : AllAllocKinds())
+        freeLists(i) = &placeholder;
+}
+
+bool
+js::gc::ArenaLists::arenaIsInUse(Arena* arena, AllocKind kind) const
+{
+    MOZ_ASSERT(arena);
+    return arena == freeLists(kind)->getArenaUnchecked();
+}
+
+MOZ_ALWAYS_INLINE js::gc::TenuredCell*
+js::gc::ArenaLists::allocateFromFreeList(AllocKind thingKind, size_t thingSize)
+{
+    return freeLists(thingKind)->allocate(thingSize);
+}
+
+void
+js::gc::ArenaLists::checkEmptyFreeLists()
+{
+#ifdef DEBUG
+    for (auto i : AllAllocKinds())
+        checkEmptyFreeList(i);
+#endif
+}
+
+bool
+js::gc::ArenaLists::checkEmptyArenaLists()
+{
+    bool empty = true;
+#ifdef DEBUG
+    for (auto i : AllAllocKinds()) {
+        if (!checkEmptyArenaList(i))
+            empty = false;
+    }
+#endif
+    return empty;
+}
+
+void
+js::gc::ArenaLists::checkEmptyFreeList(AllocKind kind)
+{
+    MOZ_ASSERT(freeLists(kind)->isEmpty());
+}
+
+#endif // gc_ArenaList_inl_h
--- a/js/src/gc/ArenaList.h
+++ b/js/src/gc/ArenaList.h
@@ -1,35 +1,48 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * 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/. */
 
+/*
+ * GC-internal definitions of ArenaList and associated heap data structures.
+ */
+
 #ifndef gc_ArenaList_h
 #define gc_ArenaList_h
 
-#include "gc/Heap.h"
+#include "gc/AllocKind.h"
+#include "js/GCAPI.h"
 #include "js/SliceBudget.h"
 #include "threading/ProtectedData.h"
 
 namespace JS {
 
 struct Zone;
 
 } /* namespace JS */
 
 namespace js {
 
+class FreeOp;
 class Nursery;
 class TenuringTracer;
 
+namespace gcstats {
+struct Statistics;
+}
+
 namespace gc {
 
+class Arena;
 struct FinalizePhase;
+class FreeSpan;
+class TenuredCell;
 
 /*
  * A single segment of a SortedArenaList. Each segment has a head and a tail,
  * which track the start and end of a segment for O(1) append and concatenation.
  */
 struct SortedArenaListSegment
 {
     Arena* head;
@@ -40,22 +53,17 @@ struct SortedArenaListSegment
         tailp = &head;
     }
 
     bool isEmpty() const {
         return tailp == &head;
     }
 
     // Appends |arena| to this segment.
-    void append(Arena* arena) {
-        MOZ_ASSERT(arena);
-        MOZ_ASSERT_IF(head, head->getAllocKind() == arena->getAllocKind());
-        *tailp = arena;
-        tailp = &arena->next;
-    }
+    inline void append(Arena* arena);
 
     // Points the tail of this segment at |arena|, which may be null. Note
     // that this does not change the tail itself, but merely which arena
     // follows it. This essentially turns the tail into a cursor (see also the
     // description of ArenaList), but from the perspective of a SortedArenaList
     // this makes no difference.
     void linkTo(Arena* arena) {
         *tailp = arena;
@@ -96,148 +104,57 @@ class ArenaList {
     //     cursor, and therefore |*cursorp_| points to the arena following the
     //     cursor.
     //
     // |cursorp_| is never null.
     //
     Arena* head_;
     Arena** cursorp_;
 
-    void copy(const ArenaList& other) {
-        other.check();
-        head_ = other.head_;
-        cursorp_ = other.isCursorAtHead() ? &head_ : other.cursorp_;
-        check();
-    }
+    inline void copy(const ArenaList& other);
 
   public:
-    ArenaList() {
-        clear();
-    }
-
-    ArenaList(const ArenaList& other) {
-        copy(other);
-    }
+    inline ArenaList();
+    inline ArenaList(const ArenaList& other);
 
-    ArenaList& operator=(const ArenaList& other) {
-        copy(other);
-        return *this;
-    }
-
-    explicit ArenaList(const SortedArenaListSegment& segment) {
-        head_ = segment.head;
-        cursorp_ = segment.isEmpty() ? &head_ : segment.tailp;
-        check();
-    }
+    inline ArenaList& operator=(const ArenaList& other);
 
-    // This does checking just of |head_| and |cursorp_|.
-    void check() const {
-#ifdef DEBUG
-        // If the list is empty, it must have this form.
-        MOZ_ASSERT_IF(!head_, cursorp_ == &head_);
+    inline explicit ArenaList(const SortedArenaListSegment& segment);
 
-        // If there's an arena following the cursor, it must not be full.
-        Arena* cursor = *cursorp_;
-        MOZ_ASSERT_IF(cursor, cursor->hasFreeThings());
-#endif
-    }
+    inline void check() const;
 
-    void clear() {
-        head_ = nullptr;
-        cursorp_ = &head_;
-        check();
-    }
-
-    ArenaList copyAndClear() {
-        ArenaList result = *this;
-        clear();
-        return result;
-    }
-
-    bool isEmpty() const {
-        check();
-        return !head_;
-    }
+    inline void clear();
+    inline ArenaList copyAndClear();
+    inline bool isEmpty() const;
 
     // This returns nullptr if the list is empty.
-    Arena* head() const {
-        check();
-        return head_;
-    }
-
-    bool isCursorAtHead() const {
-        check();
-        return cursorp_ == &head_;
-    }
+    inline Arena* head() const;
 
-    bool isCursorAtEnd() const {
-        check();
-        return !*cursorp_;
-    }
+    inline bool isCursorAtHead() const;
+    inline bool isCursorAtEnd() const;
 
-    void moveCursorToEnd() {
-        while (!isCursorAtEnd())
-            cursorp_ = &(*cursorp_)->next;
-    }
+    inline void moveCursorToEnd();
 
     // This can return nullptr.
-    Arena* arenaAfterCursor() const {
-        check();
-        return *cursorp_;
-    }
+    inline Arena* arenaAfterCursor() const;
 
     // This returns the arena after the cursor and moves the cursor past it.
-    Arena* takeNextArena() {
-        check();
-        Arena* arena = *cursorp_;
-        if (!arena)
-            return nullptr;
-        cursorp_ = &arena->next;
-        check();
-        return arena;
-    }
+    inline Arena* takeNextArena();
 
     // This does two things.
     // - Inserts |a| at the cursor.
     // - Leaves the cursor sitting just before |a|, if |a| is not full, or just
     //   after |a|, if |a| is full.
-    void insertAtCursor(Arena* a) {
-        check();
-        a->next = *cursorp_;
-        *cursorp_ = a;
-        // At this point, the cursor is sitting before |a|. Move it after |a|
-        // if necessary.
-        if (!a->hasFreeThings())
-            cursorp_ = &a->next;
-        check();
-    }
+    inline void insertAtCursor(Arena* a);
 
     // Inserts |a| at the cursor, then moves the cursor past it.
-    void insertBeforeCursor(Arena* a) {
-        check();
-        a->next = *cursorp_;
-        *cursorp_ = a;
-        cursorp_ = &a->next;
-        check();
-    }
+    inline void insertBeforeCursor(Arena* a);
 
     // This inserts |other|, which must be full, at the cursor of |this|.
-    ArenaList& insertListWithCursorAtEnd(const ArenaList& other) {
-        check();
-        other.check();
-        MOZ_ASSERT(other.isCursorAtEnd());
-        if (other.isCursorAtHead())
-            return *this;
-        // Insert the full arenas of |other| after those of |this|.
-        *other.cursorp_ = *cursorp_;
-        *cursorp_ = other.head_;
-        cursorp_ = other.cursorp_;
-        check();
-        return *this;
-    }
+    inline ArenaList& insertListWithCursorAtEnd(const ArenaList& other);
 
     Arena* removeRemainingArenas(Arena** arenap);
     Arena** pickArenasToRelocate(size_t& arenaTotalOut, size_t& relocTotalOut);
     Arena* relocateArenas(Arena* toRelocate, Arena* relocated,
                           js::SliceBudget& sliceBudget, gcstats::Statistics& stats);
 };
 
 /*
@@ -264,72 +181,37 @@ class SortedArenaList
     size_t thingsPerArena_;
     SortedArenaListSegment segments[MaxThingsPerArena + 1];
 
     // Convenience functions to get the nth head and tail.
     Arena* headAt(size_t n) { return segments[n].head; }
     Arena** tailAt(size_t n) { return segments[n].tailp; }
 
   public:
-    explicit SortedArenaList(size_t thingsPerArena = MaxThingsPerArena) {
-        reset(thingsPerArena);
-    }
+    inline explicit SortedArenaList(size_t thingsPerArena = MaxThingsPerArena);
 
-    void setThingsPerArena(size_t thingsPerArena) {
-        MOZ_ASSERT(thingsPerArena && thingsPerArena <= MaxThingsPerArena);
-        thingsPerArena_ = thingsPerArena;
-    }
+    inline void setThingsPerArena(size_t thingsPerArena);
 
     // Resets the first |thingsPerArena| segments of this list for further use.
-    void reset(size_t thingsPerArena = MaxThingsPerArena) {
-        setThingsPerArena(thingsPerArena);
-        // Initialize the segments.
-        for (size_t i = 0; i <= thingsPerArena; ++i)
-            segments[i].clear();
-    }
+    inline void reset(size_t thingsPerArena = MaxThingsPerArena);
 
     // Inserts an arena, which has room for |nfree| more things, in its segment.
-    void insertAt(Arena* arena, size_t nfree) {
-        MOZ_ASSERT(nfree <= thingsPerArena_);
-        segments[nfree].append(arena);
-    }
+    inline void insertAt(Arena* arena, size_t nfree);
 
     // Remove all empty arenas, inserting them as a linked list.
-    void extractEmpty(Arena** empty) {
-        SortedArenaListSegment& segment = segments[thingsPerArena_];
-        if (segment.head) {
-            *segment.tailp = *empty;
-            *empty = segment.head;
-            segment.clear();
-        }
-    }
+    inline void extractEmpty(Arena** empty);
 
     // Links up the tail of each non-empty segment to the head of the next
     // non-empty segment, creating a contiguous list that is returned as an
     // ArenaList. This is not a destructive operation: neither the head nor tail
     // of any segment is modified. However, note that the Arenas in the
     // resulting ArenaList should be treated as read-only unless the
     // SortedArenaList is no longer needed: inserting or removing arenas would
     // invalidate the SortedArenaList.
-    ArenaList toArenaList() {
-        // Link the non-empty segment tails up to the non-empty segment heads.
-        size_t tailIndex = 0;
-        for (size_t headIndex = 1; headIndex <= thingsPerArena_; ++headIndex) {
-            if (headAt(headIndex)) {
-                segments[tailIndex].linkTo(headAt(headIndex));
-                tailIndex = headIndex;
-            }
-        }
-        // Point the tail of the final non-empty segment at null. Note that if
-        // the list is empty, this will just set segments[0].head to null.
-        segments[tailIndex].linkTo(nullptr);
-        // Create an ArenaList with head and cursor set to the head and tail of
-        // the first segment (if that segment is empty, only the head is used).
-        return ArenaList(segments[0]);
-    }
+    inline ArenaList toArenaList();
 };
 
 enum class ShouldCheckThresholds
 {
     DontCheckThresholds = 0,
     CheckThresholds = 1
 };
 
@@ -396,114 +278,47 @@ class ArenaLists
   public:
     explicit ArenaLists(JSRuntime* rt, ZoneGroup* group);
     ~ArenaLists();
 
     const void* addressOfFreeList(AllocKind thingKind) const {
         return reinterpret_cast<const void*>(&freeLists_.refNoCheck()[thingKind]);
     }
 
-    Arena* getFirstArena(AllocKind thingKind) const {
-        return arenaLists(thingKind).head();
-    }
-
-    Arena* getFirstArenaToSweep(AllocKind thingKind) const {
-        return arenaListsToSweep(thingKind);
-    }
+    inline Arena* getFirstArena(AllocKind thingKind) const;
+    inline Arena* getFirstArenaToSweep(AllocKind thingKind) const;
+    inline Arena* getFirstSweptArena(AllocKind thingKind) const;
+    inline Arena* getArenaAfterCursor(AllocKind thingKind) const;
 
-    Arena* getFirstSweptArena(AllocKind thingKind) const {
-        if (thingKind != incrementalSweptArenaKind.ref())
-            return nullptr;
-        return incrementalSweptArenas.ref().head();
-    }
-
-    Arena* getArenaAfterCursor(AllocKind thingKind) const {
-        return arenaLists(thingKind).arenaAfterCursor();
-    }
+    inline bool arenaListsAreEmpty() const;
 
-    bool arenaListsAreEmpty() const {
-        for (auto i : AllAllocKinds()) {
-            /*
-             * The arena cannot be empty if the background finalization is not yet
-             * done.
-             */
-            if (backgroundFinalizeState(i) != BFS_DONE)
-                return false;
-            if (!arenaLists(i).isEmpty())
-                return false;
-        }
-        return true;
-    }
+    inline void unmarkAll();
 
-    void unmarkAll() {
-        for (auto i : AllAllocKinds()) {
-            /* The background finalization must have stopped at this point. */
-            MOZ_ASSERT(backgroundFinalizeState(i) == BFS_DONE);
-            for (Arena* arena = arenaLists(i).head(); arena; arena = arena->next)
-                arena->unmarkAll();
-        }
-    }
+    inline bool doneBackgroundFinalize(AllocKind kind) const;
+    inline bool needBackgroundFinalizeWait(AllocKind kind) const;
 
-    bool doneBackgroundFinalize(AllocKind kind) const {
-        return backgroundFinalizeState(kind) == BFS_DONE;
-    }
-
-    bool needBackgroundFinalizeWait(AllocKind kind) const {
-        return backgroundFinalizeState(kind) != BFS_DONE;
-    }
-
-    /*
-     * Clear the free lists so we won't try to allocate from swept arenas.
-     */
-    void purge() {
-        for (auto i : AllAllocKinds())
-            freeLists(i) = &placeholder;
-    }
+    /* Clear the free lists so we won't try to allocate from swept arenas. */
+    inline void purge();
 
     inline void prepareForIncrementalGC();
 
     /* Check if this arena is in use. */
-    bool arenaIsInUse(Arena* arena, AllocKind kind) const {
-        MOZ_ASSERT(arena);
-        return arena == freeLists(kind)->getArenaUnchecked();
-    }
+    inline bool arenaIsInUse(Arena* arena, AllocKind kind) const;
 
-    MOZ_ALWAYS_INLINE TenuredCell* allocateFromFreeList(AllocKind thingKind, size_t thingSize) {
-        return freeLists(thingKind)->allocate(thingSize);
-    }
+    MOZ_ALWAYS_INLINE TenuredCell* allocateFromFreeList(AllocKind thingKind, size_t thingSize);
 
-    /*
-     * Moves all arenas from |fromArenaLists| into |this|.
-     */
+    /* Moves all arenas from |fromArenaLists| into |this|. */
     void adoptArenas(JSRuntime* runtime, ArenaLists* fromArenaLists, bool targetZoneIsCollecting);
 
     /* True if the Arena in question is found in this ArenaLists */
     bool containsArena(JSRuntime* runtime, Arena* arena);
 
-    void checkEmptyFreeLists() {
-#ifdef DEBUG
-        for (auto i : AllAllocKinds())
-            checkEmptyFreeList(i);
-#endif
-    }
-
-    bool checkEmptyArenaLists() {
-        bool empty = true;
-#ifdef DEBUG
-        for (auto i : AllAllocKinds()) {
-            if (!checkEmptyArenaList(i))
-                empty = false;
-        }
-#endif
-        return empty;
-    }
-
-    void checkEmptyFreeList(AllocKind kind) {
-        MOZ_ASSERT(freeLists(kind)->isEmpty());
-    }
+    inline void checkEmptyFreeLists();
+    inline bool checkEmptyArenaLists();
+    inline void checkEmptyFreeList(AllocKind kind);
 
     bool checkEmptyArenaList(AllocKind kind);
 
     bool relocateArenas(JS::Zone* zone, Arena*& relocatedListOut, JS::gcreason::Reason reason,
                         js::SliceBudget& sliceBudget, gcstats::Statistics& stats);
 
     void queueForegroundObjectsForSweep(FreeOp* fop);
     void queueForegroundThingsForSweep(FreeOp* fop);
--- a/js/src/gc/Heap.h
+++ b/js/src/gc/Heap.h
@@ -6,29 +6,28 @@
 
 #ifndef gc_Heap_h
 #define gc_Heap_h
 
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/Atomics.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/DebugOnly.h"
-#include "mozilla/EnumeratedArray.h"
-#include "mozilla/EnumeratedRange.h"
 #include "mozilla/PodOperations.h"
 
 #include <stddef.h>
 #include <stdint.h>
 
 #include "jsfriendapi.h"
 #include "jspubtd.h"
 #include "jstypes.h"
 #include "jsutil.h"
 
 #include "ds/BitArray.h"
+#include "gc/AllocKind.h"
 #include "gc/GCEnum.h"
 #include "gc/Memory.h"
 #include "js/GCAPI.h"
 #include "js/HeapAPI.h"
 #include "js/RootingAPI.h"
 #include "js/TracingAPI.h"
 
 #include "vm/Printer.h"
@@ -55,185 +54,16 @@ struct Chunk;
  * estimated lifetime or lifetime requirements of objects allocated from that
  * site.
  */
 enum InitialHeap : uint8_t {
     DefaultHeap,
     TenuredHeap
 };
 
-/* The GC allocation kinds. */
-// FIXME: uint8_t would make more sense for the underlying type, but causes
-// miscompilations in GCC (fixed in 4.8.5 and 4.9.3). See also bug 1143966.
-enum class AllocKind {
-    FIRST,
-    OBJECT_FIRST = FIRST,
-    FUNCTION = FIRST,
-    FUNCTION_EXTENDED,
-    OBJECT0,
-    OBJECT0_BACKGROUND,
-    OBJECT2,
-    OBJECT2_BACKGROUND,
-    OBJECT4,
-    OBJECT4_BACKGROUND,
-    OBJECT8,
-    OBJECT8_BACKGROUND,
-    OBJECT12,
-    OBJECT12_BACKGROUND,
-    OBJECT16,
-    OBJECT16_BACKGROUND,
-    OBJECT_LIMIT,
-    OBJECT_LAST = OBJECT_LIMIT - 1,
-    SCRIPT,
-    LAZY_SCRIPT,
-    SHAPE,
-    ACCESSOR_SHAPE,
-    BASE_SHAPE,
-    OBJECT_GROUP,
-    FAT_INLINE_STRING,
-    STRING,
-    EXTERNAL_STRING,
-    FAT_INLINE_ATOM,
-    ATOM,
-    SYMBOL,
-    JITCODE,
-    SCOPE,
-    REGEXP_SHARED,
-    LIMIT,
-    LAST = LIMIT - 1
-};
-
-// Macro to enumerate the different allocation kinds supplying information about
-// the trace kind, C++ type and allocation size.
-#define FOR_EACH_OBJECT_ALLOCKIND(D) \
- /* AllocKind              TraceKind      TypeName           SizedType */ \
-    D(FUNCTION,            Object,        JSObject,          JSFunction) \
-    D(FUNCTION_EXTENDED,   Object,        JSObject,          FunctionExtended) \
-    D(OBJECT0,             Object,        JSObject,          JSObject_Slots0) \
-    D(OBJECT0_BACKGROUND,  Object,        JSObject,          JSObject_Slots0) \
-    D(OBJECT2,             Object,        JSObject,          JSObject_Slots2) \
-    D(OBJECT2_BACKGROUND,  Object,        JSObject,          JSObject_Slots2) \
-    D(OBJECT4,             Object,        JSObject,          JSObject_Slots4) \
-    D(OBJECT4_BACKGROUND,  Object,        JSObject,          JSObject_Slots4) \
-    D(OBJECT8,             Object,        JSObject,          JSObject_Slots8) \
-    D(OBJECT8_BACKGROUND,  Object,        JSObject,          JSObject_Slots8) \
-    D(OBJECT12,            Object,        JSObject,          JSObject_Slots12) \
-    D(OBJECT12_BACKGROUND, Object,        JSObject,          JSObject_Slots12) \
-    D(OBJECT16,            Object,        JSObject,          JSObject_Slots16) \
-    D(OBJECT16_BACKGROUND, Object,        JSObject,          JSObject_Slots16)
-
-#define FOR_EACH_NONOBJECT_ALLOCKIND(D) \
- /* AllocKind              TraceKind      TypeName           SizedType */ \
-    D(SCRIPT,              Script,        JSScript,          JSScript) \
-    D(LAZY_SCRIPT,         LazyScript,    js::LazyScript,    js::LazyScript) \
-    D(SHAPE,               Shape,         js::Shape,         js::Shape) \
-    D(ACCESSOR_SHAPE,      Shape,         js::AccessorShape, js::AccessorShape) \
-    D(BASE_SHAPE,          BaseShape,     js::BaseShape,     js::BaseShape) \
-    D(OBJECT_GROUP,        ObjectGroup,   js::ObjectGroup,   js::ObjectGroup) \
-    D(FAT_INLINE_STRING,   String,        JSFatInlineString, JSFatInlineString) \
-    D(STRING,              String,        JSString,          JSString) \
-    D(EXTERNAL_STRING,     String,        JSExternalString,  JSExternalString) \
-    D(FAT_INLINE_ATOM,     String,        js::FatInlineAtom, js::FatInlineAtom) \
-    D(ATOM,                String,        js::NormalAtom,    js::NormalAtom) \
-    D(SYMBOL,              Symbol,        JS::Symbol,        JS::Symbol) \
-    D(JITCODE,             JitCode,       js::jit::JitCode,  js::jit::JitCode) \
-    D(SCOPE,               Scope,         js::Scope,         js::Scope) \
-    D(REGEXP_SHARED,       RegExpShared,  js::RegExpShared,  js::RegExpShared)
-
-#define FOR_EACH_ALLOCKIND(D) \
-    FOR_EACH_OBJECT_ALLOCKIND(D) \
-    FOR_EACH_NONOBJECT_ALLOCKIND(D)
-
-static_assert(int(AllocKind::FIRST) == 0, "Various places depend on AllocKind starting at 0, "
-                                          "please audit them carefully!");
-static_assert(int(AllocKind::OBJECT_FIRST) == 0, "Various places depend on AllocKind::OBJECT_FIRST "
-                                                 "being 0, please audit them carefully!");
-
-inline bool
-IsAllocKind(AllocKind kind)
-{
-    return kind >= AllocKind::FIRST && kind <= AllocKind::LIMIT;
-}
-
-inline bool
-IsValidAllocKind(AllocKind kind)
-{
-    return kind >= AllocKind::FIRST && kind <= AllocKind::LAST;
-}
-
-inline bool
-IsObjectAllocKind(AllocKind kind)
-{
-    return kind >= AllocKind::OBJECT_FIRST && kind <= AllocKind::OBJECT_LAST;
-}
-
-inline bool
-IsShapeAllocKind(AllocKind kind)
-{
-    return kind == AllocKind::SHAPE || kind == AllocKind::ACCESSOR_SHAPE;
-}
-
-// Returns a sequence for use in a range-based for loop,
-// to iterate over all alloc kinds.
-inline decltype(mozilla::MakeEnumeratedRange(AllocKind::FIRST, AllocKind::LIMIT))
-AllAllocKinds()
-{
-    return mozilla::MakeEnumeratedRange(AllocKind::FIRST, AllocKind::LIMIT);
-}
-
-// Returns a sequence for use in a range-based for loop,
-// to iterate over all object alloc kinds.
-inline decltype(mozilla::MakeEnumeratedRange(AllocKind::OBJECT_FIRST, AllocKind::OBJECT_LIMIT))
-ObjectAllocKinds()
-{
-    return mozilla::MakeEnumeratedRange(AllocKind::OBJECT_FIRST, AllocKind::OBJECT_LIMIT);
-}
-
-// Returns a sequence for use in a range-based for loop,
-// to iterate over alloc kinds from |first| to |limit|, exclusive.
-inline decltype(mozilla::MakeEnumeratedRange(AllocKind::FIRST, AllocKind::LIMIT))
-SomeAllocKinds(AllocKind first = AllocKind::FIRST, AllocKind limit = AllocKind::LIMIT)
-{
-    MOZ_ASSERT(IsAllocKind(first), "|first| is not a valid AllocKind!");
-    MOZ_ASSERT(IsAllocKind(limit), "|limit| is not a valid AllocKind!");
-    return mozilla::MakeEnumeratedRange(first, limit);
-}
-
-// AllAllocKindArray<ValueType> gives an enumerated array of ValueTypes,
-// with each index corresponding to a particular alloc kind.
-template<typename ValueType> using AllAllocKindArray =
-    mozilla::EnumeratedArray<AllocKind, AllocKind::LIMIT, ValueType>;
-
-// ObjectAllocKindArray<ValueType> gives an enumerated array of ValueTypes,
-// with each index corresponding to a particular object alloc kind.
-template<typename ValueType> using ObjectAllocKindArray =
-    mozilla::EnumeratedArray<AllocKind, AllocKind::OBJECT_LIMIT, ValueType>;
-
-static inline JS::TraceKind
-MapAllocToTraceKind(AllocKind kind)
-{
-    static const JS::TraceKind map[] = {
-#define EXPAND_ELEMENT(allocKind, traceKind, type, sizedType) \
-        JS::TraceKind::traceKind,
-FOR_EACH_ALLOCKIND(EXPAND_ELEMENT)
-#undef EXPAND_ELEMENT
-    };
-
-    static_assert(MOZ_ARRAY_LENGTH(map) == size_t(AllocKind::LIMIT),
-                  "AllocKind-to-TraceKind mapping must be in sync");
-    return map[size_t(kind)];
-}
-
-/*
- * This must be an upper bound, but we do not need the least upper bound, so
- * we just exclude non-background objects.
- */
-static const size_t MAX_BACKGROUND_FINALIZE_KINDS =
-    size_t(AllocKind::LIMIT) - size_t(AllocKind::OBJECT_LIMIT) / 2;
-
 /* Cells are aligned to CellAlignShift, so the largest tagged null pointer is: */
 const uintptr_t LargestTaggedNullCellPointer = (1 << CellAlignShift) - 1;
 
 /*
  * The minimum cell size ends up as twice the cell alignment because the mark
  * bitmap contains one bit per CellBytesPerMarkBit bytes (which is equal to
  * CellAlignBytes) and we need two mark bits per cell.
  */
--- a/js/src/jsgcinlines.h
+++ b/js/src/jsgcinlines.h
@@ -8,16 +8,18 @@
 #define jsgcinlines_h
 
 #include "mozilla/DebugOnly.h"
 #include "mozilla/Maybe.h"
 
 #include "gc/GCTrace.h"
 #include "gc/Zone.h"
 
+#include "gc/ArenaList-inl.h"
+
 namespace js {
 namespace gc {
 
 class AutoAssertEmptyNursery;
 
 inline void
 MakeAccessibleAfterMovingGC(void* anyp) {}