From 8780c1786db440a0ef1c0776dfff68134602fcea Mon Sep 17 00:00:00 2001 From: Paul Eggert Date: Wed, 4 Nov 2015 08:27:57 -0800 Subject: [PATCH] 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. --- 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 df3829d57d..51c9431d5e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,41 @@ +2015-11-04 Paul Eggert + + 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 intprops: add parentheses for when OP has precedence lower than "-" diff --git a/NEWS b/NEWS index ffc26dfae3..122abf54ba 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 489bcb934e..55e60e99a6 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 -@code{INT_MIN} on a two's complement machine. You can also use -overflow-checking macros to check whether wraparound occurred. -@xref{Integer 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: + +@example +#include +#include + +/* Print the low order bits of A * B, + reporting whether overflow occurred. */ +void +print_product (long int a, long int b) +@{ + long int r; + int overflow = INT_MULTIPLY_WRAPV (a, b, &r); + printf ("result is %ld (%s)\n", r, + (overflow + ? "after overflow" + : "no overflow")); +@} +@end 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. + +@item +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 -@code{INT_ADD_WRAPV (@var{a}, @var{b})} might differ from the native -type of @code{@var{a} + @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 -@item INT_ADD_WRAP (@var{a}, @var{b}) -@findex INT_ADD_WRAP -Return the low-order bits of @code{@var{a} + @var{b}}. See above for +@item INT_ADD_WRAPV (@var{a}, @var{b}, @var{r}) +@findex INT_ADD_WRAPV +Store the low-order bits of the sum of @var{a} and @var{b} into +@code{*@var{r}}. Return true if overflow occurred, false if the +low-order bits are the mathematically-correct sum. See above for restrictions. -@item INT_CONST_ADD_WRAP (@var{a}, @var{b}) -@findex INT_CONST_ADD_WRAP -Return the low-order bits of @code{@var{a} + @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. - -@item INT_SUBTRACT_WRAP (@var{a}, @var{b}) -@findex INT_SUBTRACT_WRAP -Return the low-order bits of @code{@var{a} - @var{b}}. See above for +@item INT_SUBTRACT_WRAPV (@var{a}, @var{b}, @var{r}) +@findex INT_SUBTRACT_WRAPV +Store the low-order bits of the difference between @var{a} and @var{b} +into @code{*@var{r}}. Return true if overflow occurred, false if the +low-order bits are the mathematically-correct difference. See above +for restrictions. + +@item INT_MULTIPLY_WRAPV (@var{a}, @var{b}, @var{r}) +@findex INT_MULTIPLY_WRAPV +Store the low-order bits of the product of @var{a} and @var{b} into +@code{*@var{r}}. Return true if overflow occurred, false if the +low-order bits are the mathematically-correct product. See above for restrictions. - -@item INT_CONST_SUBTRACT_WRAP (@var{a}, @var{b}) -@findex INT_CONST_SUBTRACT_WRAP -Return the low-order bits of @code{@var{a} - @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. - -@item INT_NEGATE_WRAP (@var{a}) -@findex INT_NEGATE_WRAP -Return the low-order bits of @code{-@var{a}}. See above for restrictions. -This macro is an integer constant expression if its arguments are. - -@item INT_MULTIPLY_WRAP (@var{a}, @var{b}) -@findex INT_MULTIPLY_WRAP -Return the low-order bits of @code{@var{a} * @var{b}}. See above for -restrictions. - -@item INT_CONST_MULTIPLY_WRAP (@var{a}, @var{b}) -@findex INT_CONST_MULTIPLY_WRAP -Return the low-order bits of @code{@var{a} * @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. - -@item INT_DIVIDE_WRAP (@var{a}, @var{b}) -@findex INT_DIVIDE_WRAP -Return the low-order bits of @code{@var{a} / @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. - -@item INT_REMAINDER_WRAP (@var{a}, @var{b}) -@findex INT_REMAINDER_WRAP -Return the low-order bits of @code{@var{a} % @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. - -@item INT_LEFT_SHIFT_WRAP (@var{a}, @var{b}) -@findex INT_LEFT_SHIFT_WRAP -Return the low-order bits of @code{@var{a} << @var{b}}. See above for -restrictions. The C standard says that behavior is undefined for -shifts unless 0@leq{}@var{b}<@var{w} where @var{w} is @var{a}'s word -width, and that when @var{a} is negative then @code{@var{a} << -@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. @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 #include +#include -/* 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 +@noindent 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 @code{@var{a} + @var{b}} would overflow. See above for restrictions. -@item INT_CONST_ADD_OVERFLOW (@var{a}, @var{b}) -@findex INT_CONST_ADD_OVERFLOW -Yield 1 if @code{@var{a} + @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 @code{@var{a} - @var{b}} would overflow. See above for restrictions. -@item INT_CONST_SUBTRACT_OVERFLOW (@var{a}, @var{b}) -@findex INT_CONST_SUBTRACT_OVERFLOW -Yield 1 if @code{@var{a} - @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 @code{-@var{a}} 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 @code{@var{a} * @var{b}} would overflow. See above for restrictions. -@item INT_CONST_MULTIPLY_OVERFLOW (@var{a}, @var{b}) -@findex INT_CONST_MULTIPLY_OVERFLOW -Yield 1 if @code{@var{a} * @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 @code{@var{a} / @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 0@leq{}@var{b}<@var{w} where @var{w} is @var{a}'s word width, and that when @var{a} is negative then @code{@var{a} << @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 +#include +#include + 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)) +@}; @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 +@noindent These macros are tuned for constant @var{min} and @var{max}. For commutative operations such as @code{@var{a} + @var{b}}, they are also tuned for constant @var{b}. diff --git a/lib/intprops.h b/lib/intprops.h index b561f14dd7..c55c4db103 100644 --- a/lib/intprops.h +++ b/lib/intprops.h @@ -267,25 +267,22 @@ The INT__OVERFLOW macros return 1 if the corresponding C operators might not yield numerically correct answers due to arithmetic overflow. - The INT__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__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__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 - . 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 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 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 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__ macros are like their INT_CONST__ - 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 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 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 25b912657b..007823ed1a 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__RANGE_OVERFLOW tests are equally valid as - INT__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__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__RANGE_OVERFLOW, INT__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__OVERFLOW and INT__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.39.5