diff --git a/gcc/fold-const.c b/gcc/fold-const.c index c5d9a79..309ef38 100644 --- a/gcc/fold-const.c +++ b/gcc/fold-const.c @@ -10676,30 +10676,6 @@ fold_binary_loc (location_t loc, return t1; } - /* Optimize comparisons of strlen vs zero to a compare of the - first character of the string vs zero. To wit, - strlen(ptr) == 0 => *ptr == 0 - strlen(ptr) != 0 => *ptr != 0 - Other cases should reduce to one of these two (or a constant) - due to the return value of strlen being unsigned. */ - if (TREE_CODE (arg0) == CALL_EXPR - && integer_zerop (arg1)) - { - tree fndecl = get_callee_fndecl (arg0); - - if (fndecl - && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL - && DECL_FUNCTION_CODE (fndecl) == BUILT_IN_STRLEN - && call_expr_nargs (arg0) == 1 - && TREE_CODE (TREE_TYPE (CALL_EXPR_ARG (arg0, 0))) == POINTER_TYPE) - { - tree iref = build_fold_indirect_ref_loc (loc, - CALL_EXPR_ARG (arg0, 0)); - return fold_build2_loc (loc, code, type, iref, - build_int_cst (TREE_TYPE (iref), 0)); - } - } - /* Fold (X >> C) != 0 into X < 0 if C is one less than the width of X. Similarly fold (X >> C) == 0 into X >= 0. */ if (TREE_CODE (arg0) == RSHIFT_EXPR diff --git a/gcc/testsuite/gcc.dg/strlenopt-30.c b/gcc/testsuite/gcc.dg/strlenopt-30.c new file mode 100644 index 0000000..b041d86 --- /dev/null +++ b/gcc/testsuite/gcc.dg/strlenopt-30.c @@ -0,0 +1,12 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -fdump-tree-strlen" } */ + +__attribute__((noinline, no_icf)) +_Bool f1(const char *s) +{ + unsigned long len = __builtin_strlen (s); + _Bool ret = (len == 0); + return ret; +} + +/* { dg-final { scan-tree-dump-times "strlen \\(" 0 "strlen" } } */ diff --git a/gcc/tree-ssa-strlen.c b/gcc/tree-ssa-strlen.c index 9d7b4df..4ada2ee 100644 --- a/gcc/tree-ssa-strlen.c +++ b/gcc/tree-ssa-strlen.c @@ -45,6 +45,8 @@ along with GCC; see the file COPYING3. If not see #include "ipa-chkp.h" #include "tree-hash-traits.h" #include "builtins.h" +#include "tree-pretty-print.h" +#include "tree-cfg.h" /* A vector indexed by SSA_NAME_VERSION. 0 means unknown, positive value is an index into strinfo vector, negative value stands for @@ -1937,6 +1939,36 @@ handle_builtin_memset (gimple_stmt_iterator *gsi) return false; } +static void +replace_call_by_cmp (gimple_stmt_iterator *gsi, location_t loc, + tree type, tree arg1, tree arg2, + tree res_type, enum tree_code cmp_code) +{ + tree ptrtype = build_pointer_type_for_mode (char_type_node, ptr_mode, true); + tree off = build_int_cst (ptrtype, 0); + arg1 = build2_loc (loc, MEM_REF, type, arg1, off); + tree tem1 = fold_const_aggregate_ref (arg1); + if (tem1) + arg1 = tem1; + + if (POINTER_TYPE_P (TREE_TYPE (arg2))) + { + arg2 = build2_loc (loc, MEM_REF, type, arg2, off); + tree tem2 = fold_const_aggregate_ref (arg2); + if (tem2) + arg2 = tem2; + } + else + gcc_assert (INTEGRAL_TYPE_P (TREE_TYPE (arg2))); + + tree res = fold_convert_loc (loc, res_type, + fold_build2_loc (loc, cmp_code, + boolean_type_node, + arg1, arg2)); + + gimplify_and_update_call_from_tree (gsi, res); +} + /* 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. */ @@ -1994,25 +2026,11 @@ handle_builtin_memcmp (gimple_stmt_iterator *gsi) && (align >= leni || !SLOW_UNALIGNED_ACCESS (mode, align))) { location_t loc = gimple_location (stmt2); - tree type, off; + tree type; 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); + replace_call_by_cmp (gsi, loc, type, arg1, arg2, + TREE_TYPE (res), NE_EXPR); return false; } } @@ -2302,6 +2320,41 @@ strlen_optimize_stmt (gimple_stmt_iterator *gsi) else if (gimple_assign_rhs_code (stmt) == POINTER_PLUS_EXPR) handle_pointer_plus (gsi); } + /* strlen (s) eq/ne 0 -> *s eq/ne 0. */ + else if (TREE_CODE (lhs) == SSA_NAME && INTEGRAL_TYPE_P (TREE_TYPE (lhs))) + { + tree rhs2 = gimple_assign_rhs2 (stmt); + tree_code code = gimple_assign_rhs_code (stmt); + + if ((code == EQ_EXPR || code == NE_EXPR) && integer_zerop (rhs2)) + { + tree rhs1 = gimple_assign_rhs1 (stmt); + if (TREE_CODE (rhs1) == SSA_NAME) + { + gimple *def_stmt = SSA_NAME_DEF_STMT (rhs1); + if (gcall *call_stmt = dyn_cast (def_stmt)) + { + tree callee = gimple_call_fndecl (call_stmt); + if (valid_builtin_call (call_stmt) + && DECL_FUNCTION_CODE (callee) == BUILT_IN_STRLEN) + { + tree call_arg = gimple_call_arg (call_stmt, 0); + tree call_lhs = gimple_call_lhs (call_stmt); + + if (has_single_use (call_lhs)) + { + gimple_stmt_iterator call_gsi = gsi_for_stmt (call_stmt); + replace_call_by_cmp (&call_gsi, gimple_location (call_stmt), + TREE_TYPE (rhs2), call_arg, rhs2, TREE_TYPE (call_lhs), + code); + gimple_assign_set_rhs_with_ops (gsi, CONVERT_EXPR, call_lhs); + update_stmt (stmt); + } + } + } + } + } + } else if (TREE_CODE (lhs) != SSA_NAME && !TREE_SIDE_EFFECTS (lhs)) { tree type = TREE_TYPE (lhs); @@ -2505,7 +2558,7 @@ const pass_data pass_data_strlen = 0, /* properties_provided */ 0, /* properties_destroyed */ 0, /* todo_flags_start */ - 0, /* todo_flags_finish */ + TODO_update_ssa_only_virtuals, /* todo_flags_finish */ }; class pass_strlen : public gimple_opt_pass