diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index b8f5d9e1cce..b83bd902cec 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -13423,6 +13423,10 @@ Setting to 0 disables the analysis completely.
 @item modref-max-escape-points
 Specifies the maximum number of escape points tracked by modref per SSA-name.
 
+@item modref-max-adjustments
+Specifies the maximum number the access range is enlarged during modref dataflow
+analysis.
+
 @item profile-func-internal-id
 A parameter to control whether to use function internal id in profile
 database lookup. If the value is 0, the compiler uses an id that
diff --git a/gcc/ipa-modref-tree.c b/gcc/ipa-modref-tree.c
index 64e57f52147..69395b0113c 100644
--- a/gcc/ipa-modref-tree.c
+++ b/gcc/ipa-modref-tree.c
@@ -41,7 +41,7 @@ test_insert_search_collapse ()
   ASSERT_FALSE (t->every_base);
 
   /* Insert into an empty tree.  */
-  t->insert (1, 2, a);
+  t->insert (1, 2, a, false);
   ASSERT_NE (t->bases, NULL);
   ASSERT_EQ (t->bases->length (), 1);
   ASSERT_FALSE (t->every_base);
@@ -59,7 +59,7 @@ test_insert_search_collapse ()
   ASSERT_EQ (ref_node->ref, 2);
 
   /* Insert when base exists but ref does not.  */
-  t->insert (1, 3, a);
+  t->insert (1, 3, a, false);
   ASSERT_NE (t->bases, NULL);
   ASSERT_EQ (t->bases->length (), 1);
   ASSERT_EQ (t->search (1), base_node);
@@ -72,7 +72,7 @@ test_insert_search_collapse ()
 
   /* Insert when base and ref exist, but access is not dominated by nor
      dominates other accesses.  */
-  t->insert (1, 2, a);
+  t->insert (1, 2, a, false);
   ASSERT_EQ (t->bases->length (), 1);
   ASSERT_EQ (t->search (1), base_node);
 
@@ -80,12 +80,12 @@ test_insert_search_collapse ()
   ASSERT_NE (ref_node, NULL);
 
   /* Insert when base and ref exist and access is dominated.  */
-  t->insert (1, 2, a);
+  t->insert (1, 2, a, false);
   ASSERT_EQ (t->search (1), base_node);
   ASSERT_EQ (base_node->search (2), ref_node);
 
   /* Insert ref to trigger ref list collapse for base 1.  */
-  t->insert (1, 4, a);
+  t->insert (1, 4, a, false);
   ASSERT_EQ (t->search (1), base_node);
   ASSERT_EQ (base_node->refs, NULL);
   ASSERT_EQ (base_node->search (2), NULL);
@@ -93,7 +93,7 @@ test_insert_search_collapse ()
   ASSERT_TRUE (base_node->every_ref);
 
   /* Further inserts to collapsed ref list are ignored.  */
-  t->insert (1, 5, a);
+  t->insert (1, 5, a, false);
   ASSERT_EQ (t->search (1), base_node);
   ASSERT_EQ (base_node->refs, NULL);
   ASSERT_EQ (base_node->search (2), NULL);
@@ -101,13 +101,13 @@ test_insert_search_collapse ()
   ASSERT_TRUE (base_node->every_ref);
 
   /* Insert base to trigger base list collapse.  */
-  t->insert (5, 6, a);
+  t->insert (5, 6, a, false);
   ASSERT_TRUE (t->every_base);
   ASSERT_EQ (t->bases, NULL);
   ASSERT_EQ (t->search (1), NULL);
 
   /* Further inserts to collapsed base list are ignored.  */
-  t->insert (7, 8, a);
+  t->insert (7, 8, a, false);
   ASSERT_TRUE (t->every_base);
   ASSERT_EQ (t->bases, NULL);
   ASSERT_EQ (t->search (1), NULL);
@@ -123,22 +123,22 @@ test_merge ()
   modref_access_node a = unspecified_modref_access_node;
 
   t1 = new modref_tree<alias_set_type>(3, 4, 1);
-  t1->insert (1, 1, a);
-  t1->insert (1, 2, a);
-  t1->insert (1, 3, a);
-  t1->insert (2, 1, a);
-  t1->insert (3, 1, a);
+  t1->insert (1, 1, a, false);
+  t1->insert (1, 2, a, false);
+  t1->insert (1, 3, a, false);
+  t1->insert (2, 1, a, false);
+  t1->insert (3, 1, a, false);
 
   t2 = new modref_tree<alias_set_type>(10, 10, 10);
