gimple_fold_builtin_memset currently folds constant-length memset calls into
scalar stores only when the destination is an ADDR_EXPR. Calls through an
arbitrary pointer destination such as a function parameter are left as library
calls. Additionally, non-constant fill values are rejected unconditionally
even for the length-one case where no byte replication is needed.
The patch adds a dedicated folding path for length-one memset calls through
arbitrary pointer destinations. The approach follows the mode-determination
pattern already used in gimple_fold_builtin_memory_op: the store mode and type
are derived via int_mode_for_size, bitwise_mode_for_size and
bitwise_type_for_mode. Destination alignment is validated against
targetm.slow_unaligned_access and movmisalign_optab before proceeding.
The fold is gated by check_bounds_or_overlap to avoid suppressing diagnostics
from the access-warning pass. Volatile qualifiers are propagated from the
pointed-to type and under-aligned destinations are handled via
build_aligned_type. Both constant and non-constant fill values are supported.
A constant fill is converted directly via fold_convert and a non-constant fill
is converted to the byte store type via gimple_convert. The store is emitted as
a MEM_REF at offset zero using a ref-all character pointer type following the
same convention used elsewhere in gimple-fold.cc.
The existing ADDR_EXPR-based folding path for multi-byte constant memsets is
preserved unchanged.
Attempting to lift the ADDR_EXPR restriction for multi-byte lengths caused a
broad set of regressions across multiple passes:
When the memset call is folded early into stores before the access-warning pass
runs -Wstringop-overflow diagnostics are suppressed.
Early folding through arbitrary pointer destinations interferes with
__builtin_object_size and __builtin_dynamic_object_size analysis.
-fanalyzer pass inspects memset calls for safety analysis. Folding them to raw
MEM_REF stores prematurely removes the semantic information the analyzer depends
on for reporting violations.
gcc/ChangeLog:
PR tree-optimization/102202
* gimple-fold.cc (gimple_fold_builtin_memset): Hoist tree_to_uhwi (len)
before the INTEGER_CST check. Add a folding path for length-one memset
calls through arbitrary pointer destinations supporting both constant
and
non-constant fill values following the mode-determination pattern of
gimple_fold_builtin_memory_op.
gcc/testsuite/ChangeLog:
PR tree-optimization/102202
* gcc.dg/pr102202-fold.c: New test.
Signed-off-by: Naveen <[email protected]>
---
gcc/gimple-fold.cc | 76 +++++++++++++++++++++++++++-
gcc/testsuite/gcc.dg/pr102202-fold.c | 25 +++++++++
2 files changed, 99 insertions(+), 2 deletions(-)
create mode 100644 gcc/testsuite/gcc.dg/pr102202-fold.c
diff --git a/gcc/gimple-fold.cc b/gcc/gimple-fold.cc
index fab4886b0f2..856bc1f9e7f 100644
--- a/gcc/gimple-fold.cc
+++ b/gcc/gimple-fold.cc
@@ -1478,10 +1478,83 @@ gimple_fold_builtin_memset (gimple_stmt_iterator *gsi,
tree c, tree len)
if (! tree_fits_uhwi_p (len))
return false;
+ length = tree_to_uhwi (len);
+
+ tree dest = gimple_call_arg (stmt, 0);
+ if (TREE_CODE (c) != INTEGER_CST && length == 1
+ && POINTER_TYPE_P (TREE_TYPE (dest)))
+ {
+ /* Detect out-of-bounds accesses without issuing warnings. Avoid
+ folding calls that should be diagnosed later by the access warning
+ pass. */
+ if (check_bounds_or_overlap (as_a <gcall *>(stmt),
+ dest, NULL_TREE, len,
+ NULL_TREE, false, false))
+ return false;
+
+ unsigned int dest_align = get_pointer_alignment (dest);
+ scalar_int_mode imode;
+ machine_mode mode;
+ if (!int_mode_for_size (BITS_PER_UNIT, 0).exists (&imode)
+ || !bitwise_mode_for_size (BITS_PER_UNIT).exists (&mode)
+ || !known_eq (GET_MODE_BITSIZE (mode), BITS_PER_UNIT)
+ /* If the destination pointer is not aligned we must be able to emit
+ an unaligned store. */
+ || (dest_align < GET_MODE_ALIGNMENT (mode)
+ && targetm.slow_unaligned_access (mode, dest_align)
+ && (optab_handler (movmisalign_optab, mode)
+ == CODE_FOR_nothing)))
+ return false;
+
+ etype = bitwise_type_for_mode (mode);
+ if (etype == NULL_TREE)
+ return false;
+
+ if (dest_align < GET_MODE_ALIGNMENT (mode))
+ etype = build_aligned_type (etype, dest_align);
+
+ tree ptype = TREE_TYPE (TREE_TYPE (dest));
+ if (TYPE_VOLATILE (ptype))
+ etype = build_qualified_type (etype, TYPE_QUAL_VOLATILE);
+
+ location_t loc = gimple_location (stmt);
+ tree cval_tree;
+ if (TREE_CODE (c) == INTEGER_CST)
+ cval_tree = fold_convert (etype, c);
+ else
+ cval_tree = gimple_convert (gsi, true, GSI_SAME_STMT, loc, etype, c);
+
+ /* Build accesses at offset zero with a ref-all character type. */
+ tree off0
+ = build_int_cst (build_pointer_type_for_mode (char_type_node,
+ ptr_mode, true), 0);
+ tree var = fold_build2_loc (loc, MEM_REF, etype, dest, off0);
+ gimple *store = gimple_build_assign (var, cval_tree);
+ gimple_move_vops (store, stmt);
+ gimple_set_location (store, loc);
+ copy_warning (store, stmt);
+
+ tree lhs = gimple_call_lhs (stmt);
+ if (!lhs)
+ {
+ gsi_replace (gsi, store, false);
+ return true;
+ }
+
+ gsi_insert_before (gsi, store, GSI_SAME_STMT);
+ tree ret = dest;
+ if (!useless_type_conversion_p (TREE_TYPE (lhs), TREE_TYPE (dest)))
+ ret = gimple_convert (gsi, true, GSI_SAME_STMT, loc,
+ TREE_TYPE (lhs), dest);
+ gimple *asgn = gimple_build_assign (lhs, ret);
+ gsi_replace (gsi, asgn, false);
+
+ return true;
+ }
+
if (TREE_CODE (c) != INTEGER_CST)
return false;
- tree dest = gimple_call_arg (stmt, 0);
tree var = dest;
if (TREE_CODE (var) != ADDR_EXPR)
return false;
@@ -1502,7 +1575,6 @@ gimple_fold_builtin_memset (gimple_stmt_iterator *gsi,
tree c, tree len)
if (! var_decl_component_p (var))
return false;
- length = tree_to_uhwi (len);
if (GET_MODE_SIZE (SCALAR_INT_TYPE_MODE (etype)) != length
|| (GET_MODE_PRECISION (SCALAR_INT_TYPE_MODE (etype))
!= GET_MODE_BITSIZE (SCALAR_INT_TYPE_MODE (etype)))
diff --git a/gcc/testsuite/gcc.dg/pr102202-fold.c
b/gcc/testsuite/gcc.dg/pr102202-fold.c
new file mode 100644
index 00000000000..3c1a0ab4fa9
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/pr102202-fold.c
@@ -0,0 +1,25 @@
+/* PR tree-optimization/102202 */
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+
+void *
+g5 (char *d, int c)
+{
+ return __builtin_memset (d, c, 1);
+}
+
+void
+g6 (char *d, int c)
+{
+ __builtin_memset (d, c, 1);
+}
+
+void *
+g7 (int *p, int c)
+{
+ return __builtin_memset (p, c, 1);
+}
+
+/* { dg-final { scan-tree-dump-not "__builtin_memset" "optimized" } } */
+/* { dg-final { scan-tree-dump-times {\(unsigned char\) c_[0-9]+\(D\)} 3
"optimized" } } */
+/* { dg-final { scan-tree-dump-times {MEM[^;\n\r]*= _[0-9]+;} 3 "optimized" }
} */
--
2.34.1