OSDN Git Service

Update basic block profiling.
authormeissner <meissner@138bc75d-0d04-0410-961f-82ee72b054a4>
Thu, 2 Sep 1993 10:50:41 +0000 (10:50 +0000)
committermeissner <meissner@138bc75d-0d04-0410-961f-82ee72b054a4>
Thu, 2 Sep 1993 10:50:41 +0000 (10:50 +0000)
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@5249 138bc75d-0d04-0410-961f-82ee72b054a4

gcc/Makefile.in
gcc/config/m68k/sun3.h
gcc/final.c
gcc/libgcc2.c

index 21c6b49..7fce7ed 100644 (file)
@@ -971,7 +971,7 @@ reorg.o : reorg.c $(CONFIG_H) $(RTL_H) conditions.h hard-reg-set.h \
    flags.h output.h
 sched.o : sched.c $(CONFIG_H) $(RTL_H) basic-block.h regs.h hard-reg-set.h \
    flags.h insn-config.h insn-attr.h
-final.o : final.c $(CONFIG_H) $(RTL_H) gvarargs.h flags.h regs.h \
+final.o : final.c $(CONFIG_H) $(RTL_H) $(TREE_H) gvarargs.h flags.h regs.h \
    recog.h conditions.h insn-config.h insn-attr.h real.h output.h \
    hard-reg-set.h insn-flags.h insn-codes.h gstab.h xcoffout.h defaults.h
 recog.o : recog.c $(CONFIG_H) $(RTL_H)  \
index a274fa7..86b3414 100644 (file)
@@ -275,3 +275,16 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
           asm_fprintf (FILE, "%I0r%s", dstr);                          \
         }                                                              \
     } while (0)
+
+#undef BLOCK_PROFILER_CODE
+#define BLOCK_PROFILER_CODE                                            \
+extern int ___tcov_init;                                               \
+                                                                       \
+__bb_init_func (blocks)                                                        \
+       struct bb *blocks;                                              \
+{                                                                      \
+  if (! ___tcov_init)                                                  \
+    ___tcov_init_func ();                                              \
+                                                                       \
+  ___bb_link (blocks->filename, blocks->counts, blocks->ncounts);      \
+}
index 80309b8..7541c18 100644 (file)
@@ -45,6 +45,7 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
 
 #include "config.h"
 #include "gvarargs.h"
+#include "tree.h"
 #include "rtl.h"
 #include "regs.h"
 #include "insn-config.h"
@@ -137,6 +138,9 @@ static rtx debug_insn = 0;
 /* Line number of last NOTE.  */
 static int last_linenum;
 
+/* Filename of last NOTE.  */
+static char *last_filename;
+
 /* Number of basic blocks seen so far;
    used if profile_block_flag is set.  */
 static int count_basic_blocks;
@@ -239,6 +243,37 @@ rtx final_sequence;
 /* Indexed by line number, nonzero if there is a note for that line.  */
 
 static char *line_note_exists;
+
+/* Linked list to hold line numbers for each basic block.  */
+
+struct bb_list {
+  struct bb_list *next;                /* pointer to next basic block */
+  int line_num;                        /* line number */
+  int file_label_num;          /* LPBC<n> label # for stored filename */
+  int func_label_num;          /* LPBC<n> label # for stored function name */
+};
+
+static struct bb_list *bb_head = 0;            /* Head of basic block list */
+static struct bb_list **bb_tail = &bb_head;    /* Ptr to store next bb ptr */
+static int bb_file_label_num   = -1;           /* Current label # for file */
+static int bb_func_label_num   = -1;           /* Current label # for func */
+
+/* Linked list to hold the strings for each file and function name output.  */
+
+struct bb_str {
+  struct bb_str *next;         /* pointer to next string */
+  char *string;                        /* string */
+  int label_num;               /* label number */
+  int length;                  /* string length */
+};
+
+static struct bb_str *sbb_head = 0;            /* Head of string list.  */
+static struct bb_str **sbb_tail        = &sbb_head;    /* Ptr to store next bb str */
+static int sbb_label_num       = 0;            /* Last label used */
+
+static int add_bb_string PROTO((char *, int));
+static void add_bb PROTO((FILE *));
+
 \f
 /* Initialize data in final at the beginning of a compilation.  */
 