-  t2->insert (1, 2, a);
-  t2->insert (1, 3, a);
-  t2->insert (1, 4, a);
-  t2->insert (3, 2, a);
-  t2->insert (3, 3, a);
-  t2->insert (3, 4, a);
-  t2->insert (3, 5, a);
-
-  t1->merge (t2, NULL);
+  t2->insert (1, 2, a, false);
+  t2->insert (1, 3, a, false);
+  t2->insert (1, 4, a, false);
+  t2->insert (3, 2, a, false);
+  t2->insert (3, 3, a, false);
+  t2->insert (3, 4, a, false);
+  t2->insert (3, 5, a, false);
+
+  t1->merge (t2, NULL, false);
 
   ASSERT_FALSE (t1->every_base);
   ASSERT_NE (t1->bases, NULL);
diff --git a/gcc/ipa-modref-tree.h b/gcc/ipa-modref-tree.h
index 2e26b75e21f..6f6932f0875 100644
--- a/gcc/ipa-modref-tree.h
+++ b/gcc/ipa-modref-tree.h
@@ -34,6 +34,7 @@ along with GCC; see the file COPYING3.  If not see
       Again ref is an template to allow LTO streaming.
    3) Access: this level represent info about individual accesses.  Presently
       we record whether access is through a dereference of a function parameter
+      and if so we record the access range.
 */
 
 #ifndef GCC_MODREF_TREE_H
@@ -57,6 +58,9 @@ struct GTY(()) modref_access_node
      a function parameter.  */
   int parm_index;
   bool parm_offset_known;
+  /* Number of times interval was extended during dataflow.
+     This has to be limited in order to keep dataflow finite.  */
+  unsigned char adjustments;
 
   /* Return true if access node holds no useful info.  */
   bool useful_p () const
@@ -84,6 +88,8 @@ struct GTY(()) modref_access_node
 	      && !known_eq (parm_offset, a.parm_offset))
 	    return false;
 	}
+      if (range_info_useful_p () != a.range_info_useful_p ())
+	return false;
       if (range_info_useful_p ()
 	  && (!known_eq (a.offset, offset)
 	      || !known_eq (a.size, size)
@@ -92,16 +98,24 @@ struct GTY(()) modref_access_node
       return true;
     }
   /* Return true A is a subaccess.  */
-  bool contains (modref_access_node &a) const
+  bool contains (const modref_access_node &a) const
     {
-      if (parm_index != a.parm_index)
-	return false;
+      poly_int64 aoffset_adj = 0;
       if (parm_index >= 0)
 	{
-	  if (parm_offset_known
-	      && (!a.parm_offset_known
-		  || !known_eq (parm_offset, a.parm_offset)))
+	  if (parm_index != a.parm_index)
 	    return false;
+	  if (parm_offset_known)
+	    {
+	       if (!a.parm_offset_known)
+		 return false;
+	       /* Accesses are never below parm_offset, so look
+		  for smaller offset.  */
+	       if (!known_le (parm_offset, a.parm_offset))
+		 return false;
+	       aoffset_adj = (a.parm_offset - parm_offset)
+			     << LOG2_BITS_PER_UNIT;
+	    }
 	}
       if (range_info_useful_p ())
 	{
@@ -111,20 +125,181 @@ struct GTY(()) modref_access_node
 	     to fit the store, so smaller or unknown sotre is more general
 	     than large store.  */
 	  if (known_size_p (size)
-	      && !known_le (size, a.size))
+	      && (!known_size_p (a.size)
+		  || !known_le (size, a.size)))
 	    return false;
 	  if (known_size_p (max_size))
-	    return known_subrange_p (a.offset, a.max_size, offset, max_size);
+	    return known_subrange_p (a.offset + aoffset_adj,
+				     a.max_size, offset, max_size);
 	  else
-	    return known_le (offset, a.offset);
+	    return known_le (offset, a.offset + aoffset_adj);
 	}
       return true;
     }
