[v3] detect out-of-bounds stores by atomic functions [PR102453]

Message ID f3f708fe-7c91-7ac6-84d8-f9818e537352@gmail.com
State New
Headers
Series [v3] detect out-of-bounds stores by atomic functions [PR102453] |

Commit Message

Martin Sebor Oct. 24, 2021, 11:40 p.m. UTC
  Attached is a revised patch for just the access warning pass
to diagnose out-of-bounds stores by atomic functions, with
no attr-fnspec changes.

Is this okay for trunk?

Martin

PS Just to clarify the effect of the original patch in case
it wasn't: it didn't enable optimizations of atomic built-ins.
It just made it possible, by first calling the new
atomic_builtin_fnspec() to get the fnspec, and then by actually
doing something with it.  The original patch did not modify
builtin_fnspec() to call the new atomic_builtin_fnspec().  But
since you seem to have reservations about exposing the attribute
in any form I have withdrawn the original patch and replaced it
with the more limited one.

On 10/13/21 2:15 AM, Richard Biener wrote:
> On Tue, Oct 12, 2021 at 9:44 PM Martin Sebor <msebor@gmail.com> wrote:
>>
>> On 10/12/21 12:52 AM, Richard Biener wrote:
>>> On Mon, Oct 11, 2021 at 11:25 PM Martin Sebor <msebor@gmail.com> wrote:
>>>>
>>>> The attached change extends GCC's warnings for out-of-bounds
>>>> stores to cover atomic (and __sync) built-ins.
>>>>
>>>> Rather than hardcoding the properties of these built-ins just
>>>> for the sake of the out-of-bounds detection, on the assumption
>>>> that it might be useful for future optimizations as well, I took
>>>> the approach of extending class attr_fnspec to express their
>>>> special property that they encode the size of the access in their
>>>> name.
>>>>
>>>> I also took the liberty of making attr_fnspec assignable (something
>>>> the rest of my patch relies on), and updating some comments for
>>>> the characters the class uses to encode function properties, based
>>>> on my understanding of their purpose.
>>>>
>>>> Tested on x86_64-linux.
>>>
>>> Hmm, so you place 'A' at an odd place (where the return value is specified),
>>> but you do not actually specify the behavior on the return value.  Shoudln't
>>>
>>> +     'A'        specifies that the function atomically accesses a constant
>>> +               1 << N bytes where N is indicated by character 3+2i
>>>
>>> maybe read
>>>
>>>       'A'     specifies that the function returns the memory pointed to
>>> by argument
>>>                one of size 1 << N bytes where N is indicated by
>>> character 3 +2i accessed atomically
>>>
>>> ?
>>
>> I didn't think the return value would be interesting because in
>> general (parallel accesses) it's not related (in an observable
>> way) to the value of the dereferenced operand.  Not all
>> the built-ins also return a value (e.g., atomic_store), and
>> whether or not one does return the argument would need to be
>> encoded somehow because it cannot be determined from the return
>> type (__atomic_compare_exchange and __atomic_test_and_set return
>> bool that's not necessarily the value of the operand).  Also,
>> since the functions return the operand value either before or
>> after the update, we'd need another letter to describe that.
>> (This alone could be dealt with simply by using 'A' and 'a',
>> but that's not enough for the other cases.)
>>
>> So with all these possibilities I don't think encoding
>> the return value at this point is worthwhile.  If/when this
>> enhancement turns out to be used for optimization and we think
>> encoding the return value would be helpful, I'd say let's
>> revisit it then.  The accessor APIs should make it a fairly
>> straightforward exercise.
> 
> I though it would be useful for points-to analysis since knowing how
> the return value is composed improves the points-to result for it.
> 
> Note that IPA mod-ref now synthesizes fn-spec and might make use
> of 'A' if it were not narrowly defined.  Sure it's probably difficult to
> fully specify the RMW cycle that's eventually done but since we
> have a way to specify a non-constant size of accesses as passed
> by a parameter it would be nice to allow specifying a constant size
> anyhow.  It just occured to me we could use "fake" parameters to
> encode those, so for
> 
> void foo (int *);
> 
> use like ". R2c4" saying that parameter 1 is read with the size
> specified by (non-existing) parameter 2 which is specified as
> 'c'onstant 1 << 4.
> 
> Alternatively a constant size specification could use alternate
> encoding 'a' to 'f'.  That said, if 'A' is not suppose to specify
> the return value it shouldn't be in the return value specification...
> 
>>> I also wonder if it's necessary to constrain this to 'atomic' accesses
>>> for the purpose of the patch and whether that detail could be omitted to
>>> eventually make more use of it?
>>
>> I pondered the same question but I couldn't think of any other
>> built-ins with similar semantics (read-write-modify, return
>> a result either pre- or post-modification), so I opted for
>> simplicity.  I am open to generalizing it if/when there is
>> a function I could test it with, although I'm not sure
>> the current encoding scheme has enough letters and letter
>> positions to describe the effects in their full generality.
>>
>>>
>>> Likewise
>>>
>>> +     '0'...'9'  specifies the size of value written/read is given either
>>> +               by the specified argument, or for atomic functions, by
>>> +               2 ^ N where N is the constant value denoted by the character
>>>
>>> should mention (excluding '0') for the argument position.
>>
>> Sure, I'll update the comment if you think this change is worth
>> pursuing.
>>
>>>
>>>      /* length of the fn spec string.  */
>>> -  const unsigned len;
>>> +  unsigned len;
>>>
>>> why that?
>>
>> The const member is what prevents the struct from being assignable,
>> which is what the rest of the patch depends on.
>>
>>>
>>> +  /* Return true of the function is an __atomic or __sync built-in.  */
>>>
>>> you didn't specify that for 'A' ...
>>>
>>> +  bool
>>> +  atomic_p () const
>>> +  {
>>> +    return str[0] == 'A';
>>> +  }
>>>
>>> +attr_fnspec
>>> +atomic_builtin_fnspec (tree callee)
>>> +{
>>> +  switch (DECL_FUNCTION_CODE (callee))
>>> +    {
>>> +#define BUILTIN_ACCESS_SIZE_FNSPEC(N, lgsz)            \
>>> +      BUILT_IN_ATOMIC_LOAD_ ## N:                      \
>>> +       return "Ap" "R" lgsz;
>>>
>>> note that doing this for atomics makes those no longer a compiler barrier
>>> for (aliased) loads and stores which means they are no longer a reliable
>>> way to implement locks.  That's a reason why I never pushed a
>>> PTA/alias patch I have to open-code this.
>>>
>>> Thus, do we really want to do this?
>>
>> That's my question to you :) If you don't think this attr_fnspec
>> extension would be useful for optimization I'll drop this part
>> of the patch and open-code it separately only for the out-of-bounds
>> diagnostics.  I'd started out that way but the fnspec class made
>> the code cleaner and if it could be used elsewhere so much
>> the better.  Please let me know.
> 
> Well - as said, we're relying on the property of a function call being
> a barrier for global/escaped memory for things like pthread_mutex_lock
> and at least some of the atomic builtins might be used as locking primitives
> and most definitely that would break.  For example
> 
> int cnt;
> int lock;
> 
> void foo(int n)
> {
>     for (int i = 0; i < n; ++i)
>        {
>           int val = 0;
>           int ret;
>           if (__atomic_compare_exchange (&lock, &val, 1, false, __ATOMIC_ACQ))
>             {
>                cnt++;
>             }
>        }
> }
> 
> will see PRE applied to the load of 'cnt' with your patch(?), that is, the lock
> is not a barrier for protected loads and stores.  Of course you have to provide
> some incentive to perform an invalid optimization and the trivial one like
> initialization before the locked area that can be CSEd is probably invalid
> since the init then has data races.
> 
> So yes, I think we should be very careful here.  The reason why I chickened
> out myself doing the obvious improvement for the atomic builtins...
> 
> Any opinions from others?
> 
> Richard.
> 
>>
>> Martin
  

Comments

Jeff Law Oct. 25, 2021, 2:21 a.m. UTC | #1
On 10/24/2021 5:40 PM, Martin Sebor via Gcc-patches wrote:
> Attached is a revised patch for just the access warning pass
> to diagnose out-of-bounds stores by atomic functions, with
> no attr-fnspec changes.
>
> Is this okay for trunk?
>
> Martin
>
> PS Just to clarify the effect of the original patch in case
> it wasn't: it didn't enable optimizations of atomic built-ins.
> It just made it possible, by first calling the new
> atomic_builtin_fnspec() to get the fnspec, and then by actually
> doing something with it.  The original patch did not modify
> builtin_fnspec() to call the new atomic_builtin_fnspec().  But
> since you seem to have reservations about exposing the attribute
> in any form I have withdrawn the original patch and replaced it
> with the more limited one.
>
>
I'm letting Richi take the lead here.  But I did notice a tiny nit:


> +
> +  /* Tyhe size in bytes of the access by the function, and the number
s/Tyhe/The/
  
Richard Biener Oct. 26, 2021, 8:23 a.m. UTC | #2
On Mon, Oct 25, 2021 at 4:21 AM Jeff Law <jeffreyalaw@gmail.com> wrote:
>
>
>
> On 10/24/2021 5:40 PM, Martin Sebor via Gcc-patches wrote:
> > Attached is a revised patch for just the access warning pass
> > to diagnose out-of-bounds stores by atomic functions, with
> > no attr-fnspec changes.
> >
> > Is this okay for trunk?
> >
> > Martin
> >
> > PS Just to clarify the effect of the original patch in case
> > it wasn't: it didn't enable optimizations of atomic built-ins.
> > It just made it possible, by first calling the new
> > atomic_builtin_fnspec() to get the fnspec, and then by actually
> > doing something with it.  The original patch did not modify
> > builtin_fnspec() to call the new atomic_builtin_fnspec().  But
> > since you seem to have reservations about exposing the attribute
> > in any form I have withdrawn the original patch and replaced it
> > with the more limited one.
> >
> >
> I'm letting Richi take the lead here.  But I did notice a tiny nit:
>
>
> > +
> > +  /* Tyhe size in bytes of the access by the function, and the number
> s/Tyhe/The/

The patch looks OK to me.

Thanks,
Richard.

>
>
  

Patch

Detect overflow by atomic functions [PR102453].

Resolves:
PR middle-end/102453 - buffer overflow by atomic built-ins not diagnosed

gcc/ChangeLog:

	PR middle-end/102453
	* gimple-ssa-warn-access.cc (pass_waccess::check_atomic_builtin): New.
	(pass_waccess::check_atomic_builtin): Call it.

gcc/testsuite/ChangeLog:

	PR middle-end/102453
	* gcc.dg/Warray-bounds-90.c: New test.
	* gcc.dg/Wstringop-overflow-77.c: New test.
	* gcc.dg/Wstringop-overflow-78.c: New test.
	* gcc.dg/Wstringop-overflow-79.c: New test.
	* gcc.dg/Wstringop-overflow-80.c: New test.
	* c-c++-common/gomp/atomic-4.c: Avoid an out-of-bounds access.

diff --git a/gcc/gimple-ssa-warn-access.cc b/gcc/gimple-ssa-warn-access.cc
index 00c3ea0f505..e5555a4f6ec 100644
--- a/gcc/gimple-ssa-warn-access.cc
+++ b/gcc/gimple-ssa-warn-access.cc
@@ -2109,6 +2109,9 @@  private:
   pass_waccess (pass_waccess &) = delete;
   void operator= (pass_waccess &) = delete;
 
+  /* Check a call to an atomic built-in function.  */
+  bool check_atomic_builtin (gcall *);
+
   /* Check a call to a built-in function.  */
   bool check_builtin (gcall *);
 
@@ -2681,6 +2684,87 @@  pass_waccess::check_memop_access (gimple *stmt, tree dest, tree src, tree size)
 		srcsize, dstsize, data.mode, &data);
 }
 
+/* Check a call STMT to an atomic or sync built-in.  */
+
+bool
+pass_waccess::check_atomic_builtin (gcall *stmt)
+{
+  tree callee = gimple_call_fndecl (stmt);
+  if (!callee)
+    return false;
+
+  /* Tyhe size in bytes of the access by the function, and the number
+     of the second argument to check (if any).  */
+  unsigned bytes = 0, arg2 = UINT_MAX;
+
+  switch (DECL_FUNCTION_CODE (callee))
+    {
+#define BUILTIN_ACCESS_SIZE_FNSPEC(N)			\
+      BUILT_IN_ATOMIC_LOAD_ ## N:			\
+    case BUILT_IN_SYNC_FETCH_AND_ADD_ ## N:		\
+    case BUILT_IN_SYNC_FETCH_AND_SUB_ ## N:		\
+    case BUILT_IN_SYNC_FETCH_AND_OR_ ## N:		\
+    case BUILT_IN_SYNC_FETCH_AND_AND_ ## N:		\
+    case BUILT_IN_SYNC_FETCH_AND_XOR_ ## N:		\
+    case BUILT_IN_SYNC_FETCH_AND_NAND_ ## N:		\
+    case BUILT_IN_SYNC_ADD_AND_FETCH_ ## N:		\
+    case BUILT_IN_SYNC_SUB_AND_FETCH_ ## N:		\
+    case BUILT_IN_SYNC_OR_AND_FETCH_ ## N:		\
+    case BUILT_IN_SYNC_AND_AND_FETCH_ ## N:		\
+    case BUILT_IN_SYNC_XOR_AND_FETCH_ ## N:		\
+    case BUILT_IN_SYNC_NAND_AND_FETCH_ ## N:		\
+    case BUILT_IN_SYNC_LOCK_TEST_AND_SET_ ## N:		\
+    case BUILT_IN_SYNC_BOOL_COMPARE_AND_SWAP_ ## N:	\
+    case BUILT_IN_SYNC_VAL_COMPARE_AND_SWAP_ ## N:	\
+    case BUILT_IN_SYNC_LOCK_RELEASE_ ## N:		\
+    case BUILT_IN_ATOMIC_EXCHANGE_ ## N:		\
+    case BUILT_IN_ATOMIC_STORE_ ## N:			\
+    case BUILT_IN_ATOMIC_ADD_FETCH_ ## N:		\
+    case BUILT_IN_ATOMIC_SUB_FETCH_ ## N:		\
+    case BUILT_IN_ATOMIC_AND_FETCH_ ## N:		\
+    case BUILT_IN_ATOMIC_NAND_FETCH_ ## N:		\
+    case BUILT_IN_ATOMIC_XOR_FETCH_ ## N:		\
+    case BUILT_IN_ATOMIC_OR_FETCH_ ## N:		\
+    case BUILT_IN_ATOMIC_FETCH_ADD_ ## N:		\
+    case BUILT_IN_ATOMIC_FETCH_SUB_ ## N:		\
+    case BUILT_IN_ATOMIC_FETCH_AND_ ## N:		\
+    case BUILT_IN_ATOMIC_FETCH_NAND_ ## N:		\
+    case BUILT_IN_ATOMIC_FETCH_OR_ ## N:		\
+    case BUILT_IN_ATOMIC_FETCH_XOR_ ## N:		\
+	bytes = N;					\
+	break;						\
+    case BUILT_IN_ATOMIC_COMPARE_EXCHANGE_ ## N:	\
+	bytes = N;					\
+	arg2 = 1
+
+    case BUILTIN_ACCESS_SIZE_FNSPEC (1);
+      break;
+    case BUILTIN_ACCESS_SIZE_FNSPEC (2);
+      break;
+    case BUILTIN_ACCESS_SIZE_FNSPEC (4);
+      break;
+    case BUILTIN_ACCESS_SIZE_FNSPEC (8);
+      break;
+    case BUILTIN_ACCESS_SIZE_FNSPEC (16);
+      break;
+
+    default:
+      return false;
+    }
+
+  tree size = build_int_cstu (sizetype, bytes);
+  tree dst = gimple_call_arg (stmt, 0);
+  check_memop_access (stmt, dst, NULL_TREE, size);
+
+  if (arg2 != UINT_MAX)
+    {
+      tree dst = gimple_call_arg (stmt, arg2);
+      check_memop_access (stmt, dst, NULL_TREE, size);
+    }
+
+  return true;
+}
+
 /* Check call STMT to a built-in function for invalid accesses.  Return
    true if a call has been handled.  */
 
@@ -2795,10 +2879,11 @@  pass_waccess::check_builtin (gcall *stmt)
       }
 	
     default:
-      return false;
+      if (check_atomic_builtin (stmt))
+	return true;
+      break;
     }
