/* Callgraph based analysis of static variables.
- Copyright (C) 2004, 2005, 2007, 2008, 2009, 2010
- Free Software Foundation, Inc.
+ Copyright (C) 2004-2014 Free Software Foundation, Inc.
Contributed by Kenneth Zadeck <zadeck@naturalbridge.com>
This file is part of GCC.
#include "coretypes.h"
#include "tm.h"
#include "tree.h"
-#include "tree-flow.h"
+#include "print-tree.h"
+#include "calls.h"
+#include "basic-block.h"
+#include "tree-ssa-alias.h"
+#include "internal-fn.h"
+#include "tree-eh.h"
+#include "gimple-expr.h"
+#include "is-a.h"
+#include "gimple.h"
+#include "gimple-iterator.h"
+#include "gimple-walk.h"
+#include "tree-cfg.h"
+#include "tree-ssa-loop-niter.h"
#include "tree-inline.h"
#include "tree-pass.h"
#include "langhooks.h"
-#include "pointer-set.h"
-#include "ggc.h"
#include "ipa-utils.h"
-#include "gimple.h"
-#include "cgraph.h"
#include "flags.h"
#include "diagnostic.h"
#include "gimple-pretty-print.h"
/* Array, indexed by cgraph node uid, of function states. */
-DEF_VEC_P (funct_state);
-DEF_VEC_ALLOC_P (funct_state, heap);
-static VEC (funct_state, heap) *funct_state_vec;
+static vec<funct_state> funct_state_vec;
/* Holders of ipa cgraph hooks: */
static struct cgraph_node_hook_list *function_insertion_hook_holder;
known_finite, warned_about, "const");
}
-void
+static void
warn_function_noreturn (tree decl)
{
static struct pointer_set_t *warned_about;
true, warned_about, "noreturn");
}
-/* Init the function state. */
-
-static void
-finish_state (void)
-{
- free (funct_state_vec);
-}
-
-
/* Return true if we have a function state for NODE. */
static inline bool
has_function_state (struct cgraph_node *node)
{
- if (!funct_state_vec
- || VEC_length (funct_state, funct_state_vec) <= (unsigned int)node->uid)
+ if (!funct_state_vec.exists ()
+ || funct_state_vec.length () <= (unsigned int)node->uid)
return false;
- return VEC_index (funct_state, funct_state_vec, node->uid) != NULL;
+ return funct_state_vec[node->uid] != NULL;
}
/* Return the function state from NODE. */
static inline funct_state
get_function_state (struct cgraph_node *node)
{
- if (!funct_state_vec
- || VEC_length (funct_state, funct_state_vec) <= (unsigned int)node->uid
- || !VEC_index (funct_state, funct_state_vec, node->uid))
+ if (!funct_state_vec.exists ()
+ || funct_state_vec.length () <= (unsigned int)node->uid
+ || !funct_state_vec[node->uid])
/* We might want to put correct previously_known state into varying. */
return &varying_state;
- return VEC_index (funct_state, funct_state_vec, node->uid);
+ return funct_state_vec[node->uid];
}
/* Set the function state S for NODE. */
static inline void
set_function_state (struct cgraph_node *node, funct_state s)
{
- if (!funct_state_vec
- || VEC_length (funct_state, funct_state_vec) <= (unsigned int)node->uid)
- VEC_safe_grow_cleared (funct_state, heap, funct_state_vec, node->uid + 1);
- VEC_replace (funct_state, funct_state_vec, node->uid, s);
+ if (!funct_state_vec.exists ()
+ || funct_state_vec.length () <= (unsigned int)node->uid)
+ funct_state_vec.safe_grow_cleared (node->uid + 1);
+ funct_state_vec[node->uid] = s;
}
/* Check to see if the use (or definition when CHECKING_WRITE is true)
*looping = looping2;
else
*looping = MIN (*looping, looping2);
+ *state = state2;
}
else if (state2 != IPA_NEITHER)
*looping = MIN (*looping, looping2);
}
/* When not in IPA mode, we can still handle self recursion. */
- if (!ipa && callee_t == current_function_decl)
+ if (!ipa && callee_t
+ && recursive_call_p (current_function_decl, callee_t))
{
if (dump_file)
fprintf (dump_file, " Recursive call can loop.\n");
/* Wrapper around check_decl for loads in local more. */
static bool
-check_load (gimple stmt ATTRIBUTE_UNUSED, tree op, void *data)
+check_load (gimple, tree op, tree, void *data)
{
if (DECL_P (op))
check_decl ((funct_state)data, op, false, false);
/* Wrapper around check_decl for stores in local more. */
static bool
-check_store (gimple stmt ATTRIBUTE_UNUSED, tree op, void *data)
+check_store (gimple, tree op, tree, void *data)
{
if (DECL_P (op))
check_decl ((funct_state)data, op, true, false);
/* Wrapper around check_decl for loads in ipa mode. */
static bool
-check_ipa_load (gimple stmt ATTRIBUTE_UNUSED, tree op, void *data)
+check_ipa_load (gimple, tree op, tree, void *data)
{
if (DECL_P (op))
check_decl ((funct_state)data, op, false, true);
/* Wrapper around check_decl for stores in ipa mode. */
static bool
-check_ipa_store (gimple stmt ATTRIBUTE_UNUSED, tree op, void *data)
+check_ipa_store (gimple, tree op, tree, void *data)
{
if (DECL_P (op))
check_decl ((funct_state)data, op, true, true);
if (cfun->can_throw_non_call_exceptions)
{
if (dump_file)
- fprintf (dump_file, " can throw; looping");
+ fprintf (dump_file, " can throw; looping\n");
local->looping = true;
}
if (stmt_can_throw_external (stmt))
{
if (dump_file)
- fprintf (dump_file, " can throw externally");
+ fprintf (dump_file, " can throw externally\n");
local->can_throw = true;
}
+ else
+ if (dump_file)
+ fprintf (dump_file, " can throw\n");
}
switch (gimple_code (stmt))
{
/* Target of long jump. */
{
if (dump_file)
- fprintf (dump_file, " nonlocal label is not const/pure");
+ fprintf (dump_file, " nonlocal label is not const/pure\n");
local->pure_const_state = IPA_NEITHER;
}
break;
if (gimple_asm_clobbers_memory_p (stmt))
{
if (dump_file)
- fprintf (dump_file, " memory asm clobber is not const/pure");
+ fprintf (dump_file, " memory asm clobber is not const/pure\n");
/* Abandon all hope, ye who enter here. */
local->pure_const_state = IPA_NEITHER;
}
if (gimple_asm_volatile_p (stmt))
{
if (dump_file)
- fprintf (dump_file, " volatile is not const/pure");
+ fprintf (dump_file, " volatile is not const/pure\n");
/* Abandon all hope, ye who enter here. */
local->pure_const_state = IPA_NEITHER;
local->looping = true;
static funct_state
analyze_function (struct cgraph_node *fn, bool ipa)
{
- tree decl = fn->symbol.decl;
- tree old_decl = current_function_decl;
+ tree decl = fn->decl;
funct_state l;
basic_block this_block;
l->looping = false;
l->can_throw = false;
state_from_flags (&l->state_previously_known, &l->looping_previously_known,
- flags_from_decl_or_type (fn->symbol.decl),
+ flags_from_decl_or_type (fn->decl),
cgraph_node_cannot_return (fn));
if (fn->thunk.thunk_p || fn->alias)
if (dump_file)
{
fprintf (dump_file, "\n\n local analysis of %s\n ",
- cgraph_node_name (fn));
+ fn->name ());
}
push_cfun (DECL_STRUCT_FUNCTION (decl));
- current_function_decl = decl;
- FOR_EACH_BB (this_block)
+ FOR_EACH_BB_FN (this_block, cfun)
{
gimple_stmt_iterator gsi;
struct walk_stmt_info wi;
- memset (&wi, 0, sizeof(wi));
+ memset (&wi, 0, sizeof (wi));
for (gsi = gsi_start_bb (this_block);
!gsi_end_p (gsi);
gsi_next (&gsi))
{
/* 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_NORMAL
+ 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);
}
else
{
- loop_iterator li;
struct loop *loop;
scev_initialize ();
- FOR_EACH_LOOP (li, loop, 0)
+ FOR_EACH_LOOP (loop, 0)
if (!finite_loop_p (loop))
{
if (dump_file)
- fprintf (dump_file, " can not prove finiteness of loop %i\n", loop->num);
+ fprintf (dump_file, " can not prove finiteness of "
+ "loop %i\n", loop->num);
l->looping =true;
- FOR_EACH_LOOP_BREAK (li);
+ break;
}
scev_finalize ();
}
l->can_throw = false;
pop_cfun ();
- current_function_decl = old_decl;
if (dump_file)
{
if (l->looping)
CONST. */
static void
-generate_summary (void)
+pure_const_generate_summary (void)
{
struct cgraph_node *node;
lsei_next_function_in_partition (&lsei))
{
node = lsei_cgraph_node (lsei);
- if (node->analyzed && has_function_state (node))
+ if (node->definition && has_function_state (node))
count++;
}
lsei_next_function_in_partition (&lsei))
{
node = lsei_cgraph_node (lsei);
- if (node->analyzed && has_function_state (node))
+ if (node->definition && has_function_state (node))
{
struct bitpack_d bp;
funct_state fs;
fs = get_function_state (node);
encoder = ob->decl_state->symtab_node_encoder;
- node_ref = lto_symtab_encoder_encode (encoder, (symtab_node)node);
+ node_ref = lto_symtab_encoder_encode (encoder, node);
streamer_write_uhwi_stream (ob->main_stream, node_ref);
/* Note that flags will need to be read in the opposite
fs->can_throw = bp_unpack_value (&bp, 1);
if (dump_file)
{
- int flags = flags_from_decl_or_type (node->symbol.decl);
+ int flags = flags_from_decl_or_type (node->decl);
fprintf (dump_file, "Read info for %s/%i ",
- cgraph_node_name (node),
- node->uid);
+ node->name (),
+ node->order);
if (flags & ECF_CONST)
fprintf (dump_file, " const");
if (flags & ECF_PURE)
}
/* Return true if NODE is self recursive function.
- ??? self recursive and indirectly recursive funcions should
- be the same, so this function seems unnecesary. */
+ Indirectly recursive functions appears as non-trivial strongly
+ connected components, so we need to care about self recursion
+ only. */
static bool
self_recursive_p (struct cgraph_node *node)
if (dump_file)
{
dump_cgraph (dump_file);
- ipa_print_order(dump_file, "reduced", order, order_pos);
+ ipa_print_order (dump_file, "reduced", order, order_pos);
}
/* Propagate the local information through the call graph to produce
funct_state w_l = get_function_state (w);
if (dump_file && (dump_flags & TDF_DETAILS))
fprintf (dump_file, " Visiting %s/%i state:%s looping %i\n",
- cgraph_node_name (w),
- w->uid,
+ w->name (),
+ w->order,
pure_const_names[w_l->pure_const_state],
w_l->looping);
{
fprintf (dump_file,
" Call to %s/%i",
- cgraph_node_name (e->callee),
- e->callee->uid);
+ e->callee->name (),
+ e->callee->order);
}
if (avail > AVAIL_OVERWRITABLE)
{
}
}
else if (special_builtin_state (&edge_state, &edge_looping,
- y->symbol.decl))
+ y->decl))
;
else
state_from_flags (&edge_state, &edge_looping,
- flags_from_decl_or_type (y->symbol.decl),
+ flags_from_decl_or_type (y->decl),
cgraph_edge_cannot_lead_to_return (e));
/* Merge the results with what we already know. */
break;
/* And finally all loads and stores. */
- for (i = 0; ipa_ref_list_reference_iterate (&w->symbol.ref_list, i, ref); i++)
+ for (i = 0; ipa_ref_list_reference_iterate (&w->ref_list, i, ref); i++)
{
enum pure_const_state_e ref_state = IPA_CONST;
bool ref_looping = false;
{
case IPA_REF_LOAD:
/* readonly reads are safe. */
- if (TREE_READONLY (ipa_ref_varpool_node (ref)->symbol.decl))
+ if (TREE_READONLY (ipa_ref_varpool_node (ref)->decl))
break;
if (dump_file && (dump_flags & TDF_DETAILS))
fprintf (dump_file, " nonreadonly global var read\n");
if (pure_const_state == IPA_NEITHER)
break;
}
- w_info = (struct ipa_dfs_info *) w->symbol.aux;
+ w_info = (struct ipa_dfs_info *) w->aux;
w = w_info->next_cycle;
}
if (dump_file && (dump_flags & TDF_DETAILS))
w_l->pure_const_state = this_state;
w_l->looping = this_looping;
- switch (this_state)
- {
- case IPA_CONST:
- if (!TREE_READONLY (w->symbol.decl))
- {
- warn_function_const (w->symbol.decl, !this_looping);
- if (dump_file)
- fprintf (dump_file, "Function found to be %sconst: %s\n",
- this_looping ? "looping " : "",
- cgraph_node_name (w));
- }
- cgraph_set_const_flag (w, true, this_looping);
- break;
+ /* Inline clones share declaration with their offline copies;
+ do not modify their declarations since the offline copy may
+ be different. */
+ if (!w->global.inlined_to)
+ 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->name ());
+ }
+ cgraph_set_const_flag (w, true, this_looping);
+ break;
- case IPA_PURE:
- if (!DECL_PURE_P (w->symbol.decl))
- {
- warn_function_pure (w->symbol.decl, !this_looping);
- if (dump_file)
- fprintf (dump_file, "Function found to be %spure: %s\n",
- this_looping ? "looping " : "",
- cgraph_node_name (w));
- }
- cgraph_set_pure_flag (w, true, this_looping);
- 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->name ());
+ }
+ cgraph_set_pure_flag (w, true, this_looping);
+ break;
- default:
- break;
- }
- w_info = (struct ipa_dfs_info *) w->symbol.aux;
+ default:
+ break;
+ }
+ w_info = (struct ipa_dfs_info *) w->aux;
w = w_info->next_cycle;
}
}
if (can_throw)
break;
- if (y_l->can_throw && !TREE_NOTHROW (w->symbol.decl)
+ if (y_l->can_throw && !TREE_NOTHROW (w->decl)
&& e->can_throw_external)
can_throw = true;
}
- else if (e->can_throw_external && !TREE_NOTHROW (y->symbol.decl))
+ else if (e->can_throw_external && !TREE_NOTHROW (y->decl))
can_throw = true;
}
for (ie = node->indirect_calls; ie; ie = ie->next_callee)
if (ie->can_throw_external)
- can_throw = true;
- w_info = (struct ipa_dfs_info *) w->symbol.aux;
+ {
+ can_throw = true;
+ break;
+ }
+ w_info = (struct ipa_dfs_info *) w->aux;
w = w_info->next_cycle;
}
while (w)
{
funct_state w_l = get_function_state (w);
- if (!can_throw && !TREE_NOTHROW (w->symbol.decl))
+ if (!can_throw && !TREE_NOTHROW (w->decl))
{
- cgraph_set_nothrow_flag (w, true);
- if (dump_file)
- fprintf (dump_file, "Function found to be nothrow: %s\n",
- cgraph_node_name (w));
+ /* Inline clones share declaration with their offline copies;
+ do not modify their declarations since the offline copy may
+ be different. */
+ if (!w->global.inlined_to)
+ {
+ cgraph_set_nothrow_flag (w, true);
+ if (dump_file)
+ fprintf (dump_file, "Function found to be nothrow: %s\n",
+ w->name ());
+ }
}
- else if (can_throw && !TREE_NOTHROW (w->symbol.decl))
+ else if (can_throw && !TREE_NOTHROW (w->decl))
w_l->can_throw = true;
- w_info = (struct ipa_dfs_info *) w->symbol.aux;
+ w_info = (struct ipa_dfs_info *) w->aux;
w = w_info->next_cycle;
}
}
propagate_pure_const ();
/* Cleanup. */
- FOR_EACH_DEFINED_FUNCTION (node)
+ FOR_EACH_FUNCTION (node)
if (has_function_state (node))
free (get_function_state (node));
- VEC_free (funct_state, heap, funct_state_vec);
- finish_state ();
+ funct_state_vec.release ();
return 0;
}
&& !seen_error ());
}
-struct ipa_opt_pass_d pass_ipa_pure_const =
+namespace {
+
+const pass_data pass_data_ipa_pure_const =
{
- {
- IPA_PASS,
- "pure-const", /* name */
- gate_pure_const, /* gate */
- propagate, /* execute */
- NULL, /* sub */
- NULL, /* next */
- 0, /* static_pass_number */
- TV_IPA_PURE_CONST, /* tv_id */
- 0, /* properties_required */
- 0, /* properties_provided */
- 0, /* properties_destroyed */
- 0, /* todo_flags_start */
- 0 /* todo_flags_finish */
- },
- generate_summary, /* generate_summary */
- pure_const_write_summary, /* write_summary */
- pure_const_read_summary, /* read_summary */
- NULL, /* write_optimization_summary */
- NULL, /* read_optimization_summary */
- NULL, /* stmt_fixup */
- 0, /* TODOs */
- NULL, /* function_transform */
- NULL /* variable_transform */
+ IPA_PASS, /* type */
+ "pure-const", /* name */
+ OPTGROUP_NONE, /* optinfo_flags */
+ true, /* has_gate */
+ true, /* has_execute */
+ TV_IPA_PURE_CONST, /* tv_id */
+ 0, /* properties_required */
+ 0, /* properties_provided */
+ 0, /* properties_destroyed */
+ 0, /* todo_flags_start */
+ 0, /* todo_flags_finish */
};
+class pass_ipa_pure_const : public ipa_opt_pass_d
+{
+public:
+ pass_ipa_pure_const (gcc::context *ctxt)
+ : ipa_opt_pass_d (pass_data_ipa_pure_const, ctxt,
+ pure_const_generate_summary, /* generate_summary */
+ pure_const_write_summary, /* write_summary */
+ pure_const_read_summary, /* read_summary */
+ NULL, /* write_optimization_summary */
+ NULL, /* read_optimization_summary */
+ NULL, /* stmt_fixup */
+ 0, /* function_transform_todo_flags_start */
+ NULL, /* function_transform */
+ NULL) /* variable_transform */
+ {}
+
+ /* opt_pass methods: */
+ bool gate () { return gate_pure_const (); }
+ unsigned int execute () { return propagate (); }
+
+}; // class pass_ipa_pure_const
+
+} // anon namespace
+
+ipa_opt_pass_d *
+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
/* Do NORETURN discovery. */
if (!skip && !TREE_THIS_VOLATILE (current_function_decl)
- && EDGE_COUNT (EXIT_BLOCK_PTR->preds) == 0)
+ && EDGE_COUNT (EXIT_BLOCK_PTR_FOR_FN (cfun)->preds) == 0)
{
warn_function_noreturn (cfun->decl);
if (dump_file)
return 0;
}
-struct gimple_opt_pass pass_local_pure_const =
+namespace {
+
+const pass_data pass_data_local_pure_const =
+{
+ GIMPLE_PASS, /* type */
+ "local-pure-const", /* name */
+ OPTGROUP_NONE, /* optinfo_flags */
+ true, /* has_gate */
+ true, /* has_execute */
+ TV_IPA_PURE_CONST, /* tv_id */
+ 0, /* properties_required */
+ 0, /* properties_provided */
+ 0, /* properties_destroyed */
+ 0, /* todo_flags_start */
+ 0, /* todo_flags_finish */
+};
+
+class pass_local_pure_const : public gimple_opt_pass
+{
+public:
+ pass_local_pure_const (gcc::context *ctxt)
+ : gimple_opt_pass (pass_data_local_pure_const, ctxt)
+ {}
+
+ /* opt_pass methods: */
+ opt_pass * clone () { return new pass_local_pure_const (m_ctxt); }
+ bool gate () { return gate_pure_const (); }
+ unsigned int execute () { return local_pure_const (); }
+
+}; // class pass_local_pure_const
+
+} // anon namespace
+
+gimple_opt_pass *
+make_pass_local_pure_const (gcc::context *ctxt)
+{
+ return new pass_local_pure_const (ctxt);
+}
+
+/* Emit noreturn warnings. */
+
+static unsigned int
+execute_warn_function_noreturn (void)
+{
+ if (!TREE_THIS_VOLATILE (current_function_decl)
+ && EDGE_COUNT (EXIT_BLOCK_PTR_FOR_FN (cfun)->preds) == 0)
+ warn_function_noreturn (current_function_decl);
+ return 0;
+}
+
+static bool
+gate_warn_function_noreturn (void)
+{
+ return warn_suggest_attribute_noreturn;
+}
+
+namespace {
+
+const pass_data pass_data_warn_function_noreturn =
{
- {
- GIMPLE_PASS,
- "local-pure-const", /* name */
- gate_pure_const, /* gate */
- local_pure_const, /* execute */
- NULL, /* sub */
- NULL, /* next */
- 0, /* static_pass_number */
- TV_IPA_PURE_CONST, /* tv_id */
- 0, /* properties_required */
- 0, /* properties_provided */
- 0, /* properties_destroyed */
- 0, /* todo_flags_start */
- 0 /* todo_flags_finish */
- }
+ GIMPLE_PASS, /* type */
+ "*warn_function_noreturn", /* name */
+ OPTGROUP_NONE, /* optinfo_flags */
+ true, /* has_gate */
+ true, /* has_execute */
+ TV_NONE, /* tv_id */
+ PROP_cfg, /* properties_required */
+ 0, /* properties_provided */
+ 0, /* properties_destroyed */
+ 0, /* todo_flags_start */
+ 0, /* todo_flags_finish */
};
+
+class pass_warn_function_noreturn : public gimple_opt_pass
+{
+public:
+ pass_warn_function_noreturn (gcc::context *ctxt)
+ : gimple_opt_pass (pass_data_warn_function_noreturn, ctxt)
+ {}
+
+ /* opt_pass methods: */
+ bool gate () { return gate_warn_function_noreturn (); }
+ unsigned int execute () { return execute_warn_function_noreturn (); }
+
+}; // class pass_warn_function_noreturn
+
+} // anon namespace
+
+gimple_opt_pass *
+make_pass_warn_function_noreturn (gcc::context *ctxt)
+{
+ return new pass_warn_function_noreturn (ctxt);
+}
+
+