diff mbox series

Handle fnspec in ipa-modref II

Message ID 20201106000009.GB65107@kam.mff.cuni.cz
State New
Headers show
Series Handle fnspec in ipa-modref II | expand

Commit Message

Jan Hubicka Nov. 6, 2020, midnight UTC
Hi,
this patch adds the IPA propagation support for fnspecs in ipa-modref.
Fnspec string are collected to a new edge summary (since they do depend
on the call statement and not only the called function) and stored to
summaries.  Since ipa-modref is first pass that now cares about jump funitions
to non-definitions lto streaming needed to be updated, too.

Disambiguations on cc1plus:

Alias oracle query stats:                                                       
  refs_may_alias_p: 65808750 disambiguations, 75664890 queries                  
  ref_maybe_used_by_call_p: 153485 disambiguations, 66711204 queries            
  call_may_clobber_ref_p: 22816 disambiguations, 28889 queries                  
  nonoverlapping_component_refs_p: 0 disambiguations, 36846 queries             
  nonoverlapping_refs_since_match_p: 27271 disambiguations, 58917 must overlaps, 86958 queries
  aliasing_component_refs_p: 65808 disambiguations, 2067256 queries             
  TBAA oracle: 25929211 disambiguations 60395141 queries                        
               12391384 are in alias set 0                                      
               10783783 queries asked about the same object                     
               126 queries asked about the same alias set                       
               0 access volatile                                                
               9598698 are dependent in the DAG                                 
               1691939 are aritificially in conflict with void *                
                                                                                
Modref stats:                                                                   
  modref use: 14284 disambiguations, 53336 queries                              
  modref clobber: 1660281 disambiguations, 2130440 queries                      
  4311165 tbaa queries (2.023603 per modref query)                              
  685304 base compares (0.321673 per modref query)                              
                                                                                
PTA query stats:                                                                
  pt_solution_includes: 959190 disambiguations, 13169678 queries                
  pt_solutions_intersect: 1050969 disambiguations, 13246686 queries             

This is about 10% up compared to last report, but it may be also caused
by C++ new/delete operator support that I commited today.

Bootstrapped/regtested x86_64-linux, plan to commit it tomorrow after crafting
few testcases.

gcc/ChangeLog:

2020-11-06  Jan Hubicka  <hubicka@ucw.cz>

	* attr-fnspec.h (attr_fnspec::get_str): New accessor
	* ipa-fnsummary.c (read_ipa_call_summary): Store also parm info
	for builtins.
	* ipa-modref.c (class fnspec_summary): New type.
	(class fnspec_summaries_t): New type.
	(modref_summary::modref_summary): Initialize writes_errno.
	(struct modref_summary_lto): Add writes_errno.
	(modref_summary_lto::modref_summary_lto): Initialize writes_errno.
	(modref_summary::dump): Check for NULL pointers.
	(modref_summary_lto::dump): Dump writes_errno.
	(collapse_loads): Move up in source file.
	(collapse_stores): New function.
	(process_fnspec): Handle also internal calls.
	(analyze_call): Likewise.
	(analyze_stmt): Store fnspec string if needed.
	(analyze_function): Initialize fnspec_sumarries.
	(modref_summaries_lto::duplicate): Copy writes_errno.
	(modref_write): Store writes_errno and fnspec summaries.
	(read_section): Read writes_errno and fnspec summaries.
	(modref_read): Initialize fnspec summaries.
	(update_signature): Fix formating.
	(compute_parm_map): Return true if sucessful.
	(get_parm_type): New function.
	(get_access_for_fnspec): New function.
	(propagate_unknown_call): New function.
	(modref_propagate_in_scc): Use it.
	(pass_ipa_modref::execute): Delete fnspec_summaries.
	(ipa_modref_c_finalize): Delete fnspec_summaries.
	* ipa-prop.c: Include attr-fnspec.h.
	(ipa_compute_jump_functions_for_bb):  Also compute jump functions
	for functions with fnspecs.
	(ipa_read_edge_info): Read jump functions for builtins.
diff mbox series

Patch

diff --git a/gcc/attr-fnspec.h b/gcc/attr-fnspec.h
index 78b1a5a2b1c..28135328437 100644
--- a/gcc/attr-fnspec.h
+++ b/gcc/attr-fnspec.h
@@ -246,6 +246,13 @@  public:
 
   /* Check validity of the string.  */
   void verify ();
+
+  /* Return the fnspec string.  */
+  const char *
+  get_str ()
+  {
+    return str;
+  }
 };
 
 extern attr_fnspec gimple_call_fnspec (const gcall *stmt);
