CDCE already recognizes an exact two-value length range {0, N}, guards the
zero-length path and replaces the length on the nonzero path with N and folds
the call again.  The accepted PR102202 fold handles N == 1 through arbitrary
pointer destinations but the guarded {0, N} path still needs the same
scalar-store conversion for supported N greater than one.

Keep normal builtin folding behavior unchanged: ordinary constant-size memsets
are still left to the existing ADDR_EXPR/object-specific machinery.  Expose the
memset fold with an opt-in flag for multi-byte arbitrary-pointer stores and
let CDCE use that flag only after it has shrink-wrapped the zero-length path
and pinned the guarded length to N.

The multi-byte fold keeps the existing object-size deferral and bounds checks.
It limits the store to MOVE_MAX, requires an exact integer/bitwise mode, checks
unaligned-store support and replicates the fill byte into the selected scalar
type.

gcc/ChangeLog:

        PR tree-optimization/102202
        * gimple-fold.cc (gimple_fold_builtin_memset): Make non-static and
        add fold_arbitrary_n parameter.  Generalize the arbitrary-pointer
        fold to supported constant lengths when requested.
        (gimple_fold_builtin): Pass false to gimple_fold_builtin_memset.
        * gimple-fold.h (gimple_fold_builtin_memset): Declare.
        * tree-call-cdce.cc (shrink_wrap_len_call): Request the multi-byte
        arbitrary-pointer memset fold after pinning the guarded length.

gcc/testsuite/ChangeLog:

        PR tree-optimization/102202
        * gcc.dg/pr102202-fold-zero-n.c: New test.

Signed-off-by: Naveen <[email protected]>
---
 gcc/gimple-fold.cc                          | 108 +++++++++++++++++---
 gcc/gimple-fold.h                           |   2 +
 gcc/testsuite/gcc.dg/pr102202-fold-zero-n.c |  35 +++++++
 gcc/tree-call-cdce.cc                       |   3 +
 4 files changed, 135 insertions(+), 13 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/pr102202-fold-zero-n.c

diff --git a/gcc/gimple-fold.cc b/gcc/gimple-fold.cc
index fdb9b419766..6893a0b6a0f 100644
--- a/gcc/gimple-fold.cc
+++ b/gcc/gimple-fold.cc
@@ -1456,10 +1456,13 @@ gimple_fold_builtin_bzero (gimple_stmt_iterator *gsi)
 }
 
 /* Fold function call to builtin memset or bzero at *GSI setting the
-   memory of size LEN to VAL.  Return whether a simplification was made.  */
+   memory of size LEN to VAL.  Return whether a simplification was made.
+   If FOLD_ARBITRARY_N is true, also try folding supported constant
+   lengths greater than one through arbitrary pointer destinations.  */
 
