diff mbox

Optimize strchr (s, 0) to strlen

Message ID AM3PR08MB0088CA61259F65FAAB4D8196836B0@AM3PR08MB0088.eurprd08.prod.outlook.com
State New
Headers show

Commit Message

Wilco Dijkstra April 18, 2016, 5 p.m. UTC
Optimize strchr (s, 0) to s + strlen (s).  strchr (s, 0) appears a common
idiom for finding the end of a string, however it is not a very efficient
way of doing so.  Strlen is a much simpler operation which is significantly
faster (eg. on x86 strlen is 50% faster for strings of 8 bytes and about
twice as fast as strchr on strings of 1KB).

OK for trunk?

ChangeLog:
2016-04-18  Wilco Dijkstra  <wdijkstr@arm.com>

gcc/
	* gcc/builtins.c (fold_builtin_strchr): Optimize strchr (s, 0) into
	strlen.

testsuite/
	* gcc/testsuite/gcc.dg/strlenopt-20.c: Update test.
	* gcc/testsuite/gcc.dg/strlenopt-21.c: Likewise.
	* gcc/testsuite/gcc.dg/strlenopt-22.c: Likewise.
	* gcc/testsuite/gcc.dg/strlenopt-26.c: Likewise.
	* gcc/testsuite/gcc.dg/strlenopt-5.c: Likewise.
	* gcc/testsuite/gcc.dg/strlenopt-7.c: Likewise.
	* gcc/testsuite/gcc.dg/strlenopt-9.c: Likewise.

--

Comments

Jakub Jelinek April 18, 2016, 5:09 p.m. UTC | #1
On Mon, Apr 18, 2016 at 05:00:45PM +0000, Wilco Dijkstra wrote:
> Optimize strchr (s, 0) to s + strlen (s).  strchr (s, 0) appears a common
> idiom for finding the end of a string, however it is not a very efficient
> way of doing so.  Strlen is a much simpler operation which is significantly
> faster (eg. on x86 strlen is 50% faster for strings of 8 bytes and about
> twice as fast as strchr on strings of 1KB).

That is generally a bad idea, it really depends on the target.
So, perhaps such decision should be done on a case by case basis,
and certainly not this early.
Often strchr (s, 0) should be just optimized as rawmemchr (s, 0) instead.

	Jakub
Wilco Dijkstra April 18, 2016, 5:30 p.m. UTC | #2
Jakub Jelinek <jakub@redhat.com> wrote:
> On Mon, Apr 18, 2016 at 05:00:45PM +0000, Wilco Dijkstra wrote:
>> Optimize strchr (s, 0) to s + strlen (s).  strchr (s, 0) appears a common
>> idiom for finding the end of a string, however it is not a very efficient
>> way of doing so.  Strlen is a much simpler operation which is significantly
>> faster (eg. on x86 strlen is 50% faster for strings of 8 bytes and about
>> twice as fast as strchr on strings of 1KB).
>
> That is generally a bad idea, it really depends on the target.
> So, perhaps such decision should be done on a case by case basis,
> and certainly not this early.
> Often strchr (s, 0) should be just optimized as rawmemchr (s, 0) instead.

I don't see how it would depend on the target - under what circumstances could
strchr possibly be faster than strlen? The former must do 2 comparisons per 
character - the latter only 1.

Note rawmemchr is a non-standard function, ie. GCC would need to be told
whether the target library supports it, so it would require new builtins and
infrastructure.

Also rawmemchr is significantly slower than strlen on most targets, even when
an assembly implementation is available. That's why I posted this patch for GLIBC:
https://sourceware.org/ml/libc-alpha/2016-04/msg00382.html

Wilco
Oleg Endo April 18, 2016, 10:55 p.m. UTC | #3
On Mon, 2016-04-18 at 17:00 +0000, Wilco Dijkstra wrote:
> Optimize strchr (s, 0) to s + strlen (s).  strchr (s, 0) appears a
> common
> idiom for finding the end of a string, however it is not a very
> efficient
> way of doing so.  Strlen is a much simpler operation which is
> significantly
> faster (eg. on x86 strlen is 50% faster for strings of 8 bytes and
> about
> twice as fast as strchr on strings of 1KB).
> 
> OK for trunk?

Can you please file this as PR 61056?

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61056


Cheers,
Oleg


