Bug 1406303 - Make the number of significant bits used by the radix tree a template parameter. r?njn draft
authorMike Hommey <mh+mozilla@glandium.org>
Fri, 06 Oct 2017 15:50:00 +0900
changeset 677071 153dabc7d854716d7439c2f466ba7059b70f0aec
parent 677070 34d180d496601d830ca54a9735671a08cd21ff4a
child 677072 fe07958614e7a715c2e35a994d4dc068b04d7302
push id83679
push userbmo:mh+mozilla@glandium.org
push dateMon, 09 Oct 2017 23:14:34 +0000
reviewersnjn
bugs1406303
milestone58.0a1
Bug 1406303 - Make the number of significant bits used by the radix tree a template parameter. r?njn All the parameters of the radix tree (bits per level, height) are derived from the aBits argument to ::Create in a straightforward way. aBits itself is a constant at the call point, making them all constants, so we can turn all of them as constants at compile time instead of storing as data.
memory/build/mozjemalloc.cpp
--- a/memory/build/mozjemalloc.cpp
+++ b/memory/build/mozjemalloc.cpp
@@ -639,34 +639,50 @@ struct ExtentTreeBoundsTrait : public Ex
 
     return (key_addr > node_addr) - (key_addr < node_addr);
   }
 };
 
 /******************************************************************************/
 /*
  * Radix tree data structures.
+ *
+ * The number of bits passed to the template is the number of significant bits
+ * in an address to do a radix lookup with.
+ *
+ * An address is looked up by splitting it in kBitsPerLevel bit chunks, except
+ * the most significant bits, where the bit chunk is kBitsAtLevel1 which can be
+ * different if Bits is not a multiple of kBitsPerLevel.
+ *
+ * With e.g. sizeof(void*)=4, Bits=16 and kBitsPerLevel=8, an address is split
+ * like the following:
+ * 0x12345678 -> mRoot[0x12][0x34]
  */
 
+template <size_t Bits>
 class AddressRadixTree {
  // Size of each radix tree node (as a power of 2).
  // This impacts tree depth.
 #if (SIZEOF_PTR == 4)
   static const size_t kNodeSize2Pow = 14;
 #else
   static const size_t kNodeSize2Pow = CACHELINE_2POW;
 #endif
+  static const size_t kBitsPerLevel = kNodeSize2Pow - SIZEOF_PTR_2POW;
+  static const size_t kBitsAtLevel1 =
+    (Bits % kBitsPerLevel) ? Bits % kBitsPerLevel : kBitsPerLevel;
+  static const size_t kHeight = (Bits + kBitsPerLevel - 1) / kBitsPerLevel;
+  static_assert(kBitsAtLevel1 + (kHeight - 1) * kBitsPerLevel == Bits,
+                "AddressRadixTree parameters don't work out");
 
   malloc_spinlock_t mLock;
   void** mRoot;
-  unsigned mHeight;
-  unsigned mLevel2Bits[2];
 
 public:
-  static AddressRadixTree* Create(unsigned aBits);
+  static AddressRadixTree<Bits>* Create();
 
   inline void* Get(void* aAddr);
 
   // Returns whether the value was properly set
   inline bool Set(void* aAddr, void* aValue);
 
   inline bool Unset(void* aAddr)
   {
@@ -1048,17 +1064,17 @@ struct ArenaTreeTrait
   }
 };
 
 /********/
 /*
  * Chunks.
  */
 