-static bool
-gimple_fold_builtin_memset (gimple_stmt_iterator *gsi, tree c, tree len)
+bool
+gimple_fold_builtin_memset (gimple_stmt_iterator *gsi, tree c, tree len,
+                           bool fold_arbitrary_n)
 {
   gimple *stmt = gsi_stmt (*gsi);
   tree etype;
@@ -1481,12 +1484,18 @@ gimple_fold_builtin_memset (gimple_stmt_iterator *gsi, 
tree c, tree len)
   length = tree_to_uhwi (len);
 
   tree dest = gimple_call_arg (stmt, 0);
-  if (length == 1
-      && POINTER_TYPE_P (TREE_TYPE (dest)))
+  if (POINTER_TYPE_P (TREE_TYPE (dest))
+      && (length == 1 || fold_arbitrary_n))
     {
-      /* Keep the original call until object-size analysis has inspected it.  
*/
+      /* Keep the arbitrary-pointer fold until object-size analysis has
+        inspected the original call.  Preserve the old early ADDR_EXPR fold
+        for multi-byte memsets.  */
       if (!(cfun->curr_properties & PROP_objsz))
-       return false;
+       {
+         if (length == 1)
+           return false;
+         goto normal_memset;
+       }
 
       /* Detect out-of-bounds accesses without issuing warnings.
         Avoid folding out-of-bounds accesses but to avoid false
@@ -1499,19 +1508,89 @@ gimple_fold_builtin_memset (gimple_stmt_iterator *gsi, 
tree c, tree len)
        if (warning != OPT_Wrestrict)
          return false;
 
-      etype = unsigned_char_type_node;
+      unsigned int dest_align = get_pointer_alignment (dest);
+      machine_mode mode = VOIDmode;
+
+      if (length == 1)
+       etype = unsigned_char_type_node;
+      else
+       {
+         /* Use one scalar store only when it is no larger than the target's
+            normal move size and its byte-replication constant fits in a host
+            integer.  */
+         if (length > MOVE_MAX
+             || length > HOST_BITS_PER_WIDE_INT / BITS_PER_UNIT)
+           goto normal_memset;
+
+         unsigned HOST_WIDE_INT bits = length * BITS_PER_UNIT;
+         scalar_int_mode imode;
+         if (!int_mode_for_size (bits, 0).exists (&imode)
+             || !bitwise_mode_for_size (bits).exists (&mode)
+             || !known_eq (GET_MODE_BITSIZE (mode), bits)
+             /* If DEST is not naturally aligned, require an efficient
+                unaligned store or a movmisalign pattern.  */
+             || (dest_align < GET_MODE_ALIGNMENT (mode)
+                 && targetm.slow_unaligned_access (mode, dest_align)
+                 && (optab_handler (movmisalign_optab, mode)
+                     == CODE_FOR_nothing)))
+           goto normal_memset;
+
+         etype = bitwise_type_for_mode (mode);
+         if (!etype || !INTEGRAL_TYPE_P (etype))
+           goto normal_memset;
+
+         if (dest_align < GET_MODE_ALIGNMENT (mode))
+           etype = build_aligned_type (etype, dest_align);
+       }
+
+      tree value_type = TYPE_MAIN_VARIANT (etype);
       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);
+      if (length == 1)
+       {
+         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);
+       }
       else
-       cval_tree = gimple_convert (gsi, true, GSI_SAME_STMT, loc, etype, c);
+       {
+         tree byte;
+         if (TREE_CODE (c) == INTEGER_CST)
+           byte = fold_convert (unsigned_char_type_node, c);
+         else
+           byte = gimple_convert (gsi, true, GSI_SAME_STMT, loc,
+                                  unsigned_char_type_node, c);
 
-      /* Build accesses at offset zero with a ref-all character type.  */
+         tree byte_value;
+         if (TREE_CODE (byte) == INTEGER_CST)
+           byte_value = fold_convert (value_type, byte);
+         else
+           byte_value = gimple_convert (gsi, true, GSI_SAME_STMT, loc,
+                                        value_type, byte);
+
+         unsigned HOST_WIDE_INT multiplier = 1;
+         for (unsigned HOST_WIDE_INT i = 1; i < length; ++i)
+           multiplier = (multiplier << BITS_PER_UNIT) | 1;
+         tree multiplier_tree = build_int_cst_type (value_type, multiplier);
+         if (TREE_CODE (byte_value) == INTEGER_CST)
+           cval_tree = fold_build2_loc (loc, MULT_EXPR, value_type,
+                                        byte_value, multiplier_tree);
+         else
+           cval_tree = gimple_build (gsi, true, GSI_SAME_STMT, loc,
+                                     MULT_EXPR, value_type, byte_value,
+                                     multiplier_tree);
+         if (!useless_type_conversion_p (etype, TREE_TYPE (cval_tree)))
+           cval_tree = gimple_convert (gsi, true, GSI_SAME_STMT, loc,
+                                       etype, cval_tree);
+       }
+
+      /* Build the store at offset zero with the selected scalar type.  */
       tree off0
        = build_int_cst (build_pointer_type_for_mode (char_type_node,
                                                      ptr_mode, true), 0);
@@ -1539,6 +1618,8 @@ gimple_fold_builtin_memset (gimple_stmt_iterator *gsi, 
tree c, tree len)
       return true;
     }
 
