Extend the offset and size of merged object references [PR103215].

Resolves:
PR tree-optimization/103215 - bogus -Warray-bounds with two pointers with different offsets each


gcc/ChangeLog:

	PR tree-optimization/103215
	* pointer-query.cc (access_ref::merge_ref): Extend the offset and
	size of the merged object instead of using the larger.

gcc/testsuite/ChangeLog:

	PR tree-optimization/103215
	* gcc.dg/Wstringop-overflow-58.c: Adjust and xfail expected warnings.
	* gcc.dg/Wstringop-overflow-59.c: Same.
	* gcc.dg/warn-strnlen-no-nul.c: Same.
	* gcc.dg/Warray-bounds-91.c: New test.
	* gcc.dg/Warray-bounds-92.c: New test.
	* gcc.dg/Wstringop-overflow-83.c: New test.
	* gcc.dg/Wstringop-overflow-85.c: New test.

diff --git a/gcc/pointer-query.cc b/gcc/pointer-query.cc
index 2f305514ddc..7dc6e0141f8 100644
--- a/gcc/pointer-query.cc
+++ b/gcc/pointer-query.cc
@@ -686,27 +686,32 @@ access_ref::merge_ref (vec<access_ref> *all_refs, tree arg, gimple *stmt,
   if (known_size && aref.sizrng[0] < minsize)
     minsize = aref.sizrng[0];
 
-  /* Determine the amount of remaining space in the argument.  */
-  offset_int argrem[2];
-  argrem[1] = aref.size_remaining (argrem);
+  /* Extend the size and offset of *THIS to account for AREF.  The result
+     can be cached but results in false negatives.  */
 
-  /* Determine the amount of remaining space computed so far and
-     if the remaining space in the argument is more use it instead.  */
-  offset_int merged_rem[2];
-  merged_rem[1] = size_remaining (merged_rem);
+  offset_int orng[2];
+  if (sizrng[1] < aref.sizrng[1])
+    {
+      orng[0] = offrng[0];
+      orng[1] = offrng[1];
+      *this = aref;
+    }
+  else
+    {
+      orng[0] = aref.offrng[0];
+      orng[1] = aref.offrng[1];
+    }
+
+  if (orng[0] < offrng[0])
+    offrng[0] = orng[0];
+  if (offrng[1] < orng[1])
+    offrng[1] = orng[1];
 
   /* Reset the PHI's BASE0 flag if any of the nonnull arguments
      refers to an object at an unknown offset.  */
   if (!aref.base0)
     base0 = false;
 
-  if (merged_rem[1] < argrem[1]
-      || (merged_rem[1] == argrem[1]
-	  && sizrng[1] < aref.sizrng[1]))
-    /* Use the argument with the most space remaining as the result,
-       or the larger one if the space is equal.  */
-    *this = aref;
-
   sizrng[0] = minsize;
   parmarray = merged_parmarray;
 
diff --git a/gcc/testsuite/gcc.dg/Warray-bounds-91.c b/gcc/testsuite/gcc.dg/Warray-bounds-91.c
new file mode 100644
index 00000000000..1c81091b2a6
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Warray-bounds-91.c
@@ -0,0 +1,145 @@
+/* PR middle-end/103215 - bogus -Warray-bounds with two pointers with
+   different offsets each
+   Test for accesses into the same array through pointers with different
+   offsets each.
+   { dg-do compile }
+   { dg-options "-O2 -Wall -ftrack-macro-expansion=0" } */
+
+#define NOIPA __attribute__ ((noipa))
+
+#define A(p, off) ((p)[off] = __COUNTER__)
+
+extern int a4[4];
+
+
+NOIPA void p0_p1 (int i)
+{
+  int *p0 = a4 + 0;
+  int *p1 = a4 + 1;
+  int *q = i ? p0 : p1;
+  A (q, -2);      // { dg-warning "-Warray-bounds" }
+  A (q, -1); A (q, 0); A (q, 1); A (q, 2);
+  /* Since q points to a4 and -1 is a valid subscript, +3 must be invalid.
+     But the warning for each subscript is independent of prior subscripts
+     into the same object.  That should be improved.  */
+  A (q, 3);       // { dg-warning "-Warray-bounds" "pr??????" { xfail *-*-* } }
+  A (q, 4);       // { dg-warning "-Warray-bounds" }
+}
+
+NOIPA void p1_p0 (int i)
+{
+  int *p1 = a4 + 1;
+  int *p0 = a4 + 0;
+  int *q = i ? p0 : p1;
+  A (q, -2);      // { dg-warning "-Warray-bounds" }
+  A (q, -1); A (q, 0); A (q, 1); A (q, 2);
+  A (q, 3);       // { dg-warning "-Warray-bounds" "pr??????" { xfail *-*-* } }
+  A (q, 4);       // { dg-warning "-Warray-bounds" }
+}
+
+
+NOIPA void p1_p2 (int i)
+{
+  int *p1 = a4 + 1;
+  int *p2 = a4 + 2;
+  int *q = i ? p1 : p2;
+  A (q, -3);      // { dg-warning "-Warray-bounds" }
+  A (q, -2); A (q, -1); A (q, 0); A (q, 1);
+  A (q, 2);       // { dg-warning "-Warray-bounds" "pr??????" { xfail *-*-* } }
+  A (q, 3);       // { dg-warning "-Warray-bounds" }
+}
+
+NOIPA void p2_p1 (int i)
+{
+  int *p2 = a4 + 2;
+  int *p1 = a4 + 1;
+  int *q = i ? p1 : p2;
+  A (q, -3);      // { dg-warning "-Warray-bounds" }
+  A (q, -2); A (q, -1); A (q, 0); A (q, 1);
+  A (q, 2);       // { dg-warning "-Warray-bounds" "pr??????" { xfail *-*-* } }
+  A (q, 3);       // { dg-warning "-Warray-bounds" }
+}
+
+
+NOIPA void p1_p3 (int i)
+{
+  int *p1 = a4 + 1;
+  int *p3 = a4 + 3;
+  int *q = i ? p1 : p3;
+  A (q, -4);      // { dg-warning "-Warray-bounds" }
+  A (q, -3); A (q, -2); A (q, -1); A (q, 0);
+  A (q, 1);       // { dg-warning "-Warray-bounds" "pr??????" { xfail *-*-* } }
+  A (q, 2);       // { dg-warning "-Warray-bounds" "pr??????" { xfail *-*-* } }
+  A (q, 3);       // { dg-warning "-Warray-bounds" }
+}
+
+NOIPA void p3_p1 (int i)
+{
+  int *p3 = a4 + 3;
+  int *p1 = a4 + 1;
+  int *q = i ? p1 : p3;
+  A (q, -4);      // { dg-warning "-Warray-bounds" }
+  A (q, -3); A (q, -2); A (q, -1); A (q, 0);
+  A (q, 1);       // { dg-warning "-Warray-bounds" "pr??????" { xfail *-*-* } }
+  A (q, 2);       // { dg-warning "-Warray-bounds" "pr??????" { xfail *-*-* } }
+  A (q, 3);       // { dg-warning "-Warray-bounds" }
+}
+
+
+NOIPA void p1_p4 (int i)
+{
+  int *p1 = a4 + 1;
+  int *p4 = a4 + 4;
+  int *q = i ? p1 : p4;
+  A (q, -5);      // { dg-warning "-Warray-bounds" }
+  A (q, -4); A (q, -3); A (q, -2); A (q, -1);
+  A (q, 0);       // { dg-warning "-Warray-bounds" "pr??????" { xfail *-*-* } }
+  A (q, 1);       // { dg-warning "-Warray-bounds" "pr??????" { xfail *-*-* } }
+  A (q, 2);       // { dg-warning "-Warray-bounds" "pr??????" { xfail *-*-* } }
+  A (q, 3);       // { dg-warning "-Warray-bounds" }
+}
+
+NOIPA void p4_p1 (int i)
+{
+  int *p4 = a4 + 4;
+  int *p1 = a4 + 1;
+  int *q = i ? p1 : p4;
+  A (q, -5);      // { dg-warning "-Warray-bounds" }
+  A (q, -4); A (q, -3); A (q, -2); A (q, -1);
+  A (q, 0);       // { dg-warning "-Warray-bounds" "pr??????" { xfail *-*-* } }
+  A (q, 1);       // { dg-warning "-Warray-bounds" "pr??????" { xfail *-*-* } }
+  A (q, 2);       // { dg-warning "-Warray-bounds" "pr??????" { xfail *-*-* } }
+  A (q, 3);       // { dg-warning "-Warray-bounds" }
+}
+
+
+NOIPA void p0_p1_p2 (int i)
+{
+  int *p0 = a4 + 0;
+  int *p1 = a4 + 1;
+  int *p2 = a4 + 2;
+  int *q = i < 0 ? p1 : 0 < i ? p2 : p0;
+  A (q, -3);      // { dg-warning "-Warray-bounds" }
+  A (q, -2); A (q, -1); A (q, 0); A (q, 1);
+  A (q, 2);       // { dg-warning "-Warray-bounds" "pr??????" { xfail *-*-* } }
+  A (q, 3);       // { dg-warning "-Warray-bounds" "pr??????" { xfail *-*-* } }
+  A (q, 4);       // { dg-warning "-Warray-bounds" }
+}
+
+
+NOIPA void p0_p1_p2_p3_p4 (int i)
+{
+  int *p0 = a4 + 0;
+  int *p1 = a4 + 1;
+  int *p2 = a4 + 2;
+  int *p3 = a4 + 3;
+  int *p4 = a4 + 4;
+  int *q = i < -1 ? p1 : i < 0 ? p2 : 1 < i ? p4 : 0 < i ? p3 : p0;
+  A (q, -5);      // { dg-warning "-Warray-bounds" }
+  A (q, -4); A (q, -3); A (q, -2); A (q, -1);
+  A (q, 0);       // { dg-warning "-Warray-bounds" "pr??????" { xfail *-*-* } }
+  A (q, 1);       // { dg-warning "-Warray-bounds" "pr??????" { xfail *-*-* } }
+  A (q, 2);       // { dg-warning "-Warray-bounds" "pr??????" { xfail *-*-* } }
+  A (q, 3);       // { dg-warning "-Warray-bounds" "pr??????" { xfail *-*-* } }
+  A (q, 4);       // { dg-warning "-Warray-bounds" }
+}
diff --git a/gcc/testsuite/gcc.dg/Warray-bounds-92.c b/gcc/testsuite/gcc.dg/Warray-bounds-92.c
new file mode 100644
index 00000000000..8c8f5f7f459
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Warray-bounds-92.c
@@ -0,0 +1,149 @@
+/* PR middle-end/103215 - bogus -Warray-bounds with two pointers with
+   different offsets each
+   Test for accesses into distinct arrays through pointers with different
+   offsets each.
+
+   If/when -Warray-bounds is enhanced to issue "maybe" kinds of warnings
+   some of the accesses here will trigger those and will need updating.
+
+   { dg-do compile }
+   { dg-options "-O2 -Wall -ftrack-macro-expansion=0" } */
+
+#define NOIPA __attribute__ ((noipa))
+
+#define A(p, off) ((p)[off] = __COUNTER__)
+
+extern int a4[4], a8[8];
+
+
+NOIPA void a4_p1_a8_p3 (int i)
+{
+  int *a4_p1 = a4 + 1;
+  int *a8_p3 = a8 + 3;
+  int *q = i ? a4_p1 : a8_p3;
+  A (q, -4);      // { dg-warning "-Warray-bounds" }
+  /* Because -3 is a valid offset into a8 but not a4, q must point
+     to the former and so subscripts between -3 and +4 refer to its
+     elements.  */
+  A (q, -3); A (q, -2); A (q, -1); A (q, 0);
+  A (q,  1); A (q,  2); A (q,  3); A (q, 4);
+  A (q, 5);       // { dg-warning "-Warray-bounds" "pr??????" { xfail *-*-* } }
+  /* Both of the following are definitely out of bounds but the first isn't
+     diagnosed because the code conservatively merges the offsets into A4
+     and A8.  */
+  A (q, 7);       // { dg-warning "-Warray-bounds" }
+}
+
+
+NOIPA void a4_p1_a8_p5 (int i)
+{
+  int *a4_p1 = a4 + 1;
+  int *a8_p5 = a8 + 5;
+  int *q = i ? a4_p1 : a8_p5;
+  A (q, -6);     // { dg-warning "-Warray-bounds" }
+  /* Similarly to the above, because -5 is a valid offset into a8 but
+     not a4, q must point to the former and so subscripts between -5
+     and +2 refer to its elements.  */
+  A (q, -5); A (q, -4); A (q, -3); A (q, -2);
+  A (q, -1); A (q,  0); A (q,  1); A (q,  2);
+  A (q, 3);       // { dg-warning "-Warray-bounds" "pr??????" { xfail *-*-* } }
+  A (q, 7);       // { dg-warning "-Warray-bounds" }
+}
+
+
+NOIPA void a4_p1_a8_p7 (int i)
+{
+  int *a4_p1 = a4 + 1;
+  int *a8_p7 = a8 + 7;
+  int *q = i ? a4_p1 : a8_p7;
+  A (q, -8);     // { dg-warning "-Warray-bounds" }
+  A (q, -7); A (q, -6); A (q, -5); A (q, -4);
+  A (q, -3); A (q, -2); A (q, -1); A (q,  0);
+  /* Since -7 is valid, q must point to a8 and the last valid subscript
+     must be 0.  */
+  A (q, 1);       // { dg-warning "-Warray-bounds" "pr??????" { xfail *-*-* } }
+  A (q, 2);       // { dg-warning "-Warray-bounds" "pr??????" { xfail *-*-* } }
+  A (q, 3);       // { dg-warning "-Warray-bounds" "pr??????" { xfail *-*-* } }
+  A (q, 7);       // { dg-warning "-Warray-bounds" }
+}
+
+
+NOIPA void mp_1_a4_p1_a8_p7 (int i, int j)
+{
+  int *a4_p1 = a4 + 1;
+  int *a8_p7 = a8 + 7;
+  int *p = i ? a4_p1 : a8_p7;
+  int *q = j ? p + 1 : p - 1;
+
+  A (q, -9);      // { dg-warning "-Warray-bounds" }
+
+  /* q points either to a8 + [6, 8] or a4 + [0, 2].  */
+  A (q, -8); A (q, -7); A (q, -6); A (q, -5);
+  A (q, -4); A (q, -3); A (q, -2); A (q, -1);
+
+  /* Since all the above are valid, q must point to a8 + 8. But as
+     mentioned above, the warning for each subscript is independent
+     of prior subscripts into the same object so the access below
+     aren't diagnosed.  */
+  A (q, 0);       // { dg-warning "-Warray-bounds" "pr??????" { xfail *-*-* } }
+  A (q, 1);       // { dg-warning "-Warray-bounds" "pr??????" { xfail *-*-* } }
+  A (q, 2);       // { dg-warning "-Warray-bounds" "pr??????" { xfail *-*-* } }
+  A (q, 3);       // { dg-warning "-Warray-bounds" "pr??????" { xfail *-*-* } }
+  A (q, 4);       // { dg-warning "-Warray-bounds" "pr??????" { xfail *-*-* } }
+  A (q, 8);       // { dg-warning "-Warray-bounds" }
+}
+
+
+NOIPA void mp1_a4_p1_a8_p5 (int i, int j)
+{
+  int *a4_p1 = a4 + 1;
+  int *a8_p5 = a8 + 5;
+  int *p = i ? a4_p1 : a8_p5;
+
+  int *q = j ? p + 1 : p - 1;
+
+  // q is assumed to point to a8 + 6
+  A (q, -7);      // { dg-warning "-Warray-bounds" }
+  A (q, -6); A (q, -5); A (q, -4); A (q, -3);
+  A (q, -2); A (q, -1); A (q,  0); A (q,  1);
+  /* Even though the above accesses rule it out, q is now assumed
+     to point to either a4 + [0, 2] or a8 + [4, 5].  */
+  A (q, 2);
+  /* q is now assumed to point tp a4.  Given that, only the first store
+     is valid.  */
+  A (q, 3);       // { dg-warning "-Warray-bounds" "pr??????" { xfail *-*-* } }
+  A (q, 4);       // { dg-warning "-Warray-bounds" "pr??????" { xfail *-*-* } }
+  A (q, 5);       // { dg-warning "-Warray-bounds" "pr??????" { xfail *-*-* } }
+  A (q, 6);       // { dg-warning "-Warray-bounds" "pr??????" { xfail *-*-* } }
+  A (q, 7);       // { dg-warning "-Warray-bounds" "pr??????" { xfail *-*-* } }
+  A (q, 8);       // { dg-warning "-Warray-bounds" }
+}
+
+
+NOIPA void mp1_a4_p1_a8_p4 (int i, int j)
+{
+  int *a4_p1 = a4 + 1;
+  int *a8_p4 = a8 + 4;
+  int *p = i ? a4_p1 : a8_p4;
+
+  int *q = j ? p + 1 : p - 1;
+
+  // q is assumed to point to a8 + 5
+  A (q, -6);      // { dg-warning "-Warray-bounds" }
+  A (q, -5);
+  A (q, -4);
+  A (q, -3);
+  A (q, -2);
+  A (q, -1);
+  A (q,  0);
+  A (q,  1);
+  A (q,  2);
+  /* Even though the above accesses rule it out, q is now assumed
+     to point tp a4.  Given that, only the first store is valid.  */
+  A (q,  3);      // { dg-warning "-Warray-bounds" "pr??????" { xfail *-*-* } }
+  A (q,  4);      // { dg-warning "-Warray-bounds" "pr??????" { xfail *-*-* } }
+  A (q,  5);      // { dg-warning "-Warray-bounds" "pr??????" { xfail *-*-* } }
+  A (q,  6);      // { dg-warning "-Warray-bounds" "pr??????" { xfail *-*-* } }
+  A (q,  7);      // { dg-warning "-Warray-bounds" "pr??????" { xfail *-*-* } }
+  A (q,  8);      // { dg-warning "-Warray-bounds" }
+}
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-58.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-58.c
index b81186cfb94..e0a40788f0d 100644
--- a/gcc/testsuite/gcc.dg/Wstringop-overflow-58.c
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-58.c
@@ -182,8 +182,8 @@ void memset_decl_2_off (void)
   int i2 = SR (2, INT_MAX);
 
   {
-    char a5[5];               // { dg-warning "at offset [1, 5] into destination object 'a5'
-    char a7[7];               // { dg-warning "at offset [2, 7] into destination object 'a7'
+    char a5[5];               // { dg-message "at offset \\\[1, 5] into destination object 'a5'" "note" }
+    char a7[7];               // { dg-message "at offset \\\[2, 7] into destination object 'a7'" "note" }
     char *p5_p1 = a5 + i1;
     char *p7_p2 = a7 + i2;
     char *p5_7 = cond1 ? p5_p1 : p7_p2;
@@ -193,7 +193,11 @@ void memset_decl_2_off (void)
     memset (p5_7, 0, 3);
     memset (p5_7, 0, 4);
     memset (p5_7, 0, 5);
-    memset (p5_7, 0, 6);      // { dg-warning "memset' writing 6 bytes into a region of size 5 " }
+    /* The warning code conservatively "merges" both the sizes and the offsets
+       into A5 and A7 and so only the second store below is diagnosed but not
+       the first.  See PR 103215.  The logic needs to be tightened up.  */
+    memset (p5_7, 0, 6);      // { dg-warning "memset' writing 6 bytes into a region of size 5 " "pr??????" { xfail *-*-* } }
+    memset (p5_7, 0, 7);      // { dg-warning "memset' writing 7 bytes into a region of size 6 " }
   }
 
   int i3 = SR (3, INT_MAX);
@@ -208,7 +212,8 @@ void memset_decl_2_off (void)
     // { dg-message "at offset \\\[4, 9] into destination object 'a9'" "note" { target *-*-* } .-1 }
     // { dg-message "at offset \\\[3, 9] into destination object 'a9'" "note" { target *-*-* } .-2 }
     // { dg-message "at offset \\\[2, 9] into destination object 'a9'" "note" { target *-*-* } .-3 }
-    // { dg-message ": destination object 'a9'" "note" { target *-*-* } .-4 }
+    // { dg-message "at offset \\\[1, 9] into destination object 'a9'" "note" { target *-*-* } .-4 }
+    // { dg-message ": destination object 'a9'" "pr??????" { xfail *-*-* } .-5 }
     char *p5_p2 = a5 + i2;    // 3 bytes left
     char *p9_p3 = a9 + i3;    // 6 bytes left
     char *p =
@@ -220,7 +225,8 @@ void memset_decl_2_off (void)
     memset (q, 0, 3);
     memset (q, 0, 4);
     memset (q, 0, 5);
-    memset (q, 0, 6);         // { dg-warning "memset' writing 6 bytes into a region of size 5" }
+    memset (q, 0, 6);         // { dg-warning "memset' writing 6 bytes into a region of size 5" "pr??????" { xfail *-*-* } }
+    memset (q, 0, 7);         // { dg-warning "memset' writing 7 bytes into a region of size 6" }
 
     --q;                      // [3 - 6] bytes left
     memset (q, 0, 1);
@@ -229,7 +235,8 @@ void memset_decl_2_off (void)
     memset (q, 0, 4);
     memset (q, 0, 5);
     memset (q, 0, 6);
-    memset (q, 0, 7);         // { dg-warning "memset' writing 7 bytes into a region of size 6" }
+    memset (q, 0, 7);         // { dg-warning "memset' writing 7 bytes into a region of size 6" "pr??????" { xfail *-*-* } }
+    memset (q, 0, 8);         // { dg-warning "memset' writing 8 bytes into a region of size 7" }
 
     --q;                      // [4 - 7] bytes left
     memset (q, 0, 1);
@@ -239,7 +246,8 @@ void memset_decl_2_off (void)
     memset (q, 0, 5);
     memset (q, 0, 6);
     memset (q, 0, 7);
-    memset (q, 0, 8);         // { dg-warning "memset' writing 8 bytes into a region of size 7" }
+    memset (q, 0, 8);         // { dg-warning "memset' writing 8 bytes into a region of size 7" "pr??????" { xfail *-*-* } }
+    memset (q, 0, 9);         // { dg-warning "memset' writing 9 bytes into a region of size 8" }
 
     int m1_x = SR (-1, INT_MAX);
     int m2_x = SR (-2, INT_MAX);
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-59.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-59.c
index c45a92d21e1..b6265e37c86 100644
--- a/gcc/testsuite/gcc.dg/Wstringop-overflow-59.c
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-59.c
@@ -200,7 +200,11 @@ void memset_malloc_2_off (void)
     memset (p5_7, 0, 3);
     memset (p5_7, 0, 4);
     memset (p5_7, 0, 5);
-    memset (p5_7, 0, 6);      // { dg-warning "memset' writing 6 bytes into a region of size 5 " }
+    /* The warning code conservatively "merges" both the sizes and the offsets
+       into A5 and A7 and so only the second store below is diagnosed but not
+       the first.  See PR 103215.  The logic needs to be tightened up.  */
+    memset (p5_7, 0, 6);      // { dg-warning "memset' writing 6 bytes into a region of size 5 " "pr??????" { xfail *-*-* } }
+    memset (p5_7, 0, 7);      // { dg-warning "memset' writing 7 bytes into a region of size 6 " }
   }
 
   int i3 = SR (3, INT_MAX);
