bug-gnulib
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH] intprops: revise _WRAPV macros, revert _OVERFLOW


From: Paul Eggert
Subject: [PATCH] intprops: revise _WRAPV macros, revert _OVERFLOW
Date: Wed, 4 Nov 2015 08:29:37 -0800

The incompatible changes to the _OVERFLOW macros were too much of
a hassle in practice, so revert them.  Instead, change the new
_WRAPV macros to make them closer in behavior to GCC 5's new
builtin_add_overflow etc. functions.  No other software was using
these newly-added macros yet, so this should be OK.
* NEWS: Revert previous change, since the incompatible change
has been reverted, and nobody used the incompatible version.
* doc/intprops.texi (Wraparound Arithmetic, Integer Type Overflow):
Document revised behavior.
(Integer Range Overflow): Adjust example to match above revisions.
* lib/intprops.h (INT_ADD_OVERFLOW, INT_SUBTRACT_OVERFLOW)
(INT_MULTIPLY_OVERFLOW): Revert previous change, so that
these can be used in integer constant expressions again.
(INT_CONST_ADD_OVERFLOW, INT_CONST_SUBTRACT_OVERFLOW)
(INT_CONST_MULTIPLY_OVERFLOW): Remove, as these are no longer
needed.
(INT_CONST_ADD_WRAPV, INT_CONST_SUBTRACT_WRAPV)
(INT_NEGATE_WRAPV, INT_CONST_MULTIPLY_WRAPV, INT_DIVIDE_WRAPV)
(INT_REMAINDER_WRAPV, INT_LEFT_SHIFT_WRAPV):
Remove, as they did not seem that useful.
(INT_ADD_WRAPV, INT_SUBTRACT_WRAPV, INT_MULTIPLY_WRAPV)
(_GL_INT_OP_WRAPV, _GL_INT_OP_WRAPV_LONGISH)
(_GL_INT_OP_WRAPV_VIA_UNSIGNED):
Support new semantics.
(__has_builtin): New macro, if not alreay defined.
(_GL__GENERIC_BOGUS, _GL_INT_OP_CALC, _GL_INT_OP_CALC1): New macros.
* tests/test-intprops.c (INT_CONST_DIVIDE_OVERFLOW)
(INT_CONST_REMAINDER_OVERFLOW, INT_CONST_LEFT_SHIFT_OVERFLOW)
(INT_CONST_DIVIDE_WRAPV, INT_CONST_REMAINDER_WRAPV)
(INT_CONST_LEFT_SHIFT_WRAPV): Remove.
(CHECK_SBINOP, CHECK_SSUM, CHECK_SUM1, CHECK_SSUM1)
(CHECK_SDIFFERENCE, CHECK_SPRODUCT, CHECK_PRODUCT1, CHECK_SPRODUCT1):
New macros.
(CHECK_BINOP, CHECK_UNOP, main, CHECK_SUM): Test new behavior.
---
 ChangeLog             |  38 +++++++
 NEWS                  |   8 --
 doc/intprops.texi     | 196 +++++++++++++++-------------------
 lib/intprops.h        | 218 +++++++++++++++++++-------------------
 tests/test-intprops.c | 285 ++++++++++++++++++++++++++++----------------------
 5 files changed, 384 insertions(+), 361 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index df3829d..51c9431 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,41 @@
+2015-11-04  Paul Eggert  <address@hidden>
+
+       intprops: revise _WRAPV macros, revert _OVERFLOW
+       The incompatible changes to the _OVERFLOW macros were too much of
+       a hassle in practice, so revert them.  Instead, change the new
+       _WRAPV macros to make them closer in behavior to GCC 5's new
+       builtin_add_overflow etc. functions.  No other software was using
+       these newly-added macros yet, so this should be OK.
+       * NEWS: Revert previous change, since the incompatible change
+       has been reverted, and nobody used the incompatible version.
+       * doc/intprops.texi (Wraparound Arithmetic, Integer Type Overflow):
+       Document revised behavior.
+       (Integer Range Overflow): Adjust example to match above revisions.
+       * lib/intprops.h (INT_ADD_OVERFLOW, INT_SUBTRACT_OVERFLOW)
+       (INT_MULTIPLY_OVERFLOW): Revert previous change, so that
+       these can be used in integer constant expressions again.
+       (INT_CONST_ADD_OVERFLOW, INT_CONST_SUBTRACT_OVERFLOW)
+       (INT_CONST_MULTIPLY_OVERFLOW): Remove, as these are no longer
+       needed.
+       (INT_CONST_ADD_WRAPV, INT_CONST_SUBTRACT_WRAPV)
+       (INT_NEGATE_WRAPV, INT_CONST_MULTIPLY_WRAPV, INT_DIVIDE_WRAPV)
+       (INT_REMAINDER_WRAPV, INT_LEFT_SHIFT_WRAPV):
+       Remove, as they did not seem that useful.
+       (INT_ADD_WRAPV, INT_SUBTRACT_WRAPV, INT_MULTIPLY_WRAPV)
+       (_GL_INT_OP_WRAPV, _GL_INT_OP_WRAPV_LONGISH)
+       (_GL_INT_OP_WRAPV_VIA_UNSIGNED):
+       Support new semantics.
+       (__has_builtin): New macro, if not alreay defined.
+       (_GL__GENERIC_BOGUS, _GL_INT_OP_CALC, _GL_INT_OP_CALC1): New macros.
+       * tests/test-intprops.c (INT_CONST_DIVIDE_OVERFLOW)
+       (INT_CONST_REMAINDER_OVERFLOW, INT_CONST_LEFT_SHIFT_OVERFLOW)
+       (INT_CONST_DIVIDE_WRAPV, INT_CONST_REMAINDER_WRAPV)
+       (INT_CONST_LEFT_SHIFT_WRAPV): Remove.
+       (CHECK_SBINOP, CHECK_SSUM, CHECK_SUM1, CHECK_SSUM1)
+       (CHECK_SDIFFERENCE, CHECK_SPRODUCT, CHECK_PRODUCT1, CHECK_SPRODUCT1):
+       New macros.
+       (CHECK_BINOP, CHECK_UNOP, main, CHECK_SUM): Test new behavior.
+
 2015-11-03  Jim Meyering  <address@hidden>
 
        intprops: add parentheses for when OP has precedence lower than "-"
diff --git a/NEWS b/NEWS
index ffc26df..122abf5 100644
--- a/NEWS
+++ b/NEWS
@@ -42,14 +42,6 @@ User visible incompatible changes
 
 Date        Modules         Changes
 
-2015-10-30  intprops        The macros INT_ADD_OVERFLOW, INT_SUBTRACT_OVERFLOW,
-                            and INT_MULTIPLY_OVERFLOW are no longer constant
-                            expressions even when their arguments are 
constants.
-                            Use the new macros INT_CONST_ADD_OVERFLOW,
-                            INT_CONST_SUBTRACT_OVERFLOW, and
-                            INT_CONST_MULTIPLY_OVERFLOW if you need
-                            overflow checking in constant expressions.
-
 2015-09-25  c-ctype         The following macros were removed:
                             C_CTYPE_CONSECUTIVE_DIGITS
                             C_CTYPE_CONSECUTIVE_LOWERCASE
diff --git a/doc/intprops.texi b/doc/intprops.texi
index 489bcb9..55e60e9 100644
--- a/doc/intprops.texi
+++ b/doc/intprops.texi
@@ -147,97 +147,77 @@ necessarily yield @code{INT_MIN}, because the compiler 
may do
 calculations with a 64-bit register, or may generate code that
 traps on signed integer overflow.
 
