https://gcc.gnu.org/g:a8072f31bc90000b3b8a2c0c7819710cf924727a

commit r17-2113-ga8072f31bc90000b3b8a2c0c7819710cf924727a
Author: Naveen <[email protected]>
Date:   Fri Jul 3 05:24:04 2026 -0700

    tree-optimization: fold memset with length in [0, 1] to conditional store 
[PR102202]
    
    The patch improves memset optimization when the length is known to be 0 or 
1.
    It uses Ranger information to recognize such cases, shrink-wraps the call on
    the zero-length case and replaces the one-byte case with a direct byte 
store.
    It also extends gimple_fold_builtin_memset to handle Ranger-proven singleton
    lengths not just integer constants.
    
    gcc/ChangeLog:
            PR tree-optimization/102202
            * tree-call-cdce.cc: Include "tree-ssanames.h", "gimple-fold.h".
            (len_has_boolean_range_p): New function.
            (can_shrink_wrap_len_p): New function.
            (gen_zero_len_conditions): New function.
            (shrink_wrap_len_call): New function.
            (shrink_wrap_conditional_dead_built_in_calls): Dispatch to
            shrink_wrap_one_memset_call for memset calls eligible for the [0, 1]
            length transform, ahead of the generic LHS and range-test paths.
            (pass_call_cdce::execute): Collect memset calls satisfying
            can_shrink_wrap_memset_p as shrink-wrap candidates.
    
    gcc/testsuite/ChangeLog:
            PR tree-optimization/102202
            * gcc.dg/pr102202-1.c: New test.
            * gcc.dg/pr102202.c: New test.
            * gcc.target/aarch64/pr100518.c: Modify to handle the warning.
    
    Signed-off-by: Naveen <[email protected]>

Diff:
---
 gcc/testsuite/gcc.dg/pr102202-1.c           |  16 ++++
 gcc/testsuite/gcc.dg/pr102202.c             |  19 +++++
 gcc/testsuite/gcc.target/aarch64/pr100518.c |   2 +-
 gcc/tree-call-cdce.cc                       | 110 ++++++++++++++++++++++++++--
 4 files changed, 141 insertions(+), 6 deletions(-)

diff --git a/gcc/testsuite/gcc.dg/pr102202-1.c 
b/gcc/testsuite/gcc.dg/pr102202-1.c
new file mode 100644
index 000000000000..480bf749ef09
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/pr102202-1.c
@@ -0,0 +1,16 @@
+/* PR tree-optimization/102202 */
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+
+/* A [0, 2] length is not a boolean range, so the call must be left alone.  */
+
+void
+g1 (int a, int c, char *d)
+{
+  if (a < 0 || a > 2)
+    __builtin_unreachable ();
+
+  __builtin_memset (d, c, a);
+}
+
+/* { dg-final { scan-tree-dump "__builtin_memset" "optimized" } } */
diff --git a/gcc/testsuite/gcc.dg/pr102202.c b/gcc/testsuite/gcc.dg/pr102202.c
new file mode 100644
index 000000000000..3aca9815d830
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/pr102202.c
@@ -0,0 +1,19 @@
+/* PR tree-optimization/102202 */
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-cdce-details" } */
+
+void
+g1 (unsigned int n, int c, char *d)
+{
+  unsigned int len = n & 1;
+  __builtin_memset (d, c, len);
+}
+
+char *
+g2 (unsigned int n, int c, char *d)
+{
+  unsigned int len = n & 1;
+  return __builtin_memset (d, c, len);
+}
+
+/* { dg-final { scan-tree-dump-times "function call is shrink-wrapped into 
error conditions" 2 "cdce" } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/pr100518.c 
b/gcc/testsuite/gcc.target/aarch64/pr100518.c
index 177991cfb228..eb8d947185a5 100644
--- a/gcc/testsuite/gcc.target/aarch64/pr100518.c
+++ b/gcc/testsuite/gcc.target/aarch64/pr100518.c
@@ -5,5 +5,5 @@ int unsigned_range_min, unsigned_range_max, a11___trans_tmp_1;
 
 void a11() {
   a11___trans_tmp_1 = unsigned_range_max < unsigned_range_min;
-  __builtin_memset((char *)1, 0, a11___trans_tmp_1);
+  __builtin_memset((char *)1, 0, a11___trans_tmp_1); /* { dg-warning "writing 
1 byte into a region of size 0" } */
 }
diff --git a/gcc/tree-call-cdce.cc b/gcc/tree-call-cdce.cc
index 2be891a7222a..144f485909cf 100644
--- a/gcc/tree-call-cdce.cc
+++ b/gcc/tree-call-cdce.cc
@@ -37,7 +37,9 @@ along with GCC; see the file COPYING3.  If not see
 #include "internal-fn.h"
 #include "tree-dfa.h"
 #include "tree-eh.h"