diff --git a/gcc/ipa-fnsummary.c b/gcc/ipa-fnsummary.c
index 0393f2cad11..b8f4a0a9091 100644
--- a/gcc/ipa-fnsummary.c
+++ b/gcc/ipa-fnsummary.c
@@ -4304,7 +4301,11 @@  read_ipa_call_summary (class lto_input_block *ib, struct cgraph_edge *e,
   if (es)
     edge_set_predicate (e, &p);
   length = streamer_read_uhwi (ib);
-  if (length && es && e->possibly_call_in_translation_unit_p ())
+  if (length && es
+      && (e->possibly_call_in_translation_unit_p ()
+	  /* Also stream in jump functions to builtins in hope that they
+	     will get fnspecs.  */
+	  || fndecl_built_in_p (e->callee->decl, BUILT_IN_NORMAL)))
     {
       es->param.safe_grow_cleared (length, true);
       for (i = 0; i < length; i++)
diff --git a/gcc/ipa-modref.c b/gcc/ipa-modref.c
index 9df3d2bcf2d..f43d9c9c1ed 100644
--- a/gcc/ipa-modref.c
+++ b/gcc/ipa-modref.c
@@ -62,8 +62,47 @@  along with GCC; see the file COPYING3.  If not see
 #include "attr-fnspec.h"
 #include "symtab-clones.h"
 
+/* We record fnspec specifiers for call edges since they depends on actual
+   gimple statements.  */
+
+class fnspec_summary
+{
+public:
+  char *fnspec;
+
+  fnspec_summary ()
+  : fnspec (NULL)
+  {
+  }
+
+  ~fnspec_summary ()
+  {
+    free (fnspec);
+  }
+};
+
+/* Summary holding fnspec string for a given call.  */
+
+class fnspec_summaries_t : public call_summary <fnspec_summary *>
+{
+public:
+  fnspec_summaries_t (symbol_table *symtab)
+      : call_summary <fnspec_summary *> (symtab) {}
+  /* Hook that is called by summary when an edge is duplicated.  */
+  virtual void duplicate (cgraph_edge *,
+			  cgraph_edge *,
+			  fnspec_summary *src,
+			  fnspec_summary *dst)
+  {
+    dst->fnspec = xstrdup (src->fnspec);
+  }
+};
+
+static fnspec_summaries_t *fnspec_summaries = NULL;
+
 /* Class (from which there is one global instance) that holds modref summaries
    for all analyzed functions.  */
+
 class GTY((user)) modref_summaries
   : public fast_function_summary <modref_summary *, va_gc>
 {
@@ -86,6 +125,7 @@  class modref_summary_lto;
 
 /* Class (from which there is one global instance) that holds modref summaries
    for all analyzed functions.  */
+
 class GTY((user)) modref_summaries_lto
   : public fast_function_summary <modref_summary_lto *, va_gc>
 {
@@ -108,23 +148,26 @@  public:
 
 /* Global variable holding all modref summaries
    (from analysis to IPA propagation time).  */
+
 static GTY(()) fast_function_summary <modref_summary *, va_gc>
 	 *summaries;
 
 /* Global variable holding all modref optimizaiton summaries
    (from IPA propagation time or used by local optimization pass).  */
+
 static GTY(()) fast_function_summary <modref_summary *, va_gc>
 	 *optimization_summaries;
 
 /* LTO summaries hold info from analysis to LTO streaming or from LTO
    stream-in through propagation to LTO stream-out.  */
+
 static GTY(()) fast_function_summary <modref_summary_lto *, va_gc>
 	 *summaries_lto;
 
 /* Summary for a single function which this pass produces.  */
 
 modref_summary::modref_summary ()
-  : loads (NULL), stores (NULL)
+  : loads (NULL), stores (NULL), writes_errno (NULL)
 {
 }
 
@@ -161,6 +204,7 @@  struct GTY(()) modref_summary_lto
      more verbose and thus more likely to hit the limits.  */
   modref_records_lto *loads;
   modref_records_lto *stores;
+  bool writes_errno;
 
   modref_summary_lto ();
   ~modref_summary_lto ();
@@ -171,7 +215,7 @@  struct GTY(()) modref_summary_lto
 /* Summary for a single function which this pass produces.  */
 
 modref_summary_lto::modref_summary_lto ()
-  : loads (NULL), stores (NULL)
+  : loads (NULL), stores (NULL), writes_errno (NULL)
 {
 }
 
@@ -316,10 +360,16 @@  dump_lto_records (modref_records_lto *tt, FILE *out)
 void
 modref_summary::dump (FILE *out)
 {
-  fprintf (out, "  loads:\n");
-  dump_records (loads, out);
-  fprintf (out, "  stores:\n");
-  dump_records (stores, out);
+  if (loads)
+    {
+      fprintf (out, "  loads:\n");
+      dump_records (loads, out);
+    }
+  if (stores)
+    {
+      fprintf (out, "  stores:\n");
+      dump_records (stores, out);
+    }
   if (writes_errno)
     fprintf (out, "  Writes errno\n");
 }
@@ -333,6 +383,8 @@  modref_summary_lto::dump (FILE *out)
   dump_lto_records (loads, out);
   fprintf (out, "  stores:\n");
   dump_lto_records (stores, out);
+  if (writes_errno)
+    fprintf (out, "  Writes errno\n");
 }
 
 /* Get function summary for FUNC if it exists, return NULL otherwise.  */
@@ -653,12 +708,59 @@  get_access_for_fnspec (gcall *call, attr_fnspec &fnspec,
   return a;
 }
 
+/* Collapse loads and return true if something changed.  */
+
+static bool
+collapse_loads (modref_summary *cur_summary,
+		modref_summary_lto *cur_summary_lto)
+{
+  bool changed = false;
+
+  if (cur_summary && !cur_summary->loads->every_base)
+    {
+      cur_summary->loads->collapse ();
+      changed = true;
+    }
+  if (cur_summary_lto
+      && !cur_summary_lto->loads->every_base)
+    {
+      cur_summary_lto->loads->collapse ();
+      changed = true;
+    }
+  return changed;
+}
+
+/* Collapse loads and return true if something changed.  */
+
+static bool
+collapse_stores (modref_summary *cur_summary,
+		modref_summary_lto *cur_summary_lto)
+{
+  bool changed = false;
+
+  if (cur_summary && !cur_summary->stores->every_base)
+    {
+      cur_summary->stores->collapse ();
+      changed = true;
+    }
+  if (cur_summary_lto
+      && !cur_summary_lto->stores->every_base)
+    {
+      cur_summary_lto->stores->collapse ();
+      changed = true;
+    }
+  return changed;
+}
+
+
 /* Apply side effects of call STMT to CUR_SUMMARY using FNSPEC.
    If IGNORE_STORES is true ignore them.
    Return false if no useful summary can be produced.   */
 
 static bool
-process_fnspec (modref_summary *cur_summary, gcall *call, bool ignore_stores)
+process_fnspec (modref_summary *cur_summary,
+		modref_summary_lto *cur_summary_lto,
+		gcall *call, bool ignore_stores)
 {
   attr_fnspec fnspec = gimple_call_fnspec (call);
   if (!fnspec.known_p ())
@@ -668,13 +770,13 @@  process_fnspec (modref_summary *cur_summary, gcall *call, bool ignore_stores)
 		 IDENTIFIER_POINTER (DECL_NAME (gimple_call_fndecl (call))));
       if (ignore_stores)
 	{
-	  cur_summary->loads->collapse ();
+	  collapse_loads (cur_summary, cur_summary_lto);
 	  return true;
 	}
       return false;
     }
   if (fnspec.global_memory_read_p ())
-    cur_summary->loads->collapse ();
+    collapse_loads (cur_summary, cur_summary_lto);
   else
     {
       for (unsigned int i = 0; i < gimple_call_num_args (call); i++)
@@ -689,18 +791,25 @@  process_fnspec (modref_summary *cur_summary, gcall *call, bool ignore_stores)
 	      continue;
 	    if (map.parm_index == -1)
 	      {
-		cur_summary->loads->collapse ();
+		collapse_loads (cur_summary, cur_summary_lto);
 		break;
 	      }
-	    cur_summary->loads->insert (0, 0,
-					get_access_for_fnspec (call,
-							       fnspec, i, map));
+	    if (cur_summary)
+	      cur_summary->loads->insert (0, 0,
+					  get_access_for_fnspec (call,
+								 fnspec, i,
+								 map));
+	    if (cur_summary_lto)
+	      cur_summary_lto->loads->insert (0, 0,
+					      get_access_for_fnspec (call,
+								     fnspec, i,
+								     map));
 	  }
     }
   if (ignore_stores)
     return true;
   if (fnspec.global_memory_written_p ())
-    cur_summary->stores->collapse ();
+    collapse_stores (cur_summary, cur_summary_lto);
   else
     {
       for (unsigned int i = 0; i < gimple_call_num_args (call); i++)
@@ -715,16 +824,27 @@  process_fnspec (modref_summary *cur_summary, gcall *call, bool ignore_stores)
 	      continue;
 	    if (map.parm_index == -1)
 	      {
-		cur_summary->stores->collapse ();
+		collapse_stores (cur_summary, cur_summary_lto);
 		break;
 	      }
-	    cur_summary->stores->insert (0, 0,
-					 get_access_for_fnspec (call,
-								fnspec, i,
-								map));
+	    if (cur_summary)
+	      cur_summary->stores->insert (0, 0,
+					   get_access_for_fnspec (call,
+								  fnspec, i,
+								  map));
+	    if (cur_summary_lto)
+	      cur_summary_lto->stores->insert (0, 0,
+					       get_access_for_fnspec (call,
+								      fnspec, i,
+								      map));
 	  }
       if (fnspec.errno_maybe_written_p () && flag_errno_math)
-	cur_summary->writes_errno = true;
+	{
+	  if (cur_summary)
+	    cur_summary->writes_errno = true;
+	  if (cur_summary_lto)
+	    cur_summary_lto->writes_errno = true;
+	}
     }
   return true;
 }