@@ -215,7 +219,8 @@ void memset_malloc_2_off (void)
     // { dg-message "at offset \\\[4, 9] into destination object 'a9'" "note" { target *-*-* } .-1 }
     // { dg-message "at offset \\\[3, 9] into destination object 'a9'" "note" { target *-*-* } .-2 }
     // { dg-message "at offset \\\[2, 9] into destination object 'a9'" "note" { target *-*-* } .-3 }
-    // { dg-message ": destination object 'a9'" "note" { target *-*-* } .-4 }
+    // { dg-message "at offset \\\[1, 9] into destination object 'a9'" "note" { target *-*-* } .-4 }
+    // { dg-message ": destination object 'a9'" "pr??????" { xfail *-*-* } .-5 }
     char *p5_p2 = a5 + i2;    // 3 bytes left
     char *p9_p3 = a9 + i3;    // 6 bytes left
     char *p =
@@ -227,7 +232,8 @@ void memset_malloc_2_off (void)
     memset (q, 0, 3);
     memset (q, 0, 4);
     memset (q, 0, 5);
-    memset (q, 0, 6);         // { dg-warning "memset' writing 6 bytes into a region of size 5" }
+    memset (q, 0, 6);         // { dg-warning "memset' writing 6 bytes into a region of size 5" "pr??????" { xfail *-*-* } }
+    memset (q, 0, 7);         // { dg-warning "memset' writing 7 bytes into a region of size 6" }
 
     --q;                      // [3 - 6] bytes left
     memset (q, 0, 1);
