PR middle-end/84095 - false-positive -Wrestrict warnings for memcpy within array
authorMartin Sebor <msebor@redhat.com>
Tue, 20 Feb 2018 20:22:01 +0000 (20:22 +0000)
committerMartin Sebor <msebor@gcc.gnu.org>
Tue, 20 Feb 2018 20:22:01 +0000 (13:22 -0700)
gcc/ChangeLog:

PR middle-end/84095
* gimple-ssa-warn-restrict.c (builtin_memref::extend_offset_range): New.
(builtin_memref::set_base_and_offset): Same.  Handle inner references.
(builtin_memref::builtin_memref): Factor out parts into
set_base_and_offset and call it.

gcc/testsuite/ChangeLog:

PR middle-end/84095
* c-c++-common/Warray-bounds-3.c: Adjust text of expected warnings.
* c-c++-common/Wrestrict.c: Same.
* gcc.dg/Wrestrict-6.c: Same.
* gcc.dg/Warray-bounds-27.c: New test.
* gcc.dg/Wrestrict-8.c: New test.
* gcc.dg/Wrestrict-9.c: New test.
* gcc.dg/pr84095.c: New test.

From-SVN: r257860

gcc/ChangeLog
gcc/gimple-ssa-warn-restrict.c
gcc/testsuite/ChangeLog
gcc/testsuite/c-c++-common/Warray-bounds-3.c
gcc/testsuite/c-c++-common/Wrestrict.c
gcc/testsuite/gcc.dg/Warray-bounds-27.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/Wrestrict-6.c
gcc/testsuite/gcc.dg/Wrestrict-8.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/Wrestrict-9.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/pr84095.c [new file with mode: 0644]

index b2622df859a2bc02cfe32e3df8427920035707be..8b686d03a14f47855adafcb7227d856a57a341b0 100644 (file)
@@ -1,3 +1,11 @@
+2018-02-20  Martin Sebor  <msebor@redhat.com>
+
+       PR middle-end/84095
+       * gimple-ssa-warn-restrict.c (builtin_memref::extend_offset_range): New.
+       (builtin_memref::set_base_and_offset): Same.  Handle inner references.
+       (builtin_memref::builtin_memref): Factor out parts into
+       set_base_and_offset and call it.
+
 2018-02-20  Richard Sandiford  <richard.sandiford@linaro.org>
 
        PR middle-end/84406
index d0e18541275384f1ac4bf6ce455886389c04ad71..9d5c6e043701cbee9eb0aa7f32658f82f6438326 100644 (file)
@@ -158,6 +158,14 @@ struct builtin_memref
   builtin_memref (tree, tree);
 
   tree offset_out_of_bounds (int, offset_int[2]) const;
