diff options
author | Jakub Jelinek <jakub@redhat.com> | 2014-11-12 13:28:06 +0100 |
---|---|---|
committer | Jakub Jelinek <jakub@gcc.gnu.org> | 2014-11-12 13:28:06 +0100 |
commit | 1304953e4fa46305d1ce0b884bf2f58e08409ff3 (patch) | |
tree | 551c242f138ac032ded6f0171ee39700e6ea032e /gcc/gimple-fold.c | |
parent | 6a3cbe90926bbe62fcbce85dc735cbf077fd0f0b (diff) | |
download | gcc-1304953e4fa46305d1ce0b884bf2f58e08409ff3.zip gcc-1304953e4fa46305d1ce0b884bf2f58e08409ff3.tar.gz gcc-1304953e4fa46305d1ce0b884bf2f58e08409ff3.tar.bz2 |
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
Diffstat (limited to 'gcc/gimple-fold.c')
-rw-r--r-- | gcc/gimple-fold.c | 125 |
1 files changed, 112 insertions, 13 deletions
diff --git a/gcc/gimple-fold.c b/gcc/gimple-fold.c index 71e4638..aeb1803 100644 --- a/gcc/gimple-fold.c +++ b/gcc/gimple-fold.c @@ -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 (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 (integer_onep (arg1)) - result = arg0; - else if (integer_onep (arg0)) - result = arg1; + 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; |