> 
> ChangeLog:
> 2016-04-18  Wilco Dijkstra  <wdijkstr@arm.com>
> 
> gcc/
> 	* gcc/builtins.c (fold_builtin_strchr): Optimize strchr (s, 0)
> into
> 	strlen.
> 
> testsuite/
> 	* gcc/testsuite/gcc.dg/strlenopt-20.c: Update test.
> 	* gcc/testsuite/gcc.dg/strlenopt-21.c: Likewise.
> 	* gcc/testsuite/gcc.dg/strlenopt-22.c: Likewise.
> 	* gcc/testsuite/gcc.dg/strlenopt-26.c: Likewise.
> 	* gcc/testsuite/gcc.dg/strlenopt-5.c: Likewise.
> 	* gcc/testsuite/gcc.dg/strlenopt-7.c: Likewise.
> 	* gcc/testsuite/gcc.dg/strlenopt-9.c: Likewise.
> 
> --
> 
> diff --git a/gcc/builtins.c b/gcc/builtins.c
> index
> 058ecc39aab205099713e503861103ce6ba5ee6d..150e707178a3e119d42ef630b38
> 4da3eaf7b2182 100644
> --- a/gcc/builtins.c
> +++ b/gcc/builtins.c
> @@ -8567,20 +8567,20 @@ fold_builtin_strchr (location_t loc, tree s1,
> tree s2, tree type)
>    else
>      {
>        const char *p1;
> +      char c;
>  
>        if (TREE_CODE (s2) != INTEGER_CST)
>  	return NULL_TREE;
>  
> +      if (target_char_cast (s2, &c))
> +	return NULL_TREE;
> +
>        p1 = c_getstr (s1);
>        if (p1 != NULL)
>  	{
> -	  char c;
>  	  const char *r;
>  	  tree tem;
>  
> -	  if (target_char_cast (s2, &c))
> -	    return NULL_TREE;
> -
>  	  r = strchr (p1, c);
>  
>  	  if (r == NULL)
> @@ -8590,6 +8590,20 @@ fold_builtin_strchr (location_t loc, tree s1,
> tree s2, tree type)
>  	  tem = fold_build_pointer_plus_hwi_loc (loc, s1, r - p1);
>  	  return fold_convert_loc (loc, type, tem);
>  	}
> +      else if (c == 0)
> +	{
> +	  tree fn = builtin_decl_implicit (BUILT_IN_STRLEN);
> +	  if (!fn)
> +	    return NULL_TREE;
> +
> +	  s1 = builtin_save_expr (s1);
> +
> +	  /* Transform strchr (s1, '\0') to s1 + strlen (s1).  */
> +	  fn = build_call_expr_loc (loc, fn, 1, s1);
> +	  tree tem = fold_build_pointer_plus (s1, fn);
> +	  return fold_convert_loc (loc, type, tem);
> +	}
> +
>        return NULL_TREE;
>      }
>  }
> diff --git a/gcc/testsuite/gcc.dg/strlenopt-20.c
> b/gcc/testsuite/gcc.dg/strlenopt-20.c
> index
> a83e845c26d88e5acdcabf142f7b319136663488..7b483eaeac1aa47278111a92148
> a16f00b2aaa2d 100644
> --- a/gcc/testsuite/gcc.dg/strlenopt-20.c
> +++ b/gcc/testsuite/gcc.dg/strlenopt-20.c
> @@ -86,9 +86,9 @@ main ()
>    return 0;
>  }
>  
> -/* { dg-final { scan-tree-dump-times "strlen \\(" 1 "strlen" } } */
> +/* { dg-final { scan-tree-dump-times "strlen \\(" 2 "strlen" } } */
>  /* { dg-final { scan-tree-dump-times "memcpy \\(" 4 "strlen" } } */
>  /* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen" } } */
>  /* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen" } } */
> -/* { dg-final { scan-tree-dump-times "strchr \\(" 1 "strlen" } } */
> +/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */
>  /* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen" } } */
> diff --git a/gcc/testsuite/gcc.dg/strlenopt-21.c
> b/gcc/testsuite/gcc.dg/strlenopt-21.c
> index
> e22fa9fca9ba14354db2cd5f602283b64bd8dcac..05b85a49dde0a7f5d269174fd42
> 69e40be910dbd 100644
> --- a/gcc/testsuite/gcc.dg/strlenopt-21.c
> +++ b/gcc/testsuite/gcc.dg/strlenopt-21.c
> @@ -57,9 +57,9 @@ main ()
>    return 0;
>  }
>  
> -/* { dg-final { scan-tree-dump-times "strlen \\(" 1 "strlen" } } */
> +/* { dg-final { scan-tree-dump-times "strlen \\(" 2 "strlen" } } */
>  /* { dg-final { scan-tree-dump-times "memcpy \\(" 3 "strlen" } } */
>  /* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen" } } */
>  /* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen" } } */
> -/* { dg-final { scan-tree-dump-times "strchr \\(" 1 "strlen" } } */
> +/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */
>  /* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen" } } */
> diff --git a/gcc/testsuite/gcc.dg/strlenopt-22.c
> b/gcc/testsuite/gcc.dg/strlenopt-22.c
> index
> aa55f5ebd6a2d4803ee9a7fd60fc538d86f47124..b4ef772f0e59252f10a5419ede6
> 837b3c8ca8265 100644
> --- a/gcc/testsuite/gcc.dg/strlenopt-22.c
> +++ b/gcc/testsuite/gcc.dg/strlenopt-22.c
> @@ -31,9 +31,9 @@ main ()
>    return 0;
>  }
>  
> -/* { dg-final { scan-tree-dump-times "strlen \\(" 3 "strlen" } } */
> +/* { dg-final { scan-tree-dump-times "strlen \\(" 4 "strlen" } } */
>  /* { dg-final { scan-tree-dump-times "memcpy \\(" 1 "strlen" } } */
>  /* { dg-final { scan-tree-dump-times "strcpy \\(" 1 "strlen" } } */
>  /* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen" } } */
> -/* { dg-final { scan-tree-dump-times "strchr \\(" 1 "strlen" } } */
> +/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */
>  /* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen" } } */
> diff --git a/gcc/testsuite/gcc.dg/strlenopt-26.c
> b/gcc/testsuite/gcc.dg/strlenopt-26.c
> index
> 4bd54bef540806ca90d2b9bcfc33eb531991c967..da2f465a5b5003fa5dca05f3a6e
> e00e97b98b5dd 100644
> --- a/gcc/testsuite/gcc.dg/strlenopt-26.c
> +++ b/gcc/testsuite/gcc.dg/strlenopt-26.c
> @@ -21,4 +21,5 @@ main (void)
>    return 0;
>  }
>  
> -/* { dg-final { scan-tree-dump-times "strlen \\(" 1 "strlen" } } */
> +/* { dg-final { scan-tree-dump-times "strlen \\(" 2 "strlen" } } */
> +/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */
> diff --git a/gcc/testsuite/gcc.dg/strlenopt-5.c
> b/gcc/testsuite/gcc.dg/strlenopt-5.c
> index
> 1b006a93045599a75bdb10a39e86ffa59b475c83..a24aea44e8b00ff7b35a907aaa9
> 41b4c509642c4 100644
> --- a/gcc/testsuite/gcc.dg/strlenopt-5.c
> +++ b/gcc/testsuite/gcc.dg/strlenopt-5.c
> @@ -48,9 +48,9 @@ main ()
>    return 0;
>  }
>  
> -/* { dg-final { scan-tree-dump-times "strlen \\(" 0 "strlen" } } */
> +/* { dg-final { scan-tree-dump-times "strlen \\(" 2 "strlen" } } */
>  /* { dg-final { scan-tree-dump-times "memcpy \\(" 2 "strlen" } } */
>  /* { dg-final { scan-tree-dump-times "strcpy \\(" 1 "strlen" } } */
>  /* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen" } } */
> -/* { dg-final { scan-tree-dump-times "strchr \\(" 2 "strlen" } } */
> +/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */
>  /* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen" } } */
> diff --git a/gcc/testsuite/gcc.dg/strlenopt-7.c
> b/gcc/testsuite/gcc.dg/strlenopt-7.c
> index
> 3ae1e2cb3f0d08c8b05107c4e65b67bdb39cf7ab..aa53d7e75254dfe56c93172afc4
> 9f95e5b7901e6 100644
> --- a/gcc/testsuite/gcc.dg/strlenopt-7.c
> +++ b/gcc/testsuite/gcc.dg/strlenopt-7.c
> @@ -40,11 +40,11 @@ main ()
>    return 0;
>  }
>  
> -/* { dg-final { scan-tree-dump-times "strlen \\(" 0 "strlen" } } */
> +/* { dg-final { scan-tree-dump-times "strlen \\(" 1 "strlen" } } */
>  /* { dg-final { scan-tree-dump-times "memcpy \\(" 2 "strlen" } } */
>  /* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen" } } */
>  /* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen" } } */
> -/* { dg-final { scan-tree-dump-times "strchr \\(" 1 "strlen" } } */
> +/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */
>  /* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen" } } */
>  /* { dg-final { scan-tree-dump-times "\\*r_\[0-9\]* = 0;" 1 "strlen"
> } } */
>  /* { dg-final { scan-tree-dump-times "return 3;" 1 "optimized" } }
> */
> diff --git a/gcc/testsuite/gcc.dg/strlenopt-9.c
> b/gcc/testsuite/gcc.dg/strlenopt-9.c
> index
> b0406b162d48fca375883035043b0c50b9db61a1..e5e276210ba4b7d75867605f1ec
> f5c06eb970ef5 100644
> --- a/gcc/testsuite/gcc.dg/strlenopt-9.c
> +++ b/gcc/testsuite/gcc.dg/strlenopt-9.c
> @@ -98,10 +98,10 @@ main ()
>    return 0;
>  }
>  
> -/* { dg-final { scan-tree-dump-times "strlen \\(" 3 "strlen" } } */
> +/* { dg-final { scan-tree-dump-times "strlen \\(" 5 "strlen" } } */
>  /* { dg-final { scan-tree-dump-times "memcpy \\(" 6 "strlen" } } */
>  /* { dg-final { scan-tree-dump-times "strcpy \\(" 1 "strlen" } } */
>  /* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen" } } */
> -/* { dg-final { scan-tree-dump-times "strchr \\(" 3 "strlen" } } */
> +/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */
>  /* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen" } } */
>  /* { dg-final { scan-tree-dump-times "return 4;" 1 "optimized" } }
> */
>
Richard Biener April 19, 2016, 8:13 a.m. UTC | #4
On Mon, Apr 18, 2016 at 7:00 PM, Wilco Dijkstra <Wilco.Dijkstra@arm.com> wrote:
> Optimize strchr (s, 0) to s + strlen (s).  strchr (s, 0) appears a common
> idiom for finding the end of a string, however it is not a very efficient
> way of doing so.  Strlen is a much simpler operation which is significantly
> faster (eg. on x86 strlen is 50% faster for strings of 8 bytes and about
> twice as fast as strchr on strings of 1KB).
>
> OK for trunk?

