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:

Reply via email to