/* Read and write coverage files, and associated functionality.
- Copyright (C) 1990, 1991, 1992, 1993, 1994, 1996, 1997, 1998, 1999,
- 2000, 2001, 2003, 2004, 2005, 2007, 2008, 2009, 2010
- Free Software Foundation, Inc.
+ Copyright (C) 1990-2015 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.
#include "coretypes.h"
#include "tm.h"
#include "rtl.h"
+#include "alias.h"
+#include "symtab.h"
#include "tree.h"
+#include "fold-const.h"
+#include "stringpool.h"
+#include "stor-layout.h"
#include "flags.h"
#include "output.h"
#include "regs.h"
-#include "expr.h"
+#include "hard-reg-set.h"
#include "function.h"
+#include "insn-config.h"
+#include "expmed.h"
+#include "dojump.h"
+#include "explow.h"
+#include "calls.h"
+#include "emit-rtl.h"
+#include "varasm.h"
+#include "stmt.h"
+#include "expr.h"
+#include "predict.h"
+#include "dominance.h"
+#include "cfg.h"
#include "basic-block.h"
#include "toplev.h"
#include "tm_p.h"
-#include "ggc.h"
#include "coverage.h"
#include "langhooks.h"
-#include "hashtab.h"
#include "tree-iterator.h"
-#include "cgraph.h"
+#include "context.h"
+#include "pass_manager.h"
#include "tree-pass.h"
+#include "cgraph.h"
+#include "dumpfile.h"
#include "diagnostic-core.h"
#include "intl.h"
#include "filenames.h"
+#include "target.h"
+#include "params.h"
+#include "auto-profile.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 */
+ struct coverage_data *next; /* next function */
unsigned ident; /* function ident */
unsigned lineno_checksum; /* function lineno checksum */
unsigned cfg_checksum; /* function cfg checksum */
- unsigned n_ctrs[GCOV_COUNTERS];/* number of counters. */
+ tree fn_decl; /* the function decl */
+ tree ctr_vars[GCOV_COUNTERS]; /* counter variables. */
};
/* Counts information for a function. */
-typedef struct counts_entry
+typedef struct counts_entry : pointer_hash <counts_entry>
{
/* We hash by */
unsigned ident;
gcov_type *counts;
struct gcov_ctr_summary summary;
- /* Workspace */
- struct counts_entry *chain;
-
+ /* hash_table support. */
+ static inline hashval_t hash (const counts_entry *);
+ static int equal (const counts_entry *, const counts_entry *);
+ static void remove (counts_entry *);
} 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. */
-static unsigned prg_n_ctrs[GCOV_COUNTERS]; /* Total counters allocated. */
/* 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. */
-/* Name of the output file for coverage output file. */
-static char *bbg_file_name;
-static unsigned bbg_file_opened;
-static int bbg_function_announced;
+/* 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 count data file. */
-static char *da_file_name;
+/* Name of the notes (gcno) output file. The "bbg" prefix is for
+ historical reasons, when the notes file contained only the
+ basic block graph notes.
+ If this is NULL we're not writing to the notes file. */
+static char *bbg_file_name;
-/* Hash table of count data. */
-static htab_t counts_hash = NULL;
+/* File stamp for notes file. */
+static unsigned bbg_file_stamp;
-/* Trees representing the counter table arrays. */
-static GTY(()) tree tree_ctr_tables[GCOV_COUNTERS];
+/* Name of the count data (gcda) file. */
+static char *da_file_name;
/* 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;
+#define STR(str) #str
+#define DEF_GCOV_COUNTER(COUNTER, NAME, FN_TYPE) STR(__gcov_merge ## FN_TYPE),
+static const char *const ctr_merge_functions[GCOV_COUNTERS] = {
+#include "gcov-counter.def"
+};
+#undef DEF_GCOV_COUNTER
+#undef STR
+
+#define DEF_GCOV_COUNTER(COUNTER, NAME, FN_TYPE) NAME,
+static const char *const ctr_names[GCOV_COUNTERS] = {
+#include "gcov-counter.def"
+};
+#undef DEF_GCOV_COUNTER
/* Forward declarations. */
-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_fn_info_type (unsigned);
-static tree build_fn_info_value (const struct function_list *, tree);
-static tree build_ctr_info_type (void);
-static tree build_ctr_info_value (unsigned, tree);
-static tree build_gcov_info (void);
-static void create_coverage (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, va_gc> *coverage_obj_fn
+(vec<constructor_elt, va_gc> *, tree, struct coverage_data const *);
+static void coverage_obj_finish (vec<constructor_elt, va_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);
+ machine_mode mode = smallest_mode_for_size (GCOV_TYPE_SIZE, MODE_INT);
+ return lang_hooks.types.type_for_mode (mode, 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);
+ machine_mode mode = smallest_mode_for_size (32, MODE_INT);
+ return lang_hooks.types.type_for_mode (mode, true);
}
\f
-static hashval_t
-htab_counts_entry_hash (const void *of)
+inline hashval_t
+counts_entry::hash (const counts_entry *entry)
{
- const counts_entry_t *const entry = (const counts_entry_t *) of;
-
return entry->ident * GCOV_COUNTERS + entry->ctr;
}
-static int
-htab_counts_entry_eq (const void *of1, const void *of2)
+inline int
+counts_entry::equal (const counts_entry *entry1, const counts_entry *entry2)
{
- const counts_entry_t *const entry1 = (const counts_entry_t *) of1;
- const counts_entry_t *const entry2 = (const counts_entry_t *) of2;
-
return entry1->ident == entry2->ident && entry1->ctr == entry2->ctr;
}
-static void
-htab_counts_entry_del (void *of)
+inline void
+counts_entry::remove (counts_entry *entry)
{
- counts_entry_t *const entry = (counts_entry_t *) of;
-
free (entry->counts);
free (entry);
}
+/* Hash table of count data. */
+static hash_table<counts_entry> *counts_hash;
+
/* Read in the counts file, if available. */
static void
read_counts_file (void)
{
gcov_unsigned_t fn_ident = 0;
- counts_entry_t *summaried = NULL;
- unsigned seen_summary = 0;
+ struct gcov_summary summary;
+ unsigned new_summary = 1;
gcov_unsigned_t tag;
int is_error = 0;
unsigned lineno_checksum = 0;
return;
}
- /* Read and discard the stamp. */
- gcov_read_unsigned ();
+ /* Read the stamp, used for creating a generation count. */
+ tag = gcov_read_unsigned ();
+ bbg_file_stamp = crc32_unsigned (bbg_file_stamp, tag);
- counts_hash = htab_create (10,
- htab_counts_entry_hash, htab_counts_entry_eq,
- htab_counts_entry_del);
+ counts_hash = new hash_table<counts_entry> (10);
while ((tag = gcov_read_unsigned ()))
{
gcov_unsigned_t length;
offset = gcov_position ();
if (tag == GCOV_TAG_FUNCTION)
{
- fn_ident = gcov_read_unsigned ();
- lineno_checksum = gcov_read_unsigned ();
- cfg_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->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;
+ struct gcov_summary sum;
+ unsigned ix;
- gcov_read_summary (&summary);
- seen_summary = 1;
- for (entry = summaried; entry; entry = entry->chain)
- {
- struct gcov_ctr_summary *csum = &summary.ctrs[entry->ctr];
+ if (new_summary)
+ memset (&summary, 0, sizeof (summary));
- entry->summary.runs += csum->runs;
- entry->summary.sum_all += csum->sum_all;
- if (entry->summary.run_max < csum->run_max)
- entry->summary.run_max = csum->run_max;
- entry->summary.sum_max += csum->sum_max;
+ gcov_read_summary (&sum);
+ for (ix = 0; ix != GCOV_COUNTERS_SUMMABLE; ix++)
+ {
+ 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;
}
+ if (new_summary)
+ memcpy (summary.ctrs[GCOV_COUNTER_ARCS].histogram,
+ sum.ctrs[GCOV_COUNTER_ARCS].histogram,
+ sizeof (gcov_bucket_type) * GCOV_HISTOGRAM_SIZE);
+ else
+ gcov_histogram_merge (summary.ctrs[GCOV_COUNTER_ARCS].histogram,
+ sum.ctrs[GCOV_COUNTER_ARCS].histogram);
+ new_summary = 0;
}
else if (GCOV_TAG_IS_COUNTER (tag) && fn_ident)
{
elt.ident = fn_ident;
elt.ctr = GCOV_COUNTER_FOR_TAG (tag);
- slot = (counts_entry_t **) htab_find_slot
- (counts_hash, &elt, INSERT);
+ slot = counts_hash->find_slot (&elt, INSERT);
entry = *slot;
if (!entry)
{
entry->ctr = elt.ctr;
entry->lineno_checksum = lineno_checksum;
entry->cfg_checksum = cfg_checksum;
- entry->summary.num = n_counts;
+ if (elt.ctr < GCOV_COUNTERS_SUMMABLE)
+ entry->summary = summary.ctrs[elt.ctr];
+ entry->summary.num = n_counts;
entry->counts = XCNEWVEC (gcov_type, n_counts);
}
else if (entry->lineno_checksum != lineno_checksum
error ("checksum is (%x,%x) instead of (%x,%x)",
entry->lineno_checksum, entry->cfg_checksum,
lineno_checksum, cfg_checksum);
- htab_delete (counts_hash);
+ delete counts_hash;
+ counts_hash = NULL;
break;
}
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);
+ delete counts_hash;
+ counts_hash = NULL;
break;
}
else if (elt.ctr >= GCOV_COUNTERS_SUMMABLE)
ctr_names[elt.ctr], fn_ident);
goto skip_merge;
}
-
- if (elt.ctr < GCOV_COUNTERS_SUMMABLE
- /* 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. */
- && (!entry->chain || summaried != entry))
+ 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 ();
{
error (is_error < 0 ? "%qs has overflowed" : "%qs is corrupted",
da_file_name);
- htab_delete (counts_hash);
+ delete counts_hash;
+ counts_hash = NULL;
break;
}
}
{
static int warned = 0;
- if (!warned++)
- 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);
+ if (!warned++ && dump_enabled_p ())
+ dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, input_location,
+ (flag_guess_branch_prob
+ ? "file %s not found, execution counts estimated\n"
+ : "file %s not found, execution counts assumed to "
+ "be zero\n"),
+ da_file_name);
return NULL;
}
-
- elt.ident = current_function_funcdef_no + 1;
- elt.ctr = counter;
- entry = (counts_entry_t *) htab_find (counts_hash, &elt);
- if (!entry)
+ if (PARAM_VALUE (PARAM_PROFILE_FUNC_INTERNAL_ID))
+ elt.ident = current_function_funcdef_no + 1;
+ else
{
- warning (0, "no coverage for function %qE found",
- DECL_ASSEMBLER_NAME (current_function_decl));
- return NULL;
+ gcc_assert (coverage_node_map_initialized_p ());
+ elt.ident = cgraph_node::get (cfun->decl)->profile_id;
}
-
+ elt.ctr = counter;
+ entry = counts_hash->find (&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 (entry->cfg_checksum != cfg_checksum
|| entry->summary.num != expected)
{
warning_printed =
warning_at (input_location, OPT_Wcoverage_mismatch,
- "The control flow of function %qE does not match "
+ "the control flow of function %qE does not match "
"its profile data (counter %qs)", id, ctr_names[counter]);
- if (warning_printed)
+ if (warning_printed && dump_enabled_p ())
{
- inform (input_location, "Use -Wno-error=coverage-mismatch to tolerate "
- "the mismatch but performance may drop if the function is hot");
+ dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, input_location,
+ "use -Wno-error=coverage-mismatch to tolerate "
+ "the mismatch but performance may drop if the "
+ "function is hot\n");
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"));
+ dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, input_location,
+ "coverage mismatch ignored\n");
+ dump_printf (MSG_OPTIMIZED_LOCATIONS,
+ flag_guess_branch_prob
+ ? G_("execution counts estimated\n")
+ : G_("execution counts assumed to be zero\n"));
if (!flag_guess_branch_prob)
- inform (input_location,
- "this can result in poorly optimized code");
+ dump_printf (MSG_OPTIMIZED_LOCATIONS,
+ "this can result in poorly optimized code\n");
}
}
return NULL;
}
- else if (entry->lineno_checksum != lineno_checksum)
- {
- warning (0, "Source location for function %qE have changed,"
- " the profile data may be out of date",
- DECL_ASSEMBLER_NAME (current_function_decl));
- }
+ else if (entry->lineno_checksum != lineno_checksum)
+ {
+ warning (OPT_Wcoverage_mismatch,
+ "source locations for function %qE have changed,"
+ " the profile data may be out of date",
+ DECL_ASSEMBLER_NAME (current_function_decl));
+ }
if (summary)
*summary = &entry->summary;
if (!num)
return 1;
- if (!tree_ctr_tables[counter])
+ if (!fn_v_ctrs[counter])
{
- /* Generate and save a copy of this so it can be shared. Leave
- the index type unspecified for now; it will be set after all
- functions have been compiled. */
- char buf[20];
- tree gcov_type_node = get_gcov_type ();
- tree gcov_type_array_type
- = build_array_type (gcov_type_node, NULL_TREE);
- tree_ctr_tables[counter]
- = build_decl (BUILTINS_LOCATION,
- VAR_DECL, NULL_TREE, gcov_type_array_type);
- TREE_STATIC (tree_ctr_tables[counter]) = 1;
- ASM_GENERATE_INTERNAL_LABEL (buf, "LPBX", counter + 1);
- DECL_NAME (tree_ctr_tables[counter]) = get_identifier (buf);
- DECL_ALIGN (tree_ctr_tables[counter]) = TYPE_ALIGN (gcov_type_node);
-
- if (dump_file)
- fprintf (dump_file, "Using data file %s\n", da_file_name);
+ 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;
}
tree gcov_type_node = get_gcov_type ();
gcc_assert (no < fn_n_ctrs[counter] - fn_b_ctrs[counter]);
- no += prg_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, tree_ctr_tables[counter],
+ return build4 (ARRAY_REF, gcov_type_node, fn_v_ctrs[counter],
build_int_cst (integer_type_node, no), NULL, NULL);
}
tree gcov_type_node = get_gcov_type ();
gcc_assert (no < fn_n_ctrs[counter] - fn_b_ctrs[counter]);
- no += prg_n_ctrs[counter] + fn_b_ctrs[counter];
-
- TREE_ADDRESSABLE (tree_ctr_tables[counter]) = 1;
+ 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,
- tree_ctr_tables[counter],
+ fn_v_ctrs[counter],
build_int_cst (integer_type_node, no),
NULL, NULL));
}
return chksum;
}
-/* Compute cfg checksum for the current function.
+/* Compute profile ID. This is better to be unique in whole program. */
+
+unsigned
+coverage_compute_profile_id (struct cgraph_node *n)
+{
+ unsigned chksum;
+
+ /* Externally visible symbols have unique name. */
+ if (TREE_PUBLIC (n->decl) || DECL_EXTERNAL (n->decl) || n->unique_name)
+ {
+ chksum = coverage_checksum_string
+ (0, IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (n->decl)));
+ }
+ else
+ {
+ expanded_location xloc
+ = expand_location (DECL_SOURCE_LOCATION (n->decl));
+ bool use_name_only = (PARAM_VALUE (PARAM_PROFILE_FUNC_INTERNAL_ID) == 0);
+
+ chksum = (use_name_only ? 0 : xloc.line);
+ chksum = coverage_checksum_string (chksum, xloc.file);
+ chksum = coverage_checksum_string
+ (chksum, IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (n->decl)));
+ if (!use_name_only && first_global_object_name)
+ chksum = coverage_checksum_string
+ (chksum, first_global_object_name);
+ chksum = coverage_checksum_string
+ (chksum, aux_base_name);
+ }
+
+ /* Non-negative integers are hopefully small enough to fit in all targets.
+ Gcov file formats wants non-zero function IDs. */
+ chksum = chksum & 0x7fffffff;
+ return chksum + (!chksum);
+}
+
+/* Compute cfg checksum for the function FN given as argument.
The checksum is calculated carefully so that
source code changes that doesn't affect the control flow graph
won't change the checksum.
but the compiler won't detect the change and use the wrong profile data. */
unsigned
-coverage_compute_cfg_checksum (void)
+coverage_compute_cfg_checksum (struct function *fn)
{
basic_block bb;
- unsigned chksum = n_basic_blocks;
+ unsigned chksum = n_basic_blocks_for_fn (fn);
- FOR_EACH_BB (bb)
+ FOR_EACH_BB_FN (bb, fn)
{
edge e;
edge_iterator ei;
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 nonzero if data
- should be output. */
+/* Begin output to the notes file for the current function.
+ Writes the function header. Returns nonzero if data should be output. */
int
-coverage_begin_output (unsigned lineno_checksum, unsigned cfg_checksum)
+coverage_begin_function (unsigned lineno_checksum, unsigned cfg_checksum)
{
+ 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 || !flag_test_coverage || flag_compare_debug)
+ if (no_coverage || !bbg_file_name)
return 0;
- if (!bbg_function_announced)
- {
- expanded_location xloc
- = expand_location (DECL_SOURCE_LOCATION (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_NOTE_MAGIC);
- gcov_write_unsigned (GCOV_VERSION);
- gcov_write_unsigned (local_tick);
- }
- bbg_file_opened = 1;
- }
+ xloc = expand_location (DECL_SOURCE_LOCATION (current_function_decl));
+ /* Announce function */
+ offset = gcov_write_tag (GCOV_TAG_FUNCTION);
+ if (PARAM_VALUE (PARAM_PROFILE_FUNC_INTERNAL_ID))
+ gcov_write_unsigned (current_function_funcdef_no + 1);
+ else
+ {
+ gcc_assert (coverage_node_map_initialized_p ());
+ gcov_write_unsigned (
+ cgraph_node::get (current_function_decl)->profile_id);
+ }
- /* 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);
+ 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);
- bbg_function_announced = 1;
- }
return !gcov_is_error ();
}
{
unsigned i;
- if (bbg_file_opened > 1 && gcov_is_error ())
+ if (bbg_file_name && gcov_is_error ())
{
warning (0, "error writing %qs", bbg_file_name);
- bbg_file_opened = -1;
+ unlink (bbg_file_name);
+ bbg_file_name = NULL;
}
if (fn_ctr_mask)
{
- struct function_list *item;
+ struct coverage_data *item = 0;
- item = XCNEW (struct function_list);
-
- *functions_tail = item;
- functions_tail = &item->next;
+ item = ggc_alloc<coverage_data> ();
+ if (PARAM_VALUE (PARAM_PROFILE_FUNC_INTERNAL_ID))
+ item->ident = current_function_funcdef_no + 1;
+ else
+ {
+ gcc_assert (coverage_node_map_initialized_p ());
+ item->ident = cgraph_node::get (cfun->decl)->profile_id;
+ }
- item->next = 0;
- 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;
+
for (i = 0; i != GCOV_COUNTERS; i++)
{
- item->n_ctrs[i] = fn_n_ctrs[i];
- prg_n_ctrs[i] += fn_n_ctrs[i];
- fn_n_ctrs[i] = fn_b_ctrs[i] = 0;
+ tree var = fn_v_ctrs[i];
+
+ 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_node::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;
}
- bbg_function_announced = 0;
}
-/* Creates the gcov_fn_info RECORD_TYPE. */
+/* 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_fn_info_type (unsigned int counters)
+build_var (tree fn_decl, tree type, int counter)
{
- tree type = lang_hooks.types.make_type (RECORD_TYPE);
+ 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;
+
+ 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_NONALIASED (var) = 1;
+ DECL_ALIGN (var) = TYPE_ALIGN (type);
+
+ return var;
+}
+
+/* Creates the gcov_fn_info RECORD_TYPE. */
+
+static void
+build_fn_info_type (tree type, unsigned counters, tree gcov_info_type)
+{
+ tree ctr_info = lang_hooks.types.make_type (RECORD_TYPE);
tree field, fields;
tree array_type;
+ 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);
+
+ /* 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 */
- fields = build_decl (BUILTINS_LOCATION,
- FIELD_DECL, NULL_TREE, get_gcov_unsigned_t ());
+ 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 ());
+ field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE,
+ get_gcov_unsigned_t ());
DECL_CHAIN (field) = fields;
fields = field;
/* cfg checksum */
- field = build_decl (BUILTINS_LOCATION,
- FIELD_DECL, NULL_TREE, get_gcov_unsigned_t ());
+ 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 (get_gcov_unsigned_t (), array_type);
+ array_type = build_array_type (ctr_info, array_type);
/* counters */
- field = build_decl (BUILTINS_LOCATION,
- FIELD_DECL, NULL_TREE, array_type);
+ field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE, array_type);
DECL_CHAIN (field) = fields;
fields = field;
finish_builtin_struct (type, "__gcov_fn_info", fields, NULL_TREE);
-
- return type;
}
-/* Creates a CONSTRUCTOR for a gcov_fn_info. FUNCTION is
- the function being processed and TYPE is the gcov_fn_info
- RECORD_TYPE. */
+/* 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_fn_info_value (const struct function_list *function, tree type)
+build_fn_info (const struct coverage_data *data, tree type, tree key)
{
tree fields = TYPE_FIELDS (type);
+ tree ctr_type;
unsigned ix;
- VEC(constructor_elt,gc) *v1 = NULL;
- VEC(constructor_elt,gc) *v2 = NULL;
+ vec<constructor_elt, va_gc> *v1 = NULL;
+ vec<constructor_elt, va_gc> *v2 = NULL;
+ /* key */
+ CONSTRUCTOR_APPEND_ELT (v1, fields,
+ build1 (ADDR_EXPR, TREE_TYPE (fields), key));
+ fields = DECL_CHAIN (fields);
+
/* ident */
CONSTRUCTOR_APPEND_ELT (v1, fields,
build_int_cstu (get_gcov_unsigned_t (),
- function->ident));
+ data->ident));
fields = DECL_CHAIN (fields);
/* lineno_checksum */
CONSTRUCTOR_APPEND_ELT (v1, fields,
build_int_cstu (get_gcov_unsigned_t (),
- function->lineno_checksum));
+ data->lineno_checksum));
fields = DECL_CHAIN (fields);
/* cfg_checksum */
CONSTRUCTOR_APPEND_ELT (v1, fields,
build_int_cstu (get_gcov_unsigned_t (),
- function->cfg_checksum));
+ data->cfg_checksum));
fields = DECL_CHAIN (fields);
/* counters */
+ ctr_type = TREE_TYPE (TREE_TYPE (fields));
for (ix = 0; ix != GCOV_COUNTERS; ix++)
if (prg_ctr_mask & (1 << ix))
- CONSTRUCTOR_APPEND_ELT (v2, NULL,
- build_int_cstu (get_gcov_unsigned_t (),
- function->n_ctrs[ix]));
-
+ {
+ vec<constructor_elt, va_gc> *ctr = NULL;
+ tree var = data->ctr_vars[ix];
+ unsigned count = 0;
+
+ if (var)
+ count
+ = tree_to_shwi (TYPE_MAX_VALUE (TYPE_DOMAIN (TREE_TYPE (var))))
+ + 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 build_constructor (type, v1);
}
-/* Creates the gcov_ctr_info RECORD_TYPE. */
+/* 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 tree
-build_ctr_info_type (void)
+static void
+build_info_type (tree type, tree fn_info_ptr_type)
{
- tree type = lang_hooks.types.make_type (RECORD_TYPE);
tree field, fields = NULL_TREE;
- tree gcov_ptr_type = build_pointer_type (get_gcov_type ());
- tree gcov_merge_fn_type;
+ tree merge_fn_type;
- /* counters */
- field = build_decl (BUILTINS_LOCATION,
- FIELD_DECL, NULL_TREE, get_gcov_unsigned_t ());
+ /* Version ident */
+ field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE,
+ get_gcov_unsigned_t ());
DECL_CHAIN (field) = fields;
fields = field;
- /* values */
- field = build_decl (BUILTINS_LOCATION,
- FIELD_DECL, NULL_TREE, gcov_ptr_type);
+ /* 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;
- /* merge */
- gcov_merge_fn_type =
- build_function_type_list (void_type_node,
- gcov_ptr_type, get_gcov_unsigned_t (),
- NULL_TREE);
- field = build_decl (BUILTINS_LOCATION,
- FIELD_DECL, NULL_TREE,
- build_pointer_type (gcov_merge_fn_type));
+ /* stamp */
+ field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE,
+ get_gcov_unsigned_t ());
DECL_CHAIN (field) = fields;
fields = field;
- finish_builtin_struct (type, "__gcov_ctr_info", fields, NULL_TREE);
-
- return type;
-}
-
-/* Creates a CONSTRUCTOR for a gcov_ctr_info. COUNTER is
- the counter being processed and TYPE is the gcov_ctr_info
- RECORD_TYPE. */
-
-static tree
-build_ctr_info_value (unsigned int counter, tree type)
-{
- tree fields = TYPE_FIELDS (type);
- tree fn;
- VEC(constructor_elt,gc) *v = NULL;
-
- /* counters */
- CONSTRUCTOR_APPEND_ELT (v, fields,
- build_int_cstu (get_gcov_unsigned_t (),
- prg_n_ctrs[counter]));
- fields = DECL_CHAIN (fields);
+ /* Filename */
+ 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;
- if (prg_n_ctrs[counter])
- {
- tree array_type;
-
- array_type = build_int_cstu (get_gcov_unsigned_t (),
- prg_n_ctrs[counter] - 1);
- array_type = build_index_type (array_type);
- array_type = build_array_type (TREE_TYPE (TREE_TYPE (fields)),
- array_type);
-
- TREE_TYPE (tree_ctr_tables[counter]) = array_type;
- DECL_SIZE (tree_ctr_tables[counter]) = TYPE_SIZE (array_type);
- DECL_SIZE_UNIT (tree_ctr_tables[counter]) = TYPE_SIZE_UNIT (array_type);
- varpool_finalize_decl (tree_ctr_tables[counter]);
-
- CONSTRUCTOR_APPEND_ELT (v, fields,
- build1 (ADDR_EXPR, TREE_TYPE (fields),
- tree_ctr_tables[counter]));
- }
- else
- CONSTRUCTOR_APPEND_ELT (v, fields, null_pointer_node);
- fields = DECL_CHAIN (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;
+
+ /* n_functions */
+ field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE,
+ get_gcov_unsigned_t ());
+ DECL_CHAIN (field) = fields;
+ fields = field;
+
+ /* 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;
- fn = build_decl (BUILTINS_LOCATION,
- FUNCTION_DECL,
- get_identifier (ctr_merge_functions[counter]),
- TREE_TYPE (TREE_TYPE (fields)));
- DECL_EXTERNAL (fn) = 1;
- TREE_PUBLIC (fn) = 1;
- DECL_ARTIFICIAL (fn) = 1;
- TREE_NOTHROW (fn) = 1;
- DECL_ASSEMBLER_NAME (fn); /* Initialize assembler name so we can stream out. */
- CONSTRUCTOR_APPEND_ELT (v, fields, build1 (ADDR_EXPR, TREE_TYPE (fields), fn));
-
- return build_constructor (type, v);
+ finish_builtin_struct (type, "__gcov_info", fields, NULL_TREE);
}
-/* Creates the gcov_info RECORD_TYPE and initializer for it. Returns a
- CONSTRUCTOR. */
+/* 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 (void)
+build_info (tree info_type, tree fn_ary)
{
- unsigned n_ctr_types, ix;
- tree type, const_type;
- tree fn_info_type, fn_info_value = NULL_TREE;
- tree fn_info_ptr_type;
- tree ctr_info_type, ctr_info_ary_type, ctr_info_value = NULL_TREE;
- tree field, fields = NULL_TREE;
+ tree info_fields = TYPE_FIELDS (info_type);
+ tree merge_fn_type, n_funcs;
+ unsigned ix;
tree filename_string;
int da_file_name_len;
- unsigned n_fns;
- const struct function_list *fn;
- tree string_type;
- VEC(constructor_elt,gc) *v1 = NULL;
- VEC(constructor_elt,gc) *v2 = NULL;
-
- /* Count the number of active counters. */
- for (n_ctr_types = 0, ix = 0; ix != GCOV_COUNTERS; ix++)
- if (prg_ctr_mask & (1 << ix))
- n_ctr_types++;
-
- type = lang_hooks.types.make_type (RECORD_TYPE);
- const_type = build_qualified_type (type, TYPE_QUAL_CONST);
+ vec<constructor_elt, va_gc> *v1 = NULL;
+ vec<constructor_elt, va_gc> *v2 = NULL;
/* Version ident */
- field = build_decl (BUILTINS_LOCATION,
- FIELD_DECL, NULL_TREE, get_gcov_unsigned_t ());
- DECL_CHAIN (field) = fields;
- fields = field;
- CONSTRUCTOR_APPEND_ELT (v1, field,
- build_int_cstu (TREE_TYPE (field), GCOV_VERSION));
+ CONSTRUCTOR_APPEND_ELT (v1, info_fields,
+ build_int_cstu (TREE_TYPE (info_fields),
+ GCOV_VERSION));
+ info_fields = DECL_CHAIN (info_fields);
/* next -- NULL */
- field = build_decl (BUILTINS_LOCATION,
- FIELD_DECL, NULL_TREE, build_pointer_type (const_type));
- DECL_CHAIN (field) = fields;
- fields = field;
- CONSTRUCTOR_APPEND_ELT (v1, field, null_pointer_node);
-
+ CONSTRUCTOR_APPEND_ELT (v1, info_fields, null_pointer_node);
+ info_fields = DECL_CHAIN (info_fields);
+
/* stamp */
- field = build_decl (BUILTINS_LOCATION,
- FIELD_DECL, NULL_TREE, get_gcov_unsigned_t ());
- DECL_CHAIN (field) = fields;
- fields = field;
- CONSTRUCTOR_APPEND_ELT (v1, field,
- build_int_cstu (TREE_TYPE (field), local_tick));
+ CONSTRUCTOR_APPEND_ELT (v1, info_fields,
+ build_int_cstu (TREE_TYPE (info_fields),
+ bbg_file_stamp));
+ info_fields = DECL_CHAIN (info_fields);
/* Filename */
- string_type = build_pointer_type (build_qualified_type (char_type_node,
- TYPE_QUAL_CONST));
- field = build_decl (BUILTINS_LOCATION,
- FIELD_DECL, NULL_TREE, string_type);
- DECL_CHAIN (field) = fields;
- fields = field;
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, field,
- build1 (ADDR_EXPR, string_type, filename_string));
-
- /* Build the fn_info type and initializer. */
- fn_info_type = build_fn_info_type (n_ctr_types);
- fn_info_ptr_type = build_pointer_type (build_qualified_type
- (fn_info_type, TYPE_QUAL_CONST));
- for (fn = functions_head, n_fns = 0; fn; fn = fn->next, n_fns++)
- CONSTRUCTOR_APPEND_ELT (v2, NULL_TREE,
- build_fn_info_value (fn, fn_info_type));
-
- if (n_fns)
- {
- tree array_type;
+ CONSTRUCTOR_APPEND_ELT (v1, info_fields,
+ build1 (ADDR_EXPR, TREE_TYPE (info_fields),
+ filename_string));
+ info_fields = DECL_CHAIN (info_fields);
- array_type = build_index_type (size_int (n_fns - 1));
- array_type = build_array_type (fn_info_type, array_type);
+ /* 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 ptr = null_pointer_node;
- fn_info_value = build_constructor (array_type, v2);
- fn_info_value = build1 (ADDR_EXPR, fn_info_ptr_type, fn_info_value);
+ 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
- fn_info_value = null_pointer_node;
-
- /* number of functions */
- field = build_decl (BUILTINS_LOCATION,
- FIELD_DECL, NULL_TREE, get_gcov_unsigned_t ());
- DECL_CHAIN (field) = fields;
- fields = field;
- CONSTRUCTOR_APPEND_ELT (v1, field,
- build_int_cstu (get_gcov_unsigned_t (), n_fns));
-
- /* fn_info table */
- field = build_decl (BUILTINS_LOCATION,
- FIELD_DECL, NULL_TREE, fn_info_ptr_type);
- DECL_CHAIN (field) = fields;
- fields = field;
- CONSTRUCTOR_APPEND_ELT (v1, field, fn_info_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);
+}
- /* counter_mask */
- field = build_decl (BUILTINS_LOCATION,
- FIELD_DECL, NULL_TREE, get_gcov_unsigned_t ());
- DECL_CHAIN (field) = fields;
- fields = field;
- CONSTRUCTOR_APPEND_ELT (v1, field,
- build_int_cstu (get_gcov_unsigned_t (),
- prg_ctr_mask));
+/* Generate the constructor function to call __gcov_init. */
- /* counters */
- ctr_info_type = build_ctr_info_type ();
- ctr_info_ary_type = build_index_type (size_int (n_ctr_types));
- ctr_info_ary_type = build_array_type (ctr_info_type, ctr_info_ary_type);
- v2 = NULL;
- for (ix = 0; ix != GCOV_COUNTERS; ix++)
- if (prg_ctr_mask & (1 << ix))
- CONSTRUCTOR_APPEND_ELT (v2, NULL_TREE,
- build_ctr_info_value (ix, ctr_info_type));
- ctr_info_value = build_constructor (ctr_info_ary_type, v2);
+static void
+build_init_ctor (tree gcov_info_type)
+{
+ tree ctor, stmt, init_fn;
- field = build_decl (BUILTINS_LOCATION,
- FIELD_DECL, NULL_TREE, ctr_info_ary_type);
- DECL_CHAIN (field) = fields;
- fields = field;
- CONSTRUCTOR_APPEND_ELT (v1, field, ctr_info_value);
+ /* 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);
- finish_builtin_struct (type, "__gcov_info", fields, NULL_TREE);
+ /* 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);
- return build_constructor (type, v1);
+ /* Generate a constructor to run it. */
+ cgraph_build_static_cdtor ('I', ctor, DEFAULT_INIT_PRIORITY);
}
-/* Write out the structure which libgcov uses to locate all the
- 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 (void)
+static bool
+coverage_obj_init (void)
{
- tree gcov_info, gcov_init, body, t;
+ tree gcov_info_type;
+ 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;
+ return false;
- t = build_gcov_info ();
+ if (symtab->dump_file)
+ fprintf (symtab->dump_file, "Using data file %s\n", da_file_name);
- gcov_info = build_decl (BUILTINS_LOCATION,
- VAR_DECL, NULL_TREE, TREE_TYPE (t));
- TREE_STATIC (gcov_info) = 1;
+ /* 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;
+
+ if (functions_head == NULL)
+ return false;
+
+ for (ix = 0; ix != GCOV_COUNTERS; ix++)
+ if ((1u << ix) & prg_ctr_mask)
+ n_counters++;
+
+ /* 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);
+ build_fn_info_type (gcov_fn_info_type, n_counters, gcov_info_type);
+ gcov_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_info_type (gcov_info_type, gcov_fn_info_ptr_type);
+
+ /* 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) = get_identifier (name_buf);
- DECL_INITIAL (gcov_info) = t;
+ DECL_NAME (gcov_info_var) = get_identifier (name_buf);
- /* Build structure. */
- varpool_finalize_decl (gcov_info);
+ build_init_ctor (gcov_info_type);
- /* Build a decl for __gcov_init. */
- t = build_pointer_type (TREE_TYPE (gcov_info));
- t = build_function_type_list (void_type_node, t, NULL);
- t = build_decl (BUILTINS_LOCATION,
- FUNCTION_DECL, get_identifier ("__gcov_init"), t);
- TREE_PUBLIC (t) = 1;
- DECL_EXTERNAL (t) = 1;
- DECL_ASSEMBLER_NAME (t); /* Initialize assembler name so we can stream out. */
- gcov_init = t;
+ return true;
+}
- /* Generate a call to __gcov_init(&gcov_info). */
- body = NULL;
- t = build_fold_addr_expr (gcov_info);
- t = build_call_expr (gcov_init, 1, t);
- append_to_statement_list (t, &body);
+/* Generate the coverage function info for FN and DATA. Append a
+ pointer to that object to CTOR and return the appended CTOR. */
- /* Generate a constructor to run it. */
- cgraph_build_static_cdtor ('I', body, DEFAULT_INIT_PRIORITY);
+static vec<constructor_elt, va_gc> *
+coverage_obj_fn (vec<constructor_elt, va_gc> *ctor, tree fn,
+ struct coverage_data const *data)
+{
+ 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_node::finalize_decl (var);
+
+ CONSTRUCTOR_APPEND_ELT (ctor, NULL,
+ build1 (ADDR_EXPR, gcov_fn_info_ptr_type, var));
+ return ctor;
}
-\f
+
+/* Finalize the coverage data. Generates the array of pointers to
+ function objects from CTOR. Generate the gcov_info initializer. */
+
+static void
+coverage_obj_finish (vec<constructor_elt, va_gc> *ctor)
+{
+ unsigned n_functions = vec_safe_length (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_node::finalize_decl (fn_info_ary);
+
+ DECL_INITIAL (gcov_info_var)
+ = build_info (TREE_TYPE (gcov_info_var), fn_info_ary);
+ varpool_node::finalize_decl (gcov_info_var);
+}
+
/* Perform file-level initialization. Read in data file, generate name
- of graph file. */
+ of notes file. */
void
coverage_init (const char *filename)
{
int len = strlen (filename);
- /* + 1 for extra '/', in case prefix doesn't end with /. */
- int prefix_len;
+ int prefix_len = 0;
+
+ /* Since coverage_init is invoked very early, before the pass
+ manager, we need to set up the dumping explicitly. This is
+ similar to the handling in finish_optimization_passes. */
+ int profile_pass_num =
+ g->get_passes ()->get_pass_profile ()->static_pass_number;
+ g->get_dumps ()->dump_start (profile_pass_num, NULL);
- if (profile_data_prefix == 0 && !IS_ABSOLUTE_PATH(&filename[0]))
+ if (!profile_data_prefix && !IS_ABSOLUTE_PATH (filename))
profile_data_prefix = getpwd ();
- prefix_len = (profile_data_prefix) ? strlen (profile_data_prefix) + 1 : 0;
+ if (profile_data_prefix)
+ prefix_len = strlen (profile_data_prefix);
/* Name of da file. */
da_file_name = XNEWVEC (char, len + strlen (GCOV_DATA_SUFFIX)
- + prefix_len + 1);
+ + prefix_len + 2);
if (profile_data_prefix)
{
- strcpy (da_file_name, profile_data_prefix);
- da_file_name[prefix_len - 1] = '/';
- da_file_name[prefix_len] = 0;
+ memcpy (da_file_name, profile_data_prefix, prefix_len);
+ da_file_name[prefix_len++] = '/';
}
- else
- da_file_name[0] = 0;
- strcat (da_file_name, filename);
- strcat (da_file_name, GCOV_DATA_SUFFIX);
+ memcpy (da_file_name + prefix_len, filename, len);
+ strcpy (da_file_name + prefix_len + len, GCOV_DATA_SUFFIX);
+
+ bbg_file_stamp = local_tick;
+
+ if (flag_auto_profile)
+ read_autofdo_file ();
+ else if (flag_branch_probabilities)
+ read_counts_file ();
/* Name of bbg file. */
- bbg_file_name = XNEWVEC (char, len + strlen (GCOV_NOTE_SUFFIX) + 1);
- strcpy (bbg_file_name, filename);
- strcat (bbg_file_name, GCOV_NOTE_SUFFIX);
+ 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 (flag_profile_use)
- read_counts_file ();
+ 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 (bbg_file_stamp);
+ }
+ }
+
+ g->get_dumps ()->dump_finish (profile_pass_num);
}
-/* Performs file-level cleanup. Close graph file, generate coverage
+/* Performs file-level cleanup. Close notes file, generate coverage
variables and constructor. */
void
coverage_finish (void)
{
- create_coverage ();
- if (bbg_file_opened)
+ if (bbg_file_name && gcov_close ())
+ unlink (bbg_file_name);
+
+ if (!flag_branch_probabilities && flag_test_coverage
+ && (!local_tick || local_tick == (unsigned)-1))
+ /* Only remove the da file, if we're emitting coverage code and
+ cannot uniquely stamp it. If we can stamp it, libgcov will DTRT. */
+ unlink (da_file_name);
+
+ if (coverage_obj_init ())
{
- int error = gcov_close ();
-
- if (error)
- unlink (bbg_file_name);
- if (!local_tick)
- /* Only remove the da file, if we cannot stamp it. If we can
- stamp it, libgcov will DTRT. */
- unlink (da_file_name);
+ vec<constructor_elt, va_gc> *fn_ctor = NULL;
+ struct coverage_data *fn;
+
+ for (fn = functions_head; fn; fn = fn->next)
+ fn_ctor = coverage_obj_fn (fn_ctor, fn->fn_decl, fn);
+ coverage_obj_finish (fn_ctor);
}
+
+ XDELETEVEC (da_file_name);
+ da_file_name = NULL;
}
#include "gt-coverage.h"