-The following macros work around this problem by yielding the
-wraparound value, i.e., the low-order bits of the correct answer.  For
-example, @code{INT_ADD_WRAPV (INT_MAX, 1)} reliably yields
address@hidden on a two's complement machine.  You can also use
-overflow-checking macros to check whether wraparound occurred.
address@hidden Type Overflow}.
+The following macros work around this problem by storing the
+wraparound value, i.e., the low-order bits of the correct answer, and
+by returning an overflow indication.  For example, if @code{i} is of
+type @code{int}, @code{INT_ADD_WRAPV (INT_MAX, 1, &i)} sets @code{i}
+to @code{INT_MIN} and returns 1 on a two's complement machine.  On
+newer platforms, these macros are typically more efficient than the
+overflow-checking macros.  @xref{Integer Type Overflow}.
+
+Example usage:
+
address@hidden
+#include <intprops.h>
+#include <stdio.h>
+
+/* Print the low order bits of A * B,
+   reporting whether overflow occurred.  */
+void
+print_product (long int a, long int b)
address@hidden
+  long int r;
+  int overflow = INT_MULTIPLY_WRAPV (a, b, &r);
+  printf ("result is %ld (%s)\n", r,
+          (overflow
+           ? "after overflow"
+           : "no overflow"));
address@hidden
address@hidden example
 
 @noindent
 These macros have the following restrictions:
 
 @itemize @bullet
 @item
-Their arguments must be integer expressions.
+Their first two arguments must be integer expressions.
+
address@hidden
+Their last argument must be a non-null pointer to a signed integer.
+To calculate a wraparound unsigned integer you can use ordinary C
+arithmetic; to tell whether it overflowed, you can use the
+overflow-checking macros.
 
 @item
 They may evaluate their arguments zero or multiple times, so the
 arguments should not have side effects.
 
 @item
-On non-GCC-compatible compilers that do not support C11, the type of
address@hidden (@var{a}, @var{b})} might differ from the native
-type of @address@hidden + @var{b}}, so it is wise to convert the result
-to the native type.  Such a conversion is safe and cannot trap.  This
-issue applies to all the @code{_WRAP} macros.
+They are not necessarily constant expressions, even if all their
+arguments are constant expressions.
 @end itemize
 
-These macros are tuned for their last argument being a constant.
-
 @table @code
address@hidden INT_ADD_WRAP (@var{a}, @var{b})
address@hidden INT_ADD_WRAP
-Return the low-order bits of @address@hidden + @var{b}}.  See above for
address@hidden INT_ADD_WRAPV (@var{a}, @var{b}, @var{r})
address@hidden INT_ADD_WRAPV
+Store the low-order bits of the sum of @var{a} and @var{b} into
address@hidden@var{r}}.  Return true if overflow occurred, false if the
+low-order bits are the mathematically-correct sum.  See above for
 restrictions.
 
address@hidden INT_CONST_ADD_WRAP (@var{a}, @var{b})
address@hidden INT_CONST_ADD_WRAP
-Return the low-order bits of @address@hidden + @var{b}}.  See above for
-restrictions.  This macro differs from @code{INT_ADD_WRAP} in that
-although its implementation is typically slower, it is an integer
-constant expression if its arguments are.
-
address@hidden INT_SUBTRACT_WRAP (@var{a}, @var{b})
address@hidden INT_SUBTRACT_WRAP
-Return the low-order bits of @address@hidden - @var{b}}.  See above for
address@hidden INT_SUBTRACT_WRAPV (@var{a}, @var{b}, @var{r})
address@hidden INT_SUBTRACT_WRAPV
+Store the low-order bits of the difference between @var{a} and @var{b}
+into @address@hidden  Return true if overflow occurred, false if the
+low-order bits are the mathematically-correct difference.  See above
+for restrictions.
+
address@hidden INT_MULTIPLY_WRAPV (@var{a}, @var{b}, @var{r})
address@hidden INT_MULTIPLY_WRAPV
+Store the low-order bits of the product of @var{a} and @var{b} into
address@hidden@var{r}}.  Return true if overflow occurred, false if the
+low-order bits are the mathematically-correct product.  See above for
 restrictions.
-
address@hidden INT_CONST_SUBTRACT_WRAP (@var{a}, @var{b})
address@hidden INT_CONST_SUBTRACT_WRAP
-Return the low-order bits of @address@hidden - @var{b}}.  See above for
-restrictions.  This macro differs from @code{INT_SUBTRACT_WRAP} in
-that although its implementation is typically slower, it is an integer
-constant expression if its arguments are.
-
address@hidden INT_NEGATE_WRAP (@var{a})
address@hidden INT_NEGATE_WRAP
-Return the low-order bits of @address@hidden  See above for restrictions.
-This macro is an integer constant expression if its arguments are.
-
address@hidden INT_MULTIPLY_WRAP (@var{a}, @var{b})
address@hidden INT_MULTIPLY_WRAP
-Return the low-order bits of @address@hidden * @var{b}}.  See above for
-restrictions.
-
address@hidden INT_CONST_MULTIPLY_WRAP (@var{a}, @var{b})
address@hidden INT_CONST_MULTIPLY_WRAP
-Return the low-order bits of @address@hidden * @var{b}}.  See above for
-restrictions.  This macro differs from @code{INT_MULTIPLY_WRAP} in
-that although its implementation is typically slower, it is an integer
-constant expression if its arguments are.
-
address@hidden INT_DIVIDE_WRAP (@var{a}, @var{b})
address@hidden INT_DIVIDE_WRAP
-Return the low-order bits of @address@hidden / @var{b}}.  See above for
-restrictions.  This macro does not check for division by zero.  This
-macro is an integer constant expression if its arguments are.
-
address@hidden INT_REMAINDER_WRAP (@var{a}, @var{b})
address@hidden INT_REMAINDER_WRAP
-Return the low-order bits of @address@hidden % @var{b}}.  See above for
-restrictions.  This macro does not check for division by zero.  This
-macro is an integer constant expression if its arguments are.
-
address@hidden INT_LEFT_SHIFT_WRAP (@var{a}, @var{b})
address@hidden INT_LEFT_SHIFT_WRAP
-Return the low-order bits of @address@hidden << @var{b}}.  See above for
-restrictions.  The C standard says that behavior is undefined for
-shifts unless address@hidden@var{b}<@var{w} where @var{w} is @var{a}'s word
-width, and that when @var{a} is negative then @address@hidden <<
address@hidden has undefined behavior, but this macro does not check these
-other restrictions.  This macro is an integer constant expression if
-its arguments are.
 @end table
 
 @node Integer Type Overflow
@@ -262,34 +242,35 @@ These macros yield 1 if the corresponding C operators 
might not yield
 numerically correct answers due to arithmetic overflow of an integer
 type.  They work correctly on all known practical hosts, and do not
 rely on undefined behavior due to signed arithmetic overflow.  They
+are integer constant expressions if their arguments are.  They
 are typically easier to use than the integer range overflow macros
-(@pxref{Integer Range Overflow}).
+(@pxref{Integer Range Overflow}), and they support more operations and
+evaluation contexts than the wraparound macros (@pxref{Wraparound
+Arithmetic}).
 
-Example usages:
+Example usage:
 
 @example
 #include <intprops.h>
 #include <limits.h>
+#include <stdio.h>
 
