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

commit r16-3647-ged264541c353866cedf46ad873f2e2c46cf62839
Author: Andrew Pinski <quic_apin...@quicinc.com>
Date:   Sat Apr 19 09:14:54 2025 -0700

    strlen: Handle empty constructor as memset for combining with malloc to 
calloc [PR87900]
    
    This was noticed when turning memset (with constant size) into a store of 
an empty constructor
    but can be reproduced without that.
    In this case we have the following IR:
    ```
      p_3 = __builtin_malloc (4096);
      *p_3 = {};
    ```
    
    Which we can treat the store as a memset.
    So this patch adds the similar optimization as memset/malloc now for 
malloc/constructor.
    This patch is on top of 
https://gcc.gnu.org/pipermail/gcc-patches/2025-April/681439.html
    (it calls allow_memset_malloc_to_calloc but that can be removed if that 
patch is rejected).
    
    Changes since v1:
    * v2: Correctly return false from handle_assign after removing stmt.
    
    Bootstrapped and tested on x86_64-linux-gnu.
    
            PR tree-optimization/87900
    
    gcc/ChangeLog:
    
            * tree-ssa-strlen.cc  (strlen_pass::handle_assign): Add RHS 
argument.
            For empty constructor RHS, see if can combine with a previous 
malloc into
            a calloc.
            (strlen_pass::check_and_optimize_call): Update call to 
handle_assign;
            passing NULL_TREE for RHS.
            (strlen_pass::check_and_optimize_stmt): Update call to 
handle_assign.
    
    gcc/testsuite/ChangeLog:
    
            * gcc.dg/tree-ssa/calloc-10.c: New test.
            * gcc.dg/tree-ssa/calloc-11.c: New test.
            * gcc.dg/tree-ssa/calloc-12.c: New test.
    
    Signed-off-by: Andrew Pinski <quic_apin...@quicinc.com>

Diff:
---
 gcc/testsuite/gcc.dg/tree-ssa/calloc-10.c | 19 +++++++++++
 gcc/testsuite/gcc.dg/tree-ssa/calloc-11.c | 21 ++++++++++++
 gcc/testsuite/gcc.dg/tree-ssa/calloc-12.c | 20 +++++++++++
 gcc/tree-ssa-strlen.cc                    | 56 ++++++++++++++++++++++++++++---
 4 files changed, 111 insertions(+), 5 deletions(-)

diff --git a/gcc/testsuite/gcc.dg/tree-ssa/calloc-10.c 
b/gcc/testsuite/gcc.dg/tree-ssa/calloc-10.c
new file mode 100644
index 000000000000..6d91563dc15f
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/calloc-10.c
@@ -0,0 +1,19 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+
+/* PR tree-optimization/87900 */
+
+/* zeroing out via a CONSTRUCTOR should be treated similarly as a msmet and
+   be combined with the malloc below.  */
+
+struct S { int a[1024]; };
+struct S *foo ()
+{
+  struct S *p = (struct S *)__builtin_malloc (sizeof (struct S));
+  *p = (struct S){};
+  return p;
+}
+
+/* { dg-final { scan-tree-dump-times "calloc " 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-not "malloc " "optimized" } } */
+/* { dg-final { scan-tree-dump-not "memset " "optimized" } } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/calloc-11.c 
b/gcc/testsuite/gcc.dg/tree-ssa/calloc-11.c
new file mode 100644
index 000000000000..397d7fa1fe80
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/calloc-11.c
@@ -0,0 +1,21 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+
+/* PR tree-optimization/87900 */
+
+/* zeroing out via a CONSTRUCTOR should be treated similarly as a msmet and
+   be combined with the malloc below.  */
+typedef int type;
+
+#define size (1025)
+type *foo ()
+{
+  type *p = (type *)__builtin_malloc (size*sizeof(type));
+  type tmp[size] = {};
+  __builtin_memcpy(p,tmp,sizeof(tmp));
+  return p;
+}
+
+/* { dg-final { scan-tree-dump-times "calloc " 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-not "malloc " "optimized" } } */
+/* { dg-final { scan-tree-dump-not "memset " "optimized" } } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/calloc-12.c 
b/gcc/testsuite/gcc.dg/tree-ssa/calloc-12.c
new file mode 100644
index 000000000000..ef3b6322157a
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/calloc-12.c
@@ -0,0 +1,20 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+
+/* PR tree-optimization/87900 */
+
+/* zeroing out via a CONSTRUCTOR should be treated similarly as a msmet and
+   be combined with the malloc below.  */
+
+struct S { int a[1024]; };
+struct S *foo ()
+{
+  struct S *p = (struct S *)__builtin_malloc (sizeof (struct S));
+  if (p)
+    *p = (struct S){};
+  return p;
+}
+
+/* { dg-final { scan-tree-dump-times "calloc " 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-not "malloc " "optimized" } } */
+/* { dg-final { scan-tree-dump-not "memset " "optimized" } } */
diff --git a/gcc/tree-ssa-strlen.cc b/gcc/tree-ssa-strlen.cc
index 39e12fd222d7..bb6561cde039 100644
--- a/gcc/tree-ssa-strlen.cc
+++ b/gcc/tree-ssa-strlen.cc
@@ -249,7 +249,7 @@ public:
 
   bool check_and_optimize_stmt (bool *cleanup_eh);
   bool check_and_optimize_call (bool *zero_write);
