Commit c46a37c4 by Richard Henderson Committed by Richard Henderson

haifa-sched.c (sched_reg_n_calls_crossed): Delete.

        * haifa-sched.c (sched_reg_n_calls_crossed): Delete.
        (sched_reg_live_length, sched_reg_basic_block): Delete.
        (current_block_num, bb_live_regs, old_live_regs): Delete.
        (dead_notes, struct sometimes): Delete.
        (sched_note_set, birthing_insn_p): Delete.
        (adjust_priority): Gut useless reg lifetime code.
        (create_reg_dead_note, attach_deaths): Delete.
        (attach_deaths_insn, new_sometimes_live): Delete.
        (finish_sometimes_live): Delete.
        (find_pre_sched_live, find_post_sched_live): Delete.
        (update_reg_usage): Delete.
        (find_insn_reg_weight): New, from corpse of find_pre_sched_live.
        (schedule_insns): Delete reg lifetime code.
        (sched_analyze): Use REG_SAVE_NOTE to stuff NOTE_INSN notes away.
        (unlink_other_notes): Adjust REG_NOTE commentary.
        (reemit_notes): Use REG_SAVE_NOTE.
        (schedule_block): Likewise.
        (schedule_region): Allocate bitmap of blocks in region.  Use
        count_or_remove_death_notes.  Use update_life_info.

        * rtl.h (REG_SAVE_NOTE): New.
        * rtl.c (reg_note_name): Update.

