On September 26, 2020 10:20:20 AM GMT+02:00, Jan Hubicka <hubi...@ucw.cz> wrote: >Hi, >this patchs finishes the parameter tracking by implementing the >iterative >dataflow in propagation stage. This is necessary since we now can >propagate how the pointers are passed around recursive calls (as done >in >a testcase) and thus it is no-longer safe to simply merge all summaries >in one SCC component of the call-graph. > >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.
Any compile time figures for this? Firefox? > >2020-09-26 Jan Hubicka <hubi...@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. > >gcc/testsuite/ChangeLog: > >2020-09-26 Jan Hubicka <hubi...@ucw.cz> > > * gcc.dg/lto/modref-1_0.c: New test. > * gcc.dg/lto/modref-1_1.c: New test. > * gcc.dg/tree-ssa/modref-2.c: New test. > >diff --git a/gcc/ipa-inline-transform.c b/gcc/ipa-inline-transform.c >index 5e37e612bfd..af2c2856aaa 100644 >--- a/gcc/ipa-inline-transform.c >+++ b/gcc/ipa-inline-transform.c >@@ -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); >diff --git a/gcc/ipa-inline.c b/gcc/ipa-inline.c >index c667de2a97c..225a0140725 100644 >--- a/gcc/ipa-inline.c >+++ b/gcc/ipa-inline.c >@@ -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", >diff --git a/gcc/ipa-modref.c b/gcc/ipa-modref.c >index 44b844b90db..385b0318442 100644 >--- a/gcc/ipa-modref.c >+++ b/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,71 +1325,176 @@ ignore_edge (struct cgraph_edge *e) > & (ECF_CONST | ECF_NOVOPS)); > } > >-/* 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. */ >+/* Compute parm_map for CALLE_EDGE. */ > >-unsigned int pass_ipa_modref::execute (function *) >+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 0; >+ return; > >- struct cgraph_node **order = XCNEWVEC (struct cgraph_node *, >- symtab->cgraph_count); >- int order_pos; >- order_pos = ipa_reduced_postorder (order, true, ignore_edge); >- int i; >+ struct cgraph_node *to = (edge->caller->inlined_to >+ ? edge->caller->inlined_to : edge->caller); >+ class modref_summary *to_info = summaries->get (to); > >- /* Iterate over all strongly connected components in post-order. */ >- for (i = 0; i < order_pos; i++) >+ 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) > { >- bool its_hopeless = false; >- modref_records *loads = NULL; >- modref_records *stores = NULL; >- modref_records_lto *loads_lto = NULL; >- modref_records_lto *stores_lto = NULL; >+ 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; >+} > >- /* 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; >+/* Collapse loads and return true if something changed. */ > >- if (dump_file) >- fprintf (dump_file, "Start of SCC component\n"); >+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; >+} > >- /* 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; >+/* 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) > { >- /* Merge in summaries from CUR. */ >- modref_summary *cur_summary = summaries->get (cur); >- >- if (dump_file) >- fprintf (dump_file, " Processing %s\n", >- cur->dump_name ()); >+ cgraph_node *node = cur->inlined_to ? cur->inlined_to : cur; >+ modref_summary *cur_summary = summaries->get (node); > >- /* 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; >- } >+ 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)" : ""); > >- /* 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)) >@@ -1350,20 +1504,22 @@ unsigned int pass_ipa_modref::execute (function >*) > if (dump_file) > fprintf (dump_file, " Indirect call: " > "collapsing loads\n"); >- if (loads) >- loads->collapse (); >- if (loads_lto) >- loads_lto->collapse (); >+ changed |= collapse_loads (cur_summary); > } > else > { > if (dump_file) > fprintf (dump_file, " Indirect call: giving up\n"); >- its_hopeless = true; >+ summaries->remove (node); >+ changed = true; >+ cur_summary = NULL; >+ break; > } > } > >- /* Walk every function that CUR calls and merge its summary. */ >+ if (!cur_summary) >+ continue; >+ > for (cgraph_edge *callee_edge = cur->callees; callee_edge; > callee_edge = callee_edge->next_callee) > { >@@ -1371,42 +1527,26 @@ unsigned int pass_ipa_modref::execute (function >*) > modref_summary *callee_summary; > struct cgraph_node *callee; > >- if (flags & (ECF_CONST | ECF_NOVOPS)) >+ if (flags & (ECF_CONST | ECF_NOVOPS) >+ || !callee_edge->inline_failed) > 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; >+ /* 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); > >@@ -1418,101 +1558,128 @@ unsigned int pass_ipa_modref::execute >(function *) > { > 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"); >+ >+ summaries->remove (node); >+ changed = true; > 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"); >+ " 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; >- /* TODO: compute parm_map. */ >+ >+ compute_parm_map (callee_edge, &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); >+ 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); > } > } >- >- /* 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; >+ 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) >- { >- 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); >- } >- } >- } >+ 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 *) >+{ >+ if (!summaries) >+ return 0; >+ >+ struct cgraph_node **order = XCNEWVEC (struct cgraph_node *, >+ symtab->cgraph_count); >+ int order_pos; >+ order_pos = ipa_reduced_postorder (order, true, ignore_edge); >+ int i; >+ >+ /* Iterate over all strongly connected components in post-order. */ >+ for (i = 0; i < order_pos; i++) >+ { >+ /* 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]; >+ >+ if (dump_file) >+ fprintf (dump_file, "\n\nStart of SCC component\n"); >+ >+ 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; > } > >diff --git a/gcc/ipa-modref.h b/gcc/ipa-modref.h >index 152e7154aed..b6621b498f0 100644 >--- a/gcc/ipa-modref.h >+++ b/gcc/ipa-modref.h >@@ -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 >diff --git a/gcc/testsuite/gcc.dg/lto/modref-1_0.c >b/gcc/testsuite/gcc.dg/lto/modref-1_0.c >new file mode 100644 >index 00000000000..8fcb9ec52f1 >--- /dev/null >+++ b/gcc/testsuite/gcc.dg/lto/modref-1_0.c >@@ -0,0 +1,14 @@ >+/* { dg-lto-do run } */ >+/* { dg-lto-options {"-O2 -flto-partition=max -flto"} } */ >+extern void recursive (int *a, int *b, int *c, int level); >+int >+main() >+{ >+ int x = 123, y=124, z=125; >+ recursive (&x,&y,&z,1); >+ if (y) >+ __builtin_abort (); >+ if (!__builtin_constant_p (z)) >+ __builtin_abort (); >+ return 0; >+} >diff --git a/gcc/testsuite/gcc.dg/lto/modref-1_1.c >b/gcc/testsuite/gcc.dg/lto/modref-1_1.c >new file mode 100644 >index 00000000000..c7c0eaec971 >--- /dev/null >+++ b/gcc/testsuite/gcc.dg/lto/modref-1_1.c >@@ -0,0 +1,13 @@ >+short aa; >+void >+__attribute__ ((noinline, noclone)) >+recursive (int *a, int *b, int *c, int level) >+{ >+ if (level && c) >+ { >+ recursive (b,a,c,0); >+ aa++; >+ } >+ else >+ *a=0; >+} >diff --git a/gcc/testsuite/gcc.dg/tree-ssa/modref-2.c >b/gcc/testsuite/gcc.dg/tree-ssa/modref-2.c >new file mode 100644 >index 00000000000..9999d37369d >--- /dev/null >+++ b/gcc/testsuite/gcc.dg/tree-ssa/modref-2.c >@@ -0,0 +1,26 @@ >+/* { dg-do run } */ >+/* { dg-options "-O2" } */ >+short aa; >+void >+__attribute__ ((noinline, noclone)) >+recursive (int *a, int *b, int *c, int level) >+{ >+ if (level && c) >+ { >+ recursive (b,a,c,0); >+ aa++; >+ } >+ else >+ *a=0; >+} >+int >+main() >+{ >+ int x = 123, y=124, z=125; >+ recursive (&x,&y,&z,1); >+ if (y) >+ __builtin_abort (); >+ if (!__builtin_constant_p (z)) >+ __builtin_abort (); >+ return 0; >+}