[gcc r17-3337] fold-const-call.cc: add strnlen in fold_const_call [PR86937]

Daniel Barboza via Gcc-cvs <[email protected]>
Newsgroups gmane.comp.gcc.cvs
Message-ID <[email protected]>
https://gcc.gnu.org/g:e7b77e39552d82141f71f039f70885754de72093

commit r17-3337-ge7b77e39552d82141f71f039f70885754de72093
Author: Daniel Barboza <[email protected]>
Date:   Fri Aug 14 18:14:42 2026 -0300

    fold-const-call.cc: add strnlen in fold_const_call [PR86937]
    
    We're not trying to fold builtin_strnlen calls into constants like we do
    with builtin_strlen.  This results in missed optimizations where we
    have a strnlen that uses a PHI as input:
    
    const char a[4] = "123";
    
    int g (int i)
    {
      return __builtin_strnlen (i ? a : "", 4);
    }
    
    The strnlen call could be folded into a single PHI <0, 3> but instead
    we're calling strnlen with the PHI result:
    
      # iftmp.1_2 = PHI <&aD.4472(3), ""(2)>
      # VUSE <.MEM_4(D)>
      # RANGE [irange] long unsigned int [0, 4] MASK 0x7 VALUE 0x0
      # USE = nonlocal escaped const-pool { D.4472 } (nonlocal)
      _1 = strnlenD.1862 (iftmp.1_2, 4);
      # RANGE [irange] int [0, 4] MASK 0x7 VALUE 0x0
      _5 = (intD.7) _1;
      # VUSE <.MEM_4(D)>
      return _5;
    
    After this change:
    
      # RANGE [irange] int [0, 0][3, 3] MASK 0x3 VALUE 0x0
      # prephitmp_7 = PHI <3(3), i_3(D)(2)>
      # VUSE <.MEM_4(D)>
      return prephitmp_7;
    
    Bootstrapped and regression tested in x86_64.
    
            PR tree-optimization/86937
    
    gcc/ChangeLog:
    
            * fold-const-call.cc (fold_const_call): fold
            CFN_BUILT_IN_STRNLEN calls.
    
    gcc/testsuite/ChangeLog:
    
            * gcc.dg/warn-strnlen-no-nul.c: warnings were
            added/removed/changed due to the new STRNLEN folding.
            * gcc.dg/tree-ssa/pr86937.c: New test.

Diff:
---
 gcc/fold-const-call.cc                     | 11 +++++++++++
 gcc/testsuite/gcc.dg/tree-ssa/pr86937.c    | 15 +++++++++++++++
 gcc/testsuite/gcc.dg/warn-strnlen-no-nul.c | 29 +++++++++++++++--------------
 3 files changed, 41 insertions(+), 14 deletions(-)

diff --git a/gcc/fold-const-call.cc b/gcc/fold-const-call.cc
index b3a9d7715d66..c483b80e7045 100644
--- a/gcc/fold-const-call.cc
+++ b/gcc/fold-const-call.cc
@@ -1911,6 +1911,17 @@ fold_const_call (combined_fn fn, tree type, tree arg0, tree arg1)
 	}
       return NULL_TREE;
 
+    case CFN_BUILT_IN_STRNLEN:
+      if ((p0 = c_getstr (arg0)))
+	{
+	  unsigned HOST_WIDE_INT s1 = 0;
+	  if (!size_t_cst_p (arg1, &s1))
+	    return NULL_TREE;
+
+	  return build_int_cst (type, strnlen (p0, s1));
+	}
+      return NULL_TREE;
+
     case CFN_FOLD_LEFT_PLUS:
       return fold_const_fold_left (type, arg0, arg1, PLUS_EXPR);
 
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr86937.c b/gcc/testsuite/gcc.dg/tree-ssa/pr86937.c
new file mode 100644
index 000000000000..b6b2876bf647
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/pr86937.c
@@ -0,0 +1,15 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-pre" } */
+const char a[4] = "123";
+
+int f (int i)
+{
+  return __builtin_strlen (i ? a : "");
+}
+
+int g (int i)
+{
+  return __builtin_strnlen (i ? a : "", 4);
+}
+
+/* { dg-final { scan-tree-dump-times "strnlen " 0 "pre" } } */
diff --git a/gcc/testsuite/gcc.dg/warn-strnlen-no-nul.c b/gcc/testsuite/gcc.dg/warn-strnlen-no-nul.c
index 70f6a432b978..45a948c22b6e 100644
--- a/gcc/testsuite/gcc.dg/warn-strnlen-no-nul.c
+++ b/gcc/testsuite/gcc.dg/warn-strnlen-no-nul.c
@@ -147,14 +147,14 @@ T (v0 ? b[3] : "", bsz);
    the strnlen calls are safe because the reads are bounded by
    the length of the constant arguments.  Most of the calls are
    not diagnosed anymore as a result of the fix for PR 103215.  */