@@ -264,34 +299,81 @@ end_final (filename)
 
   if (profile_block_flag)
     {
-      char name[12];
+      char name[20];
+      int align = exact_log2 (BIGGEST_ALIGNMENT / BITS_PER_UNIT);
+      int size = (INT_TYPE_SIZE / BITS_PER_UNIT) * count_basic_blocks;
+      int rounded = size;
+      struct bb_list *ptr;
+      struct bb_str *sptr;
+
+      rounded += (BIGGEST_ALIGNMENT / BITS_PER_UNIT) - 1;
+      rounded = (rounded / (BIGGEST_ALIGNMENT / BITS_PER_UNIT)
+                * (BIGGEST_ALIGNMENT / BITS_PER_UNIT));
 
       data_section ();
 
-      /* Output the main header, of 6 words:
+      /* Output the main header, of 10 words:
         0:  1 if this file's initialized, else 0.
-        1:  address of file name.
-        2:  address of table of counts.
-        4:  number of counts in the table.
-        5:  always 0, for compatibility with Sun.
-        6:  extra word added by GNU: address of address table
-             which contains addresses of basic blocks,
-             in parallel with the table of counts.  */
-      ASM_OUTPUT_ALIGN (asm_out_file,
-                       exact_log2 (BIGGEST_ALIGNMENT / BITS_PER_UNIT));
+        1:  address of file name (LPBX1).
+        2:  address of table of counts (LPBX2).
+        3:  number of counts in the table.
+        4:  always 0, for compatibility with Sun.
+
+         The following are GNU extensions:
+
+        5:  address of table of start addrs of basic blocks (LPBX3).
+        6:  Number of bytes in this header.
+        7:  address of table of function names (LPBX4).
+        8:  address of table of line numbers (LPBX5) or 0.
+        9:  address of table of file names (LPBX6) or 0.  */
+
+      ASM_OUTPUT_ALIGN (asm_out_file, align);
 
       ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "LPBX", 0);
+      /* zero word */
       assemble_integer (const0_rtx, UNITS_PER_WORD, 1);
+
+      /* address of filename */
       ASM_GENERATE_INTERNAL_LABEL (name, "LPBX", 1);
       assemble_integer (gen_rtx (SYMBOL_REF, Pmode, name), UNITS_PER_WORD, 1);
+
+      /* address of count table */
       ASM_GENERATE_INTERNAL_LABEL (name, "LPBX", 2);
       assemble_integer (gen_rtx (SYMBOL_REF, Pmode, name), UNITS_PER_WORD, 1);
+
+      /* count of the # of basic blocks */
       assemble_integer (GEN_INT (count_basic_blocks), UNITS_PER_WORD, 1);
+
+      /* zero word (link field) */
       assemble_integer (const0_rtx, UNITS_PER_WORD, 1);
+
+      /* address of basic block start address table */
       ASM_GENERATE_INTERNAL_LABEL (name, "LPBX", 3);
       assemble_integer (gen_rtx (SYMBOL_REF, Pmode, name), UNITS_PER_WORD, 1);
 
-      /* Output the file name.  */
+      /* byte count for extended structure.  */
+      assemble_integer (GEN_INT (10 * UNITS_PER_WORD), UNITS_PER_WORD, 1);
+
+      /* address of function name table */
+      ASM_GENERATE_INTERNAL_LABEL (name, "LPBX", 4);
+      assemble_integer (gen_rtx (SYMBOL_REF, Pmode, name), UNITS_PER_WORD, 1);
+
+      /* address of line number and filename tables if debugging.  */
+      if (write_symbols != NO_DEBUG)
+       {
+         ASM_GENERATE_INTERNAL_LABEL (name, "LPBX", 5);
+         assemble_integer (gen_rtx (SYMBOL_REF, Pmode, name), UNITS_PER_WORD, 1);
+         ASM_GENERATE_INTERNAL_LABEL (name, "LPBX", 6);
+         assemble_integer (gen_rtx (SYMBOL_REF, Pmode, name), UNITS_PER_WORD, 1);
+       }
+      else
+       {
+         assemble_integer (const0_rtx, UNITS_PER_WORD, 1);
+         assemble_integer (const0_rtx, UNITS_PER_WORD, 1);
+       }
+
+      /* Output the file name changing the suffix to .d for Sun tcov
+        compatibility.  */
       ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "LPBX", 1);
       {
        int len = strlen (filename);
@@ -302,29 +384,98 @@ end_final (filename)
        assemble_string (data_file, strlen (data_file) + 1);
       }
 