-  bool handle_assign (tree lhs, bool *zero_write);
+  bool handle_assign (tree lhs, tree rhs, bool *zero_write);
   bool handle_store (bool *zero_write);
   void handle_pointer_plus ();
   void handle_builtin_strlen ();
@@ -5465,7 +5465,7 @@ strlen_pass::check_and_optimize_call (bool *zero_write)
        }
 
       if (tree lhs = gimple_call_lhs (stmt))
-       handle_assign (lhs, zero_write);
+       handle_assign (lhs, NULL_TREE, zero_write);
 
       /* Proceed to handle user-defined formatting functions.  */
     }
@@ -5680,15 +5680,61 @@ strlen_pass::handle_integral_assign (bool *cleanup_eh)
 }
 
 /* Handle assignment statement at *GSI to LHS.  Set *ZERO_WRITE if
-   the assignment stores all zero bytes.  */
+   the assignment stores all zero bytes. RHS is the rhs of the
+   statement if not a call.  */
 
 bool
-strlen_pass::handle_assign (tree lhs, bool *zero_write)
+strlen_pass::handle_assign (tree lhs, tree rhs, bool *zero_write)
 {
   tree type = TREE_TYPE (lhs);
   if (TREE_CODE (type) == ARRAY_TYPE)
     type = TREE_TYPE (type);
 
+  if (rhs && TREE_CODE (rhs) == CONSTRUCTOR
+      && TREE_CODE (lhs) == MEM_REF
+      && TREE_CODE (TREE_OPERAND (lhs, 0)) == SSA_NAME
+      && integer_zerop (TREE_OPERAND (lhs, 1)))
+    {
+      /* Set to the non-constant offset added to PTR.  */
+      wide_int offrng[2];
+      gcc_assert (CONSTRUCTOR_NELTS (rhs) == 0);
+      tree ptr = TREE_OPERAND (lhs, 0);
+      tree len = TYPE_SIZE_UNIT (TREE_TYPE (lhs));
+      int idx1 = get_stridx (ptr, gsi_stmt (m_gsi), offrng, ptr_qry.rvals);
+      if (idx1 > 0)
+       {
+         strinfo *si1 = get_strinfo (idx1);
+         if (si1 && si1->stmt
+             && si1->alloc && is_gimple_call (si1->alloc)
+             && valid_builtin_call (si1->stmt)
+             && offrng[0] == 0 && offrng[1] == 0)
+           {
+             gimple *malloc_stmt = si1->stmt;
+             basic_block malloc_bb = gimple_bb (malloc_stmt);
+             if ((DECL_FUNCTION_CODE (gimple_call_fndecl (malloc_stmt))
+                  == BUILT_IN_MALLOC)
+                 && operand_equal_p (len, gimple_call_arg (malloc_stmt, 0), 0)
+                 && allow_memset_malloc_to_calloc (ptr, malloc_bb,
+                                                   gsi_bb (m_gsi)))
+               {
+                 tree alloc_size = gimple_call_arg (malloc_stmt, 0);
+                 gimple_stmt_iterator gsi1 = gsi_for_stmt (malloc_stmt);
+                 tree calloc_decl = builtin_decl_implicit (BUILT_IN_CALLOC);
+                 update_gimple_call (&gsi1, calloc_decl, 2, alloc_size,
+                                     build_one_cst (size_type_node));
+                 si1->nonzero_chars = build_int_cst (size_type_node, 0);
+                 si1->full_string_p = true;
+                 si1->stmt = gsi_stmt (gsi1);
+                 gimple *stmt = gsi_stmt (m_gsi);
+                 unlink_stmt_vdef (stmt);
+                 gsi_remove (&m_gsi, true);
+                 release_defs (stmt);
+                 return false;
+               }
+           }
+       }
+    }
+
   bool is_char_store = is_char_type (type);
   if (!is_char_store && TREE_CODE (lhs) == MEM_REF)
     {
@@ -5764,7 +5810,7 @@ strlen_pass::check_and_optimize_stmt (bool *cleanup_eh)
        /* Handle assignment to a character.  */
        handle_integral_assign (cleanup_eh);
       else if (TREE_CODE (lhs) != SSA_NAME && !TREE_SIDE_EFFECTS (lhs))
-       if (!handle_assign (lhs, &zero_write))
+       if (!handle_assign (lhs, gimple_assign_rhs1 (stmt), &zero_write))
          return false;
     }
   else if (gcond *cond = dyn_cast<gcond *> (stmt))

Reply via email to