Hi Richard, The attached patch tries to fold strlen (s) eq/ne 0 to *s eq/ne 0 on GIMPLE. I am not sure where was the ideal place to put this transform in and ended up adding it to strlen_optimize_stmt(). Does that look OK ?
I needed to add TODO_update_ssa to strlen pass, otherwise we hit the following assert in execute_todo(): if (flag_checking && cfun && need_ssa_update_p (cfun)) gcc_assert (flags & TODO_update_ssa_any); Bootstrap+test in progress on x86_64-unknown-linux-gnu. Thanks, Prathamesh
2016-08-01 Prathamesh Kulkarni <prathamesh.kulka...@linaro.org> * tree-ssa-strlen.c (strlen_optimize_stmt): Fold strlen (s) eq/ne 0 to *s eq/ne 0. Change todo_flags_finish for pass_data_strlen from 0 to TODO_update_ssa. testsuite/ * gcc.dg/strlenopt-30.c: New test-case. diff --git a/gcc/testsuite/gcc.dg/strlenopt-30.c b/gcc/testsuite/gcc.dg/strlenopt-30.c new file mode 100644 index 0000000..da9732f --- /dev/null +++ b/gcc/testsuite/gcc.dg/strlenopt-30.c @@ -0,0 +1,20 @@ +/* { 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; +} + +/* Check CONVERT_EXPR's get properly handled. */ +__attribute__((noinline, no_icf)) +_Bool f2(const char *s) +{ + unsigned len = __builtin_strlen (s); + return len == 0; +} + +/* { 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..54f8109 100644 --- a/gcc/tree-ssa-strlen.c +++ b/gcc/tree-ssa-strlen.c @@ -45,6 +45,7 @@ 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" /* A vector indexed by SSA_NAME_VERSION. 0 means unknown, positive value is an index into strinfo vector, negative value stands for @@ -2302,6 +2303,43 @@ 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 (is_a<gassign *> (def_stmt) + && (gimple_assign_rhs_code (def_stmt) == CONVERT_EXPR + || gimple_assign_rhs_code (def_stmt) == NOP_EXPR) + && TREE_CODE (gimple_assign_rhs1 (def_stmt)) == SSA_NAME) + def_stmt = SSA_NAME_DEF_STMT (gimple_assign_rhs1 (def_stmt)); + + if (gcall *call_stmt = dyn_cast<gcall *> (def_stmt)) + { + tree callee = gimple_call_fndecl (call_stmt); + if (valid_builtin_call (call_stmt) + && DECL_FUNCTION_CODE (callee) == BUILT_IN_STRLEN) + { + tree arg = gimple_call_arg (call_stmt, 0); + tree op = build2 (MEM_REF, char_type_node, arg, build_zero_cst (TREE_TYPE (arg))); + tree temp = make_temp_ssa_name (TREE_TYPE (op), NULL, "strlen"); + gimple *memref_stmt = gimple_build_assign (temp, op); + gimple_stmt_iterator call_gsi = gsi_for_stmt (call_stmt); + gsi_insert_before (&call_gsi, memref_stmt, GSI_SAME_STMT); + gassign *g = gimple_build_assign (gimple_call_lhs (call_stmt), CONVERT_EXPR, temp); + gsi_replace (&call_gsi, g, true); + } + } + } + } + } else if (TREE_CODE (lhs) != SSA_NAME && !TREE_SIDE_EFFECTS (lhs)) { tree type = TREE_TYPE (lhs); @@ -2505,7 +2543,7 @@ const pass_data pass_data_strlen = 0, /* properties_provided */ 0, /* properties_destroyed */ 0, /* todo_flags_start */ - 0, /* todo_flags_finish */ + TODO_update_ssa, /* todo_flags_finish */ }; class pass_strlen : public gimple_opt_pass