+  /* Update access range to new parameters.
+     If RECORD_ADJUSTMENTS is true, record number of changes in the access
+     and if threshold is exceeded start dropping precision
+     so only constantly many updates are possible.  This makes dataflow
+     to converge.  */
+  void update (poly_int64 parm_offset1,
+	       poly_int64 offset1, poly_int64 size1, poly_int64 max_size1,
+	       bool record_adjustments)
+    {
+      if (known_eq (offset, offset1)
+	  && known_eq (size, size1)
+	  && known_eq (max_size, max_size1))
+	return;
+      if (!record_adjustments
+	  || (++adjustments) < param_modref_max_adjustments)
+	{
+	  parm_offset = parm_offset1;
+	  offset = offset1;
+	  size = size1;
+	  max_size = max_size1;
+	}
+      else
+	{
+	  if (dump_file)
+	    fprintf (dump_file,
+		     "--param param=modref-max-adjustments limit reached:");
+	  if (!known_eq (parm_offset, parm_offset1))
+	    {
+	      if (dump_file)
+		fprintf (dump_file, " parm_offset cleared");
+	      parm_offset_known = false;
+	    }
+	  if (!known_eq (size, size1))
+	    {
+	      size = -1;
+	      if (dump_file)
+		fprintf (dump_file, " size cleared");
+	    }
+	  if (!known_eq (max_size, max_size1))
+	    {
+	      max_size = -1;
+	      if (dump_file)
+		fprintf (dump_file, " max_size cleared");
+	    }
+	  if (!known_eq (offset, offset1))
+	    {
+	      offset = 0;
+	      if (dump_file)
+		fprintf (dump_file, " offset cleared");
+	    }
+	  if (dump_file)
+	    fprintf (dump_file, "\n");
+	}
+    }
+  /* Merge in access A if it is possible to do without losing
+     precision.  Return true if successful.
+     If RECORD_ADJUSTMENTs is true, remember how many interval
+     was prolonged and punt when there are too many.  */
+  bool merge (const modref_access_node &a, bool record_adjustments)
+    {
+      poly_int64 aoffset_adj = 0, offset_adj = 0;
+      poly_int64 new_parm_offset = parm_offset;
+
+      /* We assume that containment was tested earlier.  */
+      gcc_checking_assert (!contains (a) && !a.contains (*this));
+      if (parm_index >= 0)
+	{
+	  if (parm_index != a.parm_index)
+	    return false;
+	  if (parm_offset_known)
+	    {
+	      if (!a.parm_offset_known)
+		return false;
+	      if (known_le (a.parm_offset, parm_offset))
+		{
+		  offset_adj = (parm_offset - a.parm_offset)
+				<< LOG2_BITS_PER_UNIT;
+		  aoffset_adj = 0;
+		  new_parm_offset = a.parm_offset;
+		}
+	      else if (known_le (parm_offset, a.parm_offset))
+		{
+		  aoffset_adj = (a.parm_offset - parm_offset)
+				 << LOG2_BITS_PER_UNIT;
+		  offset_adj = 0;
+		}
+	      else
+		return false;
+	    }
+	}
+      /* See if we can merge ranges.  */
+      if (range_info_useful_p ())
+	{
+	  poly_int64 offset1 = offset + offset_adj;
+	  poly_int64 aoffset1 = a.offset + aoffset_adj;
+
+	  /* In this case we have containment that should be
+	     handled earlier.  */
+	  gcc_checking_assert (a.range_info_useful_p ());
+
+	  /* If a.size is less specified than size, merge only
+	     if intervals are otherwise equivalent.  */
+	  if (known_size_p (size)
+	      && (!known_size_p (a.size) || known_lt (a.size, size)))
+	    {
+	      if (((known_size_p (max_size) || known_size_p (a.max_size))
+		   && !known_eq (max_size, a.max_size))
+		   || !known_eq (offset1, aoffset1))
+		return false;
+	      update (new_parm_offset, offset1, a.size, max_size,
+		      record_adjustments);
+	      return true;
+	    }
+	  /* If sizes are same, we can extend the interval.  */
+	  if ((known_size_p (size) || known_size_p (a.size))
+	      && !known_eq (size, a.size))
+	    return false;
+	  if (known_le (offset1, aoffset1))
+	    {
+	      if (!known_size_p (max_size))
+		{
+		  update (new_parm_offset, offset1, size, max_size,
+			  record_adjustments);
+		  return true;
+		}
+	      else if (known_ge (offset1 + max_size, aoffset1))
+		{
+		  poly_int64 new_max_size = max_size;
+		  if (known_le (max_size, a.max_size + aoffset1 - offset1))
+		    new_max_size = a.max_size + aoffset1 - offset1;
+		  update (new_parm_offset, offset1, size, new_max_size,
+			  record_adjustments);
+		  return true;
+		}
+	    }
+	  else if (known_le (aoffset1, offset1))
+	    {
+	      if (!known_size_p (a.max_size))
+		{
+		  update (new_parm_offset, aoffset1, size, a.max_size,
+			  record_adjustments);
+		  return true;
+		}
+	      else if (known_ge (aoffset1 + a.max_size, offset1))
+		{
+		  poly_int64 new_max_size = a.max_size;
+		  if (known_le (a.max_size, max_size + offset1 - aoffset1))
+		    new_max_size = max_size + offset1 - aoffset1;
+		  update (new_parm_offset, aoffset1, size, new_max_size,
+			  record_adjustments);
+		  return true;
+		}
+	    }
+	  return false;
+	}
+      update (new_parm_offset, offset + offset_adj,
+	      size, max_size, record_adjustments);
+      return true;
+    }
 };
 
 /* Access node specifying no useful info.  */
 const modref_access_node unspecified_modref_access_node
