Bug 1520496 - Make address range check more reliable.
authorEmanuel Hoogeveen <emanuel.hoogeveen@gmail.com>
Fri, 22 Feb 2019 16:11:00 +0200
changeset 461095 f9aea7962fd0fcdef17355a5ce808c99bc32a4f6
parent 461094 90ec592466c052a420e600f02c9c7b558c25b7fc
child 461096 f2c0c83ede1719776285efcfbc207dc0d5e08af0
push id35618
push usershindli@mozilla.com
push dateTue, 26 Feb 2019 16:54:44 +0000
treeherdermozilla-central@d326a9d5f77b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1520496
milestone67.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 1520496 - Make address range check more reliable.
js/src/gc/Memory.cpp
--- a/js/src/gc/Memory.cpp
+++ b/js/src/gc/Memory.cpp
@@ -267,95 +267,86 @@ static inline uint64_t GetNumberInRange(
     } while (!result);
     rndNum = result.value() / binSize;
   } while (rndNum > maxNum);
 
   return minNum + rndNum;
 }
 
 #  ifndef XP_WIN
+static inline uint64_t FindAddressLimitInner(size_t highBit, size_t tries);
+
 /*
  * The address range available to applications depends on both hardware and
  * kernel configuration. For example, AArch64 on Linux uses addresses with
  * 39 significant bits by default, but can be configured to use addresses with
  * 48 significant bits by enabling a 4th translation table. Unfortunately,
  * there appears to be no standard way to query the limit at runtime
  * (Windows exposes this via GetSystemInfo()).
  *
  * This function tries to find the address limit by performing a binary search
  * on the index of the most significant set bit in the addresses it attempts to
  * allocate. As the requested address is often treated as a hint by the
  * operating system, we use the actual returned addresses to narrow the range.
  * We return the number of bits of an address that may be set.
  */
 static size_t FindAddressLimit() {
+  // Use 32 bits as a lower bound in case we keep getting nullptr.
+  uint64_t low = 31;
+  uint64_t highestSeen = (UINT64_C(1) << 32) - allocGranularity - 1;
+
+  // Exclude 48-bit and 47-bit addresses first.
+  uint64_t high = 47;
+  for (; high >= std::max(low, UINT64_C(46)); --high) {
+    highestSeen = std::max(FindAddressLimitInner(high, 4), highestSeen);
+    low = mozilla::FloorLog2(highestSeen);
+  }
+  // If those didn't work, perform a modified binary search.
+  while (high - 1 > low) {
+    uint64_t middle = low + (high - low) / 2;
+    highestSeen = std::max(FindAddressLimitInner(middle, 4), highestSeen);
+    low = mozilla::FloorLog2(highestSeen);
+    if (highestSeen < (UINT64_C(1) << middle)) {
+      high = middle;
+    }
+  }
+  // We can be sure of the lower bound, but check the upper bound again.
+  do {
+    high = low + 1;
+    highestSeen = std::max(FindAddressLimitInner(high, 8), highestSeen);
+    low = mozilla::FloorLog2(highestSeen);
+  } while (low >= high);
+
+  // `low` is the highest set bit, so `low + 1` is the number of bits.
+  return low + 1;
+}
+
+static inline uint64_t FindAddressLimitInner(size_t highBit, size_t tries) {
   const size_t length = allocGranularity;  // Used as both length and alignment.
 
-  void* address;
-  uint64_t startRaw, endRaw, start, end, desired, actual;
-
-  // Use 32 bits as a lower bound in case we keep getting nullptr.
-  size_t low = 31;
-  uint64_t highestSeen = (UINT64_C(1) << 32) - length - 1;
-
-  // Start with addresses that have bit 47 set.
-  size_t high = 47;
-  startRaw = UINT64_C(1) << high;
-  endRaw = 2 * startRaw - length - 1;
-  start = (startRaw + length - 1) / length;
-  end = (endRaw - (length - 1)) / length;
-
-  for (size_t tries = 0; tries < 4; ++tries) {
-    desired = length * GetNumberInRange(start, end);
-    address = MapMemoryAtFuzzy(reinterpret_cast<void*>(desired), length);
-    actual = uint64_t(address);
+  uint64_t highestSeen = 0;
+  uint64_t startRaw = UINT64_C(1) << highBit;
+  uint64_t endRaw = 2 * startRaw - length - 1;
+  uint64_t start = (startRaw + length - 1) / length;
+  uint64_t end = (endRaw - (length - 1)) / length;
+  for (size_t i = 0; i < tries; ++i) {
+    uint64_t desired = length * GetNumberInRange(start, end);
+    void* address = MapMemoryAtFuzzy(reinterpret_cast<void*>(desired), length);
+    uint64_t actual = uint64_t(address);
     if (address) {
       UnmapInternal(address, length);
     }
-    if (actual >= startRaw) {
-      return high + 1;  // Return early and skip the binary search.
-    }
     if (actual > highestSeen) {
       highestSeen = actual;
-      low = mozilla::FloorLog2(highestSeen);
-    }
-  }
-
-  // Those didn't work, so perform a binary search.
-  while (high - 1 > low) {
-    size_t middle = low + (high - low) / 2;
-    startRaw = UINT64_C(1) << middle;
-    endRaw = 2 * startRaw - length - 1;
-    start = (startRaw + length - 1) / length;
-    end = (endRaw - (length - 1)) / length;
-
-    for (size_t tries = 0; tries < 4; ++tries) {
-      desired = length * GetNumberInRange(start, end);
-      address = MapMemoryAtFuzzy(reinterpret_cast<void*>(desired), length);
-      actual = uint64_t(address);
-      if (address) {
-        UnmapInternal(address, length);
-      }
-      if (actual > highestSeen) {
-        highestSeen = actual;
-        low = mozilla::FloorLog2(highestSeen);
-      }
       if (actual >= startRaw) {
         break;
       }
     }
-
-    // Low was already updated above, so just check if we need to update high.
-    if (actual < startRaw) {
-      high = middle;
-    }
   }
-
-  // High was excluded, so use low (but sanity check it).
-  return std::min(low + 1, size_t(47));
+  return highestSeen;
 }
 #  endif  // !defined(XP_WIN)
 
 #endif  // defined(JS_64BIT)
 
 void InitMemorySubsystem() {
   if (pageSize == 0) {
 #ifdef XP_WIN