diff --git a/gcc/ipa-sra.cc b/gcc/ipa-sra.cc
index c35c0d7162a..93fceeafc73 100644
--- a/gcc/ipa-sra.cc
+++ b/gcc/ipa-sra.cc
@@ -173,6 +173,10 @@ struct GTY(()) isra_param_desc
   unsigned param_size_limit : ISRA_ARG_SIZE_LIMIT_BITS;
   /* Sum of unit sizes of all certain replacements.  */
   unsigned size_reached : ISRA_ARG_SIZE_LIMIT_BITS;
+  /* Minimum offset that is known to be safe to dereference because of callers
+     pass pointers to DECLs of at least this size or because of dereferences in
+     callers.  */
+  unsigned safe_size : ISRA_ARG_SIZE_LIMIT_BITS;
 
   /* A parameter that is used only in call arguments and can be removed if all
      concerned actual arguments are removed.  */
@@ -185,6 +189,12 @@ struct GTY(()) isra_param_desc
      not construct the argument just to pass it to calls.  Only meaningful for
      by_ref parameters.  */
   unsigned not_specially_constructed : 1;
+  /* Only meaningful for by_ref parameters.  If set, this parameter can only be
+     a split candidate if all callers pass pointers that are known to point to
+     a chunk of memory large enough to contain all accesses.  */
+  unsigned conditionally_dereferenceable : 1;
+  /* Set when safe_size has been updated from at least one caller.  */
+  unsigned safe_size_set : 1;
 };
 
 /* Structure used when generating summaries that describes a parameter.  */
@@ -220,6 +230,10 @@ struct gensum_param_desc
      without performing further checks (for example because it is a
      REFERENCE_TYPE)?  */
   bool safe_ref;
+  /* Only meaningful for by_ref parameters.  If set, this parameter can only be
+     a split candidate if all callers pass pointers that are known to point to
+     a chunk of memory large enough to contain all accesses.  */
+  bool conditionally_dereferenceable;
 
   /* The number of this parameter as they are ordered in function decl.  */
   int param_number;
@@ -332,10 +346,12 @@ struct isra_param_flow
 
   /* Offset within the formal parameter.  */
   unsigned unit_offset;
-  /* Size of the portion of the formal parameter that is being passed.  */
+  /* When aggregate_pass_through is set, this is the size of the portion of an
+     aggregate formal parameter that is being passed.  Otherwise, this is size
+     of pointed to memory that is known to be valid be dereferenced.  */
   unsigned unit_size : ISRA_ARG_SIZE_LIMIT_BITS;
 
