aboutsummaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
authorJan Hubicka <jh@suse.cz>2020-11-06 10:23:58 +0100
committerJan Hubicka <jh@suse.cz>2020-11-06 10:23:58 +0100
commit6cef01c32817b3d08af2cadcdb0e23c72ceed426 (patch)
tree8d0c62cd340e8857600008968c18df33092174e0 /gcc
parent366099ff0823c3c9929dd7fb911f4af04e63baef (diff)
downloadgcc-6cef01c32817b3d08af2cadcdb0e23c72ceed426.zip
gcc-6cef01c32817b3d08af2cadcdb0e23c72ceed426.tar.gz
gcc-6cef01c32817b3d08af2cadcdb0e23c72ceed426.tar.bz2
Add fnspec handling to ipa mode of ipa-modef.
gcc/: * 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. gcc/testsuite/ChangeLog: * gcc.dg/ipa/modref-2.c: New test. * gcc.dg/lto/modref-2_0.c: New test.
Diffstat (limited to 'gcc')
-rw-r--r--gcc/attr-fnspec.h7
-rw-r--r--gcc/ipa-fnsummary.c6
-rw-r--r--gcc/ipa-modref.c584
-rw-r--r--gcc/ipa-prop.c10
-rw-r--r--gcc/testsuite/gcc.dg/ipa/modref-2.c15
-rw-r--r--gcc/testsuite/gcc.dg/lto/modref-2_0.c27
6 files changed, 501 insertions, 148 deletions
diff --git a/gcc/attr-fnspec.h b/gcc/attr-fnspec.h
index 78b1a5a..28135328 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 f2781d0..b8f4a0a9 100644
--- a/gcc/ipa-fnsummary.c
+++ b/gcc/ipa-fnsummary.c
@@ -4301,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 9df3d2b..9510982 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 +705,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 +767,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 +788,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 +821,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 +850,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 +876,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 +905,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 +915,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 +1020,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 +1140,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 +1172,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 +1349,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 +1613,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 +1622,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 +1691,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 +1704,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 +1767,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 +1845,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 +1936,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 +2018,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 +2131,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 +2333,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 +2379,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 +2396,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 +2439,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 +2555,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 +2574,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 d4ef6ff..7a5fa59 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);
diff --git a/gcc/testsuite/gcc.dg/ipa/modref-2.c b/gcc/testsuite/gcc.dg/ipa/modref-2.c
new file mode 100644
index 0000000..5ac2c65
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ipa/modref-2.c
@@ -0,0 +1,15 @@
+/* { dg-options "-O2 -fdump-ipa-modref" } */
+/* { dg-do compile } */
+void
+test (int *a, int size)
+{
+ __builtin_memset (a, 0, 321);
+}
+void
+test2 (double x, double *y)
+{
+ __builtin_modf (x,y);
+}
+/* 321*8 */
+/* { dg-final { scan-ipa-dump "Parm 0 param offset:0 offset:0 size:-1 max_size:2568" "modref" } } */
+/* { dg-final { scan-ipa-dump "Parm 1 param offset:0 offset:0 size:-1 max_size:64" "modref" } } */
diff --git a/gcc/testsuite/gcc.dg/lto/modref-2_0.c b/gcc/testsuite/gcc.dg/lto/modref-2_0.c
new file mode 100644
index 0000000..cf84ed9
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/lto/modref-2_0.c
@@ -0,0 +1,27 @@
+/* { dg-lto-do run } */
+/* { dg-lto-options {"-O2 -flto-partition=max -flto -fno-ipa-sra"} } */
+__attribute__ ((noinline))
+void
+test (char *a)
+{
+ __builtin_memset (a,0,321);
+}
+__attribute__ ((noinline))
+void
+test2 (double *x, double *y)
+{
+ __builtin_modf (*x,y);
+}
+int
+main (void)
+{
+ char array[321];
+ double x=1, y=2;
+ char arrayz[321];
+ arrayz[0]=1;
+ test (array);
+ test2 (&x,&y);
+ if (!__builtin_constant_p (x==2) || !__builtin_constant_p (arrayz[0]==1))
+ __builtin_abort ();
+ return 0;
+}