diff gcc/gimple-ssa-warn-restrict.c @ 145:1830386684a0

gcc-9.2.0
author anatofuz
date Thu, 13 Feb 2020 11:34:05 +0900
parents 84e7813d76e9
children
line wrap: on
line diff
--- a/gcc/gimple-ssa-warn-restrict.c	Thu Oct 25 07:37:49 2018 +0900
+++ b/gcc/gimple-ssa-warn-restrict.c	Thu Feb 13 11:34:05 2020 +0900
@@ -1,6 +1,6 @@
 /* Pass to detect and issue warnings for violations of the restrict
    qualifier.
-   Copyright (C) 2017-2018 Free Software Foundation, Inc.
+   Copyright (C) 2017-2020 Free Software Foundation, Inc.
    Contributed by Martin Sebor <msebor@redhat.com>.
 
    This file is part of GCC.
@@ -36,7 +36,6 @@
 #include "gimple-iterator.h"
 #include "tree-dfa.h"
 #include "tree-ssa.h"
-#include "params.h"
 #include "tree-cfg.h"
 #include "tree-object-size.h"
 #include "calls.h"
@@ -75,7 +74,7 @@
 bool
 pass_wrestrict::gate (function *fun ATTRIBUTE_UNUSED)
 {
-  return warn_array_bounds != 0 || warn_restrict != 0;
+  return warn_array_bounds || warn_restrict || warn_stringop_overflow;
 }
 
 /* Class to walk the basic blocks of a function in dominator order.  */
@@ -124,8 +123,9 @@
 /* Description of a memory reference by a built-in function.  This
    is similar to ao_ref but made especially suitable for -Wrestrict
    and not for optimization.  */
-struct builtin_memref
+class builtin_memref
 {
+public:
   /* The original pointer argument to the built-in function.  */
   tree ptr;
   /* The referenced subobject or NULL if not available, and the base
@@ -136,6 +136,8 @@
   /* The size of the BASE object, PTRDIFF_MAX if indeterminate,
      and negative until (possibly lazily) initialized.  */
   offset_int basesize;
+  /* Same for the subobject.  */
+  offset_int refsize;
 
   /* The non-negative offset of the referenced subobject.  Used to avoid
      warnings for (apparently) possibly but not definitively overlapping
@@ -147,6 +149,9 @@
   /* The size range of the access to this reference.  */
   offset_int sizrange[2];
 
+  /* Cached result of get_max_objsize().  */
+  const offset_int maxobjsize;
+
   /* True for "bounded" string functions like strncat, and strncpy
      and their variants that specify either an exact or upper bound
      on the size of the accesses they perform.  For strncat both
@@ -156,7 +161,7 @@
 
   builtin_memref (tree, tree);
 
-  tree offset_out_of_bounds (int, offset_int[2]) const;
+  tree offset_out_of_bounds (int, offset_int[3]) const;
 
 private:
 
@@ -188,7 +193,8 @@
      and false for raw memory functions.  */
   bool strict () const
   {
-    return detect_overlap != &builtin_access::generic_overlap;
+    return (detect_overlap != &builtin_access::generic_overlap
+	    && detect_overlap != &builtin_access::no_overlap);
   }
 
   builtin_access (gimple *, builtin_memref &, builtin_memref &);
@@ -196,6 +202,10 @@
   /* Entry point to determine overlap.  */
   bool overlap ();
 
+  offset_int write_off (tree) const;
+
+  void dump (FILE *) const;
+
  private:
   /* Implementation functions used to determine overlap.  */
   bool generic_overlap ();
@@ -230,9 +240,11 @@
   ref (),
   base (),
   basesize (-1),
+  refsize (-1),
   refoff (HOST_WIDE_INT_MIN),
   offrange (),
   sizrange (),
+  maxobjsize (tree_to_shwi (max_object_size ())),
   strbounded_p ()
 {
   /* Unfortunately, wide_int default ctor is a no-op so array members
@@ -240,7 +252,8 @@
   offrange[0] = offrange[1] = 0;
   sizrange[0] = sizrange[1] = 0;
 
-  const offset_int maxobjsize = tree_to_shwi (max_object_size ());
+  if (!expr)
+    return;
 
   /* Find the BASE object or pointer referenced by EXPR and set
      the offset range OFFRANGE in the process.  */
@@ -256,7 +269,7 @@
       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)
+      if (sizrange[0] <= maxobjsize && sizrange[1] > maxobjsize)
 	sizrange[1] = maxobjsize;
     }
   else
@@ -272,15 +285,16 @@
 
   offset_int maxoff = maxobjsize;
   tree basetype = TREE_TYPE (base);
-  if (TREE_CODE (basetype) == ARRAY_TYPE
-      && ref
-      && array_at_struct_end_p (ref))
-    ;   /* Use the maximum possible offset for last member arrays.  */
-  else if (tree basesize = TYPE_SIZE_UNIT (basetype))
-    if (TREE_CODE (basesize) == INTEGER_CST)
-      /* Size could be non-constant for a variable-length type such
-	 as a struct with a VLA member (a GCC extension).  */
-      maxoff = wi::to_offset (basesize);
+  if (TREE_CODE (basetype) == ARRAY_TYPE)
+    {
+      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))
+	if (TREE_CODE (basesize) == INTEGER_CST)
+	  /* Size could be non-constant for a variable-length type such
+	     as a struct with a VLA member (a GCC extension).  */
+	  maxoff = wi::to_offset (basesize);
+    }
 
   if (offrange[0] >= 0)
     {
@@ -291,13 +305,26 @@
     }
 }
 
