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
7 changed files with 1128 additions and 689 deletions
|
@ -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>
|
||||
|
||||
* 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 void finish_sometimes_live PROTO ((struct sometimes *, 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 int insn_issue_delay PROTO ((rtx));
|
||||
static int birthing_insn_p PROTO ((rtx));
|
||||
|
@ -7775,683 +7772,6 @@ schedule_region (rgn)
|
|||
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
|
||||
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 set_block_num PROTO((rtx, int));
|
||||
extern void life_analysis PROTO((rtx, int, FILE *, int));
|
||||
extern void update_life_info PROTO((rtx, rtx, rtx, rtx, rtx));
|
||||
#endif
|
||||
|
||||
/* Functions in varasm.c. */
|
||||
|
|
|
@ -2669,7 +2669,7 @@ split_block_insns (b, do_split)
|
|||
/* try_split returns the NOTE that INSN became. */
|
||||
first = NEXT_INSN (first);
|
||||
#ifdef INSN_SCHEDULING
|
||||
update_flow_info (notes, first, last, insn);
|
||||
update_life_info (notes, first, last, insn, insn);
|
||||
#endif
|
||||
PUT_CODE (insn, NOTE);
|
||||
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++)
|
||||
{
|
||||
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;
|
||||
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)
|
||||
|| TEST_HARD_REG_BIT (used.regs, i + j))
|
||||
if (TEST_HARD_REG_BIT (*reg_set, regno + j)
|
||||
|| TEST_HARD_REG_BIT (used.regs, regno + j))
|
||||
{
|
||||
success = 0;
|
||||
break;
|
||||
|
@ -1279,12 +1298,33 @@ find_free_register (current_insn, class_str, mode, reg_set)
|
|||
}
|
||||
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 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 *));
|
||||
#endif
|
||||
extern void free_bb_mem PROTO ((void));
|
||||
extern void replace_insns PROTO ((rtx, rtx, rtx, rtx));
|
||||
|
||||
/* In expmed.c */
|
||||
extern void init_expmed PROTO ((void));
|
||||
|
|
Loading…
Add table
Reference in a new issue