[gcc(refs/users/guojiufu/heads/guojiufu-branch)] Move array bounds checking into its own file.

Jiu Fu Guo guojiufu@gcc.gnu.org
Wed Jun 10 03:20:09 GMT 2020


https://gcc.gnu.org/g:62efd1c481dfd3b9cd69e64a9d6053dd8fcc3382

commit 62efd1c481dfd3b9cd69e64a9d6053dd8fcc3382
Author: Aldy Hernandez <aldyh@redhat.com>
Date:   Sun May 17 15:03:20 2020 +0200

    Move array bounds checking into its own file.
    
    gcc/
            * Makefile.in (gimple-array-bounds.o): New.
            * tree-vrp.c: Move array bounds code...
            * gimple-array-bounds.cc: ...here...
            * gimple-array-bounds.h: ...and here.

Diff:
---
 gcc/Makefile.in            |   1 +
 gcc/gimple-array-bounds.cc | 700 +++++++++++++++++++++++++++++++++++++++++++++
 gcc/gimple-array-bounds.h  |  43 +++
 gcc/tree-vrp.c             | 682 +------------------------------------------
 4 files changed, 745 insertions(+), 681 deletions(-)

diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index a5d0e79ac4d..aab1dbba57b 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1352,6 +1352,7 @@ OBJS = \
 	ggc-common.o \
 	ggc-tests.o \
 	gimple.o \
+	gimple-array-bounds.o \
 	gimple-builder.o \
 	gimple-expr.o \
 	gimple-iterator.o \
