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.
This commit is contained in:
parent
366099ff08
commit
6cef01c328
@ -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);
|
||||
|
||||
@ -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++)
|
||||
|
||||
584
gcc/ipa-modref.c
584
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)
|
||||
{
|
||||
tree t = TYPE_ARG_TYPES (TREE_TYPE (decl));
|
||||
|
||||
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))
|
||||
{
|
||||
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));
|
||||
}
|
||||
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))
|
||||
{
|
||||
a.size = -1;
|
||||
a.max_size = size_hwi << LOG2_BITS_PER_UNIT;
|
||||
}
|
||||
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);
|
||||
|
||||
if (cur_summary && !cur_summary->loads->every_base)
|
||||
{
|
||||
cur_summary->loads->collapse ();
|
||||
changed = true;
|
||||
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 (cur_summary_lto
|
||||
&& !cur_summary_lto->loads->every_base)
|
||||
if (ignore_stores_p (node->decl, ecf_flags))
|
||||
{
|
||||
cur_summary_lto->loads->collapse ();
|
||||
changed = true;
|
||||
if (dump_file)
|
||||
fprintf (dump_file, " collapsing loads\n");
|
||||
return collapse_loads (cur_summary, cur_summary_lto);
|
||||
}
|
||||
return changed;
|
||||
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"
|
||||
|
||||
@ -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);
|
||||
|
||||
15
gcc/testsuite/gcc.dg/ipa/modref-2.c
Normal file
15
gcc/testsuite/gcc.dg/ipa/modref-2.c
Normal file
@ -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" } } */
|
||||
27
gcc/testsuite/gcc.dg/lto/modref-2_0.c
Normal file
27
gcc/testsuite/gcc.dg/lto/modref-2_0.c
Normal file
@ -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;
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user