+
+private:
+
+  /* Ctor helper to set or extend OFFRANGE based on argument.  */
+  void extend_offset_range (tree);
+
+  /*  Ctor helper to determine BASE and OFFRANGE from argument.  */
+  void set_base_and_offset (tree);
 };
 
 /* Description of a memory access by a raw memory or string built-in
@@ -235,11 +243,120 @@ builtin_memref::builtin_memref (tree expr, tree size)
 
   const offset_int maxobjsize = tree_to_shwi (max_object_size ());
 
+  /* Find the BASE object or pointer referenced by EXPR and set
+     the offset range OFFRANGE in the process.  */
+  set_base_and_offset (expr);
+
+  if (size)
+    {
+      tree range[2];
+      /* Determine the size range, allowing for the result to be [0, 0]
+        for SIZE in the anti-range ~[0, N] where N >= PTRDIFF_MAX.  */
+      get_size_range (size, range, true);
+      sizrange[0] = wi::to_offset (range[0]);
+      sizrange[1] = wi::to_offset (range[1]);
+      /* get_size_range returns SIZE_MAX for the maximum size.
+        Constrain it to the real maximum of PTRDIFF_MAX.  */
+      if (sizrange[1] > maxobjsize)
+       sizrange[1] = maxobjsize;
+    }
+  else
+    sizrange[1] = maxobjsize;
+
+  tree basetype = TREE_TYPE (base);
+  if (DECL_P (base) && TREE_CODE (basetype) == ARRAY_TYPE)
+    {
+      /* If the offset could be in range of the referenced object
+        constrain its bounds so neither exceeds those of the object.  */
+      if (offrange[0] < 0 && offrange[1] > 0)
+       offrange[0] = 0;
+
+      offset_int maxoff = maxobjsize;
+      if (ref && array_at_struct_end_p (ref))
+       ;   /* Use the maximum possible offset for last member arrays.  */
+      else if (tree basesize = TYPE_SIZE_UNIT (basetype))
+       maxoff = wi::to_offset (basesize);
+
+      if (offrange[0] >= 0)
+       {
+         if (offrange[1] < 0)
+           offrange[1] = offrange[0] <= maxoff ? maxoff : maxobjsize;
+         else if (offrange[0] <= maxoff && offrange[1] > maxoff)
+           offrange[1] = maxoff;
+       }
+    }
+}
+
+/* Ctor helper to set or extend OFFRANGE based on the OFFSET argument.  */
+
+void
+builtin_memref::extend_offset_range (tree offset)
+{
+  const offset_int maxobjsize = tree_to_shwi (max_object_size ());
+
+  if (TREE_CODE (offset) == INTEGER_CST)
+    {
+      offset_int off = int_cst_value (offset);
+      if (off != 0)
+       {
+         offrange[0] += off;
+         offrange[1] += off;
+       }
+      return;
+    }
+
+  if (TREE_CODE (offset) == SSA_NAME)
+    {
+      wide_int min, max;
+      value_range_type rng = get_range_info (offset, &min, &max);
+      if (rng == VR_RANGE)
+       {
+         offrange[0] += offset_int::from (min, SIGNED);
+         offrange[1] += offset_int::from (max, SIGNED);
+       }
+      else if (rng == VR_ANTI_RANGE)
+       {
+         offrange[0] += offset_int::from (max + 1, SIGNED);
+         offrange[1] += offset_int::from (min - 1, SIGNED);
+       }
+      else
+       {
+         gimple *stmt = SSA_NAME_DEF_STMT (offset);
+         tree type;
+         if (is_gimple_assign (stmt)
+             && gimple_assign_rhs_code (stmt) == NOP_EXPR
+             && (type = TREE_TYPE (gimple_assign_rhs1 (stmt)))
+             && INTEGRAL_TYPE_P (type))
+           {
+             /* Use the bounds of the type of the NOP_EXPR operand
+                even if it's signed.  The result doesn't trigger
+                warnings but makes their output more readable.  */
+             offrange[0] += wi::to_offset (TYPE_MIN_VALUE (type));
+             offrange[1] += wi::to_offset (TYPE_MAX_VALUE (type));
+           }
+         else
+           offrange[1] += maxobjsize;
+       }
+      return;
+    }
+
+  offrange[1] += maxobjsize;
+}
+
+/* Determines the base object or pointer of the reference EXPR
+   and the offset range from the beginning of the base.  */
+
+void
+builtin_memref::set_base_and_offset (tree expr)
+{
+  const offset_int maxobjsize = tree_to_shwi (max_object_size ());
+
   if (TREE_CODE (expr) == SSA_NAME)
     {
       /* Try to tease the offset out of the pointer.  */
       gimple *stmt = SSA_NAME_DEF_STMT (expr);
-      if (gimple_assign_single_p (stmt)
+      if (!base
+         && gimple_assign_single_p (stmt)
          && gimple_assign_rhs_code (stmt) == ADDR_EXPR)
        expr = gimple_assign_rhs1 (stmt);
       else if (is_gimple_assign (stmt))
@@ -250,154 +367,88 @@ builtin_memref::builtin_memref (tree expr, tree size)
              tree rhs = gimple_assign_rhs1 (stmt);
              if (POINTER_TYPE_P (TREE_TYPE (rhs)))
                expr = gimple_assign_rhs1 (stmt);
+             else
+               {
+                 base = expr;
+                 return;
+               }
            }
          else if (code == POINTER_PLUS_EXPR)
            {
              expr = gimple_assign_rhs1 (stmt);
 
              tree offset = gimple_assign_rhs2 (stmt);
-             if (TREE_CODE (offset) == INTEGER_CST)
-               {
-                 offset_int off = int_cst_value (offset);
-                 offrange[0] = off;
-                 offrange[1] = off;
-
-                 if (TREE_CODE (expr) == SSA_NAME)
-                   {
-                     gimple *stmt = SSA_NAME_DEF_STMT (expr);
-                     if (gimple_assign_single_p (stmt)
-                         && gimple_assign_rhs_code (stmt) == ADDR_EXPR)
-                       expr = gimple_assign_rhs1 (stmt);
-                   }
-               }
-             else if (TREE_CODE (offset) == SSA_NAME)
-               {
-                 wide_int min, max;
-                 value_range_type rng = get_range_info (offset, &min, &max);
-                 if (rng == VR_RANGE)
-                   {
-                     offrange[0] = offset_int::from (min, SIGNED);
-                     offrange[1] = offset_int::from (max, SIGNED);
-                   }
-                 else if (rng == VR_ANTI_RANGE)
-                   {
-                     offrange[0] = offset_int::from (max + 1, SIGNED);
-                     offrange[1] = offset_int::from (min - 1, SIGNED);
-                   }
-                 else
-                   {
-                     gimple *stmt = SSA_NAME_DEF_STMT (offset);
-                     tree type;
-                     if (is_gimple_assign (stmt)
-                         && gimple_assign_rhs_code (stmt) == NOP_EXPR
-                         && (type = TREE_TYPE (gimple_assign_rhs1 (stmt)))
-                         && INTEGRAL_TYPE_P (type))
-                       {
-                         /* Use the bounds of the type of the NOP_EXPR operand
-                            even if it's signed.  The result doesn't trigger
-                            warnings but makes their output more readable.  */
-                         offrange[0] = wi::to_offset (TYPE_MIN_VALUE (type));
-                         offrange[1] = wi::to_offset (TYPE_MAX_VALUE (type));
-                       }
-                     else
-                       offrange[1] = maxobjsize;
-                   }
-               }
-             else
-               offrange[1] = maxobjsize;
+             extend_offset_range (offset);
            }
+         else
+           {
+             base = expr;
+             return;
+           }
+       }
+      else
+       {
+         base = expr;
+         return;
        }
     }
 
   if (TREE_CODE (expr) == ADDR_EXPR)
