/* Instruction scheduling pass.
- Copyright (C) 1992, 93-96, 1997 Free Software Foundation, Inc.
+ Copyright (C) 1992, 93-97, 1998 Free Software Foundation, Inc.
Contributed by Michael Tiemann (tiemann@cygnus.com)
Enhanced by, and currently maintained by, Jim Wilson (wilson@cygnus.com)
beginning of basic blocks that have been scheduled. */
\f
#include "config.h"
-#include <stdio.h>
+#include "system.h"
#include "rtl.h"
#include "basic-block.h"
#include "regs.h"
#define UNIT_BLOCKED(B) ((B) >> (2 * BLOCKAGE_BITS))
#define BLOCKAGE_RANGE(B) \
(((((B) >> BLOCKAGE_BITS) & BLOCKAGE_MASK) << (HOST_BITS_PER_INT / 2)) \
- | (B) & BLOCKAGE_MASK)
+ | ((B) & BLOCKAGE_MASK))
/* Encodings of the `<name>_unit_blockage_range' function. */
#define MIN_BLOCKAGE_COST(R) ((R) >> (HOST_BITS_PER_INT / 2))
The transition (R->S) is implemented in the scheduling loop in
`schedule_block' when the best insn to schedule is chosen.
The transition (R->Q) is implemented in `schedule_select' when an
- insn is found to to have a function unit conflict with the already
+ insn is found to have a function unit conflict with the already
committed insns.
The transitions (P->R and P->Q) are implemented in `schedule_insn' as
insns move from the ready list to the scheduled list.
static void sched_analyze_insn PROTO((rtx, rtx, rtx));
static int sched_analyze PROTO((rtx, rtx));
static void sched_note_set PROTO((int, rtx, int));
-static int rank_for_schedule PROTO((rtx *, rtx *));
+static int rank_for_schedule PROTO((const GENERIC_PTR, const GENERIC_PTR));
static void swap_sort PROTO((rtx *, int));
static void queue_insn PROTO((rtx, int));
static int birthing_insn_p PROTO((rtx));
rtx prev, link;
int found = 0;
- for (prev = 0, link = LOG_LINKS (insn); link;
- prev = link, link = XEXP (link, 1))
+ for (prev = 0, link = LOG_LINKS (insn); link; link = XEXP (link, 1))
{
if (XEXP (link, 0) == elem)
{
+ RTX_INTEGRATED_P (link) = 1;
if (prev)
XEXP (prev, 1) = XEXP (link, 1);
else
LOG_LINKS (insn) = XEXP (link, 1);
found = 1;
}
+ else
+ prev = link;
}
if (! found)
int instance = unit;
int best_cost = actual_hazard_this_instance (unit, instance, insn,
clock, cost);
+#if MAX_MULTIPLICITY > 1
int this_cost;
-#if MAX_MULTIPLICITY > 1
if (best_cost > cost)
{
for (i = function_units[unit].multiplicity - 1; i > 0; i--)
{
rtx x = XEXP (prev, 0);
+ /* If this was a duplicate of a dependence we already deleted,
+ ignore it. */
+ if (RTX_INTEGRATED_P (prev))
+ continue;
+
/* A dependence pointing to a note or deleted insn is always
obsolete, because sched_analyze_insn will have created any
necessary new dependences which replace it. Notes and deleted
while (--i >= 0)
{
reg_last_uses[regno + i]
- = gen_rtx (INSN_LIST, VOIDmode,
- insn, reg_last_uses[regno + i]);
+ = gen_rtx_INSN_LIST (VOIDmode,
+ insn, reg_last_uses[regno + i]);
if (reg_last_sets[regno + i])
add_dependence (insn, reg_last_sets[regno + i], 0);
if ((call_used_regs[regno + i] || global_regs[regno + i])
else
{
reg_last_uses[regno]
- = gen_rtx (INSN_LIST, VOIDmode, insn, reg_last_uses[regno]);
+ = gen_rtx_INSN_LIST (VOIDmode, insn, reg_last_uses[regno]);
if (reg_last_sets[regno])
add_dependence (insn, reg_last_sets[regno], 0);
/* Add a pair of fake REG_NOTEs which we will later
convert back into a NOTE_INSN_SETJMP note. See
- reemit_notes for why we use a pair of of NOTEs. */
-
- REG_NOTES (insn) = gen_rtx (EXPR_LIST, REG_DEAD,
- GEN_INT (0),
- REG_NOTES (insn));
- REG_NOTES (insn) = gen_rtx (EXPR_LIST, REG_DEAD,
- GEN_INT (NOTE_INSN_SETJMP),
- REG_NOTES (insn));
+ reemit_notes for why we use a pair of NOTEs. */
+
+ REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_DEAD,
+ GEN_INT (0),
+ REG_NOTES (insn));
+ REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_DEAD,
+ GEN_INT (NOTE_INSN_SETJMP),
+ REG_NOTES (insn));
}
else
{
|| NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_END
|| NOTE_LINE_NUMBER (insn) == NOTE_INSN_EH_REGION_BEG
|| NOTE_LINE_NUMBER (insn) == NOTE_INSN_EH_REGION_END
+ || NOTE_LINE_NUMBER (insn) == NOTE_INSN_RANGE_START
+ || NOTE_LINE_NUMBER (insn) == NOTE_INSN_RANGE_END
|| (NOTE_LINE_NUMBER (insn) == NOTE_INSN_SETJMP
&& GET_CODE (PREV_INSN (insn)) != CALL_INSN)))
{
- loop_notes = gen_rtx (EXPR_LIST, REG_DEAD,
- GEN_INT (NOTE_BLOCK_NUMBER (insn)), loop_notes);
- loop_notes = gen_rtx (EXPR_LIST, REG_DEAD,
- GEN_INT (NOTE_LINE_NUMBER (insn)), loop_notes);
+ loop_notes = gen_rtx_EXPR_LIST (REG_DEAD,
+ GEN_INT (NOTE_BLOCK_NUMBER (insn)),
+ loop_notes);
+ loop_notes = gen_rtx_EXPR_LIST (REG_DEAD,
+ GEN_INT (NOTE_LINE_NUMBER (insn)),
+ loop_notes);
CONST_CALL_P (loop_notes) = CONST_CALL_P (insn);
}
static int
rank_for_schedule (x, y)
- rtx *x, *y;
+ const GENERIC_PTR x;
+ const GENERIC_PTR y;
{
- rtx tmp = *y;
- rtx tmp2 = *x;
+ rtx tmp = *(rtx *)y;
+ rtx tmp2 = *(rtx *)x;
rtx link;
int tmp_class, tmp2_class;
int value;
/* Choose the instruction with the highest priority, if different. */
- if (value = INSN_PRIORITY (tmp) - INSN_PRIORITY (tmp2))
+ if ((value = INSN_PRIORITY (tmp) - INSN_PRIORITY (tmp2)))
return value;
if (last_scheduled_insn)
else
tmp2_class = 2;
- if (value = tmp_class - tmp2_class)
+ if ((value = tmp_class - tmp2_class))
return value;
}
GET_MODE (XEXP (link, 0))));
while (reg_note_regs < regs_killed)
{
+ /* LINK might be zero if we killed more registers after scheduling
+ than before, and the last hard register we kill is actually
+ multiple hard regs. */
+ if (link == NULL_RTX)
+ abort ();
+
link = XEXP (link, 1);
reg_note_regs += (REGNO (XEXP (link, 0)) >= FIRST_PSEUDO_REGISTER ? 1
: HARD_REGNO_NREGS (REGNO (XEXP (link, 0)),
{
rtx temp_reg, temp_link;
- temp_reg = gen_rtx (REG, word_mode, 0);
+ temp_reg = gen_rtx_REG (word_mode, 0);
temp_link = rtx_alloc (EXPR_LIST);
PUT_REG_NOTE_KIND (temp_link, REG_DEAD);
XEXP (temp_link, 0) = temp_reg;
#endif
&& regno != STACK_POINTER_REGNUM)
{
- /* ??? It is perhaps a dead_or_set_p bug that it does
- not check for REG_UNUSED notes itself. This is necessary
- for the case where the SET_DEST is a subreg of regno, as
- dead_or_set_p handles subregs specially. */
- if (! all_needed && ! dead_or_set_p (insn, x)
- && ! find_reg_note (insn, REG_UNUSED, x))
+ if (! all_needed && ! dead_or_set_p (insn, x))
{
/* Check for the case where the register dying partially
overlaps the register set by this insn. */
i >= 0; i--)
if (! REGNO_REG_SET_P (old_live_regs, regno + i)
&& ! dead_or_set_regno_p (insn, regno + i))
- create_reg_dead_note (gen_rtx (REG,
- reg_raw_mode[regno + i],
- regno + i),
+ create_reg_dead_note (gen_rtx_REG (reg_raw_mode[regno + i],
+ regno + i),
insn);
}
}
return;
case SUBREG:
+ attach_deaths (SUBREG_REG (x), insn,
+ set_p && ((GET_MODE_SIZE (GET_MODE (SUBREG_REG (x)))
+ <= UNITS_PER_WORD)
+ || (GET_MODE_SIZE (GET_MODE (SUBREG_REG (x)))
+ == GET_MODE_SIZE (GET_MODE ((x))))));
+ return;
+
case STRICT_LOW_PART:
- /* These two cases preserve the value of SET_P, so handle them
- separately. */
- attach_deaths (XEXP (x, 0), insn, set_p);
+ attach_deaths (XEXP (x, 0), insn, 0);
return;
case ZERO_EXTRACT:
case SIGN_EXTRACT:
- /* This case preserves the value of SET_P for the first operand, but
- clears it for the other two. */
- attach_deaths (XEXP (x, 0), insn, set_p);
+ attach_deaths (XEXP (x, 0), insn, 0);
attach_deaths (XEXP (x, 1), insn, 0);
attach_deaths (XEXP (x, 2), insn, 0);
return;
else if (NOTE_LINE_NUMBER (insn) != NOTE_INSN_SETJMP
&& NOTE_LINE_NUMBER (insn) != NOTE_INSN_LOOP_BEG
&& NOTE_LINE_NUMBER (insn) != NOTE_INSN_LOOP_END
+ && NOTE_LINE_NUMBER (insn) != NOTE_INSN_RANGE_START
+ && NOTE_LINE_NUMBER (insn) != NOTE_INSN_RANGE_END
&& NOTE_LINE_NUMBER (insn) != NOTE_INSN_EH_REGION_BEG
&& NOTE_LINE_NUMBER (insn) != NOTE_INSN_EH_REGION_END)
{
reg_pending_sets_all = 0;
clear_units ();
+#if 0
+ /* We used to have code to avoid getting parameters moved from hard
+ argument registers into pseudos.
+
+ However, it was removed when it proved to be of marginal benefit and
+ caused problems because of different notions of what the "head" insn
+ was. */
+
/* Remove certain insns at the beginning from scheduling,
by advancing HEAD. */
head = NEXT_INSN (head);
}
}
+#endif
/* Don't include any notes or labels at the beginning of the
basic block, or notes at the ends of basic blocks. */
register int stalls;
for (stalls = 1; stalls < INSN_QUEUE_SIZE; stalls++)
- if (insn = insn_queue[NEXT_Q_AFTER (q_ptr, stalls)])
+ if ((insn = insn_queue[NEXT_Q_AFTER (q_ptr, stalls)]))
{
for (; insn; insn = NEXT_INSN (insn))
{
{
if (fmt[i] == 'e')
{
- if (tem = regno_use_in (regno, XEXP (x, i)))
+ if ((tem = regno_use_in (regno, XEXP (x, i))))
return tem;
}
else if (fmt[i] == 'E')
for (j = XVECLEN (x, i) - 1; j >= 0; j--)
- if (tem = regno_use_in (regno , XVECEXP (x, i, j)))
+ if ((tem = regno_use_in (regno , XVECEXP (x, i, j))))
return tem;
}
if (GET_CODE (dest) == REG)
{
+ /* If the original insn already used this register, we may not add new
+ notes for it. One example for a split that needs this test is
+ when a multi-word memory access with register-indirect addressing
+ is split into multiple memory accesses with auto-increment and
+ one adjusting add instruction for the address register. */
+ if (reg_referenced_p (dest, PATTERN (orig_insn)))
+ return;
for (tem = last; tem != insn; tem = PREV_INSN (tem))
{
if (GET_RTX_CLASS (GET_CODE (tem)) == 'i'
break;
case REG_WAS_0:
+ /* If the insn that set the register to 0 was deleted, this
+ note cannot be relied on any longer. The destination might
+ even have been moved to memory.
+ This was observed for SH4 with execute/920501-6.c compilation,
+ -O2 -fomit-frame-pointer -finline-functions . */
+ if (GET_CODE (XEXP (note, 0)) == NOTE
+ || INSN_DELETED_P (XEXP (note, 0)))
+ break;
/* This note applies to the dest of the original insn. Find the
first new insn that now has the same dest, and move the note
there. */
for (insn = first; insn != NEXT_INSN (last); insn = NEXT_INSN (insn))
if (GET_RTX_CLASS (GET_CODE (insn)) == 'i'
&& reg_mentioned_p (XEXP (note, 0), PATTERN (insn)))
- REG_NOTES (insn) = gen_rtx (EXPR_LIST, REG_LABEL,
- XEXP (note, 0), REG_NOTES (insn));
+ REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_LABEL,
+ XEXP (note, 0),
+ REG_NOTES (insn));
break;
case REG_CC_SETTER:
{
int max_uid = MAX_INSNS_PER_SPLIT * (get_max_uid () + 1);
int b;
- int i;
rtx insn;
/* Taking care of this degenerate case makes the rest of
/* Create an insn here so that we can hang dependencies off of it later. */
sched_before_next_call
- = gen_rtx (INSN, VOIDmode, 0, NULL_RTX, NULL_RTX,
- NULL_RTX, 0, NULL_RTX, NULL_RTX);
+ = gen_rtx_INSN (VOIDmode, 0, NULL_RTX, NULL_RTX,
+ NULL_RTX, 0, NULL_RTX, NULL_RTX);
/* Initialize the unused_*_lists. We can't use the ones left over from
the previous function, because gcc has freed that memory. We can use