Bug 1624937 - Align JSClass to 8 byte to satisfy GC cell requirements r=jandem
authorJon Coppeard <jcoppeard@mozilla.com>
Thu, 26 Mar 2020 11:01:07 +0000
changeset 520493 eb884c85668282ef64c5b9d165878e93728ec10f
parent 520492 77eab1e3c3ae8c821c2844233888855817ba1613
child 520494 c1b90d60baea8dda9cee5ea803303e01a72c5643
push id37252
push usermalexandru@mozilla.com
push dateThu, 26 Mar 2020 15:34:27 +0000
treeherdermozilla-central@31360ced8ff8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem
bugs1624937
milestone76.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 1624937 - Align JSClass to 8 byte to satisfy GC cell requirements r=jandem This defines a constant for the number of reserved bits in a public header and does some tidying up. Differential Revision: https://phabricator.services.mozilla.com/D68335
js/public/Class.h
js/public/HeapAPI.h
js/src/gc/Cell.h
js/src/gc/GC.cpp
js/src/vm/BigIntType.h
js/src/vm/StringType.h
--- a/js/public/Class.h
+++ b/js/public/Class.h
@@ -9,16 +9,17 @@
 #ifndef js_Class_h
 #define js_Class_h
 
 #include "mozilla/Attributes.h"
 
 #include "jstypes.h"
 
 #include "js/CallArgs.h"