-		 = {0, -1, -1, 0, -1, false};
+		 = {0, -1, -1, 0, -1, false, 0};
 
 template <typename T>
 struct GTY((user)) modref_ref_node
@@ -149,8 +324,10 @@ struct GTY((user)) modref_ref_node
 
   /* Insert access with OFFSET and SIZE.
      Collapse tree if it has more than MAX_ACCESSES entries.
+     If RECORD_ADJUSTMENTs is true avoid too many interval extensions.
      Return true if record was changed.  */
-  bool insert_access (modref_access_node a, size_t max_accesses)
+  bool insert_access (modref_access_node a, size_t max_accesses,
+		      bool record_adjustments)
   {
     /* If this base->ref pair has no access information, bail out.  */
     if (every_access)
@@ -176,7 +353,17 @@ struct GTY((user)) modref_ref_node
 	  return false;
 	if (a.contains (*a2))
 	  {
-	    *a2 = a;
+	    a.adjustments = 0;
+	    a2->parm_index = a.parm_index;
+	    a2->parm_offset_known = a.parm_offset_known;
+	    a2->update (a.parm_offset, a.offset, a.size, a.max_size,
+			record_adjustments);
+	    try_merge_with (i);
+	    return true;
+	  }
+	if (a2->merge (a, record_adjustments))
+	  {
+	    try_merge_with (i);
 	    return true;
 	  }
 	gcc_checking_assert (!(a == *a2));
@@ -192,9 +379,28 @@ struct GTY((user)) modref_ref_node
 	collapse ();
 	return true;
       }
+    a.adjustments = 0;
     vec_safe_push (accesses, a);
     return true;
   }
+private:
+  /* Try to optimize the access list after entry INDEX was modified.  */
+  void
+  try_merge_with (size_t index)
+  {
+    modref_access_node *a2;
+    size_t i;
+
+    FOR_EACH_VEC_SAFE_ELT (accesses, i, a2)
+      if (i != index)
+	if ((*accesses)[index].contains (*a2)
+	    || (*accesses)[index].merge (*a2, false))
+	{
+	  if (index == accesses->length () - 1)
+	    index = i;
+	  accesses->unordered_remove (i);
+	}
+  }
 };
 
 /* Base of an access.  */
@@ -342,7 +548,8 @@ struct GTY((user)) modref_tree
 
   /* Insert memory access to the tree.
      Return true if something changed.  */
