diff options
Diffstat (limited to 'gcc/tree-ssa-strlen.c')
-rw-r--r-- | gcc/tree-ssa-strlen.c | 87 |
1 files changed, 87 insertions, 0 deletions
diff --git a/gcc/tree-ssa-strlen.c b/gcc/tree-ssa-strlen.c index d27b607..5e2d7db 100644 --- a/gcc/tree-ssa-strlen.c +++ b/gcc/tree-ssa-strlen.c @@ -44,6 +44,7 @@ along with GCC; see the file COPYING3. If not see #include "params.h" #include "ipa-chkp.h" #include "tree-hash-traits.h" +#include "builtins.h" /* A vector indexed by SSA_NAME_VERSION. 0 means unknown, positive value is an index into strinfo vector, negative value stands for @@ -1843,6 +1844,88 @@ handle_builtin_memset (gimple_stmt_iterator *gsi) return false; } +/* Handle a call to memcmp. We try to handle small comparisons by + converting them to load and compare, and replacing the call to memcmp + with a __builtin_memcmp_eq call where possible. */ + +static bool +handle_builtin_memcmp (gimple_stmt_iterator *gsi) +{ + gcall *stmt2 = as_a <gcall *> (gsi_stmt (*gsi)); + tree res = gimple_call_lhs (stmt2); + tree arg1 = gimple_call_arg (stmt2, 0); + tree arg2 = gimple_call_arg (stmt2, 1); + tree len = gimple_call_arg (stmt2, 2); + unsigned HOST_WIDE_INT leni; + use_operand_p use_p; + imm_use_iterator iter; + + if (!res) + return true; + + FOR_EACH_IMM_USE_FAST (use_p, iter, res) + { + gimple *ustmt = USE_STMT (use_p); + + if (gimple_code (ustmt) == GIMPLE_ASSIGN) + { + gassign *asgn = as_a <gassign *> (ustmt); + tree_code code = gimple_assign_rhs_code (asgn); + if ((code != EQ_EXPR && code != NE_EXPR) + || !integer_zerop (gimple_assign_rhs2 (asgn))) + return true; + } + else if (gimple_code (ustmt) == GIMPLE_COND) + { + tree_code code = gimple_cond_code (ustmt); + if ((code != EQ_EXPR && code != NE_EXPR) + || !integer_zerop (gimple_cond_rhs (ustmt))) + return true; + } + else + return true; + } + + if (tree_fits_uhwi_p (len) + && (leni = tree_to_uhwi (len)) <= GET_MODE_SIZE (word_mode) + && exact_log2 (leni) != -1) + { + leni *= CHAR_TYPE_SIZE; + unsigned align1 = get_pointer_alignment (arg1); + unsigned align2 = get_pointer_alignment (arg2); + unsigned align = MIN (align1, align2); + machine_mode mode = mode_for_size (leni, MODE_INT, 1); + if (mode != BLKmode + && (align >= leni || !SLOW_UNALIGNED_ACCESS (mode, align))) + { + location_t loc = gimple_location (stmt2); + tree type, off; + type = build_nonstandard_integer_type (leni, 1); + gcc_assert (GET_MODE_BITSIZE (TYPE_MODE (type)) == leni); + tree ptrtype = build_pointer_type_for_mode (char_type_node, + ptr_mode, true); + off = build_int_cst (ptrtype, 0); + arg1 = build2_loc (loc, MEM_REF, type, arg1, off); + arg2 = build2_loc (loc, MEM_REF, type, arg2, off); + tree tem1 = fold_const_aggregate_ref (arg1); + if (tem1) + arg1 = tem1; + tree tem2 = fold_const_aggregate_ref (arg2); + if (tem2) + arg2 = tem2; + res = fold_convert_loc (loc, TREE_TYPE (res), + fold_build2_loc (loc, NE_EXPR, + boolean_type_node, + arg1, arg2)); + gimplify_and_update_call_from_tree (gsi, res); + return false; + } + } + + gimple_call_set_fndecl (stmt2, builtin_decl_explicit (BUILT_IN_MEMCMP_EQ)); + return false; +} + /* Handle a POINTER_PLUS_EXPR statement. For p = "abcd" + 2; compute associated length, or if p = q + off is pointing to a '\0' character of a string, call @@ -2100,6 +2183,10 @@ strlen_optimize_stmt (gimple_stmt_iterator *gsi) if (!handle_builtin_memset (gsi)) return false; break; + case BUILT_IN_MEMCMP: + if (!handle_builtin_memcmp (gsi)) + return false; + break; default: break; } |