You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING. If not, write to the Free
-Software Foundation, 59 Temple Place - Suite 330, Boston, MA
-02111-1307, USA. */
+Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301, USA. */
/* This module implements main driver of compilation process as well as
few basic intraprocedural optimizers.
#include "c-common.h"
#include "intl.h"
#include "function.h"
+#include "ipa-prop.h"
#include "tree-gimple.h"
#include "tree-pass.h"
#include "output.h"
decide_is_function_needed (struct cgraph_node *node, tree decl)
{
tree origin;
+ if (MAIN_NAME_P (DECL_NAME (decl))
+ && TREE_PUBLIC (decl))
+ {
+ node->local.externally_visible = true;
+ return true;
+ }
/* If the user told us it is used, then it must be so. */
- if (lookup_attribute ("used", DECL_ATTRIBUTES (decl)))
+ if (node->local.externally_visible
+ || lookup_attribute ("used", DECL_ATTRIBUTES (decl)))
return true;
/* ??? If the assembler name is set by hand, it is possible to assemble
/* Externally visible functions must be output. The exception is
COMDAT functions that must be output only when they are needed. */
- if (TREE_PUBLIC (decl) && !DECL_COMDAT (decl) && !DECL_EXTERNAL (decl))
+ if ((TREE_PUBLIC (decl) && !flag_whole_program)
+ && !DECL_COMDAT (decl) && !DECL_EXTERNAL (decl))
return true;
/* Constructors and destructors are reachable from the runtime by
/* When declared inline, defer even the uninlinable functions.
This allows them to be eliminated when unused. */
&& !DECL_DECLARED_INLINE_P (decl)
- && (!node->local.inlinable || !cgraph_default_inline_p (node))))
+ && (!node->local.inlinable || !cgraph_default_inline_p (node, NULL))))
return true;
return false;
&& ((DECL_ASSEMBLER_NAME_SET_P (decl)
&& TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (decl)))
|| node->force_output
- || decide_is_variable_needed (node, decl)))
+ || decide_is_variable_needed (node, decl)
+ /* ??? Cgraph does not yet rule the world with an iron hand,
+ and does not control the emission of debug information.
+ After a variable has its DECL_RTL set, we must assume that
+ it may be referenced by the debug information, and we can
+ no longer elide it. */
+ || DECL_RTL_SET_P (decl)))
cgraph_varpool_mark_needed_node (node);
node = next;
}
+ /* Make sure we mark alias targets as used targets. */
+ finish_aliases_1 ();
cgraph_varpool_analyze_pending_decls ();
}
if (!flag_unit_at_a_time)
{
cgraph_analyze_function (node);
- cgraph_decide_inlining_incrementally (node);
+ cgraph_decide_inlining_incrementally (node, false);
}
if (decide_is_function_needed (node, decl))
/* Since we reclaim unreachable nodes at the end of every language
level unit, we need to be conservative about possible entry points
there. */
- if (TREE_PUBLIC (decl) && !DECL_COMDAT (decl) && !DECL_EXTERNAL (decl))
+ if ((TREE_PUBLIC (decl) && !DECL_COMDAT (decl) && !DECL_EXTERNAL (decl)))
cgraph_mark_reachable_node (node);
/* If not unit at a time, go ahead and emit everything we've found
walk_tree (bsi_stmt_ptr (bsi), record_reference, node, visited_nodes);
}
- /* Walk over any private statics that may take addresses of functions. */
- if (TREE_CODE (DECL_INITIAL (body)) == BLOCK)
+ /* Look for initializers of constant variables and private statics. */
+ for (step = DECL_STRUCT_FUNCTION (body)->unexpanded_var_list;
+ step;
+ step = TREE_CHAIN (step))
{
- for (step = BLOCK_VARS (DECL_INITIAL (body));
- step;
- step = TREE_CHAIN (step))
- if (DECL_INITIAL (step))
- walk_tree (&DECL_INITIAL (step), record_reference, node, visited_nodes);
+ tree decl = TREE_VALUE (step);
+ if (TREE_CODE (decl) == VAR_DECL
+ && (TREE_STATIC (decl) && !DECL_EXTERNAL (decl))
+ && flag_unit_at_a_time)
+ cgraph_varpool_finalize_decl (decl);
+ else if (TREE_CODE (decl) == VAR_DECL && DECL_INITIAL (decl))
+ walk_tree (&DECL_INITIAL (decl), record_reference, node, visited_nodes);
}
-
- /* Also look here for private statics. */
- if (DECL_STRUCT_FUNCTION (body))
- for (step = DECL_STRUCT_FUNCTION (body)->unexpanded_var_list;
- step;
- step = TREE_CHAIN (step))
- {
- tree decl = TREE_VALUE (step);
- if (DECL_INITIAL (decl) && TREE_STATIC (decl))
- walk_tree (&DECL_INITIAL (decl), record_reference, node, visited_nodes);
- }
pointer_set_destroy (visited_nodes);
visited_nodes = NULL;
}
+/* Give initial reasons why inlining would fail. Those gets
+ either NULLified or usually overwritten by more precise reason
+ later. */
+static void
+initialize_inline_failed (struct cgraph_node *node)
+{
+ struct cgraph_edge *e;
+
+ for (e = node->callers; e; e = e->next_caller)
+ {
+ gcc_assert (!e->callee->global.inlined_to);
+ gcc_assert (e->inline_failed);
+ if (node->local.redefined_extern_inline)
+ e->inline_failed = N_("redefined extern inline functions are not "
+ "considered for inlining");
+ else if (!node->local.inlinable)
+ e->inline_failed = N_("function not inlinable");
+ else
+ e->inline_failed = N_("function not considered for inlining");
+ }
+}
+
+/* Rebuild call edges from current function after a passes not aware
+ of cgraph updating. */
+static void
+rebuild_cgraph_edges (void)
+{
+ basic_block bb;
+ struct cgraph_node *node = cgraph_node (current_function_decl);
+ block_stmt_iterator bsi;
+
+ cgraph_node_remove_callees (node);
+
+ node->count = ENTRY_BLOCK_PTR->count;
+
+ FOR_EACH_BB (bb)
+ for (bsi = bsi_start (bb); !bsi_end_p (bsi); bsi_next (&bsi))
+ {
+ tree stmt = bsi_stmt (bsi);
+ tree call = get_call_expr_in (stmt);
+ tree decl;
+
+ if (call && (decl = get_callee_fndecl (call)))
+ cgraph_create_edge (node, cgraph_node (decl), stmt,
+ bb->count,
+ bb->loop_depth);
+ }
+ initialize_inline_failed (node);
+ gcc_assert (!node->global.inlined_to);
+}
+
+struct tree_opt_pass pass_rebuild_cgraph_edges =
+{
+ NULL, /* name */
+ NULL, /* gate */
+ rebuild_cgraph_edges, /* execute */
+ NULL, /* sub */
+ NULL, /* next */
+ 0, /* static_pass_number */
+ 0, /* tv_id */
+ PROP_cfg, /* properties_required */
+ 0, /* properties_provided */
+ 0, /* properties_destroyed */
+ 0, /* todo_flags_start */
+ 0, /* todo_flags_finish */
+ 0 /* letter */
+};
/* Verify cgraph nodes of given cgraph node. */
void
for (e = node->callees; e; e = e->next_callee)
if (e->aux)
{
- error ("Aux field set for edge %s->%s",
+ error ("aux field set for edge %s->%s",
cgraph_node_name (e->caller), cgraph_node_name (e->callee));
error_found = true;
}
+ if (node->count < 0)
+ {
+ error ("Execution count is negative");
+ error_found = true;
+ }
for (e = node->callers; e; e = e->next_caller)
{
+ if (e->count < 0)
+ {
+ error ("caller edge count is negative");
+ error_found = true;
+ }
if (!e->inline_failed)
{
if (node->global.inlined_to
!= (e->caller->global.inlined_to
? e->caller->global.inlined_to : e->caller))
{
- error ("Inlined_to pointer is wrong");
+ error ("inlined_to pointer is wrong");
error_found = true;
}
if (node->callers->next_caller)
{
- error ("Multiple inline callers");
+ error ("multiple inline callers");
error_found = true;
}
}
else
if (node->global.inlined_to)
{
- error ("Inlined_to pointer set for noninline callers");
+ error ("inlined_to pointer set for noninline callers");
error_found = true;
}
}
if (!node->callers && node->global.inlined_to)
{
- error ("Inlined_to pointer is set but no predecesors found");
+ error ("inlined_to pointer is set but no predecesors found");
error_found = true;
}
if (node->global.inlined_to == node)
{
- error ("Inlined_to pointer refers to itself");
+ error ("inlined_to pointer refers to itself");
error_found = true;
}
break;
if (!node)
{
- error ("Node not found in DECL_ASSEMBLER_NAME hash");
+ error ("node not found in DECL_ASSEMBLER_NAME hash");
error_found = true;
}
{
if (e->aux)
{
- error ("Shared call_stmt:");
+ error ("shared call_stmt:");
debug_generic_stmt (stmt);
error_found = true;
}
if (e->callee->decl != cgraph_node (decl)->decl)
{
- error ("Edge points to wrong declaration:");
+ error ("edge points to wrong declaration:");
debug_tree (e->callee->decl);
fprintf (stderr," Instead of:");
debug_tree (decl);
}
else
{
- error ("Missing callgraph edge for call stmt:");
+ error ("missing callgraph edge for call stmt:");
debug_generic_stmt (stmt);
error_found = true;
}
{
if (!e->aux)
{
- error ("Edge %s->%s has no corresponding call_stmt",
+ error ("edge %s->%s has no corresponding call_stmt",
cgraph_node_name (e->caller),
cgraph_node_name (e->callee));
debug_generic_stmt (e->call_stmt);
if (error_found)
{
dump_cgraph_node (stderr, node);
- internal_error ("verify_cgraph_node failed.");
+ internal_error ("verify_cgraph_node failed");
}
timevar_pop (TV_CGRAPH_VERIFY);
}
if (!TREE_ASM_WRITTEN (decl) && !node->alias && !DECL_EXTERNAL (decl))
{
assemble_variable (decl, 0, 1, 0);
+ /* Local static variables are never seen by check_global_declarations
+ so we need to output debug info by hand. */
+ if (DECL_CONTEXT (decl)
+ && (TREE_CODE (DECL_CONTEXT (decl)) == BLOCK
+ || TREE_CODE (DECL_CONTEXT (decl)) == FUNCTION_DECL)
+ && errorcount == 0 && sorrycount == 0)
+ {
+ timevar_push (TV_SYMOUT);
+ (*debug_hooks->global_decl) (decl);
+ timevar_pop (TV_SYMOUT);
+ }
changed = true;
}
node->next_needed = NULL;
cgraph_analyze_function (struct cgraph_node *node)
{
tree decl = node->decl;
- struct cgraph_edge *e;
current_function_decl = decl;
push_cfun (DECL_STRUCT_FUNCTION (decl));
if (node->local.inlinable)
node->local.disregard_inline_limits
= lang_hooks.tree_inlining.disregard_inline_limits (decl);
- for (e = node->callers; e; e = e->next_caller)
- {
- if (node->local.redefined_extern_inline)
- e->inline_failed = N_("redefined extern inline functions are not "
- "considered for inlining");
- else if (!node->local.inlinable)
- e->inline_failed = N_("function not inlinable");
- else
- e->inline_failed = N_("function not considered for inlining");
- }
+ initialize_inline_failed (node);
if (flag_really_no_inline && !node->local.disregard_inline_limits)
node->local.inlinable = 0;
/* Inlining characteristics are maintained by the cgraph_mark_inline. */
{
if (node->reachable
&& (DECL_COMDAT (node->decl)
- || (TREE_PUBLIC (node->decl) && !DECL_EXTERNAL (node->decl))))
- node->local.externally_visible = 1;
+ || (!flag_whole_program
+ && TREE_PUBLIC (node->decl) && !DECL_EXTERNAL (node->decl))))
+ node->local.externally_visible = true;
+ if (!node->local.externally_visible && node->analyzed
+ && !DECL_EXTERNAL (node->decl))
+ {
+ gcc_assert (flag_whole_program || !TREE_PUBLIC (node->decl));
+ TREE_PUBLIC (node->decl) = 0;
+ }
node->local.local = (!node->needed
&& node->analyzed
&& !DECL_EXTERNAL (node->decl)
for (vnode = cgraph_varpool_nodes_queue; vnode; vnode = vnode->next_needed)
{
if (vnode->needed
+ && !flag_whole_program
&& (DECL_COMDAT (vnode->decl) || TREE_PUBLIC (vnode->decl)))
vnode->externally_visible = 1;
+ if (!vnode->externally_visible)
+ {
+ gcc_assert (flag_whole_program || !TREE_PUBLIC (vnode->decl));
+ TREE_PUBLIC (vnode->decl) = 0;
+ }
gcc_assert (TREE_STATIC (vnode->decl));
}
return false;
}
+static void
+ipa_passes (void)
+{
+ cfun = NULL;
+ tree_register_cfg_hooks ();
+ bitmap_obstack_initialize (NULL);
+ execute_ipa_pass_list (all_ipa_passes);
+ bitmap_obstack_release (NULL);
+}
+
/* Perform simple optimizations based on callgraph. */
void
dump_cgraph_node (stderr, node);
}
if (error_found)
- internal_error ("Nodes with no released memory found.");
+ internal_error ("nodes with no released memory found");
}
#endif
}
{
cgraph_dump_file = dump_begin (TDI_cgraph, NULL);
}
+
+/* The edges representing the callers of the NEW_VERSION node were
+ fixed by cgraph_function_versioning (), now the call_expr in their
+ respective tree code should be updated to call the NEW_VERSION. */
+
+static void
+update_call_expr (struct cgraph_node *new_version)
+{
+ struct cgraph_edge *e;
+
+ gcc_assert (new_version);
+ for (e = new_version->callers; e; e = e->next_caller)
+ /* Update the call expr on the edges
+ to call the new version. */
+ TREE_OPERAND (TREE_OPERAND (get_call_expr_in (e->call_stmt), 0), 0) = new_version->decl;
+}
+
+
+/* Create a new cgraph node which is the new version of
+ OLD_VERSION node. REDIRECT_CALLERS holds the callers
+ edges which should be redirected to point to
+ NEW_VERSION. ALL the callees edges of OLD_VERSION
+ are cloned to the new version node. Return the new
+ version node. */
+
+static struct cgraph_node *
+cgraph_copy_node_for_versioning (struct cgraph_node *old_version,
+ tree new_decl, varray_type redirect_callers)
+ {
+ struct cgraph_node *new_version;
+ struct cgraph_edge *e, *new_e;
+ struct cgraph_edge *next_callee;
+ unsigned i;
+
+ gcc_assert (old_version);
+
+ new_version = cgraph_node (new_decl);
+
+ new_version->analyzed = true;
+ new_version->local = old_version->local;
+ new_version->global = old_version->global;
+ new_version->rtl = new_version->rtl;
+ new_version->reachable = true;
+ new_version->count = old_version->count;
+
+ /* Clone the old node callees. Recursive calls are
+ also cloned. */
+ for (e = old_version->callees;e; e=e->next_callee)
+ {
+ new_e = cgraph_clone_edge (e, new_version, e->call_stmt, 0, e->loop_nest, true);
+ new_e->count = e->count;
+ }
+ /* Fix recursive calls.
+ If OLD_VERSION has a recursive call after the
+ previous edge cloning, the new version will have an edge
+ pointing to the old version, which is wrong;
+ Redirect it to point to the new version. */
+ for (e = new_version->callees ; e; e = next_callee)
+ {
+ next_callee = e->next_callee;
+ if (e->callee == old_version)
+ cgraph_redirect_edge_callee (e, new_version);
+
+ if (!next_callee)
+ break;
+ }
+ if (redirect_callers)
+ for (i = 0; i < VARRAY_ACTIVE_SIZE (redirect_callers); i++)
+ {
+ e = VARRAY_GENERIC_PTR (redirect_callers, i);
+ /* Redirect calls to the old version node
+ to point to it's new version. */
+ cgraph_redirect_edge_callee (e, new_version);
+ }
+
+ return new_version;
+ }
+
+ /* Perform function versioning.
+ Function versioning includes copying of the tree and
+ a callgraph update (creating a new cgraph node and updating
+ its callees and callers).
+
+ REDIRECT_CALLERS varray includes the edges to be redirected
+ to the new version.
+
+ TREE_MAP is a mapping of tree nodes we want to replace with
+ new ones (according to results of prior analysis).
+ OLD_VERSION_NODE is the node that is versioned.
+ It returns the new version's cgraph node. */
+
+struct cgraph_node *
+cgraph_function_versioning (struct cgraph_node *old_version_node,
+ varray_type redirect_callers,
+ varray_type tree_map)
+{
+ tree old_decl = old_version_node->decl;
+ struct cgraph_node *new_version_node = NULL;
+ tree new_decl;
+
+ if (!tree_versionable_function_p (old_decl))
+ return NULL;
+
+ /* Make a new FUNCTION_DECL tree node for the
+ new version. */
+ new_decl = copy_node (old_decl);
+
+ /* Create the new version's call-graph node.
+ and update the edges of the new node. */
+ new_version_node =
+ cgraph_copy_node_for_versioning (old_version_node, new_decl,
+ redirect_callers);
+
+ /* Copy the OLD_VERSION_NODE function tree to the new version. */
+ tree_function_versioning (old_decl, new_decl, tree_map);
+ /* Update the call_expr on the edges to call the new version node. */
+ update_call_expr (new_version_node);
+
+ /* Update the new version's properties.
+ Make The new version visible only within this translation unit.
+ ??? We cannot use COMDAT linkage because there is no
+ ABI support for this. */
+ DECL_EXTERNAL (new_version_node->decl) = 0;
+ DECL_ONE_ONLY (new_version_node->decl) = 0;
+ TREE_PUBLIC (new_version_node->decl) = 0;
+ DECL_COMDAT (new_version_node->decl) = 0;
+ new_version_node->local.externally_visible = 0;
+ new_version_node->local.local = 1;
+ new_version_node->lowered = true;
+ return new_version_node;
+}