-    {
-      poly_int64 off;
-      tree op = TREE_OPERAND (expr, 0);
+    expr = TREE_OPERAND (expr, 0);
 
-      /* Determine the base object or pointer of the reference
-        and its constant offset from the beginning of the base.  */
-      base = get_addr_base_and_unit_offset (op, &off);
+  poly_int64 bitsize, bitpos;
+  tree var_off;
+  machine_mode mode;
+  int sign, reverse, vol;
 
-      HOST_WIDE_INT const_off;
-      if (base && off.is_constant (&const_off))
-       {
-         offrange[0] += const_off;
-         offrange[1] += const_off;
+  /* Determine the base object or pointer of the reference and
+     the constant bit offset from the beginning of the base.
+     If the offset has a non-constant component, it will be in
+     VAR_OFF.  MODE, SIGN, REVERSE, and VOL are write only and
+     unused here.  */
+  base = get_inner_reference (expr, &bitsize, &bitpos, &var_off,
+                             &mode, &sign, &reverse, &vol);
 
-         /* Stash the reference for offset validation.  */
-         ref = op;
+  poly_int64 bytepos = exact_div (bitpos, BITS_PER_UNIT);
 
-         /* Also stash the constant offset for offset validation.  */
-         if (TREE_CODE (op) == COMPONENT_REF)
-           refoff = const_off;
-       }
-      else
-       {
-         size = NULL_TREE;
-         base = get_base_address (TREE_OPERAND (expr, 0));
-       }
+  HOST_WIDE_INT const_off;
+  if (!base || !bytepos.is_constant (&const_off))
+    {
+      base = get_base_address (TREE_OPERAND (expr, 0));
+      return;
     }
 
-  if (!base)
-    base = build2 (MEM_REF, char_type_node, expr, null_pointer_node);
+  offrange[0] += const_off;
+  offrange[1] += const_off;
 
-  if (TREE_CODE (base) == MEM_REF)
+  if (var_off)
     {
-      offset_int off;
-      if (mem_ref_offset (base).is_constant (&off))
+      if (TREE_CODE (var_off) == INTEGER_CST)
        {
-         refoff += off;
-         offrange[0] += off;
-         offrange[1] += off;
+         offset_int cstoff = wi::to_offset (var_off);
+         offrange[0] += cstoff;
+         offrange[1] += cstoff;
        }
       else
-       size = NULL_TREE;
-      base = TREE_OPERAND (base, 0);
+       offrange[1] += maxobjsize;
     }
 
-  if (TREE_CODE (base) == SSA_NAME)
-    if (gimple *stmt = SSA_NAME_DEF_STMT (base))
-      {
-       enum gimple_code code = gimple_code (stmt);
-       if (code == GIMPLE_ASSIGN)
-         if (gimple_assign_rhs_code (stmt) == POINTER_PLUS_EXPR)
-           {
-             base = gimple_assign_rhs1 (stmt);
+  /* Stash the reference for offset validation.  */
+  ref = expr;
 
-             tree offset = gimple_assign_rhs2 (stmt);
-             if (TREE_CODE (offset) == INTEGER_CST)
-               {
-                 offset_int off = int_cst_value (offset);
-                 refoff += off;
-                 offrange[0] += off;
-                 offrange[1] += off;
-               }
-           }
-      }
+  /* Also stash the constant offset for offset validation.  */
+  if (TREE_CODE (expr) == COMPONENT_REF)
+    refoff = const_off;
 
-  if (DECL_P (base) && TREE_CODE (TREE_TYPE (base)) == ARRAY_TYPE)
+  if (TREE_CODE (base) == MEM_REF)
     {
-      /* For array objects, where a negative offset wouldn't make
-        sense, use zero instead if the upper bound is positive.  */
-      if (offrange[0] < 0 && offrange[1] > 0)
-       offrange[0] = 0;
+      tree memrefoff = TREE_OPERAND (base, 1);
+      extend_offset_range (memrefoff);
+      base = TREE_OPERAND (base, 0);
     }
 
-  if (size)
-    {
-      tree range[2];
-      /* Determine the size range, allowing for the result to be [0, 0]
-        for SIZE in the anti-range ~[0, N] where N >= PTRDIFF_MAX.  */
-      get_size_range (size, range, true);
-      sizrange[0] = wi::to_offset (range[0]);
-      sizrange[1] = wi::to_offset (range[1]);
-      /* get_size_range returns SIZE_MAX for the maximum size.
-        Constrain it to the real maximum of PTRDIFF_MAX.  */
-      if (sizrange[1] > maxobjsize)
-       sizrange[1] = maxobjsize;
-    }
-  else
-    sizrange[1] = maxobjsize;
+  if (TREE_CODE (base) == SSA_NAME)
+    set_base_and_offset (base);
 }
 
 /* Return error_mark_node if the signed offset exceeds the bounds
@@ -864,10 +915,18 @@ builtin_access::generic_overlap ()
      the other.  */
   bool depends_p = detect_overlap != &builtin_access::generic_overlap;
 
