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  <hubi...@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 --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);

Reply via email to