}
/* 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,
/* 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)
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)
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);
}
}
}
+ return fixup_cfg;
}
/* Callback for generate_summary. */
unsigned int pass_modref::execute (function *f)
{
- analyze_function (f, false);
+ if (analyze_function (f, false))
+ return execute_fixup_cfg ();
return 0;
}
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. */
/* 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;
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. */
{
if (!summaries && !summaries_lto)
return 0;
+ bool pureconst = false;
if (optimization_summaries)
ggc_delete (optimization_summaries);
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);
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. */
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");
}
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");
}
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. */
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);
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;
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:
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. */
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: