struct function_list
{
struct function_list *next; /* next function */
- const char *name; /* function name */
+ unsigned ident; /* function ident */
unsigned checksum; /* function checksum */
unsigned n_ctrs[GCOV_COUNTERS];/* number of counters. */
};
typedef struct counts_entry
{
/* We hash by */
- char *function_name;
+ unsigned ident;
unsigned ctr;
/* Store */
static struct function_list *functions_head = 0;
static struct function_list **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];
+static unsigned prg_n_ctrs[GCOV_COUNTERS]; /* Total counters allocated. */
/* Counter information for current function. */
-static unsigned fn_ctr_mask;
-static unsigned fn_n_ctrs[GCOV_COUNTERS];
+static unsigned fn_ctr_mask; /* Mask of counters used. */
+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;
/* The names of the counter tables. */
static GTY(()) rtx ctr_labels[GCOV_COUNTERS];
+/* 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 *));
{
const counts_entry_t *entry = of;
- return htab_hash_string (entry->function_name) ^ entry->ctr;
+ return entry->ident * GCOV_COUNTERS + entry->ctr;
}
static int
const counts_entry_t *entry1 = of1;
const counts_entry_t *entry2 = of2;
- return !strcmp (entry1->function_name, entry2->function_name)
- && entry1->ctr == entry2->ctr;
+ return entry1->ident == entry2->ident && entry1->ctr == entry2->ctr;
}
static void
{
counts_entry_t *entry = of;
- free (entry->function_name);
free (entry->counts);
free (entry);
}
static void
read_counts_file ()
{
- char *function_name_buffer = NULL;
- unsigned version, ix, checksum = -1;
+ gcov_unsigned_t fn_ident = 0;
+ gcov_unsigned_t version, checksum = -1;
+ unsigned ix;
counts_entry_t *summaried = NULL;
unsigned seen_summary = 0;
-
+ gcov_unsigned_t tag;
+ int error = 0;
+
if (!gcov_open (da_file_name, 1))
return;
else if ((version = gcov_read_unsigned ()) != GCOV_VERSION)
{
char v[4], e[4];
- unsigned required = GCOV_VERSION;
+ gcov_unsigned_t required = GCOV_VERSION;
for (ix = 4; ix--; required >>= 8, version >>= 8)
{
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;
+ gcov_unsigned_t length;
+ gcov_position_t offset;
- tag = gcov_read_unsigned ();
length = gcov_read_unsigned ();
offset = gcov_position ();
if (tag == GCOV_TAG_FUNCTION)
{
- const char *string = gcov_read_string ();
- free (function_name_buffer);
- function_name_buffer = string ? xstrdup (string) : NULL;
+ fn_ident = gcov_read_unsigned ();
checksum = gcov_read_unsigned ();
if (seen_summary)
{
entry->summary.sum_max += csum->sum_max;
}
}
- else if (GCOV_TAG_IS_COUNTER (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 ix;
- elt.function_name = function_name_buffer;
+ elt.ident = fn_ident;
elt.ctr = GCOV_COUNTER_FOR_TAG (tag);
slot = (counts_entry_t **) htab_find_slot
if (!entry)
{
*slot = entry = xcalloc (1, sizeof (counts_entry_t));
- entry->function_name = xstrdup (elt.function_name);
+ entry->ident = elt.ident;
entry->ctr = elt.ctr;
entry->checksum = checksum;
entry->summary.num = n_counts;
else if (entry->checksum != checksum
|| entry->summary.num != n_counts)
{
- warning ("profile mismatch for `%s'", function_name_buffer);
+ warning ("coverage mismatch for function %u", fn_ident);
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 (elt.ctr >= GCOV_COUNTERS_SUMMABLE)
+ {
+ warning ("cannot merge separate %s counters for function %u",
+ 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))
{
entry->chain = summaried;
summaried = entry;
}
for (ix = 0; ix != n_counts; ix++)
entry->counts[ix] += gcov_read_counter ();
+ skip_merge:;
}
- gcov_seek (offset, length);
+ gcov_sync (offset, length);
if ((error = gcov_is_error ()))
- {
- warning (error < 0 ? "`%s' has overflowed" : "`%s' is corrupted",
- da_file_name);
- htab_delete (counts_hash);
- break;
- }
+ break;
}
- free (function_name_buffer);
+ if (!gcov_is_eof ())
+ {
+ warning (error < 0 ? "`%s' has overflowed" : "`%s' is corrupted",
+ da_file_name);
+ htab_delete (counts_hash);
+ }
+
gcov_close ();
}
return NULL;
}
- elt.function_name
- = (char *) IDENTIFIER_POINTER
- (DECL_ASSEMBLER_NAME (current_function_decl));
+ elt.ident = current_function_funcdef_no + 1;
elt.ctr = counter;
entry = htab_find (counts_hash, &elt);
if (!entry)
{
- warning ("No profile for function '%s' found.", elt.function_name);
+ warning ("no coverage for function '%s' found.", IDENTIFIER_POINTER
+ (DECL_ASSEMBLER_NAME (current_function_decl)));
return 0;
}
if (expected != entry->summary.num
|| compute_checksum () != entry->checksum)
{
- warning ("profile mismatch for `%s'", elt.function_name);
+ warning ("coverage mismatch for `%s'", IDENTIFIER_POINTER
+ (DECL_ASSEMBLER_NAME (current_function_decl)));
return NULL;
}
-
+
if (summary)
*summary = &entry->summary;
return entry->counts;
}
-/* Generate a MEM rtl to access COUNTER NO . */
+/* Allocate NUM counters of type COUNTER. Returns non-zero if the
+ allocation succeeded. */
-rtx
-coverage_counter_ref (unsigned counter, unsigned no)
+int
+coverage_counter_alloc (unsigned counter, unsigned num)
{
- enum machine_mode mode = mode_for_size (GCOV_TYPE_SIZE, MODE_INT, 0);
- rtx ref;
-
+ if (no_coverage)
+ return 0;
+
+ if (!num)
+ return 1;
+
if (!ctr_labels[counter])
{
/* Generate and save a copy of this so it can be shared. */
ASM_GENERATE_INTERNAL_LABEL (buf, "LPBX", counter + 1);
ctr_labels[counter] = gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (buf));
}
- if (no + 1 > fn_n_ctrs[counter])
- {
- fn_n_ctrs[counter] = no + 1;
- fn_ctr_mask |= 1 << counter;
- }
+ fn_b_ctrs[counter] = fn_n_ctrs[counter];
+ fn_n_ctrs[counter] += num;
+ fn_ctr_mask |= 1 << counter;
+ return 1;
+}
+
+/* Generate a MEM rtl to access COUNTER NO. */
+
+rtx
+coverage_counter_ref (unsigned counter, unsigned no)
+{
+ unsigned gcov_size = tree_low_cst (TYPE_SIZE (GCOV_TYPE_NODE), 1);
+ enum machine_mode mode = mode_for_size (gcov_size, MODE_INT, 0);
+ rtx ref;
- no += prg_n_ctrs[counter];
- ref = plus_constant (ctr_labels[counter],
- GCOV_TYPE_SIZE / BITS_PER_UNIT * no);
+ if (no >= fn_n_ctrs[counter] - fn_b_ctrs[counter])
+ abort ();
+ no += prg_n_ctrs[counter] + fn_b_ctrs[counter];
+ ref = plus_constant (ctr_labels[counter], gcov_size / BITS_PER_UNIT * no);
ref = gen_rtx_MEM (mode, ref);
set_mem_alias_set (ref, new_alias_set ());
int
coverage_begin_output ()
{
+ if (no_coverage)
+ return 0;
+
if (!bbg_function_announced)
{
const char *file = DECL_SOURCE_FILE (current_function_decl);
/* Announce function */
offset = gcov_write_tag (GCOV_TAG_FUNCTION);
+ gcov_write_unsigned (current_function_funcdef_no + 1);
+ gcov_write_unsigned (compute_checksum ());
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);
{
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->ident = current_function_funcdef_no + 1;
item->checksum = compute_checksum ();
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] = 0;
+ fn_n_ctrs[i] = fn_b_ctrs[i] = 0;
}
prg_ctr_mask |= fn_ctr_mask;
fn_ctr_mask = 0;
{
tree type = (*lang_hooks.types.make_type) (RECORD_TYPE);
tree field, fields;
- tree string_type =
- build_pointer_type (build_qualified_type (char_type_node,
- TYPE_QUAL_CONST));
tree array_type;
- /* name */
- fields = build_decl (FIELD_DECL, NULL_TREE, string_type);
+ /* ident */
+ fields = build_decl (FIELD_DECL, NULL_TREE, unsigned_intSI_type_node);
/* checksum */
- field = build_decl (FIELD_DECL, NULL_TREE, unsigned_type_node);
+ field = build_decl (FIELD_DECL, NULL_TREE, unsigned_intSI_type_node);
TREE_CHAIN (field) = fields;
fields = field;
{
tree value = NULL_TREE;
tree fields = TYPE_FIELDS (type);
- 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));
unsigned ix;
tree array_value = NULL_TREE;
- /* name */
- TREE_TYPE (fname) =
- build_array_type (char_type_node,
- build_index_type (build_int_2 (name_len, 0)));
+ /* ident */
value = tree_cons (fields,
- build1 (ADDR_EXPR, string_type, fname),
+ convert (unsigned_intSI_type_node,
+ build_int_2 (function->ident, 0)),
value);
fields = TREE_CHAIN (fields);
/* checksum */
value = tree_cons (fields,
- convert (unsigned_type_node,
+ convert (unsigned_intSI_type_node,
build_int_2 (function->checksum, 0)),
value);
fields = TREE_CHAIN (fields);
{
tree type = (*lang_hooks.types.make_type) (RECORD_TYPE);
tree field, fields = NULL_TREE;
-
+ tree gcov_ptr_type = build_pointer_type (GCOV_TYPE_NODE);
+ tree gcov_merge_fn_type;
+
/* counters */
- field = build_decl (FIELD_DECL, NULL_TREE, unsigned_type_node);
+ field = build_decl (FIELD_DECL, NULL_TREE, unsigned_intSI_type_node);
TREE_CHAIN (field) = fields;
fields = field;
/* values */
+ field = build_decl (FIELD_DECL, NULL_TREE, gcov_ptr_type);
+ TREE_CHAIN (field) = fields;
+ fields = field;
+
+ /* merge */
+ gcov_merge_fn_type =
+ build_function_type_list (void_type_node,
+ gcov_ptr_type, unsigned_type_node,
+ NULL_TREE);
field = build_decl (FIELD_DECL, NULL_TREE,
- build_pointer_type (make_signed_type (GCOV_TYPE_SIZE)));
+ build_pointer_type (gcov_merge_fn_type));
TREE_CHAIN (field) = fields;
fields = field;
{
tree value = NULL_TREE;
tree fields = TYPE_FIELDS (type);
+ tree fn;
/* counters */
value = tree_cons (fields,
- convert (unsigned_type_node,
+ convert (unsigned_intSI_type_node,
build_int_2 (prg_n_ctrs[counter], 0)),
value);
fields = TREE_CHAIN (fields);
}
else
value = tree_cons (fields, null_pointer_node, value);
+ fields = TREE_CHAIN (fields);
+
+ fn = build_decl (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;
+ value = tree_cons (fields,
+ build1 (ADDR_EXPR, TREE_TYPE (fields), fn),
+ value);
value = build_constructor (type, nreverse (value));
const_type = build_qualified_type (type, TYPE_QUAL_CONST);
/* Version ident */
- field = build_decl (FIELD_DECL, NULL_TREE, long_unsigned_type_node);
+ field = build_decl (FIELD_DECL, NULL_TREE, unsigned_intSI_type_node);
TREE_CHAIN (field) = fields;
fields = field;
- value = tree_cons (field, convert (long_unsigned_type_node,
+ value = tree_cons (field, convert (unsigned_intSI_type_node,
build_int_2 (GCOV_VERSION, 0)),
value);
char *ctor_name;
tree ctor;
rtx gcov_info_address;
- int save_flag_inline_functions = flag_inline_functions;
+ no_coverage = 1; /* Disable any further coverage. */
+
if (!prg_ctr_mask)
return;
TREE_PUBLIC (ctor) = ! targetm.have_ctors_dtors;
TREE_USED (ctor) = 1;
DECL_RESULT (ctor) = build_decl (RESULT_DECL, NULL_TREE, void_type_node);
+ DECL_UNINLINABLE (ctor) = 1;
ctor = (*lang_hooks.decls.pushdecl) (ctor);
rest_of_decl_compilation (ctor, 0, 1, 0);
current_function_decl = ctor;
DECL_INITIAL (ctor) = error_mark_node;
make_decl_rtl (ctor, NULL);
- init_function_start (ctor, input_filename, lineno);
+ init_function_start (ctor, input_filename, input_line);
(*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);
+ expand_function_end (input_filename, input_line, 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;
{
int len = strlen (filename);
+ /* Name of da file. */
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 ();
-
- /* Open the bbg output file. */
+ /* Name of bbg file. */
bbg_file_name = (char *) xmalloc (len + strlen (GCOV_GRAPH_SUFFIX) + 1);
strcpy (bbg_file_name, filename);
strcat (bbg_file_name, GCOV_GRAPH_SUFFIX);
+
+ read_counts_file ();
}
/* Performs file-level cleanup. Close graph file, generate coverage