-  bool insert (T base, T ref, modref_access_node a)
+  bool insert (T base, T ref, modref_access_node a,
+	       bool record_adjustments)
   {
     if (every_base)
       return false;
@@ -387,7 +594,8 @@ struct GTY((user)) modref_tree
       {
 	if (ref_node->every_access)
 	  return changed;
-	changed |= ref_node->insert_access (a, max_accesses);
+	changed |= ref_node->insert_access (a, max_accesses,
+					    record_adjustments);
 	/* See if we failed to add useful access.  */
 	if (ref_node->every_access)
 	  {
@@ -456,7 +664,8 @@ struct GTY((user)) modref_tree
      PARM_MAP, if non-NULL, maps parm indexes of callee to caller.  -2 is used
      to signalize that parameter is local and does not need to be tracked.
      Return true if something has changed.  */
-  bool merge (modref_tree <T> *other, vec <modref_parm_map> *parm_map)
+  bool merge (modref_tree <T> *other, vec <modref_parm_map> *parm_map,
+	      bool record_accesses)
   {
     if (!other || every_base)
       return false;
@@ -501,7 +710,8 @@ struct GTY((user)) modref_tree
 		{
 		  changed |= insert (base_node->base,
 				     ref_node->ref,
-				     unspecified_modref_access_node);
+				     unspecified_modref_access_node,
+				     record_accesses);
 		}
 	      else
 		FOR_EACH_VEC_SAFE_ELT (ref_node->accesses, k, access_node)
@@ -525,7 +735,8 @@ struct GTY((user)) modref_tree
 				 = (*parm_map) [a.parm_index].parm_index;
 			  }
 		      }
-		    changed |= insert (base_node->base, ref_node->ref, a);
+		    changed |= insert (base_node->base, ref_node->ref, a,
+				       record_accesses);
 		  }
 	    }
       }
@@ -537,7 +748,7 @@ struct GTY((user)) modref_tree
   /* Copy OTHER to THIS.  */
   void copy_from (modref_tree <T> *other)
   {
-    merge (other, NULL);
+    merge (other, NULL, false);
   }
 
   /* Search BASE in tree; return NULL if failed.  */
diff --git a/gcc/ipa-modref.c b/gcc/ipa-modref.c
index 6ab687a7ba0..0d5ab9c0561 100644
--- a/gcc/ipa-modref.c
+++ b/gcc/ipa-modref.c
@@ -426,6 +426,8 @@ dump_access (modref_access_node *a, FILE *out)
       print_dec ((poly_int64_pod)a->size, out, SIGNED);
       fprintf (out, " max_size:");
       print_dec ((poly_int64_pod)a->max_size, out, SIGNED);
+      if (a->adjustments)
+	fprintf (out, " adjusted %i times", a->adjustments);
     }
   fprintf (out, "\n");
 }
@@ -656,7 +658,7 @@ get_access (ao_ref *ref)
 
   base = ao_ref_base (ref);
   modref_access_node a = {ref->offset, ref->size, ref->max_size,
-			  0, -1, false};
+			  0, -1, false, 0};
   if (TREE_CODE (base) == MEM_REF || TREE_CODE (base) == TARGET_MEM_REF)
     {
       tree memref = base;
@@ -708,7 +710,7 @@ record_access (modref_records *tt, ao_ref *ref)
        fprintf (dump_file, "   - Recording base_set=%i ref_set=%i parm=%i\n",
 		base_set, ref_set, a.parm_index);
     }
-  tt->insert (base_set, ref_set, a);
+  tt->insert (base_set, ref_set, a, false);
 }
 
 /* IPA version of record_access_tree.  */
@@ -774,7 +776,7 @@ record_access_lto (modref_records_lto *tt, ao_ref *ref)
 	       a.parm_index);
     }
 
