OSDN Git Service

Daily bump.
[pf3gnuchains/gcc-fork.git] / gcc / gcov.c
index b90d957..a92ce91 100644 (file)
@@ -1,14 +1,15 @@
 /* Gcov.c: prepend line execution counts and branch probabilities to a
    source file.
-   Copyright (C) 1990, 1991, 1992, 1993, 1994, 1996, 1997, 1998,
-   1999, 2000, 2001, 2002 Free Software Foundation, Inc.
+   Copyright (C) 1990, 1991, 1992, 1993, 1994, 1996, 1997, 1998, 1999,
+   2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008
+   Free Software Foundation, Inc.
    Contributed by James E. Wilson of Cygnus Support.
    Mangled by Bob Manson of Cygnus Support.
    Mangled further by Nathan Sidwell <nathan@codesourcery.com>
 
 Gcov 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)
+the Free Software Foundation; either version 3, or (at your option)
 any later version.
 
 Gcov is distributed in the hope that it will be useful,
@@ -17,9 +18,8 @@ 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 Gcov; see the file COPYING.  If not, write to
-the Free Software Foundation, 59 Temple Place - Suite 330,
-Boston, MA 02111-1307, USA.  */
+along with Gcov; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
 
 /* ??? Print a list of the ten blocks with the highest execution counts,
    and list the line numbers corresponding to those blocks.  Also, perhaps
@@ -29,15 +29,6 @@ Boston, MA 02111-1307, USA.  */
 /* ??? Should have an option to print the number of basic blocks, and the
    percent of them that are covered.  */
 
-/* ??? Does not correctly handle the case where two .bb files refer to
-   the same included source file.  For example, if one has a short
-   file containing only inline functions, which is then included in
-   two other files, then there will be two .bb files which refer to
-   the include file, but there is no way to get the total execution
-   counts for the included file, can only get execution counts for one
-   or the other of the including files. this can be fixed by --ratios
-   --long-file-names --preserve-paths and perl.  */
-
 /* Need an option to show individual block counts, and show
    probabilities of fall through arcs.  */
 
@@ -47,14 +38,14 @@ Boston, MA 02111-1307, USA.  */
 #include "tm.h"
 #include "intl.h"
 #include "version.h"
-#undef abort
 
 #include <getopt.h>
 
-typedef HOST_WIDEST_INT gcov_type;
+#define IN_GCOV 1
 #include "gcov-io.h"
+#include "gcov-io.c"
 
-/* The bbg file is generated by -ftest-coverage option. The da file is
+/* The gcno file is generated by -ftest-coverage option. The gcda file is
    generated by a program compiled with -fprofile-arcs. Their formats
    are documented in gcov-io.h.  */
 
@@ -69,29 +60,41 @@ typedef HOST_WIDEST_INT gcov_type;
 
 struct function_info;
 struct block_info;
+struct source_info;
 
 /* Describes an arc between two basic blocks.  */
 
 typedef struct arc_info
 {
-  /* source and destination blocks. */
+  /* source and destination blocks.  */
   struct block_info *src;
   struct block_info *dst;
 
   /* transition counts.  */
   gcov_type count;
+  /* used in cycle search, so that we do not clobber original counts.  */
+  gcov_type cs_count;
 
   unsigned int count_valid : 1;
   unsigned int on_tree : 1;
   unsigned int fake : 1;
   unsigned int fall_through : 1;
 
-  /* Arc to a call.  */
-  unsigned int is_call : 1;
-  
+  /* Arc is for a function that abnormally returns.  */
+  unsigned int is_call_non_return : 1;
+
+  /* Arc is for catch/setjmp.  */
+  unsigned int is_nonlocal_return : 1;
+
+  /* Is an unconditional branch.  */
+  unsigned int is_unconditional : 1;
+
+  /* Loop making arc.  */
+  unsigned int cycle : 1;
+
   /* Next branch on line.  */
   struct arc_info *line_next;
-  
+
   /* Links to next arc on src and dst lists.  */
   struct arc_info *succ_next;
   struct arc_info *pred_next;
@@ -106,25 +109,49 @@ typedef struct block_info
   arc_t *succ;
   arc_t *pred;
 
-  /* Number of unprocessed exit and entry arcs. */
+  /* Number of unprocessed exit and entry arcs.  */
   gcov_type num_succ;
   gcov_type num_pred;
 
-  /* Block execution count. */
+  /* Block execution count.  */
   gcov_type count;
+  unsigned flags : 13;
   unsigned count_valid : 1;
   unsigned valid_chain : 1;
   unsigned invalid_chain : 1;
 
-  /* Array of line numbers and source files. source files are
-     introduced by a linenumber of zero, the next 'line number' is the
-     number of the source file.  Always starts with a source file.  */
-  unsigned *encoding;
-  unsigned num_encodings;
+  /* Block is a call instrumenting site.  */
+  unsigned is_call_site : 1; /* Does the call.  */
+  unsigned is_call_return : 1; /* Is the return.  */
+
+  /* Block is a landing pad for longjmp or throw.  */
+  unsigned is_nonlocal_return : 1;
 
-  /* Temporary chain for solving graph. */
+  union
+  {
+    struct
+    {
+     /* Array of line numbers and source files. source files are
+        introduced by a linenumber of zero, the next 'line number' is
+        the number of the source file.  Always starts with a source
+        file.  */
+      unsigned *encoding;
+      unsigned num;
+    } line; /* Valid until blocks are linked onto lines */
+    struct
+    {
+      /* Single line graph cycle workspace.  Used for all-blocks
+        mode.  */
+      arc_t *arc;
+      unsigned ident;
+    } cycle; /* Used in all-blocks mode, after blocks are linked onto
+              lines.  */
+  } u;
+
+  /* Temporary chain for solving graph, and for chaining blocks on one
+     line.  */
   struct block_info *chain;
-  
+
 } block_t;
 
 /* Describes a single function. Contains an array of basic blocks.  */
@@ -133,16 +160,25 @@ typedef struct function_info
 {
   /* Name of function.  */
   char *name;
+  unsigned ident;
   unsigned checksum;
 
   /* Array of basic blocks.  */
   block_t *blocks;
   unsigned num_blocks;
+  unsigned blocks_executed;
 
   /* Raw arc coverage counts.  */
   gcov_type *counts;
   unsigned num_counts;
-  
+
+  /* First line number.  */
+  unsigned line;
+  struct source_info *src;
+
+  /* Next function in same source file.  */
+  struct function_info *line_next;
+
   /* Next function.  */
   struct function_info *next;
 } function_t;
@@ -153,14 +189,14 @@ typedef struct coverage_info
 {
   int lines;
   int lines_executed;
-  
+
   int branches;
   int branches_executed;
   int branches_taken;
-  
+
   int calls;
   int calls_executed;
-  
+
   char *name;
 } coverage_t;
 
@@ -170,8 +206,14 @@ typedef struct coverage_info
 typedef struct line_info
 {
   gcov_type count;        /* execution count */
-  arc_t *branches;        /* branches from blocks that end on this
-                             line. */
+  union
+  {
+    arc_t *branches;      /* branches from blocks that end on this
+                             line. Used for branch-counts when not
+                             all-blocks mode.  */
+    block_t *blocks;       /* blocks which start on this line.  Used
+                             in all-blocks mode.  */
+  } u;
   unsigned exists : 1;
 } line_t;
 
@@ -183,13 +225,18 @@ typedef struct source_info
   /* Name of source file.  */
   char *name;
   unsigned index;
+  time_t file_time;
 
-  /* Array of line information. */
+  /* Array of line information.  */
   line_t *lines;
   unsigned num_lines;
 
   coverage_t coverage;
-  
+
+  /* Functions in this source file.  These are in ascending line
+     number order.  */
+  function_t *functions;
+
   /* Next source file.  */
   struct source_info *next;
 } source_t;
@@ -198,11 +245,21 @@ typedef struct source_info
 
 static function_t *functions;
 
-/* This points to the head of the sourcefile structure list.  */
+/* This points to the head of the sourcefile structure list.  New elements
+   are always prepended.  */
 
 static source_t *sources;
 
-/* Modification time of graph file. */
+/* Next index for a source file.  */
+
+static unsigned source_index;
+
+/* This holds data summary information.  */
+
+static struct gcov_summary object_summary;
+static unsigned program_count;
+
+/* Modification time of graph file.  */
 
 static time_t bbg_file_time;
 
@@ -210,14 +267,31 @@ static time_t bbg_file_time;
 
 static char *bbg_file_name;
 
+/* Stamp of the bbg file */
+static unsigned bbg_stamp;
+
 /* Name and file pointer of the input file for the arc count data.  */
 
 static char *da_file_name;
 
+/* Data file is missing.  */
+
+static int no_data_file;
+
+/* If there is several input files, compute and display results after
+   reading all data files.  This way if two or more gcda file refer to
+   the same source file (eg inline subprograms in a .h file), the
+   counts are added.  */
+
+static int multiple_files = 0;
+
 /* Output branch probabilities.  */
 
 static int flag_branches = 0;
 
