Bug 1441657 - Create a new mozilla/WrappingOperations.h header to contain implementations of common math operations with well-defined wraparound semantics. r=froydnj
authorJeff Walden <jwalden@mit.edu>
Thu, 15 Feb 2018 17:36:50 -0800
changeset 406214 bc01e52c16d77ba3a7ab45ec195892c88e5f861b
parent 406213 f68825f0495dba102d4f1d73d246095e7c82e214
child 406215 cf32e82edfb795a94740fef748c9ef503425e448
push id33546
push useraciure@mozilla.com
push dateFri, 02 Mar 2018 10:16:37 +0000
treeherdermozilla-central@b2a9a4bb5c94 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfroydnj
bugs1441657
milestone60.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 1441657 - Create a new mozilla/WrappingOperations.h header to contain implementations of common math operations with well-defined wraparound semantics. r=froydnj
js/public/Conversions.h
js/src/jsmath.cpp
mfbt/MathAlgorithms.h
mfbt/WrappingOperations.h
mfbt/moz.build
mfbt/tests/TestMathAlgorithms.cpp
mfbt/tests/TestWrappingOperations.cpp
mfbt/tests/moz.build
--- a/js/public/Conversions.h
+++ b/js/public/Conversions.h
@@ -8,16 +8,17 @@
 
 #ifndef js_Conversions_h
 #define js_Conversions_h
 
 #include "mozilla/Casting.h"
 #include "mozilla/FloatingPoint.h"
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/TypeTraits.h"
+#include "mozilla/WrappingOperations.h"
 
 #include <math.h>
 
 #include "jspubtd.h"
 
 #include "js/RootingAPI.h"
 #include "js/Value.h"
 
--- a/js/src/jsmath.cpp
+++ b/js/src/jsmath.cpp
@@ -9,16 +9,17 @@
  */
 
 #include "jsmath.h"
 
 #include "mozilla/FloatingPoint.h"
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/Unused.h"
+#include "mozilla/WrappingOperations.h"
 
 #include <algorithm>  // for std::max
 #include <fcntl.h>
 #ifdef XP_UNIX
 # include <unistd.h>
 #endif
 
 #include "fdlibm.h"
@@ -109,16 +110,17 @@ using mozilla::ExponentComponent;
 using mozilla::FloatingPoint;
 using mozilla::IsFinite;
 using mozilla::IsInfinite;
 using mozilla::IsNaN;
 using mozilla::IsNegative;
 using mozilla::IsNegativeZero;
 using mozilla::PositiveInfinity;
 using mozilla::NegativeInfinity;
