public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* Fold strstr (s, t) eq/ne s to strcmp (s, t) eq/ne 0 if strlen (t) is known
@ 2016-12-05 18:02 Prathamesh Kulkarni
  2016-12-05 18:08 ` Bernd Schmidt
  2016-12-05 18:17 ` Jakub Jelinek
  0 siblings, 2 replies; 18+ messages in thread
From: Prathamesh Kulkarni @ 2016-12-05 18:02 UTC (permalink / raw)
  To: gcc Patches, Richard Biener

[-- Attachment #1: Type: text/plain, Size: 862 bytes --]

Hi,
This patch folds strstr (s, t) eq/ne s to strcmp (s, t) eq/ne 0 if
strlen (t) is known.
One issue I came across was forwprop1 reverses the order of operands
in eq_expr below:

eg test-case:
_Bool f(char *s, int cond)
{
  char *t1 = __builtin_strstr (s, "hello");
  _Bool t2 = (t1 == s);
  return t2;
}

forwprop1 dump:
f (char * s, int cond)
{
  _Bool t2;
  char * t1;

  <bb 2> [0.0%]:
  t1_3 = __builtin_strstr (s_2(D), "hello");
  t2_4 = s_2(D) == t1_3;
  return t2_4;

}

So I had to check if SSA_NAME_DEF_STMT (rhs2) was call to strstr
rather than rhs1.
I suppose that's OK ?

clang unconditionally transforms
strstr (s, t) == s to strncmp (s, t, strlen (t))
However I am not sure what algorithm glibc's strstr uses, so didn't
attempt to transform
if strlen (t) is unknown. Should we do the transform even if strlen
(t) is unknown ?

Thanks,
Prathamesh

[-- Attachment #2: tcwg-701-2.txt --]
[-- Type: text/plain, Size: 3438 bytes --]

2016-12-05  Prathamesh Kulkarni  <prathamesh.kulkarni@linaro.org>

	* tree-ssa-strlen.c (strlen_optimize_stmt): Fold strstr(s, t) == s to
	strcmp (s, t) == 0.
	(pass_data_strlen): Set todo_flags_finish 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..737f37d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/strlenopt-30.c
@@ -0,0 +1,27 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-strlen" } */
+
+_Bool f1(char *s)
+{
+  char *t = "hello";
+  char *t1 = __builtin_strstr (s, t);
+  _Bool t2 = (t1 == s);
+  return t2;
+}
+
+_Bool f2(char *s)
+{
+  char *t = "hello";
+  char *t1 = __builtin_strstr (s, t);
+  _Bool t2 = (t1 != s);
+  return t2;
+}
+
+_Bool f3(char *s, char *t)
+{
+  char *t1 = __builtin_strstr (s, t);
+  _Bool t2 = (t1 == s);
+  return t2;
+}
+
+/* { dg-final { scan-tree-dump-times "__builtin_strcmp" 2 "strlen" } } */
diff --git a/gcc/tree-ssa-strlen.c b/gcc/tree-ssa-strlen.c
index 339812e..8977e80 100644
--- a/gcc/tree-ssa-strlen.c
+++ b/gcc/tree-ssa-strlen.c
@@ -2302,6 +2302,55 @@ strlen_optimize_stmt (gimple_stmt_iterator *gsi)
 	  else if (gimple_assign_rhs_code (stmt) == POINTER_PLUS_EXPR)
 	    handle_pointer_plus (gsi);
 	}
+
+      /* Fold strstr (s, t) == s to strcmp (s, t) == 0.  if strlen (t)
+	 is known.  */
+      else if (TREE_CODE (lhs) == SSA_NAME && INTEGRAL_TYPE_P (TREE_TYPE (lhs)))
+	{
+	  enum tree_code code = gimple_assign_rhs_code (stmt);
+	  if (code == EQ_EXPR || code == NE_EXPR)
+	    {
+	      tree rhs1 = gimple_assign_rhs1 (stmt);
+	      tree rhs2 = gimple_assign_rhs2 (stmt);
+	      if (TREE_CODE (rhs2) == SSA_NAME)
+		{
+		  gcall *call_stmt = dyn_cast<gcall *> (SSA_NAME_DEF_STMT (rhs2));
+		  if (call_stmt
+		      && gimple_call_builtin_p (call_stmt, BUILT_IN_STRSTR))
+		    {
+		      tree arg0 = gimple_call_arg (call_stmt, 0);
+		      if (operand_equal_p (arg0, rhs1, 0))
+			{
+			  /* Check if strlen(arg1) is known.  */
+			  tree arg1 = gimple_call_arg (call_stmt, 1);
+			  int idx = get_stridx (arg1);
+			  strinfo *si = NULL;
+			  if (idx)
+			    si = get_strinfo (idx);
+			  if ((idx < 0)
+			      || (si && (get_string_length (si) != NULL_TREE)))
+			    {
+			      gimple_stmt_iterator gsi = gsi_for_stmt (call_stmt);
+			      tree strcmp_decl = builtin_decl_explicit (BUILT_IN_STRCMP);
+			      gcall *strcmp_call = gimple_build_call (strcmp_decl, 2,
+								      arg0, arg1);
+			      tree strcmp_lhs = make_ssa_name (integer_type_node);
+			      gimple_call_set_lhs (strcmp_call, strcmp_lhs);
+			      update_stmt (strcmp_call);
+			      gsi_remove (&gsi, true);
+			      gsi_insert_before (&gsi, strcmp_call, GSI_SAME_STMT);
+
+			      gsi = gsi_for_stmt (stmt);
+			      tree zero = build_zero_cst (TREE_TYPE (strcmp_lhs));
+			      gassign *ga = gimple_build_assign (lhs, code,
+							strcmp_lhs, zero);
+			      gsi_replace (&gsi, ga, false);
+			    }
+			}
+		    }
+		}
+	    }
+	}
       else if (TREE_CODE (lhs) != SSA_NAME && !TREE_SIDE_EFFECTS (lhs))
 	{
 	  tree type = TREE_TYPE (lhs);
@@ -2505,7 +2554,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

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: Fold strstr (s, t) eq/ne s to strcmp (s, t) eq/ne 0 if strlen (t) is known
  2016-12-05 18:02 Fold strstr (s, t) eq/ne s to strcmp (s, t) eq/ne 0 if strlen (t) is known Prathamesh Kulkarni
@ 2016-12-05 18:08 ` Bernd Schmidt
  2016-12-05 18:10   ` Prathamesh Kulkarni
  2016-12-05 18:17 ` Jakub Jelinek
  1 sibling, 1 reply; 18+ messages in thread
From: Bernd Schmidt @ 2016-12-05 18:08 UTC (permalink / raw)
  To: Prathamesh Kulkarni, gcc Patches, Richard Biener

On 12/05/2016 07:02 PM, Prathamesh Kulkarni wrote:
> This patch folds strstr (s, t) eq/ne s to strcmp (s, t) eq/ne 0 if
> strlen (t) is known.

That's not the same thing, is it?

s = "hello world", t = "hello":
   strstr (s, t) == s, but not strcmp (s, t) == 0.

I think you'd want memcmp (s, t, strlen (t)) == 0.


Bernd

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: Fold strstr (s, t) eq/ne s to strcmp (s, t) eq/ne 0 if strlen (t) is known
  2016-12-05 18:08 ` Bernd Schmidt
@ 2016-12-05 18:10   ` Prathamesh Kulkarni
  2016-12-05 18:14     ` Prathamesh Kulkarni
  0 siblings, 1 reply; 18+ messages in thread
From: Prathamesh Kulkarni @ 2016-12-05 18:10 UTC (permalink / raw)
  To: Bernd Schmidt; +Cc: gcc Patches, Richard Biener

On 5 December 2016 at 23:38, Bernd Schmidt <bschmidt@redhat.com> wrote:
> On 12/05/2016 07:02 PM, Prathamesh Kulkarni wrote:
>>
>> This patch folds strstr (s, t) eq/ne s to strcmp (s, t) eq/ne 0 if
>> strlen (t) is known.
>
>
> That's not the same thing, is it?
>
> s = "hello world", t = "hello":
>   strstr (s, t) == s, but not strcmp (s, t) == 0.
>
> I think you'd want memcmp (s, t, strlen (t)) == 0.
Ah indeed! Dunno why I thought strstr (s, t) == strcmp (s, t) :(
Thanks for pointing out!
>
>
> Bernd
>

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: Fold strstr (s, t) eq/ne s to strcmp (s, t) eq/ne 0 if strlen (t) is known
  2016-12-05 18:10   ` Prathamesh Kulkarni
@ 2016-12-05 18:14     ` Prathamesh Kulkarni
  0 siblings, 0 replies; 18+ messages in thread
From: Prathamesh Kulkarni @ 2016-12-05 18:14 UTC (permalink / raw)
  To: Bernd Schmidt; +Cc: gcc Patches, Richard Biener

On 5 December 2016 at 23:40, Prathamesh Kulkarni
<prathamesh.kulkarni@linaro.org> wrote:
> On 5 December 2016 at 23:38, Bernd Schmidt <bschmidt@redhat.com> wrote:
>> On 12/05/2016 07:02 PM, Prathamesh Kulkarni wrote:
>>>
>>> This patch folds strstr (s, t) eq/ne s to strcmp (s, t) eq/ne 0 if
>>> strlen (t) is known.
>>
>>
>> That's not the same thing, is it?
>>
>> s = "hello world", t = "hello":
>>   strstr (s, t) == s, but not strcmp (s, t) == 0.
>>
>> I think you'd want memcmp (s, t, strlen (t)) == 0.
> Ah indeed! Dunno why I thought strstr (s, t) == strcmp (s, t) :(
Err, I meant strstr(s, t) == s to strcmp(s, t) == 0.
I will send a patch to fold strstr (s, t) to memcmp (s, t, strlen (t)) == 0.
Thanks for the suggestions.

Regards,
Prathamesh
> Thanks for pointing out!
>>
>>
>> Bernd
>>

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: Fold strstr (s, t) eq/ne s to strcmp (s, t) eq/ne 0 if strlen (t) is known
  2016-12-05 18:02 Fold strstr (s, t) eq/ne s to strcmp (s, t) eq/ne 0 if strlen (t) is known Prathamesh Kulkarni
  2016-12-05 18:08 ` Bernd Schmidt
@ 2016-12-05 18:17 ` Jakub Jelinek
  2016-12-07 11:33   ` Prathamesh Kulkarni
  1 sibling, 1 reply; 18+ messages in thread
From: Jakub Jelinek @ 2016-12-05 18:17 UTC (permalink / raw)
  To: Prathamesh Kulkarni; +Cc: gcc Patches, Richard Biener

On Mon, Dec 05, 2016 at 11:32:15PM +0530, Prathamesh Kulkarni wrote:
> So I had to check if SSA_NAME_DEF_STMT (rhs2) was call to strstr
> rather than rhs1.

Then you need to test both whether it is strstr (s, t) == s or
s == strstr (s, t).

> +			      gassign *ga = gimple_build_assign (lhs, code,
> +							strcmp_lhs, zero);

The formatting is wrong here.

> +			      gsi_replace (&gsi, ga, false);
> +			    }
> +			}
> +		    }
> +		}
> +	    }
> +	}
>        else if (TREE_CODE (lhs) != SSA_NAME && !TREE_SIDE_EFFECTS (lhs))
>  	{
>  	  tree type = TREE_TYPE (lhs);
> @@ -2505,7 +2554,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 */

No, please don't.  Just make sure to build proper SSA right away.

	Jakub

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: Fold strstr (s, t) eq/ne s to strcmp (s, t) eq/ne 0 if strlen (t) is known
  2016-12-05 18:17 ` Jakub Jelinek
@ 2016-12-07 11:33   ` Prathamesh Kulkarni
  2016-12-07 12:06     ` Jakub Jelinek
  0 siblings, 1 reply; 18+ messages in thread
From: Prathamesh Kulkarni @ 2016-12-07 11:33 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: gcc Patches, Richard Biener

[-- Attachment #1: Type: text/plain, Size: 1398 bytes --]

On 5 December 2016 at 23:47, Jakub Jelinek <jakub@redhat.com> wrote:
> On Mon, Dec 05, 2016 at 11:32:15PM +0530, Prathamesh Kulkarni wrote:
>> So I had to check if SSA_NAME_DEF_STMT (rhs2) was call to strstr
>> rather than rhs1.
>
> Then you need to test both whether it is strstr (s, t) == s or
> s == strstr (s, t).
>
>> +                           gassign *ga = gimple_build_assign (lhs, code,
>> +                                                     strcmp_lhs, zero);
>
> The formatting is wrong here.
>
>> +                           gsi_replace (&gsi, ga, false);
>> +                         }
>> +                     }
>> +                 }
>> +             }
>> +         }
>> +     }
>>        else if (TREE_CODE (lhs) != SSA_NAME && !TREE_SIDE_EFFECTS (lhs))
>>       {
>>         tree type = TREE_TYPE (lhs);
>> @@ -2505,7 +2554,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 */
>
> No, please don't.  Just make sure to build proper SSA right away.
Hi,
Thanks for the suggestions, I have tried to modify the patch accordingly.
Does this version look OK ?
Bootstrap+tested on x86_64-unknown-linux-gnu with --enable-languages=all,ada
Cross tested on arm*-*-*, aarch64*-*-*.

Thanks,
Prathamesh
>
>         Jakub

[-- Attachment #2: tcwg-701-5.txt --]
[-- Type: text/plain, Size: 4900 bytes --]

2016-12-07  Prathamesh Kulkarni  <prathamesh.kulkarni@linaro.org>

	* tree-ssa-strlen.c (strlen_optimize_stmt): Fold strstr(s, t) == s to
	memcmp (s, t, strlen (t)) == 0.
	Include tree-into-ssa.h.

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..603e23c
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/strlenopt-30.c
@@ -0,0 +1,42 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-strlen" } */
+
+__attribute__((no_icf))
+_Bool f1(char *s)
+{
+  return __builtin_strstr (s, "hello") == s;
+}
+
+__attribute__((no_icf))
+_Bool f2(char *s)
+{
+  return s == __builtin_strstr (s, "hello");
+}
+
+__attribute__((no_icf))
+_Bool f3(char *s)
+{
+  return s != __builtin_strstr (s, "hello");
+}
+
+__attribute__((no_icf))
+_Bool f4(char *s, char *t)
+{
+  return __builtin_strstr (s, t) == s;
+}
+
+/* Do not perform transform in this case, since
+   t1 doesn't have single use.  */
+
+__attribute__((no_icf))
+_Bool f5(char *s, char *t)
+{
+  void foo(char *);
+
+  char *t1 = __builtin_strstr (s, t);
+  foo (t1);
+  return (t1 == s);
+}
+
+/* { dg-final { scan-tree-dump-times "__builtin_memcmp" 4 "strlen" } } */
+/* { dg-final { scan-tree-dump-times "__builtin_strlen" 1 "strlen" } } */
diff --git a/gcc/tree-ssa-strlen.c b/gcc/tree-ssa-strlen.c
index 339812e..b7f4cee 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-into-ssa.h"
 
 /* A vector indexed by SSA_NAME_VERSION.  0 means unknown, positive value
    is an index into strinfo vector, negative value stands for
@@ -2302,7 +2303,94 @@ strlen_optimize_stmt (gimple_stmt_iterator *gsi)
 	  else if (gimple_assign_rhs_code (stmt) == POINTER_PLUS_EXPR)
 	    handle_pointer_plus (gsi);
 	}
-      else if (TREE_CODE (lhs) != SSA_NAME && !TREE_SIDE_EFFECTS (lhs))
+
+      /* Fold strstr (s, t) == s to memcmp (s, t, strlen (t)) == 0.
+	 if var holding return value of strstr has single use.  */
+
+      else if (TREE_CODE (lhs) == SSA_NAME && INTEGRAL_TYPE_P (TREE_TYPE (lhs)))
+	{
+	  enum tree_code code = gimple_assign_rhs_code (stmt);
+	  if (code == EQ_EXPR || code == NE_EXPR)
+	    {
+	      tree rhs1 = gimple_assign_rhs1 (stmt);
+	      tree rhs2 = gimple_assign_rhs2 (stmt);
+	      if (TREE_CODE (rhs1) == SSA_NAME
+		  && TREE_CODE (rhs2) == SSA_NAME)
+		{
+		  gcall *call_stmt = dyn_cast<gcall *> (SSA_NAME_DEF_STMT (rhs1));
+		  if (!call_stmt)
+		    {
+		      call_stmt = dyn_cast<gcall *> (SSA_NAME_DEF_STMT (rhs2));
+		      tree tmp = rhs1;
+		      rhs1 = rhs2;
+		      rhs2 = tmp;
+		    }
+
+		  tree call_lhs;
+		  if (call_stmt
+		      && gimple_call_builtin_p (call_stmt, BUILT_IN_STRSTR)
+		      && (call_lhs = gimple_call_lhs (call_stmt))
+		      && has_single_use (call_lhs))
+		    {
+		      tree arg0 = gimple_call_arg (call_stmt, 0);
+		      if (operand_equal_p (arg0, rhs2, 0))
+			{
+			  tree arg1 = gimple_call_arg (call_stmt, 1);
+			  tree arg1_len = NULL_TREE;
+			  int idx = get_stridx (arg1);
+
+			  if (idx)
+			    {
+			      if (idx < 0)
+				arg1_len = build_int_cst (size_type_node,
+							  ~idx);
+			      else
+				{
+				  strinfo *si = get_strinfo (idx);
+				  if (si)
+				    arg1_len = get_string_length (si);
+				}
+			    }
+
+			  if (arg1_len == NULL_TREE)
+			    {
+			      gimple_stmt_iterator gsi;
+			      tree strlen_decl;
+			      gimple *strlen_call;
+
+			      strlen_decl = builtin_decl_explicit (BUILT_IN_STRLEN);
+			      strlen_call = gimple_build_call (strlen_decl, 1,
+							       arg1);
+			      arg1_len = make_ssa_name (size_type_node);
+			      gimple_call_set_lhs (strlen_call, arg1_len);
+			      update_stmt (strlen_call);
+			      gsi = gsi_for_stmt (call_stmt);
+			      gsi_insert_before (&gsi, strlen_call, GSI_SAME_STMT);
+			    }
+
+			  gimple_stmt_iterator gsi = gsi_for_stmt (call_stmt);
+			  tree memcmp_decl = builtin_decl_explicit (BUILT_IN_MEMCMP);
+			  gcall *memcmp_call
+			    = gimple_build_call (memcmp_decl, 3, arg0, arg1,
+						 arg1_len);
+			  tree memcmp_lhs = make_ssa_name (integer_type_node);
+			  gimple_call_set_lhs (memcmp_call, memcmp_lhs);
+			  update_stmt (memcmp_call);
+			  gsi_remove (&gsi, true);
+			  gsi_insert_before (&gsi, memcmp_call, GSI_SAME_STMT);
+
+			  gsi = gsi_for_stmt (stmt);
+			  tree zero = build_zero_cst (TREE_TYPE (memcmp_lhs));
+			  gassign *ga = gimple_build_assign (lhs, code,
+							     memcmp_lhs, zero);
+			  gsi_replace (&gsi, ga, false);
+			  update_ssa (TODO_update_ssa);
+			}
+		    }
+		}
+	    }
+	}
+    else if (TREE_CODE (lhs) != SSA_NAME && !TREE_SIDE_EFFECTS (lhs))
 	{
 	  tree type = TREE_TYPE (lhs);
 	  if (TREE_CODE (type) == ARRAY_TYPE)

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: Fold strstr (s, t) eq/ne s to strcmp (s, t) eq/ne 0 if strlen (t) is known
  2016-12-07 11:33   ` Prathamesh Kulkarni
@ 2016-12-07 12:06     ` Jakub Jelinek
  2016-12-09 12:06       ` Prathamesh Kulkarni
  0 siblings, 1 reply; 18+ messages in thread
From: Jakub Jelinek @ 2016-12-07 12:06 UTC (permalink / raw)
  To: Prathamesh Kulkarni; +Cc: gcc Patches, Richard Biener

On Wed, Dec 07, 2016 at 05:02:46PM +0530, Prathamesh Kulkarni wrote:
> +			  if (arg1_len == NULL_TREE)
> +			    {
> +			      gimple_stmt_iterator gsi;
> +			      tree strlen_decl;
> +			      gimple *strlen_call;
> +
> +			      strlen_decl = builtin_decl_explicit (BUILT_IN_STRLEN);
> +			      strlen_call = gimple_build_call (strlen_decl, 1,
> +							       arg1);
> +			      arg1_len = make_ssa_name (size_type_node);
> +			      gimple_call_set_lhs (strlen_call, arg1_len);
> +			      update_stmt (strlen_call);
> +			      gsi = gsi_for_stmt (call_stmt);
> +			      gsi_insert_before (&gsi, strlen_call, GSI_SAME_STMT);
> +			    }

Why?  If the strlen isn't readily available, do you really think it is
always a win to replace one call with 2 calls?  The string you want to do
strlen on can be huge, the haystack could be empty or very short, etc.
I'd just punt if strlen isn't known.
> +
> +			  gimple_stmt_iterator gsi = gsi_for_stmt (call_stmt);
> +			  tree memcmp_decl = builtin_decl_explicit (BUILT_IN_MEMCMP);
> +			  gcall *memcmp_call
> +			    = gimple_build_call (memcmp_decl, 3, arg0, arg1,
> +						 arg1_len);
> +			  tree memcmp_lhs = make_ssa_name (integer_type_node);
> +			  gimple_call_set_lhs (memcmp_call, memcmp_lhs);
> +			  update_stmt (memcmp_call);
> +			  gsi_remove (&gsi, true);
> +			  gsi_insert_before (&gsi, memcmp_call, GSI_SAME_STMT);
> +
> +			  gsi = gsi_for_stmt (stmt);
> +			  tree zero = build_zero_cst (TREE_TYPE (memcmp_lhs));
> +			  gassign *ga = gimple_build_assign (lhs, code,
> +							     memcmp_lhs, zero);
> +			  gsi_replace (&gsi, ga, false);
> +			  update_ssa (TODO_update_ssa);

And this is certainly even more wrong than the old TODO_update_ssa at the
end of the pass, now you'll do it for every single replacement in the
function.  Why do you need it?  The old call stmt has gimple_vdef and
gimple_vuse, so just copy those over, see how e.g.
replace_call_with_call_and_fold in gimple-fold.c does that.
If you don't add strlen, you need to move the vdef/vuse from stmt to
memcmp_call, if you really want to add strlen (see above note though),
then that call should have a vuse added (same vuse as the stmt originally
had).

	Jakub

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: Fold strstr (s, t) eq/ne s to strcmp (s, t) eq/ne 0 if strlen (t) is known
  2016-12-07 12:06     ` Jakub Jelinek
@ 2016-12-09 12:06       ` Prathamesh Kulkarni
  2016-12-09 12:29         ` Jakub Jelinek
  0 siblings, 1 reply; 18+ messages in thread
From: Prathamesh Kulkarni @ 2016-12-09 12:06 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: gcc Patches, Richard Biener

[-- Attachment #1: Type: text/plain, Size: 3464 bytes --]

On 7 December 2016 at 17:36, Jakub Jelinek <jakub@redhat.com> wrote:
> On Wed, Dec 07, 2016 at 05:02:46PM +0530, Prathamesh Kulkarni wrote:
>> +                       if (arg1_len == NULL_TREE)
>> +                         {
>> +                           gimple_stmt_iterator gsi;
>> +                           tree strlen_decl;
>> +                           gimple *strlen_call;
>> +
>> +                           strlen_decl = builtin_decl_explicit (BUILT_IN_STRLEN);
>> +                           strlen_call = gimple_build_call (strlen_decl, 1,
>> +                                                            arg1);
>> +                           arg1_len = make_ssa_name (size_type_node);
>> +                           gimple_call_set_lhs (strlen_call, arg1_len);
>> +                           update_stmt (strlen_call);
>> +                           gsi = gsi_for_stmt (call_stmt);
>> +                           gsi_insert_before (&gsi, strlen_call, GSI_SAME_STMT);
>> +                         }
>
> Why?  If the strlen isn't readily available, do you really think it is
> always a win to replace one call with 2 calls?  The string you want to do
> strlen on can be huge, the haystack could be empty or very short, etc.
> I'd just punt if strlen isn't known.
>> +
>> +                       gimple_stmt_iterator gsi = gsi_for_stmt (call_stmt);
>> +                       tree memcmp_decl = builtin_decl_explicit (BUILT_IN_MEMCMP);
>> +                       gcall *memcmp_call
>> +                         = gimple_build_call (memcmp_decl, 3, arg0, arg1,
>> +                                              arg1_len);
>> +                       tree memcmp_lhs = make_ssa_name (integer_type_node);
>> +                       gimple_call_set_lhs (memcmp_call, memcmp_lhs);
>> +                       update_stmt (memcmp_call);
>> +                       gsi_remove (&gsi, true);
>> +                       gsi_insert_before (&gsi, memcmp_call, GSI_SAME_STMT);
>> +
>> +                       gsi = gsi_for_stmt (stmt);
>> +                       tree zero = build_zero_cst (TREE_TYPE (memcmp_lhs));
>> +                       gassign *ga = gimple_build_assign (lhs, code,
>> +                                                          memcmp_lhs, zero);
>> +                       gsi_replace (&gsi, ga, false);
>> +                       update_ssa (TODO_update_ssa);
>
> And this is certainly even more wrong than the old TODO_update_ssa at the
> end of the pass, now you'll do it for every single replacement in the
> function.  Why do you need it?  The old call stmt has gimple_vdef and
> gimple_vuse, so just copy those over, see how e.g.
> replace_call_with_call_and_fold in gimple-fold.c does that.
> If you don't add strlen, you need to move the vdef/vuse from stmt to
> memcmp_call, if you really want to add strlen (see above note though),
> then that call should have a vuse added (same vuse as the stmt originally
> had).
Hi,
Thanks for the suggestions. In attached patch, I dropped the transform
if strlen (t) is unknown.
Since strstr is marked pure, so IIUC call_stmt for strstr shouldn't
have vdef assoicated with it ?
(gimple_vdef for call_stmt returned NULL for test-cases I tried it
with). Moving gimple_vuse
from call_stmt to memcmp_call worked for me.
Does the patch look OK ?
Bootstrap+tested on x86_64-unknown-linux-gnu with --enable-langauges=all,ada
Cross-tested on arm*-*-*, aarch64*-*-*.

Thanks,
Prathamesh
>
>         Jakub

[-- Attachment #2: tcwg-701-7.diff --]
[-- Type: text/plain, Size: 3904 bytes --]

diff --git a/gcc/testsuite/gcc.dg/strlenopt-30.c b/gcc/testsuite/gcc.dg/strlenopt-30.c
new file mode 100644
index 0000000..329bc25
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/strlenopt-30.c
@@ -0,0 +1,44 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-strlen" } */
+
+__attribute__((no_icf))
+_Bool f1(char *s)
+{
+  return __builtin_strstr (s, "hello") == s;
+}
+
+__attribute__((no_icf))
+_Bool f2(char *s)
+{
+  return s == __builtin_strstr (s, "hello");
+}
+
+__attribute__((no_icf))
+_Bool f3(char *s)
+{
+  return s != __builtin_strstr (s, "hello");
+}
+
+/* Do not perform transform, since strlen (t)
+   is unknown.  */
+
+__attribute__((no_icf))
+_Bool f4(char *s, char *t)
+{
+  return __builtin_strstr (s, t) == s;
+}
+
+/* Do not perform transform in this case, since
+   t1 doesn't have single use.  */
+
+__attribute__((no_icf))
+_Bool f5(char *s)
+{
+  void foo(char *);
+
+  char *t1 = __builtin_strstr (s, "hello");
+  foo (t1);
+  return (t1 == s);
+}
+
+/* { dg-final { scan-tree-dump-times "__builtin_memcmp" 3 "strlen" } } */
diff --git a/gcc/tree-ssa-strlen.c b/gcc/tree-ssa-strlen.c
index 339812e..06b07b0 100644
--- a/gcc/tree-ssa-strlen.c
+++ b/gcc/tree-ssa-strlen.c
@@ -2302,7 +2302,81 @@ strlen_optimize_stmt (gimple_stmt_iterator *gsi)
 	  else if (gimple_assign_rhs_code (stmt) == POINTER_PLUS_EXPR)
 	    handle_pointer_plus (gsi);
 	}
-      else if (TREE_CODE (lhs) != SSA_NAME && !TREE_SIDE_EFFECTS (lhs))
+
+      /* Fold strstr (s, t) == s to memcmp (s, t, strlen (t)) == 0.
+	 if strlen (t) is known and var holding return value of strstr
+	 has single use.  */
+
+      else if (TREE_CODE (lhs) == SSA_NAME && INTEGRAL_TYPE_P (TREE_TYPE (lhs)))
+	{
+	  enum tree_code code = gimple_assign_rhs_code (stmt);
+	  if (code == EQ_EXPR || code == NE_EXPR)
+	    {
+	      tree rhs1 = gimple_assign_rhs1 (stmt);
+	      tree rhs2 = gimple_assign_rhs2 (stmt);
+	      if (TREE_CODE (rhs1) == SSA_NAME
+		  && TREE_CODE (rhs2) == SSA_NAME)
+		{
+		  gcall *call_stmt = dyn_cast<gcall *> (SSA_NAME_DEF_STMT (rhs1));
+		  if (!call_stmt)
+		    {
+		      call_stmt = dyn_cast<gcall *> (SSA_NAME_DEF_STMT (rhs2));
+		      tree tmp = rhs1;
+		      rhs1 = rhs2;
+		      rhs2 = tmp;
+		    }
+
+		  tree call_lhs;
+		  if (call_stmt
+		      && gimple_call_builtin_p (call_stmt, BUILT_IN_STRSTR)
+		      && (call_lhs = gimple_call_lhs (call_stmt))
+		      && has_single_use (call_lhs))
+		    {
+		      tree arg0 = gimple_call_arg (call_stmt, 0);
+		      if (operand_equal_p (arg0, rhs2, 0))
+			{
+			  tree arg1 = gimple_call_arg (call_stmt, 1);
+			  tree arg1_len = NULL_TREE;
+			  int idx = get_stridx (arg1);
+
+			  if (idx)
+			    {
+			      if (idx < 0)
+				arg1_len = build_int_cst (size_type_node,
+							  ~idx);
+			      else
+				{
+				  strinfo *si = get_strinfo (idx);
+				  if (si)
+				    arg1_len = get_string_length (si);
+				}
+			    }
+
+			  if (arg1_len != NULL_TREE)
+			    {
+			      gimple_stmt_iterator gsi = gsi_for_stmt (call_stmt);
+			      tree memcmp_decl = builtin_decl_explicit (BUILT_IN_MEMCMP);
+			      gcall *memcmp_call
+				= gimple_build_call (memcmp_decl, 3, arg0, arg1,
+						     arg1_len);
+			      tree memcmp_lhs = make_ssa_name (integer_type_node);
+			      gimple_set_vuse (memcmp_call, gimple_vuse (call_stmt));
+			      gimple_call_set_lhs (memcmp_call, memcmp_lhs);
+			      gsi_remove (&gsi, true);
+			      gsi_insert_before (&gsi, memcmp_call, GSI_SAME_STMT);
+			      gsi = gsi_for_stmt (stmt);
+			      tree zero = build_zero_cst (TREE_TYPE (memcmp_lhs));
+			      gassign *ga = gimple_build_assign (lhs, code,
+								 memcmp_lhs,
+								 zero);
+			      gsi_replace (&gsi, ga, false);
+			    }
+			}
+		    }
+		}
+	    }
+	}
+    else if (TREE_CODE (lhs) != SSA_NAME && !TREE_SIDE_EFFECTS (lhs))
 	{
 	  tree type = TREE_TYPE (lhs);
 	  if (TREE_CODE (type) == ARRAY_TYPE)

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: Fold strstr (s, t) eq/ne s to strcmp (s, t) eq/ne 0 if strlen (t) is known
  2016-12-09 12:06       ` Prathamesh Kulkarni
@ 2016-12-09 12:29         ` Jakub Jelinek
  2016-12-13  9:38           ` Prathamesh Kulkarni
  0 siblings, 1 reply; 18+ messages in thread
From: Jakub Jelinek @ 2016-12-09 12:29 UTC (permalink / raw)
  To: Prathamesh Kulkarni; +Cc: gcc Patches, Richard Biener

On Fri, Dec 09, 2016 at 05:36:41PM +0530, Prathamesh Kulkarni wrote:
> --- a/gcc/tree-ssa-strlen.c
> +++ b/gcc/tree-ssa-strlen.c
> @@ -2302,7 +2302,81 @@ strlen_optimize_stmt (gimple_stmt_iterator *gsi)
>  	  else if (gimple_assign_rhs_code (stmt) == POINTER_PLUS_EXPR)
>  	    handle_pointer_plus (gsi);
>  	}
> -      else if (TREE_CODE (lhs) != SSA_NAME && !TREE_SIDE_EFFECTS (lhs))
> +
> +      /* Fold strstr (s, t) == s to memcmp (s, t, strlen (t)) == 0.
> +	 if strlen (t) is known and var holding return value of strstr
> +	 has single use.  */
> +
> +      else if (TREE_CODE (lhs) == SSA_NAME && INTEGRAL_TYPE_P (TREE_TYPE (lhs)))
> +	{
> +	  enum tree_code code = gimple_assign_rhs_code (stmt);
> +	  if (code == EQ_EXPR || code == NE_EXPR)

This way you handle _8 = _5 == _7;, but not if (_5 == _7) bar ();.  Shouldn't you
also handle GIMPLE_COND similarly (of course, the rhs1 and rhs2 grabbing
and code grabbing is different for GIMPLE_COND.  But the rest should be
the same, except again that you don't want to replace the GIMPLE_COND, but
adjust it.  Maybe also COND_EXPR in gimple_assign (_9 = _5 == _7 ? _10 : _11;).

> +	    {
> +	      tree rhs1 = gimple_assign_rhs1 (stmt);
> +	      tree rhs2 = gimple_assign_rhs2 (stmt);
> +	      if (TREE_CODE (rhs1) == SSA_NAME
> +		  && TREE_CODE (rhs2) == SSA_NAME)
> +		{
> +		  gcall *call_stmt = dyn_cast<gcall *> (SSA_NAME_DEF_STMT (rhs1));
> +		  if (!call_stmt)
> +		    {
> +		      call_stmt = dyn_cast<gcall *> (SSA_NAME_DEF_STMT (rhs2));
> +		      tree tmp = rhs1;
> +		      rhs1 = rhs2;
> +		      rhs2 = tmp;

We use std::swap (rhs1, rhs2); in this case these days.

> +		    }
> +
> +		  tree call_lhs;
> +		  if (call_stmt
> +		      && gimple_call_builtin_p (call_stmt, BUILT_IN_STRSTR)
> +		      && (call_lhs = gimple_call_lhs (call_stmt))
> +		      && has_single_use (call_lhs))

This might not optimize if you have:
  _5 = foo ();
  _7 = __builtin_strstr (_5, "abcd");
  _8 = _5 == _7;

Or even you could have:
  _5 = __builtin_strstr (...);
  _7 = __builtin_strstr (_5, "abcd");
  _8 = _5 == _7;

So I wonder if you shouldn't do:
		  gimple *call_stmt = NULL;
		  for (int pass = 0; pass < 2; pass++)
		    {
		      gimple *g = SSA_NAME_DEF_STMT (rhs1);
		      if (gimple_call_builtin_p (g, BUILT_IN_STRSTR)
			  && gimple_call_lhs (g) == rhs1
			  && has_single_use (rhs1)
			  && gimple_call_arg (g, 0) == rhs2)
			{
			  call_stmt = g;
			  break;
			}
		      std::swap (rhs1, rhs2);
		    }
		  if (call_stmt)
...

I think you don't need operand_equal_p, because SSA_NAMEs should just
be the same pointer if they are the same thing.
The above way you handle both orderings.  Perhaps also it is big enough to
be done in a separate function, which you call with the code/rhs1/rhs2 and
stmt for the EQ/NE_EXPR is_gimple_assign as well as for COND_EXPR and
GIMPLE_COND.

	Jakub

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: Fold strstr (s, t) eq/ne s to strcmp (s, t) eq/ne 0 if strlen (t) is known
  2016-12-09 12:29         ` Jakub Jelinek
@ 2016-12-13  9:38           ` Prathamesh Kulkarni
  2016-12-13  9:57             ` Jakub Jelinek
  0 siblings, 1 reply; 18+ messages in thread
From: Prathamesh Kulkarni @ 2016-12-13  9:38 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: gcc Patches, Richard Biener

[-- Attachment #1: Type: text/plain, Size: 4284 bytes --]

On 9 December 2016 at 17:59, Jakub Jelinek <jakub@redhat.com> wrote:
> On Fri, Dec 09, 2016 at 05:36:41PM +0530, Prathamesh Kulkarni wrote:
>> --- a/gcc/tree-ssa-strlen.c
>> +++ b/gcc/tree-ssa-strlen.c
>> @@ -2302,7 +2302,81 @@ strlen_optimize_stmt (gimple_stmt_iterator *gsi)
>>         else if (gimple_assign_rhs_code (stmt) == POINTER_PLUS_EXPR)
>>           handle_pointer_plus (gsi);
>>       }
>> -      else if (TREE_CODE (lhs) != SSA_NAME && !TREE_SIDE_EFFECTS (lhs))
>> +
>> +      /* Fold strstr (s, t) == s to memcmp (s, t, strlen (t)) == 0.
>> +      if strlen (t) is known and var holding return value of strstr
>> +      has single use.  */
>> +
>> +      else if (TREE_CODE (lhs) == SSA_NAME && INTEGRAL_TYPE_P (TREE_TYPE (lhs)))
>> +     {
>> +       enum tree_code code = gimple_assign_rhs_code (stmt);
>> +       if (code == EQ_EXPR || code == NE_EXPR)
>
> This way you handle _8 = _5 == _7;, but not if (_5 == _7) bar ();.  Shouldn't you
> also handle GIMPLE_COND similarly (of course, the rhs1 and rhs2 grabbing
> and code grabbing is different for GIMPLE_COND.  But the rest should be
> the same, except again that you don't want to replace the GIMPLE_COND, but
> adjust it.  Maybe also COND_EXPR in gimple_assign (_9 = _5 == _7 ? _10 : _11;).
>
>> +         {
>> +           tree rhs1 = gimple_assign_rhs1 (stmt);
>> +           tree rhs2 = gimple_assign_rhs2 (stmt);
>> +           if (TREE_CODE (rhs1) == SSA_NAME
>> +               && TREE_CODE (rhs2) == SSA_NAME)
>> +             {
>> +               gcall *call_stmt = dyn_cast<gcall *> (SSA_NAME_DEF_STMT (rhs1));
>> +               if (!call_stmt)
>> +                 {
>> +                   call_stmt = dyn_cast<gcall *> (SSA_NAME_DEF_STMT (rhs2));
>> +                   tree tmp = rhs1;
>> +                   rhs1 = rhs2;
>> +                   rhs2 = tmp;
>
> We use std::swap (rhs1, rhs2); in this case these days.
>
>> +                 }
>> +
>> +               tree call_lhs;
>> +               if (call_stmt
>> +                   && gimple_call_builtin_p (call_stmt, BUILT_IN_STRSTR)
>> +                   && (call_lhs = gimple_call_lhs (call_stmt))
>> +                   && has_single_use (call_lhs))
>
> This might not optimize if you have:
>   _5 = foo ();
>   _7 = __builtin_strstr (_5, "abcd");
>   _8 = _5 == _7;
>
> Or even you could have:
>   _5 = __builtin_strstr (...);
>   _7 = __builtin_strstr (_5, "abcd");
>   _8 = _5 == _7;
>
> So I wonder if you shouldn't do:
>                   gimple *call_stmt = NULL;
>                   for (int pass = 0; pass < 2; pass++)
>                     {
>                       gimple *g = SSA_NAME_DEF_STMT (rhs1);
>                       if (gimple_call_builtin_p (g, BUILT_IN_STRSTR)
>                           && gimple_call_lhs (g) == rhs1
>                           && has_single_use (rhs1)
>                           && gimple_call_arg (g, 0) == rhs2)
>                         {
>                           call_stmt = g;
>                           break;
>                         }
>                       std::swap (rhs1, rhs2);
>                     }
>                   if (call_stmt)
> ...
>
> I think you don't need operand_equal_p, because SSA_NAMEs should just
> be the same pointer if they are the same thing.
> The above way you handle both orderings.  Perhaps also it is big enough to
> be done in a separate function, which you call with the code/rhs1/rhs2 and
> stmt for the EQ/NE_EXPR is_gimple_assign as well as for COND_EXPR and
> GIMPLE_COND.
Hi Jakub,
Thanks for the suggestions. It didn't occur to me to check for gimple_cond.
I have tried to do the changes in the attached version.
I am not sure if I have handled cond_expr correctly.
IIUC, if gimple_assign has code cond_expr, then the condition is
stored in gimple_assign_rhs1,
however it's not a single operand but a tree of the form "op1 cond_code op2".
Is that correct ?

However I am not able to write a test-case that generates cond_expr in the IL.
I tried:
t1 = strstr (s, t);
(t1 == s)  ? foo() : bar ();
and other such variants but it seems the ?: operator is getting
lowered to gimple_cond instead.

Bootstrap+tested on x86_64-unknown-linux-gnu
and cross-tested on arm*-*-*, aarch64*-*-*.
Does it look OK ?

Thanks,
Prathamesh
>
>         Jakub

[-- Attachment #2: tcwg-701-9.txt --]
[-- Type: text/plain, Size: 6124 bytes --]

2016-12-13  Jakub Jelinek  <jakub@redhat.com>
	    Prathamesh Kulkarni  <prathamesh.kulkarni@linaro.org>

	* tree-ssa-strlen.c (fold_strstr_to_memcmp): New function.
	(strlen_optimize_stmt): Call fold_strstr_to_memcmp.

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..089b3a2
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/strlenopt-30.c
@@ -0,0 +1,63 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-strlen" } */
+
+__attribute__((no_icf))
+_Bool f1(char *s)
+{
+  return __builtin_strstr (s, "hello") == s;
+}
+
+__attribute__((no_icf))
+_Bool f2(char *s)
+{
+  return s == __builtin_strstr (s, "hello");
+}
+
+__attribute__((no_icf))
+_Bool f3(char *s)
+{
+  return s != __builtin_strstr (s, "hello");
+}
+
+__attribute__((no_icf))
+_Bool f4()
+{
+  char *foo_f4(void);
+  char *t1 = foo_f4();
+  char *t2 = __builtin_strstr (t1, "hello");
+  _Bool t3 = t2 == t1;
+  return t3;
+}
+
+__attribute__((no_icf))
+void f5(char *s)
+{
+  char *t1 = __builtin_strstr (s, "hello");
+  void foo_f5(void);
+  if (t1 != s)
+    foo_f5();
+}
+
+/* Do not perform transform, since strlen (t)
+   is unknown.  */
+
+__attribute__((no_icf))
+_Bool f6(char *s, char *t)
+{
+  return __builtin_strstr (s, t) == s;
+}
+
+/* Do not perform transform in this case, since
+   t1 doesn't have single use.  */
+
+__attribute__((no_icf))
+_Bool f7(char *s)
+{
+  void foo_f7(char *);
+
+  char *t1 = __builtin_strstr (s, "hello");
+  foo_f7 (t1);
+  return (t1 == s);
+}
+
+/* { dg-final { scan-tree-dump-times "__builtin_memcmp" 5 "strlen" } } */
diff --git a/gcc/tree-ssa-strlen.c b/gcc/tree-ssa-strlen.c
index 339812e..a4a0ae1 100644
--- a/gcc/tree-ssa-strlen.c
+++ b/gcc/tree-ssa-strlen.c
@@ -2222,6 +2222,93 @@ handle_char_store (gimple_stmt_iterator *gsi)
   return true;
 }
 
+/* Try to fold strstr (s, t) == s to memcmp (s, t, strlen (t)) == 0.  */
+
+static void
+fold_strstr_to_memcmp(enum tree_code code, tree rhs1, tree rhs2, gimple *stmt)
+{
+  gimple *call_stmt = NULL;
+  for (int pass = 0; pass < 2; pass++)
+    {
+      gimple *g = SSA_NAME_DEF_STMT (rhs1);
+      if (g
+	  && gimple_call_builtin_p (g, BUILT_IN_STRSTR)
+	  && has_single_use (rhs1)
+	  && gimple_call_arg (as_a<gcall *> (g), 0) == rhs2)
+	{
+	  call_stmt = g;
+	  break;
+	}
+      std::swap (rhs1, rhs2);
+    }
+
+  if (call_stmt)
+    {
+      tree arg0 = gimple_call_arg (call_stmt, 0);
+
+      if (arg0 == rhs2)
+	{
+	  tree arg1 = gimple_call_arg (call_stmt, 1);
+	  tree arg1_len = NULL_TREE;
+	  int idx = get_stridx (arg1);
+
+	  if (idx)
+	    {
+	      if (idx < 0)
+		arg1_len = build_int_cst (size_type_node, ~idx);
+	      else
+		{
+		  strinfo *si = get_strinfo (idx);
+		  if (si)
+		    arg1_len = get_string_length (si);
+		}
+	    }
+
+	  if (arg1_len != NULL_TREE)
+	    {
+	      gimple_stmt_iterator gsi = gsi_for_stmt (call_stmt);
+	      tree memcmp_decl = builtin_decl_explicit (BUILT_IN_MEMCMP);
+	      gcall *memcmp_call = gimple_build_call (memcmp_decl, 3,
+						      arg0, arg1, arg1_len);
+	      tree memcmp_lhs = make_ssa_name (integer_type_node);
+	      gimple_set_vuse (memcmp_call, gimple_vuse (call_stmt));
+	      gimple_call_set_lhs (memcmp_call, memcmp_lhs);
+	      gsi_remove (&gsi, true);
+	      gsi_insert_before (&gsi, memcmp_call, GSI_SAME_STMT);
+	      tree zero = build_zero_cst (TREE_TYPE (memcmp_lhs));
+
+	      if (is_gimple_assign (stmt))
+		{
+		  if (gimple_assign_rhs_code (stmt) == COND_EXPR)
+		    {
+		      tree cond = gimple_assign_rhs1 (stmt);
+		      TREE_SET_CODE (cond, EQ_EXPR);
+		      TREE_OPERAND (cond, 0) = memcmp_lhs;
+		      TREE_OPERAND (cond, 1) = zero;
+		      update_stmt (stmt);
+		    }
+		  else
+		    {
+		      gsi = gsi_for_stmt (stmt);
+		      tree lhs = gimple_assign_lhs (stmt);
+		      gassign *ga = gimple_build_assign (lhs, code, memcmp_lhs,
+							 zero);
+		      gsi_replace (&gsi, ga, false);
+		    }
+		}
+	      else
+		{
+		  gcond *cond = as_a<gcond *> (stmt);
+		  gimple_cond_set_lhs (cond, memcmp_lhs);
+		  gimple_cond_set_rhs (cond, zero);
+		  gimple_cond_set_code (cond, EQ_EXPR);
+		  update_stmt (cond);
+		}
+	    }
+	}
+    }
+}
+
 /* Attempt to optimize a single statement at *GSI using string length
    knowledge.  */
 
@@ -2302,7 +2389,34 @@ strlen_optimize_stmt (gimple_stmt_iterator *gsi)
 	  else if (gimple_assign_rhs_code (stmt) == POINTER_PLUS_EXPR)
 	    handle_pointer_plus (gsi);
 	}
-      else if (TREE_CODE (lhs) != SSA_NAME && !TREE_SIDE_EFFECTS (lhs))
+    else if (TREE_CODE (lhs) == SSA_NAME && INTEGRAL_TYPE_P (TREE_TYPE (lhs)))
+      {
+	enum tree_code code = gimple_assign_rhs_code (stmt);
+	if (code == COND_EXPR)
+	  {
+	    tree cond = gimple_assign_rhs1 (stmt);
+	    enum tree_code cond_code = TREE_CODE (cond);
+
+	    if (cond_code == EQ_EXPR || cond_code == NE_EXPR)
+	      {
+		tree rhs1 = TREE_OPERAND (cond, 0);
+		tree rhs2 = TREE_OPERAND (cond, 1);
+		if (TREE_CODE (rhs1) == SSA_NAME
+		    && TREE_CODE (rhs2) == SSA_NAME)
+		  fold_strstr_to_memcmp (cond_code, rhs1, rhs2, stmt);
+	      }
+	  }
+	else if (code == EQ_EXPR || code == NE_EXPR)
+	  {
+	    tree rhs1 = gimple_assign_rhs1 (stmt);
+	    tree rhs2 = gimple_assign_rhs2 (stmt);
+
+	    if (TREE_CODE (rhs1) == SSA_NAME
+		&& TREE_CODE (rhs2) == SSA_NAME)
+	      fold_strstr_to_memcmp (code, rhs1, rhs2, stmt);
+	  }
+      }
+    else if (TREE_CODE (lhs) != SSA_NAME && !TREE_SIDE_EFFECTS (lhs))
 	{
 	  tree type = TREE_TYPE (lhs);
 	  if (TREE_CODE (type) == ARRAY_TYPE)
@@ -2316,6 +2430,17 @@ strlen_optimize_stmt (gimple_stmt_iterator *gsi)
 	    }
 	}
     }
+  else if (gcond *cond = dyn_cast<gcond *> (stmt))
+    {
+      enum tree_code code = gimple_cond_code (cond);
+      tree lhs = gimple_cond_lhs (stmt);
+      tree rhs = gimple_cond_rhs (stmt);
+
+      if ((code == EQ_EXPR || code == NE_EXPR)
+	  && TREE_CODE (lhs) == SSA_NAME
+	  && TREE_CODE (rhs) == SSA_NAME)
+	fold_strstr_to_memcmp (code, lhs, rhs, stmt);
+    }
 
   if (gimple_vdef (stmt))
     maybe_invalidate (stmt);

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: Fold strstr (s, t) eq/ne s to strcmp (s, t) eq/ne 0 if strlen (t) is known
  2016-12-13  9:38           ` Prathamesh Kulkarni
@ 2016-12-13  9:57             ` Jakub Jelinek
  2016-12-13 12:11               ` Prathamesh Kulkarni
  0 siblings, 1 reply; 18+ messages in thread
From: Jakub Jelinek @ 2016-12-13  9:57 UTC (permalink / raw)
  To: Prathamesh Kulkarni; +Cc: gcc Patches, Richard Biener

On Tue, Dec 13, 2016 at 03:08:17PM +0530, Prathamesh Kulkarni wrote:
> Thanks for the suggestions. It didn't occur to me to check for gimple_cond.
> I have tried to do the changes in the attached version.
> I am not sure if I have handled cond_expr correctly.
> IIUC, if gimple_assign has code cond_expr, then the condition is
> stored in gimple_assign_rhs1,
> however it's not a single operand but a tree of the form "op1 cond_code op2".
> Is that correct ?

Yes.  gimple_assign_rhs1 will be in what you are looking for EQ_EXPR or
NE_EXPR tree, its TREE_CODE will be this code you want to check, and
TREE_OPERAND (exp, 0) and TREE_OPERAND (exp, 1) the rhs1 and rhs2 you use
elsewhere.

> However I am not able to write a test-case that generates cond_expr in the IL.
> I tried:
> t1 = strstr (s, t);
> (t1 == s)  ? foo() : bar ();
> and other such variants but it seems the ?: operator is getting
> lowered to gimple_cond instead.

It is, but in some cases tree-if-conv.c turns them back into COND_EXPRs.
I guess you need -ftree-loop-if-convert now, and it has to be in some loop
where the addition of cond_expr would likely turn it into a single bb loop.
You probably want constants or vars, not function calls in the ? :
expressions though.

> +/* Try to fold strstr (s, t) == s to memcmp (s, t, strlen (t)) == 0.  */
> +
> +static void
> +fold_strstr_to_memcmp(enum tree_code code, tree rhs1, tree rhs2, gimple *stmt)

Formatting, space before (.

> +{
> +  gimple *call_stmt = NULL;
> +  for (int pass = 0; pass < 2; pass++)
> +    {
> +      gimple *g = SSA_NAME_DEF_STMT (rhs1);
> +      if (g

I think g should be always non-NULL (except for invalid IL), so probably no
need to check it.

> +	  && gimple_call_builtin_p (g, BUILT_IN_STRSTR)
> +	  && has_single_use (rhs1)
> +	  && gimple_call_arg (as_a<gcall *> (g), 0) == rhs2)

I think gimple_call_arg works fine even with just gimple * argument.
So you can avoid the as_a<gcall *> (g) uglification and just use g.

> +	      if (is_gimple_assign (stmt))
> +		{
> +		  if (gimple_assign_rhs_code (stmt) == COND_EXPR)
> +		    {
> +		      tree cond = gimple_assign_rhs1 (stmt);
> +		      TREE_SET_CODE (cond, EQ_EXPR);

This looks weird.  You are hardcoding EQ_EXPR, while for the
other case below you use code.  So, do you handle properly both
EQ_EXPR and NE_EXPR for this and gimple_cond cases?
Also, for non-COND_EXPR assign you build a new stmt instead of reusing
the existing one, why?

> +		      TREE_OPERAND (cond, 0) = memcmp_lhs;
> +		      TREE_OPERAND (cond, 1) = zero;
> +		      update_stmt (stmt);
> +		    }
> +		  else
> +		    {
> +		      gsi = gsi_for_stmt (stmt);
> +		      tree lhs = gimple_assign_lhs (stmt);
> +		      gassign *ga = gimple_build_assign (lhs, code, memcmp_lhs,
> +							 zero);
> +		      gsi_replace (&gsi, ga, false);
> +		    }
> +		}
> +	      else
> +		{
> +		  gcond *cond = as_a<gcond *> (stmt);
> +		  gimple_cond_set_lhs (cond, memcmp_lhs);
> +		  gimple_cond_set_rhs (cond, zero);
> +		  gimple_cond_set_code (cond, EQ_EXPR);

Likewise here.

	Jakub

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: Fold strstr (s, t) eq/ne s to strcmp (s, t) eq/ne 0 if strlen (t) is known
  2016-12-13  9:57             ` Jakub Jelinek
@ 2016-12-13 12:11               ` Prathamesh Kulkarni
  2016-12-13 12:24                 ` Jakub Jelinek
  0 siblings, 1 reply; 18+ messages in thread
From: Prathamesh Kulkarni @ 2016-12-13 12:11 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: gcc Patches, Richard Biener

[-- Attachment #1: Type: text/plain, Size: 3799 bytes --]

On 13 December 2016 at 15:27, Jakub Jelinek <jakub@redhat.com> wrote:
> On Tue, Dec 13, 2016 at 03:08:17PM +0530, Prathamesh Kulkarni wrote:
>> Thanks for the suggestions. It didn't occur to me to check for gimple_cond.
>> I have tried to do the changes in the attached version.
>> I am not sure if I have handled cond_expr correctly.
>> IIUC, if gimple_assign has code cond_expr, then the condition is
>> stored in gimple_assign_rhs1,
>> however it's not a single operand but a tree of the form "op1 cond_code op2".
>> Is that correct ?
>
> Yes.  gimple_assign_rhs1 will be in what you are looking for EQ_EXPR or
> NE_EXPR tree, its TREE_CODE will be this code you want to check, and
> TREE_OPERAND (exp, 0) and TREE_OPERAND (exp, 1) the rhs1 and rhs2 you use
> elsewhere.
>
>> However I am not able to write a test-case that generates cond_expr in the IL.
>> I tried:
>> t1 = strstr (s, t);
>> (t1 == s)  ? foo() : bar ();
>> and other such variants but it seems the ?: operator is getting
>> lowered to gimple_cond instead.
>
> It is, but in some cases tree-if-conv.c turns them back into COND_EXPRs.
> I guess you need -ftree-loop-if-convert now, and it has to be in some loop
> where the addition of cond_expr would likely turn it into a single bb loop.
> You probably want constants or vars, not function calls in the ? :
> expressions though.
>
>> +/* Try to fold strstr (s, t) == s to memcmp (s, t, strlen (t)) == 0.  */
>> +
>> +static void
>> +fold_strstr_to_memcmp(enum tree_code code, tree rhs1, tree rhs2, gimple *stmt)
>
> Formatting, space before (.
>
>> +{
>> +  gimple *call_stmt = NULL;
>> +  for (int pass = 0; pass < 2; pass++)
>> +    {
>> +      gimple *g = SSA_NAME_DEF_STMT (rhs1);
>> +      if (g
>
> I think g should be always non-NULL (except for invalid IL), so probably no
> need to check it.
Ah indeed, thanks for pointing out. I assumed if ssa-var has default definition,
then SSA_NAME_DEF_STMT would be NULL, but it's GIMPLE_NOP.
>
>> +       && gimple_call_builtin_p (g, BUILT_IN_STRSTR)
>> +       && has_single_use (rhs1)
>> +       && gimple_call_arg (as_a<gcall *> (g), 0) == rhs2)
>
> I think gimple_call_arg works fine even with just gimple * argument.
> So you can avoid the as_a<gcall *> (g) uglification and just use g.
>
>> +           if (is_gimple_assign (stmt))
>> +             {
>> +               if (gimple_assign_rhs_code (stmt) == COND_EXPR)
>> +                 {
>> +                   tree cond = gimple_assign_rhs1 (stmt);
>> +                   TREE_SET_CODE (cond, EQ_EXPR);
>
> This looks weird.  You are hardcoding EQ_EXPR, while for the
> other case below you use code.  So, do you handle properly both
> EQ_EXPR and NE_EXPR for this and gimple_cond cases?
> Also, for non-COND_EXPR assign you build a new stmt instead of reusing
> the existing one, why?
>
>> +                   TREE_OPERAND (cond, 0) = memcmp_lhs;
>> +                   TREE_OPERAND (cond, 1) = zero;
>> +                   update_stmt (stmt);
>> +                 }
>> +               else
>> +                 {
>> +                   gsi = gsi_for_stmt (stmt);
>> +                   tree lhs = gimple_assign_lhs (stmt);
>> +                   gassign *ga = gimple_build_assign (lhs, code, memcmp_lhs,
>> +                                                      zero);
>> +                   gsi_replace (&gsi, ga, false);
>> +                 }
>> +             }
>> +           else
>> +             {
>> +               gcond *cond = as_a<gcond *> (stmt);
>> +               gimple_cond_set_lhs (cond, memcmp_lhs);
>> +               gimple_cond_set_rhs (cond, zero);
>> +               gimple_cond_set_code (cond, EQ_EXPR);
>
> Likewise here.
Oops, sorry about that :/
Does this version look OK ?
Bootstrap+test in progress.

Thanks,
Prathamesh
>
>         Jakub

[-- Attachment #2: tcwg-701-10.txt --]
[-- Type: text/plain, Size: 6022 bytes --]

2016-12-13  Jakub Jelinek  <jakub@redhat.com>
	    Prathamesh Kulkarni  <prathamesh.kulkarni@linaro.org>

	* tree-ssa-strlen.c (fold_strstr_to_memcmp): New function.
	(strlen_optimize_stmt): Call fold_strstr_to_memcmp.

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..089b3a2
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/strlenopt-30.c
@@ -0,0 +1,63 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-strlen" } */
+
+__attribute__((no_icf))
+_Bool f1(char *s)
+{
+  return __builtin_strstr (s, "hello") == s;
+}
+
+__attribute__((no_icf))
+_Bool f2(char *s)
+{
+  return s == __builtin_strstr (s, "hello");
+}
+
+__attribute__((no_icf))
+_Bool f3(char *s)
+{
+  return s != __builtin_strstr (s, "hello");
+}
+
+__attribute__((no_icf))
+_Bool f4()
+{
+  char *foo_f4(void);
+  char *t1 = foo_f4();
+  char *t2 = __builtin_strstr (t1, "hello");
+  _Bool t3 = t2 == t1;
+  return t3;
+}
+
+__attribute__((no_icf))
+void f5(char *s)
+{
+  char *t1 = __builtin_strstr (s, "hello");
+  void foo_f5(void);
+  if (t1 != s)
+    foo_f5();
+}
+
+/* Do not perform transform, since strlen (t)
+   is unknown.  */
+
+__attribute__((no_icf))
+_Bool f6(char *s, char *t)
+{
+  return __builtin_strstr (s, t) == s;
+}
+
+/* Do not perform transform in this case, since
+   t1 doesn't have single use.  */
+
+__attribute__((no_icf))
+_Bool f7(char *s)
+{
+  void foo_f7(char *);
+
+  char *t1 = __builtin_strstr (s, "hello");
+  foo_f7 (t1);
+  return (t1 == s);
+}
+
+/* { dg-final { scan-tree-dump-times "__builtin_memcmp" 5 "strlen" } } */
diff --git a/gcc/tree-ssa-strlen.c b/gcc/tree-ssa-strlen.c
index 339812e..b19dab6 100644
--- a/gcc/tree-ssa-strlen.c
+++ b/gcc/tree-ssa-strlen.c
@@ -2222,6 +2222,90 @@ handle_char_store (gimple_stmt_iterator *gsi)
   return true;
 }
 
+/* Try to fold strstr (s, t) eq/ne s to memcmp (s, t, strlen (t)) eq/ne 0.  */
+
+static void
+fold_strstr_to_memcmp (enum tree_code code, tree rhs1, tree rhs2, gimple *stmt)
+{
+  gimple *call_stmt = NULL;
+  for (int pass = 0; pass < 2; pass++)
+    {
+      gimple *g = SSA_NAME_DEF_STMT (rhs1);
+      if (gimple_call_builtin_p (g, BUILT_IN_STRSTR)
+	  && has_single_use (rhs1)
+	  && gimple_call_arg (g, 0) == rhs2)
+	{
+	  call_stmt = g;
+	  break;
+	}
+      std::swap (rhs1, rhs2);
+    }
+
+  if (call_stmt)
+    {
+      tree arg0 = gimple_call_arg (call_stmt, 0);
+
+      if (arg0 == rhs2)
+	{
+	  tree arg1 = gimple_call_arg (call_stmt, 1);
+	  tree arg1_len = NULL_TREE;
+	  int idx = get_stridx (arg1);
+
+	  if (idx)
+	    {
+	      if (idx < 0)
+		arg1_len = build_int_cst (size_type_node, ~idx);
+	      else
+		{
+		  strinfo *si = get_strinfo (idx);
+		  if (si)
+		    arg1_len = get_string_length (si);
+		}
+	    }
+
+	  if (arg1_len != NULL_TREE)
+	    {
+	      gimple_stmt_iterator gsi = gsi_for_stmt (call_stmt);
+	      tree memcmp_decl = builtin_decl_explicit (BUILT_IN_MEMCMP);
+	      gcall *memcmp_call = gimple_build_call (memcmp_decl, 3,
+						      arg0, arg1, arg1_len);
+	      tree memcmp_lhs = make_ssa_name (integer_type_node);
+	      gimple_set_vuse (memcmp_call, gimple_vuse (call_stmt));
+	      gimple_call_set_lhs (memcmp_call, memcmp_lhs);
+	      gsi_remove (&gsi, true);
+	      gsi_insert_before (&gsi, memcmp_call, GSI_SAME_STMT);
+	      tree zero = build_zero_cst (TREE_TYPE (memcmp_lhs));
+
+	      if (is_gimple_assign (stmt))
+		{
+		  if (gimple_assign_rhs_code (stmt) == COND_EXPR)
+		    {
+		      tree cond = gimple_assign_rhs1 (stmt);
+		      TREE_SET_CODE (cond, code);
+		      TREE_OPERAND (cond, 0) = memcmp_lhs;
+		      TREE_OPERAND (cond, 1) = zero;
+		      update_stmt (stmt);
+		    }
+		  else
+		    {
+		      gimple_assign_set_rhs1 (stmt, memcmp_lhs);
+		      gimple_assign_set_rhs2 (stmt, zero);
+		      update_stmt (stmt);
+		    }
+		}
+	      else
+		{
+		  gcond *cond = as_a<gcond *> (stmt);
+		  gimple_cond_set_lhs (cond, memcmp_lhs);
+		  gimple_cond_set_rhs (cond, zero);
+		  gimple_cond_set_code (cond, code);
+		  update_stmt (cond);
+		}
+	    }
+	}
+    }
+}
+
 /* Attempt to optimize a single statement at *GSI using string length
    knowledge.  */
 
@@ -2302,7 +2386,34 @@ strlen_optimize_stmt (gimple_stmt_iterator *gsi)
 	  else if (gimple_assign_rhs_code (stmt) == POINTER_PLUS_EXPR)
 	    handle_pointer_plus (gsi);
 	}
-      else if (TREE_CODE (lhs) != SSA_NAME && !TREE_SIDE_EFFECTS (lhs))
+    else if (TREE_CODE (lhs) == SSA_NAME && INTEGRAL_TYPE_P (TREE_TYPE (lhs)))
+      {
+	enum tree_code code = gimple_assign_rhs_code (stmt);
+	if (code == COND_EXPR)
+	  {
+	    tree cond = gimple_assign_rhs1 (stmt);
+	    enum tree_code cond_code = TREE_CODE (cond);
+
+	    if (cond_code == EQ_EXPR || cond_code == NE_EXPR)
+	      {
+		tree rhs1 = TREE_OPERAND (cond, 0);
+		tree rhs2 = TREE_OPERAND (cond, 1);
+		if (TREE_CODE (rhs1) == SSA_NAME
+		    && TREE_CODE (rhs2) == SSA_NAME)
+		  fold_strstr_to_memcmp (cond_code, rhs1, rhs2, stmt);
+	      }
+	  }
+	else if (code == EQ_EXPR || code == NE_EXPR)
+	  {
+	    tree rhs1 = gimple_assign_rhs1 (stmt);
+	    tree rhs2 = gimple_assign_rhs2 (stmt);
+
+	    if (TREE_CODE (rhs1) == SSA_NAME
+		&& TREE_CODE (rhs2) == SSA_NAME)
+	      fold_strstr_to_memcmp (code, rhs1, rhs2, stmt);
+	  }
+      }
+    else if (TREE_CODE (lhs) != SSA_NAME && !TREE_SIDE_EFFECTS (lhs))
 	{
 	  tree type = TREE_TYPE (lhs);
 	  if (TREE_CODE (type) == ARRAY_TYPE)
@@ -2316,6 +2427,17 @@ strlen_optimize_stmt (gimple_stmt_iterator *gsi)
 	    }
 	}
     }
+  else if (gcond *cond = dyn_cast<gcond *> (stmt))
+    {
+      enum tree_code code = gimple_cond_code (cond);
+      tree lhs = gimple_cond_lhs (stmt);
+      tree rhs = gimple_cond_rhs (stmt);
+
+      if ((code == EQ_EXPR || code == NE_EXPR)
+	  && TREE_CODE (lhs) == SSA_NAME
+	  && TREE_CODE (rhs) == SSA_NAME)
+	fold_strstr_to_memcmp (code, lhs, rhs, stmt);
+    }
 
   if (gimple_vdef (stmt))
     maybe_invalidate (stmt);

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: Fold strstr (s, t) eq/ne s to strcmp (s, t) eq/ne 0 if strlen (t) is known
  2016-12-13 12:11               ` Prathamesh Kulkarni
@ 2016-12-13 12:24                 ` Jakub Jelinek
  2016-12-14  8:03                   ` Prathamesh Kulkarni
  0 siblings, 1 reply; 18+ messages in thread
From: Jakub Jelinek @ 2016-12-13 12:24 UTC (permalink / raw)
  To: Prathamesh Kulkarni; +Cc: gcc Patches, Richard Biener

On Tue, Dec 13, 2016 at 05:41:09PM +0530, Prathamesh Kulkarni wrote:
> --- a/gcc/tree-ssa-strlen.c
> +++ b/gcc/tree-ssa-strlen.c
> @@ -2222,6 +2222,90 @@ handle_char_store (gimple_stmt_iterator *gsi)
>    return true;
>  }
>  
> +/* Try to fold strstr (s, t) eq/ne s to memcmp (s, t, strlen (t)) eq/ne 0.  */
> +
> +static void
> +fold_strstr_to_memcmp (enum tree_code code, tree rhs1, tree rhs2, gimple *stmt)

You can drop code argument here, see below.  And I'd say it is better to
do the
  if (TREE_CODE (rhs1) != SSA_NAME || TREE_CODE (rhs2) != SSA_NAME)
    return;
here than repeat it in all the callers.

> +		  if (gimple_assign_rhs_code (stmt) == COND_EXPR)
> +		    {
> +		      tree cond = gimple_assign_rhs1 (stmt);
> +		      TREE_SET_CODE (cond, code);

TREE_CODE (cond) is already code, so no need to set it again.

> +		  gcond *cond = as_a<gcond *> (stmt);
> +		  gimple_cond_set_lhs (cond, memcmp_lhs);
> +		  gimple_cond_set_rhs (cond, zero);
> +		  gimple_cond_set_code (cond, code);

And gimple_cond_code (cond) == code here too.

> +		  update_stmt (cond);
> +		}

You can perhaps move the update_stmt (stmt); to a single spot after
all the 3 cases are handled.

> +	    if (cond_code == EQ_EXPR || cond_code == NE_EXPR)
> +	      {
> +		tree rhs1 = TREE_OPERAND (cond, 0);
> +		tree rhs2 = TREE_OPERAND (cond, 1);

While it is necessary to check cond_code here and in the other spots
similarly, because otherwise you don't know if it has 2 arguments etc.,
you can avoid the SSA_NAME tests here.

> +		if (TREE_CODE (rhs1) == SSA_NAME
> +		    && TREE_CODE (rhs2) == SSA_NAME)
> +		  fold_strstr_to_memcmp (cond_code, rhs1, rhs2, stmt);
> +	      }
> +	  }
> +	else if (code == EQ_EXPR || code == NE_EXPR)
> +	  {
> +	    tree rhs1 = gimple_assign_rhs1 (stmt);
> +	    tree rhs2 = gimple_assign_rhs2 (stmt);
> +
> +	    if (TREE_CODE (rhs1) == SSA_NAME
> +		&& TREE_CODE (rhs2) == SSA_NAME)

And here.
> +	      fold_strstr_to_memcmp (code, rhs1, rhs2, stmt);
> +	  }
> +      }
> +    else if (TREE_CODE (lhs) != SSA_NAME && !TREE_SIDE_EFFECTS (lhs))
>  	{
>  	  tree type = TREE_TYPE (lhs);
>  	  if (TREE_CODE (type) == ARRAY_TYPE)
> @@ -2316,6 +2427,17 @@ strlen_optimize_stmt (gimple_stmt_iterator *gsi)
>  	    }
>  	}
>      }
> +  else if (gcond *cond = dyn_cast<gcond *> (stmt))
> +    {
> +      enum tree_code code = gimple_cond_code (cond);
> +      tree lhs = gimple_cond_lhs (stmt);
> +      tree rhs = gimple_cond_rhs (stmt);
> +
> +      if ((code == EQ_EXPR || code == NE_EXPR)
> +	  && TREE_CODE (lhs) == SSA_NAME
> +	  && TREE_CODE (rhs) == SSA_NAME)

And here.
> +	fold_strstr_to_memcmp (code, lhs, rhs, stmt);
> +    }
>  
>    if (gimple_vdef (stmt))
>      maybe_invalidate (stmt);

Otherwise LGTM, but it would be nice to cover also the COND_EXPR case by a
testcase (can be done incrementally).

	Jakub

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: Fold strstr (s, t) eq/ne s to strcmp (s, t) eq/ne 0 if strlen (t) is known
  2016-12-13 12:24                 ` Jakub Jelinek
@ 2016-12-14  8:03                   ` Prathamesh Kulkarni
  2016-12-14  8:07                     ` Jakub Jelinek
  0 siblings, 1 reply; 18+ messages in thread
From: Prathamesh Kulkarni @ 2016-12-14  8:03 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: gcc Patches, Richard Biener

[-- Attachment #1: Type: text/plain, Size: 3555 bytes --]

On 13 December 2016 at 17:54, Jakub Jelinek <jakub@redhat.com> wrote:
> On Tue, Dec 13, 2016 at 05:41:09PM +0530, Prathamesh Kulkarni wrote:
>> --- a/gcc/tree-ssa-strlen.c
>> +++ b/gcc/tree-ssa-strlen.c
>> @@ -2222,6 +2222,90 @@ handle_char_store (gimple_stmt_iterator *gsi)
>>    return true;
>>  }
>>
>> +/* Try to fold strstr (s, t) eq/ne s to memcmp (s, t, strlen (t)) eq/ne 0.  */
>> +
>> +static void
>> +fold_strstr_to_memcmp (enum tree_code code, tree rhs1, tree rhs2, gimple *stmt)
>
> You can drop code argument here, see below.  And I'd say it is better to
> do the
>   if (TREE_CODE (rhs1) != SSA_NAME || TREE_CODE (rhs2) != SSA_NAME)
>     return;
> here than repeat it in all the callers.
>
>> +               if (gimple_assign_rhs_code (stmt) == COND_EXPR)
>> +                 {
>> +                   tree cond = gimple_assign_rhs1 (stmt);
>> +                   TREE_SET_CODE (cond, code);
>
> TREE_CODE (cond) is already code, so no need to set it again.
>
>> +               gcond *cond = as_a<gcond *> (stmt);
>> +               gimple_cond_set_lhs (cond, memcmp_lhs);
>> +               gimple_cond_set_rhs (cond, zero);
>> +               gimple_cond_set_code (cond, code);
>
> And gimple_cond_code (cond) == code here too.
>
>> +               update_stmt (cond);
>> +             }
>
> You can perhaps move the update_stmt (stmt); to a single spot after
> all the 3 cases are handled.
>
>> +         if (cond_code == EQ_EXPR || cond_code == NE_EXPR)
>> +           {
>> +             tree rhs1 = TREE_OPERAND (cond, 0);
>> +             tree rhs2 = TREE_OPERAND (cond, 1);
>
> While it is necessary to check cond_code here and in the other spots
> similarly, because otherwise you don't know if it has 2 arguments etc.,
> you can avoid the SSA_NAME tests here.
>
>> +             if (TREE_CODE (rhs1) == SSA_NAME
>> +                 && TREE_CODE (rhs2) == SSA_NAME)
>> +               fold_strstr_to_memcmp (cond_code, rhs1, rhs2, stmt);
>> +           }
>> +       }
>> +     else if (code == EQ_EXPR || code == NE_EXPR)
>> +       {
>> +         tree rhs1 = gimple_assign_rhs1 (stmt);
>> +         tree rhs2 = gimple_assign_rhs2 (stmt);
>> +
>> +         if (TREE_CODE (rhs1) == SSA_NAME
>> +             && TREE_CODE (rhs2) == SSA_NAME)
>
> And here.
>> +           fold_strstr_to_memcmp (code, rhs1, rhs2, stmt);
>> +       }
>> +      }
>> +    else if (TREE_CODE (lhs) != SSA_NAME && !TREE_SIDE_EFFECTS (lhs))
>>       {
>>         tree type = TREE_TYPE (lhs);
>>         if (TREE_CODE (type) == ARRAY_TYPE)
>> @@ -2316,6 +2427,17 @@ strlen_optimize_stmt (gimple_stmt_iterator *gsi)
>>           }
>>       }
>>      }
>> +  else if (gcond *cond = dyn_cast<gcond *> (stmt))
>> +    {
>> +      enum tree_code code = gimple_cond_code (cond);
>> +      tree lhs = gimple_cond_lhs (stmt);
>> +      tree rhs = gimple_cond_rhs (stmt);
>> +
>> +      if ((code == EQ_EXPR || code == NE_EXPR)
>> +       && TREE_CODE (lhs) == SSA_NAME
>> +       && TREE_CODE (rhs) == SSA_NAME)
>
> And here.
>> +     fold_strstr_to_memcmp (code, lhs, rhs, stmt);
>> +    }
>>
>>    if (gimple_vdef (stmt))
>>      maybe_invalidate (stmt);
>
> Otherwise LGTM, but it would be nice to cover also the COND_EXPR case by a
> testcase (can be done incrementally).
Hi Jakub,
Done the changes in attached version.
Bootstrap+tested on x86_64-unknown-linux-gnu with default languages
and cross-tested on arm*-*-*, aarch64*-*-* with c,c++,fortran.
It it OK to commit ?
I am trying to come up with COND_EXPR test-case.

Thanks,
Prathamesh
>
>         Jakub

[-- Attachment #2: tcwg-701-11.txt --]
[-- Type: text/plain, Size: 5568 bytes --]

2016-12-14  Jakub Jelinek  <jakub@redhat.com>
	    Prathamesh Kulkarni  <prathamesh.kulkarni@linaro.org>

	* tree-ssa-strlen.c (fold_strstr_to_memcmp): New function.
	(strlen_optimize_stmt): Call fold_strstr_to_memcmp.

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..089b3a2
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/strlenopt-30.c
@@ -0,0 +1,63 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-strlen" } */
+
+__attribute__((no_icf))
+_Bool f1(char *s)
+{
+  return __builtin_strstr (s, "hello") == s;
+}
+
+__attribute__((no_icf))
+_Bool f2(char *s)
+{
+  return s == __builtin_strstr (s, "hello");
+}
+
+__attribute__((no_icf))
+_Bool f3(char *s)
+{
+  return s != __builtin_strstr (s, "hello");
+}
+
+__attribute__((no_icf))
+_Bool f4()
+{
+  char *foo_f4(void);
+  char *t1 = foo_f4();
+  char *t2 = __builtin_strstr (t1, "hello");
+  _Bool t3 = t2 == t1;
+  return t3;
+}
+
+__attribute__((no_icf))
+void f5(char *s)
+{
+  char *t1 = __builtin_strstr (s, "hello");
+  void foo_f5(void);
+  if (t1 != s)
+    foo_f5();
+}
+
+/* Do not perform transform, since strlen (t)
+   is unknown.  */
+
+__attribute__((no_icf))
+_Bool f6(char *s, char *t)
+{
+  return __builtin_strstr (s, t) == s;
+}
+
+/* Do not perform transform in this case, since
+   t1 doesn't have single use.  */
+
+__attribute__((no_icf))
+_Bool f7(char *s)
+{
+  void foo_f7(char *);
+
+  char *t1 = __builtin_strstr (s, "hello");
+  foo_f7 (t1);
+  return (t1 == s);
+}
+
+/* { dg-final { scan-tree-dump-times "__builtin_memcmp" 5 "strlen" } } */
diff --git a/gcc/tree-ssa-strlen.c b/gcc/tree-ssa-strlen.c
index 339812e..67075f0 100644
--- a/gcc/tree-ssa-strlen.c
+++ b/gcc/tree-ssa-strlen.c
@@ -2222,6 +2222,90 @@ handle_char_store (gimple_stmt_iterator *gsi)
   return true;
 }
 
+/* Try to fold strstr (s, t) eq/ne s to memcmp (s, t, strlen (t)) eq/ne 0.  */
+
+static void
+fold_strstr_to_memcmp (tree rhs1, tree rhs2, gimple *stmt)
+{
+  if (TREE_CODE (rhs1) != SSA_NAME
+      || TREE_CODE (rhs2) != SSA_NAME)
+    return;
+
+  gimple *call_stmt = NULL;
+  for (int pass = 0; pass < 2; pass++)
+    {
+      gimple *g = SSA_NAME_DEF_STMT (rhs1);
+      if (gimple_call_builtin_p (g, BUILT_IN_STRSTR)
+	  && has_single_use (rhs1)
+	  && gimple_call_arg (g, 0) == rhs2)
+	{
+	  call_stmt = g;
+	  break;
+	}
+      std::swap (rhs1, rhs2);
+    }
+
+  if (call_stmt)
+    {
+      tree arg0 = gimple_call_arg (call_stmt, 0);
+
+      if (arg0 == rhs2)
+	{
+	  tree arg1 = gimple_call_arg (call_stmt, 1);
+	  tree arg1_len = NULL_TREE;
+	  int idx = get_stridx (arg1);
+
+	  if (idx)
+	    {
+	      if (idx < 0)
+		arg1_len = build_int_cst (size_type_node, ~idx);
+	      else
+		{
+		  strinfo *si = get_strinfo (idx);
+		  if (si)
+		    arg1_len = get_string_length (si);
+		}
+	    }
+
+	  if (arg1_len != NULL_TREE)
+	    {
+	      gimple_stmt_iterator gsi = gsi_for_stmt (call_stmt);
+	      tree memcmp_decl = builtin_decl_explicit (BUILT_IN_MEMCMP);
+	      gcall *memcmp_call = gimple_build_call (memcmp_decl, 3,
+						      arg0, arg1, arg1_len);
+	      tree memcmp_lhs = make_ssa_name (integer_type_node);
+	      gimple_set_vuse (memcmp_call, gimple_vuse (call_stmt));
+	      gimple_call_set_lhs (memcmp_call, memcmp_lhs);
+	      gsi_remove (&gsi, true);
+	      gsi_insert_before (&gsi, memcmp_call, GSI_SAME_STMT);
+	      tree zero = build_zero_cst (TREE_TYPE (memcmp_lhs));
+
+	      if (is_gimple_assign (stmt))
+		{
+		  if (gimple_assign_rhs_code (stmt) == COND_EXPR)
+		    {
+		      tree cond = gimple_assign_rhs1 (stmt);
+		      TREE_OPERAND (cond, 0) = memcmp_lhs;
+		      TREE_OPERAND (cond, 1) = zero;
+		    }
+		  else
+		    {
+		      gimple_assign_set_rhs1 (stmt, memcmp_lhs);
+		      gimple_assign_set_rhs2 (stmt, zero);
+		    }
+		}
+	      else
+		{
+		  gcond *cond = as_a<gcond *> (stmt);
+		  gimple_cond_set_lhs (cond, memcmp_lhs);
+		  gimple_cond_set_rhs (cond, zero);
+		}
+	      update_stmt (stmt);
+	    }
+	}
+    }
+}
+
 /* Attempt to optimize a single statement at *GSI using string length
    knowledge.  */
 
@@ -2302,7 +2386,23 @@ strlen_optimize_stmt (gimple_stmt_iterator *gsi)
 	  else if (gimple_assign_rhs_code (stmt) == POINTER_PLUS_EXPR)
 	    handle_pointer_plus (gsi);
 	}
-      else if (TREE_CODE (lhs) != SSA_NAME && !TREE_SIDE_EFFECTS (lhs))
+    else if (TREE_CODE (lhs) == SSA_NAME && INTEGRAL_TYPE_P (TREE_TYPE (lhs)))
+      {
+	enum tree_code code = gimple_assign_rhs_code (stmt);
+	if (code == COND_EXPR)
+	  {
+	    tree cond = gimple_assign_rhs1 (stmt);
+	    enum tree_code cond_code = TREE_CODE (cond);
+
+	    if (cond_code == EQ_EXPR || cond_code == NE_EXPR)
+	      fold_strstr_to_memcmp (TREE_OPERAND (cond, 0),
+				     TREE_OPERAND (cond, 1), stmt);
+	  }
+	else if (code == EQ_EXPR || code == NE_EXPR)
+	  fold_strstr_to_memcmp (gimple_assign_rhs1 (stmt),
+				 gimple_assign_rhs2 (stmt), stmt);
+      }
+    else if (TREE_CODE (lhs) != SSA_NAME && !TREE_SIDE_EFFECTS (lhs))
 	{
 	  tree type = TREE_TYPE (lhs);
 	  if (TREE_CODE (type) == ARRAY_TYPE)
@@ -2316,6 +2416,13 @@ strlen_optimize_stmt (gimple_stmt_iterator *gsi)
 	    }
 	}
     }
+  else if (gcond *cond = dyn_cast<gcond *> (stmt))
+    {
+      enum tree_code code = gimple_cond_code (cond);
+      if (code == EQ_EXPR || code == NE_EXPR)
+	fold_strstr_to_memcmp (gimple_cond_lhs (stmt),
+			       gimple_cond_rhs (stmt), stmt);
+    }
 
   if (gimple_vdef (stmt))
     maybe_invalidate (stmt);

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: Fold strstr (s, t) eq/ne s to strcmp (s, t) eq/ne 0 if strlen (t) is known
  2016-12-14  8:03                   ` Prathamesh Kulkarni
@ 2016-12-14  8:07                     ` Jakub Jelinek
  2017-01-23 11:51                       ` Martin Liška
  0 siblings, 1 reply; 18+ messages in thread
From: Jakub Jelinek @ 2016-12-14  8:07 UTC (permalink / raw)
  To: Prathamesh Kulkarni; +Cc: gcc Patches, Richard Biener

On Wed, Dec 14, 2016 at 01:27:44PM +0530, Prathamesh Kulkarni wrote:
> 2016-12-14  Jakub Jelinek  <jakub@redhat.com>
> 	    Prathamesh Kulkarni  <prathamesh.kulkarni@linaro.org>
> 
> 	* tree-ssa-strlen.c (fold_strstr_to_memcmp): New function.
> 	(strlen_optimize_stmt): Call fold_strstr_to_memcmp.
> 
> testsuite/
> 	* gcc.dg/strlenopt-30.c: New test-case.

Ok, thanks.
But, you wrote the patch, so if you want to give me some credit, put
yourself first at least.

	Jakub

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: Fold strstr (s, t) eq/ne s to strcmp (s, t) eq/ne 0 if strlen (t) is known
  2016-12-14  8:07                     ` Jakub Jelinek
@ 2017-01-23 11:51                       ` Martin Liška
  2017-01-23 14:14                         ` [PATCH] Fix strstr folding (PR tree-optimization/79196) Martin Liška
  0 siblings, 1 reply; 18+ messages in thread
From: Martin Liška @ 2017-01-23 11:51 UTC (permalink / raw)
  To: Jakub Jelinek, Prathamesh Kulkarni; +Cc: gcc Patches, Richard Biener

On 12/14/2016 09:03 AM, Jakub Jelinek wrote:
> On Wed, Dec 14, 2016 at 01:27:44PM +0530, Prathamesh Kulkarni wrote:
>> 2016-12-14  Jakub Jelinek  <jakub@redhat.com>
>> 	    Prathamesh Kulkarni  <prathamesh.kulkarni@linaro.org>
>>
>> 	* tree-ssa-strlen.c (fold_strstr_to_memcmp): New function.
>> 	(strlen_optimize_stmt): Call fold_strstr_to_memcmp.
>>
>> testsuite/
>> 	* gcc.dg/strlenopt-30.c: New test-case.
> 
> Ok, thanks.
> But, you wrote the patch, so if you want to give me some credit, put
> yourself first at least.
> 
> 	Jakub
> 

Caused PR79196.

Thanks,
Martin

^ permalink raw reply	[flat|nested] 18+ messages in thread

* [PATCH] Fix strstr folding (PR tree-optimization/79196).
  2017-01-23 11:51                       ` Martin Liška
@ 2017-01-23 14:14                         ` Martin Liška
  2017-01-23 14:51                           ` Jakub Jelinek
  0 siblings, 1 reply; 18+ messages in thread
From: Martin Liška @ 2017-01-23 14:14 UTC (permalink / raw)
  To: Jakub Jelinek, Prathamesh Kulkarni; +Cc: gcc Patches, Richard Biener

[-- Attachment #1: Type: text/plain, Size: 265 bytes --]

As mentioned in the PR, it should be valid to use strncmp instead of memcmp
because first argument of a strstr can be shorter than a second (known) argument.

Patch can bootstrap on ppc64le-redhat-linux and survives regression tests.

Ready to be installed?
Martin

[-- Attachment #2: 0001-Fix-strstr-folding-PR-tree-optimization-79196.patch --]
[-- Type: text/x-patch, Size: 5349 bytes --]

From 4a9dcb24d82ce1793a281710ce1ddab59f9dd7a8 Mon Sep 17 00:00:00 2001
From: marxin <mliska@suse.cz>
Date: Mon, 23 Jan 2017 13:18:37 +0100
Subject: [PATCH] Fix strstr folding (PR tree-optimization/79196).

gcc/ChangeLog:

2017-01-23  Martin Liska  <mliska@suse.cz>

	* tree-ssa-strlen.c (fold_strstr_to_memcmp): Rename
	to fold_strstr_to_strncmp and fold the pattern to strncmp
	instead of memcmp.
	(strlen_optimize_stmt): Call the renamed function.

gcc/testsuite/ChangeLog:

2017-01-23  Martin Liska  <mliska@suse.cz>

	* gcc.dg/asan/pr79196.c: New test.
	* gcc.dg/strlenopt-30.c: Update scanned pattern.
---
 gcc/testsuite/gcc.dg/asan/pr79196.c | 17 +++++++++++++++++
 gcc/testsuite/gcc.dg/strlenopt-30.c |  2 +-
 gcc/tree-ssa-strlen.c               | 36 ++++++++++++++++++------------------
 3 files changed, 36 insertions(+), 19 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/asan/pr79196.c

diff --git a/gcc/testsuite/gcc.dg/asan/pr79196.c b/gcc/testsuite/gcc.dg/asan/pr79196.c
new file mode 100644
index 00000000000..66a31b90f30
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/asan/pr79196.c
@@ -0,0 +1,17 @@
+// PR tree-optimization/79196
+// { dg-do run }
+
+int
+__attribute__((noinline))
+test(char *a)
+{
+  if (__builtin_strstr (a, "DROP CONVERSION") == a)
+    return 1;
+
+  return 0;
+}
+
+int main(int argc, char **argv)
+{
+  return test ("x");
+}
diff --git a/gcc/testsuite/gcc.dg/strlenopt-30.c b/gcc/testsuite/gcc.dg/strlenopt-30.c
index 089b3a2341d..a85df686ce2 100644
--- a/gcc/testsuite/gcc.dg/strlenopt-30.c
+++ b/gcc/testsuite/gcc.dg/strlenopt-30.c
@@ -60,4 +60,4 @@ _Bool f7(char *s)
   return (t1 == s);
 }
 
-/* { dg-final { scan-tree-dump-times "__builtin_memcmp" 5 "strlen" } } */
+/* { dg-final { scan-tree-dump-times "__builtin_strncmp" 5 "strlen" } } */
diff --git a/gcc/tree-ssa-strlen.c b/gcc/tree-ssa-strlen.c
index 2e7ac2c7786..141115ed12b 100644
--- a/gcc/tree-ssa-strlen.c
+++ b/gcc/tree-ssa-strlen.c
@@ -2225,10 +2225,10 @@ handle_char_store (gimple_stmt_iterator *gsi)
   return true;
 }
 
-/* Try to fold strstr (s, t) eq/ne s to memcmp (s, t, strlen (t)) eq/ne 0.  */
+/* Try to fold strstr (s, t) eq/ne s to strncmp (s, t, strlen (t)) eq/ne 0.  */
 
 static void
-fold_strstr_to_memcmp (tree rhs1, tree rhs2, gimple *stmt)
+fold_strstr_to_strncmp (tree rhs1, tree rhs2, gimple *stmt)
 {
   if (TREE_CODE (rhs1) != SSA_NAME
       || TREE_CODE (rhs2) != SSA_NAME)
@@ -2273,34 +2273,34 @@ fold_strstr_to_memcmp (tree rhs1, tree rhs2, gimple *stmt)
 	  if (arg1_len != NULL_TREE)
 	    {
 	      gimple_stmt_iterator gsi = gsi_for_stmt (call_stmt);
-	      tree memcmp_decl = builtin_decl_explicit (BUILT_IN_MEMCMP);
-	      gcall *memcmp_call = gimple_build_call (memcmp_decl, 3,
+	      tree strncmp_decl = builtin_decl_explicit (BUILT_IN_STRNCMP);
+	      gcall *strncmp_call = gimple_build_call (strncmp_decl, 3,
 						      arg0, arg1, arg1_len);
-	      tree memcmp_lhs = make_ssa_name (integer_type_node);
-	      gimple_set_vuse (memcmp_call, gimple_vuse (call_stmt));
-	      gimple_call_set_lhs (memcmp_call, memcmp_lhs);
+	      tree strncmp_lhs = make_ssa_name (integer_type_node);
+	      gimple_set_vuse (strncmp_call, gimple_vuse (call_stmt));
+	      gimple_call_set_lhs (strncmp_call, strncmp_lhs);
 	      gsi_remove (&gsi, true);
-	      gsi_insert_before (&gsi, memcmp_call, GSI_SAME_STMT);
-	      tree zero = build_zero_cst (TREE_TYPE (memcmp_lhs));
+	      gsi_insert_before (&gsi, strncmp_call, GSI_SAME_STMT);
+	      tree zero = build_zero_cst (TREE_TYPE (strncmp_lhs));
 
 	      if (is_gimple_assign (stmt))
 		{
 		  if (gimple_assign_rhs_code (stmt) == COND_EXPR)
 		    {
 		      tree cond = gimple_assign_rhs1 (stmt);
-		      TREE_OPERAND (cond, 0) = memcmp_lhs;
+		      TREE_OPERAND (cond, 0) = strncmp_lhs;
 		      TREE_OPERAND (cond, 1) = zero;
 		    }
 		  else
 		    {
-		      gimple_assign_set_rhs1 (stmt, memcmp_lhs);
+		      gimple_assign_set_rhs1 (stmt, strncmp_lhs);
 		      gimple_assign_set_rhs2 (stmt, zero);
 		    }
 		}
 	      else
 		{
 		  gcond *cond = as_a<gcond *> (stmt);
-		  gimple_cond_set_lhs (cond, memcmp_lhs);
+		  gimple_cond_set_lhs (cond, strncmp_lhs);
 		  gimple_cond_set_rhs (cond, zero);
 		}
 	      update_stmt (stmt);
@@ -2398,12 +2398,12 @@ strlen_optimize_stmt (gimple_stmt_iterator *gsi)
 	    enum tree_code cond_code = TREE_CODE (cond);
 
 	    if (cond_code == EQ_EXPR || cond_code == NE_EXPR)
-	      fold_strstr_to_memcmp (TREE_OPERAND (cond, 0),
-				     TREE_OPERAND (cond, 1), stmt);
+	      fold_strstr_to_strncmp (TREE_OPERAND (cond, 0),
+				      TREE_OPERAND (cond, 1), stmt);
 	  }
 	else if (code == EQ_EXPR || code == NE_EXPR)
-	  fold_strstr_to_memcmp (gimple_assign_rhs1 (stmt),
-				 gimple_assign_rhs2 (stmt), stmt);
+	  fold_strstr_to_strncmp (gimple_assign_rhs1 (stmt),
+				  gimple_assign_rhs2 (stmt), stmt);
       }
     else if (TREE_CODE (lhs) != SSA_NAME && !TREE_SIDE_EFFECTS (lhs))
 	{
@@ -2423,8 +2423,8 @@ strlen_optimize_stmt (gimple_stmt_iterator *gsi)
     {
       enum tree_code code = gimple_cond_code (cond);
       if (code == EQ_EXPR || code == NE_EXPR)
-	fold_strstr_to_memcmp (gimple_cond_lhs (stmt),
-			       gimple_cond_rhs (stmt), stmt);
+	fold_strstr_to_strncmp (gimple_cond_lhs (stmt),
+				gimple_cond_rhs (stmt), stmt);
     }
 
   if (gimple_vdef (stmt))
-- 
2.11.0


^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH] Fix strstr folding (PR tree-optimization/79196).
  2017-01-23 14:14                         ` [PATCH] Fix strstr folding (PR tree-optimization/79196) Martin Liška
@ 2017-01-23 14:51                           ` Jakub Jelinek
  0 siblings, 0 replies; 18+ messages in thread
From: Jakub Jelinek @ 2017-01-23 14:51 UTC (permalink / raw)
  To: Martin Liška; +Cc: Prathamesh Kulkarni, gcc Patches, Richard Biener

On Mon, Jan 23, 2017 at 02:58:05PM +0100, Martin Liška wrote:
> As mentioned in the PR, it should be valid to use strncmp instead of memcmp
> because first argument of a strstr can be shorter than a second (known) argument.
> 
> Patch can bootstrap on ppc64le-redhat-linux and survives regression tests.
> 
> Ready to be installed?
> Martin

> >From 4a9dcb24d82ce1793a281710ce1ddab59f9dd7a8 Mon Sep 17 00:00:00 2001
> From: marxin <mliska@suse.cz>
> Date: Mon, 23 Jan 2017 13:18:37 +0100
> Subject: [PATCH] Fix strstr folding (PR tree-optimization/79196).
> 
> gcc/ChangeLog:
> 
> 2017-01-23  Martin Liska  <mliska@suse.cz>
> 
> 	* tree-ssa-strlen.c (fold_strstr_to_memcmp): Rename
> 	to fold_strstr_to_strncmp and fold the pattern to strncmp
> 	instead of memcmp.

Please write it as:
	PR tree-optimization/79196
	* tree-ssa-strlen.c (fold_strstr_to_memcmp): Rename to ...
	(fold_strstr_to_strncmp): ... this.  Fold the pattern to strncmp
	instead of memcmp.

> 	(strlen_optimize_stmt): Call the renamed function.
> 
> gcc/testsuite/ChangeLog:
> 
> 2017-01-23  Martin Liska  <mliska@suse.cz>
> 
> 	* gcc.dg/asan/pr79196.c: New test.
> 	* gcc.dg/strlenopt-30.c: Update scanned pattern.

Ok, thanks.

	Jakub

^ permalink raw reply	[flat|nested] 18+ messages in thread

end of thread, other threads:[~2017-01-23 14:15 UTC | newest]

Thread overview: 18+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-12-05 18:02 Fold strstr (s, t) eq/ne s to strcmp (s, t) eq/ne 0 if strlen (t) is known Prathamesh Kulkarni
2016-12-05 18:08 ` Bernd Schmidt
2016-12-05 18:10   ` Prathamesh Kulkarni
2016-12-05 18:14     ` Prathamesh Kulkarni
2016-12-05 18:17 ` Jakub Jelinek
2016-12-07 11:33   ` Prathamesh Kulkarni
2016-12-07 12:06     ` Jakub Jelinek
2016-12-09 12:06       ` Prathamesh Kulkarni
2016-12-09 12:29         ` Jakub Jelinek
2016-12-13  9:38           ` Prathamesh Kulkarni
2016-12-13  9:57             ` Jakub Jelinek
2016-12-13 12:11               ` Prathamesh Kulkarni
2016-12-13 12:24                 ` Jakub Jelinek
2016-12-14  8:03                   ` Prathamesh Kulkarni
2016-12-14  8:07                     ` Jakub Jelinek
2017-01-23 11:51                       ` Martin Liška
2017-01-23 14:14                         ` [PATCH] Fix strstr folding (PR tree-optimization/79196) Martin Liška
2017-01-23 14:51                           ` Jakub Jelinek

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).