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.

--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

Reply via email to