-/* Print the low order bits of A * B,
-   reporting whether overflow occurred.
-   When optimized this code typically
-   multiplies A and B only once.  */
+/* Print A * B if in range, an overflow
+   indicator otherwise.  */
 void
 print_product (long int a, long int b)
 @{
-  long int result = INT_MULTIPLY_WRAPV (a, b);
-  printf ("result is %ld (%s)\n", result,
-          (INT_MULTIPLY_OVERFLOW (a, b)
-           ? "after overflow"
-           : "no overflow"));
+  if (INT_MULTIPLY_OVERFLOW (a, b))
+    printf ("multiply would overflow");
+  else
+    printf ("product is %ld", a * b);
 @}
 
 /* Does the product of two ints always fit
    in a long int?  */
 enum @{
   INT_PRODUCTS_FIT_IN_LONG
-    = ! (INT_CONST_MULTIPLY_OVERFLOW
+    = ! (INT_MULTIPLY_OVERFLOW
          ((long int) INT_MIN, INT_MIN))
 @};
 @end example
@@ -306,6 +287,7 @@ They may evaluate their arguments zero or multiple times, 
so the
 arguments should not have side effects.
 @end itemize
 
address@hidden
 These macros are tuned for their last argument being a constant.
 
 @table @code
@@ -314,49 +296,26 @@ These macros are tuned for their last argument being a 
constant.
 Yield 1 if @address@hidden + @var{b}} would overflow.  See above for
 restrictions.
 
address@hidden INT_CONST_ADD_OVERFLOW (@var{a}, @var{b})
address@hidden INT_CONST_ADD_OVERFLOW
-Yield 1 if @address@hidden + @var{b}} would overflow.  See above for
-restrictions.  This macro differs from @code{INT_ADD_OVERFLOW} in that
-although its implementation is typically slower, it is an integer
-constant expression if its arguments are.
-
 @item INT_SUBTRACT_OVERFLOW (@var{a}, @var{b})
 @findex INT_SUBTRACT_OVERFLOW
 Yield 1 if @address@hidden - @var{b}} would overflow.  See above for
 restrictions.
 
address@hidden INT_CONST_SUBTRACT_OVERFLOW (@var{a}, @var{b})
address@hidden INT_CONST_SUBTRACT_OVERFLOW
-Yield 1 if @address@hidden - @var{b}} would overflow.  See above for
-restrictions.  This macro differs from @code{INT_SUBTRACT_OVERFLOW} in
-that although its implementation is typically slower, it is an integer
-constant expression if its arguments are.
-
 @item INT_NEGATE_OVERFLOW (@var{a})
 @findex INT_NEGATE_OVERFLOW
 Yields 1 if @address@hidden would overflow.  See above for restrictions.
-This macro is an integer constant expression if its arguments are.
 
 @item INT_MULTIPLY_OVERFLOW (@var{a}, @var{b})
 @findex INT_MULTIPLY_OVERFLOW
 Yield 1 if @address@hidden * @var{b}} would overflow.  See above for
 restrictions.
 
address@hidden INT_CONST_MULTIPLY_OVERFLOW (@var{a}, @var{b})
address@hidden INT_CONST_MULTIPLY_OVERFLOW
-Yield 1 if @address@hidden * @var{b}} would overflow.  See above for
-restrictions.  This macro differs from @code{INT_SUBTRACT_OVERFLOW} in
-that although its implementation is typically slower, it is an integer
-constant expression if its arguments are.
-
 @item INT_DIVIDE_OVERFLOW (@var{a}, @var{b})
 @findex INT_DIVIDE_OVERFLOW
 Yields 1 if @address@hidden / @var{b}} would overflow.  See above for
 restrictions.  Division overflow can happen on two's complement hosts
 when dividing the most negative integer by @minus{}1.  This macro does
-not check for division by zero.  This macro is an integer constant
-expression if its arguments are.
+not check for division by zero.
 
 @item INT_REMAINDER_OVERFLOW (@var{a}, @var{b})
 @findex INT_REMAINDER_OVERFLOW
@@ -365,8 +324,7 @@ restrictions.  Remainder overflow can happen on two's 
complement hosts
 when dividing the most negative integer by @minus{}1; although the
 mathematical result is always 0, in practice some implementations
 trap, so this counts as an overflow.  This macro does not check for
-division by zero.  This macro is an integer constant expression if its
-arguments are.
+division by zero.
 
 @item INT_LEFT_SHIFT_OVERFLOW (@var{a}, @var{b})
 @findex INT_LEFT_SHIFT_OVERFLOW
@@ -375,8 +333,7 @@ restrictions.  The C standard says that behavior is 
undefined for
 shifts unless address@hidden@var{b}<@var{w} where @var{w} is @var{a}'s word
 width, and that when @var{a} is negative then @address@hidden <<
 @var{b}} has undefined behavior, but this macro does not check these
-other restrictions.  This macro is an integer constant expression if
-its arguments are.
+other restrictions.
 @end table
 
 @node Integer Range Overflow
@@ -407,6 +364,9 @@ Example usage:
 
 @example
 #include <intprops.h>
+#include <limits.h>
+#include <stdio.h>
+
 void
 print_product (long int a, long int b)
 @{
@@ -415,6 +375,15 @@ print_product (long int a, long int b)
   else
     printf ("product is %ld", a * b);
 @}
+
+/* Does the product of two ints always fit
+   in a long int?  */
+enum @{
+  INT_PRODUCTS_FIT_IN_LONG
+    = ! (INT_MULTIPLY_RANGE_OVERFLOW
+         ((long int) INT_MIN, (long int) INT_MIN,
+          LONG_MIN, LONG_MAX))
address@hidden;
 @end example
 
 @noindent
@@ -436,6 +405,7 @@ maximum @var{max}.  Unsigned values should use a zero 
@var{min} of the
 proper type, for example, @code{(unsigned int) 0}.
 @end itemize
 
address@hidden
 These macros are tuned for constant @var{min} and @var{max}.  For
 commutative operations such as @address@hidden + @var{b}}, they are also
 tuned for constant @var{b}.
diff --git a/lib/intprops.h b/lib/intprops.h
index b561f14..c55c4db 100644
--- a/lib/intprops.h
+++ b/lib/intprops.h
@@ -267,25 +267,22 @@
 
    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 return the low-order bits of the answer.
-   For example, INT_ADD_WRAPV (INT_MAX, 1) returns INT_MIN on a two's
-   complement host, even if INT_MAX + 1 would trap.
-
+   The INT_<op>_WRAPV macros also store the low-order bits of the answer.
    These macros work correctly on all known practical hosts, and do not rely
    on undefined behavior due to signed arithmetic overflow.
 
-   Example usage:
+   Example usage, assuming A and B are long int:
 
-     long int a = ...;
-     long int b = ...;
      long int result = INT_MULTIPLY_WRAPV (a, b);
      printf ("result is %ld (%s)\n", result,
              INT_MULTIPLY_OVERFLOW (a, b) ? "after overflow" : "no overflow");
 
-     enum {
-       INT_PRODUCTS_FIT_IN_LONG
-         = ! INT_CONST_MULTIPLY_OVERFLOW ((long int) INT_MIN, INT_MIN)
-     };
+   Example usage with WRAPV flavor:
+
+     long int result;
+     bool overflow = INT_MULTIPLY_WRAPV (a, b, &result);
+     printf ("result is %ld (%s)\n", result,
+             overflow ? "after overflow" : "no overflow");
 
    Restrictions on these macros:
 
@@ -296,35 +293,21 @@
    These macros may evaluate their arguments zero or multiple times, so the
    arguments should not have side effects.
 
-   On non-GCC-compatible compilers that do not support C11, the type
-   of INT_<op>_WRAPV (A, B) might differ from the native type of (A op
-   B), so it is wise to convert the result to the native type.  Such a
-   conversion is safe and cannot trap.
-
-   For runtime efficiency GCC 5 and later has builtin functions for +,
-   -, * when doing integer overflow checking or wraparound arithmetic.
-   Unfortunately, these builtins require nonnull pointer arguments and
-   so cannot be used in constant expressions; see GCC bug 68120
-   <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=68120>.  In constant
-   expressions, use the macros INT_CONST_ADD_OVERFLOW and
-   INT_CONST_ADD_WRAPV instead, and similarly for SUBTRACT and
-   MULTIPLY; these macros avoid the builtins and are slower in
-   non-constant expressions.  Perhaps someday GCC's API for overflow
-   checking will be improved and we can remove the need for the
-   INT_CONST_ variants.
+   The WRAPV macros are not constant expressions.  They support only
+   +, binary -, and *.  The result type must be signed.
 
    These macros are tuned for their last 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.  */
 