-  tt->insert (base_type, ref_type, a);
+  tt->insert (base_type, ref_type, a, false);
 }
 
 /* Returns true if and only if we should store the access to EXPR.
@@ -858,12 +860,15 @@ parm_map_for_arg (gimple *stmt, int i)
 
 /* Merge side effects of call STMT to function with CALLEE_SUMMARY
    int CUR_SUMMARY.  Return true if something changed.
-   If IGNORE_STORES is true, do not merge stores.  */
+   If IGNORE_STORES is true, do not merge stores.
+   If RECORD_ADJUSTMENTS is true cap number of adjustments to
+   a given access to make dataflow finite.  */
 
 bool
 merge_call_side_effects (modref_summary *cur_summary,
 			 gimple *stmt, modref_summary *callee_summary,
-			 bool ignore_stores, cgraph_node *callee_node)
+			 bool ignore_stores, cgraph_node *callee_node,
+			 bool record_adjustments)
 {
   auto_vec <modref_parm_map, 32> parm_map;
   bool changed = false;
@@ -902,11 +907,13 @@ merge_call_side_effects (modref_summary *cur_summary,
     fprintf (dump_file, "\n");
 
   /* Merge with callee's summary.  */
-  changed |= cur_summary->loads->merge (callee_summary->loads, &parm_map);
+  changed |= cur_summary->loads->merge (callee_summary->loads, &parm_map,
+					record_adjustments);
   if (!ignore_stores)
     {
       changed |= cur_summary->stores->merge (callee_summary->stores,
-					     &parm_map);
+					     &parm_map,
+					     record_adjustments);
       if (!cur_summary->writes_errno
 	  && callee_summary->writes_errno)
 	{
@@ -941,7 +948,7 @@ get_access_for_fnspec (gcall *call, attr_fnspec &fnspec,
     }
   modref_access_node a = {0, -1, -1,
 			  map.parm_offset, map.parm_index,
-			  map.parm_offset_known};
+			  map.parm_offset_known, 0};
   poly_int64 size_hwi;
   if (size
       && poly_int_tree_p (size, &size_hwi)
@@ -1044,12 +1051,14 @@ process_fnspec (modref_summary *cur_summary,
 	      cur_summary->loads->insert (0, 0,
 					  get_access_for_fnspec (call,
 								 fnspec, i,
-								 map));
+								 map),
+					  false);
 	    if (cur_summary_lto)
 	      cur_summary_lto->loads->insert (0, 0,
 					      get_access_for_fnspec (call,
 								     fnspec, i,
-								     map));
+								     map),
+					      false);
 	  }
     }
   if (ignore_stores)
@@ -1077,12 +1086,14 @@ process_fnspec (modref_summary *cur_summary,
 	      cur_summary->stores->insert (0, 0,
 					   get_access_for_fnspec (call,
 								  fnspec, i,
-								  map));
+								  map),
+					   false);
 	    if (cur_summary_lto)
 	      cur_summary_lto->stores->insert (0, 0,
 					       get_access_for_fnspec (call,
 								      fnspec, i,
-								      map));
+								      map),
+					       false);
 	  }
       if (fnspec.errno_maybe_written_p () && flag_errno_math)
 	{
@@ -1168,7 +1179,7 @@ analyze_call (modref_summary *cur_summary, modref_summary_lto *cur_summary_lto,
     }
 
   merge_call_side_effects (cur_summary, stmt, callee_summary, ignore_stores,
-			   callee_node);
+			   callee_node, false);
 
   return true;
 }
@@ -2134,6 +2145,7 @@ analyze_function (function *f, bool ipa)
   if (!ipa)
     {
       bool changed = true;
+      bool first = true;
       while (changed)
 	{
 	  changed = false;
@@ -2144,13 +2156,14 @@ analyze_function (function *f, bool ipa)
 			   ignore_stores_p (current_function_decl,
 					    gimple_call_flags
 						 (recursive_calls[i])),
-			   fnode);
+			   fnode, !first);
 	      if (!summary->useful_p (ecf_flags, false))
 		{
 		  remove_summary (lto, nolto, ipa);
 		  return;
 		}
 	    }
+	  first = false;
 	}
     }
   if (summary && !summary->useful_p (ecf_flags))
@@ -2501,11 +2514,11 @@ read_modref_records (lto_input_block *ib, struct data_in *data_in,
 		    }
 		}
 	      modref_access_node a = {offset, size, max_size, parm_offset,
-				      parm_index, parm_offset_known};
+				      parm_index, parm_offset_known, false};
 	      if (nolto_ref_node)
-		nolto_ref_node->insert_access (a, max_accesses);
+		nolto_ref_node->insert_access (a, max_accesses, false);
 	      if (lto_ref_node)
-		lto_ref_node->insert_access (a, max_accesses);
+		lto_ref_node->insert_access (a, max_accesses, false);
 	    }
 	}
     }