This folding should be added to gimple-fold.c:gimple_fold_builtin instead,
the builtins.c foldings are purerly for folding to constants nowadays.

Richard.

> ChangeLog:
> 2016-04-18  Wilco Dijkstra  <wdijkstr@arm.com>
>
> gcc/
>         * gcc/builtins.c (fold_builtin_strchr): Optimize strchr (s, 0) into
>         strlen.
>
> testsuite/
>         * gcc/testsuite/gcc.dg/strlenopt-20.c: Update test.
>         * gcc/testsuite/gcc.dg/strlenopt-21.c: Likewise.
>         * gcc/testsuite/gcc.dg/strlenopt-22.c: Likewise.
>         * gcc/testsuite/gcc.dg/strlenopt-26.c: Likewise.
>         * gcc/testsuite/gcc.dg/strlenopt-5.c: Likewise.
>         * gcc/testsuite/gcc.dg/strlenopt-7.c: Likewise.
>         * gcc/testsuite/gcc.dg/strlenopt-9.c: Likewise.
>
> --
>
> diff --git a/gcc/builtins.c b/gcc/builtins.c
> index 058ecc39aab205099713e503861103ce6ba5ee6d..150e707178a3e119d42ef630b384da3eaf7b2182 100644
> --- a/gcc/builtins.c
> +++ b/gcc/builtins.c
> @@ -8567,20 +8567,20 @@ fold_builtin_strchr (location_t loc, tree s1, tree s2, tree type)
>    else
>      {
>        const char *p1;
> +      char c;
>
>        if (TREE_CODE (s2) != INTEGER_CST)
>         return NULL_TREE;
>
> +      if (target_char_cast (s2, &c))
> +       return NULL_TREE;
> +
>        p1 = c_getstr (s1);
>        if (p1 != NULL)
>         {
> -         char c;
>           const char *r;
>           tree tem;
>
> -         if (target_char_cast (s2, &c))
> -           return NULL_TREE;
> -
>           r = strchr (p1, c);
>
>           if (r == NULL)
> @@ -8590,6 +8590,20 @@ fold_builtin_strchr (location_t loc, tree s1, tree s2, tree type)
>           tem = fold_build_pointer_plus_hwi_loc (loc, s1, r - p1);
>           return fold_convert_loc (loc, type, tem);
>         }
> +      else if (c == 0)
> +       {
> +         tree fn = builtin_decl_implicit (BUILT_IN_STRLEN);
> +         if (!fn)
> +           return NULL_TREE;
> +
> +         s1 = builtin_save_expr (s1);
> +
> +         /* Transform strchr (s1, '\0') to s1 + strlen (s1).  */
> +         fn = build_call_expr_loc (loc, fn, 1, s1);
> +         tree tem = fold_build_pointer_plus (s1, fn);
> +         return fold_convert_loc (loc, type, tem);
> +       }
> +
>        return NULL_TREE;
>      }
>  }
> diff --git a/gcc/testsuite/gcc.dg/strlenopt-20.c b/gcc/testsuite/gcc.dg/strlenopt-20.c
> index a83e845c26d88e5acdcabf142f7b319136663488..7b483eaeac1aa47278111a92148a16f00b2aaa2d 100644
> --- a/gcc/testsuite/gcc.dg/strlenopt-20.c
> +++ b/gcc/testsuite/gcc.dg/strlenopt-20.c
> @@ -86,9 +86,9 @@ main ()
>    return 0;
>  }
>
> -/* { dg-final { scan-tree-dump-times "strlen \\(" 1 "strlen" } } */
> +/* { dg-final { scan-tree-dump-times "strlen \\(" 2 "strlen" } } */
>  /* { dg-final { scan-tree-dump-times "memcpy \\(" 4 "strlen" } } */
>  /* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen" } } */
>  /* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen" } } */
> -/* { dg-final { scan-tree-dump-times "strchr \\(" 1 "strlen" } } */
> +/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */
>  /* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen" } } */
> diff --git a/gcc/testsuite/gcc.dg/strlenopt-21.c b/gcc/testsuite/gcc.dg/strlenopt-21.c
> index e22fa9fca9ba14354db2cd5f602283b64bd8dcac..05b85a49dde0a7f5d269174fd4269e40be910dbd 100644
> --- a/gcc/testsuite/gcc.dg/strlenopt-21.c
> +++ b/gcc/testsuite/gcc.dg/strlenopt-21.c
> @@ -57,9 +57,9 @@ main ()
>    return 0;
>  }
>
> -/* { dg-final { scan-tree-dump-times "strlen \\(" 1 "strlen" } } */
> +/* { dg-final { scan-tree-dump-times "strlen \\(" 2 "strlen" } } */
>  /* { dg-final { scan-tree-dump-times "memcpy \\(" 3 "strlen" } } */
>  /* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen" } } */
>  /* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen" } } */
> -/* { dg-final { scan-tree-dump-times "strchr \\(" 1 "strlen" } } */
> +/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */
>  /* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen" } } */
> diff --git a/gcc/testsuite/gcc.dg/strlenopt-22.c b/gcc/testsuite/gcc.dg/strlenopt-22.c
> index aa55f5ebd6a2d4803ee9a7fd60fc538d86f47124..b4ef772f0e59252f10a5419ede6837b3c8ca8265 100644
> --- a/gcc/testsuite/gcc.dg/strlenopt-22.c
> +++ b/gcc/testsuite/gcc.dg/strlenopt-22.c
> @@ -31,9 +31,9 @@ main ()
>    return 0;
>  }
>
> -/* { dg-final { scan-tree-dump-times "strlen \\(" 3 "strlen" } } */
> +/* { dg-final { scan-tree-dump-times "strlen \\(" 4 "strlen" } } */
>  /* { dg-final { scan-tree-dump-times "memcpy \\(" 1 "strlen" } } */
>  /* { dg-final { scan-tree-dump-times "strcpy \\(" 1 "strlen" } } */
>  /* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen" } } */
> -/* { dg-final { scan-tree-dump-times "strchr \\(" 1 "strlen" } } */
> +/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */
>  /* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen" } } */
> diff --git a/gcc/testsuite/gcc.dg/strlenopt-26.c b/gcc/testsuite/gcc.dg/strlenopt-26.c
> index 4bd54bef540806ca90d2b9bcfc33eb531991c967..da2f465a5b5003fa5dca05f3a6ee00e97b98b5dd 100644
> --- a/gcc/testsuite/gcc.dg/strlenopt-26.c
> +++ b/gcc/testsuite/gcc.dg/strlenopt-26.c
> @@ -21,4 +21,5 @@ main (void)
>    return 0;
>  }
>
> -/* { dg-final { scan-tree-dump-times "strlen \\(" 1 "strlen" } } */
> +/* { dg-final { scan-tree-dump-times "strlen \\(" 2 "strlen" } } */
> +/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */
> diff --git a/gcc/testsuite/gcc.dg/strlenopt-5.c b/gcc/testsuite/gcc.dg/strlenopt-5.c
> index 1b006a93045599a75bdb10a39e86ffa59b475c83..a24aea44e8b00ff7b35a907aaa941b4c509642c4 100644
> --- a/gcc/testsuite/gcc.dg/strlenopt-5.c
> +++ b/gcc/testsuite/gcc.dg/strlenopt-5.c
> @@ -48,9 +48,9 @@ main ()
>    return 0;
>  }
>
> -/* { dg-final { scan-tree-dump-times "strlen \\(" 0 "strlen" } } */
> +/* { dg-final { scan-tree-dump-times "strlen \\(" 2 "strlen" } } */
>  /* { dg-final { scan-tree-dump-times "memcpy \\(" 2 "strlen" } } */
>  /* { dg-final { scan-tree-dump-times "strcpy \\(" 1 "strlen" } } */
>  /* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen" } } */
> -/* { dg-final { scan-tree-dump-times "strchr \\(" 2 "strlen" } } */
> +/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */
>  /* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen" } } */
> diff --git a/gcc/testsuite/gcc.dg/strlenopt-7.c b/gcc/testsuite/gcc.dg/strlenopt-7.c
> index 3ae1e2cb3f0d08c8b05107c4e65b67bdb39cf7ab..aa53d7e75254dfe56c93172afc49f95e5b7901e6 100644
> --- a/gcc/testsuite/gcc.dg/strlenopt-7.c
> +++ b/gcc/testsuite/gcc.dg/strlenopt-7.c
> @@ -40,11 +40,11 @@ main ()
>    return 0;
>  }
>
> -/* { dg-final { scan-tree-dump-times "strlen \\(" 0 "strlen" } } */
> +/* { dg-final { scan-tree-dump-times "strlen \\(" 1 "strlen" } } */
>  /* { dg-final { scan-tree-dump-times "memcpy \\(" 2 "strlen" } } */
>  /* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen" } } */
>  /* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen" } } */
> -/* { dg-final { scan-tree-dump-times "strchr \\(" 1 "strlen" } } */
> +/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */
>  /* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen" } } */
>  /* { dg-final { scan-tree-dump-times "\\*r_\[0-9\]* = 0;" 1 "strlen" } } */
>  /* { dg-final { scan-tree-dump-times "return 3;" 1 "optimized" } } */
> diff --git a/gcc/testsuite/gcc.dg/strlenopt-9.c b/gcc/testsuite/gcc.dg/strlenopt-9.c
> index b0406b162d48fca375883035043b0c50b9db61a1..e5e276210ba4b7d75867605f1ecf5c06eb970ef5 100644
> --- a/gcc/testsuite/gcc.dg/strlenopt-9.c
> +++ b/gcc/testsuite/gcc.dg/strlenopt-9.c
> @@ -98,10 +98,10 @@ main ()
>    return 0;
>  }
>
> -/* { dg-final { scan-tree-dump-times "strlen \\(" 3 "strlen" } } */
> +/* { dg-final { scan-tree-dump-times "strlen \\(" 5 "strlen" } } */
>  /* { dg-final { scan-tree-dump-times "memcpy \\(" 6 "strlen" } } */
>  /* { dg-final { scan-tree-dump-times "strcpy \\(" 1 "strlen" } } */
>  /* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen" } } */
> -/* { dg-final { scan-tree-dump-times "strchr \\(" 3 "strlen" } } */
> +/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */
>  /* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen" } } */
>  /* { dg-final { scan-tree-dump-times "return 4;" 1 "optimized" } } */
>
diff mbox

