public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH] handle bzero/bcopy in DSE and aliasing (PR 80933, 80934)
@ 2017-06-01 20:53 Martin Sebor
  2017-06-02 11:12 ` Richard Biener
  0 siblings, 1 reply; 18+ messages in thread
From: Martin Sebor @ 2017-06-01 20:53 UTC (permalink / raw)
  To: Gcc Patch List

[-- Attachment #1: Type: text/plain, Size: 565 bytes --]

While testing some otherwise unrelated enhancements in these areas
I noticed that calls to bzero and bcopy are not being handled as
efficiently as equivalent calls to memset and memcpy.  Specifically,
redundant calls are not eliminated and the functions appear to be
treated as if they allowed their pointer arguments to escape.  This
turned out to be due to the missing handling of the former two built
ins by the DSE and aliasing passes.

The attached patch adds this handling so the cases I noted in the two
PRs are now handled.

Tested on x86_64-linux.

Martin

[-- Attachment #2: gcc-80933.diff --]
[-- Type: text/x-patch, Size: 13544 bytes --]

PR tree-optimization/80934 - bzero should be assumed not to escape pointer argument
PR tree-optimization/80933 - redundant bzero/bcopy calls not eliminated

gcc/ChangeLog:

	PR tree-optimization/80933
	* tree-ssa-alias.c (ref_maybe_used_by_call_p_1): Handle bzero.
	(call_may_clobber_ref_p_1): Likewise.
	(stmt_kills_ref_p): Likewise.
	* tree-ssa-dse.c (initialize_ao_ref_for_dse): Handle bcopy and bzero.
	(decrement_count): Add an argument.
	(maybe_trim_memstar_call): Handle bcopy.
	(dse_dom_walker::dse_optimize_stmt): Likewise.
	* tree-ssa-sccvn.c (vn_reference_lookup_3): Handle bzero.
	* tree-ssa-structalias.c (find_func_aliases_for_builtin_call): Likewise.
	(find_func_clobbers): Likewise.

gcc/testsuite/ChangeLog:

	PR tree-optimization/80933
	* gcc.dg/tree-ssa/ssa-dse-30.c: New test.
	* gcc.dg/tree-ssa/alias-36.c: Likewise.

diff --git a/gcc/testsuite/gcc.dg/tree-ssa/alias-36.c b/gcc/testsuite/gcc.dg/tree-ssa/alias-36.c
new file mode 100644
index 0000000..61b601a
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/alias-36.c
@@ -0,0 +1,28 @@
+/* PR tree-optimization/80934 - bzero should be assumed not to escape
+   pointer argument
+   { dg-do compile }
+   { dg-options "-O2 -fdump-tree-alias" } */
+
+void foobar (void);
+
+void f (void);
+
+void g (void)
+{
+  char d[32];
+  __builtin_memset (d, 0, sizeof d);
+  f ();
+  if (*d != 0)
+    foobar ();
+}
+
+void h (void)
+{
+  char d[32];
+  __builtin_bzero (d, sizeof d);
+  f ();
+  if (*d != 0)
+    foobar ();
+}
+
+/* { dg-final { scan-tree-dump-not "memset|foobar|bzero" "alias" } } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/ssa-dse-30.c b/gcc/testsuite/gcc.dg/tree-ssa/ssa-dse-30.c
new file mode 100644
index 0000000..9d2c920
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/ssa-dse-30.c
@@ -0,0 +1,30 @@
+/* PR tree-optimization/80933 - redundant bzero/bcopy calls not eliminated
+   { dg-do compile }
+   { dg-options "-O2 -fdump-tree-dse1" } */
+
+void sink (void*);
+
+void test_bcopy (const void *s)
+{
+  char d[33];
+
+  /* The bcopy calls are expanded inline in EVRP, before DSE runs,
+     so this test doesn't actually verify that DSE does its job.  */
+  __builtin_bcopy (s, d, sizeof d);
+  __builtin_bcopy (s, d, sizeof d);
+
+  sink (d);
+}
+
+void test_bzero (void)
+{
+  char d[33];
+
+  __builtin_bzero (d, sizeof d);
+  __builtin_bzero (d, sizeof d);
+
+  sink (d);
+}
+
+/* { dg-final { scan-tree-dump-times "builtin_bzero" 1 "dse1" } } */
+/* { dg-final { scan-tree-dump-not "builtin_bcopy" "dse1" } } */
diff --git a/gcc/tree-ssa-alias.c b/gcc/tree-ssa-alias.c
index 74ee2b0..8c4b289f 100644
--- a/gcc/tree-ssa-alias.c
+++ b/gcc/tree-ssa-alias.c
@@ -1783,6 +1783,7 @@ ref_maybe_used_by_call_p_1 (gcall *call, ao_ref *ref)
 	case BUILT_IN_ALLOCA_WITH_ALIGN:
 	case BUILT_IN_STACK_SAVE:
 	case BUILT_IN_STACK_RESTORE:
+	case BUILT_IN_BZERO:
 	case BUILT_IN_MEMSET:
 	case BUILT_IN_TM_MEMSET:
 	case BUILT_IN_MEMSET_CHK:
@@ -2023,6 +2024,9 @@ call_may_clobber_ref_p_1 (gcall *call, ao_ref *ref)
 
   callee = gimple_call_fndecl (call);
 
+  /* The number of the size argument to one of the built-in functions
+     below.  */
+  unsigned sizargno = 2;
   /* Handle those builtin functions explicitly that do not act as
      escape points.  See tree-ssa-structalias.c:find_func_aliases
      for the list of builtins we might need to handle here.  */
@@ -2030,8 +2034,13 @@ call_may_clobber_ref_p_1 (gcall *call, ao_ref *ref)
       && gimple_call_builtin_p (call, BUILT_IN_NORMAL))
     switch (DECL_FUNCTION_CODE (callee))
       {
-	/* All the following functions clobber memory pointed to by
-	   their first argument.  */
+        case BUILT_IN_BZERO:
+	  sizargno = 1;
+	  /* Fall through.  */
+
+	  /* With the exception of the bzero function above, all of
+	     the following clobber memory pointed to by their first
+	     argument.  */
 	case BUILT_IN_STRCPY:
 	case BUILT_IN_STRNCPY:
 	case BUILT_IN_MEMCPY:
@@ -2062,9 +2071,9 @@ call_may_clobber_ref_p_1 (gcall *call, ao_ref *ref)
 	       is strlen (dest) + n + 1 instead of n, resp.
 	       n + 1 at dest + strlen (dest), but strlen (dest) isn't
 	       known.  */
-	    if (gimple_call_num_args (call) == 3
+	    if (gimple_call_num_args (call) > sizargno
 		&& DECL_FUNCTION_CODE (callee) != BUILT_IN_STRNCAT)
-	      size = gimple_call_arg (call, 2);
+	      size = gimple_call_arg (call, sizargno);
 	    ao_ref_init_from_ptr_and_size (&dref,
 					   gimple_call_arg (call, 0),
 					   size);
@@ -2512,6 +2521,10 @@ stmt_kills_ref_p (gimple *stmt, ao_ref *ref)
 
   if (is_gimple_call (stmt))
     {
+      /* The number of the size argument to one of the built-in functions
+	 below.  */
+      unsigned sizargno = 2;
+
       tree callee = gimple_call_fndecl (stmt);
       if (callee != NULL_TREE
 	  && gimple_call_builtin_p (stmt, BUILT_IN_NORMAL))
@@ -2527,6 +2540,10 @@ stmt_kills_ref_p (gimple *stmt, ao_ref *ref)
 	      break;
 	    }
 
+	  case BUILT_IN_BZERO:
+	    sizargno = 1;
+	    /* Fall through.  */
+
 	  case BUILT_IN_MEMCPY:
 	  case BUILT_IN_MEMPCPY:
 	  case BUILT_IN_MEMMOVE:
@@ -2543,7 +2560,7 @@ stmt_kills_ref_p (gimple *stmt, ao_ref *ref)
 	      if (ref->max_size == -1)
 		return false;
 	      tree dest = gimple_call_arg (stmt, 0);
-	      tree len = gimple_call_arg (stmt, 2);
+	      tree len = gimple_call_arg (stmt, sizargno);
 	      if (!tree_fits_shwi_p (len))
 		return false;
 	      tree rbase = ref->base;
diff --git a/gcc/tree-ssa-dse.c b/gcc/tree-ssa-dse.c
index 70c8b07..359ef8c 100644
--- a/gcc/tree-ssa-dse.c
+++ b/gcc/tree-ssa-dse.c
@@ -92,22 +92,40 @@ initialize_ao_ref_for_dse (gimple *stmt, ao_ref *write)
   /* It's advantageous to handle certain mem* functions.  */
   if (gimple_call_builtin_p (stmt, BUILT_IN_NORMAL))
     {
+      /* The zero-based argument number of the destination pointer and
+	 the byte count of the raw memory call.  */
+      unsigned dstargno;
+      unsigned sizargno;
+
       switch (DECL_FUNCTION_CODE (gimple_call_fndecl (stmt)))
 	{
+  	  case BUILT_IN_BCOPY:
+	    dstargno = 1;
+	    sizargno = 2;
+	    break;
+
+	  case BUILT_IN_BZERO:
+	    dstargno = 0;
+	    sizargno = 1;
+	    break;
+
 	  case BUILT_IN_MEMCPY:
 	  case BUILT_IN_MEMMOVE:
 	  case BUILT_IN_MEMSET:
-	    {
-	      tree size = NULL_TREE;
-	      if (gimple_call_num_args (stmt) == 3)
-		size = gimple_call_arg (stmt, 2);
-	      tree ptr = gimple_call_arg (stmt, 0);
-	      ao_ref_init_from_ptr_and_size (write, ptr, size);
-	      return true;
-	    }
-	  default:
+	    dstargno = 0;
+	    sizargno = 2;
 	    break;
+
+	  default:
+	    return false;
 	}
+
+      tree size = NULL_TREE;
+      if (gimple_call_num_args (stmt) > sizargno)
+	size = gimple_call_arg (stmt, sizargno);
+      tree ptr = gimple_call_arg (stmt, dstargno);
+      ao_ref_init_from_ptr_and_size (write, ptr, size);
+      return true;
     }
   else if (is_gimple_assign (stmt))
     {
@@ -349,9 +367,9 @@ maybe_trim_constructor_store (ao_ref *ref, sbitmap live, gimple *stmt)
 /* STMT is a memcpy, memmove or memset.  Decrement the number of bytes
    copied/set by DECREMENT.  */
 static void
-decrement_count (gimple *stmt, int decrement)
+decrement_count (gimple *stmt, int decrement, unsigned argno)
 {
-  tree *countp = gimple_call_arg_ptr (stmt, 2);
+  tree *countp = gimple_call_arg_ptr (stmt, argno);
   gcc_assert (TREE_CODE (*countp) == INTEGER_CST);
   *countp = wide_int_to_tree (TREE_TYPE (*countp), (TREE_INT_CST_LOW (*countp)
 						    - decrement));
@@ -391,8 +409,19 @@ increment_start_addr (gimple *stmt, tree *where, int increment)
 static void
 maybe_trim_memstar_call (ao_ref *ref, sbitmap live, gimple *stmt)
 {
+  /* The zero-based argument number of the destination and soutce pointers,
+     and the byte count of the raw memory call.  */
+  unsigned dstargno = 0;
+  unsigned srcargno = 1;
+  unsigned sizargno = 2;
+
   switch (DECL_FUNCTION_CODE (gimple_call_fndecl (stmt)))
     {
+    case BUILT_IN_BCOPY:
+      dstargno = 1;
+      srcargno = 0;
+      /* Fall through.  */
+
     case BUILT_IN_MEMCPY:
     case BUILT_IN_MEMMOVE:
       {
@@ -401,20 +430,24 @@ maybe_trim_memstar_call (ao_ref *ref, sbitmap live, gimple *stmt)
 
 	/* Tail trimming is easy, we can just reduce the count.  */
         if (tail_trim)
-	  decrement_count (stmt, tail_trim);
+	  decrement_count (stmt, tail_trim, sizargno);
 
 	/* Head trimming requires adjusting all the arguments.  */
         if (head_trim)
           {
-	    tree *dst = gimple_call_arg_ptr (stmt, 0);
+	    tree *dst = gimple_call_arg_ptr (stmt, dstargno);
 	    increment_start_addr (stmt, dst, head_trim);
-	    tree *src = gimple_call_arg_ptr (stmt, 1);
+	    tree *src = gimple_call_arg_ptr (stmt, srcargno);
 	    increment_start_addr (stmt, src, head_trim);
-	    decrement_count (stmt, head_trim);
+	    decrement_count (stmt, head_trim, sizargno);
 	  }
         break;
       }
 
+    case BUILT_IN_BZERO:
+      sizargno = 1;
+      /* Fall through.  */
+
     case BUILT_IN_MEMSET:
       {
 	int head_trim, tail_trim;
@@ -422,14 +455,14 @@ maybe_trim_memstar_call (ao_ref *ref, sbitmap live, gimple *stmt)
 
 	/* Tail trimming is easy, we can just reduce the count.  */
         if (tail_trim)
-	  decrement_count (stmt, tail_trim);
+	  decrement_count (stmt, tail_trim, sizargno);
 
 	/* Head trimming requires adjusting all the arguments.  */
         if (head_trim)
           {
 	    tree *dst = gimple_call_arg_ptr (stmt, 0);
 	    increment_start_addr (stmt, dst, head_trim);
-	    decrement_count (stmt, head_trim);
+	    decrement_count (stmt, head_trim, sizargno);
 	  }
 	break;
       }
@@ -707,8 +740,17 @@ dse_dom_walker::dse_optimize_stmt (gimple_stmt_iterator *gsi)
      some builtin calls.  */
   if (gimple_call_builtin_p (stmt, BUILT_IN_NORMAL))
     {
+      /* The zero-based argument number of the byte count of the raw
+	 memory call.  */
+      unsigned sizargno = 2;
+
       switch (DECL_FUNCTION_CODE (gimple_call_fndecl (stmt)))
 	{
+	  case BUILT_IN_BZERO:
+	    sizargno = 1;
+	    /* Fall through.  */
+
+	  case BUILT_IN_BCOPY:
 	  case BUILT_IN_MEMCPY:
 	  case BUILT_IN_MEMMOVE:
 	  case BUILT_IN_MEMSET:
@@ -716,7 +758,7 @@ dse_dom_walker::dse_optimize_stmt (gimple_stmt_iterator *gsi)
 	      /* Occasionally calls with an explicit length of zero
 		 show up in the IL.  It's pointless to do analysis
 		 on them, they're trivially dead.  */
-	      tree size = gimple_call_arg (stmt, 2);
+	      tree size = gimple_call_arg (stmt, sizargno);
 	      if (integer_zerop (size))
 		{
 		  delete_dead_call (gsi);
diff --git a/gcc/tree-ssa-sccvn.c b/gcc/tree-ssa-sccvn.c
index c140c35..3a9541d 100644
--- a/gcc/tree-ssa-sccvn.c
+++ b/gcc/tree-ssa-sccvn.c
@@ -1882,11 +1882,13 @@ vn_reference_lookup_3 (ao_ref *ref, tree vuse, void *vr_,
 
   /* def_stmt may-defs *ref.  See if we can derive a value for *ref
      from that definition.
-     1) Memset.  */
+     1) Bzero and memset.  */
+  bool memset_p = false;
   if (is_gimple_reg_type (vr->type)
-      && gimple_call_builtin_p (def_stmt, BUILT_IN_MEMSET)
-      && integer_zerop (gimple_call_arg (def_stmt, 1))
-      && tree_fits_uhwi_p (gimple_call_arg (def_stmt, 2))
+      && (((memset_p = gimple_call_builtin_p (def_stmt, BUILT_IN_MEMSET))
+	   && integer_zerop (gimple_call_arg (def_stmt, 1))
+	   && tree_fits_uhwi_p (gimple_call_arg (def_stmt, 2)))
+	  || gimple_call_builtin_p (def_stmt, BUILT_IN_BZERO))
       && TREE_CODE (gimple_call_arg (def_stmt, 0)) == ADDR_EXPR)
     {
       tree ref2 = TREE_OPERAND (gimple_call_arg (def_stmt, 0), 0);
@@ -1895,9 +1897,10 @@ vn_reference_lookup_3 (ao_ref *ref, tree vuse, void *vr_,
       bool reverse;
       base2 = get_ref_base_and_extent (ref2, &offset2, &size2, &maxsize2,
 				       &reverse);
-      size2 = tree_to_uhwi (gimple_call_arg (def_stmt, 2)) * 8;
-      if ((unsigned HOST_WIDE_INT)size2 / 8
-	  == tree_to_uhwi (gimple_call_arg (def_stmt, 2))
+      /* The byte count is argument 2 for bzero and 3 for memset.  */
+      tree sizarg = gimple_call_arg (def_stmt, 1 + memset_p);
+      size2 = tree_to_uhwi (sizarg) * 8;
+      if ((unsigned HOST_WIDE_INT)size2 / 8 == tree_to_uhwi (sizarg)
 	  && maxsize2 != -1
 	  && operand_equal_p (base, base2, 0)
 	  && offset2 <= offset
diff --git a/gcc/tree-ssa-structalias.c b/gcc/tree-ssa-structalias.c
index a4abd28..b2ea8c2 100644
--- a/gcc/tree-ssa-structalias.c
+++ b/gcc/tree-ssa-structalias.c
@@ -4337,10 +4337,13 @@ find_func_aliases_for_builtin_call (struct function *fn, gcall *t)
 	  process_all_all_constraints (lhsc, rhsc);
 	  return true;
 	}
+      case BUILT_IN_BZERO:
       case BUILT_IN_MEMSET:
       case BUILT_IN_MEMSET_CHK:
       case BUILT_IN_TM_MEMSET:
 	{
+	  bool zero_p = DECL_FUNCTION_CODE (fndecl) == BUILT_IN_BZERO;
+
 	  tree res = gimple_call_lhs (t);
 	  tree dest = gimple_call_arg (t, 0);
 	  unsigned i;
@@ -4356,7 +4359,8 @@ find_func_aliases_for_builtin_call (struct function *fn, gcall *t)
 	  get_constraint_for_ptr_offset (dest, NULL_TREE, &lhsc);
 	  do_deref (&lhsc);
 	  if (flag_delete_null_pointer_checks
-	      && integer_zerop (gimple_call_arg (t, 1)))
+	      && (zero_p
+		  || integer_zerop (gimple_call_arg (t, 1))))
 	    {
 	      ac.type = ADDRESSOF;
 	      ac.var = nothing_id;
@@ -5164,6 +5168,7 @@ find_func_clobbers (struct function *fn, gimple *origt)
 	    }
 	  /* The following function clobbers memory pointed to by
 	     its argument.  */
+	  case BUILT_IN_BZERO:
 	  case BUILT_IN_MEMSET:
 	  case BUILT_IN_MEMSET_CHK:
 	  case BUILT_IN_POSIX_MEMALIGN:

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH] handle bzero/bcopy in DSE and aliasing (PR 80933, 80934)
  2017-06-01 20:53 [PATCH] handle bzero/bcopy in DSE and aliasing (PR 80933, 80934) Martin Sebor
@ 2017-06-02 11:12 ` Richard Biener
  2017-06-04 15:36   ` Bernhard Reutner-Fischer
  2017-06-07  3:26   ` Martin Sebor
  0 siblings, 2 replies; 18+ messages in thread
From: Richard Biener @ 2017-06-02 11:12 UTC (permalink / raw)
  To: Martin Sebor; +Cc: Gcc Patch List

  On Thu, Jun 1, 2017 at 10:52 PM, Martin Sebor <msebor@gmail.com> wrote:
> While testing some otherwise unrelated enhancements in these areas
> I noticed that calls to bzero and bcopy are not being handled as
> efficiently as equivalent calls to memset and memcpy.  Specifically,
> redundant calls are not eliminated and the functions appear to be
> treated as if they allowed their pointer arguments to escape.  This
> turned out to be due to the missing handling of the former two built
> ins by the DSE and aliasing passes.
>
> The attached patch adds this handling so the cases I noted in the two
> PRs are now handled.

+  /* The number of the size argument to one of the built-in functions
+     below.  */
+  unsigned sizargno = 2;
   /* Handle those builtin functions explicitly that do not act as
      escape points.  See tree-ssa-structalias.c:find_func_aliases
      for the list of builtins we might need to handle here.  */
