From: meissner Date: Thu, 2 Sep 1993 10:50:41 +0000 (+0000) Subject: Update basic block profiling. X-Git-Url: http://git.sourceforge.jp/view?a=commitdiff_plain;ds=sidebyside;h=543e7f6c8f2eef0e3d3dc26fa2c3c0e5f44e095a;p=pf3gnuchains%2Fgcc-fork.git Update basic block profiling. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@5249 138bc75d-0d04-0410-961f-82ee72b054a4 --- diff --git a/gcc/Makefile.in b/gcc/Makefile.in index 21c6b49411f..7fce7ed0b7f 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -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) \ diff --git a/gcc/config/m68k/sun3.h b/gcc/config/m68k/sun3.h index a274fa7d4b5..86b3414c16b 100644 --- a/gcc/config/m68k/sun3.h +++ b/gcc/config/m68k/sun3.h @@ -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); \ +} diff --git a/gcc/final.c b/gcc/final.c index 80309b89b1e..7541c1802b0 100644 --- a/gcc/final.c +++ b/gcc/final.c @@ -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 label # for stored filename */ + int func_label_num; /* LPBC 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 *)); + /* 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. */ } +/* 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 = ""; + 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; +} + + /* 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); } /* 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) diff --git a/gcc/libgcc2.c b/gcc/libgcc2.c index c4f35c83e62..48c0b91d5d8 100644 --- a/gcc/libgcc2.c +++ b/gcc/libgcc2.c @@ -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 -__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]) : ""; + len = strlen (p); + if (func_len < len) + func_len = len; + } + + if (file_p) + { + p = (ptr->filenames[i]) ? (ptr->filenames[i]) : ""; + 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] : ""); + + 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] : ""); + + 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 */ /* frills for C++ */