Bug 1441657 - Implement mozilla::WrappingMultiply. r=froydnj
authorJeff Walden <jwalden@mit.edu>
Thu, 15 Feb 2018 17:36:55 -0800
changeset 458632 cf32e82edfb795a94740fef748c9ef503425e448
parent 458631 bc01e52c16d77ba3a7ab45ec195892c88e5f861b
child 458633 0dc87bc686083ce1ee3721c6cfb38f31954f24bd
push id8808
push userarchaeopteryx@coole-files.de
push dateFri, 02 Mar 2018 22:13:05 +0000
treeherdermozilla-beta@7475508d19db [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 - Implement mozilla::WrappingMultiply. r=froydnj
build/sanitizers/ubsan_signed_overflow_blacklist.txt
build/sanitizers/ubsan_unsigned_overflow_blacklist.txt
js/public/Utility.h
js/src/jsmath.cpp
mfbt/HashFunctions.h
mfbt/WrappingOperations.h
mfbt/tests/TestWrappingOperations.cpp
--- a/build/sanitizers/ubsan_signed_overflow_blacklist.txt
+++ b/build/sanitizers/ubsan_signed_overflow_blacklist.txt
@@ -243,19 +243,16 @@ src:*/security/nss/lib/dbm/src/h_func.c
 src:*/security/nss/lib/freebl/sha512.c
 src:*/security/nss/lib/freebl/md5.c
 src:*/XorShift128PlusRNG.h
 src:*/xpcom/ds/PLDHashTable.cpp
 
 # Hash/Cache function in Skia
 fun:*GradientShaderCache*Build32bitCache*
 
-# Hash function in js/public/Utility.h
-fun:ScrambleHashCode*
-
 # Hashing functions in Cairo
 fun:*_hash_matrix_fnv*
 fun:*_hash_mix_bits*
 fun:*_cairo_hash_string*
 fun:*_cairo_hash_bytes*
 
 # Hash function in modules/libjar/nsZipArchive.cpp
 fun:*HashName*
--- a/build/sanitizers/ubsan_unsigned_overflow_blacklist.txt
+++ b/build/sanitizers/ubsan_unsigned_overflow_blacklist.txt
@@ -250,19 +250,16 @@ src:*/security/nss/lib/dbm/src/h_func.c
 src:*/security/nss/lib/freebl/sha512.c
 src:*/security/nss/lib/freebl/md5.c
 src:*/XorShift128PlusRNG.h
 src:*/xpcom/ds/PLDHashTable.cpp
 
 # Hash/Cache function in Skia
 fun:*GradientShaderCache*Build32bitCache*
 
-# Hash function in js/public/Utility.h
-fun:ScrambleHashCode*
-
 # Hashing functions in Cairo
 fun:*_hash_matrix_fnv*
 fun:*_hash_mix_bits*
 fun:*_cairo_hash_string*
 fun:*_cairo_hash_bytes*
 
 # Hash function in modules/libjar/nsZipArchive.cpp
 fun:*HashName*
--- a/js/public/Utility.h
+++ b/js/public/Utility.h
@@ -10,16 +10,17 @@
 #include "mozilla/Assertions.h"
 #include "mozilla/Atomics.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/Compiler.h"
 #include "mozilla/Move.h"
 #include "mozilla/Scoped.h"
 #include "mozilla/TemplateLib.h"
 #include "mozilla/UniquePtr.h"
+#include "mozilla/WrappingOperations.h"
 
 #include <stdlib.h>
 #include <string.h>
 
 #ifdef JS_OOM_DO_BACKTRACES
 #include <execinfo.h>
 #include <stdio.h>
 #endif
@@ -692,17 +693,17 @@ ScrambleHashCode(HashNumber h)
      * Programming, 6.4. This mixes all the bits of the input hash code h.
      *
      * The value of goldenRatio is taken from the hex
      * expansion of the golden ratio, which starts 1.9E3779B9....
      * This value is especially good if values with consecutive hash codes
      * are stored in a hash table; see Knuth for details.
      */
     static const HashNumber goldenRatio = 0x9E3779B9U;
-    return h * goldenRatio;
+    return mozilla::WrappingMultiply(h, goldenRatio);
 }
 
 } /* namespace detail */
 
 } /* namespace js */
 
 /* sixgill annotation defines */
 #ifndef HAVE_STATIC_ANNOTATIONS
