re PR c++/70507 (integer overflow builtins not constant expressions)

PR c++/70507
	PR c/68120
	* builtins.def (BUILT_IN_ADD_OVERFLOW_P, BUILT_IN_SUB_OVERFLOW_P,
	BUILT_IN_MUL_OVERFLOW_P): New builtins.
	* builtins.c: Include gimple-fold.h.
	(fold_builtin_arith_overflow): Handle
	BUILT_IN_{ADD,SUB,MUL}_OVERFLOW_P.
	(fold_builtin_3): Likewise.
	* doc/extend.texi (Integer Overflow Builtins): Document
	__builtin_{add,sub,mul}_overflow_p.
gcc/c/
	* c-typeck.c (convert_arguments): Don't promote last argument
	of BUILT_IN_{ADD,SUB,MUL}_OVERFLOW_P.
gcc/cp/
	* constexpr.c: Include gimple-fold.h.
	(cxx_eval_internal_function): New function.
	(cxx_eval_call_expression): Call it.
	(potential_constant_expression_1): Handle integer arithmetic
	overflow built-ins.
	* tree.c (builtin_valid_in_constant_expr_p): Handle
	BUILT_IN_{ADD,SUB,MUL}_OVERFLOW_P.
gcc/c-family/
	* c-common.c (check_builtin_function_arguments): Handle
	BUILT_IN_{ADD,SUB,MUL}_OVERFLOW_P.
gcc/testsuite/
	* c-c++-common/builtin-arith-overflow-1.c: Add test cases.
	* c-c++-common/builtin-arith-overflow-2.c: New test.
	* g++.dg/ext/builtin-arith-overflow-1.C: New test.
	* g++.dg/cpp0x/constexpr-arith-overflow.C: New test.
	* g++.dg/cpp1y/constexpr-arith-overflow.C: New test.

Co-Authored-By: Jakub Jelinek <jakub@redhat.com>

From-SVN: r237238
This commit is contained in:
Martin Sebor 2016-06-08 19:03:17 +00:00 committed by Jakub Jelinek
parent 379aea728e
commit 44a845ca0e
17 changed files with 1235 additions and 25 deletions

View File

@ -1,3 +1,17 @@
2016-06-08 Martin Sebor <msebor@redhat.com>
Jakub Jelinek <jakub@redhat.com>
PR c++/70507
PR c/68120
* builtins.def (BUILT_IN_ADD_OVERFLOW_P, BUILT_IN_SUB_OVERFLOW_P,
BUILT_IN_MUL_OVERFLOW_P): New builtins.
* builtins.c: Include gimple-fold.h.
(fold_builtin_arith_overflow): Handle
BUILT_IN_{ADD,SUB,MUL}_OVERFLOW_P.
(fold_builtin_3): Likewise.
* doc/extend.texi (Integer Overflow Builtins): Document
__builtin_{add,sub,mul}_overflow_p.
2016-06-08 Jose E. Marchesi <jose.marchesi@oracle.com>
* config/sparc/driver-sparc.c (cpu_names): Fix the entry for the

View File