-/* Ctor helper to set or extend OFFRANGE based on the OFFSET argument.  */
+/* Based on the initial length of the destination STARTLEN, returns
+   the offset of the first write access from the beginning of
+   the destination.  Nonzero only for strcat-type of calls.  */
+
+offset_int builtin_access::write_off (tree startlen) const
+{
+  if (detect_overlap != &builtin_access::strcat_overlap
+      || !startlen || TREE_CODE (startlen) != INTEGER_CST)
+    return 0;
+
+  return wi::to_offset (startlen);
+}
+
+/* Ctor helper to set or extend OFFRANGE based on the OFFSET argument.
+   Pointer offsets are represented as unsigned sizetype but must be
+   treated as signed.  */
 
 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);
@@ -311,40 +338,57 @@
 
   if (TREE_CODE (offset) == SSA_NAME)
     {
+      /* A pointer offset is represented as sizetype but treated
+	 as signed.  */
       wide_int min, max;
       value_range_kind rng = get_range_info (offset, &min, &max);
-      if (rng == VR_RANGE)
+      if (rng == VR_ANTI_RANGE && wi::lts_p (max, min))
 	{
+	  /* Convert an anti-range whose upper bound is less than
+	     its lower bound to a signed range.  */
+	  offrange[0] += offset_int::from (max + 1, SIGNED);
+	  offrange[1] += offset_int::from (min - 1, SIGNED);
+	  return;
+	}
+
+      if (rng == VR_RANGE
+	  && (DECL_P (base) || wi::lts_p (min, max)))
+	{
+	  /* Preserve the bounds of the range for an offset into
+	     a known object (it may be adjusted later relative to
+	     a constant offset from its beginning).  Otherwise use
+	     the bounds only when they are ascending when treated
+	     as signed.  */
 	  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);
+	  return;
 	}
-      else
+
+      /* Handle an anti-range the same as no range at all.  */
+      gimple *stmt = SSA_NAME_DEF_STMT (offset);
+      tree type;
+      if (is_gimple_assign (stmt)
+	  && (type = TREE_TYPE (gimple_assign_rhs1 (stmt)))
+	  && INTEGRAL_TYPE_P (type))
 	{
-	  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))
+	  tree_code code = gimple_assign_rhs_code (stmt);
+	  if (code == NOP_EXPR)
 	    {
 	      /* 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));
+	      return;
 	    }
-	  else
-	    offrange[1] += maxobjsize;
 	}
-      return;
     }
 
-  offrange[1] += maxobjsize;
+  const offset_int maxoff = tree_to_shwi (max_object_size ()) >> 1;
+  const offset_int minoff = -maxoff - 1;
+
+  offrange[0] += minoff;
+  offrange[1] += maxoff;
 }
 
 /* Determines the base object or pointer of the reference EXPR
@@ -353,7 +397,7 @@
 void
 builtin_memref::set_base_and_offset (tree expr)
 {
-  const offset_int maxobjsize = tree_to_shwi (max_object_size ());
+  tree offset = NULL_TREE;
 
   if (TREE_CODE (expr) == SSA_NAME)
     {
@@ -380,9 +424,7 @@
 	  else if (code == POINTER_PLUS_EXPR)
 	    {
 	      expr = gimple_assign_rhs1 (stmt);
-
-	      tree offset = gimple_assign_rhs2 (stmt);
-	      extend_offset_range (offset);
+	      offset = gimple_assign_rhs2 (stmt);
 	    }
 	  else
 	    {
@@ -392,6 +434,12 @@
 	}
       else
 	{
+	  /* FIXME: Handle PHI nodes in case like:
+	     _12 = &MEM[(void *)&a + 2B] + _10;
+
+	     <bb> [local count: 1073741824]:
+	     # prephitmp_13 = PHI <_12, &MEM[(void *)&a + 2B]>
+	     memcpy (prephitmp_13, p_7(D), 6);  */
 	  base = expr;
 	  return;
 	}
@@ -419,6 +467,9 @@
   /* get_inner_reference is not expected to return null.  */
   gcc_assert (base != NULL);
 
+  if (offset)
+    extend_offset_range (offset);
+
   poly_int64 bytepos = exact_div (bitpos, BITS_PER_UNIT);
 
   /* Convert the poly_int64 offset to offset_int.  The offset
@@ -452,32 +503,63 @@
 
   if (TREE_CODE (base) == MEM_REF)
     {
-      tree memrefoff = TREE_OPERAND (base, 1);
+      tree memrefoff = fold_convert (ptrdiff_type_node, TREE_OPERAND (base, 1));
       extend_offset_range (memrefoff);
       base = TREE_OPERAND (base, 0);
+
+      if (refoff != HOST_WIDE_INT_MIN
+      	  && TREE_CODE (expr) == COMPONENT_REF)
+      	{
+	  /* Bump up the offset of the referenced subobject to reflect
+	     the offset to the enclosing object.  For example, so that
+	     in
+	       struct S { char a, b[3]; } s[2];
+	       strcpy (s[1].b, "1234");
+	     REFOFF is set to s[1].b - (char*)s.  */
+	  offset_int off = tree_to_shwi (memrefoff);
+	  refoff += off;
+      	}
+
+      if (!integer_zerop (memrefoff))
+	/* A non-zero offset into an array of struct with flexible array
+	   members implies that the array is empty because there is no
+	   way to initialize such a member when it belongs to an array.
+	   This must be some sort of a bug.  */
+	refsize = 0;
     }
 