-#define INT_CONST_ADD_OVERFLOW(a, b) \
+#define INT_ADD_OVERFLOW(a, b) \
   _GL_BINARY_OP_OVERFLOW (a, b, _GL_ADD_OVERFLOW)
-#define INT_CONST_SUBTRACT_OVERFLOW(a, b) \
+#define INT_SUBTRACT_OVERFLOW(a, b) \
   _GL_BINARY_OP_OVERFLOW (a, b, _GL_SUBTRACT_OVERFLOW)
 #define INT_NEGATE_OVERFLOW(a) \
   INT_NEGATE_RANGE_OVERFLOW (a, _GL_INT_MINIMUM (a), _GL_INT_MAXIMUM (a))
-#define INT_CONST_MULTIPLY_OVERFLOW(a, b) \
+#define INT_MULTIPLY_OVERFLOW(a, b) \
   _GL_BINARY_OP_OVERFLOW (a, b, _GL_MULTIPLY_OVERFLOW)
 #define INT_DIVIDE_OVERFLOW(a, b) \
   _GL_BINARY_OP_OVERFLOW (a, b, _GL_DIVIDE_OVERFLOW)
@@ -343,95 +326,104 @@
                       _GL_INT_MINIMUM (0 * (b) + (a)),          \
                       _GL_INT_MAXIMUM (0 * (b) + (a)))
 
-/* Return the low order bits of the integer expressions
-   A * B, A - B, -A, A * B, A / B, A % B, and A << B, respectively.
-   See above for restrictions.  */
-#define INT_CONST_ADD_WRAPV(a, b) _GL_INT_OP_WRAPV (a, b, +)
-#define INT_CONST_SUBTRACT_WRAPV(a, b) _GL_INT_OP_WRAPV (a, b, -)
-#define INT_NEGATE_WRAPV(a) INT_CONST_SUBTRACT_WRAPV (0, a)
-#define INT_CONST_MULTIPLY_WRAPV(a, b) _GL_INT_OP_WRAPV (a, b, *)
-#define INT_DIVIDE_WRAPV(a, b) \
-  (INT_DIVIDE_OVERFLOW(a, b) ? INT_NEGATE_WRAPV (a) : (a) / (b))
-#define INT_REMAINDER_WRAPV(a, b) \
-  (INT_REMAINDER_OVERFLOW(a, b) ? 0 : (a) % (b))
-#define INT_LEFT_SHIFT_WRAPV(a, b) _GL_INT_OP_WRAPV (a, b, <<)
-
-/* Return the low order bits of A <op> B, where OP specifies the operation.
-   See above for restrictions.  */
-#if !_GL_HAVE___TYPEOF__ && 201112 <= __STDC_VERSION__
-# define _GL_INT_OP_WRAPV(a, b, op) \
-   _Generic ((a) op (b), \
-             int: _GL_INT_OP_WRAPV_VIA_UNSIGNED (a, b, op, int), \
-             long int: _GL_INT_OP_WRAPV_VIA_UNSIGNED (a, b, op, long int), \
-             long long int: _GL_INT_OP_WRAPV_VIA_UNSIGNED (a, b, op, \
-                                                           long long int), \
-             default: (a) op (b))
+/* Compute A + B, A - B, A * B, respectively, storing the result 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)
+#define INT_SUBTRACT_WRAPV(a, b, r) \
+  _GL_INT_OP_WRAPV (a, b, r, -, __builtin_sub_overflow, INT_SUBTRACT_OVERFLOW)
+#define INT_MULTIPLY_WRAPV(a, b, r) \
+  _GL_INT_OP_WRAPV (a, b, r, *, __builtin_mul_overflow, INT_MULTIPLY_OVERFLOW)
+
+#ifndef __has_builtin
+# define __has_builtin(x) 0
+#endif
+
+/* Nonzero if this compiler has GCC bug 68193 or Clang bug 25390.  See:
+   https://gcc.gnu.org/bugzilla/show_bug.cgi?id=68193
+   https://llvm.org/bugs/show_bug.cgi?id=25390
+   For now, assume all versions of GCC-like compilers generate bogus
+   warnings for _Generic.  This matters only for older compilers that
+   lack __builtin_add_overflow.  */
+#if __GNUC__
+# define _GL__GENERIC_BOGUS 1
 #else
-# define _GL_INT_OP_WRAPV(a, b, op) \
-   (! _GL_INT_SIGNED ((0 * (a)) op (0 * (b))) \
-    ? ((a) op (b)) \
-    : _GL_EXPR_CAST ((a) op (b), \
-                     (sizeof ((a) op (b)) <= sizeof (int) \
-                      ? _GL_INT_OP_WRAPV_VIA_UNSIGNED (a, b, op, int) \
-                      : _GL_INT_OP_WRAPV_LONGISH (a, b, op))))
-
-/* Cast to E's type the value of V if possible.  Yield V as-is otherwise.  */
-# if _GL_HAVE___TYPEOF__
-#  define _GL_EXPR_CAST(e, v) ((__typeof__ (e)) (v))
-# else
-#  define _GL_EXPR_CAST(e, v) (v)
-# endif
+# define _GL__GENERIC_BOGUS 0
+#endif
 
+/* Store A <op> B into *R, where OP specifies the operation.
+   BUILTIN is the builtin operation, and OVERFLOW the overflow predicate.
+   See above for restrictions.  */
+#if 5 <= __GNUC__ || __has_builtin (__builtin_add_oveflow)
+# define _GL_INT_OP_WRAPV(a, b, r, op, builtin, overflow) builtin (a, b, r)
+#elif 201112 <= __STDC_VERSION__ && !_GL__GENERIC_BOGUS
+# define _GL_INT_OP_WRAPV(a, b, r, op, builtin, overflow) \
+   (_Generic \
+    (*(r), \
+     signed char: \
+       _GL_INT_OP_CALC (a, b, r, op, overflow, unsigned char, \
+                        signed char, SCHAR_MIN, SCHAR_MAX), \
+     short int: \
+       _GL_INT_OP_CALC (a, b, r, op, overflow, unsigned short int, \
+                        short int, SHRT_MIN, SHRT_MAX), \
+     int: \
+       _GL_INT_OP_CALC (a, b, r, op, overflow, unsigned int, \
+                        int, INT_MIN, INT_MAX), \
+     long int: \
+       _GL_INT_OP_CALC (a, b, r, op, overflow, unsigned long int, \
+                        long int, LONG_MIN, LONG_MAX), \
+     long long int: \
+       _GL_INT_OP_CALC (a, b, r, op, overflow, unsigned long long int, \
+                        long long int, LLONG_MIN, LLONG_MAX)))
+#else
+# 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 char, \
+                       signed char, SCHAR_MIN, SCHAR_MAX) \
+    : sizeof *(r) == sizeof (short int) \
+    ? _GL_INT_OP_CALC (a, b, r, op, overflow, unsigned short int, \
+                       short int, SHRT_MIN, SHRT_MAX) \
+    : sizeof *(r) == sizeof (int) \
+    ? _GL_INT_OP_CALC (a, b, r, op, overflow, unsigned int, \
+                       int, INT_MIN, INT_MAX) \
+    : _GL_INT_OP_WRAPV_LONGISH(a, b, r, op, overflow))
 # ifdef LLONG_MAX