@@ -2030,8 +2034,13 @@ call_may_clobber_ref_p_1 (gcall *call, ao_ref *ref)
       && gimple_call_builtin_p (call, BUILT_IN_NORMAL))
     switch (DECL_FUNCTION_CODE (callee))
       {
-       /* All the following functions clobber memory pointed to by
-          their first argument.  */
+        case BUILT_IN_BZERO:
+         sizargno = 1;
+         /* Fall through.  */
+
+         /* With the exception of the bzero function above, all of
+            the following clobber memory pointed to by their first
+            argument.  */
        case BUILT_IN_STRCPY:
        case BUILT_IN_STRNCPY:
        case BUILT_IN_MEMCPY:
@@ -2062,9 +2071,9 @@ call_may_clobber_ref_p_1 (gcall *call, ao_ref *ref)
               is strlen (dest) + n + 1 instead of n, resp.
               n + 1 at dest + strlen (dest), but strlen (dest) isn't
               known.  */
-           if (gimple_call_num_args (call) == 3
+           if (gimple_call_num_args (call) > sizargno
                && DECL_FUNCTION_CODE (callee) != BUILT_IN_STRNCAT)
-             size = gimple_call_arg (call, 2);
+             size = gimple_call_arg (call, sizargno);
            ao_ref_init_from_ptr_and_size (&dref,
                                           gimple_call_arg (call, 0),
                                           size);

please insted do

        if (DECL_FUNCTION_CODE (callee) == BUILT_IN_BZERO)
          size = gimple_call_Arg (call, 1);
        else if (gimple_call_num_args (call) == 3
...

instead of the above changes.  Likewise in stmt_kills_ref_p.

Likewise in initialize_ao_ref_for_dse I see no value in uglifying the code
but instead do

   case BUILT_IN_BCOPY:
      {
        ao_ref_init_from_ptr_and_size (write, gimple_call_arg (stmt,
1), gimple_call_arg (stmt, 2));
        return true;
      }

and similar for BZERO.

Similar in maybe_trim_memstar_call, for decrement_count simply pass in
the tree *
instead of the argno (and you can get rid of the stmt as well.

+     1) Bzero and memset.  */
+  bool memset_p = false;
   if (is_gimple_reg_type (vr->type)
-      && gimple_call_builtin_p (def_stmt, BUILT_IN_MEMSET)
-      && integer_zerop (gimple_call_arg (def_stmt, 1))
-      && tree_fits_uhwi_p (gimple_call_arg (def_stmt, 2))
+      && (((memset_p = gimple_call_builtin_p (def_stmt, BUILT_IN_MEMSET))
+          && integer_zerop (gimple_call_arg (def_stmt, 1))
+          && tree_fits_uhwi_p (gimple_call_arg (def_stmt, 2)))
+         || gimple_call_builtin_p (def_stmt, BUILT_IN_BZERO))

you miss the tree_fits_uhwi_p check for bzero.

Note I'd be _much_ more sympathetic to simply canonicalizing all of
bzero and bcopy
to memset / memmove and be done with all the above complexity.

Richard.

> Tested on x86_64-linux.
>
> Martin

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH] handle bzero/bcopy in DSE and aliasing (PR 80933, 80934)
  2017-06-02 11:12 ` Richard Biener
@ 2017-06-04 15:36   ` Bernhard Reutner-Fischer
  2017-06-06 16:54     ` Jeff Law
  2017-06-07  3:26   ` Martin Sebor
  1 sibling, 1 reply; 18+ messages in thread
From: Bernhard Reutner-Fischer @ 2017-06-04 15:36 UTC (permalink / raw)
  To: gcc-patches, Richard Biener, Martin Sebor; +Cc: Gcc Patch List

On 2 June 2017 13:12:41 CEST, Richard Biener <richard.guenther@gmail.com> wrote:

>Note I'd be _much_ more sympathetic to simply canonicalizing all of
>bzero and bcopy
>to memset / memmove and be done with all the above complexity.

Indeed and even more so since SUSv3 marked it LEGACY and both were removed in SUSv4.
thanks,

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH] handle bzero/bcopy in DSE and aliasing (PR 80933, 80934)
  2017-06-04 15:36   ` Bernhard Reutner-Fischer
@ 2017-06-06 16:54     ` Jeff Law
  0 siblings, 0 replies; 18+ messages in thread
From: Jeff Law @ 2017-06-06 16:54 UTC (permalink / raw)
  To: Bernhard Reutner-Fischer, gcc-patches, Richard Biener, Martin Sebor

On 06/04/2017 09:36 AM, Bernhard Reutner-Fischer wrote:
> On 2 June 2017 13:12:41 CEST, Richard Biener <richard.guenther@gmail.com> wrote:
> 
>> Note I'd be _much_ more sympathetic to simply canonicalizing all of
>> bzero and bcopy
>> to memset / memmove and be done with all the above complexity.
> 
> Indeed and even more so since SUSv3 marked it LEGACY and both were removed in SUSv4.
> thanks,
Likewise.
jeff

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH] handle bzero/bcopy in DSE and aliasing (PR 80933, 80934)
  2017-06-02 11:12 ` Richard Biener
  2017-06-04 15:36   ` Bernhard Reutner-Fischer
@ 2017-06-07  3:26   ` Martin Sebor
  2017-06-07  8:23     ` Richard Biener
  1 sibling, 1 reply; 18+ messages in thread
From: Martin Sebor @ 2017-06-07  3:26 UTC (permalink / raw)
  To: Richard Biener; +Cc: Gcc Patch List

[-- Attachment #1: Type: text/plain, Size: 1569 bytes --]

> Note I'd be _much_ more sympathetic to simply canonicalizing all of
> bzero and bcopy
> to memset / memmove and be done with all the above complexity.

Attached is an updated patch along these lines.  Please let me
know if it matches your expectations.

FWIW, although I don't feel too strongly about bzero et al. I'm
not sure that this approach is the right one in general.  It might
(slightly) simplify GCC itself, but other than the incidental code
generation improvement, it offers no benefit to users.  In some
cases, it even degrades user experience by causing GCC issue
diagnostics that refer to functions that don't appear in the source
code, such as for:

   char d[1];

   void* f (const void *p)
   {
     bzero (d, 7);
   }

   warning: ‘__builtin_memset’ writing 7 bytes into a region of size 1 
overflows the destination [-Wstringop-overflow=]

For some functions like mempcpy it might even worse code overall
(slower and bigger).

In other cases (like profiling) it loses interesting information.

I think these types of transformations would be justified  f they
were done based on measurably improved efficiency of the generated
code, but I'm uneasy about swapping calls to one function for another
solely because it simplifies the implementation.  Not least because
it doesn't seem like a viable general approach to simplifying the
implementation.

Martin

PS I stopped short of simplifying GCC to remove the existing special
handling of these three built-ins.  If the patch is approved I'm
willing to do the cleanup in a subsequent pass.

[-- Attachment #2: gcc-80560.diff --]
[-- Type: text/x-patch, Size: 93226 bytes --]

diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index 37bb236..363d104 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -804,6 +804,10 @@ Wnon-template-friend
 C++ ObjC++ Var(warn_nontemplate_friend) Init(1) Warning
 Warn when non-templatized friend functions are declared within a template.
 
+Wclass-memaccess
+C++ ObjC++ Var(warn_class_memaccess) Warning LangEnabledBy(C++ ObjC++, Wall)
+Warn for unsafe raw memory writes to objects of class types.
+
 Wnon-virtual-dtor
 C++ ObjC++ Var(warn_nonvdtor) Warning LangEnabledBy(C++ ObjC++,Weffc++)
 Warn about non-virtual destructors.
diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index 51260f0..6d8e77e 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -8146,6 +8146,396 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain)
   return call;
 }
 
+/* Return the DECL of the first non-public data member of class TYPE
+   or null if none can be found.  */
+
+static tree
+first_non_public_field (tree type)
+{
+  if (!CLASS_TYPE_P (type))
+    return NULL_TREE;
+
+  for (tree field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field))
+    {
+      if (TREE_CODE (field) != FIELD_DECL)
+	continue;
+      if (TREE_STATIC (field))
+	continue;
+      if (TREE_PRIVATE (field) || TREE_PROTECTED (field))
+	return field;
+    }
+
+  int i = 0;
+
+  for (tree base_binfo, binfo = TYPE_BINFO (type);
+       BINFO_BASE_ITERATE (binfo, i, base_binfo); i++)
+    {
+      tree base = TREE_TYPE (base_binfo);
+
+      if (tree field = first_non_public_field (base))
+	return field;
+    }
+
+  return NULL_TREE;
+}
+
+/* Return true if all copy and move assignment operator overloads for
+   class TYPE are trivial and at least one of them is not deleted and,
+   when ACCESS is set, accessible.  Return false otherwise.  Set
+   HASASSIGN to true when the TYPE has a (not necessarily trivial)
+   copy or move assignment.  */
+
+static bool
+has_trivial_copy_assign_p (tree type, bool access, bool *hasassign)
+{
+  tree fns = cp_assignment_operator_id (NOP_EXPR);
+  fns = lookup_fnfields_slot (type, fns);
+
+  bool all_trivial = true;
+
+  /* Iterate over copy and move assignment overloads.  */
+
+  for (ovl_iterator oi (fns); oi; ++oi)
+    {
+      tree f = *oi;
+
+      bool accessible = !access || !(TREE_PRIVATE (f) || TREE_PROTECTED (f));
+
+      /* Skip template assignment operators and deleted functions.  */
+      if (TREE_CODE (f) != FUNCTION_DECL || DECL_DELETED_FN (f))
+	continue;
+
+      if (accessible)
+	*hasassign = true;
+
+      if (!accessible || !trivial_fn_p (f))
+	all_trivial = false;
+
+      /* Break early when both properties have been determined.  */
+      if (*hasassign && !all_trivial)
+	break;
+    }
+
+  /* Return true if they're all trivial and one of the expressions
+     TYPE() = TYPE() or TYPE() = (TYPE&)() is valid.  */
+  tree ref = cp_build_reference_type (type, false);
+  return (all_trivial
+	  && (is_trivially_xible (MODIFY_EXPR, type, type)
+	      || is_trivially_xible (MODIFY_EXPR, type, ref)));
+}
+
+/* Return true if all copy and move ctor overloads for class TYPE are
+   trivial and at least one of them is not deleted and, when ACCESS is
+   set, accessible.  Return false otherwise.  Set each element of HASCTOR[]
+   to true when the TYPE has a (not necessarily trivial) default and copy
+   (or move) ctor, respectively.  */
+
+static bool
+has_trivial_copy_p (tree type, bool access, bool hasctor[2])
+{
+  tree fns = lookup_fnfields_slot (type, complete_ctor_identifier);
+
+  bool all_trivial = true;
+
+  for (ovl_iterator oi (fns); oi; ++oi)
+    {
+      tree f = *oi;
+
+      /* Skip template constructors.  */
+      if (TREE_CODE (f) != FUNCTION_DECL)
+	continue;
+
+      tree arg = TREE_CHAIN (DECL_ARGUMENTS (f));
+      bool cpy_or_move_ctor_p;
+      if (arg)
+	{
+	  tree argtype = TREE_TYPE (arg);
+	  if (TREE_CODE (argtype) == REFERENCE_TYPE)
+	    argtype = TREE_TYPE (argtype);
+
+	  if (!same_type_ignoring_top_level_qualifiers_p (type, argtype))
+	    continue;
+
+	  cpy_or_move_ctor_p = true;
+	}
+      else
+	cpy_or_move_ctor_p = false;
+
+      if (DECL_DELETED_FN (f))
+	continue;
+
+      bool accessible = !access || !(TREE_PRIVATE (f) || TREE_PROTECTED (f));
+
+      if (accessible)
+	hasctor[cpy_or_move_ctor_p] = true;
+
+      if (cpy_or_move_ctor_p && (!accessible || !trivial_fn_p (f)))
+	all_trivial = false;
+
+      /* Break early when both properties have been determined.  */
+      if (hasctor[0] && hasctor[1] && !all_trivial)
+	break;
+    }
+
+  return all_trivial;
+}
+
+/* issue a warning on a call to the built-in function FNDECL if it is
+   a raw memory write whose destination is not an object of (something
+   like) trivial or standard layout type with a non-deleted assignment
+   and copy ctor.  Detects const correctness violations, corrupting
+   references, virtual table pointers, and bypassing non-trivial
+   assignments.  */
+
+static void
+maybe_warn_class_memaccess (location_t loc, tree fndecl, tree *args)
+{
+  /* Except for bcopy where it's second, the destination pointer is
+     the first argument for all functions handled here.  Compute
+     the index of the destination and source arguments.  */
+  unsigned dstidx = DECL_FUNCTION_CODE (fndecl) == BUILT_IN_BCOPY;
+  unsigned srcidx = !dstidx;
+
+  tree dest = args[dstidx];
+  if (!dest || !TREE_TYPE (dest) || !POINTER_TYPE_P (TREE_TYPE (dest)))
+    return;
+
+  STRIP_NOPS (dest);
+
+  tree srctype = NULL_TREE;
+
+  /* Determine the type of the pointed-to object and whether it's
+     a complete class type.  */
+  tree desttype = TREE_TYPE (TREE_TYPE (dest));
+
+  if (!desttype || !COMPLETE_TYPE_P (desttype) || !CLASS_TYPE_P (desttype))
+    return;
+
+  /* Check to see if the raw memory call is made by a ctor or dtor
+     with this as the destination argument for the destination type.
+     If so, be more permissive.  */
+  if (current_function_decl
+      && (DECL_CONSTRUCTOR_P (current_function_decl)
+	  || DECL_DESTRUCTOR_P (current_function_decl))
+      && is_this_parameter (dest))
+    {
+      tree ctx = DECL_CONTEXT (current_function_decl);
+      bool special = same_type_ignoring_top_level_qualifiers_p (ctx, desttype);
+
+      tree binfo = TYPE_BINFO (ctx);
+
+      /* A ctor and dtor for a class with no bases and no virtual functions
+	 can do whatever they want.  Bail early with no further checking.  */
+      if (special && !BINFO_VTABLE (binfo) && !BINFO_N_BASE_BINFOS (binfo))
+	return;
+    }
+
+  /* True if the class is trivial.  */
+  bool trivial = trivial_type_p (desttype);
+
+  /* Set to true if DESTYPE has an accessible copy assignment.  */
+  bool hasassign = false;
+  /* True if all of the class' overloaded copy assignment operators
+     are all trivial (and not deleted) and at least one of them is
+     accessible.  */
+  bool trivassign = has_trivial_copy_assign_p (desttype, true, &hasassign);
+
+  /* Set to true if DESTTYPE has an accessible defqault and copy ctor,
+     respectively.  */
+  bool hasctors[2] = { false, false };
+
+  /* True if all of the class' overloaded copy constructors are all
+     trivial (and not deleted) and at least one of them is accessible.  */
+  bool trivcopy = has_trivial_copy_p (desttype, true, hasctors);
+
+  /* Set FLD to the first private/protected member of the class.  */
+  tree fld = trivial ? first_non_public_field (desttype) : NULL_TREE;
+
+  /* The warning format string.  */
+  const char *warnfmt = NULL;
+  /* A suggested alternative to offer instead of the raw memory call.
+     Empty string when none can be come up with.  */
+  const char *suggest = "";
+  bool warned = false;
+
+  switch (DECL_FUNCTION_CODE (fndecl))
+    {
+    case BUILT_IN_MEMSET:
+      if (!integer_zerop (args[1]))
+	{
+	  /* Diagnose setting non-copy-assignable or non-trivial types,
+	     or types with a private member, to (potentially) non-zero
+	     bytes.  Since the value of the bytes being written is unknown,
+	     suggest using assignment instead (if one exists).  Also warn
+	     for writes into objects for which zero-initialization doesn't
+	     mean all bits clear (pointer-to-member data, where null is all
+	     bits set).  Since the value being written is (most likely)
+	     non-zero, simply suggest assignment (but not copy assignment).  */
+	  suggest = "; use assignment instead";
+	  if (!trivassign)
+	    warnfmt = G_("%qD writing to an object of type %#qT with "
+			 "no trivial copy-assignment");
+	  else if (!trivial)
+	    warnfmt = G_("%qD writing to an object of non-trivial type %#qT%s");
+	  else if (fld)
+	    {
+	      const char *access = TREE_PRIVATE (fld) ? "private" : "protected";
+	      warned = warning_at (loc, OPT_Wclass_memaccess,
+				   "%qD writing to an object of type %#qT with "
+				   "%qs member %qD",
+				   fndecl, desttype, access, fld);
+	    }
+	  else if (!zero_init_p (desttype))
+	    warnfmt = G_("%qD writing to an object of type %#qT containing "
+			 "a pointer to data member%s");
+
+	  break;
+	}
+      /* Fall through.  */
+
+    case BUILT_IN_BZERO:
+      /* Similarly to the above, diagnose clearing non-trivial or non-
+	 standard layout objects, or objects of types with no assignmenmt.
+	 Since the value being written is known to be zero, suggest either
+	 copy assignment, copy ctor, or default ctor as an alternative,
+	 depending on what's available.  */
+
+      if (hasassign && hasctors[0])
+	suggest = G_("; use assignment or value-initialization instead");
+      else if (hasassign)
+	suggest = G_("; use assignment instead");
+      else if (hasctors[0])
+	suggest = G_("; use value-initialization instead");
+
+      if (!trivassign)
+	warnfmt = G_("%qD clearing an object of type %#qT with "
+		     "no trivial copy-assignment%s");
+      else if (!trivial)
+	warnfmt =  G_("%qD clearing an object of non-trivial type %#qT%s");
+      else if (!zero_init_p (desttype))
+	warnfmt = G_("%qD clearing an object of type %#qT containing "
+		     "a pointer-to-member%s");
+      break;
+
+    case BUILT_IN_BCOPY:
+    case BUILT_IN_MEMCPY:
+    case BUILT_IN_MEMMOVE:
+    case BUILT_IN_MEMPCPY:
+      /* Determine the type of the source object.  */
+      srctype = STRIP_NOPS (args[srcidx]);
+      srctype = TREE_TYPE (TREE_TYPE (srctype));
+
+      /* Since it's impossible to determine wheter the byte copy is
+	 being used in place of assignment to an existing object or
+	 as a substitute for initialization, assume it's the former.
+	 Determine the best alternative to use instead depending on
+	 what's not deleted.  */
+      if (hasassign && hasctors[1])
+	suggest = G_("; use copy-assignment or copy-initialization instead");
+      else if (hasassign)
+	suggest = G_("; use copy-assignment instead");
+      else if (hasctors[1])
+	suggest = G_("; use copy-initialization instead");
+
+      if (!trivassign)
+	warnfmt = G_("%qD writing to an object of type %#qT with no trivial "
+		     "copy-assignment%s");
+      else if (!trivially_copyable_p (desttype))
+	warnfmt = G_("%qD writing to an object of non-trivially copyable "
+		     "type %#qT%s");
+      else if (!trivcopy)
+	warnfmt = G_("%qD writing to an object with a deleted copy constructor");
+
+      else if (!trivial
+	       && !VOID_TYPE_P (srctype)
+	       && !char_type_p (TYPE_MAIN_VARIANT (srctype))
+	       && !same_type_ignoring_top_level_qualifiers_p (desttype,
+							      srctype))
+	{
+	  /* Warn when copying into a non-trivial object from an object
+	     of a different type other than void or char.  */
+	  warned = warning_at (loc, OPT_Wclass_memaccess,
+			       "%qD copying an object of non-trivial type "
+			       "%#qT from an array of %#qT",
+			       fndecl, desttype, srctype);
+	}
+      else if (fld
+	       && !VOID_TYPE_P (srctype)
+	       && !char_type_p (TYPE_MAIN_VARIANT (srctype))
+	       && !same_type_ignoring_top_level_qualifiers_p (desttype,
+							      srctype))
+	{
+	  const char *access = TREE_PRIVATE (fld) ? "private" : "protected";
+	  warned = warning_at (loc, OPT_Wclass_memaccess,
+			       "%qD copying an object of type %#qT with "
+			       "%qs member %qD from an array of %#qT; use "
+			       "assignment or copy-initialization instead",
+			       fndecl, desttype, access, fld, srctype);
+	}
+      else if (!trivial && TREE_CODE (args[2]) == INTEGER_CST)
+	{
+	  /* Finally, warn on partial copies.  */
+	  unsigned HOST_WIDE_INT typesize
+	    = tree_to_uhwi (TYPE_SIZE_UNIT (desttype));
+	  if (unsigned HOST_WIDE_INT partial
+	      = tree_to_uhwi (args[2]) % typesize)
+	    warned = warning_at (loc, OPT_Wclass_memaccess,
+				 (typesize - partial > 1
+				  ? G_("%qD writing to an object of "
+				       "a non-trivial type %#qT leaves %wu "
+				       "bytes unchanged")
+				  : G_("%qD writing to an object of "
+				       "a non-trivial type %#qT leaves %wu "
+				       "byte unchanged")),
+				 fndecl, desttype, typesize - partial);
+	}
+      break;
+
+    case BUILT_IN_REALLOC:
+
+      if (!trivially_copyable_p (desttype))
+	warnfmt = G_("%qD moving an object of non-trivially copyable type "
+		     "%#qT; use %<new%> and %<delete%> instead");
+      else if (!trivcopy)
+	warnfmt = G_("%qD moving an object of type %#qT with deleted copy "
+		     "constructor; use %<new%> and %<delete%> instead");
+      else if (!get_dtor (desttype, tf_none))
+	warnfmt = G_("%qD moving an object of type %#qT with deleted "
+		     "destructor");
+      else if (!trivial
+	       && TREE_CODE (args[1]) == INTEGER_CST
+	       && tree_int_cst_lt (args[1], TYPE_SIZE_UNIT (desttype)))
+	{
+	  /* Finally, warn on reallocation into insufficient space.  */
+	  warned = warning_at (loc, OPT_Wclass_memaccess,
+			       "%qD moving an object of non-trivial type "
+			       "%#qT and size %E into a region of size %E",
+			       fndecl, desttype, TYPE_SIZE_UNIT (desttype),
+			       args[1]);
+	}
+      break;
+
+    default:
+      return;
+    }
+
+  if (!warned && !warnfmt)
+    return;
+
+  if (warnfmt)
+    {
+      if (suggest)
+	warned = warning_at (loc, OPT_Wclass_memaccess,
+			     warnfmt, fndecl, desttype, suggest);
+      else
+	warned = warning_at (loc, OPT_Wclass_memaccess,
+			     warnfmt, fndecl, desttype);
+    }
+
+  if (warned)
+    inform (location_of (desttype), "%#qT declared here", desttype);
+}
+
 /* Build and return a call to FN, using NARGS arguments in ARGARRAY.
    This function performs no overload resolution, conversion, or other
    high-level operations.  */
@@ -8178,6 +8568,10 @@ build_cxx_call (tree fn, int nargs, tree *argarray,
       if (!check_builtin_function_arguments (EXPR_LOCATION (fn), vNULL, fndecl,
 					     nargs, argarray))
 	return error_mark_node;
+
+      /* Warn if the built-in writes to an object of a non-trivial type.  */
+      if (nargs)
+	maybe_warn_class_memaccess (loc, fndecl, argarray);
     }
 
     /* If it is a built-in array notation function, then the return type of
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 4a83a3e..475673c 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -215,7 +215,8 @@ in the following sections.
 -Wabi=@var{n}  -Wabi-tag  -Wconversion-null  -Wctor-dtor-privacy @gol
 -Wdelete-non-virtual-dtor  -Wliteral-suffix  -Wmultiple-inheritance @gol
 -Wnamespaces  -Wnarrowing @gol
--Wnoexcept  -Wnoexcept-type  -Wnon-virtual-dtor  -Wreorder  -Wregister @gol
+-Wnoexcept  -Wnoexcept-type  -Wclass-memaccess @gol
+-Wnon-virtual-dtor  -Wreorder  -Wregister @gol
 -Weffc++  -Wstrict-null-sentinel  -Wtemplates @gol
 -Wno-non-template-friend  -Wold-style-cast @gol
 -Woverloaded-virtual  -Wno-pmf-conversions @gol
@@ -2920,6 +2921,23 @@ void g() noexcept;
 void h() @{ f(g); @} // in C++14 calls f<void(*)()>, in C++1z calls f<void(*)()noexcept>
 @end smallexample
 
+@item -Wclass-memaccess @r{(C++ and Objective-C++ only)}
+@opindex Wclass-memaccess
+Warn when the destination of a call to a raw memory function such as
+@code{memset} or @code{memcpy} is an object of class type writing into which
+might bypass the class non-trivial or deleted constructor or copy assignment,
+violate const-correctness or encapsulation, or corrupt the virtual table.
+Modifying the representation of such objects may violate invariants maintained
+by member functions of the class.  For example, the call to @code{memset}
+below is undefined becase it modifies a non-trivial class object and is,
+therefore, diagnosed.  The safe way to either initialize or clear the storage
+of objects of such types is by using the appropriate constructor or assignment
+operator, if one is available.
+@smallexample
+std::string str = "abc";
+memset (&str, 0, 3);
+@end smallexample
+The @option{-Wclass-memaccess} option is enabled by @option{-Wall}.
 
 @item -Wnon-virtual-dtor @r{(C++ and Objective-C++ only)}
 @opindex Wnon-virtual-dtor
diff --git a/gcc/dumpfile.c b/gcc/dumpfile.c
index b8bda3c..b336d5b 100644
--- a/gcc/dumpfile.c
+++ b/gcc/dumpfile.c
@@ -185,9 +185,16 @@ dump_register (const char *suffix, const char *swtch, const char *glob,
       m_extra_dump_files = XRESIZEVEC (struct dump_file_info,
 				       m_extra_dump_files,
 				       m_extra_dump_files_alloced);
+
+      /* Construct a new object in the space allocated above.  */
+      new (m_extra_dump_files + count) dump_file_info ();
+    }
+  else
+    {
+      /* Zero out the already constructed object.  */
+      m_extra_dump_files[count] = dump_file_info ();
     }
 