-      /* Realign data section.  */
-      ASM_OUTPUT_ALIGN (asm_out_file,
-                       exact_log2 (BIGGEST_ALIGNMENT / BITS_PER_UNIT));
-
       /* Make space for the table of counts.  */
-      ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "LPBX", 2);
-      if (count_basic_blocks != 0)
-       assemble_zeros (INT_TYPE_SIZE / BITS_PER_UNIT * count_basic_blocks);
+      if (flag_no_common || size == 0)
+       {
+         /* Realign data section.  */
+         ASM_OUTPUT_ALIGN (asm_out_file, align);
+         ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "LPBX", 2);
+         if (size != 0)
+           assemble_zeros (size);
+       }
+      else
+       {
+         ASM_GENERATE_INTERNAL_LABEL (name, "LPBX", 2);
+#ifdef ASM_OUTPUT_SHARED_LOCAL
+         if (flag_shared_data)
+           ASM_OUTPUT_SHARED_LOCAL (asm_out_file, name, size, rounded);
+         else
+#endif
+#ifdef ASM_OUTPUT_ALIGNED_LOCAL
+           ASM_OUTPUT_ALIGNED_LOCAL (asm_out_file, name, size, align);
+#else
+           ASM_OUTPUT_LOCAL (asm_out_file, name, size, rounded);
+#endif
+       }
 
-      /* Output the table of addresses.  */
+      /* Output any basic block strings */
       readonly_data_section ();
+      if (sbb_head)
+       {
+         ASM_OUTPUT_ALIGN (asm_out_file, align);
+         for (sptr = sbb_head; sptr != 0; sptr = sptr->next)
+           {
+             ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "LPBC", sptr->label_num);
+             assemble_string (sptr->string, sptr->length);
+           }
+       }
+
+      /* Output the table of addresses.  */
       /* Realign in new section */
-      ASM_OUTPUT_ALIGN (asm_out_file,
-                       floor_log2 (BIGGEST_ALIGNMENT / BITS_PER_UNIT));
+      ASM_OUTPUT_ALIGN (asm_out_file, align);
       ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "LPBX", 3);
       for (i = 0; i < count_basic_blocks; i++)
        {
-         char name[12];
          ASM_GENERATE_INTERNAL_LABEL (name, "LPB", i);
          assemble_integer (gen_rtx (SYMBOL_REF, Pmode, name),
                            UNITS_PER_WORD, 1);
        }
 
+      /* Output the table of function names.  */
+      ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "LPBX", 4);
+      for ((ptr = bb_head), (i = 0); ptr != 0; (ptr = ptr->next), i++)
+       {
+         if (ptr->func_label_num >= 0)
+           {
+             ASM_GENERATE_INTERNAL_LABEL (name, "LPBC", ptr->func_label_num);
+             assemble_integer (gen_rtx (SYMBOL_REF, Pmode, name),
+                               UNITS_PER_WORD, 1);
+           }
+         else
+           assemble_integer (const0_rtx, UNITS_PER_WORD, 1);
+       }
+
+      for ( ; i < count_basic_blocks; i++)
+       assemble_integer (const0_rtx, UNITS_PER_WORD, 1);
+
+      if (write_symbols != NO_DEBUG)
+       {
+         /* Output the table of line numbers.  */
+         ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "LPBX", 5);
+         for ((ptr = bb_head), (i = 0); ptr != 0; (ptr = ptr->next), i++)
+           assemble_integer (GEN_INT (ptr->line_num), UNITS_PER_WORD, 1);
+
+         for ( ; i < count_basic_blocks; i++)
+           assemble_integer (const0_rtx, UNITS_PER_WORD, 1);
+
+         /* Output the table of file names.  */
+         ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "LPBX", 6);
+         for ((ptr = bb_head), (i = 0); ptr != 0; (ptr = ptr->next), i++)
+           {
+             if (ptr->file_label_num >= 0)
+               {
+                 ASM_GENERATE_INTERNAL_LABEL (name, "LPBC", ptr->file_label_num);
+                 assemble_integer (gen_rtx (SYMBOL_REF, Pmode, name),
+                                   UNITS_PER_WORD, 1);
+               }
+             else
+               assemble_integer (const0_rtx, UNITS_PER_WORD, 1);
+           }
+
+         for ( ; i < count_basic_blocks; i++)
+           assemble_integer (const0_rtx, UNITS_PER_WORD, 1);
+       }
+
       /* End with the address of the table of addresses,
         so we can find it easily, as the last word in the file's text.  */
       ASM_GENERATE_INTERNAL_LABEL (name, "LPBX", 3);
