/* Callgraph handling code. Copyright (C) 2003 Free Software Foundation, Inc. Contributed by Jan Hubicka This file is part of GCC. GCC is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. GCC is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GCC; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "config.h" #include "system.h" /* APPLE LOCAL begin callgraph inlining */ /* #include "coretypes.h" */ /* #include "tm.h" */ /* APPLE LOCAL end callgraph inlining */ #include "tree.h" #include "tree-inline.h" #include "langhooks.h" #include "hashtab.h" #include "toplev.h" #include "flags.h" #include "ggc.h" #include "debug.h" #include "target.h" /* APPLE LOCAL begin callgraph inlining */ #include "varray.h" #include "cgraph.h" /* The known declarations must not get garbage collected. Callgraph datastructures should not get saved via PCH code since this would make it difficult to extend into intra-module optimizer later. So we store only the references into the array to prevent gabrage collector from deleting live data. */ static GTY(()) varray_type known_fns; /* APPLE LOCAL end callgraph inlining */ /* Hash table used to convert declarations into nodes. */ static GTY((param_is (struct cgraph_node))) htab_t cgraph_hash; /* APPLE LOCAL callgraph inlining */ /* The linked list of cgraph nodes. */ struct cgraph_node *cgraph_nodes; /* APPLE LOCAL callgraph inlining */ /* Number of nodes in existence. */ int cgraph_n_nodes; /* Set when whole unit has been analyzed so we can access global info. */ bool cgraph_global_info_ready = false; /* APPLE LOCAL begin callgraph inlining */ static void cgraph_remove_edge PARAMS ((struct cgraph_node *, struct cgraph_node *)); static hashval_t hash_node PARAMS ((const PTR)); static int eq_node PARAMS ((const PTR, const PTR)); static struct cgraph_edge *clone_callee_list PARAMS ((struct cgraph_edge *, struct cgraph_edge *)); static char *callee_string PARAMS ((tree)); static void dump_inlining_choices PARAMS ((FILE *, struct cgraph_edge *)); /* APPLE LOCAL end callgraph inlining */ /* Returns a hash code for P. */ static hashval_t hash_node (p) const PTR p; { return (hashval_t) htab_hash_pointer (DECL_ASSEMBLER_NAME (((struct cgraph_node *) p)->decl)); } /* Returns non-zero if P1 and P2 are equal. */ static int eq_node (p1, p2) const PTR p1; const PTR p2; { return ((DECL_ASSEMBLER_NAME (((struct cgraph_node *) p1)->decl)) == DECL_ASSEMBLER_NAME ((tree) p2)); } /* APPLE LOCAL callgraph inlining */ /* Return cgraph node assigned to DECL. Create new one when needed. */ struct cgraph_node * cgraph_node (decl) tree decl; { struct cgraph_node *node; struct cgraph_node **slot; struct cgraph_node *step; static bool cgraph_hash_init = FALSE; /* APPLE LOCAL begin callgraph inlining */ if (TREE_CODE (decl) != FUNCTION_DECL) abort (); if (!cgraph_hash_init) { cgraph_hash = htab_create_ggc (10, hash_node, eq_node, NULL); VARRAY_TREE_INIT (known_fns, 32, "known_fns"); /* Rebuild our hashtable after waking from a PCH-addled sleep. */ for (step = cgraph_nodes; step ; step = step->next) { slot = (struct cgraph_node **) htab_find_slot_with_hash (cgraph_hash, step->decl, htab_hash_pointer (DECL_ASSEMBLER_NAME (step->decl)), 1); *slot = step; } cgraph_hash_init = TRUE; } slot = (struct cgraph_node **) htab_find_slot_with_hash (cgraph_hash, decl, htab_hash_pointer (DECL_ASSEMBLER_NAME (decl)), 1); if (*slot) return *slot; node = (struct cgraph_node *) ggc_alloc_cleared (sizeof (*node)); node->decl = decl; node->next = cgraph_nodes; if (cgraph_nodes) cgraph_nodes->previous = node; node->previous = NULL; cgraph_nodes = node; cgraph_n_nodes++; *slot = node; /* APPLE LOCAL callgraph inlining */ if (DECL_CONTEXT (decl) && TREE_CODE (DECL_CONTEXT (decl)) == FUNCTION_DECL) { node->origin = cgraph_node (DECL_CONTEXT (decl)); node->next_nested = node->origin->nested; node->origin->nested = node; } VARRAY_PUSH_TREE (known_fns, decl); return node; } /* Create edge from CALLER to CALLEE in the cgraph. */ /* APPLE LOCAL begin callgraph inlining */ struct cgraph_edge * create_edge (caller, callee, call_expr) struct cgraph_node *caller, *callee; tree *call_expr; { struct cgraph_edge *edge = (struct cgraph_edge *) ggc_alloc_cleared (sizeof (struct cgraph_edge)); edge->caller = caller; edge->callee = callee; if (callee) { edge->next_caller = callee->callers; callee->callers = edge; } if (caller) { edge->next_callee = caller->callees; caller->callees = edge; } edge->inliner.call_expr = call_expr; /* i = times_call_executed (*call_expr); */ /* edge->inline.execution_count = (i > 0) ? i : 0 ; */ return edge; } /* APPLE LOCAL end callgraph inlining */ /* Remove the edge from CALLER to CALLEE in the cgraph. */ /* APPLE LOCAL begin callgraph inlining */ static void cgraph_remove_edge (caller, callee) struct cgraph_node *caller, *callee; { struct cgraph_edge **edge, **edge2; for (edge = &callee->callers; *edge && (*edge)->caller != caller; edge = &((*edge)->next_caller)) continue; if (!*edge) abort (); *edge = (*edge)->next_caller; for (edge2 = &caller->callees; *edge2 && (*edge2)->callee != callee; edge2 = &(*edge2)->next_callee) continue; if (!*edge2) abort (); *edge2 = (*edge2)->next_callee; } /* APPLE LOCAL end callgraph inlining */ /* APPLE LOCAL begin callgraph inlining */ /* Remove the node from cgraph. */ void cgraph_remove_node (node) struct cgraph_node *node; { while (node->callers) cgraph_remove_edge (node->callers->caller, node); while (node->callees) cgraph_remove_edge (node, node->callees->callee); while (node->nested) cgraph_remove_node (node->nested); if (node->origin) { struct cgraph_node **node2 = &node->origin->nested; while (*node2 != node) node2 = &(*node2)->next_nested; *node2 = node->next_nested; } if (node->previous) node->previous->next = node->next; else cgraph_nodes = node; if (node->next) node->next->previous = node->previous; DECL_SAVED_TREE (node->decl) = NULL; /* Do not free the structure itself so the walk over chain can continue. */ } /* APPLE LOCAL end callgraph inlining */ /* APPLE LOCAL begin callgraph inlining */ /* Record call from CALLER to CALLEE */ struct cgraph_edge * cgraph_record_call (caller, callee, call_expr, invocations) tree caller, callee, *call_expr; HOST_WIDE_INT invocations; { struct cgraph_node *node = cgraph_node (caller); struct cgraph_edge *edge = create_edge (cgraph_node (caller), cgraph_node (callee), call_expr); varray_type callee_array = (varray_type)NULL; edge->inliner.invocations = invocations; edge->inliner.desirability = cgraph_call_desirability (edge); node->inliner.callee_count++; if (node->inliner.top_edge) callee_array = node->inliner.top_edge->inliner.callee_array; /* Record the address of the pointer to the call. */ if (callee_array) VARRAY_PUSH_GENERIC_PTR (callee_array, call_expr); return edge; } /* APPLE LOCAL end callgraph inlining */ void cgraph_remove_call (caller, callee) tree caller, callee; { /* APPLE LOCAL callgraph inlining */ cgraph_remove_edge (cgraph_node (caller), cgraph_node (callee)); } /* Return true when CALLER_DECL calls CALLEE_DECL. */ bool cgraph_calls_p (caller_decl, callee_decl) tree caller_decl, callee_decl; { struct cgraph_node *caller = cgraph_node (caller_decl); struct cgraph_node *callee = cgraph_node (callee_decl); struct cgraph_edge *edge; for (edge = callee->callers; edge && (edge)->caller != caller; edge = (edge->next_caller)) continue; return edge != NULL; } /* APPLE LOCAL begin callgraph inlining */ /* Return local info for the compiled function. */ struct cgraph_local_info * cgraph_local_info (decl) tree decl; { struct cgraph_node *node; if (TREE_CODE (decl) != FUNCTION_DECL) abort (); node = cgraph_node (decl); return &node->local; } /* APPLE LOCAL end callgraph inlining */ /* APPLE LOCAL begin callgraph inlining */ /* Return local info for the compiled function. */ struct cgraph_global_info * cgraph_global_info (decl) tree decl; { struct cgraph_node *node; if (TREE_CODE (decl) != FUNCTION_DECL || !cgraph_global_info_ready) abort (); node = cgraph_node (decl); return &node->global; } /* APPLE LOCAL end callgraph inlining */ /* APPLE LOCAL begin callgraph inlining */ /* Return local info for the compiled function. */ struct cgraph_rtl_info * cgraph_rtl_info (decl) tree decl; { struct cgraph_node *node; if (TREE_CODE (decl) != FUNCTION_DECL) abort (); node = cgraph_node (decl); if (decl != current_function_decl && !TREE_ASM_WRITTEN (node->decl)) return NULL; return &node->rtl; } /* APPLE LOCAL end callgraph inlining */ /* APPLE LOCAL begin callgraph inlining */ /* Duplicate the given list of callee edges. */ static inline struct cgraph_edge * clone_callee_list (callee, caller) struct cgraph_edge *callee, *caller; { struct cgraph_edge *callee_list = callee->inliner.callees; struct cgraph_edge *new_list=NULL, *new, *prev=(struct cgraph_edge *)NULL, *step; double scale; if (caller->callee->inliner.execution_count == 0) scale = 0.0; else scale = (double)caller->inliner.execution_count / (double)caller->callee->inliner.execution_count; /* It's necessary for scale * caller->callee->inliner.execution_count to be == caller->inliner.execution.count. If this is not the case tweak scale until it is. */ { HOST_WIDEST_INT tweak = 1; unsigned int maxtweak = 10; while (((HOST_WIDEST_INT)(scale * caller->callee->inliner.execution_count) < caller->inliner.execution_count) && --maxtweak) scale = (double)caller->inliner.execution_count / ((double)caller->callee->inliner.execution_count - tweak++); tweak = 1; maxtweak = 10; while (((HOST_WIDEST_INT)(scale * caller->callee->inliner.execution_count) > caller->inliner.execution_count) && --maxtweak) scale = (double)caller->inliner.execution_count / ((double)caller->callee->inliner.execution_count + tweak++); } for (step = callee_list ; step ; step = step->next_callee) { new = create_edge (step->caller, step->callee, (tree *)NULL); *new = *step; new->inliner.inline_this = FALSE; new->inliner.callees = (struct cgraph_edge *)NULL; new->inliner.callee_array = (varray_type)NULL; new->inliner.uplink = caller; if (flag_use_feedback) { /* Scale the execution counts of the CALL_EXPRs created by inlining this CALL_EXPR. The scale factor was computed above and is the same for each call. It is also the same as the scale we'll use for nodes other than calls. */ new->inliner.execution_count = (HOST_WIDE_INT)(step->inliner.execution_count * scale); /* Can't set_times_call_executed() here becuase this CALL_EXPR hasn't been created yet. :-) */ new->inliner.desirability = cgraph_call_desirability (new); } /* FIXME: incomplete callgraph. */ new->next_caller = (struct cgraph_edge *)NULL; if (prev) prev->next_callee = new; if (!new_list) new_list = new; prev = new; } return new_list; } /* APPLE LOCAL end callgraph inlining */ /* APPLE LOCAL begin callgraph inlining */ /* Record decision to inline this call. */ void cgraph_record_inlining_choice (edge) struct cgraph_edge *edge; { /* We've chosen to inline the call represented by this edge. When this call has been replaced by the body of the callee, here is the list of calls from the callee body that will now be part of the callers body. */ /* FIXME: Choose one? */ edge->inliner.callees = clone_callee_list (edge->callee->inliner.top_edge, edge); edge->inliner.callee_array = NULL; edge->inliner.inline_this = TRUE; } /* APPLE LOCAL end callgraph inlining */ /* APPLE LOCAL begin callgraph inlining */ /* Given a CALL_EXPR tree, return a the (char *) name of the callee. Debugging only. */ static inline char * callee_string (call_node) tree call_node; { tree addr_node, decl_node; if (TREE_CODE (call_node) == CALL_EXPR) { addr_node = TREE_OPERAND (call_node, 0); if (TREE_CODE (addr_node) == ADDR_EXPR) { decl_node = TREE_OPERAND (addr_node, 0); if (TREE_CODE (decl_node) == FUNCTION_DECL) return (char *) IDENTIFIER_POINTER (DECL_NAME (decl_node)); } } abort(); } /* APPLE LOCAL end callgraph inlining */ /* APPLE LOCAL begin callgraph inlining */ /* Dump a list of inlining choices. */ static inline void dump_inlining_choices (f, inlines) FILE *f; struct cgraph_edge *inlines; { struct cgraph_edge *step; char *name; for (step = inlines; step; step = step->next_callee) { name = callee_string (*step->inliner.call_expr); fprintf (f, " %s", name ? name : ""); fprintf (f, " [%llu %e]", step->inliner.execution_count, step->inliner.desirability); if (step->inliner.callees) { fprintf (f, "("); dump_inlining_choices (f, step->inliner.callees); fprintf (f, ")"); } } } /* APPLE LOCAL end callgraph inlining */ /* APPLE LOCAL begin callgraph inlining */ /* Dump the callgraph. */ void dump_cgraph (f) FILE *f; { struct cgraph_node *node; fprintf (f, "\nCallgraph:\n\n"); for (node = cgraph_nodes; node; node = node->next) { struct cgraph_edge *edge; fprintf (f, "%s", IDENTIFIER_POINTER (DECL_NAME (node->decl))); if (node->origin) fprintf (f, " nested in: %s", IDENTIFIER_POINTER (DECL_NAME (node->origin->decl))); if (node->needed) fprintf (f, " needed"); else if (node->reachable) fprintf (f, " reachable"); if (DECL_SAVED_TREE (node->decl)) fprintf (f, " tree"); fprintf (f, "\n called by :"); for (edge = node->callers; edge; edge = edge->next_caller) fprintf (f, "%s ", IDENTIFIER_POINTER (DECL_NAME (edge->caller->decl))); fprintf (f, "\n calls: "); for (edge = node->callees; edge; edge = edge->next_callee) fprintf (f, "%s ", IDENTIFIER_POINTER (DECL_NAME (edge->callee->decl))); /* APPLE LOCAL callgraph inlining */ /* big deletion; code has moved to callgraphunit.c */ if (node->inliner.top_edge && node->inliner.top_edge->inliner.callees) { fprintf (f, "\n inlines: "); dump_inlining_choices (f, node->inliner.top_edge->inliner.callees); } else fprintf (f, "\n (no inlines)"); fprintf (f, "\n decl: %p saved_tree: %p exec_count: %llu\n", (void *)node->decl, (void *)DECL_SAVED_TREE (node->decl), node->inliner.execution_count); fprintf (f, "\n"); } } #include "gt-cgraph.h" /* APPLE LOCAL end callgraph inlining */