@@ -733,7 +853,7 @@  process_fnspec (modref_summary *cur_summary, gcall *call, bool ignore_stores)
    Remember recursive calls in RECURSIVE_CALLS.  */
 
 static bool
-analyze_call (modref_summary *cur_summary,
+analyze_call (modref_summary *cur_summary, modref_summary_lto *cur_summary_lto,
 	      gcall *stmt, vec <gimple *> *recursive_calls)
 {
   /* Check flags on the function call.  In certain cases, analysis can be
@@ -759,21 +879,13 @@  analyze_call (modref_summary *cur_summary,
   /* Check if this is an indirect call.  */
   if (!callee)
     {
-      /* If the indirect call does not write memory, our store summary is
-	 unaffected, but we have to discard our loads summary (we don't know
-	 anything about the loads that the called function performs).  */
-      if (ignore_stores)
-	{
-	  if (dump_file)
-	    fprintf (dump_file, " - Indirect call which does not write memory, "
-		    "discarding loads.\n");
-	  cur_summary->loads->collapse ();
-	  return true;
-	}
       if (dump_file)
-	fprintf (dump_file, " - Indirect call.\n");
-      return false;
+	fprintf (dump_file, gimple_call_internal_p (stmt)
+		 ? " - Internal call" : " - Indirect call.\n");
+      return process_fnspec (cur_summary, cur_summary_lto, stmt, ignore_stores);
     }
+  /* We only need to handle internal calls in IPA mode.  */
+  gcc_checking_assert (!cur_summary_lto);
 
   struct cgraph_node *callee_node = cgraph_node::get_create (callee);
 
@@ -796,7 +908,7 @@  analyze_call (modref_summary *cur_summary,
     {
       if (dump_file)
 	fprintf (dump_file, " - Function availability <= AVAIL_INTERPOSABLE.\n");
-      return process_fnspec (cur_summary, stmt, ignore_stores);
+      return process_fnspec (cur_summary, cur_summary_lto, stmt, ignore_stores);
     }
 
   /* Get callee's modref summary.  As above, if there's no summary, we either
@@ -806,7 +918,7 @@  analyze_call (modref_summary *cur_summary,
     {
       if (dump_file)
 	fprintf (dump_file, " - No modref summary available for callee.\n");
-      return process_fnspec (cur_summary, stmt, ignore_stores);
+      return process_fnspec (cur_summary, cur_summary_lto, stmt, ignore_stores);
     }
 
   merge_call_side_effects (cur_summary, stmt, callee_summary, ignore_stores,
@@ -911,8 +1023,24 @@  analyze_stmt (modref_summary *summary, modref_summary_lto *summary_lto,
 	       "which clobbers memory.\n");
      return false;
    case GIMPLE_CALL:
-     if (!ipa)
-       return analyze_call (summary, as_a <gcall *> (stmt), recursive_calls);
+     if (!ipa || gimple_call_internal_p (stmt))
+       return analyze_call (summary, summary_lto,
+			    as_a <gcall *> (stmt), recursive_calls);
+     else
+      {
+	attr_fnspec fnspec = gimple_call_fnspec (as_a <gcall *>(stmt));
+
+	if (fnspec.known_p ()
+	    && (!fnspec.global_memory_read_p ()
+		|| !fnspec.global_memory_written_p ()))
+	  {
+	    fnspec_summaries->get_create
+		 (cgraph_node::get (current_function_decl)->get_edge (stmt))
+			->fnspec = xstrdup (fnspec.get_str ());
+	    if (dump_file)
+	      fprintf (dump_file, "  Recorded fnspec %s\n", fnspec.get_str ());
+	  }
+      }
      return true;
    default:
      /* Nothing to do for other types of statements.  */
@@ -1015,6 +1143,8 @@  analyze_function (function *f, bool ipa)
 	    summaries_lto->remove (cgraph_node::get (f->decl));
 	  summary_lto = summaries_lto->get_create (cgraph_node::get (f->decl));
 	}
+      if (!fnspec_summaries)
+	fnspec_summaries = new fnspec_summaries_t (symtab);
      }
 
 
@@ -1045,6 +1175,7 @@  analyze_function (function *f, bool ipa)
 				 (param_modref_max_bases,
 				  param_modref_max_refs,
 				  param_modref_max_accesses);
+      summary_lto->writes_errno = false;
     }
   int ecf_flags = flags_from_decl_or_type (current_function_decl);
   auto_vec <gimple *, 32> recursive_calls;