@@ -761,6 +912,15 @@ final_start_function (first, file, optimize)
     profile_after_prologue (file);
 
   profile_label_no++;
+
+  /* If we are doing basic block profiling, remember a printable version
+     of the function name.  */
+  if (profile_block_flag)
+    {
+      char *junk = "function";
+      bb_func_label_num =
+       add_bb_string ((*decl_printable_name) (current_function_decl, &junk), FALSE);
+    }
 }
 
 static void
@@ -894,10 +1054,93 @@ final_end_function (first, file, optimize)
     xcoffout_end_epilogue (file);
 #endif
 
+  bb_func_label_num = -1;      /* not in function, nuke label # */
+
   /* If FUNCTION_EPILOGUE is not defined, then the function body
      itself contains return instructions wherever needed.  */
 }
 \f
+/* Add a block to the linked list that remembers the current line/file/function
+   for basic block profiling.  Emit the label in front of the basic block and
+   the instructions that increment the count field.  */
+
+static void
+add_bb (file)
+     FILE *file;
+{
+  struct bb_list *ptr = (struct bb_list *) permalloc (sizeof (struct bb_list));
+
+  /* Add basic block to linked list.  */
+  ptr->next = 0;
+  ptr->line_num = last_linenum;
+  ptr->file_label_num = bb_file_label_num;
+  ptr->func_label_num = bb_func_label_num;
+  *bb_tail = ptr;
+  bb_tail = &ptr->next;
+
+  /* Enable the table of basic-block use counts
+     to point at the code it applies to.  */
+  ASM_OUTPUT_INTERNAL_LABEL (file, "LPB", count_basic_blocks);
+
+  /* Before first insn of this basic block, increment the
+     count of times it was entered.  */
+#ifdef BLOCK_PROFILER
+  BLOCK_PROFILER (file, count_basic_blocks);
+  CC_STATUS_INIT;
+#endif
+
+  new_block = 0;
+  count_basic_blocks++;
+}
+
+/* Add a string to be used for basic block profiling.  */
+
+static int
+add_bb_string (string, perm_p)
+     char *string;
+     int perm_p;
+{
+  int len;
+  struct bb_str *ptr = 0;
+
+  if (!string)
+    {
+      string = "<unknown>";
+      perm_p = TRUE;
+    }
+
+  /* Allocate a new string if the current string isn't permanent.  If
+     the string is permanent search for the same string in other
+     allocations.  */
+
+  len = strlen (string) + 1;
+  if (!perm_p)
+    {
+      char *p = (char *) permalloc (len);
+      bcopy (string, p, len);
+      string = p;
+    }
+  else
+    for (ptr = sbb_head; ptr != (struct bb_str *)0; ptr = ptr->next)
+      if (ptr->string == string)
+       break;
+
+  /* Allocate a new string block if we need to.  */
+  if (!ptr)
+    {
+      ptr = (struct bb_str *) permalloc (sizeof (*ptr));
+      ptr->next = 0;
+      ptr->length = len;
+      ptr->label_num = sbb_label_num++;
+      ptr->string = string;
+      *sbb_tail = ptr;
+      sbb_tail = &ptr->next;
+    }
+
+  return ptr->label_num;
+}
+
+\f
 /* Output assembler code for some insns: all or part of a function.
    For description of args, see `final_start_function', above.
 
@@ -974,19 +1217,7 @@ final (first, file, optimize, prescan)
   /* Do basic-block profiling here
      if the last insn was a conditional branch.  */
   if (profile_block_flag && new_block)
