This is the mail archive of the
gcc@gcc.gnu.org
mailing list for the GCC project.
Re: [RFC] Adjusted VRP
- From: Richard Biener <richard dot guenther at gmail dot com>
- To: Marat Zakirov <m dot zakirov at samsung dot com>
- Cc: GCC Mailing List <gcc at gcc dot gnu dot org>, Jakub Jelinek <jakub at redhat dot com>, Yury Gribov <y dot gribov at samsung dot com>, Maksim Ostapenko <m dot ostapenko at partner dot samsung dot com>
- Date: Thu, 30 Oct 2014 11:27:44 +0100
- Subject: Re: [RFC] Adjusted VRP
- Authentication-results: sourceware.org; auth=none
- References: <5451093F dot 2090905 at samsung dot com>
On Wed, Oct 29, 2014 at 4:35 PM, Marat Zakirov <m.zakirov@samsung.com> wrote:
> Hi folks!
>
> During asan performance tunning we tried to use VRP and found that it is
> path-insensitive and thus suboptimal. In example bellow "i" has VRP range
> 0..1000 across whole program but in loop body it is just 0..999.
>
> int a[1000];
> void foo ()
> {
> for (int i=0;i<1000;i++)
> a[i] = 0;
> }
>
> I think that path-sensitive approach could significantly increase VRP
> precision. I suggest to extend existing get_range_info (tree t) user
> interface by get_range_info (tree t, basic_block = NULL). So if user do not
> want specify basic_block he will get usual range info. In case when bb is
> specified if hash<pair<tree, basic_block>> has entry - bblock-accurate range
> info will be returned, usual otherwise. The goal of adjustment algorithm is
> to fill hash<pair<tree, basic_block>> where tree is a SSA which has
> bblock-accurate VRP. Memory usage of hash<pair<tree, basic_block>> could be
> reduced by memorizing just important values such as loop iterators, array
> indexes etc.
>
> I propose two approaches to fill up hash<pair<tree, basic_block>>. First one
> is built on top of existing VRP pass. Special CFG RPO iterative traverse
> truncates and stores VRP in every basic block during CFG traversal. It
> merges VRP on join bblocks and truncates on conditional bblocks. A draft
> implementation of this approach is in the attached patch. The patch was
> developed for asan but VRP enhancements are generic.
>
> Second approach is about making pre and post pass for VRP pass. Pre-pass
> splits every tree "i" in every conditional bblock ending with "if (i)" and
> build new phis to merge splited versions of each tree "i". After ordinary
> VRP pass did its job post-pass extract path sensitive info from tree copies
> build in pre-pass during gluing original trees with their copies.
>
> Second approach could be illustrated by using IR of simple example from the
> above.
> IR before transformation:
>
> int a[1000];
> void foo ()
> {
> int i_0;
> int i_1;
> int i_2;
>
> bb 0:
> i_0 = 0;
>
> bb 1:
> i_1 = PHI (i_2,i_0));
> if (i_1 == 1000)
> goto bb 3;
> else
> goto bb 2;
>
> bb 2:
> a[i_1] = 1;
> i_2 = i_1 + 1;
> goto bb 1;
>
> bb 3:
> exit;
> }
>
> IR after pre-pass:
>
> int a[1000];
> void foo ()
> {
> int i_0;
> int i_1;
> int i_2;
> int i_3;
> int i_4;
>
> bb 0:
> i_0 = 0;
>
> bb 1:
> i_1 = PHI (i_2,i_0));
> if (i_1 == 1000)
> goto bb 3;
> else
> goto bb 2;
>
> bb 2:
> i_3 = i_1; << i_1 was splitted
> a[i_3] = 1;
> i_2 = i_3 + 1;
> goto bb 1;
>
> bb 3:
> i_4 = i_1; << i_1 was splitted
> exit;
> }
>
> After ordinary VRP analysis SSA variables would have following ranges:
>
> i_0 = 0
> i_1 = [0..1000]
> i_2 = [1..999]
> i_3 = [0..999]
> i_4 = 1000
>
> After VRP adjustments gathering during copies deletion following info will
> be available: get_vrp (i_1, bb 2) = get_vrp (i_3) = [0..999] and get_vrp
> (i_1, bb 3) = get_vrp (i_4) = 1000
>
> First approach is less precise because it uses small and simple subset of
> ordinary VRP traverses. Second approach requires accurate SSA splitting with
> possibly complex def-use corrections. There is also third approach - just
> fix existing VRP. It will require unnecessary complication of well debugged
> code. At the same time, it won't be more accurate than second approach.
>
> We can teach CFG transformations (bblock splitting, etc.) to propagate
> collected info but even without that propagation range info remains correct
> - if there no entry in hash - just return ordinary range info.
>
> I will appreciate your notes and comments. Task is not seem to be trivial
> and I need to understand its importance for community to implement it.
Well, VRP is not path-insensitive - it is the value-ranges we are able
to retain after removing the ASSERT_EXPRs VRP inserts.
Why can't you do the ASAN optimizations in the VRP transform phase?
Thanks,
Richard.
> --Marat
>
> diff --git a/gcc/asan.c b/gcc/asan.c
> index 2a61a82..49e77eb 100644
> --- a/gcc/asan.c
> +++ b/gcc/asan.c
> @@ -369,6 +369,134 @@ asan_mem_ref_hasher::equal (const asan_mem_ref *m1,
> && operand_equal_p (m1->start, m2->start, 0));
> }
>
> +// Return true if last in of bb is condition
> +inline bool
> +maybe_parse_last_integer_comparison (basic_block bb, tree * op0_p = NULL,
> tree * op1_p = NULL, tree_code * cond_p = NULL);
> +
> +struct asan_tree_hasher : default_hashmap_traits
> +{
> + static hashval_t hash (tree t)
> + {
> + return iterative_hash_expr (t, 0);
> + }
> + static bool equal_keys (const tree & t1, const tree & t2)
> + {
> + return operand_equal_p (t1, t2, 0);
> + }
> +};
> +
> +class vrp_bounds
> +{
> + HOST_WIDE_INT low;
> + HOST_WIDE_INT up;
> + inline HOST_WIDE_INT max (const HOST_WIDE_INT a, const HOST_WIDE_INT b)
> + {
> + return a > b ? a : b;
> + }
> + inline HOST_WIDE_INT min (const HOST_WIDE_INT a, const HOST_WIDE_INT b)
> + {
> + return a < b ? a : b;
> + }
> +public:
> + void check () { gcc_assert (up >= low); }
> + vrp_bounds (HOST_WIDE_INT in_low, HOST_WIDE_INT in_up) : low (in_low), up
> (in_up) { ; }
> + vrp_bounds () : low (HOST_WIDE_INT_MIN), up (HOST_WIDE_INT_MAX) { ; }
> + vrp_bounds operator &= (const vrp_bounds & vrpb)
> + {
> + gcc_assert (!(low > vrpb.up || up < vrpb.low));
> + low = max (low, vrpb.low);
> + up = min (up, vrpb.up);
> + return *this;
> + }
> + vrp_bounds operator &= (HOST_WIDE_INT cval)
> + {
> + gcc_assert (cval >= low && cval <= up);
> + low = cval;
> + up = cval;
> + return *this;
> + }
> + vrp_bounds operator &= (tree t)
> + {
> + wide_int min_sub, max_sub;
> + if (get_range_info (t, &min_sub, &max_sub) == VR_RANGE)
> + {
> + bool sign = TYPE_SIGN (TREE_TYPE (t)) == SIGNED;
> + low = max (low, min_sub.slow());
> + if (sign || max_sub.ulow () < HOST_WIDE_INT_MAX)
> + up = min (up, max_sub.slow());
> + }
> + return *this;
> + }
> + bool and_with_edge (edge e, tree t)
> + {
> + tree op0, op1;
> + tree_code cond;
> +
> + if (!maybe_parse_last_integer_comparison (e->src, &op0, &op1, &cond)
> + || !operand_equal_p (op0, t, 0)
> + || !tree_fits_shwi_p (op1))
> + return true;
> +
> + HOST_WIDE_INT bnd = tree_to_shwi (op1);
> +
> + if (e->flags & EDGE_FALSE_VALUE)
> + cond = invert_tree_comparison (cond, false);
> +
> + (*this) &= t;
> + switch (cond)
> + {
> + case GT_EXPR: bnd++;
> + case GE_EXPR: low = max (low, bnd); break;
> + case LT_EXPR: bnd--;
> + case LE_EXPR: up = min (up, bnd); break;
> + case NE_EXPR:
> + if (low == bnd) low++;
> + else if (up == bnd) up--;
> + break;
> + case EQ_EXPR: low = bnd; up = bnd; break;
> + default: break;
> + }
> + (*this) &= t;
> + if (low > up)
> + {
> + low = up = 0;
> + return false;
> + }
> + return true;
> + }
> + vrp_bounds operator |= (const vrp_bounds & vrpb)
> + {
> + low = min (low, vrpb.low);
> + up = max (up, vrpb.up);
> + return *this;
> + }
> + vrp_bounds operator |= (HOST_WIDE_INT cval)
> + {
> + low = min (low, cval);
> + up = max (up, cval);
> + return *this;
> + }
> + vrp_bounds operator = (HOST_WIDE_INT cval)
> + {
> + low = cval;
> + up = cval;
> + return *this;
> + }
> + bool includes (const vrp_bounds & other) const
> + {
> + return (other.low >= low) && (other.up <= up);
> + }
> + bool progress (const vrp_bounds & other) const
> + {
> + if (this->includes (other))
> + if (other.low > low || other.up < up)
> + return true;
> + return false;
> + }
> +};
> +
> +typedef hash_map<tree, vrp_bounds, asan_tree_hasher> node_vrp_t;
> +
> static hash_table<asan_mem_ref_hasher> *asan_mem_ref_ht;
>
> /* Returns a reference to the hash table containing memory references.
> @@ -1470,7 +1598,6 @@ create_cond_insert_point (gimple_stmt_iterator *iter,
> statement pointed to by ITER. The fallthrough block -- which is the
> else block of the condition as well as the destination of the
> outcoming edge of the 'then block' -- starts with the statement
> - pointed to by ITER.
>
> COND is the condition of the if.
>
> @@ -1687,6 +1814,33 @@ build_check_stmt (location_t loc, tree base, tree
> len,
> }
> }
>
> +/* Whether deref is safe */
> +static bool
> +deref_is_safe_p (tree t, gimple s)
> +{
> + wide_int min_sub, max_sub;
> + tree low, up, idx = TREE_OPERAND (t, 1);
> +
> + if (TREE_CODE (idx) != SSA_NAME)
> + return false;
> +
> + vrp_bounds index_vrpb;
> + index_vrpb &= idx;
> + node_vrp_t * node_vrp = (node_vrp_t *) gimple_bb (s)->aux;
> + if (node_vrp && node_vrp->get (idx))
> + index_vrpb &= *(node_vrp->get (idx));
> +
> + low = array_ref_low_bound (t);
> + up = array_ref_up_bound (t);
> + vrp_bounds array_vrpb (low ? ((wide_int)low).slow () : HOST_WIDE_INT_MAX,
> + up ? ((wide_int)up).slow () : HOST_WIDE_INT_MIN);
> +
> + if (array_vrpb.includes (index_vrpb))
> + return true;
> +
> + return false;
> +}
> +
> /* If T represents a memory access, add instrumentation code before ITER.
> LOCATION is source code location.
> IS_STORE is either TRUE (for a store) or FALSE (for a load). */
> @@ -1771,6 +1925,20 @@ instrument_derefs (gimple_stmt_iterator *iter, tree
> t,
> }
> }
>
> + if (TREE_CODE (inner) == VAR_DECL
> + && !DECL_EXTERNAL (inner)
> + && TREE_CODE (t) == ARRAY_REF
> + && bitpos >= 0
> + && DECL_SIZE (inner)
> + && tree_fits_shwi_p (DECL_SIZE (inner))
> + && bitpos + bitsize <= tree_to_shwi (DECL_SIZE (inner)))
> + {
> + if (deref_is_safe_p (t, gsi_stmt (*iter)))
> + {
> + return;
> + }
> + }
> +
> base = build_fold_addr_expr (t);
> if (!has_mem_ref_been_instrumented (base, size_in_bytes))
> {
> @@ -1978,6 +2146,7 @@ maybe_instrument_assignment (gimple_stmt_iterator
> *iter)
> if (gimple_store_p (s))
> {
> ref_expr = gimple_assign_lhs (s);
> +
> is_store = true;
> instrument_derefs (iter, ref_expr,
> gimple_location (s),
> @@ -1988,6 +2157,7 @@ maybe_instrument_assignment (gimple_stmt_iterator
> *iter)
> if (gimple_assign_load_p (s))
> {
> ref_expr = gimple_assign_rhs1 (s);
> +
> is_store = false;
> instrument_derefs (iter, ref_expr,
> gimple_location (s),
> @@ -2041,6 +2211,182 @@ maybe_instrument_call (gimple_stmt_iterator *iter)
> return false;
> }
>
> +inline bool
> +maybe_parse_last_integer_comparison (basic_block bb, tree * op0_p,
> + tree * op1_p, tree_code * cond_p)
> +{
> + gimple last_ins = gsi_stmt (gsi_last_bb (bb));
> +
> + if (!(last_ins && gimple_code (last_ins) == GIMPLE_COND))
> + return false;
> +
> + tree op0 = gimple_op (last_ins, 0);
> + tree op1 = gimple_op (last_ins, 1);
> +
> + if (op0_p) *op0_p = op0;
> + if (op1_p) *op1_p = op1;
> + if (cond_p) *cond_p = gimple_cond_code (last_ins);
> +
> + if (TREE_CODE (TREE_TYPE (op0)) != INTEGER_TYPE)
> + return false;
> +
> + if (!tree_fits_shwi_p (op1))
> + return false;
> +
> + vrp_bounds vrpb;
> +
> + node_vrp_t * node_vrp = (node_vrp_t *) bb->aux;
> + if (!node_vrp->get (op0))
> + node_vrp->put (op0, vrpb);
> +
> + return true;
> +}
> +
> +inline bool
> +is_phi_appropriate (gimple phi)
> +{
> + for (unsigned arg = 0; arg < gimple_phi_num_args (phi); arg++)
> + {
> + tree op = gimple_phi_arg (phi, arg)->def;
> + if (TREE_CODE (op) == INTEGER_CST && !tree_fits_shwi_p (op))
> + return false;
> + }
> + return true;
> +}
> +
> +static bool
> +traverse_basic_block_vrp (basic_block bb)
> +{
> + bool changed = false;
> + node_vrp_t * node_vrp = (node_vrp_t *)bb->aux;
> + edge_iterator ei;
> + edge e;
> +
> + for (gimple_stmt_iterator i = gsi_start_phis (bb); !gsi_end_p (i);
> gsi_next (&i))
> + {
> + gimple s = gsi_stmt (i);
> + tree res = gimple_phi_result (s);
> + vrp_bounds old_vrpb;
> +
> + if (TREE_CODE (TREE_TYPE (res)) != INTEGER_TYPE)
> + continue;
> +
> + if (!is_phi_appropriate (s))
> + continue;
> +
> + if (node_vrp->get (res))
> + old_vrpb = *(node_vrp->get (res));
> + else
> + node_vrp->put (res, old_vrpb);
> +
> + bool first = true;
> + for (unsigned arg = 0; arg < gimple_phi_num_args (s); arg++)
> + {
> + edge e = gimple_phi_arg_edge (s, arg);
> + tree op = gimple_phi_arg (s, arg)->def;
> +
> + if (tree_fits_shwi_p (op))
> + {
> + if (first)
> + { *(node_vrp->get (res)) = tree_to_shwi (op); first=false; }
> + else
> + *(node_vrp->get (res)) |= tree_to_shwi (op);
> + }
> + else
> + {
> + vrp_bounds pred_vrpb;
> +
> + if (e->src->aux && ((node_vrp_t *)e->src->aux)->get (op))
> + pred_vrpb = *((node_vrp_t *)e->src->aux)->get (op);
> +
> + if (!pred_vrpb.and_with_edge (e, op))
> + continue;
> +
> + if (first)
> + { *(node_vrp->get (res)) = pred_vrpb; first = false; }
> + else
> + *(node_vrp->get (res)) |= pred_vrpb;
> + }
> + }
> + *(node_vrp->get (res)) &= res;
> + if (old_vrpb.progress (*(node_vrp->get (res))))
> + changed = true;
> + }
> +
> + FOR_EACH_EDGE (e, ei, bb->preds)
> + {
> + node_vrp_t * pred_vrp = (node_vrp_t *) e->src->aux;
> + for (node_vrp_t::iterator iter = pred_vrp->begin ();
> + iter != pred_vrp->end (); ++iter)
> + if (!node_vrp->get (iter.key()))
> + {
> + gimple def = SSA_NAME_DEF_STMT (iter.key ());
> +
> + // propogate only through SSA liverange
> + if (gimple_code (def) == GIMPLE_NOP
> + || dominated_by_p (CDI_DOMINATORS, bb, gimple_bb (def)))
> + node_vrp->put (iter.key(), vrp_bounds());
> + }
> + }
> +
> + for (node_vrp_t::iterator iter = node_vrp->begin ();
> + iter != node_vrp->end (); ++iter)
> + {
> + bool first = true;
> + tree t = iter.key ();
> + vrp_bounds temp;
> +
> + FOR_EACH_EDGE (e, ei, bb->preds)
> + {
> + node_vrp_t * pred_vrp = (node_vrp_t *) e->src->aux;
> + vrp_bounds pred_vrpb;
> +
> + if (pred_vrp->get (t))
> + pred_vrpb = *pred_vrp->get (t);
> + else if (dominated_by_p (CDI_DOMINATORS, e->src, bb))
> + continue;
> +
> + if (!pred_vrpb.and_with_edge (e, t))
> + continue;
> +
> + if (first)
> + { temp = pred_vrpb; first = false; }
> + else
> + temp |= pred_vrpb;
> + }
> + temp &= t;
> + if (node_vrp->get (t)->progress (temp))
> + *(node_vrp->get (t)) = temp;
> + }
> +
> + return changed;
> +}
> +
> +/* Clarify VRP using paths information */
> +static void
> +traverse_collect_bounds (void)
> +{
> + int * rev_post_order = XALLOCAVEC (int, n_basic_blocks_for_fn (cfun));
> + int n_blocks = pre_and_rev_post_order_compute_fn (cfun, NULL,
> rev_post_order, true);
> +
> + for (int i = 0; i < n_blocks; i++)
> + {
> + basic_block bb = BASIC_BLOCK_FOR_FN(cfun, rev_post_order[i]);
> + bb->aux = new node_vrp_t;
> + }
> + bool changed = true;
> + while (changed)
> + {
> + changed = false;
> + for (int i = 0; i < n_blocks; i++)
> + {
> + basic_block bb = BASIC_BLOCK_FOR_FN(cfun, rev_post_order[i]);
> + changed |= traverse_basic_block_vrp (bb);
> + maybe_parse_last_integer_comparison (bb);
> + }
> + }
> +}
> +
> /* Walk each instruction of all basic block and instrument those that
> represent memory references: loads, stores, or function calls.
> In a given basic block, this function avoids instrumenting memory
> @@ -2053,6 +2399,9 @@ transform_statements (void)
> gimple_stmt_iterator i;
> int saved_last_basic_block = last_basic_block_for_fn (cfun);
>
> + if (n_basic_blocks_for_fn (cfun) < PARAM_ASAN_VRP_THRESHOLD)
> + traverse_collect_bounds();
> +
> FOR_EACH_BB_FN (bb, cfun)
> {
> basic_block prev_bb = bb;
> @@ -2102,6 +2451,12 @@ transform_statements (void)
> gsi_next (&i);
> }
> }
> + if (bb->aux)
> + {
> + ((node_vrp_t *) bb->aux)->empty ();
> + delete (node_vrp_t *) bb->aux;
> + bb->aux = NULL;
> + }
> }
> free_mem_ref_resources ();
> }
> diff --git a/gcc/hash-map.h b/gcc/hash-map.h
> index 4988fe4..d69a96d 100644
> --- a/gcc/hash-map.h
> +++ b/gcc/hash-map.h
> @@ -98,7 +98,7 @@ private:
> static void
> mark_key_empty (T *&k)
> {
> - k = static_cast<T *> (0);
> + k = reinterpret_cast<T *> (0);
> }
> };
>
> @@ -264,6 +264,43 @@ public:
> break;
> }
>
> + /* Clear all entries. */
> +
> + void empty ()
> + {
> + m_table.empty ();
> + }
> +
> + class iterator
> + {
> + public:
> + iterator () {}
> +
> + iterator (hash_table<hash_entry> &ht) { hti = ht.begin (); }
> +
> + inline iterator &operator ++ () { ++hti; return *this; }
> + bool operator != (const iterator &other) const { return hti !=
> other.hti; }
> +
> + inline const Key &key () const { return (*hti).m_key; }
> +
> + inline const Value &operator * () const { return (*hti).m_value; }
> +
> + inline Value &operator * () { return (*hti).m_value; }
> +
> + inline const Value *operator -> () const { return &(*hti).m_value; }
> +
> + inline Value *operator -> () { return &(*hti).m_value; }
> +
> + private:
> + typename hash_table<hash_entry>::iterator hti;
> + };
> +
> + iterator begin () { return iterator (m_table); }
> +
> + iterator end () { return iterator (); }
> +
> + unsigned elements () const { return m_table.elements (); }
> +
> private:
>
> template<typename T, typename U, typename V> friend void gt_ggc_mx
> (hash_map<T, U, V> *);
> diff --git a/gcc/hash-set.h b/gcc/hash-set.h
> index 0a500cb..85f0dfa 100644
> --- a/gcc/hash-set.h
> +++ b/gcc/hash-set.h
> @@ -230,6 +230,32 @@ public:
>
> size_t elements () const { return m_table.elements (); }
>
> + class iterator
> + {
> + public:
> + iterator () {}
> +
> + iterator (hash_table<hash_entry> &ht) { hti = ht.begin (); }
> +
> + inline iterator &operator ++ () { ++hti; return *this; }
> + bool operator != (const iterator &other) const { return hti !=
> other.hti; }
> +
> + inline const Key &operator * () const { return (*hti).m_key; }
> +
> + inline Key &operator * () { return (*hti).m_key; }
> +
> + inline const Key *operator -> () const { return &(*hti).m_key; }
> +
> + inline Key *operator -> () { return &(*hti).m_key; }
> +
> + private:
> + typename hash_table<hash_entry>::iterator hti;
> + };
> +
> + iterator begin () { return iterator (m_table); }
> +
> + iterator end () { return iterator (); }
> +
> private:
>
> template<typename T, typename U> friend void gt_ggc_mx (hash_set<T, U>
> *);
> diff --git a/gcc/hash-table.h b/gcc/hash-table.h
> index 2493f2e..76c9916 100644
> --- a/gcc/hash-table.h
> +++ b/gcc/hash-table.h
> @@ -1124,6 +1124,7 @@ public:
> m_slot (slot), m_limit (limit) {}
>
> inline value_type &operator * () { return *m_slot; }
> + inline const value_type &operator * () const { return *m_slot; }
> void slide ();
> inline iterator &operator ++ ();
> bool operator != (const iterator &other) const
> @@ -1512,7 +1513,7 @@ hash_table<Descriptor, Allocator, true>
> ::remove_elt_with_hash (const compare_type &comparable, hashval_t hash)
> {
> value_type *slot = find_slot_with_hash (comparable, hash, NO_INSERT);
> - if (is_empty (*slot))
> + if (slot == NULL || is_empty (*slot))
> return;
>
> Descriptor::remove (*slot);
> diff --git a/gcc/params.def b/gcc/params.def
> index beff7e6..5f05a7e 100644
> --- a/gcc/params.def
> +++ b/gcc/params.def
> @@ -1102,6 +1102,11 @@ DEFPARAM
> (PARAM_ASAN_INSTRUMENTATION_WITH_CALL_THRESHOLD,
> "in function becomes greater or equal to this number",
> 7000, 0, INT_MAX)
>
> +DEFPARAM (PARAM_ASAN_VRP_THRESHOLD,
> + "asan-vrp-threshold",
> + "Skip vrp analysis if CFG has more than 999 nodes",
> + 1000, 0, INT_MAX)
> +
> DEFPARAM (PARAM_UNINIT_CONTROL_DEP_ATTEMPTS,
> "uninit-control-dep-attempts",
> "Maximum number of nested calls to search for control dependencies
> "
> diff --git a/gcc/params.h b/gcc/params.h
> index d488e32..d1dd159 100644
> --- a/gcc/params.h
> +++ b/gcc/params.h
> @@ -234,5 +234,7 @@ extern void init_param_values (int *params);
> PARAM_VALUE (PARAM_ASAN_USE_AFTER_RETURN)
> #define ASAN_INSTRUMENTATION_WITH_CALL_THRESHOLD \
> PARAM_VALUE (PARAM_ASAN_INSTRUMENTATION_WITH_CALL_THRESHOLD)
> +#define ASAN_VRP_THRESHOLD \
> + PARAM_VALUE (PARAM_ASAN_VRP_THRESHOLD)
>
> #endif /* ! GCC_PARAMS_H */
> diff --git a/gcc/testsuite/c-c++-common/asan/no-redundant-check-1.c
> b/gcc/testsuite/c-c++-common/asan/no-redundant-check-1.c
> new file mode 100644
> index 0000000..da123f2
> --- /dev/null
> +++ b/gcc/testsuite/c-c++-common/asan/no-redundant-check-1.c
> @@ -0,0 +1,14 @@
> +/* { dg-options "-fdump-tree-asan" } */
> +/* { dg-do compile } */
> +/* { dg-skip-if "" { *-*-* } { "*" } { "-O3" } } */
> +int a[1000];
> +
> +void foo () {
> +
> + int i = 0;
> + for (; i < 1000; i++)
> + a[i] = 0;
> +}
> +
> +/* { dg-final { scan-tree-dump-times "ASAN_CHECK" 0 "asan1" } } */
> +/* { dg-final { cleanup-tree-dump "asan1" } } */
> diff --git a/gcc/testsuite/c-c++-common/asan/no-redundant-check-2.c
> b/gcc/testsuite/c-c++-common/asan/no-redundant-check-2.c
> new file mode 100644
> index 0000000..96e46d2
> --- /dev/null
> +++ b/gcc/testsuite/c-c++-common/asan/no-redundant-check-2.c
> @@ -0,0 +1,14 @@
> +/* { dg-options "-fdump-tree-asan" } */
> +/* { dg-do compile } */
> +/* { dg-skip-if "" { *-*-* } { "*" } { "-O3" } } */
> +
> +int a[1000];
> +
> +void foo () {
> + int i;
> + for (i = 999; i >= 0; i--)
> + a[i] = 0;
> +}
> +
> +/* { dg-final { scan-tree-dump-times "ASAN_CHECK" 0 "asan1" } } */
> +/* { dg-final { cleanup-tree-dump "asan1" } } */
> diff --git a/gcc/testsuite/c-c++-common/asan/no-redundant-check-3.c
> b/gcc/testsuite/c-c++-common/asan/no-redundant-check-3.c
> new file mode 100644
> index 0000000..181af16
> --- /dev/null
> +++ b/gcc/testsuite/c-c++-common/asan/no-redundant-check-3.c
> @@ -0,0 +1,14 @@
> +/* { dg-options "-fdump-tree-asan" } */
> +/* { dg-do compile } */
> +/* { dg-skip-if "" { *-*-* } { "*" } { "-O3" } } */
> +
> +int a[1000];
> +
> +void foo () {
> + unsigned i = 0;
> + for (; i < 1000; i++)
> + a[i] = 0;
> +}
> +
> +/* { dg-final { scan-tree-dump-times "ASAN_CHECK" 0 "asan1" } } */
> +/* { dg-final { cleanup-tree-dump "asan1" } } */
> diff --git a/gcc/testsuite/c-c++-common/asan/no-redundant-check-4.c
> b/gcc/testsuite/c-c++-common/asan/no-redundant-check-4.c
> new file mode 100644
> index 0000000..479b5d7
> --- /dev/null
> +++ b/gcc/testsuite/c-c++-common/asan/no-redundant-check-4.c
> @@ -0,0 +1,14 @@
> +/* { dg-options "-fdump-tree-asan" } */
> +/* { dg-do compile } */
> +/* { dg-skip-if "" { *-*-* } { "*" } { "-O3" } } */
> +
> +int a[1000];
> +
> +void foo () {
> + unsigned i;
> + for (i = 999; i >= 1; i--)
> + a[i] = 0;
> +}
> +
> +/* { dg-final { scan-tree-dump-times "ASAN_CHECK" 0 "asan1" } } */
> +/* { dg-final { cleanup-tree-dump "asan1" } } */
> diff --git a/gcc/testsuite/c-c++-common/asan/no-redundant-check-5.c
> b/gcc/testsuite/c-c++-common/asan/no-redundant-check-5.c
> new file mode 100644
> index 0000000..039cd78
> --- /dev/null
> +++ b/gcc/testsuite/c-c++-common/asan/no-redundant-check-5.c
> @@ -0,0 +1,15 @@
> +/* { dg-options "-fdump-tree-asan" } */
> +/* { dg-do compile } */
> +/* { dg-skip-if "" { *-*-* } { "*" } { "-O3" } } */
> +int a[1000];
> +extern int f ();
> +
> +void foo () {
> +
> + int i = 0;
> + for (; i < f (); i++)
> + a[i] = 0;
> +}
> +
> +/* { dg-final { scan-tree-dump-times "ASAN_CHECK" 1 "asan1" } } */
> +/* { dg-final { cleanup-tree-dump "asan1" } } */
> diff --git a/gcc/tree-vrp.c b/gcc/tree-vrp.c
> index 7ca0528..dac95cc 100644
> --- a/gcc/tree-vrp.c
> +++ b/gcc/tree-vrp.c
> @@ -8634,7 +8634,8 @@ vrp_visit_phi_node (gimple phi)
> if ((cmp_min > 0 || cmp_min < 0
> || cmp_max < 0 || cmp_max > 0)
> && (l = loop_containing_stmt (phi))
> - && l->header == gimple_bb (phi))
> + && l->header == gimple_bb (phi)
> + && !(flag_sanitize & SANITIZE_ADDRESS))
> adjust_range_with_scev (&vr_result, l, phi, lhs);
>
> /* If we will end up with a (-INF, +INF) range, set it to
>