diff options
author | Paul Eggert <eggert@cs.ucla.edu> | 2015-11-07 23:36:45 -0800 |
---|---|---|
committer | Paul Eggert <eggert@cs.ucla.edu> | 2015-11-07 23:37:07 -0800 |
commit | 6ea4ff5a362a150fb9e22eff1d8f2b87d017b7a4 (patch) | |
tree | 0ed8138e49f63f79593d5f5e81a64ef48e01c6ba /lib | |
parent | b9acb9502fd7c4d3cd32b1fd46e644acba4bd878 (diff) |
Merge from gnulib
This incorporates:
2015-11-05 timespec-sub: fix overflow bug; add tests
2015-11-04 intprops: revise _WRAPV macros, revert _OVERFLOW
2015-11-03 intprops: add parentheses
* lib/intprops.h, lib/timespec-add.c, lib/timespec-sub.c:
Copy from gnulib.
Diffstat (limited to 'lib')
-rw-r--r-- | lib/intprops.h | 218 | ||||
-rw-r--r-- | lib/timespec-add.c | 27 | ||||
-rw-r--r-- | lib/timespec-sub.c | 27 |
3 files changed, 135 insertions, 137 deletions
diff --git a/lib/intprops.h b/lib/intprops.h index 4441f1c294..c55c4db103 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/lib/timespec-add.c b/lib/timespec-add.c index 255489eeb1..e8f6aac29d 100644 --- a/lib/timespec-add.c +++ b/lib/timespec-add.c @@ -33,36 +33,39 @@ timespec_add (struct timespec a, struct timespec b) int ns = a.tv_nsec + b.tv_nsec; int nsd = ns - TIMESPEC_RESOLUTION; int rns = ns; + time_t tmin = TYPE_MINIMUM (time_t); + time_t tmax = TYPE_MAXIMUM (time_t); if (0 <= nsd) { rns = nsd; - if (rs == TYPE_MAXIMUM (time_t)) - { - if (0 <= bs) - goto high_overflow; - bs++; - } - else + if (bs < tmax) + bs++; + else if (rs < 0) rs++; + else + goto high_overflow; } - if (INT_ADD_OVERFLOW (rs, bs)) + /* INT_ADD_WRAPV is not appropriate since time_t might be unsigned. + In theory time_t might be narrower than int, so plain + INT_ADD_OVERFLOW does not suffice. */ + if (! INT_ADD_OVERFLOW (rs, bs) && tmin <= rs + bs && rs + bs <= tmax) + rs += bs; + else { if (rs < 0) { - rs = TYPE_MINIMUM (time_t); + rs = tmin; rns = 0; } else { high_overflow: - rs = TYPE_MAXIMUM (time_t); + rs = tmax; rns = TIMESPEC_RESOLUTION - 1; } } - else - rs += bs; return make_timespec (rs, rns); } diff --git a/lib/timespec-sub.c b/lib/timespec-sub.c index c57437556d..392ec1592a 100644 --- a/lib/timespec-sub.c +++ b/lib/timespec-sub.c @@ -33,36 +33,39 @@ timespec_sub (struct timespec a, struct timespec b) time_t bs = b.tv_sec; int ns = a.tv_nsec - b.tv_nsec; int rns = ns; + time_t tmin = TYPE_MINIMUM (time_t); + time_t tmax = TYPE_MAXIMUM (time_t); if (ns < 0) { rns = ns + TIMESPEC_RESOLUTION; - if (rs == TYPE_MINIMUM (time_t)) - { - if (bs <= 0) - goto low_overflow; - bs--; - } - else + if (bs < tmax) + bs++; + else if (- TYPE_SIGNED (time_t) < rs) rs--; + else + goto low_overflow; } - if (INT_SUBTRACT_OVERFLOW (rs, bs)) + /* INT_SUBTRACT_WRAPV is not appropriate since time_t might be unsigned. + In theory time_t might be narrower than int, so plain + INT_SUBTRACT_OVERFLOW does not suffice. */ + if (! INT_SUBTRACT_OVERFLOW (rs, bs) && tmin <= rs - bs && rs - bs <= tmax) + rs -= bs; + else { if (rs < 0) { low_overflow: - rs = TYPE_MINIMUM (time_t); + rs = tmin; rns = 0; } else { - rs = TYPE_MAXIMUM (time_t); + rs = tmax; rns = TIMESPEC_RESOLUTION - 1; } } - else - rs -= bs; return make_timespec (rs, rns); } |