diff options
author | hubicka <hubicka@138bc75d-0d04-0410-961f-82ee72b054a4> | 2005-03-29 11:45:51 +0000 |
---|---|---|
committer | hubicka <hubicka@138bc75d-0d04-0410-961f-82ee72b054a4> | 2005-03-29 11:45:51 +0000 |
commit | d297148788686526bc4f9a4b8b1bbaf0c8c96dc9 (patch) | |
tree | 06715d5d420f0cc621f0ca883cf604ecc6862320 /gcc/tree-profile.c | |
parent | cb09492adbe1e4187a6b9f80afdb8bc9637543b1 (diff) | |
download | gcc-d297148788686526bc4f9a4b8b1bbaf0c8c96dc9.tar.gz |
* 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.
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@97156 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'gcc/tree-profile.c')
-rw-r--r-- | gcc/tree-profile.c | 523 |
1 files changed, 473 insertions, 50 deletions
diff --git a/gcc/tree-profile.c b/gcc/tree-profile.c index 145c7213158..4f6b792c369 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 */ |