/* Hooks for cfg representation specific functions.
- Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc.
+ Copyright (C) 2003-2019 Free Software Foundation, Inc.
Contributed by Sebastian Pop <s.pop@laposte.net>
This file is part of GCC.
GCC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2, or (at your option)
+the Free Software Foundation; either version 3, or (at your option)
any later version.
GCC is distributed in the hope that it will be useful,
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
-along with GCC; see the file COPYING. If not, write to
-the Free Software Foundation, 51 Franklin Street, Fifth Floor,
-Boston, MA 02110-1301, USA. */
+along with GCC; see the file COPYING3. If not see
+<http://www.gnu.org/licenses/>. */
#include "config.h"
#include "system.h"
#include "coretypes.h"
-#include "tm.h"
-#include "tree.h"
+#include "backend.h"
#include "rtl.h"
-#include "basic-block.h"
-#include "tree-flow.h"
+#include "cfghooks.h"
#include "timevar.h"
-#include "toplev.h"
+#include "pretty-print.h"
+#include "diagnostic-core.h"
+#include "dumpfile.h"
+#include "cfganal.h"
+#include "tree-ssa.h"
#include "cfgloop.h"
/* A pointer to one of the hooks containers. */
/* Initialization of functions specific to the tree IR. */
void
-tree_register_cfg_hooks (void)
+gimple_register_cfg_hooks (void)
{
- cfg_hooks = &tree_cfg_hooks;
+ cfg_hooks = &gimple_cfg_hooks;
+}
+
+struct cfg_hooks
+get_cfg_hooks (void)
+{
+ return *cfg_hooks;
+}
+
+void
+set_cfg_hooks (struct cfg_hooks new_cfg_hooks)
+{
+ *cfg_hooks = new_cfg_hooks;
}
/* Returns current ir type. */
enum ir_type
current_ir_type (void)
{
- if (cfg_hooks == &tree_cfg_hooks)
+ if (cfg_hooks == &gimple_cfg_hooks)
return IR_GIMPLE;
else if (cfg_hooks == &rtl_cfg_hooks)
return IR_RTL_CFGRTL;
Currently it does following: checks edge and basic block list correctness
and calls into IL dependent checking then. */
-void
+DEBUG_FUNCTION void
verify_flow_info (void)
{
size_t *edge_checksum;
basic_block *last_visited;
timevar_push (TV_CFG_VERIFY);
- last_visited = XCNEWVEC (basic_block, last_basic_block);
- edge_checksum = XCNEWVEC (size_t, last_basic_block);
+ last_visited = XCNEWVEC (basic_block, last_basic_block_for_fn (cfun));
+ edge_checksum = XCNEWVEC (size_t, last_basic_block_for_fn (cfun));
/* Check bb chain & numbers. */
- last_bb_seen = ENTRY_BLOCK_PTR;
- FOR_BB_BETWEEN (bb, ENTRY_BLOCK_PTR->next_bb, NULL, next_bb)
+ last_bb_seen = ENTRY_BLOCK_PTR_FOR_FN (cfun);
+ FOR_BB_BETWEEN (bb, ENTRY_BLOCK_PTR_FOR_FN (cfun)->next_bb, NULL, next_bb)
{
- if (bb != EXIT_BLOCK_PTR
- && bb != BASIC_BLOCK (bb->index))
+ if (bb != EXIT_BLOCK_PTR_FOR_FN (cfun)
+ && bb != BASIC_BLOCK_FOR_FN (cfun, bb->index))
{
error ("bb %d on wrong place", bb->index);
err = 1;
}
/* Now check the basic blocks (boundaries etc.) */
- FOR_EACH_BB_REVERSE (bb)
+ FOR_EACH_BB_REVERSE_FN (bb, cfun)
{
int n_fallthru = 0;
edge e;
err = 1;
}
- if (bb->count < 0)
+ if (!bb->count.verify ())
{
- error ("verify_flow_info: Wrong count of block %i %i",
- bb->index, (int)bb->count);
+ error ("verify_flow_info: Wrong count of block %i", bb->index);
err = 1;
}
- if (bb->frequency < 0)
+ /* FIXME: Graphite and SLJL and target code still tends to produce
+ edges with no probablity. */
+ if (profile_status_for_fn (cfun) >= PROFILE_GUESSED
+ && !bb->count.initialized_p () && !flag_graphite && 0)
{
- error ("verify_flow_info: Wrong frequency of block %i %i",
- bb->index, bb->frequency);
+ error ("verify_flow_info: Missing count of block %i", bb->index);
err = 1;
}
+
FOR_EACH_EDGE (e, ei, bb->succs)
{
if (last_visited [e->dest->index] == bb)
e->src->index, e->dest->index);
err = 1;
}
- if (e->probability < 0 || e->probability > REG_BR_PROB_BASE)
+ /* FIXME: Graphite and SLJL and target code still tends to produce
+ edges with no probablity. */
+ if (profile_status_for_fn (cfun) >= PROFILE_GUESSED
+ && !e->probability.initialized_p () && !flag_graphite && 0)
{
- error ("verify_flow_info: Wrong probability of edge %i->%i %i",
- e->src->index, e->dest->index, e->probability);
+ error ("Uninitialized probability of edge %i->%i", e->src->index,
+ e->dest->index);
err = 1;
}
- if (e->count < 0)
+ if (!e->probability.verify ())
{
- error ("verify_flow_info: Wrong count of edge %i->%i %i",
- e->src->index, e->dest->index, (int)e->count);
+ error ("verify_flow_info: Wrong probability of edge %i->%i",
+ e->src->index, e->dest->index);
err = 1;
}
error ("verify_flow_info: Basic block %d succ edge is corrupted",
bb->index);
fprintf (stderr, "Predecessor: ");
- dump_edge_info (stderr, e, 0);
+ dump_edge_info (stderr, e, TDF_DETAILS, 0);
fprintf (stderr, "\nSuccessor: ");
- dump_edge_info (stderr, e, 1);
+ dump_edge_info (stderr, e, TDF_DETAILS, 1);
fprintf (stderr, "\n");
err = 1;
}
{
error ("basic block %d pred edge is corrupted", bb->index);
fputs ("Predecessor: ", stderr);
- dump_edge_info (stderr, e, 0);
+ dump_edge_info (stderr, e, TDF_DETAILS, 0);
fputs ("\nSuccessor: ", stderr);
- dump_edge_info (stderr, e, 1);
+ dump_edge_info (stderr, e, TDF_DETAILS, 1);
fputc ('\n', stderr);
err = 1;
}
error ("its dest_idx should be %d, not %d",
ei.index, e->dest_idx);
fputs ("Predecessor: ", stderr);
- dump_edge_info (stderr, e, 0);
+ dump_edge_info (stderr, e, TDF_DETAILS, 0);
fputs ("\nSuccessor: ", stderr);
- dump_edge_info (stderr, e, 1);
+ dump_edge_info (stderr, e, TDF_DETAILS, 1);
fputc ('\n', stderr);
err = 1;
}
edge e;
edge_iterator ei;
- FOR_EACH_EDGE (e, ei, ENTRY_BLOCK_PTR->succs)
+ FOR_EACH_EDGE (e, ei, ENTRY_BLOCK_PTR_FOR_FN (cfun)->succs)
edge_checksum[e->dest->index] += (size_t) e;
- FOR_EACH_EDGE (e, ei, EXIT_BLOCK_PTR->preds)
+ FOR_EACH_EDGE (e, ei, EXIT_BLOCK_PTR_FOR_FN (cfun)->preds)
edge_checksum[e->dest->index] -= (size_t) e;
}
- FOR_BB_BETWEEN (bb, ENTRY_BLOCK_PTR, NULL, next_bb)
+ FOR_BB_BETWEEN (bb, ENTRY_BLOCK_PTR_FOR_FN (cfun), NULL, next_bb)
if (edge_checksum[bb->index])
{
error ("basic block %i edge lists are corrupted", bb->index);
err = 1;
}
- last_bb_seen = ENTRY_BLOCK_PTR;
+ last_bb_seen = ENTRY_BLOCK_PTR_FOR_FN (cfun);
/* Clean up. */
free (last_visited);
timevar_pop (TV_CFG_VERIFY);
}
-/* Print out one basic block. This function takes care of the purely
- graph related information. The cfg hook for the active representation
- should dump representation-specific information. */
+/* Print out one basic block BB to file OUTF. INDENT is printed at the
+ start of each new line. FLAGS are the TDF_* flags in dumpfile.h.
+
+ This function takes care of the purely graph related information.
+ The cfg hook for the active representation should dump
+ representation-specific information. */
void
-dump_bb (basic_block bb, FILE *outf, int indent)
+dump_bb (FILE *outf, basic_block bb, int indent, dump_flags_t flags)
{
- edge e;
- edge_iterator ei;
- char *s_indent;
-
- s_indent = (char *) alloca ((size_t) indent + 1);
- memset (s_indent, ' ', (size_t) indent);
- s_indent[indent] = '\0';
+ if (flags & TDF_BLOCKS)
+ dump_bb_info (outf, bb, indent, flags, true, false);
+ if (cfg_hooks->dump_bb)
+ cfg_hooks->dump_bb (outf, bb, indent, flags);
+ if (flags & TDF_BLOCKS)
+ dump_bb_info (outf, bb, indent, flags, false, true);
+ fputc ('\n', outf);
+}
- fprintf (outf, ";;%s basic block %d, loop depth %d, count ",
- s_indent, bb->index, bb->loop_depth);
- fprintf (outf, HOST_WIDEST_INT_PRINT_DEC, (HOST_WIDEST_INT) bb->count);
- putc ('\n', outf);
+DEBUG_FUNCTION void
+debug (basic_block_def &ref)
+{
+ dump_bb (stderr, &ref, 0, TDF_NONE);
+}
- fprintf (outf, ";;%s prev block ", s_indent);
- if (bb->prev_bb)
- fprintf (outf, "%d, ", bb->prev_bb->index);
- else
- fprintf (outf, "(nil), ");
- fprintf (outf, "next block ");
- if (bb->next_bb)
- fprintf (outf, "%d", bb->next_bb->index);
+DEBUG_FUNCTION void
+debug (basic_block_def *ptr)
+{
+ if (ptr)
+ debug (*ptr);
else
- fprintf (outf, "(nil)");
- putc ('\n', outf);
+ fprintf (stderr, "<nil>\n");
+}
+
+static void
+debug_slim (basic_block ptr)
+{
+ fprintf (stderr, "<basic_block %p (%d)>", (void *) ptr, ptr->index);
+}
- fprintf (outf, ";;%s pred: ", s_indent);
- FOR_EACH_EDGE (e, ei, bb->preds)
- dump_edge_info (outf, e, 0);
- putc ('\n', outf);
+DEFINE_DEBUG_VEC (basic_block_def *)
+DEFINE_DEBUG_HASH_SET (basic_block_def *)
- fprintf (outf, ";;%s succ: ", s_indent);
- FOR_EACH_EDGE (e, ei, bb->succs)
- dump_edge_info (outf, e, 1);
- putc ('\n', outf);
+/* Dumps basic block BB to pretty-printer PP, for use as a label of
+ a DOT graph record-node. The implementation of this hook is
+ expected to write the label to the stream that is attached to PP.
+ Field separators between instructions are pipe characters printed
+ verbatim. Instructions should be written with some characters
+ escaped, using pp_write_text_as_dot_label_to_stream(). */
- if (cfg_hooks->dump_bb)
- cfg_hooks->dump_bb (bb, outf, indent);
+void
+dump_bb_for_graph (pretty_printer *pp, basic_block bb)
+{
+ if (!cfg_hooks->dump_bb_for_graph)
+ internal_error ("%s does not support dump_bb_for_graph",
+ cfg_hooks->name);
+ /* TODO: Add pretty printer for counter. */
+ if (bb->count.initialized_p ())
+ pp_printf (pp, "COUNT:" "%" PRId64, bb->count.to_gcov_type ());
+ pp_write_text_to_stream (pp);
+ if (!(dump_flags & TDF_SLIM))
+ cfg_hooks->dump_bb_for_graph (pp, bb);
+}
+
+/* Dump the complete CFG to FILE. FLAGS are the TDF_* flags in dumpfile.h. */
+void
+dump_flow_info (FILE *file, dump_flags_t flags)
+{
+ basic_block bb;
+
+ fprintf (file, "\n%d basic blocks, %d edges.\n", n_basic_blocks_for_fn (cfun),
+ n_edges_for_fn (cfun));
+ FOR_ALL_BB_FN (bb, cfun)
+ dump_bb (file, bb, 0, flags);
+
+ putc ('\n', file);
+}
+
+/* Like above, but dump to stderr. To be called from debuggers. */
+void debug_flow_info (void);
+DEBUG_FUNCTION void
+debug_flow_info (void)
+{
+ dump_flow_info (stderr, TDF_DETAILS);
}
/* Redirect edge E to the given basic block DEST and update underlying program
to the destination of the other edge going from its source. */
bool
-can_remove_branch_p (edge e)
+can_remove_branch_p (const_edge e)
{
if (!cfg_hooks->can_remove_branch_p)
internal_error ("%s does not support can_remove_branch_p",
remove_edge (edge e)
{
if (current_loops != NULL)
- rescan_loop_exit (e, false, true);
+ {
+ rescan_loop_exit (e, false, true);
+
+ /* Removal of an edge inside an irreducible region or which leads
+ to an irreducible region can turn the region into a natural loop.
+ In that case, ask for the loop structure fixups.
+
+ FIXME: Note that LOOPS_HAVE_MARKED_IRREDUCIBLE_REGIONS is not always
+ set, so always ask for fixups when removing an edge in that case. */
+ if (!loops_state_satisfies_p (LOOPS_HAVE_MARKED_IRREDUCIBLE_REGIONS)
+ || (e->flags & EDGE_IRREDUCIBLE_LOOP)
+ || (e->dest->flags & BB_IRREDUCIBLE_LOOP))
+ loops_state_set (LOOPS_NEED_FIXUP);
+ }
+
+ /* This is probably not needed, but it doesn't hurt. */
+ /* FIXME: This should be called via a remove_edge hook. */
+ if (current_ir_type () == IR_GIMPLE)
+ redirect_edge_var_map_clear (e);
remove_edge_raw (e);
}
+/* Like redirect_edge_succ but avoid possible duplicate edge. */
+
+edge
+redirect_edge_succ_nodup (edge e, basic_block new_succ)
+{
+ edge s;
+
+ s = find_edge (e->src, new_succ);
+ if (s && s != e)
+ {
+ s->flags |= e->flags;
+ s->probability += e->probability;
+ /* FIXME: This should be called via a hook and only for IR_GIMPLE. */
+ redirect_edge_var_map_dup (s, e);
+ remove_edge (e);
+ e = s;
+ }
+ else
+ redirect_edge_succ (e, new_succ);
+
+ return e;
+}
+
/* Redirect the edge E to basic block DEST even if it requires creating
of a new basic block; then it returns the newly created basic block.
Aborts when redirection is impossible. */
redirect_edge_and_branch_force (edge e, basic_block dest)
{
basic_block ret, src = e->src;
- struct loop *loop;
if (!cfg_hooks->redirect_edge_and_branch_force)
internal_error ("%s does not support redirect_edge_and_branch_force",
rescan_loop_exit (e, false, true);
ret = cfg_hooks->redirect_edge_and_branch_force (e, dest);
- if (ret != NULL
- && dom_info_available_p (CDI_DOMINATORS))
+
+ if (ret != NULL && dom_info_available_p (CDI_DOMINATORS))
set_immediate_dominator (CDI_DOMINATORS, ret, src);
if (current_loops != NULL)
{
if (ret != NULL)
{
- loop = find_common_loop (single_pred (ret)->loop_father,
- single_succ (ret)->loop_father);
+ struct loop *loop
+ = find_common_loop (single_pred (ret)->loop_father,
+ single_succ (ret)->loop_father);
add_bb_to_loop (ret, loop);
}
else if (find_edge (src, dest) == e)
the labels). If I is NULL, splits just after labels. The newly created edge
is returned. The new basic block is created just after the old one. */
-edge
-split_block (basic_block bb, void *i)
+static edge
+split_block_1 (basic_block bb, void *i)
{
basic_block new_bb;
+ edge res;
if (!cfg_hooks->split_block)
internal_error ("%s does not support split_block", cfg_hooks->name);
return NULL;
new_bb->count = bb->count;
- new_bb->frequency = bb->frequency;
- new_bb->loop_depth = bb->loop_depth;
+ new_bb->discriminator = bb->discriminator;
if (dom_info_available_p (CDI_DOMINATORS))
{
}
if (current_loops != NULL)
- add_bb_to_loop (new_bb, bb->loop_father);
+ {
+ edge_iterator ei;
+ edge e;
+ add_bb_to_loop (new_bb, bb->loop_father);
+ /* Identify all loops bb may have been the latch of and adjust them. */
+ FOR_EACH_EDGE (e, ei, new_bb->succs)
+ if (e->dest->loop_father->latch == bb)
+ e->dest->loop_father->latch = new_bb;
+ }
+
+ res = make_single_succ_edge (bb, new_bb, EDGE_FALLTHRU);
+
+ if (bb->flags & BB_IRREDUCIBLE_LOOP)
+ {
+ new_bb->flags |= BB_IRREDUCIBLE_LOOP;
+ res->flags |= EDGE_IRREDUCIBLE_LOOP;
+ }
+
+ return res;
+}
- return make_single_succ_edge (bb, new_bb, EDGE_FALLTHRU);
+edge
+split_block (basic_block bb, gimple *i)
+{
+ return split_block_1 (bb, i);
+}
+
+edge
+split_block (basic_block bb, rtx i)
+{
+ return split_block_1 (bb, i);
}
/* Splits block BB just after labels. The newly created edge is returned. */
edge
split_block_after_labels (basic_block bb)
{
- return split_block (bb, NULL);
+ return split_block_1 (bb, NULL);
}
/* Moves block BB immediately after block AFTER. Returns false if the
struct loop *loop = bb->loop_father;
/* If we remove the header or the latch of a loop, mark the loop for
- removal by setting its header and latch to NULL. */
+ removal. */
if (loop->latch == bb
|| loop->header == bb)
- {
- loop->header = NULL;
- loop->latch = NULL;
- }
+ mark_loop_for_removal (loop);
remove_bb_from_loops (bb);
}
split_edge (edge e)
{
basic_block ret;
- gcov_type count = e->count;
- int freq = EDGE_FREQUENCY (e);
+ profile_count count = e->count ();
edge f;
bool irr = (e->flags & EDGE_IRREDUCIBLE_LOOP) != 0;
struct loop *loop;
ret = cfg_hooks->split_edge (e);
ret->count = count;
- ret->frequency = freq;
- single_succ_edge (ret)->probability = REG_BR_PROB_BASE;
- single_succ_edge (ret)->count = count;
+ single_succ_edge (ret)->probability = profile_probability::always ();
if (irr)
{
loop = find_common_loop (src->loop_father, dest->loop_father);
add_bb_to_loop (ret, loop);
- if (loop->latch == src)
+ /* If we split the latch edge of loop adjust the latch block. */
+ if (loop->latch == src
+ && loop->header == dest)
loop->latch = ret;
}
HEAD and END are the first and the last statement belonging
to the block. If both are NULL, an empty block is created. */
-basic_block
-create_basic_block (void *head, void *end, basic_block after)
+static basic_block
+create_basic_block_1 (void *head, void *end, basic_block after)
{
basic_block ret;
return ret;
}
+basic_block
+create_basic_block (gimple_seq seq, basic_block after)
+{
+ return create_basic_block_1 (seq, NULL, after);
+}
+
+basic_block
+create_basic_block (rtx head, rtx end, basic_block after)
+{
+ return create_basic_block_1 (head, end, after);
+}
+
+
/* Creates an empty basic block just after basic block AFTER. */
basic_block
create_empty_bb (basic_block after)
{
- return create_basic_block (NULL, NULL, after);
+ return create_basic_block_1 (NULL, NULL, after);
}
/* Checks whether we may merge blocks BB1 and BB2. */
}
bool
-predicted_by_p (basic_block bb, enum br_predictor predictor)
+predicted_by_p (const_basic_block bb, enum br_predictor predictor)
{
if (!cfg_hooks->predict_edge)
internal_error ("%s does not support predicted_by_p", cfg_hooks->name);
cfg_hooks->merge_blocks (a, b);
if (current_loops != NULL)
- remove_bb_from_loops (b);
+ {
+ /* If the block we merge into is a loop header do nothing unless ... */
+ if (a->loop_father->header == a)
+ {
+ /* ... we merge two loop headers, in which case we kill
+ the inner loop. */
+ if (b->loop_father->header == b)
+ mark_loop_for_removal (b->loop_father);
+ }
+ /* If we merge a loop header into its predecessor, update the loop
+ structure. */
+ else if (b->loop_father->header == b)
+ {
+ remove_bb_from_loops (a);
+ add_bb_to_loop (a, b->loop_father);
+ a->loop_father->header = a;
+ }
+ /* If we merge a loop latch into its predecessor, update the loop
+ structure. */
+ if (b->loop_father->latch
+ && b->loop_father->latch == b)
+ b->loop_father->latch = a;
+ remove_bb_from_loops (b);
+ }
/* Normally there should only be one successor of A and that is B, but
partway though the merge of blocks for conditional_execution we'll
{
e->src = a;
if (current_loops != NULL)
- rescan_loop_exit (e, true, false);
+ {
+ /* If b was a latch, a now is. */
+ if (e->dest->loop_father->latch == b)
+ e->dest->loop_father->latch = a;
+ rescan_loop_exit (e, true, false);
+ }
}
a->succs = b->succs;
a->flags |= b->flags;
fallthru = split_block_after_labels (bb);
dummy = fallthru->src;
+ dummy->count = profile_count::zero ();
bb = fallthru->dest;
/* Redirect back edges we want to keep. */
for (ei = ei_start (dummy->preds); (e = ei_safe_edge (ei)); )
{
+ basic_block e_src;
+
if (redirect_edge_p (e))
{
+ dummy->count += e->count ();
ei_next (&ei);
continue;
}
- dummy->frequency -= EDGE_FREQUENCY (e);
- dummy->count -= e->count;
- if (dummy->frequency < 0)
- dummy->frequency = 0;
- if (dummy->count < 0)
- dummy->count = 0;
- fallthru->count -= e->count;
- if (fallthru->count < 0)
- fallthru->count = 0;
-
+ e_src = e->src;
jump = redirect_edge_and_branch_force (e, bb);
- if (jump != NULL
- && new_bb_cbk != NULL)
- new_bb_cbk (jump);
+ if (jump != NULL)
+ {
+ /* If we redirected the loop latch edge, the JUMP block now acts like
+ the new latch of the loop. */
+ if (current_loops != NULL
+ && dummy->loop_father != NULL
+ && dummy->loop_father->header == dummy
+ && dummy->loop_father->latch == e_src)
+ dummy->loop_father->latch = jump;
+
+ if (new_bb_cbk != NULL)
+ new_bb_cbk (jump);
+ }
}
if (dom_info_available_p (CDI_DOMINATORS))
{
- VEC (basic_block, heap) *doms_to_fix = VEC_alloc (basic_block, heap, 2);
- VEC_quick_push (basic_block, doms_to_fix, dummy);
- VEC_quick_push (basic_block, doms_to_fix, bb);
+ vec<basic_block> doms_to_fix;
+ doms_to_fix.create (2);
+ doms_to_fix.quick_push (dummy);
+ doms_to_fix.quick_push (bb);
iterate_fix_dominators (CDI_DOMINATORS, doms_to_fix, false);
- VEC_free (basic_block, heap, doms_to_fix);
+ doms_to_fix.release ();
}
if (current_loops != NULL)
return fallthru;
}
+/* Try to make the edge fallthru. */
+
void
tidy_fallthru_edge (edge e)
{
/* Fix up edges that now fall through, or rather should now fall through
but previously required a jump around now deleted blocks. Simplify
the search by only examining blocks numerically adjacent, since this
- is how find_basic_blocks created them. */
+ is how they were created.
+
+ ??? This routine is currently RTL specific. */
void
tidy_fallthru_edges (void)
if (!cfg_hooks->tidy_fallthru_edge)
return;
- if (ENTRY_BLOCK_PTR->next_bb == EXIT_BLOCK_PTR)
+ if (ENTRY_BLOCK_PTR_FOR_FN (cfun)->next_bb == EXIT_BLOCK_PTR_FOR_FN (cfun))
return;
- FOR_BB_BETWEEN (b, ENTRY_BLOCK_PTR->next_bb, EXIT_BLOCK_PTR->prev_bb, next_bb)
+ FOR_BB_BETWEEN (b, ENTRY_BLOCK_PTR_FOR_FN (cfun)->next_bb,
+ EXIT_BLOCK_PTR_FOR_FN (cfun)->prev_bb, next_bb)
{
edge s;
a single successor.
If we had a conditional branch to the next instruction when
- find_basic_blocks was called, then there will only be one
- out edge for the block which ended with the conditional
- branch (since we do not create duplicate edges).
+ CFG was built, then there will only be one out edge for the
+ block which ended with the conditional branch (since we do
+ not create duplicate edges).
Furthermore, the edge will be marked as a fallthru because we
merge the flags for the duplicate edges. So we do not want to
s = single_succ_edge (b);
if (! (s->flags & EDGE_COMPLEX)
&& s->dest == c
- && !find_reg_note (BB_END (b), REG_CROSSING_JUMP, NULL_RTX))
+ && !(JUMP_P (BB_END (b)) && CROSSING_JUMP_P (BB_END (b))))
tidy_fallthru_edge (s);
}
}
}
+/* Edge E is assumed to be fallthru edge. Emit needed jump instruction
+ (and possibly create new basic block) to make edge non-fallthru.
+ Return newly created BB or NULL if none. */
+
+basic_block
+force_nonfallthru (edge e)
+{
+ basic_block ret, src = e->src;
+
+ if (!cfg_hooks->force_nonfallthru)
+ internal_error ("%s does not support force_nonfallthru",
+ cfg_hooks->name);
+
+ ret = cfg_hooks->force_nonfallthru (e);
+ if (ret != NULL)
+ {
+ if (dom_info_available_p (CDI_DOMINATORS))
+ set_immediate_dominator (CDI_DOMINATORS, ret, src);
+
+ if (current_loops != NULL)
+ {
+ basic_block pred = single_pred (ret);
+ basic_block succ = single_succ (ret);
+ struct loop *loop
+ = find_common_loop (pred->loop_father, succ->loop_father);
+ rescan_loop_exit (e, false, true);
+ add_bb_to_loop (ret, loop);
+
+ /* If we split the latch edge of loop adjust the latch block. */
+ if (loop->latch == pred
+ && loop->header == succ)
+ loop->latch = ret;
+ }
+ }
+
+ return ret;
+}
+
/* Returns true if we can duplicate basic block BB. */
bool
-can_duplicate_block_p (basic_block bb)
+can_duplicate_block_p (const_basic_block bb)
{
if (!cfg_hooks->can_duplicate_block_p)
internal_error ("%s does not support can_duplicate_block_p",
cfg_hooks->name);
- if (bb == EXIT_BLOCK_PTR || bb == ENTRY_BLOCK_PTR)
+ if (bb == EXIT_BLOCK_PTR_FOR_FN (cfun) || bb == ENTRY_BLOCK_PTR_FOR_FN (cfun))
return false;
return cfg_hooks->can_duplicate_block_p (bb);
AFTER. */
basic_block
-duplicate_block (basic_block bb, edge e, basic_block after)
+duplicate_block (basic_block bb, edge e, basic_block after, copy_bb_data *id)
{
edge s, n;
basic_block new_bb;
- gcov_type new_count = e ? e->count : 0;
+ profile_count new_count = e ? e->count (): profile_count::uninitialized ();
edge_iterator ei;
if (!cfg_hooks->duplicate_block)
if (bb->count < new_count)
new_count = bb->count;
-#ifdef ENABLE_CHECKING
- gcc_assert (can_duplicate_block_p (bb));
-#endif
+ gcc_checking_assert (can_duplicate_block_p (bb));
- new_bb = cfg_hooks->duplicate_block (bb);
+ new_bb = cfg_hooks->duplicate_block (bb, id);
if (after)
move_block_after (new_bb, after);
- new_bb->loop_depth = bb->loop_depth;
- new_bb->flags = bb->flags;
+ new_bb->flags = (bb->flags & ~BB_DUPLICATED);
FOR_EACH_EDGE (s, ei, bb->succs)
{
/* Since we are creating edges from a new block to successors
is no need to actually check for duplicated edges. */
n = unchecked_make_edge (new_bb, s->dest, s->flags);
n->probability = s->probability;
- if (e && bb->count)
- {
- /* Take care for overflows! */
- n->count = s->count * (new_count * 10000 / bb->count) / 10000;
- s->count -= n->count;
- }
- else
- n->count = s->count;
n->aux = s->aux;
}
new_bb->count = new_count;
bb->count -= new_count;
- new_bb->frequency = EDGE_FREQUENCY (e);
- bb->frequency -= EDGE_FREQUENCY (e);
-
redirect_edge_and_branch_force (e, new_bb);
-
- if (bb->count < 0)
- bb->count = 0;
- if (bb->frequency < 0)
- bb->frequency = 0;
}
else
- {
- new_bb->count = bb->count;
- new_bb->frequency = bb->frequency;
- }
+ new_bb->count = bb->count;
set_bb_original (new_bb, bb);
set_bb_copy (bb, new_bb);
{
struct loop *cloop = bb->loop_father;
struct loop *copy = get_loop_copy (cloop);
- add_bb_to_loop (new_bb, copy ? copy : cloop);
+ /* If we copied the loop header block but not the loop
+ we have created a loop with multiple entries. Ditch the loop,
+ add the new block to the outer loop and arrange for a fixup. */
+ if (!copy
+ && cloop->header == bb)
+ {
+ add_bb_to_loop (new_bb, loop_outer (cloop));
+ mark_loop_for_removal (cloop);
+ }
+ else
+ {
+ add_bb_to_loop (new_bb, copy ? copy : cloop);
+ /* If we copied the loop latch block but not the loop, adjust
+ loop state. */
+ if (!copy
+ && cloop->latch == bb)
+ {
+ cloop->latch = NULL;
+ loops_state_set (LOOPS_MAY_HAVE_MULTIPLE_LATCHES);
+ }
+ }
}
return new_bb;
/* Return 1 if BB ends with a conditional branch, 0 otherwise. */
bool
-block_ends_with_condjump_p (basic_block bb)
+block_ends_with_condjump_p (const_basic_block bb)
{
if (!cfg_hooks->block_ends_with_condjump_p)
internal_error ("%s does not support block_ends_with_condjump_p",
void
execute_on_growing_pred (edge e)
{
- if (cfg_hooks->execute_on_growing_pred)
+ if (! (e->dest->flags & BB_DUPLICATED)
+ && cfg_hooks->execute_on_growing_pred)
cfg_hooks->execute_on_growing_pred (e);
}
void
execute_on_shrinking_pred (edge e)
{
- if (cfg_hooks->execute_on_shrinking_pred)
+ if (! (e->dest->flags & BB_DUPLICATED)
+ && cfg_hooks->execute_on_shrinking_pred)
cfg_hooks->execute_on_shrinking_pred (e);
}
cfg_hook_duplicate_loop_to_header_edge (struct loop *loop, edge e,
unsigned int ndupl,
sbitmap wont_exit, edge orig,
- VEC (edge, heap) **to_remove,
+ vec<edge> *to_remove,
int flags)
{
gcc_assert (cfg_hooks->cfg_hook_duplicate_loop_to_header_edge);
/* Conditional jumps are represented differently in trees and RTL,
this hook takes a basic block that is known to have a cond jump
- at its end and extracts the taken and not taken eges out of it
+ at its end and extracts the taken and not taken edges out of it
and store it in E1 and E2 respectively. */
void
extract_cond_bb_edges (basic_block b, edge *e1, edge *e2)
gcc_assert (cfg_hooks->lv_add_condition_to_bb);
cfg_hooks->lv_add_condition_to_bb (first, second, new_block, cond);
}
+
+/* Checks whether all N blocks in BBS array can be copied. */
+bool
+can_copy_bbs_p (basic_block *bbs, unsigned n)
+{
+ unsigned i;
+ edge e;
+ int ret = true;
+
+ for (i = 0; i < n; i++)
+ bbs[i]->flags |= BB_DUPLICATED;
+
+ for (i = 0; i < n; i++)
+ {
+ /* In case we should redirect abnormal edge during duplication, fail. */
+ edge_iterator ei;
+ FOR_EACH_EDGE (e, ei, bbs[i]->succs)
+ if ((e->flags & EDGE_ABNORMAL)
+ && (e->dest->flags & BB_DUPLICATED))
+ {
+ ret = false;
+ goto end;
+ }
+
+ if (!can_duplicate_block_p (bbs[i]))
+ {
+ ret = false;
+ break;
+ }
+ }
+
+end:
+ for (i = 0; i < n; i++)
+ bbs[i]->flags &= ~BB_DUPLICATED;
+
+ return ret;
+}
+
+/* Duplicates N basic blocks stored in array BBS. Newly created basic blocks
+ are placed into array NEW_BBS in the same order. Edges from basic blocks
+ in BBS are also duplicated and copies of those that lead into BBS are
+ redirected to appropriate newly created block. The function assigns bbs
+ into loops (copy of basic block bb is assigned to bb->loop_father->copy
+ loop, so this must be set up correctly in advance)
+
+ If UPDATE_DOMINANCE is true then this function updates dominators locally
+ (LOOPS structure that contains the information about dominators is passed
+ to enable this), otherwise it does not update the dominator information
+ and it assumed that the caller will do this, perhaps by destroying and
+ recreating it instead of trying to do an incremental update like this
+ function does when update_dominance is true.
+
+ BASE is the superloop to that basic block belongs; if its header or latch
+ is copied, we do not set the new blocks as header or latch.
+
+ Created copies of N_EDGES edges in array EDGES are stored in array NEW_EDGES,
+ also in the same order.
+
+ Newly created basic blocks are put after the basic block AFTER in the
+ instruction stream, and the order of the blocks in BBS array is preserved. */
+
+void
+copy_bbs (basic_block *bbs, unsigned n, basic_block *new_bbs,
+ edge *edges, unsigned num_edges, edge *new_edges,
+ struct loop *base, basic_block after, bool update_dominance)
+{
+ unsigned i, j;
+ basic_block bb, new_bb, dom_bb;
+ edge e;
+ copy_bb_data id;
+
+ /* Mark the blocks to be copied. This is used by edge creation hooks
+ to decide whether to reallocate PHI nodes capacity to avoid reallocating
+ PHIs in the set of source BBs. */
+ for (i = 0; i < n; i++)
+ bbs[i]->flags |= BB_DUPLICATED;
+
+ /* Duplicate bbs, update dominators, assign bbs to loops. */
+ for (i = 0; i < n; i++)
+ {
+ /* Duplicate. */
+ bb = bbs[i];
+ new_bb = new_bbs[i] = duplicate_block (bb, NULL, after, &id);
+ after = new_bb;
+ if (bb->loop_father)
+ {
+ /* Possibly set loop header. */
+ if (bb->loop_father->header == bb && bb->loop_father != base)
+ new_bb->loop_father->header = new_bb;
+ /* Or latch. */
+ if (bb->loop_father->latch == bb && bb->loop_father != base)
+ new_bb->loop_father->latch = new_bb;
+ }
+ }
+
+ /* Set dominators. */
+ if (update_dominance)
+ {
+ for (i = 0; i < n; i++)
+ {
+ bb = bbs[i];
+ new_bb = new_bbs[i];
+
+ dom_bb = get_immediate_dominator (CDI_DOMINATORS, bb);
+ if (dom_bb->flags & BB_DUPLICATED)
+ {
+ dom_bb = get_bb_copy (dom_bb);
+ set_immediate_dominator (CDI_DOMINATORS, new_bb, dom_bb);
+ }
+ }
+ }
+
+ /* Redirect edges. */
+ for (j = 0; j < num_edges; j++)
+ new_edges[j] = NULL;
+ for (i = 0; i < n; i++)
+ {
+ edge_iterator ei;
+ new_bb = new_bbs[i];
+ bb = bbs[i];
+
+ FOR_EACH_EDGE (e, ei, new_bb->succs)
+ {
+ for (j = 0; j < num_edges; j++)
+ if (edges[j] && edges[j]->src == bb && edges[j]->dest == e->dest)
+ new_edges[j] = e;
+
+ if (!(e->dest->flags & BB_DUPLICATED))
+ continue;
+ redirect_edge_and_branch_force (e, get_bb_copy (e->dest));
+ }
+ }
+
+ /* Clear information about duplicates. */
+ for (i = 0; i < n; i++)
+ bbs[i]->flags &= ~BB_DUPLICATED;
+}
+
+/* Return true if BB contains only labels or non-executable
+ instructions */
+bool
+empty_block_p (basic_block bb)
+{
+ gcc_assert (cfg_hooks->empty_block_p);
+ return cfg_hooks->empty_block_p (bb);
+}
+
+/* Split a basic block if it ends with a conditional branch and if
+ the other part of the block is not empty. */
+basic_block
+split_block_before_cond_jump (basic_block bb)
+{
+ gcc_assert (cfg_hooks->split_block_before_cond_jump);
+ return cfg_hooks->split_block_before_cond_jump (bb);
+}
+
+/* Work-horse for passes.c:check_profile_consistency.
+ Do book-keeping of the CFG for the profile consistency checker.
+ Store the counting in RECORD. */
+
+void
+profile_record_check_consistency (profile_record *record)
+{
+ basic_block bb;
+ edge_iterator ei;
+ edge e;
+
+ FOR_ALL_BB_FN (bb, cfun)
+ {
+ if (bb != EXIT_BLOCK_PTR_FOR_FN (cfun)
+ && profile_status_for_fn (cfun) != PROFILE_ABSENT)
+ {
+ profile_probability sum = profile_probability::never ();
+ FOR_EACH_EDGE (e, ei, bb->succs)
+ sum += e->probability;
+ if (EDGE_COUNT (bb->succs)
+ && sum.differs_from_p (profile_probability::always ()))
+ record->num_mismatched_freq_out++;
+ profile_count lsum = profile_count::zero ();
+ FOR_EACH_EDGE (e, ei, bb->succs)
+ lsum += e->count ();
+ if (EDGE_COUNT (bb->succs) && (lsum.differs_from_p (bb->count)))
+ record->num_mismatched_count_out++;
+ }
+ if (bb != ENTRY_BLOCK_PTR_FOR_FN (cfun)
+ && profile_status_for_fn (cfun) != PROFILE_ABSENT)
+ {
+ profile_probability sum = profile_probability::never ();
+ profile_count lsum = profile_count::zero ();
+ FOR_EACH_EDGE (e, ei, bb->preds)
+ {
+ sum += e->probability;
+ lsum += e->count ();
+ }
+ if (EDGE_COUNT (bb->preds)
+ && sum.differs_from_p (profile_probability::always ()))
+ record->num_mismatched_freq_in++;
+ if (lsum.differs_from_p (bb->count))
+ record->num_mismatched_count_in++;
+ }
+ if (bb == ENTRY_BLOCK_PTR_FOR_FN (cfun)
+ || bb == EXIT_BLOCK_PTR_FOR_FN (cfun))
+ continue;
+ gcc_assert (cfg_hooks->account_profile_record);
+ cfg_hooks->account_profile_record (bb, record);
+ }
+}
+
+/* Work-horse for passes.c:acount_profile.
+ Do book-keeping of the CFG for the profile accounting.
+ Store the counting in RECORD. */
+
+void
+profile_record_account_profile (profile_record *record)
+{
+ basic_block bb;
+
+ FOR_ALL_BB_FN (bb, cfun)
+ {
+ gcc_assert (cfg_hooks->account_profile_record);
+ cfg_hooks->account_profile_record (bb, record);
+ }
+}