better handle MIN/MAX_EXPR of unrelated objects [PR102200]

Message ID 0e9718c5-3c42-4f32-da36-200272b77f09@gmail.com
State New
Headers
Series better handle MIN/MAX_EXPR of unrelated objects [PR102200] |

Commit Message

Martin Sebor Sept. 17, 2021, 12:28 a.m. UTC
  When computing the size of an object pointed to by the result of
a MIN/MAX_EXPR, the handle_min_max_size() function tries to deal
gracefully with operands that designate distinct objects.  But
the handling fails to consider an edge case when one of
the operands is a PHI one of whose operands references the same
MIN/MAX_EXPR.  This ultimately results in attempting to cache
as the result of the MIN/MAX_EXPR two different object references,
which triggers an ICE in the cache consistency checking.

The attached fix avoids the problem by instead caching the SSA_NAME
that's the result of the MIN/MAX_EXPR when its operands might
reference distinct objects, and by enhancing the infor_access()
function to handle this case.  Besides the absence if the ICE
the two additional tests verify that the right subobject of
the MIN/MAX_EXPR is used under the various combinations
of conditions.

Tested on x86_64-linux.

Martin
  

Comments

Jeff Law Sept. 17, 2021, 4:10 a.m. UTC | #1
On 9/16/2021 6:28 PM, Martin Sebor via Gcc-patches wrote:
> When computing the size of an object pointed to by the result of
> a MIN/MAX_EXPR, the handle_min_max_size() function tries to deal
> gracefully with operands that designate distinct objects.  But
> the handling fails to consider an edge case when one of
> the operands is a PHI one of whose operands references the same
> MIN/MAX_EXPR.  This ultimately results in attempting to cache
> as the result of the MIN/MAX_EXPR two different object references,
> which triggers an ICE in the cache consistency checking.
>
> The attached fix avoids the problem by instead caching the SSA_NAME
> that's the result of the MIN/MAX_EXPR when its operands might
> reference distinct objects, and by enhancing the infor_access()
> function to handle this case.  Besides the absence if the ICE
> the two additional tests verify that the right subobject of
> the MIN/MAX_EXPR is used under the various combinations
> of conditions.
>
> Tested on x86_64-linux.
>
> Martin
>
> gcc-102200.diff
>
> PR middle-end/102200 - ICE on a min of a decl and pointer in a loop
>
> gcc/ChangeLog:
>
> 	PR middle-end/102200
> 	* pointer-query.cc (access_ref::inform_access): Handle MIN/MAX_EXPR.
> 	(handle_min_max_size): Change argument.  Store original SSA_NAME for
> 	operands to potentially distinct (sub)objects.
> 	(compute_objsize_r): Adjust call to the above.
>
> gcc/testsuite/ChangeLog:
>
> 	PR middle-end/102200
> 	* gcc.dg/Wstringop-overflow-62.c: Adjust text of an expected note.
> 	* gcc.dg/Warray-bounds-89.c: New test.
> 	* gcc.dg/Wstringop-overflow-74.c: New test.
> 	* gcc.dg/Wstringop-overflow-75.c: New test.
> 	* gcc.dg/Wstringop-overflow-76.c: New test.
OK.  And just for the record, I was initially concerned that we might be 
focused too much on trying to issue an access diagnostic for invalid 
code.  But we could have pointers to different subjects or pointers to 
different elements within an array and the like.  So there's value for 
valid code as well.

jeff
  

Patch

PR middle-end/102200 - ICE on a min of a decl and pointer in a loop

gcc/ChangeLog:

	PR middle-end/102200
	* pointer-query.cc (access_ref::inform_access): Handle MIN/MAX_EXPR.
	(handle_min_max_size): Change argument.  Store original SSA_NAME for
	operands to potentially distinct (sub)objects.
	(compute_objsize_r): Adjust call to the above.

gcc/testsuite/ChangeLog:

	PR middle-end/102200
	* gcc.dg/Wstringop-overflow-62.c: Adjust text of an expected note.
	* gcc.dg/Warray-bounds-89.c: New test.
	* gcc.dg/Wstringop-overflow-74.c: New test.
	* gcc.dg/Wstringop-overflow-75.c: New test.
	* gcc.dg/Wstringop-overflow-76.c: New test.