-
+#include "tree-ssanames.h"
+#include "gimple-fold.h"
+
 
 /* This pass serves two closely-related purposes:
 
@@ -1255,6 +1257,97 @@ use_internal_fn (gcall *call)
                                            is_arg_conds ? new_call : NULL);
 }
 
+/* Return true if LEN is an SSA_NAME known to have a boolean range, i.e. its
+   value is provably in [0, 1].  */
+
+static bool
+len_has_boolean_range_p (tree len, gimple *stmt)
+{
+  if (TREE_CODE (len) != SSA_NAME || !INTEGRAL_TYPE_P (TREE_TYPE (len)))
+    return false;
+  return ssa_name_has_boolean_range (len, stmt);
+}
+
+/* Return true if CALL is a supported length-taking builtin whose length
+   argument is an SSA name known to have a boolean range.  On success,
+   set LEN_ARG to the argument index of the length.  */
+
+static bool
+can_shrink_wrap_len_p (gcall *call, unsigned *len_arg)
+{
+  if (!gimple_call_builtin_p (call, BUILT_IN_MEMSET)
+      || !gimple_vdef (call))
+    return false;
+
+  constexpr unsigned memset_len_arg = 2;
+  if (gimple_call_num_args (call) <= memset_len_arg)
+    return false;
+
+  tree len = gimple_call_arg (call, memset_len_arg);
+  if (!len_has_boolean_range_p (len, call))
+    return false;
+
+  *len_arg = memset_len_arg;
+  return true;
+}
+
+/* Generate the condition vector that guards a call whose LEN is known to be
+   in [0, 1]: skip the call when LEN is zero.  */
+
+static void
+gen_zero_len_conditions (tree len, vec<gimple *> &conds, unsigned *nconds)
+{
+  tree zero = build_zero_cst (TREE_TYPE (len));
+
+  gcc_assert (nconds);
+  conds.quick_push (gimple_build_cond (EQ_EXPR, len, zero,
+                                      NULL_TREE, NULL_TREE));
+  *nconds = 1;
+}
+
+/* Shrink-wrap CALL whose LEN_ARG argument is known to be in [0, 1].
+   ZERO_LEN_RESULT is the value of the call result when its length is zero.
+   On the guarded path the length is one, so pin it to a constant for
+   subsequent folding.  */
+
+static void
+shrink_wrap_len_call (gcall *call, unsigned len_arg, tree zero_len_result)
+{
+  tree lhs = gimple_call_lhs (call);
+
+  /* Define the result on both the guarded and bypass paths before wrapping.  
*/
+  if (lhs)
+    {
+      gcc_assert (zero_len_result);
+      location_t loc = gimple_location (call);
+      gimple_stmt_iterator gsi = gsi_for_stmt (call);
+
+      zero_len_result = gimple_convert (&gsi, true, GSI_SAME_STMT, loc,
+                                       TREE_TYPE (lhs), zero_len_result);
+      gassign *stmt = gimple_build_assign (lhs, zero_len_result);
+      gimple_set_location (stmt, loc);
+      gsi_insert_before (&gsi, stmt, GSI_SAME_STMT);
+
+      gimple_call_set_lhs (call, NULL_TREE);
+      SSA_NAME_DEF_STMT (lhs) = stmt;
+      update_stmt (call);
+    }
+
+  tree len = gimple_call_arg (call, len_arg);
+  unsigned nconds = 0;
+  auto_vec<gimple *, 1> conds;
+  gen_zero_len_conditions (len, conds, &nconds);
+  gcc_assert (nconds != 0);
+
+  shrink_wrap_one_built_in_call_with_conds (call, conds, nconds);
+
+  /* On the guarded path the length is one.  */
+  gimple_call_set_arg (call, len_arg, build_one_cst (TREE_TYPE (len)));
+  update_stmt (call);
+  gimple_stmt_iterator gsi = gsi_for_stmt (call);
+  fold_stmt (&gsi);
+}
+
 /* The top level function for conditional dead code shrink
    wrapping transformation.  */
 
@@ -1267,7 +1360,12 @@ shrink_wrap_conditional_dead_built_in_calls (const 
vec<gcall *> &calls)
   for (; i < n ; i++)
     {
       gcall *bi_call = calls[i];
-      if (gimple_call_lhs (bi_call))
+      unsigned len_arg;
+
+      /* memset returns its destination pointer on the zero-length path.  */
+      if (can_shrink_wrap_len_p (bi_call, &len_arg))
+       shrink_wrap_len_call (bi_call, len_arg, gimple_call_arg (bi_call, 0));
+      else if (gimple_call_lhs (bi_call))
        use_internal_fn (bi_call);
       else
        shrink_wrap_one_built_in_call (bi_call);
@@ -1326,11 +1424,13 @@ pass_call_cdce::execute (function *fun)
       for (i = gsi_start_bb (bb); !gsi_end_p (i); gsi_next (&i))
         {
          gcall *stmt = dyn_cast <gcall *> (gsi_stmt (i));
+         unsigned len_arg;
           if (stmt
              && gimple_call_builtin_p (stmt, BUILT_IN_NORMAL)
-             && (gimple_call_lhs (stmt)
-                 ? can_use_internal_fn (stmt)
-                 : can_test_argument_range (stmt))
+             && (can_shrink_wrap_len_p (stmt, &len_arg)
+                 || (gimple_call_lhs (stmt)
+                     ? can_use_internal_fn (stmt)
+                     : can_test_argument_range (stmt)))
              && can_guard_call_p (stmt))
             {
               if (dump_file && (dump_flags & TDF_DETAILS))

Reply via email to