aboutsummaryrefslogtreecommitdiffstats
path: root/gcc-4.4.0/gcc/dyn-ipa.c
diff options
context:
space:
mode:
authorJing Yu <jingyu@google.com>2009-11-05 15:11:04 -0800
committerJing Yu <jingyu@google.com>2009-11-05 15:11:04 -0800
commitdf62c1c110e8532b995b23540b7e3695729c0779 (patch)
treedbbd4cbdb50ac38011e058a2533ee4c3168b0205 /gcc-4.4.0/gcc/dyn-ipa.c
parent8d401cf711539af5a2f78d12447341d774892618 (diff)
downloadtoolchain_gcc-df62c1c110e8532b995b23540b7e3695729c0779.tar.gz
toolchain_gcc-df62c1c110e8532b995b23540b7e3695729c0779.tar.bz2
toolchain_gcc-df62c1c110e8532b995b23540b7e3695729c0779.zip
Check in gcc sources for prebuilt toolchains in Eclair.
Diffstat (limited to 'gcc-4.4.0/gcc/dyn-ipa.c')
-rw-r--r--gcc-4.4.0/gcc/dyn-ipa.c1272
1 files changed, 1272 insertions, 0 deletions
diff --git a/gcc-4.4.0/gcc/dyn-ipa.c b/gcc-4.4.0/gcc/dyn-ipa.c
new file mode 100644
index 000000000..99b7fd25a
--- /dev/null
+++ b/gcc-4.4.0/gcc/dyn-ipa.c
@@ -0,0 +1,1272 @@
+/* Compile this one with gcc. */
+/* Copyright (C) 2009. Free Software Foundation, Inc.
+ Contributed by Xinliang David Li (davidxl@google.com) and
+ Raksit Ashok (raksit@google.com)
+
+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 3, 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.
+
+Under Section 7 of GPL version 3, you are granted additional
+permissions described in the GCC Runtime Library Exception, version
+3.1, as published by the Free Software Foundation.
+
+You should have received a copy of the GNU General Public License and
+a copy of the GCC Runtime Library Exception along with this program;
+see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
+<http://www.gnu.org/licenses/>. */
+
+#include "tconfig.h"
+#include "tsystem.h"
+#include "coretypes.h"
+#include "tm.h"
+
+#if defined(inhibit_libc)
+#define IN_LIBGCOV (-1)
+#else
+#undef NULL /* Avoid errors if stdio.h and our stddef.h mismatch. */
+#include <stdio.h>
+#include <stdlib.h>
+#define IN_LIBGCOV 1
+#if defined(L_gcov)
+#define GCOV_LINKAGE /* nothing */
+#endif
+#endif
+#include "gcov-io.h"
+
+struct dyn_pointer_set;
+struct dyn_vect;
+
+#define XNEWVEC(type,ne) (type *)malloc(sizeof(type) * (ne))
+#define XNEW(type) (type *)malloc(sizeof(type))
+#define XDELETEVEC(p) free(p)
+#define XDELETE(p) free(p)
+
+struct dyn_cgraph_node
+{
+ struct dyn_cgraph_edge *callees;
+ struct dyn_cgraph_edge *callers;
+ struct dyn_pointer_set *imported_modules;
+
+ gcov_type guid;
+ gcov_unsigned_t visited;
+};
+
+struct dyn_cgraph_edge
+{
+ struct dyn_cgraph_node *caller;
+ struct dyn_cgraph_node *callee;
+ struct dyn_cgraph_edge *next_caller;
+ struct dyn_cgraph_edge *next_callee;
+ gcov_type count;
+};
+
+struct dyn_module_info
+{
+ struct dyn_pointer_set *imported_modules;
+};
+
+struct dyn_cgraph
+{
+ struct dyn_cgraph_node **call_graph_nodes;
+ struct gcov_info **modules;
+ /* supplement module information */
+ struct dyn_module_info *sup_modules;
+ const struct gcov_fn_info ***functions;
+ unsigned num_modules;
+};
+
+struct dyn_pointer_set
+{
+ size_t log_slots;
+ size_t n_slots; /* n_slots = 2^log_slots */
+ size_t n_elements;
+
+ const void **slots;
+};
+
+
+#if defined(inhibit_libc)
+__gcov_build_callgraph (void) {}
+#else
+
+void __gcov_compute_module_groups (void) ATTRIBUTE_HIDDEN;
+void __gcov_finalize_dyn_callgraph (void) ATTRIBUTE_HIDDEN;
+static void gcov_dump_callgraph (gcov_type);
+static void gcov_dump_cgraph_node_short (struct dyn_cgraph_node *node);
+static void gcov_dump_cgraph_node (struct dyn_cgraph_node *node,
+ unsigned m, unsigned f);
+static void
+gcov_dump_cgraph_node_dot (struct dyn_cgraph_node *node,
+ unsigned m, unsigned f,
+ gcov_type cutoff_count);
+static void
+pointer_set_destroy (struct dyn_pointer_set *pset);
+
+static struct dyn_cgraph the_dyn_call_graph;
+
+static void
+init_dyn_cgraph_node (struct dyn_cgraph_node *node, gcov_type guid)
+{
+ node->callees = 0;
+ node->callers = 0;
+ node->imported_modules = 0;
+ node->guid = guid;
+ node->visited = 0;
+}
+
+/* Return (module_id - 1). FUNC_GUID is the global unique id. */
+
+static inline gcov_unsigned_t
+get_module_idx_from_func_glob_uid (gcov_type func_guid)
+{
+ return EXTRACT_MODULE_ID_FROM_GLOBAL_ID (func_guid) - 1;
+}
+
+/* Return (module_id - 1) for MODULE_INFO. */
+
+static inline gcov_unsigned_t
+get_module_idx (const struct gcov_info *module_info)
+{
+ return module_info->mod_info->ident - 1;
+}
+
+/* Return intra-module function id given function global unique id
+ FUNC_GUID. */
+
+static inline gcov_unsigned_t
+get_intra_module_func_id (gcov_type func_guid)
+{
+ return EXTRACT_FUNC_ID_FROM_GLOBAL_ID (func_guid);
+}
+
+/* Return the pointer to the dynamic call graph node for FUNC_GUID. */
+
+static inline struct dyn_cgraph_node *
+get_cgraph_node (gcov_type func_guid)
+{
+ gcov_unsigned_t mod_id, func_id;
+
+ mod_id = get_module_idx_from_func_glob_uid (func_guid);
+ func_id = get_intra_module_func_id (func_guid);
+
+ return &the_dyn_call_graph.call_graph_nodes[mod_id][func_id];
+}
+
+/* Return the gcov_info pointer for module with id MODULE_ID. */
+
+static inline struct gcov_info *
+get_module_info (gcov_unsigned_t module_id)
+{
+ return the_dyn_call_graph.modules[module_id];
+}
+
+struct gcov_info *__gcov_list ATTRIBUTE_HIDDEN;
+
+/* Initialize dynamic call graph. */
+
+static void
+init_dyn_call_graph (void)
+{
+ unsigned num_modules = 0;
+ struct gcov_info *gi_ptr;
+
+ the_dyn_call_graph.call_graph_nodes = 0;
+ the_dyn_call_graph.modules = 0;
+ the_dyn_call_graph.functions = 0;
+
+ gi_ptr = __gcov_list;
+
+ for (; gi_ptr; gi_ptr = gi_ptr->next)
+ num_modules++;
+
+ the_dyn_call_graph.num_modules = num_modules;
+
+ the_dyn_call_graph.modules
+ = XNEWVEC (struct gcov_info *, num_modules);
+
+ the_dyn_call_graph.sup_modules
+ = XNEWVEC (struct dyn_module_info, num_modules);
+ memset (the_dyn_call_graph.sup_modules, 0,
+ num_modules * sizeof (struct dyn_module_info));
+
+ the_dyn_call_graph.functions
+ = XNEWVEC (const struct gcov_fn_info **, num_modules);
+
+ the_dyn_call_graph.call_graph_nodes
+ = XNEWVEC (struct dyn_cgraph_node *, num_modules);
+
+ gi_ptr = __gcov_list;
+
+ for (; gi_ptr; gi_ptr = gi_ptr->next)
+ {
+ unsigned c_ix = 0, t_ix, j, mod_id, fi_stride, max_func_ident = 0;
+ struct dyn_cgraph_node *node;
+
+ mod_id = get_module_idx (gi_ptr);
+
+ the_dyn_call_graph.modules[mod_id] = gi_ptr;
+
+ the_dyn_call_graph.functions[mod_id]
+ = XNEWVEC (const struct gcov_fn_info *, gi_ptr->n_functions);
+
+ for (t_ix = 0; t_ix < GCOV_COUNTERS; t_ix++)
+ if ((1 << t_ix) & gi_ptr->ctr_mask)
+ c_ix++;
+
+ fi_stride = sizeof (struct gcov_fn_info) + c_ix * sizeof (unsigned);
+ if (__alignof__ (struct gcov_fn_info) > sizeof (unsigned))
+ {
+ fi_stride += __alignof__ (struct gcov_fn_info) - 1;
+ fi_stride &= ~(__alignof__ (struct gcov_fn_info) - 1);
+ }
+
+ for (j = 0; j < gi_ptr->n_functions; j++)
+ {
+ const struct gcov_fn_info *fi_ptr = (const struct gcov_fn_info *)
+ ((const char *) gi_ptr->functions + j * fi_stride);
+ the_dyn_call_graph.functions[mod_id][j] = fi_ptr;
+ if (fi_ptr->ident > max_func_ident)
+ max_func_ident = fi_ptr->ident;
+ }
+
+
+ the_dyn_call_graph.call_graph_nodes[mod_id]
+ = XNEWVEC (struct dyn_cgraph_node, max_func_ident + 1);
+
+ for (j = 0; j < max_func_ident + 1; j++)
+ init_dyn_cgraph_node (&the_dyn_call_graph.call_graph_nodes[mod_id][j], 0);
+
+ for (j = 0; j < gi_ptr->n_functions; j++)
+ {
+ const struct gcov_fn_info *fi_ptr
+ = the_dyn_call_graph.functions[mod_id][j];
+
+ node = &the_dyn_call_graph.call_graph_nodes[mod_id][fi_ptr->ident];
+ init_dyn_cgraph_node (node, GEN_FUNC_GLOBAL_ID (gi_ptr->mod_info->ident,
+ fi_ptr->ident));
+ }
+ }
+}
+
+/* Free up memory allocated for dynamic call graph. */
+
+void
+__gcov_finalize_dyn_callgraph (void)
+{
+ unsigned i;
+ struct gcov_info *gi_ptr;
+
+ for (i = 0; i < the_dyn_call_graph.num_modules; i++)
+ {
+ gi_ptr = the_dyn_call_graph.modules[i];
+ const struct gcov_fn_info *fi_ptr;
+ unsigned f_ix;
+ for (f_ix = 0; f_ix < gi_ptr->n_functions; f_ix++)
+ {
+ struct dyn_cgraph_node *node;
+ struct dyn_cgraph_edge *callees, *next_callee;
+ fi_ptr = the_dyn_call_graph.functions[i][f_ix];
+ node = &the_dyn_call_graph.call_graph_nodes[i][fi_ptr->ident];
+ callees = node->callees;
+
+ if (!callees)
+ continue;
+ while (callees != 0)
+ {
+ next_callee = callees->next_callee;
+ XDELETE (callees);
+ callees = next_callee;
+ }
+ if (node->imported_modules)
+ pointer_set_destroy (node->imported_modules);
+ }
+ if (the_dyn_call_graph.call_graph_nodes[i])
+ XDELETEVEC (the_dyn_call_graph.call_graph_nodes[i]);
+ if (the_dyn_call_graph.functions[i])
+ XDELETEVEC (the_dyn_call_graph.functions[i]);
+ /* Now delete sup modules */
+ if (the_dyn_call_graph.sup_modules[i].imported_modules)
+ pointer_set_destroy (the_dyn_call_graph.sup_modules[i].imported_modules);
+ }
+ XDELETEVEC (the_dyn_call_graph.call_graph_nodes);
+ XDELETEVEC (the_dyn_call_graph.functions);
+ XDELETEVEC (the_dyn_call_graph.sup_modules);
+ XDELETEVEC (the_dyn_call_graph.modules);
+}
+
+/* Add outgoing edge OUT_EDGE for caller node CALLER. */
+
+static void
+gcov_add_out_edge (struct dyn_cgraph_node *caller,
+ struct dyn_cgraph_edge *out_edge)
+{
+ if (!caller->callees)
+ caller->callees = out_edge;
+ else
+ {
+ out_edge->next_callee = caller->callees;
+ caller->callees = out_edge;
+ }
+}
+
+/* Add incoming edge IN_EDGE for callee node CALLEE. */
+
+static void
+gcov_add_in_edge (struct dyn_cgraph_node *callee,
+ struct dyn_cgraph_edge *in_edge)
+{
+ if (!callee->callers)
+ callee->callers = in_edge;
+ else
+ {
+ in_edge->next_caller = callee->callers;
+ callee->callers = in_edge;
+ }
+}
+
+/* Add a call graph edge between caller CALLER and callee CALLEE.
+ The edge count is COUNT. */
+
+static void
+gcov_add_cgraph_edge (struct dyn_cgraph_node *caller,
+ struct dyn_cgraph_node *callee,
+ gcov_type count)
+{
+ struct dyn_cgraph_edge *new_edge = XNEW (struct dyn_cgraph_edge);
+ new_edge->caller = caller;
+ new_edge->callee = callee;
+ new_edge->count = count;
+ new_edge->next_caller = 0;
+ new_edge->next_callee = 0;
+
+ gcov_add_out_edge (caller, new_edge);
+ gcov_add_in_edge (callee, new_edge);
+}
+
+/* Add call graph edges from direct calls for caller CALLER. DIR_CALL_COUNTERS
+ is the array of call counters. N_COUNTS is the number of counters. */
+
+static void
+gcov_build_callgraph_dc_fn (struct dyn_cgraph_node *caller,
+ gcov_type *dir_call_counters,
+ unsigned n_counts)
+{
+ unsigned i;
+
+ for (i = 0; i < n_counts; i += 2)
+ {
+ struct dyn_cgraph_node *callee;
+ gcov_type count;
+ gcov_type callee_guid = dir_call_counters[i];
+ count = dir_call_counters[i + 1];
+ if (count == 0)
+ continue;
+ /* This is to workaround: calls in __static_initialization_and_destruction
+ should not be instrumented as the module id context for the calles have
+ not setup yet. */
+ if (EXTRACT_MODULE_ID_FROM_GLOBAL_ID (callee_guid) == 0)
+ continue;
+ callee = get_cgraph_node (callee_guid);
+ gcov_add_cgraph_edge (caller, callee, count);
+ }
+}
+
+/* Add call graph edges from indirect calls for caller CALLER. ICALL_COUNTERS
+ is the array of icall counters. N_COUNTS is the number of counters. */
+
+static void
+gcov_build_callgraph_ic_fn (struct dyn_cgraph_node *caller,
+ gcov_type *icall_counters,
+ unsigned n_counts)
+{
+ unsigned i, j;
+
+ for (i = 0; i < n_counts; i += GCOV_ICALL_TOPN_NCOUNTS)
+ {
+ gcov_type *value_array = &icall_counters[i + 1];
+ for (j = 0; j < GCOV_ICALL_TOPN_NCOUNTS - 1; j += 2)
+ {
+ struct dyn_cgraph_node *callee;
+ gcov_type count;
+ gcov_type callee_guid = value_array[j];
+ count = value_array[j + 1];
+ if (count == 0)
+ continue;
+ /* This is to workaround: calls in __static_initialization_and_destruction
+ should not be instrumented as the module id context for the calles have
+ not setup yet. */
+ if (EXTRACT_MODULE_ID_FROM_GLOBAL_ID (callee_guid) == 0)
+ continue;
+ callee = get_cgraph_node (callee_guid);
+ gcov_add_cgraph_edge (caller, callee, count);
+ }
+ }
+}
+
+/* Build the dynamic call graph. */
+
+static void
+gcov_build_callgraph (void)
+{
+ struct gcov_info *gi_ptr;
+ unsigned t_ix, m_ix;
+
+ init_dyn_call_graph ();
+
+ for (m_ix = 0; m_ix < the_dyn_call_graph.num_modules; m_ix++)
+ {
+ const struct gcov_fn_info *fi_ptr;
+ unsigned c_ix, f_ix, n_counts, dp_cix = 0, ip_cix = 0;
+ gcov_type *dcall_profile_values, *icall_profile_values;
+
+ gi_ptr = the_dyn_call_graph.modules[m_ix];
+
+ dcall_profile_values = 0;
+ icall_profile_values = 0;
+ c_ix = 0;
+ for (t_ix = 0; t_ix < GCOV_COUNTERS; t_ix++)
+ if ((1 << t_ix) & gi_ptr->ctr_mask)
+ {
+ if (t_ix == GCOV_COUNTER_DIRECT_CALL)
+ {
+ dcall_profile_values = gi_ptr->counts[c_ix].values;
+ dp_cix = c_ix;
+ }
+ if (t_ix == GCOV_COUNTER_ICALL_TOPNV)
+ {
+ icall_profile_values = gi_ptr->counts[c_ix].values;
+ ip_cix = c_ix;
+ }
+ c_ix++;
+ }
+
+ if (dcall_profile_values == 0 && icall_profile_values == 0)
+ continue;
+
+ for (f_ix = 0; f_ix < gi_ptr->n_functions; f_ix++)
+ {
+ struct dyn_cgraph_node *caller;
+ fi_ptr = the_dyn_call_graph.functions[m_ix][f_ix];
+ caller = &the_dyn_call_graph.call_graph_nodes[m_ix][fi_ptr->ident];
+ if (dcall_profile_values)
+ {
+ n_counts = fi_ptr->n_ctrs[dp_cix];
+ gcov_build_callgraph_dc_fn (caller, dcall_profile_values, n_counts);
+ dcall_profile_values += n_counts;
+ }
+ if (icall_profile_values)
+ {
+ n_counts = fi_ptr->n_ctrs[ip_cix];
+ gcov_build_callgraph_ic_fn (caller, icall_profile_values, n_counts);
+ icall_profile_values += n_counts;
+ }
+ }
+ }
+
+}
+
+static inline size_t
+hash1 (const void *p, unsigned long max, unsigned long logmax)
+{
+ const unsigned long long A = 0x9e3779b97f4a7c16ull;
+ const unsigned long long shift = 64 - logmax;
+
+ return ((A * (unsigned long) p) >> shift) & (max - 1);
+}
+
+/* Allocate an empty pointer set. */
+
+static struct dyn_pointer_set *
+pointer_set_create (void)
+{
+ struct dyn_pointer_set *result = XNEW (struct dyn_pointer_set);
+
+ result->n_elements = 0;
+ result->log_slots = 8;
+ result->n_slots = (size_t) 1 << result->log_slots;
+
+ result->slots = XNEWVEC (const void *, result->n_slots);
+ memset (result->slots, 0, sizeof (const void *) * result->n_slots);
+ return result;
+}
+
+/* Reclaim all memory associated with PSET. */
+
+static void
+pointer_set_destroy (struct dyn_pointer_set *pset)
+{
+ XDELETEVEC (pset->slots);
+ XDELETE (pset);
+}
+
+/* Subroutine of pointer_set_insert. Return the insertion slot for P into
+ an empty element of SLOTS, an array of length N_SLOTS. */
+static inline size_t
+insert_aux (const void *p, const void **slots, size_t n_slots, size_t log_slots)
+{
+ size_t n = hash1 (p, n_slots, log_slots);
+ while (1)
+ {
+ if (slots[n] == p || slots[n] == 0)
+ return n;
+ else
+ {
+ ++n;
+ if (n == n_slots)
+ n = 0;
+ }
+ }
+}
+
+/* Insert P into PSET if it wasn't already there. Returns nonzero
+ if it was already there. P must be nonnull. */
+
+static int
+pointer_set_insert (struct dyn_pointer_set *pset, const void *p)
+{
+ size_t n;
+
+ /* For simplicity, expand the set even if P is already there. This can be
+ superfluous but can happen at most once. */
+ if (pset->n_elements > pset->n_slots / 4)
+ {
+ size_t new_log_slots = pset->log_slots + 1;
+ size_t new_n_slots = pset->n_slots * 2;
+ const void **new_slots = XNEWVEC (const void *, new_n_slots);
+ memset (new_slots, 0, sizeof (const void*) * new_n_slots);
+ size_t i;
+
+ for (i = 0; i < pset->n_slots; ++i)
+ {
+ const void *value = pset->slots[i];
+ n = insert_aux (value, new_slots, new_n_slots, new_log_slots);
+ new_slots[n] = value;
+ }
+
+ XDELETEVEC (pset->slots);
+ pset->n_slots = new_n_slots;
+ pset->log_slots = new_log_slots;
+ pset->slots = new_slots;
+ }
+
+ n = insert_aux (p, pset->slots, pset->n_slots, pset->log_slots);
+ if (pset->slots[n])
+ return 1;
+
+ pset->slots[n] = p;
+ ++pset->n_elements;
+ return 0;
+}
+
+/* Pass each pointer in PSET to the function in FN, together with the fixed
+ parameter DATA. If FN returns false, the iteration stops. */
+
+static void
+pointer_set_traverse (const struct dyn_pointer_set *pset,
+ int (*fn) (const void *, void *), void *data)
+{
+ size_t i;
+ for (i = 0; i < pset->n_slots; ++i)
+ if (pset->slots[i] && !fn (pset->slots[i], data))
+ break;
+}
+
+/* Callback function to propagate import module set (VALUE) from callee to
+ caller (DATA). */
+static int
+gcov_propagate_imp_modules (const void *value, void *data)
+{
+ struct dyn_pointer_set *receiving_set
+ = (struct dyn_pointer_set *) data;
+
+ pointer_set_insert (receiving_set, value);
+ return 1;
+}
+
+static int
+sort_by_count (const void *pa, const void *pb)
+{
+ const struct dyn_cgraph_edge *edge_a = *(struct dyn_cgraph_edge * const *)pa;
+ const struct dyn_cgraph_edge *edge_b = *(struct dyn_cgraph_edge * const *)pb;
+
+ /* This can overvlow. */
+ /* return edge_b->count - edge_a->count; */
+ if (edge_b->count > edge_a->count)
+ return 1;
+ else if (edge_b->count == edge_a->count)
+ return 0;
+ else
+ return -1;
+}
+
+/* Compute the hot callgraph edge threhold. */
+
+static gcov_type
+gcov_compute_cutoff_count (void)
+{
+ unsigned m_ix, capacity, i;
+ unsigned num_edges = 0;
+ gcov_type cutoff_count;
+ double total, cum, cum_cutoff;
+ struct dyn_cgraph_edge **edges;
+ struct gcov_info *gi_ptr;
+ char *cutoff_str;
+ char *num_perc_str;
+ unsigned cutoff_perc;
+ unsigned num_perc;
+ int do_dump;
+
+ capacity = 100;
+ /* allocate an edge array */
+ edges = XNEWVEC (struct dyn_cgraph_edge*, capacity);
+ /* First count the number of edges. */
+ for (m_ix = 0; m_ix < the_dyn_call_graph.num_modules; m_ix++)
+ {
+ const struct gcov_fn_info *fi_ptr;
+ unsigned f_ix;
+
+ gi_ptr = the_dyn_call_graph.modules[m_ix];
+
+ for (f_ix = 0; f_ix < gi_ptr->n_functions; f_ix++)
+ {
+ struct dyn_cgraph_node *node;
+ struct dyn_cgraph_edge *callees;
+
+ fi_ptr = the_dyn_call_graph.functions[m_ix][f_ix];
+
+ node = &the_dyn_call_graph.call_graph_nodes[m_ix][fi_ptr->ident];
+
+ callees = node->callees;
+ while (callees != 0)
+ {
+ num_edges++;
+ if (num_edges < capacity)
+ edges[num_edges - 1] = callees;
+ else
+ {
+ capacity = capacity + (capacity >> 1);
+ edges = (struct dyn_cgraph_edge **)realloc (edges, sizeof (void*) * capacity);
+ edges[num_edges - 1] = callees;
+ }
+ callees = callees->next_callee;
+ }
+ }
+ }
+
+ /* Now sort */
+ qsort (edges, num_edges, sizeof (void *), sort_by_count);
+#define CUM_CUTOFF_PERCENT 95
+#define MIN_NUM_EDGE_PERCENT 0
+ cutoff_str = getenv ("GCOV_DYN_CGRAPH_CUTOFF");
+ if (cutoff_str && strlen (cutoff_str))
+ {
+ if ((num_perc_str = strchr (cutoff_str, ':')))
+ {
+ *num_perc_str = '\0';
+ num_perc_str++;
+ }
+ cutoff_perc = atoi (cutoff_str);
+ if (num_perc_str)
+ num_perc = atoi (num_perc_str);
+ else
+ num_perc = MIN_NUM_EDGE_PERCENT;
+ }
+ else
+ {
+ cutoff_perc = CUM_CUTOFF_PERCENT;
+ num_perc = MIN_NUM_EDGE_PERCENT;
+ }
+
+ total = 0;
+ cum = 0;
+ for (i = 0; i < num_edges; i++)
+ total += edges[i]->count;
+
+ cum_cutoff = (total * cutoff_perc)/100;
+ do_dump = (getenv ("GCOV_DYN_CGRAPH_DUMP") != 0);
+ for (i = 0; i < num_edges; i++)
+ {
+ cum += edges[i]->count;
+ if (do_dump)
+ fprintf (stderr, "// edge[%d] count = %.0f [%llx --> %llx]\n",
+ i, (double) edges[i]->count,
+ (long long) edges[i]->caller->guid,
+ (long long) edges[i]->callee->guid);
+ if (cum >= cum_cutoff && (i * 100 >= num_edges * num_perc))
+ {
+ cutoff_count = edges[i]->count;
+ break;
+ }
+ }
+
+ if (do_dump)
+ fprintf (stderr, "//total = %.0f cum = %.0f cum/total = %.0f%%"
+ " cutoff_count = %lld [total edges: %d hot edges: %d perc: %d%%]\n",
+ total, cum, (cum * 100)/total, (long long) cutoff_count,
+ num_edges, i, (i * 100)/num_edges);
+
+ XDELETEVEC (edges);
+ return cutoff_count;
+}
+
+/* Return the imported module set for NODE. */
+
+static struct dyn_pointer_set *
+gcov_get_imp_module_set (struct dyn_cgraph_node *node)
+{
+ if (!node->imported_modules)
+ node->imported_modules = pointer_set_create ();
+
+ return node->imported_modules;
+}
+
+/* Return the imported module set for MODULE MI. */
+
+static struct dyn_pointer_set *
+gcov_get_module_imp_module_set (struct dyn_module_info *mi)
+{
+ if (!mi->imported_modules)
+ mi->imported_modules = pointer_set_create ();
+
+ return mi->imported_modules;
+}
+
+/* Callback function to mark if a module needs to be exported. */
+
+static int
+gcov_mark_export_modules (const void *value, void *data ATTRIBUTE_UNUSED)
+{
+ const struct gcov_info *module_info
+ = (const struct gcov_info *)value;
+
+ module_info->mod_info->is_exported = 1;
+ return 1;
+}
+
+struct gcov_import_mod_array
+{
+ const struct gcov_info **imported_modules;
+ struct gcov_info *importing_module;
+ unsigned len;
+};
+
+/* Callback function to compute pointer set size. */
+
+static int
+gcov_compute_pset_size (const void *value ATTRIBUTE_UNUSED,
+ void *data)
+{
+ unsigned *len = (unsigned *) data;
+ (*len)++;
+ return 1;
+}
+
+/* Callback function to collect imported modules. */
+
+static int
+gcov_collect_imported_modules (const void *value, void *data)
+{
+ struct gcov_import_mod_array *out_array;
+ const struct gcov_info *module_info
+ = (const struct gcov_info *)value;
+
+ out_array = (struct gcov_import_mod_array *) data;
+
+ if (module_info != out_array->importing_module)
+ out_array->imported_modules[out_array->len++] = module_info;
+
+ return 1;
+}
+
+/* Comparitor for sorting imported modules using module ids. */
+
+static int
+sort_by_module_id (const void *pa, const void *pb)
+{
+ const struct gcov_info *m_a = *(struct gcov_info * const *)pa;
+ const struct gcov_info *m_b = *(struct gcov_info * const *)pb;
+
+ return (int) m_a->mod_info->ident - (int) m_b->mod_info->ident;
+}
+
+/* Return a dynamic array of imported modules that is sorted for
+ the importing module MOD_INFO. The length of the array is returned
+ in *LEN. */
+
+const struct gcov_info **
+gcov_get_sorted_import_module_array (struct gcov_info *mod_info,
+ unsigned *len)
+{
+ unsigned mod_id;
+ struct dyn_module_info *sup_mod_info;
+ unsigned array_len = 0;
+ struct gcov_import_mod_array imp_array;
+
+ mod_id = get_module_idx (mod_info);
+ sup_mod_info = &the_dyn_call_graph.sup_modules[mod_id];
+
+ if (sup_mod_info->imported_modules == 0)
+ return 0;
+
+ pointer_set_traverse (sup_mod_info->imported_modules,
+ gcov_compute_pset_size, &array_len);
+ imp_array.imported_modules = XNEWVEC (const struct gcov_info *, array_len);
+ imp_array.len = 0;
+ imp_array.importing_module = mod_info;
+ pointer_set_traverse (sup_mod_info->imported_modules,
+ gcov_collect_imported_modules, &imp_array);
+ *len = imp_array.len;
+ qsort (imp_array.imported_modules, imp_array.len,
+ sizeof (void *), sort_by_module_id);
+ return imp_array.imported_modules;
+}
+
+/* Compute modules that are needed for NODE (for cross module inlining).
+ CUTTOFF_COUNT is the call graph edge count cutoff value. */
+
+static void
+gcov_process_cgraph_node (struct dyn_cgraph_node *node,
+ gcov_type cutoff_count)
+{
+ unsigned mod_id;
+ struct dyn_cgraph_edge *callees;
+ node->visited = 1;
+
+ callees = node->callees;
+ mod_id = get_module_idx_from_func_glob_uid (node->guid);
+
+ while (callees)
+ {
+ if (!callees->callee->visited)
+ gcov_process_cgraph_node (callees->callee,
+ cutoff_count);
+ callees = callees->next_callee;
+ }
+
+ callees = node->callees;
+ while (callees)
+ {
+ if (callees->count >= cutoff_count)
+ {
+ unsigned callee_mod_id;
+ struct dyn_pointer_set *imp_modules
+ = gcov_get_imp_module_set (node);
+
+ callee_mod_id
+ = get_module_idx_from_func_glob_uid (callees->callee->guid);
+
+ if (mod_id != callee_mod_id)
+ {
+ struct gcov_info *callee_mod_info
+ = get_module_info (callee_mod_id);
+ pointer_set_insert (imp_modules, callee_mod_info);
+ }
+ if (callees->callee->imported_modules)
+ pointer_set_traverse (callees->callee->imported_modules,
+ gcov_propagate_imp_modules,
+ imp_modules);
+ }
+
+ callees = callees->next_callee;
+ }
+}
+
+/* Compute module grouping using CUTOFF_COUNT as the hot edge
+ threshold. */
+
+static void
+gcov_compute_module_groups (gcov_type cutoff_count)
+{
+ unsigned m_ix;
+ struct gcov_info *gi_ptr;
+
+ for (m_ix = 0; m_ix < the_dyn_call_graph.num_modules; m_ix++)
+ {
+ const struct gcov_fn_info *fi_ptr;
+ unsigned f_ix;
+
+ gi_ptr = the_dyn_call_graph.modules[m_ix];
+
+ for (f_ix = 0; f_ix < gi_ptr->n_functions; f_ix++)
+ {
+ struct dyn_cgraph_node *node;
+
+ fi_ptr = the_dyn_call_graph.functions[m_ix][f_ix];
+ node = &the_dyn_call_graph.call_graph_nodes[m_ix][fi_ptr->ident];
+ if (node->visited)
+ continue;
+
+ gcov_process_cgraph_node (node, cutoff_count);
+ }
+ }
+
+ for (m_ix = 0; m_ix < the_dyn_call_graph.num_modules; m_ix++)
+ {
+ const struct gcov_fn_info *fi_ptr;
+ unsigned f_ix;
+
+ gi_ptr = the_dyn_call_graph.modules[m_ix];
+
+ for (f_ix = 0; f_ix < gi_ptr->n_functions; f_ix++)
+ {
+ struct dyn_cgraph_node *node;
+ unsigned mod_id;
+ struct dyn_pointer_set *imp_modules;
+
+ fi_ptr = the_dyn_call_graph.functions[m_ix][f_ix];
+ node = &the_dyn_call_graph.call_graph_nodes[m_ix][fi_ptr->ident];
+
+ if (!node->imported_modules)
+ continue;
+
+ mod_id = get_module_idx_from_func_glob_uid (node->guid);
+ gcc_assert (mod_id == m_ix);
+
+ imp_modules
+ = gcov_get_module_imp_module_set (
+ &the_dyn_call_graph.sup_modules[mod_id]);
+
+ pointer_set_traverse (node->imported_modules,
+ gcov_propagate_imp_modules,
+ imp_modules);
+ }
+ }
+
+ /* Now compute the export attribute */
+ for (m_ix = 0; m_ix < the_dyn_call_graph.num_modules; m_ix++)
+ {
+ struct dyn_module_info *mi
+ = &the_dyn_call_graph.sup_modules[m_ix];
+ if (mi->imported_modules)
+ pointer_set_traverse (mi->imported_modules,
+ gcov_mark_export_modules, 0);
+ }
+}
+
+/* For each module, compute at random, the group of imported modules,
+ that is of size at most MAX_GROUP_SIZE. */
+
+static void
+gcov_compute_random_module_groups (unsigned max_group_size)
+{
+ unsigned m_ix;
+
+ if (max_group_size > the_dyn_call_graph.num_modules)
+ max_group_size = the_dyn_call_graph.num_modules;
+
+ for (m_ix = 0; m_ix < the_dyn_call_graph.num_modules; m_ix++)
+ {
+ struct dyn_pointer_set *imp_modules =
+ gcov_get_module_imp_module_set (&the_dyn_call_graph.sup_modules[m_ix]);
+ int cur_group_size = random () % max_group_size;
+ int i = 0;
+ while (i < cur_group_size)
+ {
+ struct gcov_info *imp_mod_info;
+ unsigned mod_id = random () % the_dyn_call_graph.num_modules;
+ if (mod_id == m_ix)
+ continue;
+ imp_mod_info = get_module_info (mod_id);
+ if (!pointer_set_insert (imp_modules, imp_mod_info))
+ i++;
+ }
+ }
+
+ /* Now compute the export attribute */
+ for (m_ix = 0; m_ix < the_dyn_call_graph.num_modules; m_ix++)
+ {
+ struct dyn_module_info *mi
+ = &the_dyn_call_graph.sup_modules[m_ix];
+ if (mi->imported_modules)
+ pointer_set_traverse (mi->imported_modules,
+ gcov_mark_export_modules, 0);
+ }
+}
+
+/* Write out MOD_INFO into the gcda file. IS_PRIMARY is a flag
+ indicating if the module is the primary module in the group. */
+
+static void
+gcov_write_module_info (const struct gcov_info *mod_info,
+ unsigned is_primary)
+{
+ gcov_unsigned_t len = 0, filename_len = 0, src_filename_len = 0, i, j;
+ gcov_unsigned_t num_strings;
+ gcov_unsigned_t *aligned_fname;
+ struct gcov_module_info *module_info = mod_info->mod_info;
+ filename_len = (strlen (module_info->da_filename) +
+ sizeof (gcov_unsigned_t)) / sizeof (gcov_unsigned_t);
+ src_filename_len = (strlen (module_info->source_filename) +
+ sizeof (gcov_unsigned_t)) / sizeof (gcov_unsigned_t);
+ len = filename_len + src_filename_len;
+ len += 2; /* each name string is led by a length. */
+
+ num_strings = module_info->num_quote_paths + module_info->num_bracket_paths +
+ module_info->num_cpp_defines;
+ for (i = 0; i < num_strings; i++)
+ {
+ gcov_unsigned_t string_len
+ = (strlen (module_info->string_array[i]) + sizeof (gcov_unsigned_t))
+ / sizeof (gcov_unsigned_t);
+ len += string_len;
+ len += 1; /* Each string is lead by a length. */
+ }
+
+ len += 7; /* 7 more fields */
+
+ gcov_write_tag_length (GCOV_TAG_MODULE_INFO, len);
+ gcov_write_unsigned (module_info->ident);
+ gcov_write_unsigned (is_primary);
+ gcov_write_unsigned (module_info->is_exported);
+ gcov_write_unsigned (module_info->lang);
+ gcov_write_unsigned (module_info->num_quote_paths);
+ gcov_write_unsigned (module_info->num_bracket_paths);
+ gcov_write_unsigned (module_info->num_cpp_defines);
+
+ /* Now write the filenames */
+ aligned_fname = (gcov_unsigned_t *) alloca ((filename_len + src_filename_len + 2) *
+ sizeof (gcov_unsigned_t));
+ memset (aligned_fname, 0,
+ (filename_len + src_filename_len + 2) * sizeof (gcov_unsigned_t));
+ aligned_fname[0] = filename_len;
+ strcpy ((char*) (aligned_fname + 1), module_info->da_filename);
+ aligned_fname[filename_len + 1] = src_filename_len;
+ strcpy ((char*) (aligned_fname + filename_len + 2), module_info->source_filename);
+
+ for (i = 0; i < (filename_len + src_filename_len + 2); i++)
+ gcov_write_unsigned (aligned_fname[i]);
+
+ /* Now write the string array. */
+ for (j = 0; j < num_strings; j++)
+ {
+ gcov_unsigned_t *aligned_string;
+ gcov_unsigned_t string_len =
+ (strlen (module_info->string_array[j]) + sizeof (gcov_unsigned_t)) /
+ sizeof (gcov_unsigned_t);
+ aligned_string = (gcov_unsigned_t *)
+ alloca ((string_len + 1) * sizeof (gcov_unsigned_t));
+ memset (aligned_string, 0, (string_len + 1) * sizeof (gcov_unsigned_t));
+ aligned_string[0] = string_len;
+ strcpy ((char*) (aligned_string + 1), module_info->string_array[j]);
+ for (i = 0; i < (string_len + 1); i++)
+ gcov_write_unsigned (aligned_string[i]);
+ }
+}
+
+/* Write out MOD_INFO and its imported modules into gcda file. */
+
+void
+gcov_write_module_infos (struct gcov_info *mod_info)
+{
+ unsigned mod_id, imp_len = 0;
+ const struct gcov_info **imp_mods;
+
+ mod_id = get_module_idx (mod_info);
+ gcov_write_module_info (mod_info, 1);
+
+ imp_mods = gcov_get_sorted_import_module_array (mod_info, &imp_len);
+ if (imp_mods)
+ {
+ unsigned i;
+
+ for (i = 0; i < imp_len; i++)
+ {
+ const struct gcov_info *imp_mod = imp_mods[i];
+ gcov_write_module_info (imp_mod, 0);
+ }
+ free (imp_mods);
+ }
+}
+
+/* Compute module groups needed for L-IPO compilation. */
+
+void
+__gcov_compute_module_groups (void)
+{
+ gcov_type cut_off_count;
+ char *seed = getenv ("LIPO_RANDOM_GROUPING");
+ char *max_group_size = seed ? strchr (seed, ':') : 0;
+
+ if (seed && max_group_size)
+ {
+ *max_group_size = '\0';
+ max_group_size++;
+ srandom (atoi (seed));
+ init_dyn_call_graph ();
+ gcov_compute_random_module_groups (atoi (max_group_size));
+ return;
+ }
+
+ /* First compute dynamic call graph. */
+ gcov_build_callgraph ();
+
+ cut_off_count = gcov_compute_cutoff_count ();
+
+ gcov_compute_module_groups (cut_off_count);
+
+ gcov_dump_callgraph (cut_off_count);
+
+}
+
+/* Dumper function for NODE. */
+static void
+gcov_dump_cgraph_node_short (struct dyn_cgraph_node *node)
+{
+ unsigned mod_id, func_id;
+ struct gcov_info *mod_info;
+ mod_id = get_module_idx_from_func_glob_uid (node->guid);
+ func_id = get_intra_module_func_id (node->guid);
+
+ mod_info = the_dyn_call_graph.modules[mod_id];
+
+ fprintf (stderr, "NODE(%llx) module(%s) func(%u)",
+ (long long)node->guid,
+ mod_info->mod_info->source_filename, func_id);
+}
+
+/* Dumper function for NODE. M is the module id and F is the function id. */
+
+static void
+gcov_dump_cgraph_node (struct dyn_cgraph_node *node, unsigned m, unsigned f)
+{
+ unsigned mod_id, func_id;
+ struct gcov_info *mod_info;
+ struct dyn_cgraph_edge *callers;
+ struct dyn_cgraph_edge *callees;
+
+ mod_id = get_module_idx_from_func_glob_uid (node->guid);
+ func_id = get_intra_module_func_id (node->guid);
+ gcc_assert (mod_id == m && func_id == f);
+
+ mod_info = the_dyn_call_graph.modules[mod_id];
+
+ fprintf (stderr, "NODE(%llx) module(%s) func(%x)\n",
+ (long long) node->guid,
+ mod_info->mod_info->source_filename, f);
+
+ /* Now dump callers. */
+ callers = node->callers;
+ fprintf (stderr, "\t[CALLERS]\n");
+ while (callers != 0)
+ {
+ fprintf (stderr,"\t\t[count=%ld] ", (long) callers->count);
+ gcov_dump_cgraph_node_short (callers->caller);
+ fprintf (stderr,"\n");
+ callers = callers->next_caller;
+ }
+
+ callees = node->callees;
+ fprintf (stderr, "\t[CALLEES]\n");
+ while (callees != 0)
+ {
+ fprintf (stderr,"\t\t[count=%ld] ", (long) callees->count);
+ gcov_dump_cgraph_node_short (callees->callee);
+ fprintf (stderr,"\n");
+ callees = callees->next_callee;
+ }
+}
+
+/* Dumper function for NODE. M is the module id and F is the function id. */
+
+static void
+gcov_dump_cgraph_node_dot (struct dyn_cgraph_node *node,
+ unsigned m, unsigned f,
+ gcov_type cutoff_count)
+{
+ unsigned mod_id, func_id, imp_len = 0, i;
+ struct gcov_info *mod_info;
+ const struct gcov_info **imp_mods;
+ struct dyn_cgraph_edge *callees;
+
+ mod_id = get_module_idx_from_func_glob_uid (node->guid);
+ func_id = get_intra_module_func_id (node->guid);
+ gcc_assert (mod_id == m && func_id == f);
+
+ mod_info = the_dyn_call_graph.modules[mod_id];
+
+ fprintf (stderr, "NODE_%llx[label=\"MODULE\\n(%s)\\n FUNC(%x)\\n",
+ (long long) node->guid, mod_info->mod_info->source_filename, f);
+
+ imp_mods = gcov_get_sorted_import_module_array (mod_info, &imp_len);
+ fprintf (stderr, "IMPORTS:\\n");
+ if (imp_mods)
+ {
+ for (i = 0; i < imp_len; i++)
+ fprintf (stderr, "%s\\n", imp_mods[i]->mod_info->source_filename);
+ fprintf (stderr, "\"]\n");
+ free (imp_mods);
+ }
+ else
+ fprintf (stderr, "\"]\n");
+
+ callees = node->callees;
+ while (callees != 0)
+ {
+ if (callees->count >= cutoff_count)
+ fprintf (stderr, "NODE_%llx -> NODE_%llx[label=%lld color=red]\n",
+ (long long) node->guid, (long long) callees->callee->guid,
+ (long long) callees->count);
+ else
+ fprintf (stderr, "NODE_%llx -> NODE_%llx[label=%lld color=blue]\n",
+ (long long) node->guid, (long long) callees->callee->guid,
+ (long long) callees->count);
+ callees = callees->next_callee;
+ }
+}
+
+/* Dump dynamic call graph. CUTOFF_COUNT is the computed hot edge threshold. */
+
+static void
+gcov_dump_callgraph (gcov_type cutoff_count)
+{
+ struct gcov_info *gi_ptr;
+ unsigned m_ix;
+ const char *dyn_cgraph_dump = 0;
+
+ dyn_cgraph_dump = getenv ("GCOV_DYN_CGRAPH_DUMP");
+
+ if (!dyn_cgraph_dump || !strlen (dyn_cgraph_dump))
+ return;
+
+ fprintf (stderr,"digraph dyn_call_graph {\n");
+ fprintf (stderr,"node[shape=box]\nsize=\"11,8.5\"\n");
+
+ for (m_ix = 0; m_ix < the_dyn_call_graph.num_modules; m_ix++)
+ {
+ const struct gcov_fn_info *fi_ptr;
+ unsigned f_ix;
+
+ gi_ptr = the_dyn_call_graph.modules[m_ix];
+
+ for (f_ix = 0; f_ix < gi_ptr->n_functions; f_ix++)
+ {
+ struct dyn_cgraph_node *node;
+ fi_ptr = the_dyn_call_graph.functions[m_ix][f_ix];
+
+ node = &the_dyn_call_graph.call_graph_nodes[m_ix][fi_ptr->ident];
+
+ /* skip dead functions */
+ if (!node->callees && !node->callers)
+ continue;
+
+ if (dyn_cgraph_dump[0] == '1')
+ gcov_dump_cgraph_node (node, m_ix, fi_ptr->ident);
+ else
+ gcov_dump_cgraph_node_dot (node, m_ix, fi_ptr->ident,
+ cutoff_count);
+ }
+ }
+ fprintf (stderr,"}\n");
+}
+
+
+#endif