This patch folds strchr (e, 0) to e + strlen (e), if e has no side-effects. Bootstrapped, regtested on x86_64-unknown-linux-gnu Ok for trunk ?
[gcc] * gcc/builtins.c (fold_builtin_strchr): returns tree for s1 + strlen (s1) if TREE_SIDE_EFFECTS (s1) is false and integer_zerop (s2) is true. [gcc/testsuite] * gcc.dg/strlenopt-5.c: modified dg-final scan-tree-dump-times for strchr and strlen * gcc.dg/strlenopt-7.c: Likewise * gcc.dg/strlenopt-9.c: Likewise * gcc.dg/strlenopt-20.c: Likewise * gcc.dg/strlenopt-21.c: Likewise * gcc.dg/strlenopt-22.c: Likewise * gcc.dg/strlenopt-22g.c: Likewise * gcc.dg/strlenopt-26.c: Likewise * gcc.c-torture/execute/builtins/strchr.c: added test case Thanks and Regards, Prathamesh
Index: gcc/builtins.c =================================================================== --- gcc/builtins.c (revision 207700) +++ gcc/builtins.c (working copy) @@ -11587,6 +11587,17 @@ fold_builtin_strchr (location_t loc, tre tem = fold_build_pointer_plus_hwi_loc (loc, s1, r - p1); return fold_convert_loc (loc, type, tem); } + else if (integer_zerop (s2) && !TREE_SIDE_EFFECTS (s1)) // simplify strchr (s1, '\0') to s1 + strlen (s1) + { + tree fn = builtin_decl_implicit (BUILT_IN_STRLEN); + + if (!fn) + return NULL_TREE; + + tree call_expr = build_call_expr_loc (loc, fn, 1, s1); + tree ptr_plus_expr = build2_loc (loc, POINTER_PLUS_EXPR, TREE_TYPE (s1), s1, call_expr); + return fold_convert_loc (loc, type, ptr_plus_expr); + } return NULL_TREE; } } Index: gcc/testsuite/gcc.dg/strlenopt-20.c =================================================================== --- gcc/testsuite/gcc.dg/strlenopt-20.c (revision 207700) +++ gcc/testsuite/gcc.dg/strlenopt-20.c (working copy) @@ -48,7 +48,7 @@ __attribute__((noinline, noclone)) char fn3 (char *p) { char *c; - /* The strcpy call can be optimized into memcpy, strchr needs to stay, + /* The strcpy call can be optimized into memcpy, strchr (p, '\0') is converted to p + strlen (p), strcat is optimized into memcpy. */ strcpy (p, "abc"); p[3] = 'd'; @@ -86,10 +86,10 @@ 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" } } */ /* { dg-final { cleanup-tree-dump "strlen" } } */ Index: gcc/testsuite/gcc.dg/strlenopt-21.c =================================================================== --- gcc/testsuite/gcc.dg/strlenopt-21.c (revision 207700) +++ gcc/testsuite/gcc.dg/strlenopt-21.c (working copy) @@ -10,6 +10,7 @@ foo (char *x, int n) { int i; char a[64]; + /* strlen (x, '\0') is converted to x + strlen (x) */ char *p = strchr (x, '\0'); struct S s; /* strcpy here is optimized into memcpy, length computed as p - x + 1. */ @@ -57,10 +58,10 @@ 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" } } */ /* { dg-final { cleanup-tree-dump "strlen" } } */ Index: gcc/testsuite/gcc.dg/strlenopt-22.c =================================================================== --- gcc/testsuite/gcc.dg/strlenopt-22.c (revision 207700) +++ gcc/testsuite/gcc.dg/strlenopt-22.c (working copy) @@ -9,7 +9,7 @@ bar (char *p, char *q) size_t l1, l2, l3; char *r = strchr (p, '\0'); strcpy (r, "abcde"); - char *s = strchr (r, '\0'); + char *s = strchr (r, '\0'); /* strchr (r, '\0') is converted to r + strlen (r) */ strcpy (s, q); l1 = strlen (p); l2 = strlen (r); @@ -31,10 +31,10 @@ 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 "stpcpy \\(" 0 "strlen" } } * +/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */ +/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen" } } */ /* { dg-final { cleanup-tree-dump "strlen" } } */ Index: gcc/testsuite/gcc.dg/strlenopt-22g.c =================================================================== --- gcc/testsuite/gcc.dg/strlenopt-22g.c (revision 207700) +++ gcc/testsuite/gcc.dg/strlenopt-22g.c (working copy) @@ -5,10 +5,10 @@ #define USE_GNU #include "strlenopt-22.c" -/* { 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 \\(" 1 "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 \\(" 1 "strlen" } } */ /* { dg-final { cleanup-tree-dump "strlen" } } */ Index: gcc/testsuite/gcc.dg/strlenopt-26.c =================================================================== --- gcc/testsuite/gcc.dg/strlenopt-26.c (revision 207700) +++ gcc/testsuite/gcc.dg/strlenopt-26.c (working copy) @@ -7,7 +7,7 @@ __attribute__((noinline, noclone)) size_ fn1 (char *p, const char *r) { size_t len1 = strlen (r); - char *q = strchr (p, '\0'); + char *q = strchr (p, '\0'); // strchr (p, '\0') is converted to p + strlen (p) *q = '\0'; return len1 - strlen (r); // This strlen should be optimized into len1. } @@ -21,5 +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 { cleanup-tree-dump "strlen" } } */ Index: gcc/testsuite/gcc.dg/strlenopt-5.c =================================================================== --- gcc/testsuite/gcc.dg/strlenopt-5.c (revision 207700) +++ gcc/testsuite/gcc.dg/strlenopt-5.c (working copy) @@ -6,7 +6,7 @@ __attribute__((noinline, noclone)) char * foo (char *p, const char *q) { - char *e = strchr (p, '\0'); + char *e = strchr (p, '\0'); /* strchr (p, '\0') is converted to p + strlen (p) */ strcat (p, q); return e; } @@ -21,7 +21,7 @@ bar (char *p) __attribute__((noinline, noclone)) void baz (char *p) { - char *e = strchr (p, '\0'); + char *e = strchr (p, '\0'); /* strchr (p, '\0') is converted to p + strlen (p) */ strcat (e, "abcd"); } @@ -48,10 +48,10 @@ 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" } } */ /* { dg-final { cleanup-tree-dump "strlen" } } */ Index: gcc/testsuite/gcc.dg/strlenopt-7.c =================================================================== --- gcc/testsuite/gcc.dg/strlenopt-7.c (revision 207700) +++ gcc/testsuite/gcc.dg/strlenopt-7.c (working copy) @@ -21,7 +21,7 @@ foo (void) __attribute__((noinline, noclone)) size_t bar (char *p) { - char *r = strchr (p, '\0'); + char *r = strchr (p, '\0'); /* strchr (p, '\0') is converted to p + strlen (p) */ /* This store shouldn't be optimized away, because we want to crash if p is e.g. a string literal. */ *r = '\0'; @@ -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 { cleanup-tree-dump "strlen" } } */ Index: gcc/testsuite/gcc.dg/strlenopt-9.c =================================================================== --- gcc/testsuite/gcc.dg/strlenopt-9.c (revision 207700) +++ gcc/testsuite/gcc.dg/strlenopt-9.c (working copy) @@ -7,8 +7,7 @@ __attribute__((noinline, noclone)) char fn1 (int r) { char *p = r ? "a" : "bc"; - /* String length for p varies, therefore strchr below isn't - optimized away. */ + /* strchr (p, '\0') is converted to p + strlen (p) */ return strchr (p, '\0'); } @@ -27,7 +26,7 @@ __attribute__((noinline, noclone)) size_ fn3 (char *p, int n) { int i; - p = strchr (p, '\0'); + p = strchr (p, '\0'); /* strchr (p, '\0') is converted to p + strlen (p) */ /* strcat here can be optimized into memcpy. */ strcat (p, "abcd"); for (i = 0; i < n; i++) @@ -47,7 +46,7 @@ fn4 (char *x, int n) int i; size_t l; char a[64]; - char *p = strchr (x, '\0'); + char *p = strchr (x, '\0'); /* strchr (x, '\0') is converted to x + strlen (x) */ /* strcpy here is optimized into memcpy, length computed as p - x + 1. */ strcpy (a, x); /* strcat here is optimized into memcpy. */ @@ -98,11 +97,11 @@ main () return 0; } -/* { dg-final { scan-tree-dump-times "strlen \\(" 4 "strlen" } } */ +/* { dg-final { scan-tree-dump-times "strlen \\(" 7 "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 { cleanup-tree-dump "strlen" } } */ /* { dg-final { scan-tree-dump-times "return 4;" 1 "optimized" } } */ Index: gcc/testsuite/gcc.c-torture/execute/builtins/strchr.c =================================================================== --- gcc/testsuite/gcc.c-torture/execute/builtins/strchr.c (revision 207700) +++ gcc/testsuite/gcc.c-torture/execute/builtins/strchr.c (working copy) @@ -9,6 +9,8 @@ extern void abort (void); extern char *strchr (const char *, int); extern char *index (const char *, int); +int x = 7; + void main_test (void) { @@ -22,6 +24,8 @@ main_test (void) abort (); if (strchr (foo, '\0') != foo + 11) abort (); + if (strchr (foo + (x++ & 3), '\0') != foo + 11) + abort (); /* Test only one instance of index since the code path is the same as that of strchr. */ if (index ("hello", 'z') != 0)