+  if (TREE_CODE (ref) == COMPONENT_REF)
+    if (tree size = component_ref_size (ref))
+      if (TREE_CODE (size) == INTEGER_CST)
+	refsize = wi::to_offset (size);
+
   if (TREE_CODE (base) == SSA_NAME)
     set_base_and_offset (base);
 }
 
 /* Return error_mark_node if the signed offset exceeds the bounds
-   of the address space (PTRDIFF_MAX).  Otherwise, return either
-   BASE or REF when the offset exceeds the bounds of the BASE or
-   REF object, and set OOBOFF to the past-the-end offset formed
-   by the reference, including its size.  When STRICT is non-zero
-   use REF size, when available, otherwise use BASE size.  When
-   STRICT is greater than 1, use the size of the last array member
-   as the bound, otherwise treat such a member as a flexible array
-   member.  Return NULL when the offset is in bounds.  */
+   of the address space (PTRDIFF_MAX).  Otherwise, return either BASE
+   or REF when the offset exceeds the bounds of the BASE or REF object,
+   and set OOBOFF to the past-the-end offset formed by the reference,
+   including its size.  OOBOFF is initially setto the range of offsets,
+   and OOBOFF[2] to the offset of the first write access (nonzero for
+   the strcat family).  When STRICT is nonzero use REF size, when
+   available, otherwise use BASE size.  When STRICT is greater than 1,
+   use the size of the last array member as the bound, otherwise treat
+   such a member as a flexible array member.  Return NULL when the offset
+   is in bounds.  */
 
 tree
-builtin_memref::offset_out_of_bounds (int strict, offset_int ooboff[2]) const
+builtin_memref::offset_out_of_bounds (int strict, offset_int ooboff[3]) const
 {
-  const offset_int maxobjsize = tree_to_shwi (max_object_size ());
+  if (!ptr)
+    return NULL_TREE;
+
+  /* The offset of the first write access or zero.  */
+  offset_int wroff = ooboff[2];
 
   /* A temporary, possibly adjusted, copy of the offset range.  */
-  offset_int offrng[2] = { offrange[0], offrange[1] };
+  offset_int offrng[2] = { ooboff[0], ooboff[1] };
 
   if (DECL_P (base) && TREE_CODE (TREE_TYPE (base)) == ARRAY_TYPE)
     {
@@ -495,9 +577,19 @@
   bool hib = wi::les_p (offrng[0], offrng[1]);
   bool lob = !hib;
 
+  /* Set to the size remaining in the object object after subtracting
+     REFOFF.  It may become negative as a result of negative indices
+     into the enclosing object, such as in:
+       extern struct S { char a[4], b[3], c[1]; } *p;
+       strcpy (p[-3].b, "123");  */
+  offset_int size = basesize;
+  tree obj = base;
+
+  const bool decl_p = DECL_P (obj);
+
   if (basesize < 0)
     {
-      endoff = offrng[lob] + sizrange[0];
+      endoff = offrng[lob] + (sizrange[0] - wroff);
 
       /* For a reference through a pointer to an object of unknown size
 	 all initial offsets are considered valid, positive as well as
@@ -507,41 +599,45 @@
       if (endoff > maxobjsize)
 	return error_mark_node;
 
-      return NULL_TREE;
+      /* When the referenced subobject is known, the end offset must be
+	 within its bounds.  Otherwise there is nothing to do.  */
+      if (strict
+	  && !decl_p
+	  && ref
+	  && refsize >= 0
+	  && TREE_CODE (ref) == COMPONENT_REF)
+	{
+	  /* If REFOFF is negative, SIZE will become negative here.  */
+	  size = refoff + refsize;
+	  obj = ref;
+	}
+      else
+	return NULL_TREE;
     }
 
   /* A reference to an object of known size must be within the bounds
-     of the base object.  */
-  if (offrng[hib] < 0 || offrng[lob] > basesize)
-    return base;
+     of either the base object or the subobject (see above for when
+     a subobject can be used).  */
+  if ((decl_p && offrng[hib] < 0) || offrng[lob] > size)
+    return obj;
 
   /* The extent of the reference must also be within the bounds of
-     the base object (if known) or the maximum object size otherwise.  */
-  endoff = wi::smax (offrng[lob], 0) + sizrange[0];
+     the base object (if known) or the subobject or the maximum object
+     size otherwise.  */
+  endoff = offrng[lob] + sizrange[0];
   if (endoff > maxobjsize)
     return error_mark_node;
 
-  offset_int size = basesize;
-  tree obj = base;
-
   if (strict
-      && DECL_P (obj)
+      && decl_p
       && ref
-      && refoff >= 0
-      && TREE_CODE (ref) == COMPONENT_REF
-      && (strict > 1
-	  || !array_at_struct_end_p (ref)))
+      && refsize >= 0
+      && TREE_CODE (ref) == COMPONENT_REF)
     {
-      /* If the reference is to a member subobject, the offset must
-	 be within the bounds of the subobject.  */
-      tree field = TREE_OPERAND (ref, 1);
-      tree type = TREE_TYPE (field);
-      if (tree sz = TYPE_SIZE_UNIT (type))
-	if (TREE_CODE (sz) == INTEGER_CST)
-	  {
-	    size = refoff + wi::to_offset (sz);
-	    obj = ref;
-	  }
+      /* If the reference is to a member subobject of a declared object,
+	 the offset must be within the bounds of the subobject.  */
+      size = refoff + refsize;
+      obj = ref;
     }
 
   if (endoff <= size)
@@ -549,12 +645,12 @@
 
   /* Set the out-of-bounds offset range to be one greater than
      that delimited by the reference including its size.  */
-  ooboff[lob] = size + 1;
+  ooboff[lob] = size;
 
   if (endoff > ooboff[lob])
-    ooboff[hib] = endoff;
+    ooboff[hib] = endoff - 1;
   else
-    ooboff[hib] = wi::smax (offrng[lob], 0) + sizrange[1];
+    ooboff[hib] = offrng[lob] + sizrange[1];
 
   return obj;
 }
