The multi-byte store enhancement to the strlen optimization checked sometime last summer didn't take care to prevent the nul-over-nul store elimination of multi-byte assignments. This made it possible for subsequent multi-byte stores of fewer nuls to cause prior larger stores to be eliminated. The latent bug was exposed by a small unrelated change last December that had masked it until then.
The attached patch makes sure that only single-byte nul stores are considered as candidates for this elimination. I will commit it next week if there are no concerns or suggestions. Martin
PR tree-optimization/93213 - wrong code with -Og -foptimize-strlen gcc/testsuite/ChangeLog: PR tree-optimization/93213 * gcc.c-torture/execute/pr93213.c: New test. gcc/ChangeLog: PR tree-optimization/93213 * tree-ssa-strlen.c (handle_store): Only allow single-byte nul-over-nul stores to be eliminated. Index: gcc/testsuite/gcc.c-torture/execute/pr93213.c =================================================================== --- gcc/testsuite/gcc.c-torture/execute/pr93213.c (nonexistent) +++ gcc/testsuite/gcc.c-torture/execute/pr93213.c (working copy) @@ -0,0 +1,45 @@ +/* PR tree-optimization/93213 - wrong code on a multibyte store with + -Og -foptimize-strlen + { dg-additional-options "-Og -foptimize-strlen" } */ + +typedef unsigned short u16; +typedef unsigned int u32; +typedef unsigned __int128 u128; + +static inline u128 +foo (u16 u16_1, u32 u32_1, u128 u128_1) +{ + u128 u128_0 = 0; + u128_1 -= __builtin_mul_overflow (u32_1, u16_1, &u32_1); + __builtin_memmove (&u16_1, &u128_0, 2); + __builtin_memmove (&u16_1, &u128_1, 1); + return u16_1; +} + +__attribute__ ((noipa)) void +bar (void) +{ + char a[] = { 1, 2 }; + const char b[] = { 0, 0 }; + const char c[] = { 2 }; + __builtin_memcpy (a, b, 2); + // The above is transformed into + // MEM <short unsigned int> [(char * {ref-all})&a] = 0; + // which was then dropped because of the non-nul store below. + __builtin_memcpy (a, c, 1); + + volatile char *p = a; + if (p[0] != 2 || p[1] != 0) + __builtin_abort (); +} + +int +main (void) +{ + u16 x = foo (-1, -1, 0); + if (x != 0xff) + __builtin_abort (); + + bar (); + return 0; +} Index: gcc/tree-ssa-strlen.c =================================================================== --- gcc/tree-ssa-strlen.c (revision 280100) +++ gcc/tree-ssa-strlen.c (working copy) @@ -5239,10 +5239,10 @@ handle_store (gimple_stmt_iterator *gsi, bool *zer } } - if (si != NULL && offset == 0 && storing_all_zeros_p) + if (si != NULL && offset == 0 && storing_all_zeros_p && lenrange[2] == 1) { - /* Allow adjust_last_stmt to remove it if the stored '\0' - is immediately overwritten. */ + /* For single-byte stores only, allow adjust_last_stmt to remove + the statement if the stored '\0' is immediately overwritten. */ laststmt.stmt = stmt; laststmt.len = build_int_cst (size_type_node, 1); laststmt.stridx = si->idx;