]> Savannah Git Hosting - gnulib.git/commitdiff
intprops: support unsigned *_WRAPV results
authorPaul Eggert <eggert@cs.ucla.edu>
Thu, 15 Aug 2019 00:43:46 +0000 (17:43 -0700)
committerPaul Eggert <eggert@cs.ucla.edu>
Thu, 15 Aug 2019 00:44:22 +0000 (17:44 -0700)
Add support for unsigned, unsigned long, and unsigned long long
results to INT_ADD_WRAPV, INT_SUBTRACT_WRAPV, and
INT_MULTIPLY_WRAPV.  Also, work around GCC bug 91450, and fix a
bug with unsigned inputs reported by Eli Zaretskii in:
https://lists.gnu.org/r/bug-gnulib/2019-08/msg00012.html
* config/srclist.txt: Break the glibc connection for intprops.h
temporarily, while more testing is done in Gnulib-using apps.
* lib/intprops.h (INT_ADD_WRAPV, INT_SUBTRACT_WRAPV)
(INT_MULTIPLY_WRAPV, _GL_INT_OP_WRAPV, _GL_INT_OP_WRAPV_LONGISH):
Support unsigned results no narrower than unsigned int.  Report
overflow correctly if some arguments are unsigned.
(_GL_BUILTIN_MUL_OVERFLOW): New macro, to work around GCC bug 91450.
(_GL_INT_OP_CALC): Simplify now that the OVERFLOW argument does
the right thing with narrow args.
(_GL_INT_OP_CALC1): Remove.  All callers removed.
(_GL_INT_ADD_RANGE_OVERFLOW, _GL_INT_SUBTRACT_RANGE_OVERFLOW)
(_GL_INT_MULTIPLY_RANGE_OVERFLOW): New macros.
* tests/test-intprops.c: Check for bugs and test new behavior.

ChangeLog
config/srclist.txt
lib/intprops.h
tests/test-intprops.c

index 4d1eeddfc1cb4e8d58f7908ea67871c1f4c7ab1a..7fe978f4a8b7b938f02a7f940997843b589bc504 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,25 @@
+2019-08-14  Paul Eggert  <eggert@cs.ucla.edu>
+
+       intprops: support unsigned *_WRAPV results
+       Add support for unsigned, unsigned long, and unsigned long long
+       results to INT_ADD_WRAPV, INT_SUBTRACT_WRAPV, and
+       INT_MULTIPLY_WRAPV.  Also, work around GCC bug 91450, and fix a
+       bug with unsigned inputs reported by Eli Zaretskii in:
+       https://lists.gnu.org/r/bug-gnulib/2019-08/msg00012.html
+       * config/srclist.txt: Break the glibc connection for intprops.h
+       temporarily, while more testing is done in Gnulib-using apps.
+       * lib/intprops.h (INT_ADD_WRAPV, INT_SUBTRACT_WRAPV)
+       (INT_MULTIPLY_WRAPV, _GL_INT_OP_WRAPV, _GL_INT_OP_WRAPV_LONGISH):
+       Support unsigned results no narrower than unsigned int.  Report
+       overflow correctly if some arguments are unsigned.
+       (_GL_BUILTIN_MUL_OVERFLOW): New macro, to work around GCC bug 91450.
+       (_GL_INT_OP_CALC): Simplify now that the OVERFLOW argument does
+       the right thing with narrow args.
+       (_GL_INT_OP_CALC1): Remove.  All callers removed.
+       (_GL_INT_ADD_RANGE_OVERFLOW, _GL_INT_SUBTRACT_RANGE_OVERFLOW)
+       (_GL_INT_MULTIPLY_RANGE_OVERFLOW): New macros.
+       * tests/test-intprops.c: Check for bugs and test new behavior.
+
 2019-08-14  Bruno Haible  <bruno@clisp.org>
 
        get_progname_of: New module.
index b86e6a57aa22f2b94f84e6a7c56a3cba2ba7592e..bb6ad87abd426d8008b34c99ea32d5b4e9de23e3 100644 (file)
@@ -46,7 +46,8 @@ $GNUORG Copyright/request-assign.future               doc/Copyright
 $GNUORG Copyright/request-assign.program       doc/Copyright
 $GNUORG Copyright/request-disclaim.changes     doc/Copyright
 
