/* Control flow graph manipulation code for GNU compiler.
Copyright (C) 1987, 1988, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
- 1999, 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
+ 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007
+ Free Software Foundation, Inc.
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) any later
+Software Foundation; either version 3, or (at your option) any later
version.
GCC is distributed in the hope that it will be useful, but WITHOUT ANY
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/>. */
/* This file contains low level functions to manipulate the CFG and analyze it
that are aware of the RTL intermediate language.
#include "cfgloop.h"
#include "ggc.h"
#include "tree-pass.h"
+#include "df.h"
static int can_delete_note_p (rtx);
static int can_delete_label_p (rtx);
-static void commit_one_edge_insertion (edge, int);
+static void commit_one_edge_insertion (edge);
static basic_block rtl_split_edge (edge);
static bool rtl_move_block_after (basic_block, basic_block);
static int rtl_verify_flow_info (void);
static int
can_delete_note_p (rtx note)
{
- return (NOTE_LINE_NUMBER (note) == NOTE_INSN_DELETED
- || NOTE_LINE_NUMBER (note) == NOTE_INSN_BASIC_BLOCK);
+ return (NOTE_KIND (note) == NOTE_INSN_DELETED
+ || NOTE_KIND (note) == NOTE_INSN_BASIC_BLOCK);
}
/* True if a given label can be deleted. */
really_delete = false;
PUT_CODE (insn, NOTE);
- NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED_LABEL;
+ NOTE_KIND (insn) = NOTE_INSN_DELETED_LABEL;
NOTE_DELETED_LABEL_NAME (insn) = name;
}
}
/* Unlink a chain of insns between START and FINISH, leaving notes
- that must be paired. */
+ that must be paired. If CLEAR_BB is true, we set bb field for
+ insns that cannot be removed to NULL. */
void
-delete_insn_chain (rtx start, rtx finish)
+delete_insn_chain (rtx start, rtx finish, bool clear_bb)
{
rtx next;
else
next = delete_insn (start);
+ if (clear_bb && !INSN_DELETED_P (start))
+ set_block_for_insn (start, NULL);
+
if (start == finish)
break;
start = next;
}
}
-
-/* Like delete_insn but also purge dead edges from BB. */
-void
-delete_insn_chain_and_edges (rtx first, rtx last)
-{
- bool purge = false;
-
- if (INSN_P (last)
- && BLOCK_FOR_INSN (last)
- && BB_END (BLOCK_FOR_INSN (last)) == last)
- purge = true;
- delete_insn_chain (first, last);
- if (purge)
- purge_dead_edges (BLOCK_FOR_INSN (last));
-}
\f
/* Create a new basic block consisting of the instructions between HEAD and END
inclusive. This function is designed to allow fast BB construction - reuses
bb->flags = BB_NEW | BB_RTL;
link_block (bb, after);
SET_BASIC_BLOCK (bb->index, bb);
+ df_bb_refs_record (bb->index, false);
update_bb_for_insn (bb);
BB_SET_PARTITION (bb, BB_UNPARTITIONED);
static basic_block
rtl_create_basic_block (void *headp, void *endp, basic_block after)
{
- rtx head = headp, end = endp;
+ rtx head = (rtx) headp, end = (rtx) endp;
basic_block bb;
/* Grow the basic block array if needed. */
if ((size_t) last_basic_block >= VEC_length (basic_block, basic_block_info))
{
- size_t old_size = VEC_length (basic_block, basic_block_info);
size_t new_size = last_basic_block + (last_basic_block + 3) / 4;
- basic_block *p;
- VEC_safe_grow (basic_block, gc, basic_block_info, new_size);
- p = VEC_address (basic_block, basic_block_info);
- memset (&p[old_size], 0, sizeof (basic_block) * (new_size - old_size));
+ VEC_safe_grow_cleared (basic_block, gc, basic_block_info, new_size);
}
n_basic_blocks++;
static void
rtl_delete_block (basic_block b)
{
- rtx insn, end, tmp;
+ rtx insn, end;
/* If the head of this block is a CODE_LABEL, then it might be the
label for an exception handler which can't be reached. We need
if (LABEL_P (insn))
maybe_remove_eh_handler (insn);
- /* Include any jump table following the basic block. */
- end = BB_END (b);
- if (tablejump_p (end, NULL, &tmp))
- end = tmp;
-
- /* Include any barriers that may follow the basic block. */
- tmp = next_nonnote_insn (end);
- while (tmp && BARRIER_P (tmp))
- {
- end = tmp;
- tmp = next_nonnote_insn (end);
- }
+ end = get_last_bb_insn (b);
/* Selectively delete the entire chain. */
BB_HEAD (b) = NULL;
- delete_insn_chain (insn, end);
- if (b->il.rtl->global_live_at_start)
- {
- FREE_REG_SET (b->il.rtl->global_live_at_start);
- FREE_REG_SET (b->il.rtl->global_live_at_end);
- b->il.rtl->global_live_at_start = NULL;
- b->il.rtl->global_live_at_end = NULL;
- }
+ delete_insn_chain (insn, end, true);
+
+
+ if (dump_file)
+ fprintf (dump_file, "deleting block %d\n", b->index);
+ df_bb_delete (b->index);
}
\f
/* Records the basic block struct in BLOCK_FOR_INSN for every insn. */
for (insn = BB_HEAD (bb); ; insn = NEXT_INSN (insn))
{
if (!BARRIER_P (insn))
- set_block_for_insn (insn, bb);
+ {
+ set_block_for_insn (insn, bb);
+ df_insn_change_bb (insn);
+ }
if (insn == BB_END (bb))
break;
}
}
\f
+/* Return the INSN immediately following the NOTE_INSN_BASIC_BLOCK
+ note associated with the BLOCK. */
+
+static rtx
+first_insn_after_basic_block_note (basic_block block)
+{
+ rtx insn;
+
+ /* Get the first instruction in the block. */
+ insn = BB_HEAD (block);
+
+ if (insn == NULL_RTX)
+ return NULL_RTX;
+ if (LABEL_P (insn))
+ insn = NEXT_INSN (insn);
+ gcc_assert (NOTE_INSN_BASIC_BLOCK_P (insn));
+
+ return NEXT_INSN (insn);
+}
+
/* Creates a new basic block just after basic block B by splitting
everything after specified instruction I. */
rtl_split_block (basic_block bb, void *insnp)
{
basic_block new_bb;
- rtx insn = insnp;
+ rtx insn = (rtx) insnp;
edge e;
edge_iterator ei;
FOR_EACH_EDGE (e, ei, new_bb->succs)
e->src = new_bb;
- if (bb->il.rtl->global_live_at_start)
- {
- new_bb->il.rtl->global_live_at_start = ALLOC_REG_SET (®_obstack);
- new_bb->il.rtl->global_live_at_end = ALLOC_REG_SET (®_obstack);
- COPY_REG_SET (new_bb->il.rtl->global_live_at_end, bb->il.rtl->global_live_at_end);
-
- /* We now have to calculate which registers are live at the end
- of the split basic block and at the start of the new basic
- block. Start with those registers that are known to be live
- at the end of the original basic block and get
- propagate_block to determine which registers are live. */
- COPY_REG_SET (new_bb->il.rtl->global_live_at_start, bb->il.rtl->global_live_at_end);
- propagate_block (new_bb, new_bb->il.rtl->global_live_at_start, NULL, NULL, 0);
- COPY_REG_SET (bb->il.rtl->global_live_at_end,
- new_bb->il.rtl->global_live_at_start);
-#ifdef HAVE_conditional_execution
- /* In the presence of conditional execution we are not able to update
- liveness precisely. */
- if (reload_completed)
- {
- bb->flags |= BB_DIRTY;
- new_bb->flags |= BB_DIRTY;
- }
-#endif
- }
-
+ /* The new block starts off being dirty. */
+ df_set_bb_dirty (bb);
return new_bb;
}
rtx del_first = NULL_RTX, del_last = NULL_RTX;
int b_empty = 0;
+ if (dump_file)
+ fprintf (dump_file, "merging block %d into block %d\n", b->index, a->index);
+
/* If there was a CODE_LABEL beginning B, delete it. */
if (LABEL_P (b_head))
{
for (prev = PREV_INSN (a_end); ; prev = PREV_INSN (prev))
if (!NOTE_P (prev)
- || NOTE_LINE_NUMBER (prev) == NOTE_INSN_BASIC_BLOCK
+ || NOTE_INSN_BASIC_BLOCK_P (prev)
|| prev == BB_HEAD (a))
break;
/* Delete everything marked above as well as crap that might be
hanging out between the two blocks. */
BB_HEAD (b) = NULL;
- delete_insn_chain (del_first, del_last);
+ delete_insn_chain (del_first, del_last, true);
/* Reassociate the insns of B with A. */
if (!b_empty)
rtx x;
for (x = a_end; x != b_end; x = NEXT_INSN (x))
- set_block_for_insn (x, a);
+ {
+ set_block_for_insn (x, a);
+ df_insn_change_bb (x);
+ }
set_block_for_insn (b_end, a);
+ df_insn_change_bb (b_end);
a_end = b_end;
}
+ df_bb_delete (b->index);
BB_END (a) = a_end;
- a->il.rtl->global_live_at_end = b->il.rtl->global_live_at_end;
}
+
/* Return true when block A and B can be merged. */
static bool
rtl_can_merge_blocks (basic_block a,basic_block b)
the cc0 setter too. */
kill_from = insn;
#ifdef HAVE_cc0
- if (reg_mentioned_p (cc0_rtx, PATTERN (insn)))
+ if (reg_mentioned_p (cc0_rtx, PATTERN (insn))
+ && only_sets_cc0_p (PREV_INSN (insn)))
kill_from = PREV_INSN (insn);
#endif
{
rtx insn = src->il.rtl->footer;
- delete_insn_chain (kill_from, BB_END (src));
+ delete_insn_chain (kill_from, BB_END (src), false);
/* Remove barriers but keep jumptables. */
while (insn)
}
}
else
- delete_insn_chain (kill_from, PREV_INSN (BB_HEAD (target)));
+ delete_insn_chain (kill_from, PREV_INSN (BB_HEAD (target)),
+ false);
}
/* If this already is simplejump, redirect it. */
INSN_UID (insn), INSN_UID (BB_END (src)));
- delete_insn_chain (kill_from, insn);
+ delete_insn_chain (kill_from, insn, false);
/* Recognize a tablejump that we are converting to a
simple jump and remove its associated CODE_LABEL
and ADDR_VEC or ADDR_DIFF_VEC. */
if (tablejump_p (insn, &label, &table))
- delete_insn_chain (label, table);
+ delete_insn_chain (label, table, false);
barrier = next_nonnote_insn (BB_END (src));
if (!barrier || !BARRIER_P (barrier))
for (tmp = NEXT_INSN (BB_END (src)); tmp != barrier;
tmp = NEXT_INSN (tmp))
- set_block_for_insn (tmp, src);
+ {
+ set_block_for_insn (tmp, src);
+ df_insn_change_bb (tmp);
+ }
NEXT_INSN (PREV_INSN (new_insn)) = NEXT_INSN (new_insn);
PREV_INSN (NEXT_INSN (new_insn)) = PREV_INSN (new_insn);
if (e->dest != target)
redirect_edge_succ (e, target);
-
return e;
}
if (e->dest != target)
e = redirect_edge_succ_nodup (e, target);
+
return e;
}
if ((ret = try_redirect_by_replacing_jump (e, target, false)) != NULL)
{
- src->flags |= BB_DIRTY;
+ df_set_bb_dirty (src);
return ret;
}
if (!ret)
return NULL;
- src->flags |= BB_DIRTY;
+ df_set_bb_dirty (src);
return ret;
}
jump_block->frequency = EDGE_FREQUENCY (e);
jump_block->loop_depth = target->loop_depth;
- if (target->il.rtl->global_live_at_start)
- {
- jump_block->il.rtl->global_live_at_start = ALLOC_REG_SET (®_obstack);
- jump_block->il.rtl->global_live_at_end = ALLOC_REG_SET (®_obstack);
- COPY_REG_SET (jump_block->il.rtl->global_live_at_start,
- target->il.rtl->global_live_at_start);
- COPY_REG_SET (jump_block->il.rtl->global_live_at_end,
- target->il.rtl->global_live_at_start);
- }
-
/* Make sure new block ends up in correct hot/cold section. */
BB_COPY_PARTITION (jump_block, e->src);
if (abnormal_edge_flags)
make_edge (src, target, abnormal_edge_flags);
+ df_mark_solutions_dirty ();
return new_bb;
}
/* In case the edge redirection failed, try to force it to be non-fallthru
and redirect newly created simplejump. */
- e->src->flags |= BB_DIRTY;
+ df_set_bb_dirty (e->src);
return force_nonfallthru_and_redirect (e, target);
}
/* Selectively unlink the sequence. */
if (q != PREV_INSN (BB_HEAD (c)))
- delete_insn_chain (NEXT_INSN (q), PREV_INSN (BB_HEAD (c)));
+ delete_insn_chain (NEXT_INSN (q), PREV_INSN (BB_HEAD (c)), false);
e->flags |= EDGE_FALLTHRU;
}
BB_COPY_PARTITION (bb, edge_in->dest);
}
- /* ??? This info is likely going to be out of date very soon. */
- if (edge_in->dest->il.rtl->global_live_at_start)
- {
- bb->il.rtl->global_live_at_start = ALLOC_REG_SET (®_obstack);
- bb->il.rtl->global_live_at_end = ALLOC_REG_SET (®_obstack);
- COPY_REG_SET (bb->il.rtl->global_live_at_start,
- edge_in->dest->il.rtl->global_live_at_start);
- COPY_REG_SET (bb->il.rtl->global_live_at_end,
- edge_in->dest->il.rtl->global_live_at_start);
- }
-
make_single_succ_edge (bb, edge_in->dest, EDGE_FALLTHRU);
/* For non-fallthru edges, we must adjust the predecessor's
/* Update the CFG for the instructions queued on edge E. */
static void
-commit_one_edge_insertion (edge e, int watch_calls)
+commit_one_edge_insertion (edge e)
{
rtx before = NULL_RTX, after = NULL_RTX, insns, tmp, last;
basic_block bb = NULL;
insns = e->insns.r;
e->insns.r = NULL_RTX;
- /* Special case -- avoid inserting code between call and storing
- its return value. */
- if (watch_calls && (e->flags & EDGE_FALLTHRU)
- && single_pred_p (e->dest)
- && e->src != ENTRY_BLOCK_PTR
- && CALL_P (BB_END (e->src)))
- {
- rtx next = next_nonnote_insn (BB_END (e->src));
-
- after = BB_HEAD (e->dest);
- /* The first insn after the call may be a stack pop, skip it. */
- while (next
- && keep_with_call_p (next))
- {
- after = next;
- next = next_nonnote_insn (next);
- }
- bb = e->dest;
- }
if (!before && !after)
{
/* Figure out where to put these things. If the destination has
bb_note = NULL_RTX;
for (cur_insn = BB_HEAD (bb); cur_insn != NEXT_INSN (BB_END (bb));
cur_insn = NEXT_INSN (cur_insn))
- if (NOTE_P (cur_insn)
- && NOTE_LINE_NUMBER (cur_insn) == NOTE_INSN_BASIC_BLOCK)
+ if (NOTE_INSN_BASIC_BLOCK_P (cur_insn))
{
bb_note = cur_insn;
break;
if (before)
{
- emit_insn_before_noloc (insns, before);
+ emit_insn_before_noloc (insns, before, bb);
last = prev_nonnote_insn (before);
}
else
- last = emit_insn_after_noloc (insns, after);
+ last = emit_insn_after_noloc (insns, after, bb);
if (returnjump_p (last))
{
gcc_assert (!JUMP_P (last));
/* Mark the basic block for find_many_sub_basic_blocks. */
- bb->aux = &bb->aux;
+ if (current_ir_type () != IR_RTL_CFGLAYOUT)
+ bb->aux = &bb->aux;
}
/* Update the CFG for all queued instructions. */
if (e->insns.r)
{
changed = true;
- commit_one_edge_insertion (e, false);
+ commit_one_edge_insertion (e);
}
}
if (!changed)
return;
- blocks = sbitmap_alloc (last_basic_block);
- sbitmap_zero (blocks);
- FOR_EACH_BB (bb)
- if (bb->aux)
- {
- SET_BIT (blocks, bb->index);
- /* Check for forgotten bb->aux values before commit_edge_insertions
- call. */
- gcc_assert (bb->aux == &bb->aux);
- bb->aux = NULL;
- }
- find_many_sub_basic_blocks (blocks);
- sbitmap_free (blocks);
-}
-\f
-/* Update the CFG for all queued instructions, taking special care of inserting
- code on edges between call and storing its return value. */
-
-void
-commit_edge_insertions_watch_calls (void)
-{
- basic_block bb;
- sbitmap blocks;
- bool changed = false;
-
-#ifdef ENABLE_CHECKING
- verify_flow_info ();
-#endif
-
- FOR_BB_BETWEEN (bb, ENTRY_BLOCK_PTR, EXIT_BLOCK_PTR, next_bb)
- {
- edge e;
- edge_iterator ei;
-
- FOR_EACH_EDGE (e, ei, bb->succs)
- if (e->insns.r)
- {
- changed = true;
- commit_one_edge_insertion (e, true);
- }
- }
-
- if (!changed)
+ /* In the old rtl CFG API, it was OK to insert control flow on an
+ edge, apparently? In cfglayout mode, this will *not* work, and
+ the caller is responsible for making sure that control flow is
+ valid at all times. */
+ if (current_ir_type () == IR_RTL_CFGLAYOUT)
return;
blocks = sbitmap_alloc (last_basic_block);
sbitmap_free (blocks);
}
\f
+
/* Print out RTL-specific basic block information (live information
at start and end). */
rtx last;
char *s_indent;
- s_indent = alloca ((size_t) indent + 1);
+ s_indent = (char *) alloca ((size_t) indent + 1);
memset (s_indent, ' ', (size_t) indent);
s_indent[indent] = '\0';
-
- fprintf (outf, ";;%s Registers live at start: ", s_indent);
- dump_regset (bb->il.rtl->global_live_at_start, outf);
- putc ('\n', outf);
+
+ if (df)
+ {
+ df_dump_top (bb, outf);
+ putc ('\n', outf);
+ }
for (insn = BB_HEAD (bb), last = NEXT_INSN (BB_END (bb)); insn != last;
insn = NEXT_INSN (insn))
print_rtl_single (outf, insn);
- fprintf (outf, ";;%s Registers live at end: ", s_indent);
- dump_regset (bb->il.rtl->global_live_at_end, outf);
- putc ('\n', outf);
+ if (df)
+ {
+ df_dump_bottom (bb, outf);
+ putc ('\n', outf);
+ }
+
}
\f
/* Like print_rtl, but also print out live information for the start of each
print_rtl_with_bb (FILE *outf, rtx rtx_first)
{
rtx tmp_rtx;
-
if (rtx_first == 0)
fprintf (outf, "(nil)\n");
else
basic_block bb;
+ if (df)
+ df_dump_start (outf);
+
FOR_EACH_BB_REVERSE (bb)
{
rtx x;
for (tmp_rtx = rtx_first; NULL != tmp_rtx; tmp_rtx = NEXT_INSN (tmp_rtx))
{
int did_output;
-
if ((bb = start[INSN_UID (tmp_rtx)]) != NULL)
{
- fprintf (outf, ";; Start of basic block %d, registers live:",
- bb->index);
- dump_regset (bb->il.rtl->global_live_at_start, outf);
- putc ('\n', outf);
+ edge e;
+ edge_iterator ei;
+
+ fprintf (outf, ";; Start of basic block (");
+ FOR_EACH_EDGE (e, ei, bb->preds)
+ fprintf (outf, " %d", e->src->index);
+ fprintf (outf, ") -> %d\n", bb->index);
+
+ if (df)
+ {
+ df_dump_top (bb, outf);
+ putc ('\n', outf);
+ }
+ FOR_EACH_EDGE (e, ei, bb->preds)
+ {
+ fputs (";; Pred edge ", outf);
+ dump_edge_info (outf, e, 0);
+ fputc ('\n', outf);
+ }
}
if (in_bb_p[INSN_UID (tmp_rtx)] == NOT_IN_BB
if ((bb = end[INSN_UID (tmp_rtx)]) != NULL)
{
- fprintf (outf, ";; End of basic block %d, registers live:\n",
- bb->index);
- dump_regset (bb->il.rtl->global_live_at_end, outf);
+ edge e;
+ edge_iterator ei;
+
+ fprintf (outf, ";; End of basic block %d -> (", bb->index);
+ FOR_EACH_EDGE (e, ei, bb->succs)
+ fprintf (outf, " %d", e->dest->index);
+ fprintf (outf, ")\n");
+
+ if (df)
+ {
+ df_dump_bottom (bb, outf);
+ putc ('\n', outf);
+ }
putc ('\n', outf);
+ FOR_EACH_EDGE (e, ei, bb->succs)
+ {
+ fputs (";; Succ edge ", outf);
+ dump_edge_info (outf, e, 1);
+ fputc ('\n', outf);
+ }
}
-
if (did_output)
putc ('\n', outf);
}
return;
XEXP (note, 0) = GEN_INT (BRANCH_EDGE (bb)->probability);
}
+
+/* Get the last insn associated with block BB (that includes barriers and
+ tablejumps after BB). */
+rtx
+get_last_bb_insn (basic_block bb)
+{
+ rtx tmp;
+ rtx end = BB_END (bb);
+
+ /* Include any jump table following the basic block. */
+ if (tablejump_p (end, NULL, &tmp))
+ end = tmp;
+
+ /* Include any barriers that may follow the basic block. */
+ tmp = next_nonnote_insn (end);
+ while (tmp && BARRIER_P (tmp))
+ {
+ end = tmp;
+ tmp = next_nonnote_insn (end);
+ }
+
+ return end;
+}
\f
/* Verify the CFG and RTL consistency common for both underlying RTL and
cfglayout RTL.
Currently it does following checks:
- - test head/end pointers
- overlapping of basic blocks
+ - insns with wrong BLOCK_FOR_INSN pointers
- headers of basic blocks (the NOTE_INSN_BASIC_BLOCK note)
- tails of basic blocks (ensure that boundary is necessary)
- scans body of the basic block for JUMP_INSN, CODE_LABEL
and NOTE_INSN_BASIC_BLOCK
- verify that no fall_thru edge crosses hot/cold partition boundaries
+ - verify that there are no pending RTL branch predictions
In future it can be extended check a lot of other stuff as well
(reachability of basic blocks, life information, etc. etc.). */
static int
rtl_verify_flow_info_1 (void)
{
- const int max_uid = get_max_uid ();
- rtx last_head = get_last_insn ();
- basic_block *bb_info;
rtx x;
int err = 0;
basic_block bb;
- bb_info = XCNEWVEC (basic_block, max_uid);
-
+ /* Check the general integrity of the basic blocks. */
FOR_EACH_BB_REVERSE (bb)
{
- rtx head = BB_HEAD (bb);
- rtx end = BB_END (bb);
-
- /* Verify the end of the basic block is in the INSN chain. */
- for (x = last_head; x != NULL_RTX; x = PREV_INSN (x))
- if (x == end)
- break;
+ rtx insn;
if (!(bb->flags & BB_RTL))
{
err = 1;
}
- if (!x)
- {
- error ("end insn %d for block %d not found in the insn stream",
- INSN_UID (end), bb->index);
- err = 1;
- }
-
- /* Work backwards from the end to the head of the basic block
- to verify the head is in the RTL chain. */
- for (; x != NULL_RTX; x = PREV_INSN (x))
- {
- /* While walking over the insn chain, verify insns appear
- in only one basic block and initialize the BB_INFO array
- used by other passes. */
- if (bb_info[INSN_UID (x)] != NULL)
- {
- error ("insn %d is in multiple basic blocks (%d and %d)",
- INSN_UID (x), bb->index, bb_info[INSN_UID (x)]->index);
- err = 1;
- }
-
- bb_info[INSN_UID (x)] = bb;
-
- if (x == head)
- break;
- }
- if (!x)
- {
- error ("head insn %d for block %d not found in the insn stream",
- INSN_UID (head), bb->index);
- err = 1;
- }
+ FOR_BB_INSNS (bb, insn)
+ if (BLOCK_FOR_INSN (insn) != bb)
+ {
+ error ("insn %d basic block pointer is %d, should be %d",
+ INSN_UID (insn),
+ BLOCK_FOR_INSN (insn) ? BLOCK_FOR_INSN (insn)->index : 0,
+ bb->index);
+ err = 1;
+ }
- last_head = x;
+ for (insn = bb->il.rtl->header; insn; insn = NEXT_INSN (insn))
+ if (!BARRIER_P (insn)
+ && BLOCK_FOR_INSN (insn) != NULL)
+ {
+ error ("insn %d in header of bb %d has non-NULL basic block",
+ INSN_UID (insn), bb->index);
+ err = 1;
+ }
+ for (insn = bb->il.rtl->footer; insn; insn = NEXT_INSN (insn))
+ if (!BARRIER_P (insn)
+ && BLOCK_FOR_INSN (insn) != NULL)
+ {
+ error ("insn %d in footer of bb %d has non-NULL basic block",
+ INSN_UID (insn), bb->index);
+ err = 1;
+ }
}
/* Now check the basic blocks (boundaries etc.) */
}
/* Clean up. */
- free (bb_info);
return err;
}
Currently it does following checks:
- all checks of rtl_verify_flow_info_1
+ - test head/end pointers
- check that all insns are in the basic blocks
(except the switch handling code, barriers and notes)
- check that all returns are followed by barriers
- check that all fallthru edge points to the adjacent blocks. */
+
static int
rtl_verify_flow_info (void)
{
basic_block bb;
int err = rtl_verify_flow_info_1 ();
rtx x;
+ rtx last_head = get_last_insn ();
+ basic_block *bb_info;
int num_bb_notes;
const rtx rtx_first = get_insns ();
basic_block last_bb_seen = ENTRY_BLOCK_PTR, curr_bb = NULL;
+ const int max_uid = get_max_uid ();
+
+ bb_info = XCNEWVEC (basic_block, max_uid);
FOR_EACH_BB_REVERSE (bb)
{
edge e;
edge_iterator ei;
+ rtx head = BB_HEAD (bb);
+ rtx end = BB_END (bb);
+
+ for (x = last_head; x != NULL_RTX; x = PREV_INSN (x))
+ {
+ /* Verify the end of the basic block is in the INSN chain. */
+ if (x == end)
+ break;
+
+ /* And that the code outside of basic blocks has NULL bb field. */
+ if (!BARRIER_P (x)
+ && BLOCK_FOR_INSN (x) != NULL)
+ {
+ error ("insn %d outside of basic blocks has non-NULL bb field",
+ INSN_UID (x));
+ err = 1;
+ }
+ }
- if (bb->predictions)
+ if (!x)
+ {
+ error ("end insn %d for block %d not found in the insn stream",
+ INSN_UID (end), bb->index);
+ err = 1;
+ }
+
+ /* Work backwards from the end to the head of the basic block
+ to verify the head is in the RTL chain. */
+ for (; x != NULL_RTX; x = PREV_INSN (x))
+ {
+ /* While walking over the insn chain, verify insns appear
+ in only one basic block. */
+ if (bb_info[INSN_UID (x)] != NULL)
+ {
+ error ("insn %d is in multiple basic blocks (%d and %d)",
+ INSN_UID (x), bb->index, bb_info[INSN_UID (x)]->index);
+ err = 1;
+ }
+
+ bb_info[INSN_UID (x)] = bb;
+
+ if (x == head)
+ break;
+ }
+ if (!x)
{
- error ("bb prediction set for block %i, but it is not used in RTL land", bb->index);
+ error ("head insn %d for block %d not found in the insn stream",
+ INSN_UID (head), bb->index);
err = 1;
}
+ last_head = PREV_INSN (x);
+
FOR_EACH_EDGE (e, ei, bb->succs)
if (e->flags & EDGE_FALLTHRU)
break;
for (insn = BB_END (bb); !insn || !BARRIER_P (insn);
insn = NEXT_INSN (insn))
if (!insn
- || (NOTE_P (insn)
- && NOTE_LINE_NUMBER (insn) == NOTE_INSN_BASIC_BLOCK))
+ || NOTE_INSN_BASIC_BLOCK_P (insn))
{
error ("missing barrier after block %i", bb->index);
err = 1;
}
}
+ for (x = last_head; x != NULL_RTX; x = PREV_INSN (x))
+ {
+ /* Check that the code before the first basic block has NULL
+ bb field. */
+ if (!BARRIER_P (x)
+ && BLOCK_FOR_INSN (x) != NULL)
+ {
+ error ("insn %d outside of basic blocks has non-NULL bb field",
+ INSN_UID (x));
+ err = 1;
+ }
+ }
+ free (bb_info);
+
num_bb_notes = 0;
last_bb_seen = ENTRY_BLOCK_PTR;
if (JUMP_P (x)
&& returnjump_p (x) && ! condjump_p (x)
- && ! (NEXT_INSN (x) && BARRIER_P (NEXT_INSN (x))))
+ && ! (next_nonnote_insn (x) && BARRIER_P (next_nonnote_insn (x))))
fatal_insn ("return not followed by barrier", x);
if (curr_bb && x == BB_END (curr_bb))
curr_bb = NULL;
}
remove_edge (e);
- bb->flags |= BB_DIRTY;
+ df_set_bb_dirty (bb);
purged = true;
}
}
/* We do not need this edge. */
- bb->flags |= BB_DIRTY;
+ df_set_bb_dirty (bb);
purged = true;
remove_edge (e);
}
{
if (!(e->flags & (EDGE_FALLTHRU | EDGE_FAKE)))
{
- bb->flags |= BB_DIRTY;
+ df_set_bb_dirty (bb);
remove_edge (e);
purged = true;
}
static basic_block
cfg_layout_split_block (basic_block bb, void *insnp)
{
- rtx insn = insnp;
+ rtx insn = (rtx) insnp;
basic_block new_bb = rtl_split_block (bb, insn);
new_bb->il.rtl->footer = bb->il.rtl->footer;
return new_bb;
}
-
/* Redirect Edge to DEST. */
static edge
cfg_layout_redirect_edge_and_branch (edge e, basic_block dest)
if (e->src != ENTRY_BLOCK_PTR
&& (ret = try_redirect_by_replacing_jump (e, dest, true)))
{
- src->flags |= BB_DIRTY;
+ df_set_bb_dirty (src);
return ret;
}
fprintf (dump_file, "Redirecting entry edge from bb %i to %i\n",
e->src->index, dest->index);
- e->src->flags |= BB_DIRTY;
+ df_set_bb_dirty (e->src);
redirect_edge_succ (e, dest);
return e;
}
redirected = redirect_branch_edge (e, dest);
gcc_assert (redirected);
e->flags |= EDGE_FALLTHRU;
- e->src->flags |= BB_DIRTY;
+ df_set_bb_dirty (e->src);
return e;
}
/* In case we are redirecting fallthru edge to the branch edge
/* We don't want simplejumps in the insn stream during cfglayout. */
gcc_assert (!simplejump_p (BB_END (src)));
- src->flags |= BB_DIRTY;
+ df_set_bb_dirty (src);
return ret;
}
/* Must be simple edge. */
&& !(single_succ_edge (a)->flags & EDGE_COMPLEX)
&& a != ENTRY_BLOCK_PTR && b != EXIT_BLOCK_PTR
- /* If the jump insn has side effects,
- we can't kill the edge. */
+ /* If the jump insn has side effects, we can't kill the edge.
+ When not optimizing, try_redirect_by_replacing_jump will
+ not allow us to redirect an edge by replacing a table jump. */
&& (!JUMP_P (BB_END (a))
- || (reload_completed
+ || ((!optimize || reload_completed)
? simplejump_p (BB_END (a)) : onlyjump_p (BB_END (a)))));
}
gcc_assert (cfg_layout_can_merge_blocks_p (a, b));
#endif
+ if (dump_file)
+ fprintf (dump_file, "merging block %d into block %d\n", b->index, a->index);
+
/* If there was a CODE_LABEL beginning B, delete it. */
if (LABEL_P (BB_HEAD (b)))
{
{
rtx first = BB_END (a), last;
- last = emit_insn_after_noloc (b->il.rtl->header, BB_END (a));
- delete_insn_chain (NEXT_INSN (first), last);
+ last = emit_insn_after_noloc (b->il.rtl->header, BB_END (a), a);
+ delete_insn_chain (NEXT_INSN (first), last, false);
b->il.rtl->header = NULL;
}
{
rtx first = unlink_insn_chain (BB_HEAD (b), BB_END (b));
- emit_insn_after_noloc (first, BB_END (a));
+ emit_insn_after_noloc (first, BB_END (a), a);
/* Skip possible DELETED_LABEL insn. */
if (!NOTE_INSN_BASIC_BLOCK_P (first))
first = NEXT_INSN (first);
for (insn = BB_HEAD (b);
insn != NEXT_INSN (BB_END (b));
insn = NEXT_INSN (insn))
- set_block_for_insn (insn, a);
+ {
+ set_block_for_insn (insn, a);
+ df_insn_change_bb (insn);
+ }
+
insn = BB_HEAD (b);
/* Skip possible DELETED_LABEL insn. */
if (!NOTE_INSN_BASIC_BLOCK_P (insn))
delete_insn (insn);
}
+ df_bb_delete (b->index);
+
/* Possible tablejumps and barriers should appear after the block. */
if (b->il.rtl->footer)
{
}
b->il.rtl->footer = NULL;
}
- a->il.rtl->global_live_at_end = b->il.rtl->global_live_at_end;
if (dump_file)
fprintf (dump_file, "Merged blocks %d and %d.\n",
? NEXT_INSN (BB_END (e->src)) : get_insns (),
NULL_RTX, e->src);
- /* ??? This info is likely going to be out of date very soon, but we must
- create it to avoid getting an ICE later. */
- if (e->dest->il.rtl->global_live_at_start)
- {
- new_bb->il.rtl->global_live_at_start = ALLOC_REG_SET (®_obstack);
- new_bb->il.rtl->global_live_at_end = ALLOC_REG_SET (®_obstack);
- COPY_REG_SET (new_bb->il.rtl->global_live_at_start,
- e->dest->il.rtl->global_live_at_start);
- COPY_REG_SET (new_bb->il.rtl->global_live_at_end,
- e->dest->il.rtl->global_live_at_start);
- }
-
make_edge (new_bb, e->dest, EDGE_FALLTHRU);
redirect_edge_and_branch_force (e, new_bb);
init_rtl_bb_info (basic_block bb)
{
gcc_assert (!bb->il.rtl);
- bb->il.rtl = ggc_alloc_cleared (sizeof (struct rtl_bb_info));
+ bb->il.rtl = GGC_CNEW (struct rtl_bb_info);
}
#endif
/* FIXME: What if something in cc0/jump uses value set in new
insn? */
- new_insn = emit_insn_before_noloc (pat, insn);
+ new_insn = emit_insn_before_noloc (pat, insn, bb);
}
/* Likewise if the last insn is a call, as will happen in the presence
|| NOTE_INSN_BASIC_BLOCK_P (insn))
insn = NEXT_INSN (insn);
- new_insn = emit_insn_before_noloc (pat, insn);
+ new_insn = emit_insn_before_noloc (pat, insn, bb);
}
else
- new_insn = emit_insn_after_noloc (pat, insn);
+ new_insn = emit_insn_after_noloc (pat, insn, bb);
return new_insn;
}
+/* Returns true if it is possible to remove edge E by redirecting
+ it to the destination of the other edge from E->src. */
+
+static bool
+rtl_can_remove_branch_p (edge e)
+{
+ basic_block src = e->src;
+ basic_block target = EDGE_SUCC (src, EDGE_SUCC (src, 0) == e)->dest;
+ rtx insn = BB_END (src), set;
+
+ /* The conditions are taken from try_redirect_by_replacing_jump. */
+ if (target == EXIT_BLOCK_PTR)
+ return false;
+
+ if (e->flags & (EDGE_ABNORMAL_CALL | EDGE_EH))
+ return false;
+
+ if (find_reg_note (insn, REG_CROSSING_JUMP, NULL_RTX)
+ || BB_PARTITION (src) != BB_PARTITION (target))
+ return false;
+
+ if (!onlyjump_p (insn)
+ || tablejump_p (insn, NULL, NULL))
+ return false;
+
+ set = single_set (insn);
+ if (!set || side_effects_p (set))
+ return false;
+
+ return true;
+}
+
/* Implementation of CFG manipulation for linearized RTL. */
struct cfg_hooks rtl_cfg_hooks = {
"rtl",
rtl_create_basic_block,
rtl_redirect_edge_and_branch,
rtl_redirect_edge_and_branch_force,
+ rtl_can_remove_branch_p,
rtl_delete_block,
rtl_split_block,
rtl_move_block_after,
/* We do not want to declare these functions in a header file, since they
should only be used through the cfghooks interface, and we do not want to
move them here since it would require also moving quite a lot of related
- code. */
+ code. They are in cfglayout.c. */
extern bool cfg_layout_can_duplicate_bb_p (basic_block);
extern basic_block cfg_layout_duplicate_bb (basic_block);
cfg_layout_create_basic_block,
cfg_layout_redirect_edge_and_branch,
cfg_layout_redirect_edge_and_branch_force,
+ rtl_can_remove_branch_p,
cfg_layout_delete_block,
cfg_layout_split_block,
rtl_move_block_after,