summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorrth <rth@138bc75d-0d04-0410-961f-82ee72b054a4>1999-08-24 22:35:55 +0000
committerrth <rth@138bc75d-0d04-0410-961f-82ee72b054a4>1999-08-24 22:35:55 +0000
commit0cc10fd32985e2c604c4e0fdd10af4554f759b36 (patch)
treee86a9635ca292ea67589cef742a73eea422d2d19
parent131872590b7c1c8f0cef6ec3dccc4bb8279dc351 (diff)
downloadppe42-gcc-0cc10fd32985e2c604c4e0fdd10af4554f759b36.tar.gz
ppe42-gcc-0cc10fd32985e2c604c4e0fdd10af4554f759b36.zip
* 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. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@28828 138bc75d-0d04-0410-961f-82ee72b054a4
-rw-r--r--gcc/ChangeLog28
-rw-r--r--gcc/flow.c1049
-rw-r--r--gcc/haifa-sched.c680
-rw-r--r--gcc/output.h1
-rw-r--r--gcc/recog.c2
-rw-r--r--gcc/resource.c56
-rw-r--r--gcc/rtl.h1
7 files changed, 1128 insertions, 689 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index b107d30a121..e98401a62e3 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -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
diff --git a/gcc/flow.c b/gcc/flow.c
index 6b2be37a13b..27e8355bb34 100644
--- a/gcc/flow.c
+++ b/gcc/flow.c
@@ -132,6 +132,7 @@ Boston, MA 02111-1307, USA. */
#include "toplev.h"
#include "recog.h"
#include "insn-flags.h"
+#include "resource.h"
#include "obstack.h"
#define obstack_chunk_alloc xmalloc
@@ -333,6 +334,13 @@ static void count_reg_sets PROTO ((rtx));
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.
@@ -5034,6 +5042,1047 @@ set_block_num (insn, 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
aborts when something is wrong. Hope that this function will help to
convert many optimization passes to preserve CFG consistent.
diff --git a/gcc/haifa-sched.c b/gcc/haifa-sched.c
index 3a5a64ab8f8..d63e04c816f 100644
--- a/gcc/haifa-sched.c
+++ b/gcc/haifa-sched.c
@@ -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. */
diff --git a/gcc/output.h b/gcc/output.h
index f3375e5b5b5..dd8e83438f3 100644
--- a/gcc/output.h
+++ b/gcc/output.h
@@ -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. */
diff --git a/gcc/recog.c b/gcc/recog.c
index 9e06fb5a976..f41c3d18959 100644
--- a/gcc/recog.c
+++ b/gcc/recog.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;
diff --git a/gcc/resource.c b/gcc/resource.c
index d200f30dfce..b310dd654d3 100644
--- a/gcc/resource.c
+++ b/gcc/resource.c
@@ -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;
+
+#ifdef REG_ALLOC_ORDER
+ regno = reg_alloc_order [i];
+#else
+ regno = i;
+#endif
- if (! TEST_HARD_REG_BIT (reg_class_contents[class], i))
+ /* 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;
- for (j = HARD_REGNO_NREGS (i, mode) - 1; j >= 0; j--)
+ /* 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;
+}
diff --git a/gcc/rtl.h b/gcc/rtl.h
index c5fc12afb65..40a3125342f 100644
--- a/gcc/rtl.h
+++ b/gcc/rtl.h
@@ -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));
OpenPOWER on IntegriCloud