@@ -3187,16 +3200,18 @@ ipa_merge_modref_summary_after_inlining (cgraph_edge *edge)
       if (!ignore_stores)
 	{
 	  if (to_info && callee_info)
-	    to_info->stores->merge (callee_info->stores, &parm_map);
+	    to_info->stores->merge (callee_info->stores, &parm_map, false);
 	  if (to_info_lto && callee_info_lto)
-	    to_info_lto->stores->merge (callee_info_lto->stores, &parm_map);
+	    to_info_lto->stores->merge (callee_info_lto->stores, &parm_map,
+					false);
 	}
       if (!(flags & (ECF_CONST | ECF_NOVOPS)))
 	{
 	  if (to_info && callee_info)
-	    to_info->loads->merge (callee_info->loads, &parm_map);
+	    to_info->loads->merge (callee_info->loads, &parm_map, false);
 	  if (to_info_lto && callee_info_lto)
-	    to_info_lto->loads->merge (callee_info_lto->loads, &parm_map);
+	    to_info_lto->loads->merge (callee_info_lto->loads, &parm_map,
+				       false);
 	}
     }
 
@@ -3346,7 +3361,7 @@ get_access_for_fnspec (cgraph_edge *e, attr_fnspec &fnspec,
     size = TYPE_SIZE_UNIT (get_parm_type (e->callee->decl, i));
   modref_access_node a = {0, -1, -1,
 			  map.parm_offset, map.parm_index,
-			  map.parm_offset_known};
+			  map.parm_offset_known, 0};
   poly_int64 size_hwi;
   if (size
       && poly_int_tree_p (size, &size_hwi)
@@ -3399,10 +3414,10 @@ propagate_unknown_call (cgraph_node *node,
 		}
 	      if (cur_summary)
 		changed |= cur_summary->loads->insert
-		  (0, 0, get_access_for_fnspec (e, fnspec, i, map));
+		  (0, 0, get_access_for_fnspec (e, fnspec, i, map), false);
 	      if (cur_summary_lto)
 		changed |= cur_summary_lto->loads->insert
-		  (0, 0, get_access_for_fnspec (e, fnspec, i, map));
+		  (0, 0, get_access_for_fnspec (e, fnspec, i, map), false);
 	    }
 	}
       if (ignore_stores_p (node->decl, ecf_flags))
@@ -3429,10 +3444,10 @@ propagate_unknown_call (cgraph_node *node,
 		}
 	      if (cur_summary)
 		changed |= cur_summary->stores->insert
-		  (0, 0, get_access_for_fnspec (e, fnspec, i, map));
+		  (0, 0, get_access_for_fnspec (e, fnspec, i, map), false);
 	      if (cur_summary_lto)
 		changed |= cur_summary_lto->stores->insert
-		  (0, 0, get_access_for_fnspec (e, fnspec, i, map));
+		  (0, 0, get_access_for_fnspec (e, fnspec, i, map), false);
 	    }
 	}
       if (fnspec.errno_maybe_written_p () && flag_errno_math)