Patch

diff --git a/gcc/builtins.c b/gcc/builtins.c
index 058ecc39aab205099713e503861103ce6ba5ee6d..150e707178a3e119d42ef630b384da3eaf7b2182 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -8567,20 +8567,20 @@  fold_builtin_strchr (location_t loc, tree s1, tree s2, tree type)
   else
     {
       const char *p1;
+      char c;
 
       if (TREE_CODE (s2) != INTEGER_CST)
 	return NULL_TREE;
 
+      if (target_char_cast (s2, &c))
+	return NULL_TREE;
+
       p1 = c_getstr (s1);
       if (p1 != NULL)
 	{
-	  char c;
 	  const char *r;
 	  tree tem;
 
-	  if (target_char_cast (s2, &c))
-	    return NULL_TREE;
-
 	  r = strchr (p1, c);
 
 	  if (r == NULL)
@@ -8590,6 +8590,20 @@  fold_builtin_strchr (location_t loc, tree s1, tree s2, tree type)
 	  tem = fold_build_pointer_plus_hwi_loc (loc, s1, r - p1);
 	  return fold_convert_loc (loc, type, tem);
 	}
+      else if (c == 0)
+	{
+	  tree fn = builtin_decl_implicit (BUILT_IN_STRLEN);
+	  if (!fn)
+	    return NULL_TREE;
+
+	  s1 = builtin_save_expr (s1);
+
+	  /* Transform strchr (s1, '\0') to s1 + strlen (s1).  */
+	  fn = build_call_expr_loc (loc, fn, 1, s1);
+	  tree tem = fold_build_pointer_plus (s1, fn);
+	  return fold_convert_loc (loc, type, tem);
+	}
+
       return NULL_TREE;
     }
 }