@@ -567,8 +663,10 @@
 : dstref (&dst), srcref (&src), sizrange (), ovloff (), ovlsiz (),
   dstoff (), srcoff (), dstsiz (), srcsiz ()
 {
+  dstoff[0] = dst.offrange[0];
+  dstoff[1] = dst.offrange[1];
+
   /* Zero out since the offset_int ctors invoked above are no-op.  */
-  dstoff[0] = dstoff[1] = 0;
   srcoff[0] = srcoff[1] = 0;
   dstsiz[0] = dstsiz[1] = 0;
   srcsiz[0] = srcsiz[1] = 0;
@@ -609,6 +707,14 @@
       detect_overlap = &builtin_access::no_overlap;
       break;
 
+    case BUILT_IN_MEMSET:
+    case BUILT_IN_MEMSET_CHK:
+      /* For memset there is never any overlap to check for.  */
+      ostype = 0;
+      depends_p = false;
+      detect_overlap = &builtin_access::no_overlap;
+      break;
+
     case BUILT_IN_STPNCPY:
     case BUILT_IN_STPNCPY_CHK:
     case BUILT_IN_STRNCPY:
@@ -643,7 +749,7 @@
       return;
     }
 
-  const offset_int maxobjsize = tree_to_shwi (max_object_size ());
+  const offset_int maxobjsize = dst.maxobjsize;
 
   /* Try to determine the size of the base object.  compute_objsize
      expects a pointer so create one if BASE is a non-pointer object.  */
@@ -662,7 +768,7 @@
 	dst.basesize = maxobjsize;
     }
 
-  if (src.basesize < 0)
+  if (src.base && src.basesize < 0)
     {
       addr = src.base;
       if (!POINTER_TYPE_P (TREE_TYPE (addr)))
@@ -676,21 +782,19 @@
 	src.basesize = maxobjsize;
     }
 
-  /* If there is no dependency between the references or the base
-     objects of the two references aren't the same there's nothing
-     else to do.  */
-  if (depends_p && dstref->base != srcref->base)
-    return;
-
-  /* ...otherwise, make adjustments for references to the same object
-     by string built-in functions to reflect the constraints imposed
-     by the function.  */
+  /* Make adjustments for references to the same object by string
+     built-in functions to reflect the constraints imposed by
+     the function.  */
 
   /* For bounded string functions determine the range of the bound
      on the access.  For others, the range stays unbounded.  */
   offset_int bounds[2] = { maxobjsize, maxobjsize };
   if (dstref->strbounded_p)
     {
+      unsigned nargs = gimple_call_num_args (call);
+      if (nargs <= sizeargno)
+	return;
+
       tree size = gimple_call_arg (call, sizeargno);
       tree range[2];
       if (get_size_range (size, range, true))
@@ -711,6 +815,7 @@
 	}
     }
 
+  bool dstsize_set = false;
   /* The size range of one reference involving the same base object
      can be determined from the size range of the other reference.
      This makes it possible to compute accurate offsets for warnings
@@ -722,6 +827,7 @@
 	 the source.  */
       dstref->sizrange[0] = srcref->sizrange[0];
       dstref->sizrange[1] = srcref->sizrange[1];
