summaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
authorhubicka <hubicka@138bc75d-0d04-0410-961f-82ee72b054a4>2002-05-09 12:54:19 +0000
committerhubicka <hubicka@138bc75d-0d04-0410-961f-82ee72b054a4>2002-05-09 12:54:19 +0000
commit90c2be4439c656f63074408d6c91442511a9e061 (patch)
treeda04474c0297ae3c91b3bdce0c519ca2f9d2e5a8 /gcc
parent40734805198ef874623eb7be9ec3de9251c799b7 (diff)
downloadgcc-90c2be4439c656f63074408d6c91442511a9e061.tar.gz
* 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
Diffstat (limited to 'gcc')
-rw-r--r--gcc/ChangeLog39
-rw-r--r--gcc/Makefile.in4
-rw-r--r--gcc/doc/gcov.texi40
-rw-r--r--gcc/final.c383
-rw-r--r--gcc/function.c2
-rw-r--r--gcc/function.h3
-rw-r--r--gcc/gcov-io.h115
-rw-r--r--gcc/gcov.c182
-rw-r--r--gcc/libgcc2.c204
-rw-r--r--gcc/libgcc2.h11
-rw-r--r--gcc/profile.c281
-rw-r--r--gcc/toplev.c12
12 files changed, 985 insertions, 291 deletions
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 <jh@suse.cz>
+ Pavel Nejedly <bim@atrey.karlin.mff.cuni.cz>
+
+ * 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 <kazu@cs.umass.edu>
* 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 <stdio.h>
#include <sys/types.h>
-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 <errno.h>
#endif
+#include <gthr.h>
+
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