/* Calculate branch probabilities, and basic block execution counts.
Copyright (C) 1990, 1991, 1992, 1993, 1994, 1996, 1997, 1998, 1999,
- 2000, 2001 Free Software Foundation, Inc.
+ 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
Contributed by James E. Wilson, UC Berkeley/Cygnus Support;
based on some ideas from Dain Samples of UC Berkeley.
Further mangling by Bob Manson, Cygnus Support.
edges must be on the spanning tree. We also attempt to place
EDGE_CRITICAL edges on the spanning tree.
- The auxiliary file generated is <dumpbase>.bbg. The format is
+ The auxiliary files generated are <dumpbase>.gcno (at compile time)
+ and <dumpbase>.gcda (at run time). The format is
described in full in gcov-io.h. */
/* ??? Register allocation should use basic block execution counts to
#include "coretypes.h"
#include "tm.h"
#include "rtl.h"
-#include "tree.h"
#include "flags.h"
-#include "insn-config.h"
#include "output.h"
#include "regs.h"
#include "expr.h"
#include "function.h"
#include "toplev.h"
-#include "ggc.h"
-#include "hard-reg-set.h"
-#include "basic-block.h"
-#include "gcov-io.h"
-#include "target.h"
-#include "profile.h"
-#include "libfuncs.h"
-#include "langhooks.h"
-#include "hashtab.h"
+#include "coverage.h"
+#include "value-prof.h"
+#include "tree.h"
+#include "cfghooks.h"
+#include "tree-flow.h"
+
+/* Hooks for profiling. */
+static struct profile_hooks* profile_hooks;
+
+/* File for profiling debug output. */
+static inline FILE*
+profile_dump_file (void) {
+ return profile_hooks->profile_dump_file ();
+}
/* Additional information about the edges we need. */
struct edge_info {
unsigned int count_valid : 1;
-
+
/* Is on the spanning tree. */
unsigned int on_tree : 1;
-
+
/* Pretend this edge does not exist (it is abnormal and we've
inserted a fake to compensate). */
unsigned int ignore : 1;
gcov_type pred_count;
};
-struct function_list
-{
- struct function_list *next; /* next function */
- const char *name; /* function name */
- unsigned cfg_checksum; /* function checksum */
- unsigned n_counter_sections; /* number of counter sections */
- struct counter_section counter_sections[MAX_COUNTER_SECTIONS];
- /* the sections */
-};
-
-
-/* Counts information for a function. */
-typedef struct counts_entry
-{
- /* We hash by */
- char *function_name;
- unsigned section;
-
- /* Store */
- unsigned checksum;
- unsigned n_counts;
- gcov_type *counts;
- unsigned merged;
- gcov_type max_counter;
- gcov_type max_counter_sum;
-
- /* Workspace */
- struct counts_entry *chain;
-
-} counts_entry_t;
-
-static struct function_list *functions_head = 0;
-static struct function_list **functions_tail = &functions_head;
-
#define EDGE_INFO(e) ((struct edge_info *) (e)->aux)
#define BB_INFO(b) ((struct bb_info *) (b)->aux)
-/* Keep all basic block indexes nonnegative in the gcov output. Index 0
- is used for entry block, last block exit block. */
-#define BB_TO_GCOV_INDEX(bb) ((bb) == ENTRY_BLOCK_PTR ? 0 \
- : ((bb) == EXIT_BLOCK_PTR \
- ? last_basic_block + 1 : (bb)->index + 1))
-
-/* Instantiate the profile info structure. */
-
-struct profile_info profile_info;
-
-/* Name and file pointer of the output file for the basic block graph. */
+/* Counter summary from the last set of coverage counts read. */
-static char *bbg_file_name;
-
-/* Name and file pointer of the input file for the arc count data. */
-
-static char *da_file_name;
-
-/* The name of the count table. Used by the edge profiling code. */
-static GTY(()) rtx profiler_label;
+const struct gcov_ctr_summary *profile_info;
/* Collect statistics on the performance of this pass for the entire source
file. */
static int total_num_branches;
/* Forward declarations. */
-static void find_spanning_tree PARAMS ((struct edge_list *));
-static rtx gen_edge_profiler PARAMS ((int));
-static void instrument_edges PARAMS ((struct edge_list *));
-static void compute_branch_probabilities PARAMS ((void));
-static hashval_t htab_counts_entry_hash PARAMS ((const void *));
-static int htab_counts_entry_eq PARAMS ((const void *, const void *));
-static void htab_counts_entry_del PARAMS ((void *));
-static void read_counts_file PARAMS ((const char *));
-static gcov_type * get_exec_counts PARAMS ((void));
-static unsigned compute_checksum PARAMS ((void));
-static basic_block find_group PARAMS ((basic_block));
-static void union_groups PARAMS ((basic_block, basic_block));
-static void set_purpose PARAMS ((tree, tree));
-static rtx label_for_tag PARAMS ((unsigned));
-static tree build_counter_section_fields PARAMS ((void));
-static tree build_counter_section_value PARAMS ((unsigned, unsigned));
-static tree build_counter_section_data_fields PARAMS ((void));
-static tree build_counter_section_data_value PARAMS ((unsigned, unsigned));
-static tree build_function_info_fields PARAMS ((void));
-static tree build_function_info_value PARAMS ((struct function_list *));
-static tree build_gcov_info_fields PARAMS ((tree));
-static tree build_gcov_info_value PARAMS ((void));
+static void find_spanning_tree (struct edge_list *);
+static unsigned instrument_edges (struct edge_list *);
+static void instrument_values (unsigned, struct histogram_value *);
+static void compute_branch_probabilities (void);
+static void compute_value_histograms (unsigned, struct histogram_value *);
+static gcov_type * get_exec_counts (void);
+static basic_block find_group (basic_block);
+static void union_groups (basic_block, basic_block);
\f
/* Add edge instrumentation code to the entire insn chain.
F is the first insn of the chain.
NUM_BLOCKS is the number of basic blocks found in F. */
-static void
-instrument_edges (el)
- struct edge_list *el;
+static unsigned
+instrument_edges (struct edge_list *el)
{
- int num_instr_edges = 0;
+ unsigned num_instr_edges = 0;
int num_edges = NUM_EDGES (el);
basic_block bb;
- struct section_info *section_info;
+
remove_fake_edges ();
FOR_BB_BETWEEN (bb, ENTRY_BLOCK_PTR, NULL, next_bb)
{
- edge e = bb->succ;
- while (e)
+ edge e;
+
+ for (e = bb->succ; e; e = e->succ_next)
{
struct edge_info *inf = EDGE_INFO (e);
+
if (!inf->ignore && !inf->on_tree)
{
if (e->flags & EDGE_ABNORMAL)
abort ();
- if (rtl_dump_file)
- fprintf (rtl_dump_file, "Edge %d to %d instrumented%s\n",
+ if (dump_file)
+ fprintf (dump_file, "Edge %d to %d instrumented%s\n",
e->src->index, e->dest->index,
EDGE_CRITICAL_P (e) ? " (and split)" : "");
- insert_insn_on_edge (
- gen_edge_profiler (total_num_edges_instrumented
- + num_instr_edges++), e);
- rebuild_jump_labels (e->insns);
+ (profile_hooks->gen_edge_profiler) (num_instr_edges++, e);
}
- e = e->succ_next;
}
}
- section_info = find_counters_section (GCOV_TAG_ARC_COUNTS);
- section_info->n_counters_now = num_instr_edges;
- total_num_edges_instrumented += num_instr_edges;
- section_info->n_counters = total_num_edges_instrumented;
-
total_num_blocks_created += num_edges;
- if (rtl_dump_file)
- fprintf (rtl_dump_file, "%d edges instrumented\n", num_instr_edges);
-}
-\f
-static hashval_t
-htab_counts_entry_hash (of)
- const void *of;
-{
- const counts_entry_t *entry = of;
-
- return htab_hash_string (entry->function_name) ^ entry->section;
-}
-
-static int
-htab_counts_entry_eq (of1, of2)
- const void *of1;
- const void *of2;
-{
- const counts_entry_t *entry1 = of1;
- const counts_entry_t *entry2 = of2;
-
- return !strcmp (entry1->function_name, entry2->function_name)
- && entry1->section == entry2->section;
+ if (dump_file)
+ fprintf (dump_file, "%d edges instrumented\n", num_instr_edges);
+ return num_instr_edges;
}
+/* Add code to measure histograms list of VALUES of length N_VALUES. */
static void
-htab_counts_entry_del (of)
- void *of;
+instrument_values (unsigned n_values, struct histogram_value *values)
{
- counts_entry_t *entry = of;
+ unsigned i, t;
- free (entry->function_name);
- free (entry->counts);
- free (entry);
-}
-
-static htab_t counts_hash = NULL;
+ /* Emit code to generate the histograms before the insns. */
-static void
-read_counts_file (const char *name)
-{
- char *function_name_buffer = NULL;
- unsigned version, ix, checksum = -1;
- counts_entry_t *summaried = NULL;
- unsigned seen_summary = 0;
-
- if (!gcov_open (name, 1))
- {
- warning ("file %s not found, execution counts assumed to be zero", name);
- return;
- }
-
- if (gcov_read_unsigned () != GCOV_DATA_MAGIC)
- {
- warning ("`%s' is not a gcov data file", name);
- gcov_close ();
- return;
- }
- else if ((version = gcov_read_unsigned ()) != GCOV_VERSION)
+ for (i = 0; i < n_values; i++)
{
- char v[4], e[4];
- unsigned required = GCOV_VERSION;
-
- for (ix = 4; ix--; required >>= 8, version >>= 8)
+ switch (values[i].type)
{
- v[ix] = version;
- e[ix] = required;
- }
- warning ("`%s' is version `%.4s', expected version `%.4s'", name, v, e);
- gcov_close ();
- return;
- }
-
- counts_hash = htab_create (10,
- htab_counts_entry_hash, htab_counts_entry_eq,
- htab_counts_entry_del);
- while (!gcov_is_eof ())
- {
- unsigned tag, length;
- unsigned long offset;
- int error;
-
- tag = gcov_read_unsigned ();
- length = gcov_read_unsigned ();
- offset = gcov_position ();
- if (tag == GCOV_TAG_FUNCTION)
- {
- const char *string = gcov_read_string ();
- free (function_name_buffer);
- function_name_buffer = string ? xstrdup (string) : NULL;
- checksum = gcov_read_unsigned ();
- if (seen_summary)
- {
- /* We have already seen a summary, this means that this
- new function begins a new set of program runs. We
- must unlink the summaried chain. */
- counts_entry_t *entry, *chain;
-
- for (entry = summaried; entry; entry = chain)
- {
- chain = entry->chain;
-
- entry->max_counter_sum += entry->max_counter;
- entry->chain = NULL;
- }
- summaried = NULL;
- seen_summary = 0;
- }
- }
- else if (tag == GCOV_TAG_PROGRAM_SUMMARY)
- {
- counts_entry_t *entry;
- struct gcov_summary summary;
-
- gcov_read_summary (&summary);
- seen_summary = 1;
- for (entry = summaried; entry; entry = entry->chain)
- {
- entry->merged += summary.runs;
- if (entry->max_counter < summary.arc_sum_max)
- entry->max_counter = summary.arc_sum_max;
- }
- }
- else if (GCOV_TAG_IS_SUBTAG (GCOV_TAG_FUNCTION, tag)
- && function_name_buffer)
- {
- counts_entry_t **slot, *entry, elt;
- unsigned n_counts = length / 8;
- unsigned ix;
+ case HIST_TYPE_INTERVAL:
+ t = GCOV_COUNTER_V_INTERVAL;
+ break;
- elt.function_name = function_name_buffer;
- elt.section = tag;
+ case HIST_TYPE_POW2:
+ t = GCOV_COUNTER_V_POW2;
+ break;
- slot = (counts_entry_t **) htab_find_slot
- (counts_hash, &elt, INSERT);
- entry = *slot;
- if (!entry)
- {
- *slot = entry = xmalloc (sizeof (counts_entry_t));
- entry->function_name = xstrdup (function_name_buffer);
- entry->section = tag;
- entry->checksum = checksum;
- entry->n_counts = n_counts;
- entry->counts = xcalloc (n_counts, sizeof (gcov_type));
- }
- else if (entry->checksum != checksum || entry->n_counts != n_counts)
- {
- warning ("profile mismatch for `%s'", function_name_buffer);
- htab_delete (counts_hash);
- break;
- }
-
- /* This should always be true for a just allocated entry,
- and always false for an existing one. Check this way, in
- case the gcov file is corrupt. */
- if (!entry->chain || summaried != entry)
- {
- entry->chain = summaried;
- summaried = entry;
- }
- for (ix = 0; ix != n_counts; ix++)
- entry->counts[ix] += gcov_read_counter ();
+ case HIST_TYPE_SINGLE_VALUE:
+ t = GCOV_COUNTER_V_SINGLE;
+ break;
+
+ case HIST_TYPE_CONST_DELTA:
+ t = GCOV_COUNTER_V_DELTA;
+ break;
+
+ default:
+ abort ();
}
- gcov_seek (offset, length);
- if ((error = gcov_is_error ()))
+ if (!coverage_counter_alloc (t, values[i].n_counters))
+ continue;
+
+ switch (values[i].type)
{
- warning (error < 0 ? "`%s' has overflowed" : "`%s' is corrupted",
- name);
- htab_delete (counts_hash);
+ case HIST_TYPE_INTERVAL:
+ (profile_hooks->gen_interval_profiler) (values + i, t, 0);
+ break;
+
+ case HIST_TYPE_POW2:
+ (profile_hooks->gen_pow2_profiler) (values + i, t, 0);
break;
+
+ case HIST_TYPE_SINGLE_VALUE:
+ (profile_hooks->gen_one_value_profiler) (values + i, t, 0);
+ break;
+
+ case HIST_TYPE_CONST_DELTA:
+ (profile_hooks->gen_const_delta_profiler) (values + i, t, 0);
+ break;
+
+ default:
+ abort ();
}
}
-
- free (function_name_buffer);
- gcov_close ();
}
+\f
-/* Computes hybrid profile for all matching entries in da_file.
- Sets max_counter_in_program as a side effect. */
+/* Computes hybrid profile for all matching entries in da_file. */
static gcov_type *
-get_exec_counts ()
+get_exec_counts (void)
{
unsigned num_edges = 0;
basic_block bb;
- const char *name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (current_function_decl));
- counts_entry_t *entry, elt;
-
- profile_info.max_counter_in_program = 0;
- profile_info.count_profiles_merged = 0;
-
- /* No hash table, no counts. */
- if (!counts_hash)
- return NULL;
+ gcov_type *counts;
/* Count the edges to be (possibly) instrumented. */
FOR_BB_BETWEEN (bb, ENTRY_BLOCK_PTR, NULL, next_bb)
num_edges++;
}
- elt.function_name = (char *) name;
- elt.section = GCOV_TAG_ARC_COUNTS;
- entry = htab_find (counts_hash, &elt);
- if (!entry)
- {
- warning ("No profile for function '%s' found.", name);
- return NULL;
- }
-
- if (entry->checksum != profile_info.current_function_cfg_checksum
- || num_edges != entry->n_counts)
- {
- warning ("profile mismatch for `%s'", current_function_name);
- return NULL;
- }
-
- profile_info.count_profiles_merged = entry->merged;
- profile_info.max_counter_in_program = entry->max_counter_sum;
+ counts = get_coverage_counts (GCOV_COUNTER_ARCS, num_edges, &profile_info);
+ if (!counts)
+ return NULL;
- if (rtl_dump_file)
- {
- fprintf(rtl_dump_file, "Merged %i profiles with maximal count %i.\n",
- profile_info.count_profiles_merged,
- (int)profile_info.max_counter_in_program);
- }
+ if (dump_file && profile_info)
+ fprintf(dump_file, "Merged %u profiles with maximal count %u.\n",
+ profile_info->runs, (unsigned) profile_info->sum_max);
- return entry->counts;
+ return counts;
}
\f
Annotate them accordingly. */
static void
-compute_branch_probabilities ()
+compute_branch_probabilities (void)
{
basic_block bb;
int i;
gcov_type *exec_counts = get_exec_counts ();
int exec_counts_pos = 0;
+ /* Very simple sanity checks so we catch bugs in our profiling code. */
+ if (profile_info)
+ {
+ if (profile_info->run_max * profile_info->runs < profile_info->sum_max)
+ {
+ error ("corrupted profile info: run_max * runs < sum_max");
+ exec_counts = NULL;
+ }
+
+ if (profile_info->sum_all < profile_info->sum_max)
+ {
+ error ("corrupted profile info: sum_all is smaller than sum_max");
+ exec_counts = NULL;
+ }
+ }
+
/* Attach extra info block to each bb. */
alloc_aux_for_blocks (sizeof (struct bb_info));
if (exec_counts)
{
e->count = exec_counts[exec_counts_pos++];
+ if (e->count > profile_info->sum_max)
+ {
+ error ("corrupted profile info: edge from %i to %i exceeds maximal count",
+ bb->index, e->dest->index);
+ }
}
else
e->count = 0;
EDGE_INFO (e)->count_valid = 1;
BB_INFO (bb)->succ_count--;
BB_INFO (e->dest)->pred_count--;
- if (rtl_dump_file)
+ if (dump_file)
{
- fprintf (rtl_dump_file, "\nRead edge from %i to %i, count:",
+ fprintf (dump_file, "\nRead edge from %i to %i, count:",
bb->index, e->dest->index);
- fprintf (rtl_dump_file, HOST_WIDEST_INT_PRINT_DEC,
+ fprintf (dump_file, HOST_WIDEST_INT_PRINT_DEC,
(HOST_WIDEST_INT) e->count);
}
}
}
- if (rtl_dump_file)
- fprintf (rtl_dump_file, "\n%d edge counts read\n", num_edges);
+ if (dump_file)
+ fprintf (dump_file, "\n%d edge counts read\n", num_edges);
/* For every block in the file,
- if every exit/entrance edge has a known count, then set the block count
for (e = bb->pred; e; e = e->pred_next)
total += e->count;
- /* Seedgeh for the invalid edge, and set its count. */
+ /* Search for the invalid edge, and set its count. */
for (e = bb->pred; e; e = e->pred_next)
- if (! EDGE_INFO (e)->count_valid && ! EDGE_INFO (e)->ignore)
+ if (!EDGE_INFO (e)->count_valid && !EDGE_INFO (e)->ignore)
break;
/* Calculate count for remaining edge by conservation. */
}
}
}
- if (rtl_dump_file)
- dump_flow_info (rtl_dump_file);
+ if (dump_file)
+ dump_flow_info (dump_file);
total_num_passes += passes;
- if (rtl_dump_file)
- fprintf (rtl_dump_file, "Graph solving took %d passes.\n\n", passes);
+ if (dump_file)
+ fprintf (dump_file, "Graph solving took %d passes.\n\n", passes);
/* If the graph has been correctly solved, every block will have a
succ and pred count of zero. */
FOR_BB_BETWEEN (bb, ENTRY_BLOCK_PTR, NULL, next_bb)
{
edge e;
- gcov_type total;
rtx note;
- total = bb->count;
- if (total)
+ if (bb->count < 0)
{
- for (e = bb->succ; e; e = e->succ_next)
+ error ("corrupted profile info: number of iterations for basic block %d thought to be %i",
+ bb->index, (int)bb->count);
+ bb->count = 0;
+ }
+ for (e = bb->succ; e; e = e->succ_next)
+ {
+ /* Function may return twice in the cased the called function is
+ setjmp or calls fork, but we can't represent this by extra
+ edge from the entry, since extra edge from the exit is
+ already present. We get negative frequency from the entry
+ point. */
+ if ((e->count < 0
+ && e->dest == EXIT_BLOCK_PTR)
+ || (e->count > bb->count
+ && e->dest != EXIT_BLOCK_PTR))
{
- e->probability = (e->count * REG_BR_PROB_BASE + total / 2) / total;
- if (e->probability < 0 || e->probability > REG_BR_PROB_BASE)
- {
- error ("corrupted profile info: prob for %d-%d thought to be %d",
- e->src->index, e->dest->index, e->probability);
- e->probability = REG_BR_PROB_BASE / 2;
- }
+ if (block_ends_with_call_p (bb))
+ e->count = e->count < 0 ? 0 : bb->count;
}
+ if (e->count < 0 || e->count > bb->count)
+ {
+ error ("corrupted profile info: number of executions for edge %d-%d thought to be %i",
+ e->src->index, e->dest->index,
+ (int)e->count);
+ e->count = bb->count / 2;
+ }
+ }
+ if (bb->count)
+ {
+ for (e = bb->succ; e; e = e->succ_next)
+ e->probability = (e->count * REG_BR_PROB_BASE + bb->count / 2) / bb->count;
if (bb->index >= 0
- && any_condjump_p (bb->end)
+ && block_ends_with_condjump_p (bb)
&& bb->succ->succ_next)
{
int prob;
index = 19;
hist_br_prob[index]++;
- note = find_reg_note (bb->end, REG_BR_PROB, 0);
- /* There may be already note put by some other pass, such
- as builtin_expect expander. */
- if (note)
- XEXP (note, 0) = GEN_INT (prob);
- else
- REG_NOTES (bb->end)
- = gen_rtx_EXPR_LIST (REG_BR_PROB, GEN_INT (prob),
- REG_NOTES (bb->end));
+ /* Do this for RTL only. */
+ if (!ir_type ())
+ {
+ note = find_reg_note (BB_END (bb), REG_BR_PROB, 0);
+ /* There may be already note put by some other pass, such
+ as builtin_expect expander. */
+ if (note)
+ XEXP (note, 0) = GEN_INT (prob);
+ else
+ REG_NOTES (BB_END (bb))
+ = gen_rtx_EXPR_LIST (REG_BR_PROB, GEN_INT (prob),
+ REG_NOTES (BB_END (bb)));
+ }
num_branches++;
}
}
calls). */
else
{
+ int total = 0;
+
for (e = bb->succ; e; e = e->succ_next)
if (!(e->flags & (EDGE_COMPLEX | EDGE_FAKE)))
total ++;
e->probability = REG_BR_PROB_BASE / total;
}
if (bb->index >= 0
- && any_condjump_p (bb->end)
+ && block_ends_with_condjump_p (bb)
&& bb->succ->succ_next)
num_branches++, num_never_executed;
}
}
- if (rtl_dump_file)
+ if (dump_file)
{
- fprintf (rtl_dump_file, "%d branches\n", num_branches);
- fprintf (rtl_dump_file, "%d branches never executed\n",
+ fprintf (dump_file, "%d branches\n", num_branches);
+ fprintf (dump_file, "%d branches never executed\n",
num_never_executed);
if (num_branches)
for (i = 0; i < 10; i++)
- fprintf (rtl_dump_file, "%d%% branches in range %d-%d%%\n",
+ fprintf (dump_file, "%d%% branches in range %d-%d%%\n",
(hist_br_prob[i] + hist_br_prob[19-i]) * 100 / num_branches,
5 * i, 5 * i + 5);
for (i = 0; i < 20; i++)
total_hist_br_prob[i] += hist_br_prob[i];
- fputc ('\n', rtl_dump_file);
- fputc ('\n', rtl_dump_file);
+ fputc ('\n', dump_file);
+ fputc ('\n', dump_file);
}
free_aux_for_blocks ();
- find_counters_section (GCOV_TAG_ARC_COUNTS)->present = 1;
}
-/* Compute checksum for the current function. We generate a CRC32. */
-
-static unsigned
-compute_checksum ()
+/* Load value histograms for N_VALUES values whose description is stored
+ in VALUES array from .da file. */
+static void
+compute_value_histograms (unsigned n_values, struct histogram_value *values)
{
- unsigned chksum = 0;
- basic_block bb;
-
- FOR_EACH_BB (bb)
+ unsigned i, j, t, any;
+ unsigned n_histogram_counters[GCOV_N_VALUE_COUNTERS];
+ gcov_type *histogram_counts[GCOV_N_VALUE_COUNTERS];
+ gcov_type *act_count[GCOV_N_VALUE_COUNTERS];
+ gcov_type *aact_count;
+
+ for (t = 0; t < GCOV_N_VALUE_COUNTERS; t++)
+ n_histogram_counters[t] = 0;
+
+ for (i = 0; i < n_values; i++)
+ n_histogram_counters[(int) (values[i].type)] += values[i].n_counters;
+
+ any = 0;
+ for (t = 0; t < GCOV_N_VALUE_COUNTERS; t++)
{
- edge e = NULL;
-
- do
+ if (!n_histogram_counters[t])
{
- unsigned value = BB_TO_GCOV_INDEX (e ? e->dest : bb);
- unsigned ix;
-
- /* No need to use all bits in value identically, nearly all
- functions have less than 256 blocks. */
- value ^= value << 16;
- value ^= value << 8;
-
- for (ix = 8; ix--; value <<= 1)
- {
- unsigned feedback;
+ histogram_counts[t] = NULL;
+ continue;
+ }
- feedback = (value ^ chksum) & 0x80000000 ? 0x04c11db7 : 0;
- chksum <<= 1;
- chksum ^= feedback;
- }
-
- e = e ? e->succ_next : bb->succ;
+ histogram_counts[t] =
+ get_coverage_counts (COUNTER_FOR_HIST_TYPE (t),
+ n_histogram_counters[t], NULL);
+ if (histogram_counts[t])
+ any = 1;
+ act_count[t] = histogram_counts[t];
+ }
+ if (!any)
+ return;
+
+ for (i = 0; i < n_values; i++)
+ {
+ rtx hist_list = NULL_RTX;
+ t = (int) (values[i].type);
+
+ /* FIXME: make this work for trees. */
+ if (!ir_type ())
+ {
+ aact_count = act_count[t];
+ act_count[t] += values[i].n_counters;
+ for (j = values[i].n_counters; j > 0; j--)
+ hist_list = alloc_EXPR_LIST (0, GEN_INT (aact_count[j - 1]),
+ hist_list);
+ hist_list = alloc_EXPR_LIST (0,
+ copy_rtx ((rtx)values[i].value), hist_list);
+ hist_list = alloc_EXPR_LIST (0, GEN_INT (values[i].type), hist_list);
+ REG_NOTES ((rtx)values[i].insn) =
+ alloc_EXPR_LIST (REG_VALUE_PROFILE, hist_list,
+ REG_NOTES ((rtx)values[i].insn));
}
- while (e);
}
- return chksum;
+ for (t = 0; t < GCOV_N_VALUE_COUNTERS; t++)
+ if (histogram_counts[t])
+ free (histogram_counts[t]);
}
/* Instrument and/or analyze program behavior based on program flow graph.
Main entry point of this file. */
void
-branch_prob ()
+branch_prob (void)
{
basic_block bb;
unsigned i;
unsigned num_edges, ignored_edges;
+ unsigned num_instrumented;
struct edge_list *el;
- const char *name = IDENTIFIER_POINTER
- (DECL_ASSEMBLER_NAME (current_function_decl));
-
- profile_info.current_function_cfg_checksum = compute_checksum ();
- for (i = 0; i < profile_info.n_sections; i++)
- {
- profile_info.section_info[i].n_counters_now = 0;
- profile_info.section_info[i].present = 0;
- }
-
- if (rtl_dump_file)
- fprintf (rtl_dump_file, "CFG checksum is %u\n",
- profile_info.current_function_cfg_checksum);
+ unsigned n_values = 0;
+ struct histogram_value *values = NULL;
total_num_times_called++;
{
int need_exit_edge = 0, need_entry_edge = 0;
int have_exit_edge = 0, have_entry_edge = 0;
- rtx insn;
edge e;
- /* Add fake edges from entry block to the call insns that may return
- twice. The CFG is not quite correct then, as call insn plays more
- role of CODE_LABEL, but for our purposes, everything should be OK,
- as we never insert code to the beginning of basic block. */
- for (insn = bb->head; insn != NEXT_INSN (bb->end);
- insn = NEXT_INSN (insn))
- {
- if (GET_CODE (insn) == CALL_INSN
- && find_reg_note (insn, REG_SETJMP, NULL))
- {
- if (GET_CODE (bb->head) == CODE_LABEL
- || insn != NEXT_INSN (bb->head))
- {
- e = split_block (bb, PREV_INSN (insn));
- make_edge (ENTRY_BLOCK_PTR, e->dest, EDGE_FAKE);
- break;
- }
- else
- {
- /* We should not get abort here, as call to setjmp should not
- be the very first instruction of function. */
- if (bb == ENTRY_BLOCK_PTR)
- abort ();
- make_edge (ENTRY_BLOCK_PTR, bb, EDGE_FAKE);
- }
- }
- }
+ /* Functions returning multiple times are not handled by extra edges.
+ Instead we simply allow negative counts on edges from exit to the
+ block past call and corresponding probabilities. We can't go
+ with the extra edges because that would result in flowgraph that
+ needs to have fake edges outside the spanning tree. */
for (e = bb->succ; e; e = e->succ_next)
{
if (need_exit_edge && !have_exit_edge)
{
- if (rtl_dump_file)
- fprintf (rtl_dump_file, "Adding fake exit edge to bb %i\n",
+ if (dump_file)
+ fprintf (dump_file, "Adding fake exit edge to bb %i\n",
bb->index);
make_edge (bb, EXIT_BLOCK_PTR, EDGE_FAKE);
}
if (need_entry_edge && !have_entry_edge)
{
- if (rtl_dump_file)
- fprintf (rtl_dump_file, "Adding fake entry edge to bb %i\n",
+ if (dump_file)
+ fprintf (dump_file, "Adding fake entry edge to bb %i\n",
bb->index);
make_edge (ENTRY_BLOCK_PTR, bb, EDGE_FAKE);
}
/* Fake edges that are not on the tree will not be instrumented, so
mark them ignored. */
- for (i = 0; i < num_edges; i++)
+ for (num_instrumented = i = 0; i < num_edges; i++)
{
edge e = INDEX_EDGE (el, i);
struct edge_info *inf = EDGE_INFO (e);
- if ((e->flags & EDGE_FAKE) && !inf->ignore && !inf->on_tree)
+
+ if (inf->ignore || inf->on_tree)
+ /*NOP*/;
+ else if (e->flags & EDGE_FAKE)
{
inf->ignore = 1;
ignored_edges++;
}
+ else
+ num_instrumented++;
}
total_num_blocks += n_basic_blocks + 2;
- if (rtl_dump_file)
- fprintf (rtl_dump_file, "%d basic blocks\n", n_basic_blocks);
+ if (dump_file)
+ fprintf (dump_file, "%d basic blocks\n", n_basic_blocks);
total_num_edges += num_edges;
- if (rtl_dump_file)
- fprintf (rtl_dump_file, "%d edges\n", num_edges);
+ if (dump_file)
+ fprintf (dump_file, "%d edges\n", num_edges);
total_num_edges_ignored += ignored_edges;
- if (rtl_dump_file)
- fprintf (rtl_dump_file, "%d ignored edges\n", ignored_edges);
+ if (dump_file)
+ fprintf (dump_file, "%d ignored edges\n", ignored_edges);
- /* Create a .bbg file from which gcov can reconstruct the basic block
- graph. First output the number of basic blocks, and then for every
- edge output the source and target basic block numbers.
- NOTE: The format of this file must be compatible with gcov. */
+ /* Write the data from which gcov can reconstruct the basic block
+ graph. */
- if (!gcov_is_error ())
+ /* Basic block flags */
+ if (coverage_begin_output ())
{
- long offset;
- const char *file = DECL_SOURCE_FILE (current_function_decl);
- unsigned line = DECL_SOURCE_LINE (current_function_decl);
-
- /* Announce function */
- offset = gcov_write_tag (GCOV_TAG_FUNCTION);
- gcov_write_string (name);
- gcov_write_unsigned (profile_info.current_function_cfg_checksum);
- gcov_write_string (file);
- gcov_write_unsigned (line);
- gcov_write_length (offset);
+ gcov_position_t offset;
- /* Basic block flags */
offset = gcov_write_tag (GCOV_TAG_BLOCKS);
for (i = 0; i != (unsigned) (n_basic_blocks + 2); i++)
gcov_write_unsigned (0);
gcov_write_length (offset);
-
- /* Arcs */
+ }
+
+ /* Keep all basic block indexes nonnegative in the gcov output.
+ Index 0 is used for entry block, last index is for exit block.
+ */
+ ENTRY_BLOCK_PTR->index = -1;
+ EXIT_BLOCK_PTR->index = last_basic_block;
+#define BB_TO_GCOV_INDEX(bb) ((bb)->index + 1)
+
+ /* Arcs */
+ if (coverage_begin_output ())
+ {
+ gcov_position_t offset;
+
FOR_BB_BETWEEN (bb, ENTRY_BLOCK_PTR, EXIT_BLOCK_PTR, next_bb)
{
edge e;
offset = gcov_write_tag (GCOV_TAG_ARCS);
gcov_write_unsigned (BB_TO_GCOV_INDEX (bb));
-
+
for (e = bb->succ; e; e = e->succ_next)
{
struct edge_info *i = EDGE_INFO (e);
if (!i->ignore)
{
unsigned flag_bits = 0;
-
+
if (i->on_tree)
flag_bits |= GCOV_ARC_ON_TREE;
if (e->flags & EDGE_FAKE)
gcov_write_length (offset);
}
+ }
- /* Output line number information about each basic block for
- GCOV utility. */
- {
- char const *prev_file_name = NULL;
-
- FOR_EACH_BB (bb)
- {
- rtx insn = bb->head;
- int ignore_next_note = 0;
-
- offset = 0;
-
- /* We are looking for line number notes. Search backward
- before basic block to find correct ones. */
- insn = prev_nonnote_insn (insn);
- if (!insn)
- insn = get_insns ();
- else
- insn = NEXT_INSN (insn);
+ /* Line numbers. */
+ /* FIXME: make this work for trees. (Line numbers are in location_t
+ objects, but aren't always attached to the obvious tree...) */
+ if (coverage_begin_output () && !ir_type ())
+ {
+ char const *prev_file_name = NULL;
+ gcov_position_t offset;
- while (insn != bb->end)
- {
- if (GET_CODE (insn) == NOTE)
- {
- /* Must ignore the line number notes that immediately
- follow the end of an inline function to avoid counting
- it twice. There is a note before the call, and one
- after the call. */
- if (NOTE_LINE_NUMBER (insn)
- == NOTE_INSN_REPEATED_LINE_NUMBER)
- ignore_next_note = 1;
- else if (NOTE_LINE_NUMBER (insn) <= 0)
- /*NOP*/;
- else if (ignore_next_note)
- ignore_next_note = 0;
- else
- {
- if (!offset)
- {
- offset = gcov_write_tag (GCOV_TAG_LINES);
- gcov_write_unsigned (BB_TO_GCOV_INDEX (bb));
- }
-
- /* If this is a new source file, then output
- the file's name to the .bb file. */
- if (!prev_file_name
- || strcmp (NOTE_SOURCE_FILE (insn),
- prev_file_name))
- {
- prev_file_name = NOTE_SOURCE_FILE (insn);
- gcov_write_unsigned (0);
- gcov_write_string (prev_file_name);
- }
- gcov_write_unsigned (NOTE_LINE_NUMBER (insn));
- }
- }
- insn = NEXT_INSN (insn);
- }
+ FOR_EACH_BB (bb)
+ {
+ rtx insn = BB_HEAD (bb);
+ int ignore_next_note = 0;
- if (offset)
- {
- /* A file of NULL indicates the end of run. */
- gcov_write_unsigned (0);
- gcov_write_string (NULL);
- gcov_write_length (offset);
- }
- if (gcov_is_error ())
- warning ("error writing `%s'", bbg_file_name);
- }
- }
+ offset = 0;
+
+ /* We are looking for line number notes. Search backward
+ before basic block to find correct ones. */
+ insn = prev_nonnote_insn (insn);
+ if (!insn)
+ insn = get_insns ();
+ else
+ insn = NEXT_INSN (insn);
+
+ while (insn != BB_END (bb))
+ {
+ if (NOTE_P (insn))
+ {
+ /* Must ignore the line number notes that
+ immediately follow the end of an inline function
+ to avoid counting it twice. There is a note
+ before the call, and one after the call. */
+ if (NOTE_LINE_NUMBER (insn)
+ == NOTE_INSN_REPEATED_LINE_NUMBER)
+ ignore_next_note = 1;
+ else if (NOTE_LINE_NUMBER (insn) <= 0)
+ /*NOP*/;
+ else if (ignore_next_note)
+ ignore_next_note = 0;
+ else
+ {
+ expanded_location s;
+
+ if (!offset)
+ {
+ offset = gcov_write_tag (GCOV_TAG_LINES);
+ gcov_write_unsigned (BB_TO_GCOV_INDEX (bb));
+ }
+
+ NOTE_EXPANDED_LOCATION (s, insn);
+
+ /* If this is a new source file, then output the
+ file's name to the .bb file. */
+ if (!prev_file_name
+ || strcmp (s.file, prev_file_name))
+ {
+ prev_file_name = s.file;
+ gcov_write_unsigned (0);
+ gcov_write_string (prev_file_name);
+ }
+ gcov_write_unsigned (s.line);
+ }
+ }
+ insn = NEXT_INSN (insn);
+ }
+
+ if (offset)
+ {
+ /* A file of NULL indicates the end of run. */
+ gcov_write_unsigned (0);
+ gcov_write_string (NULL);
+ gcov_write_length (offset);
+ }
+ }
}
- if (flag_branch_probabilities)
- compute_branch_probabilities ();
+ ENTRY_BLOCK_PTR->index = ENTRY_BLOCK;
+ EXIT_BLOCK_PTR->index = EXIT_BLOCK;
+#undef BB_TO_GCOV_INDEX
- /* For each edge not on the spanning tree, add counting code as rtl. */
+ if (flag_profile_values)
+ find_values_to_profile (&n_values, &values);
- if (cfun->arc_profile && profile_arc_flag)
+ if (flag_branch_probabilities)
{
- struct function_list *item;
-
- instrument_edges (el);
+ compute_branch_probabilities ();
+ if (flag_profile_values)
+ compute_value_histograms (n_values, values);
+ }
+
+ /* For each edge not on the spanning tree, add counting code. */
+ if (profile_arc_flag
+ && coverage_counter_alloc (GCOV_COUNTER_ARCS, num_instrumented))
+ {
+ unsigned n_instrumented = instrument_edges (el);
+
+ if (n_instrumented != num_instrumented)
+ abort ();
+
+ if (flag_profile_values)
+ instrument_values (n_values, values);
/* Commit changes done by instrumentation. */
- commit_edge_insertions_watch_calls ();
- allocate_reg_info (max_reg_num (), FALSE, FALSE);
-
- /* ??? Probably should re-use the existing struct function. */
- item = xmalloc (sizeof (struct function_list));
-
- *functions_tail = item;
- functions_tail = &item->next;
-
- item->next = 0;
- item->name = xstrdup (name);
- item->cfg_checksum = profile_info.current_function_cfg_checksum;
- item->n_counter_sections = 0;
- for (i = 0; i < profile_info.n_sections; i++)
- if (profile_info.section_info[i].n_counters_now)
- {
- item->counter_sections[item->n_counter_sections].tag =
- profile_info.section_info[i].tag;
- item->counter_sections[item->n_counter_sections].n_counters =
- profile_info.section_info[i].n_counters_now;
- item->n_counter_sections++;
- }
+ if (ir_type ())
+ bsi_commit_edge_inserts ((int *)NULL);
+ else
+ {
+ commit_edge_insertions_watch_calls ();
+ allocate_reg_info (max_reg_num (), FALSE, FALSE);
+ }
}
remove_fake_edges ();
free_aux_for_edges ();
- /* Re-merge split basic blocks and the mess introduced by
- insert_insn_on_edge. */
- cleanup_cfg (profile_arc_flag ? CLEANUP_EXPENSIVE : 0);
- if (rtl_dump_file)
- dump_flow_info (rtl_dump_file);
+
+ if (!ir_type ())
+ {
+ /* Re-merge split basic blocks and the mess introduced by
+ insert_insn_on_edge. */
+ cleanup_cfg (profile_arc_flag ? CLEANUP_EXPENSIVE : 0);
+ if (profile_dump_file())
+ dump_flow_info (profile_dump_file());
+ }
free_edge_list (el);
}
aux fields. */
static basic_block
-find_group (bb)
- basic_block bb;
+find_group (basic_block bb)
{
basic_block group = bb, bb1;
}
static void
-union_groups (bb1, bb2)
- basic_block bb1, bb2;
+union_groups (basic_block bb1, basic_block bb2)
{
basic_block bb1g = find_group (bb1);
basic_block bb2g = find_group (bb2);
are more expensive to instrument. */
static void
-find_spanning_tree (el)
- struct edge_list *el;
+find_spanning_tree (struct edge_list *el)
{
int i;
int num_edges = NUM_EDGES (el);
{
edge e = INDEX_EDGE (el, i);
if (((e->flags & (EDGE_ABNORMAL | EDGE_ABNORMAL_CALL | EDGE_FAKE))
- || e->dest == EXIT_BLOCK_PTR
- )
+ || e->dest == EXIT_BLOCK_PTR)
&& !EDGE_INFO (e)->ignore
&& (find_group (e->src) != find_group (e->dest)))
{
- if (rtl_dump_file)
- fprintf (rtl_dump_file, "Abnormal edge %d to %d put to tree\n",
+ if (dump_file)
+ fprintf (dump_file, "Abnormal edge %d to %d put to tree\n",
e->src->index, e->dest->index);
EDGE_INFO (e)->on_tree = 1;
union_groups (e->src, e->dest);
for (i = 0; i < num_edges; i++)
{
edge e = INDEX_EDGE (el, i);
- if ((EDGE_CRITICAL_P (e))
- && !EDGE_INFO (e)->ignore
- && (find_group (e->src) != find_group (e->dest)))
+ if (EDGE_CRITICAL_P (e) && !EDGE_INFO (e)->ignore
+ && find_group (e->src) != find_group (e->dest))
{
- if (rtl_dump_file)
- fprintf (rtl_dump_file, "Critical edge %d to %d put to tree\n",
+ if (dump_file)
+ fprintf (dump_file, "Critical edge %d to %d put to tree\n",
e->src->index, e->dest->index);
EDGE_INFO (e)->on_tree = 1;
union_groups (e->src, e->dest);
for (i = 0; i < num_edges; i++)
{
edge e = INDEX_EDGE (el, i);
- if (find_group (e->src) != find_group (e->dest)
- && !EDGE_INFO (e)->ignore)
+ if (!EDGE_INFO (e)->ignore
+ && find_group (e->src) != find_group (e->dest))
{
- if (rtl_dump_file)
- fprintf (rtl_dump_file, "Normal edge %d to %d put to tree\n",
+ if (dump_file)
+ fprintf (dump_file, "Normal edge %d to %d put to tree\n",
e->src->index, e->dest->index);
EDGE_INFO (e)->on_tree = 1;
union_groups (e->src, e->dest);
/* Perform file-level initialization for branch-prob processing. */
void
-init_branch_prob (filename)
- const char *filename;
+init_branch_prob (void)
{
- int len = strlen (filename);
int i;
- da_file_name = (char *) xmalloc (len + strlen (GCOV_DATA_SUFFIX) + 1);
- strcpy (da_file_name, filename);
- strcat (da_file_name, GCOV_DATA_SUFFIX);
-
- if (flag_branch_probabilities)
- read_counts_file (da_file_name);
-
- if (flag_test_coverage)
- {
- /* Open the bbg output file. */
- bbg_file_name = (char *) xmalloc (len + strlen (GCOV_GRAPH_SUFFIX) + 1);
- strcpy (bbg_file_name, filename);
- strcat (bbg_file_name, GCOV_GRAPH_SUFFIX);
- if (!gcov_open (bbg_file_name, -1))
- error ("cannot open %s", bbg_file_name);
- gcov_write_unsigned (GCOV_GRAPH_MAGIC);
- gcov_write_unsigned (GCOV_VERSION);
- }
-
- if (profile_arc_flag)
- {
- /* Generate and save a copy of this so it can be shared. */
- char buf[20];
-
- ASM_GENERATE_INTERNAL_LABEL (buf, "LPBX", 2);
- profiler_label = gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (buf));
- }
-
total_num_blocks = 0;
total_num_edges = 0;
total_num_edges_ignored = 0;
is completed. */
void
-end_branch_prob ()
+end_branch_prob (void)
{
- if (flag_test_coverage)
+ if (dump_file)
{
- int error = gcov_close ();
-
- if (error)
- unlink (bbg_file_name);
-#if SELF_COVERAGE
- /* If the compiler is instrumented, we should not
- unconditionally remove the counts file, because we might be
- recompiling ourselves. The .da files are all removed during
- copying the stage1 files. */
- if (error)
-#endif
- unlink (da_file_name);
- }
-
- if (rtl_dump_file)
- {
- fprintf (rtl_dump_file, "\n");
- fprintf (rtl_dump_file, "Total number of blocks: %d\n",
+ fprintf (dump_file, "\n");
+ fprintf (dump_file, "Total number of blocks: %d\n",
total_num_blocks);
- fprintf (rtl_dump_file, "Total number of edges: %d\n", total_num_edges);
- fprintf (rtl_dump_file, "Total number of ignored edges: %d\n",
+ fprintf (dump_file, "Total number of edges: %d\n", total_num_edges);
+ fprintf (dump_file, "Total number of ignored edges: %d\n",
total_num_edges_ignored);
- fprintf (rtl_dump_file, "Total number of instrumented edges: %d\n",
+ fprintf (dump_file, "Total number of instrumented edges: %d\n",
total_num_edges_instrumented);
- fprintf (rtl_dump_file, "Total number of blocks created: %d\n",
+ fprintf (dump_file, "Total number of blocks created: %d\n",
total_num_blocks_created);
- fprintf (rtl_dump_file, "Total number of graph solution passes: %d\n",
+ fprintf (dump_file, "Total number of graph solution passes: %d\n",
total_num_passes);
if (total_num_times_called != 0)
- fprintf (rtl_dump_file, "Average number of graph solution passes: %d\n",
+ fprintf (dump_file, "Average number of graph solution passes: %d\n",
(total_num_passes + (total_num_times_called >> 1))
/ total_num_times_called);
- fprintf (rtl_dump_file, "Total number of branches: %d\n",
+ fprintf (dump_file, "Total number of branches: %d\n",
total_num_branches);
- fprintf (rtl_dump_file, "Total number of branches never executed: %d\n",
+ fprintf (dump_file, "Total number of branches never executed: %d\n",
total_num_never_executed);
if (total_num_branches)
{
int i;
for (i = 0; i < 10; i++)
- fprintf (rtl_dump_file, "%d%% branches in range %d-%d%%\n",
+ fprintf (dump_file, "%d%% branches in range %d-%d%%\n",
(total_hist_br_prob[i] + total_hist_br_prob[19-i]) * 100
/ total_num_branches, 5*i, 5*i+5);
}
}
}
-/* Find (and create if not present) a section with TAG. */
-struct section_info *
-find_counters_section (tag)
- unsigned tag;
-{
- unsigned i;
-
- for (i = 0; i < profile_info.n_sections; i++)
- if (profile_info.section_info[i].tag == tag)
- return profile_info.section_info + i;
-
- if (i == MAX_COUNTER_SECTIONS)
- abort ();
-
- profile_info.section_info[i].tag = tag;
- profile_info.section_info[i].present = 0;
- profile_info.section_info[i].n_counters = 0;
- profile_info.section_info[i].n_counters_now = 0;
- profile_info.n_sections++;
-
- return profile_info.section_info + i;
-}
-
-/* Set FIELDS as purpose to VALUE. */
-static void
-set_purpose (value, fields)
- tree value;
- tree fields;
-{
- tree act_field, act_value;
-
- for (act_field = fields, act_value = value;
- act_field;
- act_field = TREE_CHAIN (act_field), act_value = TREE_CHAIN (act_value))
- TREE_PURPOSE (act_value) = act_field;
-}
+/* Set up hooks to enable tree-based profiling. */
-/* Returns label for base of counters inside TAG section. */
-static rtx
-label_for_tag (tag)
- unsigned tag;
-{
- switch (tag)
- {
- case GCOV_TAG_ARC_COUNTS:
- return profiler_label;
- default:
- abort ();
- }
-}
-
-/* Creates fields of struct counter_section (in gcov-io.h). */
-static tree
-build_counter_section_fields ()
-{
- tree field, fields;
-
- /* tag */
- fields = build_decl (FIELD_DECL, NULL_TREE, unsigned_type_node);
-
- /* n_counters */
- field = build_decl (FIELD_DECL, NULL_TREE, unsigned_type_node);
- TREE_CHAIN (field) = fields;
- fields = field;
-
- return fields;
-}
-
-/* Creates value of struct counter_section (in gcov-io.h). */
-static tree
-build_counter_section_value (tag, n_counters)
- unsigned tag;
- unsigned n_counters;
-{
- tree value = NULL_TREE;
-
- /* tag */
- value = tree_cons (NULL_TREE,
- convert (unsigned_type_node,
- build_int_2 (tag, 0)),
- value);
-
- /* n_counters */
- value = tree_cons (NULL_TREE,
- convert (unsigned_type_node,
- build_int_2 (n_counters, 0)),
- value);
-
- return value;
-}
-
-/* Creates fields of struct counter_section_data (in gcov-io.h). */
-static tree
-build_counter_section_data_fields ()
-{
- tree field, fields, gcov_type, gcov_ptr_type;
-
- gcov_type = make_signed_type (GCOV_TYPE_SIZE);
- gcov_ptr_type =
- build_pointer_type (build_qualified_type (gcov_type,
- TYPE_QUAL_CONST));
-
- /* tag */
- fields = build_decl (FIELD_DECL, NULL_TREE, unsigned_type_node);
-
- /* n_counters */
- field = build_decl (FIELD_DECL, NULL_TREE, unsigned_type_node);
- TREE_CHAIN (field) = fields;
- fields = field;
-
- /* counters */
- field = build_decl (FIELD_DECL, NULL_TREE, gcov_ptr_type);
- TREE_CHAIN (field) = fields;
- fields = field;
-
- return fields;
-}
-
-/* Creates value of struct counter_section_data (in gcov-io.h). */
-static tree
-build_counter_section_data_value (tag, n_counters)
- unsigned tag;
- unsigned n_counters;
-{
- tree value = NULL_TREE, counts_table, gcov_type, gcov_ptr_type;
-
- gcov_type = make_signed_type (GCOV_TYPE_SIZE);
- gcov_ptr_type
- = build_pointer_type (build_qualified_type
- (gcov_type, TYPE_QUAL_CONST));
-
- /* tag */
- value = tree_cons (NULL_TREE,
- convert (unsigned_type_node,
- build_int_2 (tag, 0)),
- value);
-
- /* n_counters */
- value = tree_cons (NULL_TREE,
- convert (unsigned_type_node,
- build_int_2 (n_counters, 0)),
- value);
-
- /* counters */
- if (n_counters)
- {
- tree gcov_type_array_type =
- build_array_type (gcov_type,
- build_index_type (build_int_2 (n_counters - 1,
- 0)));
- counts_table =
- build (VAR_DECL, gcov_type_array_type, NULL_TREE, NULL_TREE);
- TREE_STATIC (counts_table) = 1;
- DECL_NAME (counts_table) = get_identifier (XSTR (label_for_tag (tag), 0));
- assemble_variable (counts_table, 0, 0, 0);
- counts_table = build1 (ADDR_EXPR, gcov_ptr_type, counts_table);
- }
- else
- counts_table = null_pointer_node;
-
- value = tree_cons (NULL_TREE, counts_table, value);
-
- return value;
-}
-
-/* Creates fields for struct function_info type (in gcov-io.h). */
-static tree
-build_function_info_fields ()
-{
- tree field, fields, counter_section_fields, counter_section_type;
- tree counter_sections_ptr_type;
- tree string_type =
- build_pointer_type (build_qualified_type (char_type_node,
- TYPE_QUAL_CONST));
- /* name */
- fields = build_decl (FIELD_DECL, NULL_TREE, string_type);
-
- /* checksum */
- field = build_decl (FIELD_DECL, NULL_TREE, unsigned_type_node);
- TREE_CHAIN (field) = fields;
- fields = field;
-
- /* n_counter_sections */
- field = build_decl (FIELD_DECL, NULL_TREE, unsigned_type_node);
- TREE_CHAIN (field) = fields;
- fields = field;
-
- /* counter_sections */
- counter_section_fields = build_counter_section_fields ();
- counter_section_type = (*lang_hooks.types.make_type) (RECORD_TYPE);
- finish_builtin_struct (counter_section_type, "__counter_section",
- counter_section_fields, NULL_TREE);
- counter_sections_ptr_type =
- build_pointer_type
- (build_qualified_type (counter_section_type,
- TYPE_QUAL_CONST));
- field = build_decl (FIELD_DECL, NULL_TREE, counter_sections_ptr_type);
- TREE_CHAIN (field) = fields;
- fields = field;
-
- return fields;
-}
-
-/* Creates value for struct function_info (in gcov-io.h). */
-static tree
-build_function_info_value (function)
- struct function_list *function;
-{
- tree value = NULL_TREE;
- size_t name_len = strlen (function->name);
- tree fname = build_string (name_len + 1, function->name);
- tree string_type =
- build_pointer_type (build_qualified_type (char_type_node,
- TYPE_QUAL_CONST));
- tree counter_section_fields, counter_section_type, counter_sections_value;
- tree counter_sections_ptr_type, counter_sections_array_type;
- unsigned i;
-
- /* name */
- TREE_TYPE (fname) =
- build_array_type (char_type_node,
- build_index_type (build_int_2 (name_len, 0)));
- value = tree_cons (NULL_TREE,
- build1 (ADDR_EXPR,
- string_type,
- fname),
- value);
-
- /* checksum */
- value = tree_cons (NULL_TREE,
- convert (unsigned_type_node,
- build_int_2 (function->cfg_checksum, 0)),
- value);
-
- /* n_counter_sections */
-
- value = tree_cons (NULL_TREE,
- convert (unsigned_type_node,
- build_int_2 (function->n_counter_sections, 0)),
- value);
-
- /* counter_sections */
- counter_section_fields = build_counter_section_fields ();
- counter_section_type = (*lang_hooks.types.make_type) (RECORD_TYPE);
- counter_sections_ptr_type =
- build_pointer_type
- (build_qualified_type (counter_section_type,
- TYPE_QUAL_CONST));
- counter_sections_array_type =
- build_array_type (counter_section_type,
- build_index_type (
- build_int_2 (function->n_counter_sections - 1,
- 0)));
-
- counter_sections_value = NULL_TREE;
- for (i = 0; i < function->n_counter_sections; i++)
- {
- tree counter_section_value =
- build_counter_section_value (function->counter_sections[i].tag,
- function->counter_sections[i].n_counters);
- set_purpose (counter_section_value, counter_section_fields);
- counter_sections_value =
- tree_cons (NULL_TREE,
- build_constructor (counter_section_type,
- nreverse (counter_section_value)),
- counter_sections_value);
- }
- finish_builtin_struct (counter_section_type, "__counter_section",
- counter_section_fields, NULL_TREE);
-
- if (function->n_counter_sections)
- {
- counter_sections_value =
- build_constructor (counter_sections_array_type,
- nreverse (counter_sections_value)),
- counter_sections_value = build1 (ADDR_EXPR,
- counter_sections_ptr_type,
- counter_sections_value);
- }
- else
- counter_sections_value = null_pointer_node;
-
- value = tree_cons (NULL_TREE, counter_sections_value, value);
-
- return value;
-}
-
-/* Creates fields of struct gcov_info type (in gcov-io.h). */
-static tree
-build_gcov_info_fields (gcov_info_type)
- tree gcov_info_type;
-{
- tree field, fields;
- char *filename;
- int filename_len;
- tree string_type =
- build_pointer_type (build_qualified_type (char_type_node,
- TYPE_QUAL_CONST));
- tree function_info_fields, function_info_type, function_info_ptr_type;
- tree counter_section_data_fields, counter_section_data_type;
- tree counter_section_data_ptr_type;
-
- /* Version ident */
- fields = build_decl (FIELD_DECL, NULL_TREE, long_unsigned_type_node);
-
- /* next -- NULL */
- field = build_decl (FIELD_DECL, NULL_TREE,
- build_pointer_type (build_qualified_type (gcov_info_type,
- TYPE_QUAL_CONST)));
- TREE_CHAIN (field) = fields;
- fields = field;
-
- /* Filename */
- filename = getpwd ();
- filename = (filename && da_file_name[0] != '/'
- ? concat (filename, "/", da_file_name, NULL)
- : da_file_name);
- filename_len = strlen (filename);
- if (filename != da_file_name)
- free (filename);
-
- field = build_decl (FIELD_DECL, NULL_TREE, string_type);
- TREE_CHAIN (field) = fields;
- fields = field;
-
- /* Workspace */
- field = build_decl (FIELD_DECL, NULL_TREE, long_integer_type_node);
- TREE_CHAIN (field) = fields;
- fields = field;
-
- /* number of functions */
- field = build_decl (FIELD_DECL, NULL_TREE, unsigned_type_node);
- TREE_CHAIN (field) = fields;
- fields = field;
-
- /* function_info table */
- function_info_fields = build_function_info_fields ();
- function_info_type = (*lang_hooks.types.make_type) (RECORD_TYPE);
- finish_builtin_struct (function_info_type, "__function_info",
- function_info_fields, NULL_TREE);
- function_info_ptr_type =
- build_pointer_type
- (build_qualified_type (function_info_type,
- TYPE_QUAL_CONST));
- field = build_decl (FIELD_DECL, NULL_TREE, function_info_ptr_type);
- TREE_CHAIN (field) = fields;
- fields = field;
-
- /* n_counter_sections */
- field = build_decl (FIELD_DECL, NULL_TREE, unsigned_type_node);
- TREE_CHAIN (field) = fields;
- fields = field;
-
- /* counter sections */
- counter_section_data_fields = build_counter_section_data_fields ();
- counter_section_data_type = (*lang_hooks.types.make_type) (RECORD_TYPE);
- finish_builtin_struct (counter_section_data_type, "__counter_section_data",
- counter_section_data_fields, NULL_TREE);
- counter_section_data_ptr_type =
- build_pointer_type
- (build_qualified_type (counter_section_data_type,
- TYPE_QUAL_CONST));
- field = build_decl (FIELD_DECL, NULL_TREE, counter_section_data_ptr_type);
- TREE_CHAIN (field) = fields;
- fields = field;
-
- return fields;
-}
-
-/* Creates struct gcov_info value (in gcov-io.h). */
-static tree
-build_gcov_info_value ()
+void
+tree_register_profile_hooks (void)
{
- tree value = NULL_TREE;
- tree filename_string;
- char *filename;
- int filename_len;
- unsigned n_functions, i;
- struct function_list *item;
- tree string_type =
- build_pointer_type (build_qualified_type (char_type_node,
- TYPE_QUAL_CONST));
- tree function_info_fields, function_info_type, function_info_ptr_type;
- tree functions;
- tree counter_section_data_fields, counter_section_data_type;
- tree counter_section_data_ptr_type, counter_sections;
-
- /* Version ident */
- value = tree_cons (NULL_TREE,
- convert (long_unsigned_type_node,
- build_int_2 (GCOV_VERSION, 0)),
- value);
-
- /* next -- NULL */
- value = tree_cons (NULL_TREE, null_pointer_node, value);
-
- /* Filename */
- filename = getpwd ();
- filename = (filename && da_file_name[0] != '/'
- ? concat (filename, "/", da_file_name, NULL)
- : da_file_name);
- filename_len = strlen (filename);
- filename_string = build_string (filename_len + 1, filename);
- if (filename != da_file_name)
- free (filename);
- TREE_TYPE (filename_string) =
- build_array_type (char_type_node,
- build_index_type (build_int_2 (filename_len, 0)));
- value = tree_cons (NULL_TREE,
- build1 (ADDR_EXPR,
- string_type,
- filename_string),
- value);
-
- /* Workspace */
- value = tree_cons (NULL_TREE,
- convert (long_integer_type_node, integer_zero_node),
- value);
-
- /* number of functions */
- n_functions = 0;
- for (item = functions_head; item != 0; item = item->next, n_functions++)
- continue;
- value = tree_cons (NULL_TREE,
- convert (unsigned_type_node,
- build_int_2 (n_functions, 0)),
- value);
-
- /* function_info table */
- function_info_fields = build_function_info_fields ();
- function_info_type = (*lang_hooks.types.make_type) (RECORD_TYPE);
- function_info_ptr_type =
- build_pointer_type (
- build_qualified_type (function_info_type,
- TYPE_QUAL_CONST));
- functions = NULL_TREE;
- for (item = functions_head; item != 0; item = item->next)
- {
- tree function_info_value = build_function_info_value (item);
- set_purpose (function_info_value, function_info_fields);
- functions = tree_cons (NULL_TREE,
- build_constructor (function_info_type,
- nreverse (function_info_value)),
- functions);
- }
- finish_builtin_struct (function_info_type, "__function_info",
- function_info_fields, NULL_TREE);
-
- /* Create constructor for array. */
- if (n_functions)
- {
- tree array_type;
-
- array_type = build_array_type (
- function_info_type,
- build_index_type (build_int_2 (n_functions - 1, 0)));
- functions = build_constructor (array_type, nreverse (functions));
- functions = build1 (ADDR_EXPR,
- function_info_ptr_type,
- functions);
- }
- else
- functions = null_pointer_node;
-
- value = tree_cons (NULL_TREE, functions, value);
-
- /* n_counter_sections */
- value = tree_cons (NULL_TREE,
- convert (unsigned_type_node,
- build_int_2 (profile_info.n_sections, 0)),
- value);
-
- /* counter sections */
- counter_section_data_fields = build_counter_section_data_fields ();
- counter_section_data_type = (*lang_hooks.types.make_type) (RECORD_TYPE);
- counter_sections = NULL_TREE;
- for (i = 0; i < profile_info.n_sections; i++)
- {
- tree counter_sections_value =
- build_counter_section_data_value (
- profile_info.section_info[i].tag,
- profile_info.section_info[i].n_counters);
- set_purpose (counter_sections_value, counter_section_data_fields);
- counter_sections =
- tree_cons (NULL_TREE,
- build_constructor (counter_section_data_type,
- nreverse (counter_sections_value)),
- counter_sections);
- }
- finish_builtin_struct (counter_section_data_type, "__counter_section_data",
- counter_section_data_fields, NULL_TREE);
- counter_section_data_ptr_type =
- build_pointer_type
- (build_qualified_type (counter_section_data_type,
- TYPE_QUAL_CONST));
-
- if (profile_info.n_sections)
- {
- tree cst_type = build_index_type (build_int_2 (profile_info.n_sections-1,
- 0));
- cst_type = build_array_type (counter_section_data_type, cst_type);
- counter_sections = build_constructor (cst_type,
- nreverse (counter_sections));
- counter_sections = build1 (ADDR_EXPR,
- counter_section_data_ptr_type,
- counter_sections);
- }
- else
- counter_sections = null_pointer_node;
- value = tree_cons (NULL_TREE, counter_sections, value);
-
- return value;
+ profile_hooks = &tree_profile_hooks;
+ if (!ir_type ())
+ abort ();
}
-/* Write out the structure which libgcc uses to locate all the arc
- counters. The structures used here must match those defined in
- gcov-io.h. Write out the constructor to call __gcov_init. */
+/* Set up hooks to enable RTL-based profiling. */
void
-create_profiler ()
-{
- tree gcov_info_fields, gcov_info_type, gcov_info_value, gcov_info;
- char name[20];
- char *ctor_name;
- tree ctor;
- rtx gcov_info_address;
- int save_flag_inline_functions = flag_inline_functions;
- unsigned i;
-
- for (i = 0; i < profile_info.n_sections; i++)
- if (profile_info.section_info[i].n_counters_now)
- break;
- if (i == profile_info.n_sections)
- return;
-
- gcov_info_type = (*lang_hooks.types.make_type) (RECORD_TYPE);
- gcov_info_fields = build_gcov_info_fields (gcov_info_type);
- gcov_info_value = build_gcov_info_value ();
- set_purpose (gcov_info_value, gcov_info_fields);
- finish_builtin_struct (gcov_info_type, "__gcov_info",
- gcov_info_fields, NULL_TREE);
-
- gcov_info = build (VAR_DECL, gcov_info_type, NULL_TREE, NULL_TREE);
- DECL_INITIAL (gcov_info) =
- build_constructor (gcov_info_type, nreverse (gcov_info_value));
-
- TREE_STATIC (gcov_info) = 1;
- ASM_GENERATE_INTERNAL_LABEL (name, "LPBX", 0);
- DECL_NAME (gcov_info) = get_identifier (name);
-
- /* Build structure. */
- assemble_variable (gcov_info, 0, 0, 0);
-
- /* Build the constructor function to invoke __gcov_init. */
- ctor_name = concat (IDENTIFIER_POINTER (get_file_function_name ('I')),
- "_GCOV", NULL);
- ctor = build_decl (FUNCTION_DECL, get_identifier (ctor_name),
- build_function_type (void_type_node, NULL_TREE));
- free (ctor_name);
- DECL_EXTERNAL (ctor) = 0;
-
- /* It can be a static function as long as collect2 does not have
- to scan the object file to find its ctor/dtor routine. */
- TREE_PUBLIC (ctor) = ! targetm.have_ctors_dtors;
- TREE_USED (ctor) = 1;
- DECL_RESULT (ctor) = build_decl (RESULT_DECL, NULL_TREE, void_type_node);
-
- ctor = (*lang_hooks.decls.pushdecl) (ctor);
- rest_of_decl_compilation (ctor, 0, 1, 0);
- announce_function (ctor);
- current_function_decl = ctor;
- DECL_INITIAL (ctor) = error_mark_node;
- make_decl_rtl (ctor, NULL);
- init_function_start (ctor, input_filename, lineno);
- (*lang_hooks.decls.pushlevel) (0);
- expand_function_start (ctor, 0);
- cfun->arc_profile = 0;
-
- /* Actually generate the code to call __gcov_init. */
- gcov_info_address = force_reg (Pmode, XEXP (DECL_RTL (gcov_info), 0));
- emit_library_call (gcov_init_libfunc, LCT_NORMAL, VOIDmode, 1,
- gcov_info_address, Pmode);
-
- expand_function_end (input_filename, lineno, 0);
- (*lang_hooks.decls.poplevel) (1, 0, 1);
-
- /* Since ctor isn't in the list of globals, it would never be emitted
- when it's considered to be 'safe' for inlining, so turn off
- flag_inline_functions. */
- flag_inline_functions = 0;
-
- rest_of_compilation (ctor);
-
- /* Reset flag_inline_functions to its original value. */
- flag_inline_functions = save_flag_inline_functions;
-
- if (! quiet_flag)
- fflush (asm_out_file);
- current_function_decl = NULL_TREE;
-
- if (targetm.have_ctors_dtors)
- (* targetm.asm_out.constructor) (XEXP (DECL_RTL (ctor), 0),
- DEFAULT_INIT_PRIORITY);
-}
-\f
-/* Output instructions as RTL to increment the edge execution count. */
-
-static rtx
-gen_edge_profiler (edgeno)
- int edgeno;
+rtl_register_profile_hooks (void)
{
- enum machine_mode mode = mode_for_size (GCOV_TYPE_SIZE, MODE_INT, 0);
- rtx mem_ref, tmp;
- rtx sequence;
-
- start_sequence ();
-
- tmp = force_reg (Pmode, profiler_label);
- tmp = plus_constant (tmp, GCOV_TYPE_SIZE / BITS_PER_UNIT * edgeno);
- mem_ref = validize_mem (gen_rtx_MEM (mode, tmp));
-
- set_mem_alias_set (mem_ref, new_alias_set ());
-
- tmp = expand_simple_binop (mode, PLUS, mem_ref, const1_rtx,
- mem_ref, 0, OPTAB_WIDEN);
-
- if (tmp != mem_ref)
- emit_move_insn (copy_rtx (mem_ref), tmp);
-
- sequence = get_insns ();
- end_sequence ();
- return sequence;
+ profile_hooks = &rtl_profile_hooks;
+ if (ir_type ())
+ abort ();
}
-
-#include "gt-profile.h"