/* Instruction scheduling pass.
- Copyright (C) 1992-2014 Free Software Foundation, Inc.
+ Copyright (C) 1992-2015 Free Software Foundation, Inc.
Contributed by Michael Tiemann (tiemann@cygnus.com) Enhanced by,
and currently maintained by, Jim Wilson (wilson@cygnus.com)
#include "insn-attr.h"
#include "except.h"
#include "recog.h"
+#include "dominance.h"
+#include "cfg.h"
+#include "cfgrtl.h"
+#include "cfgbuild.h"
+#include "predict.h"
+#include "basic-block.h"
#include "sched-int.h"
#include "target.h"
#include "common/common-target.h"
#include "cfgloop.h"
#include "ira.h"
#include "emit-rtl.h" /* FIXME: Can go away once crtl is moved to rtl.h. */
-#include "hash-table.h"
#include "dumpfile.h"
#ifdef INSN_SCHEDULING
/* The minimal value of the INSN_TICK of an instruction. */
#define MIN_TICK (-max_insn_queue_index)
+/* Original order of insns in the ready list.
+ Used to keep order of normal insns while separating DEBUG_INSNs. */
+#define INSN_RFS_DEBUG_ORIG_ORDER(INSN) (HID (INSN)->rfs_debug_orig_order)
+
+/* The deciding reason for INSN's place in the ready list. */
+#define INSN_LAST_RFS_WIN(INSN) (HID (INSN)->last_rfs_win)
+
/* List of important notes we must keep around. This is a pointer to the
last element in the list. */
rtx_insn *note_list;
the base maximal time of functional unit reservations and getting a
result. This is the longest time an insn may be queued. */
-static rtx *insn_queue;
+static rtx_insn_list **insn_queue;
static int q_ptr = 0;
static int q_size = 0;
#define NEXT_Q(X) (((X)+1) & max_insn_queue_index)
/* Helpers for delay hashing. */
-struct delay_i1_hasher : typed_noop_remove <delay_pair>
+struct delay_i1_hasher : nofree_ptr_hash <delay_pair>
{
- typedef delay_pair value_type;
- typedef void compare_type;
- static inline hashval_t hash (const value_type *);
- static inline bool equal (const value_type *, const compare_type *);
+ typedef void *compare_type;
+ static inline hashval_t hash (const delay_pair *);
+ static inline bool equal (const delay_pair *, const void *);
};
/* Returns a hash value for X, based on hashing just I1. */
inline hashval_t
-delay_i1_hasher::hash (const value_type *x)
+delay_i1_hasher::hash (const delay_pair *x)
{
return htab_hash_pointer (x->i1);
}
/* Return true if I1 of pair X is the same as that of pair Y. */
inline bool
-delay_i1_hasher::equal (const value_type *x, const compare_type *y)
+delay_i1_hasher::equal (const delay_pair *x, const void *y)
{
return x->i1 == y;
}
-struct delay_i2_hasher : typed_free_remove <delay_pair>
+struct delay_i2_hasher : free_ptr_hash <delay_pair>
{
- typedef delay_pair value_type;
- typedef void compare_type;
- static inline hashval_t hash (const value_type *);
- static inline bool equal (const value_type *, const compare_type *);
+ typedef void *compare_type;
+ static inline hashval_t hash (const delay_pair *);
+ static inline bool equal (const delay_pair *, const void *);
};
/* Returns a hash value for X, based on hashing just I2. */
inline hashval_t
-delay_i2_hasher::hash (const value_type *x)
+delay_i2_hasher::hash (const delay_pair *x)
{
return htab_hash_pointer (x->i2);
}
/* Return true if I2 of pair X is the same as that of pair Y. */
inline bool
-delay_i2_hasher::equal (const value_type *x, const compare_type *y)
+delay_i2_hasher::equal (const delay_pair *x, const void *y)
{
return x->i2 == y;
}
/* Forward declarations. */
static int priority (rtx_insn *);
+static int autopref_rank_for_schedule (const rtx_insn *, const rtx_insn *);
static int rank_for_schedule (const void *, const void *);
static void swap_sort (rtx_insn **, int);
static void queue_insn (rtx_insn *, int, const char *);
/* The following functions are used to implement multi-pass scheduling
on the first cycle. */
static rtx_insn *ready_remove (struct ready_list *, int);
-static void ready_remove_insn (rtx);
+static void ready_remove_insn (rtx_insn *);
static void fix_inter_tick (rtx_insn *, rtx_insn *);
static int fix_tick_ready (rtx_insn *);
static void init_h_i_d (rtx_insn *);
static int haifa_speculate_insn (rtx_insn *, ds_t, rtx *);
static void generate_recovery_code (rtx_insn *);
-static void process_insn_forw_deps_be_in_spec (rtx, rtx_insn *, ds_t);
+static void process_insn_forw_deps_be_in_spec (rtx_insn *, rtx_insn *, ds_t);
static void begin_speculative_block (rtx_insn *);
static void add_to_speculative_block (rtx_insn *);
static void init_before_recovery (basic_block *);
/* Registers mentioned in the current region. */
static bitmap region_ref_regs;
+/* Effective number of available registers of a given class (see comment
+ in sched_pressure_start_bb). */
+static int sched_class_regs_num[N_REG_CLASSES];
+/* Number of call_used_regs. This is a helper for calculating of
+ sched_class_regs_num. */
+static int call_used_regs_num[N_REG_CLASSES];
+
/* Initiate register pressure relative info for scheduling the current
region. Currently it is only clearing register mentioned in the
current region. */
static void
setup_ref_regs (rtx x)
{
- int i, j, regno;
+ int i, j;
const RTX_CODE code = GET_CODE (x);
const char *fmt;
if (REG_P (x))
{
- regno = REGNO (x);
- if (HARD_REGISTER_NUM_P (regno))
- bitmap_set_range (region_ref_regs, regno,
- hard_regno_nregs[regno][GET_MODE (x)]);
- else
- bitmap_set_bit (region_ref_regs, REGNO (x));
+ bitmap_set_range (region_ref_regs, REGNO (x), REG_NREGS (x));
return;
}
fmt = GET_RTX_FORMAT (code);
initiate_bb_reg_pressure_info (basic_block bb)
{
unsigned int i ATTRIBUTE_UNUSED;
- rtx insn;
+ rtx_insn *insn;
if (current_nr_blocks > 1)
FOR_BB_INSNS (bb, insn)
if (NONDEBUG_INSN_P (insn))
setup_ref_regs (PATTERN (insn));
initiate_reg_pressure_info (df_get_live_in (bb));
-#ifdef EH_RETURN_DATA_REGNO
if (bb_has_eh_pred (bb))
for (i = 0; ; ++i)
{
mark_regno_birth_or_death (curr_reg_live, curr_reg_pressure,
regno, true);
}
-#endif
}
/* Save current register pressure related info. */
gcc_assert (curr_reg_pressure[cl] >= 0);
fprintf (sched_dump, " %s:%d(%d)", reg_class_names[cl],
curr_reg_pressure[cl],
- curr_reg_pressure[cl] - ira_class_hard_regs_num[cl]);
+ curr_reg_pressure[cl] - sched_class_regs_num[cl]);
}
fprintf (sched_dump, "\n");
}
INSN_COST (insn) = -1;
/* Invalidate INSN_TICK, so it'll be recalculated. */
INSN_TICK (insn) = INVALID_TICK;
+
+ /* Invalidate autoprefetch data entry. */
+ INSN_AUTOPREF_MULTIPASS_DATA (insn)[0].status
+ = AUTOPREF_MULTIPASS_DATA_UNINITIALIZED;
+ INSN_AUTOPREF_MULTIPASS_DATA (insn)[1].status
+ = AUTOPREF_MULTIPASS_DATA_UNINITIALIZED;
}
if (!sd_lists_empty_p (next, SD_LIST_HARD_BACK))
return HARD_DEP;
+ /* If NEXT is intended to sit adjacent to this instruction, we don't
+ want to try to break any dependencies. Treat it as a HARD_DEP. */
+ if (SCHED_GROUP_P (next))
+ return HARD_DEP;
+
/* Now we've got NEXT with speculative deps only.
1. Look at the deps to see what we have to do.
2. Check if we can do 'todo'. */
block, or the prev_head of the scheduling block. Used by
rank_for_schedule, so that insns independent of the last scheduled
insn will be preferred over dependent instructions. */
-static rtx last_nondebug_scheduled_insn;
+static rtx_insn *last_nondebug_scheduled_insn;
/* Pointer that iterates through the list of unscheduled insns if we
have a dbg_cnt enabled. It always points at an insn prior to the
{
int cost;
+ if (sched_fusion)
+ return 0;
+
if (sel_sched_p ())
{
if (recog_memoized (insn) < 0)
{
/* This variable is used for backward compatibility with the
targets. */
- rtx dep_cost_rtx_link = alloc_INSN_LIST (NULL_RTX, NULL_RTX);
+ rtx_insn_list *dep_cost_rtx_link =
+ alloc_INSN_LIST (NULL_RTX, NULL);
/* Make it self-cycled, so that if some tries to walk over this
incomplete list he/she will be caught in an endless loop. */
/* Compute the number of nondebug deps in list LIST for INSN. */
static int
-dep_list_size (rtx insn, sd_list_types_def list)
+dep_list_size (rtx_insn *insn, sd_list_types_def list)
{
sd_iterator_def sd_it;
dep_t dep;
return nodbgcount;
}
+bool sched_fusion;
+
/* Compute the priority number for INSN. */
static int
priority (rtx_insn *insn)
{
int this_priority = -1;
- if (dep_list_size (insn, SD_LIST_FORW) == 0)
+ if (sched_fusion)
+ {
+ int this_fusion_priority;
+
+ targetm.sched.fusion_priority (insn, FUSION_MAX_PRIORITY,
+ &this_fusion_priority, &this_priority);
+ INSN_FUSION_PRIORITY (insn) = this_fusion_priority;
+ }
+ else if (dep_list_size (insn, SD_LIST_FORW) == 0)
/* ??? We should set INSN_PRIORITY to insn_cost when and insn has
some forward deps but all of them are ignored by
contributes_to_priority hook. At the moment we set priority of
this_priority = insn_cost (insn);
else
{
- rtx prev_first, twin;
+ rtx_insn *prev_first, *twin;
basic_block rec;
/* For recovery check instructions we calculate priority slightly
{
int i, change, before, after, hard_regno;
int excess_cost_change;
- enum machine_mode mode;
+ machine_mode mode;
enum reg_class cl;
struct reg_pressure_data *pressure_info;
int *max_reg_pressure;
cl = ira_pressure_classes[i];
gcc_assert (curr_reg_pressure[cl] >= 0);
change = (int) pressure_info[i].set_increase - death[cl];
- before = MAX (0, max_reg_pressure[i] - ira_class_hard_regs_num[cl]);
+ before = MAX (0, max_reg_pressure[i] - sched_class_regs_num[cl]);
after = MAX (0, max_reg_pressure[i] + change
- - ira_class_hard_regs_num[cl]);
+ - sched_class_regs_num[cl]);
hard_regno = ira_class_hard_regs[cl][0];
gcc_assert (hard_regno >= 0);
mode = reg_raw_mode[hard_regno];
/* Check whether the maximum pressure in the overall schedule
has increased. (This means that the MODEL_MAX_PRESSURE of
- every point <= POINT will need to increae too; see below.) */
+ every point <= POINT will need to increase too; see below.) */
if (group->limits[pci].pressure < ref_pressure)
group->limits[pci].pressure = ref_pressure;
/* Return the cost of increasing the pressure in class CL from FROM to TO.
Here we use the very simplistic cost model that every register above
- ira_class_hard_regs_num[CL] has a spill cost of 1. We could use other
+ sched_class_regs_num[CL] has a spill cost of 1. We could use other
measures instead, such as one based on MEMORY_MOVE_COST. However:
(1) In order for an instruction to be scheduled, the higher cost
static int
model_spill_cost (int cl, int from, int to)
{
- from = MAX (from, ira_class_hard_regs_num[cl]);
+ from = MAX (from, sched_class_regs_num[cl]);
return MAX (to, from) - from;
}
bool print_p;
/* Record the baseECC value for each instruction in the model schedule,
- except that negative costs are converted to zero ones now rather thatn
+ except that negative costs are converted to zero ones now rather than
later. Do not assign a cost to debug instructions, since they must
not change code-generation decisions. Experiments suggest we also
get better results by not assigning a cost to instructions from
/* Enum of rank_for_schedule heuristic decisions. */
enum rfs_decision {
- RFS_DEBUG, RFS_LIVE_RANGE_SHRINK1, RFS_LIVE_RANGE_SHRINK2,
+ RFS_LIVE_RANGE_SHRINK1, RFS_LIVE_RANGE_SHRINK2,
RFS_SCHED_GROUP, RFS_PRESSURE_DELAY, RFS_PRESSURE_TICK,
RFS_FEEDS_BACKTRACK_INSN, RFS_PRIORITY, RFS_SPECULATION,
RFS_SCHED_RANK, RFS_LAST_INSN, RFS_PRESSURE_INDEX,
- RFS_DEP_COUNT, RFS_TIE, RFS_N };
+ RFS_DEP_COUNT, RFS_TIE, RFS_FUSION, RFS_N };
/* Corresponding strings for print outs. */
static const char *rfs_str[RFS_N] = {
- "RFS_DEBUG", "RFS_LIVE_RANGE_SHRINK1", "RFS_LIVE_RANGE_SHRINK2",
+ "RFS_LIVE_RANGE_SHRINK1", "RFS_LIVE_RANGE_SHRINK2",
"RFS_SCHED_GROUP", "RFS_PRESSURE_DELAY", "RFS_PRESSURE_TICK",
"RFS_FEEDS_BACKTRACK_INSN", "RFS_PRIORITY", "RFS_SPECULATION",
"RFS_SCHED_RANK", "RFS_LAST_INSN", "RFS_PRESSURE_INDEX",
- "RFS_DEP_COUNT", "RFS_TIE" };
+ "RFS_DEP_COUNT", "RFS_TIE", "RFS_FUSION" };
/* Statistical breakdown of rank_for_schedule decisions. */
typedef struct { unsigned stats[RFS_N]; } rank_for_schedule_stats_t;
static rank_for_schedule_stats_t rank_for_schedule_stats;
+/* Return the result of comparing insns TMP and TMP2 and update
+ Rank_For_Schedule statistics. */
static int
-rfs_result (enum rfs_decision decision, int result)
+rfs_result (enum rfs_decision decision, int result, rtx tmp, rtx tmp2)
{
++rank_for_schedule_stats.stats[decision];
+ if (result < 0)
+ INSN_LAST_RFS_WIN (tmp) = decision;
+ else if (result > 0)
+ INSN_LAST_RFS_WIN (tmp2) = decision;
+ else
+ gcc_unreachable ();
return result;
}
+/* Sorting predicate to move DEBUG_INSNs to the top of ready list, while
+ keeping normal insns in original order. */
+
+static int
+rank_for_schedule_debug (const void *x, const void *y)
+{
+ rtx_insn *tmp = *(rtx_insn * const *) y;
+ rtx_insn *tmp2 = *(rtx_insn * const *) x;
+
+ /* Schedule debug insns as early as possible. */
+ if (DEBUG_INSN_P (tmp) && !DEBUG_INSN_P (tmp2))
+ return -1;
+ else if (!DEBUG_INSN_P (tmp) && DEBUG_INSN_P (tmp2))
+ return 1;
+ else if (DEBUG_INSN_P (tmp) && DEBUG_INSN_P (tmp2))
+ return INSN_LUID (tmp) - INSN_LUID (tmp2);
+ else
+ return INSN_RFS_DEBUG_ORIG_ORDER (tmp2) - INSN_RFS_DEBUG_ORIG_ORDER (tmp);
+}
+
/* Returns a positive value if x is preferred; returns a negative value if
y is preferred. Should never return 0, since that will make the sort
unstable. */
int tmp_class, tmp2_class;
int val, priority_val, info_val, diff;
- if (MAY_HAVE_DEBUG_INSNS)
- {
- /* Schedule debug insns as early as possible. */
- if (DEBUG_INSN_P (tmp) && !DEBUG_INSN_P (tmp2))
- return rfs_result (RFS_DEBUG, -1);
- else if (!DEBUG_INSN_P (tmp) && DEBUG_INSN_P (tmp2))
- return rfs_result (RFS_DEBUG, 1);
- else if (DEBUG_INSN_P (tmp) && DEBUG_INSN_P (tmp2))
- return rfs_result (RFS_DEBUG, INSN_LUID (tmp) - INSN_LUID (tmp2));
- }
-
if (live_range_shrinkage_p)
{
/* Don't use SCHED_PRESSURE_MODEL -- it results in much worse
|| INSN_REG_PRESSURE_EXCESS_COST_CHANGE (tmp2) < 0)
&& (diff = (INSN_REG_PRESSURE_EXCESS_COST_CHANGE (tmp)
- INSN_REG_PRESSURE_EXCESS_COST_CHANGE (tmp2))) != 0)
- return rfs_result (RFS_LIVE_RANGE_SHRINK1, diff);
+ return rfs_result (RFS_LIVE_RANGE_SHRINK1, diff, tmp, tmp2);
/* Sort by INSN_LUID (original insn order), so that we make the
sort stable. This minimizes instruction movement, thus
minimizing sched's effect on debugging and cross-jumping. */
return rfs_result (RFS_LIVE_RANGE_SHRINK2,
- INSN_LUID (tmp) - INSN_LUID (tmp2));
+ INSN_LUID (tmp) - INSN_LUID (tmp2), tmp, tmp2);
}
/* The insn in a schedule group should be issued the first. */
if (flag_sched_group_heuristic &&
SCHED_GROUP_P (tmp) != SCHED_GROUP_P (tmp2))
- return rfs_result (RFS_SCHED_GROUP, SCHED_GROUP_P (tmp2) ? 1 : -1);
+ return rfs_result (RFS_SCHED_GROUP, SCHED_GROUP_P (tmp2) ? 1 : -1,
+ tmp, tmp2);
/* Make sure that priority of TMP and TMP2 are initialized. */
gcc_assert (INSN_PRIORITY_KNOWN (tmp) && INSN_PRIORITY_KNOWN (tmp2));
+ if (sched_fusion)
+ {
+ /* The instruction that has the same fusion priority as the last
+ instruction is the instruction we picked next. If that is not
+ the case, we sort ready list firstly by fusion priority, then
+ by priority, and at last by INSN_LUID. */
+ int a = INSN_FUSION_PRIORITY (tmp);
+ int b = INSN_FUSION_PRIORITY (tmp2);
+ int last = -1;
+
+ if (last_nondebug_scheduled_insn
+ && !NOTE_P (last_nondebug_scheduled_insn)
+ && BLOCK_FOR_INSN (tmp)
+ == BLOCK_FOR_INSN (last_nondebug_scheduled_insn))
+ last = INSN_FUSION_PRIORITY (last_nondebug_scheduled_insn);
+
+ if (a != last && b != last)
+ {
+ if (a == b)
+ {
+ a = INSN_PRIORITY (tmp);
+ b = INSN_PRIORITY (tmp2);
+ }
+ if (a != b)
+ return rfs_result (RFS_FUSION, b - a, tmp, tmp2);
+ else
+ return rfs_result (RFS_FUSION,
+ INSN_LUID (tmp) - INSN_LUID (tmp2), tmp, tmp2);
+ }
+ else if (a == b)
+ {
+ gcc_assert (last_nondebug_scheduled_insn
+ && !NOTE_P (last_nondebug_scheduled_insn));
+ last = INSN_PRIORITY (last_nondebug_scheduled_insn);
+
+ a = abs (INSN_PRIORITY (tmp) - last);
+ b = abs (INSN_PRIORITY (tmp2) - last);
+ if (a != b)
+ return rfs_result (RFS_FUSION, a - b, tmp, tmp2);
+ else
+ return rfs_result (RFS_FUSION,
+ INSN_LUID (tmp) - INSN_LUID (tmp2), tmp, tmp2);
+ }
+ else if (a == last)
+ return rfs_result (RFS_FUSION, -1, tmp, tmp2);
+ else
+ return rfs_result (RFS_FUSION, 1, tmp, tmp2);
+ }
+
if (sched_pressure != SCHED_PRESSURE_NONE)
{
/* Prefer insn whose scheduling results in the smallest register
+ insn_delay (tmp)
- INSN_REG_PRESSURE_EXCESS_COST_CHANGE (tmp2)
- insn_delay (tmp2))))
- return rfs_result (RFS_PRESSURE_DELAY, diff);
+ return rfs_result (RFS_PRESSURE_DELAY, diff, tmp, tmp2);
}
if (sched_pressure != SCHED_PRESSURE_NONE
&& INSN_TICK (tmp2) != INSN_TICK (tmp))
{
diff = INSN_TICK (tmp) - INSN_TICK (tmp2);
- return rfs_result (RFS_PRESSURE_TICK, diff);
+ return rfs_result (RFS_PRESSURE_TICK, diff, tmp, tmp2);
}
/* If we are doing backtracking in this schedule, prefer insns that
{
priority_val = FEEDS_BACKTRACK_INSN (tmp2) - FEEDS_BACKTRACK_INSN (tmp);
if (priority_val)
- return rfs_result (RFS_FEEDS_BACKTRACK_INSN, priority_val);
+ return rfs_result (RFS_FEEDS_BACKTRACK_INSN, priority_val, tmp, tmp2);
}
/* Prefer insn with higher priority. */
priority_val = INSN_PRIORITY (tmp2) - INSN_PRIORITY (tmp);
if (flag_sched_critical_path_heuristic && priority_val)
- return rfs_result (RFS_PRIORITY, priority_val);
+ return rfs_result (RFS_PRIORITY, priority_val, tmp, tmp2);
+
+ if (PARAM_VALUE (PARAM_SCHED_AUTOPREF_QUEUE_DEPTH) >= 0)
+ {
+ int autopref = autopref_rank_for_schedule (tmp, tmp2);
+ if (autopref != 0)
+ return autopref;
+ }
/* Prefer speculative insn with greater dependencies weakness. */
if (flag_sched_spec_insn_heuristic && spec_info)
dw = dw2 - dw1;
if (dw > (NO_DEP_WEAK / 8) || dw < -(NO_DEP_WEAK / 8))
- return rfs_result (RFS_SPECULATION, dw);
+ return rfs_result (RFS_SPECULATION, dw, tmp, tmp2);
}
info_val = (*current_sched_info->rank) (tmp, tmp2);
if (flag_sched_rank_heuristic && info_val)
- return rfs_result (RFS_SCHED_RANK, info_val);
+ return rfs_result (RFS_SCHED_RANK, info_val, tmp, tmp2);
/* Compare insns based on their relation to the last scheduled
non-debug insn. */
{
dep_t dep1;
dep_t dep2;
- rtx last = last_nondebug_scheduled_insn;
+ rtx_insn *last = last_nondebug_scheduled_insn;
/* Classify the instructions into three classes:
1) Data dependent on last schedule insn.
tmp2_class = 2;
if ((val = tmp2_class - tmp_class))
- return rfs_result (RFS_LAST_INSN, val);
+ return rfs_result (RFS_LAST_INSN, val, tmp, tmp2);
}
/* Prefer instructions that occur earlier in the model schedule. */
{
diff = model_index (tmp) - model_index (tmp2);
gcc_assert (diff != 0);
- return rfs_result (RFS_PRESSURE_INDEX, diff);
+ return rfs_result (RFS_PRESSURE_INDEX, diff, tmp, tmp2);
}
/* Prefer the insn which has more later insns that depend on it.
- dep_list_size (tmp, SD_LIST_FORW));
if (flag_sched_dep_count_heuristic && val != 0)
- return rfs_result (RFS_DEP_COUNT, val);
+ return rfs_result (RFS_DEP_COUNT, val, tmp, tmp2);
/* If insns are equally good, sort by INSN_LUID (original insn order),
so that we make the sort stable. This minimizes instruction movement,
thus minimizing sched's effect on debugging and cross-jumping. */
- return rfs_result (RFS_TIE, INSN_LUID (tmp) - INSN_LUID (tmp2));
+ return rfs_result (RFS_TIE, INSN_LUID (tmp) - INSN_LUID (tmp2), tmp, tmp2);
}
/* Resort the array A in which only element at index N may be out of order. */
queue_insn (rtx_insn *insn, int n_cycles, const char *reason)
{
int next_q = NEXT_Q_AFTER (q_ptr, n_cycles);
- rtx link = alloc_INSN_LIST (insn, insn_queue[next_q]);
+ rtx_insn_list *link = alloc_INSN_LIST (insn, insn_queue[next_q]);
int new_tick;
gcc_assert (n_cycles <= max_insn_queue_index);
/* Remove INSN from the ready list. */
static void
-ready_remove_insn (rtx insn)
+ready_remove_insn (rtx_insn *insn)
{
int i;
/* Print rank_for_schedule statistics. */
static void
print_rank_for_schedule_stats (const char *prefix,
- const rank_for_schedule_stats_t *stats)
+ const rank_for_schedule_stats_t *stats,
+ struct ready_list *ready)
{
for (int i = 0; i < RFS_N; ++i)
if (stats->stats[i])
- fprintf (sched_dump, "%s%20s: %u\n", prefix, rfs_str[i], stats->stats[i]);
+ {
+ fprintf (sched_dump, "%s%20s: %u", prefix, rfs_str[i], stats->stats[i]);
+
+ if (ready != NULL)
+ /* Print out insns that won due to RFS_<I>. */
+ {
+ rtx_insn **p = ready_lastpos (ready);
+
+ fprintf (sched_dump, ":");
+ /* Start with 1 since least-priority insn didn't have any wins. */
+ for (int j = 1; j < ready->n_ready; ++j)
+ if (INSN_LAST_RFS_WIN (p[j]) == i)
+ fprintf (sched_dump, " %s",
+ (*current_sched_info->print_insn) (p[j], 0));
+ }
+ fprintf (sched_dump, "\n");
+ }
}
-/* Sort the ready list READY by ascending priority, using the SCHED_SORT
- macro. */
+/* Separate DEBUG_INSNS from normal insns. DEBUG_INSNs go to the end
+ of array. */
+static void
+ready_sort_debug (struct ready_list *ready)
+{
+ int i;
+ rtx_insn **first = ready_lastpos (ready);
-void
-ready_sort (struct ready_list *ready)
+ for (i = 0; i < ready->n_ready; ++i)
+ if (!DEBUG_INSN_P (first[i]))
+ INSN_RFS_DEBUG_ORIG_ORDER (first[i]) = i;
+
+ qsort (first, ready->n_ready, sizeof (rtx), rank_for_schedule_debug);
+}
+
+/* Sort non-debug insns in the ready list READY by ascending priority.
+ Assumes that all debug insns are separated from the real insns. */
+static void
+ready_sort_real (struct ready_list *ready)
{
int i;
rtx_insn **first = ready_lastpos (ready);
+ int n_ready_real = ready->n_ready - ready->n_debug;
if (sched_pressure == SCHED_PRESSURE_WEIGHTED)
- {
- for (i = 0; i < ready->n_ready; i++)
- if (!DEBUG_INSN_P (first[i]))
- setup_insn_reg_pressure_info (first[i]);
- }
- if (sched_pressure == SCHED_PRESSURE_MODEL
- && model_curr_point < model_num_insns)
- model_set_excess_costs (first, ready->n_ready);
+ for (i = 0; i < n_ready_real; ++i)
+ setup_insn_reg_pressure_info (first[i]);
+ else if (sched_pressure == SCHED_PRESSURE_MODEL
+ && model_curr_point < model_num_insns)
+ model_set_excess_costs (first, n_ready_real);
rank_for_schedule_stats_t stats1;
if (sched_verbose >= 4)
stats1 = rank_for_schedule_stats;
- if (ready->n_ready == 2)
- swap_sort (first, ready->n_ready);
- else if (ready->n_ready > 2)
- qsort (first, ready->n_ready, sizeof (rtx), rank_for_schedule);
+ if (n_ready_real == 2)
+ swap_sort (first, n_ready_real);
+ else if (n_ready_real > 2)
+ qsort (first, n_ready_real, sizeof (rtx), rank_for_schedule);
if (sched_verbose >= 4)
{
rank_for_schedule_stats_diff (&stats1, &rank_for_schedule_stats);
- print_rank_for_schedule_stats (";;\t\t", &stats1);
+ print_rank_for_schedule_stats (";;\t\t", &stats1, ready);
}
}
+/* Sort the ready list READY by ascending priority. */
+static void
+ready_sort (struct ready_list *ready)
+{
+ if (ready->n_debug > 0)
+ ready_sort_debug (ready);
+ else
+ ready_sort_real (ready);
+}
+
/* PREV is an insn that is ready to execute. Adjust its priority if that
will help shorten or lengthen register lifetimes as appropriate. Also
provide a hook for the target to tweak itself. */
meaning in sched-int.h::_haifa_insn_data) for all current BB insns
after insn AFTER. */
static void
-setup_insn_max_reg_pressure (rtx after, bool update_p)
+setup_insn_max_reg_pressure (rtx_insn *after, bool update_p)
{
int i, p;
bool eq_p;
insns starting after insn AFTER. Set up also max register pressure
for all insns of the basic block. */
void
-sched_setup_bb_reg_pressure_info (basic_block bb, rtx after)
+sched_setup_bb_reg_pressure_info (basic_block bb, rtx_insn *after)
{
gcc_assert (sched_pressure == SCHED_PRESSURE_WEIGHTED);
initiate_bb_reg_pressure_info (bb);
only be scheduled once their control dependency is resolved. */
static void
-check_clobbered_conditions (rtx insn)
+check_clobbered_conditions (rtx_insn *insn)
{
HARD_REG_SET t;
int i;
}
for (i = 0; i <= max_insn_queue_index; i++)
{
- rtx link;
+ rtx_insn_list *link;
int q = NEXT_Q_AFTER (q_ptr, i);
restart_queue:
- for (link = insn_queue[q]; link; link = XEXP (link, 1))
+ for (link = insn_queue[q]; link; link = link->next ())
{
- rtx_insn *x = as_a <rtx_insn *> (XEXP (link, 0));
+ rtx_insn *x = link->insn ();
if (TODO_SPEC (x) == DEP_CONTROL && cond_clobbered_p (x, t))
{
queue_remove (x);
scheduling region. */
static void
-model_start_schedule (void)
+model_start_schedule (basic_block bb)
{
- basic_block bb;
-
model_next_priority = 1;
model_schedule.create (sched_max_luid);
model_insns = XCNEWVEC (struct model_insn_info, sched_max_luid);
- bb = BLOCK_FOR_INSN (NEXT_INSN (current_sched_info->prev_head));
+ gcc_assert (bb == BLOCK_FOR_INSN (NEXT_INSN (current_sched_info->prev_head)));
initiate_reg_pressure_info (df_get_live_in (bb));
model_analyze_insns ();
model_finalize_pressure_group (&model_before_pressure);
model_schedule.release ();
}
+
+/* Prepare reg pressure scheduling for basic block BB. */
+static void
+sched_pressure_start_bb (basic_block bb)
+{
+ /* Set the number of available registers for each class taking into account
+ relative probability of current basic block versus function prologue and
+ epilogue.
+ * If the basic block executes much more often than the prologue/epilogue
+ (e.g., inside a hot loop), then cost of spill in the prologue is close to
+ nil, so the effective number of available registers is
+ (ira_class_hard_regs_num[cl] - 0).
+ * If the basic block executes as often as the prologue/epilogue,
+ then spill in the block is as costly as in the prologue, so the effective
+ number of available registers is
+ (ira_class_hard_regs_num[cl] - call_used_regs_num[cl]).
+ Note that all-else-equal, we prefer to spill in the prologue, since that
+ allows "extra" registers for other basic blocks of the function.
+ * If the basic block is on the cold path of the function and executes
+ rarely, then we should always prefer to spill in the block, rather than
+ in the prologue/epilogue. The effective number of available register is
+ (ira_class_hard_regs_num[cl] - call_used_regs_num[cl]). */
+ {
+ int i;
+ int entry_freq = ENTRY_BLOCK_PTR_FOR_FN (cfun)->frequency;
+ int bb_freq = bb->frequency;
+
+ if (bb_freq == 0)
+ {
+ if (entry_freq == 0)
+ entry_freq = bb_freq = 1;
+ }
+ if (bb_freq < entry_freq)
+ bb_freq = entry_freq;
+
+ for (i = 0; i < ira_pressure_classes_num; ++i)
+ {
+ enum reg_class cl = ira_pressure_classes[i];
+ sched_class_regs_num[cl] = ira_class_hard_regs_num[cl];
+ sched_class_regs_num[cl]
+ -= (call_used_regs_num[cl] * entry_freq) / bb_freq;
+ }
+ }
+
+ if (sched_pressure == SCHED_PRESSURE_MODEL)
+ model_start_schedule (bb);
+}
\f
/* A structure that holds local state for the loop in schedule_block. */
struct sched_block_state
gcc_assert (INSN_TICK (insn) >= MIN_TICK);
if (INSN_TICK (insn) > clock_var)
/* INSN has been prematurely moved from the queue to the ready list.
- This is possible only if following flag is set. */
- gcc_assert (flag_sched_stalled_insns);
+ This is possible only if following flags are set. */
+ gcc_assert (flag_sched_stalled_insns || sched_fusion);
/* ??? Probably, if INSN is scheduled prematurely, we should leave
INSN_TICK untouched. This is a machine-dependent issue, actually. */
state_t curr_state;
rtx_insn *last_scheduled_insn;
- rtx last_nondebug_scheduled_insn;
+ rtx_insn *last_nondebug_scheduled_insn;
rtx_insn *nonscheduled_insns_begin;
int cycle_issued_insns;
/* We don't need to save q_ptr, as its value is arbitrary and we can set it
to 0 when restoring. */
int q_size;
- rtx *insn_queue;
+ rtx_insn_list **insn_queue;
/* Describe pattern replacements that occurred since this backtrack point
was queued. */
/* For every dependency of INSN, set the FEEDS_BACKTRACK_INSN bit according
to SET_P. */
static void
-mark_backtrack_feeds (rtx insn, int set_p)
+mark_backtrack_feeds (rtx_insn *insn, int set_p)
{
sd_iterator_def sd_it;
dep_t dep;
save->ready.vec = XNEWVEC (rtx_insn *, ready.veclen);
memcpy (save->ready.vec, ready.vec, ready.veclen * sizeof (rtx));
- save->insn_queue = XNEWVEC (rtx, max_insn_queue_index + 1);
+ save->insn_queue = XNEWVEC (rtx_insn_list *, max_insn_queue_index + 1);
save->q_size = q_size;
for (i = 0; i <= max_insn_queue_index; i++)
{
for (i = 0; i <= max_insn_queue_index; i++)
{
int q = NEXT_Q_AFTER (q_ptr, i);
- rtx link;
- for (link = insn_queue[q]; link; link = XEXP (link, 1))
+ rtx_insn_list *link;
+ for (link = insn_queue[q]; link; link = link->next ())
{
- rtx insn = XEXP (link, 0);
+ rtx_insn *insn = link->insn ();
FOR_EACH_DEP (insn, SD_LIST_BACK, sd_it, dep)
if (!DEBUG_INSN_P (DEP_PRO (dep)))
{
queued nowhere. */
static void
-unschedule_insns_until (rtx insn)
+unschedule_insns_until (rtx_insn *insn)
{
auto_vec<rtx_insn *> recompute_vec;
static void
restore_last_backtrack_point (struct sched_block_state *psched_block)
{
- rtx link;
int i;
struct haifa_saved_data *save = backtrack_queue;
{
int q = NEXT_Q_AFTER (q_ptr, i);
- for (link = insn_queue[q]; link; link = XEXP (link, 1))
+ for (rtx_insn_list *link = insn_queue[q]; link; link = link->next ())
{
- rtx_insn *x = as_a <rtx_insn *> (XEXP (link, 0));
+ rtx_insn *x = link->insn ();
QUEUE_INDEX (x) = QUEUE_NOWHERE;
INSN_TICK (x) = INVALID_TICK;
}
insn_queue[q] = save->insn_queue[q];
- for (link = insn_queue[q]; link; link = XEXP (link, 1))
+ for (rtx_insn_list *link = insn_queue[q]; link; link = link->next ())
{
- rtx_insn *x = as_a <rtx_insn *> (XEXP (link, 0));
+ rtx_insn *x = link->insn ();
QUEUE_INDEX (x) = i;
TODO_SPEC (x) = recompute_todo_spec (x, true);
INSN_TICK (x) = save->clock_var + i;
/* Return nonzero if there are no real insns in the range [ HEAD, TAIL ]. */
int
-no_real_insns_p (const_rtx head, const_rtx tail)
+no_real_insns_p (const rtx_insn *head, const rtx_insn *tail)
{
while (head != NEXT_INSN (tail))
{
queue_to_ready (struct ready_list *ready)
{
rtx_insn *insn;
- rtx link;
- rtx skip_insn;
+ rtx_insn_list *link;
+ rtx_insn *skip_insn;
q_ptr = NEXT_Q (q_ptr);
nonscheduled insn. */
skip_insn = first_nonscheduled_insn ();
else
- skip_insn = NULL_RTX;
+ skip_insn = NULL;
/* Add all pending insns that can be scheduled without stalls to the
ready list. */
- for (link = insn_queue[q_ptr]; link; link = XEXP (link, 1))
+ for (link = insn_queue[q_ptr]; link; link = link->next ())
{
- insn = as_a <rtx_insn *> (XEXP (link, 0));
+ insn = link->insn ();
q_size -= 1;
if (sched_verbose >= 2)
{
if ((link = insn_queue[NEXT_Q_AFTER (q_ptr, stalls)]))
{
- for (; link; link = XEXP (link, 1))
+ for (; link; link = link->next ())
{
- insn = as_a <rtx_insn *> (XEXP (link, 0));
+ insn = link->insn ();
q_size -= 1;
if (sched_verbose >= 2)
addition) depending on user flags and target hooks. */
static bool
-ok_for_early_queue_removal (rtx insn)
+ok_for_early_queue_removal (rtx_insn *insn)
{
if (targetm.sched.is_costly_dependence)
{
- rtx prev_insn;
int n_cycles;
int i = scheduled_insns.length ();
for (n_cycles = flag_sched_stalled_insns_dep; n_cycles; n_cycles--)
{
int cost;
- prev_insn = scheduled_insns[i];
+ rtx_insn *prev_insn = scheduled_insns[i];
if (!NOTE_P (prev_insn))
{
early_queue_to_ready (state_t state, struct ready_list *ready)
{
rtx_insn *insn;
- rtx link;
- rtx next_link;
- rtx prev_link;
+ rtx_insn_list *link;
+ rtx_insn_list *next_link;
+ rtx_insn_list *prev_link;
bool move_to_ready;
int cost;
state_t temp_state = alloca (dfa_state_size);
prev_link = 0;
while (link)
{
- next_link = XEXP (link, 1);
- insn = as_a <rtx_insn *> (XEXP (link, 0));
+ next_link = link->next ();
+ insn = link->insn ();
if (insn && sched_verbose > 6)
print_rtl_single (sched_dump, insn);
fprintf (sched_dump, ":prio=%d", INSN_PRIORITY (p[i]));
if (INSN_TICK (p[i]) > clock_var)
fprintf (sched_dump, ":delay=%d", INSN_TICK (p[i]) - clock_var);
+ if (sched_pressure == SCHED_PRESSURE_MODEL)
+ fprintf (sched_dump, ":idx=%d",
+ model_index (p[i]));
if (sched_pressure != SCHED_PRESSURE_NONE)
fprintf (sched_dump, ")");
}
/* Move INSN. Reemit notes if needed. Update CFG, if needed. */
static void
-move_insn (rtx_insn *insn, rtx last, rtx nt)
+move_insn (rtx_insn *insn, rtx_insn *last, rtx nt)
{
if (PREV_INSN (insn) != last)
{
return false;
}
+/* Functions to model cache auto-prefetcher.
+
+ Some of the CPUs have cache auto-prefetcher, which /seems/ to initiate
+ memory prefetches if it sees instructions with consequitive memory accesses
+ in the instruction stream. Details of such hardware units are not published,
+ so we can only guess what exactly is going on there.
+ In the scheduler, we model abstract auto-prefetcher. If there are memory
+ insns in the ready list (or the queue) that have same memory base, but
+ different offsets, then we delay the insns with larger offsets until insns
+ with smaller offsets get scheduled. If PARAM_SCHED_AUTOPREF_QUEUE_DEPTH
+ is "1", then we look at the ready list; if it is N>1, then we also look
+ through N-1 queue entries.
+ If the param is N>=0, then rank_for_schedule will consider auto-prefetching
+ among its heuristics.
+ Param value of "-1" disables modelling of the auto-prefetcher. */
+
+/* Initialize autoprefetcher model data for INSN. */
+static void
+autopref_multipass_init (const rtx_insn *insn, int write)
+{
+ autopref_multipass_data_t data = &INSN_AUTOPREF_MULTIPASS_DATA (insn)[write];
+
+ gcc_assert (data->status == AUTOPREF_MULTIPASS_DATA_UNINITIALIZED);
+ data->base = NULL_RTX;
+ data->offset = 0;
+ /* Set insn entry initialized, but not relevant for auto-prefetcher. */
+ data->status = AUTOPREF_MULTIPASS_DATA_IRRELEVANT;
+
+ rtx set = single_set (insn);
+ if (set == NULL_RTX)
+ return;
+
+ rtx mem = write ? SET_DEST (set) : SET_SRC (set);
+ if (!MEM_P (mem))
+ return;
+
+ struct address_info info;
+ decompose_mem_address (&info, mem);
+
+ /* TODO: Currently only (base+const) addressing is supported. */
+ if (info.base == NULL || !REG_P (*info.base)
+ || (info.disp != NULL && !CONST_INT_P (*info.disp)))
+ return;
+
+ /* This insn is relevant for auto-prefetcher. */
+ data->base = *info.base;
+ data->offset = info.disp ? INTVAL (*info.disp) : 0;
+ data->status = AUTOPREF_MULTIPASS_DATA_NORMAL;
+}
+
+/* Helper function for rank_for_schedule sorting. */
+static int
+autopref_rank_for_schedule (const rtx_insn *insn1, const rtx_insn *insn2)
+{
+ for (int write = 0; write < 2; ++write)
+ {
+ autopref_multipass_data_t data1
+ = &INSN_AUTOPREF_MULTIPASS_DATA (insn1)[write];
+ autopref_multipass_data_t data2
+ = &INSN_AUTOPREF_MULTIPASS_DATA (insn2)[write];
+
+ if (data1->status == AUTOPREF_MULTIPASS_DATA_UNINITIALIZED)
+ autopref_multipass_init (insn1, write);
+ if (data1->status == AUTOPREF_MULTIPASS_DATA_IRRELEVANT)
+ continue;
+
+ if (data2->status == AUTOPREF_MULTIPASS_DATA_UNINITIALIZED)
+ autopref_multipass_init (insn2, write);
+ if (data2->status == AUTOPREF_MULTIPASS_DATA_IRRELEVANT)
+ continue;
+
+ if (!rtx_equal_p (data1->base, data2->base))
+ continue;
+
+ return data1->offset - data2->offset;
+ }
+
+ return 0;
+}
+
+/* True if header of debug dump was printed. */
+static bool autopref_multipass_dfa_lookahead_guard_started_dump_p;
+
+/* Helper for autopref_multipass_dfa_lookahead_guard.
+ Return "1" if INSN1 should be delayed in favor of INSN2. */
+static int
+autopref_multipass_dfa_lookahead_guard_1 (const rtx_insn *insn1,
+ const rtx_insn *insn2, int write)
+{
+ autopref_multipass_data_t data1
+ = &INSN_AUTOPREF_MULTIPASS_DATA (insn1)[write];
+ autopref_multipass_data_t data2
+ = &INSN_AUTOPREF_MULTIPASS_DATA (insn2)[write];
+
+ if (data2->status == AUTOPREF_MULTIPASS_DATA_UNINITIALIZED)
+ autopref_multipass_init (insn2, write);
+ if (data2->status == AUTOPREF_MULTIPASS_DATA_IRRELEVANT)
+ return 0;
+
+ if (rtx_equal_p (data1->base, data2->base)
+ && data1->offset > data2->offset)
+ {
+ if (sched_verbose >= 2)
+ {
+ if (!autopref_multipass_dfa_lookahead_guard_started_dump_p)
+ {
+ fprintf (sched_dump,
+ ";;\t\tnot trying in max_issue due to autoprefetch "
+ "model: ");
+ autopref_multipass_dfa_lookahead_guard_started_dump_p = true;
+ }
+
+ fprintf (sched_dump, " %d(%d)", INSN_UID (insn1), INSN_UID (insn2));
+ }
+
+ return 1;
+ }
+
+ return 0;
+}
+
+/* General note:
+
+ We could have also hooked autoprefetcher model into
+ first_cycle_multipass_backtrack / first_cycle_multipass_issue hooks
+ to enable intelligent selection of "[r1+0]=r2; [r1+4]=r3" on the same cycle
+ (e.g., once "[r1+0]=r2" is issued in max_issue(), "[r1+4]=r3" gets
+ unblocked). We don't bother about this yet because target of interest
+ (ARM Cortex-A15) can issue only 1 memory operation per cycle. */
+
+/* Implementation of first_cycle_multipass_dfa_lookahead_guard hook.
+ Return "1" if INSN1 should not be considered in max_issue due to
+ auto-prefetcher considerations. */
+int
+autopref_multipass_dfa_lookahead_guard (rtx_insn *insn1, int ready_index)
+{
+ int r = 0;
+
+ if (PARAM_VALUE (PARAM_SCHED_AUTOPREF_QUEUE_DEPTH) <= 0)
+ return 0;
+
+ if (sched_verbose >= 2 && ready_index == 0)
+ autopref_multipass_dfa_lookahead_guard_started_dump_p = false;
+
+ for (int write = 0; write < 2; ++write)
+ {
+ autopref_multipass_data_t data1
+ = &INSN_AUTOPREF_MULTIPASS_DATA (insn1)[write];
+
+ if (data1->status == AUTOPREF_MULTIPASS_DATA_UNINITIALIZED)
+ autopref_multipass_init (insn1, write);
+ if (data1->status == AUTOPREF_MULTIPASS_DATA_IRRELEVANT)
+ continue;
+
+ if (ready_index == 0
+ && data1->status == AUTOPREF_MULTIPASS_DATA_DONT_DELAY)
+ /* We allow only a single delay on priviledged instructions.
+ Doing otherwise would cause infinite loop. */
+ {
+ if (sched_verbose >= 2)
+ {
+ if (!autopref_multipass_dfa_lookahead_guard_started_dump_p)
+ {
+ fprintf (sched_dump,
+ ";;\t\tnot trying in max_issue due to autoprefetch "
+ "model: ");
+ autopref_multipass_dfa_lookahead_guard_started_dump_p = true;
+ }
+
+ fprintf (sched_dump, " *%d*", INSN_UID (insn1));
+ }
+ continue;
+ }
+
+ for (int i2 = 0; i2 < ready.n_ready; ++i2)
+ {
+ rtx_insn *insn2 = get_ready_element (i2);
+ if (insn1 == insn2)
+ continue;
+ r = autopref_multipass_dfa_lookahead_guard_1 (insn1, insn2, write);
+ if (r)
+ {
+ if (ready_index == 0)
+ {
+ r = -1;
+ data1->status = AUTOPREF_MULTIPASS_DATA_DONT_DELAY;
+ }
+ goto finish;
+ }
+ }
+
+ if (PARAM_VALUE (PARAM_SCHED_AUTOPREF_QUEUE_DEPTH) == 1)
+ continue;
+
+ /* Everything from the current queue slot should have been moved to
+ the ready list. */
+ gcc_assert (insn_queue[NEXT_Q_AFTER (q_ptr, 0)] == NULL_RTX);
+
+ int n_stalls = PARAM_VALUE (PARAM_SCHED_AUTOPREF_QUEUE_DEPTH) - 1;
+ if (n_stalls > max_insn_queue_index)
+ n_stalls = max_insn_queue_index;
+
+ for (int stalls = 1; stalls <= n_stalls; ++stalls)
+ {
+ for (rtx_insn_list *link = insn_queue[NEXT_Q_AFTER (q_ptr, stalls)];
+ link != NULL_RTX;
+ link = link->next ())
+ {
+ rtx_insn *insn2 = link->insn ();
+ r = autopref_multipass_dfa_lookahead_guard_1 (insn1, insn2,
+ write);
+ if (r)
+ {
+ /* Queue INSN1 until INSN2 can issue. */
+ r = -stalls;
+ if (ready_index == 0)
+ data1->status = AUTOPREF_MULTIPASS_DATA_DONT_DELAY;
+ goto finish;
+ }
+ }
+ }
+ }
+
+ finish:
+ if (sched_verbose >= 2
+ && autopref_multipass_dfa_lookahead_guard_started_dump_p
+ && (ready_index == ready.n_ready - 1 || r < 0))
+ /* This does not /always/ trigger. We don't output EOL if the last
+ insn is not recognized (INSN_CODE < 0) and lookahead_guard is not
+ called. We can live with this. */
+ fprintf (sched_dump, "\n");
+
+ return r;
+}
+
/* Define type for target data used in multipass scheduling. */
#ifndef TARGET_SCHED_FIRST_CYCLE_MULTIPASS_DATA_T
# define TARGET_SCHED_FIRST_CYCLE_MULTIPASS_DATA_T int
could achieve DFA_LOOKAHEAD ** N , where N is the queue length. */
static int max_lookahead_tries;
-/* The following value is value of hook
- `first_cycle_multipass_dfa_lookahead' at the last call of
- `max_issue'. */
-static int cached_first_cycle_multipass_dfa_lookahead = 0;
-
-/* The following value is value of `issue_rate' at the last call of
- `sched_init'. */
-static int cached_issue_rate = 0;
-
/* The following function returns maximal (or close to maximal) number
of insns which can be issued on the same cycle and one of which
insns is insns with the best rank (the first insn in READY). To
struct choice_entry *top;
rtx_insn *insn;
+ if (sched_fusion)
+ return 0;
+
n_ready = ready->n_ready;
gcc_assert (dfa_lookahead >= 1 && privileged_n >= 0
&& privileged_n <= n_ready);
/* Init MAX_LOOKAHEAD_TRIES. */
- if (cached_first_cycle_multipass_dfa_lookahead != dfa_lookahead)
+ if (max_lookahead_tries == 0)
{
- cached_first_cycle_multipass_dfa_lookahead = dfa_lookahead;
max_lookahead_tries = 100;
for (i = 0; i < issue_rate; i++)
max_lookahead_tries *= dfa_lookahead;
choose_ready (struct ready_list *ready, bool first_cycle_insn_p,
rtx_insn **insn_ptr)
{
- int lookahead;
-
if (dbg_cnt (sched_insn) == false)
{
if (nonscheduled_insns_begin == NULL_RTX)
return -1;
}
- lookahead = 0;
-
- if (targetm.sched.first_cycle_multipass_dfa_lookahead)
- lookahead = targetm.sched.first_cycle_multipass_dfa_lookahead ();
- if (lookahead <= 0 || SCHED_GROUP_P (ready_element (ready, 0))
+ if (dfa_lookahead <= 0 || SCHED_GROUP_P (ready_element (ready, 0))
|| DEBUG_INSN_P (ready_element (ready, 0)))
{
if (targetm.sched.dispatch (NULL, IS_DISPATCH_ON))
bool sched_group_found = false;
int min_cost_group = 1;
+ if (sched_fusion)
+ return;
+
for (i = 0; i < ready.n_ready; i++)
{
rtx_insn *insn = ready_element (&ready, i);
if (SCHED_GROUP_P (insn) && cost > min_cost_group)
min_cost_group = cost;
ready_remove (&ready, i);
- queue_insn (insn, cost, reason);
+ /* Normally we'd want to queue INSN for COST cycles. However,
+ if SCHED_GROUP_P is set, then we must ensure that nothing
+ else comes between INSN and its predecessor. If there is
+ some other insn ready to fire on the next cycle, then that
+ invariant would be broken.
+
+ So when SCHED_GROUP_P is set, just queue this insn for a
+ single cycle. */
+ queue_insn (insn, SCHED_GROUP_P (insn) ? 1 : cost, reason);
if (i + 1 < n)
break;
}
/* Head/tail info for this block. */
rtx_insn *prev_head = current_sched_info->prev_head;
- rtx next_tail = current_sched_info->next_tail;
+ rtx_insn *next_tail = current_sched_info->next_tail;
rtx_insn *head = NEXT_INSN (prev_head);
rtx_insn *tail = PREV_INSN (next_tail);
if ((current_sched_info->flags & DONT_BREAK_DEPENDENCIES) == 0
- && sched_pressure != SCHED_PRESSURE_MODEL)
+ && sched_pressure != SCHED_PRESSURE_MODEL && !sched_fusion)
find_modifiable_mems (head, tail);
/* We used to have code to avoid getting parameters moved from hard
/* We start inserting insns after PREV_HEAD. */
last_scheduled_insn = prev_head;
- last_nondebug_scheduled_insn = NULL_RTX;
+ last_nondebug_scheduled_insn = NULL;
nonscheduled_insns_begin = NULL;
gcc_assert ((NOTE_P (last_scheduled_insn)
q_ptr = 0;
q_size = 0;
- insn_queue = XALLOCAVEC (rtx, max_insn_queue_index + 1);
+ insn_queue = XALLOCAVEC (rtx_insn_list *, max_insn_queue_index + 1);
memset (insn_queue, 0, (max_insn_queue_index + 1) * sizeof (rtx));
/* Start just before the beginning of time. */
in try_ready () (which is called through init_ready_list ()). */
(*current_sched_info->init_ready_list) ();
- if (sched_pressure == SCHED_PRESSURE_MODEL)
- model_start_schedule ();
+ if (sched_pressure)
+ sched_pressure_start_bb (*target_bb);
/* The algorithm is O(n^2) in the number of ready insns at any given
time in the worst case. Before reload we are more likely to have
if (!reload_completed
&& ready.n_ready - ready.n_debug > MAX_SCHED_READY_INSNS)
{
- ready_sort (&ready);
+ ready_sort_debug (&ready);
+ ready_sort_real (&ready);
/* Find first free-standing insn past MAX_SCHED_READY_INSNS.
If there are debug insns, we know they're first. */
if (sched_verbose >= 2)
{
fprintf (sched_dump,
- ";;\t\tReady list on entry: %d insns\n", ready.n_ready);
+ ";;\t\tReady list on entry: %d insns: ", ready.n_ready);
+ debug_ready_list (&ready);
fprintf (sched_dump,
";;\t\t before reload => truncated to %d insns\n", i);
}
{
memcpy (temp_state, curr_state, dfa_state_size);
cost = state_transition (curr_state, insn);
- if (sched_pressure != SCHED_PRESSURE_WEIGHTED)
+ if (sched_pressure != SCHED_PRESSURE_WEIGHTED && !sched_fusion)
gcc_assert (cost < 0);
if (memcmp (temp_state, curr_state, dfa_state_size) != 0)
cycle_issued_insns++;
if (ls.modulo_epilogue)
success = true;
end_schedule:
- if (!ls.first_cycle_insn_p)
+ if (!ls.first_cycle_insn_p || advance)
advance_one_cycle ();
perform_replacements_new_cycle ();
if (modulo_ii > 0)
}
for (i = 0; i <= max_insn_queue_index; i++)
{
- rtx link;
+ rtx_insn_list *link;
while ((link = insn_queue[i]) != NULL)
{
- rtx_insn *x = as_a <rtx_insn *> (XEXP (link, 0));
- insn_queue[i] = XEXP (link, 1);
+ rtx_insn *x = link->insn ();
+ insn_queue[i] = link->next ();
QUEUE_INDEX (x) = QUEUE_NOWHERE;
free_INSN_LIST_node (link);
resolve_dependencies (x);
if (q_size)
for (i = 0; i <= max_insn_queue_index; i++)
{
- rtx link;
- for (link = insn_queue[i]; link; link = XEXP (link, 1))
+ rtx_insn_list *link;
+ for (link = insn_queue[i]; link; link = link->next ())
{
rtx_insn *x;
- x = as_a <rtx_insn *> (XEXP (link, 0));
+ x = link->insn ();
QUEUE_INDEX (x) = QUEUE_NOWHERE;
TODO_SPEC (x) = HARD_DEP;
}
if (sched_verbose >= 2)
{
dump_insn_stream (head, tail);
- print_rank_for_schedule_stats (";; TOTAL ", &rank_for_schedule_stats);
+ print_rank_for_schedule_stats (";; TOTAL ", &rank_for_schedule_stats,
+ NULL);
}
fprintf (sched_dump, "\n");
saved_reg_live = BITMAP_ALLOC (NULL);
region_ref_regs = BITMAP_ALLOC (NULL);
}
+
+ /* Calculate number of CALL_USED_REGS in register classes that
+ we calculate register pressure for. */
+ for (int c = 0; c < ira_pressure_classes_num; ++c)
+ {
+ enum reg_class cl = ira_pressure_classes[c];
+
+ call_used_regs_num[cl] = 0;
+
+ for (int i = 0; i < ira_class_hard_regs_num[cl]; ++i)
+ if (call_used_regs[ira_class_hard_regs[cl][i]])
+ ++call_used_regs_num[cl];
+ }
}
}
sched_init (void)
{
/* Disable speculative loads in their presence if cc0 defined. */
-#ifdef HAVE_cc0
+ if (HAVE_cc0)
flag_schedule_speculative_load = 0;
-#endif
if (targetm.sched.dispatch (NULL, IS_DISPATCH_ON))
targetm.sched.dispatch_do (NULL, DISPATCH_INIT);
else
issue_rate = 1;
- if (cached_issue_rate != issue_rate)
- {
- cached_issue_rate = issue_rate;
- /* To invalidate max_lookahead_tries: */
- cached_first_cycle_multipass_dfa_lookahead = 0;
- }
-
- if (targetm.sched.first_cycle_multipass_dfa_lookahead)
+ if (targetm.sched.first_cycle_multipass_dfa_lookahead
+ /* Don't use max_issue with reg_pressure scheduling. Multipass
+ scheduling and reg_pressure scheduling undo each other's decisions. */
+ && sched_pressure == SCHED_PRESSURE_NONE)
dfa_lookahead = targetm.sched.first_cycle_multipass_dfa_lookahead ();
else
dfa_lookahead = 0;
+ /* Set to "0" so that we recalculate. */
+ max_lookahead_tries = 0;
+
if (targetm.sched.init_dfa_pre_cycle_insn)
targetm.sched.init_dfa_pre_cycle_insn ();
INSN_TICK (next) = tick;
delay = tick - clock_var;
- if (delay <= 0 || sched_pressure != SCHED_PRESSURE_NONE)
+ if (delay <= 0 || sched_pressure != SCHED_PRESSURE_NONE || sched_fusion)
delay = QUEUE_READY;
change_queue_index (next, delay);
Tries to add speculative dependencies of type FS between instructions
in deps_list L and TWIN. */
static void
-process_insn_forw_deps_be_in_spec (rtx insn, rtx_insn *twin, ds_t fs)
+process_insn_forw_deps_be_in_spec (rtx_insn *insn, rtx_insn *twin, ds_t fs)
{
sd_iterator_def sd_it;
dep_t dep;
ds_t ts;
sd_iterator_def sd_it;
dep_t dep;
- rtx twins = NULL;
+ rtx_insn_list *twins = NULL;
rtx_vec_t priorities_roots;
ts = TODO_SPEC (insn);
because that would make TWINS appear in the INSN_BACK_DEPS (INSN). */
while (twins)
{
- rtx twin;
+ rtx_insn *twin;
+ rtx_insn_list *next_node;
- twin = XEXP (twins, 0);
+ twin = twins->insn ();
{
dep_def _new_dep, *new_dep = &_new_dep;
- init_dep (new_dep, insn, as_a <rtx_insn *> (twin), REG_DEP_OUTPUT);
+ init_dep (new_dep, insn, twin, REG_DEP_OUTPUT);
sd_add_dep (new_dep, false);
}
- twin = XEXP (twins, 1);
+ next_node = twins->next ();
free_INSN_LIST_node (twins);
- twins = twin;
+ twins = next_node;
}
calc_priorities (priorities_roots);
Between these two blocks recovery blocks will be emitted. */
basic_block single, empty;
- rtx_insn *x;
- rtx label;
/* If the fallthrough edge to exit we've found is from the block we've
created before, don't do anything more. */
make_single_succ_edge (empty, EXIT_BLOCK_PTR_FOR_FN (cfun),
EDGE_FALLTHRU);
- label = block_label (empty);
- x = emit_jump_insn_after (gen_jump (label), BB_END (single));
+ rtx_code_label *label = block_label (empty);
+ rtx_jump_insn *x = emit_jump_insn_after (gen_jump (label),
+ BB_END (single));
JUMP_LABEL (x) = label;
LABEL_NUSES (label)++;
haifa_init_insn (x);
basic_block
sched_create_recovery_block (basic_block *before_recovery_ptr)
{
- rtx label;
rtx_insn *barrier;
basic_block rec;
barrier = get_last_bb_insn (before_recovery);
gcc_assert (BARRIER_P (barrier));
- label = emit_label_after (gen_label_rtx (), barrier);
+ rtx_insn *label = emit_label_after (gen_label_rtx (), barrier);
rec = create_basic_block (label, label, before_recovery);
sched_create_recovery_edges (basic_block first_bb, basic_block rec,
basic_block second_bb)
{
- rtx label;
- rtx jump;
int edge_flags;
/* This is fixing of incoming edge. */
edge_flags = 0;
make_edge (first_bb, rec, edge_flags);
- label = block_label (second_bb);
- jump = emit_jump_insn_after (gen_jump (label), BB_END (rec));
+ rtx_code_label *label = block_label (second_bb);
+ rtx_jump_insn *jump = emit_jump_insn_after (gen_jump (label), BB_END (rec));
JUMP_LABEL (jump) = label;
LABEL_NUSES (label)++;
fix_recovery_deps (basic_block rec)
{
rtx_insn *note, *insn, *jump;
- rtx ready_list = 0;
+ rtx_insn_list *ready_list = 0;
bitmap_head in_ready;
- rtx link;
+ rtx_insn_list *link;
bitmap_initialize (&in_ready, 0);
bitmap_clear (&in_ready);
/* Try to add instructions to the ready or queue list. */
- for (link = ready_list; link; link = XEXP (link, 1))
- try_ready (as_a <rtx_insn *> (XEXP (link, 0)));
+ for (link = ready_list; link; link = link->next ())
+ try_ready (link->insn ());
free_INSN_LIST_list (&ready_list);
/* Fixing jump's dependences. */
INSN_EXACT_TICK (insn) = INVALID_TICK;
INTER_TICK (insn) = INVALID_TICK;
TODO_SPEC (insn) = HARD_DEP;
+ INSN_AUTOPREF_MULTIPASS_DATA (insn)[0].status
+ = AUTOPREF_MULTIPASS_DATA_UNINITIALIZED;
+ INSN_AUTOPREF_MULTIPASS_DATA (insn)[1].status
+ = AUTOPREF_MULTIPASS_DATA_UNINITIALIZED;
}
}
/* Get number of ready's in the ready list. */
-rtx
+rtx_insn *
get_ready_element (int i)
{
return ready_element (&ready, i);