-T (v0 ? "" : b[0], bsz + 1);      /* { dg-warning "bound 6 exceeds source size 5" } */
+T (v0 ? "" : b[0], bsz + 1);
 T (v0 ? "" : b[1], bsz + 1);
 T (v0 ? "" : b[2], bsz + 1);
-T (v0 ? "" : b[3], bsz + 1);
-T (v0 ? b[0] : "", bsz + 1);      /* { dg-warning "bound 6 exceeds source size 5" } */
+T (v0 ? "" : b[3], bsz + 1);      /* { dg-warning "unterminated" } */
+T (v0 ? b[0] : "", bsz + 1);
 T (v0 ? b[1] : "", bsz + 1);
 T (v0 ? b[2] : "", bsz + 1);
-T (v0 ? b[3] : "", bsz + 1);
+T (v0 ? b[3] : "", bsz + 1);      /* { dg-warning "unterminated" } */
 
 T (v0 ? "" : b[i0], bsz);
 T (v0 ? "" : b[i1], bsz);
@@ -168,11 +168,11 @@ T (v0 ? b[i3] : "", bsz);
 T (v0 ? "" : b[i0], bsz + 1);
 T (v0 ? "" : b[i1], bsz + 1);
 T (v0 ? "" : b[i2], bsz + 1);
-T (v0 ? "" : b[i3], bsz + 1);
+T (v0 ? "" : b[i3], bsz + 1);     /* { dg-warning "unterminated" } */
 T (v0 ? b[i0] : "", bsz + 1);
 T (v0 ? b[i1] : "", bsz + 1);
 T (v0 ? b[i2] : "", bsz + 1);
-T (v0 ? b[i3] : "", bsz + 1);
+T (v0 ? b[i3] : "", bsz + 1);     /* { dg-warning "unterminated" } */
 
 T (v0 ? "1234" : b[3], bsz);
 T (v0 ? "1234" : b[i3], bsz);
@@ -184,17 +184,18 @@ T (v0 ? b[0] : b[2], bsz);
 T (v0 ? b[2] : b[3], bsz);
 T (v0 ? b[3] : b[2], bsz);
 
-T (v0 ? "1234" : b[3], bsz + 1);
-T (v0 ? "1234" : b[i3], bsz + 1);
-T (v0 ? b[3] : "1234", bsz + 1);
-T (v0 ? b[i3] : "1234", bsz + 1);
+/* New warnings being thrown after PR86937.  */
+T (v0 ? "1234" : b[3], bsz + 1);     /* { dg-warning "unterminated" } */
+T (v0 ? "1234" : b[i3], bsz + 1);    /* { dg-warning "unterminated" } */
+T (v0 ? b[3] : "1234", bsz + 1);     /* { dg-warning "unterminated" } */
+T (v0 ? b[i3] : "1234", bsz + 1);    /* { dg-warning "unterminated" } */
 
 /* That the following are not diagnosed is a bug/limitation resulting from
    the fix for PR 103215.  */
-T (v0 ? a : b[3], bsz + 1);       /* { dg-warning "bound 6 exceeds source size 5" "pr103215" { xfail *-*-* } } */
-T (v0 ? b[0] : b[2], bsz + 1);    /* { dg-warning "bound 6 exceeds source size 5" "pr103215" { xfail *-*-* } } */
-T (v0 ? b[2] : b[3], bsz + 1);    /* { dg-warning "bound 6 exceeds source size 5" "pr103215" { xfail *-*-* } } */
-T (v0 ? b[3] : b[2], bsz + 1);    /* { dg-warning "bound 6 exceeds source size 5" "pr103215" { xfail *-*-* } } */
+T (v0 ? a : b[3], bsz + 1);
+T (v0 ? b[0] : b[2], bsz + 1);
+T (v0 ? b[2] : b[3], bsz + 1);    /* { dg-warning "unterminated" } */
+T (v0 ? b[3] : b[2], bsz + 1);    /* { dg-warning "unterminated" } */
 
 struct A { char a[5], b[5]; };
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.