The current patch addresses the review feedback by removing the CDCE-specific
entry into the memset folder. Instead the ordinary memset fold now handles
supported small constant-length calls through arbitrary pointer destinations.

The wider fold is deliberately delayed until after the final access-warning
point. This preserves late -Wstringop-overflow diagnostics for complex
pointer expressions such as PHI/MIN/MAX cases fixing the regressions seen in
Wstringop-overflow-58.c, Wstringop-overflow-59.c and Wstringop-overflow-62.c.

The fold is limited to constant lengths no larger than MOVE_MAX requires an
exact integer/bitwise mode, checks target alignment support and uses
check_bounds_or_overlap before replacing the call. Existing length-one
behavior after object-size analysis is preserved.

Bootstrapped and regtested on X86-64 and Aarch64.

gcc/ChangeLog:
        PR tree-optimization/102202
        * gimple-fold.cc (gimple_fold_builtin_memset): Fold supported
        constant-length memset calls through arbitrary pointer destinations.
        Defer wider folds until after the final access-warning point.
        * tree-cfgcleanup.cc (execute_cleanup_cfg_post_optimizing): Set
        PROP_warn_access_done and sweep surviving BUILT_IN_MEMSET calls.
        * tree-pass.h (PROP_warn_access_done): New pass property.

gcc/testsuite/ChangeLog:
        PR tree-optimization/102202
        * gcc.dg/pr102202-fold-warn.c: New test.
        * gcc.dg/pr102202-fold-zero-n.c: New test.
        * gcc.dg/pr86010-2.c: Accept an inline scalar store.
        * gcc.dg/pr86010.c: Likewise.
        * gcc.dg/tree-ssa/calloc-5.c: Likewise.
        * gcc.target/aarch64/pr102202-fold-no-mode.c: New test

Signed-off-by: Naveen <[email protected]>
---
 gcc/gimple-fold.cc                            | 170 +++++++++++++-----
 gcc/testsuite/gcc.dg/pr102202-fold-warn.c     |  13 ++
 gcc/testsuite/gcc.dg/pr102202-fold-zero-n.c   |  55 ++++++
 gcc/testsuite/gcc.dg/pr86010-2.c              |   2 +-
 gcc/testsuite/gcc.dg/pr86010.c                |   2 +-
 gcc/testsuite/gcc.dg/tree-ssa/calloc-5.c      |   8 +-
 .../aarch64/pr102202-fold-no-mode.c           |  12 ++
 gcc/tree-cfgcleanup.cc                        |  26 +++
 gcc/tree-pass.h                               |   1 +
 9 files changed, 236 insertions(+), 53 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/pr102202-fold-warn.c
 create mode 100644 gcc/testsuite/gcc.dg/pr102202-fold-zero-n.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/pr102202-fold-no-mode.c

diff --git a/gcc/gimple-fold.cc b/gcc/gimple-fold.cc
index 24041466108..5c04b7b7566 100644
--- a/gcc/gimple-fold.cc
+++ b/gcc/gimple-fold.cc
@@ -1481,62 +1481,138 @@ 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)))
     {
-      /* Keep the original call until object-size analysis has inspected it.  
*/
-      if (!(cfun->curr_properties & PROP_objsz))
+      /* Preserve existing length-one timing after object-size analysis.
+        Defer wider arbitrary-pointer folds until after final access warnings 
so
+        -Wstringop-overflow can still diagnose complex pointer expressions.  */
+      if (length == 1 && !(cfun->curr_properties & PROP_objsz))
        return false;
 
-      /* 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;
+      if (cfun->curr_properties & PROP_objsz)
+       {
+         unsigned int dest_align = get_pointer_alignment (dest);
+         machine_mode mode = VOIDmode;
+         etype = NULL_TREE;
 
-      etype = unsigned_char_type_node;
-      tree ptype = TREE_TYPE (TREE_TYPE (dest));
-      if (TYPE_VOLATILE (ptype))
-       etype = build_qualified_type (etype, TYPE_QUAL_VOLATILE);
+         /* Preserve the established length-one timing after object-size
+            analysis.  Only the new wider fold needs delaying for late
+            range-based diagnostics.  */
+         if (length == 1)
+           etype = unsigned_char_type_node;
+         else if ((cfun->curr_properties & PROP_warn_access_done)
+                  && length <= MOVE_MAX
+                  && length <= HOST_BITS_PER_WIDE_INT / BITS_PER_UNIT)
+           {
+             unsigned HOST_WIDE_INT bits = length * BITS_PER_UNIT;
+             if (int_mode_for_size (bits, 0).exists ()
+                 && 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)))
+               {
+                 etype = bitwise_type_for_mode (mode);
+                 if (etype && !INTEGRAL_TYPE_P (etype))
+                   etype = NULL_TREE;
+                 if (etype && dest_align < GET_MODE_ALIGNMENT (mode))
+                   etype = build_aligned_type (etype, dest_align);
+               }
+           }
 
