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:
Jakub Jelinek 2014-11-12 13:28:06 +01:00 committed by Jakub Jelinek
parent 6a3cbe9092
commit 1304953e4f
45 changed files with 3079 additions and 316 deletions

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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;
}

View File

@ -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)

View File

@ -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

View File

@ -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;
}

View File

@ -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>

View File

@ -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

View File

@ -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;

View File

@ -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)

View File

@ -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:

View File

@ -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;

View File

@ -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));

View File

@ -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))

File diff suppressed because it is too large Load Diff

View File

@ -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)

View File

@ -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")

View File

@ -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

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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)

View File

@ -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"

View File

@ -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"

View File

@ -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;
}

View File

@ -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)

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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"

View File

@ -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"

View File

@ -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)

View File

@ -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" } } */

View File

@ -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" } } */

View File

@ -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;
}
}
}
}

View File

@ -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;
}