/* Driver of optimization process
- Copyright (C) 2003-2014 Free Software Foundation, Inc.
+ Copyright (C) 2003-2015 Free Software Foundation, Inc.
Contributed by Jan Hubicka
This file is part of GCC.
The front-end is supposed to use following functionality:
- - cgraph_finalize_function
+ - finalize_function
This function is called once front-end has parsed whole body of function
and it is certain that the function body nor the declaration will change.
#include "system.h"
#include "coretypes.h"
#include "tm.h"
+#include "alias.h"
+#include "symtab.h"
#include "tree.h"
+#include "fold-const.h"
#include "varasm.h"
#include "stor-layout.h"
#include "stringpool.h"
#include "output.h"
#include "rtl.h"
+#include "predict.h"
+#include "hard-reg-set.h"
+#include "function.h"
#include "basic-block.h"
+#include "dominance.h"
+#include "cfgcleanup.h"
+#include "cfg.h"
#include "tree-ssa-alias.h"
#include "internal-fn.h"
#include "gimple-fold.h"
#include "gimple-expr.h"
-#include "is-a.h"
#include "gimple.h"
#include "gimplify.h"
#include "gimple-iterator.h"
#include "target.h"
#include "diagnostic.h"
#include "params.h"
-#include "fibheap.h"
#include "intl.h"
-#include "function.h"
+#include "cgraph.h"
+#include "alloc-pool.h"
+#include "symbol-summary.h"
#include "ipa-prop.h"
#include "tree-iterator.h"
#include "tree-pass.h"
#include "pass_manager.h"
#include "tree-nested.h"
#include "gimplify.h"
+#include "dbgcnt.h"
+#include "tree-chkp.h"
+#include "lto-section-names.h"
+#include "omp-low.h"
+#include "print-tree.h"
/* Queue of cgraph nodes scheduled to be added into cgraph. This is a
secondary queue used during optimization to accommodate passes that
may generate new functions that need to be optimized and expanded. */
-cgraph_node_set cgraph_new_nodes;
+vec<cgraph_node *> cgraph_new_nodes;
static void expand_all_functions (void);
static void mark_functions_to_output (void);
-static void expand_function (struct cgraph_node *);
-static void analyze_function (struct cgraph_node *);
static void handle_alias_pairs (void);
-FILE *cgraph_dump_file;
-
-/* Linked list of cgraph asm nodes. */
-struct asm_node *asm_nodes;
-
-/* Last node in cgraph_asm_nodes. */
-static GTY(()) struct asm_node *asm_last_node;
-
/* Used for vtable lookup in thunk adjusting. */
static GTY (()) tree vtable_entry_type;
-/* Determine if symbol DECL is needed. That is, visible to something
+/* Determine if symbol declaration is needed. That is, visible to something
either outside this translation unit, something magic in the system
configury */
bool
-decide_is_symbol_needed (symtab_node *node)
+symtab_node::needed_p (void)
{
- tree decl = node->decl;
-
/* Double check that no one output the function into assembly file
early. */
gcc_checking_assert (!DECL_ASSEMBLER_NAME_SET_P (decl)
|| !TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (decl)));
- if (!node->definition)
+ if (!definition)
return false;
if (DECL_EXTERNAL (decl))
return false;
/* If the user told us it is used, then it must be so. */
- if (node->force_output)
+ if (force_output)
return true;
/* ABI forced symbols are needed when they are external. */
- if (node->forced_by_abi && TREE_PUBLIC (decl))
+ if (forced_by_abi && TREE_PUBLIC (decl))
return true;
- /* Keep constructors, destructors and virtual functions. */
+ /* Keep constructors, destructors and virtual functions. */
if (TREE_CODE (decl) == FUNCTION_DECL
&& (DECL_STATIC_CONSTRUCTOR (decl) || DECL_STATIC_DESTRUCTOR (decl)))
return true;
functions inserted into callgraph already at construction time. */
void
-cgraph_process_new_functions (void)
+symbol_table::process_new_functions (void)
{
tree fndecl;
- struct cgraph_node *node;
- cgraph_node_set_iterator csi;
- if (!cgraph_new_nodes)
+ if (!cgraph_new_nodes.exists ())
return;
+
handle_alias_pairs ();
/* Note that this queue may grow as its being processed, as the new
functions may generate new ones. */
- for (csi = csi_start (cgraph_new_nodes); !csi_end_p (csi); csi_next (&csi))
+ for (unsigned i = 0; i < cgraph_new_nodes.length (); i++)
{
- node = csi_node (csi);
+ cgraph_node *node = cgraph_new_nodes[i];
fndecl = node->decl;
- switch (cgraph_state)
+ switch (state)
{
- case CGRAPH_STATE_CONSTRUCTION:
+ case CONSTRUCTION:
/* At construction time we just need to finalize function and move
it into reachable functions list. */
- cgraph_finalize_function (fndecl, false);
- cgraph_call_function_insertion_hooks (node);
+ cgraph_node::finalize_function (fndecl, false);
+ call_cgraph_insertion_hooks (node);
enqueue_node (node);
break;
- case CGRAPH_STATE_IPA:
- case CGRAPH_STATE_IPA_SSA:
+ case IPA:
+ case IPA_SSA:
+ case IPA_SSA_AFTER_INLINING:
/* When IPA optimization already started, do all essential
transformations that has been already performed on the whole
cgraph but not on this function. */
gimple_register_cfg_hooks ();
if (!node->analyzed)
- analyze_function (node);
+ node->analyze ();
push_cfun (DECL_STRUCT_FUNCTION (fndecl));
- if (cgraph_state == CGRAPH_STATE_IPA_SSA
+ if ((state == IPA_SSA || state == IPA_SSA_AFTER_INLINING)
&& !gimple_in_ssa_p (DECL_STRUCT_FUNCTION (fndecl)))
g->get_passes ()->execute_early_local_passes ();
- else if (inline_summary_vec != NULL)
+ else if (inline_summaries != NULL)
compute_inline_parameters (node, true);
free_dominance_info (CDI_POST_DOMINATORS);
free_dominance_info (CDI_DOMINATORS);
pop_cfun ();
- cgraph_call_function_insertion_hooks (node);
+ call_cgraph_insertion_hooks (node);
break;
- case CGRAPH_STATE_EXPANSION:
+ case EXPANSION:
/* Functions created during expansion shall be compiled
directly. */
node->process = 0;
- cgraph_call_function_insertion_hooks (node);
- expand_function (node);
+ call_cgraph_insertion_hooks (node);
+ node->expand ();
break;
default:
break;
}
}
- free_cgraph_node_set (cgraph_new_nodes);
- cgraph_new_nodes = NULL;
+
+ cgraph_new_nodes.release ();
}
/* As an GCC extension we allow redefinition of the function. The
body for expanding the function but this is difficult to do. */
void
-cgraph_reset_node (struct cgraph_node *node)
+cgraph_node::reset (void)
{
- /* If node->process is set, then we have already begun whole-unit analysis.
+ /* If process is set, then we have already begun whole-unit analysis.
This is *not* testing for whether we've already emitted the function.
That case can be sort-of legitimately seen with real function redefinition
errors. I would argue that the front end should never present us with
such a case, but don't enforce that for now. */
- gcc_assert (!node->process);
+ gcc_assert (!process);
/* Reset our data structures so we can analyze the function again. */
- memset (&node->local, 0, sizeof (node->local));
- memset (&node->global, 0, sizeof (node->global));
- memset (&node->rtl, 0, sizeof (node->rtl));
- node->analyzed = false;
- node->definition = false;
- node->alias = false;
- node->weakref = false;
- node->cpp_implicit_alias = false;
-
- cgraph_node_remove_callees (node);
- ipa_remove_all_references (&node->ref_list);
+ memset (&local, 0, sizeof (local));
+ memset (&global, 0, sizeof (global));
+ memset (&rtl, 0, sizeof (rtl));
+ analyzed = false;
+ definition = false;
+ alias = false;
+ weakref = false;
+ cpp_implicit_alias = false;
+
+ remove_callees ();
+ remove_all_references ();
}
-/* Return true when there are references to NODE. */
+/* Return true when there are references to the node. INCLUDE_SELF is
+ true if a self reference counts as a reference. */
-static bool
-referred_to_p (symtab_node *node)
+bool
+symtab_node::referred_to_p (bool include_self)
{
- struct ipa_ref *ref;
+ ipa_ref *ref = NULL;
/* See if there are any references at all. */
- if (ipa_ref_list_referring_iterate (&node->ref_list, 0, ref))
+ if (iterate_referring (0, ref))
return true;
/* For functions check also calls. */
- cgraph_node *cn = dyn_cast <cgraph_node *> (node);
+ cgraph_node *cn = dyn_cast <cgraph_node *> (this);
if (cn && cn->callers)
- return true;
+ {
+ if (include_self)
+ return true;
+ for (cgraph_edge *e = cn->callers; e; e = e->next_caller)
+ if (e->caller != this)
+ return true;
+ }
return false;
}
a new GC context, or just not compile right now. */
void
-cgraph_finalize_function (tree decl, bool no_collect)
+cgraph_node::finalize_function (tree decl, bool no_collect)
{
- struct cgraph_node *node = cgraph_get_create_node (decl);
+ cgraph_node *node = cgraph_node::get_create (decl);
if (node->definition)
{
/* Nested functions should only be defined once. */
gcc_assert (!DECL_CONTEXT (decl)
|| TREE_CODE (DECL_CONTEXT (decl)) != FUNCTION_DECL);
- cgraph_reset_node (node);
+ node->reset ();
node->local.redefined_extern_inline = true;
}
- notice_global_symbol (decl);
+ /* Set definition first before calling notice_global_symbol so that
+ it is available to notice_global_symbol. */
node->definition = true;
+ notice_global_symbol (decl);
node->lowered = DECL_STRUCT_FUNCTION (decl)->cfg != NULL;
/* With -fkeep-inline-functions we are keeping all inline functions except
declared inline and nested functions. These were optimized out
in the original implementation and it is unclear whether we want
to change the behavior here. */
- if ((!optimize
+ if ((!opt_for_fn (decl, optimize)
&& !node->cpp_implicit_alias
&& !DECL_DISREGARD_INLINE_LIMITS (decl)
&& !DECL_DECLARED_INLINE_P (decl)
if (!TREE_ASM_WRITTEN (decl))
(*debug_hooks->deferred_inline_function) (decl);
- /* Possibly warn about unused parameters. */
- if (warn_unused_parameter)
- do_warn_unused_parameter (decl);
-
if (!no_collect)
ggc_collect ();
- if (cgraph_state == CGRAPH_STATE_CONSTRUCTION
- && (decide_is_symbol_needed (node)
- || referred_to_p (node)))
+ if (symtab->state == CONSTRUCTION
+ && (node->needed_p () || node->referred_to_p ()))
enqueue_node (node);
}
/* Add the function FNDECL to the call graph.
- Unlike cgraph_finalize_function, this function is intended to be used
+ Unlike finalize_function, this function is intended to be used
by middle end and allows insertion of new function at arbitrary point
of compilation. The function can be either in high, low or SSA form
GIMPLE.
processing to avoid need the passes to be re-entrant. */
void
-cgraph_add_new_function (tree fndecl, bool lowered)
+cgraph_node::add_new_function (tree fndecl, bool lowered)
{
gcc::pass_manager *passes = g->get_passes ();
- struct cgraph_node *node;
- switch (cgraph_state)
+ cgraph_node *node;
+
+ if (dump_file)
{
- case CGRAPH_STATE_PARSING:
- cgraph_finalize_function (fndecl, false);
+ struct function *fn = DECL_STRUCT_FUNCTION (fndecl);
+ const char *function_type = ((gimple_has_body_p (fndecl))
+ ? (lowered
+ ? (gimple_in_ssa_p (fn)
+ ? "ssa gimple"
+ : "low gimple")
+ : "high gimple")
+ : "to-be-gimplified");
+ fprintf (dump_file,
+ "Added new %s function %s to callgraph\n",
+ function_type,
+ fndecl_name (fndecl));
+ }
+
+ switch (symtab->state)
+ {
+ case PARSING:
+ cgraph_node::finalize_function (fndecl, false);
break;
- case CGRAPH_STATE_CONSTRUCTION:
+ case CONSTRUCTION:
/* Just enqueue function to be processed at nearest occurrence. */
- node = cgraph_create_node (fndecl);
+ node = cgraph_node::get_create (fndecl);
if (lowered)
node->lowered = true;
- if (!cgraph_new_nodes)
- cgraph_new_nodes = cgraph_node_set_new ();
- cgraph_node_set_add (cgraph_new_nodes, node);
+ cgraph_new_nodes.safe_push (node);
break;
- case CGRAPH_STATE_IPA:
- case CGRAPH_STATE_IPA_SSA:
- case CGRAPH_STATE_EXPANSION:
+ case IPA:
+ case IPA_SSA:
+ case IPA_SSA_AFTER_INLINING:
+ case EXPANSION:
/* Bring the function into finalized state and enqueue for later
analyzing and compilation. */
- node = cgraph_get_create_node (fndecl);
+ node = cgraph_node::get_create (fndecl);
node->local.local = false;
node->definition = true;
node->force_output = true;
- if (!lowered && cgraph_state == CGRAPH_STATE_EXPANSION)
+ if (!lowered && symtab->state == EXPANSION)
{
push_cfun (DECL_STRUCT_FUNCTION (fndecl));
gimple_register_cfg_hooks ();
bitmap_obstack_initialize (NULL);
- execute_pass_list (passes->all_lowering_passes);
+ execute_pass_list (cfun, passes->all_lowering_passes);
passes->execute_early_local_passes ();
bitmap_obstack_release (NULL);
pop_cfun ();
}
if (lowered)
node->lowered = true;
- if (!cgraph_new_nodes)
- cgraph_new_nodes = cgraph_node_set_new ();
- cgraph_node_set_add (cgraph_new_nodes, node);
+ cgraph_new_nodes.safe_push (node);
break;
- case CGRAPH_STATE_FINISHED:
+ case FINISHED:
/* At the very end of compilation we have to do all the work up
to expansion. */
- node = cgraph_create_node (fndecl);
+ node = cgraph_node::create (fndecl);
if (lowered)
node->lowered = true;
node->definition = true;
- analyze_function (node);
+ node->analyze ();
push_cfun (DECL_STRUCT_FUNCTION (fndecl));
gimple_register_cfg_hooks ();
bitmap_obstack_initialize (NULL);
g->get_passes ()->execute_early_local_passes ();
bitmap_obstack_release (NULL);
pop_cfun ();
- expand_function (node);
+ node->expand ();
break;
default:
DECL_FUNCTION_PERSONALITY (fndecl) = lang_hooks.eh_personality ();
}
-/* Add a top-level asm statement to the list. */
-
-struct asm_node *
-add_asm_node (tree asm_str)
-{
- struct asm_node *node;
-
- node = ggc_alloc_cleared_asm_node ();
- node->asm_str = asm_str;
- node->order = symtab_order++;
- node->next = NULL;
- if (asm_nodes == NULL)
- asm_nodes = node;
- else
- asm_last_node->next = node;
- asm_last_node = node;
- return node;
-}
-
-/* Output all asm statements we have stored up to be output. */
-
-static void
-output_asm_statements (void)
-{
- struct asm_node *can;
-
- if (seen_error ())
- return;
-
- for (can = asm_nodes; can; can = can->next)
- assemble_asm (can->asm_str);
- asm_nodes = NULL;
-}
-
/* Analyze the function scheduled to be output. */
-static void
-analyze_function (struct cgraph_node *node)
+void
+cgraph_node::analyze (void)
{
- tree decl = node->decl;
+ tree decl = this->decl;
location_t saved_loc = input_location;
input_location = DECL_SOURCE_LOCATION (decl);
- if (node->thunk.thunk_p)
+ if (thunk.thunk_p)
{
- cgraph_create_edge (node, cgraph_get_node (node->thunk.alias),
- NULL, 0, CGRAPH_FREQ_BASE);
- if (!expand_thunk (node, false))
+ cgraph_node *t = cgraph_node::get (thunk.alias);
+
+ create_edge (t, NULL, 0, CGRAPH_FREQ_BASE);
+ /* Target code in expand_thunk may need the thunk's target
+ to be analyzed, so recurse here. */
+ if (!t->analyzed)
+ t->analyze ();
+ if (t->alias)
+ {
+ t = t->get_alias_target ();
+ if (!t->analyzed)
+ t->analyze ();
+ }
+ if (!expand_thunk (false, false))
{
- node->thunk.alias = NULL;
- node->analyzed = true;
+ thunk.alias = NULL;
return;
}
- node->thunk.alias = NULL;
+ thunk.alias = NULL;
}
- if (node->alias)
- symtab_resolve_alias
- (node, cgraph_get_node (node->alias_target));
- else if (node->dispatcher_function)
+ if (alias)
+ resolve_alias (cgraph_node::get (alias_target));
+ else if (dispatcher_function)
{
/* Generate the dispatcher body of multi-versioned functions. */
- struct cgraph_function_version_info *dispatcher_version_info
- = get_cgraph_node_version (node);
+ cgraph_function_version_info *dispatcher_version_info
+ = function_version ();
if (dispatcher_version_info != NULL
&& (dispatcher_version_info->dispatcher_resolver
== NULL_TREE))
{
tree resolver = NULL_TREE;
gcc_assert (targetm.generate_version_dispatcher_body);
- resolver = targetm.generate_version_dispatcher_body (node);
+ resolver = targetm.generate_version_dispatcher_body (this);
gcc_assert (resolver != NULL_TREE);
}
}
{
push_cfun (DECL_STRUCT_FUNCTION (decl));
- assign_assembler_name_if_neeeded (node->decl);
+ assign_assembler_name_if_neeeded (decl);
/* Make sure to gimplify bodies only once. During analyzing a
function we lower it, which will require gimplified nested
body. */
if (!gimple_has_body_p (decl))
gimplify_function_tree (decl);
- dump_function (TDI_generic, decl);
/* Lower the function. */
- if (!node->lowered)
+ if (!lowered)
{
- if (node->nested)
- lower_nested_functions (node->decl);
- gcc_assert (!node->nested);
+ if (nested)
+ lower_nested_functions (decl);
+ gcc_assert (!nested);
gimple_register_cfg_hooks ();
bitmap_obstack_initialize (NULL);
- execute_pass_list (g->get_passes ()->all_lowering_passes);
+ execute_pass_list (cfun, g->get_passes ()->all_lowering_passes);
free_dominance_info (CDI_POST_DOMINATORS);
free_dominance_info (CDI_DOMINATORS);
compact_blocks ();
bitmap_obstack_release (NULL);
- node->lowered = true;
+ lowered = true;
}
pop_cfun ();
}
- node->analyzed = true;
+ analyzed = true;
input_location = saved_loc;
}
PCH we build the links via this function. */
void
-cgraph_process_same_body_aliases (void)
+symbol_table::process_same_body_aliases (void)
{
symtab_node *node;
FOR_EACH_SYMBOL (node)
if (node->cpp_implicit_alias && !node->analyzed)
- symtab_resolve_alias
- (node,
- TREE_CODE (node->alias_target) == VAR_DECL
- ? (symtab_node *)varpool_node_for_decl (node->alias_target)
- : (symtab_node *)cgraph_get_create_node (node->alias_target));
+ node->resolve_alias
+ (TREE_CODE (node->alias_target) == VAR_DECL
+ ? (symtab_node *)varpool_node::get_create (node->alias_target)
+ : (symtab_node *)cgraph_node::get_create (node->alias_target));
cpp_implicit_aliases_done = true;
}
/* Process attributes common for vars and functions. */
static void
-process_common_attributes (tree decl)
+process_common_attributes (symtab_node *node, tree decl)
{
tree weakref = lookup_attribute ("weakref", DECL_ATTRIBUTES (decl));
DECL_ATTRIBUTES (decl) = remove_attribute ("weakref",
DECL_ATTRIBUTES (decl));
}
+
+ if (lookup_attribute ("no_reorder", DECL_ATTRIBUTES (decl)))
+ node->no_reorder = 1;
}
/* Look for externally_visible and used attributes and mark cgraph nodes
declaration -- but the front end will subsequently merge that declaration
with the original declaration and discard the second declaration.
- Furthermore, we can't mark these nodes in cgraph_finalize_function because:
+ Furthermore, we can't mark these nodes in finalize_function because:
void f() {}
void f() __attribute__((externally_visible));
attributes at that point. */
static void
-process_function_and_variable_attributes (struct cgraph_node *first,
+process_function_and_variable_attributes (cgraph_node *first,
varpool_node *first_var)
{
- struct cgraph_node *node;
+ cgraph_node *node;
varpool_node *vnode;
- for (node = cgraph_first_function (); node != first;
- node = cgraph_next_function (node))
+ for (node = symtab->first_function (); node != first;
+ node = symtab->next_function (node))
{
tree decl = node->decl;
if (DECL_PRESERVE_P (decl))
- cgraph_mark_force_output_node (node);
+ node->mark_force_output ();
else if (lookup_attribute ("externally_visible", DECL_ATTRIBUTES (decl)))
{
if (! TREE_PUBLIC (node->decl))
warning_at (DECL_SOURCE_LOCATION (decl), OPT_Wattributes,
"always_inline function might not be inlinable");
- process_common_attributes (decl);
+ process_common_attributes (node, decl);
}
- for (vnode = varpool_first_variable (); vnode != first_var;
- vnode = varpool_next_variable (vnode))
+ for (vnode = symtab->first_variable (); vnode != first_var;
+ vnode = symtab->next_variable (vnode))
{
tree decl = vnode->decl;
if (DECL_EXTERNAL (decl)
&& DECL_INITIAL (decl))
- varpool_finalize_decl (decl);
+ varpool_node::finalize_decl (decl);
if (DECL_PRESERVE_P (decl))
vnode->force_output = true;
else if (lookup_attribute ("externally_visible", DECL_ATTRIBUTES (decl)))
DECL_ATTRIBUTES (decl) = remove_attribute ("weakref",
DECL_ATTRIBUTES (decl));
}
- process_common_attributes (decl);
+ process_common_attributes (vnode, decl);
}
}
visible. */
void
-varpool_finalize_decl (tree decl)
+varpool_node::finalize_decl (tree decl)
{
- varpool_node *node = varpool_node_for_decl (decl);
+ varpool_node *node = varpool_node::get_create (decl);
gcc_assert (TREE_STATIC (decl) || DECL_EXTERNAL (decl));
if (node->definition)
return;
- notice_global_symbol (decl);
+ /* Set definition first before calling notice_global_symbol so that
+ it is available to notice_global_symbol. */
node->definition = true;
+ notice_global_symbol (decl);
if (TREE_THIS_VOLATILE (decl) || DECL_PRESERVE_P (decl)
/* Traditionally we do not eliminate static variables when not
optimizing and when not doing toplevel reoder. */
- || (!flag_toplevel_reorder && !DECL_COMDAT (node->decl)
- && !DECL_ARTIFICIAL (node->decl)))
+ || node->no_reorder
+ || ((!flag_toplevel_reorder
+ && !DECL_COMDAT (node->decl)
+ && !DECL_ARTIFICIAL (node->decl))))
node->force_output = true;
- if (cgraph_state == CGRAPH_STATE_CONSTRUCTION
- && (decide_is_symbol_needed (node)
- || referred_to_p (node)))
+ if (symtab->state == CONSTRUCTION
+ && (node->needed_p () || node->referred_to_p ()))
enqueue_node (node);
- if (cgraph_state >= CGRAPH_STATE_IPA_SSA)
- varpool_analyze_node (node);
+ if (symtab->state >= IPA_SSA)
+ node->analyze ();
/* Some frontends produce various interface variables after compilation
finished. */
- if (cgraph_state == CGRAPH_STATE_FINISHED
- || (!flag_toplevel_reorder && cgraph_state == CGRAPH_STATE_EXPANSION))
- varpool_assemble_decl (node);
+ if (symtab->state == FINISHED
+ || (!flag_toplevel_reorder
+ && symtab->state == EXPANSION))
+ node->assemble_decl ();
+
+ if (DECL_INITIAL (decl))
+ chkp_register_var_initializer (decl);
}
/* EDGE is an polymorphic call. Mark all possible targets as reachable
avoid udplicate work. */
static void
-walk_polymorphic_call_targets (pointer_set_t *reachable_call_targets,
- struct cgraph_edge *edge)
+walk_polymorphic_call_targets (hash_set<void *> *reachable_call_targets,
+ cgraph_edge *edge)
{
unsigned int i;
void *cache_token;
= possible_polymorphic_call_targets
(edge, &final, &cache_token);
- if (!pointer_set_insert (reachable_call_targets,
- cache_token))
+ if (!reachable_call_targets->add (cache_token))
{
- if (cgraph_dump_file)
+ if (symtab->dump_file)
dump_possible_polymorphic_call_targets
- (cgraph_dump_file, edge);
+ (symtab->dump_file, edge);
for (i = 0; i < targets.length (); i++)
{
(TREE_TYPE (targets[i]->decl))
== METHOD_TYPE
&& !type_in_anonymous_namespace_p
- (method_class_type
- (TREE_TYPE (targets[i]->decl))))
- enqueue_node (targets[i]);
+ (TYPE_METHOD_BASETYPE (TREE_TYPE (targets[i]->decl))))
+ enqueue_node (targets[i]);
}
}
make the edge direct. */
if (final)
{
- if (targets.length () <= 1)
+ if (targets.length () <= 1 && dbg_cnt (devirt))
{
cgraph_node *target;
if (targets.length () == 1)
target = targets[0];
else
- target = cgraph_get_create_node
- (builtin_decl_implicit (BUILT_IN_UNREACHABLE));
+ target = cgraph_node::create
+ (builtin_decl_implicit (BUILT_IN_UNREACHABLE));
- if (cgraph_dump_file)
+ if (symtab->dump_file)
{
- fprintf (cgraph_dump_file,
+ fprintf (symtab->dump_file,
"Devirtualizing call: ");
- print_gimple_stmt (cgraph_dump_file,
+ print_gimple_stmt (symtab->dump_file,
edge->call_stmt, 0,
TDF_SLIM);
}
- cgraph_make_edge_direct (edge, target);
- cgraph_redirect_edge_call_stmt_to_callee (edge);
- if (cgraph_dump_file)
+ if (dump_enabled_p ())
+ {
+ location_t locus = gimple_location_safe (edge->call_stmt);
+ dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, locus,
+ "devirtualizing call in %s to %s\n",
+ edge->caller->name (), target->name ());
+ }
+
+ edge->make_direct (target);
+ edge->redirect_call_stmt_to_callee ();
+
+ /* Call to __builtin_unreachable shouldn't be instrumented. */
+ if (!targets.length ())
+ gimple_call_set_with_bounds (edge->call_stmt, false);
+
+ if (symtab->dump_file)
{
- fprintf (cgraph_dump_file,
+ fprintf (symtab->dump_file,
"Devirtualized as: ");
- print_gimple_stmt (cgraph_dump_file,
+ print_gimple_stmt (symtab->dump_file,
edge->call_stmt, 0,
TDF_SLIM);
}
/* Discover all functions and variables that are trivially needed, analyze
them as well as all functions and variables referred by them */
+static cgraph_node *first_analyzed;
+static varpool_node *first_analyzed_var;
+
+/* FIRST_TIME is set to TRUE for the first time we are called for a
+ translation unit from finalize_compilation_unit() or false
+ otherwise. */
static void
-analyze_functions (void)
+analyze_functions (bool first_time)
{
/* Keep track of already processed nodes when called multiple times for
intermodule optimization. */
- static struct cgraph_node *first_analyzed;
- struct cgraph_node *first_handled = first_analyzed;
- static varpool_node *first_analyzed_var;
+ cgraph_node *first_handled = first_analyzed;
varpool_node *first_handled_var = first_analyzed_var;
- struct pointer_set_t *reachable_call_targets = pointer_set_create ();
+ hash_set<void *> reachable_call_targets;
symtab_node *node;
symtab_node *next;
int i;
- struct ipa_ref *ref;
+ ipa_ref *ref;
bool changed = true;
location_t saved_loc = input_location;
bitmap_obstack_initialize (NULL);
- cgraph_state = CGRAPH_STATE_CONSTRUCTION;
+ symtab->state = CONSTRUCTION;
input_location = UNKNOWN_LOCATION;
/* Ugly, but the fixup can not happen at a time same body alias is created;
C++ FE is confused about the COMDAT groups being right. */
- if (cpp_implicit_aliases_done)
+ if (symtab->cpp_implicit_aliases_done)
FOR_EACH_SYMBOL (node)
if (node->cpp_implicit_alias)
- fixup_same_cpp_alias_visibility (node, symtab_alias_target (node));
- if (optimize && flag_devirtualize)
- build_type_inheritance_graph ();
+ node->fixup_same_cpp_alias_visibility (node->get_alias_target ());
+ build_type_inheritance_graph ();
/* Analysis adds static variables that in turn adds references to new functions.
So we need to iterate the process until it stabilize. */
first_analyzed_var);
/* First identify the trivially needed symbols. */
- for (node = symtab_nodes;
+ for (node = symtab->first_symbol ();
node != first_analyzed
&& node != first_analyzed_var; node = node->next)
{
- if (decide_is_symbol_needed (node))
+ /* Convert COMDAT group designators to IDENTIFIER_NODEs. */
+ node->get_comdat_group_id ();
+ if (node->needed_p ())
{
enqueue_node (node);
- if (!changed && cgraph_dump_file)
- fprintf (cgraph_dump_file, "Trivially needed symbols:");
+ if (!changed && symtab->dump_file)
+ fprintf (symtab->dump_file, "Trivially needed symbols:");
changed = true;
- if (cgraph_dump_file)
- fprintf (cgraph_dump_file, " %s", node->asm_name ());
- if (!changed && cgraph_dump_file)
- fprintf (cgraph_dump_file, "\n");
+ if (symtab->dump_file)
+ fprintf (symtab->dump_file, " %s", node->asm_name ());
+ if (!changed && symtab->dump_file)
+ fprintf (symtab->dump_file, "\n");
}
if (node == first_analyzed
|| node == first_analyzed_var)
break;
}
- cgraph_process_new_functions ();
- first_analyzed_var = varpool_first_variable ();
- first_analyzed = cgraph_first_function ();
+ symtab->process_new_functions ();
+ first_analyzed_var = symtab->first_variable ();
+ first_analyzed = symtab->first_function ();
- if (changed && dump_file)
- fprintf (cgraph_dump_file, "\n");
+ if (changed && symtab->dump_file)
+ fprintf (symtab->dump_file, "\n");
/* Lower representation, build callgraph edges and references for all trivially
needed symbols and all symbols referred by them. */
cgraph_node *cnode = dyn_cast <cgraph_node *> (node);
if (cnode && cnode->definition)
{
- struct cgraph_edge *edge;
+ cgraph_edge *edge;
tree decl = cnode->decl;
/* ??? It is possible to create extern inline function
&& !cnode->thunk.thunk_p
&& !cnode->dispatcher_function)
{
- cgraph_reset_node (cnode);
+ cnode->reset ();
cnode->local.redefined_extern_inline = true;
continue;
}
if (!cnode->analyzed)
- analyze_function (cnode);
+ cnode->analyze ();
for (edge = cnode->callees; edge; edge = edge->next_callee)
- if (edge->callee->definition)
+ if (edge->callee->definition
+ && (!DECL_EXTERNAL (edge->callee->decl)
+ /* When not optimizing, do not try to analyze extern
+ inline functions. Doing so is pointless. */
+ || opt_for_fn (edge->callee->decl, optimize)
+ /* Weakrefs needs to be preserved. */
+ || edge->callee->alias
+ /* always_inline functions are inlined aven at -O0. */
+ || lookup_attribute
+ ("always_inline",
+ DECL_ATTRIBUTES (edge->callee->decl))
+ /* Multiversioned functions needs the dispatcher to
+ be produced locally even for extern functions. */
+ || edge->callee->function_version ()))
enqueue_node (edge->callee);
- if (optimize && flag_devirtualize)
+ if (opt_for_fn (cnode->decl, optimize)
+ && opt_for_fn (cnode->decl, flag_devirtualize))
{
- struct cgraph_edge *next;
+ cgraph_edge *next;
for (edge = cnode->indirect_calls; edge; edge = next)
{
next = edge->next_callee;
if (edge->indirect_info->polymorphic)
- walk_polymorphic_call_targets (reachable_call_targets,
+ walk_polymorphic_call_targets (&reachable_call_targets,
edge);
}
}
will be later needed to output debug info. */
if (DECL_ABSTRACT_ORIGIN (decl))
{
- struct cgraph_node *origin_node
- = cgraph_get_node (DECL_ABSTRACT_ORIGIN (decl));
+ cgraph_node *origin_node
+ = cgraph_node::get_create (DECL_ABSTRACT_ORIGIN (decl));
origin_node->used_as_abstract_origin = true;
}
}
{
varpool_node *vnode = dyn_cast <varpool_node *> (node);
if (vnode && vnode->definition && !vnode->analyzed)
- varpool_analyze_node (vnode);
+ vnode->analyze ();
}
if (node->same_comdat_group)
for (next = node->same_comdat_group;
next != node;
next = next->same_comdat_group)
- enqueue_node (next);
+ if (!next->comdat_local_p ())
+ enqueue_node (next);
}
- for (i = 0; ipa_ref_list_reference_iterate (&node->ref_list, i, ref); i++)
- if (ref->referred->definition)
+ for (i = 0; node->iterate_reference (i, ref); i++)
+ if (ref->referred->definition
+ && (!DECL_EXTERNAL (ref->referred->decl)
+ || ((TREE_CODE (ref->referred->decl) != FUNCTION_DECL
+ && optimize)
+ || (TREE_CODE (ref->referred->decl) == FUNCTION_DECL
+ && opt_for_fn (ref->referred->decl, optimize))
+ || node->alias
+ || ref->referred->alias)))
enqueue_node (ref->referred);
- cgraph_process_new_functions ();
+ symtab->process_new_functions ();
}
}
- if (optimize && flag_devirtualize)
- update_type_inheritance_graph ();
+ update_type_inheritance_graph ();
/* Collect entry points to the unit. */
- if (cgraph_dump_file)
+ if (symtab->dump_file)
+ {
+ fprintf (symtab->dump_file, "\n\nInitial ");
+ symtab_node::dump_table (symtab->dump_file);
+ }
+
+ if (first_time)
{
- fprintf (cgraph_dump_file, "\n\nInitial ");
- dump_symtab (cgraph_dump_file);
+ symtab_node *snode;
+ FOR_EACH_SYMBOL (snode)
+ check_global_declaration (snode->decl);
}
- if (cgraph_dump_file)
- fprintf (cgraph_dump_file, "\nRemoving unused symbols:");
+ if (symtab->dump_file)
+ fprintf (symtab->dump_file, "\nRemoving unused symbols:");
- for (node = symtab_nodes;
+ for (node = symtab->first_symbol ();
node != first_handled
&& node != first_handled_var; node = next)
{
next = node->next;
- if (!node->aux && !referred_to_p (node))
+ if (!node->aux && !node->referred_to_p ())
{
- if (cgraph_dump_file)
- fprintf (cgraph_dump_file, " %s", node->name ());
- symtab_remove_node (node);
+ if (symtab->dump_file)
+ fprintf (symtab->dump_file, " %s", node->name ());
+
+ /* See if the debugger can use anything before the DECL
+ passes away. Perhaps it can notice a DECL that is now a
+ constant and can tag the early DIE with an appropriate
+ attribute.
+
+ Otherwise, this is the last chance the debug_hooks have
+ at looking at optimized away DECLs, since
+ late_global_decl will subsequently be called from the
+ contents of the now pruned symbol table. */
+ if (!decl_function_context (node->decl))
+ (*debug_hooks->late_global_decl) (node->decl);
+
+ node->remove ();
continue;
}
if (cgraph_node *cnode = dyn_cast <cgraph_node *> (node))
if (cnode->definition && !gimple_has_body_p (decl)
&& !cnode->alias
&& !cnode->thunk.thunk_p)
- cgraph_reset_node (cnode);
+ cnode->reset ();
gcc_assert (!cnode->definition || cnode->thunk.thunk_p
|| cnode->alias
}
for (;node; node = node->next)
node->aux = NULL;
- first_analyzed = cgraph_first_function ();
- first_analyzed_var = varpool_first_variable ();
- if (cgraph_dump_file)
+ first_analyzed = symtab->first_function ();
+ first_analyzed_var = symtab->first_variable ();
+ if (symtab->dump_file)
{
- fprintf (cgraph_dump_file, "\n\nReclaimed ");
- dump_symtab (cgraph_dump_file);
+ fprintf (symtab->dump_file, "\n\nReclaimed ");
+ symtab_node::dump_table (symtab->dump_file);
}
bitmap_obstack_release (NULL);
- pointer_set_destroy (reachable_call_targets);
ggc_collect ();
/* Initialize assembler name hash, in particular we want to trigger C++
mangling and same body alias creation before we free DECL_ARGUMENTS
used by it. */
if (!seen_error ())
- symtab_initialize_asm_name_hash ();
+ symtab->symtab_initialize_asm_name_hash ();
input_location = saved_loc;
}
for (i = 0; alias_pairs && alias_pairs->iterate (i, &p);)
{
- symtab_node *target_node = symtab_node_for_asm (p->target);
+ symtab_node *target_node = symtab_node::get_for_asmname (p->target);
/* Weakrefs with target not defined in current unit are easy to handle:
they behave just as external variables except we need to note the
if (!target_node
&& lookup_attribute ("weakref", DECL_ATTRIBUTES (p->decl)) != NULL)
{
- symtab_node *node = symtab_get_node (p->decl);
+ symtab_node *node = symtab_node::get (p->decl);
if (node)
{
node->alias_target = p->target;
else if (!target_node)
{
error ("%q+D aliased to undefined symbol %qE", p->decl, p->target);
- symtab_node *node = symtab_get_node (p->decl);
+ symtab_node *node = symtab_node::get (p->decl);
if (node)
node->alias = false;
alias_pairs->unordered_remove (i);
/* We use local aliases for C++ thunks to force the tailcall
to bind locally. This is a hack - to keep it working do
the following (which is not strictly correct). */
- && (! TREE_CODE (target_node->decl) == FUNCTION_DECL
+ && (TREE_CODE (target_node->decl) != FUNCTION_DECL
|| ! DECL_VIRTUAL_P (target_node->decl))
&& ! lookup_attribute ("weakref", DECL_ATTRIBUTES (p->decl)))
{
if (TREE_CODE (p->decl) == FUNCTION_DECL
&& target_node && is_a <cgraph_node *> (target_node))
{
- struct cgraph_node *src_node = cgraph_get_node (p->decl);
+ cgraph_node *src_node = cgraph_node::get (p->decl);
if (src_node && src_node->definition)
- cgraph_reset_node (src_node);
- cgraph_create_function_alias (p->decl, target_node->decl);
+ src_node->reset ();
+ cgraph_node::create_alias (p->decl, target_node->decl);
alias_pairs->unordered_remove (i);
}
else if (TREE_CODE (p->decl) == VAR_DECL
&& target_node && is_a <varpool_node *> (target_node))
{
- varpool_create_variable_alias (p->decl, target_node->decl);
+ varpool_node::create_alias (p->decl, target_node->decl);
alias_pairs->unordered_remove (i);
}
else
static void
mark_functions_to_output (void)
{
- struct cgraph_node *node;
+ cgraph_node *node;
#ifdef ENABLE_CHECKING
bool check_same_comdat_groups = false;
node->process = 1;
if (node->same_comdat_group)
{
- struct cgraph_node *next;
- for (next = cgraph (node->same_comdat_group);
+ cgraph_node *next;
+ for (next = dyn_cast<cgraph_node *> (node->same_comdat_group);
next != node;
- next = cgraph (next->same_comdat_group))
+ next = dyn_cast<cgraph_node *> (next->same_comdat_group))
if (!next->thunk.thunk_p && !next->alias
- && !symtab_comdat_local_p (next))
+ && !next->comdat_local_p ())
next->process = 1;
}
}
&& !node->clones
&& !DECL_EXTERNAL (decl))
{
- dump_cgraph_node (stderr, node);
+ node->debug ();
internal_error ("failed to reclaim unneeded function");
}
#endif
&& !node->clones
&& !DECL_EXTERNAL (decl))
{
- dump_cgraph_node (stderr, node);
+ node->debug ();
internal_error ("failed to reclaim unneeded function in same "
"comdat group");
}
return basic block in the function body. */
basic_block
-init_lowered_empty_function (tree decl, bool in_ssa)
+init_lowered_empty_function (tree decl, bool in_ssa, gcov_type count)
{
basic_block bb;
+ edge e;
current_function_decl = decl;
allocate_struct_function (decl, false);
cfun->curr_properties |= (PROP_gimple_lcf | PROP_gimple_leh | PROP_gimple_any
| PROP_cfg | PROP_loops);
- set_loops_for_fn (cfun, ggc_alloc_cleared_loops ());
+ set_loops_for_fn (cfun, ggc_cleared_alloc<loops> ());
init_loops_structure (cfun, loops_for_fn (cfun), 1);
loops_for_fn (cfun)->state |= LOOPS_MAY_HAVE_MULTIPLE_LATCHES;
/* Create BB for body of the function and connect it properly. */
- bb = create_basic_block (NULL, (void *) 0, ENTRY_BLOCK_PTR_FOR_FN (cfun));
- make_edge (ENTRY_BLOCK_PTR_FOR_FN (cfun), bb, EDGE_FALLTHRU);
- make_edge (bb, EXIT_BLOCK_PTR_FOR_FN (cfun), 0);
+ ENTRY_BLOCK_PTR_FOR_FN (cfun)->count = count;
+ ENTRY_BLOCK_PTR_FOR_FN (cfun)->frequency = REG_BR_PROB_BASE;
+ EXIT_BLOCK_PTR_FOR_FN (cfun)->count = count;
+ EXIT_BLOCK_PTR_FOR_FN (cfun)->frequency = REG_BR_PROB_BASE;
+ bb = create_basic_block (NULL, ENTRY_BLOCK_PTR_FOR_FN (cfun));
+ bb->count = count;
+ bb->frequency = BB_FREQ_MAX;
+ e = make_edge (ENTRY_BLOCK_PTR_FOR_FN (cfun), bb, EDGE_FALLTHRU);
+ e->count = count;
+ e->probability = REG_BR_PROB_BASE;
+ e = make_edge (bb, EXIT_BLOCK_PTR_FOR_FN (cfun), 0);
+ e->count = count;
+ e->probability = REG_BR_PROB_BASE;
add_bb_to_loop (bb, ENTRY_BLOCK_PTR_FOR_FN (cfun)->loop_father);
return bb;
tree ptr, bool this_adjusting,
HOST_WIDE_INT fixed_offset, tree virtual_offset)
{
- gimple stmt;
+ gassign *stmt;
tree ret;
if (this_adjusting
}
/* Expand thunk NODE to gimple if possible.
+ When FORCE_GIMPLE_THUNK is true, gimple thunk is created and
+ no assembler is produced.
When OUTPUT_ASM_THUNK is true, also produce assembler for
thunks that are not lowered. */
bool
-expand_thunk (struct cgraph_node *node, bool output_asm_thunks)
+cgraph_node::expand_thunk (bool output_asm_thunks, bool force_gimple_thunk)
{
- bool this_adjusting = node->thunk.this_adjusting;
- HOST_WIDE_INT fixed_offset = node->thunk.fixed_offset;
- HOST_WIDE_INT virtual_value = node->thunk.virtual_value;
+ bool this_adjusting = thunk.this_adjusting;
+ HOST_WIDE_INT fixed_offset = thunk.fixed_offset;
+ HOST_WIDE_INT virtual_value = thunk.virtual_value;
tree virtual_offset = NULL;
- tree alias = node->callees->callee->decl;
- tree thunk_fndecl = node->decl;
+ tree alias = callees->callee->decl;
+ tree thunk_fndecl = decl;
tree a;
+ /* Instrumentation thunk is the same function with
+ a different signature. Never need to expand it. */
+ if (thunk.add_pointer_bounds_args)
+ return false;
- if (this_adjusting
+ if (!force_gimple_thunk && this_adjusting
&& targetm.asm_out.can_output_mi_thunk (thunk_fndecl, fixed_offset,
virtual_value, alias))
{
tree restype = TREE_TYPE (TREE_TYPE (thunk_fndecl));
if (!output_asm_thunks)
- return false;
+ {
+ analyzed = true;
+ return false;
+ }
if (in_lto_p)
- cgraph_get_body (node);
+ get_untransformed_body ();
a = DECL_ARGUMENTS (thunk_fndecl);
current_function_decl = thunk_fndecl;
/* Ensure thunks are emitted in their correct sections. */
- resolve_unique_section (thunk_fndecl, 0, flag_function_sections);
+ resolve_unique_section (thunk_fndecl, 0,
+ flag_function_sections);
DECL_RESULT (thunk_fndecl)
= build_decl (DECL_SOURCE_LOCATION (thunk_fndecl),
free_after_compilation (cfun);
set_cfun (NULL);
TREE_ASM_WRITTEN (thunk_fndecl) = 1;
- node->thunk.thunk_p = false;
- node->analyzed = false;
+ thunk.thunk_p = false;
+ analyzed = false;
+ }
+ else if (stdarg_p (TREE_TYPE (thunk_fndecl)))
+ {
+ error ("generic thunk code fails for method %qD which uses %<...%>",
+ thunk_fndecl);
+ TREE_ASM_WRITTEN (thunk_fndecl) = 1;
+ analyzed = true;
+ return false;
}
else
{
int i;
tree resdecl;
tree restmp = NULL;
+ tree resbnd = NULL;
- gimple call;
- gimple ret;
+ gcall *call;
+ greturn *ret;
+ bool alias_is_noreturn = TREE_THIS_VOLATILE (alias);
if (in_lto_p)
- cgraph_get_body (node);
+ get_untransformed_body ();
a = DECL_ARGUMENTS (thunk_fndecl);
-
+
current_function_decl = thunk_fndecl;
/* Ensure thunks are emitted in their correct sections. */
- resolve_unique_section (thunk_fndecl, 0, flag_function_sections);
+ resolve_unique_section (thunk_fndecl, 0,
+ flag_function_sections);
DECL_IGNORED_P (thunk_fndecl) = 1;
bitmap_obstack_initialize (NULL);
- if (node->thunk.virtual_offset_p)
+ if (thunk.virtual_offset_p)
virtual_offset = size_int (virtual_value);
/* Build the return declaration for the function. */
else
resdecl = DECL_RESULT (thunk_fndecl);
- bb = then_bb = else_bb = return_bb = init_lowered_empty_function (thunk_fndecl, true);
+ bb = then_bb = else_bb = return_bb
+ = init_lowered_empty_function (thunk_fndecl, true, count);
bsi = gsi_start_bb (bb);
/* Build call to the function being thunked. */
- if (!VOID_TYPE_P (restype))
+ if (!VOID_TYPE_P (restype) && !alias_is_noreturn)
{
if (DECL_BY_REFERENCE (resdecl))
- restmp = gimple_fold_indirect_ref (resdecl);
+ {
+ restmp = gimple_fold_indirect_ref (resdecl);
+ if (!restmp)
+ restmp = build2 (MEM_REF,
+ TREE_TYPE (TREE_TYPE (DECL_RESULT (alias))),
+ resdecl,
+ build_int_cst (TREE_TYPE
+ (DECL_RESULT (alias)), 0));
+ }
else if (!is_gimple_reg_type (restype))
{
- restmp = resdecl;
- add_local_decl (cfun, restmp);
- BLOCK_VARS (DECL_INITIAL (current_function_decl)) = restmp;
+ if (aggregate_value_p (resdecl, TREE_TYPE (thunk_fndecl)))
+ {
+ restmp = resdecl;
+
+ if (TREE_CODE (restmp) == VAR_DECL)
+ add_local_decl (cfun, restmp);
+ BLOCK_VARS (DECL_INITIAL (current_function_decl)) = restmp;
+ }
+ else
+ restmp = create_tmp_var (restype, "retval");
}
else
restmp = create_tmp_reg (restype, "retval");
for (arg = a; arg; arg = DECL_CHAIN (arg))
nargs++;
auto_vec<tree> vargs (nargs);
+ i = 0;
+ arg = a;
if (this_adjusting)
- vargs.quick_push (thunk_adjust (&bsi, a, 1, fixed_offset,
- virtual_offset));
- else if (nargs)
- vargs.quick_push (a);
+ {
+ vargs.quick_push (thunk_adjust (&bsi, a, 1, fixed_offset,
+ virtual_offset));
+ arg = DECL_CHAIN (a);
+ i = 1;
+ }
if (nargs)
- for (i = 1, arg = DECL_CHAIN (a); i < nargs; i++, arg = DECL_CHAIN (arg))
+ for (; i < nargs; i++, arg = DECL_CHAIN (arg))
{
tree tmp = arg;
if (!is_gimple_val (arg))
vargs.quick_push (tmp);
}
call = gimple_build_call_vec (build_fold_addr_expr_loc (0, alias), vargs);
- node->callees->call_stmt = call;
+ callees->call_stmt = call;
gimple_call_set_from_thunk (call, true);
- if (restmp)
+ gimple_call_set_with_bounds (call, instrumentation_clone);
+
+ /* Return slot optimization is always possible and in fact requred to
+ return values with DECL_BY_REFERENCE. */
+ if (aggregate_value_p (resdecl, TREE_TYPE (thunk_fndecl))
+ && (!is_gimple_reg_type (TREE_TYPE (resdecl))
+ || DECL_BY_REFERENCE (resdecl)))
+ gimple_call_set_return_slot_opt (call, true);
+
+ if (restmp && !alias_is_noreturn)
{
gimple_call_set_lhs (call, restmp);
gcc_assert (useless_type_conversion_p (TREE_TYPE (restmp),
TREE_TYPE (TREE_TYPE (alias))));
}
gsi_insert_after (&bsi, call, GSI_NEW_STMT);
- if (!(gimple_call_flags (call) & ECF_NORETURN))
+ if (!alias_is_noreturn)
{
+ if (instrumentation_clone
+ && !DECL_BY_REFERENCE (resdecl)
+ && restmp
+ && BOUNDED_P (restmp))
+ {
+ resbnd = chkp_insert_retbnd_call (NULL, restmp, &bsi);
+ create_edge (get_create (gimple_call_fndecl (gsi_stmt (bsi))),
+ as_a <gcall *> (gsi_stmt (bsi)),
+ callees->count, callees->frequency);
+ }
+
if (restmp && !this_adjusting
&& (fixed_offset || virtual_offset))
{
if (TREE_CODE (TREE_TYPE (restmp)) == POINTER_TYPE)
{
gimple stmt;
+ edge e;
/* If the return type is a pointer, we need to
protect against NULL. We know there will be an
adjustment, because that's why we're emitting a
thunk. */
- then_bb = create_basic_block (NULL, (void *) 0, bb);
- return_bb = create_basic_block (NULL, (void *) 0, then_bb);
- else_bb = create_basic_block (NULL, (void *) 0, else_bb);
+ then_bb = create_basic_block (NULL, bb);
+ then_bb->count = count - count / 16;
+ then_bb->frequency = BB_FREQ_MAX - BB_FREQ_MAX / 16;
+ return_bb = create_basic_block (NULL, then_bb);
+ return_bb->count = count;
+ return_bb->frequency = BB_FREQ_MAX;
+ else_bb = create_basic_block (NULL, else_bb);
+ then_bb->count = count / 16;
+ then_bb->frequency = BB_FREQ_MAX / 16;
add_bb_to_loop (then_bb, bb->loop_father);
add_bb_to_loop (return_bb, bb->loop_father);
add_bb_to_loop (else_bb, bb->loop_father);
build_zero_cst (TREE_TYPE (restmp)),
NULL_TREE, NULL_TREE);
gsi_insert_after (&bsi, stmt, GSI_NEW_STMT);
- make_edge (bb, then_bb, EDGE_TRUE_VALUE);
- make_edge (bb, else_bb, EDGE_FALSE_VALUE);
- make_edge (return_bb, EXIT_BLOCK_PTR_FOR_FN (cfun), 0);
- make_edge (then_bb, return_bb, EDGE_FALLTHRU);
- make_edge (else_bb, return_bb, EDGE_FALLTHRU);
+ e = make_edge (bb, then_bb, EDGE_TRUE_VALUE);
+ e->probability = REG_BR_PROB_BASE - REG_BR_PROB_BASE / 16;
+ e->count = count - count / 16;
+ e = make_edge (bb, else_bb, EDGE_FALSE_VALUE);
+ e->probability = REG_BR_PROB_BASE / 16;
+ e->count = count / 16;
+ e = make_edge (return_bb, EXIT_BLOCK_PTR_FOR_FN (cfun), 0);
+ e->probability = REG_BR_PROB_BASE;
+ e->count = count;
+ e = make_edge (then_bb, return_bb, EDGE_FALLTHRU);
+ e->probability = REG_BR_PROB_BASE;
+ e->count = count - count / 16;
+ e = make_edge (else_bb, return_bb, EDGE_FALLTHRU);
+ e->probability = REG_BR_PROB_BASE;
+ e->count = count / 16;
bsi = gsi_last_bb (then_bb);
}
gimple_call_set_tail (call, true);
/* Build return value. */
- ret = gimple_build_return (restmp);
+ if (!DECL_BY_REFERENCE (resdecl))
+ ret = gimple_build_return (restmp);
+ else
+ ret = gimple_build_return (resdecl);
+ gimple_return_set_retbnd (ret, resbnd);
+
gsi_insert_after (&bsi, ret, GSI_NEW_STMT);
}
else
}
cfun->gimple_df->in_ssa_p = true;
+ profile_status_for_fn (cfun)
+ = count ? PROFILE_READ : PROFILE_GUESSED;
/* FIXME: C++ FE should stop setting TREE_ASM_WRITTEN on thunks. */
TREE_ASM_WRITTEN (thunk_fndecl) = false;
delete_unreachable_blocks ();
#ifdef ENABLE_CHECKING
verify_flow_info ();
#endif
+ free_dominance_info (CDI_DOMINATORS);
/* Since we want to emit the thunk, we explicitly mark its name as
referenced. */
- node->thunk.thunk_p = false;
- node->lowered = true;
+ thunk.thunk_p = false;
+ lowered = true;
bitmap_obstack_release (NULL);
}
current_function_decl = NULL;
return true;
}
-/* Assemble thunks and aliases associated to NODE. */
+/* Assemble thunks and aliases associated to node. */
-static void
-assemble_thunks_and_aliases (struct cgraph_node *node)
+void
+cgraph_node::assemble_thunks_and_aliases (void)
{
- struct cgraph_edge *e;
- int i;
- struct ipa_ref *ref;
+ cgraph_edge *e;
+ ipa_ref *ref;
- for (e = node->callers; e;)
- if (e->caller->thunk.thunk_p)
+ for (e = callers; e;)
+ if (e->caller->thunk.thunk_p
+ && !e->caller->thunk.add_pointer_bounds_args)
{
- struct cgraph_node *thunk = e->caller;
+ cgraph_node *thunk = e->caller;
e = e->next_caller;
- assemble_thunks_and_aliases (thunk);
- expand_thunk (thunk, true);
+ thunk->expand_thunk (true, false);
+ thunk->assemble_thunks_and_aliases ();
}
else
e = e->next_caller;
- for (i = 0; ipa_ref_list_referring_iterate (&node->ref_list,
- i, ref); i++)
- if (ref->use == IPA_REF_ALIAS)
- {
- struct cgraph_node *alias = ipa_ref_referring_node (ref);
- bool saved_written = TREE_ASM_WRITTEN (node->decl);
-
- /* Force assemble_alias to really output the alias this time instead
- of buffering it in same alias pairs. */
- TREE_ASM_WRITTEN (node->decl) = 1;
- do_assemble_alias (alias->decl,
- DECL_ASSEMBLER_NAME (node->decl));
- assemble_thunks_and_aliases (alias);
- TREE_ASM_WRITTEN (node->decl) = saved_written;
- }
+
+ FOR_EACH_ALIAS (this, ref)
+ {
+ cgraph_node *alias = dyn_cast <cgraph_node *> (ref->referring);
+ bool saved_written = TREE_ASM_WRITTEN (decl);
+
+ /* Force assemble_alias to really output the alias this time instead
+ of buffering it in same alias pairs. */
+ TREE_ASM_WRITTEN (decl) = 1;
+ do_assemble_alias (alias->decl,
+ DECL_ASSEMBLER_NAME (decl));
+ alias->assemble_thunks_and_aliases ();
+ TREE_ASM_WRITTEN (decl) = saved_written;
+ }
}
-/* Expand function specified by NODE. */
+/* Expand function specified by node. */
-static void
-expand_function (struct cgraph_node *node)
+void
+cgraph_node::expand (void)
{
- tree decl = node->decl;
location_t saved_loc;
/* We ought to not compile any inline clones. */
- gcc_assert (!node->global.inlined_to);
+ gcc_assert (!global.inlined_to);
announce_function (decl);
- node->process = 0;
- gcc_assert (node->lowered);
- cgraph_get_body (node);
+ process = 0;
+ gcc_assert (lowered);
+ get_untransformed_body ();
/* Generate RTL for the body of DECL. */
timevar_push (TV_REST_OF_COMPILATION);
- gcc_assert (cgraph_global_info_ready);
+ gcc_assert (symtab->global_info_ready);
/* Initialize the default bitmap obstack. */
bitmap_obstack_initialize (NULL);
/* Signal the start of passes. */
invoke_plugin_callbacks (PLUGIN_ALL_PASSES_START, NULL);
- execute_pass_list (g->get_passes ()->all_passes);
+ execute_pass_list (cfun, g->get_passes ()->all_passes);
/* Signal the end of passes. */
invoke_plugin_callbacks (PLUGIN_ALL_PASSES_END, NULL);
gimple_set_body (decl, NULL);
if (DECL_STRUCT_FUNCTION (decl) == 0
- && !cgraph_get_node (decl)->origin)
+ && !cgraph_node::get (decl)->origin)
{
/* Stop pointing to the local nodes about to be freed.
But DECL_INITIAL must remain nonzero so we know this
make one pass assemblers, like one on AIX, happy. See PR 50689.
FIXME: Perhaps thunks should be move before function IFF they are not in comdat
groups. */
- assemble_thunks_and_aliases (node);
- cgraph_release_function_body (node);
+ assemble_thunks_and_aliases ();
+ release_body ();
/* Eliminate all call edges. This is important so the GIMPLE_CALL no longer
points to the dead function body. */
- cgraph_node_remove_callees (node);
- ipa_remove_all_references (&node->ref_list);
+ remove_callees ();
+ remove_all_references ();
}
/* Node comparer that is responsible for the order that corresponds
static int
node_cmp (const void *pa, const void *pb)
{
- const struct cgraph_node *a = *(const struct cgraph_node * const *) pa;
- const struct cgraph_node *b = *(const struct cgraph_node * const *) pb;
+ const cgraph_node *a = *(const cgraph_node * const *) pa;
+ const cgraph_node *b = *(const cgraph_node * const *) pb;
/* Functions with time profile must be before these without profile. */
if (!a->tp_first_run || !b->tp_first_run)
static void
expand_all_functions (void)
{
- struct cgraph_node *node;
- struct cgraph_node **order = XCNEWVEC (struct cgraph_node *, cgraph_n_nodes);
+ cgraph_node *node;
+ cgraph_node **order = XCNEWVEC (cgraph_node *,
+ symtab->cgraph_count);
unsigned int expanded_func_count = 0, profiled_func_count = 0;
int order_pos, new_order_pos = 0;
int i;
order_pos = ipa_reverse_postorder (order);
- gcc_assert (order_pos == cgraph_n_nodes);
+ gcc_assert (order_pos == symtab->cgraph_count);
/* Garbage collector may remove inline clones we eliminate during
optimization. So we must be sure to not reference them. */
order[new_order_pos++] = order[i];
if (flag_profile_reorder_functions)
- qsort (order, new_order_pos, sizeof (struct cgraph_node *), node_cmp);
+ qsort (order, new_order_pos, sizeof (cgraph_node *), node_cmp);
for (i = new_order_pos - 1; i >= 0; i--)
{
if (node->process)
{
- expanded_func_count++;
- if(node->tp_first_run)
- profiled_func_count++;
-
- if (cgraph_dump_file)
- fprintf (cgraph_dump_file, "Time profile order in expand_all_functions:%s:%d\n", node->asm_name (), node->tp_first_run);
-
+ expanded_func_count++;
+ if(node->tp_first_run)
+ profiled_func_count++;
+
+ if (symtab->dump_file)
+ fprintf (symtab->dump_file,
+ "Time profile order in expand_all_functions:%s:%d\n",
+ node->asm_name (), node->tp_first_run);
node->process = 0;
- expand_function (node);
+ node->expand ();
}
}
fprintf (dump_file, "Expanded functions with time profile (%s):%u/%u\n",
main_input_filename, profiled_func_count, expanded_func_count);
- if (cgraph_dump_file && flag_profile_reorder_functions)
- fprintf (cgraph_dump_file, "Expanded functions with time profile:%u/%u\n",
+ if (symtab->dump_file && flag_profile_reorder_functions)
+ fprintf (symtab->dump_file, "Expanded functions with time profile:%u/%u\n",
profiled_func_count, expanded_func_count);
- cgraph_process_new_functions ();
+ symtab->process_new_functions ();
free_gimplify_stack ();
free (order);
enum cgraph_order_sort_kind kind;
union
{
- struct cgraph_node *f;
+ cgraph_node *f;
varpool_node *v;
- struct asm_node *a;
+ asm_node *a;
} u;
};
according to their order fields, which is the order in which they
appeared in the file. This implements -fno-toplevel-reorder. In
this mode we may output functions and variables which don't really
- need to be output. */
+ need to be output.
+ When NO_REORDER is true only do this for symbols marked no reorder. */
static void
-output_in_order (void)
+output_in_order (bool no_reorder)
{
int max;
- struct cgraph_order_sort *nodes;
+ cgraph_order_sort *nodes;
int i;
- struct cgraph_node *pf;
+ cgraph_node *pf;
varpool_node *pv;
- struct asm_node *pa;
-
- max = symtab_order;
- nodes = XCNEWVEC (struct cgraph_order_sort, max);
+ asm_node *pa;
+ max = symtab->order;
+ nodes = XCNEWVEC (cgraph_order_sort, max);
FOR_EACH_DEFINED_FUNCTION (pf)
{
if (pf->process && !pf->thunk.thunk_p && !pf->alias)
{
+ if (no_reorder && !pf->no_reorder)
+ continue;
i = pf->order;
gcc_assert (nodes[i].kind == ORDER_UNDEFINED);
nodes[i].kind = ORDER_FUNCTION;
FOR_EACH_DEFINED_VARIABLE (pv)
if (!DECL_EXTERNAL (pv->decl))
{
+ if (no_reorder && !pv->no_reorder)
+ continue;
i = pv->order;
gcc_assert (nodes[i].kind == ORDER_UNDEFINED);
nodes[i].kind = ORDER_VAR;
nodes[i].u.v = pv;
}
- for (pa = asm_nodes; pa; pa = pa->next)
+ for (pa = symtab->first_asm_symbol (); pa; pa = pa->next)
{
i = pa->order;
gcc_assert (nodes[i].kind == ORDER_UNDEFINED);
for (i = 0; i < max; ++i)
if (nodes[i].kind == ORDER_VAR)
- varpool_finalize_named_section_flags (nodes[i].u.v);
+ nodes[i].u.v->finalize_named_section_flags ();
for (i = 0; i < max; ++i)
{
{
case ORDER_FUNCTION:
nodes[i].u.f->process = 0;
- expand_function (nodes[i].u.f);
+ nodes[i].u.f->expand ();
break;
case ORDER_VAR:
- varpool_assemble_decl (nodes[i].u.v);
+ nodes[i].u.v->assemble_decl ();
break;
case ORDER_ASM:
}
}
- asm_nodes = NULL;
+ symtab->clear_asm_symbols ();
+
free (nodes);
}
return;
}
- /* We never run removal of unreachable nodes after early passes. This is
- because TODO is run before the subpasses. It is important to remove
- the unreachable functions to save works at IPA level and to get LTO
- symbol tables right. */
- symtab_remove_unreachable_nodes (true, cgraph_dump_file);
+ /* This extra symtab_remove_unreachable_nodes pass tends to catch some
+ devirtualization and other changes where removal iterate. */
+ symtab->remove_unreachable_nodes (symtab->dump_file);
/* If pass_all_early_optimizations was not scheduled, the state of
the cgraph will not be properly updated. Update it now. */
- if (cgraph_state < CGRAPH_STATE_IPA_SSA)
- cgraph_state = CGRAPH_STATE_IPA_SSA;
+ if (symtab->state < IPA_SSA)
+ symtab->state = IPA_SSA;
if (!in_lto_p)
{
/* Process new functions added. */
set_cfun (NULL);
current_function_decl = NULL;
- cgraph_process_new_functions ();
+ symtab->process_new_functions ();
execute_ipa_summary_passes
((ipa_opt_pass_d *) passes->all_regular_ipa_passes);
}
/* Some targets need to handle LTO assembler output specially. */
- if (flag_generate_lto)
+ if (flag_generate_lto || flag_generate_offload)
targetm.asm_out.lto_start ();
if (!in_lto_p)
- ipa_write_summaries ();
+ {
+ if (g->have_offload)
+ {
+ section_name_prefix = OFFLOAD_SECTION_NAME_PREFIX;
+ lto_stream_offload_p = true;
+ ipa_write_summaries ();
+ lto_stream_offload_p = false;
+ }
+ if (flag_lto)
+ {
+ section_name_prefix = LTO_SECTION_NAME_PREFIX;
+ lto_stream_offload_p = false;
+ ipa_write_summaries ();
+ }
+ }
- if (flag_generate_lto)
+ if (flag_generate_lto || flag_generate_offload)
targetm.asm_out.lto_end ();
if (!flag_ltrans && (in_lto_p || !flag_lto || flag_fat_lto_objects))
/* Weakrefs may be associated to external decls and thus not output
at expansion time. Emit all necessary aliases. */
-static void
-output_weakrefs (void)
+void
+symbol_table::output_weakrefs (void)
{
symtab_node *node;
+ cgraph_node *cnode;
FOR_EACH_SYMBOL (node)
if (node->alias
&& !TREE_ASM_WRITTEN (node->decl)
+ && (!(cnode = dyn_cast <cgraph_node *> (node))
+ || !cnode->instrumented_version
+ || !TREE_ASM_WRITTEN (cnode->instrumented_version->decl))
&& node->weakref)
{
tree target;
? DECL_ASSEMBLER_NAME (node->alias_target)
: node->alias_target);
else if (node->analyzed)
- target = DECL_ASSEMBLER_NAME (symtab_alias_target (node)->decl);
+ target = DECL_ASSEMBLER_NAME (node->get_alias_target ()->decl);
else
{
gcc_unreachable ();
}
}
-/* Initialize callgraph dump file. */
-
-void
-init_cgraph (void)
-{
- if (!cgraph_dump_file)
- cgraph_dump_file = dump_begin (TDI_cgraph, NULL);
-}
-
-
/* Perform simple optimizations based on callgraph. */
void
-compile (void)
+symbol_table::compile (void)
{
if (seen_error ())
return;
#ifdef ENABLE_CHECKING
- verify_symtab ();
+ symtab_node::verify_symtab_nodes ();
#endif
timevar_push (TV_CGRAPHOPT);
}
if (!quiet_flag)
fprintf (stderr, "Performing interprocedural optimizations\n");
- cgraph_state = CGRAPH_STATE_IPA;
+ state = IPA;
+
+ /* Offloading requires LTO infrastructure. */
+ if (!in_lto_p && g->have_offload)
+ flag_generate_offload = 1;
/* If LTO is enabled, initialize the streamer hooks needed by GIMPLE. */
- if (flag_lto)
+ if (flag_generate_lto || flag_generate_offload)
lto_streamer_hooks_init ();
/* Don't run the IPA passes if there was any error or sorry messages. */
return;
}
- /* This pass remove bodies of extern inline functions we never inlined.
- Do this later so other IPA passes see what is really going on. */
- symtab_remove_unreachable_nodes (false, dump_file);
- cgraph_global_info_ready = true;
- if (cgraph_dump_file)
+ global_info_ready = true;
+ if (dump_file)
{
- fprintf (cgraph_dump_file, "Optimized ");
- dump_symtab (cgraph_dump_file);
+ fprintf (dump_file, "Optimized ");
+ symtab_node:: dump_table (dump_file);
}
if (post_ipa_mem_report)
{
if (!quiet_flag)
fprintf (stderr, "Assembling functions:\n");
#ifdef ENABLE_CHECKING
- verify_symtab ();
+ symtab_node::verify_symtab_nodes ();
#endif
- cgraph_materialize_all_clones ();
+ materialize_all_clones ();
bitmap_obstack_initialize (NULL);
execute_ipa_pass_list (g->get_passes ()->all_late_ipa_passes);
- symtab_remove_unreachable_nodes (true, dump_file);
-#ifdef ENABLE_CHECKING
- verify_symtab ();
-#endif
bitmap_obstack_release (NULL);
mark_functions_to_output ();
(DECL_ASSEMBLER_NAME (node->decl)) = 1;
TREE_CHAIN (DECL_ASSEMBLER_NAME (node->decl))
= (node->alias_target ? node->alias_target
- : DECL_ASSEMBLER_NAME (symtab_alias_target (node)->decl));
+ : DECL_ASSEMBLER_NAME (node->get_alias_target ()->decl));
}
#endif
- cgraph_state = CGRAPH_STATE_EXPANSION;
+ state = EXPANSION;
if (!flag_toplevel_reorder)
- output_in_order ();
+ output_in_order (false);
else
{
- output_asm_statements ();
-
+ /* Output first asm statements and anything ordered. The process
+ flag is cleared for these nodes, so we skip them later. */
+ output_in_order (true);
expand_all_functions ();
- varpool_output_variables ();
+ output_variables ();
}
- cgraph_process_new_functions ();
- cgraph_state = CGRAPH_STATE_FINISHED;
+ process_new_functions ();
+ state = FINISHED;
output_weakrefs ();
- if (cgraph_dump_file)
+ if (dump_file)
{
- fprintf (cgraph_dump_file, "\nFinal ");
- dump_symtab (cgraph_dump_file);
+ fprintf (dump_file, "\nFinal ");
+ symtab_node::dump_table (dump_file);
}
#ifdef ENABLE_CHECKING
- verify_symtab ();
+ symtab_node::verify_symtab_nodes ();
/* Double check that all inline clones are gone and that all
function bodies have been released from memory. */
if (!seen_error ())
{
- struct cgraph_node *node;
+ cgraph_node *node;
bool error_found = false;
FOR_EACH_DEFINED_FUNCTION (node)
|| gimple_has_body_p (node->decl))
{
error_found = true;
- dump_cgraph_node (stderr, node);
+ node->debug ();
}
if (error_found)
internal_error ("nodes with unreleased memory found");
/* Analyze the whole compilation unit once it is parsed completely. */
void
-finalize_compilation_unit (void)
+symbol_table::finalize_compilation_unit (void)
{
timevar_push (TV_CGRAPH);
/* Gimplify and lower all functions, compute reachability and
remove unreachable nodes. */
- analyze_functions ();
+ analyze_functions (/*first_time=*/true);
/* Mark alias targets necessary and emit diagnostics. */
handle_alias_pairs ();
/* Gimplify and lower thunks. */
- analyze_functions ();
+ analyze_functions (/*first_time=*/false);
+
+ /* Emit early debug for reachable functions, and by consequence,
+ locally scoped symbols. */
+ struct cgraph_node *cnode;
+ FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (cnode)
+ (*debug_hooks->early_global_decl) (cnode->decl);
+
+ /* Clean up anything that needs cleaning up after initial debug
+ generation. */
+ (*debug_hooks->early_finish) ();
/* Finally drive the pass manager. */
compile ();
timevar_pop (TV_CGRAPH);
}
+/* Reset all state within cgraphunit.c so that we can rerun the compiler
+ within the same process. For use by toplev::finalize. */
+
+void
+cgraphunit_c_finalize (void)
+{
+ gcc_assert (cgraph_new_nodes.length () == 0);
+ cgraph_new_nodes.truncate (0);
+
+ vtable_entry_type = NULL;
+ queued_nodes = &symtab_terminator;
+
+ first_analyzed = NULL;
+ first_analyzed_var = NULL;
+}
+
+/* Creates a wrapper from cgraph_node to TARGET node. Thunk is used for this
+ kind of wrapper method. */
+
+void
+cgraph_node::create_wrapper (cgraph_node *target)
+{
+ /* Preserve DECL_RESULT so we get right by reference flag. */
+ tree decl_result = DECL_RESULT (decl);
+
+ /* Remove the function's body but keep arguments to be reused
+ for thunk. */
+ release_body (true);
+ reset ();
+
+ DECL_UNINLINABLE (decl) = false;
+ DECL_RESULT (decl) = decl_result;
+ DECL_INITIAL (decl) = NULL;
+ allocate_struct_function (decl, false);
+ set_cfun (NULL);
+
+ /* Turn alias into thunk and expand it into GIMPLE representation. */
+ definition = true;
+
+ memset (&thunk, 0, sizeof (cgraph_thunk_info));
+ thunk.thunk_p = true;
+ create_edge (target, NULL, count, CGRAPH_FREQ_BASE);
+
+ tree arguments = DECL_ARGUMENTS (decl);
+
+ while (arguments)
+ {
+ TREE_ADDRESSABLE (arguments) = false;
+ arguments = TREE_CHAIN (arguments);
+ }
+
+ expand_thunk (false, true);
+
+ /* Inline summary set-up. */
+ analyze ();
+ inline_analyze_function (this);
+}
#include "gt-cgraphunit.h"