diff --git a/gcc/testsuite/gcc.dg/strlenopt-20.c b/gcc/testsuite/gcc.dg/strlenopt-20.c
index a83e845c26d88e5acdcabf142f7b319136663488..7b483eaeac1aa47278111a92148a16f00b2aaa2d 100644
--- a/gcc/testsuite/gcc.dg/strlenopt-20.c
+++ b/gcc/testsuite/gcc.dg/strlenopt-20.c
@@ -86,9 +86,9 @@  main ()
   return 0;
 }
 
-/* { dg-final { scan-tree-dump-times "strlen \\(" 1 "strlen" } } */
+/* { dg-final { scan-tree-dump-times "strlen \\(" 2 "strlen" } } */
 /* { dg-final { scan-tree-dump-times "memcpy \\(" 4 "strlen" } } */
 /* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen" } } */
 /* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "strchr \\(" 1 "strlen" } } */
+/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */
 /* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-21.c b/gcc/testsuite/gcc.dg/strlenopt-21.c
index e22fa9fca9ba14354db2cd5f602283b64bd8dcac..05b85a49dde0a7f5d269174fd4269e40be910dbd 100644
--- a/gcc/testsuite/gcc.dg/strlenopt-21.c
+++ b/gcc/testsuite/gcc.dg/strlenopt-21.c
@@ -57,9 +57,9 @@  main ()
   return 0;
 }
 
-/* { dg-final { scan-tree-dump-times "strlen \\(" 1 "strlen" } } */
+/* { dg-final { scan-tree-dump-times "strlen \\(" 2 "strlen" } } */
 /* { dg-final { scan-tree-dump-times "memcpy \\(" 3 "strlen" } } */
 /* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen" } } */
 /* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "strchr \\(" 1 "strlen" } } */
