diff options
author | Jakub Jelinek <jakub@redhat.com> | 2013-12-16 19:44:51 +0100 |
---|---|---|
committer | Jakub Jelinek <jakub@gcc.gnu.org> | 2013-12-16 19:44:51 +0100 |
commit | 97286431624c245b32ae05c33e9af2876eda55ee (patch) | |
tree | 41dc923f102f79eeb2f9561e2cba92cf1201e9c7 /gcc/internal-fn.c | |
parent | 91c5ee5b4ac9c85b6cbf596dc7917910b0d7b1b3 (diff) | |
download | gcc-97286431624c245b32ae05c33e9af2876eda55ee.zip gcc-97286431624c245b32ae05c33e9af2876eda55ee.tar.gz gcc-97286431624c245b32ae05c33e9af2876eda55ee.tar.bz2 |
internal-fn.c: Include stringpool.h and tree-ssanames.h.
* 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.
From-SVN: r206025
Diffstat (limited to 'gcc/internal-fn.c')
-rw-r--r-- | gcc/internal-fn.c | 75 |
1 files changed, 65 insertions, 10 deletions
diff --git a/gcc/internal-fn.c b/gcc/internal-fn.c index a0874d4..a0dbad1 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 (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. */ - emit_jump (do_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); |