Use enclosing object size if it's smaller than member [PR 101475]

Message ID b75c8486-3073-b696-ee0c-681b8b88e8a8@gmail.com
State New
Headers
Series Use enclosing object size if it's smaller than member [PR 101475] |

Commit Message

Martin Sebor Dec. 16, 2021, 7:56 p.m. UTC
  Enabling vectorization at -O2 caused quite a few tests for
warnings to start failing in GCC 12.  These tests were xfailed
and bugs were opened to track the problems until they can be
fully analyzed and ultimately fixed before GCC 12 is released.

I've now started going through these and the first such bug
I tackled is PR 102944.  As it turns out, the xfails there
are all due to a known limitation tracked in PR 101475: when
determining the size of a destination for A COMPONENT_REF,
unless asked for the size of the complete object,
compute_objsize() only considers the size of the referenced
member, even when the member is larger than the object would
allow.  This prevents warnings from diagnosing unvectorized
past-the-end accesses to objects in backing buffers (such as
in character arrays or allocated chunks of memory).

Many (though not all) accesses that are vectorized are diagnosed
because there the COMPONENT_REF is replaced by a MEM_REF.  But
because vectorization depends on target-specific things like
alignment requirements, what is and isn't diagnosed also tends
to be target-specific, making these tests quite brittle..

The attached patch corrects this oversight by using the complete
object's size instead of the member when the former is smaller.
Besides improving the out-of-bounds access detection it also
makes the tests behave more consistently across targets.

Tested on x86_64-linux and by building Glibc and verifying
that the change triggers no new warnings.

Martin
  

Comments

Jeff Law Dec. 20, 2021, 7:29 p.m. UTC | #1
On 12/16/2021 12:56 PM, Martin Sebor via Gcc-patches wrote:
> Enabling vectorization at -O2 caused quite a few tests for
> warnings to start failing in GCC 12.  These tests were xfailed
> and bugs were opened to track the problems until they can be
> fully analyzed and ultimately fixed before GCC 12 is released.
>
> I've now started going through these and the first such bug
> I tackled is PR 102944.  As it turns out, the xfails there
> are all due to a known limitation tracked in PR 101475: when
> determining the size of a destination for A COMPONENT_REF,
> unless asked for the size of the complete object,
> compute_objsize() only considers the size of the referenced
> member, even when the member is larger than the object would
> allow.  This prevents warnings from diagnosing unvectorized
> past-the-end accesses to objects in backing buffers (such as
> in character arrays or allocated chunks of memory).
>
> Many (though not all) accesses that are vectorized are diagnosed
> because there the COMPONENT_REF is replaced by a MEM_REF.  But
> because vectorization depends on target-specific things like
> alignment requirements, what is and isn't diagnosed also tends
> to be target-specific, making these tests quite brittle..
>
> The attached patch corrects this oversight by using the complete
> object's size instead of the member when the former is smaller.
> Besides improving the out-of-bounds access detection it also
> makes the tests behave more consistently across targets.
>
> Tested on x86_64-linux and by building Glibc and verifying
> that the change triggers no new warnings.
I must be missing something here.  How can the enclosing object be 
smaller than a member?

Jeff
  
Martin Sebor Jan. 4, 2022, 5:28 p.m. UTC | #2
On 12/20/21 12:29 PM, Jeff Law wrote:
> 
> 
> On 12/16/2021 12:56 PM, Martin Sebor via Gcc-patches wrote:
>> Enabling vectorization at -O2 caused quite a few tests for
>> warnings to start failing in GCC 12.  These tests were xfailed
>> and bugs were opened to track the problems until they can be
>> fully analyzed and ultimately fixed before GCC 12 is released.
>>
>> I've now started going through these and the first such bug
>> I tackled is PR 102944.  As it turns out, the xfails there
>> are all due to a known limitation tracked in PR 101475: when
>> determining the size of a destination for A COMPONENT_REF,
>> unless asked for the size of the complete object,
>> compute_objsize() only considers the size of the referenced
>> member, even when the member is larger than the object would
>> allow.  This prevents warnings from diagnosing unvectorized
>> past-the-end accesses to objects in backing buffers (such as
>> in character arrays or allocated chunks of memory).
>>
>> Many (though not all) accesses that are vectorized are diagnosed
>> because there the COMPONENT_REF is replaced by a MEM_REF.  But
>> because vectorization depends on target-specific things like
>> alignment requirements, what is and isn't diagnosed also tends
>> to be target-specific, making these tests quite brittle..
>>
>> The attached patch corrects this oversight by using the complete
>> object's size instead of the member when the former is smaller.
>> Besides improving the out-of-bounds access detection it also
>> makes the tests behave more consistently across targets.
>>
>> Tested on x86_64-linux and by building Glibc and verifying
>> that the change triggers no new warnings.
> I must be missing something here.  How can the enclosing object be 
> smaller than a member?

