aboutsummaryrefslogtreecommitdiff
path: root/gcc/tree-profile.c
diff options
context:
space:
mode:
authorDale Johannesen <dalej@apple.com>2005-03-29 11:45:51 +0000
committerJan Hubicka <hubicka@gcc.gnu.org>2005-03-29 11:45:51 +0000
commit1f1e85278aee66245bf2258c372216635436ced4 (patch)
tree06715d5d420f0cc621f0ca883cf604ecc6862320 /gcc/tree-profile.c
parent82498ed4b96badc4fc2ccd9889657b628320877c (diff)
downloadgcc-1f1e85278aee66245bf2258c372216635436ced4.zip
gcc-1f1e85278aee66245bf2258c372216635436ced4.tar.gz
gcc-1f1e85278aee66245bf2258c372216635436ced4.tar.bz2
Makefile.in (value-prof.o): New dependencies on $(DIAGNOSTIC_H) $(TREE_H) and $(COVERAGE_H).
* Makefile.in (value-prof.o): New dependencies on $(DIAGNOSTIC_H) $(TREE_H) and $(COVERAGE_H). * coverage.c (compute_checksum): Use DECL_NAME not DECL_ASSEMBLER_NAME. * opts.c (common_handle_option): Enable tree-based value transforms. * toplev.c (process_options): Ditto. * value-prof.h (struct histogram_value_t): Redefine. "Adjust" below refers to references to this type. * tree-flow.h: (struct stmt_ann_d): Add histograms field. * rtl-profile.c (rtl_gen_interval_profiler): Adjust. Remove checks for may_be_more, may_be_less. (rtl_gen_pow2_profiler): Adjust. (rtl_gen_one_value_profiler_no_edge_manip): Adjust. (rtl_gen_one_value_profiler): Adjust. (rtl_gen_const_delta_profiler): Adjust. * tree-profile.c (tree_gen_interval_profiler): Implement. (tree_gen_pow2_profiler): Ditto. (tree_gen_one_value_profiler): Ditto. (tree_profiling): New. (pass_tree_profile): Reference it. * value-prof.c: Include tree-flow.h, tree-flow-inline.h, diagnostic.h, tree.h, gcov-io.h. (insn_divmod_values_to_profile): Rename to rtl_divmod_values_to_profile. Adjust. (insn_values_to_profile): Rename to rtl_values_to_profile. Adjust. (insn_prefetch_values_to_profile): Adjust. (rtl_value_profile_transformations): Adjust. (gen_divmod_fixed_value): Rename to rtl_divmod_fixed_value. (gen_mod_pow2): Rename to rtl_mod_pow2. (gen_mod_subtract): Rename to rtl_mod_subtract. (divmod_fixed_value_transform): Rename to rtl_divmod_fixed_value_transform. (mod_pow2_value_transform): Rename to rtl_mod_pow2_value_transform. (mod_subtract_transform): Rename to rtl_mod_subtract_transform. (rtl_find_values_to_profile): Adjust. (tree_value_profile_transformations): Implement. (tree_divmod_values_to_profile): New. (tree_values_to_profile): New. (tree_divmod_fixed_value): New. (tree_mod_pow2): New. (tree_mod_subtract): New. (tree_divmod_fixed_value_transform): New. (tree_mod_pow2_value_transform): New. (tree_mod_subtract_transform): New. (tree_find_values_to_profile): Implement. * profile.c (instrument_values): Free histograms. (compute_value_histograms): Adjust. Implement tree version. From-SVN: r97156
Diffstat (limited to 'gcc/tree-profile.c')
-rw-r--r--gcc/tree-profile.c523
1 files changed, 473 insertions, 50 deletions
diff --git a/gcc/tree-profile.c b/gcc/tree-profile.c
index 145c721..4f6b792c 100644
--- a/gcc/tree-profile.c
+++ b/gcc/tree-profile.c
@@ -24,30 +24,7 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA. */
/* Generate basic block profile instrumentation and auxiliary files.
- Profile generation is optimized, so that not all arcs in the basic
- block graph need instrumenting. First, the BB graph is closed with
- one entry (function start), and one exit (function exit). Any
- ABNORMAL_EDGE cannot be instrumented (because there is no control
- path to place the code). We close the graph by inserting fake
- EDGE_FAKE edges to the EXIT_BLOCK, from the sources of abnormal
- edges that do not go to the exit_block. We ignore such abnormal
- edges. Naturally these fake edges are never directly traversed,
- and so *cannot* be directly instrumented. Some other graph
- massaging is done. To optimize the instrumentation we generate the
- BB minimal span tree, only edges that are not on the span tree
- (plus the entry point) need instrumenting. From that information
- all other edge counts can be deduced. By construction all fake
- edges must be on the spanning tree. We also attempt to place
- EDGE_CRITICAL edges on the spanning tree.
-
- The auxiliary file generated is <dumpbase>.bbg. The format is
- described in full in gcov-io.h. */
-
-/* ??? Register allocation should use basic block execution counts to
- give preference to the most commonly executed blocks. */
-
-/* ??? Should calculate branch probabilities before instrumenting code, since
- then we can use arc counts to help decide which arcs to instrument. */
+ Tree-based version. See profile.c for overview. */
#include "config.h"
#include "system.h"
@@ -102,15 +79,163 @@ tree_gen_edge_profiler (int edgeno, edge e)
tag of the section for counters, BASE is offset of the counter position. */
static void
-tree_gen_interval_profiler (histogram_value value ATTRIBUTE_UNUSED,
- unsigned tag ATTRIBUTE_UNUSED,
- unsigned base ATTRIBUTE_UNUSED)
+tree_gen_interval_profiler (histogram_value value, unsigned tag, unsigned base)
{
- /* FIXME implement this. */
-#ifdef ENABLE_CHECKING
- internal_error ("unimplemented functionality");
-#endif
- gcc_unreachable ();
+ tree op, op1, op2, op1copy, op2copy;
+ tree tmp1, tmp2, tmp3, val, index;
+ tree label_decl2, label_decl3, label_decl4, label_decl5, label_decl6;
+ edge e12, e23, e34, e45, e56;
+ tree label2, label3, label4, label5, label6;
+ tree stmt1, stmt2, stmt3, stmt4;
+ /* Initializations are to prevent bogus uninitialized warnings. */
+ tree bb1end = NULL_TREE, bb2end = NULL_TREE, bb3end = NULL_TREE;
+ tree bb4end = NULL_TREE, bb5end = NULL_TREE;
+ tree ref = tree_coverage_counter_ref (tag, base), ref2;
+ basic_block bb2, bb3, bb4, bb5, bb6;
+ tree stmt = value->hvalue.tree.stmt;
+ block_stmt_iterator bsi = bsi_for_stmt (stmt);
+ basic_block bb = bb_for_stmt (stmt);
+ tree optype;
+
+ op = stmt;
+ if (TREE_CODE (stmt) == RETURN_EXPR
+ && TREE_OPERAND (stmt, 0)
+ && TREE_CODE (TREE_OPERAND (stmt, 0)) == MODIFY_EXPR)
+ op = TREE_OPERAND (stmt, 0);
+ /* op == MODIFY_EXPR */
+ op = TREE_OPERAND (op, 1);
+ /* op == TRUNC_DIV or TRUNC_MOD */
+ op1 = TREE_OPERAND (op, 0);
+ op2 = TREE_OPERAND (op, 1);
+ optype = TREE_TYPE (op);
+
+ /* Blocks:
+ Original = 1
+ For 2nd compare = 2
+ Normal case, neither more nor less = 3
+ More = 4
+ Less = 5
+ End = 6 */
+ label_decl2 = create_artificial_label ();
+ label_decl3 = create_artificial_label ();
+ label_decl4 = create_artificial_label ();
+ label_decl5 = create_artificial_label ();
+ label_decl6 = create_artificial_label ();
+
+ /* Do not evaluate op1 or op2 more than once. Probably
+ volatile loads are the only things that could cause
+ a problem, but this is harmless in any case. */
+ op1copy = create_tmp_var (optype, "PROF");
+ op2copy = create_tmp_var (optype, "PROF");
+ stmt1 = build2 (MODIFY_EXPR, optype, op1copy, op1);
+ stmt2 = build2 (MODIFY_EXPR, optype, op2copy, op2);
+ TREE_OPERAND (op, 0) = op1copy;
+ TREE_OPERAND (op, 1) = op2copy;
+
+ val = create_tmp_var (optype, "PROF");
+ stmt3 = build2 (MODIFY_EXPR, optype, val,
+ build2 (TRUNC_DIV_EXPR, optype, op1copy, op2copy));
+ stmt4 = build2 (MODIFY_EXPR, optype, val,
+ build2 (MINUS_EXPR, optype, val,
+ build_int_cst (optype, value->hdata.intvl.int_start)));
+ bsi_insert_before (&bsi, stmt1, BSI_SAME_STMT);
+ bsi_insert_before (&bsi, stmt2, BSI_SAME_STMT);
+ bsi_insert_before (&bsi, stmt3, BSI_SAME_STMT);
+ bsi_insert_before (&bsi, stmt4, BSI_SAME_STMT);
+
+ index = create_tmp_var (GCOV_TYPE_NODE, "PROF");
+
+ /* Check for too big. */
+ stmt1 = build3 (COND_EXPR, void_type_node,
+ build2 (GE_EXPR, boolean_type_node, val,
+ build_int_cst (optype, value->hdata.intvl.steps)),
+ build1 (GOTO_EXPR, void_type_node, label_decl4),
+ build1 (GOTO_EXPR, void_type_node, label_decl2));
+ bsi_insert_before (&bsi, stmt1, BSI_SAME_STMT);
+ bb1end = stmt1;
+
+ /* Check for too small. */
+ label2 = build1 (LABEL_EXPR, void_type_node, label_decl2);
+ bsi_insert_before (&bsi, label2, BSI_SAME_STMT);
+ stmt1 = build3 (COND_EXPR, void_type_node,
+ build2 (LT_EXPR, boolean_type_node, val, integer_zero_node),
+ build1 (GOTO_EXPR, void_type_node, label_decl5),
+ build1 (GOTO_EXPR, void_type_node, label_decl3));
+ bsi_insert_before (&bsi, stmt1, BSI_SAME_STMT);
+ bb2end = stmt1;
+
+ /* Normal case, within range. */
+ label3 = build1 (LABEL_EXPR, void_type_node, label_decl3);
+ bsi_insert_before (&bsi, label3, BSI_SAME_STMT);
+ stmt1 = build2 (MODIFY_EXPR, GCOV_TYPE_NODE, index,
+ build1 (NOP_EXPR, GCOV_TYPE_NODE, val));
+ bsi_insert_before (&bsi, stmt1, BSI_SAME_STMT);
+ bb3end = stmt1;
+
+ /* Too big */
+ label4 = build1 (LABEL_EXPR, void_type_node, label_decl4);
+ stmt1 = build2 (MODIFY_EXPR, GCOV_TYPE_NODE, index,
+ build_int_cst (GCOV_TYPE_NODE, value->hdata.intvl.steps));
+ bsi_insert_before (&bsi, label4, BSI_SAME_STMT);
+ bsi_insert_before (&bsi, stmt1, BSI_SAME_STMT);
+ bb4end = stmt1;
+
+ /* Too small */
+ label5 = build1 (LABEL_EXPR, void_type_node, label_decl5);
+ stmt1 = build2 (MODIFY_EXPR, GCOV_TYPE_NODE, index,
+ build_int_cst (GCOV_TYPE_NODE, value->hdata.intvl.steps + 1));
+ bsi_insert_before (&bsi, label5, BSI_SAME_STMT);
+ bsi_insert_before (&bsi, stmt1, BSI_SAME_STMT);
+ bb5end = stmt1;
+
+ /* Increment appropriate counter. */
+ label6 = build1 (LABEL_EXPR, void_type_node, label_decl6);
+ bsi_insert_before (&bsi, label6, BSI_SAME_STMT);
+
+ tmp1 = create_tmp_var (GCOV_TYPE_NODE, "PROF");
+ tmp2 = create_tmp_var (GCOV_TYPE_NODE, "PROF");
+ tmp3 = create_tmp_var (GCOV_TYPE_NODE, "PROF");
+ stmt1 = build2 (MODIFY_EXPR, GCOV_TYPE_NODE, tmp1,
+ build2 (PLUS_EXPR, GCOV_TYPE_NODE, index,
+ TREE_OPERAND (ref, 1)));
+ TREE_OPERAND (ref, 1) = tmp1;
+ /* Make a copy to avoid sharing complaints. */
+ ref2 = build4 (ARRAY_REF, TREE_TYPE (ref), TREE_OPERAND (ref, 0),
+ TREE_OPERAND (ref, 1), TREE_OPERAND (ref, 2),
+ TREE_OPERAND (ref, 3));
+
+ stmt2 = build2 (MODIFY_EXPR, GCOV_TYPE_NODE, tmp2, ref);
+ stmt3 = build2 (MODIFY_EXPR, GCOV_TYPE_NODE, tmp3,
+ build2 (PLUS_EXPR, GCOV_TYPE_NODE, tmp2, integer_one_node));
+ stmt4 = build2 (MODIFY_EXPR, GCOV_TYPE_NODE, ref2, tmp3);
+ bsi_insert_before (&bsi, stmt1, BSI_SAME_STMT);
+ bsi_insert_before (&bsi, stmt2, BSI_SAME_STMT);
+ bsi_insert_before (&bsi, stmt3, BSI_SAME_STMT);
+ bsi_insert_before (&bsi, stmt4, BSI_SAME_STMT);
+
+ /* Now fix up the CFG. */
+ /* 1->2,4; 2->3,5; 3->6; 4->6; 5->6 */
+ e12 = split_block (bb, bb1end);
+ bb2 = e12->dest;
+ e23 = split_block (bb2, bb2end);
+ bb3 = e23->dest;
+ e34 = split_block (bb3, bb3end);
+ bb4 = e34->dest;
+ e45 = split_block (bb4, bb4end);
+ bb5 = e45->dest;
+ e56 = split_block (bb5, bb5end);
+ bb6 = e56->dest;
+
+ e12->flags &= ~EDGE_FALLTHRU;
+ e12->flags |= EDGE_FALSE_VALUE;
+ make_edge (bb, bb4, EDGE_TRUE_VALUE);
+ e23->flags &= ~EDGE_FALLTHRU;
+ e23->flags |= EDGE_FALSE_VALUE;
+ make_edge (bb2, bb5, EDGE_TRUE_VALUE);
+ remove_edge (e34);
+ make_edge (bb3, bb6, EDGE_FALLTHRU);
+ remove_edge (e45);
+ make_edge (bb4, bb6, EDGE_FALLTHRU);
}
/* Output instructions as GIMPLE trees to increment the power of two histogram
@@ -118,15 +243,162 @@ tree_gen_interval_profiler (histogram_value value ATTRIBUTE_UNUSED,
of the section for counters, BASE is offset of the counter position. */
static void
-tree_gen_pow2_profiler (histogram_value value ATTRIBUTE_UNUSED,
- unsigned tag ATTRIBUTE_UNUSED,
- unsigned base ATTRIBUTE_UNUSED)
+tree_gen_pow2_profiler (histogram_value value, unsigned tag, unsigned base)
{
- /* FIXME implement this. */
-#ifdef ENABLE_CHECKING
- internal_error ("unimplemented functionality");
-#endif
- gcc_unreachable ();
+ tree op;
+ tree tmp1, tmp2, tmp3;
+ tree index, denom;
+ tree label_decl1 = create_artificial_label ();
+ tree label_decl2 = create_artificial_label ();
+ tree label_decl3 = create_artificial_label ();
+ tree label1, label2, label3;
+ tree stmt1, stmt2, stmt3, stmt4;
+ tree bb1end, bb2end, bb3end;
+ tree ref = tree_coverage_counter_ref (tag, base), ref2;
+ basic_block bb2, bb3, bb4;
+ tree stmt = value->hvalue.tree.stmt;
+ block_stmt_iterator bsi = bsi_for_stmt (stmt);
+ basic_block bb = bb_for_stmt (stmt);
+ tree optype, optypesigned, optypeunsigned;
+
+ op = stmt;
+ if (TREE_CODE (stmt) == RETURN_EXPR
+ && TREE_OPERAND (stmt, 0)
+ && TREE_CODE (TREE_OPERAND (stmt, 0)) == MODIFY_EXPR)
+ op = TREE_OPERAND (stmt, 0);
+ /* op == MODIFY_EXPR */
+ op = TREE_OPERAND (op, 1);
+ /* op == TRUNC_DIV or TRUNC_MOD */
+ op = TREE_OPERAND (op, 1);
+ /* op == denominator */
+ optype = TREE_TYPE (op);
+ if (TYPE_UNSIGNED (optype))
+ {
+ /* Right shift must be unsigned. */
+ optypeunsigned = optype;
+ optypesigned = build_distinct_type_copy (optype);
+ TYPE_UNSIGNED (optypesigned) = false;
+ }
+ else
+ {
+ /* Compare to zero must be signed. */
+ optypesigned = optype;
+ optypeunsigned = build_distinct_type_copy (optype);
+ TYPE_UNSIGNED (optypeunsigned) = true;
+ }
+
+ /* Set up variables and check if denominator is negative when considered
+ as signed. */
+ index = create_tmp_var (GCOV_TYPE_NODE, "PROF");
+ denom = create_tmp_var (optype, "PROF");
+ stmt1 = build2 (MODIFY_EXPR, GCOV_TYPE_NODE, index, integer_zero_node);
+ stmt2 = build2 (MODIFY_EXPR, optype, denom, op);
+ if (optypesigned == optype)
+ {
+ tmp1 = denom;
+ stmt3 = NULL_TREE;
+ }
+ else
+ {
+ tmp1 = create_tmp_var (optypesigned, "PROF");
+ stmt3 = build2 (MODIFY_EXPR, optypesigned, tmp1,
+ build1 (NOP_EXPR, optypesigned, denom));
+ }
+ stmt4 = build3 (COND_EXPR, void_type_node,
+ build2 (LE_EXPR, boolean_type_node, tmp1, integer_zero_node),
+ build1 (GOTO_EXPR, void_type_node, label_decl3),
+ build1 (GOTO_EXPR, void_type_node, label_decl1));
+ bsi_insert_before (&bsi, stmt1, BSI_SAME_STMT);
+ bsi_insert_before (&bsi, stmt2, BSI_SAME_STMT);
+ if (stmt3)
+ bsi_insert_before (&bsi, stmt3, BSI_SAME_STMT);
+ bsi_insert_before (&bsi, stmt4, BSI_SAME_STMT);
+ bb1end = stmt4;
+
+ /* Nonnegative. Check if denominator is power of 2. */
+ label1 = build1 (LABEL_EXPR, void_type_node, label_decl1);
+ tmp1 = create_tmp_var (optype, "PROF");
+ tmp2 = create_tmp_var (optype, "PROF");
+ stmt1 = build2 (MODIFY_EXPR, optype, tmp1,
+ build2 (PLUS_EXPR, optype, denom, integer_minus_one_node));
+ stmt2 = build2 (MODIFY_EXPR, optype, tmp2,
+ build2 (BIT_AND_EXPR, optype, tmp1, denom));
+ stmt3 = build3 (COND_EXPR, void_type_node,
+ build2 (NE_EXPR, boolean_type_node, tmp2, integer_zero_node),
+ build1 (GOTO_EXPR, void_type_node, label_decl3),
+ build1 (GOTO_EXPR, void_type_node, label_decl2));
+ bsi_insert_before (&bsi, label1, BSI_SAME_STMT);
+ bsi_insert_before (&bsi, stmt1, BSI_SAME_STMT);
+ bsi_insert_before (&bsi, stmt2, BSI_SAME_STMT);
+ bsi_insert_before (&bsi, stmt3, BSI_SAME_STMT);
+ bb2end = stmt3;
+
+ /* Loop. Increment index, shift denominator, repeat if denominator nonzero. */
+ label2 = build1 (LABEL_EXPR, void_type_node, label_decl2);
+ stmt1 = build2 (MODIFY_EXPR, GCOV_TYPE_NODE, index,
+ build2 (PLUS_EXPR, GCOV_TYPE_NODE, index, integer_one_node));
+ if (optypeunsigned == optype)
+ {
+ tmp1 = denom;
+ stmt2 = NULL_TREE;
+ }
+ else
+ {
+ tmp1 = create_tmp_var (optypeunsigned, "PROF");
+ stmt2 = build2 (MODIFY_EXPR, optypeunsigned, tmp1,
+ build1 (NOP_EXPR, optypeunsigned, denom));
+ }
+ stmt3 = build2 (MODIFY_EXPR, optype, denom,
+ build2 (RSHIFT_EXPR, optype, tmp1, integer_one_node));
+ stmt4 = build3 (COND_EXPR, void_type_node,
+ build2 (NE_EXPR, boolean_type_node, denom, integer_zero_node),
+ build1 (GOTO_EXPR, void_type_node, label_decl2),
+ build1 (GOTO_EXPR, void_type_node, label_decl3));
+ bsi_insert_before (&bsi, label2, BSI_SAME_STMT);
+ bsi_insert_before (&bsi, stmt1, BSI_SAME_STMT);
+ if (stmt2)
+ bsi_insert_before (&bsi, stmt2, BSI_SAME_STMT);
+ bsi_insert_before (&bsi, stmt3, BSI_SAME_STMT);
+ bsi_insert_before (&bsi, stmt4, BSI_SAME_STMT);
+ bb3end = stmt4;
+
+ /* Increment the appropriate counter. */
+ label3 = build1 (LABEL_EXPR, void_type_node, label_decl3);
+ tmp1 = create_tmp_var (GCOV_TYPE_NODE, "PROF");
+ tmp2 = create_tmp_var (GCOV_TYPE_NODE, "PROF");
+ tmp3 = create_tmp_var (GCOV_TYPE_NODE, "PROF");
+ stmt1 = build2 (MODIFY_EXPR, GCOV_TYPE_NODE, tmp1,
+ build2 (PLUS_EXPR, GCOV_TYPE_NODE, index, TREE_OPERAND (ref, 1)));
+ TREE_OPERAND (ref, 1) = tmp1;
+ /* Make a copy to avoid sharing complaints. */
+ ref2 = build4 (ARRAY_REF, TREE_TYPE (ref), TREE_OPERAND (ref, 0),
+ TREE_OPERAND (ref, 1), TREE_OPERAND (ref, 2), TREE_OPERAND (ref, 3));
+ stmt2 = build2 (MODIFY_EXPR, GCOV_TYPE_NODE, tmp2, ref);
+ stmt3 = build2 (MODIFY_EXPR, GCOV_TYPE_NODE, tmp3,
+ build2 (PLUS_EXPR, GCOV_TYPE_NODE, tmp2, integer_one_node));
+ stmt4 = build2 (MODIFY_EXPR, GCOV_TYPE_NODE, ref2, tmp3);
+ bsi_insert_before (&bsi, label3, BSI_SAME_STMT);
+ bsi_insert_before (&bsi, stmt1, BSI_SAME_STMT);
+ bsi_insert_before (&bsi, stmt2, BSI_SAME_STMT);
+ bsi_insert_before (&bsi, stmt3, BSI_SAME_STMT);
+ bsi_insert_before (&bsi, stmt4, BSI_SAME_STMT);
+
+ /* Now fix up the CFG. */
+ bb2 = (split_block (bb, bb1end))->dest;
+ bb3 = (split_block (bb2, bb2end))->dest;
+ bb4 = (split_block (bb3, bb3end))->dest;
+
+ EDGE_SUCC (bb, 0)->flags &= ~EDGE_FALLTHRU;
+ EDGE_SUCC (bb, 0)->flags |= EDGE_FALSE_VALUE;
+ make_edge (bb, bb4, EDGE_TRUE_VALUE);
+
+ EDGE_SUCC (bb2, 0)->flags &= ~EDGE_FALLTHRU;
+ EDGE_SUCC (bb2, 0)->flags |= EDGE_FALSE_VALUE;
+ make_edge (bb2, bb4, EDGE_TRUE_VALUE);
+
+ EDGE_SUCC (bb3, 0)->flags &= ~EDGE_FALLTHRU;
+ EDGE_SUCC (bb3, 0)->flags |= EDGE_FALSE_VALUE;
+ make_edge (bb3, bb3, EDGE_TRUE_VALUE);
}
/* Output instructions as GIMPLE trees for code to find the most common value.
@@ -134,15 +406,150 @@ tree_gen_pow2_profiler (histogram_value value ATTRIBUTE_UNUSED,
section for counters, BASE is offset of the counter position. */
static void
-tree_gen_one_value_profiler (histogram_value value ATTRIBUTE_UNUSED,
- unsigned tag ATTRIBUTE_UNUSED,
- unsigned base ATTRIBUTE_UNUSED)
+tree_gen_one_value_profiler (histogram_value value, unsigned tag, unsigned base)
{
- /* FIXME implement this. */
-#ifdef ENABLE_CHECKING
- internal_error ("unimplemented functionality");
-#endif
- gcc_unreachable ();
+ tree op;
+ tree tmp1, tmp2, tmp3;
+ tree label_decl1 = create_artificial_label ();
+ tree label_decl2 = create_artificial_label ();
+ tree label_decl3 = create_artificial_label ();
+ tree label_decl4 = create_artificial_label ();
+ tree label_decl5 = create_artificial_label ();
+ tree label1, label2, label3, label4, label5;
+ tree stmt1, stmt2, stmt3, stmt4;
+ tree bb1end, bb2end, bb3end, bb4end, bb5end;
+ tree ref1 = tree_coverage_counter_ref (tag, base);
+ tree ref2 = tree_coverage_counter_ref (tag, base + 1);
+ tree ref3 = tree_coverage_counter_ref (tag, base + 2);
+ basic_block bb2, bb3, bb4, bb5, bb6;
+ tree stmt = value->hvalue.tree.stmt;
+ block_stmt_iterator bsi = bsi_for_stmt (stmt);
+ basic_block bb = bb_for_stmt (stmt);
+ tree optype;
+
+ op = stmt;
+ if (TREE_CODE (stmt) == RETURN_EXPR
+ && TREE_OPERAND (stmt, 0)
+ && TREE_CODE (TREE_OPERAND (stmt, 0)) == MODIFY_EXPR)
+ op = TREE_OPERAND (stmt, 0);
+ /* op == MODIFY_EXPR */
+ op = TREE_OPERAND (op, 1);
+ /* op == TRUNC_DIV or TRUNC_MOD */
+ op = TREE_OPERAND (op, 1);
+ /* op == denominator */
+ optype = TREE_TYPE (op);
+
+ /* Check if the stored value matches. */
+ tmp1 = create_tmp_var (GCOV_TYPE_NODE, "PROF");
+ tmp2 = create_tmp_var (optype, "PROF");
+ tmp3 = create_tmp_var (optype, "PROF");
+ stmt1 = build2 (MODIFY_EXPR, GCOV_TYPE_NODE, tmp1, ref1);
+ stmt2 = build2 (MODIFY_EXPR, optype, tmp2,
+ build1 (NOP_EXPR, optype, tmp1));
+ stmt3 = build2 (MODIFY_EXPR, optype, tmp3, op);
+ stmt4 = build3 (COND_EXPR, void_type_node,
+ build2 (EQ_EXPR, boolean_type_node, tmp2, tmp3),
+ build1 (GOTO_EXPR, void_type_node, label_decl4),
+ build1 (GOTO_EXPR, void_type_node, label_decl1));
+ bsi_insert_before (&bsi, stmt1, BSI_SAME_STMT);
+ bsi_insert_before (&bsi, stmt2, BSI_SAME_STMT);
+ bsi_insert_before (&bsi, stmt3, BSI_SAME_STMT);
+ bsi_insert_before (&bsi, stmt4, BSI_SAME_STMT);
+ bb1end = stmt4;
+
+ /* Does not match; check whether the counter is zero. */
+ label1 = build1 (LABEL_EXPR, void_type_node, label_decl1);
+ tmp1 = create_tmp_var (GCOV_TYPE_NODE, "PROF");
+ stmt1 = build2 (MODIFY_EXPR, GCOV_TYPE_NODE, tmp1, ref2);
+ stmt2 = build3 (COND_EXPR, void_type_node,
+ build2 (EQ_EXPR, boolean_type_node, tmp1, integer_zero_node),
+ build1 (GOTO_EXPR, void_type_node, label_decl3),
+ build1 (GOTO_EXPR, void_type_node, label_decl2));
+ bsi_insert_before (&bsi, label1, BSI_SAME_STMT);
+ bsi_insert_before (&bsi, stmt1, BSI_SAME_STMT);
+ bsi_insert_before (&bsi, stmt2, BSI_SAME_STMT);
+ bb2end = stmt2;
+
+ /* Counter is not zero yet, decrement. */
+ label2 = build1 (LABEL_EXPR, void_type_node, label_decl2);
+ tmp1 = create_tmp_var (GCOV_TYPE_NODE, "PROF");
+ tmp2 = create_tmp_var (GCOV_TYPE_NODE, "PROF");
+ stmt1 = build2 (MODIFY_EXPR, GCOV_TYPE_NODE, tmp1, ref2);
+ stmt2 = build2 (MODIFY_EXPR, GCOV_TYPE_NODE, tmp2,
+ build (MINUS_EXPR, GCOV_TYPE_NODE,
+ tmp1, integer_one_node));
+ stmt3 = build2 (MODIFY_EXPR, GCOV_TYPE_NODE, ref2, tmp2);
+ bsi_insert_before (&bsi, label2, BSI_SAME_STMT);
+ bsi_insert_before (&bsi, stmt1, BSI_SAME_STMT);
+ bsi_insert_before (&bsi, stmt2, BSI_SAME_STMT);
+ bsi_insert_before (&bsi, stmt3, BSI_SAME_STMT);
+ bb3end = stmt3;
+
+ /* Counter was zero, store new value. */
+ label3 = build1 (LABEL_EXPR, void_type_node, label_decl3);
+ tmp1 = create_tmp_var (optype, "PROF");
+ tmp2 = create_tmp_var (GCOV_TYPE_NODE, "PROF");
+ stmt1 = build2 (MODIFY_EXPR, optype, tmp1, op);
+ stmt2 = build2 (MODIFY_EXPR, GCOV_TYPE_NODE, tmp2,
+ build1 (NOP_EXPR, GCOV_TYPE_NODE, tmp1));
+ stmt3 = build2 (MODIFY_EXPR, GCOV_TYPE_NODE, ref1, tmp2);
+ bsi_insert_before (&bsi, label3, BSI_SAME_STMT);
+ bsi_insert_before (&bsi, stmt1, BSI_SAME_STMT);
+ bsi_insert_before (&bsi, stmt2, BSI_SAME_STMT);
+ bsi_insert_before (&bsi, stmt3, BSI_SAME_STMT);
+ bb4end = stmt3;
+ /* (fall through) */
+
+ /* Increment counter. */
+ label4 = build1 (LABEL_EXPR, void_type_node, label_decl4);
+ tmp1 = create_tmp_var (GCOV_TYPE_NODE, "PROF");
+ tmp2 = create_tmp_var (GCOV_TYPE_NODE, "PROF");
+ stmt1 = build2 (MODIFY_EXPR, GCOV_TYPE_NODE, tmp1, ref2);
+ stmt2 = build2 (MODIFY_EXPR, GCOV_TYPE_NODE, tmp2,
+ build (PLUS_EXPR, GCOV_TYPE_NODE,
+ tmp1, integer_one_node));
+ stmt3 = build2 (MODIFY_EXPR, GCOV_TYPE_NODE, ref2, tmp2);
+ bsi_insert_before (&bsi, label4, BSI_SAME_STMT);
+ bsi_insert_before (&bsi, stmt1, BSI_SAME_STMT);
+ bsi_insert_before (&bsi, stmt2, BSI_SAME_STMT);
+ bsi_insert_before (&bsi, stmt3, BSI_SAME_STMT);
+ bb5end = stmt3;
+
+ /* Increment the counter of all executions; this seems redundant given
+ that we have counts for edges in cfg, but it may happen that some
+ optimization will change the counts for the block (either because
+ it is unable to update them correctly, or because it will duplicate
+ the block or its part). */
+ label5 = build1 (LABEL_EXPR, void_type_node, label_decl5);
+ tmp1 = create_tmp_var (GCOV_TYPE_NODE, "PROF");
+ tmp2 = create_tmp_var (GCOV_TYPE_NODE, "PROF");
+ stmt1 = build2 (MODIFY_EXPR, GCOV_TYPE_NODE, tmp1, ref3);
+ stmt2 = build2 (MODIFY_EXPR, GCOV_TYPE_NODE, tmp2,
+ build (PLUS_EXPR, GCOV_TYPE_NODE,
+ tmp1, integer_one_node));
+ stmt3 = build2 (MODIFY_EXPR, GCOV_TYPE_NODE, ref3, tmp2);
+ bsi_insert_before (&bsi, label5, BSI_SAME_STMT);
+ bsi_insert_before (&bsi, stmt1, BSI_SAME_STMT);
+ bsi_insert_before (&bsi, stmt2, BSI_SAME_STMT);
+ bsi_insert_before (&bsi, stmt3, BSI_SAME_STMT);
+
+ /* Now fix up the CFG. */
+ bb2 = (split_block (bb, bb1end))->dest;
+ bb3 = (split_block (bb2, bb2end))->dest;
+ bb4 = (split_block (bb3, bb3end))->dest;
+ bb5 = (split_block (bb4, bb4end))->dest;
+ bb6 = (split_block (bb5, bb5end))->dest;
+
+ EDGE_SUCC (bb, 0)->flags &= ~EDGE_FALLTHRU;
+ EDGE_SUCC (bb, 0)->flags |= EDGE_FALSE_VALUE;
+ make_edge (bb, bb5, EDGE_TRUE_VALUE);
+
+ EDGE_SUCC (bb2, 0)->flags &= ~EDGE_FALLTHRU;
+ EDGE_SUCC (bb2, 0)->flags |= EDGE_FALSE_VALUE;
+ make_edge (bb2, bb4, EDGE_TRUE_VALUE);
+
+ remove_edge (EDGE_SUCC (bb3, 0));
+ make_edge (bb3, bb6, EDGE_FALLTHRU);
}
/* Output instructions as GIMPLE trees for code to find the most common value
@@ -166,7 +573,8 @@ tree_gen_const_delta_profiler (histogram_value value ATTRIBUTE_UNUSED,
If it is, set up hooks for tree-based profiling.
Gate for pass_tree_profile. */
-static bool do_tree_profiling (void)
+static bool
+do_tree_profiling (void)
{
if (flag_tree_based_profiling
&& (profile_arc_flag || flag_test_coverage || flag_branch_probabilities))
@@ -184,11 +592,26 @@ static FILE *tree_profile_dump_file (void) {
return dump_file;
}
+static void
+tree_profiling (void)
+{
+ branch_prob ();
+ if (flag_branch_probabilities
+ && flag_profile_values
+ && flag_value_profile_transformations)
+ value_profile_transformations ();
+ /* The above could hose dominator info. Currently there is
+ none coming in, this is a safety valve. It should be
+ easy to adjust it, if and when there is some. */
+ free_dominance_info (CDI_DOMINATORS);
+ free_dominance_info (CDI_POST_DOMINATORS);
+}
+
struct tree_opt_pass pass_tree_profile =
{
"tree_profile", /* name */
do_tree_profiling, /* gate */
- branch_prob, /* execute */
+ tree_profiling, /* execute */
NULL, /* sub */
NULL, /* next */
0, /* static_pass_number */