-static AddressRadixTree* gChunkRTree;
+static AddressRadixTree<(SIZEOF_PTR << 3) - CHUNK_2POW_DEFAULT>* gChunkRTree;
 
 /* Protects chunk-related data structures. */
 static malloc_mutex_t	chunks_mtx;
 
 /*
  * Trees of chunks that were previously allocated (trees differ only in node
  * ordering).  These are used when allocating chunks, in an attempt to re-use
  * address space.  Depending on function, different tree orderings are needed,
@@ -1728,85 +1744,77 @@ pages_copy(void *dest, const void *src, 
 	MOZ_ASSERT(n >= VM_COPY_MIN);
 	MOZ_ASSERT((void *)((uintptr_t)src & ~pagesize_mask) == src);
 
 	vm_copy(mach_task_self(), (vm_address_t)src, (vm_size_t)n,
 	    (vm_address_t)dest);
 }
 #endif
 
-AddressRadixTree*
-AddressRadixTree::Create(unsigned aBits)
+template <size_t Bits>
+AddressRadixTree<Bits>*
+AddressRadixTree<Bits>::Create()
 {
-  AddressRadixTree* ret;
-  unsigned bits_per_level, height;
-
-  bits_per_level = AddressRadixTree::kNodeSize2Pow - SIZEOF_PTR_2POW;
-  height = (aBits + bits_per_level - 1) / bits_per_level;
-
-  ret = (AddressRadixTree*)base_calloc(1, sizeof(AddressRadixTree));
+  AddressRadixTree<Bits>* ret;
+
+  ret = (AddressRadixTree<Bits>*)base_calloc(1, sizeof(AddressRadixTree<Bits>));
   if (!ret) {
     return nullptr;
   }
 
   malloc_spin_init(&ret->mLock);
-  ret->mHeight = height;
-  if (bits_per_level * height > aBits) {
-    ret->mLevel2Bits[0] = aBits % bits_per_level;
-  } else {
-    ret->mLevel2Bits[0] = bits_per_level;
-  }
-  ret->mLevel2Bits[1] = bits_per_level;
-
-  ret->mRoot = (void**)base_calloc(1 << ret->mLevel2Bits[0], sizeof(void*));
+
+  ret->mRoot = (void**)base_calloc(1 << kBitsAtLevel1, sizeof(void*));
   if (!ret->mRoot) {
     // We leak the rtree here, since there's no generic base deallocation.
     return nullptr;
   }
 
   return ret;
 }
 
+template <size_t Bits>
 void**
-AddressRadixTree::GetSlot(void* aKey, bool aCreate)
+AddressRadixTree<Bits>::GetSlot(void* aKey, bool aCreate)
 {
   uintptr_t key = reinterpret_cast<uintptr_t>(aKey);
   uintptr_t subkey;
   unsigned i, lshift, height, bits;
   void** node;
   void** child;
 
-  for (i = lshift = 0, height = mHeight, node = mRoot;
+  for (i = lshift = 0, height = kHeight, node = mRoot;
        i < height - 1;
        i++, lshift += bits, node = child) {
-    bits = mLevel2Bits[i ? 1 : 0];
+    bits = i ? kBitsPerLevel : kBitsAtLevel1;
     subkey = (key << lshift) >> ((SIZEOF_PTR << 3) - bits);
     child = (void**) node[subkey];
     if (!child && aCreate) {
-      child = (void**) base_calloc(1 << mLevel2Bits[1], sizeof(void*));
+      child = (void**) base_calloc(1 << kBitsPerLevel, sizeof(void*));
       if (child) {
         node[subkey] = child;
       }
     }
     if (!child) {
       return nullptr;
     }
   }
 
   /*
    * node is a leaf, so it contains values rather than node
    * pointers.
    */
-  bits = mLevel2Bits[i ? 1 : 0];
+  bits = i ? kBitsPerLevel : kBitsAtLevel1;
   subkey = (key << lshift) >> ((SIZEOF_PTR << 3) - bits);
   return &node[subkey];
 }
 
+template <size_t Bits>
 void*
-AddressRadixTree::Get(void* aKey)
+AddressRadixTree<Bits>::Get(void* aKey)
 {
   void* ret = nullptr;
 
   void** slot = GetSlot(aKey);
 
   if (slot) {
     ret = *slot;
   }
@@ -1832,18 +1840,19 @@ AddressRadixTree::Get(void* aKey)
   } else {
     MOZ_ASSERT(ret == nullptr);
   }
   malloc_spin_unlock(&mlock);
 #endif
   return ret;
 }
 
+template <size_t Bits>
 bool
-AddressRadixTree::Set(void* aKey, void* aValue)
+AddressRadixTree<Bits>::Set(void* aKey, void* aValue)
 {
   malloc_spin_lock(&mLock);
   void** slot = GetSlot(aKey, /* create */ true);
   if (slot) {
     *slot = aValue;
   }
   malloc_spin_unlock(&mLock);
   return slot;
@@ -4494,17 +4503,17 @@ MALLOC_OUT:
    * reset to the default value for the main arena. */
   gMainArena->mMaxDirty = opt_dirty_max;
 
   /*
    * Assign the initial arena to the initial thread.
    */
   thread_arena.set(gMainArena);
 
-  gChunkRTree = AddressRadixTree::Create((SIZEOF_PTR << 3) - CHUNK_2POW_DEFAULT);
+  gChunkRTree = AddressRadixTree<(SIZEOF_PTR << 3) - CHUNK_2POW_DEFAULT>::Create();
   if (!gChunkRTree) {
     return true;
   }
 
   malloc_initialized = true;
 
 #if !defined(XP_WIN) && !defined(XP_DARWIN)
   /* Prevent potential deadlock on malloc locks after fork. */