+ /* Create spanning tree from basic block graph, mark each edge that is
+ on the spanning tree. We insert as many abnormal and critical edges
+ as possible to minimize number of edge splits necessary. */
+
+ find_spanning_tree (el);
+
+ /* Fake edges that are not on the tree will not be instrumented, so
+ mark them ignored. */
+ for (num_instrumented = i = 0; i < num_edges; i++)
+ {
+ edge e = INDEX_EDGE (el, i);
+ struct edge_info *inf = EDGE_INFO (e);
+
+ if (inf->ignore || inf->on_tree)
+ /*NOP*/;
+ else if (e->flags & EDGE_FAKE)
+ {
+ inf->ignore = 1;
+ ignored_edges++;
+ }
+ else
+ num_instrumented++;
+ }
+
+ total_num_blocks += n_basic_blocks + 2;
+ if (dump_file)
+ fprintf (dump_file, "%d basic blocks\n", n_basic_blocks);
+
+ total_num_edges += num_edges;
+ if (dump_file)
+ fprintf (dump_file, "%d edges\n", num_edges);
+
+ total_num_edges_ignored += ignored_edges;
+ if (dump_file)
+ fprintf (dump_file, "%d ignored edges\n", ignored_edges);
+
+ /* Write the data from which gcov can reconstruct the basic block
+ graph. */
+
+ /* Basic block flags */
+ if (coverage_begin_output ())
+ {
+ gcov_position_t offset;
+
+ offset = gcov_write_tag (GCOV_TAG_BLOCKS);
+ for (i = 0; i != (unsigned) (n_basic_blocks + 2); i++)
+ gcov_write_unsigned (0);
+ gcov_write_length (offset);
+ }
+
+ /* Keep all basic block indexes nonnegative in the gcov output.
+ Index 0 is used for entry block, last index is for exit block.
+ */
+ ENTRY_BLOCK_PTR->index = -1;
+ EXIT_BLOCK_PTR->index = last_basic_block;
+#define BB_TO_GCOV_INDEX(bb) ((bb)->index + 1)
+
+ /* Arcs */
+ if (coverage_begin_output ())
+ {
+ gcov_position_t offset;
+
+ FOR_BB_BETWEEN (bb, ENTRY_BLOCK_PTR, EXIT_BLOCK_PTR, next_bb)
+ {
+ edge e;
+
+ offset = gcov_write_tag (GCOV_TAG_ARCS);
+ gcov_write_unsigned (BB_TO_GCOV_INDEX (bb));
+
+ for (e = bb->succ; e; e = e->succ_next)
+ {
+ struct edge_info *i = EDGE_INFO (e);
+ if (!i->ignore)
+ {
+ unsigned flag_bits = 0;
+
+ if (i->on_tree)
+ flag_bits |= GCOV_ARC_ON_TREE;
+ if (e->flags & EDGE_FAKE)
+ flag_bits |= GCOV_ARC_FAKE;
+ if (e->flags & EDGE_FALLTHRU)
+ flag_bits |= GCOV_ARC_FALLTHROUGH;
+
+ gcov_write_unsigned (BB_TO_GCOV_INDEX (e->dest));
+ gcov_write_unsigned (flag_bits);
+ }
+ }
+
+ gcov_write_length (offset);
+ }
+ }
+
+ /* Line numbers. */
+ /* FIXME: make this work for trees. (Line numbers are in location_t
+ objects, but aren't always attached to the obvious tree...) */
+ if (coverage_begin_output () && !ir_type ())
+ {
+ char const *prev_file_name = NULL;
+ gcov_position_t offset;
+
+ FOR_EACH_BB (bb)
+ {
+ rtx insn = BB_HEAD (bb);
+ int ignore_next_note = 0;
+
+ offset = 0;
+
+ /* We are looking for line number notes. Search backward
+ before basic block to find correct ones. */
+ insn = prev_nonnote_insn (insn);
+ if (!insn)
+ insn = get_insns ();
+ else
+ insn = NEXT_INSN (insn);
+
+ while (insn != BB_END (bb))
+ {
+ if (GET_CODE (insn) == NOTE)
+ {
+ /* Must ignore the line number notes that
+ immediately follow the end of an inline function
+ to avoid counting it twice. There is a note
+ before the call, and one after the call. */
+ if (NOTE_LINE_NUMBER (insn)
+ == NOTE_INSN_REPEATED_LINE_NUMBER)
+ ignore_next_note = 1;
+ else if (NOTE_LINE_NUMBER (insn) <= 0)
+ /*NOP*/;
+ else if (ignore_next_note)
+ ignore_next_note = 0;
+ else
+ {
+ if (!offset)
+ {
+ offset = gcov_write_tag (GCOV_TAG_LINES);
+ gcov_write_unsigned (BB_TO_GCOV_INDEX (bb));
+ }
+
+ /* If this is a new source file, then output the
+ file's name to the .bb file. */
+ if (!prev_file_name
+ || strcmp (NOTE_SOURCE_FILE (insn),
+ prev_file_name))
+ {
+ prev_file_name = NOTE_SOURCE_FILE (insn);
+ gcov_write_unsigned (0);
+ gcov_write_string (prev_file_name);
+ }
+ gcov_write_unsigned (NOTE_LINE_NUMBER (insn));
+ }
+ }
+ insn = NEXT_INSN (insn);
+ }
+
+ if (offset)
+ {
+ /* A file of NULL indicates the end of run. */
+ gcov_write_unsigned (0);
+ gcov_write_string (NULL);
+ gcov_write_length (offset);
+ }
+ }
+ }
+
+ ENTRY_BLOCK_PTR->index = ENTRY_BLOCK;
+ EXIT_BLOCK_PTR->index = EXIT_BLOCK;
+#undef BB_TO_GCOV_INDEX
+
+ if (flag_profile_values)
+ find_values_to_profile (&n_values, &values);
+
+ if (flag_branch_probabilities)
+ {
+ compute_branch_probabilities ();
+ if (flag_profile_values)
+ compute_value_histograms (n_values, values);
+ }
+
+ /* For each edge not on the spanning tree, add counting code. */
+ if (profile_arc_flag
+ && coverage_counter_alloc (GCOV_COUNTER_ARCS, num_instrumented))
+ {
+ unsigned n_instrumented = instrument_edges (el);
+
+ if (n_instrumented != num_instrumented)
+ abort ();
+
+ if (flag_profile_values)
+ instrument_values (n_values, values);
+
+ /* Commit changes done by instrumentation. */
+ if (ir_type ())
+ bsi_commit_edge_inserts ((int *)NULL);
+ else
+ {
+ commit_edge_insertions_watch_calls ();
+ allocate_reg_info (max_reg_num (), FALSE, FALSE);
+ }
+ }
+
+ remove_fake_edges ();
+ free_aux_for_edges ();
+
+ if (!ir_type ())
+ {
+ /* Re-merge split basic blocks and the mess introduced by
+ insert_insn_on_edge. */
+ cleanup_cfg (profile_arc_flag ? CLEANUP_EXPENSIVE : 0);
+ if (profile_dump_file())
+ dump_flow_info (profile_dump_file());
+ }
+
+ free_edge_list (el);