/* Driver of optimization process
- Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
- 2011, 2012 Free Software Foundation, Inc.
+ Copyright (C) 2003-2013 Free Software Foundation, Inc.
Contributed by Jan Hubicka
This file is part of GCC.
(There is one exception needed for implementing GCC extern inline
function.)
- - varpool_finalize_variable
+ - varpool_finalize_decl
This function has same behavior as the above but is used for static
variables.
The symbol table is constructed starting from the trivially needed
symbols finalized by the frontend. Functions are lowered into
GIMPLE representation and callgraph/reference lists are constructed.
- Those are used to discover other neccesary functions and variables.
+ Those are used to discover other necessary functions and variables.
At the end the bodies of unreachable functions are removed.
#include "target.h"
#include "cgraph.h"
#include "diagnostic.h"
-#include "timevar.h"
#include "params.h"
#include "fibheap.h"
#include "intl.h"
#include "ipa-utils.h"
#include "lto-streamer.h"
#include "except.h"
+#include "cfgloop.h"
#include "regset.h" /* FIXME: For reg_obstack. */
/* Queue of cgraph nodes scheduled to be added into cgraph. This is a
static void expand_all_functions (void);
static void mark_functions_to_output (void);
static void expand_function (struct cgraph_node *);
-static void cgraph_analyze_function (struct cgraph_node *);
+static void analyze_function (struct cgraph_node *);
+static void handle_alias_pairs (void);
FILE *cgraph_dump_file;
/* Used for vtable lookup in thunk adjusting. */
static GTY (()) tree vtable_entry_type;
-/* Determine if function DECL is trivially needed and should stay in the
- compilation unit. This is used at the symbol table construction time
- and differs from later logic removing unnecesary functions that can
- take into account results of analysis, whole program info etc. */
-
-static bool
-cgraph_decide_is_function_needed (struct cgraph_node *node, tree decl)
+/* Determine if symbol DECL 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)
{
- /* If the user told us it is used, then it must be so. */
- if (node->symbol.force_output)
- return true;
+ tree decl = node->symbol.decl;
/* Double check that no one output the function into assembly file
early. */
gcc_checking_assert (!DECL_ASSEMBLER_NAME_SET_P (decl)
- || (node->thunk.thunk_p || node->same_body_alias)
- || !TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (decl)));
+ || !TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (decl)));
+ if (!node->symbol.definition)
+ return false;
- /* Keep constructors, destructors and virtual functions. */
- if (DECL_STATIC_CONSTRUCTOR (decl)
- || DECL_STATIC_DESTRUCTOR (decl)
- || (DECL_VIRTUAL_P (decl)
- && optimize && (DECL_COMDAT (decl) || DECL_EXTERNAL (decl))))
- return true;
+ /* Devirtualization may access these. */
+ if (DECL_VIRTUAL_P (decl) && optimize)
+ return true;
- /* Externally visible functions must be output. The exception is
- COMDAT functions that must be output only when they are needed. */
+ if (DECL_EXTERNAL (decl))
+ return false;
- if (TREE_PUBLIC (decl)
- && !DECL_COMDAT (decl) && !DECL_EXTERNAL (decl))
+ /* If the user told us it is used, then it must be so. */
+ if (node->symbol.force_output)
+ return true;
+
+ /* ABI forced symbols are needed when they are external. */
+ if (node->symbol.forced_by_abi && TREE_PUBLIC (decl))
+ return true;
+
+ /* Keep constructors, destructors and virtual functions. */
+ if (TREE_CODE (decl) == FUNCTION_DECL
+ && (DECL_STATIC_CONSTRUCTOR (decl) || DECL_STATIC_DESTRUCTOR (decl)))
+ return true;
+
+ /* Externally visible variables must be output. The exception is
+ COMDAT variables that must be output only when they are needed. */
+ if (TREE_PUBLIC (decl) && !DECL_COMDAT (decl))
return true;
return false;
if (!cgraph_new_nodes)
return false;
+ 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))
cgraph but not on this function. */
gimple_register_cfg_hooks ();
- if (!node->analyzed)
- cgraph_analyze_function (node);
+ if (!node->symbol.analyzed)
+ analyze_function (node);
push_cfun (DECL_STRUCT_FUNCTION (fndecl));
- current_function_decl = fndecl;
if ((cgraph_state == CGRAPH_STATE_IPA_SSA
&& !gimple_in_ssa_p (DECL_STRUCT_FUNCTION (fndecl)))
/* When not optimizing, be sure we run early local passes anyway
free_dominance_info (CDI_POST_DOMINATORS);
free_dominance_info (CDI_DOMINATORS);
pop_cfun ();
- current_function_decl = NULL;
cgraph_call_function_insertion_hooks (node);
break;
??? It may make more sense to use one body for inlining and other
body for expanding the function but this is difficult to do. */
-static void
+void
cgraph_reset_node (struct cgraph_node *node)
{
/* If node->process is set, then we have already begun whole-unit analysis.
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->local.finalized = false;
+ node->symbol.analyzed = false;
+ node->symbol.definition = false;
+ node->symbol.alias = false;
+ node->symbol.weakref = false;
+ node->symbol.cpp_implicit_alias = false;
cgraph_node_remove_callees (node);
+ ipa_remove_all_references (&node->symbol.ref_list);
}
/* Return true when there are references to NODE. */
{
struct ipa_ref *ref;
- /* See if there are any refrences at all. */
+ /* See if there are any references at all. */
if (ipa_ref_list_referring_iterate (&node->symbol.ref_list, 0, ref))
return true;
/* For functions check also calls. */
- if (symtab_function_p (node) && cgraph (node)->callers)
+ cgraph_node *cn = dyn_cast <cgraph_node> (node);
+ if (cn && cn->callers)
return true;
return false;
}
{
struct cgraph_node *node = cgraph_get_create_node (decl);
- if (node->local.finalized)
+ if (node->symbol.definition)
{
cgraph_reset_node (node);
node->local.redefined_extern_inline = true;
}
notice_global_symbol (decl);
- node->local.finalized = true;
+ node->symbol.definition = true;
node->lowered = DECL_STRUCT_FUNCTION (decl)->cfg != NULL;
/* With -fkeep-inline-functions we are keeping all inline functions except
in the original implementation and it is unclear whether we want
to change the behavior here. */
if ((!optimize
- && !node->same_body_alias
+ && !node->symbol.cpp_implicit_alias
&& !DECL_DISREGARD_INLINE_LIMITS (decl)
&& !DECL_DECLARED_INLINE_P (decl)
&& !(DECL_CONTEXT (decl)
ggc_collect ();
if (cgraph_state == CGRAPH_STATE_CONSTRUCTION
- && (cgraph_decide_is_function_needed (node, decl)
+ && (decide_is_symbol_needed ((symtab_node) node)
|| referred_to_p ((symtab_node)node)))
enqueue_node ((symtab_node)node);
}
analyzing and compilation. */
node = cgraph_get_create_node (fndecl);
node->local.local = false;
- node->local.finalized = true;
+ node->symbol.definition = true;
node->symbol.force_output = true;
if (!lowered && cgraph_state == CGRAPH_STATE_EXPANSION)
{
push_cfun (DECL_STRUCT_FUNCTION (fndecl));
- current_function_decl = fndecl;
gimple_register_cfg_hooks ();
bitmap_obstack_initialize (NULL);
execute_pass_list (all_lowering_passes);
execute_pass_list (pass_early_local_passes.pass.sub);
bitmap_obstack_release (NULL);
pop_cfun ();
- current_function_decl = NULL;
lowered = true;
}
node = cgraph_create_node (fndecl);
if (lowered)
node->lowered = true;
- cgraph_analyze_function (node);
+ node->symbol.definition = true;
+ analyze_function (node);
push_cfun (DECL_STRUCT_FUNCTION (fndecl));
- current_function_decl = fndecl;
gimple_register_cfg_hooks ();
bitmap_obstack_initialize (NULL);
if (!gimple_in_ssa_p (DECL_STRUCT_FUNCTION (fndecl)))
bitmap_obstack_release (NULL);
pop_cfun ();
expand_function (node);
- current_function_decl = NULL;
break;
default:
asm_nodes = NULL;
}
-/* C++ FE sometimes change linkage flags after producing same body aliases. */
-void
-fixup_same_cpp_alias_visibility (symtab_node node, symtab_node target, tree alias)
-{
- DECL_VIRTUAL_P (node->symbol.decl) = DECL_VIRTUAL_P (alias);
- if (TREE_PUBLIC (node->symbol.decl))
- {
- DECL_EXTERNAL (node->symbol.decl) = DECL_EXTERNAL (alias);
- DECL_COMDAT (node->symbol.decl) = DECL_COMDAT (alias);
- DECL_COMDAT_GROUP (node->symbol.decl) = DECL_COMDAT_GROUP (alias);
- if (DECL_ONE_ONLY (alias)
- && !node->symbol.same_comdat_group)
- symtab_add_to_same_comdat_group ((symtab_node)node, (symtab_node)target);
- }
-}
-
/* Analyze the function scheduled to be output. */
static void
-cgraph_analyze_function (struct cgraph_node *node)
+analyze_function (struct cgraph_node *node)
{
- tree save = current_function_decl;
tree decl = node->symbol.decl;
location_t saved_loc = input_location;
input_location = DECL_SOURCE_LOCATION (decl);
- if (node->alias && node->thunk.alias)
- {
- struct cgraph_node *tgt = cgraph_get_node (node->thunk.alias);
- struct cgraph_node *n;
-
- for (n = tgt; n && n->alias;
- n = n->analyzed ? cgraph_alias_aliased_node (n) : NULL)
- if (n == node)
- {
- error ("function %q+D part of alias cycle", node->symbol.decl);
- node->alias = false;
- input_location = saved_loc;
- return;
- }
- if (!VEC_length (ipa_ref_t, node->symbol.ref_list.references))
- ipa_record_reference ((symtab_node)node, (symtab_node)tgt,
- IPA_REF_ALIAS, NULL);
- if (node->same_body_alias)
- {
- DECL_DECLARED_INLINE_P (node->symbol.decl)
- = DECL_DECLARED_INLINE_P (node->thunk.alias);
- DECL_DISREGARD_INLINE_LIMITS (node->symbol.decl)
- = DECL_DISREGARD_INLINE_LIMITS (node->thunk.alias);
- fixup_same_cpp_alias_visibility ((symtab_node) node, (symtab_node) tgt, node->thunk.alias);
- }
-
- if (node->symbol.address_taken)
- cgraph_mark_address_taken_node (cgraph_alias_aliased_node (node));
- }
+ if (node->symbol.alias)
+ symtab_resolve_alias
+ ((symtab_node) node, (symtab_node) cgraph_get_node (node->symbol.alias_target));
else if (node->thunk.thunk_p)
{
cgraph_create_edge (node, cgraph_get_node (node->thunk.alias),
NULL, 0, CGRAPH_FREQ_BASE);
+ node->thunk.alias = NULL;
+ }
+ else if (node->dispatcher_function)
+ {
+ /* Generate the dispatcher body of multi-versioned functions. */
+ struct cgraph_function_version_info *dispatcher_version_info
+ = get_cgraph_node_version (node);
+ 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);
+ gcc_assert (resolver != NULL_TREE);
+ }
}
else
{
- current_function_decl = decl;
push_cfun (DECL_STRUCT_FUNCTION (decl));
assign_assembler_name_if_neeeded (node->symbol.decl);
pop_cfun ();
}
- node->analyzed = true;
+ node->symbol.analyzed = true;
- current_function_decl = save;
input_location = saved_loc;
}
void
cgraph_process_same_body_aliases (void)
{
- struct cgraph_node *node;
- FOR_EACH_FUNCTION (node)
- if (node->same_body_alias
- && !VEC_length (ipa_ref_t, node->symbol.ref_list.references))
- {
- struct cgraph_node *tgt = cgraph_get_node (node->thunk.alias);
- ipa_record_reference ((symtab_node)node, (symtab_node)tgt,
- IPA_REF_ALIAS, NULL);
- }
- same_body_aliases_done = true;
+ symtab_node node;
+ FOR_EACH_SYMBOL (node)
+ if (node->symbol.cpp_implicit_alias && !node->symbol.analyzed)
+ symtab_resolve_alias
+ (node,
+ TREE_CODE (node->symbol.alias_target) == VAR_DECL
+ ? (symtab_node)varpool_node_for_decl (node->symbol.alias_target)
+ : (symtab_node)cgraph_get_create_node (node->symbol.alias_target));
+ cpp_implicit_aliases_done = true;
}
/* Process attributes common for vars and functions. */
" attribute have effect only on public objects");
}
if (lookup_attribute ("weakref", DECL_ATTRIBUTES (decl))
- && (node->local.finalized && !node->alias))
+ && (node->symbol.definition && !node->symbol.alias))
{
warning_at (DECL_SOURCE_LOCATION (node->symbol.decl), OPT_Wattributes,
"%<weakref%> attribute ignored"
vnode = varpool_next_variable (vnode))
{
tree decl = vnode->symbol.decl;
+ if (DECL_EXTERNAL (decl)
+ && DECL_INITIAL (decl))
+ varpool_finalize_decl (decl);
if (DECL_PRESERVE_P (decl))
vnode->symbol.force_output = true;
else if (lookup_attribute ("externally_visible", DECL_ATTRIBUTES (decl)))
" attribute have effect only on public objects");
}
if (lookup_attribute ("weakref", DECL_ATTRIBUTES (decl))
- && vnode->finalized
+ && vnode->symbol.definition
&& DECL_INITIAL (decl))
{
warning_at (DECL_SOURCE_LOCATION (vnode->symbol.decl), OPT_Wattributes,
void
varpool_finalize_decl (tree decl)
{
- struct varpool_node *node = varpool_node (decl);
+ struct varpool_node *node = varpool_node_for_decl (decl);
- gcc_assert (TREE_STATIC (decl));
+ gcc_assert (TREE_STATIC (decl) || DECL_EXTERNAL (decl));
- if (node->finalized)
+ if (node->symbol.definition)
return;
notice_global_symbol (decl);
- node->finalized = true;
+ node->symbol.definition = true;
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. */
node->symbol.force_output = true;
if (cgraph_state == CGRAPH_STATE_CONSTRUCTION
- && (decide_is_variable_needed (node, decl)
+ && (decide_is_symbol_needed ((symtab_node) node)
|| referred_to_p ((symtab_node)node)))
enqueue_node ((symtab_node)node);
if (cgraph_state >= CGRAPH_STATE_IPA_SSA)
varpool_assemble_decl (node);
}
+
/* Discover all functions and variables that are trivially needed, analyze
them as well as all functions and variables referred by them */
static void
-cgraph_analyze_functions (void)
+analyze_functions (void)
{
/* Keep track of already processed nodes when called multiple times for
intermodule optimization. */
bitmap_obstack_initialize (NULL);
cgraph_state = CGRAPH_STATE_CONSTRUCTION;
+ /* 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)
+ FOR_EACH_SYMBOL (node)
+ if (node->symbol.cpp_implicit_alias)
+ fixup_same_cpp_alias_visibility (node, symtab_alias_target (node));
+
/* Analysis adds static variables that in turn adds references to new functions.
So we need to iterate the process until it stabilize. */
while (changed)
node != (symtab_node)first_analyzed
&& node != (symtab_node)first_analyzed_var; node = node->symbol.next)
{
- if ((symtab_function_p (node)
- && cgraph (node)->local.finalized
- && cgraph_decide_is_function_needed (cgraph (node), node->symbol.decl))
- || (symtab_variable_p (node)
- && varpool (node)->finalized
- && !DECL_EXTERNAL (node->symbol.decl)
- && decide_is_variable_needed (varpool (node), node->symbol.decl)))
+ if (decide_is_symbol_needed (node))
{
enqueue_node (node);
if (!changed && cgraph_dump_file)
changed = true;
node = first;
first = (symtab_node)first->symbol.aux;
- if (symtab_function_p (node) && cgraph (node)->local.finalized)
+ cgraph_node *cnode = dyn_cast <cgraph_node> (node);
+ if (cnode && cnode->symbol.definition)
{
struct cgraph_edge *edge;
- struct cgraph_node *cnode;
- tree decl;
-
- cnode = cgraph (node);
- decl = cnode->symbol.decl;
+ tree decl = cnode->symbol.decl;
- /* ??? It is possible to create extern inline function and later using
- weak alias attribute to kill its body. See
- gcc.c-torture/compile/20011119-1.c */
+ /* ??? It is possible to create extern inline function
+ and later using weak alias attribute to kill its body.
+ See gcc.c-torture/compile/20011119-1.c */
if (!DECL_STRUCT_FUNCTION (decl)
- && (!cnode->alias || !cnode->thunk.alias)
- && !cnode->thunk.thunk_p)
+ && !cnode->symbol.alias
+ && !cnode->thunk.thunk_p
+ && !cnode->dispatcher_function)
{
cgraph_reset_node (cnode);
cnode->local.redefined_extern_inline = true;
continue;
}
- if (!cnode->analyzed)
- cgraph_analyze_function (cnode);
+ if (!cnode->symbol.analyzed)
+ analyze_function (cnode);
for (edge = cnode->callees; edge; edge = edge->next_callee)
- if (edge->callee->local.finalized)
- enqueue_node ((symtab_node)edge->callee);
+ if (edge->callee->symbol.definition)
+ enqueue_node ((symtab_node)edge->callee);
- /* If decl is a clone of an abstract function, mark that abstract
- function so that we don't release its body. The DECL_INITIAL() of that
- abstract function declaration will be later needed to output debug
- info. */
+ /* If decl is a clone of an abstract function,
+ mark that abstract function so that we don't release its body.
+ The DECL_INITIAL() of that abstract function declaration
+ will be later needed to output debug info. */
if (DECL_ABSTRACT_ORIGIN (decl))
{
- struct cgraph_node *origin_node;
- origin_node = cgraph_get_node (DECL_ABSTRACT_ORIGIN (decl));
+ struct cgraph_node *origin_node
+ = cgraph_get_node (DECL_ABSTRACT_ORIGIN (decl));
origin_node->abstract_and_needed = true;
}
-
}
- else if (symtab_variable_p (node)
- && varpool (node)->finalized)
- varpool_analyze_node (varpool (node));
+ else
+ {
+ varpool_node *vnode = dyn_cast <varpool_node> (node);
+ if (vnode && vnode->symbol.definition && !vnode->symbol.analyzed)
+ varpool_analyze_node (vnode);
+ }
if (node->symbol.same_comdat_group)
{
enqueue_node (next);
}
for (i = 0; ipa_ref_list_reference_iterate (&node->symbol.ref_list, i, ref); i++)
- if ((symtab_function_p (ref->referred) && cgraph (ref->referred)->local.finalized)
- || (symtab_variable_p (ref->referred) && varpool (ref->referred)->finalized))
+ if (ref->referred->symbol.definition)
enqueue_node (ref->referred);
cgraph_process_new_functions ();
}
symtab_remove_node (node);
continue;
}
- if (symtab_function_p (node))
+ if (cgraph_node *cnode = dyn_cast <cgraph_node> (node))
{
tree decl = node->symbol.decl;
- struct cgraph_node *cnode = cgraph (node);
- if (cnode->local.finalized && !gimple_has_body_p (decl)
- && (!cnode->alias || !cnode->thunk.alias)
+ if (cnode->symbol.definition && !gimple_has_body_p (decl)
+ && !cnode->symbol.alias
&& !cnode->thunk.thunk_p)
cgraph_reset_node (cnode);
- gcc_assert (!cnode->local.finalized || cnode->thunk.thunk_p
- || cnode->alias
+ gcc_assert (!cnode->symbol.definition || cnode->thunk.thunk_p
+ || cnode->symbol.alias
|| gimple_has_body_p (decl));
- gcc_assert (cnode->analyzed == cnode->local.finalized);
+ gcc_assert (cnode->symbol.analyzed == cnode->symbol.definition);
}
node->symbol.aux = NULL;
}
{
alias_pair *p;
unsigned i;
- struct cgraph_node *target_node;
- struct cgraph_node *src_node;
- struct varpool_node *target_vnode;
- for (i = 0; VEC_iterate (alias_pair, alias_pairs, i, p);)
+ for (i = 0; alias_pairs && alias_pairs->iterate (i, &p);)
{
+ symtab_node target_node = symtab_node_for_asm (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 alias flag
+ to later output the weakref pseudo op into asm file. */
+ if (!target_node && lookup_attribute ("weakref", DECL_ATTRIBUTES (p->decl)) != NULL)
+ {
+ symtab_node node = symtab_get_node (p->decl);
+ if (node)
+ {
+ node->symbol.alias_target = p->target;
+ node->symbol.weakref = true;
+ node->symbol.alias = true;
+ }
+ alias_pairs->unordered_remove (i);
+ continue;
+ }
+ else if (!target_node)
+ {
+ error ("%q+D aliased to undefined symbol %qE", p->decl, p->target);
+ alias_pairs->unordered_remove (i);
+ continue;
+ }
+
+ if (DECL_EXTERNAL (target_node->symbol.decl)
+ /* 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->symbol.decl) == FUNCTION_DECL
+ || ! DECL_VIRTUAL_P (target_node->symbol.decl))
+ && ! lookup_attribute ("weakref", DECL_ATTRIBUTES (p->decl)))
+ {
+ error ("%q+D aliased to external symbol %qE",
+ p->decl, p->target);
+ }
+
if (TREE_CODE (p->decl) == FUNCTION_DECL
- && (target_node = cgraph_node_for_asm (p->target)) != NULL)
+ && target_node && is_a <cgraph_node> (target_node))
{
- src_node = cgraph_get_node (p->decl);
- if (src_node && src_node->local.finalized)
+ struct cgraph_node *src_node = cgraph_get_node (p->decl);
+ if (src_node && src_node->symbol.definition)
cgraph_reset_node (src_node);
- /* Normally EXTERNAL flag is used to mark external inlines,
- however for aliases it seems to be allowed to use it w/o
- any meaning. See gcc.dg/attr-alias-3.c
- However for weakref we insist on EXTERNAL flag being set.
- See gcc.dg/attr-alias-5.c */
- if (DECL_EXTERNAL (p->decl))
- DECL_EXTERNAL (p->decl)
- = lookup_attribute ("weakref",
- DECL_ATTRIBUTES (p->decl)) != NULL;
cgraph_create_function_alias (p->decl, target_node->symbol.decl);
- VEC_unordered_remove (alias_pair, alias_pairs, i);
+ alias_pairs->unordered_remove (i);
}
else if (TREE_CODE (p->decl) == VAR_DECL
- && (target_vnode = varpool_node_for_asm (p->target)) != NULL)
+ && target_node && is_a <varpool_node> (target_node))
{
- /* Normally EXTERNAL flag is used to mark external inlines,
- however for aliases it seems to be allowed to use it w/o
- any meaning. See gcc.dg/attr-alias-3.c
- However for weakref we insist on EXTERNAL flag being set.
- See gcc.dg/attr-alias-5.c */
- if (DECL_EXTERNAL (p->decl))
- DECL_EXTERNAL (p->decl)
- = lookup_attribute ("weakref",
- DECL_ATTRIBUTES (p->decl)) != NULL;
- varpool_create_variable_alias (p->decl, target_vnode->symbol.decl);
- VEC_unordered_remove (alias_pair, alias_pairs, i);
- }
- /* Weakrefs with target not defined in current unit are easy to handle; they
- behave just as external variables except we need to note the alias flag
- to later output the weakref pseudo op into asm file. */
- else if (lookup_attribute ("weakref", DECL_ATTRIBUTES (p->decl)) != NULL
- && (TREE_CODE (p->decl) == FUNCTION_DECL
- ? (varpool_node_for_asm (p->target) == NULL)
- : (cgraph_node_for_asm (p->target) == NULL)))
- {
- if (TREE_CODE (p->decl) == FUNCTION_DECL)
- cgraph_get_create_node (p->decl)->alias = true;
- else
- varpool_get_node (p->decl)->alias = true;
- DECL_EXTERNAL (p->decl) = 1;
- VEC_unordered_remove (alias_pair, alias_pairs, i);
+ varpool_create_variable_alias (p->decl, target_node->symbol.decl);
+ alias_pairs->unordered_remove (i);
}
else
{
- if (dump_file)
- fprintf (dump_file, "Unhandled alias %s->%s\n",
- IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (p->decl)),
- IDENTIFIER_POINTER (p->target));
-
- i++;
+ error ("%q+D alias in between function and variable is not supported",
+ p->decl);
+ warning (0, "%q+D aliased declaration",
+ target_node->symbol.decl);
+ alias_pairs->unordered_remove (i);
}
}
+ vec_free (alias_pairs);
}
/* We need to output all local functions that are used and not
always inlined, as well as those that are reachable from
outside the current compilation unit. */
- if (node->analyzed
+ if (node->symbol.analyzed
&& !node->thunk.thunk_p
- && !node->alias
+ && !node->symbol.alias
&& !node->global.inlined_to
&& !TREE_ASM_WRITTEN (decl)
&& !DECL_EXTERNAL (decl))
for (next = cgraph (node->symbol.same_comdat_group);
next != node;
next = cgraph (next->symbol.same_comdat_group))
- if (!next->thunk.thunk_p && !next->alias)
+ if (!next->thunk.thunk_p && !next->symbol.alias)
next->process = 1;
}
}
are inside partition, we can end up not removing the body since we no longer
have analyzed node pointing to it. */
&& !node->symbol.in_other_partition
- && !node->alias
+ && !node->symbol.alias
+ && !node->clones
&& !DECL_EXTERNAL (decl))
{
dump_cgraph_node (stderr, node);
gcc_assert (node->global.inlined_to
|| !gimple_has_body_p (decl)
|| node->symbol.in_other_partition
+ || node->clones
+ || DECL_ARTIFICIAL (decl)
|| DECL_EXTERNAL (decl));
}
end up not removing the body since we no longer have an
analyzed node pointing to it. */
&& !node->symbol.in_other_partition
+ && !node->clones
&& !DECL_EXTERNAL (decl))
{
dump_cgraph_node (stderr, node);
}
/* DECL is FUNCTION_DECL. Initialize datastructures so DECL is a function
- in lowered gimple form.
+ in lowered gimple form. IN_SSA is true if the gimple is in SSA.
Set current_function_decl and cfun to newly constructed empty function body.
return basic block in the function body. */
-static basic_block
-init_lowered_empty_function (tree decl)
+basic_block
+init_lowered_empty_function (tree decl, bool in_ssa)
{
basic_block bb;
allocate_struct_function (decl, false);
gimple_register_cfg_hooks ();
init_empty_tree_cfg ();
- init_tree_ssa (cfun);
- init_ssa_operands ();
- cfun->gimple_df->in_ssa_p = true;
+
+ if (in_ssa)
+ {
+ init_tree_ssa (cfun);
+ init_ssa_operands (cfun);
+ cfun->gimple_df->in_ssa_p = true;
+ cfun->curr_properties |= PROP_ssa;
+ }
+
DECL_INITIAL (decl) = make_node (BLOCK);
DECL_SAVED_TREE (decl) = error_mark_node;
- cfun->curr_properties |=
- (PROP_gimple_lcf | PROP_gimple_leh | PROP_cfg | PROP_referenced_vars |
- PROP_ssa | PROP_gimple_any);
+ 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 ());
+ 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);
- make_edge (ENTRY_BLOCK_PTR, bb, 0);
+ make_edge (ENTRY_BLOCK_PTR, bb, EDGE_FALLTHRU);
make_edge (bb, EXIT_BLOCK_PTR, 0);
+ add_bb_to_loop (bb, ENTRY_BLOCK_PTR->loop_father);
return bb;
}
}
vtabletmp =
- make_rename_temp (build_pointer_type
+ create_tmp_reg (build_pointer_type
(build_pointer_type (vtable_entry_type)), "vptr");
/* The vptr is always at offset zero in the object. */
gsi_insert_after (bsi, stmt, GSI_NEW_STMT);
/* Form the vtable address. */
- vtabletmp2 = make_rename_temp (TREE_TYPE (TREE_TYPE (vtabletmp)),
+ vtabletmp2 = create_tmp_reg (TREE_TYPE (TREE_TYPE (vtabletmp)),
"vtableaddr");
stmt = gimple_build_assign (vtabletmp2,
build_simple_mem_ref (vtabletmp));
gsi_insert_after (bsi, stmt, GSI_NEW_STMT);
/* Get the offset itself. */
- vtabletmp3 = make_rename_temp (TREE_TYPE (TREE_TYPE (vtabletmp2)),
+ vtabletmp3 = create_tmp_reg (TREE_TYPE (TREE_TYPE (vtabletmp2)),
"vcalloffset");
stmt = gimple_build_assign (vtabletmp3,
build_simple_mem_ref (vtabletmp2));
ptrtmp = ptr;
else
{
- ptrtmp = make_rename_temp (TREE_TYPE (ptr), "ptr");
+ ptrtmp = create_tmp_reg (TREE_TYPE (ptr), "ptr");
stmt = gimple_build_assign (ptrtmp, ptr);
gsi_insert_after (bsi, stmt, GSI_NEW_STMT);
}
}
/* Emit the statement and gimplify the adjustment expression. */
- ret = make_rename_temp (TREE_TYPE (ptr), "adjusted_this");
+ ret = create_tmp_reg (TREE_TYPE (ptr), "adjusted_this");
stmt = gimple_build_assign (ret, ptr);
gsi_insert_after (bsi, stmt, GSI_NEW_STMT);
HOST_WIDE_INT fixed_offset = node->thunk.fixed_offset;
HOST_WIDE_INT virtual_value = node->thunk.virtual_value;
tree virtual_offset = NULL;
- tree alias = node->thunk.alias;
+ tree alias = node->callees->callee->symbol.decl;
tree thunk_fndecl = node->symbol.decl;
tree a = DECL_ARGUMENTS (thunk_fndecl);
DECL_INITIAL (thunk_fndecl) = fn_block;
init_function_start (thunk_fndecl);
cfun->is_thunk = 1;
+ insn_locations_init ();
+ set_curr_insn_location (DECL_SOURCE_LOCATION (thunk_fndecl));
+ prologue_location = curr_insn_location ();
assemble_start_function (thunk_fndecl, fnname);
targetm.asm_out.output_mi_thunk (asm_out_file, thunk_fndecl,
fixed_offset, virtual_value, alias);
assemble_end_function (thunk_fndecl, fnname);
+ insn_locations_finalize ();
init_insn_lengths ();
free_after_compilation (cfun);
set_cfun (NULL);
TREE_ASM_WRITTEN (thunk_fndecl) = 1;
node->thunk.thunk_p = false;
- node->analyzed = false;
+ node->symbol.analyzed = false;
}
else
{
int i;
tree resdecl;
tree restmp = NULL;
- VEC(tree, heap) *vargs;
+ vec<tree> vargs;
gimple call;
gimple ret;
else
resdecl = DECL_RESULT (thunk_fndecl);
- bb = then_bb = else_bb = return_bb = init_lowered_empty_function (thunk_fndecl);
+ bb = then_bb = else_bb = return_bb = init_lowered_empty_function (thunk_fndecl, true);
bsi = gsi_start_bb (bb);
BLOCK_VARS (DECL_INITIAL (current_function_decl)) = restmp;
}
else
- restmp = make_rename_temp (restype, "retval");
+ restmp = create_tmp_reg (restype, "retval");
}
for (arg = a; arg; arg = DECL_CHAIN (arg))
nargs++;
- vargs = VEC_alloc (tree, heap, nargs);
+ vargs.create (nargs);
if (this_adjusting)
- VEC_quick_push (tree, vargs,
- thunk_adjust (&bsi,
- a, 1, fixed_offset,
- virtual_offset));
+ vargs.quick_push (thunk_adjust (&bsi, a, 1, fixed_offset,
+ virtual_offset));
else
- VEC_quick_push (tree, vargs, a);
- add_referenced_var (a);
- if (is_gimple_reg (a))
- mark_sym_for_renaming (a);
+ vargs.quick_push (a);
for (i = 1, arg = DECL_CHAIN (a); i < nargs; i++, arg = DECL_CHAIN (arg))
- {
- add_referenced_var (arg);
- if (is_gimple_reg (arg))
- mark_sym_for_renaming (arg);
- VEC_quick_push (tree, vargs, arg);
- }
+ vargs.quick_push (arg);
call = gimple_build_call_vec (build_fold_addr_expr_loc (0, alias), vargs);
- VEC_free (tree, heap, vargs);
+ vargs.release ();
gimple_call_set_from_thunk (call, true);
if (restmp)
gimple_call_set_lhs (call, restmp);
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);
+ 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);
remove_edge (single_succ_edge (bb));
true_label = gimple_block_label (then_bb);
stmt = gimple_build_cond (NE_EXPR, restmp,
bitmap_obstack_release (NULL);
}
current_function_decl = NULL;
+ set_cfun (NULL);
}
-/* Assemble thunks and aliases asociated to NODE. */
+/* Assemble thunks and aliases associated to NODE. */
static void
assemble_thunks_and_aliases (struct cgraph_node *node)
if (ref->use == IPA_REF_ALIAS)
{
struct cgraph_node *alias = ipa_ref_referring_node (ref);
- bool saved_written = TREE_ASM_WRITTEN (alias->thunk.alias);
+ bool saved_written = TREE_ASM_WRITTEN (node->symbol.decl);
/* Force assemble_alias to really output the alias this time instead
of buffering it in same alias pairs. */
- TREE_ASM_WRITTEN (alias->thunk.alias) = 1;
- assemble_alias (alias->symbol.decl,
- DECL_ASSEMBLER_NAME (alias->thunk.alias));
+ TREE_ASM_WRITTEN (node->symbol.decl) = 1;
+ do_assemble_alias (alias->symbol.decl,
+ DECL_ASSEMBLER_NAME (node->symbol.decl));
assemble_thunks_and_aliases (alias);
- TREE_ASM_WRITTEN (alias->thunk.alias) = saved_written;
+ TREE_ASM_WRITTEN (node->symbol.decl) = saved_written;
}
}
/* Release the default bitmap obstack. */
bitmap_obstack_release (NULL);
- set_cfun (NULL);
-
/* If requested, warn about function definitions where the function will
return a value (usually of some struct or union type) which itself will
take up a lot of stack space. */
/* Make sure that BE didn't give up on compiling. */
gcc_assert (TREE_ASM_WRITTEN (decl));
+ set_cfun (NULL);
current_function_decl = NULL;
/* It would make a lot more sense to output thunks before function body to get more
FOR_EACH_DEFINED_FUNCTION (pf)
{
- if (pf->process && !pf->thunk.thunk_p && !pf->alias)
+ if (pf->process && !pf->thunk.thunk_p && !pf->symbol.alias)
{
i = pf->symbol.order;
gcc_assert (nodes[i].kind == ORDER_UNDEFINED);
}
FOR_EACH_DEFINED_VARIABLE (pv)
- {
- i = pv->symbol.order;
- gcc_assert (nodes[i].kind == ORDER_UNDEFINED);
- nodes[i].kind = ORDER_VAR;
- nodes[i].u.v = pv;
- }
+ if (!DECL_EXTERNAL (pv->symbol.decl))
+ {
+ i = pv->symbol.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)
{
/* Weakrefs may be associated to external decls and thus not output
- at expansion time. Emit all neccesary aliases. */
+ at expansion time. Emit all necessary aliases. */
static void
output_weakrefs (void)
{
- struct cgraph_node *node;
- struct varpool_node *vnode;
- FOR_EACH_FUNCTION (node)
- if (node->alias && DECL_EXTERNAL (node->symbol.decl)
+ symtab_node node;
+ FOR_EACH_SYMBOL (node)
+ if (node->symbol.alias
&& !TREE_ASM_WRITTEN (node->symbol.decl)
- && lookup_attribute ("weakref", DECL_ATTRIBUTES (node->symbol.decl)))
- assemble_alias (node->symbol.decl,
- node->thunk.alias ? DECL_ASSEMBLER_NAME (node->thunk.alias)
- : get_alias_symbol (node->symbol.decl));
- FOR_EACH_VARIABLE (vnode)
- if (vnode->alias && DECL_EXTERNAL (vnode->symbol.decl)
- && !TREE_ASM_WRITTEN (vnode->symbol.decl)
- && lookup_attribute ("weakref", DECL_ATTRIBUTES (vnode->symbol.decl)))
- assemble_alias (vnode->symbol.decl,
- vnode->alias_of ? DECL_ASSEMBLER_NAME (vnode->alias_of)
- : get_alias_symbol (vnode->symbol.decl));
+ && node->symbol.weakref)
+ {
+ tree target;
+
+ /* Weakrefs are special by not requiring target definition in current
+ compilation unit. It is thus bit hard to work out what we want to
+ alias.
+ When alias target is defined, we need to fetch it from symtab reference,
+ otherwise it is pointed to by alias_target. */
+ if (node->symbol.alias_target)
+ target = (DECL_P (node->symbol.alias_target)
+ ? DECL_ASSEMBLER_NAME (node->symbol.alias_target)
+ : node->symbol.alias_target);
+ else if (node->symbol.analyzed)
+ target = DECL_ASSEMBLER_NAME (symtab_alias_target (node)->symbol.decl);
+ else
+ {
+ gcc_unreachable ();
+ target = get_alias_symbol (node->symbol.decl);
+ }
+ do_assemble_alias (node->symbol.decl, target);
+ }
}
/* Initialize callgraph dump file. */
#endif
bitmap_obstack_release (NULL);
mark_functions_to_output ();
- output_weakrefs ();
+
+ /* When weakref support is missing, we autmatically translate all
+ references to NODE to references to its ultimate alias target.
+ The renaming mechanizm uses flag IDENTIFIER_TRANSPARENT_ALIAS and
+ TREE_CHAIN.
+
+ Set up this mapping before we output any assembler but once we are sure
+ that all symbol renaming is done.
+
+ FIXME: All this uglyness can go away if we just do renaming at gimple
+ level by physically rewritting the IL. At the moment we can only redirect
+ calls, so we need infrastructure for renaming references as well. */
+#ifndef ASM_OUTPUT_WEAKREF
+ symtab_node node;
+
+ FOR_EACH_SYMBOL (node)
+ if (node->symbol.alias
+ && lookup_attribute ("weakref", DECL_ATTRIBUTES (node->symbol.decl)))
+ {
+ IDENTIFIER_TRANSPARENT_ALIAS
+ (DECL_ASSEMBLER_NAME (node->symbol.decl)) = 1;
+ TREE_CHAIN (DECL_ASSEMBLER_NAME (node->symbol.decl))
+ = (node->symbol.alias_target ? node->symbol.alias_target
+ : DECL_ASSEMBLER_NAME (symtab_alias_target (node)->symbol.decl));
+ }
+#endif
cgraph_state = CGRAPH_STATE_EXPANSION;
if (!flag_toplevel_reorder)
cgraph_process_new_functions ();
cgraph_state = CGRAPH_STATE_FINISHED;
+ output_weakrefs ();
if (cgraph_dump_file)
{
finalize_size_functions ();
/* Mark alias targets necessary and emit diagnostics. */
- finish_aliases_1 ();
handle_alias_pairs ();
if (!quiet_flag)
/* Gimplify and lower all functions, compute reachability and
remove unreachable nodes. */
- cgraph_analyze_functions ();
+ analyze_functions ();
/* Mark alias targets necessary and emit diagnostics. */
- finish_aliases_1 ();
handle_alias_pairs ();
/* Gimplify and lower thunks. */
- cgraph_analyze_functions ();
+ analyze_functions ();
/* Finally drive the pass manager. */
compile ();