X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=gcc%2Fcgraphunit.c;h=731a0e91294c9d5fd0eadb57049d26b34039ca5c;hb=6c84d5762f1e1a60b1d4cee0caf8b4e7e0ba5f6f;hp=c21ddb84c2f00d244c3d32a70b8d5836ab923db7;hpb=7d80ca1f439cd6be490336659740ca6d8a06db40;p=gcc.git diff --git a/gcc/cgraphunit.c b/gcc/cgraphunit.c index c21ddb84c2f..731a0e91294 100644 --- a/gcc/cgraphunit.c +++ b/gcc/cgraphunit.c @@ -1,6 +1,5 @@ /* 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. @@ -34,7 +33,7 @@ along with GCC; see the file COPYING3. If not see (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. @@ -51,7 +50,7 @@ along with GCC; see the file COPYING3. If not see 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. @@ -176,7 +175,6 @@ along with GCC; see the file COPYING3. If not see #include "target.h" #include "cgraph.h" #include "diagnostic.h" -#include "timevar.h" #include "params.h" #include "fibheap.h" #include "intl.h" @@ -194,6 +192,7 @@ along with GCC; see the file COPYING3. If not see #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 @@ -204,7 +203,8 @@ cgraph_node_set 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 cgraph_analyze_function (struct cgraph_node *); +static void analyze_function (struct cgraph_node *); +static void handle_alias_pairs (void); FILE *cgraph_dump_file; @@ -217,37 +217,45 @@ static GTY(()) struct asm_node *asm_last_node; /* 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; @@ -284,6 +292,7 @@ cgraph_process_new_functions (void) 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)) @@ -309,10 +318,9 @@ cgraph_process_new_functions (void) 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 @@ -324,7 +332,6 @@ cgraph_process_new_functions (void) free_dominance_info (CDI_POST_DOMINATORS); free_dominance_info (CDI_DOMINATORS); pop_cfun (); - current_function_decl = NULL; cgraph_call_function_insertion_hooks (node); break; @@ -356,7 +363,7 @@ cgraph_process_new_functions (void) ??? 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. @@ -370,10 +377,14 @@ cgraph_reset_node (struct cgraph_node *node) 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. */ @@ -383,11 +394,12 @@ referred_to_p (symtab_node 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 (node); + if (cn && cn->callers) return true; return false; } @@ -402,14 +414,14 @@ cgraph_finalize_function (tree decl, bool nested) { 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 @@ -426,7 +438,7 @@ cgraph_finalize_function (tree decl, bool nested) 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) @@ -446,7 +458,7 @@ cgraph_finalize_function (tree decl, bool nested) 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); } @@ -489,19 +501,17 @@ cgraph_add_new_function (tree fndecl, bool lowered) 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; } @@ -518,9 +528,9 @@ cgraph_add_new_function (tree fndecl, bool lowered) 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))) @@ -528,7 +538,6 @@ cgraph_add_new_function (tree fndecl, bool lowered) bitmap_obstack_release (NULL); pop_cfun (); expand_function (node); - current_function_decl = NULL; break; default: @@ -576,68 +585,40 @@ output_asm_statements (void) 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); @@ -669,9 +650,8 @@ cgraph_analyze_function (struct cgraph_node *node) pop_cfun (); } - node->analyzed = true; + node->symbol.analyzed = true; - current_function_decl = save; input_location = saved_loc; } @@ -684,16 +664,15 @@ cgraph_analyze_function (struct cgraph_node *node) 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. */ @@ -759,7 +738,7 @@ process_function_and_variable_attributes (struct cgraph_node *first, " 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, "% attribute ignored" @@ -782,6 +761,9 @@ process_function_and_variable_attributes (struct cgraph_node *first, 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))) @@ -792,7 +774,7 @@ process_function_and_variable_attributes (struct cgraph_node *first, " 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, @@ -813,14 +795,14 @@ process_function_and_variable_attributes (struct cgraph_node *first, 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. */ @@ -829,7 +811,7 @@ varpool_finalize_decl (tree decl) 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) @@ -840,11 +822,12 @@ varpool_finalize_decl (tree decl) 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. */ @@ -861,6 +844,13 @@ cgraph_analyze_functions (void) 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) @@ -874,13 +864,7 @@ cgraph_analyze_functions (void) 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) @@ -907,49 +891,49 @@ cgraph_analyze_functions (void) changed = true; node = first; first = (symtab_node)first->symbol.aux; - if (symtab_function_p (node) && cgraph (node)->local.finalized) + cgraph_node *cnode = dyn_cast (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 (node); + if (vnode && vnode->symbol.definition && !vnode->symbol.analyzed) + varpool_analyze_node (vnode); + } if (node->symbol.same_comdat_group) { @@ -960,8 +944,7 @@ cgraph_analyze_functions (void) 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 (); } @@ -989,20 +972,19 @@ cgraph_analyze_functions (void) symtab_remove_node (node); continue; } - if (symtab_function_p (node)) + if (cgraph_node *cnode = dyn_cast (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; } @@ -1026,70 +1008,70 @@ handle_alias_pairs (void) { 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 (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 (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); } @@ -1117,9 +1099,9 @@ mark_functions_to_output (void) /* 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)) @@ -1131,7 +1113,7 @@ mark_functions_to_output (void) 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; } } @@ -1151,7 +1133,8 @@ mark_functions_to_output (void) 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); @@ -1161,6 +1144,8 @@ mark_functions_to_output (void) gcc_assert (node->global.inlined_to || !gimple_has_body_p (decl) || node->symbol.in_other_partition + || node->clones + || DECL_ARTIFICIAL (decl) || DECL_EXTERNAL (decl)); } @@ -1179,6 +1164,7 @@ mark_functions_to_output (void) 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); @@ -1190,13 +1176,13 @@ mark_functions_to_output (void) } /* 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; @@ -1204,20 +1190,30 @@ init_lowered_empty_function (tree decl) 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; } @@ -1264,7 +1260,7 @@ thunk_adjust (gimple_stmt_iterator * bsi, } 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. */ @@ -1274,7 +1270,7 @@ thunk_adjust (gimple_stmt_iterator * bsi, 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)); @@ -1288,7 +1284,7 @@ thunk_adjust (gimple_stmt_iterator * bsi, 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)); @@ -1310,7 +1306,7 @@ thunk_adjust (gimple_stmt_iterator * bsi, 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); } @@ -1319,7 +1315,7 @@ thunk_adjust (gimple_stmt_iterator * bsi, } /* 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); @@ -1335,7 +1331,7 @@ assemble_thunk (struct cgraph_node *node) 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); @@ -1364,18 +1360,22 @@ assemble_thunk (struct cgraph_node *node) 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 { @@ -1387,7 +1387,7 @@ assemble_thunk (struct cgraph_node *node) int i; tree resdecl; tree restmp = NULL; - VEC(tree, heap) *vargs; + vec vargs; gimple call; gimple ret; @@ -1410,7 +1410,7 @@ assemble_thunk (struct cgraph_node *node) 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); @@ -1424,31 +1424,21 @@ assemble_thunk (struct cgraph_node *node) 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); @@ -1468,6 +1458,9 @@ assemble_thunk (struct cgraph_node *node) 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, @@ -1512,11 +1505,12 @@ assemble_thunk (struct cgraph_node *node) 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) @@ -1541,15 +1535,15 @@ 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; } } @@ -1604,8 +1598,6 @@ expand_function (struct cgraph_node *node) /* 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. */ @@ -1650,6 +1642,7 @@ expand_function (struct cgraph_node *node) /* 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 @@ -1750,7 +1743,7 @@ output_in_order (void) 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); @@ -1760,12 +1753,13 @@ output_in_order (void) } 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) { @@ -1884,27 +1878,37 @@ get_alias_symbol (tree decl) /* 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. */ @@ -1988,7 +1992,32 @@ compile (void) #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) @@ -2003,6 +2032,7 @@ compile (void) cgraph_process_new_functions (); cgraph_state = CGRAPH_STATE_FINISHED; + output_weakrefs (); if (cgraph_dump_file) { @@ -2051,7 +2081,6 @@ finalize_compilation_unit (void) finalize_size_functions (); /* Mark alias targets necessary and emit diagnostics. */ - finish_aliases_1 (); handle_alias_pairs (); if (!quiet_flag) @@ -2065,14 +2094,13 @@ finalize_compilation_unit (void) /* 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 ();