aboutsummaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
authorAndrew Pinski <quic_apinski@quicinc.com>2025-04-19 09:14:54 -0700
committerAndrew Pinski <andrew.pinski@oss.qualcomm.com>2025-09-07 23:56:59 -0700
commited264541c353866cedf46ad873f2e2c46cf62839 (patch)
treec79a5905bd3062337f019e9fc4fc906eb6d2328d /gcc
parentd8c407a7c842a449f8bdb3bffd7e667833427ea4 (diff)
downloadgcc-ed264541c353866cedf46ad873f2e2c46cf62839.zip
gcc-ed264541c353866cedf46ad873f2e2c46cf62839.tar.gz
gcc-ed264541c353866cedf46ad873f2e2c46cf62839.tar.bz2
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_apinski@quicinc.com>
Diffstat (limited to 'gcc')
-rw-r--r--gcc/testsuite/gcc.dg/tree-ssa/calloc-10.c19
-rw-r--r--gcc/testsuite/gcc.dg/tree-ssa/calloc-11.c21
-rw-r--r--gcc/testsuite/gcc.dg/tree-ssa/calloc-12.c20
-rw-r--r--gcc/tree-ssa-strlen.cc56
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 0000000..6d91563
--- /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 0000000..397d7fa
--- /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 0000000..ef3b632
--- /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 39e12fd..bb6561c 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))