-  memset (&m_extra_dump_files[count], 0, sizeof (struct dump_file_info));
   m_extra_dump_files[count].suffix = suffix;
   m_extra_dump_files[count].swtch = swtch;
   m_extra_dump_files[count].glob = glob;
diff --git a/gcc/gcc.c b/gcc/gcc.c
index 4724276..a65bdbf 100644
--- a/gcc/gcc.c
+++ b/gcc/gcc.c
@@ -1259,9 +1259,9 @@ struct compiler
   const char *cpp_spec;         /* If non-NULL, substitute this spec
 				   for `%C', rather than the usual
 				   cpp_spec.  */
-  const int combinable;          /* If nonzero, compiler can deal with
+  int combinable;               /* If nonzero, compiler can deal with
 				    multiple source files at once (IMA).  */
-  const int needs_preprocessing; /* If nonzero, source files need to
+  int needs_preprocessing;       /* If nonzero, source files need to
 				    be run through a preprocessor.  */
 };
 
diff --git a/gcc/genattrtab.c b/gcc/genattrtab.c
index 3629b5f..51dfe77 100644
--- a/gcc/genattrtab.c
+++ b/gcc/genattrtab.c
@@ -4703,8 +4703,8 @@ gen_insn_reserv (md_rtx_info *info)
   struct insn_reserv *decl = oballoc (struct insn_reserv);
   rtx def = info->def;
 
-  struct attr_desc attr;
-  memset (&attr, 0, sizeof (attr));
+  struct attr_desc attr = { };
+
   attr.name = DEF_ATTR_STRING (XSTR (def, 0));
   attr.loc = info->loc;
 
diff --git a/gcc/hash-table.h b/gcc/hash-table.h
index 0f7e21a..443d16c 100644
--- a/gcc/hash-table.h
+++ b/gcc/hash-table.h
@@ -803,7 +803,10 @@ hash_table<Descriptor, Allocator>::empty_slow ()
       m_size_prime_index = nindex;
     }
   else
-    memset (entries, 0, size * sizeof (value_type));
+    {
+      for ( ; size; ++entries, --size)
+	*entries = value_type ();
+    }
   m_n_deleted = 0;
   m_n_elements = 0;
 }
diff --git a/gcc/ipa-cp.c b/gcc/ipa-cp.c
index 3c9c3f2..c7e3c71 100644
--- a/gcc/ipa-cp.c
+++ b/gcc/ipa-cp.c
@@ -1471,8 +1471,7 @@ allocate_and_init_ipcp_value (tree source)
 {
   ipcp_value<tree> *val;
 
-  val = ipcp_cst_values_pool.allocate ();
-  memset (val, 0, sizeof (*val));
+  val = new (ipcp_cst_values_pool.allocate ()) ipcp_value<tree>();
   val->value = source;
   return val;
 }
@@ -1486,8 +1485,8 @@ allocate_and_init_ipcp_value (ipa_polymorphic_call_context source)
   ipcp_value<ipa_polymorphic_call_context> *val;
 
   // TODO
-  val = ipcp_poly_ctx_values_pool.allocate ();
-  memset (val, 0, sizeof (*val));
+  val = new (ipcp_poly_ctx_values_pool.allocate ())
+    ipcp_value<ipa_polymorphic_call_context>();
   val->value = source;
   return val;
 }
diff --git a/gcc/ipa-prop.c b/gcc/ipa-prop.c
index c73ffd7..292f3e2 100644
--- a/gcc/ipa-prop.c
+++ b/gcc/ipa-prop.c
@@ -3711,7 +3711,7 @@ void
 ipa_free_edge_args_substructures (struct ipa_edge_args *args)
 {
   vec_free (args->jump_functions);
-  memset (args, 0, sizeof (*args));
+  *args = ipa_edge_args ();
 }
 
 /* Free all ipa_edge structures.  */