diff --git a/gcc/gimple-array-bounds.cc b/gcc/gimple-array-bounds.cc
new file mode 100644
index 00000000000..352d0745178
--- /dev/null
+++ b/gcc/gimple-array-bounds.cc
@@ -0,0 +1,700 @@
+/* Array bounds checking.
+   Copyright (C) 2005-2020 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3, or (at your option)
+any later version.
+
+GCC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "backend.h"
+#include "tree.h"
+#include "gimple.h"
+#include "ssa.h"
+#include "gimple-array-bounds.h"
+#include "gimple-iterator.h"
+#include "gimple-walk.h"
+#include "tree-dfa.h"
+#include "fold-const.h"
+#include "diagnostic-core.h"
+#include "intl.h"
+#include "tree-vrp.h"
+#include "alloc-pool.h"
+#include "vr-values.h"
+#include "domwalk.h"
+#include "tree-cfg.h"
+
+// This purposely returns a value_range, not a value_range_equiv, to
+// break the dependency on equivalences for this pass.
+
+const value_range *
+array_bounds_checker::get_value_range (const_tree op)
+{
+  return ranges->get_value_range (op);
+}
+
+/* Checks one ARRAY_REF in REF, located at LOCUS. Ignores flexible
+   arrays and "struct" hacks. If VRP can determine that the array
+   subscript is a constant, check if it is outside valid range.  If
+   the array subscript is a RANGE, warn if it is non-overlapping with
+   valid range.  IGNORE_OFF_BY_ONE is true if the ARRAY_REF is inside
+   a ADDR_EXPR.  Returns true if a warning has been issued.  */
+
+bool
+array_bounds_checker::check_array_ref (location_t location, tree ref,
+				       bool ignore_off_by_one)
+{
+  if (TREE_NO_WARNING (ref))
+    return false;
+
+  tree low_sub = TREE_OPERAND (ref, 1);
+  tree up_sub = low_sub;
+  tree up_bound = array_ref_up_bound (ref);
+
+  /* Referenced decl if one can be determined.  */
+  tree decl = NULL_TREE;
+
+  /* Set for accesses to interior zero-length arrays.  */
+  bool interior_zero_len = false;
+
+  tree up_bound_p1;
+
+  if (!up_bound
+      || TREE_CODE (up_bound) != INTEGER_CST
+      || (warn_array_bounds < 2
+	  && array_at_struct_end_p (ref)))
+    {
+      /* Accesses to trailing arrays via pointers may access storage
+	 beyond the types array bounds.  For such arrays, or for flexible
+	 array members, as well as for other arrays of an unknown size,
+	 replace the upper bound with a more permissive one that assumes
+	 the size of the largest object is PTRDIFF_MAX.  */
+      tree eltsize = array_ref_element_size (ref);
+
+      if (TREE_CODE (eltsize) != INTEGER_CST
+	  || integer_zerop (eltsize))
+	{
+	  up_bound = NULL_TREE;
+	  up_bound_p1 = NULL_TREE;
+	}
+      else
+	{
+	  tree ptrdiff_max = TYPE_MAX_VALUE (ptrdiff_type_node);
+	  tree maxbound = ptrdiff_max;
+	  tree arg = TREE_OPERAND (ref, 0);
+
+	  const bool compref = TREE_CODE (arg) == COMPONENT_REF;
+	  if (compref)
+	    {
+	      /* Try to determine the size of the trailing array from
+		 its initializer (if it has one).  */
+	      if (tree refsize = component_ref_size (arg, &interior_zero_len))
+		if (TREE_CODE (refsize) == INTEGER_CST)
+		  maxbound = refsize;
+	    }
+
+	  if (maxbound == ptrdiff_max)
+	    {
+	      /* Try to determine the size of the base object.  Avoid
+		 COMPONENT_REF already tried above.  Using its DECL_SIZE
+		 size wouldn't necessarily be correct if the reference is
+		 to its flexible array member initialized in a different
+		 translation unit.  */
+	      poly_int64 off;
+	      if (tree base = get_addr_base_and_unit_offset (arg, &off))
+		{
+		  if (!compref && DECL_P (base))
+		    if (tree basesize = DECL_SIZE_UNIT (base))
+		      if (TREE_CODE (basesize) == INTEGER_CST)
+			{
+			  maxbound = basesize;
+			  decl = base;
+			}
+
+		  if (known_gt (off, 0))
+		    maxbound = wide_int_to_tree (sizetype,
+						 wi::sub (wi::to_wide (maxbound),
+							  off));
+		}
+	    }
+	  else
+	    maxbound = fold_convert (sizetype, maxbound);
+
+	  up_bound_p1 = int_const_binop (TRUNC_DIV_EXPR, maxbound, eltsize);
+
+	  if (up_bound_p1 != NULL_TREE)
+	    up_bound = int_const_binop (MINUS_EXPR, up_bound_p1,
+					build_int_cst (ptrdiff_type_node, 1));
+	  else
+	    up_bound = NULL_TREE;
+	}
+    }
+  else
+    up_bound_p1 = int_const_binop (PLUS_EXPR, up_bound,
+				   build_int_cst (TREE_TYPE (up_bound), 1));
+
+  tree low_bound = array_ref_low_bound (ref);
+
+  tree artype = TREE_TYPE (TREE_OPERAND (ref, 0));
+
+  bool warned = false;
+
+  /* Empty array.  */
+  if (up_bound && tree_int_cst_equal (low_bound, up_bound_p1))
+    warned = warning_at (location, OPT_Warray_bounds,
+			 "array subscript %E is outside array bounds of %qT",
+			 low_sub, artype);
+
+  const value_range *vr = NULL;
+  if (TREE_CODE (low_sub) == SSA_NAME)
+    {
+      vr = get_value_range (low_sub);
+      if (!vr->undefined_p () && !vr->varying_p ())
+	{
+	  low_sub = vr->kind () == VR_RANGE ? vr->max () : vr->min ();
+	  up_sub = vr->kind () == VR_RANGE ? vr->min () : vr->max ();
+	}
+    }
+
+  if (warned)
+    ; /* Do nothing.  */
+  else if (vr && vr->kind () == VR_ANTI_RANGE)
+    {
+      if (up_bound
+	  && TREE_CODE (up_sub) == INTEGER_CST
+	  && (ignore_off_by_one
+	      ? tree_int_cst_lt (up_bound, up_sub)
+	      : tree_int_cst_le (up_bound, up_sub))
+	  && TREE_CODE (low_sub) == INTEGER_CST
+	  && tree_int_cst_le (low_sub, low_bound))
+	warned = warning_at (location, OPT_Warray_bounds,
+			     "array subscript [%E, %E] is outside "
+			     "array bounds of %qT",
+			     low_sub, up_sub, artype);
+    }
+  else if (up_bound
+	   && TREE_CODE (up_sub) == INTEGER_CST
+	   && (ignore_off_by_one
+	       ? !tree_int_cst_le (up_sub, up_bound_p1)
+	       : !tree_int_cst_le (up_sub, up_bound)))
+    warned = warning_at (location, OPT_Warray_bounds,
+			 "array subscript %E is above array bounds of %qT",
+			 up_sub, artype);
+  else if (TREE_CODE (low_sub) == INTEGER_CST
+	   && tree_int_cst_lt (low_sub, low_bound))
+    warned = warning_at (location, OPT_Warray_bounds,
+			 "array subscript %E is below array bounds of %qT",
+			 low_sub, artype);
+
+  if (!warned && interior_zero_len)
+    warned = warning_at (location, OPT_Wzero_length_bounds,
+			 (TREE_CODE (low_sub) == INTEGER_CST
+			  ? G_("array subscript %E is outside the bounds "
+			       "of an interior zero-length array %qT")
+			  : G_("array subscript %qE is outside the bounds "
+			       "of an interior zero-length array %qT")),
+			 low_sub, artype);
+
+  if (warned)
+    {
+      if (dump_file && (dump_flags & TDF_DETAILS))
+	{
+	  fprintf (dump_file, "Array bound warning for ");
+	  dump_generic_expr (MSG_NOTE, TDF_SLIM, ref);
+	  fprintf (dump_file, "\n");
+	}
+
+      ref = decl ? decl : TREE_OPERAND (ref, 0);
+
+      tree rec = NULL_TREE;
+      if (TREE_CODE (ref) == COMPONENT_REF)
+	{
+	  /* For a reference to a member of a struct object also mention
+	     the object if it's known.  It may be defined in a different
+	     function than the out-of-bounds access.  */
+	  rec = TREE_OPERAND (ref, 0);
+	  if (!VAR_P (rec))
+	    rec = NULL_TREE;
+	  ref = TREE_OPERAND (ref, 1);
+	}
+
+      if (DECL_P (ref))
+	inform (DECL_SOURCE_LOCATION (ref), "while referencing %qD", ref);
+      if (rec && DECL_P (rec))
+	inform (DECL_SOURCE_LOCATION (rec), "defined here %qD", rec);
+
+      TREE_NO_WARNING (ref) = 1;
+    }
+
+  return warned;
+}
+
+/* Checks one MEM_REF in REF, located at LOCATION, for out-of-bounds
+   references to string constants.  If VRP can determine that the array
+   subscript is a constant, check if it is outside valid range.
+   If the array subscript is a RANGE, warn if it is non-overlapping
+   with valid range.
+   IGNORE_OFF_BY_ONE is true if the MEM_REF is inside an ADDR_EXPR
+   (used to allow one-past-the-end indices for code that takes
+   the address of the just-past-the-end element of an array).
+   Returns true if a warning has been issued.  */
+
+bool
+array_bounds_checker::check_mem_ref (location_t location, tree ref,
+				     bool ignore_off_by_one)
+{
+  if (TREE_NO_WARNING (ref))
+    return false;
+
+  tree arg = TREE_OPERAND (ref, 0);
+  /* The constant and variable offset of the reference.  */
+  tree cstoff = TREE_OPERAND (ref, 1);
+  tree varoff = NULL_TREE;
+
+  const offset_int maxobjsize = tree_to_shwi (max_object_size ());
+
+  /* The array or string constant bounds in bytes.  Initially set
+     to [-MAXOBJSIZE - 1, MAXOBJSIZE]  until a tighter bound is
+     determined.  */
+  offset_int arrbounds[2] = { -maxobjsize - 1, maxobjsize };
+
+  /* The minimum and maximum intermediate offset.  For a reference
+     to be valid, not only does the final offset/subscript must be
+     in bounds but all intermediate offsets should be as well.
+     GCC may be able to deal gracefully with such out-of-bounds
+     offsets so the checking is only enbaled at -Warray-bounds=2
+     where it may help detect bugs in uses of the intermediate
+     offsets that could otherwise not be detectable.  */
+  offset_int ioff = wi::to_offset (fold_convert (ptrdiff_type_node, cstoff));
+  offset_int extrema[2] = { 0, wi::abs (ioff) };
+
+  /* The range of the byte offset into the reference.  */
+  offset_int offrange[2] = { 0, 0 };
+
+  const value_range *vr = NULL;
+
+  /* Determine the offsets and increment OFFRANGE for the bounds of each.
+     The loop computes the range of the final offset for expressions such
+     as (A + i0 + ... + iN)[CSTOFF] where i0 through iN are SSA_NAMEs in
+     some range.  */
+  const unsigned limit = param_ssa_name_def_chain_limit;
+  for (unsigned n = 0; TREE_CODE (arg) == SSA_NAME && n < limit; ++n)
+    {
+      gimple *def = SSA_NAME_DEF_STMT (arg);
+      if (!is_gimple_assign (def))
+	break;
+
+      tree_code code = gimple_assign_rhs_code (def);
+      if (code == POINTER_PLUS_EXPR)
+	{
+	  arg = gimple_assign_rhs1 (def);
+	  varoff = gimple_assign_rhs2 (def);
+	}
+      else if (code == ASSERT_EXPR)
+	{
+	  arg = TREE_OPERAND (gimple_assign_rhs1 (def), 0);
+	  continue;
+	}
+      else
+	return false;
+
+      /* VAROFF should always be a SSA_NAME here (and not even
+	 INTEGER_CST) but there's no point in taking chances.  */
+      if (TREE_CODE (varoff) != SSA_NAME)
+	break;
+
+      vr = get_value_range (varoff);
+      if (!vr || vr->undefined_p () || vr->varying_p ())
+	break;
+
+      if (!vr->constant_p ())
+	break;
+
+      if (vr->kind () == VR_RANGE)
+	{
+	  offset_int min
+	    = wi::to_offset (fold_convert (ptrdiff_type_node, vr->min ()));
+	  offset_int max
+	    = wi::to_offset (fold_convert (ptrdiff_type_node, vr->max ()));
+	  if (min < max)
+	    {
+	      offrange[0] += min;
+	      offrange[1] += max;
+	    }
+	  else
+	    {
+	      /* When MIN >= MAX, the offset is effectively in a union
+		 of two ranges: [-MAXOBJSIZE -1, MAX] and [MIN, MAXOBJSIZE].
+		 Since there is no way to represent such a range across
+		 additions, conservatively add [-MAXOBJSIZE -1, MAXOBJSIZE]
+		 to OFFRANGE.  */
+	      offrange[0] += arrbounds[0];
+	      offrange[1] += arrbounds[1];
+	    }
+	}
+      else
+	{
+	  /* For an anti-range, analogously to the above, conservatively
+	     add [-MAXOBJSIZE -1, MAXOBJSIZE] to OFFRANGE.  */
+	  offrange[0] += arrbounds[0];
+	  offrange[1] += arrbounds[1];
+	}
+
+      /* Keep track of the minimum and maximum offset.  */
+      if (offrange[1] < 0 && offrange[1] < extrema[0])
+	extrema[0] = offrange[1];
+      if (offrange[0] > 0 && offrange[0] > extrema[1])
+	extrema[1] = offrange[0];
+
+      if (offrange[0] < arrbounds[0])
+	offrange[0] = arrbounds[0];
+
+      if (offrange[1] > arrbounds[1])
+	offrange[1] = arrbounds[1];
+    }
+
+  if (TREE_CODE (arg) == ADDR_EXPR)
+    {
+      arg = TREE_OPERAND (arg, 0);
+      if (TREE_CODE (arg) != STRING_CST
+	  && TREE_CODE (arg) != PARM_DECL
+	  && TREE_CODE (arg) != VAR_DECL)
+	return false;
+    }
+  else
+    return false;
+
+  /* The type of the object being referred to.  It can be an array,
+     string literal, or a non-array type when the MEM_REF represents
+     a reference/subscript via a pointer to an object that is not
+     an element of an array.  Incomplete types are excluded as well
+     because their size is not known.  */
+  tree reftype = TREE_TYPE (arg);
+  if (POINTER_TYPE_P (reftype)
+      || !COMPLETE_TYPE_P (reftype)
+      || TREE_CODE (TYPE_SIZE_UNIT (reftype)) != INTEGER_CST)
+    return false;
+
+  /* Except in declared objects, references to trailing array members
+     of structs and union objects are excluded because MEM_REF doesn't
+     make it possible to identify the member where the reference
+     originated.  */
+  if (RECORD_OR_UNION_TYPE_P (reftype)
+      && (!VAR_P (arg)
+	  || (DECL_EXTERNAL (arg) && array_at_struct_end_p (ref))))
+    return false;
+
+  arrbounds[0] = 0;
+
+  offset_int eltsize;
+  if (TREE_CODE (reftype) == ARRAY_TYPE)
+    {
+      eltsize = wi::to_offset (TYPE_SIZE_UNIT (TREE_TYPE (reftype)));
+      if (tree dom = TYPE_DOMAIN (reftype))
+	{
+	  tree bnds[] = { TYPE_MIN_VALUE (dom), TYPE_MAX_VALUE (dom) };
+	  if (TREE_CODE (arg) == COMPONENT_REF)
+	    {
+	      offset_int size = maxobjsize;
+	      if (tree fldsize = component_ref_size (arg))
+		size = wi::to_offset (fldsize);
+	      arrbounds[1] = wi::lrshift (size, wi::floor_log2 (eltsize));
+	    }
+	  else if (array_at_struct_end_p (arg) || !bnds[0] || !bnds[1])
+	    arrbounds[1] = wi::lrshift (maxobjsize, wi::floor_log2 (eltsize));
+	  else
+	    arrbounds[1] = (wi::to_offset (bnds[1]) - wi::to_offset (bnds[0])
+			    + 1) * eltsize;
+	}
+      else
+	arrbounds[1] = wi::lrshift (maxobjsize, wi::floor_log2 (eltsize));
+
+      /* Determine a tighter bound of the non-array element type.  */
+      tree eltype = TREE_TYPE (reftype);
+      while (TREE_CODE (eltype) == ARRAY_TYPE)
+	eltype = TREE_TYPE (eltype);
+      eltsize = wi::to_offset (TYPE_SIZE_UNIT (eltype));
+    }
+  else
+    {
+      eltsize = 1;
+      tree size = TYPE_SIZE_UNIT (reftype);
+      if (VAR_P (arg))
+	if (tree initsize = DECL_SIZE_UNIT (arg))
+	  if (tree_int_cst_lt (size, initsize))
+	    size = initsize;
+
+      arrbounds[1] = wi::to_offset (size);
+    }
+
+  offrange[0] += ioff;
+  offrange[1] += ioff;
+
+  /* Compute the more permissive upper bound when IGNORE_OFF_BY_ONE
+     is set (when taking the address of the one-past-last element
+     of an array) but always use the stricter bound in diagnostics. */
+  offset_int ubound = arrbounds[1];
+  if (ignore_off_by_one)
+    ubound += 1;
+
+  if (arrbounds[0] == arrbounds[1]
+      || offrange[0] >= ubound
+      || offrange[1] < arrbounds[0])
+    {
+      /* Treat a reference to a non-array object as one to an array
+	 of a single element.  */
+      if (TREE_CODE (reftype) != ARRAY_TYPE)
+	reftype = build_array_type_nelts (reftype, 1);
+
+      /* Extract the element type out of MEM_REF and use its size
+	 to compute the index to print in the diagnostic; arrays
+	 in MEM_REF don't mean anything.  A type with no size like
+	 void is as good as having a size of 1.  */
+      tree type = TREE_TYPE (ref);
+      while (TREE_CODE (type) == ARRAY_TYPE)
+	type = TREE_TYPE (type);
+      if (tree size = TYPE_SIZE_UNIT (type))
+	{
+	  offrange[0] = offrange[0] / wi::to_offset (size);
+	  offrange[1] = offrange[1] / wi::to_offset (size);
+	}
+
+      bool warned;
+      if (offrange[0] == offrange[1])
+	warned = warning_at (location, OPT_Warray_bounds,
+			     "array subscript %wi is outside array bounds "
+			     "of %qT",
+			     offrange[0].to_shwi (), reftype);
+      else
+	warned = warning_at (location, OPT_Warray_bounds,
+			     "array subscript [%wi, %wi] is outside "
+			     "array bounds of %qT",
+			     offrange[0].to_shwi (),
+			     offrange[1].to_shwi (), reftype);
+      if (warned && DECL_P (arg))
+	inform (DECL_SOURCE_LOCATION (arg), "while referencing %qD", arg);
+
+      if (warned)
+	TREE_NO_WARNING (ref) = 1;
+      return warned;
+    }
+
+  if (warn_array_bounds < 2)
+    return false;
+
+  /* At level 2 check also intermediate offsets.  */
+  int i = 0;
+  if (extrema[i] < -arrbounds[1] || extrema[i = 1] > ubound)
+    {
+      HOST_WIDE_INT tmpidx = extrema[i].to_shwi () / eltsize.to_shwi ();
+
+      if (warning_at (location, OPT_Warray_bounds,
+		      "intermediate array offset %wi is outside array bounds "
+		      "of %qT", tmpidx, reftype))
+	{
+	  TREE_NO_WARNING (ref) = 1;
+	  return true;
+	}
+    }
+
+  return false;
+}
+
+/* Searches if the expr T, located at LOCATION computes
+   address of an ARRAY_REF, and call check_array_ref on it.  */
+
+void
+array_bounds_checker::check_addr_expr (location_t location, tree t)
+{
+  /* Check each ARRAY_REF and MEM_REF in the reference chain. */
+  do
+    {
+      bool warned = false;
+      if (TREE_CODE (t) == ARRAY_REF)
+	warned = check_array_ref (location, t, true /*ignore_off_by_one*/);
+      else if (TREE_CODE (t) == MEM_REF)
+	warned = check_mem_ref (location, t, true /*ignore_off_by_one*/);
+
+      if (warned)
+	TREE_NO_WARNING (t) = true;
+
+      t = TREE_OPERAND (t, 0);
+    }
+  while (handled_component_p (t) || TREE_CODE (t) == MEM_REF);
+
+  if (TREE_CODE (t) != MEM_REF
+      || TREE_CODE (TREE_OPERAND (t, 0)) != ADDR_EXPR
+      || TREE_NO_WARNING (t))
+    return;
+
+  tree tem = TREE_OPERAND (TREE_OPERAND (t, 0), 0);
+  tree low_bound, up_bound, el_sz;
+  if (TREE_CODE (TREE_TYPE (tem)) != ARRAY_TYPE
+      || TREE_CODE (TREE_TYPE (TREE_TYPE (tem))) == ARRAY_TYPE
+      || !TYPE_DOMAIN (TREE_TYPE (tem)))
+    return;
+
+  low_bound = TYPE_MIN_VALUE (TYPE_DOMAIN (TREE_TYPE (tem)));
+  up_bound = TYPE_MAX_VALUE (TYPE_DOMAIN (TREE_TYPE (tem)));
+  el_sz = TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (tem)));
+  if (!low_bound
+      || TREE_CODE (low_bound) != INTEGER_CST
+      || !up_bound
+      || TREE_CODE (up_bound) != INTEGER_CST
+      || !el_sz
+      || TREE_CODE (el_sz) != INTEGER_CST)
+    return;
+
+  offset_int idx;
+  if (!mem_ref_offset (t).is_constant (&idx))
+    return;
+
+  bool warned = false;
+  idx = wi::sdiv_trunc (idx, wi::to_offset (el_sz));
+  if (idx < 0)
+    {
+      if (dump_file && (dump_flags & TDF_DETAILS))
+	{
+	  fprintf (dump_file, "Array bound warning for ");
+	  dump_generic_expr (MSG_NOTE, TDF_SLIM, t);
+	  fprintf (dump_file, "\n");
+	}
+      warned = warning_at (location, OPT_Warray_bounds,
+			   "array subscript %wi is below "
+			   "array bounds of %qT",
+			   idx.to_shwi (), TREE_TYPE (tem));
+    }
+  else if (idx > (wi::to_offset (up_bound)
+		  - wi::to_offset (low_bound) + 1))
+    {
+      if (dump_file && (dump_flags & TDF_DETAILS))
+	{
+	  fprintf (dump_file, "Array bound warning for ");
+	  dump_generic_expr (MSG_NOTE, TDF_SLIM, t);
+	  fprintf (dump_file, "\n");
+	}
+      warned = warning_at (location, OPT_Warray_bounds,
+			   "array subscript %wu is above "
+			   "array bounds of %qT",
+			   idx.to_uhwi (), TREE_TYPE (tem));
+    }
+
+  if (warned)
+    {
+      if (DECL_P (t))
+	inform (DECL_SOURCE_LOCATION (t), "while referencing %qD", t);
+
+      TREE_NO_WARNING (t) = 1;
+    }
+}
+
+/* Callback for walk_tree to check a tree for out of bounds array
+   accesses.  The array_bounds_checker class is passed in DATA.  */
+
+tree
+array_bounds_checker::check_array_bounds (tree *tp, int *walk_subtree,
+					  void *data)
+{
+  tree t = *tp;
+  struct walk_stmt_info *wi = (struct walk_stmt_info *) data;
+  location_t location;
+
+  if (EXPR_HAS_LOCATION (t))
+    location = EXPR_LOCATION (t);
+  else
+    location = gimple_location (wi->stmt);
+
+  *walk_subtree = TRUE;
+
+  bool warned = false;
+  array_bounds_checker *checker = (array_bounds_checker *) wi->info;
+  if (TREE_CODE (t) == ARRAY_REF)
+    warned = checker->check_array_ref (location, t,
+				       false/*ignore_off_by_one*/);
+  else if (TREE_CODE (t) == MEM_REF)
+    warned = checker->check_mem_ref (location, t,
+				     false /*ignore_off_by_one*/);
+  else if (TREE_CODE (t) == ADDR_EXPR)
+    {
+      checker->check_addr_expr (location, t);
+      *walk_subtree = FALSE;
+    }
+  /* Propagate the no-warning bit to the outer expression.  */
+  if (warned)
+    TREE_NO_WARNING (t) = true;
+
+  return NULL_TREE;
+}
+
+/* A dom_walker subclass for use by check_all_array_refs, to walk over
+   all statements of all reachable BBs and call check_array_bounds on
+   them.  */
+
+class check_array_bounds_dom_walker : public dom_walker
+{
+public:
+  check_array_bounds_dom_walker (array_bounds_checker *checker)
+    : dom_walker (CDI_DOMINATORS,
+		  /* Discover non-executable edges, preserving EDGE_EXECUTABLE
+		     flags, so that we can merge in information on
+		     non-executable edges from vrp_folder .  */
+		  REACHABLE_BLOCKS_PRESERVING_FLAGS),
+    checker (checker) { }
+  ~check_array_bounds_dom_walker () {}
+
+  edge before_dom_children (basic_block) FINAL OVERRIDE;
+
+private:
+  array_bounds_checker *checker;
+};
+
+/* Implementation of dom_walker::before_dom_children.
+
+   Walk over all statements of BB and call check_array_bounds on them,
+   and determine if there's a unique successor edge.  */
+
+edge
+check_array_bounds_dom_walker::before_dom_children (basic_block bb)
+{
+  gimple_stmt_iterator si;
+  for (si = gsi_start_bb (bb); !gsi_end_p (si); gsi_next (&si))
+    {
+      gimple *stmt = gsi_stmt (si);
+      struct walk_stmt_info wi;
+      if (!gimple_has_location (stmt)
+	  || is_gimple_debug (stmt))
+	continue;
+
+      memset (&wi, 0, sizeof (wi));
+
+      wi.info = checker;
+
+      walk_gimple_op (stmt, array_bounds_checker::check_array_bounds, &wi);
+    }
+
+  /* Determine if there's a unique successor edge, and if so, return
+     that back to dom_walker, ensuring that we don't visit blocks that
+     became unreachable during the VRP propagation
+     (PR tree-optimization/83312).  */
+  return find_taken_edge (bb, NULL_TREE);
+}
+
+void
+array_bounds_checker::check ()
+{
+  check_array_bounds_dom_walker w (this);
+  w.walk (ENTRY_BLOCK_PTR_FOR_FN (fun));
+}
diff --git a/gcc/gimple-array-bounds.h b/gcc/gimple-array-bounds.h
new file mode 100644
index 00000000000..faa227d9005
--- /dev/null
+++ b/gcc/gimple-array-bounds.h
@@ -0,0 +1,43 @@
+/* Array bounds checking.
+   Copyright (C) 2020 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3, or (at your option)
+any later version.
+
+GCC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#ifndef GCC_GIMPLE_ARRAY_BOUNDS_H
+#define GCC_GIMPLE_ARRAY_BOUNDS_H
+
+class array_bounds_checker
+{
+  friend class check_array_bounds_dom_walker;
+
+public:
+  array_bounds_checker (struct function *fun, class vr_values *v)
+    : fun (fun), ranges (v) { }
+  void check ();
+
+private:
+  static tree check_array_bounds (tree *tp, int *walk_subtree, void *data);
+  bool check_array_ref (location_t, tree, bool ignore_off_by_one);
+  bool check_mem_ref (location_t, tree, bool ignore_off_by_one);
+  void check_addr_expr (location_t, tree);
+  const value_range *get_value_range (const_tree op);
+
+  struct function *fun;
+  class vr_values *ranges;
+};
+
+#endif // GCC_GIMPLE_ARRAY_BOUNDS_H
diff --git a/gcc/tree-vrp.c b/gcc/tree-vrp.c
index e529a7184e8..811fe0d20bb 100644
--- a/gcc/tree-vrp.c
+++ b/gcc/tree-vrp.c
@@ -31,7 +31,6 @@ along with GCC; see the file COPYING3.  If not see
 #include "ssa.h"
 #include "optabs-tree.h"
 #include "gimple-pretty-print.h"
-#include "diagnostic-core.h"
 #include "flags.h"
 #include "fold-const.h"
 #include "stor-layout.h"
@@ -42,13 +41,11 @@ along with GCC; see the file COPYING3.  If not see
 #include "gimple-iterator.h"
 #include "gimple-walk.h"
 #include "tree-cfg.h"
-#include "tree-dfa.h"
 #include "tree-ssa-loop-manip.h"
 #include "tree-ssa-loop-niter.h"
 #include "tree-ssa-loop.h"
 #include "tree-into-ssa.h"
 #include "tree-ssa.h"
-#include "intl.h"
 #include "cfgloop.h"
 #include "tree-scalar-evolution.h"
 #include "tree-ssa-propagate.h"
@@ -68,6 +65,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "builtins.h"
 #include "range-op.h"
 #include "value-range-equiv.h"
+#include "gimple-array-bounds.h"
 
 /* Set of SSA names found live during the RPO traversal of the function
    for still active basic-blocks.  */
