Implement iterative dataflow in mod-ref
cc1plus stats are now: Alias oracle query stats: refs_may_alias_p: 62971744 disambiguations, 73160711 queries ref_maybe_used_by_call_p: 141176 disambiguations, 63867883 queries call_may_clobber_ref_p: 23573 disambiguations, 29322 queries nonoverlapping_component_refs_p: 0 disambiguations, 37720 queries nonoverlapping_refs_since_match_p: 19432 disambiguations, 55659 must overlaps, 75860 queries aliasing_component_refs_p: 54724 disambiguations, 753570 queries TBAA oracle: 24124230 disambiguations 56228428 queries 16058141 are in alias set 0 10338303 queries asked about the same object 125 queries asked about the same alias set 0 access volatile 3919230 are dependent in the DAG 1788399 are aritificially in conflict with void * Modref stats: modref use: 10408 disambiguations, 46993 queries modref clobber: 1418549 disambiguations, 1951251 queries 4898707 tbaa queries (2.510547 per modref query) 396878 base compares (0.203397 per modref query) PTA query stats: pt_solution_includes: 975364 disambiguations, 13604284 queries pt_solutions_intersect: 1026606 disambiguations, 13181198 queries So compared to https://gcc.gnu.org/pipermail/gcc-patches/2020-September/554692.html we get 25% use disambiguations and 91% more clobber disambiguations. Tramp3d is Alias oracle query stats: refs_may_alias_p: 2056905 disambiguations, 2317461 queries ref_maybe_used_by_call_p: 7137 disambiguations, 2093762 queries call_may_clobber_ref_p: 234 disambiguations, 234 queries nonoverlapping_component_refs_p: 0 disambiguations, 4313 queries nonoverlapping_refs_since_match_p: 329 disambiguations, 10200 must overlaps, 10616 queries aliasing_component_refs_p: 858 disambiguations, 34600 queries TBAA oracle: 894996 disambiguations 1695991 queries 138346 are in alias set 0 470668 queries asked about the same object 0 queries asked about the same alias set 0 access volatile 191666 are dependent in the DAG 315 are aritificially in conflict with void * Modref stats: modref use: 842 disambiguations, 2265 queries modref clobber: 14833 disambiguations, 28900 queries 34884 tbaa queries (1.207059 per modref query) 5041 base compares (0.174429 per modref query) PTA query stats: pt_solution_includes: 313372 disambiguations, 525724 queries pt_solutions_intersect: 130374 disambiguations, 415138 queries So about twice many use and 40% clobber disambiguations. Bootstrapped/regtested x86_64-linux, I plan to commit it later today after more testing. 2020-09-26 Jan Hubicka <hubicka@ucw.cz> * ipa-inline-transform.c: Include ipa-modref-tree.h and ipa-modref.h. (inline_call): Call ipa_merge_modref_summary_after_inlining. * ipa-inline.c (ipa_inline): Do not free summaries. * ipa-modref.c (dump_records): Fix formating. (merge_call_side_effects): Break out from ... (analyze_call): ... here; record recursive calls. (analyze_stmt): Add new parameter RECURSIVE_CALLS. (analyze_function): Do iterative dataflow on recursive calls. (compute_parm_map): New function. (ipa_merge_modref_summary_after_inlining): New function. (collapse_loads): New function. (modref_propagate_in_scc): Break out from ... (pass_ipa_modref::execute): ... here; Do iterative dataflow. * ipa-modref.h (ipa_merge_modref_summary_after_inlining): Declare.
This commit is contained in:
parent
a29bd4f59e
commit
ada353b879
@ -48,6 +48,8 @@ along with GCC; see the file COPYING3. If not see
|
||||
#include "cfg.h"
|
||||
#include "basic-block.h"
|
||||
#include "ipa-utils.h"
|
||||
#include "ipa-modref-tree.h"
|
||||
#include "ipa-modref.h"
|
||||
|
||||
int ncalls_inlined;
|
||||
int nfunctions_inlined;
|
||||
@ -487,6 +489,7 @@ inline_call (struct cgraph_edge *e, bool update_original,
|
||||
gcc_assert (curr->callee->inlined_to == to);
|
||||
|
||||
old_size = ipa_size_summaries->get (to)->size;
|
||||
ipa_merge_modref_summary_after_inlining (e);
|
||||
ipa_merge_fn_summary_after_inlining (e);
|
||||
if (e->in_polymorphic_cdtor)
|
||||
mark_all_inlined_calls_cdtor (e->callee);
|
||||
|
@ -2770,9 +2770,6 @@ ipa_inline (void)
|
||||
}
|
||||
}
|
||||
|
||||
/* Free ipa-prop structures if they are no longer needed. */
|
||||
ipa_free_all_structures_after_iinln ();
|
||||
|
||||
if (dump_enabled_p ())
|
||||
dump_printf (MSG_NOTE,
|
||||
"\nInlined %i calls, eliminated %i functions\n\n",
|
||||
|
683
gcc/ipa-modref.c
683
gcc/ipa-modref.c
@ -175,7 +175,7 @@ dump_records (modref_records *tt, FILE *out)
|
||||
fprintf (out, " Ref %i: alias set %i\n", (int)j, r->ref);
|
||||
if (r->every_access)
|
||||
{
|
||||
fprintf (out, " Every access\n");
|
||||
fprintf (out, " Every access\n");
|
||||
continue;
|
||||
}
|
||||
size_t k;
|
||||
@ -437,11 +437,70 @@ ignore_stores_p (tree caller, int flags)
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Analyze function call STMT in function F. */
|
||||
/* Merge side effects of call STMT to function with CALLEE_SUMMARY
|
||||
int CUR_SUMMARY. Return true if something changed.
|
||||
If IGNORE_STORES is true, do not merge stores. */
|
||||
|
||||
bool
|
||||
merge_call_side_effects (modref_summary *cur_summary,
|
||||
gimple *stmt, modref_summary *callee_summary,
|
||||
bool ignore_stores)
|
||||
{
|
||||
auto_vec <int, 32> parm_map;
|
||||
bool changed = false;
|
||||
|
||||
parm_map.safe_grow (gimple_call_num_args (stmt));
|
||||
for (unsigned i = 0; i < gimple_call_num_args (stmt); i++)
|
||||
{
|
||||
tree op = gimple_call_arg (stmt, i);
|
||||
STRIP_NOPS (op);
|
||||
if (TREE_CODE (op) == SSA_NAME
|
||||
&& SSA_NAME_IS_DEFAULT_DEF (op)
|
||||
&& TREE_CODE (SSA_NAME_VAR (op)) == PARM_DECL)
|
||||
{
|
||||
int index = 0;
|
||||
for (tree t = DECL_ARGUMENTS (current_function_decl);
|
||||
t != SSA_NAME_VAR (op); t = DECL_CHAIN (t))
|
||||
{
|
||||
if (!t)
|
||||
{
|
||||
index = -1;
|
||||
break;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
parm_map[i] = index;
|
||||
}
|
||||
else if (points_to_local_or_readonly_memory_p (op))
|
||||
parm_map[i] = -2;
|
||||
else
|
||||
parm_map[i] = -1;
|
||||
}
|
||||
|
||||
/* Merge with callee's summary. */
|
||||
if (cur_summary->loads)
|
||||
changed |= cur_summary->loads->merge (callee_summary->loads, &parm_map);
|
||||
if (cur_summary->loads_lto)
|
||||
changed |= cur_summary->loads_lto->merge (callee_summary->loads_lto,
|
||||
&parm_map);
|
||||
if (!ignore_stores)
|
||||
{
|
||||
if (cur_summary->stores)
|
||||
changed |= cur_summary->stores->merge (callee_summary->stores,
|
||||
&parm_map);
|
||||
if (cur_summary->stores_lto)
|
||||
changed |= cur_summary->stores_lto->merge (callee_summary->stores_lto,
|
||||
&parm_map);
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
/* Analyze function call STMT in function F.
|
||||
Remember recursive calls in RECURSIVE_CALLS. */
|
||||
|
||||
static bool
|
||||
analyze_call (modref_summary *cur_summary,
|
||||
gimple *stmt)
|
||||
gimple *stmt, vec <gimple *> *recursive_calls)
|
||||
{
|
||||
/* Check flags on the function call. In certain cases, analysis can be
|
||||
simplified. */
|
||||
@ -505,6 +564,7 @@ analyze_call (modref_summary *cur_summary,
|
||||
there's nothing to do. */
|
||||
if (recursive_call_p (current_function_decl, callee))
|
||||
{
|
||||
recursive_calls->safe_push (stmt);
|
||||
if (dump_file)
|
||||
fprintf (dump_file, " - Skipping recursive call.\n");
|
||||
return true;
|
||||
@ -550,48 +610,7 @@ analyze_call (modref_summary *cur_summary,
|
||||
return false;
|
||||
}
|
||||
|
||||
auto_vec <int, 32> parm_map;
|
||||
|
||||
parm_map.safe_grow (gimple_call_num_args (stmt));
|
||||
for (unsigned i = 0; i < gimple_call_num_args (stmt); i++)
|
||||
{
|
||||
tree op = gimple_call_arg (stmt, i);
|
||||
STRIP_NOPS (op);
|
||||
if (TREE_CODE (op) == SSA_NAME
|
||||
&& SSA_NAME_IS_DEFAULT_DEF (op)
|
||||
&& TREE_CODE (SSA_NAME_VAR (op)) == PARM_DECL)
|
||||
{
|
||||
int index = 0;
|
||||
for (tree t = DECL_ARGUMENTS (current_function_decl);
|
||||
t != SSA_NAME_VAR (op); t = DECL_CHAIN (t))
|
||||
{
|
||||
if (!t)
|
||||
{
|
||||
index = -1;
|
||||
break;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
parm_map[i] = index;
|
||||
}
|
||||
else if (points_to_local_or_readonly_memory_p (op))
|
||||
parm_map[i] = -2;
|
||||
else
|
||||
parm_map[i] = -1;
|
||||
}
|
||||
|
||||
/* Merge with callee's summary. */
|
||||
if (cur_summary->loads)
|
||||
cur_summary->loads->merge (callee_summary->loads, &parm_map);
|
||||
if (cur_summary->loads_lto)
|
||||
cur_summary->loads_lto->merge (callee_summary->loads_lto, &parm_map);
|
||||
if (!ignore_stores)
|
||||
{
|
||||
if (cur_summary->stores)
|
||||
cur_summary->stores->merge (callee_summary->stores, &parm_map);
|
||||
if (cur_summary->stores_lto)
|
||||
cur_summary->stores_lto->merge (callee_summary->stores_lto, &parm_map);
|
||||
}
|
||||
merge_call_side_effects (cur_summary, stmt, callee_summary, ignore_stores);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -654,7 +673,8 @@ analyze_store (gimple *, tree, tree op, void *data)
|
||||
If IPA is true do not merge in side effects of calls. */
|
||||
|
||||
static bool
|
||||
analyze_stmt (modref_summary *summary, gimple *stmt, bool ipa)
|
||||
analyze_stmt (modref_summary *summary, gimple *stmt, bool ipa,
|
||||
vec <gimple *> *recursive_calls)
|
||||
{
|
||||
/* There is no need to record clobbers. */
|
||||
if (gimple_clobber_p (stmt))
|
||||
@ -677,7 +697,7 @@ analyze_stmt (modref_summary *summary, gimple *stmt, bool ipa)
|
||||
return false;
|
||||
case GIMPLE_CALL:
|
||||
if (!ipa)
|
||||
return analyze_call (summary, stmt);
|
||||
return analyze_call (summary, stmt, recursive_calls);
|
||||
return true;
|
||||
default:
|
||||
/* Nothing to do for other types of statements. */
|
||||
@ -750,6 +770,7 @@ analyze_function (function *f, bool ipa)
|
||||
}
|
||||
summary->finished = false;
|
||||
int ecf_flags = flags_from_decl_or_type (current_function_decl);
|
||||
auto_vec <gimple *, 32> recursive_calls;
|
||||
|
||||
/* Analyze each statement in each basic block of the function. If the
|
||||
statement cannot be analyzed (for any reason), the entire function cannot
|
||||
@ -760,7 +781,7 @@ analyze_function (function *f, bool ipa)
|
||||
gimple_stmt_iterator si;
|
||||
for (si = gsi_after_labels (bb); !gsi_end_p (si); gsi_next (&si))
|
||||
{
|
||||
if (!analyze_stmt (summary, gsi_stmt (si), ipa)
|
||||
if (!analyze_stmt (summary, gsi_stmt (si), ipa, &recursive_calls)
|
||||
|| !summary->useful_p (ecf_flags))
|
||||
{
|
||||
cgraph_node *fnode = cgraph_node::get (current_function_decl);
|
||||
@ -773,6 +794,34 @@ analyze_function (function *f, bool ipa)
|
||||
}
|
||||
}
|
||||
|
||||
/* In non-IPA mode we need to perform iterative datafow on recursive calls.
|
||||
This needs to be done after all other side effects are computed. */
|
||||
if (!ipa)
|
||||
{
|
||||
bool changed = true;
|
||||
while (changed)
|
||||
{
|
||||
changed = false;
|
||||
for (unsigned i = 0; i < recursive_calls.length (); i++)
|
||||
{
|
||||
changed |= merge_call_side_effects
|
||||
(summary, recursive_calls[i], summary,
|
||||
ignore_stores_p (current_function_decl,
|
||||
gimple_call_flags
|
||||
(recursive_calls[i])));
|
||||
if (!summary->useful_p (ecf_flags))
|
||||
{
|
||||
cgraph_node *fnode = cgraph_node::get (current_function_decl);
|
||||
summaries->remove (fnode);
|
||||
if (dump_file)
|
||||
fprintf (dump_file,
|
||||
" - modref done with result: not tracked.\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!ipa)
|
||||
summary->finished = true;
|
||||
|
||||
@ -1276,12 +1325,335 @@ ignore_edge (struct cgraph_edge *e)
|
||||
& (ECF_CONST | ECF_NOVOPS));
|
||||
}
|
||||
|
||||
/* Compute parm_map for CALLE_EDGE. */
|
||||
|
||||
static void
|
||||
compute_parm_map (cgraph_edge *callee_edge, vec<int> *parm_map)
|
||||
{
|
||||
class ipa_edge_args *args;
|
||||
if (ipa_node_params_sum
|
||||
&& !callee_edge->call_stmt_cannot_inline_p
|
||||
&& (args = IPA_EDGE_REF (callee_edge)) != NULL)
|
||||
{
|
||||
int i, count = ipa_get_cs_argument_count (args);
|
||||
class ipa_node_params *caller_parms_info, *callee_pi;
|
||||
class ipa_call_summary *es
|
||||
= ipa_call_summaries->get (callee_edge);
|
||||
cgraph_node *callee
|
||||
= callee_edge->callee->function_or_virtual_thunk_symbol
|
||||
(NULL, callee_edge->caller);
|
||||
|
||||
caller_parms_info = IPA_NODE_REF (callee_edge->caller->inlined_to
|
||||
? callee_edge->caller->inlined_to
|
||||
: callee_edge->caller);
|
||||
callee_pi = IPA_NODE_REF (callee);
|
||||
|
||||
(*parm_map).safe_grow (count);
|
||||
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
if (es && es->param[i].points_to_local_or_readonly_memory)
|
||||
{
|
||||
(*parm_map)[i] = -2;
|
||||
continue;
|
||||
}
|
||||
|
||||
struct ipa_jump_func *jf
|
||||
= ipa_get_ith_jump_func (args, i);
|
||||
if (jf)
|
||||
{
|
||||
tree cst = ipa_value_from_jfunc (caller_parms_info,
|
||||
jf,
|
||||
ipa_get_type
|
||||
(callee_pi, i));
|
||||
if (cst && points_to_local_or_readonly_memory_p (cst))
|
||||
{
|
||||
(*parm_map)[i] = -2;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (jf && jf->type == IPA_JF_PASS_THROUGH)
|
||||
{
|
||||
(*parm_map)[i]
|
||||
= ipa_get_jf_pass_through_formal_id (jf);
|
||||
continue;
|
||||
}
|
||||
if (jf && jf->type == IPA_JF_ANCESTOR)
|
||||
(*parm_map)[i] = ipa_get_jf_ancestor_formal_id (jf);
|
||||
else
|
||||
(*parm_map)[i] = -1;
|
||||
}
|
||||
if (dump_file)
|
||||
{
|
||||
fprintf (dump_file, " Parm map: ");
|
||||
for (i = 0; i < count; i++)
|
||||
fprintf (dump_file, " %i", (*parm_map)[i]);
|
||||
fprintf (dump_file, "\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Call EDGE was inlined; merge summary from callee to the caller. */
|
||||
|
||||
void
|
||||
ipa_merge_modref_summary_after_inlining (cgraph_edge *edge)
|
||||
{
|
||||
if (!summaries)
|
||||
return;
|
||||
|
||||
struct cgraph_node *to = (edge->caller->inlined_to
|
||||
? edge->caller->inlined_to : edge->caller);
|
||||
class modref_summary *to_info = summaries->get (to);
|
||||
|
||||
if (!to_info)
|
||||
return;
|
||||
|
||||
class modref_summary *callee_info = summaries->get (edge->callee);
|
||||
int flags = flags_from_decl_or_type (edge->callee->decl);
|
||||
|
||||
if (!callee_info)
|
||||
{
|
||||
if (ignore_stores_p (edge->callee->decl, flags))
|
||||
{
|
||||
if (to_info->loads)
|
||||
to_info->loads->collapse ();
|
||||
if (to_info->loads_lto)
|
||||
to_info->loads_lto->collapse ();
|
||||
}
|
||||
else
|
||||
{
|
||||
summaries->remove (to);
|
||||
summaries->remove (edge->callee);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto_vec <int, 32> parm_map;
|
||||
|
||||
compute_parm_map (edge, &parm_map);
|
||||
|
||||
if (to_info->loads)
|
||||
to_info->loads->merge (callee_info->loads, &parm_map);
|
||||
if (to_info->stores)
|
||||
to_info->stores->merge (callee_info->stores, &parm_map);
|
||||
if (to_info->loads_lto)
|
||||
to_info->loads_lto->merge (callee_info->loads_lto, &parm_map);
|
||||
if (to_info->stores_lto)
|
||||
to_info->stores_lto->merge (callee_info->stores_lto, &parm_map);
|
||||
}
|
||||
if (!to_info->useful_p (flags))
|
||||
summaries->remove (to);
|
||||
summaries->remove (edge->callee);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Collapse loads and return true if something changed. */
|
||||
|
||||
bool
|
||||
collapse_loads (modref_summary *cur_summary)
|
||||
{
|
||||
bool changed = false;
|
||||
|
||||
if (cur_summary->loads && !cur_summary->loads->every_base)
|
||||
{
|
||||
cur_summary->loads->collapse ();
|
||||
changed = true;
|
||||
}
|
||||
if (cur_summary->loads_lto
|
||||
&& !cur_summary->loads_lto->every_base)
|
||||
{
|
||||
cur_summary->loads_lto->collapse ();
|
||||
changed = true;
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
/* Perform iterative dataflow on SCC component starting in COMPONENT_NODE. */
|
||||
|
||||
static void
|
||||
modref_propagate_in_scc (cgraph_node *component_node)
|
||||
{
|
||||
bool changed = true;
|
||||
int iteration = 0;
|
||||
|
||||
while (changed)
|
||||
{
|
||||
changed = false;
|
||||
for (struct cgraph_node *cur = component_node; cur;
|
||||
cur = ((struct ipa_dfs_info *) cur->aux)->next_cycle)
|
||||
{
|
||||
cgraph_node *node = cur->inlined_to ? cur->inlined_to : cur;
|
||||
modref_summary *cur_summary = summaries->get (node);
|
||||
|
||||
if (!cur_summary)
|
||||
continue;
|
||||
|
||||
if (dump_file)
|
||||
fprintf (dump_file, " Processing %s%s%s\n",
|
||||
cur->dump_name (),
|
||||
TREE_READONLY (cur->decl) ? " (const)" : "",
|
||||
DECL_PURE_P (cur->decl) ? " (pure)" : "");
|
||||
|
||||
for (cgraph_edge *e = cur->indirect_calls; e; e = e->next_callee)
|
||||
{
|
||||
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);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (dump_file)
|
||||
fprintf (dump_file, " Indirect call: giving up\n");
|
||||
summaries->remove (node);
|
||||
changed = true;
|
||||
cur_summary = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!cur_summary)
|
||||
continue;
|
||||
|
||||
for (cgraph_edge *callee_edge = cur->callees; callee_edge;
|
||||
callee_edge = callee_edge->next_callee)
|
||||
{
|
||||
int flags = flags_from_decl_or_type (callee_edge->callee->decl);
|
||||
modref_summary *callee_summary;
|
||||
struct cgraph_node *callee;
|
||||
|
||||
if (flags & (ECF_CONST | ECF_NOVOPS)
|
||||
|| !callee_edge->inline_failed)
|
||||
continue;
|
||||
|
||||
/* Get the callee and its summary. */
|
||||
enum availability avail;
|
||||
callee = callee_edge->callee->function_or_virtual_thunk_symbol
|
||||
(&avail, cur);
|
||||
|
||||
/* It is not necessary to re-process calls outside of the
|
||||
SCC component. */
|
||||
if (iteration > 0
|
||||
&& (!callee->aux
|
||||
|| ((struct ipa_dfs_info *)cur->aux)->scc_no
|
||||
!= ((struct ipa_dfs_info *)callee->aux)->scc_no))
|
||||
continue;
|
||||
|
||||
if (dump_file)
|
||||
fprintf (dump_file, " Call to %s\n",
|
||||
callee_edge->callee->dump_name ());
|
||||
|
||||
bool ignore_stores = ignore_stores_p (cur->decl, flags);
|
||||
|
||||
/* We don't know anything about CALLEE, hence we cannot tell
|
||||
anything about the entire component. */
|
||||
|
||||
if (avail <= AVAIL_INTERPOSABLE
|
||||
|| !(callee_summary = summaries->get (callee)))
|
||||
{
|
||||
if (!ignore_stores)
|
||||
{
|
||||
if (dump_file && avail <= AVAIL_INTERPOSABLE)
|
||||
fprintf (dump_file, " Call target interposable"
|
||||
" or not available\n");
|
||||
else if (dump_file)
|
||||
fprintf (dump_file, " No call target summary\n");
|
||||
|
||||
summaries->remove (node);
|
||||
changed = true;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (dump_file && avail <= AVAIL_INTERPOSABLE)
|
||||
fprintf (dump_file, " Call target interposable"
|
||||
" or not available; collapsing loads\n");
|
||||
else if (dump_file)
|
||||
fprintf (dump_file, " No call target summary;"
|
||||
" collapsing loads\n");
|
||||
|
||||
changed |= collapse_loads (cur_summary);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
/* We can not safely optimize based on summary of callee if it
|
||||
does not always bind to current def: it is possible that
|
||||
memory load was optimized out earlier which may not happen in
|
||||
the interposed variant. */
|
||||
if (!callee_edge->binds_to_current_def_p ())
|
||||
{
|
||||
changed |= collapse_loads (cur_summary);
|
||||
if (dump_file)
|
||||
fprintf (dump_file, " May not bind local;"
|
||||
" collapsing loads\n");
|
||||
}
|
||||
|
||||
|
||||
auto_vec <int, 32> parm_map;
|
||||
|
||||
compute_parm_map (callee_edge, &parm_map);
|
||||
|
||||
/* Merge in callee's information. */
|
||||
if (callee_summary->loads)
|
||||
changed |= cur_summary->loads->merge
|
||||
(callee_summary->loads, &parm_map);
|
||||
if (callee_summary->stores)
|
||||
changed |= cur_summary->stores->merge
|
||||
(callee_summary->stores, &parm_map);
|
||||
if (callee_summary->loads_lto)
|
||||
changed |= cur_summary->loads_lto->merge
|
||||
(callee_summary->loads_lto, &parm_map);
|
||||
if (callee_summary->stores_lto)
|
||||
changed |= cur_summary->stores_lto->merge
|
||||
(callee_summary->stores_lto, &parm_map);
|
||||
if (dump_file && changed)
|
||||
cur_summary->dump (dump_file);
|
||||
}
|
||||
}
|
||||
iteration++;
|
||||
}
|
||||
for (struct cgraph_node *cur = component_node; cur;
|
||||
cur = ((struct ipa_dfs_info *) cur->aux)->next_cycle)
|
||||
{
|
||||
modref_summary *cur_summary = summaries->get (cur);
|
||||
if (cur_summary)
|
||||
cur_summary->finished = true;
|
||||
}
|
||||
if (dump_file)
|
||||
{
|
||||
fprintf (dump_file,
|
||||
"Propagation finished in %i iterations\n", iteration);
|
||||
for (struct cgraph_node *cur = component_node; cur;
|
||||
cur = ((struct ipa_dfs_info *) cur->aux)->next_cycle)
|
||||
if (!cur->inlined_to)
|
||||
{
|
||||
modref_summary *cur_summary = summaries->get (cur);
|
||||
|
||||
fprintf (dump_file, "Propagated modref for %s%s%s\n",
|
||||
cur->dump_name (),
|
||||
TREE_READONLY (cur->decl) ? " (const)" : "",
|
||||
DECL_PURE_P (cur->decl) ? " (pure)" : "");
|
||||
if (cur_summary)
|
||||
cur_summary->dump (dump_file);
|
||||
else
|
||||
fprintf (dump_file, " Not tracked\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Run the IPA pass. This will take a function's summaries and calls and
|
||||
construct new summaries which represent a transitive closure. So that
|
||||
summary of an analyzed function contains information about the loads and
|
||||
stores that the function or any function that it calls does. */
|
||||
|
||||
unsigned int pass_ipa_modref::execute (function *)
|
||||
unsigned int
|
||||
pass_ipa_modref::execute (function *)
|
||||
{
|
||||
if (!summaries)
|
||||
return 0;
|
||||
@ -1295,224 +1667,19 @@ unsigned int pass_ipa_modref::execute (function *)
|
||||
/* Iterate over all strongly connected components in post-order. */
|
||||
for (i = 0; i < order_pos; i++)
|
||||
{
|
||||
bool its_hopeless = false;
|
||||
modref_records *loads = NULL;
|
||||
modref_records *stores = NULL;
|
||||
modref_records_lto *loads_lto = NULL;
|
||||
modref_records_lto *stores_lto = NULL;
|
||||
|
||||
/* Get the component's representative. That's just any node in the
|
||||
component from which we can traverse the entire component. */
|
||||
struct cgraph_node *component_node = order[i];
|
||||
cgraph_node *first = NULL;
|
||||
|
||||
if (dump_file)
|
||||
fprintf (dump_file, "Start of SCC component\n");
|
||||
fprintf (dump_file, "\n\nStart of SCC component\n");
|
||||
|
||||
/* Walk the component. CUR is the current node of the component that's
|
||||
being processed. */
|
||||
for (struct cgraph_node *cur = component_node; cur && !its_hopeless;
|
||||
cur = ((struct ipa_dfs_info *) cur->aux)->next_cycle)
|
||||
{
|
||||
/* Merge in summaries from CUR. */
|
||||
modref_summary *cur_summary = summaries->get (cur);
|
||||
|
||||
if (dump_file)
|
||||
fprintf (dump_file, " Processing %s\n",
|
||||
cur->dump_name ());
|
||||
|
||||
/* We don't know anything about CUR, hence we cannot tell anything
|
||||
about the entire component. */
|
||||
if (!cur_summary)
|
||||
{
|
||||
if (dump_file)
|
||||
fprintf (dump_file, " No summary\n");
|
||||
its_hopeless = true;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Summaries are all going to be same, pick first ones and merge
|
||||
everything in. */
|
||||
if (!first)
|
||||
{
|
||||
first = cur;
|
||||
loads = cur_summary->loads;
|
||||
stores = cur_summary->stores;
|
||||
loads_lto = cur_summary->loads_lto;
|
||||
stores_lto = cur_summary->stores_lto;
|
||||
}
|
||||
for (cgraph_edge *e = cur->indirect_calls; e; e = e->next_callee)
|
||||
{
|
||||
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");
|
||||
if (loads)
|
||||
loads->collapse ();
|
||||
if (loads_lto)
|
||||
loads_lto->collapse ();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (dump_file)
|
||||
fprintf (dump_file, " Indirect call: giving up\n");
|
||||
its_hopeless = true;
|
||||
}
|
||||
}
|
||||
|
||||
/* Walk every function that CUR calls and merge its summary. */
|
||||
for (cgraph_edge *callee_edge = cur->callees; callee_edge;
|
||||
callee_edge = callee_edge->next_callee)
|
||||
{
|
||||
int flags = flags_from_decl_or_type (callee_edge->callee->decl);
|
||||
modref_summary *callee_summary;
|
||||
struct cgraph_node *callee;
|
||||
|
||||
if (flags & (ECF_CONST | ECF_NOVOPS))
|
||||
continue;
|
||||
|
||||
if (dump_file)
|
||||
fprintf (dump_file, " Call to %s\n",
|
||||
callee_edge->callee->dump_name ());
|
||||
|
||||
/* We can not safely optimize based on summary of callee if it
|
||||
does not always bind to current def: it is possible that
|
||||
memory load was optimized out earlier which may not happen in
|
||||
the interposed variant. */
|
||||
if (!callee_edge->binds_to_current_def_p ())
|
||||
{
|
||||
if (loads)
|
||||
loads->collapse ();
|
||||
if (loads_lto)
|
||||
loads_lto->collapse ();
|
||||
if (dump_file)
|
||||
fprintf (dump_file, " May not bind local;"
|
||||
" collapsing loads\n");
|
||||
}
|
||||
|
||||
/* Get the callee and its summary. */
|
||||
enum availability avail;
|
||||
callee = callee_edge->callee->function_or_virtual_thunk_symbol
|
||||
(&avail, cur);
|
||||
|
||||
/* See if we can derive something from ECF flags. Be careful on
|
||||
not skipping calls within the SCC component: we must merge
|
||||
all their summaries.
|
||||
If we switch to iterative dataflow that may be necessary
|
||||
for future improvements this may go away. */
|
||||
if (callee->aux
|
||||
&& ((struct ipa_dfs_info *)cur->aux)->scc_no
|
||||
== ((struct ipa_dfs_info *)callee->aux)->scc_no)
|
||||
flags = 0;
|
||||
|
||||
bool ignore_stores = ignore_stores_p (cur->decl, flags);
|
||||
|
||||
/* We don't know anything about CALLEE, hence we cannot tell
|
||||
anything about the entire component. */
|
||||
|
||||
if (avail <= AVAIL_INTERPOSABLE
|
||||
|| !(callee_summary = summaries->get (callee)))
|
||||
{
|
||||
if (!ignore_stores)
|
||||
{
|
||||
its_hopeless = true;
|
||||
if (dump_file && avail <= AVAIL_INTERPOSABLE)
|
||||
fprintf (dump_file, " Call target interposable"
|
||||
" or not available\n");
|
||||
else if (dump_file)
|
||||
fprintf (dump_file, " No call target summary\n");
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (loads)
|
||||
loads->collapse ();
|
||||
if (loads_lto)
|
||||
loads_lto->collapse ();
|
||||
if (dump_file && avail <= AVAIL_INTERPOSABLE)
|
||||
fprintf (dump_file, " Call target interposable"
|
||||
"or not available; collapsing loads\n");
|
||||
else if (dump_file)
|
||||
fprintf (dump_file, " No call target summary;"
|
||||
" collapsing loads\n");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
auto_vec <int, 32> parm_map;
|
||||
/* TODO: compute parm_map. */
|
||||
|
||||
/* Merge in callee's information. */
|
||||
if (callee_summary->loads
|
||||
&& callee_summary->loads != loads)
|
||||
loads->merge (callee_summary->loads, &parm_map);
|
||||
if (callee_summary->stores
|
||||
&& callee_summary->stores != stores)
|
||||
stores->merge (callee_summary->stores, &parm_map);
|
||||
if (callee_summary->loads_lto
|
||||
&& callee_summary->loads_lto != loads_lto)
|
||||
loads_lto->merge (callee_summary->loads_lto, &parm_map);
|
||||
if (callee_summary->stores_lto
|
||||
&& callee_summary->stores_lto != stores_lto)
|
||||
stores_lto->merge (callee_summary->stores_lto, &parm_map);
|
||||
}
|
||||
}
|
||||
|
||||
/* At this time, ipa_loads and ipa_stores contain information
|
||||
about all loads and stores done by any of the component's nodes and
|
||||
all functions that any of the nodes calls. We will now propagate
|
||||
this information to all nodes in the component. Therefore, we will
|
||||
walk the component one more time to do it. */
|
||||
for (struct cgraph_node *cur = component_node; cur;
|
||||
cur = ((struct ipa_dfs_info *) cur->aux)->next_cycle)
|
||||
{
|
||||
modref_summary *cur_summary = summaries->get (cur);
|
||||
if (!cur_summary)
|
||||
{
|
||||
/* The function doesn't have a summary. We must have noticed
|
||||
that during the first pass and the hopeless flag must
|
||||
therefore be set. Skip the function. */
|
||||
gcc_assert (its_hopeless);
|
||||
}
|
||||
else if (its_hopeless)
|
||||
{
|
||||
if (dump_file)
|
||||
fprintf (dump_file, "Cleared modref info for %s\n",
|
||||
cur->dump_name ());
|
||||
summaries->remove (cur);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (cur == first)
|
||||
;
|
||||
else
|
||||
{
|
||||
if (loads)
|
||||
cur_summary->loads->merge (loads, NULL);
|
||||
if (stores)
|
||||
cur_summary->stores->merge (stores, NULL);
|
||||
if (loads_lto)
|
||||
cur_summary->loads_lto->merge (loads_lto, NULL);
|
||||
if (stores_lto)
|
||||
cur_summary->stores_lto->merge (stores_lto, NULL);
|
||||
}
|
||||
cur_summary->finished = true;
|
||||
if (dump_file)
|
||||
{
|
||||
fprintf (dump_file, "Propagated modref for %s%s%s\n",
|
||||
cur->dump_name (),
|
||||
TREE_READONLY (cur->decl) ? " (const)" : "",
|
||||
DECL_PURE_P (cur->decl) ? " (pure)" : "");
|
||||
cur_summary->dump (dump_file);
|
||||
}
|
||||
}
|
||||
}
|
||||
modref_propagate_in_scc (component_node);
|
||||
}
|
||||
((modref_summaries *)summaries)->ipa = false;
|
||||
ipa_free_postorder_info ();
|
||||
/* Free ipa-prop structures if they are no longer needed. */
|
||||
ipa_free_all_structures_after_iinln ();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -47,5 +47,6 @@ struct GTY(()) modref_summary
|
||||
|
||||
modref_summary *get_modref_function_summary (cgraph_node *func);
|
||||
void ipa_modref_c_finalize ();
|
||||
void ipa_merge_modref_summary_after_inlining (cgraph_edge *e);
|
||||
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user