[PATCH v2] tree-optimization: Fold CDCE {0, N} memset through arbitrary pointers [PR102202]

Naveen <[email protected]>
Newsgroups gmane.comp.gcc.patches
Message-ID <[email protected]>
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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.