+/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */
 /* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-22.c b/gcc/testsuite/gcc.dg/strlenopt-22.c
index aa55f5ebd6a2d4803ee9a7fd60fc538d86f47124..b4ef772f0e59252f10a5419ede6837b3c8ca8265 100644
--- a/gcc/testsuite/gcc.dg/strlenopt-22.c
+++ b/gcc/testsuite/gcc.dg/strlenopt-22.c
@@ -31,9 +31,9 @@  main ()
   return 0;
 }
 
-/* { dg-final { scan-tree-dump-times "strlen \\(" 3 "strlen" } } */
+/* { dg-final { scan-tree-dump-times "strlen \\(" 4 "strlen" } } */
 /* { dg-final { scan-tree-dump-times "memcpy \\(" 1 "strlen" } } */
 /* { dg-final { scan-tree-dump-times "strcpy \\(" 1 "strlen" } } */
 /* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "strchr \\(" 1 "strlen" } } */
+/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */
 /* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-26.c b/gcc/testsuite/gcc.dg/strlenopt-26.c
index 4bd54bef540806ca90d2b9bcfc33eb531991c967..da2f465a5b5003fa5dca05f3a6ee00e97b98b5dd 100644
--- a/gcc/testsuite/gcc.dg/strlenopt-26.c
+++ b/gcc/testsuite/gcc.dg/strlenopt-26.c
@@ -21,4 +21,5 @@  main (void)
   return 0;
 }
 
-/* { dg-final { scan-tree-dump-times "strlen \\(" 1 "strlen" } } */
+/* { dg-final { scan-tree-dump-times "strlen \\(" 2 "strlen" } } */
+/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-5.c b/gcc/testsuite/gcc.dg/strlenopt-5.c
index 1b006a93045599a75bdb10a39e86ffa59b475c83..a24aea44e8b00ff7b35a907aaa941b4c509642c4 100644
--- a/gcc/testsuite/gcc.dg/strlenopt-5.c
+++ b/gcc/testsuite/gcc.dg/strlenopt-5.c
@@ -48,9 +48,9 @@  main ()
   return 0;
 }
 