@@ -1221,6 +1352,7 @@  modref_summaries_lto::duplicate (cgraph_node *, cgraph_node *,
 			 src_data->loads->max_refs,
 			 src_data->loads->max_accesses);
   dst_data->loads->copy_from (src_data->loads);
+  dst_data->writes_errno = src_data->writes_errno;
 }
 
 namespace
@@ -1484,7 +1616,6 @@  modref_write ()
 
       if (cnode && cnode->definition && !cnode->alias)
 	{
-
 	  modref_summary_lto *r = summaries_lto->get (cnode);
 
 	  if (!r || !r->useful_p (flags_from_decl_or_type (cnode->decl)))
@@ -1494,6 +1625,28 @@  modref_write ()
 
 	  write_modref_records (r->loads, ob);
 	  write_modref_records (r->stores, ob);
+
+	  struct bitpack_d bp = bitpack_create (ob->main_stream);
+	  bp_pack_value (&bp, r->writes_errno, 1);
+	  if (!flag_wpa)
+	    {
+	      for (cgraph_edge *e = cnode->indirect_calls;
+		   e; e = e->next_callee)
+		{
+		  class fnspec_summary *sum = fnspec_summaries->get (e);
+		  bp_pack_value (&bp, sum != NULL, 1);
+		  if (sum)
+		    bp_pack_string (ob, &bp, sum->fnspec, true);
+		}
+	      for (cgraph_edge *e = cnode->callees; e; e = e->next_callee)
+		{
+		  class fnspec_summary *sum = fnspec_summaries->get (e);
+		  bp_pack_value (&bp, sum != NULL, 1);
+		  if (sum)
+		    bp_pack_string (ob, &bp, sum->fnspec, true);
+		}
+	    }
+	  streamer_write_bitpack (&bp);
 	}
     }
   streamer_write_char_stream (ob->main_stream, 0);