diff --git a/gcc/pointer-query.cc b/gcc/pointer-query.cc
index 4ad28796e57..83b1f0fc866 100644
--- a/gcc/pointer-query.cc
+++ b/gcc/pointer-query.cc
@@ -1087,6 +1087,34 @@  access_ref::inform_access (access_mode mode) const
       else if (gimple_nop_p (stmt))
 	/* Handle DECL_PARM below.  */
 	ref = SSA_NAME_VAR (ref);
+      else if (is_gimple_assign (stmt)
+	       && (gimple_assign_rhs_code (stmt) == MIN_EXPR
+		   || gimple_assign_rhs_code (stmt) == MAX_EXPR))
+	{
+	  /* MIN or MAX_EXPR here implies a reference to a known object
+	     and either an unknown or distinct one (the latter being
+	     the result of an invalid relational expression).  Determine
+	     the identity of the former and point to it in the note.
+	     TODO: Consider merging with PHI handling.  */
+	  access_ref arg_ref[2];
+	  tree arg = gimple_assign_rhs1 (stmt);
+	  compute_objsize (arg, /* ostype = */ 1 , &arg_ref[0]);
+	  arg = gimple_assign_rhs2 (stmt);
+	  compute_objsize (arg, /* ostype = */ 1 , &arg_ref[1]);
+
+	  /* Use the argument that references a known object with more
+	     space remaining.  */
+	  const bool idx
+	    = (!arg_ref[0].ref || !arg_ref[0].base0
+	       || (arg_ref[0].base0 && arg_ref[1].base0
+		   && (arg_ref[0].size_remaining ()
+		       < arg_ref[1].size_remaining ())));
+
+	  arg_ref[idx].offrng[0] = offrng[0];
+	  arg_ref[idx].offrng[1] = offrng[1];
+	  arg_ref[idx].inform_access (mode);
+	  return;
+	}
     }
 
   if (DECL_P (ref))
@@ -1463,15 +1491,18 @@  pointer_query::dump (FILE *dump_file, bool contents /* = false */)
 }
 
 /* A helper of compute_objsize_r() to determine the size from an assignment
-   statement STMT with the RHS of either MIN_EXPR or MAX_EXPR.  */
+   statement STMT with the RHS of either MIN_EXPR or MAX_EXPR.  On success
+   set PREF->REF to the operand with more or less space remaining,
+   respectively, if both refer to the same (sub)object, or to PTR if they
+   might not, and return true.  Otherwise, if the identity of neither
+   operand can be determined, return false.  */
 
 static bool
