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:
Jan Hubicka 2020-11-06 10:23:58 +01:00
parent 366099ff08
commit 6cef01c328
6 changed files with 501 additions and 148 deletions

View File

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

View File

@ -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++)

View File

@ -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"

View File

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

View 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" } } */

View 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;
}