/* Instruction scheduling pass. Selective scheduler and pipeliner.
- Copyright (C) 2006-2016 Free Software Foundation, Inc.
+ Copyright (C) 2006-2020 Free Software Foundation, Inc.
This file is part of GCC.
#include "tree.h"
#include "rtl.h"
#include "df.h"
+#include "memmodel.h"
#include "tm_p.h"
#include "cfgrtl.h"
#include "cfganal.h"
#include "insn-config.h"
#include "insn-attr.h"
#include "recog.h"
-#include "params.h"
#include "target.h"
#include "sched-int.h"
#include "emit-rtl.h" /* FIXME: Can go away once crtl is moved to rtl.h. */
#include "sel-sched-dump.h"
/* A vector holding bb info for whole scheduling pass. */
-vec<sel_global_bb_info_def>
- sel_global_bb_info = vNULL;
+vec<sel_global_bb_info_def> sel_global_bb_info;
/* A vector holding bb info. */
-vec<sel_region_bb_info_def>
- sel_region_bb_info = vNULL;
+vec<sel_region_bb_info_def> sel_region_bb_info;
/* A pool for allocating all lists. */
object_allocator<_list_node> sched_lists_pool ("sel-sched-lists");
static struct common_sched_info_def sel_common_sched_info;
/* The loop nest being pipelined. */
-struct loop *current_loop_nest;
+class loop *current_loop_nest;
/* LOOP_NESTS is a vector containing the corresponding loop nest for
each region. */
-static vec<loop_p> loop_nests = vNULL;
+static vec<loop_p> loop_nests;
/* Saves blocks already in loop regions, indexed by bb->index. */
static sbitmap bbs_in_loop_rgns = NULL;
flist_remove (lp);
}
-/* Add ORIGINAL_INSN the def list DL honoring CROSSES_CALL. */
+/* Add ORIGINAL_INSN the def list DL honoring CROSSED_CALL_ABIS. */
void
-def_list_add (def_list_t *dl, insn_t original_insn, bool crosses_call)
+def_list_add (def_list_t *dl, insn_t original_insn,
+ unsigned int crossed_call_abis)
{
def_t d;
d = DEF_LIST_DEF (*dl);
d->orig_insn = original_insn;
- d->crosses_call = crosses_call;
+ d->crossed_call_abis = crossed_call_abis;
}
\f
}
\f
/* Functions to work with dependence contexts.
- Dc (aka deps context, aka deps_t, aka struct deps_desc *) is short for dependence
+ Dc (aka deps context, aka deps_t, aka class deps_desc *) is short for dependence
context. It accumulates information about processed insns to decide if
current insn is dependent on the processed ones. */
static deps_t
alloc_deps_context (void)
{
- return XNEW (struct deps_desc);
+ return XNEW (class deps_desc);
}
/* Allocate and initialize dep context. */
else
if (candidate->src == BLOCK_FOR_INSN (last_scheduled_insn))
{
- /* Would be weird if same insn is successor of several fallthrough
- edges. */
- gcc_assert (BLOCK_FOR_INSN (insn)->prev_bb
- != BLOCK_FOR_INSN (last_scheduled_insn_old));
-
state_free (FENCE_STATE (f));
FENCE_STATE (f) = state;
}
/* Return the cost of the VI.
- !!! FIXME: Unify with haifa-sched.c: insn_cost (). */
+ !!! FIXME: Unify with haifa-sched.c: insn_sched_cost (). */
int
sel_vinsn_cost (vinsn_t vi)
{
if (EXPR_PRIORITY (to) < EXPR_PRIORITY (from))
EXPR_PRIORITY (to) = EXPR_PRIORITY (from);
- if (EXPR_SCHED_TIMES (to) > EXPR_SCHED_TIMES (from))
- EXPR_SCHED_TIMES (to) = EXPR_SCHED_TIMES (from);
+ /* We merge sched-times half-way to the larger value to avoid the endless
+ pipelining of unneeded insns. The average seems to be good compromise
+ between pipelining opportunities and avoiding extra work. */
+ if (EXPR_SCHED_TIMES (to) != EXPR_SCHED_TIMES (from))
+ EXPR_SCHED_TIMES (to) = ((EXPR_SCHED_TIMES (from) + EXPR_SCHED_TIMES (to)
+ + 1) / 2);
if (EXPR_ORIG_BB_INDEX (to) != EXPR_ORIG_BB_INDEX (from))
EXPR_ORIG_BB_INDEX (to) = 0;
/* Make sure that speculative pattern is propagated into exprs that
have non-speculative one. This will provide us with consistent
speculative bits and speculative patterns inside expr. */
- if ((EXPR_SPEC_DONE_DS (from) != 0
- && EXPR_SPEC_DONE_DS (to) == 0)
- /* Do likewise for volatile insns, so that we always retain
- the may_trap_p bit on the resulting expression. */
- || (VINSN_MAY_TRAP_P (EXPR_VINSN (from))
- && !VINSN_MAY_TRAP_P (EXPR_VINSN (to))))
+ if (EXPR_SPEC_DONE_DS (to) == 0
+ && (EXPR_SPEC_DONE_DS (from) != 0
+ /* Do likewise for volatile insns, so that we always retain
+ the may_trap_p bit on the resulting expression. However,
+ avoid propagating the trapping bit into the instructions
+ already speculated. This would result in replacing the
+ speculative pattern with the non-speculative one and breaking
+ the speculation support. */
+ || (!VINSN_MAY_TRAP_P (EXPR_VINSN (to))
+ && VINSN_MAY_TRAP_P (EXPR_VINSN (from)))))
change_vinsn_in_expr (to, EXPR_VINSN (from));
merge_expr_data (to, from, split_point);
IDATA_TYPE (id) = USE;
}
+/* Setup implicit register clobbers calculated by sched-deps for INSN
+ before reload and save them in ID. */
+static void
+setup_id_implicit_regs (idata_t id, insn_t insn)
+{
+ if (reload_completed)
+ return;
+
+ HARD_REG_SET temp;
+
+ get_implicit_reg_pending_clobbers (&temp, insn);
+ IOR_REG_SET_HRS (IDATA_REG_SETS (id), temp);
+}
+
/* Setup register sets describing INSN in ID. */
static void
setup_id_reg_sets (idata_t id, insn_t insn)
}
}
+ /* Also get implicit reg clobbers from sched-deps. */
+ setup_id_implicit_regs (id, insn);
+
return_regset_to_pool (tmp);
}
static void
deps_init_id (idata_t id, insn_t insn, bool force_unique_p)
{
- struct deps_desc _dc, *dc = &_dc;
+ class deps_desc _dc, *dc = &_dc;
deps_init_id_data.where = DEPS_IN_NOWHERE;
deps_init_id_data.id = id;
deps_init_id_data.force_use_p = false;
init_deps (dc, false);
-
memcpy (&deps_init_id_sched_deps_info,
&const_deps_init_id_sched_deps_info,
sizeof (deps_init_id_sched_deps_info));
-
if (spec_info != NULL)
deps_init_id_sched_deps_info.generate_spec_deps = 1;
-
sched_deps_info = &deps_init_id_sched_deps_info;
deps_analyze_insn (dc, insn);
+ /* Implicit reg clobbers received from sched-deps separately. */
+ setup_id_implicit_regs (id, insn);
free_deps (dc);
-
deps_init_id_data.id = NULL;
}
/* Note a dependence. */
static void
-has_dependence_note_dep (insn_t pro ATTRIBUTE_UNUSED,
- ds_t ds ATTRIBUTE_UNUSED)
-{
- if (!sched_insns_conditions_mutex_p (has_dependence_data.pro,
- VINSN_INSN_RTX (has_dependence_data.con)))
+has_dependence_note_dep (insn_t pro, ds_t ds ATTRIBUTE_UNUSED)
+{
+ insn_t real_pro = has_dependence_data.pro;
+ insn_t real_con = VINSN_INSN_RTX (has_dependence_data.con);
+
+ /* We do not allow for debug insns to move through others unless they
+ are at the start of bb. This movement may create bookkeeping copies
+ that later would not be able to move up, violating the invariant
+ that a bookkeeping copy should be movable as the original insn.
+ Detect that here and allow that movement if we allowed it before
+ in the first place. */
+ if (DEBUG_INSN_P (real_con) && !DEBUG_INSN_P (real_pro)
+ && INSN_UID (NEXT_INSN (pro)) == INSN_UID (real_con))
+ return;
+
+ if (!sched_insns_conditions_mutex_p (real_pro, real_con))
{
ds_t *dsp = &has_dependence_data.has_dep_p[has_dependence_data.where];
{
int i;
ds_t ds;
- struct deps_desc *dc;
+ class deps_desc *dc;
if (INSN_SIMPLEJUMP_P (pred))
/* Unconditional jump is just a transfer of control flow.
if (!(e->flags & EDGE_FALLTHRU))
{
- /* We can not invalidate computed topological order by moving
+ /* We cannot invalidate computed topological order by moving
the edge destination block (E->SUCC) along a fallthru edge.
We will update dominators here only when we'll get
&& INSN_SCHED_TIMES (BB_END (xbb)) == 0
&& !IN_CURRENT_FENCE_P (BB_END (xbb)))
{
- if (sel_remove_insn (BB_END (xbb), false, false))
- return true;
+ /* We used to call sel_remove_insn here that can trigger tidy_control_flow
+ before we fix up the fallthru edge. Correct that ordering by
+ explicitly doing the latter before the former. */
+ clear_expr (INSN_EXPR (BB_END (xbb)));
tidy_fallthru_edge (EDGE_SUCC (xbb, 0));
+ if (tidy_control_flow (xbb, false))
+ return true;
}
first = sel_bb_head (xbb);
gcc_assert (EDGE_SUCC (xbb->prev_bb, 0)->flags & EDGE_FALLTHRU);
+ /* We could have skipped some debug insns which did not get removed with the block,
+ and the seqnos could become incorrect. Fix them up here. */
+ if (MAY_HAVE_DEBUG_INSNS && (sel_bb_head (xbb) != first || sel_bb_end (xbb) != last))
+ {
+ if (!sel_bb_empty_p (xbb->prev_bb))
+ {
+ int prev_seqno = INSN_SEQNO (sel_bb_end (xbb->prev_bb));
+ if (prev_seqno > INSN_SEQNO (sel_bb_head (xbb)))
+ for (insn_t insn = sel_bb_head (xbb); insn != first; insn = NEXT_INSN (insn))
+ INSN_SEQNO (insn) = prev_seqno + 1;
+ }
+ }
+
/* It can turn out that after removing unused jump, basic block
that contained that jump, becomes empty too. In such case
remove it too. */
insn_t *preds;
int n, i, seqno;
- while (tmp != head)
+ /* Loop backwards from INSN to HEAD including both. */
+ while (1)
{
- tmp = PREV_INSN (tmp);
if (INSN_P (tmp))
- return INSN_SEQNO (tmp);
+ return INSN_SEQNO (tmp);
+ if (tmp == head)
+ break;
+ tmp = PREV_INSN (tmp);
}
cfg_preds (bb, &preds, &n);
\f
/* Data for each insn in current region. */
-vec<sel_insn_data_def> s_i_d = vNULL;
+vec<sel_insn_data_def> s_i_d;
/* Extend data structures for insns from current region. */
static void
/* The basic block that already has been processed by the sched_data_update (),
but hasn't been in sel_add_bb () yet. */
-static vec<basic_block>
- last_added_blocks = vNULL;
+static vec<basic_block> last_added_blocks;
/* A pool for allocating successor infos. */
static struct
sinfo->probs_ok.safe_push (
/* FIXME: Improve calculation when skipping
inner loop to exits. */
- si.bb_end ? si.e1->probability : REG_BR_PROB_BASE);
+ si.bb_end
+ ? (si.e1->probability.initialized_p ()
+ ? si.e1->probability.to_reg_br_prob_base ()
+ : 0)
+ : REG_BR_PROB_BASE);
sinfo->succs_ok_n++;
}
else
/* Compute all_prob. */
if (!si.bb_end)
sinfo->all_prob = REG_BR_PROB_BASE;
- else
- sinfo->all_prob += si.e1->probability;
+ else if (si.e1->probability.initialized_p ())
+ sinfo->all_prob += si.e1->probability.to_reg_br_prob_base ();
sinfo->all_succs_n++;
}
if (current_loop_nest)
{
- struct loop *loop;
+ class loop *loop;
for (loop = current_loop_nest; loop; loop = loop_outer (loop))
if (considered_for_pipelining_p (loop) && loop->latch == from)
recompute_dominator (CDI_DOMINATORS, to));
set_immediate_dominator (CDI_DOMINATORS, orig_dest,
recompute_dominator (CDI_DOMINATORS, orig_dest));
+ if (jump && sel_bb_head_p (jump))
+ compute_live (jump);
}
/* A wrapper for redirect_edge_and_branch. Return TRUE if blocks connected by
set_immediate_dominator (CDI_DOMINATORS, orig_dest,
recompute_dominator (CDI_DOMINATORS, orig_dest));
}
+ if (jump && sel_bb_head_p (jump))
+ compute_live (jump);
return recompute_toporder_p;
}
res = create_insn_rtx_from_pattern (copy_rtx (PATTERN (insn_rtx)),
NULL_RTX);
+ /* Locate the end of existing REG_NOTES in NEW_RTX. */
+ rtx *ptail = ®_NOTES (res);
+ while (*ptail != NULL_RTX)
+ ptail = &XEXP (*ptail, 1);
+
/* Copy all REG_NOTES except REG_EQUAL/REG_EQUIV and REG_LABEL_OPERAND
since mark_jump_label will make them. REG_LABEL_TARGETs are created
there too, but are supposed to be sticky, so we copy them. */
&& REG_NOTE_KIND (link) != REG_EQUAL
&& REG_NOTE_KIND (link) != REG_EQUIV)
{
- if (GET_CODE (link) == EXPR_LIST)
- add_reg_note (res, REG_NOTE_KIND (link),
- copy_insn_1 (XEXP (link, 0)));
- else
- add_reg_note (res, REG_NOTE_KIND (link), XEXP (link, 0));
+ *ptail = duplicate_reg_note (link);
+ ptail = &XEXP (*ptail, 1);
}
return res;
/* Create a region for LOOP and return its number. If we don't want
to pipeline LOOP, return -1. */
static int
-make_region_from_loop (struct loop *loop)
+make_region_from_loop (class loop *loop)
{
unsigned int i;
int new_rgn_number = -1;
- struct loop *inner;
+ class loop *inner;
/* Basic block index, to be assigned to BLOCK_TO_BB. */
int bb_ord_index = 0;
basic_block preheader_block;
if (loop->num_nodes
- > (unsigned) PARAM_VALUE (PARAM_MAX_PIPELINE_REGION_BLOCKS))
+ > (unsigned) param_max_pipeline_region_blocks)
return -1;
/* Don't pipeline loops whose latch belongs to some of its inner loops. */
return -1;
loop->ninsns = num_loop_insns (loop);
- if ((int) loop->ninsns > PARAM_VALUE (PARAM_MAX_PIPELINE_REGION_INSNS))
+ if ((int) loop->ninsns > param_max_pipeline_region_insns)
return -1;
loop_blocks = get_loop_body_in_custom_order (loop, bb_top_order_comparator);
pipelined before outer loops. Returns true when a region for LOOP
is created. */
static bool
-make_regions_from_loop_nest (struct loop *loop)
+make_regions_from_loop_nest (class loop *loop)
{
- struct loop *cur_loop;
+ class loop *cur_loop;
int rgn_number;
/* Traverse all inner nodes of the loop. */
recompute_rev_top_order ();
}
-/* Returns a struct loop for region RGN. */
+/* Returns a class loop for region RGN. */
loop_p
get_loop_nest_for_rgn (unsigned int rgn)
{
/* True when LOOP was included into pipelining regions. */
bool
-considered_for_pipelining_p (struct loop *loop)
+considered_for_pipelining_p (class loop *loop)
{
if (loop_depth (loop) == 0)
return false;
/* Free data structures used in pipelining of loops. */
void sel_finish_pipelining (void)
{
- struct loop *loop;
+ class loop *loop;
/* Release aux fields so we don't free them later by mistake. */
FOR_EACH_LOOP (loop, 0)
{
if (current_loop_nest)
{
- struct loop *outer;
+ class loop *outer;
if (preheader_removed)
return false;