@@ -236,7 +242,8 @@ void memset_malloc_2_off (void)
     memset (q, 0, 4);
     memset (q, 0, 5);
     memset (q, 0, 6);
-    memset (q, 0, 7);         // { dg-warning "memset' writing 7 bytes into a region of size 6" }
+    memset (q, 0, 7);         // { dg-warning "memset' writing 7 bytes into a region of size 6" "pr??????" { xfail *-*-* } }
+    memset (q, 0, 8);         // { dg-warning "memset' writing 8 bytes into a region of size 7" }
 
     --q;                      // [4 - 7] bytes left
     memset (q, 0, 1);
@@ -246,7 +253,8 @@ void memset_malloc_2_off (void)
     memset (q, 0, 5);
     memset (q, 0, 6);
     memset (q, 0, 7);
-    memset (q, 0, 8);         // { dg-warning "memset' writing 8 bytes into a region of size 7" }
+    memset (q, 0, 8);         // { dg-warning "memset' writing 8 bytes into a region of size 7" "pr??????" { xfail *-*-* } }
+    memset (q, 0, 9);         // { dg-warning "memset' writing 9 bytes into a region of size 8" }
 
     int m1_x = SR (-1, INT_MAX);
     int m2_x = SR (-2, INT_MAX);
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-83.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-83.c
new file mode 100644
index 00000000000..bc121928f1b
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-83.c
@@ -0,0 +1,147 @@
+/* PR middle-end/103215 - bogus -Warray-bounds with two pointers with
+   different offsets each
+   Test for accesses by a user-defined function into the same array
+   through pointers with different offsets each.  See Warray-bounds-91.c
+   for the corresponding test exercising -Warray-bounds for direct accesses.
+   { dg-do compile }
+   { dg-options "-O2 -Wall -ftrack-macro-expansion=0" } */
+
+#define NOIPA __attribute__ ((noipa))
+
+void sink (int[1]);
+#define A(p, off) sink (p + off)
+
+extern int a4[4];
+
+
+NOIPA void p0_p1 (int i)
+{
+  int *p0 = a4 + 0;
+  int *p1 = a4 + 1;
+  int *q = i ? p0 : p1;
+  A (q, -2);      // { dg-warning "-Wstringop-overflow" }
+  A (q, -1); A (q, 0); A (q, 1); A (q, 2);
+  /* Since q points to a4 and -1 is a valid subscript, +3 must be invalid.
+     But the warning for each subscript is independent of prior subscripts
+     into the same object.  That should be improved.  */
+  A (q, 3);       // { dg-warning "-Wstringop-overflow" "pr??????" { xfail *-*-* } }
+  A (q, 4);       // { dg-warning "-Wstringop-overflow" }
+}
+
+NOIPA void p1_p0 (int i)
+{
+  int *p1 = a4 + 1;
+  int *p0 = a4 + 0;
+  int *q = i ? p0 : p1;
+  A (q, -2);      // { dg-warning "-Wstringop-overflow" }
+  A (q, -1); A (q, 0); A (q, 1); A (q, 2);
+  A (q, 3);       // { dg-warning "-Wstringop-overflow" "pr??????" { xfail *-*-* } }
+  A (q, 4);       // { dg-warning "-Wstringop-overflow" }
+}
+
+
+NOIPA void p1_p2 (int i)
+{
+  int *p1 = a4 + 1;
+  int *p2 = a4 + 2;
+  int *q = i ? p1 : p2;
+  A (q, -3);      // { dg-warning "-Wstringop-overflow" }
+  A (q, -2); A (q, -1); A (q, 0); A (q, 1);
+  A (q, 2);       // { dg-warning "-Wstringop-overflow" "pr??????" { xfail *-*-* } }
+  A (q, 3);       // { dg-warning "-Wstringop-overflow" }
+}
+
+NOIPA void p2_p1 (int i)
+{
+  int *p2 = a4 + 2;
+  int *p1 = a4 + 1;
+  int *q = i ? p1 : p2;
+  A (q, -3);      // { dg-warning "-Wstringop-overflow" }
+  A (q, -2); A (q, -1); A (q, 0); A (q, 1);
+  A (q, 2);       // { dg-warning "-Wstringop-overflow" "pr??????" { xfail *-*-* } }
+  A (q, 3);       // { dg-warning "-Wstringop-overflow" }
+}
+
+
+NOIPA void p1_p3 (int i)
+{
+  int *p1 = a4 + 1;
+  int *p3 = a4 + 3;
+  int *q = i ? p1 : p3;
+  A (q, -4);      // { dg-warning "-Wstringop-overflow" }
+  A (q, -3); A (q, -2); A (q, -1); A (q, 0);
+  A (q, 1);       // { dg-warning "-Wstringop-overflow" "pr??????" { xfail *-*-* } }
+  A (q, 2);       // { dg-warning "-Wstringop-overflow" "pr??????" { xfail *-*-* } }
+  A (q, 3);       // { dg-warning "-Wstringop-overflow" }
+}
+
+NOIPA void p3_p1 (int i)
+{
+  int *p3 = a4 + 3;
+  int *p1 = a4 + 1;
+  int *q = i ? p1 : p3;
+  A (q, -4);      // { dg-warning "-Wstringop-overflow" }
+  A (q, -3); A (q, -2); A (q, -1); A (q, 0);
+  A (q, 1);       // { dg-warning "-Wstringop-overflow" "pr??????" { xfail *-*-* } }
+  A (q, 2);       // { dg-warning "-Wstringop-overflow" "pr??????" { xfail *-*-* } }
+  A (q, 3);       // { dg-warning "-Wstringop-overflow" }
+}
+
+
+NOIPA void p1_p4 (int i)
+{
+  int *p1 = a4 + 1;
+  int *p4 = a4 + 4;
+  int *q = i ? p1 : p4;
+  A (q, -5);      // { dg-warning "-Wstringop-overflow" }
+  A (q, -4); A (q, -3); A (q, -2); A (q, -1);
+  A (q, 0);       // { dg-warning "-Wstringop-overflow" "pr??????" { xfail *-*-* } }
+  A (q, 1);       // { dg-warning "-Wstringop-overflow" "pr??????" { xfail *-*-* } }
+  A (q, 2);       // { dg-warning "-Wstringop-overflow" "pr??????" { xfail *-*-* } }
+  A (q, 3);       // { dg-warning "-Wstringop-overflow" }
+}
+
+NOIPA void p4_p1 (int i)
+{
+  int *p4 = a4 + 4;
+  int *p1 = a4 + 1;
+  int *q = i ? p1 : p4;
+  A (q, -5);      // { dg-warning "-Wstringop-overflow" }
+  A (q, -4); A (q, -3); A (q, -2); A (q, -1);
+  A (q, 0);       // { dg-warning "-Wstringop-overflow" "pr??????" { xfail *-*-* } }
+  A (q, 1);       // { dg-warning "-Wstringop-overflow" "pr??????" { xfail *-*-* } }
+  A (q, 2);       // { dg-warning "-Wstringop-overflow" "pr??????" { xfail *-*-* } }
+  A (q, 3);       // { dg-warning "-Wstringop-overflow" }
+}
+
+
+NOIPA void p0_p1_p2 (int i)
+{
+  int *p0 = a4 + 0;
+  int *p1 = a4 + 1;
+  int *p2 = a4 + 2;
+  int *q = i < 0 ? p1 : 0 < i ? p2 : p0;
+  A (q, -3);      // { dg-warning "-Wstringop-overflow" }
+  A (q, -2); A (q, -1); A (q, 0); A (q, 1);
+  A (q, 2);       // { dg-warning "-Wstringop-overflow" "pr??????" { xfail *-*-* } }
+  A (q, 3);       // { dg-warning "-Wstringop-overflow" "pr??????" { xfail *-*-* } }
+  A (q, 4);       // { dg-warning "-Wstringop-overflow" }
+}
+
+
+NOIPA void p0_p1_p2_p3_p4 (int i)
+{
+  int *p0 = a4 + 0;
+  int *p1 = a4 + 1;
+  int *p2 = a4 + 2;
+  int *p3 = a4 + 3;
+  int *p4 = a4 + 4;
+  int *q = i < -1 ? p1 : i < 0 ? p2 : 1 < i ? p4 : 0 < i ? p3 : p0;
+  A (q, -5);      // { dg-warning "-Wstringop-overflow" }
+  A (q, -4); A (q, -3); A (q, -2); A (q, -1);
+  A (q, 0);       // { dg-warning "-Wstringop-overflow" "pr??????" { xfail *-*-* } }
+  A (q, 1);       // { dg-warning "-Wstringop-overflow" "pr??????" { xfail *-*-* } }
+  A (q, 2);       // { dg-warning "-Wstringop-overflow" "pr??????" { xfail *-*-* } }
+  A (q, 3);       // { dg-warning "-Wstringop-overflow" "pr??????" { xfail *-*-* } }
+  A (q, 4);       // { dg-warning "-Wstringop-overflow" }
+}
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-85.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-85.c
new file mode 100644
index 00000000000..ac61e0a0a64
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-85.c
@@ -0,0 +1,153 @@
+/* PR middle-end/103215 - bogus -Wstringop-overflow with two pointers with
+   different offsets each
+   Test for accesses into distinct arrays through pointers with different
+   offsets each.
+
+   If/when -Wstringop-overflow is enhanced to issue "maybe" kinds of
+   warnings some of the accesses here will trigger those and will need
+   updating.
+
+   { dg-do compile }
+   { dg-options "-O2 -Wall -ftrack-macro-expansion=0" } */
+
+#define NOIPA __attribute__ ((noipa))
+
+void sink (int[1]);
+#define A(p, off) sink (p + off)
+
+extern int a4[4], a8[8];
+
+
+
+
+NOIPA void a4_p1_a8_p3 (int i)
+{
+  int *a4_p1 = a4 + 1;
+  int *a8_p3 = a8 + 3;
+  int *q = i ? a4_p1 : a8_p3;
+  A (q, -4);      // { dg-warning "-Wstringop-overflow" }
+  /* Because -3 is a valid offset into a8 but not a4, q must point
+     to the former and so subscripts between -3 and +4 refer to its
+     elements.  */
+  A (q, -3); A (q, -2); A (q, -1); A (q, 0);
+  A (q,  1); A (q,  2); A (q,  3); A (q, 4);
+  A (q, 5);       // { dg-warning "-Wstringop-overflow" "pr??????" { xfail *-*-* } }
+  /* Both of the following are definitely out of bounds but the first isn't
+     diagnosed because the code conservatively merges the offsets into A4
+     and A8.  */
+  A (q, 7);       // { dg-warning "-Wstringop-overflow" }
+}
+
+
+NOIPA void a4_p1_a8_p5 (int i)
+{
+  int *a4_p1 = a4 + 1;
+  int *a8_p5 = a8 + 5;
+  int *q = i ? a4_p1 : a8_p5;
+  A (q, -6);     // { dg-warning "-Wstringop-overflow" }
+  /* Similarly to the above, because -5 is a valid offset into a8 but
+     not a4, q must point to the former and so subscripts between -5
+     and +2 refer to its elements.  */
+  A (q, -5); A (q, -4); A (q, -3); A (q, -2);
+  A (q, -1); A (q,  0); A (q,  1); A (q,  2);
+  A (q, 3);       // { dg-warning "-Wstringop-overflow" "pr??????" { xfail *-*-* } }
+  A (q, 7);       // { dg-warning "-Wstringop-overflow" }
+}
+
+
+NOIPA void a4_p1_a8_p7 (int i)
+{
+  int *a4_p1 = a4 + 1;
+  int *a8_p7 = a8 + 7;
+  int *q = i ? a4_p1 : a8_p7;
+  A (q, -8);     // { dg-warning "-Wstringop-overflow" }
+  A (q, -7); A (q, -6); A (q, -5); A (q, -4);
+  A (q, -3); A (q, -2); A (q, -1); A (q,  0);
+  /* Since -7 is valid, q must point to a8 and the last valid subscript
+     must be 0.  */
+  A (q, 1);       // { dg-warning "-Wstringop-overflow" "pr??????" { xfail *-*-* } }
+  A (q, 2);       // { dg-warning "-Wstringop-overflow" "pr??????" { xfail *-*-* } }
+  A (q, 3);       // { dg-warning "-Wstringop-overflow" "pr??????" { xfail *-*-* } }
+  A (q, 7);       // { dg-warning "-Wstringop-overflow" }
+}
+
+
+NOIPA void mp_1_a4_p1_a8_p7 (int i, int j)
+{
+  int *a4_p1 = a4 + 1;
+  int *a8_p7 = a8 + 7;
+  int *p = i ? a4_p1 : a8_p7;
+  int *q = j ? p + 1 : p - 1;
+
+  A (q, -9);      // { dg-warning "-Wstringop-overflow" }
+
+  /* q points either to a8 + [6, 8] or a4 + [0, 2].  */
+  A (q, -8); A (q, -7); A (q, -6); A (q, -5);
+  A (q, -4); A (q, -3); A (q, -2); A (q, -1);
+
+  /* Since all the above are valid, q must point to a8 + 8. But as
+     mentioned above, the warning for each subscript is independent
+     of prior subscripts into the same object so the access below
+     aren't diagnosed.  */
+  A (q, 0);       // { dg-warning "-Wstringop-overflow" "pr??????" { xfail *-*-* } }
+  A (q, 1);       // { dg-warning "-Wstringop-overflow" "pr??????" { xfail *-*-* } }
+  A (q, 2);       // { dg-warning "-Wstringop-overflow" "pr??????" { xfail *-*-* } }
+  A (q, 3);       // { dg-warning "-Wstringop-overflow" "pr??????" { xfail *-*-* } }
+  A (q, 4);       // { dg-warning "-Wstringop-overflow" "pr??????" { xfail *-*-* } }
+  A (q, 8);       // { dg-warning "-Wstringop-overflow" }
+}
+
+
+NOIPA void mp1_a4_p1_a8_p5 (int i, int j)
+{
+  int *a4_p1 = a4 + 1;
+  int *a8_p5 = a8 + 5;
+  int *p = i ? a4_p1 : a8_p5;
+
+  int *q = j ? p + 1 : p - 1;
+
+  // q is assumed to point to a8 + 6
+  A (q, -7);      // { dg-warning "-Wstringop-overflow" }
+  A (q, -6); A (q, -5); A (q, -4); A (q, -3);
+  A (q, -2); A (q, -1); A (q,  0); A (q,  1);
+  /* Even though the above accesses rule it out, q is now assumed
+     to point to either a4 + [0, 2] or a8 + [4, 5].  */
+  A (q, 2);
+  /* q is now assumed to point tp a4.  Given that, only the first store
+     is valid.  */
+  A (q, 3);       // { dg-warning "-Wstringop-overflow" "pr??????" { xfail *-*-* } }
+  A (q, 4);       // { dg-warning "-Wstringop-overflow" "pr??????" { xfail *-*-* } }
+  A (q, 5);       // { dg-warning "-Wstringop-overflow" "pr??????" { xfail *-*-* } }
+  A (q, 6);       // { dg-warning "-Wstringop-overflow" "pr??????" { xfail *-*-* } }
+  A (q, 7);       // { dg-warning "-Wstringop-overflow" "pr??????" { xfail *-*-* } }
+  A (q, 8);       // { dg-warning "-Wstringop-overflow" }
+}
+
+
+NOIPA void mp1_a4_p1_a8_p4 (int i, int j)
+{
+  int *a4_p1 = a4 + 1;
+  int *a8_p4 = a8 + 4;
+  int *p = i ? a4_p1 : a8_p4;
+
+  int *q = j ? p + 1 : p - 1;
+
+  // q is assumed to point to a8 + 5
+  A (q, -6);      // { dg-warning "-Wstringop-overflow" }
+  A (q, -5);
+  A (q, -4);
+  A (q, -3);
+  A (q, -2);
+  A (q, -1);
+  A (q,  0);
+  A (q,  1);
+  A (q,  2);
+  /* Even though the above accesses rule it out, q is now assumed
+     to point tp a4.  Given that, only the first store is valid.  */
+  A (q,  3);      // { dg-warning "-Wstringop-overflow" "pr??????" { xfail *-*-* } }
+  A (q,  4);      // { dg-warning "-Wstringop-overflow" "pr??????" { xfail *-*-* } }
+  A (q,  5);      // { dg-warning "-Wstringop-overflow" "pr??????" { xfail *-*-* } }
+  A (q,  6);      // { dg-warning "-Wstringop-overflow" "pr??????" { xfail *-*-* } }
+  A (q,  7);      // { dg-warning "-Wstringop-overflow" "pr??????" { xfail *-*-* } }
+  A (q,  8);      // { dg-warning "-Wstringop-overflow" }
+}
diff --git a/gcc/testsuite/gcc.dg/warn-strnlen-no-nul.c b/gcc/testsuite/gcc.dg/warn-strnlen-no-nul.c
index 846e9300750..4ce70d036b7 100644
--- a/gcc/testsuite/gcc.dg/warn-strnlen-no-nul.c
+++ b/gcc/testsuite/gcc.dg/warn-strnlen-no-nul.c
@@ -143,17 +143,18 @@ T (v0 ? b[1] : "", bsz);
 T (v0 ? b[2] : "", bsz);
 T (v0 ? b[3] : "", bsz);
 
