[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: