/* Read and write coverage files, and associated functionality.
Copyright (C) 1990, 1991, 1992, 1993, 1994, 1996, 1997, 1998, 1999,
- 2000, 2001, 2003 Free Software Foundation, Inc.
+ 2000, 2001, 2003, 2004, 2005, 2007, 2008, 2009, 2010, 2011, 2012
+ 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.
GCC is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
-Software Foundation; either version 2, or (at your option) any later
+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
for more details.
You should have received a copy of the GNU General Public License
-along with GCC; see the file COPYING. If not, write to the Free
-Software Foundation, 59 Temple Place - Suite 330, Boston, MA
-02111-1307, USA. */
+along with GCC; see the file COPYING3. If not see
+<http://www.gnu.org/licenses/>. */
#define GCOV_LINKAGE
#include "regs.h"
#include "expr.h"
#include "function.h"
+#include "basic-block.h"
#include "toplev.h"
+#include "tm_p.h"
#include "ggc.h"
-#include "target.h"
#include "coverage.h"
-#include "libfuncs.h"
#include "langhooks.h"
#include "hashtab.h"
+#include "tree-iterator.h"
+#include "cgraph.h"
+#include "tree-pass.h"
+#include "diagnostic-core.h"
+#include "intl.h"
+#include "filenames.h"
+#include "target.h"
+#include "gcov-io.h"
#include "gcov-io.c"
-struct function_list
+struct GTY((chain_next ("%h.next"))) coverage_data
{
- 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 gcov_counter_section counter_sections[MAX_COUNTER_SECTIONS];
- /* the sections */
+ struct coverage_data *next; /* next function */
+ unsigned ident; /* function ident */
+ unsigned lineno_checksum; /* function lineno checksum */
+ unsigned cfg_checksum; /* function cfg checksum */
+ tree fn_decl; /* the function decl */
+ tree ctr_vars[GCOV_COUNTERS]; /* counter variables. */
};
/* Counts information for a function. */
typedef struct counts_entry
{
/* We hash by */
- char *function_name;
- unsigned section;
-
+ unsigned ident;
+ unsigned ctr;
+
/* Store */
- unsigned checksum;
- unsigned n_counts;
+ unsigned lineno_checksum;
+ unsigned cfg_checksum;
gcov_type *counts;
- unsigned merged;
- gcov_type max_counter;
- gcov_type max_counter_sum;
-
- /* Workspace */
- struct counts_entry *chain;
-
+ struct gcov_ctr_summary summary;
} counts_entry_t;
-static struct function_list *functions_head = 0;
-static struct function_list **functions_tail = &functions_head;
+static GTY(()) struct coverage_data *functions_head = 0;
+static struct coverage_data **functions_tail = &functions_head;
+static unsigned no_coverage = 0;
+
+/* Cumulative counter information for whole program. */
+static unsigned prg_ctr_mask; /* Mask of counter types generated. */
-/* Instantiate the profile info structure. */
+/* Counter information for current function. */
+static unsigned fn_ctr_mask; /* Mask of counters used. */
+static GTY(()) tree fn_v_ctrs[GCOV_COUNTERS]; /* counter variables. */
+static unsigned fn_n_ctrs[GCOV_COUNTERS]; /* Counters allocated. */
+static unsigned fn_b_ctrs[GCOV_COUNTERS]; /* Allocation base. */
-struct profile_info profile_info;
+/* Coverage info VAR_DECL and function info type nodes. */
+static GTY(()) tree gcov_info_var;
+static GTY(()) tree gcov_fn_info_type;
+static GTY(()) tree gcov_fn_info_ptr_type;
-/* Name of the output file for coverage output file. */
+/* Name of the output file for coverage output file. If this is NULL
+ we're not writing to the notes file. */
static char *bbg_file_name;
-static unsigned bbg_file_opened;
-static int bbg_function_announced;
/* Name of the count data file. */
static char *da_file_name;
/* Hash table of count data. */
static htab_t counts_hash = NULL;
-/* The name of the count table. Used by the edge profiling code. */
-static GTY(()) rtx profiler_label;
+/* The names of merge functions for counters. */
+static const char *const ctr_merge_functions[GCOV_COUNTERS] = GCOV_MERGE_FUNCTIONS;
+static const char *const ctr_names[GCOV_COUNTERS] = GCOV_COUNTER_NAMES;
/* Forward declarations. */
-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 ((void));
-static unsigned compute_checksum PARAMS ((void));
-static unsigned checksum_string PARAMS ((unsigned, const char *));
-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 create_coverage PARAMS ((void));
+static hashval_t htab_counts_entry_hash (const void *);
+static int htab_counts_entry_eq (const void *, const void *);
+static void htab_counts_entry_del (void *);
+static void read_counts_file (void);
+static tree build_var (tree, tree, int);
+static void build_fn_info_type (tree, unsigned, tree);
+static void build_info_type (tree, tree);
+static tree build_fn_info (const struct coverage_data *, tree, tree);
+static tree build_info (tree, tree);
+static bool coverage_obj_init (void);
+static VEC(constructor_elt,gc) *coverage_obj_fn
+(VEC(constructor_elt,gc) *, tree, struct coverage_data const *);
+static void coverage_obj_finish (VEC(constructor_elt,gc) *);
+\f
+/* Return the type node for gcov_type. */
+tree
+get_gcov_type (void)
+{
+ return lang_hooks.types.type_for_size (GCOV_TYPE_SIZE, false);
+}
+
+/* Return the type node for gcov_unsigned_t. */
+
+static tree
+get_gcov_unsigned_t (void)
+{
+ return lang_hooks.types.type_for_size (32, true);
+}
\f
static hashval_t
-htab_counts_entry_hash (of)
- const void *of;
+htab_counts_entry_hash (const void *of)
{
- const counts_entry_t *entry = of;
+ const counts_entry_t *const entry = (const counts_entry_t *) of;
- return htab_hash_string (entry->function_name) ^ entry->section;
+ return entry->ident * GCOV_COUNTERS + entry->ctr;
}
static int
-htab_counts_entry_eq (of1, of2)
- const void *of1;
- const void *of2;
+htab_counts_entry_eq (const void *of1, const void *of2)
{
- const counts_entry_t *entry1 = of1;
- const counts_entry_t *entry2 = of2;
+ const counts_entry_t *const entry1 = (const counts_entry_t *) of1;
+ const counts_entry_t *const entry2 = (const counts_entry_t *) of2;
- return !strcmp (entry1->function_name, entry2->function_name)
- && entry1->section == entry2->section;
+ return entry1->ident == entry2->ident && entry1->ctr == entry2->ctr;
}
static void
-htab_counts_entry_del (of)
- void *of;
+htab_counts_entry_del (void *of)
{
- counts_entry_t *entry = of;
+ counts_entry_t *const entry = (counts_entry_t *) of;
- free (entry->function_name);
free (entry->counts);
free (entry);
}
/* Read in the counts file, if available. */
static void
-read_counts_file ()
+read_counts_file (void)
{
- char *function_name_buffer = NULL;
- unsigned version, ix, checksum = -1;
- counts_entry_t *summaried = NULL;
- unsigned seen_summary = 0;
-
+ gcov_unsigned_t fn_ident = 0;
+ struct gcov_summary summary;
+ unsigned new_summary = 1;
+ gcov_unsigned_t tag;
+ int is_error = 0;
+ unsigned lineno_checksum = 0;
+ unsigned cfg_checksum = 0;
+
if (!gcov_open (da_file_name, 1))
return;
-
- if (gcov_read_unsigned () != GCOV_DATA_MAGIC)
+
+ if (!gcov_magic (gcov_read_unsigned (), GCOV_DATA_MAGIC))
{
- warning ("`%s' is not a gcov data file", da_file_name);
+ warning (0, "%qs is not a gcov data file", da_file_name);
gcov_close ();
return;
}
- else if ((version = gcov_read_unsigned ()) != GCOV_VERSION)
+ else if ((tag = gcov_read_unsigned ()) != GCOV_VERSION)
{
char v[4], e[4];
- unsigned required = GCOV_VERSION;
-
- for (ix = 4; ix--; required >>= 8, version >>= 8)
- {
- v[ix] = version;
- e[ix] = required;
- }
- warning ("`%s' is version `%.4s', expected version `%.4s'",
- da_file_name, v, e);
+
+ GCOV_UNSIGNED2STRING (v, tag);
+ GCOV_UNSIGNED2STRING (e, GCOV_VERSION);
+
+ warning (0, "%qs is version %q.*s, expected version %q.*s",
+ da_file_name, 4, v, 4, e);
gcov_close ();
return;
}
-
+
+ /* Read and discard the stamp. */
+ gcov_read_unsigned ();
+
counts_hash = htab_create (10,
htab_counts_entry_hash, htab_counts_entry_eq,
htab_counts_entry_del);
- while (!gcov_is_eof ())
+ while ((tag = gcov_read_unsigned ()))
{
- unsigned tag, length;
- unsigned long offset;
- int error;
-
- tag = gcov_read_unsigned ();
+ gcov_unsigned_t length;
+ gcov_position_t offset;
+
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)
+ if (length)
{
- /* 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;
+ fn_ident = gcov_read_unsigned ();
+ lineno_checksum = gcov_read_unsigned ();
+ cfg_checksum = gcov_read_unsigned ();
}
+ else
+ fn_ident = lineno_checksum = cfg_checksum = 0;
+ new_summary = 1;
}
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)
+ struct gcov_summary sum;
+ unsigned ix;
+
+ if (new_summary)
+ memset (&summary, 0, sizeof (summary));
+
+ gcov_read_summary (&sum);
+ for (ix = 0; ix != GCOV_COUNTERS_SUMMABLE; ix++)
{
- entry->merged += summary.runs;
- if (entry->max_counter < summary.arc_sum_max)
- entry->max_counter = summary.arc_sum_max;
+ summary.ctrs[ix].runs += sum.ctrs[ix].runs;
+ summary.ctrs[ix].sum_all += sum.ctrs[ix].sum_all;
+ if (summary.ctrs[ix].run_max < sum.ctrs[ix].run_max)
+ summary.ctrs[ix].run_max = sum.ctrs[ix].run_max;
+ summary.ctrs[ix].sum_max += sum.ctrs[ix].sum_max;
}
+ new_summary = 0;
}
- else if (GCOV_TAG_IS_SUBTAG (GCOV_TAG_FUNCTION, tag)
- && function_name_buffer)
+ else if (GCOV_TAG_IS_COUNTER (tag) && fn_ident)
{
counts_entry_t **slot, *entry, elt;
- unsigned n_counts = length / 8;
+ unsigned n_counts = GCOV_TAG_COUNTER_NUM (length);
unsigned ix;
- elt.function_name = function_name_buffer;
- elt.section = tag;
+ elt.ident = fn_ident;
+ elt.ctr = GCOV_COUNTER_FOR_TAG (tag);
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));
+ *slot = entry = XCNEW (counts_entry_t);
+ entry->ident = fn_ident;
+ entry->ctr = elt.ctr;
+ entry->lineno_checksum = lineno_checksum;
+ entry->cfg_checksum = cfg_checksum;
+ entry->summary = summary.ctrs[elt.ctr];
+ entry->summary.num = n_counts;
+ entry->counts = XCNEWVEC (gcov_type, n_counts);
}
- else if (entry->checksum != checksum || entry->n_counts != n_counts)
+ else if (entry->lineno_checksum != lineno_checksum
+ || entry->cfg_checksum != cfg_checksum)
{
- warning ("profile mismatch for `%s'", function_name_buffer);
+ error ("Profile data for function %u is corrupted", fn_ident);
+ error ("checksum is (%x,%x) instead of (%x,%x)",
+ entry->lineno_checksum, entry->cfg_checksum,
+ lineno_checksum, cfg_checksum);
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)
+ else if (entry->summary.num != n_counts)
+ {
+ error ("Profile data for function %u is corrupted", fn_ident);
+ error ("number of counters is %d instead of %d", entry->summary.num, n_counts);
+ htab_delete (counts_hash);
+ break;
+ }
+ else if (elt.ctr >= GCOV_COUNTERS_SUMMABLE)
+ {
+ error ("cannot merge separate %s counters for function %u",
+ ctr_names[elt.ctr], fn_ident);
+ goto skip_merge;
+ }
+ else
{
- entry->chain = summaried;
- summaried = entry;
+ entry->summary.runs += summary.ctrs[elt.ctr].runs;
+ entry->summary.sum_all += summary.ctrs[elt.ctr].sum_all;
+ if (entry->summary.run_max < summary.ctrs[elt.ctr].run_max)
+ entry->summary.run_max = summary.ctrs[elt.ctr].run_max;
+ entry->summary.sum_max += summary.ctrs[elt.ctr].sum_max;
}
for (ix = 0; ix != n_counts; ix++)
entry->counts[ix] += gcov_read_counter ();
+ skip_merge:;
}
- gcov_seek (offset, length);
- if ((error = gcov_is_error ()))
+ gcov_sync (offset, length);
+ if ((is_error = gcov_is_error ()))
{
- warning (error < 0 ? "`%s' has overflowed" : "`%s' is corrupted",
- da_file_name);
+ error (is_error < 0 ? "%qs has overflowed" : "%qs is corrupted",
+ da_file_name);
htab_delete (counts_hash);
break;
}
}
- free (function_name_buffer);
gcov_close ();
}
/* Returns the counters for a particular tag. */
gcov_type *
-get_coverage_counts (unsigned tag, unsigned expected)
+get_coverage_counts (unsigned counter, unsigned expected,
+ unsigned cfg_checksum, unsigned lineno_checksum,
+ const struct gcov_ctr_summary **summary)
{
counts_entry_t *entry, elt;
- profile_info.max_counter_in_program = 0;
- profile_info.count_profiles_merged = 0;
-
- /* No hash table, no counts. */
+ /* No hash table, no counts. */
if (!counts_hash)
{
static int warned = 0;
if (!warned++)
- warning ("file %s not found, execution counts assumed to be zero",
- da_file_name);
+ inform (input_location, (flag_guess_branch_prob
+ ? "file %s not found, execution counts estimated"
+ : "file %s not found, execution counts assumed to be zero"),
+ da_file_name);
return NULL;
}
- elt.function_name
- = (char *) IDENTIFIER_POINTER
- (DECL_ASSEMBLER_NAME (current_function_decl));
- elt.section = tag;
- entry = htab_find (counts_hash, &elt);
- if (!entry)
- {
- warning ("No profile for function '%s' found.", elt.function_name);
- return 0;
- }
+ elt.ident = current_function_funcdef_no + 1;
+ elt.ctr = counter;
+ entry = (counts_entry_t *) htab_find (counts_hash, &elt);
+ if (!entry || !entry->summary.num)
+ /* The function was not emitted, or is weak and not chosen in the
+ final executable. Silently fail, because there's nothing we
+ can do about it. */
+ return NULL;
- if (expected != entry->n_counts
- || compute_checksum () != entry->checksum)
+ if (entry->cfg_checksum != cfg_checksum
+ || entry->summary.num != expected)
{
- warning ("profile mismatch for `%s'", elt.function_name);
+ static int warned = 0;
+ bool warning_printed = false;
+ tree id = DECL_ASSEMBLER_NAME (current_function_decl);
+
+ warning_printed =
+ warning_at (input_location, OPT_Wcoverage_mismatch,
+ "the control flow of function %qE does not match "
+ "its profile data (counter %qs)", id, ctr_names[counter]);
+ if (warning_printed)
+ {
+ inform (input_location, "use -Wno-error=coverage-mismatch to tolerate "
+ "the mismatch but performance may drop if the function is hot");
+
+ if (!seen_error ()
+ && !warned++)
+ {
+ inform (input_location, "coverage mismatch ignored");
+ inform (input_location, flag_guess_branch_prob
+ ? G_("execution counts estimated")
+ : G_("execution counts assumed to be zero"));
+ if (!flag_guess_branch_prob)
+ inform (input_location,
+ "this can result in poorly optimized code");
+ }
+ }
+
return NULL;
}
+ else if (entry->lineno_checksum != lineno_checksum)
+ {
+ warning (0, "source locations for function %qE have changed,"
+ " the profile data may be out of date",
+ DECL_ASSEMBLER_NAME (current_function_decl));
+ }
- profile_info.count_profiles_merged = entry->merged;
- profile_info.max_counter_in_program = entry->max_counter_sum;
+ if (summary)
+ *summary = &entry->summary;
return entry->counts;
}
+
+/* Allocate NUM counters of type COUNTER. Returns nonzero if the
+ allocation succeeded. */
+
+int
+coverage_counter_alloc (unsigned counter, unsigned num)
+{
+ if (no_coverage)
+ return 0;
+
+ if (!num)
+ return 1;
+
+ if (!fn_v_ctrs[counter])
+ {
+ tree array_type = build_array_type (get_gcov_type (), NULL_TREE);
+
+ fn_v_ctrs[counter]
+ = build_var (current_function_decl, array_type, counter);
+ }
+
+ fn_b_ctrs[counter] = fn_n_ctrs[counter];
+ fn_n_ctrs[counter] += num;
+
+ fn_ctr_mask |= 1 << counter;
+ return 1;
+}
+
+/* Generate a tree to access COUNTER NO. */
+
+tree
+tree_coverage_counter_ref (unsigned counter, unsigned no)
+{
+ tree gcov_type_node = get_gcov_type ();
+
+ gcc_assert (no < fn_n_ctrs[counter] - fn_b_ctrs[counter]);
+
+ no += fn_b_ctrs[counter];
+
+ /* "no" here is an array index, scaled to bytes later. */
+ return build4 (ARRAY_REF, gcov_type_node, fn_v_ctrs[counter],
+ build_int_cst (integer_type_node, no), NULL, NULL);
+}
+
+/* Generate a tree to access the address of COUNTER NO. */
+
+tree
+tree_coverage_counter_addr (unsigned counter, unsigned no)
+{
+ tree gcov_type_node = get_gcov_type ();
+
+ gcc_assert (no < fn_n_ctrs[counter] - fn_b_ctrs[counter]);
+ no += fn_b_ctrs[counter];
+
+ /* "no" here is an array index, scaled to bytes later. */
+ return build_fold_addr_expr (build4 (ARRAY_REF, gcov_type_node,
+ fn_v_ctrs[counter],
+ build_int_cst (integer_type_node, no),
+ NULL, NULL));
+}
\f
+
/* Generate a checksum for a string. CHKSUM is the current
- checksum. */
+ checksum. */
static unsigned
-checksum_string (unsigned chksum, const char *string)
+coverage_checksum_string (unsigned chksum, const char *string)
{
- do
+ int i;
+ char *dup = NULL;
+
+ /* Look for everything that looks if it were produced by
+ get_file_function_name and zero out the second part
+ that may result from flag_random_seed. This is not critical
+ as the checksums are used only for sanity checking. */
+ for (i = 0; string[i]; i++)
{
- unsigned value = *string << 24;
- unsigned ix;
-
- for (ix = 8; ix--; value <<= 1)
+ int offset = 0;
+ if (!strncmp (string + i, "_GLOBAL__N_", 11))
+ offset = 11;
+ if (!strncmp (string + i, "_GLOBAL__", 9))
+ offset = 9;
+
+ /* C++ namespaces do have scheme:
+ _GLOBAL__N_<filename>_<wrongmagicnumber>_<magicnumber>functionname
+ since filename might contain extra underscores there seems
+ to be no better chance then walk all possible offsets looking
+ for magicnumber. */
+ if (offset)
{
- unsigned feedback;
-
- feedback = (value ^ chksum) & 0x80000000 ? 0x04c11db7 : 0;
- chksum <<= 1;
- chksum ^= feedback;
+ for (i = i + offset; string[i]; i++)
+ if (string[i]=='_')
+ {
+ int y;
+
+ for (y = 1; y < 9; y++)
+ if (!(string[i + y] >= '0' && string[i + y] <= '9')
+ && !(string[i + y] >= 'A' && string[i + y] <= 'F'))
+ break;
+ if (y != 9 || string[i + 9] != '_')
+ continue;
+ for (y = 10; y < 18; y++)
+ if (!(string[i + y] >= '0' && string[i + y] <= '9')
+ && !(string[i + y] >= 'A' && string[i + y] <= 'F'))
+ break;
+ if (y != 18)
+ continue;
+ if (!dup)
+ string = dup = xstrdup (string);
+ for (y = 10; y < 18; y++)
+ dup[i + y] = '0';
+ }
+ break;
}
}
- while (*string++);
-
+
+ chksum = crc32_string (chksum, string);
+ free (dup);
+
return chksum;
}
/* Compute checksum for the current function. We generate a CRC32. */
-static unsigned
-compute_checksum ()
+unsigned
+coverage_compute_lineno_checksum (void)
{
- unsigned chksum = DECL_SOURCE_LINE (current_function_decl);
+ expanded_location xloc
+ = expand_location (DECL_SOURCE_LOCATION (current_function_decl));
+ unsigned chksum = xloc.line;
- chksum = checksum_string (chksum, DECL_SOURCE_FILE (current_function_decl));
- chksum = checksum_string
+ chksum = coverage_checksum_string (chksum, xloc.file);
+ chksum = coverage_checksum_string
(chksum, IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (current_function_decl)));
return chksum;
}
+
+/* Compute cfg checksum for the current function.
+ The checksum is calculated carefully so that
+ source code changes that doesn't affect the control flow graph
+ won't change the checksum.
+ This is to make the profile data useable across source code change.
+ The downside of this is that the compiler may use potentially
+ wrong profile data - that the source code change has non-trivial impact
+ on the validity of profile data (e.g. the reversed condition)
+ but the compiler won't detect the change and use the wrong profile data. */
+
+unsigned
+coverage_compute_cfg_checksum (void)
+{
+ basic_block bb;
+ unsigned chksum = n_basic_blocks;
+
+ FOR_EACH_BB (bb)
+ {
+ edge e;
+ edge_iterator ei;
+ chksum = crc32_byte (chksum, bb->index);
+ FOR_EACH_EDGE (e, ei, bb->succs)
+ {
+ chksum = crc32_byte (chksum, e->dest->index);
+ }
+ }
+
+ return chksum;
+}
\f
/* Begin output to the graph file for the current function.
- Opens the output file, if not already done. Writes the
- function header, if not already done. Returns non-zero if data
- should be output. */
+ Writes the function header. Returns nonzero if data should be output. */
int
-coverage_begin_output ()
+coverage_begin_function (unsigned lineno_checksum, unsigned cfg_checksum)
{
- if (!bbg_function_announced)
- {
- const char *file = DECL_SOURCE_FILE (current_function_decl);
- unsigned line = DECL_SOURCE_LINE (current_function_decl);
- unsigned long offset;
-
- if (!bbg_file_opened)
- {
- if (!gcov_open (bbg_file_name, -1))
- error ("cannot open %s", bbg_file_name);
- else
- {
- gcov_write_unsigned (GCOV_GRAPH_MAGIC);
- gcov_write_unsigned (GCOV_VERSION);
- }
- bbg_file_opened = 1;
- }
-
- /* Announce function */
- offset = gcov_write_tag (GCOV_TAG_FUNCTION);
- gcov_write_string (IDENTIFIER_POINTER
- (DECL_ASSEMBLER_NAME (current_function_decl)));
- gcov_write_unsigned (compute_checksum ());
- gcov_write_string (file);
- gcov_write_unsigned (line);
- gcov_write_length (offset);
-
- bbg_function_announced = 1;
- }
+ expanded_location xloc;
+ unsigned long offset;
+
+ /* We don't need to output .gcno file unless we're under -ftest-coverage
+ (e.g. -fprofile-arcs/generate/use don't need .gcno to work). */
+ if (no_coverage || !bbg_file_name)
+ return 0;
+
+ xloc = expand_location (DECL_SOURCE_LOCATION (current_function_decl));
+
+ /* Announce function */
+ offset = gcov_write_tag (GCOV_TAG_FUNCTION);
+ gcov_write_unsigned (current_function_funcdef_no + 1);
+ gcov_write_unsigned (lineno_checksum);
+ gcov_write_unsigned (cfg_checksum);
+ gcov_write_string (IDENTIFIER_POINTER
+ (DECL_ASSEMBLER_NAME (current_function_decl)));
+ gcov_write_string (xloc.file);
+ gcov_write_unsigned (xloc.line);
+ gcov_write_length (offset);
+
return !gcov_is_error ();
}
error has occurred. Save function coverage counts. */
void
-coverage_end_function ()
+coverage_end_function (unsigned lineno_checksum, unsigned cfg_checksum)
{
unsigned i;
-
- if (bbg_file_opened > 1 && gcov_is_error ())
- {
- warning ("error writing `%s'", bbg_file_name);
- bbg_file_opened = -1;
- }
-
- for (i = 0; i != profile_info.n_sections; i++)
- if (profile_info.section_info[i].n_counters_now)
- {
- struct function_list *item;
-
- /* ??? 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 (IDENTIFIER_POINTER
- (DECL_ASSEMBLER_NAME (current_function_decl)));
- item->cfg_checksum = compute_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++;
- profile_info.section_info[i].n_counters
- += profile_info.section_info[i].n_counters_now;
- profile_info.section_info[i].n_counters_now = 0;
- }
- break;
- }
- bbg_function_announced = 0;
-}
-
-/* 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;
-}
-/* Returns label for base of counters inside TAG section. */
-static rtx
-label_for_tag (tag)
- unsigned tag;
-{
- switch (tag)
+ if (bbg_file_name && gcov_is_error ())
{
- case GCOV_TAG_ARC_COUNTS:
- return profiler_label;
- default:
- abort ();
+ warning (0, "error writing %qs", bbg_file_name);
+ unlink (bbg_file_name);
+ bbg_file_name = NULL;
}
-}
-/* Creates fields of struct counter_section (in gcov-io.h). */
-static tree
-build_counter_section_fields ()
-{
- tree field, fields;
+ if (fn_ctr_mask)
+ {
+ struct coverage_data *item = 0;
- /* tag */
- fields = build_decl (FIELD_DECL, NULL_TREE, unsigned_type_node);
+ /* If the function is extern (i.e. extern inline), then we won't
+ be outputting it, so don't chain it onto the function
+ list. */
+ if (!DECL_EXTERNAL (current_function_decl))
+ {
+ item = ggc_alloc_coverage_data ();
+
+ item->ident = current_function_funcdef_no + 1;
+ item->lineno_checksum = lineno_checksum;
+ item->cfg_checksum = cfg_checksum;
+
+ item->fn_decl = current_function_decl;
+ item->next = 0;
+ *functions_tail = item;
+ functions_tail = &item->next;
+ }
- /* n_counters */
- field = build_decl (FIELD_DECL, NULL_TREE, unsigned_type_node);
- TREE_CHAIN (field) = fields;
- fields = field;
+ for (i = 0; i != GCOV_COUNTERS; i++)
+ {
+ tree var = fn_v_ctrs[i];
- return fields;
+ if (item)
+ item->ctr_vars[i] = var;
+ if (var)
+ {
+ tree array_type = build_index_type (size_int (fn_n_ctrs[i] - 1));
+ array_type = build_array_type (get_gcov_type (), array_type);
+ TREE_TYPE (var) = array_type;
+ DECL_SIZE (var) = TYPE_SIZE (array_type);
+ DECL_SIZE_UNIT (var) = TYPE_SIZE_UNIT (array_type);
+ varpool_finalize_decl (var);
+ }
+
+ fn_b_ctrs[i] = fn_n_ctrs[i] = 0;
+ fn_v_ctrs[i] = NULL_TREE;
+ }
+ prg_ctr_mask |= fn_ctr_mask;
+ fn_ctr_mask = 0;
+ }
}
-/* Creates value of struct counter_section (in gcov-io.h). */
+/* Build a coverage variable of TYPE for function FN_DECL. If COUNTER
+ >= 0 it is a counter array, otherwise it is the function structure. */
+
static tree
-build_counter_section_value (tag, n_counters)
- unsigned tag;
- unsigned n_counters;
+build_var (tree fn_decl, tree type, int counter)
{
- tree value = NULL_TREE;
+ tree var = build_decl (BUILTINS_LOCATION, VAR_DECL, NULL_TREE, type);
+ const char *fn_name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (fn_decl));
+ char *buf;
+ size_t fn_name_len, len;
- /* 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);
+ fn_name = targetm.strip_name_encoding (fn_name);
+ fn_name_len = strlen (fn_name);
+ buf = XALLOCAVEC (char, fn_name_len + 8 + sizeof (int) * 3);
+
+ if (counter < 0)
+ strcpy (buf, "__gcov__");
+ else
+ sprintf (buf, "__gcov%u_", counter);
+ len = strlen (buf);
+#ifndef NO_DOT_IN_LABEL
+ buf[len - 1] = '.';
+#elif !defined NO_DOLLAR_IN_LABEL
+ buf[len - 1] = '$';
+#endif
+ memcpy (buf + len, fn_name, fn_name_len + 1);
+ DECL_NAME (var) = get_identifier (buf);
+ TREE_STATIC (var) = 1;
+ TREE_ADDRESSABLE (var) = 1;
+ DECL_ALIGN (var) = TYPE_ALIGN (type);
- return value;
+ return var;
}
-/* Creates fields of struct counter_section_data (in gcov-io.h). */
-static tree
-build_counter_section_data_fields ()
+/* Creates the gcov_fn_info RECORD_TYPE. */
+
+static void
+build_fn_info_type (tree type, unsigned counters, tree gcov_info_type)
{
- tree field, fields, gcov_type, gcov_ptr_type;
+ tree ctr_info = lang_hooks.types.make_type (RECORD_TYPE);
+ tree field, fields;
+ tree array_type;
- gcov_type = make_signed_type (GCOV_TYPE_SIZE);
- gcov_ptr_type =
- build_pointer_type (build_qualified_type (gcov_type,
- TYPE_QUAL_CONST));
+ gcc_assert (counters);
+
+ /* ctr_info::num */
+ field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE,
+ get_gcov_unsigned_t ());
+ fields = field;
+
+ /* ctr_info::values */
+ field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE,
+ build_pointer_type (get_gcov_type ()));
+ DECL_CHAIN (field) = fields;
+ fields = field;
+
+ finish_builtin_struct (ctr_info, "__gcov_ctr_info", fields, NULL_TREE);
- /* tag */
- fields = build_decl (FIELD_DECL, NULL_TREE, unsigned_type_node);
+ /* key */
+ field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE,
+ build_pointer_type (build_qualified_type
+ (gcov_info_type, TYPE_QUAL_CONST)));
+ fields = field;
+
+ /* ident */
+ field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE,
+ get_gcov_unsigned_t ());
+ DECL_CHAIN (field) = fields;
+ fields = field;
+
+ /* lineno_checksum */
+ field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE,
+ get_gcov_unsigned_t ());
+ DECL_CHAIN (field) = fields;
+ fields = field;
- /* n_counters */
- field = build_decl (FIELD_DECL, NULL_TREE, unsigned_type_node);
- TREE_CHAIN (field) = fields;
+ /* cfg checksum */
+ field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE,
+ get_gcov_unsigned_t ());
+ DECL_CHAIN (field) = fields;
fields = field;
+ array_type = build_index_type (size_int (counters - 1));
+ array_type = build_array_type (ctr_info, array_type);
+
/* counters */
- field = build_decl (FIELD_DECL, NULL_TREE, gcov_ptr_type);
- TREE_CHAIN (field) = fields;
+ field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE, array_type);
+ DECL_CHAIN (field) = fields;
fields = field;
- return fields;
+ finish_builtin_struct (type, "__gcov_fn_info", fields, NULL_TREE);
}
-/* Creates value of struct counter_section_data (in gcov-io.h). */
+/* Returns a CONSTRUCTOR for a gcov_fn_info. DATA is
+ the coverage data for the function and TYPE is the gcov_fn_info
+ RECORD_TYPE. KEY is the object file key. */
+
static tree
-build_counter_section_data_value (tag, n_counters)
- unsigned tag;
- unsigned n_counters;
+build_fn_info (const struct coverage_data *data, tree type, tree key)
{
- 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);
+ tree fields = TYPE_FIELDS (type);
+ tree ctr_type;
+ unsigned ix;
+ VEC(constructor_elt,gc) *v1 = NULL;
+ VEC(constructor_elt,gc) *v2 = NULL;
+
+ /* key */
+ CONSTRUCTOR_APPEND_ELT (v1, fields,
+ build1 (ADDR_EXPR, TREE_TYPE (fields), key));
+ fields = DECL_CHAIN (fields);
- /* n_counters */
- value = tree_cons (NULL_TREE,
- convert (unsigned_type_node,
- build_int_2 (n_counters, 0)),
- value);
+ /* ident */
+ CONSTRUCTOR_APPEND_ELT (v1, fields,
+ build_int_cstu (get_gcov_unsigned_t (),
+ data->ident));
+ fields = DECL_CHAIN (fields);
+
+ /* lineno_checksum */
+ CONSTRUCTOR_APPEND_ELT (v1, fields,
+ build_int_cstu (get_gcov_unsigned_t (),
+ data->lineno_checksum));
+ fields = DECL_CHAIN (fields);
+
+ /* cfg_checksum */
+ CONSTRUCTOR_APPEND_ELT (v1, fields,
+ build_int_cstu (get_gcov_unsigned_t (),
+ data->cfg_checksum));
+ fields = DECL_CHAIN (fields);
/* 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);
+ ctr_type = TREE_TYPE (TREE_TYPE (fields));
+ for (ix = 0; ix != GCOV_COUNTERS; ix++)
+ if (prg_ctr_mask & (1 << ix))
+ {
+ VEC(constructor_elt,gc) *ctr = NULL;
+ tree var = data->ctr_vars[ix];
+ unsigned count = 0;
+
+ if (var)
+ count
+ = tree_low_cst (TYPE_MAX_VALUE (TYPE_DOMAIN (TREE_TYPE (var))), 0)
+ + 1;
+
+ CONSTRUCTOR_APPEND_ELT (ctr, TYPE_FIELDS (ctr_type),
+ build_int_cstu (get_gcov_unsigned_t (),
+ count));
+
+ if (var)
+ CONSTRUCTOR_APPEND_ELT (ctr, DECL_CHAIN (TYPE_FIELDS (ctr_type)),
+ build_fold_addr_expr (var));
+
+ CONSTRUCTOR_APPEND_ELT (v2, NULL, build_constructor (ctr_type, ctr));
+ }
+
+ CONSTRUCTOR_APPEND_ELT (v1, fields,
+ build_constructor (TREE_TYPE (fields), v2));
- return value;
+ return build_constructor (type, v1);
}
-/* Creates fields for struct function_info type (in gcov-io.h). */
-static tree
-build_function_info_fields ()
+/* Create gcov_info struct. TYPE is the incomplete RECORD_TYPE to be
+ completed, and FN_INFO_PTR_TYPE is a pointer to the function info type. */
+
+static void
+build_info_type (tree type, tree fn_info_ptr_type)
{
- 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;
+ tree field, fields = NULL_TREE;
+ tree merge_fn_type;
- /* n_counter_sections */
- field = build_decl (FIELD_DECL, NULL_TREE, unsigned_type_node);
- TREE_CHAIN (field) = fields;
+ /* Version ident */
+ field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE,
+ get_gcov_unsigned_t ());
+ DECL_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;
+ /* next pointer */
+ field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE,
+ build_pointer_type (build_qualified_type
+ (type, TYPE_QUAL_CONST)));
+ DECL_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;
+ /* stamp */
+ field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE,
+ get_gcov_unsigned_t ());
+ DECL_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;
+ field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE,
+ build_pointer_type (build_qualified_type
+ (char_type_node, TYPE_QUAL_CONST)));
+ DECL_CHAIN (field) = fields;
fields = field;
- /* number of functions */
- field = build_decl (FIELD_DECL, NULL_TREE, unsigned_type_node);
- TREE_CHAIN (field) = fields;
+ /* merge fn array */
+ merge_fn_type
+ = build_function_type_list (void_type_node,
+ build_pointer_type (get_gcov_type ()),
+ get_gcov_unsigned_t (), NULL_TREE);
+ merge_fn_type
+ = build_array_type (build_pointer_type (merge_fn_type),
+ build_index_type (size_int (GCOV_COUNTERS - 1)));
+ field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE,
+ merge_fn_type);
+ DECL_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;
+
+ /* n_functions */
+ field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE,
+ get_gcov_unsigned_t ());
+ DECL_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;
+ /* function_info pointer pointer */
+ fn_info_ptr_type = build_pointer_type
+ (build_qualified_type (fn_info_ptr_type, TYPE_QUAL_CONST));
+ field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE,
+ fn_info_ptr_type);
+ DECL_CHAIN (field) = fields;
fields = field;
- return fields;
+ finish_builtin_struct (type, "__gcov_info", fields, NULL_TREE);
}
-/* Creates struct gcov_info value (in gcov-io.h). */
+/* Returns a CONSTRUCTOR for the gcov_info object. INFO_TYPE is the
+ gcov_info structure type, FN_ARY is the array of pointers to
+ function info objects. */
+
static tree
-build_gcov_info_value ()
+build_info (tree info_type, tree fn_ary)
{
- tree value = NULL_TREE;
+ tree info_fields = TYPE_FIELDS (info_type);
+ tree merge_fn_type, n_funcs;
+ unsigned ix;
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;
+ int da_file_name_len;
+ VEC(constructor_elt,gc) *v1 = NULL;
+ VEC(constructor_elt,gc) *v2 = NULL;
/* Version ident */
- value = tree_cons (NULL_TREE,
- convert (long_unsigned_type_node,
- build_int_2 (GCOV_VERSION, 0)),
- value);
+ CONSTRUCTOR_APPEND_ELT (v1, info_fields,
+ build_int_cstu (TREE_TYPE (info_fields),
+ GCOV_VERSION));
+ info_fields = DECL_CHAIN (info_fields);
/* next -- NULL */
- value = tree_cons (NULL_TREE, null_pointer_node, value);
+ CONSTRUCTOR_APPEND_ELT (v1, info_fields, null_pointer_node);
+ info_fields = DECL_CHAIN (info_fields);
- /* 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);
+ /* stamp */
+ CONSTRUCTOR_APPEND_ELT (v1, info_fields,
+ build_int_cstu (TREE_TYPE (info_fields),
+ local_tick));
+ info_fields = DECL_CHAIN (info_fields);
- /* Create constructor for array. */
- if (n_functions)
+ /* Filename */
+ da_file_name_len = strlen (da_file_name);
+ filename_string = build_string (da_file_name_len + 1, da_file_name);
+ TREE_TYPE (filename_string) = build_array_type
+ (char_type_node, build_index_type (size_int (da_file_name_len)));
+ CONSTRUCTOR_APPEND_ELT (v1, info_fields,
+ build1 (ADDR_EXPR, TREE_TYPE (info_fields),
+ filename_string));
+ info_fields = DECL_CHAIN (info_fields);
+
+ /* merge fn array -- NULL slots indicate unmeasured counters */
+ merge_fn_type = TREE_TYPE (TREE_TYPE (info_fields));
+ for (ix = 0; ix != GCOV_COUNTERS; ix++)
{
- 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;
+ tree ptr = 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);
+ if ((1u << ix) & prg_ctr_mask)
+ {
+ tree merge_fn = build_decl (BUILTINS_LOCATION,
+ FUNCTION_DECL,
+ get_identifier (ctr_merge_functions[ix]),
+ TREE_TYPE (merge_fn_type));
+ DECL_EXTERNAL (merge_fn) = 1;
+ TREE_PUBLIC (merge_fn) = 1;
+ DECL_ARTIFICIAL (merge_fn) = 1;
+ TREE_NOTHROW (merge_fn) = 1;
+ /* Initialize assembler name so we can stream out. */
+ DECL_ASSEMBLER_NAME (merge_fn);
+ ptr = build1 (ADDR_EXPR, merge_fn_type, merge_fn);
+ }
+ CONSTRUCTOR_APPEND_ELT (v2, NULL, ptr);
}
- else
- counter_sections = null_pointer_node;
- value = tree_cons (NULL_TREE, counter_sections, value);
-
- return value;
+ CONSTRUCTOR_APPEND_ELT (v1, info_fields,
+ build_constructor (TREE_TYPE (info_fields), v2));
+ info_fields = DECL_CHAIN (info_fields);
+
+ /* n_functions */
+ n_funcs = TYPE_MAX_VALUE (TYPE_DOMAIN (TREE_TYPE (fn_ary)));
+ n_funcs = fold_build2 (PLUS_EXPR, TREE_TYPE (info_fields),
+ n_funcs, size_one_node);
+ CONSTRUCTOR_APPEND_ELT (v1, info_fields, n_funcs);
+ info_fields = DECL_CHAIN (info_fields);
+
+ /* functions */
+ CONSTRUCTOR_APPEND_ELT (v1, info_fields,
+ build1 (ADDR_EXPR, TREE_TYPE (info_fields), fn_ary));
+ info_fields = DECL_CHAIN (info_fields);
+
+ gcc_assert (!info_fields);
+ return build_constructor (info_type, v1);
}
-/* 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. */
+/* Create the gcov_info types and object. Generate the constructor
+ function to call __gcov_init. Does not generate the initializer
+ for the object. Returns TRUE if coverage data is being emitted. */
-static void
-create_coverage ()
+static bool
+coverage_obj_init (void)
{
- 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)
- break;
- if (i == profile_info.n_sections)
- return;
+ tree gcov_info_type, ctor, stmt, init_fn;
+ unsigned n_counters = 0;
+ unsigned ix;
+ struct coverage_data *fn;
+ struct coverage_data **fn_prev;
+ char name_buf[32];
+
+ no_coverage = 1; /* Disable any further coverage. */
+
+ if (!prg_ctr_mask)
+ return false;
+
+ if (cgraph_dump_file)
+ fprintf (cgraph_dump_file, "Using data file %s\n", da_file_name);
+
+ /* Prune functions. */
+ for (fn_prev = &functions_head; (fn = *fn_prev);)
+ if (DECL_STRUCT_FUNCTION (fn->fn_decl))
+ fn_prev = &fn->next;
+ else
+ /* The function is not being emitted, remove from list. */
+ *fn_prev = fn->next;
+
+ for (ix = 0; ix != GCOV_COUNTERS; ix++)
+ if ((1u << ix) & prg_ctr_mask)
+ n_counters++;
- 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 the info and fn_info types. These are mutually recursive. */
+ gcov_info_type = lang_hooks.types.make_type (RECORD_TYPE);
+ gcov_fn_info_type = lang_hooks.types.make_type (RECORD_TYPE);
+ gcov_fn_info_ptr_type = build_pointer_type
+ (build_qualified_type (gcov_fn_info_type, TYPE_QUAL_CONST));
+ build_fn_info_type (gcov_fn_info_type, n_counters, gcov_info_type);
+ build_info_type (gcov_info_type, gcov_fn_info_ptr_type);
- /* 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);
+ /* Build the gcov info var, this is referred to in its own
+ initializer. */
+ gcov_info_var = build_decl (BUILTINS_LOCATION,
+ VAR_DECL, NULL_TREE, gcov_info_type);
+ TREE_STATIC (gcov_info_var) = 1;
+ ASM_GENERATE_INTERNAL_LABEL (name_buf, "LPBX", 0);
+ DECL_NAME (gcov_info_var) = get_identifier (name_buf);
+
+ /* Build a decl for __gcov_init. */
+ init_fn = build_pointer_type (gcov_info_type);
+ init_fn = build_function_type_list (void_type_node, init_fn, NULL);
+ init_fn = build_decl (BUILTINS_LOCATION, FUNCTION_DECL,
+ get_identifier ("__gcov_init"), init_fn);
+ TREE_PUBLIC (init_fn) = 1;
+ DECL_EXTERNAL (init_fn) = 1;
+ DECL_ASSEMBLER_NAME (init_fn);
+
+ /* Generate a call to __gcov_init(&gcov_info). */
+ ctor = NULL;
+ stmt = build_fold_addr_expr (gcov_info_var);
+ stmt = build_call_expr (init_fn, 1, stmt);
+ append_to_statement_list (stmt, &ctor);
+
+ /* Generate a constructor to run it. */
+ cgraph_build_static_cdtor ('I', ctor, DEFAULT_INIT_PRIORITY);
+
+ return true;
}
-\f
-/* Find (and create if not present) a section with TAG for the current
- function. */
-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;
+/* Generate the coverage function info for FN and DATA. Append a
+ pointer to that object to CTOR and return the appended CTOR. */
- 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;
-}
-
-/* Generate a MEM rtl to access counter NO in counter section TAG. */
-
-rtx
-coverage_counter_ref (unsigned tag, unsigned no)
+static VEC(constructor_elt,gc) *
+coverage_obj_fn (VEC(constructor_elt,gc) *ctor, tree fn,
+ struct coverage_data const *data)
{
- enum machine_mode mode = mode_for_size (GCOV_TYPE_SIZE, MODE_INT, 0);
- struct section_info *sect = find_counters_section (tag);
- rtx ref;
-
- if (!profiler_label)
- {
- /* Generate and save a copy of this so it can be shared. */
- char buf[20];
+ tree init = build_fn_info (data, gcov_fn_info_type, gcov_info_var);
+ tree var = build_var (fn, gcov_fn_info_type, -1);
+
+ DECL_INITIAL (var) = init;
+ varpool_finalize_decl (var);
- ASM_GENERATE_INTERNAL_LABEL (buf, "LPBX", 2);
- profiler_label = gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (buf));
- }
- if (no + 1 > (unsigned) sect->n_counters_now)
- sect->n_counters_now = no + 1;
+ CONSTRUCTOR_APPEND_ELT (ctor, NULL,
+ build1 (ADDR_EXPR, gcov_fn_info_ptr_type, var));
+ return ctor;
+}
- no += sect->n_counters;
- ref = plus_constant (profiler_label, GCOV_TYPE_SIZE / BITS_PER_UNIT * no);
- ref = gen_rtx_MEM (mode, ref);
- set_mem_alias_set (ref, new_alias_set ());
+/* Finalize the coverage data. Generates the array of pointers to
+ function objects from CTOR. Generate the gcov_info initializer. */
- return ref;
+static void
+coverage_obj_finish (VEC(constructor_elt,gc) *ctor)
+{
+ unsigned n_functions = VEC_length(constructor_elt, ctor);
+ tree fn_info_ary_type = build_array_type
+ (build_qualified_type (gcov_fn_info_ptr_type, TYPE_QUAL_CONST),
+ build_index_type (size_int (n_functions - 1)));
+ tree fn_info_ary = build_decl (BUILTINS_LOCATION, VAR_DECL, NULL_TREE,
+ fn_info_ary_type);
+ char name_buf[32];
+
+ TREE_STATIC (fn_info_ary) = 1;
+ ASM_GENERATE_INTERNAL_LABEL (name_buf, "LPBX", 1);
+ DECL_NAME (fn_info_ary) = get_identifier (name_buf);
+ DECL_INITIAL (fn_info_ary) = build_constructor (fn_info_ary_type, ctor);
+ varpool_finalize_decl (fn_info_ary);
+
+ DECL_INITIAL (gcov_info_var)
+ = build_info (TREE_TYPE (gcov_info_var), fn_info_ary);
+ varpool_finalize_decl (gcov_info_var);
}
-\f
/* Perform file-level initialization. Read in data file, generate name
- of graph file. */
+ of graph file. */
void
-coverage_init (filename)
- const char *filename;
+coverage_init (const char *filename)
{
int len = strlen (filename);
+ int prefix_len = 0;
- da_file_name = (char *) xmalloc (len + strlen (GCOV_DATA_SUFFIX) + 1);
- strcpy (da_file_name, filename);
- strcat (da_file_name, GCOV_DATA_SUFFIX);
-
- read_counts_file ();
+ if (!profile_data_prefix && !IS_ABSOLUTE_PATH (filename))
+ profile_data_prefix = getpwd ();
+
+ if (profile_data_prefix)
+ prefix_len = strlen (profile_data_prefix);
- /* 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);
+ /* Name of da file. */
+ da_file_name = XNEWVEC (char, len + strlen (GCOV_DATA_SUFFIX)
+ + prefix_len + 2);
+
+ if (profile_data_prefix)
+ {
+ memcpy (da_file_name, profile_data_prefix, prefix_len);
+ da_file_name[prefix_len++] = '/';
+ }
+ memcpy (da_file_name + prefix_len, filename, len);
+ strcpy (da_file_name + prefix_len + len, GCOV_DATA_SUFFIX);
+
+ /* Name of bbg file. */
+ if (flag_test_coverage && !flag_compare_debug)
+ {
+ bbg_file_name = XNEWVEC (char, len + strlen (GCOV_NOTE_SUFFIX) + 1);
+ memcpy (bbg_file_name, filename, len);
+ strcpy (bbg_file_name + len, GCOV_NOTE_SUFFIX);
+
+ if (!gcov_open (bbg_file_name, -1))
+ {
+ error ("cannot open %s", bbg_file_name);
+ bbg_file_name = NULL;
+ }
+ else
+ {
+ gcov_write_unsigned (GCOV_NOTE_MAGIC);
+ gcov_write_unsigned (GCOV_VERSION);
+ gcov_write_unsigned (local_tick);
+ }
+ }
+
+ if (flag_branch_probabilities)
+ read_counts_file ();
}
/* Performs file-level cleanup. Close graph file, generate coverage
variables and constructor. */
void
-coverage_finish ()
+coverage_finish (void)
{
- create_coverage ();
- if (bbg_file_opened)
+ if (bbg_file_name && gcov_close ())
+ unlink (bbg_file_name);
+
+ if (!local_tick || local_tick == (unsigned)-1)
+ /* Only remove the da file, if we cannot stamp it. If we can
+ stamp it, libgcov will DTRT. */
+ unlink (da_file_name);
+
+ if (coverage_obj_init ())
{
- int error = gcov_close ();
+ VEC(constructor_elt,gc) *fn_ctor = NULL;
+ struct coverage_data *fn;
- 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);
+ for (fn = functions_head; fn; fn = fn->next)
+ fn_ctor = coverage_obj_fn (fn_ctor, fn->fn_decl, fn);
+ coverage_obj_finish (fn_ctor);
}
}
-
#include "gt-coverage.h"