void clone_inlined_nodes (struct cgraph_edge *e, bool duplicate, bool update_original, int *overall_size) { if (duplicate) { /* We may eliminate the need for out-of-line copy to be output. In that case just go ahead and re-use it. This is not just an memory optimization. Making offline copy of fuction disappear from the program will improve future decisions on inlining. */ if (!e->callee->callers->next_caller /* Recursive inlining never wants the master clone to be overwritten. */ && update_original && can_remove_node_now_p (e->callee, e)) { /* TODO: When callee is in a comdat group, we could remove all of it, including all inline clones inlined into it. That would however need small function inlining to register edge removal hook to maintain the priority queue. For now we keep the ohter functions in the group in program until cgraph_remove_unreachable_functions gets rid of them. */ gcc_assert (!e->callee->global.inlined_to); symtab_dissolve_same_comdat_group_list ((symtab_node) e->callee); if (e->callee->analyzed && !DECL_EXTERNAL (e->callee->symbol.decl)) { if (overall_size) *overall_size -= inline_summary (e->callee)->size; nfunctions_inlined++; } duplicate = false; e->callee->symbol.externally_visible = false; update_noncloned_frequencies (e->callee, e->frequency); } else { struct cgraph_node *n; n = cgraph_clone_node (e->callee, e->callee->symbol.decl, e->count, e->frequency, update_original, vNULL, true); cgraph_redirect_edge_callee (e, n); } } else symtab_dissolve_same_comdat_group_list ((symtab_node) e->callee); if (e->caller->global.inlined_to) e->callee->global.inlined_to = e->caller->global.inlined_to; else e->callee->global.inlined_to = e->caller; /* Recursively clone all bodies. */ for (e = e->callee->callees; e; e = e->next_callee) if (!e->inline_failed) clone_inlined_nodes (e, duplicate, update_original, overall_size); }
bool inline_call (struct cgraph_edge *e, bool update_original, vec<cgraph_edge_p> *new_edges, int *overall_size, bool update_overall_summary) { int old_size = 0, new_size = 0; struct cgraph_node *to = NULL; struct cgraph_edge *curr = e; struct cgraph_node *callee = cgraph_function_or_thunk_node (e->callee, NULL); bool new_edges_found = false; #ifdef ENABLE_CHECKING int estimated_growth = estimate_edge_growth (e); bool predicated = inline_edge_summary (e)->predicate != NULL; #endif /* Don't inline inlined edges. */ gcc_assert (e->inline_failed); /* Don't even think of inlining inline clone. */ gcc_assert (!callee->global.inlined_to); e->inline_failed = CIF_OK; DECL_POSSIBLY_INLINED (callee->symbol.decl) = true; to = e->caller; if (to->global.inlined_to) to = to->global.inlined_to; /* If aliases are involved, redirect edge to the actual destination and possibly remove the aliases. */ if (e->callee != callee) { struct cgraph_node *alias = e->callee, *next_alias; cgraph_redirect_edge_callee (e, callee); while (alias && alias != callee) { if (!alias->callers && can_remove_node_now_p (alias, e)) { next_alias = cgraph_alias_target (alias); cgraph_remove_node (alias); alias = next_alias; } else break; } } clone_inlined_nodes (e, true, update_original, overall_size); gcc_assert (curr->callee->global.inlined_to == to); old_size = inline_summary (to)->size; inline_merge_summary (e); if (optimize) new_edges_found = ipa_propagate_indirect_call_infos (curr, new_edges); if (update_overall_summary) inline_update_overall_summary (to); new_size = inline_summary (to)->size; #ifdef ENABLE_CHECKING /* Verify that estimated growth match real growth. Allow off-by-one error due to INLINE_SIZE_SCALE roudoff errors. */ gcc_assert (!update_overall_summary || !overall_size || new_edges_found || abs (estimated_growth - (new_size - old_size)) <= 1 /* FIXME: a hack. Edges with false predicate are accounted wrong, we should remove them from callgraph. */ || predicated); #endif /* Account the change of overall unit size; external functions will be removed and are thus not accounted. */ if (overall_size && !DECL_EXTERNAL (to->symbol.decl)) *overall_size += new_size - old_size; ncalls_inlined++; /* This must happen after inline_merge_summary that rely on jump functions of callee to not be updated. */ return new_edges_found; }
void clone_inlined_nodes (struct cgraph_edge *e, bool duplicate, bool update_original, int *overall_size, int freq_scale) { struct cgraph_node *inlining_into; struct cgraph_edge *next; if (e->caller->global.inlined_to) inlining_into = e->caller->global.inlined_to; else inlining_into = e->caller; if (duplicate) { /* We may eliminate the need for out-of-line copy to be output. In that case just go ahead and re-use it. This is not just an memory optimization. Making offline copy of fuction disappear from the program will improve future decisions on inlining. */ if (!e->callee->callers->next_caller /* Recursive inlining never wants the master clone to be overwritten. */ && update_original && can_remove_node_now_p (e->callee, e) /* We cannot overwrite a master clone with non-inline clones until after these clones are materialized. */ && !master_clone_with_noninline_clones_p (e->callee)) { /* TODO: When callee is in a comdat group, we could remove all of it, including all inline clones inlined into it. That would however need small function inlining to register edge removal hook to maintain the priority queue. For now we keep the ohter functions in the group in program until cgraph_remove_unreachable_functions gets rid of them. */ gcc_assert (!e->callee->global.inlined_to); e->callee->dissolve_same_comdat_group_list (); if (e->callee->definition && !DECL_EXTERNAL (e->callee->decl)) { if (overall_size) *overall_size -= inline_summary (e->callee)->size; nfunctions_inlined++; } duplicate = false; e->callee->externally_visible = false; update_noncloned_frequencies (e->callee, e->frequency); } else { struct cgraph_node *n; if (freq_scale == -1) freq_scale = e->frequency; n = e->callee->create_clone (e->callee->decl, MIN (e->count, e->callee->count), freq_scale, update_original, vNULL, true, inlining_into, NULL); e->redirect_callee (n); } } else e->callee->dissolve_same_comdat_group_list (); e->callee->global.inlined_to = inlining_into; /* Recursively clone all bodies. */ for (e = e->callee->callees; e; e = next) { next = e->next_callee; if (!e->inline_failed) clone_inlined_nodes (e, duplicate, update_original, overall_size, freq_scale); if (e->speculative && !speculation_useful_p (e, true)) { e->resolve_speculation (NULL); speculation_removed = true; } } }