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++.
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 <mse...@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 <mse...@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 <mse...@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 <mse...@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; }