+normal_memset:
+
   if (TREE_CODE (c) != INTEGER_CST)
     return false;
 
@@ -5527,7 +5608,8 @@ gimple_fold_builtin (gimple_stmt_iterator *gsi)
     case BUILT_IN_MEMSET:
       return gimple_fold_builtin_memset (gsi,
                                         gimple_call_arg (stmt, 1),
-                                        gimple_call_arg (stmt, 2));
+                                        gimple_call_arg (stmt, 2),
+                                        false);
     case BUILT_IN_MEMPCPY:
       if (gimple_fold_builtin_memory_op (gsi, gimple_call_arg (stmt, 0),
                                            gimple_call_arg (stmt, 1), fcode))
diff --git a/gcc/gimple-fold.h b/gcc/gimple-fold.h
index f1853560779..b0d56a71819 100644
--- a/gcc/gimple-fold.h
+++ b/gcc/gimple-fold.h
@@ -58,6 +58,8 @@ extern tree gimple_get_virt_method_for_vtable (HOST_WIDE_INT, 
tree,
                                               unsigned HOST_WIDE_INT,
                                               bool *can_refer = NULL);
 extern tree gimple_fold_indirect_ref (tree);
+extern bool gimple_fold_builtin_memset (gimple_stmt_iterator *, tree, tree,
+                                       bool = false);
 extern bool gimple_fold_builtin_sprintf (gimple_stmt_iterator *);
 extern bool gimple_fold_builtin_snprintf (gimple_stmt_iterator *);
 extern bool arith_code_with_undefined_signed_overflow (tree_code);
diff --git a/gcc/testsuite/gcc.dg/pr102202-fold-zero-n.c 
b/gcc/testsuite/gcc.dg/pr102202-fold-zero-n.c
new file mode 100644
index 00000000000..e0700632ad6
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/pr102202-fold-zero-n.c
@@ -0,0 +1,35 @@
+/* PR tree-optimization/102202 */
+/* { dg-do compile } */
+/* { dg-require-effective-target int32plus } */
+/* { dg-options "-O2 -fdump-tree-cdce-details -fdump-tree-optimized" } */
+
+/* CDCE turns the exact range {0, 2} into a zero-length bypass and a call
+   with constant length two.  The latter should then fold to one scalar
+   store even though the destination is an arbitrary pointer.  */
+
+void
+g1 (unsigned int n, int c, unsigned short *d)
+{
+  __SIZE_TYPE__ len = (n & 1) ? 2 : 0;
+  __builtin_memset (d, c, len);
+}
+
+void *
+g2 (unsigned int n, int c, unsigned short *d)
+{
+  __SIZE_TYPE__ len = (n & 1) ? 2 : 0;
+  return __builtin_memset (d, c, len);
+}
+
+void
+g3 (unsigned int n, unsigned short *d)
+{
+  __SIZE_TYPE__ len = (n & 1) ? 2 : 0;
+  __builtin_memset (d, 7, len);
+}
+
+/* The three calls have exact {0, 2} lengths.  */
+/* { dg-final { scan-tree-dump-times "function call is shrink-wrapped into 
error conditions" 3 "cdce" } } */
+
+/* All three nonzero paths are scalarized.  */
+/* { dg-final { scan-tree-dump-not "__builtin_memset" "optimized" } } */
diff --git a/gcc/tree-call-cdce.cc b/gcc/tree-call-cdce.cc
index 289504fc927..b0fbbbdfb07 100644
--- a/gcc/tree-call-cdce.cc
+++ b/gcc/tree-call-cdce.cc
@@ -1369,6 +1369,9 @@ shrink_wrap_len_call (gcall *call, unsigned len_arg, tree 
zero_len_result,
   update_stmt (call);
   gimple_stmt_iterator gsi = gsi_for_stmt (call);
   fold_stmt (&gsi);
+  if (gsi_stmt (gsi) == call)
+    gimple_fold_builtin_memset (&gsi, gimple_call_arg (call, 1),
+                               nonzero_len, true);
 }
 
 /* The top level function for conditional dead code shrink
-- 
2.34.1

Reply via email to