-    {
-      new_block = 0;
-      /* Enable the table of basic-block use counts
-        to point at the code it applies to.  */
-      ASM_OUTPUT_INTERNAL_LABEL (file, "LPB", count_basic_blocks);
-      /* Before first insn of this basic block, increment the
-        count of times it was entered.  */
-#ifdef BLOCK_PROFILER
-      BLOCK_PROFILER (file, count_basic_blocks);
-      CC_STATUS_INIT;
-#endif
-      count_basic_blocks++;
-    }
+    add_bb (file);
 }
 \f
 /* The final scan for one insn, INSN.
@@ -1354,19 +1585,7 @@ final_scan_insn (insn, file, optimize, prescan, nopeepholes)
        /* Do basic-block profiling when we reach a new block.
           Done here to avoid jump tables.  */
        if (profile_block_flag && new_block)
-         {
-           new_block = 0;
-           /* Enable the table of basic-block use counts
-              to point at the code it applies to.  */
-           ASM_OUTPUT_INTERNAL_LABEL (file, "LPB", count_basic_blocks);
-           /* Before first insn of this basic block, increment the
-              count of times it was entered.  */
-#ifdef BLOCK_PROFILER
-           BLOCK_PROFILER (file, count_basic_blocks);
-           CC_STATUS_INIT;
-#endif
-           count_basic_blocks++;
-         }
+         add_bb (file);
 
        if (GET_CODE (body) == ASM_INPUT)
          {
@@ -1777,6 +1996,15 @@ output_source_line (file, insn)
   char ltext_label_name[100];
   register char *filename = NOTE_SOURCE_FILE (insn);
 
+  /* Remember filename for basic block profiling.
+     Filenames are allocated on the permanent obstack
+     or are passed in ARGV, so we don't have to save
+     the string.  */
+
+  if (profile_block_flag && last_filename != filename)
+    bb_file_label_num = add_bb_string (filename, TRUE);
+
+  last_filename = filename;
   last_linenum = NOTE_LINE_NUMBER (insn);
 
   if (write_symbols != NO_DEBUG)
index c4f35c8..48c0b91 100644 (file)
@@ -1285,35 +1285,199 @@ __eprintf (string, expression, line, filename)
 #endif
 
 #ifdef L_bb
-/* Avoid warning from ranlib about empty object file.  */
-void
-__bb_avoid_warning ()
-{}
 
-#if defined (__sun__) && defined (__mc68000__)
+/* Structure emitted by -a  */
 struct bb
 {
-  int initialized;
-  char *filename;
-  int *counts;
-  int ncounts;
-  int zero_word;
-  int *addresses;
+  long zero_word;
+  const char *filename;
+  long *counts;
+  long ncounts;
+  struct bb *next;
+  const unsigned long *addresses;
+
+  /* Older GCC's did not emit these fields.  */
+  long nwords;
+  const char **functions;
+  const long *line_nums;
+  const char **filenames;
 };
 
-extern int ___tcov_init;
+#ifdef BLOCK_PROFILER_CODE
+BLOCK_PROFILER_CODE
+#else
+
+/* Simple minded basic block profiling output dumper for
+   systems that don't provde tcov support.  At present,
+   it requires atexit and stdio.  */
+
+#include <stdio.h>
 
-__bb_init_func (blocks)
-       struct bb *blocks;
+#ifdef HAVE_ATEXIT
+extern void atexit (void (*) (void));
+#define ON_EXIT(FUNC,ARG) atexit ((FUNC))
+#else
+#ifdef sun
+extern void on_exit (void*, void*);
+#define ON_EXIT(FUNC,ARG) on_exit ((FUNC), (ARG))
+#endif
+#endif
+
+static struct bb *bb_head = (struct bb *)0;
+
+/* Return the number of digits needed to print a value */
+/* __inline__ */ static int num_digits (long value, int base)
 {
-  if (! ___tcov_init)
-    ___tcov_init_func ();
+  int minus = (value < 0 && base != 16);
+  unsigned long v = (minus) ? -value : value;
+  int ret = minus;
 
-  ___bb_link (blocks->filename, blocks->counts, blocks->ncounts);
+  do
+    {
+      v /= base;
+      ret++;
+    }
+  while (v);
+
+  return ret;
 }
 
+void
+__bb_exit_func (void)
+{
+  FILE *file = fopen ("bb.out", "a");
+  long time_value;
+
+  if (!file)
+    perror ("bb.out");
+
+  else
+    {
+      struct bb *ptr;
+
+      /* This is somewhat type incorrect, but it avoids worrying about
+        exactly where time.h is included from.  It should be ok unless
+        a void * differs from other pointer formats, or if sizeof(long)
+        is < sizeof (time_t).  It would be nice if we could assume the
+        use of rationale standards here.  */
+
+      time((void *) &time_value);
+      fprintf (file, "Basic block profiling finished on %s\n", ctime ((void *) &time_value));
+
+      /* We check the length field explicitly in order to allow compatibility
+        with older GCC's which did not provide it.  */
+
+      for (ptr = bb_head; ptr != (struct bb *)0; ptr = ptr->next)
+       {
+         int i;
+         int func_p    = (ptr->nwords >= sizeof (struct bb) && ptr->nwords <= 1000);
+         int line_p    = (func_p && ptr->line_nums);
+         int file_p    = (func_p && ptr->filenames);
+         long ncounts  = ptr->ncounts;
+         long cnt_max  = 0;
+         long line_max = 0;
+         long addr_max = 0;
+         int file_len  = 0;
+         int func_len  = 0;
+         int blk_len   = num_digits (ncounts, 10);
+         int cnt_len;
+         int line_len;
+         int addr_len;
+
+         fprintf (file, "File %s, %ld basic blocks \n\n",
+                  ptr->filename, ncounts);
+
+         /* Get max values for each field.  */
+         for (i = 0; i < ncounts; i++)
+           {
+             const char *p;
+             int len;
+
+             if (cnt_max < ptr->counts[i])
+               cnt_max = ptr->counts[i];
+
+             if (addr_max < ptr->addresses[i])
+               addr_max = ptr->addresses[i];
+
+             if (line_p && line_max < ptr->line_nums[i])
+               line_max = ptr->line_nums[i];
+
+             if (func_p)
+               {
+                 p = (ptr->functions[i]) ? (ptr->functions[i]) : "<none>";
+                 len = strlen (p);
+                 if (func_len < len)
+                   func_len = len;
+               }
+
+             if (file_p)
+               {
+                 p = (ptr->filenames[i]) ? (ptr->filenames[i]) : "<none>";
+                 len = strlen (p);
+                 if (file_len < len)
+                   file_len = len;
+               }
+           }
+
+         addr_len = num_digits (addr_max, 16);
+         cnt_len  = num_digits (cnt_max, 10);
+         line_len = num_digits (line_max, 10);
+
+         /* Now print out the basic block information.  */
+         for (i = 0; i < ncounts; i++)
+           {
+             fprintf (file,
+                      "    Block #%*d: executed %*ld time(s) address=0x%.*lx",
+                      blk_len, i+1,
+                      cnt_len, ptr->counts[i],
+                      addr_len, ptr->addresses[i]);
+
+             if (func_p)
+               fprintf (file, " function=%-*s", func_len,
+                        (ptr->functions[i]) ? ptr->functions[i] : "<none>");
+
+             if (line_p)
+               fprintf (file, " line=%*d", line_len, ptr->line_nums[i]);
+
+             if (file_p)
+               fprintf (file, " file=%s",
+                        (ptr->filenames[i]) ? ptr->filenames[i] : "<none>");
+
+             fprintf (file, "\n");
+           }
+
+         fprintf (file, "\n");
+         fflush (file);
+       }
+
+      fprintf (file, "\n\n");
+      fclose (file);
+    }
+}
+
+void
+__bb_init_func (struct bb *blocks)
+{
+  /* User is supposed to check whether the first word is non-0,
+     but just in case.... */
+
+  if (blocks->zero_word)
+    return;
+
+#ifdef ON_EXIT
+  /* Initialize destructor.  */
+  if (!bb_head)
+    ON_EXIT (__bb_exit_func, 0);
 #endif
-#endif
+
+  /* Set up linked list.  */
+  blocks->zero_word = 1;
+  blocks->next = bb_head;
+  bb_head = blocks;
+}
+
+#endif /* !BLOCK_PROFILER_CODE */
+#endif /* L_bb */
 \f
 /* frills for C++ */