X-Git-Url: http://git.sourceforge.jp/view?a=blobdiff_plain;f=gcc%2Fcgraphunit.c;h=024a3bc2eedaf070aaa2d16df33d4f87e114f755;hb=c3a945cde92094f4713cefdafe5c1bd3f07fbbcc;hp=c41477bede36288447b002d0dade3a92e1df70ff;hpb=c596d83002a937c3943de34c6f47da047337f865;p=pf3gnuchains%2Fgcc-fork.git diff --git a/gcc/cgraphunit.c b/gcc/cgraphunit.c index c41477bede3..024a3bc2eed 100644 --- a/gcc/cgraphunit.c +++ b/gcc/cgraphunit.c @@ -123,6 +123,8 @@ along with GCC; see the file COPYING3. If not see #include "target.h" #include "cgraph.h" #include "diagnostic.h" +#include "tree-pretty-print.h" +#include "gimple-pretty-print.h" #include "timevar.h" #include "params.h" #include "fibheap.h" @@ -143,176 +145,11 @@ static void cgraph_expand_function (struct cgraph_node *); static void cgraph_output_pending_asms (void); static void cgraph_analyze_function (struct cgraph_node *); -static FILE *cgraph_dump_file; - -/* A vector of FUNCTION_DECLs declared as static constructors. */ -static GTY (()) VEC(tree, gc) *static_ctors; -/* A vector of FUNCTION_DECLs declared as static destructors. */ -static GTY (()) VEC(tree, gc) *static_dtors; +FILE *cgraph_dump_file; /* Used for vtable lookup in thunk adjusting. */ static GTY (()) tree vtable_entry_type; -/* When target does not have ctors and dtors, we call all constructor - and destructor by special initialization/destruction function - recognized by collect2. - - When we are going to build this function, collect all constructors and - destructors and turn them into normal functions. */ - -static void -record_cdtor_fn (tree fndecl) -{ - struct cgraph_node *node; - if (targetm.have_ctors_dtors - || (!DECL_STATIC_CONSTRUCTOR (fndecl) - && !DECL_STATIC_DESTRUCTOR (fndecl))) - return; - - if (DECL_STATIC_CONSTRUCTOR (fndecl)) - { - VEC_safe_push (tree, gc, static_ctors, fndecl); - DECL_STATIC_CONSTRUCTOR (fndecl) = 0; - } - if (DECL_STATIC_DESTRUCTOR (fndecl)) - { - VEC_safe_push (tree, gc, static_dtors, fndecl); - DECL_STATIC_DESTRUCTOR (fndecl) = 0; - } - node = cgraph_node (fndecl); - node->local.disregard_inline_limits = 1; - cgraph_mark_reachable_node (node); -} - -/* Define global constructors/destructor functions for the CDTORS, of - which they are LEN. The CDTORS are sorted by initialization - priority. If CTOR_P is true, these are constructors; otherwise, - they are destructors. */ - -static void -build_cdtor (bool ctor_p, tree *cdtors, size_t len) -{ - size_t i; - - i = 0; - while (i < len) - { - tree body; - tree fn; - priority_type priority; - - priority = 0; - body = NULL_TREE; - /* Find the next batch of constructors/destructors with the same - initialization priority. */ - do - { - priority_type p; - fn = cdtors[i]; - p = ctor_p ? DECL_INIT_PRIORITY (fn) : DECL_FINI_PRIORITY (fn); - if (!body) - priority = p; - else if (p != priority) - break; - append_to_statement_list (build_function_call_expr (UNKNOWN_LOCATION, - fn, 0), - &body); - ++i; - } - while (i < len); - gcc_assert (body != NULL_TREE); - /* Generate a function to call all the function of like - priority. */ - cgraph_build_static_cdtor (ctor_p ? 'I' : 'D', body, priority); - } -} - -/* Comparison function for qsort. P1 and P2 are actually of type - "tree *" and point to static constructors. DECL_INIT_PRIORITY is - used to determine the sort order. */ - -static int -compare_ctor (const void *p1, const void *p2) -{ - tree f1; - tree f2; - int priority1; - int priority2; - - f1 = *(const tree *)p1; - f2 = *(const tree *)p2; - priority1 = DECL_INIT_PRIORITY (f1); - priority2 = DECL_INIT_PRIORITY (f2); - - if (priority1 < priority2) - return -1; - else if (priority1 > priority2) - return 1; - else - /* Ensure a stable sort. */ - return (const tree *)p1 - (const tree *)p2; -} - -/* Comparison function for qsort. P1 and P2 are actually of type - "tree *" and point to static destructors. DECL_FINI_PRIORITY is - used to determine the sort order. */ - -static int -compare_dtor (const void *p1, const void *p2) -{ - tree f1; - tree f2; - int priority1; - int priority2; - - f1 = *(const tree *)p1; - f2 = *(const tree *)p2; - priority1 = DECL_FINI_PRIORITY (f1); - priority2 = DECL_FINI_PRIORITY (f2); - - if (priority1 < priority2) - return -1; - else if (priority1 > priority2) - return 1; - else - /* Ensure a stable sort. */ - return (const tree *)p1 - (const tree *)p2; -} - -/* Generate functions to call static constructors and destructors - for targets that do not support .ctors/.dtors sections. These - functions have magic names which are detected by collect2. */ - -static void -cgraph_build_cdtor_fns (void) -{ - if (!VEC_empty (tree, static_ctors)) - { - gcc_assert (!targetm.have_ctors_dtors); - qsort (VEC_address (tree, static_ctors), - VEC_length (tree, static_ctors), - sizeof (tree), - compare_ctor); - build_cdtor (/*ctor_p=*/true, - VEC_address (tree, static_ctors), - VEC_length (tree, static_ctors)); - VEC_truncate (tree, static_ctors, 0); - } - - if (!VEC_empty (tree, static_dtors)) - { - gcc_assert (!targetm.have_ctors_dtors); - qsort (VEC_address (tree, static_dtors), - VEC_length (tree, static_dtors), - sizeof (tree), - compare_dtor); - build_cdtor (/*ctor_p=*/false, - VEC_address (tree, static_dtors), - VEC_length (tree, static_dtors)); - VEC_truncate (tree, static_dtors, 0); - } -} - /* Determine if function DECL is needed. That is, visible to something either outside this translation unit, something magic in the system configury. */ @@ -350,24 +187,21 @@ cgraph_decide_is_function_needed (struct cgraph_node *node, tree decl) When not optimizing, also output the static functions. (see PR24561), but don't do so for always_inline functions, functions - declared inline and nested functions. These was optimized out + 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 (((TREE_PUBLIC (decl) - || (!optimize && !node->local.disregard_inline_limits + || (!optimize + && !node->local.disregard_inline_limits && !DECL_DECLARED_INLINE_P (decl) - && !node->origin)) + && !(DECL_CONTEXT (decl) + && TREE_CODE (DECL_CONTEXT (decl)) == FUNCTION_DECL))) && !flag_whole_program && !flag_lto && !flag_whopr) && !DECL_COMDAT (decl) && !DECL_EXTERNAL (decl)) return true; - /* Constructors and destructors are reachable from the runtime by - some mechanism. */ - if (DECL_STATIC_CONSTRUCTOR (decl) || DECL_STATIC_DESTRUCTOR (decl)) - return true; - return false; } @@ -382,6 +216,7 @@ cgraph_process_new_functions (void) tree fndecl; struct cgraph_node *node; + varpool_analyze_pending_decls (); /* Note that this queue may grow as its being processed, as the new functions may generate new ones. */ while (cgraph_new_nodes) @@ -437,6 +272,7 @@ cgraph_process_new_functions (void) break; } cgraph_call_function_insertion_hooks (node); + varpool_analyze_pending_decls (); } return output; } @@ -518,7 +354,6 @@ cgraph_finalize_function (tree decl, bool nested) node->local.finalized = true; node->lowered = DECL_STRUCT_FUNCTION (decl)->cfg != NULL; node->finalized_by_frontend = true; - record_cdtor_fn (node->decl); if (cgraph_decide_is_function_needed (node, decl)) cgraph_mark_needed_node (node); @@ -526,7 +361,9 @@ cgraph_finalize_function (tree decl, bool nested) /* Since we reclaim unreachable nodes at the end of every language level unit, we need to be conservative about possible entry points there. */ - if ((TREE_PUBLIC (decl) && !DECL_COMDAT (decl) && !DECL_EXTERNAL (decl))) + if ((TREE_PUBLIC (decl) && !DECL_COMDAT (decl) && !DECL_EXTERNAL (decl)) + || DECL_STATIC_CONSTRUCTOR (decl) + || DECL_STATIC_DESTRUCTOR (decl)) cgraph_mark_reachable_node (node); /* If we've not yet emitted decl, tell the debug info about it. */ @@ -553,6 +390,7 @@ cgraph_mark_if_needed (tree decl) cgraph_mark_needed_node (node); } +#ifdef ENABLE_CHECKING /* Return TRUE if NODE2 is equivalent to NODE or its clone. */ static bool clone_of_p (struct cgraph_node *node, struct cgraph_node *node2) @@ -561,9 +399,46 @@ clone_of_p (struct cgraph_node *node, struct cgraph_node *node2) node2 = node2->clone_of; return node2 != NULL; } +#endif + +/* Verify edge E count and frequency. */ + +static bool +verify_edge_count_and_frequency (struct cgraph_edge *e) +{ + bool error_found = false; + if (e->count < 0) + { + error ("caller edge count is negative"); + error_found = true; + } + if (e->frequency < 0) + { + error ("caller edge frequency is negative"); + error_found = true; + } + if (e->frequency > CGRAPH_FREQ_MAX) + { + error ("caller edge frequency is too large"); + error_found = true; + } + if (gimple_has_body_p (e->caller->decl) + && !e->caller->global.inlined_to + && (e->frequency + != compute_call_stmt_bb_frequency (e->caller->decl, + gimple_bb (e->call_stmt)))) + { + error ("caller edge frequency %i does not match BB freqency %i", + e->frequency, + compute_call_stmt_bb_frequency (e->caller->decl, + gimple_bb (e->call_stmt))); + error_found = true; + } + return error_found; +} /* Verify cgraph nodes of given cgraph node. */ -void +DEBUG_FUNCTION void verify_cgraph_node (struct cgraph_node *node) { struct cgraph_edge *e; @@ -573,7 +448,7 @@ verify_cgraph_node (struct cgraph_node *node) gimple_stmt_iterator gsi; bool error_found = false; - if (errorcount || sorrycount) + if (seen_error ()) return; timevar_push (TV_CGRAPH_VERIFY); @@ -607,35 +482,28 @@ verify_cgraph_node (struct cgraph_node *node) error ("Inline clone is needed"); error_found = true; } - for (e = node->callers; e; e = e->next_caller) + for (e = node->indirect_calls; e; e = e->next_callee) { - if (e->count < 0) + if (e->aux) { - error ("caller edge count is negative"); + error ("aux field set for indirect edge from %s", + identifier_to_locale (cgraph_node_name (e->caller))); error_found = true; } - if (e->frequency < 0) + if (!e->indirect_unknown_callee + || !e->indirect_info) { - error ("caller edge frequency is negative"); - error_found = true; - } - if (e->frequency > CGRAPH_FREQ_MAX) - { - error ("caller edge frequency is too large"); - error_found = true; - } - if (gimple_has_body_p (e->caller->decl) - && !e->caller->global.inlined_to - && (e->frequency - != compute_call_stmt_bb_frequency (e->caller->decl, - gimple_bb (e->call_stmt)))) - { - error ("caller edge frequency %i does not match BB freqency %i", - e->frequency, - compute_call_stmt_bb_frequency (e->caller->decl, - gimple_bb (e->call_stmt))); + error ("An indirect edge from %s is not marked as indirect or has " + "associated indirect_info, the corresponding statement is: ", + identifier_to_locale (cgraph_node_name (e->caller))); + debug_gimple_stmt (e->call_stmt); error_found = true; } + } + for (e = node->callers; e; e = e->next_caller) + { + if (verify_edge_count_and_frequency (e)) + error_found = true; if (!e->inline_failed) { if (node->global.inlined_to @@ -658,6 +526,9 @@ verify_cgraph_node (struct cgraph_node *node) error_found = true; } } + for (e = node->indirect_calls; e; e = e->next_callee) + if (verify_edge_count_and_frequency (e)) + error_found = true; if (!node->callers && node->global.inlined_to) { error ("inlined_to pointer is set but no predecessors found"); @@ -714,6 +585,32 @@ verify_cgraph_node (struct cgraph_node *node) error ("double linked list of clones corrupted"); error_found = true; } + if (node->same_comdat_group) + { + struct cgraph_node *n = node->same_comdat_group; + + if (!DECL_ONE_ONLY (node->decl)) + { + error ("non-DECL_ONE_ONLY node in a same_comdat_group list"); + error_found = true; + } + if (n == node) + { + error ("node is alone in a comdat group"); + error_found = true; + } + do + { + if (!n->same_comdat_group) + { + error ("same_comdat_group is not a circular list"); + error_found = true; + break; + } + n = n->same_comdat_group; + } + while (n != node); + } if (node->analyzed && gimple_has_body_p (node->decl) && !TREE_ASM_WRITTEN (node->decl) @@ -733,10 +630,10 @@ verify_cgraph_node (struct cgraph_node *node) gsi_next (&gsi)) { gimple stmt = gsi_stmt (gsi); - tree decl; - if (is_gimple_call (stmt) && (decl = gimple_call_fndecl (stmt))) + if (is_gimple_call (stmt)) { struct cgraph_edge *e = cgraph_edge (node, stmt); + tree decl = gimple_call_fndecl (stmt); if (e) { if (e->aux) @@ -745,25 +642,42 @@ verify_cgraph_node (struct cgraph_node *node) debug_gimple_stmt (stmt); error_found = true; } - if (e->callee->same_body_alias) + if (!e->indirect_unknown_callee) { - error ("edge points to same body alias:"); - debug_tree (e->callee->decl); - error_found = true; + if (e->callee->same_body_alias) + { + error ("edge points to same body alias:"); + debug_tree (e->callee->decl); + error_found = true; + } +#ifdef ENABLE_CHECKING + else if (!e->callee->global.inlined_to + && decl + && cgraph_get_node (decl) + && (e->callee->former_clone_of + != cgraph_get_node (decl)->decl) + && !clone_of_p (cgraph_node (decl), + e->callee)) + { + error ("edge points to wrong declaration:"); + debug_tree (e->callee->decl); + fprintf (stderr," Instead of:"); + debug_tree (decl); + error_found = true; + } +#endif } - else if (!node->global.inlined_to - && !e->callee->global.inlined_to - && !clone_of_p (cgraph_node (decl), e->callee)) + else if (decl) { - error ("edge points to wrong declaration:"); - debug_tree (e->callee->decl); - fprintf (stderr," Instead of:"); - debug_tree (decl); + error ("an indirect edge with unknown callee " + "corresponding to a call_stmt with " + "a known declaration:"); error_found = true; + debug_gimple_stmt (e->call_stmt); } e->aux = (void *)1; } - else + else if (decl) { error ("missing callgraph edge for call stmt:"); debug_gimple_stmt (stmt); @@ -779,7 +693,7 @@ verify_cgraph_node (struct cgraph_node *node) for (e = node->callees; e; e = e->next_callee) { - if (!e->aux && !e->indirect_call) + if (!e->aux) { error ("edge %s->%s has no corresponding call_stmt", identifier_to_locale (cgraph_node_name (e->caller)), @@ -789,6 +703,17 @@ verify_cgraph_node (struct cgraph_node *node) } e->aux = 0; } + for (e = node->indirect_calls; e; e = e->next_callee) + { + if (!e->aux) + { + error ("an indirect edge from %s has no corresponding call_stmt", + identifier_to_locale (cgraph_node_name (e->caller))); + debug_gimple_stmt (e->call_stmt); + error_found = true; + } + e->aux = 0; + } } if (error_found) { @@ -800,12 +725,12 @@ verify_cgraph_node (struct cgraph_node *node) } /* Verify whole cgraph structure. */ -void +DEBUG_FUNCTION void verify_cgraph (void) { struct cgraph_node *node; - if (sorrycount || errorcount) + if (seen_error ()) return; for (node = cgraph_nodes; node; node = node->next) @@ -819,7 +744,7 @@ cgraph_output_pending_asms (void) { struct cgraph_asm_node *can; - if (errorcount || sorrycount) + if (seen_error ()) return; for (can = cgraph_asm_nodes; can; can = can->next) @@ -889,11 +814,7 @@ process_function_and_variable_attributes (struct cgraph_node *first, { tree decl = node->decl; if (DECL_PRESERVE_P (decl)) - { - mark_decl_referenced (decl); - if (node->local.finalized) - cgraph_mark_needed_node (node); - } + cgraph_mark_needed_node (node); if (lookup_attribute ("externally_visible", DECL_ATTRIBUTES (decl))) { if (! TREE_PUBLIC (node->decl)) @@ -909,7 +830,6 @@ process_function_and_variable_attributes (struct cgraph_node *first, tree decl = vnode->decl; if (DECL_PRESERVE_P (decl)) { - mark_decl_referenced (decl); vnode->force_output = true; if (vnode->finalized) varpool_mark_needed_node (vnode); @@ -1069,10 +989,6 @@ cgraph_finalize_compilation_unit (void) /* Emit size functions we didn't inline. */ finalize_size_functions (); - /* Call functions declared with the "constructor" or "destructor" - attribute. */ - cgraph_build_cdtor_fns (); - /* Mark alias targets necessary and emit diagnostics. */ finish_aliases_1 (); @@ -1130,7 +1046,7 @@ cgraph_mark_functions_to_output (void) outside the current compilation unit. */ if (node->analyzed && !node->global.inlined_to - && (node->needed + && (!cgraph_only_called_directly_p (node) || (e && node->reachable)) && !TREE_ASM_WRITTEN (decl) && !DECL_EXTERNAL (decl)) @@ -1157,6 +1073,10 @@ cgraph_mark_functions_to_output (void) #ifdef ENABLE_CHECKING if (!node->global.inlined_to && gimple_has_body_p (decl) + /* FIXME: in ltrans unit when offline copy is outside partition but inline copies + are inside partition, we can end up not removing the body since we no longer + have analyzed node pointing to it. */ + && !node->in_other_partition && !DECL_EXTERNAL (decl)) { dump_cgraph_node (stderr, node); @@ -1165,6 +1085,7 @@ cgraph_mark_functions_to_output (void) #endif gcc_assert (node->global.inlined_to || !gimple_has_body_p (decl) + || node->in_other_partition || DECL_EXTERNAL (decl)); } @@ -1178,6 +1099,10 @@ cgraph_mark_functions_to_output (void) tree decl = node->decl; if (!node->global.inlined_to && gimple_has_body_p (decl) + /* FIXME: in ltrans unit when offline copy is outside partition but inline copies + are inside partition, we can end up not removing the body since we no longer + have analyzed node pointing to it. */ + && !node->in_other_partition && !DECL_EXTERNAL (decl)) { dump_cgraph_node (stderr, node); @@ -1279,8 +1204,7 @@ thunk_adjust (gimple_stmt_iterator * bsi, vtabletmp2 = create_tmp_var (TREE_TYPE (TREE_TYPE (vtabletmp)), "vtableaddr"); stmt = gimple_build_assign (vtabletmp2, - build1 (INDIRECT_REF, - TREE_TYPE (vtabletmp2), vtabletmp)); + build_simple_mem_ref (vtabletmp)); gsi_insert_after (bsi, stmt, GSI_NEW_STMT); mark_symbols_for_renaming (stmt); find_referenced_vars_in (stmt); @@ -1299,9 +1223,7 @@ thunk_adjust (gimple_stmt_iterator * bsi, vtabletmp3 = create_tmp_var (TREE_TYPE (TREE_TYPE (vtabletmp2)), "vcalloffset"); stmt = gimple_build_assign (vtabletmp3, - build1 (INDIRECT_REF, - TREE_TYPE (vtabletmp3), - vtabletmp2)); + build_simple_mem_ref (vtabletmp2)); gsi_insert_after (bsi, stmt, GSI_NEW_STMT); mark_symbols_for_renaming (stmt); find_referenced_vars_in (stmt); @@ -1438,14 +1360,14 @@ assemble_thunk (struct cgraph_node *node) if (!is_gimple_reg_type (restype)) { restmp = resdecl; - cfun->local_decls = tree_cons (NULL_TREE, restmp, cfun->local_decls); + add_local_decl (cfun, restmp); BLOCK_VARS (DECL_INITIAL (current_function_decl)) = restmp; } else restmp = create_tmp_var_raw (restype, "retval"); } - for (arg = a; arg; arg = TREE_CHAIN (arg)) + for (arg = a; arg; arg = DECL_CHAIN (arg)) nargs++; vargs = VEC_alloc (tree, heap, nargs); if (this_adjusting) @@ -1455,7 +1377,7 @@ assemble_thunk (struct cgraph_node *node) virtual_offset)); else VEC_quick_push (tree, vargs, a); - for (i = 1, arg = TREE_CHAIN (a); i < nargs; i++, arg = TREE_CHAIN (arg)) + for (i = 1, arg = DECL_CHAIN (a); i < nargs; i++, arg = DECL_CHAIN (arg)) VEC_quick_push (tree, vargs, arg); call = gimple_build_call_vec (build_fold_addr_expr_loc (0, alias), vargs); VEC_free (tree, heap, vargs); @@ -1522,7 +1444,6 @@ assemble_thunk (struct cgraph_node *node) cgraph_remove_same_body_alias (node); /* Since we want to emit the thunk, we explicitly mark its name as referenced. */ - mark_decl_referenced (thunk_fndecl); cgraph_add_new_function (thunk_fndecl, true); bitmap_obstack_release (NULL); } @@ -1664,7 +1585,6 @@ static void cgraph_output_in_order (void) { int max; - size_t size; struct cgraph_order_sort *nodes; int i; struct cgraph_node *pf; @@ -1672,9 +1592,7 @@ cgraph_output_in_order (void) struct cgraph_asm_node *pa; max = cgraph_order; - size = max * sizeof (struct cgraph_order_sort); - nodes = (struct cgraph_order_sort *) alloca (size); - memset (nodes, 0, size); + nodes = XCNEWVEC (struct cgraph_order_sort, max); varpool_analyze_pending_decls (); @@ -1741,6 +1659,7 @@ cgraph_output_in_order (void) } cgraph_asm_nodes = NULL; + free (nodes); } /* Return true when function body of DECL still needs to be kept around @@ -1789,11 +1708,19 @@ ipa_passes (void) execute_ipa_summary_passes ((struct ipa_opt_pass_d *) all_regular_ipa_passes); } + + /* Some targets need to handle LTO assembler output specially. */ + if (flag_generate_lto) + targetm.asm_out.lto_start (); + execute_ipa_summary_passes ((struct ipa_opt_pass_d *) all_lto_gen_passes); if (!in_lto_p) ipa_write_summaries (); + if (flag_generate_lto) + targetm.asm_out.lto_end (); + if (!flag_ltrans) execute_ipa_pass_list (all_regular_ipa_passes); invoke_plugin_callbacks (PLUGIN_ALL_IPA_PASSES_END, NULL); @@ -1807,7 +1734,7 @@ ipa_passes (void) void cgraph_optimize (void) { - if (errorcount || sorrycount) + if (seen_error ()) return; #ifdef ENABLE_CHECKING @@ -1829,11 +1756,11 @@ cgraph_optimize (void) cgraph_state = CGRAPH_STATE_IPA; /* Don't run the IPA passes if there was any error or sorry messages. */ - if (errorcount == 0 && sorrycount == 0) + if (!seen_error ()) ipa_passes (); /* Do nothing else if any IPA pass found errors. */ - if (errorcount || sorrycount) + if (seen_error ()) { timevar_pop (TV_CGRAPHOPT); return; @@ -1891,7 +1818,7 @@ cgraph_optimize (void) verify_cgraph (); /* Double check that all inline clones are gone and that all function bodies have been released from memory. */ - if (!(sorrycount || errorcount)) + if (!seen_error ()) { struct cgraph_node *node; bool error_found = false; @@ -1910,76 +1837,11 @@ cgraph_optimize (void) #endif } - -/* Generate and emit a static constructor or destructor. WHICH must - be one of 'I' (for a constructor) or 'D' (for a destructor). BODY - is a STATEMENT_LIST containing GENERIC statements. PRIORITY is the - initialization priority for this constructor or destructor. */ - -void -cgraph_build_static_cdtor (char which, tree body, int priority) -{ - static int counter = 0; - char which_buf[16]; - tree decl, name, resdecl; - - /* The priority is encoded in the constructor or destructor name. - collect2 will sort the names and arrange that they are called at - program startup. */ - sprintf (which_buf, "%c_%.5d_%d", which, priority, counter++); - name = get_file_function_name (which_buf); - - decl = build_decl (input_location, FUNCTION_DECL, name, - build_function_type (void_type_node, void_list_node)); - current_function_decl = decl; - - resdecl = build_decl (input_location, - RESULT_DECL, NULL_TREE, void_type_node); - DECL_ARTIFICIAL (resdecl) = 1; - DECL_RESULT (decl) = resdecl; - DECL_CONTEXT (resdecl) = decl; - - allocate_struct_function (decl, false); - - TREE_STATIC (decl) = 1; - TREE_USED (decl) = 1; - DECL_ARTIFICIAL (decl) = 1; - DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT (decl) = 1; - DECL_SAVED_TREE (decl) = body; - TREE_PUBLIC (decl) = ! targetm.have_ctors_dtors; - DECL_UNINLINABLE (decl) = 1; - - DECL_INITIAL (decl) = make_node (BLOCK); - TREE_USED (DECL_INITIAL (decl)) = 1; - - DECL_SOURCE_LOCATION (decl) = input_location; - cfun->function_end_locus = input_location; - - switch (which) - { - case 'I': - DECL_STATIC_CONSTRUCTOR (decl) = 1; - decl_init_priority_insert (decl, priority); - break; - case 'D': - DECL_STATIC_DESTRUCTOR (decl) = 1; - decl_fini_priority_insert (decl, priority); - break; - default: - gcc_unreachable (); - } - - gimplify_function_tree (decl); - - cgraph_add_new_function (decl, false); - cgraph_mark_needed_node (cgraph_node (decl)); - set_cfun (NULL); -} - void init_cgraph (void) { - cgraph_dump_file = dump_begin (TDI_cgraph, NULL); + if (!cgraph_dump_file) + cgraph_dump_file = dump_begin (TDI_cgraph, NULL); } /* The edges representing the callers of the NEW_VERSION node were @@ -2008,16 +1870,20 @@ update_call_expr (struct cgraph_node *new_version) edges which should be redirected to point to NEW_VERSION. ALL the callees edges of OLD_VERSION are cloned to the new version node. Return the new - version node. */ + version node. + + If non-NULL BLOCK_TO_COPY determine what basic blocks + was copied to prevent duplications of calls that are dead + in the clone. */ static struct cgraph_node * cgraph_copy_node_for_versioning (struct cgraph_node *old_version, tree new_decl, - VEC(cgraph_edge_p,heap) *redirect_callers) + VEC(cgraph_edge_p,heap) *redirect_callers, + bitmap bbs_to_copy) { struct cgraph_node *new_version; - struct cgraph_edge *e, *new_e; - struct cgraph_edge *next_callee; + struct cgraph_edge *e; unsigned i; gcc_assert (old_version); @@ -2026,35 +1892,29 @@ cgraph_copy_node_for_versioning (struct cgraph_node *old_version, new_version->analyzed = true; new_version->local = old_version->local; + new_version->local.externally_visible = false; + new_version->local.local = true; + new_version->local.vtable_method = false; new_version->global = old_version->global; - new_version->rtl = new_version->rtl; + new_version->rtl = old_version->rtl; new_version->reachable = true; new_version->count = old_version->count; - /* Clone the old node callees. Recursive calls are - also cloned. */ - for (e = old_version->callees;e; e=e->next_callee) - { - new_e = cgraph_clone_edge (e, new_version, e->call_stmt, - e->lto_stmt_uid, 0, e->frequency, - e->loop_nest, true); - new_e->count = e->count; - } - /* Fix recursive calls. - If OLD_VERSION has a recursive call after the - previous edge cloning, the new version will have an edge - pointing to the old version, which is wrong; - Redirect it to point to the new version. */ - for (e = new_version->callees ; e; e = next_callee) - { - next_callee = e->next_callee; - if (e->callee == old_version) - cgraph_redirect_edge_callee (e, new_version); - - if (!next_callee) - break; - } - for (i = 0; VEC_iterate (cgraph_edge_p, redirect_callers, i, e); i++) + for (e = old_version->callees; e; e=e->next_callee) + if (!bbs_to_copy + || bitmap_bit_p (bbs_to_copy, gimple_bb (e->call_stmt)->index)) + cgraph_clone_edge (e, new_version, e->call_stmt, + e->lto_stmt_uid, REG_BR_PROB_BASE, + CGRAPH_FREQ_BASE, + e->loop_nest, true); + for (e = old_version->indirect_calls; e; e=e->next_callee) + if (!bbs_to_copy + || bitmap_bit_p (bbs_to_copy, gimple_bb (e->call_stmt)->index)) + cgraph_clone_edge (e, new_version, e->call_stmt, + e->lto_stmt_uid, REG_BR_PROB_BASE, + CGRAPH_FREQ_BASE, + e->loop_nest, true); + FOR_EACH_VEC_ELT (cgraph_edge_p, redirect_callers, i, e) { /* Redirect calls to the old version node to point to its new version. */ @@ -2076,14 +1936,19 @@ cgraph_copy_node_for_versioning (struct cgraph_node *old_version, new ones (according to results of prior analysis). OLD_VERSION_NODE is the node that is versioned. It returns the new version's cgraph node. - ARGS_TO_SKIP lists arguments to be omitted from functions - */ + If non-NULL ARGS_TO_SKIP determine function parameters to remove + from new version. + If non-NULL BLOCK_TO_COPY determine what basic blocks to copy. + If non_NULL NEW_ENTRY determine new entry BB of the clone. */ struct cgraph_node * cgraph_function_versioning (struct cgraph_node *old_version_node, VEC(cgraph_edge_p,heap) *redirect_callers, VEC (ipa_replace_map_p,gc)* tree_map, - bitmap args_to_skip) + bitmap args_to_skip, + bitmap bbs_to_copy, + basic_block new_entry_block, + const char *clone_name) { tree old_decl = old_version_node->decl; struct cgraph_node *new_version_node = NULL; @@ -2099,14 +1964,20 @@ cgraph_function_versioning (struct cgraph_node *old_version_node, else new_decl = build_function_decl_skip_args (old_decl, args_to_skip); + /* Generate a new name for the new version. */ + DECL_NAME (new_decl) = clone_function_name (old_decl, clone_name); + SET_DECL_ASSEMBLER_NAME (new_decl, DECL_NAME (new_decl)); + SET_DECL_RTL (new_decl, NULL); + /* Create the new version's call-graph node. and update the edges of the new node. */ new_version_node = cgraph_copy_node_for_versioning (old_version_node, new_decl, - redirect_callers); + redirect_callers, bbs_to_copy); /* Copy the OLD_VERSION_NODE function tree to the new version. */ - tree_function_versioning (old_decl, new_decl, tree_map, false, args_to_skip); + tree_function_versioning (old_decl, new_decl, tree_map, false, args_to_skip, + bbs_to_copy, new_entry_block); /* Update the new version's properties. Make The new version visible only within this translation unit. Make sure @@ -2177,7 +2048,8 @@ save_inline_function_body (struct cgraph_node *node) } /* Copy the OLD_VERSION_NODE function tree to the new version. */ - tree_function_versioning (node->decl, first_clone->decl, NULL, true, NULL); + tree_function_versioning (node->decl, first_clone->decl, NULL, true, NULL, + NULL, NULL); DECL_EXTERNAL (first_clone->decl) = 0; DECL_COMDAT_GROUP (first_clone->decl) = NULL_TREE; @@ -2198,10 +2070,15 @@ static void cgraph_materialize_clone (struct cgraph_node *node) { bitmap_obstack_initialize (NULL); +#ifdef ENABLE_CHECKING + node->former_clone_of = node->clone_of->decl; + if (node->clone_of->former_clone_of) + node->former_clone_of = node->clone_of->former_clone_of; +#endif /* Copy the OLD_VERSION_NODE function tree to the new version. */ tree_function_versioning (node->clone_of->decl, node->decl, node->clone.tree_map, true, - node->clone.args_to_skip); + node->clone.args_to_skip, NULL, NULL); if (cgraph_dump_file) { dump_function_to_file (node->clone_of->decl, cgraph_dump_file, dump_flags); @@ -2218,7 +2095,11 @@ cgraph_materialize_clone (struct cgraph_node *node) node->next_sibling_clone = NULL; node->prev_sibling_clone = NULL; if (!node->clone_of->analyzed && !node->clone_of->clones) - cgraph_remove_node (node->clone_of); + { + cgraph_release_function_body (node->clone_of); + cgraph_node_remove_callees (node->clone_of); + ipa_remove_all_references (&node->clone_of->ref_list); + } node->clone_of = NULL; bitmap_obstack_release (NULL); } @@ -2231,36 +2112,58 @@ cgraph_redirect_edge_call_stmt_to_callee (struct cgraph_edge *e) { tree decl = gimple_call_fndecl (e->call_stmt); gimple new_stmt; - gimple_stmt_iterator gsi; +#ifdef ENABLE_CHECKING + struct cgraph_node *node; +#endif - if (!decl || decl == e->callee->decl + if (e->indirect_unknown_callee + || decl == e->callee->decl /* Don't update call from same body alias to the real function. */ - || cgraph_get_node (decl) == cgraph_get_node (e->callee->decl)) + || (decl && cgraph_get_node (decl) == cgraph_get_node (e->callee->decl))) return e->call_stmt; +#ifdef ENABLE_CHECKING + if (decl) + { + node = cgraph_get_node (decl); + gcc_assert (!node || !node->clone.combined_args_to_skip); + } +#endif + if (cgraph_dump_file) { fprintf (cgraph_dump_file, "updating call of %s/%i -> %s/%i: ", cgraph_node_name (e->caller), e->caller->uid, cgraph_node_name (e->callee), e->callee->uid); print_gimple_stmt (cgraph_dump_file, e->call_stmt, 0, dump_flags); + if (e->callee->clone.combined_args_to_skip) + { + fprintf (cgraph_dump_file, " combined args to skip: "); + dump_bitmap (cgraph_dump_file, + e->callee->clone.combined_args_to_skip); + } } if (e->callee->clone.combined_args_to_skip) - new_stmt = gimple_call_copy_skip_args (e->call_stmt, - e->callee->clone.combined_args_to_skip); + { + gimple_stmt_iterator gsi; + + new_stmt + = gimple_call_copy_skip_args (e->call_stmt, + e->callee->clone.combined_args_to_skip); + + if (gimple_vdef (new_stmt) + && TREE_CODE (gimple_vdef (new_stmt)) == SSA_NAME) + SSA_NAME_DEF_STMT (gimple_vdef (new_stmt)) = new_stmt; + + gsi = gsi_for_stmt (e->call_stmt); + gsi_replace (&gsi, new_stmt, true); + } else new_stmt = e->call_stmt; - if (gimple_vdef (new_stmt) - && TREE_CODE (gimple_vdef (new_stmt)) == SSA_NAME) - SSA_NAME_DEF_STMT (gimple_vdef (new_stmt)) = new_stmt; - gimple_call_set_fndecl (new_stmt, e->callee->decl); - - gsi = gsi_for_stmt (e->call_stmt); - gsi_replace (&gsi, new_stmt, true); - /* Update EH information too, just in case. */ - maybe_clean_or_replace_eh_stmt (e->call_stmt, new_stmt); + gimple_call_set_fndecl (new_stmt, e->callee->decl); + update_stmt (new_stmt); cgraph_set_call_stmt_including_clones (e->caller, e->call_stmt, new_stmt); @@ -2340,9 +2243,8 @@ cgraph_materialize_all_clones (void) } } cgraph_materialize_clone (node); + stabilized = false; } - else - stabilized = false; } } } @@ -2350,30 +2252,7 @@ cgraph_materialize_all_clones (void) if (!node->analyzed && node->callees) cgraph_node_remove_callees (node); if (cgraph_dump_file) - fprintf (cgraph_dump_file, "Updating call sites\n"); - for (node = cgraph_nodes; node; node = node->next) - if (node->analyzed && !node->clone_of - && gimple_has_body_p (node->decl)) - { - struct cgraph_edge *e; - - current_function_decl = node->decl; - push_cfun (DECL_STRUCT_FUNCTION (node->decl)); - for (e = node->callees; e; e = e->next_callee) - cgraph_redirect_edge_call_stmt_to_callee (e); - pop_cfun (); - current_function_decl = NULL; -#ifdef ENABLE_CHECKING - verify_cgraph_node (node); -#endif - } - if (cgraph_dump_file) fprintf (cgraph_dump_file, "Materialization Call site updates done.\n"); - /* All changes to parameters have been performed. In order not to - incorrectly repeat them, we simply dispose of the bitmaps that drive the - changes. */ - for (node = cgraph_nodes; node; node = node->next) - node->clone.combined_args_to_skip = NULL; #ifdef ENABLE_CHECKING verify_cgraph (); #endif