-/* { dg-final { scan-tree-dump-times "strlen \\(" 0 "strlen" } } */
+/* { dg-final { scan-tree-dump-times "strlen \\(" 2 "strlen" } } */
 /* { dg-final { scan-tree-dump-times "memcpy \\(" 2 "strlen" } } */
 /* { dg-final { scan-tree-dump-times "strcpy \\(" 1 "strlen" } } */
 /* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "strchr \\(" 2 "strlen" } } */
+/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */
 /* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-7.c b/gcc/testsuite/gcc.dg/strlenopt-7.c
index 3ae1e2cb3f0d08c8b05107c4e65b67bdb39cf7ab..aa53d7e75254dfe56c93172afc49f95e5b7901e6 100644
--- a/gcc/testsuite/gcc.dg/strlenopt-7.c
+++ b/gcc/testsuite/gcc.dg/strlenopt-7.c
@@ -40,11 +40,11 @@  main ()
   return 0;
 }
 
-/* { dg-final { scan-tree-dump-times "strlen \\(" 0 "strlen" } } */
+/* { dg-final { scan-tree-dump-times "strlen \\(" 1 "strlen" } } */
 /* { dg-final { scan-tree-dump-times "memcpy \\(" 2 "strlen" } } */
 /* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen" } } */
 /* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "strchr \\(" 1 "strlen" } } */