@@ -1541,6 +1694,8 @@  read_section (struct lto_file_decl_data *file_data, const char *data,
 
       if (modref_sum)
 	modref_sum->writes_errno = false;
+      if (modref_sum_lto)
+	modref_sum_lto->writes_errno = false;
 
       gcc_assert (!modref_sum || (!modref_sum->loads
 				  && !modref_sum->stores));
@@ -1552,6 +1707,33 @@  read_section (struct lto_file_decl_data *file_data, const char *data,
       read_modref_records (&ib, data_in,
 			   modref_sum ? &modref_sum->stores : NULL,
 			   modref_sum_lto ? &modref_sum_lto->stores : NULL);
+      struct bitpack_d bp = streamer_read_bitpack (&ib);
+      if (bp_unpack_value (&bp, 1))
+	{
+	  if (modref_sum)
+	    modref_sum->writes_errno = true;
+	  if (modref_sum_lto)
+	    modref_sum_lto->writes_errno = true;
+	}
+      if (!flag_ltrans)
+	{
+	  for (cgraph_edge *e = node->indirect_calls; e; e = e->next_callee)
+	    {
+	      if (bp_unpack_value (&bp, 1))
+		{
+		  class fnspec_summary *sum = fnspec_summaries->get_create (e);
+		  sum->fnspec = xstrdup (bp_unpack_string (data_in, &bp));
+		}
+	    }
+	  for (cgraph_edge *e = node->callees; e; e = e->next_callee)
+	    {
+	      if (bp_unpack_value (&bp, 1))
+		{
+		  class fnspec_summary *sum = fnspec_summaries->get_create (e);
+		  sum->fnspec = xstrdup (bp_unpack_string (data_in, &bp));
+		}
+	    }
+	}
       if (dump_file)
 	{
 	  fprintf (dump_file, "Read modref for %s\n",
@@ -1588,6 +1770,8 @@  modref_read (void)
 	  || (flag_incremental_link == INCREMENTAL_LINK_LTO
 	      && flag_fat_lto_objects))
 	summaries = modref_summaries::create_ggc (symtab);
+      if (!fnspec_summaries)
+	fnspec_summaries = new fnspec_summaries_t (symtab);
     }
 
   while ((file_data = file_data_vec[j++]))
@@ -1664,9 +1848,9 @@  update_signature (struct cgraph_node *node)
     {
       fprintf (dump_file, "to:\n");
       if (r)
-        r->dump (dump_file);
+	r->dump (dump_file);
       if (r_lto)
-        r_lto->dump (dump_file);
+	r_lto->dump (dump_file);
     }
   return;
 }
@@ -1755,7 +1939,7 @@  ignore_edge (struct cgraph_edge *e)
 
 /* Compute parm_map for CALLE_EDGE.  */
 
-static void
+static bool
 compute_parm_map (cgraph_edge *callee_edge, vec<modref_parm_map> *parm_map)
 {
   class ipa_edge_args *args;
@@ -1837,7 +2021,9 @@  compute_parm_map (cgraph_edge *callee_edge, vec<modref_parm_map> *parm_map)
 	    fprintf (dump_file, " %i", (*parm_map)[i].parm_index);
 	  fprintf (dump_file, "\n");
 	}
+      return true;
     }
+  return false;
 }
 
 /* Call EDGE was inlined; merge summary from callee to the caller.  */
@@ -1948,26 +2134,171 @@  ipa_merge_modref_summary_after_inlining (cgraph_edge *edge)
   return;
 }
 
-/* Collapse loads and return true if something changed.  */
+/* Get parameter type from DECL.  This is only safe for special cases
+   like builtins we create fnspec for because the type match is checked
+   at fnspec creation time.  */
 
