[gcc r17-2695] [PATCH v2] tree-optimization: Fix strlen(s) != 0 not folded into *s [PR92408]

Jeff Law via Gcc-cvs <[email protected]>
Newsgroups gmane.comp.gcc.cvs
Message-ID <[email protected]>
https://gcc.gnu.org/g:1649ee187dfdb89f52e6acf15525465f77b67a64

commit r17-2695-g1649ee187dfdb89f52e6acf15525465f77b67a64
Author: Ashley Chekhova <[email protected]>
Date:   Fri Jul 24 23:26:43 2026 -0600

    [PATCH v2] tree-optimization: Fix strlen(s) != 0 not folded into *s [PR92408]
    
    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.
    
    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.
            (simplify_builtin_call): Added call to optimize_strlen_comp.
    
    gcc/testsuite/ChangeLog:
            * gcc.dg/pr92408.c: New test.

Diff:
---
 gcc/testsuite/gcc.dg/pr92408.c | 81 ++++++++++++++++++++++++++++++++++++++++++
 gcc/tree-ssa-forwprop.cc       | 31 ++++++++++++++++
 2 files changed, 112 insertions(+)

diff --git a/gcc/testsuite/gcc.dg/pr92408.c b/gcc/testsuite/gcc.dg/pr92408.c
new file mode 100644
index 000000000000..380c72619cf7
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/pr92408.c
@@ -0,0 +1,81 @@
+/* { dg-do compile } */
+
+/* { dg-options "-O2 -Wno-error=incompatible-pointer-types -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" 4 "forwprop2" } } */
+
+/* { dg-final { scan-tree-dump-times "\\*s" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "void \\*\\)s" 2 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "char.*?void \\*\\)&s" 1 "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 ();
+}
+
+int main (void) {
+  volatile char s[] = "\0\1\1\1";
+
+  // This ought to dereference as a char * pointer.
+  __SIZE_TYPE__ n = __builtin_strlen ((int *) s); /* { dg-warning "incompatible pointer type" } */
+
+  /*
+    If the strlen call above is turned into ((int *) s)*, then it will see the
+    four-byte string "\0\1\1\1" and read it as 0x01010100. This is undesirable
+    given that the original strlen call would have just produced a 0.
+
+    So, this tests to ensure that the value is 0, as we would expect.
+  */
+  if (n)
+    return 1;
+  else
+    return 0;
+}
\ No newline at end of file
diff --git a/gcc/tree-ssa-forwprop.cc b/gcc/tree-ssa-forwprop.cc
index 12c07c99c9bd..75f06c6ba410 100644
--- a/gcc/tree-ssa-forwprop.cc
+++ b/gcc/tree-ssa-forwprop.cc
@@ -2454,6 +2454,35 @@ optimize_stack_restore (gimple_stmt_iterator *gsi, gimple *call)
   return true;
 }
 
+/* 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);
+  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 = fold_build2 (MEM_REF, char_type_node, ptr,
+			    build_zero_cst (ptr_type_node));
+
+  /* Perform a type conversion. */
+  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 +2514,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:
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.