+
+/* Same as split_block but update cfg_layout structures. */
+
+static basic_block
+cfg_layout_split_block (basic_block bb, void *insnp)
+{
+ rtx insn = insnp;
+ basic_block new_bb = rtl_split_block (bb, insn);
+
+ new_bb->rbi->footer = bb->rbi->footer;
+ bb->rbi->footer = NULL;
+
+ return new_bb;
+}
+
+
+/* Redirect Edge to DEST. */
+static edge
+cfg_layout_redirect_edge_and_branch (edge e, basic_block dest)
+{
+ basic_block src = e->src;
+ edge ret;
+
+ if (e->flags & (EDGE_ABNORMAL_CALL | EDGE_EH))
+ return NULL;
+
+ if (e->dest == dest)
+ return e;
+
+ if (e->src != ENTRY_BLOCK_PTR
+ && (ret = try_redirect_by_replacing_jump (e, dest, true)))
+ {
+ src->flags |= BB_DIRTY;
+ return ret;
+ }
+
+ if (e->src == ENTRY_BLOCK_PTR
+ && (e->flags & EDGE_FALLTHRU) && !(e->flags & EDGE_COMPLEX))
+ {
+ if (dump_file)
+ fprintf (dump_file, "Redirecting entry edge from bb %i to %i\n",
+ e->src->index, dest->index);
+
+ e->src->flags |= BB_DIRTY;
+ redirect_edge_succ (e, dest);
+ return e;
+ }
+
+ /* Redirect_edge_and_branch may decide to turn branch into fallthru edge
+ in the case the basic block appears to be in sequence. Avoid this
+ transformation. */
+
+ if (e->flags & EDGE_FALLTHRU)
+ {
+ /* Redirect any branch edges unified with the fallthru one. */
+ if (JUMP_P (BB_END (src))
+ && label_is_jump_target_p (BB_HEAD (e->dest),
+ BB_END (src)))
+ {
+ edge redirected;
+
+ if (dump_file)
+ fprintf (dump_file, "Fallthru edge unified with branch "
+ "%i->%i redirected to %i\n",
+ e->src->index, e->dest->index, dest->index);
+ e->flags &= ~EDGE_FALLTHRU;
+ redirected = redirect_branch_edge (e, dest);
+ gcc_assert (redirected);
+ e->flags |= EDGE_FALLTHRU;
+ e->src->flags |= BB_DIRTY;
+ return e;
+ }
+ /* In case we are redirecting fallthru edge to the branch edge
+ of conditional jump, remove it. */
+ if (EDGE_COUNT (src->succs) == 2)
+ {
+ bool found = false;
+ unsigned ix = 0;
+ edge tmp, s;
+ edge_iterator ei;
+
+ FOR_EACH_EDGE (tmp, ei, src->succs)
+ if (e == tmp)
+ {
+ found = true;
+ ix = ei.index;
+ break;
+ }
+
+ gcc_assert (found);
+
+ if (EDGE_COUNT (src->succs) > (ix + 1))
+ s = EDGE_SUCC (src, ix + 1);
+ else
+ s = EDGE_SUCC (src, 0);
+
+ if (s->dest == dest
+ && any_condjump_p (BB_END (src))
+ && 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",
+ e->src->index, e->dest->index, dest->index);
+ }
+ else
+ ret = redirect_branch_edge (e, dest);
+
+ /* We don't want simplejumps in the insn stream during cfglayout. */
+ gcc_assert (!simplejump_p (BB_END (src)));
+
+ src->flags |= BB_DIRTY;
+ return ret;
+}
+
+/* Simple wrapper as we always can redirect fallthru edges. */
+static basic_block
+cfg_layout_redirect_edge_and_branch_force (edge e, basic_block dest)
+{
+ edge redirected = cfg_layout_redirect_edge_and_branch (e, dest);
+
+ gcc_assert (redirected);
+ return NULL;
+}
+
+/* Same as delete_basic_block but update cfg_layout structures. */
+
+static void
+cfg_layout_delete_block (basic_block bb)
+{
+ rtx insn, next, prev = PREV_INSN (BB_HEAD (bb)), *to, remaints;
+
+ if (bb->rbi->header)
+ {
+ next = BB_HEAD (bb);
+ if (prev)
+ NEXT_INSN (prev) = bb->rbi->header;
+ else
+ set_first_insn (bb->rbi->header);
+ PREV_INSN (bb->rbi->header) = prev;
+ insn = bb->rbi->header;
+ while (NEXT_INSN (insn))
+ insn = NEXT_INSN (insn);
+ NEXT_INSN (insn) = next;
+ PREV_INSN (next) = insn;
+ }
+ next = NEXT_INSN (BB_END (bb));
+ if (bb->rbi->footer)
+ {
+ insn = bb->rbi->footer;
+ while (insn)
+ {
+ if (BARRIER_P (insn))
+ {
+ if (PREV_INSN (insn))
+ NEXT_INSN (PREV_INSN (insn)) = NEXT_INSN (insn);
+ else
+ bb->rbi->footer = NEXT_INSN (insn);
+ if (NEXT_INSN (insn))
+ PREV_INSN (NEXT_INSN (insn)) = PREV_INSN (insn);
+ }
+ if (LABEL_P (insn))
+ break;
+ insn = NEXT_INSN (insn);
+ }
+ if (bb->rbi->footer)
+ {
+ insn = BB_END (bb);
+ NEXT_INSN (insn) = bb->rbi->footer;
+ PREV_INSN (bb->rbi->footer) = insn;
+ while (NEXT_INSN (insn))
+ insn = NEXT_INSN (insn);
+ NEXT_INSN (insn) = next;
+ if (next)
+ PREV_INSN (next) = insn;
+ else
+ set_last_insn (insn);
+ }
+ }
+ if (bb->next_bb != EXIT_BLOCK_PTR)
+ to = &bb->next_bb->rbi->header;
+ else
+ to = &cfg_layout_function_footer;
+ rtl_delete_block (bb);
+
+ if (prev)
+ prev = NEXT_INSN (prev);
+ else
+ prev = get_insns ();
+ if (next)
+ next = PREV_INSN (next);
+ else
+ next = get_last_insn ();
+
+ if (next && NEXT_INSN (next) != prev)
+ {
+ remaints = unlink_insn_chain (prev, next);
+ insn = remaints;
+ while (NEXT_INSN (insn))
+ insn = NEXT_INSN (insn);
+ NEXT_INSN (insn) = *to;
+ if (*to)
+ PREV_INSN (*to) = insn;
+ *to = remaints;
+ }
+}
+
+/* Return true when blocks A and B can be safely merged. */
+static bool
+cfg_layout_can_merge_blocks_p (basic_block a, basic_block b)
+{
+ /* If we are partitioning hot/cold basic blocks, we don't want to
+ mess up unconditional or indirect jumps that cross between hot
+ and cold sections.
+
+ Basic block partitioning may result in some jumps that appear to
+ be optimizable (or blocks that appear to be mergeable), but which really
+ must be left untouched (they are required to make it safely across
+ partition boundaries). See the comments at the top of
+ bb-reorder.c:partition_hot_cold_basic_blocks for complete details. */
+
+ if (flag_reorder_blocks_and_partition
+ && (find_reg_note (BB_END (a), REG_CROSSING_JUMP, NULL_RTX)
+ || find_reg_note (BB_END (b), REG_CROSSING_JUMP, NULL_RTX)
+ || BB_PARTITION (a) != BB_PARTITION (b)))
+ return false;
+
+ /* There must be exactly one edge in between the blocks. */
+ return (EDGE_COUNT (a->succs) == 1
+ && EDGE_SUCC (a, 0)->dest == b
+ && EDGE_COUNT (b->preds) == 1
+ && a != b
+ /* Must be simple edge. */
+ && !(EDGE_SUCC (a, 0)->flags & EDGE_COMPLEX)
+ && a != ENTRY_BLOCK_PTR && b != EXIT_BLOCK_PTR
+ /* If the jump insn has side effects,
+ we can't kill the edge. */
+ && (!JUMP_P (BB_END (a))
+ || (reload_completed
+ ? simplejump_p (BB_END (a)) : onlyjump_p (BB_END (a)))));
+}
+
+/* Merge block A and B, abort when it is not possible. */
+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
+
+ /* If there was a CODE_LABEL beginning B, delete it. */
+ if (LABEL_P (BB_HEAD (b)))
+ delete_insn (BB_HEAD (b));
+
+ /* We should have fallthru edge in a, or we can do dummy redirection to get
+ it cleaned up. */
+ if (JUMP_P (BB_END (a)))
+ try_redirect_by_replacing_jump (EDGE_SUCC (a, 0), b, true);
+ gcc_assert (!JUMP_P (BB_END (a)));
+
+ /* Possible line number notes should appear in between. */
+ if (b->rbi->header)
+ {
+ rtx first = BB_END (a), last;
+
+ last = emit_insn_after_noloc (b->rbi->header, BB_END (a));
+ delete_insn_chain (NEXT_INSN (first), last);
+ b->rbi->header = NULL;
+ }
+
+ /* In the case basic blocks are not adjacent, move them around. */
+ if (NEXT_INSN (BB_END (a)) != BB_HEAD (b))
+ {
+ rtx first = unlink_insn_chain (BB_HEAD (b), BB_END (b));
+
+ emit_insn_after_noloc (first, BB_END (a));
+ /* Skip possible DELETED_LABEL insn. */
+ if (!NOTE_INSN_BASIC_BLOCK_P (first))
+ first = NEXT_INSN (first);
+ gcc_assert (NOTE_INSN_BASIC_BLOCK_P (first));
+ BB_HEAD (b) = NULL;
+ delete_insn (first);
+ }
+ /* Otherwise just re-associate the instructions. */
+ else
+ {
+ rtx insn;
+
+ for (insn = BB_HEAD (b);
+ insn != NEXT_INSN (BB_END (b));
+ insn = NEXT_INSN (insn))
+ set_block_for_insn (insn, a);
+ insn = BB_HEAD (b);
+ /* Skip possible DELETED_LABEL insn. */
+ if (!NOTE_INSN_BASIC_BLOCK_P (insn))
+ insn = NEXT_INSN (insn);
+ gcc_assert (NOTE_INSN_BASIC_BLOCK_P (insn));
+ BB_HEAD (b) = NULL;
+ BB_END (a) = BB_END (b);
+ delete_insn (insn);
+ }
+
+ /* Possible tablejumps and barriers should appear after the block. */
+ if (b->rbi->footer)
+ {
+ if (!a->rbi->footer)
+ a->rbi->footer = b->rbi->footer;
+ else
+ {
+ rtx last = a->rbi->footer;
+
+ while (NEXT_INSN (last))
+ last = NEXT_INSN (last);
+ NEXT_INSN (last) = b->rbi->footer;
+ PREV_INSN (b->rbi->footer) = last;
+ }
+ b->rbi->footer = NULL;
+ }
+
+ if (dump_file)
+ fprintf (dump_file, "Merged blocks %d and %d.\n",
+ a->index, b->index);
+}
+
+/* Split edge E. */
+
+static basic_block
+cfg_layout_split_edge (edge e)
+{
+ edge new_e;
+ basic_block new_bb =
+ create_basic_block (e->src != ENTRY_BLOCK_PTR
+ ? 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->global_live_at_start)
+ {
+ new_bb->global_live_at_start = OBSTACK_ALLOC_REG_SET (&flow_obstack);
+ new_bb->global_live_at_end = OBSTACK_ALLOC_REG_SET (&flow_obstack);
+ COPY_REG_SET (new_bb->global_live_at_start,
+ e->dest->global_live_at_start);
+ COPY_REG_SET (new_bb->global_live_at_end,
+ e->dest->global_live_at_start);
+ }
+
+ new_e = make_edge (new_bb, e->dest, EDGE_FALLTHRU);
+ redirect_edge_and_branch_force (e, new_bb);
+
+ return new_bb;
+}
+
+/* Do postprocessing after making a forwarder block joined by edge FALLTHRU. */
+
+static void
+rtl_make_forwarder_block (edge fallthru ATTRIBUTE_UNUSED)
+{
+}
+
+/* Return 1 if BB ends with a call, possibly followed by some
+ instructions that must stay with the call, 0 otherwise. */
+
+static bool
+rtl_block_ends_with_call_p (basic_block bb)
+{
+ rtx insn = BB_END (bb);
+
+ while (!CALL_P (insn)
+ && insn != BB_HEAD (bb)
+ && keep_with_call_p (insn))
+ insn = PREV_INSN (insn);
+ return (CALL_P (insn));
+}
+
+/* Return 1 if BB ends with a conditional branch, 0 otherwise. */
+
+static bool
+rtl_block_ends_with_condjump_p (basic_block bb)
+{
+ return any_condjump_p (BB_END (bb));
+}
+
+/* Return true if we need to add fake edge to exit.
+ Helper function for rtl_flow_call_edges_add. */
+
+static bool
+need_fake_edge_p (rtx insn)
+{
+ if (!INSN_P (insn))
+ return false;
+
+ if ((CALL_P (insn)
+ && !SIBLING_CALL_P (insn)
+ && !find_reg_note (insn, REG_NORETURN, NULL)
+ && !find_reg_note (insn, REG_ALWAYS_RETURN, NULL)
+ && !CONST_OR_PURE_CALL_P (insn)))
+ return true;
+
+ return ((GET_CODE (PATTERN (insn)) == ASM_OPERANDS
+ && MEM_VOLATILE_P (PATTERN (insn)))
+ || (GET_CODE (PATTERN (insn)) == PARALLEL
+ && asm_noperands (insn) != -1
+ && MEM_VOLATILE_P (XVECEXP (PATTERN (insn), 0, 0)))
+ || GET_CODE (PATTERN (insn)) == ASM_INPUT);
+}
+
+/* Add fake edges to the function exit for any non constant and non noreturn
+ calls, volatile inline assembly in the bitmap of blocks specified by
+ BLOCKS or to the whole CFG if BLOCKS is zero. Return the number of blocks
+ that were split.
+
+ The goal is to expose cases in which entering a basic block does not imply
+ that all subsequent instructions must be executed. */
+
+static int
+rtl_flow_call_edges_add (sbitmap blocks)
+{
+ int i;
+ int blocks_split = 0;
+ int last_bb = last_basic_block;
+ bool check_last_block = false;
+
+ if (n_basic_blocks == 0)
+ return 0;
+
+ if (! blocks)
+ check_last_block = true;
+ else
+ check_last_block = TEST_BIT (blocks, EXIT_BLOCK_PTR->prev_bb->index);
+
+ /* In the last basic block, before epilogue generation, there will be
+ a fallthru edge to EXIT. Special care is required if the last insn
+ of the last basic block is a call because make_edge folds duplicate
+ edges, which would result in the fallthru edge also being marked
+ fake, which would result in the fallthru edge being removed by
+ remove_fake_edges, which would result in an invalid CFG.
+
+ Moreover, we can't elide the outgoing fake edge, since the block
+ profiler needs to take this into account in order to solve the minimal
+ spanning tree in the case that the call doesn't return.
+
+ Handle this by adding a dummy instruction in a new last basic block. */
+ if (check_last_block)
+ {
+ basic_block bb = EXIT_BLOCK_PTR->prev_bb;
+ rtx insn = BB_END (bb);
+
+ /* Back up past insns that must be kept in the same block as a call. */
+ while (insn != BB_HEAD (bb)
+ && keep_with_call_p (insn))
+ insn = PREV_INSN (insn);
+
+ if (need_fake_edge_p (insn))
+ {
+ edge e;
+ edge_iterator ei;
+
+ FOR_EACH_EDGE (e, ei, bb->succs)
+ if (e->dest == EXIT_BLOCK_PTR)
+ {
+ insert_insn_on_edge (gen_rtx_USE (VOIDmode, const0_rtx), e);
+ commit_edge_insertions ();
+ break;
+ }
+ }
+ }
+
+ /* Now add fake edges to the function exit for any non constant
+ calls since there is no way that we can determine if they will
+ return or not... */
+
+ for (i = 0; i < last_bb; i++)
+ {
+ basic_block bb = BASIC_BLOCK (i);
+ rtx insn;
+ rtx prev_insn;
+
+ if (!bb)
+ continue;
+
+ if (blocks && !TEST_BIT (blocks, i))
+ continue;
+
+ for (insn = BB_END (bb); ; insn = prev_insn)
+ {
+ prev_insn = PREV_INSN (insn);
+ if (need_fake_edge_p (insn))
+ {
+ edge e;
+ rtx split_at_insn = insn;
+
+ /* Don't split the block between a call and an insn that should
+ remain in the same block as the call. */
+ if (CALL_P (insn))
+ while (split_at_insn != BB_END (bb)
+ && keep_with_call_p (NEXT_INSN (split_at_insn)))
+ split_at_insn = NEXT_INSN (split_at_insn);
+
+ /* The handling above of the final block before the epilogue
+ should be enough to verify that there is no edge to the exit
+ block in CFG already. Calling make_edge in such case would
+ cause us to mark that edge as fake and remove it later. */
+
+#ifdef ENABLE_CHECKING
+ if (split_at_insn == BB_END (bb))
+ {
+ edge_iterator ei;
+ FOR_EACH_EDGE (e, ei, bb->succs)
+ gcc_assert (e->dest != EXIT_BLOCK_PTR);
+ }
+#endif
+
+ /* Note that the following may create a new basic block
+ and renumber the existing basic blocks. */
+ if (split_at_insn != BB_END (bb))
+ {
+ e = split_block (bb, split_at_insn);
+ if (e)
+ blocks_split++;
+ }
+
+ make_edge (bb, EXIT_BLOCK_PTR, EDGE_FAKE);
+ }
+
+ if (insn == BB_HEAD (bb))
+ break;
+ }
+ }
+
+ if (blocks_split)
+ verify_flow_info ();
+
+ return blocks_split;
+}
+
+/* Implementation of CFG manipulation for linearized RTL. */
+struct cfg_hooks rtl_cfg_hooks = {
+ "rtl",
+ rtl_verify_flow_info,
+ rtl_dump_bb,
+ rtl_create_basic_block,
+ rtl_redirect_edge_and_branch,
+ rtl_redirect_edge_and_branch_force,
+ rtl_delete_block,
+ rtl_split_block,
+ rtl_move_block_after,
+ rtl_can_merge_blocks, /* can_merge_blocks_p */
+ rtl_merge_blocks,
+ rtl_predict_edge,
+ rtl_predicted_by_p,
+ NULL, /* can_duplicate_block_p */
+ NULL, /* duplicate_block */
+ rtl_split_edge,
+ rtl_make_forwarder_block,
+ rtl_tidy_fallthru_edge,
+ rtl_block_ends_with_call_p,
+ rtl_block_ends_with_condjump_p,
+ rtl_flow_call_edges_add
+};
+
+/* Implementation of CFG manipulation for cfg layout RTL, where
+ basic block connected via fallthru edges does not have to be adjacent.
+ 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. */
+extern bool cfg_layout_can_duplicate_bb_p (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,
+ rtl_dump_bb,
+ cfg_layout_create_basic_block,
+ cfg_layout_redirect_edge_and_branch,
+ cfg_layout_redirect_edge_and_branch_force,
+ cfg_layout_delete_block,
+ cfg_layout_split_block,
+ rtl_move_block_after,
+ cfg_layout_can_merge_blocks_p,
+ cfg_layout_merge_blocks,
+ rtl_predict_edge,
+ rtl_predicted_by_p,
+ cfg_layout_can_duplicate_bb_p,
+ cfg_layout_duplicate_bb,
+ cfg_layout_split_edge,
+ rtl_make_forwarder_block,
+ NULL,
+ rtl_block_ends_with_call_p,
+ rtl_block_ends_with_condjump_p,
+ rtl_flow_call_edges_add
+};
+