From-SVN: r29894
parent 715e7fbc
Sun Oct 10 16:37:01 1999 Richard Henderson <rth@cygnus.com>
* haifa-sched.c (sched_reg_n_calls_crossed): Delete.
(sched_reg_live_length, sched_reg_basic_block): Delete.
(current_block_num, bb_live_regs, old_live_regs): Delete.
(dead_notes, struct sometimes): Delete.
(sched_note_set, birthing_insn_p): Delete.
(adjust_priority): Gut useless reg lifetime code.
(create_reg_dead_note, attach_deaths): Delete.
(attach_deaths_insn, new_sometimes_live): Delete.
(finish_sometimes_live): Delete.
(find_pre_sched_live, find_post_sched_live): Delete.
(update_reg_usage): Delete.
(find_insn_reg_weight): New, from corpse of find_pre_sched_live.
(schedule_insns): Delete reg lifetime code.
(sched_analyze): Use REG_SAVE_NOTE to stuff NOTE_INSN notes away.
(unlink_other_notes): Adjust REG_NOTE commentary.
(reemit_notes): Use REG_SAVE_NOTE.
(schedule_block): Likewise.
(schedule_region): Allocate bitmap of blocks in region. Use
count_or_remove_death_notes. Use update_life_info.
* rtl.h (REG_SAVE_NOTE): New.
* rtl.c (reg_note_name): Update.
Sun Oct 10 16:14:16 1999 Richard Henderson <rth@cygnus.com>
* combine.c (refresh_blocks, need_refresh): New.
......
......@@ -232,22 +232,6 @@ fix_sched_param (param, val)
}
/* Arrays set up by scheduling for the same respective purposes as
similar-named arrays set up by flow analysis. We work with these
arrays during the scheduling pass so we can compare values against
unscheduled code.
Values of these arrays are copied at the end of this pass into the
arrays set up by flow analysis. */
static int *sched_reg_n_calls_crossed;
static int *sched_reg_live_length;
static int *sched_reg_basic_block;
/* We need to know the current block number during the post scheduling
update of live register information so that we can also update
REG_BASIC_BLOCK if a register changes blocks. */
static int current_block_num;
/* Element N is the next insn that sets (hard or pseudo) register
N within the current basic block; or zero, if there is no
such insn. Needed for new registers which may be introduced
......@@ -338,23 +322,6 @@ static rtx *line_note_head;
last element in the list. */
static rtx note_list;
/* Regsets telling whether a given register is live or dead before the last
scheduled insn. Must scan the instructions once before scheduling to
determine what registers are live or dead at the end of the block. */
static regset bb_live_regs;
/* Regset telling whether a given register is live after the insn currently
being scheduled. Before processing an insn, this is equal to bb_live_regs
above. This is used so that we can find registers that are newly born/dead
after processing an insn. */
static regset old_live_regs;
/* The chain of REG_DEAD notes. REG_DEAD notes are removed from all insns
during the initial scan and reused later. If there are not exactly as
many REG_DEAD notes in the post scheduled code as there were in the
prescheduled code then we trigger an abort because this indicates a bug. */
static rtx dead_notes;
/* Queues, etc. */
/* An instruction is ready to be scheduled when all insns preceding it
......@@ -414,16 +381,6 @@ static int q_size = 0;
static int *insn_tick;
#define INSN_TICK(INSN) (insn_tick[INSN_UID (INSN)])
/* Data structure for keeping track of register information
during that register's life. */
struct sometimes
{
int regno;
int live_length;
int calls_crossed;
};
/* Forward declarations. */
static void add_dependence PROTO ((rtx, rtx, enum reg_note));
static void remove_dependence PROTO ((rtx, rtx));
......@@ -444,20 +401,14 @@ static void sched_analyze_1 PROTO ((rtx, rtx));
static void sched_analyze_2 PROTO ((rtx, rtx));
static void sched_analyze_insn PROTO ((rtx, rtx, rtx));
static void sched_analyze PROTO ((rtx, rtx));
static void sched_note_set PROTO ((rtx, int));
static int rank_for_schedule PROTO ((const PTR, const PTR));
static void swap_sort PROTO ((rtx *, int));
static void queue_insn PROTO ((rtx, int));
static int schedule_insn PROTO ((rtx, rtx *, int, int));
static void create_reg_dead_note PROTO ((rtx, rtx));
static void attach_deaths PROTO ((rtx, rtx, int));
static void attach_deaths_insn PROTO ((rtx));
static int new_sometimes_live PROTO ((struct sometimes *, int, int));
static void finish_sometimes_live PROTO ((struct sometimes *, int));
static void find_insn_reg_weight PROTO ((int));
static int schedule_block PROTO ((int, int));
static char *safe_concat PROTO ((char *, char *, const char *));
static int insn_issue_delay PROTO ((rtx));
static int birthing_insn_p PROTO ((rtx));
static void adjust_priority PROTO ((rtx));
/* Mapping of insns to their original block prior to scheduling. */
......@@ -736,9 +687,6 @@ static rtx reemit_notes PROTO ((rtx, rtx));
static void get_block_head_tail PROTO ((int, rtx *, rtx *));
static void find_pre_sched_live PROTO ((int));
static void find_post_sched_live PROTO ((int));
static void update_reg_usage PROTO ((void));
static int queue_to_ready PROTO ((rtx [], int));
static void debug_ready_list PROTO ((rtx[], int));
......@@ -3867,13 +3815,13 @@ sched_analyze (head, tail)
}
reg_pending_sets_all = 1;
/* Add a pair of fake REG_NOTEs which we will later
/* Add a pair of REG_SAVE_NOTEs which we will later
convert back into a NOTE_INSN_SETJMP note. See
reemit_notes for why we use a pair of NOTEs. */
REG_NOTES (insn) = alloc_EXPR_LIST (REG_DEAD,
REG_NOTES (insn) = alloc_EXPR_LIST (REG_SAVE_NOTE,
GEN_INT (0),
REG_NOTES (insn));
REG_NOTES (insn) = alloc_EXPR_LIST (REG_DEAD,
REG_NOTES (insn) = alloc_EXPR_LIST (REG_SAVE_NOTE,
GEN_INT (NOTE_INSN_SETJMP),
REG_NOTES (insn));
}
......@@ -3926,9 +3874,9 @@ sched_analyze (head, tail)
&& (NOTE_LINE_NUMBER (insn) == NOTE_INSN_RANGE_START
|| NOTE_LINE_NUMBER (insn) == NOTE_INSN_RANGE_END))
{
loop_notes = alloc_EXPR_LIST (REG_DEAD, NOTE_RANGE_INFO (insn),
loop_notes = alloc_EXPR_LIST (REG_SAVE_NOTE, NOTE_RANGE_INFO (insn),
loop_notes);
loop_notes = alloc_EXPR_LIST (REG_DEAD,
loop_notes = alloc_EXPR_LIST (REG_SAVE_NOTE,
GEN_INT (NOTE_LINE_NUMBER (insn)),
loop_notes);
}
......@@ -3948,10 +3896,10 @@ sched_analyze (head, tail)
else
rtx_region = GEN_INT (0);
loop_notes = alloc_EXPR_LIST (REG_DEAD,
loop_notes = alloc_EXPR_LIST (REG_SAVE_NOTE,
rtx_region,
loop_notes);
loop_notes = alloc_EXPR_LIST (REG_DEAD,
loop_notes = alloc_EXPR_LIST (REG_SAVE_NOTE,
GEN_INT (NOTE_LINE_NUMBER (insn)),
loop_notes);
CONST_CALL_P (loop_notes) = CONST_CALL_P (insn);
......@@ -3963,102 +3911,6 @@ sched_analyze (head, tail)
abort ();
}
/* Called when we see a set of a register. If death is true, then we are
scanning backwards. Mark that register as unborn. If nobody says
otherwise, that is how things will remain. If death is false, then we
are scanning forwards. Mark that register as being born. */
static void
sched_note_set (x, death)
rtx x;
int death;
{
register int regno;
register rtx reg = SET_DEST (x);
int subreg_p = 0;
if (reg == 0)
return;
if (GET_CODE (reg) == PARALLEL
&& GET_MODE (reg) == BLKmode)
{
register int i;
for (i = XVECLEN (reg, 0) - 1; i >= 0; i--)
sched_note_set (XVECEXP (reg, 0, i), death);
return;
}
while (GET_CODE (reg) == SUBREG || GET_CODE (reg) == STRICT_LOW_PART
|| GET_CODE (reg) == SIGN_EXTRACT || GET_CODE (reg) == ZERO_EXTRACT)
{
/* Must treat modification of just one hardware register of a multi-reg
value or just a byte field of a register exactly the same way that
mark_set_1 in flow.c does, i.e. anything except a paradoxical subreg
does not kill the entire register. */
if (GET_CODE (reg) != SUBREG
|| REG_SIZE (SUBREG_REG (reg)) > REG_SIZE (reg))
subreg_p = 1;
reg = SUBREG_REG (reg);
}
if (GET_CODE (reg) != REG)
return;
/* Global registers are always live, so the code below does not apply
to them. */
regno = REGNO (reg);
if (regno >= FIRST_PSEUDO_REGISTER || !global_regs[regno])
{
if (death)
{
/* If we only set part of the register, then this set does not
kill it. */
if (subreg_p)
return;
/* Try killing this register. */
if (regno < FIRST_PSEUDO_REGISTER)
{
int j = HARD_REGNO_NREGS (regno, GET_MODE (reg));
while (--j >= 0)
{
CLEAR_REGNO_REG_SET (bb_live_regs, regno + j);
}
}
else
{
/* Recompute REG_BASIC_BLOCK as we update all the other
dataflow information. */
if (sched_reg_basic_block[regno] == REG_BLOCK_UNKNOWN)
sched_reg_basic_block[regno] = current_block_num;
else if (sched_reg_basic_block[regno] != current_block_num)
sched_reg_basic_block[regno] = REG_BLOCK_GLOBAL;
CLEAR_REGNO_REG_SET (bb_live_regs, regno);
}
}
else
{
/* Make the register live again. */
if (regno < FIRST_PSEUDO_REGISTER)
{
int j = HARD_REGNO_NREGS (regno, GET_MODE (reg));
while (--j >= 0)
{
SET_REGNO_REG_SET (bb_live_regs, regno + j);
}
}
else
{
SET_REGNO_REG_SET (bb_live_regs, regno);
}
}
}
}
/* Macros and functions for keeping the priority queue sorted, and
dealing with queueing and dequeueing of instructions. */
......@@ -4209,104 +4061,21 @@ queue_insn (insn, n_cycles)
}
/* Return nonzero if PAT is the pattern of an insn which makes a
register live. */
HAIFA_INLINE static int
birthing_insn_p (pat)
rtx pat;
{
int j;
if (reload_completed == 1)
return 0;
if (GET_CODE (pat) == SET
&& (GET_CODE (SET_DEST (pat)) == REG
|| (GET_CODE (SET_DEST (pat)) == PARALLEL
&& GET_MODE (SET_DEST (pat)) == BLKmode)))
{
rtx dest = SET_DEST (pat);
int i;
/* It would be more accurate to use refers_to_regno_p or
reg_mentioned_p to determine when the dest is not live before this
insn. */
if (GET_CODE (dest) == REG)
{
i = REGNO (dest);
if (REGNO_REG_SET_P (bb_live_regs, i))
return (REG_N_SETS (i) == 1);
}
else
{
for (i = XVECLEN (dest, 0) - 1; i >= 0; i--)
{
int regno = REGNO (SET_DEST (XVECEXP (dest, 0, i)));
if (REGNO_REG_SET_P (bb_live_regs, regno))
return (REG_N_SETS (regno) == 1);
}
}
return 0;
}
if (GET_CODE (pat) == PARALLEL)
{
for (j = 0; j < XVECLEN (pat, 0); j++)
if (birthing_insn_p (XVECEXP (pat, 0, j)))
return 1;
}
return 0;
}
/* PREV is an insn that is ready to execute. Adjust its priority if that
will help shorten register lifetimes. */
will help shorten or lengthen register lifetimes as appropriate. Also
provide a hook for the target to tweek itself. */
HAIFA_INLINE static void
adjust_priority (prev)
rtx prev;
rtx prev ATTRIBUTE_UNUSED;
{
/* Trying to shorten register lives after reload has completed
is useless and wrong. It gives inaccurate schedules. */
if (reload_completed == 0)
{
rtx note;
int n_deaths = 0;
/* ??? This code has no effect, because REG_DEAD notes are removed
before we ever get here. */
for (note = REG_NOTES (prev); note; note = XEXP (note, 1))
if (REG_NOTE_KIND (note) == REG_DEAD)
n_deaths += 1;
/* Defer scheduling insns which kill registers, since that
shortens register lives. Prefer scheduling insns which
make registers live for the same reason. */
switch (n_deaths)
{
default:
INSN_PRIORITY (prev) >>= 3;
break;
case 3:
INSN_PRIORITY (prev) >>= 2;
break;
case 2:
case 1:
INSN_PRIORITY (prev) >>= 1;
break;
case 0:
if (birthing_insn_p (PATTERN (prev)))
{
int max = max_priority;
/* ??? There used to be code here to try and estimate how an insn
affected register lifetimes, but it did it by looking at REG_DEAD
notes, which we removed in schedule_region. Nor did it try to
take into account register pressure or anything useful like that.
if (max > INSN_PRIORITY (prev))
INSN_PRIORITY (prev) = max;
}
break;
}
}
Revisit when we have a machine model to work with and not before. */
/* That said, a target might have it's own reasons for adjusting
priority after reload. */
#ifdef ADJUST_PRIORITY
ADJUST_PRIORITY (prev);
#endif
......@@ -4415,334 +4184,6 @@ schedule_insn (insn, ready, n_ready, clock)
return n_ready;
}
/* Add a REG_DEAD note for REG to INSN, reusing a REG_DEAD note from the
dead_notes list. */
static void
create_reg_dead_note (reg, insn)
rtx reg, insn;
{
rtx link;
/* The number of registers killed after scheduling must be the same as the
number of registers killed before scheduling. The number of REG_DEAD
notes may not be conserved, i.e. two SImode hard register REG_DEAD notes
might become one DImode hard register REG_DEAD note, but the number of
registers killed will be conserved.
We carefully remove REG_DEAD notes from the dead_notes list, so that
there will be none left at the end. If we run out early, then there
is a bug somewhere in flow, combine and/or sched. */
if (dead_notes == 0)
{
if (current_nr_blocks <= 1)
abort ();
else
link = alloc_EXPR_LIST (REG_DEAD, NULL_RTX, NULL_RTX);
}
else
{
/* Number of regs killed by REG. */
int regs_killed = (REGNO (reg) >= FIRST_PSEUDO_REGISTER ? 1
: HARD_REGNO_NREGS (REGNO (reg), GET_MODE (reg)));
/* Number of regs killed by REG_DEAD notes taken off the list. */
int reg_note_regs;
link = dead_notes;
reg_note_regs = (REGNO (XEXP (link, 0)) >= FIRST_PSEUDO_REGISTER ? 1
: HARD_REGNO_NREGS (REGNO (XEXP (link, 0)),
GET_MODE (XEXP (link, 0))));
while (reg_note_regs < regs_killed)
{
link = XEXP (link, 1);
/* 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.
This is normal for interblock scheduling, so deal with it in
that case, else abort. */
if (link == NULL_RTX && current_nr_blocks <= 1)
abort ();
else if (link == NULL_RTX)
link = alloc_EXPR_LIST (REG_DEAD, gen_rtx_REG (word_mode, 0),
NULL_RTX);
reg_note_regs += (REGNO (XEXP (link, 0)) >= FIRST_PSEUDO_REGISTER ? 1
: HARD_REGNO_NREGS (REGNO (XEXP (link, 0)),
GET_MODE (XEXP (link, 0))));
}
dead_notes = XEXP (link, 1);
/* If we took too many regs kills off, put the extra ones back. */
while (reg_note_regs > regs_killed)
{
rtx temp_reg, temp_link;
temp_reg = gen_rtx_REG (word_mode, 0);
temp_link = alloc_EXPR_LIST (REG_DEAD, temp_reg, dead_notes);
dead_notes = temp_link;
reg_note_regs--;
}
}
XEXP (link, 0) = reg;
XEXP (link, 1) = REG_NOTES (insn);
REG_NOTES (insn) = link;
}
/* Subroutine on attach_deaths_insn--handles the recursive search
through INSN. If SET_P is true, then x is being modified by the insn. */
static void
attach_deaths (x, insn, set_p)
rtx x;
rtx insn;
int set_p;
{
register int i;
register int j;
register enum rtx_code code;
register const char *fmt;
if (x == 0)
return;
code = GET_CODE (x);
switch (code)
{
case CONST_INT:
case CONST_DOUBLE:
case LABEL_REF:
case SYMBOL_REF:
case CONST:
case CODE_LABEL:
case PC:
case CC0:
/* Get rid of the easy cases first. */
return;
case REG:
{
/* If the register dies in this insn, queue that note, and mark
this register as needing to die. */
/* This code is very similar to mark_used_1 (if set_p is false)
and mark_set_1 (if set_p is true) in flow.c. */
register int regno;
int some_needed;
int all_needed;
if (set_p)
return;
regno = REGNO (x);
all_needed = some_needed = REGNO_REG_SET_P (old_live_regs, regno);
if (regno < FIRST_PSEUDO_REGISTER)
{
int n;
n = HARD_REGNO_NREGS (regno, GET_MODE (x));
while (--n > 0)
{
int needed = (REGNO_REG_SET_P (old_live_regs, regno + n));
some_needed |= needed;
all_needed &= needed;
}
}
/* If it wasn't live before we started, then add a REG_DEAD note.
We must check the previous lifetime info not the current info,
because we may have to execute this code several times, e.g.
once for a clobber (which doesn't add a note) and later
for a use (which does add a note).
Always make the register live. We must do this even if it was
live before, because this may be an insn which sets and uses
the same register, in which case the register has already been
killed, so we must make it live again.
Global registers are always live, and should never have a REG_DEAD
note added for them, so none of the code below applies to them. */
if (regno >= FIRST_PSEUDO_REGISTER || ! global_regs[regno])
{
/* Never add REG_DEAD notes for STACK_POINTER_REGNUM
since it's always considered to be live. Similarly
for FRAME_POINTER_REGNUM if a frame pointer is needed
and for ARG_POINTER_REGNUM if it is fixed. */
if (! (regno == FRAME_POINTER_REGNUM
&& (! reload_completed || frame_pointer_needed))
#if HARD_FRAME_POINTER_REGNUM != FRAME_POINTER_REGNUM
&& ! (regno == HARD_FRAME_POINTER_REGNUM
&& (! reload_completed || frame_pointer_needed))
#endif
#if ARG_POINTER_REGNUM != FRAME_POINTER_REGNUM
&& ! (regno == ARG_POINTER_REGNUM && fixed_regs[regno])
#endif
&& regno != STACK_POINTER_REGNUM)
{
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. */
if (regno < FIRST_PSEUDO_REGISTER
&& HARD_REGNO_NREGS (regno, GET_MODE (x)) > 1)
{
int n = HARD_REGNO_NREGS (regno, GET_MODE (x));
while (--n >= 0)
some_needed |= dead_or_set_regno_p (insn, regno + n);
}
/* If none of the words in X is needed, make a REG_DEAD
note. Otherwise, we must make partial REG_DEAD
notes. */
if (! some_needed)
create_reg_dead_note (x, insn);
else
{
int i;
/* Don't make a REG_DEAD note for a part of a
register that is set in the insn. */
for (i = HARD_REGNO_NREGS (regno, GET_MODE (x)) - 1;
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),
insn);
}
}
}
if (regno < FIRST_PSEUDO_REGISTER)
{
int j = HARD_REGNO_NREGS (regno, GET_MODE (x));
while (--j >= 0)
{
SET_REGNO_REG_SET (bb_live_regs, regno + j);
}
}
else
{
/* Recompute REG_BASIC_BLOCK as we update all the other
dataflow information. */
if (sched_reg_basic_block[regno] == REG_BLOCK_UNKNOWN)
sched_reg_basic_block[regno] = current_block_num;
else if (sched_reg_basic_block[regno] != current_block_num)
sched_reg_basic_block[regno] = REG_BLOCK_GLOBAL;
SET_REGNO_REG_SET (bb_live_regs, regno);
}
}
return;
}
case MEM:
/* Handle tail-recursive case. */
attach_deaths (XEXP (x, 0), insn, 0);
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:
attach_deaths (XEXP (x, 0), insn, 0);
return;
case ZERO_EXTRACT:
case SIGN_EXTRACT:
attach_deaths (XEXP (x, 0), insn, 0);
attach_deaths (XEXP (x, 1), insn, 0);
attach_deaths (XEXP (x, 2), insn, 0);
return;
case PARALLEL:
if (set_p
&& GET_MODE (x) == BLKmode)
{
for (i = XVECLEN (x, 0) - 1; i >= 0; i--)
attach_deaths (SET_DEST (XVECEXP (x, 0, i)), insn, 1);
return;
}
/* Fallthrough. */
default:
/* Other cases: walk the insn. */
fmt = GET_RTX_FORMAT (code);
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
{
if (fmt[i] == 'e')
attach_deaths (XEXP (x, i), insn, 0);
else if (fmt[i] == 'E')
for (j = 0; j < XVECLEN (x, i); j++)
attach_deaths (XVECEXP (x, i, j), insn, 0);
}
}
}
/* After INSN has executed, add register death notes for each register
that is dead after INSN. */
static void
attach_deaths_insn (insn)
rtx insn;
{
rtx x = PATTERN (insn);
register RTX_CODE code = GET_CODE (x);
rtx link;
if (code == SET)
{
attach_deaths (SET_SRC (x), insn, 0);
/* A register might die here even if it is the destination, e.g.
it is the target of a volatile read and is otherwise unused.
Hence we must always call attach_deaths for the SET_DEST. */
attach_deaths (SET_DEST (x), insn, 1);
}
else if (code == PARALLEL)
{
register int i;
for (i = XVECLEN (x, 0) - 1; i >= 0; i--)
{
code = GET_CODE (XVECEXP (x, 0, i));
if (code == SET)
{
attach_deaths (SET_SRC (XVECEXP (x, 0, i)), insn, 0);
attach_deaths (SET_DEST (XVECEXP (x, 0, i)), insn, 1);
}
/* Flow does not add REG_DEAD notes to registers that die in
clobbers, so we can't either. */
else if (code != CLOBBER)
attach_deaths (XVECEXP (x, 0, i), insn, 0);
}
}
/* If this is a CLOBBER, only add REG_DEAD notes to registers inside a
MEM being clobbered, just like flow. */
else if (code == CLOBBER && GET_CODE (XEXP (x, 0)) == MEM)
attach_deaths (XEXP (XEXP (x, 0), 0), insn, 0);
/* Otherwise don't add a death note to things being clobbered. */
else if (code != CLOBBER)
attach_deaths (x, insn, 0);
/* Make death notes for things used in the called function. */
if (GET_CODE (insn) == CALL_INSN)
for (link = CALL_INSN_FUNCTION_USAGE (insn); link; link = XEXP (link, 1))
attach_deaths (XEXP (XEXP (link, 0), 0), insn,
GET_CODE (XEXP (link, 0)) == CLOBBER);
}
/* Functions for handling of notes. */
/* Delete notes beginning with INSN and put them in the chain
......@@ -4764,10 +4205,7 @@ unlink_other_notes (insn, tail)
if (next)
PREV_INSN (next) = prev;
/* Don't save away NOTE_INSN_SETJMPs, because they must remain
immediately after the call they follow. We use a fake
(REG_DEAD (const_int -1)) note to remember them.
Likewise with NOTE_INSN_{LOOP,EHREGION}_{BEG, END}. */
/* See sched_analyze to see how these are handled. */
if (NOTE_LINE_NUMBER (insn) != NOTE_INSN_SETJMP
&& NOTE_LINE_NUMBER (insn) != NOTE_INSN_LOOP_BEG
&& NOTE_LINE_NUMBER (insn) != NOTE_INSN_LOOP_END
......@@ -5081,459 +4519,55 @@ rm_other_notes (head, tail)
}
}
/* Constructor for `sometimes' data structure. */
static int
new_sometimes_live (regs_sometimes_live, regno, sometimes_max)
struct sometimes *regs_sometimes_live;
int regno;
int sometimes_max;
{
register struct sometimes *p;
/* There should never be a register greater than max_regno here. If there
is, it means that a define_split has created a new pseudo reg. This
is not allowed, since there will not be flow info available for any
new register, so catch the error here. */
if (regno >= max_regno)
abort ();
p = &regs_sometimes_live[sometimes_max];
p->regno = regno;
p->live_length = 0;
p->calls_crossed = 0;
sometimes_max++;
return sometimes_max;
}
/* Count lengths of all regs we are currently tracking,
and find new registers no longer live. */
static void
finish_sometimes_live (regs_sometimes_live, sometimes_max)
struct sometimes *regs_sometimes_live;
int sometimes_max;
{
int i;
for (i = 0; i < sometimes_max; i++)
{
register struct sometimes *p = &regs_sometimes_live[i];
int regno = p->regno;
sched_reg_live_length[regno] += p->live_length;
sched_reg_n_calls_crossed[regno] += p->calls_crossed;
}
}
/* Functions for computation of registers live/usage info. */
/* It is assumed that prior to scheduling BASIC_BLOCK (b)->global_live_at_start
contains the registers that are alive at the entry to b.
Two passes follow: The first pass is performed before the scheduling
of a region. It scans each block of the region forward, computing
the set of registers alive at the end of the basic block and
discard REG_DEAD notes (done by find_pre_sched_live ()).
The second path is invoked after scheduling all region blocks.
It scans each block of the region backward, a block being traversed
only after its succesors in the region. When the set of registers
live at the end of a basic block may be changed by the scheduling
(this may happen for multiple blocks region), it is computed as
the union of the registers live at the start of its succesors.
The last-use information is updated by inserting REG_DEAD notes.
(done by find_post_sched_live ()) */
/* Scan all the insns to be scheduled, removing register death notes.
Register death notes end up in DEAD_NOTES.
Recreate the register life information for the end of this basic
block. */
/* Calculate INSN_REG_WEIGHT for all insns of a block. */
static void
find_pre_sched_live (bb)
int bb;
find_insn_reg_weight (bb)
int bb;
{
rtx insn, next_tail, head, tail;
int b = BB_TO_BLOCK (bb);
get_block_head_tail (bb, &head, &tail);
COPY_REG_SET (bb_live_regs, BASIC_BLOCK (b)->global_live_at_start);
next_tail = NEXT_INSN (tail);
for (insn = head; insn != next_tail; insn = NEXT_INSN (insn))
{
rtx prev, next, link;
int reg_weight = 0;
rtx x;
/* Handle register life information. */
if (GET_RTX_CLASS (GET_CODE (insn)) == 'i')
{
/* See if the register gets born here. */
/* We must check for registers being born before we check for
registers dying. It is possible for a register to be born and
die in the same insn, e.g. reading from a volatile memory
location into an otherwise unused register. Such a register
must be marked as dead after this insn. */
if (GET_CODE (PATTERN (insn)) == SET
|| GET_CODE (PATTERN (insn)) == CLOBBER)
{
sched_note_set (PATTERN (insn), 0);
reg_weight++;
}
else if (GET_CODE (PATTERN (insn)) == PARALLEL)
{
int j;
for (j = XVECLEN (PATTERN (insn), 0) - 1; j >= 0; j--)
if (GET_CODE (XVECEXP (PATTERN (insn), 0, j)) == SET
|| GET_CODE (XVECEXP (PATTERN (insn), 0, j)) == CLOBBER)
{
sched_note_set (XVECEXP (PATTERN (insn), 0, j), 0);
reg_weight++;
}
/* ??? This code is obsolete and should be deleted. It
is harmless though, so we will leave it in for now. */
for (j = XVECLEN (PATTERN (insn), 0) - 1; j >= 0; j--)
if (GET_CODE (XVECEXP (PATTERN (insn), 0, j)) == USE)
sched_note_set (XVECEXP (PATTERN (insn), 0, j), 0);
}
/* Each call cobbers (makes live) all call-clobbered regs
that are not global or fixed. Note that the function-value
reg is a call_clobbered reg. */
if (GET_CODE (insn) == CALL_INSN)
{
int j;
for (j = 0; j < FIRST_PSEUDO_REGISTER; j++)
if (call_used_regs[j] && !global_regs[j]
&& ! fixed_regs[j])
{
SET_REGNO_REG_SET (bb_live_regs, j);
}
}
/* Need to know what registers this insn kills. */
for (prev = 0, link = REG_NOTES (insn); link; link = next)
{
next = XEXP (link, 1);
if ((REG_NOTE_KIND (link) == REG_DEAD
|| REG_NOTE_KIND (link) == REG_UNUSED)
/* Verify that the REG_NOTE has a valid value. */
&& GET_CODE (XEXP (link, 0)) == REG)
{
register int regno = REGNO (XEXP (link, 0));
reg_weight--;
/* Only unlink REG_DEAD notes; leave REG_UNUSED notes
alone. */
if (REG_NOTE_KIND (link) == REG_DEAD)
{
if (prev)
XEXP (prev, 1) = next;
else
REG_NOTES (insn) = next;
XEXP (link, 1) = dead_notes;
dead_notes = link;
}
else
prev = link;
if (regno < FIRST_PSEUDO_REGISTER)
{
int j = HARD_REGNO_NREGS (regno,
GET_MODE (XEXP (link, 0)));
while (--j >= 0)
{
CLEAR_REGNO_REG_SET (bb_live_regs, regno+j);
}
}
else
{
CLEAR_REGNO_REG_SET (bb_live_regs, regno);
}
}
else
prev = link;
}
}
INSN_REG_WEIGHT (insn) = reg_weight;
}
}
/* Update register life and usage information for block bb
after scheduling. Put register dead notes back in the code. */
static void
find_post_sched_live (bb)
int bb;
{
int sometimes_max;
int j, i;
int b;
rtx insn;
rtx head, tail, prev_head, next_tail;
register struct sometimes *regs_sometimes_live;
b = BB_TO_BLOCK (bb);
/* Compute live regs at the end of bb as a function of its successors. */
if (current_nr_blocks > 1)
{
int e;
int first_edge;
first_edge = e = OUT_EDGES (b);
CLEAR_REG_SET (bb_live_regs);
if (e)
do
{
int b_succ;
b_succ = TO_BLOCK (e);
IOR_REG_SET (bb_live_regs,
BASIC_BLOCK (b_succ)->global_live_at_start);
e = NEXT_OUT (e);
}
while (e != first_edge);
}
get_block_head_tail (bb, &head, &tail);
next_tail = NEXT_INSN (tail);
prev_head = PREV_INSN (head);
EXECUTE_IF_SET_IN_REG_SET (bb_live_regs, FIRST_PSEUDO_REGISTER, i,
{
sched_reg_basic_block[i] = REG_BLOCK_GLOBAL;
});
/* If the block is empty, same regs are alive at its end and its start.
since this is not guaranteed after interblock scheduling, make sure they
are truly identical. */
if (NEXT_INSN (prev_head) == tail
&& (GET_RTX_CLASS (GET_CODE (tail)) != 'i'))
{
if (current_nr_blocks > 1)
COPY_REG_SET (BASIC_BLOCK (b)->global_live_at_start, bb_live_regs);
return;
}
b = BB_TO_BLOCK (bb);
current_block_num = b;
/* Keep track of register lives. */
old_live_regs = ALLOCA_REG_SET ();
regs_sometimes_live
= (struct sometimes *) alloca (max_regno * sizeof (struct sometimes));
sometimes_max = 0;
/* Initiate "sometimes" data, starting with registers live at end. */
sometimes_max = 0;
COPY_REG_SET (old_live_regs, bb_live_regs);
EXECUTE_IF_SET_IN_REG_SET (bb_live_regs, 0, j,
{
sometimes_max
= new_sometimes_live (regs_sometimes_live,
j, sometimes_max);
});
/* Scan insns back, computing regs live info. */
for (insn = tail; insn != prev_head; insn = PREV_INSN (insn))
{
/* First we kill registers set by this insn, and then we
make registers used by this insn live. This is the opposite
order used above because we are traversing the instructions
backwards. */
/* Strictly speaking, we should scan REG_UNUSED notes and make
every register mentioned there live, however, we will just
kill them again immediately below, so there doesn't seem to
be any reason why we bother to do this. */
/* See if this is the last notice we must take of a register. */
if (GET_RTX_CLASS (GET_CODE (insn)) != 'i')
continue;
if (GET_CODE (PATTERN (insn)) == SET
|| GET_CODE (PATTERN (insn)) == CLOBBER)
sched_note_set (PATTERN (insn), 1);
else if (GET_CODE (PATTERN (insn)) == PARALLEL)
/* Increment weight for each register born here. */
x = PATTERN (insn);
if ((GET_CODE (x) == SET || GET_CODE (x) == CLOBBER)
&& register_operand (SET_DEST (x), VOIDmode))
reg_weight++;
else if (GET_CODE (x) == PARALLEL)
{
for (j = XVECLEN (PATTERN (insn), 0) - 1; j >= 0; j--)
if (GET_CODE (XVECEXP (PATTERN (insn), 0, j)) == SET
|| GET_CODE (XVECEXP (PATTERN (insn), 0, j)) == CLOBBER)
sched_note_set (XVECEXP (PATTERN (insn), 0, j), 1);
int j;
for (j = XVECLEN (x, 0) - 1; j >= 0; j--)
{
x = XVECEXP (PATTERN (insn), 0, j);
if ((GET_CODE (x) == SET || GET_CODE (x) == CLOBBER)
&& register_operand (SET_DEST (x), VOIDmode))
reg_weight++;
}
}
/* This code keeps life analysis information up to date. */
if (GET_CODE (insn) == CALL_INSN)
/* Decrement weight for each register that dies here. */
for (x = REG_NOTES (insn); x; x = XEXP (x, 1))
{
register struct sometimes *p;
/* A call kills all call used registers that are not
global or fixed, except for those mentioned in the call
pattern which will be made live again later. */
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
if (call_used_regs[i] && ! global_regs[i]
&& ! fixed_regs[i])
{
CLEAR_REGNO_REG_SET (bb_live_regs, i);
}
/* Regs live at the time of a call instruction must not
go in a register clobbered by calls. Record this for
all regs now live. Note that insns which are born or
die in a call do not cross a call, so this must be done
after the killings (above) and before the births
(below). */
p = regs_sometimes_live;
for (i = 0; i < sometimes_max; i++, p++)
if (REGNO_REG_SET_P (bb_live_regs, p->regno))
p->calls_crossed += 1;
if (REG_NOTE_KIND (x) == REG_DEAD
|| REG_NOTE_KIND (x) == REG_UNUSED)
reg_weight--;
}
/* Make every register used live, and add REG_DEAD notes for
registers which were not live before we started. */
attach_deaths_insn (insn);
/* Find registers now made live by that instruction. */
EXECUTE_IF_AND_COMPL_IN_REG_SET (bb_live_regs, old_live_regs, 0, j,
{
sometimes_max
= new_sometimes_live (regs_sometimes_live,
j, sometimes_max);
});
IOR_REG_SET (old_live_regs, bb_live_regs);
/* Count lengths of all regs we are worrying about now,
and handle registers no longer live. */
for (i = 0; i < sometimes_max; i++)
{
register struct sometimes *p = &regs_sometimes_live[i];
int regno = p->regno;
p->live_length += 1;
if (!REGNO_REG_SET_P (bb_live_regs, regno))
{
/* This is the end of one of this register's lifetime
segments. Save the lifetime info collected so far,
and clear its bit in the old_live_regs entry. */
sched_reg_live_length[regno] += p->live_length;
sched_reg_n_calls_crossed[regno] += p->calls_crossed;
CLEAR_REGNO_REG_SET (old_live_regs, p->regno);
/* Delete the reg_sometimes_live entry for this reg by
copying the last entry over top of it. */
*p = regs_sometimes_live[--sometimes_max];
/* ...and decrement i so that this newly copied entry
will be processed. */
i--;
}
}
INSN_REG_WEIGHT (insn) = reg_weight;
}
finish_sometimes_live (regs_sometimes_live, sometimes_max);
/* In interblock scheduling, global_live_at_start may have changed. */
if (current_nr_blocks > 1)
COPY_REG_SET (BASIC_BLOCK (b)->global_live_at_start, bb_live_regs);
FREE_REG_SET (old_live_regs);
} /* find_post_sched_live */
/* After scheduling the subroutine, restore information about uses of
registers. */
static void
update_reg_usage ()
{
int regno;
if (n_basic_blocks > 0)
EXECUTE_IF_SET_IN_REG_SET (bb_live_regs, FIRST_PSEUDO_REGISTER, regno,
{
sched_reg_basic_block[regno]
= REG_BLOCK_GLOBAL;
});
for (regno = 0; regno < max_regno; regno++)
if (sched_reg_live_length[regno])
{
if (sched_verbose)
{
if (REG_LIVE_LENGTH (regno) > sched_reg_live_length[regno])
fprintf (dump,
";; register %d life shortened from %d to %d\n",
regno, REG_LIVE_LENGTH (regno),
sched_reg_live_length[regno]);
/* Negative values are special; don't overwrite the current
reg_live_length value if it is negative. */
else if (REG_LIVE_LENGTH (regno) < sched_reg_live_length[regno]
&& REG_LIVE_LENGTH (regno) >= 0)
fprintf (dump,
";; register %d life extended from %d to %d\n",
regno, REG_LIVE_LENGTH (regno),
sched_reg_live_length[regno]);
if (!REG_N_CALLS_CROSSED (regno)
&& sched_reg_n_calls_crossed[regno])
fprintf (dump,
";; register %d now crosses calls\n", regno);
else if (REG_N_CALLS_CROSSED (regno)
&& !sched_reg_n_calls_crossed[regno]
&& REG_BASIC_BLOCK (regno) != REG_BLOCK_GLOBAL)
fprintf (dump,
";; register %d no longer crosses calls\n", regno);
if (REG_BASIC_BLOCK (regno) != sched_reg_basic_block[regno]
&& sched_reg_basic_block[regno] != REG_BLOCK_UNKNOWN
&& REG_BASIC_BLOCK(regno) != REG_BLOCK_UNKNOWN)
fprintf (dump,
";; register %d changed basic block from %d to %d\n",
regno, REG_BASIC_BLOCK(regno),
sched_reg_basic_block[regno]);
}
/* Negative values are special; don't overwrite the current
reg_live_length value if it is negative. */
if (REG_LIVE_LENGTH (regno) >= 0)
REG_LIVE_LENGTH (regno) = sched_reg_live_length[regno];
if (sched_reg_basic_block[regno] != REG_BLOCK_UNKNOWN
&& REG_BASIC_BLOCK(regno) != REG_BLOCK_UNKNOWN)
REG_BASIC_BLOCK(regno) = sched_reg_basic_block[regno];
/* We can't change the value of reg_n_calls_crossed to zero for
pseudos which are live in more than one block.
This is because combine might have made an optimization which
invalidated global_live_at_start and reg_n_calls_crossed,
but it does not update them. If we update reg_n_calls_crossed
here, the two variables are now inconsistent, and this might
confuse the caller-save code into saving a register that doesn't
need to be saved. This is only a problem when we zero calls
crossed for a pseudo live in multiple basic blocks.
Alternatively, we could try to correctly update basic block live
at start here in sched, but that seems complicated.
Note: it is possible that a global register became local,
as result of interblock motion, but will remain marked as a
global register. */
if (sched_reg_n_calls_crossed[regno]
|| REG_BASIC_BLOCK (regno) != REG_BLOCK_GLOBAL)
REG_N_CALLS_CROSSED (regno) = sched_reg_n_calls_crossed[regno];
}
}
/* Scheduling clock, modified in schedule_block() and queue_to_ready (). */
......@@ -6500,10 +5534,10 @@ move_insn1 (insn, last)
return insn;
}
/* Search INSN for fake REG_DEAD note pairs for NOTE_INSN_SETJMP,
/* Search INSN for REG_SAVE_NOTE note pairs for NOTE_INSN_SETJMP,
NOTE_INSN_{LOOP,EHREGION}_{BEG,END}; and convert them back into
NOTEs. The REG_DEAD note following first one is contains the saved
value for NOTE_BLOCK_NUMBER which is useful for
NOTEs. The REG_SAVE_NOTE note following first one is contains the
saved value for NOTE_BLOCK_NUMBER which is useful for
NOTE_INSN_EH_REGION_{BEG,END} NOTEs. LAST is the last instruction
output by the instruction scheduler. Return the new value of LAST. */
......@@ -6517,8 +5551,7 @@ reemit_notes (insn, last)
retval = last;
for (note = REG_NOTES (insn); note; note = XEXP (note, 1))
{
if (REG_NOTE_KIND (note) == REG_DEAD
&& GET_CODE (XEXP (note, 0)) == CONST_INT)
if (REG_NOTE_KIND (note) == REG_SAVE_NOTE)
{
int note_type = INTVAL (XEXP (note, 0));
if (note_type == NOTE_INSN_SETJMP)
......@@ -6671,8 +5704,7 @@ schedule_block (bb, rgn_n_insns)
rtx note;
for (note = REG_NOTES (head); note; note = XEXP (note, 1))
if (REG_NOTE_KIND (note) == REG_DEAD
&& GET_CODE (XEXP (note, 0)) == CONST_INT)
if (REG_NOTE_KIND (note) == REG_SAVE_NOTE)
remove_note (head, note);
}
......@@ -7566,6 +6598,8 @@ schedule_region (rgn)
int bb;
int rgn_n_insns = 0;
int sched_rgn_n_insns = 0;
int initial_deaths;
sbitmap blocks;
/* Set variables for the current region. */
current_nr_blocks = RGN_NR_BLOCKS (rgn);
......@@ -7575,6 +6609,13 @@ schedule_region (rgn)
reg_pending_clobbers = ALLOCA_REG_SET ();
reg_pending_sets_all = 0;
/* Create a bitmap of the blocks in this region. */
blocks = sbitmap_alloc (n_basic_blocks);
sbitmap_zero (blocks);
for (bb = current_nr_blocks - 1; bb >= 0; --bb)
SET_BIT (blocks, BB_TO_BLOCK (bb));
/* Initializations for region data dependence analyisis. */
if (current_nr_blocks > 1)
{
......@@ -7624,13 +6665,16 @@ schedule_region (rgn)
for (bb = current_nr_blocks - 1; bb >= 0; bb--)
compute_block_forward_dependences (bb);
/* Delete line notes, compute live-regs at block end, and set priorities. */
dead_notes = 0;
/* Compute INSN_REG_WEIGHT. */
for (bb = current_nr_blocks - 1; bb >= 0; bb--)
find_insn_reg_weight (bb);
/* Remove death notes. */
initial_deaths = count_or_remove_death_notes (blocks, 1);
/* Delete line notes and set priorities. */
for (bb = 0; bb < current_nr_blocks; bb++)
{
if (reload_completed == 0)
find_pre_sched_live (bb);
if (write_symbols != NO_DEBUG)
{
save_line_notes (bb);
......@@ -7704,20 +6748,18 @@ schedule_region (rgn)
if (sched_rgn_n_insns != rgn_n_insns)
abort ();
/* Update register life and usage information. */
if (reload_completed == 0)
/* Update register life and usage information. Scheduling a multi-block
region requires a global update. */
if (current_nr_blocks > 1)
update_life_info (blocks, UPDATE_LIFE_GLOBAL);
else
{
for (bb = current_nr_blocks - 1; bb >= 0; bb--)
find_post_sched_live (bb);
if (current_nr_blocks <= 1)
/* Sanity check. There should be no REG_DEAD notes leftover
at the end. In practice, this can occur as the result of
bugs in flow, combine.c, and/or sched.c. The values of the
REG_DEAD notes remaining are meaningless, because
dead_notes is just used as a free list. */
if (dead_notes != 0)
abort ();
update_life_info (blocks, UPDATE_LIFE_LOCAL);
/* In the single block case, the count of registers that died should
not have changed during the schedule. */
if (count_or_remove_death_notes (blocks, 0) != initial_deaths)
abort ();
}
/* Restore line notes. */
......@@ -7732,6 +6774,7 @@ schedule_region (rgn)
FREE_REG_SET (reg_pending_sets);
FREE_REG_SET (reg_pending_clobbers);
sbitmap_free (blocks);
}
/* The one entry point in this file. DUMP_FILE is the dump file for
......@@ -7920,26 +6963,6 @@ schedule_insns (dump_file)
insn_dep_count = (int *) xcalloc (max_uid, sizeof (int));
insn_depend = (rtx *) xcalloc (max_uid, sizeof (rtx));
if (reload_completed == 0)
{
int i;
sched_reg_n_calls_crossed = (int *) alloca (max_regno * sizeof (int));
sched_reg_live_length = (int *) alloca (max_regno * sizeof (int));
sched_reg_basic_block = (int *) alloca (max_regno * sizeof (int));
bb_live_regs = ALLOCA_REG_SET ();
bzero ((char *) sched_reg_n_calls_crossed, max_regno * sizeof (int));
bzero ((char *) sched_reg_live_length, max_regno * sizeof (int));
for (i = 0; i < max_regno; i++)
sched_reg_basic_block[i] = REG_BLOCK_UNKNOWN;
}
else
{
sched_reg_n_calls_crossed = 0;
sched_reg_live_length = 0;
bb_live_regs = 0;
}
init_alias_analysis ();
if (write_symbols != NO_DEBUG)
......@@ -8001,10 +7024,6 @@ schedule_insns (dump_file)
if (write_symbols != NO_DEBUG)
rm_redundant_line_notes ();
/* Update information about uses of registers in the subroutine. */
if (reload_completed == 0)
update_reg_usage ();
if (sched_verbose)
{
if (reload_completed == 0 && flag_schedule_interblock)
......@@ -8040,9 +7059,6 @@ schedule_insns (dump_file)
if (write_symbols != NO_DEBUG)
free (line_note);
if (bb_live_regs)
FREE_REG_SET (bb_live_regs);
if (edge_table)
{
free (edge_table);
......
......@@ -257,7 +257,7 @@ const char * const reg_note_name[] = { "", "REG_DEAD", "REG_INC", "REG_EQUIV", "
"REG_EXEC_COUNT", "REG_NOALIAS", "REG_SAVE_AREA",
"REG_BR_PRED", "REG_EH_CONTEXT",
"REG_FRAME_RELATED_EXPR", "REG_EH_REGION",
"REG_EH_RETHROW" };
"REG_EH_RETHROW", "REG_SAVE_NOTE" };
static void dump_and_abort PROTO((int, int, FILE *)) ATTRIBUTE_NORETURN;
static void read_name PROTO((char *, FILE *));
......
......@@ -456,8 +456,8 @@ extern void rtvec_check_failed_bounds PROTO((rtvec, int,
flags computed by get_jump_flags() after dbr scheduling is complete.
REG_FRAME_RELATED_EXPR is attached to insns that are RTX_FRAME_RELATED_P,
but are too complex for DWARF to interpret what they imply. The attached
rtx is used instead of intuition. */
/* REG_EH_REGION is used to indicate what exception region an INSN
rtx is used instead of intuition.
REG_EH_REGION is used to indicate what exception region an INSN
belongs in. This can be used to indicate what region a call may throw
to. a REGION of 0 indicates that a call cannot throw at all.
a REGION of -1 indicates that it cannot throw, nor will it execute
......@@ -465,8 +465,9 @@ extern void rtvec_check_failed_bounds PROTO((rtvec, int,
REG_EH_RETHROW is used to indicate that a call is actually a
call to rethrow, and specifies the rethrow symbol for the region
the rethrow is targetting. This provides a way to generate the
non standard flow edges required for a rethrow. */
non standard flow edges required for a rethrow.
REG_SAVE_NOTE is used by haifa-sched to save NOTE_INSN notes
across scheduling. */
#define REG_NOTES(INSN) XEXP(INSN, 6)
......@@ -481,7 +482,7 @@ enum reg_note { REG_DEAD = 1, REG_INC = 2, REG_EQUIV = 3, REG_WAS_0 = 4,
REG_EXEC_COUNT = 17, REG_NOALIAS = 18, REG_SAVE_AREA = 19,
REG_BR_PRED = 20, REG_EH_CONTEXT = 21,
REG_FRAME_RELATED_EXPR = 22, REG_EH_REGION = 23,
REG_EH_RETHROW = 24 };
REG_EH_RETHROW = 24, REG_SAVE_NOTE = 25 };
/* The base value for branch probability notes. */
#define REG_BR_PROB_BASE 10000
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment