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). 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. Signed-off-by: Andrew Pinski <quic_apin...@quicinc.com> --- gcc/testsuite/gcc.dg/tree-ssa/calloc-10.c | 19 ++++++++ gcc/testsuite/gcc.dg/tree-ssa/calloc-11.c | 21 +++++++++ gcc/tree-ssa-strlen.cc | 56 +++++++++++++++++++++-- 3 files changed, 91 insertions(+), 5 deletions(-) create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/calloc-10.c create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/calloc-11.c 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 00000000000..6d91563dc15 --- /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 00000000000..397d7fa1fe8 --- /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/tree-ssa-strlen.cc b/gcc/tree-ssa-strlen.cc index e69ceeffb03..f56570c3b78 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 (); @@ -5524,7 +5524,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. */ } @@ -5743,15 +5743,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 true; + } + } + } + } + bool is_char_store = is_char_type (type); if (!is_char_store && TREE_CODE (lhs) == MEM_REF) { @@ -5827,7 +5873,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)) -- 2.43.0