Mercurial > hg > CbC > CbC_gcc
diff gcc/ipa-utils.c @ 111:04ced10e8804
gcc 7
author | kono |
---|---|
date | Fri, 27 Oct 2017 22:46:09 +0900 |
parents | 77e2b8dfacca |
children | 84e7813d76e9 |
line wrap: on
line diff
--- a/gcc/ipa-utils.c Sun Aug 21 07:07:55 2011 +0900 +++ b/gcc/ipa-utils.c Fri Oct 27 22:46:09 2017 +0900 @@ -1,5 +1,5 @@ /* Utilities for ipa analysis. - Copyright (C) 2005, 2007, 2008 Free Software Foundation, Inc. + Copyright (C) 2005-2017 Free Software Foundation, Inc. Contributed by Kenneth Zadeck <zadeck@naturalbridge.com> This file is part of GCC. @@ -21,52 +21,49 @@ #include "config.h" #include "system.h" #include "coretypes.h" -#include "tm.h" +#include "backend.h" #include "tree.h" -#include "tree-flow.h" -#include "tree-inline.h" -#include "tree-pass.h" -#include "langhooks.h" -#include "pointer-set.h" +#include "gimple.h" +#include "predict.h" +#include "alloc-pool.h" +#include "cgraph.h" +#include "lto-streamer.h" +#include "dumpfile.h" #include "splay-tree.h" -#include "ggc.h" #include "ipa-utils.h" -#include "ipa-reference.h" -#include "gimple.h" -#include "cgraph.h" -#include "output.h" -#include "flags.h" -#include "timevar.h" -#include "diagnostic.h" -#include "langhooks.h" +#include "symbol-summary.h" +#include "tree-vrp.h" +#include "ipa-prop.h" +#include "ipa-fnsummary.h" /* Debugging function for postorder and inorder code. NOTE is a string that is printed before the nodes are printed. ORDER is an array of cgraph_nodes that has COUNT useful nodes in it. */ void -ipa_utils_print_order (FILE* out, - const char * note, - struct cgraph_node** order, - int count) +ipa_print_order (FILE* out, + const char * note, + struct cgraph_node** order, + int count) { int i; fprintf (out, "\n\n ordered call graph: %s\n", note); for (i = count - 1; i >= 0; i--) - dump_cgraph_node(dump_file, order[i]); + order[i]->dump (out); fprintf (out, "\n"); - fflush(out); + fflush (out); } - + struct searchc_env { struct cgraph_node **stack; + struct cgraph_node **result; int stack_size; - struct cgraph_node **result; int order_pos; splay_tree nodes_marked_new; bool reduce; + bool allow_overwritable; int count; }; @@ -76,7 +73,7 @@ has been customized for cgraph_nodes. The env parameter is because it is recursive and there are no nested functions here. This function should only be called from itself or - ipa_utils_reduced_inorder. ENV is a stack env and would be + ipa_reduced_postorder. ENV is a stack env and would be unnecessary if C had nested functions. V is the node to start searching from. */ @@ -100,12 +97,15 @@ for (edge = v->callees; edge; edge = edge->next_callee) { struct ipa_dfs_info * w_info; - struct cgraph_node *w = edge->callee; + enum availability avail; + struct cgraph_node *w = edge->callee->ultimate_alias_target (&avail); - if (ignore_edge && ignore_edge (edge)) + if (!w || (ignore_edge && ignore_edge (edge))) continue; - if (w->aux && cgraph_function_body_availability (edge->callee) > AVAIL_OVERWRITABLE) + if (w->aux + && (avail > AVAIL_INTERPOSABLE + || (env->allow_overwritable && avail == AVAIL_INTERPOSABLE))) { w_info = (struct ipa_dfs_info *) w->aux; if (w_info->new_node) @@ -134,6 +134,7 @@ x = env->stack[--(env->stack_size)]; x_info = (struct ipa_dfs_info *) x->aux; x_info->on_stack = false; + x_info->scc_no = v_info->dfn_number; if (env->reduce) { @@ -151,32 +152,38 @@ /* Topsort the call graph by caller relation. Put the result in ORDER. - The REDUCE flag is true if you want the cycles reduced to single - nodes. Only consider nodes that have the output bit set. */ + The REDUCE flag is true if you want the cycles reduced to single nodes. + You can use ipa_get_nodes_in_cycle to obtain a vector containing all real + call graph nodes in a reduced node. + + Set ALLOW_OVERWRITABLE if nodes with such availability should be included. + IGNORE_EDGE, if non-NULL is a hook that may make some edges insignificant + for the topological sort. */ int -ipa_utils_reduced_inorder (struct cgraph_node **order, - bool reduce, bool allow_overwritable, - bool (*ignore_edge) (struct cgraph_edge *)) +ipa_reduced_postorder (struct cgraph_node **order, + bool reduce, bool allow_overwritable, + bool (*ignore_edge) (struct cgraph_edge *)) { struct cgraph_node *node; struct searchc_env env; splay_tree_node result; - env.stack = XCNEWVEC (struct cgraph_node *, cgraph_n_nodes); + env.stack = XCNEWVEC (struct cgraph_node *, symtab->cgraph_count); env.stack_size = 0; env.result = order; env.order_pos = 0; env.nodes_marked_new = splay_tree_new (splay_tree_compare_ints, 0, 0); env.count = 1; env.reduce = reduce; + env.allow_overwritable = allow_overwritable; - for (node = cgraph_nodes; node; node = node->next) + FOR_EACH_DEFINED_FUNCTION (node) { - enum availability avail = cgraph_function_body_availability (node); + enum availability avail = node->get_availability (); - if (avail > AVAIL_OVERWRITABLE + if (avail > AVAIL_INTERPOSABLE || (allow_overwritable - && (avail == AVAIL_OVERWRITABLE))) + && (avail == AVAIL_INTERPOSABLE))) { /* Reuse the info if it is already there. */ struct ipa_dfs_info *info = (struct ipa_dfs_info *) node->aux; @@ -207,9 +214,150 @@ return env.order_pos; } +/* Deallocate all ipa_dfs_info structures pointed to by the aux pointer of call + graph nodes. */ + +void +ipa_free_postorder_info (void) +{ + struct cgraph_node *node; + FOR_EACH_DEFINED_FUNCTION (node) + { + /* Get rid of the aux information. */ + if (node->aux) + { + free (node->aux); + node->aux = NULL; + } + } +} + +/* Get the set of nodes for the cycle in the reduced call graph starting + from NODE. */ + +vec<cgraph_node *> +ipa_get_nodes_in_cycle (struct cgraph_node *node) +{ + vec<cgraph_node *> v = vNULL; + struct ipa_dfs_info *node_dfs_info; + while (node) + { + v.safe_push (node); + node_dfs_info = (struct ipa_dfs_info *) node->aux; + node = node_dfs_info->next_cycle; + } + return v; +} + +/* Return true iff the CS is an edge within a strongly connected component as + computed by ipa_reduced_postorder. */ + +bool +ipa_edge_within_scc (struct cgraph_edge *cs) +{ + struct ipa_dfs_info *caller_dfs = (struct ipa_dfs_info *) cs->caller->aux; + struct ipa_dfs_info *callee_dfs; + struct cgraph_node *callee = cs->callee->function_symbol (); + + callee_dfs = (struct ipa_dfs_info *) callee->aux; + return (caller_dfs + && callee_dfs + && caller_dfs->scc_no == callee_dfs->scc_no); +} + +struct postorder_stack +{ + struct cgraph_node *node; + struct cgraph_edge *edge; + int ref; +}; + +/* Fill array order with all nodes with output flag set in the reverse + topological order. Return the number of elements in the array. + FIXME: While walking, consider aliases, too. */ + +int +ipa_reverse_postorder (struct cgraph_node **order) +{ + struct cgraph_node *node, *node2; + int stack_size = 0; + int order_pos = 0; + struct cgraph_edge *edge; + int pass; + struct ipa_ref *ref = NULL; + + struct postorder_stack *stack = + XCNEWVEC (struct postorder_stack, symtab->cgraph_count); + + /* We have to deal with cycles nicely, so use a depth first traversal + output algorithm. Ignore the fact that some functions won't need + to be output and put them into order as well, so we get dependencies + right through inline functions. */ + FOR_EACH_FUNCTION (node) + node->aux = NULL; + for (pass = 0; pass < 2; pass++) + FOR_EACH_FUNCTION (node) + if (!node->aux + && (pass + || (!node->address_taken + && !node->global.inlined_to + && !node->alias && !node->thunk.thunk_p + && !node->only_called_directly_p ()))) + { + stack_size = 0; + stack[stack_size].node = node; + stack[stack_size].edge = node->callers; + stack[stack_size].ref = 0; + node->aux = (void *)(size_t)1; + while (stack_size >= 0) + { + while (true) + { + node2 = NULL; + while (stack[stack_size].edge && !node2) + { + edge = stack[stack_size].edge; + node2 = edge->caller; + stack[stack_size].edge = edge->next_caller; + /* Break possible cycles involving always-inline + functions by ignoring edges from always-inline + functions to non-always-inline functions. */ + if (DECL_DISREGARD_INLINE_LIMITS (edge->caller->decl) + && !DECL_DISREGARD_INLINE_LIMITS + (edge->callee->function_symbol ()->decl)) + node2 = NULL; + } + for (; stack[stack_size].node->iterate_referring ( + stack[stack_size].ref, + ref) && !node2; + stack[stack_size].ref++) + { + if (ref->use == IPA_REF_ALIAS) + node2 = dyn_cast <cgraph_node *> (ref->referring); + } + if (!node2) + break; + if (!node2->aux) + { + stack[++stack_size].node = node2; + stack[stack_size].edge = node2->callers; + stack[stack_size].ref = 0; + node2->aux = (void *)(size_t)1; + } + } + order[order_pos++] = stack[stack_size--].node; + } + } + free (stack); + FOR_EACH_FUNCTION (node) + node->aux = NULL; + return order_pos; +} + + /* Given a memory reference T, will return the variable at the bottom - of the access. Unlike get_base_address, this will recurse thru + of the access. Unlike get_base_address, this will recurse through INDIRECT_REFS. */ tree @@ -227,3 +375,307 @@ return t; } + +/* SRC and DST are going to be merged. Take SRC's profile and merge it into + DST so it is not going to be lost. Possibly destroy SRC's body on the way + unless PRESERVE_BODY is set. */ + +void +ipa_merge_profiles (struct cgraph_node *dst, + struct cgraph_node *src, + bool preserve_body) +{ + tree oldsrcdecl = src->decl; + struct function *srccfun, *dstcfun; + bool match = true; + + if (!src->definition + || !dst->definition) + return; + if (src->frequency < dst->frequency) + src->frequency = dst->frequency; + + /* Time profiles are merged. */ + if (dst->tp_first_run > src->tp_first_run && src->tp_first_run) + dst->tp_first_run = src->tp_first_run; + + if (src->profile_id && !dst->profile_id) + dst->profile_id = src->profile_id; + + /* FIXME when we merge in unknown profile, we ought to set counts as + unsafe. */ + if (!src->count.initialized_p ()) + return; + if (symtab->dump_file) + { + fprintf (symtab->dump_file, "Merging profiles of %s to %s\n", + src->dump_name (), dst->dump_name ()); + } + if (dst->count.initialized_p ()) + dst->count += src->count; + else + dst->count = src->count; + + /* This is ugly. We need to get both function bodies into memory. + If declaration is merged, we need to duplicate it to be able + to load body that is being replaced. This makes symbol table + temporarily inconsistent. */ + if (src->decl == dst->decl) + { + struct lto_in_decl_state temp; + struct lto_in_decl_state *state; + + /* We are going to move the decl, we want to remove its file decl data. + and link these with the new decl. */ + temp.fn_decl = src->decl; + lto_in_decl_state **slot + = src->lto_file_data->function_decl_states->find_slot (&temp, + NO_INSERT); + state = *slot; + src->lto_file_data->function_decl_states->clear_slot (slot); + gcc_assert (state); + + /* Duplicate the decl and be sure it does not link into body of DST. */ + src->decl = copy_node (src->decl); + DECL_STRUCT_FUNCTION (src->decl) = NULL; + DECL_ARGUMENTS (src->decl) = NULL; + DECL_INITIAL (src->decl) = NULL; + DECL_RESULT (src->decl) = NULL; + + /* Associate the decl state with new declaration, so LTO streamer + can look it up. */ + state->fn_decl = src->decl; + slot + = src->lto_file_data->function_decl_states->find_slot (state, INSERT); + gcc_assert (!*slot); + *slot = state; + } + src->get_untransformed_body (); + dst->get_untransformed_body (); + srccfun = DECL_STRUCT_FUNCTION (src->decl); + dstcfun = DECL_STRUCT_FUNCTION (dst->decl); + if (n_basic_blocks_for_fn (srccfun) + != n_basic_blocks_for_fn (dstcfun)) + { + if (symtab->dump_file) + fprintf (symtab->dump_file, + "Giving up; number of basic block mismatch.\n"); + match = false; + } + else if (last_basic_block_for_fn (srccfun) + != last_basic_block_for_fn (dstcfun)) + { + if (symtab->dump_file) + fprintf (symtab->dump_file, + "Giving up; last block mismatch.\n"); + match = false; + } + else + { + basic_block srcbb, dstbb; + + FOR_ALL_BB_FN (srcbb, srccfun) + { + unsigned int i; + + dstbb = BASIC_BLOCK_FOR_FN (dstcfun, srcbb->index); + if (dstbb == NULL) + { + if (symtab->dump_file) + fprintf (symtab->dump_file, + "No matching block for bb %i.\n", + srcbb->index); + match = false; + break; + } + if (EDGE_COUNT (srcbb->succs) != EDGE_COUNT (dstbb->succs)) + { + if (symtab->dump_file) + fprintf (symtab->dump_file, + "Edge count mistmatch for bb %i.\n", + srcbb->index); + match = false; + break; + } + for (i = 0; i < EDGE_COUNT (srcbb->succs); i++) + { + edge srce = EDGE_SUCC (srcbb, i); + edge dste = EDGE_SUCC (dstbb, i); + if (srce->dest->index != dste->dest->index) + { + if (symtab->dump_file) + fprintf (symtab->dump_file, + "Succ edge mistmatch for bb %i.\n", + srce->dest->index); + match = false; + break; + } + } + } + } + if (match) + { + struct cgraph_edge *e, *e2; + basic_block srcbb, dstbb; + + /* TODO: merge also statement histograms. */ + FOR_ALL_BB_FN (srcbb, srccfun) + { + unsigned int i; + + dstbb = BASIC_BLOCK_FOR_FN (dstcfun, srcbb->index); + if (!dstbb->count.initialized_p ()) + { + dstbb->count = srcbb->count; + for (i = 0; i < EDGE_COUNT (srcbb->succs); i++) + { + edge srce = EDGE_SUCC (srcbb, i); + edge dste = EDGE_SUCC (dstbb, i); + if (srce->probability.initialized_p ()) + dste->probability = srce->probability; + } + } + else if (srcbb->count.initialized_p ()) + { + for (i = 0; i < EDGE_COUNT (srcbb->succs); i++) + { + edge srce = EDGE_SUCC (srcbb, i); + edge dste = EDGE_SUCC (dstbb, i); + dste->probability = + dste->probability * dstbb->count.probability_in (dstbb->count + srcbb->count) + + srce->probability * srcbb->count.probability_in (dstbb->count + srcbb->count); + } + dstbb->count += srcbb->count; + } + } + push_cfun (dstcfun); + counts_to_freqs (); + compute_function_frequency (); + pop_cfun (); + for (e = dst->callees; e; e = e->next_callee) + { + if (e->speculative) + continue; + e->count = gimple_bb (e->call_stmt)->count; + e->frequency = compute_call_stmt_bb_frequency + (dst->decl, + gimple_bb (e->call_stmt)); + } + for (e = dst->indirect_calls, e2 = src->indirect_calls; e; + e2 = (e2 ? e2->next_callee : NULL), e = e->next_callee) + { + profile_count count = gimple_bb (e->call_stmt)->count; + int freq = compute_call_stmt_bb_frequency + (dst->decl, + gimple_bb (e->call_stmt)); + /* When call is speculative, we need to re-distribute probabilities + the same way as they was. This is not really correct because + in the other copy the speculation may differ; but probably it + is not really worth the effort. */ + if (e->speculative) + { + cgraph_edge *direct, *indirect; + cgraph_edge *direct2 = NULL, *indirect2 = NULL; + ipa_ref *ref; + + e->speculative_call_info (direct, indirect, ref); + gcc_assert (e == indirect); + if (e2 && e2->speculative) + e2->speculative_call_info (direct2, indirect2, ref); + if (indirect->count > profile_count::zero () + || direct->count > profile_count::zero ()) + { + /* We should mismatch earlier if there is no matching + indirect edge. */ + if (!e2) + { + if (dump_file) + fprintf (dump_file, + "Mismatch in merging indirect edges\n"); + } + else if (!e2->speculative) + indirect->count += e2->count; + else if (e2->speculative) + { + if (DECL_ASSEMBLER_NAME (direct2->callee->decl) + != DECL_ASSEMBLER_NAME (direct->callee->decl)) + { + if (direct2->count >= direct->count) + { + direct->redirect_callee (direct2->callee); + indirect->count += indirect2->count + + direct->count; + direct->count = direct2->count; + } + else + indirect->count += indirect2->count + direct2->count; + } + else + { + direct->count += direct2->count; + indirect->count += indirect2->count; + } + } + int prob = direct->count.probability_in (direct->count + + indirect->count). + to_reg_br_prob_base (); + direct->frequency = RDIV (freq * prob, REG_BR_PROB_BASE); + indirect->frequency = RDIV (freq * (REG_BR_PROB_BASE - prob), + REG_BR_PROB_BASE); + } + else + /* At the moment we should have only profile feedback based + speculations when merging. */ + gcc_unreachable (); + } + else if (e2 && e2->speculative) + { + cgraph_edge *direct, *indirect; + ipa_ref *ref; + + e2->speculative_call_info (direct, indirect, ref); + e->count = count; + e->frequency = freq; + int prob = direct->count.probability_in (e->count) + .to_reg_br_prob_base (); + e->make_speculative (direct->callee, direct->count, + RDIV (freq * prob, REG_BR_PROB_BASE)); + } + else + { + e->count = count; + e->frequency = freq; + } + } + if (!preserve_body) + src->release_body (); + ipa_update_overall_fn_summary (dst); + } + /* TODO: if there is no match, we can scale up. */ + src->decl = oldsrcdecl; +} + +/* Return true if call to DEST is known to be self-recusive call withing FUNC. */ + +bool +recursive_call_p (tree func, tree dest) +{ + struct cgraph_node *dest_node = cgraph_node::get_create (dest); + struct cgraph_node *cnode = cgraph_node::get_create (func); + ipa_ref *alias; + enum availability avail; + + gcc_assert (!cnode->alias); + if (cnode != dest_node->ultimate_alias_target (&avail)) + return false; + if (avail >= AVAIL_AVAILABLE) + return true; + if (!dest_node->semantically_equivalent_p (cnode)) + return false; + /* If there is only one way to call the fuction or we know all of them + are semantically equivalent, we still can consider call recursive. */ + FOR_EACH_ALIAS (cnode, alias) + if (!dest_node->semantically_equivalent_p (alias->referring)) + return false; + return true; +}