+/* Show unconditional branches too.  */
+static int flag_unconditional = 0;
+
 /* Output a gcov file if this is true.  This is on by default, and can
    be turned off by the -n option.  */
 
@@ -229,6 +303,11 @@ static int flag_gcov_file = 1;
 
 static int flag_long_names = 0;
 
+/* Output count information for every basic block, not merely those
+   that contain line number information.  */
+
+static int flag_all_blocks = 0;
+
 /* Output summary info for each function.  */
 
 static int flag_function_summary = 0;
@@ -245,89 +324,87 @@ static char *object_directory = 0;
 static int flag_preserve_paths = 0;
 
 /* Output the number of times a branch was taken as opposed to the percentage
-   of times it was taken. */
+   of times it was taken.  */
 
 static int flag_counts = 0;
 
 /* Forward declarations.  */
-static void fnotice PARAMS ((FILE *, const char *, ...)) ATTRIBUTE_PRINTF_2;
-static int process_args PARAMS ((int, char **));
-static void print_usage PARAMS ((int)) ATTRIBUTE_NORETURN;
-static void print_version PARAMS ((void)) ATTRIBUTE_NORETURN;
-static void process_file PARAMS ((const char *));
-static void create_file_names PARAMS ((const char *));
-static int read_graph_file PARAMS ((void));
-static int read_count_file PARAMS ((void));
-static void solve_flow_graph PARAMS ((function_t *));
-static void add_branch_counts PARAMS ((coverage_t *, const arc_t *));
-static void add_line_counts PARAMS ((coverage_t *, const function_t *));
-static void function_summary PARAMS ((const coverage_t *, const char *));
-static const char *format_gcov PARAMS ((gcov_type, gcov_type, int));
-static void accumulate_line_counts PARAMS ((source_t *));
-static void output_lines PARAMS ((FILE *, const source_t *));
-static char *make_gcov_file_name PARAMS ((const char *, const char *));
-static void release_structures PARAMS ((void));
-extern int main PARAMS ((int, char **));
+static void fnotice (FILE *, const char *, ...) ATTRIBUTE_PRINTF_2;
+static int process_args (int, char **);
+static void print_usage (int) ATTRIBUTE_NORETURN;
+static void print_version (void) ATTRIBUTE_NORETURN;
+static void process_file (const char *);
+static void generate_results (const char *);
+static void create_file_names (const char *);
+static source_t *find_source (const char *);
+static int read_graph_file (void);
+static int read_count_file (void);
+static void solve_flow_graph (function_t *);
+static void add_branch_counts (coverage_t *, const arc_t *);
+static void add_line_counts (coverage_t *, function_t *);
+static void function_summary (const coverage_t *, const char *);
+static const char *format_gcov (gcov_type, gcov_type, int);
+static void accumulate_line_counts (source_t *);
+static int output_branch_count (FILE *, int, const arc_t *);
+static void output_lines (FILE *, const source_t *);
+static char *make_gcov_file_name (const char *, const char *);
+static void release_structures (void);
+extern int main (int, char **);
 
 int
-main (argc, argv)
-     int argc;
-     char **argv;
+main (int argc, char **argv)
 {
   int argno;
-  
+
+  /* Unlock the stdio streams.  */
+  unlock_std_streams ();
+
   gcc_init_libintl ();
 
+  /* Handle response files.  */
+  expandargv (&argc, &argv);
+
   argno = process_args (argc, argv);
   if (optind == argc)
     print_usage (true);
 
+  if (argc - argno > 1)
+    multiple_files = 1;
+
   for (; argno != argc; argno++)
-    {
-      release_structures ();
-      
-      process_file (argv[argno]);
-    }
-  
+    process_file (argv[argno]);
+
+  generate_results (multiple_files ? NULL : argv[argc - 1]);
+
+  release_structures ();
+
   return 0;
 }
 
 static void
-fnotice VPARAMS ((FILE *file, const char *msgid, ...))
+fnotice (FILE *file, const char *cmsgid, ...)
 {
-  VA_OPEN (ap, msgid);
-  VA_FIXEDARG (ap, FILE *, file);
-  VA_FIXEDARG (ap, const char *, msgid);
+  va_list ap;
 
-  vfprintf (file, _(msgid), ap);
-  VA_CLOSE (ap);
-}
-
-/* More 'friendly' abort that prints the line and file.
-   config.h can #define abort fancy_abort if you like that sort of thing.  */
-extern void fancy_abort PARAMS ((void)) ATTRIBUTE_NORETURN;
-
-void
-fancy_abort ()
-{
-  fnotice (stderr, "Internal gcov abort.\n");
-  exit (FATAL_EXIT_CODE);
+  va_start (ap, cmsgid);
+  vfprintf (file, _(cmsgid), ap);
+  va_end (ap);
 }
 \f
 /* Print a usage message and exit.  If ERROR_P is nonzero, this is an error,
    otherwise the output of --help.  */
 
 static void
-print_usage (error_p)
-     int error_p;
+print_usage (int error_p)
 {
   FILE *file = error_p ? stderr : stdout;
   int status = error_p ? FATAL_EXIT_CODE : SUCCESS_EXIT_CODE;
-  
-  fnotice (file, "Usage: gcov [OPTION]... SOURCEFILE\n\n");
+
+  fnotice (file, "Usage: gcov [OPTION]... SOURCEFILE...\n\n");
   fnotice (file, "Print code coverage information.\n\n");
   fnotice (file, "  -h, --help                      Print this help, then exit\n");
   fnotice (file, "  -v, --version                   Print version number, then exit\n");
+  fnotice (file, "  -a, --all-blocks                Show information for every basic block\n");
   fnotice (file, "  -b, --branch-probabilities      Include branch probabilities in output\n");
   fnotice (file, "  -c, --branch-counts             Given counts of branches taken\n\
                                     rather than percentages\n");
@@ -337,6 +414,7 @@ print_usage (error_p)
   fnotice (file, "  -f, --function-summaries        Output summaries for each function\n");
   fnotice (file, "  -o, --object-directory DIR|FILE Search for object files in DIR or called FILE\n");
   fnotice (file, "  -p, --preserve-paths            Preserve all pathname components\n");
+  fnotice (file, "  -u, --unconditional-branches    Show unconditional branch counts too\n");
   fnotice (file, "\nFor bug reporting instructions, please see:\n%s.\n",
           bug_report_url);
   exit (status);
@@ -345,19 +423,15 @@ print_usage (error_p)
 /* Print version information and exit.  */
 
 static void
-print_version ()
+print_version (void)
 {
-  char v[4];
-  unsigned version = GCOV_VERSION;
-  unsigned ix;
-
-  for (ix = 4; ix--; version >>= 8)
-    v[ix] = version;
-  fnotice (stdout, "gcov %.4s (GCC %s)\n", v, version_string);
-  fnotice (stdout, "Copyright (C) 2002 Free Software Foundation, Inc.\n");
+  fnotice (stdout, "gcov %s%s\n", pkgversion_string, version_string);
+  fprintf (stdout, "Copyright %s 2008 Free Software Foundation, Inc.\n",
+          _("(C)"));
   fnotice (stdout,
-          "This is free software; see the source for copying conditions.  There is NO\n\
-warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\n");
+          _("This is free software; see the source for copying conditions.\n"
+            "There is NO warranty; not even for MERCHANTABILITY or \n"
+            "FITNESS FOR A PARTICULAR PURPOSE.\n\n"));
   exit (SUCCESS_EXIT_CODE);
 }
 
@@ -365,6 +439,7 @@ static const struct option options[] =
 {
   { "help",                 no_argument,       NULL, 'h' },
   { "version",              no_argument,       NULL, 'v' },
+  { "all-blocks",           no_argument,       NULL, 'a' },
   { "branch-probabilities", no_argument,       NULL, 'b' },
   { "branch-counts",        no_argument,       NULL, 'c' },
   { "no-output",            no_argument,       NULL, 'n' },
@@ -373,41 +448,41 @@ static const struct option options[] =
   { "preserve-paths",       no_argument,       NULL, 'p' },
   { "object-directory",     required_argument, NULL, 'o' },
   { "object-file",          required_argument, NULL, 'o' },
+  { "unconditional-branches", no_argument,     NULL, 'u' },
+  { 0, 0, 0, 0 }
 };
 
 /* Process args, return index to first non-arg.  */
 
 static int
