[gcc r13-10438] strlen: Adjust objsz arg in __strcat_chk -> __stpcpy_chk transformation [PR125079]

Jakub Jelinek via Gcc-cvs <[email protected]> Sat, 1 Aug 2026 10:57:49 +0000 (GMT)
Newsgroups gmane.comp.gcc.cvs
Message-ID <[email protected]>
https://gcc.gnu.org/g:841e9d7349464a5b51db6c1e05c8a967084d02d7

commit r13-10438-g841e9d7349464a5b51db6c1e05c8a967084d02d7
Author: Jakub Jelinek <[email protected]>
Date:   Fri May 1 14:54:35 2026 +0200

    strlen: Adjust objsz arg in __strcat_chk -> __stpcpy_chk transformation [PR125079]
    
    As the following testcase shows, we have two different transformations
    of __strcat_chk.  One done in strlen_pass::handle_builtin_strcat,
    which transforms __strcat_chk (x, y, z) if we know beforehand strlen (x),
    so something like:
      l = strlen (x);
      __strcat_chk (x, y, z);
    and since PR87672 we change that to
      l = strlen (x);
      __strcpy_chk (x + l, y, z - l);
    i.e. decrease the objsz in
      if (objsz)
        {
          objsz = fold_build2_loc (loc, MINUS_EXPR, TREE_TYPE (objsz), objsz,
                                   fold_convert_loc (loc, TREE_TYPE (objsz),
                                                     unshare_expr (dstlen)));
          objsz = force_gimple_operand_gsi (&m_gsi, objsz, true, NULL_TREE, true,
                                            GSI_SAME_STMT);
        }
    And another transformation is when we have earlier __strcat_chk (x, y, z)
    call and want to compute strlen (x) after that.  In that case
    get_string_length transforms
      __strcat_chk (x, y, z);
    to
      t = strlen (x);
      l = __stpcpy_chk (x + t, y, z) - x;
    where l is the len we are looking for.  This patch changes it similarly to
    the PR87672 to
      t = strlen (x);
      l = __stpcpy_chk (x + t, y, z - t) - x;
    instead.
    
    2026-05-01  Jakub Jelinek  <[email protected]>
    
            PR tree-optimization/125079
            * tree-ssa-strlen.cc (get_string_length): Transform
            __strcat_chk (x, y, z) when we need strlen (x) afterwards into
            l1 = strlen (x); l = __stpcpy_chk (x + l1, y, z - l1) - x;
            where l is the strlen (x), instead of using z as last __stpcpy_chk
            argument.
    
            * gcc.dg/strlenopt-97.c: New test.
    
    Reviewed-by: Richard Biener <[email protected]>
    (cherry picked from commit c1aa090bb8e1c23e733bc7beafceb7c34dc713f8)

Diff:
---
 gcc/testsuite/gcc.dg/strlenopt-97.c | 33 +++++++++++++++++++++++++++++++++
 gcc/tree-ssa-strlen.cc              | 19 +++++++++++++++----
 2 files changed, 48 insertions(+), 4 deletions(-)

diff --git a/gcc/testsuite/gcc.dg/strlenopt-97.c b/gcc/testsuite/gcc.dg/strlenopt-97.c
new file mode 100644
index 000000000000..c4fadbd89425
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/strlenopt-97.c
@@ -0,0 +1,33 @@
+/* PR tree-optimization/125079 */
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-strlen1" } */
+/* { dg-final { scan-tree-dump-times "__builtin___strcpy_chk \\\(" 1 "strlen1" } } */
+/* { dg-final { scan-tree-dump-not "__builtin___strcpy_chk \\\(\[^\n\r]*, 64\\\)" "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "__builtin___stpcpy_chk \\\(" 1 "strlen1" } } */
+/* { dg-final { scan-tree-dump-not "__builtin___stpcpy_chk \\\(\[^\n\r]*, 128\\\)" "strlen1" } } */
+
+typedef __SIZE_TYPE__ size_t;
+void foo (char *, int);
+char *stpcpy (char *, const char *);
+
+size_t
+bar (char *r)
+{
+  char buf[64];
+  foo (buf, 0);
+  size_t ret = __builtin_strlen (buf);
+  __builtin___strcat_chk (buf, r, 64);
+  foo (buf, 1);
+  return ret;
+}
+
+size_t
+baz (char *r)
+{
+  char buf[128];
+  foo (buf, 2);
+  __builtin___strcat_chk (buf, r, 128);
+  size_t ret = __builtin_strlen (buf);
+  foo (buf, 3);
+  return ret;
+}
diff --git a/gcc/tree-ssa-strlen.cc b/gcc/tree-ssa-strlen.cc
index 6651d5e88552..54a47b4fb68e 100644
--- a/gcc/tree-ssa-strlen.cc
+++ b/gcc/tree-ssa-strlen.cc
@@ -848,7 +848,7 @@ get_string_length (strinfo *si)
      attempt to compute the length from the call statement.  */
   if (si->stmt)
     {
-      gimple *stmt = si->stmt, *lenstmt;
+      gimple *stmt = si->stmt, *lenstmt = NULL, *g;
       tree callee, lhs, fn, tem;
       location_t loc;
       gimple_stmt_iterator gsi;
@@ -883,11 +883,11 @@ get_string_length (strinfo *si)
               lhs = force_gimple_operand_gsi (&gsi, lhs, true, NULL_TREE,
                                               true, GSI_SAME_STMT);
             }
-	  lenstmt = gimple_build_assign
+	  g = gimple_build_assign
 			(make_ssa_name (TREE_TYPE (gimple_call_arg (stmt, 0))),
 			 POINTER_PLUS_EXPR,tem, lhs);
-	  gsi_insert_before (&gsi, lenstmt, GSI_SAME_STMT);
-	  gimple_call_set_arg (stmt, 0, gimple_assign_lhs (lenstmt));
+	  gsi_insert_before (&gsi, g, GSI_SAME_STMT);
+	  gimple_call_set_arg (stmt, 0, gimple_assign_lhs (g));
 	  lhs = NULL_TREE;
 	  /* FALLTHRU */
 	case BUILT_IN_STRCPY:
@@ -906,6 +906,17 @@ get_string_length (strinfo *si)
 	  gimple_call_set_fndecl (stmt, fn);
 	  lhs = make_ssa_name (TREE_TYPE (TREE_TYPE (fn)), stmt);
 	  gimple_call_set_lhs (stmt, lhs);
+	  if (DECL_FUNCTION_CODE (callee) == BUILT_IN_STRCAT_CHK)
+	    {
+	      tree objsz = gimple_call_lhs (lenstmt);
+	      gimple *g
+		= gimple_build_assign (make_ssa_name (TREE_TYPE (objsz)),
+				       MINUS_EXPR, gimple_call_arg (stmt, 2),
+				       objsz);
+	      gimple_set_location (g, gimple_location (stmt));
+	      gsi_insert_before (&gsi, g, GSI_SAME_STMT);
+	      gimple_call_set_arg (stmt, 2, gimple_assign_lhs (g));
+	    }
 	  update_stmt (stmt);
 	  if (dump_file && (dump_flags & TDF_DETAILS) != 0)
 	    {