diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 941753aa2b2..25f85d2cea4 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,13 @@ +2013-12-16 Jakub Jelinek + + * internal-fn.c: Include stringpool.h and tree-ssanames.h. + (ubsan_expand_si_overflow_addsub_check): In the generic expansion, + try to improve generated code if one of the arguments is constant + or get_range_info says that one of the argument is always + negative or always non-negative. + * tree-vrp.c (simplify_internal_call_using_ranges): New function. + (simplify_stmt_using_ranges): Call it. + 2013-12-16 Vladimir Makarov PR rtl-optimization/59466 diff --git a/gcc/internal-fn.c b/gcc/internal-fn.c index a0874d467b4..a0dbad139a0 100644 --- a/gcc/internal-fn.c +++ b/gcc/internal-fn.c @@ -34,6 +34,8 @@ along with GCC; see the file COPYING3. If not see #include "ubsan.h" #include "target.h" #include "predict.h" +#include "stringpool.h" +#include "tree-ssanames.h" /* The names of each internal function, indexed by function number. */ const char *const internal_fn_name_array[] = { @@ -211,28 +213,81 @@ ubsan_expand_si_overflow_addsub_check (tree_code code, gimple stmt) if (icode == CODE_FOR_nothing) { rtx sub_check = gen_label_rtx (); + int pos_neg = 3; /* Compute the operation. On RTL level, the addition is always unsigned. */ res = expand_binop (mode, code == PLUS_EXPR ? add_optab : sub_optab, op0, op1, NULL_RTX, false, OPTAB_LIB_WIDEN); + /* If we can prove one of the arguments is always non-negative + or always negative, we can do just one comparison and + conditional jump instead of 2 at runtime, 3 present in the + emitted code. If one of the arguments is CONST_INT, all we + need is to make sure it is op1, then the first + emit_cmp_and_jump_insns will be just folded. Otherwise try + to use range info if available. */ + if (CONST_INT_P (op0)) + { + rtx tem = op0; + op0 = op1; + op1 = tem; + } + else if (CONST_INT_P (op1)) + ; + else if (TREE_CODE (arg0) == SSA_NAME) + { + double_int arg0_min, arg0_max; + if (get_range_info (arg0, &arg0_min, &arg0_max) == VR_RANGE) + { + if (!arg0_min.is_negative ()) + pos_neg = 1; + else if (arg0_max.is_negative ()) + pos_neg = 2; + } + if (pos_neg != 3) + { + rtx tem = op0; + op0 = op1; + op1 = tem; + } + } + if (pos_neg == 3 && !CONST_INT_P (op1) && TREE_CODE (arg1) == SSA_NAME) + { + double_int arg1_min, arg1_max; + if (get_range_info (arg1, &arg1_min, &arg1_max) == VR_RANGE) + { + if (!arg1_min.is_negative ()) + pos_neg = 1; + else if (arg1_max.is_negative ()) + pos_neg = 2; + } + } + /* If the op1 is negative, we have to use a different check. */ - emit_cmp_and_jump_insns (op1, const0_rtx, LT, NULL_RTX, mode, - false, sub_check, PROB_EVEN); + if (pos_neg == 3) + emit_cmp_and_jump_insns (op1, const0_rtx, LT, NULL_RTX, mode, + false, sub_check, PROB_EVEN); /* Compare the result of the operation with one of the operands. */ - emit_cmp_and_jump_insns (res, op0, code == PLUS_EXPR ? GE : LE, - NULL_RTX, mode, false, done_label, - PROB_VERY_LIKELY); - /* If we get here, we have to print the error. */ - emit_jump (do_error); + if (pos_neg & 1) + emit_cmp_and_jump_insns (res, op0, code == PLUS_EXPR ? GE : LE, + NULL_RTX, mode, false, done_label, + PROB_VERY_LIKELY); + + /* If we get here, we have to print the error. */ + if (pos_neg == 3) + { + emit_jump (do_error); + + emit_label (sub_check); + } - emit_label (sub_check); /* We have k = a + b for b < 0 here. k <= a must hold. */ - emit_cmp_and_jump_insns (res, op0, code == PLUS_EXPR ? LE : GE, - NULL_RTX, mode, false, done_label, - PROB_VERY_LIKELY); + if (pos_neg & 2) + emit_cmp_and_jump_insns (res, op0, code == PLUS_EXPR ? LE : GE, + NULL_RTX, mode, false, done_label, + PROB_VERY_LIKELY); } emit_label (do_error); diff --git a/gcc/tree-vrp.c b/gcc/tree-vrp.c index 8ab6d767f25..4de7c4d9199 100644 --- a/gcc/tree-vrp.c +++ b/gcc/tree-vrp.c @@ -9299,6 +9299,68 @@ simplify_float_conversion_using_ranges (gimple_stmt_iterator *gsi, gimple stmt) return true; } +/* Simplify an internal fn call using ranges if possible. */ + +static bool +simplify_internal_call_using_ranges (gimple_stmt_iterator *gsi, gimple stmt) +{ + enum tree_code subcode; + switch (gimple_call_internal_fn (stmt)) + { + case IFN_UBSAN_CHECK_ADD: + subcode = PLUS_EXPR; + break; + case IFN_UBSAN_CHECK_SUB: + subcode = MINUS_EXPR; + break; + case IFN_UBSAN_CHECK_MUL: + 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); + else + 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 + return false; + + if (!range_int_cst_p (&vr0) || !range_int_cst_p (&vr1)) + return false; + + tree r1 = int_const_binop (subcode, vr0.min, vr1.min); + tree r2 = int_const_binop (subcode, vr0.max, vr1.max); + if (r1 == NULL_TREE || TREE_OVERFLOW (r1) + || r2 == NULL_TREE || TREE_OVERFLOW (r2)) + return false; + if (subcode == MULT_EXPR) + { + 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; + } + gimple g = gimple_build_assign_with_ops (subcode, gimple_call_lhs (stmt), + op0, op1); + gsi_replace (gsi, g, false); + return true; +} + /* Simplify STMT using ranges if possible. */ static bool @@ -9367,6 +9429,9 @@ simplify_stmt_using_ranges (gimple_stmt_iterator *gsi) return simplify_cond_using_ranges (stmt); else if (gimple_code (stmt) == GIMPLE_SWITCH) return simplify_switch_using_ranges (stmt); + else if (is_gimple_call (stmt) + && gimple_call_internal_p (stmt)) + return simplify_internal_call_using_ranges (gsi, stmt); return false; }