diff options
Diffstat (limited to 'gcc-4.6/function_reordering_plugin/callgraph.c')
-rw-r--r-- | gcc-4.6/function_reordering_plugin/callgraph.c | 597 |
1 files changed, 597 insertions, 0 deletions
diff --git a/gcc-4.6/function_reordering_plugin/callgraph.c b/gcc-4.6/function_reordering_plugin/callgraph.c new file mode 100644 index 0000000..ee5b867 --- /dev/null +++ b/gcc-4.6/function_reordering_plugin/callgraph.c @@ -0,0 +1,597 @@ +/* Callgraph implementation. + Copyright (C) 2011 Free Software Foundation, Inc. + Contributed by Sriraman Tallam (tmsriram@google.com). + +This program 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 3, or (at your option) +any later version. + +This program 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 this program; see the file COPYING3. If not see +<http://www.gnu.org/licenses/>. */ + +#include "callgraph.h" +#include <stdio.h> +#include <stdlib.h> +#include <assert.h> +#include <string.h> +#include <hashtab.h> + +/*****************************************************************************/ +/* section_map hashtable definition and helpers. */ + +/* Maps section name to its corresponding object handle and section index. */ +static htab_t section_map = NULL; + +/* Hashtable helper for section_map htab. */ +static hashval_t +section_map_htab_hash_descriptor (const void *p) +{ + const Section_id *s = (const Section_id *)p; + const char *name = s->name; + return htab_hash_string(name); +} + +/* Hashtable helper for section_map htab. */ +static int +section_map_htab_eq_descriptor (const void *p1, const void *p2) +{ + const Section_id *s1 = (const Section_id *)p1; + const char *c1 = s1->name; + const char *c2 = (const char *)p2; + + return (strcmp (c1, c2) == 0); +} +/*****************************************************************************/ + + +/*****************************************************************************/ +/* function_map hashtable definition and helpers. + Maps function name to a unique Node. */ +static htab_t function_map = NULL; +static unsigned int last_function_id = 0; + +/* Hashtable helper for function_map htab. */ +static hashval_t +function_map_htab_hash_descriptor (const void *p) +{ + const Node *s = (const Node *)p; + const char *name = s->name; + return htab_hash_string(name); +} + +/* Hashtable helper for section_map htab. */ +static int +function_map_htab_eq_descriptor (const void *p1, const void *p2) +{ + const Node *s1 = (const Node *)p1; + const char *c1 = s1->name; + const char *c2 = (const char *)p2; + + return (strcmp (c1, c2) == 0); +} +/*****************************************************************************/ + +/*****************************************************************************/ +/* edge_map hashtable definition and helpers. + Maps two node ids to a unique edge. */ +static htab_t edge_map = NULL; + +static inline hashval_t +edge_hash_function (unsigned int id1, unsigned int id2) +{ + return (id1 << 16) | id2; +} + +/* Hashtable helper for edge_map htab. */ +static hashval_t +edge_map_htab_hash_descriptor (const void *p) +{ + Edge *e = (Edge *) p; + return edge_hash_function (e->first_function->id, e->second_function->id); +} + +/* Hashtable helper for edge_map htab. */ +static int +edge_map_htab_eq_descriptor (const void *p1, const void *p2) +{ + Edge *e1 = (Edge *) p1; + Raw_edge *r1 = (Raw_edge *) p2; + return ((e1->first_function->id == r1->n1->id) + && (e1->second_function->id == r1->n2->id)); +} + + +/*****************************************************************************/ + +/* Chain of all the created nodes. */ +Node *node_chain = NULL; +/* Number of nodes that correspond to functions which will be reordered. */ +unsigned int num_real_nodes = 0; +/* Chain of all edges in the merged callgraph. */ +Edge *active_edges = NULL; +/* Chain of all the merged edges. */ +Edge *inactive_edges = NULL; + +/* Initial value of number of functions to allocate hash tables. */ +const int NUM_FUNCTIONS = 100; + +/* Reads off the next string from the char stream CONTENTS and updates + READ_LENGTH to the length of the string read. The value of CONTENTS + is updated to start at the next string. */ + +static char * +get_next_string (char **contents, unsigned int *read_length) +{ + char *s = *contents; + *read_length = strlen (*contents) + 1; + *contents += *read_length; + return s; +} + +/* Add an EDGE to the list of edges in the call graph. */ + +static void +add_edge_to_list (Edge *edge) +{ + assert (edge != NULL); + edge->next = active_edges; + if (active_edges != NULL) + active_edges->prev = edge; + active_edges = edge; +} + +/* Remove the edge from the list of edges in the call graph. This is done + when the nodes corresponding to this edge are merged. */ + +static void +remove_edge_from_list (Edge * curr_edge) +{ + assert (curr_edge != NULL); + if (curr_edge->prev != NULL) + curr_edge->prev->next = curr_edge->next; + if (curr_edge->next != NULL) + curr_edge->next->prev = curr_edge->prev; + if (active_edges == curr_edge) + active_edges = curr_edge->next; + curr_edge->next = NULL; + curr_edge->prev = NULL; + + /* Add to inactive edges to be freed later. */ + curr_edge->next = inactive_edges; + inactive_edges = curr_edge; + return; +} + +/* Adds the WEIGHT value to the edge count of CALLER and CALLEE. */ + +static void +update_edge (Node *n1, Node *n2, unsigned int weight) +{ + void **slot; + Raw_edge re, *r; + Edge *e; + + if (n1->id == n2->id) + return; + if (weight == 0) + return; + + if (edge_map == NULL) + { + edge_map = htab_create ((NUM_FUNCTIONS * 2), + edge_map_htab_hash_descriptor, + edge_map_htab_eq_descriptor , NULL); + assert (edge_map != NULL); + } + + r = &re; + init_raw_edge (r, n1, n2); + slot = htab_find_slot_with_hash (edge_map, r, + edge_hash_function (r->n1->id, r->n2->id), + INSERT); + if (*slot == NULL) + { + e = make_edge (r->n1, r->n2, weight); + *slot = e; + add_edge_to_list (e); + } + else + { + e = *slot; + e->weight += weight; + } +} + +/* Create a unique node for a function. */ + +static Node * +get_function_node (char *name) +{ + void **slot = NULL; + Node *node; + + if (function_map == NULL) + { + function_map = htab_create (NUM_FUNCTIONS, + function_map_htab_hash_descriptor, + function_map_htab_eq_descriptor , NULL); + assert (function_map != NULL); + } + + slot = htab_find_slot_with_hash (function_map, name, htab_hash_string (name), + INSERT); + + if (*slot == NULL) + { + node = make_node (last_function_id, name); + /* Chain the node to the node_chain. */ + node->next = node_chain; + node_chain = node; + *slot = node; + last_function_id++; + } + else + { + node = (Node *)*slot; + } + return node; +} + +/* Dumper funcction to print the list of functions that will be considered for + re-ordering. */ + +void +dump_functions () +{ + Node *node = node_chain; + while (node) + { + if (node->is_real_node) + fprintf (stderr, "Dumping function %s\n", node->name); + node = node->next; + } +} + +/* Dump all the edges existing in the callgraph. */ + +void dump_edges (FILE *fp) +{ + Edge *it; + for (it = active_edges; + it != NULL; + it = it->next) + { + fprintf (fp,"# %s ---- (%u)---- %s\n", + it->first_function->name, + it->weight, + it->second_function->name); + } +} + +/* HEADER_LEN is the length of string 'Function '. */ +const int HEADER_LEN = 9; + +/* Parse the section contents of ".gnu.callgraph.text" sections and create + call graph edges with appropriate weights. The section contents have the + following format : + Function <caller_name> + <callee_1> + <edge count between caller and callee_1> + <callee_2> + <edge count between caller and callee_2> + .... */ +void +parse_callgraph_section_contents (unsigned char *section_contents, + unsigned int length) +{ + char *contents; + char *caller; + unsigned int read_length = 0, curr_length = 0; + Node *caller_node; + + /* First string in contents is 'Function <function-name>'. */ + assert (length > 0); + contents = (char*) (section_contents); + caller = get_next_string (&contents, &read_length); + assert (read_length > HEADER_LEN); + caller = caller + HEADER_LEN; + curr_length = read_length; + caller_node = get_function_node (caller); + num_real_nodes++; + + while (curr_length < length) + { + /* Read callee, weight tuples. */ + char *callee; + char *weight_str; + unsigned int weight; + Node *callee_node; + + callee = get_next_string (&contents, &read_length); + curr_length += read_length; + callee_node = get_function_node (callee); + + assert (curr_length < length); + weight_str = get_next_string (&contents, &read_length); + weight = atoi (weight_str); + curr_length += read_length; + update_edge (caller_node, callee_node, weight); + } +} + +/* Traverse the list of edges and find the edge with the maximum weight. */ + +static Edge * +find_max_edge () +{ + Edge *it, *max_edge; + + if (active_edges == NULL) + return NULL; + + max_edge = active_edges; + assert (!active_edges->is_merged); + + it = active_edges->next; + for (;it != NULL; it = it->next) + { + assert (!it->is_merged); + if (edge_lower (max_edge , it)) + max_edge = it; + } + + return max_edge; +} + +/* Change the EDGE from OLD_NODE to KEPT_NODE to be between NEW_NODE + and KEPT_NODE. */ + +static void +merge_edge (Edge *edge, Node *new_node, Node *old_node, + Node *kept_node) +{ + void **slot; + Raw_edge re, *r; + + r = &re; + init_raw_edge (r, new_node, kept_node); + slot = htab_find_slot_with_hash (edge_map, r, + edge_hash_function (r->n1->id, r->n2->id), + INSERT); + + if (*slot == NULL) + { + reset_functions (edge, new_node, kept_node); + *slot = edge; + add_edge_to_node (new_node, edge); + } + else + { + Edge *new_edge = *slot; + new_edge->weight += edge->weight; + edge->is_merged = 1; + remove_edge_from_list (edge); + } +} + +/* Merge the two nodes in this EDGE. The new node's edges are the union of + the edges of the original nodes. */ + +static void +collapse_edge (Edge * edge) +{ + Edge_list *it; + Node *kept_node = edge->first_function; + Node *merged_node = edge->second_function; + + /* Go through all merged_node edges and merge with kept_node. */ + for (it = merged_node->edge_list; it != NULL; it = it->next) + { + Node *other_node = NULL; + Edge *this_edge = it->edge; + if (this_edge->is_merged) + continue; + if (this_edge == edge) + continue; + assert (this_edge->first_function->id == merged_node->id + || this_edge->second_function->id == merged_node->id); + other_node = (this_edge->first_function->id + == merged_node->id) + ? this_edge->second_function + : this_edge->first_function; + merge_edge (this_edge, kept_node, merged_node, other_node); + } + + merge_node (kept_node, merged_node); + edge->is_merged = 1; + remove_edge_from_list (edge); +} + +/* Make node N a real node if it can be reordered, that is, its .text + section is available. */ +static void set_node_type (Node *n) +{ + void *slot; + char *name = n->name; + slot = htab_find_with_hash (section_map, name, htab_hash_string (name)); + if (slot != NULL) + set_as_real_node (n); +} + +void +find_pettis_hansen_function_layout (FILE *fp) +{ + Node *n_it; + Edge *it; + + assert (node_chain != NULL); + assert (active_edges != NULL); + assert (fp != NULL); + + dump_edges (fp); + + /* Go over all the nodes and set it as real node only if a corresponding + function section exists. */ + for (n_it = node_chain; n_it != NULL; n_it = n_it->next) + set_node_type (n_it); + + /* Set edge types. A WEAK_EDGE has one of its nodes corresponding to a + function that cannot be re-ordered. */ + for (it = active_edges; it != NULL; it = it->next) + set_edge_type (it); + + it = find_max_edge (); + while (it != NULL) + { + collapse_edge (it); + it = find_max_edge (); + } +} + +/* Maps the function name corresponding to section SECTION_NAME to the + object handle and the section index. */ + +void +map_section_name_to_index (char *section_name, void *handle, int shndx) +{ + void **slot; + const char *sections[] = {".text.hot.", + ".text.unlikely.", + ".text.cold.", + ".text.startup.", + ".text." }; + char *function_name = NULL; + int i; + + for (i = 0; i < ARRAY_SIZE (sections); ++i) + { + if (is_prefix_of (sections[i], section_name)) + { + function_name = section_name + strlen (sections[i]); + break; + } + } + assert (function_name != NULL); + + /* Allocate section_map. */ + if (section_map == NULL) + { + section_map = htab_create (NUM_FUNCTIONS, + section_map_htab_hash_descriptor, + section_map_htab_eq_descriptor , NULL); + assert (section_map != NULL); + } + + slot = htab_find_slot_with_hash (section_map, function_name, + htab_hash_string (function_name), + INSERT); + if (*slot == NULL) + *slot = make_section_id (function_name, section_name, handle, shndx); +} + +static void +write_out_node (FILE *fp, char *name, + void **handles, unsigned int *shndx, int position) +{ + void *slot; + slot = htab_find_with_hash (section_map, name, htab_hash_string (name)); + if (slot != NULL) + { + Section_id *s = (Section_id *)slot; + handles[position] = s->handle; + shndx[position] = s->shndx; + fprintf (fp, "%s\n", s->full_name); + /* No more use of full_name */ + free (s->full_name); + } +} + +/* Visit each node and print the chain of merged nodes to the file. */ + +unsigned int +get_layout (FILE *fp, void*** handles, + unsigned int** shndx) +{ + Node *it; + int position = 0; + + assert (fp != NULL); + + *handles = XNEWVEC (void *, num_real_nodes); + *shndx = XNEWVEC (unsigned int, num_real_nodes); + + /* Dump edges to the final reordering file. */ + + for (it = node_chain; it != NULL; it = it->next) + { + Node *node; + if (it->is_merged) + continue; + if (it->is_real_node) + { + write_out_node (fp, it->name, *handles, *shndx, position); + position++; + } + node = it->merge_next; + while (node != NULL) + { + if (node->is_real_node) + { + write_out_node (fp, node->name, *handles, *shndx, position); + position++; + } + node = node->merge_next; + } + } + return position; +} + +/* Free all heap objects. */ + +void +cleanup () +{ + Node *node; + + /* Free all nodes and edge_lists. */ + for (node = node_chain; node != NULL; ) + { + Node *next_node = node->next; + Edge_list *it; + for (it = node->edge_list; it != NULL; ) + { + Edge_list *next_it = it->next; + free (it); + it = next_it; + } + free (node); + node = next_node; + } + + /* Free all active_edges. */ + free_edge_chain (active_edges); + + /* Free all inactive edges. */ + free_edge_chain (inactive_edges); + + /* Delete all htabs. */ + htab_delete (section_map); + htab_delete (function_map); + htab_delete (edge_map); +} + +/* Check if the callgraph is empty. */ +unsigned int +is_callgraph_empty () +{ + if (active_edges == NULL) + return 1; + return 0; +} |