-  if (!overlap_certain
-      && !dstref->strbounded_p
-      && !depends_p)
-    return false;
+  if (!overlap_certain)
+    {
+      if (!dstref->strbounded_p && !depends_p)
+       return false;
+
+      /* There's no way to distinguish an access to the same member
+        of a structure from one to two distinct members of the same
+        structure.  Give up to avoid excessive false positives.  */
+      tree basetype = TREE_TYPE (TREE_TYPE (dstref->base));
+      if (RECORD_OR_UNION_TYPE_P (basetype))
+       return false;
+    }
 
   /* True for stpcpy and strcpy.  */
   bool stxcpy_p = (!dstref->strbounded_p
index a72601690ce1810915bbf2013354aa6ce093a7f6..97983a56a58893ee8792c5c4b1341b34e8922025 100644 (file)
@@ -1,3 +1,14 @@
+2018-02-20  Martin Sebor  <msebor@redhat.com>
+
+       PR middle-end/84095
+       * c-c++-common/Warray-bounds-3.c: Adjust text of expected warnings.
+       * c-c++-common/Wrestrict.c: Same.
+       * gcc.dg/Wrestrict-6.c: Same.
+       * gcc.dg/Warray-bounds-27.c: New test.
+       * gcc.dg/Wrestrict-8.c: New test.
+       * gcc.dg/Wrestrict-9.c: New test.
+       * gcc.dg/pr84095.c: New test.
+
 2018-02-20  Thomas Koenig  <tkoenig@gcc.gnu.org>
 
        * gfortran.dg/structure_constructor_14.f90: Adjust STOP number.
index 3445b950811b4429ea971997b0def986ce24805c..2ee81464375d29e156062ed7901f90b6c97b58c6 100644 (file)
@@ -61,7 +61,7 @@ void test_memcpy_bounds (char *d, const char *s, size_t n)
      they appear as large positive in the source.  It would be nice
      if they retained their type but unfortunately that's not how
      it works so be prepared for both in case it even gets fixed.  */
-  T (char, 1, a + UR (3, SIZE_MAX - 1), s, n);   /* { dg-warning "offset \\\[3, -2] is out of the bounds \\\[0, 1] of object" "memcpy" } */
+  T (char, 1, a + UR (3, SIZE_MAX - 1), s, n);   /* { dg-warning "offset \\\[3, -?\[0-9\]+] is out of the bounds \\\[0, 1] of object" "memcpy" } */
 
   /* Verify that invalid offsets into an array of unknown size are
      detected.  */
@@ -226,7 +226,7 @@ T (char, 1, a + SR (-2, -1), s, n);     /* { dg-warning "offset \\\[-2, -1] is o
      they appear as large positive in the source.  It would be nice
      if they retained their type but unfortunately that's not how
      it works so be prepared for both in case it ever gets fixed.  */
-  T (char, 1, a + UR (3, SIZE_MAX), s, n);   /* { dg-warning "offset \\\[3, -1] is out of the bounds \\\[0, 1] of object "  "mempcpy" } */
+  T (char, 1, a + UR (3, SIZE_MAX), s, n);   /* { dg-warning "offset \\\[3, -?\[0-9\]+] is out of the bounds \\\[0, 1] of object "  "mempcpy" } */
 
   /* Verify that invalid offsets into an array of unknown size are
      detected.  */
index 72d9a4716fb22b4de5f36a08bd606c27b5b5fa98..e62652f78e1a97d1797785fff51f8b70e9a476cd 100644 (file)
@@ -223,7 +223,7 @@ void test_memcpy_range (char *d, size_t sz)
 
   /* Because the size is constant and a power of 2 the following is
      folded too early to detect the overlap.  */
-  T (d + ir, d, 4);               /* { dg-warning "accessing 4 bytes at offsets \\\[2, 3] and 0 overlaps 2 byte at offset 2" "" { xfail *-*-* } } */
+  T (d + ir, d, 4);               /* { dg-warning "accessing 4 bytes at offsets \\\[2, 3] and 0 overlaps 2 byte at offset 2" "memcpy" { xfail *-*-* } } */
   T (d + ir, d, 5);               /* { dg-warning "accessing 5 bytes at offsets \\\[2, 3] and 0 overlaps between 2 and 3 bytes at offset \\\[2, 3]" "memcpy" } */
 
   /* Exercise the full range of size_t.  */
@@ -325,11 +325,11 @@ void test_memcpy_anti_range (char *d, const char *s)
   T (d, d + SAR (0, 3), 1);
   T (d, d + SAR (0, 3), 2);
   T (d, d + SAR (0, 3), 3);
-  T (d, d + SAR (0, 3), DIFF_MAX - 2);   /* { dg-warning "overlaps \[0-9\]+ bytes at offset 2" } */
-  T (d, d + SAR (0, 3), DIFF_MAX - 1);   /* { dg-warning "overlaps \[0-9\]+ bytes at offset 1" } */
-  T (d, d + SAR (0, 3), DIFF_MAX);       /* { dg-warning "overlaps \[0-9\]+ bytes at offset 0" } */
+  T (d, d + SAR (0, 3), DIFF_MAX - 2);   /* { dg-warning "overlaps \[0-9\]+ bytes at offset 2" "memcpy" } */
+  T (d, d + SAR (0, 3), DIFF_MAX - 1);   /* { dg-warning "overlaps \[0-9\]+ bytes at offset 1" "memcpy" } */
+  T (d, d + SAR (0, 3), DIFF_MAX);       /* { dg-warning "overlaps \[0-9\]+ bytes at offset 0" "memcpy" } */
 
-  T (d, d + SAR (0, 3), UR (DIFF_MAX - 2, DIFF_MAX));               /* { dg-warning "accessing \[0-9\]+ or more bytes at offsets 0 and \\\[-?\[0-9\]+, -?\[0-9\]+] overlaps \[0-9\]+ bytes at offset 2" } */
+  T (d, d + SAR (0, 3), UR (DIFF_MAX - 2, DIFF_MAX));               /* { dg-warning "accessing \[0-9\]+ or more bytes at offsets 0 and \\\[-?\[0-9\]+, -?\[0-9\]+] overlaps \[0-9\]+ bytes at offset 2" "memcpy" } */
 
   /* Verify that a size in an anti-range ~[0, N] where N >= PTRDIFF_MAX
      doesn't trigger a warning.  */
@@ -399,7 +399,7 @@ void test_memcpy_range_exceed (char *d, const char *s)
   T (d + i, s, 5);   /* { dg-warning "accessing 5 bytes at offsets \\\[9223372036854775805, 9223372036854775807] and 0 overlaps 3 bytes at offset 9223372036854775802" "LP64" { target lp64 } } */
 #elif __SIZEOF_SIZE_T__ == 4
   T (d, d + i, 5);   /* { dg-warning "accessing 5 bytes at offsets 0 and \\\[2147483645, 2147483647] overlaps 3 bytes at offset 2147483642" "ILP32" { target ilp32 } } */
-  T (d + i, d, 5);   /* { dg-warning "accessing 5 bytes at offsets \\\[2147483645, 2147483647] and 0 overlaps 3 bytes at offset 2147483642" "ILP32" { target ilp32} } */
+  T (d + i, d, 5);   /* { dg-warning "accessing 5 bytes at offsets \\\[2147483645, 2147483647] and 0 overlaps 3 bytes at offset 2147483642" "ILP32" { target ilp32 } } */
 
   T (d, s + i, 5);   /* { dg-warning "accessing 5 bytes at offsets 0 and \\\[2147483645, 2147483647] overlaps 3 bytes at offset 2147483642" "ILP32" { target ilp32 } } */
   T (d + i, s, 5);   /* { dg-warning "accessing 5 bytes at offsets \\\[2147483645, 2147483647] and 0 overlaps 3 bytes at offset 2147483642" "ILP32" { target ilp32} } */
@@ -523,7 +523,7 @@ void test_memcpy_memarrray (struct MemArrays *p)
   T (p->a8, p->a8 + 2, 2);
   T (p->a8, p->a8 + 8, 1);
 
-  T (p->a8, p->a8 + 2, 3);        /* { dg-warning "accessing 3 bytes at offsets 0 and 2 overlaps 1 byte at offset 2" } */
+  T (p->a8, p->a8 + 2, 3);        /* { dg-warning "accessing 3 bytes at offsets 0 and 2 overlaps 1 byte at offset 2" "memcpy" } */
 }
 
 /* Exercise the absence of warnings with memmove.  */
@@ -768,13 +768,13 @@ void test_strcpy_range (void)
 
   /* The overlap in the cases below isn't inevitable but it is diagnosed
      because it is possible and so the code is considered unsafe.  */
-  T (8, "", a, a + r);               /* { dg-warning "accessing 1 byte may overlap 1 byte" "strcpy" } */
-  T (8, "0", a + r, a);              /* { dg-warning "accessing 2 bytes may overlap up to 2 bytes" "strcpy" } */
-  T (8, "012", a + r, a);            /* { dg-warning "accessing 4 bytes may overlap up to 4 bytes" "strcpy" } */
+  T (8, "", a, a + r);               /* { dg-warning "accessing 1 byte at offsets 0 and \\\[0, 8] may overlap 1 byte" "strcpy" } */
+  T (8, "0", a + r, a);              /* { dg-warning "accessing 2 bytes at offsets \\\[0, 8] and 0 may overlap up to 2 bytes" "strcpy" } */
+  T (8, "012", a + r, a);            /* { dg-warning "accessing 4 bytes at offsets \\\[0, 8] and 0 may overlap up to 4 bytes" "strcpy" } */
 
-  T (8, "", a, a + r);               /* { dg-warning "accessing 1 byte may overlap" "strcpy" } */
-  T (8, "0", a, a + r);              /* { dg-warning "accessing between 0 and 2 bytes may overlap up to 2 bytes" "strcpy" } */
-  T (8, "012", a, a + r);            /* { dg-warning "accessing between 0 and 4 bytes may overlap up to 4 bytes" "strcpy" } */
+  T (8, "", a, a + r);               /* { dg-warning "accessing 1 byte at offsets 0 and \\\[0, 8] may overlap" "strcpy" } */
+  T (8, "0", a, a + r);              /* { dg-warning "accessing between 0 and 2 bytes at offsets 0 and \\\[0, 8] may overlap up to 2 bytes" "strcpy" } */
+  T (8, "012", a, a + r);            /* { dg-warning "accessing between 0 and 4 bytes at offsets 0 and \\\[0, 8] may overlap up to 4 bytes" "strcpy" } */
 }
 
 /* Exercise strcpy with destination and/or source of unknown lengthu.  */
