From: hubicka Date: Thu, 9 May 2002 12:54:19 +0000 (+0000) Subject: * final.c (end_final): Use C trees to output data structures for profiling. X-Git-Url: http://git.sourceforge.jp/view?a=commitdiff_plain;h=90c2be4439c656f63074408d6c91442511a9e061;p=pf3gnuchains%2Fgcc-fork.git * final.c (end_final): Use C trees to output data structures for profiling. * Makefile.in (LIBGCC_DEPS): Added missing dependency on gcov-io.h (profile.o): New dependency profile.h (final.o): New dependency profile.h * profile.h: New file. New global structure profile_info. * final.h (count_edges_instrumented_now): Declare. (current_function_cfg_checksum): Declare. (function_list): New structure. (functions_head, functions_tail): New static variables. (end_final): Emits more data, removed some -ax stuff. (final): Stores function names and chcksums. * gcov-io.h (__write_gcov_string): New function. (__read_gcov_string): New function. * gcov.c (read_profile): New function. (create_program_flow_graph): Uses read_profile instead of reading da_file. (read_files): Removed da_file checking, it's done by read_profile now. * libgcc2.c (bb_function_info): New structure. (bb): New field in structure, removed some -ax stuff. (__bb_exit_func): Changed structure of da_file. * profile.c (count_edges_instrumented_now): New global variable. (current_function_cfg_checksum): New global variable. (max_counter_in_program): New global variable. (get_exec_counts): New function. (compute_checksum): New function. (instrument_edges): Sets count_edges_instrumented_now. (compute_branch_probabilities): Uses get_exec_counts instead of reading da_file. (branch_prob): Calls compute_checksum and writes extra data to bbg_file. (init_branch_prob): Removed da_file checking, done in get_exec_counts now. (end_branch_prob): Removed da_file checking, done in get_exec_counts now. * gcov.texi: Updated information about gcov file format. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@53326 138bc75d-0d04-0410-961f-82ee72b054a4 --- diff --git a/gcc/ChangeLog b/gcc/ChangeLog index bae7fb00973..5100e798097 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,42 @@ +Thu May 9 14:52:45 CEST 2002 Jan Hubicka + Pavel Nejedly + + * final.c (end_final): Use C trees to output data structures for profiling. + + * Makefile.in (LIBGCC_DEPS): Added missing dependency on gcov-io.h + (profile.o): New dependency profile.h + (final.o): New dependency profile.h + * profile.h: New file. New global structure profile_info. + * final.h (count_edges_instrumented_now): Declare. + (current_function_cfg_checksum): Declare. + (function_list): New structure. + (functions_head, functions_tail): New static variables. + (end_final): Emits more data, removed some -ax stuff. + (final): Stores function names and chcksums. + * gcov-io.h (__write_gcov_string): New function. + (__read_gcov_string): New function. + * gcov.c (read_profile): New function. + (create_program_flow_graph): Uses read_profile instead of reading + da_file. + (read_files): Removed da_file checking, it's done by read_profile now. + * libgcc2.c (bb_function_info): New structure. + (bb): New field in structure, removed some -ax stuff. + (__bb_exit_func): Changed structure of da_file. + * profile.c (count_edges_instrumented_now): New global variable. + (current_function_cfg_checksum): New global variable. + (max_counter_in_program): New global variable. + (get_exec_counts): New function. + (compute_checksum): New function. + (instrument_edges): Sets count_edges_instrumented_now. + (compute_branch_probabilities): Uses get_exec_counts instead of + reading da_file. + (branch_prob): Calls compute_checksum and writes extra data to bbg_file. + (init_branch_prob): Removed da_file checking, done in get_exec_counts + now. + (end_branch_prob): Removed da_file checking, done in get_exec_counts + now. + * gcov.texi: Updated information about gcov file format. + 2002-05-09 Kazu Hirata * sbitmap.c: Fix formatting. diff --git a/gcc/Makefile.in b/gcc/Makefile.in index 0f21cf4c96a..7affc56800a 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -1484,7 +1484,7 @@ conflict.o : conflict.c $(CONFIG_H) $(SYSTEM_H) $(OBSTACK_H) $(HASHTAB_H) \ profile.o : profile.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) $(TREE_H) flags.h \ insn-config.h output.h $(REGS_H) $(EXPR_H) function.h \ gcov-io.h toplev.h $(GGC_H) hard-reg-set.h $(BASIC_BLOCK_H) $(TARGET_H) \ - langhooks.h + langhooks.h profile.h libfuncs.h loop.o : loop.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) flags.h $(LOOP_H) \ insn-config.h $(REGS_H) hard-reg-set.h $(RECOG_H) $(EXPR_H) \ real.h $(PREDICT_H) $(BASIC_BLOCK_H) function.h \ @@ -1568,7 +1568,7 @@ sched-vis.o : sched-vis.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) sched-int.h \ $(TARGET_H) real.h final.o : final.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) $(TREE_H) flags.h intl.h \ $(REGS_H) $(RECOG_H) conditions.h insn-config.h $(INSN_ATTR_H) function.h \ - real.h output.h hard-reg-set.h except.h debug.h xcoffout.h \ + real.h output.h hard-reg-set.h except.h debug.h xcoffout.h profile.h \ toplev.h reload.h dwarf2out.h $(BASIC_BLOCK_H) $(TM_P_H) $(TARGET_H) $(EXPR_H) recog.o : recog.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) function.h $(BASIC_BLOCK_H) \ $(REGS_H) $(RECOG_H) $(EXPR_H) hard-reg-set.h flags.h insn-config.h \ diff --git a/gcc/doc/gcov.texi b/gcc/doc/gcov.texi index 86b64e0e8e5..5f5f18bd6d1 100644 --- a/gcc/doc/gcov.texi +++ b/gcc/doc/gcov.texi @@ -363,6 +363,8 @@ program flow. In the @file{.bbg} file, the format is: @smallexample + name of function #0 + checksum of function #0 number of basic blocks for function #0 (4-byte number) total number of arcs for function #0 (4-byte number) count of arcs in basic block #0 (4-byte number) @@ -383,6 +385,9 @@ A @minus{}1 (stored as a 4-byte number) is used to separate each function's list of basic blocks, and to verify that the file has been read correctly. +The function name is stored as a @minus{}1 (4 bytes), the length (4 bytes), +the name itself (padded to 4-byte boundary) followed by a @minus{}1 (4 bytes). + The @file{.da} file is generated when a program containing object files built with the GCC @option{-fprofile-arcs} option is executed. A separate @file{.da} file is created for each source file compiled with @@ -390,15 +395,32 @@ this option, and the name of the @file{.da} file is stored as an absolute pathname in the resulting object file. This path name is derived from the source file name by substituting a @file{.da} suffix. -The format of the @file{.da} file is fairly simple. The first 8-byte -number is the number of counts in the file, followed by the counts -(stored as 8-byte numbers). Each count corresponds to the number of -times each arc in the program is executed. The counts are cumulative; -each time the program is executed, it attempts to combine the existing -@file{.da} files with the new counts for this invocation of the -program. It ignores the contents of any @file{.da} files whose number of -arcs doesn't correspond to the current program, and merely overwrites -them instead. +The @file{.da} consists of several blocks (one for each run) with the following structure: +@smallexample + "magic" number @minus{}123 (4-byte number) + number of functions (4-byte number) + length of the "extension block" in bytes + extension block (variable length) + name of function #0 (the same format as in .bbg file) + checksum of function #0 + number of instrumented arcs (4-byte number) + count of arc #0 (8-byte number) + count of arc #1 (8-byte number) + @dots{} + count of arc #M_0 (8-byte number) + name of function #1 (the same format as in .bbg file) + checksum of function #1 + @dots{} +@end smallexample +The current structure of the extension block is as follows: +@smallexample + number of instrumented arcs in whole program (4-byte number) + sum all of instrumented arcs in whole program (8-byte number) + maximal value of counter in whole program (8-byte number) + number of instrumented arcs in the object file (4-byte number) + sum all of instrumented arcs in the object file (8-byte number) + maximal value of counter in the object file (8-byte number) +@end smallexample All three of these files use the functions in @file{gcov-io.h} to store integers; the functions in this header provide a machine-independent diff --git a/gcc/final.c b/gcc/final.c index a0adcbe70b4..57e94e78e4f 100644 --- a/gcc/final.c +++ b/gcc/final.c @@ -68,6 +68,7 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA #include "target.h" #include "debug.h" #include "expr.h" +#include "profile.h" #ifdef XCOFF_DEBUGGING_INFO #include "xcoffout.h" /* Needed for external data @@ -114,9 +115,6 @@ static int high_function_linenum; /* Filename of last NOTE. */ static const char *last_filename; -/* Number of instrumented arcs when profile_arc_flag is set. */ -extern int count_instrumented_edges; - extern int length_unit_log; /* This is defined in insn-attrtab.c. */ /* Nonzero while outputting an `asm' with operands. @@ -198,6 +196,17 @@ static char *line_note_exists; rtx current_insn_predicate; #endif +struct function_list +{ + struct function_list *next; /* next function */ + const char *name; /* function name */ + long cfg_checksum; /* function checksum */ + long count_edges; /* number of intrumented edges in this function */ +}; + +static struct function_list *functions_head = 0; +static struct function_list **functions_tail = &functions_head; + #ifdef HAVE_ATTR_length static int asm_insn_count PARAMS ((rtx)); #endif @@ -237,7 +246,7 @@ init_final (filename) } /* Called at end of source file, - to output the block-profiling table for this entire compilation. */ + to output the arc-profiling table for this entire compilation. */ void end_final (filename) @@ -246,128 +255,268 @@ end_final (filename) if (profile_arc_flag) { char name[20]; - int align = exact_log2 (BIGGEST_ALIGNMENT / BITS_PER_UNIT); - int size, rounded; - int long_bytes = LONG_TYPE_SIZE / BITS_PER_UNIT; - int gcov_type_bytes = GCOV_TYPE_SIZE / BITS_PER_UNIT; - int pointer_bytes = POINTER_SIZE / BITS_PER_UNIT; - unsigned int align2 = LONG_TYPE_SIZE; - - size = gcov_type_bytes * count_instrumented_edges; - rounded = size; - - rounded += (BIGGEST_ALIGNMENT / BITS_PER_UNIT) - 1; - rounded = (rounded / (BIGGEST_ALIGNMENT / BITS_PER_UNIT) - * (BIGGEST_ALIGNMENT / BITS_PER_UNIT)); - - /* ??? This _really_ ought to be done with a structure layout - and with assemble_constructor. If long_bytes != pointer_bytes - we'll be emitting unaligned data at some point. */ - if (long_bytes != pointer_bytes) - abort (); - - data_section (); - - /* Output the main header, of 11 words: - 0: 1 if this file is initialized, else 0. - 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. + tree string_type, string_cst; + tree structure_decl, structure_value, structure_pointer_type; + tree field_decl, decl_chain, value_chain; + tree nwords_field_value, domain_type; + + /* Build types. */ + string_type = build_pointer_type (char_type_node); + + /* Libgcc2 bb structure. */ + structure_decl = make_node (RECORD_TYPE); + TYPE_PACKED (structure_decl) = flag_pack_struct; + structure_pointer_type = build_pointer_type (structure_decl); + + /* Output the main header, of 7 words: + 0: 1 if this file is initialized, else 0. + 1: address of file name (LPBX1). + 2: address of table of counts (LPBX2). + 3: number of counts in the table. + 4: always 0, libgcc2 uses this as a pointer to next ``struct bb'' 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. - 10: space reserved for basic block profiling. */ - - ASM_OUTPUT_ALIGN (asm_out_file, align); + 5: Number of bytes in this header. + 6: address of table of function checksums (LPBX7). */ - ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "LPBX", 0); - - /* Zero word. */ - assemble_integer (const0_rtx, long_bytes, align2, 1); + /* The zero word. */ + decl_chain = + build_decl (FIELD_DECL, get_identifier ("zero_word"), + long_integer_type_node); + value_chain = build_tree_list (decl_chain, integer_zero_node); /* Address of filename. */ - ASM_GENERATE_INTERNAL_LABEL (name, "LPBX", 1); - assemble_integer (gen_rtx_SYMBOL_REF (Pmode, name), pointer_bytes, - align2, 1); + { + char *cwd = getpwd (); + int da_filename_len = strlen (filename) + strlen (cwd) + 4 + 1; + char *da_filename = (char *) alloca (da_filename_len); + + strcpy (da_filename, cwd); + strcat (da_filename, "/"); + strcat (da_filename, filename); + strip_off_ending (da_filename, da_filename_len - 3); + strcat (da_filename, ".da"); + field_decl = + build_decl (FIELD_DECL, get_identifier ("filename"), string_type); + string_cst = build_string (strlen (da_filename) + 1, da_filename); + domain_type = build_index_type (build_int_2 (strlen (da_filename) + 1, + 0)); + TREE_TYPE (string_cst) = + build_array_type (char_type_node, domain_type); + value_chain = tree_cons (field_decl, + build1 (ADDR_EXPR, string_type, string_cst), + value_chain); + TREE_CHAIN (field_decl) = decl_chain; + decl_chain = field_decl; + } - /* Address of count table. */ - ASM_GENERATE_INTERNAL_LABEL (name, "LPBX", 2); - assemble_integer (gen_rtx_SYMBOL_REF (Pmode, name), pointer_bytes, - align2, 1); + /* Table of counts. */ + { + tree gcov_type_type = make_unsigned_type (GCOV_TYPE_SIZE); + tree gcov_type_pointer_type = build_pointer_type (gcov_type_type); + tree gcov_type_array_type, gcov_type_array_pointer_type; + tree domain_tree = build_index_type (build_int_2 + (profile_info. + count_instrumented_edges - 1, + 0)); + tree counts_table; + + gcov_type_array_type = build_array_type (gcov_type_type, domain_tree); + gcov_type_array_pointer_type = + build_pointer_type (gcov_type_array_type); + + /* No values. */ + counts_table = + build (VAR_DECL, gcov_type_array_type, NULL_TREE, NULL_TREE); + TREE_STATIC (counts_table) = 1; + ASM_GENERATE_INTERNAL_LABEL (name, "LPBX", 2); + DECL_NAME (counts_table) = get_identifier (name); + assemble_variable (counts_table, 0, 0, 0); + + field_decl = + build_decl (FIELD_DECL, get_identifier ("counts"), + gcov_type_pointer_type); + value_chain = tree_cons (field_decl, + build1 (ADDR_EXPR, + gcov_type_array_pointer_type, + counts_table), value_chain); + TREE_CHAIN (field_decl) = decl_chain; + decl_chain = field_decl; + } /* Count of the # of instrumented arcs. */ - assemble_integer (GEN_INT (count_instrumented_edges), - long_bytes, align2, 1); + field_decl = + build_decl (FIELD_DECL, get_identifier ("ncounts"), + long_integer_type_node); + value_chain = tree_cons (field_decl, + convert (long_integer_type_node, + build_int_2 (profile_info. + count_instrumented_edges, + 0)), value_chain); + TREE_CHAIN (field_decl) = decl_chain; + decl_chain = field_decl; + + /* Pointer to the next bb. */ + field_decl = + build_decl (FIELD_DECL, get_identifier ("next"), + structure_pointer_type); + value_chain = tree_cons (field_decl, null_pointer_node, value_chain); + TREE_CHAIN (field_decl) = decl_chain; + decl_chain = field_decl; + + /* Number of words. We'll set this after entire structure is laid out. */ + field_decl = + build_decl (FIELD_DECL, get_identifier ("nwords"), + long_integer_type_node); + value_chain = nwords_field_value = + tree_cons (field_decl, NULL, value_chain); + TREE_CHAIN (field_decl) = decl_chain; + decl_chain = field_decl; + + /* struct bb_function []. */ + { + struct function_list *item; + int num_nodes; + tree checksum_field, arc_count_field, name_field; + tree domain; + tree array_value_chain = NULL_TREE; + tree bb_fn_struct_type; + tree bb_fn_struct_array_type; + tree bb_fn_struct_array_pointer_type; + tree bb_fn_struct_pointer_type; + tree field_value, field_value_chain; + + bb_fn_struct_type = make_node (RECORD_TYPE); + TYPE_PACKED (bb_fn_struct_type) = flag_pack_struct; + + checksum_field = build_decl (FIELD_DECL, get_identifier ("checksum"), + long_integer_type_node); + arc_count_field = + build_decl (FIELD_DECL, get_identifier ("arc_count"), + integer_type_node); + TREE_CHAIN (checksum_field) = arc_count_field; + name_field = + build_decl (FIELD_DECL, get_identifier ("name"), string_type); + TREE_CHAIN (arc_count_field) = name_field; + + TYPE_FIELDS (bb_fn_struct_type) = checksum_field; + + num_nodes = 0; + + for (item = functions_head; item != 0; item = item->next) + num_nodes++; + + /* Note that the array contains a terminator, hence no - 1. */ + domain = build_index_type (build_int_2 (num_nodes, 0)); + + bb_fn_struct_pointer_type = build_pointer_type (bb_fn_struct_type); + bb_fn_struct_array_type = build_array_type (bb_fn_struct_type, + domain); + bb_fn_struct_array_pointer_type = + build_pointer_type (bb_fn_struct_array_type); + + layout_type (bb_fn_struct_type); + layout_type (bb_fn_struct_pointer_type); + layout_type (bb_fn_struct_array_type); + layout_type (bb_fn_struct_array_pointer_type); + + for (item = functions_head; item != 0; item = item->next) + { + /* create constructor for structure. */ + field_value_chain = build_tree_list (checksum_field, + convert + (long_integer_type_node, + build_int_2 (item-> + cfg_checksum, + 0))); + field_value_chain = + tree_cons (arc_count_field, + convert (integer_type_node, + build_int_2 (item->count_edges, 0)), + field_value_chain); + + string_cst = build_string (strlen (item->name) + 1, item->name); + domain_type = build_index_type (build_int_2 (strlen (item->name) + + 1, 0)); + TREE_TYPE (string_cst) = build_array_type (char_type_node, + domain_type); + field_value_chain = tree_cons (name_field, + build1 (ADDR_EXPR, string_type, + string_cst), + field_value_chain); + + /* Add to chain. */ + + array_value_chain = tree_cons (NULL_TREE, + build (CONSTRUCTOR, + bb_fn_struct_type, + NULL_TREE, + nreverse + (field_value_chain)), + array_value_chain); + } - /* Zero word (link field). */ - assemble_integer (const0_rtx, pointer_bytes, align2, 1); + /* Add terminator. */ + field_value = build_tree_list (arc_count_field, + convert (integer_type_node, + build_int_2 (-1, 0))); + + array_value_chain = tree_cons (NULL_TREE, + build (CONSTRUCTOR, bb_fn_struct_type, + NULL_TREE, field_value), + array_value_chain); + + + /* Create constructor for array. */ + + field_decl = + build_decl (FIELD_DECL, get_identifier ("function_infos"), + bb_fn_struct_pointer_type); + value_chain = tree_cons (field_decl, + build1 (ADDR_EXPR, + bb_fn_struct_array_pointer_type, + build (CONSTRUCTOR, + bb_fn_struct_array_type, + NULL_TREE, + nreverse + (array_value_chain))), + value_chain); + TREE_CHAIN (field_decl) = decl_chain; + decl_chain = field_decl; + } - assemble_integer (const0_rtx, pointer_bytes, align2, 1); - /* Byte count for extended structure. */ - assemble_integer (GEN_INT (11 * UNITS_PER_WORD), long_bytes, align2, 1); + /* Finish structure. */ + TYPE_FIELDS (structure_decl) = nreverse (decl_chain); + layout_type (structure_decl); - /* Address of function name table. */ - assemble_integer (const0_rtx, pointer_bytes, align2, 1); + structure_value = + build (VAR_DECL, structure_decl, NULL_TREE, NULL_TREE); + DECL_INITIAL (structure_value) = + build (CONSTRUCTOR, structure_decl, NULL_TREE, + nreverse (value_chain)); + TREE_STATIC (structure_value) = 1; + ASM_GENERATE_INTERNAL_LABEL (name, "LPBX", 0); + DECL_NAME (structure_value) = get_identifier (name); - /* Address of line number and filename tables if debugging. */ - assemble_integer (const0_rtx, pointer_bytes, align2, 1); - assemble_integer (const0_rtx, pointer_bytes, align2, 1); + /* Set number of words in this structure. */ + TREE_VALUE (nwords_field_value) = + build_int_2 (TREE_INT_CST_LOW (TYPE_SIZE_UNIT (structure_decl)) / + (INT_TYPE_SIZE / BITS_PER_UNIT), 0); - /* Space for extension ptr (link field). */ - assemble_integer (const0_rtx, UNITS_PER_WORD, align2, 1); + /* Build structure. */ + assemble_variable (structure_value, 0, 0, 0); - /* Output the file name changing the suffix to .d for - Sun tcov compatibility. */ - ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "LPBX", 1); + /* Offset to table of arc counters for thread-safe profiling. */ { - char *cwd = getpwd (); - int len = strlen (filename) + strlen (cwd) + 1; - char *data_file = (char *) alloca (len + 4); - - strcpy (data_file, cwd); - strcat (data_file, "/"); - strcat (data_file, filename); - strip_off_ending (data_file, len); - strcat (data_file, ".da"); - assemble_string (data_file, strlen (data_file) + 1); + tree table_offset_var = make_node (VAR_DECL); + TREE_TYPE (table_offset_var) = build_pointer_type (integer_type_node); + DECL_INITIAL (table_offset_var) = integer_zero_node; + DECL_NAME (table_offset_var) = get_identifier (".LPBF0"); + TREE_STATIC (table_offset_var) = 1; + assemble_variable (table_offset_var, 0, 0, 0); } - - /* Make space for the table of counts. */ - if (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_DECL_LOCAL - ASM_OUTPUT_ALIGNED_DECL_LOCAL (asm_out_file, NULL_TREE, name, - size, BIGGEST_ALIGNMENT); -#else -#ifdef ASM_OUTPUT_ALIGNED_LOCAL - ASM_OUTPUT_ALIGNED_LOCAL (asm_out_file, name, size, - BIGGEST_ALIGNMENT); -#else - ASM_OUTPUT_LOCAL (asm_out_file, name, size, rounded); -#endif -#endif - } } } @@ -1781,6 +1930,24 @@ final (first, file, optimize, prescan) insn = final_scan_insn (insn, file, optimize, prescan, 0); } + /* Store function names for edge-profiling. */ + + if (profile_arc_flag) + { + struct function_list *new_item = xmalloc (sizeof (struct function_list)); + + /* Add function to linked list. */ + new_item->next = 0; + *functions_tail = new_item; + functions_tail = &new_item->next; + + /* Set values. */ + new_item->cfg_checksum = profile_info.current_function_cfg_checksum; + new_item->count_edges = profile_info.count_edges_instrumented_now; + new_item->name = xstrdup (current_function_name); + + } + free (line_note_exists); line_note_exists = NULL; } diff --git a/gcc/function.c b/gcc/function.c index d0ad459a975..9f1c00a08b1 100644 --- a/gcc/function.c +++ b/gcc/function.c @@ -6318,6 +6318,8 @@ prepare_function_start () current_function_outgoing_args_size = 0; + cfun->arc_profile = profile_arc_flag || flag_test_coverage; + (*lang_hooks.function.init) (cfun); if (init_machine_status) (*init_machine_status) (cfun); diff --git a/gcc/function.h b/gcc/function.h index 70164f1d9fa..2a566641eca 100644 --- a/gcc/function.h +++ b/gcc/function.h @@ -437,6 +437,9 @@ struct function generated. */ unsigned int instrument_entry_exit : 1; + /* Nonzero if no profiling should be done for the function. */ + unsigned int arc_profile : 1; + /* Nonzero if profiling code should be generated. */ unsigned int profile : 1; diff --git a/gcc/gcov-io.h b/gcc/gcov-io.h index 7352429c80d..9e1c081b16d 100644 --- a/gcc/gcov-io.h +++ b/gcc/gcov-io.h @@ -24,13 +24,24 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA #include #include -static int __fetch_long PARAMS ((long *, char *, size_t)) ATTRIBUTE_UNUSED; -static int __read_long PARAMS ((long *, FILE *, size_t)) ATTRIBUTE_UNUSED; -static int __write_long PARAMS ((long, FILE *, size_t)) ATTRIBUTE_UNUSED; -static int __fetch_gcov_type PARAMS ((gcov_type *, char *, size_t)) ATTRIBUTE_UNUSED; -static int __store_gcov_type PARAMS ((gcov_type, char *, size_t)) ATTRIBUTE_UNUSED; -static int __read_gcov_type PARAMS ((gcov_type *, FILE *, size_t)) ATTRIBUTE_UNUSED; -static int __write_gcov_type PARAMS ((gcov_type, FILE *, size_t)) ATTRIBUTE_UNUSED; +static int __fetch_long PARAMS ((long *, char *, size_t)) + ATTRIBUTE_UNUSED; +static int __read_long PARAMS ((long *, FILE *, size_t)) + ATTRIBUTE_UNUSED; +static int __write_long PARAMS ((long, FILE *, size_t)) + ATTRIBUTE_UNUSED; +static int __fetch_gcov_type PARAMS ((gcov_type *, char *, size_t)) + ATTRIBUTE_UNUSED; +static int __store_gcov_type PARAMS ((gcov_type, char *, size_t)) + ATTRIBUTE_UNUSED; +static int __read_gcov_type PARAMS ((gcov_type *, FILE *, size_t)) + ATTRIBUTE_UNUSED; +static int __write_gcov_type PARAMS ((gcov_type, FILE *, size_t)) + ATTRIBUTE_UNUSED; +static int __write_gcov_string PARAMS ((const char *, size_t, FILE*, long)) + ATTRIBUTE_UNUSED; +static int __read_gcov_string PARAMS ((char *, size_t, FILE*, long)) + ATTRIBUTE_UNUSED; /* These routines only work for signed values. */ @@ -193,4 +204,94 @@ __read_long (dest, file, bytes) return __fetch_long (dest, c, bytes); } + +/* Writes string in gcov format. */ + +static int +__write_gcov_string (string, length, file, delim) + const char *string; + size_t length; + FILE *file; + long delim; +{ + size_t temp = length + 1; + + /* delimiter */ + if (__write_long (delim, file, 4) != 0) + return 1; + + if (__write_long (length, file, 4) != 0) + return 1; + + if (fwrite (string, temp, 1, file) != 1) + return 1; + + temp &= 3; + + if (temp) + { + char c[4]; + + c[0] = c[1] = c[2] = c[3] = 0; + + if (fwrite (c, sizeof (char), 4 - temp, file) != 4 - temp) + return 1; + } + + if (__write_long (delim, file, 4) != 0) + return 1; + + return 0; +} + +/* Reads string in gcov format. */ + + +static int +__read_gcov_string (string, max_length, file, delim) + char *string; + size_t max_length; + FILE *file; + long delim; +{ + long delim_from_file; + long length; + long read_length; + long tmp; + + if (__read_long (&delim_from_file, file, 4) != 0) + return 1; + + if (delim_from_file != delim) + return 1; + + if (__read_long (&length, file, 4) != 0) + return 1; + + if (length > (long) max_length) + read_length = max_length; + else + read_length = length; + + tmp = (((length + 1) - 1) / 4 + 1) * 4; + /* This is the size occupied by the string in the file */ + + if (fread (string, read_length, 1, file) != 1) + return 1; + + string[read_length] = 0; + + if (fseek (file, tmp - read_length, SEEK_CUR) < 0) + return 1; + + if (__read_long (&delim_from_file, file, 4) != 0) + return 1; + + if (delim_from_file != delim) + return 1; + + return 0; +} + + #endif /* ! GCC_GCOV_IO_H */ diff --git a/gcc/gcov.c b/gcc/gcov.c index 3bb2ce19e2f..fbc3d00e898 100644 --- a/gcc/gcov.c +++ b/gcc/gcov.c @@ -232,6 +232,7 @@ static void print_usage PARAMS ((int)) ATTRIBUTE_NORETURN; static void print_version PARAMS ((void)) ATTRIBUTE_NORETURN; static void init_arc PARAMS ((struct adj_list *, int, int, struct bb_info *)); static struct adj_list *reverse_arcs PARAMS ((struct adj_list *)); +static gcov_type *read_profile PARAMS ((char *, long, int)); static void create_program_flow_graph PARAMS ((struct bb_info_list *)); static void solve_program_flow_graph PARAMS ((struct bb_info_list *)); static void calculate_branch_probs PARAMS ((struct bb_info_list *, int, @@ -538,6 +539,130 @@ reverse_arcs (arcptr) return prev; } +/* Reads profiles from the .da file and compute a hybrid profile. */ + +static gcov_type * +read_profile (function_name, cfg_checksum, instr_arcs) + char *function_name; + long cfg_checksum; + int instr_arcs; +{ + int i; + int okay = 1; + gcov_type *profile; + char *function_name_buffer; + int function_name_buffer_len; + + profile = xmalloc (sizeof (gcov_type) * instr_arcs); + rewind (da_file); + function_name_buffer_len = strlen (function_name) + 1; + function_name_buffer = xmalloc (function_name_buffer_len + 1); + + for (i = 0; i < instr_arcs; i++) + profile[i] = 0; + + if (!da_file) + return profile; + + while (1) + { + long magic, extra_bytes; + long func_count; + int i; + + if (__read_long (&magic, da_file, 4) != 0) + break; + + if (magic != -123) + { + okay = 0; + break; + } + + if (__read_long (&func_count, da_file, 4) != 0) + { + okay = 0; + break; + } + + if (__read_long (&extra_bytes, da_file, 4) != 0) + { + okay = 0; + break; + } + + /* skip extra data emited by __bb_exit_func. */ + fseek (da_file, extra_bytes, SEEK_CUR); + + for (i = 0; i < func_count; i++) + { + long arc_count; + long chksum; + int j; + + if (__read_gcov_string + (function_name_buffer, function_name_buffer_len, da_file, + -1) != 0) + { + okay = 0; + break; + } + + if (__read_long (&chksum, da_file, 4) != 0) + { + okay = 0; + break; + } + + if (__read_long (&arc_count, da_file, 4) != 0) + { + okay = 0; + break; + } + + if (strcmp (function_name_buffer, function_name) != 0 + || arc_count != instr_arcs || chksum != cfg_checksum) + { + /* skip */ + if (fseek (da_file, arc_count * 8, SEEK_CUR) < 0) + { + okay = 0; + break; + } + } + else + { + gcov_type tmp; + + for (j = 0; j < arc_count; j++) + if (__read_gcov_type (&tmp, da_file, 8) != 0) + { + okay = 0; + break; + } + else + { + profile[j] += tmp; + } + } + } + + if (!okay) + break; + + } + + free (function_name_buffer); + + if (!okay) + { + fprintf (stderr, ".da file corrupted!\n"); + free (profile); + abort (); + } + + return profile; +} /* Construct the program flow graph from the .bbg file, and read in the data in the .da file. */ @@ -550,6 +675,29 @@ create_program_flow_graph (bptr) int i; struct adj_list *arcptr; struct bb_info *bb_graph; + long cfg_checksum; + long instr_arcs = 0; + gcov_type *profile; + int profile_pos = 0; + char *function_name; + long function_name_len, tmp; + + /* Read function name. */ + __read_long (&tmp, bbg_file, 4); /* ignore -1. */ + __read_long (&function_name_len, bbg_file, 4); + function_name = xmalloc (function_name_len + 1); + fread (function_name, 1, function_name_len + 1, bbg_file); + + /* Skip padding. */ + tmp = (function_name_len + 1) % 4; + + if (tmp) + fseek (bbg_file, 4 - tmp, SEEK_CUR); + + __read_long (&tmp, bbg_file, 4); /* ignore -1. */ + + /* Read the cfg checksum. */ + __read_long (&cfg_checksum, bbg_file, 4); /* Read the number of blocks. */ __read_long (&num_blocks, bbg_file, 4); @@ -579,7 +727,10 @@ create_program_flow_graph (bptr) init_arc (arcptr, src, dest, bb_graph); __read_long (&flag_bits, bbg_file, 4); - arcptr->on_tree = flag_bits & 0x1; + if (flag_bits & 0x1) + arcptr->on_tree++; + else + instr_arcs++; arcptr->fake = !! (flag_bits & 0x2); arcptr->fall_through = !! (flag_bits & 0x4); } @@ -601,6 +752,10 @@ create_program_flow_graph (bptr) if (bb_graph[i].succ) bb_graph[i].succ = reverse_arcs (bb_graph[i].succ); + /* Read profile from the .da file. */ + + profile = read_profile (function_name, cfg_checksum, instr_arcs); + /* For each arc not on the spanning tree, set its execution count from the .da file. */ @@ -613,15 +768,13 @@ create_program_flow_graph (bptr) for (arcptr = bb_graph[i].succ; arcptr; arcptr = arcptr->succ_next) if (! arcptr->on_tree) { - gcov_type tmp_count = 0; - if (da_file && __read_gcov_type (&tmp_count, da_file, 8)) - abort (); - - arcptr->arc_count = tmp_count; + arcptr->arc_count = profile[profile_pos++]; arcptr->count_valid = 1; bb_graph[i].succ_count--; bb_graph[arcptr->target].pred_count--; } + free (profile); + free (function_name); } static void @@ -755,12 +908,6 @@ read_files () struct stat buf; struct bb_info_list *list_end = 0; struct bb_info_list *b_ptr; - long total; - - /* Read and ignore the first word of the .da file, which is the count of - how many numbers follow. */ - if (da_file && __read_long (&total, da_file, 8)) - abort (); while (! feof (bbg_file)) { @@ -781,17 +928,6 @@ read_files () ungetc (getc (bbg_file), bbg_file); } - /* Check to make sure the .da file data is valid. */ - - if (da_file) - { - if (feof (da_file)) - fnotice (stderr, ".da file contents exhausted too early\n"); - /* Should be at end of file now. */ - if (__read_long (&total, da_file, 8) == 0) - fnotice (stderr, ".da file contents not exhausted\n"); - } - /* Calculate all of the basic block execution counts and branch taken probabilities. */ diff --git a/gcc/libgcc2.c b/gcc/libgcc2.c index a06b7951ab9..6c004a59a3b 100644 --- a/gcc/libgcc2.c +++ b/gcc/libgcc2.c @@ -1238,12 +1238,11 @@ __eprintf (const char *string, const char *expression, #ifdef L_bb -#if LONG_TYPE_SIZE == GCOV_TYPE_SIZE -typedef long gcov_type; -#else -typedef long long gcov_type; -#endif - +struct bb_function_info { + long checksum; + int arc_count; + const char *name; +}; /* Structure emitted by -a */ struct bb @@ -1253,14 +1252,10 @@ struct bb gcov_type *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; - char *flags; + struct bb_function_info *function_infos; }; #ifdef BLOCK_PROFILER_CODE @@ -1283,39 +1278,66 @@ BLOCK_PROFILER_CODE #include #endif +#include + static struct bb *bb_head; +int __global_counters = 0, __gthreads_active = 0; + void __bb_exit_func (void) { FILE *da_file; - int i; struct bb *ptr; + long n_counters_p = 0; + gcov_type max_counter_p = 0; + gcov_type sum_counters_p = 0; if (bb_head == 0) return; - i = strlen (bb_head->filename) - 3; + /* Calculate overall "statistics". */ + for (ptr = bb_head; ptr != (struct bb *) 0; ptr = ptr->next) + { + int i; + + n_counters_p += ptr->ncounts; + + for (i = 0; i < ptr->ncounts; i++) + { + sum_counters_p += ptr->counts[i]; + + if (ptr->counts[i] > max_counter_p) + max_counter_p = ptr->counts[i]; + } + } for (ptr = bb_head; ptr != (struct bb *) 0; ptr = ptr->next) { - int firstchar; + gcov_type max_counter_o = 0; + gcov_type sum_counters_o = 0; + int i; + + /* Calculate the per-object statistics. */ - /* Make sure the output file exists - - but don't clobber exiting data. */ - if ((da_file = fopen (ptr->filename, "a")) != 0) - fclose (da_file); + for (i = 0; i < ptr->ncounts; i++) + { + sum_counters_o += ptr->counts[i]; - /* Need to re-open in order to be able to write from the start. */ - da_file = fopen (ptr->filename, "r+b"); + if (ptr->counts[i] > max_counter_o) + max_counter_o = ptr->counts[i]; + } + + /* open the file for appending, creating it if necessary. */ + da_file = fopen (ptr->filename, "ab"); /* Some old systems might not allow the 'b' mode modifier. Therefore, try to open without it. This can lead to a race condition so that when you delete and re-create the file, the file might be opened in text mode, but then, you shouldn't delete the file in the first place. */ if (da_file == 0) - da_file = fopen (ptr->filename, "r+"); + da_file = fopen (ptr->filename, "a"); if (da_file == 0) { fprintf (stderr, "arc profiling: Can't open output file %s.\n", @@ -1341,92 +1363,96 @@ __bb_exit_func (void) } #endif - /* If the file is not empty, and the number of counts in it is the - same, then merge them in. */ - firstchar = fgetc (da_file); - if (firstchar == EOF) + if (__write_long (-123, da_file, 4) != 0) /* magic */ { - if (ferror (da_file)) - { - fprintf (stderr, "arc profiling: Can't read output file "); - perror (ptr->filename); - } + fprintf (stderr, "arc profiling: Error writing output file %s.\n", + ptr->filename); } else { - long n_counts = 0; - - if (ungetc (firstchar, da_file) == EOF) - rewind (da_file); - if (__read_long (&n_counts, da_file, 8) != 0) - { - fprintf (stderr, "arc profiling: Can't read output file %s.\n", - ptr->filename); - continue; - } - if (n_counts == ptr->ncounts) + struct bb_function_info *fn_info; + gcov_type *count_ptr = ptr->counts; + int i; + int count_functions = 0; + + for (fn_info = ptr->function_infos; fn_info->arc_count != -1; + fn_info++) + count_functions++; + + /* number of functions in this block. */ + __write_long (count_functions, da_file, 4); + + /* length of extra data in bytes. */ + __write_long ((4 + 8 + 8) + (4 + 8 + 8), da_file, 4); + + /* overall statistics. */ + /* number of counters. */ + __write_long (n_counters_p, da_file, 4); + /* sum of counters. */ + __write_gcov_type (sum_counters_p, da_file, 8); + /* maximal counter. */ + __write_gcov_type (max_counter_p, da_file, 8); + + /* per-object statistics. */ + /* number of counters. */ + __write_long (ptr->ncounts, da_file, 4); + /* sum of counters. */ + __write_gcov_type (sum_counters_o, da_file, 8); + /* maximal counter. */ + __write_gcov_type (max_counter_o, da_file, 8); + + /* write execution counts for each function. */ + + for (fn_info = ptr->function_infos; fn_info->arc_count != -1; + fn_info++) { - int i; - - for (i = 0; i < n_counts; i++) + /* new function. */ + if (__write_gcov_string + (fn_info->name, strlen (fn_info->name), da_file, -1) != 0) { - gcov_type v = 0; - - if (__read_gcov_type (&v, da_file, 8) != 0) - { - fprintf (stderr, - "arc profiling: Can't read output file %s.\n", - ptr->filename); - break; - } - ptr->counts[i] += v; + fprintf (stderr, + "arc profiling: Error writing output file %s.\n", + ptr->filename); + break; } - } - } - - rewind (da_file); - - /* ??? Should first write a header to the file. Preferably, a 4 byte - magic number, 4 bytes containing the time the program was - compiled, 4 bytes containing the last modification time of the - source file, and 4 bytes indicating the compiler options used. + if (__write_long (fn_info->checksum, da_file, 4) != 0) + { + fprintf (stderr, + "arc profiling: Error writing output file %s.\n", + ptr->filename); + break; + } - That way we can easily verify that the proper source/executable/ - data file combination is being used from gcov. */ + if (__write_long (fn_info->arc_count, da_file, 4) != 0) + { + fprintf (stderr, + "arc profiling: Error writing output file %s.\n", + ptr->filename); + break; + } - if (__write_gcov_type (ptr->ncounts, da_file, 8) != 0) - { + for (i = fn_info->arc_count; i > 0; i--, count_ptr++) + { + if (__write_gcov_type (*count_ptr, da_file, 8) != 0) + break; + } - fprintf (stderr, "arc profiling: Error writing output file %s.\n", - ptr->filename); - } - else - { - int j; - gcov_type *count_ptr = ptr->counts; - int ret = 0; - for (j = ptr->ncounts; j > 0; j--) - { - if (__write_gcov_type (*count_ptr, da_file, 8) != 0) + if (i) /* there was an error */ { - ret = 1; + fprintf (stderr, + "arc profiling: Error writing output file %s.\n", + ptr->filename); break; } - count_ptr++; } - if (ret) - fprintf (stderr, "arc profiling: Error writing output file %s.\n", - ptr->filename); } - if (fclose (da_file) == EOF) + if (fclose (da_file) != 0) fprintf (stderr, "arc profiling: Error closing output file %s.\n", ptr->filename); } - - return; } void @@ -1437,8 +1463,8 @@ __bb_init_func (struct bb *blocks) if (blocks->zero_word) return; - - /* Initialize destructor. */ + + /* Initialize destructor and per-thread data. */ if (!bb_head) atexit (__bb_exit_func); @@ -1451,7 +1477,7 @@ __bb_init_func (struct bb *blocks) /* Called before fork or exec - write out profile information gathered so far and reset it to zero. This avoids duplication or loss of the profile information gathered so far. */ -void +void __bb_fork_func (void) { struct bb *ptr; diff --git a/gcc/libgcc2.h b/gcc/libgcc2.h index 715ccfc610b..79c79970057 100644 --- a/gcc/libgcc2.h +++ b/gcc/libgcc2.h @@ -31,9 +31,14 @@ struct bb; extern void __bb_exit_func (void); extern void __bb_init_func (struct bb *); extern void __bb_fork_func (void); -extern void __bb_trace_func (void); -extern void __bb_trace_ret (void); -extern void __bb_init_trace_func (struct bb *, unsigned long); + +#if LONG_TYPE_SIZE == GCOV_TYPE_SIZE +typedef long gcov_type; +#else +typedef long long gcov_type; +#endif + +extern gcov_type *__bb_find_arc_counters (void); struct exception_descriptor; extern short int __get_eh_table_language (struct exception_descriptor *); diff --git a/gcc/profile.c b/gcc/profile.c index 881604e1e27..6fa4e12f031 100644 --- a/gcc/profile.c +++ b/gcc/profile.c @@ -49,6 +49,8 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA #include "basic-block.h" #include "gcov-io.h" #include "target.h" +#include "profile.h" +#include "libfuncs.h" #include "langhooks.h" /* Additional information about the edges we need. */ @@ -92,11 +94,6 @@ static FILE *bb_file; static char *last_bb_file_name; -/* Used by final, for allocating the proper amount of storage for the - instrumented arc execution counts. */ - -int count_instrumented_edges; - /* Collect statistics on the performance of this pass for the entire source file. */ @@ -118,6 +115,8 @@ static rtx gen_edge_profiler PARAMS ((int)); static void instrument_edges PARAMS ((struct edge_list *)); static void output_gcov_string PARAMS ((const char *, long)); static void compute_branch_probabilities PARAMS ((void)); +static gcov_type * get_exec_counts PARAMS ((void)); +static long compute_checksum PARAMS ((void)); static basic_block find_group PARAMS ((basic_block)); static void union_groups PARAMS ((basic_block, basic_block)); @@ -163,8 +162,9 @@ instrument_edges (el) } } + profile_info.count_edges_instrumented_now = num_instr_edges; total_num_edges_instrumented += num_instr_edges; - count_instrumented_edges = total_num_edges_instrumented; + profile_info.count_instrumented_edges = total_num_edges_instrumented; total_num_blocks_created += num_edges; if (rtl_dump_file) @@ -205,6 +205,167 @@ output_gcov_string (string, delimiter) } +/* Computes hybrid profile for all matching entries in da_file. + Sets max_counter_in_program as a side effect. */ + +static gcov_type * +get_exec_counts () +{ + int num_edges = 0; + int i; + int okay = 1; + int mismatch = 0; + gcov_type *profile; + char *function_name_buffer; + int function_name_buffer_len; + gcov_type max_counter_in_run; + + profile_info.max_counter_in_program = 0; + profile_info.count_profiles_merged = 0; + + /* No .da file, no execution counts. */ + if (!da_file) + return 0; + + /* Count the edges to be (possibly) instrumented. */ + + for (i = 0; i < n_basic_blocks + 2; i++) + { + basic_block bb = GCOV_INDEX_TO_BB (i); + edge e; + for (e = bb->succ; e; e = e->succ_next) + if (!EDGE_INFO (e)->ignore && !EDGE_INFO (e)->on_tree) + { + num_edges++; + } + } + + /* now read and combine all matching profiles. */ + + profile = xmalloc (sizeof (gcov_type) * num_edges); + rewind (da_file); + function_name_buffer_len = strlen (current_function_name) + 1; + function_name_buffer = xmalloc (function_name_buffer_len + 1); + + for (i = 0; i < num_edges; i++) + profile[i] = 0; + + while (1) + { + long magic, extra_bytes; + long func_count; + int i; + + if (__read_long (&magic, da_file, 4) != 0) + break; + + if (magic != -123) + { + okay = 0; + break; + } + + if (__read_long (&func_count, da_file, 4) != 0) + { + okay = 0; + break; + } + + if (__read_long (&extra_bytes, da_file, 4) != 0) + { + okay = 0; + break; + } + + fseek (da_file, 4 + 8, SEEK_CUR); + + /* read the maximal counter. */ + __read_gcov_type (&max_counter_in_run, da_file, 8); + + /* skip the rest of "statistics" emited by __bb_exit_func. */ + fseek (da_file, extra_bytes - (4 + 8 + 8), SEEK_CUR); + + for (i = 0; i < func_count; i++) + { + long arc_count; + long chksum; + int j; + + if (__read_gcov_string + (function_name_buffer, function_name_buffer_len, da_file, + -1) != 0) + { + okay = 0; + break; + } + + if (__read_long (&chksum, da_file, 4) != 0) + { + okay = 0; + break; + } + + if (__read_long (&arc_count, da_file, 4) != 0) + { + okay = 0; + break; + } + + if (strcmp (function_name_buffer, current_function_name) != 0) + { + /* skip */ + if (fseek (da_file, arc_count * 8, SEEK_CUR) < 0) + { + okay = 0; + break; + } + } + else if (arc_count != num_edges + || chksum != profile_info.current_function_cfg_checksum) + okay = 0, mismatch = 1; + else + { + gcov_type tmp; + + profile_info.max_counter_in_program += max_counter_in_run; + profile_info.count_profiles_merged++; + + for (j = 0; j < arc_count; j++) + if (__read_gcov_type (&tmp, da_file, 8) != 0) + { + okay = 0; + break; + } + else + { + profile[j] += tmp; + } + } + } + + if (!okay) + break; + + } + + free (function_name_buffer); + + if (!okay) + { + if (mismatch) + error + ("Profile does not match flowgraph of function %s (out of date?)", + current_function_name); + else + error (".da file corrupted"); + free (profile); + return 0; + } + + return profile; +} + + /* Compute the branch probabilities for the various branches. Annotate them accordingly. */ @@ -218,6 +379,8 @@ compute_branch_probabilities () int hist_br_prob[20]; int num_never_executed; int num_branches; + gcov_type *exec_counts = get_exec_counts (); + int exec_counts_pos = 0; /* Attach extra info block to each bb. */ @@ -253,14 +416,13 @@ compute_branch_probabilities () if (!EDGE_INFO (e)->ignore && !EDGE_INFO (e)->on_tree) { num_edges++; - if (da_file) + if (exec_counts) { - gcov_type value; - __read_gcov_type (&value, da_file, 8); - e->count = value; + e->count = exec_counts[exec_counts_pos++]; } else e->count = 0; + EDGE_INFO (e)->count_valid = 1; BB_INFO (bb)->succ_count--; BB_INFO (e->dest)->pred_count--; @@ -517,6 +679,36 @@ compute_branch_probabilities () } free_aux_for_blocks (); + if (exec_counts) + free (exec_counts); +} + +/* Compute checksum for the current function. */ + +#define CHSUM_HASH 500000003 +#define CHSUM_SHIFT 2 + +static long +compute_checksum () +{ + long chsum = 0; + int i; + + + for (i = 0; i < n_basic_blocks ; i++) + { + basic_block bb = BASIC_BLOCK (i); + edge e; + + for (e = bb->succ; e; e = e->succ_next) + { + chsum = ((chsum << CHSUM_SHIFT) + (BB_TO_GCOV_INDEX (e->dest) + 1)) % CHSUM_HASH; + } + + chsum = (chsum << CHSUM_SHIFT) % CHSUM_HASH; + } + + return chsum; } /* Instrument and/or analyze program behavior based on program flow graph. @@ -542,6 +734,12 @@ branch_prob () int num_edges, ignored_edges; struct edge_list *el; + profile_info.current_function_cfg_checksum = compute_checksum (); + + if (rtl_dump_file) + fprintf (rtl_dump_file, "CFG checksum is %ld\n", + profile_info.current_function_cfg_checksum); + /* Start of a function. */ if (flag_test_coverage) output_gcov_string (current_function_name, (long) -2); @@ -758,6 +956,12 @@ branch_prob () { int flag_bits; + __write_gcov_string (current_function_name, + strlen (current_function_name), bbg_file, -1); + + /* write checksum. */ + __write_long (profile_info.current_function_cfg_checksum, bbg_file, 4); + /* The plus 2 stands for entry and exit block. */ __write_long (n_basic_blocks + 2, bbg_file, 4); __write_long (num_edges - ignored_edges + 1, bbg_file, 4); @@ -884,14 +1088,21 @@ find_spanning_tree (el) /* Add fake edge exit to entry we can't instrument. */ union_groups (EXIT_BLOCK_PTR, ENTRY_BLOCK_PTR); - /* First add all abnormal edges to the tree unless they form an cycle. */ + /* First add all abnormal edges to the tree unless they form an cycle. Also + add all edges to EXIT_BLOCK_PTR to avoid inserting profiling code behind + setting return value from function. */ for (i = 0; i < num_edges; i++) { edge e = INDEX_EDGE (el, i); - if ((e->flags & (EDGE_ABNORMAL | EDGE_ABNORMAL_CALL | EDGE_FAKE)) + if (((e->flags & (EDGE_ABNORMAL | EDGE_ABNORMAL_CALL | EDGE_FAKE)) + || e->dest == EXIT_BLOCK_PTR + ) && !EDGE_INFO (e)->ignore && (find_group (e->src) != find_group (e->dest))) { + if (rtl_dump_file) + fprintf (rtl_dump_file, "Abnormal edge %d to %d put to tree\n", + e->src->index, e->dest->index); EDGE_INFO (e)->on_tree = 1; union_groups (e->src, e->dest); } @@ -905,6 +1116,9 @@ find_spanning_tree (el) && !EDGE_INFO (e)->ignore && (find_group (e->src) != find_group (e->dest))) { + if (rtl_dump_file) + fprintf (rtl_dump_file, "Critical edge %d to %d put to tree\n", + e->src->index, e->dest->index); EDGE_INFO (e)->on_tree = 1; union_groups (e->src, e->dest); } @@ -917,6 +1131,9 @@ find_spanning_tree (el) if (find_group (e->src) != find_group (e->dest) && !EDGE_INFO (e)->ignore) { + if (rtl_dump_file) + fprintf (rtl_dump_file, "Normal edge %d to %d put to tree\n", + e->src->index, e->dest->index); EDGE_INFO (e)->on_tree = 1; union_groups (e->src, e->dest); } @@ -975,12 +1192,6 @@ init_branch_prob (filename) if ((da_file = fopen (da_file_name, "rb")) == 0) warning ("file %s not found, execution counts assumed to be zero", da_file_name); - - /* The first word in the .da file gives the number of instrumented - edges, which is not needed for our purposes. */ - - if (da_file) - __read_long (&len, da_file, 8); } if (profile_arc_flag) @@ -1011,22 +1222,8 @@ end_branch_prob () fclose (bbg_file); } - if (flag_branch_probabilities) - { - if (da_file) - { - long temp; - /* This seems slightly dangerous, as it presumes the EOF - flag will not be set until an attempt is made to read - past the end of the file. */ - if (feof (da_file)) - error (".da file contents exhausted too early"); - /* Should be at end of file now. */ - if (__read_long (&temp, da_file, 8) == 0) - error (".da file contents not exhausted"); - fclose (da_file); - } - } + if (flag_branch_probabilities && da_file) + fclose (da_file); if (rtl_dump_file) { @@ -1097,6 +1294,8 @@ gen_edge_profiler (edgeno) tmp = expand_simple_binop (mode, PLUS, mem_ref, const1_rtx, mem_ref, 0, OPTAB_WIDEN); + set_mem_alias_set (mem_ref, new_alias_set ()); + if (tmp != mem_ref) emit_move_insn (copy_rtx (mem_ref), tmp); @@ -1118,9 +1317,6 @@ output_func_start_profiler () rtx table_address; enum machine_mode mode = mode_for_size (GCOV_TYPE_SIZE, MODE_INT, 0); int save_flag_inline_functions = flag_inline_functions; - int save_flag_test_coverage = flag_test_coverage; - int save_profile_arc_flag = profile_arc_flag; - int save_flag_branch_probabilities = flag_branch_probabilities; /* It's either already been output, or we don't need it because we're not doing profile-edges. */ @@ -1163,6 +1359,7 @@ output_func_start_profiler () init_function_start (fndecl, input_filename, lineno); (*lang_hooks.decls.pushlevel) (0); expand_function_start (fndecl, 0); + cfun->arc_profile = 0; /* Actually generate the code to call __bb_init_func. */ ASM_GENERATE_INTERNAL_LABEL (buf, "LPBX", 0); @@ -1179,20 +1376,10 @@ output_func_start_profiler () flag_inline_functions. */ flag_inline_functions = 0; - /* Don't instrument the function that turns on instrumentation. Which - is also handy since we'd get silly warnings about not consuming all - of our da_file input. */ - flag_test_coverage = 0; - profile_arc_flag = 0; - flag_branch_probabilities = 0; - rest_of_compilation (fndecl); /* Reset flag_inline_functions to its original value. */ flag_inline_functions = save_flag_inline_functions; - flag_test_coverage = save_flag_test_coverage; - profile_arc_flag = save_profile_arc_flag; - flag_branch_probabilities = save_flag_branch_probabilities; if (! quiet_flag) fflush (asm_out_file); diff --git a/gcc/toplev.c b/gcc/toplev.c index 572d4c094c1..4170afde8e8 100644 --- a/gcc/toplev.c +++ b/gcc/toplev.c @@ -364,6 +364,11 @@ int profile_flag = 0; int profile_arc_flag = 0; +/* Nonzero if we should not attempt to generate thread-safe + code to profile program flow graph arcs. */ + +int flag_unsafe_profile_arcs = 0; + /* Nonzero if generating info for gcov to calculate line test coverage. */ int flag_test_coverage = 0; @@ -1061,6 +1066,8 @@ static const lang_independent_options f_options[] = N_("Support synchronous non-call exceptions") }, {"profile-arcs", &profile_arc_flag, 1, N_("Insert arc based program profiling code") }, + {"unsafe-profile-arcs", &flag_unsafe_profile_arcs, 1, + N_("Avoid thread safety profiling overhead") }, {"test-coverage", &flag_test_coverage, 1, N_("Create data files needed by gcov") }, {"branch-probabilities", &flag_branch_probabilities, 1, @@ -2891,14 +2898,13 @@ rest_of_compilation (decl) close_dump_file (DFI_cfg, print_rtl_with_bb, insns); /* Do branch profiling and static profile estimation passes. */ - if (optimize > 0 || profile_arc_flag || flag_test_coverage - || flag_branch_probabilities) + if (optimize > 0 || cfun->arc_profile || flag_branch_probabilities) { struct loops loops; timevar_push (TV_BRANCH_PROB); open_dump_file (DFI_bp, decl); - if (profile_arc_flag || flag_test_coverage || flag_branch_probabilities) + if (cfun->arc_profile || flag_branch_probabilities) branch_prob (); /* Discover and record the loop depth at the head of each basic