-bool
-collapse_loads (modref_summary *cur_summary,
-		modref_summary_lto *cur_summary_lto)
+static tree
+get_parm_type (tree decl, unsigned int i)
 {
-  bool changed = false;
+  tree t = TYPE_ARG_TYPES (TREE_TYPE (decl));
 
-  if (cur_summary && !cur_summary->loads->every_base)
+  for (unsigned int p = 0; p < i; p++)
+    t = TREE_CHAIN (t);
+  return TREE_VALUE (t);
+}
+
+/* Return access mode for argument I of call E with FNSPEC.  */
+
+static modref_access_node
+get_access_for_fnspec (cgraph_edge *e, attr_fnspec &fnspec,
+		       unsigned int i, modref_parm_map &map)
+{
+  tree size = NULL_TREE;
+  unsigned int size_arg;
+
+  if (!fnspec.arg_specified_p (i))
+    ;
+  else if (fnspec.arg_max_access_size_given_by_arg_p (i, &size_arg))
     {
-      cur_summary->loads->collapse ();
-      changed = true;
+      cgraph_node *node = e->caller->inlined_to
+			  ? e->caller->inlined_to : e->caller;
+      class ipa_node_params *caller_parms_info = IPA_NODE_REF (node);
+      class ipa_edge_args *args = IPA_EDGE_REF (e);
+      struct ipa_jump_func *jf = ipa_get_ith_jump_func (args, size_arg);
+
+      if (jf)
+	size = ipa_value_from_jfunc (caller_parms_info, jf,
+				     get_parm_type (e->callee->decl, size_arg));
     }
-  if (cur_summary_lto
-      && !cur_summary_lto->loads->every_base)
+  else if (fnspec.arg_access_size_given_by_type_p (i))
+    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};
+  poly_int64 size_hwi;
+  if (size
+      && poly_int_tree_p (size, &size_hwi)
+      && coeffs_in_range_p (size_hwi, 0,
+			    HOST_WIDE_INT_MAX / BITS_PER_UNIT))
     {
-      cur_summary_lto->loads->collapse ();
-      changed = true;
+      a.size = -1;
+      a.max_size = size_hwi << LOG2_BITS_PER_UNIT;
     }
-  return changed;
+  return a;
+}
+
+/* Call E in NODE with ECF_FLAGS has no summary; update MODREF_SUMMARY and
+   CUR_SUMMARY_LTO accordingly.  Return true if something changed.  */
+
+static bool
+propagate_unknown_call (cgraph_node *node,
+			cgraph_edge *e, int ecf_flags,
+			modref_summary **cur_summary_ptr,
+			modref_summary_lto **cur_summary_lto_ptr)
+{
+  bool changed = false;
+  modref_summary *cur_summary = cur_summary_ptr ? *cur_summary_ptr : NULL;
+  modref_summary_lto *cur_summary_lto = cur_summary_lto_ptr
+					? *cur_summary_lto_ptr : NULL;
+  class fnspec_summary *fnspec_sum = fnspec_summaries->get (e);
+  auto_vec <modref_parm_map, 32> parm_map;
+  if (fnspec_sum
+      && compute_parm_map (e, &parm_map))
+    {
+      attr_fnspec fnspec (fnspec_sum->fnspec);
+
+      gcc_checking_assert (fnspec.known_p ());
+      if (fnspec.global_memory_read_p ())
+	collapse_loads (cur_summary, cur_summary_lto);
+      else
+	{
+	  tree t = TYPE_ARG_TYPES (TREE_TYPE (e->callee->decl));
+	  for (unsigned i = 0; i < parm_map.length () && t;
+	       i++, t = TREE_CHAIN (t))
+	    if (!POINTER_TYPE_P (TREE_VALUE (t)))
+	      ;
+	  else if (!fnspec.arg_specified_p (i)
+		   || fnspec.arg_maybe_read_p (i))
+	    {
+	      modref_parm_map map = parm_map[i];
+	      if (map.parm_index == -2)
+		continue;
+	      if (map.parm_index == -1)
+		{
+		  collapse_loads (cur_summary, cur_summary_lto);
+		  break;
+		}
+	      if (cur_summary)
+		changed |= cur_summary->loads->insert
+		  (0, 0, get_access_for_fnspec (e, fnspec, i, map));
+	      if (cur_summary_lto)
+		changed |= cur_summary_lto->loads->insert
+		  (0, 0, get_access_for_fnspec (e, fnspec, i, map));
+	    }
+	}
+      if (ignore_stores_p (node->decl, ecf_flags))
+	;
+      else if (fnspec.global_memory_written_p ())
+	collapse_stores (cur_summary, cur_summary_lto);
+      else
+	{
+	  tree t = TYPE_ARG_TYPES (TREE_TYPE (e->callee->decl));
+	  for (unsigned i = 0; i < parm_map.length () && t;
+	       i++, t = TREE_CHAIN (t))
+	    if (!POINTER_TYPE_P (TREE_VALUE (t)))
+	      ;
+	  else if (!fnspec.arg_specified_p (i)
+		   || fnspec.arg_maybe_written_p (i))
+	    {
+	      modref_parm_map map = parm_map[i];
+	      if (map.parm_index == -2)
+		continue;
+	      if (map.parm_index == -1)
+		{
+		  collapse_stores (cur_summary, cur_summary_lto);
+		  break;
+		}
+	      if (cur_summary)
+		changed |= cur_summary->stores->insert
+		  (0, 0, get_access_for_fnspec (e, fnspec, i, map));
+	      if (cur_summary_lto)
+		changed |= cur_summary_lto->stores->insert
+		  (0, 0, get_access_for_fnspec (e, fnspec, i, map));
+	    }
+	}
+      if (fnspec.errno_maybe_written_p () && flag_errno_math)
+	{
+	  if (cur_summary && !cur_summary->writes_errno)
+	    {
+	      cur_summary->writes_errno = true;
+	      changed = true;
+	    }
+	  if (cur_summary_lto && !cur_summary_lto->writes_errno)
+	    {
+	      cur_summary_lto->writes_errno = true;
+	      changed = true;
+	    }
+	}
+      return changed;
+    }
+  if (ignore_stores_p (node->decl, ecf_flags))
+    {
+      if (dump_file)
+	fprintf (dump_file, "      collapsing loads\n");
+      return collapse_loads (cur_summary, cur_summary_lto);
+    }
+  if (optimization_summaries)
+    optimization_summaries->remove (node);
+  if (summaries_lto)
+    summaries_lto->remove (node);
+  if (cur_summary_ptr)
+    *cur_summary_ptr = NULL;
+  if (cur_summary_lto_ptr)
+    *cur_summary_lto_ptr = NULL;
+  if (dump_file)
+    fprintf (dump_file, "    Giving up\n");
+  return true;
 }
 
 /* Perform iterative dataflow on SCC component starting in COMPONENT_NODE.  */