+using mozilla::WrapToSigned;
 using JS::ToNumber;
 using JS::GenericNaN;
 
 static const JSConstDoubleSpec math_constants[] = {
     {"E"      ,  M_E       },
     {"LOG2E"  ,  M_LOG2E   },
     {"LOG10E" ,  M_LOG10E  },
     {"LN2"    ,  M_LN2     },
@@ -484,19 +486,17 @@ js::math_imul_handle(JSContext* cx, Hand
 {
     uint32_t a = 0, b = 0;
     if (!lhs.isUndefined() && !ToUint32(cx, lhs, &a))
         return false;
     if (!rhs.isUndefined() && !ToUint32(cx, rhs, &b))
         return false;
 
     uint32_t product = a * b;
-    res.setInt32(product > INT32_MAX
-                 ? int32_t(INT32_MIN + (product - INT32_MAX - 1))
-                 : int32_t(product));
+    res.setInt32(WrapToSigned(product));
     return true;
 }
 
 bool
 js::math_imul(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
--- a/mfbt/MathAlgorithms.h
+++ b/mfbt/MathAlgorithms.h
@@ -537,80 +537,11 @@ Clamp(const T aValue, const T aMin, cons
 
     if (aValue <= aMin)
         return aMin;
     if (aValue >= aMax)
         return aMax;
     return aValue;
 }
 
-namespace detail {
-
-template<typename UnsignedType>
-struct WrapToSignedHelper
-{
-  static_assert(mozilla::IsUnsigned<UnsignedType>::value,
-                "WrapToSigned must be passed an unsigned type");
-
-  using SignedType = typename mozilla::MakeSigned<UnsignedType>::Type;
-
-  static constexpr SignedType MaxValue =
-    (UnsignedType(1) << (CHAR_BIT * sizeof(SignedType) - 1)) - 1;
-  static constexpr SignedType MinValue = -MaxValue - 1;
-
-  static constexpr UnsignedType MinValueUnsigned =
-    static_cast<UnsignedType>(MinValue);
-  static constexpr UnsignedType MaxValueUnsigned =
-    static_cast<UnsignedType>(MaxValue);
-
-  // Overflow-correctness was proven in bug 1432646 and is explained in the
-  // comment below.  This function is very hot, both at compile time and
-  // runtime, so disable all overflow checking in it.
-  MOZ_NO_SANITIZE_UNSIGNED_OVERFLOW MOZ_NO_SANITIZE_SIGNED_OVERFLOW
-  static constexpr SignedType compute(UnsignedType aValue)
-  {
-    // This algorithm was originally provided here:
-    // https://stackoverflow.com/questions/13150449/efficient-unsigned-to-signed-cast-avoiding-implementation-defined-behavior
-    //
-    // If the value is in the non-negative signed range, just cast.
-    //
-    // If the value will be negative, compute its delta from the first number
-    // past the max signed integer, then add that to the minimum signed value.
-    //
-    // At the low end: if |u| is the maximum signed value plus one, then it has
-    // the same mathematical value as |MinValue| cast to unsigned form.  The
-    // delta is zero, so the signed form of |u| is |MinValue| -- exactly the
-    // result of adding zero delta to |MinValue|.
-    //
-    // At the high end: if |u| is the maximum *unsigned* value, then it has all
-    // bits set.  |MinValue| cast to unsigned form is purely the high bit set.
-    // So the delta is all bits but high set -- exactly |MaxValue|.  And as
-    // |MinValue = -MaxValue - 1|, we have |MaxValue + (-MaxValue - 1)| to
-    // equal -1.
-    //
-    // Thus the delta below is in signed range, the corresponding cast is safe,
-    // and this computation produces values spanning [MinValue, 0): exactly the
-    // desired range of all negative signed integers.
-    return (aValue <= MaxValueUnsigned)
-           ? static_cast<SignedType>(aValue)
-           : static_cast<SignedType>(aValue - MinValueUnsigned) + MinValue;
-  }
-};
-
-} // namespace detail
-
-/**
- * Convert an unsigned value to signed, if necessary wrapping around.
- *
- * This is the behavior normal C++ casting will perform in most implementations
- * these days -- but this function makes explicit that such conversion is
- * happening.
- */
-template<typename UnsignedType>
-inline constexpr typename detail::WrapToSignedHelper<UnsignedType>::SignedType
-WrapToSigned(UnsignedType aValue)
-{
-  return detail::WrapToSignedHelper<UnsignedType>::compute(aValue);
-}
-
 } /* namespace mozilla */
 
 #endif /* mozilla_MathAlgorithms_h */
copy from mfbt/MathAlgorithms.h
copy to mfbt/WrappingOperations.h
--- a/mfbt/MathAlgorithms.h
+++ b/mfbt/WrappingOperations.h
@@ -1,554 +1,30 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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/. */
 
-/* mfbt maths algorithms. */
+/*
+ * Math operations that implement wraparound semantics on overflow or underflow.
+ */
 
-#ifndef mozilla_MathAlgorithms_h
-#define mozilla_MathAlgorithms_h
+#ifndef mozilla_WrappingOperations_h
+#define mozilla_WrappingOperations_h
 
-#include "mozilla/Assertions.h"
+#include "mozilla/Attributes.h"
 #include "mozilla/TypeTraits.h"
 
-#include <cmath>
 #include <limits.h>
-#include <stdint.h>
-
-namespace mozilla {
-
-// Greatest Common Divisor
-template<typename IntegerType>
-MOZ_ALWAYS_INLINE IntegerType
-EuclidGCD(IntegerType aA, IntegerType aB)
-{
-  // Euclid's algorithm; O(N) in the worst case.  (There are better
-  // ways, but we don't need them for the current use of this algo.)
-  MOZ_ASSERT(aA > IntegerType(0));
-  MOZ_ASSERT(aB > IntegerType(0));
-
-  while (aA != aB) {
-    if (aA > aB) {
-      aA = aA - aB;
-    } else {
-      aB = aB - aA;
-    }
-  }
-
-  return aA;
-}
-
-// Least Common Multiple
-template<typename IntegerType>
-MOZ_ALWAYS_INLINE IntegerType
-EuclidLCM(IntegerType aA, IntegerType aB)
-{
-  // Divide first to reduce overflow risk.
-  return (aA / EuclidGCD(aA, aB)) * aB;
-}
-
-namespace detail {
-
-template<typename T>
-struct AllowDeprecatedAbsFixed : FalseType {};
-
-template<> struct AllowDeprecatedAbsFixed<int32_t> : TrueType {};
-template<> struct AllowDeprecatedAbsFixed<int64_t> : TrueType {};
-
-template<typename T>
-struct AllowDeprecatedAbs : AllowDeprecatedAbsFixed<T> {};
-
-template<> struct AllowDeprecatedAbs<int> : TrueType {};
-template<> struct AllowDeprecatedAbs<long> : TrueType {};
-
-} // namespace detail
-
-// DO NOT USE DeprecatedAbs.  It exists only until its callers can be converted
-// to Abs below, and it will be removed when all callers have been changed.
-template<typename T>
-inline typename mozilla::EnableIf<detail::AllowDeprecatedAbs<T>::value, T>::Type
-DeprecatedAbs(const T aValue)
-{
-  // The absolute value of the smallest possible value of a signed-integer type
-  // won't fit in that type (on twos-complement systems -- and we're blithely
-  // assuming we're on such systems, for the non-<stdint.h> types listed above),
-  // so assert that the input isn't that value.
-  //
-  // This is the case if: the value is non-negative; or if adding one (giving a
-  // value in the range [-maxvalue, 0]), then negating (giving a value in the
-  // range [0, maxvalue]), doesn't produce maxvalue (because in twos-complement,
-  // (minvalue + 1) == -maxvalue).
-  MOZ_ASSERT(aValue >= 0 ||
-             -(aValue + 1) != T((1ULL << (CHAR_BIT * sizeof(T) - 1)) - 1),
-             "You can't negate the smallest possible negative integer!");
-  return aValue >= 0 ? aValue : -aValue;
-}
-
-namespace detail {
-
-// For now mozilla::Abs only takes intN_T, the signed natural types, and
-// float/double/long double.  Feel free to add overloads for other standard,
-// signed types if you need them.
-
-template<typename T>
-struct AbsReturnTypeFixed;
-
-template<> struct AbsReturnTypeFixed<int8_t> { typedef uint8_t Type; };
-template<> struct AbsReturnTypeFixed<int16_t> { typedef uint16_t Type; };
-template<> struct AbsReturnTypeFixed<int32_t> { typedef uint32_t Type; };
-template<> struct AbsReturnTypeFixed<int64_t> { typedef uint64_t Type; };
-
-template<typename T>
-struct AbsReturnType : AbsReturnTypeFixed<T> {};
-
-template<> struct AbsReturnType<char> :
-  EnableIf<char(-1) < char(0), unsigned char> {};
-template<> struct AbsReturnType<signed char> { typedef unsigned char Type; };
-template<> struct AbsReturnType<short> { typedef unsigned short Type; };
-template<> struct AbsReturnType<int> { typedef unsigned int Type; };
-template<> struct AbsReturnType<long> { typedef unsigned long Type; };
-template<> struct AbsReturnType<long long> { typedef unsigned long long Type; };
-template<> struct AbsReturnType<float> { typedef float Type; };
-template<> struct AbsReturnType<double> { typedef double Type; };
-template<> struct AbsReturnType<long double> { typedef long double Type; };
-
-} // namespace detail
-
-template<typename T>
-inline constexpr typename detail::AbsReturnType<T>::Type
-Abs(const T aValue)
-{
-  using ReturnType = typename detail::AbsReturnType<T>::Type;
-  return aValue >= 0 ? ReturnType(aValue) : ~ReturnType(aValue) + 1;
-}
-
-template<>
-inline float
-Abs<float>(const float aFloat)
-{
-  return std::fabs(aFloat);
-}
-
-template<>
-inline double
-Abs<double>(const double aDouble)
-{
-  return std::fabs(aDouble);
-}
-
-template<>
-inline long double
-Abs<long double>(const long double aLongDouble)
-{
-  return std::fabs(aLongDouble);
-}
-
-} // namespace mozilla
-
-#if defined(_MSC_VER) && \
-    (defined(_M_IX86) || defined(_M_AMD64) || defined(_M_X64))
-#  define MOZ_BITSCAN_WINDOWS
-
-#  include <intrin.h>
-#  pragma intrinsic(_BitScanForward, _BitScanReverse)
-
-#  if defined(_M_AMD64) || defined(_M_X64)
-#    define MOZ_BITSCAN_WINDOWS64
-#   pragma intrinsic(_BitScanForward64, _BitScanReverse64)
-#  endif
-
-#endif
 
 namespace mozilla {
 
 namespace detail {
 
-#if defined(MOZ_BITSCAN_WINDOWS)
-
-inline uint_fast8_t
-CountLeadingZeroes32(uint32_t aValue)
-{
-  unsigned long index;
-  if (!_BitScanReverse(&index, static_cast<unsigned long>(aValue)))
-      return 32;
-  return uint_fast8_t(31 - index);
-}
-
-
-inline uint_fast8_t
-CountTrailingZeroes32(uint32_t aValue)
-{
-  unsigned long index;
-  if (!_BitScanForward(&index, static_cast<unsigned long>(aValue)))
-      return 32;
-  return uint_fast8_t(index);
-}
-
-inline uint_fast8_t
-CountPopulation32(uint32_t aValue)
-{
-  uint32_t x = aValue - ((aValue >> 1) & 0x55555555);
-  x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
-  return (((x + (x >> 4)) & 0xf0f0f0f) * 0x1010101) >> 24;
-}
-inline uint_fast8_t
-CountPopulation64(uint64_t aValue)
-{
-  return uint_fast8_t(CountPopulation32(aValue & 0xffffffff) +
-                      CountPopulation32(aValue >> 32));
-}
-
-inline uint_fast8_t
-CountLeadingZeroes64(uint64_t aValue)
-{
-#if defined(MOZ_BITSCAN_WINDOWS64)
-  unsigned long index;
-  if (!_BitScanReverse64(&index, static_cast<unsigned __int64>(aValue)))
-      return 64;
-  return uint_fast8_t(63 - index);
-#else
-  uint32_t hi = uint32_t(aValue >> 32);
-  if (hi != 0) {
-    return CountLeadingZeroes32(hi);
-  }
-  return 32u + CountLeadingZeroes32(uint32_t(aValue));
-#endif
-}
-
-inline uint_fast8_t
-CountTrailingZeroes64(uint64_t aValue)
-{
-#if defined(MOZ_BITSCAN_WINDOWS64)
-  unsigned long index;
-  if (!_BitScanForward64(&index, static_cast<unsigned __int64>(aValue)))
-      return 64;
-  return uint_fast8_t(index);
-#else
-  uint32_t lo = uint32_t(aValue);
-  if (lo != 0) {
-    return CountTrailingZeroes32(lo);
-  }
-  return 32u + CountTrailingZeroes32(uint32_t(aValue >> 32));
-#endif
-}
-
-#  ifdef MOZ_HAVE_BITSCAN64
-#    undef MOZ_HAVE_BITSCAN64
-#  endif
-
-#elif defined(__clang__) || defined(__GNUC__)
-
-#  if defined(__clang__)
-#    if !__has_builtin(__builtin_ctz) || !__has_builtin(__builtin_clz)
-#      error "A clang providing __builtin_c[lt]z is required to build"
-#    endif
-#  else
-     // gcc has had __builtin_clz and friends since 3.4: no need to check.
-#  endif
-
-inline uint_fast8_t
-CountLeadingZeroes32(uint32_t aValue)
-{
-  return __builtin_clz(aValue);
-}
-
-inline uint_fast8_t
-CountTrailingZeroes32(uint32_t aValue)
-{
-  return __builtin_ctz(aValue);
-}
-
-inline uint_fast8_t
-CountPopulation32(uint32_t aValue)
-{
-  return __builtin_popcount(aValue);
-}
-
-inline uint_fast8_t
-CountPopulation64(uint64_t aValue)
-{
-  return __builtin_popcountll(aValue);
-}
-
-inline uint_fast8_t
-CountLeadingZeroes64(uint64_t aValue)
-{
-  return __builtin_clzll(aValue);
-}
-
-inline uint_fast8_t
-CountTrailingZeroes64(uint64_t aValue)
-{
-  return __builtin_ctzll(aValue);
-}
-
-#else
-#  error "Implement these!"
-inline uint_fast8_t CountLeadingZeroes32(uint32_t aValue) = delete;
-inline uint_fast8_t CountTrailingZeroes32(uint32_t aValue) = delete;
-inline uint_fast8_t CountPopulation32(uint32_t aValue) = delete;
-inline uint_fast8_t CountPopulation64(uint64_t aValue) = delete;
-inline uint_fast8_t CountLeadingZeroes64(uint64_t aValue) = delete;
-inline uint_fast8_t CountTrailingZeroes64(uint64_t aValue) = delete;
-#endif
-
-} // namespace detail
-
-/**
- * Compute the number of high-order zero bits in the NON-ZERO number |aValue|.
- * That is, looking at the bitwise representation of the number, with the
- * highest- valued bits at the start, return the number of zeroes before the
- * first one is observed.
- *
- * CountLeadingZeroes32(0xF0FF1000) is 0;
- * CountLeadingZeroes32(0x7F8F0001) is 1;
- * CountLeadingZeroes32(0x3FFF0100) is 2;
- * CountLeadingZeroes32(0x1FF50010) is 3; and so on.
- */
-inline uint_fast8_t
-CountLeadingZeroes32(uint32_t aValue)
-{
-  MOZ_ASSERT(aValue != 0);
-  return detail::CountLeadingZeroes32(aValue);
-}
-
-/**
- * Compute the number of low-order zero bits in the NON-ZERO number |aValue|.
- * That is, looking at the bitwise representation of the number, with the
- * lowest- valued bits at the start, return the number of zeroes before the
- * first one is observed.
- *
- * CountTrailingZeroes32(0x0100FFFF) is 0;
- * CountTrailingZeroes32(0x7000FFFE) is 1;
- * CountTrailingZeroes32(0x0080FFFC) is 2;
- * CountTrailingZeroes32(0x0080FFF8) is 3; and so on.
- */
-inline uint_fast8_t
-CountTrailingZeroes32(uint32_t aValue)
-{
-  MOZ_ASSERT(aValue != 0);
-  return detail::CountTrailingZeroes32(aValue);
-}
-
-/**
- * Compute the number of one bits in the number |aValue|,
- */
-inline uint_fast8_t
-CountPopulation32(uint32_t aValue)
-{
-  return detail::CountPopulation32(aValue);
-}
-
-/** Analogous to CountPopulation32, but for 64-bit numbers */
-inline uint_fast8_t
-CountPopulation64(uint64_t aValue)
-{
-  return detail::CountPopulation64(aValue);
-}
-
-/** Analogous to CountLeadingZeroes32, but for 64-bit numbers. */
-inline uint_fast8_t
-CountLeadingZeroes64(uint64_t aValue)
-{
-  MOZ_ASSERT(aValue != 0);
-  return detail::CountLeadingZeroes64(aValue);
-}
-
-/** Analogous to CountTrailingZeroes32, but for 64-bit numbers. */
-inline uint_fast8_t
-CountTrailingZeroes64(uint64_t aValue)
-{
-  MOZ_ASSERT(aValue != 0);
-  return detail::CountTrailingZeroes64(aValue);
-}
-
-namespace detail {
-
-template<typename T, size_t Size = sizeof(T)>
-class CeilingLog2;
-
-template<typename T>
-class CeilingLog2<T, 4>
-{
-public:
-  static uint_fast8_t compute(const T aValue)
-  {
-    // Check for <= 1 to avoid the == 0 undefined case.
-    return aValue <= 1 ? 0u : 32u - CountLeadingZeroes32(aValue - 1);
-  }
-};
-
-template<typename T>
-class CeilingLog2<T, 8>
-{
-public:
-  static uint_fast8_t compute(const T aValue)
-  {
-    // Check for <= 1 to avoid the == 0 undefined case.
-    return aValue <= 1 ? 0u : 64u - CountLeadingZeroes64(aValue - 1);
-  }
-};
-
-} // namespace detail
-
-/**
- * Compute the log of the least power of 2 greater than or equal to |aValue|.
- *
- * CeilingLog2(0..1) is 0;
- * CeilingLog2(2) is 1;
- * CeilingLog2(3..4) is 2;
- * CeilingLog2(5..8) is 3;
- * CeilingLog2(9..16) is 4; and so on.
- */
-template<typename T>
-inline uint_fast8_t
-CeilingLog2(const T aValue)
-{
-  return detail::CeilingLog2<T>::compute(aValue);
-}
-
-/** A CeilingLog2 variant that accepts only size_t. */
-inline uint_fast8_t
-CeilingLog2Size(size_t aValue)
-{
-  return CeilingLog2(aValue);
-}
-
-namespace detail {
-
-template<typename T, size_t Size = sizeof(T)>
-class FloorLog2;
-
-template<typename T>
-class FloorLog2<T, 4>
-{
-public:
-  static uint_fast8_t compute(const T aValue)
-  {
-    return 31u - CountLeadingZeroes32(aValue | 1);
-  }
-};
-
-template<typename T>
-class FloorLog2<T, 8>
-{
-public:
-  static uint_fast8_t compute(const T aValue)
-  {
-    return 63u - CountLeadingZeroes64(aValue | 1);
-  }
-};
-
-} // namespace detail
-
-/**
- * Compute the log of the greatest power of 2 less than or equal to |aValue|.
- *
- * FloorLog2(0..1) is 0;
- * FloorLog2(2..3) is 1;
- * FloorLog2(4..7) is 2;
- * FloorLog2(8..15) is 3; and so on.
- */
-template<typename T>
-inline uint_fast8_t
-FloorLog2(const T aValue)
-{
-  return detail::FloorLog2<T>::compute(aValue);
-}
-
-/** A FloorLog2 variant that accepts only size_t. */
-inline uint_fast8_t
-FloorLog2Size(size_t aValue)
-{
-  return FloorLog2(aValue);
-}
-
-/*
- * Compute the smallest power of 2 greater than or equal to |x|.  |x| must not
- * be so great that the computed value would overflow |size_t|.
- */
-inline size_t
-RoundUpPow2(size_t aValue)
-{
-  MOZ_ASSERT(aValue <= (size_t(1) << (sizeof(size_t) * CHAR_BIT - 1)),
-             "can't round up -- will overflow!");
-  return size_t(1) << CeilingLog2(aValue);
-}
-
-/**
- * Rotates the bits of the given value left by the amount of the shift width.
- */
-template<typename T>
-inline T
-RotateLeft(const T aValue, uint_fast8_t aShift)
-{
-  MOZ_ASSERT(aShift < sizeof(T) * CHAR_BIT, "Shift value is too large!");
-  MOZ_ASSERT(aShift > 0,
-             "Rotation by value length is undefined behavior, but compilers "
-             "do not currently fold a test into the rotate instruction. "
-             "Please remove this restriction when compilers optimize the "
-             "zero case (http://blog.regehr.org/archives/1063).");
-  static_assert(IsUnsigned<T>::value, "Rotates require unsigned values");
-  return (aValue << aShift) | (aValue >> (sizeof(T) * CHAR_BIT - aShift));
-}
-
-/**
- * Rotates the bits of the given value right by the amount of the shift width.
- */
-template<typename T>
-inline T
-RotateRight(const T aValue, uint_fast8_t aShift)
-{
-  MOZ_ASSERT(aShift < sizeof(T) * CHAR_BIT, "Shift value is too large!");
-  MOZ_ASSERT(aShift > 0,
-             "Rotation by value length is undefined behavior, but compilers "
-             "do not currently fold a test into the rotate instruction. "
-             "Please remove this restriction when compilers optimize the "
-             "zero case (http://blog.regehr.org/archives/1063).");
-  static_assert(IsUnsigned<T>::value, "Rotates require unsigned values");
-  return (aValue >> aShift) | (aValue << (sizeof(T) * CHAR_BIT - aShift));
-}
-
-/**
- * Returns true if |x| is a power of two.
- * Zero is not an integer power of two. (-Inf is not an integer)
- */
-template<typename T>
-constexpr bool
-IsPowerOfTwo(T x)
-{
-    static_assert(IsUnsigned<T>::value,
-                  "IsPowerOfTwo requires unsigned values");
-    return x && (x & (x - 1)) == 0;
-}
-
-template<typename T>
-inline T
-Clamp(const T aValue, const T aMin, const T aMax)
-{
-    static_assert(IsIntegral<T>::value,
-                  "Clamp accepts only integral types, so that it doesn't have"
-                  " to distinguish differently-signed zeroes (which users may"
-                  " or may not care to distinguish, likely at a perf cost) or"
-                  " to decide how to clamp NaN or a range with a NaN"
-                  " endpoint.");
-    MOZ_ASSERT(aMin <= aMax);
-
-    if (aValue <= aMin)
-        return aMin;
-    if (aValue >= aMax)
-        return aMax;
-    return aValue;
-}
-
-namespace detail {
-
 template<typename UnsignedType>
 struct WrapToSignedHelper
 {
   static_assert(mozilla::IsUnsigned<UnsignedType>::value,
                 "WrapToSigned must be passed an unsigned type");
 
   using SignedType = typename mozilla::MakeSigned<UnsignedType>::Type;
 
@@ -608,9 +84,9 @@ template<typename UnsignedType>
 inline constexpr typename detail::WrapToSignedHelper<UnsignedType>::SignedType
 WrapToSigned(UnsignedType aValue)
 {
   return detail::WrapToSignedHelper<UnsignedType>::compute(aValue);
 }
 
 } /* namespace mozilla */
 
-#endif /* mozilla_MathAlgorithms_h */
+#endif /* mozilla_WrappingOperations_h */
--- a/mfbt/moz.build
+++ b/mfbt/moz.build
@@ -97,16 +97,17 @@ EXPORTS.mozilla = [
     'Types.h',
     'TypeTraits.h',
     'UniquePtr.h',
     'UniquePtrExtensions.h',
     'Unused.h',
     'Variant.h',
     'Vector.h',
     'WeakPtr.h',
+    'WrappingOperations.h',
     'XorShift128PlusRNG.h',
 ]
 
 EXPORTS["double-conversion"] = [
     'double-conversion/double-conversion/double-conversion.h',
     'double-conversion/double-conversion/utils.h',
 ]
 
--- a/mfbt/tests/TestMathAlgorithms.cpp
+++ b/mfbt/tests/TestMathAlgorithms.cpp
@@ -5,17 +5,16 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/MathAlgorithms.h"
 
 #include <stdint.h>
 
 using mozilla::Clamp;
 using mozilla::IsPowerOfTwo;
-using mozilla::WrapToSigned;
 
 static void
 TestClamp()
 {
   MOZ_RELEASE_ASSERT(Clamp(0, 0, 0) == 0);
   MOZ_RELEASE_ASSERT(Clamp(1, 0, 0) == 0);
   MOZ_RELEASE_ASSERT(Clamp(-1, 0, 0) == 0);
 
@@ -75,65 +74,16 @@ TestIsPowerOfTwo()
 
   static_assert(!IsPowerOfTwo(uint64_t(UINT64_MAX/2)), "0x7fffffffffffffff isn't a power of two");
   static_assert(IsPowerOfTwo(uint64_t(UINT64_MAX/2 + 1)), "0x8000000000000000 is a power of two");
   static_assert(!IsPowerOfTwo(uint64_t(UINT64_MAX/2 + 2)), "0x8000000000000001 isn't a power of two");
   static_assert(!IsPowerOfTwo(uint64_t(UINT64_MAX - 1)), "0xfffffffffffffffe isn't a power of two");
   static_assert(!IsPowerOfTwo(uint64_t(UINT64_MAX)), "0xffffffffffffffff isn't a power of two");
 }
 
-// NOTE: In places below |-FOO_MAX - 1| is used instead of |-FOO_MIN| because
-//       in C++ numeric literals are full expressions -- the |-| in a negative
-//       number is technically separate.  So with most compilers that limit
-//       |int| to the signed 32-bit range, something like |-2147483648| is
-//       operator-() applied to an *unsigned* expression.  And MSVC, at least,
-//       warns when you do that.  (The operation is well-defined, but it likely
-//       doesn't do what was intended.)  So we do the usual workaround for this
-//       (see your local copy of <stdint.h> for a likely demo of this), writing
-//       it out by negating the max value and subtracting 1.
-
-static_assert(WrapToSigned(uint8_t(17)) == 17,
-              "no wraparound should work, 8-bit");
-static_assert(WrapToSigned(uint8_t(128)) == -128,
-              "works for 8-bit numbers, wraparound low end");
-static_assert(WrapToSigned(uint8_t(128 + 7)) == -128 + 7,
-              "works for 8-bit numbers, wraparound mid");
-static_assert(WrapToSigned(uint8_t(128 + 127)) == -128 + 127,
-              "works for 8-bit numbers, wraparound high end");
-
-static_assert(WrapToSigned(uint16_t(12345)) == 12345,
-              "no wraparound should work, 16-bit");
-static_assert(WrapToSigned(uint16_t(32768)) == -32768,
-              "works for 16-bit numbers, wraparound low end");
-static_assert(WrapToSigned(uint16_t(32768 + 42)) == -32768 + 42,
-              "works for 16-bit numbers, wraparound mid");
-static_assert(WrapToSigned(uint16_t(32768 + 32767)) == -32768 + 32767,
-              "works for 16-bit numbers, wraparound high end");
-
-static_assert(WrapToSigned(uint32_t(8675309)) == 8675309,
-              "no wraparound should work, 32-bit");
-static_assert(WrapToSigned(uint32_t(2147483648)) == -2147483647 - 1,
-              "works for 32-bit numbers, wraparound low end");
-static_assert(WrapToSigned(uint32_t(2147483648 + 42)) == -2147483647 - 1 + 42,
-              "works for 32-bit numbers, wraparound mid");
-static_assert(WrapToSigned(uint32_t(2147483648 + 2147483647)) ==
-                -2147483647 - 1 + 2147483647,
-              "works for 32-bit numbers, wraparound high end");
-
-static_assert(WrapToSigned(uint64_t(4152739164)) == 4152739164,
-              "no wraparound should work, 64-bit");
-static_assert(WrapToSigned(uint64_t(9223372036854775808ULL)) == -9223372036854775807LL - 1,
-              "works for 64-bit numbers, wraparound low end");
-static_assert(WrapToSigned(uint64_t(9223372036854775808ULL + 8005552368LL)) ==
-                -9223372036854775807LL - 1 + 8005552368LL,
-              "works for 64-bit numbers, wraparound mid");
-static_assert(WrapToSigned(uint64_t(9223372036854775808ULL + 9223372036854775807ULL)) ==
-                -9223372036854775807LL - 1 + 9223372036854775807LL,
-              "works for 64-bit numbers, wraparound high end");
-
 int
 main()
 {
   TestIsPowerOfTwo();
   TestClamp();
 
   return 0;
 }
copy from mfbt/tests/TestMathAlgorithms.cpp
copy to mfbt/tests/TestWrappingOperations.cpp
--- a/mfbt/tests/TestMathAlgorithms.cpp
+++ b/mfbt/tests/TestWrappingOperations.cpp
@@ -1,90 +1,20 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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/. */
 
-#include "mozilla/MathAlgorithms.h"
+#include "mozilla/WrappingOperations.h"
 
 #include <stdint.h>
 
-using mozilla::Clamp;
-using mozilla::IsPowerOfTwo;
 using mozilla::WrapToSigned;
 
-static void
-TestClamp()
-{
-  MOZ_RELEASE_ASSERT(Clamp(0, 0, 0) == 0);
-  MOZ_RELEASE_ASSERT(Clamp(1, 0, 0) == 0);
-  MOZ_RELEASE_ASSERT(Clamp(-1, 0, 0) == 0);
-
-  MOZ_RELEASE_ASSERT(Clamp(0, 1, 1) == 1);
-  MOZ_RELEASE_ASSERT(Clamp(0, 1, 2) == 1);
-
-  MOZ_RELEASE_ASSERT(Clamp(0, -1, -1) == -1);
-  MOZ_RELEASE_ASSERT(Clamp(0, -2, -1) == -1);
-
-  MOZ_RELEASE_ASSERT(Clamp(0, 1, 3) == 1);
-  MOZ_RELEASE_ASSERT(Clamp(1, 1, 3) == 1);
-  MOZ_RELEASE_ASSERT(Clamp(2, 1, 3) == 2);
-  MOZ_RELEASE_ASSERT(Clamp(3, 1, 3) == 3);
-  MOZ_RELEASE_ASSERT(Clamp(4, 1, 3) == 3);
-  MOZ_RELEASE_ASSERT(Clamp(5, 1, 3) == 3);
-
-  MOZ_RELEASE_ASSERT(Clamp<uint8_t>(UINT8_MAX, 0, UINT8_MAX) == UINT8_MAX);
-  MOZ_RELEASE_ASSERT(Clamp<uint8_t>(0, 0, UINT8_MAX) == 0);
-
-  MOZ_RELEASE_ASSERT(Clamp<int8_t>(INT8_MIN, INT8_MIN, INT8_MAX) == INT8_MIN);
-  MOZ_RELEASE_ASSERT(Clamp<int8_t>(INT8_MIN, 0, INT8_MAX) == 0);
-  MOZ_RELEASE_ASSERT(Clamp<int8_t>(INT8_MAX, INT8_MIN, INT8_MAX) == INT8_MAX);
-  MOZ_RELEASE_ASSERT(Clamp<int8_t>(INT8_MAX, INT8_MIN, 0) == 0);
-}
-
-static void
-TestIsPowerOfTwo()
-{
-  static_assert(!IsPowerOfTwo(0u), "0 isn't a power of two");
-  static_assert(IsPowerOfTwo(1u), "1 is a power of two");
-  static_assert(IsPowerOfTwo(2u), "2 is a power of two");
-  static_assert(!IsPowerOfTwo(3u), "3 isn't a power of two");
-  static_assert(IsPowerOfTwo(4u), "4 is a power of two");
-  static_assert(!IsPowerOfTwo(5u), "5 isn't a power of two");
-  static_assert(!IsPowerOfTwo(6u), "6 isn't a power of two");
-  static_assert(!IsPowerOfTwo(7u), "7 isn't a power of two");
-  static_assert(IsPowerOfTwo(8u), "8 is a power of two");
-  static_assert(!IsPowerOfTwo(9u), "9 isn't a power of two");
-
-  static_assert(!IsPowerOfTwo(uint8_t(UINT8_MAX/2)), "127, 0x7f isn't a power of two");
-  static_assert(IsPowerOfTwo(uint8_t(UINT8_MAX/2 + 1)), "128, 0x80 is a power of two");
-  static_assert(!IsPowerOfTwo(uint8_t(UINT8_MAX/2 + 2)), "129, 0x81 isn't a power of two");
-  static_assert(!IsPowerOfTwo(uint8_t(UINT8_MAX - 1)), "254, 0xfe isn't a power of two");
-  static_assert(!IsPowerOfTwo(uint8_t(UINT8_MAX)), "255, 0xff isn't a power of two");
-
-  static_assert(!IsPowerOfTwo(uint16_t(UINT16_MAX/2)), "0x7fff isn't a power of two");
-  static_assert(IsPowerOfTwo(uint16_t(UINT16_MAX/2 + 1)), "0x8000 is a power of two");
-  static_assert(!IsPowerOfTwo(uint16_t(UINT16_MAX/2 + 2)), "0x8001 isn't a power of two");
-  static_assert(!IsPowerOfTwo(uint16_t(UINT16_MAX - 1)), "0xfffe isn't a power of two");
-  static_assert(!IsPowerOfTwo(uint16_t(UINT16_MAX)), "0xffff isn't a power of two");
-
-  static_assert(!IsPowerOfTwo(uint32_t(UINT32_MAX/2)), "0x7fffffff isn't a power of two");
-  static_assert(IsPowerOfTwo(uint32_t(UINT32_MAX/2 + 1)), "0x80000000 is a power of two");
-  static_assert(!IsPowerOfTwo(uint32_t(UINT32_MAX/2 + 2)), "0x80000001 isn't a power of two");
-  static_assert(!IsPowerOfTwo(uint32_t(UINT32_MAX - 1)), "0xfffffffe isn't a power of two");
-  static_assert(!IsPowerOfTwo(uint32_t(UINT32_MAX)), "0xffffffff isn't a power of two");
-
-  static_assert(!IsPowerOfTwo(uint64_t(UINT64_MAX/2)), "0x7fffffffffffffff isn't a power of two");
-  static_assert(IsPowerOfTwo(uint64_t(UINT64_MAX/2 + 1)), "0x8000000000000000 is a power of two");
-  static_assert(!IsPowerOfTwo(uint64_t(UINT64_MAX/2 + 2)), "0x8000000000000001 isn't a power of two");
-  static_assert(!IsPowerOfTwo(uint64_t(UINT64_MAX - 1)), "0xfffffffffffffffe isn't a power of two");
-  static_assert(!IsPowerOfTwo(uint64_t(UINT64_MAX)), "0xffffffffffffffff isn't a power of two");
-}
-
 // NOTE: In places below |-FOO_MAX - 1| is used instead of |-FOO_MIN| because
 //       in C++ numeric literals are full expressions -- the |-| in a negative
 //       number is technically separate.  So with most compilers that limit
 //       |int| to the signed 32-bit range, something like |-2147483648| is
 //       operator-() applied to an *unsigned* expression.  And MSVC, at least,
 //       warns when you do that.  (The operation is well-defined, but it likely
 //       doesn't do what was intended.)  So we do the usual workaround for this
 //       (see your local copy of <stdint.h> for a likely demo of this), writing
@@ -127,13 +57,10 @@ static_assert(WrapToSigned(uint64_t(9223
               "works for 64-bit numbers, wraparound mid");
 static_assert(WrapToSigned(uint64_t(9223372036854775808ULL + 9223372036854775807ULL)) ==
                 -9223372036854775807LL - 1 + 9223372036854775807LL,
               "works for 64-bit numbers, wraparound high end");
 
 int
 main()
 {
-  TestIsPowerOfTwo();
-  TestClamp();
-
   return 0;
 }
--- a/mfbt/tests/moz.build
+++ b/mfbt/tests/moz.build
@@ -53,16 +53,17 @@ CppUnitTests([
     'TestThreadSafeWeakPtr',
     'TestTuple',
     'TestTypedEnum',
     'TestTypeTraits',
     'TestUniquePtr',
     'TestVariant',
     'TestVector',
     'TestWeakPtr',
+    'TestWrappingOperations',
     'TestXorShift128PlusRNG',
 ])
 
 if not CONFIG['MOZ_ASAN']:
     CppUnitTests([
         'TestPoisonArea',
     ])