* [PATCH] integer overflow checking builtins in constant expressions @ 2016-05-01 16:40 Martin Sebor 2016-05-09 16:39 ` PING " Martin Sebor ` (2 more replies) 0 siblings, 3 replies; 36+ messages in thread From: Martin Sebor @ 2016-05-01 16:40 UTC (permalink / raw) To: Gcc Patch List, Jason Merrill, Joseph S. Myers [-- Attachment #1: Type: text/plain, Size: 1828 bytes --] c/68120 - can't easily deal with integer overflow at compile time, is an enhancement request to make the integer overflow intrinsics usable in constant expressions in C (in addition to letting them be invoked with just two arguments). The inability to use the built-ins in constant expressions also limited to non-constexpr the contexts in which the patch for c++/ 69517 - SEGV on a VLA with excess initializer elements, was able to prevent the SEGV. This limitation is noted in c++/70507 - integer overflow builtins not constant expressions. The attached patch implements the request in c/68120 for both the C and C++ front-ends. It stops short of providing the new __builtin_add_wrapv function requested in c/68120. It doesn't seem necessary since the same functionality is available with the patch via the existing built-ins. With this enhancement in place it will be possible to add the C++ VLA checking to constexpr functions and fully resolve c++/ 69517 (which I plan to do next). While testing the patch, I also noticed an minor inconsistency in the text of the diagnostic GCC issues for invalid calls to the built-ins with insufficient numbers of arguments: for one set of built-ins the error says: "not enough arguments," while for another it says: "too few arguments." I raised PR c/70883 - inconsistent error message for calls to __builtin_add_overflow with too few arguments, for this and include a fix in this patch as well. Martin PS The enhancement to call the type-generic built-ins with a null pointer is not available in C++ 98 mode because GCC doesn't allow null pointers in constant expressions. Since C and later versions of C++ do, it seems that it might be worthwhile to relax the rules and accept them in C++ 98 as well so that the built-ins can be used portably across all versions of C++. [-- Attachment #2: gcc-70507.patch --] [-- Type: text/x-patch, Size: 68037 bytes --] PR c++/70507 - integer overflow builtins not constant expressions PR c/68120 - can't easily deal with integer overflow at compile time PR c/70883 - inconsistent error message for calls to __builtin_add_overflow with too few arguments gcc/testsuite/ChangeLog: 2016-04-30 Martin Sebor <msebor@redhat.com> PR c++/70507 PR c/68120 PR c/70883 * c-c++-common/builtin-arith-overflow-1.c: Add test cases. * c-c++-common/builtin-arith-overflow-2.c: New test. * g++.dg/cpp0x/constexpr-arith-overflow.C: New test. * gcc.dg/builtin-constant_p-1.c: Adjust text of diagnostic. * gcc.dg/builtins-error.c: Same. gcc/cp/ChangeLog: 2016-04-30 Martin Sebor <msebor@redhat.com> PR c++/70507 PR c/68120 PR c/70883 * constexpr.c (cxx_eval_internal_function): New function. (cxx_eval_call_expression): Call it. * tree.c (builtin_valid_in_constant_expr_p): Handle integer arithmetic overflow built-ins. gcc/c-family/ChangeLog: 2016-04-30 Martin Sebor <msebor@redhat.com> PR c/68120 PR c/70883 * c-common.c (builtin_function_validate_nargs): Adjust text of diagnostic. (check_builtin_function_arguments): Allow type-specific integer arithmetic overflow built-ins to take either 2 or three arguments. gcc/ChangeLog: 2016-04-30 Martin Sebor <msebor@redhat.com> PR c++/70507 PR c/68120 PR c/70883 * builtin-types.def (BT_FN_BOOL_INT_INT_VAR, BT_FN_BOOL_LONG_LONG_VAR) (BT_FN_BOOL_LONGLONG_LONGLONG_VAR, BT_FN_BOOL_UINT_UINT_VAR) (BT_FN_BOOL_UINT_UINT_VAR, BT_FN_BOOL_ULONG_ULONG_VAR) (BT_FN_BOOL_ULONGLONG_ULONGLONG_VAR): New. * builtins.c (fold_builtin_unordered_cmp): Handle integer arithmetic overflow built-ins. (fold_builtin_2): Handle invocations of type-specific integer arithmetic overflow built-ins with two arguments. * builtins.def (BUILT_IN_SADD_OVERFLOW, BUILT_IN_SADDL_OVERFLOW): Declare built-ins to take two or more arguments (constraining them to take two or three in c-common.c). (BUILT_IN_SADDLL_OVERFLOW, BUILT_IN_SSUB_OVERFLOW): Same. (BUILT_IN_SSUBL_OVERFLOW, BUILT_IN_SSUBLL_OVERFLOW): Same. (BUILT_IN_SMUL_OVERFLOW, BUILT_IN_SMULL_OVERFLOW): Same. (BUILT_IN_SMULLL_OVERFLOW, BUILT_IN_UADD_OVERFLOW): Same. (BUILT_IN_UADDL_OVERFLOW, BUILT_IN_UADDLL_OVERFLOW): Same. (BUILT_IN_USUB_OVERFLOW, BUILT_IN_USUBL_OVERFLOW): Same. (BUILT_IN_USUBLL_OVERFLOW, BUILT_IN_UMUL_OVERFLOW): Same. (BUILT_IN_UMULL_OVERFLOW, BUILT_IN_UMULLL_OVERFLOW): Same. * doc/extend.texi (Integer Overflow Builtins): Update. diff --git a/gcc/builtin-types.def b/gcc/builtin-types.def index 7fab9f8..ee63284 100644 --- a/gcc/builtin-types.def +++ b/gcc/builtin-types.def @@ -451,6 +451,16 @@ DEF_FUNCTION_TYPE_3 (BT_FN_BOOL_ULONG_ULONG_ULONGPTR, BT_BOOL, BT_ULONG, DEF_FUNCTION_TYPE_3 (BT_FN_BOOL_ULONGLONG_ULONGLONG_ULONGLONGPTR, BT_BOOL, BT_ULONGLONG, BT_ULONGLONG, BT_PTR_ULONGLONG) +DEF_FUNCTION_TYPE_VAR_2 (BT_FN_BOOL_INT_INT_VAR, BT_BOOL, BT_INT, BT_INT) +DEF_FUNCTION_TYPE_VAR_2 (BT_FN_BOOL_LONG_LONG_VAR, BT_BOOL, BT_LONG, BT_LONG) +DEF_FUNCTION_TYPE_VAR_2 (BT_FN_BOOL_LONGLONG_LONGLONG_VAR, BT_BOOL, + BT_LONGLONG, BT_LONGLONG) +DEF_FUNCTION_TYPE_VAR_2 (BT_FN_BOOL_UINT_UINT_VAR, BT_BOOL, BT_UINT, BT_UINT) +DEF_FUNCTION_TYPE_VAR_2 (BT_FN_BOOL_ULONG_ULONG_VAR, BT_BOOL, BT_ULONG, + BT_ULONG) +DEF_FUNCTION_TYPE_VAR_2 (BT_FN_BOOL_ULONGLONG_ULONGLONG_VAR, BT_BOOL, + BT_ULONGLONG, BT_ULONGLONG) + DEF_FUNCTION_TYPE_4 (BT_FN_SIZE_CONST_PTR_SIZE_SIZE_FILEPTR, BT_SIZE, BT_CONST_PTR, BT_SIZE, BT_SIZE, BT_FILEPTR) DEF_FUNCTION_TYPE_4 (BT_FN_INT_STRING_SIZE_CONST_STRING_VALIST_ARG, diff --git a/gcc/builtins.c b/gcc/builtins.c index 3d89baf..c43d08e 100644 --- a/gcc/builtins.c +++ b/gcc/builtins.c @@ -7878,50 +7878,101 @@ fold_builtin_unordered_cmp (location_t loc, tree fndecl, tree arg0, tree arg1, static tree fold_builtin_arith_overflow (location_t loc, enum built_in_function fcode, - tree arg0, tree arg1, tree arg2) + tree arg0, tree arg1, tree arg2 = NULL_TREE) { enum internal_fn ifn = IFN_LAST; - tree type = TREE_TYPE (TREE_TYPE (arg2)); - tree mem_arg2 = build_fold_indirect_ref_loc (loc, arg2); + enum tree_code opcode; + + /* 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 names of each of the other overloads determines + the target type and so the last argument can be omitted. */ + tree type = NULL_TREE; + switch (fcode) { case BUILT_IN_ADD_OVERFLOW: + type = type ? type : TREE_TYPE (TREE_TYPE (arg2)); case BUILT_IN_SADD_OVERFLOW: + type = type ? type : integer_type_node; case BUILT_IN_SADDL_OVERFLOW: + type = type ? type : long_integer_type_node; case BUILT_IN_SADDLL_OVERFLOW: + type = type ? type : long_long_integer_type_node; case BUILT_IN_UADD_OVERFLOW: + type = type ? type : unsigned_type_node; case BUILT_IN_UADDL_OVERFLOW: + type = type ? type : long_unsigned_type_node; case BUILT_IN_UADDLL_OVERFLOW: + type = type ? type : long_long_unsigned_type_node; ifn = IFN_ADD_OVERFLOW; + opcode = PLUS_EXPR; break; case BUILT_IN_SUB_OVERFLOW: + type = type ? type : TREE_TYPE (TREE_TYPE (arg2)); case BUILT_IN_SSUB_OVERFLOW: + type = type ? type : integer_type_node; case BUILT_IN_SSUBL_OVERFLOW: + type = type ? type : long_integer_type_node; case BUILT_IN_SSUBLL_OVERFLOW: + type = type ? type : long_long_integer_type_node; case BUILT_IN_USUB_OVERFLOW: + type = type ? type : unsigned_type_node; case BUILT_IN_USUBL_OVERFLOW: + type = type ? type : long_unsigned_type_node; case BUILT_IN_USUBLL_OVERFLOW: + type = type ? type : long_long_unsigned_type_node; ifn = IFN_SUB_OVERFLOW; + opcode = MINUS_EXPR; break; case BUILT_IN_MUL_OVERFLOW: + type = type ? type : TREE_TYPE (TREE_TYPE (arg2)); case BUILT_IN_SMUL_OVERFLOW: + type = type ? type : integer_type_node; case BUILT_IN_SMULL_OVERFLOW: + type = type ? type : long_integer_type_node; case BUILT_IN_SMULLL_OVERFLOW: + type = type ? type : long_long_integer_type_node; case BUILT_IN_UMUL_OVERFLOW: + type = type ? type : unsigned_type_node; case BUILT_IN_UMULL_OVERFLOW: + type = type ? type : long_unsigned_type_node; case BUILT_IN_UMULLL_OVERFLOW: + type = type ? type : long_long_unsigned_type_node; ifn = IFN_MUL_OVERFLOW; + opcode = MULT_EXPR; break; default: gcc_unreachable (); } + + /* When the last argument is a NULL pointer and the first two arguments + are constant, attempt to fold the built-in call into a constant + expression indicating whether or not it detected an overflow but + without storing the result. */ + if ((!arg2 || zerop (arg2)) + && TREE_CODE (arg0) == INTEGER_CST && TREE_CODE (arg1) == INTEGER_CST) + { + /* Perform the computation in the target type and check for overflow. */ + arg0 = fold_convert (type, arg0); + arg1 = fold_convert (type, arg1); + + if (tree result = size_binop_loc (loc, opcode, arg0, arg1)) + return TREE_OVERFLOW (result) ? build_one_cst (boolean_type_node) + : build_zero_cst (boolean_type_node); + } + tree ctype = build_complex_type (type); - tree call = build_call_expr_internal_loc (loc, ifn, ctype, - 2, arg0, arg1); + tree call = build_call_expr_internal_loc (loc, ifn, ctype, 2, arg0, arg1); tree tgt = save_expr (call); 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 (!arg2 || zerop (arg2)) + return ovfres; + + tree mem_arg2 = build_fold_indirect_ref_loc (loc, arg2); tree store = fold_build2_loc (loc, MODIFY_EXPR, void_type_node, mem_arg2, intres); return build2_loc (loc, COMPOUND_EXPR, boolean_type_node, store, ovfres); @@ -8171,6 +8222,29 @@ fold_builtin_2 (location_t loc, tree fndecl, tree arg0, tree arg1) case BUILT_IN_ATOMIC_IS_LOCK_FREE: return fold_builtin_atomic_is_lock_free (arg0, arg1); + case BUILT_IN_ADD_OVERFLOW: + case BUILT_IN_SUB_OVERFLOW: + case BUILT_IN_MUL_OVERFLOW: + case BUILT_IN_SADD_OVERFLOW: + case BUILT_IN_SADDL_OVERFLOW: + case BUILT_IN_SADDLL_OVERFLOW: + case BUILT_IN_SSUB_OVERFLOW: + case BUILT_IN_SSUBL_OVERFLOW: + case BUILT_IN_SSUBLL_OVERFLOW: + case BUILT_IN_SMUL_OVERFLOW: + case BUILT_IN_SMULL_OVERFLOW: + case BUILT_IN_SMULLL_OVERFLOW: + case BUILT_IN_UADD_OVERFLOW: + case BUILT_IN_UADDL_OVERFLOW: + case BUILT_IN_UADDLL_OVERFLOW: + case BUILT_IN_USUB_OVERFLOW: + case BUILT_IN_USUBL_OVERFLOW: + case BUILT_IN_USUBLL_OVERFLOW: + case BUILT_IN_UMUL_OVERFLOW: + case BUILT_IN_UMULL_OVERFLOW: + case BUILT_IN_UMULLL_OVERFLOW: + return fold_builtin_arith_overflow (loc, fcode, arg0, arg1); + default: break; } diff --git a/gcc/builtins.def b/gcc/builtins.def index 2fc7f65..34ebc22 100644 --- a/gcc/builtins.def +++ b/gcc/builtins.def @@ -706,29 +706,30 @@ DEF_C94_BUILTIN (BUILT_IN_ISWXDIGIT, "iswxdigit", BT_FN_INT_WINT, ATTR_PU DEF_C94_BUILTIN (BUILT_IN_TOWLOWER, "towlower", BT_FN_WINT_WINT, ATTR_PURE_NOTHROW_LEAF_LIST) DEF_C94_BUILTIN (BUILT_IN_TOWUPPER, "towupper", BT_FN_WINT_WINT, ATTR_PURE_NOTHROW_LEAF_LIST) -/* Category: integer overflow checking builtins. */ +/* Category: type-generic integer overflow checking builtins. */ 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) -/* 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) -DEF_GCC_BUILTIN (BUILT_IN_SADDLL_OVERFLOW, "saddll_overflow", BT_FN_BOOL_LONGLONG_LONGLONG_LONGLONGPTR, ATTR_NOTHROW_LEAF_LIST) -DEF_GCC_BUILTIN (BUILT_IN_SSUB_OVERFLOW, "ssub_overflow", BT_FN_BOOL_INT_INT_INTPTR, ATTR_NOTHROW_LEAF_LIST) -DEF_GCC_BUILTIN (BUILT_IN_SSUBL_OVERFLOW, "ssubl_overflow", BT_FN_BOOL_LONG_LONG_LONGPTR, ATTR_NOTHROW_LEAF_LIST) -DEF_GCC_BUILTIN (BUILT_IN_SSUBLL_OVERFLOW, "ssubll_overflow", BT_FN_BOOL_LONGLONG_LONGLONG_LONGLONGPTR, ATTR_NOTHROW_LEAF_LIST) -DEF_GCC_BUILTIN (BUILT_IN_SMUL_OVERFLOW, "smul_overflow", BT_FN_BOOL_INT_INT_INTPTR, ATTR_NOTHROW_LEAF_LIST) -DEF_GCC_BUILTIN (BUILT_IN_SMULL_OVERFLOW, "smull_overflow", BT_FN_BOOL_LONG_LONG_LONGPTR, ATTR_NOTHROW_LEAF_LIST) -DEF_GCC_BUILTIN (BUILT_IN_SMULLL_OVERFLOW, "smulll_overflow", BT_FN_BOOL_LONGLONG_LONGLONG_LONGLONGPTR, ATTR_NOTHROW_LEAF_LIST) -DEF_GCC_BUILTIN (BUILT_IN_UADD_OVERFLOW, "uadd_overflow", BT_FN_BOOL_UINT_UINT_UINTPTR, ATTR_NOTHROW_LEAF_LIST) -DEF_GCC_BUILTIN (BUILT_IN_UADDL_OVERFLOW, "uaddl_overflow", BT_FN_BOOL_ULONG_ULONG_ULONGPTR, ATTR_NOTHROW_LEAF_LIST) -DEF_GCC_BUILTIN (BUILT_IN_UADDLL_OVERFLOW, "uaddll_overflow", BT_FN_BOOL_ULONGLONG_ULONGLONG_ULONGLONGPTR, ATTR_NOTHROW_LEAF_LIST) -DEF_GCC_BUILTIN (BUILT_IN_USUB_OVERFLOW, "usub_overflow", BT_FN_BOOL_UINT_UINT_UINTPTR, ATTR_NOTHROW_LEAF_LIST) -DEF_GCC_BUILTIN (BUILT_IN_USUBL_OVERFLOW, "usubl_overflow", BT_FN_BOOL_ULONG_ULONG_ULONGPTR, ATTR_NOTHROW_LEAF_LIST) -DEF_GCC_BUILTIN (BUILT_IN_USUBLL_OVERFLOW, "usubll_overflow", BT_FN_BOOL_ULONGLONG_ULONGLONG_ULONGLONGPTR, ATTR_NOTHROW_LEAF_LIST) -DEF_GCC_BUILTIN (BUILT_IN_UMUL_OVERFLOW, "umul_overflow", BT_FN_BOOL_UINT_UINT_UINTPTR, ATTR_NOTHROW_LEAF_LIST) -DEF_GCC_BUILTIN (BUILT_IN_UMULL_OVERFLOW, "umull_overflow", BT_FN_BOOL_ULONG_ULONG_ULONGPTR, ATTR_NOTHROW_LEAF_LIST) -DEF_GCC_BUILTIN (BUILT_IN_UMULLL_OVERFLOW, "umulll_overflow", BT_FN_BOOL_ULONGLONG_ULONGLONG_ULONGLONGPTR, ATTR_NOTHROW_LEAF_LIST) +/* For Clang compatibility: typed integer overflow checking builtins, last + argument is optional. */ +DEF_GCC_BUILTIN (BUILT_IN_SADD_OVERFLOW, "sadd_overflow", BT_FN_BOOL_INT_INT_VAR, ATTR_NOTHROW_LEAF_LIST) +DEF_GCC_BUILTIN (BUILT_IN_SADDL_OVERFLOW, "saddl_overflow", BT_FN_BOOL_LONG_LONG_VAR, ATTR_NOTHROW_LEAF_LIST) +DEF_GCC_BUILTIN (BUILT_IN_SADDLL_OVERFLOW, "saddll_overflow", BT_FN_BOOL_LONGLONG_LONGLONG_VAR, ATTR_NOTHROW_LEAF_LIST) +DEF_GCC_BUILTIN (BUILT_IN_SSUB_OVERFLOW, "ssub_overflow", BT_FN_BOOL_INT_INT_VAR, ATTR_NOTHROW_LEAF_LIST) +DEF_GCC_BUILTIN (BUILT_IN_SSUBL_OVERFLOW, "ssubl_overflow", BT_FN_BOOL_LONG_LONG_VAR, ATTR_NOTHROW_LEAF_LIST) +DEF_GCC_BUILTIN (BUILT_IN_SSUBLL_OVERFLOW, "ssubll_overflow", BT_FN_BOOL_LONGLONG_LONGLONG_VAR, ATTR_NOTHROW_LEAF_LIST) +DEF_GCC_BUILTIN (BUILT_IN_SMUL_OVERFLOW, "smul_overflow", BT_FN_BOOL_INT_INT_VAR, ATTR_NOTHROW_LEAF_LIST) +DEF_GCC_BUILTIN (BUILT_IN_SMULL_OVERFLOW, "smull_overflow", BT_FN_BOOL_LONG_LONG_VAR, ATTR_NOTHROW_LEAF_LIST) +DEF_GCC_BUILTIN (BUILT_IN_SMULLL_OVERFLOW, "smulll_overflow", BT_FN_BOOL_LONGLONG_LONGLONG_VAR, ATTR_NOTHROW_LEAF_LIST) +DEF_GCC_BUILTIN (BUILT_IN_UADD_OVERFLOW, "uadd_overflow", BT_FN_BOOL_UINT_UINT_VAR, ATTR_NOTHROW_LEAF_LIST) +DEF_GCC_BUILTIN (BUILT_IN_UADDL_OVERFLOW, "uaddl_overflow", BT_FN_BOOL_ULONG_ULONG_VAR, ATTR_NOTHROW_LEAF_LIST) +DEF_GCC_BUILTIN (BUILT_IN_UADDLL_OVERFLOW, "uaddll_overflow", BT_FN_BOOL_ULONGLONG_ULONGLONG_VAR, ATTR_NOTHROW_LEAF_LIST) +DEF_GCC_BUILTIN (BUILT_IN_USUB_OVERFLOW, "usub_overflow", BT_FN_BOOL_UINT_UINT_VAR, ATTR_NOTHROW_LEAF_LIST) +DEF_GCC_BUILTIN (BUILT_IN_USUBL_OVERFLOW, "usubl_overflow", BT_FN_BOOL_ULONG_ULONG_VAR, ATTR_NOTHROW_LEAF_LIST) +DEF_GCC_BUILTIN (BUILT_IN_USUBLL_OVERFLOW, "usubll_overflow", BT_FN_BOOL_ULONGLONG_ULONGLONG_VAR, ATTR_NOTHROW_LEAF_LIST) +DEF_GCC_BUILTIN (BUILT_IN_UMUL_OVERFLOW, "umul_overflow", BT_FN_BOOL_UINT_UINT_VAR, ATTR_NOTHROW_LEAF_LIST) +DEF_GCC_BUILTIN (BUILT_IN_UMULL_OVERFLOW, "umull_overflow", BT_FN_BOOL_ULONG_ULONG_VAR, ATTR_NOTHROW_LEAF_LIST) +DEF_GCC_BUILTIN (BUILT_IN_UMULLL_OVERFLOW, "umulll_overflow", BT_FN_BOOL_ULONGLONG_ULONGLONG_VAR, ATTR_NOTHROW_LEAF_LIST) /* Category: miscellaneous builtins. */ DEF_LIB_BUILTIN (BUILT_IN_ABORT, "abort", BT_FN_VOID, ATTR_TMPURE_NORETURN_NOTHROW_LEAF_LIST) diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c index 1edc0bc..87a9d99 100644 --- a/gcc/c-family/c-common.c +++ b/gcc/c-family/c-common.c @@ -9806,7 +9806,7 @@ builtin_function_validate_nargs (tree fndecl, int nargs, int required) if (nargs < required) { error_at (input_location, - "not enough arguments to function %qE", fndecl); + "too few arguments to function %qE", fndecl); return false; } else if (nargs > required) @@ -9828,6 +9828,11 @@ check_builtin_function_arguments (tree fndecl, int nargs, tree *args) || DECL_BUILT_IN_CLASS (fndecl) != BUILT_IN_NORMAL) return true; + /* Set to the number of required arguments when a built-in takes a fixed + number of arguments, otherwise negative non-zero when the built-in is + overloaded to accept different numbers of arguments. */ + int maxargs = -1; + switch (DECL_FUNCTION_CODE (fndecl)) { case BUILT_IN_ALLOCA_WITH_ALIGN: @@ -9939,7 +9944,39 @@ check_builtin_function_arguments (tree fndecl, int nargs, tree *args) case BUILT_IN_ADD_OVERFLOW: case BUILT_IN_SUB_OVERFLOW: case BUILT_IN_MUL_OVERFLOW: - if (builtin_function_validate_nargs (fndecl, nargs, 3)) + maxargs = 3; + /* Fall through. */ + + case BUILT_IN_SADD_OVERFLOW: + case BUILT_IN_SADDL_OVERFLOW: + case BUILT_IN_SADDLL_OVERFLOW: + + case BUILT_IN_SSUB_OVERFLOW: + case BUILT_IN_SSUBL_OVERFLOW: + case BUILT_IN_SSUBLL_OVERFLOW: + + case BUILT_IN_SMUL_OVERFLOW: + case BUILT_IN_SMULL_OVERFLOW: + case BUILT_IN_SMULLL_OVERFLOW: + + case BUILT_IN_UADD_OVERFLOW: + case BUILT_IN_UADDL_OVERFLOW: + case BUILT_IN_UADDLL_OVERFLOW: + + case BUILT_IN_USUB_OVERFLOW: + case BUILT_IN_USUBL_OVERFLOW: + case BUILT_IN_USUBLL_OVERFLOW: + + case BUILT_IN_UMUL_OVERFLOW: + case BUILT_IN_UMULL_OVERFLOW: + case BUILT_IN_UMULLL_OVERFLOW: + + /* The type-generic integer overflow built-ins take exactly three + arguments. The typed built-ins take 2 or 3 arguments. */ + if (maxargs < 1) + maxargs = 2 + (nargs == 3); + + if (builtin_function_validate_nargs (fndecl, nargs, maxargs)) { unsigned i; for (i = 0; i < 2; i++) @@ -9949,8 +9986,11 @@ check_builtin_function_arguments (tree fndecl, int nargs, tree *args) "integral type", i + 1, fndecl); return false; } - if (TREE_CODE (TREE_TYPE (args[2])) != POINTER_TYPE - || TREE_CODE (TREE_TYPE (TREE_TYPE (args[2]))) != INTEGER_TYPE) + + tree a3type = 2 < nargs ? TREE_TYPE (args[2]) : NULL_TREE; + if (a3type + && (TREE_CODE (a3type) != POINTER_TYPE + || TREE_CODE (TREE_TYPE (a3type)) != INTEGER_TYPE)) { error ("argument 3 in call to function %qE does not have " "pointer to integer type", fndecl); diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c index 41f0b5c..b93e0f0 100644 --- a/gcc/cp/constexpr.c +++ b/gcc/cp/constexpr.c @@ -1255,6 +1255,68 @@ cx_error_context (void) return r; } +/* Evaluate a call T to a GCC internal function when possible and return + the evaluated result or, under the control of CTX, give an error, set + NON_CONSTANT_P, and return the unevaluated call T otherwise. */ + +static tree +cxx_eval_internal_function (const constexpr_ctx *ctx, tree t, + bool lval, + bool *non_constant_p, bool *overflow_p) +{ + enum tree_code opcode = ERROR_MARK; + + switch (CALL_EXPR_IFN (t)) + { + case IFN_UBSAN_NULL: + case IFN_UBSAN_BOUNDS: + case IFN_UBSAN_VPTR: + return void_node; + + case IFN_ADD_OVERFLOW: + opcode = PLUS_EXPR; + break; + case IFN_SUB_OVERFLOW: + opcode = MINUS_EXPR; + break; + case IFN_MUL_OVERFLOW: + opcode = MULT_EXPR; + break; + + default: + if (!ctx->quiet) + error_at (EXPR_LOC_OR_LOC (t, input_location), + "call to internal function"); + *non_constant_p = true; + return t; + } + + tree arg0 = cxx_eval_constant_expression (ctx, CALL_EXPR_ARG (t, 0), lval, + non_constant_p, overflow_p); + tree arg1 = cxx_eval_constant_expression (ctx, CALL_EXPR_ARG (t, 1), lval, + non_constant_p, overflow_p); + + if (TREE_CODE (arg0) == INTEGER_CST && TREE_CODE (arg1) == INTEGER_CST) + { + if (tree result = size_binop_loc (EXPR_LOC_OR_LOC (t, input_location), + opcode, arg0, arg1)) + { + if (TREE_OVERFLOW (result)) + { + /* Reset TREE_OVERFLOW to avoid warnings for the overflow. */ + TREE_OVERFLOW (result) = 0; + + return build_complex (TREE_TYPE (t), result, integer_one_node); + } + + return build_complex (TREE_TYPE (t), result, integer_zero_node); + } + } + + *non_constant_p = true; + return t; +} + /* Subroutine of cxx_eval_constant_expression. Evaluate the call expression tree T in the context of OLD_CALL expression evaluation. */ @@ -1270,18 +1332,8 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t, bool depth_ok; if (fun == NULL_TREE) - switch (CALL_EXPR_IFN (t)) - { - case IFN_UBSAN_NULL: - case IFN_UBSAN_BOUNDS: - case IFN_UBSAN_VPTR: - return void_node; - default: - if (!ctx->quiet) - error_at (loc, "call to internal function"); - *non_constant_p = true; - return t; - } + return cxx_eval_internal_function (ctx, t, lval, + non_constant_p, overflow_p); if (TREE_CODE (fun) != FUNCTION_DECL) { diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c index 381c24f..6fe8a34 100644 --- a/gcc/cp/tree.c +++ b/gcc/cp/tree.c @@ -307,10 +307,36 @@ builtin_valid_in_constant_expr_p (const_tree decl) return false; switch (DECL_FUNCTION_CODE (decl)) { - case BUILT_IN_CONSTANT_P: - case BUILT_IN_ATOMIC_ALWAYS_LOCK_FREE: + /* The following built-ins are valid in constant expressions + when their arguments are. */ + case BUILT_IN_ADD_OVERFLOW: + case BUILT_IN_SADD_OVERFLOW: + case BUILT_IN_SADDL_OVERFLOW: + case BUILT_IN_SADDLL_OVERFLOW: + case BUILT_IN_UADD_OVERFLOW: + case BUILT_IN_UADDL_OVERFLOW: + case BUILT_IN_UADDLL_OVERFLOW: + + case BUILT_IN_SUB_OVERFLOW: + case BUILT_IN_SSUB_OVERFLOW: + case BUILT_IN_SSUBL_OVERFLOW: + case BUILT_IN_SSUBLL_OVERFLOW: + case BUILT_IN_USUB_OVERFLOW: + case BUILT_IN_USUBL_OVERFLOW: + case BUILT_IN_USUBLL_OVERFLOW: + + case BUILT_IN_MUL_OVERFLOW: + case BUILT_IN_SMUL_OVERFLOW: + case BUILT_IN_SMULL_OVERFLOW: + case BUILT_IN_SMULLL_OVERFLOW: + case BUILT_IN_UMUL_OVERFLOW: + case BUILT_IN_UMULL_OVERFLOW: + case BUILT_IN_UMULLL_OVERFLOW: + /* These have constant results even if their operands are non-constant. */ + case BUILT_IN_CONSTANT_P: + case BUILT_IN_ATOMIC_ALWAYS_LOCK_FREE: return true; default: return false; diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi index a5a8b23..c6b6769 100644 --- a/gcc/doc/extend.texi +++ b/gcc/doc/extend.texi @@ -9731,58 +9731,90 @@ compiler may also ignore this parameter. @section Built-in Functions to Perform Arithmetic with Overflow Checking The following built-in functions allow performing simple arithmetic operations -together with checking whether the operations overflowed. +together with checking whether the operations overflowed. The functions shown +with a default argument may be invoked without explicitly providing it. +The functions yield constant integer expressions when their arguments are +and can be used in contexts where constant integer expressions are required. @deftypefn {Built-in Function} bool __builtin_add_overflow (@var{type1} a, @var{type2} b, @var{type3} *res) -@deftypefnx {Built-in Function} bool __builtin_sadd_overflow (int a, int b, int *res) -@deftypefnx {Built-in Function} bool __builtin_saddl_overflow (long int a, long int b, long int *res) -@deftypefnx {Built-in Function} bool __builtin_saddll_overflow (long long int a, long long int b, long int *res) -@deftypefnx {Built-in Function} bool __builtin_uadd_overflow (unsigned int a, unsigned int b, unsigned int *res) -@deftypefnx {Built-in Function} bool __builtin_uaddl_overflow (unsigned long int a, unsigned long int b, unsigned long int *res) -@deftypefnx {Built-in Function} bool __builtin_uaddll_overflow (unsigned long long int a, unsigned long long int b, unsigned long int *res) - -These built-in functions promote the first two operands into infinite precision signed -type and perform addition on those promoted operands. The result is then -cast to the type the third pointer argument points to and stored there. -If the stored result is equal to the infinite precision result, the built-in -functions return false, otherwise they return true. As the addition is -performed in infinite signed precision, these built-in functions have fully defined -behavior for all argument values. - -The first built-in function allows arbitrary integral types for operands and -the result type must be pointer to some integer type, the rest of the built-in -functions have explicit integer types. - -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. +@deftypefnx {Built-in Function} bool __builtin_sadd_overflow (int a, int b, int *res = 0) +@deftypefnx {Built-in Function} bool __builtin_saddl_overflow (long int a, long int b, long int *res = 0) +@deftypefnx {Built-in Function} bool __builtin_saddll_overflow (long long int a, long long int b, long int *res = 0) +@deftypefnx {Built-in Function} bool __builtin_uadd_overflow (unsigned int a, unsigned int b, unsigned int *res = 0) +@deftypefnx {Built-in Function} bool __builtin_uaddl_overflow (unsigned long int a, unsigned long int b, unsigned long int *res = 0) +@deftypefnx {Built-in Function} bool __builtin_uaddll_overflow (unsigned long long int a, unsigned long long int b, unsigned long int *res = 0) + +These built-in functions promote the first two operands into infinite precision +signed type and perform addition on those promoted operands. The result is then +converted to the type the third pointer argument points to, and when the pointer +is not null, stored there. If the converted result is equal to the infinite +precision result, the built-in functions return @code{false}, otherwise they +return @code{true} to indicate that an overflow has been detected. Because +the addition is performed in infinite precision, these built-in functions +have fully defined behavior for all argument values and integer types. + +The first type-generic built-in function allows arbitrary integer types as +the first two arguments and requires that a pointer to some possibly distinct +integer type be passed to it as the third argument. The pointed-to type is +the type used to determine the overflow. As a result, this built-in function +can be used to detect overflow in any arbitrary integer type, including +@code{char} and @code{short}. The remaining built-in functions take +arguments of explicit integer types and make it possible to determine +overflow only in the ranges of those types. Since the only provided forms +of these latter built-in functions are for the signed and unsigned variants +of types @code{int}, @code{long}, and @code{long long}, they cannot be used +to determine overflow in other integer types. + +To enable the efficient integer overflow detection at translation-time, +in C and C++ 11 and later programs (but not in C++ 98), the built-ins may +be invoked in constant integer expression contexts with a null pointer +cast to a pointer to the appropriate integer type as the third argument. +All but the type-generic built-ins may also be invoked without the third +argument. For example, the following macro can be used to portably check, +at compile-time, whether or not adding two constant integers will overflow, +and perform the addition only when it is known to be safe and not to trigger +a @option{-Woverflow} warning. + +@smallexample +#define INT_ADD_OVERFLOW(a, b) \ + __builtin_add_overflow (a, b, (__typeof__ ((a) + (b)) *) 0) + +enum @{ + A = INT_MAX, B = 3, + C = INT_ADD_OVERFLOW (A, B) ? 0 : A + B +@}; +@end smallexample + +For invocations of the built-in functions evaluated at run-time the compiler +will attempt to make use of efficient hardware instructions such as conditional +jump on overflow after addition, conditional jump on carry, etc. @end deftypefn @deftypefn {Built-in Function} bool __builtin_sub_overflow (@var{type1} a, @var{type2} b, @var{type3} *res) -@deftypefnx {Built-in Function} bool __builtin_ssub_overflow (int a, int b, int *res) -@deftypefnx {Built-in Function} bool __builtin_ssubl_overflow (long int a, long int b, long int *res) -@deftypefnx {Built-in Function} bool __builtin_ssubll_overflow (long long int a, long long int b, long int *res) -@deftypefnx {Built-in Function} bool __builtin_usub_overflow (unsigned int a, unsigned int b, unsigned int *res) -@deftypefnx {Built-in Function} bool __builtin_usubl_overflow (unsigned long int a, unsigned long int b, unsigned long int *res) -@deftypefnx {Built-in Function} bool __builtin_usubll_overflow (unsigned long long int a, unsigned long long int b, unsigned long int *res) +@deftypefnx {Built-in Function} bool __builtin_ssub_overflow (int a, int b, int *res = 0) +@deftypefnx {Built-in Function} bool __builtin_ssubl_overflow (long int a, long int b, long int *res = 0) +@deftypefnx {Built-in Function} bool __builtin_ssubll_overflow (long long int a, long long int b, long int *res = 0) +@deftypefnx {Built-in Function} bool __builtin_usub_overflow (unsigned int a, unsigned int b, unsigned int *res = 0) +@deftypefnx {Built-in Function} bool __builtin_usubl_overflow (unsigned long int a, unsigned long int b, unsigned long int *res = 0) +@deftypefnx {Built-in Function} bool __builtin_usubll_overflow (unsigned long long int a, unsigned long long int b, unsigned long int *res = 0) -These built-in functions are similar to the add overflow checking built-in -functions above, except they perform subtraction, subtract the second argument -from the first one, instead of addition. +These built-in functions are analogous to the add overflow checking built-in +functions above, except they subtract the second argument from the first one +rather than adding it to it. @end deftypefn @deftypefn {Built-in Function} bool __builtin_mul_overflow (@var{type1} a, @var{type2} b, @var{type3} *res) -@deftypefnx {Built-in Function} bool __builtin_smul_overflow (int a, int b, int *res) -@deftypefnx {Built-in Function} bool __builtin_smull_overflow (long int a, long int b, long int *res) -@deftypefnx {Built-in Function} bool __builtin_smulll_overflow (long long int a, long long int b, long int *res) -@deftypefnx {Built-in Function} bool __builtin_umul_overflow (unsigned int a, unsigned int b, unsigned int *res) -@deftypefnx {Built-in Function} bool __builtin_umull_overflow (unsigned long int a, unsigned long int b, unsigned long int *res) -@deftypefnx {Built-in Function} bool __builtin_umulll_overflow (unsigned long long int a, unsigned long long int b, unsigned long int *res) - -These built-in functions are similar to the add overflow checking built-in -functions above, except they perform multiplication, instead of addition. +@deftypefnx {Built-in Function} bool __builtin_smul_overflow (int a, int b, int *res = 0) +@deftypefnx {Built-in Function} bool __builtin_smull_overflow (long int a, long int b, long int *res = 0) +@deftypefnx {Built-in Function} bool __builtin_smulll_overflow (long long int a, long long int b, long int *res = 0) +@deftypefnx {Built-in Function} bool __builtin_umul_overflow (unsigned int a, unsigned int b, unsigned int *res = 0) +@deftypefnx {Built-in Function} bool __builtin_umull_overflow (unsigned long int a, unsigned long int b, unsigned long int *res = 0) +@deftypefnx {Built-in Function} bool __builtin_umulll_overflow (unsigned long long int a, unsigned long long int b, unsigned long int *res = 0) + +These built-in functions are analogous to the add overflow checking built-in +functions above, except they perform multiplication rather than addition. @end deftypefn diff --git a/gcc/testsuite/c-c++-common/builtin-arith-overflow-1.c b/gcc/testsuite/c-c++-common/builtin-arith-overflow-1.c index 69b5083..7d09a95 100644 --- a/gcc/testsuite/c-c++-common/builtin-arith-overflow-1.c +++ b/gcc/testsuite/c-c++-common/builtin-arith-overflow-1.c @@ -1,23 +1,133 @@ /* { dg-do compile } */ +/* Verify that calls with fewer or more than 3 arguments to the generic + __builtin_op_overflow functions are rejected. */ + +int +generic_0 (void) +{ + int x = __builtin_add_overflow (); /* { dg-error "too few arguments to function" } */ + x += __builtin_sub_overflow (); /* { dg-error "too few arguments to function" } */ + x += __builtin_mul_overflow (); /* { dg-error "too few arguments to function" } */ + return x; +} + int -f1 (void) +generic_1 (int a) { - int x = __builtin_add_overflow (); /* { dg-error "not enough arguments to function" } */ - x += __builtin_sub_overflow (); /* { dg-error "not enough arguments to function" } */ - x += __builtin_mul_overflow (); /* { dg-error "not enough arguments to function" } */ + int x = __builtin_add_overflow (a); /* { dg-error "too few arguments to function" } */ + x += __builtin_sub_overflow (a); /* { dg-error "too few arguments to function" } */ + x += __builtin_mul_overflow (a); /* { dg-error "too few arguments to function" } */ + + /* Literal argument. */ + x += __builtin_add_overflow (1); /* { dg-error "too few arguments to function" } */ + x += __builtin_sub_overflow (2); /* { dg-error "too few arguments to function" } */ + x += __builtin_mul_overflow (3); /* { dg-error "too few arguments to function" } */ return x; } int -f2 (int a, int b, int *c, int d) +generic_2 (int a, int b) +{ + int x = __builtin_add_overflow (a, b);/* { dg-error "too few arguments to function" } */ + x += __builtin_sub_overflow (a, b); /* { dg-error "too few arguments to function" } */ + x += __builtin_mul_overflow (a, b); /* { dg-error "too few arguments to function" } */ + x += __builtin_add_overflow (a, 1); /* { dg-error "too few arguments to function" } */ + x += __builtin_sub_overflow (a, 2); /* { dg-error "too few arguments to function" } */ + x += __builtin_mul_overflow (a, 3); /* { dg-error "too few arguments to function" } */ + x += __builtin_add_overflow (4, b); /* { dg-error "too few arguments to function" } */ + x += __builtin_sub_overflow (5, b); /* { dg-error "too few arguments to function" } */ + x += __builtin_mul_overflow (6, b); /* { dg-error "too few arguments to function" } */ + return x; +} + +/* Verify that calls with the correct number of arguments to the generic + __builtin_op_overflow functions are accepted. */ + +int +generic_3 (int a, int b, int c) +{ + int x = __builtin_add_overflow (a, b, &c); + x += __builtin_sub_overflow (a, b, &c); + x += __builtin_mul_overflow (a, b, &c); + x += __builtin_add_overflow (a, 1, &c); + x += __builtin_sub_overflow (a, 2, &c); + x += __builtin_mul_overflow (a, 3, &c); + x += __builtin_add_overflow (4, b, &c); + x += __builtin_sub_overflow (5, b, &c); + x += __builtin_mul_overflow (6, b, &c); + x += __builtin_add_overflow (7, 8, &c); + x += __builtin_sub_overflow (9, 10, &c); + x += __builtin_mul_overflow (11, 12, &c); + return x; +} + +int +generic_4 (int a, int b, int *c, int d) { int x = __builtin_add_overflow (a, b, c, d); /* { dg-error "too many arguments to function" } */ - x += __builtin_sub_overflow (a, b, c, d, d, d); /* { dg-error "too many arguments to function" } */ + x += __builtin_sub_overflow (a, b, c, d); /* { dg-error "too many arguments to function" } */ x += __builtin_mul_overflow (a, b, c, d); /* { dg-error "too many arguments to function" } */ return x; } +/* Verify that calls with the last argument of the wrong type to + the generic __builtin_op_overflow functions are rejected. */ + +int +generic_wrong_type (int a, int b) +{ + void *p = 0; + double d = 0; + int x = __builtin_add_overflow (a, b, p); /* { dg-error "does not have pointer to integer type" } */ + x += __builtin_sub_overflow (a, b, &p); /* { dg-error "does not have pointer to integer type" } */ + x += __builtin_mul_overflow (a, b, &d); /* { dg-error "does not have pointer to integer type" } */ + + /* Also verify literal arguments. */ + x += __builtin_add_overflow (1, 1, p); /* { dg-error "does not have pointer to integer type" } */ + x += __builtin_sub_overflow (1, 1, &p); /* { dg-error "does not have pointer to integer type" } */ + x += __builtin_mul_overflow (1, 1, &d); /* { dg-error "does not have pointer to integer type" } */ + return x; +} + +/* Verify that calls with fewer than 2 or more than 3 arguments to + the typed __builtin_op_overflow functions are rejected. */ +int +typed_0 (void) +{ + int x = __builtin_add_overflow (); /* { dg-error "too few arguments to function" } */ + x += __builtin_sub_overflow (); /* { dg-error "too few arguments to function" } */ + x += __builtin_mul_overflow (); /* { dg-error "too few arguments to function" } */ + return x; +} + +int +typed_1 (int a) +{ + int x = __builtin_sadd_overflow (a); /* { dg-error "too few arguments to function" } */ + x += __builtin_ssub_overflow (a); /* { dg-error "too few arguments to function" } */ + x += __builtin_smul_overflow (a); /* { dg-error "too few arguments to function" } */ + return x; +} + +int +typed_2 (int a, int b) +{ + int x = __builtin_sadd_overflow (a, b); + x += __builtin_ssub_overflow (a, b); + x += __builtin_smul_overflow (a, b); + return x; +} + +int +typed_4 (int a, int b, int *c, int d) +{ + int x = __builtin_sadd_overflow (a, b, c, d); /* { dg-error "too many arguments to function" } */ + x += __builtin_ssub_overflow (a, b, c, d); /* { dg-error "too many arguments to function" } */ + x += __builtin_smul_overflow (a, b, c, d); /* { dg-error "too many arguments to function" } */ + return x; +} + enum E { e0 = 0, e1 = 1 }; #ifndef __cplusplus diff --git a/gcc/testsuite/c-c++-common/builtin-arith-overflow-2.c b/gcc/testsuite/c-c++-common/builtin-arith-overflow-2.c new file mode 100644 index 0000000..dbd825f --- /dev/null +++ b/gcc/testsuite/c-c++-common/builtin-arith-overflow-2.c @@ -0,0 +1,469 @@ +/* PR c/68120 - can't easily deal with integer overflow at compile time */ +/* { dg-do run } */ +/* { dg-additional-options "-Wno-long-long" } */ + +#define SCHAR_MAX __SCHAR_MAX__ +#define SHRT_MAX __SHRT_MAX__ +#define INT_MAX __INT_MAX__ +#define LONG_MAX __LONG_MAX__ +#define LLONG_MAX __LONG_LONG_MAX__ + +#define SCHAR_MIN (-__SCHAR_MAX__ - 1) +#define SHRT_MIN (-__SHRT_MAX__ - 1) +#define INT_MIN (-__INT_MAX__ - 1) +#define LONG_MIN (-__LONG_MAX__ - 1) +#define LLONG_MIN (-__LONG_LONG_MAX__ - 1) + +#define UCHAR_MAX (SCHAR_MAX * 2U + 1) +#define USHRT_MAX (SHRT_MAX * 2U + 1) +#define UINT_MAX (INT_MAX * 2U + 1) +#define ULONG_MAX (LONG_MAX * 2LU + 1) +#define ULLONG_MAX (LLONG_MAX * 2LLU + 1) + +#define USCHAR_MIN (-__USCHAR_MAX__ - 1) +#define USHRT_MIN (-__USHRT_MAX__ - 1) +#define UINT_MIN (-__UINT_MAX__ - 1) +#define ULONG_MIN (-__ULONG_MAX__ - 1) +#define ULLONG_MIN (-__ULONG_LONG_MAX__ - 1) + +/* Number of failed runtime assertions. */ +int nfails; + +void __attribute__ ((noclone, noinline)) +runtime_assert (int expr, int line) +{ + if (!expr) + { + __builtin_printf ("line %i: assertion failed\n", line); + ++nfails; + } +} + +/* Helper macros for run-time testing. */ +#define add(x, y) ((a) + (b)) +#define sub(x, y) ((a) - (b)) +#define mul(x, y) ((a) * (b)) + +int main (void) +{ + +#if __cplusplus >= 201103L +# define StaticAssert(expr) static_assert ((expr), #expr) +#elif __STDC_VERSION__ >= 201112L +# define StaticAssert(expr) _Static_assert ((expr), #expr) +#else + /* The following pragma has no effect due to bug 70888 - #pragma + diagnostic ignored -Wlong-long ineffective with __LONG_LONG_MAX__ + in c++98 mode. */ +# pragma GCC diagnostic ignored "-Wlong-long" +# pragma GCC diagnostic ignored "-Wunused-local-typedefs" + +# define CONCAT(a, b) a ## b +# define CAT(a, b) CONCAT (a, b) +# define StaticAssert(expr) \ + typedef int CAT (StaticAssert_, __LINE__) [1 - 2 * !(expr)] +#endif + + /* Make extra effort to prevent constant folding seeing the constant + values of the arguments and optimizing the run-time test into + a constant. */ +#define RuntimeAssert(op, T, x, y, vflow) \ + do { \ + volatile T a = (x), b = (y), c = 0; \ + volatile int vf = __builtin_ ## op ## _overflow (a, b, &c); \ + runtime_assert ((vf == vflow), __LINE__); \ + if (vf == 0) \ + runtime_assert (op (a, b) == c, __LINE__); \ + } while (0) + + /* Verify that each call to the type-generic __builtin_op_overflow(x, y) + yields a constant expression equal to z indicating whether or not + the constant expression (x op y) overflows when evaluated in type T. */ +#if !__cplusplus || __cplusplus >= 201103L + /* Perform both a run-time test followed by a compile-time test. */ +# define G_TEST(op, T, x, y, vflow) \ + RuntimeAssert(op, T, x, y, vflow); \ + StaticAssert ((vflow) == __builtin_ ## op ## _overflow ((x), (y), (T*)0)) +#else + /* C++ 98 doesn't permit casts to pointer types to appear in constant + expressions. Only perform the run-time test. */ +# define G_TEST(op, T, x, y, vflow) \ + RuntimeAssert(op, T, x, y, vflow) +#endif + + /* Addition. */ + /* G_TEST (add, signed char, 0, 0, 0); */ + /* G_TEST (add, signed char, 0, SCHAR_MAX, 0); */ + G_TEST (add, signed char, 1, SCHAR_MAX, 1); + G_TEST (add, signed char, SCHAR_MAX, SCHAR_MAX, 1); + G_TEST (add, signed char, 0, SCHAR_MIN, 0); + G_TEST (add, signed char, -1, SCHAR_MIN, 1); + + G_TEST (add, short, 0, 0, 0); + G_TEST (add, short, 0, SHRT_MAX, 0); + G_TEST (add, short, 1, SHRT_MAX, 1); + G_TEST (add, short, SHRT_MAX, SHRT_MAX, 1); + G_TEST (add, short, 0, SHRT_MIN, 0); + G_TEST (add, short, -1, SHRT_MIN, 1); + G_TEST (add, short, SHRT_MIN, SHRT_MIN, 1); + + G_TEST (add, int, 0, 0, 0); + G_TEST (add, int, 0, INT_MAX, 0); + G_TEST (add, int, 1, INT_MAX, 1); + G_TEST (add, int, INT_MAX, INT_MAX, 1); + G_TEST (add, int, 0, INT_MIN, 0); + G_TEST (add, int, -1, INT_MIN, 1); + G_TEST (add, int, INT_MIN, INT_MIN, 1); + + G_TEST (add, long, 0, 0, 0); + G_TEST (add, long, 0, LONG_MAX, 0); + G_TEST (add, long, 1, LONG_MAX, 1); + G_TEST (add, long, LONG_MAX, LONG_MAX, 1); + G_TEST (add, long, 0, LONG_MIN, 0); + G_TEST (add, long, -1, LONG_MIN, 1); + G_TEST (add, long, LONG_MIN, LONG_MIN, 1); + + G_TEST (add, long long, 0, 0, 0); + G_TEST (add, long long, 0, LLONG_MAX, 0); + G_TEST (add, long long, 1, LLONG_MAX, 1); + G_TEST (add, long long, LLONG_MAX, LLONG_MAX, 1); + G_TEST (add, long long, 0, LLONG_MIN, 0); + G_TEST (add, long long, -1, LLONG_MIN, 1); + G_TEST (add, long long, LLONG_MIN, LLONG_MIN, 1); + + /* Subtraction */ + G_TEST (sub, unsigned char, 0, 0, 0); + G_TEST (sub, unsigned char, 0, UCHAR_MAX, 1); + G_TEST (sub, unsigned char, 1, UCHAR_MAX, 1); + + G_TEST (sub, unsigned char, UCHAR_MAX, UCHAR_MAX, 0); + G_TEST (sub, unsigned short, 0, 0, 0); + G_TEST (sub, unsigned short, 0, USHRT_MAX, 1); + G_TEST (sub, unsigned short, 1, USHRT_MAX, 1); + G_TEST (sub, unsigned short, USHRT_MAX, USHRT_MAX, 0); + + G_TEST (sub, unsigned, 0, 0, 0); + G_TEST (sub, unsigned, 0, UINT_MAX, 1); + G_TEST (sub, unsigned, 1, UINT_MAX, 1); + G_TEST (sub, unsigned, UINT_MAX, UINT_MAX, 0); + + G_TEST (sub, unsigned long, 0, 0, 0); + G_TEST (sub, unsigned long, 0, ULONG_MAX, 1); + G_TEST (sub, unsigned long, 1, ULONG_MAX, 1); + G_TEST (sub, unsigned long, ULONG_MAX, ULONG_MAX, 0); + + G_TEST (sub, unsigned long long, 0, 0, 0); + G_TEST (sub, unsigned long long, 0, ULLONG_MAX, 1); + G_TEST (sub, unsigned long long, 1, ULLONG_MAX, 1); + G_TEST (sub, unsigned long long, ULLONG_MAX, ULLONG_MAX, 0); + + G_TEST (sub, signed char, 0, 0, 0); + G_TEST (sub, signed char, 0, SCHAR_MAX, 0); + G_TEST (sub, signed char, 1, SCHAR_MAX, 0); + G_TEST (sub, signed char, SCHAR_MAX, SCHAR_MAX, 0); + G_TEST (sub, signed char, SCHAR_MIN, 1, 1); + G_TEST (sub, signed char, 0, SCHAR_MIN, 1); + G_TEST (sub, signed char, -1, SCHAR_MIN, 0); + + G_TEST (sub, short, 0, 0, 0); + G_TEST (sub, short, 0, SHRT_MAX, 0); + G_TEST (sub, short, 1, SHRT_MAX, 0); + G_TEST (sub, short, SHRT_MAX, SHRT_MAX, 0); + G_TEST (sub, short, 0, SHRT_MIN, 1); + G_TEST (sub, short, -1, SHRT_MIN, 0); + G_TEST (sub, short, SHRT_MIN, SHRT_MIN, 0); + + G_TEST (sub, int, 0, 0, 0); + G_TEST (sub, int, 0, INT_MAX, 0); + G_TEST (sub, int, 1, INT_MAX, 0); + G_TEST (sub, int, INT_MAX, INT_MAX, 0); + G_TEST (sub, int, 0, INT_MIN, 1); + G_TEST (sub, int, -1, INT_MIN, 0); + G_TEST (sub, int, INT_MIN, INT_MIN, 0); + + G_TEST (sub, long, 0, 0, 0); + G_TEST (sub, long, 0, LONG_MAX, 0); + G_TEST (sub, long, 1, LONG_MAX, 0); + G_TEST (sub, long, LONG_MAX, LONG_MAX, 0); + G_TEST (sub, long, 0, LONG_MIN, 1); + G_TEST (sub, long, -1, LONG_MIN, 0); + G_TEST (sub, long, LONG_MIN, LONG_MIN, 0); + + G_TEST (sub, long long, 0, 0, 0); + G_TEST (sub, long long, 0, LLONG_MAX, 0); + G_TEST (sub, long long, 1, LLONG_MAX, 0); + G_TEST (sub, long long, LLONG_MAX, LLONG_MAX, 0); + G_TEST (sub, long long, 0, LLONG_MIN, 1); + G_TEST (sub, long long, -1, LLONG_MIN, 0); + G_TEST (sub, long long, LLONG_MIN, LLONG_MIN, 0); + + G_TEST (sub, unsigned char, 0, 0, 0); + G_TEST (sub, unsigned char, 0, UCHAR_MAX, 1); + G_TEST (sub, unsigned char, 1, UCHAR_MAX, 1); + G_TEST (sub, unsigned char, UCHAR_MAX, UCHAR_MAX, 0); + + G_TEST (sub, unsigned short, 0, 0, 0); + G_TEST (sub, unsigned short, 0, USHRT_MAX, 1); + G_TEST (sub, unsigned short, 1, USHRT_MAX, 1); + G_TEST (sub, unsigned short, USHRT_MAX, USHRT_MAX, 0); + + G_TEST (sub, unsigned, 0, 0, 0); + G_TEST (sub, unsigned, 0, UINT_MAX, 1); + G_TEST (sub, unsigned, 1, UINT_MAX, 1); + G_TEST (sub, unsigned, UINT_MAX, UINT_MAX, 0); + + G_TEST (sub, unsigned long, 0, 0, 0); + G_TEST (sub, unsigned long, 0, ULONG_MAX, 1); + G_TEST (sub, unsigned long, 1, ULONG_MAX, 1); + G_TEST (sub, unsigned long, ULONG_MAX, ULONG_MAX, 0); + + G_TEST (sub, unsigned long long, 0, 0, 0); + G_TEST (sub, unsigned long long, 0, ULLONG_MAX, 1); + G_TEST (sub, unsigned long long, 1, ULLONG_MAX, 1); + G_TEST (sub, unsigned long long, ULLONG_MAX, ULLONG_MAX, 0); + + /* Multiplication. */ + G_TEST (mul, unsigned char, 0, 0, 0); + G_TEST (mul, unsigned char, 0, UCHAR_MAX, 0); + G_TEST (mul, unsigned char, 1, UCHAR_MAX, 0); + G_TEST (mul, unsigned char, 2, UCHAR_MAX, 1); + G_TEST (mul, unsigned char, UCHAR_MAX, UCHAR_MAX, 1); + + G_TEST (mul, unsigned short, 0, 0, 0); + G_TEST (mul, unsigned short, 0, USHRT_MAX, 0); + G_TEST (mul, unsigned short, 1, USHRT_MAX, 0); + G_TEST (mul, unsigned short, USHRT_MAX, 2, 1); + G_TEST (mul, unsigned short, USHRT_MAX, USHRT_MAX, 1); + + G_TEST (mul, unsigned, 0, 0, 0); + G_TEST (mul, unsigned, 0, UINT_MAX, 0); + G_TEST (mul, unsigned, 1, UINT_MAX, 0); + G_TEST (mul, unsigned, 2, UINT_MAX, 1); + G_TEST (mul, unsigned, UINT_MAX, UINT_MAX, 1); + + G_TEST (mul, unsigned long, 0, 0, 0); + G_TEST (mul, unsigned long, 0, ULONG_MAX, 0); + G_TEST (mul, unsigned long, 1, ULONG_MAX, 0); + G_TEST (mul, unsigned long, 2, ULONG_MAX, 1); + G_TEST (mul, unsigned long, ULONG_MAX, ULONG_MAX, 1); + + G_TEST (mul, unsigned long long, 0, 0, 0); + G_TEST (mul, unsigned long long, 0, ULLONG_MAX, 0); + G_TEST (mul, unsigned long long, 1, ULLONG_MAX, 0); + G_TEST (mul, unsigned long long, 2, ULLONG_MAX, 1); + G_TEST (mul, unsigned long long, ULLONG_MAX, ULLONG_MAX, 1); + + G_TEST (mul, signed char, 0, 0, 0); + G_TEST (mul, signed char, 0, SCHAR_MAX, 0); + G_TEST (mul, signed char, 1, SCHAR_MAX, 0); + G_TEST (mul, signed char, SCHAR_MAX, SCHAR_MAX, 1); + G_TEST (mul, signed char, SCHAR_MIN, 1, 0); + G_TEST (mul, signed char, 0, SCHAR_MIN, 0); + G_TEST (mul, signed char, -1, SCHAR_MIN, 1); + + G_TEST (mul, short, 0, 0, 0); + G_TEST (mul, short, 0, SHRT_MAX, 0); + G_TEST (mul, short, 1, SHRT_MAX, 0); + G_TEST (mul, short, SHRT_MAX, SHRT_MAX, 1); + G_TEST (mul, short, 0, SHRT_MIN, 0); + G_TEST (mul, short, -1, SHRT_MIN, 1); + G_TEST (mul, short, SHRT_MIN, SHRT_MIN, 1); + + G_TEST (mul, int, 0, 0, 0); + G_TEST (mul, int, 0, INT_MAX, 0); + G_TEST (mul, int, 1, INT_MAX, 0); + G_TEST (mul, int, INT_MAX, INT_MAX, 1); + G_TEST (mul, int, 0, INT_MIN, 0); + G_TEST (mul, int, -1, INT_MIN, 1); + G_TEST (mul, int, INT_MIN, INT_MIN, 1); + + G_TEST (mul, long, 0, 0, 0); + G_TEST (mul, long, 0, LONG_MAX, 0); + G_TEST (mul, long, 1, LONG_MAX, 0); + G_TEST (mul, long, LONG_MAX, LONG_MAX, 1); + G_TEST (mul, long, 0, LONG_MIN, 0); + G_TEST (mul, long, -1, LONG_MIN, 1); + G_TEST (mul, long, LONG_MIN, LONG_MIN, 1); + + G_TEST (mul, long long, 0, 0, 0); + G_TEST (mul, long long, 0, LLONG_MAX, 0); + G_TEST (mul, long long, 1, LLONG_MAX, 0); + G_TEST (mul, long long, LLONG_MAX, LLONG_MAX, 1); + G_TEST (mul, long long, 0, LLONG_MIN, 0); + G_TEST (mul, long long, -1, LLONG_MIN, 1); + G_TEST (mul, long long, LLONG_MIN, LLONG_MIN, 1); + + G_TEST (mul, unsigned char, 0, 0, 0); + G_TEST (mul, unsigned char, 0, UCHAR_MAX, 0); + G_TEST (mul, unsigned char, 1, UCHAR_MAX, 0); + G_TEST (mul, unsigned char, UCHAR_MAX, UCHAR_MAX, 1); + + G_TEST (mul, unsigned short, 0, 0, 0); + G_TEST (mul, unsigned short, 0, USHRT_MAX, 0); + G_TEST (mul, unsigned short, 1, USHRT_MAX, 0); + G_TEST (mul, unsigned short, USHRT_MAX, USHRT_MAX, 1); + + G_TEST (mul, unsigned, 0, 0, 0); + G_TEST (mul, unsigned, 0, UINT_MAX, 0); + G_TEST (mul, unsigned, 1, UINT_MAX, 0); + G_TEST (mul, unsigned, UINT_MAX, UINT_MAX, 1); + + G_TEST (mul, unsigned long, 0, 0, 0); + G_TEST (mul, unsigned long, 0, ULONG_MAX, 0); + G_TEST (mul, unsigned long, 1, ULONG_MAX, 0); + G_TEST (mul, unsigned long, ULONG_MAX, ULONG_MAX, 1); + + G_TEST (mul, unsigned long long, 0, 0, 0); + G_TEST (mul, unsigned long long, 0, ULLONG_MAX, 0); + G_TEST (mul, unsigned long long, 1, ULLONG_MAX, 0); + G_TEST (mul, unsigned long long, ULLONG_MAX, ULLONG_MAX, 1); + + /* Verify that each call to the type-specific __builtin_op_overflow(x, y) + yields a constant expression equal to z indicating whether or not + the constant expression (x op y) overflows. The type-specific + overloads detect overflow after arithmetic promotions and so unlike + the type-generic overloads cannot detect overflow in char or short + types. */ +#define T_TEST(op, T, x, y, vflow) \ + StaticAssert ((vflow) == __builtin_ ## op ## _overflow ((T)x, (T)y)) + + /* 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, 0, 0, 0); + T_TEST (saddl, long, 0, LONG_MAX, 0); + T_TEST (saddl, long, 1, LONG_MAX, 1); + T_TEST (saddl, long, LONG_MAX, LONG_MAX, 1); + T_TEST (saddl, long, 0, LONG_MIN, 0); + T_TEST (saddl, long, -1, LONG_MIN, 1); + T_TEST (saddl, long, LONG_MIN, LONG_MIN, 1); + + T_TEST (saddll, long long, 0, 0, 0); + T_TEST (saddll, long long, 0, LLONG_MAX, 0); + T_TEST (saddll, long long, 1, LLONG_MAX, 1); + T_TEST (saddll, long long, LLONG_MAX, LLONG_MAX, 1); + T_TEST (saddll, long long, 0, LLONG_MIN, 0); + T_TEST (saddll, long long, -1, LLONG_MIN, 1); + T_TEST (saddll, long long, LLONG_MIN, LLONG_MIN, 1); + + /* Unsigned int addition. */ + T_TEST (uadd, unsigned char, 0, 0, 0); + T_TEST (uadd, unsigned char, 0, UCHAR_MAX, 0); + T_TEST (uadd, unsigned char, 1, UCHAR_MAX, 0); + T_TEST (uadd, unsigned char, UCHAR_MAX, UCHAR_MAX, 0); + + T_TEST (uadd, unsigned short, 0, 0, 0); + T_TEST (uadd, unsigned short, 0, USHRT_MAX, 0); + T_TEST (uadd, unsigned short, 1, USHRT_MAX, 0); + T_TEST (uadd, unsigned short, USHRT_MAX, USHRT_MAX, 0); + + T_TEST (uadd, unsigned, 0, 0, 0); + T_TEST (uadd, unsigned, 0, UINT_MAX, 0); + T_TEST (uadd, unsigned, 1, UINT_MAX, 1); + T_TEST (uadd, unsigned, UINT_MAX, UINT_MAX, 1); + + /* Unsigned long addition. */ + T_TEST (uaddl, unsigned long, 0, 0, 0); + T_TEST (uaddl, unsigned long, 0, ULONG_MAX, 0); + T_TEST (uaddl, unsigned long, 1, ULONG_MAX, 1); + T_TEST (uaddl, unsigned long, ULONG_MAX, ULONG_MAX, 1); + + T_TEST (uaddl, unsigned long long, 0, 0, 0); + T_TEST (uaddl, unsigned long long, 0, ULLONG_MAX, 0); + T_TEST (uaddl, unsigned long long, 1, ULLONG_MAX, 1); + T_TEST (uaddl, 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, 0, 0, 0); + T_TEST (ssubl, long, 0, LONG_MAX, 0); + T_TEST (ssubl, long, 1, LONG_MAX, 0); + T_TEST (ssubl, long, LONG_MAX, LONG_MAX, 0); + T_TEST (ssubl, long, 0, LONG_MIN, 1); + T_TEST (ssubl, long, -1, LONG_MIN, 0); + T_TEST (ssubl, long, LONG_MIN, LONG_MIN, 0); + + /* Signed long long subtraction. */ + T_TEST (ssubll, long long, 0, 0, 0); + T_TEST (ssubll, long long, 0, LLONG_MAX, 0); + T_TEST (ssubll, long long, 1, LLONG_MAX, 0); + T_TEST (ssubll, long long, LLONG_MAX, LLONG_MAX, 0); + T_TEST (ssubll, long long, 0, LLONG_MIN, 1); + T_TEST (ssubll, long long, -1, LLONG_MIN, 0); + T_TEST (ssubll, long long, LLONG_MIN, LLONG_MIN, 0); + + /* Unsigned int subtraction. */ + T_TEST (usub, unsigned char, 0, 0, 0); + T_TEST (usub, unsigned char, 0, UCHAR_MAX, 1); + T_TEST (usub, unsigned char, 1, UCHAR_MAX, 1); + T_TEST (usub, unsigned char, UCHAR_MAX, UCHAR_MAX, 0); + + T_TEST (usub, unsigned short, 0, 0, 0); + T_TEST (usub, unsigned short, 0, USHRT_MAX, 1); + T_TEST (usub, unsigned short, 1, USHRT_MAX, 1); + T_TEST (usub, unsigned short, USHRT_MAX, USHRT_MAX, 0); + + T_TEST (usub, unsigned, 0, 0, 0); + T_TEST (usub, unsigned, 0, UINT_MAX, 1); + T_TEST (usub, unsigned, 1, UINT_MAX, 1); + T_TEST (usub, unsigned, UINT_MAX, UINT_MAX, 0); + + /* Unsigned long subtraction. */ + T_TEST (usubl, unsigned long, 0, 0, 0); + T_TEST (usubl, unsigned long, 0, ULONG_MAX, 1); + T_TEST (usubl, unsigned long, 1, ULONG_MAX, 1); + T_TEST (usubl, unsigned long, ULONG_MAX, ULONG_MAX, 0); + + /* Unsigned long long subtraction. */ + T_TEST (usubl, unsigned long long, 0, 0, 0); + T_TEST (usubl, unsigned long long, 0, ULLONG_MAX, 1); + T_TEST (usubl, unsigned long long, 1, ULLONG_MAX, 1); + T_TEST (usubl, unsigned long long, ULLONG_MAX, ULLONG_MAX, 0); + + return 0; +} diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-arith-overflow.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-arith-overflow.C new file mode 100644 index 0000000..7be4ca4 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-arith-overflow.C @@ -0,0 +1,116 @@ +// 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 + diff --git a/gcc/testsuite/gcc.dg/builtin-constant_p-1.c b/gcc/testsuite/gcc.dg/builtin-constant_p-1.c index b0b34f4..f177fe3 100644 --- a/gcc/testsuite/gcc.dg/builtin-constant_p-1.c +++ b/gcc/testsuite/gcc.dg/builtin-constant_p-1.c @@ -2,9 +2,9 @@ int main() { - if (__builtin_constant_p ()) /* { dg-error "not enough" } */ + if (__builtin_constant_p ()) /* { dg-error "too few arguments" } */ return 0; - if (__builtin_constant_p (5, 6)) /* { dg-error "too many" } */ + if (__builtin_constant_p (5, 6)) /* { dg-error "too many arguments" } */ return 1; return 0; } diff --git a/gcc/testsuite/gcc.dg/builtins-error.c b/gcc/testsuite/gcc.dg/builtins-error.c index 9ddf1b1..945d239 100644 --- a/gcc/testsuite/gcc.dg/builtins-error.c +++ b/gcc/testsuite/gcc.dg/builtins-error.c @@ -23,19 +23,19 @@ int test1(struct X x) int test2(double x) { - if (x == 1) return __builtin_fpclassify(1,2,3,4,5); /* { dg-error "not enough arguments" } */ - if (x == 2) return __builtin_isfinite(); /* { dg-error "not enough arguments" } */ - if (x == 3) return __builtin_isinf_sign(); /* { dg-error "not enough arguments" } */ - if (x == 4) return __builtin_isinf(); /* { dg-error "not enough arguments" } */ - if (x == 5) return __builtin_isnan(); /* { dg-error "not enough arguments" } */ - if (x == 6) return __builtin_isnormal(); /* { dg-error "not enough arguments" } */ - if (x == 7) return __builtin_isgreater(x); /* { dg-error "not enough arguments" } */ - if (x == 8) return __builtin_isgreaterequal(x); /* { dg-error "not enough arguments" } */ - if (x == 9) return __builtin_isless(x); /* { dg-error "not enough arguments" } */ - if (x == 10) return __builtin_islessequal(x); /* { dg-error "not enough arguments" } */ - if (x == 11) return __builtin_islessgreater(x); /* { dg-error "not enough arguments" } */ - if (x == 12) return __builtin_isunordered(x); /* { dg-error "not enough arguments" } */ - if (x == 13) return __builtin_signbit(); /* { dg-error "not enough arguments" } */ + if (x == 1) return __builtin_fpclassify(1,2,3,4,5); /* { dg-error "too few arguments" } */ + if (x == 2) return __builtin_isfinite(); /* { dg-error "too few arguments" } */ + if (x == 3) return __builtin_isinf_sign(); /* { dg-error "too few arguments" } */ + if (x == 4) return __builtin_isinf(); /* { dg-error "too few arguments" } */ + if (x == 5) return __builtin_isnan(); /* { dg-error "too few arguments" } */ + if (x == 6) return __builtin_isnormal(); /* { dg-error "too few arguments" } */ + if (x == 7) return __builtin_isgreater(x); /* { dg-error "too few arguments" } */ + if (x == 8) return __builtin_isgreaterequal(x); /* { dg-error "too few arguments" } */ + if (x == 9) return __builtin_isless(x); /* { dg-error "too few arguments" } */ + if (x == 10) return __builtin_islessequal(x); /* { dg-error "too few arguments" } */ + if (x == 11) return __builtin_islessgreater(x); /* { dg-error "too few arguments" } */ + if (x == 12) return __builtin_isunordered(x); /* { dg-error "too few arguments" } */ + if (x == 13) return __builtin_signbit(); /* { dg-error "too few arguments" } */ return 0; } ^ permalink raw reply [flat|nested] 36+ messages in thread
* PING [PATCH] integer overflow checking builtins in constant expressions 2016-05-01 16:40 [PATCH] integer overflow checking builtins in constant expressions Martin Sebor @ 2016-05-09 16:39 ` Martin Sebor 2016-05-16 19:30 ` PING 2 " Martin Sebor 2016-05-17 18:54 ` Jason Merrill [not found] ` <20160531215025.GK28550@tucnak.redhat.com> 2 siblings, 1 reply; 36+ messages in thread From: Martin Sebor @ 2016-05-09 16:39 UTC (permalink / raw) To: Gcc Patch List, Jason Merrill, Joseph S. Myers Pinging the following patch: https://gcc.gnu.org/ml/gcc-patches/2016-05/msg00013.html On 05/01/2016 10:39 AM, Martin Sebor wrote: > c/68120 - can't easily deal with integer overflow at compile time, > is an enhancement request to make the integer overflow intrinsics > usable in constant expressions in C (in addition to letting them > be invoked with just two arguments). > > The inability to use the built-ins in constant expressions also > limited to non-constexpr the contexts in which the patch for c++/ > 69517 - SEGV on a VLA with excess initializer elements, was able > to prevent the SEGV. This limitation is noted in c++/70507 - > integer overflow builtins not constant expressions. > > The attached patch implements the request in c/68120 for both > the C and C++ front-ends. It stops short of providing the new > __builtin_add_wrapv function requested in c/68120. It doesn't > seem necessary since the same functionality is available with > the patch via the existing built-ins. > > With this enhancement in place it will be possible to add the > C++ VLA checking to constexpr functions and fully resolve c++/ > 69517 (which I plan to do next). > > While testing the patch, I also noticed an minor inconsistency > in the text of the diagnostic GCC issues for invalid calls to > the built-ins with insufficient numbers of arguments: for one > set of built-ins the error says: "not enough arguments," while > for another it says: "too few arguments." I raised PR c/70883 > - inconsistent error message for calls to __builtin_add_overflow > with too few arguments, for this and include a fix in this patch > as well. > > Martin > > PS The enhancement to call the type-generic built-ins with a null > pointer is not available in C++ 98 mode because GCC doesn't allow > null pointers in constant expressions. Since C and later versions > of C++ do, it seems that it might be worthwhile to relax the rules > and accept them in C++ 98 as well so that the built-ins can be used > portably across all versions of C++. > ^ permalink raw reply [flat|nested] 36+ messages in thread
* PING 2 [PATCH] integer overflow checking builtins in constant expressions 2016-05-09 16:39 ` PING " Martin Sebor @ 2016-05-16 19:30 ` Martin Sebor 0 siblings, 0 replies; 36+ messages in thread From: Martin Sebor @ 2016-05-16 19:30 UTC (permalink / raw) To: Gcc Patch List, Jason Merrill, Joseph S. Myers Ping 2 of the following patch: https://gcc.gnu.org/ml/gcc-patches/2016-05/msg00013.html On 05/09/2016 10:38 AM, Martin Sebor wrote: > Pinging the following patch: > https://gcc.gnu.org/ml/gcc-patches/2016-05/msg00013.html > > On 05/01/2016 10:39 AM, Martin Sebor wrote: >> c/68120 - can't easily deal with integer overflow at compile time, >> is an enhancement request to make the integer overflow intrinsics >> usable in constant expressions in C (in addition to letting them >> be invoked with just two arguments). >> >> The inability to use the built-ins in constant expressions also >> limited to non-constexpr the contexts in which the patch for c++/ >> 69517 - SEGV on a VLA with excess initializer elements, was able >> to prevent the SEGV. This limitation is noted in c++/70507 - >> integer overflow builtins not constant expressions. >> >> The attached patch implements the request in c/68120 for both >> the C and C++ front-ends. It stops short of providing the new >> __builtin_add_wrapv function requested in c/68120. It doesn't >> seem necessary since the same functionality is available with >> the patch via the existing built-ins. >> >> With this enhancement in place it will be possible to add the >> C++ VLA checking to constexpr functions and fully resolve c++/ >> 69517 (which I plan to do next). >> >> While testing the patch, I also noticed an minor inconsistency >> in the text of the diagnostic GCC issues for invalid calls to >> the built-ins with insufficient numbers of arguments: for one >> set of built-ins the error says: "not enough arguments," while >> for another it says: "too few arguments." I raised PR c/70883 >> - inconsistent error message for calls to __builtin_add_overflow >> with too few arguments, for this and include a fix in this patch >> as well. >> >> Martin >> >> PS The enhancement to call the type-generic built-ins with a null >> pointer is not available in C++ 98 mode because GCC doesn't allow >> null pointers in constant expressions. Since C and later versions >> of C++ do, it seems that it might be worthwhile to relax the rules >> and accept them in C++ 98 as well so that the built-ins can be used >> portably across all versions of C++. >> > ^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH] integer overflow checking builtins in constant expressions 2016-05-01 16:40 [PATCH] integer overflow checking builtins in constant expressions Martin Sebor 2016-05-09 16:39 ` PING " Martin Sebor @ 2016-05-17 18:54 ` Jason Merrill 2016-05-31 23:43 ` Martin Sebor [not found] ` <20160531215025.GK28550@tucnak.redhat.com> 2 siblings, 1 reply; 36+ messages in thread From: Jason Merrill @ 2016-05-17 18:54 UTC (permalink / raw) To: Martin Sebor, Gcc Patch List, Joseph S. Myers On 05/01/2016 12:39 PM, Martin Sebor wrote: > + if (TREE_CODE (arg0) == INTEGER_CST && TREE_CODE (arg1) == INTEGER_CST) > + { > + if (tree result = size_binop_loc (EXPR_LOC_OR_LOC (t, input_location), > + opcode, arg0, arg1)) > + { > + if (TREE_OVERFLOW (result)) > + { > + /* Reset TREE_OVERFLOW to avoid warnings for the overflow. */ > + TREE_OVERFLOW (result) = 0; > + > + return build_complex (TREE_TYPE (t), result, integer_one_node); > + } > + > + return build_complex (TREE_TYPE (t), result, integer_zero_node); > + } > + } Should this be in the middle-end somewhere, perhaps shared with fold_builtin_arith_overflow? I notice that the comment for that function says that it folds into normal arithmetic if the operation can never overflow, but I don't see any code that would accomplish that. Jason ^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH] integer overflow checking builtins in constant expressions 2016-05-17 18:54 ` Jason Merrill @ 2016-05-31 23:43 ` Martin Sebor 0 siblings, 0 replies; 36+ messages in thread From: Martin Sebor @ 2016-05-31 23:43 UTC (permalink / raw) To: Jason Merrill, Gcc Patch List, Joseph S. Myers On 05/17/2016 12:54 PM, Jason Merrill wrote: > On 05/01/2016 12:39 PM, Martin Sebor wrote: >> + if (TREE_CODE (arg0) == INTEGER_CST && TREE_CODE (arg1) == >> INTEGER_CST) >> + { >> + if (tree result = size_binop_loc (EXPR_LOC_OR_LOC (t, >> input_location), >> + opcode, arg0, arg1)) >> + { >> + if (TREE_OVERFLOW (result)) >> + { >> + /* Reset TREE_OVERFLOW to avoid warnings for the overflow. */ >> + TREE_OVERFLOW (result) = 0; >> + >> + return build_complex (TREE_TYPE (t), result, >> integer_one_node); >> + } >> + >> + return build_complex (TREE_TYPE (t), result, integer_zero_node); >> + } >> + } > > Should this be in the middle-end somewhere, perhaps shared with > fold_builtin_arith_overflow? I notice that the comment for that > function says that it folds into normal arithmetic if the operation can > never overflow, but I don't see any code that would accomplish that. I'm not quite sure where to move this hunk so that it could be shared or with what. With the patch, fold_builtin_arith_overflow returns the overflow bit alone when the last argument is null. Otherwise it returns a complex number with both the overflow bit and the result. Here we need both parts of the result. I suppose I could factor out the call to size_binop_loc into its own function, have it return the overflow bit, and set a by-reference argument to the arithmetic result so that it could be built into a complex result here but that doesn't seem like it would gain us much, if anything. Do you have any suggestions? Martin ^ permalink raw reply [flat|nested] 36+ messages in thread
[parent not found: <20160531215025.GK28550@tucnak.redhat.com>]
[parent not found: <574E13DD.9040401@gmail.com>]
* Re: [PATCH] integer overflow checking builtins in constant expressions [not found] ` <574E13DD.9040401@gmail.com> @ 2016-06-01 7:52 ` Jakub Jelinek 2016-06-01 15:17 ` Martin Sebor 2016-06-01 18:46 ` Jason Merrill 1 sibling, 1 reply; 36+ messages in thread From: Jakub Jelinek @ 2016-06-01 7:52 UTC (permalink / raw) To: Martin Sebor; +Cc: Gcc Patch List, Jason Merrill, Joseph S. Myers On Tue, May 31, 2016 at 04:44:45PM -0600, Martin Sebor wrote: > >I fail to understand this. You set type to NULL_TREE, and then (not in any > >kind of loop, nor any label you could goto to) do: > > type = type ? type : xxx; > >That is necessarily equal to type = xxx;, isn't it? > > Each label falls through to the next, so the test prevents > the subsequent labels from overwriting the value assigned > to type by the label that matched the switch expression. > I'm not exactly enamored with these repetitive tests but > the only alternative that occurred to me was to duplicate > the whole switch statement, and this seemed like the lesser > of the two evils. If you have a suggestion for something > better I'd be happy to change it. Ugh, I've missed missing break; Anyway, IMHO we shouldn't change the old style builtins and therefore you really don't need this. > >>+ /* When the last argument is a NULL pointer and the first two arguments > >>+ are constant, attempt to fold the built-in call into a constant > >>+ expression indicating whether or not it detected an overflow but > >>+ without storing the result. */ > >>+ if ((!arg2 || zerop (arg2)) > > > >As I said in another mail, IMNSHO you don't want to change the clang > >compatibility builtins, therefore arg2 should never be NULL. > > Allowing the last argument to be null is the subject of > the enhancement request in c/68120 (one of the two PRs > driving this enhancement). The argument can only be null > for these overloads and not the type-generic ones because > the latter use the type to which the pointer points to > determine the type in which to do the arithmetic. Without > this change, C or C++ 98 users wouldn't be able to use the > built-ins in constant expressions (C++ 98 doesn't allow > casting a null pointer in those contexts). Sorry, I don't understand this argument. What is wrong on using const int x = __builtin_add_overflow (1234567, 123456, (int *) NULL); ? I don't get any warning/error on #include <stddef.h> int *const p = (int *) NULL; in C89 nor C++98 with -pedantic-errors, not to mention that the builtins are extensions anyway, so what do you accept/reject in its arguments doesn't need to honor any specific rules. Even if (int *) NULL is not allowed for whatever reason, is (int *) 0 disallowed too? It is just a matter of what we document. The clang compatibility builtins are severely limited and too ugly to live with, they don't allow different types of the arguments, have just a very limited set of accepted types, don't allow different result type, how do you even handle a type for which you don't know if it is unsigned, unsigned long or unsigned long long? size_t a, b, c, d; ... if (sizeof (size_t) == sizeof (unsigned long long)) a = __builtin_uaddll_overflow (b, c, &d); else if (sizeof (size_t) == sizeof (unsigned long)) a = __builtin_uaddl_overflow (b, c, &d); else if (sizeof (size_t) == sizeof (unsigned int)) a = __builtin_uadd_overflow (b, c, &d); else abort (); ? We really shouldn't encourage these at all. > I suspect I didn't use integer_zerop because the argument > is a pointer and not integer, but I'll change it before > I commit the patch. Pointers are treated the same as integers, they are represented as INTEGER_CSTs too. > > >Regarding the varargs vs. named args only different diagnostics, if you > >think it should change, then IMHO you should submit as independent patch > >a change to the diagnostics wording, so that the wording is the same, rather > >than changing functions from fixed arguments to varargs (/ typegeneric). > > I'm not sure I know what part of the patch you're referring > to. Are you suggesting to submit a separate patch for the > change from "not enough arguments" to "too few arguments" > below? > > - "not enough arguments to function %qE", fndecl); > + "too few arguments to function %qE", fndecl); Yeah. IMO that is an independent change, you can/should add a testcase for it, and it can go in before or after the patch. Jakub ^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH] integer overflow checking builtins in constant expressions 2016-06-01 7:52 ` Jakub Jelinek @ 2016-06-01 15:17 ` Martin Sebor 2016-06-01 15:45 ` Jakub Jelinek 2016-06-02 3:11 ` Martin Sebor 0 siblings, 2 replies; 36+ messages in thread From: Martin Sebor @ 2016-06-01 15:17 UTC (permalink / raw) To: Jakub Jelinek; +Cc: Gcc Patch List, Jason Merrill, Joseph S. Myers >>> As I said in another mail, IMNSHO you don't want to change the clang >>> compatibility builtins, therefore arg2 should never be NULL. >> >> Allowing the last argument to be null is the subject of >> the enhancement request in c/68120 (one of the two PRs >> driving this enhancement). The argument can only be null >> for these overloads and not the type-generic ones because >> the latter use the type to which the pointer points to >> determine the type in which to do the arithmetic. Without >> this change, C or C++ 98 users wouldn't be able to use the >> built-ins in constant expressions (C++ 98 doesn't allow >> casting a null pointer in those contexts). > > Sorry, I don't understand this argument. What is wrong on using > const int x = __builtin_add_overflow (1234567, 123456, (int *) NULL); I see. You meant that only the clang compatibility built-ins (i.e. the typed ones like __builtin_uadd_overflow) shouldn't be allowed to take null pointer as the last argument, but the type-generic ones should. That would work for C, though it won't satisfy the feature request in c/68120 which asks for the last argument to be optional (it can't be there since it determines the type of the operation). It will not work for C++ 98. But those might be acceptable limitations (at least in C there's a workaround). > ? > I don't get any warning/error on > #include <stddef.h> > int *const p = (int *) NULL; > in C89 nor C++98 with -pedantic-errors, That's because the above is not a constant expression. If such a cast were to be used in one like the enum below, GCC in C++ 98 mode would give: error: a cast to a type other than an integral or enumeration type cannot appear in a constant-expression enum { e = (long)(int*)0 }; ^ (Curiously, this is accepted with a pedantic warning in C mode.) > not to mention that the > builtins are extensions anyway, so what do you accept/reject in its > arguments doesn't need to honor any specific rules. Even if (int *) NULL > is not allowed for whatever reason, is (int *) 0 disallowed too? It is > just a matter of what we document. Since GCC accepts the above as an extension it was tempting to change G++ to accept it as well. But such a change seemed beyond the scope of this patch. Applying different type system rules in a set of built-ins than in the rest of the language wouldn't seem appropriate to me. I would rather accept the limitation that the typed built-ins won't be usable in C++ 98 unless Jason recommends to go down that route. Unless I hear otherwise, I'll go ahead and change the patch to have only the type-generic built-ins accept a null pointer as an argument. >> I'm not sure I know what part of the patch you're referring >> to. Are you suggesting to submit a separate patch for the >> change from "not enough arguments" to "too few arguments" >> below? >> >> - "not enough arguments to function %qE", fndecl); >> + "too few arguments to function %qE", fndecl); > > Yeah. IMO that is an independent change, you can/should add a testcase for > it, and it can go in before or after the patch. I agree that independent substantive changes are best made separately. I made this one part of the patch because it affects the tests that go with it (that's how I found the inconsistency, by my tests failing), and because changing the spelling of a warning seemed too trivial of a change to justify a patch of its own and taking up all of your time to review and approve it. But since you insist I will submit it separately ahead of this updated patch. Martin ^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH] integer overflow checking builtins in constant expressions 2016-06-01 15:17 ` Martin Sebor @ 2016-06-01 15:45 ` Jakub Jelinek 2016-06-01 16:13 ` Martin Sebor 2016-06-02 3:11 ` Martin Sebor 1 sibling, 1 reply; 36+ messages in thread From: Jakub Jelinek @ 2016-06-01 15:45 UTC (permalink / raw) To: Martin Sebor; +Cc: Gcc Patch List, Jason Merrill, Joseph S. Myers On Wed, Jun 01, 2016 at 09:17:35AM -0600, Martin Sebor wrote: > I see. You meant that only the clang compatibility built-ins > (i.e. the typed ones like __builtin_uadd_overflow) shouldn't > be allowed to take null pointer as the last argument, but the > type-generic ones should. That would work for C, though it > won't satisfy the feature request in c/68120 which asks for > the last argument to be optional (it can't be there since it > determines the type of the operation). It will not work for I don't think we should make it optional. > C++ 98. But those might be acceptable limitations (at least > in C there's a workaround). __builtin_* is an extension. Perhaps for C (any) and C++ (< C++14) you could require that in constant expressions the last argument of the builtin has to be a NULL pointer cast to some pointer type (not necessarily a constant expression)? This can be handled simply by saving/restoring the constant expression context around the parsing of the last argument of the builtin, and afterwards verifying it has the required properties. For C++14 and later, it would be nice if one could also do: struct S { int val; bool ovf; }; constexpr S foo (int x, int y) { S ret = { 0, false }; ret.ovf = __builtin_add_overflow (x, y, &ret.val); return ret; } instead of doing: ret.ovf = __builtin_add_overflow (x, y, (int *) 0); ret.val = (unsigned) x + y; or similar. Does that work with your patch? Jakub ^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH] integer overflow checking builtins in constant expressions 2016-06-01 15:45 ` Jakub Jelinek @ 2016-06-01 16:13 ` Martin Sebor 0 siblings, 0 replies; 36+ messages in thread From: Martin Sebor @ 2016-06-01 16:13 UTC (permalink / raw) To: Jakub Jelinek; +Cc: Gcc Patch List, Jason Merrill, Joseph S. Myers >> C++ 98. But those might be acceptable limitations (at least >> in C there's a workaround). > > __builtin_* is an extension. Perhaps for C (any) and C++ (< C++14) > you could require that in constant expressions the last argument > of the builtin has to be a NULL pointer cast to some pointer type > (not necessarily a constant expression)? This can be handled > simply by saving/restoring the constant expression context around > the parsing of the last argument of the builtin, and afterwards > verifying it has the required properties. I'll see if there's an easy way to make it work. > > For C++14 and later, it would be nice if one could also do: > struct S { int val; bool ovf; }; > constexpr S foo (int x, int y) > { > S ret = { 0, false }; > ret.ovf = __builtin_add_overflow (x, y, &ret.val); > return ret; > } > instead of doing: > ret.ovf = __builtin_add_overflow (x, y, (int *) 0); > ret.val = (unsigned) x + y; > or similar. > Does that work with your patch? Yes, it does (it's exercised by the constexpr-arith-overflow.C test). The patch is in part motivated by GCC needing to make use of the overflow built-ins in constexpr functions (to detect VLA bounds overflow). Martin ^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH] integer overflow checking builtins in constant expressions 2016-06-01 15:17 ` Martin Sebor 2016-06-01 15:45 ` Jakub Jelinek @ 2016-06-02 3:11 ` Martin Sebor 2016-06-02 7:23 ` Jakub Jelinek 1 sibling, 1 reply; 36+ messages in thread From: Martin Sebor @ 2016-06-02 3:11 UTC (permalink / raw) To: Jakub Jelinek; +Cc: Gcc Patch List, Jason Merrill, Joseph S. Myers [-- Attachment #1: Type: text/plain, Size: 425 bytes --] > Unless I hear otherwise, I'll go ahead and change the patch > to have only the type-generic built-ins accept a null pointer > as an argument. Since Jason doesn't think making it work in C++ 98 is important I modified the patch to keep the Clang-compatibility built-ins unchanged and to remove the default argument from the others. Attached is an updated version intended to be applied on top of the c/70883 patch. Martin [-- Attachment #2: gcc-70507.patch --] [-- Type: text/x-patch, Size: 49231 bytes --] PR c++/70507 - integer overflow builtins not constant expressions PR c/68120 - can't easily deal with integer overflow at compile time gcc/testsuite/ChangeLog: 2016-06-01 Martin Sebor <msebor@redhat.com> PR c++/70507 PR c/68120 * c-c++-common/builtin-arith-overflow-1.c: Add test cases. * c-c++-common/builtin-arith-overflow-2.c: New test. * g++.dg/cpp0x/constexpr-arith-overflow.C: New test. gcc/cp/ChangeLog: 2016-06-01 Martin Sebor <msebor@redhat.com> PR c++/70507 PR c/68120 * constexpr.c (cxx_eval_internal_function): New function. (cxx_eval_call_expression): Call it. * tree.c (builtin_valid_in_constant_expr_p): Handle integer arithmetic overflow built-ins. gcc/c-family/ChangeLog: 2016-06-01 Martin Sebor <msebor@redhat.com> PR c/68120 (check_builtin_function_arguments): Allow type-specific integer arithmetic overflow built-ins to take either 2 or three arguments. gcc/ChangeLog: 2016-06-01 Martin Sebor <msebor@redhat.com> PR c++/70507 PR c/68120 * builtins.c (fold_builtin_unordered_cmp): Handle integer arithmetic overflow built-ins. * doc/extend.texi (Integer Overflow Builtins): Update. diff --git a/gcc/builtins.c b/gcc/builtins.c index 931d4a6..4620553 100644 --- a/gcc/builtins.c +++ b/gcc/builtins.c @@ -7957,8 +7957,8 @@ fold_builtin_arith_overflow (location_t loc, enum built_in_function fcode, tree arg0, tree arg1, tree arg2) { enum internal_fn ifn = IFN_LAST; - tree type = TREE_TYPE (TREE_TYPE (arg2)); - tree mem_arg2 = build_fold_indirect_ref_loc (loc, arg2); + enum tree_code opcode; + switch (fcode) { case BUILT_IN_ADD_OVERFLOW: @@ -7969,6 +7969,7 @@ fold_builtin_arith_overflow (location_t loc, enum built_in_function fcode, case BUILT_IN_UADDL_OVERFLOW: case BUILT_IN_UADDLL_OVERFLOW: ifn = IFN_ADD_OVERFLOW; + opcode = PLUS_EXPR; break; case BUILT_IN_SUB_OVERFLOW: case BUILT_IN_SSUB_OVERFLOW: @@ -7978,6 +7979,7 @@ fold_builtin_arith_overflow (location_t loc, enum built_in_function fcode, case BUILT_IN_USUBL_OVERFLOW: case BUILT_IN_USUBLL_OVERFLOW: ifn = IFN_SUB_OVERFLOW; + opcode = MINUS_EXPR; break; case BUILT_IN_MUL_OVERFLOW: case BUILT_IN_SMUL_OVERFLOW: @@ -7987,10 +7989,35 @@ fold_builtin_arith_overflow (location_t loc, enum built_in_function fcode, case BUILT_IN_UMULL_OVERFLOW: case BUILT_IN_UMULLL_OVERFLOW: ifn = IFN_MUL_OVERFLOW; + opcode = MULT_EXPR; break; default: gcc_unreachable (); } + + /* For the "generic" overloads, the first two arguments can have different + types and the last argument determines the target type to use to check + for overflow. The arguments of the other overloads all have the same + type. */ + tree type = TREE_TYPE (TREE_TYPE (arg2)); + bool isnullp = integer_zerop (arg2); + + /* When the last argument is a null pointer and the first two arguments + are constant, attempt to fold the built-in call into a constant + expression indicating whether or not it detected an overflow but + without storing the result. */ + if (isnullp + && TREE_CODE (arg0) == INTEGER_CST && TREE_CODE (arg1) == INTEGER_CST) + { + /* Perform the computation in the target type and check for overflow. */ + arg0 = fold_convert (type, arg0); + arg1 = fold_convert (type, arg1); + + if (tree result = size_binop_loc (loc, opcode, arg0, arg1)) + return TREE_OVERFLOW (result) ? build_one_cst (boolean_type_node) + : build_zero_cst (boolean_type_node); + } + tree ctype = build_complex_type (type); tree call = build_call_expr_internal_loc (loc, ifn, ctype, 2, arg0, arg1); @@ -7998,6 +8025,11 @@ fold_builtin_arith_overflow (location_t loc, enum built_in_function fcode, tree intres = build1_loc (loc, REALPART_EXPR, type, tgt); tree ovfres = build1_loc (loc, IMAGPART_EXPR, type, tgt); ovfres = fold_convert_loc (loc, boolean_type_node, ovfres); + + if (isnullp) + return ovfres; + + tree mem_arg2 = build_fold_indirect_ref_loc (loc, arg2); tree store = fold_build2_loc (loc, MODIFY_EXPR, void_type_node, mem_arg2, intres); return build2_loc (loc, COMPOUND_EXPR, boolean_type_node, store, ovfres); diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c index 482f8af..c77e939 100644 --- a/gcc/cp/constexpr.c +++ b/gcc/cp/constexpr.c @@ -1255,6 +1255,68 @@ cx_error_context (void) return r; } +/* Evaluate a call T to a GCC internal function when possible and return + the evaluated result or, under the control of CTX, give an error, set + NON_CONSTANT_P, and return the unevaluated call T otherwise. */ + +static tree +cxx_eval_internal_function (const constexpr_ctx *ctx, tree t, + bool lval, + bool *non_constant_p, bool *overflow_p) +{ + enum tree_code opcode = ERROR_MARK; + + switch (CALL_EXPR_IFN (t)) + { + case IFN_UBSAN_NULL: + case IFN_UBSAN_BOUNDS: + case IFN_UBSAN_VPTR: + return void_node; + + case IFN_ADD_OVERFLOW: + opcode = PLUS_EXPR; + break; + case IFN_SUB_OVERFLOW: + opcode = MINUS_EXPR; + break; + case IFN_MUL_OVERFLOW: + opcode = MULT_EXPR; + break; + + default: + if (!ctx->quiet) + error_at (EXPR_LOC_OR_LOC (t, input_location), + "call to internal function"); + *non_constant_p = true; + return t; + } + + tree arg0 = cxx_eval_constant_expression (ctx, CALL_EXPR_ARG (t, 0), lval, + non_constant_p, overflow_p); + tree arg1 = cxx_eval_constant_expression (ctx, CALL_EXPR_ARG (t, 1), lval, + non_constant_p, overflow_p); + + if (TREE_CODE (arg0) == INTEGER_CST && TREE_CODE (arg1) == INTEGER_CST) + { + if (tree result = size_binop_loc (EXPR_LOC_OR_LOC (t, input_location), + opcode, arg0, arg1)) + { + if (TREE_OVERFLOW (result)) + { + /* Reset TREE_OVERFLOW to avoid warnings for the overflow. */ + TREE_OVERFLOW (result) = 0; + + return build_complex (TREE_TYPE (t), result, integer_one_node); + } + + return build_complex (TREE_TYPE (t), result, integer_zero_node); + } + } + + *non_constant_p = true; + return t; +} + /* Subroutine of cxx_eval_constant_expression. Evaluate the call expression tree T in the context of OLD_CALL expression evaluation. */ @@ -1270,18 +1332,8 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t, bool depth_ok; if (fun == NULL_TREE) - switch (CALL_EXPR_IFN (t)) - { - case IFN_UBSAN_NULL: - case IFN_UBSAN_BOUNDS: - case IFN_UBSAN_VPTR: - return void_node; - default: - if (!ctx->quiet) - error_at (loc, "call to internal function"); - *non_constant_p = true; - return t; - } + return cxx_eval_internal_function (ctx, t, lval, + non_constant_p, overflow_p); if (TREE_CODE (fun) != FUNCTION_DECL) { diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c index 04702ee..f9db199 100644 --- a/gcc/cp/tree.c +++ b/gcc/cp/tree.c @@ -352,6 +352,32 @@ builtin_valid_in_constant_expr_p (const_tree decl) case BUILT_IN_FUNCTION: case BUILT_IN_LINE: + /* The following built-ins are valid in constant expressions + when their arguments are. */ + case BUILT_IN_ADD_OVERFLOW: + case BUILT_IN_SADD_OVERFLOW: + case BUILT_IN_SADDL_OVERFLOW: + case BUILT_IN_SADDLL_OVERFLOW: + case BUILT_IN_UADD_OVERFLOW: + case BUILT_IN_UADDL_OVERFLOW: + case BUILT_IN_UADDLL_OVERFLOW: + + case BUILT_IN_SUB_OVERFLOW: + case BUILT_IN_SSUB_OVERFLOW: + case BUILT_IN_SSUBL_OVERFLOW: + case BUILT_IN_SSUBLL_OVERFLOW: + case BUILT_IN_USUB_OVERFLOW: + case BUILT_IN_USUBL_OVERFLOW: + case BUILT_IN_USUBLL_OVERFLOW: + + case BUILT_IN_MUL_OVERFLOW: + case BUILT_IN_SMUL_OVERFLOW: + case BUILT_IN_SMULL_OVERFLOW: + case BUILT_IN_SMULLL_OVERFLOW: + case BUILT_IN_UMUL_OVERFLOW: + case BUILT_IN_UMULL_OVERFLOW: + case BUILT_IN_UMULLL_OVERFLOW: + /* These have constant results even if their operands are non-constant. */ case BUILT_IN_CONSTANT_P: diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi index 2d4f028..00435f7 100644 --- a/gcc/doc/extend.texi +++ b/gcc/doc/extend.texi @@ -9737,7 +9737,10 @@ compiler may also ignore this parameter. @section Built-in Functions to Perform Arithmetic with Overflow Checking The following built-in functions allow performing simple arithmetic operations -together with checking whether the operations overflowed. +together with checking whether the operations overflowed. The first of the +functions accepts either a pointer to an integer object or a null pointer to +integer as the last argument. The rest require a pointer to an object of +the specified type as the last argument. @deftypefn {Built-in Function} bool __builtin_add_overflow (@var{type1} a, @var{type2} b, @var{type3} *res) @deftypefnx {Built-in Function} bool __builtin_sadd_overflow (int a, int b, int *res) @@ -9747,21 +9750,50 @@ together with checking whether the operations overflowed. @deftypefnx {Built-in Function} bool __builtin_uaddl_overflow (unsigned long int a, unsigned long int b, unsigned long int *res) @deftypefnx {Built-in Function} bool __builtin_uaddll_overflow (unsigned long long int a, unsigned long long int b, unsigned long int *res) -These built-in functions promote the first two operands into infinite precision signed -type and perform addition on those promoted operands. The result is then -cast to the type the third pointer argument points to and stored there. -If the stored result is equal to the infinite precision result, the built-in -functions return false, otherwise they return true. As the addition is -performed in infinite signed precision, these built-in functions have fully defined -behavior for all argument values. - -The first built-in function allows arbitrary integral types for operands and -the result type must be pointer to some integer type, the rest of the built-in -functions have explicit integer types. +These built-in functions promote the first two operands into infinite precision +signed type and perform addition on those promoted operands. The result is then +converted to the type the third pointer argument points to, and for the first +function when the pointer is not null, stored there. If the converted result +is equal to the infinite precision result, the built-in functions return +@code{false}, otherwise they return @code{true} to indicate that an overflow +has been detected. Because the addition is performed in infinite precision, +these built-in functions have fully defined behavior for all argument values +and integer types. + +The first type-generic built-in function allows arbitrary integer types as +the first two arguments and requires that a pointer to some possibly distinct +integer type be passed to it as the third argument. The pointed-to type is +then used to determine the overflow. As a result, this built-in function +can be used to detect overflow in any arbitrary integer type, including +@code{char} and @code{short}. The remaining built-in functions take +arguments of explicit integer types and make it possible to determine +overflow only in the ranges of those types. Since the only provided forms +of these latter built-in functions are for the signed and unsigned variants +of types @code{int}, @code{long}, and @code{long long}, they cannot be used +to determine overflow in other integer types. + +To enable the efficient integer overflow detection at translation-time, +in C and C++ 11 and later programs (but not in C++ 98), the first built-in +may be invoked in constant integer expression contexts with a null pointer +cast to a pointer to the appropriate integer type as the third argument. +For example, the following macro can be used to portably check, at +compile-time, whether or not adding two constant integers will overflow, +and perform the addition only when it is known to be safe and not to trigger +a @option{-Woverflow} warning. + +@smallexample +#define INT_ADD_OVERFLOW(a, b) \ + __builtin_add_overflow (a, b, (__typeof__ ((a) + (b)) *) 0) + +enum @{ + A = INT_MAX, B = 3, + C = INT_ADD_OVERFLOW (A, B) ? 0 : A + B +@}; +@end smallexample -The compiler will attempt to use hardware instructions to implement -these built-in functions where possible, like conditional jump on overflow -after addition, conditional jump on carry etc. +For invocations of the built-in functions evaluated at run-time the compiler +will attempt to make use of efficient hardware instructions such as conditional +jump on overflow after addition, conditional jump on carry, etc. @end deftypefn @@ -9773,9 +9805,9 @@ after addition, conditional jump on carry etc. @deftypefnx {Built-in Function} bool __builtin_usubl_overflow (unsigned long int a, unsigned long int b, unsigned long int *res) @deftypefnx {Built-in Function} bool __builtin_usubll_overflow (unsigned long long int a, unsigned long long int b, unsigned long int *res) -These built-in functions are similar to the add overflow checking built-in -functions above, except they perform subtraction, subtract the second argument -from the first one, instead of addition. +These built-in functions are analogous to the add overflow checking built-in +functions above, except they subtract the second argument from the first one +rather than adding it to it. @end deftypefn @@ -9787,8 +9819,8 @@ from the first one, instead of addition. @deftypefnx {Built-in Function} bool __builtin_umull_overflow (unsigned long int a, unsigned long int b, unsigned long int *res) @deftypefnx {Built-in Function} bool __builtin_umulll_overflow (unsigned long long int a, unsigned long long int b, unsigned long int *res) -These built-in functions are similar to the add overflow checking built-in -functions above, except they perform multiplication, instead of addition. +These built-in functions are analogous to the add overflow checking built-in +functions above, except they perform multiplication rather than addition. @end deftypefn diff --git a/gcc/testsuite/c-c++-common/builtin-arith-overflow-1.c b/gcc/testsuite/c-c++-common/builtin-arith-overflow-1.c index 69b5083..4fc58d1 100644 --- a/gcc/testsuite/c-c++-common/builtin-arith-overflow-1.c +++ b/gcc/testsuite/c-c++-common/builtin-arith-overflow-1.c @@ -1,23 +1,133 @@ /* { dg-do compile } */ +/* Verify that calls with fewer or more than 3 arguments to the generic + __builtin_op_overflow functions are rejected. */ + +int +generic_0 (void) +{ + int x = __builtin_add_overflow (); /* { dg-error "too few arguments to function" } */ + x += __builtin_sub_overflow (); /* { dg-error "too few arguments to function" } */ + x += __builtin_mul_overflow (); /* { dg-error "too few arguments to function" } */ + return x; +} + int -f1 (void) +generic_1 (int a) { - int x = __builtin_add_overflow (); /* { dg-error "not enough arguments to function" } */ - x += __builtin_sub_overflow (); /* { dg-error "not enough arguments to function" } */ - x += __builtin_mul_overflow (); /* { dg-error "not enough arguments to function" } */ + int x = __builtin_add_overflow (a); /* { dg-error "too few arguments to function" } */ + x += __builtin_sub_overflow (a); /* { dg-error "too few arguments to function" } */ + x += __builtin_mul_overflow (a); /* { dg-error "too few arguments to function" } */ + + /* Literal argument. */ + x += __builtin_add_overflow (1); /* { dg-error "too few arguments to function" } */ + x += __builtin_sub_overflow (2); /* { dg-error "too few arguments to function" } */ + x += __builtin_mul_overflow (3); /* { dg-error "too few arguments to function" } */ return x; } int -f2 (int a, int b, int *c, int d) +generic_2 (int a, int b) +{ + int x = __builtin_add_overflow (a, b);/* { dg-error "too few arguments to function" } */ + x += __builtin_sub_overflow (a, b); /* { dg-error "too few arguments to function" } */ + x += __builtin_mul_overflow (a, b); /* { dg-error "too few arguments to function" } */ + x += __builtin_add_overflow (a, 1); /* { dg-error "too few arguments to function" } */ + x += __builtin_sub_overflow (a, 2); /* { dg-error "too few arguments to function" } */ + x += __builtin_mul_overflow (a, 3); /* { dg-error "too few arguments to function" } */ + x += __builtin_add_overflow (4, b); /* { dg-error "too few arguments to function" } */ + x += __builtin_sub_overflow (5, b); /* { dg-error "too few arguments to function" } */ + x += __builtin_mul_overflow (6, b); /* { dg-error "too few arguments to function" } */ + return x; +} + +/* Verify that calls with the correct number of arguments to the generic + __builtin_op_overflow functions are accepted. */ + +int +generic_3 (int a, int b, int c) +{ + int x = __builtin_add_overflow (a, b, &c); + x += __builtin_sub_overflow (a, b, &c); + x += __builtin_mul_overflow (a, b, &c); + x += __builtin_add_overflow (a, 1, &c); + x += __builtin_sub_overflow (a, 2, &c); + x += __builtin_mul_overflow (a, 3, &c); + x += __builtin_add_overflow (4, b, &c); + x += __builtin_sub_overflow (5, b, &c); + x += __builtin_mul_overflow (6, b, &c); + x += __builtin_add_overflow (7, 8, &c); + x += __builtin_sub_overflow (9, 10, &c); + x += __builtin_mul_overflow (11, 12, &c); + return x; +} + +int +generic_4 (int a, int b, int *c, int d) { int x = __builtin_add_overflow (a, b, c, d); /* { dg-error "too many arguments to function" } */ - x += __builtin_sub_overflow (a, b, c, d, d, d); /* { dg-error "too many arguments to function" } */ + x += __builtin_sub_overflow (a, b, c, d); /* { dg-error "too many arguments to function" } */ x += __builtin_mul_overflow (a, b, c, d); /* { dg-error "too many arguments to function" } */ return x; } +/* Verify that calls with fewer or more than 3 arguments to the type + specific forms of the __builtin_op_overflow functions are rejected. */ + +int +generic_wrong_type (int a, int b) +{ + void *p = 0; + double d = 0; + int x = __builtin_add_overflow (a, b, p); /* { dg-error "does not have pointer to integer type" } */ + x += __builtin_sub_overflow (a, b, &p); /* { dg-error "does not have pointer to integer type" } */ + x += __builtin_mul_overflow (a, b, &d); /* { dg-error "does not have pointer to integer type" } */ + + /* Also verify literal arguments. */ + x += __builtin_add_overflow (1, 1, p); /* { dg-error "does not have pointer to integer type" } */ + x += __builtin_sub_overflow (1, 1, &p); /* { dg-error "does not have pointer to integer type" } */ + x += __builtin_mul_overflow (1, 1, &d); /* { dg-error "does not have pointer to integer type" } */ + return x; +} + +/* Verify that calls with fewer than 2 or more than 3 arguments to + the typed __builtin_op_overflow functions are rejected. */ +int +typed_0 (void) +{ + int x = __builtin_add_overflow (); /* { dg-error "too few arguments to function" } */ + x += __builtin_sub_overflow (); /* { dg-error "too few arguments to function" } */ + x += __builtin_mul_overflow (); /* { dg-error "too few arguments to function" } */ + return x; +} + +int +typed_1 (int a) +{ + int x = __builtin_sadd_overflow (a); /* { dg-error "too few arguments to function" } */ + x += __builtin_ssub_overflow (a); /* { dg-error "too few arguments to function" } */ + x += __builtin_smul_overflow (a); /* { dg-error "too few arguments to function" } */ + return x; +} + +int +typed_2 (int a, int b) +{ + int x = __builtin_sadd_overflow (a, b); /* { dg-error "too few arguments to function" } */ + x += __builtin_ssub_overflow (a, b); /* { dg-error "too few arguments to function" } */ + x += __builtin_smul_overflow (a, b); /* { dg-error "too few arguments to function" } */ + return x; +} + +int +typed_4 (int a, int b, int *c, int d) +{ + int x = __builtin_sadd_overflow (a, b, c, d); /* { dg-error "too many arguments to function" } */ + x += __builtin_ssub_overflow (a, b, c, d); /* { dg-error "too many arguments to function" } */ + x += __builtin_smul_overflow (a, b, c, d); /* { dg-error "too many arguments to function" } */ + return x; +} + enum E { e0 = 0, e1 = 1 }; #ifndef __cplusplus diff --git a/gcc/testsuite/c-c++-common/builtin-arith-overflow-2.c b/gcc/testsuite/c-c++-common/builtin-arith-overflow-2.c new file mode 100644 index 0000000..66e0388 --- /dev/null +++ b/gcc/testsuite/c-c++-common/builtin-arith-overflow-2.c @@ -0,0 +1,499 @@ +/* PR c/68120 - can't easily deal with integer overflow at compile time */ +/* { dg-do run } */ +/* { dg-additional-options "-Wno-long-long" } */ + +#define SCHAR_MAX __SCHAR_MAX__ +#define SHRT_MAX __SHRT_MAX__ +#define INT_MAX __INT_MAX__ +#define LONG_MAX __LONG_MAX__ +#define LLONG_MAX __LONG_LONG_MAX__ + +#define SCHAR_MIN (-__SCHAR_MAX__ - 1) +#define SHRT_MIN (-__SHRT_MAX__ - 1) +#define INT_MIN (-__INT_MAX__ - 1) +#define LONG_MIN (-__LONG_MAX__ - 1) +#define LLONG_MIN (-__LONG_LONG_MAX__ - 1) + +#define UCHAR_MAX (SCHAR_MAX * 2U + 1) +#define USHRT_MAX (SHRT_MAX * 2U + 1) +#define UINT_MAX (INT_MAX * 2U + 1) +#define ULONG_MAX (LONG_MAX * 2LU + 1) +#define ULLONG_MAX (LLONG_MAX * 2LLU + 1) + +#define USCHAR_MIN (-__USCHAR_MAX__ - 1) +#define USHRT_MIN (-__USHRT_MAX__ - 1) +#define UINT_MIN (-__UINT_MAX__ - 1) +#define ULONG_MIN (-__ULONG_MAX__ - 1) +#define ULLONG_MIN (-__ULONG_LONG_MAX__ - 1) + +/* Number of failed runtime assertions. */ +int nfails; + +void __attribute__ ((noclone, noinline)) +runtime_assert (int expr, int line) +{ + if (!expr) + { + __builtin_printf ("line %i: assertion failed\n", line); + ++nfails; + } +} + +/* Helper macros for run-time testing. */ +#define add(x, y) ((a) + (b)) +#define sadd(x, y) ((a) + (b)) +#define saddl(x, y) ((a) + (b)) +#define saddll(x, y) ((a) + (b)) +#define uadd(x, y) ((a) + (b)) +#define uaddl(x, y) ((a) + (b)) +#define uaddll(x, y) ((a) + (b)) +#define sub(x, y) ((a) - (b)) +#define ssub(x, y) ((a) - (b)) +#define ssubl(x, y) ((a) - (b)) +#define ssubll(x, y) ((a) - (b)) +#define usub(x, y) ((a) - (b)) +#define usubl(x, y) ((a) - (b)) +#define usubll(x, y) ((a) - (b)) +#define mul(x, y) ((a) * (b)) +#define smul(x, y) ((a) * (b)) +#define smull(x, y) ((a) * (b)) +#define smulll(x, y) ((a) * (b)) +#define umul(x, y) ((a) * (b)) +#define umull(x, y) ((a) * (b)) +#define umulll(x, y) ((a) * (b)) + +int main (void) +{ + +#if __cplusplus >= 201103L +# define StaticAssert(expr) static_assert ((expr), #expr) +#elif __STDC_VERSION__ >= 201112L +# define StaticAssert(expr) _Static_assert ((expr), #expr) +#else + /* The following pragma has no effect due to bug 70888 - #pragma + diagnostic ignored -Wlong-long ineffective with __LONG_LONG_MAX__ + in c++98 mode. */ +# pragma GCC diagnostic ignored "-Wlong-long" +# pragma GCC diagnostic ignored "-Wunused-local-typedefs" + +# define CONCAT(a, b) a ## b +# define CAT(a, b) CONCAT (a, b) +# define StaticAssert(expr) \ + typedef int CAT (StaticAssert_, __LINE__) [1 - 2 * !(expr)] +#endif + + /* Make extra effort to prevent constant folding seeing the constant + values of the arguments and optimizing the run-time test into + a constant. */ +#define RuntimeAssert(op, T, U, x, y, vflow) \ + do { \ + volatile T a = (x), b = (y); \ + U c = 0; \ + volatile int vf = __builtin_ ## op ## _overflow (a, b, &c); \ + runtime_assert ((vf == vflow), __LINE__); \ + if (vf == 0) \ + runtime_assert (op (a, b) == c, __LINE__); \ + } while (0) + + /* Verify that each call to the type-generic __builtin_op_overflow(x, y) + yields a constant expression equal to z indicating whether or not + the constant expression (x op y) overflows when evaluated in type T. */ +#if !__cplusplus || __cplusplus >= 201103L + /* Perform both a run-time test followed by a compile-time test. */ +# define G_TEST(op, T, x, y, vflow) \ + RuntimeAssert(op, T, T, x, y, vflow); \ + StaticAssert ((vflow) == __builtin_ ## op ## _overflow ((x), (y), (T*)0)) +#else + /* C++ 98 doesn't permit casts to pointer types to appear in constant + expressions. Only perform the run-time test. */ +# define G_TEST(op, T, x, y, vflow) \ + RuntimeAssert(op, T, T, x, y, vflow) +#endif + + /* Addition. */ + /* G_TEST (add, signed char, 0, 0, 0); */ + /* G_TEST (add, signed char, 0, SCHAR_MAX, 0); */ + G_TEST (add, signed char, 1, SCHAR_MAX, 1); + G_TEST (add, signed char, SCHAR_MAX, SCHAR_MAX, 1); + G_TEST (add, signed char, 0, SCHAR_MIN, 0); + G_TEST (add, signed char, -1, SCHAR_MIN, 1); + + G_TEST (add, short, 0, 0, 0); + G_TEST (add, short, 0, SHRT_MAX, 0); + G_TEST (add, short, 1, SHRT_MAX, 1); + G_TEST (add, short, SHRT_MAX, SHRT_MAX, 1); + G_TEST (add, short, 0, SHRT_MIN, 0); + G_TEST (add, short, -1, SHRT_MIN, 1); + G_TEST (add, short, SHRT_MIN, SHRT_MIN, 1); + + G_TEST (add, int, 0, 0, 0); + G_TEST (add, int, 0, INT_MAX, 0); + G_TEST (add, int, 1, INT_MAX, 1); + G_TEST (add, int, INT_MAX, INT_MAX, 1); + G_TEST (add, int, 0, INT_MIN, 0); + G_TEST (add, int, -1, INT_MIN, 1); + G_TEST (add, int, INT_MIN, INT_MIN, 1); + + G_TEST (add, long, 0, 0, 0); + G_TEST (add, long, 0, LONG_MAX, 0); + G_TEST (add, long, 1, LONG_MAX, 1); + G_TEST (add, long, LONG_MAX, LONG_MAX, 1); + G_TEST (add, long, 0, LONG_MIN, 0); + G_TEST (add, long, -1, LONG_MIN, 1); + G_TEST (add, long, LONG_MIN, LONG_MIN, 1); + + G_TEST (add, long long, 0, 0, 0); + G_TEST (add, long long, 0, LLONG_MAX, 0); + G_TEST (add, long long, 1, LLONG_MAX, 1); + G_TEST (add, long long, LLONG_MAX, LLONG_MAX, 1); + G_TEST (add, long long, 0, LLONG_MIN, 0); + G_TEST (add, long long, -1, LLONG_MIN, 1); + G_TEST (add, long long, LLONG_MIN, LLONG_MIN, 1); + + /* Subtraction */ + G_TEST (sub, unsigned char, 0, 0, 0); + G_TEST (sub, unsigned char, 0, UCHAR_MAX, 1); + G_TEST (sub, unsigned char, 1, UCHAR_MAX, 1); + + G_TEST (sub, unsigned char, UCHAR_MAX, UCHAR_MAX, 0); + G_TEST (sub, unsigned short, 0, 0, 0); + G_TEST (sub, unsigned short, 0, USHRT_MAX, 1); + G_TEST (sub, unsigned short, 1, USHRT_MAX, 1); + G_TEST (sub, unsigned short, USHRT_MAX, USHRT_MAX, 0); + + G_TEST (sub, unsigned, 0, 0, 0); + G_TEST (sub, unsigned, 0, UINT_MAX, 1); + G_TEST (sub, unsigned, 1, UINT_MAX, 1); + G_TEST (sub, unsigned, UINT_MAX, UINT_MAX, 0); + + G_TEST (sub, unsigned long, 0, 0, 0); + G_TEST (sub, unsigned long, 0, ULONG_MAX, 1); + G_TEST (sub, unsigned long, 1, ULONG_MAX, 1); + G_TEST (sub, unsigned long, ULONG_MAX, ULONG_MAX, 0); + + G_TEST (sub, unsigned long long, 0, 0, 0); + G_TEST (sub, unsigned long long, 0, ULLONG_MAX, 1); + G_TEST (sub, unsigned long long, 1, ULLONG_MAX, 1); + G_TEST (sub, unsigned long long, ULLONG_MAX, ULLONG_MAX, 0); + + G_TEST (sub, signed char, 0, 0, 0); + G_TEST (sub, signed char, 0, SCHAR_MAX, 0); + G_TEST (sub, signed char, 1, SCHAR_MAX, 0); + G_TEST (sub, signed char, SCHAR_MAX, SCHAR_MAX, 0); + G_TEST (sub, signed char, SCHAR_MIN, 1, 1); + G_TEST (sub, signed char, 0, SCHAR_MIN, 1); + G_TEST (sub, signed char, -1, SCHAR_MIN, 0); + + G_TEST (sub, short, 0, 0, 0); + G_TEST (sub, short, 0, SHRT_MAX, 0); + G_TEST (sub, short, 1, SHRT_MAX, 0); + G_TEST (sub, short, SHRT_MAX, SHRT_MAX, 0); + G_TEST (sub, short, 0, SHRT_MIN, 1); + G_TEST (sub, short, -1, SHRT_MIN, 0); + G_TEST (sub, short, SHRT_MIN, SHRT_MIN, 0); + + G_TEST (sub, int, 0, 0, 0); + G_TEST (sub, int, 0, INT_MAX, 0); + G_TEST (sub, int, 1, INT_MAX, 0); + G_TEST (sub, int, INT_MAX, INT_MAX, 0); + G_TEST (sub, int, 0, INT_MIN, 1); + G_TEST (sub, int, -1, INT_MIN, 0); + G_TEST (sub, int, INT_MIN, INT_MIN, 0); + + G_TEST (sub, long, 0, 0, 0); + G_TEST (sub, long, 0, LONG_MAX, 0); + G_TEST (sub, long, 1, LONG_MAX, 0); + G_TEST (sub, long, LONG_MAX, LONG_MAX, 0); + G_TEST (sub, long, 0, LONG_MIN, 1); + G_TEST (sub, long, -1, LONG_MIN, 0); + G_TEST (sub, long, LONG_MIN, LONG_MIN, 0); + + G_TEST (sub, long long, 0, 0, 0); + G_TEST (sub, long long, 0, LLONG_MAX, 0); + G_TEST (sub, long long, 1, LLONG_MAX, 0); + G_TEST (sub, long long, LLONG_MAX, LLONG_MAX, 0); + G_TEST (sub, long long, 0, LLONG_MIN, 1); + G_TEST (sub, long long, -1, LLONG_MIN, 0); + G_TEST (sub, long long, LLONG_MIN, LLONG_MIN, 0); + + G_TEST (sub, unsigned char, 0, 0, 0); + G_TEST (sub, unsigned char, 0, UCHAR_MAX, 1); + G_TEST (sub, unsigned char, 1, UCHAR_MAX, 1); + G_TEST (sub, unsigned char, UCHAR_MAX, UCHAR_MAX, 0); + + G_TEST (sub, unsigned short, 0, 0, 0); + G_TEST (sub, unsigned short, 0, USHRT_MAX, 1); + G_TEST (sub, unsigned short, 1, USHRT_MAX, 1); + G_TEST (sub, unsigned short, USHRT_MAX, USHRT_MAX, 0); + + G_TEST (sub, unsigned, 0, 0, 0); + G_TEST (sub, unsigned, 0, UINT_MAX, 1); + G_TEST (sub, unsigned, 1, UINT_MAX, 1); + G_TEST (sub, unsigned, UINT_MAX, UINT_MAX, 0); + + G_TEST (sub, unsigned long, 0, 0, 0); + G_TEST (sub, unsigned long, 0, ULONG_MAX, 1); + G_TEST (sub, unsigned long, 1, ULONG_MAX, 1); + G_TEST (sub, unsigned long, ULONG_MAX, ULONG_MAX, 0); + + G_TEST (sub, unsigned long long, 0, 0, 0); + G_TEST (sub, unsigned long long, 0, ULLONG_MAX, 1); + G_TEST (sub, unsigned long long, 1, ULLONG_MAX, 1); + G_TEST (sub, unsigned long long, ULLONG_MAX, ULLONG_MAX, 0); + + /* Multiplication. */ + G_TEST (mul, unsigned char, 0, 0, 0); + G_TEST (mul, unsigned char, 0, UCHAR_MAX, 0); + G_TEST (mul, unsigned char, 1, UCHAR_MAX, 0); + G_TEST (mul, unsigned char, 2, UCHAR_MAX, 1); + G_TEST (mul, unsigned char, UCHAR_MAX, UCHAR_MAX, 1); + + G_TEST (mul, unsigned short, 0, 0, 0); + G_TEST (mul, unsigned short, 0, USHRT_MAX, 0); + G_TEST (mul, unsigned short, 1, USHRT_MAX, 0); + G_TEST (mul, unsigned short, USHRT_MAX, 2, 1); + G_TEST (mul, unsigned short, USHRT_MAX, USHRT_MAX, 1); + + G_TEST (mul, unsigned, 0, 0, 0); + G_TEST (mul, unsigned, 0, UINT_MAX, 0); + G_TEST (mul, unsigned, 1, UINT_MAX, 0); + G_TEST (mul, unsigned, 2, UINT_MAX, 1); + G_TEST (mul, unsigned, UINT_MAX, UINT_MAX, 1); + + G_TEST (mul, unsigned long, 0, 0, 0); + G_TEST (mul, unsigned long, 0, ULONG_MAX, 0); + G_TEST (mul, unsigned long, 1, ULONG_MAX, 0); + G_TEST (mul, unsigned long, 2, ULONG_MAX, 1); + G_TEST (mul, unsigned long, ULONG_MAX, ULONG_MAX, 1); + + G_TEST (mul, unsigned long long, 0, 0, 0); + G_TEST (mul, unsigned long long, 0, ULLONG_MAX, 0); + G_TEST (mul, unsigned long long, 1, ULLONG_MAX, 0); + G_TEST (mul, unsigned long long, 2, ULLONG_MAX, 1); + G_TEST (mul, unsigned long long, ULLONG_MAX, ULLONG_MAX, 1); + + G_TEST (mul, signed char, 0, 0, 0); + G_TEST (mul, signed char, 0, SCHAR_MAX, 0); + G_TEST (mul, signed char, 1, SCHAR_MAX, 0); + G_TEST (mul, signed char, SCHAR_MAX, SCHAR_MAX, 1); + G_TEST (mul, signed char, SCHAR_MIN, 1, 0); + G_TEST (mul, signed char, 0, SCHAR_MIN, 0); + G_TEST (mul, signed char, -1, SCHAR_MIN, 1); + + G_TEST (mul, short, 0, 0, 0); + G_TEST (mul, short, 0, SHRT_MAX, 0); + G_TEST (mul, short, 1, SHRT_MAX, 0); + G_TEST (mul, short, SHRT_MAX, SHRT_MAX, 1); + G_TEST (mul, short, 0, SHRT_MIN, 0); + G_TEST (mul, short, -1, SHRT_MIN, 1); + G_TEST (mul, short, SHRT_MIN, SHRT_MIN, 1); + + G_TEST (mul, int, 0, 0, 0); + G_TEST (mul, int, 0, INT_MAX, 0); + G_TEST (mul, int, 1, INT_MAX, 0); + G_TEST (mul, int, INT_MAX, INT_MAX, 1); + G_TEST (mul, int, 0, INT_MIN, 0); + G_TEST (mul, int, -1, INT_MIN, 1); + G_TEST (mul, int, INT_MIN, INT_MIN, 1); + + G_TEST (mul, long, 0, 0, 0); + G_TEST (mul, long, 0, LONG_MAX, 0); + G_TEST (mul, long, 1, LONG_MAX, 0); + G_TEST (mul, long, LONG_MAX, LONG_MAX, 1); + G_TEST (mul, long, 0, LONG_MIN, 0); + G_TEST (mul, long, -1, LONG_MIN, 1); + G_TEST (mul, long, LONG_MIN, LONG_MIN, 1); + + G_TEST (mul, long long, 0, 0, 0); + G_TEST (mul, long long, 0, LLONG_MAX, 0); + G_TEST (mul, long long, 1, LLONG_MAX, 0); + G_TEST (mul, long long, LLONG_MAX, LLONG_MAX, 1); + G_TEST (mul, long long, 0, LLONG_MIN, 0); + G_TEST (mul, long long, -1, LLONG_MIN, 1); + G_TEST (mul, long long, LLONG_MIN, LLONG_MIN, 1); + + G_TEST (mul, unsigned char, 0, 0, 0); + G_TEST (mul, unsigned char, 0, UCHAR_MAX, 0); + G_TEST (mul, unsigned char, 1, UCHAR_MAX, 0); + G_TEST (mul, unsigned char, UCHAR_MAX, UCHAR_MAX, 1); + + G_TEST (mul, unsigned short, 0, 0, 0); + G_TEST (mul, unsigned short, 0, USHRT_MAX, 0); + G_TEST (mul, unsigned short, 1, USHRT_MAX, 0); + G_TEST (mul, unsigned short, USHRT_MAX, USHRT_MAX, 1); + + G_TEST (mul, unsigned, 0, 0, 0); + G_TEST (mul, unsigned, 0, UINT_MAX, 0); + G_TEST (mul, unsigned, 1, UINT_MAX, 0); + G_TEST (mul, unsigned, UINT_MAX, UINT_MAX, 1); + + G_TEST (mul, unsigned long, 0, 0, 0); + G_TEST (mul, unsigned long, 0, ULONG_MAX, 0); + G_TEST (mul, unsigned long, 1, ULONG_MAX, 0); + G_TEST (mul, unsigned long, ULONG_MAX, ULONG_MAX, 1); + + G_TEST (mul, unsigned long long, 0, 0, 0); + G_TEST (mul, unsigned long long, 0, ULLONG_MAX, 0); + G_TEST (mul, unsigned long long, 1, ULLONG_MAX, 0); + G_TEST (mul, unsigned long long, ULLONG_MAX, ULLONG_MAX, 1); + + /* Verify that each call to the type-specific __builtin_op_overflow + yields a constant expression equal to z indicating whether or not + the constant expression (x op y) overflows. The type-specific + overloads detect overflow after arithmetic promotions and so unlike + the type-generic overloads cannot detect overflow in char or short + types. */ + +#if !__cplusplus || __cplusplus >= 201103L + /* Perform both a run-time test followed by a compile-time test. */ +# define T_TEST(op, T, x, y, vflow) \ + RuntimeAssert(op, T, decltype ((x) + (y)), x, y, vflow); \ + StaticAssert ((vflow) == __builtin_ ## op ## _overflow ((x), (y), \ + (decltype ((x) + (y))*)0)) +#else + /* C++ 98 doesn't permit casts to pointer types to appear in constant + expressions. Only perform the run-time test. */ +# define T_TEST(op, T, x, y, vflow) \ + RuntimeAssert (op, T, __typeof__ ((x) + (y)), x, y, vflow) +#endif + + /* Signed int addition. */ + T_TEST (sadd, signed char, 0, 0, 0); + T_TEST (sadd, signed char, 0, SCHAR_MAX, 0); + T_TEST (sadd, signed char, 1, SCHAR_MAX, 0); + T_TEST (sadd, signed char, SCHAR_MAX, SCHAR_MAX, 0); + T_TEST (sadd, signed char, 0, SCHAR_MIN, 0); + T_TEST (sadd, signed char, -1, SCHAR_MIN, 0); + + T_TEST (sadd, short, 0, 0, 0); + T_TEST (sadd, short, 0, SHRT_MAX, 0); + T_TEST (sadd, short, 1, SHRT_MAX, 0); + T_TEST (sadd, short, SHRT_MAX, SHRT_MAX, 0); + T_TEST (sadd, short, 0, SHRT_MIN, 0); + T_TEST (sadd, short, -1, SHRT_MIN, 0); + T_TEST (sadd, short, SHRT_MIN, SHRT_MIN, 0); + + T_TEST (sadd, int, 0, 0, 0); + T_TEST (sadd, int, 0, INT_MAX, 0); + T_TEST (sadd, int, 1, INT_MAX, 1); + T_TEST (sadd, int, INT_MAX, INT_MAX, 1); + T_TEST (sadd, int, 0, INT_MIN, 0); + T_TEST (sadd, int, -1, INT_MIN, 1); + T_TEST (sadd, int, INT_MIN, INT_MIN, 1); + + /* Signed long addition. */ + T_TEST (saddl, long, 0L, 0L, 0); + T_TEST (saddl, long, 0L, LONG_MAX, 0); + T_TEST (saddl, long, 1L, LONG_MAX, 1); + T_TEST (saddl, long, LONG_MAX, LONG_MAX, 1); + T_TEST (saddl, long, 0L, LONG_MIN, 0); + T_TEST (saddl, long, -1L, LONG_MIN, 1); + T_TEST (saddl, long, LONG_MIN, LONG_MIN, 1); + + T_TEST (saddll, long long, 0LL, 0LL, 0); + T_TEST (saddll, long long, 0LL, LLONG_MAX, 0); + T_TEST (saddll, long long, 1LL, LLONG_MAX, 1); + T_TEST (saddll, long long, LLONG_MAX, LLONG_MAX, 1); + T_TEST (saddll, long long, 0LL, LLONG_MIN, 0); + T_TEST (saddll, long long, -1LL, LLONG_MIN, 1); + T_TEST (saddll, long long, LLONG_MIN, LLONG_MIN, 1); + + /* Unsigned int addition. */ + T_TEST (uadd, unsigned char, 0U, 0U, 0); + T_TEST (uadd, unsigned char, 0U, UCHAR_MAX, 0); + T_TEST (uadd, unsigned char, 1U, UCHAR_MAX, 0); + T_TEST (uadd, unsigned char, UCHAR_MAX, UCHAR_MAX, 0); + + T_TEST (uadd, unsigned short, 0U, 0U, 0); + T_TEST (uadd, unsigned short, 0U, USHRT_MAX, 0); + T_TEST (uadd, unsigned short, 1U, USHRT_MAX, 0); + T_TEST (uadd, unsigned short, USHRT_MAX, USHRT_MAX, 0); + + T_TEST (uadd, unsigned, 0U, 0U, 0); + T_TEST (uadd, unsigned, 0U, UINT_MAX, 0); + T_TEST (uadd, unsigned, 1U, UINT_MAX, 1); + T_TEST (uadd, unsigned, UINT_MAX, UINT_MAX, 1); + + /* Unsigned long addition. */ + T_TEST (uaddl, unsigned long, 0UL, 0UL, 0); + T_TEST (uaddl, unsigned long, 0UL, ULONG_MAX, 0); + T_TEST (uaddl, unsigned long, 1UL, ULONG_MAX, 1); + T_TEST (uaddl, unsigned long, ULONG_MAX, ULONG_MAX, 1); + + T_TEST (uaddll, unsigned long long, 0ULL, 0ULL, 0); + T_TEST (uaddll, unsigned long long, 0ULL, ULLONG_MAX, 0); + T_TEST (uaddll, unsigned long long, 1ULL, ULLONG_MAX, 1); + T_TEST (uaddll, unsigned long long, ULLONG_MAX, ULLONG_MAX, 1); + + /* Signed int subtraction. */ + T_TEST (ssub, signed char, 0, 0, 0); + T_TEST (ssub, signed char, 0, SCHAR_MAX, 0); + T_TEST (ssub, signed char, 1, SCHAR_MAX, 0); + T_TEST (ssub, signed char, SCHAR_MAX, SCHAR_MAX, 0); + T_TEST (ssub, signed char, 0, SCHAR_MIN, 0); + T_TEST (ssub, signed char, -1, SCHAR_MIN, 0); + + T_TEST (ssub, short, 0, 0, 0); + T_TEST (ssub, short, 0, SHRT_MAX, 0); + T_TEST (ssub, short, 1, SHRT_MAX, 0); + T_TEST (ssub, short, SHRT_MAX, SHRT_MAX, 0); + T_TEST (ssub, short, 0, SHRT_MIN, 0); + T_TEST (ssub, short, -1, SHRT_MIN, 0); + T_TEST (ssub, short, SHRT_MIN, SHRT_MIN, 0); + + T_TEST (ssub, int, 0, 0, 0); + T_TEST (ssub, int, 0, INT_MAX, 0); + T_TEST (ssub, int, 1, INT_MAX, 0); + T_TEST (ssub, int, INT_MAX, INT_MAX, 0); + T_TEST (ssub, int, 0, INT_MIN, 1); + T_TEST (ssub, int, -1, INT_MIN, 0); + T_TEST (ssub, int, INT_MIN, INT_MIN, 0); + + /* Signed long subtraction. */ + T_TEST (ssubl, long, 0L, 0L, 0); + T_TEST (ssubl, long, 0L, LONG_MAX, 0); + T_TEST (ssubl, long, 1L, LONG_MAX, 0); + T_TEST (ssubl, long, LONG_MAX, LONG_MAX, 0); + T_TEST (ssubl, long, 0L, LONG_MIN, 1); + T_TEST (ssubl, long, -1L, LONG_MIN, 0); + T_TEST (ssubl, long, LONG_MIN, LONG_MIN, 0); + + /* Signed long long subtraction. */ + T_TEST (ssubll, long long, 0LL, 0LL, 0); + T_TEST (ssubll, long long, 0LL, LLONG_MAX, 0); + T_TEST (ssubll, long long, 1LL, LLONG_MAX, 0); + T_TEST (ssubll, long long, LLONG_MAX, LLONG_MAX, 0); + T_TEST (ssubll, long long, 0LL, LLONG_MIN, 1); + T_TEST (ssubll, long long, -1LL, LLONG_MIN, 0); + T_TEST (ssubll, long long, LLONG_MIN, LLONG_MIN, 0); + + /* Unsigned int subtraction. */ + T_TEST (usub, unsigned char, 0U, 0U, 0); + T_TEST (usub, unsigned char, 0U, UCHAR_MAX, 1); + T_TEST (usub, unsigned char, 1U, UCHAR_MAX, 1); + T_TEST (usub, unsigned char, UCHAR_MAX, UCHAR_MAX, 0); + + T_TEST (usub, unsigned short, 0U, 0U, 0); + T_TEST (usub, unsigned short, 0U, USHRT_MAX, 1); + T_TEST (usub, unsigned short, 1U, USHRT_MAX, 1); + T_TEST (usub, unsigned short, USHRT_MAX, USHRT_MAX, 0); + + T_TEST (usub, unsigned, 0U, 0U, 0); + T_TEST (usub, unsigned, 0U, UINT_MAX, 1); + T_TEST (usub, unsigned, 1U, UINT_MAX, 1); + T_TEST (usub, unsigned, UINT_MAX, UINT_MAX, 0); + + /* Unsigned long subtraction. */ + T_TEST (usubl, unsigned long, 0UL, 0UL, 0); + T_TEST (usubl, unsigned long, 0UL, ULONG_MAX, 1); + T_TEST (usubl, unsigned long, 1UL, ULONG_MAX, 1); + T_TEST (usubl, unsigned long, ULONG_MAX, ULONG_MAX, 0); + + /* Unsigned long long subtraction. */ + T_TEST (usubll, unsigned long long, 0ULL, 0ULL, 0); + T_TEST (usubll, unsigned long long, 0ULL, ULLONG_MAX, 1); + T_TEST (usubll, unsigned long long, 1ULL, ULLONG_MAX, 1); + T_TEST (usubll, unsigned long long, ULLONG_MAX, ULLONG_MAX, 0); + + return 0; +} diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-arith-overflow.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-arith-overflow.C new file mode 100644 index 0000000..93e26e5 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-arith-overflow.C @@ -0,0 +1,115 @@ +// PR c++/70507 - integer overflow builtins not constant expressions +// { dg-do compile { target c++11 } } + +#define SCHAR_MAX __SCHAR_MAX__ +#define SHRT_MAX __SHRT_MAX__ +#define INT_MAX __INT_MAX__ +#define LONG_MAX __LONG_MAX__ +#define LLONG_MAX __LONG_LONG_MAX__ + +#define SCHAR_MIN (-__SCHAR_MAX__ - 1) +#define SHRT_MIN (-__SHRT_MAX__ - 1) +#define INT_MIN (-__INT_MAX__ - 1) +#define LONG_MIN (-__LONG_MAX__ - 1) +#define LLONG_MIN (-__LONG_LONG_MAX__ - 1) + +#define UCHAR_MAX (SCHAR_MAX * 2U + 1) +#define USHRT_MAX (SHRT_MAX * 2U + 1) +#define UINT_MAX (INT_MAX * 2U + 1) +#define ULONG_MAX (LONG_MAX * 2LU + 1) +#define ULLONG_MAX (LLONG_MAX * 2LLU + 1) + +#define USCHAR_MIN (-__USCHAR_MAX__ - 1) +#define USHRT_MIN (-__USHRT_MAX__ - 1) +#define UINT_MIN (-__UINT_MAX__ - 1) +#define ULONG_MIN (-__ULONG_MAX__ - 1) +#define ULLONG_MIN (-__ULONG_LONG_MAX__ - 1) + +#define Assert(expr) static_assert ((expr), #expr) + +template <class T> +constexpr T add (T x, T y, T z = T ()) +{ + return __builtin_add_overflow (x, y, &z) ? 0 : z; +} + +template <class T> +constexpr T sub (T x, T y, T z = T ()) +{ + return __builtin_sub_overflow (x, y, &z) ? 0 : z; +} + +template <class T> +constexpr T mul (T x, T y, T z = T ()) +{ + return __builtin_mul_overflow (x, y, &z) ? 0 : z; +} + +#define TEST_ADD(T, x, y, z) Assert (z == add<T>(x, y)) +#define TEST_SUB(T, x, y, z) Assert (z == sub<T>(x, y)) +#define TEST_MUL(T, x, y, z) Assert (z == mul<T>(x, y)) + + +TEST_ADD (signed char, 0, 0, 0); +TEST_ADD (signed char, 0, SCHAR_MAX, SCHAR_MAX); +TEST_ADD (signed char, 1, SCHAR_MAX, 0); // overflow +TEST_ADD (signed char, SCHAR_MAX, SCHAR_MAX, 0); // overflow +TEST_ADD (signed char, 0, SCHAR_MIN, SCHAR_MIN); +TEST_ADD (signed char, -1, SCHAR_MIN, 0); // overflow + +TEST_ADD (short, 0, 0, 0); +TEST_ADD (short, 0, SHRT_MAX, SHRT_MAX); +TEST_ADD (short, 1, SHRT_MAX, 0); // overflow +TEST_ADD (short, SHRT_MAX, SHRT_MAX, 0); // overflow +TEST_ADD (short, 0, SHRT_MIN, SHRT_MIN); +TEST_ADD (short, -1, SHRT_MIN, 0); // overflow +TEST_ADD (short, SHRT_MIN, SHRT_MIN, 0); // overflow + +TEST_ADD (int, 0, 0, 0); +TEST_ADD (int, 0, INT_MAX, INT_MAX); +TEST_ADD (int, 1, INT_MAX, 0); // overflow +TEST_ADD (int, INT_MAX, INT_MAX, 0); // overflow +TEST_ADD (int, 0, INT_MIN, INT_MIN); +TEST_ADD (int, -1, INT_MIN, 0); // overflow +TEST_ADD (int, INT_MIN, INT_MIN, 0); // overflow + +TEST_ADD (long, 0, 0, 0); +TEST_ADD (long, 0, LONG_MAX, LONG_MAX); +TEST_ADD (long, 1, LONG_MAX, 0); // overflow +TEST_ADD (long, LONG_MAX, LONG_MAX, 0); // overflow +TEST_ADD (long, 0, LONG_MIN, LONG_MIN); +TEST_ADD (long, -1, LONG_MIN, 0); // overflow +TEST_ADD (long, LONG_MIN, LONG_MIN, 0); // overflow + +TEST_ADD (long long, 0, 0, 0); +TEST_ADD (long long, 0, LLONG_MAX, LLONG_MAX); +TEST_ADD (long long, 1, LLONG_MAX, 0); // overflow +TEST_ADD (long long, LLONG_MAX, LLONG_MAX, 0); // overflow +TEST_ADD (long long, 0, LLONG_MIN, LLONG_MIN); +TEST_ADD (long long, -1, LLONG_MIN, 0); // overflow +TEST_ADD (long long, LLONG_MIN, LLONG_MIN, 0); // overflow + +TEST_ADD (unsigned char, 0, 0, 0); +TEST_ADD (unsigned char, 0, UCHAR_MAX, UCHAR_MAX); +TEST_ADD (unsigned char, 1, UCHAR_MAX, 0); // overflow + +TEST_ADD (unsigned char, UCHAR_MAX, UCHAR_MAX, 0); // overflow +TEST_ADD (unsigned short, 0, 0, 0); +TEST_ADD (unsigned short, 0, USHRT_MAX, USHRT_MAX); +TEST_ADD (unsigned short, 1, USHRT_MAX, 0); // overflow +TEST_ADD (unsigned short, USHRT_MAX, USHRT_MAX, 0); // overflow + +TEST_ADD (unsigned, 0, 0, 0); +TEST_ADD (unsigned, 0, UINT_MAX, UINT_MAX); +TEST_ADD (unsigned, 1, UINT_MAX, 0); // overflow +TEST_ADD (unsigned, UINT_MAX, UINT_MAX, 0); // overflow + +TEST_ADD (unsigned long, 0, 0, 0); +TEST_ADD (unsigned long, 0, ULONG_MAX, ULONG_MAX); +TEST_ADD (unsigned long, 1, ULONG_MAX, 0); // overflow +TEST_ADD (unsigned long, ULONG_MAX, ULONG_MAX, 0); // overflow + +TEST_ADD (unsigned long long, 0, 0, 0); +TEST_ADD (unsigned long long, 0, ULLONG_MAX, ULLONG_MAX); +TEST_ADD (unsigned long long, 1, ULLONG_MAX, 0); // overflow +TEST_ADD (unsigned long long, ULLONG_MAX, ULLONG_MAX, 0); // overflow ^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH] integer overflow checking builtins in constant expressions 2016-06-02 3:11 ` Martin Sebor @ 2016-06-02 7:23 ` Jakub Jelinek 2016-06-02 7:34 ` Jakub Jelinek 2016-06-02 23:21 ` Martin Sebor 0 siblings, 2 replies; 36+ messages in thread From: Jakub Jelinek @ 2016-06-02 7:23 UTC (permalink / raw) To: Martin Sebor; +Cc: Gcc Patch List, Jason Merrill, Joseph S. Myers On Wed, Jun 01, 2016 at 09:10:52PM -0600, Martin Sebor wrote: > @@ -7957,8 +7957,8 @@ fold_builtin_arith_overflow (location_t loc, enum built_in_function fcode, > tree arg0, tree arg1, tree arg2) > { > enum internal_fn ifn = IFN_LAST; > - tree type = TREE_TYPE (TREE_TYPE (arg2)); > - tree mem_arg2 = build_fold_indirect_ref_loc (loc, arg2); > + enum tree_code opcode; > + > switch (fcode) > { > case BUILT_IN_ADD_OVERFLOW: > @@ -7969,6 +7969,7 @@ fold_builtin_arith_overflow (location_t loc, enum built_in_function fcode, > case BUILT_IN_UADDL_OVERFLOW: > case BUILT_IN_UADDLL_OVERFLOW: > ifn = IFN_ADD_OVERFLOW; > + opcode = PLUS_EXPR; > break; > case BUILT_IN_SUB_OVERFLOW: > case BUILT_IN_SSUB_OVERFLOW: > @@ -7978,6 +7979,7 @@ fold_builtin_arith_overflow (location_t loc, enum built_in_function fcode, > case BUILT_IN_USUBL_OVERFLOW: > case BUILT_IN_USUBLL_OVERFLOW: > ifn = IFN_SUB_OVERFLOW; > + opcode = MINUS_EXPR; > break; > case BUILT_IN_MUL_OVERFLOW: > case BUILT_IN_SMUL_OVERFLOW: > @@ -7987,10 +7989,35 @@ fold_builtin_arith_overflow (location_t loc, enum built_in_function fcode, > case BUILT_IN_UMULL_OVERFLOW: > case BUILT_IN_UMULLL_OVERFLOW: > ifn = IFN_MUL_OVERFLOW; > + opcode = MULT_EXPR; > break; > default: > gcc_unreachable (); > } > + > + /* For the "generic" overloads, the first two arguments can have different > + types and the last argument determines the target type to use to check > + for overflow. The arguments of the other overloads all have the same > + type. */ > + tree type = TREE_TYPE (TREE_TYPE (arg2)); > + bool isnullp = integer_zerop (arg2); > + > + /* When the last argument is a null pointer and the first two arguments > + are constant, attempt to fold the built-in call into a constant > + expression indicating whether or not it detected an overflow but > + without storing the result. */ > + if (isnullp > + && TREE_CODE (arg0) == INTEGER_CST && TREE_CODE (arg1) == INTEGER_CST) Doesn't this mean you allow NULL arg2 even for BUILT_IN_ADDL_OVERFLOW and similar clang builtins? I'd set opcode to ERROR_MARK first, and then do: switch (fcode) { case BUILT_IN_ADD_OVERFLOW: opcode = PLUS_EXPR; /* FALLTHROUGH */ case BUILT_IN_SADD_OVERFLOW: case BUILT_IN_SADDL_OVERFLOW: case BUILT_IN_SADDLL_OVERFLOW: case BUILT_IN_UADD_OVERFLOW: case BUILT_IN_UADDL_OVERFLOW: case BUILT_IN_UADDLL_OVERFLOW: ifn = IFN_ADD_OVERFLOW; break; and similarly, then bool isnullp = opcode != ERROR_MARK ? integer_zerop (arg2) : false; or so. > + { > + /* Perform the computation in the target type and check for overflow. */ > + arg0 = fold_convert (type, arg0); > + arg1 = fold_convert (type, arg1); > + > + if (tree result = size_binop_loc (loc, opcode, arg0, arg1)) > + return TREE_OVERFLOW (result) ? build_one_cst (boolean_type_node) > + : build_zero_cst (boolean_type_node); This is wrong, it is not the computation of overflow that is intended. The documentation says that we compute (infinite_precision_signed_type) arg0 op (infinite_precision_signed_type) arg1 and then cast it to type, extend again to infinite_precision_signed_type and compare. And we have a helper function for that already. Furthermore, we have boolean_true_node and boolean_false_node. Thus, I'd expect here: return (arith_overflowed_p (opcode, type, arg0, arg1) ? boolean_true_node : boolean_false_node); > + tree arg0 = cxx_eval_constant_expression (ctx, CALL_EXPR_ARG (t, 0), lval, > + non_constant_p, overflow_p); > + tree arg1 = cxx_eval_constant_expression (ctx, CALL_EXPR_ARG (t, 1), lval, > + non_constant_p, overflow_p); > + > + if (TREE_CODE (arg0) == INTEGER_CST && TREE_CODE (arg1) == INTEGER_CST) > + { > + if (tree result = size_binop_loc (EXPR_LOC_OR_LOC (t, input_location), > + opcode, arg0, arg1)) > + { > + if (TREE_OVERFLOW (result)) > + { > + /* Reset TREE_OVERFLOW to avoid warnings for the overflow. */ > + TREE_OVERFLOW (result) = 0; Again, this is wrong, it should have used arith_overflowed_p. > @@ -1270,18 +1332,8 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t, > bool depth_ok; > > if (fun == NULL_TREE) > - switch (CALL_EXPR_IFN (t)) > - { > - case IFN_UBSAN_NULL: > - case IFN_UBSAN_BOUNDS: > - case IFN_UBSAN_VPTR: > - return void_node; > - default: > - if (!ctx->quiet) > - error_at (loc, "call to internal function"); > - *non_constant_p = true; > - return t; > - } > + return cxx_eval_internal_function (ctx, t, lval, > + non_constant_p, overflow_p); > > if (TREE_CODE (fun) != FUNCTION_DECL) > { > diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c > index 04702ee..f9db199 100644 > --- a/gcc/cp/tree.c > +++ b/gcc/cp/tree.c > @@ -352,6 +352,32 @@ builtin_valid_in_constant_expr_p (const_tree decl) > case BUILT_IN_FUNCTION: > case BUILT_IN_LINE: > > + /* The following built-ins are valid in constant expressions > + when their arguments are. */ > + case BUILT_IN_ADD_OVERFLOW: > + case BUILT_IN_SADD_OVERFLOW: > + case BUILT_IN_SADDL_OVERFLOW: > + case BUILT_IN_SADDLL_OVERFLOW: > + case BUILT_IN_UADD_OVERFLOW: > + case BUILT_IN_UADDL_OVERFLOW: > + case BUILT_IN_UADDLL_OVERFLOW: > + > + case BUILT_IN_SUB_OVERFLOW: > + case BUILT_IN_SSUB_OVERFLOW: > + case BUILT_IN_SSUBL_OVERFLOW: > + case BUILT_IN_SSUBLL_OVERFLOW: > + case BUILT_IN_USUB_OVERFLOW: > + case BUILT_IN_USUBL_OVERFLOW: > + case BUILT_IN_USUBLL_OVERFLOW: > + > + case BUILT_IN_MUL_OVERFLOW: > + case BUILT_IN_SMUL_OVERFLOW: > + case BUILT_IN_SMULL_OVERFLOW: > + case BUILT_IN_SMULLL_OVERFLOW: > + case BUILT_IN_UMUL_OVERFLOW: > + case BUILT_IN_UMULL_OVERFLOW: > + case BUILT_IN_UMULLL_OVERFLOW: > + > /* These have constant results even if their operands are > non-constant. */ > case BUILT_IN_CONSTANT_P: > diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi > index 2d4f028..00435f7 100644 > --- a/gcc/doc/extend.texi > +++ b/gcc/doc/extend.texi > @@ -9737,7 +9737,10 @@ compiler may also ignore this parameter. > @section Built-in Functions to Perform Arithmetic with Overflow Checking > > The following built-in functions allow performing simple arithmetic operations > -together with checking whether the operations overflowed. > +together with checking whether the operations overflowed. The first of the > +functions accepts either a pointer to an integer object or a null pointer to > +integer as the last argument. The rest require a pointer to an object of > +the specified type as the last argument. > > @deftypefn {Built-in Function} bool __builtin_add_overflow (@var{type1} a, @var{type2} b, @var{type3} *res) > @deftypefnx {Built-in Function} bool __builtin_sadd_overflow (int a, int b, int *res) > @@ -9747,21 +9750,50 @@ together with checking whether the operations overflowed. > @deftypefnx {Built-in Function} bool __builtin_uaddl_overflow (unsigned long int a, unsigned long int b, unsigned long int *res) > @deftypefnx {Built-in Function} bool __builtin_uaddll_overflow (unsigned long long int a, unsigned long long int b, unsigned long int *res) > > -These built-in functions promote the first two operands into infinite precision signed > -type and perform addition on those promoted operands. The result is then > -cast to the type the third pointer argument points to and stored there. > -If the stored result is equal to the infinite precision result, the built-in > -functions return false, otherwise they return true. As the addition is > -performed in infinite signed precision, these built-in functions have fully defined > -behavior for all argument values. > - > -The first built-in function allows arbitrary integral types for operands and > -the result type must be pointer to some integer type, the rest of the built-in > -functions have explicit integer types. > +These built-in functions promote the first two operands into infinite precision > +signed type and perform addition on those promoted operands. The result is then > +converted to the type the third pointer argument points to, and for the first > +function when the pointer is not null, stored there. If the converted result > +is equal to the infinite precision result, the built-in functions return > +@code{false}, otherwise they return @code{true} to indicate that an overflow > +has been detected. Because the addition is performed in infinite precision, > +these built-in functions have fully defined behavior for all argument values > +and integer types. > + > +The first type-generic built-in function allows arbitrary integer types as > +the first two arguments and requires that a pointer to some possibly distinct > +integer type be passed to it as the third argument. The pointed-to type is > +then used to determine the overflow. As a result, this built-in function > +can be used to detect overflow in any arbitrary integer type, including > +@code{char} and @code{short}. The remaining built-in functions take > +arguments of explicit integer types and make it possible to determine > +overflow only in the ranges of those types. Since the only provided forms > +of these latter built-in functions are for the signed and unsigned variants > +of types @code{int}, @code{long}, and @code{long long}, they cannot be used > +to determine overflow in other integer types. > + > +To enable the efficient integer overflow detection at translation-time, > +in C and C++ 11 and later programs (but not in C++ 98), the first built-in AFAIK the docs use either C++98, C++11 (without space), or ISO/IEC 14882:2011 etc., but not C++ 98 or C++ 11. Also, perhaps just a documentation thing, it would be good to clarify the NULL last argument. From the POV of generating efficient code, I think we should say something that the last argument (for the GNU builtins) must be either a pointer to a valid object, or NULL/nullptr constant cast expression cast to a pointer type, but nothing else. That is actually what your patch implements. But, I'd like to make sure that int *p = NULL; if (__builtin_add_overflow (a, b, p)) ... is actually not valid, otherwise we unnecessarily pessimize many of the GNU style calls (those that don't pass &var), where instead of tem = ADD_OVERFLOW (a, b); *p = REALPART_EXPR (tem); ovf = IMAGPART_EXPR (tem); we'd need to emit instead tem = ADD_OVERFLOW (a, b); ovf = IMAGPART_EXPR (tem); if (p != NULL) *p = REALPART_EXPR (tem); Jakub ^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH] integer overflow checking builtins in constant expressions 2016-06-02 7:23 ` Jakub Jelinek @ 2016-06-02 7:34 ` Jakub Jelinek 2016-06-03 15:07 ` Martin Sebor 2016-06-02 23:21 ` Martin Sebor 1 sibling, 1 reply; 36+ messages in thread From: Jakub Jelinek @ 2016-06-02 7:34 UTC (permalink / raw) To: Martin Sebor; +Cc: Gcc Patch List, Jason Merrill, Joseph S. Myers On Thu, Jun 02, 2016 at 09:23:16AM +0200, Jakub Jelinek wrote: > Also, perhaps just a documentation thing, it would be good to clarify the > NULL last argument. From the POV of generating efficient code, I think we > should say something that the last argument (for the GNU builtins) must be > either a pointer to a valid object, or NULL/nullptr constant cast expression > cast to a pointer type, but nothing else. That is actually what your patch > implements. But, I'd like to make sure that > int *p = NULL; > if (__builtin_add_overflow (a, b, p)) > ... > is actually not valid, otherwise we unnecessarily pessimize many of the GNU > style calls (those that don't pass &var), where instead of > tem = ADD_OVERFLOW (a, b); > *p = REALPART_EXPR (tem); > ovf = IMAGPART_EXPR (tem); > we'd need to emit instead > tem = ADD_OVERFLOW (a, b); > ovf = IMAGPART_EXPR (tem); > if (p != NULL) > *p = REALPART_EXPR (tem); Though, perhaps that is too ugly, that it has different semantics for __builtin_add_overflow (a, b, (int *) NULL) and for int *p = NULL; __builtin_add_overflow (a, b, p) Maybe the cleanest would be to just add 3 extra builtins, again, typegeneric, __builtin_{add,sub,mul}_overflow_p where either the arguments would be instead of integertype1, integertype2, integertype3 * rather integertype1, integertype2, integertype3 and we'd only care about the type, not value, of the last argument, so use it like __builtin_add_overflow_p (a, b, (__typeof ((a) + (b))) 0) or handle those 3 similarly to __builtin_va_arg, and use __builtin_add_overflow_p (a, b, int); I think I prefer the former though. Jakub ^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH] integer overflow checking builtins in constant expressions 2016-06-02 7:34 ` Jakub Jelinek @ 2016-06-03 15:07 ` Martin Sebor 2016-06-03 15:23 ` Jakub Jelinek 0 siblings, 1 reply; 36+ messages in thread From: Martin Sebor @ 2016-06-03 15:07 UTC (permalink / raw) To: Jakub Jelinek; +Cc: Gcc Patch List, Jason Merrill, Joseph S. Myers On 06/02/2016 01:34 AM, Jakub Jelinek wrote: > On Thu, Jun 02, 2016 at 09:23:16AM +0200, Jakub Jelinek wrote: >> Also, perhaps just a documentation thing, it would be good to clarify the >> NULL last argument. From the POV of generating efficient code, I think we >> should say something that the last argument (for the GNU builtins) must be >> either a pointer to a valid object, or NULL/nullptr constant cast expression >> cast to a pointer type, but nothing else. That is actually what your patch >> implements. But, I'd like to make sure that >> int *p = NULL; >> if (__builtin_add_overflow (a, b, p)) >> ... >> is actually not valid, otherwise we unnecessarily pessimize many of the GNU >> style calls (those that don't pass &var), where instead of >> tem = ADD_OVERFLOW (a, b); >> *p = REALPART_EXPR (tem); >> ovf = IMAGPART_EXPR (tem); >> we'd need to emit instead >> tem = ADD_OVERFLOW (a, b); >> ovf = IMAGPART_EXPR (tem); >> if (p != NULL) >> *p = REALPART_EXPR (tem); > > Though, perhaps that is too ugly, that it has different semantics for > __builtin_add_overflow (a, b, (int *) NULL) > and for > int *p = NULL; > __builtin_add_overflow (a, b, p) > Maybe the cleanest would be to just add 3 extra builtins, again, > typegeneric, > __builtin_{add,sub,mul}_overflow_p > where either the arguments would be instead of integertype1, integertype2, > integertype3 * rather integertype1, integertype2, integertype3 > and we'd only care about the type, not value, of the last argument, > so use it like __builtin_add_overflow_p (a, b, (__typeof ((a) + (b))) 0) > or handle those 3 similarly to __builtin_va_arg, and use > __builtin_add_overflow_p (a, b, int); > I think I prefer the former though. I'm not sure I understand your concern but at this point I would prefer to keep things as they are. I like that the functionality that was requested in c/68120 could be provided under the existing interface, and I'm not fond of the idea of adding yet another set of built-ins just to get at a bit that's already available via the existing ones (in C++ 11, even in constexpr contexts, provided the built-ins were allowed there). I've also spent far more time on this patch than I had planned and I'm way behind on the tasks I was asked to work on. That said, in c/68120 the requester commented that he's considering submitting a request for yet another enhancement in this area, so I think letting him play with what we've got now for a bit will be an opportunity to get his feedback and tweak the API further based on it. Martin ^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH] integer overflow checking builtins in constant expressions 2016-06-03 15:07 ` Martin Sebor @ 2016-06-03 15:23 ` Jakub Jelinek 2016-06-03 16:22 ` Martin Sebor 0 siblings, 1 reply; 36+ messages in thread From: Jakub Jelinek @ 2016-06-03 15:23 UTC (permalink / raw) To: Martin Sebor; +Cc: Gcc Patch List, Jason Merrill, Joseph S. Myers On Fri, Jun 03, 2016 at 09:07:09AM -0600, Martin Sebor wrote: > I'm not sure I understand your concern but at this point I would > prefer to keep things as they are. I like that the functionality My concern is that if you declare that NULL is valid third argument for e.g. __builtin_add_overflow, then unless we add some complicated wording into the docs, we have to penalize the code we emit for e.g. bool foo (int a, int b, int *p) { return __builtin_add_overflow (a, b, p); } While we could previously emit addl %edi, %esi movl %esi, (%rdx) seto %al retq you suddenly have to emit (and your patch doesn't do that): addl %edi, %esi seto %al testq %rdx, %rdx je .L1 movl %esi, (%rdx) .L1: retq because you can't at compile time prove if p is NULL (then you wouldn't store the result) or not. Trying to document that the third argument may be NULL, but only if it is constant NULL pointer expression or something like that (what exactly?) might not be very easily understandable and clear to users. Which is why I've suggested just not allowing (like before) the third argument to be NULL, and just add new 3 builtins for the test for overflow, but don't store it anywhere. They would just be folded early to the same internal function. And when adding the 3 new builtins, we can also choose a different calling convention that would allow the C++98/C constant expressions, by not having the third argument a pointer (we don't need to dereference anything), but e.g. just any integer where we ignore the value (well, evaluate for side-effects) and only care about the type. Jakub ^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH] integer overflow checking builtins in constant expressions 2016-06-03 15:23 ` Jakub Jelinek @ 2016-06-03 16:22 ` Martin Sebor 0 siblings, 0 replies; 36+ messages in thread From: Martin Sebor @ 2016-06-03 16:22 UTC (permalink / raw) To: Jakub Jelinek; +Cc: Gcc Patch List, Jason Merrill, Joseph S. Myers On 06/03/2016 09:23 AM, Jakub Jelinek wrote: > On Fri, Jun 03, 2016 at 09:07:09AM -0600, Martin Sebor wrote: >> I'm not sure I understand your concern but at this point I would >> prefer to keep things as they are. I like that the functionality > > My concern is that if you declare that NULL is valid third argument for e.g. > __builtin_add_overflow, then unless we add some complicated wording into the > docs, Thanks the clarification. > Trying to document that the third argument may be NULL, but only if it is > constant NULL pointer expression or something like that (what exactly?) > might not be very easily understandable and clear to users. I think the updated wording is quite clear: The first of the functions accepts either a pointer to an integer object or a null pointer constant. /A null pointer constant/ is a basic term defined by both C and C++ so it should be familiar to all C++ programmers. (To make it 100% correct we should perhaps say: "...or a null pointer constant cast to a pointer to integer." even though that should be obvious since the functions won't accept a bare NULL.) > Which is why I've suggested just not allowing (like before) the third > argument to be NULL, and just add new 3 builtins for the test for overflow, > but don't store it anywhere. They would just be folded early to the same > internal function. And when adding the 3 new builtins, we can also choose > a different calling convention that would allow the C++98/C constant > expressions, by not having the third argument a pointer (we don't need to > dereference anything), but e.g. just any integer where we ignore the value > (well, evaluate for side-effects) and only care about the type. I understand what you're suggesting and it's something I could have easily done when I started on it a few weeks ago but I'm afraid really out of cycles to continue to tweak the patch. I think it's good enough as it is for now, gives the requester what they asked for and lets me finish the C++ VLA bounds checking, which is the main reason why I did this work to begin with. We can revisit this idea when we get the requester's feedback (and after I've made some headway on my pending tasks). Does that sound reasonable? Martin ^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH] integer overflow checking builtins in constant expressions 2016-06-02 7:23 ` Jakub Jelinek 2016-06-02 7:34 ` Jakub Jelinek @ 2016-06-02 23:21 ` Martin Sebor 2016-06-03 0:28 ` Martin Sebor 1 sibling, 1 reply; 36+ messages in thread From: Martin Sebor @ 2016-06-02 23:21 UTC (permalink / raw) To: Jakub Jelinek; +Cc: Gcc Patch List, Jason Merrill, Joseph S. Myers [-- Attachment #1: Type: text/plain, Size: 3903 bytes --] >> + /* For the "generic" overloads, the first two arguments can have different >> + types and the last argument determines the target type to use to check >> + for overflow. The arguments of the other overloads all have the same >> + type. */ >> + tree type = TREE_TYPE (TREE_TYPE (arg2)); >> + bool isnullp = integer_zerop (arg2); >> + >> + /* When the last argument is a null pointer and the first two arguments >> + are constant, attempt to fold the built-in call into a constant >> + expression indicating whether or not it detected an overflow but >> + without storing the result. */ >> + if (isnullp >> + && TREE_CODE (arg0) == INTEGER_CST && TREE_CODE (arg1) == INTEGER_CST) > > Doesn't this mean you allow NULL arg2 even for BUILT_IN_ADDL_OVERFLOW and > similar clang builtins? Yes, that's possible, and always has been. I've raised a new bug for it: c/71392 - SEGV calling integer overflow built-ins with a null pointer. The built-ins are missing the non-null attribute. I've assigned the bug to myself and will submit a separate patch for it in a minute. >> + { >> + /* Perform the computation in the target type and check for overflow. */ >> + arg0 = fold_convert (type, arg0); >> + arg1 = fold_convert (type, arg1); >> + >> + if (tree result = size_binop_loc (loc, opcode, arg0, arg1)) >> + return TREE_OVERFLOW (result) ? build_one_cst (boolean_type_node) >> + : build_zero_cst (boolean_type_node); > > This is wrong, it is not the computation of overflow that is intended. > The documentation says that we compute (infinite_precision_signed_type) arg0 > op (infinite_precision_signed_type) arg1 and then cast it to type, extend > again to infinite_precision_signed_type and compare. And we have a helper > function for that already. Thank you for the suggestion. Using the helper instead is simpler (though my tests don't show any difference in the computed result so I'm not sure I see in what way the original code was wrong). >> + tree arg0 = cxx_eval_constant_expression (ctx, CALL_EXPR_ARG (t, 0), lval, >> + non_constant_p, overflow_p); >> + tree arg1 = cxx_eval_constant_expression (ctx, CALL_EXPR_ARG (t, 1), lval, >> + non_constant_p, overflow_p); >> + >> + if (TREE_CODE (arg0) == INTEGER_CST && TREE_CODE (arg1) == INTEGER_CST) >> + { >> + if (tree result = size_binop_loc (EXPR_LOC_OR_LOC (t, input_location), >> + opcode, arg0, arg1)) >> + { >> + if (TREE_OVERFLOW (result)) >> + { >> + /* Reset TREE_OVERFLOW to avoid warnings for the overflow. */ >> + TREE_OVERFLOW (result) = 0; > > Again, this is wrong, it should have used arith_overflowed_p. No. As is apparent from the rest of hunk, we need the result or the computation here, not just the overflow bit. > AFAIK the docs use either C++98, C++11 (without space), or > ISO/IEC 14882:2011 etc., but not C++ 98 or C++ 11. > > Also, perhaps just a documentation thing, it would be good to clarify the > NULL last argument. From the POV of generating efficient code, I think we > should say something that the last argument (for the GNU builtins) must be > either a pointer to a valid object, or NULL/nullptr constant cast expression > cast to a pointer type, but nothing else. That is actually what your patch > implements. I've updated the manual to make it clear that the null pointer must be a constant. While testing the updated patch I noticed that I had neglected to update potential_constant_expression_1 in constexpr.c to handle the internal functions corresponding to the built-ins, so I've made that change and added test cases to exercise it. (In the process, I also uncovered a new constexpr bug and raised c++/71391 - error on aggregate initialization with side-effects in a constexpr function. Attached is an updated patch with these changes. Martin [-- Attachment #2: gcc-70507.patch --] [-- Type: text/x-patch, Size: 61685 bytes --] diff --git a/gcc/builtins.c b/gcc/builtins.c index 931d4a6..ada1904 100644 --- a/gcc/builtins.c +++ b/gcc/builtins.c @@ -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; @@ -7957,11 +7958,14 @@ 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; + switch (fcode) { case BUILT_IN_ADD_OVERFLOW: + opcode = PLUS_EXPR; case BUILT_IN_SADD_OVERFLOW: case BUILT_IN_SADDL_OVERFLOW: case BUILT_IN_SADDLL_OVERFLOW: @@ -7971,6 +7975,7 @@ fold_builtin_arith_overflow (location_t loc, enum built_in_function fcode, ifn = IFN_ADD_OVERFLOW; break; case BUILT_IN_SUB_OVERFLOW: + opcode = MINUS_EXPR; case BUILT_IN_SSUB_OVERFLOW: case BUILT_IN_SSUBL_OVERFLOW: case BUILT_IN_SSUBLL_OVERFLOW: @@ -7980,6 +7985,7 @@ fold_builtin_arith_overflow (location_t loc, enum built_in_function fcode, ifn = IFN_SUB_OVERFLOW; break; case BUILT_IN_MUL_OVERFLOW: + opcode = MULT_EXPR; case BUILT_IN_SMUL_OVERFLOW: case BUILT_IN_SMULL_OVERFLOW: case BUILT_IN_SMULLL_OVERFLOW: @@ -7991,6 +7997,26 @@ 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. */ + bool isnullp = integer_zerop (arg2); + tree type = TREE_TYPE (TREE_TYPE (arg2)); + + /* When the last argument to the type-generic built-in is a null pointer + and the first two arguments are constant, attempt to fold the built-in + call into a constant expression indicating whether or not it detected + an overflow but without storing the result. */ + if (opcode != ERROR_MARK && isnullp + && TREE_CODE (arg0) == INTEGER_CST && TREE_CODE (arg1) == INTEGER_CST) + { + /* Perform the computation in the target type and check for overflow. */ + return (arith_overflowed_p (opcode, type, arg0, arg1) + ? boolean_true_node : boolean_false_node); + } + tree ctype = build_complex_type (type); tree call = build_call_expr_internal_loc (loc, ifn, ctype, 2, arg0, arg1); @@ -7998,6 +8024,11 @@ fold_builtin_arith_overflow (location_t loc, enum built_in_function fcode, tree intres = build1_loc (loc, REALPART_EXPR, type, tgt); tree ovfres = build1_loc (loc, IMAGPART_EXPR, type, tgt); ovfres = fold_convert_loc (loc, boolean_type_node, ovfres); + + if (isnullp) + return ovfres; + + tree mem_arg2 = build_fold_indirect_ref_loc (loc, arg2); tree store = fold_build2_loc (loc, MODIFY_EXPR, void_type_node, mem_arg2, intres); return build2_loc (loc, COMPOUND_EXPR, boolean_type_node, store, ovfres); diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c index 482f8af..8107f20 100644 --- a/gcc/cp/constexpr.c +++ b/gcc/cp/constexpr.c @@ -1255,6 +1255,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) + { + if (tree result = size_binop_loc (EXPR_LOC_OR_LOC (t, input_location), + opcode, arg0, arg1)) + { + tree vflow = TREE_OVERFLOW (result) + ? integer_one_node : integer_zero_node; + + /* Reset TREE_OVERFLOW to avoid warnings for the overflow. */ + if (TREE_OVERFLOW (result)) + TREE_OVERFLOW (result) = 0; + + return build_complex (TREE_TYPE (t), result, vflow); + } + } + + *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 +1333,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 +4641,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 +4655,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 +4720,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. */; diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c index 04702ee..f9db199 100644 --- a/gcc/cp/tree.c +++ b/gcc/cp/tree.c @@ -352,6 +352,32 @@ builtin_valid_in_constant_expr_p (const_tree decl) case BUILT_IN_FUNCTION: case BUILT_IN_LINE: + /* The following built-ins are valid in constant expressions + when their arguments are. */ + case BUILT_IN_ADD_OVERFLOW: + case BUILT_IN_SADD_OVERFLOW: + case BUILT_IN_SADDL_OVERFLOW: + case BUILT_IN_SADDLL_OVERFLOW: + case BUILT_IN_UADD_OVERFLOW: + case BUILT_IN_UADDL_OVERFLOW: + case BUILT_IN_UADDLL_OVERFLOW: + + case BUILT_IN_SUB_OVERFLOW: + case BUILT_IN_SSUB_OVERFLOW: + case BUILT_IN_SSUBL_OVERFLOW: + case BUILT_IN_SSUBLL_OVERFLOW: + case BUILT_IN_USUB_OVERFLOW: + case BUILT_IN_USUBL_OVERFLOW: + case BUILT_IN_USUBLL_OVERFLOW: + + case BUILT_IN_MUL_OVERFLOW: + case BUILT_IN_SMUL_OVERFLOW: + case BUILT_IN_SMULL_OVERFLOW: + case BUILT_IN_SMULLL_OVERFLOW: + case BUILT_IN_UMUL_OVERFLOW: + case BUILT_IN_UMULL_OVERFLOW: + case BUILT_IN_UMULLL_OVERFLOW: + /* These have constant results even if their operands are non-constant. */ case BUILT_IN_CONSTANT_P: diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi index 2d4f028..d880821 100644 --- a/gcc/doc/extend.texi +++ b/gcc/doc/extend.texi @@ -9737,7 +9737,10 @@ compiler may also ignore this parameter. @section Built-in Functions to Perform Arithmetic with Overflow Checking The following built-in functions allow performing simple arithmetic operations -together with checking whether the operations overflowed. +together with checking whether the operations overflowed. The first of the +functions accepts either a pointer to an integer object or a null pointer +constant cast to the appropriate type as the last argument. The rest require +a pointer to an object of the specified type as the last argument. @deftypefn {Built-in Function} bool __builtin_add_overflow (@var{type1} a, @var{type2} b, @var{type3} *res) @deftypefnx {Built-in Function} bool __builtin_sadd_overflow (int a, int b, int *res) @@ -9747,21 +9750,50 @@ together with checking whether the operations overflowed. @deftypefnx {Built-in Function} bool __builtin_uaddl_overflow (unsigned long int a, unsigned long int b, unsigned long int *res) @deftypefnx {Built-in Function} bool __builtin_uaddll_overflow (unsigned long long int a, unsigned long long int b, unsigned long int *res) -These built-in functions promote the first two operands into infinite precision signed -type and perform addition on those promoted operands. The result is then -cast to the type the third pointer argument points to and stored there. -If the stored result is equal to the infinite precision result, the built-in -functions return false, otherwise they return true. As the addition is -performed in infinite signed precision, these built-in functions have fully defined -behavior for all argument values. - -The first built-in function allows arbitrary integral types for operands and -the result type must be pointer to some integer type, the rest of the built-in -functions have explicit integer types. +These built-in functions promote the first two operands into infinite precision +signed type and perform addition on those promoted operands. The result is then +converted to the type the third pointer argument points to, and for the first +function when the pointer is not null, stored there. If the converted result +is equal to the infinite precision result, the built-in functions return +@code{false}, otherwise they return @code{true} to indicate that an overflow +has been detected. Because the addition is performed in infinite precision, +these built-in functions have fully defined behavior for all argument values +and integer types. + +The first type-generic built-in function allows arbitrary integer types as +the first two arguments and requires that a pointer to some possibly distinct +integer type be passed to it as the third argument. The pointed-to type is +then used to determine the overflow. As a result, this built-in function +can be used to detect overflow in any arbitrary integer type, including +@code{char} and @code{short}. The remaining built-in functions take +arguments of explicit integer types and make it possible to determine +overflow only in the ranges of those types. Since the only provided forms +of these latter built-in functions are for the signed and unsigned variants +of types @code{int}, @code{long}, and @code{long long}, they cannot be used +to determine overflow in other integer types. + +To enable the efficient integer overflow detection at translation-time, +in C and C++11 and later programs (but not in C++98), the first built-in +may be invoked in constant integer expression contexts with a null pointer +cast to a pointer to the appropriate integer type as the third argument. +For example, the following macro can be used to portably check, at +compile-time, whether or not adding two constant integers will overflow, +and perform the addition only when it is known to be safe and not to trigger +a @option{-Woverflow} warning. + +@smallexample +#define INT_ADD_OVERFLOW(a, b) \ + __builtin_add_overflow (a, b, (__typeof__ ((a) + (b)) *) 0) + +enum @{ + A = INT_MAX, B = 3, + C = INT_ADD_OVERFLOW (A, B) ? 0 : A + B +@}; +@end smallexample -The compiler will attempt to use hardware instructions to implement -these built-in functions where possible, like conditional jump on overflow -after addition, conditional jump on carry etc. +For invocations of the built-in functions evaluated at run-time the compiler +will attempt to make use of efficient hardware instructions such as conditional +jump on overflow after addition, conditional jump on carry, etc. @end deftypefn @@ -9773,9 +9805,9 @@ after addition, conditional jump on carry etc. @deftypefnx {Built-in Function} bool __builtin_usubl_overflow (unsigned long int a, unsigned long int b, unsigned long int *res) @deftypefnx {Built-in Function} bool __builtin_usubll_overflow (unsigned long long int a, unsigned long long int b, unsigned long int *res) -These built-in functions are similar to the add overflow checking built-in -functions above, except they perform subtraction, subtract the second argument -from the first one, instead of addition. +These built-in functions are analogous to the add overflow checking built-in +functions above, except they subtract the second argument from the first one +rather than adding it to it. @end deftypefn @@ -9787,8 +9819,8 @@ from the first one, instead of addition. @deftypefnx {Built-in Function} bool __builtin_umull_overflow (unsigned long int a, unsigned long int b, unsigned long int *res) @deftypefnx {Built-in Function} bool __builtin_umulll_overflow (unsigned long long int a, unsigned long long int b, unsigned long int *res) -These built-in functions are similar to the add overflow checking built-in -functions above, except they perform multiplication, instead of addition. +These built-in functions are analogous to the add overflow checking built-in +functions above, except they perform multiplication rather than addition. @end deftypefn diff --git a/gcc/testsuite/c-c++-common/builtin-arith-overflow-1.c b/gcc/testsuite/c-c++-common/builtin-arith-overflow-1.c index 69b5083..f2be467 100644 --- a/gcc/testsuite/c-c++-common/builtin-arith-overflow-1.c +++ b/gcc/testsuite/c-c++-common/builtin-arith-overflow-1.c @@ -1,7 +1,10 @@ /* { dg-do compile } */ +/* Verify that calls with fewer or more than 3 arguments to the generic + __builtin_op_overflow functions are rejected. */ + int -f1 (void) +generic_0 (void) { int x = __builtin_add_overflow (); /* { dg-error "not enough arguments to function" } */ x += __builtin_sub_overflow (); /* { dg-error "not enough arguments to function" } */ @@ -10,14 +13,121 @@ f1 (void) } int -f2 (int a, int b, int *c, int d) +generic_1 (int a) +{ + int x = __builtin_add_overflow (a); /* { dg-error "not enough arguments to function" } */ + x += __builtin_sub_overflow (a); /* { dg-error "not enough arguments to function" } */ + x += __builtin_mul_overflow (a); /* { dg-error "not enough arguments to function" } */ + + /* Literal argument. */ + x += __builtin_add_overflow (1); /* { dg-error "not enough arguments to function" } */ + x += __builtin_sub_overflow (2); /* { dg-error "not enough arguments to function" } */ + x += __builtin_mul_overflow (3); /* { dg-error "not enough arguments to function" } */ + return x; +} + +int +generic_2 (int a, int b) +{ + int x = __builtin_add_overflow (a, b);/* { dg-error "not enough arguments to function" } */ + x += __builtin_sub_overflow (a, b); /* { dg-error "not enough arguments to function" } */ + x += __builtin_mul_overflow (a, b); /* { dg-error "not enough arguments to function" } */ + x += __builtin_add_overflow (a, 1); /* { dg-error "not enough arguments to function" } */ + x += __builtin_sub_overflow (a, 2); /* { dg-error "not enough arguments to function" } */ + x += __builtin_mul_overflow (a, 3); /* { dg-error "not enough arguments to function" } */ + x += __builtin_add_overflow (4, b); /* { dg-error "not enough arguments to function" } */ + x += __builtin_sub_overflow (5, b); /* { dg-error "not enough arguments to function" } */ + x += __builtin_mul_overflow (6, b); /* { dg-error "not enough arguments to function" } */ + return x; +} + +/* Verify that calls with the correct number of arguments to the generic + __builtin_op_overflow functions are accepted. */ + +int +generic_3 (int a, int b, int c) +{ + int x = __builtin_add_overflow (a, b, &c); + x += __builtin_sub_overflow (a, b, &c); + x += __builtin_mul_overflow (a, b, &c); + x += __builtin_add_overflow (a, 1, &c); + x += __builtin_sub_overflow (a, 2, &c); + x += __builtin_mul_overflow (a, 3, &c); + x += __builtin_add_overflow (4, b, &c); + x += __builtin_sub_overflow (5, b, &c); + x += __builtin_mul_overflow (6, b, &c); + x += __builtin_add_overflow (7, 8, &c); + x += __builtin_sub_overflow (9, 10, &c); + x += __builtin_mul_overflow (11, 12, &c); + return x; +} + +int +generic_4 (int a, int b, int *c, int d) { int x = __builtin_add_overflow (a, b, c, d); /* { dg-error "too many arguments to function" } */ - x += __builtin_sub_overflow (a, b, c, d, d, d); /* { dg-error "too many arguments to function" } */ + x += __builtin_sub_overflow (a, b, c, d); /* { dg-error "too many arguments to function" } */ x += __builtin_mul_overflow (a, b, c, d); /* { dg-error "too many arguments to function" } */ return x; } +/* Verify that calls with fewer or more than 3 arguments to the type + specific forms of the __builtin_op_overflow functions are rejected. */ + +int +generic_wrong_type (int a, int b) +{ + void *p = 0; + double d = 0; + int x = __builtin_add_overflow (a, b, p); /* { dg-error "does not have pointer to integer type" } */ + x += __builtin_sub_overflow (a, b, &p); /* { dg-error "does not have pointer to integer type" } */ + x += __builtin_mul_overflow (a, b, &d); /* { dg-error "does not have pointer to integer type" } */ + + /* Also verify literal arguments. */ + x += __builtin_add_overflow (1, 1, p); /* { dg-error "does not have pointer to integer type" } */ + x += __builtin_sub_overflow (1, 1, &p); /* { dg-error "does not have pointer to integer type" } */ + x += __builtin_mul_overflow (1, 1, &d); /* { dg-error "does not have pointer to integer type" } */ + return x; +} + +/* Verify that calls with fewer than 2 or more than 3 arguments to + the typed __builtin_op_overflow functions are rejected. */ +int +typed_0 (void) +{ + int x = __builtin_add_overflow (); /* { dg-error "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" } */ + return x; +} + +int +typed_1 (int a) +{ + int x = __builtin_sadd_overflow (a); /* { dg-error "too few arguments to function" } */ + x += __builtin_ssub_overflow (a); /* { dg-error "too few arguments to function" } */ + x += __builtin_smul_overflow (a); /* { dg-error "too few arguments to function" } */ + return x; +} + +int +typed_2 (int a, int b) +{ + int x = __builtin_sadd_overflow (a, b); /* { dg-error "too few arguments to function" } */ + x += __builtin_ssub_overflow (a, b); /* { dg-error "too few arguments to function" } */ + x += __builtin_smul_overflow (a, b); /* { dg-error "too few arguments to function" } */ + return x; +} + +int +typed_4 (int a, int b, int *c, int d) +{ + int x = __builtin_sadd_overflow (a, b, c, d); /* { dg-error "too many arguments to function" } */ + x += __builtin_ssub_overflow (a, b, c, d); /* { dg-error "too many arguments to function" } */ + x += __builtin_smul_overflow (a, b, c, d); /* { dg-error "too many arguments to function" } */ + return x; +} + enum E { e0 = 0, e1 = 1 }; #ifndef __cplusplus diff --git a/gcc/testsuite/c-c++-common/builtin-arith-overflow-2.c b/gcc/testsuite/c-c++-common/builtin-arith-overflow-2.c new file mode 100644 index 0000000..e7daccd --- /dev/null +++ b/gcc/testsuite/c-c++-common/builtin-arith-overflow-2.c @@ -0,0 +1,489 @@ +/* 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. */ +#if !__cplusplus || __cplusplus >= 201103L + /* Perform both a run-time test followed by a compile-time test. */ +# define G_TEST(op, T, x, y, vflow) \ + RuntimeAssert(op, T, T, x, y, vflow); \ + StaticAssert ((vflow) == __builtin_ ## op ## _overflow ((x), (y), (T*)0)) +#else + /* C++ 98 doesn't permit casts to pointer types to appear in constant + expressions. Only perform the run-time test. */ +# define G_TEST(op, T, x, y, vflow) \ + RuntimeAssert(op, T, T, x, y, vflow) +#endif + + /* Addition. */ + /* G_TEST (add, signed char, 0, 0, 0); */ + /* G_TEST (add, signed char, 0, SCHAR_MAX, 0); */ + G_TEST (add, signed char, 1, SCHAR_MAX, 1); + G_TEST (add, signed char, SCHAR_MAX, SCHAR_MAX, 1); + G_TEST (add, signed char, 0, SCHAR_MIN, 0); + G_TEST (add, signed char, -1, SCHAR_MIN, 1); + + G_TEST (add, short, 0, 0, 0); + G_TEST (add, short, 0, SHRT_MAX, 0); + G_TEST (add, short, 1, SHRT_MAX, 1); + G_TEST (add, short, SHRT_MAX, SHRT_MAX, 1); + G_TEST (add, short, 0, SHRT_MIN, 0); + G_TEST (add, short, -1, SHRT_MIN, 1); + G_TEST (add, short, SHRT_MIN, SHRT_MIN, 1); + + G_TEST (add, int, 0, 0, 0); + G_TEST (add, int, 0, INT_MAX, 0); + G_TEST (add, int, 1, INT_MAX, 1); + G_TEST (add, int, INT_MAX, INT_MAX, 1); + G_TEST (add, int, 0, INT_MIN, 0); + G_TEST (add, int, -1, INT_MIN, 1); + G_TEST (add, int, INT_MIN, INT_MIN, 1); + + G_TEST (add, long, 0, 0, 0); + G_TEST (add, long, 0, LONG_MAX, 0); + G_TEST (add, long, 1, LONG_MAX, 1); + G_TEST (add, long, LONG_MAX, LONG_MAX, 1); + G_TEST (add, long, 0, LONG_MIN, 0); + G_TEST (add, long, -1, LONG_MIN, 1); + G_TEST (add, long, LONG_MIN, LONG_MIN, 1); + + G_TEST (add, long long, 0, 0, 0); + G_TEST (add, long long, 0, LLONG_MAX, 0); + G_TEST (add, long long, 1, LLONG_MAX, 1); + G_TEST (add, long long, LLONG_MAX, LLONG_MAX, 1); + G_TEST (add, long long, 0, LLONG_MIN, 0); + G_TEST (add, long long, -1, LLONG_MIN, 1); + G_TEST (add, long long, LLONG_MIN, LLONG_MIN, 1); + + /* Subtraction */ + G_TEST (sub, unsigned char, 0, 0, 0); + G_TEST (sub, unsigned char, 0, UCHAR_MAX, 1); + G_TEST (sub, unsigned char, 1, UCHAR_MAX, 1); + + G_TEST (sub, unsigned char, UCHAR_MAX, UCHAR_MAX, 0); + G_TEST (sub, unsigned short, 0, 0, 0); + G_TEST (sub, unsigned short, 0, USHRT_MAX, 1); + G_TEST (sub, unsigned short, 1, USHRT_MAX, 1); + G_TEST (sub, unsigned short, USHRT_MAX, USHRT_MAX, 0); + + G_TEST (sub, unsigned, 0, 0, 0); + G_TEST (sub, unsigned, 0, UINT_MAX, 1); + G_TEST (sub, unsigned, 1, UINT_MAX, 1); + G_TEST (sub, unsigned, UINT_MAX, UINT_MAX, 0); + + G_TEST (sub, unsigned long, 0, 0, 0); + G_TEST (sub, unsigned long, 0, ULONG_MAX, 1); + G_TEST (sub, unsigned long, 1, ULONG_MAX, 1); + G_TEST (sub, unsigned long, ULONG_MAX, ULONG_MAX, 0); + + G_TEST (sub, unsigned long long, 0, 0, 0); + G_TEST (sub, unsigned long long, 0, ULLONG_MAX, 1); + G_TEST (sub, unsigned long long, 1, ULLONG_MAX, 1); + G_TEST (sub, unsigned long long, ULLONG_MAX, ULLONG_MAX, 0); + + G_TEST (sub, signed char, 0, 0, 0); + G_TEST (sub, signed char, 0, SCHAR_MAX, 0); + G_TEST (sub, signed char, 1, SCHAR_MAX, 0); + G_TEST (sub, signed char, SCHAR_MAX, SCHAR_MAX, 0); + G_TEST (sub, signed char, SCHAR_MIN, 1, 1); + G_TEST (sub, signed char, 0, SCHAR_MIN, 1); + G_TEST (sub, signed char, -1, SCHAR_MIN, 0); + + G_TEST (sub, short, 0, 0, 0); + G_TEST (sub, short, 0, SHRT_MAX, 0); + G_TEST (sub, short, 1, SHRT_MAX, 0); + G_TEST (sub, short, SHRT_MAX, SHRT_MAX, 0); + G_TEST (sub, short, 0, SHRT_MIN, 1); + G_TEST (sub, short, -1, SHRT_MIN, 0); + G_TEST (sub, short, SHRT_MIN, SHRT_MIN, 0); + + G_TEST (sub, int, 0, 0, 0); + G_TEST (sub, int, 0, INT_MAX, 0); + G_TEST (sub, int, 1, INT_MAX, 0); + G_TEST (sub, int, INT_MAX, INT_MAX, 0); + G_TEST (sub, int, 0, INT_MIN, 1); + G_TEST (sub, int, -1, INT_MIN, 0); + G_TEST (sub, int, INT_MIN, INT_MIN, 0); + + G_TEST (sub, long, 0, 0, 0); + G_TEST (sub, long, 0, LONG_MAX, 0); + G_TEST (sub, long, 1, LONG_MAX, 0); + G_TEST (sub, long, LONG_MAX, LONG_MAX, 0); + G_TEST (sub, long, 0, LONG_MIN, 1); + G_TEST (sub, long, -1, LONG_MIN, 0); + G_TEST (sub, long, LONG_MIN, LONG_MIN, 0); + + G_TEST (sub, long long, 0, 0, 0); + G_TEST (sub, long long, 0, LLONG_MAX, 0); + G_TEST (sub, long long, 1, LLONG_MAX, 0); + G_TEST (sub, long long, LLONG_MAX, LLONG_MAX, 0); + G_TEST (sub, long long, 0, LLONG_MIN, 1); + G_TEST (sub, long long, -1, LLONG_MIN, 0); + G_TEST (sub, long long, LLONG_MIN, LLONG_MIN, 0); + + G_TEST (sub, unsigned char, 0, 0, 0); + G_TEST (sub, unsigned char, 0, UCHAR_MAX, 1); + G_TEST (sub, unsigned char, 1, UCHAR_MAX, 1); + G_TEST (sub, unsigned char, UCHAR_MAX, UCHAR_MAX, 0); + + G_TEST (sub, unsigned short, 0, 0, 0); + G_TEST (sub, unsigned short, 0, USHRT_MAX, 1); + G_TEST (sub, unsigned short, 1, USHRT_MAX, 1); + G_TEST (sub, unsigned short, USHRT_MAX, USHRT_MAX, 0); + + G_TEST (sub, unsigned, 0, 0, 0); + G_TEST (sub, unsigned, 0, UINT_MAX, 1); + G_TEST (sub, unsigned, 1, UINT_MAX, 1); + G_TEST (sub, unsigned, UINT_MAX, UINT_MAX, 0); + + G_TEST (sub, unsigned long, 0, 0, 0); + G_TEST (sub, unsigned long, 0, ULONG_MAX, 1); + G_TEST (sub, unsigned long, 1, ULONG_MAX, 1); + G_TEST (sub, unsigned long, ULONG_MAX, ULONG_MAX, 0); + + G_TEST (sub, unsigned long long, 0, 0, 0); + G_TEST (sub, unsigned long long, 0, ULLONG_MAX, 1); + G_TEST (sub, unsigned long long, 1, ULLONG_MAX, 1); + G_TEST (sub, unsigned long long, ULLONG_MAX, ULLONG_MAX, 0); + + /* Multiplication. */ + G_TEST (mul, unsigned char, 0, 0, 0); + G_TEST (mul, unsigned char, 0, UCHAR_MAX, 0); + G_TEST (mul, unsigned char, 1, UCHAR_MAX, 0); + G_TEST (mul, unsigned char, 2, UCHAR_MAX, 1); + G_TEST (mul, unsigned char, UCHAR_MAX, UCHAR_MAX, 1); + + G_TEST (mul, unsigned short, 0, 0, 0); + G_TEST (mul, unsigned short, 0, USHRT_MAX, 0); + G_TEST (mul, unsigned short, 1, USHRT_MAX, 0); + G_TEST (mul, unsigned short, USHRT_MAX, 2, 1); + G_TEST (mul, unsigned short, USHRT_MAX, USHRT_MAX, 1); + + G_TEST (mul, unsigned, 0, 0, 0); + G_TEST (mul, unsigned, 0, UINT_MAX, 0); + G_TEST (mul, unsigned, 1, UINT_MAX, 0); + G_TEST (mul, unsigned, 2, UINT_MAX, 1); + G_TEST (mul, unsigned, UINT_MAX, UINT_MAX, 1); + + G_TEST (mul, unsigned long, 0, 0, 0); + G_TEST (mul, unsigned long, 0, ULONG_MAX, 0); + G_TEST (mul, unsigned long, 1, ULONG_MAX, 0); + G_TEST (mul, unsigned long, 2, ULONG_MAX, 1); + G_TEST (mul, unsigned long, ULONG_MAX, ULONG_MAX, 1); + + G_TEST (mul, unsigned long long, 0, 0, 0); + G_TEST (mul, unsigned long long, 0, ULLONG_MAX, 0); + G_TEST (mul, unsigned long long, 1, ULLONG_MAX, 0); + G_TEST (mul, unsigned long long, 2, ULLONG_MAX, 1); + G_TEST (mul, unsigned long long, ULLONG_MAX, ULLONG_MAX, 1); + + G_TEST (mul, signed char, 0, 0, 0); + G_TEST (mul, signed char, 0, SCHAR_MAX, 0); + G_TEST (mul, signed char, 1, SCHAR_MAX, 0); + G_TEST (mul, signed char, SCHAR_MAX, SCHAR_MAX, 1); + G_TEST (mul, signed char, SCHAR_MIN, 1, 0); + G_TEST (mul, signed char, 0, SCHAR_MIN, 0); + G_TEST (mul, signed char, -1, SCHAR_MIN, 1); + + G_TEST (mul, short, 0, 0, 0); + G_TEST (mul, short, 0, SHRT_MAX, 0); + G_TEST (mul, short, 1, SHRT_MAX, 0); + G_TEST (mul, short, SHRT_MAX, SHRT_MAX, 1); + G_TEST (mul, short, 0, SHRT_MIN, 0); + G_TEST (mul, short, -1, SHRT_MIN, 1); + G_TEST (mul, short, SHRT_MIN, SHRT_MIN, 1); + + G_TEST (mul, int, 0, 0, 0); + G_TEST (mul, int, 0, INT_MAX, 0); + G_TEST (mul, int, 1, INT_MAX, 0); + G_TEST (mul, int, INT_MAX, INT_MAX, 1); + G_TEST (mul, int, 0, INT_MIN, 0); + G_TEST (mul, int, -1, INT_MIN, 1); + G_TEST (mul, int, INT_MIN, INT_MIN, 1); + + G_TEST (mul, long, 0, 0, 0); + G_TEST (mul, long, 0, LONG_MAX, 0); + G_TEST (mul, long, 1, LONG_MAX, 0); + G_TEST (mul, long, LONG_MAX, LONG_MAX, 1); + G_TEST (mul, long, 0, LONG_MIN, 0); + G_TEST (mul, long, -1, LONG_MIN, 1); + G_TEST (mul, long, LONG_MIN, LONG_MIN, 1); + + G_TEST (mul, long long, 0, 0, 0); + G_TEST (mul, long long, 0, LLONG_MAX, 0); + G_TEST (mul, long long, 1, LLONG_MAX, 0); + G_TEST (mul, long long, LLONG_MAX, LLONG_MAX, 1); + G_TEST (mul, long long, 0, LLONG_MIN, 0); + G_TEST (mul, long long, -1, LLONG_MIN, 1); + G_TEST (mul, long long, LLONG_MIN, LLONG_MIN, 1); + + G_TEST (mul, unsigned char, 0, 0, 0); + G_TEST (mul, unsigned char, 0, UCHAR_MAX, 0); + G_TEST (mul, unsigned char, 1, UCHAR_MAX, 0); + G_TEST (mul, unsigned char, UCHAR_MAX, UCHAR_MAX, 1); + + G_TEST (mul, unsigned short, 0, 0, 0); + G_TEST (mul, unsigned short, 0, USHRT_MAX, 0); + G_TEST (mul, unsigned short, 1, USHRT_MAX, 0); + G_TEST (mul, unsigned short, USHRT_MAX, USHRT_MAX, 1); + + G_TEST (mul, unsigned, 0, 0, 0); + G_TEST (mul, unsigned, 0, UINT_MAX, 0); + G_TEST (mul, unsigned, 1, UINT_MAX, 0); + G_TEST (mul, unsigned, UINT_MAX, UINT_MAX, 1); + + G_TEST (mul, unsigned long, 0, 0, 0); + G_TEST (mul, unsigned long, 0, ULONG_MAX, 0); + G_TEST (mul, unsigned long, 1, ULONG_MAX, 0); + G_TEST (mul, unsigned long, ULONG_MAX, ULONG_MAX, 1); + + G_TEST (mul, unsigned long long, 0, 0, 0); + G_TEST (mul, unsigned long long, 0, ULLONG_MAX, 0); + G_TEST (mul, unsigned long long, 1, ULLONG_MAX, 0); + G_TEST (mul, unsigned long long, ULLONG_MAX, ULLONG_MAX, 1); + + /* Verify that each call to the type-specific __builtin_op_overflow + 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; +} diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-arith-overflow.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-arith-overflow.C new file mode 100644 index 0000000..ec7e5a8 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-arith-overflow.C @@ -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 ( 1, 1, (int*)0), + a1 = __builtin_add_overflow (INT_MAX, 1, (int*)0) +}; + +Assert (a0 == 0); +Assert (a1 == 1); + +enum Sub { + s0 = __builtin_sub_overflow ( 1, 1, (int*)0), + s1 = __builtin_sub_overflow (INT_MIN, 1, (int*)0) +}; + +Assert (s0 == 0); +Assert (s1 == 1); + +enum Mul { + m0 = __builtin_add_overflow ( 1, 1, (int*)0), + m1 = __builtin_add_overflow (INT_MAX, INT_MAX, (int*)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 (x, y, (V*)0)> +struct Add { + Assert (z == v); +}; + +template <class T, class U, class V, + T x, U y, bool v, bool z = __builtin_sub_overflow (x, y, (V*)0)> +struct Sub { + Assert (z == v); +}; + +template <class T, class U, class V, + T x, U y, bool v, bool z = __builtin_mul_overflow (x, y, (V*)0)> +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 (a, b, (int*)0) }; +#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 diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-arith-overflow.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-arith-overflow.C new file mode 100644 index 0000000..7ca0033 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-arith-overflow.C @@ -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); ^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH] integer overflow checking builtins in constant expressions 2016-06-02 23:21 ` Martin Sebor @ 2016-06-03 0:28 ` Martin Sebor 2016-06-03 7:06 ` Jakub Jelinek 0 siblings, 1 reply; 36+ messages in thread From: Martin Sebor @ 2016-06-03 0:28 UTC (permalink / raw) To: Jakub Jelinek; +Cc: Gcc Patch List, Jason Merrill, Joseph S. Myers [-- Attachment #1: Type: text/plain, Size: 1528 bytes --] >>> + { >>> + /* Perform the computation in the target type and check for >>> overflow. */ >>> + arg0 = fold_convert (type, arg0); >>> + arg1 = fold_convert (type, arg1); >>> + >>> + if (tree result = size_binop_loc (loc, opcode, arg0, arg1)) >>> + return TREE_OVERFLOW (result) ? build_one_cst (boolean_type_node) >>> + : build_zero_cst (boolean_type_node); >> >> This is wrong, it is not the computation of overflow that is intended. >> The documentation says that we compute >> (infinite_precision_signed_type) arg0 >> op (infinite_precision_signed_type) arg1 and then cast it to type, extend >> again to infinite_precision_signed_type and compare. And we have a >> helper >> function for that already. > > Thank you for the suggestion. Using the helper instead is simpler > (though my tests don't show any difference in the computed result > so I'm not sure I see in what way the original code was wrong). ... >> >> Again, this is wrong, it should have used arith_overflowed_p. > > No. As is apparent from the rest of hunk, we need the result or > the computation here, not just the overflow bit. It just dawned on me what the problem was and why both places need arith_overflowed_p: Converting the arguments to the result type and doing the arithmetic and checking for overflow in it would mask it in cases like: __builtin_add_overflow (SCHAR_MAX + 1, 1, (signed char*)0) I've fixed it in the attached patch (and added test cases to verify it). Martin [-- Attachment #2: gcc-70507.patch --] [-- Type: text/x-patch, Size: 63669 bytes --] PR c++/70507 - integer overflow builtins not constant expressions PR c/68120 - can't easily deal with integer overflow at compile time gcc/cp/ChangeLog: 2016-06-02 Martin Sebor <msebor@redhat.com> PR c++/70507 PR c/68120 * constexpr.c (cxx_eval_internal_function): New function. (cxx_eval_call_expression): Call it. (potential_constant_expression_1): Handle integer arithmetic overflow built-ins. * tree.c (builtin_valid_in_constant_expr_p): Same. gcc/ChangeLog: 2016-06-02 Martin Sebor <msebor@redhat.com> PR c++/70507 PR c/68120 * builtins.c (fold_builtin_unordered_cmp): Handle integer arithmetic overflow built-ins. * doc/extend.texi (Integer Overflow Builtins): Update. gcc/testsuite/ChangeLog: 2016-06-02 Martin Sebor <msebor@redhat.com> PR c++/70507 PR c/68120 * c-c++-common/builtin-arith-overflow-1.c: Add test cases. * c-c++-common/builtin-arith-overflow-2.c: New test. * g++.dg/cpp0x/constexpr-arith-overflow.C: New test. * g++.dg/cpp1y/constexpr-arith-overflow.C: New test. diff --git a/gcc/builtins.c b/gcc/builtins.c index 931d4a6..ada1904 100644 --- a/gcc/builtins.c +++ b/gcc/builtins.c @@ -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; @@ -7957,11 +7958,14 @@ 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; + switch (fcode) { case BUILT_IN_ADD_OVERFLOW: + opcode = PLUS_EXPR; case BUILT_IN_SADD_OVERFLOW: case BUILT_IN_SADDL_OVERFLOW: case BUILT_IN_SADDLL_OVERFLOW: @@ -7971,6 +7975,7 @@ fold_builtin_arith_overflow (location_t loc, enum built_in_function fcode, ifn = IFN_ADD_OVERFLOW; break; case BUILT_IN_SUB_OVERFLOW: + opcode = MINUS_EXPR; case BUILT_IN_SSUB_OVERFLOW: case BUILT_IN_SSUBL_OVERFLOW: case BUILT_IN_SSUBLL_OVERFLOW: @@ -7980,6 +7985,7 @@ fold_builtin_arith_overflow (location_t loc, enum built_in_function fcode, ifn = IFN_SUB_OVERFLOW; break; case BUILT_IN_MUL_OVERFLOW: + opcode = MULT_EXPR; case BUILT_IN_SMUL_OVERFLOW: case BUILT_IN_SMULL_OVERFLOW: case BUILT_IN_SMULLL_OVERFLOW: @@ -7991,6 +7997,26 @@ 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. */ + bool isnullp = integer_zerop (arg2); + tree type = TREE_TYPE (TREE_TYPE (arg2)); + + /* When the last argument to the type-generic built-in is a null pointer + and the first two arguments are constant, attempt to fold the built-in + call into a constant expression indicating whether or not it detected + an overflow but without storing the result. */ + if (opcode != ERROR_MARK && isnullp + && TREE_CODE (arg0) == INTEGER_CST && TREE_CODE (arg1) == INTEGER_CST) + { + /* Perform the computation in the target type and check for overflow. */ + return (arith_overflowed_p (opcode, type, arg0, arg1) + ? boolean_true_node : boolean_false_node); + } + tree ctype = build_complex_type (type); tree call = build_call_expr_internal_loc (loc, ifn, ctype, 2, arg0, arg1); @@ -7998,6 +8024,11 @@ fold_builtin_arith_overflow (location_t loc, enum built_in_function fcode, tree intres = build1_loc (loc, REALPART_EXPR, type, tgt); tree ovfres = build1_loc (loc, IMAGPART_EXPR, type, tgt); ovfres = fold_convert_loc (loc, boolean_type_node, ovfres); + + if (isnullp) + return ovfres; + + tree mem_arg2 = build_fold_indirect_ref_loc (loc, arg2); tree store = fold_build2_loc (loc, MODIFY_EXPR, void_type_node, mem_arg2, intres); return build2_loc (loc, COMPOUND_EXPR, boolean_type_node, store, ovfres); diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c index 482f8af..301efa78 100644 --- a/gcc/cp/constexpr.c +++ b/gcc/cp/constexpr.c @@ -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,70 @@ 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) + { + if (tree result = size_binop_loc (EXPR_LOC_OR_LOC (t, input_location), + opcode, arg0, arg1)) + { + tree type = TREE_TYPE (TREE_TYPE (t)); + tree vflow = arith_overflowed_p (opcode, type, arg0, arg1) + ? integer_one_node : integer_zero_node; + + /* Reset TREE_OVERFLOW to avoid warnings for the overflow. */ + if (TREE_OVERFLOW (result)) + TREE_OVERFLOW (result) = 0; + + return build_complex (TREE_TYPE (t), result, vflow); + } + } + + *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 +1335,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 +4643,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 +4657,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 +4722,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. */; diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c index 04702ee..f9db199 100644 --- a/gcc/cp/tree.c +++ b/gcc/cp/tree.c @@ -352,6 +352,32 @@ builtin_valid_in_constant_expr_p (const_tree decl) case BUILT_IN_FUNCTION: case BUILT_IN_LINE: + /* The following built-ins are valid in constant expressions + when their arguments are. */ + case BUILT_IN_ADD_OVERFLOW: + case BUILT_IN_SADD_OVERFLOW: + case BUILT_IN_SADDL_OVERFLOW: + case BUILT_IN_SADDLL_OVERFLOW: + case BUILT_IN_UADD_OVERFLOW: + case BUILT_IN_UADDL_OVERFLOW: + case BUILT_IN_UADDLL_OVERFLOW: + + case BUILT_IN_SUB_OVERFLOW: + case BUILT_IN_SSUB_OVERFLOW: + case BUILT_IN_SSUBL_OVERFLOW: + case BUILT_IN_SSUBLL_OVERFLOW: + case BUILT_IN_USUB_OVERFLOW: + case BUILT_IN_USUBL_OVERFLOW: + case BUILT_IN_USUBLL_OVERFLOW: + + case BUILT_IN_MUL_OVERFLOW: + case BUILT_IN_SMUL_OVERFLOW: + case BUILT_IN_SMULL_OVERFLOW: + case BUILT_IN_SMULLL_OVERFLOW: + case BUILT_IN_UMUL_OVERFLOW: + case BUILT_IN_UMULL_OVERFLOW: + case BUILT_IN_UMULLL_OVERFLOW: + /* These have constant results even if their operands are non-constant. */ case BUILT_IN_CONSTANT_P: diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi index 2d4f028..d880821 100644 --- a/gcc/doc/extend.texi +++ b/gcc/doc/extend.texi @@ -9737,7 +9737,10 @@ compiler may also ignore this parameter. @section Built-in Functions to Perform Arithmetic with Overflow Checking The following built-in functions allow performing simple arithmetic operations -together with checking whether the operations overflowed. +together with checking whether the operations overflowed. The first of the +functions accepts either a pointer to an integer object or a null pointer +constant cast to the appropriate type as the last argument. The rest require +a pointer to an object of the specified type as the last argument. @deftypefn {Built-in Function} bool __builtin_add_overflow (@var{type1} a, @var{type2} b, @var{type3} *res) @deftypefnx {Built-in Function} bool __builtin_sadd_overflow (int a, int b, int *res) @@ -9747,21 +9750,50 @@ together with checking whether the operations overflowed. @deftypefnx {Built-in Function} bool __builtin_uaddl_overflow (unsigned long int a, unsigned long int b, unsigned long int *res) @deftypefnx {Built-in Function} bool __builtin_uaddll_overflow (unsigned long long int a, unsigned long long int b, unsigned long int *res) -These built-in functions promote the first two operands into infinite precision signed -type and perform addition on those promoted operands. The result is then -cast to the type the third pointer argument points to and stored there. -If the stored result is equal to the infinite precision result, the built-in -functions return false, otherwise they return true. As the addition is -performed in infinite signed precision, these built-in functions have fully defined -behavior for all argument values. - -The first built-in function allows arbitrary integral types for operands and -the result type must be pointer to some integer type, the rest of the built-in -functions have explicit integer types. +These built-in functions promote the first two operands into infinite precision +signed type and perform addition on those promoted operands. The result is then +converted to the type the third pointer argument points to, and for the first +function when the pointer is not null, stored there. If the converted result +is equal to the infinite precision result, the built-in functions return +@code{false}, otherwise they return @code{true} to indicate that an overflow +has been detected. Because the addition is performed in infinite precision, +these built-in functions have fully defined behavior for all argument values +and integer types. + +The first type-generic built-in function allows arbitrary integer types as +the first two arguments and requires that a pointer to some possibly distinct +integer type be passed to it as the third argument. The pointed-to type is +then used to determine the overflow. As a result, this built-in function +can be used to detect overflow in any arbitrary integer type, including +@code{char} and @code{short}. The remaining built-in functions take +arguments of explicit integer types and make it possible to determine +overflow only in the ranges of those types. Since the only provided forms +of these latter built-in functions are for the signed and unsigned variants +of types @code{int}, @code{long}, and @code{long long}, they cannot be used +to determine overflow in other integer types. + +To enable the efficient integer overflow detection at translation-time, +in C and C++11 and later programs (but not in C++98), the first built-in +may be invoked in constant integer expression contexts with a null pointer +cast to a pointer to the appropriate integer type as the third argument. +For example, the following macro can be used to portably check, at +compile-time, whether or not adding two constant integers will overflow, +and perform the addition only when it is known to be safe and not to trigger +a @option{-Woverflow} warning. + +@smallexample +#define INT_ADD_OVERFLOW(a, b) \ + __builtin_add_overflow (a, b, (__typeof__ ((a) + (b)) *) 0) + +enum @{ + A = INT_MAX, B = 3, + C = INT_ADD_OVERFLOW (A, B) ? 0 : A + B +@}; +@end smallexample -The compiler will attempt to use hardware instructions to implement -these built-in functions where possible, like conditional jump on overflow -after addition, conditional jump on carry etc. +For invocations of the built-in functions evaluated at run-time the compiler +will attempt to make use of efficient hardware instructions such as conditional +jump on overflow after addition, conditional jump on carry, etc. @end deftypefn @@ -9773,9 +9805,9 @@ after addition, conditional jump on carry etc. @deftypefnx {Built-in Function} bool __builtin_usubl_overflow (unsigned long int a, unsigned long int b, unsigned long int *res) @deftypefnx {Built-in Function} bool __builtin_usubll_overflow (unsigned long long int a, unsigned long long int b, unsigned long int *res) -These built-in functions are similar to the add overflow checking built-in -functions above, except they perform subtraction, subtract the second argument -from the first one, instead of addition. +These built-in functions are analogous to the add overflow checking built-in +functions above, except they subtract the second argument from the first one +rather than adding it to it. @end deftypefn @@ -9787,8 +9819,8 @@ from the first one, instead of addition. @deftypefnx {Built-in Function} bool __builtin_umull_overflow (unsigned long int a, unsigned long int b, unsigned long int *res) @deftypefnx {Built-in Function} bool __builtin_umulll_overflow (unsigned long long int a, unsigned long long int b, unsigned long int *res) -These built-in functions are similar to the add overflow checking built-in -functions above, except they perform multiplication, instead of addition. +These built-in functions are analogous to the add overflow checking built-in +functions above, except they perform multiplication rather than addition. @end deftypefn diff --git a/gcc/testsuite/c-c++-common/builtin-arith-overflow-1.c b/gcc/testsuite/c-c++-common/builtin-arith-overflow-1.c index 69b5083..f2be467 100644 --- a/gcc/testsuite/c-c++-common/builtin-arith-overflow-1.c +++ b/gcc/testsuite/c-c++-common/builtin-arith-overflow-1.c @@ -1,7 +1,10 @@ /* { dg-do compile } */ +/* Verify that calls with fewer or more than 3 arguments to the generic + __builtin_op_overflow functions are rejected. */ + int -f1 (void) +generic_0 (void) { int x = __builtin_add_overflow (); /* { dg-error "not enough arguments to function" } */ x += __builtin_sub_overflow (); /* { dg-error "not enough arguments to function" } */ @@ -10,14 +13,121 @@ f1 (void) } int -f2 (int a, int b, int *c, int d) +generic_1 (int a) +{ + int x = __builtin_add_overflow (a); /* { dg-error "not enough arguments to function" } */ + x += __builtin_sub_overflow (a); /* { dg-error "not enough arguments to function" } */ + x += __builtin_mul_overflow (a); /* { dg-error "not enough arguments to function" } */ + + /* Literal argument. */ + x += __builtin_add_overflow (1); /* { dg-error "not enough arguments to function" } */ + x += __builtin_sub_overflow (2); /* { dg-error "not enough arguments to function" } */ + x += __builtin_mul_overflow (3); /* { dg-error "not enough arguments to function" } */ + return x; +} + +int +generic_2 (int a, int b) +{ + int x = __builtin_add_overflow (a, b);/* { dg-error "not enough arguments to function" } */ + x += __builtin_sub_overflow (a, b); /* { dg-error "not enough arguments to function" } */ + x += __builtin_mul_overflow (a, b); /* { dg-error "not enough arguments to function" } */ + x += __builtin_add_overflow (a, 1); /* { dg-error "not enough arguments to function" } */ + x += __builtin_sub_overflow (a, 2); /* { dg-error "not enough arguments to function" } */ + x += __builtin_mul_overflow (a, 3); /* { dg-error "not enough arguments to function" } */ + x += __builtin_add_overflow (4, b); /* { dg-error "not enough arguments to function" } */ + x += __builtin_sub_overflow (5, b); /* { dg-error "not enough arguments to function" } */ + x += __builtin_mul_overflow (6, b); /* { dg-error "not enough arguments to function" } */ + return x; +} + +/* Verify that calls with the correct number of arguments to the generic + __builtin_op_overflow functions are accepted. */ + +int +generic_3 (int a, int b, int c) +{ + int x = __builtin_add_overflow (a, b, &c); + x += __builtin_sub_overflow (a, b, &c); + x += __builtin_mul_overflow (a, b, &c); + x += __builtin_add_overflow (a, 1, &c); + x += __builtin_sub_overflow (a, 2, &c); + x += __builtin_mul_overflow (a, 3, &c); + x += __builtin_add_overflow (4, b, &c); + x += __builtin_sub_overflow (5, b, &c); + x += __builtin_mul_overflow (6, b, &c); + x += __builtin_add_overflow (7, 8, &c); + x += __builtin_sub_overflow (9, 10, &c); + x += __builtin_mul_overflow (11, 12, &c); + return x; +} + +int +generic_4 (int a, int b, int *c, int d) { int x = __builtin_add_overflow (a, b, c, d); /* { dg-error "too many arguments to function" } */ - x += __builtin_sub_overflow (a, b, c, d, d, d); /* { dg-error "too many arguments to function" } */ + x += __builtin_sub_overflow (a, b, c, d); /* { dg-error "too many arguments to function" } */ x += __builtin_mul_overflow (a, b, c, d); /* { dg-error "too many arguments to function" } */ return x; } +/* Verify that calls with fewer or more than 3 arguments to the type + specific forms of the __builtin_op_overflow functions are rejected. */ + +int +generic_wrong_type (int a, int b) +{ + void *p = 0; + double d = 0; + int x = __builtin_add_overflow (a, b, p); /* { dg-error "does not have pointer to integer type" } */ + x += __builtin_sub_overflow (a, b, &p); /* { dg-error "does not have pointer to integer type" } */ + x += __builtin_mul_overflow (a, b, &d); /* { dg-error "does not have pointer to integer type" } */ + + /* Also verify literal arguments. */ + x += __builtin_add_overflow (1, 1, p); /* { dg-error "does not have pointer to integer type" } */ + x += __builtin_sub_overflow (1, 1, &p); /* { dg-error "does not have pointer to integer type" } */ + x += __builtin_mul_overflow (1, 1, &d); /* { dg-error "does not have pointer to integer type" } */ + return x; +} + +/* Verify that calls with fewer than 2 or more than 3 arguments to + the typed __builtin_op_overflow functions are rejected. */ +int +typed_0 (void) +{ + int x = __builtin_add_overflow (); /* { dg-error "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" } */ + return x; +} + +int +typed_1 (int a) +{ + int x = __builtin_sadd_overflow (a); /* { dg-error "too few arguments to function" } */ + x += __builtin_ssub_overflow (a); /* { dg-error "too few arguments to function" } */ + x += __builtin_smul_overflow (a); /* { dg-error "too few arguments to function" } */ + return x; +} + +int +typed_2 (int a, int b) +{ + int x = __builtin_sadd_overflow (a, b); /* { dg-error "too few arguments to function" } */ + x += __builtin_ssub_overflow (a, b); /* { dg-error "too few arguments to function" } */ + x += __builtin_smul_overflow (a, b); /* { dg-error "too few arguments to function" } */ + return x; +} + +int +typed_4 (int a, int b, int *c, int d) +{ + int x = __builtin_sadd_overflow (a, b, c, d); /* { dg-error "too many arguments to function" } */ + x += __builtin_ssub_overflow (a, b, c, d); /* { dg-error "too many arguments to function" } */ + x += __builtin_smul_overflow (a, b, c, d); /* { dg-error "too many arguments to function" } */ + return x; +} + enum E { e0 = 0, e1 = 1 }; #ifndef __cplusplus diff --git a/gcc/testsuite/c-c++-common/builtin-arith-overflow-2.c b/gcc/testsuite/c-c++-common/builtin-arith-overflow-2.c new file mode 100644 index 0000000..8707416 --- /dev/null +++ b/gcc/testsuite/c-c++-common/builtin-arith-overflow-2.c @@ -0,0 +1,501 @@ +/* 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. */ +#if !__cplusplus || __cplusplus >= 201103L + /* Perform both a run-time test followed by a compile-time test. */ +# define G_TEST(op, T, x, y, vflow) \ + RuntimeAssert(op, __typeof__ (op (x, y)), T, x, y, vflow); \ + StaticAssert ((vflow) == __builtin_ ## op ## _overflow ((x), (y), (T*)0)) +#else + /* C++ 98 doesn't permit casts to pointer types to appear in constant + expressions. Only perform the run-time test. */ +# define G_TEST(op, T, x, y, vflow) \ + RuntimeAssert(op, __typeof__ (op (x, y)), T, x, y, vflow) +#endif + + /* Addition. */ + G_TEST (add, signed char, 0, 0, 0); + G_TEST (add, signed char, 0, SCHAR_MAX, 0); + G_TEST (add, signed char, 1, SCHAR_MAX, 1); + G_TEST (add, signed char, SCHAR_MAX, SCHAR_MAX, 1); + G_TEST (add, signed char, 0, SCHAR_MIN, 0); + G_TEST (add, signed char, -1, SCHAR_MIN, 1); + /* 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; +} diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-arith-overflow.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-arith-overflow.C new file mode 100644 index 0000000..ec7e5a8 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-arith-overflow.C @@ -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 ( 1, 1, (int*)0), + a1 = __builtin_add_overflow (INT_MAX, 1, (int*)0) +}; + +Assert (a0 == 0); +Assert (a1 == 1); + +enum Sub { + s0 = __builtin_sub_overflow ( 1, 1, (int*)0), + s1 = __builtin_sub_overflow (INT_MIN, 1, (int*)0) +}; + +Assert (s0 == 0); +Assert (s1 == 1); + +enum Mul { + m0 = __builtin_add_overflow ( 1, 1, (int*)0), + m1 = __builtin_add_overflow (INT_MAX, INT_MAX, (int*)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 (x, y, (V*)0)> +struct Add { + Assert (z == v); +}; + +template <class T, class U, class V, + T x, U y, bool v, bool z = __builtin_sub_overflow (x, y, (V*)0)> +struct Sub { + Assert (z == v); +}; + +template <class T, class U, class V, + T x, U y, bool v, bool z = __builtin_mul_overflow (x, y, (V*)0)> +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 (a, b, (int*)0) }; +#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 diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-arith-overflow.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-arith-overflow.C new file mode 100644 index 0000000..7ca0033 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-arith-overflow.C @@ -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); ^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH] integer overflow checking builtins in constant expressions 2016-06-03 0:28 ` Martin Sebor @ 2016-06-03 7:06 ` Jakub Jelinek 2016-06-03 15:29 ` Martin Sebor 0 siblings, 1 reply; 36+ messages in thread From: Jakub Jelinek @ 2016-06-03 7:06 UTC (permalink / raw) To: Martin Sebor; +Cc: Gcc Patch List, Jason Merrill, Joseph S. Myers Hi! On Thu, Jun 02, 2016 at 06:28:21PM -0600, Martin Sebor wrote: First of all, can you please respond to the mail I've sent about NULL argument issues (and proposal for __builtin_*_overflow_p)? This patch as well as the nonnull attribute patch then depends on that decision... > + { > + tree type = TREE_TYPE (TREE_TYPE (t)); > + tree vflow = arith_overflowed_p (opcode, type, arg0, arg1) > + ? integer_one_node : integer_zero_node; This looks incorrect, the return type is TREE_TYPE (t), some complex integer type, therefore vflow needs to be tree vflow = build_int_cst (TREE_TYPE (TREE_TYPE (t)), arith_overflowed_p (opcode, type, arg0, arg1) ? 1 : 0); no? Jakub ^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH] integer overflow checking builtins in constant expressions 2016-06-03 7:06 ` Jakub Jelinek @ 2016-06-03 15:29 ` Martin Sebor 2016-06-03 15:32 ` Jakub Jelinek 0 siblings, 1 reply; 36+ messages in thread From: Martin Sebor @ 2016-06-03 15:29 UTC (permalink / raw) To: Jakub Jelinek; +Cc: Gcc Patch List, Jason Merrill, Joseph S. Myers >> + { >> + tree type = TREE_TYPE (TREE_TYPE (t)); >> + tree vflow = arith_overflowed_p (opcode, type, arg0, arg1) >> + ? integer_one_node : integer_zero_node; > > This looks incorrect, the return type is TREE_TYPE (t), some complex integer > type, therefore vflow needs to be > tree vflow = build_int_cst (TREE_TYPE (TREE_TYPE (t)), > arith_overflowed_p (opcode, type, arg0, arg1) > ? 1 : 0); > no? I guess it didn't think it mattered since the complex type specifies the types of the two members. I don't mind changing it if it does but I'd like to have a test case to go with it. Maybe Jason can help with that. Martin ^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH] integer overflow checking builtins in constant expressions 2016-06-03 15:29 ` Martin Sebor @ 2016-06-03 15:32 ` Jakub Jelinek 2016-06-03 20:09 ` Martin Sebor 0 siblings, 1 reply; 36+ messages in thread From: Jakub Jelinek @ 2016-06-03 15:32 UTC (permalink / raw) To: Martin Sebor; +Cc: Gcc Patch List, Jason Merrill, Joseph S. Myers On Fri, Jun 03, 2016 at 09:29:48AM -0600, Martin Sebor wrote: > >>+ { > >>+ tree type = TREE_TYPE (TREE_TYPE (t)); > >>+ tree vflow = arith_overflowed_p (opcode, type, arg0, arg1) > >>+ ? integer_one_node : integer_zero_node; > > > >This looks incorrect, the return type is TREE_TYPE (t), some complex integer > >type, therefore vflow needs to be > > tree vflow = build_int_cst (TREE_TYPE (TREE_TYPE (t)), > > arith_overflowed_p (opcode, type, arg0, arg1) > > ? 1 : 0); > >no? > > I guess it didn't think it mattered since the complex type specifies > the types of the two members. I don't mind changing it if it does Sure, it does. But if there are any differences between the lhs and rhs type (or e.g. in COMPLEX_EXPR args etc. in GENERIC), then it is invalid IL, or for GIMPLE if the two types aren't compatible according to the GIMPLE rules (useless conversion). Jakub ^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH] integer overflow checking builtins in constant expressions 2016-06-03 15:32 ` Jakub Jelinek @ 2016-06-03 20:09 ` Martin Sebor 2016-06-06 12:36 ` Jakub Jelinek 0 siblings, 1 reply; 36+ messages in thread From: Martin Sebor @ 2016-06-03 20:09 UTC (permalink / raw) To: Jakub Jelinek; +Cc: Gcc Patch List, Jason Merrill, Joseph S. Myers [-- Attachment #1: Type: text/plain, Size: 1778 bytes --] On 06/03/2016 09:32 AM, Jakub Jelinek wrote: > On Fri, Jun 03, 2016 at 09:29:48AM -0600, Martin Sebor wrote: >>>> + { >>>> + tree type = TREE_TYPE (TREE_TYPE (t)); >>>> + tree vflow = arith_overflowed_p (opcode, type, arg0, arg1) >>>> + ? integer_one_node : integer_zero_node; >>> >>> This looks incorrect, the return type is TREE_TYPE (t), some complex integer >>> type, therefore vflow needs to be >>> tree vflow = build_int_cst (TREE_TYPE (TREE_TYPE (t)), >>> arith_overflowed_p (opcode, type, arg0, arg1) >>> ? 1 : 0); >>> no? >> >> I guess it didn't think it mattered since the complex type specifies >> the types of the two members. I don't mind changing it if it does > > Sure, it does. But if there are any differences between the lhs and rhs > type (or e.g. in COMPLEX_EXPR args etc. in GENERIC), then it is invalid IL, > or for GIMPLE if the two types aren't compatible according to the GIMPLE > rules (useless conversion). I see. I've made the change in the latest update to the patch but I wasn't able to create a test case to verify it. Maybe that's because this is constexpr the COMPLEX_EXPR doesn't make it far enough to trigger a problem. If there is a way to test it I'd appreciate a suggestion for how (otherwise, if not caught in a code review like in this case, it would be a ticking time bomb). It also occurred to me that a more robust solution might be to change build_complex to either enforce as a precondition that the members have a type that matches the complex type. I've taken the liberty of making this change as part of this patch. (It seems that an even better solution would be to have build_complex convert the arguments to the expected type so that callers don't have to worry about it.) Martin [-- Attachment #2: gcc-70507.patch --] [-- Type: text/x-patch, Size: 64313 bytes --] PR c++/70507 - integer overflow builtins not constant expressions PR c/68120 - can't easily deal with integer overflow at compile time gcc/cp/ChangeLog: 2016-06-03 Martin Sebor <msebor@redhat.com> PR c++/70507 PR c/68120 * constexpr.c (cxx_eval_internal_function): New function. (cxx_eval_call_expression): Call it. (potential_constant_expression_1): Handle integer arithmetic overflow built-ins. * tree.c (builtin_valid_in_constant_expr_p): Same. gcc/ChangeLog: 2016-06-03 Martin Sebor <msebor@redhat.com> PR c++/70507 PR c/68120 * builtins.c (fold_builtin_unordered_cmp): Handle integer arithmetic overflow built-ins. * tree.c (build_complex): Assert preconditions. * doc/extend.texi (Integer Overflow Builtins): Update. gcc/testsuite/ChangeLog: 2016-06-03 Martin Sebor <msebor@redhat.com> PR c++/70507 PR c/68120 * c-c++-common/builtin-arith-overflow-1.c: Add test cases. * c-c++-common/builtin-arith-overflow-2.c: New test. * g++.dg/cpp0x/constexpr-arith-overflow.C: New test. * g++.dg/cpp1y/constexpr-arith-overflow.C: New test. diff --git a/gcc/builtins.c b/gcc/builtins.c index 931d4a6..ada1904 100644 --- a/gcc/builtins.c +++ b/gcc/builtins.c @@ -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; @@ -7957,11 +7958,14 @@ 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; + switch (fcode) { case BUILT_IN_ADD_OVERFLOW: + opcode = PLUS_EXPR; case BUILT_IN_SADD_OVERFLOW: case BUILT_IN_SADDL_OVERFLOW: case BUILT_IN_SADDLL_OVERFLOW: @@ -7971,6 +7975,7 @@ fold_builtin_arith_overflow (location_t loc, enum built_in_function fcode, ifn = IFN_ADD_OVERFLOW; break; case BUILT_IN_SUB_OVERFLOW: + opcode = MINUS_EXPR; case BUILT_IN_SSUB_OVERFLOW: case BUILT_IN_SSUBL_OVERFLOW: case BUILT_IN_SSUBLL_OVERFLOW: @@ -7980,6 +7985,7 @@ fold_builtin_arith_overflow (location_t loc, enum built_in_function fcode, ifn = IFN_SUB_OVERFLOW; break; case BUILT_IN_MUL_OVERFLOW: + opcode = MULT_EXPR; case BUILT_IN_SMUL_OVERFLOW: case BUILT_IN_SMULL_OVERFLOW: case BUILT_IN_SMULLL_OVERFLOW: @@ -7991,6 +7997,26 @@ 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. */ + bool isnullp = integer_zerop (arg2); + tree type = TREE_TYPE (TREE_TYPE (arg2)); + + /* When the last argument to the type-generic built-in is a null pointer + and the first two arguments are constant, attempt to fold the built-in + call into a constant expression indicating whether or not it detected + an overflow but without storing the result. */ + if (opcode != ERROR_MARK && isnullp + && TREE_CODE (arg0) == INTEGER_CST && TREE_CODE (arg1) == INTEGER_CST) + { + /* Perform the computation in the target type and check for overflow. */ + return (arith_overflowed_p (opcode, type, arg0, arg1) + ? boolean_true_node : boolean_false_node); + } + tree ctype = build_complex_type (type); tree call = build_call_expr_internal_loc (loc, ifn, ctype, 2, arg0, arg1); @@ -7998,6 +8024,11 @@ fold_builtin_arith_overflow (location_t loc, enum built_in_function fcode, tree intres = build1_loc (loc, REALPART_EXPR, type, tgt); tree ovfres = build1_loc (loc, IMAGPART_EXPR, type, tgt); ovfres = fold_convert_loc (loc, boolean_type_node, ovfres); + + if (isnullp) + return ovfres; + + tree mem_arg2 = build_fold_indirect_ref_loc (loc, arg2); tree store = fold_build2_loc (loc, MODIFY_EXPR, void_type_node, mem_arg2, intres); return build2_loc (loc, COMPOUND_EXPR, boolean_type_node, store, ovfres); diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c index 482f8af..5d7da68 100644 --- a/gcc/cp/constexpr.c +++ b/gcc/cp/constexpr.c @@ -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,70 @@ 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) + { + if (tree result = size_binop_loc (EXPR_LOC_OR_LOC (t, input_location), + opcode, arg0, arg1)) + { + tree type = TREE_TYPE (TREE_TYPE (t)); + tree vflow = 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, vflow); + } + } + + *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 +1335,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 +4643,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 +4657,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 +4722,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. */; diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c index 04702ee..f9db199 100644 --- a/gcc/cp/tree.c +++ b/gcc/cp/tree.c @@ -352,6 +352,32 @@ builtin_valid_in_constant_expr_p (const_tree decl) case BUILT_IN_FUNCTION: case BUILT_IN_LINE: + /* The following built-ins are valid in constant expressions + when their arguments are. */ + case BUILT_IN_ADD_OVERFLOW: + case BUILT_IN_SADD_OVERFLOW: + case BUILT_IN_SADDL_OVERFLOW: + case BUILT_IN_SADDLL_OVERFLOW: + case BUILT_IN_UADD_OVERFLOW: + case BUILT_IN_UADDL_OVERFLOW: + case BUILT_IN_UADDLL_OVERFLOW: + + case BUILT_IN_SUB_OVERFLOW: + case BUILT_IN_SSUB_OVERFLOW: + case BUILT_IN_SSUBL_OVERFLOW: + case BUILT_IN_SSUBLL_OVERFLOW: + case BUILT_IN_USUB_OVERFLOW: + case BUILT_IN_USUBL_OVERFLOW: + case BUILT_IN_USUBLL_OVERFLOW: + + case BUILT_IN_MUL_OVERFLOW: + case BUILT_IN_SMUL_OVERFLOW: + case BUILT_IN_SMULL_OVERFLOW: + case BUILT_IN_SMULLL_OVERFLOW: + case BUILT_IN_UMUL_OVERFLOW: + case BUILT_IN_UMULL_OVERFLOW: + case BUILT_IN_UMULLL_OVERFLOW: + /* These have constant results even if their operands are non-constant. */ case BUILT_IN_CONSTANT_P: diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi index 2d4f028..d880821 100644 --- a/gcc/doc/extend.texi +++ b/gcc/doc/extend.texi @@ -9737,7 +9737,10 @@ compiler may also ignore this parameter. @section Built-in Functions to Perform Arithmetic with Overflow Checking The following built-in functions allow performing simple arithmetic operations -together with checking whether the operations overflowed. +together with checking whether the operations overflowed. The first of the +functions accepts either a pointer to an integer object or a null pointer +constant cast to the appropriate type as the last argument. The rest require +a pointer to an object of the specified type as the last argument. @deftypefn {Built-in Function} bool __builtin_add_overflow (@var{type1} a, @var{type2} b, @var{type3} *res) @deftypefnx {Built-in Function} bool __builtin_sadd_overflow (int a, int b, int *res) @@ -9747,21 +9750,50 @@ together with checking whether the operations overflowed. @deftypefnx {Built-in Function} bool __builtin_uaddl_overflow (unsigned long int a, unsigned long int b, unsigned long int *res) @deftypefnx {Built-in Function} bool __builtin_uaddll_overflow (unsigned long long int a, unsigned long long int b, unsigned long int *res) -These built-in functions promote the first two operands into infinite precision signed -type and perform addition on those promoted operands. The result is then -cast to the type the third pointer argument points to and stored there. -If the stored result is equal to the infinite precision result, the built-in -functions return false, otherwise they return true. As the addition is -performed in infinite signed precision, these built-in functions have fully defined -behavior for all argument values. - -The first built-in function allows arbitrary integral types for operands and -the result type must be pointer to some integer type, the rest of the built-in -functions have explicit integer types. +These built-in functions promote the first two operands into infinite precision +signed type and perform addition on those promoted operands. The result is then +converted to the type the third pointer argument points to, and for the first +function when the pointer is not null, stored there. If the converted result +is equal to the infinite precision result, the built-in functions return +@code{false}, otherwise they return @code{true} to indicate that an overflow +has been detected. Because the addition is performed in infinite precision, +these built-in functions have fully defined behavior for all argument values +and integer types. + +The first type-generic built-in function allows arbitrary integer types as +the first two arguments and requires that a pointer to some possibly distinct +integer type be passed to it as the third argument. The pointed-to type is +then used to determine the overflow. As a result, this built-in function +can be used to detect overflow in any arbitrary integer type, including +@code{char} and @code{short}. The remaining built-in functions take +arguments of explicit integer types and make it possible to determine +overflow only in the ranges of those types. Since the only provided forms +of these latter built-in functions are for the signed and unsigned variants +of types @code{int}, @code{long}, and @code{long long}, they cannot be used +to determine overflow in other integer types. + +To enable the efficient integer overflow detection at translation-time, +in C and C++11 and later programs (but not in C++98), the first built-in +may be invoked in constant integer expression contexts with a null pointer +cast to a pointer to the appropriate integer type as the third argument. +For example, the following macro can be used to portably check, at +compile-time, whether or not adding two constant integers will overflow, +and perform the addition only when it is known to be safe and not to trigger +a @option{-Woverflow} warning. + +@smallexample +#define INT_ADD_OVERFLOW(a, b) \ + __builtin_add_overflow (a, b, (__typeof__ ((a) + (b)) *) 0) + +enum @{ + A = INT_MAX, B = 3, + C = INT_ADD_OVERFLOW (A, B) ? 0 : A + B +@}; +@end smallexample -The compiler will attempt to use hardware instructions to implement -these built-in functions where possible, like conditional jump on overflow -after addition, conditional jump on carry etc. +For invocations of the built-in functions evaluated at run-time the compiler +will attempt to make use of efficient hardware instructions such as conditional +jump on overflow after addition, conditional jump on carry, etc. @end deftypefn @@ -9773,9 +9805,9 @@ after addition, conditional jump on carry etc. @deftypefnx {Built-in Function} bool __builtin_usubl_overflow (unsigned long int a, unsigned long int b, unsigned long int *res) @deftypefnx {Built-in Function} bool __builtin_usubll_overflow (unsigned long long int a, unsigned long long int b, unsigned long int *res) -These built-in functions are similar to the add overflow checking built-in -functions above, except they perform subtraction, subtract the second argument -from the first one, instead of addition. +These built-in functions are analogous to the add overflow checking built-in +functions above, except they subtract the second argument from the first one +rather than adding it to it. @end deftypefn @@ -9787,8 +9819,8 @@ from the first one, instead of addition. @deftypefnx {Built-in Function} bool __builtin_umull_overflow (unsigned long int a, unsigned long int b, unsigned long int *res) @deftypefnx {Built-in Function} bool __builtin_umulll_overflow (unsigned long long int a, unsigned long long int b, unsigned long int *res) -These built-in functions are similar to the add overflow checking built-in -functions above, except they perform multiplication, instead of addition. +These built-in functions are analogous to the add overflow checking built-in +functions above, except they perform multiplication rather than addition. @end deftypefn diff --git a/gcc/testsuite/c-c++-common/builtin-arith-overflow-1.c b/gcc/testsuite/c-c++-common/builtin-arith-overflow-1.c index 69b5083..f2be467 100644 --- a/gcc/testsuite/c-c++-common/builtin-arith-overflow-1.c +++ b/gcc/testsuite/c-c++-common/builtin-arith-overflow-1.c @@ -1,7 +1,10 @@ /* { dg-do compile } */ +/* Verify that calls with fewer or more than 3 arguments to the generic + __builtin_op_overflow functions are rejected. */ + int -f1 (void) +generic_0 (void) { int x = __builtin_add_overflow (); /* { dg-error "not enough arguments to function" } */ x += __builtin_sub_overflow (); /* { dg-error "not enough arguments to function" } */ @@ -10,14 +13,121 @@ f1 (void) } int -f2 (int a, int b, int *c, int d) +generic_1 (int a) +{ + int x = __builtin_add_overflow (a); /* { dg-error "not enough arguments to function" } */ + x += __builtin_sub_overflow (a); /* { dg-error "not enough arguments to function" } */ + x += __builtin_mul_overflow (a); /* { dg-error "not enough arguments to function" } */ + + /* Literal argument. */ + x += __builtin_add_overflow (1); /* { dg-error "not enough arguments to function" } */ + x += __builtin_sub_overflow (2); /* { dg-error "not enough arguments to function" } */ + x += __builtin_mul_overflow (3); /* { dg-error "not enough arguments to function" } */ + return x; +} + +int +generic_2 (int a, int b) +{ + int x = __builtin_add_overflow (a, b);/* { dg-error "not enough arguments to function" } */ + x += __builtin_sub_overflow (a, b); /* { dg-error "not enough arguments to function" } */ + x += __builtin_mul_overflow (a, b); /* { dg-error "not enough arguments to function" } */ + x += __builtin_add_overflow (a, 1); /* { dg-error "not enough arguments to function" } */ + x += __builtin_sub_overflow (a, 2); /* { dg-error "not enough arguments to function" } */ + x += __builtin_mul_overflow (a, 3); /* { dg-error "not enough arguments to function" } */ + x += __builtin_add_overflow (4, b); /* { dg-error "not enough arguments to function" } */ + x += __builtin_sub_overflow (5, b); /* { dg-error "not enough arguments to function" } */ + x += __builtin_mul_overflow (6, b); /* { dg-error "not enough arguments to function" } */ + return x; +} + +/* Verify that calls with the correct number of arguments to the generic + __builtin_op_overflow functions are accepted. */ + +int +generic_3 (int a, int b, int c) +{ + int x = __builtin_add_overflow (a, b, &c); + x += __builtin_sub_overflow (a, b, &c); + x += __builtin_mul_overflow (a, b, &c); + x += __builtin_add_overflow (a, 1, &c); + x += __builtin_sub_overflow (a, 2, &c); + x += __builtin_mul_overflow (a, 3, &c); + x += __builtin_add_overflow (4, b, &c); + x += __builtin_sub_overflow (5, b, &c); + x += __builtin_mul_overflow (6, b, &c); + x += __builtin_add_overflow (7, 8, &c); + x += __builtin_sub_overflow (9, 10, &c); + x += __builtin_mul_overflow (11, 12, &c); + return x; +} + +int +generic_4 (int a, int b, int *c, int d) { int x = __builtin_add_overflow (a, b, c, d); /* { dg-error "too many arguments to function" } */ - x += __builtin_sub_overflow (a, b, c, d, d, d); /* { dg-error "too many arguments to function" } */ + x += __builtin_sub_overflow (a, b, c, d); /* { dg-error "too many arguments to function" } */ x += __builtin_mul_overflow (a, b, c, d); /* { dg-error "too many arguments to function" } */ return x; } +/* Verify that calls with fewer or more than 3 arguments to the type + specific forms of the __builtin_op_overflow functions are rejected. */ + +int +generic_wrong_type (int a, int b) +{ + void *p = 0; + double d = 0; + int x = __builtin_add_overflow (a, b, p); /* { dg-error "does not have pointer to integer type" } */ + x += __builtin_sub_overflow (a, b, &p); /* { dg-error "does not have pointer to integer type" } */ + x += __builtin_mul_overflow (a, b, &d); /* { dg-error "does not have pointer to integer type" } */ + + /* Also verify literal arguments. */ + x += __builtin_add_overflow (1, 1, p); /* { dg-error "does not have pointer to integer type" } */ + x += __builtin_sub_overflow (1, 1, &p); /* { dg-error "does not have pointer to integer type" } */ + x += __builtin_mul_overflow (1, 1, &d); /* { dg-error "does not have pointer to integer type" } */ + return x; +} + +/* Verify that calls with fewer than 2 or more than 3 arguments to + the typed __builtin_op_overflow functions are rejected. */ +int +typed_0 (void) +{ + int x = __builtin_add_overflow (); /* { dg-error "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" } */ + return x; +} + +int +typed_1 (int a) +{ + int x = __builtin_sadd_overflow (a); /* { dg-error "too few arguments to function" } */ + x += __builtin_ssub_overflow (a); /* { dg-error "too few arguments to function" } */ + x += __builtin_smul_overflow (a); /* { dg-error "too few arguments to function" } */ + return x; +} + +int +typed_2 (int a, int b) +{ + int x = __builtin_sadd_overflow (a, b); /* { dg-error "too few arguments to function" } */ + x += __builtin_ssub_overflow (a, b); /* { dg-error "too few arguments to function" } */ + x += __builtin_smul_overflow (a, b); /* { dg-error "too few arguments to function" } */ + return x; +} + +int +typed_4 (int a, int b, int *c, int d) +{ + int x = __builtin_sadd_overflow (a, b, c, d); /* { dg-error "too many arguments to function" } */ + x += __builtin_ssub_overflow (a, b, c, d); /* { dg-error "too many arguments to function" } */ + x += __builtin_smul_overflow (a, b, c, d); /* { dg-error "too many arguments to function" } */ + return x; +} + enum E { e0 = 0, e1 = 1 }; #ifndef __cplusplus diff --git a/gcc/testsuite/c-c++-common/builtin-arith-overflow-2.c b/gcc/testsuite/c-c++-common/builtin-arith-overflow-2.c new file mode 100644 index 0000000..8707416 --- /dev/null +++ b/gcc/testsuite/c-c++-common/builtin-arith-overflow-2.c @@ -0,0 +1,501 @@ +/* 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. */ +#if !__cplusplus || __cplusplus >= 201103L + /* Perform both a run-time test followed by a compile-time test. */ +# define G_TEST(op, T, x, y, vflow) \ + RuntimeAssert(op, __typeof__ (op (x, y)), T, x, y, vflow); \ + StaticAssert ((vflow) == __builtin_ ## op ## _overflow ((x), (y), (T*)0)) +#else + /* C++ 98 doesn't permit casts to pointer types to appear in constant + expressions. Only perform the run-time test. */ +# define G_TEST(op, T, x, y, vflow) \ + RuntimeAssert(op, __typeof__ (op (x, y)), T, x, y, vflow) +#endif + + /* Addition. */ + G_TEST (add, signed char, 0, 0, 0); + G_TEST (add, signed char, 0, SCHAR_MAX, 0); + G_TEST (add, signed char, 1, SCHAR_MAX, 1); + G_TEST (add, signed char, SCHAR_MAX, SCHAR_MAX, 1); + G_TEST (add, signed char, 0, SCHAR_MIN, 0); + G_TEST (add, signed char, -1, SCHAR_MIN, 1); + /* 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; +} diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-arith-overflow.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-arith-overflow.C new file mode 100644 index 0000000..ec7e5a8 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-arith-overflow.C @@ -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 ( 1, 1, (int*)0), + a1 = __builtin_add_overflow (INT_MAX, 1, (int*)0) +}; + +Assert (a0 == 0); +Assert (a1 == 1); + +enum Sub { + s0 = __builtin_sub_overflow ( 1, 1, (int*)0), + s1 = __builtin_sub_overflow (INT_MIN, 1, (int*)0) +}; + +Assert (s0 == 0); +Assert (s1 == 1); + +enum Mul { + m0 = __builtin_add_overflow ( 1, 1, (int*)0), + m1 = __builtin_add_overflow (INT_MAX, INT_MAX, (int*)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 (x, y, (V*)0)> +struct Add { + Assert (z == v); +}; + +template <class T, class U, class V, + T x, U y, bool v, bool z = __builtin_sub_overflow (x, y, (V*)0)> +struct Sub { + Assert (z == v); +}; + +template <class T, class U, class V, + T x, U y, bool v, bool z = __builtin_mul_overflow (x, y, (V*)0)> +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 (a, b, (int*)0) }; +#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 diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-arith-overflow.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-arith-overflow.C new file mode 100644 index 0000000..7ca0033 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-arith-overflow.C @@ -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); diff --git a/gcc/tree.c b/gcc/tree.c index f4b470b..15cb373 100644 --- a/gcc/tree.c +++ b/gcc/tree.c @@ -2018,6 +2018,15 @@ build_string (int len, const char *str) tree build_complex (tree type, tree real, tree imag) { + if (type) + gcc_assert (TYPE_MAIN_VARIANT (TREE_TYPE (real)) + == TYPE_MAIN_VARIANT (TREE_TYPE (type)) + && TYPE_MAIN_VARIANT (TREE_TYPE (imag)) + == TYPE_MAIN_VARIANT (TREE_TYPE (type))); + else + gcc_assert (TYPE_MAIN_VARIANT (TREE_TYPE (real)) + == TYPE_MAIN_VARIANT (TREE_TYPE (imag))); + tree t = make_node (COMPLEX_CST); TREE_REALPART (t) = real; ^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH] integer overflow checking builtins in constant expressions 2016-06-03 20:09 ` Martin Sebor @ 2016-06-06 12:36 ` Jakub Jelinek 2016-06-06 19:44 ` Jakub Jelinek 2016-06-07 14:32 ` Jason Merrill 0 siblings, 2 replies; 36+ messages in thread From: Jakub Jelinek @ 2016-06-06 12:36 UTC (permalink / raw) To: Martin Sebor; +Cc: Gcc Patch List, Jason Merrill, Joseph S. Myers On Fri, Jun 03, 2016 at 02:09:44PM -0600, Martin Sebor wrote: > I see. I've made the change in the latest update to the patch > but I wasn't able to create a test case to verify it. Maybe > that's because this is constexpr the COMPLEX_EXPR doesn't make > it far enough to trigger a problem. If there is a way to test > it I'd appreciate a suggestion for how (otherwise, if not caught > in a code review like in this case, it would be a ticking time > bomb). As you haven't been willing to try the __builtin_*_overflow_p way, I've done that myself, it hasn't been really that hard. I've also found another bug, when computing the value that is stored in the constexpr function, you weren't converting the INTEGER_CST args to type, so you could get incorrect type of the result or other issues. > It also occurred to me that a more robust solution might be to > change build_complex to either enforce as a precondition that > the members have a type that matches the complex type. I've > taken the liberty of making this change as part of this patch. IMHO it doesn't belong to this patch, feel free to submit the tree.c change separately. > (It seems that an even better solution would be to have > build_complex convert the arguments to the expected type > so that callers don't have to worry about it.) I diagree with that, build_complex is a low-level function, it shouldn't perform such conversions. 2016-06-06 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. 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): Likewise. 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/cpp0x/constexpr-arith-overflow.C: New test. * g++.dg/cpp1y/constexpr-arith-overflow.C: New test. --- gcc/builtins.c.jj 2016-06-03 21:25:16.595678286 +0200 +++ gcc/builtins.c 2016-06-06 11:48:06.800100603 +0200 @@ -64,6 +64,7 @@ along with GCC; see the file COPYING3. #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 l /* 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 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 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 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 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 fnd 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: --- gcc/cp/constexpr.c.jj 2016-05-24 10:56:00.972164643 +0200 +++ gcc/cp/constexpr.c 2016-06-06 12:07:45.551788040 +0200 @@ -31,6 +31,7 @@ along with GCC; see the file COPYING3. #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 constexp 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, 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, 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, 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. */; --- gcc/cp/tree.c.jj 2016-05-13 22:23:03.024721307 +0200 +++ gcc/cp/tree.c 2016-06-06 11:54:23.675203508 +0200 @@ -352,6 +352,35 @@ builtin_valid_in_constant_expr_p (const_ case BUILT_IN_FUNCTION: case BUILT_IN_LINE: + /* The following built-ins are valid in constant expressions + when their arguments are. */ + case BUILT_IN_ADD_OVERFLOW: + case BUILT_IN_ADD_OVERFLOW_P: + case BUILT_IN_SADD_OVERFLOW: + case BUILT_IN_SADDL_OVERFLOW: + case BUILT_IN_SADDLL_OVERFLOW: + case BUILT_IN_UADD_OVERFLOW: + case BUILT_IN_UADDL_OVERFLOW: + case BUILT_IN_UADDLL_OVERFLOW: + + case BUILT_IN_SUB_OVERFLOW: + case BUILT_IN_SUB_OVERFLOW_P: + case BUILT_IN_SSUB_OVERFLOW: + case BUILT_IN_SSUBL_OVERFLOW: + case BUILT_IN_SSUBLL_OVERFLOW: + case BUILT_IN_USUB_OVERFLOW: + case BUILT_IN_USUBL_OVERFLOW: + case BUILT_IN_USUBLL_OVERFLOW: + + case BUILT_IN_MUL_OVERFLOW: + case BUILT_IN_MUL_OVERFLOW_P: + case BUILT_IN_SMUL_OVERFLOW: + case BUILT_IN_SMULL_OVERFLOW: + case BUILT_IN_SMULLL_OVERFLOW: + case BUILT_IN_UMUL_OVERFLOW: + case BUILT_IN_UMULL_OVERFLOW: + case BUILT_IN_UMULLL_OVERFLOW: + /* These have constant results even if their operands are non-constant. */ case BUILT_IN_CONSTANT_P: --- gcc/testsuite/c-c++-common/builtin-arith-overflow-1.c.jj 2014-11-12 13:28:47.188688801 +0100 +++ gcc/testsuite/c-c++-common/builtin-arith-overflow-1.c 2016-06-06 12:47:18.567054514 +0200 @@ -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 c 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; } --- gcc/testsuite/c-c++-common/builtin-arith-overflow-2.c.jj 2016-06-06 11:37:40.770240477 +0200 +++ gcc/testsuite/c-c++-common/builtin-arith-overflow-2.c 2016-06-06 12:52:32.419977756 +0200 @@ -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; +} --- gcc/testsuite/g++.dg/cpp1y/constexpr-arith-overflow.C.jj 2016-06-06 11:37:40.771240464 +0200 +++ gcc/testsuite/g++.dg/cpp1y/constexpr-arith-overflow.C 2016-06-06 13:55:24.551263688 +0200 @@ -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); --- gcc/testsuite/g++.dg/cpp0x/constexpr-arith-overflow.C.jj 2016-06-06 11:37:40.771240464 +0200 +++ gcc/testsuite/g++.dg/cpp0x/constexpr-arith-overflow.C 2016-06-06 13:01:55.928674227 +0200 @@ -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 --- gcc/c/c-typeck.c.jj 2016-06-03 21:25:15.000000000 +0200 +++ gcc/c/c-typeck.c 2016-06-06 13:40:18.588949985 +0200 @@ -3150,6 +3150,7 @@ convert_arguments (location_t loc, vec<l 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 @@ -3179,8 +3180,15 @@ convert_arguments (location_t loc, vec<l 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; } } @@ -3430,9 +3438,12 @@ convert_arguments (location_t loc, vec<l 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))) --- gcc/builtins.def.jj 2016-06-03 21:25:17.000000000 +0200 +++ gcc/builtins.def 2016-06-06 11:38:19.320739229 +0200 @@ -710,6 +710,9 @@ DEF_C94_BUILTIN (BUILT_IN_TOWUPPE 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_NOTHROW_TYPEGENERIC_LEAF) +DEF_GCC_BUILTIN (BUILT_IN_SUB_OVERFLOW_P, "sub_overflow_p", BT_FN_BOOL_VAR, ATTR_NOTHROW_TYPEGENERIC_LEAF) +DEF_GCC_BUILTIN (BUILT_IN_MUL_OVERFLOW_P, "mul_overflow_p", BT_FN_BOOL_VAR, ATTR_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) --- gcc/c-family/c-common.c.jj 2016-06-01 21:26:28.000000000 +0200 +++ gcc/c-family/c-common.c 2016-06-06 11:53:11.583140114 +0200 @@ -9966,6 +9966,23 @@ check_builtin_function_arguments (locati } 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; } --- gcc/doc/extend.texi.jj 2016-06-03 21:25:14.619703978 +0200 +++ gcc/doc/extend.texi 2016-06-06 14:02:12.956005088 +0200 @@ -9865,6 +9865,47 @@ functions above, except they perform mul @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 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 Jakub ^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH] integer overflow checking builtins in constant expressions 2016-06-06 12:36 ` Jakub Jelinek @ 2016-06-06 19:44 ` Jakub Jelinek 2016-06-07 9:12 ` Marek Polacek 2016-06-07 14:32 ` Jason Merrill 1 sibling, 1 reply; 36+ messages in thread From: Jakub Jelinek @ 2016-06-06 19:44 UTC (permalink / raw) To: Jason Merrill, Joseph S. Myers, Marek Polacek, Martin Sebor Cc: Gcc Patch List Hi! On Mon, Jun 06, 2016 at 02:36:17PM +0200, Jakub Jelinek wrote: > 2016-06-06 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. > 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): Likewise. > 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/cpp0x/constexpr-arith-overflow.C: New test. > * g++.dg/cpp1y/constexpr-arith-overflow.C: New test. Now successfully bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk? Jakub ^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH] integer overflow checking builtins in constant expressions 2016-06-06 19:44 ` Jakub Jelinek @ 2016-06-07 9:12 ` Marek Polacek 0 siblings, 0 replies; 36+ messages in thread From: Marek Polacek @ 2016-06-07 9:12 UTC (permalink / raw) To: Jakub Jelinek Cc: Jason Merrill, Joseph S. Myers, Martin Sebor, Gcc Patch List On Mon, Jun 06, 2016 at 09:44:08PM +0200, Jakub Jelinek wrote: > Hi! > > On Mon, Jun 06, 2016 at 02:36:17PM +0200, Jakub Jelinek wrote: > > 2016-06-06 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. > > 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): Likewise. > > 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/cpp0x/constexpr-arith-overflow.C: New test. > > * g++.dg/cpp1y/constexpr-arith-overflow.C: New test. > > Now successfully bootstrapped/regtested on x86_64-linux and i686-linux, ok > for trunk? I just played with this a bit -- nice. The c/ and c-family/ parts are OK. Marek ^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH] integer overflow checking builtins in constant expressions 2016-06-06 12:36 ` Jakub Jelinek 2016-06-06 19:44 ` Jakub Jelinek @ 2016-06-07 14:32 ` Jason Merrill 2016-06-07 15:51 ` Martin Sebor 1 sibling, 1 reply; 36+ messages in thread From: Jason Merrill @ 2016-06-07 14:32 UTC (permalink / raw) To: Jakub Jelinek, Martin Sebor; +Cc: Gcc Patch List, Joseph S. Myers On 06/06/2016 08:36 AM, Jakub Jelinek wrote: > @@ -352,6 +352,35 @@ builtin_valid_in_constant_expr_p (const_ > case BUILT_IN_FUNCTION: > case BUILT_IN_LINE: > > + /* The following built-ins are valid in constant expressions > + when their arguments are. */ > + case BUILT_IN_ADD_OVERFLOW: > + case BUILT_IN_ADD_OVERFLOW_P: .... Why is this necessary? builtin_valid_in_constant_expr_p is only needed for builtins that can have constant values even if their operands are non-constant, which doesn't apply here. For that matter, I don't see why we needed to add _FUNCTION et al, either. Jason ^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH] integer overflow checking builtins in constant expressions 2016-06-07 14:32 ` Jason Merrill @ 2016-06-07 15:51 ` Martin Sebor 2016-06-07 16:34 ` Jakub Jelinek 2016-06-07 19:36 ` Jason Merrill 0 siblings, 2 replies; 36+ messages in thread From: Martin Sebor @ 2016-06-07 15:51 UTC (permalink / raw) To: Jason Merrill, Jakub Jelinek; +Cc: Gcc Patch List, Joseph S. Myers On 06/07/2016 08:32 AM, Jason Merrill wrote: > On 06/06/2016 08:36 AM, Jakub Jelinek wrote: >> @@ -352,6 +352,35 @@ builtin_valid_in_constant_expr_p (const_ >> case BUILT_IN_FUNCTION: >> case BUILT_IN_LINE: >> >> + /* The following built-ins are valid in constant expressions >> + when their arguments are. */ >> + case BUILT_IN_ADD_OVERFLOW: >> + case BUILT_IN_ADD_OVERFLOW_P: > .... > > Why is this necessary? builtin_valid_in_constant_expr_p is only needed > for builtins that can have constant values even if their operands are > non-constant, which doesn't apply here. > > For that matter, I don't see why we needed to add _FUNCTION et al, either. I don't remember what prompted me to change the function in either patch. Perhaps it was the comment above the function that says this: /* Test whether DECL is a builtin that may appear in a constant-expression. */ But removing the change doesn't seem to affect the C++ test results for the two features so it looks like the change may not be needed. Unless I hear otherwise I'll submit a separate patch removing the BUILTIN_FILE, FUNCTION, and LINE labels (and adjust the comment to more closely reflect the function's purpose). I'll leave it to Jakub to tweak this patch. Martin ^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH] integer overflow checking builtins in constant expressions 2016-06-07 15:51 ` Martin Sebor @ 2016-06-07 16:34 ` Jakub Jelinek 2016-06-07 19:35 ` Jason Merrill 2016-06-07 19:36 ` Jason Merrill 1 sibling, 1 reply; 36+ messages in thread From: Jakub Jelinek @ 2016-06-07 16:34 UTC (permalink / raw) To: Jason Merrill, Martin Sebor; +Cc: Gcc Patch List, Joseph S. Myers On Tue, Jun 07, 2016 at 09:51:05AM -0600, Martin Sebor wrote: > On 06/07/2016 08:32 AM, Jason Merrill wrote: > >On 06/06/2016 08:36 AM, Jakub Jelinek wrote: > >>@@ -352,6 +352,35 @@ builtin_valid_in_constant_expr_p (const_ > >> case BUILT_IN_FUNCTION: > >> case BUILT_IN_LINE: > >> > >>+ /* The following built-ins are valid in constant expressions > >>+ when their arguments are. */ > >>+ case BUILT_IN_ADD_OVERFLOW: > >>+ case BUILT_IN_ADD_OVERFLOW_P: > >.... > > > >Why is this necessary? builtin_valid_in_constant_expr_p is only needed > >for builtins that can have constant values even if their operands are > >non-constant, which doesn't apply here. > > > >For that matter, I don't see why we needed to add _FUNCTION et al, either. > > I don't remember what prompted me to change the function in either > patch. Perhaps it was the comment above the function that says > this: > > /* Test whether DECL is a builtin that may appear in a > constant-expression. */ > > But removing the change doesn't seem to affect the C++ test results > for the two features so it looks like the change may not be needed. Indeed, confirming removal of the cp/tree.c hunk doesn't affect anything in the testsuite, nor e.g. 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); } That said, builtin_valid_in_constant_expr_p is used in lots of spots, and in various of them I see that without the change it actually sets *non_integral_constant_expression_p = true; or something similar, but I have no idea why it still works despite of that. Is the patch ok for trunk without the cp/tree.c hunk? Jakub ^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH] integer overflow checking builtins in constant expressions 2016-06-07 16:34 ` Jakub Jelinek @ 2016-06-07 19:35 ` Jason Merrill 2016-06-07 19:42 ` Jakub Jelinek 0 siblings, 1 reply; 36+ messages in thread From: Jason Merrill @ 2016-06-07 19:35 UTC (permalink / raw) To: Jakub Jelinek, Martin Sebor; +Cc: Gcc Patch List, Joseph S. Myers On 06/07/2016 12:34 PM, Jakub Jelinek wrote: > On Tue, Jun 07, 2016 at 09:51:05AM -0600, Martin Sebor wrote: >> On 06/07/2016 08:32 AM, Jason Merrill wrote: >>> On 06/06/2016 08:36 AM, Jakub Jelinek wrote: >>>> @@ -352,6 +352,35 @@ builtin_valid_in_constant_expr_p (const_ >>>> case BUILT_IN_FUNCTION: >>>> case BUILT_IN_LINE: >>>> >>>> + /* The following built-ins are valid in constant expressions >>>> + when their arguments are. */ >>>> + case BUILT_IN_ADD_OVERFLOW: >>>> + case BUILT_IN_ADD_OVERFLOW_P: >>> .... >>> >>> Why is this necessary? builtin_valid_in_constant_expr_p is only needed >>> for builtins that can have constant values even if their operands are >>> non-constant, which doesn't apply here. >>> >>> For that matter, I don't see why we needed to add _FUNCTION et al, either. >> >> I don't remember what prompted me to change the function in either >> patch. Perhaps it was the comment above the function that says >> this: >> >> /* Test whether DECL is a builtin that may appear in a >> constant-expression. */ >> >> But removing the change doesn't seem to affect the C++ test results >> for the two features so it looks like the change may not be needed. > > Indeed, confirming removal of the cp/tree.c hunk doesn't affect anything in > the testsuite, nor e.g. > 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); > } > That said, builtin_valid_in_constant_expr_p is used in lots of spots, and in > various of them I see that without the change it actually sets > *non_integral_constant_expression_p = true; > or something similar, but I have no idea why it still works despite of that. Ah, that's the C++98 constant expression handling, which is a lot more restricted. C++11 and up don't care about that flag. > Is the patch ok for trunk without the cp/tree.c hunk? Yes. Jason ^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH] integer overflow checking builtins in constant expressions 2016-06-07 19:35 ` Jason Merrill @ 2016-06-07 19:42 ` Jakub Jelinek 2016-06-07 20:30 ` Martin Sebor 0 siblings, 1 reply; 36+ messages in thread From: Jakub Jelinek @ 2016-06-07 19:42 UTC (permalink / raw) To: Jason Merrill; +Cc: Martin Sebor, Gcc Patch List, Joseph S. Myers On Tue, Jun 07, 2016 at 03:35:28PM -0400, Jason Merrill wrote: > >Indeed, confirming removal of the cp/tree.c hunk doesn't affect anything in > >the testsuite, nor e.g. > >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); > >} > >That said, builtin_valid_in_constant_expr_p is used in lots of spots, and in > >various of them I see that without the change it actually sets > >*non_integral_constant_expression_p = true; > >or something similar, but I have no idea why it still works despite of that. > > Ah, that's the C++98 constant expression handling, which is a lot more > restricted. C++11 and up don't care about that flag. Oops, actually, it seems even the cp/tree.c hunks are significant: I've only compiled the above testcase with the default -std=c++14, with -std=c++98 without the cp/tree.c bits I get: a.C:4:7: error: âbool __builtin_add_overflow_p(...)â cannot appear in a constant-expression D = __builtin_add_overflow_p (B, C, C) ^~~~~~~~~~~~~~~~~~~~~~~~ a.C:4:40: error: a function call cannot appear in a constant-expression D = __builtin_add_overflow_p (B, C, C) ^ a.C:6:45: error: array bound is not an integer constant before â]â token int e[__builtin_add_overflow_p (B, C, C) + 1]; ^ a.C: In function âvoid bar()â: a.C:11:8: error: âbool __builtin_add_overflow_p(...)â cannot appear in a constant-expression foo <__builtin_add_overflow_p (B, C, C) + 1> (0); ^~~~~~~~~~~~~~~~~~~~~~~~ a.C:11:41: error: a function call cannot appear in a constant-expression foo <__builtin_add_overflow_p (B, C, C) + 1> (0); ^ a.C:11:50: error: no matching function for call to âfoo(int)â foo <__builtin_add_overflow_p (B, C, C) + 1> (0); ^ a.C:7:22: note: candidate: template<int N> int foo(int) template <int N> int foo (int); ^~~ a.C:7:22: note: template argument deduction/substitution failed: a.C:11:50: error: template argument 1 is invalid foo <__builtin_add_overflow_p (B, C, C) + 1> (0); ^ Though, maybe it is only worth supporting the __builtin_*_overflow_p builtins for C++98 integer constant expression contexts and thus only handle there the 3 builtins instead of all the others. I guess I should add this testcase to the testsuite then. Jakub ^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH] integer overflow checking builtins in constant expressions 2016-06-07 19:42 ` Jakub Jelinek @ 2016-06-07 20:30 ` Martin Sebor 2016-06-07 20:52 ` Jakub Jelinek 0 siblings, 1 reply; 36+ messages in thread From: Martin Sebor @ 2016-06-07 20:30 UTC (permalink / raw) To: Jakub Jelinek, Jason Merrill; +Cc: Gcc Patch List, Joseph S. Myers On 06/07/2016 01:42 PM, Jakub Jelinek wrote: > On Tue, Jun 07, 2016 at 03:35:28PM -0400, Jason Merrill wrote: >>> Indeed, confirming removal of the cp/tree.c hunk doesn't affect anything in >>> the testsuite, nor e.g. >>> 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); >>> } >>> That said, builtin_valid_in_constant_expr_p is used in lots of spots, and in >>> various of them I see that without the change it actually sets >>> *non_integral_constant_expression_p = true; >>> or something similar, but I have no idea why it still works despite of that. >> >> Ah, that's the C++98 constant expression handling, which is a lot more >> restricted. C++11 and up don't care about that flag. > > Oops, actually, it seems even the cp/tree.c hunks are significant: > > I've only compiled the above testcase with the default -std=c++14, with > -std=c++98 without the cp/tree.c bits I get: > > a.C:4:7: error: âbool __builtin_add_overflow_p(...)â cannot appear in a > constant-expression > D = __builtin_add_overflow_p (B, C, C) > ^~~~~~~~~~~~~~~~~~~~~~~~ > a.C:4:40: error: a function call cannot appear in a constant-expression > D = __builtin_add_overflow_p (B, C, C) > ^ > a.C:6:45: error: array bound is not an integer constant before â]â token > int e[__builtin_add_overflow_p (B, C, C) + 1]; > ^ > a.C: In function âvoid bar()â: > a.C:11:8: error: âbool __builtin_add_overflow_p(...)â cannot appear in a > constant-expression > foo <__builtin_add_overflow_p (B, C, C) + 1> (0); > ^~~~~~~~~~~~~~~~~~~~~~~~ > a.C:11:41: error: a function call cannot appear in a constant-expression > foo <__builtin_add_overflow_p (B, C, C) + 1> (0); > ^ > a.C:11:50: error: no matching function for call to âfoo(int)â > foo <__builtin_add_overflow_p (B, C, C) + 1> (0); > ^ > a.C:7:22: note: candidate: template<int N> int foo(int) > template <int N> int foo (int); > ^~~ > a.C:7:22: note: template argument deduction/substitution failed: > a.C:11:50: error: template argument 1 is invalid > foo <__builtin_add_overflow_p (B, C, C) + 1> (0); > ^ > > Though, maybe it is only worth supporting the __builtin_*_overflow_p > builtins for C++98 integer constant expression contexts and > thus only handle there the 3 builtins instead of all the others. > > I guess I should add this testcase to the testsuite then. I've also since discovered that the handling of (at least) BUILTIN_LINE is important in C++ 98 mode and if I had run the c-c++-common tests instead of just the strict C++ subset I would have seen the c-c++-common/builtin_location.c test fail with the same error. I suspect I added the other built-ins to the function to get the Clang-compatibility typed built-ins to work in C++ 98 (with the default argument). When it was decided that the Clang built-ins shouldn't change I removed the tests but not this change. Martin ^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH] integer overflow checking builtins in constant expressions 2016-06-07 20:30 ` Martin Sebor @ 2016-06-07 20:52 ` Jakub Jelinek 2016-06-07 21:56 ` Martin Sebor 0 siblings, 1 reply; 36+ messages in thread From: Jakub Jelinek @ 2016-06-07 20:52 UTC (permalink / raw) To: Jason Merrill, Martin Sebor; +Cc: Gcc Patch List, Joseph S. Myers On Tue, Jun 07, 2016 at 02:30:06PM -0600, Martin Sebor wrote: > I've also since discovered that the handling of (at least) > BUILTIN_LINE is important in C++ 98 mode and if I had run > the c-c++-common tests instead of just the strict C++ subset > I would have seen the c-c++-common/builtin_location.c test > fail with the same error. > > I suspect I added the other built-ins to the function to get > the Clang-compatibility typed built-ins to work in C++ 98 > (with the default argument). When it was decided that > the Clang built-ins shouldn't change I removed the tests > but not this change. Here is the updated patch, with the extra testcase (the previous C++ only testcases were only c++11 and c++14 effective targets) and the BUILTIN_{ADD,SUB,MUL}_OVERFLOW_P kept in cp/tree.c. Tested on x86_64-linux, ok for trunk? 2016-06-07 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. 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. --- gcc/builtins.def.jj 2016-06-06 14:40:35.619347198 +0200 +++ gcc/builtins.def 2016-06-07 17:46:52.034206039 +0200 @@ -710,6 +710,9 @@ DEF_C94_BUILTIN (BUILT_IN_TOWUPPE 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_NOTHROW_TYPEGENERIC_LEAF) +DEF_GCC_BUILTIN (BUILT_IN_SUB_OVERFLOW_P, "sub_overflow_p", BT_FN_BOOL_VAR, ATTR_NOTHROW_TYPEGENERIC_LEAF) +DEF_GCC_BUILTIN (BUILT_IN_MUL_OVERFLOW_P, "mul_overflow_p", BT_FN_BOOL_VAR, ATTR_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) --- gcc/builtins.c.jj 2016-06-06 14:40:35.601347427 +0200 +++ gcc/builtins.c 2016-06-07 17:46:51.958207014 +0200 @@ -64,6 +64,7 @@ along with GCC; see the file COPYING3. #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 l /* 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 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 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 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 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 fnd 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: --- gcc/doc/extend.texi.jj 2016-06-06 14:40:36.365337676 +0200 +++ gcc/doc/extend.texi 2016-06-07 17:46:52.063205668 +0200 @@ -9869,6 +9869,47 @@ functions above, except they perform mul @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 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 --- gcc/c/c-typeck.c.jj 2016-06-07 17:44:04.308356420 +0200 +++ gcc/c/c-typeck.c 2016-06-07 17:46:52.033206052 +0200 @@ -3186,6 +3186,7 @@ convert_arguments (location_t loc, vec<l 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<l 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<l 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))) --- gcc/cp/constexpr.c.jj 2016-06-06 14:40:35.597347478 +0200 +++ gcc/cp/constexpr.c 2016-06-07 17:46:51.960206988 +0200 @@ -31,6 +31,7 @@ along with GCC; see the file COPYING3. #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 constexp 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, 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, 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, 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. */; --- gcc/cp/tree.c.jj 2016-06-06 14:40:35.544348155 +0200 +++ gcc/cp/tree.c 2016-06-07 22:45:06.431850707 +0200 @@ -352,6 +352,12 @@ builtin_valid_in_constant_expr_p (const_ 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: --- gcc/c-family/c-common.c.jj 2016-06-07 14:56:42.008578592 +0200 +++ gcc/c-family/c-common.c 2016-06-07 17:46:52.038205988 +0200 @@ -9989,6 +9989,23 @@ check_builtin_function_arguments (locati } 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; } --- gcc/testsuite/c-c++-common/builtin-arith-overflow-1.c.jj 2016-06-06 14:40:35.695346228 +0200 +++ gcc/testsuite/c-c++-common/builtin-arith-overflow-1.c 2016-06-07 17:46:52.028206116 +0200 @@ -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 c 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; } --- gcc/testsuite/c-c++-common/builtin-arith-overflow-2.c.jj 2016-06-07 17:46:52.028206116 +0200 +++ gcc/testsuite/c-c++-common/builtin-arith-overflow-2.c 2016-06-07 17:46:52.028206116 +0200 @@ -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; +} --- gcc/testsuite/g++.dg/ext/builtin-arith-overflow-1.C.jj 2016-06-07 22:43:01.601501755 +0200 +++ gcc/testsuite/g++.dg/ext/builtin-arith-overflow-1.C 2016-06-07 22:43:01.601501755 +0200 @@ -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); +} --- gcc/testsuite/g++.dg/cpp0x/constexpr-arith-overflow.C.jj 2016-06-07 17:46:52.029206104 +0200 +++ gcc/testsuite/g++.dg/cpp0x/constexpr-arith-overflow.C 2016-06-07 17:46:52.029206104 +0200 @@ -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 --- gcc/testsuite/g++.dg/cpp1y/constexpr-arith-overflow.C.jj 2016-06-07 17:46:52.029206104 +0200 +++ gcc/testsuite/g++.dg/cpp1y/constexpr-arith-overflow.C 2016-06-07 17:46:52.029206104 +0200 @@ -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); Jakub ^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH] integer overflow checking builtins in constant expressions 2016-06-07 20:52 ` Jakub Jelinek @ 2016-06-07 21:56 ` Martin Sebor 2016-06-08 7:23 ` Jakub Jelinek 0 siblings, 1 reply; 36+ messages in thread From: Martin Sebor @ 2016-06-07 21:56 UTC (permalink / raw) To: Jakub Jelinek, Jason Merrill; +Cc: Gcc Patch List, Joseph S. Myers > +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 the third argument. The above is missing an "of" (it should read "type of the third argument".) Martin ^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH] integer overflow checking builtins in constant expressions 2016-06-07 21:56 ` Martin Sebor @ 2016-06-08 7:23 ` Jakub Jelinek 2016-06-08 18:38 ` Jason Merrill 0 siblings, 1 reply; 36+ messages in thread From: Jakub Jelinek @ 2016-06-08 7:23 UTC (permalink / raw) To: Jason Merrill, Martin Sebor; +Cc: Gcc Patch List, Joseph S. Myers On Tue, Jun 07, 2016 at 03:56:43PM -0600, Martin Sebor wrote: > >+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 the third argument. > > The above is missing an "of" (it should read "type of the third > argument".) Thanks for spotting that, fixed below. Additionally, I've noticed that ATTR_NOTHROW_TYPEGENERIC_LEAF for the 3 new builtins is wrong, unlike the other overflow builtins, the *_p ones are also const. It shouldn't change much, because we lower it very early to the internal fns that are const, but at least from the POV of the FEs it describes the builtins properly. Ok for trunk? 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. 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. --- gcc/builtins.def.jj 2016-06-06 14:40:35.619347198 +0200 +++ gcc/builtins.def 2016-06-07 17:46:52.034206039 +0200 @@ -710,6 +710,9 @@ DEF_C94_BUILTIN (BUILT_IN_TOWUPPE 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) --- gcc/builtins.c.jj 2016-06-06 14:40:35.601347427 +0200 +++ gcc/builtins.c 2016-06-07 17:46:51.958207014 +0200 @@ -64,6 +64,7 @@ along with GCC; see the file COPYING3. #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 l /* 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 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 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 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 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 fnd 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: --- gcc/doc/extend.texi.jj 2016-06-06 14:40:36.365337676 +0200 +++ gcc/doc/extend.texi 2016-06-07 17:46:52.063205668 +0200 @@ -9869,6 +9869,47 @@ functions above, except they perform mul @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 --- gcc/c/c-typeck.c.jj 2016-06-07 17:44:04.308356420 +0200 +++ gcc/c/c-typeck.c 2016-06-07 17:46:52.033206052 +0200 @@ -3186,6 +3186,7 @@ convert_arguments (location_t loc, vec<l 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<l 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<l 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))) --- gcc/cp/constexpr.c.jj 2016-06-06 14:40:35.597347478 +0200 +++ gcc/cp/constexpr.c 2016-06-07 17:46:51.960206988 +0200 @@ -31,6 +31,7 @@ along with GCC; see the file COPYING3. #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 constexp 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, 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, 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, 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. */; --- gcc/cp/tree.c.jj 2016-06-06 14:40:35.544348155 +0200 +++ gcc/cp/tree.c 2016-06-07 22:45:06.431850707 +0200 @@ -352,6 +352,12 @@ builtin_valid_in_constant_expr_p (const_ 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: --- gcc/c-family/c-common.c.jj 2016-06-07 14:56:42.008578592 +0200 +++ gcc/c-family/c-common.c 2016-06-07 17:46:52.038205988 +0200 @@ -9989,6 +9989,23 @@ check_builtin_function_arguments (locati } 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; } --- gcc/testsuite/c-c++-common/builtin-arith-overflow-1.c.jj 2016-06-06 14:40:35.695346228 +0200 +++ gcc/testsuite/c-c++-common/builtin-arith-overflow-1.c 2016-06-07 17:46:52.028206116 +0200 @@ -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 c 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; } --- gcc/testsuite/c-c++-common/builtin-arith-overflow-2.c.jj 2016-06-07 17:46:52.028206116 +0200 +++ gcc/testsuite/c-c++-common/builtin-arith-overflow-2.c 2016-06-07 17:46:52.028206116 +0200 @@ -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; +} --- gcc/testsuite/g++.dg/ext/builtin-arith-overflow-1.C.jj 2016-06-07 22:43:01.601501755 +0200 +++ gcc/testsuite/g++.dg/ext/builtin-arith-overflow-1.C 2016-06-07 22:43:01.601501755 +0200 @@ -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); +} --- gcc/testsuite/g++.dg/cpp0x/constexpr-arith-overflow.C.jj 2016-06-07 17:46:52.029206104 +0200 +++ gcc/testsuite/g++.dg/cpp0x/constexpr-arith-overflow.C 2016-06-07 17:46:52.029206104 +0200 @@ -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 --- gcc/testsuite/g++.dg/cpp1y/constexpr-arith-overflow.C.jj 2016-06-07 17:46:52.029206104 +0200 +++ gcc/testsuite/g++.dg/cpp1y/constexpr-arith-overflow.C 2016-06-07 17:46:52.029206104 +0200 @@ -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); Jakub ^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH] integer overflow checking builtins in constant expressions 2016-06-08 7:23 ` Jakub Jelinek @ 2016-06-08 18:38 ` Jason Merrill 0 siblings, 0 replies; 36+ messages in thread From: Jason Merrill @ 2016-06-08 18:38 UTC (permalink / raw) To: Jakub Jelinek, Martin Sebor; +Cc: Gcc Patch List, Joseph S. Myers OK. Jason ^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH] integer overflow checking builtins in constant expressions 2016-06-07 15:51 ` Martin Sebor 2016-06-07 16:34 ` Jakub Jelinek @ 2016-06-07 19:36 ` Jason Merrill 1 sibling, 0 replies; 36+ messages in thread From: Jason Merrill @ 2016-06-07 19:36 UTC (permalink / raw) To: Martin Sebor, Jakub Jelinek; +Cc: Gcc Patch List, Joseph S. Myers On 06/07/2016 11:51 AM, Martin Sebor wrote: > Unless I hear otherwise I'll submit a separate patch removing > the BUILTIN_FILE, FUNCTION, and LINE labels (and adjust the > comment to more closely reflect the function's purpose). Since Jakub pointed out that this function is used by the parser for C++98 constant-expressions, I think we can leave these labels alone. A comment tweak wouldn't hurt, though. Jason ^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH] integer overflow checking builtins in constant expressions [not found] ` <574E13DD.9040401@gmail.com> 2016-06-01 7:52 ` Jakub Jelinek @ 2016-06-01 18:46 ` Jason Merrill 1 sibling, 0 replies; 36+ messages in thread From: Jason Merrill @ 2016-06-01 18:46 UTC (permalink / raw) To: Martin Sebor, Jakub Jelinek; +Cc: Gcc Patch List, Joseph S. Myers On 05/31/2016 06:44 PM, Martin Sebor wrote: > Without this change, C or C++ 98 users wouldn't be able to use the > built-ins in constant expressions (C++ 98 doesn't allow > casting a null pointer in those contexts). I can't imagine that C++98 users care about using these built-ins in constant expressions. Jason ^ permalink raw reply [flat|nested] 36+ messages in thread
end of thread, other threads:[~2016-06-08 18:38 UTC | newest] Thread overview: 36+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2016-05-01 16:40 [PATCH] integer overflow checking builtins in constant expressions Martin Sebor 2016-05-09 16:39 ` PING " Martin Sebor 2016-05-16 19:30 ` PING 2 " Martin Sebor 2016-05-17 18:54 ` Jason Merrill 2016-05-31 23:43 ` Martin Sebor [not found] ` <20160531215025.GK28550@tucnak.redhat.com> [not found] ` <574E13DD.9040401@gmail.com> 2016-06-01 7:52 ` Jakub Jelinek 2016-06-01 15:17 ` Martin Sebor 2016-06-01 15:45 ` Jakub Jelinek 2016-06-01 16:13 ` Martin Sebor 2016-06-02 3:11 ` Martin Sebor 2016-06-02 7:23 ` Jakub Jelinek 2016-06-02 7:34 ` Jakub Jelinek 2016-06-03 15:07 ` Martin Sebor 2016-06-03 15:23 ` Jakub Jelinek 2016-06-03 16:22 ` Martin Sebor 2016-06-02 23:21 ` Martin Sebor 2016-06-03 0:28 ` Martin Sebor 2016-06-03 7:06 ` Jakub Jelinek 2016-06-03 15:29 ` Martin Sebor 2016-06-03 15:32 ` Jakub Jelinek 2016-06-03 20:09 ` Martin Sebor 2016-06-06 12:36 ` Jakub Jelinek 2016-06-06 19:44 ` Jakub Jelinek 2016-06-07 9:12 ` Marek Polacek 2016-06-07 14:32 ` Jason Merrill 2016-06-07 15:51 ` Martin Sebor 2016-06-07 16:34 ` Jakub Jelinek 2016-06-07 19:35 ` Jason Merrill 2016-06-07 19:42 ` Jakub Jelinek 2016-06-07 20:30 ` Martin Sebor 2016-06-07 20:52 ` Jakub Jelinek 2016-06-07 21:56 ` Martin Sebor 2016-06-08 7:23 ` Jakub Jelinek 2016-06-08 18:38 ` Jason Merrill 2016-06-07 19:36 ` Jason Merrill 2016-06-01 18:46 ` Jason Merrill
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).