--- a/js/src/jsmath.cpp
+++ b/js/src/jsmath.cpp
@@ -110,17 +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 mozilla::WrappingMultiply;
 using JS::ToNumber;
 using JS::GenericNaN;
 
 static const JSConstDoubleSpec math_constants[] = {
     {"E"      ,  M_E       },
     {"LOG2E"  ,  M_LOG2E   },
     {"LOG10E" ,  M_LOG10E  },
     {"LN2"    ,  M_LN2     },
@@ -479,24 +479,23 @@ js::math_floor(JSContext* cx, unsigned a
     }
 
     return math_floor_handle(cx, args[0], args.rval());
 }
 
 bool
 js::math_imul_handle(JSContext* cx, HandleValue lhs, HandleValue rhs, MutableHandleValue res)
 {
-    uint32_t a = 0, b = 0;
-    if (!lhs.isUndefined() && !ToUint32(cx, lhs, &a))
+    int32_t a = 0, b = 0;
+    if (!lhs.isUndefined() && !ToInt32(cx, lhs, &a))
         return false;
-    if (!rhs.isUndefined() && !ToUint32(cx, rhs, &b))
+    if (!rhs.isUndefined() && !ToInt32(cx, rhs, &b))
         return false;
 
-    uint32_t product = a * b;
-    res.setInt32(WrapToSigned(product));
+    res.setInt32(WrappingMultiply(a, b));
     return true;
 }
 
 bool
 js::math_imul(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
--- a/mfbt/HashFunctions.h
+++ b/mfbt/HashFunctions.h
@@ -47,16 +47,17 @@
 #ifndef mozilla_HashFunctions_h
 #define mozilla_HashFunctions_h
 
 #include "mozilla/Assertions.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/Char16.h"
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/Types.h"
+#include "mozilla/WrappingOperations.h"
 
 #include <stdint.h>
 
 #ifdef __cplusplus
 namespace mozilla {
 
 /**
  * The golden ratio as a 32-bit fixed-point value.
@@ -91,17 +92,19 @@ AddU32ToHash(uint32_t aHash, uint32_t aV
    *
    * The rotation length of 5 is also arbitrary, although an odd number is again
    * preferable so our hash explores the whole universe of possible rotations.
    *
    * Finally, we multiply by the golden ratio *after* xor'ing, not before.
    * Otherwise, if |aHash| is 0 (as it often is for the beginning of a
    * message), the expression
    *
-   *   (kGoldenRatioU32 * RotateBitsLeft(aHash, 5)) |xor| aValue
+   *   mozilla::WrappingMultiply(kGoldenRatioU32, RotateBitsLeft(aHash, 5))
+   *   |xor|
+   *   aValue
    *
    * evaluates to |aValue|.
    *
    * (Number-theoretic aside: Because any odd number |m| is relatively prime to
    * our modulus (2^32), the list
    *
    *    [x * m (mod 2^32) for 0 <= x < 2^32]
    *
@@ -109,17 +112,18 @@ AddU32ToHash(uint32_t aHash, uint32_t aV
    * cause us to skip any possible hash values.
    *
    * It's also nice if |m| has large-ish order mod 2^32 -- that is, if the
    * smallest k such that m^k == 1 (mod 2^32) is large -- so we can safely
    * multiply our hash value by |m| a few times without negating the
    * multiplicative effect.  Our golden ratio constant has order 2^29, which is
    * more than enough for our purposes.)
    */
-  return kGoldenRatioU32 * (RotateBitsLeft32(aHash, 5) ^ aValue);
+  return mozilla::WrappingMultiply(kGoldenRatioU32,
+                                   (RotateBitsLeft32(aHash, 5) ^ aValue));
 }
 
 /**
  * AddUintptrToHash takes sizeof(uintptr_t) as a template parameter.
  */
 template<size_t PtrSize>
 inline uint32_t
 AddUintptrToHash(uint32_t aHash, uintptr_t aValue)
--- a/mfbt/WrappingOperations.h
+++ b/mfbt/WrappingOperations.h
@@ -1,16 +1,18 @@
 /* -*- 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/. */
 
 /*
- * Math operations that implement wraparound semantics on overflow or underflow.
+ * Math operations that implement wraparound semantics on overflow or underflow
+ * without performing C++ undefined behavior or tripping up compiler-based
+ * integer-overflow sanitizers.
  */
 
 #ifndef mozilla_WrappingOperations_h
 #define mozilla_WrappingOperations_h
 
 #include "mozilla/Attributes.h"
 #include "mozilla/TypeTraits.h"
 
@@ -82,11 +84,105 @@ struct WrapToSignedHelper
  */
 template<typename UnsignedType>
 inline constexpr typename detail::WrapToSignedHelper<UnsignedType>::SignedType
 WrapToSigned(UnsignedType aValue)
 {
   return detail::WrapToSignedHelper<UnsignedType>::compute(aValue);
 }
 
+namespace detail {
+
+template<typename T>
+struct WrappingMultiplyHelper
+{
+private:
+  using UnsignedT = typename MakeUnsigned<T>::Type;
+
+  MOZ_NO_SANITIZE_UNSIGNED_OVERFLOW
+  static UnsignedT
+  multiply(UnsignedT aX, UnsignedT aY)
+  {
+  // |mozilla::WrappingMultiply| isn't constexpr because MSVC warns about well-
+  // defined unsigned integer overflows that may happen here.
+  // https://msdn.microsoft.com/en-us/library/4kze989h.aspx  And constexpr
+  // seems to cause the warning to be emitted at |WrappingMultiply| call *sites*
+  // instead of here, so these #pragmas are ineffective.
+  //
+  // https://stackoverflow.com/questions/37658794/integer-constant-overflow-warning-in-constexpr
+  //
+  // If/when MSVC fix this bug, we should make these functions constexpr.
+
+    // Begin with |1U| to ensure the overall operation chain is never promoted
+    // to signed integer operations that might have *signed* integer overflow.
+    return static_cast<UnsignedT>(1U * aX * aY);
+  }
+
+  static T
+  toResult(UnsignedT aX, UnsignedT aY)
+  {
+    // We could always return WrapToSigned and rely on unsigned conversion
+    // undoing the wrapping when |T| is unsigned, but this seems clearer.
+    return IsSigned<T>::value
+           ? WrapToSigned(multiply(aX, aY))
+           : multiply(aX, aY);
+  }
+
+public:
+  MOZ_NO_SANITIZE_UNSIGNED_OVERFLOW
+  static T compute(T aX, T aY)
+  {
+    return toResult(static_cast<UnsignedT>(aX), static_cast<UnsignedT>(aY));
+  }
+};
+
+} // namespace detail
+
+/**
+ * Multiply two integers of the same type, and return the result converted to
+ * that type using wraparound semantics.  This function:
+ *
+ *   1) makes explicit the desire for and dependence upon wraparound semantics,
+ *   2) provides wraparound semantics *safely* with no signed integer overflow
+ *      that would have undefined behavior, and
+ *   3) won't trip up {,un}signed-integer overflow sanitizers (see
+ *      build/autoconf/sanitize.m4) at runtime.
+ *
+ * For N-bit unsigned integer types, this is equivalent to multiplying the two
+ * numbers, then taking the result mod 2**N:
+ *
+ *   WrappingMultiply(uint32_t(42), uint32_t(17)) is 714 (714 mod 2**32);
+ *   WrappingMultiply(uint8_t(16), uint8_t(24)) is 128 (384 mod 2**8);
+ *   WrappingMultiply(uint16_t(3), uint16_t(32768)) is 32768 (98304 mod 2*16).
+ *
+ * Use this function for any unsigned multiplication that can wrap (instead of
+ * normal C++ multiplication) to play nice with the sanitizers.  But it's
+ * especially important to use it for uint16_t multiplication: in most compilers
+ * for uint16_t*uint16_t some operand values will trigger signed integer
+ * overflow with undefined behavior!  http://kqueue.org/blog/2013/09/17/cltq/
+ * has the grody details.  Other than that one weird case, WrappingMultiply on
+ * unsigned types is the same as C++ multiplication.
+ *
+ * For N-bit signed integer types, this is equivalent to multiplying the two
+ * numbers wrapped to unsigned, taking the product mod 2**N, then wrapping that
+ * number to the signed range:
+ *
+ *   WrappingMultiply(int16_t(-456), int16_t(123)) is 9448 ((-56088 mod 2**16) + 2**16);
+ *   WrappingMultiply(int32_t(-7), int32_t(-9)) is 63 (63 mod 2**32);
+ *   WrappingMultiply(int8_t(16), int8_t(24)) is -128 ((384 mod 2**8) - 2**8);
+ *   WrappingMultiply(int8_t(16), int8_t(255)) is -16 ((4080 mod 2**8) - 2**8).
+ *
+ * There is no ready equivalent to this operation in C++, as applying C++
+ * multiplication to signed integer types in ways that trigger overflow has
+ * undefined behavior.  However, it's how multiplication *tends* to behave with
+ * most compilers in most situations, even though it's emphatically not required
+ * to do so.
+ */
+template<typename T>
+inline T
+WrappingMultiply(T aX, T aY)
+{
+  return detail::WrappingMultiplyHelper<T>::compute(aX, aY);
+}
+
 } /* namespace mozilla */
 
 #endif /* mozilla_WrappingOperations_h */
