/* Control flow graph manipulation code for GNU compiler.
- Copyright (C) 1987-2018 Free Software Foundation, Inc.
+ Copyright (C) 1987-2020 Free Software Foundation, Inc.
This file is part of GCC.
#include "tree-pass.h"
#include "print-rtl.h"
+/* Disable warnings about missing quoting in GCC diagnostics. */
+#if __GNUC__ >= 10
+# pragma GCC diagnostic push
+# pragma GCC diagnostic ignored "-Wformat-diag"
+#endif
+
/* Holds the interesting leading and trailing notes for the function.
Only applicable if the CFG is in cfglayout mode. */
static GTY(()) rtx_insn *cfg_layout_function_footer;
{
bool purge = false;
- if (INSN_P (insn)
- && BLOCK_FOR_INSN (insn)
- && BB_END (BLOCK_FOR_INSN (insn)) == insn)
- purge = true;
+ if (INSN_P (insn) && BLOCK_FOR_INSN (insn))
+ {
+ basic_block bb = BLOCK_FOR_INSN (insn);
+ if (BB_END (bb) == insn)
+ purge = true;
+ else if (DEBUG_INSN_P (BB_END (bb)))
+ for (rtx_insn *dinsn = NEXT_INSN (insn);
+ DEBUG_INSN_P (dinsn); dinsn = NEXT_INSN (dinsn))
+ if (BB_END (bb) == dinsn)
+ {
+ purge = true;
+ break;
+ }
+ }
delete_insn (insn);
if (purge)
return purge_dead_edges (BLOCK_FOR_INSN (insn));
}
\f
-/* Like active_insn_p, except keep the return value clobber around
+/* Like active_insn_p, except keep the return value use or clobber around
even after reload. */
static bool
programs that fail to return a value. Its effect is to
keep the return value from being live across the entire
function. If we allow it to be skipped, we introduce the
- possibility for register lifetime confusion. */
- if (GET_CODE (PATTERN (insn)) == CLOBBER
+ possibility for register lifetime confusion.
+ Similarly, keep a USE of the function return value, otherwise
+ the USE is dropped and we could fail to thread jump if USE
+ appears on some paths and not on others, see PR90257. */
+ if ((GET_CODE (PATTERN (insn)) == CLOBBER
+ || GET_CODE (PATTERN (insn)) == USE)
&& REG_P (XEXP (PATTERN (insn), 0))
&& REG_FUNCTION_VALUE_P (XEXP (PATTERN (insn), 0)))
return true;
static void
rtl_merge_blocks (basic_block a, basic_block b)
{
+ /* If B is a forwarder block whose outgoing edge has no location, we'll
+ propagate the locus of the edge between A and B onto it. */
+ const bool forward_edge_locus
+ = (b->flags & BB_FORWARDER_BLOCK) != 0
+ && LOCATION_LOCUS (EDGE_SUCC (b, 0)->goto_locus) == UNKNOWN_LOCATION;
rtx_insn *b_head = BB_HEAD (b), *b_end = BB_END (b), *a_end = BB_END (a);
rtx_insn *del_first = NULL, *del_last = NULL;
rtx_insn *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)
BB_HEAD (b) = b_empty ? NULL : b_head;
delete_insn_chain (del_first, del_last, true);
- /* When not optimizing and the edge is the only place in RTL which holds
- some unique locus, emit a nop with that locus in between. */
- if (!optimize)
+ /* If not optimizing, preserve the locus of the single edge between
+ blocks A and B if necessary by emitting a nop. */
+ if (!optimize
+ && !forward_edge_locus
+ && !DECL_IGNORED_P (current_function_decl))
{
emit_nop_for_unique_locus_between (a, b);
a_end = BB_END (a);
df_bb_delete (b->index);
- /* If B was a forwarder block, propagate the locus on the edge. */
- if (forwarder_p
- && LOCATION_LOCUS (EDGE_SUCC (b, 0)->goto_locus) == UNKNOWN_LOCATION)
+ if (forward_edge_locus)
EDGE_SUCC (b, 0)->goto_locus = EDGE_SUCC (a, 0)->goto_locus;
if (dump_file)
return as_a <rtx_code_label *> (BB_HEAD (block));
}
+/* Remove all barriers from BB_FOOTER of a BB. */
+
+static void
+remove_barriers_from_footer (basic_block bb)
+{
+ rtx_insn *insn = BB_FOOTER (bb);
+
+ /* Remove barriers but keep jumptables. */
+ while (insn)
+ {
+ if (BARRIER_P (insn))
+ {
+ if (PREV_INSN (insn))
+ SET_NEXT_INSN (PREV_INSN (insn)) = NEXT_INSN (insn);
+ else
+ BB_FOOTER (bb) = NEXT_INSN (insn);
+ if (NEXT_INSN (insn))
+ SET_PREV_INSN (NEXT_INSN (insn)) = PREV_INSN (insn);
+ }
+ if (LABEL_P (insn))
+ return;
+ insn = NEXT_INSN (insn);
+ }
+}
+
/* Attempt to perform edge redirection by replacing possibly complex jump
instruction by unconditional jump or removing jump completely. This can
apply only if all edges now point to the same block. The parameters and
/* Selectively unlink whole insn chain. */
if (in_cfglayout)
{
- rtx_insn *insn = BB_FOOTER (src);
-
delete_insn_chain (kill_from, BB_END (src), false);
-
- /* Remove barriers but keep jumptables. */
- while (insn)
- {
- if (BARRIER_P (insn))
- {
- if (PREV_INSN (insn))
- SET_NEXT_INSN (PREV_INSN (insn)) = NEXT_INSN (insn);
- else
- BB_FOOTER (src) = NEXT_INSN (insn);
- if (NEXT_INSN (insn))
- SET_PREV_INSN (NEXT_INSN (insn)) = PREV_INSN (insn);
- }
- if (LABEL_P (insn))
- break;
- insn = NEXT_INSN (insn);
- }
+ remove_barriers_from_footer (src);
}
else
delete_insn_chain (kill_from, PREV_INSN (BB_HEAD (target)),
}
/* Handle casesi dispatch insns. */
- if ((tmp = single_set (insn)) != NULL
- && SET_DEST (tmp) == pc_rtx
- && GET_CODE (SET_SRC (tmp)) == IF_THEN_ELSE
- && GET_CODE (XEXP (SET_SRC (tmp), 2)) == LABEL_REF
+ if ((tmp = tablejump_casesi_pattern (insn)) != NULL_RTX
&& label_ref_label (XEXP (SET_SRC (tmp), 2)) == old_label)
{
XEXP (SET_SRC (tmp), 2) = gen_rtx_LABEL_REF (Pmode,
/* If the substitution doesn't succeed, die. This can happen
if the back end emitted unrecognizable instructions or if
- target is exit block on some arches. */
+ target is exit block on some arches. Or for crossing
+ jumps. */
if (!redirect_jump (as_a <rtx_jump_insn *> (insn),
block_label (new_bb), 0))
{
- gcc_assert (new_bb == EXIT_BLOCK_PTR_FOR_FN (cfun));
+ gcc_assert (new_bb == EXIT_BLOCK_PTR_FOR_FN (cfun)
+ || CROSSING_JUMP_P (insn));
return false;
}
}
which will be done by fixup_partitions. */
fixup_partitions ();
- checking_verify_flow_info ();
+ if (!currently_expanding_to_rtl)
+ checking_verify_flow_info ();
FOR_BB_BETWEEN (bb, ENTRY_BLOCK_PTR_FOR_FN (cfun),
EXIT_BLOCK_PTR_FOR_FN (cfun), next_bb)
FOR_EACH_EDGE (e, ei, bb->succs)
if (e->insns.r)
- commit_one_edge_insertion (e);
+ {
+ if (currently_expanding_to_rtl)
+ rebuild_jump_labels_chain (e->insns.r);
+ commit_one_edge_insertion (e);
+ }
}
}
\f
if (df)
df_dump_start (outf);
- if (flags & TDF_BLOCKS)
+ if (cfun->curr_properties & PROP_cfg)
{
FOR_EACH_BB_REVERSE_FN (bb, cfun)
{
start[INSN_UID (BB_HEAD (bb))] = bb;
end[INSN_UID (BB_END (bb))] = bb;
- for (x = BB_HEAD (bb); x != NULL_RTX; x = NEXT_INSN (x))
+ if (flags & TDF_BLOCKS)
{
- enum bb_state state = IN_MULTIPLE_BB;
+ for (x = BB_HEAD (bb); x != NULL_RTX; x = NEXT_INSN (x))
+ {
+ enum bb_state state = IN_MULTIPLE_BB;
- if (in_bb_p[INSN_UID (x)] == NOT_IN_BB)
- state = IN_ONE_BB;
- in_bb_p[INSN_UID (x)] = state;
+ if (in_bb_p[INSN_UID (x)] == NOT_IN_BB)
+ state = IN_ONE_BB;
+ in_bb_p[INSN_UID (x)] = state;
- if (x == BB_END (bb))
- break;
+ if (x == BB_END (bb))
+ break;
+ }
}
}
}
if (flags & TDF_DETAILS)
df_dump_insn_bottom (tmp_rtx, outf);
- if (flags & TDF_BLOCKS)
+ bb = end[INSN_UID (tmp_rtx)];
+ if (bb != NULL)
{
- bb = end[INSN_UID (tmp_rtx)];
- if (bb != NULL)
+ if (flags & TDF_BLOCKS)
{
dump_bb_info (outf, bb, 0, dump_flags, false, true);
if (df && (flags & TDF_DETAILS))
df_dump_bottom (bb, outf);
putc ('\n', outf);
}
+ /* Emit a hint if the fallthrough target of current basic block
+ isn't the one placed right next. */
+ else if (EDGE_COUNT (bb->succs) > 0)
+ {
+ gcc_assert (BB_END (bb) == tmp_rtx);
+ const rtx_insn *ninsn = NEXT_INSN (tmp_rtx);
+ /* Bypass intervening deleted-insn notes and debug insns. */
+ while (ninsn
+ && !NONDEBUG_INSN_P (ninsn)
+ && !start[INSN_UID (ninsn)])
+ ninsn = NEXT_INSN (ninsn);
+ edge e = find_fallthru_edge (bb->succs);
+ if (e && ninsn)
+ {
+ basic_block dest = e->dest;
+ if (start[INSN_UID (ninsn)] != dest)
+ fprintf (outf, "%s ; pc falls through to BB %d\n",
+ print_rtx_head, dest->index);
+ }
+ }
}
}
find_partition_fixes (bool flag_only)
{
basic_block bb;
- vec<basic_block> bbs_in_cold_partition = vNULL;
vec<basic_block> bbs_to_fix = vNULL;
hash_set<basic_block> set;
else
BB_SET_PARTITION (bb, BB_COLD_PARTITION);
bbs_to_fix.safe_push (bb);
- bbs_in_cold_partition.safe_push (bb);
}
return bbs_to_fix;
basic_block last_bb_seen = ENTRY_BLOCK_PTR_FOR_FN (cfun), curr_bb = NULL;
num_bb_notes = 0;
- last_bb_seen = ENTRY_BLOCK_PTR_FOR_FN (cfun);
for (x = rtx_first; x; x = NEXT_INSN (x))
{
bool found;
edge_iterator ei;
- if (DEBUG_INSN_P (insn) && insn != BB_HEAD (bb))
+ if ((DEBUG_INSN_P (insn) || NOTE_P (insn)) && insn != BB_HEAD (bb))
do
insn = PREV_INSN (insn);
while ((DEBUG_INSN_P (insn) || NOTE_P (insn)) && insn != BB_HEAD (bb));
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)
+ on the non-existent edge.
+ Similarly, sometimes a call that can throw is
+ followed in the source with __builtin_unreachable (),
+ meaning that there is UB if the call returns rather
+ than throws. If there weren't any instructions
+ following such calls before, supposedly even the ones
+ we've deleted aren't significant and can be
+ removed. */
+ if (e)
{
/* We're not deleting it, we're moving it. */
insn->set_undeleted ();
{
fprintf (dump_file, " %i ", index);
if (get_bb_original (bb))
- fprintf (dump_file, "duplicate of %i ",
+ fprintf (dump_file, "duplicate of %i\n",
get_bb_original (bb)->index);
else if (forwarder_block_p (bb)
&& !LABEL_P (BB_HEAD (bb)))
- fprintf (dump_file, "compensation ");
+ fprintf (dump_file, "compensation\n");
else
- fprintf (dump_file, "bb %i ", bb->index);
+ fprintf (dump_file, "bb %i\n", bb->index);
}
}
force_nonfallthru (e);
}
- /* Ensure goto_locus from edges has some instructions with that locus
- in RTL. */
- if (!optimize)
+ /* Ensure goto_locus from edges has some instructions with that locus in RTL
+ when not optimizing. */
+ if (!optimize && !DECL_IGNORED_P (current_function_decl))
FOR_EACH_BB_FN (bb, cfun)
{
edge e;
/* Create a duplicate of the basic block BB. */
static basic_block
-cfg_layout_duplicate_bb (basic_block bb)
+cfg_layout_duplicate_bb (basic_block bb, copy_bb_data *)
{
rtx_insn *insn;
basic_block new_bb;
"Removing crossing jump while redirecting edge form %i to %i\n",
e->src->index, dest->index);
delete_insn (BB_END (src));
+ remove_barriers_from_footer (src);
e->flags |= EDGE_FALLTHRU;
}
else
ret = redirect_branch_edge (e, dest);
+ if (!ret)
+ return NULL;
+
fixup_partition_crossing (ret);
/* We don't want simplejumps in the insn stream during cfglayout. */
gcc_assert (!simplejump_p (BB_END (src)) || CROSSING_JUMP_P (BB_END (src)));
static void
cfg_layout_merge_blocks (basic_block a, basic_block b)
{
- bool forwarder_p = (b->flags & BB_FORWARDER_BLOCK) != 0;
+ /* If B is a forwarder block whose outgoing edge has no location, we'll
+ propagate the locus of the edge between A and B onto it. */
+ const bool forward_edge_locus
+ = (b->flags & BB_FORWARDER_BLOCK) != 0
+ && LOCATION_LOCUS (EDGE_SUCC (b, 0)->goto_locus) == UNKNOWN_LOCATION;
rtx_insn *insn;
gcc_checking_assert (cfg_layout_can_merge_blocks_p (a, b));
try_redirect_by_replacing_jump (EDGE_SUCC (a, 0), b, true);
gcc_assert (!JUMP_P (BB_END (a)));
- /* When not optimizing and the edge is the only place in RTL which holds
- some unique locus, emit a nop with that locus in between. */
- if (!optimize)
+ /* If not optimizing, preserve the locus of the single edge between
+ blocks A and B if necessary by emitting a nop. */
+ if (!optimize
+ && !forward_edge_locus
+ && !DECL_IGNORED_P (current_function_decl))
emit_nop_for_unique_locus_between (a, b);
/* Move things from b->footer after a->footer. */
df_bb_delete (b->index);
- /* If B was a forwarder block, propagate the locus on the edge. */
- if (forwarder_p
- && LOCATION_LOCUS (EDGE_SUCC (b, 0)->goto_locus) == UNKNOWN_LOCATION)
+ if (forward_edge_locus)
EDGE_SUCC (b, 0)->goto_locus = EDGE_SUCC (a, 0)->goto_locus;
if (dump_file)
}
static basic_block
-rtl_duplicate_bb (basic_block bb)
+rtl_duplicate_bb (basic_block bb, copy_bb_data *id)
{
- bb = cfg_layout_duplicate_bb (bb);
+ bb = cfg_layout_duplicate_bb (bb, id);
bb->aux = NULL;
return bb;
}
/* Do book-keeping of basic block BB for the profile consistency checker.
- If AFTER_PASS is 0, do pre-pass accounting, or if AFTER_PASS is 1
- then do post-pass accounting. Store the counting in RECORD. */
+ Store the counting in RECORD. */
static void
-rtl_account_profile_record (basic_block bb, int after_pass,
- struct profile_record *record)
+rtl_account_profile_record (basic_block bb, struct profile_record *record)
{
rtx_insn *insn;
FOR_BB_INSNS (bb, insn)
if (INSN_P (insn))
{
- record->size[after_pass] += insn_cost (insn, false);
+ record->size += insn_cost (insn, false);
if (bb->count.initialized_p ())
- record->time[after_pass]
+ record->time
+= insn_cost (insn, true) * bb->count.to_gcov_type ();
else if (profile_status_for_fn (cfun) == PROFILE_GUESSED)
- record->time[after_pass]
+ record->time
+= insn_cost (insn, true) * bb->count.to_frequency (cfun);
}
}
};
#include "gt-cfgrtl.h"
+
+#if __GNUC__ >= 10
+# pragma GCC diagnostic pop
+#endif