#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "tm.h"
#include "tree.h"
#include "rtl.h"
#include "basic-block.h"
#include "tree-flow.h"
#include "timevar.h"
#include "toplev.h"
static struct cfg_hooks *cfg_hooks;
void
rtl_register_cfg_hooks (void)
{
cfg_hooks = &rtl_cfg_hooks;
}
void
cfg_layout_rtl_register_cfg_hooks (void)
{
cfg_hooks = &cfg_layout_rtl_cfg_hooks;
}
void
tree_register_cfg_hooks (void)
{
cfg_hooks = &tree_cfg_hooks;
}
int
ir_type (void)
{
return cfg_hooks == &tree_cfg_hooks ? 1 : 0;
}
void
verify_flow_info (void)
{
size_t *edge_checksum;
int num_bb_notes, err = 0;
basic_block bb, last_bb_seen;
basic_block *last_visited;
timevar_push (TV_CFG_VERIFY);
last_visited = xcalloc (last_basic_block + 2, sizeof (basic_block));
edge_checksum = xcalloc (last_basic_block + 2, sizeof (size_t));
last_bb_seen = ENTRY_BLOCK_PTR;
FOR_BB_BETWEEN (bb, ENTRY_BLOCK_PTR->next_bb, NULL, next_bb)
{
if (bb != EXIT_BLOCK_PTR
&& bb != BASIC_BLOCK (bb->index))
{
error ("bb %d on wrong place", bb->index);
err = 1;
}
if (bb->prev_bb != last_bb_seen)
{
error ("prev_bb of %d should be %d, not %d",
bb->index, last_bb_seen->index, bb->prev_bb->index);
err = 1;
}
last_bb_seen = bb;
}
FOR_EACH_BB_REVERSE (bb)
{
int n_fallthru = 0;
edge e;
edge_iterator ei;
if (bb->count < 0)
{
error ("verify_flow_info: Wrong count of block %i %i",
bb->index, (int)bb->count);
err = 1;
}
if (bb->frequency < 0)
{
error ("verify_flow_info: Wrong frequency of block %i %i",
bb->index, bb->frequency);
err = 1;
}
FOR_EACH_EDGE (e, ei, bb->succs)
{
if (last_visited [e->dest->index + 2] == bb)
{
error ("verify_flow_info: Duplicate edge %i->%i",
e->src->index, e->dest->index);
err = 1;
}
if (e->probability < 0 || e->probability > REG_BR_PROB_BASE)
{
error ("verify_flow_info: Wrong probability of edge %i->%i %i",
e->src->index, e->dest->index, e->probability);
err = 1;
}
if (e->count < 0)
{
error ("verify_flow_info: Wrong count of edge %i->%i %i",
e->src->index, e->dest->index, (int)e->count);
err = 1;
}
last_visited [e->dest->index + 2] = bb;
if (e->flags & EDGE_FALLTHRU)
n_fallthru++;
if (e->src != bb)
{
error ("verify_flow_info: Basic block %d succ edge is corrupted",
bb->index);
fprintf (stderr, "Predecessor: ");
dump_edge_info (stderr, e, 0);
fprintf (stderr, "\nSuccessor: ");
dump_edge_info (stderr, e, 1);
fprintf (stderr, "\n");
err = 1;
}
edge_checksum[e->dest->index + 2] += (size_t) e;
}
if (n_fallthru > 1)
{
error ("Wrong amount of branch edges after unconditional jump %i", bb->index);
err = 1;
}
FOR_EACH_EDGE (e, ei, bb->preds)
{
if (e->dest != bb)
{
error ("basic block %d pred edge is corrupted", bb->index);
fputs ("Predecessor: ", stderr);
dump_edge_info (stderr, e, 0);
fputs ("\nSuccessor: ", stderr);
dump_edge_info (stderr, e, 1);
fputc ('\n', stderr);
err = 1;
}
if (ei.index != e->dest_idx)
{
error ("basic block %d pred edge is corrupted", bb->index);
error ("its dest_idx should be %d, not %d",
ei.index, e->dest_idx);
fputs ("Predecessor: ", stderr);
dump_edge_info (stderr, e, 0);
fputs ("\nSuccessor: ", stderr);
dump_edge_info (stderr, e, 1);
fputc ('\n', stderr);
err = 1;
}
edge_checksum[e->dest->index + 2] -= (size_t) e;
}
}
{
edge e;
edge_iterator ei;
FOR_EACH_EDGE (e, ei, ENTRY_BLOCK_PTR->succs)
edge_checksum[e->dest->index + 2] += (size_t) e;
FOR_EACH_EDGE (e, ei, EXIT_BLOCK_PTR->preds)
edge_checksum[e->dest->index + 2] -= (size_t) e;
}
FOR_BB_BETWEEN (bb, ENTRY_BLOCK_PTR, NULL, next_bb)
if (edge_checksum[bb->index + 2])
{
error ("basic block %i edge lists are corrupted", bb->index);
err = 1;
}
num_bb_notes = 0;
last_bb_seen = ENTRY_BLOCK_PTR;
free (last_visited);
free (edge_checksum);
if (cfg_hooks->verify_flow_info)
err |= cfg_hooks->verify_flow_info ();
if (err)
internal_error ("verify_flow_info failed");
timevar_pop (TV_CFG_VERIFY);
}
void
dump_bb (basic_block bb, FILE *outf, int indent)
{
edge e;
edge_iterator ei;
char *s_indent;
s_indent = alloca ((size_t) indent + 1);
memset (s_indent, ' ', (size_t) indent);
s_indent[indent] = '\0';
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);
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);
else
fprintf (outf, "(nil)");
putc ('\n', outf);
fprintf (outf, ";;%s pred: ", s_indent);
FOR_EACH_EDGE (e, ei, bb->preds)
dump_edge_info (outf, e, 0);
putc ('\n', outf);
fprintf (outf, ";;%s succ: ", s_indent);
FOR_EACH_EDGE (e, ei, bb->succs)
dump_edge_info (outf, e, 1);
putc ('\n', outf);
if (cfg_hooks->dump_bb)
cfg_hooks->dump_bb (bb, outf, indent);
}
edge
redirect_edge_and_branch (edge e, basic_block dest)
{
edge ret;
if (!cfg_hooks->redirect_edge_and_branch)
internal_error ("%s does not support redirect_edge_and_branch.",
cfg_hooks->name);
ret = cfg_hooks->redirect_edge_and_branch (e, dest);
return ret;
}
basic_block
redirect_edge_and_branch_force (edge e, basic_block dest)
{
basic_block ret;
if (!cfg_hooks->redirect_edge_and_branch_force)
internal_error ("%s does not support redirect_edge_and_branch_force.",
cfg_hooks->name);
ret = cfg_hooks->redirect_edge_and_branch_force (e, dest);
return ret;
}
edge
split_block (basic_block bb, void *i)
{
basic_block new_bb;
bool irr = (bb->flags & BB_IRREDUCIBLE_LOOP) != 0;
int flags = EDGE_FALLTHRU;
if (!cfg_hooks->split_block)
internal_error ("%s does not support split_block.", cfg_hooks->name);
new_bb = cfg_hooks->split_block (bb, i);
if (!new_bb)
return NULL;
new_bb->count = bb->count;
new_bb->frequency = bb->frequency;
new_bb->loop_depth = bb->loop_depth;
if (irr)
{
new_bb->flags |= BB_IRREDUCIBLE_LOOP;
flags |= EDGE_IRREDUCIBLE_LOOP;
}
if (dom_info_available_p (CDI_DOMINATORS))
{
redirect_immediate_dominators (CDI_DOMINATORS, bb, new_bb);
set_immediate_dominator (CDI_DOMINATORS, new_bb, bb);
}
return make_single_succ_edge (bb, new_bb, EDGE_FALLTHRU);
}
edge
split_block_after_labels (basic_block bb)
{
return split_block (bb, NULL);
}
bool
move_block_after (basic_block bb, basic_block after)
{
bool ret;
if (!cfg_hooks->move_block_after)
internal_error ("%s does not support move_block_after.", cfg_hooks->name);
ret = cfg_hooks->move_block_after (bb, after);
return ret;
}
void
delete_basic_block (basic_block bb)
{
if (!cfg_hooks->delete_basic_block)
internal_error ("%s does not support delete_basic_block.", cfg_hooks->name);
cfg_hooks->delete_basic_block (bb);
while (EDGE_COUNT (bb->preds) != 0)
remove_edge (EDGE_PRED (bb, 0));
while (EDGE_COUNT (bb->succs) != 0)
remove_edge (EDGE_SUCC (bb, 0));
if (dom_computed[CDI_DOMINATORS])
delete_from_dominance_info (CDI_DOMINATORS, bb);
if (dom_computed[CDI_POST_DOMINATORS])
delete_from_dominance_info (CDI_POST_DOMINATORS, bb);
expunge_block (bb);
}
basic_block
split_edge (edge e)
{
basic_block ret;
gcov_type count = e->count;
int freq = EDGE_FREQUENCY (e);
edge f;
bool irr = (e->flags & EDGE_IRREDUCIBLE_LOOP) != 0;
if (!cfg_hooks->split_edge)
internal_error ("%s does not support split_edge.", cfg_hooks->name);
ret = cfg_hooks->split_edge (e);
ret->count = count;
ret->frequency = freq;
EDGE_SUCC (ret, 0)->probability = REG_BR_PROB_BASE;
EDGE_SUCC (ret, 0)->count = count;
if (irr)
{
ret->flags |= BB_IRREDUCIBLE_LOOP;
EDGE_PRED (ret, 0)->flags |= EDGE_IRREDUCIBLE_LOOP;
EDGE_SUCC (ret, 0)->flags |= EDGE_IRREDUCIBLE_LOOP;
}
if (dom_computed[CDI_DOMINATORS])
set_immediate_dominator (CDI_DOMINATORS, ret, EDGE_PRED (ret, 0)->src);
if (dom_computed[CDI_DOMINATORS] >= DOM_NO_FAST_QUERY)
{
if (get_immediate_dominator (CDI_DOMINATORS, EDGE_SUCC (ret, 0)->dest)
== EDGE_PRED (ret, 0)->src)
{
edge_iterator ei;
FOR_EACH_EDGE (f, ei, EDGE_SUCC (ret, 0)->dest->preds)
{
if (f == EDGE_SUCC (ret, 0))
continue;
if (!dominated_by_p (CDI_DOMINATORS, f->src,
EDGE_SUCC (ret, 0)->dest))
break;
}
if (!f)
set_immediate_dominator (CDI_DOMINATORS, EDGE_SUCC (ret, 0)->dest, ret);
}
};
if (irr)
{
ret->flags |= BB_IRREDUCIBLE_LOOP;
EDGE_PRED (ret, 0)->flags |= EDGE_IRREDUCIBLE_LOOP;
EDGE_SUCC (ret, 0)->flags |= EDGE_IRREDUCIBLE_LOOP;
}
return ret;
}
basic_block
create_basic_block (void *head, void *end, basic_block after)
{
basic_block ret;
if (!cfg_hooks->create_basic_block)
internal_error ("%s does not support create_basic_block.", cfg_hooks->name);
ret = cfg_hooks->create_basic_block (head, end, after);
if (dom_computed[CDI_DOMINATORS])
add_to_dominance_info (CDI_DOMINATORS, ret);
if (dom_computed[CDI_POST_DOMINATORS])
add_to_dominance_info (CDI_POST_DOMINATORS, ret);
return ret;
}
basic_block
create_empty_bb (basic_block after)
{
return create_basic_block (NULL, NULL, after);
}
bool
can_merge_blocks_p (basic_block bb1, basic_block bb2)
{
bool ret;
if (!cfg_hooks->can_merge_blocks_p)
internal_error ("%s does not support can_merge_blocks_p.", cfg_hooks->name);
ret = cfg_hooks->can_merge_blocks_p (bb1, bb2);
return ret;
}
void
predict_edge (edge e, enum br_predictor predictor, int probability)
{
if (!cfg_hooks->predict_edge)
internal_error ("%s does not support predict_edge.", cfg_hooks->name);
cfg_hooks->predict_edge (e, predictor, probability);
}
bool
predicted_by_p (basic_block bb, enum br_predictor predictor)
{
if (!cfg_hooks->predict_edge)
internal_error ("%s does not support predicted_by_p.", cfg_hooks->name);
return cfg_hooks->predicted_by_p (bb, predictor);
}
void
merge_blocks (basic_block a, basic_block b)
{
edge e;
edge_iterator ei;
if (!cfg_hooks->merge_blocks)
internal_error ("%s does not support merge_blocks.", cfg_hooks->name);
cfg_hooks->merge_blocks (a, b);
while (EDGE_COUNT (a->succs) != 0)
remove_edge (EDGE_SUCC (a, 0));
FOR_EACH_EDGE (e, ei, b->succs)
e->src = a;
a->succs = b->succs;
a->flags |= b->flags;
b->preds = b->succs = NULL;
a->global_live_at_end = b->global_live_at_end;
if (dom_computed[CDI_DOMINATORS])
redirect_immediate_dominators (CDI_DOMINATORS, b, a);
if (dom_computed[CDI_DOMINATORS])
delete_from_dominance_info (CDI_DOMINATORS, b);
if (dom_computed[CDI_POST_DOMINATORS])
delete_from_dominance_info (CDI_POST_DOMINATORS, b);
expunge_block (b);
}
edge
make_forwarder_block (basic_block bb, bool (*redirect_edge_p) (edge),
void (*new_bb_cbk) (basic_block))
{
edge e, fallthru;
edge_iterator ei;
basic_block dummy, jump;
bool fst_irr = false;
if (!cfg_hooks->make_forwarder_block)
internal_error ("%s does not support make_forwarder_block.",
cfg_hooks->name);
fallthru = split_block_after_labels (bb);
dummy = fallthru->src;
bb = fallthru->dest;
for (ei = ei_start (dummy->preds); (e = ei_safe_edge (ei)); )
{
if (redirect_edge_p (e))
{
fst_irr |= (e->flags & EDGE_IRREDUCIBLE_LOOP) != 0;
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;
jump = redirect_edge_and_branch_force (e, bb);
if (jump)
new_bb_cbk (jump);
}
if (!fst_irr)
{
dummy->flags &= ~BB_IRREDUCIBLE_LOOP;
fallthru->flags &= ~EDGE_IRREDUCIBLE_LOOP;
}
if (dom_info_available_p (CDI_DOMINATORS))
{
basic_block doms_to_fix[2];
doms_to_fix[0] = dummy;
doms_to_fix[1] = bb;
iterate_fix_dominators (CDI_DOMINATORS, doms_to_fix, 2);
}
cfg_hooks->make_forwarder_block (fallthru);
return fallthru;
}
void
tidy_fallthru_edge (edge e)
{
if (cfg_hooks->tidy_fallthru_edge)
cfg_hooks->tidy_fallthru_edge (e);
}
void
tidy_fallthru_edges (void)
{
basic_block b, c;
if (!cfg_hooks->tidy_fallthru_edge)
return;
if (ENTRY_BLOCK_PTR->next_bb == EXIT_BLOCK_PTR)
return;
FOR_BB_BETWEEN (b, ENTRY_BLOCK_PTR->next_bb, EXIT_BLOCK_PTR->prev_bb, next_bb)
{
edge s;
c = b->next_bb;
if (EDGE_COUNT (b->succs) == 1)
{
s = EDGE_SUCC (b, 0);
if (! (s->flags & EDGE_COMPLEX)
&& s->dest == c
&& !find_reg_note (BB_END (b), REG_CROSSING_JUMP, NULL_RTX))
tidy_fallthru_edge (s);
}
}
}
bool
can_duplicate_block_p (basic_block bb)
{
edge e;
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)
return false;
e = find_edge (bb, EXIT_BLOCK_PTR);
if (e && (e->flags & EDGE_FALLTHRU))
return false;
return cfg_hooks->can_duplicate_block_p (bb);
}
basic_block
duplicate_block (basic_block bb, edge e)
{
edge s, n;
basic_block new_bb;
gcov_type new_count = e ? e->count : 0;
edge_iterator ei;
if (!cfg_hooks->duplicate_block)
internal_error ("%s does not support duplicate_block.",
cfg_hooks->name);
if (bb->count < new_count)
new_count = bb->count;
#ifdef ENABLE_CHECKING
gcc_assert (can_duplicate_block_p (bb));
#endif
new_bb = cfg_hooks->duplicate_block (bb);
new_bb->loop_depth = bb->loop_depth;
new_bb->flags = bb->flags;
FOR_EACH_EDGE (s, ei, bb->succs)
{
n = unchecked_make_edge (new_bb, s->dest, s->flags);
n->probability = s->probability;
if (e && bb->count)
{
n->count = s->count * (new_count * 10000 / bb->count) / 10000;
s->count -= n->count;
}
else
n->count = s->count;
n->aux = s->aux;
}
if (e)
{
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->rbi->original = bb;
bb->rbi->copy = new_bb;
return new_bb;
}
bool
block_ends_with_call_p (basic_block bb)
{
if (!cfg_hooks->block_ends_with_call_p)
internal_error ("%s does not support block_ends_with_call_p", cfg_hooks->name);
return (cfg_hooks->block_ends_with_call_p) (bb);
}
bool
block_ends_with_condjump_p (basic_block bb)
{
if (!cfg_hooks->block_ends_with_condjump_p)
internal_error ("%s does not support block_ends_with_condjump_p",
cfg_hooks->name);
return (cfg_hooks->block_ends_with_condjump_p) (bb);
}
int
flow_call_edges_add (sbitmap blocks)
{
if (!cfg_hooks->flow_call_edges_add)
internal_error ("%s does not support flow_call_edges_add",
cfg_hooks->name);
return (cfg_hooks->flow_call_edges_add) (blocks);
}
void
execute_on_growing_pred (edge e)
{
if (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)
cfg_hooks->execute_on_shrinking_pred (e);
}