/* 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, 2006, 2007, 2008, 2009, 2010
- Free Software Foundation, Inc.
+ 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
+ 2011, 2012 Free Software Foundation, Inc.
This file is part of GCC.
insert_insn_on_edge, commit_edge_insertions
- CFG updating after insn simplification
purge_dead_edges, purge_all_dead_edges
+ - CFG fixing after coarse manipulation
+ fixup_abnormal_edges
Functions not supposed for generic use:
- Infrastructure to determine quickly basic block for insn
#include "coretypes.h"
#include "tm.h"
#include "tree.h"
-#include "rtl.h"
#include "hard-reg-set.h"
#include "basic-block.h"
#include "regs.h"
#include "output.h"
#include "function.h"
#include "except.h"
-#include "toplev.h"
+#include "rtl-error.h"
#include "tm_p.h"
#include "obstack.h"
#include "insn-attr.h"
#include "cfglayout.h"
#include "expr.h"
#include "target.h"
+#include "common/common-target.h"
#include "cfgloop.h"
#include "ggc.h"
#include "tree-pass.h"
}
/* Create new basic block consisting of instructions in between HEAD and END
- and place it to the BB chain after block AFTER. END can be NULL in to
- create new empty basic block before HEAD. Both END and HEAD can be NULL to
- create basic block at the end of INSN chain. */
+ and place it to the BB chain after block AFTER. END can be NULL to
+ create a new empty basic block before HEAD. Both END and HEAD can be
+ NULL to create basic block at the end of INSN chain. */
static basic_block
rtl_create_basic_block (void *headp, void *endp, basic_block after)
/* The resource.c machinery uses DF but the CFG isn't guaranteed to be
valid at that point so it would be too late to call df_analyze. */
if (optimize > 0 && flag_delayed_branch)
- df_analyze ();
+ {
+ df_note_add_problem ();
+ df_analyze ();
+ }
#endif
free_bb_for_insn ();
}
\f
+/* Return the NOTE_INSN_BASIC_BLOCK of BB. */
+rtx
+bb_note (basic_block bb)
+{
+ rtx note;
+
+ note = BB_HEAD (bb);
+ if (LABEL_P (note))
+ note = NEXT_INSN (note);
+
+ gcc_assert (NOTE_INSN_BASIC_BLOCK_P (note));
+ return note;
+}
+
/* Return the INSN immediately following the NOTE_INSN_BASIC_BLOCK
note associated with the BLOCK. */
rtx b_head = BB_HEAD (b), b_end = BB_END (b), a_end = BB_END (a);
rtx del_first = NULL_RTX, del_last = NULL_RTX;
rtx b_debug_start = b_end, b_debug_end = b_end;
+ bool forwarder_p = (b->flags & BB_FORWARDER_BLOCK) != 0;
int b_empty = 0;
if (dump_file)
- fprintf (dump_file, "merging block %d into block %d\n", b->index, a->index);
+ fprintf (dump_file, "Merging block %d into block %d...\n", b->index,
+ a->index);
while (DEBUG_INSN_P (b_end))
b_end = PREV_INSN (b_debug_start = b_end);
df_bb_delete (b->index);
BB_END (a) = a_end;
+
+ /* If B was a forwarder block, propagate the locus on the edge. */
+ if (forwarder_p && !EDGE_SUCC (b, 0)->goto_locus)
+ EDGE_SUCC (b, 0)->goto_locus = EDGE_SUCC (a, 0)->goto_locus;
+
+ if (dump_file)
+ fprintf (dump_file, "Merged blocks %d and %d.\n", a->index, b->index);
}
/* When expanding this BB might actually contain multiple
jumps (i.e. not yet split by find_many_sub_basic_blocks).
Redirect all of those that match our label. */
- for (insn = BB_HEAD (src); insn != NEXT_INSN (BB_END (src));
- insn = NEXT_INSN (insn))
+ FOR_BB_INSNS (src, insn)
if (JUMP_P (insn) && !patch_jump_insn (insn, old_label, target))
return NULL;
}
/* Like force_nonfallthru below, but additionally performs redirection
- Used by redirect_edge_and_branch_force. */
+ Used by redirect_edge_and_branch_force. JUMP_LABEL is used only
+ when redirecting to the EXIT_BLOCK, it is either ret_rtx or
+ simple_return_rtx, indicating which kind of returnjump to create.
+ It should be NULL otherwise. */
-static basic_block
-force_nonfallthru_and_redirect (edge e, basic_block target)
+basic_block
+force_nonfallthru_and_redirect (edge e, basic_block target, rtx jump_label)
{
basic_block jump_block, new_bb = NULL, src = e->src;
rtx note;
edge new_edge;
int abnormal_edge_flags = 0;
+ bool asm_goto_edge = false;
int loc;
/* In the case the last instruction is conditional jump to the next
}
}
- if (EDGE_COUNT (e->src->succs) >= 2 || abnormal_edge_flags)
+ /* If e->src ends with asm goto, see if any of the ASM_OPERANDS_LABELs
+ don't point to target label. */
+ if (JUMP_P (BB_END (e->src))
+ && target != EXIT_BLOCK_PTR
+ && e->dest == target
+ && (e->flags & EDGE_FALLTHRU)
+ && (note = extract_asm_operands (PATTERN (BB_END (e->src)))))
{
+ int i, n = ASM_OPERANDS_LABEL_LENGTH (note);
+
+ for (i = 0; i < n; ++i)
+ if (XEXP (ASM_OPERANDS_LABEL (note, i), 0) == BB_HEAD (target))
+ {
+ asm_goto_edge = true;
+ break;
+ }
+ }
+
+ if (EDGE_COUNT (e->src->succs) >= 2 || abnormal_edge_flags || asm_goto_edge)
+ {
+ gcov_type count = e->count;
+ int probability = e->probability;
/* Create the new structures. */
/* If the old block ended with a tablejump, skip its table
note = NEXT_INSN (note);
jump_block = create_basic_block (note, NULL, e->src);
- jump_block->count = e->count;
+ jump_block->count = count;
jump_block->frequency = EDGE_FREQUENCY (e);
jump_block->loop_depth = target->loop_depth;
BB_COPY_PARTITION (jump_block, e->src);
if (flag_reorder_blocks_and_partition
- && targetm.have_named_sections
+ && targetm_common.have_named_sections
&& JUMP_P (BB_END (jump_block))
&& !any_condjump_p (BB_END (jump_block))
&& (EDGE_SUCC (jump_block, 0)->flags & EDGE_CROSSING))
/* Wire edge in. */
new_edge = make_edge (e->src, jump_block, EDGE_FALLTHRU);
- new_edge->probability = e->probability;
- new_edge->count = e->count;
+ new_edge->probability = probability;
+ new_edge->count = count;
/* Redirect old edge. */
redirect_edge_pred (e, jump_block);
e->probability = REG_BR_PROB_BASE;
+ /* If asm goto has any label refs to target's label,
+ add also edge from asm goto bb to target. */
+ if (asm_goto_edge)
+ {
+ new_edge->probability /= 2;
+ new_edge->count /= 2;
+ jump_block->count /= 2;
+ jump_block->frequency /= 2;
+ new_edge = make_edge (new_edge->src, target,
+ e->flags & ~EDGE_FALLTHRU);
+ new_edge->probability = probability - probability / 2;
+ new_edge->count = count - count / 2;
+ }
+
new_bb = jump_block;
}
else
e->flags &= ~EDGE_FALLTHRU;
if (target == EXIT_BLOCK_PTR)
{
+ if (jump_label == ret_rtx)
+ {
#ifdef HAVE_return
- emit_jump_insn_after_setloc (gen_return (), BB_END (jump_block), loc);
+ emit_jump_insn_after_setloc (gen_return (), BB_END (jump_block), loc);
#else
- gcc_unreachable ();
+ gcc_unreachable ();
#endif
+ }
+ else
+ {
+ gcc_assert (jump_label == simple_return_rtx);
+#ifdef HAVE_simple_return
+ emit_jump_insn_after_setloc (gen_simple_return (),
+ BB_END (jump_block), loc);
+#else
+ gcc_unreachable ();
+#endif
+ }
+ set_return_jump_label (BB_END (jump_block));
}
else
{
(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)
+static basic_block
+rtl_force_nonfallthru (edge e)
{
- return force_nonfallthru_and_redirect (e, e->dest);
+ return force_nonfallthru_and_redirect (e, e->dest, NULL_RTX);
}
/* Redirect edge even at the expense of creating new jump insn or
/* In case the edge redirection failed, try to force it to be non-fallthru
and redirect newly created simplejump. */
df_set_bb_dirty (e->src);
- return force_nonfallthru_and_redirect (e, target);
+ return force_nonfallthru_and_redirect (e, target, NULL_RTX);
}
/* The given edge should potentially be a fallthru edge. If that is in
Avoid existence of fallthru predecessors. */
if ((edge_in->flags & EDGE_FALLTHRU) == 0)
{
- edge e;
- edge_iterator ei;
-
- FOR_EACH_EDGE (e, ei, edge_in->dest->preds)
- if (e->flags & EDGE_FALLTHRU)
- break;
+ edge e = find_fallthru_edge (edge_in->dest->preds);
if (e)
force_nonfallthru (e);
before = NULL_RTX;
/* If this is a fall through edge to the exit block, the blocks might be
- not adjacent, and the right place is the after the source. */
- if (edge_in->flags & EDGE_FALLTHRU && edge_in->dest == EXIT_BLOCK_PTR)
+ not adjacent, and the right place is after the source. */
+ if ((edge_in->flags & EDGE_FALLTHRU) && edge_in->dest == EXIT_BLOCK_PTR)
{
before = NEXT_INSN (BB_END (edge_in->src));
bb = create_basic_block (before, NULL, edge_in->src);
commit_one_edge_insertion (edge e)
{
rtx before = NULL_RTX, after = NULL_RTX, insns, tmp, last;
- basic_block bb = NULL;
+ basic_block bb;
/* Pull the insns off the edge now since the edge might go away. */
insns = e->insns.r;
e->insns.r = NULL_RTX;
- if (!before && !after)
- {
- /* Figure out where to put these things. If the destination has
- one predecessor, insert there. Except for the exit block. */
- if (single_pred_p (e->dest) && e->dest != EXIT_BLOCK_PTR)
- {
- bb = e->dest;
-
- /* Get the location correct wrt a code label, and "nice" wrt
- a basic block note, and before everything else. */
- tmp = BB_HEAD (bb);
- if (LABEL_P (tmp))
- tmp = NEXT_INSN (tmp);
- if (NOTE_INSN_BASIC_BLOCK_P (tmp))
- tmp = NEXT_INSN (tmp);
- if (tmp == BB_HEAD (bb))
- before = tmp;
- else if (tmp)
- after = PREV_INSN (tmp);
- else
- after = get_last_insn ();
- }
-
- /* If the source has one successor and the edge is not abnormal,
- insert there. Except for the entry block. */
- else if ((e->flags & EDGE_ABNORMAL) == 0
- && single_succ_p (e->src)
- && e->src != ENTRY_BLOCK_PTR)
- {
- bb = e->src;
+ /* Figure out where to put these insns. If the destination has
+ one predecessor, insert there. Except for the exit block. */
+ if (single_pred_p (e->dest) && e->dest != EXIT_BLOCK_PTR)
+ {
+ bb = e->dest;
+
+ /* Get the location correct wrt a code label, and "nice" wrt
+ a basic block note, and before everything else. */
+ tmp = BB_HEAD (bb);
+ if (LABEL_P (tmp))
+ tmp = NEXT_INSN (tmp);
+ if (NOTE_INSN_BASIC_BLOCK_P (tmp))
+ tmp = NEXT_INSN (tmp);
+ if (tmp == BB_HEAD (bb))
+ before = tmp;
+ else if (tmp)
+ after = PREV_INSN (tmp);
+ else
+ after = get_last_insn ();
+ }
- /* It is possible to have a non-simple jump here. Consider a target
- where some forms of unconditional jumps clobber a register. This
- happens on the fr30 for example.
+ /* If the source has one successor and the edge is not abnormal,
+ insert there. Except for the entry block. */
+ else if ((e->flags & EDGE_ABNORMAL) == 0
+ && single_succ_p (e->src)
+ && e->src != ENTRY_BLOCK_PTR)
+ {
+ bb = e->src;
- We know this block has a single successor, so we can just emit
- the queued insns before the jump. */
- if (JUMP_P (BB_END (bb)))
- before = BB_END (bb);
- else
- {
- /* We'd better be fallthru, or we've lost track of
- what's what. */
- gcc_assert (e->flags & EDGE_FALLTHRU);
+ /* It is possible to have a non-simple jump here. Consider a target
+ where some forms of unconditional jumps clobber a register. This
+ happens on the fr30 for example.
- after = BB_END (bb);
- }
- }
- /* Otherwise we must split the edge. */
+ We know this block has a single successor, so we can just emit
+ the queued insns before the jump. */
+ if (JUMP_P (BB_END (bb)))
+ before = BB_END (bb);
else
{
- bb = split_edge (e);
- after = BB_END (bb);
+ /* We'd better be fallthru, or we've lost track of what's what. */
+ gcc_assert (e->flags & EDGE_FALLTHRU);
- if (flag_reorder_blocks_and_partition
- && targetm.have_named_sections
- && e->src != ENTRY_BLOCK_PTR
- && BB_PARTITION (e->src) == BB_COLD_PARTITION
- && !(e->flags & EDGE_CROSSING)
- && JUMP_P (after)
- && !any_condjump_p (after)
- && (single_succ_edge (bb)->flags & EDGE_CROSSING))
- add_reg_note (after, REG_CROSSING_JUMP, NULL_RTX);
+ after = BB_END (bb);
}
}
- /* Now that we've found the spot, do the insertion. */
+ /* Otherwise we must split the edge. */
+ else
+ {
+ bb = split_edge (e);
+ after = BB_END (bb);
+ if (flag_reorder_blocks_and_partition
+ && targetm_common.have_named_sections
+ && e->src != ENTRY_BLOCK_PTR
+ && BB_PARTITION (e->src) == BB_COLD_PARTITION
+ && !(e->flags & EDGE_CROSSING)
+ && JUMP_P (after)
+ && !any_condjump_p (after)
+ && (single_succ_edge (bb)->flags & EDGE_CROSSING))
+ add_reg_note (after, REG_CROSSING_JUMP, NULL_RTX);
+ }
+
+ /* Now that we've found the spot, do the insertion. */
if (before)
{
emit_insn_before_noloc (insns, before, bb);
}
else
gcc_assert (!JUMP_P (last));
-
- /* Mark the basic block for find_many_sub_basic_blocks. */
- if (current_ir_type () != IR_RTL_CFGLAYOUT)
- bb->aux = &bb->aux;
}
/* Update the CFG for all queued instructions. */
commit_edge_insertions (void)
{
basic_block bb;
- sbitmap blocks;
- bool changed = false;
#ifdef ENABLE_CHECKING
verify_flow_info ();
FOR_EACH_EDGE (e, ei, bb->succs)
if (e->insns.r)
- {
- changed = true;
- commit_one_edge_insertion (e);
- }
+ commit_one_edge_insertion (e);
}
-
- if (!changed)
- return;
-
- /* 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_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
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);
+ if (bb->index != ENTRY_BLOCK && bb->index != EXIT_BLOCK)
+ for (insn = BB_HEAD (bb), last = NEXT_INSN (BB_END (bb)); insn != last;
+ insn = NEXT_INSN (insn))
+ print_rtl_single (outf, insn);
if (df)
{
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)
- {
- 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);
- }
- }
+ bb = start[INSN_UID (tmp_rtx)];
+ if (bb != NULL)
+ dump_bb_info (bb, true, false, dump_flags, ";; ", outf);
if (in_bb_p[INSN_UID (tmp_rtx)] == NOT_IN_BB
&& !NOTE_P (tmp_rtx)
did_output = print_rtl_single (outf, tmp_rtx);
- if ((bb = end[INSN_UID (tmp_rtx)]) != NULL)
- {
- 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);
- }
- }
+ bb = end[INSN_UID (tmp_rtx)];
+ if (bb != NULL)
+ dump_bb_info (bb, false, true, dump_flags, ";; ", outf);
if (did_output)
putc ('\n', outf);
}
}
FOR_EACH_EDGE (e, ei, bb->succs)
{
+ bool is_crossing;
+
if (e->flags & EDGE_FALLTHRU)
+ n_fallthru++, fallthru = e;
+
+ is_crossing = (BB_PARTITION (e->src) != BB_PARTITION (e->dest)
+ && e->src != ENTRY_BLOCK_PTR
+ && e->dest != EXIT_BLOCK_PTR);
+ if (e->flags & EDGE_CROSSING)
{
- n_fallthru++, fallthru = e;
- if ((e->flags & EDGE_CROSSING)
- || (BB_PARTITION (e->src) != BB_PARTITION (e->dest)
- && e->src != ENTRY_BLOCK_PTR
- && e->dest != EXIT_BLOCK_PTR))
- {
+ if (!is_crossing)
+ {
+ error ("EDGE_CROSSING incorrectly set across same section");
+ err = 1;
+ }
+ if (e->flags & EDGE_FALLTHRU)
+ {
error ("fallthru edge crosses section boundary (bb %i)",
e->src->index);
err = 1;
}
+ if (e->flags & EDGE_EH)
+ {
+ error ("EH edge crosses section boundary (bb %i)",
+ e->src->index);
+ err = 1;
+ }
+ }
+ else if (is_crossing)
+ {
+ error ("EDGE_CROSSING missing across section boundary");
+ err = 1;
}
if ((e->flags & ~(EDGE_DFS_BACK
| EDGE_CAN_FALLTHRU
| EDGE_IRREDUCIBLE_LOOP
| EDGE_LOOP_EXIT
- | EDGE_CROSSING)) == 0)
+ | EDGE_CROSSING
+ | EDGE_PRESERVE)) == 0)
n_branch++;
if (e->flags & EDGE_ABNORMAL_CALL)
FOR_EACH_BB_REVERSE (bb)
{
edge e;
- edge_iterator ei;
rtx head = BB_HEAD (bb);
rtx end = BB_END (bb);
last_head = PREV_INSN (x);
- FOR_EACH_EDGE (e, ei, bb->succs)
- if (e->flags & EDGE_FALLTHRU)
- break;
+ e = find_fallthru_edge (bb->succs);
if (!e)
{
rtx insn;
;
else if ((e->flags & EDGE_EH) && can_throw_internal (insn))
;
+ else if (flag_tm && find_reg_note (insn, REG_TM, NULL))
+ ;
else
remove = true;
}
return purged;
}
+/* This is used by a few passes that emit some instructions after abnormal
+ calls, moving the basic block's end, while they in fact do want to emit
+ them on the fallthru edge. Look for abnormal call edges, find backward
+ the call in the block and insert the instructions on the edge instead.
+
+ Similarly, handle instructions throwing exceptions internally.
+
+ Return true when instructions have been found and inserted on edges. */
+
+bool
+fixup_abnormal_edges (void)
+{
+ bool inserted = false;
+ basic_block bb;
+
+ FOR_EACH_BB (bb)
+ {
+ edge e;
+ edge_iterator ei;
+
+ /* Look for cases we are interested in - calls or instructions causing
+ exceptions. */
+ FOR_EACH_EDGE (e, ei, bb->succs)
+ if ((e->flags & EDGE_ABNORMAL_CALL)
+ || ((e->flags & (EDGE_ABNORMAL | EDGE_EH))
+ == (EDGE_ABNORMAL | EDGE_EH)))
+ break;
+
+ if (e && !CALL_P (BB_END (bb)) && !can_throw_internal (BB_END (bb)))
+ {
+ rtx insn;
+
+ /* Get past the new insns generated. Allow notes, as the insns
+ may be already deleted. */
+ insn = BB_END (bb);
+ while ((NONJUMP_INSN_P (insn) || NOTE_P (insn))
+ && !can_throw_internal (insn)
+ && insn != BB_HEAD (bb))
+ insn = PREV_INSN (insn);
+
+ if (CALL_P (insn) || can_throw_internal (insn))
+ {
+ rtx stop, next;
+
+ e = find_fallthru_edge (bb->succs);
+
+ stop = NEXT_INSN (BB_END (bb));
+ BB_END (bb) = insn;
+
+ for (insn = NEXT_INSN (insn); insn != stop; insn = next)
+ {
+ next = NEXT_INSN (insn);
+ if (INSN_P (insn))
+ {
+ delete_insn (insn);
+
+ /* Sometimes there's still the return value USE.
+ If it's placed after a trapping call (i.e. that
+ call is the last insn anyway), we have no fallthru
+ edge. Simply delete this use and don't try to insert
+ on the non-existent edge. */
+ if (GET_CODE (PATTERN (insn)) != USE)
+ {
+ /* We're not deleting it, we're moving it. */
+ INSN_DELETED_P (insn) = 0;
+ PREV_INSN (insn) = NULL_RTX;
+ NEXT_INSN (insn) = NULL_RTX;
+
+ insert_insn_on_edge (insn, e);
+ inserted = true;
+ }
+ }
+ else if (!BARRIER_P (insn))
+ set_block_for_insn (insn, NULL);
+ }
+ }
+
+ /* It may be that we don't find any trapping insn. In this
+ case we discovered quite late that the insn that had been
+ marked as can_throw_internal in fact couldn't trap at all.
+ So we should in fact delete the EH edges out of the block. */
+ else
+ purge_dead_edges (bb);
+ }
+ }
+
+ return inserted;
+}
+
/* Same as split_block but update cfg_layout structures. */
static basic_block
e->flags &= ~EDGE_FALLTHRU;
redirected = redirect_branch_edge (e, dest);
gcc_assert (redirected);
- e->flags |= EDGE_FALLTHRU;
- df_set_bb_dirty (e->src);
- return e;
+ redirected->flags |= EDGE_FALLTHRU;
+ df_set_bb_dirty (redirected->src);
+ return redirected;
}
/* In case we are redirecting fallthru edge to the branch edge
of conditional jump, remove it. */
&& onlyjump_p (BB_END (src)))
delete_insn (BB_END (src));
}
- ret = redirect_edge_succ_nodup (e, dest);
if (dump_file)
- fprintf (dump_file, "Fallthru edge %i->%i redirected to %i\n",
+ fprintf (dump_file, "Redirecting fallthru edge %i->%i to %i\n",
e->src->index, e->dest->index, dest->index);
+ ret = redirect_edge_succ_nodup (e, dest);
}
else
ret = redirect_branch_edge (e, dest);
if (BB_PARTITION (a) != BB_PARTITION (b))
return false;
+ /* If we would end up moving B's instructions, make sure it doesn't fall
+ through into the exit block, since we cannot recover from a fallthrough
+ edge into the exit block occurring in the middle of a function. */
+ if (NEXT_INSN (BB_END (a)) != BB_HEAD (b))
+ {
+ edge e = find_fallthru_edge (b->succs);
+ if (e && e->dest == EXIT_BLOCK_PTR)
+ return false;
+ }
+
/* There must be exactly one edge in between the blocks. */
return (single_succ_p (a)
&& single_succ (a) == b
static void
cfg_layout_merge_blocks (basic_block a, basic_block b)
{
-#ifdef ENABLE_CHECKING
- gcc_assert (cfg_layout_can_merge_blocks_p (a, b));
-#endif
+ bool forwarder_p = (b->flags & BB_FORWARDER_BLOCK) != 0;
+
+ gcc_checking_assert (cfg_layout_can_merge_blocks_p (a, b));
if (dump_file)
- fprintf (dump_file, "merging block %d into block %d\n", b->index, a->index);
+ 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), a);
+ /* The above might add a BARRIER as BB_END, but as barriers
+ aren't valid parts of a bb, remove_insn doesn't update
+ BB_END if it is a barrier. So adjust BB_END here. */
+ while (BB_END (a) != first && BARRIER_P (BB_END (a)))
+ BB_END (a) = PREV_INSN (BB_END (a));
delete_insn_chain (NEXT_INSN (first), last, false);
b->il.rtl->header = NULL;
}
b->il.rtl->footer = NULL;
}
+ /* If B was a forwarder block, propagate the locus on the edge. */
+ if (forwarder_p && !EDGE_SUCC (b, 0)->goto_locus)
+ EDGE_SUCC (b, 0)->goto_locus = EDGE_SUCC (a, 0)->goto_locus;
+
if (dump_file)
- fprintf (dump_file, "Merged blocks %d and %d.\n",
- a->index, b->index);
+ fprintf (dump_file, "Merged blocks %d and %d.\n", a->index, b->index);
}
/* Split edge E. */
init_rtl_bb_info (basic_block bb)
{
gcc_assert (!bb->il.rtl);
- bb->il.rtl = GGC_CNEW (struct rtl_bb_info);
-}
-
-
-/* Add EXPR to the end of basic block BB. */
-
-rtx
-insert_insn_end_bb_new (rtx pat, basic_block bb)
-{
- rtx insn = BB_END (bb);
- rtx new_insn;
- rtx pat_end = pat;
-
- while (NEXT_INSN (pat_end) != NULL_RTX)
- pat_end = NEXT_INSN (pat_end);
-
- /* If the last insn is a jump, insert EXPR in front [taking care to
- handle cc0, etc. properly]. Similarly we need to care trapping
- instructions in presence of non-call exceptions. */
-
- if (JUMP_P (insn)
- || (NONJUMP_INSN_P (insn)
- && (!single_succ_p (bb)
- || single_succ_edge (bb)->flags & EDGE_ABNORMAL)))
- {
-#ifdef HAVE_cc0
- rtx note;
-#endif
- /* If this is a jump table, then we can't insert stuff here. Since
- we know the previous real insn must be the tablejump, we insert
- the new instruction just before the tablejump. */
- if (GET_CODE (PATTERN (insn)) == ADDR_VEC
- || GET_CODE (PATTERN (insn)) == ADDR_DIFF_VEC)
- insn = prev_real_insn (insn);
-
-#ifdef HAVE_cc0
- /* FIXME: 'twould be nice to call prev_cc0_setter here but it aborts
- if cc0 isn't set. */
- note = find_reg_note (insn, REG_CC_SETTER, NULL_RTX);
- if (note)
- insn = XEXP (note, 0);
- else
- {
- rtx maybe_cc0_setter = prev_nonnote_insn (insn);
- if (maybe_cc0_setter
- && INSN_P (maybe_cc0_setter)
- && sets_cc0_p (PATTERN (maybe_cc0_setter)))
- insn = maybe_cc0_setter;
- }
-#endif
- /* FIXME: What if something in cc0/jump uses value set in new
- insn? */
- new_insn = emit_insn_before_noloc (pat, insn, bb);
- }
-
- /* Likewise if the last insn is a call, as will happen in the presence
- of exception handling. */
- else if (CALL_P (insn)
- && (!single_succ_p (bb)
- || single_succ_edge (bb)->flags & EDGE_ABNORMAL))
- {
- /* Keeping in mind targets with small register classes and parameters
- in registers, we search backward and place the instructions before
- the first parameter is loaded. Do this for everyone for consistency
- and a presumption that we'll get better code elsewhere as well. */
-
- /* Since different machines initialize their parameter registers
- in different orders, assume nothing. Collect the set of all
- parameter registers. */
- insn = find_first_parameter_load (insn, BB_HEAD (bb));
-
- /* If we found all the parameter loads, then we want to insert
- before the first parameter load.
-
- If we did not find all the parameter loads, then we might have
- stopped on the head of the block, which could be a CODE_LABEL.
- If we inserted before the CODE_LABEL, then we would be putting
- the insn in the wrong basic block. In that case, put the insn
- after the CODE_LABEL. Also, respect NOTE_INSN_BASIC_BLOCK. */
- while (LABEL_P (insn)
- || NOTE_INSN_BASIC_BLOCK_P (insn))
- insn = NEXT_INSN (insn);
-
- new_insn = emit_insn_before_noloc (pat, insn, bb);
- }
- else
- new_insn = emit_insn_after_noloc (pat, insn, bb);
-
- return new_insn;
+ bb->il.rtl = ggc_alloc_cleared_rtl_bb_info ();
}
/* Returns true if it is possible to remove edge E by redirecting
return true;
}
+/* 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. They are in cfglayout.c. */
+extern bool cfg_layout_can_duplicate_bb_p (const_basic_block);
+extern basic_block cfg_layout_duplicate_bb (basic_block);
+
+static basic_block
+rtl_duplicate_bb (basic_block bb)
+{
+ bb = cfg_layout_duplicate_bb (bb);
+ bb->aux = NULL;
+ return bb;
+}
+
/* Implementation of CFG manipulation for linearized RTL. */
struct cfg_hooks rtl_cfg_hooks = {
"rtl",
rtl_merge_blocks,
rtl_predict_edge,
rtl_predicted_by_p,
- NULL, /* can_duplicate_block_p */
- NULL, /* duplicate_block */
+ cfg_layout_can_duplicate_bb_p,
+ rtl_duplicate_bb,
rtl_split_edge,
rtl_make_forwarder_block,
rtl_tidy_fallthru_edge,
+ rtl_force_nonfallthru,
rtl_block_ends_with_call_p,
rtl_block_ends_with_condjump_p,
rtl_flow_call_edges_add,
This representation will hopefully become the default one in future
version of the compiler. */
-/* 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. They are in cfglayout.c. */
-extern bool cfg_layout_can_duplicate_bb_p (const_basic_block);
-extern basic_block cfg_layout_duplicate_bb (basic_block);
-
struct cfg_hooks cfg_layout_rtl_cfg_hooks = {
"cfglayout mode",
rtl_verify_flow_info_1,
cfg_layout_duplicate_bb,
cfg_layout_split_edge,
rtl_make_forwarder_block,
- NULL,
+ NULL, /* tidy_fallthru_edge */
+ rtl_force_nonfallthru,
rtl_block_ends_with_call_p,
rtl_block_ends_with_condjump_p,
rtl_flow_call_edges_add,