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 762439 bc01e52c16d77ba3a7ab45ec195892c88e5f861b
parent 762438 f68825f0495dba102d4f1d73d246095e7c82e214
child 762440 cf32e82edfb795a94740fef748c9ef503425e448
push id101169
push userluca.greco@alcacoop.it
push dateFri, 02 Mar 2018 12:32:00 +0000
reviewersfroydnj
bugs1441657
milestone60.0a1
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',
     ])