When the enclosing object is backed by a buffer of insufficient
size.  The buffer might be a declared character array such as
in the the tests added and modified by the patch, or it might
be dynamically allocated.

Martin
  
Martin Sebor Jan. 10, 2022, 9:53 p.m. UTC | #3
Ping (CC'ing Jason as requested):
https://gcc.gnu.org/pipermail/gcc-patches/2021-December/587033.html

On 1/4/22 10:28, Martin Sebor wrote:
> On 12/20/21 12:29 PM, Jeff Law wrote:
>>
>>
>> On 12/16/2021 12:56 PM, Martin Sebor via Gcc-patches wrote:
>>> Enabling vectorization at -O2 caused quite a few tests for
>>> warnings to start failing in GCC 12.  These tests were xfailed
>>> and bugs were opened to track the problems until they can be
>>> fully analyzed and ultimately fixed before GCC 12 is released.
>>>
>>> I've now started going through these and the first such bug
>>> I tackled is PR 102944.  As it turns out, the xfails there
>>> are all due to a known limitation tracked in PR 101475: when
>>> determining the size of a destination for A COMPONENT_REF,
>>> unless asked for the size of the complete object,
>>> compute_objsize() only considers the size of the referenced
>>> member, even when the member is larger than the object would
>>> allow.  This prevents warnings from diagnosing unvectorized
>>> past-the-end accesses to objects in backing buffers (such as
>>> in character arrays or allocated chunks of memory).
>>>
>>> Many (though not all) accesses that are vectorized are diagnosed
>>> because there the COMPONENT_REF is replaced by a MEM_REF.  But
>>> because vectorization depends on target-specific things like
>>> alignment requirements, what is and isn't diagnosed also tends
>>> to be target-specific, making these tests quite brittle..
>>>
>>> The attached patch corrects this oversight by using the complete
>>> object's size instead of the member when the former is smaller.
>>> Besides improving the out-of-bounds access detection it also
>>> makes the tests behave more consistently across targets.
>>>
>>> Tested on x86_64-linux and by building Glibc and verifying
>>> that the change triggers no new warnings.
>> I must be missing something here.  How can the enclosing object be 
>> smaller than a member?
> 
> When the enclosing object is backed by a buffer of insufficient
> size.  The buffer might be a declared character array such as
> in the the tests added and modified by the patch, or it might
> be dynamically allocated.
> 
> Martin
  
Jason Merrill Jan. 11, 2022, 9:43 p.m. UTC | #4
On 12/16/21 14:56, Martin Sebor via Gcc-patches wrote:
> Enabling vectorization at -O2 caused quite a few tests for
> warnings to start failing in GCC 12.  These tests were xfailed
> and bugs were opened to track the problems until they can be
> fully analyzed and ultimately fixed before GCC 12 is released.
> 
> I've now started going through these and the first such bug
> I tackled is PR 102944.  As it turns out, the xfails there
> are all due to a known limitation tracked in PR 101475: when
> determining the size of a destination for A COMPONENT_REF,
> unless asked for the size of the complete object,
> compute_objsize() only considers the size of the referenced
> member, even when the member is larger than the object would
> allow.  This prevents warnings from diagnosing unvectorized
> past-the-end accesses to objects in backing buffers (such as
> in character arrays or allocated chunks of memory).
> 
> Many (though not all) accesses that are vectorized are diagnosed
> because there the COMPONENT_REF is replaced by a MEM_REF.  But
> because vectorization depends on target-specific things like
> alignment requirements, what is and isn't diagnosed also tends
> to be target-specific, making these tests quite brittle..
> 
> The attached patch corrects this oversight by using the complete
> object's size instead of the member when the former is smaller.
> Besides improving the out-of-bounds access detection it also
> makes the tests behave more consistently across targets.
> 
> Tested on x86_64-linux and by building Glibc and verifying
> that the change triggers no new warnings.

> +  /* Unconditionally determine the size of the base object (it could
> +     be smaller than the referenced member).  */

This comment could use a mention of the backing buffer case.  OK with 
that change.

Jason
  

Patch

Use enclosing object size if it's smaller than member [PR 101475].

Resolves:
PR middle-end/101475 - missing -Wstringop-overflow storing a compound literal

gcc/ChangeLog:

	PR middle-end/101475
	* pointer-query.cc (handle_component_ref): Use the size of
	the enclosing object if it's smaller than the member.

gcc/testsuite/ChangeLog:

	PR middle-end/101475
	* gcc.dg/Wstringop-overflow-68.c: Adjust, remove xfails.
	* gcc.dg/Wstringop-overflow-88.c: New test.

diff --git a/gcc/pointer-query.cc b/gcc/pointer-query.cc
index 4bedf7fca47..644b4de9bcb 100644
--- a/gcc/pointer-query.cc
+++ b/gcc/pointer-query.cc
@@ -1914,36 +1914,40 @@  handle_component_ref (tree cref, gimple *stmt, bool addr, int ostype,
   gcc_assert (TREE_CODE (cref) == COMPONENT_REF);
 
   const tree base = TREE_OPERAND (cref, 0);
+  const tree field = TREE_OPERAND (cref, 1);
+  access_ref base_ref = *pref;
+
+  /* Unconditionally determine the size of the base object (it could
+     be smaller than the referenced member).  */
+  if (!compute_objsize_r (base, stmt, addr, 0, &base_ref, snlim, qry))
+    return false;
+
+  /* Add the offset of the member to the offset into the object computed
+     so far.  */
+  tree offset = byte_position (field);
+  if (TREE_CODE (offset) == INTEGER_CST)
+    base_ref.add_offset (wi::to_offset (offset));
+  else
+    base_ref.add_max_offset ();
+
+  if (!base_ref.ref)
+    /* PREF->REF may have been already set to an SSA_NAME earlier
+       to provide better context for diagnostics.  In that case,
+       leave it unchanged.  */
+    base_ref.ref = base;
+
   const tree base_type = TREE_TYPE (base);
   if (TREE_CODE (base_type) == UNION_TYPE)
     /* In accesses through union types consider the entire unions
        rather than just their members.  */
     ostype = 0;
 
-  tree field = TREE_OPERAND (cref, 1);
-
   if (ostype == 0)
     {
       /* In OSTYPE zero (for raw memory functions like memcpy), use
 	 the maximum size instead if the identity of the enclosing
 	 object cannot be determined.  */
-      if (!compute_objsize_r (base, stmt, addr, ostype, pref, snlim, qry))
-	return false;
-
-      /* Otherwise, use the size of the enclosing object and add
-	 the offset of the member to the offset computed so far.  */
-      tree offset = byte_position (field);
-      if (TREE_CODE (offset) == INTEGER_CST)
-	pref->add_offset (wi::to_offset (offset));
-      else
-	pref->add_max_offset ();
-
-      if (!pref->ref)
-	/* PREF->REF may have been already set to an SSA_NAME earlier
-	   to provide better context for diagnostics.  In that case,
-	   leave it unchanged.  */
-	pref->ref = base;
-
+      *pref = base_ref;
       return true;
     }
 
@@ -1958,6 +1962,11 @@  handle_component_ref (tree cref, gimple *stmt, bool addr, int ostype,
     }
 
   set_component_ref_size (cref, pref);
+
+  if (base_ref.size_remaining () < pref->size_remaining ())
+    /* Use the base object if it's smaller than the member.  */
+    *pref = base_ref;
+
   return true;
 }
 
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-68.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-68.c
index 05ea56fca67..4d132394f0f 100644
--- a/gcc/testsuite/gcc.dg/Wstringop-overflow-68.c
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-68.c
@@ -2,7 +2,7 @@ 
    a larger scalar into a smaller array
    Verify overflow by aggregate stores.
    { dg-do compile }
-   { dg-options "-O2" } */
+   { dg-options "-O2 -fno-tree-vectorize" } */
 
 #define A(N) (A ## N)
 #define Ac1 (AC1){ 0 }
@@ -57,19 +57,20 @@  void warn_comp_lit_zero (void)
 
 void warn_comp_lit (void)
 {
-  *(AC2*)a1 = Ac2;      // { dg-warning "writing 2 bytes into a region of size 1" "pr101475" { xfail *-*-* } }
-  // After vectorization, below codes are optimized to
-  // MEM <vector(4) char> [(char *)&a2] = { 0, 1, 2, 3 };
-  // MEM <vector(4) char> [(char *)&a3] = { 0, 1, 2, 3 };
-  // MEM <vector(8) char> [(char *)&a4] = { 0, 1, 2, 3, 4, 5, 6, 7 };
-  // MEM <vector(8) char> [(char *)&a7] = { 0, 1, 2, 3, 4, 5, 6, 7 };
-  // MEM <vector(16) char> [(char *)&a15] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
-  // and warning should be expected, refer to PR102722.
-  *(AC4*)a2 = Ac4;      // { dg-warning "writing 4 bytes into a region of size 2" "pr101475" { xfail { ! { vect_slp_v4qi_store_unalign_1 } } } }
-  *(AC4*)a3 = Ac4;      // { dg-warning "writing 4 bytes into a region of size 3" "pr101475" { xfail { ! { vect_slp_v4qi_store_unalign_1 } } } }
-  *(AC8*)a4 = Ac8;      // { dg-warning "writing 8 bytes into a region of size 4" "pr101475" { xfail { ! { vect_slp_v8qi_store_unalign_1 } } } }
-  *(AC8*)a7 = Ac8;      // { dg-warning "writing 8 bytes into a region of size 7" "pr101475" { xfail { ! { vect_slp_v8qi_store_unalign_1 } } } }
-  *(AC16*)a15 = Ac16;   // { dg-warning "writing 16 bytes into a region of size 15" "pr101475" { xfail { ! { vect_slp_v16qi_store_unalign_1 } } } }
+  /* Ideally only one warning would be issued for each of the stores
+     mentioning the size of the rest of the source being assigned to
+     the destination that doesn't fit.  But without vectorization
+     the assignment is a series of one-character stores, except in
+     the first instance multiple warnings end up being issued for
+     each assignment, each saying "writing 1 byte into a region of
+     size 0".  That's suboptimal and should be improved.  See also
+     PR 92110.  */
+  *(AC2*)a1 = Ac2;      // { dg-warning "writing (2 bytes|1 byte) into a region of size (1|0)" "pr101475" }
+  *(AC4*)a2 = Ac4;      // { dg-warning "writing (4 bytes|1 byte) into a region of size (2|0)" "pr101475" }
+  *(AC4*)a3 = Ac4;      // { dg-warning "writing (4 bytes|1 byte) into a region of size (3|0)" "pr101475" }
+  *(AC8*)a4 = Ac8;      // { dg-warning "writing (8 bytes|1 byte) into a region of size (4|0)" "pr101475" }
+  *(AC8*)a7 = Ac8;      // { dg-warning "writing (8 bytes|1 byte) into a region of size (7|0)" "pr101475" }
+  *(AC16*)a15 = Ac16;   // { dg-warning "writing (16 bytes|1 byte) into a region of size (15|0)" "pr101475" }
 }
 
 void warn_aggr_decl (void)
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-88.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-88.c
new file mode 100644
index 00000000000..c6b443e4d3d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-88.c
@@ -0,0 +1,327 @@ 
+/* PR middle-end/101475 - missing -Wstringop-overflow storing a compound
+   literal
+   { dg-do compile }
+   { dg-options "-O2 -fno-tree-vectorize" } */
+
+extern char ea1[1], ea2[2], ea3[3], ea4[4];
+
+/* The trailing A member of all of Sx, S0, and S1 is treated the same:
+   as a flexible array member.  */
+struct Sx { char n, a[]; };
+struct S0 { char n, a[0]; };
+struct S1 { char n, a[1]; };
+/* The trailing A member in both S2 and S3 is treated as an ordinary
+   array with exactly two elements and accesses to elements beyond
+   the last are diagnosed regardless of whether they are within
+   the bounds the enclosing object.  */
+struct S2 { char n, a[2]; };
+struct S3 { char n, a[3]; };
+
+
+void fx_ea1 (void)
+{
+  struct Sx *p = (struct Sx*)ea1;
+  p->n = 0;
+  p->a[0] = 0;      // { dg-warning "-Wstringop-overflow" }
+  p->a[1] = 1;      // { dg-warning "-Wstringop-overflow" }
+  p->a[2] = 2;      // { dg-warning "-Wstringop-overflow" }
+  p->a[3] = 3;      // { dg-warning "-Wstringop-overflow" }
+}
+
+void f0_ea1 (void)
+{
+  struct S0 *p = (struct S0*)ea1;
+  p->n = 0;
+  p->a[0] = 0;      // { dg-warning "-Wstringop-overflow" }
+  p->a[1] = 1;      // { dg-warning "-Wstringop-overflow" }
+  p->a[2] = 2;      // { dg-warning "-Wstringop-overflow" }
+  p->a[3] = 3;      // { dg-warning "-Wstringop-overflow" }
+}
+
+void f1_ea1 (void)
+{
+  struct S1 *p = (struct S1*)ea1;
+  p->n = 0;
+  p->a[0] = 0;      // { dg-warning "-Wstringop-overflow" }
+  p->a[1] = 1;      // { dg-warning "-Wstringop-overflow" }
+  p->a[2] = 2;      // { dg-warning "-Wstringop-overflow" }
+  p->a[3] = 3;      // { dg-warning "-Wstringop-overflow" }
+}
+
+void f2_ea1 (void)
+{
+  struct S2 *p = (struct S2*)ea1;
+  p->n = 0;
+  p->a[0] = 0;      // { dg-warning "-Wstringop-overflow" }
+  p->a[1] = 1;      // { dg-warning "-Wstringop-overflow" }
+  p->a[2] = 2;      // { dg-warning "-Wstringop-overflow" }
+  p->a[3] = 3;      // { dg-warning "-Wstringop-overflow" }
+}
+
+void f3_ea1 (void)
+{
+  struct S3 *p = (struct S3*)ea1;
+  p->n = 0;
+  p->a[0] = 0;      // { dg-warning "-Wstringop-overflow" }
+  p->a[1] = 1;      // { dg-warning "-Wstringop-overflow" }
+  p->a[2] = 2;      // { dg-warning "-Wstringop-overflow" }
+  p->a[3] = 3;      // { dg-warning "-Wstringop-overflow" }
+}
+
+
+void fx_ea1_p1 (void)
+{
+  struct Sx *p = (struct Sx*)(ea1 + 1);
+  p->n = 0;         // { dg-warning "-Wstringop-overflow" }
+  p->a[0] = 0;      // { dg-warning "-Wstringop-overflow" }
+  p->a[1] = 1;      // { dg-warning "-Wstringop-overflow" }
+  p->a[2] = 2;      // { dg-warning "-Wstringop-overflow" }
+  p->a[3] = 3;      // { dg-warning "-Wstringop-overflow" }
+}
+
+void f0_ea1_p1 (void)
+{
+  struct S0 *p = (struct S0*)(ea1 + 1);
+  p->n = 0;         // { dg-warning "-Wstringop-overflow" }
+  p->a[0] = 0;      // { dg-warning "-Wstringop-overflow" }
+  p->a[1] = 1;      // { dg-warning "-Wstringop-overflow" }
+  p->a[2] = 2;      // { dg-warning "-Wstringop-overflow" }
+  p->a[3] = 3;      // { dg-warning "-Wstringop-overflow" }
+}
+
+void f1_ea1_p1 (void)
+{
+  struct S1 *p = (struct S1*)(ea1 + 1);
+  p->n = 0;         // { dg-warning "-Wstringop-overflow" }
+  p->a[0] = 0;      // { dg-warning "-Wstringop-overflow" }
+  p->a[1] = 1;      // { dg-warning "-Wstringop-overflow" }
+  p->a[2] = 2;      // { dg-warning "-Wstringop-overflow" }
+  p->a[3] = 3;      // { dg-warning "-Wstringop-overflow" }
+}
+
+void f2_ea1_p1 (void)
+{
+  struct S2 *p = (struct S2*)(ea1 + 1);
+  p->n = 0;         // { dg-warning "-Wstringop-overflow" }
+  p->a[0] = 0;      // { dg-warning "-Wstringop-overflow" }
+  p->a[1] = 1;      // { dg-warning "-Wstringop-overflow" }
+  p->a[2] = 2;      // { dg-warning "-Wstringop-overflow" }
+  p->a[3] = 3;      // { dg-warning "-Wstringop-overflow" }
+}
+
+void f3_ea1_p1 (void)
+{
+  struct S3 *p = (struct S3*)(ea1 + 1);
+  p->n = 0;         // { dg-warning "-Wstringop-overflow" }
+  p->a[0] = 0;      // { dg-warning "-Wstringop-overflow" }
+  p->a[1] = 1;      // { dg-warning "-Wstringop-overflow" }
+  p->a[2] = 2;      // { dg-warning "-Wstringop-overflow" }
+  p->a[3] = 3;      // { dg-warning "-Wstringop-overflow" }
+}
+
+
+void fx_ea2 (void)
+{
+  struct Sx *p = (struct Sx*)ea2;
+  p->n = 0;
+  p->a[0] = 0;
+  p->a[1] = 1;      // { dg-warning "-Wstringop-overflow" }
+  p->a[2] = 2;      // { dg-warning "-Wstringop-overflow" }
+  p->a[3] = 3;      // { dg-warning "-Wstringop-overflow" }
+}
+
+void f0_ea2 (void)
+{
+  struct S0 *p = (struct S0*)ea2;
+  p->n = 0;
+  p->a[0] = 0;
+  p->a[1] = 1;      // { dg-warning "-Wstringop-overflow" }
+  p->a[2] = 2;      // { dg-warning "-Wstringop-overflow" }
+  p->a[3] = 3;      // { dg-warning "-Wstringop-overflow" }
+}
+
+void f1_ea2 (void)
+{
+  struct S1 *p = (struct S1*)ea2;
+  p->n = 0;
+  p->a[0] = 0;
+  p->a[1] = 1;      // { dg-warning "-Wstringop-overflow" }
+  p->a[2] = 2;      // { dg-warning "-Wstringop-overflow" }
+  p->a[3] = 3;      // { dg-warning "-Wstringop-overflow" }
+}
+
+void f2_ea2 (void)
+{
+  struct S2 *p = (struct S2*)ea2;
+  p->n = 0;
+  p->a[0] = 0;
+  p->a[1] = 1;      // { dg-warning "-Wstringop-overflow" }
+  p->a[2] = 2;      // { dg-warning "-Wstringop-overflow" }
+  p->a[3] = 3;      // { dg-warning "-Wstringop-overflow" }
+}
+
+void f3_ea2 (void)
+{
+  struct S3 *p = (struct S3*)ea2;
+  p->n = 0;
+  p->a[0] = 0;
+  p->a[1] = 1;      // { dg-warning "-Wstringop-overflow" }
+  p->a[2] = 2;      // { dg-warning "-Wstringop-overflow" }
+  p->a[3] = 3;      // { dg-warning "-Wstringop-overflow" }
+}
+
+
+void fx_ea2_p1 (void)
+{
+  struct Sx *p = (struct Sx*)(ea2 + 1);
+  p->n = 0;
+  p->a[0] = 0;      // { dg-warning "-Wstringop-overflow" } 
+  p->a[1] = 1;      // { dg-warning "-Wstringop-overflow" }
+  p->a[2] = 2;      // { dg-warning "-Wstringop-overflow" }
+  p->a[3] = 3;      // { dg-warning "-Wstringop-overflow" }
+}
+
+void f0_ea2_p1 (void)
+{
+  struct S0 *p = (struct S0*)(ea2 + 1);
+  p->n = 0;
+  p->a[0] = 0;      // { dg-warning "-Wstringop-overflow" }
+  p->a[1] = 1;      // { dg-warning "-Wstringop-overflow" }
+  p->a[2] = 2;      // { dg-warning "-Wstringop-overflow" }
+  p->a[3] = 3;      // { dg-warning "-Wstringop-overflow" }
+}
+
+void f1_ea2_p1 (void)
+{
+  struct S1 *p = (struct S1*)(ea2 + 1);
+  p->n = 0;
+  p->a[0] = 0;      // { dg-warning "-Wstringop-overflow" }
+  p->a[1] = 1;      // { dg-warning "-Wstringop-overflow" }
+  p->a[2] = 2;      // { dg-warning "-Wstringop-overflow" }
+  p->a[3] = 3;      // { dg-warning "-Wstringop-overflow" }
+}
+
+void f2_ea2_p1 (void)
+{
+  struct S2 *p = (struct S2*)(ea2 + 1);
+  p->n = 0;
+  p->a[0] = 0;      // { dg-warning "-Wstringop-overflow" }
+  p->a[1] = 1;      // { dg-warning "-Wstringop-overflow" }
+  p->a[2] = 2;      // { dg-warning "-Wstringop-overflow" }
+  p->a[3] = 3;      // { dg-warning "-Wstringop-overflow" }
+}
+
+void f3_ea2_p1 (void)
+{
+  struct S3 *p = (struct S3*)(ea2 + 1);
+  p->n = 0;
+  p->a[0] = 0;      // { dg-warning "-Wstringop-overflow" }
+  p->a[1] = 1;      // { dg-warning "-Wstringop-overflow" }
+  p->a[2] = 2;      // { dg-warning "-Wstringop-overflow" }
+  p->a[3] = 3;      // { dg-warning "-Wstringop-overflow" }
+}
+
+
+void fx_ea3 (void)
+{
+  struct Sx *p = (struct Sx*)ea3;
+  p->n = 0;
+  p->a[0] = 0;
+  p->a[1] = 1;
+  p->a[2] = 2;      // { dg-warning "-Wstringop-overflow" }
+  p->a[3] = 3;      // { dg-warning "-Wstringop-overflow" }
+}
+
+void f0_ea3 (void)
+{
+  struct S0 *p = (struct S0*)ea3;
+  p->n = 0;
+  p->a[0] = 0;
+  p->a[1] = 1;
+  p->a[2] = 2;      // { dg-warning "-Wstringop-overflow" }
+  p->a[3] = 3;      // { dg-warning "-Wstringop-overflow" }
+}
+
+void f1_ea3 (void)
+{
+  struct S1 *p = (struct S1*)ea3;
+  p->n = 0;
+  p->a[0] = 0;
+  p->a[1] = 1;
+  p->a[2] = 2;      // { dg-warning "-Wstringop-overflow" }
+  p->a[3] = 3;      // { dg-warning "-Wstringop-overflow" }
+}
+
+void f2_ea3 (void)
+{
+  struct S2 *p = (struct S2*)ea3;
+  p->n = 0;
+  p->a[0] = 0;
+  p->a[1] = 1;
+  p->a[2] = 2;      // { dg-warning "-Wstringop-overflow" }
+  p->a[3] = 3;      // { dg-warning "-Wstringop-overflow" }
+}
+
+void f3_ea3 (void)
+{
+  struct S3 *p = (struct S3*)ea3;
+  p->n = 0;
+  p->a[0] = 0;
+  p->a[1] = 1;
+  p->a[2] = 2;      // { dg-warning "-Wstringop-overflow" }
+  p->a[3] = 3;      // { dg-warning "-Wstringop-overflow" }
+}
+
+
+void fx_ea4 (void)
+{
+  struct Sx *p = (struct Sx*)ea4;
+  p->n = 0;
+  p->a[0] = 0;
+  p->a[1] = 1;
+  p->a[2] = 2;
+  p->a[3] = 3;      // { dg-warning "-Wstringop-overflow" }
+}
+
+void f0_ea4 (void)
+{
+  struct S0 *p = (struct S0*)ea4;
+  p->n = 0;
+  p->a[0] = 0;
+  p->a[1] = 1;
+  p->a[2] = 2;
+  p->a[3] = 3;      // { dg-warning "-Wstringop-overflow" }
+}
+
+void f1_ea4 (void)
+{
+  struct S1 *p = (struct S1*)ea4;
+  p->n = 0;
+  p->a[0] = 0;
+  p->a[1] = 1;
+  p->a[2] = 2;
+  p->a[3] = 3;      // { dg-warning "-Wstringop-overflow" }
+}
+
+void f2_ea4 (void)
+{
+  struct S2 *p = (struct S2*)ea4;
+  p->n = 0;
+  p->a[0] = 0;
+  p->a[1] = 1;
+  /* Even though the offset of p->a[2] is within the bounds of EA4
+     the warning triggers because it only considers trailing arrays
+     of at mnost one element as "poor man's flexible arrays."  */
+  p->a[2] = 2;      // { dg-warning "-Wstringop-overflow" }
+  p->a[3] = 3;      // { dg-warning "-Wstringop-overflow" }
+}
+
+void f3_ea4 (void)
+{
+  struct S3 *p = (struct S3*)ea4;
+  p->n = 0;
+  p->a[0] = 0;
+  p->a[1] = 1;
+  p->a[2] = 2;
+  p->a[3] = 3;      // { dg-warning "-Wstringop-overflow" }
+}