@@ -87,6 +87,7 @@ along with GCC; see the file COPYING3. If not see
#include "tree-ssanames.h"
#include "attribs.h"
#include "tree-cfg.h"
+#include "tree-eh.h"
namespace {
@@ -273,7 +274,7 @@ static GTY(()) fast_function_summary <modref_summary_lto *, va_gc>
modref_summary::modref_summary ()
: loads (NULL), stores (NULL), retslot_flags (0), static_chain_flags (0),
- writes_errno (false)
+ writes_errno (false), side_effects (false)
{
}
@@ -371,6 +372,7 @@ struct GTY(()) modref_summary_lto
eaf_flags_t retslot_flags;
eaf_flags_t static_chain_flags;
bool writes_errno;
+ bool side_effects;
modref_summary_lto ();
~modref_summary_lto ();
@@ -382,7 +384,7 @@ struct GTY(()) modref_summary_lto
modref_summary_lto::modref_summary_lto ()
: loads (NULL), stores (NULL), retslot_flags (0), static_chain_flags (0),
- writes_errno (false)
+ writes_errno (false), side_effects (false)
{
}
@@ -615,6 +617,8 @@ modref_summary::dump (FILE *out)
}
if (writes_errno)
fprintf (out, " Writes errno\n");
+ if (side_effects)
+ fprintf (out, " Side effects\n");
if (arg_flags.length ())
{
for (unsigned int i = 0; i < arg_flags.length (); i++)
@@ -647,6 +651,8 @@ modref_summary_lto::dump (FILE *out)
dump_lto_records (stores, out);
if (writes_errno)
fprintf (out, " Writes errno\n");
+ if (side_effects)
+ fprintf (out, " Side effects\n");
if (arg_flags.length ())
{
for (unsigned int i = 0; i < arg_flags.length (); i++)
@@ -980,6 +986,12 @@ merge_call_side_effects (modref_summary *cur_summary,
changed = true;
}
}
+ if (!cur_summary->side_effects
+ && callee_summary->side_effects)
+ {
+ cur_summary->side_effects = true;
+ changed = true;
+ }
return changed;
}
@@ -1075,6 +1087,18 @@ process_fnspec (modref_summary *cur_summary,
gcall *call, bool ignore_stores)
{
attr_fnspec fnspec = gimple_call_fnspec (call);
+ int flags = gimple_call_flags (call);
+
+ if (!(flags & (ECF_CONST | ECF_NOVOPS))
+ || (flags & ECF_LOOPING_CONST_OR_PURE)
+ || (cfun->can_throw_non_call_exceptions
+ && stmt_could_throw_p (cfun, call)))
+ {
+ if (cur_summary)
+ cur_summary->side_effects = true;
+ if (cur_summary_lto)
+ cur_summary_lto->side_effects = true;
+ }
if (!fnspec.known_p ())
{
if (dump_file && gimple_call_builtin_p (call, BUILT_IN_NORMAL))
@@ -1212,6 +1236,10 @@ analyze_call (modref_summary *cur_summary, modref_summary_lto *cur_summary_lto,
if (recursive_call_p (current_function_decl, callee))
{
recursive_calls->safe_push (stmt);
+ if (cur_summary)
+ cur_summary->side_effects = true;
+ if (cur_summary_lto)
+ cur_summary_lto->side_effects = true;
if (dump_file)
fprintf (dump_file, " - Skipping recursive call.\n");
return true;
@@ -1222,6 +1250,20 @@ analyze_call (modref_summary *cur_summary, modref_summary_lto *cur_summary_lto,
/* Get the function symbol and its availability. */
enum availability avail;
callee_node = callee_node->function_symbol (&avail);
+ bool looping;
+ if (builtin_safe_for_const_function_p (&looping, callee))
+ {
+ if (looping)
+ {
+ if (cur_summary)
+ cur_summary->side_effects = true;
+ if (cur_summary_lto)
+ cur_summary_lto->side_effects = true;
+ }
+ if (dump_file)
+ fprintf (dump_file, " - Bulitin is safe for const.\n");
+ return true;
+ }
if (avail <= AVAIL_INTERPOSABLE)
{
if (dump_file)
@@ -1268,6 +1310,18 @@ analyze_load (gimple *, tree, tree op, void *data)
fprintf (dump_file, "\n");
}
+ if (TREE_THIS_VOLATILE (op)
+ || (cfun->can_throw_non_call_exceptions
+ && tree_could_throw_p (op)))
+ {
+ if (dump_file)
+ fprintf (dump_file, " (volatile or can throw; marking side effects) ");
+ if (summary)
+ summary->side_effects = true;
+ if (summary_lto)
+ summary_lto->side_effects = true;
+ }
+
if (!record_access_p (op))
return false;
@@ -1296,6 +1350,18 @@ analyze_store (gimple *, tree, tree op, void *data)
fprintf (dump_file, "\n");
}
+ if (TREE_THIS_VOLATILE (op)
+ || (cfun->can_throw_non_call_exceptions
+ && tree_could_throw_p (op)))
+ {
+ if (dump_file)
+ fprintf (dump_file, " (volatile or can throw; marking side effects) ");
+ if (summary)
+ summary->side_effects = true;
+ if (summary_lto)
+ summary_lto->side_effects = true;
+ }
+
if (!record_access_p (op))
return false;
@@ -1332,6 +1398,15 @@ analyze_stmt (modref_summary *summary, modref_summary_lto *summary_lto,
switch (gimple_code (stmt))
{
case GIMPLE_ASM:
+ if (gimple_asm_volatile_p (as_a <gasm *> (stmt))
+ || (cfun->can_throw_non_call_exceptions
+ && stmt_could_throw_p (cfun, stmt)))
+ {
+ if (summary)
+ summary->side_effects = true;
+ if (summary_lto)
+ summary_lto->side_effects = true;
+ }
/* If the ASM statement does not read nor write memory, there's nothing
to do. Otherwise just give up. */
if (!gimple_asm_clobbers_memory_p (as_a <gasm *> (stmt)))
@@ -1363,7 +1438,14 @@ analyze_stmt (modref_summary *summary, modref_summary_lto *summary_lto,
}
return true;
default:
- /* Nothing to do for other types of statements. */
+ if (cfun->can_throw_non_call_exceptions
+ && stmt_could_throw_p (cfun, stmt))
+ {
+ if (summary)
+ summary->side_effects = true;
+ if (summary_lto)
+ summary_lto->side_effects = true;
+ }
return true;
}
}
@@ -2606,6 +2688,7 @@ analyze_function (function *f, bool ipa)
param_modref_max_refs,
param_modref_max_accesses);
summary->writes_errno = false;
+ summary->side_effects = false;
}
if (lto)
{
@@ -2620,6 +2703,7 @@ analyze_function (function *f, bool ipa)
param_modref_max_refs,
param_modref_max_accesses);
summary_lto->writes_errno = false;
+ summary_lto->side_effects = false;
}
analyze_parms (summary, summary_lto, ipa,
@@ -2690,6 +2774,12 @@ 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);
@@ -2886,6 +2976,7 @@ modref_summaries::duplicate (cgraph_node *, cgraph_node *dst,
src_data->loads->max_accesses);
dst_data->loads->copy_from (src_data->loads);
dst_data->writes_errno = src_data->writes_errno;
+ dst_data->side_effects = src_data->side_effects;
if (src_data->arg_flags.length ())
dst_data->arg_flags = src_data->arg_flags.copy ();
dst_data->retslot_flags = src_data->retslot_flags;
@@ -2913,6 +3004,7 @@ modref_summaries_lto::duplicate (cgraph_node *, cgraph_node *,
src_data->loads->max_accesses);
dst_data->loads->copy_from (src_data->loads);
dst_data->writes_errno = src_data->writes_errno;
+ dst_data->side_effects = src_data->side_effects;
if (src_data->arg_flags.length ())
dst_data->arg_flags = src_data->arg_flags.copy ();
dst_data->retslot_flags = src_data->retslot_flags;
@@ -3241,6 +3333,7 @@ modref_write ()
struct bitpack_d bp = bitpack_create (ob->main_stream);
bp_pack_value (&bp, r->writes_errno, 1);
+ bp_pack_value (&bp, r->side_effects, 1);
if (!flag_wpa)
{
for (cgraph_edge *e = cnode->indirect_calls;
@@ -3310,9 +3403,15 @@ read_section (struct lto_file_decl_data *file_data, const char *data,
modref_sum = optimization_summaries->get_create (node);
if (modref_sum)
- modref_sum->writes_errno = false;
+ {
+ modref_sum->writes_errno = false;
+ modref_sum->side_effects = false;
+ }
if (modref_sum_lto)
- modref_sum_lto->writes_errno = false;
+ {
+ modref_sum_lto->writes_errno = false;
+ modref_sum_lto->side_effects = false;
+ }
gcc_assert (!modref_sum || (!modref_sum->loads
&& !modref_sum->stores));
@@ -3357,6 +3456,13 @@ read_section (struct lto_file_decl_data *file_data, const char *data,
if (modref_sum_lto)
modref_sum_lto->writes_errno = true;
}
+ if (bp_unpack_value (&bp, 1))
+ {
+ if (modref_sum)
+ modref_sum->side_effects = true;
+ if (modref_sum_lto)
+ modref_sum_lto->side_effects = true;
+ }
if (!flag_ltrans)
{
for (cgraph_edge *e = node->indirect_calls; e; e = e->next_callee)
@@ -3505,7 +3611,7 @@ update_signature (struct cgraph_node *node)
map.reserve (max + 1);
for (i = 0; i <= max; i++)
- map.quick_push (-1);
+ map.quick_push (MODREF_UNKNOWN_PARM);
FOR_EACH_VEC_SAFE_ELT (info->param_adjustments->m_adj_params, i, p)
{
int idx = info->param_adjustments->get_original_index (i);
@@ -3844,6 +3950,39 @@ propagate_unknown_call (cgraph_node *node,
bool changed = false;
class fnspec_summary *fnspec_sum = fnspec_summaries->get (e);
auto_vec <modref_parm_map, 32> parm_map;
+ bool looping;
+
+ if (e->callee
+ && builtin_safe_for_const_function_p (&looping, e->callee->decl))
+ {
+ if (cur_summary && !cur_summary->side_effects)
+ {
+ cur_summary->side_effects = true;
+ changed = true;
+ }
+ if (cur_summary_lto && !cur_summary_lto->side_effects)
+ {
+ cur_summary_lto->side_effects = true;
+ changed = true;
+ }
+ return changed;
+ }
+
+ if (!(ecf_flags & (ECF_CONST | ECF_NOVOPS))
+ || (ecf_flags & ECF_LOOPING_CONST_OR_PURE))
+ {
+ if (cur_summary && !cur_summary->side_effects)
+ {
+ cur_summary->side_effects = true;
+ changed = true;
+ }
+ if (cur_summary_lto && !cur_summary_lto->side_effects)
+ {
+ cur_summary_lto->side_effects = true;
+ changed = true;
+ }
+ }
+
if (fnspec_sum
&& compute_parm_map (e, &parm_map))
{
@@ -4108,6 +4247,12 @@ modref_propagate_in_scc (cgraph_node *component_node)
changed |= cur_summary->loads->merge
(callee_summary->loads, &parm_map,
&chain_map, !first);
+ if (!cur_summary->side_effects
+ && callee_summary->side_effects)
+ {
+ cur_summary->side_effects = true;
+ changed = true;
+ }
if (!ignore_stores)
{
changed |= cur_summary->stores->merge
@@ -4126,6 +4271,12 @@ modref_propagate_in_scc (cgraph_node *component_node)
changed |= cur_summary_lto->loads->merge
(callee_summary_lto->loads, &parm_map,
&chain_map, !first);
+ if (!cur_summary_lto->side_effects
+ && callee_summary_lto->side_effects)
+ {
+ cur_summary_lto->side_effects = true;
+ changed = true;
+ }
if (!ignore_stores)
{
changed |= cur_summary_lto->stores->merge
@@ -34,6 +34,7 @@ struct GTY(()) modref_summary
eaf_flags_t retslot_flags;
eaf_flags_t static_chain_flags;
bool writes_errno;
+ bool side_effects;
modref_summary ();
~modref_summary ();
@@ -506,11 +506,10 @@ worse_state (enum pure_const_state_e *state, bool *looping,
*looping = MAX (*looping, looping2);
}
-/* Recognize special cases of builtins that are by themselves not pure or const
+/* Recognize special cases of builtins that are by themselves not const
but function using them is. */
-static bool
-special_builtin_state (enum pure_const_state_e *state, bool *looping,
- tree callee)
+bool
+builtin_safe_for_const_function_p (bool *looping, tree callee)
{
if (DECL_BUILT_IN_CLASS (callee) == BUILT_IN_NORMAL)
switch (DECL_FUNCTION_CODE (callee))
@@ -532,11 +531,9 @@ special_builtin_state (enum pure_const_state_e *state, bool *looping,
case BUILT_IN_DWARF_CFA:
case BUILT_IN_RETURN_ADDRESS:
*looping = false;
- *state = IPA_CONST;
return true;
case BUILT_IN_PREFETCH:
*looping = true;
- *state = IPA_CONST;
return true;
default:
break;
@@ -594,17 +591,16 @@ check_call (funct_state local, gcall *call, bool ipa)
graph. */
if (callee_t)
{
- enum pure_const_state_e call_state;
bool call_looping;
if (gimple_call_builtin_p (call, BUILT_IN_NORMAL)
&& !nonfreeing_call_p (call))
local->can_free = true;
- if (special_builtin_state (&call_state, &call_looping, callee_t))
+ if (builtin_safe_for_const_function_p (&call_looping, callee_t))
{
worse_state (&local->pure_const_state, &local->looping,
- call_state, call_looping,
+ IPA_CONST, call_looping,
NULL, NULL);
return;
}
@@ -1007,6 +1003,51 @@ malloc_candidate_p (function *fun, bool ipa)
#undef DUMP_AND_RETURN
+/* Return true if function is known to be finite. */
+
+bool
+finite_function_p ()
+{
+ /* Const functions cannot have back edges (an
+ indication of possible infinite loop side
+ effect. */
+ bool finite = true;
+ if (mark_dfs_back_edges ())
+ {
+ /* Preheaders are needed for SCEV to work.
+ Simple latches and recorded exits improve chances that loop will
+ proved to be finite in testcases such as in loop-15.c
+ and loop-24.c */
+ loop_optimizer_init (LOOPS_HAVE_PREHEADERS
+ | LOOPS_HAVE_SIMPLE_LATCHES
+ | LOOPS_HAVE_RECORDED_EXITS);
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ flow_loops_dump (dump_file, NULL, 0);
+ if (mark_irreducible_loops ())
+ {
+ if (dump_file)
+ fprintf (dump_file, " has irreducible loops\n");
+ finite = false;
+ }
+ else
+ {
+ scev_initialize ();
+ for (auto loop : loops_list (cfun, 0))
+ if (!finite_loop_p (loop))
+ {
+ if (dump_file)
+ fprintf (dump_file, " cannot prove finiteness of "
+ "loop %i\n", loop->num);
+ finite =false;
+ break;
+ }
+ scev_finalize ();
+ }
+ loop_optimizer_finalize ();
+ }
+ return finite;
+}
+
/* This is the main routine for finding the reference patterns for
global variables within a function FN. */
@@ -1065,45 +1106,10 @@ analyze_function (struct cgraph_node *fn, bool ipa)
}
end:
- if (l->pure_const_state != IPA_NEITHER)
- {
- /* Const functions cannot have back edges (an
- indication of possible infinite loop side
- effect. */
- if (mark_dfs_back_edges ())
- {
- /* Preheaders are needed for SCEV to work.
- Simple latches and recorded exits improve chances that loop will
- proved to be finite in testcases such as in loop-15.c
- and loop-24.c */
- loop_optimizer_init (LOOPS_HAVE_PREHEADERS
- | LOOPS_HAVE_SIMPLE_LATCHES
- | LOOPS_HAVE_RECORDED_EXITS);
- if (dump_file && (dump_flags & TDF_DETAILS))
- flow_loops_dump (dump_file, NULL, 0);
- if (mark_irreducible_loops ())
- {
- if (dump_file)
- fprintf (dump_file, " has irreducible loops\n");
- l->looping = true;
- }
- else
- {
- scev_initialize ();
- for (auto loop : loops_list (cfun, 0))
- if (!finite_loop_p (loop))
- {
- if (dump_file)
- fprintf (dump_file, " cannot prove finiteness of "
- "loop %i\n", loop->num);
- l->looping =true;
- break;
- }
- scev_finalize ();
- }
- loop_optimizer_finalize ();
- }
- }
+ if (l->pure_const_state != IPA_NEITHER
+ && !l->looping
+ && !finite_function_p ())
+ l->looping = true;
if (dump_file && (dump_flags & TDF_DETAILS))
fprintf (dump_file, " checking previously known:");
@@ -1539,9 +1545,9 @@ propagate_pure_const (void)
edge_looping = y_l->looping;
}
}
- else if (special_builtin_state (&edge_state, &edge_looping,
- y->decl))
- ;
+ else if (builtin_safe_for_const_function_p (&edge_looping,
+ y->decl))
+ edge_state = IPA_CONST;
else
state_from_flags (&edge_state, &edge_looping,
flags_from_decl_or_type (y->decl),
@@ -47,6 +47,10 @@ void ipa_merge_profiles (struct cgraph_node *dst,
struct cgraph_node *src, bool preserve_body = false);
bool recursive_call_p (tree, tree);
+/* In ipa-pure-const.c */
+bool finite_function_p ();
+bool builtin_safe_for_const_function_p (bool *, tree);
+
/* In ipa-profile.c */
bool ipa_propagate_frequency (struct cgraph_node *node);