-/* The warnings below are strictly correct but the strnlen calls are safe
-   because the reads are bounded by the length of the constant arguments.
-   It might make sense to relax the warning to avoid triggering for them.  */
+/* Warning for the calls below would be strictly correct even though
+   the strnlen calls are safe because the reads are bounded by
+   the length of the constant arguments.  Most of the calls are
+   not diagnosed anymore as a result of the fix for PR 103215.  */
 T (v0 ? "" : b[0], bsz + 1);      /* { dg-warning "bound 6 exceeds source size 5" } */
-T (v0 ? "" : b[1], bsz + 1);      /* { dg-warning "bound 6 exceeds source size 5" } */
-T (v0 ? "" : b[2], bsz + 1);      /* { dg-warning "bound 6 exceeds source size 5" } */
-T (v0 ? "" : b[3], bsz + 1);      /* { dg-warning "bound 6 exceeds source size 5" } */
+T (v0 ? "" : b[1], bsz + 1);
+T (v0 ? "" : b[2], bsz + 1);
+T (v0 ? "" : b[3], bsz + 1);
 T (v0 ? b[0] : "", bsz + 1);      /* { dg-warning "bound 6 exceeds source size 5" } */
-T (v0 ? b[1] : "", bsz + 1);      /* { dg-warning "bound 6 exceeds source size 5" } */
-T (v0 ? b[2] : "", bsz + 1);      /* { dg-warning "bound 6 exceeds source size 5" } */
-T (v0 ? b[3] : "", bsz + 1);      /* { dg-warning "bound 6 exceeds source size 5" } */
+T (v0 ? b[1] : "", bsz + 1);
+T (v0 ? b[2] : "", bsz + 1);
+T (v0 ? b[3] : "", bsz + 1);
 
 T (v0 ? "" : b[i0], bsz);
 T (v0 ? "" : b[i1], bsz);