-      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);
+         if (etype)
+           {
+             /* Avoid folding accesses that still need diagnostics.  Emit no
+                warnings here; unreachable-code pruning and the warning passes
+                handle them later while -Wrestrict remains 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;
 
-      /* 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 ptype = TREE_TYPE (TREE_TYPE (dest));
+             if (TYPE_VOLATILE (ptype))
+               etype = build_qualified_type (etype, TYPE_QUAL_VOLATILE);
 
-      tree lhs = gimple_call_lhs (stmt);
-      if (!lhs)
-       {
-         gsi_replace (gsi, store, false);
-         return true;
-       }
+             location_t loc = gimple_location (stmt);
+             tree cval_tree;
+             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
+               {
+                 tree value_type = TYPE_MAIN_VARIANT (etype);
+                 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);
 
-      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);
+                 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);
+               }
 
-      return true;
+             /* Build a 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);
+             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)
diff --git a/gcc/testsuite/gcc.dg/pr102202-fold-warn.c 
b/gcc/testsuite/gcc.dg/pr102202-fold-warn.c
new file mode 100644
index 00000000000..9bfe566c9f0
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/pr102202-fold-warn.c
@@ -0,0 +1,13 @@
+/* PR tree-optimization/102202 */
+/* { dg-do compile } */
+/* { dg-options "-O2 -Wstringop-overflow=2" } */
+
+extern void sink (void *);
+
+void
+f (int c)
+{
+  void *p = __builtin_malloc (1);
+  __builtin_memset (p, c, 2); /* { dg-warning "writing 2 bytes into a region 
of size 1" } */
+  sink (p);
+}
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..275cc0bd034
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/pr102202-fold-zero-n.c
@@ -0,0 +1,55 @@
+/* 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);
+}
+
+/* Ordinary constant-length calls use the same fold.  */
+
+void
+g4 (int c, unsigned short *d)
+{
+  __builtin_memset (d, c, 2);
+}
+
+void *
+g5 (int c, unsigned short *d)
+{
+  return __builtin_memset (d, c, 2);
+}
+
+void
+g6 (unsigned short *d)
+{
+  __builtin_memset (d, 7, 2);
+}
+
+/* Only the first three calls have exact {0, 2} lengths.  */
+/* { dg-final { scan-tree-dump-times "function call is shrink-wrapped into 
error conditions" 3 "cdce" } } */
+
+/* Both CDCE-produced and ordinary constant-length calls are scalarized.  */
+/* { dg-final { scan-tree-dump-not "__builtin_memset" "optimized" } } */
diff --git a/gcc/testsuite/gcc.dg/pr86010-2.c b/gcc/testsuite/gcc.dg/pr86010-2.c
index 4c82e65aeb5..4da26ea64b8 100644
--- a/gcc/testsuite/gcc.dg/pr86010-2.c
+++ b/gcc/testsuite/gcc.dg/pr86010-2.c
@@ -19,4 +19,4 @@ void h (char *a)
   f (a);
 }
 
-/* { dg-final { scan-tree-dump-times "__builtin_memset" 2 "optimized" } } */
+/* { dg-final { scan-tree-dump-times {__builtin_memset|MEM[^;\n\r]*=} 2 
"optimized" } } */
diff --git a/gcc/testsuite/gcc.dg/pr86010.c b/gcc/testsuite/gcc.dg/pr86010.c
index ac279893268..5af32f4adb8 100644
--- a/gcc/testsuite/gcc.dg/pr86010.c
+++ b/gcc/testsuite/gcc.dg/pr86010.c
@@ -21,4 +21,4 @@ void h (void)
   f (a);
 }
 