@@ -2005,26 +2336,14 @@  modref_propagate_in_scc (cgraph_node *component_node)
 	    {
 	      if (e->indirect_info->ecf_flags & (ECF_CONST | ECF_NOVOPS))
 		continue;
-	      if (ignore_stores_p (cur->decl, e->indirect_info->ecf_flags))
-		{
-		  if (dump_file)
-		    fprintf (dump_file, "    Indirect call: "
-			     "collapsing loads\n");
-		  changed |= collapse_loads (cur_summary, cur_summary_lto);
-		}
-	      else
-		{
-		  if (dump_file)
-		    fprintf (dump_file, "    Indirect call: giving up\n");
-		  if (optimization_summaries)
-		    optimization_summaries->remove (node);
-		  if (summaries_lto)
-		    summaries_lto->remove (node);
-		  changed = true;
-		  cur_summary = NULL;
-		  cur_summary_lto = NULL;
-		  break;
-		}
+	      if (dump_file)
+		fprintf (dump_file, "    Indirect call"
+			 "collapsing loads\n");
+	      changed |= propagate_unknown_call
+			   (node, e, e->indirect_info->ecf_flags,
+			    &cur_summary, &cur_summary_lto);
+	      if (!cur_summary && !cur_summary_lto)
+		break;
 	    }
 
 	  if (!cur_summary && !cur_summary_lto)