-$LIBCSRC include/intprops.h             lib
+# Temporarily newer in Gnulib than in glibc.
+#$LIBCSRC include/intprops.h             lib
 $LIBCSRC posix/regcomp.c               lib
 $LIBCSRC posix/regex.c                 lib
 $LIBCSRC posix/regex.h                 lib
index 140f6d2a4bebd3ddc8254c8d247db91b4a75014a..556cadec801d72946585a3b6fdec32d2aadf4ef8 100644 (file)
    Subtract 1 for the sign bit if T is signed, and then add 1 more for
    a minus sign if needed.
 
-   Because _GL_SIGNED_TYPE_OR_EXPR sometimes returns 0 when its argument is
-   signed, this macro may overestimate the true bound by one byte when
+   Because _GL_SIGNED_TYPE_OR_EXPR sometimes returns 1 when its argument is
+   unsigned, this macro may overestimate the true bound by one byte when
    applied to unsigned types of size 2, 4, 16, ... bytes.  */
 #define INT_STRLEN_BOUND(t)                                     \
   (INT_BITS_STRLEN_BOUND (TYPE_WIDTH (t) - _GL_SIGNED_TYPE_OR_EXPR (t)) \
 
    The INT_<op>_OVERFLOW macros return 1 if the corresponding C operators
    might not yield numerically correct answers due to arithmetic overflow.
-   The INT_<op>_WRAPV macros also store the low-order bits of the answer.
+   The INT_<op>_WRAPV macros compute the low-order bits of the sum,
+   difference, and product of two C integers, and return 1 if these
+   low-order bits are not numerically correct.
    These macros work correctly on all known practical hosts, and do not rely
    on undefined behavior due to signed arithmetic overflow.
 
    arguments should not have side effects.
 
    The WRAPV macros are not constant expressions.  They support only
-   +, binary -, and *.  The result type must be signed.
+   +, binary -, and *.  The result type must be either signed, or an
+   unsigned type that is 'unsigned int' or wider.  Because the WRAPV
+   macros convert the result, the report overflow in different
+   circumstances than the OVERFLOW macros do.
 
-   These macros are tuned for their last argument being a constant.
+   These macros are tuned for their last input argument being a constant.
 
    Return 1 if the integer expressions A * B, A - B, -A, A * B, A / B,
    A % B, and A << B would overflow, respectively.  */
 /* Store the low-order bits of A + B, A - B, A * B, respectively, into *R.
    Return 1 if the result overflows.  See above for restrictions.  */
 #define INT_ADD_WRAPV(a, b, r) \
-  _GL_INT_OP_WRAPV (a, b, r, +, __builtin_add_overflow, INT_ADD_OVERFLOW)
+  _GL_INT_OP_WRAPV (a, b, r, +, __builtin_add_overflow, \
+                    _GL_INT_ADD_RANGE_OVERFLOW)
 #define INT_SUBTRACT_WRAPV(a, b, r) \
-  _GL_INT_OP_WRAPV (a, b, r, -, __builtin_sub_overflow, INT_SUBTRACT_OVERFLOW)
+  _GL_INT_OP_WRAPV (a, b, r, -, __builtin_sub_overflow, \
+                    _GL_INT_SUBTRACT_RANGE_OVERFLOW)
 #define INT_MULTIPLY_WRAPV(a, b, r) \
-  _GL_INT_OP_WRAPV (a, b, r, *, __builtin_mul_overflow, INT_MULTIPLY_OVERFLOW)
+   _GL_INT_OP_WRAPV (a, b, r, *, _GL_BUILTIN_MUL_OVERFLOW, \
+                     _GL_INT_MULTIPLY_RANGE_OVERFLOW)
+
+/* Like __builtin_mul_overflow, but work around GCC bug 91450.  */
+#define _GL_BUILTIN_MUL_OVERFLOW(a, b, r) \
+  ((!_GL_SIGNED_TYPE_OR_EXPR (*(r)) && EXPR_SIGNED (a) && EXPR_SIGNED (b) \
+    && _GL_INT_MULTIPLY_RANGE_OVERFLOW (a, b, 0, (__typeof__ (*(r))) -1)) \
+   ? (__builtin_mul_overflow (a, b, r), 1) \
+   : __builtin_mul_overflow (a, b, r))
 
 /* Nonzero if this compiler has GCC bug 68193 or Clang bug 25390.  See:
    https://gcc.gnu.org/bugzilla/show_bug.cgi?id=68193
      signed char: \
        _GL_INT_OP_CALC (a, b, r, op, overflow, unsigned int, \
                         signed char, SCHAR_MIN, SCHAR_MAX), \
+     unsigned char: \
+       _GL_INT_OP_CALC (a, b, r, op, overflow, unsigned int, \
+                        unsigned char, 0, UCHAR_MAX), \
      short int: \
        _GL_INT_OP_CALC (a, b, r, op, overflow, unsigned int, \
                         short int, SHRT_MIN, SHRT_MAX), \
+     unsigned short int: \
+       _GL_INT_OP_CALC (a, b, r, op, overflow, unsigned int, \
+                        unsigned short int, 0, USHRT_MAX), \
      int: \
        _GL_INT_OP_CALC (a, b, r, op, overflow, unsigned int, \
                         int, INT_MIN, INT_MAX), \
+     unsigned int: \
+       _GL_INT_OP_CALC (a, b, r, op, overflow, unsigned int, \
+                        unsigned int, 0, UINT_MAX), \
      long int: \
        _GL_INT_OP_CALC (a, b, r, op, overflow, unsigned long int, \
                         long int, LONG_MIN, LONG_MAX), \
+     unsigned long int: \
+       _GL_INT_OP_CALC (a, b, r, op, overflow, unsigned long int, \
+                        unsigned long int, 0, ULONG_MAX), \
      long long int: \
        _GL_INT_OP_CALC (a, b, r, op, overflow, unsigned long long int, \
-                        long long int, LLONG_MIN, LLONG_MAX)))
+                        long long int, LLONG_MIN, LLONG_MAX),
+     unsigned long long int: \
+       _GL_INT_OP_CALC (a, b, r, op, overflow, unsigned long long int, \
+                        unsigned long long int, ULLONG_MIN, ULLONG_MAX)))
 #else
+/* This fallback implementation uses _GL_SIGNED_TYPE_OR_EXPR, and so
+   may guess wrong on some non-GNU pre-C11 compilers when the type of
+   *R is unsigned char or unsigned short.  This is why the
+   documentation for INT_ADD_WRAPV says that the result type, if
+   unsigned, should be unsigned int or wider.  */
 # define _GL_INT_OP_WRAPV(a, b, r, op, builtin, overflow) \
    (sizeof *(r) == sizeof (signed char) \
-    ? _GL_INT_OP_CALC (a, b, r, op, overflow, unsigned int, \
-                       signed char, SCHAR_MIN, SCHAR_MAX) \
+    ? (_GL_SIGNED_TYPE_OR_EXPR (*(r)) \
+       ? _GL_INT_OP_CALC (a, b, r, op, overflow, unsigned int, \
+                          signed char, SCHAR_MIN, SCHAR_MAX) \
+       : _GL_INT_OP_CALC (a, b, r, op, overflow, unsigned int, \
+                          unsigned char, 0, UCHAR_MAX)) \
     : sizeof *(r) == sizeof (short int) \
-    ? _GL_INT_OP_CALC (a, b, r, op, overflow, unsigned int, \
-                       short int, SHRT_MIN, SHRT_MAX) \
+    ? (_GL_SIGNED_TYPE_OR_EXPR (*(r)) \
+       ? _GL_INT_OP_CALC (a, b, r, op, overflow, unsigned int, \
+                          short int, SHRT_MIN, SHRT_MAX) \
+       : _GL_INT_OP_CALC (a, b, r, op, overflow, unsigned int, \
+                          unsigned short int, 0, USHRT_MAX)) \
     : sizeof *(r) == sizeof (int) \
-    ? _GL_INT_OP_CALC (a, b, r, op, overflow, unsigned int, \
-                       int, INT_MIN, INT_MAX) \
+    ? (EXPR_SIGNED (*(r)) \
+       ? _GL_INT_OP_CALC (a, b, r, op, overflow, unsigned int, \
+                          int, INT_MIN, INT_MAX) \
+       : _GL_INT_OP_CALC (a, b, r, op, overflow, unsigned int, \
+                          unsigned int, 0, UINT_MAX)) \
     : _GL_INT_OP_WRAPV_LONGISH(a, b, r, op, overflow))
 # ifdef LLONG_MAX
 #  define _GL_INT_OP_WRAPV_LONGISH(a, b, r, op, overflow) \
     (sizeof *(r) == sizeof (long int) \
-     ? _GL_INT_OP_CALC (a, b, r, op, overflow, unsigned long int, \
-                        long int, LONG_MIN, LONG_MAX) \
-     : _GL_INT_OP_CALC (a, b, r, op, overflow, unsigned long long int, \
-                        long long int, LLONG_MIN, LLONG_MAX))
+     ? (EXPR_SIGNED (*(r)) \
+        ? _GL_INT_OP_CALC (a, b, r, op, overflow, unsigned long int, \
+                           long int, LONG_MIN, LONG_MAX) \
+        : _GL_INT_OP_CALC (a, b, r, op, overflow, unsigned long int, \
+                           unsigned long int, 0, ULONG_MAX)) \
+     : (EXPR_SIGNED (*(r)) \
+        ? _GL_INT_OP_CALC (a, b, r, op, overflow, unsigned long long int, \
+                           long long int, LLONG_MIN, LLONG_MAX) \
+        : _GL_INT_OP_CALC (a, b, r, op, overflow, unsigned long long int, \
+                           unsigned long long int, 0, ULLONG_MAX)))
 # else
 #  define _GL_INT_OP_WRAPV_LONGISH(a, b, r, op, overflow) \
