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 <jakub@redhat.com>
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 <rguenth@suse.de>
--- /dev/null
+/* 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;
+}
attempt to compute the length from the call statement. */
if (si->stmt)
{
- gimple *stmt = si->stmt, *lenstmt;
+ gimple *stmt = si->stmt, *lenstmt = NULL;
tree callee, lhs, fn, tem;
location_t loc;
gimple_stmt_iterator gsi;
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)
{