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.

Reply via email to