+#include "js/HeapAPI.h"
 #include "js/Id.h"
 #include "js/TypeDecls.h"
 
 /*
  * A JSClass acts as a vtable for JS objects that allows JSAPI clients to
  * control various aspects of the behavior of an object like property lookup.
  * It contains some engine-private extensions that allows more control over
  * object behavior and, e.g., allows custom slow layout.
@@ -733,17 +734,17 @@ struct MOZ_STATIC_CLASS JSClassOps {
   JSNative call;
   JSHasInstanceOp hasInstance;
   JSNative construct;
   JSTraceOp trace;
 };
 
 static constexpr const JSClassOps* JS_NULL_CLASS_OPS = nullptr;
 
-struct JSClass {
+struct alignas(js::gc::JSClassAlignBytes) JSClass {
   const char* name;
   uint32_t flags;
   const JSClassOps* cOps;
 
   const js::ClassSpec* spec;
   const js::ClassExtension* ext;
   const js::ObjectOps* oOps;
 
--- a/js/public/HeapAPI.h
+++ b/js/public/HeapAPI.h
@@ -69,16 +69,24 @@ const size_t ChunkRuntimeOffset = ChunkS
 const size_t ChunkTrailerSize = 2 * sizeof(uintptr_t) + sizeof(uint64_t);
 const size_t ChunkLocationOffset = ChunkSize - ChunkTrailerSize;
 const size_t ChunkStoreBufferOffset =
     ChunkSize - ChunkTrailerSize + sizeof(uint64_t);
 const size_t ArenaZoneOffset = sizeof(size_t);
 const size_t ArenaHeaderSize =
     sizeof(size_t) + 2 * sizeof(uintptr_t) + sizeof(size_t) + sizeof(uintptr_t);
 
+// The first word of a GC thing has certain requirements from the GC and is used
+// to store flags in the low bits.
+const size_t CellFlagBitsReservedForGC = 3;
+
+// The first word can be used to store JSClass pointers for some thing kinds, so
+// these must be suitably aligned.
+const size_t JSClassAlignBytes = size_t(1) << CellFlagBitsReservedForGC;
+
 /*
  * Live objects are marked black or gray. Everything reachable from a JS root is
  * marked black. Objects marked gray are eligible for cycle collection.
  *
  *    BlackBit:     GrayOrBlackBit:  Color:
  *       0               0           white
  *       0               1           gray
  *       1               0           black
--- a/js/src/gc/Cell.h
+++ b/js/src/gc/Cell.h
@@ -100,31 +100,33 @@ class CellColor {
 };
 
 // [SMDOC] GC Cell
 //
 // A GC cell is the base class for all GC things. All types allocated on the GC
 // heap extend either gc::Cell or gc::TenuredCell. If a type is always tenured,
 // prefer the TenuredCell class as base.
 //
-// The first word (a pointer or uintptr_t) of each Cell must reserve the low bit
-// for GC purposes. In addition to that, nursery Cells must reserve the low
-// Cell::ReservedBits bits for GC purposes. The remaining bits are available to
-// sub-classes and typically store a pointer to another gc::Cell.
+// The first word (a pointer or uintptr_t) of each Cell must reserve the low
+// three bits for GC purposes. The remaining bits are available to sub-classes
+// and typically store a pointer to another gc::Cell.
 //
 // During moving GC operation a Cell may be marked as forwarded. This indicates
 // that a gc::RelocationOverlay is currently stored in the Cell's memory and
 // should be used to find the new location of the Cell.
 struct alignas(gc::CellAlignBytes) Cell {
  public:
-  // The low bits of the first word of each Cell are reserved for GC flags.
-  static constexpr int ReservedBits = 3;
-  static constexpr uintptr_t RESERVED_MASK = BitMask(ReservedBits);
+  static_assert(gc::CellFlagBitsReservedForGC >= 3,
+                "Not enough flag bits reserved for GC");
 
-  // Indicates if the cell is currently a RelocationOverlay
+  static constexpr uintptr_t RESERVED_MASK =
+      BitMask(gc::CellFlagBitsReservedForGC);
+
+  // Indicates if the cell has been forwarded (moved) by generational or
+  // compacting GC and is now a RelocationOverlay.
   static constexpr uintptr_t FORWARD_BIT = Bit(0);
 
   // When a Cell is in the nursery, this will indicate if it is a JSString (1)
   // or JSObject/BigInt (0). When not in nursery, this bit is still reserved for
   // JSString to use as JSString::NON_ATOM bit. This may be removed by Bug
   // 1376646.
   static constexpr uintptr_t JSSTRING_BIT = Bit(1);
 
@@ -539,17 +541,17 @@ bool TenuredCell::isAligned() const {
 // would store a pointer. If a single word isn't large enough, the length
 // is stored separately.
 //
 //          32       0
 //  ------------------
 //  | Length | Flags |
 //  ------------------
 //
-// The low bits of the flags word (see NumFlagBitsReservedForGC) are reserved
+// The low bits of the flags word (see CellFlagBitsReservedForGC) are reserved
 // for GC. Derived classes must ensure they don't use these flags for non-GC
 // purposes.
 template <class BaseCell>
 class CellWithLengthAndFlags : public BaseCell {
   static_assert(std::is_same<BaseCell, Cell>::value ||
                     std::is_same<BaseCell, TenuredCell>::value,
                 "BaseCell must be either Cell or TenuredCell");
 
@@ -558,18 +560,16 @@ class CellWithLengthAndFlags : public Ba
   uintptr_t flags_;
 
 #if JS_BITS_PER_WORD == 32
   // Additional storage for length if |flags_| is too small to fit both.
   uint32_t length_;
 #endif
 
  protected:
-  static constexpr size_t NumFlagBitsReservedForGC = Cell::ReservedBits;
-
   uint32_t lengthField() const {
 #if JS_BITS_PER_WORD == 32
     return length_;
 #else
     return uint32_t(flags_ >> 32);
 #endif
   }
 
--- a/js/src/gc/GC.cpp
+++ b/js/src/gc/GC.cpp
@@ -281,22 +281,25 @@ const AllocKind gc::slotsToThingKind[] =
     /*  8 */ AllocKind::OBJECT8,  AllocKind::OBJECT12, AllocKind::OBJECT12, AllocKind::OBJECT12,
     /* 12 */ AllocKind::OBJECT12, AllocKind::OBJECT16, AllocKind::OBJECT16, AllocKind::OBJECT16,
     /* 16 */ AllocKind::OBJECT16
     // clang-format on
 };
 
 // Check that reserved bits of a Cell are compatible with our typical allocators
 // since most derived classes will store a pointer in the first word.