-process_args (argc, argv)
-     int argc;
-     char **argv;
+process_args (int argc, char **argv)
 {
   int opt;
 
-  while ((opt = getopt_long (argc, argv, "hvbclnfo:p", options, NULL)) != -1)
+  while ((opt = getopt_long (argc, argv, "abcfhlno:puv", options, NULL)) != -1)
     {
       switch (opt)
        {
-       case 'h':
-         print_usage (false);
-         /* print_usage will exit.  */
-       case 'v':
-         print_version ();
-         /* print_version will exit.  */
+       case 'a':
+         flag_all_blocks = 1;
+         break;
        case 'b':
          flag_branches = 1;
          break;
        case 'c':
          flag_counts = 1;
          break;
-       case 'n':
-         flag_gcov_file = 0;
+       case 'f':
+         flag_function_summary = 1;
          break;
+       case 'h':
+         print_usage (false);
+         /* print_usage will exit.  */
        case 'l':
          flag_long_names = 1;
          break;
-       case 'f':
-         flag_function_summary = 1;
+       case 'n':
+         flag_gcov_file = 0;
          break;
        case 'o':
          object_directory = optarg;
@@ -415,6 +490,12 @@ process_args (argc, argv)
        case 'p':
          flag_preserve_paths = 1;
          break;
+       case 'u':
+         flag_unconditional = 1;
+         break;
+       case 'v':
+         print_version ();
+         /* print_version will exit.  */
        default:
          print_usage (true);
          /* print_usage will exit.  */
@@ -427,33 +508,49 @@ process_args (argc, argv)
 /* Process a single source file.  */
 
 static void
-process_file (file_name)
-     const char *file_name;
+process_file (const char *file_name)
 {
-  source_t *src;
   function_t *fn;
-  
+  function_t *fn_p;
+  function_t *old_functions;
+
+  /* Save and clear the list of current functions.  They will be appended
+     later.  */
+  old_functions = functions;
+  functions = NULL;
+
   create_file_names (file_name);
   if (read_graph_file ())
     return;
-  
+
   if (!functions)
     {
       fnotice (stderr, "%s:no functions found\n", bbg_file_name);
       return;
     }
-  
+
   if (read_count_file ())
     return;
-  
-  for (fn = functions; fn; fn = fn->next)
+
+  for (fn_p = NULL, fn = functions; fn; fn_p = fn, fn = fn->next)
     solve_flow_graph (fn);
+
+  if (fn_p)
+    fn_p->next = old_functions;
+}
+
+static void
+generate_results (const char *file_name)
+{
+  source_t *src;
+  function_t *fn;
+
   for (src = sources; src; src = src->next)
-    src->lines = (line_t *) xcalloc (src->num_lines, sizeof (line_t));
+    src->lines = XCNEWVEC (line_t, src->num_lines);
   for (fn = functions; fn; fn = fn->next)
     {
       coverage_t coverage;
-      
+
       memset (&coverage, 0, sizeof (coverage));
       coverage.name = fn->name;
       add_line_counts (flag_function_summary ? &coverage : NULL, fn);
@@ -463,7 +560,7 @@ process_file (file_name)
          fnotice (stdout, "\n");
        }
     }
-  
+
   for (src = sources; src; src = src->next)
     {
       accumulate_line_counts (src);
@@ -472,19 +569,19 @@ process_file (file_name)
        {
          char *gcov_file_name = make_gcov_file_name (file_name, src->name);
          FILE *gcov_file = fopen (gcov_file_name, "w");
-         
+
          if (gcov_file)
            {
-             fnotice (stdout, "%s:creating `%s'\n",
+             fnotice (stdout, "%s:creating '%s'\n",
                       src->name, gcov_file_name);
              output_lines (gcov_file, src);
              if (ferror (gcov_file))
-                   fnotice (stderr, "%s:error writing output file `%s'\n",
+                   fnotice (stderr, "%s:error writing output file '%s'\n",
                             src->name, gcov_file_name);
              fclose (gcov_file);
            }
          else
-           fnotice (stderr, "%s:could not open output file `%s'\n",
+           fnotice (stderr, "%s:could not open output file '%s'\n",
                     src->name, gcov_file_name);
          free (gcov_file_name);
        }
@@ -495,16 +592,11 @@ process_file (file_name)
 /* Release all memory used.  */
 
 static void
-release_structures ()
+release_structures (void)
 {
   function_t *fn;
   source_t *src;
-  
-  free (bbg_file_name);
-  free (da_file_name);
-  da_file_name = bbg_file_name = NULL;
-  bbg_file_time = 0;
-  
+
   while ((src = sources))
     {
       sources = src->next;
@@ -512,12 +604,12 @@ release_structures ()
       free (src->name);
       free (src->lines);
     }
-  
+
   while ((fn = functions))
     {
       unsigned ix;
       block_t *block;
-      
+
       functions = fn->next;
       for (ix = fn->num_blocks, block = fn->blocks; ix--; block++)
        {
@@ -528,7 +620,6 @@ release_structures ()
              arc_n = arc->succ_next;
              free (arc);
            }
-         free (block->encoding);
        }
       free (fn->blocks);
       free (fn->counts);
@@ -544,202 +635,283 @@ release_structures ()
    the object *file*, and the data files are named from that.  */
 
 static void
-create_file_names (file_name)
-     const char *file_name;
+create_file_names (const char *file_name)
 {
   char *cptr;
   char *name;
   int length = strlen (file_name);
   int base;
-  
+
+  /* Free previous file names.  */
+  if (bbg_file_name)
+    free (bbg_file_name);
+  if (da_file_name)
+    free (da_file_name);
+  da_file_name = bbg_file_name = NULL;
+  bbg_file_time = 0;
+  bbg_stamp = 0;
+
   if (object_directory && object_directory[0])
     {
       struct stat status;
 
       length += strlen (object_directory) + 2;
-      name = xmalloc (length);
+      name = XNEWVEC (char, length);
       name[0] = 0;
-      
+
       base = !stat (object_directory, &status) && S_ISDIR (status.st_mode);
       strcat (name, object_directory);
-      if (base && name[strlen (name) - 1] != '/')
+      if (base && (! IS_DIR_SEPARATOR (name[strlen (name) - 1])))
        strcat (name, "/");
     }
   else
     {
-      name = xmalloc (length + 1);
+      name = XNEWVEC (char, length + 1);
       name[0] = 0;
       base = 1;
     }
-  
+
   if (base)
     {
-      /* Append source file name */
-      cptr = strrchr (file_name, '/');
-      strcat (name, cptr ? cptr + 1 : file_name);
+      /* Append source file name */
+      const char *cptr = lbasename (file_name);
+      strcat (name, cptr ? cptr : file_name);
     }
-  
+
   /* Remove the extension.  */
   cptr = strrchr (name, '.');
   if (cptr)
     *cptr = 0;
-  
+
   length = strlen (name);
   
-  bbg_file_name = xmalloc (length + strlen (GCOV_GRAPH_SUFFIX) + 1);
+  bbg_file_name = XNEWVEC (char, length + strlen (GCOV_NOTE_SUFFIX) + 1);
   strcpy (bbg_file_name, name);
-  strcpy (bbg_file_name + length, GCOV_GRAPH_SUFFIX);
+  strcpy (bbg_file_name + length, GCOV_NOTE_SUFFIX);
 
-  da_file_name = xmalloc (length + strlen (GCOV_DATA_SUFFIX) + 1);
+  da_file_name = XNEWVEC (char, length + strlen (GCOV_DATA_SUFFIX) + 1);
   strcpy (da_file_name, name);
   strcpy (da_file_name + length, GCOV_DATA_SUFFIX);
-  
+
+  free (name);
   return;
 }
 
-/* Read the graph file. Return non-zero on fatal error.  */
+/* Find or create a source file structure for FILE_NAME. Copies
+   FILE_NAME on creation */
 
-static int
-read_graph_file ()
+static source_t *
+find_source (const char *file_name)
 {
-  FILE *file;
+  source_t *src;
   struct stat status;
-  unsigned magic, version;
+
+  if (!file_name)
+    file_name = "<unknown>";
+
+  for (src = sources; src; src = src->next)
+    if (!strcmp (file_name, src->name))
+      break;
+
+  if (!src)
+    {
+      src = XCNEW (source_t);
+      src->name = xstrdup (file_name);
+      src->coverage.name = src->name;
+      src->index = source_index++;
+      src->next = sources;
+      sources = src;
+      
+      if (!stat (file_name, &status))
+       src->file_time = status.st_mtime;
+    }
+
+  if (src->file_time > bbg_file_time)
+    {
+      static int info_emitted;
+
+      fnotice (stderr, "%s:source file is newer than graph file '%s'\n",
+              src->name, bbg_file_name);
+      if (!info_emitted)
+       {
+         fnotice (stderr,
+                  "(the message is only displayed one per source file)\n");
+         info_emitted = 1;
+       }
+      src->file_time = 0;
+    }
+
+  return src;
+}
+
+/* Read the graph file. Return nonzero on fatal error.  */
+
+static int
+read_graph_file (void)
+{
+  unsigned version;
   unsigned current_tag = 0;
-  unsigned tag;
   struct function_info *fn = NULL;
+  function_t *old_functions_head = functions;
   source_t *src = NULL;
   unsigned ix;
+  unsigned tag;
 
-  file = fopen (bbg_file_name, "rb");
-  if (!file)
+  if (!gcov_open (bbg_file_name, 1))
     {
       fnotice (stderr, "%s:cannot open graph file\n", bbg_file_name);
       return 1;
     }
-  if (!fstat (fileno (file), &status))
-    bbg_file_time = status.st_mtime;
-  if (gcov_read_unsigned (file, &magic) || magic != GCOV_GRAPH_MAGIC)
+  bbg_file_time = gcov_time ();
+  if (!gcov_magic (gcov_read_unsigned (), GCOV_NOTE_MAGIC))
     {
       fnotice (stderr, "%s:not a gcov graph file\n", bbg_file_name);
-      fclose (file);
+      gcov_close ();
       return 1;
     }
 
-  if (gcov_read_unsigned (file, &version) || version != GCOV_VERSION)
+  version = gcov_read_unsigned ();
+  if (version != GCOV_VERSION)
     {
       char v[4], e[4];
 
-      magic = GCOV_VERSION;
-      
-      for (ix = 4; ix--; magic >>= 8, version >>= 8)
-       {
-         v[ix] = version;
-         e[ix] = magic;
-       }
-      fnotice (stderr, "%s:version `%.4s', prefer `%.4s'\n",
+      GCOV_UNSIGNED2STRING (v, version);
+      GCOV_UNSIGNED2STRING (e, GCOV_VERSION);
+
+      fnotice (stderr, "%s:version '%.4s', prefer '%.4s'\n",
               bbg_file_name, v, e);
     }
-  
-  while (!gcov_read_unsigned (file, &tag))
-    {
-      unsigned length;
-      long base;
+  bbg_stamp = gcov_read_unsigned ();
 
-      if (gcov_read_unsigned (file, &length))
-       goto corrupt;
+  while ((tag = gcov_read_unsigned ()))
+    {
+      unsigned length = gcov_read_unsigned ();
+      gcov_position_t base = gcov_position ();
 
-      base = gcov_save_position (file);
-      
       if (tag == GCOV_TAG_FUNCTION)
        {
-         char *function_name = NULL;
-         unsigned checksum;
-
-         if (gcov_read_string (file, &function_name, NULL)
-             || gcov_read_unsigned (file, &checksum))
-           goto corrupt;
-         fn = (function_t *)xcalloc (1, sizeof (function_t));
+         char *function_name;
+         unsigned ident, checksum, lineno;
+         source_t *src;
+         function_t *probe, *prev;
+
+         ident = gcov_read_unsigned ();
+         checksum = gcov_read_unsigned ();
+         function_name = xstrdup (gcov_read_string ());
+         src = find_source (gcov_read_string ());
+         lineno = gcov_read_unsigned ();
+
+         fn = XCNEW (function_t);
          fn->name = function_name;
+         fn->ident = ident;
          fn->checksum = checksum;
+         fn->src = src;
+         fn->line = lineno;
 
          fn->next = functions;
          functions = fn;
          current_tag = tag;
+
+         if (lineno >= src->num_lines)
+           src->num_lines = lineno + 1;
+         /* Now insert it into the source file's list of
+            functions. Normally functions will be encountered in
+            ascending order, so a simple scan is quick.  */
+         for (probe = src->functions, prev = NULL;
+              probe && probe->line > lineno;
+              prev = probe, probe = probe->line_next)
+           continue;
+         fn->line_next = probe;
+         if (prev)
+           prev->line_next = fn;
+         else
+           src->functions = fn;
        }
       else if (fn && tag == GCOV_TAG_BLOCKS)
        {
          if (fn->blocks)
-           fnotice (stderr, "%s:already seen blocks for `%s'\n",
+           fnotice (stderr, "%s:already seen blocks for '%s'\n",
                     bbg_file_name, fn->name);
          else
            {
-             fn->num_blocks = length / 4;
-             fn->blocks
-               = (block_t *)xcalloc (fn->num_blocks, sizeof (block_t));
+             unsigned ix, num_blocks = GCOV_TAG_BLOCKS_NUM (length);
+             fn->num_blocks = num_blocks;
+
+             fn->blocks = XCNEWVEC (block_t, fn->num_blocks);
+             for (ix = 0; ix != num_blocks; ix++)
+               fn->blocks[ix].flags = gcov_read_unsigned ();
            }
        }
       else if (fn && tag == GCOV_TAG_ARCS)
        {
-         unsigned src;
-         unsigned num_dests = (length - 4) / 8;
-         unsigned dest, flags;
+         unsigned src = gcov_read_unsigned ();
+         unsigned num_dests = GCOV_TAG_ARCS_NUM (length);
 
-         if (gcov_read_unsigned (file, &src)
-             || src >= fn->num_blocks
-             || fn->blocks[src].succ)
+         if (src >= fn->num_blocks || fn->blocks[src].succ)
            goto corrupt;
-         
+
          while (num_dests--)
            {
              struct arc_info *arc;
-             
-             if (gcov_read_unsigned (file, &dest)
-                 || gcov_read_unsigned (file, &flags)
-                 || dest >= fn->num_blocks)
+             unsigned dest = gcov_read_unsigned ();
+             unsigned flags = gcov_read_unsigned ();
+
+             if (dest >= fn->num_blocks)
                goto corrupt;
-             arc = (arc_t *) xcalloc (1, sizeof (arc_t));
-             
+             arc = XCNEW (arc_t);
+
              arc->dst = &fn->blocks[dest];
              arc->src = &fn->blocks[src];
-             
+
              arc->count = 0;
              arc->count_valid = 0;
              arc->on_tree = !!(flags & GCOV_ARC_ON_TREE);
              arc->fake = !!(flags & GCOV_ARC_FAKE);
              arc->fall_through = !!(flags & GCOV_ARC_FALLTHROUGH);
-             
+
              arc->succ_next = fn->blocks[src].succ;
              fn->blocks[src].succ = arc;
              fn->blocks[src].num_succ++;
-             
+
              arc->pred_next = fn->blocks[dest].pred;
              fn->blocks[dest].pred = arc;
              fn->blocks[dest].num_pred++;
 
-             arc->is_call = arc->fake;
-             
+             if (arc->fake)
+               {
+                 if (src)
+                   {
+                     /* Exceptional exit from this function, the
+                        source block must be a call.  */
+                     fn->blocks[src].is_call_site = 1;
+                     arc->is_call_non_return = 1;
+                   }
+                 else
+                   {
+                     /* Non-local return from a callee of this
+                        function. The destination block is a catch or
+                        setjmp.  */
+                     arc->is_nonlocal_return = 1;
+                     fn->blocks[dest].is_nonlocal_return = 1;
+                   }
+               }
+
              if (!arc->on_tree)
                fn->num_counts++;
            }
        }
       else if (fn && tag == GCOV_TAG_LINES)
        {
-         unsigned blockno;
-         unsigned *line_nos
-           = (unsigned *)xcalloc ((length - 4) / 4, sizeof (unsigned));
+         unsigned blockno = gcov_read_unsigned ();
+         unsigned *line_nos = XCNEWVEC (unsigned, length - 1);
 
-         if (gcov_read_unsigned (file, &blockno)
-             || blockno >= fn->num_blocks
-             || fn->blocks[blockno].encoding)
+         if (blockno >= fn->num_blocks || fn->blocks[blockno].u.line.encoding)
            goto corrupt;
-         
+
          for (ix = 0; ;  )
            {
-             unsigned lineno;
-             
-             if (gcov_read_unsigned (file, &lineno))
-               goto corrupt;
+             unsigned lineno = gcov_read_unsigned ();
+
              if (lineno)
                {
                  if (!ix)
@@ -753,52 +925,38 @@ read_graph_file ()
                }
              else
                {
-                 char *file_name = NULL;
-                 
-                 if (gcov_read_string (file, &file_name, NULL))
-                   goto corrupt;
+                 const char *file_name = gcov_read_string ();
+
                  if (!file_name)
                    break;
-                 for (src = sources; src; src = src->next)
-                   if (!strcmp (file_name, src->name))
-                     {
-                       free (file_name);
-                       break;
-                     }
-                 if (!src)
-                   {
-                     src = (source_t *)xcalloc (1, sizeof (source_t));
-                     src->name = file_name;
-                     src->coverage.name = file_name;
-                     src->index = sources ? sources->index + 1 : 1;
-                     src->next = sources;
-                     sources = src;
-                   }
+                 src = find_source (file_name);
+
                  line_nos[ix++] = 0;
                  line_nos[ix++] = src->index;
                }
            }
-         
-         fn->blocks[blockno].encoding = line_nos;
-         fn->blocks[blockno].num_encodings = ix;
+
+         fn->blocks[blockno].u.line.encoding = line_nos;
+         fn->blocks[blockno].u.line.num = ix;
        }
       else if (current_tag && !GCOV_TAG_IS_SUBTAG (current_tag, tag))
        {
          fn = NULL;
          current_tag = 0;
        }
-      if (gcov_resync (file, base, length))
+      gcov_sync (base, length);
+      if (gcov_is_error ())
        {
        corrupt:;
          fnotice (stderr, "%s:corrupted\n", bbg_file_name);
-         fclose (file);
+         gcov_close ();
          return 1;
        }
     }
-  fclose (file);
-  
-  /* We built everything backwards, so nreverse them all */
-  
+  gcov_close ();
+
+  /* We built everything backwards, so nreverse them all */
+
   /* Reverse sources. Not strictly necessary, but we'll then process
      them in the 'expected' order.  */
   {
@@ -816,18 +974,20 @@ read_graph_file ()
   {
     function_t *fn, *fn_p, *fn_n;
 
-    for (fn_p = NULL, fn = functions; fn; fn_p = fn, fn = fn_n)
+    for (fn_p = old_functions_head, fn = functions;
+        fn != old_functions_head;
+        fn_p = fn, fn = fn_n)
       {
        unsigned ix;
-       
+
        fn_n = fn->next;
        fn->next = fn_p;
 
-       /* Reverse the arcs */
+       /* Reverse the arcs */
        for (ix = fn->num_blocks; ix--;)
          {
            arc_t *arc, *arc_p, *arc_n;
-           
+
            for (arc_p = NULL, arc = fn->blocks[ix].succ; arc;
                 arc_p = arc, arc = arc_n)
              {
@@ -851,70 +1011,66 @@ read_graph_file ()
 }
 
 /* Reads profiles from the count file and attach to each
-   function. Return non-zero if fatal error.  */
+   function. Return nonzero if fatal error.  */
 
 static int
-read_count_file ()
+read_count_file (void)
 {
-  FILE *file;
   unsigned ix;
-  char *function_name_buffer = NULL;
-  unsigned magic, version;
+  unsigned version;
+  unsigned tag;
   function_t *fn = NULL;
+  int error = 0;
 
-  file = fopen (da_file_name, "rb");
-  if (!file)
+  if (!gcov_open (da_file_name, 1))
     {
-      fnotice (stderr, "%s:cannot open data file\n", da_file_name);
-      return 1;
+      fnotice (stderr, "%s:cannot open data file, assuming not executed\n",
+              da_file_name);
+      no_data_file = 1;
+      return 0;
     }
-  if (gcov_read_unsigned (file, &magic) || magic != GCOV_DATA_MAGIC)
+  if (!gcov_magic (gcov_read_unsigned (), GCOV_DATA_MAGIC))
     {
       fnotice (stderr, "%s:not a gcov data file\n", da_file_name);
     cleanup:;
-      free (function_name_buffer);
-      fclose (file);
+      gcov_close ();
       return 1;
     }
-  if (gcov_read_unsigned (file, &version) || version != GCOV_VERSION)
+  version = gcov_read_unsigned ();
+  if (version != GCOV_VERSION)
     {
       char v[4], e[4];
+
+      GCOV_UNSIGNED2STRING (v, version);
+      GCOV_UNSIGNED2STRING (e, GCOV_VERSION);
       
-      magic = GCOV_VERSION;
-      for (ix = 4; ix--; magic >>= 8, version >>= 8)
-       {
-         v[ix] = version;
-         e[ix] = magic;
-       }
-      fnotice (stderr, "%s:version `%.4s', prefer version `%.4s'\n",
+      fnotice (stderr, "%s:version '%.4s', prefer version '%.4s'\n",
               da_file_name, v, e);
     }
-  
-  while (1)
+  tag = gcov_read_unsigned ();
+  if (tag != bbg_stamp)
     {
-      unsigned tag, length;
-      long base;
-      
-      if (gcov_read_unsigned (file, &tag)
-         || gcov_read_unsigned (file, &length))
-       {
-         if (feof (file))
-           break;
-         
-       corrupt:;
-         fnotice (stderr, "%s:corrupted\n", da_file_name);
-         goto cleanup;
-       }
-      base = gcov_save_position (file);
-      if (tag == GCOV_TAG_FUNCTION)
+      fnotice (stderr, "%s:stamp mismatch with graph file\n", da_file_name);
+      goto cleanup;
+    }
+
+  while ((tag = gcov_read_unsigned ()))
+    {
+      unsigned length = gcov_read_unsigned ();
+      unsigned long base = gcov_position ();
+
+      if (tag == GCOV_TAG_OBJECT_SUMMARY)
+       gcov_read_summary (&object_summary);
+      else if (tag == GCOV_TAG_PROGRAM_SUMMARY)
+       program_count++;
+      else if (tag == GCOV_TAG_FUNCTION)
        {
-         unsigned checksum;
+         unsigned ident = gcov_read_unsigned ();
          struct function_info *fn_n = functions;
-         
-         if (gcov_read_string (file, &function_name_buffer, NULL)
-             || gcov_read_unsigned (file, &checksum))
-           goto corrupt;
 
+         /* Try to find the function in the list.
+            To speed up the search, first start from the last function
+            found.   */
          for (fn = fn ? fn->next : NULL; ; fn = fn->next)
            {
              if (fn)
@@ -923,47 +1079,45 @@ read_count_file ()
                fn_n = NULL;
              else
                {
-                 fnotice (stderr, "%s:unknown function `%s'\n",
-                          da_file_name, function_name_buffer);
+                 fnotice (stderr, "%s:unknown function '%u'\n",
+                          da_file_name, ident);
                  break;
                }
-             if (!strcmp (fn->name, function_name_buffer))
+             if (fn->ident == ident)
                break;
            }
 
          if (!fn)
            ;
-         else if (checksum != fn->checksum)
+         else if (gcov_read_unsigned () != fn->checksum)
            {
            mismatch:;
-             fnotice (stderr, "%s:profile mismatch for `%s'\n",
-                      da_file_name, function_name_buffer);
+             fnotice (stderr, "%s:profile mismatch for '%s'\n",
+                      da_file_name, fn->name);
              goto cleanup;
            }
        }
-      else if (tag == GCOV_TAG_ARC_COUNTS && fn)
+      else if (tag == GCOV_TAG_FOR_COUNTER (GCOV_COUNTER_ARCS) && fn)
        {
-         if (length != 8 * fn->num_counts)
+         if (length != GCOV_TAG_COUNTER_LENGTH (fn->num_counts))
            goto mismatch;
-         
+
          if (!fn->counts)
-           fn->counts
-             = (gcov_type *)xcalloc (fn->num_counts, sizeof (gcov_type));
-         
+           fn->counts = XCNEWVEC (gcov_type, fn->num_counts);
+
          for (ix = 0; ix != fn->num_counts; ix++)
-           {
-             gcov_type count;
-             
-             if (gcov_read_counter (file, &count))
-               goto corrupt;
-             fn->counts[ix] += count;
-           }
+           fn->counts[ix] += gcov_read_counter ();
+       }
+      gcov_sync (base, length);
+      if ((error = gcov_is_error ()))
+       {
+         fnotice (stderr, error < 0 ? "%s:overflowed\n" : "%s:corrupted\n",
+                  da_file_name);
+         goto cleanup;
        }
-      gcov_resync (file, base, length);
     }
 
-  fclose (file);
-  free (function_name_buffer);
+  gcov_close ();
   return 0;
 }
 
@@ -971,30 +1125,30 @@ read_count_file ()
    to the blocks and the uninstrumented arcs.  */
 
 static void
-solve_flow_graph (fn)
-     function_t *fn;
+solve_flow_graph (function_t *fn)
 {
   unsigned ix;
   arc_t *arc;
   gcov_type *count_ptr = fn->counts;
-  block_t *valid_blocks = NULL;    /* valid, but unpropagated blocks. */
-  block_t *invalid_blocks = NULL;  /* invalid, but inferable blocks. */
-  
+  block_t *blk;
+  block_t *valid_blocks = NULL;    /* valid, but unpropagated blocks.  */
+  block_t *invalid_blocks = NULL;  /* invalid, but inferable blocks.  */
+
   if (fn->num_blocks < 2)
-    fnotice (stderr, "%s:`%s' lacks entry and/or exit blocks\n",
+    fnotice (stderr, "%s:'%s' lacks entry and/or exit blocks\n",
             bbg_file_name, fn->name);
   else
     {
       if (fn->blocks[0].num_pred)
-       fnotice (stderr, "%s:`%s' has arcs to entry block\n",
+       fnotice (stderr, "%s:'%s' has arcs to entry block\n",
                 bbg_file_name, fn->name);
       else
        /* We can't deduce the entry block counts from the lack of
           predecessors.  */
        fn->blocks[0].num_pred = ~(unsigned)0;
-      
+
       if (fn->blocks[fn->num_blocks - 1].num_succ)
-       fnotice (stderr, "%s:`%s' has arcs from exit block\n",
+       fnotice (stderr, "%s:'%s' has arcs from exit block\n",
                 bbg_file_name, fn->name);
       else
        /* Likewise, we can't deduce exit block counts from the lack
@@ -1004,39 +1158,62 @@ solve_flow_graph (fn)
 
   /* Propagate the measured counts, this must be done in the same
      order as the code in profile.c  */
-  for (ix = 0; ix != fn->num_blocks; ix++)
+  for (ix = 0, blk = fn->blocks; ix != fn->num_blocks; ix++, blk++)
     {
       block_t const *prev_dst = NULL;
       int out_of_order = 0;
-      
-      for (arc = fn->blocks[ix].succ; arc; arc = arc->succ_next)
+      int non_fake_succ = 0;
+
+      for (arc = blk->succ; arc; arc = arc->succ_next)
        {
+         if (!arc->fake)
+           non_fake_succ++;
+
          if (!arc->on_tree)
            {
              if (count_ptr)
                arc->count = *count_ptr++;
              arc->count_valid = 1;
-             fn->blocks[ix].num_succ--;
+             blk->num_succ--;
              arc->dst->num_pred--;
            }
          if (prev_dst && prev_dst > arc->dst)
            out_of_order = 1;
          prev_dst = arc->dst;
        }
-      
+      if (non_fake_succ == 1)
+       {
+         /* If there is only one non-fake exit, it is an
+            unconditional branch.  */
+         for (arc = blk->succ; arc; arc = arc->succ_next)
+           if (!arc->fake)
+             {
+               arc->is_unconditional = 1;
+               /* If this block is instrumenting a call, it might be
+                  an artificial block. It is not artificial if it has
+                  a non-fallthrough exit, or the destination of this
+                  arc has more than one entry.  Mark the destination
+                  block as a return site, if none of those conditions
+                  hold.  */
+               if (blk->is_call_site && arc->fall_through
+                   && arc->dst->pred == arc && !arc->pred_next)
+                 arc->dst->is_call_return = 1;
+             }
+       }
+
       /* Sort the successor arcs into ascending dst order. profile.c
         normally produces arcs in the right order, but sometimes with
         one or two out of order.  We're not using a particularly
-        smart sort. */
+        smart sort.  */
       if (out_of_order)
        {
-         arc_t *start = fn->blocks[ix].succ;
+         arc_t *start = blk->succ;
          unsigned changes = 1;
-         
+
          while (changes)
            {
              arc_t *arc, *arc_p, *arc_n;
-             
+
              changes = 0;
              for (arc_p = NULL, arc = start; (arc_n = arc->succ_next);)
                {
@@ -1058,25 +1235,23 @@ solve_flow_graph (fn)
                    }
                }
            }
-         fn->blocks[ix].succ = start;
+         blk->succ = start;
        }
-      
+
       /* Place it on the invalid chain, it will be ignored if that's
         wrong.  */
-      fn->blocks[ix].invalid_chain = 1;
-      fn->blocks[ix].chain = invalid_blocks;
-      invalid_blocks = &fn->blocks[ix];
+      blk->invalid_chain = 1;
+      blk->chain = invalid_blocks;
+      invalid_blocks = blk;
     }
 
   while (invalid_blocks || valid_blocks)
     {
-      block_t *blk;
-      
       while ((blk = invalid_blocks))
        {
          gcov_type total = 0;
          const arc_t *arc;
-         
+
          invalid_blocks = blk->chain;
          blk->invalid_chain = 0;
          if (!blk->num_succ)
@@ -1087,7 +1262,7 @@ solve_flow_graph (fn)
              total += arc->count;
          else
            continue;
-         
+
          blk->count = total;
          blk->count_valid = 1;
          blk->chain = valid_blocks;
@@ -1104,7 +1279,7 @@ solve_flow_graph (fn)
          if (blk->num_succ == 1)
            {
              block_t *dst;
-             
+
              total = blk->count;
              inv_arc = NULL;
              for (arc = blk->succ; arc; arc = arc->succ_next)
@@ -1140,7 +1315,7 @@ solve_flow_graph (fn)
          if (blk->num_pred == 1)
            {
              block_t *src;
-             
+
              total = blk->count;
              inv_arc = NULL;
              for (arc = blk->pred; arc; arc = arc->pred_next)
@@ -1175,13 +1350,13 @@ solve_flow_graph (fn)
            }
        }
     }
-  
+
   /* If the graph has been correctly solved, every block will have a
      valid count.  */
   for (ix = 0; ix < fn->num_blocks; ix++)
     if (!fn->blocks[ix].count_valid)
       {
-       fnotice (stderr, "%s:graph is unsolvable for `%s'\n",
+       fnotice (stderr, "%s:graph is unsolvable for '%s'\n",
                 bbg_file_name, fn->name);
        break;
       }
@@ -1192,17 +1367,15 @@ solve_flow_graph (fn)
 /* Increment totals in COVERAGE according to arc ARC.  */
 
 static void
-add_branch_counts (coverage, arc)
-     coverage_t *coverage;
-     const arc_t *arc;
+add_branch_counts (coverage_t *coverage, const arc_t *arc)
 {
-  if (arc->is_call)
+  if (arc->is_call_non_return)
     {
       coverage->calls++;
       if (arc->src->count)
        coverage->calls_executed++;
     }
-  else
+  else if (!arc->is_unconditional)
     {
       coverage->branches++;
       if (arc->src->count)
@@ -1219,22 +1392,20 @@ add_branch_counts (coverage, arc)
    format TOP.  Return pointer to a static string.  */
 
 static char const *
-format_gcov (top, bottom, dp)
-     gcov_type top, bottom;
-     int dp;
+format_gcov (gcov_type top, gcov_type bottom, int dp)
 {
   static char buffer[20];
-  
+
   if (dp >= 0)
     {
       float ratio = bottom ? (float)top / bottom : 0;
       int ix;
       unsigned limit = 100;
       unsigned percent;
-  
+
       for (ix = dp; ix--; )
        limit *= 10;
-      
+
       percent = (unsigned) (ratio * limit + (float)0.5);
       if (percent <= 0 && top)
        percent = 1;
@@ -1255,7 +1426,7 @@ format_gcov (top, bottom, dp)
     }
   else
     sprintf (buffer, HOST_WIDEST_INT_PRINT_DEC, (HOST_WIDEST_INT)top);
-  
+
   return buffer;
 }
 
@@ -1263,18 +1434,16 @@ format_gcov (top, bottom, dp)
 /* Output summary info for a function.  */
 
 static void
-function_summary (coverage, title)
-     const coverage_t *coverage;
-     const char *title;
+function_summary (const coverage_t *coverage, const char *title)
 {
-  fnotice (stdout, "%s `%s'\n", title, coverage->name);
+  fnotice (stdout, "%s '%s'\n", title, coverage->name);
 
   if (coverage->lines)
     fnotice (stdout, "Lines executed:%s of %d\n",
             format_gcov (coverage->lines_executed, coverage->lines, 2),
             coverage->lines);
   else
-    fnotice (stdout, "No executable lines");
+    fnotice (stdout, "No executable lines\n");
 
   if (flag_branches)
     {
@@ -1311,58 +1480,75 @@ function_summary (coverage, title)
    removed and '..'  components are renamed to '^'.  */
 
 static char *
-make_gcov_file_name (input_name, src_name)
-     const char *input_name;
-     const char *src_name;
+make_gcov_file_name (const char *input_name, const char *src_name)
 {
-  char *cptr;
-  char *name = xmalloc (strlen (src_name) + strlen (input_name) + 10);
-  
-  name[0] = 0;
-  if (flag_long_names && strcmp (src_name, input_name))
+  const char *cptr;
+  char *name;
+
+  if (flag_long_names && input_name && strcmp (src_name, input_name))
     {
+      name = XNEWVEC (char, strlen (src_name) + strlen (input_name) + 10);
+      name[0] = 0;
       /* Generate the input filename part.  */
-      cptr = flag_preserve_paths ? NULL : strrchr (input_name, '/');
-      strcat (name, cptr ? cptr + 1 : input_name);
+      cptr = flag_preserve_paths ? NULL : lbasename (input_name);
+      strcat (name, cptr ? cptr : input_name);
       strcat (name, "##");
     }
-   
+  else
+    {
+      name = XNEWVEC (char, strlen (src_name) + 10);
+      name[0] = 0;
+    }
+
   /* Generate the source filename part.  */
-  cptr = flag_preserve_paths ? NULL : strrchr (src_name, '/');
-  strcat (name, cptr ? cptr + 1 : src_name);
-  
+
+  cptr = flag_preserve_paths ? NULL : lbasename (src_name);
+  strcat (name, cptr ? cptr : src_name);
+
   if (flag_preserve_paths)
     {
-      /* Convert '/' to '#', remove '/./', convert '/../' to '/^/' */
-      char *prev;
-      
-      for (cptr = name; (cptr = strchr ((prev = cptr), '/'));)
-       {
-         unsigned shift = 0;
-         
-         if (prev + 1 == cptr && prev[0] == '.')
-           {
-             /* Remove '.' */
-             shift = 2;
-           }
-         else if (prev + 2 == cptr && prev[0] == '.' && prev[1] == '.')
-           {
-             /* Convert '..' */
-             shift = 1;
-             prev[1] = '^';
-           }
-         else
-           *cptr++ = '#';
-         if (shift)
-           {
-             cptr = prev;
-             do
-               prev[0] = prev[shift];
-             while (*prev++);
-           }
-       }
+      /* Convert '/' and '\' to '#', remove '/./', convert '/../' to '/^/',
+        convert ':' to '~' on DOS based file system.  */
+      char *pnew = name, *pold = name;
+
+      /* First check for leading drive separator.  */
+
+      while (*pold != '\0')
+       {
+         if (*pold == '/' || *pold == '\\')
+           {
+             *pnew++ = '#';
+             pold++;
+           }
+#if defined (HAVE_DOS_BASED_FILE_SYSTEM)
+         else if (*pold == ':')
+           {
+             *pnew++ = '~';
+             pold++;
+           }
+#endif
+         else if ((*pold == '/' && strstr (pold, "/./") == pold)
+                  || (*pold == '\\' && strstr (pold, "\\.\\") == pold))
+             pold += 3;
+         else if (*pold == '/' && strstr (pold, "/../") == pold)
+           {
+             strcpy (pnew, "/^/");
+             pnew += 3;
+             pold += 4;
+           }
+         else if (*pold == '\\' && strstr (pold, "\\..\\") == pold)
+           {
+             strcpy (pnew, "\\^\\");
+             pnew += 3;
+             pold += 4;
+           }
+         else
+           *pnew++ = *pold++;
+       }
+
+      *pnew = '\0';
     }
-  
+
   strcat (name, ".gcov");
   return name;
 }
@@ -1372,24 +1558,24 @@ make_gcov_file_name (input_name, src_name)
    the appropriate basic block.  */
 
 static void
-add_line_counts (coverage, fn)
-     coverage_t *coverage;
-     const function_t *fn;
+add_line_counts (coverage_t *coverage, function_t *fn)
 {
   unsigned ix;
-  line_t *line = NULL; /* this is propagated from one iteration to the
+  line_t *line = NULL; /* This is propagated from one iteration to the
                          next.  */
 
-  /* Scan each basic block. */
+  /* Scan each basic block.  */
   for (ix = 0; ix != fn->num_blocks; ix++)
     {
-      const block_t *block = &fn->blocks[ix];
+      block_t *block = &fn->blocks[ix];
       unsigned *encoding;
       const source_t *src = NULL;
       unsigned jx;
 
-      for (jx = 0, encoding = block->encoding;
-          jx != block->num_encodings; jx++, encoding++)
+      if (block->count && ix && ix + 1 != fn->num_blocks)
+       fn->blocks_executed++;
+      for (jx = 0, encoding = block->u.line.encoding;
+          jx != block->u.line.num; jx++, encoding++)
        if (!*encoding)
          {
            unsigned src_n = *++encoding;
@@ -1406,56 +1592,213 @@ add_line_counts (coverage, fn)
              {
                if (!line->exists)
                  coverage->lines++;
-               if  (!line->count && block->count)
+               if (!line->count && block->count)
                  coverage->lines_executed++;
              }
            line->exists = 1;
            line->count += block->count;
          }
-      
-      if (line && flag_branches)
+      free (block->u.line.encoding);
+      block->u.cycle.arc = NULL;
+      block->u.cycle.ident = ~0U;
+
+      if (!ix || ix + 1 == fn->num_blocks)
+       /* Entry or exit block */;
+      else if (flag_all_blocks)
+       {
+         line_t *block_line = line ? line : &fn->src->lines[fn->line];
+
+         block->chain = block_line->u.blocks;
+         block_line->u.blocks = block;
+       }
+      else if (flag_branches)
        {
          arc_t *arc;
-         
+
          for (arc = block->succ; arc; arc = arc->succ_next)
            {
-             /* Ignore fall through arcs as they aren't really branches.  */
-             if (arc->fall_through)
-               continue;
-             
-             arc->line_next = line->branches;
-             line->branches = arc;
-             if (coverage)
+             arc->line_next = line->u.branches;
+             line->u.branches = arc;
+             if (coverage && !arc->is_unconditional)
                add_branch_counts (coverage, arc);
            }
        }
     }
   if (!line)
-    fnotice (stderr, "%s:no lines for `%s'\n", bbg_file_name, fn->name);
+    fnotice (stderr, "%s:no lines for '%s'\n", bbg_file_name, fn->name);
 }
 
-/* Accumulate the line counts of a file. */
+/* Accumulate the line counts of a file.  */
 
 static void
-accumulate_line_counts (src)
-     source_t *src;
+accumulate_line_counts (source_t *src)
 {
   line_t *line;
+  function_t *fn, *fn_p, *fn_n;
   unsigned ix;
-  
+
+  /* Reverse the function order.  */
+  for (fn = src->functions, fn_p = NULL; fn;
+       fn_p = fn, fn = fn_n)
+    {
+      fn_n = fn->line_next;
+      fn->line_next = fn_p;
+    }
+  src->functions = fn_p;
+
   for (ix = src->num_lines, line = src->lines; ix--; line++)
     {
-      arc_t *arc, *arc_p, *arc_n;
-      
-      /* Total and reverse the branch information. */
-      for (arc = line->branches, arc_p = NULL; arc; arc_p = arc, arc = arc_n)
+      if (!flag_all_blocks)
        {
-         arc_n = arc->line_next;
-         arc->line_next = arc_p;
+         arc_t *arc, *arc_p, *arc_n;
 
-         add_branch_counts (&src->coverage, arc);
+         /* Total and reverse the branch information.  */
+         for (arc = line->u.branches, arc_p = NULL; arc;
+              arc_p = arc, arc = arc_n)
+           {
+             arc_n = arc->line_next;
+             arc->line_next = arc_p;
+
+             add_branch_counts (&src->coverage, arc);
+           }
+         line->u.branches = arc_p;
+       }
+      else if (line->u.blocks)
+       {
+         /* The user expects the line count to be the number of times
+            a line has been executed. Simply summing the block count
+            will give an artificially high number.  The Right Thing
+            is to sum the entry counts to the graph of blocks on this
+            line, then find the elementary cycles of the local graph
+            and add the transition counts of those cycles.  */
+         block_t *block, *block_p, *block_n;
+         gcov_type count = 0;
+
+         /* Reverse the block information.  */
+         for (block = line->u.blocks, block_p = NULL; block;
+              block_p = block, block = block_n)
+           {
+             block_n = block->chain;
+             block->chain = block_p;
+             block->u.cycle.ident = ix;
+           }
+         line->u.blocks = block_p;
+
+         /* Sum the entry arcs.  */
+         for (block = line->u.blocks; block; block = block->chain)
+           {
+             arc_t *arc;
+
+             for (arc = block->pred; arc; arc = arc->pred_next)
+               {
+                 if (arc->src->u.cycle.ident != ix)
+                   count += arc->count;
+                 if (flag_branches)
+                   add_branch_counts (&src->coverage, arc);
+               }
+
+             /* Initialize the cs_count.  */
+             for (arc = block->succ; arc; arc = arc->succ_next)
+               arc->cs_count = arc->count;
+           }
+
+         /* Find the loops. This uses the algorithm described in
+            Tiernan 'An Efficient Search Algorithm to Find the
+            Elementary Circuits of a Graph', CACM Dec 1970. We hold
+            the P array by having each block point to the arc that
+            connects to the previous block. The H array is implicitly
+            held because of the arc ordering, and the block's
+            previous arc pointer.
+
+            Although the algorithm is O(N^3) for highly connected
+            graphs, at worst we'll have O(N^2), as most blocks have
+            only one or two exits. Most graphs will be small.
+
+            For each loop we find, locate the arc with the smallest
+            transition count, and add that to the cumulative
+            count.  Decrease flow over the cycle and remove the arc
+            from consideration.  */
+         for (block = line->u.blocks; block; block = block->chain)
+           {
+             block_t *head = block;
+             arc_t *arc;
+
+           next_vertex:;
+             arc = head->succ;
+           current_vertex:;
+             while (arc)
+               {
+                 block_t *dst = arc->dst;
+                 if (/* Already used that arc.  */
+                     arc->cycle
+                     /* Not to same graph, or before first vertex.  */
+                     || dst->u.cycle.ident != ix
+                     /* Already in path.  */
+                     || dst->u.cycle.arc)
+                   {
+                     arc = arc->succ_next;
+                     continue;
+                   }
+
+                 if (dst == block)
+                   {
+                     /* Found a closing arc.  */
+                     gcov_type cycle_count = arc->cs_count;
+                     arc_t *cycle_arc = arc;
+                     arc_t *probe_arc;
+
+                     /* Locate the smallest arc count of the loop.  */
+                     for (dst = head; (probe_arc = dst->u.cycle.arc);
+                          dst = probe_arc->src)
+                       if (cycle_count > probe_arc->cs_count)
+                         {
+                           cycle_count = probe_arc->cs_count;
+                           cycle_arc = probe_arc;
+                         }
+
+                     count += cycle_count;
+                     cycle_arc->cycle = 1;
+
+                     /* Remove the flow from the cycle.  */
+                     arc->cs_count -= cycle_count;
+                     for (dst = head; (probe_arc = dst->u.cycle.arc);
+                          dst = probe_arc->src)
+                       probe_arc->cs_count -= cycle_count;
+
+                     /* Unwind to the cyclic arc.  */
+                     while (head != cycle_arc->src)
+                       {
+                         arc = head->u.cycle.arc;
+                         head->u.cycle.arc = NULL;
+                         head = arc->src;
+                       }
+                     /* Move on.  */
+                     arc = arc->succ_next;
+                     continue;
+                   }
+
+                 /* Add new block to chain.  */
+                 dst->u.cycle.arc = arc;
+                 head = dst;
+                 goto next_vertex;
+               }
+             /* We could not add another vertex to the path. Remove
+                the last vertex from the list.  */
+             arc = head->u.cycle.arc;
+             if (arc)
+               {
+                 /* It was not the first vertex. Move onto next arc.  */
+                 head->u.cycle.arc = NULL;
+                 head = arc->src;
+                 arc = arc->succ_next;
+                 goto current_vertex;
+               }
+             /* Mark this block as unusable.  */
+             block->u.cycle.ident = ~0U;
+           }
+
+         line->count = count;
        }
-      line->branches = arc_p;
 
       if (line->exists)
        {
@@ -1466,58 +1809,116 @@ accumulate_line_counts (src)
     }
 }
 
+/* Output information about ARC number IX.  Returns nonzero if
+   anything is output.  */
+
+static int
+output_branch_count (FILE *gcov_file, int ix, const arc_t *arc)
+{
+
+  if (arc->is_call_non_return)
+    {
+      if (arc->src->count)
+       {
+         fnotice (gcov_file, "call   %2d returned %s\n", ix,
+                  format_gcov (arc->src->count - arc->count,
+                               arc->src->count, -flag_counts));
+       }
+      else
+       fnotice (gcov_file, "call   %2d never executed\n", ix);
+    }
+  else if (!arc->is_unconditional)
+    {
+      if (arc->src->count)
+       fnotice (gcov_file, "branch %2d taken %s%s\n", ix,
+                format_gcov (arc->count, arc->src->count, -flag_counts),
+                arc->fall_through ? " (fallthrough)" : "");
+      else
+       fnotice (gcov_file, "branch %2d never executed\n", ix);
+    }
+  else if (flag_unconditional && !arc->dst->is_call_return)
+    {
+      if (arc->src->count)
+       fnotice (gcov_file, "unconditional %2d taken %s\n", ix,
+                format_gcov (arc->count, arc->src->count, -flag_counts));
+      else
+       fnotice (gcov_file, "unconditional %2d never executed\n", ix);
+    }
+  else
+    return 0;
+  return 1;
+
+}
+
 /* Read in the source file one line at a time, and output that line to
    the gcov file preceded by its execution count and other
    information.  */
 
 static void
-output_lines (gcov_file, src)
-     FILE *gcov_file;
-     const source_t *src;
+output_lines (FILE *gcov_file, const source_t *src)
 {
   FILE *source_file;
-  unsigned line_num;           /* current line number.  */
+  unsigned line_num;   /* current line number.  */
   const line_t *line;           /* current line info ptr.  */
   char string[STRING_SIZE];     /* line buffer.  */
   char const *retval = "";     /* status of source file reading.  */
+  function_t *fn = NULL;
 
   fprintf (gcov_file, "%9s:%5d:Source:%s\n", "-", 0, src->name);
-  fprintf (gcov_file, "%9s:%5d:Graph:%s\n", "-", 0, bbg_file_name);
-  fprintf (gcov_file, "%9s:%5d:Data:%s\n", "-", 0, da_file_name);
-  
+  if (!multiple_files)
+    {
+      fprintf (gcov_file, "%9s:%5d:Graph:%s\n", "-", 0, bbg_file_name);
+      fprintf (gcov_file, "%9s:%5d:Data:%s\n", "-", 0,
+              no_data_file ? "-" : da_file_name);
+      fprintf (gcov_file, "%9s:%5d:Runs:%u\n", "-", 0,
+              object_summary.ctrs[GCOV_COUNTER_ARCS].runs);
+    }
+  fprintf (gcov_file, "%9s:%5d:Programs:%u\n", "-", 0, program_count);
+
   source_file = fopen (src->name, "r");
   if (!source_file)
     {
       fnotice (stderr, "%s:cannot open source file\n", src->name);
       retval = NULL;
     }
-  else
-    {
-      struct stat status;
-      
-      if (!fstat (fileno (source_file), &status)
-         && status.st_mtime > bbg_file_time)
-       {
-         fnotice (stderr, "%s:source file is newer than graph file `%s'\n",
-                  src->name, bbg_file_name);
-         fprintf (gcov_file, "%9s:%5d:Source is newer than graph\n",
-                  "-", 0);
-       }
-    }
+  else if (src->file_time == 0)
+    fprintf (gcov_file, "%9s:%5d:Source is newer than graph\n", "-", 0);
+
+  if (flag_branches)
+    fn = src->functions;
 
   for (line_num = 1, line = &src->lines[line_num];
        line_num < src->num_lines; line_num++, line++)
     {
+      for (; fn && fn->line == line_num; fn = fn->line_next)
+       {
+         arc_t *arc = fn->blocks[fn->num_blocks - 1].pred;
+         gcov_type return_count = fn->blocks[fn->num_blocks - 1].count;
+         
+         for (; arc; arc = arc->pred_next)
+           if (arc->fake)
+             return_count -= arc->count;
+         
+         fprintf (gcov_file, "function %s", fn->name);
+         fprintf (gcov_file, " called %s",
+                  format_gcov (fn->blocks[0].count, 0, -1));
+         fprintf (gcov_file, " returned %s",
+                  format_gcov (return_count, fn->blocks[0].count, 0));
+         fprintf (gcov_file, " blocks executed %s",
+                  format_gcov (fn->blocks_executed, fn->num_blocks - 2, 0));
+         fprintf (gcov_file, "\n");
+       }
+
       /* For lines which don't exist in the .bb file, print '-' before
-        the source line.  For lines which exist but were never
-        executed, print '#####' before the source line.  Otherwise,
-        print the execution count before the source line.  There are
-        16 spaces of indentation added before the source line so that
-        tabs won't be messed up.  */
+        the source line.  For lines which exist but were never
+        executed, print '#####' before the source line.  Otherwise,
+        print the execution count before the source line.  There are
+        16 spaces of indentation added before the source line so that
+        tabs won't be messed up.  */
       fprintf (gcov_file, "%9s:%5u:",
               !line->exists ? "-" : !line->count ? "#####"
               : format_gcov (line->count, 0, -1), line_num);
-      
+
       if (retval)
        {
          /* Copy source line.  */
@@ -1525,49 +1926,43 @@ output_lines (gcov_file, src)
            {
              retval = fgets (string, STRING_SIZE, source_file);
              if (!retval)
-               {
-                 fnotice (stderr, "%s:unexpected EOF\n", src->name);
-                 break;
-               }
+               break;
              fputs (retval, gcov_file);
            }
          while (!retval[0] || retval[strlen (retval) - 1] != '\n');
        }
       if (!retval)
-       fputs ("??\n", gcov_file);
-      
-      if (flag_branches)
+       fputs ("/*EOF*/\n", gcov_file);
+
+      if (flag_all_blocks)
        {
-         int ix;
+         block_t *block;
          arc_t *arc;
-         
-         for (ix = 0, arc = line->branches; arc; arc = arc->line_next, ix++)
+         int ix, jx;
+
+         for (ix = jx = 0, block = line->u.blocks; block;
+              block = block->chain)
            {
-             if (arc->is_call)
-               {
-                 if (arc->src->count)
-                   fnotice
-                     (gcov_file, "call   %2d returns %s\n", ix,
-                      format_gcov (arc->src->count - arc->count,
-                                   arc->src->count,
-                                   -flag_counts));
-                 else
-                   fnotice (gcov_file, "call   %2d never executed\n", ix);
-               }
-             else
-               {
-                 if (arc->src->count)
-                   fnotice
-                     (gcov_file, "branch %2d taken %s\n", ix,
-                      format_gcov (arc->count, arc->src->count,
-                                   -flag_counts));
-                 else
-                   fnotice (gcov_file, "branch %2d never executed\n", ix);
-               }
+             if (!block->is_call_return)
+               fprintf (gcov_file, "%9s:%5u-block %2d\n",
+                        !line->exists ? "-" : !block->count ? "$$$$$"
+                        : format_gcov (block->count, 0, -1),
+                        line_num, ix++);
+             if (flag_branches)
+               for (arc = block->succ; arc; arc = arc->succ_next)
+                 jx += output_branch_count (gcov_file, jx, arc);
            }
        }
+      else if (flag_branches)
+       {
+         int ix;
+         arc_t *arc;
+
+         for (ix = 0, arc = line->u.branches; arc; arc = arc->line_next)
+           ix += output_branch_count (gcov_file, ix, arc);
+       }
     }
-  
+
   /* Handle all remaining source lines.  There may be lines after the
      last line of code.  */
   if (retval)
@@ -1575,7 +1970,7 @@ output_lines (gcov_file, src)
       for (; (retval = fgets (string, STRING_SIZE, source_file)); line_num++)
        {
          fprintf (gcov_file, "%9s:%5u:%s", "-", line_num, retval);
-         
+
          while (!retval[0] || retval[strlen (retval) - 1] != '\n')
            {
              retval = fgets (string, STRING_SIZE, source_file);
@@ -1585,7 +1980,7 @@ output_lines (gcov_file, src)
            }
        }
     }
-  
+
   if (source_file)
     fclose (source_file);
 }