diff --git a/gcc/omp-low.c b/gcc/omp-low.c
index 9a16248..dd4a092 100644
--- a/gcc/omp-low.c
+++ b/gcc/omp-low.c
@@ -6320,7 +6320,11 @@ lower_omp_ordered_clauses (gimple_stmt_iterator *gsi_p, gomp_ordered *ord_stmt,
     return;
 
   wide_int *folded_deps = XALLOCAVEC (wide_int, 2 * len - 1);
-  memset (folded_deps, 0, sizeof (*folded_deps) * (2 * len - 1));
+
+  /* wide_int is not a POD so it must be default-constructed.  */
+  for (unsigned i = 0; i != 2 * len - 1; ++i)
+    new (static_cast<void*>(folded_deps + i)) wide_int ();
+
   tree folded_dep = NULL_TREE;
   /* TRUE if the first dimension's offset is negative.  */
   bool neg_offset_p = false;
diff --git a/gcc/params.h b/gcc/params.h
index b61cff9..8b91660 100644
--- a/gcc/params.h
+++ b/gcc/params.h
@@ -42,7 +42,7 @@ struct param_info
 {
   /* The name used with the `--param <name>=<value>' switch to set this
      value.  */
-  const char *const option;
+  const char *option;
 
   /* The default value.  */
   int default_value;
@@ -54,7 +54,7 @@ struct param_info
   int max_value;
 
   /* A short description of the option.  */
-  const char *const help;
+  const char *help;
 
   /* The optional names corresponding to the values.  */
   const char **value_names;
diff --git a/gcc/testsuite/g++.dg/Wclass-memaccess.C b/gcc/testsuite/g++.dg/Wclass-memaccess.C
new file mode 100644
index 0000000..0c5b0ca
--- /dev/null
+++ b/gcc/testsuite/g++.dg/Wclass-memaccess.C
@@ -0,0 +1,1574 @@
+/* PR c++/80560 - warn on undefined memory operations involving non-trivial
+   types
+   { dg-do compile }
+   { dg-options "-Wclass-memaccess -ftrack-macro-expansion=0" } */
+
+typedef __SIZE_TYPE__ size_t;
+
+extern "C"
+{
+void* memcpy (void*, const void*, size_t);
+void* memmove (void*, const void*, size_t);
+void* mempcpy (void*, const void*, size_t);
+void* memset (void*, int, size_t);
+void* realloc (void*, size_t);
+}
+
+/* Ordinary bzcopy and bzero aren't recognized as special.  */
+#define bcopy __builtin_bcopy
+#define bzero __builtin_bzero
+
+void sink (void*);
+
+#define T(fn, arglist) ((fn arglist), sink (p))
+
+#if !defined TEST || TEST == TEST_TRIVIAL
+
+/* Trivial can be manipulated by raw memory functions.  */
+struct Trivial
+{
+  int i; unsigned bf: 1; char *s; char a[4];
+
+  // Template assignment doesn't make the class non-trivial or not
+  // trivially assignable.
+  template <class U>
+  Trivial& operator= (U);
+};
+
+void test (Trivial *p, void *q, int x)
+{
+  const size_t n = x;
+
+  T (bzero, (p, 1));
+  T (bzero, (p, n));
+  T (bzero, (p, sizeof *p));
+  T (bzero, (q, 1));
+  T (bzero, (q, n));
+  T (bzero, (q, sizeof *p));
+
+  T (bcopy, (p, q, 1));
+  T (bcopy, (p, q, n));
+  T (bcopy, (p, q, sizeof *p));
+  T (bcopy, (q, p, 1));
+  T (bcopy, (q, p, n));
+  T (bcopy, (q, p, sizeof *p));
+
+  T (memcpy, (p, q, 1));
+  T (memcpy, (p, q, n));
+  T (memcpy, (p, q, sizeof *p));
+  T (memcpy, (q, p, 1));
+  T (memcpy, (q, p, n));
+  T (memcpy, (q, p, sizeof *p));
+
+  T (memset, (p, 0, 1));
+  T (memset, (p, 0, n));
+  T (memset, (p, 0, sizeof *p));
+  T (memset, (q, 0, 1));
+  T (memset, (q, 0, n));
+  T (memset, (q, 0, sizeof *p));
+
+  T (memset, (p, 1, 1));
+  T (memset, (p, 1, n));
+  T (memset, (p, 1, sizeof *p));
+  T (memset, (q, 1, 1));
+  T (memset, (q, 1, n));
+  T (memset, (q, 1, sizeof *p));
+
+  T (memset, (p, x, 1));
+  T (memset, (p, x, n));
+  T (memset, (p, x, sizeof *p));
+  T (memset, (q, x, 1));
+  T (memset, (q, x, n));
+  T (memset, (q, x, sizeof *p));
+
+  T (memmove, (p, q, 1));
+  T (memmove, (p, q, n));
+  T (memmove, (p, q, sizeof *p));
+  T (memmove, (q, p, 1));
+  T (memmove, (q, p, n));
+  T (memmove, (q, p, sizeof *p));
+
+  T (q = realloc, (p, 1));
+  T (q = realloc, (p, n));
+  T (q = realloc, (p, sizeof *p));
+
+  T (q = realloc, (q, 1));
+  T (q = realloc, (q, n));
+  T (q = realloc, (q, sizeof *p));
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_DEFAULT
+
+/* HasDefault is trivially copyable but should be initialized by
+   the ctor, not bzero or memset.  */
+struct HasDefault { char a[4]; HasDefault (); };
+
+void test (HasDefault *p, const HasDefault &x,
+	   void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // HasDefault is neither trivial nor standard-layout.  The warning
+  // should mention the former since it's more permissive than the latter
+  // and so more informative.
+  T (bzero, (p, sizeof *p));   // { dg-warning "bzero(\[^\n\r\]*). clearing an object of non-trivial type .struct HasDefault.; use assignment or value-initialization instead" }
+
+  T (memset, (p, 0, sizeof *p));   // { dg-warning ".void\\* memset(\[^\n\r\]*). clearing an object of non-trivial type .struct HasDefault.; use assignment or value-initialization instead" }
+
+  T (memset, (p, 1, sizeof *p));   // { dg-warning ".void\\* memset(\[^\n\r\]*). writing to an object of non-trivial type .struct HasDefault.; use assignment instead" }
+
+  T (memset, (p, i, sizeof *p));   // { dg-warning ".void\\* memset(\[^\n\r\]*). writing to an object of non-trivial type .struct HasDefault.; use assignment instead" }
+
+  // Copying from another object of the same type is fine.
+  T (bcopy, (&x, p, sizeof *p));
+  T (bcopy, (&x, p, n));
+
+  T (memcpy, (p, &x, sizeof *p));
+  T (memcpy, (p, &x, n));
+
+  // Copying from a void* or character buffer is also fine.
+  T (bcopy, (q, p, sizeof *p));
+  T (bcopy, (q, p, n));
+  T (bcopy, (s, p, sizeof *p));
+  T (bcopy, (s, p, n));
+
+  T (memcpy, (p, q, sizeof *p));
+  T (memcpy, (p, q, n));
+  T (memcpy, (p, s, sizeof *p));
+  T (memcpy, (p, s, n));
+
+  T (memmove, (p, q, sizeof *p));
+  T (memmove, (p, q, n));
+  T (memmove, (p, s, sizeof *p));
+  T (memmove, (p, s, n));
+
+  T (mempcpy, (p, q, sizeof *p));
+  T (mempcpy, (p, q, n));
+  T (mempcpy, (p, s, sizeof *p));
+  T (mempcpy, (p, s, n));
+
+  // ...but partial copies are diagnosed.
+  T (memcpy, (p, &x, 1));   // { dg-warning "writing to an object of a non-trivial type .struct HasDefault. leaves 3 bytes unchanged" } */
+  T (memmove, (p, q, 2));   // { dg-warning "writing to an object of a non-trivial type .struct HasDefault. leaves 2 bytes unchanged" } */
+  T (mempcpy, (p, q, 3));   // { dg-warning "writing to an object of a non-trivial type .struct HasDefault. leaves 1 byte unchanged" } */
+
+  // Otherwise, copying from an object of an unrelated type is diagnosed.
+  T (memcpy, (p, ia, sizeof *p));  // { dg-warning ".void\\* memcpy(\[^\n\r\]*). copying an object of non-trivial type .struct HasDefault. from an array of .const int." }
+  extern long *ip;
+  T (memcpy, (p, ip, sizeof *p));  // { dg-warning ".void\\* memcpy(\[^\n\r\]*). copying an object of non-trivial type .struct HasDefault. from an array of .long." }
+
+  T (memmove, (p, ia, sizeof *p)); // { dg-warning ".void\\* memmove(\[^\n\r\]*). copying an object of non-trivial type .struct HasDefault. from an array of .const int." }
+
+  T (mempcpy, (p, ia, sizeof *p)); // { dg-warning ".void\\* mempcpy(\[^\n\r\]*). copying an object of non-trivial type .struct HasDefault. from an array of .const int." }
+
+  // Reallocating is the same as calling memcpy except that only
+  // shrinking reallocation is diagnosed.
+  T (q = realloc, (p, 1));   // { dg-warning "moving an object of non-trivial type .struct HasDefault. and size 4 into a region of size 1" }
+  T (q = realloc, (p, n));
+  T (q = realloc, (p, sizeof *p));
+  T (q = realloc, (p, sizeof *p + 1));
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_TEMPLATE_DEFAULT
+
+/* HasTemplateDefault should be initialized by means of the ctor,
+   not zeroed out by bzero/memset.  */
+struct HasTemplateDefault
+{
+  template <class U>
+  HasTemplateDefault (U);
+};
+
+void test (HasTemplateDefault *p, const HasTemplateDefault &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // Zeroing out is diagnosed because value initialization is
+  // invalid (the template ctor makes default ctor unavailable).
+  T (bzero, (p, sizeof *p));        // { dg-warning "bzero" }
+  T (memset, (p, 0, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  // Copying from an object of any type is okay.
+  T (bcopy, (&x, p, sizeof *p));
+  T (bcopy, (q, p, sizeof *p));
+  T (bcopy, (s, p, sizeof *p));
+  T (bcopy, (ia, p, sizeof *p));    // { dg-warning "bcopy" }
+
+  T (memcpy, (p, &x, sizeof *p));
+  T (memcpy, (p, q, sizeof *p));
+  T (memcpy, (p, s, sizeof *p));
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+
+  T (memmove, (p, &x, sizeof *p));
+  T (memmove, (p, q, sizeof *p));
+  T (memmove, (p, s, sizeof *p));
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+
+  T (mempcpy, (p, &x, sizeof *p));
+  T (mempcpy, (p, q, sizeof *p));
+  T (mempcpy, (p, s, sizeof *p));
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+
+  // Reallocating is the same as calling memcpy.
+  T (q = realloc, (p, 1));
+  T (q = realloc, (p, n));
+  T (q = realloc, (p, sizeof *p));
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_COPY
+
+/* HasCopy should be copied using the copy ctor or assignment, not
+   by memcpy or memmove.  Since it's non-trivial, it should not be zeroed
+   out by bzero/memset either and should instead use assignment and/or
+   value initialization.  */
+struct HasCopy { int i; HasCopy (const HasCopy&); };
+
+void test (HasCopy *p, const HasCopy &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // Zeroing out is diagnosed because value initialization is
+  // invalid (the copy ctor makes no default ctor unavailable).
+  T (bzero, (p, sizeof *p));        // { dg-warning "bzero" }
+  T (memset, (p, 0, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  // Copying from an object of any type is diagnosed.
+  T (bcopy, (&x, p, sizeof *p));    // { dg-warning "bcopy" }
+  T (bcopy, (q, p, sizeof *p));     // { dg-warning "bcopy" }
+  T (bcopy, (s, p, sizeof *p));     // { dg-warning "bcopy" }
+  T (bcopy, (ia, p, sizeof *p));    // { dg-warning "bcopy" }
+
+  T (memcpy, (p, &x, sizeof *p));   // { dg-warning "memcpy" }
+  T (memcpy, (p, q, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, s, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+
+  T (memmove, (p, &x, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, q, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, s, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+
+  T (mempcpy, (p, &x, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, q, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+
+  // Reallocating is the same as calling memcpy.
+  T (q = realloc, (p, 1));          // { dg-warning "realloc" }
+  T (q = realloc, (p, n));          // { dg-warning "realloc" }
+  T (q = realloc, (p, sizeof *p));  // { dg-warning "realloc" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_PRIVATE_COPY
+
+/* HasPrivateCopy cannot be copied using memcpy or memmove.  Since it's
+   non-trivial, it it should not be zeroed out by bzero/memset either
+   and should instead use assignment and/or value initialization.  */
+struct HasPrivateCopy {
+  int i;
+private:
+  HasPrivateCopy (const HasPrivateCopy&);
+};
+
+void test (HasPrivateCopy *p, const HasPrivateCopy &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // Zeroing out is diagnosed because value initialization is
+  // invalid (the copy ctor makes no default ctor unavailable).
+  // Verify also that the suggestion offers assignment but not
+  // value initialization (since the lattare is not available).
+  T (bzero, (p, sizeof *p));        // { dg-warning "clearing an object of non-trivial type .struct HasPrivateCopy.; use assignment instead" }
+  T (memset, (p, 0, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  // Copying from an object of any type is diagnosed.
+  T (memcpy, (p, &x, sizeof *p));   // { dg-warning ".void\\* memcpy(\[^\n\r\]*). writing to an object of non-trivially copyable type .struct HasPrivateCopy.; use copy-assignment instead" }
+  T (memcpy, (p, q, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, s, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+
+  T (memmove, (p, &x, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, q, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, s, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+
+  T (mempcpy, (p, &x, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, q, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+
+  // Reallocating is the same as calling memcpy.
+  T (q = realloc, (p, 1));          // { dg-warning "realloc" }
+  T (q = realloc, (p, n));          // { dg-warning "realloc" }
+  T (q = realloc, (p, sizeof *p));  // { dg-warning "realloc" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_DTOR
+
+/* HasDtor should be initialized using aggregate or memberwise intialization,
+   not bzero or memset.  */
+struct HasDtor { int i; ~HasDtor (); };
+
+void test (HasDtor *p, const HasDtor &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // Zeroing out is diagnosed only because it's difficult not to.
+  // Otherwise, a class that's non-trivial only because it has
+  // a non-trivial dtor can be safely zeroed out (that's what
+  // value-initializing it does).
+  T (bzero, (p, sizeof *p));        // { dg-warning "bzero" }
+  T (memset, (p, 0, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  // Copying from an object of any type is diagnosed simply because
+  // a class with a user-defined dtor is not trivially copyable.
+  T (memcpy, (p, &x, sizeof *p));   // { dg-warning "memcpy" }
+  T (memcpy, (p, q, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, s, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+
+  T (memmove, (p, &x, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, q, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, s, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+
+  T (mempcpy, (p, &x, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, q, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+
+  // Reallocating is the same as calling memcpy.
+  T (q = realloc, (p, 1));          // { dg-warning "realloc" }
+  T (q = realloc, (p, n));          // { dg-warning "realloc" }
+  T (q = realloc, (p, sizeof *p));  // { dg-warning "realloc" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_DELETED_DTOR
+
+// HasDeletedDtor is trivial so clearing and cpying it is okay.
+// Relocation would bypass the deleted dtor and so it's diagnosed.
+
+struct HasDeletedDtor
+{
+  int i;
+  ~HasDeletedDtor () = delete;
+};
+
+void test (HasDeletedDtor *p, const HasDeletedDtor &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  T (bzero, (p, sizeof *p));
+  T (memset, (p, 0, sizeof *p));
+  T (memset, (p, 1, sizeof *p));
+  T (memset, (p, i, sizeof *p));
+
+  T (memcpy, (p, &x, sizeof *p));
+  T (memcpy, (p, q, sizeof *p));
+  T (memcpy, (p, s, sizeof *p));
+  T (memcpy, (p, ia, sizeof *p));
+
+  T (memmove, (p, &x, sizeof *p));
+  T (memmove, (p, q, sizeof *p));
+  T (memmove, (p, s, sizeof *p));
+  T (memmove, (p, ia, sizeof *p));
+
+  T (mempcpy, (p, &x, sizeof *p));
+  T (mempcpy, (p, q, sizeof *p));
+  T (mempcpy, (p, s, sizeof *p));
+  T (mempcpy, (p, ia, sizeof *p));
+
+  // Reallocating is diagnosed.
+  T (q = realloc, (p, 1));          // { dg-warning "moving an object of type .struct HasDeletedDtor. with deleted destructor" }
+  T (q = realloc, (p, n));          // { dg-warning "realloc" }
+  T (q = realloc, (p, sizeof *p));  // { dg-warning "realloc" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_PRIVATE_DTOR
+
+// Unlike HasDeletedDtor, HasPrivateDtor is okay to zero-out and copy
+// but not relocate because doing so would bypass the deleted dtor..
+
+struct HasPrivateDtor
+{
+  int i;
+private:
+  ~HasPrivateDtor ();
+};
+
+void test (HasPrivateDtor *p, const HasPrivateDtor &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  T (bzero, (p, sizeof *p));        // { dg-warning "clearing an object of non-trivial type .struct HasPrivateDtor.; use assignment or value-initialization instead" }
+  T (memset, (p, 0, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  T (memcpy, (p, &x, sizeof *p));   // { dg-warning "writing to an object of non-trivially copyable type .struct HasPrivateDtor.; use copy-assignment or copy-initialization instead" }
+  T (memcpy, (p, q, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, s, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+
+  T (memmove, (p, &x, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, q, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, s, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+
+  T (mempcpy, (p, &x, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, q, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+
+  // Reallocating is diagnosed.
+  T (q = realloc, (p, 1));          // { dg-warning "moving an object of non-trivially copyable type .struct HasPrivateDtor." }
+  T (q = realloc, (p, n));          // { dg-warning "realloc" }
+  T (q = realloc, (p, sizeof *p));  // { dg-warning "realloc" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_COPY_ASSIGN
+
+/* HasCopyAssign should be copied using the copy ctor or assignment, not
+   by memcpy or memmove.  */
+struct HasCopyAssign { void operator= (HasCopyAssign&); };
+
+void test (HasCopyAssign *p, const HasCopyAssign &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // Zeroing out is diagnosed because it when used with an existing
+  // (already constructed) object in lieu of assigning a new value
+  // to it would bypass the user-defined assignment.
+  T (bzero, (p, sizeof *p));        // { dg-warning "bzero" }
+  T (memset, (p, 0, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  // Copying from an object of any type is diagnosed.
+  T (memcpy, (p, &x, sizeof *p));   // { dg-warning "memcpy" }
+  T (memcpy, (p, q, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, s, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+
+  T (memmove, (p, &x, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, q, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, s, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+
+  T (mempcpy, (p, &x, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, q, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+
+  // Reallocating is the same as calling memcpy.
+  T (q = realloc, (p, 1));          // { dg-warning "realloc" }
+  T (q = realloc, (p, n));          // { dg-warning "realloc" }
+  T (q = realloc, (p, sizeof *p));  // { dg-warning "realloc" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_MOVE_ASSIGN
+
+/* Like HasCopyAssign, HasMoveAssign should be copied using the copy
+   ctor or assignment, not by memcpy or memmove.  */
+struct HasMoveAssign
+{
+#if __cplusplus > 199711L
+  void operator= (HasMoveAssign&&);
+#else
+  // C++ 98 has no reference references.  Simply repeat the HasCopyAssign
+  // test to avoid having to add a conditional to every dg-warning directive.
+  void operator= (const HasMoveAssign&);
+#endif
+};
+
+void test (HasMoveAssign *p, const HasMoveAssign &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // Zeroing out is diagnosed because it when used with an existing
+  // (already constructed) object in lieu of assigning a new value
+  // to it would bypass the user-defined assignment.
+  T (bzero, (p, sizeof *p));        // { dg-warning "bzero" }
+  T (memset, (p, 0, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  // Copying from an object of any type is diagnosed.
+  T (memcpy, (p, &x, sizeof *p));   // { dg-warning "memcpy" }
+  T (memcpy, (p, q, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, s, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+
+  T (memmove, (p, &x, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, q, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, s, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+
+  T (mempcpy, (p, &x, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, q, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+
+  // Reallocating is the same as calling memcpy.
+  T (q = realloc, (p, 1));          // { dg-warning "realloc" }
+  T (q = realloc, (p, n));          // { dg-warning "realloc" }
+  T (q = realloc, (p, sizeof *p));  // { dg-warning "realloc" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_TRIVIAL_COPY_HAS_MOVE_ASSIGN
+
+/* TrivialCopyHasMoveAssign should be copied using the copy ctor
+   or assignment, not by memcpy or memmove.  */
+struct TrivialCopyHasMoveAssign
+{
+  typedef TrivialCopyHasMoveAssign Self;
+
+  Self& operator= (const Self&) = default;
+
+#if __cplusplus > 199711L
+  Self& operator= (Self&&);
+#else
+  // C++ 98 has no reference references.  Fake the test by adding
+  // a non-const overload of the assignment operator (which should
+  // have the same effect).
+  Self& operator= (Self&);
+#endif
+};
+
+void test (TrivialCopyHasMoveAssign *p, const TrivialCopyHasMoveAssign &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // Zeroing out is diagnosed because it when used with an existing
+  // (already constructed) object in lieu of assigning a new value
+  // to it would bypass the user-defined assignment.
+  T (bzero, (p, sizeof *p));        // { dg-warning "bzero" }
+  T (memset, (p, 0, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  // Copying from an object of any type is diagnosed.
+  T (memcpy, (p, &x, sizeof *p));   // { dg-warning "memcpy" }
+  T (memcpy, (p, q, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, s, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+
+  T (memmove, (p, &x, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, q, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, s, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+
+  T (mempcpy, (p, &x, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, q, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+
+  // Reallocating is the same as calling memcpy.
+  T (q = realloc, (p, 1));          // { dg-warning "realloc" }
+  T (q = realloc, (p, n));          // { dg-warning "realloc" }
+  T (q = realloc, (p, sizeof *p));  // { dg-warning "realloc" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_TRIVIAL_MOVE_HAS_COPY_ASSIGN
+
+/* TrivialMoveNontrivialCopyAssign should be copied using the copy ctor
+   or assignment, not by memcpy or memmove.  */
+struct TrivialMoveNontrivialCopyAssign
+{
+  typedef TrivialMoveNontrivialCopyAssign Self;
+
+  Self& operator= (const Self&);
+#if __cplusplus > 199711L
+  // C++ 98 has no reference references.  Fake the test by simply
+  // not declaring the move assignment.
+  Self& operator= (Self&&) = default;
+#endif
+};
+
+void test (TrivialMoveNontrivialCopyAssign *p,
+	   const TrivialMoveNontrivialCopyAssign &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // Zeroing out is diagnosed because it when used with an existing
+  // (already constructed) object in lieu of assigning a new value
+  // to it would bypass the user-defined assignment.
+  T (bzero, (p, sizeof *p));        // { dg-warning "bzero" }
+  T (memset, (p, 0, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  // Copying from an object of any type is diagnosed.
+  T (memcpy, (p, &x, sizeof *p));   // { dg-warning "memcpy" }
+  T (memcpy, (p, q, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, s, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+
+  T (memmove, (p, &x, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, q, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, s, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+
+  T (mempcpy, (p, &x, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, q, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+
+  // Reallocating is the same as calling memcpy.
+  T (q = realloc, (p, 1));          // { dg-warning "realloc" }
+  T (q = realloc, (p, n));          // { dg-warning "realloc" }
+  T (q = realloc, (p, sizeof *p));  // { dg-warning "realloc" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_TRIVIAL_ASSIGN_REF_OVERLOAD
+
+/* TrivialAssignRefOverload is a trivial type.  */
+struct TrivialAssignRefOverload {
+  int i;
+  typedef TrivialAssignRefOverload Self;
+
+  Self& operator= (Self&) = default;
+  Self& operator= (const Self&) = delete;
+  Self& operator= (volatile Self&) = delete;
+  Self& operator= (const volatile Self&) = delete;
+};
+
+void test (TrivialAssignRefOverload *p, const TrivialAssignRefOverload &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  T (bzero, (p, sizeof *p));
+  T (memset, (p, 0, sizeof *p));
+  T (memset, (p, 1, sizeof *p));
+  T (memset, (p, i, sizeof *p));
+
+  T (memcpy, (p, &x, sizeof *p));
+  T (memcpy, (p, q, sizeof *p));
+  T (memcpy, (p, s, sizeof *p));
+  T (memcpy, (p, ia, sizeof *p));
+
+  T (memmove, (p, &x, sizeof *p));
+  T (memmove, (p, q, sizeof *p));
+  T (memmove, (p, s, sizeof *p));
+  T (memmove, (p, ia, sizeof *p));
+
+  T (mempcpy, (p, &x, sizeof *p));
+  T (mempcpy, (p, q, sizeof *p));
+  T (mempcpy, (p, s, sizeof *p));
+  T (mempcpy, (p, ia, sizeof *p));
+
+  T (q = realloc, (p, 1));
+  T (q = realloc, (p, n));
+  T (q = realloc, (p, sizeof *p));
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_TRIVIAL_ASSIGN_CSTREF_OVERLOAD
+
+/* TrivialAssignCstOverload is a trivial type.  */
+struct TrivialAssignCstRefOverload {
+  int i;
+  typedef TrivialAssignCstRefOverload Self;
+
+  Self& operator= (Self&) = delete;
+  Self& operator= (const Self&) = default;
+  Self& operator= (volatile Self&) = delete;
+  Self& operator= (const volatile Self&) = delete;
+};
+
+void test (TrivialAssignCstRefOverload *p,
+	   const TrivialAssignCstRefOverload &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  T (bzero, (p, sizeof *p));
+  T (memset, (p, 0, sizeof *p));
+  T (memset, (p, 1, sizeof *p));
+  T (memset, (p, i, sizeof *p));
+
+  T (memcpy, (p, &x, sizeof *p));
+  T (memcpy, (p, q, sizeof *p));
+  T (memcpy, (p, s, sizeof *p));
+  T (memcpy, (p, ia, sizeof *p));
+
+  T (memmove, (p, &x, sizeof *p));
+  T (memmove, (p, q, sizeof *p));
+  T (memmove, (p, s, sizeof *p));
+  T (memmove, (p, ia, sizeof *p));
+
+  T (mempcpy, (p, &x, sizeof *p));
+  T (mempcpy, (p, q, sizeof *p));
+  T (mempcpy, (p, s, sizeof *p));
+  T (mempcpy, (p, ia, sizeof *p));
+
+  T (q = realloc, (p, 1));
+  T (q = realloc, (p, n));
+  T (q = realloc, (p, sizeof *p));
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_TRIVIAL_REF_HAS_VOLREF_ASSIGN
+
+struct TrivialRefHasVolRefAssign
+{
+  typedef TrivialRefHasVolRefAssign Self;
+
+  Self& operator= (Self&) = default;
+  Self& operator= (volatile Self&);
+};
+
+void test (TrivialRefHasVolRefAssign *p,
+	   const TrivialRefHasVolRefAssign &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // Zeroing out is diagnosed because it when used with an existing
+  // (already constructed) object in lieu of assigning a new value
+  // to it would bypass the user-defined assignment.
+  T (bzero, (p, sizeof *p));        // { dg-warning "bzero" }
+  T (memset, (p, 0, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  // Copying from an object of any type is diagnosed.
+  T (memcpy, (p, &x, sizeof *p));   // { dg-warning "memcpy" }
+  T (memcpy, (p, q, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, s, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+
+  T (memmove, (p, &x, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, q, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, s, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+
+  T (mempcpy, (p, &x, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, q, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+
+  // Reallocating is the same as calling memcpy.
+  T (q = realloc, (p, 1));          // { dg-warning "realloc" }
+  T (q = realloc, (p, n));          // { dg-warning "realloc" }
+  T (q = realloc, (p, sizeof *p));  // { dg-warning "realloc" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_VOLREF_ASSIGN
+
+struct HasVolRefAssign {
+  int i;
+  typedef HasVolRefAssign Self;
+
+  Self& operator= (volatile Self&);
+};
+
+void test (HasVolRefAssign *p, const HasVolRefAssign &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // Zeroing out is diagnosed because it when used with an existing
+  // (already constructed) object in lieu of assigning a new value
+  // to it would bypass the user-defined assignment.
+  T (bzero, (p, sizeof *p));        // { dg-warning "bzero" }
+  T (memset, (p, 0, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  // Copying from an object of any type is diagnosed.
+  T (memcpy, (p, &x, sizeof *p));   // { dg-warning "memcpy" }
+  T (memcpy, (p, q, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, s, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+
+  T (memmove, (p, &x, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, q, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, s, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+
+  T (mempcpy, (p, &x, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, q, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+
+  // Reallocating is the same as calling memcpy.
+  T (q = realloc, (p, 1));          // { dg-warning "realloc" }
+  T (q = realloc, (p, n));          // { dg-warning "realloc" }
+  T (q = realloc, (p, sizeof *p));  // { dg-warning "realloc" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_VIRTUALS
+
+/* HasVirtuals should only be manipulated by the special member functions
+   and not by bzero, memcpy, or any other raw memory function. Doing
+   otherwse might corrupt the the vtable pointer.  */
+struct HasVirtuals { int i; virtual void foo (); };
+
+void test (HasVirtuals *p, const HasVirtuals &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // Zeroing out is diagnosed because it corrupts the vtable.
+  T (bzero, (p, sizeof *p));        // { dg-warning "bzero" }
+  T (memset, (p, 0, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  // Copying is diagnosed because when used to initialize an object
+  // could incorrectly initialize the vtable.
+  T (memcpy, (p, &x, sizeof *p));   // { dg-warning "memcpy" }
+  T (memcpy, (p, q, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, s, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+
+  T (memmove, (p, &x, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, q, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, s, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+
+  T (mempcpy, (p, &x, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, q, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+
+  // Reallocating is the same as calling memcpy.
+  T (q = realloc, (p, 1));          // { dg-warning "realloc" }
+  T (q = realloc, (p, n));          // { dg-warning "realloc" }
+  T (q = realloc, (p, sizeof *p));  // { dg-warning "realloc" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_CONST_DATA
+
+/* HasConstData should only be initialized using aggregate initializatoon
+   and not cleared by bzero, or copied into using memcpy.  Since it's not
+   assignable allowing, raw memory functions to write into it would defeat
+   const-correctness.  */
+struct HasConstData { const char a[4]; };
+
+void test (HasConstData *p, const HasConstData &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // The following is ill-formed because HasConstData's cannot
+  // be assigned (the assignment is implicitly deleted).  For
+  // that reason all raw memory operations are diagnosed.
+  // *p = x;
+
+  // Zeroing out is diagnosed because if used with an existing
+  // (already initialized) object could break const correctness.
+  // Since the default ctor and copy assignment are both deleted,
+  // verify that they're not suggested as a possible alternative.
+  T (bzero, (p, sizeof *p));        // { dg-warning "clearing an object of type .struct HasConstData. with no trivial copy-assignment \\\[" "c++ 11 and later" { target { c++11 } } }
+  // { dg-warning "clearing an object of type .struct HasConstData. with no trivial copy-assignment" "c++ 98" { target { c++98_only } } .-1 }
+  T (memset, (p, 0, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  // Copying is also diagnosed.
+  T (memcpy, (p, &x, sizeof *p));   // { dg-warning "writing to an object of type .struct HasConstData. with no trivial copy-assignment; use copy-initialization instead" "c++ 11 and later" { target { c++11 } } }
+  // { dg-warning "writing to an object of type .struct HasConstData. with no trivial copy-assignment" "c++ 98" { target { c++98_only } } .-1 }
+  T (memcpy, (p, q, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, s, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+
+  T (memmove, (p, &x, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, q, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, s, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+
+  T (mempcpy, (p, &x, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, q, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+
+  // Reallocating is not diagnosed except in C++ 98 due to a bug.
+  T (q = realloc, (p, 1));          // { dg-warning "moving an object of non-trivially copyable type .struct HasConstData.; use .new. and .delete. instead" "c++98" { target { c++98_only } } }
+  T (q = realloc, (p, n));          // { dg-warning "realloc" "c++98" { target { c++98_only } } }
+  T (q = realloc, (p, sizeof *p));  // { dg-warning "realloc" "c++98" { target { c++98_only } } }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_REFERENCE
+
+/* HasReference should only be initialized using aggregate initializatoon
+   and not cleared by bzero, or copied into using memcpy.  Since it's not
+   assignable, allowing raw memory functions to write into it could
+   corrupt the reference.  */
+struct HasReference { int &ci; };
+
+void test (HasReference *p, const HasReference &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // Similarly to HasConstData, the following is ill-formed because
+  // Hasreference cannot be assigned (the assignment is implicitly
+  // deleted).  For that reason all raw memory operations are diagnosed.
+  // *p = x;
+
+  // Zeroing out is diagnosed because if used with an existing
+  // (already initialized) object would invalidate the reference.
+  // Since copy-assignment is deleted verify it's not suggested
+  // as an alternative.  (C++ 11 and later only; C++ 98 is broken).
+  T (bzero, (p, sizeof *p));        // { dg-warning "clearing an object of type .struct HasReference. with no trivial copy-assignment \\\[" "c++ 11 and later" { target { c++11 } } }
+  // { dg-warning "clearing an object of type .struct HasReference. with no trivial copy-assignment" "c++ 98" { target { c++98_only } } .-1 }
+  T (bzero, (p, n));                // { dg-warning "bzero" }
+  T (memset, (p, 0, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, 0, n));            // { dg-warning "memset" }
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, 1, n));            // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, n));            // { dg-warning "memset" }
+
+  // Copying is also diagnosed.
+  T (memcpy, (p, &x, sizeof *p));   // { dg-warning "writing to an object of type .struct HasReference. with no trivial copy-assignment; use copy-initialization instead" "c++ 11 and later" { target { c++11 } } }
+  // { dg-warning "writing to an object of type .struct HasReference. with no trivial copy-assignment" "c++ 98" { target { c++98_only } } .-1 }
+  T (memcpy, (p, &x, n));           // { dg-warning "memcpy" }
+  T (memcpy, (p, q, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, q, n));            // { dg-warning "memcpy" }
+  T (memcpy, (p, s, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, s, n));            // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, n));           // { dg-warning "memcpy" }
+
+  T (memmove, (p, &x, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, q, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, s, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+
+  T (mempcpy, (p, &x, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, q, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+
+  // Reallocating is not diagnosed because a type with a reference
+  // is (perhaps surprisingly) trivially copyable.  It is diagnosed
+  // in C++ 98 because of a bug, but it seems like it should be
+  // diagnosed in all modes.
+  T (q = realloc, (p, 1));          // { dg-warning "realloc" "c++ 98" { target { c++98_only } } }
+  T (q = realloc, (p, n));          // { dg-warning "realloc" "c++ 98" { target { c++98_only } } }
+  T (q = realloc, (p, sizeof *p));  // { dg-warning "realloc" "c++ 98" { target { c++98_only } } }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_MEM_DATA_PTR
+
+/* HasMemDataPtr should only be initialized using aggregate initializatoon
+   and not cleared by bzero or written into using memset because its
+   representation is different from ordinary scalars (a null member data
+   pointer is all ones).  It can be copied into using memcpy from an object
+   of the same type or from a character buffer.  */
+struct HasMemDataPtr { int HasMemDataPtr::*p; };
+
+void test (HasMemDataPtr *p, const HasMemDataPtr &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // Zeroing out is diagnosed because a null member data pointer has
+  // a representation that's all bits set.
+  T (bzero, (p, sizeof *p));        // { dg-warning "clearing an object of type .struct HasMemDataPtr. containing a pointer-to-member" }
+  T (bzero, (p, n));                // { dg-warning "bzero" }
+  T (memset, (p, 0, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, 0, n));            // { dg-warning "memset" }
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, 1, n));            // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, n));            // { dg-warning "memset" }
+
+  // Copying is not diagnosed.
+  T (memcpy, (p, &x, sizeof *p));
+  T (memcpy, (p, &x, n));
+  T (memcpy, (p, q, sizeof *p));
+  T (memcpy, (p, q, n));
+  T (memcpy, (p, s, sizeof *p));
+  T (memcpy, (p, s, n));
+  T (memcpy, (p, ia, sizeof *p));
+  T (memcpy, (p, ia, n));
+
+  T (memmove, (p, &x, sizeof *p));
+  T (memmove, (p, q, sizeof *p));
+  T (memmove, (p, s, sizeof *p));
+  T (memmove, (p, ia, sizeof *p));
+
+  T (mempcpy, (p, &x, sizeof *p));
+  T (mempcpy, (p, q, sizeof *p));
+  T (mempcpy, (p, s, sizeof *p));
+  T (mempcpy, (p, ia, sizeof *p));
+
+  // Reallocating is the same as calling memcpy.
+  T (q = realloc, (p, 1));
+  T (q = realloc, (p, n));
+  T (q = realloc, (p, sizeof *p));
+  T (q = realloc, (p, sizeof *p + 1));
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_SOME_PRIVATE_DATA
+
+/* HasSomePrivateData can be initialized using value initialization
+   and should not be written to using memset with a non-zero argument.
+   Doing otherwise would break encapsulation.  */
+struct HasSomePrivateData { char a[2]; private: char b[2]; };
+
+void test (HasSomePrivateData *p, const HasSomePrivateData &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // Zeroing out is not diagnosed because it's equivalent to value
+  // initialization.
+  T (bzero, (p, sizeof *p));
+  T (memset, (p, 0, sizeof *p));
+  // Calling memset with a (possibly) non-zero argument is diagnosed
+  // because it breaks encapsulation.
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  // Calling memcpy to copy from an object of the same type or from
+  // a character or void buffer is not diagnosed because that's what
+  // copy construction and copy assignment do.
+  T (memcpy, (p, &x, sizeof *p));
+  T (memcpy, (p, &x, n));
+  T (memcpy, (p, q, sizeof *p));
+  T (memcpy, (p, s, sizeof *p));
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, n));           // { dg-warning "memcpy" }
+
+  // Same as memcpy above.
+  T (memmove, (p, &x, sizeof *p));
+  T (memmove, (p, &x, n));
+  T (memmove, (p, q, sizeof *p));
+  T (memmove, (p, s, sizeof *p));
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, ia, n));          // { dg-warning "memmove" }
+
+  // Same as memcpy above.
+  T (mempcpy, (p, &x, sizeof *p));
+  T (mempcpy, (p, &x, n));
+  T (mempcpy, (p, q, sizeof *p));
+  T (mempcpy, (p, q, n));
+  T (mempcpy, (p, s, sizeof *p));
+  T (mempcpy, (p, s, n));
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, n));          // { dg-warning "mempcpy" }
+
+  // Reallocating is the same as calling memcpy except that partial
+  // copies are not diagnosed.
+  T (q = realloc, (p, 1));
+  T (q = realloc, (p, n));
+  T (q = realloc, (p, sizeof *p));
+  T (q = realloc, (p, sizeof *p + 1));
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_SOME_PROTECTED_DATA
+
+/* Similarly to HasSomePrivateData, HasSomeProtectedData can be
+   initialized using value initialization and should not be written
+   to using memset with a non-zero argument.  Doing otherwise would
+   break encapsulation.  */
+struct HasSomeProtectedData { char a[2]; protected: char b[2]; };
+
+void test (HasSomeProtectedData *p, const HasSomeProtectedData &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // Zeroing out is not diagnosed because it's equivalent to value
+  // initialization.
+  T (bzero, (p, sizeof *p));
+  T (memset, (p, 0, sizeof *p));
+  // Calling memset with a (possibly) non-zero argument is diagnosed
+  // because it breaks encapsulation.
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  // Calling memcpy to copy from an object of the same type or from
+  // a character or void buffer is not diagnosed because that's what
+  // copy construction and copy assignment do.
+  T (memcpy, (p, &x, sizeof *p));
+  T (memcpy, (p, &x, n));
+  T (memcpy, (p, q, sizeof *p));
+  T (memcpy, (p, s, sizeof *p));
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, n));           // { dg-warning "memcpy" }
+
+  // Same as memcpy above.
+  T (memmove, (p, &x, sizeof *p));
+  T (memmove, (p, &x, n));
+  T (memmove, (p, q, sizeof *p));
+  T (memmove, (p, s, sizeof *p));
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, ia, n));          // { dg-warning "memmove" }
+
+  // Same as memcpy above.
+  T (mempcpy, (p, &x, sizeof *p));
+  T (mempcpy, (p, &x, n));
+  T (mempcpy, (p, q, sizeof *p));
+  T (mempcpy, (p, q, n));
+  T (mempcpy, (p, s, sizeof *p));
+  T (mempcpy, (p, s, n));
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, n));          // { dg-warning "mempcpy" }
+
+  // Reallocating is the same as calling memcpy except that partial
+  // copies are not diagnosed.
+  T (q = realloc, (p, 1));
+  T (q = realloc, (p, n));
+  T (q = realloc, (p, sizeof *p));
+  T (q = realloc, (p, sizeof *p + 1));
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_ALL_PRIVATE_DATA
+
+/* Similarly to HasSomePrivateData, HasAllPrivateData should only be
+   initialized using value initializatoon and should not be written
+   to using memset with non-zero argument.  They are tested separately
+   because unlike the former classes, these are standard layout.  */
+struct HasAllPrivateData { private: char a[4]; };
+
+void test (HasAllPrivateData *p, const HasAllPrivateData &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // Zeroing out is not diagnosed because it's equivalent to value
+  // initialization.
+  T (bzero, (p, sizeof *p));
+  T (memset, (p, 0, sizeof *p));
+  // Calling memset with a (possibly) non-zero argument is diagnosed
+  // because it breaks encapsulation.
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  // Calling memcpy to copy from an object of the same type or from
+  // a character or void buffer is not diagnosed because that's what
+  // copy construction and copy assignment do.
+  T (memcpy, (p, &x, sizeof *p));
+  T (memcpy, (p, &x, n));
+  T (memcpy, (p, q, sizeof *p));
+  T (memcpy, (p, s, sizeof *p));
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, n));           // { dg-warning "memcpy" }
+
+  // Same as memcpy above.
+  T (memmove, (p, &x, sizeof *p));
+  T (memmove, (p, &x, n));
+  T (memmove, (p, q, sizeof *p));
+  T (memmove, (p, s, sizeof *p));
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, ia, n));          // { dg-warning "memmove" }
+
+  // Same as memcpy above.
+  T (mempcpy, (p, &x, sizeof *p));
+  T (mempcpy, (p, &x, n));
+  T (mempcpy, (p, q, sizeof *p));
+  T (mempcpy, (p, q, n));
+  T (mempcpy, (p, s, sizeof *p));
+  T (mempcpy, (p, s, n));
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, n));          // { dg-warning "mempcpy" }
+
+  // Reallocating is the same as calling memcpy except that partial
+  // copies are not diagnosed.
+  T (q = realloc, (p, 1));
+  T (q = realloc, (p, n));
+  T (q = realloc, (p, sizeof *p));
+  T (q = realloc, (p, sizeof *p + 1));
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_ALL_PROTECTED_DATA
+
+/* Similarly to HasSomeProtectedData, HasAllProtectedData should only
+   be initialized using value initializatoon and should not be written
+   to using memset with non-zero argument.  They are tested separately
+   because unlike the former classes, these are standard layout.  */
+struct HasAllProtectedData { protected: char a[4]; };
+
+void test (HasAllProtectedData *p, const HasAllProtectedData &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // Zeroing out is not diagnosed because it's equivalent to value
+  // initialization.
+  T (bzero, (p, sizeof *p));
+  T (memset, (p, 0, sizeof *p));
+  // Calling memset with a (possibly) non-zero argument is diagnosed
+  // because it breaks encapsulation.
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  // Calling memcpy to copy from an object of the same type or from
+  // a character or void buffer is not diagnosed because that's what
+  // copy construction and copy assignment do.
+  T (memcpy, (p, &x, sizeof *p));
+  T (memcpy, (p, &x, n));
+  T (memcpy, (p, q, sizeof *p));
+  T (memcpy, (p, s, sizeof *p));
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, n));           // { dg-warning "memcpy" }
+
+  // Same as memcpy above.
+  T (memmove, (p, &x, sizeof *p));
+  T (memmove, (p, &x, n));
+  T (memmove, (p, q, sizeof *p));
+  T (memmove, (p, s, sizeof *p));
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, ia, n));          // { dg-warning "memmove" }
+
+  // Same as memcpy above.
+  T (mempcpy, (p, &x, sizeof *p));
+  T (mempcpy, (p, &x, n));
+  T (mempcpy, (p, q, sizeof *p));
+  T (mempcpy, (p, q, n));
+  T (mempcpy, (p, s, sizeof *p));
+  T (mempcpy, (p, s, n));
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, n));          // { dg-warning "mempcpy" }
+
+  // Reallocating is the same as calling memcpy except that partial
+  // copies are not diagnosed.
+  T (q = realloc, (p, 1));
+  T (q = realloc, (p, n));
+  T (q = realloc, (p, sizeof *p));
+  T (q = realloc, (p, sizeof *p + 1));
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_DEFAULT_CTOR_PRIVATE_ASSIGN
+
+/* Used to verify suggested alternatives.  */
+struct HasDefaultPrivateAssign
+{
+  char a[4];
+  HasDefaultPrivateAssign ();
+private:
+  void operator= (HasDefaultPrivateAssign&);
+};
+
+void test (HasDefaultPrivateAssign *p, const HasDefaultPrivateAssign &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // HasDefaultPrivateAssign isn't trivial or assignable.  Verify
+  // that the alternative suggested in the warning is to use copy or
+  // default but not assignment.
+  T (bzero, (p, sizeof *p));   // { dg-warning "bzero(\[^\n\r\]*). clearing an object of type .struct HasDefaultPrivateAssign. with no trivial copy-assignment; use value-initialization instead" "c++ 11 and later" { target { c++11 } } }
+  // { dg-warning "bzero(\[^\n\r\]*). clearing an object of type .struct HasDefaultPrivateAssign. with no trivial copy-assignment; use value-initialization instead" "c++ 98" { target { c++98_only } } .-1 }
+
+  T (memset, (p, 0, sizeof *p));   // { dg-warning ".void\\* memset(\[^\n\r\]*). clearing an object of type .struct HasDefaultPrivateAssign. with (deleted|no trivial) copy-assignment; use value-initialization instead" }
+
+  T (memset, (p, 1, sizeof *p));   // { dg-warning ".void\\* memset(\[^\n\r\]*). writing to an object of type .struct HasDefaultPrivateAssign. with (deleted|no trivial) copy-assignment" }
+
+  T (memset, (p, i, sizeof *p));   // { dg-warning ".void\\* memset(\[^\n\r\]*). writing to an object of type .struct HasDefaultPrivateAssign. with (deleted|no trivial) copy-assignment" }
+
+  // Copying from another object of the same type is diagnosed because
+  // the copy assignment is inaccessible.  Verify that the suggested
+  // alternative is not copy assignment (C++ 98 is busted).
+  T (memcpy, (p, &x, sizeof *p));   // { dg-warning ".void\\* memcpy(\[^\n\r\]*). writing to an object of type .struct HasDefaultPrivateAssign. with no trivial copy-assignment; use copy-initialization instead" "c++11 and later" { target c++11 } }
+  // { dg-warning ".void\\* memcpy(\[^\n\r\]*). writing to an object of type .struct HasDefaultPrivateAssign. with no trivial copy-assignment" "c++98" { target c++98_only } .-1 }
+  T (memcpy, (p, &x, n));           // { dg-warning "memcpy" }
+
+  // Similarly for copying from a void* or character buffer.
+  T (memcpy, (p, q, sizeof *p));    // { dg-warning ".void\\* memcpy(\[^\n\r\]*). writing to an object of type .struct HasDefaultPrivateAssign. with no trivial copy-assignment; use copy-initialization instead" "c++11 and later" { target c++11 } }
+  // { dg-warning ".void\\* memcpy(\[^\n\r\]*). writing to an object of type .struct HasDefaultPrivateAssign. with no trivial copy-assignment" "c++98" { target c++98_only } ,-1 }
+  T (memcpy, (p, q, n));            // { dg-warning "memcpy" }
+  T (memcpy, (p, s, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, s, n));            // { dg-warning "memcpy" }
+
+  T (memmove, (p, q, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, q, n));           // { dg-warning "memmove" }
+  T (memmove, (p, s, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, s, n));           // { dg-warning "memmove" }
+
+  T (mempcpy, (p, q, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, q, n));           // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, n));           // { dg-warning "mempcpy" }
+
+  // Same for partial copies are diagnosed.
+  T (memcpy, (p, &x, 1));   // { dg-warning "writing to an object of type .struct HasDefaultPrivateAssign. with (deleted|no trivial) copy-assignment" } */
+  T (memmove, (p, q, 2));   // { dg-warning "memmove" } */
+  T (mempcpy, (p, q, 3));   // { dg-warning "mempcpy" } */
+
+  // Otherwise, copying from an object of an unrelated type is diagnosed.
+  T (memcpy, (p, ia, sizeof *p));  // { dg-warning "writing to an object of type .struct HasDefaultPrivateAssign. with (deleted|no trivial) copy-assignment." }
+  T (memmove, (p, ia, sizeof *p)); // { dg-warning "memmove" }
+  T (mempcpy, (p, ia, sizeof *p)); // { dg-warning "mempcpy" }
+
+  // Reallocating is the same as calling memcpy.
+  T (q = realloc, (p, 1));          // { dg-warning "realloc" }
+  T (q = realloc, (p, n));          // { dg-warning "realloc" }
+  T (q = realloc, (p, sizeof *p));  // { dg-warning "realloc" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_DEFAULT_CTOR_DELETED_ASSIGN
+
+/* Used to verify suggested alternatives.  */
+struct HasDefaultDeletedAssign
+{
+  char a[4];
+  HasDefaultDeletedAssign ();
+private:
+  void operator= (HasDefaultDeletedAssign&);
+};
+
+void test (HasDefaultDeletedAssign *p, const HasDefaultDeletedAssign &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // HasDefaultDeletedAssign isn't trivial or assignable.  Verify
+  // that the alternative suggested in the warning is to use copy or
+  // default but not assignment.
+  T (bzero, (p, sizeof *p));   // { dg-warning "bzero(\[^\n\r\]*). clearing an object of type .struct HasDefaultDeletedAssign. with no trivial copy-assignment; use value-initialization instead" "c++ 11 and later" { target { c++11 } } }
+  // { dg-warning "bzero(\[^\n\r\]*). clearing an object of type .struct HasDefaultDeletedAssign. with no trivial copy-assignment; use value-initialization instead" "c++ 98" { target { c++98_only } } .-1 }
+
+  T (memset, (p, 0, sizeof *p));   // { dg-warning ".void\\* memset(\[^\n\r\]*). clearing an object of type .struct HasDefaultDeletedAssign. with (deleted|no trivial) copy-assignment; use value-initialization instead" }
+
+  T (memset, (p, 1, sizeof *p));   // { dg-warning ".void\\* memset(\[^\n\r\]*). writing to an object of type .struct HasDefaultDeletedAssign. with (deleted|no trivial) copy-assignment" }
+
+  T (memset, (p, i, sizeof *p));   // { dg-warning ".void\\* memset(\[^\n\r\]*). writing to an object of type .struct HasDefaultDeletedAssign. with (deleted|no trivial) copy-assignment" }
+
+  // Copying from another object of the same type is diagnosed because
+  // the copy assignment is inaccessible.  Verify that the suggested
+  // alternative is not copy assignment (C++ 98 is busted).
+  T (memcpy, (p, &x, sizeof *p));   // { dg-warning ".void\\* memcpy(\[^\n\r\]*). writing to an object of type .struct HasDefaultDeletedAssign. with no trivial copy-assignment; use copy-initialization instead" "c++11 and later" { target c++11 } }
+  // { dg-warning ".void\\* memcpy(\[^\n\r\]*). writing to an object of type .struct HasDefaultDeletedAssign. with no trivial copy-assignment" "c++98" { target c++98_only } .-1 }
+  T (memcpy, (p, &x, n));           // { dg-warning "memcpy" }
+
+  // Similarly for copying from a void* or character buffer.
+  T (memcpy, (p, q, sizeof *p));    // { dg-warning ".void\\* memcpy(\[^\n\r\]*). writing to an object of type .struct HasDefaultDeletedAssign. with no trivial copy-assignment; use copy-initialization instead" "c++11 and later" { target c++11 } }
+  // { dg-warning ".void\\* memcpy(\[^\n\r\]*). writing to an object of type .struct HasDefaultDeletedAssign. with no trivial copy-assignment" "c++98" { target c++98_only } ,-1 }
+  T (memcpy, (p, q, n));            // { dg-warning "memcpy" }
+  T (memcpy, (p, s, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, s, n));            // { dg-warning "memcpy" }
+
+  T (memmove, (p, q, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, q, n));           // { dg-warning "memmove" }
+  T (memmove, (p, s, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, s, n));           // { dg-warning "memmove" }
+
+  T (mempcpy, (p, q, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, q, n));           // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, n));           // { dg-warning "mempcpy" }
+
+  // Same for partial copies are diagnosed.
+  T (memcpy, (p, &x, 1));   // { dg-warning "writing to an object of type .struct HasDefaultDeletedAssign. with (deleted|no trivial) copy-assignment" } */
+  T (memmove, (p, q, 2));   // { dg-warning "memmove" } */
+  T (mempcpy, (p, q, 3));   // { dg-warning "mempcpy" } */
+
+  // Otherwise, copying from an object of an unrelated type is diagnosed.
+  T (memcpy, (p, ia, sizeof *p));  // { dg-warning "writing to an object of type .struct HasDefaultDeletedAssign. with (deleted|no trivial) copy-assignment." }
+  T (memmove, (p, ia, sizeof *p)); // { dg-warning "memmove" }
+  T (mempcpy, (p, ia, sizeof *p)); // { dg-warning "mempcpy" }
+
+  // Reallocating is the same as calling memcpy.
+  T (q = realloc, (p, 1));          // { dg-warning "realloc" }
+  T (q = realloc, (p, n));          // { dg-warning "realloc" }
+  T (q = realloc, (p, sizeof *p));  // { dg-warning "realloc" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_EXPRESSION
+
+void test_expr (int i)
+{
+  struct TestClass { TestClass () { } };
+  TestClass a, b;
+
+  static void *p;
+
+  T (bzero, (i < 0 ? &a : &b, 1));  // { dg-warning "bzero" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_CTOR
+
+void test_ctor ()
+{
+#undef T
+#define T(fn, arglist) (fn arglist, sink (this))
+
+  static void *p;
+
+  struct TestBase
+  {
+    TestBase ()
+    {
+      /* A ctor of a base class with no virtual function can do whatever
+	 it wants.  */
+      T (bzero, (this, sizeof *this));
+      T (memset, (this, 0, sizeof *this));
+      T (memcpy, (this, p, sizeof *this));
+      T (memmove, (this, p, sizeof *this));
+      T (mempcpy, (this, p, sizeof *this));
+    }
+
+    ~TestBase ()
+    {
+      /* A dtor of a base class with no virtual function can do whatever
+	 it wants.  */
+      T (bzero, (this, sizeof *this));
+      T (memset, (this, 0, sizeof *this));
+      T (memcpy, (this, p, sizeof *this));
+      T (memmove, (this, p, sizeof *this));
+      T (mempcpy, (this, p, sizeof *this));
+    }
+  };
+
+  struct TestBaseVtable
+  {
+    TestBaseVtable ()
+    {
+      /* A ctor of a base class with virtual function is treated
+	 as an ordinary function.  */
+      T (bzero, (this, sizeof *this));      // { dg-warning "bzero" }
+      T (memset, (this, 0, sizeof *this));  // { dg-warning "memset" }
+      T (memcpy, (this, p, sizeof *this));  // { dg-warning "memcpy" }
+      T (memmove, (this, p, sizeof *this)); // { dg-warning "memmove" }
+      T (mempcpy, (this, p, sizeof *this)); // { dg-warning "mempcpy" }
+    }
+
+    ~TestBaseVtable ()
+    {
+      /* A dtor of a base class with virtual function is treated
+	 as an ordinary function.  */
+      T (bzero, (this, sizeof *this));      // { dg-warning "bzero" }
+      T (memset, (this, 0, sizeof *this));  // { dg-warning "memset" }
+      T (memcpy, (this, p, sizeof *this));  // { dg-warning "memcpy" }
+      T (memmove, (this, p, sizeof *this)); // { dg-warning "memmove" }
+      T (mempcpy, (this, p, sizeof *this)); // { dg-warning "mempcpy" }
+    }
+
+    virtual void foo ();
+  };
+
+  struct TestDerived: HasDefault
+  {
+    TestDerived ()
+    {
+      /* A derived class ctor is treated as an ordinary function.  */
+      T (bzero, (this, sizeof *this));      // { dg-warning "bzero" }
+      T (memset, (this, 0, sizeof *this));  // { dg-warning "memset" }
+      T (memcpy, (this, p, sizeof *this));
+      T (memmove, (this, p, sizeof *this));
+      T (mempcpy, (this, p, sizeof *this));
+    }
+  };
+
+  struct TestDerivedDtor: HasDefault
+  {
+    ~TestDerivedDtor ()
+    {
+      /* A derived class dtor is treated as an ordinary function though
+	 it probably shouldn't be unless the base dtor is trivial.  But
+	 it doesn't seem worth the trouble.  */
+      T (bzero, (this, sizeof *this));      // { dg-warning "bzero" }
+      T (memset, (this, 0, sizeof *this));  // { dg-warning "memset" }
+      T (memcpy, (this, p, sizeof *this));  // { dg-warning "memcpy" }
+      T (memmove, (this, p, sizeof *this)); // { dg-warning "memmove" }
+      T (mempcpy, (this, p, sizeof *this)); // { dg-warning "mempcpy" }
+    }
+  };
+}
+
+#endif
+
+// { dg-prune-output "defaulted and deleted functions" }
diff --git a/gcc/tree-switch-conversion.c b/gcc/tree-switch-conversion.c
index 66db20f..72927bf 100644
--- a/gcc/tree-switch-conversion.c
+++ b/gcc/tree-switch-conversion.c
@@ -268,7 +268,7 @@ static void
 emit_case_bit_tests (gswitch *swtch, tree index_expr,
 		     tree minval, tree range, tree maxval)
 {
-  struct case_bit_test test[MAX_CASE_BIT_TESTS];
+  struct case_bit_test test[MAX_CASE_BIT_TESTS] = { };
   unsigned int i, j, k;
   unsigned int count;
 
@@ -293,8 +293,6 @@ emit_case_bit_tests (gswitch *swtch, tree index_expr,
   int prec = TYPE_PRECISION (word_type_node);
   wide_int wone = wi::one (prec);
 
-  memset (&test, 0, sizeof (test));
-
   /* Get the edge for the default case.  */
   tmp = gimple_switch_default_label (swtch);
   default_bb = label_to_block (CASE_LABEL (tmp));
diff --git a/gcc/vec.h b/gcc/vec.h
index 755a1f8..cbdd439 100644
--- a/gcc/vec.h
+++ b/gcc/vec.h
@@ -407,6 +407,26 @@ struct GTY((user)) vec
 {
 };
 
+/* Default-construct N elements in DST.  */
+
+template <typename T>
+inline void
+vec_default_construct (T *dst, unsigned n)
+{
+  for ( ; n; ++dst, --n)
+    ::new (static_cast<void*>(dst)) T ();
+}
+
+/* Copy-construct N elements in DST from *SRC.  */
+
+template <typename T>
+inline void
+vec_copy_construct (T *dst, const T *src, unsigned n)
+{
+  for ( ; n; ++dst, ++src, --n)
+    ::new (static_cast<void*>(dst)) T (*src);
+}
+
 /* Type to provide NULL values for vec<T, A, L>.  This is used to
    provide nil initializers for vec instances.  Since vec must be
    a POD, we cannot have proper ctor/dtor for it.  To initialize
@@ -612,7 +632,7 @@ vec_safe_grow_cleared (vec<T, A, vl_embed> *&v, unsigned len CXX_MEM_STAT_INFO)
 {
   unsigned oldlen = vec_safe_length (v);
   vec_safe_grow (v, len PASS_MEM_STAT);
-  memset (&(v->address ()[oldlen]), 0, sizeof (T) * (len - oldlen));
+  vec_default_construct (v->address () + oldlen, len - oldlen);
 }
 
 
@@ -818,7 +838,7 @@ vec<T, A, vl_embed>::copy (ALONE_MEM_STAT_DECL) const
     {
       vec_alloc (new_vec, len PASS_MEM_STAT);
       new_vec->embedded_init (len, len);
-      memcpy (new_vec->address (), m_vecdata, sizeof (T) * len);
+      vec_copy_construct (new_vec->address (), m_vecdata, len);
     }
   return new_vec;
 }
@@ -835,7 +855,7 @@ vec<T, A, vl_embed>::splice (const vec<T, A, vl_embed> &src)
   if (len)
     {
       gcc_checking_assert (space (len));
-      memcpy (address () + length (), src.address (), len * sizeof (T));
+      vec_copy_construct (end (), src.address (), len);
       m_vecpfx.m_num += len;
     }
 }
@@ -1089,13 +1109,12 @@ inline void
 vec<T, A, vl_embed>::quick_grow_cleared (unsigned len)
 {
   unsigned oldlen = length ();
-  size_t sz = sizeof (T) * (len - oldlen);
+  size_t growby = len - oldlen;
   quick_grow (len);
-  if (sz != 0)
-    memset (&(address ()[oldlen]), 0, sz);
+  if (growby != 0)
+    vec_default_construct (address () + oldlen, growby);
 }
 
-
 /* Garbage collection support for vec<T, A, vl_embed>.  */
 
 template<typename T>
@@ -1454,7 +1473,7 @@ vec<T, va_heap, vl_ptr>::reserve (unsigned nelems, bool exact MEM_STAT_DECL)
   va_heap::reserve (m_vec, nelems, exact PASS_MEM_STAT);
   if (handle_auto_vec)
     {
-      memcpy (m_vec->address (), oldvec->address (), sizeof (T) * oldsize);
+      vec_copy_construct (m_vec->address (), oldvec->address (), oldsize);
       m_vec->m_vecpfx.m_num = oldsize;
     }
 
@@ -1616,10 +1635,10 @@ inline void
 vec<T, va_heap, vl_ptr>::safe_grow_cleared (unsigned len MEM_STAT_DECL)
 {
   unsigned oldlen = length ();
-  size_t sz = sizeof (T) * (len - oldlen);
+  size_t growby = len - oldlen;
   safe_grow (len PASS_MEM_STAT);
-  if (sz != 0)
-    memset (&(address ()[oldlen]), 0, sz);
+  if (growby != 0)
+    vec_default_construct (address () + oldlen, growby);
 }
 
 
diff --git a/libcpp/line-map.c b/libcpp/line-map.c
index c4b7cb2..56b7913 100644
--- a/libcpp/line-map.c
+++ b/libcpp/line-map.c
@@ -62,7 +62,8 @@ extern unsigned num_macro_tokens_counter;
 
 line_maps::~line_maps ()
 {
-  htab_delete (location_adhoc_data_map.htab);
+  if (location_adhoc_data_map.htab)
+    htab_delete (location_adhoc_data_map.htab);
 }
 
 /* Hash function for location_adhoc_data hashtable.  */
@@ -347,7 +348,7 @@ void
 linemap_init (struct line_maps *set,
 	      source_location builtin_location)
 {
-  memset (set, 0, sizeof (struct line_maps));
+  *set = line_maps ();
   set->highest_location = RESERVED_LOCATION_COUNT - 1;
   set->highest_line = RESERVED_LOCATION_COUNT - 1;
   set->location_adhoc_data_map.htab =
diff --git a/libitm/beginend.cc b/libitm/beginend.cc
index d04f3e9..c6550a3 100644
--- a/libitm/beginend.cc
+++ b/libitm/beginend.cc
@@ -431,7 +431,7 @@ GTM::gtm_transaction_cp::save(gtm_thread* tx)
   // Save everything that we might have to restore on restarts or aborts.
   jb = tx->jb;
   undolog_size = tx->undolog.size();
-  memcpy(&alloc_actions, &tx->alloc_actions, sizeof(alloc_actions));
+  alloc_actions = tx->alloc_actions;
   user_actions_size = tx->user_actions.size();
   id = tx->id;
   prop = tx->prop;
@@ -449,7 +449,7 @@ GTM::gtm_transaction_cp::commit(gtm_thread* tx)
   // commits of nested transactions. Allocation actions must be committed
   // before committing the snapshot.
   tx->jb = jb;
-  memcpy(&tx->alloc_actions, &alloc_actions, sizeof(alloc_actions));
+  tx->alloc_actions = alloc_actions;
   tx->id = id;
   tx->prop = prop;
 }
@@ -485,7 +485,7 @@ GTM::gtm_thread::rollback (gtm_transaction_cp *cp, bool aborting)
       prop = cp->prop;
       if (cp->disp != abi_disp())
 	set_abi_disp(cp->disp);
-      memcpy(&alloc_actions, &cp->alloc_actions, sizeof(alloc_actions));
+      alloc_actions = cp->alloc_actions;
       nesting = cp->nesting;
     }
   else
diff --git a/libitm/method-ml.cc b/libitm/method-ml.cc
index fcae334..b857bff 100644
--- a/libitm/method-ml.cc
+++ b/libitm/method-ml.cc
@@ -138,7 +138,11 @@ struct ml_mg : public method_group
     // This store is only executed while holding the serial lock, so relaxed
     // memory order is sufficient here.  Same holds for the memset.
     time.store(0, memory_order_relaxed);
-    memset(orecs, 0, sizeof(atomic<gtm_word>) * L2O_ORECS);
+    // The memset below isn't strictly kosher because it bypasses
+    // the non-trivial assignment operator defined by std::atomic.  Using
+    // a local void* is enough to prevent GCC from warning for this.
+    void *p = orecs;
+    memset(p, 0, sizeof(atomic<gtm_word>) * L2O_ORECS);
   }
 };
 

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH] handle bzero/bcopy in DSE and aliasing (PR 80933, 80934)
  2017-06-07  3:26   ` Martin Sebor
@ 2017-06-07  8:23     ` Richard Biener
  2017-06-07 14:46       ` Martin Sebor
  0 siblings, 1 reply; 18+ messages in thread
From: Richard Biener @ 2017-06-07  8:23 UTC (permalink / raw)
  To: Martin Sebor; +Cc: Gcc Patch List

On Wed, Jun 7, 2017 at 5:26 AM, Martin Sebor <msebor@gmail.com> wrote:
>> Note I'd be _much_ more sympathetic to simply canonicalizing all of
>> bzero and bcopy
>> to memset / memmove and be done with all the above complexity.
>
>
> Attached is an updated patch along these lines.  Please let me
> know if it matches your expectations.

I think you attached the wrong patch.

Richard.

> FWIW, although I don't feel too strongly about bzero et al. I'm
> not sure that this approach is the right one in general.  It might
> (slightly) simplify GCC itself, but other than the incidental code
> generation improvement, it offers no benefit to users.  In some
> cases, it even degrades user experience by causing GCC issue
> diagnostics that refer to functions that don't appear in the source
> code, such as for:
>
>   char d[1];
>
>   void* f (const void *p)
>   {
>     bzero (d, 7);
>   }
>
>   warning: ‘__builtin_memset’ writing 7 bytes into a region of size 1
> overflows the destination [-Wstringop-overflow=]
>
> For some functions like mempcpy it might even worse code overall
> (slower and bigger).
>
> In other cases (like profiling) it loses interesting information.
>
> I think these types of transformations would be justified  f they
> were done based on measurably improved efficiency of the generated
> code, but I'm uneasy about swapping calls to one function for another
> solely because it simplifies the implementation.  Not least because
> it doesn't seem like a viable general approach to simplifying the
> implementation.
>
> Martin
>
> PS I stopped short of simplifying GCC to remove the existing special
> handling of these three built-ins.  If the patch is approved I'm
> willing to do the cleanup in a subsequent pass.

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH] handle bzero/bcopy in DSE and aliasing (PR 80933, 80934)
  2017-06-07  8:23     ` Richard Biener
@ 2017-06-07 14:46       ` Martin Sebor
  2017-06-07 18:37         ` Bernhard Reutner-Fischer
  0 siblings, 1 reply; 18+ messages in thread
From: Martin Sebor @ 2017-06-07 14:46 UTC (permalink / raw)
  To: Richard Biener; +Cc: Gcc Patch List

[-- Attachment #1: Type: text/plain, Size: 1902 bytes --]

On 06/07/2017 02:23 AM, Richard Biener wrote:
> On Wed, Jun 7, 2017 at 5:26 AM, Martin Sebor <msebor@gmail.com> wrote:
>>> Note I'd be _much_ more sympathetic to simply canonicalizing all of
>>> bzero and bcopy
>>> to memset / memmove and be done with all the above complexity.
>>
>>
>> Attached is an updated patch along these lines.  Please let me
>> know if it matches your expectations.
>
> I think you attached the wrong patch.

Yes I did, sorry.  The correct one is attached.

Martin

>
> Richard.
>
>> FWIW, although I don't feel too strongly about bzero et al. I'm
>> not sure that this approach is the right one in general.  It might
>> (slightly) simplify GCC itself, but other than the incidental code
>> generation improvement, it offers no benefit to users.  In some
>> cases, it even degrades user experience by causing GCC issue
>> diagnostics that refer to functions that don't appear in the source
>> code, such as for:
>>
>>   char d[1];
>>
>>   void* f (const void *p)
>>   {
>>     bzero (d, 7);
>>   }
>>
>>   warning: ‘__builtin_memset’ writing 7 bytes into a region of size 1
>> overflows the destination [-Wstringop-overflow=]
>>
>> For some functions like mempcpy it might even worse code overall
>> (slower and bigger).
>>
>> In other cases (like profiling) it loses interesting information.
>>
>> I think these types of transformations would be justified  f they
>> were done based on measurably improved efficiency of the generated
>> code, but I'm uneasy about swapping calls to one function for another
>> solely because it simplifies the implementation.  Not least because
>> it doesn't seem like a viable general approach to simplifying the
>> implementation.
>>
>> Martin
>>
>> PS I stopped short of simplifying GCC to remove the existing special
>> handling of these three built-ins.  If the patch is approved I'm
>> willing to do the cleanup in a subsequent pass.


[-- Attachment #2: gcc-80933.diff --]
[-- Type: text/x-patch, Size: 6264 bytes --]

PR tree-optimization/80934 - bzero should be assumed not to escape pointer argument
PR tree-optimization/80933 - redundant bzero/bcopy calls not eliminated

gcc/ChangeLog:

	PR tree-optimization/80933
	PR tree-optimization/80934
	* builtins.c (fold_builtin_bcmp, fold_builtin_bcopy): New functions.
	(fold_builtin_bzero): Likewise.
	(fold_builtin_2): Handle bzero.
	(fold_builtin_3): Handle bcmp and bcpy.

gcc/testsuite/ChangeLog:

	PR tree-optimization/80933
	PR tree-optimization/80934
	* gcc.dg/fold-bcopy.c: New test.
	* gcc.dg/tree-ssa/ssa-dse-30.c: Likewise..
	* gcc.dg/tree-ssa/alias-36.c: Likewise.
	* gcc/testsuite/gcc.dg/pr79214.c: Adjust.

diff --git a/gcc/builtins.c b/gcc/builtins.c
index 30462ad..c6a2ec5 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -145,6 +145,9 @@ static rtx expand_builtin_unop (machine_mode, tree, rtx, rtx, optab);
 static rtx expand_builtin_frame_address (tree, tree);
 static tree stabilize_va_list_loc (location_t, tree, int);
 static rtx expand_builtin_expect (tree, rtx);
+static tree fold_builtin_bcmp (location_t, tree, tree, tree);
+static tree fold_builtin_bcopy (location_t, tree, tree, tree);
+static tree fold_builtin_bzero (location_t, tree, tree);
 static tree fold_builtin_constant_p (tree);
 static tree fold_builtin_classify_type (tree);
 static tree fold_builtin_strlen (location_t, tree, tree);
@@ -7982,6 +7985,53 @@ fold_builtin_sincos (location_t loc,
 			 fold_build1_loc (loc, REALPART_EXPR, type, call)));
 }
 
+/* Fold function call to built-in bzero with arguments SRC and LEN
+   into a call to built-in memset (DST, 0, LEN).  */
+
+static tree
+fold_builtin_bzero (location_t loc, tree dst, tree len)
+{
+  if (!validate_arg (dst, POINTER_TYPE)
+      || !validate_arg (len, INTEGER_TYPE))
+    return NULL_TREE;
+
+  tree fn = builtin_decl_implicit (BUILT_IN_MEMSET);
+  return build_call_expr_loc (loc, fn, 3, dst, integer_zero_node, len);
+}
+
+/* Fold function call to built-in bcmp with arguments ARG1, ARG2, and LEN
+   into a call to built-in memcmp(ARG1, ARG2, LEN).  */
+
+static tree
+fold_builtin_bcmp (location_t loc, tree arg1, tree arg2, tree len)
+{
+  if (tree ret = fold_builtin_memcmp (loc, arg1, arg2, len))
+    return ret;
+
+  if (!validate_arg (arg1, POINTER_TYPE)
+      || !validate_arg (arg2, POINTER_TYPE)
+      || !validate_arg (len, INTEGER_TYPE))
+    return NULL_TREE;
+
+  tree fn = builtin_decl_implicit (BUILT_IN_MEMCMP);
+  return build_call_expr_loc (loc, fn, 3, arg1, arg2, len);
+}
+
+/* Fold function call to built-in bcopy with arguments SRC, DST, and LEN
+   into a call to built-in memcpy(DST, SRC, LEN).  */
+
+static tree
+fold_builtin_bcopy (location_t loc, tree src, tree dst, tree len)
+{
+  if (!validate_arg (src, POINTER_TYPE)
+      || !validate_arg (dst, POINTER_TYPE)
+      || !validate_arg (len, INTEGER_TYPE))
+    return NULL_TREE;
+
+  tree fn = builtin_decl_implicit (BUILT_IN_MEMCPY);
+  return build_call_expr_loc (loc, fn, 3, dst, src, len);
+}
+
 /* Fold function call to builtin memcmp with arguments ARG1 and ARG2.
    Return NULL_TREE if no simplification can be made.  */
 
@@ -8947,6 +8997,9 @@ fold_builtin_2 (location_t loc, tree fndecl, tree arg0, tree arg1)
     CASE_FLT_FN (BUILT_IN_MODF):
       return fold_builtin_modf (loc, arg0, arg1, type);
 
+    case BUILT_IN_BZERO:
+      return fold_builtin_bzero (loc, arg0, arg1);
+
     case BUILT_IN_STRSPN:
       return fold_builtin_strspn (loc, arg0, arg1);
 
@@ -9034,7 +9087,12 @@ fold_builtin_3 (location_t loc, tree fndecl,
 	return do_mpfr_remquo (arg0, arg1, arg2);
     break;
 
+    case BUILT_IN_BCOPY:
+      return fold_builtin_bcopy (loc, arg0, arg1, arg2);;
+
     case BUILT_IN_BCMP:
+      return fold_builtin_bcmp (loc, arg0, arg1, arg2);;
+
     case BUILT_IN_MEMCMP:
       return fold_builtin_memcmp (loc, arg0, arg1, arg2);;
 
diff --git a/gcc/testsuite/gcc.dg/fold-bcopy.c b/gcc/testsuite/gcc.dg/fold-bcopy.c
new file mode 100644
index 0000000..bf6a143
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/fold-bcopy.c
@@ -0,0 +1,21 @@
+/* PR tree-optimization/80933 - redundant bzero/bcopy calls not eliminated
+   { dg-do compile }
+   { dg-options "-O0 -fdump-tree-gimple" } */
+
+void* f0 (void *p, const void *q, unsigned n)
+{
+  return __builtin_bcopy (q, p, n);
+}
+
+int f1 (const void *p, const void *q, unsigned n)
+{
+  return __builtin_bcmp (q, p, n);
+}
+
+void f2 (void *p, unsigned n)
+{
+  __builtin_bzero (p, n);
+}
+
+/* { dg-final { scan-tree-dump-not "bcmp|bcopy|bzero" "gimple" } } */
+/* { dg-final { scan-tree-dump-times "memcpy|memcmp|memset" 3 "gimple" } } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/alias-36.c b/gcc/testsuite/gcc.dg/tree-ssa/alias-36.c
new file mode 100644
index 0000000..61b601a
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/alias-36.c
@@ -0,0 +1,28 @@
+/* PR tree-optimization/80934 - bzero should be assumed not to escape
+   pointer argument
+   { dg-do compile }
+   { dg-options "-O2 -fdump-tree-alias" } */
+
+void foobar (void);
+
+void f (void);
+
+void g (void)
+{
+  char d[32];
+  __builtin_memset (d, 0, sizeof d);
+  f ();
+  if (*d != 0)
+    foobar ();
+}
+
+void h (void)
+{
+  char d[32];
+  __builtin_bzero (d, sizeof d);
+  f ();
+  if (*d != 0)
+    foobar ();
+}
+
+/* { dg-final { scan-tree-dump-not "memset|foobar|bzero" "alias" } } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/ssa-dse-30.c b/gcc/testsuite/gcc.dg/tree-ssa/ssa-dse-30.c
new file mode 100644
index 0000000..9d2c920
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/ssa-dse-30.c
@@ -0,0 +1,30 @@
+/* PR tree-optimization/80933 - redundant bzero/bcopy calls not eliminated
+   { dg-do compile }
+   { dg-options "-O2 -fdump-tree-dse1" } */
+
+void sink (void*);
+
+void test_bcopy (const void *s)
+{
+  char d[33];
+
+  /* The bcopy calls are expanded inline in EVRP, before DSE runs,
+     so this test doesn't actually verify that DSE does its job.  */
+  __builtin_bcopy (s, d, sizeof d);
+  __builtin_bcopy (s, d, sizeof d);
+
+  sink (d);
+}
+
+void test_bzero (void)
+{
+  char d[33];
+
+  __builtin_bzero (d, sizeof d);
+  __builtin_bzero (d, sizeof d);
+
+  sink (d);
+}
+
+/* { dg-final { scan-tree-dump-times "builtin_bzero" 1 "dse1" } } */
+/* { dg-final { scan-tree-dump-not "builtin_bcopy" "dse1" } } */

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH] handle bzero/bcopy in DSE and aliasing (PR 80933, 80934)
  2017-06-07 14:46       ` Martin Sebor
@ 2017-06-07 18:37         ` Bernhard Reutner-Fischer
  2017-06-07 20:01           ` Marc Glisse
  2017-06-07 20:23           ` Joseph Myers
  0 siblings, 2 replies; 18+ messages in thread
From: Bernhard Reutner-Fischer @ 2017-06-07 18:37 UTC (permalink / raw)
  To: gcc-patches, Martin Sebor, Richard Biener; +Cc: Gcc Patch List

On 7 June 2017 16:46:53 CEST, Martin Sebor <msebor@gmail.com> wrote:
>On 06/07/2017 02:23 AM, Richard Biener wrote:
>> On Wed, Jun 7, 2017 at 5:26 AM, Martin Sebor <msebor@gmail.com>
>wrote:
>>>> Note I'd be _much_ more sympathetic to simply canonicalizing all of
>>>> bzero and bcopy
>>>> to memset / memmove and be done with all the above complexity.
>>>
>>>
>>> Attached is an updated patch along these lines.  Please let me
>>> know if it matches your expectations.
>>
>> I think you attached the wrong patch.
>
>Yes I did, sorry.  The correct one is attached.

Under POSIX.1-2008 "optimizing" bzero or bcmp is IMO plain wrong.

It's like optimizing foo() to a random built-in but maybe that's just me. If your libc provides a define to a standard function for these under a compat knob then fine but otherwise you should fix that. *shrug*. Joseph?

thanks,
>
>Martin
>
>>
>> Richard.
>>
>>> FWIW, although I don't feel too strongly about bzero et al. I'm
>>> not sure that this approach is the right one in general.  It might
>>> (slightly) simplify GCC itself, but other than the incidental code
>>> generation improvement, it offers no benefit to users.  In some
>>> cases, it even degrades user experience by causing GCC issue
>>> diagnostics that refer to functions that don't appear in the source
>>> code, such as for:
>>>
>>>   char d[1];
>>>
>>>   void* f (const void *p)
>>>   {
>>>     bzero (d, 7);
>>>   }
>>>
>>>   warning: ‘__builtin_memset’ writing 7 bytes into a region of size
>1
>>> overflows the destination [-Wstringop-overflow=]
>>>
>>> For some functions like mempcpy it might even worse code overall
>>> (slower and bigger).
>>>
>>> In other cases (like profiling) it loses interesting information.
>>>
>>> I think these types of transformations would be justified  f they
>>> were done based on measurably improved efficiency of the generated
>>> code, but I'm uneasy about swapping calls to one function for
>another
>>> solely because it simplifies the implementation.  Not least because
>>> it doesn't seem like a viable general approach to simplifying the
>>> implementation.
>>>
>>> Martin
>>>
>>> PS I stopped short of simplifying GCC to remove the existing special
>>> handling of these three built-ins.  If the patch is approved I'm
>>> willing to do the cleanup in a subsequent pass.

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH] handle bzero/bcopy in DSE and aliasing (PR 80933, 80934)
  2017-06-07 18:37         ` Bernhard Reutner-Fischer
@ 2017-06-07 20:01           ` Marc Glisse
  2017-06-07 20:12             ` Martin Sebor
  2017-06-07 20:23           ` Joseph Myers
  1 sibling, 1 reply; 18+ messages in thread
From: Marc Glisse @ 2017-06-07 20:01 UTC (permalink / raw)
  To: Bernhard Reutner-Fischer; +Cc: Gcc Patch List, Martin Sebor, Richard Biener

On Wed, 7 Jun 2017, Bernhard Reutner-Fischer wrote:

> On 7 June 2017 16:46:53 CEST, Martin Sebor <msebor@gmail.com> wrote:
>> On 06/07/2017 02:23 AM, Richard Biener wrote:
>>> On Wed, Jun 7, 2017 at 5:26 AM, Martin Sebor <msebor@gmail.com>
>> wrote:
>>>>> Note I'd be _much_ more sympathetic to simply canonicalizing all of
>>>>> bzero and bcopy
>>>>> to memset / memmove and be done with all the above complexity.
>>>>
>>>>
>>>> Attached is an updated patch along these lines.  Please let me
>>>> know if it matches your expectations.
>>>
>>> I think you attached the wrong patch.
>>
>> Yes I did, sorry.  The correct one is attached.
>
> Under POSIX.1-2008 "optimizing" bzero or bcmp is IMO plain wrong.
>
> It's like optimizing foo() to a random built-in but maybe that's just 
> me. If your libc provides a define to a standard function for these 
> under a compat knob then fine but otherwise you should fix that. 
> *shrug*. Joseph?

The patch optimizes __builtin_bzero, which should be ok. The question 
(independent from this patch) is then under what conditions bzero should 
be detected as a builtin.

-- 
Marc Glisse

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH] handle bzero/bcopy in DSE and aliasing (PR 80933, 80934)
  2017-06-07 20:01           ` Marc Glisse
@ 2017-06-07 20:12             ` Martin Sebor
  2017-06-08  2:33               ` Martin Sebor
  0 siblings, 1 reply; 18+ messages in thread
From: Martin Sebor @ 2017-06-07 20:12 UTC (permalink / raw)
  To: gcc-patches, Bernhard Reutner-Fischer; +Cc: Richard Biener

On 06/07/2017 02:01 PM, Marc Glisse wrote:
> On Wed, 7 Jun 2017, Bernhard Reutner-Fischer wrote:
>
>> On 7 June 2017 16:46:53 CEST, Martin Sebor <msebor@gmail.com> wrote:
>>> On 06/07/2017 02:23 AM, Richard Biener wrote:
>>>> On Wed, Jun 7, 2017 at 5:26 AM, Martin Sebor <msebor@gmail.com>
>>> wrote:
>>>>>> Note I'd be _much_ more sympathetic to simply canonicalizing all of
>>>>>> bzero and bcopy
>>>>>> to memset / memmove and be done with all the above complexity.
>>>>>
>>>>>
>>>>> Attached is an updated patch along these lines.  Please let me
>>>>> know if it matches your expectations.
>>>>
>>>> I think you attached the wrong patch.
>>>
>>> Yes I did, sorry.  The correct one is attached.
>>
>> Under POSIX.1-2008 "optimizing" bzero or bcmp is IMO plain wrong.
>>
>> It's like optimizing foo() to a random built-in but maybe that's just
>> me. If your libc provides a define to a standard function for these
>> under a compat knob then fine but otherwise you should fix that.
>> *shrug*. Joseph?
>
> The patch optimizes __builtin_bzero, which should be ok. The question
> (independent from this patch) is then under what conditions bzero should
> be detected as a builtin.

Yes.  The problem is that unlike for C and C++, GCC doesn't have
a mechanism to select the target version of POSIX.  I think it
should.

But there is a subtle problem with the patch that needs fixing.
Bcopy should not be transformed to memcpy but rather memmove.
I'll fix that before committing.

Martin

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH] handle bzero/bcopy in DSE and aliasing (PR 80933, 80934)
  2017-06-07 18:37         ` Bernhard Reutner-Fischer
  2017-06-07 20:01           ` Marc Glisse
@ 2017-06-07 20:23           ` Joseph Myers
  1 sibling, 0 replies; 18+ messages in thread
From: Joseph Myers @ 2017-06-07 20:23 UTC (permalink / raw)
  To: Bernhard Reutner-Fischer; +Cc: Gcc Patch List, Martin Sebor, Richard Biener

On Wed, 7 Jun 2017, Bernhard Reutner-Fischer wrote:

> It's like optimizing foo() to a random built-in but maybe that's just 
> me. If your libc provides a define to a standard function for these 
> under a compat knob then fine but otherwise you should fix that. 
> *shrug*. Joseph?

It's the usual thing of: if the -std option enables a function as built-in 
it can be assumed to have certain semantics if called (this does *not* 
mean it's appropriate to generate calls to it from code that called other 
functions, as the library might not have it at all; that's for the 
libc_has_function hook to specify).  Otherwise (-std=c11 etc. options) 
semantics cannot be assumed based on the name (they still can for 
__builtin_*) - but in principle it may be OK to assume semantics for a 
function whose name is reserved in the relevant C standard version, or if 
an explicit declaration came from a system header (which would generally 
be the case for e.g. -std=c99 -D_POSIX_C_SOURCE=whatever).

-- 
Joseph S. Myers
joseph@codesourcery.com

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH] handle bzero/bcopy in DSE and aliasing (PR 80933, 80934)
  2017-06-07 20:12             ` Martin Sebor
@ 2017-06-08  2:33               ` Martin Sebor
  2017-06-08  7:51                 ` Richard Biener
  0 siblings, 1 reply; 18+ messages in thread
From: Martin Sebor @ 2017-06-08  2:33 UTC (permalink / raw)
  To: gcc-patches, Richard Biener; +Cc: Bernhard Reutner-Fischer

[-- Attachment #1: Type: text/plain, Size: 1742 bytes --]

On 06/07/2017 02:12 PM, Martin Sebor wrote:
> On 06/07/2017 02:01 PM, Marc Glisse wrote:
>> On Wed, 7 Jun 2017, Bernhard Reutner-Fischer wrote:
>>
>>> On 7 June 2017 16:46:53 CEST, Martin Sebor <msebor@gmail.com> wrote:
>>>> On 06/07/2017 02:23 AM, Richard Biener wrote:
>>>>> On Wed, Jun 7, 2017 at 5:26 AM, Martin Sebor <msebor@gmail.com>
>>>> wrote:
>>>>>>> Note I'd be _much_ more sympathetic to simply canonicalizing all of
>>>>>>> bzero and bcopy
>>>>>>> to memset / memmove and be done with all the above complexity.
>>>>>>
>>>>>>
>>>>>> Attached is an updated patch along these lines.  Please let me
>>>>>> know if it matches your expectations.
>>>>>
>>>>> I think you attached the wrong patch.
>>>>
>>>> Yes I did, sorry.  The correct one is attached.
>>>
>>> Under POSIX.1-2008 "optimizing" bzero or bcmp is IMO plain wrong.
>>>
>>> It's like optimizing foo() to a random built-in but maybe that's just
>>> me. If your libc provides a define to a standard function for these
>>> under a compat knob then fine but otherwise you should fix that.
>>> *shrug*. Joseph?
>>
>> The patch optimizes __builtin_bzero, which should be ok. The question
>> (independent from this patch) is then under what conditions bzero should
>> be detected as a builtin.
>
> Yes.  The problem is that unlike for C and C++, GCC doesn't have
> a mechanism to select the target version of POSIX.  I think it
> should.
>
> But there is a subtle problem with the patch that needs fixing.
> Bcopy should not be transformed to memcpy but rather memmove.
> I'll fix that before committing.

Attached is an updated patch with this fix.  I also added a cast
from bcopy and bzero to void to detect accidental uses of the
return value.  Tested on x86_64-linux.

Martin

[-- Attachment #2: gcc-80933.diff --]
[-- Type: text/x-patch, Size: 9571 bytes --]

PR tree-optimization/80934 - bzero should be assumed not to escape pointer argument
PR tree-optimization/80933 - redundant bzero/bcopy calls not eliminated

gcc/ChangeLog:

	PR tree-optimization/80933
	PR tree-optimization/80934
	* builtins.c (fold_builtin_bcmp, fold_builtin_bcopy): New functions.
	(fold_builtin_bzero): Likewise.
	(fold_builtin_2): Handle bzero.
	(fold_builtin_3): Handle bcmp and bcpy.

gcc/testsuite/ChangeLog:

	PR tree-optimization/80933
	PR tree-optimization/80934
	* gcc.dg/fold-bcopy.c: New test.
	* gcc.dg/tree-ssa/ssa-dse-30.c: Likewise..
	* gcc.dg/tree-ssa/alias-36.c: Likewise.
	* gcc/testsuite/gcc.dg/pr79214.c: Adjust.
	* gcc.dg/tree-prof/val-prof-7.c: Likewise.
	* gcc.dg/Wsizeof-pointer-memaccess1.c: Likewise.
	* gcc.dg/builtins-nonnull.c: Likewise.

diff --git a/gcc/builtins.c b/gcc/builtins.c
index 30462ad..52d42b9 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -145,6 +145,9 @@ static rtx expand_builtin_unop (machine_mode, tree, rtx, rtx, optab);
 static rtx expand_builtin_frame_address (tree, tree);
 static tree stabilize_va_list_loc (location_t, tree, int);
 static rtx expand_builtin_expect (tree, rtx);
+static tree fold_builtin_bcmp (location_t, tree, tree, tree);
+static tree fold_builtin_bcopy (location_t, tree, tree, tree);
+static tree fold_builtin_bzero (location_t, tree, tree);
 static tree fold_builtin_constant_p (tree);
 static tree fold_builtin_classify_type (tree);
 static tree fold_builtin_strlen (location_t, tree, tree);
@@ -7982,6 +7985,56 @@ fold_builtin_sincos (location_t loc,
 			 fold_build1_loc (loc, REALPART_EXPR, type, call)));
 }
 
+/* Fold function call to built-in bzero with arguments SRC and LEN
+   into a call to built-in memset (DST, 0, LEN).  */
+
+static tree
+fold_builtin_bzero (location_t loc, tree dst, tree len)
+{
+  if (!validate_arg (dst, POINTER_TYPE)
+      || !validate_arg (len, INTEGER_TYPE))
+    return NULL_TREE;
+
+  tree fn = builtin_decl_implicit (BUILT_IN_MEMSET);
+  /* Call memset and return the result cast to void to detect its use
+     (bzero returns void).  */
+  tree call = build_call_expr_loc (loc, fn, 3, dst, integer_zero_node, len);
+  return fold_convert (void_type_node, call);
+}
+
+/* Fold function call to built-in bcmp with arguments ARG1, ARG2, and LEN
+   into a call to built-in memcmp(ARG1, ARG2, LEN).  */
+
+static tree
+fold_builtin_bcmp (location_t loc, tree arg1, tree arg2, tree len)
+{
+  if (!validate_arg (arg1, POINTER_TYPE)
+      || !validate_arg (arg2, POINTER_TYPE)
+      || !validate_arg (len, INTEGER_TYPE))
+    return NULL_TREE;
+
+  tree fn = builtin_decl_implicit (BUILT_IN_MEMCMP);
+  return build_call_expr_loc (loc, fn, 3, arg1, arg2, len);
+}
+
+/* Fold function call to built-in bcopy with arguments SRC, DST, and LEN
+   into a call to built-in memmove(DST, SRC, LEN).  */
+
+static tree
+fold_builtin_bcopy (location_t loc, tree src, tree dst, tree len)
+{
+  if (!validate_arg (src, POINTER_TYPE)
+      || !validate_arg (dst, POINTER_TYPE)
+      || !validate_arg (len, INTEGER_TYPE))
+    return NULL_TREE;
+
+  /* bcopy has been removed from POSIX in Issue 7 but Issue 6 specifies
+     it's quivalent to memmove (not memcpy).  Call memmove and return
+     the result cast to void to detect its use (bcopy returns void).  */
+  tree fn = builtin_decl_implicit (BUILT_IN_MEMMOVE);
+  return build_call_expr_loc (loc, fn, 3, dst, src, len);
+}
+
 /* Fold function call to builtin memcmp with arguments ARG1 and ARG2.
    Return NULL_TREE if no simplification can be made.  */
 
@@ -8947,6 +9000,9 @@ fold_builtin_2 (location_t loc, tree fndecl, tree arg0, tree arg1)
     CASE_FLT_FN (BUILT_IN_MODF):
       return fold_builtin_modf (loc, arg0, arg1, type);
 
+    case BUILT_IN_BZERO:
+      return fold_builtin_bzero (loc, arg0, arg1);
+
     case BUILT_IN_STRSPN:
       return fold_builtin_strspn (loc, arg0, arg1);
 
@@ -9034,7 +9090,12 @@ fold_builtin_3 (location_t loc, tree fndecl,
 	return do_mpfr_remquo (arg0, arg1, arg2);
     break;
 
+    case BUILT_IN_BCOPY:
+      return fold_builtin_bcopy (loc, arg0, arg1, arg2);;
+
     case BUILT_IN_BCMP:
+      return fold_builtin_bcmp (loc, arg0, arg1, arg2);;
+
     case BUILT_IN_MEMCMP:
       return fold_builtin_memcmp (loc, arg0, arg1, arg2);;
 
diff --git a/gcc/testsuite/gcc.dg/fold-bcopy.c b/gcc/testsuite/gcc.dg/fold-bcopy.c
new file mode 100644
index 0000000..ce5b317
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/fold-bcopy.c
@@ -0,0 +1,43 @@
+/* PR tree-optimization/80933 - redundant bzero/bcopy calls not eliminated
+   { dg-do compile }
+   { dg-options "-O0 -Wall -fdump-tree-gimple" } */
+
+void f0 (void *p, const void *q, unsigned n)
+{
+  /* Bcopy corresponds to memmmove, not memcpy.  */
+  __builtin_bcopy (q, p, n);
+}
+
+void f1 (void *p, const void *q, unsigned n)
+{
+  /* This call should be eliminated.  */
+  __builtin_bcopy (q, p, 0);
+}
+
+int f2 (const void *p, const void *q, unsigned n)
+{
+  return __builtin_bcmp (q, p, n);
+}
+
+int f3 (const void *p, const void *q, unsigned n)
+{
+  /* This call should be folded into 0.  */
+  return __builtin_bcmp (q, p, 0);
+}
+
+void f4 (void *p, unsigned n)
+{
+  __builtin_bzero (p, n);
+}
+
+void f5 (void *p)
+{
+  /* This call should be eliminated.  */
+  __builtin_bzero (p, 0);
+}
+
+/* Verify that calls to bcmp, bcopy, and bzero have all been removed
+   and one of each replaced with memcmp, memmove, and memset, respectively.
+   The remaining three should be eliminated.
+  { dg-final { scan-tree-dump-not "bcmp|bcopy|bzero" "gimple" } }
+  { dg-final { scan-tree-dump-times "memcmp|memmove|memset" 3 "gimple" } } */
diff --git a/gcc/testsuite/gcc.dg/pr79214.c b/gcc/testsuite/gcc.dg/pr79214.c
index 79d2a25..6cf254fb 100644
--- a/gcc/testsuite/gcc.dg/pr79214.c
+++ b/gcc/testsuite/gcc.dg/pr79214.c
@@ -22,7 +22,7 @@ size_t range (void)
 
 void test_bzero (void)
 {
-  bzero (d, range ());   /* { dg-warning ".__builtin_bzero. writing 4 or more bytes into a region of size 3 overflows the destination" } */
+  bzero (d, range ());   /* { dg-warning ".__builtin_(bzero|memset). writing 4 or more bytes into a region of size 3 overflows the destination" } */
 }
 
 void test_memcpy (void)
diff --git a/gcc/testsuite/gcc.dg/tree-prof/val-prof-7.c b/gcc/testsuite/gcc.dg/tree-prof/val-prof-7.c
index 84ec9fb..5a4e777 100644
--- a/gcc/testsuite/gcc.dg/tree-prof/val-prof-7.c
+++ b/gcc/testsuite/gcc.dg/tree-prof/val-prof-7.c
@@ -4,14 +4,10 @@
 char *buffer1;
 char *buffer2;
 
+/* Bzero is not tested because it gets transformed into memset.  */
+
 #define DEFINE_TEST(N) \
 __attribute__((noinline)) \
-void bzero_test_ ## N (int len) \
-{ \
-  __builtin_bzero (buffer1, len); \
-} \
- \
-__attribute__((noinline)) \
 void memcpy_test_ ## N (int len) \
 { \
   __builtin_memcpy (buffer1, buffer2, len); \
@@ -31,7 +27,6 @@ void memset_test_ ## N (int len) \
  \
 void test_stringops_ ## N(int len) \
 { \
-  bzero_test_ ## N (len); \
   memcpy_test_## N (len); \
   mempcpy_test_ ## N (len); \
   memset_test_ ## N (len); \
@@ -64,10 +59,6 @@ int main() {
   return 0;
 }
 
-/* { dg-final-use-not-autofdo { scan-ipa-dump "Single value 8 stringop transformation on __builtin_bzero" "profile" } } */
-/* { dg-final-use-not-autofdo { scan-ipa-dump "Single value 55 stringop transformation on __builtin_bzero" "profile" } } */
-/* { dg-final-use-not-autofdo { scan-ipa-dump-times "Single value 32 stringop transformation on __builtin_bzero" 0 "profile" } } */
-
 /* { dg-final-use-not-autofdo { scan-ipa-dump "Single value 8 stringop transformation on __builtin_memcpy" "profile" } } */
 /* { dg-final-use-not-autofdo { scan-ipa-dump "Single value 55 stringop transformation on __builtin_memcpy" "profile" } } */
 /* { dg-final-use-not-autofdo { scan-ipa-dump-times "Single value 32 stringop transformation on __builtin_memcpy" 0 "profile" } } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/alias-36.c b/gcc/testsuite/gcc.dg/tree-ssa/alias-36.c
new file mode 100644
index 0000000..61b601a
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/alias-36.c
@@ -0,0 +1,28 @@
+/* PR tree-optimization/80934 - bzero should be assumed not to escape
+   pointer argument
+   { dg-do compile }
+   { dg-options "-O2 -fdump-tree-alias" } */
+
+void foobar (void);
+
+void f (void);
+
+void g (void)
+{
+  char d[32];
+  __builtin_memset (d, 0, sizeof d);
+  f ();
+  if (*d != 0)
+    foobar ();
+}
+
+void h (void)
+{
+  char d[32];
+  __builtin_bzero (d, sizeof d);
+  f ();
+  if (*d != 0)
+    foobar ();
+}
+
+/* { dg-final { scan-tree-dump-not "memset|foobar|bzero" "alias" } } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/ssa-dse-30.c b/gcc/testsuite/gcc.dg/tree-ssa/ssa-dse-30.c
new file mode 100644
index 0000000..ece8cb2
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/ssa-dse-30.c
@@ -0,0 +1,31 @@
+/* PR tree-optimization/80933 - redundant bzero/bcopy calls not eliminated
+   { dg-do compile }
+   { dg-options "-O2 -fdump-tree-dse1" } */
+
+void sink (void*);
+
+void test_bcopy (const void *s)
+{
+  char d[33];
+
+  /* Bcopy is transformed into memcpy and those calls are expanded
+     inline in EVRP, before DSE runs, so this test doesn't actually
+     verify that DSE does its job.  */
+  __builtin_bcopy (s, d, sizeof d);
+  __builtin_bcopy (s, d, sizeof d);
+
+  sink (d);
+}
+
+void test_bzero (void)
+{
+  char d[33];
+
+  __builtin_bzero (d, sizeof d);
+  __builtin_bzero (d, sizeof d);
+
+  sink (d);
+}
+
+/* { dg-final { scan-tree-dump-times "builtin_memset" 1 "dse1" } } */
+/* { dg-final { scan-tree-dump-not "builtin_(bcopy|bzero|memcpy)" "dse1" } } */

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH] handle bzero/bcopy in DSE and aliasing (PR 80933, 80934)
  2017-06-08  2:33               ` Martin Sebor
@ 2017-06-08  7:51                 ` Richard Biener
  2017-06-08 16:29                   ` Martin Sebor
  0 siblings, 1 reply; 18+ messages in thread
From: Richard Biener @ 2017-06-08  7:51 UTC (permalink / raw)
  To: Martin Sebor; +Cc: GCC Patches, Bernhard Reutner-Fischer

On Thu, Jun 8, 2017 at 4:33 AM, Martin Sebor <msebor@gmail.com> wrote:
> On 06/07/2017 02:12 PM, Martin Sebor wrote:
>>
>> On 06/07/2017 02:01 PM, Marc Glisse wrote:
>>>
>>> On Wed, 7 Jun 2017, Bernhard Reutner-Fischer wrote:
>>>
>>>> On 7 June 2017 16:46:53 CEST, Martin Sebor <msebor@gmail.com> wrote:
>>>>>
>>>>> On 06/07/2017 02:23 AM, Richard Biener wrote:
>>>>>>
>>>>>> On Wed, Jun 7, 2017 at 5:26 AM, Martin Sebor <msebor@gmail.com>
>>>>>
>>>>> wrote:
>>>>>>>>
>>>>>>>> Note I'd be _much_ more sympathetic to simply canonicalizing all of
>>>>>>>> bzero and bcopy
>>>>>>>> to memset / memmove and be done with all the above complexity.
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>> Attached is an updated patch along these lines.  Please let me
>>>>>>> know if it matches your expectations.
>>>>>>
>>>>>>
>>>>>> I think you attached the wrong patch.
>>>>>
>>>>>
>>>>> Yes I did, sorry.  The correct one is attached.
>>>>
>>>>
>>>> Under POSIX.1-2008 "optimizing" bzero or bcmp is IMO plain wrong.
>>>>
>>>> It's like optimizing foo() to a random built-in but maybe that's just
>>>> me. If your libc provides a define to a standard function for these
>>>> under a compat knob then fine but otherwise you should fix that.
>>>> *shrug*. Joseph?
>>>
>>>
>>> The patch optimizes __builtin_bzero, which should be ok. The question
>>> (independent from this patch) is then under what conditions bzero should
>>> be detected as a builtin.
>>
>>
>> Yes.  The problem is that unlike for C and C++, GCC doesn't have
>> a mechanism to select the target version of POSIX.  I think it
>> should.
>>
>> But there is a subtle problem with the patch that needs fixing.
>> Bcopy should not be transformed to memcpy but rather memmove.
>> I'll fix that before committing.
>
>
> Attached is an updated patch with this fix.  I also added a cast
> from bcopy and bzero to void to detect accidental uses of the
> return value.  Tested on x86_64-linux.

Please do not add foldings to builtins.c but instead add them to gimple-fold.c.

+  /* Call memset and return the result cast to void to detect its use
+     (bzero returns void).  */
+  tree call = build_call_expr_loc (loc, fn, 3, dst, integer_zero_node, len);
+  return fold_convert (void_type_node, call);

???  How can the result be used if the original call result was not?

Thanks,
Richard.

> Martin

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH] handle bzero/bcopy in DSE and aliasing (PR 80933, 80934)
  2017-06-08  7:51                 ` Richard Biener
@ 2017-06-08 16:29                   ` Martin Sebor
  2017-06-09 12:35                     ` Richard Biener
  0 siblings, 1 reply; 18+ messages in thread
From: Martin Sebor @ 2017-06-08 16:29 UTC (permalink / raw)
  To: Richard Biener; +Cc: GCC Patches, Bernhard Reutner-Fischer

[-- Attachment #1: Type: text/plain, Size: 2735 bytes --]

On 06/08/2017 01:51 AM, Richard Biener wrote:
> On Thu, Jun 8, 2017 at 4:33 AM, Martin Sebor <msebor@gmail.com> wrote:
>> On 06/07/2017 02:12 PM, Martin Sebor wrote:
>>>
>>> On 06/07/2017 02:01 PM, Marc Glisse wrote:
>>>>
>>>> On Wed, 7 Jun 2017, Bernhard Reutner-Fischer wrote:
>>>>
>>>>> On 7 June 2017 16:46:53 CEST, Martin Sebor <msebor@gmail.com> wrote:
>>>>>>
>>>>>> On 06/07/2017 02:23 AM, Richard Biener wrote:
>>>>>>>
>>>>>>> On Wed, Jun 7, 2017 at 5:26 AM, Martin Sebor <msebor@gmail.com>
>>>>>>
>>>>>> wrote:
>>>>>>>>>
>>>>>>>>> Note I'd be _much_ more sympathetic to simply canonicalizing all of
>>>>>>>>> bzero and bcopy
>>>>>>>>> to memset / memmove and be done with all the above complexity.
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>> Attached is an updated patch along these lines.  Please let me
>>>>>>>> know if it matches your expectations.
>>>>>>>
>>>>>>>
>>>>>>> I think you attached the wrong patch.
>>>>>>
>>>>>>
>>>>>> Yes I did, sorry.  The correct one is attached.
>>>>>
>>>>>
>>>>> Under POSIX.1-2008 "optimizing" bzero or bcmp is IMO plain wrong.
>>>>>
>>>>> It's like optimizing foo() to a random built-in but maybe that's just
>>>>> me. If your libc provides a define to a standard function for these
>>>>> under a compat knob then fine but otherwise you should fix that.
>>>>> *shrug*. Joseph?
>>>>
>>>>
>>>> The patch optimizes __builtin_bzero, which should be ok. The question
>>>> (independent from this patch) is then under what conditions bzero should
>>>> be detected as a builtin.
>>>
>>>
>>> Yes.  The problem is that unlike for C and C++, GCC doesn't have
>>> a mechanism to select the target version of POSIX.  I think it
>>> should.
>>>
>>> But there is a subtle problem with the patch that needs fixing.
>>> Bcopy should not be transformed to memcpy but rather memmove.
>>> I'll fix that before committing.
>>
>>
>> Attached is an updated patch with this fix.  I also added a cast
>> from bcopy and bzero to void to detect accidental uses of the
>> return value.  Tested on x86_64-linux.
>
> Please do not add foldings to builtins.c but instead add them to gimple-fold.c.

Sure.  Attached is an adjusted patch.

>
> +  /* Call memset and return the result cast to void to detect its use
> +     (bzero returns void).  */
> +  tree call = build_call_expr_loc (loc, fn, 3, dst, integer_zero_node, len);
> +  return fold_convert (void_type_node, call);
>
> ???  How can the result be used if the original call result was not?

The cast ensured GCC would continue to warn on code like:

   void f (void *d, unsigned n)
   {
     return bzero (d, n);
   }

Without the cast (as in the first patch) the above was silently
accepted.

This isn't necessary when the folding is done in gimple-fold.c.

Martin

[-- Attachment #2: gcc-80933.diff --]
[-- Type: text/x-patch, Size: 11357 bytes --]

PR tree-optimization/80934 - bzero should be assumed not to escape pointer argument
PR tree-optimization/80933 - redundant bzero/bcopy calls not eliminated

gcc/ChangeLog:

	PR tree-optimization/80933
	PR tree-optimization/80934
	* builtins.c (fold_builtin_3): Do not handle bcmp here.
	* gimple-fold.c (gimple_fold_builtin_bcmp): New function.
	(gimple_fold_builtin_bcopy, gimple_fold_builtin_bzero): Likewise.
	(gimple_fold_builtin): Call them.

gcc/testsuite/ChangeLog:

	PR tree-optimization/80933
	PR tree-optimization/80934
	* gcc.dg/fold-bcopy.c: New test.
	* gcc.dg/tree-ssa/ssa-dse-30.c: Likewise..
	* gcc.dg/tree-ssa/alias-36.c: Likewise.
	* gcc/testsuite/gcc.dg/pr79214.c: Adjust.
	* gcc.dg/tree-prof/val-prof-7.c: Likewise.
	* gcc.dg/Wsizeof-pointer-memaccess1.c: Likewise.
	* gcc.dg/builtins-nonnull.c: Likewise.

diff --git a/gcc/builtins.c b/gcc/builtins.c
index 30462ad..ce657bf 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -9034,7 +9034,6 @@ fold_builtin_3 (location_t loc, tree fndecl,
 	return do_mpfr_remquo (arg0, arg1, arg2);
     break;
 
-    case BUILT_IN_BCMP:
     case BUILT_IN_MEMCMP:
       return fold_builtin_memcmp (loc, arg0, arg1, arg2);;
 
diff --git a/gcc/gimple-fold.c b/gcc/gimple-fold.c
index d12f9d0..159a7e6 100644
--- a/gcc/gimple-fold.c
+++ b/gcc/gimple-fold.c
@@ -1076,6 +1076,88 @@ done:
   return true;
 }
 
+/* Transform a call to built-in bcmp(a, b, len) at *GSI into one
+   to built-in memcmp (a, b, len).  */
+
+static bool
+gimple_fold_builtin_bcmp (gimple_stmt_iterator *gsi)
+{
+  tree fn = builtin_decl_implicit (BUILT_IN_MEMCMP);
+
+  if (!fn)
+    return false;
+
+  /* Transform bcmp (a, b, len) into memcmp (a, b, len).  */
+
+  gimple *stmt = gsi_stmt (*gsi);
+  tree a = gimple_call_arg (stmt, 0);
+  tree b = gimple_call_arg (stmt, 1);
+  tree len = gimple_call_arg (stmt, 2);
+
+  gimple_seq seq = NULL;
+  gimple *repl = gimple_build_call (fn, 3, a, b, len);
+  gimple_seq_add_stmt_without_update (&seq, repl);
+  gsi_replace_with_seq_vops (gsi, seq);
+  fold_stmt (gsi);
+
+  return true;
+}
+
+/* Transform a call to built-in bcopy (src, dest, len) at *GSI into one
+   to built-in memmove (dest, src, len).  */
+
+static bool
+gimple_fold_builtin_bcopy (gimple_stmt_iterator *gsi)
+{
+  tree fn = builtin_decl_implicit (BUILT_IN_MEMMOVE);
+
+  if (!fn)
+    return false;
+
+  /* bcopy has been removed from POSIX in Issue 7 but Issue 6 specifies
+     it's quivalent to memmove (not memcpy).  Transform bcopy (src, dest,
+     len) into memmove (dest, src, len).  */
+
+  gimple *stmt = gsi_stmt (*gsi);
+  tree src = gimple_call_arg (stmt, 0);
+  tree dest = gimple_call_arg (stmt, 1);
+  tree len = gimple_call_arg (stmt, 2);
+
+  gimple_seq seq = NULL;
+  gimple *repl = gimple_build_call (fn, 3, dest, src, len);
+  gimple_seq_add_stmt_without_update (&seq, repl);
+  gsi_replace_with_seq_vops (gsi, seq);
+  fold_stmt (gsi);
+
+  return true;
+}
+
+/* Transform a call to built-in bzero (dest, len) at *GSI into one
+   to built-in memset (dest, 0, len).  */
+
+static bool
+gimple_fold_builtin_bzero (gimple_stmt_iterator *gsi)
+{
+  tree fn = builtin_decl_implicit (BUILT_IN_MEMSET);
+
+  if (!fn)
+    return false;
+
+  /* Transform bzero (dest, len) into memset (dest, 0, len).  */
+
+  gimple *stmt = gsi_stmt (*gsi);
+  tree dest = gimple_call_arg (stmt, 0);
+  tree len = gimple_call_arg (stmt, 1);
+
+  gimple_seq seq = NULL;
+  gimple *repl = gimple_build_call (fn, 3, dest, integer_zero_node, len);
+  gimple_seq_add_stmt_without_update (&seq, repl);
+  gsi_replace_with_seq_vops (gsi, seq);
+  fold_stmt (gsi);
+
+  return true;
+}
+
 /* Fold function call to builtin memset or bzero at *GSI setting the
    memory of size LEN to VAL.  Return whether a simplification was made.  */
 
@@ -3287,16 +3369,17 @@ gimple_fold_builtin (gimple_stmt_iterator *gsi)
   enum built_in_function fcode = DECL_FUNCTION_CODE (callee);
   switch (fcode)
     {
+    case BUILT_IN_BCMP:
+      return gimple_fold_builtin_bcmp (gsi);
+    case BUILT_IN_BCOPY:
+      return gimple_fold_builtin_bcopy (gsi);
     case BUILT_IN_BZERO:
-      return gimple_fold_builtin_memset (gsi, integer_zero_node,
-					 gimple_call_arg (stmt, 1));
+      return gimple_fold_builtin_bzero (gsi);
+
     case BUILT_IN_MEMSET:
       return gimple_fold_builtin_memset (gsi,
 					 gimple_call_arg (stmt, 1),
 					 gimple_call_arg (stmt, 2));
-    case BUILT_IN_BCOPY:
-      return gimple_fold_builtin_memory_op (gsi, gimple_call_arg (stmt, 1),
-					    gimple_call_arg (stmt, 0), 3);
     case BUILT_IN_MEMCPY:
       return gimple_fold_builtin_memory_op (gsi, gimple_call_arg (stmt, 0),
 					    gimple_call_arg (stmt, 1), 0);
diff --git a/gcc/testsuite/gcc.dg/Wsizeof-pointer-memaccess1.c b/gcc/testsuite/gcc.dg/Wsizeof-pointer-memaccess1.c
index 7feb122..f4e8552 100644
--- a/gcc/testsuite/gcc.dg/Wsizeof-pointer-memaccess1.c
+++ b/gcc/testsuite/gcc.dg/Wsizeof-pointer-memaccess1.c
@@ -1,6 +1,6 @@
 /* Test -Wsizeof-pointer-memaccess warnings.  */
 /* { dg-do compile } */
-/* { dg-options "-Wall -Wno-sizeof-array-argument" } */
+/* { dg-options "-Wall -Wno-sizeof-array-argument -Wno-stringop-overflow" } */
 /* { dg-require-effective-target alloca } */
 
 typedef __SIZE_TYPE__ size_t;
diff --git a/gcc/testsuite/gcc.dg/builtins-nonnull.c b/gcc/testsuite/gcc.dg/builtins-nonnull.c
index fa9eaf2..726f8e1 100644
--- a/gcc/testsuite/gcc.dg/builtins-nonnull.c
+++ b/gcc/testsuite/gcc.dg/builtins-nonnull.c
@@ -24,8 +24,9 @@ void sink (int, ...);
 
 void test_memfuncs (void *s, unsigned n)
 {
-  /* Bzero is not declared attribute nonnull.  */
-  bzero (null (), n);
+  /* Bzero is not declared attribute nonnull (maybe it should be?)
+     but it's transformed into a call to memset() which is.  */
+  bzero (null (), n);             /* { dg-warning "argument 1 null where non-null expected" } */
 
   T (memcpy (null (), s, n));     /* { dg-warning "argument 1 null where non-null expected" } */
   T (memcpy (s, null (), n));     /* { dg-warning "argument 2 null where non-null expected" } */
diff --git a/gcc/testsuite/gcc.dg/fold-bcopy.c b/gcc/testsuite/gcc.dg/fold-bcopy.c
new file mode 100644
index 0000000..ce5b317
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/fold-bcopy.c
@@ -0,0 +1,43 @@
+/* PR tree-optimization/80933 - redundant bzero/bcopy calls not eliminated
+   { dg-do compile }
+   { dg-options "-O0 -Wall -fdump-tree-gimple" } */
+
+void f0 (void *p, const void *q, unsigned n)
+{
+  /* Bcopy corresponds to memmmove, not memcpy.  */
+  __builtin_bcopy (q, p, n);
+}
+
+void f1 (void *p, const void *q, unsigned n)
+{
+  /* This call should be eliminated.  */
+  __builtin_bcopy (q, p, 0);
+}
+
+int f2 (const void *p, const void *q, unsigned n)
+{
+  return __builtin_bcmp (q, p, n);
+}
+
+int f3 (const void *p, const void *q, unsigned n)
+{
+  /* This call should be folded into 0.  */
+  return __builtin_bcmp (q, p, 0);
+}
+
+void f4 (void *p, unsigned n)
+{
+  __builtin_bzero (p, n);
+}
+
+void f5 (void *p)
+{
+  /* This call should be eliminated.  */
+  __builtin_bzero (p, 0);
+}
+
+/* Verify that calls to bcmp, bcopy, and bzero have all been removed
+   and one of each replaced with memcmp, memmove, and memset, respectively.
+   The remaining three should be eliminated.
+  { dg-final { scan-tree-dump-not "bcmp|bcopy|bzero" "gimple" } }
+  { dg-final { scan-tree-dump-times "memcmp|memmove|memset" 3 "gimple" } } */
diff --git a/gcc/testsuite/gcc.dg/pr79214.c b/gcc/testsuite/gcc.dg/pr79214.c
index 79d2a25..6cf254fb 100644
--- a/gcc/testsuite/gcc.dg/pr79214.c
+++ b/gcc/testsuite/gcc.dg/pr79214.c
@@ -22,7 +22,7 @@ size_t range (void)
 
 void test_bzero (void)
 {
-  bzero (d, range ());   /* { dg-warning ".__builtin_bzero. writing 4 or more bytes into a region of size 3 overflows the destination" } */
+  bzero (d, range ());   /* { dg-warning ".__builtin_(bzero|memset). writing 4 or more bytes into a region of size 3 overflows the destination" } */
 }
 
 void test_memcpy (void)
diff --git a/gcc/testsuite/gcc.dg/tree-prof/val-prof-7.c b/gcc/testsuite/gcc.dg/tree-prof/val-prof-7.c
index 84ec9fb..5a4e777 100644
--- a/gcc/testsuite/gcc.dg/tree-prof/val-prof-7.c
+++ b/gcc/testsuite/gcc.dg/tree-prof/val-prof-7.c
@@ -4,14 +4,10 @@
 char *buffer1;
 char *buffer2;
 
+/* Bzero is not tested because it gets transformed into memset.  */
+
 #define DEFINE_TEST(N) \
 __attribute__((noinline)) \
-void bzero_test_ ## N (int len) \
-{ \
-  __builtin_bzero (buffer1, len); \
-} \
- \
-__attribute__((noinline)) \
 void memcpy_test_ ## N (int len) \
 { \
   __builtin_memcpy (buffer1, buffer2, len); \
@@ -31,7 +27,6 @@ void memset_test_ ## N (int len) \
  \
 void test_stringops_ ## N(int len) \
 { \
-  bzero_test_ ## N (len); \
   memcpy_test_## N (len); \
   mempcpy_test_ ## N (len); \
   memset_test_ ## N (len); \
@@ -64,10 +59,6 @@ int main() {
   return 0;
 }
 
-/* { dg-final-use-not-autofdo { scan-ipa-dump "Single value 8 stringop transformation on __builtin_bzero" "profile" } } */
-/* { dg-final-use-not-autofdo { scan-ipa-dump "Single value 55 stringop transformation on __builtin_bzero" "profile" } } */
-/* { dg-final-use-not-autofdo { scan-ipa-dump-times "Single value 32 stringop transformation on __builtin_bzero" 0 "profile" } } */
-
 /* { dg-final-use-not-autofdo { scan-ipa-dump "Single value 8 stringop transformation on __builtin_memcpy" "profile" } } */
 /* { dg-final-use-not-autofdo { scan-ipa-dump "Single value 55 stringop transformation on __builtin_memcpy" "profile" } } */
 /* { dg-final-use-not-autofdo { scan-ipa-dump-times "Single value 32 stringop transformation on __builtin_memcpy" 0 "profile" } } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/alias-36.c b/gcc/testsuite/gcc.dg/tree-ssa/alias-36.c
new file mode 100644
index 0000000..61b601a
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/alias-36.c
@@ -0,0 +1,28 @@
+/* PR tree-optimization/80934 - bzero should be assumed not to escape
+   pointer argument
+   { dg-do compile }
+   { dg-options "-O2 -fdump-tree-alias" } */
+
+void foobar (void);
+
+void f (void);
+
+void g (void)
+{
+  char d[32];
+  __builtin_memset (d, 0, sizeof d);
+  f ();
+  if (*d != 0)
+    foobar ();
+}
+
+void h (void)
+{
+  char d[32];
+  __builtin_bzero (d, sizeof d);
+  f ();
+  if (*d != 0)
+    foobar ();
+}
+
+/* { dg-final { scan-tree-dump-not "memset|foobar|bzero" "alias" } } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/ssa-dse-30.c b/gcc/testsuite/gcc.dg/tree-ssa/ssa-dse-30.c
new file mode 100644
index 0000000..ece8cb2
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/ssa-dse-30.c
@@ -0,0 +1,31 @@
+/* PR tree-optimization/80933 - redundant bzero/bcopy calls not eliminated
+   { dg-do compile }
+   { dg-options "-O2 -fdump-tree-dse1" } */
+
+void sink (void*);
+
+void test_bcopy (const void *s)
+{
+  char d[33];
+
+  /* Bcopy is transformed into memcpy and those calls are expanded
+     inline in EVRP, before DSE runs, so this test doesn't actually
+     verify that DSE does its job.  */
+  __builtin_bcopy (s, d, sizeof d);
+  __builtin_bcopy (s, d, sizeof d);
+
+  sink (d);
+}
+
+void test_bzero (void)
+{
+  char d[33];
+
+  __builtin_bzero (d, sizeof d);
+  __builtin_bzero (d, sizeof d);
+
+  sink (d);
+}
+
+/* { dg-final { scan-tree-dump-times "builtin_memset" 1 "dse1" } } */
+/* { dg-final { scan-tree-dump-not "builtin_(bcopy|bzero|memcpy)" "dse1" } } */

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH] handle bzero/bcopy in DSE and aliasing (PR 80933, 80934)
  2017-06-08 16:29                   ` Martin Sebor
@ 2017-06-09 12:35                     ` Richard Biener
  2017-06-09 13:04                       ` Jakub Jelinek
  2017-06-16 20:52                       ` Martin Sebor
  0 siblings, 2 replies; 18+ messages in thread
From: Richard Biener @ 2017-06-09 12:35 UTC (permalink / raw)
  To: Martin Sebor; +Cc: GCC Patches, Bernhard Reutner-Fischer

On Thu, Jun 8, 2017 at 6:29 PM, Martin Sebor <msebor@gmail.com> wrote:
> On 06/08/2017 01:51 AM, Richard Biener wrote:
>>
>> On Thu, Jun 8, 2017 at 4:33 AM, Martin Sebor <msebor@gmail.com> wrote:
>>>
>>> On 06/07/2017 02:12 PM, Martin Sebor wrote:
>>>>
>>>>
>>>> On 06/07/2017 02:01 PM, Marc Glisse wrote:
>>>>>
>>>>>
>>>>> On Wed, 7 Jun 2017, Bernhard Reutner-Fischer wrote:
>>>>>
>>>>>> On 7 June 2017 16:46:53 CEST, Martin Sebor <msebor@gmail.com> wrote:
>>>>>>>
>>>>>>>
>>>>>>> On 06/07/2017 02:23 AM, Richard Biener wrote:
>>>>>>>>
>>>>>>>>
>>>>>>>> On Wed, Jun 7, 2017 at 5:26 AM, Martin Sebor <msebor@gmail.com>
>>>>>>>
>>>>>>>
>>>>>>> wrote:
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> Note I'd be _much_ more sympathetic to simply canonicalizing all
>>>>>>>>>> of
>>>>>>>>>> bzero and bcopy
>>>>>>>>>> to memset / memmove and be done with all the above complexity.
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> Attached is an updated patch along these lines.  Please let me
>>>>>>>>> know if it matches your expectations.
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>> I think you attached the wrong patch.
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>> Yes I did, sorry.  The correct one is attached.
>>>>>>
>>>>>>
>>>>>>
>>>>>> Under POSIX.1-2008 "optimizing" bzero or bcmp is IMO plain wrong.
>>>>>>
>>>>>> It's like optimizing foo() to a random built-in but maybe that's just
>>>>>> me. If your libc provides a define to a standard function for these
>>>>>> under a compat knob then fine but otherwise you should fix that.
>>>>>> *shrug*. Joseph?
>>>>>
>>>>>
>>>>>
>>>>> The patch optimizes __builtin_bzero, which should be ok. The question
>>>>> (independent from this patch) is then under what conditions bzero
>>>>> should
>>>>> be detected as a builtin.
>>>>
>>>>
>>>>
>>>> Yes.  The problem is that unlike for C and C++, GCC doesn't have
>>>> a mechanism to select the target version of POSIX.  I think it
>>>> should.
>>>>
>>>> But there is a subtle problem with the patch that needs fixing.
>>>> Bcopy should not be transformed to memcpy but rather memmove.
>>>> I'll fix that before committing.
>>>
>>>
>>>
>>> Attached is an updated patch with this fix.  I also added a cast
>>> from bcopy and bzero to void to detect accidental uses of the
>>> return value.  Tested on x86_64-linux.
>>
>>
>> Please do not add foldings to builtins.c but instead add them to
>> gimple-fold.c.
>
>
> Sure.  Attached is an adjusted patch.
>
>>
>> +  /* Call memset and return the result cast to void to detect its use
>> +     (bzero returns void).  */
>> +  tree call = build_call_expr_loc (loc, fn, 3, dst, integer_zero_node,
>> len);
>> +  return fold_convert (void_type_node, call);
>>
>> ???  How can the result be used if the original call result was not?
>
>
> The cast ensured GCC would continue to warn on code like:
>
>   void f (void *d, unsigned n)
>   {
>     return bzero (d, n);
>   }
>
> Without the cast (as in the first patch) the above was silently
> accepted.
>
> This isn't necessary when the folding is done in gimple-fold.c.

+static bool
+gimple_fold_builtin_bcmp (gimple_stmt_iterator *gsi)
+{

+  /* Transform bcmp (a, b, len) into memcmp (a, b, len).  */
+
+  gimple *stmt = gsi_stmt (*gsi);
+  tree a = gimple_call_arg (stmt, 0);
+  tree b = gimple_call_arg (stmt, 1);
+  tree len = gimple_call_arg (stmt, 2);
+
+  gimple_seq seq = NULL;
+  gimple *repl = gimple_build_call (fn, 3, a, b, len);
+  gimple_seq_add_stmt_without_update (&seq, repl);
+  gsi_replace_with_seq_vops (gsi, seq);

given they have the same prototype you can do like gimple_fold_builtin_stpcpy:

  gimple_call_set_fndecl (stmt, fn);
  fold_stmt (gsi);

That works even with bcopy -> memmove if you swap arguments.

Ok with that changes.

Thanks,
Richard.

> Martin

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH] handle bzero/bcopy in DSE and aliasing (PR 80933, 80934)
  2017-06-09 12:35                     ` Richard Biener
@ 2017-06-09 13:04                       ` Jakub Jelinek
  2017-06-09 13:36                         ` Richard Biener
  2017-06-16 20:52                       ` Martin Sebor
  1 sibling, 1 reply; 18+ messages in thread
From: Jakub Jelinek @ 2017-06-09 13:04 UTC (permalink / raw)
  To: Richard Biener; +Cc: Martin Sebor, GCC Patches, Bernhard Reutner-Fischer

On Fri, Jun 09, 2017 at 02:35:45PM +0200, Richard Biener wrote:
> +static bool
> +gimple_fold_builtin_bcmp (gimple_stmt_iterator *gsi)
> +{
> 
> +  /* Transform bcmp (a, b, len) into memcmp (a, b, len).  */
> +
> +  gimple *stmt = gsi_stmt (*gsi);
> +  tree a = gimple_call_arg (stmt, 0);
> +  tree b = gimple_call_arg (stmt, 1);
> +  tree len = gimple_call_arg (stmt, 2);
> +
> +  gimple_seq seq = NULL;
> +  gimple *repl = gimple_build_call (fn, 3, a, b, len);
> +  gimple_seq_add_stmt_without_update (&seq, repl);
> +  gsi_replace_with_seq_vops (gsi, seq);
> 
> given they have the same prototype you can do like gimple_fold_builtin_stpcpy:
> 
>   gimple_call_set_fndecl (stmt, fn);
>   fold_stmt (gsi);
> 
> That works even with bcopy -> memmove if you swap arguments.

Shouldn't it also update gimple_call_fntype though, at least for memmove?
Those differ with void const * vs. void * arguments, and also in the return
value.  At least if the old fntype is compatible with the bcopy call.

	Jakub

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH] handle bzero/bcopy in DSE and aliasing (PR 80933, 80934)
  2017-06-09 13:04                       ` Jakub Jelinek
@ 2017-06-09 13:36                         ` Richard Biener
  0 siblings, 0 replies; 18+ messages in thread
From: Richard Biener @ 2017-06-09 13:36 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: Martin Sebor, GCC Patches, Bernhard Reutner-Fischer

On Fri, Jun 9, 2017 at 3:04 PM, Jakub Jelinek <jakub@redhat.com> wrote:
> On Fri, Jun 09, 2017 at 02:35:45PM +0200, Richard Biener wrote:
>> +static bool
>> +gimple_fold_builtin_bcmp (gimple_stmt_iterator *gsi)
>> +{
>>
>> +  /* Transform bcmp (a, b, len) into memcmp (a, b, len).  */
>> +
>> +  gimple *stmt = gsi_stmt (*gsi);
>> +  tree a = gimple_call_arg (stmt, 0);
>> +  tree b = gimple_call_arg (stmt, 1);
>> +  tree len = gimple_call_arg (stmt, 2);
>> +
>> +  gimple_seq seq = NULL;
>> +  gimple *repl = gimple_build_call (fn, 3, a, b, len);
>> +  gimple_seq_add_stmt_without_update (&seq, repl);
>> +  gsi_replace_with_seq_vops (gsi, seq);
>>
>> given they have the same prototype you can do like gimple_fold_builtin_stpcpy:
>>
>>   gimple_call_set_fndecl (stmt, fn);
>>   fold_stmt (gsi);
>>
>> That works even with bcopy -> memmove if you swap arguments.
>
> Shouldn't it also update gimple_call_fntype though, at least for memmove?
> Those differ with void const * vs. void * arguments, and also in the return
> value.  At least if the old fntype is compatible with the bcopy call.

Yes.  If the old fntype isn't compatible we don't reach the folding.

Richard.

>         Jakub

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH] handle bzero/bcopy in DSE and aliasing (PR 80933, 80934)
  2017-06-09 12:35                     ` Richard Biener
  2017-06-09 13:04                       ` Jakub Jelinek
@ 2017-06-16 20:52                       ` Martin Sebor
  1 sibling, 0 replies; 18+ messages in thread
From: Martin Sebor @ 2017-06-16 20:52 UTC (permalink / raw)
  To: Richard Biener; +Cc: GCC Patches, Bernhard Reutner-Fischer

> +static bool
> +gimple_fold_builtin_bcmp (gimple_stmt_iterator *gsi)
> +{
>
> +  /* Transform bcmp (a, b, len) into memcmp (a, b, len).  */
> +
> +  gimple *stmt = gsi_stmt (*gsi);
> +  tree a = gimple_call_arg (stmt, 0);
> +  tree b = gimple_call_arg (stmt, 1);
> +  tree len = gimple_call_arg (stmt, 2);
> +
> +  gimple_seq seq = NULL;
> +  gimple *repl = gimple_build_call (fn, 3, a, b, len);
> +  gimple_seq_add_stmt_without_update (&seq, repl);
> +  gsi_replace_with_seq_vops (gsi, seq);
>
> given they have the same prototype you can do like gimple_fold_builtin_stpcpy:
>
>   gimple_call_set_fndecl (stmt, fn);
>   fold_stmt (gsi);
>
> That works even with bcopy -> memmove if you swap arguments.
>
> Ok with that changes.

I made these changes along with those mentioned downthread and
committed r249278.

Martin

^ permalink raw reply	[flat|nested] 18+ messages in thread

end of thread, other threads:[~2017-06-16 20:52 UTC | newest]

Thread overview: 18+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-06-01 20:53 [PATCH] handle bzero/bcopy in DSE and aliasing (PR 80933, 80934) Martin Sebor
2017-06-02 11:12 ` Richard Biener
2017-06-04 15:36   ` Bernhard Reutner-Fischer
2017-06-06 16:54     ` Jeff Law
2017-06-07  3:26   ` Martin Sebor
2017-06-07  8:23     ` Richard Biener
2017-06-07 14:46       ` Martin Sebor
2017-06-07 18:37         ` Bernhard Reutner-Fischer
2017-06-07 20:01           ` Marc Glisse
2017-06-07 20:12             ` Martin Sebor
2017-06-08  2:33               ` Martin Sebor
2017-06-08  7:51                 ` Richard Biener
2017-06-08 16:29                   ` Martin Sebor
2017-06-09 12:35                     ` Richard Biener
2017-06-09 13:04                       ` Jakub Jelinek
2017-06-09 13:36                         ` Richard Biener
2017-06-16 20:52                       ` Martin Sebor
2017-06-07 20:23           ` Joseph Myers

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).