+/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */
 /* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen" } } */
 /* { dg-final { scan-tree-dump-times "\\*r_\[0-9\]* = 0;" 1 "strlen" } } */
 /* { dg-final { scan-tree-dump-times "return 3;" 1 "optimized" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-9.c b/gcc/testsuite/gcc.dg/strlenopt-9.c
index b0406b162d48fca375883035043b0c50b9db61a1..e5e276210ba4b7d75867605f1ecf5c06eb970ef5 100644
--- a/gcc/testsuite/gcc.dg/strlenopt-9.c
+++ b/gcc/testsuite/gcc.dg/strlenopt-9.c
@@ -98,10 +98,10 @@  main ()
   return 0;
 }
 
-/* { dg-final { scan-tree-dump-times "strlen \\(" 3 "strlen" } } */
+/* { dg-final { scan-tree-dump-times "strlen \\(" 5 "strlen" } } */
 /* { dg-final { scan-tree-dump-times "memcpy \\(" 6 "strlen" } } */
 /* { dg-final { scan-tree-dump-times "strcpy \\(" 1 "strlen" } } */
 /* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "strchr \\(" 3 "strlen" } } */
+/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */
 /* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen" } } */
 /* { dg-final { scan-tree-dump-times "return 4;" 1 "optimized" } } */