Hi, the attached patch fixes all the strlenopt failures on s390x (without nuking the strcat folding).
The one case I couldn't get working so far is the second strlen in: __attribute__((noinline, noclone)) size_t bar (char *p, char *q) { char *r; size_t l1, l2; r = strchr (p, '\0'); strcpy (r, q); l1 = strlen (p); strcpy (r, "567"); l2 = strlen (p); return l1 + l2; } Perhaps this could be fixed by putting a stmt_addend value into the strinfo structs. Bootstrapped on x86_64 and s390x. No regressions. Ok for mainline? Bye, -Andreas- 2011-10-24 Andreas Krebbel <andreas.kreb...@de.ibm.com> * tree-ssa-strlen.c (get_string_length): Change assertion to STPCPY. (zero_length_string): Change assertion to accept strinfo without length but with stmt instead. Set the endptr pointer also if starting a new chain. (adjust_related_strinfos): Ignore strinfos marked for delayed length computation. (handle_builtin_strcpy): Mark earlier strinfo elements also for delayed length computation. 2011-10-24 Andreas Krebbel <andreas.kreb...@de.ibm.com> * gcc.dg/strlenopt-22.c: New testcase. * gcc.dg/strlenopt-4.c: Change scan value for s390(x). Index: gcc/tree-ssa-strlen.c =================================================================== *** gcc/tree-ssa-strlen.c.orig --- gcc/tree-ssa-strlen.c *************** get_string_length (strinfo si) *** 397,403 **** callee = gimple_call_fndecl (stmt); gcc_assert (callee && DECL_BUILT_IN_CLASS (callee) == BUILT_IN_NORMAL); lhs = gimple_call_lhs (stmt); ! gcc_assert (builtin_decl_implicit_p (BUILT_IN_STRCPY)); /* unshare_strinfo is intentionally not called here. The (delayed) transformation of strcpy or strcat into stpcpy is done at the place of the former strcpy/strcat call and so can affect all the strinfos --- 397,403 ---- callee = gimple_call_fndecl (stmt); gcc_assert (callee && DECL_BUILT_IN_CLASS (callee) == BUILT_IN_NORMAL); lhs = gimple_call_lhs (stmt); ! gcc_assert (builtin_decl_implicit_p (BUILT_IN_STPCPY)); /* unshare_strinfo is intentionally not called here. The (delayed) transformation of strcpy or strcat into stpcpy is done at the place of the former strcpy/strcat call and so can affect all the strinfos *************** zero_length_string (tree ptr, strinfo ch *** 588,600 **** || si->prev != chainsi->idx) break; } ! gcc_assert (chainsi->length); if (chainsi->endptr == NULL_TREE) { chainsi = unshare_strinfo (chainsi); chainsi->endptr = ptr; } ! if (integer_zerop (chainsi->length)) { if (chainsi->next) { --- 588,600 ---- || si->prev != chainsi->idx) break; } ! gcc_assert (chainsi->length || chainsi->stmt); if (chainsi->endptr == NULL_TREE) { chainsi = unshare_strinfo (chainsi); chainsi->endptr = ptr; } ! if (chainsi->length && integer_zerop (chainsi->length)) { if (chainsi->next) { *************** zero_length_string (tree ptr, strinfo ch *** 626,631 **** --- 626,633 ---- if (chainsi->first == 0) chainsi->first = chainsi->idx; chainsi->next = idx; + if (chainsi->endptr == NULL_TREE) + chainsi->endptr = ptr; si->prev = chainsi->idx; si->first = chainsi->first; si->writable = chainsi->writable; *************** adjust_related_strinfos (location_t loc, *** 654,664 **** tree tem; si = unshare_strinfo (si); ! gcc_assert (si->length); ! tem = fold_convert_loc (loc, TREE_TYPE (si->length), adj); ! si->length = fold_build2_loc (loc, PLUS_EXPR, ! TREE_TYPE (si->length), si->length, ! tem); si->endptr = NULL_TREE; si->dont_invalidate = true; } --- 656,674 ---- tree tem; si = unshare_strinfo (si); ! if (si->length) ! { ! tem = fold_convert_loc (loc, TREE_TYPE (si->length), adj); ! si->length = fold_build2_loc (loc, PLUS_EXPR, ! TREE_TYPE (si->length), si->length, ! tem); ! } ! else if (si->stmt != NULL) ! /* Delayed length computation is unaffected. */ ! ; ! else ! gcc_unreachable (); ! si->endptr = NULL_TREE; si->dont_invalidate = true; } *************** handle_builtin_strcpy (enum built_in_fun *** 1117,1126 **** --- 1127,1162 ---- if (dsi->length == NULL_TREE) { + strinfo chainsi; + /* If string length of src is unknown, use delayed length computation. If string lenth of dst will be needed, it can be computed by transforming this strcpy call into stpcpy and subtracting dst from the return value. */ + + /* Look for earlier strings whose length could be determined if + this strcpy is turned into an stpcpy. */ + + if (dsi->prev != 0 && (chainsi = verify_related_strinfos (dsi)) != NULL) + { + bool stmt_set_p = false; + + for (; chainsi && chainsi != dsi; chainsi = get_strinfo (chainsi->next)) + { + /* When setting a stmt for delayed length computation + prevent all strinfos through dsi from being + invalidated. */ + if (stmt_set_p) + chainsi->dont_invalidate = true; + + chainsi = unshare_strinfo (chainsi); + chainsi->stmt = stmt; + chainsi->length = NULL_TREE; + chainsi->endptr = NULL_TREE; + chainsi->dont_invalidate = true; + stmt_set_p = true; + } + } dsi->stmt = stmt; return; } Index: gcc/testsuite/gcc.dg/strlenopt-22.c =================================================================== *** /dev/null --- gcc/testsuite/gcc.dg/strlenopt-22.c *************** *** 0 **** --- 1,41 ---- + /* { dg-do run } */ + /* { dg-options "-O2 -fdump-tree-strlen" } */ + + #define USE_GNU + #include "strlenopt.h" + + __attribute__((noinline, noclone)) size_t + bar (char *p, char *q) + { + size_t l1, l2, l3; + char *r = strchr (p, '\0'); + strcpy (r, "abcde"); + char *s = strchr (r, '\0'); + strcpy (s, q); + l1 = strlen (p); + l2 = strlen (r); + l3 = strlen (s); + return l1 + l2 + l3; + } + + int + main () + { + char buf[16] = "01234"; + + if (bar (buf, "56789") != 30) + abort (); + + if (memcmp (buf, "01234abcde56789", 16) != 0) + abort (); + + return 0; + } + + /* { dg-final { scan-tree-dump-times "strlen \\(" 0 "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 "stpcpy \\(" 1 "strlen" } } */ + /* { dg-final { cleanup-tree-dump "strlen" } } */ Index: gcc/testsuite/gcc.dg/strlenopt-4.c =================================================================== *** gcc/testsuite/gcc.dg/strlenopt-4.c.orig --- gcc/testsuite/gcc.dg/strlenopt-4.c *************** main () *** 66,75 **** return 0; } ! /* { dg-final { scan-tree-dump-times "strlen \\(" 3 "strlen" } } */ /* { dg-final { scan-tree-dump-times "memcpy \\(" 4 "strlen" } } */ ! /* { dg-final { scan-tree-dump-times "strcpy \\(" 3 "strlen" } } */ ! /* { dg-final { scan-tree-dump-times "strcat \\(" 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" } } */ --- 66,81 ---- return 0; } ! /* For targets providing a movstr pattern strcat is already decomposed ! into strlen + strcpy by fold_builtin_strcat. */ ! ! /* { dg-final { scan-tree-dump-times "strlen \\(" 3 "strlen" { target { ! s390*-*-* } } } } */ ! /* { dg-final { scan-tree-dump-times "strlen \\(" 6 "strlen" { target s390*-*-* } } } */ /* { dg-final { scan-tree-dump-times "memcpy \\(" 4 "strlen" } } */ ! /* { dg-final { scan-tree-dump-times "strcpy \\(" 3 "strlen" { target { ! s390*-*-* } } } } */ ! /* { dg-final { scan-tree-dump-times "strcpy \\(" 6 "strlen" { target s390*-*-* } } } */ ! /* { dg-final { scan-tree-dump-times "strcat \\(" 3 "strlen" { target { ! s390*-*-* } } } } */ ! /* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen" { target s390*-*-* } } } */ /* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen" } } */ /* { dg-final { cleanup-tree-dump "strlen" } } */