re PR c/59708 (clang-compatible checked arithmetic builtins)
PR c/59708 * builtin-attrs.def (ATTR_NOTHROW_TYPEGENERIC_LEAF): New attribute. * builtins.c (fold_builtin_arith_overflow): New function. (fold_builtin_3): Use it. * builtins.def (BUILT_IN_ADD_OVERFLOW, BUILT_IN_SUB_OVERFLOW, BUILT_IN_MUL_OVERFLOW, BUILT_IN_SADD_OVERFLOW, BUILT_IN_SADDL_OVERFLOW, BUILT_IN_SADDLL_OVERFLOW, BUILT_IN_SSUB_OVERFLOW, BUILT_IN_SSUBL_OVERFLOW, BUILT_IN_SSUBLL_OVERFLOW, BUILT_IN_SMUL_OVERFLOW, BUILT_IN_SMULL_OVERFLOW, BUILT_IN_SMULLL_OVERFLOW, BUILT_IN_UADDL_OVERFLOW, BUILT_IN_UADDLL_OVERFLOW, BUILT_IN_USUB_OVERFLOW, BUILT_IN_USUBL_OVERFLOW, BUILT_IN_USUBLL_OVERFLOW, BUILT_IN_UMUL_OVERFLOW, BUILT_IN_UMULL_OVERFLOW, BUILT_IN_UMULLL_OVERFLOW): New built-in functions. * builtin-types.def (BT_PTR_UINT, BT_PTR_ULONG, BT_PTR_LONGLONG, BT_FN_BOOL_INT_INT_INTPTR, BT_FN_BOOL_LONG_LONG_LONGPTR, BT_FN_BOOL_LONGLONG_LONGLONG_LONGLONGPTR, BT_FN_BOOL_UINT_UINT_UINTPTR, BT_FN_BOOL_ULONG_ULONG_ULONGPTR, BT_FN_BOOL_ULONGLONG_ULONGLONG_ULONGLONGPTR, BT_FN_BOOL_VAR): New. * expr.c (write_complex_part): Remove prototype, no longer static. * expr.h (write_complex_part): New prototype. * function.c (aggregate_value_p): For internal functions return 0. * gimple-fold.c (arith_overflowed_p): New functions. (gimple_fold_call): Fold {ADD,SUB,MUL}_OVERFLOW internal calls. * gimple-fold.h (arith_overflowed_p): New prototype. * tree-ssa-dce.c: Include tree-ssa-propagate.h and gimple-fold.h. (find_non_realpart_uses, maybe_optimize_arith_overflow): New functions. (eliminate_unnecessary_stmts): Transform {ADD,SUB,MUL}_OVERFLOW into COMPLEX_CST/COMPLEX_EXPR if IMAGPART_EXPR of the result is never used. * gimplify.c (gimplify_call_expr): Handle gimplification of internal calls with lhs. * internal-fn.c (get_range_pos_neg, get_min_precision, expand_arith_overflow_result_store): New functions. (ubsan_expand_si_overflow_addsub_check): Renamed to ... (expand_addsub_overflow): ... this. Add LOC, LHS, ARG0, ARG1, UNSR_P, UNS0_P, UNS1_P, IS_UBSAN arguments, remove STMT argument. Handle ADD_OVERFLOW and SUB_OVERFLOW expansion. (ubsan_expand_si_overflow_neg_check): Renamed to ... (expand_neg_overflow): ... this. Add LOC, LHS, ARG1, IS_UBSAN arguments, remove STMT argument. Handle SUB_OVERFLOW with 0 as first argument expansion. (ubsan_expand_si_overflow_mul_check): Renamed to ... (expand_mul_overflow): ... this. Add LOC, LHS, ARG0, ARG1, UNSR_P, UNS0_P, UNS1_P, IS_UBSAN arguments, remove STMT argument. Handle MUL_OVERFLOW expansion. (expand_UBSAN_CHECK_ADD): Use expand_addsub_overflow, prepare arguments for it. (expand_UBSAN_CHECK_SUB): Use expand_addsub_overflow or expand_neg_overflow, prepare arguments for it. (expand_UBSAN_CHECK_MUL): Use expand_mul_overflow, prepare arguments for it. (expand_arith_overflow, expand_ADD_OVERFLOW, expand_SUB_OVERFLOW, expand_MUL_OVERFLOW): New functions. * internal-fn.def (ADD_OVERFLOW, SUB_OVERFLOW, MUL_OVERFLOW): New internal functions. * tree-vrp.c (check_for_binary_op_overflow): New function. (extract_range_basic): Handle {REAL,IMAG}PART_EXPR if the operand is SSA_NAME set by {ADD,SUB,MUL}_OVERFLOW internal functions. (simplify_internal_call_using_ranges): Handle {ADD,SUB,MUL}_OVERFLOW internal functions. * optabs.def (umulv4_optab): New optab. * config/i386/i386.md (umulv<mode>4, <u>mulvqi4): New define_expands. (*umulv<mode>4, *<u>mulvqi4): New define_insns. * doc/extend.texi (Integer Overflow Builtins): Document __builtin_*_overflow. c-family/ * c-common.c (check_builtin_function_arguments): Handle BUILT_IN_{ADD,SUB,MUL}_OVERFLOW. testsuite/ * c-c++-common/builtin-arith-overflow-1.c: New test. * c-c++-common/torture/builtin-arith-overflow-10.c: New test. * c-c++-common/torture/builtin-arith-overflow-11.c: New test. * c-c++-common/torture/builtin-arith-overflow-12.c: New test. * c-c++-common/torture/builtin-arith-overflow-12.h: New file. * c-c++-common/torture/builtin-arith-overflow-13.c: New test. * c-c++-common/torture/builtin-arith-overflow-14.c: New test. * c-c++-common/torture/builtin-arith-overflow-15.c: New test. * c-c++-common/torture/builtin-arith-overflow-16.c: New test. * c-c++-common/torture/builtin-arith-overflow-17.c: New test. * c-c++-common/torture/builtin-arith-overflow-18.c: New test. * c-c++-common/torture/builtin-arith-overflow-1.c: New test. * c-c++-common/torture/builtin-arith-overflow-1.h: New file. * c-c++-common/torture/builtin-arith-overflow-2.c: New test. * c-c++-common/torture/builtin-arith-overflow-3.c: New test. * c-c++-common/torture/builtin-arith-overflow-4.c: New test. * c-c++-common/torture/builtin-arith-overflow-5.c: New test. * c-c++-common/torture/builtin-arith-overflow-6.c: New test. * c-c++-common/torture/builtin-arith-overflow-7.c: New test. * c-c++-common/torture/builtin-arith-overflow-8.c: New test. * c-c++-common/torture/builtin-arith-overflow-9.c: New test. * c-c++-common/torture/builtin-arith-overflow.h: New file. * gcc.dg/builtin-arith-overflow-1.c: New test. * gcc.dg/builtin-arith-overflow-2.c: New test. From-SVN: r217415
This commit is contained in:
parent
6a3cbe9092
commit
1304953e4f
|
@ -1,3 +1,73 @@
|
|||
2014-11-12 Jakub Jelinek <jakub@redhat.com>
|
||||
|
||||
PR c/59708
|
||||
* builtin-attrs.def (ATTR_NOTHROW_TYPEGENERIC_LEAF): New attribute.
|
||||
* builtins.c (fold_builtin_arith_overflow): New function.
|
||||
(fold_builtin_3): Use it.
|
||||
* builtins.def (BUILT_IN_ADD_OVERFLOW, BUILT_IN_SUB_OVERFLOW,
|
||||
BUILT_IN_MUL_OVERFLOW, BUILT_IN_SADD_OVERFLOW, BUILT_IN_SADDL_OVERFLOW,
|
||||
BUILT_IN_SADDLL_OVERFLOW, BUILT_IN_SSUB_OVERFLOW,
|
||||
BUILT_IN_SSUBL_OVERFLOW, BUILT_IN_SSUBLL_OVERFLOW,
|
||||
BUILT_IN_SMUL_OVERFLOW, BUILT_IN_SMULL_OVERFLOW,
|
||||
BUILT_IN_SMULLL_OVERFLOW, BUILT_IN_UADDL_OVERFLOW,
|
||||
BUILT_IN_UADDLL_OVERFLOW, BUILT_IN_USUB_OVERFLOW,
|
||||
BUILT_IN_USUBL_OVERFLOW, BUILT_IN_USUBLL_OVERFLOW,
|
||||
BUILT_IN_UMUL_OVERFLOW, BUILT_IN_UMULL_OVERFLOW,
|
||||
BUILT_IN_UMULLL_OVERFLOW): New built-in functions.
|
||||
* builtin-types.def (BT_PTR_UINT, BT_PTR_ULONG, BT_PTR_LONGLONG,
|
||||
BT_FN_BOOL_INT_INT_INTPTR, BT_FN_BOOL_LONG_LONG_LONGPTR,
|
||||
BT_FN_BOOL_LONGLONG_LONGLONG_LONGLONGPTR, BT_FN_BOOL_UINT_UINT_UINTPTR,
|
||||
BT_FN_BOOL_ULONG_ULONG_ULONGPTR,
|
||||
BT_FN_BOOL_ULONGLONG_ULONGLONG_ULONGLONGPTR, BT_FN_BOOL_VAR): New.
|
||||
* expr.c (write_complex_part): Remove prototype, no longer static.
|
||||
* expr.h (write_complex_part): New prototype.
|
||||
* function.c (aggregate_value_p): For internal functions return 0.
|
||||
* gimple-fold.c (arith_overflowed_p): New functions.
|
||||
(gimple_fold_call): Fold {ADD,SUB,MUL}_OVERFLOW internal calls.
|
||||
* gimple-fold.h (arith_overflowed_p): New prototype.
|
||||
* tree-ssa-dce.c: Include tree-ssa-propagate.h and gimple-fold.h.
|
||||
(find_non_realpart_uses, maybe_optimize_arith_overflow): New
|
||||
functions.
|
||||
(eliminate_unnecessary_stmts): Transform {ADD,SUB,MUL}_OVERFLOW
|
||||
into COMPLEX_CST/COMPLEX_EXPR if IMAGPART_EXPR of the result is
|
||||
never used.
|
||||
* gimplify.c (gimplify_call_expr): Handle gimplification of
|
||||
internal calls with lhs.
|
||||
* internal-fn.c (get_range_pos_neg, get_min_precision,
|
||||
expand_arith_overflow_result_store): New functions.
|
||||
(ubsan_expand_si_overflow_addsub_check): Renamed to ...
|
||||
(expand_addsub_overflow): ... this. Add LOC, LHS, ARG0, ARG1,
|
||||
UNSR_P, UNS0_P, UNS1_P, IS_UBSAN arguments, remove STMT argument.
|
||||
Handle ADD_OVERFLOW and SUB_OVERFLOW expansion.
|
||||
(ubsan_expand_si_overflow_neg_check): Renamed to ...
|
||||
(expand_neg_overflow): ... this. Add LOC, LHS, ARG1, IS_UBSAN
|
||||
arguments, remove STMT argument. Handle SUB_OVERFLOW with
|
||||
0 as first argument expansion.
|
||||
(ubsan_expand_si_overflow_mul_check): Renamed to ...
|
||||
(expand_mul_overflow): ... this. Add LOC, LHS, ARG0, ARG1,
|
||||
UNSR_P, UNS0_P, UNS1_P, IS_UBSAN arguments, remove STMT argument.
|
||||
Handle MUL_OVERFLOW expansion.
|
||||
(expand_UBSAN_CHECK_ADD): Use expand_addsub_overflow, prepare
|
||||
arguments for it.
|
||||
(expand_UBSAN_CHECK_SUB): Use expand_addsub_overflow or
|
||||
expand_neg_overflow, prepare arguments for it.
|
||||
(expand_UBSAN_CHECK_MUL): Use expand_mul_overflow, prepare arguments
|
||||
for it.
|
||||
(expand_arith_overflow, expand_ADD_OVERFLOW, expand_SUB_OVERFLOW,
|
||||
expand_MUL_OVERFLOW): New functions.
|
||||
* internal-fn.def (ADD_OVERFLOW, SUB_OVERFLOW, MUL_OVERFLOW): New
|
||||
internal functions.
|
||||
* tree-vrp.c (check_for_binary_op_overflow): New function.
|
||||
(extract_range_basic): Handle {REAL,IMAG}PART_EXPR if the operand
|
||||
is SSA_NAME set by {ADD,SUB,MUL}_OVERFLOW internal functions.
|
||||
(simplify_internal_call_using_ranges): Handle {ADD,SUB,MUL}_OVERFLOW
|
||||
internal functions.
|
||||
* optabs.def (umulv4_optab): New optab.
|
||||
* config/i386/i386.md (umulv<mode>4, <u>mulvqi4): New define_expands.
|
||||
(*umulv<mode>4, *<u>mulvqi4): New define_insns.
|
||||
* doc/extend.texi (Integer Overflow Builtins): Document
|
||||
__builtin_*_overflow.
|
||||
|
||||
2014-11-12 Richard Biener <rguenther@suse.de>
|
||||
|
||||
* genmatch.c (capture_info::capture_info): Add missing
|
||||
|
|
|
@ -178,6 +178,9 @@ DEF_ATTR_TREE_LIST (ATTR_NOTHROW_NONNULL_4, ATTR_NONNULL, ATTR_LIST_4, \
|
|||
/* Nothrow functions whose fifth parameter is a nonnull pointer. */
|
||||
DEF_ATTR_TREE_LIST (ATTR_NOTHROW_NONNULL_5, ATTR_NONNULL, ATTR_LIST_5, \
|
||||
ATTR_NOTHROW_LIST)
|
||||
/* Nothrow leaf functions which are type-generic. */
|
||||
DEF_ATTR_TREE_LIST (ATTR_NOTHROW_TYPEGENERIC_LEAF, ATTR_TYPEGENERIC, ATTR_NULL, \
|
||||
ATTR_NOTHROW_LEAF_LIST)
|
||||
/* Nothrow const functions whose pointer parameter(s) are all nonnull. */
|
||||
DEF_ATTR_TREE_LIST (ATTR_CONST_NOTHROW_NONNULL, ATTR_CONST, ATTR_NULL, \
|
||||
ATTR_NOTHROW_NONNULL)
|
||||
|
|
|
@ -126,7 +126,10 @@ DEF_PRIMITIVE_TYPE (BT_I16, builtin_type_for_size (BITS_PER_UNIT*16, 1))
|
|||
DEF_PRIMITIVE_TYPE (BT_BND, pointer_bounds_type_node)
|
||||
|
||||
DEF_POINTER_TYPE (BT_PTR_CONST_STRING, BT_CONST_STRING)
|
||||
DEF_POINTER_TYPE (BT_PTR_UINT, BT_UINT)
|
||||
DEF_POINTER_TYPE (BT_PTR_LONG, BT_LONG)
|
||||
DEF_POINTER_TYPE (BT_PTR_ULONG, BT_ULONG)
|
||||
DEF_POINTER_TYPE (BT_PTR_LONGLONG, BT_LONGLONG)
|
||||
DEF_POINTER_TYPE (BT_PTR_ULONGLONG, BT_ULONGLONG)
|
||||
DEF_POINTER_TYPE (BT_PTR_PTR, BT_PTR)
|
||||
|
||||
|
@ -435,6 +438,18 @@ DEF_FUNCTION_TYPE_3 (BT_FN_VOID_VPTR_I16_INT, BT_VOID, BT_VOLATILE_PTR, BT_I16,
|
|||
DEF_FUNCTION_TYPE_3 (BT_FN_INT_PTRPTR_SIZE_SIZE, BT_INT, BT_PTR_PTR, BT_SIZE, BT_SIZE)
|
||||
DEF_FUNCTION_TYPE_3 (BT_FN_PTR_CONST_PTR_CONST_PTR_SIZE, BT_PTR, BT_CONST_PTR, BT_CONST_PTR, BT_SIZE)
|
||||
DEF_FUNCTION_TYPE_3 (BT_FN_VOID_CONST_PTR_BND_CONST_PTR, BT_VOID, BT_CONST_PTR, BT_BND, BT_CONST_PTR)
|
||||
DEF_FUNCTION_TYPE_3 (BT_FN_BOOL_INT_INT_INTPTR, BT_BOOL, BT_INT, BT_INT,
|
||||
BT_INT_PTR)
|
||||
DEF_FUNCTION_TYPE_3 (BT_FN_BOOL_LONG_LONG_LONGPTR, BT_BOOL, BT_LONG, BT_LONG,
|
||||
BT_PTR_LONG)
|
||||
DEF_FUNCTION_TYPE_3 (BT_FN_BOOL_LONGLONG_LONGLONG_LONGLONGPTR, BT_BOOL,
|
||||
BT_LONGLONG, BT_LONGLONG, BT_PTR_LONGLONG)
|
||||
DEF_FUNCTION_TYPE_3 (BT_FN_BOOL_UINT_UINT_UINTPTR, BT_BOOL, BT_UINT, BT_UINT,
|
||||
BT_PTR_UINT)
|
||||
DEF_FUNCTION_TYPE_3 (BT_FN_BOOL_ULONG_ULONG_ULONGPTR, BT_BOOL, BT_ULONG,
|
||||
BT_ULONG, BT_PTR_ULONG)
|
||||
DEF_FUNCTION_TYPE_3 (BT_FN_BOOL_ULONGLONG_ULONGLONG_ULONGLONGPTR, BT_BOOL,
|
||||
BT_ULONGLONG, BT_ULONGLONG, BT_PTR_ULONGLONG)
|
||||
|
||||
DEF_FUNCTION_TYPE_4 (BT_FN_SIZE_CONST_PTR_SIZE_SIZE_FILEPTR,
|
||||
BT_SIZE, BT_CONST_PTR, BT_SIZE, BT_SIZE, BT_FILEPTR)
|
||||
|
@ -532,6 +547,7 @@ DEF_FUNCTION_TYPE_8 (BT_FN_VOID_OMPFN_PTR_OMPCPYFN_LONG_LONG_BOOL_UINT_PTR,
|
|||
DEF_FUNCTION_TYPE_VAR_0 (BT_FN_VOID_VAR, BT_VOID)
|
||||
DEF_FUNCTION_TYPE_VAR_0 (BT_FN_INT_VAR, BT_INT)
|
||||
DEF_FUNCTION_TYPE_VAR_0 (BT_FN_PTR_VAR, BT_PTR)
|
||||
DEF_FUNCTION_TYPE_VAR_0 (BT_FN_BOOL_VAR, BT_BOOL)
|
||||
|
||||
DEF_FUNCTION_TYPE_VAR_1 (BT_FN_VOID_VALIST_REF_VAR,
|
||||
BT_VOID, BT_VALIST_REF)
|
||||
|
|
|
@ -9724,6 +9724,62 @@ fold_builtin_unordered_cmp (location_t loc, tree fndecl, tree arg0, tree arg1,
|
|||
fold_build2_loc (loc, code, type, arg0, arg1));
|
||||
}
|
||||
|
||||
/* Fold __builtin_{,s,u}{add,sub,mul}{,l,ll}_overflow, either into normal
|
||||
arithmetics if it can never overflow, or into internal functions that
|
||||
return both result of arithmetics and overflowed boolean flag in
|
||||
a complex integer result, or some other check for overflow. */
|
||||
|
||||
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);
|
||||
switch (fcode)
|
||||
{
|
||||
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:
|
||||
ifn = IFN_ADD_OVERFLOW;
|
||||
break;
|
||||
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:
|
||||
ifn = IFN_SUB_OVERFLOW;
|
||||
break;
|
||||
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:
|
||||
ifn = IFN_MUL_OVERFLOW;
|
||||
break;
|
||||
default:
|
||||
gcc_unreachable ();
|
||||
}
|
||||
tree ctype = build_complex_type (type);
|
||||
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);
|
||||
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);
|
||||
}
|
||||
|
||||
/* Fold a call to built-in function FNDECL with 0 arguments.
|
||||
IGNORE is true if the result of the function call is ignored. This
|
||||
function returns NULL_TREE if no simplification was possible. */
|
||||
|
@ -10431,6 +10487,29 @@ fold_builtin_3 (location_t loc, tree fndecl,
|
|||
case BUILT_IN_EXPECT:
|
||||
return fold_builtin_expect (loc, arg0, arg1, arg2);
|
||||
|
||||
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, arg2);
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -671,6 +671,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. */
|
||||
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)
|
||||
|
||||
/* Category: miscellaneous builtins. */
|
||||
DEF_LIB_BUILTIN (BUILT_IN_ABORT, "abort", BT_FN_VOID, ATTR_NORETURN_NOTHROW_LEAF_LIST)
|
||||
DEF_LIB_BUILTIN (BUILT_IN_ABS, "abs", BT_FN_INT_INT, ATTR_CONST_NOTHROW_LEAF_LIST)
|
||||
|
|
|
@ -1,3 +1,9 @@
|
|||
2014-11-12 Jakub Jelinek <jakub@redhat.com>
|
||||
|
||||
PR c/59708
|
||||
* c-common.c (check_builtin_function_arguments): Handle
|
||||
BUILT_IN_{ADD,SUB,MUL}_OVERFLOW.
|
||||
|
||||
2014-11-10 Andi Kleen <ak@linux.intel.com>
|
||||
|
||||
PR c/60804
|
||||
|
|
|
@ -9649,6 +9649,30 @@ check_builtin_function_arguments (tree fndecl, int nargs, tree *args)
|
|||
}
|
||||
return false;
|
||||
|
||||
case BUILT_IN_ADD_OVERFLOW:
|
||||
case BUILT_IN_SUB_OVERFLOW:
|
||||
case BUILT_IN_MUL_OVERFLOW:
|
||||
if (builtin_function_validate_nargs (fndecl, nargs, 3))
|
||||
{
|
||||
unsigned i;
|
||||
for (i = 0; i < 2; i++)
|
||||
if (!INTEGRAL_TYPE_P (TREE_TYPE (args[i])))
|
||||
{
|
||||
error ("argument %u in call to function %qE does not have "
|
||||
"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)
|
||||
{
|
||||
error ("argument 3 in call to function %qE does not have "
|
||||
"pointer to integer type", fndecl);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -6663,6 +6663,99 @@
|
|||
(const_string "4")]
|
||||
(const_string "<MODE_SIZE>")))])
|
||||
|
||||
(define_expand "umulv<mode>4"
|
||||
[(parallel [(set (reg:CCO FLAGS_REG)
|
||||
(eq:CCO (mult:<DWI>
|
||||
(zero_extend:<DWI>
|
||||
(match_operand:SWI48 1
|
||||
"nonimmediate_operand"))
|
||||
(zero_extend:<DWI>
|
||||
(match_operand:SWI48 2
|
||||
"nonimmediate_operand")))
|
||||
(zero_extend:<DWI>
|
||||
(mult:SWI48 (match_dup 1) (match_dup 2)))))
|
||||
(set (match_operand:SWI48 0 "register_operand")
|
||||
(mult:SWI48 (match_dup 1) (match_dup 2)))
|
||||
(clobber (match_scratch:SWI48 4))])
|
||||
(set (pc) (if_then_else
|
||||
(eq (reg:CCO FLAGS_REG) (const_int 0))
|
||||
(label_ref (match_operand 3))
|
||||
(pc)))]
|
||||
""
|
||||
{
|
||||
if (MEM_P (operands[1]) && MEM_P (operands[2]))
|
||||
operands[1] = force_reg (<MODE>mode, operands[1]);
|
||||
})
|
||||
|
||||
(define_insn "*umulv<mode>4"
|
||||
[(set (reg:CCO FLAGS_REG)
|
||||
(eq:CCO (mult:<DWI>
|
||||
(zero_extend:<DWI>
|
||||
(match_operand:SWI48 1 "nonimmediate_operand" "%0"))
|
||||
(zero_extend:<DWI>
|
||||
(match_operand:SWI48 2 "nonimmediate_operand" "rm")))
|
||||
(zero_extend:<DWI>
|
||||
(mult:SWI48 (match_dup 1) (match_dup 2)))))
|
||||
(set (match_operand:SWI48 0 "register_operand" "=a")
|
||||
(mult:SWI48 (match_dup 1) (match_dup 2)))
|
||||
(clobber (match_scratch:SWI48 3 "=d"))]
|
||||
"!(MEM_P (operands[1]) && MEM_P (operands[2]))"
|
||||
"mul{<imodesuffix>}\t%2"
|
||||
[(set_attr "type" "imul")
|
||||
(set_attr "length_immediate" "0")
|
||||
(set (attr "athlon_decode")
|
||||
(if_then_else (eq_attr "cpu" "athlon")
|
||||
(const_string "vector")
|
||||
(const_string "double")))
|
||||
(set_attr "amdfam10_decode" "double")
|
||||
(set_attr "bdver1_decode" "direct")
|
||||
(set_attr "mode" "<MODE>")])
|
||||
|
||||
(define_expand "<u>mulvqi4"
|
||||
[(parallel [(set (reg:CCO FLAGS_REG)
|
||||
(eq:CCO (mult:HI
|
||||
(any_extend:HI
|
||||
(match_operand:QI 1 "nonimmediate_operand"))
|
||||
(any_extend:HI
|
||||
(match_operand:QI 2 "nonimmediate_operand")))
|
||||
(any_extend:HI
|
||||
(mult:QI (match_dup 1) (match_dup 2)))))
|
||||
(set (match_operand:QI 0 "register_operand")
|
||||
(mult:QI (match_dup 1) (match_dup 2)))])
|
||||
(set (pc) (if_then_else
|
||||
(eq (reg:CCO FLAGS_REG) (const_int 0))
|
||||
(label_ref (match_operand 3))
|
||||
(pc)))]
|
||||
"TARGET_QIMODE_MATH"
|
||||
{
|
||||
if (MEM_P (operands[1]) && MEM_P (operands[2]))
|
||||
operands[1] = force_reg (QImode, operands[1]);
|
||||
})
|
||||
|
||||
(define_insn "*<u>mulvqi4"
|
||||
[(set (reg:CCO FLAGS_REG)
|
||||
(eq:CCO (mult:HI
|
||||
(any_extend:HI
|
||||
(match_operand:QI 1 "nonimmediate_operand" "%0"))
|
||||
(any_extend:HI
|
||||
(match_operand:QI 2 "nonimmediate_operand" "qm")))
|
||||
(any_extend:HI
|
||||
(mult:QI (match_dup 1) (match_dup 2)))))
|
||||
(set (match_operand:QI 0 "register_operand" "=a")
|
||||
(mult:QI (match_dup 1) (match_dup 2)))]
|
||||
"TARGET_QIMODE_MATH
|
||||
&& !(MEM_P (operands[1]) && MEM_P (operands[2]))"
|
||||
"<sgnprefix>mul{b}\t%2"
|
||||
[(set_attr "type" "imul")
|
||||
(set_attr "length_immediate" "0")
|
||||
(set (attr "athlon_decode")
|
||||
(if_then_else (eq_attr "cpu" "athlon")
|
||||
(const_string "vector")
|
||||
(const_string "direct")))
|
||||
(set_attr "amdfam10_decode" "direct")
|
||||
(set_attr "bdver1_decode" "direct")
|
||||
(set_attr "mode" "QI")])
|
||||
|
||||
(define_expand "<u>mul<mode><dwi>3"
|
||||
[(parallel [(set (match_operand:<DWI> 0 "register_operand")
|
||||
(mult:<DWI>
|
||||
|
|
|
@ -76,6 +76,8 @@ extensions, accepted by GCC in C90 mode and in C++.
|
|||
* Offsetof:: Special syntax for implementing @code{offsetof}.
|
||||
* __sync Builtins:: Legacy built-in functions for atomic memory access.
|
||||
* __atomic Builtins:: Atomic built-in functions with memory model.
|
||||
* Integer Overflow Builtins:: Built-in functions to perform arithmetics and
|
||||
arithmetic overflow checking.
|
||||
* x86 specific memory model extensions for transactional memory:: x86 memory models.
|
||||
* Object Size Checking:: Built-in functions for limited buffer overflow
|
||||
checking.
|
||||
|
@ -8487,6 +8489,65 @@ alignment. A value of 0 indicates typical alignment should be used. The
|
|||
compiler may also ignore this parameter.
|
||||
@end deftypefn
|
||||
|
||||
@node Integer Overflow Builtins
|
||||
@section Built-in functions to perform arithmetics and arithmetic overflow checking.
|
||||
|
||||
The following built-in functions allow performing simple arithmetic operations
|
||||
together with checking whether the operations overflowed.
|
||||
|
||||
@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.
|
||||
|
||||
@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)
|
||||
|
||||
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.
|
||||
|
||||
@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.
|
||||
|
||||
@end deftypefn
|
||||
|
||||
@node x86 specific memory model extensions for transactional memory
|
||||
@section x86 specific memory model extensions for transactional memory
|
||||
|
||||
|
|
|
@ -168,7 +168,6 @@ static void emit_single_push_insn (machine_mode, rtx, tree);
|
|||
#endif
|
||||
static void do_tablejump (rtx, machine_mode, rtx, rtx, rtx, int);
|
||||
static rtx const_vector_from_tree (tree);
|
||||
static void write_complex_part (rtx, rtx, bool);
|
||||
|
||||
|
||||
/* This is run to set up which modes can be used
|
||||
|
@ -2995,7 +2994,7 @@ set_storage_via_setmem (rtx object, rtx size, rtx val, unsigned int align,
|
|||
/* Write to one of the components of the complex value CPLX. Write VAL to
|
||||
the real part if IMAG_P is false, and the imaginary part if its true. */
|
||||
|
||||
static void
|
||||
void
|
||||
write_complex_part (rtx cplx, rtx val, bool imag_p)
|
||||
{
|
||||
machine_mode cmode;
|
||||
|
|
|
@ -255,6 +255,7 @@ extern rtx_insn *emit_move_insn_1 (rtx, rtx);
|
|||
|
||||
extern rtx_insn *emit_move_complex_push (machine_mode, rtx, rtx);
|
||||
extern rtx_insn *emit_move_complex_parts (rtx, rtx);
|
||||
extern void write_complex_part (rtx, rtx, bool);
|
||||
extern rtx emit_move_resolve_push (machine_mode, rtx);
|
||||
|
||||
/* Push a block of length SIZE (perhaps variable)
|
||||
|
|
|
@ -2020,9 +2020,14 @@ aggregate_value_p (const_tree exp, const_tree fntype)
|
|||
case CALL_EXPR:
|
||||
{
|
||||
tree fndecl = get_callee_fndecl (fntype);
|
||||
fntype = (fndecl
|
||||
? TREE_TYPE (fndecl)
|
||||
: TREE_TYPE (TREE_TYPE (CALL_EXPR_FN (fntype))));
|
||||
if (fndecl)
|
||||
fntype = TREE_TYPE (fndecl);
|
||||
else if (CALL_EXPR_FN (fntype))
|
||||
fntype = TREE_TYPE (TREE_TYPE (CALL_EXPR_FN (fntype)));
|
||||
else
|
||||
/* For internal functions, assume nothing needs to be
|
||||
returned in memory. */
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
case FUNCTION_DECL:
|
||||
|
|
|
@ -2586,6 +2586,33 @@ gimple_fold_builtin (gimple_stmt_iterator *gsi)
|
|||
return false;
|
||||
}
|
||||
|
||||
/* Return true if ARG0 CODE ARG1 in infinite signed precision operation
|
||||
doesn't fit into TYPE. The test for overflow should be regardless of
|
||||
-fwrapv, and even for unsigned types. */
|
||||
|
||||
bool
|
||||
arith_overflowed_p (enum tree_code code, const_tree type,
|
||||
const_tree arg0, const_tree arg1)
|
||||
{
|
||||
typedef FIXED_WIDE_INT (WIDE_INT_MAX_PRECISION * 2) widest2_int;
|
||||
typedef generic_wide_int <wi::extended_tree <WIDE_INT_MAX_PRECISION * 2> >
|
||||
widest2_int_cst;
|
||||
widest2_int warg0 = widest2_int_cst (arg0);
|
||||
widest2_int warg1 = widest2_int_cst (arg1);
|
||||
widest2_int wres;
|
||||
switch (code)
|
||||
{
|
||||
case PLUS_EXPR: wres = wi::add (warg0, warg1); break;
|
||||
case MINUS_EXPR: wres = wi::sub (warg0, warg1); break;
|
||||
case MULT_EXPR: wres = wi::mul (warg0, warg1); break;
|
||||
default: gcc_unreachable ();
|
||||
}
|
||||
signop sign = TYPE_SIGN (type);
|
||||
if (sign == UNSIGNED && wi::neg_p (wres))
|
||||
return true;
|
||||
return wi::min_precision (wres, sign) > TYPE_PRECISION (type);
|
||||
}
|
||||
|
||||
/* Attempt to fold a call statement referenced by the statement iterator GSI.
|
||||
The statement may be replaced by another statement, e.g., if the call
|
||||
simplifies to a constant value. Return true if any changes were made.
|
||||
|
@ -2714,6 +2741,8 @@ gimple_fold_call (gimple_stmt_iterator *gsi, bool inplace)
|
|||
{
|
||||
enum tree_code subcode = ERROR_MARK;
|
||||
tree result = NULL_TREE;
|
||||
bool cplx_result = false;
|
||||
tree overflow = NULL_TREE;
|
||||
switch (gimple_call_internal_fn (stmt))
|
||||
{
|
||||
case IFN_BUILTIN_EXPECT:
|
||||
|
@ -2744,6 +2773,18 @@ gimple_fold_call (gimple_stmt_iterator *gsi, bool inplace)
|
|||
case IFN_UBSAN_CHECK_MUL:
|
||||
subcode = MULT_EXPR;
|
||||
break;
|
||||
case IFN_ADD_OVERFLOW:
|
||||
subcode = PLUS_EXPR;
|
||||
cplx_result = true;
|
||||
break;
|
||||
case IFN_SUB_OVERFLOW:
|
||||
subcode = MINUS_EXPR;
|
||||
cplx_result = true;
|
||||
break;
|
||||
case IFN_MUL_OVERFLOW:
|
||||
subcode = MULT_EXPR;
|
||||
cplx_result = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -2751,30 +2792,88 @@ gimple_fold_call (gimple_stmt_iterator *gsi, bool inplace)
|
|||
{
|
||||
tree arg0 = gimple_call_arg (stmt, 0);
|
||||
tree arg1 = gimple_call_arg (stmt, 1);
|
||||
tree type = TREE_TYPE (arg0);
|
||||
if (cplx_result)
|
||||
{
|
||||
tree lhs = gimple_call_lhs (stmt);
|
||||
if (lhs == NULL_TREE)
|
||||
type = NULL_TREE;
|
||||
else
|
||||
type = TREE_TYPE (TREE_TYPE (lhs));
|
||||
}
|
||||
if (type == NULL_TREE)
|
||||
;
|
||||
/* x = y + 0; x = y - 0; x = y * 0; */
|
||||
if (integer_zerop (arg1))
|
||||
result = subcode == MULT_EXPR
|
||||
? build_zero_cst (TREE_TYPE (arg0))
|
||||
: arg0;
|
||||
else if (integer_zerop (arg1))
|
||||
result = subcode == MULT_EXPR ? integer_zero_node : arg0;
|
||||
/* x = 0 + y; x = 0 * y; */
|
||||
else if (subcode != MINUS_EXPR && integer_zerop (arg0))
|
||||
result = subcode == MULT_EXPR
|
||||
? build_zero_cst (TREE_TYPE (arg0))
|
||||
: arg1;
|
||||
result = subcode == MULT_EXPR ? integer_zero_node : arg1;
|
||||
/* x = y - y; */
|
||||
else if (subcode == MINUS_EXPR && operand_equal_p (arg0, arg1, 0))
|
||||
result = build_zero_cst (TREE_TYPE (arg0));
|
||||
result = integer_zero_node;
|
||||
/* x = y * 1; x = 1 * y; */
|
||||
else if (subcode == MULT_EXPR)
|
||||
else if (subcode == MULT_EXPR && integer_onep (arg1))
|
||||
result = arg0;
|
||||
else if (subcode == MULT_EXPR && integer_onep (arg0))
|
||||
result = arg1;
|
||||
else if (TREE_CODE (arg0) == INTEGER_CST
|
||||
&& TREE_CODE (arg1) == INTEGER_CST)
|
||||
{
|
||||
if (integer_onep (arg1))
|
||||
result = arg0;
|
||||
else if (integer_onep (arg0))
|
||||
result = arg1;
|
||||
if (cplx_result)
|
||||
result = int_const_binop (subcode, fold_convert (type, arg0),
|
||||
fold_convert (type, arg1));
|
||||
else
|
||||
result = int_const_binop (subcode, arg0, arg1);
|
||||
if (result && arith_overflowed_p (subcode, type, arg0, arg1))
|
||||
{
|
||||
if (cplx_result)
|
||||
overflow = build_one_cst (type);
|
||||
else
|
||||
result = NULL_TREE;
|
||||
}
|
||||
}
|
||||
if (result)
|
||||
{
|
||||
if (result == integer_zero_node)
|
||||
result = build_zero_cst (type);
|
||||
else if (cplx_result && TREE_TYPE (result) != type)
|
||||
{
|
||||
if (TREE_CODE (result) == INTEGER_CST)
|
||||
{
|
||||
if (arith_overflowed_p (PLUS_EXPR, type, result,
|
||||
integer_zero_node))
|
||||
overflow = build_one_cst (type);
|
||||
}
|
||||
else if ((!TYPE_UNSIGNED (TREE_TYPE (result))
|
||||
&& TYPE_UNSIGNED (type))
|
||||
|| (TYPE_PRECISION (type)
|
||||
< (TYPE_PRECISION (TREE_TYPE (result))
|
||||
+ (TYPE_UNSIGNED (TREE_TYPE (result))
|
||||
&& !TYPE_UNSIGNED (type)))))
|
||||
result = NULL_TREE;
|
||||
if (result)
|
||||
result = fold_convert (type, result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (result)
|
||||
{
|
||||
if (TREE_CODE (result) == INTEGER_CST && TREE_OVERFLOW (result))
|
||||
result = drop_tree_overflow (result);
|
||||
if (cplx_result)
|
||||
{
|
||||
if (overflow == NULL_TREE)
|
||||
overflow = build_zero_cst (TREE_TYPE (result));
|
||||
tree ctype = build_complex_type (TREE_TYPE (result));
|
||||
if (TREE_CODE (result) == INTEGER_CST
|
||||
&& TREE_CODE (overflow) == INTEGER_CST)
|
||||
result = build_complex (ctype, result, overflow);
|
||||
else
|
||||
result = build2_loc (gimple_location (stmt), COMPLEX_EXPR,
|
||||
ctype, result, overflow);
|
||||
}
|
||||
if (!update_call_from_tree (gsi, result))
|
||||
gimplify_and_update_call_from_tree (gsi, result);
|
||||
changed = true;
|
||||
|
|
|
@ -32,6 +32,8 @@ extern tree maybe_fold_and_comparisons (enum tree_code, tree, tree,
|
|||
enum tree_code, tree, tree);
|
||||
extern tree maybe_fold_or_comparisons (enum tree_code, tree, tree,
|
||||
enum tree_code, tree, tree);
|
||||
extern bool arith_overflowed_p (enum tree_code, const_tree, const_tree,
|
||||
const_tree);
|
||||
extern tree no_follow_ssa_edges (tree);
|
||||
extern tree follow_single_use_edges (tree);
|
||||
extern tree gimple_fold_stmt_to_constant_1 (gimple, tree (*) (tree));
|
||||
|
|
|
@ -2281,6 +2281,9 @@ gimplify_call_expr (tree *expr_p, gimple_seq *pre_p, bool want_value)
|
|||
/* Gimplify internal functions created in the FEs. */
|
||||
if (CALL_EXPR_FN (*expr_p) == NULL_TREE)
|
||||
{
|
||||
if (want_value)
|
||||
return GS_ALL_DONE;
|
||||
|
||||
nargs = call_expr_nargs (*expr_p);
|
||||
enum internal_fn ifn = CALL_EXPR_IFN (*expr_p);
|
||||
auto_vec<tree> vargs (nargs);
|
||||
|
@ -4644,22 +4647,41 @@ gimplify_modify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p,
|
|||
{
|
||||
/* Since the RHS is a CALL_EXPR, we need to create a GIMPLE_CALL
|
||||
instead of a GIMPLE_ASSIGN. */
|
||||
tree fnptrtype = TREE_TYPE (CALL_EXPR_FN (*from_p));
|
||||
CALL_EXPR_FN (*from_p) = TREE_OPERAND (CALL_EXPR_FN (*from_p), 0);
|
||||
STRIP_USELESS_TYPE_CONVERSION (CALL_EXPR_FN (*from_p));
|
||||
tree fndecl = get_callee_fndecl (*from_p);
|
||||
if (fndecl
|
||||
&& DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL
|
||||
&& DECL_FUNCTION_CODE (fndecl) == BUILT_IN_EXPECT
|
||||
&& call_expr_nargs (*from_p) == 3)
|
||||
assign = gimple_build_call_internal (IFN_BUILTIN_EXPECT, 3,
|
||||
CALL_EXPR_ARG (*from_p, 0),
|
||||
CALL_EXPR_ARG (*from_p, 1),
|
||||
CALL_EXPR_ARG (*from_p, 2));
|
||||
if (CALL_EXPR_FN (*from_p) == NULL_TREE)
|
||||
{
|
||||
/* Gimplify internal functions created in the FEs. */
|
||||
int nargs = call_expr_nargs (*from_p), i;
|
||||
enum internal_fn ifn = CALL_EXPR_IFN (*from_p);
|
||||
auto_vec<tree> vargs (nargs);
|
||||
|
||||
for (i = 0; i < nargs; i++)
|
||||
{
|
||||
gimplify_arg (&CALL_EXPR_ARG (*from_p, i), pre_p,
|
||||
EXPR_LOCATION (*from_p));
|
||||
vargs.quick_push (CALL_EXPR_ARG (*from_p, i));
|
||||
}
|
||||
assign = gimple_build_call_internal_vec (ifn, vargs);
|
||||
gimple_set_location (assign, EXPR_LOCATION (*expr_p));
|
||||
}
|
||||
else
|
||||
{
|
||||
assign = gimple_build_call_from_tree (*from_p);
|
||||
gimple_call_set_fntype (assign, TREE_TYPE (fnptrtype));
|
||||
tree fnptrtype = TREE_TYPE (CALL_EXPR_FN (*from_p));
|
||||
CALL_EXPR_FN (*from_p) = TREE_OPERAND (CALL_EXPR_FN (*from_p), 0);
|
||||
STRIP_USELESS_TYPE_CONVERSION (CALL_EXPR_FN (*from_p));
|
||||
tree fndecl = get_callee_fndecl (*from_p);
|
||||
if (fndecl
|
||||
&& DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL
|
||||
&& DECL_FUNCTION_CODE (fndecl) == BUILT_IN_EXPECT
|
||||
&& call_expr_nargs (*from_p) == 3)
|
||||
assign = gimple_build_call_internal (IFN_BUILTIN_EXPECT, 3,
|
||||
CALL_EXPR_ARG (*from_p, 0),
|
||||
CALL_EXPR_ARG (*from_p, 1),
|
||||
CALL_EXPR_ARG (*from_p, 2));
|
||||
else
|
||||
{
|
||||
assign = gimple_build_call_from_tree (*from_p);
|
||||
gimple_call_set_fntype (assign, TREE_TYPE (fnptrtype));
|
||||
}
|
||||
}
|
||||
notice_special_calls (assign);
|
||||
if (!gimple_call_noreturn_p (assign))
|
||||
|
|
1428
gcc/internal-fn.c
1428
gcc/internal-fn.c
File diff suppressed because it is too large
Load Diff
|
@ -57,3 +57,6 @@ DEF_INTERNAL_FN (UBSAN_OBJECT_SIZE, ECF_LEAF | ECF_NOTHROW, NULL)
|
|||
DEF_INTERNAL_FN (ABNORMAL_DISPATCHER, ECF_NORETURN, NULL)
|
||||
DEF_INTERNAL_FN (BUILTIN_EXPECT, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL)
|
||||
DEF_INTERNAL_FN (ASAN_CHECK, ECF_TM_PURE | ECF_LEAF | ECF_NOTHROW, ".W...")
|
||||
DEF_INTERNAL_FN (ADD_OVERFLOW, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL)
|
||||
DEF_INTERNAL_FN (SUB_OVERFLOW, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL)
|
||||
DEF_INTERNAL_FN (MUL_OVERFLOW, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL)
|
||||
|
|
|
@ -190,6 +190,7 @@ OPTAB_D (ctrap_optab, "ctrap$a4")
|
|||
OPTAB_D (addv4_optab, "addv$I$a4")
|
||||
OPTAB_D (subv4_optab, "subv$I$a4")
|
||||
OPTAB_D (mulv4_optab, "mulv$I$a4")
|
||||
OPTAB_D (umulv4_optab, "umulv$I$a4")
|
||||
OPTAB_D (negv3_optab, "negv$I$a3")
|
||||
OPTAB_D (addptr3_optab, "addptr$a3")
|
||||
|
||||
|
|
|
@ -1,3 +1,31 @@
|
|||
2014-11-12 Jakub Jelinek <jakub@redhat.com>
|
||||
|
||||
PR c/59708
|
||||
* c-c++-common/builtin-arith-overflow-1.c: New test.
|
||||
* c-c++-common/torture/builtin-arith-overflow-10.c: New test.
|
||||
* c-c++-common/torture/builtin-arith-overflow-11.c: New test.
|
||||
* c-c++-common/torture/builtin-arith-overflow-12.c: New test.
|
||||
* c-c++-common/torture/builtin-arith-overflow-12.h: New file.
|
||||
* c-c++-common/torture/builtin-arith-overflow-13.c: New test.
|
||||
* c-c++-common/torture/builtin-arith-overflow-14.c: New test.
|
||||
* c-c++-common/torture/builtin-arith-overflow-15.c: New test.
|
||||
* c-c++-common/torture/builtin-arith-overflow-16.c: New test.
|
||||
* c-c++-common/torture/builtin-arith-overflow-17.c: New test.
|
||||
* c-c++-common/torture/builtin-arith-overflow-18.c: New test.
|
||||
* c-c++-common/torture/builtin-arith-overflow-1.c: New test.
|
||||
* c-c++-common/torture/builtin-arith-overflow-1.h: New file.
|
||||
* c-c++-common/torture/builtin-arith-overflow-2.c: New test.
|
||||
* c-c++-common/torture/builtin-arith-overflow-3.c: New test.
|
||||
* c-c++-common/torture/builtin-arith-overflow-4.c: New test.
|
||||
* c-c++-common/torture/builtin-arith-overflow-5.c: New test.
|
||||
* c-c++-common/torture/builtin-arith-overflow-6.c: New test.
|
||||
* c-c++-common/torture/builtin-arith-overflow-7.c: New test.
|
||||
* c-c++-common/torture/builtin-arith-overflow-8.c: New test.
|
||||
* c-c++-common/torture/builtin-arith-overflow-9.c: New test.
|
||||
* c-c++-common/torture/builtin-arith-overflow.h: New file.
|
||||
* gcc.dg/builtin-arith-overflow-1.c: New test.
|
||||
* gcc.dg/builtin-arith-overflow-2.c: New test.
|
||||
|
||||
2014-11-12 Richard Biener <rguenther@suse.de>
|
||||
|
||||
PR middle-end/63821
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
/* { dg-do compile } */
|
||||
|
||||
int
|
||||
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" } */
|
||||
return x;
|
||||
}
|
||||
|
||||
int
|
||||
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" } */
|
||||
return x;
|
||||
}
|
||||
|
||||
enum E { e0 = 0, e1 = 1 };
|
||||
|
||||
#ifndef __cplusplus
|
||||
#define bool _Bool
|
||||
#endif
|
||||
|
||||
int
|
||||
f3 (float fa, int a, _Complex long int ca, double fb, void *pb, int b, enum E eb, bool bb, int *c)
|
||||
{
|
||||
int x = __builtin_add_overflow (fa, b, c); /* { dg-error "argument 1 in call to function\[^\n\r]*does not have integral type" } */
|
||||
x += __builtin_sub_overflow (ca, b, c); /* { dg-error "argument 1 in call to function\[^\n\r]*does not have integral type" } */
|
||||
x += __builtin_mul_overflow (a, fb, c); /* { dg-error "argument 2 in call to function\[^\n\r]*does not have integral type" } */
|
||||
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);
|
||||
return x;
|
||||
}
|
||||
|
||||
int
|
||||
f4 (float *fp, double *dp, _Complex int *cp, enum E *ep, bool *bp, long long int *llp)
|
||||
{
|
||||
int x = __builtin_add_overflow (1, 2, fp); /* { dg-error "argument 3 in call to function\[^\n\r]*does not have pointer to integer type" } */
|
||||
x += __builtin_sub_overflow (1, 2, dp); /* { dg-error "argument 3 in call to function\[^\n\r]*does not have pointer to integer type" } */
|
||||
x += __builtin_mul_overflow (1, 2, cp); /* { dg-error "argument 3 in call to function\[^\n\r]*does not have pointer to integer type" } */
|
||||
x += __builtin_add_overflow (1, 2, ep); /* { dg-error "argument 3 in call to function\[^\n\r]*does not have pointer to integer type" } */
|
||||
x += __builtin_sub_overflow (1, 2, bp); /* { dg-error "argument 3 in call to function\[^\n\r]*does not have pointer to integer type" } */
|
||||
x += __builtin_mul_overflow (1, 2, llp);
|
||||
return x;
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
/* Test __builtin_{add,sub,mul,{s,u}add,{s,u}sub,{s,u}mul}_overflow. */
|
||||
/* { dg-do run } */
|
||||
/* { dg-skip-if "" { ! run_expensive_tests } { "*" } { "-O0" "-O2" } } */
|
||||
|
||||
#include "builtin-arith-overflow-1.h"
|
||||
|
||||
#define U(s, op) s##op
|
||||
TESTS (int, INT_MIN, INT_MAX)
|
||||
#undef U
|
||||
#define U(s, op) op
|
||||
TESTS (int, INT_MIN, INT_MAX)
|
||||
|
||||
#undef T
|
||||
#define T(n, t1, t2, tr, v1, v2, vr, b, o) t##n##b ();
|
||||
|
||||
int
|
||||
main ()
|
||||
{
|
||||
TESTS (int, INT_MIN, INT_MAX)
|
||||
#undef U
|
||||
#define U(s, op) s##op
|
||||
TESTS (int, INT_MIN, INT_MAX)
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
#include "builtin-arith-overflow.h"
|
||||
|
||||
#define TESTS(type, min, max) \
|
||||
ST (100, signed type, 2, 3, 5, U(s, add), 0) \
|
||||
ST (101, signed type, max, -1, max - 1, U(s, add), 0) \
|
||||
ST (102, signed type, max, 0, max, U(s, add), 0) \
|
||||
ST (103, signed type, 1, max, min, U(s, add), 1) \
|
||||
ST (104, signed type, 0, min, min, U(s, sub), 1) \
|
||||
ST (110, signed type, 2, 3, -1, U(s, sub), 0) \
|
||||
ST (111, signed type, max, -1, min, U(s, sub), 1) \
|
||||
ST (112, signed type, max, 0, max, U(s, sub), 0) \
|
||||
ST (113, signed type, 1, max, min + 2, U(s, sub), 0) \
|
||||
ST (114, signed type, max, -1, min, U(s, sub), 1) \
|
||||
ST (120, signed type, 2, 3, 6, U(s, mul), 0) \
|
||||
ST (122, signed type, min, -1, min, U(s, mul), 1) \
|
||||
ST (123, signed type, max, 0, 0, U(s, mul), 0) \
|
||||
ST (124, signed type, 1, max, max, U(s, mul), 0) \
|
||||
ST (125, signed type, max, 2, -2, U(s, mul), 1) \
|
||||
ST (126, signed type, max / 25, 25, max / 25 * 25, U(s, mul), 0) \
|
||||
ST (127, signed type, max / 25 + 1, 25, max / 25 * 25 + (unsigned type) 25, U(s, mul), 1) \
|
||||
ST (150, unsigned type, 2, 3, 5, U(u, add), 0) \
|
||||
ST (151, unsigned type, -1, -1, -2, U(u, add), 1) \
|
||||
ST (152, unsigned type, -1, 0, -1, U(u, add), 0) \
|
||||
ST (153, unsigned type, 1, -1, 0, U(u, add), 1) \
|
||||
ST (154, unsigned type, 0, min, min, U(u, sub), 1) \
|
||||
ST (160, unsigned type, 2, 3, -1, U(u, sub), 1) \
|
||||
ST (161, unsigned type, -1, -1, 0, U(u, sub), 0) \
|
||||
ST (162, unsigned type, -1, 0, -1, U(u, sub), 0) \
|
||||
ST (163, unsigned type, 1, -1, 2, U(u, sub), 1) \
|
||||
ST (164, unsigned type, 15, 14, 1, U(u, sub), 0) \
|
||||
ST (170, unsigned type, 2, 3, 6, U(u, mul), 0) \
|
||||
ST (171, unsigned type, max, 3, 3 * (unsigned type) max, U(u, mul), 1) \
|
||||
ST (172, unsigned type, -1, 0, 0, U(u, mul), 0) \
|
||||
ST (173, unsigned type, 1, -1, -1, U(u, mul), 0) \
|
||||
ST (174, unsigned type, -1, 2, -2, U(u, mul), 1) \
|
||||
ST (175, unsigned type, ((unsigned type) -1) / 25, 25, ((unsigned type) -1) / 25 * 25, U(u, mul), 0) \
|
||||
ST (176, unsigned type, ((unsigned type) -1) / 25 + 1, 25, ((unsigned type) -1) / 25 * 25 + (unsigned type) 25, U(u, mul), 1)
|
|
@ -0,0 +1,19 @@
|
|||
/* Test __builtin_{add,sub}_overflow on {,un}signed long int. */
|
||||
/* { dg-do run } */
|
||||
/* { dg-skip-if "" { ! run_expensive_tests } { "*" } { "-O0" "-O2" } } */
|
||||
|
||||
typedef signed long int S;
|
||||
typedef unsigned long int U;
|
||||
#define COND 1
|
||||
#define SHIFT ((__SIZEOF_LONG__ - 1) * __CHAR_BIT__)
|
||||
#define S_MAX __LONG_MAX__
|
||||
#define S_MIN (-__LONG_MAX__ - 1)
|
||||
#if __SIZEOF_LONG_LONG__ > __SIZEOF_LONG__
|
||||
typedef long long int W;
|
||||
#elif __SIZEOF_INT128__ > __SIZEOF_LONG__
|
||||
typedef __int128 W;
|
||||
#else
|
||||
#undef COND
|
||||
#define COND 0
|
||||
#endif
|
||||
#include "builtin-arith-overflow-7.c"
|
|
@ -0,0 +1,17 @@
|
|||
/* Test __builtin_{add,sub}_overflow on {,un}signed long long int. */
|
||||
/* { dg-do run } */
|
||||
/* { dg-skip-if "" { ! run_expensive_tests } { "*" } { "-O0" "-O2" } } */
|
||||
|
||||
typedef signed long long int S;
|
||||
typedef unsigned long long int U;
|
||||
#define COND 1
|
||||
#define SHIFT ((__SIZEOF_LONG_LONG__ - 1) * __CHAR_BIT__)
|
||||
#define S_MAX __LONG_LONG_MAX__
|
||||
#define S_MIN (-__LONG_LONG_MAX__ - 1)
|
||||
#if __SIZEOF_INT128__ > __SIZEOF_LONG_LONG__
|
||||
typedef __int128 W;
|
||||
#else
|
||||
#undef COND
|
||||
#define COND 0
|
||||
#endif
|
||||
#include "builtin-arith-overflow-7.c"
|
|
@ -0,0 +1,17 @@
|
|||
/* Test __builtin_{add,sub,mul_overflow. */
|
||||
/* { dg-do run } */
|
||||
/* { dg-skip-if "" { ! run_expensive_tests } { "*" } { "-O0" "-O2" } } */
|
||||
|
||||
#include "builtin-arith-overflow-12.h"
|
||||
|
||||
TESTS (int, INT_MIN, INT_MAX)
|
||||
|
||||
#undef T
|
||||
#define T(n, t1, t2, tr, v1, v2, vr, b, o) t##n##b ();
|
||||
|
||||
int
|
||||
main ()
|
||||
{
|
||||
TESTS (int, INT_MIN, INT_MAX)
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
#include "builtin-arith-overflow.h"
|
||||
|
||||
#define TESTS(type, min, max) \
|
||||
T (100, signed type, unsigned type, unsigned type, -1, 0, 0, mul, 0) \
|
||||
T (101, signed type, unsigned type, unsigned type, -1, 1, (unsigned type) -1, mul, 1) \
|
||||
T (102, unsigned type, signed type, unsigned type, 12, -3, (unsigned type) -36, mul, 1) \
|
||||
T (103, signed type, unsigned type, unsigned type, 3, 4, 12, mul, 0) \
|
||||
T (104, unsigned type, signed type, unsigned type, (unsigned type) -1 / 12, 12, (unsigned type) -1 / 12 * 12, mul, 0) \
|
||||
T (105, unsigned type, signed type, unsigned type, (unsigned type) -1 / 12, 13, (unsigned type) -1 / 12 * 13, mul, 1) \
|
||||
T (106, unsigned type, unsigned type, signed type, 0, 0, 0, mul, 0) \
|
||||
T (107, unsigned type, unsigned type, signed type, max / 31, 31, (signed type) ((unsigned type) max / 31 * 31), mul, 0) \
|
||||
T (108, unsigned type, unsigned type, signed type, max / 31, 32, (signed type) ((unsigned type) max / 31 * 32), mul, 1) \
|
||||
T (109, unsigned type, unsigned type, signed type, max / 31, 65, (signed type) ((unsigned type) max / 31 * 65), mul, 1) \
|
||||
T (110, signed type, unsigned type, signed type, -1, 7, -7, mul, 0) \
|
||||
T (111, unsigned type, signed type, signed type, 2, min / 2, min, mul, 0) \
|
||||
T (112, signed type, unsigned type, signed type, max / 12, 13, (signed type) ((unsigned type) max / 12 * 13), mul, 1) \
|
||||
T (113, unsigned type, signed type, signed type, (unsigned type) max + 19, 0, 0, mul, 0) \
|
||||
T (114, signed type, unsigned type, signed type, 0, (unsigned type) max + 1, 0, mul, 0) \
|
||||
T (115, unsigned type, signed type, signed type, (unsigned type) max + 1, -1, min, mul, 0) \
|
||||
T (116, signed type, unsigned type, signed type, -1, (unsigned type) max + 2, max, mul, 1) \
|
||||
T (117, signed type, signed type, unsigned type, min / 64, -64, (unsigned type) min, mul, 0) \
|
||||
T (118, signed type, signed type, unsigned type, min / 32, -33, ((unsigned type) max + 1) / 32 * 33, mul, 0) \
|
||||
T (119, signed type, signed type, unsigned type, min / 32, -65, ((unsigned type) max + 1) / 32 * 65, mul, 1) \
|
||||
T (120, signed type, signed type, unsigned type, -1, -1, 1, mul, 0) \
|
||||
T (121, signed type, signed type, unsigned type, 0, 0, 0, mul, 0) \
|
||||
T (122, signed type, signed type, unsigned type, 0, -6, 0, mul, 0) \
|
||||
T (123, signed type, signed type, unsigned type, -15, 0, 0, mul, 0) \
|
||||
T (124, signed type, signed type, unsigned type, -1, 1, ~(unsigned type) 0, mul, 1) \
|
||||
T (125, signed type, signed type, unsigned type, -17, 5, (unsigned type) -17 * 5, mul, 1) \
|
||||
T (126, signed type, signed type, unsigned type, 7, max / 7, max / 7 * 7, mul, 0) \
|
||||
T (127, signed type, signed type, unsigned type, max / 7, 8, (unsigned type) max / 7 * 8, mul, 0) \
|
||||
T (128, signed type, signed type, unsigned type, 15, max / 7, (unsigned type) max / 7 * 15, mul, 1) \
|
||||
T (129, signed type, unsigned type, signed type, min, 5, min + 5, add, 0) \
|
||||
T (130, unsigned type, signed type, signed type, ~(unsigned type) 0, min, max, add, 0) \
|
||||
T (131, signed type, unsigned type, signed type, max, 1, min, add, 1) \
|
||||
T (132, unsigned type, signed type, signed type, max / 2, max / 2 + 1, max, add, 0) \
|
||||
T (133, signed type, unsigned type, signed type, max / 2 + 1, max / 2 + 1, min, add, 1) \
|
||||
T (134, signed type, unsigned type, unsigned type, min, ~(unsigned type) 0, max, add, 0) \
|
||||
T (135, unsigned type, signed type, unsigned type, ~(unsigned type) 0, min + 1, (unsigned type) max + 1, add, 0) \
|
||||
T (136, signed type, unsigned type, unsigned type, 1, ~(unsigned type) 0, 0, add, 1) \
|
||||
T (137, unsigned type, signed type, unsigned type, 2, -3, ~(unsigned type) 0, add, 1) \
|
||||
T (138, signed type, unsigned type, signed type, min, 1, max, sub, 1) \
|
||||
T (139, signed type, unsigned type, signed type, min + 1, 1, min, sub, 0) \
|
||||
T (140, signed type, unsigned type, signed type, max, (unsigned type) max + 1, -1, sub, 0) \
|
||||
T (141, signed type, unsigned type, signed type, max, ~(unsigned type) 0, min, sub, 0) \
|
||||
T (142, signed type, unsigned type, signed type, max - 1, ~(unsigned type) 0, max, sub, 1) \
|
||||
T (143, signed type, unsigned type, unsigned type, -1, 0, ~(unsigned type) 0, sub, 1) \
|
||||
T (144, signed type, unsigned type, unsigned type, -1, ~(unsigned type) 0, 0, sub, 1) \
|
||||
T (145, signed type, unsigned type, unsigned type, min, 0, min, sub, 1) \
|
||||
T (146, signed type, unsigned type, unsigned type, max, max, 0, sub, 0) \
|
||||
T (147, signed type, unsigned type, unsigned type, max, (unsigned type) max + 1, -1, sub, 1) \
|
||||
T (148, signed type, unsigned type, unsigned type, max - 1, max, -1, sub, 1) \
|
||||
T (149, unsigned type, signed type, signed type, 0, max, -max, sub, 0) \
|
||||
T (150, unsigned type, signed type, signed type, (unsigned type) max + 1, 0, min, sub, 1) \
|
||||
T (151, unsigned type, signed type, signed type, (unsigned type) max + 1, 1, max, sub, 0) \
|
||||
T (152, unsigned type, unsigned type, signed type, 0, (unsigned type) max + 1, min, add, 1) \
|
||||
T (153, signed type, signed type, unsigned type, -1, 0, -1, add, 1) \
|
||||
T (154, unsigned type, signed type, unsigned type, 5, 6, -1, sub, 1) \
|
||||
T (155, unsigned type, signed type, unsigned type, ~(unsigned type) 0, max, (unsigned type) max + 1, sub, 0) \
|
||||
T (156, unsigned type, signed type, unsigned type, (unsigned type) max + 1, min, 0, sub, 1) \
|
||||
T (157, signed type, signed type, unsigned type, 3, -2, 1, add, 0) \
|
||||
T (158, signed type, signed type, unsigned type, 3, -4, -1, add, 1) \
|
||||
T (159, signed type, signed type, unsigned type, -3, -4, -7, add, 1) \
|
||||
T (160, signed type, signed type, unsigned type, -5, 4, -1, add, 1) \
|
||||
T (161, signed type, signed type, unsigned type, -5, 5, 0, add, 0) \
|
||||
T (162, signed type, signed type, unsigned type, min, 1, min + 1, add, 1) \
|
||||
T (163, unsigned type, unsigned type, signed type, max, 1, min, add, 1) \
|
||||
T (164, unsigned type, unsigned type, signed type, max - 1, 1, max, add, 0) \
|
||||
T (165, unsigned type, unsigned type, signed type, ~(unsigned type) 0, ~(unsigned type) 0, ~(unsigned type) 0 - 1, add, 1) \
|
||||
T (166, unsigned type, unsigned type, signed type, (unsigned type) max + 3, 2, min, sub, 1) \
|
||||
T (167, unsigned type, unsigned type, signed type, (unsigned type) max + 2, 2, max, sub, 0) \
|
||||
T (168, unsigned type, unsigned type, signed type, (unsigned type) max + 2, (unsigned type) max + 3, -1, sub, 0) \
|
||||
T (169, unsigned type, unsigned type, signed type, 0, (unsigned type) max + 1, min, sub, 0) \
|
||||
T (170, unsigned type, unsigned type, signed type, 0, (unsigned type) max + 2, max, sub, 1) \
|
||||
T (171, signed type, signed type, unsigned type, 3, 2, 1, sub, 0) \
|
||||
T (172, signed type, signed type, unsigned type, 3, 4, -1, sub, 1) \
|
||||
T (173, signed type, signed type, unsigned type, -3, 4, -7, sub, 1) \
|
||||
T (174, signed type, signed type, unsigned type, -5, -4, -1, sub, 1) \
|
||||
T (175, signed type, signed type, unsigned type, -5, -5, 0, sub, 0) \
|
||||
T (176, signed type, signed type, unsigned type, min, -1, min + 1, sub, 1)
|
|
@ -0,0 +1,17 @@
|
|||
/* Test __builtin_{add,sub,mul}_overflow. */
|
||||
/* { dg-do run } */
|
||||
/* { dg-skip-if "" { ! run_expensive_tests } { "*" } { "-O0" "-O2" } } */
|
||||
|
||||
#include "builtin-arith-overflow-12.h"
|
||||
|
||||
TESTS (long, LONG_MIN, LONG_MAX)
|
||||
|
||||
#undef T
|
||||
#define T(n, t1, t2, tr, v1, v2, vr, b, o) t##n##b ();
|
||||
|
||||
int
|
||||
main ()
|
||||
{
|
||||
TESTS (long, LONG_MIN, LONG_MAX)
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
/* Test __builtin_{add,sub,mul}_overflow. */
|
||||
/* { dg-do run } */
|
||||
/* { dg-skip-if "" { ! run_expensive_tests } { "*" } { "-O0" "-O2" } } */
|
||||
|
||||
#include "builtin-arith-overflow-12.h"
|
||||
|
||||
TESTS (long long, LLONG_MIN, LLONG_MAX)
|
||||
|
||||
#undef T
|
||||
#define T(n, t1, t2, tr, v1, v2, vr, b, o) t##n##b ();
|
||||
|
||||
int
|
||||
main ()
|
||||
{
|
||||
TESTS (long long, LLONG_MIN, LLONG_MAX)
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
/* Test __builtin_{add,sub,mul}_overflow. */
|
||||
/* { dg-do run } */
|
||||
/* { dg-skip-if "" { ! run_expensive_tests } { "*" } { "-O0" "-O2" } } */
|
||||
|
||||
#include "builtin-arith-overflow-12.h"
|
||||
|
||||
TESTS (char, SCHAR_MIN, SCHAR_MAX)
|
||||
|
||||
#undef T
|
||||
#define T(n, t1, t2, tr, v1, v2, vr, b, o) t##n##b ();
|
||||
|
||||
int
|
||||
main ()
|
||||
{
|
||||
TESTS (char, SCHAR_MIN, SCHAR_MAX)
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
/* Test __builtin_{add,sub,mul}_overflow. */
|
||||
/* { dg-do run } */
|
||||
/* { dg-skip-if "" { ! run_expensive_tests } { "*" } { "-O0" "-O2" } } */
|
||||
|
||||
#include "builtin-arith-overflow-12.h"
|
||||
|
||||
TESTS (short, SHRT_MIN, SHRT_MAX)
|
||||
|
||||
#undef T
|
||||
#define T(n, t1, t2, tr, v1, v2, vr, b, o) t##n##b ();
|
||||
|
||||
int
|
||||
main ()
|
||||
{
|
||||
TESTS (short, SHRT_MIN, SHRT_MAX)
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
/* Test __builtin_{add,sub,mul}_overflow. */
|
||||
/* { dg-do run { target int128 } } */
|
||||
/* { dg-skip-if "" { ! run_expensive_tests } { "*" } { "-O0" "-O2" } } */
|
||||
|
||||
#include "builtin-arith-overflow-12.h"
|
||||
|
||||
#define INT128_MAX ((signed __int128) (((unsigned __int128) 1 << (__SIZEOF_INT128__ * __CHAR_BIT__ - 1)) - 1))
|
||||
#define INT128_MIN (-INT128_MAX - 1)
|
||||
|
||||
TESTS (__int128, INT128_MIN, INT128_MAX)
|
||||
|
||||
#undef T
|
||||
#define T(n, t1, t2, tr, v1, v2, vr, b, o) t##n##b ();
|
||||
|
||||
int
|
||||
main ()
|
||||
{
|
||||
TESTS (__int128, INT128_MIN, INT128_MAX)
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
/* Test __builtin_{add,sub,mul}_overflow. */
|
||||
/* { dg-do run } */
|
||||
/* { dg-skip-if "" { ! run_expensive_tests } { "*" } { "-O0" "-O2" } } */
|
||||
|
||||
#include "builtin-arith-overflow.h"
|
||||
|
||||
#ifdef __SIZEOF_INT128__
|
||||
#define WTYPE __int128
|
||||
#else
|
||||
#define WTYPE long long int
|
||||
#endif
|
||||
|
||||
#define TESTS \
|
||||
T (100, signed char, signed char, unsigned WTYPE, -1, 0, -1, add, 1) \
|
||||
T (101, unsigned char, unsigned char, unsigned WTYPE, 5, 5, 10, add, 0) \
|
||||
T (102, signed char, unsigned short, unsigned WTYPE, 5, 5, 0, sub, 0) \
|
||||
T (103, signed char, unsigned short, unsigned WTYPE, 5, 6, -1, sub, 1) \
|
||||
T (104, signed char, signed char, unsigned WTYPE, -1, -1, 1, mul, 0) \
|
||||
T (105, unsigned char, signed char, unsigned WTYPE, 17, -2, -34, mul, 1) \
|
||||
T (106, unsigned WTYPE, signed WTYPE, signed char, 5, -2, -10, mul, 0) \
|
||||
T (107, long long int, long long int, unsigned char, -3, 5, 2, add, 0) \
|
||||
T (108, long long int, int, unsigned char, -5, 3, -2, add, 1) \
|
||||
T (109, int, WTYPE, unsigned char, -3, 5, 2, add, 0) \
|
||||
T (110, unsigned char, unsigned char, unsigned WTYPE, SCHAR_MAX - 1, (unsigned char) SCHAR_MAX + 4, -5, sub, 1)
|
||||
|
||||
TESTS
|
||||
|
||||
#undef T
|
||||
#define T(n, t1, t2, tr, v1, v2, vr, b, o) t##n##b ();
|
||||
|
||||
int
|
||||
main ()
|
||||
{
|
||||
TESTS
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
/* Test __builtin_{add,sub,mul,{s,u}addl,{s,u}subl,{s,u}mull}_overflow. */
|
||||
/* { dg-do run } */
|
||||
/* { dg-skip-if "" { ! run_expensive_tests } { "*" } { "-O0" "-O2" } } */
|
||||
|
||||
#include "builtin-arith-overflow-1.h"
|
||||
|
||||
#define U(s, op) s##op##l
|
||||
TESTS (long, LONG_MIN, LONG_MAX)
|
||||
#undef U
|
||||
#define U(s, op) op
|
||||
TESTS (long, LONG_MIN, LONG_MAX)
|
||||
|
||||
#undef T
|
||||
#define T(n, t1, t2, tr, v1, v2, vr, b, o) t##n##b ();
|
||||
|
||||
int
|
||||
main ()
|
||||
{
|
||||
TESTS (long, LONG_MIN, LONG_MAX)
|
||||
#undef U
|
||||
#define U(s, op) s##op##l
|
||||
TESTS (long, LONG_MIN, LONG_MAX)
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
/* Test __builtin_{add,sub,mul,{s,u}addll,{s,u}subll,{s,u}mulll}_overflow. */
|
||||
/* { dg-do run } */
|
||||
/* { dg-skip-if "" { ! run_expensive_tests } { "*" } { "-O0" "-O2" } } */
|
||||
|
||||
#include "builtin-arith-overflow-1.h"
|
||||
|
||||
#define U(s, op) s##op##ll
|
||||
TESTS (long long, LLONG_MIN, LLONG_MAX)
|
||||
#undef U
|
||||
#define U(s, op) op
|
||||
TESTS (long long, LLONG_MIN, LLONG_MAX)
|
||||
|
||||
#undef T
|
||||
#define T(n, t1, t2, tr, v1, v2, vr, b, o) t##n##b ();
|
||||
|
||||
int
|
||||
main ()
|
||||
{
|
||||
TESTS (long long, LLONG_MIN, LLONG_MAX)
|
||||
#undef U
|
||||
#define U(s, op) s##op##ll
|
||||
TESTS (long long, LLONG_MIN, LLONG_MAX)
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
/* Test __builtin_{add,sub,mul}_overflow. */
|
||||
/* { dg-do run } */
|
||||
/* { dg-skip-if "" { ! run_expensive_tests } { "*" } { "-O0" "-O2" } } */
|
||||
|
||||
#include "builtin-arith-overflow-1.h"
|
||||
|
||||
#define U(s, op) op
|
||||
TESTS (char, SCHAR_MIN, SCHAR_MAX)
|
||||
|
||||
#undef T
|
||||
#define T(n, t1, t2, tr, v1, v2, vr, b, o) t##n##b ();
|
||||
|
||||
int
|
||||
main ()
|
||||
{
|
||||
TESTS (char, SCHAR_MIN, SCHAR_MAX)
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
/* Test __builtin_{add,sub,mul}_overflow. */
|
||||
/* { dg-do run } */
|
||||
/* { dg-skip-if "" { ! run_expensive_tests } { "*" } { "-O0" "-O2" } } */
|
||||
|
||||
#include "builtin-arith-overflow-1.h"
|
||||
|
||||
#define U(s, op) op
|
||||
TESTS (short, SHRT_MIN, SHRT_MAX)
|
||||
|
||||
#undef T
|
||||
#define T(n, t1, t2, tr, v1, v2, vr, b, o) t##n##b ();
|
||||
|
||||
int
|
||||
main ()
|
||||
{
|
||||
TESTS (short, SHRT_MIN, SHRT_MAX)
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
/* Test __builtin_{add,sub,mul}_overflow. */
|
||||
/* { dg-do run { target int128 } } */
|
||||
/* { dg-skip-if "" { ! run_expensive_tests } { "*" } { "-O0" "-O2" } } */
|
||||
|
||||
#include "builtin-arith-overflow-1.h"
|
||||
|
||||
#define INT128_MAX ((signed __int128) (((unsigned __int128) 1 << (__SIZEOF_INT128__ * __CHAR_BIT__ - 1)) - 1))
|
||||
#define INT128_MIN (-INT128_MAX - 1)
|
||||
|
||||
#define U(s, op) op
|
||||
TESTS (__int128, INT128_MIN, INT128_MAX)
|
||||
|
||||
#undef T
|
||||
#define T(n, t1, t2, tr, v1, v2, vr, b, o) t##n##b ();
|
||||
|
||||
int
|
||||
main ()
|
||||
{
|
||||
TESTS (__int128, INT128_MIN, INT128_MAX)
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
/* Test __builtin_{add,sub}_overflow on {,un}signed char. */
|
||||
/* { dg-do run } */
|
||||
/* { dg-skip-if "" { ! run_expensive_tests } { "*" } { "-O0" "-O2" } } */
|
||||
|
||||
#define UCHAR_MAX ((unsigned char) ~0)
|
||||
#ifndef SHIFT
|
||||
typedef signed char S;
|
||||
typedef unsigned char U;
|
||||
typedef int W;
|
||||
#define SHIFT 0
|
||||
#define S_MAX __SCHAR_MAX__
|
||||
#define S_MIN (-__SCHAR_MAX__ - 1)
|
||||
#define COND (__SIZEOF_INT__ > 1)
|
||||
#endif
|
||||
|
||||
#define F(n, t1, t2, tr, b) \
|
||||
__attribute__((noinline, noclone)) tr \
|
||||
n (t1 x, t2 y, int *ovf) \
|
||||
{ \
|
||||
tr res; \
|
||||
*ovf = __builtin_##b##_overflow (x, y, &res); \
|
||||
return res; \
|
||||
}
|
||||
|
||||
F (spses, S, S, S, add)
|
||||
F (upueu, U, U, U, add)
|
||||
F (spseu, S, S, U, add)
|
||||
F (upues, U, U, S, add)
|
||||
F (spues, S, U, S, add)
|
||||
F (upses, U, S, S, add)
|
||||
F (spueu, S, U, U, add)
|
||||
F (upseu, U, S, U, add)
|
||||
F (ssses, S, S, S, sub)
|
||||
F (usueu, U, U, U, sub)
|
||||
F (ssseu, S, S, U, sub)
|
||||
F (usues, U, U, S, sub)
|
||||
F (ssues, S, U, S, sub)
|
||||
F (usses, U, S, S, sub)
|
||||
F (ssueu, S, U, U, sub)
|
||||
F (usseu, U, S, U, sub)
|
||||
|
||||
int
|
||||
main ()
|
||||
{
|
||||
#if COND
|
||||
int i, j;
|
||||
for (i = 0; i < UCHAR_MAX; i++)
|
||||
for (j = 0; j < UCHAR_MAX; j++)
|
||||
{
|
||||
S s1 = ((W) i << SHIFT) + S_MIN;
|
||||
U u1 = ((W) i << SHIFT);
|
||||
S s2 = ((W) j << SHIFT) + S_MIN;
|
||||
U u2 = ((W) j << SHIFT);
|
||||
W w;
|
||||
int ovf;
|
||||
#define T(n, t1, t2, tr, op) \
|
||||
w = ((W) t1##1) op ((W) t2##2); \
|
||||
if (n (t1##1, t2##2, &ovf) != (tr) w \
|
||||
|| ovf != (w != (tr) w)) \
|
||||
__builtin_abort ();
|
||||
T (spses, s, s, S, +)
|
||||
T (upueu, u, u, U, +)
|
||||
T (spseu, s, s, U, +)
|
||||
T (upues, u, u, S, +)
|
||||
T (spues, s, u, S, +)
|
||||
T (upses, u, s, S, +)
|
||||
T (spueu, s, u, U, +)
|
||||
T (upseu, u, s, U, +)
|
||||
T (ssses, s, s, S, -)
|
||||
T (usueu, u, u, U, -)
|
||||
T (ssseu, s, s, U, -)
|
||||
T (usues, u, u, S, -)
|
||||
T (ssues, s, u, S, -)
|
||||
T (usses, u, s, S, -)
|
||||
T (ssueu, s, u, U, -)
|
||||
T (usseu, u, s, U, -)
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
/* Test __builtin_{add,sub}_overflow on {,un}signed short. */
|
||||
/* { dg-do run } */
|
||||
/* { dg-skip-if "" { ! run_expensive_tests } { "*" } { "-O0" "-O2" } } */
|
||||
|
||||
typedef signed short int S;
|
||||
typedef unsigned short int U;
|
||||
#define COND 1
|
||||
#define SHIFT ((__SIZEOF_SHORT__ - 1) * __CHAR_BIT__)
|
||||
#define S_MAX __SHRT_MAX__
|
||||
#define S_MIN (-__SHRT_MAX__ - 1)
|
||||
#if __SIZEOF_INT__ > __SIZEOF_SHORT__
|
||||
typedef int W;
|
||||
#elif __SIZEOF_LONG__ > __SIZEOF_SHORT__
|
||||
typedef long int W;
|
||||
#elif __SIZEOF_LONG_LONG__ > __SIZEOF_SHORT__
|
||||
typedef long long int W;
|
||||
#elif __SIZEOF_INT128__ > __SIZEOF_SHORT__
|
||||
typedef __int128 W;
|
||||
#else
|
||||
#undef COND
|
||||
#define COND 0
|
||||
#endif
|
||||
#include "builtin-arith-overflow-7.c"
|
|
@ -0,0 +1,21 @@
|
|||
/* Test __builtin_{add,sub}_overflow on {,un}signed int. */
|
||||
/* { dg-do run } */
|
||||
/* { dg-skip-if "" { ! run_expensive_tests } { "*" } { "-O0" "-O2" } } */
|
||||
|
||||
typedef signed int S;
|
||||
typedef unsigned int U;
|
||||
#define COND 1
|
||||
#define SHIFT ((__SIZEOF_INT__ - 1) * __CHAR_BIT__)
|
||||
#define S_MAX __INT_MAX__
|
||||
#define S_MIN (-__INT_MAX__ - 1)
|
||||
#if __SIZEOF_LONG__ > __SIZEOF_INT__
|
||||
typedef long int W;
|
||||
#elif __SIZEOF_LONG_LONG__ > __SIZEOF_INT__
|
||||
typedef long long int W;
|
||||
#elif __SIZEOF_INT128__ > __SIZEOF_INT__
|
||||
typedef __int128 W;
|
||||
#else
|
||||
#undef COND
|
||||
#define COND 0
|
||||
#endif
|
||||
#include "builtin-arith-overflow-7.c"
|
|
@ -0,0 +1,94 @@
|
|||
#define SCHAR_MAX __SCHAR_MAX__
|
||||
#define SCHAR_MIN (-__SCHAR_MAX__ - 1)
|
||||
#define SHRT_MAX __SHRT_MAX__
|
||||
#define SHRT_MIN (-__SHRT_MAX__ - 1)
|
||||
#define INT_MAX __INT_MAX__
|
||||
#define INT_MIN (-__INT_MAX__ - 1)
|
||||
#define LONG_MAX __LONG_MAX__
|
||||
#define LONG_MIN (-__LONG_MAX__ - 1)
|
||||
#define LLONG_MAX __LONG_LONG_MAX__
|
||||
#define LLONG_MIN (-__LONG_LONG_MAX__ - 1)
|
||||
|
||||
int v;
|
||||
|
||||
__attribute__((noinline, noclone)) void
|
||||
bar (void)
|
||||
{
|
||||
v++;
|
||||
}
|
||||
|
||||
#define T(n, t1, t2, tr, v1, v2, vr, b, o) \
|
||||
__attribute__((noinline, noclone)) tr \
|
||||
t##n##_1##b (t1 x, t2 y) \
|
||||
{ \
|
||||
tr r; \
|
||||
if (__builtin_##b##_overflow (x, y, &r)) \
|
||||
bar (); \
|
||||
return r; \
|
||||
} \
|
||||
\
|
||||
__attribute__((noinline, noclone)) tr \
|
||||
t##n##_2##b (t2 y) \
|
||||
{ \
|
||||
t1 x = (v1); \
|
||||
tr r; \
|
||||
if (__builtin_##b##_overflow (x, y, &r)) \
|
||||
bar (); \
|
||||
return r; \
|
||||
} \
|
||||
\
|
||||
__attribute__((noinline, noclone)) tr \
|
||||
t##n##_3##b (t2 y) \
|
||||
{ \
|
||||
tr r; \
|
||||
if (__builtin_##b##_overflow ((t1) (v1), y, \
|
||||
&r)) \
|
||||
bar (); \
|
||||
return r; \
|
||||
} \
|
||||
\
|
||||
__attribute__((noinline, noclone)) tr \
|
||||
t##n##_4##b (t1 x) \
|
||||
{ \
|
||||
t2 y = (v2); \
|
||||
tr r; \
|
||||
if (__builtin_##b##_overflow (x, y, &r)) \
|
||||
bar (); \
|
||||
return r; \
|
||||
} \
|
||||
\
|
||||
__attribute__((noinline, noclone)) tr \
|
||||
t##n##_5##b (t1 x) \
|
||||
{ \
|
||||
tr r; \
|
||||
if (__builtin_##b##_overflow (x, (t2) (v2), \
|
||||
&r)) \
|
||||
bar (); \
|
||||
return r; \
|
||||
} \
|
||||
\
|
||||
__attribute__((noinline, noclone)) void \
|
||||
t##n##b (void) \
|
||||
{ \
|
||||
t1 x = (v1); \
|
||||
t2 y = (v2); \
|
||||
tr r1, r2; \
|
||||
v = 0; \
|
||||
if (t##n##_1##b (x, y) != (tr) (vr) \
|
||||
|| t##n##_2##b (y) != (tr) (vr) \
|
||||
|| t##n##_3##b (y) != (tr) (vr) \
|
||||
|| t##n##_4##b (x) != (tr) (vr) \
|
||||
|| t##n##_5##b (x) != (tr) (vr)) \
|
||||
__builtin_abort (); \
|
||||
if (__builtin_##b##_overflow (x, y, &r1)) \
|
||||
bar (); \
|
||||
if (r1 != (tr) (vr)) \
|
||||
__builtin_abort (); \
|
||||
if (__builtin_##b##_overflow ((t1) (v1), \
|
||||
(t2) (v2), &r2))\
|
||||
bar (); \
|
||||
if (r2 != (tr) (vr) || v != 7 * o) \
|
||||
__builtin_abort (); \
|
||||
}
|
||||
#define ST(n, t, v1, v2, vr, b, o) \
|
||||
T (n, t, t, t, v1, v2, vr, b, o)
|
|
@ -0,0 +1,132 @@
|
|||
/* { dg-do run } */
|
||||
/* { dg-options "-O2 -fdump-tree-optimized -g" } */
|
||||
|
||||
/* SUB_OVERFLOW should be folded into unsigned subtraction,
|
||||
because ovf is never used. */
|
||||
__attribute__((noinline, noclone)) int
|
||||
fn1 (int x, unsigned int y)
|
||||
{
|
||||
int res;
|
||||
int ovf = __builtin_sub_overflow (x, y, &res);
|
||||
int res2 = res;
|
||||
int res3 = res2 - 2;
|
||||
(void) ovf;
|
||||
return res;
|
||||
}
|
||||
|
||||
/* MUL_OVERFLOW should be folded into unsigned multiplication,
|
||||
because ovf is never used. */
|
||||
__attribute__((noinline, noclone)) int
|
||||
fn2 (char x, long int y)
|
||||
{
|
||||
short int res;
|
||||
int ovf = __builtin_mul_overflow (x, y, &res);
|
||||
int res2 = res;
|
||||
int res3 = res2 - 2;
|
||||
(void) ovf;
|
||||
return res;
|
||||
}
|
||||
|
||||
#if __SIZEOF_INT__ > __SIZEOF_SHORT__ && __SIZEOF_INT__ > 1
|
||||
/* ADD_OVERFLOW should be folded into unsigned addition,
|
||||
because it never overflows. */
|
||||
__attribute__((noinline, noclone)) int
|
||||
fn3 (char x, unsigned short y, int *ovf)
|
||||
{
|
||||
int res;
|
||||
*ovf = __builtin_add_overflow (x, y, &res);
|
||||
return res;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* MUL_OVERFLOW should be folded into unsigned multiplication,
|
||||
because it never overflows. */
|
||||
__attribute__((noinline, noclone)) long int
|
||||
fn4 (long int x, long int y, int *ovf)
|
||||
{
|
||||
long int res;
|
||||
x &= 65535;
|
||||
y = (y & 65535) - 32768;
|
||||
*ovf = __builtin_mul_overflow (x, y, &res);
|
||||
return res;
|
||||
}
|
||||
|
||||
#if __SIZEOF_INT__ > 1
|
||||
/* MUL_OVERFLOW should be folded into unsigned multiplication,
|
||||
because it always overflows. */
|
||||
__attribute__((noinline, noclone)) signed char
|
||||
fn5 (long int x, long int y, int *ovf)
|
||||
{
|
||||
signed char res;
|
||||
x = (x & 63) + (__SCHAR_MAX__ / 4);
|
||||
y = (y & 3) + 5;
|
||||
*ovf = __builtin_mul_overflow (x, y, &res);
|
||||
return res;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* ADD_OVERFLOW should be folded into unsigned additrion,
|
||||
because it never overflows. */
|
||||
__attribute__((noinline, noclone)) unsigned char
|
||||
fn6 (unsigned char x, unsigned char y, int *ovf)
|
||||
{
|
||||
unsigned char res;
|
||||
x = (x & 63) + ((unsigned char) ~0 - 66);
|
||||
y = (y & 3);
|
||||
*ovf = __builtin_add_overflow (x, y, &res);
|
||||
return res;
|
||||
}
|
||||
|
||||
/* ADD_OVERFLOW should be folded into unsigned additrion,
|
||||
because it always overflows. */
|
||||
__attribute__((noinline, noclone)) unsigned char
|
||||
fn7 (unsigned char x, unsigned char y, int *ovf)
|
||||
{
|
||||
unsigned char res;
|
||||
x = (x & 15) + ((unsigned char) ~0 - 15);
|
||||
y = (y & 3) + 16;
|
||||
*ovf = __builtin_add_overflow (x, y, &res);
|
||||
return res;
|
||||
}
|
||||
|
||||
int
|
||||
main ()
|
||||
{
|
||||
int ovf;
|
||||
if (fn1 (-10, __INT_MAX__) != (int) (-10U - __INT_MAX__)
|
||||
|| fn2 (0, 0) != 0
|
||||
|| fn2 (32, 16383) != (short int) 524256ULL)
|
||||
__builtin_abort ();
|
||||
#if __SIZEOF_INT__ > __SIZEOF_SHORT__ && __SIZEOF_INT__ > 1
|
||||
if (fn3 (__SCHAR_MAX__, (unsigned short) ~0, &ovf) != (int) (__SCHAR_MAX__ + (unsigned short) ~0)
|
||||
|| ovf
|
||||
|| fn3 (-__SCHAR_MAX__ - 1, 0, &ovf) != (int) (-__SCHAR_MAX__ - 1)
|
||||
|| ovf)
|
||||
__builtin_abort ();
|
||||
#endif
|
||||
if (fn4 (65535, 0, &ovf) != 65535L * -32768 || ovf)
|
||||
__builtin_abort ();
|
||||
#if __SIZEOF_INT__ > 1
|
||||
if (fn5 (0, 0, &ovf) != (signed char) (__SCHAR_MAX__ / 4 * 5)
|
||||
|| !ovf
|
||||
|| fn5 (63, 3, &ovf) != (signed char) ((__SCHAR_MAX__ / 4 + 63) * 8)
|
||||
|| !ovf)
|
||||
__builtin_abort ();
|
||||
#endif
|
||||
if (fn6 (0, 0, &ovf) != (unsigned char) ~0 - 66
|
||||
|| ovf
|
||||
|| fn6 (63, 3, &ovf) != (unsigned char) ~0
|
||||
|| ovf)
|
||||
__builtin_abort ();
|
||||
if (fn7 (0, 0, &ovf) != 0
|
||||
|| !ovf
|
||||
|| fn7 (63, 3, &ovf) != 18
|
||||
|| !ovf)
|
||||
__builtin_abort ();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* { dg-final { scan-tree-dump-not "ADD_OVERFLOW" "optimized" } } */
|
||||
/* { dg-final { scan-tree-dump-not "SUB_OVERFLOW" "optimized" } } */
|
||||
/* { dg-final { scan-tree-dump-not "MUL_OVERFLOW" "optimized" } } */
|
||||
/* { dg-final { cleanup-tree-dump "optimized" } } */
|
|
@ -0,0 +1,110 @@
|
|||
/* { dg-do run } */
|
||||
/* { dg-options "-O2 -fdump-tree-optimized" } */
|
||||
|
||||
/* MUL_OVERFLOW should not be folded into unsigned multiplication,
|
||||
because it sometimes overflows and sometimes does not. */
|
||||
__attribute__((noinline, noclone)) long int
|
||||
fn1 (long int x, long int y, int *ovf)
|
||||
{
|
||||
long int res;
|
||||
x &= 65535;
|
||||
y = (y & 65535) - (__LONG_MAX__ / 65535 + 32768);
|
||||
*ovf = __builtin_mul_overflow (x, y, &res);
|
||||
return res;
|
||||
}
|
||||
|
||||
/* MUL_OVERFLOW should not be folded into unsigned multiplication,
|
||||
because it sometimes overflows and sometimes does not. */
|
||||
__attribute__((noinline, noclone)) signed char
|
||||
fn2 (long int x, long int y, int *ovf)
|
||||
{
|
||||
signed char res;
|
||||
x = (x & 63) + (__SCHAR_MAX__ / 4);
|
||||
y = (y & 3) + 4;
|
||||
*ovf = __builtin_mul_overflow (x, y, &res);
|
||||
return res;
|
||||
}
|
||||
|
||||
/* ADD_OVERFLOW should be folded into unsigned additrion,
|
||||
because it sometimes overflows and sometimes does not. */
|
||||
__attribute__((noinline, noclone)) unsigned char
|
||||
fn3 (unsigned char x, unsigned char y, int *ovf)
|
||||
{
|
||||
unsigned char res;
|
||||
x = (x & 63) + ((unsigned char) ~0 - 65);
|
||||
y = (y & 3);
|
||||
*ovf = __builtin_add_overflow (x, y, &res);
|
||||
return res;
|
||||
}
|
||||
|
||||
/* ADD_OVERFLOW should be folded into unsigned additrion,
|
||||
because it sometimes overflows and sometimes does not. */
|
||||
__attribute__((noinline, noclone)) unsigned char
|
||||
fn4 (unsigned char x, unsigned char y, int *ovf)
|
||||
{
|
||||
unsigned char res;
|
||||
x = (x & 15) + ((unsigned char) ~0 - 16);
|
||||
y = (y & 3) + 16;
|
||||
*ovf = __builtin_add_overflow (x, y, &res);
|
||||
return res;
|
||||
}
|
||||
|
||||
/* MUL_OVERFLOW should not be folded into unsigned multiplication,
|
||||
because it sometimes overflows and sometimes does not. */
|
||||
__attribute__((noinline, noclone)) long int
|
||||
fn5 (long int x, unsigned long int y, int *ovf)
|
||||
{
|
||||
long int res;
|
||||
y = -65536UL + (y & 65535);
|
||||
*ovf = __builtin_mul_overflow (x, y, &res);
|
||||
return res;
|
||||
}
|
||||
|
||||
int
|
||||
main ()
|
||||
{
|
||||
int ovf;
|
||||
if (fn1 (0, 0, &ovf) != 0
|
||||
|| ovf
|
||||
|| fn1 (65535, 0, &ovf) != (long int) ((__LONG_MAX__ / 65535 + 32768UL) * -65535UL)
|
||||
|| !ovf)
|
||||
__builtin_abort ();
|
||||
if (fn2 (0, 0, &ovf) != (signed char) (__SCHAR_MAX__ / 4 * 4U)
|
||||
|| ovf
|
||||
|| fn2 (0, 1, &ovf) != (signed char) (__SCHAR_MAX__ / 4 * 5U)
|
||||
|| !ovf)
|
||||
__builtin_abort ();
|
||||
if (fn3 (0, 0, &ovf) != (unsigned char) ~0 - 65
|
||||
|| ovf
|
||||
|| fn3 (63, 2, &ovf) != (unsigned char) ~0
|
||||
|| ovf
|
||||
|| fn3 (62, 3, &ovf) != (unsigned char) ~0
|
||||
|| ovf
|
||||
|| fn3 (63, 3, &ovf) != 0
|
||||
|| !ovf)
|
||||
__builtin_abort ();
|
||||
if (fn4 (0, 0, &ovf) != (unsigned char) ~0
|
||||
|| ovf
|
||||
|| fn4 (1, 0, &ovf) != 0
|
||||
|| !ovf
|
||||
|| fn4 (0, 1, &ovf) != 0
|
||||
|| !ovf
|
||||
|| fn4 (63, 3, &ovf) != 17
|
||||
|| !ovf)
|
||||
__builtin_abort ();
|
||||
if (fn5 (0, 0, &ovf) != 0
|
||||
|| ovf
|
||||
|| fn5 (1, 0, &ovf) != -65536L
|
||||
|| !ovf
|
||||
|| fn5 (2, 32768, &ovf) != -65536L
|
||||
|| !ovf
|
||||
|| fn5 (4, 32768 + 16384 + 8192, &ovf) != -32768L
|
||||
|| !ovf)
|
||||
__builtin_abort ();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* { dg-final { scan-tree-dump-times "ADD_OVERFLOW" 2 "optimized" } } */
|
||||
/* { dg-final { scan-tree-dump-times "SUB_OVERFLOW" 0 "optimized" } } */
|
||||
/* { dg-final { scan-tree-dump-times "MUL_OVERFLOW" 3 "optimized" } } */
|
||||
/* { dg-final { cleanup-tree-dump "optimized" } } */
|
|
@ -85,6 +85,8 @@ along with GCC; see the file COPYING3. If not see
|
|||
#include "cfgloop.h"
|
||||
#include "tree-scalar-evolution.h"
|
||||
#include "tree-chkp.h"
|
||||
#include "tree-ssa-propagate.h"
|
||||
#include "gimple-fold.h"
|
||||
|
||||
static struct stmt_stats
|
||||
{
|
||||
|
@ -1162,6 +1164,109 @@ remove_dead_stmt (gimple_stmt_iterator *i, basic_block bb)
|
|||
release_defs (stmt);
|
||||
}
|
||||
|
||||
/* Helper for maybe_optimize_arith_overflow. Find in *TP if there are any
|
||||
uses of data (SSA_NAME) other than REALPART_EXPR referencing it. */
|
||||
|
||||
static tree
|
||||
find_non_realpart_uses (tree *tp, int *walk_subtrees, void *data)
|
||||
{
|
||||
if (TYPE_P (*tp) || TREE_CODE (*tp) == REALPART_EXPR)
|
||||
*walk_subtrees = 0;
|
||||
if (*tp == (tree) data)
|
||||
return *tp;
|
||||
return NULL_TREE;
|
||||
}
|
||||
|
||||
/* If the IMAGPART_EXPR of the {ADD,SUB,MUL}_OVERFLOW result is never used,
|
||||
but REALPART_EXPR is, optimize the {ADD,SUB,MUL}_OVERFLOW internal calls
|
||||
into plain unsigned {PLUS,MINUS,MULT}_EXPR, and if needed reset debug
|
||||
uses. */
|
||||
|
||||
static void
|
||||
maybe_optimize_arith_overflow (gimple_stmt_iterator *gsi,
|
||||
enum tree_code subcode)
|
||||
{
|
||||
gimple stmt = gsi_stmt (*gsi);
|
||||
tree lhs = gimple_call_lhs (stmt);
|
||||
|
||||
if (lhs == NULL || TREE_CODE (lhs) != SSA_NAME)
|
||||
return;
|
||||
|
||||
imm_use_iterator imm_iter;
|
||||
use_operand_p use_p;
|
||||
bool has_debug_uses = false;
|
||||
bool has_realpart_uses = false;
|
||||
bool has_other_uses = false;
|
||||
FOR_EACH_IMM_USE_FAST (use_p, imm_iter, lhs)
|
||||
{
|
||||
gimple use_stmt = USE_STMT (use_p);
|
||||
if (is_gimple_debug (use_stmt))
|
||||
has_debug_uses = true;
|
||||
else if (is_gimple_assign (use_stmt)
|
||||
&& gimple_assign_rhs_code (use_stmt) == REALPART_EXPR
|
||||
&& TREE_OPERAND (gimple_assign_rhs1 (use_stmt), 0) == lhs)
|
||||
has_realpart_uses = true;
|
||||
else
|
||||
{
|
||||
has_other_uses = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!has_realpart_uses || has_other_uses)
|
||||
return;
|
||||
|
||||
tree arg0 = gimple_call_arg (stmt, 0);
|
||||
tree arg1 = gimple_call_arg (stmt, 1);
|
||||
location_t loc = gimple_location (stmt);
|
||||
tree type = TREE_TYPE (TREE_TYPE (lhs));
|
||||
tree utype = type;
|
||||
if (!TYPE_UNSIGNED (type))
|
||||
utype = build_nonstandard_integer_type (TYPE_PRECISION (type), 1);
|
||||
tree result = fold_build2_loc (loc, subcode, utype,
|
||||
fold_convert_loc (loc, utype, arg0),
|
||||
fold_convert_loc (loc, utype, arg1));
|
||||
result = fold_convert_loc (loc, type, result);
|
||||
|
||||
if (has_debug_uses)
|
||||
{
|
||||
gimple use_stmt;
|
||||
FOR_EACH_IMM_USE_STMT (use_stmt, imm_iter, lhs)
|
||||
{
|
||||
if (!gimple_debug_bind_p (use_stmt))
|
||||
continue;
|
||||
tree v = gimple_debug_bind_get_value (use_stmt);
|
||||
if (walk_tree (&v, find_non_realpart_uses, lhs, NULL))
|
||||
{
|
||||
gimple_debug_bind_reset_value (use_stmt);
|
||||
update_stmt (use_stmt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (TREE_CODE (result) == INTEGER_CST && TREE_OVERFLOW (result))
|
||||
result = drop_tree_overflow (result);
|
||||
tree overflow = build_zero_cst (type);
|
||||
tree ctype = build_complex_type (type);
|
||||
if (TREE_CODE (result) == INTEGER_CST)
|
||||
result = build_complex (ctype, result, overflow);
|
||||
else
|
||||
result = build2_loc (gimple_location (stmt), COMPLEX_EXPR,
|
||||
ctype, result, overflow);
|
||||
|
||||
if (dump_file && (dump_flags & TDF_DETAILS))
|
||||
{
|
||||
fprintf (dump_file, "Transforming call: ");
|
||||
print_gimple_stmt (dump_file, stmt, 0, TDF_SLIM);
|
||||
fprintf (dump_file, "because the overflow result is never used into: ");
|
||||
print_generic_stmt (dump_file, result, TDF_SLIM);
|
||||
fprintf (dump_file, "\n");
|
||||
}
|
||||
|
||||
if (!update_call_from_tree (gsi, result))
|
||||
gimplify_and_update_call_from_tree (gsi, result);
|
||||
}
|
||||
|
||||
/* Eliminate unnecessary statements. Any instruction not marked as necessary
|
||||
contributes nothing to the program, and can be deleted. */
|
||||
|
||||
|
@ -1298,6 +1403,21 @@ eliminate_unnecessary_stmts (void)
|
|||
update_stmt (stmt);
|
||||
release_ssa_name (name);
|
||||
}
|
||||
else if (gimple_call_internal_p (stmt))
|
||||
switch (gimple_call_internal_fn (stmt))
|
||||
{
|
||||
case IFN_ADD_OVERFLOW:
|
||||
maybe_optimize_arith_overflow (&gsi, PLUS_EXPR);
|
||||
break;
|
||||
case IFN_SUB_OVERFLOW:
|
||||
maybe_optimize_arith_overflow (&gsi, MINUS_EXPR);
|
||||
break;
|
||||
case IFN_MUL_OVERFLOW:
|
||||
maybe_optimize_arith_overflow (&gsi, MULT_EXPR);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
315
gcc/tree-vrp.c
315
gcc/tree-vrp.c
|
@ -3754,6 +3754,113 @@ extract_range_from_comparison (value_range_t *vr, enum tree_code code,
|
|||
set_value_range_to_truthvalue (vr, type);
|
||||
}
|
||||
|
||||
/* Helper function for simplify_internal_call_using_ranges and
|
||||
extract_range_basic. Return true if OP0 SUBCODE OP1 for
|
||||
SUBCODE {PLUS,MINUS,MULT}_EXPR is known to never overflow or
|
||||
always overflow. Set *OVF to true if it is known to always
|
||||
overflow. */
|
||||
|
||||
static bool
|
||||
check_for_binary_op_overflow (enum tree_code subcode, tree type,
|
||||
tree op0, tree op1, bool *ovf)
|
||||
{
|
||||
value_range_t vr0 = VR_INITIALIZER;
|
||||
value_range_t vr1 = VR_INITIALIZER;
|
||||
if (TREE_CODE (op0) == SSA_NAME)
|
||||
vr0 = *get_value_range (op0);
|
||||
else if (TREE_CODE (op0) == INTEGER_CST)
|
||||
set_value_range_to_value (&vr0, op0, NULL);
|
||||
else
|
||||
set_value_range_to_varying (&vr0);
|
||||
|
||||
if (TREE_CODE (op1) == SSA_NAME)
|
||||
vr1 = *get_value_range (op1);
|
||||
else if (TREE_CODE (op1) == INTEGER_CST)
|
||||
set_value_range_to_value (&vr1, op1, NULL);
|
||||
else
|
||||
set_value_range_to_varying (&vr1);
|
||||
|
||||
if (!range_int_cst_p (&vr0)
|
||||
|| TREE_OVERFLOW (vr0.min)
|
||||
|| TREE_OVERFLOW (vr0.max))
|
||||
{
|
||||
vr0.min = vrp_val_min (TREE_TYPE (op0));
|
||||
vr0.max = vrp_val_max (TREE_TYPE (op0));
|
||||
}
|
||||
if (!range_int_cst_p (&vr1)
|
||||
|| TREE_OVERFLOW (vr1.min)
|
||||
|| TREE_OVERFLOW (vr1.max))
|
||||
{
|
||||
vr1.min = vrp_val_min (TREE_TYPE (op1));
|
||||
vr1.max = vrp_val_max (TREE_TYPE (op1));
|
||||
}
|
||||
*ovf = arith_overflowed_p (subcode, type, vr0.min,
|
||||
subcode == MINUS_EXPR ? vr1.max : vr1.min);
|
||||
if (arith_overflowed_p (subcode, type, vr0.max,
|
||||
subcode == MINUS_EXPR ? vr1.min : vr1.max) != *ovf)
|
||||
return false;
|
||||
if (subcode == MULT_EXPR)
|
||||
{
|
||||
if (arith_overflowed_p (subcode, type, vr0.min, vr1.max) != *ovf
|
||||
|| arith_overflowed_p (subcode, type, vr0.max, vr1.min) != *ovf)
|
||||
return false;
|
||||
}
|
||||
if (*ovf)
|
||||
{
|
||||
/* So far we found that there is an overflow on the boundaries.
|
||||
That doesn't prove that there is an overflow even for all values
|
||||
in between the boundaries. For that compute widest_int range
|
||||
of the result and see if it doesn't overlap the range of
|
||||
type. */
|
||||
widest_int wmin, wmax;
|
||||
widest_int w[4];
|
||||
int i;
|
||||
w[0] = wi::to_widest (vr0.min);
|
||||
w[1] = wi::to_widest (vr0.max);
|
||||
w[2] = wi::to_widest (vr1.min);
|
||||
w[3] = wi::to_widest (vr1.max);
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
widest_int wt;
|
||||
switch (subcode)
|
||||
{
|
||||
case PLUS_EXPR:
|
||||
wt = wi::add (w[i & 1], w[2 + (i & 2) / 2]);
|
||||
break;
|
||||
case MINUS_EXPR:
|
||||
wt = wi::sub (w[i & 1], w[2 + (i & 2) / 2]);
|
||||
break;
|
||||
case MULT_EXPR:
|
||||
wt = wi::mul (w[i & 1], w[2 + (i & 2) / 2]);
|
||||
break;
|
||||
default:
|
||||
gcc_unreachable ();
|
||||
}
|
||||
if (i == 0)
|
||||
{
|
||||
wmin = wt;
|
||||
wmax = wt;
|
||||
}
|
||||
else
|
||||
{
|
||||
wmin = wi::smin (wmin, wt);
|
||||
wmax = wi::smax (wmax, wt);
|
||||
}
|
||||
}
|
||||
/* The result of op0 CODE op1 is known to be in range
|
||||
[wmin, wmax]. */
|
||||
widest_int wtmin = wi::to_widest (vrp_val_min (type));
|
||||
widest_int wtmax = wi::to_widest (vrp_val_max (type));
|
||||
/* If all values in [wmin, wmax] are smaller than
|
||||
[wtmin, wtmax] or all are larger than [wtmin, wtmax],
|
||||
the arithmetic operation will always overflow. */
|
||||
if (wi::lts_p (wmax, wtmin) || wi::gts_p (wmin, wtmax))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Try to derive a nonnegative or nonzero range out of STMT relying
|
||||
primarily on generic routines in fold in conjunction with range data.
|
||||
Store the result in *VR */
|
||||
|
@ -3943,8 +4050,7 @@ extract_range_basic (value_range_t *vr, gimple stmt)
|
|||
break;
|
||||
}
|
||||
}
|
||||
else if (is_gimple_call (stmt)
|
||||
&& gimple_call_internal_p (stmt))
|
||||
else if (is_gimple_call (stmt) && gimple_call_internal_p (stmt))
|
||||
{
|
||||
enum tree_code subcode = ERROR_MARK;
|
||||
switch (gimple_call_internal_fn (stmt))
|
||||
|
@ -3984,6 +4090,84 @@ extract_range_basic (value_range_t *vr, gimple stmt)
|
|||
return;
|
||||
}
|
||||
}
|
||||
/* Handle extraction of the two results (result of arithmetics and
|
||||
a flag whether arithmetics overflowed) from {ADD,SUB,MUL}_OVERFLOW
|
||||
internal function. */
|
||||
else if (is_gimple_assign (stmt)
|
||||
&& (gimple_assign_rhs_code (stmt) == REALPART_EXPR
|
||||
|| gimple_assign_rhs_code (stmt) == IMAGPART_EXPR)
|
||||
&& INTEGRAL_TYPE_P (type))
|
||||
{
|
||||
enum tree_code code = gimple_assign_rhs_code (stmt);
|
||||
tree op = gimple_assign_rhs1 (stmt);
|
||||
if (TREE_CODE (op) == code && TREE_CODE (TREE_OPERAND (op, 0)) == SSA_NAME)
|
||||
{
|
||||
gimple g = SSA_NAME_DEF_STMT (TREE_OPERAND (op, 0));
|
||||
if (is_gimple_call (g) && gimple_call_internal_p (g))
|
||||
{
|
||||
enum tree_code subcode = ERROR_MARK;
|
||||
switch (gimple_call_internal_fn (g))
|
||||
{
|
||||
case IFN_ADD_OVERFLOW:
|
||||
subcode = PLUS_EXPR;
|
||||
break;
|
||||
case IFN_SUB_OVERFLOW:
|
||||
subcode = MINUS_EXPR;
|
||||
break;
|
||||
case IFN_MUL_OVERFLOW:
|
||||
subcode = MULT_EXPR;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (subcode != ERROR_MARK)
|
||||
{
|
||||
tree op0 = gimple_call_arg (g, 0);
|
||||
tree op1 = gimple_call_arg (g, 1);
|
||||
if (code == IMAGPART_EXPR)
|
||||
{
|
||||
bool ovf = false;
|
||||
if (check_for_binary_op_overflow (subcode, type,
|
||||
op0, op1, &ovf))
|
||||
set_value_range_to_value (vr,
|
||||
build_int_cst (type, ovf),
|
||||
NULL);
|
||||
else
|
||||
set_value_range (vr, VR_RANGE, build_int_cst (type, 0),
|
||||
build_int_cst (type, 1), NULL);
|
||||
}
|
||||
else if (types_compatible_p (type, TREE_TYPE (op0))
|
||||
&& types_compatible_p (type, TREE_TYPE (op1)))
|
||||
{
|
||||
bool saved_flag_wrapv = flag_wrapv;
|
||||
/* Pretend the arithmetics is wrapping. If there is
|
||||
any overflow, IMAGPART_EXPR will be set. */
|
||||
flag_wrapv = 1;
|
||||
extract_range_from_binary_expr (vr, subcode, type,
|
||||
op0, op1);
|
||||
flag_wrapv = saved_flag_wrapv;
|
||||
}
|
||||
else
|
||||
{
|
||||
value_range_t vr0 = VR_INITIALIZER;
|
||||
value_range_t vr1 = VR_INITIALIZER;
|
||||
bool saved_flag_wrapv = flag_wrapv;
|
||||
/* Pretend the arithmetics is wrapping. If there is
|
||||
any overflow, IMAGPART_EXPR will be set. */
|
||||
flag_wrapv = 1;
|
||||
extract_range_from_unary_expr (&vr0, NOP_EXPR,
|
||||
type, op0);
|
||||
extract_range_from_unary_expr (&vr1, NOP_EXPR,
|
||||
type, op1);
|
||||
extract_range_from_binary_expr_1 (vr, subcode, type,
|
||||
&vr0, &vr1);
|
||||
flag_wrapv = saved_flag_wrapv;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (INTEGRAL_TYPE_P (type)
|
||||
&& gimple_stmt_nonnegative_warnv_p (stmt, &sop))
|
||||
set_value_range_to_nonnegative (vr, type,
|
||||
|
@ -9419,87 +9603,100 @@ static bool
|
|||
simplify_internal_call_using_ranges (gimple_stmt_iterator *gsi, gimple stmt)
|
||||
{
|
||||
enum tree_code subcode;
|
||||
bool is_ubsan = false;
|
||||
bool ovf = false;
|
||||
switch (gimple_call_internal_fn (stmt))
|
||||
{
|
||||
case IFN_UBSAN_CHECK_ADD:
|
||||
subcode = PLUS_EXPR;
|
||||
is_ubsan = true;
|
||||
break;
|
||||
case IFN_UBSAN_CHECK_SUB:
|
||||
subcode = MINUS_EXPR;
|
||||
is_ubsan = true;
|
||||
break;
|
||||
case IFN_UBSAN_CHECK_MUL:
|
||||
subcode = MULT_EXPR;
|
||||
is_ubsan = true;
|
||||
break;
|
||||
case IFN_ADD_OVERFLOW:
|
||||
subcode = PLUS_EXPR;
|
||||
break;
|
||||
case IFN_SUB_OVERFLOW:
|
||||
subcode = MINUS_EXPR;
|
||||
break;
|
||||
case IFN_MUL_OVERFLOW:
|
||||
subcode = MULT_EXPR;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
value_range_t vr0 = VR_INITIALIZER;
|
||||
value_range_t vr1 = VR_INITIALIZER;
|
||||
tree op0 = gimple_call_arg (stmt, 0);
|
||||
tree op1 = gimple_call_arg (stmt, 1);
|
||||
|
||||
if (TREE_CODE (op0) == SSA_NAME)
|
||||
vr0 = *get_value_range (op0);
|
||||
else if (TREE_CODE (op0) == INTEGER_CST)
|
||||
set_value_range_to_value (&vr0, op0, NULL);
|
||||
tree type;
|
||||
if (is_ubsan)
|
||||
type = TREE_TYPE (op0);
|
||||
else if (gimple_call_lhs (stmt) == NULL_TREE)
|
||||
return false;
|
||||
else
|
||||
set_value_range_to_varying (&vr0);
|
||||
type = TREE_TYPE (TREE_TYPE (gimple_call_lhs (stmt)));
|
||||
if (!check_for_binary_op_overflow (subcode, type, op0, op1, &ovf)
|
||||
|| (is_ubsan && ovf))
|
||||
return false;
|
||||
|
||||
if (TREE_CODE (op1) == SSA_NAME)
|
||||
vr1 = *get_value_range (op1);
|
||||
else if (TREE_CODE (op1) == INTEGER_CST)
|
||||
set_value_range_to_value (&vr1, op1, NULL);
|
||||
else
|
||||
set_value_range_to_varying (&vr1);
|
||||
|
||||
if (!range_int_cst_p (&vr0))
|
||||
{
|
||||
/* If one range is VR_ANTI_RANGE, VR_VARYING etc.,
|
||||
optimize at least x = y + 0; x = y - 0; x = y * 0;
|
||||
and x = y * 1; which never overflow. */
|
||||
if (!range_int_cst_p (&vr1))
|
||||
return false;
|
||||
if (tree_int_cst_sgn (vr1.min) == -1)
|
||||
return false;
|
||||
if (compare_tree_int (vr1.max, subcode == MULT_EXPR) == 1)
|
||||
return false;
|
||||
}
|
||||
else if (!range_int_cst_p (&vr1))
|
||||
{
|
||||
/* If one range is VR_ANTI_RANGE, VR_VARYING etc.,
|
||||
optimize at least x = 0 + y; x = 0 * y; and x = 1 * y;
|
||||
which never overflow. */
|
||||
if (subcode == MINUS_EXPR)
|
||||
return false;
|
||||
if (!range_int_cst_p (&vr0))
|
||||
return false;
|
||||
if (tree_int_cst_sgn (vr0.min) == -1)
|
||||
return false;
|
||||
if (compare_tree_int (vr0.max, subcode == MULT_EXPR) == 1)
|
||||
return false;
|
||||
}
|
||||
gimple g;
|
||||
location_t loc = gimple_location (stmt);
|
||||
if (is_ubsan)
|
||||
g = gimple_build_assign_with_ops (subcode, gimple_call_lhs (stmt),
|
||||
op0, op1);
|
||||
else
|
||||
{
|
||||
tree r1 = int_const_binop (subcode, vr0.min,
|
||||
subcode == MINUS_EXPR ? vr1.max : vr1.min);
|
||||
tree r2 = int_const_binop (subcode, vr0.max,
|
||||
subcode == MINUS_EXPR ? vr1.min : vr1.max);
|
||||
if (r1 == NULL_TREE || TREE_OVERFLOW (r1)
|
||||
|| r2 == NULL_TREE || TREE_OVERFLOW (r2))
|
||||
return false;
|
||||
if (subcode == MULT_EXPR)
|
||||
int prec = TYPE_PRECISION (type);
|
||||
tree utype = type;
|
||||
if (ovf
|
||||
|| !useless_type_conversion_p (type, TREE_TYPE (op0))
|
||||
|| !useless_type_conversion_p (type, TREE_TYPE (op1)))
|
||||
utype = build_nonstandard_integer_type (prec, 1);
|
||||
if (TREE_CODE (op0) == INTEGER_CST)
|
||||
op0 = fold_convert (utype, op0);
|
||||
else if (!useless_type_conversion_p (utype, TREE_TYPE (op0)))
|
||||
{
|
||||
tree r3 = int_const_binop (subcode, vr0.min, vr1.max);
|
||||
tree r4 = int_const_binop (subcode, vr0.max, vr1.min);
|
||||
if (r3 == NULL_TREE || TREE_OVERFLOW (r3)
|
||||
|| r4 == NULL_TREE || TREE_OVERFLOW (r4))
|
||||
return false;
|
||||
g = gimple_build_assign_with_ops (NOP_EXPR,
|
||||
make_ssa_name (utype, NULL),
|
||||
op0, NULL_TREE);
|
||||
gimple_set_location (g, loc);
|
||||
gsi_insert_before (gsi, g, GSI_SAME_STMT);
|
||||
op0 = gimple_assign_lhs (g);
|
||||
}
|
||||
if (TREE_CODE (op1) == INTEGER_CST)
|
||||
op1 = fold_convert (utype, op1);
|
||||
else if (!useless_type_conversion_p (utype, TREE_TYPE (op1)))
|
||||
{
|
||||
g = gimple_build_assign_with_ops (NOP_EXPR,
|
||||
make_ssa_name (utype, NULL),
|
||||
op1, NULL_TREE);
|
||||
gimple_set_location (g, loc);
|
||||
gsi_insert_before (gsi, g, GSI_SAME_STMT);
|
||||
op1 = gimple_assign_lhs (g);
|
||||
}
|
||||
g = gimple_build_assign_with_ops (subcode, make_ssa_name (utype, NULL),
|
||||
op0, op1);
|
||||
gimple_set_location (g, loc);
|
||||
gsi_insert_before (gsi, g, GSI_SAME_STMT);
|
||||
if (utype != type)
|
||||
{
|
||||
g = gimple_build_assign_with_ops (NOP_EXPR,
|
||||
make_ssa_name (type, NULL),
|
||||
gimple_assign_lhs (g), NULL_TREE);
|
||||
gimple_set_location (g, loc);
|
||||
gsi_insert_before (gsi, g, GSI_SAME_STMT);
|
||||
}
|
||||
g = gimple_build_assign_with_ops (COMPLEX_EXPR, gimple_call_lhs (stmt),
|
||||
gimple_assign_lhs (g),
|
||||
build_int_cst (type, ovf));
|
||||
}
|
||||
|
||||
gimple g = gimple_build_assign_with_ops (subcode, gimple_call_lhs (stmt),
|
||||
op0, op1);
|
||||
gimple_set_location (g, loc);
|
||||
gsi_replace (gsi, g, false);
|
||||
return true;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue