#include "config.h"
#include "gvarargs.h"
+#include "tree.h"
#include "rtl.h"
#include "regs.h"
#include "insn-config.h"
/* 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;
/* 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. */
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);
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);
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
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.
/* 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.
/* 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)
{
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)
#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++ */