-    _GL_INT_OP_CALC (a, b, r, op, overflow, unsigned long int, \
-                     long int, LONG_MIN, LONG_MAX)
+    (EXPR_SIGNED (*(r)) \
+     ? _GL_INT_OP_CALC (a, b, r, op, overflow, unsigned long int, \
+                        long int, LONG_MIN, LONG_MAX) \
+     : _GL_INT_OP_CALC (a, b, r, op, overflow, unsigned long int, \
+                        unsigned long int, 0, ULONG_MAX))
 # endif
 #endif
 
    overflow problems.  *R's type is T, with extrema TMIN and TMAX.
    T must be a signed integer type.  Return 1 if the result overflows.  */
 #define _GL_INT_OP_CALC(a, b, r, op, overflow, ut, t, tmin, tmax) \
-  (sizeof ((a) op (b)) < sizeof (t) \
-   ? _GL_INT_OP_CALC1 ((t) (a), (t) (b), r, op, overflow, ut, t, tmin, tmax) \
-   : _GL_INT_OP_CALC1 (a, b, r, op, overflow, ut, t, tmin, tmax))
-#define _GL_INT_OP_CALC1(a, b, r, op, overflow, ut, t, tmin, tmax) \
-  ((overflow (a, b) \
-    || (EXPR_SIGNED ((a) op (b)) && ((a) op (b)) < (tmin)) \
-    || (tmax) < ((a) op (b))) \
+  (overflow (a, b, tmin, tmax) \
    ? (*(r) = _GL_INT_OP_WRAPV_VIA_UNSIGNED (a, b, op, ut, t), 1) \
    : (*(r) = _GL_INT_OP_WRAPV_VIA_UNSIGNED (a, b, op, ut, t), 0))
 
 #define _GL_INT_OP_WRAPV_VIA_UNSIGNED(a, b, op, ut, t) \
   ((t) ((ut) (a) op (ut) (b)))
 
+/* Return true if the numeric values A + B, A - B, A * B fall outside
+   the range TMIN..TMAX.  Arguments should be integer expressions
+   without side effects.  TMIN should be signed and nonpositive.
+   TMAX should be positive, and should be signed unless TMIN is zero.  */
+#define _GL_INT_ADD_RANGE_OVERFLOW(a, b, tmin, tmax) \
+  ((b) < 0 \
+   ? (((tmin) \
+       ? ((EXPR_SIGNED (_GL_INT_CONVERT (a, (tmin) - (b))) || (b) < (tmin)) \
+          && (a) < (tmin) - (b)) \
+       : (a) <= -1 - (b)) \
+      || ((EXPR_SIGNED (a) ? 0 <= (a) : (tmax) < (a)) && (tmax) < (a) + (b))) \
+   : (a) < 0 \
+   ? (((tmin) \
+       ? ((EXPR_SIGNED (_GL_INT_CONVERT (b, (tmin) - (a))) || (a) < (tmin)) \
+          && (b) < (tmin) - (a)) \
+       : (b) <= -1 - (a)) \
+      || ((EXPR_SIGNED (_GL_INT_CONVERT (a, b)) || (tmax) < (b)) \
+          && (tmax) < (a) + (b))) \
+   : (tmax) < (b) || (tmax) - (b) < (a))
+#define _GL_INT_SUBTRACT_RANGE_OVERFLOW(a, b, tmin, tmax) \
+  (((a) < 0) == ((b) < 0) \
+   ? ((a) < (b) \
+      ? !(tmin) || -1 - (tmin) < (b) - (a) - 1 \
+      : (tmax) < (a) - (b)) \
+   : (a) < 0 \
+   ? ((!EXPR_SIGNED (_GL_INT_CONVERT ((a) - (tmin), b)) && (a) - (tmin) < 0) \
+      || (a) - (tmin) < (b)) \
+   : ((! (EXPR_SIGNED (_GL_INT_CONVERT (tmax, b)) \
+          && EXPR_SIGNED (_GL_INT_CONVERT ((tmax) + (b), a))) \
+       && (tmax) <= -1 - (b)) \
+      || (tmax) + (b) < (a)))
+#define _GL_INT_MULTIPLY_RANGE_OVERFLOW(a, b, tmin, tmax) \
+  ((b) < 0 \
+   ? ((a) < 0 \
+      ? (EXPR_SIGNED (_GL_INT_CONVERT (tmax, b)) \
+         ? (a) < (tmax) / (b) \
+         : ((INT_NEGATE_OVERFLOW (b) \
+             ? _GL_INT_CONVERT (b, tmax) >> (TYPE_WIDTH (b) - 1) \
+             : (tmax) / -(b)) \
+            <= -1 - (a))) \
+      : INT_NEGATE_OVERFLOW (_GL_INT_CONVERT (b, tmin)) && (b) == -1 \
+      ? (EXPR_SIGNED (a) ? 0 < (a) + (tmin) : (a) && -1 - (tmin) < (a) - 1) \
+      : (tmin) / (b) < (a)) \
+   : (b) == 0 \
+   ? 0 \
+   : ((a) < 0 \
+      ? (INT_NEGATE_OVERFLOW (_GL_INT_CONVERT (a, tmin)) && (a) == -1 \
+         ? (EXPR_SIGNED (b) ? 0 < (b) + (tmin) : -1 - (tmin) < (b) - 1) \
+         : (tmin) / (a) < (b)) \
+      : (tmax) / (b) < (a)))
+
 #endif /* _GL_INTPROPS_H */
index babcafd60254fd71f23a430d70a65f3be7baddf6..be0c6b3a7c2fb6e6cef2128fdf00695f33cb78b8 100644 (file)
@@ -56,6 +56,9 @@
 
 #define DONTCARE __LINE__
 
+int int_minus_2 = -2;
+int int_1 = 1;
+
 int
 main (void)
 {
@@ -237,33 +240,48 @@ main (void)
 
   /* INT_<op>_OVERFLOW and INT_<op>_WRAPV with mixed types.  */
   #define CHECK_SUM(a, b, t, v, vres)                                     \
-    CHECK_SUM1(a, b, t, v, vres);                                         \
-    CHECK_SUM1(b, a, t, v, vres)
-  #define CHECK_SSUM(a, b, t, v, vres)                                    \
-    CHECK_SSUM1(a, b, t, v, vres);                                        \
-    CHECK_SSUM1(b, a, t, v, vres)
+    CHECK_SUM1 (a, b, t, v, vres);                                        \
+    CHECK_SUM1 (b, a, t, v, vres)
+  #define CHECK_SUM_WRAPV(a, b, t, v, vres, okres)                        \
+    CHECK_SUM_WRAPV1 (a, b, t, v, vres, okres);                           \
+    CHECK_SUM_WRAPV1 (b, a, t, v, vres, okres)
   #define CHECK_SUM1(a, b, t, v, vres)                                    \
-    VERIFY (INT_ADD_OVERFLOW (a, b) == (v))
-  #define CHECK_SSUM1(a, b, t, v, vres)                                   \
-    CHECK_SUM1(a, b, t, v, vres);                                         \
+    VERIFY (INT_ADD_OVERFLOW (a, b) == (v));                              \
+    CHECK_SUM_WRAPV1 (a, b, t, v, vres, (a) + (b))
+  #define CHECK_SUM_WRAPV1(a, b, t, v, vres, okres)                       \
     {                                                                     \
       t result;                                                           \
       ASSERT (INT_ADD_WRAPV (a, b, &result) == (v));                      \
-      ASSERT (result == ((v) ? (vres) : ((a) + (b))));                    \
+      ASSERT (result == ((v) ? (vres) : (okres)));                        \
     }
-  CHECK_SSUM (-1, LONG_MIN, long int, true, LONG_MAX);
+  CHECK_SUM (-1, LONG_MIN, long int, true, LONG_MAX);
   CHECK_SUM (-1, UINT_MAX, unsigned int, false, DONTCARE);
-  CHECK_SSUM (-1L, INT_MIN, long int, INT_MIN == LONG_MIN,
+  CHECK_SUM (-1L, INT_MIN, long int, INT_MIN == LONG_MIN,
               INT_MIN == LONG_MIN ? INT_MAX : DONTCARE);
   CHECK_SUM (0u, -1, unsigned int, true, 0u + -1);
   CHECK_SUM (0u, 0, unsigned int, false, DONTCARE);
   CHECK_SUM (0u, 1, unsigned int, false, DONTCARE);
-  CHECK_SSUM (1, LONG_MAX, long int, true, LONG_MIN);
+  CHECK_SUM (1, LONG_MAX, long int, true, LONG_MIN);
   CHECK_SUM (1, UINT_MAX, unsigned int, true, 0u);
-  CHECK_SSUM (1L, INT_MAX, long int, INT_MAX == LONG_MAX,
+  CHECK_SUM (1L, INT_MAX, long int, INT_MAX == LONG_MAX,
               INT_MAX == LONG_MAX ? INT_MIN : DONTCARE);
   CHECK_SUM (1u, INT_MAX, unsigned int, INT_MAX == UINT_MAX, 1u + INT_MAX);
   CHECK_SUM (1u, INT_MIN, unsigned int, true, 1u + INT_MIN);
+  CHECK_SUM_WRAPV (-1, 1u, int, false, DONTCARE, 0);
+  CHECK_SUM_WRAPV (-1, 1ul, int, false, DONTCARE, 0);
+  CHECK_SUM_WRAPV (-1l, 1u, int, false, DONTCARE, 0);
+  CHECK_SUM_WRAPV (-100, 1000u, int, false, DONTCARE, 900);
+  CHECK_SUM_WRAPV (INT_MIN, UINT_MAX, int, false, DONTCARE, INT_MAX);
+  CHECK_SUM_WRAPV (1u, INT_MAX, int, true, INT_MIN, DONTCARE);
+  CHECK_SUM_WRAPV (INT_MAX, 1, long int, LONG_MAX <= INT_MAX, INT_MIN,
+                   INT_MAX + 1L);
+  CHECK_SUM_WRAPV (UINT_MAX, 1, long int, LONG_MAX <= UINT_MAX, 0,
+                   UINT_MAX + 1L);
+  CHECK_SUM_WRAPV (INT_MAX, 1, unsigned long int, ULONG_MAX <= INT_MAX, 0,
+                   INT_MAX + 1uL);
+  CHECK_SUM_WRAPV (UINT_MAX, 1, unsigned long int, ULONG_MAX <= UINT_MAX, 0,
+                   UINT_MAX + 1uL);
+
   {
     long int result;
     ASSERT (INT_ADD_WRAPV (1, INT_MAX, &result) == (INT_MAX == LONG_MAX));
@@ -273,7 +291,9 @@ main (void)
   #define CHECK_DIFFERENCE(a, b, t, v, vres)                              \
     VERIFY (INT_SUBTRACT_OVERFLOW (a, b) == (v))
   #define CHECK_SDIFFERENCE(a, b, t, v, vres)                             \
-    CHECK_DIFFERENCE(a, b, t, v, vres);                                   \
+    CHECK_DIFFERENCE (a, b, t, v, vres);                                  \
+    CHECK_SDIFFERENCE_WRAPV (a, b, t, v, vres)
+  #define CHECK_SDIFFERENCE_WRAPV(a, b, t, v, vres)                       \
     {                                                                     \
       t result;                                                           \
       ASSERT (INT_SUBTRACT_WRAPV (a, b, &result) == (v));                 \
@@ -290,6 +310,11 @@ main (void)
   CHECK_SDIFFERENCE (-1, INT_MAX, int, false, -1 - INT_MAX);
   CHECK_SDIFFERENCE (0, INT_MIN, int, INT_MIN < -INT_MAX, INT_MIN);
   CHECK_SDIFFERENCE (0, INT_MAX, int, false, 0 - INT_MAX);
+  CHECK_SDIFFERENCE_WRAPV (-1, 1u, int, false, DONTCARE);
+  CHECK_SDIFFERENCE_WRAPV (-1, 1ul, int, false, DONTCARE);
+  CHECK_SDIFFERENCE_WRAPV (-1l, 1u, int, false, DONTCARE);
+  CHECK_SDIFFERENCE_WRAPV (0u, INT_MAX, int, false, DONTCARE);
+  CHECK_SDIFFERENCE_WRAPV (1u, INT_MIN, int, true, 1u - INT_MIN);
   {
     long int result;
     ASSERT (INT_SUBTRACT_WRAPV (INT_MAX, -1, &result) == (INT_MAX == LONG_MAX));
@@ -297,15 +322,20 @@ main (void)
   }
 
   #define CHECK_PRODUCT(a, b, t, v, vres)                                 \
-    CHECK_PRODUCT1(a, b, t, v, vres);                                     \
-    CHECK_PRODUCT1(b, a, t, v, vres)
+    CHECK_PRODUCT1 (a, b, t, v, vres);                                    \
+    CHECK_PRODUCT1 (b, a, t, v, vres)
   #define CHECK_SPRODUCT(a, b, t, v, vres)                                \
-    CHECK_SPRODUCT1(a, b, t, v, vres);                                    \
-    CHECK_SPRODUCT1(b, a, t, v, vres)
+    CHECK_SPRODUCT1 (a, b, t, v, vres);                                   \
+    CHECK_SPRODUCT1 (b, a, t, v, vres)
+  #define CHECK_SPRODUCT_WRAPV(a, b, t, v, vres)                          \
+    CHECK_SPRODUCT_WRAPV1 (a, b, t, v, vres);                             \
+    CHECK_SPRODUCT_WRAPV1 (b, a, t, v, vres)
   #define CHECK_PRODUCT1(a, b, t, v, vres)                                \
     VERIFY (INT_MULTIPLY_OVERFLOW (a, b) == (v))
   #define CHECK_SPRODUCT1(a, b, t, v, vres)                               \
-    CHECK_PRODUCT1(a, b, t, v, vres);                                     \
+    CHECK_PRODUCT1 (a, b, t, v, vres);                                    \
+    CHECK_SPRODUCT_WRAPV1 (a, b, t, v, vres)
+  #define CHECK_SPRODUCT_WRAPV1(a, b, t, v, vres)                         \
     {                                                                     \
       t result;                                                           \
       ASSERT (INT_MULTIPLY_WRAPV (a, b, &result) == (v));                 \
@@ -343,6 +373,10 @@ main (void)
   CHECK_PRODUCT (INT_MIN, UINT_MAX, unsigned int, true, INT_MIN * UINT_MAX);
   CHECK_PRODUCT (INT_MIN, ULONG_MAX, unsigned long int, true,
                  INT_MIN * ULONG_MAX);
+  CHECK_SPRODUCT_WRAPV (-1, INT_MAX + 1u, int, false, DONTCARE);
+  CHECK_SPRODUCT_WRAPV (-1, 1u, int, false, DONTCARE);
+  CHECK_SPRODUCT (0, ULONG_MAX, int, false, DONTCARE);
+  CHECK_SPRODUCT (0u, LONG_MIN, int, false, DONTCARE);
   {
     long int result;
     ASSERT (INT_MULTIPLY_WRAPV (INT_MAX, INT_MAX, &result)
@@ -365,6 +399,12 @@ main (void)
   }
 # endif
 
+  /* Check for GCC bug 91450.  */
+  {
+    unsigned long long result;
+    ASSERT (INT_MULTIPLY_WRAPV (int_minus_2, int_1, &result) && result == -2);
+  }
+
   #define CHECK_QUOTIENT(a, b, v) VERIFY (INT_DIVIDE_OVERFLOW (a, b) == (v))
 
   CHECK_QUOTIENT (INT_MIN, -1L, INT_MIN == LONG_MIN);