+      dstsize_set = true;
     }
   else if (srcref->sizrange[0] == 0 && srcref->sizrange[1] == maxobjsize)
     {
@@ -734,11 +840,15 @@
 	{
 	  if (dstref->strbounded_p)
 	    {
-	      /* Read access by strncpy is bounded.  */
-	      if (bounds[0] < srcref->sizrange[0])
-		srcref->sizrange[0] = bounds[0];
-	      if (bounds[1] < srcref->sizrange[1])
-		srcref->sizrange[1] = bounds[1];
+	      /* Read access by strncpy is constrained by the third
+		 argument but except for a zero bound is at least one.  */
+	      offset_int size = wi::umax (srcref->basesize, 1);
+	      offset_int bound = wi::umin (size, bounds[0]);
+	      if (bound < srcref->sizrange[0])
+		srcref->sizrange[0] = bound;
+	      bound = wi::umin (srcref->basesize, bounds[1]);
+	      if (bound < srcref->sizrange[1])
+		srcref->sizrange[1] = bound;
 	    }
 
 	  /* For string functions, adjust the size range of the source
@@ -790,6 +900,11 @@
 	    }
 	}
     }
+  else if (!dstsize_set && detect_overlap == &builtin_access::strcat_overlap)
+    {
+      dstref->sizrange[0] += srcref->sizrange[0] - 1;
+      dstref->sizrange[1] += srcref->sizrange[1] - 1;
+    }
 
   if (dstref->strbounded_p)
     {
@@ -848,10 +963,9 @@
 
   gcc_assert (dstref->base == srcref->base);
 
-  const offset_int maxobjsize = tree_to_shwi (max_object_size ());
+  const offset_int maxobjsize = acs.dstref->maxobjsize;
 
   offset_int maxsize = dstref->basesize < 0 ? maxobjsize : dstref->basesize;
-  gcc_assert (maxsize <= maxobjsize);
 
   /* Adjust the larger bounds of the offsets (which may be the first
      element if the lower bound is larger than the upper bound) to
@@ -1057,17 +1171,18 @@
 
   gcc_assert (dstref->base == srcref->base);
 
-  const offset_int maxobjsize = tree_to_shwi (max_object_size ());
+  const offset_int maxobjsize = acs.dstref->maxobjsize;
 
   gcc_assert (dstref->base && dstref->base == srcref->base);
 
   /* Adjust for strcat-like accesses.  */
 
   /* As a special case for strcat, set the DSTREF offsets to the length
-     of the source string since the function starts writing at the first
-     nul, and set the size to 1 for the length of the nul.  */
-  acs.dstoff[0] += acs.dstsiz[0];
-  acs.dstoff[1] += acs.dstsiz[1];
+     of the destination string since the function starts writing over
+     its terminating nul, and set the destination size to 1 for the length
+     of the nul.  */
+  acs.dstoff[0] += dstsiz[0] - srcref->sizrange[0];
+  acs.dstoff[1] += dstsiz[1] - srcref->sizrange[1];
 
   bool strfunc_unknown_args = acs.dstsiz[0] == 0 && acs.dstsiz[1] != 0;
 
@@ -1077,7 +1192,6 @@
   acs.dstsiz[1] = 1;
 
   offset_int maxsize = dstref->basesize < 0 ? maxobjsize : dstref->basesize;
-  gcc_assert (maxsize <= maxobjsize);
 
   /* For references to the same base object, determine if there's a pair
      of valid offsets into the two references such that access between
@@ -1145,7 +1259,8 @@
   acs.ovlsiz[0] = dstref->sizrange[0] == dstref->sizrange[1] ? 1 : 0;
   acs.ovlsiz[1] = 1;
 
-  offset_int endoff = dstref->offrange[0] + dstref->sizrange[0];
+  offset_int endoff
+    = dstref->offrange[0] + (dstref->sizrange[0] - srcref->sizrange[0]);
   if (endoff <= srcref->offrange[0])
     acs.ovloff[0] = wi::smin (maxobjsize, srcref->offrange[0]).to_shwi ();
   else
@@ -1194,7 +1309,7 @@
 {
   builtin_access &acs = *this;
 
-  const offset_int maxobjsize = tree_to_shwi (max_object_size ());
+  const offset_int maxobjsize = dstref->maxobjsize;
 
   acs.sizrange[0] = wi::smax (dstref->sizrange[0],
 			      srcref->sizrange[0]).to_shwi ();
@@ -1217,10 +1332,6 @@
   if (!dstref->base || !srcref->base)
     return false;
 
-  /* Set the access offsets.  */
-  acs.dstoff[0] = dstref->offrange[0];
-  acs.dstoff[1] = dstref->offrange[1];
-
   /* If the base object is an array adjust the bounds of the offset
      to be non-negative and within the bounds of the array if possible.  */
   if (dstref->base
@@ -1328,6 +1439,9 @@
   if (!acs.overlap ())
     return false;
 
+  if (gimple_no_warning_p (call))
+    return true;
+
   /* For convenience.  */
   const builtin_memref &dstref = *acs.dstref;
   const builtin_memref &srcref = *acs.srcref;
@@ -1372,7 +1486,7 @@
 	     "[" HOST_WIDE_INT_PRINT_DEC ", " HOST_WIDE_INT_PRINT_DEC "]",
 	     ovloff[0], ovloff[1]);
 
-  const offset_int maxobjsize = tree_to_shwi (max_object_size ());
+  const offset_int maxobjsize = dstref.maxobjsize;
   bool must_overlap = ovlsiz[0] > 0;
 
   if (ovlsiz[1] == 0)
@@ -1567,28 +1681,81 @@
   return true;
 }
 
-/* Validate REF offsets in an EXPRession passed as an argument to a CALL
-   to a built-in function FUNC to make sure they are within the bounds
-   of the referenced object if its size is known, or PTRDIFF_MAX otherwise.
-   Both initial values of the offsets and their final value computed by
-   the function by incrementing the initial value by the size are
+/* Validate REF size and offsets in an expression passed as an argument
+   to a CALL to a built-in function FUNC to make sure they are within
+   the bounds of the referenced object if its size is known, or
+   PTRDIFF_MAX otherwise.  DO_WARN is true when a diagnostic should
+   be issued, false otherwise.
+   Both initial values of the offsets and their final value computed
+   by the function by incrementing the initial value by the size are
    validated.  Return true if the offsets are not valid and a diagnostic
-   has been issued.  */
+   has been issued, or would have been issued if DO_WARN had been true.  */
 
 static bool
-maybe_diag_offset_bounds (location_t loc, gimple *call, tree func, int strict,
-			  tree expr, const builtin_memref &ref)
+maybe_diag_access_bounds (location_t loc, gimple *call, tree func, int strict,
+			  const builtin_memref &ref, offset_int wroff,
+			  bool do_warn)
 {
-  if (!warn_array_bounds)
-    return false;
+  const offset_int maxobjsize = ref.maxobjsize;
+
+  /* Check for excessive size first and regardless of warning options
+     since the result is used to make codegen decisions.  */
+  if (ref.sizrange[0] > maxobjsize)
+    {
+      /* Return true without issuing a warning.  */
+      if (!do_warn)
+	return true;
+
+      if (ref.ref && TREE_NO_WARNING (ref.ref))
+	return false;
+
+      if (warn_stringop_overflow)
+	{
+	  if (EXPR_HAS_LOCATION (ref.ptr))
+	    loc = EXPR_LOCATION (ref.ptr);
+
+	  loc = expansion_point_location_if_in_system_header (loc);
 
-  offset_int ooboff[] = { ref.offrange[0], ref.offrange[1] };
+	  if (ref.sizrange[0] == ref.sizrange[1])
+	    return warning_at (loc, OPT_Wstringop_overflow_,
+			       "%G%qD specified bound %wu "
+			       "exceeds maximum object size %wu",
+			       call, func, ref.sizrange[0].to_uhwi (),
+			       maxobjsize.to_uhwi ());
+
+	  return warning_at (loc, OPT_Wstringop_overflow_,
+			     "%G%qD specified bound between %wu and %wu "
+			     "exceeds maximum object size %wu",
+			     call, func, ref.sizrange[0].to_uhwi (),
+			     ref.sizrange[1].to_uhwi (),
+			     maxobjsize.to_uhwi ());
+	}
+    }
+
+  /* Check for out-bounds pointers regardless of warning options since
+     the result is used to make codegen decisions.  An excessive WROFF
+     can only come up as a result of an invalid strncat bound and is
+     diagnosed separately using a more meaningful warning.  */
+  if (maxobjsize < wroff)
+    wroff = 0;
+  offset_int ooboff[] = { ref.offrange[0], ref.offrange[1], wroff };
   tree oobref = ref.offset_out_of_bounds (strict, ooboff);
   if (!oobref)
     return false;
 
-  if (EXPR_HAS_LOCATION (expr))
-    loc = EXPR_LOCATION (expr);
+  /* Return true without issuing a warning.  */
+  if (!do_warn)
+    return true;
+
+  if (!warn_array_bounds)
+    return false;
+
+  if (TREE_NO_WARNING (ref.ptr)
+      || (ref.ref && TREE_NO_WARNING (ref.ref)))
+    return false;
+
+  if (EXPR_HAS_LOCATION (ref.ptr))
+    loc = EXPR_LOCATION (ref.ptr);
 
   loc = expansion_point_location_if_in_system_header (loc);
 
@@ -1607,11 +1774,12 @@
   if (oobref == error_mark_node)
     {
       if (ref.sizrange[0] == ref.sizrange[1])
-	sprintf (rangestr[1], "%lli", (long long) ref.sizrange[0].to_shwi ());
+	sprintf (rangestr[1], "%llu",
+		 (unsigned long long) ref.sizrange[0].to_shwi ());
       else
 	sprintf (rangestr[1], "[%lli, %lli]",
-		 (long long) ref.sizrange[0].to_shwi (),
-		 (long long) ref.sizrange[1].to_shwi ());
+		 (unsigned long long) ref.sizrange[0].to_uhwi (),
+		 (unsigned long long) ref.sizrange[1].to_uhwi ());
 
       tree type;
 
@@ -1642,8 +1810,6 @@
     }
   else if (oobref == ref.base)
     {
-      const offset_int maxobjsize = tree_to_shwi (max_object_size ());
-
       /* True when the offset formed by an access to the reference
 	 is out of bounds, rather than the initial offset wich is
 	 in bounds.  This implies access past the end.  */
@@ -1693,27 +1859,45 @@
     }
   else if (TREE_CODE (ref.ref) == MEM_REF)
     {
-      tree type = TREE_TYPE (TREE_OPERAND (ref.ref, 0));
+      tree refop = TREE_OPERAND (ref.ref, 0);
+      tree type = TREE_TYPE (refop);
       if (POINTER_TYPE_P (type))
 	type = TREE_TYPE (type);
       type = TYPE_MAIN_VARIANT (type);
 
-      warned = warning_at (loc, OPT_Warray_bounds,
-			   "%G%qD offset %s from the object at %qE is out "
-			   "of the bounds of %qT",
-			   call, func, rangestr[0], ref.base, type);
+      if (warning_at (loc, OPT_Warray_bounds,
+		      "%G%qD offset %s from the object at %qE is out "
+		      "of the bounds of %qT",
+		      call, func, rangestr[0], ref.base, type))
+	{
+	  if (TREE_CODE (ref.ref) == COMPONENT_REF)
+	    refop = TREE_OPERAND (ref.ref, 1);
+	  if (DECL_P (refop))
+	    inform (DECL_SOURCE_LOCATION (refop),
+		    "subobject %qD declared here", refop);
+	  warned = true;
+	}
     }
   else
     {
+      tree refop = TREE_OPERAND (ref.ref, 0);
       tree type = TYPE_MAIN_VARIANT (TREE_TYPE (ref.ref));
 
-      warned = warning_at (loc, OPT_Warray_bounds,
-			   "%G%qD offset %s from the object at %qE is out "
-			   "of the bounds of referenced subobject %qD with "
-			   "type %qT at offset %wu",
-			   call, func, rangestr[0], ref.base,
-			   TREE_OPERAND (ref.ref, 1), type,
-			   ref.refoff.to_uhwi ());
+      if (warning_at (loc, OPT_Warray_bounds,
+		      "%G%qD offset %s from the object at %qE is out "
+		      "of the bounds of referenced subobject %qD with "
+		      "type %qT at offset %wi",
+		      call, func, rangestr[0], ref.base,
+		      TREE_OPERAND (ref.ref, 1), type,
+		      ref.refoff.to_shwi ()))
+	{
+	  if (TREE_CODE (ref.ref) == COMPONENT_REF)
+	    refop = TREE_OPERAND (ref.ref, 1);
+	  if (DECL_P (refop))
+	    inform (DECL_SOURCE_LOCATION (refop),
+		    "subobject %qD declared here", refop);
+	  warned = true;
+	}
     }
 
   return warned;
@@ -1766,6 +1950,12 @@
       bnd_idx = 2;
       break;
 
+    case BUILT_IN_MEMSET:
+    case BUILT_IN_MEMSET_CHK:
+      dst_idx = 0;
+      bnd_idx = 2;
+      break;
+
     case BUILT_IN_STPCPY:
     case BUILT_IN_STPCPY_CHK:
     case BUILT_IN_STRCPY:
@@ -1796,18 +1986,18 @@
 
   /* DST and SRC can be null for a call with an insufficient number
      of arguments to a built-in function declared without a protype.  */
-  if (!dst || !src)
+  if (!dst || (src_idx < nargs && !src))
     return;
 
   /* DST, SRC, or DSTWR can also have the wrong type in a call to
      a function declared without a prototype.  Avoid checking such
      invalid calls.  */
   if (TREE_CODE (TREE_TYPE (dst)) != POINTER_TYPE
-      || TREE_CODE (TREE_TYPE (src)) != POINTER_TYPE
+      || (src && TREE_CODE (TREE_TYPE (src)) != POINTER_TYPE)
       || (dstwr && !INTEGRAL_TYPE_P (TREE_TYPE (dstwr))))
     return;
 
-  if (check_bounds_or_overlap (call, dst, src, dstwr, NULL_TREE))
+  if (!check_bounds_or_overlap (call, dst, src, dstwr, NULL_TREE))
     return;
 
   /* Avoid diagnosing the call again.  */
@@ -1819,12 +2009,14 @@
 /* Attempt to detect and diagnose invalid offset bounds and (except for
    memmove) overlapping copy in a call expression EXPR from SRC to DST
    and DSTSIZE and SRCSIZE bytes, respectively.  Both DSTSIZE and
-   SRCSIZE may be NULL.  Return false when one or the other has been
-   detected and diagnosed, true otherwise.  */
+   SRCSIZE may be NULL.  DO_WARN is false to detect either problem
+   without issue a warning.  Return the OPT_Wxxx constant corresponding
+   to the warning if one has been detected and zero otherwise.  */
 
-bool
+int
 check_bounds_or_overlap (gimple *call, tree dst, tree src, tree dstsize,
-			 tree srcsize, bool bounds_only /* = false */)
+			 tree srcsize, bool bounds_only /* = false */,
+			 bool do_warn /* = true */)
 {
   location_t loc = gimple_nonartificial_location (call);
   loc = expansion_point_location_if_in_system_header (loc);
@@ -1834,30 +2026,45 @@
   builtin_memref dstref (dst, dstsize);
   builtin_memref srcref (src, srcsize);
 
+  /* Create a descriptor of the access.  This may adjust both DSTREF
+     and SRCREF based on one another and the kind of the access.  */
   builtin_access acs (call, dstref, srcref);
 
   /* Set STRICT to the value of the -Warray-bounds=N argument for
      string functions or when N > 1.  */
   int strict = (acs.strict () || warn_array_bounds > 1 ? warn_array_bounds : 0);
 
-  /* Validate offsets first to make sure they are within the bounds
-     of the destination object if its size is known, or PTRDIFF_MAX
-     otherwise.  */
-  if (maybe_diag_offset_bounds (loc, call, func, strict, dst, dstref)
-      || maybe_diag_offset_bounds (loc, call, func, strict, src, srcref))
+  /* The starting offset of the destination write access.  Nonzero only
+     for the strcat family of functions.  */
+  offset_int wroff = acs.write_off (dstsize);
+
+  /* Validate offsets to each reference before the access first to make
+     sure they are within the bounds of the destination object if its
+     size is known, or PTRDIFF_MAX otherwise.  */
+  if (maybe_diag_access_bounds (loc, call, func, strict, dstref, wroff, do_warn)
+      || maybe_diag_access_bounds (loc, call, func, strict, srcref, 0, do_warn))
     {
-      gimple_set_no_warning (call, true);
-      return false;
+      if (do_warn)
+	gimple_set_no_warning (call, true);
+      return OPT_Warray_bounds;
     }
 
-  bool check_overlap
-    = (warn_restrict
-       && (bounds_only
-	   || (DECL_FUNCTION_CODE (func) != BUILT_IN_MEMMOVE
-	       && DECL_FUNCTION_CODE (func) != BUILT_IN_MEMMOVE_CHK)));
+  if (!warn_restrict || bounds_only || !src)
+    return 0;
 
-  if (!check_overlap)
-    return true;
+  if (!bounds_only)
+    {
+      switch (DECL_FUNCTION_CODE (func))
+	{
+	case BUILT_IN_MEMMOVE:
+	case BUILT_IN_MEMMOVE_CHK:
+	case BUILT_IN_MEMSET:
+	case BUILT_IN_MEMSET_CHK:
+	  return 0;
+	default:
+	  break;
+	}
+    }
 
   if (operand_equal_p (dst, src, 0))
     {
@@ -1871,20 +2078,20 @@
 		      "%G%qD source argument is the same as destination",
 		      call, func);
 	  gimple_set_no_warning (call, true);
-	  return false;
+	  return OPT_Wrestrict;
 	}
 