diff --git a/gcc/testsuite/gcc.dg/Warray-bounds-27.c b/gcc/testsuite/gcc.dg/Warray-bounds-27.c
new file mode 100644 (file)
index 0000000..98c9439
--- /dev/null
@@ -0,0 +1,35 @@
+/* { dg-do compile }
+   { dg-options "-O2 -Wall -Wextra -Warray-bounds -Wrestrict" } */
+
+typedef __SIZE_TYPE__ size_t;
+
+extern void* memcpy (void* restrict, const void* restrict, size_t);
+
+extern void sink (void*, ...);
+
+struct Data {
+  size_t n;
+  void *p;
+};
+
+void test_copy (void)
+{
+  struct Data d;
+  sink (&d);
+
+  char dp1[sizeof d + 1];
+  char d2x[2 * sizeof d];
+  char d2xp1[2 * sizeof d + 1];
+
+  /* During development the following would incorrectly trigger:
+     warning: 'memcpy' forming offset [17, 25] is out of the bounds [0, 16]
+             of object ā€˜dā€™ with type 'struct Data' [-Warray-bounds]
+     that wasn't caught by the test suite.  Make sure it is.  */
+  memcpy (&dp1, d.p, sizeof dp1);       /* { dg-bogus "\\\[-Warray-bounds" } */
+
+  /* Likewise.  */
+  memcpy (&d2x, d.p, sizeof d2x);       /* { dg-bogus "\\\[-Warray-bounds" } */
+  memcpy (&d2xp1, d.p, sizeof d2xp1);   /* { dg-bogus "\\\[-Warray-bounds" } */
+
+  sink (&d, &dp1, &d2x, &d2xp1);
+}
index c1bb373c5cd0d4930d57eb8562d9c90791cd514a..cc7185f0998967f8aca707340b053adde1ae780b 100644 (file)
@@ -21,7 +21,7 @@ void warn_2_smax_p2 (void)
 
   ptrdiff_t i = UR (2, DIFF_MAX + (size_t)2);
 
-  strcpy (d, d + i);          /* { dg-warning "accessing between 0 and 4 bytes at offsets 0 and \\\[2, -\[0-9\]+] may overlap up to 2 bytes at offset 2" } */
+  strcpy (d, d + i);          /* { dg-warning "accessing between 0 and 4 bytes at offsets 0 and \\\[2, 7] may overlap up to 2 bytes at offset 2" } */
 
   sink (d);
 }
@@ -47,7 +47,7 @@ void warn_2u_smax_p2 (void)
 
   size_t i = UR (2, DIFF_MAX + (size_t)2);
 
-  strcpy (d, d + i);          /* { dg-warning "accessing between 0 and 4 bytes at offsets 0 and \\\[2, -\[0-9\]+] may overlap up to 2 bytes at offset 2" } */
+  strcpy (d, d + i);          /* { dg-warning "accessing between 0 and 4 bytes at offsets 0 and \\\[2, 7] may overlap up to 2 bytes at offset 2" } */
 
   sink (d);
 }