-
-  return true;
+  return false;
 }
 
 /* Returns the type of the argument ARGNO to function with type FNTYPE
diff --git a/gcc/testsuite/c-c++-common/gomp/atomic-4.c b/gcc/testsuite/c-c++-common/gomp/atomic-4.c
index 7f27370d535..5dd18d1d5fa 100644
--- a/gcc/testsuite/c-c++-common/gomp/atomic-4.c
+++ b/gcc/testsuite/c-c++-common/gomp/atomic-4.c
@@ -8,7 +8,7 @@  int *bar(void);
 void f1(void)
 {
   #pragma omp atomic
-    a[4] += 1;
+    a[3] += 1;
   #pragma omp atomic
     *p += 1;
   #pragma omp atomic
diff --git a/gcc/testsuite/gcc.dg/Warray-bounds-90.c b/gcc/testsuite/gcc.dg/Warray-bounds-90.c
new file mode 100644
index 00000000000..2e72a3daa1c
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Warray-bounds-90.c
@@ -0,0 +1,147 @@ 
+/* PR middle-end/102453 - buffer overflow by atomic built-ins not diagnosed
+   Verify that out-of-bounds accesses by atomic functions are diagnosed.
+   { dg-do compile }
+   { dg-options "-O2 -Wall -ftrack-macro-expansion=0" }  */
+
+#ifndef __cplusplus
+#  define bool _Bool
+#endif
+
+#define load        __atomic_load
+#define store       __atomic_store
+#define add_fetch   __atomic_add_fetch
+#define sub_fetch   __atomic_sub_fetch
+#define and_fetch   __atomic_and_fetch
+#define or_fetch    __atomic_or_fetch
+#define xor_fetch   __atomic_xor_fetch
+#define nand_fetch  __atomic_nand_fetch
+
+typedef __SIZE_TYPE__ size_t;
+
+void sink (void*, ...);
+#define sink(...) sink (0, __VA_ARGS__)
+
+extern _Bool eb;
+extern char ec;
+extern short int esi;
+extern int ei;
+extern long int eli;
+extern long long int elli;
+
+extern const _Bool ecb;
+extern const char ecc;
+extern const short int ecsi;
+extern const int eci;
+extern const long int ecli;
+extern const long long int eclli;
+
+extern _Atomic _Bool eab;
+extern _Atomic char eac;
+extern _Atomic short int easi;
+extern _Atomic int eai;
+extern _Atomic long int eali;
+extern _Atomic long long int ealli;
+
+extern _Atomic const _Bool eacb;
+extern _Atomic const char eacc;
+extern _Atomic const short int eacsi;
+extern _Atomic const int eaci;
+extern _Atomic const long int eacli;
+extern _Atomic const long long int eaclli;
+
+
+void nowarn_atomic_load (void)
+{
+  load (&eacb, &eb, 0);
+  load (&eacc, &ec, 0);
+  load (&eacsi, &esi, 0);
+  load (&eaci, &ei, 0);
+  load (&eacli, &eli, 0);
+  load (&eaclli, &elli, 0);
+}
+
+
+void warn_atomic_load_note (void)
+{
+  int i;                            // { dg-message "'i'" }
+
+  int *pi = (int*)((char*)&i + 1);
+  load (&eaci, pi, 0);              // { dg-warning "-Warray-bounds" }
+  sink (&i);
+
+  pi = (int*)((char*)&i + 2);
+  load (&eaci, pi, 0);              // { dg-warning "-Warray-bounds" }
+  sink (&i);
+
+  pi = &i + 1;
+  load (&eaci, pi, 0);              // { dg-warning "-Warray-bounds" }
+  sink (&i);
+}
+
+
+void warn_atomic_load (void)
+{
+  bool *pb = &eb + 1;
+  load (&eacb, pb, 0);              // { dg-warning "-Warray-bounds" }
+
+  char *pc = &ec + 1;
+  load (&eacc, pc, 0);              // { dg-warning "-Warray-bounds" }
+
+  short *psi = (short*)((char*)&esi + 1);
+  load (&eacsi, psi, 0);            // { dg-warning "-Warray-bounds" }
+  psi = (short*)((char*)&esi + 2);
+  load (&eacsi, psi, 0);            // { dg-warning "-Warray-bounds" }
+
+  int *pi = (int*)((char*)&ei + 1);
+  load (&eaci, pi, 0);              // { dg-warning "-Warray-bounds" }
+  pi = (int*)((char*)&ei + 2);
+  load (&eaci, pi, 0);              // { dg-warning "-Warray-bounds" }
+  pi = (int*)((char*)&ei + sizeof ei);
+  load (&eaci, pi, 0);              // { dg-warning "-Warray-bounds" }
+
+  long *pli = (long*)((char*)&eli + 1);
+  load (&eacli, pli, 0);            // { dg-warning "-Warray-bounds" }
+  pli = (long*)((char*)&eli + 1);
+  load (&eacli, pli, 0);            // { dg-warning "-Warray-bounds" }
+  pli = &eli + 1;
+  load (&eacli, pli, 0);            // { dg-warning "-Warray-bounds" }
+
+  long long *plli = (long long*)((char*)&elli + 1);
+  load (&eaclli, plli, 0);          // { dg-warning "-Warray-bounds" }
+  plli = (long long*)((char*)&elli + 1);
+  load (&eacli, plli, 0);           // { dg-warning "-Warray-bounds" }
+  plli = &elli + 1;
+  load (&eaclli, plli, 0);          // { dg-warning "-Warray-bounds" }
+}
+
+
+void warn_atomic_store (void)
+{
+  const bool *pb = &eb + 1;
+  store (&eab, pb, 0);              // { dg-warning "-Warray-bounds" }
+
+  const char *pc = &ec + 1;
+  store (&eac, pc, 0);              // { dg-warning "-Warray-bounds" }
+
+  const short *psi = (const short*)((const char*)&ecsi + 1);
+  store (&easi, psi, 0);            // { dg-warning "-Warray-bounds" }
+  psi = (const short*)((const char*)&esi + 2);
+  store (&easi, psi, 0);            // { dg-warning "-Warray-bounds" }
+
+  const int *pi = (const int*)((const char*)&eci + 1);
+  store (&eai, pi, 0);              // { dg-warning "-Warray-bounds" }
+  pi = (const int*)((const char*)&ei + 2);
+  store (&eai, pi, 0);              // { dg-warning "-Warray-bounds" }
+  pi = (const int*)((const char*)&ei + sizeof ei);
+  store (&eai, pi, 0);              // { dg-warning "-Warray-bounds" }
+
+  const long *pli = (const long*)((const char*)&eli + 1);
+  store (&eali, pli, 0);            // { dg-warning "-Warray-bounds" }
+  pli = (const long*)((const char*)&eli + sizeof (eli));
+  store (&eali, pli, 0);            // { dg-warning "-Warray-bounds" }
+
+  const long long *plli = (const long long*)((const char*)&elli + 1);
+  store (&ealli, plli, 0);          // { dg-warning "-Warray-bounds" }
+  plli = (const long long*)((const char*)&elli + sizeof elli);
+  store (&ealli, plli, 0);          // { dg-warning "-Warray-bounds" }
+}
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-77.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-77.c
new file mode 100644
index 00000000000..732f56849ae
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-77.c
@@ -0,0 +1,516 @@ 
+/* PR middle-end/102453 - buffer overflow by atomic built-ins not diagnosed
+   Verify that out-of-bounds accesses by atomic functions are diagnosed with
+   optimization disabled.
+   { dg-do compile }
+   { dg-options "-O0 -Wall -ftrack-macro-expansion=0" }  */
+
+#ifndef __cplusplus
+#  define bool _Bool
+#endif
+
+#define add_fetch(p, q)    __atomic_add_fetch (p, q, 0)
+#define sub_fetch(p, q)    __atomic_sub_fetch (p, q, 0)
+#define and_fetch(p, q)    __atomic_and_fetch (p, q, 0)
+#define or_fetch(p, q)     __atomic_or_fetch (p, q, 0)
+#define xor_fetch(p, q)    __atomic_xor_fetch (p, q, 0)
+#define nand_fetch(p, q)   __atomic_nand_fetch (p, q, 0)
+#define exchange(p, q, r)  __atomic_exchange (p, q, r, 0)
+#define exchange_n(p, n)   __atomic_exchange_n (p, n, 0)
+#define cmpxchg(p, q, r)   __atomic_compare_exchange (p, q, r, 0, 0, 0)
+
+typedef __SIZE_TYPE__ size_t;
+
+void sink (void*, ...);
+#define sink(...) sink (0, __VA_ARGS__)
+
+extern _Bool eb;
+extern char ec;
+extern short int esi;
+extern int ei;
+extern long int eli;
+extern long long int elli;
+
+extern const _Bool ecb;
+extern const char ecc;
+extern const short int ecsi;
+extern const int eci;
+extern const long int ecli;
+extern const long long int eclli;
+
+extern _Atomic _Bool eab;
+extern _Atomic char eac;
+extern _Atomic short int easi;
+extern _Atomic int eai;
+extern _Atomic long int eali;
+extern _Atomic long long int ealli;
+
+extern _Atomic const _Bool eacb;
+extern _Atomic const char eacc;
+extern _Atomic const short int eacsi;
+extern _Atomic const int eaci;
+extern _Atomic const long int eacli;
+extern _Atomic const long long int eaclli;
+
+
+void nowarn_atomic_add_fetch (void)
+{
+  add_fetch (&eac, ecc);
+  add_fetch (&easi, esi);
+  add_fetch (&eai, ei);
+  add_fetch (&eali, eli);
+  add_fetch (&ealli, elli);
+}
+
+
+void warn_atomic_add_fetch (void)
+{
+  _Atomic char *pc = &eac + 1;
+  add_fetch (pc, ecc);                  // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic short *psi = (_Atomic short*)((char*)&easi + 1);
+  add_fetch (psi, esi);                 // { dg-warning "-Wstringop-overflow" }
+  psi = (_Atomic short*)((char*)&easi + 2);
+  add_fetch (psi, esi);                 // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic int *pi = (_Atomic int*)((char*)&eai + 1);
+  add_fetch (pi, ei);                   // { dg-warning "-Wstringop-overflow" }
+  pi = (_Atomic int*)((char*)&eai + 2);
+  add_fetch (pi, ei);                   // { dg-warning "-Wstringop-overflow" }
+  pi = (_Atomic int*)((char*)&eai + sizeof eai);
+  add_fetch (pi, ei);                   // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic long *pli = (_Atomic long*)((char*)&eali + 1);
+  add_fetch (pli, eli);                 // { dg-warning "-Wstringop-overflow" }
+  pli = (_Atomic long*)((char*)&eali + 1);
+  add_fetch (pli, eli);                 // { dg-warning "-Wstringop-overflow" }
+  pli = &eali + 1;
+  add_fetch (pli, eli);                 // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic long long *plli = (_Atomic long long*)((char*)&ealli + 1);
+  add_fetch (plli, elli);               // { dg-warning "-Wstringop-overflow" }
+  plli = (_Atomic long long*)((char*)&ealli + 1);
+  add_fetch (plli, eali);               // { dg-warning "-Wstringop-overflow" }
+  plli = &ealli + 1;
+  add_fetch (plli, elli);               // { dg-warning "-Wstringop-overflow" }
+}
+
+
+void nowarn_atomic_sub_fetch (void)
+{
+  _Atomic char *pc = &eac;
+  sub_fetch (pc, ecc);
+
+  _Atomic short *psi = &easi;
+  sub_fetch (psi, esi);
+
+  _Atomic int *pi = &eai;
+  sub_fetch (pi, ei);
+
+  _Atomic long *pli = &eali;
+  sub_fetch (pli, eli);
+
+  _Atomic long long *plli = &ealli;
+  sub_fetch (plli, elli);
+}
+
+
+void warn_atomic_sub_fetch (void)
+{
+  _Atomic char *pc = &eac + 1;
+  sub_fetch (pc, ecc);                  // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic short *psi = (_Atomic short*)((char*)&easi + 1);
+  sub_fetch (psi, esi);                 // { dg-warning "-Wstringop-overflow" }
+  psi = (_Atomic short*)((char*)&easi + 2);
+  sub_fetch (psi, esi);                 // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic int *pi = (_Atomic int*)((char*)&eai + 1);
+  sub_fetch (pi, ei);                   // { dg-warning "-Wstringop-overflow" }
+  pi = (_Atomic int*)((char*)&eai + 2);
+  sub_fetch (pi, ei);                   // { dg-warning "-Wstringop-overflow" }
+  pi = (_Atomic int*)((char*)&eai + sizeof eai);
+  sub_fetch (pi, ei);                   // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic long *pli = (_Atomic long*)((char*)&eali + 1);
+  sub_fetch (pli, eli);                 // { dg-warning "-Wstringop-overflow" }
+  pli = (_Atomic long*)((char*)&eali + 1);
+  sub_fetch (pli, eli);                 // { dg-warning "-Wstringop-overflow" }
+  pli = &eali + 1;
+  sub_fetch (pli, eli);                 // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic long long *plli = (_Atomic long long*)((char*)&ealli + 1);
+  sub_fetch (plli, elli);               // { dg-warning "-Wstringop-overflow" }
+  plli = (_Atomic long long*)((char*)&ealli + 1);
+  sub_fetch (plli, eali);               // { dg-warning "-Wstringop-overflow" }
+  plli = &ealli + 1;
+  sub_fetch (plli, elli);               // { dg-warning "-Wstringop-overflow" }
+}
+
+
+void nowarn_atomic_and_fetch (void)
+{
+  _Atomic char *pc = &eac;
+  and_fetch (pc, ecc);
+
+  _Atomic short *psi = &easi;
+  and_fetch (psi, esi);
+
+  _Atomic int *pi = &eai;
+  and_fetch (pi, ei);
+
+  _Atomic long *pli = &eali;
+  and_fetch (pli, eli);
+
+  _Atomic long long *plli = &ealli;
+  and_fetch (plli, elli);
+}
+
+
+void warn_atomic_and_fetch (void)
+{
+  _Atomic char *pc = &eac + 1;
+  and_fetch (pc, ecc);                  // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic short *psi = (_Atomic short*)((char*)&easi + 1);
+  and_fetch (psi, esi);                 // { dg-warning "-Wstringop-overflow" }
+  psi = (_Atomic short*)((char*)&easi + 2);
+  and_fetch (psi, esi);                 // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic int *pi = (_Atomic int*)((char*)&eai + 1);
+  and_fetch (pi, ei);                   // { dg-warning "-Wstringop-overflow" }
+  pi = (_Atomic int*)((char*)&eai + 2);
+  and_fetch (pi, ei);                   // { dg-warning "-Wstringop-overflow" }
+  pi = (_Atomic int*)((char*)&eai + sizeof eai);
+  and_fetch (pi, ei);                   // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic long *pli = (_Atomic long*)((char*)&eali + 1);
+  and_fetch (pli, eli);                 // { dg-warning "-Wstringop-overflow" }
+  pli = (_Atomic long*)((char*)&eali + 1);
+  and_fetch (pli, eli);                 // { dg-warning "-Wstringop-overflow" }
+  pli = &eali + 1;
+  and_fetch (pli, eli);                 // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic long long *plli = (_Atomic long long*)((char*)&ealli + 1);
+  and_fetch (plli, elli);               // { dg-warning "-Wstringop-overflow" }
+  plli = (_Atomic long long*)((char*)&ealli + 1);
+  and_fetch (plli, eali);               // { dg-warning "-Wstringop-overflow" }
+  plli = &ealli + 1;
+  and_fetch (plli, elli);               // { dg-warning "-Wstringop-overflow" }
+}
+
+
+void nowarn_atomic_or_fetch (void)
+{
+  _Atomic char *pc = &eac;
+  or_fetch (pc, ecc);
+
+  _Atomic short *psi = &easi;
+  or_fetch (psi, esi);
+
+  _Atomic int *pi = &eai;
+  or_fetch (pi, ei);
+
+  _Atomic long *pli = &eali;
+  or_fetch (pli, eli);
+
+  _Atomic long long *plli = &ealli;
+  or_fetch (plli, elli);
+}
+
+
+void warn_atomic_or_fetch (void)
+{
+  _Atomic char *pc = &eac + 1;
+  or_fetch (pc, ecc);                   // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic short *psi = (_Atomic short*)((char*)&easi + 1);
+  or_fetch (psi, esi);                  // { dg-warning "-Wstringop-overflow" }
+  psi = (_Atomic short*)((char*)&easi + 2);
+  or_fetch (psi, esi);                  // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic int *pi = (_Atomic int*)((char*)&eai + 1);
+  or_fetch (pi, ei);                    // { dg-warning "-Wstringop-overflow" }
+  pi = (_Atomic int*)((char*)&eai + 2);
+  or_fetch (pi, ei);                    // { dg-warning "-Wstringop-overflow" }
+  pi = (_Atomic int*)((char*)&eai + sizeof eai);
+  or_fetch (pi, ei);                    // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic long *pli = (_Atomic long*)((char*)&eali + 1);
+  or_fetch (pli, eli);                  // { dg-warning "-Wstringop-overflow" }
+  pli = (_Atomic long*)((char*)&eali + 1);
+  or_fetch (pli, eli);                  // { dg-warning "-Wstringop-overflow" }
+  pli = &eali + 1;
+  or_fetch (pli, eli);                  // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic long long *plli = (_Atomic long long*)((char*)&ealli + 1);
+  or_fetch (plli, elli);                // { dg-warning "-Wstringop-overflow" }
+  plli = (_Atomic long long*)((char*)&ealli + 1);
+  or_fetch (plli, eali);                // { dg-warning "-Wstringop-overflow" }
+  plli = &ealli + 1;
+  or_fetch (plli, elli);                // { dg-warning "-Wstringop-overflow" }
+}
+
+
+void nowarn_atomic_xor_fetch (void)
+{
+  _Atomic char *pc = &eac;
+  xor_fetch (pc, ecc);
+
+  _Atomic short *psi = &easi;
+  xor_fetch (psi, esi);
+
+  _Atomic int *pi = &eai;
+  xor_fetch (pi, ei);
+
+  _Atomic long *pli = &eali;
+  xor_fetch (pli, eli);
+
+  _Atomic long long *plli = &ealli;
+  xor_fetch (plli, elli);
+}
+
+
+void warn_atomic_xor_fetch (void)
+{
+  _Atomic char *pc = &eac + 1;
+  xor_fetch (pc, ecc);                  // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic short *psi = (_Atomic short*)((char*)&easi + 1);
+  xor_fetch (psi, esi);                 // { dg-warning "-Wstringop-overflow" }
+  psi = (_Atomic short*)((char*)&easi + 1);
+  xor_fetch (psi, esi);                 // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic int *pi = (_Atomic int*)((char*)&eai + 1);
+  xor_fetch (pi, ei);                   // { dg-warning "-Wstringop-overflow" }
+  pi = (_Atomic int*)((char*)&eai + 2);
+  xor_fetch (pi, ei);                   // { dg-warning "-Wstringop-overflow" }
+  pi = (_Atomic int*)((char*)&eai + sizeof eai);
+  xor_fetch (pi, ei);                   // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic long *pli = (_Atomic long*)((char*)&eali + 1);
+  xor_fetch (pli, eli);                 // { dg-warning "-Wstringop-overflow" }
+  pli = (_Atomic long*)((char*)&eali + 1);
+  xor_fetch (pli, eli);                 // { dg-warning "-Wstringop-overflow" }
+  pli = &eali + 1;
+  xor_fetch (pli, eli);                 // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic long long *plli = (_Atomic long long*)((char*)&ealli + 1);
+  xor_fetch (plli, elli);               // { dg-warning "-Wstringop-overflow" }
+  plli = (_Atomic long long*)((char*)&eali + 1);
+  xor_fetch (plli, eali);               // { dg-warning "-Wstringop-overflow" }
+  plli = &ealli + 1;
+  xor_fetch (plli, elli);               // { dg-warning "-Wstringop-overflow" }
+}
+
+
+void nowarn_atomic_nand_fetch (void)
+{
+  _Atomic char *pc = &eac;
+  nand_fetch (pc, ecc);
+
+  _Atomic short *psi = &easi;
+  nand_fetch (psi, esi);
+
+  _Atomic int *pi = &eai;
+  nand_fetch (pi, ei);
+
+  _Atomic long *pli = &eali;
+  nand_fetch (pli, eli);
+
+  _Atomic long long *plli = &ealli;
+  nand_fetch (plli, elli);
+}
+
+
+void warn_atomic_nand_fetch (void)
+{
+  _Atomic char *pc = &eac + 1;
+  nand_fetch (pc, ecc);                 // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic short *psi = (_Atomic short*)((char*)&easi + 1);
+  nand_fetch (psi, esi);                // { dg-warning "-Wstringop-overflow" }
+  psi = (_Atomic short*)((char*)&easi + 1);
+  nand_fetch (psi, esi);                // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic int *pi = (_Atomic int*)((char*)&eai + 1);
+  nand_fetch (pi, ei);                  // { dg-warning "-Wstringop-overflow" }
+  pi = (_Atomic int*)((char*)&eai + 2);
+  nand_fetch (pi, ei);                  // { dg-warning "-Wstringop-overflow" }
+  pi = (_Atomic int*)((char*)&eai + sizeof eai);
+  nand_fetch (pi, ei);                  // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic long *pli = (_Atomic long*)((char*)&eali + 1);
+  nand_fetch (pli, eli);                // { dg-warning "-Wstringop-overflow" }
+  pli = (_Atomic long*)((char*)&eali + 1);
+  nand_fetch (pli, eli);                // { dg-warning "-Wstringop-overflow" }
+  pli = &eali + 1;
+  nand_fetch (pli, eli);                // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic long long *plli = (_Atomic long long*)((char*)&ealli + 1);
+  nand_fetch (plli, elli);              // { dg-warning "-Wstringop-overflow" }
+  plli = (_Atomic long long*)((char*)&eai + 1);
+  nand_fetch (plli, eali);              // { dg-warning "-Wstringop-overflow" }
+  plli = &ealli + 1;
+  nand_fetch (plli, elli);              // { dg-warning "-Wstringop-overflow" }
+}
+
+
+void nowarn_atomic_exchange (void)
+{
+  char rc;
+  _Atomic char *pc = &eac;
+  exchange (pc, &ecc, &rc);
+
+  short rsi;
+  _Atomic short *psi = &easi;
+  exchange (psi, &esi, &rsi);
+
+  int ri;
+  _Atomic int *pi = &eai;
+  exchange (pi, &ei, &ri);
+
+  long rli;
+  _Atomic long *pli = &eali;
+  exchange (pli, &eli, &rli);
+
+  long long rlli;
+  _Atomic long long *plli = &ealli;
+  exchange (plli, &elli, &rlli);
+
+  sink (&rc, &rsi, &ri, &rli, &rlli);
+}
+
+void warn_atomic_exchange (void)
+{
+  char rc;
+  _Atomic char *pc = &eac + 1;
+  exchange (pc, &ecc, &rc);             // { dg-warning "-Wstringop-overflow" }
+
+  short rsi[2];
+  _Atomic short *psi = (_Atomic short*)((char*)&easi + 1);
+  exchange (psi, &ecsi, rsi);           // { dg-warning "-Wstringop-overflow" }
+  psi = (_Atomic short*)((char*)&easi + 2);
+  exchange (psi, &ecsi, rsi + 1);       // { dg-warning "-Wstringop-overflow" }
+
+  int ri[3];
+  _Atomic int *pi = (_Atomic int*)((char*)&eai + 1);
+  exchange (pi, &eci, ri);              // { dg-warning "-Wstringop-overflow" }
+  pi = (_Atomic int*)((char*)&eai + 2);
+  exchange (pi, &eci, ri + 1);          // { dg-warning "-Wstringop-overflow" }
+  pi = (_Atomic int*)((char*)&eai + sizeof eai);
+  exchange (pi, &eci, ri + 2);          // { dg-warning "-Wstringop-overflow" }
+
+  long rli[3];
+  _Atomic long *pli = (_Atomic long*)((char*)&eali + 1);
+  exchange (pli, &ecli, rli);           // { dg-warning "-Wstringop-overflow" }
+  pli = (_Atomic long*)((char*)&eali + 1);
+  exchange (pli, &ecli, rli + 1);       // { dg-warning "-Wstringop-overflow" }
+  pli = &eali + 1;
+  exchange (pli, &ecli, rli + 2);       // { dg-warning "-Wstringop-overflow" }
+
+  long long rlli[3];
+  _Atomic long long *plli = (_Atomic long long*)((char*)&ealli + 1);
+  exchange (plli, &eclli, rlli);        // { dg-warning "-Wstringop-overflow" }
+  plli = (_Atomic long long*)((char*)&ealli + 1);
+  exchange (plli, &eclli, rlli + 1);    // { dg-warning "-Wstringop-overflow" }
+  plli = &ealli + 1;
+  exchange (plli, &eclli, rlli + 2);    // { dg-warning "-Wstringop-overflow" }
+
+  sink (&rc, rsi, ri, rli, rlli);
+}
+
+
+void nowarn_atomic_exchange_n (_Atomic unsigned char *pauc,
+			       _Atomic unsigned short *pausi,
+			       _Atomic unsigned int *paui,
+			       _Atomic unsigned long *pauli,
+			       _Atomic unsigned long long *paulli)
+{
+  char rc = exchange_n (&eac, ecc);
+  short rsi = exchange_n (&easi, esi);
+  int ri = exchange_n (&eai, ei);
+  long rli = exchange_n (&eali, eli);
+  long long rlli = exchange_n (&ealli, elli);
+
+  sink (rc, rsi, ri, rli, rlli);
+
+  char ruc = exchange_n (pauc, ecc);
+  short rusi = exchange_n (pausi, esi);
+  int rui = exchange_n (paui, ei);
+  long ruli = exchange_n (pauli, eli);
+  long long rulli = exchange_n (paulli, elli);
+
+  sink (ruc, rusi, rui, ruli, rulli);
+}
+
+
+void warn_atomic_exchange_n (void)
+{
+  _Atomic char *pc = &eac + 1;
+  char rc = exchange_n (pc, ecc);       // { dg-warning "-Wstringop-overflow" }
+
+  short rsi[2];
+  _Atomic short *psi = (_Atomic short*)((char*)&easi + 1);
+  rsi[0] = exchange_n (psi, ecsi);      // { dg-warning "-Wstringop-overflow" }
+  psi = (_Atomic short*)((char*)&easi + 2);
+  rsi[1] = exchange_n (psi, ecsi);      // { dg-warning "-Wstringop-overflow" }
+
+  int ri[3];
+  _Atomic int *pi = (_Atomic int*)((char*)&eai + 1);
+  ri[0] = exchange_n (pi, eci);         // { dg-warning "-Wstringop-overflow" }
+  pi = (_Atomic int*)((char*)&eai + 2);
+  ri[1] = exchange_n (pi, eci);         // { dg-warning "-Wstringop-overflow" }
+  pi = (_Atomic int*)((char*)&eai + sizeof eai);
+  ri[2] = exchange_n (pi, eci);         // { dg-warning "-Wstringop-overflow" }
+
+  long rli[3];
+  _Atomic long *pli = (_Atomic long*)((char*)&eali + 1);
+  rli[0] = exchange_n (pli, ecli);      // { dg-warning "-Wstringop-overflow" }
+  pli = (_Atomic long*)((char*)&eali + 1);
+  rli[1] = exchange_n (pli, ecli);      // { dg-warning "-Wstringop-overflow" }
+  pli = &eali + 1;
+  rli[2] = exchange_n (pli, ecli);      // { dg-warning "-Wstringop-overflow" }
+
+  long long rlli[3];
+  _Atomic long long *plli = (_Atomic long long*)((char*)&ealli + 1);
+  rlli[0] = exchange_n (plli, eclli);   // { dg-warning "-Wstringop-overflow" }
+  plli = (_Atomic long long*)((char*)&ealli + 1);
+  rlli[1] = exchange_n (plli, eclli);   // { dg-warning "-Wstringop-overflow" }
+  plli = &ealli + 1;
+  rlli[2] = exchange_n (plli, eclli);   // { dg-warning "-Wstringop-overflow" }
+
+  sink (&rc, rsi, ri, rli, rlli);
+}
+
+
+void warn_atomic_compare_exchange (void)
+{
+  _Atomic char *pc = &eac + 1;
+  cmpxchg (pc, &ec, &ecc);              // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic short *psi = (_Atomic short*)((char*)&easi + 1);
+  cmpxchg (psi, &esi, &ecsi);           // { dg-warning "-Wstringop-overflow" }
+  psi = (_Atomic short*)((char*)&easi + 2);
+  cmpxchg (psi, &esi, &ecsi);           // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic int *pi = (_Atomic int*)((char*)&eai + 1);
+  cmpxchg (pi, &ei, &eci);              // { dg-warning "-Wstringop-overflow" }
+  pi = (_Atomic int*)((char*)&eai + 2);
+  cmpxchg (pi, &ei, &eci);              // { dg-warning "-Wstringop-overflow" }
+  pi = (_Atomic int*)((char*)&eai + sizeof eai);
+  cmpxchg (pi, &ei, &eci);              // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic long *pli = (_Atomic long*)((char*)&eali + 1);
+  cmpxchg (pli, &eli, &ecli);           // { dg-warning "-Wstringop-overflow" }
+  pli = (_Atomic long*)((char*)&eali + 1);
+  cmpxchg (pli, &eli, &ecli);           // { dg-warning "-Wstringop-overflow" }
+  pli = &eali + 1;
+  cmpxchg (pli, &eli, &ecli);           // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic long long *plli = (_Atomic long long*)((char*)&ealli + 1);
+  cmpxchg (plli, &elli, &eclli);        // { dg-warning "-Wstringop-overflow" }
+  plli = (_Atomic long long*)((char*)&ealli + 1);
+  cmpxchg (plli, &elli, &eclli);        // { dg-warning "-Wstringop-overflow" }
+  plli = &ealli + 1;
+  cmpxchg (plli, &elli, &eclli);        // { dg-warning "-Wstringop-overflow" }
+}
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-78.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-78.c
new file mode 100644
index 00000000000..a25a418ed76
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-78.c
@@ -0,0 +1,518 @@ 
+/* PR middle-end/102453 - buffer overflow by atomic built-ins not diagnosed
+   Verify that out-of-bounds accesses by atomic functions are diagnosed with
+   optimization enabled.
+   { dg-do compile }
+   { dg-options "-O3 -Wall -ftrack-macro-expansion=0" }  */
+
+#ifndef __cplusplus
+#  define bool _Bool
+#endif
+
+#define NOIPA __attribute__ ((noipa))
+
+#define add_fetch(p, q)   __atomic_add_fetch (p, q, 0)
+#define sub_fetch(p, q)   __atomic_sub_fetch (p, q, 0)
+#define and_fetch(p, q)   __atomic_and_fetch (p, q, 0)
+#define or_fetch(p, q)    __atomic_or_fetch (p, q, 0)
+#define xor_fetch(p, q)   __atomic_xor_fetch (p, q, 0)
+#define nand_fetch(p, q)  __atomic_nand_fetch (p, q, 0)
+#define exchange(p, q, r) __atomic_exchange (p, q, r, 0)
+#define exchange_n(p, n)  __atomic_exchange_n (p, n, 0)
+#define cmpxchg(p, q, r)  __atomic_compare_exchange (p, q, r, __COUNTER__, 0, 0)
+
+typedef __SIZE_TYPE__ size_t;
+
+void sink (void*, ...);
+#define sink(...) sink (0, __VA_ARGS__)
+
+extern _Bool eb;
+extern char ec;
+extern short int esi;
+extern int ei;
+extern long int eli;
+extern long long int elli;
+
+extern const _Bool ecb;
+extern const char ecc;
+extern const short int ecsi;
+extern const int eci;
+extern const long int ecli;
+extern const long long int eclli;
+
+extern _Atomic _Bool eab;
+extern _Atomic char eac;
+extern _Atomic short int easi;
+extern _Atomic int eai;
+extern _Atomic long int eali;
+extern _Atomic long long int ealli;
+
+extern _Atomic const _Bool eacb;
+extern _Atomic const char eacc;
+extern _Atomic const short int eacsi;
+extern _Atomic const int eaci;
+extern _Atomic const long int eacli;
+extern _Atomic const long long int eaclli;
+
+
+NOIPA void nowarn_atomic_add_fetch (void)
+{
+  add_fetch (&eac, ecc);
+  add_fetch (&easi, esi);
+  add_fetch (&eai, ei);
+  add_fetch (&eali, eli);
+  add_fetch (&ealli, elli);
+}
+
+
+NOIPA void warn_atomic_add_fetch (void)
+{
+  _Atomic char *pc = &eac + 1;
+  add_fetch (pc, ecc);                  // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic short *psi = (_Atomic short*)((char*)&easi + 1);
+  add_fetch (psi, esi);                 // { dg-warning "-Wstringop-overflow" }
+  psi = (_Atomic short*)((char*)&easi + 2);
+  add_fetch (psi, esi);                 // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic int *pi = (_Atomic int*)((char*)&eai + 1);
+  add_fetch (pi, ei);                   // { dg-warning "-Wstringop-overflow" }
+  pi = (_Atomic int*)((char*)&eai + 2);
+  add_fetch (pi, ei);                   // { dg-warning "-Wstringop-overflow" }
+  pi = (_Atomic int*)((char*)&eai + sizeof eai);
+  add_fetch (pi, ei);                   // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic long *pli = (_Atomic long*)((char*)&eali + 1);
+  add_fetch (pli, eli);                 // { dg-warning "-Wstringop-overflow" }
+  pli = (_Atomic long*)((char*)&eali + 1);
+  add_fetch (pli, eli);                 // { dg-warning "-Wstringop-overflow" }
+  pli = &eali + 1;
+  add_fetch (pli, eli);                 // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic long long *plli = (_Atomic long long*)((char*)&ealli + 1);
+  add_fetch (plli, elli);               // { dg-warning "-Wstringop-overflow" }
+  plli = (_Atomic long long*)((char*)&ealli + 1);
+  add_fetch (plli, eali);               // { dg-warning "-Wstringop-overflow" }
+  plli = &ealli + 1;
+  add_fetch (plli, elli);               // { dg-warning "-Wstringop-overflow" }
+}
+
+
+NOIPA void nowarn_atomic_sub_fetch (void)
+{
+  _Atomic char *pc = &eac;
+  sub_fetch (pc, ecc);
+
+  _Atomic short *psi = &easi;
+  sub_fetch (psi, esi);
+
+  _Atomic int *pi = &eai;
+  sub_fetch (pi, ei);
+
+  _Atomic long *pli = &eali;
+  sub_fetch (pli, eli);
+
+  _Atomic long long *plli = &ealli;
+  sub_fetch (plli, elli);
+}
+
+
+NOIPA void warn_atomic_sub_fetch (void)
+{
+  _Atomic char *pc = &eac + 1;
+  sub_fetch (pc, ecc);                  // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic short *psi = (_Atomic short*)((char*)&easi + 1);
+  sub_fetch (psi, esi);                 // { dg-warning "-Wstringop-overflow" }
+  psi = (_Atomic short*)((char*)&easi + 2);
+  sub_fetch (psi, esi);                 // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic int *pi = (_Atomic int*)((char*)&eai + 1);
+  sub_fetch (pi, ei);                   // { dg-warning "-Wstringop-overflow" }
+  pi = (_Atomic int*)((char*)&eai + 2);
+  sub_fetch (pi, ei);                   // { dg-warning "-Wstringop-overflow" }
+  pi = (_Atomic int*)((char*)&eai + sizeof eai);
+  sub_fetch (pi, ei);                   // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic long *pli = (_Atomic long*)((char*)&eali + 1);
+  sub_fetch (pli, eli);                 // { dg-warning "-Wstringop-overflow" }
+  pli = (_Atomic long*)((char*)&eali + 1);
+  sub_fetch (pli, eli);                 // { dg-warning "-Wstringop-overflow" }
+  pli = &eali + 1;
+  sub_fetch (pli, eli);                 // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic long long *plli = (_Atomic long long*)((char*)&ealli + 1);
+  sub_fetch (plli, elli);               // { dg-warning "-Wstringop-overflow" }
+  plli = (_Atomic long long*)((char*)&ealli + 1);
+  sub_fetch (plli, eali);               // { dg-warning "-Wstringop-overflow" }
+  plli = &ealli + 1;
+  sub_fetch (plli, elli);               // { dg-warning "-Wstringop-overflow" }
+}
+
+
+NOIPA void nowarn_atomic_and_fetch (void)
+{
+  _Atomic char *pc = &eac;
+  and_fetch (pc, ecc);
+
+  _Atomic short *psi = &easi;
+  and_fetch (psi, esi);
+
+  _Atomic int *pi = &eai;
+  and_fetch (pi, ei);
+
+  _Atomic long *pli = &eali;
+  and_fetch (pli, eli);
+
+  _Atomic long long *plli = &ealli;
+  and_fetch (plli, elli);
+}
+
+
+NOIPA void warn_atomic_and_fetch (void)
+{
+  _Atomic char *pc = &eac + 1;
+  and_fetch (pc, ecc);                  // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic short *psi = (_Atomic short*)((char*)&easi + 1);
+  and_fetch (psi, esi);                 // { dg-warning "-Wstringop-overflow" }
+  psi = (_Atomic short*)((char*)&easi + 2);
+  and_fetch (psi, esi);                 // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic int *pi = (_Atomic int*)((char*)&eai + 1);
+  and_fetch (pi, ei);                   // { dg-warning "-Wstringop-overflow" }
+  pi = (_Atomic int*)((char*)&eai + 2);
+  and_fetch (pi, ei);                   // { dg-warning "-Wstringop-overflow" }
+  pi = (_Atomic int*)((char*)&eai + sizeof eai);
+  and_fetch (pi, ei);                   // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic long *pli = (_Atomic long*)((char*)&eali + 1);
+  and_fetch (pli, eli);                 // { dg-warning "-Wstringop-overflow" }
+  pli = (_Atomic long*)((char*)&eali + 1);
+  and_fetch (pli, eli);                 // { dg-warning "-Wstringop-overflow" }
+  pli = &eali + 1;
+  and_fetch (pli, eli);                 // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic long long *plli = (_Atomic long long*)((char*)&ealli + 1);
+  and_fetch (plli, elli);               // { dg-warning "-Wstringop-overflow" }
+  plli = (_Atomic long long*)((char*)&ealli + 1);
+  and_fetch (plli, eali);               // { dg-warning "-Wstringop-overflow" }
+  plli = &ealli + 1;
+  and_fetch (plli, elli);               // { dg-warning "-Wstringop-overflow" }
+}
+
+
+NOIPA void nowarn_atomic_or_fetch (void)
+{
+  _Atomic char *pc = &eac;
+  or_fetch (pc, ecc);
+
+  _Atomic short *psi = &easi;
+  or_fetch (psi, esi);
+
+  _Atomic int *pi = &eai;
+  or_fetch (pi, ei);
+
+  _Atomic long *pli = &eali;
+  or_fetch (pli, eli);
+
+  _Atomic long long *plli = &ealli;
+  or_fetch (plli, elli);
+}
+
+
+NOIPA void warn_atomic_or_fetch (void)
+{
+  _Atomic char *pc = &eac + 1;
+  or_fetch (pc, ecc);                   // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic short *psi = (_Atomic short*)((char*)&easi + 1);
+  or_fetch (psi, esi);                  // { dg-warning "-Wstringop-overflow" }
+  psi = (_Atomic short*)((char*)&easi + 2);
+  or_fetch (psi, esi);                  // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic int *pi = (_Atomic int*)((char*)&eai + 1);
+  or_fetch (pi, ei);                    // { dg-warning "-Wstringop-overflow" }
+  pi = (_Atomic int*)((char*)&eai + 2);
+  or_fetch (pi, ei);                    // { dg-warning "-Wstringop-overflow" }
+  pi = (_Atomic int*)((char*)&eai + sizeof eai);
+  or_fetch (pi, ei);                    // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic long *pli = (_Atomic long*)((char*)&eali + 1);
+  or_fetch (pli, eli);                  // { dg-warning "-Wstringop-overflow" }
+  pli = (_Atomic long*)((char*)&eali + 1);
+  or_fetch (pli, eli);                  // { dg-warning "-Wstringop-overflow" }
+  pli = &eali + 1;
+  or_fetch (pli, eli);                  // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic long long *plli = (_Atomic long long*)((char*)&ealli + 1);
+  or_fetch (plli, elli);                // { dg-warning "-Wstringop-overflow" }
+  plli = (_Atomic long long*)((char*)&ealli + 1);
+  or_fetch (plli, eali);                // { dg-warning "-Wstringop-overflow" }
+  plli = &ealli + 1;
+  or_fetch (plli, elli);                // { dg-warning "-Wstringop-overflow" }
+}
+
+
+NOIPA void nowarn_atomic_xor_fetch (void)
+{
+  _Atomic char *pc = &eac;
+  xor_fetch (pc, ecc);
+
+  _Atomic short *psi = &easi;
+  xor_fetch (psi, esi);
+
+  _Atomic int *pi = &eai;
+  xor_fetch (pi, ei);
+
+  _Atomic long *pli = &eali;
+  xor_fetch (pli, eli);
+
+  _Atomic long long *plli = &ealli;
+  xor_fetch (plli, elli);
+}
+
+
+NOIPA void warn_atomic_xor_fetch (void)
+{
+  _Atomic char *pc = &eac + 1;
+  xor_fetch (pc, ecc);                  // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic short *psi = (_Atomic short*)((char*)&easi + 1);
+  xor_fetch (psi, esi);                 // { dg-warning "-Wstringop-overflow" }
+  psi = (_Atomic short*)((char*)&easi + 1);
+  xor_fetch (psi, esi);                 // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic int *pi = (_Atomic int*)((char*)&eai + 1);
+  xor_fetch (pi, ei);                   // { dg-warning "-Wstringop-overflow" }
+  pi = (_Atomic int*)((char*)&eai + 2);
+  xor_fetch (pi, ei);                   // { dg-warning "-Wstringop-overflow" }
+  pi = (_Atomic int*)((char*)&eai + sizeof eai);
+  xor_fetch (pi, ei);                   // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic long *pli = (_Atomic long*)((char*)&eali + 1);
+  xor_fetch (pli, eli);                 // { dg-warning "-Wstringop-overflow" }
+  pli = (_Atomic long*)((char*)&eali + 1);
+  xor_fetch (pli, eli);                 // { dg-warning "-Wstringop-overflow" }
+  pli = &eali + 1;
+  xor_fetch (pli, eli);                 // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic long long *plli = (_Atomic long long*)((char*)&ealli + 1);
+  xor_fetch (plli, elli);               // { dg-warning "-Wstringop-overflow" }
+  plli = (_Atomic long long*)((char*)&eali + 1);
+  xor_fetch (plli, eali);               // { dg-warning "-Wstringop-overflow" }
+  plli = &ealli + 1;
+  xor_fetch (plli, elli);               // { dg-warning "-Wstringop-overflow" }
+}
+
+
+NOIPA void nowarn_atomic_nand_fetch (void)
+{
+  _Atomic char *pc = &eac;
+  nand_fetch (pc, ecc);
+
+  _Atomic short *psi = &easi;
+  nand_fetch (psi, esi);
+
+  _Atomic int *pi = &eai;
+  nand_fetch (pi, ei);
+
+  _Atomic long *pli = &eali;
+  nand_fetch (pli, eli);
+
+  _Atomic long long *plli = &ealli;
+  nand_fetch (plli, elli);
+}
+
+
+NOIPA void warn_atomic_nand_fetch (void)
+{
+  _Atomic char *pc = &eac + 1;
+  nand_fetch (pc, ecc);                 // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic short *psi = (_Atomic short*)((char*)&easi + 1);
+  nand_fetch (psi, esi);                // { dg-warning "-Wstringop-overflow" }
+  psi = (_Atomic short*)((char*)&easi + 1);
+  nand_fetch (psi, esi);                // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic int *pi = (_Atomic int*)((char*)&eai + 1);
+  nand_fetch (pi, ei);                  // { dg-warning "-Wstringop-overflow" }
+  pi = (_Atomic int*)((char*)&eai + 2);
+  nand_fetch (pi, ei);                  // { dg-warning "-Wstringop-overflow" }
+  pi = (_Atomic int*)((char*)&eai + sizeof eai);
+  nand_fetch (pi, ei);                  // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic long *pli = (_Atomic long*)((char*)&eali + 1);
+  nand_fetch (pli, eli);                // { dg-warning "-Wstringop-overflow" }
+  pli = (_Atomic long*)((char*)&eali + 1);
+  nand_fetch (pli, eli);                // { dg-warning "-Wstringop-overflow" }
+  pli = &eali + 1;
+  nand_fetch (pli, eli);                // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic long long *plli = (_Atomic long long*)((char*)&ealli + 1);
+  nand_fetch (plli, elli);              // { dg-warning "-Wstringop-overflow" }
+  plli = (_Atomic long long*)((char*)&eai + 1);
+  nand_fetch (plli, eali);              // { dg-warning "-Wstringop-overflow" }
+  plli = &ealli + 1;
+  nand_fetch (plli, elli);              // { dg-warning "-Wstringop-overflow" }
+}
+
+
+NOIPA void nowarn_atomic_exchange (void)
+{
+  char rc;
+  _Atomic char *pc = &eac;
+  exchange (pc, &ecc, &rc);
+
+  short rsi;
+  _Atomic short *psi = &easi;
+  exchange (psi, &esi, &rsi);
+
+  int ri;
+  _Atomic int *pi = &eai;
+  exchange (pi, &ei, &ri);
+
+  long rli;
+  _Atomic long *pli = &eali;
+  exchange (pli, &eli, &rli);
+
+  long long rlli;
+  _Atomic long long *plli = &ealli;
+  exchange (plli, &elli, &rlli);
+
+  sink (&rc, &rsi, &ri, &rli, &rlli);
+}
+
+NOIPA void warn_atomic_exchange (void)
+{
+  char rc;
+  _Atomic char *pc = &eac + 1;
+  exchange (pc, &ecc, &rc);             // { dg-warning "-Wstringop-overflow" }
+
+  short rsi[2];
+  _Atomic short *psi = (_Atomic short*)((char*)&easi + 1);
+  exchange (psi, &ecsi, rsi);           // { dg-warning "-Wstringop-overflow" }
+  psi = (_Atomic short*)((char*)&easi + 2);
+  exchange (psi, &ecsi, rsi + 1);       // { dg-warning "-Wstringop-overflow" }
+
+  int ri[3];
+  _Atomic int *pi = (_Atomic int*)((char*)&eai + 1);
+  exchange (pi, &eci, ri);              // { dg-warning "-Wstringop-overflow" }
+  pi = (_Atomic int*)((char*)&eai + 2);
+  exchange (pi, &eci, ri + 1);          // { dg-warning "-Wstringop-overflow" }
+  pi = (_Atomic int*)((char*)&eai + sizeof eai);
+  exchange (pi, &eci, ri + 2);          // { dg-warning "-Wstringop-overflow" }
+
+  long rli[3];
+  _Atomic long *pli = (_Atomic long*)((char*)&eali + 1);
+  exchange (pli, &ecli, rli);           // { dg-warning "-Wstringop-overflow" }
+  pli = (_Atomic long*)((char*)&eali + 1);
+  exchange (pli, &ecli, rli + 1);       // { dg-warning "-Wstringop-overflow" }
+  pli = &eali + 1;
+  exchange (pli, &ecli, rli + 2);       // { dg-warning "-Wstringop-overflow" }
+
+  long long rlli[3];
+  _Atomic long long *plli = (_Atomic long long*)((char*)&ealli + 1);
+  exchange (plli, &eclli, rlli);        // { dg-warning "-Wstringop-overflow" }
+  plli = (_Atomic long long*)((char*)&ealli + 1);
+  exchange (plli, &eclli, rlli + 1);    // { dg-warning "-Wstringop-overflow" }
+  plli = &ealli + 1;
+  exchange (plli, &eclli, rlli + 2);    // { dg-warning "-Wstringop-overflow" }
+
+  sink (&rc, rsi, ri, rli, rlli);
+}
+
+
+NOIPA void nowarn_atomic_exchange_n (_Atomic unsigned char *pauc,
+			       _Atomic unsigned short *pausi,
+			       _Atomic unsigned int *paui,
+			       _Atomic unsigned long *pauli,
+			       _Atomic unsigned long long *paulli)
+{
+  char rc = exchange_n (&eac, ecc);
+  short rsi = exchange_n (&easi, esi);
+  int ri = exchange_n (&eai, ei);
+  long rli = exchange_n (&eali, eli);
+  long long rlli = exchange_n (&ealli, elli);
+
+  sink (rc, rsi, ri, rli, rlli);
+
+  char ruc = exchange_n (pauc, ecc);
+  short rusi = exchange_n (pausi, esi);
+  int rui = exchange_n (paui, ei);
+  long ruli = exchange_n (pauli, eli);
+  long long rulli = exchange_n (paulli, elli);
+
+  sink (ruc, rusi, rui, ruli, rulli);
+}
+
+
+NOIPA void warn_atomic_exchange_n (void)
+{
+  _Atomic char *pc = &eac + 1;
+  char rc = exchange_n (pc, ecc);       // { dg-warning "-Wstringop-overflow" }
+
+  short rsi[2];
+  _Atomic short *psi = (_Atomic short*)((char*)&easi + 1);
+  rsi[0] = exchange_n (psi, ecsi);      // { dg-warning "-Wstringop-overflow" }
+  psi = (_Atomic short*)((char*)&easi + 2);
+  rsi[1] = exchange_n (psi, ecsi);      // { dg-warning "-Wstringop-overflow" }
+
+  int ri[3];
+  _Atomic int *pi = (_Atomic int*)((char*)&eai + 1);
+  ri[0] = exchange_n (pi, eci);         // { dg-warning "-Wstringop-overflow" }
+  pi = (_Atomic int*)((char*)&eai + 2);
+  ri[1] = exchange_n (pi, eci);         // { dg-warning "-Wstringop-overflow" }
+  pi = (_Atomic int*)((char*)&eai + sizeof eai);
+  ri[2] = exchange_n (pi, eci);         // { dg-warning "-Wstringop-overflow" }
+
+  long rli[3];
+  _Atomic long *pli = (_Atomic long*)((char*)&eali + 1);
+  rli[0] = exchange_n (pli, ecli);      // { dg-warning "-Wstringop-overflow" }
+  pli = (_Atomic long*)((char*)&eali + 1);
+  rli[1] = exchange_n (pli, ecli);      // { dg-warning "-Wstringop-overflow" }
+  pli = &eali + 1;
+  rli[2] = exchange_n (pli, ecli);      // { dg-warning "-Wstringop-overflow" }
+
+  long long rlli[3];
+  _Atomic long long *plli = (_Atomic long long*)((char*)&ealli + 1);
+  rlli[0] = exchange_n (plli, eclli);   // { dg-warning "-Wstringop-overflow" }
+  plli = (_Atomic long long*)((char*)&ealli + 1);
+  rlli[1] = exchange_n (plli, eclli);   // { dg-warning "-Wstringop-overflow" }
+  plli = &ealli + 1;
+  rlli[2] = exchange_n (plli, eclli);   // { dg-warning "-Wstringop-overflow" }
+
+  sink (&rc, rsi, ri, rli, rlli);
+}
+
+
+NOIPA void warn_atomic_compare_exchange (void)
+{
+  _Atomic char *pc = &eac + 1;
+  cmpxchg (pc, &ec, &ecc);              // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic short *psi = (_Atomic short*)((char*)&easi + 1);
+  cmpxchg (psi, &esi, &ecsi);           // { dg-warning "-Wstringop-overflow" }
+  psi = (_Atomic short*)((char*)&easi + 2);
+  cmpxchg (psi, &esi, &ecsi);           // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic int *pi = (_Atomic int*)((char*)&eai + 1);
+  cmpxchg (pi, &ei, &eci);              // { dg-warning "-Wstringop-overflow" }
+  pi = (_Atomic int*)((char*)&eai + 2);
+  cmpxchg (pi, &ei, &eci);              // { dg-warning "-Wstringop-overflow" }
+  pi = (_Atomic int*)((char*)&eai + sizeof eai);
+  cmpxchg (pi, &ei, &eci);              // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic long *pli = (_Atomic long*)((char*)&eali + 1);
+  cmpxchg (pli, &eli, &ecli);           // { dg-warning "-Wstringop-overflow" }
+  pli = (_Atomic long*)((char*)&eali + 1);
+  cmpxchg (pli, &eli, &ecli);           // { dg-warning "-Wstringop-overflow" }
+  pli = &eali + 1;
+  cmpxchg (pli, &eli, &ecli);           // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic long long *plli = (_Atomic long long*)((char*)&ealli + 1);
+  cmpxchg (plli, &elli, &eclli);        // { dg-warning "-Wstringop-overflow" }
+  plli = (_Atomic long long*)((char*)&ealli + 1);
+  cmpxchg (plli, &elli, &eclli);        // { dg-warning "-Wstringop-overflow" }
+  plli = &ealli + 1;
+  cmpxchg (plli, &elli, &eclli);        // { dg-warning "-Wstringop-overflow" }
+}
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-79.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-79.c
new file mode 100644
index 00000000000..15eb26fbdb7
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-79.c
@@ -0,0 +1,70 @@ 
+/* Verify that a separate note is issued for each offset into the same
+   object after a -Wstringop-overflow.  Since all arguments are known
+   the test doesn't need optimization.  Wstringop-overflow-79.c verifies
+   they're also issued at -O2.
+   { dg-do compile }
+   { dg-options "-O0 -Wno-array-bounds" } */
+
+extern char a[8];                 // dg-message at offset \\\[3, 6] into destination object 'a'" "note 1" }
+                                  // dg-message at offset \\\[5, 8] into destination object 'a'" "note 2" { target *-*-* } .-1 }
+
+void test_2_notes (int i)
+{
+  char *p = i ? a + 3 : a + 5;
+  __builtin_memset (p, 0, 7);     // { dg-warning "-Wstringop-overflow" }
+}
+
+
+extern char b[8];                 // dg-message at offset \\\[3, 6] into destination object 'b'" "note 1" }
+                                  // dg-message at offset \\\[4, 7] into destination object 'b'" "note 2" { target *-*-* } .-1 }
+                                  // dg-message at offset \\\[5, 8] into destination object 'b'" "note 3" { target *-*-* } .-2 }
+
+void test_3_notes (int i)
+{
+  char *p = i < 0 ? b + 3 : 0 < i ? b + 5 : b + 4;
+  __builtin_memset (p, 0, 7);     // { dg-warning "-Wstringop-overflow" }
+}
+
+
+extern char c[8];                 // dg-message at offset \\\[3, 6] into destination object 'c'" "note 1" }
+                                  // dg-message at offset \\\[4, 7] into destination object 'c'" "note 2" { target *-*-* } .-1 }
+                                  // dg-message at offset \\\[5, 8] into destination object 'c'" "note 3" { target *-*-* } .-2 }
+                                  // dg-message at offset \\\[6, 8] into destination object 'c'" "note 3" { target *-*-* } .-2 }
+
+void test_4_notes (int i)
+{
+  char *p;
+  if (i < -1)
+    p = c + 3;
+  else if (i < 0)
+    p = c + 4;
+  else if (0 < i)
+    p = c + 6;
+  else
+    p = c + 5;
+
+  __builtin_memset (p, 0, 7);     // { dg-warning "-Wstringop-overflow" }
+}
+
+
+extern char d[8];                 // dg-message at offset \\\[3, 6] into destination object 'd'" "note 1" }
+                                  // dg-message at offset \\\[4, 7] into destination object 'd'" "note 2" { target *-*-* } .-1 }
+                                  // dg-message at offset \\\[5, 8] into destination object 'd'" "note 3" { target *-*-* } .-2 }
+                                  // dg-message at offset \\\[6, 8] into destination object 'd'" "note 3" { target *-*-* } .-3 }
+                                  // dg-message at offset \\\[7, 8] into destination object 'd'" "note 3" { target *-*-* } .-4 }
+
+void test_5_notes (int i)
+{
+  char *p;
+  switch (i)
+    {
+    case -9: p = d + 3; break;
+    case -5: p = d + 4; break;
+    case  0: p = d + 5; break;
+    case  3: p = d + 6; break;
+    case  4: p = d + 7; break;
+    default: return;
+    }
+
+  __builtin_memset (p, 0, 7);     // { dg-warning "-Wstringop-overflow" }
+}
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-80.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-80.c
new file mode 100644
index 00000000000..1628c2f0159
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-80.c
@@ -0,0 +1,70 @@ 
+/* Verify that a separate note is issued for each offset into the same
+   object after a -Wstringop-overflow.  Even though the warnings don't
+   need optimization the test enables it to verify they're still issued
+   with it.  Wstringop-overflow-78.c verifies they're issued at -O0.
+   { dg-do compile }
+   { dg-options "-O2 -Wno-array-bounds" } */
+
+extern char a[8];                 // dg-message at offset \\\[3, 6] into destination object 'a'" "note 1" }
+                                  // dg-message at offset \\\[5, 8] into destination object 'a'" "note 2" { target *-*-* } .-1 }
+
+void test_2_notes (int i)
+{
+  char *p = i ? a + 3 : a + 5;
+  __builtin_memset (p, 0, 7);     // { dg-warning "-Wstringop-overflow" }
+}
+
+
+extern char b[8];                 // dg-message at offset \\\[3, 6] into destination object 'b'" "note 1" }
+                                  // dg-message at offset \\\[4, 7] into destination object 'b'" "note 2" { target *-*-* } .-1 }
+                                  // dg-message at offset \\\[5, 8] into destination object 'b'" "note 3" { target *-*-* } .-2 }
+
+void test_3_notes (int i)
+{
+  char *p = i < 0 ? b + 3 : 0 < i ? b + 5 : b + 4;
+  __builtin_memset (p, 0, 7);     // { dg-warning "-Wstringop-overflow" }
+}
+
+
+extern char c[8];                 // dg-message at offset \\\[3, 6] into destination object 'c'" "note 1" }
+                                  // dg-message at offset \\\[4, 7] into destination object 'c'" "note 2" { target *-*-* } .-1 }
+                                  // dg-message at offset \\\[5, 8] into destination object 'c'" "note 3" { target *-*-* } .-2 }
+                                  // dg-message at offset \\\[6, 8] into destination object 'c'" "note 3" { target *-*-* } .-2 }
+
+void test_4_notes (int i)
+{
+  char *p;
+  if (i < -1)
+    p = c + 3;
+  else if (i < 0)
+    p = c + 4;
+  else if (0 < i)
+    p = c + 6;
+  else
+    p = c + 5;
+
+  __builtin_memset (p, 0, 7);     // { dg-warning "-Wstringop-overflow" }
+}
+
+
+extern char d[8];                 // dg-message at offset \\\[3, 6] into destination object 'd'" "note 1" }
+                                  // dg-message at offset \\\[4, 7] into destination object 'd'" "note 2" { target *-*-* } .-1 }
+                                  // dg-message at offset \\\[5, 8] into destination object 'd'" "note 3" { target *-*-* } .-2 }
+                                  // dg-message at offset \\\[6, 8] into destination object 'd'" "note 3" { target *-*-* } .-3 }
+                                  // dg-message at offset \\\[7, 8] into destination object 'd'" "note 3" { target *-*-* } .-4 }
+
+void test_5_notes (int i)
+{
+  char *p;
+  switch (i)
+    {
+    case -9: p = d + 3; break;
+    case -5: p = d + 4; break;
+    case  0: p = d + 5; break;
+    case  3: p = d + 6; break;
+    case  4: p = d + 7; break;
+    default: return;
+    }
+
+  __builtin_memset (p, 0, 7);     // { dg-warning "-Wstringop-overflow" }
+}