haifa-sched.c (split_hard_reg_notes): Move to flow.c
* haifa-sched.c (split_hard_reg_notes): Move to flow.c (new_insn_dead_notes): Likewise. (update_n_sets): Likewise. (update_flow_info): Move to flow.c, renamed to update_life_info; extend to handle multiple source insns. * flow.c: Include resource.h (unlink_insn_chain): New. (split_hard_reg_notes): New. (maybe_add_dead_note): New. (maybe_add_dead_note_use): New. (find_insn_with_note): New. (new_insn_dead_notes): New. (update_n_sets): New. (sets_reg_or_subreg_1, sets_reg_or_subreg): New. (maybe_remove_dead_notes): New. (update_life_info): New. (prepend_reg_notes): New. (replace_insns): New. * output.h (update_life_info): Declare. * recog.c (split_block_insns): Use update_life_info. * resource.c (find_free_register): Use reg_alloc_order, don't use fixed regs, make sure the mode is supported, don't use new regs. (reg_dead_p): New. * rtl.h (replace_insns): Declare. Co-Authored-By: Richard Henderson <rth@cygnus.com> From-SVN: r28828
This commit is contained in:
parent
952d33b8db
commit
f2a1bc0267
|
@ -1,3 +1,31 @@
|
||||||
|
Tue Aug 24 11:46:10 1999 Bob Manson <manson@cygnus.com>
|
||||||
|
Richard Henderson <rth@cygnus.com>
|
||||||
|
|
||||||
|
* haifa-sched.c (split_hard_reg_notes): Move to flow.c
|
||||||
|
(new_insn_dead_notes): Likewise.
|
||||||
|
(update_n_sets): Likewise.
|
||||||
|
(update_flow_info): Move to flow.c, renamed to update_life_info;
|
||||||
|
extend to handle multiple source insns.
|
||||||
|
* flow.c: Include resource.h
|
||||||
|
(unlink_insn_chain): New.
|
||||||
|
(split_hard_reg_notes): New.
|
||||||
|
(maybe_add_dead_note): New.
|
||||||
|
(maybe_add_dead_note_use): New.
|
||||||
|
(find_insn_with_note): New.
|
||||||
|
(new_insn_dead_notes): New.
|
||||||
|
(update_n_sets): New.
|
||||||
|
(sets_reg_or_subreg_1, sets_reg_or_subreg): New.
|
||||||
|
(maybe_remove_dead_notes): New.
|
||||||
|
(update_life_info): New.
|
||||||
|
(prepend_reg_notes): New.
|
||||||
|
(replace_insns): New.
|
||||||
|
* output.h (update_life_info): Declare.
|
||||||
|
* recog.c (split_block_insns): Use update_life_info.
|
||||||
|
* resource.c (find_free_register): Use reg_alloc_order, don't use
|
||||||
|
fixed regs, make sure the mode is supported, don't use new regs.
|
||||||
|
(reg_dead_p): New.
|
||||||
|
* rtl.h (replace_insns): Declare.
|
||||||
|
|
||||||
Tue Aug 24 13:48:39 1999 Nathan Sidwell <nathan@acm.org>
|
Tue Aug 24 13:48:39 1999 Nathan Sidwell <nathan@acm.org>
|
||||||
|
|
||||||
* expr.c (expand_expr): Cope with COND_EXPRs with one
|
* expr.c (expand_expr): Cope with COND_EXPRs with one
|
||||||
|
|
1049
gcc/flow.c
1049
gcc/flow.c
File diff suppressed because it is too large
Load Diff
|
@ -452,9 +452,6 @@ static void attach_deaths_insn PROTO ((rtx));
|
||||||
static int new_sometimes_live PROTO ((struct sometimes *, int, int));
|
static int new_sometimes_live PROTO ((struct sometimes *, int, int));
|
||||||
static void finish_sometimes_live PROTO ((struct sometimes *, int));
|
static void finish_sometimes_live PROTO ((struct sometimes *, int));
|
||||||
static int schedule_block PROTO ((int, int));
|
static int schedule_block PROTO ((int, int));
|
||||||
static void split_hard_reg_notes PROTO ((rtx, rtx, rtx));
|
|
||||||
static void new_insn_dead_notes PROTO ((rtx, rtx, rtx, rtx));
|
|
||||||
static void update_n_sets PROTO ((rtx, int));
|
|
||||||
static char *safe_concat PROTO ((char *, char *, const char *));
|
static char *safe_concat PROTO ((char *, char *, const char *));
|
||||||
static int insn_issue_delay PROTO ((rtx));
|
static int insn_issue_delay PROTO ((rtx));
|
||||||
static int birthing_insn_p PROTO ((rtx));
|
static int birthing_insn_p PROTO ((rtx));
|
||||||
|
@ -7775,683 +7772,6 @@ schedule_region (rgn)
|
||||||
FREE_REG_SET (reg_pending_clobbers);
|
FREE_REG_SET (reg_pending_clobbers);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Subroutine of update_flow_info. Determines whether any new REG_NOTEs are
|
|
||||||
needed for the hard register mentioned in the note. This can happen
|
|
||||||
if the reference to the hard register in the original insn was split into
|
|
||||||
several smaller hard register references in the split insns. */
|
|
||||||
|
|
||||||
static void
|
|
||||||
split_hard_reg_notes (note, first, last)
|
|
||||||
rtx note, first, last;
|
|
||||||
{
|
|
||||||
rtx reg, temp, link;
|
|
||||||
int n_regs, i, new_reg;
|
|
||||||
rtx insn;
|
|
||||||
|
|
||||||
/* Assume that this is a REG_DEAD note. */
|
|
||||||
if (REG_NOTE_KIND (note) != REG_DEAD)
|
|
||||||
abort ();
|
|
||||||
|
|
||||||
reg = XEXP (note, 0);
|
|
||||||
|
|
||||||
n_regs = HARD_REGNO_NREGS (REGNO (reg), GET_MODE (reg));
|
|
||||||
|
|
||||||
for (i = 0; i < n_regs; i++)
|
|
||||||
{
|
|
||||||
new_reg = REGNO (reg) + i;
|
|
||||||
|
|
||||||
/* Check for references to new_reg in the split insns. */
|
|
||||||
for (insn = last;; insn = PREV_INSN (insn))
|
|
||||||
{
|
|
||||||
if (GET_RTX_CLASS (GET_CODE (insn)) == 'i'
|
|
||||||
&& (temp = regno_use_in (new_reg, PATTERN (insn))))
|
|
||||||
{
|
|
||||||
/* Create a new reg dead note ere. */
|
|
||||||
link = alloc_EXPR_LIST (REG_DEAD, temp, REG_NOTES (insn));
|
|
||||||
REG_NOTES (insn) = link;
|
|
||||||
|
|
||||||
/* If killed multiple registers here, then add in the excess. */
|
|
||||||
i += HARD_REGNO_NREGS (REGNO (temp), GET_MODE (temp)) - 1;
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
/* It isn't mentioned anywhere, so no new reg note is needed for
|
|
||||||
this register. */
|
|
||||||
if (insn == first)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Subroutine of update_flow_info. Determines whether a SET or CLOBBER in an
|
|
||||||
insn created by splitting needs a REG_DEAD or REG_UNUSED note added. */
|
|
||||||
|
|
||||||
static void
|
|
||||||
new_insn_dead_notes (pat, insn, last, orig_insn)
|
|
||||||
rtx pat, insn, last, orig_insn;
|
|
||||||
{
|
|
||||||
rtx dest, tem, set;
|
|
||||||
|
|
||||||
/* PAT is either a CLOBBER or a SET here. */
|
|
||||||
dest = XEXP (pat, 0);
|
|
||||||
|
|
||||||
while (GET_CODE (dest) == ZERO_EXTRACT || GET_CODE (dest) == SUBREG
|
|
||||||
|| GET_CODE (dest) == STRICT_LOW_PART
|
|
||||||
|| GET_CODE (dest) == SIGN_EXTRACT)
|
|
||||||
dest = XEXP (dest, 0);
|
|
||||||
|
|
||||||
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'
|
|
||||||
&& reg_overlap_mentioned_p (dest, PATTERN (tem))
|
|
||||||
&& (set = single_set (tem)))
|
|
||||||
{
|
|
||||||
rtx tem_dest = SET_DEST (set);
|
|
||||||
|
|
||||||
while (GET_CODE (tem_dest) == ZERO_EXTRACT
|
|
||||||
|| GET_CODE (tem_dest) == SUBREG
|
|
||||||
|| GET_CODE (tem_dest) == STRICT_LOW_PART
|
|
||||||
|| GET_CODE (tem_dest) == SIGN_EXTRACT)
|
|
||||||
tem_dest = XEXP (tem_dest, 0);
|
|
||||||
|
|
||||||
if (!rtx_equal_p (tem_dest, dest))
|
|
||||||
{
|
|
||||||
/* Use the same scheme as combine.c, don't put both REG_DEAD
|
|
||||||
and REG_UNUSED notes on the same insn. */
|
|
||||||
if (!find_regno_note (tem, REG_UNUSED, REGNO (dest))
|
|
||||||
&& !find_regno_note (tem, REG_DEAD, REGNO (dest)))
|
|
||||||
{
|
|
||||||
rtx note = alloc_EXPR_LIST (REG_DEAD, dest,
|
|
||||||
REG_NOTES (tem));
|
|
||||||
REG_NOTES (tem) = note;
|
|
||||||
}
|
|
||||||
/* The reg only dies in one insn, the last one that uses
|
|
||||||
it. */
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else if (reg_overlap_mentioned_p (dest, SET_SRC (set)))
|
|
||||||
/* We found an instruction that both uses the register,
|
|
||||||
and sets it, so no new REG_NOTE is needed for this set. */
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* If this is a set, it must die somewhere, unless it is the dest of
|
|
||||||
the original insn, and hence is live after the original insn. Abort
|
|
||||||
if it isn't supposed to be live after the original insn.
|
|
||||||
|
|
||||||
If this is a clobber, then just add a REG_UNUSED note. */
|
|
||||||
if (tem == insn)
|
|
||||||
{
|
|
||||||
int live_after_orig_insn = 0;
|
|
||||||
rtx pattern = PATTERN (orig_insn);
|
|
||||||
int i;
|
|
||||||
|
|
||||||
if (GET_CODE (pat) == CLOBBER)
|
|
||||||
{
|
|
||||||
rtx note = alloc_EXPR_LIST (REG_UNUSED, dest, REG_NOTES (insn));
|
|
||||||
REG_NOTES (insn) = note;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* The original insn could have multiple sets, so search the
|
|
||||||
insn for all sets. */
|
|
||||||
if (GET_CODE (pattern) == SET)
|
|
||||||
{
|
|
||||||
if (reg_overlap_mentioned_p (dest, SET_DEST (pattern)))
|
|
||||||
live_after_orig_insn = 1;
|
|
||||||
}
|
|
||||||
else if (GET_CODE (pattern) == PARALLEL)
|
|
||||||
{
|
|
||||||
for (i = 0; i < XVECLEN (pattern, 0); i++)
|
|
||||||
if (GET_CODE (XVECEXP (pattern, 0, i)) == SET
|
|
||||||
&& reg_overlap_mentioned_p (dest,
|
|
||||||
SET_DEST (XVECEXP (pattern,
|
|
||||||
0, i))))
|
|
||||||
live_after_orig_insn = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!live_after_orig_insn)
|
|
||||||
abort ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Subroutine of update_flow_info. Update the value of reg_n_sets for all
|
|
||||||
registers modified by X. INC is -1 if the containing insn is being deleted,
|
|
||||||
and is 1 if the containing insn is a newly generated insn. */
|
|
||||||
|
|
||||||
static void
|
|
||||||
update_n_sets (x, inc)
|
|
||||||
rtx x;
|
|
||||||
int inc;
|
|
||||||
{
|
|
||||||
rtx dest = SET_DEST (x);
|
|
||||||
|
|
||||||
while (GET_CODE (dest) == STRICT_LOW_PART || GET_CODE (dest) == SUBREG
|
|
||||||
|| GET_CODE (dest) == ZERO_EXTRACT || GET_CODE (dest) == SIGN_EXTRACT)
|
|
||||||
dest = SUBREG_REG (dest);
|
|
||||||
|
|
||||||
if (GET_CODE (dest) == REG)
|
|
||||||
{
|
|
||||||
int regno = REGNO (dest);
|
|
||||||
|
|
||||||
if (regno < FIRST_PSEUDO_REGISTER)
|
|
||||||
{
|
|
||||||
register int i;
|
|
||||||
int endregno = regno + HARD_REGNO_NREGS (regno, GET_MODE (dest));
|
|
||||||
|
|
||||||
for (i = regno; i < endregno; i++)
|
|
||||||
REG_N_SETS (i) += inc;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
REG_N_SETS (regno) += inc;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Updates all flow-analysis related quantities (including REG_NOTES) for
|
|
||||||
the insns from FIRST to LAST inclusive that were created by splitting
|
|
||||||
ORIG_INSN. NOTES are the original REG_NOTES. */
|
|
||||||
|
|
||||||
void
|
|
||||||
update_flow_info (notes, first, last, orig_insn)
|
|
||||||
rtx notes;
|
|
||||||
rtx first, last;
|
|
||||||
rtx orig_insn;
|
|
||||||
{
|
|
||||||
rtx insn, note;
|
|
||||||
rtx next;
|
|
||||||
rtx orig_dest, temp;
|
|
||||||
rtx set;
|
|
||||||
|
|
||||||
/* Get and save the destination set by the original insn. */
|
|
||||||
|
|
||||||
orig_dest = single_set (orig_insn);
|
|
||||||
if (orig_dest)
|
|
||||||
orig_dest = SET_DEST (orig_dest);
|
|
||||||
|
|
||||||
/* Move REG_NOTES from the original insn to where they now belong. */
|
|
||||||
|
|
||||||
for (note = notes; note; note = next)
|
|
||||||
{
|
|
||||||
next = XEXP (note, 1);
|
|
||||||
switch (REG_NOTE_KIND (note))
|
|
||||||
{
|
|
||||||
case REG_DEAD:
|
|
||||||
case REG_UNUSED:
|
|
||||||
/* Move these notes from the original insn to the last new insn where
|
|
||||||
the register is now set. */
|
|
||||||
|
|
||||||
for (insn = last;; insn = PREV_INSN (insn))
|
|
||||||
{
|
|
||||||
if (GET_RTX_CLASS (GET_CODE (insn)) == 'i'
|
|
||||||
&& reg_mentioned_p (XEXP (note, 0), PATTERN (insn)))
|
|
||||||
{
|
|
||||||
/* If this note refers to a multiple word hard register, it
|
|
||||||
may have been split into several smaller hard register
|
|
||||||
references, so handle it specially. */
|
|
||||||
temp = XEXP (note, 0);
|
|
||||||
if (REG_NOTE_KIND (note) == REG_DEAD
|
|
||||||
&& GET_CODE (temp) == REG
|
|
||||||
&& REGNO (temp) < FIRST_PSEUDO_REGISTER
|
|
||||||
&& HARD_REGNO_NREGS (REGNO (temp), GET_MODE (temp)) > 1)
|
|
||||||
split_hard_reg_notes (note, first, last);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
XEXP (note, 1) = REG_NOTES (insn);
|
|
||||||
REG_NOTES (insn) = note;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Sometimes need to convert REG_UNUSED notes to REG_DEAD
|
|
||||||
notes. */
|
|
||||||
/* ??? This won't handle multiple word registers correctly,
|
|
||||||
but should be good enough for now. */
|
|
||||||
if (REG_NOTE_KIND (note) == REG_UNUSED
|
|
||||||
&& GET_CODE (XEXP (note, 0)) != SCRATCH
|
|
||||||
&& !dead_or_set_p (insn, XEXP (note, 0)))
|
|
||||||
PUT_REG_NOTE_KIND (note, REG_DEAD);
|
|
||||||
|
|
||||||
/* The reg only dies in one insn, the last one that uses
|
|
||||||
it. */
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
/* It must die somewhere, fail it we couldn't find where it died.
|
|
||||||
|
|
||||||
If this is a REG_UNUSED note, then it must be a temporary
|
|
||||||
register that was not needed by this instantiation of the
|
|
||||||
pattern, so we can safely ignore it. */
|
|
||||||
if (insn == first)
|
|
||||||
{
|
|
||||||
if (REG_NOTE_KIND (note) != REG_UNUSED)
|
|
||||||
abort ();
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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. */
|
|
||||||
|
|
||||||
if (!orig_dest)
|
|
||||||
abort ();
|
|
||||||
|
|
||||||
for (insn = first;; insn = NEXT_INSN (insn))
|
|
||||||
{
|
|
||||||
if (GET_RTX_CLASS (GET_CODE (insn)) == 'i'
|
|
||||||
&& (temp = single_set (insn))
|
|
||||||
&& rtx_equal_p (SET_DEST (temp), orig_dest))
|
|
||||||
{
|
|
||||||
XEXP (note, 1) = REG_NOTES (insn);
|
|
||||||
REG_NOTES (insn) = note;
|
|
||||||
/* The reg is only zero before one insn, the first that
|
|
||||||
uses it. */
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
/* If this note refers to a multiple word hard
|
|
||||||
register, it may have been split into several smaller
|
|
||||||
hard register references. We could split the notes,
|
|
||||||
but simply dropping them is good enough. */
|
|
||||||
if (GET_CODE (orig_dest) == REG
|
|
||||||
&& REGNO (orig_dest) < FIRST_PSEUDO_REGISTER
|
|
||||||
&& HARD_REGNO_NREGS (REGNO (orig_dest),
|
|
||||||
GET_MODE (orig_dest)) > 1)
|
|
||||||
break;
|
|
||||||
/* It must be set somewhere, fail if we couldn't find where it
|
|
||||||
was set. */
|
|
||||||
if (insn == last)
|
|
||||||
abort ();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case REG_EQUAL:
|
|
||||||
case REG_EQUIV:
|
|
||||||
/* A REG_EQUIV or REG_EQUAL note on an insn with more than one
|
|
||||||
set is meaningless. Just drop the note. */
|
|
||||||
if (!orig_dest)
|
|
||||||
break;
|
|
||||||
|
|
||||||
case REG_NO_CONFLICT:
|
|
||||||
/* These notes apply to the dest of the original insn. Find the last
|
|
||||||
new insn that now has the same dest, and move the note there. */
|
|
||||||
|
|
||||||
if (!orig_dest)
|
|
||||||
abort ();
|
|
||||||
|
|
||||||
for (insn = last;; insn = PREV_INSN (insn))
|
|
||||||
{
|
|
||||||
if (GET_RTX_CLASS (GET_CODE (insn)) == 'i'
|
|
||||||
&& (temp = single_set (insn))
|
|
||||||
&& rtx_equal_p (SET_DEST (temp), orig_dest))
|
|
||||||
{
|
|
||||||
XEXP (note, 1) = REG_NOTES (insn);
|
|
||||||
REG_NOTES (insn) = note;
|
|
||||||
/* Only put this note on one of the new insns. */
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* The original dest must still be set someplace. Abort if we
|
|
||||||
couldn't find it. */
|
|
||||||
if (insn == first)
|
|
||||||
{
|
|
||||||
/* However, if this note refers to a multiple word hard
|
|
||||||
register, it may have been split into several smaller
|
|
||||||
hard register references. We could split the notes,
|
|
||||||
but simply dropping them is good enough. */
|
|
||||||
if (GET_CODE (orig_dest) == REG
|
|
||||||
&& REGNO (orig_dest) < FIRST_PSEUDO_REGISTER
|
|
||||||
&& HARD_REGNO_NREGS (REGNO (orig_dest),
|
|
||||||
GET_MODE (orig_dest)) > 1)
|
|
||||||
break;
|
|
||||||
/* Likewise for multi-word memory references. */
|
|
||||||
if (GET_CODE (orig_dest) == MEM
|
|
||||||
&& SIZE_FOR_MODE (orig_dest) > UNITS_PER_WORD)
|
|
||||||
break;
|
|
||||||
abort ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case REG_LIBCALL:
|
|
||||||
/* Move a REG_LIBCALL note to the first insn created, and update
|
|
||||||
the corresponding REG_RETVAL note. */
|
|
||||||
XEXP (note, 1) = REG_NOTES (first);
|
|
||||||
REG_NOTES (first) = note;
|
|
||||||
|
|
||||||
insn = XEXP (note, 0);
|
|
||||||
note = find_reg_note (insn, REG_RETVAL, NULL_RTX);
|
|
||||||
if (note)
|
|
||||||
XEXP (note, 0) = first;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case REG_EXEC_COUNT:
|
|
||||||
/* Move a REG_EXEC_COUNT note to the first insn created. */
|
|
||||||
XEXP (note, 1) = REG_NOTES (first);
|
|
||||||
REG_NOTES (first) = note;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case REG_RETVAL:
|
|
||||||
/* Move a REG_RETVAL note to the last insn created, and update
|
|
||||||
the corresponding REG_LIBCALL note. */
|
|
||||||
XEXP (note, 1) = REG_NOTES (last);
|
|
||||||
REG_NOTES (last) = note;
|
|
||||||
|
|
||||||
insn = XEXP (note, 0);
|
|
||||||
note = find_reg_note (insn, REG_LIBCALL, NULL_RTX);
|
|
||||||
if (note)
|
|
||||||
XEXP (note, 0) = last;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case REG_NONNEG:
|
|
||||||
case REG_BR_PROB:
|
|
||||||
/* This should be moved to whichever instruction is a JUMP_INSN. */
|
|
||||||
|
|
||||||
for (insn = last;; insn = PREV_INSN (insn))
|
|
||||||
{
|
|
||||||
if (GET_CODE (insn) == JUMP_INSN)
|
|
||||||
{
|
|
||||||
XEXP (note, 1) = REG_NOTES (insn);
|
|
||||||
REG_NOTES (insn) = note;
|
|
||||||
/* Only put this note on one of the new insns. */
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
/* Fail if we couldn't find a JUMP_INSN. */
|
|
||||||
if (insn == first)
|
|
||||||
abort ();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case REG_INC:
|
|
||||||
/* reload sometimes leaves obsolete REG_INC notes around. */
|
|
||||||
if (reload_completed)
|
|
||||||
break;
|
|
||||||
/* This should be moved to whichever instruction now has the
|
|
||||||
increment operation. */
|
|
||||||
abort ();
|
|
||||||
|
|
||||||
case REG_LABEL:
|
|
||||||
/* Should be moved to the new insn(s) which use the label. */
|
|
||||||
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) = alloc_EXPR_LIST (REG_LABEL,
|
|
||||||
XEXP (note, 0),
|
|
||||||
REG_NOTES (insn));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case REG_CC_SETTER:
|
|
||||||
case REG_CC_USER:
|
|
||||||
/* These two notes will never appear until after reorg, so we don't
|
|
||||||
have to handle them here. */
|
|
||||||
default:
|
|
||||||
abort ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Each new insn created, except the last, has a new set. If the destination
|
|
||||||
is a register, then this reg is now live across several insns, whereas
|
|
||||||
previously the dest reg was born and died within the same insn. To
|
|
||||||
reflect this, we now need a REG_DEAD note on the insn where this
|
|
||||||
dest reg dies.
|
|
||||||
|
|
||||||
Similarly, the new insns may have clobbers that need REG_UNUSED notes. */
|
|
||||||
|
|
||||||
for (insn = first; insn != last; insn = NEXT_INSN (insn))
|
|
||||||
{
|
|
||||||
rtx pat;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
pat = PATTERN (insn);
|
|
||||||
if (GET_CODE (pat) == SET || GET_CODE (pat) == CLOBBER)
|
|
||||||
new_insn_dead_notes (pat, insn, last, orig_insn);
|
|
||||||
else if (GET_CODE (pat) == PARALLEL)
|
|
||||||
{
|
|
||||||
for (i = 0; i < XVECLEN (pat, 0); i++)
|
|
||||||
if (GET_CODE (XVECEXP (pat, 0, i)) == SET
|
|
||||||
|| GET_CODE (XVECEXP (pat, 0, i)) == CLOBBER)
|
|
||||||
new_insn_dead_notes (XVECEXP (pat, 0, i), insn, last, orig_insn);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If any insn, except the last, uses the register set by the last insn,
|
|
||||||
then we need a new REG_DEAD note on that insn. In this case, there
|
|
||||||
would not have been a REG_DEAD note for this register in the original
|
|
||||||
insn because it was used and set within one insn. */
|
|
||||||
|
|
||||||
set = single_set (last);
|
|
||||||
if (set)
|
|
||||||
{
|
|
||||||
rtx dest = SET_DEST (set);
|
|
||||||
|
|
||||||
while (GET_CODE (dest) == ZERO_EXTRACT || GET_CODE (dest) == SUBREG
|
|
||||||
|| GET_CODE (dest) == STRICT_LOW_PART
|
|
||||||
|| GET_CODE (dest) == SIGN_EXTRACT)
|
|
||||||
dest = XEXP (dest, 0);
|
|
||||||
|
|
||||||
if (GET_CODE (dest) == REG
|
|
||||||
/* Global registers are always live, so the code below does not
|
|
||||||
apply to them. */
|
|
||||||
&& (REGNO (dest) >= FIRST_PSEUDO_REGISTER
|
|
||||||
|| ! global_regs[REGNO (dest)]))
|
|
||||||
{
|
|
||||||
rtx stop_insn = PREV_INSN (first);
|
|
||||||
|
|
||||||
/* If the last insn uses the register that it is setting, then
|
|
||||||
we don't want to put a REG_DEAD note there. Search backwards
|
|
||||||
to find the first insn that sets but does not use DEST. */
|
|
||||||
|
|
||||||
insn = last;
|
|
||||||
if (reg_overlap_mentioned_p (dest, SET_SRC (set)))
|
|
||||||
{
|
|
||||||
for (insn = PREV_INSN (insn); insn != first;
|
|
||||||
insn = PREV_INSN (insn))
|
|
||||||
{
|
|
||||||
if ((set = single_set (insn))
|
|
||||||
&& reg_mentioned_p (dest, SET_DEST (set))
|
|
||||||
&& ! reg_overlap_mentioned_p (dest, SET_SRC (set)))
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Now find the first insn that uses but does not set DEST. */
|
|
||||||
|
|
||||||
for (insn = PREV_INSN (insn); insn != stop_insn;
|
|
||||||
insn = PREV_INSN (insn))
|
|
||||||
{
|
|
||||||
if (GET_RTX_CLASS (GET_CODE (insn)) == 'i'
|
|
||||||
&& reg_mentioned_p (dest, PATTERN (insn))
|
|
||||||
&& (set = single_set (insn)))
|
|
||||||
{
|
|
||||||
rtx insn_dest = SET_DEST (set);
|
|
||||||
|
|
||||||
while (GET_CODE (insn_dest) == ZERO_EXTRACT
|
|
||||||
|| GET_CODE (insn_dest) == SUBREG
|
|
||||||
|| GET_CODE (insn_dest) == STRICT_LOW_PART
|
|
||||||
|| GET_CODE (insn_dest) == SIGN_EXTRACT)
|
|
||||||
insn_dest = XEXP (insn_dest, 0);
|
|
||||||
|
|
||||||
if (insn_dest != dest)
|
|
||||||
{
|
|
||||||
note = alloc_EXPR_LIST (REG_DEAD, dest, REG_NOTES (insn));
|
|
||||||
REG_NOTES (insn) = note;
|
|
||||||
/* The reg only dies in one insn, the last one
|
|
||||||
that uses it. */
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If the original dest is modifying a multiple register target, and the
|
|
||||||
original instruction was split such that the original dest is now set
|
|
||||||
by two or more SUBREG sets, then the split insns no longer kill the
|
|
||||||
destination of the original insn.
|
|
||||||
|
|
||||||
In this case, if there exists an instruction in the same basic block,
|
|
||||||
before the split insn, which uses the original dest, and this use is
|
|
||||||
killed by the original insn, then we must remove the REG_DEAD note on
|
|
||||||
this insn, because it is now superfluous.
|
|
||||||
|
|
||||||
This does not apply when a hard register gets split, because the code
|
|
||||||
knows how to handle overlapping hard registers properly. */
|
|
||||||
if (orig_dest && GET_CODE (orig_dest) == REG)
|
|
||||||
{
|
|
||||||
int found_orig_dest = 0;
|
|
||||||
int found_split_dest = 0;
|
|
||||||
|
|
||||||
for (insn = first;; insn = NEXT_INSN (insn))
|
|
||||||
{
|
|
||||||
rtx pat;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
/* I'm not sure if this can happen, but let's be safe. */
|
|
||||||
if (GET_RTX_CLASS (GET_CODE (insn)) != 'i')
|
|
||||||
continue;
|
|
||||||
|
|
||||||
pat = PATTERN (insn);
|
|
||||||
i = GET_CODE (pat) == PARALLEL ? XVECLEN (pat, 0) : 0;
|
|
||||||
set = pat;
|
|
||||||
|
|
||||||
for (;;)
|
|
||||||
{
|
|
||||||
if (GET_CODE (set) == SET)
|
|
||||||
{
|
|
||||||
if (GET_CODE (SET_DEST (set)) == REG
|
|
||||||
&& REGNO (SET_DEST (set)) == REGNO (orig_dest))
|
|
||||||
{
|
|
||||||
found_orig_dest = 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else if (GET_CODE (SET_DEST (set)) == SUBREG
|
|
||||||
&& SUBREG_REG (SET_DEST (set)) == orig_dest)
|
|
||||||
{
|
|
||||||
found_split_dest = 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (--i < 0)
|
|
||||||
break;
|
|
||||||
set = XVECEXP (pat, 0, i);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (insn == last)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (found_split_dest)
|
|
||||||
{
|
|
||||||
/* Search backwards from FIRST, looking for the first insn that uses
|
|
||||||
the original dest. Stop if we pass a CODE_LABEL or a JUMP_INSN.
|
|
||||||
If we find an insn, and it has a REG_DEAD note, then delete the
|
|
||||||
note. */
|
|
||||||
|
|
||||||
for (insn = first; insn; insn = PREV_INSN (insn))
|
|
||||||
{
|
|
||||||
if (GET_CODE (insn) == CODE_LABEL
|
|
||||||
|| GET_CODE (insn) == JUMP_INSN)
|
|
||||||
break;
|
|
||||||
else if (GET_RTX_CLASS (GET_CODE (insn)) == 'i'
|
|
||||||
&& reg_mentioned_p (orig_dest, insn))
|
|
||||||
{
|
|
||||||
note = find_regno_note (insn, REG_DEAD, REGNO (orig_dest));
|
|
||||||
if (note)
|
|
||||||
remove_note (insn, note);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (!found_orig_dest)
|
|
||||||
{
|
|
||||||
int i, regno;
|
|
||||||
|
|
||||||
/* Should never reach here for a pseudo reg. */
|
|
||||||
if (REGNO (orig_dest) >= FIRST_PSEUDO_REGISTER)
|
|
||||||
abort ();
|
|
||||||
|
|
||||||
/* This can happen for a hard register, if the splitter
|
|
||||||
does not bother to emit instructions which would be no-ops.
|
|
||||||
We try to verify that this is the case by checking to see if
|
|
||||||
the original instruction uses all of the registers that it
|
|
||||||
set. This case is OK, because deleting a no-op can not affect
|
|
||||||
REG_DEAD notes on other insns. If this is not the case, then
|
|
||||||
abort. */
|
|
||||||
|
|
||||||
regno = REGNO (orig_dest);
|
|
||||||
for (i = HARD_REGNO_NREGS (regno, GET_MODE (orig_dest)) - 1;
|
|
||||||
i >= 0; i--)
|
|
||||||
if (! refers_to_regno_p (regno + i, regno + i + 1, orig_insn,
|
|
||||||
NULL_PTR))
|
|
||||||
break;
|
|
||||||
if (i >= 0)
|
|
||||||
abort ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Update reg_n_sets. This is necessary to prevent local alloc from
|
|
||||||
converting REG_EQUAL notes to REG_EQUIV when splitting has modified
|
|
||||||
a reg from set once to set multiple times. */
|
|
||||||
|
|
||||||
{
|
|
||||||
rtx x = PATTERN (orig_insn);
|
|
||||||
RTX_CODE code = GET_CODE (x);
|
|
||||||
|
|
||||||
if (code == SET || code == CLOBBER)
|
|
||||||
update_n_sets (x, -1);
|
|
||||||
else if (code == PARALLEL)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
for (i = XVECLEN (x, 0) - 1; i >= 0; i--)
|
|
||||||
{
|
|
||||||
code = GET_CODE (XVECEXP (x, 0, i));
|
|
||||||
if (code == SET || code == CLOBBER)
|
|
||||||
update_n_sets (XVECEXP (x, 0, i), -1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (insn = first;; insn = NEXT_INSN (insn))
|
|
||||||
{
|
|
||||||
x = PATTERN (insn);
|
|
||||||
code = GET_CODE (x);
|
|
||||||
|
|
||||||
if (code == SET || code == CLOBBER)
|
|
||||||
update_n_sets (x, 1);
|
|
||||||
else if (code == PARALLEL)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
for (i = XVECLEN (x, 0) - 1; i >= 0; i--)
|
|
||||||
{
|
|
||||||
code = GET_CODE (XVECEXP (x, 0, i));
|
|
||||||
if (code == SET || code == CLOBBER)
|
|
||||||
update_n_sets (XVECEXP (x, 0, i), 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (insn == last)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* The one entry point in this file. DUMP_FILE is the dump file for
|
/* The one entry point in this file. DUMP_FILE is the dump file for
|
||||||
this pass. */
|
this pass. */
|
||||||
|
|
||||||
|
|
|
@ -132,6 +132,7 @@ extern void find_basic_blocks PROTO((rtx, int, FILE *, int));
|
||||||
extern void free_basic_block_vars PROTO((int));
|
extern void free_basic_block_vars PROTO((int));
|
||||||
extern void set_block_num PROTO((rtx, int));
|
extern void set_block_num PROTO((rtx, int));
|
||||||
extern void life_analysis PROTO((rtx, int, FILE *, int));
|
extern void life_analysis PROTO((rtx, int, FILE *, int));
|
||||||
|
extern void update_life_info PROTO((rtx, rtx, rtx, rtx, rtx));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Functions in varasm.c. */
|
/* Functions in varasm.c. */
|
||||||
|
|
|
@ -2669,7 +2669,7 @@ split_block_insns (b, do_split)
|
||||||
/* try_split returns the NOTE that INSN became. */
|
/* try_split returns the NOTE that INSN became. */
|
||||||
first = NEXT_INSN (first);
|
first = NEXT_INSN (first);
|
||||||
#ifdef INSN_SCHEDULING
|
#ifdef INSN_SCHEDULING
|
||||||
update_flow_info (notes, first, last, insn);
|
update_life_info (notes, first, last, insn, insn);
|
||||||
#endif
|
#endif
|
||||||
PUT_CODE (insn, NOTE);
|
PUT_CODE (insn, NOTE);
|
||||||
NOTE_SOURCE_FILE (insn) = 0;
|
NOTE_SOURCE_FILE (insn) = 0;
|
||||||
|
|
|
@ -1264,14 +1264,33 @@ find_free_register (current_insn, class_str, mode, reg_set)
|
||||||
|
|
||||||
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
|
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
|
||||||
{
|
{
|
||||||
int success = 1;
|
int regno;
|
||||||
|
int success;
|
||||||
|
|
||||||
if (! TEST_HARD_REG_BIT (reg_class_contents[class], i))
|
#ifdef REG_ALLOC_ORDER
|
||||||
|
regno = reg_alloc_order [i];
|
||||||
|
#else
|
||||||
|
regno = i;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Don't allocate fixed registers. */
|
||||||
|
if (fixed_regs[regno])
|
||||||
continue;
|
continue;
|
||||||
for (j = HARD_REGNO_NREGS (i, mode) - 1; j >= 0; j--)
|
/* Make sure the register is of the right class. */
|
||||||
|
if (! TEST_HARD_REG_BIT (reg_class_contents[class], regno))
|
||||||
|
continue;
|
||||||
|
/* And can support the mode we need. */
|
||||||
|
if (! HARD_REGNO_MODE_OK (regno, mode))
|
||||||
|
continue;
|
||||||
|
/* And that we don't create an extra save/restore. */
|
||||||
|
if (! call_used_regs[regno] && ! regs_ever_live[regno])
|
||||||
|
continue;
|
||||||
|
|
||||||
|
success = 1;
|
||||||
|
for (j = HARD_REGNO_NREGS (regno, mode) - 1; j >= 0; j--)
|
||||||
{
|
{
|
||||||
if (TEST_HARD_REG_BIT (*reg_set, i + j)
|
if (TEST_HARD_REG_BIT (*reg_set, regno + j)
|
||||||
|| TEST_HARD_REG_BIT (used.regs, i + j))
|
|| TEST_HARD_REG_BIT (used.regs, regno + j))
|
||||||
{
|
{
|
||||||
success = 0;
|
success = 0;
|
||||||
break;
|
break;
|
||||||
|
@ -1279,12 +1298,33 @@ find_free_register (current_insn, class_str, mode, reg_set)
|
||||||
}
|
}
|
||||||
if (success)
|
if (success)
|
||||||
{
|
{
|
||||||
for (j = HARD_REGNO_NREGS (i, mode) - 1; j >= 0; j--)
|
for (j = HARD_REGNO_NREGS (regno, mode) - 1; j >= 0; j--)
|
||||||
{
|
{
|
||||||
SET_HARD_REG_BIT (*reg_set, i + j);
|
SET_HARD_REG_BIT (*reg_set, regno + j);
|
||||||
}
|
}
|
||||||
return gen_rtx_REG (mode, i);
|
return gen_rtx_REG (mode, regno);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return NULL_RTX;
|
return NULL_RTX;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Return true if REG is dead at CURRENT_INSN. */
|
||||||
|
|
||||||
|
int
|
||||||
|
reg_dead_p (current_insn, reg)
|
||||||
|
rtx current_insn, reg;
|
||||||
|
{
|
||||||
|
struct resources used;
|
||||||
|
int regno, j;
|
||||||
|
|
||||||
|
mark_target_live_regs (get_insns (), current_insn, &used);
|
||||||
|
|
||||||
|
regno = REGNO (reg);
|
||||||
|
for (j = HARD_REGNO_NREGS (regno, GET_MODE (reg)) - 1; j >= 0; j--)
|
||||||
|
{
|
||||||
|
if (TEST_HARD_REG_BIT (used.regs, regno + j))
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
|
@ -1412,6 +1412,7 @@ extern void recompute_reg_usage PROTO ((rtx, int));
|
||||||
extern void dump_flow_info PROTO ((FILE *));
|
extern void dump_flow_info PROTO ((FILE *));
|
||||||
#endif
|
#endif
|
||||||
extern void free_bb_mem PROTO ((void));
|
extern void free_bb_mem PROTO ((void));
|
||||||
|
extern void replace_insns PROTO ((rtx, rtx, rtx, rtx));
|
||||||
|
|
||||||
/* In expmed.c */
|
/* In expmed.c */
|
||||||
extern void init_expmed PROTO ((void));
|
extern void init_expmed PROTO ((void));
|
||||||
|
|
Loading…
Reference in New Issue