-#  define _GL_INT_OP_WRAPV_LONGISH(a, b, op) \
-    (sizeof ((a) op (b)) <= sizeof (long int) \
-     ? _GL_INT_OP_WRAPV_VIA_UNSIGNED (a, b, op, long int) \
-     : _GL_INT_OP_WRAPV_VIA_UNSIGNED (a, b, op, long long int))
+#  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))
 # else
-#  define _GL_INT_OP_WRAPV_LONGISH(a, b, op) \
-    _GL_INT_OP_WRAPV_VIA_UNSIGNED (a, b, op, long int)
+#  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))
 # endif
 #endif
 
-/* Return A <op> B, where the operation is given by OP and the result
-   type is T.  T is a signed integer type that is at least as wide as int.
-   Do arithmetic using 'unsigned T' to avoid signed integer overflow.
-   Subtract TYPE_MINIMUM (T) before converting back to T, and add it
-   back afterwards, to avoid signed overflow during conversion.  */
-#define _GL_INT_OP_WRAPV_VIA_UNSIGNED(a, b, op, t) \
-  ((unsigned t) (a) op (unsigned t) (b) <= TYPE_MAXIMUM (t) \
-   ? (t) ((unsigned t) (a) op (unsigned t) (b)) \
-   : ((t) (((unsigned t) (a) op (unsigned t) (b)) - TYPE_MINIMUM (t)) \
-      + TYPE_MINIMUM (t)))
-
-/* Calls to the INT_<op>_<result> macros are like their INT_CONST_<op>_<result>
-   counterparts, except they are faster with GCC 5 or later, and they
-   are not constant expressions due to limitations in the GNU C API.  */
-
-#define INT_ADD_OVERFLOW(a, b) \
-  _GL_OP_OVERFLOW (a, b, INT_CONST_ADD_OVERFLOW, __builtin_add_overflow)
-#define INT_SUBTRACT_OVERFLOW(a, b) \
-  _GL_OP_OVERFLOW (a, b, INT_CONST_SUBTRACT_OVERFLOW, __builtin_sub_overflow)
-#define INT_MULTIPLY_OVERFLOW(a, b) \
-  _GL_OP_OVERFLOW (a, b, INT_CONST_MULTIPLY_OVERFLOW, __builtin_mul_overflow)
-
-#define INT_ADD_WRAPV(a, b) \
-  _GL_OP_WRAPV (a, b, INT_CONST_ADD_WRAPV, __builtin_add_overflow)
-#define INT_SUBTRACT_WRAPV(a, b) \
-  _GL_OP_WRAPV (a, b, INT_CONST_SUBTRACT_WRAPV, __builtin_sub_overflow)
-#define INT_MULTIPLY_WRAPV(a, b) \
-  _GL_OP_WRAPV (a, b, INT_CONST_MULTIPLY_WRAPV, __builtin_mul_overflow)
-
-#if __GNUC__ < 5
-# define _GL_OP_OVERFLOW(a, b, portable, builtin) portable (a, b)
-# define _GL_OP_WRAPV(a, b, portable, builtin) portable (a, b)
-#else
-# define _GL_OP_OVERFLOW(a, b, portable, builtin) \
-   builtin (a, b, &(__typeof__ ((a) + (b))) {0})
-# define _GL_OP_WRAPV(a, b, portable, builtin) \
-   _GL_OP_WRAPV_GENSYM(a, b, builtin, __gl_wrapv##__COUNTER__)
-# define _GL_OP_WRAPV_GENSYM(a, b, builtin, r) \
-   ({__typeof__ ((a) + (b)) r; builtin (a, b, &r); r; })
-#endif
+/* Store the low-order bits of A <op> B into *R, where the operation
+   is given by OP.  Use the unsigned type UT for calculation to avoid
+   overflow problems.  *R's type is T, with extremal values TMIN and
+   TMAX.  T must be a signed integer type.  */
+#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) \
+    || (_GL_INT_SIGNED ((a) op (b)) && ((a) op (b)) < (tmin)) \
+    || (tmax) < ((a) op (b))) \
+   ? (*(r) = _GL_INT_OP_WRAPV_VIA_UNSIGNED (a, b, op, ut, t, tmin, tmax), 1) \
+   : (*(r) = _GL_INT_OP_WRAPV_VIA_UNSIGNED (a, b, op, ut, t, tmin, tmax), 0))
+
+/* Return A <op> B, where the operation is given by OP.  Use the
+   unsigned type UT for calculation to avoid overflow problems.
+   Convert the result to type T without overflow by subtracting TMIN
+   from large values before converting, and adding it afterwards.
+   Compilers can optimize all the operations except OP.  */
+#define _GL_INT_OP_WRAPV_VIA_UNSIGNED(a, b, op, ut, t, tmin, tmax) \
+  (((ut) (a) op (ut) (b)) <= (tmax) \
+   ? (t) ((ut) (a) op (ut) (b)) \
+   : ((t) (((ut) (a) op (ut) (b)) - (tmin)) + (tmin)))
 
 #endif /* _GL_INTPROPS_H */
diff --git a/tests/test-intprops.c b/tests/test-intprops.c
index 25b9126..007823e 100644
--- a/tests/test-intprops.c
+++ b/tests/test-intprops.c
@@ -32,16 +32,6 @@
 
 #include "macros.h"
 
-/* Create these CONST macros as alias for the standard ones, as some
-   of the generic code below assumes each binary operator has a CONST
-   alternative.  */
-#define INT_CONST_DIVIDE_OVERFLOW(a, b) INT_DIVIDE_OVERFLOW (a, b)
-#define INT_CONST_REMAINDER_OVERFLOW(a, b) INT_REMAINDER_OVERFLOW (a, b)
-#define INT_CONST_LEFT_SHIFT_OVERFLOW(a, b) INT_LEFT_SHIFT_OVERFLOW (a, b)
-#define INT_CONST_DIVIDE_WRAPV(a, b) INT_DIVIDE_WRAPV (a, b)
-#define INT_CONST_REMAINDER_WRAPV(a, b) INT_REMAINDER_WRAPV (a, b)
-#define INT_CONST_LEFT_SHIFT_WRAPV(a, b) INT_LEFT_SHIFT_WRAPV (a, b)
-
 /* VERIFY (X) uses a static assertion for compilers that are known to work,
    and falls back on a dynamic assertion for other compilers.
    These tests should be checkable via 'verify' rather than 'ASSERT', but
@@ -140,61 +130,64 @@ main (void)
   #endif
 
   /* All the INT_<op>_RANGE_OVERFLOW tests are equally valid as
-     INT_<op>_OVERFLOW tests, so define a single macro to do both.
-     OP is the operation, A and B its operands, T the result type,
-     V the overflow flag, and VRES the result if V.  If overflow
-     occurs, assumes two's complement; that's good enough for
-     tests.  */
+     INT_<op>_OVERFLOW tests, so define macros to do both.  OP is the
+     operation, OPNAME its symbolic name, A and B its operands, T the
+     result type, V the overflow flag, and VRES the result if V and if
+     two's complement.  CHECK_BINOP is for most binary operatinos,
+     CHECK_SBINOP for binary +, -, * when the result type is signed,
+     and CHECK_UNOP for unary operations.  */
   #define CHECK_BINOP(op, opname, a, b, t, v, vres)                       \
     VERIFY (INT_##opname##_RANGE_OVERFLOW (a, b, TYPE_MINIMUM (t),        \
                                            TYPE_MAXIMUM (t))              \
             == (v));                                                      \