@@ -167,11 +168,11 @@ T (v0 ? b[i3] : "", bsz);
 T (v0 ? "" : b[i0], bsz + 1);
 T (v0 ? "" : b[i1], bsz + 1);
 T (v0 ? "" : b[i2], bsz + 1);
-T (v0 ? "" : b[i3], bsz + 1);     /* { dg-warning "bound 6 exceeds source size 5" "pr86937" } */
+T (v0 ? "" : b[i3], bsz + 1);
 T (v0 ? b[i0] : "", bsz + 1);
 T (v0 ? b[i1] : "", bsz + 1);
 T (v0 ? b[i2] : "", bsz + 1);
-T (v0 ? b[i3] : "", bsz + 1);     /* { dg-warning "bound 6 exceeds source size 5" "pr86937" } */
+T (v0 ? b[i3] : "", bsz + 1);
 
 T (v0 ? "1234" : b[3], bsz);
 T (v0 ? "1234" : b[i3], bsz);
@@ -183,14 +184,16 @@ T (v0 ? b[0] : b[2], bsz);
 T (v0 ? b[2] : b[3], bsz);
 T (v0 ? b[3] : b[2], bsz);
 
-T (v0 ? "1234" : b[3], bsz + 1);  /* { dg-warning "bound 6 exceeds source size 5" } */
-T (v0 ? "1234" : b[i3], bsz + 1); /* { dg-warning "bound 6 exceeds source size 5" } */
-T (v0 ? b[3] : "1234", bsz + 1);  /* { dg-warning "bound 6 exceeds source size 5" } */
-T (v0 ? b[i3] : "1234", bsz + 1); /* { dg-warning "bound 6 exceeds source size 5" } */
+T (v0 ? "1234" : b[3], bsz + 1);
+T (v0 ? "1234" : b[i3], bsz + 1);
+T (v0 ? b[3] : "1234", bsz + 1);
+T (v0 ? b[i3] : "1234", bsz + 1);
 
-T (v0 ? a : b[3], bsz + 1);       /* { dg-warning "bound 6 exceeds source size 5" } */
-T (v0 ? b[0] : b[2], bsz + 1);    /* { dg-warning "bound 6 exceeds source size 5" } */
-T (v0 ? b[2] : b[3], bsz + 1);    /* { dg-warning "bound 6 exceeds source size 5" } */
+/* That the following are not diagnosed is a bug/limitation resulting from
+   the fix for PR 103215.  */
+T (v0 ? a : b[3], bsz + 1);       /* { dg-warning "bound 6 exceeds source size 5" "pr??????" { xfail *-*-* } } */
+T (v0 ? b[0] : b[2], bsz + 1);    /* { dg-warning "bound 6 exceeds source size 5" "pr??????" { xfail *-*-* } } */
+T (v0 ? b[2] : b[3], bsz + 1);    /* { dg-warning "bound 6 exceeds source size 5" "pr??????" { xfail *-*-* } } */
 T (v0 ? b[3] : b[2], bsz + 1);    /* { dg-warning "bound 6 exceeds source size 5" } */
 
 struct A { char a[5], b[5]; };