-  /* True when the value of this actual copy is a portion of a formal
+  /* True when the value of this actual argument is a portion of a formal
      parameter.  */
   unsigned aggregate_pass_through : 1;
   /* True when the value of this actual copy is a verbatim pass through of an
@@ -425,10 +441,13 @@ ipa_sra_function_summaries::duplicate (cgraph_node *, cgraph_node *,
 
       d->param_size_limit = s->param_size_limit;
       d->size_reached = s->size_reached;
+      d->safe_size = s->safe_size;
       d->locally_unused = s->locally_unused;
       d->split_candidate = s->split_candidate;
       d->by_ref = s->by_ref;
       d->not_specially_constructed = s->not_specially_constructed;
+      d->conditionally_dereferenceable = s->conditionally_dereferenceable;
+      d->safe_size_set = s->safe_size_set;
 
       unsigned acc_count = vec_safe_length (s->accesses);
       vec_safe_reserve_exact (d->accesses, acc_count);
@@ -537,6 +556,8 @@ isra_call_summary::dump (FILE *f)
 	fprintf (f, "      Aggregate pass through from the param given above, "
 		 "unit offset: %u , unit size: %u\n",
 		 ipf->unit_offset, ipf->unit_size);
+      else if (ipf->unit_size > 0)
+	fprintf (f, "      Known dereferenceable size: %u\n", ipf->unit_size);
       if (ipf->pointer_pass_through)
 	fprintf (f, "      Pointer pass through from the param given above, "
 		 "safe_to_import_accesses: %u\n", ipf->safe_to_import_accesses);
@@ -717,8 +738,11 @@ dump_gensum_param_descriptor (FILE *f, gensum_param_desc *desc)
       return;
     }
   if (desc->by_ref)
-    fprintf (f, "    %s by_ref with %u pass throughs\n",
-	     desc->safe_ref ? "safe" : "unsafe", desc->ptr_pt_count);
+    fprintf (f, "    %s%s by_ref with %u pass throughs\n",
+	     desc->safe_ref ? "safe" : "unsafe",
+	     desc->conditionally_dereferenceable
+	     ? " conditionally_dereferenceable" : " ok",
+	     desc->ptr_pt_count);
 
   for (gensum_param_access *acc = desc->accesses; acc; acc = acc->next_sibling)
     dump_gensum_access (f, acc, 2);
@@ -756,16 +780,23 @@ dump_isra_param_descriptor (FILE *f, isra_param_desc *desc, bool hints)
     }
   if (!desc->split_candidate)
     {
-      fprintf (f, "    not a candidate for splitting\n");
+      fprintf (f, "    not a candidate for splitting");
+      if (hints && desc->by_ref && desc->safe_size_set)
+	fprintf (f, ", safe_size: %u", (unsigned) desc->safe_size);
+      fprintf (f, "\n");
       return;
     }
   fprintf (f, "    param_size_limit: %u, size_reached: %u%s",
 	   desc->param_size_limit, desc->size_reached,
 	   desc->by_ref ? ", by_ref" : "");
+  if (desc->by_ref && desc->conditionally_dereferenceable)
+    fprintf (f, ", conditionally_dereferenceable");
   if (hints)
     {
       if (desc->by_ref && !desc->not_specially_constructed)
 	fprintf (f, ", args_specially_constructed");
+      if (desc->by_ref && desc->safe_size_set)
+	fprintf (f, ", safe_size: %u", (unsigned) desc->safe_size);
     }
   fprintf (f, "\n");
 
@@ -2085,8 +2116,19 @@ isra_analyze_call (cgraph_edge *cs)
 	  bool reverse;
 	  tree base = get_ref_base_and_extent (TREE_OPERAND (arg, 0), &poffset,
 					       &psize, &pmax_size, &reverse);
-	  /* TODO: Next patch will need the offset too, so we cannot use
-	     get_base_address. */
+	  HOST_WIDE_INT offset;
+	  unsigned HOST_WIDE_INT ds;
+	  if (DECL_P (base)
+	      && (poffset.is_constant (&offset))
+	      && tree_fits_uhwi_p (DECL_SIZE (base))
+	      && ((ds = tree_to_uhwi (DECL_SIZE (base)) - offset)
+		  < ISRA_ARG_SIZE_LIMIT * BITS_PER_UNIT))
+	    {
+	      csum->init_inputs (count);
+	      gcc_assert (!csum->m_arg_flow[i].aggregate_pass_through);
+	      csum->m_arg_flow[i].unit_size = ds / BITS_PER_UNIT;
+	    }
+
 	  if (TREE_CODE (base) == VAR_DECL
 	      && !TREE_STATIC (base)
 	      && !loaded_decls->contains (base))
@@ -2308,9 +2350,14 @@ check_gensum_access (struct function *fun, tree parm, gensum_param_desc *desc,
 	  int idx = (entry_bb_index * unsafe_by_ref_count + desc->deref_index);
 	  if ((access->offset + access->size) > bb_dereferences[idx])
 	    {
-	      disqualify_split_candidate (desc, "Would create a possibly "
-					  "illegal dereference in a caller.");
-	      return true;
+	      if (!dereference_probable_p (fun, access))
+		{
+		  disqualify_split_candidate (desc, "Would create a possibly "
+					      "illegal dereference in a "
+					      "caller.");
+		  return true;
+		}
+	      desc->conditionally_dereferenceable = true;
 	    }
 	}
     }
@@ -2539,6 +2586,7 @@ process_scan_results (cgraph_node *node, struct function *fun,
       d->locally_unused = s->locally_unused;
       d->split_candidate = s->split_candidate;
       d->by_ref = s->by_ref;
+      d->conditionally_dereferenceable = s->conditionally_dereferenceable;
 
       for (gensum_param_access *acc = s->accesses;
 	   acc;
@@ -2693,11 +2741,14 @@ isra_write_node_summary (output_block *ob, cgraph_node *node)
 	}
       streamer_write_uhwi (ob, desc->param_size_limit);
       streamer_write_uhwi (ob, desc->size_reached);
+      gcc_assert (desc->safe_size == 0);
       bitpack_d bp = bitpack_create (ob->main_stream);
       bp_pack_value (&bp, desc->locally_unused, 1);
       bp_pack_value (&bp, desc->split_candidate, 1);
       bp_pack_value (&bp, desc->by_ref, 1);
       gcc_assert (!desc->not_specially_constructed);
+      bp_pack_value (&bp, desc->conditionally_dereferenceable, 1);
+      gcc_assert (!desc->safe_size_set);
       streamer_write_bitpack (&bp);
     }
   bitpack_d bp = bitpack_create (ob->main_stream);
