Message ID | 20200926082020.GB627@kam.mff.cuni.cz |
---|---|
State | New |
Headers | show |
Series | Implement iterative dataflow in modref to track parameters | expand |
On September 26, 2020 10:20:20 AM GMT+02:00, Jan Hubicka <hubicka@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 <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. > >gcc/testsuite/ChangeLog: > >2020-09-26 Jan Hubicka <hubicka@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; >+}
Hello. The patch caused: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=97235 Martin
On 9/29/20 10:13 AM, Martin Liška wrote: > Hello. > > The patch caused: > https://gcc.gnu.org/bugzilla/show_bug.cgi?id=97235 > > Martin And these 2 PRs: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=97243 https://gcc.gnu.org/bugzilla/show_bug.cgi?id=97244 Thanks, Martin
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; +}