-static_assert(js::detail::LIFO_ALLOC_ALIGN > BitMask(Cell::ReservedBits),
-              "Cell::ReservedBits should support LifoAlloc");
-static_assert(CellAlignBytes > BitMask(Cell::ReservedBits),
-              "Cell::ReservedBits should support gc::Cell");
-static_assert(js::jit::CodeAlignment > BitMask(Cell::ReservedBits),
-              "Cell::ReservedBits should support JIT code");
+static const size_t MinFirstWordAlignment = 1u << CellFlagBitsReservedForGC;
+static_assert(js::detail::LIFO_ALLOC_ALIGN >= MinFirstWordAlignment,
+              "CellFlagBitsReservedForGC should support LifoAlloc");
+static_assert(CellAlignBytes >= MinFirstWordAlignment,
+              "CellFlagBitsReservedForGC should support gc::Cell");
+static_assert(js::jit::CodeAlignment >= MinFirstWordAlignment,
+              "CellFlagBitsReservedForGC should support JIT code");
+static_assert(js::gc::JSClassAlignBytes >= MinFirstWordAlignment,
+              "CellFlagBitsReservedForGC should support JSClass pointers");
 
 static_assert(mozilla::ArrayLength(slotsToThingKind) ==
                   SLOTS_TO_THING_KIND_LIMIT,
               "We have defined a slot count for each kind.");
 
 #define CHECK_THING_SIZE(allocKind, traceKind, type, sizedType, bgFinal,       \
                          nursery, compact)                                     \
   static_assert(sizeof(sizedType) >= SortedArenaList::MinThingSize,            \
--- a/js/src/vm/BigIntType.h
+++ b/js/src/vm/BigIntType.h
@@ -43,18 +43,19 @@ class BigInt final : public js::gc::Cell
   using Base = js::gc::CellWithLengthAndFlags<js::gc::Cell>;
 
  public:
   using Digit = uintptr_t;
 
   static constexpr uintptr_t TYPE_FLAGS = js::gc::Cell::BIGINT_BIT;
 
  private:
-  // The low NumFlagBitsReservedForGC flag bits are reserved.
-  static constexpr uintptr_t SignBit = js::Bit(Base::NumFlagBitsReservedForGC);
+  // The low CellFlagBitsReservedForGC flag bits are reserved.
+  static constexpr uintptr_t SignBit =
+      js::Bit(js::gc::CellFlagBitsReservedForGC);
 
   static constexpr size_t InlineDigitsLength =
       (js::gc::MinCellSize - sizeof(Base)) / sizeof(Digit);
 
   // Note: 32-bit length and flags fields are inherited from
   // CellWithLengthAndFlags.
 
   // The digit storage starts with the least significant digit (little-endian
--- a/js/src/vm/StringType.h
+++ b/js/src/vm/StringType.h
@@ -254,17 +254,17 @@ class JSString : public js::gc::CellWith
    * The atom bit (NON_ATOM_BIT) is inverted and stored in a Cell
    * ReservedBit. Atoms are never stored in nursery, so the nursery can use
    * this bit to distinguish between JSString (1) and JSObject (0).
    *
    * If the INDEX_VALUE_BIT is set, flags will also hold an integer index.
    */
 
   // The low bits of flag word are reserved by GC.
-  static_assert(Base::NumFlagBitsReservedForGC <= 3,
+  static_assert(js::gc::CellFlagBitsReservedForGC <= 3,
                 "JSString::flags must reserve enough bits for Cell");
 
   static const uint32_t NON_ATOM_BIT = js::gc::Cell::JSSTRING_BIT;
   static const uint32_t LINEAR_BIT = js::Bit(4);
   static const uint32_t DEPENDENT_BIT = js::Bit(5);
   static const uint32_t INLINE_CHARS_BIT = js::Bit(6);
 
   static const uint32_t EXTENSIBLE_FLAGS =