--- a/mfbt/tests/TestWrappingOperations.cpp
+++ b/mfbt/tests/TestWrappingOperations.cpp
@@ -1,18 +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/Assertions.h"
 #include "mozilla/WrappingOperations.h"
 
 #include <stdint.h>
 
+using mozilla::WrappingMultiply;
 using mozilla::WrapToSigned;
 
 // 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
@@ -54,13 +56,193 @@ static_assert(WrapToSigned(uint64_t(9223
               "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");
 
+template<typename T>
+inline constexpr bool
+TestEqual(T aX, T aY)
+{
+  return aX == aY;
+}
+
+static void
+TestWrappingMultiply8()
+{
+  MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(uint8_t(0), uint8_t(128)),
+                               uint8_t(0)),
+                     "zero times anything is zero");
+  MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(uint8_t(128), uint8_t(1)),
+                               uint8_t(128)),
+                     "1 times anything is anything");
+  MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(uint8_t(2), uint8_t(128)),
+                               uint8_t(0)),
+                     "2 times high bit overflows, produces zero");
+  MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(uint8_t(8), uint8_t(16)),
+                               uint8_t(128)),
+                     "multiply that populates the high bit produces that value");
+  MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(uint8_t(127), uint8_t(127)),
+                               uint8_t(1)),
+                     "multiplying signed maxvals overflows all the way to 1");
+
+  MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(int8_t(0), int8_t(-128)),
+                               int8_t(0)),
+                     "zero times anything is zero");
+  MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(int8_t(-128), int8_t(1)),
+                               int8_t(-128)),
+                     "1 times anything is anything");
+  MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(int8_t(2), int8_t(-128)),
+                               int8_t(0)),
+                     "2 times min overflows, produces zero");
+  MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(int8_t(16), int8_t(24)),
+                               int8_t(-128)),
+                     "multiply that populates the sign bit produces minval");
+  MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(int8_t(8), int8_t(16)),
+                               int8_t(-128)),
+                     "multiply that populates the sign bit produces minval");
+  MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(int8_t(127), int8_t(127)),
+                               int8_t(1)),
+                     "multiplying maxvals overflows all the way to 1");
+}
+
+static void
+TestWrappingMultiply16()
+{
+  MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(uint16_t(0), uint16_t(32768)),
+                               uint16_t(0)),
+                     "zero times anything is zero");
+  MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(uint16_t(32768), uint16_t(1)),
+                               uint16_t(32768)),
+                     "1 times anything is anything");
+  MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(uint16_t(2), uint16_t(32768)),
+                               uint16_t(0)),
+                     "2 times high bit overflows, produces zero");
+  MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(uint16_t(3), uint16_t(32768)),
+                               uint16_t(-32768)),
+                     "3 * 32768 - 65536 is 32768");
+  MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(uint16_t(64), uint16_t(512)),
+                               uint16_t(32768)),
+                     "multiply that populates the high bit produces that value");
+  MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(uint16_t(32767), uint16_t(32767)),
+                               uint16_t(1)),
+                    "multiplying signed maxvals overflows all the way to 1");
+
+  MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(int16_t(0), int16_t(-32768)),
+                               int16_t(0)),
+                     "zero times anything is zero");
+  MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(int16_t(-32768), int16_t(1)),
+                               int16_t(-32768)),
+                     "1 times anything is anything");
+  MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(int16_t(-456), int16_t(123)),
+                               int16_t(9448)),
+                     "multiply opposite signs, then add 2**16 for the result");
+  MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(int16_t(2), int16_t(-32768)),
+                               int16_t(0)),
+                     "2 times min overflows, produces zero");
+  MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(int16_t(64), int16_t(512)),
+                               int16_t(-32768)),
+                     "multiply that populates the sign bit produces minval");
+  MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(int16_t(32767), int16_t(32767)),
+                               int16_t(1)),
+                     "multiplying maxvals overflows all the way to 1");
+}
+
+static void
+TestWrappingMultiply32()
+{
+  MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(uint32_t(0), uint32_t(2147483648)),
+                               uint32_t(0)),
+                    "zero times anything is zero");
+  MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(uint32_t(42), uint32_t(17)),
+                               uint32_t(714)),
+                     "42 * 17 is 714 without wraparound");
+  MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(uint32_t(2147483648), uint32_t(1)),
+                               uint32_t(2147483648)),
+                     "1 times anything is anything");
+  MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(uint32_t(2), uint32_t(2147483648)),
+                               uint32_t(0)),
+                     "2 times high bit overflows, produces zero");
+  MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(uint32_t(8192), uint32_t(262144)),
+                               uint32_t(2147483648)),
+                     "multiply that populates the high bit produces that value");
+  MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(uint32_t(2147483647),
+                                                uint32_t(2147483647)),
+                               uint32_t(1)),
+                     "multiplying signed maxvals overflows all the way to 1");
+
+  MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(int32_t(0), int32_t(-2147483647 - 1)),
+                               int32_t(0)),
+                     "zero times anything is zero");
+  MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(int32_t(-2147483647 - 1), int32_t(1)),
+                               int32_t(-2147483647 - 1)),
+                     "1 times anything is anything");
+  MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(int32_t(2), int32_t(-2147483647 - 1)),
+                               int32_t(0)),
+                     "2 times min overflows, produces zero");
+  MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(int32_t(-7), int32_t(-9)),
+                               int32_t(63)),
+                     "-7 * -9 is 63, no wraparound needed");
+  MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(int32_t(8192), int32_t(262144)),
+                               int32_t(-2147483647 - 1)),
+                     "multiply that populates the sign bit produces minval");
+  MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(int32_t(2147483647), int32_t(2147483647)),
+                               int32_t(1)),
+                     "multiplying maxvals overflows all the way to 1");
+}
+
+static void
+TestWrappingMultiply64()
+{
+  MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(uint64_t(0),
+                                                uint64_t(9223372036854775808ULL)),
+                               uint64_t(0)),
+                     "zero times anything is zero");
+  MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(uint64_t(9223372036854775808ULL),
+                                                uint64_t(1)),
+                               uint64_t(9223372036854775808ULL)),
+                     "1 times anything is anything");
+  MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(uint64_t(2),
+                                                uint64_t(9223372036854775808ULL)),
+                               uint64_t(0)),
+                     "2 times high bit overflows, produces zero");
+  MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(uint64_t(131072),
+                                                uint64_t(70368744177664)),
+                               uint64_t(9223372036854775808ULL)),
+                     "multiply that populates the high bit produces that value");
+  MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(uint64_t(9223372036854775807),
+                                                uint64_t(9223372036854775807)),
+                               uint64_t(1)),
+                     "multiplying signed maxvals overflows all the way to 1");
+
+  MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(int64_t(0), int64_t(-9223372036854775807 - 1)),
+                               int64_t(0)),
+                     "zero times anything is zero");
+  MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(int64_t(-9223372036854775807 - 1),
+                                                int64_t(1)),
+                               int64_t(-9223372036854775807 - 1)),
+                     "1 times anything is anything");
+  MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(int64_t(2),
+                                                int64_t(-9223372036854775807 - 1)),
+                               int64_t(0)),
+                     "2 times min overflows, produces zero");
+  MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(int64_t(131072),
+                                                int64_t(70368744177664)),
+                               int64_t(-9223372036854775807 - 1)),
+                     "multiply that populates the sign bit produces minval");
+  MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(int64_t(9223372036854775807),
+                                                int64_t(9223372036854775807)),
+                               int64_t(1)),
+                     "multiplying maxvals overflows all the way to 1");
+}
+
 int
 main()
 {
+  TestWrappingMultiply8();
+  TestWrappingMultiply16();
+  TestWrappingMultiply32();
+  TestWrappingMultiply64();
   return 0;
 }