@ -64,6 +64,7 @@ along with GCC; see the file COPYING3. If not see
#include "rtl-chkp.h"
#include "internal-fn.h"
#include "case-cfn-macros.h"
#include "gimple-fold.h"
struct target_builtins default_target_builtins;
@ -7943,18 +7944,28 @@ fold_builtin_unordered_cmp (location_t loc, tree fndecl, tree arg0, tree arg1,
/* Fold __builtin_{,s,u}{add,sub,mul}{,l,ll}_overflow, either into normal
arithmetics if it can never overflow, or into internal functions that
return both result of arithmetics and overflowed boolean flag in
a complex integer result, or some other check for overflow. */
a complex integer result, or some other check for overflow.
Similarly fold __builtin_{add,sub,mul}_overflow_p to just the overflow
checking part of that. */
static tree
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);
/* The code of the expression corresponding to the type-generic
built-in, or ERROR_MARK for the type-specific ones. */
enum tree_code opcode = ERROR_MARK;
bool ovf_only = false;
switch (fcode)
{
case BUILT_IN_ADD_OVERFLOW_P:
ovf_only = true;
/* FALLTHRU */
case BUILT_IN_ADD_OVERFLOW:
opcode = PLUS_EXPR;
/* FALLTHRU */
case BUILT_IN_SADD_OVERFLOW:
case BUILT_IN_SADDL_OVERFLOW:
case BUILT_IN_SADDLL_OVERFLOW:
@ -7963,7 +7974,12 @@ fold_builtin_arith_overflow (location_t loc, enum built_in_function fcode,
case BUILT_IN_UADDLL_OVERFLOW:
ifn = IFN_ADD_OVERFLOW;
break;
case BUILT_IN_SUB_OVERFLOW_P:
ovf_only = true;
/* FALLTHRU */
case BUILT_IN_SUB_OVERFLOW:
opcode = MINUS_EXPR;
/* FALLTHRU */
case BUILT_IN_SSUB_OVERFLOW:
case BUILT_IN_SSUBL_OVERFLOW:
case BUILT_IN_SSUBLL_OVERFLOW:
@ -7972,7 +7988,12 @@ fold_builtin_arith_overflow (location_t loc, enum built_in_function fcode,
case BUILT_IN_USUBLL_OVERFLOW:
ifn = IFN_SUB_OVERFLOW;
break;
case BUILT_IN_MUL_OVERFLOW_P:
ovf_only = true;
/* FALLTHRU */
case BUILT_IN_MUL_OVERFLOW:
opcode = MULT_EXPR;
/* FALLTHRU */
case BUILT_IN_SMUL_OVERFLOW:
case BUILT_IN_SMULL_OVERFLOW:
case BUILT_IN_SMULLL_OVERFLOW:
@ -7984,6 +8005,25 @@ fold_builtin_arith_overflow (location_t loc, enum built_in_function fcode,
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 = ovf_only ? TREE_TYPE (arg2) : TREE_TYPE (TREE_TYPE (arg2));
/* For the __builtin_{add,sub,mul}_overflow_p builtins, when 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. */
if (ovf_only
&& TREE_CODE (arg0) == INTEGER_CST
&& TREE_CODE (arg1) == INTEGER_CST)
/* Perform the computation in the target type and check for overflow. */
return omit_one_operand_loc (loc, boolean_type_node,
arith_overflowed_p (opcode, type, arg0, arg1)
? boolean_true_node : boolean_false_node,
arg2);
tree ctype = build_complex_type (type);
tree call = build_call_expr_internal_loc (loc, ifn, ctype,
2, arg0, arg1);
@ -7991,6 +8031,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 (ovf_only)
return omit_one_operand_loc (loc, boolean_type_node, ovfres, arg2);
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);
@ -8340,6 +8385,9 @@ fold_builtin_3 (location_t loc, tree fndecl,
case BUILT_IN_ADD_OVERFLOW:
case BUILT_IN_SUB_OVERFLOW:
case BUILT_IN_MUL_OVERFLOW:
case BUILT_IN_ADD_OVERFLOW_P:
case BUILT_IN_SUB_OVERFLOW_P:
case BUILT_IN_MUL_OVERFLOW_P:
case BUILT_IN_SADD_OVERFLOW:
case BUILT_IN_SADDL_OVERFLOW:
case BUILT_IN_SADDLL_OVERFLOW:

View File

@ -710,6 +710,9 @@ DEF_C94_BUILTIN (BUILT_IN_TOWUPPER, "towupper", BT_FN_WINT_WINT, ATTR_PUR
DEF_GCC_BUILTIN (BUILT_IN_ADD_OVERFLOW, "add_overflow", BT_FN_BOOL_VAR, ATTR_NOTHROW_TYPEGENERIC_LEAF)
DEF_GCC_BUILTIN (BUILT_IN_SUB_OVERFLOW, "sub_overflow", BT_FN_BOOL_VAR, ATTR_NOTHROW_TYPEGENERIC_LEAF)
DEF_GCC_BUILTIN (BUILT_IN_MUL_OVERFLOW, "mul_overflow", BT_FN_BOOL_VAR, ATTR_NOTHROW_TYPEGENERIC_LEAF)
DEF_GCC_BUILTIN (BUILT_IN_ADD_OVERFLOW_P, "add_overflow_p", BT_FN_BOOL_VAR, ATTR_CONST_NOTHROW_TYPEGENERIC_LEAF)
DEF_GCC_BUILTIN (BUILT_IN_SUB_OVERFLOW_P, "sub_overflow_p", BT_FN_BOOL_VAR, ATTR_CONST_NOTHROW_TYPEGENERIC_LEAF)
DEF_GCC_BUILTIN (BUILT_IN_MUL_OVERFLOW_P, "mul_overflow_p", BT_FN_BOOL_VAR, ATTR_CONST_NOTHROW_TYPEGENERIC_LEAF)
/* Clang compatibility. */
DEF_GCC_BUILTIN (BUILT_IN_SADD_OVERFLOW, "sadd_overflow", BT_FN_BOOL_INT_INT_INTPTR, ATTR_NOTHROW_LEAF_LIST)
DEF_GCC_BUILTIN (BUILT_IN_SADDL_OVERFLOW, "saddl_overflow", BT_FN_BOOL_LONG_LONG_LONGPTR, ATTR_NOTHROW_LEAF_LIST)

View File

@ -1,3 +1,11 @@
2016-06-08 Martin Sebor <msebor@redhat.com>
Jakub Jelinek <jakub@redhat.com>
PR c++/70507
PR c/68120
* c-common.c (check_builtin_function_arguments): Handle
BUILT_IN_{ADD,SUB,MUL}_OVERFLOW_P.
2016-06-08 Richard Biener <rguenther@suse.de>
* c-common.c (parse_optimize_options): Improve diagnostic messages.

View File

@ -9989,6 +9989,23 @@ check_builtin_function_arguments (location_t loc, vec<location_t> arg_loc,
}
return false;
case BUILT_IN_ADD_OVERFLOW_P:
case BUILT_IN_SUB_OVERFLOW_P:
case BUILT_IN_MUL_OVERFLOW_P:
if (builtin_function_validate_nargs (loc, fndecl, nargs, 3))
{
unsigned i;
for (i = 0; i < 3; i++)
if (!INTEGRAL_TYPE_P (TREE_TYPE (args[i])))
{
error_at (ARG_LOCATION (i), "argument %u in call to function "
"%qE does not have integral type", i + 1, fndecl);
return false;
}
return true;
}
return false;
default:
return true;
}

View File

@ -1,3 +1,11 @@
2016-06-08 Martin Sebor <msebor@redhat.com>
Jakub Jelinek <jakub@redhat.com>
PR c++/70507
PR c/68120
* c-typeck.c (convert_arguments): Don't promote last argument
of BUILT_IN_{ADD,SUB,MUL}_OVERFLOW_P.
2016-06-08 Marek Polacek <polacek@redhat.com>
PR c/71418

View File

@ -3186,6 +3186,7 @@ convert_arguments (location_t loc, vec<location_t> arg_loc, tree typelist,
const bool type_generic = fundecl
&& lookup_attribute ("type generic", TYPE_ATTRIBUTES (TREE_TYPE (fundecl)));
bool type_generic_remove_excess_precision = false;
bool type_generic_overflow_p = false;
tree selector;
/* Change pointer to function to the function itself for
@ -3215,8 +3216,15 @@ convert_arguments (location_t loc, vec<location_t> arg_loc, tree typelist,
type_generic_remove_excess_precision = true;
break;
case BUILT_IN_ADD_OVERFLOW_P:
case BUILT_IN_SUB_OVERFLOW_P:
case BUILT_IN_MUL_OVERFLOW_P:
/* The last argument of these type-generic builtins
should not be promoted. */
type_generic_overflow_p = true;
break;
default:
type_generic_remove_excess_precision = false;
break;
}
}
@ -3466,9 +3474,12 @@ convert_arguments (location_t loc, vec<location_t> arg_loc, tree typelist,
parmval = convert (double_type_node, val);
}
}
else if (excess_precision && !type_generic)
else if ((excess_precision && !type_generic)
|| (type_generic_overflow_p && parmnum == 2))
/* A "double" argument with excess precision being passed
without a prototype or in variable arguments. */
without a prototype or in variable arguments.
The last argument of __builtin_*_overflow_p should not be
promoted. */
parmval = convert (valtype, val);
else if ((invalid_func_diag =
targetm.calls.invalid_arg_for_unprototyped_fn (typelist, fundecl, val)))

View File

@ -1,3 +1,16 @@
2016-06-08 Martin Sebor <msebor@redhat.com>
Jakub Jelinek <jakub@redhat.com>
PR c++/70507
PR c/68120
* constexpr.c: Include gimple-fold.h.
(cxx_eval_internal_function): New function.
(cxx_eval_call_expression): Call it.
(potential_constant_expression_1): Handle integer arithmetic
overflow built-ins.
* tree.c (builtin_valid_in_constant_expr_p): Handle
BUILT_IN_{ADD,SUB,MUL}_OVERFLOW_P.
2016-06-08 Paolo Carlini <paolo.carlini@oracle.com>
* pt.c (tsubst, case TYPENAME_TYPE): Don't delay checking the

View File

@ -31,6 +31,7 @@ along with GCC; see the file COPYING3. If not see
#include "builtins.h"
#include "tree-inline.h"
#include "ubsan.h"
#include "gimple-fold.h"
static bool verify_constant (tree, bool, bool *, bool *);
#define VERIFY_CONSTANT(X) \
@ -1255,6 +1256,69 @@ 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 %qE", t);
*non_constant_p = true;
return t;
}
/* Evaluate constant arguments using OPCODE and return a complex
number containing the result and the overflow bit. */
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)
{
location_t loc = EXPR_LOC_OR_LOC (t, input_location);
tree type = TREE_TYPE (TREE_TYPE (t));
tree result = fold_binary_loc (loc, opcode, type,
fold_convert_loc (loc, type, arg0),
fold_convert_loc (loc, type, arg1));
tree ovf
= build_int_cst (type, arith_overflowed_p (opcode, type, arg0, arg1));
/* Reset TREE_OVERFLOW to avoid warnings for the overflow. */
if (TREE_OVERFLOW (result))
TREE_OVERFLOW (result) = 0;
return build_complex (TREE_TYPE (t), result, ovf);
}
*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 +1334,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)
{
@ -4588,6 +4642,10 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict,
if (fun == NULL_TREE)
{
/* Reset to allow the function to continue past the end
of the block below. Otherwise return early. */
bool bail = true;
if (TREE_CODE (t) == CALL_EXPR
&& CALL_EXPR_FN (t) == NULL_TREE)
switch (CALL_EXPR_IFN (t))
@ -4598,16 +4656,27 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict,
case IFN_UBSAN_BOUNDS:
case IFN_UBSAN_VPTR:
return true;
case IFN_ADD_OVERFLOW:
case IFN_SUB_OVERFLOW:
case IFN_MUL_OVERFLOW:
bail = false;
default:
break;
}
/* fold_call_expr can't do anything with IFN calls. */
if (flags & tf_error)
error_at (EXPR_LOC_OR_LOC (t, input_location),
"call to internal function");
return false;
if (bail)
{
/* fold_call_expr can't do anything with IFN calls. */
if (flags & tf_error)
error_at (EXPR_LOC_OR_LOC (t, input_location),
"call to internal function %qE", t);
return false;
}
}
if (is_overloaded_fn (fun))
if (fun && is_overloaded_fn (fun))
{
if (TREE_CODE (fun) == FUNCTION_DECL)
{
@ -4652,7 +4721,7 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict,
i = num_artificial_parms_for (fun);
fun = DECL_ORIGIN (fun);
}
else
else if (fun)
{
if (RECUR (fun, rval))
/* Might end up being a constant function pointer. */;

View File

@ -352,6 +352,12 @@ 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_P:
case BUILT_IN_SUB_OVERFLOW_P:
case BUILT_IN_MUL_OVERFLOW_P:
/* These have constant results even if their operands are
non-constant. */
case BUILT_IN_CONSTANT_P:

View File

@ -9869,6 +9869,47 @@ functions above, except they perform multiplication, instead of addition.
@end deftypefn
The following built-in functions allow checking if simple arithmetic operation
would overflow.
@deftypefn {Built-in Function} bool __builtin_add_overflow_p (@var{type1} a, @var{type2} b, @var{type3} c)
@deftypefnx {Built-in Function} bool __builtin_sub_overflow_p (@var{type1} a, @var{type2} b, @var{type3} c)
@deftypefnx {Built-in Function} bool __builtin_mul_overflow_p (@var{type1} a, @var{type2} b, @var{type3} c)
These built-in functions are similar to @code{__builtin_add_overflow},
@code{__builtin_sub_overflow}, or @code{__builtin_mul_overflow}, except that
they don't store the result of the arithmetic operation anywhere and the
last argument is not a pointer, but some integral expression.
The 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 of the third argument. If the cast result is equal to the infinite
precision result, the built-in functions return false, otherwise they return true.
The value of the third argument is ignored, just the side-effects in the third argument
are evaluated, and no integral argument promotions are performed on the last 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_P(a, b) \
__builtin_add_overflow_p (a, b, (__typeof__ ((a) + (b))) 0)
enum @{
A = INT_MAX, B = 3,
C = INT_ADD_OVERFLOW_P (A, B) ? 0 : A + B,
D = __builtin_add_overflow_p (1, SCHAR_MAX, (signed char) 0)
@};
@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.
@end deftypefn
@node x86 specific memory model extensions for transactional memory
@section x86-Specific Memory Model Extensions for Transactional Memory

View File

@ -1,3 +1,14 @@
2016-06-08 Martin Sebor <msebor@redhat.com>
Jakub Jelinek <jakub@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/ext/builtin-arith-overflow-1.C: New test.
* g++.dg/cpp0x/constexpr-arith-overflow.C: New test.
* g++.dg/cpp1y/constexpr-arith-overflow.C: New test.
2016-06-08 Jakub Jelinek <jakub@redhat.com>
PR c++/71442

View File

@ -6,6 +6,9 @@ f1 (void)
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" } */
x += __builtin_add_overflow_p (); /* { dg-error "not enough arguments to function" } */
x += __builtin_sub_overflow_p (); /* { dg-error "not enough arguments to function" } */
x += __builtin_mul_overflow_p (); /* { dg-error "not enough arguments to function" } */
return x;
}
@ -15,6 +18,10 @@ f2 (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_mul_overflow (a, b, c, d); /* { dg-error "too many arguments to function" } */
x += __builtin_add_overflow_p (a, b, d, d); /* { dg-error "too many arguments to function" } */
x += __builtin_sub_overflow_p (a, b, d, d, 1, d); /* { dg-error "too many arguments to function" } */
x += __builtin_mul_overflow_p (a, b, d, d); /* { dg-error "too many arguments to function" } */
return x;
}
@ -33,6 +40,15 @@ f3 (float fa, int a, _Complex long int ca, double fb, void *pb, int b, enum E eb
x += __builtin_add_overflow (a, pb, c); /* { dg-error "argument 2 in call to function\[^\n\r]*does not have integral type" } */
x += __builtin_sub_overflow (a, eb, c);
x += __builtin_mul_overflow (a, bb, c);
x += __builtin_add_overflow_p (fa, b, a); /* { dg-error "argument 1 in call to function\[^\n\r]*does not have integral type" } */
x += __builtin_sub_overflow_p (ca, b, eb); /* { dg-error "argument 1 in call to function\[^\n\r]*does not have integral type" } */
x += __builtin_mul_overflow_p (a, fb, bb); /* { dg-error "argument 2 in call to function\[^\n\r]*does not have integral type" } */
x += __builtin_add_overflow_p (a, pb, a); /* { dg-error "argument 2 in call to function\[^\n\r]*does not have integral type" } */
x += __builtin_sub_overflow_p (a, eb, eb);
x += __builtin_mul_overflow_p (a, bb, bb);
x += __builtin_add_overflow_p (a, b, fa); /* { dg-error "argument 3 in call to function\[^\n\r]*does not have integral type" } */
x += __builtin_sub_overflow_p (a, b, ca); /* { dg-error "argument 3 in call to function\[^\n\r]*does not have integral type" } */
x += __builtin_mul_overflow_p (a, b, c); /* { dg-error "argument 3 in call to function\[^\n\r]*does not have integral type" } */
return x;
}

View File

@ -0,0 +1,493 @@
/* 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) ((x) + (y))
#define sadd(x, y) ((x) + (y))
#define saddl(x, y) ((x) + (y))
#define saddll(x, y) ((x) + (y))
#define uadd(x, y) ((x) + (y))
#define uaddl(x, y) ((x) + (y))
#define uaddll(x, y) ((x) + (y))
#define sub(x, y) ((x) - (y))
#define ssub(x, y) ((x) - (y))
#define ssubl(x, y) ((x) - (y))
#define ssubll(x, y) ((x) - (y))
#define usub(x, y) ((x) - (y))
#define usubl(x, y) ((x) - (y))
#define usubll(x, y) ((x) - (y))
#define mul(x, y) ((x) * (y))
#define smul(x, y) ((x) * (y))
#define smull(x, y) ((x) * (y))
#define smulll(x, y) ((x) * (y))
#define umul(x, y) ((x) * (y))
#define umull(x, y) ((x) * (y))
#define umulll(x, y) ((x) * (y))
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. */
# define G_TEST(op, T, x, y, vflow) \
RuntimeAssert(op, __typeof__ (op (x, y)), T, x, y, vflow); \
StaticAssert ((vflow) == __builtin_ ## op ## _overflow_p ((x), (y), (T)0))
/* 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);
/* Verify any slicing in the result type doesn't prevent the overflow
from being detected. */
G_TEST (add, signed char, UCHAR_MAX + 1, 0, 1);
G_TEST (add, signed char, UCHAR_MAX + 1, 1, 1);
G_TEST (add, signed char, 1, UCHAR_MAX + 1, 1);
G_TEST (add, unsigned char, 0, 0, 0);
/* Verify any slicing in the result type doesn't prevent the overflow
from being detected. */
G_TEST (add, unsigned char, UCHAR_MAX + 1, 0, 1);
G_TEST (add, unsigned char, UCHAR_MAX + 1, 1, 1);
G_TEST (add, unsigned char, 1, UCHAR_MAX + 1, 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
evaluates to a (not-necessarily constant) expression indicating
whether or not the constant expression (x op y) overflows.
The type-specific forms of the built-ins detect overflow after
arithmetic promotions and so unlike the type-generic overloads
cannot detect overflow in char or short types. */
#define T_TEST(op, T, x, y, vflow) \
RuntimeAssert (op, T, __typeof__ ((x) + (y)), x, y, vflow)
/* 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;
}

View File

@ -0,0 +1,212 @@
// 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
// Make sure the built-ins are accepted in the following contexts
// where constant expressions are required and that they return
// the expected overflow value.
namespace Enum {
enum Add {
a0 = __builtin_add_overflow_p ( 1, 1, 0),
a1 = __builtin_add_overflow_p (INT_MAX, 1, 0)
};
Assert (a0 == 0);
Assert (a1 == 1);
enum Sub {
s0 = __builtin_sub_overflow_p ( 1, 1, 0),
s1 = __builtin_sub_overflow_p (INT_MIN, 1, 0)
};
Assert (s0 == 0);
Assert (s1 == 1);
enum Mul {
m0 = __builtin_add_overflow_p ( 1, 1, 0),
m1 = __builtin_add_overflow_p (INT_MAX, INT_MAX, 0)
};
Assert (m0 == 0);
Assert (m1 == 1);
} // namespace Enum
namespace TemplateArg {
template <class T, class U, class V,
T x, U y, bool v, bool z = __builtin_add_overflow_p (x, y, V ())>
struct Add {
Assert (z == v);
};
template <class T, class U, class V,
T x, U y, bool v, bool z = __builtin_sub_overflow_p (x, y, V ())>
struct Sub {
Assert (z == v);
};
template <class T, class U, class V,
T x, U y, bool v, bool z = __builtin_mul_overflow_p (x, y, V ())>
struct Mul {
Assert (z == v);
};
template struct Add<int, int, int, 1, 1, false>;
template struct Add<int, int, int, 1, INT_MAX, true>;
template struct Sub<int, int, int, 1, 1, false>;
template struct Sub<int, int, int, -2, INT_MAX, true>;
template struct Mul<int, int, int, 1, 1, false>;
template struct Mul<int, int, int, 2, INT_MAX / 2 + 1, true>;
} // namespace TemplateArg
#if __cplusplus >= 201402L
namespace Initializer {
struct Result {
int res;
bool vflow;
};
constexpr Result
add_vflow (int a, int b)
{
#if 1
Result res = { a + b, __builtin_add_overflow_p (a, b, int ()) };
#else
// The following fails to compile because of c++/71391 - error
// on aggregate initialization with side-effects in a constexpr
// function
int c = 0;
Result res = { 0, __builtin_add_overflow (a, b, &c) };
res.c = c;
#endif
return res;
}
constexpr Result sum = add_vflow (123, 456);
Assert (sum.res == 123 + 456);
Assert (!sum.vflow);
} // namespace Initializer
#endif // __cplusplus >= 201402L

View File

@ -0,0 +1,229 @@
// Test to exercise that the type-specific integer arithmetic built-ins
// with overflow checking can be used in C++ 14 constant expressions.
// -Woverflow is disabled to prevent (bogus?) G++ warnings.
// { dg-do compile { target c++14 } }
// { dg-additional-options "-Wno-overflow" }
#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)
// Helper macros.
#define sadd(x, y) ((x) + (y))
#define saddl(x, y) ((x) + (y))
#define saddll(x, y) ((x) + (y))
#define uadd(x, y) ((x) + (y))
#define uaddl(x, y) ((x) + (y))
#define uaddll(x, y) ((x) + (y))
#define ssub(x, y) ((x) - (y))
#define ssubl(x, y) ((x) - (y))
#define ssubll(x, y) ((x) - (y))
#define usub(x, y) ((x) - (y))
#define usubl(x, y) ((x) - (y))
#define usubll(x, y) ((x) - (y))
#define smul(x, y) ((x) * (y))
#define smull(x, y) ((x) * (y))
#define smulll(x, y) ((x) * (y))
#define umul(x, y) ((x) * (y))
#define umull(x, y) ((x) * (y))
#define umulll(x, y) ((x) * (y))
// Result object.
template <class T>
struct Res
{
constexpr Res (T a, bool v): z (a), v (v) { }
T z; bool v;
};
template <class T>
constexpr bool operator== (Res<T> a, Res<T> b)
{
return a.z == b.z && a.v == b.v;
}
#define StaticAssert(expr) static_assert ((expr), #expr)
#define CONCAT(a, b) a ## b
#define CAT(a, b) CONCAT (a, b)
// Helper to determine the type of the result of the arithmetic
// as specified by the built-ins.
template <class T> struct ResType { typedef T type; };
template <> struct ResType<signed char> { typedef int type; };
template <> struct ResType<unsigned char> { typedef unsigned type; };
template <> struct ResType<signed short> { typedef int type; };
template <> struct ResType<unsigned short> { typedef unsigned type; };
// Macro to define a single test case verifying that integer overflow
// is detected when expected, and when not, that the result matches
// the result computed using ordinary arithmetic. The result cannot
// be tested in the presence of overflow since it's not a core
// constant expression.
#define TEST(op, T, x, y, vflow) \
constexpr Res<T> CAT (op, __LINE__)(T a, T b) \
{ \
ResType<T>::type c = 0; \
bool v = __builtin_ ## op ## _overflow (a, b, &c); \
return Res<T>(c, v); \
} \
StaticAssert (vflow ? CAT (op, __LINE__)(x, y).v \
: CAT (op, __LINE__)(x, y) == Res<T>(op (x, y), vflow))
/* Signed int addition. */
TEST (sadd, signed char, 0, 0, 0);
TEST (sadd, signed char, 0, SCHAR_MAX, 0);
TEST (sadd, signed char, 1, SCHAR_MAX, 0);
TEST (sadd, signed char, SCHAR_MAX, SCHAR_MAX, 0);
TEST (sadd, signed char, 0, SCHAR_MIN, 0);
TEST (sadd, signed char, -1, SCHAR_MIN, 0);
TEST (sadd, short, 0, 0, 0);
TEST (sadd, short, 0, SHRT_MAX, 0);
TEST (sadd, short, 1, SHRT_MAX, 0);
TEST (sadd, short, SHRT_MAX, SHRT_MAX, 0);
TEST (sadd, short, 0, SHRT_MIN, 0);
TEST (sadd, short, -1, SHRT_MIN, 0);
TEST (sadd, short, SHRT_MIN, SHRT_MIN, 0);
TEST (sadd, int, 0, 0, 0);
TEST (sadd, int, 0, INT_MAX, 0);
TEST (sadd, int, 1, INT_MAX, 1);
TEST (sadd, int, INT_MAX, INT_MAX, 1);
TEST (sadd, int, 0, INT_MIN, 0);
TEST (sadd, int, -1, INT_MIN, 1);
TEST (sadd, int, INT_MIN, INT_MIN, 1);
/* Signed long addition. */
TEST (saddl, long, 0L, 0L, 0);
TEST (saddl, long, 0L, LONG_MAX, 0);
TEST (saddl, long, 1L, LONG_MAX, 1);
TEST (saddl, long, LONG_MAX, LONG_MAX, 1);
TEST (saddl, long, 0L, LONG_MIN, 0);
TEST (saddl, long, -1L, LONG_MIN, 1);
TEST (saddl, long, LONG_MIN, LONG_MIN, 1);
TEST (saddll, long long, 0LL, 0LL, 0);
TEST (saddll, long long, 0LL, LLONG_MAX, 0);
TEST (saddll, long long, 1LL, LLONG_MAX, 1);
TEST (saddll, long long, LLONG_MAX, LLONG_MAX, 1);
TEST (saddll, long long, 0LL, LLONG_MIN, 0);
TEST (saddll, long long, -1LL, LLONG_MIN, 1);
TEST (saddll, long long, LLONG_MIN, LLONG_MIN, 1);
/* Unsigned int addition. */
TEST (uadd, unsigned char, 0U, 0U, 0);
TEST (uadd, unsigned char, 0U, UCHAR_MAX, 0);
TEST (uadd, unsigned char, 1U, UCHAR_MAX, 0);
TEST (uadd, unsigned char, UCHAR_MAX, UCHAR_MAX, 0);
TEST (uadd, unsigned short, 0U, 0U, 0);
TEST (uadd, unsigned short, 0U, USHRT_MAX, 0);
TEST (uadd, unsigned short, 1U, USHRT_MAX, 0);
TEST (uadd, unsigned short, USHRT_MAX, USHRT_MAX, 0);
TEST (uadd, unsigned, 0U, 0U, 0);
TEST (uadd, unsigned, 0U, UINT_MAX, 0);
TEST (uadd, unsigned, 1U, UINT_MAX, 1);
TEST (uadd, unsigned, UINT_MAX, UINT_MAX, 1);
/* Unsigned long addition. */
TEST (uaddl, unsigned long, 0UL, 0UL, 0);
TEST (uaddl, unsigned long, 0UL, ULONG_MAX, 0);
TEST (uaddl, unsigned long, 1UL, ULONG_MAX, 1);
TEST (uaddl, unsigned long, ULONG_MAX, ULONG_MAX, 1);
TEST (uaddll, unsigned long long, 0ULL, 0ULL, 0);
TEST (uaddll, unsigned long long, 0ULL, ULLONG_MAX, 0);
TEST (uaddll, unsigned long long, 1ULL, ULLONG_MAX, 1);
TEST (uaddll, unsigned long long, ULLONG_MAX, ULLONG_MAX, 1);
/* Signed int subtraction. */
TEST (ssub, signed char, 0, 0, 0);
TEST (ssub, signed char, 0, SCHAR_MAX, 0);
TEST (ssub, signed char, 1, SCHAR_MAX, 0);
TEST (ssub, signed char, SCHAR_MAX, SCHAR_MAX, 0);
TEST (ssub, signed char, 0, SCHAR_MIN, 0);
TEST (ssub, signed char, -1, SCHAR_MIN, 0);
TEST (ssub, short, 0, 0, 0);
TEST (ssub, short, 0, SHRT_MAX, 0);
TEST (ssub, short, 1, SHRT_MAX, 0);
TEST (ssub, short, SHRT_MAX, SHRT_MAX, 0);
TEST (ssub, short, 0, SHRT_MIN, 0);
TEST (ssub, short, -1, SHRT_MIN, 0);
TEST (ssub, short, SHRT_MIN, SHRT_MIN, 0);
TEST (ssub, int, 0, 0, 0);
TEST (ssub, int, 0, INT_MAX, 0);
TEST (ssub, int, 1, INT_MAX, 0);
TEST (ssub, int, INT_MAX, INT_MAX, 0);
TEST (ssub, int, 0, INT_MIN, 1);
TEST (ssub, int, -1, INT_MIN, 0);
TEST (ssub, int, INT_MIN, INT_MIN, 0);
/* Signed long subtraction. */
TEST (ssubl, long, 0L, 0L, 0);
TEST (ssubl, long, 0L, LONG_MAX, 0);
TEST (ssubl, long, 1L, LONG_MAX, 0);
TEST (ssubl, long, LONG_MAX, LONG_MAX, 0);
TEST (ssubl, long, 0L, LONG_MIN, 1);
TEST (ssubl, long, -1L, LONG_MIN, 0);
TEST (ssubl, long, LONG_MIN, LONG_MIN, 0);
/* Signed long long subtraction. */
TEST (ssubll, long long, 0LL, 0LL, 0);
TEST (ssubll, long long, 0LL, LLONG_MAX, 0);
TEST (ssubll, long long, 1LL, LLONG_MAX, 0);
TEST (ssubll, long long, LLONG_MAX, LLONG_MAX, 0);
TEST (ssubll, long long, 0LL, LLONG_MIN, 1);
TEST (ssubll, long long, -1LL, LLONG_MIN, 0);
TEST (ssubll, long long, LLONG_MIN, LLONG_MIN, 0);
/* Unsigned int subtraction. */
TEST (usub, unsigned char, 0U, 0U, 0);
TEST (usub, unsigned char, 0U, UCHAR_MAX, 1);
TEST (usub, unsigned char, 1U, UCHAR_MAX, 1);
TEST (usub, unsigned char, UCHAR_MAX, UCHAR_MAX, 0);
TEST (usub, unsigned short, 0U, 0U, 0);
TEST (usub, unsigned short, 0U, USHRT_MAX, 1);
TEST (usub, unsigned short, 1U, USHRT_MAX, 1);
TEST (usub, unsigned short, USHRT_MAX, USHRT_MAX, 0);
TEST (usub, unsigned, 0U, 0U, 0);
TEST (usub, unsigned, 0U, UINT_MAX, 1);
TEST (usub, unsigned, 1U, UINT_MAX, 1);
TEST (usub, unsigned, UINT_MAX, UINT_MAX, 0);
/* Unsigned long subtraction. */
TEST (usubl, unsigned long, 0UL, 0UL, 0);
TEST (usubl, unsigned long, 0UL, ULONG_MAX, 1);
TEST (usubl, unsigned long, 1UL, ULONG_MAX, 1);
TEST (usubl, unsigned long, ULONG_MAX, ULONG_MAX, 0);
/* Unsigned long long subtraction. */
TEST (usubll, unsigned long long, 0ULL, 0ULL, 0);
TEST (usubll, unsigned long long, 0ULL, ULLONG_MAX, 1);
TEST (usubll, unsigned long long, 1ULL, ULLONG_MAX, 1);
TEST (usubll, unsigned long long, ULLONG_MAX, ULLONG_MAX, 0);

View File

@ -0,0 +1,11 @@
// { dg-do compile }
enum A { B = 1, C = 2, D = __builtin_add_overflow_p (B, C, C) };
int e[__builtin_add_overflow_p (B, C, C) + 1];
template <int N> int foo (int);
void
bar ()
{
foo <__builtin_add_overflow_p (B, C, C) + 1> (0);
}