diff options
-rw-r--r-- | gcc/ChangeLog | 48 | ||||
-rw-r--r-- | gcc/Makefile.in | 49 | ||||
-rw-r--r-- | gcc/coverage.c | 1146 | ||||
-rw-r--r-- | gcc/coverage.h (renamed from gcc/profile.h) | 18 | ||||
-rw-r--r-- | gcc/gcov-dump.c | 1 | ||||
-rw-r--r-- | gcc/gcov-io.c | 431 | ||||
-rw-r--r-- | gcc/gcov-io.h | 469 | ||||
-rw-r--r-- | gcc/libgcov.c | 3 | ||||
-rw-r--r-- | gcc/loop-init.c | 1 | ||||
-rw-r--r-- | gcc/profile.c | 1176 | ||||
-rw-r--r-- | gcc/rtl.h | 8 | ||||
-rw-r--r-- | gcc/toplev.c | 13 | ||||
-rw-r--r-- | gcc/tracer.c | 4 |
13 files changed, 1802 insertions, 1565 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog index b9eb79bf76f..43c1f055748 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,34 @@ +2003-04-21 Nathan Sidwell <nathan@codesourcery.com> + + Break out coverage routines to new file. + * Makefile.in (COVERAGE_H): New variable + (C_OBJS): Add coverage.o + (coverage.o): New target. + (profile.o, loop-init.o, sched-ebb.o, predict.o, tracer.o): Adjust + dependencies. + (GTFILES): Adjust. + (gt-coverage.h): New target. + (gt-profile.h): Remove. + * profile.h: Remove. Move to ... + * coverage.h: ... here. New. #include gcov-io.h. + * gcov-io.h: Move function definitions to ... + * gcov-io.c: ... here. New. + * profile.c: Move coverage routines to coverage.c. + (instrument_edges, get_exec_counts, branch_prob, init_branch_prob, + end_branch_prob): Adjust. + * coverage.c: New. Coverage routines from profile.c + (coverage_counter_ref, coverage_init, coverage_finish, + coverage_end_function, coverage_begin_output, + coverage_counter_ref, get_coverage_counts): Define. + * gcov-dump.c, gcov.c: #include gcov-io.c. + * libgcov.c: Likewise. Adjust. + * loop-init.c: Don't #include profile.h + * tracer.c, predict.c, sched-ebb.c: Adjust #includes. + * rtl.h: Add coverage prototypes. + * toplev.c (compile_file): Init coverage, not branch_prob. + Always call coverage_finish. + (rest_of_compilation): Call coverage_end_function. + 2003-04-21 Matt Kraai <kraai@alumni.cmu.edu> * config/rs6000/rs6000.md (*movsf_softfloat): Add "h" <- "0" case. @@ -417,12 +448,14 @@ Sat Apr 19 14:56:17 CEST 2003 Jan Hubicka <jh@suse.cz> Fri Apr 18 01:28:51 CEST 2003 Jan Hubicka <jh@suse.cz> * gcov-dump.c (tag_summary): Remove max_sum - * gcov-io.h (gcov_summary, gcov_write_summary, gcov_read_summary): Kill - max_sum. - * libgcov.c (gcov_exit): Do one pass over the data. Make error message - more verbose. - - * emit-rtl.c (subreg_hard_regno): Check that register is representable. + * gcov-io.h (gcov_summary, gcov_write_summary, + gcov_read_summary): Kill max_sum. + * libgcov.c (gcov_exit): Do one pass over the data. Make error + message more verbose. + + * emit-rtl.c (subreg_hard_regno): Check that register is + representable. + * reload.c (reload_inner_reg_of_subreg): When register is not representable, reload the whole thing. (find_reloads): Likewsie. @@ -430,7 +463,8 @@ Fri Apr 18 01:28:51 CEST 2003 Jan Hubicka <jh@suse.cz> * profile.c (compute_branch_probabilities): Cleanup sanity checking; allow negative probabilities for edges from the call to exit. - (branch_prob): Do not add fake edges for functions that may return twice + (branch_prob): Do not add fake edges for functions that may return + twice. 2003-04-17 DJ Delorie <dj@redhat.com> diff --git a/gcc/Makefile.in b/gcc/Makefile.in index ceff352623b..67e94128b76 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -636,6 +636,7 @@ TREE_H = tree.h tree.def $(MACHMODE_H) tree-check.h version.h builtins.def \ location.h BASIC_BLOCK_H = basic-block.h bitmap.h sbitmap.h varray.h $(PARTITION_H) \ hard-reg-set.h +COVERAGE_H = coverage.h gcov-io.h gcov-iov.h DEMANGLE_H = $(srcdir)/../include/demangle.h RECOG_H = recog.h EXPR_H = expr.h @@ -803,8 +804,8 @@ C_OBJS = c-parse.o c-lang.o c-pretty-print.o $(C_AND_OBJC_OBJS) OBJS = alias.o bb-reorder.o bitmap.o builtins.o caller-save.o calls.o \ cfg.o cfganal.o cfgbuild.o cfgcleanup.o cfglayout.o cfgloop.o \ cfgloopanal.o cfgloopmanip.o loop-init.o loop-unswitch.o loop-unroll.o \ - cfgrtl.o combine.o conflict.o convert.o cse.o cselib.o dbxout.o \ - debug.o df.o diagnostic.o dojump.o doloop.o dominance.o \ + cfgrtl.o combine.o conflict.o convert.o coverage.o cse.o cselib.o \ + dbxout.o debug.o df.o diagnostic.o dojump.o doloop.o dominance.o \ dwarf2asm.o dwarf2out.o dwarfout.o emit-rtl.o except.o explow.o \ expmed.o expr.o final.o flow.o fold-const.o function.o gcse.o \ genrtl.o ggc-common.o global.o graph.o gtype-desc.o \ @@ -1584,6 +1585,10 @@ cgraph.o : cgraph.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(TREE_H) \ langhooks.h tree-inline.h toplev.h flags.h ggc.h $(TARGET_H) cgraph.h gt-cgraph.h cgraphunit.o : cgraphunit.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(TREE_H) \ langhooks.h tree-inline.h toplev.h flags.h ggc.h $(TARGET_H) cgraph.h +coverage.o : coverage.c gcov-io.c $(CONFIG_H) $(SYSTEM_H) coretypes.h \ + $(TM_H) $(RTL_H) $(TREE_H) flags.h output.h $(REGS_H) $(EXPR_H) function.h \ + toplev.h $(GGC_H) $(TARGET_H) langhooks.h $(COVERAGE_H) libfuncs.h \ + gt-coverage.h $(HASHTAB_H) cselib.o : cselib.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) $(REGS_H) \ hard-reg-set.h flags.h real.h insn-config.h $(RECOG_H) $(EXPR_H) toplev.h \ output.h function.h cselib.h $(GGC_H) $(TM_P_H) gt-cselib.h @@ -1615,10 +1620,9 @@ df.o : df.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \ $(BASIC_BLOCK_H) df.h $(FIBHEAP_H) conflict.o : conflict.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(OBSTACK_H) \ $(HASHTAB_H) $(RTL_H) hard-reg-set.h $(BASIC_BLOCK_H) -profile.o : profile.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) $(TREE_H) \ - flags.h insn-config.h output.h $(REGS_H) $(EXPR_H) function.h \ - gcov-io.h gcov-iov.h toplev.h $(GGC_H) hard-reg-set.h $(BASIC_BLOCK_H) \ - $(TARGET_H) langhooks.h profile.h libfuncs.h gt-profile.h $(HASHTAB_H) +profile.o : profile.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \ + $(TREE_H) flags.h output.h $(REGS_H) $(EXPR_H) function.h \ + toplev.h $(BASIC_BLOCK_H) $(COVERAGE_H) loop.o : loop.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) flags.h $(LOOP_H) \ insn-config.h $(REGS_H) hard-reg-set.h $(RECOG_H) $(EXPR_H) \ real.h $(PREDICT_H) $(BASIC_BLOCK_H) function.h cfgloop.h \ @@ -1656,7 +1660,7 @@ cfgloopanal.o : cfgloopanal.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) \ cfgloopmanip.o : cfgloopmanip.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) \ $(BASIC_BLOCK_H) hard-reg-set.h cfgloop.h cfglayout.h output.h coretypes.h $(TM_H) loop-init.o : loop-init.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) \ - $(BASIC_BLOCK_H) hard-reg-set.h cfgloop.h cfglayout.h profile.h \ + $(BASIC_BLOCK_H) hard-reg-set.h cfgloop.h cfglayout.h \ coretypes.h $(TM_H) loop-unswitch.o : loop-unswitch.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) $(TM_H) \ $(BASIC_BLOCK_H) hard-reg-set.h cfgloop.h cfglayout.h params.h \ @@ -1728,7 +1732,7 @@ sched-rgn.o : sched-rgn.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \ $(INSN_ATTR_H) toplev.h $(RECOG_H) except.h $(TM_P_H) $(TARGET_H) sched-ebb.o : sched-ebb.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \ sched-int.h $(BASIC_BLOCK_H) $(REGS_H) hard-reg-set.h flags.h insn-config.h function.h \ - $(INSN_ATTR_H) toplev.h $(RECOG_H) except.h $(TM_P_H) $(PARAMS_H) profile.h + $(INSN_ATTR_H) toplev.h $(RECOG_H) except.h $(TM_P_H) $(PARAMS_H) sched-vis.o : sched-vis.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \ sched-int.h hard-reg-set.h $(BASIC_BLOCK_H) $(INSN_ATTR_H) $(REGS_H) $(TM_P_H) \ $(TARGET_H) real.h @@ -1747,14 +1751,14 @@ sreal.o: sreal.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) sreal.h predict.o: predict.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) $(TREE_H) \ flags.h insn-config.h $(BASIC_BLOCK_H) $(REGS_H) hard-reg-set.h output.h toplev.h \ $(RECOG_H) function.h except.h $(EXPR_H) $(TM_P_H) $(PREDICT_H) sreal.h \ - $(PARAMS_H) $(TARGET_H) cfgloop.h + $(PARAMS_H) $(TARGET_H) cfgloop.h $(COVERAGE_H) lists.o: lists.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) toplev.h $(RTL_H) $(GGC_H) bb-reorder.o : bb-reorder.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \ $(RTL_H) $(BASIC_BLOCK_H) flags.h output.h cfglayout.h $(FIBHEAP_H) \ $(TARGET_H) tracer.o : tracer.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) $(TREE_H) \ $(BASIC_BLOCK_H) hard-reg-set.h output.h cfglayout.h flags.h \ - $(PARAMS_H) profile.h + $(PARAMS_H) $(COVERAGE_H) cfglayout.o : cfglayout.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \ $(RTL_H) $(TREE_H) insn-config.h $(BASIC_BLOCK_H) hard-reg-set.h output.h \ function.h cfglayout.h cfgloop.h $(TARGET_H) @@ -1966,10 +1970,10 @@ s-preds: genpreds$(build_exeext) $(srcdir)/move-if-change GTFILES = $(srcdir)/location.h $(srcdir)/coretypes.h $(srcdir)/cpplib.h \ $(host_xm_file_list) $(tm_file_list) $(HASHTAB_H) $(SPLAY_TREE_H) \ - $(srcdir)/bitmap.h $(srcdir)/function.h $(srcdir)/rtl.h $(srcdir)/optabs.h \ - $(srcdir)/tree.h $(srcdir)/libfuncs.h $(srcdir)/hashtable.h $(srcdir)/real.h \ - $(srcdir)/varray.h $(srcdir)/ssa.h $(srcdir)/insn-addr.h $(srcdir)/cselib.h \ - $(srcdir)/basic-block.h $(srcdir)/location.h \ + $(srcdir)/bitmap.h $(srcdir)/coverage.c $(srcdir)/function.h $(srcdir)/rtl.h \ + $(srcdir)/optabs.h $(srcdir)/tree.h $(srcdir)/libfuncs.h $(srcdir)/hashtable.h \ + $(srcdir)/real.h $(srcdir)/varray.h $(srcdir)/ssa.h $(srcdir)/insn-addr.h \ + $(srcdir)/cselib.h $(srcdir)/basic-block.h $(srcdir)/location.h \ $(srcdir)/c-common.h $(srcdir)/c-tree.h \ $(srcdir)/alias.c $(srcdir)/bitmap.c $(srcdir)/cselib.c $(srcdir)/cgraph.c \ $(srcdir)/dbxout.c $(srcdir)/dwarf2out.c $(srcdir)/dwarf2asm.c \ @@ -1988,14 +1992,15 @@ GTFILES_FILES_FILES = @all_gtfiles_files_files@ GTFILES_LANG_DIR_NAMES = @subdirs@ GTFILES_SRCDIR = @srcdir@ -gt-cgraph.h gtype-desc.h gtype-desc.c gt-except.h gt-function.h : s-gtype; @true -gt-integrate.h gt-stmt.h gt-tree.h gt-varasm.h gt-emit-rtl.h : s-gtype; @true -gt-explow.h gt-stor-layout.h gt-regclass.h gt-lists.h : s-gtype; @true -gt-alias.h gt-cselib.h gt-fold-const.h gt-gcse.h gt-profile.h : s-gtype; @true -gt-expr.h gt-sdbout.h gt-optabs.h gt-bitmap.h gt-dwarf2out.h : s-gtype ; @true -gt-ra-build.h gt-reg-stack.h gt-dwarf2asm.h gt-dbxout.h : s-gtype ; @true -gt-c-common.h gt-c-decl.h gt-c-parse.h gt-c-pragma.h : s-gtype; @true -gt-c-objc-common.h gtype-c.h gt-location.h gt-stringpool.h : s-gtype ; @true +gt-cgraph.h gt-coverage.h gtype-desc.h gtype-desc.c gt-except.h \ +gt-function.h gt-integrate.h gt-stmt.h gt-tree.h gt-varasm.h \ +gt-emit-rtl.h gt-explow.h gt-stor-layout.h gt-regclass.h \ +gt-lists.h gt-alias.h gt-cselib.h gt-fold-const.h gt-gcse.h \ +gt-expr.h gt-sdbout.h gt-optabs.h gt-bitmap.h \ +gt-dwarf2out.h gt-ra-build.h gt-reg-stack.h gt-dwarf2asm.h \ +gt-dbxout.h gt-c-common.h gt-c-decl.h gt-c-parse.h \ +gt-c-pragma.h gt-c-objc-common.h gtype-c.h gt-location.h \ +gt-stringpool.h : s-gtype ; @true gtyp-gen.h: Makefile echo "/* This file is machine generated. Do not edit. */" > tmp-gtyp.h diff --git a/gcc/coverage.c b/gcc/coverage.c new file mode 100644 index 00000000000..07c56a3ffc7 --- /dev/null +++ b/gcc/coverage.c @@ -0,0 +1,1146 @@ +/* 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. + 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. + Further mangled by Nathan Sidwell, CodeSourcery + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 2, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +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. */ + + +#define GCOV_LINKAGE + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tm.h" +#include "rtl.h" +#include "tree.h" +#include "flags.h" +#include "output.h" +#include "regs.h" +#include "expr.h" +#include "function.h" +#include "toplev.h" +#include "ggc.h" +#include "target.h" +#include "coverage.h" +#include "libfuncs.h" +#include "langhooks.h" +#include "hashtab.h" + +#include "gcov-io.c" + +struct function_list +{ + struct function_list *next; /* next function */ + const char *name; /* function name */ + unsigned cfg_checksum; /* function checksum */ + unsigned n_counter_sections; /* number of counter sections */ + struct gcov_counter_section counter_sections[MAX_COUNTER_SECTIONS]; + /* the sections */ +}; + +/* Counts information for a function. */ +typedef struct counts_entry +{ + /* We hash by */ + char *function_name; + unsigned section; + + /* Store */ + unsigned checksum; + unsigned n_counts; + gcov_type *counts; + unsigned merged; + gcov_type max_counter; + gcov_type max_counter_sum; + + /* Workspace */ + struct counts_entry *chain; + +} counts_entry_t; + +static struct function_list *functions_head = 0; +static struct function_list **functions_tail = &functions_head; + +/* Instantiate the profile info structure. */ + +struct profile_info profile_info; + +/* Name of the output file for coverage output 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; + +/* 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 (of) + const void *of; +{ + const counts_entry_t *entry = of; + + return htab_hash_string (entry->function_name) ^ entry->section; +} + +static int +htab_counts_entry_eq (of1, of2) + const void *of1; + const void *of2; +{ + const counts_entry_t *entry1 = of1; + const counts_entry_t *entry2 = of2; + + return !strcmp (entry1->function_name, entry2->function_name) + && entry1->section == entry2->section; +} + +static void +htab_counts_entry_del (of) + void *of; +{ + counts_entry_t *entry = of; + + free (entry->function_name); + free (entry->counts); + free (entry); +} + +/* Read in the counts file, if available. */ + +static void +read_counts_file () +{ + char *function_name_buffer = NULL; + unsigned version, ix, checksum = -1; + counts_entry_t *summaried = NULL; + unsigned seen_summary = 0; + + if (!gcov_open (da_file_name, 1)) + return; + + if (gcov_read_unsigned () != GCOV_DATA_MAGIC) + { + warning ("`%s' is not a gcov data file", da_file_name); + gcov_close (); + return; + } + else if ((version = 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_close (); + return; + } + + counts_hash = htab_create (10, + htab_counts_entry_hash, htab_counts_entry_eq, + htab_counts_entry_del); + while (!gcov_is_eof ()) + { + unsigned tag, length; + unsigned long offset; + int error; + + tag = gcov_read_unsigned (); + length = gcov_read_unsigned (); + offset = gcov_position (); + if (tag == GCOV_TAG_FUNCTION) + { + const char *string = gcov_read_string (); + free (function_name_buffer); + function_name_buffer = string ? xstrdup (string) : NULL; + checksum = gcov_read_unsigned (); + if (seen_summary) + { + /* We have already seen a summary, this means that this + new function begins a new set of program runs. We + must unlink the summaried chain. */ + counts_entry_t *entry, *chain; + + for (entry = summaried; entry; entry = chain) + { + chain = entry->chain; + + entry->max_counter_sum += entry->max_counter; + entry->chain = NULL; + } + summaried = NULL; + seen_summary = 0; + } + } + else if (tag == GCOV_TAG_PROGRAM_SUMMARY) + { + counts_entry_t *entry; + struct gcov_summary summary; + + gcov_read_summary (&summary); + seen_summary = 1; + for (entry = summaried; entry; entry = entry->chain) + { + entry->merged += summary.runs; + if (entry->max_counter < summary.arc_sum_max) + entry->max_counter = summary.arc_sum_max; + } + } + else if (GCOV_TAG_IS_SUBTAG (GCOV_TAG_FUNCTION, tag) + && function_name_buffer) + { + counts_entry_t **slot, *entry, elt; + unsigned n_counts = length / 8; + unsigned ix; + + elt.function_name = function_name_buffer; + elt.section = 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)); + } + else if (entry->checksum != checksum || entry->n_counts != n_counts) + { + warning ("profile mismatch for `%s'", function_name_buffer); + htab_delete (counts_hash); + break; + } + + /* This should always be true for a just allocated entry, + and always false for an existing one. Check this way, in + case the gcov file is corrupt. */ + if (!entry->chain || summaried != entry) + { + entry->chain = summaried; + summaried = entry; + } + for (ix = 0; ix != n_counts; ix++) + entry->counts[ix] += gcov_read_counter (); + } + gcov_seek (offset, length); + if ((error = gcov_is_error ())) + { + warning (error < 0 ? "`%s' has overflowed" : "`%s' 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) +{ + counts_entry_t *entry, elt; + + profile_info.max_counter_in_program = 0; + profile_info.count_profiles_merged = 0; + + /* No hash table, no counts. */ + if (!counts_hash) + { + static int warned = 0; + + if (!warned++) + warning ("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; + } + + if (expected != entry->n_counts + || compute_checksum () != entry->checksum) + { + warning ("profile mismatch for `%s'", elt.function_name); + return NULL; + } + + profile_info.count_profiles_merged = entry->merged; + profile_info.max_counter_in_program = entry->max_counter_sum; + + return entry->counts; +} + +/* Generate a checksum for a string. CHKSUM is the current + checksum. */ + +static unsigned +checksum_string (unsigned chksum, const char *string) +{ + do + { + unsigned value = *string << 24; + unsigned ix; + + for (ix = 8; ix--; value <<= 1) + { + unsigned feedback; + + feedback = (value ^ chksum) & 0x80000000 ? 0x04c11db7 : 0; + chksum <<= 1; + chksum ^= feedback; + } + } + while (*string++); + + return chksum; +} + +/* Compute checksum for the current function. We generate a CRC32. */ + +static unsigned +compute_checksum () +{ + unsigned chksum = DECL_SOURCE_LINE (current_function_decl); + + chksum = checksum_string (chksum, DECL_SOURCE_FILE (current_function_decl)); + chksum = checksum_string + (chksum, IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (current_function_decl))); + + return chksum; +} + +/* 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. */ + +int +coverage_begin_output () +{ + 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; + } + return !gcov_is_error (); +} + +/* Finish coverage data for the current function. Verify no output + error has occurred. Save function coverage counts. */ + +void +coverage_end_function () +{ + 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) + { + case GCOV_TAG_ARC_COUNTS: + return profiler_label; + default: + abort (); + } +} + +/* Creates fields of struct counter_section (in gcov-io.h). */ +static tree +build_counter_section_fields () +{ + tree field, fields; + + /* tag */ + fields = build_decl (FIELD_DECL, NULL_TREE, unsigned_type_node); + + /* n_counters */ + field = build_decl (FIELD_DECL, NULL_TREE, unsigned_type_node); + TREE_CHAIN (field) = fields; + fields = field; + + return fields; +} + +/* Creates value of struct counter_section (in gcov-io.h). */ +static tree +build_counter_section_value (tag, n_counters) + unsigned tag; + unsigned n_counters; +{ + tree value = NULL_TREE; + + /* tag */ + value = tree_cons (NULL_TREE, + convert (unsigned_type_node, + build_int_2 (tag, 0)), + value); + + /* n_counters */ + value = tree_cons (NULL_TREE, + convert (unsigned_type_node, + build_int_2 (n_counters, 0)), + value); + + return value; +} + +/* Creates fields of struct counter_section_data (in gcov-io.h). */ +static tree +build_counter_section_data_fields () +{ + tree field, fields, gcov_type, gcov_ptr_type; + + gcov_type = make_signed_type (GCOV_TYPE_SIZE); + gcov_ptr_type = + build_pointer_type (build_qualified_type (gcov_type, + TYPE_QUAL_CONST)); + + /* tag */ + fields = build_decl (FIELD_DECL, NULL_TREE, unsigned_type_node); + + /* n_counters */ + field = build_decl (FIELD_DECL, NULL_TREE, unsigned_type_node); + TREE_CHAIN (field) = fields; + fields = field; + + /* counters */ + field = build_decl (FIELD_DECL, NULL_TREE, gcov_ptr_type); + TREE_CHAIN (field) = fields; + fields = field; + + return fields; +} + +/* Creates value of struct counter_section_data (in gcov-io.h). */ +static tree +build_counter_section_data_value (tag, n_counters) + unsigned tag; + unsigned n_counters; +{ + tree value = NULL_TREE, counts_table, gcov_type, gcov_ptr_type; + + gcov_type = make_signed_type (GCOV_TYPE_SIZE); + gcov_ptr_type + = build_pointer_type (build_qualified_type + (gcov_type, TYPE_QUAL_CONST)); + + /* tag */ + value = tree_cons (NULL_TREE, + convert (unsigned_type_node, + build_int_2 (tag, 0)), + value); + + /* n_counters */ + value = tree_cons (NULL_TREE, + convert (unsigned_type_node, + build_int_2 (n_counters, 0)), + value); + + /* counters */ + if (n_counters) + { + tree gcov_type_array_type = + build_array_type (gcov_type, + build_index_type (build_int_2 (n_counters - 1, + 0))); + counts_table = + build (VAR_DECL, gcov_type_array_type, NULL_TREE, NULL_TREE); + TREE_STATIC (counts_table) = 1; + DECL_NAME (counts_table) = get_identifier (XSTR (label_for_tag (tag), 0)); + assemble_variable (counts_table, 0, 0, 0); + counts_table = build1 (ADDR_EXPR, gcov_ptr_type, counts_table); + } + else + counts_table = null_pointer_node; + + value = tree_cons (NULL_TREE, counts_table, value); + + return value; +} + +/* Creates fields for struct function_info type (in gcov-io.h). */ +static tree +build_function_info_fields () +{ + tree field, fields, counter_section_fields, counter_section_type; + tree counter_sections_ptr_type; + tree string_type = + build_pointer_type (build_qualified_type (char_type_node, + TYPE_QUAL_CONST)); + /* name */ + fields = build_decl (FIELD_DECL, NULL_TREE, string_type); + + /* checksum */ + field = build_decl (FIELD_DECL, NULL_TREE, unsigned_type_node); + TREE_CHAIN (field) = fields; + fields = field; + + /* n_counter_sections */ + field = build_decl (FIELD_DECL, NULL_TREE, unsigned_type_node); + TREE_CHAIN (field) = fields; + fields = field; + + /* counter_sections */ + counter_section_fields = build_counter_section_fields (); + counter_section_type = (*lang_hooks.types.make_type) (RECORD_TYPE); + finish_builtin_struct (counter_section_type, "__counter_section", + counter_section_fields, NULL_TREE); + counter_sections_ptr_type = + build_pointer_type + (build_qualified_type (counter_section_type, + TYPE_QUAL_CONST)); + field = build_decl (FIELD_DECL, NULL_TREE, counter_sections_ptr_type); + TREE_CHAIN (field) = fields; + fields = field; + + return fields; +} + +/* Creates value for struct function_info (in gcov-io.h). */ +static tree +build_function_info_value (function) + struct function_list *function; +{ + tree value = NULL_TREE; + size_t name_len = strlen (function->name); + tree fname = build_string (name_len + 1, function->name); + tree string_type = + build_pointer_type (build_qualified_type (char_type_node, + TYPE_QUAL_CONST)); + tree counter_section_fields, counter_section_type, counter_sections_value; + tree counter_sections_ptr_type, counter_sections_array_type; + unsigned i; + + /* name */ + TREE_TYPE (fname) = + build_array_type (char_type_node, + build_index_type (build_int_2 (name_len, 0))); + value = tree_cons (NULL_TREE, + build1 (ADDR_EXPR, + string_type, + fname), + value); + + /* checksum */ + value = tree_cons (NULL_TREE, + convert (unsigned_type_node, + build_int_2 (function->cfg_checksum, 0)), + value); + + /* n_counter_sections */ + + value = tree_cons (NULL_TREE, + convert (unsigned_type_node, + build_int_2 (function->n_counter_sections, 0)), + value); + + /* counter_sections */ + counter_section_fields = build_counter_section_fields (); + counter_section_type = (*lang_hooks.types.make_type) (RECORD_TYPE); + counter_sections_ptr_type = + build_pointer_type + (build_qualified_type (counter_section_type, + TYPE_QUAL_CONST)); + counter_sections_array_type = + build_array_type (counter_section_type, + build_index_type ( + build_int_2 (function->n_counter_sections - 1, + 0))); + + counter_sections_value = NULL_TREE; + for (i = 0; i < function->n_counter_sections; i++) + { + tree counter_section_value + = build_counter_section_value (function->counter_sections[i].tag, + function->counter_sections[i].n_counters); + set_purpose (counter_section_value, counter_section_fields); + counter_sections_value = + tree_cons (NULL_TREE, + build_constructor (counter_section_type, + nreverse (counter_section_value)), + counter_sections_value); + } + finish_builtin_struct (counter_section_type, "__counter_section", + counter_section_fields, NULL_TREE); + + if (function->n_counter_sections) + { + counter_sections_value = + build_constructor (counter_sections_array_type, + nreverse (counter_sections_value)), + counter_sections_value = build1 (ADDR_EXPR, + counter_sections_ptr_type, + counter_sections_value); + } + else + counter_sections_value = null_pointer_node; + + value = tree_cons (NULL_TREE, counter_sections_value, value); + + return value; +} + +/* Creates fields of struct gcov_info type (in gcov-io.h). */ +static tree +build_gcov_info_fields (gcov_info_type) + tree gcov_info_type; +{ + tree field, fields; + char *filename; + int filename_len; + tree string_type = + build_pointer_type (build_qualified_type (char_type_node, + TYPE_QUAL_CONST)); + tree function_info_fields, function_info_type, function_info_ptr_type; + tree counter_section_data_fields, counter_section_data_type; + tree counter_section_data_ptr_type; + + /* Version ident */ + fields = build_decl (FIELD_DECL, NULL_TREE, long_unsigned_type_node); + + /* next -- NULL */ + field = build_decl (FIELD_DECL, NULL_TREE, + build_pointer_type + (build_qualified_type + (gcov_info_type, TYPE_QUAL_CONST))); + TREE_CHAIN (field) = fields; + fields = field; + + /* Filename */ + filename = getpwd (); + filename = (filename && da_file_name[0] != '/' + ? concat (filename, "/", da_file_name, NULL) + : da_file_name); + filename_len = strlen (filename); + if (filename != da_file_name) + free (filename); + + field = build_decl (FIELD_DECL, NULL_TREE, string_type); + TREE_CHAIN (field) = fields; + fields = field; + + /* Workspace */ + field = build_decl (FIELD_DECL, NULL_TREE, long_integer_type_node); + TREE_CHAIN (field) = fields; + fields = field; + + /* number of functions */ + field = build_decl (FIELD_DECL, NULL_TREE, unsigned_type_node); + TREE_CHAIN (field) = fields; + fields = field; + + /* function_info table */ + function_info_fields = build_function_info_fields (); + function_info_type = (*lang_hooks.types.make_type) (RECORD_TYPE); + finish_builtin_struct (function_info_type, "__function_info", + function_info_fields, NULL_TREE); + function_info_ptr_type = + build_pointer_type + (build_qualified_type (function_info_type, + TYPE_QUAL_CONST)); + field = build_decl (FIELD_DECL, NULL_TREE, function_info_ptr_type); + TREE_CHAIN (field) = fields; + fields = field; + + /* n_counter_sections */ + field = build_decl (FIELD_DECL, NULL_TREE, unsigned_type_node); + TREE_CHAIN (field) = fields; + fields = field; + + /* counter sections */ + counter_section_data_fields = build_counter_section_data_fields (); + counter_section_data_type = (*lang_hooks.types.make_type) (RECORD_TYPE); + finish_builtin_struct (counter_section_data_type, "__counter_section_data", + counter_section_data_fields, NULL_TREE); + counter_section_data_ptr_type = + build_pointer_type + (build_qualified_type (counter_section_data_type, + TYPE_QUAL_CONST)); + field = build_decl (FIELD_DECL, NULL_TREE, counter_section_data_ptr_type); + TREE_CHAIN (field) = fields; + fields = field; + + return fields; +} + +/* Creates struct gcov_info value (in gcov-io.h). */ +static tree +build_gcov_info_value () +{ + tree value = NULL_TREE; + tree filename_string; + char *filename; + int filename_len; + unsigned n_functions, i; + struct function_list *item; + tree string_type = + build_pointer_type (build_qualified_type (char_type_node, + TYPE_QUAL_CONST)); + tree function_info_fields, function_info_type, function_info_ptr_type; + tree functions; + tree counter_section_data_fields, counter_section_data_type; + tree counter_section_data_ptr_type, counter_sections; + + /* Version ident */ + value = tree_cons (NULL_TREE, + convert (long_unsigned_type_node, + build_int_2 (GCOV_VERSION, 0)), + value); + + /* next -- NULL */ + value = tree_cons (NULL_TREE, null_pointer_node, value); + + /* Filename */ + filename = getpwd (); + filename = (filename && da_file_name[0] != '/' + ? concat (filename, "/", da_file_name, NULL) + : da_file_name); + filename_len = strlen (filename); + filename_string = build_string (filename_len + 1, filename); + if (filename != da_file_name) + free (filename); + TREE_TYPE (filename_string) = + build_array_type (char_type_node, + build_index_type (build_int_2 (filename_len, 0))); + value = tree_cons (NULL_TREE, + build1 (ADDR_EXPR, + string_type, + filename_string), + value); + + /* Workspace */ + value = tree_cons (NULL_TREE, + convert (long_integer_type_node, integer_zero_node), + value); + + /* number of functions */ + n_functions = 0; + for (item = functions_head; item != 0; item = item->next, n_functions++) + continue; + value = tree_cons (NULL_TREE, + convert (unsigned_type_node, + build_int_2 (n_functions, 0)), + value); + + /* function_info table */ + function_info_fields = build_function_info_fields (); + function_info_type = (*lang_hooks.types.make_type) (RECORD_TYPE); + function_info_ptr_type = + build_pointer_type ( + build_qualified_type (function_info_type, + TYPE_QUAL_CONST)); + functions = NULL_TREE; + for (item = functions_head; item != 0; item = item->next) + { + tree function_info_value = build_function_info_value (item); + set_purpose (function_info_value, function_info_fields); + functions + = tree_cons (NULL_TREE, + build_constructor (function_info_type, + nreverse (function_info_value)), + functions); + } + finish_builtin_struct (function_info_type, "__function_info", + function_info_fields, NULL_TREE); + + /* Create constructor for array. */ + if (n_functions) + { + tree array_type; + + array_type = build_array_type ( + function_info_type, + build_index_type (build_int_2 (n_functions - 1, 0))); + functions = build_constructor (array_type, nreverse (functions)); + functions = build1 (ADDR_EXPR, + function_info_ptr_type, + functions); + } + else + functions = null_pointer_node; + + value = tree_cons (NULL_TREE, functions, value); + + /* n_counter_sections */ + value = tree_cons (NULL_TREE, + convert (unsigned_type_node, + build_int_2 (profile_info.n_sections, 0)), + value); + + /* counter sections */ + counter_section_data_fields = build_counter_section_data_fields (); + counter_section_data_type = (*lang_hooks.types.make_type) (RECORD_TYPE); + counter_sections = NULL_TREE; + for (i = 0; i < profile_info.n_sections; i++) + { + tree counter_sections_value = + build_counter_section_data_value ( + profile_info.section_info[i].tag, + profile_info.section_info[i].n_counters); + set_purpose (counter_sections_value, counter_section_data_fields); + counter_sections = + tree_cons (NULL_TREE, + build_constructor (counter_section_data_type, + nreverse (counter_sections_value)), + counter_sections); + } + finish_builtin_struct (counter_section_data_type, "__counter_section_data", + counter_section_data_fields, NULL_TREE); + counter_section_data_ptr_type = + build_pointer_type + (build_qualified_type (counter_section_data_type, + TYPE_QUAL_CONST)); + + if (profile_info.n_sections) + { + tree cst_type = build_index_type (build_int_2 (profile_info.n_sections-1, + 0)); + cst_type = build_array_type (counter_section_data_type, cst_type); + counter_sections = build_constructor (cst_type, + nreverse (counter_sections)); + counter_sections = build1 (ADDR_EXPR, + counter_section_data_ptr_type, + counter_sections); + } + else + counter_sections = null_pointer_node; + value = tree_cons (NULL_TREE, counter_sections, value); + + return value; +} + +/* 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. */ + +static void +create_coverage () +{ + 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; + + gcov_info_type = (*lang_hooks.types.make_type) (RECORD_TYPE); + gcov_info_fields = build_gcov_info_fields (gcov_info_type); + gcov_info_value = build_gcov_info_value (); + set_purpose (gcov_info_value, gcov_info_fields); + finish_builtin_struct (gcov_info_type, "__gcov_info", + gcov_info_fields, NULL_TREE); + + gcov_info = build (VAR_DECL, gcov_info_type, NULL_TREE, NULL_TREE); + DECL_INITIAL (gcov_info) = + build_constructor (gcov_info_type, nreverse (gcov_info_value)); + + TREE_STATIC (gcov_info) = 1; + ASM_GENERATE_INTERNAL_LABEL (name, "LPBX", 0); + DECL_NAME (gcov_info) = get_identifier (name); + + /* Build structure. */ + assemble_variable (gcov_info, 0, 0, 0); + + /* Build the constructor function to invoke __gcov_init. */ + ctor_name = concat (IDENTIFIER_POINTER (get_file_function_name ('I')), + "_GCOV", NULL); + ctor = build_decl (FUNCTION_DECL, get_identifier (ctor_name), + build_function_type (void_type_node, NULL_TREE)); + free (ctor_name); + DECL_EXTERNAL (ctor) = 0; + + /* It can be a static function as long as collect2 does not have + to scan the object file to find its ctor/dtor routine. */ + TREE_PUBLIC (ctor) = ! targetm.have_ctors_dtors; + TREE_USED (ctor) = 1; + DECL_RESULT (ctor) = build_decl (RESULT_DECL, NULL_TREE, void_type_node); + + ctor = (*lang_hooks.decls.pushdecl) (ctor); + rest_of_decl_compilation (ctor, 0, 1, 0); + announce_function (ctor); + current_function_decl = ctor; + DECL_INITIAL (ctor) = error_mark_node; + make_decl_rtl (ctor, NULL); + init_function_start (ctor, input_filename, lineno); + (*lang_hooks.decls.pushlevel) (0); + expand_function_start (ctor, 0); + cfun->arc_profile = 0; + + /* Actually generate the code to call __gcov_init. */ + gcov_info_address = force_reg (Pmode, XEXP (DECL_RTL (gcov_info), 0)); + emit_library_call (gcov_init_libfunc, LCT_NORMAL, VOIDmode, 1, + gcov_info_address, Pmode); + + expand_function_end (input_filename, lineno, 0); + (*lang_hooks.decls.poplevel) (1, 0, 1); + + /* Since ctor isn't in the list of globals, it would never be emitted + when it's considered to be 'safe' for inlining, so turn off + flag_inline_functions. */ + flag_inline_functions = 0; + + rest_of_compilation (ctor); + + /* Reset flag_inline_functions to its original value. */ + flag_inline_functions = save_flag_inline_functions; + + if (! quiet_flag) + fflush (asm_out_file); + current_function_decl = NULL_TREE; + + if (targetm.have_ctors_dtors) + (* targetm.asm_out.constructor) (XEXP (DECL_RTL (ctor), 0), + DEFAULT_INIT_PRIORITY); +} + +/* 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; + + 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) +{ + 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]; + + 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; + + 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 ()); + + return ref; +} + + +/* Perform file-level initialization. Read in data file, generate name + of graph file. */ + +void +coverage_init (filename) + const char *filename; +{ + int len = strlen (filename); + + 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. */ + bbg_file_name = (char *) xmalloc (len + strlen (GCOV_GRAPH_SUFFIX) + 1); + strcpy (bbg_file_name, filename); + strcat (bbg_file_name, GCOV_GRAPH_SUFFIX); +} + +/* Performs file-level cleanup. Close graph file, generate coverage + variables and constructor. */ + +void +coverage_finish () +{ + create_coverage (); + if (bbg_file_opened) + { + int error = gcov_close (); + + if (error) + unlink (bbg_file_name); +#if SELF_COVERAGE + /* If the compiler is instrumented, we should not + unconditionally remove the counts file, because we might be + recompiling ourselves. The .da files are all removed during + copying the stage1 files. */ + if (error) +#endif + unlink (da_file_name); + } +} + + +#include "gt-coverage.h" diff --git a/gcc/profile.h b/gcc/coverage.h index 5d681e8f5db..f6ead44a597 100644 --- a/gcc/profile.h +++ b/gcc/coverage.h @@ -1,5 +1,5 @@ -/* profile.h - Defines data exported from profile.c to other passes. - Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc. +/* coverage.h - Defines data exported from coverage.c + Copyright (C) 1998, 1999, 2000, 2001, 2003 Free Software Foundation, Inc. This file is part of GCC. @@ -18,8 +18,10 @@ 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. */ -#ifndef GCC_PROFILE_H -#define GCC_PROFILE_H +#ifndef GCC_COVERAGE_H +#define GCC_COVERAGE_H + +#include "gcov-io.h" /* The number of different counter sections. */ #define MAX_COUNTER_SECTIONS 1 @@ -57,6 +59,14 @@ struct profile_info extern struct profile_info profile_info; +extern void coverage_init (const char *); +extern void coverage_finish (void); +extern void coverage_end_function (void); +extern int coverage_begin_output (void); +extern rtx coverage_counter_ref (unsigned /*tag*/, unsigned/*num*/); + +gcov_type *get_coverage_counts (unsigned /*tag*/, unsigned /*expected*/); struct section_info *find_counters_section PARAMS ((unsigned)); + #endif diff --git a/gcc/gcov-dump.c b/gcc/gcov-dump.c index 9b17d2145fe..fd9d1228467 100644 --- a/gcc/gcov-dump.c +++ b/gcc/gcov-dump.c @@ -25,6 +25,7 @@ Boston, MA 02111-1307, USA. */ #include <getopt.h> #define IN_GCOV (-1) #include "gcov-io.h" +#include "gcov-io.c" static void dump_file PARAMS ((const char *)); static void print_prefix PARAMS ((const char *, unsigned)); diff --git a/gcc/gcov-io.c b/gcc/gcov-io.c new file mode 100644 index 00000000000..94002a9c42b --- /dev/null +++ b/gcc/gcov-io.c @@ -0,0 +1,431 @@ +/* File format for coverage information + Copyright (C) 1996, 1997, 1998, 2000, 2002, + 2003 Free Software Foundation, Inc. + Contributed by Bob Manson <manson@cygnus.com>. + Completely remangled by Nathan Sidwell <nathan@codesourcery.com>. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 2, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +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. */ + +/* Routines declared in gcov-io.h. This file should be #included by + another source file, after having #included gcov-io.h. */ + +/* Open a gcov file. NAME is the name of the file to open and MODE + indicates whether a new file should be created, or an existing file + opened for modification. If MODE is >= 0 an existing file will be + opened, if possible, and if MODE is <= 0, a new file will be + created. Use MODE=0 to attempt to reopen an existing file and then + fall back on creating a new one. Return zero on failure, >0 on + opening an existing file and <0 on creating a new one. */ + +GCOV_LINKAGE int +gcov_open (const char *name, int mode) +{ + int result = 1; + size_t alloc = 1024; +#if defined (TARGET_HAS_F_SETLKW) && IN_LIBGCOV + struct flock s_flock; + + s_flock.l_type = F_WRLCK; + s_flock.l_whence = SEEK_SET; + s_flock.l_start = 0; + s_flock.l_len = 0; /* Until EOF. */ + s_flock.l_pid = getpid (); +#endif + + if (gcov_var.file) + abort (); + gcov_var.position = gcov_var.length = 0; + gcov_var.error = gcov_var.modified = 0; + if (mode >= 0) + gcov_var.file = fopen (name, "r+b"); + if (!gcov_var.file && mode <= 0) + { + result = -1; + gcov_var.file = fopen (name, "w+b"); + } + if (!gcov_var.file) + return 0; + +#if defined (TARGET_HAS_F_SETLKW) && IN_LIBGCOV + while (fcntl (fileno (gcov_var.file), F_SETLKW, &s_flock) + && errno == EINTR) + continue; +#endif + + if (result >= 0) + { + if (fseek (gcov_var.file, 0, SEEK_END)) + { + fclose (gcov_var.file); + gcov_var.file = 0; + return 0; + } + gcov_var.length = ftell (gcov_var.file); + fseek (gcov_var.file, 0, SEEK_SET); + alloc += gcov_var.length; + } + if (alloc > gcov_var.alloc) + { + if (gcov_var.buffer) + free (gcov_var.buffer); + gcov_var.alloc = alloc; +#if IN_LIBGCOV + gcov_var.buffer = malloc (gcov_var.alloc); + if (!gcov_var.buffer) + { + fclose (gcov_var.file); + gcov_var.file = 0; + gcov_var.length = 0; + gcov_var.alloc = 0; + return 0; + } +#else + gcov_var.buffer = xmalloc (gcov_var.alloc); +#endif + } + if (result >= 0 + && fread (gcov_var.buffer, gcov_var.length, 1, gcov_var.file) != 1) + { + fclose (gcov_var.file); + gcov_var.file = 0; + gcov_var.length = 0; + return 0; + } + return result; +} + +/* Close the current gcov file. Flushes data to disk. Returns nonzero + on failure or error flag set. */ + +GCOV_LINKAGE int +gcov_close () +{ + int result = 0; + + if (gcov_var.file) + { + if (gcov_var.modified + && (fseek (gcov_var.file, 0, SEEK_SET) + || fwrite (gcov_var.buffer, gcov_var.length, + 1, gcov_var.file) != 1)) + result = 1; + fclose (gcov_var.file); + gcov_var.file = 0; + gcov_var.length = 0; + } +#if !IN_LIBGCOV + free (gcov_var.buffer); + gcov_var.alloc = 0; + gcov_var.buffer = 0; +#endif + return result ? 1 : gcov_var.error; +} + +#if !IN_GCOV +/* Allocate space to write BYTES bytes to the gcov file. Return a + pointer to those bytes, or NULL on failure. */ + +GCOV_LINKAGE unsigned char * +gcov_write_bytes (unsigned bytes) +{ + char unsigned *result; + + if (gcov_var.position + bytes > gcov_var.alloc) + { + size_t new_size = (gcov_var.alloc + bytes) * 3 / 2; + + if (!gcov_var.buffer) + return 0; +#if IN_LIBGCOV + result = realloc (gcov_var.buffer, new_size); + if (!result) + { + free (gcov_var.buffer); + gcov_var.buffer = 0; + gcov_var.alloc = 0; + gcov_var.position = gcov_var.length = 0; + gcov_var.error = 1; + return 0; + } +#else + result = xrealloc (gcov_var.buffer, new_size); +#endif + gcov_var.alloc = new_size; + gcov_var.buffer = result; + } + + result = &gcov_var.buffer[gcov_var.position]; + gcov_var.position += bytes; + gcov_var.modified = 1; + if (gcov_var.position > gcov_var.length) + gcov_var.length = gcov_var.position; + return result; +} + +/* Write unsigned VALUE to coverage file. Sets error flag + appropriately. */ + +GCOV_LINKAGE void +gcov_write_unsigned (unsigned value) +{ + unsigned char *buffer = gcov_write_bytes (4); + unsigned ix; + + if (!buffer) + return; + for (ix = 4; ix--; ) + { + buffer[ix] = value; + value >>= 8; + } + if (sizeof (value) > 4 && value) + gcov_var.error = -1; + + return; +} + +/* Write counter VALUE to coverage file. Sets error flag + appropriately. */ + +#if IN_LIBGCOV +GCOV_LINKAGE void +gcov_write_counter (gcov_type value) +{ + unsigned char *buffer = gcov_write_bytes (8); + unsigned ix; + + if (!buffer) + return; + for (ix = 8; ix--; ) + { + buffer[ix] = value; + value >>= 8; + } + if ((sizeof (value) > 8 && value) || value < 0) + gcov_var.error = -1; + return; +} +#endif /* IN_LIBGCOV */ + +/* Write STRING to coverage file. Sets error flag on file + error, overflow flag on overflow */ + +GCOV_LINKAGE void +gcov_write_string (const char *string) +{ + unsigned length = 0; + unsigned pad = 0; + unsigned rem = 0; + unsigned char *buffer; + + if (string) + { + length = strlen (string); + rem = 4 - (length & 3); + } + + buffer = gcov_write_bytes (4 + length + rem); + if (buffer) + { + unsigned ix; + unsigned value = length; + + for (ix = 4; ix--; ) + { + buffer[ix] = value; + value >>= 8; + } + memcpy (buffer + 4, string, length); + memcpy (buffer + 4 + length, &pad, rem); + } +} + +/* Write a tag TAG and reserve space for the record length. Return a + value to be used for gcov_write_length. */ + +GCOV_LINKAGE unsigned long +gcov_write_tag (unsigned tag) +{ + unsigned long result = gcov_var.position; + unsigned char *buffer = gcov_write_bytes (8); + unsigned ix; + + if (!buffer) + return 0; + for (ix = 4; ix--; ) + { + buffer[ix] = tag; + tag >>= 8; + } + memset (buffer + 4, 0, 4); + return result; +} + +/* Write a record length using POSITION, which was returned by + gcov_write_tag. The current file position is the end of the + record, and is restored before returning. Returns nonzero on + overflow. */ + +GCOV_LINKAGE void +gcov_write_length (unsigned long position) +{ + if (position) + { + unsigned length = gcov_var.position - position - 8; + unsigned char *buffer = &gcov_var.buffer[position + 4]; + unsigned ix; + + for (ix = 4; ix--; ) + { + buffer[ix] = length; + length >>= 8; + } + } +} + +#if IN_LIBGCOV +/* Write a summary structure to the gcov file. Return non-zero on + overflow. */ + +GCOV_LINKAGE void +gcov_write_summary (unsigned tag, const struct gcov_summary *summary) +{ + unsigned long base; + + base = gcov_write_tag (tag); + gcov_write_unsigned (summary->checksum); + gcov_write_unsigned (summary->runs); + gcov_write_unsigned (summary->arcs); + gcov_write_counter (summary->arc_sum); + gcov_write_counter (summary->arc_max_one); + gcov_write_counter (summary->arc_sum_max); + gcov_write_length (base); +} +#endif /* IN_LIBGCOV */ + +#endif /*!IN_GCOV */ + +/* Return a pointer to read BYTES bytes from the gcov file. Returns + NULL on failure (read past EOF). */ + +GCOV_LINKAGE const unsigned char * +gcov_read_bytes (unsigned bytes) +{ + const unsigned char *result; + + if (gcov_var.position + bytes > gcov_var.length) + { + gcov_var.error = 1; + return 0; + } + + result = &gcov_var.buffer[gcov_var.position]; + gcov_var.position += bytes; + return result; +} + +/* Read unsigned value from a coverage file. Sets error flag on file + error, overflow flag on overflow */ + +GCOV_LINKAGE unsigned +gcov_read_unsigned () +{ + unsigned value = 0; + unsigned ix; + const unsigned char *buffer = gcov_read_bytes (4); + + if (!buffer) + return 0; + for (ix = sizeof (value); ix < 4; ix++) + if (buffer[ix]) + gcov_var.error = -1; + for (ix = 0; ix != 4; ix++) + { + value <<= 8; + value |= buffer[ix]; + } + return value; +} + +/* Read counter value from a coverage file. Sets error flag on file + error, overflow flag on overflow */ + +GCOV_LINKAGE gcov_type +gcov_read_counter () +{ + gcov_type value = 0; + unsigned ix; + const unsigned char *buffer = gcov_read_bytes (8); + + if (!buffer) + return 0; + for (ix = sizeof (value); ix < 8; ix++) + if (buffer[ix]) + gcov_var.error = -1; + for (ix = 0; ix != 8; ix++) + { + value <<= 8; + value |= buffer[ix]; + } + if (value < 0) + gcov_var.error = -1; + return value; +} + +/* Read string from coverage file. Returns a pointer to a static + buffer, or NULL on empty string. You must copy the string before + calling another gcov function. */ + +GCOV_LINKAGE const char * +gcov_read_string () +{ + unsigned length = gcov_read_unsigned (); + + if (!length) + return 0; + + length += 4 - (length & 3); + return (const char *) gcov_read_bytes (length); +} + +GCOV_LINKAGE void +gcov_read_summary (struct gcov_summary *summary) +{ + summary->checksum = gcov_read_unsigned (); + summary->runs = gcov_read_unsigned (); + summary->arcs = gcov_read_unsigned (); + summary->arc_sum = gcov_read_counter (); + summary->arc_max_one = gcov_read_counter (); + summary->arc_sum_max = gcov_read_counter (); +} + + +#if IN_GCOV > 0 +/* Return the modification time of the current gcov file. */ + +GCOV_LINKAGE time_t +gcov_time () +{ + struct stat status; + + if (fstat (fileno (gcov_var.file), &status)) + return 0; + else + return status.st_mtime; +} +#endif /* IN_GCOV */ diff --git a/gcc/gcov-io.h b/gcc/gcov-io.h index 4da674921dc..634f6b3d534 100644 --- a/gcc/gcov-io.h +++ b/gcc/gcov-io.h @@ -1,5 +1,6 @@ /* File format for coverage information - Copyright (C) 1996, 1997, 1998, 2000, 2002 Free Software Foundation, Inc. + Copyright (C) 1996, 1997, 1998, 2000, 2002, + 2003 Free Software Foundation, Inc. Contributed by Bob Manson <manson@cygnus.com>. Completely remangled by Nathan Sidwell <nathan@codesourcery.com>. @@ -160,6 +161,17 @@ typedef HOST_WIDEST_INT gcov_type; #endif #endif +/* In lib gcov we want function linkage to be static, so we do not + polute the global namespace. In the compiler we want it extern, so + that they can be accessed from elsewhere. */ +#if IN_LIBGCOV || IN_GCOV +#define GCOV_LINKAGE static +#else +#ifndef GCOV_LINKAGE +#define GCOV_LINKAGE extern +#endif +#endif + /* File suffixes. */ #define GCOV_DATA_SUFFIX ".da" #define GCOV_GRAPH_SUFFIX ".bbg" @@ -227,7 +239,7 @@ struct gcov_summary by write_profile must match these. */ /* Information about section of counters for a function. */ -struct counter_section +struct gcov_counter_section { unsigned tag; /* Tag of the section. */ unsigned n_counters; /* Number of counters in the section. */ @@ -235,7 +247,7 @@ struct counter_section #if IN_LIBGCOV /* Information about section of counters for an object file. */ -struct counter_section_data +struct gcov_counter_section_data { unsigned tag; /* Tag of the section. */ unsigned n_counters; /* Number of counters in the section. */ @@ -243,12 +255,12 @@ struct counter_section_data }; /* Information about a single function. */ -struct function_info +struct gcov_function_info { const char *name; /* (mangled) name of function */ unsigned checksum; /* function checksum */ unsigned n_counter_sections; /* Number of types of counters */ - const struct counter_section *counter_sections; + const struct gcov_counter_section *counter_sections; /* The section descriptions */ }; @@ -262,10 +274,10 @@ struct gcov_info long wkspc; /* libgcc workspace */ unsigned n_functions; /* number of functions */ - const struct function_info *functions; /* table of functions */ + const struct gcov_function_info *functions; /* table of functions */ unsigned n_counter_sections; /* Number of types of counters */ - const struct counter_section_data *counter_sections; + const struct gcov_counter_section_data *counter_sections; /* The data to be put into the sections. */ }; @@ -275,18 +287,12 @@ extern void __gcov_init (struct gcov_info *); /* Called before fork, to avoid double counting. */ extern void __gcov_flush (void); -/* Since this file is used in both host and target files, and we don't - include ansidecl.h in target files, provide some necessary macros. */ -#ifndef ATTRIBUTE_UNUSED -# define ATTRIBUTE_UNUSED __attribute__ ((__unused__)) -#endif - #endif /* IN_LIBGCOV */ /* Because small reads and writes, interspersed with seeks cause lots of disk activity, we buffer the entire count files. */ -static struct gcov_var +GCOV_LINKAGE struct gcov_var { FILE *file; size_t position; @@ -298,26 +304,26 @@ static struct gcov_var } gcov_var; /* Functions for reading and writing gcov files. */ -static int gcov_open (const char */*name*/, int /*truncate*/); -static int gcov_close (void); +GCOV_LINKAGE int gcov_open (const char */*name*/, int /*truncate*/); +GCOV_LINKAGE int gcov_close (void); #if !IN_GCOV -static unsigned char *gcov_write_bytes (unsigned); -static void gcov_write_unsigned (unsigned); +GCOV_LINKAGE unsigned char *gcov_write_bytes (unsigned); +GCOV_LINKAGE void gcov_write_unsigned (unsigned); #if IN_LIBGCOV -static void gcov_write_counter (gcov_type); +GCOV_LINKAGE void gcov_write_counter (gcov_type); #endif -static void gcov_write_string (const char *); -static unsigned long gcov_write_tag (unsigned); -static void gcov_write_length (unsigned long /*position*/); +GCOV_LINKAGE void gcov_write_string (const char *); +GCOV_LINKAGE unsigned long gcov_write_tag (unsigned); +GCOV_LINKAGE void gcov_write_length (unsigned long /*position*/); #if IN_LIBGCOV -static void gcov_write_summary (unsigned, const struct gcov_summary *); +GCOV_LINKAGE void gcov_write_summary (unsigned, const struct gcov_summary *); #endif #endif /* !IN_GCOV */ -static const unsigned char *gcov_read_bytes (unsigned); -static unsigned gcov_read_unsigned (void); -static gcov_type gcov_read_counter (void); -static const char *gcov_read_string (void); -static void gcov_read_summary (struct gcov_summary *); +GCOV_LINKAGE const unsigned char *gcov_read_bytes (unsigned); +GCOV_LINKAGE unsigned gcov_read_unsigned (void); +GCOV_LINKAGE gcov_type gcov_read_counter (void); +GCOV_LINKAGE const char *gcov_read_string (void); +GCOV_LINKAGE void gcov_read_summary (struct gcov_summary *); static unsigned long gcov_position (void); static void gcov_seek (unsigned long /*base*/, unsigned /*length */); @@ -325,399 +331,8 @@ static unsigned long gcov_seek_end (void); static int gcov_is_eof (void); static int gcov_is_error (void); #if IN_GCOV > 0 -static time_t gcov_time (void); -#endif - -/* Open a gcov file. NAME is the name of the file to open and MODE - indicates whether a new file should be created, or an existing file - opened for modification. If MODE is >= 0 an existing file will be - opened, if possible, and if MODE is <= 0, a new file will be - created. Use MODE=0 to attempt to reopen an existing file and then - fall back on creating a new one. Return zero on failure, >0 on - opening an existing file and <0 on creating a new one. */ - -static int -gcov_open (const char *name, int mode) -{ - int result = 1; - size_t alloc = 1024; -#if defined (TARGET_HAS_F_SETLKW) && IN_LIBGCOV - struct flock s_flock; - - s_flock.l_type = F_WRLCK; - s_flock.l_whence = SEEK_SET; - s_flock.l_start = 0; - s_flock.l_len = 0; /* Until EOF. */ - s_flock.l_pid = getpid (); -#endif - - if (gcov_var.file) - abort (); - gcov_var.position = gcov_var.length = 0; - gcov_var.error = gcov_var.modified = 0; - if (mode >= 0) - gcov_var.file = fopen (name, "r+b"); - if (!gcov_var.file && mode <= 0) - { - result = -1; - gcov_var.file = fopen (name, "w+b"); - } - if (!gcov_var.file) - return 0; - -#if defined (TARGET_HAS_F_SETLKW) && IN_LIBGCOV - while (fcntl (fileno (gcov_var.file), F_SETLKW, &s_flock) - && errno == EINTR) - continue; -#endif - - if (result >= 0) - { - if (fseek (gcov_var.file, 0, SEEK_END)) - { - fclose (gcov_var.file); - gcov_var.file = 0; - return 0; - } - gcov_var.length = ftell (gcov_var.file); - fseek (gcov_var.file, 0, SEEK_SET); - alloc += gcov_var.length; - } - if (alloc > gcov_var.alloc) - { - if (gcov_var.buffer) - free (gcov_var.buffer); - gcov_var.alloc = alloc; -#if IN_LIBGCOV - gcov_var.buffer = malloc (gcov_var.alloc); - if (!gcov_var.buffer) - { - fclose (gcov_var.file); - gcov_var.file = 0; - gcov_var.length = 0; - gcov_var.alloc = 0; - return 0; - } -#else - gcov_var.buffer = xmalloc (gcov_var.alloc); -#endif - } - if (result >= 0 - && fread (gcov_var.buffer, gcov_var.length, 1, gcov_var.file) != 1) - { - fclose (gcov_var.file); - gcov_var.file = 0; - gcov_var.length = 0; - return 0; - } - return result; -} - -/* Close the current gcov file. Flushes data to disk. Returns nonzero - on failure or error flag set. */ - -static int -gcov_close () -{ - int result = 0; - - if (gcov_var.file) - { - if (gcov_var.modified - && (fseek (gcov_var.file, 0, SEEK_SET) - || fwrite (gcov_var.buffer, gcov_var.length, - 1, gcov_var.file) != 1)) - result = 1; - fclose (gcov_var.file); - gcov_var.file = 0; - gcov_var.length = 0; - } -#if !IN_LIBGCOV - free (gcov_var.buffer); - gcov_var.alloc = 0; - gcov_var.buffer = 0; +GCOV_LINKAGE time_t gcov_time (void); #endif - return result ? 1 : gcov_var.error; -} - -#if !IN_GCOV -/* Allocate space to write BYTES bytes to the gcov file. Return a - pointer to those bytes, or NULL on failure. */ - -static unsigned char * -gcov_write_bytes (unsigned bytes) -{ - char unsigned *result; - - if (gcov_var.position + bytes > gcov_var.alloc) - { - size_t new_size = (gcov_var.alloc + bytes) * 3 / 2; - - if (!gcov_var.buffer) - return 0; -#if IN_LIBGCOV - result = realloc (gcov_var.buffer, new_size); - if (!result) - { - free (gcov_var.buffer); - gcov_var.buffer = 0; - gcov_var.alloc = 0; - gcov_var.position = gcov_var.length = 0; - gcov_var.error = 1; - return 0; - } -#else - result = xrealloc (gcov_var.buffer, new_size); -#endif - gcov_var.alloc = new_size; - gcov_var.buffer = result; - } - - result = &gcov_var.buffer[gcov_var.position]; - gcov_var.position += bytes; - gcov_var.modified = 1; - if (gcov_var.position > gcov_var.length) - gcov_var.length = gcov_var.position; - return result; -} - -/* Write unsigned VALUE to coverage file. Sets error flag - appropriately. */ - -static void -gcov_write_unsigned (unsigned value) -{ - unsigned char *buffer = gcov_write_bytes (4); - unsigned ix; - - if (!buffer) - return; - for (ix = 4; ix--; ) - { - buffer[ix] = value; - value >>= 8; - } - if (sizeof (value) > 4 && value) - gcov_var.error = -1; - - return; -} - -/* Write counter VALUE to coverage file. Sets error flag - appropriately. */ - -#if IN_LIBGCOV -static void -gcov_write_counter (gcov_type value) -{ - unsigned char *buffer = gcov_write_bytes (8); - unsigned ix; - - if (!buffer) - return; - for (ix = 8; ix--; ) - { - buffer[ix] = value; - value >>= 8; - } - if ((sizeof (value) > 8 && value) || value < 0) - gcov_var.error = -1; - return; -} -#endif /* IN_LIBGCOV */ - -/* Write STRING to coverage file. Sets error flag on file - error, overflow flag on overflow */ - -static void -gcov_write_string (const char *string) -{ - unsigned length = 0; - unsigned pad = 0; - unsigned rem = 0; - unsigned char *buffer; - - if (string) - { - length = strlen (string); - rem = 4 - (length & 3); - } - - buffer = gcov_write_bytes (4 + length + rem); - if (buffer) - { - unsigned ix; - unsigned value = length; - - for (ix = 4; ix--; ) - { - buffer[ix] = value; - value >>= 8; - } - memcpy (buffer + 4, string, length); - memcpy (buffer + 4 + length, &pad, rem); - } -} - -/* Write a tag TAG and reserve space for the record length. Return a - value to be used for gcov_write_length. */ - -static unsigned long -gcov_write_tag (unsigned tag) -{ - unsigned long result = gcov_var.position; - unsigned char *buffer = gcov_write_bytes (8); - unsigned ix; - - if (!buffer) - return 0; - for (ix = 4; ix--; ) - { - buffer[ix] = tag; - tag >>= 8; - } - memset (buffer + 4, 0, 4); - return result; -} - -/* Write a record length using POSITION, which was returned by - gcov_write_tag. The current file position is the end of the - record, and is restored before returning. Returns nonzero on - overflow. */ - -static void -gcov_write_length (unsigned long position) -{ - if (position) - { - unsigned length = gcov_var.position - position - 8; - unsigned char *buffer = &gcov_var.buffer[position + 4]; - unsigned ix; - - for (ix = 4; ix--; ) - { - buffer[ix] = length; - length >>= 8; - } - } -} - -#if IN_LIBGCOV -/* Write a summary structure to the gcov file. Return non-zero on - overflow. */ - -static void -gcov_write_summary (unsigned tag, const struct gcov_summary *summary) -{ - unsigned long base; - - base = gcov_write_tag (tag); - gcov_write_unsigned (summary->checksum); - gcov_write_unsigned (summary->runs); - gcov_write_unsigned (summary->arcs); - gcov_write_counter (summary->arc_sum); - gcov_write_counter (summary->arc_max_one); - gcov_write_counter (summary->arc_sum_max); - gcov_write_length (base); -} -#endif /* IN_LIBGCOV */ - -#endif /*!IN_GCOV */ - -/* Return a pointer to read BYTES bytes from the gcov file. Returns - NULL on failure (read past EOF). */ - -static const unsigned char * -gcov_read_bytes (unsigned bytes) -{ - const unsigned char *result; - - if (gcov_var.position + bytes > gcov_var.length) - { - gcov_var.error = 1; - return 0; - } - - result = &gcov_var.buffer[gcov_var.position]; - gcov_var.position += bytes; - return result; -} - -/* Read unsigned value from a coverage file. Sets error flag on file - error, overflow flag on overflow */ - -static unsigned -gcov_read_unsigned () -{ - unsigned value = 0; - unsigned ix; - const unsigned char *buffer = gcov_read_bytes (4); - - if (!buffer) - return 0; - for (ix = sizeof (value); ix < 4; ix++) - if (buffer[ix]) - gcov_var.error = -1; - for (ix = 0; ix != 4; ix++) - { - value <<= 8; - value |= buffer[ix]; - } - return value; -} - -/* Read counter value from a coverage file. Sets error flag on file - error, overflow flag on overflow */ - -static gcov_type -gcov_read_counter () -{ - gcov_type value = 0; - unsigned ix; - const unsigned char *buffer = gcov_read_bytes (8); - - if (!buffer) - return 0; - for (ix = sizeof (value); ix < 8; ix++) - if (buffer[ix]) - gcov_var.error = -1; - for (ix = 0; ix != 8; ix++) - { - value <<= 8; - value |= buffer[ix]; - } - if (value < 0) - gcov_var.error = -1; - return value; -} - -/* Read string from coverage file. Returns a pointer to a static - buffer, or NULL on empty string. You must copy the string before - calling another gcov function. */ - -static const char * -gcov_read_string () -{ - unsigned length = gcov_read_unsigned (); - - if (!length) - return 0; - - length += 4 - (length & 3); - return (const char *) gcov_read_bytes (length); -} - -#define GCOV_SUMMARY_LENGTH 44 -static void -gcov_read_summary (struct gcov_summary *summary) -{ - summary->checksum = gcov_read_unsigned (); - summary->runs = gcov_read_unsigned (); - summary->arcs = gcov_read_unsigned (); - summary->arc_sum = gcov_read_counter (); - summary->arc_max_one = gcov_read_counter (); - summary->arc_sum_max = gcov_read_counter (); -} /* Save the current position in the gcov file. */ @@ -770,18 +385,4 @@ gcov_is_error () return gcov_var.file ? gcov_var.error : 1; } -#if IN_GCOV > 0 -/* Return the modification time of the current gcov file. */ - -static time_t -gcov_time () -{ - struct stat status; - - if (fstat (fileno (gcov_var.file), &status)) - return 0; - else - return status.st_mtime; -} -#endif /* IN_GCOV */ #endif /* GCC_GCOV_IO_H */ diff --git a/gcc/libgcov.c b/gcc/libgcov.c index 7a1a3926c93..237da2d39e3 100644 --- a/gcc/libgcov.c +++ b/gcc/libgcov.c @@ -59,6 +59,7 @@ void __gcov_flush (void) { } #endif #define IN_LIBGCOV 1 #include "gcov-io.h" +#include "gcov-io.c" /* Chain of per-object gcov structures. */ static struct gcov_info *gcov_list; @@ -136,7 +137,7 @@ gcov_exit (void) int error; int merging; unsigned long base; - const struct function_info *fn_info; + const struct gcov_function_info *fn_info; gcov_type **counters; gcov_type *count_ptr; gcov_type object_max_one = 0; diff --git a/gcc/loop-init.c b/gcc/loop-init.c index d64d407ad94..6cae157e66d 100644 --- a/gcc/loop-init.c +++ b/gcc/loop-init.c @@ -27,7 +27,6 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA #include "basic-block.h" #include "cfgloop.h" #include "cfglayout.h" -#include "profile.h" /* Initialize loop optimizer. */ diff --git a/gcc/profile.c b/gcc/profile.c index afabe20a42f..3472bd05d9c 100644 --- a/gcc/profile.c +++ b/gcc/profile.c @@ -1,6 +1,6 @@ /* Calculate branch probabilities, and basic block execution counts. Copyright (C) 1990, 1991, 1992, 1993, 1994, 1996, 1997, 1998, 1999, - 2000, 2001 Free Software Foundation, Inc. + 2000, 2001, 2002, 2003 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. @@ -53,23 +53,13 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA #include "coretypes.h" #include "tm.h" #include "rtl.h" -#include "tree.h" #include "flags.h" -#include "insn-config.h" #include "output.h" #include "regs.h" #include "expr.h" #include "function.h" #include "toplev.h" -#include "ggc.h" -#include "hard-reg-set.h" -#include "basic-block.h" -#include "gcov-io.h" -#include "target.h" -#include "profile.h" -#include "libfuncs.h" -#include "langhooks.h" -#include "hashtab.h" +#include "coverage.h" /* Additional information about the edges we need. */ struct edge_info { @@ -91,17 +81,6 @@ struct bb_info { gcov_type pred_count; }; -struct function_list -{ - struct function_list *next; /* next function */ - const char *name; /* function name */ - unsigned cfg_checksum; /* function checksum */ - unsigned n_counter_sections; /* number of counter sections */ - struct counter_section counter_sections[MAX_COUNTER_SECTIONS]; - /* the sections */ -}; - - /* Counts information for a function. */ typedef struct counts_entry { @@ -122,9 +101,6 @@ typedef struct counts_entry } counts_entry_t; -static struct function_list *functions_head = 0; -static struct function_list **functions_tail = &functions_head; - #define EDGE_INFO(e) ((struct edge_info *) (e)->aux) #define BB_INFO(b) ((struct bb_info *) (b)->aux) @@ -134,21 +110,6 @@ static struct function_list **functions_tail = &functions_head; : ((bb) == EXIT_BLOCK_PTR \ ? last_basic_block + 1 : (bb)->index + 1)) -/* Instantiate the profile info structure. */ - -struct profile_info profile_info; - -/* Name and file pointer of the output file for the basic block graph. */ - -static char *bbg_file_name; - -/* Name and file pointer of the input file for the arc count data. */ - -static char *da_file_name; - -/* The name of the count table. Used by the edge profiling code. */ -static GTY(()) rtx profiler_label; - /* Collect statistics on the performance of this pass for the entire source file. */ @@ -168,24 +129,9 @@ static void find_spanning_tree PARAMS ((struct edge_list *)); static rtx gen_edge_profiler PARAMS ((int)); static void instrument_edges PARAMS ((struct edge_list *)); static void compute_branch_probabilities PARAMS ((void)); -static hashval_t htab_counts_entry_hash PARAMS ((const void *)); -static int htab_counts_entry_eq PARAMS ((const void *, const void *)); -static void htab_counts_entry_del PARAMS ((void *)); -static void read_counts_file PARAMS ((const char *)); static gcov_type * get_exec_counts PARAMS ((void)); -static unsigned compute_checksum PARAMS ((void)); static basic_block find_group PARAMS ((basic_block)); static void union_groups PARAMS ((basic_block, basic_block)); -static void set_purpose PARAMS ((tree, tree)); -static rtx label_for_tag PARAMS ((unsigned)); -static tree build_counter_section_fields PARAMS ((void)); -static tree build_counter_section_value PARAMS ((unsigned, unsigned)); -static tree build_counter_section_data_fields PARAMS ((void)); -static tree build_counter_section_data_value PARAMS ((unsigned, unsigned)); -static tree build_function_info_fields PARAMS ((void)); -static tree build_function_info_value PARAMS ((struct function_list *)); -static tree build_gcov_info_fields PARAMS ((tree)); -static tree build_gcov_info_value PARAMS ((void)); /* Add edge instrumentation code to the entire insn chain. @@ -200,7 +146,6 @@ instrument_edges (el) int num_instr_edges = 0; int num_edges = NUM_EDGES (el); basic_block bb; - struct section_info *section_info; remove_fake_edges (); FOR_BB_BETWEEN (bb, ENTRY_BLOCK_PTR, NULL, next_bb) @@ -218,196 +163,18 @@ instrument_edges (el) e->src->index, e->dest->index, EDGE_CRITICAL_P (e) ? " (and split)" : ""); insert_insn_on_edge ( - gen_edge_profiler (total_num_edges_instrumented - + num_instr_edges++), e); + gen_edge_profiler (num_instr_edges++), e); rebuild_jump_labels (e->insns); } e = e->succ_next; } } - section_info = find_counters_section (GCOV_TAG_ARC_COUNTS); - section_info->n_counters_now = num_instr_edges; - total_num_edges_instrumented += num_instr_edges; - section_info->n_counters = total_num_edges_instrumented; - total_num_blocks_created += num_edges; if (rtl_dump_file) fprintf (rtl_dump_file, "%d edges instrumented\n", num_instr_edges); } -static hashval_t -htab_counts_entry_hash (of) - const void *of; -{ - const counts_entry_t *entry = of; - - return htab_hash_string (entry->function_name) ^ entry->section; -} - -static int -htab_counts_entry_eq (of1, of2) - const void *of1; - const void *of2; -{ - const counts_entry_t *entry1 = of1; - const counts_entry_t *entry2 = of2; - - return !strcmp (entry1->function_name, entry2->function_name) - && entry1->section == entry2->section; -} - -static void -htab_counts_entry_del (of) - void *of; -{ - counts_entry_t *entry = of; - - free (entry->function_name); - free (entry->counts); - free (entry); -} - -static htab_t counts_hash = NULL; - -static void -read_counts_file (const char *name) -{ - char *function_name_buffer = NULL; - unsigned version, ix, checksum = -1; - counts_entry_t *summaried = NULL; - unsigned seen_summary = 0; - - if (!gcov_open (name, 1)) - { - warning ("file %s not found, execution counts assumed to be zero", name); - return; - } - - if (gcov_read_unsigned () != GCOV_DATA_MAGIC) - { - warning ("`%s' is not a gcov data file", name); - gcov_close (); - return; - } - else if ((version = gcov_read_unsigned ()) != GCOV_VERSION) - { - 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'", name, v, e); - gcov_close (); - return; - } - - counts_hash = htab_create (10, - htab_counts_entry_hash, htab_counts_entry_eq, - htab_counts_entry_del); - while (!gcov_is_eof ()) - { - unsigned tag, length; - unsigned long offset; - int error; - - tag = gcov_read_unsigned (); - length = gcov_read_unsigned (); - offset = gcov_position (); - if (tag == GCOV_TAG_FUNCTION) - { - const char *string = gcov_read_string (); - free (function_name_buffer); - function_name_buffer = string ? xstrdup (string) : NULL; - checksum = gcov_read_unsigned (); - if (seen_summary) - { - /* We have already seen a summary, this means that this - new function begins a new set of program runs. We - must unlink the summaried chain. */ - counts_entry_t *entry, *chain; - - for (entry = summaried; entry; entry = chain) - { - chain = entry->chain; - - entry->max_counter_sum += entry->max_counter; - entry->chain = NULL; - } - summaried = NULL; - seen_summary = 0; - } - } - else if (tag == GCOV_TAG_PROGRAM_SUMMARY) - { - counts_entry_t *entry; - struct gcov_summary summary; - - gcov_read_summary (&summary); - seen_summary = 1; - for (entry = summaried; entry; entry = entry->chain) - { - entry->merged += summary.runs; - if (entry->max_counter < summary.arc_sum_max) - entry->max_counter = summary.arc_sum_max; - } - } - else if (GCOV_TAG_IS_SUBTAG (GCOV_TAG_FUNCTION, tag) - && function_name_buffer) - { - counts_entry_t **slot, *entry, elt; - unsigned n_counts = length / 8; - unsigned ix; - - elt.function_name = function_name_buffer; - elt.section = 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)); - } - else if (entry->checksum != checksum || entry->n_counts != n_counts) - { - warning ("profile mismatch for `%s'", function_name_buffer); - htab_delete (counts_hash); - break; - } - - /* This should always be true for a just allocated entry, - and always false for an existing one. Check this way, in - case the gcov file is corrupt. */ - if (!entry->chain || summaried != entry) - { - entry->chain = summaried; - summaried = entry; - } - for (ix = 0; ix != n_counts; ix++) - entry->counts[ix] += gcov_read_counter (); - } - gcov_seek (offset, length); - if ((error = gcov_is_error ())) - { - warning (error < 0 ? "`%s' has overflowed" : "`%s' is corrupted", - name); - htab_delete (counts_hash); - break; - } - } - - free (function_name_buffer); - gcov_close (); -} /* Computes hybrid profile for all matching entries in da_file. Sets max_counter_in_program as a side effect. */ @@ -417,16 +184,8 @@ get_exec_counts () { unsigned num_edges = 0; basic_block bb; - const char *name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (current_function_decl)); - counts_entry_t *entry, elt; - - profile_info.max_counter_in_program = 0; - profile_info.count_profiles_merged = 0; - - /* No hash table, no counts. */ - if (!counts_hash) - return NULL; - + gcov_type *counts; + /* Count the edges to be (possibly) instrumented. */ FOR_BB_BETWEEN (bb, ENTRY_BLOCK_PTR, NULL, next_bb) { @@ -436,24 +195,9 @@ get_exec_counts () num_edges++; } - elt.function_name = (char *) name; - elt.section = GCOV_TAG_ARC_COUNTS; - entry = htab_find (counts_hash, &elt); - if (!entry) - { - warning ("No profile for function '%s' found.", name); - return NULL; - } - - if (entry->checksum != profile_info.current_function_cfg_checksum - || num_edges != entry->n_counts) - { - warning ("profile mismatch for `%s'", current_function_name); - return NULL; - } - - profile_info.count_profiles_merged = entry->merged; - profile_info.max_counter_in_program = entry->max_counter_sum; + counts = get_coverage_counts (GCOV_TAG_ARC_COUNTS, num_edges); + if (!counts) + return NULL; if (rtl_dump_file) { @@ -462,7 +206,7 @@ get_exec_counts () (int)profile_info.max_counter_in_program); } - return entry->counts; + return counts; } @@ -806,45 +550,6 @@ compute_branch_probabilities () find_counters_section (GCOV_TAG_ARC_COUNTS)->present = 1; } -/* Compute checksum for the current function. We generate a CRC32. */ - -static unsigned -compute_checksum () -{ - unsigned chksum = 0; - basic_block bb; - - FOR_EACH_BB (bb) - { - edge e = NULL; - - do - { - unsigned value = BB_TO_GCOV_INDEX (e ? e->dest : bb); - unsigned ix; - - /* No need to use all bits in value identically, nearly all - functions have less than 256 blocks. */ - value ^= value << 16; - value ^= value << 8; - - for (ix = 8; ix--; value <<= 1) - { - unsigned feedback; - - feedback = (value ^ chksum) & 0x80000000 ? 0x04c11db7 : 0; - chksum <<= 1; - chksum ^= feedback; - } - - e = e ? e->succ_next : bb->succ; - } - while (e); - } - - return chksum; -} - /* Instrument and/or analyze program behavior based on program flow graph. In either case, this function builds a flow graph for the function being compiled. The flow graph is stored in BB_GRAPH. @@ -868,19 +573,6 @@ branch_prob () unsigned i; unsigned num_edges, ignored_edges; struct edge_list *el; - const char *name = IDENTIFIER_POINTER - (DECL_ASSEMBLER_NAME (current_function_decl)); - - profile_info.current_function_cfg_checksum = compute_checksum (); - for (i = 0; i < profile_info.n_sections; i++) - { - profile_info.section_info[i].n_counters_now = 0; - profile_info.section_info[i].present = 0; - } - - if (rtl_dump_file) - fprintf (rtl_dump_file, "CFG checksum is %u\n", - profile_info.current_function_cfg_checksum); total_num_times_called++; @@ -998,25 +690,15 @@ branch_prob () if (rtl_dump_file) fprintf (rtl_dump_file, "%d ignored edges\n", ignored_edges); - /* Create a .bbg file from which gcov can reconstruct the basic block + /* Write the .bbg data from which gcov can reconstruct the basic block graph. First output the number of basic blocks, and then for every edge output the source and target basic block numbers. NOTE: The format of this file must be compatible with gcov. */ - if (!gcov_is_error ()) + if (coverage_begin_output ()) { long offset; - const char *file = DECL_SOURCE_FILE (current_function_decl); - unsigned line = DECL_SOURCE_LINE (current_function_decl); - /* Announce function */ - offset = gcov_write_tag (GCOV_TAG_FUNCTION); - gcov_write_string (name); - gcov_write_unsigned (profile_info.current_function_cfg_checksum); - gcov_write_string (file); - gcov_write_unsigned (line); - gcov_write_length (offset); - /* Basic block flags */ offset = gcov_write_tag (GCOV_TAG_BLOCKS); for (i = 0; i != (unsigned) (n_basic_blocks + 2); i++) @@ -1052,77 +734,77 @@ branch_prob () gcov_write_length (offset); } - - /* Output line number information about each basic block for - GCOV utility. */ - { - char const *prev_file_name = NULL; - - FOR_EACH_BB (bb) - { - rtx insn = bb->head; - int ignore_next_note = 0; - - offset = 0; - - /* We are looking for line number notes. Search backward - before basic block to find correct ones. */ - insn = prev_nonnote_insn (insn); - if (!insn) - insn = get_insns (); - else + } + + /* Output line number information about each basic block for GCOV + utility. */ + if (coverage_begin_output ()) + { + char const *prev_file_name = NULL; + long offset; + + FOR_EACH_BB (bb) + { + rtx insn = bb->head; + int ignore_next_note = 0; + + offset = 0; + + /* We are looking for line number notes. Search backward + before basic block to find correct ones. */ + insn = prev_nonnote_insn (insn); + if (!insn) + insn = get_insns (); + else + insn = NEXT_INSN (insn); + + while (insn != bb->end) + { + if (GET_CODE (insn) == NOTE) + { + /* Must ignore the line number notes that + immediately follow the end of an inline function + to avoid counting it twice. There is a note + before the call, and one after the call. */ + if (NOTE_LINE_NUMBER (insn) + == NOTE_INSN_REPEATED_LINE_NUMBER) + ignore_next_note = 1; + else if (NOTE_LINE_NUMBER (insn) <= 0) + /*NOP*/; + else if (ignore_next_note) + ignore_next_note = 0; + else + { + if (!offset) + { + offset = gcov_write_tag (GCOV_TAG_LINES); + gcov_write_unsigned (BB_TO_GCOV_INDEX (bb)); + } + + /* If this is a new source file, then output the + file's name to the .bb file. */ + if (!prev_file_name + || strcmp (NOTE_SOURCE_FILE (insn), + prev_file_name)) + { + prev_file_name = NOTE_SOURCE_FILE (insn); + gcov_write_unsigned (0); + gcov_write_string (prev_file_name); + } + gcov_write_unsigned (NOTE_LINE_NUMBER (insn)); + } + } insn = NEXT_INSN (insn); - - while (insn != bb->end) - { - if (GET_CODE (insn) == NOTE) - { - /* Must ignore the line number notes that immediately - follow the end of an inline function to avoid counting - it twice. There is a note before the call, and one - after the call. */ - if (NOTE_LINE_NUMBER (insn) - == NOTE_INSN_REPEATED_LINE_NUMBER) - ignore_next_note = 1; - else if (NOTE_LINE_NUMBER (insn) <= 0) - /*NOP*/; - else if (ignore_next_note) - ignore_next_note = 0; - else - { - if (!offset) - { - offset = gcov_write_tag (GCOV_TAG_LINES); - gcov_write_unsigned (BB_TO_GCOV_INDEX (bb)); - } - - /* If this is a new source file, then output - the file's name to the .bb file. */ - if (!prev_file_name - || strcmp (NOTE_SOURCE_FILE (insn), - prev_file_name)) - { - prev_file_name = NOTE_SOURCE_FILE (insn); - gcov_write_unsigned (0); - gcov_write_string (prev_file_name); - } - gcov_write_unsigned (NOTE_LINE_NUMBER (insn)); - } - } - insn = NEXT_INSN (insn); - } - - if (offset) - { - /* A file of NULL indicates the end of run. */ - gcov_write_unsigned (0); - gcov_write_string (NULL); - gcov_write_length (offset); - } - if (gcov_is_error ()) - warning ("error writing `%s'", bbg_file_name); - } - } + } + + if (offset) + { + /* A file of NULL indicates the end of run. */ + gcov_write_unsigned (0); + gcov_write_string (NULL); + gcov_write_length (offset); + } + } } if (flag_branch_probabilities) @@ -1132,33 +814,11 @@ branch_prob () if (cfun->arc_profile && profile_arc_flag) { - struct function_list *item; - instrument_edges (el); /* Commit changes done by instrumentation. */ commit_edge_insertions_watch_calls (); allocate_reg_info (max_reg_num (), FALSE, FALSE); - - /* ??? Probably should re-use the existing struct function. */ - item = xmalloc (sizeof (struct function_list)); - - *functions_tail = item; - functions_tail = &item->next; - - item->next = 0; - item->name = xstrdup (name); - item->cfg_checksum = profile_info.current_function_cfg_checksum; - item->n_counter_sections = 0; - for (i = 0; i < profile_info.n_sections; i++) - if (profile_info.section_info[i].n_counters_now) - { - item->counter_sections[item->n_counter_sections].tag = - profile_info.section_info[i].tag; - item->counter_sections[item->n_counter_sections].n_counters = - profile_info.section_info[i].n_counters_now; - item->n_counter_sections++; - } } remove_fake_edges (); @@ -1289,40 +949,10 @@ find_spanning_tree (el) /* Perform file-level initialization for branch-prob processing. */ void -init_branch_prob (filename) - const char *filename; +init_branch_prob () { - int len = strlen (filename); int i; - da_file_name = (char *) xmalloc (len + strlen (GCOV_DATA_SUFFIX) + 1); - strcpy (da_file_name, filename); - strcat (da_file_name, GCOV_DATA_SUFFIX); - - if (flag_branch_probabilities) - read_counts_file (da_file_name); - - if (flag_test_coverage) - { - /* Open the bbg output file. */ - bbg_file_name = (char *) xmalloc (len + strlen (GCOV_GRAPH_SUFFIX) + 1); - strcpy (bbg_file_name, filename); - strcat (bbg_file_name, GCOV_GRAPH_SUFFIX); - if (!gcov_open (bbg_file_name, -1)) - error ("cannot open %s", bbg_file_name); - gcov_write_unsigned (GCOV_GRAPH_MAGIC); - gcov_write_unsigned (GCOV_VERSION); - } - - if (profile_arc_flag) - { - /* Generate and save a copy of this so it can be shared. */ - char buf[20]; - - ASM_GENERATE_INTERNAL_LABEL (buf, "LPBX", 2); - profiler_label = gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (buf)); - } - total_num_blocks = 0; total_num_edges = 0; total_num_edges_ignored = 0; @@ -1342,22 +972,6 @@ init_branch_prob (filename) void end_branch_prob () { - if (flag_test_coverage) - { - int error = gcov_close (); - - if (error) - unlink (bbg_file_name); -#if SELF_COVERAGE - /* If the compiler is instrumented, we should not - unconditionally remove the counts file, because we might be - recompiling ourselves. The .da files are all removed during - copying the stage1 files. */ - if (error) -#endif - unlink (da_file_name); - } - if (rtl_dump_file) { fprintf (rtl_dump_file, "\n"); @@ -1392,610 +1006,6 @@ end_branch_prob () } } -/* Find (and create if not present) a section with TAG. */ -struct section_info * -find_counters_section (tag) - unsigned tag; -{ - unsigned i; - - for (i = 0; i < profile_info.n_sections; i++) - if (profile_info.section_info[i].tag == tag) - return profile_info.section_info + i; - - if (i == MAX_COUNTER_SECTIONS) - abort (); - - profile_info.section_info[i].tag = tag; - profile_info.section_info[i].present = 0; - profile_info.section_info[i].n_counters = 0; - profile_info.section_info[i].n_counters_now = 0; - profile_info.n_sections++; - - return profile_info.section_info + i; -} - -/* Set FIELDS as purpose to VALUE. */ -static void -set_purpose (value, fields) - tree value; - tree fields; -{ - tree act_field, act_value; - - for (act_field = fields, act_value = value; - act_field; - act_field = TREE_CHAIN (act_field), act_value = TREE_CHAIN (act_value)) - TREE_PURPOSE (act_value) = act_field; -} - -/* Returns label for base of counters inside TAG section. */ -static rtx -label_for_tag (tag) - unsigned tag; -{ - switch (tag) - { - case GCOV_TAG_ARC_COUNTS: - return profiler_label; - default: - abort (); - } -} - -/* Creates fields of struct counter_section (in gcov-io.h). */ -static tree -build_counter_section_fields () -{ - tree field, fields; - - /* tag */ - fields = build_decl (FIELD_DECL, NULL_TREE, unsigned_type_node); - - /* n_counters */ - field = build_decl (FIELD_DECL, NULL_TREE, unsigned_type_node); - TREE_CHAIN (field) = fields; - fields = field; - - return fields; -} - -/* Creates value of struct counter_section (in gcov-io.h). */ -static tree -build_counter_section_value (tag, n_counters) - unsigned tag; - unsigned n_counters; -{ - tree value = NULL_TREE; - - /* tag */ - value = tree_cons (NULL_TREE, - convert (unsigned_type_node, - build_int_2 (tag, 0)), - value); - - /* n_counters */ - value = tree_cons (NULL_TREE, - convert (unsigned_type_node, - build_int_2 (n_counters, 0)), - value); - - return value; -} - -/* Creates fields of struct counter_section_data (in gcov-io.h). */ -static tree -build_counter_section_data_fields () -{ - tree field, fields, gcov_type, gcov_ptr_type; - - gcov_type = make_signed_type (GCOV_TYPE_SIZE); - gcov_ptr_type = - build_pointer_type (build_qualified_type (gcov_type, - TYPE_QUAL_CONST)); - - /* tag */ - fields = build_decl (FIELD_DECL, NULL_TREE, unsigned_type_node); - - /* n_counters */ - field = build_decl (FIELD_DECL, NULL_TREE, unsigned_type_node); - TREE_CHAIN (field) = fields; - fields = field; - - /* counters */ - field = build_decl (FIELD_DECL, NULL_TREE, gcov_ptr_type); - TREE_CHAIN (field) = fields; - fields = field; - - return fields; -} - -/* Creates value of struct counter_section_data (in gcov-io.h). */ -static tree -build_counter_section_data_value (tag, n_counters) - unsigned tag; - unsigned n_counters; -{ - tree value = NULL_TREE, counts_table, gcov_type, gcov_ptr_type; - - gcov_type = make_signed_type (GCOV_TYPE_SIZE); - gcov_ptr_type - = build_pointer_type (build_qualified_type - (gcov_type, TYPE_QUAL_CONST)); - - /* tag */ - value = tree_cons (NULL_TREE, - convert (unsigned_type_node, - build_int_2 (tag, 0)), - value); - - /* n_counters */ - value = tree_cons (NULL_TREE, - convert (unsigned_type_node, - build_int_2 (n_counters, 0)), - value); - - /* counters */ - if (n_counters) - { - tree gcov_type_array_type = - build_array_type (gcov_type, - build_index_type (build_int_2 (n_counters - 1, - 0))); - counts_table = - build (VAR_DECL, gcov_type_array_type, NULL_TREE, NULL_TREE); - TREE_STATIC (counts_table) = 1; - DECL_NAME (counts_table) = get_identifier (XSTR (label_for_tag (tag), 0)); - assemble_variable (counts_table, 0, 0, 0); - counts_table = build1 (ADDR_EXPR, gcov_ptr_type, counts_table); - } - else - counts_table = null_pointer_node; - - value = tree_cons (NULL_TREE, counts_table, value); - - return value; -} - -/* Creates fields for struct function_info type (in gcov-io.h). */ -static tree -build_function_info_fields () -{ - tree field, fields, counter_section_fields, counter_section_type; - tree counter_sections_ptr_type; - tree string_type = - build_pointer_type (build_qualified_type (char_type_node, - TYPE_QUAL_CONST)); - /* name */ - fields = build_decl (FIELD_DECL, NULL_TREE, string_type); - - /* checksum */ - field = build_decl (FIELD_DECL, NULL_TREE, unsigned_type_node); - TREE_CHAIN (field) = fields; - fields = field; - - /* n_counter_sections */ - field = build_decl (FIELD_DECL, NULL_TREE, unsigned_type_node); - TREE_CHAIN (field) = fields; - fields = field; - - /* counter_sections */ - counter_section_fields = build_counter_section_fields (); - counter_section_type = (*lang_hooks.types.make_type) (RECORD_TYPE); - finish_builtin_struct (counter_section_type, "__counter_section", - counter_section_fields, NULL_TREE); - counter_sections_ptr_type = - build_pointer_type - (build_qualified_type (counter_section_type, - TYPE_QUAL_CONST)); - field = build_decl (FIELD_DECL, NULL_TREE, counter_sections_ptr_type); - TREE_CHAIN (field) = fields; - fields = field; - - return fields; -} - -/* Creates value for struct function_info (in gcov-io.h). */ -static tree -build_function_info_value (function) - struct function_list *function; -{ - tree value = NULL_TREE; - size_t name_len = strlen (function->name); - tree fname = build_string (name_len + 1, function->name); - tree string_type = - build_pointer_type (build_qualified_type (char_type_node, - TYPE_QUAL_CONST)); - tree counter_section_fields, counter_section_type, counter_sections_value; - tree counter_sections_ptr_type, counter_sections_array_type; - unsigned i; - - /* name */ - TREE_TYPE (fname) = - build_array_type (char_type_node, - build_index_type (build_int_2 (name_len, 0))); - value = tree_cons (NULL_TREE, - build1 (ADDR_EXPR, - string_type, - fname), - value); - - /* checksum */ - value = tree_cons (NULL_TREE, - convert (unsigned_type_node, - build_int_2 (function->cfg_checksum, 0)), - value); - - /* n_counter_sections */ - - value = tree_cons (NULL_TREE, - convert (unsigned_type_node, - build_int_2 (function->n_counter_sections, 0)), - value); - - /* counter_sections */ - counter_section_fields = build_counter_section_fields (); - counter_section_type = (*lang_hooks.types.make_type) (RECORD_TYPE); - counter_sections_ptr_type = - build_pointer_type - (build_qualified_type (counter_section_type, - TYPE_QUAL_CONST)); - counter_sections_array_type = - build_array_type (counter_section_type, - build_index_type ( - build_int_2 (function->n_counter_sections - 1, - 0))); - - counter_sections_value = NULL_TREE; - for (i = 0; i < function->n_counter_sections; i++) - { - tree counter_section_value = - build_counter_section_value (function->counter_sections[i].tag, - function->counter_sections[i].n_counters); - set_purpose (counter_section_value, counter_section_fields); - counter_sections_value = - tree_cons (NULL_TREE, - build_constructor (counter_section_type, - nreverse (counter_section_value)), - counter_sections_value); - } - finish_builtin_struct (counter_section_type, "__counter_section", - counter_section_fields, NULL_TREE); - - if (function->n_counter_sections) - { - counter_sections_value = - build_constructor (counter_sections_array_type, - nreverse (counter_sections_value)), - counter_sections_value = build1 (ADDR_EXPR, - counter_sections_ptr_type, - counter_sections_value); - } - else - counter_sections_value = null_pointer_node; - - value = tree_cons (NULL_TREE, counter_sections_value, value); - - return value; -} - -/* Creates fields of struct gcov_info type (in gcov-io.h). */ -static tree -build_gcov_info_fields (gcov_info_type) - tree gcov_info_type; -{ - tree field, fields; - char *filename; - int filename_len; - tree string_type = - build_pointer_type (build_qualified_type (char_type_node, - TYPE_QUAL_CONST)); - tree function_info_fields, function_info_type, function_info_ptr_type; - tree counter_section_data_fields, counter_section_data_type; - tree counter_section_data_ptr_type; - - /* Version ident */ - fields = build_decl (FIELD_DECL, NULL_TREE, long_unsigned_type_node); - - /* next -- NULL */ - field = build_decl (FIELD_DECL, NULL_TREE, - build_pointer_type (build_qualified_type (gcov_info_type, - TYPE_QUAL_CONST))); - TREE_CHAIN (field) = fields; - fields = field; - - /* Filename */ - filename = getpwd (); - filename = (filename && da_file_name[0] != '/' - ? concat (filename, "/", da_file_name, NULL) - : da_file_name); - filename_len = strlen (filename); - if (filename != da_file_name) - free (filename); - - field = build_decl (FIELD_DECL, NULL_TREE, string_type); - TREE_CHAIN (field) = fields; - fields = field; - - /* Workspace */ - field = build_decl (FIELD_DECL, NULL_TREE, long_integer_type_node); - TREE_CHAIN (field) = fields; - fields = field; - - /* number of functions */ - field = build_decl (FIELD_DECL, NULL_TREE, unsigned_type_node); - TREE_CHAIN (field) = fields; - fields = field; - - /* function_info table */ - function_info_fields = build_function_info_fields (); - function_info_type = (*lang_hooks.types.make_type) (RECORD_TYPE); - finish_builtin_struct (function_info_type, "__function_info", - function_info_fields, NULL_TREE); - function_info_ptr_type = - build_pointer_type - (build_qualified_type (function_info_type, - TYPE_QUAL_CONST)); - field = build_decl (FIELD_DECL, NULL_TREE, function_info_ptr_type); - TREE_CHAIN (field) = fields; - fields = field; - - /* n_counter_sections */ - field = build_decl (FIELD_DECL, NULL_TREE, unsigned_type_node); - TREE_CHAIN (field) = fields; - fields = field; - - /* counter sections */ - counter_section_data_fields = build_counter_section_data_fields (); - counter_section_data_type = (*lang_hooks.types.make_type) (RECORD_TYPE); - finish_builtin_struct (counter_section_data_type, "__counter_section_data", - counter_section_data_fields, NULL_TREE); - counter_section_data_ptr_type = - build_pointer_type - (build_qualified_type (counter_section_data_type, - TYPE_QUAL_CONST)); - field = build_decl (FIELD_DECL, NULL_TREE, counter_section_data_ptr_type); - TREE_CHAIN (field) = fields; - fields = field; - - return fields; -} - -/* Creates struct gcov_info value (in gcov-io.h). */ -static tree -build_gcov_info_value () -{ - tree value = NULL_TREE; - tree filename_string; - char *filename; - int filename_len; - unsigned n_functions, i; - struct function_list *item; - tree string_type = - build_pointer_type (build_qualified_type (char_type_node, - TYPE_QUAL_CONST)); - tree function_info_fields, function_info_type, function_info_ptr_type; - tree functions; - tree counter_section_data_fields, counter_section_data_type; - tree counter_section_data_ptr_type, counter_sections; - - /* Version ident */ - value = tree_cons (NULL_TREE, - convert (long_unsigned_type_node, - build_int_2 (GCOV_VERSION, 0)), - value); - - /* next -- NULL */ - value = tree_cons (NULL_TREE, null_pointer_node, value); - - /* Filename */ - filename = getpwd (); - filename = (filename && da_file_name[0] != '/' - ? concat (filename, "/", da_file_name, NULL) - : da_file_name); - filename_len = strlen (filename); - filename_string = build_string (filename_len + 1, filename); - if (filename != da_file_name) - free (filename); - TREE_TYPE (filename_string) = - build_array_type (char_type_node, - build_index_type (build_int_2 (filename_len, 0))); - value = tree_cons (NULL_TREE, - build1 (ADDR_EXPR, - string_type, - filename_string), - value); - - /* Workspace */ - value = tree_cons (NULL_TREE, - convert (long_integer_type_node, integer_zero_node), - value); - - /* number of functions */ - n_functions = 0; - for (item = functions_head; item != 0; item = item->next, n_functions++) - continue; - value = tree_cons (NULL_TREE, - convert (unsigned_type_node, - build_int_2 (n_functions, 0)), - value); - - /* function_info table */ - function_info_fields = build_function_info_fields (); - function_info_type = (*lang_hooks.types.make_type) (RECORD_TYPE); - function_info_ptr_type = - build_pointer_type ( - build_qualified_type (function_info_type, - TYPE_QUAL_CONST)); - functions = NULL_TREE; - for (item = functions_head; item != 0; item = item->next) - { - tree function_info_value = build_function_info_value (item); - set_purpose (function_info_value, function_info_fields); - functions = tree_cons (NULL_TREE, - build_constructor (function_info_type, - nreverse (function_info_value)), - functions); - } - finish_builtin_struct (function_info_type, "__function_info", - function_info_fields, NULL_TREE); - - /* Create constructor for array. */ - if (n_functions) - { - tree array_type; - - array_type = build_array_type ( - function_info_type, - build_index_type (build_int_2 (n_functions - 1, 0))); - functions = build_constructor (array_type, nreverse (functions)); - functions = build1 (ADDR_EXPR, - function_info_ptr_type, - functions); - } - else - functions = null_pointer_node; - - value = tree_cons (NULL_TREE, functions, value); - - /* n_counter_sections */ - value = tree_cons (NULL_TREE, - convert (unsigned_type_node, - build_int_2 (profile_info.n_sections, 0)), - value); - - /* counter sections */ - counter_section_data_fields = build_counter_section_data_fields (); - counter_section_data_type = (*lang_hooks.types.make_type) (RECORD_TYPE); - counter_sections = NULL_TREE; - for (i = 0; i < profile_info.n_sections; i++) - { - tree counter_sections_value = - build_counter_section_data_value ( - profile_info.section_info[i].tag, - profile_info.section_info[i].n_counters); - set_purpose (counter_sections_value, counter_section_data_fields); - counter_sections = - tree_cons (NULL_TREE, - build_constructor (counter_section_data_type, - nreverse (counter_sections_value)), - counter_sections); - } - finish_builtin_struct (counter_section_data_type, "__counter_section_data", - counter_section_data_fields, NULL_TREE); - counter_section_data_ptr_type = - build_pointer_type - (build_qualified_type (counter_section_data_type, - TYPE_QUAL_CONST)); - - if (profile_info.n_sections) - { - tree cst_type = build_index_type (build_int_2 (profile_info.n_sections-1, - 0)); - cst_type = build_array_type (counter_section_data_type, cst_type); - counter_sections = build_constructor (cst_type, - nreverse (counter_sections)); - counter_sections = build1 (ADDR_EXPR, - counter_section_data_ptr_type, - counter_sections); - } - else - counter_sections = null_pointer_node; - value = tree_cons (NULL_TREE, counter_sections, value); - - return value; -} - -/* 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. */ - -void -create_profiler () -{ - tree gcov_info_fields, gcov_info_type, gcov_info_value, gcov_info; - char name[20]; - char *ctor_name; - tree ctor; - rtx gcov_info_address; - int save_flag_inline_functions = flag_inline_functions; - unsigned i; - - for (i = 0; i < profile_info.n_sections; i++) - if (profile_info.section_info[i].n_counters_now) - break; - if (i == profile_info.n_sections) - return; - - gcov_info_type = (*lang_hooks.types.make_type) (RECORD_TYPE); - gcov_info_fields = build_gcov_info_fields (gcov_info_type); - gcov_info_value = build_gcov_info_value (); - set_purpose (gcov_info_value, gcov_info_fields); - finish_builtin_struct (gcov_info_type, "__gcov_info", - gcov_info_fields, NULL_TREE); - - gcov_info = build (VAR_DECL, gcov_info_type, NULL_TREE, NULL_TREE); - DECL_INITIAL (gcov_info) = - build_constructor (gcov_info_type, nreverse (gcov_info_value)); - - TREE_STATIC (gcov_info) = 1; - ASM_GENERATE_INTERNAL_LABEL (name, "LPBX", 0); - DECL_NAME (gcov_info) = get_identifier (name); - - /* Build structure. */ - assemble_variable (gcov_info, 0, 0, 0); - - /* Build the constructor function to invoke __gcov_init. */ - ctor_name = concat (IDENTIFIER_POINTER (get_file_function_name ('I')), - "_GCOV", NULL); - ctor = build_decl (FUNCTION_DECL, get_identifier (ctor_name), - build_function_type (void_type_node, NULL_TREE)); - free (ctor_name); - DECL_EXTERNAL (ctor) = 0; - - /* It can be a static function as long as collect2 does not have - to scan the object file to find its ctor/dtor routine. */ - TREE_PUBLIC (ctor) = ! targetm.have_ctors_dtors; - TREE_USED (ctor) = 1; - DECL_RESULT (ctor) = build_decl (RESULT_DECL, NULL_TREE, void_type_node); - - ctor = (*lang_hooks.decls.pushdecl) (ctor); - rest_of_decl_compilation (ctor, 0, 1, 0); - announce_function (ctor); - current_function_decl = ctor; - DECL_INITIAL (ctor) = error_mark_node; - make_decl_rtl (ctor, NULL); - init_function_start (ctor, input_filename, lineno); - (*lang_hooks.decls.pushlevel) (0); - expand_function_start (ctor, 0); - cfun->arc_profile = 0; - - /* Actually generate the code to call __gcov_init. */ - gcov_info_address = force_reg (Pmode, XEXP (DECL_RTL (gcov_info), 0)); - emit_library_call (gcov_init_libfunc, LCT_NORMAL, VOIDmode, 1, - gcov_info_address, Pmode); - - expand_function_end (input_filename, lineno, 0); - (*lang_hooks.decls.poplevel) (1, 0, 1); - - /* Since ctor isn't in the list of globals, it would never be emitted - when it's considered to be 'safe' for inlining, so turn off - flag_inline_functions. */ - flag_inline_functions = 0; - - rest_of_compilation (ctor); - - /* Reset flag_inline_functions to its original value. */ - flag_inline_functions = save_flag_inline_functions; - - if (! quiet_flag) - fflush (asm_out_file); - current_function_decl = NULL_TREE; - - if (targetm.have_ctors_dtors) - (* targetm.asm_out.constructor) (XEXP (DECL_RTL (ctor), 0), - DEFAULT_INIT_PRIORITY); -} /* Output instructions as RTL to increment the edge execution count. */ @@ -2003,27 +1013,21 @@ static rtx gen_edge_profiler (edgeno) int edgeno; { - enum machine_mode mode = mode_for_size (GCOV_TYPE_SIZE, MODE_INT, 0); - rtx mem_ref, tmp; + rtx ref = coverage_counter_ref (GCOV_TAG_ARC_COUNTS, edgeno); + rtx tmp; + enum machine_mode mode = GET_MODE (ref); rtx sequence; start_sequence (); + ref = validize_mem (ref); - tmp = force_reg (Pmode, profiler_label); - tmp = plus_constant (tmp, GCOV_TYPE_SIZE / BITS_PER_UNIT * edgeno); - mem_ref = validize_mem (gen_rtx_MEM (mode, tmp)); - - set_mem_alias_set (mem_ref, new_alias_set ()); + tmp = expand_simple_binop (mode, PLUS, ref, const1_rtx, + ref, 0, OPTAB_WIDEN); - tmp = expand_simple_binop (mode, PLUS, mem_ref, const1_rtx, - mem_ref, 0, OPTAB_WIDEN); - - if (tmp != mem_ref) - emit_move_insn (copy_rtx (mem_ref), tmp); + if (tmp != ref) + emit_move_insn (copy_rtx (ref), tmp); sequence = get_insns (); end_sequence (); return sequence; } - -#include "gt-profile.h" diff --git a/gcc/rtl.h b/gcc/rtl.h index b78e9e78db3..acd724353ce 100644 --- a/gcc/rtl.h +++ b/gcc/rtl.h @@ -2210,11 +2210,15 @@ extern void dump_local_alloc PARAMS ((FILE *)); extern int local_alloc PARAMS ((void)); extern int function_invariant_p PARAMS ((rtx)); +/* In coverage.c */ +extern void coverage_init (const char *); +extern void coverage_finish (void); +extern void coverage_end_function (void); + /* In profile.c */ -extern void init_branch_prob PARAMS ((const char *)); +extern void init_branch_prob PARAMS ((void)); extern void branch_prob PARAMS ((void)); extern void end_branch_prob PARAMS ((void)); -extern void create_profiler PARAMS ((void)); /* In reg-stack.c */ #ifdef BUFSIZ diff --git a/gcc/toplev.c b/gcc/toplev.c index 49301fbf74f..bf879929973 100644 --- a/gcc/toplev.c +++ b/gcc/toplev.c @@ -2195,7 +2195,7 @@ compile_file () /* Initialize yet another pass. */ init_final (main_input_filename); - init_branch_prob (aux_base_name); + coverage_init (aux_base_name); timevar_push (TV_PARSE); @@ -2216,11 +2216,10 @@ compile_file () (*lang_hooks.decls.final_write_globals)(); - if (profile_arc_flag) - /* This must occur after the loop to output deferred functions. - Else the profiler initializer would not be emitted if all the - functions in this compilation unit were deferred. */ - create_profiler (); + /* This must occur after the loop to output deferred functions. + Else the coverage initializer would not be emitted if all the + functions in this compilation unit were deferred. */ + coverage_finish (); /* Write out any pending weak symbol declarations. */ @@ -3765,6 +3764,8 @@ rest_of_compilation (decl) exit_rest_of_compilation: + coverage_end_function (); + /* In case the function was not output, don't leave any temporary anonymous types queued up for sdb output. */ diff --git a/gcc/tracer.c b/gcc/tracer.c index 0e31b2ae2f6..1448670ddc6 100644 --- a/gcc/tracer.c +++ b/gcc/tracer.c @@ -1,6 +1,6 @@ /* The tracer pass for the GNU compiler. Contributed by Jan Hubicka, SuSE Labs. - Copyright (C) 2001, 2002 Free Software Foundation, Inc. + Copyright (C) 2001, 2002, 2003 Free Software Foundation, Inc. This file is part of GCC. @@ -46,7 +46,7 @@ #include "fibheap.h" #include "flags.h" #include "params.h" -#include "profile.h" +#include "coverage.h" static int count_insns PARAMS ((basic_block)); static bool ignore_bb_p PARAMS ((basic_block)); |