On Tue, Jul 14, 2026 at 7:48 AM Ashley Chekhova <[email protected]> wrote: > > On Monday, 13.07.2026 at 23:22, Jeffrey Law wrote: > > On 7/10/2026 6:55 PM, Andrea Pinski wrote: > > > On Fri, Jul 10, 2026 at 5:24 AM Ashley Chekhova <[email protected]> > > > wrote: > >>> On Monday, 06.07.2026 at 15:33, Richard Biener wrote: > >>>> On Mon, Jun 29, 2026 at 1:06 PM Ashley Chekhova > >>>> <[email protected]> wrote: > >>>>> Checks for strlen(s) == 0 could be rewritten as *s == 0 in the simple > >>>>> case, but in complex cases (such as those involving variable > >>>>> assignment), the optimization wouldn't be implemented. Fix this > >>>>> by moving it over to forwprop from fold-const. Although, since > >>>>> this currently only runs when PROP_last_full_fold is set, the > >>>>> original code is kept in as well. > >>>> Can you tell why it's restricted to the last full fold? > >>>> > >>>>> Bootstrapped and tested on x86_64-pc-linux-gnu > >>>>> > >>>>> PR tree-optimization/92408 > >>>>> > >>>>> gcc/ChangeLog: > >>>>> * tree-ssa-forwprop.cc (optimize_strlen_comp): Rewrite > >>>>> strlen(s) == 0 as *s == 0 and strlen(s) != 0 as *s != 0. > >>>>> * tree-ssa-forwprop.cc (simplify_builtin_call): Added call to > >>>>> optimize_strlen_comp. > >>>>> > >>>>> gcc/testsuite/ChangeLog: > >>>>> + gcc.dg/pr92408.c: New test. > >>>>> > >>>>> Signed-off-by: Ashley Chekhova <[email protected]> > >>>>> --- > >>>>> gcc/testsuite/gcc.dg/pr92408.c | 60 > >>>>> ++++++++++++++++++++++++++++++++++ > >>>>> gcc/tree-ssa-forwprop.cc | 46 ++++++++++++++++++++++++++ > >>>>> 2 files changed, 106 insertions(+) > >>>>> create mode 100644 gcc/testsuite/gcc.dg/pr92408.c > >>>>> > >>>>> diff --git a/gcc/testsuite/gcc.dg/pr92408.c > >>>>> b/gcc/testsuite/gcc.dg/pr92408.c > >>>>> new file mode 100644 > >>>>> index 00000000000..f7adc7153cc > >>>>> --- /dev/null > >>>>> +++ b/gcc/testsuite/gcc.dg/pr92408.c > >>>>> @@ -0,0 +1,60 @@ > >>>>> +/* { dg-do compile } */ > >>>>> + > >>>>> +/* { dg-options "-O2 -fdump-tree-forwprop2 -fdump-tree-optimized" } */ > >>>>> + > >>>>> +/* > >>>>> + Two different checks are used here to ensure that this optimization > >>>>> + doesn't occur before PROP_last_full_fold is set. > >>>>> +*/ > >>>>> + > >>>>> +/* { dg-final { scan-tree-dump-times "\\*s" 1 "forwprop2" } } */ > >>>>> +/* { dg-final { scan-tree-dump-times "__builtin_strlen" 3 "forwprop2" } > >>>>> } */ > >>>>> + > >>>>> +/* { dg-final { scan-tree-dump-times "\\*s" 3 "optimized" } } */ > >>>>> +/* { dg-final { scan-tree-dump-times "__builtin_strlen" 1 "optimized" } > >>>>> } */ > >>>>> + > >>>>> +typedef __SIZE_TYPE__ size_t; > >>>>> +void a (void); > >>>>> +void b (void); > >>>>> + > >>>>> +void modified1 (const char *s) > >>>>> +{ > >>>>> + if (__builtin_strlen (s)) // folded to if (*s) > >>>>> + a (); > >>>>> +} > >>>>> + > >>>>> +void modified2 (const char *s) > >>>>> +{ > >>>>> + /* > >>>>> + folded to > >>>>> + __SIZE_TYPE__ n = (*s); > >>>>> + */ > >>>>> + __SIZE_TYPE__ n = __builtin_strlen (s); > >>>>> + if (n) > >>>>> + a (); > >>>>> +} > >>>>> + > >>>>> +void unaffected1 (const char *s) > >>>>> +{ > >>>>> + /* > >>>>> + this shouldn't be folded. > >>>>> + */ > >>>>> + __SIZE_TYPE__ n = __builtin_strlen (s); > >>>>> + if (n) > >>>>> + a (); > >>>>> + if (n > 5) > >>>>> + b (); > >>>>> +} > >>>>> + > >>>>> +void modified3 (const char *s) > >>>>> +{ > >>>>> + /* > >>>>> + folded to > >>>>> + __SIZE_TYPE__ n = (*s); > >>>>> + */ > >>>>> + __SIZE_TYPE__ n = __builtin_strlen (s); > >>>>> + if (n) > >>>>> + a (); > >>>>> + if (!n) > >>>>> + b (); > >>>>> +} > >>>>> \ No newline at end of file > >>>>> diff --git a/gcc/tree-ssa-forwprop.cc b/gcc/tree-ssa-forwprop.cc > >>>>> index 9bb001d2f63..1db6bf8ccd8 100644 > >>>>> --- a/gcc/tree-ssa-forwprop.cc > >>>>> +++ b/gcc/tree-ssa-forwprop.cc > >>>>> @@ -2454,6 +2454,50 @@ optimize_stack_restore (gimple_stmt_iterator > >>>>> *gsi, gimple *call) > >>>>> return true; > >>>>> } > >>>>> > >>>>> + > >>>>> +/* Optimize > >>>>> + (strlen(s) == 0) > >>>>> + into > >>>>> + (*s == 0) > >>>>> + > >>>>> + and do the same with > >>>>> + (strlen(s) != 0) > >>>>> + into > >>>>> + (*s != 0) > >>>> Please compat the comment, like to > >>>> > >>>> /* Optimizes strlen (s) ==/!= 0 to *s ==/!= 0. */ > >>>> > >>>>> +*/ > >>>>> + > >>>>> + > >>>>> +static bool > >>>>> +optimize_strlen_comp (gimple_stmt_iterator *gsi, gimple *call) > >>>>> +{ > >>>>> + if (!fold_before_rtl_expansion_p ()) > >>>>> + return false; > >>>>> + > >>>>> + tree lhs = gimple_call_lhs (call); > >>>>> + > >>>> less vertical space > >>>> > >>>>> + if (lhs == NULL_TREE || use_in_zero_equality (lhs, true) == NULL) > >>>>> + return false; > >>>>> + > >>>>> + /* The string passed to strlen. */ > >>>>> + tree ptr = gimple_call_arg (call, 0); > >>>>> + > >>>>> + /* Dereference the string. */ > >>>>> + tree deref = build_simple_mem_ref (ptr); > >>>> This doesn't work reliably since pointer types are all equal in GIMPLE > >>>> so you could end up with a int * pointer here for which > >>>> build_simple_mem_ref > >>>> would create an 'int' access. > >>>> > >>>> You need to use sth like > >>>> > >>>> tree deref = fold_build2 (MEM_REF, char_type_node, ptr, > >>>> build_zero_cst (ptr_type_node)); > >>>> > >>>>> + > >>>>> + /* Perform a type conversion if necessary. */ > >>>>> + if (!useless_type_conversion_p (TREE_TYPE (lhs), TREE_TYPE (deref))) > >>>> And this should always be required. > >>>> > >>>>> + { > >>>>> + deref = fold_convert_loc (gimple_location (call), > >>>>> + TREE_TYPE (lhs), > >>>>> + deref); > >>>>> + } > >>>>> + > >>>>> + /* Replace the original call to strlen with the dereference we just > >>>>> built. */ > >>>>> + gimplify_and_update_call_from_tree (gsi, deref); > >>>>> + > >>>>> + return true; > >>>>> +} > >>>>> + > >>>>> /* *GSI_P is a GIMPLE_CALL to a builtin function. > >>>>> Optimize > >>>>> memcpy (p, "abcd", 4); > >>>>> @@ -2485,6 +2529,8 @@ simplify_builtin_call (gimple_stmt_iterator > >>>>> *gsi_p, tree callee2, bool full_walk > >>>>> > >>>>> switch (DECL_FUNCTION_CODE (callee2)) > >>>>> { > >>>>> + case BUILT_IN_STRLEN: > >>>>> + return optimize_strlen_comp (gsi_p, as_a<gcall*>(stmt2)); > >>>>> case BUILT_IN_STACK_RESTORE: > >>>>> return optimize_stack_restore (gsi_p, as_a<gcall*>(stmt2)); > >>>>> case BUILT_IN_MEMCMP: > >>>>> -- > >>>>> 2.53.0 > >>>>> > >>> Changes in v2: > >>> - Type conversion after dereferencing is now always required. > >>> - Fixed the issue regarding strlen being called on pointer-types other > >>> than char *. > >>> - Added a test-case for that issue. > >>> > >>>> Can you tell why it's restricted to the last full fold? > >>> Restriction to the last full fold comes at the recommendation of Drea > >>> Pinski in comment 6 on the PR > >>> (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=92408#c6). Perhaps this > >>> doesn't warrant inclusion in the test case? I'm impartial regardless. > > > Yes, my suggestion was just in case the folding gets in the way of the > > > strlen pass. I didn't know if the strlen pass would like the checking > > > done like `*a == 0` or `strlen(a) == 0`. And I didn't want to > > > introduce a regression due to that. > > So given it was just one test affected (on every target), do we want to > > try and be more aggressive by enabling the transformation earlier and > > fix that one test? Or would you prefer more conservatism here? > > > > jeff > This test failure does seem to at-least be substantive, given that it > represents a missed optimization. > > When the transformation is applied early, the call to strlen is removed > before the strlen pass can see it, and thus information about the string > can no longer be gathered. Ordinarily for this test case, the compiler > will see strlen (s) == 0 and use that to turn strcpy into memcpy, but > since the strlen call is removed, it can no longer do so. In an ideal > world, I imagine the compiler should be able to discern the string is > empty given *s == 0, just as with strlen (s) == 0. Moreover, perhaps it > should remove the strcpy of an empty string altogether. Regardless, > without that information, some optimizations will be missed. > > Would an "if *s == 0, the string is empty" (or perhaps "if s[i] == 0, > the length of the string is less than or equal to i") analysis in strlen > be feasible to implement? Is it even true? I think whether or not the > last full fold restriction can be lifted comes down to these things.
I think it's OK to restrict the folding to last forwprop for now. But please open a bugreport about strlen missing to recognize *s == 0, the existing testcase can be modified to replace strlen(s) == 0 with *s == 0 to show an existing issue. When that's fixed we can relax the folding. Richard.