-/* { dg-final { scan-tree-dump-times "__builtin_memset" 2 "optimized" } } */
+/* { dg-final { scan-tree-dump-times {__builtin_memset|MEM[^;\n\r]*=} 2 
"optimized" } } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/calloc-5.c 
b/gcc/testsuite/gcc.dg/tree-ssa/calloc-5.c
index 3d3e5a14542..9c4dbe9d4db 100644
--- a/gcc/testsuite/gcc.dg/tree-ssa/calloc-5.c
+++ b/gcc/testsuite/gcc.dg/tree-ssa/calloc-5.c
@@ -1,8 +1,8 @@
 /* PR tree-optimization/83821 - local aggregate initialization defeats
    strlen optimization
-   Verify that with DSE disabled, a memset() call to zero out a subregion
-   of memory allocated by calloc() is not eliminated after a non-zero byte
-   is written into it using memset() in between the two calls.
+   Verify that with DSE disabled, a write that zeroes a subregion of memory
+   allocated by calloc() is not eliminated after a nonzero value is written
+   into it between the two writes.
    { dg-do compile }
    { dg-options "-O2 -fno-tree-dse -fdump-tree-optimized" } */
 
@@ -19,4 +19,4 @@ char* keep_memset_calls (void)
 
 /* { dg-final { scan-tree-dump-not "malloc" "optimized" } }
    { dg-final { scan-tree-dump-times "_calloc \\\(" 1 "optimized" } }
-   { dg-final { scan-tree-dump-times "_memset \\\(" 2 "optimized" } } */
+   { dg-final { scan-tree-dump-times {__builtin_memset|MEM[^;\n\r]*=} 2 
"optimized" } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/pr102202-fold-no-mode.c 
b/gcc/testsuite/gcc.target/aarch64/pr102202-fold-no-mode.c
new file mode 100644
index 00000000000..ad66c66cabc
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/pr102202-fold-no-mode.c
@@ -0,0 +1,12 @@
+/* PR tree-optimization/102202 */
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+
+void
+f (int c, unsigned char *d)
+{
+  __builtin_memset (d, c, 3);
+}
+
+/* AArch64 has no three-byte integer mode, so keep the call.  */
+/* { dg-final { scan-tree-dump-times "__builtin_memset" 1 "optimized" } } */
diff --git a/gcc/tree-cfgcleanup.cc b/gcc/tree-cfgcleanup.cc
index 36a3d02f32f..33e77d45aca 100644
--- a/gcc/tree-cfgcleanup.cc
+++ b/gcc/tree-cfgcleanup.cc
@@ -1445,6 +1445,32 @@ execute_cleanup_cfg_post_optimizing (void)
      has already happened.  */
   todo &= ~TODO_cleanup_cfg;
 
+  cfun->curr_properties |= PROP_warn_access_done;
+
+  /* Fold any remaining constant-length memset calls that were deferred past
+     the final access-warning point to preserve -Wstringop-overflow
+     diagnostics for complex pointer expressions.  */
+  if (cfun->curr_properties & PROP_objsz)
+    {
+      basic_block fbb;
+      FOR_EACH_BB_FN (fbb, cfun)
+       {
+         for (gimple_stmt_iterator fgsi = gsi_start_bb (fbb);
+              !gsi_end_p (fgsi); )
+           {
+             if (gimple_call_builtin_p (gsi_stmt (fgsi), BUILT_IN_MEMSET))
+               {
+                 if (fold_stmt (&fgsi))
+                   {
+                     todo |= TODO_update_ssa;
+                     continue;
+                   }
+               }
+             gsi_next (&fgsi);
+           }
+       }
+    }
+
   basic_block bb = single_succ (ENTRY_BLOCK_PTR_FOR_FN (cfun));
   gimple_stmt_iterator gsi = gsi_start_nondebug_after_labels_bb (bb);
   /* If the first (and only) bb and the only non debug
diff --git a/gcc/tree-pass.h b/gcc/tree-pass.h
index a3b35e009e0..07cd386df76 100644
--- a/gcc/tree-pass.h
+++ b/gcc/tree-pass.h
@@ -231,6 +231,7 @@ protected:
                                                   around.  */
 #define PROP_gimple_lbitint    (1 << 20)       /* lowered large _BitInt */
 #define PROP_last_full_fold    (1 << 21)       /* Start of last forwprop.  */
+#define PROP_warn_access_done  (1 << 22)       /* Final access warning point 
done.  */
 
 #define PROP_gimple \
   (PROP_gimple_any | PROP_gimple_lcf | PROP_gimple_leh | PROP_gimple_lomp)
-- 
2.34.1

Reply via email to