This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
ia32: resources
- To: gcc-patches at gcc dot gnu dot org
- Subject: ia32: resources
- From: Richard Henderson <rth at cygnus dot com>
- Date: Mon, 9 Aug 1999 16:39:00 -0700
This patch is prep work for the peephole2 pass. It unifies the two
copies of update_flow_info from the two schedulers and extends the
interface to be able to handle multiple source instructions. I.e.
the old update_flow_info would only handle the one->many case of
redistributing reg notes for split_block_insns. The new code
handles the many->many case that ocurrs in rtl->rtl peepholes.
r~
* 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.
* sched.c (split_hard_reg_notes): Delete.
(new_insn_dead_notes): Delete.
(update_n_sets): Delete.
(update_flow_info): Delete.
Index: flow.c
===================================================================
RCS file: /egcs/carton/cvsfiles/egcs/gcc/flow.c,v
retrieving revision 1.129
diff -c -p -d -r1.129 flow.c
*** flow.c 1999/08/09 13:59:44 1.129
--- flow.c 1999/08/09 23:25:54
*************** Boston, MA 02111-1307, USA. */
*** 132,137 ****
--- 132,138 ----
#include "toplev.h"
#include "recog.h"
#include "insn-flags.h"
+ #include "resource.h"
#include "obstack.h"
#define obstack_chunk_alloc xmalloc
*************** static void count_reg_sets PROTO ((rtx)
*** 333,338 ****
--- 334,346 ----
static void count_reg_references PROTO ((rtx));
static void notice_stack_pointer_modification PROTO ((rtx, rtx));
static void invalidate_mems_from_autoinc PROTO ((rtx));
+ static void maybe_remove_dead_notes PROTO ((rtx, rtx, rtx, rtx,
+ rtx, rtx));
+ static int maybe_add_dead_note_use PROTO ((rtx, rtx));
+ static int maybe_add_dead_note PROTO ((rtx, rtx, rtx));
+ static int sets_reg_or_subreg PROTO ((rtx, rtx));
+ static void update_n_sets PROTO ((rtx, int));
+ static void new_insn_dead_notes PROTO ((rtx, rtx, rtx, rtx, rtx, rtx));
void verify_flow_info PROTO ((void));
/* Find basic blocks of the current function.
*************** set_block_num (insn, bb)
*** 5029,5034 ****
--- 5037,6083 ----
int bb;
{
set_block_for_insn (insn, BASIC_BLOCK (bb));
+ }
+
+ /* Unlink a chain of insns between START and FINISH inclusive, leaving notes
+ that must be paired, and return the new chain. */
+
+ rtx
+ unlink_insn_chain (start, finish)
+ rtx start, finish;
+ {
+ rtx insert_point = PREV_INSN (start);
+ rtx chain = NULL_RTX, curr;
+
+ /* Unchain the insns one by one. It would be quicker to delete all
+ of these with a single unchaining, rather than one at a time, but
+ we need to keep the NOTE's. */
+
+ while (1)
+ {
+ rtx next = NEXT_INSN (start);
+
+ remove_insn (start);
+
+ /* ??? Despite the fact that we're patching out the insn, it's
+ still referenced in LOG_LINKS. Rather than try and track
+ them all down and remove them, just mark the insn deleted. */
+ INSN_DELETED_P (start) = 1;
+
+ if (GET_CODE (start) == NOTE && ! can_delete_note_p (start))
+ {
+ add_insn_after (start, insert_point);
+ insert_point = start;
+ }
+ else
+ {
+ if (chain != NULL)
+ {
+ NEXT_INSN (curr) = start;
+ PREV_INSN (start) = curr;
+ curr = start;
+ }
+ else
+ {
+ chain = start;
+ curr = start;
+ PREV_INSN (chain) = NULL_RTX;
+ }
+ }
+
+ if (start == finish)
+ break;
+ start = next;
+ }
+
+ if (chain != NULL_RTX)
+ NEXT_INSN (curr) = NULL_RTX;
+
+ return chain;
+ }
+
+ /* Subroutine of update_life_info. Determines whether multiple
+ REG_NOTEs need to be distributed for the hard register mentioned in
+ NOTE. This can happen if a reference to a hard register in the
+ original insns was split into several smaller hard register
+ references in the new insns. */
+
+ static void
+ split_hard_reg_notes (curr_insn, note, first, last)
+ rtx curr_insn, note, first, last;
+ {
+ rtx reg, temp, link;
+ rtx insn;
+ int n_regs, i, new_reg;
+
+ reg = XEXP (note, 0);
+
+ if (REG_NOTE_KIND (note) != REG_DEAD
+ || GET_CODE (reg) != REG
+ || REGNO (reg) >= FIRST_PSEUDO_REGISTER
+ || HARD_REGNO_NREGS (REGNO (reg), GET_MODE (reg)) == 1)
+ {
+ XEXP (note, 1) = REG_NOTES (curr_insn);
+ REG_NOTES (curr_insn) = note;
+ return;
+ }
+
+ 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 here. */
+ link = rtx_alloc (EXPR_LIST);
+ PUT_REG_NOTE_KIND (link, REG_DEAD);
+ XEXP (link, 0) = temp;
+ XEXP (link, 1) = 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;
+ }
+ }
+ }
+
+ /* SET_INSN kills REG; add a REG_DEAD note mentioning REG to the last
+ use of REG in the insns after SET_INSN and before or including
+ LAST, if necessary.
+
+ A non-zero value is returned if we added a REG_DEAD note, or if we
+ determined that a REG_DEAD note because of this particular SET
+ wasn't necessary. */
+
+ static int
+ maybe_add_dead_note (reg, set_insn, last)
+ rtx reg, set_insn, last;
+ {
+ rtx insn;
+
+ for (insn = last; insn != set_insn; insn = PREV_INSN (insn))
+ {
+ rtx set;
+
+ if (GET_RTX_CLASS (GET_CODE (insn)) == 'i'
+ && reg_overlap_mentioned_p (reg, 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 (! rtx_equal_p (insn_dest, reg))
+ {
+ /* 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 (insn, REG_UNUSED, REGNO (reg))
+ && ! find_regno_note (insn, REG_DEAD, REGNO (reg)))
+ {
+ rtx note = rtx_alloc (EXPR_LIST);
+ PUT_REG_NOTE_KIND (note, REG_DEAD);
+ XEXP (note, 0) = reg;
+ XEXP (note, 1) = REG_NOTES (insn);
+ REG_NOTES (insn) = note;
+ }
+ return 1;
+ }
+ else if (reg_overlap_mentioned_p (reg, SET_SRC (set)))
+ {
+ /* We found an instruction that both uses the register and
+ sets it, so no new REG_NOTE is needed for the previous
+ set. */
+ return 0;
+ }
+ }
+ }
+ return 0;
+ }
+
+ static int
+ maybe_add_dead_note_use (insn, dest)
+ rtx insn, dest;
+ {
+ rtx set;
+
+ /* We need to add a REG_DEAD note to the last place DEST is
+ referenced. */
+
+ 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 (! rtx_equal_p (insn_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 (insn, REG_UNUSED, REGNO (dest))
+ && ! find_regno_note (insn, REG_DEAD, REGNO (dest)))
+ {
+ rtx note = rtx_alloc (EXPR_LIST);
+ PUT_REG_NOTE_KIND (note, REG_DEAD);
+ XEXP (note, 0) = dest;
+ XEXP (note, 1) = REG_NOTES (insn);
+ REG_NOTES (insn) = note;
+ }
+ return 1;
+ }
+ }
+ return 0;
+ }
+
+ /* Find the first insn in the set of insns from FIRST to LAST inclusive
+ that contains the note NOTE. */
+ rtx
+ find_insn_with_note (note, first, last)
+ rtx note, first, last;
+ {
+ rtx insn;
+
+ for (insn = first; insn != NULL_RTX; insn = NEXT_INSN (insn))
+ {
+ rtx temp = find_reg_note (insn, REG_NOTE_KIND (note), XEXP (note, 0));
+ if (temp == note)
+ {
+ return insn;
+ }
+ if (insn == last)
+ {
+ break;
+ }
+ }
+ return NULL_RTX;
+ }
+
+ /* Subroutine of update_life_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, first, last, orig_first_insn, orig_last_insn)
+ rtx pat, insn, first, last, orig_first_insn, orig_last_insn;
+ {
+ rtx dest, tem;
+
+ if (GET_CODE (pat) != CLOBBER && GET_CODE (pat) != SET)
+ abort ();
+
+ 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 insns already used this register, we may not
+ add new notes for it. One example for a replacement that
+ needs this test is when a multi-word memory access with
+ register-indirect addressing is changed into multiple memory
+ accesses with auto-increment and one adjusting add
+ instruction for the address register.
+
+ However, there is a problem with this code. We're assuming
+ that any registers that are set in the new insns are either
+ set/referenced in the old insns (and thus "inherit" the
+ liveness of the old insns), or are registers that are dead
+ before we enter this part of the stream (and thus should be
+ dead when we leave).
+
+ To do this absolutely correctly, we must determine the actual
+ liveness of the registers before we go randomly adding
+ REG_DEAD notes. This can probably be accurately done by
+ calling mark_referenced_resources() on the old stream before
+ replacing the old insns. */
+
+ for (tem = orig_first_insn; tem != NULL_RTX; tem = NEXT_INSN (tem))
+ {
+ if (GET_RTX_CLASS (GET_CODE (tem)) == 'i'
+ && reg_referenced_p (dest, PATTERN (tem)))
+ return;
+ if (tem == orig_last_insn)
+ break;
+ }
+ /* So it's a new register, presumably only used within this
+ group of insns. Find the last insn in the set of new insns
+ that DEST is referenced in, and add a dead note to it. */
+ if (! maybe_add_dead_note (dest, insn, last))
+ {
+ /* If this is a set, it must die somewhere, unless it is the
+ dest of the original insn, and thus 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 (GET_CODE (pat) == CLOBBER)
+ {
+ rtx note = rtx_alloc (EXPR_LIST);
+ PUT_REG_NOTE_KIND (note, REG_UNUSED);
+ XEXP (note, 0) = dest;
+ XEXP (note, 1) = REG_NOTES (insn);
+ REG_NOTES (insn) = note;
+ return;
+ }
+ else
+ {
+ struct resources res;
+ rtx curr;
+
+ CLEAR_RESOURCE (&res);
+ for (curr = orig_first_insn;
+ curr != NULL_RTX;
+ curr = NEXT_INSN (curr))
+ {
+ if (GET_RTX_CLASS (GET_CODE (curr)) == 'i')
+ mark_set_resources (PATTERN (curr), &res, 0, 0);
+ if (TEST_HARD_REG_BIT (res.regs, REGNO (dest)))
+ break;
+ if (curr == orig_last_insn)
+ break;
+ }
+
+ /* In case reg was not used later, it is dead store.
+ add REG_UNUSED note. */
+ if (! TEST_HARD_REG_BIT (res.regs, REGNO (dest)))
+ {
+ rtx note = rtx_alloc (EXPR_LIST);
+ PUT_REG_NOTE_KIND (note, REG_UNUSED);
+ XEXP (note, 0) = dest;
+ XEXP (note, 1) = REG_NOTES (insn);
+ REG_NOTES (insn) = note;
+ return;
+ }
+ }
+ }
+ if (insn != first)
+ {
+ rtx set = single_set (insn);
+ /* If this is a set, scan backwards for a previous
+ reference, and attach a REG_DEAD note to it. But we don't
+ want to do it if the insn is both using and setting the
+ register.
+
+ Global registers are always live. */
+ if (set && ! reg_overlap_mentioned_p (dest, SET_SRC (pat))
+ && (REGNO (dest) >= FIRST_PSEUDO_REGISTER
+ || ! global_regs[REGNO (dest)]))
+ {
+ for (tem = PREV_INSN (insn);
+ tem != NULL_RTX; tem = PREV_INSN (tem))
+ {
+ if (maybe_add_dead_note_use (tem, dest))
+ break;
+ if (tem == first)
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ /* Subroutine of update_life_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;
+ }
+ }
+
+ /* Scan INSN for a SET that sets REG. If it sets REG via a SUBREG,
+ then return 2. If it sets REG directly, return 1. Otherwise, return
+ 0. */
+
+ static int sets_reg_or_subreg_ret;
+ static rtx sets_reg_or_subreg_rtx;
+
+ static void
+ sets_reg_or_subreg_1 (x, set)
+ rtx x, set;
+ {
+ if (rtx_equal_p (x, sets_reg_or_subreg_rtx))
+ {
+ if (x == XEXP (set, 0))
+ sets_reg_or_subreg_ret = 1;
+ else if (GET_CODE (XEXP (set, 0)) == SUBREG)
+ sets_reg_or_subreg_ret = 2;
+ }
+ }
+
+ static int
+ sets_reg_or_subreg (insn, reg)
+ rtx insn;
+ rtx reg;
+ {
+ if (GET_RTX_CLASS (GET_CODE (insn)) != 'i')
+ return 0;
+
+ sets_reg_or_subreg_ret = 0;
+ sets_reg_or_subreg_rtx = reg;
+ note_stores (PATTERN (insn), sets_reg_or_subreg_1);
+ return sets_reg_or_subreg_ret;
+ }
+
+ /* If a replaced SET_INSN (which is part of the insns between
+ OLD_FIRST_INSN and OLD_LAST_INSN inclusive) is modifying a multiple
+ register target, and the original dest is now set in the new insns
+ (between FIRST_INSN and LAST_INSN inclusive) by one or more subreg
+ sets, then the new insns no longer kill the destination of the
+ original insn.
+
+ We may also be directly using the register in the new insns before
+ setting it.
+
+ In either case, if there exists an instruction in the same basic
+ block before the replaced insns which uses the original dest (and
+ contains a corresponding REG_DEAD note), then we must remove this
+ REG_DEAD note.
+
+ SET_INSN is the insn that contains the SET; it may be a PARALLEL
+ containing the SET insn.
+
+ SET is the actual SET insn proper. */
+
+ static void
+ maybe_remove_dead_notes (set_insn, set, first_insn, last_insn,
+ old_first_insn, old_last_insn)
+ rtx set_insn, set;
+ rtx first_insn, last_insn;
+ rtx old_first_insn, old_last_insn;
+ {
+ rtx insn;
+ rtx stop_insn = NEXT_INSN (last_insn);
+ int set_type = 0;
+ rtx set_dest;
+ rtx set_pattern;
+
+ if (GET_RTX_CLASS (GET_CODE (set)) != 'i')
+ return;
+
+ set_pattern = PATTERN (set);
+
+ if (GET_CODE (set_pattern) == PARALLEL)
+ {
+ int i;
+
+ for (i = 0; i < XVECLEN (set_pattern, 0); i++)
+ {
+ maybe_remove_dead_notes (set_insn, XVECEXP (set_pattern, 0, i),
+ first_insn, last_insn,
+ old_first_insn, old_last_insn);
+ }
+ return;
+ }
+
+ if (GET_CODE (set_pattern) != SET)
+ {
+ return;
+ }
+
+ set_dest = SET_DEST (set_pattern);
+
+ if (GET_CODE (set_dest) != REG)
+ {
+ return;
+ }
+
+ /* We have a set of a REG. First we need to determine if this set is
+ both using and setting the register. (FIXME: if this is in a
+ PARALLEL, we will have to check the other exprs as well.) */
+ if (reg_overlap_mentioned_p (set_dest, SET_SRC (set_pattern)))
+ {
+ return;
+ }
+
+ /* Now determine if we used or set the register in the old insns
+ previous to this one. */
+
+ for (insn = old_first_insn; insn != set_insn; insn = NEXT_INSN (insn))
+ {
+ if (reg_overlap_mentioned_p (set_dest, insn))
+ {
+ return;
+ }
+ }
+
+ /* Now determine if we're setting it in the new insns, or using
+ it. */
+ for (insn = first_insn; insn != stop_insn; insn = NEXT_INSN (insn))
+ {
+ set_type = sets_reg_or_subreg (insn, set_dest);
+ if (set_type != 0)
+ {
+ break;
+ }
+ else if (reg_overlap_mentioned_p (set_dest, insn))
+ {
+ /* Is the reg now used in this new insn? -- This is probably an
+ error. */
+ set_type = 2;
+ break;
+ }
+ }
+ if (set_type == 2)
+ {
+ /* The register is being set via a SUBREG or is being used in
+ some other way, so it's no longer dead.
+
+ Search backwards from first_insn, looking for the first insn
+ that uses the original dest. Stop if we pass a CODE_LABEL or
+ a JUMP_INSN.
+
+ If we find such an insn and it has a REG_DEAD note referring
+ to the original dest, then delete the note. */
+
+ for (insn = first_insn; insn != NULL_RTX; 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 (set_dest, insn))
+ {
+ rtx note = find_regno_note (insn, REG_DEAD, REGNO (set_dest));
+ if (note != NULL_RTX)
+ {
+ remove_note (insn, note);
+ }
+ /* ??? -- Is this right? */
+ break;
+ }
+ }
+ }
+ else if (set_type == 0)
+ {
+ /* The reg is not being set or used in the new insns at all. */
+ int i, regno;
+
+ /* Should never reach here for a pseudo reg. */
+ if (REGNO (set_dest) >= FIRST_PSEUDO_REGISTER)
+ abort ();
+
+ /* This can happen for a hard register, if the new insns do not
+ contain instructions which would be no-ops referring to the
+ old registers.
+
+ 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 (set_dest);
+ for (i = HARD_REGNO_NREGS (regno, GET_MODE (set_dest)) - 1;
+ i >= 0; i--)
+ {
+ if (! refers_to_regno_p (regno + i, regno + i + 1, set,
+ NULL_PTR))
+ break;
+ }
+ if (i >= 0)
+ abort ();
+ }
+ }
+
+ /* Updates all flow-analysis related quantities (including REG_NOTES) for
+ the insns from FIRST to LAST inclusive that were created by replacing
+ the insns from ORIG_INSN_FIRST to ORIG_INSN_LAST inclusive. NOTES
+ are the original REG_NOTES. */
+
+ void
+ update_life_info (notes, first, last, orig_first_insn, orig_last_insn)
+ rtx notes;
+ rtx first, last;
+ rtx orig_first_insn, orig_last_insn;
+ {
+ rtx insn, note;
+ rtx next;
+ rtx orig_dest, temp;
+ rtx orig_insn;
+ rtx tem;
+
+ /* Get and save the destination set by the original insn, if there
+ was only one insn replaced. */
+
+ if (orig_first_insn == orig_last_insn)
+ {
+ orig_insn = orig_first_insn;
+ orig_dest = single_set (orig_insn);
+ if (orig_dest)
+ orig_dest = SET_DEST (orig_dest);
+ }
+ else
+ {
+ orig_insn = NULL_RTX;
+ orig_dest = NULL_RTX;
+ }
+
+ /* Move REG_NOTES from the original insns 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 mentioned. */
+
+ for (insn = last; ; insn = PREV_INSN (insn))
+ {
+ if (GET_RTX_CLASS (GET_CODE (insn)) == 'i'
+ && reg_mentioned_p (XEXP (note, 0), PATTERN (insn)))
+ {
+ /* Sometimes need to convert REG_UNUSED notes to
+ REG_DEAD notes. */
+ if (REG_NOTE_KIND (note) == REG_UNUSED
+ && GET_CODE (XEXP (note, 0)) == REG
+ && ! dead_or_set_p (insn, XEXP (note, 0)))
+ {
+ PUT_REG_NOTE_KIND (note, REG_DEAD);
+ }
+ split_hard_reg_notes (insn, note, first, last);
+ /* The reg only dies in one insn, the last one that uses
+ it. */
+ break;
+ }
+ /* It must die somewhere, fail if we couldn't find where it died.
+
+ We abort because otherwise the register will be live
+ longer than it should, and we'll probably take an
+ abort later. What we should do instead is search back
+ and find the appropriate places to insert the note. */
+ if (insn == first)
+ {
+ if (REG_NOTE_KIND (note) == REG_DEAD)
+ {
+ abort ();
+ }
+ break;
+ }
+ }
+ break;
+
+ case REG_WAS_0:
+ {
+ rtx note_dest;
+
+ /* 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;
+ if (orig_insn != NULL_RTX)
+ {
+ note_dest = orig_dest;
+ }
+ else
+ {
+ note_dest = find_insn_with_note (note, first, last);
+ if (note_dest != NULL_RTX)
+ {
+ note_dest = single_set (orig_dest);
+ if (note_dest != NULL_RTX)
+ {
+ note_dest = SET_DEST (orig_dest);
+ }
+ }
+ }
+ /* 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 (! note_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), note_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 (note_dest) == REG
+ && REGNO (note_dest) < FIRST_PSEUDO_REGISTER
+ && HARD_REGNO_NREGS (REGNO (note_dest),
+ GET_MODE (note_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 we are replacing multiple insns, just drop the note. */
+
+ if (! orig_insn)
+ break;
+
+ 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
+ && GET_MODE_SIZE (GET_MODE (orig_dest)) > MOVE_MAX)
+ 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) = gen_rtx_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 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 = 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, first, last,
+ orig_first_insn, orig_last_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)
+ {
+ rtx parpat = XVECEXP (pat, 0, i);
+
+ new_insn_dead_notes (parpat, insn, first, last,
+ orig_first_insn, orig_last_insn);
+ }
+ }
+ }
+ if (insn == last)
+ {
+ break;
+ }
+ }
+
+ /* Check to see if we have any REG_DEAD notes on insns previous to
+ the new ones that are now incorrect and need to be removed. */
+
+ for (insn = orig_first_insn; ; insn = NEXT_INSN (insn))
+ {
+ maybe_remove_dead_notes (insn, insn, first, last,
+ orig_first_insn, orig_last_insn);
+
+ if (insn == orig_last_insn)
+ break;
+ }
+
+ /* Update reg_n_sets. This is necessary to prevent local alloc from
+ converting REG_EQUAL notes to REG_EQUIV when the new insns are setting
+ a reg multiple times instead of once. */
+
+ for (tem = orig_first_insn; tem != NULL_RTX; tem = NEXT_INSN (tem))
+ {
+ rtx x;
+ RTX_CODE code;
+
+ if (GET_RTX_CLASS (GET_CODE (tem)) != 'i')
+ continue;
+
+ x = PATTERN (tem);
+ 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 (tem == orig_last_insn)
+ break;
+ }
+
+ for (insn = first; ; insn = NEXT_INSN (insn))
+ {
+ rtx x = PATTERN (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);
+ }
+ }
+
+ if (insn == last)
+ break;
+ }
+ }
+
+ /* Prepends the set of REG_NOTES in NEW to NOTES, and returns NEW. */
+ static rtx
+ prepend_reg_notes (notes, new)
+ rtx notes, new;
+ {
+ rtx end;
+
+ if (new == NULL_RTX)
+ {
+ return notes;
+ }
+ if (notes == NULL_RTX)
+ {
+ return new;
+ }
+ end = new;
+ while (XEXP (end, 1) != NULL_RTX)
+ {
+ end = XEXP (end, 1);
+ }
+ XEXP (end, 1) = notes;
+ return new;
+ }
+
+ /* Replace the insns from FIRST to LAST inclusive with the set of insns in
+ NEW, and update the life analysis info accordingly. */
+ void
+ replace_insns (first, last, first_new, notes)
+ rtx first, last, first_new, notes;
+ {
+ rtx stop = NEXT_INSN (last);
+ rtx last_new;
+ rtx curr, next;
+ rtx prev = PREV_INSN (first);
+ int i;
+
+ if (notes == NULL_RTX)
+ {
+ for (curr = first; curr != stop; curr = NEXT_INSN (curr))
+ {
+ notes = prepend_reg_notes (notes, REG_NOTES (curr));
+ }
+ }
+ for (curr = first; curr; curr = next)
+ {
+ next = NEXT_INSN (curr);
+ delete_insn (curr);
+ if (curr == last)
+ break;
+ }
+ last_new = emit_insn_after (first_new, prev);
+ first_new = NEXT_INSN (prev);
+ for (i = 0; i < n_basic_blocks; i++)
+ {
+ if (BLOCK_HEAD (i) == first)
+ {
+ BLOCK_HEAD (i) = first_new;
+ }
+ if (BLOCK_END (i) == last)
+ {
+ BLOCK_END (i) = last_new;
+ }
+ }
+ /* This is probably bogus. */
+ if (first_new == last_new)
+ {
+ if (GET_CODE (first_new) == SEQUENCE)
+ {
+ first_new = XVECEXP (first_new, 0, 0);
+ last_new = XVECEXP (last_new, 0, XVECLEN (last_new, 0) - 1);
+ }
+ }
+ update_life_info (notes, first_new, last_new, first, last);
}
/* Verify the CFG consistency. This function check some CFG invariants and
Index: haifa-sched.c
===================================================================
RCS file: /egcs/carton/cvsfiles/egcs/gcc/haifa-sched.c,v
retrieving revision 1.92
diff -c -p -d -r1.92 haifa-sched.c
*** haifa-sched.c 1999/08/09 13:59:45 1.92
--- haifa-sched.c 1999/08/09 23:25:54
*************** static void attach_deaths_insn PROTO ((r
*** 452,460 ****
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 *, char *));
static int insn_issue_delay PROTO ((rtx));
static int birthing_insn_p PROTO ((rtx));
--- 452,457 ----
*************** schedule_region (rgn)
*** 7773,8455 ****
FREE_REG_SET (reg_pending_sets);
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
--- 7770,7775 ----
Index: output.h
===================================================================
RCS file: /egcs/carton/cvsfiles/egcs/gcc/output.h,v
retrieving revision 1.26
diff -c -p -d -r1.26 output.h
*** output.h 1999/08/09 13:59:47 1.26
--- output.h 1999/08/09 23:25:54
*************** extern void find_basic_blocks PR
*** 132,137 ****
--- 132,138 ----
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. */
Index: recog.c
===================================================================
RCS file: /egcs/carton/cvsfiles/egcs/gcc/recog.c,v
retrieving revision 1.35
diff -c -p -d -r1.35 recog.c
*** recog.c 1999/08/09 13:59:47 1.35
--- recog.c 1999/08/09 23:25:54
*************** split_block_insns (b, do_split)
*** 2669,2675 ****
/* try_split returns the NOTE that INSN became. */
first = NEXT_INSN (first);
#ifdef INSN_SCHEDULING
! update_flow_info (notes, first, last, insn);
#endif
PUT_CODE (insn, NOTE);
NOTE_SOURCE_FILE (insn) = 0;
--- 2669,2675 ----
/* try_split returns the NOTE that INSN became. */
first = NEXT_INSN (first);
#ifdef INSN_SCHEDULING
! update_life_info (notes, first, last, insn, insn);
#endif
PUT_CODE (insn, NOTE);
NOTE_SOURCE_FILE (insn) = 0;
Index: resource.c
===================================================================
RCS file: /egcs/carton/cvsfiles/egcs/gcc/resource.c,v
retrieving revision 1.7
diff -c -p -d -r1.7 resource.c
*** resource.c 1999/08/09 13:59:50 1.7
--- resource.c 1999/08/09 23:25:54
*************** find_free_register (current_insn, class_
*** 1264,1277 ****
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
{
! int success = 1;
! if (! TEST_HARD_REG_BIT (reg_class_contents[class], i))
continue;
! for (j = HARD_REGNO_NREGS (i, mode) - 1; j >= 0; j--)
{
! if (TEST_HARD_REG_BIT (*reg_set, i + j)
! || TEST_HARD_REG_BIT (used.regs, i + j))
{
success = 0;
break;
--- 1264,1296 ----
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
{
! int regno;
! int success;
! #ifdef REG_ALLOC_ORDER
! regno = reg_alloc_order [i];
! #else
! regno = i;
! #endif
!
! /* Don't allocate fixed registers. */
! if (fixed_regs[regno])
continue;
! /* 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, regno + j)
! || TEST_HARD_REG_BIT (used.regs, regno + j))
{
success = 0;
break;
*************** find_free_register (current_insn, class_
*** 1279,1290 ****
}
if (success)
{
! for (j = HARD_REGNO_NREGS (i, mode) - 1; j >= 0; j--)
{
! SET_HARD_REG_BIT (*reg_set, i + j);
}
! return gen_rtx_REG (mode, i);
}
}
return NULL_RTX;
}
--- 1298,1330 ----
}
if (success)
{
! for (j = HARD_REGNO_NREGS (regno, mode) - 1; j >= 0; j--)
{
! SET_HARD_REG_BIT (*reg_set, regno + j);
}
! 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;
}
Index: rtl.h
===================================================================
RCS file: /egcs/carton/cvsfiles/egcs/gcc/rtl.h,v
retrieving revision 1.110
diff -c -p -d -r1.110 rtl.h
*** rtl.h 1999/08/09 13:59:52 1.110
--- rtl.h 1999/08/09 23:25:54
*************** extern void recompute_reg_usage PROTO (
*** 1415,1420 ****
--- 1415,1421 ----
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));
Index: sched.c
===================================================================
RCS file: /egcs/carton/cvsfiles/egcs/gcc/sched.c,v
retrieving revision 1.39
diff -c -p -d -r1.39 sched.c
*** sched.c 1999/08/09 13:59:52 1.39
--- sched.c 1999/08/09 23:25:55
*************** static int new_sometimes_live PROTO((st
*** 344,352 ****
static void finish_sometimes_live PROTO((struct sometimes *, int));
static rtx reemit_notes PROTO((rtx, rtx));
static void schedule_block PROTO((int, FILE *));
- 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));
/* Main entry point of this file. */
void schedule_insns PROTO((FILE *));
--- 344,349 ----
*************** ret:
*** 3543,4226 ****
FREE_REG_SET (old_live_regs);
return;
- }
-
- /* 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 here. */
- link = rtx_alloc (EXPR_LIST);
- PUT_REG_NOTE_KIND (link, REG_DEAD);
- XEXP (link, 0) = temp;
- XEXP (link, 1) = 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 = rtx_alloc (EXPR_LIST);
- PUT_REG_NOTE_KIND (note, REG_DEAD);
- XEXP (note, 0) = dest;
- XEXP (note, 1) = 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 = rtx_alloc (EXPR_LIST);
- PUT_REG_NOTE_KIND (note, REG_UNUSED);
- XEXP (note, 0) = dest;
- XEXP (note, 1) = 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) > MOVE_MAX)
- 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) = gen_rtx_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 = rtx_alloc (EXPR_LIST);
- PUT_REG_NOTE_KIND (note, REG_DEAD);
- XEXP (note, 0) = dest;
- XEXP (note, 1) = 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 = PATTERN (insn);
- int 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
--- 3540,3545 ----