gimple_fold_builtin_memset currently folds constant-length memset calls into
scalar stores only when the destination is an ADDR_EXPR. A length-one memset
through an arbitrary pointer destination such as a function parameter is left
as a call.

For a length-one memset, byte replication is unnecessary: the stored value is
simply the low byte of the fill argument. Extend the fold to handle this case
for arbitrary pointer destinations including non-constant fill values.

The new path derives an appropriate byte store type and mode using the same
mode-selection scheme as gimple_fold_builtin_memory_op. It verifies that the
destination alignment is suitable for the selected mode taking
targetm.slow_unaligned_access and movmisalign_optab into account.

Before replacing the call, run check_bounds_or_overlap so that access-related
diagnostics are preserved. The generated MEM_REF store retains qualifiers from
the pointed-to type including volatile qualification and uses an aligned type
when required. Constant fill values are converted with fold_convert while
non-constant values are converted with gimple_convert.

The transformation is intentionally restricted to length-one memset calls.
Relaxing the ADDR_EXPR restriction for multi-byte memset calls can remove
information needed by object-size analysis, -Wstringop-overflow diagnostics
and -fanalyzer before those analyses have inspected the original call.

Signed-off-by: Naveen <[email protected]>
---
 gcc/gimple-fold.cc                   | 77 +++++++++++++++++++++++++++-
 gcc/testsuite/gcc.dg/pr102202-fold.c | 37 +++++++++++++
 2 files changed, 112 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..703cad2349c 100644
--- a/gcc/gimple-fold.cc
+++ b/gcc/gimple-fold.cc
@@ -1478,10 +1478,84 @@ 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 (length == 1 && POINTER_TYPE_P (TREE_TYPE (dest)))
+    {
+      /* Detect out-of-bounds accesses without issuing warnings.  Avoid
+       folding out-of-bounds accesses but to avoid false positives for
+       unreachable code defer warning until after DCE has worked its magic.
+       -Wrestrict is still diagnosed.  */
+      if (int warning = check_bounds_or_overlap (as_a <gcall *>(stmt), dest,
+                                                NULL_TREE, len, NULL_TREE,
+                                                false, false))
+       if (warning != OPT_Wrestrict)
+         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 +1576,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..7bec2320957
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/pr102202-fold.c
@@ -0,0 +1,37 @@
+/* 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);
+}
+
+void *
+g8 (char *d)
+{
+  return __builtin_memset (d, 0, 1);
+}
+
+void
+g9 (char *d)
+{
+  __builtin_memset (d, 42, 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

Reply via email to