@@ -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. */
@@ -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);
@@ -1513,6 +1611,9 @@ propagate_pure_const (void)
enum pure_const_state_e edge_state = IPA_CONST;
bool edge_looping = false;
+ if (e->recursive_p ())
+ looping = true;
+
if (e->recursive_p ())
looping = true;
@@ -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:
@@ -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);
@@ -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. */
@@ -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 ()
@@ -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" } } */
@@ -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;
};
new file mode 100644
@@ -0,0 +1,15 @@
+/* { dg-do compile } */
+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 a candidate for attribute .const" } */
+{
+ struct a a;
+ init (&a);
+ return a.a + a.b + a.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)
@@ -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.