@@ -3398,684 +3396,6 @@ private:
     { vr_values.extract_range_from_phi_node (phi, vr); }
 };
 
-/* Array bounds checking pass.  */
-
-class array_bounds_checker
-{
-  friend class check_array_bounds_dom_walker;
-
-public:
-  array_bounds_checker (struct function *fun, class vr_values *v)
-    : fun (fun), ranges (v) { }
-  void check ();
-
-private:
-  static tree check_array_bounds (tree *tp, int *walk_subtree, void *data);
-  bool check_array_ref (location_t, tree, bool ignore_off_by_one);
-  bool check_mem_ref (location_t, tree, bool ignore_off_by_one);
-  void check_addr_expr (location_t, tree);
-  const value_range_equiv *get_value_range (const_tree op)
-    { return ranges->get_value_range (op); }
-  struct function *fun;
-  class vr_values *ranges;
-};
-
-/* Checks one ARRAY_REF in REF, located at LOCUS. Ignores flexible arrays
-   and "struct" hacks. If VRP can determine that the
-   array subscript is a constant, check if it is outside valid
-   range. If the array subscript is a RANGE, warn if it is
-   non-overlapping with valid range.
-   IGNORE_OFF_BY_ONE is true if the ARRAY_REF is inside a ADDR_EXPR.
-   Returns true if a warning has been issued.  */
-
-bool
-array_bounds_checker::check_array_ref (location_t location, tree ref,
-				       bool ignore_off_by_one)
-{
-  if (TREE_NO_WARNING (ref))
-    return false;
-
-  tree low_sub = TREE_OPERAND (ref, 1);
-  tree up_sub = low_sub;
-  tree up_bound = array_ref_up_bound (ref);
-
-  /* Referenced decl if one can be determined.  */
-  tree decl = NULL_TREE;
-
-  /* Set for accesses to interior zero-length arrays.  */
-  bool interior_zero_len = false;
-
-  tree up_bound_p1;
-
-  if (!up_bound
-      || TREE_CODE (up_bound) != INTEGER_CST
-      || (warn_array_bounds < 2
-	  && array_at_struct_end_p (ref)))
-    {
-      /* Accesses to trailing arrays via pointers may access storage
-	 beyond the types array bounds.  For such arrays, or for flexible
-	 array members, as well as for other arrays of an unknown size,
-	 replace the upper bound with a more permissive one that assumes
-	 the size of the largest object is PTRDIFF_MAX.  */
-      tree eltsize = array_ref_element_size (ref);
-
-      if (TREE_CODE (eltsize) != INTEGER_CST
-	  || integer_zerop (eltsize))
-	{
-	  up_bound = NULL_TREE;
-	  up_bound_p1 = NULL_TREE;
-	}
-      else
-	{
-	  tree ptrdiff_max = TYPE_MAX_VALUE (ptrdiff_type_node);
-	  tree maxbound = ptrdiff_max;
-	  tree arg = TREE_OPERAND (ref, 0);
-
-	  const bool compref = TREE_CODE (arg) == COMPONENT_REF;
-	  if (compref)
-	    {
-	      /* Try to determine the size of the trailing array from
-		 its initializer (if it has one).  */
-	      if (tree refsize = component_ref_size (arg, &interior_zero_len))
-		if (TREE_CODE (refsize) == INTEGER_CST)
-		  maxbound = refsize;
-	    }
-
-	  if (maxbound == ptrdiff_max)
-	    {
-	      /* Try to determine the size of the base object.  Avoid
-		 COMPONENT_REF already tried above.  Using its DECL_SIZE
-		 size wouldn't necessarily be correct if the reference is
-		 to its flexible array member initialized in a different
-		 translation unit.  */
-	      poly_int64 off;
-	      if (tree base = get_addr_base_and_unit_offset (arg, &off))
-		{
-		  if (!compref && DECL_P (base))
-		    if (tree basesize = DECL_SIZE_UNIT (base))
-		      if (TREE_CODE (basesize) == INTEGER_CST)
-			{
-			  maxbound = basesize;
-			  decl = base;
-			}
-
-		  if (known_gt (off, 0))
-		    maxbound = wide_int_to_tree (sizetype,
-						 wi::sub (wi::to_wide (maxbound),
-							  off));
-		}
-	    }
-	  else
-	    maxbound = fold_convert (sizetype, maxbound);
-
-	  up_bound_p1 = int_const_binop (TRUNC_DIV_EXPR, maxbound, eltsize);
-
-	  if (up_bound_p1 != NULL_TREE)
-	    up_bound = int_const_binop (MINUS_EXPR, up_bound_p1,
-					build_int_cst (ptrdiff_type_node, 1));
-	  else
-	    up_bound = NULL_TREE;
-	}
-    }
-  else
-    up_bound_p1 = int_const_binop (PLUS_EXPR, up_bound,
-				   build_int_cst (TREE_TYPE (up_bound), 1));
-
-  tree low_bound = array_ref_low_bound (ref);
-
-  tree artype = TREE_TYPE (TREE_OPERAND (ref, 0));
-
-  bool warned = false;
-
-  /* Empty array.  */
-  if (up_bound && tree_int_cst_equal (low_bound, up_bound_p1))
-    warned = warning_at (location, OPT_Warray_bounds,
-			 "array subscript %E is outside array bounds of %qT",
-			 low_sub, artype);
-
-  const value_range_equiv *vr = NULL;
-  if (TREE_CODE (low_sub) == SSA_NAME)
-    {
-      vr = get_value_range (low_sub);
-      if (!vr->undefined_p () && !vr->varying_p ())
-        {
-          low_sub = vr->kind () == VR_RANGE ? vr->max () : vr->min ();
-          up_sub = vr->kind () == VR_RANGE ? vr->min () : vr->max ();
-        }
-    }
-
-  if (warned)
-    ; /* Do nothing.  */
-  else if (vr && vr->kind () == VR_ANTI_RANGE)
-    {
-      if (up_bound
-	  && TREE_CODE (up_sub) == INTEGER_CST
-          && (ignore_off_by_one
-	      ? tree_int_cst_lt (up_bound, up_sub)
-	      : tree_int_cst_le (up_bound, up_sub))
-          && TREE_CODE (low_sub) == INTEGER_CST
-          && tree_int_cst_le (low_sub, low_bound))
-	warned = warning_at (location, OPT_Warray_bounds,
-			     "array subscript [%E, %E] is outside "
-			     "array bounds of %qT",
-			     low_sub, up_sub, artype);
-    }
-  else if (up_bound
-	   && TREE_CODE (up_sub) == INTEGER_CST
-	   && (ignore_off_by_one
-	       ? !tree_int_cst_le (up_sub, up_bound_p1)
-	       : !tree_int_cst_le (up_sub, up_bound)))
-    warned = warning_at (location, OPT_Warray_bounds,
-			 "array subscript %E is above array bounds of %qT",
-			 up_sub, artype);
-  else if (TREE_CODE (low_sub) == INTEGER_CST
-           && tree_int_cst_lt (low_sub, low_bound))
-    warned = warning_at (location, OPT_Warray_bounds,
-			 "array subscript %E is below array bounds of %qT",
-			 low_sub, artype);
-
-  if (!warned && interior_zero_len)
-    warned = warning_at (location, OPT_Wzero_length_bounds,
-			 (TREE_CODE (low_sub) == INTEGER_CST
-			  ? G_("array subscript %E is outside the bounds "
-			       "of an interior zero-length array %qT")
-			  : G_("array subscript %qE is outside the bounds "
-			       "of an interior zero-length array %qT")),
-			 low_sub, artype);
-
-  if (warned)
-    {
-      if (dump_file && (dump_flags & TDF_DETAILS))
-	{
-	  fprintf (dump_file, "Array bound warning for ");
-	  dump_generic_expr (MSG_NOTE, TDF_SLIM, ref);
-	  fprintf (dump_file, "\n");
-	}
-
-      ref = decl ? decl : TREE_OPERAND (ref, 0);
-
-      tree rec = NULL_TREE;
-      if (TREE_CODE (ref) == COMPONENT_REF)
-	{
-	  /* For a reference to a member of a struct object also mention
-	     the object if it's known.  It may be defined in a different
-	     function than the out-of-bounds access.  */
-	  rec = TREE_OPERAND (ref, 0);
-	  if (!VAR_P (rec))
-	    rec = NULL_TREE;
-	  ref = TREE_OPERAND (ref, 1);
-	}
-
-      if (DECL_P (ref))
-	inform (DECL_SOURCE_LOCATION (ref), "while referencing %qD", ref);
-      if (rec && DECL_P (rec))
-	inform (DECL_SOURCE_LOCATION (rec), "defined here %qD", rec);
-
-      TREE_NO_WARNING (ref) = 1;
-    }
-
-  return warned;
-}
-
-/* Checks one MEM_REF in REF, located at LOCATION, for out-of-bounds
-   references to string constants.  If VRP can determine that the array
-   subscript is a constant, check if it is outside valid range.
-   If the array subscript is a RANGE, warn if it is non-overlapping
-   with valid range.
-   IGNORE_OFF_BY_ONE is true if the MEM_REF is inside an ADDR_EXPR
-   (used to allow one-past-the-end indices for code that takes
-   the address of the just-past-the-end element of an array).
-   Returns true if a warning has been issued.  */
-
-bool
-array_bounds_checker::check_mem_ref (location_t location, tree ref,
-				     bool ignore_off_by_one)
-{
-  if (TREE_NO_WARNING (ref))
-    return false;
-
-  tree arg = TREE_OPERAND (ref, 0);
-  /* The constant and variable offset of the reference.  */
-  tree cstoff = TREE_OPERAND (ref, 1);
-  tree varoff = NULL_TREE;
-
-  const offset_int maxobjsize = tree_to_shwi (max_object_size ());
-
-  /* The array or string constant bounds in bytes.  Initially set
-     to [-MAXOBJSIZE - 1, MAXOBJSIZE]  until a tighter bound is
-     determined.  */
-  offset_int arrbounds[2] = { -maxobjsize - 1, maxobjsize };
-
-  /* The minimum and maximum intermediate offset.  For a reference
-     to be valid, not only does the final offset/subscript must be
-     in bounds but all intermediate offsets should be as well.
-     GCC may be able to deal gracefully with such out-of-bounds
-     offsets so the checking is only enbaled at -Warray-bounds=2
-     where it may help detect bugs in uses of the intermediate
-     offsets that could otherwise not be detectable.  */
-  offset_int ioff = wi::to_offset (fold_convert (ptrdiff_type_node, cstoff));
-  offset_int extrema[2] = { 0, wi::abs (ioff) };
-
-  /* The range of the byte offset into the reference.  */
-  offset_int offrange[2] = { 0, 0 };
-
-  const value_range_equiv *vr = NULL;
-
-  /* Determine the offsets and increment OFFRANGE for the bounds of each.
-     The loop computes the range of the final offset for expressions such
-     as (A + i0 + ... + iN)[CSTOFF] where i0 through iN are SSA_NAMEs in
-     some range.  */
-  const unsigned limit = param_ssa_name_def_chain_limit;
-  for (unsigned n = 0; TREE_CODE (arg) == SSA_NAME && n < limit; ++n)
-    {
-      gimple *def = SSA_NAME_DEF_STMT (arg);
-      if (!is_gimple_assign (def))
-	break;
-
-      tree_code code = gimple_assign_rhs_code (def);
-      if (code == POINTER_PLUS_EXPR)
-	{
-	  arg = gimple_assign_rhs1 (def);
-	  varoff = gimple_assign_rhs2 (def);
-	}
-      else if (code == ASSERT_EXPR)
-	{
-	  arg = TREE_OPERAND (gimple_assign_rhs1 (def), 0);
-	  continue;
-	}
-      else
-	return false;
-
-      /* VAROFF should always be a SSA_NAME here (and not even
-	 INTEGER_CST) but there's no point in taking chances.  */
-      if (TREE_CODE (varoff) != SSA_NAME)
-	break;
-
-      vr = get_value_range (varoff);
-      if (!vr || vr->undefined_p () || vr->varying_p ())
-	break;
-
-      if (!vr->constant_p ())
-        break;
-
-      if (vr->kind () == VR_RANGE)
-	{
-	  offset_int min
-	    = wi::to_offset (fold_convert (ptrdiff_type_node, vr->min ()));
-	  offset_int max
-	    = wi::to_offset (fold_convert (ptrdiff_type_node, vr->max ()));
-	  if (min < max)
-	    {
-	      offrange[0] += min;
-	      offrange[1] += max;
-	    }
-	  else
-	    {
-	      /* When MIN >= MAX, the offset is effectively in a union
-		 of two ranges: [-MAXOBJSIZE -1, MAX] and [MIN, MAXOBJSIZE].
-		 Since there is no way to represent such a range across
-		 additions, conservatively add [-MAXOBJSIZE -1, MAXOBJSIZE]
-		 to OFFRANGE.  */
-	      offrange[0] += arrbounds[0];
-	      offrange[1] += arrbounds[1];
-	    }
-	}
-      else
-	{
-	  /* For an anti-range, analogously to the above, conservatively
-	     add [-MAXOBJSIZE -1, MAXOBJSIZE] to OFFRANGE.  */
-	  offrange[0] += arrbounds[0];
-	  offrange[1] += arrbounds[1];
-	}
-
-      /* Keep track of the minimum and maximum offset.  */
-      if (offrange[1] < 0 && offrange[1] < extrema[0])
-	extrema[0] = offrange[1];
-      if (offrange[0] > 0 && offrange[0] > extrema[1])
-	extrema[1] = offrange[0];
-
-      if (offrange[0] < arrbounds[0])
-	offrange[0] = arrbounds[0];
-
-      if (offrange[1] > arrbounds[1])
-	offrange[1] = arrbounds[1];
-    }
-
-  if (TREE_CODE (arg) == ADDR_EXPR)
-    {
-      arg = TREE_OPERAND (arg, 0);
-      if (TREE_CODE (arg) != STRING_CST
-	  && TREE_CODE (arg) != PARM_DECL
-	  && TREE_CODE (arg) != VAR_DECL)
-	return false;
-    }
-  else
-    return false;
-
-  /* The type of the object being referred to.  It can be an array,
-     string literal, or a non-array type when the MEM_REF represents
-     a reference/subscript via a pointer to an object that is not
-     an element of an array.  Incomplete types are excluded as well
-     because their size is not known.  */
-  tree reftype = TREE_TYPE (arg);
-  if (POINTER_TYPE_P (reftype)
-      || !COMPLETE_TYPE_P (reftype)
-      || TREE_CODE (TYPE_SIZE_UNIT (reftype)) != INTEGER_CST)
-    return false;
-
-  /* Except in declared objects, references to trailing array members
-     of structs and union objects are excluded because MEM_REF doesn't
-     make it possible to identify the member where the reference
-     originated.  */
-  if (RECORD_OR_UNION_TYPE_P (reftype)
-      && (!VAR_P (arg)
-	  || (DECL_EXTERNAL (arg) && array_at_struct_end_p (ref))))
-    return false;
-
-  arrbounds[0] = 0;
-
-  offset_int eltsize;
-  if (TREE_CODE (reftype) == ARRAY_TYPE)
-    {
-      eltsize = wi::to_offset (TYPE_SIZE_UNIT (TREE_TYPE (reftype)));
-      if (tree dom = TYPE_DOMAIN (reftype))
-	{
-	  tree bnds[] = { TYPE_MIN_VALUE (dom), TYPE_MAX_VALUE (dom) };
-	  if (TREE_CODE (arg) == COMPONENT_REF)
-	    {
-	      offset_int size = maxobjsize;
-	      if (tree fldsize = component_ref_size (arg))
-		size = wi::to_offset (fldsize);
-	      arrbounds[1] = wi::lrshift (size, wi::floor_log2 (eltsize));
-	    }
-	  else if (array_at_struct_end_p (arg) || !bnds[0] || !bnds[1])
-	    arrbounds[1] = wi::lrshift (maxobjsize, wi::floor_log2 (eltsize));
-	  else
-	    arrbounds[1] = (wi::to_offset (bnds[1]) - wi::to_offset (bnds[0])
-			    + 1) * eltsize;
-	}
-      else
-	arrbounds[1] = wi::lrshift (maxobjsize, wi::floor_log2 (eltsize));
-
-      /* Determine a tighter bound of the non-array element type.  */
-      tree eltype = TREE_TYPE (reftype);
-      while (TREE_CODE (eltype) == ARRAY_TYPE)
-	eltype = TREE_TYPE (eltype);
-      eltsize = wi::to_offset (TYPE_SIZE_UNIT (eltype));
-    }
-  else
-    {
-      eltsize = 1;
-      tree size = TYPE_SIZE_UNIT (reftype);
-      if (VAR_P (arg))
-	if (tree initsize = DECL_SIZE_UNIT (arg))
-	  if (tree_int_cst_lt (size, initsize))
-	    size = initsize;
-
-      arrbounds[1] = wi::to_offset (size);
-    }
-
-  offrange[0] += ioff;
-  offrange[1] += ioff;
-
-  /* Compute the more permissive upper bound when IGNORE_OFF_BY_ONE
-     is set (when taking the address of the one-past-last element
-     of an array) but always use the stricter bound in diagnostics. */
-  offset_int ubound = arrbounds[1];
-  if (ignore_off_by_one)
-    ubound += 1;
-
-  if (arrbounds[0] == arrbounds[1]
-      || offrange[0] >= ubound
-      || offrange[1] < arrbounds[0])
-    {
-      /* Treat a reference to a non-array object as one to an array
-	 of a single element.  */
-      if (TREE_CODE (reftype) != ARRAY_TYPE)
-	reftype = build_array_type_nelts (reftype, 1);
-
-      /* Extract the element type out of MEM_REF and use its size
-	 to compute the index to print in the diagnostic; arrays
-	 in MEM_REF don't mean anything.  A type with no size like
-	 void is as good as having a size of 1.  */
-      tree type = TREE_TYPE (ref);
-      while (TREE_CODE (type) == ARRAY_TYPE)
-	type = TREE_TYPE (type);
-      if (tree size = TYPE_SIZE_UNIT (type))
-	{
-	  offrange[0] = offrange[0] / wi::to_offset (size);
-	  offrange[1] = offrange[1] / wi::to_offset (size);
-	}
-
-      bool warned;
-      if (offrange[0] == offrange[1])
-	warned = warning_at (location, OPT_Warray_bounds,
-			     "array subscript %wi is outside array bounds "
-			     "of %qT",
-			     offrange[0].to_shwi (), reftype);
-      else
-	warned = warning_at (location, OPT_Warray_bounds,
-			     "array subscript [%wi, %wi] is outside "
-			     "array bounds of %qT",
-			     offrange[0].to_shwi (),
-			     offrange[1].to_shwi (), reftype);
-      if (warned && DECL_P (arg))
-	inform (DECL_SOURCE_LOCATION (arg), "while referencing %qD", arg);
-
-      if (warned)
-	TREE_NO_WARNING (ref) = 1;
-      return warned;
-    }
-
-  if (warn_array_bounds < 2)
-    return false;
-
-  /* At level 2 check also intermediate offsets.  */
-  int i = 0;
-  if (extrema[i] < -arrbounds[1] || extrema[i = 1] > ubound)
-    {
-      HOST_WIDE_INT tmpidx = extrema[i].to_shwi () / eltsize.to_shwi ();
-
-      if (warning_at (location, OPT_Warray_bounds,
-		      "intermediate array offset %wi is outside array bounds "
-		      "of %qT", tmpidx, reftype))
-	{
-	  TREE_NO_WARNING (ref) = 1;
-	  return true;
-	}
-    }
-
-  return false;
-}
-
-/* Searches if the expr T, located at LOCATION computes
-   address of an ARRAY_REF, and call check_array_ref on it.  */
-
-void
-array_bounds_checker::check_addr_expr (location_t location, tree t)
-{
-  /* Check each ARRAY_REF and MEM_REF in the reference chain. */
-  do
-    {
-      bool warned = false;
-      if (TREE_CODE (t) == ARRAY_REF)
-	warned = check_array_ref (location, t, true /*ignore_off_by_one*/);
-      else if (TREE_CODE (t) == MEM_REF)
-	warned = check_mem_ref (location, t, true /*ignore_off_by_one*/);
-
-      if (warned)
-	TREE_NO_WARNING (t) = true;
-
-      t = TREE_OPERAND (t, 0);
-    }
-  while (handled_component_p (t) || TREE_CODE (t) == MEM_REF);
-
-  if (TREE_CODE (t) != MEM_REF
-      || TREE_CODE (TREE_OPERAND (t, 0)) != ADDR_EXPR
-      || TREE_NO_WARNING (t))
-    return;
-
-  tree tem = TREE_OPERAND (TREE_OPERAND (t, 0), 0);
-  tree low_bound, up_bound, el_sz;
-  if (TREE_CODE (TREE_TYPE (tem)) != ARRAY_TYPE
-      || TREE_CODE (TREE_TYPE (TREE_TYPE (tem))) == ARRAY_TYPE
-      || !TYPE_DOMAIN (TREE_TYPE (tem)))
-    return;
-
-  low_bound = TYPE_MIN_VALUE (TYPE_DOMAIN (TREE_TYPE (tem)));
-  up_bound = TYPE_MAX_VALUE (TYPE_DOMAIN (TREE_TYPE (tem)));
-  el_sz = TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (tem)));
-  if (!low_bound
-      || TREE_CODE (low_bound) != INTEGER_CST
-      || !up_bound
-      || TREE_CODE (up_bound) != INTEGER_CST
-      || !el_sz
-      || TREE_CODE (el_sz) != INTEGER_CST)
-    return;
-
-  offset_int idx;
-  if (!mem_ref_offset (t).is_constant (&idx))
-    return;
-
-  bool warned = false;
-  idx = wi::sdiv_trunc (idx, wi::to_offset (el_sz));
-  if (idx < 0)
-    {
-      if (dump_file && (dump_flags & TDF_DETAILS))
-	{
-	  fprintf (dump_file, "Array bound warning for ");
-	  dump_generic_expr (MSG_NOTE, TDF_SLIM, t);
-	  fprintf (dump_file, "\n");
-	}
-      warned = warning_at (location, OPT_Warray_bounds,
-			   "array subscript %wi is below "
-			   "array bounds of %qT",
-			   idx.to_shwi (), TREE_TYPE (tem));
-    }
-  else if (idx > (wi::to_offset (up_bound)
-		  - wi::to_offset (low_bound) + 1))
-    {
-      if (dump_file && (dump_flags & TDF_DETAILS))
-	{
-	  fprintf (dump_file, "Array bound warning for ");
-	  dump_generic_expr (MSG_NOTE, TDF_SLIM, t);
-	  fprintf (dump_file, "\n");
-	}
-      warned = warning_at (location, OPT_Warray_bounds,
-			   "array subscript %wu is above "
-			   "array bounds of %qT",
-			   idx.to_uhwi (), TREE_TYPE (tem));
-    }
-
-  if (warned)
-    {
-      if (DECL_P (t))
-	inform (DECL_SOURCE_LOCATION (t), "while referencing %qD", t);
-
-      TREE_NO_WARNING (t) = 1;
-    }
-}
-
-/* Callback for walk_tree to check a tree for out of bounds array
-   accesses.  The array_bounds_checker class is passed in DATA.  */
-
-tree
-array_bounds_checker::check_array_bounds (tree *tp, int *walk_subtree,
-					  void *data)
-{
-  tree t = *tp;
-  struct walk_stmt_info *wi = (struct walk_stmt_info *) data;
-  location_t location;
-
-  if (EXPR_HAS_LOCATION (t))
-    location = EXPR_LOCATION (t);
-  else
-    location = gimple_location (wi->stmt);
-
-  *walk_subtree = TRUE;
-
-  bool warned = false;
-  array_bounds_checker *checker = (array_bounds_checker *) wi->info;
-  if (TREE_CODE (t) == ARRAY_REF)
-    warned = checker->check_array_ref (location, t,
-				       false/*ignore_off_by_one*/);
-  else if (TREE_CODE (t) == MEM_REF)
-    warned = checker->check_mem_ref (location, t,
-				     false /*ignore_off_by_one*/);
-  else if (TREE_CODE (t) == ADDR_EXPR)
-    {
-      checker->check_addr_expr (location, t);
-      *walk_subtree = FALSE;
-    }
-  /* Propagate the no-warning bit to the outer expression.  */
-  if (warned)
-    TREE_NO_WARNING (t) = true;
-
-  return NULL_TREE;
-}
-
-/* A dom_walker subclass for use by check_all_array_refs, to walk over
-   all statements of all reachable BBs and call check_array_bounds on
-   them.  */
-
-class check_array_bounds_dom_walker : public dom_walker
-{
-public:
-  check_array_bounds_dom_walker (array_bounds_checker *checker)
-    : dom_walker (CDI_DOMINATORS,
-		  /* Discover non-executable edges, preserving EDGE_EXECUTABLE
-		     flags, so that we can merge in information on
-		     non-executable edges from vrp_folder .  */
-		  REACHABLE_BLOCKS_PRESERVING_FLAGS),
-    checker (checker) { }
-  ~check_array_bounds_dom_walker () {}
-
-  edge before_dom_children (basic_block) FINAL OVERRIDE;
-
-private:
-  array_bounds_checker *checker;
-};
-
-/* Implementation of dom_walker::before_dom_children.
-
-   Walk over all statements of BB and call check_array_bounds on them,
-   and determine if there's a unique successor edge.  */
-
-edge
-check_array_bounds_dom_walker::before_dom_children (basic_block bb)
-{
-  gimple_stmt_iterator si;
-  for (si = gsi_start_bb (bb); !gsi_end_p (si); gsi_next (&si))
-    {
-      gimple *stmt = gsi_stmt (si);
-      struct walk_stmt_info wi;
-      if (!gimple_has_location (stmt)
-	  || is_gimple_debug (stmt))
-	continue;
-
-      memset (&wi, 0, sizeof (wi));
-
-      wi.info = checker;
-
-      walk_gimple_op (stmt, array_bounds_checker::check_array_bounds, &wi);
-    }
-
-  /* Determine if there's a unique successor edge, and if so, return
-     that back to dom_walker, ensuring that we don't visit blocks that
-     became unreachable during the VRP propagation
-     (PR tree-optimization/83312).  */
-  return find_taken_edge (bb, NULL_TREE);
-}
-
-/* Entry point into array bounds checking pass.  */
-
-void
-array_bounds_checker::check ()
-{
-  check_array_bounds_dom_walker w (this);
-  w.walk (ENTRY_BLOCK_PTR_FOR_FN (fun));
-}
-
 /* Return true if all imm uses of VAR are either in STMT, or
    feed (optionally through a chain of single imm uses) GIMPLE_COND
    in basic block COND_BB.  */


More information about the Gcc-cvs mailing list