diff --git a/gcc/testsuite/gcc.dg/Wrestrict-8.c b/gcc/testsuite/gcc.dg/Wrestrict-8.c
new file mode 100644 (file)
index 0000000..24946b0
--- /dev/null
@@ -0,0 +1,116 @@
+/* PR tree-optimization/84095 - false-positive -Wrestrict warnings for
+   memcpy within array
+   { dg-do compile }
+   { dg-options "-O2 -Wrestrict -ftrack-macro-expansion=0" } */
+
+typedef __SIZE_TYPE__ size_t;
+
+extern void* memcpy (void* restrict, const void* restrict, size_t);
+
+#define T(d, s, n)   memcpy (d, s, n)
+
+struct S1 { char c; } a8_1[8];
+
+void test_1_dim_var (int i, int j)
+{
+  /* Variable destination index and constant source index.  */
+  T (&a8_1[i], &a8_1[0], 1);
+  T (&a8_1[i], &a8_1[0], 2);
+  T (&a8_1[i], &a8_1[0], 3);
+  T (&a8_1[i], &a8_1[0], 4);
+
+  T (&a8_1[i], &a8_1[0], 5);    /* { dg-warning "accessing 5 bytes at offsets \\\[0, 8] and 0 overlaps between 2 and 5 bytes at offset \\\[0, 3\\\]" } */
+  T (&a8_1[i], &a8_1[0], 6);    /* { dg-warning "accessing 6 bytes at offsets \\\[0, 8] and 0 overlaps between 4 and 6 bytes at offset \\\[0, 2\\\]" } */
+  T (&a8_1[i], &a8_1[0], 7);    /* { dg-warning "accessing 7 bytes at offsets \\\[0, 8] and 0 overlaps between 6 and 7 bytes at offset \\\[0, 1\\\]" } */
+  T (&a8_1[i], &a8_1[0], 8);    /* { dg-warning "accessing 8 bytes at offsets \\\[0, 8] and 0 overlaps 8 bytes at offset 0" } */
+
+  /* The following is diagnosed by -Warray-bounds when it's enabled
+     rather than by -Wrestrict.  */
+  T (&a8_1[i], &a8_1[0], 9);    /* { dg-warning "accessing 9 bytes at offsets \\\[0, 8] and 0 overlaps 9 bytes at offset 0" } */
+
+  /* Same as above but with constant destination index and variable
+     source index.  */
+  T (&a8_1[0], &a8_1[i], 1);
+  T (&a8_1[0], &a8_1[i], 2);
+  T (&a8_1[0], &a8_1[i], 3);
+  T (&a8_1[0], &a8_1[i], 4);
+
+  T (&a8_1[0], &a8_1[i], 5);    /* { dg-warning "accessing 5 bytes at offsets 0 and \\\[0, 8] overlaps between 2 and 5 bytes at offset \\\[0, 3\\\]" } */
+  T (&a8_1[0], &a8_1[i], 6);    /* { dg-warning "accessing 6 bytes at offsets 0 and \\\[0, 8] overlaps between 4 and 6 bytes at offset \\\[0, 2\\\]" } */
+  T (&a8_1[0], &a8_1[i], 7);    /* { dg-warning "accessing 7 bytes at offsets 0 and \\\[0, 8] overlaps between 6 and 7 bytes at offset \\\[0, 1\\\]" } */
+  T (&a8_1[0], &a8_1[i], 8);    /* { dg-warning "accessing 8 bytes at offsets 0 and \\\[0, 8] overlaps 8 bytes at offset 0" } */
+  T (&a8_1[0], &a8_1[i], 9);    /* { dg-warning "accessing 9 bytes at offsets 0 and \\\[0, 8] overlaps 9 bytes at offset 0" } */
+
+
+  /* Variable destination and source indices.  */
+  T (&a8_1[i], &a8_1[j], 1);
+  T (&a8_1[i], &a8_1[j], 2);
+  T (&a8_1[i], &a8_1[j], 3);
+  T (&a8_1[i], &a8_1[j], 4);
+
+  T (&a8_1[i], &a8_1[j], 5);    /* { dg-warning "accessing 5 bytes at offsets \\\[0, 8] and \\\[0, 8] overlaps between 2 and 5 bytes at offset \\\[0, 3\\\]" } */
+  T (&a8_1[i], &a8_1[j], 6);    /* { dg-warning "accessing 6 bytes at offsets \\\[0, 8] and \\\[0, 8] overlaps between 4 and 6 bytes at offset \\\[0, 2\\\]" } */
+  T (&a8_1[i], &a8_1[j], 7);    /* { dg-warning "accessing 7 bytes at offsets \\\[0, 8] and \\\[0, 8] overlaps between 6 and 7 bytes at offset \\\[0, 1\\\]" } */
+  T (&a8_1[i], &a8_1[j], 8);    /* { dg-warning "accessing 8 bytes at offsets \\\[0, 8] and \\\[0, 8] overlaps 8 bytes at offset 0" } */
+
+  /* The following is diagnosed by -Warray-bounds when it's enabled
+     rather than by -Wrestrict.  */
+  T (&a8_1[i], &a8_1[j], 9);    /* { dg-warning "accessing 9 bytes at offsets \\\[0, 8] and \\\[0, 8] overlaps 9 bytes at offset 0" } */
+}
+
+struct S4 { char a4[4]; } a2_4[2];
+
+void test_2_dim (int i, int j)
+{
+  T (&a2_4[i], &a2_4[0], 1);
+  T (&a2_4[i], &a2_4[0], 4);
+
+  T (&a2_4[i], &a2_4[0], 5);    /* { dg-warning "accessing 5 bytes at offsets \\\[0, 8] and 0 overlaps between 2 and 5 bytes at offset \\\[0, 3]" } */
+  T (&a2_4[i], &a2_4[0], 6);    /* { dg-warning "accessing 6 bytes at offsets \\\[0, 8] and 0 overlaps between 4 and 6 bytes at offset \\\[0, 2]" } */
+  T (&a2_4[i], &a2_4[0], 7);    /* { dg-warning "accessing 7 bytes at offsets \\\[0, 8] and 0 overlaps between 6 and 7 bytes at offset \\\[0, 1]" } */
+  T (&a2_4[i], &a2_4[0], 8);    /* { dg-warning "accessing 8 bytes at offsets \\\[0, 8] and 0 overlaps 8 bytes at offset 0" } */
+
+  T (a2_4[i].a4, a2_4[0].a4, 1);
+  T (a2_4[i].a4, a2_4[0].a4, 4);
+
+  T (a2_4[i].a4, a2_4[0].a4, 5);   /* { dg-warning "accessing 5 bytes at offsets \\\[0, 8] and 0 overlaps between 2 and 5 bytes at offset \\\[0, 3]" } */
+  T (a2_4[i].a4, a2_4[0].a4, 8);   /* { dg-warning "accessing 8 bytes at offsets \\\[0, 8] and 0 overlaps 8 bytes at offset 0" } */
+
+  T (a2_4[i].a4, a2_4[j].a4, 1);
+  T (a2_4[i].a4, a2_4[j].a4, 4);
+
+  /* The destination and source offsets printed below ignore the size
+     of the copy and only indicate the values that are valid for each
+     of the destination and source arguments on its own, without
+     considering the size of the overlapping access.  */
+  T (a2_4[i].a4, a2_4[j].a4, 5);   /* { dg-warning "accessing 5 bytes at offsets \\\[0, 8] and \\\[0, 8] overlaps between 2 and 5 bytes at offset \\\[0, 3]" } */
+  T (a2_4[i].a4, a2_4[j].a4, 8);   /* { dg-warning "accessing 8 bytes at offsets \\\[0, 8] and \\\[0, 8] overlaps 8 bytes at offset 0" } */
+
+  /* Same as above but referencing the first elements of each array.  */
+  T (&a2_4[i].a4[0], &a2_4[j].a4[0], 1);
+  T (&a2_4[i].a4[0], &a2_4[j].a4[0], 4);
+
+  T (&a2_4[i].a4[0], &a2_4[j].a4[0], 5);   /* { dg-warning "accessing 5 bytes at offsets \\\[0, 8] and \\\[0, 8] overlaps between 2 and 5 bytes at offset \\\[0, 3]" } */
+  T (&a2_4[i].a4[0], &a2_4[j].a4[0], 8);   /* { dg-warning "accessing 8 bytes at offsets \\\[0, 8] and \\\[0, 8] overlaps 8 bytes at offset 0" } */
+
+  T (&a2_4[i].a4[0], &a2_4[j].a4[1], 3);
+  T (&a2_4[i].a4[0], &a2_4[j].a4[2], 2);
+  T (&a2_4[i].a4[0], &a2_4[j].a4[3], 1);
+}
+
+struct { int i; } a2[2][8];
+
+void test_single_2_dim_major (int i)
+{
+  memcpy (&a2[i], &a2[0], sizeof *a2);   /* { dg-bogus "\\\[-Wrestrict]" } */
+}
+
+void test_single_2_dim_minor (int i)
+{
+  memcpy (&a2[i][0], &a2[0][0], sizeof a2[0][0]);   /* { dg-bogus "\\\[-Wrestrict]" } */
+}
+
+void test_single_2_dim_major_minor (int i, int j)
+{
+  memcpy (&a2[i][j], &a2[0][0], sizeof a2[0][0]);   /* { dg-bogus "\\\[-Wrestrict]" } */
+}
diff --git a/gcc/testsuite/gcc.dg/Wrestrict-9.c b/gcc/testsuite/gcc.dg/Wrestrict-9.c
new file mode 100644 (file)
index 0000000..5ad8740
--- /dev/null
@@ -0,0 +1,315 @@
+/* PR tree-optimization/84095 - false-positive -Wrestrict warnings for
+   strcpy within array
+   { dg-do compile }
+   { dg-options "-O2 -Wrestrict -ftrack-macro-expansion=0" } */
+
+typedef __SIZE_TYPE__ size_t;
+
+extern void* memcpy (void* restrict, const void* restrict, size_t);
+extern char* strcpy (char* restrict, const char* restrict);
+
+#define T(d, s)   strcpy (d, s)
+
+struct MemArrays {
+  char pad[8];
+  char a2[3][9];
+  char a3[3][4][9];
+} a[2];
+
+struct NestedMemArrays {
+  struct MemArrays ma;
+  struct MemArrays ma1[2];
+} nma[2];
+
+/* Use a variable as the source of a copy to verify that VAR_DECL
+   is handled correctly when it's the source of GIMPLE assignment.  */
+const char *str = "1234567";
+
+void test_obj_2_dim_const (void)
+{
+  const char *s = strcpy (a[0].a2[0], "1234567");
+
+  T (a[0].a2[1], s);
+  T (a[0].a2[2], s);
+
+  /* Ideally, the offsets in the warning below would be relative to
+     the beginning of the accessed array member.  Unfortunately, when
+     the offset is represented as
+     MEM_REF (char[9], A, offsetof (struct MemArrays, A[0].A2[0]) + 1)
+     as it is in this instance it's difficult to determine the member
+     that is being accessed and tease out from the MEM_REF the offset
+     as it appears in the source.  As a result, the warning mentions
+     the offset from the beginning of A instead.  This is suboptimal
+     and should be fixed, either by printing the correct offsets or
+     by mentioning the base object that the offset is relative to.  */
+  T (a[0].a2[0] + 1, s);      /* { dg-warning "accessing 8 bytes at offsets \(1|9\) and \(0|8\) overlaps 7 bytes at offset \(1|9\)." } */
+  T (a[0].a2[1] + 2, s);
+  T (a[0].a2[2] + 3, s);
+
+  T (a[1].a2[0], s);
+  T (a[1].a2[1], s);
+  T (a[1].a2[2], s);
+
+  T (a[1].a2[0] + 1, s);
+  T (a[1].a2[1] + 2, s);
+  T (a[1].a2[2] + 3, s);
+}
+
+void test_obj_nested_2_dim_const (void)
+{
+  const char *s = strcpy (nma[0].ma.a2[0], str);
+
+  T (nma[0].ma.a2[1], s);
+  T (nma[0].ma.a2[2], s);
+
+  T (nma[0].ma.a2[0] + 1, s);      /* { dg-warning "accessing 8 bytes at offsets 1 and 0 overlaps 7 bytes at offset 1" "bug " { xfail *-*-* } } */
+  T (nma[0].ma.a2[1] + 2, s);
+  T (nma[0].ma.a2[2] + 3, s);
+
+  T (nma[1].ma.a2[1], s);
+  T (nma[1].ma.a2[2], s);
+
+  T (nma[1].ma.a2[0] + 1, s);
+  T (nma[1].ma.a2[1] + 2, s);
+  T (nma[1].ma.a2[2] + 3, s);
+
+
+  T (nma[0].ma1[0].a2[1], s);
+  T (nma[0].ma1[0].a2[2], s);
+
+  T (nma[0].ma1[0].a2[0] + 1, s);
+  T (nma[0].ma1[0].a2[1] + 2, s);
+  T (nma[0].ma1[0].a2[2] + 3, s);
+
+  T (nma[1].ma1[0].a2[1], s);
+  T (nma[1].ma1[0].a2[2], s);
+
+  T (nma[1].ma1[0].a2[0] + 1, s);
+  T (nma[1].ma1[0].a2[1] + 2, s);
+  T (nma[1].ma1[0].a2[2] + 3, s);
+}
+
+void test_obj_2_dim_var (int i, int j)
+{
+  const char *s = memcpy (a[0].a2[0], "1234567", 8);
+
+  T (a[i].a2[0], s);          /* { dg-bogus "\\\[-Wrestrict]" } */
+  T (a[i].a2[1], s);
+  T (a[i].a2[2], s);
+
+  T (a[i].a2[0] + 1, s);
+  T (a[i].a2[1] + 1, s);
+  T (a[i].a2[2] + 1, s);
+
+  T (a[0].a2[i], s);          /* { dg-bogus "\\\[-Wrestrict]" } */
+  T (a[1].a2[i], s);
+
+  T (a[i].a2[0] + j, s);
+  T (a[i].a2[1] + j, s);
+  T (a[i].a2[2] + j, s);
+
+  T (a[0].a2[i] + 1, s);
+  T (a[1].a2[i] + 1, s);
+
+  T (a[0].a2[i] + j, s);
+  T (a[1].a2[i] + j, s);
+
+  if (i < 0 || 1 < i)
+    i = 1;
+
+  T (a[i].a2[0], s);
+  T (a[i].a2[1], s);
+  T (a[i].a2[2], s);
+
+  T (a[i].a2[0] + 1, s);
+  T (a[i].a2[1] + 1, s);
+  T (a[i].a2[2] + 1, s);
+
+  T (a[0].a2[i], s);
+  T (a[1].a2[i], s);
+
+  T (a[i].a2[0] + j, s);
+  T (a[i].a2[1] + j, s);
+  T (a[i].a2[2] + j, s);
+
+  T (a[0].a2[i] + 1, s);
+  T (a[1].a2[i] + 1, s);
+
+  T (a[0].a2[i] + j, s);
+  T (a[1].a2[i] + j, s);
+}
+
+void test_obj_nested_2_dim_var (int i, int j)
+{
+  const char *s = strcpy (nma[0].ma.a2[0], "1234567");
+
+  T (nma[i].ma.a2[0], s);     /* { dg-bogus "\\\[-Wrestrict]" } */
+  T (nma[i].ma.a2[1], s);
+  T (nma[i].ma.a2[2], s);
+
+  T (nma[i].ma.a2[0] + 1, s);
+  T (nma[i].ma.a2[1] + 1, s);
+  T (nma[i].ma.a2[2] + 1, s);
+
+  T (nma[0].ma.a2[i], s);     /* { dg-bogus "\\\[-Wrestrict]" } */
+  T (nma[1].ma.a2[i], s);
+
+  T (nma[i].ma.a2[0] + j, s);
+  T (nma[i].ma.a2[1] + j, s);
+  T (nma[i].ma.a2[2] + j, s);
+
+  T (nma[0].ma.a2[i] + 1, s);
+  T (nma[1].ma.a2[i] + 1, s);
+
+  T (nma[0].ma.a2[i] + j, s);
+  T (nma[1].ma.a2[i] + j, s);
+}
+
+void test_ref_2_dim_const (struct MemArrays *p)
+{
+  strcpy (p[0].a2[0], "1234567");
+  const char *s = p[0].a2[0];
+
+  T (p[0].a2[1], s);
+  T (p[0].a2[2], s);
+
+  T (p[1].a2[0], s);
+  T (p[1].a2[1], s);
+  T (p[1].a2[2], s);
+}
+
+void test_ref_2_dim_var (struct MemArrays *p, int i, int j)
+{
+  strcpy (p[0].a2[0], "1234567");
+  const char *s = p[0].a2[0];
+
+  T (p[i].a2[0], s);          /* { dg-bogus "\\\[-Wrestrict]" } */
+  T (p[i].a2[1], s);
+  T (p[i].a2[2], s);
+
+  T (p[0].a2[i], s);
+  T (p[1].a2[i], s);
+
+  T (p[i].a2[0] + j, s);
+  T (p[i].a2[1] + j, s);
+  T (p[i].a2[2] + j, s);
+
+  T (p[0].a2[i] + j, s);
+  T (p[1].a2[i] + j, s);
+}
+
+void test_obj_3_dim_var (int i, int j)
+{
+  strcpy (a[0].a3[0][0], "1234567");
+  const char *s = a[0].a3[0][0];
+
+  T (a[0].a3[0][i], s);
+  T (a[0].a3[1][i], s);
+  T (a[0].a3[2][i], s);
+
+  T (a[1].a3[0][i], s);
+  T (a[1].a3[1][i], s);
+  T (a[1].a3[2][i], s);
+
+  T (a[0].a3[i][0], s);       /* { dg-bogus "\\\[-Wrestrict\]" } */
+  T (a[0].a3[i][1], s);
+  T (a[0].a3[i][2], s);
+
+  T (a[1].a3[i][0], s);
+  T (a[1].a3[i][1], s);
+  T (a[1].a3[i][2], s);
+
+  T (a[i].a3[0][0], s);       /* { dg-bogus "\\\[-Wrestrict\]" } */
+  T (a[i].a3[0][1], s);
+  T (a[i].a3[0][2], s);
+
+  T (a[i].a3[1][0], s);
+  T (a[i].a3[1][1], s);
+  T (a[i].a3[1][2], s);
+
+  T (a[i].a3[2][0], s);
+  T (a[i].a3[2][1], s);
+  T (a[i].a3[2][2], s);
+
+
+  T (a[0].a3[0][i] + 1, s);
+  T (a[0].a3[1][i] + 1, s);
+  T (a[0].a3[2][i] + 1, s);
+
+  T (a[1].a3[0][i] + 1, s);
+  T (a[1].a3[1][i] + 1, s);
+  T (a[1].a3[2][i] + 1, s);
+
+
+  T (a[0].a3[0][i] + j, s);
+  T (a[0].a3[1][i] + j, s);
+  T (a[0].a3[2][i] + j, s);
+
+  T (a[1].a3[0][i] + j, s);
+  T (a[1].a3[1][i] + j, s);
+  T (a[1].a3[2][i] + j, s);
+
+  T (a[0].a3[i][0] + j, s);
+  T (a[0].a3[i][1] + j, s);
+  T (a[0].a3[i][2] + j, s);
+
+  T (a[1].a3[i][0] + j, s);
+  T (a[1].a3[i][1] + j, s);
+  T (a[1].a3[i][2] + j, s);
+
+  T (a[i].a3[0][0] + j, s);
+  T (a[i].a3[0][1] + j, s);
+  T (a[i].a3[0][2] + j, s);
+
+  T (a[i].a3[1][0] + j, s);
+  T (a[i].a3[1][1] + j, s);
+  T (a[i].a3[1][2] + j, s);
+
+  T (a[i].a3[2][0] + j, s);
+  T (a[i].a3[2][1] + j, s);
+  T (a[i].a3[2][2] + j, s);
+}
+
+void test_obj_3_dim_const (struct MemArrays *p)
+{
+  strcpy (p[0].a3[0][0], "1234567");
+  const char *s = p[0].a3[0][0];
+
+  T (p[0].a3[0][1], s);
+  T (p[0].a3[0][2], s);
+  T (p[0].a3[0][3], s);
+
+  T (p[0].a3[0][1] + 1, s);
+  T (p[0].a3[0][2] + 1, s);
+  T (p[0].a3[0][3] + 1, s);
+
+  T (p[0].a3[1][0], s);
+  T (p[0].a3[1][1], s);
+  T (p[0].a3[1][2], s);
+  T (p[0].a3[1][3], s);
+
+  T (p[0].a3[1][0] + 1, s);
+  T (p[0].a3[1][1] + 1, s);
+  T (p[0].a3[1][2] + 1, s);
+  T (p[0].a3[1][3] + 1, s);
+
+  T (p[0].a3[2][0], s);
+  T (p[0].a3[2][1], s);
+  T (p[0].a3[2][2], s);
+  T (p[0].a3[2][3], s);
+
+  T (p[1].a3[0][0], s);
+  T (p[1].a3[0][1], s);
+  T (p[1].a3[0][2], s);
+  T (p[1].a3[0][3], s);
+
+  T (p[1].a3[1][0], s);
+  T (p[1].a3[1][1], s);
+  T (p[1].a3[1][2], s);
+  T (p[1].a3[1][3], s);
+
+  T (p[1].a3[2][0], s);
+  T (p[1].a3[2][1], s);
+  T (p[1].a3[2][2], s);
+  T (p[1].a3[2][3], s);
+}
diff --git a/gcc/testsuite/gcc.dg/pr84095.c b/gcc/testsuite/gcc.dg/pr84095.c
new file mode 100644 (file)
index 0000000..af2bc0e
--- /dev/null
@@ -0,0 +1,14 @@
+/* PR tree-optimization/84095 - false-positive -Wrestrict warnings for
+   memcpy within array
+   { dg-do compile }
+   { dg-options "-O2 -Wrestrict" } */
+
+struct { int i; } a[8];
+
+void f (void)
+{
+  int i;
+
+  for (i = 1; i < 8; i++)
+    __builtin_memcpy (&a[i], &a[0], sizeof(a[0]));   /* { dg-bogus "\\\[-Wrestrict]" } */
+}