aboutsummaryrefslogtreecommitdiffstats
path: root/gcc-4.9/function_reordering_plugin/callgraph.c
diff options
context:
space:
mode:
Diffstat (limited to 'gcc-4.9/function_reordering_plugin/callgraph.c')
-rw-r--r--gcc-4.9/function_reordering_plugin/callgraph.c1188
1 files changed, 1188 insertions, 0 deletions
diff --git a/gcc-4.9/function_reordering_plugin/callgraph.c b/gcc-4.9/function_reordering_plugin/callgraph.c
new file mode 100644
index 000000000..8f7399e75
--- /dev/null
+++ b/gcc-4.9/function_reordering_plugin/callgraph.c
@@ -0,0 +1,1188 @@
+/* Callgraph implementation.
+ Copyright (C) 2011 Free Software Foundation, Inc.
+ Contributed by Sriraman Tallam (tmsriram@google.com)
+ and Easwaran Raman (eraman@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));
+}
+
+
+/*****************************************************************************/
+
+
+/* Keep track of all allocated memory. */
+typedef struct
+{
+ void *ptr;
+ void *next;
+} mm_node;
+
+mm_node *mm_node_chain = NULL;
+
+void
+push_allocated_ptr (void *ptr)
+{
+ mm_node *node = XNEW (mm_node);
+ node->ptr = ptr;
+ node->next = mm_node_chain;
+ mm_node_chain = node;
+}
+
+/* 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. UPDATE_CONTENTS tells if
+ CONTENTS must be moved past the read string to the next string. To
+ peek at the string, UPDATE_CONTENTS can be set to false. */
+
+static char *
+get_next_string (char **contents, unsigned int *read_length,
+ int update_contents)
+{
+ char *s = *contents;
+ *read_length = strlen (*contents) + 1;
+ if (update_contents)
+ *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 long long 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;
+ }
+ /* Update the computed node weight for n2, which is the sum of its incoming
+ edge weights. */
+ n2->computed_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 (%llu, %llu) ---- (%llu)---- %s (%llu, %llu)\n",
+ it->first_function->name,
+ it->first_function->weight,
+ it->first_function->computed_weight,
+ it->weight,
+ it->second_function->name,
+ it->second_function->weight,
+ it->second_function->computed_weight);
+ }
+}
+
+/* For file local functions, append a unique identifier corresponding to
+ the file, FILE_HANDLE, to the NAME to keep the name unique. */
+
+static char *
+canonicalize_function_name (void *file_handle, char *name)
+{
+ /* Number of hexadecimal digits in file_handle, plus length of "0x". */
+ const int FILE_HANDLE_LEN = sizeof (void *) * 2 + 2;
+ char *canonical_name;
+
+ /* File local functions have _ZL prefix in the mangled name. */
+ /* XXX: Handle file local functions exhaustively, like functions in
+ anonymous name spaces. */
+ if (!is_prefix_of ("_ZL", name))
+ return name;
+
+ XNEWVEC_ALLOC (canonical_name, char, (strlen(name) + FILE_HANDLE_LEN + 2));
+ sprintf (canonical_name, "%s.%p", name, file_handle);
+ return canonical_name;
+}
+
+/* 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>
+ Weight <entry_count> <max_count> (optional line)
+ ColdWeight <max_count> (optional line)
+ <callee_1>
+ <edge count between caller and callee_1>
+ <callee_2>
+ <edge count between caller and callee_2>
+ .... */
+void
+parse_callgraph_section_contents (void *file_handle,
+ unsigned char *section_contents,
+ unsigned int length)
+{
+ char *contents;
+ char *caller;
+ char *node_weight_s = NULL;
+ unsigned int read_length = 0, curr_length = 0;
+ Node *caller_node;
+
+ /* HEADER_LEN is the length of string 'Function '. */
+ const int HEADER_LEN = 9;
+
+ /* Prefix of line containing node weights. */
+ const char *NODE_WEIGHT_PREFIX = "Weight ";
+ /* Prefix of line containing max bb count of cold split part. */
+ const char *SPLIT_FUNCTION_PREFIX = "ColdWeight ";
+
+ /* First string in contents is 'Function <function-name>'. */
+ assert (length > 0);
+ contents = (char*) (section_contents);
+ caller = get_next_string (&contents, &read_length, 1);
+ assert (read_length > HEADER_LEN);
+ caller = canonicalize_function_name (file_handle, caller + HEADER_LEN);
+ curr_length = read_length;
+ caller_node = get_function_node (caller);
+
+ /* Check if next string is a node weight, which has the format
+ "Weight <entry_count> <max_count>". We could have callgraph
+ sections with or without node weights. */
+
+ /* Peek at the next string. */
+ if (curr_length < length)
+ node_weight_s = get_next_string (&contents, &read_length, 0);
+ if (node_weight_s != NULL
+ && is_prefix_of (NODE_WEIGHT_PREFIX, node_weight_s))
+ {
+ char *max_count_s;
+ unsigned long long max_count;
+ unsigned long long node_weight
+ = atoll (node_weight_s + strlen (NODE_WEIGHT_PREFIX));
+ /* Functions like comdats only have one caller_node and can
+ have multiple node weights from multiple modules. */
+ caller_node->weight += node_weight;
+
+ /* Find the space and get the max_count. */
+ max_count_s = strstr (node_weight_s + strlen (NODE_WEIGHT_PREFIX), " ");
+ if (max_count_s != NULL)
+ {
+ max_count = atoll (max_count_s + 1);
+ /* Functions like comdats only have one caller_node and can
+ have multiple node weights from multiple modules. */
+ caller_node->max_count += max_count;
+ }
+ /* Actually read the node weight here. */
+ get_next_string (&contents, &read_length, 1);
+ curr_length += read_length;
+ }
+
+ /* If the function is split it could have the weight of the split cold
+ section here as "SplitWeight <max_count>". */
+
+ /* Peek at the next string. */
+ if (curr_length < length)
+ node_weight_s = get_next_string (&contents, &read_length, 0);
+ if (node_weight_s != NULL
+ && is_prefix_of (SPLIT_FUNCTION_PREFIX, node_weight_s))
+ {
+ unsigned long long split_weight
+ = atoll (node_weight_s + strlen (SPLIT_FUNCTION_PREFIX));
+ caller_node->split_weight = split_weight;
+ /* Actually read the node weight here. */
+ get_next_string (&contents, &read_length, 1);
+ curr_length += read_length;
+ }
+
+ while (curr_length < length)
+ {
+ /* Read callee, weight tuples. */
+ char *callee;
+ char *weight_str;
+ unsigned long long weight;
+ Node *callee_node;
+
+ callee = get_next_string (&contents, &read_length, 1);
+ curr_length += read_length;
+
+ /* We can have multiple header lines; such a situation arises when
+ we've linked objects into a shared library, and we use that
+ library as input to the linker for something else. Deal
+ gracefully with such cases. */
+ if (strncmp (callee, "Function ", HEADER_LEN) == 0)
+ continue;
+
+ callee = canonicalize_function_name (file_handle, callee);
+ callee_node = get_function_node (callee);
+
+ assert (curr_length < length);
+ weight_str = get_next_string (&contents, &read_length, 1);
+ weight = atoll (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)
+ {
+ /* Update the section instance corresponding to the node instance.
+ Assign the weights from the node instance to the section instance. */
+ Section_id *s = (Section_id *)(slot);
+ Section_id *s_comdat;
+ assert (s->weight == 0 && s->computed_weight == 0 && s->max_count == 0);
+ s->weight = n->weight;
+ s->computed_weight = n->computed_weight;
+ s->max_count = n->max_count;
+
+ /* If s is split into a cold section, assign the split weight to the
+ max count of the split section. Use this also for the weight of the
+ split section. */
+ if (s->split_section)
+ {
+ s->split_section->max_count = s->split_section->weight = n->split_weight;
+ /* If split_section is comdat, update all the comdat
+ candidates for weight. */
+ s_comdat = s->split_section->comdat_group;
+ while (s_comdat != NULL)
+ {
+ /* Set the different weights for comdat candidates. No need to se
+ computed_weight as it is zero for split sections. A split cold
+ section is never called, it is only jumped into from the parent
+ section. */
+ s_comdat->weight = s->split_section->weight;
+ s_comdat->max_count = s->split_section->max_count;
+ s_comdat = s_comdat->comdat_group;
+ }
+ }
+
+ /* If s is comdat, update all the comdat candidates for weight. */
+ s_comdat = s->comdat_group;
+ while (s_comdat != NULL)
+ {
+ s_comdat->weight = s->weight;
+ s_comdat->computed_weight = s->computed_weight;
+ s_comdat->max_count = s->max_count;
+ s_comdat = s_comdat->comdat_group;
+ }
+ set_as_real_node (n);
+ num_real_nodes++;
+ }
+}
+
+/* Return true if WEIGHT is more than the cutoff, specified either as
+ as percent, CUTOFF_P, of MAX or as an absolute value, CUTOFF_A. */
+int
+edge_over_cutoff (unsigned long long weight, unsigned long long max,
+ unsigned int cutoff_p, unsigned long long cutoff_a)
+{
+ /* First check if weight if more than cutoff_p% of max. */
+ if (((double)(max) * (cutoff_p/100.0)) >= (double) weight)
+ return 0;
+ if (cutoff_a >= weight)
+ return 0;
+ return 1;
+}
+
+/* Edge cutoff is used to discard callgraph edges that are not above a
+ certain threshold. cutoff_p is to express this as a percent of the
+ maximum value and cutoff_a is used to express this as an absolute
+ value. */
+extern unsigned int edge_cutoff_p;
+extern unsigned long long edge_cutoff_a;
+
+void
+find_pettis_hansen_function_layout (FILE *fp)
+{
+ Node *n_it;
+ Edge *it;
+ unsigned int max_edge_value = 0;
+
+ assert (node_chain != NULL);
+ assert (active_edges != NULL);
+ if (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 ();
+ if (it != NULL)
+ max_edge_value = it->weight;
+ while (it != NULL)
+ {
+ if (!edge_over_cutoff (it->weight, max_edge_value, edge_cutoff_p,
+ edge_cutoff_a))
+ {
+ if (fp !=NULL)
+ fprintf (fp, "Not considering edge with weight %llu and below\n",
+ it->weight);
+ break;
+ }
+ collapse_edge (it);
+ it = find_max_edge ();
+ }
+}
+
+/* The list of sections created, excluding comdat duplicates. */
+Section_id *first_section = NULL;
+/* The number of sections. */
+int num_sections = 0;
+
+const int NUM_SECTION_TYPES = 4;
+const char *section_types[] = {".text.hot.",
+ ".text.unlikely.",
+ ".text.startup.",
+ ".text." };
+
+/* For sections that are not in the callgraph, the priority gives the
+ importance of each section type. Sections are grouped according to
+ priority, higher priority (lower number). */
+const int section_priority[] = {0, 3, 1, 2};
+
+/* Order in which the sections must be laid out is given by
+ section_position[section_type]. The order in which the section
+ types are laid out from address low to high are: .text.unlikely,
+ .text.startup, .text., .text.hot followed by the sections grouped
+ by the callgraph. */
+const int section_position[] = {3, 0, 1, 2};
+
+/* The position of the sections grouped using the callgraph. It comes after
+ all the sections not present in the callgraph are laid out. */
+#define CALLGRAPH_POSITION NUM_SECTION_TYPES
+
+/* 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;
+ char *function_name = NULL;
+ int i, section_type = -1;
+
+ for (i = 0; i < ARRAY_SIZE (section_types); ++i)
+ {
+ if (is_prefix_of (section_types[i], section_name))
+ {
+ function_name = section_name + strlen (section_types[i]);
+ section_type = i;
+ break;
+ }
+ }
+
+ assert (function_name != NULL && section_type >= 0);
+ function_name = canonicalize_function_name (handle, function_name);
+ num_sections++;
+
+ /* 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)
+ {
+ Section_id *section = make_section_id (function_name, section_name,
+ section_type, handle, shndx);
+ /* Chain it to the list of sections. */
+ section->next = first_section;
+ first_section = section;
+ *slot = section;
+ }
+ else
+ {
+ /* Handle function splitting here. With function splitting, the split
+ function sections have the same name and they are in the same module.
+ Here, we only care about the section that is marked with prefix
+ like ".text.hot". The other section is cold. The plugin should not
+ be adding this cold section to the section_map. In get_layout it will
+ later be picked up when processing the non-callgraph sections and it
+ will laid out appropriately. */
+ Section_id *kept = (Section_id *)(*slot);
+ Section_id *section = make_section_id (function_name, section_name,
+ section_type, handle, shndx);
+ int is_split_function = 0;
+ Section_id *split_comdat = NULL;
+ /* Check if this is a split function. The modules are the same means this
+ is not comdat and we assume it is split. It can be split and comdat
+ too, in which case we have to search the comdat list of kept. */
+ if (kept->handle == handle)
+ is_split_function = 1;
+ else if (kept->comdat_group != NULL)
+ {
+ split_comdat = kept;
+ do
+ {
+ if (split_comdat->comdat_group->handle == handle)
+ break;
+ split_comdat = split_comdat->comdat_group;
+ }
+ while (split_comdat->comdat_group != NULL);
+ }
+
+ /* It is split and it is comdat. */
+ if (split_comdat != NULL
+ && split_comdat->comdat_group != NULL)
+ {
+ /* The comdat_section that is split. */
+ Section_id *comdat_section = split_comdat->comdat_group;
+ Section_id *cold_section = NULL;
+ /* If the existing section is cold, the newly detected split must
+ be hot. */
+ if (is_prefix_of (".text.unlikely", comdat_section->full_name))
+ {
+ assert (!is_prefix_of (".text.unlikely", section_name));
+ cold_section = comdat_section;
+ /* Replace the comdat_section in the kept section list with the
+ new section. */
+ split_comdat->comdat_group = section;
+ section->comdat_group = comdat_section->comdat_group;
+ comdat_section->comdat_group = NULL;
+ }
+ else
+ {
+ assert (is_prefix_of (".text.unlikely", section_name));
+ cold_section = section;
+ }
+ assert (cold_section != NULL && cold_section->comdat_group == NULL);
+ cold_section->is_split_cold_section = 1;
+ /* The cold section must be added to the unlikely chain of comdat
+ groups. */
+ if (kept->split_section == NULL)
+ {
+ /* This happens if no comdat function in this group so far has
+ been split. */
+ kept->split_section = cold_section;
+ }
+ else
+ {
+ /* Add the cold_section to the unlikely chain of comdats. */
+ cold_section->comdat_group = kept->split_section->comdat_group;
+ kept->split_section->comdat_group = cold_section;
+ }
+ }
+ /* It is split and it is not comdat. */
+ else if (is_split_function)
+ {
+ Section_id *cold_section = NULL;
+ /* Function splitting means that the "hot" part is really the
+ relevant section and the other section is unlikely executed and
+ should not be part of the callgraph. */
+
+ /* Store the new section in the section list. */
+ section->next = first_section;
+ first_section = section;
+ /* If the existing section is cold, the newly detected split must
+ be hot. */
+ if (is_prefix_of (".text.unlikely", kept->full_name))
+ {
+ assert (!is_prefix_of (".text.unlikely", section_name));
+ /* The kept section was the unlikely section. Change the section
+ in section_map to be the new section which is the hot one. */
+ *slot = section;
+ /* Record the split cold section in the hot section. */
+ section->split_section = kept;
+ /* Comdats and function splitting are already handled. */
+ assert (kept->comdat_group == NULL);
+ cold_section = kept;
+ }
+ else
+ {
+ /* Record the split cold section in the hot section. */
+ assert (is_prefix_of (".text.unlikely", section_name));
+ kept->split_section = section;
+ cold_section = section;
+ }
+ assert (cold_section != NULL && cold_section->comdat_group == NULL);
+ cold_section->is_split_cold_section = 1;
+ }
+ else
+ {
+ /* The function already exists, it must be a COMDAT. Only one section
+ in the comdat group will be kept, we don't know which. Chain all
+ the comdat sections in the same comdat group to be emitted
+ together later. Keep one section as representative (kept) and
+ update its section_type to be equal to the type of the highest
+ priority section in the group. */
+
+ /* Two comdats in the same group can have different priorities. This
+ ensures that the "kept" comdat section has the priority of the
+ highest section in that comdat group. This is necessary because
+ the plugin does not know which section will be kept. */
+ if (section_priority[kept->section_type]
+ > section_priority[section_type])
+ kept->section_type = section_type;
+
+ section->comdat_group = kept->comdat_group;
+ kept->comdat_group = section;
+ }
+ }
+}
+
+/* Add section S to the chain SECTION_START ... SECTION_END.
+ If it is a comdat, get all the comdat sections in the group.
+ Chain these sections to SECTION_END. Set SECTION_START if it
+ is NULL. */
+
+static void
+write_out_node (Section_id *s, Section_id **section_start,
+ Section_id **section_end)
+{
+ assert (s != NULL && s->processed == 0);
+ s->processed = 1;
+ if (*section_start == NULL)
+ {
+ *section_start = s;
+ *section_end = s;
+ }
+ else
+ {
+ (*section_end)->group = s;
+ *section_end = s;
+ }
+
+ /* Print all other sections in the same comdat group. */
+ while (s->comdat_group)
+ {
+ s = s->comdat_group;
+ s->processed = 1;
+ (*section_end)->group = s;
+ *section_end = s;
+ }
+}
+
+/* Find the max of a, b and c. */
+static unsigned long long
+get_max (unsigned long long a, unsigned long long b, unsigned long long c)
+{
+ unsigned long long max = a;
+ if (b > max)
+ max = b;
+ if (c > max)
+ max = c;
+ return max;
+}
+
+/* This is true if the max count of any bb in a function should be used as
+ the node weight rather than the count of the entry bb. */
+extern int use_max_count;
+
+/* Comparison function for sorting two sections a and b by their
+ weight. */
+static
+int section_weight_compare (const void *a, const void *b)
+{
+ Section_id *s_a = *(Section_id **)a;
+ Section_id *s_b = *(Section_id **)b;
+ assert (use_max_count <= 1);
+ unsigned long long max_sa_weight = get_max (s_a->weight, s_a->computed_weight,
+ s_a->max_count * use_max_count);
+ unsigned long long max_sb_weight = get_max (s_b->weight, s_b->computed_weight,
+ s_b->max_count * use_max_count);
+
+ if (max_sa_weight < max_sb_weight)
+ return -1;
+ else if (max_sa_weight == max_sb_weight)
+ return 0;
+
+ return 1;
+}
+
+/* s is a pointer to a section and the group of sections is linked
+ via s->group. The output is the list of sections sorted by their
+ node weights (which is the maximum of their profile count, computed
+ weights or the max bb count if use_max_count is true). */
+static Section_id *
+sort_section_group (Section_id *s)
+{
+ Section_id **sort_array;
+ Section_id *s_tmp;
+ int num_elements = 0;
+ int i;
+
+ if (s == NULL)
+ return s;
+
+ s_tmp = s;
+ while (s_tmp != NULL)
+ {
+ num_elements++;
+ s_tmp = s_tmp->group;
+ }
+
+ if (num_elements == 1)
+ return s;
+
+ XNEWVEC_ALLOC (sort_array, Section_id *, num_elements);
+ s_tmp = s;
+ for (i = 0; i < num_elements; ++i)
+ {
+ sort_array[i] = s_tmp;
+ s_tmp = s_tmp->group;
+ }
+
+ for (i = 0; i < num_elements; ++i)
+ {
+ sort_array[i]->group = NULL;
+ }
+
+ qsort (sort_array, num_elements, sizeof (Section_id *),
+ section_weight_compare);
+
+ s_tmp = sort_array[0];
+ for (i = 1; i < num_elements; ++i)
+ {
+ s_tmp->group = sort_array[i];
+ s_tmp = s_tmp->group;
+ }
+ s_tmp->group = NULL;
+ return sort_array[0];
+}
+
+/* If sort_name_prefix is true then the sections not touched by the callgraph
+ are grouped according to their name prefix. When sort_name_prefix is zero,
+ all the sections are put together and sorted according to their node
+ weights. The default value of sort_name_prefix is 0. Even when sections
+ are grouped by their prefix, each group is sorted by the node weights. */
+extern int sort_name_prefix;
+static int section_position_index (int section_type)
+{
+ assert (section_type >= 0 && section_type < NUM_SECTION_TYPES);
+ if (!sort_name_prefix)
+ return 0;
+ else
+ return section_position[section_type];
+}
+
+/* Track where the unlikely sections start and end. This will be needed if
+ the unlikely sections need to be split into a separate segment. */
+int unlikely_segment_start = -1;
+int unlikely_segment_end = -1;
+
+/* This value is used to determine the profile threshold below which the
+ section is considered unlikely. The default is zero. */
+extern unsigned long long unlikely_segment_profile_cutoff;
+
+/* Visit each node and print the chain of merged nodes to the file. Update
+ HANDLES and SHNDX to contain the ordered list of sections. */
+
+unsigned int
+get_layout (FILE *fp, void*** handles,
+ unsigned int** shndx)
+{
+ Node *n_it;
+ int i = 0;
+ int position;
+ void *slot;
+ int unlikely_section_index;
+
+ /* Form NUM_SECTION_TYPES + 1 groups of sections. Index 5 corresponds
+ to the list of sections that correspond to functions in the callgraph.
+ For other sections, they are grouped by section_type and stored in
+ index: section_position[section_type]).
+ SECTION_START points to the first section in each section group and
+ SECTION_END points to the last. */
+ Section_id *section_start[NUM_SECTION_TYPES + 1];
+ Section_id *section_end[NUM_SECTION_TYPES + 1];
+ Section_id *s_it;
+
+ XNEWVEC_ALLOC (*handles, void *, num_sections);
+ XNEWVEC_ALLOC (*shndx, unsigned int, num_sections);
+
+ for (i = 0; i < NUM_SECTION_TYPES + 1; i++)
+ {
+ section_start[i] = NULL;
+ section_end[i] = NULL;
+ }
+
+ /* Dump edges to the final reordering file. */
+ for (n_it = node_chain; n_it != NULL; n_it = n_it->next)
+ {
+ Section_id *s;
+ Node *node;
+ /* First, only consider nodes that are real and that have other
+ nodes merged with it. */
+ if (n_it->is_merged || !n_it->is_real_node || !n_it->merge_next)
+ continue;
+
+ slot = htab_find_with_hash (section_map, n_it->name,
+ htab_hash_string (n_it->name));
+ assert (slot != NULL);
+ s = (Section_id *)slot;
+ write_out_node (s, &section_start[CALLGRAPH_POSITION],
+ &section_end[CALLGRAPH_POSITION]);
+
+ if (fp)
+ fprintf (fp, "# Callgraph group : %s", n_it->name);
+
+ node = n_it->merge_next;
+ while (node != NULL)
+ {
+ if (node->is_real_node)
+ {
+ slot = htab_find_with_hash (section_map, node->name,
+ htab_hash_string (node->name));
+ assert (slot != NULL);
+ s = (Section_id *)slot;
+ write_out_node (s, &section_start[CALLGRAPH_POSITION],
+ &section_end[CALLGRAPH_POSITION]);
+ if (fp)
+ fprintf (fp, " %s", node->name);
+ }
+ node = node->merge_next;
+ }
+
+ if (fp)
+ fprintf (fp, "\n");
+ }
+
+
+ /* Now handle all the sections that were not processed above during
+ callgraph handling. Go through all the sections and sort unprocessed
+ sections into different section_type groups. */
+ s_it = first_section;
+ while (s_it)
+ {
+ if (!s_it->processed)
+ {
+ int index = section_position_index(s_it->section_type);
+ write_out_node (s_it, &section_start[index], &section_end[index]);
+ }
+ s_it = s_it->next;
+ }
+
+ /* Determine the unlikely section index */
+ unlikely_section_index = -1;
+ for (i = 0; i < ARRAY_SIZE (section_types); ++i)
+ if (strcmp (".text.unlikely.", section_types[i]) == 0)
+ break;
+
+ assert (i < ARRAY_SIZE (section_types));
+ unlikely_section_index = section_position_index(i);
+
+ position = 0;
+ for (i = 0; i < NUM_SECTION_TYPES + 1; ++i)
+ {
+ s_it = section_start[i];
+
+ if (s_it == NULL)
+ continue;
+
+ /* Sort all section groups by weight except the callgraph group. */
+ if (i != CALLGRAPH_POSITION)
+ s_it = sort_section_group (s_it);
+
+ /* Start the unlikely segment if necessary. */
+ assert (use_max_count <= 1);
+ if (i == unlikely_section_index
+ && (get_max (s_it->weight, s_it->computed_weight,
+ s_it->max_count * use_max_count)
+ <= unlikely_segment_profile_cutoff))
+ {
+ assert (unlikely_segment_start == -1);
+ unlikely_segment_start = position;
+ if (fp != NULL)
+ fprintf (fp, "=== Unlikely sections start ===\n");
+ }
+
+ do
+ {
+ assert (position < num_sections);
+ (*handles)[position] = s_it->handle;
+ (*shndx)[position] = s_it->shndx;
+
+ /* Check if this section will end the unlikely segment. */
+ if (i == unlikely_section_index
+ && unlikely_segment_start >= 0
+ && unlikely_segment_start != position
+ && unlikely_segment_end == -1
+ && (get_max (s_it->weight, s_it->computed_weight,
+ s_it->max_count * use_max_count)
+ > unlikely_segment_profile_cutoff))
+ {
+ unlikely_segment_end = position - 1;
+ if (fp != NULL)
+ fprintf (fp, "=== Unlikely sections end ===\n");
+ }
+
+ position++;
+ if (fp != NULL)
+ {
+ fprintf (fp, "%s entry count = %llu computed = %llu "
+ "max count = %llu split = %d\n",
+ s_it->full_name, s_it->weight,
+ s_it->computed_weight, s_it->max_count,
+ s_it->is_split_cold_section);
+ }
+ s_it = s_it->group;
+ }
+ while (s_it);
+
+ /* End the unlikely segment if it has not been done already. */
+ if (i == unlikely_section_index
+ && unlikely_segment_start != -1
+ && unlikely_segment_end == -1)
+ {
+ unlikely_segment_end = position - 1;
+ if (fp != NULL)
+ fprintf (fp, "=== Unlikely sections end ===\n");
+ }
+ }
+ return position;
+}
+
+void
+cleanup ()
+{
+ /* Go through heap allocated objects and free them. */
+ while (mm_node_chain)
+ {
+ mm_node *node = mm_node_chain;
+ free (node->ptr);
+ mm_node_chain = node->next;
+ free (node);
+ }
+
+ /* 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;
+}