-    ASSERT (INT_##opname##_OVERFLOW (a, b) == (v));                       \
-    VERIFY (INT_CONST_##opname##_OVERFLOW (a, b) == (v));                 \
-    VERIFY (((t) INT_CONST_##opname##_WRAPV (a, b)                        \
-             == ((v) ? (vres) : ((a) op (b))))                            \
-            || ((v) && TYPE_SIGNED (t) && !TYPE_TWOS_COMPLEMENT (t)));    \
-    ASSERT (INT_##opname##_WRAPV (a, b) == INT_CONST_##opname##_WRAPV (a, b))
-  #define CHECK_UNOP(op, opname, a, t, v, vres)                           \
+    VERIFY (INT_##opname##_OVERFLOW (a, b) == (v))
+  #define CHECK_SBINOP(op, opname, a, b, t, v, vres)                      \
+    CHECK_BINOP(op, opname, a, b, t, v, vres);                            \
+    {                                                                     \
+      t result;                                                           \
+      ASSERT (INT_##opname##_WRAPV (a, b, &result) == (v));               \
+      ASSERT (result == ((v) ? (vres) : ((a) op (b)))                     \
+              || ((v) && !TYPE_TWOS_COMPLEMENT (t)));                     \
+    }
+  #define CHECK_UNOP(op, opname, a, t, v)                                 \
     VERIFY (INT_##opname##_RANGE_OVERFLOW (a, TYPE_MINIMUM (t),           \
                                            TYPE_MAXIMUM (t))              \
             == (v));                                                      \
-    VERIFY (INT_##opname##_OVERFLOW (a) == (v));                          \
-    VERIFY ((t) INT_##opname##_WRAPV (a) == ((v) ? (vres) : (op (a)))     \
-            || ((v) && TYPE_SIGNED (t) && !TYPE_TWOS_COMPLEMENT (t)))
+    VERIFY (INT_##opname##_OVERFLOW (a) == (v))
 
   /* INT_<op>_RANGE_OVERFLOW, INT_<op>_OVERFLOW.  */
   VERIFY (INT_ADD_RANGE_OVERFLOW (INT_MAX, 1, INT_MIN, INT_MAX));
-  VERIFY (INT_CONST_ADD_OVERFLOW (INT_MAX, 1));
-  CHECK_BINOP (+, ADD, INT_MAX, 1, int, true, INT_MIN);
-  CHECK_BINOP (+, ADD, INT_MAX, -1, int, false, INT_MAX - 1);
-  CHECK_BINOP (+, ADD, INT_MIN, 1, int, false, INT_MIN + 1);
-  CHECK_BINOP (+, ADD, INT_MIN, -1, int, true, INT_MAX);
+  VERIFY (INT_ADD_OVERFLOW (INT_MAX, 1));
+
+  CHECK_SBINOP (+, ADD, INT_MAX, 1, int, true, INT_MIN);
+  CHECK_SBINOP (+, ADD, INT_MAX, -1, int, false, INT_MAX - 1);
+  CHECK_SBINOP (+, ADD, INT_MIN, 1, int, false, INT_MIN + 1);
+  CHECK_SBINOP (+, ADD, INT_MIN, -1, int, true, INT_MAX);
   CHECK_BINOP (+, ADD, UINT_MAX, 1u, unsigned int, true, 0u);
   CHECK_BINOP (+, ADD, 0u, 1u, unsigned int, false, 1u);
 
-  CHECK_BINOP (-, SUBTRACT, INT_MAX, 1, int, false, INT_MAX - 1);
-  CHECK_BINOP (-, SUBTRACT, INT_MAX, -1, int, true, INT_MIN);
-  CHECK_BINOP (-, SUBTRACT, INT_MIN, 1, int, true, INT_MAX);
-  CHECK_BINOP (-, SUBTRACT, INT_MIN, -1, int, false, INT_MIN - -1);
+  CHECK_SBINOP (-, SUBTRACT, INT_MAX, 1, int, false, INT_MAX - 1);
+  CHECK_SBINOP (-, SUBTRACT, INT_MAX, -1, int, true, INT_MIN);
+  CHECK_SBINOP (-, SUBTRACT, INT_MIN, 1, int, true, INT_MAX);
+  CHECK_SBINOP (-, SUBTRACT, INT_MIN, -1, int, false, INT_MIN - -1);
   CHECK_BINOP (-, SUBTRACT, UINT_MAX, 1u, unsigned int, false, UINT_MAX - 1u);
   CHECK_BINOP (-, SUBTRACT, 0u, 1u, unsigned int, true, 0u - 1u);
 
-  CHECK_UNOP (-, NEGATE, INT_MIN, int, TYPE_TWOS_COMPLEMENT (int), INT_MIN);
-  CHECK_UNOP (-, NEGATE, 0, int, false, -0);
-  CHECK_UNOP (-, NEGATE, INT_MAX, int, false, -INT_MAX);
-  CHECK_UNOP (-, NEGATE, 0u, unsigned int, false, -0u);
-  CHECK_UNOP (-, NEGATE, 1u, unsigned int, true, -1u);
-  CHECK_UNOP (-, NEGATE, UINT_MAX, unsigned int, true, -UINT_MAX);
-
-  CHECK_BINOP (*, MULTIPLY, INT_MAX, INT_MAX, int, true, 1);
-  CHECK_BINOP (*, MULTIPLY, INT_MAX, INT_MIN, int, true, INT_MIN);
-  CHECK_BINOP (*, MULTIPLY, INT_MIN, INT_MAX, int, true, INT_MIN);
-  CHECK_BINOP (*, MULTIPLY, INT_MIN, INT_MIN, int, true, 0);
-  CHECK_BINOP (*, MULTIPLY, -1, INT_MIN, int,
-               INT_NEGATE_OVERFLOW (INT_MIN), INT_MIN);
-  CHECK_BINOP (*, MULTIPLY, LONG_MIN / INT_MAX, (long int) INT_MAX,
-               long int, false, LONG_MIN - LONG_MIN % INT_MAX);
+  CHECK_UNOP (-, NEGATE, INT_MIN, int, TYPE_TWOS_COMPLEMENT (int));
+  CHECK_UNOP (-, NEGATE, 0, int, false);
+  CHECK_UNOP (-, NEGATE, INT_MAX, int, false);
+  CHECK_UNOP (-, NEGATE, 0u, unsigned int, false);
+  CHECK_UNOP (-, NEGATE, 1u, unsigned int, true);
+  CHECK_UNOP (-, NEGATE, UINT_MAX, unsigned int, true);
+
+  CHECK_SBINOP (*, MULTIPLY, INT_MAX, INT_MAX, int, true, 1);
+  CHECK_SBINOP (*, MULTIPLY, INT_MAX, INT_MIN, int, true, INT_MIN);
+  CHECK_SBINOP (*, MULTIPLY, INT_MIN, INT_MAX, int, true, INT_MIN);
+  CHECK_SBINOP (*, MULTIPLY, INT_MIN, INT_MIN, int, true, 0);
+  CHECK_SBINOP (*, MULTIPLY, -1, INT_MIN, int,
+                INT_NEGATE_OVERFLOW (INT_MIN), INT_MIN);
+  CHECK_SBINOP (*, MULTIPLY, LONG_MIN / INT_MAX, (long int) INT_MAX,
+                long int, false, LONG_MIN - LONG_MIN % INT_MAX);
 
   CHECK_BINOP (/, DIVIDE, INT_MIN, -1, int,
                INT_NEGATE_OVERFLOW (INT_MIN), INT_MIN);
@@ -215,60 +208,94 @@ main (void)
 
   /* INT_<op>_OVERFLOW and INT_<op>_WRAPV with mixed types.  */
   #define CHECK_SUM(a, b, t, v, vres)                                     \
-    ASSERT (INT_ADD_OVERFLOW (a, b) == (v));                              \
-    ASSERT (INT_ADD_OVERFLOW (b, a) == (v));                              \
-    VERIFY (INT_CONST_ADD_OVERFLOW (a, b) == (v));                        \
-    VERIFY (INT_CONST_ADD_OVERFLOW (b, a) == (v));                        \
-    VERIFY ((t) INT_CONST_ADD_WRAPV (a, b) == (t) INT_CONST_ADD_WRAPV (b, a)); 
\
-    VERIFY ((t) INT_CONST_ADD_WRAPV (a, b) == ((v) ? (vres) : (a) + (b))  \
-            || ((v) && TYPE_SIGNED (t) && !TYPE_TWOS_COMPLEMENT (t)));    \
-    ASSERT ((t) INT_ADD_WRAPV (a, b) == (t) INT_CONST_ADD_WRAPV (a, b));   \
-    ASSERT ((t) INT_ADD_WRAPV (b, a) == (t) INT_CONST_ADD_WRAPV (a, b))
-  CHECK_SUM (-1, LONG_MIN, long int, true, LONG_MAX);
+    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)
+  #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);                                         \
+    {                                                                     \
+      t result;                                                           \
+      ASSERT (INT_ADD_WRAPV (a, b, &result) == (v));                      \
+      ASSERT (result == ((v) ? (vres) : ((a) + (b)))                      \
+              || ((v) && !TYPE_TWOS_COMPLEMENT (t)));                     \
+    }
+  CHECK_SSUM (-1, LONG_MIN, long int, true, LONG_MAX);
   CHECK_SUM (-1, UINT_MAX, unsigned int, false, DONTCARE);
-  CHECK_SUM (-1L, INT_MIN, long int, INT_MIN == LONG_MIN,
-             INT_MIN == LONG_MIN ? INT_MAX : DONTCARE);
+  CHECK_SSUM (-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_SUM (1, LONG_MAX, long int, true, LONG_MIN);
+  CHECK_SSUM (1, LONG_MAX, long int, true, LONG_MIN);
   CHECK_SUM (1, UINT_MAX, unsigned int, true, 0u);
-  CHECK_SUM (1L, INT_MAX, long int, INT_MAX == LONG_MAX,
-             INT_MAX == LONG_MAX ? INT_MIN : DONTCARE);
+  CHECK_SSUM (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);
-
-  VERIFY (! INT_CONST_SUBTRACT_OVERFLOW (INT_MAX, 1u));
-  VERIFY (! INT_CONST_SUBTRACT_OVERFLOW (UINT_MAX, 1));
-  VERIFY (! INT_CONST_SUBTRACT_OVERFLOW (0u, -1));
-  VERIFY (INT_CONST_SUBTRACT_OVERFLOW (UINT_MAX, -1));
-  VERIFY (INT_CONST_SUBTRACT_OVERFLOW (INT_MIN, 1u));
-  VERIFY (INT_CONST_SUBTRACT_OVERFLOW (-1, 0u));
+  {
+    long int result;
+    ASSERT (INT_ADD_WRAPV (1, INT_MAX, &result) == (INT_MAX == LONG_MAX));
+    ASSERT (INT_ADD_WRAPV (-1, INT_MIN, &result) == (INT_MIN == LONG_MIN));
+  }
+
+  #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);                                   \
+    {                                                                     \
+      t result;                                                           \
+      ASSERT (INT_SUBTRACT_WRAPV (a, b, &result) == (v));                 \
+      ASSERT (result == ((v) ? (vres) : ((a) - (b)))                      \
+              || ((v) && !TYPE_TWOS_COMPLEMENT (t)));                     \
+    }
+  CHECK_DIFFERENCE (INT_MAX, 1u, unsigned int, UINT_MAX < INT_MAX - 1,
+                    INT_MAX - 1u);
+  CHECK_DIFFERENCE (UINT_MAX, 1, unsigned int, false, UINT_MAX - 1);
+  CHECK_DIFFERENCE (0u, -1, unsigned int, false, 0u - -1);
+  CHECK_DIFFERENCE (UINT_MAX, -1, unsigned int, true, UINT_MAX - -1);
+  CHECK_DIFFERENCE (INT_MIN, 1u, unsigned int, true, INT_MIN - 1u);
+  CHECK_DIFFERENCE (-1, 0u, unsigned int, true, -1 - 0u);
+  CHECK_SDIFFERENCE (-1, INT_MIN, int, false, -1 - INT_MIN);
+  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);
+  {
+    long int result;
+    ASSERT (INT_SUBTRACT_WRAPV (INT_MAX, -1, &result) == (INT_MAX == 
LONG_MAX));
+    ASSERT (INT_SUBTRACT_WRAPV (INT_MIN, 1, &result) == (INT_MAX == LONG_MAX));
+  }
 
   #define CHECK_PRODUCT(a, b, t, v, vres)                                 \
-    ASSERT (INT_MULTIPLY_OVERFLOW (a, b) == (v));                         \
-    ASSERT (INT_MULTIPLY_OVERFLOW (b, a) == (v));                         \
-    VERIFY (INT_CONST_MULTIPLY_OVERFLOW (a, b) == (v));                   \
-    VERIFY (INT_CONST_MULTIPLY_OVERFLOW (b, a) == (v));                   \
-    VERIFY ((t) INT_CONST_MULTIPLY_WRAPV (a, b)                           \
-            == (t) INT_CONST_MULTIPLY_WRAPV (b, a));                      \
-    VERIFY ((t) INT_CONST_MULTIPLY_WRAPV (a, b) == ((v) ? (vres) : (a) * (b)) \
-            || ((v) && TYPE_SIGNED (t) && !TYPE_TWOS_COMPLEMENT (t)));    \
-    ASSERT ((t) INT_MULTIPLY_WRAPV (a, b)                                 \
-            == (t) INT_CONST_MULTIPLY_WRAPV (a, b));                      \
-    ASSERT ((t) INT_MULTIPLY_WRAPV (b, a)                                 \
-            == (t) INT_CONST_MULTIPLY_WRAPV (a, b))
+    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)
+  #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);                                     \
+    {                                                                     \
+      t result;                                                           \
+      ASSERT (INT_MULTIPLY_WRAPV (a, b, &result) == (v));                 \
+      ASSERT (result == ((v) ? (vres) : ((a) * (b)))                      \
+              || ((v) && !TYPE_TWOS_COMPLEMENT (t)));                     \
+    }
   CHECK_PRODUCT (-1, 1u, unsigned int, true, -1 * 1u);
-  CHECK_PRODUCT (-1, INT_MIN, int, INT_NEGATE_OVERFLOW (INT_MIN), INT_MIN);
+  CHECK_SPRODUCT (-1, INT_MIN, int, INT_NEGATE_OVERFLOW (INT_MIN), INT_MIN);
   CHECK_PRODUCT (-1, UINT_MAX, unsigned int, true, -1 * UINT_MAX);
-  CHECK_PRODUCT (-32768, LONG_MAX / -32768 - 1, long int, true, LONG_MIN);
-  CHECK_PRODUCT (-12345, LONG_MAX / -12345, long int, false, DONTCARE);
-  CHECK_PRODUCT (0, -1, int, false, DONTCARE);
-  CHECK_PRODUCT (0, 0, int, false, DONTCARE);
+  CHECK_SPRODUCT (-32768, LONG_MAX / -32768 - 1, long int, true, LONG_MIN);
+  CHECK_SPRODUCT (-12345, LONG_MAX / -12345, long int, false, DONTCARE);
+  CHECK_SPRODUCT (0, -1, int, false, DONTCARE);
+  CHECK_SPRODUCT (0, 0, int, false, DONTCARE);
   CHECK_PRODUCT (0, 0u, unsigned int, false, DONTCARE);
-  CHECK_PRODUCT (0, 1, int, false, DONTCARE);
-  CHECK_PRODUCT (0, INT_MAX, int, false, DONTCARE);
-  CHECK_PRODUCT (0, INT_MIN, int, false, DONTCARE);
+  CHECK_SPRODUCT (0, 1, int, false, DONTCARE);
+  CHECK_SPRODUCT (0, INT_MAX, int, false, DONTCARE);
+  CHECK_SPRODUCT (0, INT_MIN, int, false, DONTCARE);
   CHECK_PRODUCT (0, UINT_MAX, unsigned int, false, DONTCARE);
   CHECK_PRODUCT (0u, -1, unsigned int, false, DONTCARE);
   CHECK_PRODUCT (0u, 0, unsigned int, false, DONTCARE);
@@ -277,54 +304,58 @@ main (void)
   CHECK_PRODUCT (0u, INT_MAX, unsigned int, false, DONTCARE);
   CHECK_PRODUCT (0u, INT_MIN, unsigned int, false, DONTCARE);
   CHECK_PRODUCT (0u, UINT_MAX, unsigned int, false, DONTCARE);
-  CHECK_PRODUCT (1, INT_MAX, int, false, DONTCARE);
-  CHECK_PRODUCT (1, INT_MIN, int, false, DONTCARE);
-  CHECK_PRODUCT (1, UINT_MAX, int, false, DONTCARE);
+  CHECK_SPRODUCT (1, INT_MAX, int, false, DONTCARE);
+  CHECK_SPRODUCT (1, INT_MIN, int, false, DONTCARE);
+  CHECK_PRODUCT (1, UINT_MAX, unsigned int, false, DONTCARE);
   CHECK_PRODUCT (1u, INT_MIN, unsigned int, true, 1u * INT_MIN);
   CHECK_PRODUCT (1u, INT_MAX, unsigned int, UINT_MAX < INT_MAX, 1u * INT_MAX);
   CHECK_PRODUCT (INT_MAX, UINT_MAX, unsigned int, true, INT_MAX * UINT_MAX);
   CHECK_PRODUCT (INT_MAX, ULONG_MAX, unsigned long int, true,
                  INT_MAX * ULONG_MAX);
-  CHECK_PRODUCT (INT_MIN, LONG_MAX / INT_MIN - 1, long int, true, LONG_MIN);
-  CHECK_PRODUCT (INT_MIN, LONG_MAX / INT_MIN, long int, false, DONTCARE);
+  CHECK_SPRODUCT (INT_MIN, LONG_MAX / INT_MIN - 1, long int, true, LONG_MIN);
+  CHECK_SPRODUCT (INT_MIN, LONG_MAX / INT_MIN, long int, false, DONTCARE);
   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);
-
-  #define CHECK_QUOTIENT(a, b, t, v)                                 \
-    VERIFY (INT_DIVIDE_OVERFLOW (a, b) == (v));                      \
-    VERIFY ((v) || (t) INT_DIVIDE_WRAPV (a, b) == (a) / (b))
-
-  CHECK_QUOTIENT (INT_MIN, -1L, long int,
+  {
+    long int result;
+    ASSERT (INT_MULTIPLY_WRAPV (INT_MAX, INT_MAX, &result)
+            == (LONG_MAX / INT_MAX < INT_MAX));
+    ASSERT (INT_MULTIPLY_WRAPV (INT_MAX, INT_MAX, &result)
+            || result == INT_MAX * (long int) INT_MAX);
+    ASSERT (INT_MULTIPLY_WRAPV (INT_MIN, INT_MIN, &result)
+            || result == INT_MIN * (long int) INT_MIN);
+  }
+
+  #define CHECK_QUOTIENT(a, b, v) VERIFY (INT_DIVIDE_OVERFLOW (a, b) == (v))
+
+  CHECK_QUOTIENT (INT_MIN, -1L,
                   TYPE_TWOS_COMPLEMENT (long int) && INT_MIN == LONG_MIN);
-  CHECK_QUOTIENT (INT_MIN, UINT_MAX, unsigned int, false);
-  CHECK_QUOTIENT (INTMAX_MIN, UINTMAX_MAX, uintmax_t, false);
-  CHECK_QUOTIENT (INTMAX_MIN, UINT_MAX, intmax_t, false);
-  CHECK_QUOTIENT (-11, 10u, unsigned int, true);
-  CHECK_QUOTIENT (-10, 10u, unsigned int, true);
-  CHECK_QUOTIENT (-9, 10u, unsigned int, false);
-  CHECK_QUOTIENT (11u, -10, unsigned int, true);
-  CHECK_QUOTIENT (10u, -10, unsigned int, true);
-  CHECK_QUOTIENT (9u, -10, unsigned int, false);
-
-  #define CHECK_REMAINDER(a, b, t, v)                             \
-    VERIFY (INT_REMAINDER_OVERFLOW (a, b) == (v));                \
-    VERIFY ((v) || (t) INT_REMAINDER_WRAPV (a, b) == (a) % (b))
-
-  CHECK_REMAINDER (INT_MIN, -1L, long int,
+  CHECK_QUOTIENT (INT_MIN, UINT_MAX, false);
+  CHECK_QUOTIENT (INTMAX_MIN, UINTMAX_MAX, false);
+  CHECK_QUOTIENT (INTMAX_MIN, UINT_MAX, false);
+  CHECK_QUOTIENT (-11, 10u, true);
+  CHECK_QUOTIENT (-10, 10u, true);
+  CHECK_QUOTIENT (-9, 10u, false);
+  CHECK_QUOTIENT (11u, -10, true);
+  CHECK_QUOTIENT (10u, -10, true);
+  CHECK_QUOTIENT (9u, -10, false);
+
+  #define CHECK_REMAINDER(a, b, v) VERIFY (INT_REMAINDER_OVERFLOW (a, b) == 
(v))
+
+  CHECK_REMAINDER (INT_MIN, -1L,
                    TYPE_TWOS_COMPLEMENT (long int) && INT_MIN == LONG_MIN);
-  CHECK_REMAINDER (-1, UINT_MAX, unsigned int, true);
-  CHECK_REMAINDER ((intmax_t) -1, UINTMAX_MAX, uintmax_t, true);
-  CHECK_REMAINDER (INTMAX_MIN, UINT_MAX, intmax_t,
+  CHECK_REMAINDER (-1, UINT_MAX, true);
+  CHECK_REMAINDER ((intmax_t) -1, UINTMAX_MAX, true);
+  CHECK_REMAINDER (INTMAX_MIN, UINT_MAX,
                    (INTMAX_MAX < UINT_MAX
                     && - (unsigned int) INTMAX_MIN % UINT_MAX != 0));
-  CHECK_REMAINDER (INT_MIN, ULONG_MAX, unsigned long int,
-                   INT_MIN % ULONG_MAX != 1);
-  CHECK_REMAINDER (1u, -1, unsigned int, false);
-  CHECK_REMAINDER (37*39u, -39, unsigned int, false);
-  CHECK_REMAINDER (37*39u + 1, -39, unsigned int, true);
-  CHECK_REMAINDER (37*39u - 1, -39, unsigned int, true);
-  CHECK_REMAINDER (LONG_MAX, -INT_MAX, long int, false);
+  CHECK_REMAINDER (INT_MIN, ULONG_MAX, INT_MIN % ULONG_MAX != 1);
+  CHECK_REMAINDER (1u, -1, false);
+  CHECK_REMAINDER (37*39u, -39, false);
+  CHECK_REMAINDER (37*39u + 1, -39, true);
+  CHECK_REMAINDER (37*39u - 1, -39, true);
+  CHECK_REMAINDER (LONG_MAX, -INT_MAX, false);
 
   return 0;
 }
-- 
2.1.0





reply via email to

[Prev in Thread] Current Thread [Next in Thread]