-      return true;
+      return 0;
     }
 
   /* Return false when overlap has been detected.  */
   if (maybe_diag_overlap (loc, call, acs))
     {
       gimple_set_no_warning (call, true);
-      return false;
+      return OPT_Wrestrict;
     }
 
-  return true;
+  return 0;
 }
 
 gimple_opt_pass *
@@ -1892,3 +2099,76 @@
 {
   return new pass_wrestrict (ctxt);
 }
+
+DEBUG_FUNCTION void
+dump_builtin_memref (FILE *fp, const builtin_memref &ref)
+{
+  fprintf (fp, "\n    ptr = ");
+  print_generic_expr (fp, ref.ptr, TDF_LINENO);
+  fprintf (fp, "\n    ref = ");
+  if (ref.ref)
+    print_generic_expr (fp, ref.ref, TDF_LINENO);
+  else
+    fputs ("null", fp);
+  fprintf (fp, "\n    base = ");
+  print_generic_expr (fp, ref.base, TDF_LINENO);
+  fprintf (fp,
+	   "\n    basesize = %lli"
+	   "\n    refsize = %lli"
+	   "\n    refoff = %lli"
+	   "\n    offrange = [%lli, %lli]"
+	   "\n    sizrange = [%lli, %lli]"
+	   "\n    strbounded_p = %s\n",
+	   (long long)ref.basesize.to_shwi (),
+	   (long long)ref.refsize.to_shwi (),
+	   (long long)ref.refoff.to_shwi (),
+	   (long long)ref.offrange[0].to_shwi (),
+	   (long long)ref.offrange[1].to_shwi (),
+	   (long long)ref.sizrange[0].to_shwi (),
+	   (long long)ref.sizrange[1].to_shwi (),
+	   ref.strbounded_p ? "true" : "false");
+}
+
+void
+builtin_access::dump (FILE *fp) const
+{
+  fprintf (fp, "  dstref:");
+  dump_builtin_memref (fp, *dstref);
+  fprintf (fp, "\n  srcref:");
+  dump_builtin_memref (fp, *srcref);
+
+  fprintf (fp,
+	   "  sizrange = [%lli, %lli]\n"
+	   "  ovloff = [%lli, %lli]\n"
+	   "  ovlsiz = [%lli, %lli]\n"
+	   "  dstoff = [%lli, %lli]\n"
+	   "  dstsiz = [%lli, %lli]\n"
+	   "  srcoff = [%lli, %lli]\n"
+	   "  srcsiz = [%lli, %lli]\n",
+	   (long long)sizrange[0], (long long)sizrange[1],
+	   (long long)ovloff[0], (long long)ovloff[1],
+	   (long long)ovlsiz[0], (long long)ovlsiz[1],
+	   (long long)dstoff[0].to_shwi (), (long long)dstoff[1].to_shwi (),
+	   (long long)dstsiz[0].to_shwi (), (long long)dstsiz[1].to_shwi (),
+	   (long long)srcoff[0].to_shwi (), (long long)srcoff[1].to_shwi (),
+	   (long long)srcsiz[0].to_shwi (), (long long)srcsiz[1].to_shwi ());
+}
+
+DEBUG_FUNCTION void
+dump_builtin_access (FILE *fp, gimple *stmt, const builtin_access &acs)
+{
+  if (stmt)
+    {
+      fprintf (fp, "\nDumping builtin_access for ");
+      print_gimple_expr (fp, stmt, TDF_LINENO);
+      fputs (":\n", fp);
+    }
+
+  acs.dump (fp);
+}
+
+DEBUG_FUNCTION void
+debug (gimple *stmt, const builtin_access &acs)
+{
+  dump_builtin_access (stdout, stmt, acs);
+}