diff options
author | Jan Hubicka <jh@suse.cz> | 2021-11-11 18:14:45 +0100 |
---|---|---|
committer | Jan Hubicka <jh@suse.cz> | 2021-11-11 18:14:45 +0100 |
commit | 494bdadf28d0fb3505ff8dce5afa587e0ff46544 (patch) | |
tree | 9924cba8140a7768bc2b1a4e4997f1d1eec9fa07 | |
parent | abdff441a07f55d16e3d0e5ced3123c83d210a0a (diff) | |
download | gcc-494bdadf28d0fb3505ff8dce5afa587e0ff46544.tar.gz |
Enable pure-const discovery in modref.
We newly can handle some extra cases, for example:
struct a {int a,b,c;};
__attribute__ ((noinline))
int init (struct a *a)
{
a->a=1;
a->b=2;
a->c=3;
}
int const_fn ()
{
struct a a;
init (&a);
return a.a + a.b + a.c;
}
Here pure/const stops on the fact that const_fn calls non-const init, while
modref knows that the memory it initializes is local to const_fn.
I ended up reordering passes so early modref is done after early pure-const
mostly to avoid need to change testsuite which greps for const functions
being detects in pure-const. Stil some testuiste compensation is needed.
gcc/ChangeLog:
2021-11-11 Jan Hubicka <hubicka@ucw.cz>
* ipa-modref.c (analyze_function): Do pure/const discovery, return
true on success.
(pass_modref::execute): If pure/const is discovered fixup cfg.
(ignore_edge): Do not ignore pure/const edges.
(modref_propagate_in_scc): Do pure/const discovery, return true if
cdtor was promoted pure/const.
(pass_ipa_modref::execute): If needed remove unreachable functions.
* ipa-pure-const.c (warn_function_noreturn): Fix whitespace.
(warn_function_cold): Likewise.
(skip_function_for_local_pure_const): Move earlier.
(ipa_make_function_const): Break out from ...
(ipa_make_function_pure): Break out from ...
(propagate_pure_const): ... here.
(pass_local_pure_const::execute): Use it.
* ipa-utils.h (ipa_make_function_const): Declare.
(ipa_make_function_pure): Declare.
* passes.def: Move early modref after pure-const.
gcc/testsuite/ChangeLog:
2021-11-11 Jan Hubicka <hubicka@ucw.cz>
* c-c++-common/tm/inline-asm.c: Disable pure-const.
* g++.dg/ipa/modref-1.C: Update template.
* gcc.dg/tree-ssa/modref-11.c: Disable pure-const.
* gcc.dg/tree-ssa/modref-14.c: New test.
* gcc.dg/tree-ssa/modref-8.c: Do not optimize sibling calls.
* gfortran.dg/do_subscript_3.f90: Add -O0.
-rw-r--r-- | gcc/ipa-modref.c | 85 | ||||
-rw-r--r-- | gcc/ipa-pure-const.c | 233 | ||||
-rw-r--r-- | gcc/ipa-utils.h | 2 | ||||
-rw-r--r-- | gcc/passes.def | 2 | ||||
-rw-r--r-- | gcc/testsuite/c-c++-common/tm/inline-asm.c | 2 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/ipa/modref-1.C | 4 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/tree-ssa/modref-11.c | 2 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/tree-ssa/modref-14.c | 16 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/tree-ssa/modref-8.c | 2 | ||||
-rw-r--r-- | gcc/testsuite/gfortran.dg/do_subscript_3.f90 | 1 |
10 files changed, 204 insertions, 145 deletions
diff --git a/gcc/ipa-modref.c b/gcc/ipa-modref.c index 45b391a565e..72006251f29 100644 --- a/gcc/ipa-modref.c +++ b/gcc/ipa-modref.c @@ -2603,11 +2603,13 @@ analyze_parms (modref_summary *summary, modref_summary_lto *summary_lto, } /* Analyze function F. IPA indicates whether we're running in local mode - (false) or the IPA mode (true). */ + (false) or the IPA mode (true). + Return true if fixup cfg is needed after the pass. */ -static void +static bool analyze_function (function *f, bool ipa) { + bool fixup_cfg = false; if (dump_file) fprintf (dump_file, "modref analyzing '%s' (ipa=%i)%s%s\n", function_name (f), ipa, @@ -2617,7 +2619,7 @@ analyze_function (function *f, bool ipa) /* Don't analyze this function if it's compiled with -fno-strict-aliasing. */ if (!flag_ipa_modref || lookup_attribute ("noipa", DECL_ATTRIBUTES (current_function_decl))) - return; + return false; /* Compute no-LTO summaries when local optimization is going to happen. */ bool nolto = (!ipa || ((!flag_lto || flag_fat_lto_objects) && !in_lto_p) @@ -2774,12 +2776,32 @@ analyze_function (function *f, bool ipa) if (!summary->useful_p (ecf_flags, false)) { remove_summary (lto, nolto, ipa); - return; + return false; } } first = false; } } + if (summary && !summary->global_memory_written_p () && !summary->side_effects + && !finite_function_p ()) + summary->side_effects = true; + if (summary_lto && !summary_lto->side_effects && !finite_function_p ()) + summary_lto->side_effects = true; + + if (!ipa && flag_ipa_pure_const) + { + if (!summary->stores->every_base && !summary->stores->bases) + { + if (!summary->loads->every_base && !summary->loads->bases) + fixup_cfg = ipa_make_function_const + (cgraph_node::get (current_function_decl), + summary->side_effects, true); + else + fixup_cfg = ipa_make_function_pure + (cgraph_node::get (current_function_decl), + summary->side_effects, true); + } + } if (summary && !summary->useful_p (ecf_flags)) { if (!ipa) @@ -2793,11 +2815,6 @@ analyze_function (function *f, bool ipa) summaries_lto->remove (fnode); summary_lto = NULL; } - if (summary && !summary->global_memory_written_p () && !summary->side_effects - && !finite_function_p ()) - summary->side_effects = true; - if (summary_lto && !summary_lto->side_effects && !finite_function_p ()) - summary_lto->side_effects = true; if (ipa && !summary && !summary_lto) remove_modref_edge_summaries (fnode); @@ -2907,6 +2924,7 @@ analyze_function (function *f, bool ipa) } } } + return fixup_cfg; } /* Callback for generate_summary. */ @@ -3714,7 +3732,8 @@ public: unsigned int pass_modref::execute (function *f) { - analyze_function (f, false); + if (analyze_function (f, false)) + return execute_fixup_cfg (); return 0; } @@ -3749,9 +3768,7 @@ ignore_edge (struct cgraph_edge *e) return (avail <= AVAIL_INTERPOSABLE || ((!optimization_summaries || !optimization_summaries->get (callee)) - && (!summaries_lto || !summaries_lto->get (callee))) - || flags_from_decl_or_type (e->callee->decl) - & (ECF_CONST | ECF_NOVOPS)); + && (!summaries_lto || !summaries_lto->get (callee)))); } /* Compute parm_map for CALLEE_EDGE. */ @@ -4130,7 +4147,7 @@ remove_useless_summaries (cgraph_node *node, /* Perform iterative dataflow on SCC component starting in COMPONENT_NODE and propagate loads/stores. */ -static void +static bool modref_propagate_in_scc (cgraph_node *component_node) { bool changed = true; @@ -4352,6 +4369,38 @@ modref_propagate_in_scc (cgraph_node *component_node) if (dump_file) fprintf (dump_file, "Propagation finished in %i iterations\n", iteration); + bool pureconst = false; + for (struct cgraph_node *cur = component_node; cur; + cur = ((struct ipa_dfs_info *) cur->aux)->next_cycle) + if (!cur->inlined_to && opt_for_fn (cur->decl, flag_ipa_pure_const)) + { + modref_summary *summary = optimization_summaries + ? optimization_summaries->get (cur) + : NULL; + modref_summary_lto *summary_lto = summaries_lto + ? summaries_lto->get (cur) + : NULL; + if (summary && !summary->stores->every_base && !summary->stores->bases) + { + if (!summary->loads->every_base && !summary->loads->bases) + pureconst |= ipa_make_function_const + (cur, summary->side_effects, false); + else + pureconst |= ipa_make_function_pure + (cur, summary->side_effects, false); + } + if (summary_lto && !summary_lto->stores->every_base + && !summary_lto->stores->bases) + { + if (!summary_lto->loads->every_base && !summary_lto->loads->bases) + pureconst |= ipa_make_function_const + (cur, summary_lto->side_effects, false); + else + pureconst |= ipa_make_function_pure + (cur, summary_lto->side_effects, false); + } + } + return pureconst; } /* Dump results of propagation in SCC rooted in COMPONENT_NODE. */ @@ -4831,6 +4880,7 @@ pass_ipa_modref::execute (function *) { if (!summaries && !summaries_lto) return 0; + bool pureconst = false; if (optimization_summaries) ggc_delete (optimization_summaries); @@ -4853,7 +4903,7 @@ pass_ipa_modref::execute (function *) if (dump_file) fprintf (dump_file, "\n\nStart of SCC component\n"); - modref_propagate_in_scc (component_node); + pureconst |= modref_propagate_in_scc (component_node); modref_propagate_flags_in_scc (component_node); if (dump_file) modref_propagate_dump_scc (component_node); @@ -4869,7 +4919,10 @@ pass_ipa_modref::execute (function *) fnspec_summaries = NULL; delete escape_summaries; escape_summaries = NULL; - return 0; + + /* If we posibly made constructors const/pure we may need to remove + them. */ + return pureconst ? TODO_remove_functions : 0; } /* Summaries must stay alive until end of compilation. */ diff --git a/gcc/ipa-pure-const.c b/gcc/ipa-pure-const.c index 422b52fba4b..550bdeded16 100644 --- a/gcc/ipa-pure-const.c +++ b/gcc/ipa-pure-const.c @@ -275,7 +275,7 @@ warn_function_noreturn (tree decl) static hash_set<tree> *warned_about; if (!lang_hooks.missing_noreturn_ok_p (decl) && targetm.warn_func_return (decl)) - warned_about + warned_about = suggest_attribute (OPT_Wsuggest_attribute_noreturn, original_decl, true, warned_about, "noreturn"); } @@ -286,7 +286,7 @@ warn_function_cold (tree decl) tree original_decl = decl; static hash_set<tree> *warned_about; - warned_about + warned_about = suggest_attribute (OPT_Wsuggest_attribute_cold, original_decl, true, warned_about, "cold"); } @@ -1428,6 +1428,105 @@ ignore_edge_for_pure_const (struct cgraph_edge *e) flag_ipa_pure_const)); } +/* Return true if function should be skipped for local pure const analysis. */ + +static bool +skip_function_for_local_pure_const (struct cgraph_node *node) +{ + /* Because we do not schedule pass_fixup_cfg over whole program after early + optimizations we must not promote functions that are called by already + processed functions. */ + + if (function_called_by_processed_nodes_p ()) + { + if (dump_file) + fprintf (dump_file, "Function called in recursive cycle; ignoring\n"); + return true; + } + /* Save some work and do not analyze functions which are interposable and + do not have any non-interposable aliases. */ + if (node->get_availability () <= AVAIL_INTERPOSABLE + && !flag_lto + && !node->has_aliases_p ()) + { + if (dump_file) + fprintf (dump_file, + "Function is interposable; not analyzing.\n"); + return true; + } + return false; +} + +/* Make function const and output warning. If LOCAL is true, + return true if anything changed. Otherwise return true if + we may have introduced removale ctors. */ + +bool +ipa_make_function_const (struct cgraph_node *node, bool looping, bool local) +{ + bool cdtor = false; + + if (TREE_READONLY (node->decl) + && (looping || !DECL_LOOPING_CONST_OR_PURE_P (node->decl))) + return false; + warn_function_const (node->decl, !looping); + if (local && skip_function_for_local_pure_const (node)) + return false; + if (dump_file) + fprintf (dump_file, "Function found to be %sconst: %s\n", + looping ? "looping " : "", + node->dump_name ()); + if (!local) + cdtor = node->call_for_symbol_and_aliases (cdtor_p, NULL, true); + if (node->set_const_flag (true, looping)) + { + if (dump_file) + fprintf (dump_file, + "Declaration updated to be %sconst: %s\n", + looping ? "looping " : "", + node->dump_name ()); + if (local) + return true; + return cdtor; + } + return false; +} + +/* Make function const and output warning. If LOCAL is true, + return true if anything changed. Otherwise return true if + we may have introduced removale ctors. */ + +bool +ipa_make_function_pure (struct cgraph_node *node, bool looping, bool local) +{ + bool cdtor = false; + + if (DECL_PURE_P (node->decl) + && (looping || DECL_LOOPING_CONST_OR_PURE_P (node->decl))) + return false; + warn_function_pure (node->decl, !looping); + if (local && skip_function_for_local_pure_const (node)) + return false; + if (dump_file) + fprintf (dump_file, "Function found to be %spure: %s\n", + looping ? "looping " : "", + node->dump_name ()); + if (!local) + cdtor = node->call_for_symbol_and_aliases (cdtor_p, NULL, true); + if (node->set_pure_flag (true, looping)) + { + if (dump_file) + fprintf (dump_file, + "Declaration updated to be %spure: %s\n", + looping ? "looping " : "", + node->dump_name ()); + if (local) + return true; + return cdtor; + } + return false; +} + /* Produce transitive closure over the callgraph and compute pure/const attributes. */ @@ -1442,7 +1541,6 @@ propagate_pure_const (void) int i; struct ipa_dfs_info * w_info; bool remove_p = false; - bool has_cdtor; order_pos = ipa_reduced_postorder (order, true, ignore_edge_for_pure_const); @@ -1516,6 +1614,9 @@ propagate_pure_const (void) if (e->recursive_p ()) looping = true; + if (e->recursive_p ()) + looping = true; + if (dump_file && (dump_flags & TDF_DETAILS)) { fprintf (dump_file, " Call to %s", @@ -1699,55 +1800,11 @@ propagate_pure_const (void) switch (this_state) { case IPA_CONST: - if (!TREE_READONLY (w->decl)) - { - warn_function_const (w->decl, !this_looping); - if (dump_file) - fprintf (dump_file, "Function found to be %sconst: %s\n", - this_looping ? "looping " : "", - w->dump_name ()); - } - /* Turning constructor or destructor to non-looping const/pure - enables us to possibly remove the function completely. */ - if (this_looping) - has_cdtor = false; - else - has_cdtor = w->call_for_symbol_and_aliases (cdtor_p, - NULL, true); - if (w->set_const_flag (true, this_looping)) - { - if (dump_file) - fprintf (dump_file, - "Declaration updated to be %sconst: %s\n", - this_looping ? "looping " : "", - w->dump_name ()); - remove_p |= has_cdtor; - } + remove_p |= ipa_make_function_const (node, looping, false); break; case IPA_PURE: - if (!DECL_PURE_P (w->decl)) - { - warn_function_pure (w->decl, !this_looping); - if (dump_file) - fprintf (dump_file, "Function found to be %spure: %s\n", - this_looping ? "looping " : "", - w->dump_name ()); - } - if (this_looping) - has_cdtor = false; - else - has_cdtor = w->call_for_symbol_and_aliases (cdtor_p, - NULL, true); - if (w->set_pure_flag (true, this_looping)) - { - if (dump_file) - fprintf (dump_file, - "Declaration updated to be %spure: %s\n", - this_looping ? "looping " : "", - w->dump_name ()); - remove_p |= has_cdtor; - } + remove_p |= ipa_make_function_pure (node, looping, false); break; default: @@ -2046,34 +2103,6 @@ make_pass_ipa_pure_const (gcc::context *ctxt) return new pass_ipa_pure_const (ctxt); } -/* Return true if function should be skipped for local pure const analysis. */ - -static bool -skip_function_for_local_pure_const (struct cgraph_node *node) -{ - /* Because we do not schedule pass_fixup_cfg over whole program after early - optimizations we must not promote functions that are called by already - processed functions. */ - - if (function_called_by_processed_nodes_p ()) - { - if (dump_file) - fprintf (dump_file, "Function called in recursive cycle; ignoring\n"); - return true; - } - /* Save some work and do not analyze functions which are interposable and - do not have any non-interposable aliases. */ - if (node->get_availability () <= AVAIL_INTERPOSABLE - && !node->has_aliases_p ()) - { - if (dump_file) - fprintf (dump_file, - "Function is interposable; not analyzing.\n"); - return true; - } - return false; -} - /* Simple local pass for pure const discovery reusing the analysis from ipa_pure_const. This pass is effective when executed together with other optimization passes in early optimization pass queue. */ @@ -2144,55 +2173,13 @@ pass_local_pure_const::execute (function *fun) switch (l->pure_const_state) { case IPA_CONST: - if (!TREE_READONLY (current_function_decl)) - { - warn_function_const (current_function_decl, !l->looping); - if (dump_file) - fprintf (dump_file, "Function found to be %sconst: %s\n", - l->looping ? "looping " : "", - current_function_name ()); - } - else if (DECL_LOOPING_CONST_OR_PURE_P (current_function_decl) - && !l->looping) - { - if (dump_file) - fprintf (dump_file, "Function found to be non-looping: %s\n", - current_function_name ()); - } - if (!skip && node->set_const_flag (true, l->looping)) - { - if (dump_file) - fprintf (dump_file, "Declaration updated to be %sconst: %s\n", - l->looping ? "looping " : "", - current_function_name ()); - changed = true; - } + changed |= ipa_make_function_const + (cgraph_node::get (current_function_decl), l->looping, true); break; case IPA_PURE: - if (!DECL_PURE_P (current_function_decl)) - { - warn_function_pure (current_function_decl, !l->looping); - if (dump_file) - fprintf (dump_file, "Function found to be %spure: %s\n", - l->looping ? "looping " : "", - current_function_name ()); - } - else if (DECL_LOOPING_CONST_OR_PURE_P (current_function_decl) - && !l->looping) - { - if (dump_file) - fprintf (dump_file, "Function found to be non-looping: %s\n", - current_function_name ()); - } - if (!skip && node->set_pure_flag (true, l->looping)) - { - if (dump_file) - fprintf (dump_file, "Declaration updated to be %spure: %s\n", - l->looping ? "looping " : "", - current_function_name ()); - changed = true; - } + changed |= ipa_make_function_pure + (cgraph_node::get (current_function_decl), l->looping, true); break; default: diff --git a/gcc/ipa-utils.h b/gcc/ipa-utils.h index 824780f562a..e2440a1f893 100644 --- a/gcc/ipa-utils.h +++ b/gcc/ipa-utils.h @@ -50,6 +50,8 @@ bool recursive_call_p (tree, tree); /* In ipa-pure-const.c */ bool finite_function_p (); bool builtin_safe_for_const_function_p (bool *, tree); +bool ipa_make_function_const (cgraph_node *, bool, bool); +bool ipa_make_function_pure (cgraph_node *, bool, bool); /* In ipa-profile.c */ bool ipa_propagate_frequency (struct cgraph_node *node); diff --git a/gcc/passes.def b/gcc/passes.def index 56dab80a029..b583d17c86f 100644 --- a/gcc/passes.def +++ b/gcc/passes.def @@ -92,13 +92,13 @@ along with GCC; see the file COPYING3. If not see NEXT_PASS (pass_dse); NEXT_PASS (pass_cd_dce, false /* update_address_taken_p */); NEXT_PASS (pass_phiopt, true /* early_p */); - NEXT_PASS (pass_modref); NEXT_PASS (pass_tail_recursion); NEXT_PASS (pass_if_to_switch); NEXT_PASS (pass_convert_switch); NEXT_PASS (pass_cleanup_eh); NEXT_PASS (pass_profile); NEXT_PASS (pass_local_pure_const); + NEXT_PASS (pass_modref); /* Split functions creates parts that are not run through early optimizations again. It is thus good idea to do this late. */ diff --git a/gcc/testsuite/c-c++-common/tm/inline-asm.c b/gcc/testsuite/c-c++-common/tm/inline-asm.c index 73892601897..176266893e9 100644 --- a/gcc/testsuite/c-c++-common/tm/inline-asm.c +++ b/gcc/testsuite/c-c++-common/tm/inline-asm.c @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-fgnu-tm -O1" } */ +/* { dg-options "-fgnu-tm -O1 -fno-ipa-modref -fno-ipa-pure-const" } */ static inline void inline_death () diff --git a/gcc/testsuite/g++.dg/ipa/modref-1.C b/gcc/testsuite/g++.dg/ipa/modref-1.C index c57aaca0230..b49238162fe 100644 --- a/gcc/testsuite/g++.dg/ipa/modref-1.C +++ b/gcc/testsuite/g++.dg/ipa/modref-1.C @@ -30,6 +30,6 @@ int main() linker_error (); return 0; } -/* { dg-final { scan-tree-dump "Function found to be const: {anonymous}::B::genB" "local-pure-const1" } } */ -/* { dg-final { scan-tree-dump "Retslot flags: no_indirect_clobber no_direct_escape no_indirect_escape not_returned_directly not_returned_indirectly no_direct_read no_indirect_read" "modref1" } } */ +/* { dg-final { scan-tree-dump "Function found to be const: static {anonymous}::B {anonymous}::B::genB" "local-pure-const1" } } */ +/* { dg-final { scan-tree-dump "Retslot flags: not_returned_directly" "modref1" } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/modref-11.c b/gcc/testsuite/gcc.dg/tree-ssa/modref-11.c index cafb4f34894..10ebe1ff474 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/modref-11.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/modref-11.c @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-O2 -fdump-tree-modref1" } */ +/* { dg-options "-O2 -fdump-tree-modref1 -fno-ipa-pure-const" } */ struct linkedlist { struct linkedlist *next; }; diff --git a/gcc/testsuite/gcc.dg/tree-ssa/modref-14.c b/gcc/testsuite/gcc.dg/tree-ssa/modref-14.c new file mode 100644 index 00000000000..986248f37a2 --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/modref-14.c @@ -0,0 +1,16 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -Wsuggest-attribute=const" } */ +struct a {int a,b,c;}; +__attribute__ ((noinline)) +int init (struct a *a) +{ + a->a=1; + a->b=2; + a->c=3; +} +int const_fn () /* { dg-warning "function might be candidate for attribute 'const" } */ +{ + struct a a; + init (&a); + return a.a + a.b + a.c; +} diff --git a/gcc/testsuite/gcc.dg/tree-ssa/modref-8.c b/gcc/testsuite/gcc.dg/tree-ssa/modref-8.c index 15ae4acc03f..4a18e34cd16 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/modref-8.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/modref-8.c @@ -1,4 +1,4 @@ -/* { dg-options "-O2 --param modref-max-adjustments=8 -fdump-tree-modref1" } */ +/* { dg-options "-O2 --param modref-max-adjustments=8 -fdump-tree-modref1 -fno-optimize-sibling-calls" } */ /* { dg-do compile } */ void set (char *p) diff --git a/gcc/testsuite/gfortran.dg/do_subscript_3.f90 b/gcc/testsuite/gfortran.dg/do_subscript_3.f90 index 2f62f58142b..8f7183eb9b6 100644 --- a/gcc/testsuite/gfortran.dg/do_subscript_3.f90 +++ b/gcc/testsuite/gfortran.dg/do_subscript_3.f90 @@ -1,4 +1,5 @@ ! { dg-do compile } +! { dg-options "-O0" } ! PR fortran/91424 ! Check that only one warning is issued inside blocks, and that ! warnings are also issued for contained subroutines. |