Hi, given that the patch has received feedback and I have weekend for fixing the fallout, I decided to commit the following version today. It contains fix in visibility handling of thunks that has shown in Mozilla build.
* cgraph.c (cgraph_add_thunk): Create real function node instead of alias node; finalize it and mark needed/reachale; arrange visibility to be right and add it into the corresponding same comdat group list. (dump_cgraph_node): Dump thunks. * cgraph.h (cgraph_first_defined_function, cgraph_next_defined_function, cgraph_function_with_gimple_body_p, cgraph_first_function_with_gimple_body, cgraph_next_function_with_gimple_body): New functions. (FOR_EACH_FUNCTION_WITH_GIMPLE_BODY, FOR_EACH_DEFINED_FUNCTION): New macros. * ipa-cp.c (ipcp_need_redirect_p): Thunks can't be redirected. (ipcp_generate_summary): Use FOR_EACH_FUNCTION_WITH_GIMPLE_BODY. * cgraphunit.c (cgraph_finalize_function): Only look into possible devirtualization when optimizing. (verify_cgraph_node): Verify thunks. (cgraph_analyze_function): Analyze thunks. (cgraph_mark_functions_to_output): Output thunks only in combination with function they are assigned to. (assemble_thunk): Turn thunk into non-thunk; don't try to turn alias into normal node. (assemble_thunks): New functoin. (cgraph_expand_function): Use it. * lto-cgraph.c (lto_output_node): Stream thunks. (input_overwrite_node): Stream in thunks. * ipa-pure-const.c (analyze_function): Thunks do nothing interesting. * lto-streamer-out.c (lto_output): Do not try to output thunk's body. * ipa-inline.c (inline_small_functions): Use FOR_EACH_DEFINED_FUNCTION. * ipa-inline-analysis.c (compute_inline_parameters): "Analyze" thunks. (inline_analyze_function): Do not care about thunk jump functions. (inline_generate_summary):Use FOR_EACH_DEFINED_FUNCTION. * ipa-prop.c (ipa_prop_write_jump_functions): Use cgraph_function_with_gimple_body_p. * passes.c (do_per_function_toporder): Use cgraph_function_with_gimple_body_p. (execute_one_pass);Use FOR_EACH_FUNCTION_WITH_GIMPLE_BODY. (ipa_write_summaries): Use cgraph_function_with_gimple_body_p. (function_called_by_processed_nodes_p): Likewise. * lto.c (lto_materialize_function): Use cgraph_function_with_gimple_body_p. (add_cgraph_node_to_partition): Do not re-add items to partition; handle thunks. (add_varpool_node_to_partition): Do not re-add items to partition. Index: cgraph.c =================================================================== *** cgraph.c (revision 173251) --- cgraph.c (working copy) *************** cgraph_same_body_alias (struct cgraph_no *** 595,608 **** See comments in thunk_adjust for detail on the parameters. */ struct cgraph_node * ! cgraph_add_thunk (struct cgraph_node *decl_node, tree alias, tree decl, bool this_adjusting, HOST_WIDE_INT fixed_offset, HOST_WIDE_INT virtual_value, tree virtual_offset, tree real_alias) { ! struct cgraph_node *node = cgraph_get_node (alias); if (node) { gcc_assert (node->local.finalized); --- 595,610 ---- See comments in thunk_adjust for detail on the parameters. */ struct cgraph_node * ! cgraph_add_thunk (struct cgraph_node *decl_node ATTRIBUTE_UNUSED, ! tree alias, tree decl, bool this_adjusting, HOST_WIDE_INT fixed_offset, HOST_WIDE_INT virtual_value, tree virtual_offset, tree real_alias) { ! struct cgraph_node *node; + node = cgraph_get_node (alias); if (node) { gcc_assert (node->local.finalized); *************** cgraph_add_thunk (struct cgraph_node *de *** 610,617 **** cgraph_remove_node (node); } ! node = cgraph_same_body_alias_1 (decl_node, alias, decl); ! gcc_assert (node); gcc_checking_assert (!virtual_offset || tree_int_cst_equal (virtual_offset, size_int (virtual_value))); --- 612,618 ---- cgraph_remove_node (node); } ! node = cgraph_create_node (alias); gcc_checking_assert (!virtual_offset || tree_int_cst_equal (virtual_offset, size_int (virtual_value))); *************** cgraph_add_thunk (struct cgraph_node *de *** 621,626 **** --- 622,636 ---- node->thunk.virtual_offset_p = virtual_offset != NULL; node->thunk.alias = real_alias; node->thunk.thunk_p = true; + node->local.finalized = true; + + if (cgraph_decide_is_function_needed (node, decl)) + cgraph_mark_needed_node (node); + + if ((TREE_PUBLIC (decl) && !DECL_COMDAT (decl) && !DECL_EXTERNAL (decl)) + || (DECL_VIRTUAL_P (decl) + && (DECL_COMDAT (decl) || DECL_EXTERNAL (decl)))) + cgraph_mark_reachable_node (node); return node; } *************** dump_cgraph_node (FILE *f, struct cgraph *** 1874,1880 **** if (node->only_called_at_exit) fprintf (f, " only_called_at_exit"); ! fprintf (f, "\n called by: "); for (edge = node->callers; edge; edge = edge->next_caller) { fprintf (f, "%s/%i ", cgraph_node_name (edge->caller), --- 1884,1904 ---- if (node->only_called_at_exit) fprintf (f, " only_called_at_exit"); ! fprintf (f, "\n"); ! ! if (node->thunk.thunk_p) ! { ! fprintf (f, " thunk of %s (asm: %s) fixed offset %i virtual value %i has " ! "virtual offset %i)\n", ! lang_hooks.decl_printable_name (node->thunk.alias, 2), ! IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (node->thunk.alias)), ! (int)node->thunk.fixed_offset, ! (int)node->thunk.virtual_value, ! (int)node->thunk.virtual_offset_p); ! } ! ! fprintf (f, " called by: "); ! for (edge = node->callers; edge; edge = edge->next_caller) { fprintf (f, "%s/%i ", cgraph_node_name (edge->caller), *************** dump_cgraph_node (FILE *f, struct cgraph *** 1926,1945 **** if (node->same_body) { struct cgraph_node *n; ! fprintf (f, " aliases & thunks:"); for (n = node->same_body; n; n = n->next) { fprintf (f, " %s/%i", cgraph_node_name (n), n->uid); - if (n->thunk.thunk_p) - { - fprintf (f, " (thunk of %s fixed offset %i virtual value %i has " - "virtual offset %i", - lang_hooks.decl_printable_name (n->thunk.alias, 2), - (int)n->thunk.fixed_offset, - (int)n->thunk.virtual_value, - (int)n->thunk.virtual_offset_p); - fprintf (f, ")"); - } if (DECL_ASSEMBLER_NAME_SET_P (n->decl)) fprintf (f, " (asm: %s)", IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (n->decl))); } --- 1950,1959 ---- if (node->same_body) { struct cgraph_node *n; ! fprintf (f, " aliases:"); for (n = node->same_body; n; n = n->next) { fprintf (f, " %s/%i", cgraph_node_name (n), n->uid); if (DECL_ASSEMBLER_NAME_SET_P (n->decl)) fprintf (f, " (asm: %s)", IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (n->decl))); } Index: cgraph.h =================================================================== *** cgraph.h (revision 173251) --- cgraph.h (working copy) *************** varpool_next_static_initializer (struct *** 715,720 **** --- 715,793 ---- for ((node) = varpool_first_static_initializer (); (node); \ (node) = varpool_next_static_initializer (node)) + /* Return first function with body defined. */ + static inline struct cgraph_node * + cgraph_first_defined_function (void) + { + struct cgraph_node *node; + for (node = cgraph_nodes; node; node = node->next) + { + if (node->analyzed) + return node; + } + return NULL; + } + + /* Return next reachable static variable with initializer after NODE. */ + static inline struct cgraph_node * + cgraph_next_defined_function (struct cgraph_node *node) + { + for (node = node->next; node; node = node->next) + { + if (node->analyzed) + return node; + } + return NULL; + } + + /* Walk all functions with body defined. */ + #define FOR_EACH_DEFINED_FUNCTION(node) \ + for ((node) = cgraph_first_defined_function (); (node); \ + (node) = cgraph_next_defined_function (node)) + + + /* Return true when NODE is a function with Gimple body defined + in current unit. Functions can also be define externally or they + can be thunks with no Gimple representation. + + Note that at WPA stage, the function body may not be present in memory. */ + + static inline bool + cgraph_function_with_gimple_body_p (struct cgraph_node *node) + { + return node->analyzed && !node->thunk.thunk_p; + } + + /* Return first function with body defined. */ + static inline struct cgraph_node * + cgraph_first_function_with_gimple_body (void) + { + struct cgraph_node *node; + for (node = cgraph_nodes; node; node = node->next) + { + if (cgraph_function_with_gimple_body_p (node)) + return node; + } + return NULL; + } + + /* Return next reachable static variable with initializer after NODE. */ + static inline struct cgraph_node * + cgraph_next_function_with_gimple_body (struct cgraph_node *node) + { + for (node = node->next; node; node = node->next) + { + if (cgraph_function_with_gimple_body_p (node)) + return node; + } + return NULL; + } + + /* Walk all functions with body defined. */ + #define FOR_EACH_FUNCTION_WITH_GIMPLE_BODY(node) \ + for ((node) = cgraph_first_function_with_gimple_body (); (node); \ + (node) = cgraph_next_function_with_gimple_body (node)) + /* Create a new static variable of type TYPE. */ tree add_new_static_var (tree type); Index: ipa-cp.c =================================================================== *** ipa-cp.c (revision 173251) --- ipa-cp.c (working copy) *************** ipcp_need_redirect_p (struct cgraph_edge *** 951,956 **** --- 951,960 ---- if (!n_cloning_candidates) return false; + /* We can't redirect anything in thunks, yet. */ + if (cs->caller->thunk.thunk_p) + return true; + if ((orig = ipcp_get_orig_node (node)) != NULL) node = orig; if (ipcp_get_orig_node (cs->caller)) *************** ipcp_generate_summary (void) *** 1508,1515 **** fprintf (dump_file, "\nIPA constant propagation start:\n"); ipa_register_cgraph_hooks (); ! for (node = cgraph_nodes; node; node = node->next) ! if (node->analyzed) { /* Unreachable nodes should have been eliminated before ipcp. */ gcc_assert (node->needed || node->reachable); --- 1512,1520 ---- fprintf (dump_file, "\nIPA constant propagation start:\n"); ipa_register_cgraph_hooks (); ! /* FIXME: We could propagate through thunks happily and we could be ! even able to clone them, if needed. Do that later. */ ! FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node) { /* Unreachable nodes should have been eliminated before ipcp. */ gcc_assert (node->needed || node->reachable); Index: cgraphunit.c =================================================================== *** cgraphunit.c (revision 173251) --- cgraphunit.c (working copy) *************** cgraph_finalize_function (tree decl, boo *** 370,376 **** to those so we need to analyze them. FIXME: We should introduce may edges for this purpose and update their handling in unreachable function removal and inliner too. */ ! || (DECL_VIRTUAL_P (decl) && (DECL_COMDAT (decl) || DECL_EXTERNAL (decl)))) cgraph_mark_reachable_node (node); /* If we've not yet emitted decl, tell the debug info about it. */ --- 370,377 ---- to those so we need to analyze them. FIXME: We should introduce may edges for this purpose and update their handling in unreachable function removal and inliner too. */ ! || (DECL_VIRTUAL_P (decl) ! && optimize && (DECL_COMDAT (decl) || DECL_EXTERNAL (decl)))) cgraph_mark_reachable_node (node); /* If we've not yet emitted decl, tell the debug info about it. */ *************** verify_cgraph_node (struct cgraph_node * *** 624,633 **** while (n != node); } ! if (node->analyzed && gimple_has_body_p (node->decl) ! && !TREE_ASM_WRITTEN (node->decl) ! && (!DECL_EXTERNAL (node->decl) || node->global.inlined_to) ! && !flag_wpa) { if (this_cfun->cfg) { --- 625,652 ---- while (n != node); } ! if (node->analyzed && node->thunk.thunk_p) ! { ! if (!node->callees) ! { ! error ("No edge out of thunk node"); ! error_found = true; ! } ! else if (node->callees->next_callee) ! { ! error ("More than one edge out of thunk node"); ! error_found = true; ! } ! if (gimple_has_body_p (node->decl)) ! { ! error ("Thunk is not supposed to have body"); ! error_found = true; ! } ! } ! else if (node->analyzed && gimple_has_body_p (node->decl) ! && !TREE_ASM_WRITTEN (node->decl) ! && (!DECL_EXTERNAL (node->decl) || node->global.inlined_to) ! && !flag_wpa) { if (this_cfun->cfg) { *************** verify_cgraph_node (struct cgraph_node * *** 656,663 **** } if (!e->indirect_unknown_callee) { - struct cgraph_node *n; - if (e->callee->same_body_alias) { error ("edge points to same body alias:"); --- 675,680 ---- *************** verify_cgraph_node (struct cgraph_node * *** 678,693 **** debug_tree (decl); error_found = true; } - else if (decl - && (n = cgraph_get_node_or_alias (decl)) - && (n->same_body_alias - && n->thunk.thunk_p)) - { - error ("a call to thunk improperly represented " - "in the call graph:"); - cgraph_debug_gimple_stmt (this_cfun, stmt); - error_found = true; - } } else if (decl) { --- 695,700 ---- *************** cgraph_analyze_function (struct cgraph_n *** 780,802 **** tree save = current_function_decl; tree decl = node->decl; ! current_function_decl = decl; ! push_cfun (DECL_STRUCT_FUNCTION (decl)); ! assign_assembler_name_if_neeeded (node->decl); ! /* Make sure to gimplify bodies only once. During analyzing a ! function we lower it, which will require gimplified nested ! functions, so we can end up here with an already gimplified ! body. */ ! if (!gimple_body (decl)) ! gimplify_function_tree (decl); ! dump_function (TDI_generic, decl); ! cgraph_lower_function (node); node->analyzed = true; - pop_cfun (); current_function_decl = save; } --- 787,817 ---- tree save = current_function_decl; tree decl = node->decl; ! if (node->thunk.thunk_p) ! { ! cgraph_create_edge (node, cgraph_get_node (node->thunk.alias), ! NULL, 0, CGRAPH_FREQ_BASE); ! } ! else ! { ! current_function_decl = decl; ! push_cfun (DECL_STRUCT_FUNCTION (decl)); ! assign_assembler_name_if_neeeded (node->decl); ! /* Make sure to gimplify bodies only once. During analyzing a ! function we lower it, which will require gimplified nested ! functions, so we can end up here with an already gimplified ! body. */ ! if (!gimple_body (decl)) ! gimplify_function_tree (decl); ! dump_function (TDI_generic, decl); ! cgraph_lower_function (node); ! pop_cfun (); ! } node->analyzed = true; current_function_decl = save; } *************** cgraph_analyze_functions (void) *** 969,975 **** /* ??? 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)) { cgraph_reset_node (node); continue; --- 984,991 ---- /* ??? 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) ! && !node->thunk.thunk_p) { cgraph_reset_node (node); continue; *************** cgraph_analyze_functions (void) *** 981,986 **** --- 997,1005 ---- for (edge = node->callees; edge; edge = edge->next_callee) if (!edge->callee->reachable) cgraph_mark_reachable_node (edge->callee); + for (edge = node->callers; edge; edge = edge->next_caller) + if (!edge->caller->reachable && edge->caller->thunk.thunk_p) + cgraph_mark_reachable_node (edge->caller); if (node->same_comdat_group) { *************** cgraph_analyze_functions (void) *** 1031,1040 **** tree decl = node->decl; next = node->next; ! if (node->local.finalized && !gimple_has_body_p (decl)) cgraph_reset_node (node); ! if (!node->reachable && gimple_has_body_p (decl)) { if (cgraph_dump_file) fprintf (cgraph_dump_file, " %s", cgraph_node_name (node)); --- 1050,1061 ---- tree decl = node->decl; next = node->next; ! if (node->local.finalized && !gimple_has_body_p (decl) ! && !node->thunk.thunk_p) cgraph_reset_node (node); ! if (!node->reachable ! && (gimple_has_body_p (decl) || node->thunk.thunk_p)) { if (cgraph_dump_file) fprintf (cgraph_dump_file, " %s", cgraph_node_name (node)); *************** cgraph_analyze_functions (void) *** 1043,1049 **** } else node->next_needed = NULL; ! gcc_assert (!node->local.finalized || gimple_has_body_p (decl)); gcc_assert (node->analyzed == node->local.finalized); } if (cgraph_dump_file) --- 1064,1071 ---- } else node->next_needed = NULL; ! gcc_assert (!node->local.finalized || node->thunk.thunk_p ! || gimple_has_body_p (decl)); gcc_assert (node->analyzed == node->local.finalized); } if (cgraph_dump_file) *************** cgraph_mark_functions_to_output (void) *** 1132,1137 **** --- 1154,1160 ---- always inlined, as well as those that are reachable from outside the current compilation unit. */ if (node->analyzed + && !node->thunk.thunk_p && !node->global.inlined_to && (!cgraph_only_called_directly_p (node) || (e && node->reachable)) *************** cgraph_mark_functions_to_output (void) *** 1145,1151 **** for (next = node->same_comdat_group; next != node; next = next->same_comdat_group) ! next->process = 1; } } else if (node->same_comdat_group) --- 1168,1175 ---- for (next = node->same_comdat_group; next != node; next = next->same_comdat_group) ! if (!next->thunk.thunk_p) ! next->process = 1; } } else if (node->same_comdat_group) *************** assemble_thunk (struct cgraph_node *node *** 1406,1411 **** --- 1430,1437 ---- free_after_compilation (cfun); set_cfun (NULL); TREE_ASM_WRITTEN (thunk_fndecl) = 1; + node->thunk.thunk_p = false; + node->analyzed = false; } else { *************** assemble_thunk (struct cgraph_node *node *** 1530,1544 **** delete_unreachable_blocks (); update_ssa (TODO_update_ssa); - cgraph_remove_same_body_alias (node); /* Since we want to emit the thunk, we explicitly mark its name as referenced. */ cgraph_add_new_function (thunk_fndecl, true); bitmap_obstack_release (NULL); } current_function_decl = NULL; } /* Expand function specified by NODE. */ static void --- 1556,1591 ---- delete_unreachable_blocks (); update_ssa (TODO_update_ssa); /* Since we want to emit the thunk, we explicitly mark its name as referenced. */ + node->thunk.thunk_p = false; + cgraph_node_remove_callees (node); cgraph_add_new_function (thunk_fndecl, true); bitmap_obstack_release (NULL); } current_function_decl = NULL; } + + /* Assemble thunks asociated to NODE. */ + + static void + assemble_thunks (struct cgraph_node *node) + { + struct cgraph_edge *e; + for (e = node->callers; e;) + if (e->caller->thunk.thunk_p) + { + struct cgraph_node *thunk = e->caller; + + e = e->next_caller; + assemble_thunks (thunk); + assemble_thunk (thunk); + } + else + e = e->next_caller; + } + /* Expand function specified by NODE. */ static void *************** cgraph_expand_function (struct cgraph_no *** 1566,1578 **** if (!alias->thunk.thunk_p) assemble_alias (alias->decl, DECL_ASSEMBLER_NAME (alias->thunk.alias)); - else - assemble_thunk (alias); } node->alias = saved_alias; cgraph_process_new_functions (); } gcc_assert (node->lowered); /* Generate RTL for the body of DECL. */ --- 1613,1624 ---- if (!alias->thunk.thunk_p) assemble_alias (alias->decl, DECL_ASSEMBLER_NAME (alias->thunk.alias)); } node->alias = saved_alias; cgraph_process_new_functions (); } + assemble_thunks (node); gcc_assert (node->lowered); /* Generate RTL for the body of DECL. */ *************** cgraph_output_in_order (void) *** 1688,1694 **** for (pf = cgraph_nodes; pf; pf = pf->next) { ! if (pf->process) { i = pf->order; gcc_assert (nodes[i].kind == ORDER_UNDEFINED); --- 1734,1740 ---- for (pf = cgraph_nodes; pf; pf = pf->next) { ! if (pf->process && !pf->thunk.thunk_p) { i = pf->order; gcc_assert (nodes[i].kind == ORDER_UNDEFINED); Index: lto-cgraph.c =================================================================== *** lto-cgraph.c (revision 173251) --- lto-cgraph.c (working copy) *************** lto_output_node (struct lto_simple_outpu *** 502,510 **** --- 502,525 ---- bp_pack_value (&bp, node->frequency, 2); bp_pack_value (&bp, node->only_called_at_startup, 1); bp_pack_value (&bp, node->only_called_at_exit, 1); + bp_pack_value (&bp, node->thunk.thunk_p && !boundary_p, 1); lto_output_bitpack (&bp); lto_output_uleb128_stream (ob->main_stream, node->resolution); + if (node->thunk.thunk_p && !boundary_p) + { + lto_output_uleb128_stream + (ob->main_stream, + 1 + (node->thunk.this_adjusting != 0) * 2 + + (node->thunk.virtual_offset_p != 0) * 4); + lto_output_uleb128_stream (ob->main_stream, + node->thunk.fixed_offset); + lto_output_uleb128_stream (ob->main_stream, + node->thunk.virtual_value); + lto_output_fn_decl_index (ob->decl_state, ob->main_stream, + node->thunk.alias); + } + if (node->same_body) { struct cgraph_node *alias; *************** lto_output_node (struct lto_simple_outpu *** 516,540 **** { lto_output_fn_decl_index (ob->decl_state, ob->main_stream, alias->decl); ! if (alias->thunk.thunk_p) ! { ! lto_output_uleb128_stream ! (ob->main_stream, ! 1 + (alias->thunk.this_adjusting != 0) * 2 ! + (alias->thunk.virtual_offset_p != 0) * 4); ! lto_output_uleb128_stream (ob->main_stream, ! alias->thunk.fixed_offset); ! lto_output_uleb128_stream (ob->main_stream, ! alias->thunk.virtual_value); ! lto_output_fn_decl_index (ob->decl_state, ob->main_stream, ! alias->thunk.alias); ! } ! else ! { ! lto_output_uleb128_stream (ob->main_stream, 0); ! lto_output_fn_decl_index (ob->decl_state, ob->main_stream, ! alias->thunk.alias); ! } gcc_assert (cgraph_get_node (alias->thunk.alias) == node); lto_output_uleb128_stream (ob->main_stream, alias->resolution); alias = alias->previous; --- 531,538 ---- { lto_output_fn_decl_index (ob->decl_state, ob->main_stream, alias->decl); ! lto_output_fn_decl_index (ob->decl_state, ob->main_stream, ! alias->thunk.alias); gcc_assert (cgraph_get_node (alias->thunk.alias) == node); lto_output_uleb128_stream (ob->main_stream, alias->resolution); alias = alias->previous; *************** input_overwrite_node (struct lto_file_de *** 947,952 **** --- 945,951 ---- node->frequency = (enum node_frequency)bp_unpack_value (bp, 2); node->only_called_at_startup = bp_unpack_value (bp, 1); node->only_called_at_exit = bp_unpack_value (bp, 1); + node->thunk.thunk_p = bp_unpack_value (bp, 1); node->resolution = resolution; } *************** input_node (struct lto_file_decl_data *f *** 1031,1064 **** /* Store a reference for now, and fix up later to be a pointer. */ node->same_comdat_group = (cgraph_node_ptr) (intptr_t) ref2; same_body_count = lto_input_uleb128 (ib); while (same_body_count-- > 0) { ! tree alias_decl; ! int type; struct cgraph_node *alias; decl_index = lto_input_uleb128 (ib); alias_decl = lto_file_decl_data_get_fn_decl (file_data, decl_index); ! type = lto_input_uleb128 (ib); ! if (!type) ! { ! tree real_alias; ! decl_index = lto_input_uleb128 (ib); ! real_alias = lto_file_decl_data_get_fn_decl (file_data, decl_index); ! alias = cgraph_same_body_alias (node, alias_decl, real_alias); ! } ! else ! { ! HOST_WIDE_INT fixed_offset = lto_input_uleb128 (ib); ! HOST_WIDE_INT virtual_value = lto_input_uleb128 (ib); ! tree real_alias; ! decl_index = lto_input_uleb128 (ib); ! real_alias = lto_file_decl_data_get_fn_decl (file_data, decl_index); ! alias = cgraph_add_thunk (node, alias_decl, fn_decl, type & 2, fixed_offset, ! virtual_value, ! (type & 4) ? size_int (virtual_value) : NULL_TREE, ! real_alias); ! } gcc_assert (alias); alias->resolution = (enum ld_plugin_symbol_resolution)lto_input_uleb128 (ib); } --- 1030,1062 ---- /* Store a reference for now, and fix up later to be a pointer. */ node->same_comdat_group = (cgraph_node_ptr) (intptr_t) ref2; + if (node->thunk.thunk_p) + { + int type = lto_input_uleb128 (ib); + HOST_WIDE_INT fixed_offset = lto_input_uleb128 (ib); + HOST_WIDE_INT virtual_value = lto_input_uleb128 (ib); + tree real_alias; + + decl_index = lto_input_uleb128 (ib); + real_alias = lto_file_decl_data_get_fn_decl (file_data, decl_index); + node->thunk.fixed_offset = fixed_offset; + node->thunk.this_adjusting = (type & 2); + node->thunk.virtual_value = virtual_value; + node->thunk.virtual_offset_p = (type & 4); + node->thunk.alias = real_alias; + } + same_body_count = lto_input_uleb128 (ib); while (same_body_count-- > 0) { ! tree alias_decl, real_alias; struct cgraph_node *alias; + decl_index = lto_input_uleb128 (ib); alias_decl = lto_file_decl_data_get_fn_decl (file_data, decl_index); ! decl_index = lto_input_uleb128 (ib); ! real_alias = lto_file_decl_data_get_fn_decl (file_data, decl_index); ! alias = cgraph_same_body_alias (node, alias_decl, real_alias); gcc_assert (alias); alias->resolution = (enum ld_plugin_symbol_resolution)lto_input_uleb128 (ib); } Index: ipa-pure-const.c =================================================================== *** ipa-pure-const.c (revision 173251) --- ipa-pure-const.c (working copy) *************** analyze_function (struct cgraph_node *fn *** 731,736 **** --- 731,746 ---- l->looping_previously_known = true; l->looping = false; l->can_throw = false; + state_from_flags (&l->state_previously_known, &l->looping_previously_known, + flags_from_decl_or_type (fn->decl), + cgraph_node_cannot_return (fn)); + + if (fn->thunk.thunk_p) + { + /* Thunk gets propagated through, so nothing interesting happens. */ + gcc_assert (ipa); + return l; + } if (dump_file) { *************** end: *** 799,807 **** if (dump_file && (dump_flags & TDF_DETAILS)) fprintf (dump_file, " checking previously known:"); - state_from_flags (&l->state_previously_known, &l->looping_previously_known, - flags_from_decl_or_type (fn->decl), - cgraph_node_cannot_return (fn)); better_state (&l->pure_const_state, &l->looping, l->state_previously_known, --- 809,814 ---- Index: lto-streamer-out.c =================================================================== *** lto-streamer-out.c (revision 173251) --- lto-streamer-out.c (working copy) *************** lto_output (cgraph_node_set set, varpool *** 2197,2203 **** for (i = 0; i < n_nodes; i++) { node = lto_cgraph_encoder_deref (encoder, i); ! if (lto_cgraph_encoder_encode_body_p (encoder, node)) { #ifdef ENABLE_CHECKING gcc_assert (!bitmap_bit_p (output, DECL_UID (node->decl))); --- 2197,2204 ---- for (i = 0; i < n_nodes; i++) { node = lto_cgraph_encoder_deref (encoder, i); ! if (lto_cgraph_encoder_encode_body_p (encoder, node) ! && !node->thunk.thunk_p) { #ifdef ENABLE_CHECKING gcc_assert (!bitmap_bit_p (output, DECL_UID (node->decl))); Index: ipa-inline.c =================================================================== *** ipa-inline.c (revision 173251) --- ipa-inline.c (working copy) *************** inline_small_functions (void) *** 1177,1185 **** max_count = 0; initialize_growth_caches (); ! for (node = cgraph_nodes; node; node = node->next) ! if (node->analyzed ! && !node->global.inlined_to) { struct inline_summary *info = inline_summary (node); --- 1177,1184 ---- max_count = 0; initialize_growth_caches (); ! FOR_EACH_DEFINED_FUNCTION (node) ! if (!node->global.inlined_to) { struct inline_summary *info = inline_summary (node); *************** inline_small_functions (void) *** 1197,1205 **** /* Populate the heeap with all edges we might inline. */ ! for (node = cgraph_nodes; node; node = node->next) ! if (node->analyzed ! && !node->global.inlined_to) { if (dump_file) fprintf (dump_file, "Enqueueing calls of %s/%i.\n", --- 1196,1203 ---- /* Populate the heeap with all edges we might inline. */ ! FOR_EACH_DEFINED_FUNCTION (node) ! if (!node->global.inlined_to) { if (dump_file) fprintf (dump_file, "Enqueueing calls of %s/%i.\n", Index: ipa.c =================================================================== *** ipa.c (revision 173251) --- ipa.c (working copy) *************** function_and_variable_visibility (bool w *** 877,883 **** --- 877,923 ---- segfault though. */ dissolve_same_comdat_group_list (node); } + + if (node->thunk.thunk_p + && TREE_PUBLIC (node->decl)) + { + struct cgraph_node *decl_node = node; + + while (decl_node->thunk.thunk_p) + decl_node = decl_node->callees->callee; + + /* Thunks have the same visibility as function they are attached to. + For some reason C++ frontend don't seem to care. I.e. in + g++.dg/torture/pr41257-2.C the thunk is not comdat while function + it is attached to is. + + We also need to arrange the thunk into the same comdat group as + the function it reffers to. */ + if (DECL_COMDAT (decl_node->decl)) + { + DECL_COMDAT (node->decl) = 1; + DECL_COMDAT_GROUP (node->decl) = DECL_COMDAT_GROUP (decl_node->decl); + if (!node->same_comdat_group) + { + node->same_comdat_group = decl_node; + if (!decl_node->same_comdat_group) + decl_node->same_comdat_group = node; + else + { + struct cgraph_node *n; + for (n = decl_node->same_comdat_group; + n->same_comdat_group != decl_node; + n = n->same_comdat_group) + ; + n->same_comdat_group = node; + } + } + } + if (DECL_EXTERNAL (decl_node->decl)) + DECL_EXTERNAL (node->decl) = 1; + } node->local.local = cgraph_local_node_p (node); + } for (vnode = varpool_nodes; vnode; vnode = vnode->next) { Index: ipa-inline-analysis.c =================================================================== *** ipa-inline-analysis.c (revision 173251) --- ipa-inline-analysis.c (working copy) *************** compute_inline_parameters (struct cgraph *** 1443,1448 **** --- 1443,1465 ---- info = inline_summary (node); + /* FIXME: Thunks are inlinable, but tree-inline don't know how to do that. + Once this happen, we will need to more curefully predict call + statement size. */ + if (node->thunk.thunk_p) + { + struct inline_edge_summary *es = inline_edge_summary (node->callees); + struct predicate t = true_predicate (); + + info->inlinable = info->versionable = 0; + node->callees->call_stmt_cannot_inline_p = true; + node->local.can_change_signature = false; + es->call_stmt_time = 1; + es->call_stmt_size = 1; + account_size_time (info, 0, 0, &t); + return; + } + /* Estimate the stack size for the function if we're optimizing. */ self_stack_size = optimize ? estimated_stack_frame_size (node) : 0; info->estimated_self_stack_size = self_stack_size; *************** inline_analyze_function (struct cgraph_n *** 2027,2033 **** cgraph_node_name (node), node->uid); /* FIXME: We should remove the optimize check after we ensure we never run IPA passes when not optimizing. */ ! if (flag_indirect_inlining && optimize) inline_indirect_intraprocedural_analysis (node); compute_inline_parameters (node, false); --- 2044,2050 ---- cgraph_node_name (node), node->uid); /* FIXME: We should remove the optimize check after we ensure we never run IPA passes when not optimizing. */ ! if (flag_indirect_inlining && optimize && !node->thunk.thunk_p) inline_indirect_intraprocedural_analysis (node); compute_inline_parameters (node, false); *************** inline_generate_summary (void) *** 2058,2065 **** if (flag_indirect_inlining) ipa_register_cgraph_hooks (); ! for (node = cgraph_nodes; node; node = node->next) ! if (node->analyzed) inline_analyze_function (node); } --- 2075,2081 ---- if (flag_indirect_inlining) ipa_register_cgraph_hooks (); ! FOR_EACH_DEFINED_FUNCTION (node) inline_analyze_function (node); } Index: lto/lto.c =================================================================== *** lto/lto.c (revision 173251) --- lto/lto.c (working copy) *************** lto_materialize_function (struct cgraph_ *** 147,155 **** decl = node->decl; /* Read in functions with body (analyzed nodes) and also functions that are needed to produce virtual clones. */ ! if (node->analyzed || has_analyzed_clone_p (node)) { ! /* Clones don't need to be read. */ if (node->clone_of) return; --- 147,155 ---- decl = node->decl; /* Read in functions with body (analyzed nodes) and also functions that are needed to produce virtual clones. */ ! if (cgraph_function_with_gimple_body_p (node) || has_analyzed_clone_p (node)) { ! /* Clones and thunks don't need to be read. */ if (node->clone_of) return; *************** static void *** 1183,1188 **** --- 1183,1194 ---- add_cgraph_node_to_partition (ltrans_partition part, struct cgraph_node *node) { struct cgraph_edge *e; + cgraph_node_set_iterator csi; + + /* If NODE is already there, we have nothing to do. */ + csi = cgraph_node_set_find (part->cgraph_set, node); + if (!csi_end_p (csi)) + return; part->insns += inline_summary (node)->self_size; *************** add_cgraph_node_to_partition (ltrans_par *** 1197,1202 **** --- 1203,1215 ---- cgraph_node_set_add (part->cgraph_set, node); + /* Thunks always must go along with function they reffer to. */ + if (node->thunk.thunk_p) + add_cgraph_node_to_partition (part, node->callees->callee); + for (e = node->callers; e; e = e->next_caller) + if (e->caller->thunk.thunk_p) + add_cgraph_node_to_partition (part, e->caller); + for (e = node->callees; e; e = e->next_callee) if ((!e->inline_failed || DECL_COMDAT (e->callee->decl)) && !cgraph_node_in_set_p (e->callee, part->cgraph_set)) *************** add_cgraph_node_to_partition (ltrans_par *** 1214,1219 **** --- 1227,1239 ---- static void add_varpool_node_to_partition (ltrans_partition part, struct varpool_node *vnode) { + varpool_node_set_iterator vsi; + + /* If NODE is already there, we have nothing to do. */ + vsi = varpool_node_set_find (part->varpool_set, vnode); + if (!vsi_end_p (vsi)) + return; + varpool_node_set_add (part->varpool_set, vnode); if (vnode->aux) Index: ipa-prop.c =================================================================== *** ipa-prop.c (revision 173251) --- ipa-prop.c (working copy) *************** ipa_prop_write_jump_functions (cgraph_no *** 2888,2894 **** for (csi = csi_start (set); !csi_end_p (csi); csi_next (&csi)) { node = csi_node (csi); ! if (node->analyzed && IPA_NODE_REF (node) != NULL) count++; } --- 2888,2895 ---- for (csi = csi_start (set); !csi_end_p (csi); csi_next (&csi)) { node = csi_node (csi); ! if (cgraph_function_with_gimple_body_p (node) ! && IPA_NODE_REF (node) != NULL) count++; } *************** ipa_prop_write_jump_functions (cgraph_no *** 2898,2904 **** for (csi = csi_start (set); !csi_end_p (csi); csi_next (&csi)) { node = csi_node (csi); ! if (node->analyzed && IPA_NODE_REF (node) != NULL) ipa_write_node_info (ob, node); } lto_output_1_stream (ob->main_stream, 0); --- 2899,2906 ---- for (csi = csi_start (set); !csi_end_p (csi); csi_next (&csi)) { node = csi_node (csi); ! if (cgraph_function_with_gimple_body_p (node) ! && IPA_NODE_REF (node) != NULL) ipa_write_node_info (ob, node); } lto_output_1_stream (ob->main_stream, 0); Index: passes.c =================================================================== *** passes.c (revision 173251) --- passes.c (working copy) *************** do_per_function_toporder (void (*callbac *** 1135,1141 **** /* Allow possibly removed nodes to be garbage collected. */ order[i] = NULL; node->process = 0; ! if (node->analyzed) { push_cfun (DECL_STRUCT_FUNCTION (node->decl)); current_function_decl = node->decl; --- 1135,1141 ---- /* Allow possibly removed nodes to be garbage collected. */ order[i] = NULL; node->process = 0; ! if (cgraph_function_with_gimple_body_p (node)) { push_cfun (DECL_STRUCT_FUNCTION (node->decl)); current_function_decl = node->decl; *************** execute_one_pass (struct opt_pass *pass) *** 1581,1590 **** if (pass->type == IPA_PASS) { struct cgraph_node *node; ! for (node = cgraph_nodes; node; node = node->next) ! if (node->analyzed) ! VEC_safe_push (ipa_opt_pass, heap, node->ipa_transforms_to_apply, ! (struct ipa_opt_pass_d *)pass); } if (!current_function_decl) --- 1581,1589 ---- if (pass->type == IPA_PASS) { struct cgraph_node *node; ! FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node) ! VEC_safe_push (ipa_opt_pass, heap, node->ipa_transforms_to_apply, ! (struct ipa_opt_pass_d *)pass); } if (!current_function_decl) *************** ipa_write_summaries (void) *** 1705,1711 **** { struct cgraph_node *node = order[i]; ! if (node->analyzed) { /* When streaming out references to statements as part of some IPA pass summary, the statements need to have uids assigned and the --- 1704,1710 ---- { struct cgraph_node *node = order[i]; ! if (cgraph_function_with_gimple_body_p (node)) { /* When streaming out references to statements as part of some IPA pass summary, the statements need to have uids assigned and the *************** ipa_write_summaries (void) *** 1718,1724 **** pop_cfun (); } if (node->analyzed) ! cgraph_node_set_add (set, node); } vset = varpool_node_set_new (); --- 1717,1723 ---- pop_cfun (); } if (node->analyzed) ! cgraph_node_set_add (set, node); } vset = varpool_node_set_new (); *************** function_called_by_processed_nodes_p (vo *** 2036,2042 **** { if (e->caller->decl == current_function_decl) continue; ! if (!e->caller->analyzed) continue; if (TREE_ASM_WRITTEN (e->caller->decl)) continue; --- 2035,2041 ---- { if (e->caller->decl == current_function_decl) continue; ! if (!cgraph_function_with_gimple_body_p (e->caller)) continue; if (TREE_ASM_WRITTEN (e->caller->decl)) continue;