@@ -2814,11 +2865,14 @@ isra_read_node_info (struct lto_input_block *ib, cgraph_node *node,
 	}
       desc->param_size_limit = streamer_read_uhwi (ib);
       desc->size_reached = streamer_read_uhwi (ib);
+      desc->safe_size = 0;
       bitpack_d bp = streamer_read_bitpack (ib);
       desc->locally_unused = bp_unpack_value (&bp, 1);
       desc->split_candidate = bp_unpack_value (&bp, 1);
       desc->by_ref = bp_unpack_value (&bp, 1);
       desc->not_specially_constructed = 0;
+      desc->conditionally_dereferenceable = bp_unpack_value (&bp, 1);
+      desc->safe_size_set = 0;
     }
   bitpack_d bp = streamer_read_bitpack (ib);
   ifs->m_candidate = bp_unpack_value (&bp, 1);
@@ -3265,44 +3319,65 @@ isra_mark_caller_param_used (isra_func_summary *from_ifs, int input_idx,
     }
 }
 
-/* Set all param hints in DESC to the pessimistic values.  */
+/* Combine safe_size of DESC with SIZE and return true if it has changed.  */
 
-static void
+static bool
+update_safe_size (isra_param_desc *desc, unsigned size)
+{
+  if (!desc->safe_size_set)
+    {
+      desc->safe_size_set = 1;
+      desc->safe_size = size;
+      return true;
+    }
+  if (desc->safe_size <= size)
+    return false;
+  desc->safe_size = size;
+  return true;
+}
+
+/* Set all param hints in DESC to the pessimistic values.  Return true if any
+   hints that need to potentially trigger further propagation have changed.  */
+
+static bool
 flip_all_hints_pessimistic (isra_param_desc *desc)
 {
   desc->not_specially_constructed = true;
-
-  return;
+  return update_safe_size (desc, 0);
 }
 
-/* Because we have not analyzed a caller, go over all parameter int flags of
-   NODE and turn them pessimistic.  */
+/* Because we have not analyzed or otherwise problematic caller, go over all
+   parameter int flags of IFS describing a call graph node of a calllee and
+   turn them pessimistic.  Return true if any hints that need to potentially
+   trigger further propagation have changed.  */
 
-static void
-flip_all_param_hints_pessimistic (cgraph_node *node)
+static bool
+flip_all_param_hints_pessimistic (isra_func_summary *ifs)
 {
-  isra_func_summary *ifs = func_sums->get (node);
   if (!ifs || !ifs->m_candidate)
-    return;
+    return false;
 
+  bool ret = false;
   unsigned param_count = vec_safe_length (ifs->m_parameters);
 
   for (unsigned i = 0; i < param_count; i++)
-    flip_all_hints_pessimistic (&(*ifs->m_parameters)[i]);
+    ret |= flip_all_hints_pessimistic (&(*ifs->m_parameters)[i]);
 
-  return;
+  return ret;
 }
 
-/* Propagate hints accross edge CS which ultimately leads to CALLEE.  */
+/* Propagate hints accross edge CS which ultimately leads to a node described
+   by TO_IFS.  Return true if any hints of the callee which should potentially
+   trigger further propagation have changed.  */
 
