PR c++/70507 - integer overflow builtins not constant expressions
PR c/68120 - can't easily deal with integer overflow at compile time
gcc/testsuite/ChangeLog:
2016-06-01 Martin Sebor <msebor@redhat.com>
PR c++/70507
PR c/68120
* c-c++-common/builtin-arith-overflow-1.c: Add test cases.
* c-c++-common/builtin-arith-overflow-2.c: New test.
* g++.dg/cpp0x/constexpr-arith-overflow.C: New test.
gcc/cp/ChangeLog:
2016-06-01 Martin Sebor <msebor@redhat.com>
PR c++/70507
PR c/68120
* constexpr.c (cxx_eval_internal_function): New function.
(cxx_eval_call_expression): Call it.
* tree.c (builtin_valid_in_constant_expr_p): Handle integer
arithmetic overflow built-ins.
gcc/c-family/ChangeLog:
2016-06-01 Martin Sebor <msebor@redhat.com>
PR c/68120
(check_builtin_function_arguments): Allow type-specific integer
arithmetic overflow built-ins to take either 2 or three arguments.
gcc/ChangeLog:
2016-06-01 Martin Sebor <msebor@redhat.com>
PR c++/70507
PR c/68120
* builtins.c (fold_builtin_unordered_cmp): Handle integer arithmetic
overflow built-ins.
* doc/extend.texi (Integer Overflow Builtins): Update.
@@ -7957,8 +7957,8 @@ fold_builtin_arith_overflow (location_t loc, enum built_in_function fcode,
tree arg0, tree arg1, tree arg2)
{
enum internal_fn ifn = IFN_LAST;
- tree type = TREE_TYPE (TREE_TYPE (arg2));
- tree mem_arg2 = build_fold_indirect_ref_loc (loc, arg2);
+ enum tree_code opcode;
+
switch (fcode)
{
case BUILT_IN_ADD_OVERFLOW:
@@ -7969,6 +7969,7 @@ fold_builtin_arith_overflow (location_t loc, enum built_in_function fcode,
case BUILT_IN_UADDL_OVERFLOW:
case BUILT_IN_UADDLL_OVERFLOW:
ifn = IFN_ADD_OVERFLOW;
+ opcode = PLUS_EXPR;
break;
case BUILT_IN_SUB_OVERFLOW:
case BUILT_IN_SSUB_OVERFLOW:
@@ -7978,6 +7979,7 @@ fold_builtin_arith_overflow (location_t loc, enum built_in_function fcode,
case BUILT_IN_USUBL_OVERFLOW:
case BUILT_IN_USUBLL_OVERFLOW:
ifn = IFN_SUB_OVERFLOW;
+ opcode = MINUS_EXPR;
break;
case BUILT_IN_MUL_OVERFLOW:
case BUILT_IN_SMUL_OVERFLOW:
@@ -7987,10 +7989,35 @@ fold_builtin_arith_overflow (location_t loc, enum built_in_function fcode,
case BUILT_IN_UMULL_OVERFLOW:
case BUILT_IN_UMULLL_OVERFLOW:
ifn = IFN_MUL_OVERFLOW;
+ opcode = MULT_EXPR;
break;
default:
gcc_unreachable ();
}
+
+ /* For the "generic" overloads, the first two arguments can have different
+ types and the last argument determines the target type to use to check
+ for overflow. The arguments of the other overloads all have the same
+ type. */
+ tree type = TREE_TYPE (TREE_TYPE (arg2));
+ bool isnullp = integer_zerop (arg2);
+
+ /* When the last argument is a null pointer and the first two arguments
+ are constant, attempt to fold the built-in call into a constant
+ expression indicating whether or not it detected an overflow but
+ without storing the result. */
+ if (isnullp
+ && TREE_CODE (arg0) == INTEGER_CST && TREE_CODE (arg1) == INTEGER_CST)
+ {
+ /* Perform the computation in the target type and check for overflow. */
+ arg0 = fold_convert (type, arg0);
+ arg1 = fold_convert (type, arg1);
+
+ if (tree result = size_binop_loc (loc, opcode, arg0, arg1))
+ return TREE_OVERFLOW (result) ? build_one_cst (boolean_type_node)
+ : build_zero_cst (boolean_type_node);
+ }
+
tree ctype = build_complex_type (type);
tree call = build_call_expr_internal_loc (loc, ifn, ctype,
2, arg0, arg1);
@@ -7998,6 +8025,11 @@ fold_builtin_arith_overflow (location_t loc, enum built_in_function fcode,
tree intres = build1_loc (loc, REALPART_EXPR, type, tgt);
tree ovfres = build1_loc (loc, IMAGPART_EXPR, type, tgt);
ovfres = fold_convert_loc (loc, boolean_type_node, ovfres);
+
+ if (isnullp)
+ return ovfres;
+
+ tree mem_arg2 = build_fold_indirect_ref_loc (loc, arg2);
tree store
= fold_build2_loc (loc, MODIFY_EXPR, void_type_node, mem_arg2, intres);
return build2_loc (loc, COMPOUND_EXPR, boolean_type_node, store, ovfres);
@@ -1255,6 +1255,68 @@ cx_error_context (void)
return r;
}
+/* Evaluate a call T to a GCC internal function when possible and return
+ the evaluated result or, under the control of CTX, give an error, set
+ NON_CONSTANT_P, and return the unevaluated call T otherwise. */
+
+static tree
+cxx_eval_internal_function (const constexpr_ctx *ctx, tree t,
+ bool lval,
+ bool *non_constant_p, bool *overflow_p)
+{
+ enum tree_code opcode = ERROR_MARK;
+
+ switch (CALL_EXPR_IFN (t))
+ {
+ case IFN_UBSAN_NULL:
+ case IFN_UBSAN_BOUNDS:
+ case IFN_UBSAN_VPTR:
+ return void_node;
+
+ case IFN_ADD_OVERFLOW:
+ opcode = PLUS_EXPR;
+ break;
+ case IFN_SUB_OVERFLOW:
+ opcode = MINUS_EXPR;
+ break;
+ case IFN_MUL_OVERFLOW:
+ opcode = MULT_EXPR;
+ break;
+
+ default:
+ if (!ctx->quiet)
+ error_at (EXPR_LOC_OR_LOC (t, input_location),
+ "call to internal function");
+ *non_constant_p = true;
+ return t;
+ }
+
+ tree arg0 = cxx_eval_constant_expression (ctx, CALL_EXPR_ARG (t, 0), lval,
+ non_constant_p, overflow_p);
+ tree arg1 = cxx_eval_constant_expression (ctx, CALL_EXPR_ARG (t, 1), lval,
+ non_constant_p, overflow_p);
+
+ if (TREE_CODE (arg0) == INTEGER_CST && TREE_CODE (arg1) == INTEGER_CST)
+ {
+ if (tree result = size_binop_loc (EXPR_LOC_OR_LOC (t, input_location),
+ opcode, arg0, arg1))
+ {
+ if (TREE_OVERFLOW (result))
+ {
+ /* Reset TREE_OVERFLOW to avoid warnings for the overflow. */
+ TREE_OVERFLOW (result) = 0;
+
+ return build_complex (TREE_TYPE (t), result, integer_one_node);
+ }
+
+ return build_complex (TREE_TYPE (t), result, integer_zero_node);
+ }
+ }
+
+ *non_constant_p = true;
+ return t;
+}
+
/* Subroutine of cxx_eval_constant_expression.
Evaluate the call expression tree T in the context of OLD_CALL expression
evaluation. */
@@ -1270,18 +1332,8 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
bool depth_ok;
if (fun == NULL_TREE)
- switch (CALL_EXPR_IFN (t))
- {
- case IFN_UBSAN_NULL:
- case IFN_UBSAN_BOUNDS:
- case IFN_UBSAN_VPTR:
- return void_node;
- default:
- if (!ctx->quiet)
- error_at (loc, "call to internal function");
- *non_constant_p = true;
- return t;
- }
+ return cxx_eval_internal_function (ctx, t, lval,
+ non_constant_p, overflow_p);
if (TREE_CODE (fun) != FUNCTION_DECL)
{
@@ -352,6 +352,32 @@ builtin_valid_in_constant_expr_p (const_tree decl)
case BUILT_IN_FUNCTION:
case BUILT_IN_LINE:
+ /* The following built-ins are valid in constant expressions
+ when their arguments are. */
+ case BUILT_IN_ADD_OVERFLOW:
+ case BUILT_IN_SADD_OVERFLOW:
+ case BUILT_IN_SADDL_OVERFLOW:
+ case BUILT_IN_SADDLL_OVERFLOW:
+ case BUILT_IN_UADD_OVERFLOW:
+ case BUILT_IN_UADDL_OVERFLOW:
+ case BUILT_IN_UADDLL_OVERFLOW:
+
+ case BUILT_IN_SUB_OVERFLOW:
+ case BUILT_IN_SSUB_OVERFLOW:
+ case BUILT_IN_SSUBL_OVERFLOW:
+ case BUILT_IN_SSUBLL_OVERFLOW:
+ case BUILT_IN_USUB_OVERFLOW:
+ case BUILT_IN_USUBL_OVERFLOW:
+ case BUILT_IN_USUBLL_OVERFLOW:
+
+ case BUILT_IN_MUL_OVERFLOW:
+ case BUILT_IN_SMUL_OVERFLOW:
+ case BUILT_IN_SMULL_OVERFLOW:
+ case BUILT_IN_SMULLL_OVERFLOW:
+ case BUILT_IN_UMUL_OVERFLOW:
+ case BUILT_IN_UMULL_OVERFLOW:
+ case BUILT_IN_UMULLL_OVERFLOW:
+
/* These have constant results even if their operands are
non-constant. */
case BUILT_IN_CONSTANT_P:
@@ -9737,7 +9737,10 @@ compiler may also ignore this parameter.
@section Built-in Functions to Perform Arithmetic with Overflow Checking
The following built-in functions allow performing simple arithmetic operations
-together with checking whether the operations overflowed.
+together with checking whether the operations overflowed. The first of the
+functions accepts either a pointer to an integer object or a null pointer to
+integer as the last argument. The rest require a pointer to an object of
+the specified type as the last argument.
@deftypefn {Built-in Function} bool __builtin_add_overflow (@var{type1} a, @var{type2} b, @var{type3} *res)
@deftypefnx {Built-in Function} bool __builtin_sadd_overflow (int a, int b, int *res)
@@ -9747,21 +9750,50 @@ together with checking whether the operations overflowed.
@deftypefnx {Built-in Function} bool __builtin_uaddl_overflow (unsigned long int a, unsigned long int b, unsigned long int *res)
@deftypefnx {Built-in Function} bool __builtin_uaddll_overflow (unsigned long long int a, unsigned long long int b, unsigned long int *res)
-These built-in functions promote the first two operands into infinite precision signed
-type and perform addition on those promoted operands. The result is then
-cast to the type the third pointer argument points to and stored there.
-If the stored result is equal to the infinite precision result, the built-in
-functions return false, otherwise they return true. As the addition is
-performed in infinite signed precision, these built-in functions have fully defined
-behavior for all argument values.
-
-The first built-in function allows arbitrary integral types for operands and
-the result type must be pointer to some integer type, the rest of the built-in
-functions have explicit integer types.
+These built-in functions promote the first two operands into infinite precision
+signed type and perform addition on those promoted operands. The result is then
+converted to the type the third pointer argument points to, and for the first
+function when the pointer is not null, stored there. If the converted result
+is equal to the infinite precision result, the built-in functions return
+@code{false}, otherwise they return @code{true} to indicate that an overflow
+has been detected. Because the addition is performed in infinite precision,
+these built-in functions have fully defined behavior for all argument values
+and integer types.
+
+The first type-generic built-in function allows arbitrary integer types as
+the first two arguments and requires that a pointer to some possibly distinct
+integer type be passed to it as the third argument. The pointed-to type is
+then used to determine the overflow. As a result, this built-in function
+can be used to detect overflow in any arbitrary integer type, including
+@code{char} and @code{short}. The remaining built-in functions take
+arguments of explicit integer types and make it possible to determine
+overflow only in the ranges of those types. Since the only provided forms
+of these latter built-in functions are for the signed and unsigned variants
+of types @code{int}, @code{long}, and @code{long long}, they cannot be used
+to determine overflow in other integer types.
+
+To enable the efficient integer overflow detection at translation-time,
+in C and C++ 11 and later programs (but not in C++ 98), the first built-in
+may be invoked in constant integer expression contexts with a null pointer
+cast to a pointer to the appropriate integer type as the third argument.
+For example, the following macro can be used to portably check, at
+compile-time, whether or not adding two constant integers will overflow,
+and perform the addition only when it is known to be safe and not to trigger
+a @option{-Woverflow} warning.
+
+@smallexample
+#define INT_ADD_OVERFLOW(a, b) \
+ __builtin_add_overflow (a, b, (__typeof__ ((a) + (b)) *) 0)
+
+enum @{
+ A = INT_MAX, B = 3,
+ C = INT_ADD_OVERFLOW (A, B) ? 0 : A + B
+@};
+@end smallexample
-The compiler will attempt to use hardware instructions to implement
-these built-in functions where possible, like conditional jump on overflow
-after addition, conditional jump on carry etc.
+For invocations of the built-in functions evaluated at run-time the compiler
+will attempt to make use of efficient hardware instructions such as conditional
+jump on overflow after addition, conditional jump on carry, etc.
@end deftypefn
@@ -9773,9 +9805,9 @@ after addition, conditional jump on carry etc.
@deftypefnx {Built-in Function} bool __builtin_usubl_overflow (unsigned long int a, unsigned long int b, unsigned long int *res)
@deftypefnx {Built-in Function} bool __builtin_usubll_overflow (unsigned long long int a, unsigned long long int b, unsigned long int *res)
-These built-in functions are similar to the add overflow checking built-in
-functions above, except they perform subtraction, subtract the second argument
-from the first one, instead of addition.
+These built-in functions are analogous to the add overflow checking built-in
+functions above, except they subtract the second argument from the first one
+rather than adding it to it.
@end deftypefn
@@ -9787,8 +9819,8 @@ from the first one, instead of addition.
@deftypefnx {Built-in Function} bool __builtin_umull_overflow (unsigned long int a, unsigned long int b, unsigned long int *res)
@deftypefnx {Built-in Function} bool __builtin_umulll_overflow (unsigned long long int a, unsigned long long int b, unsigned long int *res)
-These built-in functions are similar to the add overflow checking built-in
-functions above, except they perform multiplication, instead of addition.
+These built-in functions are analogous to the add overflow checking built-in
+functions above, except they perform multiplication rather than addition.
@end deftypefn
@@ -1,23 +1,133 @@
/* { dg-do compile } */
+/* Verify that calls with fewer or more than 3 arguments to the generic
+ __builtin_op_overflow functions are rejected. */
+
+int
+generic_0 (void)
+{
+ int x = __builtin_add_overflow (); /* { dg-error "too few arguments to function" } */
+ x += __builtin_sub_overflow (); /* { dg-error "too few arguments to function" } */
+ x += __builtin_mul_overflow (); /* { dg-error "too few arguments to function" } */
+ return x;
+}
+
int
-f1 (void)
+generic_1 (int a)
{
- int x = __builtin_add_overflow (); /* { dg-error "not enough arguments to function" } */
- x += __builtin_sub_overflow (); /* { dg-error "not enough arguments to function" } */
- x += __builtin_mul_overflow (); /* { dg-error "not enough arguments to function" } */
+ int x = __builtin_add_overflow (a); /* { dg-error "too few arguments to function" } */
+ x += __builtin_sub_overflow (a); /* { dg-error "too few arguments to function" } */
+ x += __builtin_mul_overflow (a); /* { dg-error "too few arguments to function" } */
+
+ /* Literal argument. */
+ x += __builtin_add_overflow (1); /* { dg-error "too few arguments to function" } */
+ x += __builtin_sub_overflow (2); /* { dg-error "too few arguments to function" } */
+ x += __builtin_mul_overflow (3); /* { dg-error "too few arguments to function" } */
return x;
}
int
-f2 (int a, int b, int *c, int d)
+generic_2 (int a, int b)
+{
+ int x = __builtin_add_overflow (a, b);/* { dg-error "too few arguments to function" } */
+ x += __builtin_sub_overflow (a, b); /* { dg-error "too few arguments to function" } */
+ x += __builtin_mul_overflow (a, b); /* { dg-error "too few arguments to function" } */
+ x += __builtin_add_overflow (a, 1); /* { dg-error "too few arguments to function" } */
+ x += __builtin_sub_overflow (a, 2); /* { dg-error "too few arguments to function" } */
+ x += __builtin_mul_overflow (a, 3); /* { dg-error "too few arguments to function" } */
+ x += __builtin_add_overflow (4, b); /* { dg-error "too few arguments to function" } */
+ x += __builtin_sub_overflow (5, b); /* { dg-error "too few arguments to function" } */
+ x += __builtin_mul_overflow (6, b); /* { dg-error "too few arguments to function" } */
+ return x;
+}
+
+/* Verify that calls with the correct number of arguments to the generic
+ __builtin_op_overflow functions are accepted. */
+
+int
+generic_3 (int a, int b, int c)
+{
+ int x = __builtin_add_overflow (a, b, &c);
+ x += __builtin_sub_overflow (a, b, &c);
+ x += __builtin_mul_overflow (a, b, &c);
+ x += __builtin_add_overflow (a, 1, &c);
+ x += __builtin_sub_overflow (a, 2, &c);
+ x += __builtin_mul_overflow (a, 3, &c);
+ x += __builtin_add_overflow (4, b, &c);
+ x += __builtin_sub_overflow (5, b, &c);
+ x += __builtin_mul_overflow (6, b, &c);
+ x += __builtin_add_overflow (7, 8, &c);
+ x += __builtin_sub_overflow (9, 10, &c);
+ x += __builtin_mul_overflow (11, 12, &c);
+ return x;
+}
+
+int
+generic_4 (int a, int b, int *c, int d)
{
int x = __builtin_add_overflow (a, b, c, d); /* { dg-error "too many arguments to function" } */
- x += __builtin_sub_overflow (a, b, c, d, d, d); /* { dg-error "too many arguments to function" } */
+ x += __builtin_sub_overflow (a, b, c, d); /* { dg-error "too many arguments to function" } */
x += __builtin_mul_overflow (a, b, c, d); /* { dg-error "too many arguments to function" } */
return x;
}
+/* Verify that calls with fewer or more than 3 arguments to the type
+ specific forms of the __builtin_op_overflow functions are rejected. */
+
+int
+generic_wrong_type (int a, int b)
+{
+ void *p = 0;
+ double d = 0;
+ int x = __builtin_add_overflow (a, b, p); /* { dg-error "does not have pointer to integer type" } */
+ x += __builtin_sub_overflow (a, b, &p); /* { dg-error "does not have pointer to integer type" } */
+ x += __builtin_mul_overflow (a, b, &d); /* { dg-error "does not have pointer to integer type" } */
+
+ /* Also verify literal arguments. */
+ x += __builtin_add_overflow (1, 1, p); /* { dg-error "does not have pointer to integer type" } */
+ x += __builtin_sub_overflow (1, 1, &p); /* { dg-error "does not have pointer to integer type" } */
+ x += __builtin_mul_overflow (1, 1, &d); /* { dg-error "does not have pointer to integer type" } */
+ return x;
+}
+
+/* Verify that calls with fewer than 2 or more than 3 arguments to
+ the typed __builtin_op_overflow functions are rejected. */
+int
+typed_0 (void)
+{
+ int x = __builtin_add_overflow (); /* { dg-error "too few arguments to function" } */
+ x += __builtin_sub_overflow (); /* { dg-error "too few arguments to function" } */
+ x += __builtin_mul_overflow (); /* { dg-error "too few arguments to function" } */
+ return x;
+}
+
+int
+typed_1 (int a)
+{
+ int x = __builtin_sadd_overflow (a); /* { dg-error "too few arguments to function" } */
+ x += __builtin_ssub_overflow (a); /* { dg-error "too few arguments to function" } */
+ x += __builtin_smul_overflow (a); /* { dg-error "too few arguments to function" } */
+ return x;
+}
+
+int
+typed_2 (int a, int b)
+{
+ int x = __builtin_sadd_overflow (a, b); /* { dg-error "too few arguments to function" } */
+ x += __builtin_ssub_overflow (a, b); /* { dg-error "too few arguments to function" } */
+ x += __builtin_smul_overflow (a, b); /* { dg-error "too few arguments to function" } */
+ return x;
+}
+
+int
+typed_4 (int a, int b, int *c, int d)
+{
+ int x = __builtin_sadd_overflow (a, b, c, d); /* { dg-error "too many arguments to function" } */
+ x += __builtin_ssub_overflow (a, b, c, d); /* { dg-error "too many arguments to function" } */
+ x += __builtin_smul_overflow (a, b, c, d); /* { dg-error "too many arguments to function" } */
+ return x;
+}
+
enum E { e0 = 0, e1 = 1 };
#ifndef __cplusplus
new file mode 100644
@@ -0,0 +1,499 @@
+/* PR c/68120 - can't easily deal with integer overflow at compile time */
+/* { dg-do run } */
+/* { dg-additional-options "-Wno-long-long" } */
+
+#define SCHAR_MAX __SCHAR_MAX__
+#define SHRT_MAX __SHRT_MAX__
+#define INT_MAX __INT_MAX__
+#define LONG_MAX __LONG_MAX__
+#define LLONG_MAX __LONG_LONG_MAX__
+
+#define SCHAR_MIN (-__SCHAR_MAX__ - 1)
+#define SHRT_MIN (-__SHRT_MAX__ - 1)
+#define INT_MIN (-__INT_MAX__ - 1)
+#define LONG_MIN (-__LONG_MAX__ - 1)
+#define LLONG_MIN (-__LONG_LONG_MAX__ - 1)
+
+#define UCHAR_MAX (SCHAR_MAX * 2U + 1)
+#define USHRT_MAX (SHRT_MAX * 2U + 1)
+#define UINT_MAX (INT_MAX * 2U + 1)
+#define ULONG_MAX (LONG_MAX * 2LU + 1)
+#define ULLONG_MAX (LLONG_MAX * 2LLU + 1)
+
+#define USCHAR_MIN (-__USCHAR_MAX__ - 1)
+#define USHRT_MIN (-__USHRT_MAX__ - 1)
+#define UINT_MIN (-__UINT_MAX__ - 1)
+#define ULONG_MIN (-__ULONG_MAX__ - 1)
+#define ULLONG_MIN (-__ULONG_LONG_MAX__ - 1)
+
+/* Number of failed runtime assertions. */
+int nfails;
+
+void __attribute__ ((noclone, noinline))
+runtime_assert (int expr, int line)
+{
+ if (!expr)
+ {
+ __builtin_printf ("line %i: assertion failed\n", line);
+ ++nfails;
+ }
+}
+
+/* Helper macros for run-time testing. */
+#define add(x, y) ((a) + (b))
+#define sadd(x, y) ((a) + (b))
+#define saddl(x, y) ((a) + (b))
+#define saddll(x, y) ((a) + (b))
+#define uadd(x, y) ((a) + (b))
+#define uaddl(x, y) ((a) + (b))
+#define uaddll(x, y) ((a) + (b))
+#define sub(x, y) ((a) - (b))
+#define ssub(x, y) ((a) - (b))
+#define ssubl(x, y) ((a) - (b))
+#define ssubll(x, y) ((a) - (b))
+#define usub(x, y) ((a) - (b))
+#define usubl(x, y) ((a) - (b))
+#define usubll(x, y) ((a) - (b))
+#define mul(x, y) ((a) * (b))
+#define smul(x, y) ((a) * (b))
+#define smull(x, y) ((a) * (b))
+#define smulll(x, y) ((a) * (b))
+#define umul(x, y) ((a) * (b))
+#define umull(x, y) ((a) * (b))
+#define umulll(x, y) ((a) * (b))
+
+int main (void)
+{
+
+#if __cplusplus >= 201103L
+# define StaticAssert(expr) static_assert ((expr), #expr)
+#elif __STDC_VERSION__ >= 201112L
+# define StaticAssert(expr) _Static_assert ((expr), #expr)
+#else
+ /* The following pragma has no effect due to bug 70888 - #pragma
+ diagnostic ignored -Wlong-long ineffective with __LONG_LONG_MAX__
+ in c++98 mode. */
+# pragma GCC diagnostic ignored "-Wlong-long"
+# pragma GCC diagnostic ignored "-Wunused-local-typedefs"
+
+# define CONCAT(a, b) a ## b
+# define CAT(a, b) CONCAT (a, b)
+# define StaticAssert(expr) \
+ typedef int CAT (StaticAssert_, __LINE__) [1 - 2 * !(expr)]
+#endif
+
+ /* Make extra effort to prevent constant folding seeing the constant
+ values of the arguments and optimizing the run-time test into
+ a constant. */
+#define RuntimeAssert(op, T, U, x, y, vflow) \
+ do { \
+ volatile T a = (x), b = (y); \
+ U c = 0; \
+ volatile int vf = __builtin_ ## op ## _overflow (a, b, &c); \
+ runtime_assert ((vf == vflow), __LINE__); \
+ if (vf == 0) \
+ runtime_assert (op (a, b) == c, __LINE__); \
+ } while (0)
+
+ /* Verify that each call to the type-generic __builtin_op_overflow(x, y)
+ yields a constant expression equal to z indicating whether or not
+ the constant expression (x op y) overflows when evaluated in type T. */
+#if !__cplusplus || __cplusplus >= 201103L
+ /* Perform both a run-time test followed by a compile-time test. */
+# define G_TEST(op, T, x, y, vflow) \
+ RuntimeAssert(op, T, T, x, y, vflow); \
+ StaticAssert ((vflow) == __builtin_ ## op ## _overflow ((x), (y), (T*)0))
+#else
+ /* C++ 98 doesn't permit casts to pointer types to appear in constant
+ expressions. Only perform the run-time test. */
+# define G_TEST(op, T, x, y, vflow) \
+ RuntimeAssert(op, T, T, x, y, vflow)
+#endif
+
+ /* Addition. */
+ /* G_TEST (add, signed char, 0, 0, 0); */
+ /* G_TEST (add, signed char, 0, SCHAR_MAX, 0); */
+ G_TEST (add, signed char, 1, SCHAR_MAX, 1);
+ G_TEST (add, signed char, SCHAR_MAX, SCHAR_MAX, 1);
+ G_TEST (add, signed char, 0, SCHAR_MIN, 0);
+ G_TEST (add, signed char, -1, SCHAR_MIN, 1);
+
+ G_TEST (add, short, 0, 0, 0);
+ G_TEST (add, short, 0, SHRT_MAX, 0);
+ G_TEST (add, short, 1, SHRT_MAX, 1);
+ G_TEST (add, short, SHRT_MAX, SHRT_MAX, 1);
+ G_TEST (add, short, 0, SHRT_MIN, 0);
+ G_TEST (add, short, -1, SHRT_MIN, 1);
+ G_TEST (add, short, SHRT_MIN, SHRT_MIN, 1);
+
+ G_TEST (add, int, 0, 0, 0);
+ G_TEST (add, int, 0, INT_MAX, 0);
+ G_TEST (add, int, 1, INT_MAX, 1);
+ G_TEST (add, int, INT_MAX, INT_MAX, 1);
+ G_TEST (add, int, 0, INT_MIN, 0);
+ G_TEST (add, int, -1, INT_MIN, 1);
+ G_TEST (add, int, INT_MIN, INT_MIN, 1);
+
+ G_TEST (add, long, 0, 0, 0);
+ G_TEST (add, long, 0, LONG_MAX, 0);
+ G_TEST (add, long, 1, LONG_MAX, 1);
+ G_TEST (add, long, LONG_MAX, LONG_MAX, 1);
+ G_TEST (add, long, 0, LONG_MIN, 0);
+ G_TEST (add, long, -1, LONG_MIN, 1);
+ G_TEST (add, long, LONG_MIN, LONG_MIN, 1);
+
+ G_TEST (add, long long, 0, 0, 0);
+ G_TEST (add, long long, 0, LLONG_MAX, 0);
+ G_TEST (add, long long, 1, LLONG_MAX, 1);
+ G_TEST (add, long long, LLONG_MAX, LLONG_MAX, 1);
+ G_TEST (add, long long, 0, LLONG_MIN, 0);
+ G_TEST (add, long long, -1, LLONG_MIN, 1);
+ G_TEST (add, long long, LLONG_MIN, LLONG_MIN, 1);
+
+ /* Subtraction */
+ G_TEST (sub, unsigned char, 0, 0, 0);
+ G_TEST (sub, unsigned char, 0, UCHAR_MAX, 1);
+ G_TEST (sub, unsigned char, 1, UCHAR_MAX, 1);
+
+ G_TEST (sub, unsigned char, UCHAR_MAX, UCHAR_MAX, 0);
+ G_TEST (sub, unsigned short, 0, 0, 0);
+ G_TEST (sub, unsigned short, 0, USHRT_MAX, 1);
+ G_TEST (sub, unsigned short, 1, USHRT_MAX, 1);
+ G_TEST (sub, unsigned short, USHRT_MAX, USHRT_MAX, 0);
+
+ G_TEST (sub, unsigned, 0, 0, 0);
+ G_TEST (sub, unsigned, 0, UINT_MAX, 1);
+ G_TEST (sub, unsigned, 1, UINT_MAX, 1);
+ G_TEST (sub, unsigned, UINT_MAX, UINT_MAX, 0);
+
+ G_TEST (sub, unsigned long, 0, 0, 0);
+ G_TEST (sub, unsigned long, 0, ULONG_MAX, 1);
+ G_TEST (sub, unsigned long, 1, ULONG_MAX, 1);
+ G_TEST (sub, unsigned long, ULONG_MAX, ULONG_MAX, 0);
+
+ G_TEST (sub, unsigned long long, 0, 0, 0);
+ G_TEST (sub, unsigned long long, 0, ULLONG_MAX, 1);
+ G_TEST (sub, unsigned long long, 1, ULLONG_MAX, 1);
+ G_TEST (sub, unsigned long long, ULLONG_MAX, ULLONG_MAX, 0);
+
+ G_TEST (sub, signed char, 0, 0, 0);
+ G_TEST (sub, signed char, 0, SCHAR_MAX, 0);
+ G_TEST (sub, signed char, 1, SCHAR_MAX, 0);
+ G_TEST (sub, signed char, SCHAR_MAX, SCHAR_MAX, 0);
+ G_TEST (sub, signed char, SCHAR_MIN, 1, 1);
+ G_TEST (sub, signed char, 0, SCHAR_MIN, 1);
+ G_TEST (sub, signed char, -1, SCHAR_MIN, 0);
+
+ G_TEST (sub, short, 0, 0, 0);
+ G_TEST (sub, short, 0, SHRT_MAX, 0);
+ G_TEST (sub, short, 1, SHRT_MAX, 0);
+ G_TEST (sub, short, SHRT_MAX, SHRT_MAX, 0);
+ G_TEST (sub, short, 0, SHRT_MIN, 1);
+ G_TEST (sub, short, -1, SHRT_MIN, 0);
+ G_TEST (sub, short, SHRT_MIN, SHRT_MIN, 0);
+
+ G_TEST (sub, int, 0, 0, 0);
+ G_TEST (sub, int, 0, INT_MAX, 0);
+ G_TEST (sub, int, 1, INT_MAX, 0);
+ G_TEST (sub, int, INT_MAX, INT_MAX, 0);
+ G_TEST (sub, int, 0, INT_MIN, 1);
+ G_TEST (sub, int, -1, INT_MIN, 0);
+ G_TEST (sub, int, INT_MIN, INT_MIN, 0);
+
+ G_TEST (sub, long, 0, 0, 0);
+ G_TEST (sub, long, 0, LONG_MAX, 0);
+ G_TEST (sub, long, 1, LONG_MAX, 0);
+ G_TEST (sub, long, LONG_MAX, LONG_MAX, 0);
+ G_TEST (sub, long, 0, LONG_MIN, 1);
+ G_TEST (sub, long, -1, LONG_MIN, 0);
+ G_TEST (sub, long, LONG_MIN, LONG_MIN, 0);
+
+ G_TEST (sub, long long, 0, 0, 0);
+ G_TEST (sub, long long, 0, LLONG_MAX, 0);
+ G_TEST (sub, long long, 1, LLONG_MAX, 0);
+ G_TEST (sub, long long, LLONG_MAX, LLONG_MAX, 0);
+ G_TEST (sub, long long, 0, LLONG_MIN, 1);
+ G_TEST (sub, long long, -1, LLONG_MIN, 0);
+ G_TEST (sub, long long, LLONG_MIN, LLONG_MIN, 0);
+
+ G_TEST (sub, unsigned char, 0, 0, 0);
+ G_TEST (sub, unsigned char, 0, UCHAR_MAX, 1);
+ G_TEST (sub, unsigned char, 1, UCHAR_MAX, 1);
+ G_TEST (sub, unsigned char, UCHAR_MAX, UCHAR_MAX, 0);
+
+ G_TEST (sub, unsigned short, 0, 0, 0);
+ G_TEST (sub, unsigned short, 0, USHRT_MAX, 1);
+ G_TEST (sub, unsigned short, 1, USHRT_MAX, 1);
+ G_TEST (sub, unsigned short, USHRT_MAX, USHRT_MAX, 0);
+
+ G_TEST (sub, unsigned, 0, 0, 0);
+ G_TEST (sub, unsigned, 0, UINT_MAX, 1);
+ G_TEST (sub, unsigned, 1, UINT_MAX, 1);
+ G_TEST (sub, unsigned, UINT_MAX, UINT_MAX, 0);
+
+ G_TEST (sub, unsigned long, 0, 0, 0);
+ G_TEST (sub, unsigned long, 0, ULONG_MAX, 1);
+ G_TEST (sub, unsigned long, 1, ULONG_MAX, 1);
+ G_TEST (sub, unsigned long, ULONG_MAX, ULONG_MAX, 0);
+
+ G_TEST (sub, unsigned long long, 0, 0, 0);
+ G_TEST (sub, unsigned long long, 0, ULLONG_MAX, 1);
+ G_TEST (sub, unsigned long long, 1, ULLONG_MAX, 1);
+ G_TEST (sub, unsigned long long, ULLONG_MAX, ULLONG_MAX, 0);
+
+ /* Multiplication. */
+ G_TEST (mul, unsigned char, 0, 0, 0);
+ G_TEST (mul, unsigned char, 0, UCHAR_MAX, 0);
+ G_TEST (mul, unsigned char, 1, UCHAR_MAX, 0);
+ G_TEST (mul, unsigned char, 2, UCHAR_MAX, 1);
+ G_TEST (mul, unsigned char, UCHAR_MAX, UCHAR_MAX, 1);
+
+ G_TEST (mul, unsigned short, 0, 0, 0);
+ G_TEST (mul, unsigned short, 0, USHRT_MAX, 0);
+ G_TEST (mul, unsigned short, 1, USHRT_MAX, 0);
+ G_TEST (mul, unsigned short, USHRT_MAX, 2, 1);
+ G_TEST (mul, unsigned short, USHRT_MAX, USHRT_MAX, 1);
+
+ G_TEST (mul, unsigned, 0, 0, 0);
+ G_TEST (mul, unsigned, 0, UINT_MAX, 0);
+ G_TEST (mul, unsigned, 1, UINT_MAX, 0);
+ G_TEST (mul, unsigned, 2, UINT_MAX, 1);
+ G_TEST (mul, unsigned, UINT_MAX, UINT_MAX, 1);
+
+ G_TEST (mul, unsigned long, 0, 0, 0);
+ G_TEST (mul, unsigned long, 0, ULONG_MAX, 0);
+ G_TEST (mul, unsigned long, 1, ULONG_MAX, 0);
+ G_TEST (mul, unsigned long, 2, ULONG_MAX, 1);
+ G_TEST (mul, unsigned long, ULONG_MAX, ULONG_MAX, 1);
+
+ G_TEST (mul, unsigned long long, 0, 0, 0);
+ G_TEST (mul, unsigned long long, 0, ULLONG_MAX, 0);
+ G_TEST (mul, unsigned long long, 1, ULLONG_MAX, 0);
+ G_TEST (mul, unsigned long long, 2, ULLONG_MAX, 1);
+ G_TEST (mul, unsigned long long, ULLONG_MAX, ULLONG_MAX, 1);
+
+ G_TEST (mul, signed char, 0, 0, 0);
+ G_TEST (mul, signed char, 0, SCHAR_MAX, 0);
+ G_TEST (mul, signed char, 1, SCHAR_MAX, 0);
+ G_TEST (mul, signed char, SCHAR_MAX, SCHAR_MAX, 1);
+ G_TEST (mul, signed char, SCHAR_MIN, 1, 0);
+ G_TEST (mul, signed char, 0, SCHAR_MIN, 0);
+ G_TEST (mul, signed char, -1, SCHAR_MIN, 1);
+
+ G_TEST (mul, short, 0, 0, 0);
+ G_TEST (mul, short, 0, SHRT_MAX, 0);
+ G_TEST (mul, short, 1, SHRT_MAX, 0);
+ G_TEST (mul, short, SHRT_MAX, SHRT_MAX, 1);
+ G_TEST (mul, short, 0, SHRT_MIN, 0);
+ G_TEST (mul, short, -1, SHRT_MIN, 1);
+ G_TEST (mul, short, SHRT_MIN, SHRT_MIN, 1);
+
+ G_TEST (mul, int, 0, 0, 0);
+ G_TEST (mul, int, 0, INT_MAX, 0);
+ G_TEST (mul, int, 1, INT_MAX, 0);
+ G_TEST (mul, int, INT_MAX, INT_MAX, 1);
+ G_TEST (mul, int, 0, INT_MIN, 0);
+ G_TEST (mul, int, -1, INT_MIN, 1);
+ G_TEST (mul, int, INT_MIN, INT_MIN, 1);
+
+ G_TEST (mul, long, 0, 0, 0);
+ G_TEST (mul, long, 0, LONG_MAX, 0);
+ G_TEST (mul, long, 1, LONG_MAX, 0);
+ G_TEST (mul, long, LONG_MAX, LONG_MAX, 1);
+ G_TEST (mul, long, 0, LONG_MIN, 0);
+ G_TEST (mul, long, -1, LONG_MIN, 1);
+ G_TEST (mul, long, LONG_MIN, LONG_MIN, 1);
+
+ G_TEST (mul, long long, 0, 0, 0);
+ G_TEST (mul, long long, 0, LLONG_MAX, 0);
+ G_TEST (mul, long long, 1, LLONG_MAX, 0);
+ G_TEST (mul, long long, LLONG_MAX, LLONG_MAX, 1);
+ G_TEST (mul, long long, 0, LLONG_MIN, 0);
+ G_TEST (mul, long long, -1, LLONG_MIN, 1);
+ G_TEST (mul, long long, LLONG_MIN, LLONG_MIN, 1);
+
+ G_TEST (mul, unsigned char, 0, 0, 0);
+ G_TEST (mul, unsigned char, 0, UCHAR_MAX, 0);
+ G_TEST (mul, unsigned char, 1, UCHAR_MAX, 0);
+ G_TEST (mul, unsigned char, UCHAR_MAX, UCHAR_MAX, 1);
+
+ G_TEST (mul, unsigned short, 0, 0, 0);
+ G_TEST (mul, unsigned short, 0, USHRT_MAX, 0);
+ G_TEST (mul, unsigned short, 1, USHRT_MAX, 0);
+ G_TEST (mul, unsigned short, USHRT_MAX, USHRT_MAX, 1);
+
+ G_TEST (mul, unsigned, 0, 0, 0);
+ G_TEST (mul, unsigned, 0, UINT_MAX, 0);
+ G_TEST (mul, unsigned, 1, UINT_MAX, 0);
+ G_TEST (mul, unsigned, UINT_MAX, UINT_MAX, 1);
+
+ G_TEST (mul, unsigned long, 0, 0, 0);
+ G_TEST (mul, unsigned long, 0, ULONG_MAX, 0);
+ G_TEST (mul, unsigned long, 1, ULONG_MAX, 0);
+ G_TEST (mul, unsigned long, ULONG_MAX, ULONG_MAX, 1);
+
+ G_TEST (mul, unsigned long long, 0, 0, 0);
+ G_TEST (mul, unsigned long long, 0, ULLONG_MAX, 0);
+ G_TEST (mul, unsigned long long, 1, ULLONG_MAX, 0);
+ G_TEST (mul, unsigned long long, ULLONG_MAX, ULLONG_MAX, 1);
+
+ /* Verify that each call to the type-specific __builtin_op_overflow
+ yields a constant expression equal to z indicating whether or not
+ the constant expression (x op y) overflows. The type-specific
+ overloads detect overflow after arithmetic promotions and so unlike
+ the type-generic overloads cannot detect overflow in char or short
+ types. */
+
+#if !__cplusplus || __cplusplus >= 201103L
+ /* Perform both a run-time test followed by a compile-time test. */
+# define T_TEST(op, T, x, y, vflow) \
+ RuntimeAssert(op, T, decltype ((x) + (y)), x, y, vflow); \
+ StaticAssert ((vflow) == __builtin_ ## op ## _overflow ((x), (y), \
+ (decltype ((x) + (y))*)0))
+#else
+ /* C++ 98 doesn't permit casts to pointer types to appear in constant
+ expressions. Only perform the run-time test. */
+# define T_TEST(op, T, x, y, vflow) \
+ RuntimeAssert (op, T, __typeof__ ((x) + (y)), x, y, vflow)
+#endif
+
+ /* Signed int addition. */
+ T_TEST (sadd, signed char, 0, 0, 0);
+ T_TEST (sadd, signed char, 0, SCHAR_MAX, 0);
+ T_TEST (sadd, signed char, 1, SCHAR_MAX, 0);
+ T_TEST (sadd, signed char, SCHAR_MAX, SCHAR_MAX, 0);
+ T_TEST (sadd, signed char, 0, SCHAR_MIN, 0);
+ T_TEST (sadd, signed char, -1, SCHAR_MIN, 0);
+
+ T_TEST (sadd, short, 0, 0, 0);
+ T_TEST (sadd, short, 0, SHRT_MAX, 0);
+ T_TEST (sadd, short, 1, SHRT_MAX, 0);
+ T_TEST (sadd, short, SHRT_MAX, SHRT_MAX, 0);
+ T_TEST (sadd, short, 0, SHRT_MIN, 0);
+ T_TEST (sadd, short, -1, SHRT_MIN, 0);
+ T_TEST (sadd, short, SHRT_MIN, SHRT_MIN, 0);
+
+ T_TEST (sadd, int, 0, 0, 0);
+ T_TEST (sadd, int, 0, INT_MAX, 0);
+ T_TEST (sadd, int, 1, INT_MAX, 1);
+ T_TEST (sadd, int, INT_MAX, INT_MAX, 1);
+ T_TEST (sadd, int, 0, INT_MIN, 0);
+ T_TEST (sadd, int, -1, INT_MIN, 1);
+ T_TEST (sadd, int, INT_MIN, INT_MIN, 1);
+
+ /* Signed long addition. */
+ T_TEST (saddl, long, 0L, 0L, 0);
+ T_TEST (saddl, long, 0L, LONG_MAX, 0);
+ T_TEST (saddl, long, 1L, LONG_MAX, 1);
+ T_TEST (saddl, long, LONG_MAX, LONG_MAX, 1);
+ T_TEST (saddl, long, 0L, LONG_MIN, 0);
+ T_TEST (saddl, long, -1L, LONG_MIN, 1);
+ T_TEST (saddl, long, LONG_MIN, LONG_MIN, 1);
+
+ T_TEST (saddll, long long, 0LL, 0LL, 0);
+ T_TEST (saddll, long long, 0LL, LLONG_MAX, 0);
+ T_TEST (saddll, long long, 1LL, LLONG_MAX, 1);
+ T_TEST (saddll, long long, LLONG_MAX, LLONG_MAX, 1);
+ T_TEST (saddll, long long, 0LL, LLONG_MIN, 0);
+ T_TEST (saddll, long long, -1LL, LLONG_MIN, 1);
+ T_TEST (saddll, long long, LLONG_MIN, LLONG_MIN, 1);
+
+ /* Unsigned int addition. */
+ T_TEST (uadd, unsigned char, 0U, 0U, 0);
+ T_TEST (uadd, unsigned char, 0U, UCHAR_MAX, 0);
+ T_TEST (uadd, unsigned char, 1U, UCHAR_MAX, 0);
+ T_TEST (uadd, unsigned char, UCHAR_MAX, UCHAR_MAX, 0);
+
+ T_TEST (uadd, unsigned short, 0U, 0U, 0);
+ T_TEST (uadd, unsigned short, 0U, USHRT_MAX, 0);
+ T_TEST (uadd, unsigned short, 1U, USHRT_MAX, 0);
+ T_TEST (uadd, unsigned short, USHRT_MAX, USHRT_MAX, 0);
+
+ T_TEST (uadd, unsigned, 0U, 0U, 0);
+ T_TEST (uadd, unsigned, 0U, UINT_MAX, 0);
+ T_TEST (uadd, unsigned, 1U, UINT_MAX, 1);
+ T_TEST (uadd, unsigned, UINT_MAX, UINT_MAX, 1);
+
+ /* Unsigned long addition. */
+ T_TEST (uaddl, unsigned long, 0UL, 0UL, 0);
+ T_TEST (uaddl, unsigned long, 0UL, ULONG_MAX, 0);
+ T_TEST (uaddl, unsigned long, 1UL, ULONG_MAX, 1);
+ T_TEST (uaddl, unsigned long, ULONG_MAX, ULONG_MAX, 1);
+
+ T_TEST (uaddll, unsigned long long, 0ULL, 0ULL, 0);
+ T_TEST (uaddll, unsigned long long, 0ULL, ULLONG_MAX, 0);
+ T_TEST (uaddll, unsigned long long, 1ULL, ULLONG_MAX, 1);
+ T_TEST (uaddll, unsigned long long, ULLONG_MAX, ULLONG_MAX, 1);
+
+ /* Signed int subtraction. */
+ T_TEST (ssub, signed char, 0, 0, 0);
+ T_TEST (ssub, signed char, 0, SCHAR_MAX, 0);
+ T_TEST (ssub, signed char, 1, SCHAR_MAX, 0);
+ T_TEST (ssub, signed char, SCHAR_MAX, SCHAR_MAX, 0);
+ T_TEST (ssub, signed char, 0, SCHAR_MIN, 0);
+ T_TEST (ssub, signed char, -1, SCHAR_MIN, 0);
+
+ T_TEST (ssub, short, 0, 0, 0);
+ T_TEST (ssub, short, 0, SHRT_MAX, 0);
+ T_TEST (ssub, short, 1, SHRT_MAX, 0);
+ T_TEST (ssub, short, SHRT_MAX, SHRT_MAX, 0);
+ T_TEST (ssub, short, 0, SHRT_MIN, 0);
+ T_TEST (ssub, short, -1, SHRT_MIN, 0);
+ T_TEST (ssub, short, SHRT_MIN, SHRT_MIN, 0);
+
+ T_TEST (ssub, int, 0, 0, 0);
+ T_TEST (ssub, int, 0, INT_MAX, 0);
+ T_TEST (ssub, int, 1, INT_MAX, 0);
+ T_TEST (ssub, int, INT_MAX, INT_MAX, 0);
+ T_TEST (ssub, int, 0, INT_MIN, 1);
+ T_TEST (ssub, int, -1, INT_MIN, 0);
+ T_TEST (ssub, int, INT_MIN, INT_MIN, 0);
+
+ /* Signed long subtraction. */
+ T_TEST (ssubl, long, 0L, 0L, 0);
+ T_TEST (ssubl, long, 0L, LONG_MAX, 0);
+ T_TEST (ssubl, long, 1L, LONG_MAX, 0);
+ T_TEST (ssubl, long, LONG_MAX, LONG_MAX, 0);
+ T_TEST (ssubl, long, 0L, LONG_MIN, 1);
+ T_TEST (ssubl, long, -1L, LONG_MIN, 0);
+ T_TEST (ssubl, long, LONG_MIN, LONG_MIN, 0);
+
+ /* Signed long long subtraction. */
+ T_TEST (ssubll, long long, 0LL, 0LL, 0);
+ T_TEST (ssubll, long long, 0LL, LLONG_MAX, 0);
+ T_TEST (ssubll, long long, 1LL, LLONG_MAX, 0);
+ T_TEST (ssubll, long long, LLONG_MAX, LLONG_MAX, 0);
+ T_TEST (ssubll, long long, 0LL, LLONG_MIN, 1);
+ T_TEST (ssubll, long long, -1LL, LLONG_MIN, 0);
+ T_TEST (ssubll, long long, LLONG_MIN, LLONG_MIN, 0);
+
+ /* Unsigned int subtraction. */
+ T_TEST (usub, unsigned char, 0U, 0U, 0);
+ T_TEST (usub, unsigned char, 0U, UCHAR_MAX, 1);
+ T_TEST (usub, unsigned char, 1U, UCHAR_MAX, 1);
+ T_TEST (usub, unsigned char, UCHAR_MAX, UCHAR_MAX, 0);
+
+ T_TEST (usub, unsigned short, 0U, 0U, 0);
+ T_TEST (usub, unsigned short, 0U, USHRT_MAX, 1);
+ T_TEST (usub, unsigned short, 1U, USHRT_MAX, 1);
+ T_TEST (usub, unsigned short, USHRT_MAX, USHRT_MAX, 0);
+
+ T_TEST (usub, unsigned, 0U, 0U, 0);
+ T_TEST (usub, unsigned, 0U, UINT_MAX, 1);
+ T_TEST (usub, unsigned, 1U, UINT_MAX, 1);
+ T_TEST (usub, unsigned, UINT_MAX, UINT_MAX, 0);
+
+ /* Unsigned long subtraction. */
+ T_TEST (usubl, unsigned long, 0UL, 0UL, 0);
+ T_TEST (usubl, unsigned long, 0UL, ULONG_MAX, 1);
+ T_TEST (usubl, unsigned long, 1UL, ULONG_MAX, 1);
+ T_TEST (usubl, unsigned long, ULONG_MAX, ULONG_MAX, 0);
+
+ /* Unsigned long long subtraction. */
+ T_TEST (usubll, unsigned long long, 0ULL, 0ULL, 0);
+ T_TEST (usubll, unsigned long long, 0ULL, ULLONG_MAX, 1);
+ T_TEST (usubll, unsigned long long, 1ULL, ULLONG_MAX, 1);
+ T_TEST (usubll, unsigned long long, ULLONG_MAX, ULLONG_MAX, 0);
+
+ return 0;
+}
new file mode 100644
@@ -0,0 +1,115 @@
+// PR c++/70507 - integer overflow builtins not constant expressions
+// { dg-do compile { target c++11 } }
+
+#define SCHAR_MAX __SCHAR_MAX__
+#define SHRT_MAX __SHRT_MAX__
+#define INT_MAX __INT_MAX__
+#define LONG_MAX __LONG_MAX__
+#define LLONG_MAX __LONG_LONG_MAX__
+
+#define SCHAR_MIN (-__SCHAR_MAX__ - 1)
+#define SHRT_MIN (-__SHRT_MAX__ - 1)
+#define INT_MIN (-__INT_MAX__ - 1)
+#define LONG_MIN (-__LONG_MAX__ - 1)
+#define LLONG_MIN (-__LONG_LONG_MAX__ - 1)
+
+#define UCHAR_MAX (SCHAR_MAX * 2U + 1)
+#define USHRT_MAX (SHRT_MAX * 2U + 1)
+#define UINT_MAX (INT_MAX * 2U + 1)
+#define ULONG_MAX (LONG_MAX * 2LU + 1)
+#define ULLONG_MAX (LLONG_MAX * 2LLU + 1)
+
+#define USCHAR_MIN (-__USCHAR_MAX__ - 1)
+#define USHRT_MIN (-__USHRT_MAX__ - 1)
+#define UINT_MIN (-__UINT_MAX__ - 1)
+#define ULONG_MIN (-__ULONG_MAX__ - 1)
+#define ULLONG_MIN (-__ULONG_LONG_MAX__ - 1)
+
+#define Assert(expr) static_assert ((expr), #expr)
+
+template <class T>
+constexpr T add (T x, T y, T z = T ())
+{
+ return __builtin_add_overflow (x, y, &z) ? 0 : z;
+}
+
+template <class T>
+constexpr T sub (T x, T y, T z = T ())
+{
+ return __builtin_sub_overflow (x, y, &z) ? 0 : z;
+}
+
+template <class T>
+constexpr T mul (T x, T y, T z = T ())
+{
+ return __builtin_mul_overflow (x, y, &z) ? 0 : z;
+}
+
+#define TEST_ADD(T, x, y, z) Assert (z == add<T>(x, y))
+#define TEST_SUB(T, x, y, z) Assert (z == sub<T>(x, y))
+#define TEST_MUL(T, x, y, z) Assert (z == mul<T>(x, y))
+
+
+TEST_ADD (signed char, 0, 0, 0);
+TEST_ADD (signed char, 0, SCHAR_MAX, SCHAR_MAX);
+TEST_ADD (signed char, 1, SCHAR_MAX, 0); // overflow
+TEST_ADD (signed char, SCHAR_MAX, SCHAR_MAX, 0); // overflow
+TEST_ADD (signed char, 0, SCHAR_MIN, SCHAR_MIN);
+TEST_ADD (signed char, -1, SCHAR_MIN, 0); // overflow
+
+TEST_ADD (short, 0, 0, 0);
+TEST_ADD (short, 0, SHRT_MAX, SHRT_MAX);
+TEST_ADD (short, 1, SHRT_MAX, 0); // overflow
+TEST_ADD (short, SHRT_MAX, SHRT_MAX, 0); // overflow
+TEST_ADD (short, 0, SHRT_MIN, SHRT_MIN);
+TEST_ADD (short, -1, SHRT_MIN, 0); // overflow
+TEST_ADD (short, SHRT_MIN, SHRT_MIN, 0); // overflow
+
+TEST_ADD (int, 0, 0, 0);
+TEST_ADD (int, 0, INT_MAX, INT_MAX);
+TEST_ADD (int, 1, INT_MAX, 0); // overflow
+TEST_ADD (int, INT_MAX, INT_MAX, 0); // overflow
+TEST_ADD (int, 0, INT_MIN, INT_MIN);
+TEST_ADD (int, -1, INT_MIN, 0); // overflow
+TEST_ADD (int, INT_MIN, INT_MIN, 0); // overflow
+
+TEST_ADD (long, 0, 0, 0);
+TEST_ADD (long, 0, LONG_MAX, LONG_MAX);
+TEST_ADD (long, 1, LONG_MAX, 0); // overflow
+TEST_ADD (long, LONG_MAX, LONG_MAX, 0); // overflow
+TEST_ADD (long, 0, LONG_MIN, LONG_MIN);
+TEST_ADD (long, -1, LONG_MIN, 0); // overflow
+TEST_ADD (long, LONG_MIN, LONG_MIN, 0); // overflow
+
+TEST_ADD (long long, 0, 0, 0);
+TEST_ADD (long long, 0, LLONG_MAX, LLONG_MAX);
+TEST_ADD (long long, 1, LLONG_MAX, 0); // overflow
+TEST_ADD (long long, LLONG_MAX, LLONG_MAX, 0); // overflow
+TEST_ADD (long long, 0, LLONG_MIN, LLONG_MIN);
+TEST_ADD (long long, -1, LLONG_MIN, 0); // overflow
+TEST_ADD (long long, LLONG_MIN, LLONG_MIN, 0); // overflow
+
+TEST_ADD (unsigned char, 0, 0, 0);
+TEST_ADD (unsigned char, 0, UCHAR_MAX, UCHAR_MAX);
+TEST_ADD (unsigned char, 1, UCHAR_MAX, 0); // overflow
+
+TEST_ADD (unsigned char, UCHAR_MAX, UCHAR_MAX, 0); // overflow
+TEST_ADD (unsigned short, 0, 0, 0);
+TEST_ADD (unsigned short, 0, USHRT_MAX, USHRT_MAX);
+TEST_ADD (unsigned short, 1, USHRT_MAX, 0); // overflow
+TEST_ADD (unsigned short, USHRT_MAX, USHRT_MAX, 0); // overflow
+
+TEST_ADD (unsigned, 0, 0, 0);
+TEST_ADD (unsigned, 0, UINT_MAX, UINT_MAX);
+TEST_ADD (unsigned, 1, UINT_MAX, 0); // overflow
+TEST_ADD (unsigned, UINT_MAX, UINT_MAX, 0); // overflow
+
+TEST_ADD (unsigned long, 0, 0, 0);
+TEST_ADD (unsigned long, 0, ULONG_MAX, ULONG_MAX);
+TEST_ADD (unsigned long, 1, ULONG_MAX, 0); // overflow
+TEST_ADD (unsigned long, ULONG_MAX, ULONG_MAX, 0); // overflow
+
+TEST_ADD (unsigned long long, 0, 0, 0);
+TEST_ADD (unsigned long long, 0, ULLONG_MAX, ULLONG_MAX);
+TEST_ADD (unsigned long long, 1, ULLONG_MAX, 0); // overflow
+TEST_ADD (unsigned long long, ULLONG_MAX, ULLONG_MAX, 0); // overflow