@@ -3491,6 +3506,7 @@ static void
 modref_propagate_in_scc (cgraph_node *component_node)
 {
   bool changed = true;
+  bool first = true;
   int iteration = 0;
 
   while (changed)
@@ -3628,11 +3644,12 @@ modref_propagate_in_scc (cgraph_node *component_node)
 	      if (callee_summary)
 		{
 		  changed |= cur_summary->loads->merge
-				  (callee_summary->loads, &parm_map);
+				  (callee_summary->loads, &parm_map, !first);
 		  if (!ignore_stores)
 		    {
 		      changed |= cur_summary->stores->merge
-				      (callee_summary->stores, &parm_map);
+				      (callee_summary->stores, &parm_map,
+				       !first);
 		      if (!cur_summary->writes_errno
 			  && callee_summary->writes_errno)
 			{
@@ -3644,11 +3661,13 @@ modref_propagate_in_scc (cgraph_node *component_node)
 	      if (callee_summary_lto)
 		{
 		  changed |= cur_summary_lto->loads->merge
-				  (callee_summary_lto->loads, &parm_map);
+				  (callee_summary_lto->loads, &parm_map,
+				   !first);
 		  if (!ignore_stores)
 		    {
 		      changed |= cur_summary_lto->stores->merge
-				      (callee_summary_lto->stores, &parm_map);
+				      (callee_summary_lto->stores, &parm_map,
+				       !first);
 		      if (!cur_summary_lto->writes_errno
 			  && callee_summary_lto->writes_errno)
 			{
@@ -3674,6 +3693,7 @@ modref_propagate_in_scc (cgraph_node *component_node)
 	    }
 	}
       iteration++;
+      first = false;
     }
   if (dump_file)
     fprintf (dump_file,
diff --git a/gcc/params.opt b/gcc/params.opt
index f414dc1a61c..223f0a02111 100644
--- a/gcc/params.opt
+++ b/gcc/params.opt
@@ -1013,6 +1013,10 @@ Maximum depth of DFS walk used by modref escape analysis.
 Common Joined UInteger Var(param_modref_max_escape_points) Init(256) Param Optimization
 Maximum number of escape points tracked by modref per SSA-name.
 
+-param=modref-max-adjustments=
+Common Joined UInteger Var(param_modref_max_adjustments) Init(8) IntegerRange (0, 255) Param Optimization
+Maximum number of times a given range is adjusted during the dataflow
+
 -param=tm-max-aggregate-size=
 Common Joined UInteger Var(param_tm_max_aggregate_size) Init(9) Param Optimization
 Size in bytes after which thread-local aggregates should be instrumented with the logging functions instead of save/restore pairs.
diff --git a/gcc/testsuite/gcc.dg/ipa/modref-1.c b/gcc/testsuite/gcc.dg/ipa/modref-1.c
index 858567d35d5..5314e7dbbf7 100644
--- a/gcc/testsuite/gcc.dg/ipa/modref-1.c
+++ b/gcc/testsuite/gcc.dg/ipa/modref-1.c
@@ -10,15 +10,15 @@ void a(char *ptr, char *ptr2)
 __attribute__((noinline))
 void b(char *ptr)
 {
-  a(ptr+1,&ptr[2]);
+  a(ptr+1,&ptr[3]);
 }
 
 int main()
 {
-  char c[3]={0,1,0};
+  char c[4]={0,1,0,0};
   b(c);
-  return c[0]+c[2];
+  return c[0]+c[3];
 }
 /* Check that both param offsets are determined correctly.  */
 /* { dg-final { scan-ipa-dump "param offset:1" "modref"  } } */
-/* { dg-final { scan-ipa-dump "param offset:2" "modref"  } } */
+/* { dg-final { scan-ipa-dump "param offset:3" "modref"  } } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/modref-4.c b/gcc/testsuite/gcc.dg/tree-ssa/modref-4.c
index 3ac217bafb8..a2b3b1102ec 100644
--- a/gcc/testsuite/gcc.dg/tree-ssa/modref-4.c
+++ b/gcc/testsuite/gcc.dg/tree-ssa/modref-4.c
@@ -10,7 +10,7 @@ void a(char *ptr, char *ptr2)
 __attribute__((noinline))
 void b(char *ptr)
 {
-  a(ptr+1,&ptr[2]);
+  a(ptr+1,&ptr[3]);
 }
 
 int main()
@@ -22,5 +22,5 @@ int main()
 /* Check that both param offsets are determined correctly and the computation
    is optimized out.  */
 /* { dg-final { scan-tree-dump "param offset:1" "modref1"  } } */
-/* { dg-final { scan-tree-dump "param offset:2" "modref1"  } } */
+/* { dg-final { scan-tree-dump "param offset:3" "modref1"  } } */
 /* { dg-final { scan-tree-dump "return 0" "modref1"  } } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/modref-8.c b/gcc/testsuite/gcc.dg/tree-ssa/modref-8.c
new file mode 100644
index 00000000000..15ae4acc03f
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/modref-8.c
@@ -0,0 +1,25 @@
+/* { dg-options "-O2 --param modref-max-adjustments=8 -fdump-tree-modref1"  } */
+/* { dg-do compile } */
+void
+set (char *p)
+{
+   p[1]=1;
+   p[0]=0;
+   p[2]=2;
+   p[4]=4;
+   p[3]=3;
+}
+
+void
+recurse (char *p, int n)
+{
+	*p = 0;
+	if (n)
+	  recurse (p+1,n-1);
+}
+/* { dg-final { scan-tree-dump-not "param=modref-max-accesses" "modref1" } } */
+/* { dg-final { scan-tree-dump "param=modref-max-adjustments" "modref1" } } */
+/* In set all accesses should merge together.  */
+/* { dg-final { scan-tree-dump "access: Parm 0 param offset:0 offset:0 size:8 max_size:40" "modref1" } } */
+/* In recurse we should cap the recrusion after 8 attempts and set max_size to -1.  */
+/* { dg-final { scan-tree-dump "access: Parm 0 param offset:0 offset:0 size:8 max_size:-1 adjusted 8 times" "modref1" } } */