-static void
-propagate_param_hints (cgraph_edge *cs, cgraph_node *callee)
+static bool
+propagate_param_hints_accross_call (cgraph_edge *cs, isra_func_summary *to_ifs)
 {
-  isra_call_summary *csum = call_sums->get (cs);
-  isra_func_summary *to_ifs = func_sums->get (callee);
   if (!to_ifs || !to_ifs->m_candidate)
-    return;
+    return false;
 
+  isra_call_summary *csum = call_sums->get (cs);
+  bool ret = false;
   unsigned args_count = csum->m_arg_flow.length ();
   unsigned param_count = vec_safe_length (to_ifs->m_parameters);
 
@@ -3311,14 +3386,62 @@ propagate_param_hints (cgraph_edge *cs, cgraph_node *callee)
       isra_param_desc *desc = &(*to_ifs->m_parameters)[i];
       if (i >= args_count)
 	{
-	  flip_all_hints_pessimistic (desc);
+	  ret |= flip_all_hints_pessimistic (desc);
 	  continue;
 	}
 
-      if (desc->by_ref && !csum->m_arg_flow[i].constructed_for_calls)
-	desc->not_specially_constructed = true;
+      if (desc->by_ref)
+	{
+	  isra_param_flow *ipf = &csum->m_arg_flow[i];
+
+	  if (!ipf->constructed_for_calls)
+	    desc->not_specially_constructed = true;
+
+	  if (ipf->pointer_pass_through)
+	    {
+	      isra_func_summary *from_ifs = func_sums->get (cs->caller);
+	      int srcidx = get_single_param_flow_source (ipf);
+	      if (vec_safe_length (from_ifs->m_parameters) > (unsigned) srcidx)
+		{
+		  isra_param_desc *src_d = &(*from_ifs->m_parameters)[srcidx];
+		  if (src_d->safe_size_set)
+		    ret |= update_safe_size (desc, src_d->safe_size);
+		}
+	      else
+		ret |= update_safe_size (desc, 0);
+	    }
+	  else if (!ipf->aggregate_pass_through)
+	    ret |= update_safe_size (desc, ipf->unit_size);
+	  else
+	    /* LTOing type-mismatched programs can end up here.  */
+	    ret |= update_safe_size (desc, 0);
+	}
+    }
+  return ret;
+}
+
+/* Propagate hints from NODE described by FROM_IFS to all its (dorect) callees,
+   push those that may need re-visiting onto STACK.  */
+
+static void
+propagate_hints_to_all_callees (cgraph_node *node, isra_func_summary *from_ifs,
+				vec<cgraph_node *> *stack)
+{
+  for (cgraph_edge *cs = node->callees; cs; cs = cs->next_callee)
+    {
+      enum availability availability;
+      cgraph_node *callee = cs->callee->function_symbol (&availability);
+      isra_func_summary *to_ifs = func_sums->get (callee);
+      if (!from_ifs)
+	{
+	  if (flip_all_param_hints_pessimistic (to_ifs)
+	      && ipa_edge_within_scc (cs))
+	    isra_push_node_to_stack (callee, to_ifs, stack);
+	}
+      else if (propagate_param_hints_accross_call (cs, to_ifs)
+	       && ipa_edge_within_scc (cs))
+	isra_push_node_to_stack (callee, to_ifs, stack);
     }
-  return;
 }
 
 /* Propagate information that any parameter is not used only locally within a
@@ -3990,7 +4113,8 @@ adjust_parameter_descriptions (cgraph_node *node, isra_func_summary *ifs)
       check_surviving = true;
       cinfo->param_adjustments->get_surviving_params (&surviving_params);
     }
-  bool dumped_first = false;
+  auto_vec <unsigned> dump_dead_indices;
+  auto_vec <unsigned> dump_bad_cond_indices;
   for (unsigned i = 0; i < len; i++)
     {
       isra_param_desc *desc = &(*ifs->m_parameters)[i];
@@ -4009,19 +4133,23 @@ adjust_parameter_descriptions (cgraph_node *node, isra_func_summary *ifs)
 	  desc->split_candidate = false;
 
 	  if (dump_file && (dump_flags & TDF_DETAILS))
-	    {
-	      if (!dumped_first)
-		{
-		  fprintf (dump_file,
-			   "The following parameters of %s are dead on "
-			   "arrival:", node->dump_name ());
-		  dumped_first = true;
-		}
-	      fprintf (dump_file, " %u", i);
-	    }
+	    dump_dead_indices.safe_push (i);
 	}
       else
 	{
+	  if (desc->split_candidate && desc->conditionally_dereferenceable)
+	    {
+	      gcc_assert (desc->safe_size_set);
+	      for (param_access *pa : *desc->accesses)
+		if ((pa->unit_offset + pa->unit_size) > desc->safe_size)
+		  {
+		    if (dump_file && (dump_flags & TDF_DETAILS))
+		      dump_bad_cond_indices.safe_push (i);
+		    desc->split_candidate = false;
+		    break;
+		  }
+	    }
+
 	  if (desc->split_candidate)
 	    {
 	      if (desc->by_ref && !desc->not_specially_constructed)
@@ -4039,8 +4167,22 @@ adjust_parameter_descriptions (cgraph_node *node, isra_func_summary *ifs)
 	}
     }
 
-  if (dumped_first)
-    fprintf (dump_file, "\n");
+  if (!dump_dead_indices.is_empty ())
+    {
+      fprintf (dump_file, "The following parameters of %s are dead on arrival:",
+	       node->dump_name ());
+      for (unsigned i : dump_dead_indices)
+	fprintf (dump_file, " %u", i);
+      fprintf (dump_file, "\n");
+    }
+  if (!dump_bad_cond_indices.is_empty ())
+    {
+      fprintf (dump_file, "The following parameters of %s are not safe to "
+	       "derefernce in all callers:", node->dump_name ());
+      for (unsigned i : dump_bad_cond_indices)
+	fprintf (dump_file, " %u", i);
+      fprintf (dump_file, "\n");
+    }
 
   return ret;
 }
@@ -4121,17 +4263,16 @@ ipa_sra_analysis (void)
       for (cgraph_node *v : cycle_nodes)
 	{
 	  isra_func_summary *ifs = func_sums->get (v);
-	  for (cgraph_edge *cs = v->callees; cs; cs = cs->next_callee)
-	    {
-	      enum availability availability;
-	      cgraph_node *callee = cs->callee->function_symbol (&availability);
-	      if (!ifs)
-		{
-		  flip_all_param_hints_pessimistic (callee);
-		  continue;
-		}
-	      propagate_param_hints (cs, callee);
-	    }
+	  propagate_hints_to_all_callees (v, ifs, &stack);
+	}
+
+      while (!stack.is_empty ())
+	{
+	  cgraph_node *node = stack.pop ();
+	  isra_func_summary *ifs = func_sums->get (node);
+	  gcc_checking_assert (ifs && ifs->m_queued);
+	  ifs->m_queued = false;
+	  propagate_hints_to_all_callees (node, ifs, &stack);
 	}
 
       cycle_nodes.release ();
diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-sra-26.c b/gcc/testsuite/gcc.dg/ipa/ipa-sra-26.c
new file mode 100644
index 00000000000..08a40da1482
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ipa/ipa-sra-26.c
@@ -0,0 +1,31 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-ipa-sra-details"  } */
+
+struct S
+{
+  short a, b, c;
+};
+
+extern int gc;
+extern int *arr;
+
+static void __attribute__((noinline))
+foo (struct S *p)
+{
+  for (int i = 0; i < gc; i++)
+    arr += p->b;
+}
+
+void
+bar (short a, short b, short c)
+{
+  struct S s;
+  s.a = a;
+  s.b = b;
+  s.c = c;
+  foo (&s);
+  return;
+}
+
+/* { dg-final { scan-ipa-dump "Will split parameter" "sra" } } */
+
diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-sra-27.c b/gcc/testsuite/gcc.dg/ipa/ipa-sra-27.c
new file mode 100644
index 00000000000..b815e8a83b1
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ipa/ipa-sra-27.c
@@ -0,0 +1,49 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-ipa-sra-details"  } */
+
+struct S
+{
+  short a, b, c;
+};
+
+extern int gc;
+extern int *arr;
+
+static void __attribute__((noinline))
+foo (struct S *p)
+{
+  for (int i = 0; i < gc; i++)
+    arr += p->b;
+}
+
+static void __attribute__((noinline))
+baz (struct S *p)
+{
+  foo (p);
+  gc = p->a + p->c;
+}
+
+void
+bar (short a, short b, short c)
+{
+  struct S s;
+  s.a = a;
+  s.b = b;
+  s.c = c;
+  foo (&s);
+  return;
+}
+
+void
+bar2 (short a, short b, short c)
+{
+  struct S s;
+  s.a = a;
+  s.b = b;
+  s.c = c;
+  baz (&s);
+  return;
+}
+
+/* { dg-final { scan-ipa-dump-times "Will split parameter" 2 "sra" } } */
+
diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-sra-28.c b/gcc/testsuite/gcc.dg/ipa/ipa-sra-28.c
new file mode 100644
index 00000000000..d77d33a3608
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ipa/ipa-sra-28.c
@@ -0,0 +1,51 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-ipa-sra-details"  } */
+
+struct S
+{
+  short a, b, c;
+};
+
+volatile int gc;
+volatile int *arr;
+
+static void __attribute__((noinline))
+foo (struct S *p)
+{
+  for (int i = 0; i < gc; i++)
+    arr += p->b;
+}
+
+void
+bar (short a, short b, short c)
+{
+  struct S s;
+  s.a = a;
+  s.b = b;
+  s.c = c;
+  foo (&s);
+  return;
+}
+
+void
+baz (void)
+{
+  foo ((struct S *) 0);
+}
+
+void __attribute__((noipa))
+confuse (void)
+{
+  gc = 0;
+  baz ();
+}
+
+int
+main (int argc, char **argv)
+{
+  confuse ();
+  return 0;
+}
+
+/* { dg-final { scan-ipa-dump-not "Will split parameter" "sra" } } */
+