-handle_min_max_size (gimple *stmt, int ostype, access_ref *pref,
+handle_min_max_size (tree ptr, int ostype, access_ref *pref,
 		     ssa_name_limit_t &snlim, pointer_query *qry)
 {
-  tree_code code = gimple_assign_rhs_code (stmt);
-
-  tree ptr = gimple_assign_rhs1 (stmt);
+  const gimple *stmt = SSA_NAME_DEF_STMT (ptr);
+  const tree_code code = gimple_assign_rhs_code (stmt);
 
   /* In a valid MAX_/MIN_EXPR both operands must refer to the same array.
      Determine the size/offset of each and use the one with more or less
@@ -1479,7 +1510,8 @@  handle_min_max_size (gimple *stmt, int ostype, access_ref *pref,
      determined from the other instead, adjusted up or down as appropriate
      for the expression.  */
   access_ref aref[2] = { *pref, *pref };
-  if (!compute_objsize_r (ptr, ostype, &aref[0], snlim, qry))
+  tree arg1 = gimple_assign_rhs1 (stmt);
+  if (!compute_objsize_r (arg1, ostype, &aref[0], snlim, qry))
     {
       aref[0].base0 = false;
       aref[0].offrng[0] = aref[0].offrng[1] = 0;
@@ -1487,8 +1519,8 @@  handle_min_max_size (gimple *stmt, int ostype, access_ref *pref,
       aref[0].set_max_size_range ();
     }
 
-  ptr = gimple_assign_rhs2 (stmt);
-  if (!compute_objsize_r (ptr, ostype, &aref[1], snlim, qry))
+  tree arg2 = gimple_assign_rhs2 (stmt);
+  if (!compute_objsize_r (arg2, ostype, &aref[1], snlim, qry))
     {
       aref[1].base0 = false;
       aref[1].offrng[0] = aref[1].offrng[1] = 0;
@@ -1517,6 +1549,13 @@  handle_min_max_size (gimple *stmt, int ostype, access_ref *pref,
 	    *pref = aref[i1];
 	  else
 	    *pref = aref[i0];
+
+	  if (aref[i0].ref != aref[i1].ref)
+	    /* If the operands don't refer to the same (sub)object set
+	       PREF->REF to the SSA_NAME from which STMT was obtained
+	       so that both can be identified in a diagnostic.  */
+	    pref->ref = ptr;
+
 	  return true;
 	}
 
@@ -1537,6 +1576,10 @@  handle_min_max_size (gimple *stmt, int ostype, access_ref *pref,
       pref->offrng[0] = aref[i0].offrng[0];
       pref->offrng[1] = aref[i0].offrng[1];
     }
+
+  /* Replace PTR->REF with the SSA_NAME to indicate the expression
+     might not refer to the same (sub)object.  */
+  pref->ref = ptr;
   return true;
 }
 
@@ -2009,8 +2052,9 @@  compute_objsize_r (tree ptr, int ostype, access_ref *pref,
 
       if (code == MAX_EXPR || code == MIN_EXPR)
 	{
-	  if (!handle_min_max_size (stmt, ostype, pref, snlim, qry))
+	  if (!handle_min_max_size (ptr, ostype, pref, snlim, qry))
 	    return false;
+
 	  qry->put_ref (ptr, *pref);
 	  return true;
 	}
diff --git a/gcc/testsuite/gcc.dg/Warray-bounds-89.c b/gcc/testsuite/gcc.dg/Warray-bounds-89.c
new file mode 100644
index 00000000000..2604f65e6d5
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Warray-bounds-89.c
@@ -0,0 +1,139 @@ 
+/* Verify warnings and notes for MIN_EXPRs involving either pointers
+   to distinct objects or one to a known object and the other to
+   an unknown one.  The relational expressions are strictly invalid
+   but that should be diagnosed by a separate warning.
+   { dg-do compile }
+   { dg-options "-O2 -Warray-bounds -Wno-stringop-overflow" } */
+
+/* Verify the note points to the larger of the two objects and mentions
+   the offset into it (alhough the offset would ideally be a part of
+   the warning).  */
+extern char a3[3];
+extern char a5[5];  // { dg-message "at offset 5 into object 'a5' of size 5" "note" }
+
+void min_a3_a5 (int i)
+{
+  char *p = a3 + i;
+  char *q = a5 + i;
+
+  /* The relational expression below is invalid and should be diagnosed
+     by its own warning independently of -Warray-bounds.  */
+  char *d = p < q ? p : q;
+
+  d[4] = 0;
+
+  /* Verify the type in the warning corresponds to the larger of the two
+     objects.  */
+  d[5] = 0;         // { dg-warning "subscript 5 is outside array bounds of 'char\\\[5]'" }
+}
+
+
+// Same as above but with the larger array as the first MIN_EXPR operand.
+extern char b4[4];
+extern char b6[6];  // { dg-message "at offset 6 into object 'b6' of size 6" "note" }
+
+void min_b6_b4 (int i)
+{
+  char *p = b6 + i;
+  char *q = b4 + i;
+  char *d = p < q ? p : q;
+
+  d[5] = 0;
+  d[6] = 0;         // { dg-warning "subscript 6 is outside array bounds of 'char\\\[6]'" }
+}
+
+
+/* Same as above but with the first MIN_EXPR operand pointing to an unknown
+   object.  */
+extern char c7[7];  // { dg-message "at offset 7 into object 'c7' of size 7" "note" }
+
+void min_p_c7 (char *p, int i)
+{
+  char *q = c7 + i;
+  char *d = p < q ? p : q;
+
+  d[6] = 0;
+  d[7] = 0;         // { dg-warning "subscript 7 is outside array bounds of 'char\\\[7]'" }
+}
+
+
+/* Same as above but with the second MIN_EXPR operand pointing to an unknown
+   object.  */
+extern char d8[8];  // { dg-message "at offset 8 into object 'd8' of size 8" "note" }
+
+void min_d8_p (char *q, int i)
+{
+  char *p = d8 + i;
+  char *d = p < q ? p : q;
+
+  d[7] = 0;
+  d[8] = 0;         // { dg-warning "subscript 8 is outside array bounds of 'char\\\[8]'" }
+}
+
+
+/* The following are diagnosed by -Wstringop-overflow but, as a result
+   of PR 101374, not by -Warray-bounds.  */
+
+struct A3_5
+{
+  char a3[3];
+  char a5[5];  // { dg-message "at offset 5 into object 'a5' of size 5" "note" { xfail *-*-* } }
+};
+
+void min_A3_A5 (int i, struct A3_5 *pa3_5)
+{
+  char *p = pa3_5->a3 + i;
+  char *q = pa3_5->a5 + i;
+
+  char *d = p < q ? p : q;
+
+  // d[4] = 0;
+  d[5] = 0;         // { dg-warning "subscript 5 is outside array bounds of 'char\\\[5]'" "pr??????" { xfail *-*-* } }
+}
+
+
+struct B4_B6
+{
+  char b4[4];
+  char b6[6];       // { dg-message "at offset 6 into object 'b6' of size 6" "note" { xfail *-*-* } }
+};
+
+void min_B6_B4 (int i, struct B4_B6 *pb4_b6)
+{
+  char *p = pb4_b6->b6 + i;
+  char *q = pb4_b6->b4 + i;
+  char *d = p < q ? p : q;
+
+  d[5] = 0;
+  d[6] = 0;         // { dg-warning "subscript 6 is outside array bounds of 'char\\\[6]'" "pr??????" { xfail *-*-* } }
+}
+
+
+struct C7
+{
+  char c7[7];       // { dg-message "at offset 7 into object 'c7' of size 7" "note" { xfail *-*-* } }
+};
+
+void min_p_C7 (char *p, int i, struct C7 *pc7)
+{
+  char *q = pc7->c7 + i;
+  char *d = p < q ? p : q;
+
+  d[6] = 0;
+  d[7] = 0;         // { dg-warning "subscript 7 is outside array bounds of 'char\\\[7]'" "pr??????" { xfail *-*-* } }
+}
+
+
+struct D8
+{
+  char d8[8];       // { dg-message "at offset 8 into object 'd8' of size 8" "note" { xfail *-*-* } }
+};
+
+void min_D8_p (char *q, int i, struct D8 *pd8)
+{
+  char *p = pd8->d8 + i;
+  char *d = p < q ? p : q;
+
+  d[7] = 0;
+  d[8] = 0;         // { dg-warning "subscript 8 is outside array bounds of 'char\\\[8]'" "pr??????" { xfail *-*-* } }
+}
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-62.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-62.c
index 318d9bd1f94..4b6d1ab83c5 100644
--- a/gcc/testsuite/gcc.dg/Wstringop-overflow-62.c
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-62.c
@@ -117,7 +117,7 @@  void test_min (void)
   {
     /* Exercise a pointer pointing to a known object plus constant offset
        with one pointing to an unknown object.  */
-    char a6[6];               // { dg-message ": destination object 'a6'" "note" }
+    char a6[6];               // { dg-message "(at offset 1 into )?destination object 'a6'" "note" }
     char *p1 = ptr;
     char *p2 = a6 + 1;
     char *q = MIN (p1, p2);
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-74.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-74.c
new file mode 100644
index 00000000000..bacec964d36
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-74.c
@@ -0,0 +1,22 @@ 
+/* PR middle-end/102200 - ICE on a min of a decl and pointer in a loop
+   { dg-do compile }
+   { dg-options "-O2 -Wall" } */
+
+typedef __SIZE_TYPE__ size_t;
+
+extern char a[], n;
+
+void f (void)
+{
+  char *p = a;
+  size_t end = 1;
+
+  while (n)
+    {
+      if (p < (char*)end)
+        *p = ';';
+
+      if (p > (char*)&end)
+        p = (char*)&end;
+    }
+}
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-75.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-75.c
new file mode 100644
index 00000000000..2ebadc649a5
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-75.c
@@ -0,0 +1,133 @@ 
+/* Verify warnings and notes for MIN_EXPRs involving either pointers
+   to distinct objects or one to a known object and the other to
+   an unknown one.  The relational expressions are strictly invalid
+   but that should be diagnosed by a separate warning.
+   { dg-do compile }
+   { dg-options "-O2 -Wno-array-bounds" } */
+
+/* Verify the note points to the larger of the two objects and mentions
+   the offset into it (alhough the offset would ideally be a part of
+   the warning).  */
+extern char a3[3];
+extern char a5[5];  // { dg-message "at offset 5 into destination object 'a5' of size 5" "note" }
+
+void min_a3_a5 (int i)
+{
+  char *p = a3 + i;
+  char *q = a5 + i;
+
+  /* The relational expression below is invalid and should be diagnosed
+     by its own warning independently of -Wstringop-overflow.  */
+  char *d = p < q ? p : q;
+
+  d[4] = 0;
+  d[5] = 0;         // { dg-warning "writing 1 byte into a region of size 0" }
+}
+
+
+// Same as above but with the larger array as the first MIN_EXPR operand.
+extern char b4[4];
+extern char b6[6];  // { dg-message "at offset 6 into destination object 'b6' of size 6" "note" }
+
+void min_b6_b4 (int i)
+{
+  char *p = b6 + i;
+  char *q = b4 + i;
+  char *d = p < q ? p : q;
+
+  d[5] = 0;
+  d[6] = 0;         // { dg-warning "writing 1 byte into a region of size 0" }
+}
+
+
+/* Same as above but with the first MIN_EXPR operand pointing to an unknown
+   object.  */
+extern char c7[7];  // { dg-message "at offset 7 into destination object 'c7' of size 7" "note" }
+
+void min_p_c7 (char *p, int i)
+{
+  char *q = c7 + i;
+  char *d = p < q ? p : q;
+
+  d[6] = 0;
+  d[7] = 0;         // { dg-warning "writing 1 byte into a region of size 0" }
+}
+
+
+/* Same as above but with the second MIN_EXPR operand pointing to an unknown
+   object.  */
+extern char d8[8];  // { dg-message "at offset 8 into destination object 'd8' of size 8" "note" }
+
+void min_d8_p (char *q, int i)
+{
+  char *p = d8 + i;
+  char *d = p < q ? p : q;
+
+  d[7] = 0;
+  d[8] = 0;         // { dg-warning "writing 1 byte into a region of size 0" }
+}
+
+
+struct A3_5
+{
+  char a3[3];
+  char a5[5];  // { dg-message "at offset 5 into destination object 'a5' of size 5" "note" }
+};
+
+void min_A3_A5 (int i, struct A3_5 *pa3_5)
+{
+  char *p = pa3_5->a3 + i;
+  char *q = pa3_5->a5 + i;
+
+  char *d = p < q ? p : q;
+
+  // d[4] = 0;
+  d[5] = 0;         // { dg-warning "writing 1 byte into a region of size 0" }
+}
+
+
+struct B4_B6
+{
+  char b4[4];
+  char b6[6];       // { dg-message "at offset 6 into destination object 'b6' of size 6" "note" }
+};
+
+void min_B6_B4 (int i, struct B4_B6 *pb4_b6)
+{
+  char *p = pb4_b6->b6 + i;
+  char *q = pb4_b6->b4 + i;
+  char *d = p < q ? p : q;
+
+  d[5] = 0;
+  d[6] = 0;         // { dg-warning "writing 1 byte into a region of size 0" }
+}
+
+
+struct C7
+{
+  char c7[7];       // { dg-message "at offset 7 into destination object 'c7' of size 7" "note" }
+};
+
+void min_p_C7 (char *p, int i, struct C7 *pc7)
+{
+  char *q = pc7->c7 + i;
+  char *d = p < q ? p : q;
+
+  d[6] = 0;
+  d[7] = 0;         // { dg-warning "writing 1 byte into a region of size 0" }
+}
+
+
+struct D8
+{
+  char d8[8];       // { dg-message "at offset 8 into destination object 'd8' of size 8" "note" }
+};
+
+void min_D8_p (char *q, int i, struct D8 *pd8)
+{
+  char *p = pd8->d8 + i;
+  char *d = p < q ? p : q;
+
+  d[7] = 0;
+  d[8] = 0;         // { dg-warning "writing 1 byte into a region of size 0" }
+}
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-76.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-76.c
new file mode 100644
index 00000000000..18191a1aa5e
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-76.c
@@ -0,0 +1,148 @@ 
+/* Verify warnings and notes for MAX_EXPRs involving either pointers
+   to distinct objects or one to a known object and the other to
+   an unknown one.  Unlike for the same object, for unrelated objects
+   the expected warnings and notes are the same as for MIN_EXPR: when
+   the order of the objects in the address space cannot be determined
+   the larger of them is assumed to be used.  (This is different for
+   distinct struct members where the order is given.)
+   The relational expressions are strictly invalid but that should be
+   diagnosed by a separate warning.
+   { dg-do compile }
+   { dg-options "-O2 -Wno-array-bounds" } */
+
+#define MAX(p, q) ((p) > (q) ? (p) : (q))
+
+/* Verify that even for MAX_EXPR and like for MIN_EXPR, the note points
+   to the larger of the two objects and mentions the offset into it
+   (although the offset might be better included in the warning).  */
+extern char a3[3];
+extern char a5[5];  // { dg-message "at offset 5 into destination object 'a5' of size 5" "note" }
+
+void max_a3_a5 (int i)
+{
+  char *p = a3 + i;
+  char *q = a5 + i;
+
+  /* The relational expression below is invalid and should be diagnosed
+     by its own warning independently of -Wstringop-overflow.  */
+  char *d = MAX (p, q);
+
+  d[2] = 0;
+  d[3] = 0;
+  d[4] = 0;
+  d[5] = 0;         // { dg-warning "writing 1 byte into a region of size 0" }
+}
+
+
+// Same as above but with the larger array as the first MAX_EXPR operand.
+extern char b4[4];
+extern char b6[6];  // { dg-message "at offset 6 into destination object 'b6' of size 6" "note" }
+
+void max_b6_b4 (int i)
+{
+  char *p = b6 + i;
+  char *q = b4 + i;
+  char *d = MAX (p, q);
+
+  d[3] = 0;
+  d[4] = 0;
+  d[5] = 0;
+  d[6] = 0;         // { dg-warning "writing 1 byte into a region of size 0" }
+}
+
+
+/* Same as above but with the first MAX_EXPR operand pointing to an unknown
+   object.  */
+extern char c7[7];  // { dg-message "at offset 7 into destination object 'c7' of size 7" "note" }
+
+void max_p_c7 (char *p, int i)
+{
+  char *q = c7 + i;
+  char *d = MAX (p, q);
+
+  d[6] = 0;
+  d[7] = 0;         // { dg-warning "writing 1 byte into a region of size 0" }
+}
+
+
+/* Same as above but with the second MIN_EXPR operand pointing to an unknown
+   object.  */
+extern char d8[8];  // { dg-message "at offset 8 into destination object 'd8' of size 8" "note" }
+
+void max_d8_p (char *q, int i)
+{
+  char *p = d8 + i;
+  char *d = MAX (p, q);
+
+  d[7] = 0;
+  d[8] = 0;         // { dg-warning "writing 1 byte into a region of size 0" }
+}
+
+
+struct A3_5
+{
+  char a3[3];  // { dg-message "at offset 3 into destination object 'a3' of size 3" "pr??????" { xfail *-*-* } }
+  char a5[5];  // { dg-message "at offset 5 into destination object 'a5' of size 5" "note" }
+};
+
+void max_A3_A5 (int i, struct A3_5 *pa3_5)
+{
+  char *p = pa3_5->a3 + i;
+  char *q = pa3_5->a5 + i;
+
+  char *d = MAX (p, q);
+
+  d[2] = 0;
+  d[3] = 0;         // { dg-warning "writing 1 byte into a region of size 0" "pr??????" { xfail *-*-* } }
+  d[4] = 0;
+  d[5] = 0;         // { dg-warning "writing 1 byte into a region of size 0" }
+}
+
+
+struct B4_B6
+{
+  char b4[4];
+  char b6[6];       // { dg-message "at offset 6 into destination object 'b6' of size 6" "note" }
+};
+
+void max_B6_B4 (int i, struct B4_B6 *pb4_b6)
+{
+  char *p = pb4_b6->b6 + i;
+  char *q = pb4_b6->b4 + i;
+  char *d = MAX (p, q);
+
+  d[3] = 0;
+  d[4] = 0;
+  d[5] = 0;
+  d[6] = 0;         // { dg-warning "writing 1 byte into a region of size 0" }
+}
+
+
+struct C7
+{
+  char c7[7];       // { dg-message "at offset 7 into destination object 'c7' of size 7" "note" }
+};
+
+void max_p_C7 (char *p, int i, struct C7 *pc7)
+{
+  char *q = pc7->c7 + i;
+  char *d = MAX (p, q);
+
+  d[6] = 0;
+  d[7] = 0;         // { dg-warning "writing 1 byte into a region of size 0" }
+}
+
+
+struct D8
+{
+  char d8[8];       // { dg-message "at offset 8 into destination object 'd8' of size 8" "note" }
+};
+
+void max_D8_p (char *q, int i, struct D8 *pd8)
+{
+  char *p = pd8->d8 + i;
+  char *d = MAX (p, q);
+
+  d[7] = 0;
+  d[8] = 0;         // { dg-warning "writing 1 byte into a region of size 0" }
+}