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:
parent
379aea728e
commit
44a845ca0e
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)))
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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. */;
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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);
|
|
@ -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);
|
||||
}
|
Loading…
Reference in New Issue