@@ -2063,30 +2382,15 @@  modref_propagate_in_scc (cgraph_node *component_node)
 
 	      if (avail <= AVAIL_INTERPOSABLE)
 		{
-		  if (!ignore_stores)
-		    {
-		      if (dump_file)
-			fprintf (dump_file, "      Call target interposable"
-				 " or not available\n");
-
-		      if (optimization_summaries)
-			optimization_summaries->remove (node);
-		      if (summaries_lto)
-			summaries_lto->remove (node);
-		      cur_summary = NULL;
-		      cur_summary_lto = NULL;
-		      changed = true;
-		      break;
-		    }
-		  else
-		    {
-		      if (dump_file)
-			fprintf (dump_file, "      Call target interposable"
-				 " or not available; collapsing loads\n");
-
-		      changed |= collapse_loads (cur_summary, cur_summary_lto);
-		      continue;
-		    }
+		  if (dump_file)
+		    fprintf (dump_file, "      Call target interposable"
+			     " or not available\n");
+		  changed |= propagate_unknown_call
+			       (node, callee_edge, flags,
+				&cur_summary, &cur_summary_lto);
+		  if (!cur_summary && !cur_summary_lto)
+		    break;
+		  continue;
 		}
 
 	      /* We don't know anything about CALLEE, hence we cannot tell
@@ -2095,52 +2399,24 @@  modref_propagate_in_scc (cgraph_node *component_node)
 	      if (cur_summary
 		  && !(callee_summary = optimization_summaries->get (callee)))
 		{
-		  if (!ignore_stores)
-		    {
-		      if (dump_file)
-			fprintf (dump_file, "      No call target summary\n");
-
-		      optimization_summaries->remove (node);
-		      cur_summary = NULL;
-		      changed = true;
-		    }
-		  else
-		    {
-		      if (dump_file)
-			fprintf (dump_file, "      No call target summary;"
-				 " collapsing loads\n");
-
-		      if (!cur_summary->loads->every_base)
-			{
-			  cur_summary->loads->collapse ();
-			  changed = true;
-			}
-		    }
+		  if (dump_file)
+		    fprintf (dump_file, "      No call target summary\n");
+		  changed |= propagate_unknown_call
+			       (node, callee_edge, flags,
+				&cur_summary, NULL);
+		  if (!cur_summary && !cur_summary_lto)
+		    break;
 		}
 	      if (cur_summary_lto
 		  && !(callee_summary_lto = summaries_lto->get (callee)))
 		{
-		  if (!ignore_stores)
-		    {
-		      if (dump_file)
-			fprintf (dump_file, "      No call target summary\n");
-
-		      summaries_lto->remove (node);
-		      cur_summary_lto = NULL;
-		      changed = true;
-		    }
-		  else
-		    {
-		      if (dump_file)
-			fprintf (dump_file, "      No call target summary;"
-				 " collapsing loads\n");
-
-		      if (!cur_summary_lto->loads->every_base)
-			{
-			  cur_summary_lto->loads->collapse ();
-			  changed = true;
-			}
-		    }
+		  if (dump_file)
+		    fprintf (dump_file, "      No call target summary\n");
+		  changed |= propagate_unknown_call
+			       (node, callee_edge, flags,
+				NULL, &cur_summary_lto);
+		  if (!cur_summary && !cur_summary_lto)
+		    break;
 		}
 
 	      /* We can not safely optimize based on summary of callee if it
@@ -2166,16 +2442,32 @@  modref_propagate_in_scc (cgraph_node *component_node)
 		  changed |= cur_summary->loads->merge
 				  (callee_summary->loads, &parm_map);
 		  if (!ignore_stores)
-		    changed |= cur_summary->stores->merge
-				    (callee_summary->stores, &parm_map);
+		    {
+		      changed |= cur_summary->stores->merge
+				      (callee_summary->stores, &parm_map);
+		      if (!cur_summary->writes_errno
+			  && callee_summary->writes_errno)
+			{
+			  cur_summary->writes_errno = true;
+			  changed = true;
+			}
+		    }
 		}
 	      if (callee_summary_lto)
 		{
 		  changed |= cur_summary_lto->loads->merge
 				  (callee_summary_lto->loads, &parm_map);
 		  if (!ignore_stores)
-		    changed |= cur_summary_lto->stores->merge
-				    (callee_summary_lto->stores, &parm_map);
+		    {
+		      changed |= cur_summary_lto->stores->merge
+				      (callee_summary_lto->stores, &parm_map);
+		      if (!cur_summary_lto->writes_errno
+			  && callee_summary_lto->writes_errno)
+			{
+			  cur_summary_lto->writes_errno = true;
+			  changed = true;
+			}
+		    }
 		}
 	      if (dump_file && changed)
 		{
@@ -2266,6 +2558,8 @@  pass_ipa_modref::execute (function *)
     ((modref_summaries_lto *)summaries_lto)->propagated = true;
   ipa_free_postorder_info ();
   free (order);
+  delete fnspec_summaries;
+  fnspec_summaries = NULL;
   return 0;
 }
 
@@ -2283,6 +2577,9 @@  ipa_modref_c_finalize ()
       ggc_delete (summaries_lto);
       summaries_lto = NULL;
     }
+  if (fnspec_summaries)
+    delete fnspec_summaries;
+  fnspec_summaries = NULL;
 }
 
 #include "gt-ipa-modref.h"
diff --git a/gcc/ipa-prop.c b/gcc/ipa-prop.c
index d4ef6ff8a8e..7a5fa59ecec 100644
--- a/gcc/ipa-prop.c
+++ b/gcc/ipa-prop.c
@@ -54,6 +54,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "tree-cfgcleanup.h"
 #include "options.h"
 #include "symtab-clones.h"
+#include "attr-fnspec.h"
 
 /* Function summary where the parameter infos are actually stored. */
 ipa_node_params_t *ipa_node_params_sum = NULL;
@@ -2364,7 +2365,8 @@  ipa_compute_jump_functions_for_bb (struct ipa_func_body_info *fbi, basic_block b
 	  callee = callee->ultimate_alias_target ();
 	  /* We do not need to bother analyzing calls to unknown functions
 	     unless they may become known during lto/whopr.  */
-	  if (!callee->definition && !flag_lto)
+	  if (!callee->definition && !flag_lto
+	      && !gimple_call_fnspec (cs->call_stmt).known_p ())
 	    continue;
 	}
       ipa_compute_jump_functions_for_edge (fbi, cs);
@@ -4974,7 +4976,11 @@  ipa_read_edge_info (class lto_input_block *ib,
   count /= 2;
   if (!count)
     return;
-  if (prevails && e->possibly_call_in_translation_unit_p ())
+  if (prevails
+      && (e->possibly_call_in_translation_unit_p ()
+	  /* Also stream in jump functions to builtins in hope that they
+	     will get fnspecs.  */
+	  || fndecl_built_in_p (e->callee->decl, BUILT_IN_NORMAL)))
     {
       class ipa_edge_args *args = IPA_EDGE_REF_GET_CREATE (e);
       vec_safe_grow_cleared (args->jump_functions, count, true);