+/* Optimize jump instructions, for GNU compiler.
+ Copyright (C) 1987, 1988, 1989 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 1, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+/* This is the jump-optimization pass of the compiler.
+ It is run two or three times: once before cse, sometimes once after cse,
+ and once after reload (before final).
+
+ jump_optimize deletes unreachable code and labels that are not used.
+ It also deletes jumps that jump to the following insn,
+ and simplifies jumps around unconditional jumps and jumps
+ to unconditional jumps.
+
+ Each CODE_LABEL has a count of the times it is used
+ stored in the LABEL_NUSES internal field, and each JUMP_INSN
+ has one label that it refers to stored in the
+ JUMP_LABEL internal field. With this we can detect labels that
+ become unused because of the deletion of all the jumps that
+ formerly used them. The JUMP_LABEL info is sometimes looked
+ at by later passes.
+
+ Optionally, cross-jumping can be done. Currently it is done
+ only the last time (when after reload and before final).
+ In fact, the code for cross-jumping now assumes that register
+ allocation has been done, since it uses `rtx_renumbered_equal_p'.
+
+ Jump optimization is done after cse when cse's constant-propagation
+ causes jumps to become unconditional or to be deleted.
+
+ Unreachable loops are not detected here, because the labels
+ have references and the insns appear reachable from the labels.
+ find_basic_blocks in flow.c finds and deletes such loops.
+
+ The subroutines delete_insn, redirect_jump, invert_jump, next_real_insn
+ and prev_real_insn are used from other passes as well. */
+
+#include "config.h"
+#include "rtl.h"
+#include "flags.h"
+#include "regs.h"
+
+/* ??? Eventually must record somehow the labels used by jumps
+ from nested functions. */
+/* Pre-record the next or previous real insn for each label?
+ No, this pass is very fast anyway. */
+/* Condense consecutive labels?
+ This would make life analysis faster, maybe. */
+/* Optimize jump y; x: ... y: jumpif... x?
+ Don't know if it is worth bothering with. */
+/* Optimize two cases of conditional jump to conditional jump?
+ This can never delete any instruction or make anything dead,
+ or even change what is live at any point.
+ So perhaps let combiner do it. */
+
+/* Vector indexed by uid.
+ For each CODE_LABEL, index by its uid to get first unconditional jump
+ that jumps to the label.
+ For each JUMP_INSN, index by its uid to get the next unconditional jump
+ that jumps to the same label.
+ Element 0 is the start of a chain of all return insns.
+ (It is safe to use element 0 because insn uid 0 is not used. */
+
+rtx *jump_chain;
+
+rtx delete_insn ();
+void redirect_jump ();
+void invert_jump ();
+rtx next_real_insn ();
+rtx prev_real_insn ();
+rtx next_label ();
+
+static void mark_jump_label ();
+static void delete_jump ();
+static void squeeze_block_notes ();
+void invert_exp ();
+static void redirect_exp ();
+static rtx follow_jumps ();
+static int tension_vector_labels ();
+static void find_cross_jump ();
+static void do_cross_jump ();
+static enum rtx_code reverse_condition ();
+static int jump_back_p ();
+int condjump_p ();
+\f
+/* Delete no-op jumps and optimize jumps to jumps
+ and jumps around jumps.
+ Delete unused labels and unreachable code.
+ If CROSS_JUMP is nonzero, detect matching code
+ before a jump and its destination and unify them.
+ If NOOP_MOVES is nonzero, also delete no-op move insns.
+
+ If `optimize' is zero, don't change any code,
+ just determine whether control drops off the end of the function.
+ This case occurs when we have -W and not -O.
+ It works because `delete_insn' checks the value of `optimize'
+ and refrains from actually deleting when that is 0. */
+
+void
+jump_optimize (f, cross_jump, noop_moves)
+ rtx f;
+{
+ register rtx insn;
+ int changed;
+ int first = 1;
+ int max_uid = 0;
+ rtx last_insn;
+
+ /* Initialize LABEL_NUSES and JUMP_LABEL fields. */
+
+ for (insn = f; insn; insn = NEXT_INSN (insn))
+ {
+ if (GET_CODE (insn) == CODE_LABEL)
+ LABEL_NUSES (insn) = 0;
+ if (GET_CODE (insn) == JUMP_INSN)
+ JUMP_LABEL (insn) = 0;
+ if (INSN_UID (insn) > max_uid)
+ max_uid = INSN_UID (insn);
+ }
+
+ max_uid++;
+
+ jump_chain = (rtx *) alloca (max_uid * sizeof (rtx));
+ bzero (jump_chain, max_uid * sizeof (rtx));
+
+ /* Delete insns following barriers, up to next label. */
+
+ for (insn = f; insn;)
+ {
+ if (GET_CODE (insn) == BARRIER)
+ {
+ insn = NEXT_INSN (insn);
+ while (insn != 0 && GET_CODE (insn) != CODE_LABEL)
+ {
+ if (GET_CODE (insn) == NOTE
+ && NOTE_LINE_NUMBER (insn) != NOTE_INSN_FUNCTION_END)
+ insn = NEXT_INSN (insn);
+ else
+ insn = delete_insn (insn);
+ }
+ /* INSN is now the code_label. */
+ }
+ else
+ insn = NEXT_INSN (insn);
+ }
+
+ /* Mark the label each jump jumps to.
+ Combine consecutive labels, and count uses of labels.
+
+ For each label, make a chain (using `jump_chain')
+ of all the *unconditional* jumps that jump to it;
+ also make a chain of all returns. */
+
+ for (insn = f; insn; insn = NEXT_INSN (insn))
+ if (GET_CODE (insn) == JUMP_INSN && ! INSN_DELETED_P (insn))
+ {
+ mark_jump_label (PATTERN (insn), insn, cross_jump);
+ if (JUMP_LABEL (insn) != 0 && simplejump_p (insn))
+ {
+ jump_chain[INSN_UID (insn)]
+ = jump_chain[INSN_UID (JUMP_LABEL (insn))];
+ jump_chain[INSN_UID (JUMP_LABEL (insn))] = insn;
+ }
+ if (GET_CODE (PATTERN (insn)) == RETURN)
+ {
+ jump_chain[INSN_UID (insn)] = jump_chain[0];
+ jump_chain[0] = insn;
+ }
+ }
+
+ /* Delete all labels already not referenced.
+ Also find the last insn. */
+
+ last_insn = 0;
+ for (insn = f; insn; )
+ {
+ if (GET_CODE (insn) == CODE_LABEL && LABEL_NUSES (insn) == 0)
+ insn = delete_insn (insn);
+ else
+ {
+ last_insn = insn;
+ insn = NEXT_INSN (insn);
+ }
+ }
+
+ if (!optimize)
+ {
+ /* See if there is still a NOTE_INSN_FUNCTION_END in this function.
+ If so record that this function can drop off the end. */
+
+ insn = last_insn;
+ {
+ int n_labels = 1;
+ while (insn
+ /* One label can follow the end-note: the return label. */
+ && ((GET_CODE (insn) == CODE_LABEL && n_labels-- > 0)
+ /* Ordinary insns can follow it if returning a structure. */
+ || GET_CODE (insn) == INSN
+ /* If machine uses explicit RETURN insns, no epilogue,
+ then one of them follows the note. */
+ || (GET_CODE (insn) == JUMP_INSN
+ && GET_CODE (PATTERN (insn)) == RETURN)
+ /* Other kinds of notes can follow also. */
+ || (GET_CODE (insn) == NOTE
+ && NOTE_LINE_NUMBER (insn) != NOTE_INSN_FUNCTION_END)))
+ insn = PREV_INSN (insn);
+ }
+
+ if (insn && GET_CODE (insn) == NOTE
+ && NOTE_LINE_NUMBER (insn) == NOTE_INSN_FUNCTION_END
+ && ! INSN_DELETED_P (insn))
+ {
+ extern int current_function_returns_null;
+ current_function_returns_null = 1;
+ }
+ /* Zero the "deleted" flag of all the "deleted" insns. */
+ for (insn = f; insn; insn = NEXT_INSN (insn))
+ INSN_DELETED_P (insn) = 0;
+ return;
+ }
+
+ if (noop_moves)
+ for (insn = f; insn; )
+ {
+ register rtx next = NEXT_INSN (insn);
+
+ if (GET_CODE (insn) == INSN)
+ {
+ register rtx body = PATTERN (insn);
+
+#if 0 /* Keep these insns, since they are used for conditional branch
+ scheduling peepholes on the sparc. */
+#endif
+ /* Delete insns that existed just to advise flow-analysis. */
+
+ if (GET_CODE (body) == USE
+ || GET_CODE (body) == CLOBBER)
+ delete_insn (insn);
+ else
+
+ /* Detect and delete no-op move instructions
+ resulting from not allocating a parameter in a register. */
+
+ if (GET_CODE (body) == SET
+ && (SET_DEST (body) == SET_SRC (body)
+ || (GET_CODE (SET_DEST (body)) == MEM
+ && GET_CODE (SET_SRC (body)) == MEM
+ && rtx_equal_p (SET_SRC (body), SET_DEST (body))))
+ && ! (GET_CODE (SET_DEST (body)) == MEM
+ && MEM_VOLATILE_P (SET_DEST (body)))
+ && ! (GET_CODE (SET_SRC (body)) == MEM
+ && MEM_VOLATILE_P (SET_SRC (body))))
+ delete_insn (insn);
+
+ /* Detect and ignore no-op move instructions
+ resulting from smart or fortuitous register allocation. */
+
+ else if (GET_CODE (body) == SET)
+ {
+ int sreg = true_regnum (SET_SRC (body));
+ int dreg = true_regnum (SET_DEST (body));
+
+ if (sreg == dreg && sreg >= 0)
+ delete_insn (insn);
+ else if (sreg >= 0 && dreg >= 0)
+ {
+ rtx tem = find_equiv_reg (0, insn, 0,
+ sreg, 0, dreg,
+ GET_MODE (SET_SRC (body)));
+
+#ifdef PRESERVE_DEATH_INFO_REGNO_P
+ /* Deleting insn could lose a death-note for SREG or DREG
+ so don't do it if final needs accurate death-notes. */
+ if (! PRESERVE_DEATH_INFO_REGNO_P (sreg)
+ && ! PRESERVE_DEATH_INFO_REGNO_P (dreg))
+#endif
+ if (tem != 0
+ && GET_MODE (tem) == GET_MODE (SET_DEST (body)))
+ delete_insn (insn);
+ }
+ }
+ }
+ insn = next;
+ }
+
+ /* Now iterate optimizing jumps until nothing changes over one pass. */
+ changed = 1;
+ while (changed)
+ {
+ register rtx next;
+ changed = 0;
+
+ for (insn = f; insn; insn = next)
+ {
+#if 0
+ /* If NOT the first iteration, if this is the last jump pass
+ (just before final), do the special peephole optimizations.
+ Avoiding the first iteration gives ordinary jump opts
+ a chance to work before peephole opts. */
+
+ if (noop_moves && !first && !flag_no_peephole)
+ if (GET_CODE (insn) == INSN || GET_CODE (insn) == JUMP_INSN)
+ peephole (insn);
+#endif
+
+ /* That could have deleted some insns after INSN, so check now
+ what the following insn is. */
+
+ next = NEXT_INSN (insn);
+
+ /* Tension the labels in dispatch tables. */
+
+ if (GET_CODE (insn) == JUMP_INSN)
+ {
+ if (GET_CODE (PATTERN (insn)) == ADDR_VEC)
+ changed |= tension_vector_labels (PATTERN (insn), 0, noop_moves);
+ if (GET_CODE (PATTERN (insn)) == ADDR_DIFF_VEC)
+ changed |= tension_vector_labels (PATTERN (insn), 1, noop_moves);
+ }
+
+ /* Don't allow dropping through into a dispatch table.
+ That means the dispatch insn itself was deleted,
+ so delete the table too. */
+
+ if (GET_CODE (insn) == JUMP_INSN)
+ {
+ /* Note: the corresponding job for ADDR_VEC is done
+ in delete_insn. */
+
+ /* A vector of offsets is unused if its label
+ is used only once (i.e., from the vector). */
+ if (GET_CODE (PATTERN (insn)) == ADDR_DIFF_VEC
+ && LABEL_NUSES (XEXP (XEXP (PATTERN (insn), 0), 0)) == 1)
+ {
+ /* So delete both label and vector. */
+ delete_insn (PREV_INSN (insn));
+ delete_insn (insn);
+ changed = 1;
+ }
+ }
+
+ if (GET_CODE (insn) == JUMP_INSN && JUMP_LABEL (insn))
+ {
+ register rtx reallabelprev = prev_real_insn (JUMP_LABEL (insn));
+ rtx temp;
+
+ /* Detect jump to following insn. */
+ if (reallabelprev == insn && condjump_p (insn))
+ {
+ delete_jump (insn);
+ changed = 1;
+ }
+ /* Detect worthless conditional jump. */
+ else if ((temp = next_real_insn (insn))
+ && GET_CODE (temp) == JUMP_INSN
+ && condjump_p (insn)
+ && simplejump_p (temp)
+ && JUMP_LABEL (insn) == JUMP_LABEL (temp))
+ {
+ delete_jump (insn);
+ changed = 1;
+ next = NEXT_INSN (insn);
+ }
+ /* A jump to a return becomes a return. */
+ else if (simplejump_p (insn)
+ && (temp = next_real_insn (JUMP_LABEL (insn))) != 0
+ && GET_CODE (PATTERN (temp)) == RETURN)
+ {
+ PATTERN (insn) = PATTERN (temp);
+ /* Re-recognize this insn. */
+ INSN_CODE (insn) = -1;
+ }
+ /* Detect jumping over an unconditional jump. */
+ else if (reallabelprev != 0
+ && GET_CODE (reallabelprev) == JUMP_INSN
+ && prev_real_insn (reallabelprev) == insn
+ && no_labels_between_p (insn, reallabelprev)
+ && simplejump_p (reallabelprev)
+ /* Ignore this if INSN is a hairy kind of jump,
+ since they may not be invertible.
+ This is conservative; could instead construct
+ the inverted insn and try recognizing it. */
+ && condjump_p (insn))
+ {
+ /* Delete the original unconditional jump (and barrier). */
+ /* But don't let its destination go with it. */
+ ++LABEL_NUSES (JUMP_LABEL (reallabelprev));
+ delete_insn (reallabelprev);
+ /* Now change the condition, and make it go to the
+ place the deleted jump went to.
+ This may cause the label after the deletion to go away.
+ But now that the unconditional jump and its barrier
+ are gone, that is ok. */
+ invert_jump (insn, JUMP_LABEL (reallabelprev));
+ --LABEL_NUSES (JUMP_LABEL (reallabelprev));
+ next = insn;
+ changed = 1;
+ }
+ else
+ {
+ /* Detect a jump to a jump. */
+ {
+ register rtx nlabel
+ = follow_jumps (JUMP_LABEL (insn), noop_moves);
+ if (nlabel != JUMP_LABEL (insn))
+ {
+ redirect_jump (insn, nlabel);
+ changed = 1;
+ next = insn;
+ }
+ }
+
+ /* Look for if (foo) bar; else break; */
+ /* The insns look like this:
+ insn = condjump label1;
+ ...range1 (some insns)...
+ jump label2;
+ label1:
+ ...range2 (some insns)...
+ jump somewhere unconditionally
+ label2: */
+ {
+ rtx label1 = next_label (insn);
+ rtx range1end = label1 ? prev_real_insn (label1) : 0;
+ /* Don't do this optimization on the first round, so that
+ jump-around-a-jump gets simplified before we ask here
+ whether a jump is unconditional. */
+ if (! first
+ /* Make sure INSN is something we can invert. */
+ && condjump_p (insn)
+ && JUMP_LABEL (insn) == label1
+ && LABEL_NUSES (label1) == 1
+ && GET_CODE (range1end) == JUMP_INSN
+ && simplejump_p (range1end))
+ {
+ rtx label2 = next_label (label1);
+ rtx range2end = label2 ? prev_real_insn (label2) : 0;
+ if (range1end != range2end
+ && JUMP_LABEL (range1end) == label2
+ && GET_CODE (range2end) == JUMP_INSN
+ && GET_CODE (NEXT_INSN (range2end)) == BARRIER)
+ {
+ rtx range1beg = next_real_insn (insn);
+ rtx range2beg = next_real_insn (label1);
+ rtx range1after, range2after;
+ rtx range1before, range2before;
+
+ /* Don't move NOTEs for blocks; shift them
+ outside the ranges, where they'll stay put. */
+ squeeze_block_notes (range1beg, range1end);
+ squeeze_block_notes (range2beg, range2end);
+
+ /* Get current surrounds of the 2 ranges. */
+ range1before = PREV_INSN (range1beg);
+ range2before = PREV_INSN (range2beg);
+ range1after = NEXT_INSN (range1end);
+ range2after = NEXT_INSN (range2end);
+
+ /* Splice range2 where range1 was. */
+ NEXT_INSN (range1before) = range2beg;
+ PREV_INSN (range2beg) = range1before;
+ NEXT_INSN (range2end) = range1after;
+ PREV_INSN (range1after) = range2end;
+ /* Splice range1 where range2 was. */
+ NEXT_INSN (range2before) = range1beg;
+ PREV_INSN (range1beg) = range2before;
+ NEXT_INSN (range1end) = range2after;
+ PREV_INSN (range2after) = range1end;
+ /* Invert the jump condition, so we
+ still execute the same insns in each case. */
+ invert_jump (insn, label1);
+ changed = 1;
+ continue;
+ }
+ }
+ }
+
+ /* Now that the jump has been tensioned,
+ try cross jumping: check for identical code
+ before the jump and before its target label. */
+
+ /* First, cross jumping of conditional jumps: */
+
+ if (cross_jump && condjump_p (insn))
+ {
+ rtx newjpos, newlpos;
+ rtx x = prev_real_insn (JUMP_LABEL (insn));
+
+ /* A conditional jump may be crossjumped
+ only if the place it jumps to follows
+ an opposing jump that comes back here. */
+
+ if (x != 0 && ! jump_back_p (x, insn))
+ /* We have no opposing jump;
+ cannot cross jump this insn. */
+ x = 0;
+
+ newjpos = 0;
+ /* TARGET is nonzero if it is ok to cross jump
+ to code before TARGET. If so, see if matches. */
+ if (x != 0)
+ find_cross_jump (insn, x, 2,
+ &newjpos, &newlpos);
+
+ if (newjpos != 0)
+ {
+ do_cross_jump (insn, newjpos, newlpos);
+ /* Make the old conditional jump
+ into an unconditional one. */
+ SET_SRC (PATTERN (insn))
+ = gen_rtx (LABEL_REF, VOIDmode, JUMP_LABEL (insn));
+ emit_barrier_after (insn);
+ changed = 1;
+ next = insn;
+ }
+ }
+
+ /* Cross jumping of unconditional jumps:
+ a few differences. */
+
+ if (cross_jump && simplejump_p (insn))
+ {
+ rtx newjpos, newlpos;
+ rtx target;
+
+ newjpos = 0;
+
+ /* TARGET is nonzero if it is ok to cross jump
+ to code before TARGET. If so, see if matches. */
+ find_cross_jump (insn, JUMP_LABEL (insn), 1,
+ &newjpos, &newlpos);
+
+ /* If cannot cross jump to code before the label,
+ see if we can cross jump to another jump to
+ the same label. */
+ /* Try each other jump to this label. */
+ if (INSN_UID (JUMP_LABEL (insn)) < max_uid)
+ for (target = jump_chain[INSN_UID (JUMP_LABEL (insn))];
+ target != 0 && newjpos == 0;
+ target = jump_chain[INSN_UID (target)])
+ if (target != insn
+ && JUMP_LABEL (target) == JUMP_LABEL (insn)
+ /* Ignore TARGET if it's deleted. */
+ && ! INSN_DELETED_P (target))
+ find_cross_jump (insn, target, 2,
+ &newjpos, &newlpos);
+
+ if (newjpos != 0)
+ {
+ do_cross_jump (insn, newjpos, newlpos);
+ changed = 1;
+ next = insn;
+ }
+ }
+ }
+ }
+ else if (GET_CODE (insn) == JUMP_INSN
+ && GET_CODE (PATTERN (insn)) == RETURN)
+ {
+ /* Return insns all "jump to the same place"
+ so we can cross-jump between any two of them. */
+ if (cross_jump)
+ {
+ rtx newjpos, newlpos, target;
+
+ newjpos = 0;
+
+ /* If cannot cross jump to code before the label,
+ see if we can cross jump to another jump to
+ the same label. */
+ /* Try each other jump to this label. */
+ for (target = jump_chain[0];
+ target != 0 && newjpos == 0;
+ target = jump_chain[INSN_UID (target)])
+ if (target != insn
+ && ! INSN_DELETED_P (target)
+ && GET_CODE (PATTERN (target)) == RETURN)
+ find_cross_jump (insn, target, 2,
+ &newjpos, &newlpos);
+
+ if (newjpos != 0)
+ {
+ do_cross_jump (insn, newjpos, newlpos);
+ changed = 1;
+ next = insn;
+ }
+ }
+ }
+
+ }
+
+ first = 0;
+ }
+
+ /* See if there is still a NOTE_INSN_FUNCTION_END in this function.
+ If so, delete it, and record that this function can drop off the end. */
+
+ insn = last_insn;
+ {
+ int n_labels = 1;
+ while (insn
+ /* One label can follow the end-note: the return label. */
+ && ((GET_CODE (insn) == CODE_LABEL && n_labels-- > 0)
+ /* Ordinary insns can follow it if returning a structure. */
+ || GET_CODE (insn) == INSN
+ /* If machine uses explicit RETURN insns, no epilogue,
+ then one of them follows the note. */
+ || (GET_CODE (insn) == JUMP_INSN
+ && GET_CODE (PATTERN (insn)) == RETURN)
+ /* Other kinds of notes can follow also. */
+ || (GET_CODE (insn) == NOTE
+ && NOTE_LINE_NUMBER (insn) != NOTE_INSN_FUNCTION_END)))
+ insn = PREV_INSN (insn);
+ }
+ if (insn && GET_CODE (insn) == NOTE
+ && NOTE_LINE_NUMBER (insn) == NOTE_INSN_FUNCTION_END)
+ {
+ extern int current_function_returns_null;
+ current_function_returns_null = 1;
+ delete_insn (insn);
+ }
+}
+\f
+/* Compare the instructions before insn E1 with those before E2.
+ Assume E1 is a jump that jumps to label E2
+ (that is not always true but it might as well be).
+ Find the longest possible equivalent sequences
+ and store the first insns of those sequences into *F1 and *F2.
+ Store zero there if no equivalent preceding instructions are found.
+
+ We give up if we find a label in stream 1.
+ Actually we could transfer that label into stream 2. */
+
+static void
+find_cross_jump (e1, e2, minimum, f1, f2)
+ rtx e1, e2;
+ int minimum;
+ rtx *f1, *f2;
+{
+ register rtx i1 = e1, i2 = e2;
+ register rtx p1, p2;
+
+ rtx last1 = 0, last2 = 0;
+ rtx afterlast1 = 0, afterlast2 = 0;
+
+ *f1 = 0;
+ *f2 = 0;
+
+ while (1)
+ {
+ i1 = PREV_INSN (i1);
+ while (i1 && GET_CODE (i1) == NOTE)
+ i1 = PREV_INSN (i1);
+
+ i2 = PREV_INSN (i2);
+ while (i2 && (GET_CODE (i2) == NOTE || GET_CODE (i2) == CODE_LABEL))
+ i2 = PREV_INSN (i2);
+
+ if (i1 == 0)
+ break;
+
+ /* Don't allow the range of insns preceding E1 or E2
+ to include the other (E2 or E1). */
+ if (i2 == e1 || i1 == e2)
+ break;
+
+ /* If we will get to this code by jumping, those jumps will be
+ tensioned to go directly to the new label (before I2),
+ so this cross-jumping won't cost extra. So reduce the minimum. */
+ if (GET_CODE (i1) == CODE_LABEL)
+ {
+ --minimum;
+ break;
+ }
+
+ if (i2 == 0 || GET_CODE (i1) != GET_CODE (i2))
+ break;
+
+ p1 = PATTERN (i1);
+ p2 = PATTERN (i2);
+
+ if (GET_CODE (p1) != GET_CODE (p2)
+ || !rtx_renumbered_equal_p (p1, p2))
+ {
+ /* Insns fail to match; cross jumping is limited to the following
+ insns. */
+
+ /* Don't allow the insn after a compare to be shared by cross-jumping
+ unless the compare is also shared.
+ Here, if either of these non-matching insns is a compare,
+ exclude the following insn from possible cross-jumping. */
+ if (sets_cc0_p (p1) || sets_cc0_p (p2))
+ last1 = afterlast1, last2 = afterlast2, ++minimum;
+
+ /* If cross-jumping here will feed a jump-around-jump optimization,
+ this jump won't cost extra, so reduce the minimum. */
+ if (GET_CODE (i1) == JUMP_INSN
+ && JUMP_LABEL (i1)
+ && prev_real_insn (JUMP_LABEL (i1)) == e1)
+ --minimum;
+ break;
+ }
+
+ if (GET_CODE (p1) != USE && GET_CODE (p1) != CLOBBER)
+ {
+ /* Ok, this insn is potentially includable in a cross-jump here. */
+ afterlast1 = last1, afterlast2 = last2;
+ last1 = i1, last2 = i2, --minimum;
+ }
+ }
+
+ if (minimum <= 0 && last1 != 0)
+ *f1 = last1, *f2 = last2;
+}
+
+static void
+do_cross_jump (insn, newjpos, newlpos)
+ rtx insn, newjpos, newlpos;
+{
+ register rtx label;
+ /* Find an existing label at this point
+ or make a new one if there is none. */
+ label = PREV_INSN (newlpos);
+ while (label && GET_CODE (label) == NOTE)
+ label = PREV_INSN (label);
+
+ if (label == 0 || GET_CODE (label) != CODE_LABEL)
+ {
+ label = gen_label_rtx ();
+ emit_label_after (label, PREV_INSN (newlpos));
+ LABEL_NUSES (label) = 0;
+ }
+ /* Make the same jump insn jump to the new point. */
+ if (GET_CODE (PATTERN (insn)) == RETURN)
+ {
+ extern rtx gen_jump ();
+ PATTERN (insn) = gen_jump (label);
+ INSN_CODE (insn) = -1;
+ JUMP_LABEL (insn) = label;
+ LABEL_NUSES (label)++;
+ }
+ else
+ redirect_jump (insn, label);
+ /* Delete the matching insns before the jump. */
+ newjpos = PREV_INSN (newjpos);
+ while (NEXT_INSN (newjpos) != insn)
+ /* Don't delete line numbers. */
+ if (GET_CODE (NEXT_INSN (newjpos)) != NOTE)
+ delete_insn (NEXT_INSN (newjpos));
+ else
+ newjpos = NEXT_INSN (newjpos);
+}
+\f
+/* Move all block-beg and block-end notes between START and END
+ out before START. Assume neither START nor END is such a note. */
+
+static void
+squeeze_block_notes (start, end)
+ rtx start, end;
+{
+ rtx insn;
+ rtx next;
+
+ for (insn = start; insn != end; insn = next)
+ {
+ next = NEXT_INSN (insn);
+ if (GET_CODE (insn) == NOTE
+ && (NOTE_LINE_NUMBER (insn) == NOTE_INSN_BLOCK_END
+ || NOTE_LINE_NUMBER (insn) == NOTE_INSN_BLOCK_BEG))
+ {
+ rtx prev = PREV_INSN (insn);
+ PREV_INSN (insn) = PREV_INSN (start);
+ NEXT_INSN (insn) = start;
+ NEXT_INSN (PREV_INSN (insn)) = insn;
+ PREV_INSN (NEXT_INSN (insn)) = insn;
+ NEXT_INSN (prev) = next;
+ PREV_INSN (next) = prev;
+ }
+ }
+}
+
+/* Return 1 if INSN is a jump that jumps to right after TARGET
+ only on the condition that TARGET itself would drop through.
+ Assumes that TARGET is a conditional jump. */
+
+static int
+jump_back_p (insn, target)
+ rtx insn, target;
+{
+ rtx cinsn, ctarget, prev;
+ enum rtx_code codei, codet;
+
+ if (simplejump_p (insn) || ! condjump_p (insn)
+ || simplejump_p (target))
+ return 0;
+ if (target != prev_real_insn (JUMP_LABEL (insn)))
+ return 0;
+
+ /* Verify that the condition code was based on a fixed-point computation.
+ Using reverse_condition is invalid for IEEE floating point with nans. */
+ prev = prev_real_insn (insn);
+ if (! (prev != 0
+ && GET_CODE (prev) == INSN
+ && GET_CODE (PATTERN (prev)) == SET
+ && SET_DEST (PATTERN (prev)) == cc0_rtx
+ && (GET_MODE_CLASS (GET_MODE (SET_SRC (PATTERN (prev)))) == MODE_INT
+ || (GET_CODE (SET_SRC (PATTERN (prev))) == COMPARE
+ && (GET_MODE_CLASS (GET_MODE (XEXP (SET_SRC (PATTERN (prev)), 0)))
+ == MODE_INT)))))
+ return 0;
+
+ cinsn = XEXP (SET_SRC (PATTERN (insn)), 0);
+ ctarget = XEXP (SET_SRC (PATTERN (target)), 0);
+
+ codei = GET_CODE (cinsn);
+ codet = GET_CODE (ctarget);
+ if (XEXP (SET_SRC (PATTERN (insn)), 1) == pc_rtx)
+
+ codei = reverse_condition (codei);
+ if (XEXP (SET_SRC (PATTERN (target)), 2) == pc_rtx)
+ codet = reverse_condition (codet);
+ return (codei == codet
+ && rtx_renumbered_equal_p (XEXP (cinsn, 0), XEXP (ctarget, 0))
+ && rtx_renumbered_equal_p (XEXP (cinsn, 1), XEXP (ctarget, 1)));
+}
+
+/* Given an rtx-code for a comparison, return the code
+ for the negated comparison.
+ WATCH OUT! reverse_condition is not safe to use on a jump
+ that might be acting on the results of an IEEE floating point comparison,
+ because of the special treatment of non-signaling nans in comparisons. */
+
+static enum rtx_code
+reverse_condition (code)
+ enum rtx_code code;
+{
+ switch (code)
+ {
+ case EQ:
+ return NE;
+
+ case NE:
+ return EQ;
+
+ case GT:
+ return LE;
+
+ case GE:
+ return LT;
+
+ case LT:
+ return GE;
+
+ case LE:
+ return GT;
+
+ case GTU:
+ return LEU;
+
+ case GEU:
+ return LTU;
+
+ case LTU:
+ return GEU;
+
+ case LEU:
+ return GTU;
+
+ default:
+ abort ();
+ return UNKNOWN;
+ }
+}
+\f
+/* Return 1 if INSN is an unconditional jump and nothing else. */
+
+int
+simplejump_p (insn)
+ rtx insn;
+{
+ register rtx x = PATTERN (insn);
+ if (GET_CODE (x) != SET)
+ return 0;
+ if (GET_CODE (SET_DEST (x)) != PC)
+ return 0;
+ if (GET_CODE (SET_SRC (x)) != LABEL_REF)
+ return 0;
+ return 1;
+}
+
+/* Return nonzero if INSN is a (possibly) conditional jump
+ and nothing more. */
+
+int
+condjump_p (insn)
+ rtx insn;
+{
+ register rtx x = PATTERN (insn);
+ if (GET_CODE (x) != SET)
+ return 0;
+ if (GET_CODE (SET_DEST (x)) != PC)
+ return 0;
+ if (GET_CODE (SET_SRC (x)) == LABEL_REF)
+ return 1;
+ if (GET_CODE (SET_SRC (x)) != IF_THEN_ELSE)
+ return 0;
+ if (XEXP (SET_SRC (x), 2) == pc_rtx
+ && GET_CODE (XEXP (SET_SRC (x), 1)) == LABEL_REF)
+ return 1;
+ if (XEXP (SET_SRC (x), 1) == pc_rtx
+ && GET_CODE (XEXP (SET_SRC (x), 2)) == LABEL_REF)
+ return 1;
+ return 0;
+}
+
+/* Return 1 if X is an RTX that does nothing but set the condition codes
+ and CLOBBER or USE registers.
+ Return -1 if X does explicitly set the condition codes,
+ but also does other things. */
+
+int
+sets_cc0_p (x)
+ rtx x;
+{
+ if (GET_CODE (x) == SET && SET_DEST (x) == cc0_rtx)
+ return 1;
+ if (GET_CODE (x) == PARALLEL)
+ {
+ int i;
+ int sets_cc0 = 0;
+ int other_things = 0;
+ for (i = XVECLEN (x, 0) - 1; i >= 0; i--)
+ {
+ if (GET_CODE (XVECEXP (x, 0, i)) == SET
+ && SET_DEST (XVECEXP (x, 0, i)) == cc0_rtx)
+ sets_cc0 = 1;
+ else if (GET_CODE (XVECEXP (x, 0, i)) == SET)
+ other_things = 1;
+ }
+ return ! sets_cc0 ? 0 : other_things ? -1 : 1;
+ }
+ return 0;
+}
+
+/* Return 1 if in between BEG and END there is no CODE_LABEL insn. */
+
+int
+no_labels_between_p (beg, end)
+ rtx beg, end;
+{
+ register rtx p;
+ for (p = beg; p != end; p = NEXT_INSN (p))
+ if (GET_CODE (p) == CODE_LABEL)
+ return 0;
+ return 1;
+}
+\f
+/* Return the last INSN, CALL_INSN or JUMP_INSN before LABEL;
+ or 0, if there is none. */
+
+rtx
+prev_real_insn (label)
+ rtx label;
+{
+ register rtx insn = PREV_INSN (label);
+ register RTX_CODE code;
+
+ while (1)
+ {
+ if (insn == 0)
+ return 0;
+ code = GET_CODE (insn);
+ if (code == INSN || code == CALL_INSN || code == JUMP_INSN)
+ break;
+ insn = PREV_INSN (insn);
+ }
+
+ return insn;
+}
+
+/* Return the next INSN, CALL_INSN or JUMP_INSN after LABEL;
+ or 0, if there is none. */
+
+rtx
+next_real_insn (label)
+ rtx label;
+{
+ register rtx insn = NEXT_INSN (label);
+ register RTX_CODE code;
+
+ while (1)
+ {
+ if (insn == 0)
+ return insn;
+ code = GET_CODE (insn);
+ if (code == INSN || code == CALL_INSN || code == JUMP_INSN)
+ break;
+ insn = NEXT_INSN (insn);
+ }
+
+ return insn;
+}
+
+/* Return the next CODE_LABEL after the insn INSN, or 0 if there is none. */
+
+rtx
+next_label (insn)
+ rtx insn;
+{
+ do insn = NEXT_INSN (insn);
+ while (insn != 0 && GET_CODE (insn) != CODE_LABEL);
+ return insn;
+}
+\f
+/* Follow any unconditional jump at LABEL;
+ return the ultimate label reached by any such chain of jumps.
+ If LABEL is not followed by a jump, return LABEL.
+ If IGNORE_LOOPS is 0, we do not chain across a NOTE_INSN_LOOP_BEG. */
+
+static rtx
+follow_jumps (label, ignore_loops)
+ rtx label;
+ int ignore_loops;
+{
+ register rtx insn;
+ register rtx next;
+ register rtx value = label;
+ register int depth;
+
+ for (depth = 0;
+ (depth < 10
+ && (insn = next_real_insn (value)) != 0
+ && GET_CODE (insn) == JUMP_INSN
+ && JUMP_LABEL (insn) != 0
+ && (next = NEXT_INSN (insn))
+ && GET_CODE (next) == BARRIER);
+ depth++)
+ {
+ /* Don't chain through the insn that jumps into a loop
+ from outside the loop,
+ since that would create multiple loop entry jumps
+ and prevent loop optimization. */
+ rtx tem;
+ if (!ignore_loops)
+ for (tem = value; tem != insn; tem = NEXT_INSN (tem))
+ if (GET_CODE (tem) == NOTE
+ && NOTE_LINE_NUMBER (tem) == NOTE_INSN_LOOP_BEG)
+ return value;
+
+ /* If we have found a cycle, make the insn jump to itself. */
+ if (JUMP_LABEL (insn) == label)
+ break;
+ value = JUMP_LABEL (insn);
+ }
+ return value;
+}
+
+/* Assuming that field IDX of X is a vector of label_refs,
+ replace each of them by the ultimate label reached by it.
+ Return nonzero if a change is made.
+ If IGNORE_LOOPS is 0, we do not chain across a NOTE_INSN_LOOP_BEG. */
+
+static int
+tension_vector_labels (x, idx, ignore_loops)
+ register rtx x;
+ register int idx;
+ int ignore_loops;
+{
+ int changed = 0;
+ register int i;
+ for (i = XVECLEN (x, idx) - 1; i >= 0; i--)
+ {
+ register rtx olabel = XEXP (XVECEXP (x, idx, i), 0);
+ register rtx nlabel = follow_jumps (olabel, ignore_loops);
+ if (nlabel != olabel)
+ {
+ XEXP (XVECEXP (x, idx, i), 0) = nlabel;
+ ++LABEL_NUSES (nlabel);
+ if (--LABEL_NUSES (olabel) == 0)
+ delete_insn (olabel);
+ changed = 1;
+ }
+ }
+ return changed;
+}
+\f
+/* Find all CODE_LABELs referred to in X,
+ and increment their use counts.
+ Also store one of them in JUMP_LABEL (INSN) if INSN is nonzero.
+ Also, when there are consecutive labels,
+ canonicalize on the last of them.
+
+ Note that two labels separated by a loop-beginning note
+ must be kept distinct if we have not yet done loop-optimization,
+ because the gap between them is where loop-optimize
+ will want to move invariant code to. CROSS_JUMP tells us
+ that loop-optimization is done with. */
+
+static void
+mark_jump_label (x, insn, cross_jump)
+ register rtx x;
+ rtx insn;
+ int cross_jump;
+{
+ register RTX_CODE code = GET_CODE (x);
+ register int i;
+ register char *fmt;
+
+ if (code == LABEL_REF)
+ {
+ register rtx label = XEXP (x, 0);
+ register rtx next;
+ if (GET_CODE (label) != CODE_LABEL)
+ return;
+ /* If there are other labels following this one,
+ replace it with the last of the consecutive labels. */
+ for (next = NEXT_INSN (label); next; next = NEXT_INSN (next))
+ {
+ if (GET_CODE (next) == CODE_LABEL)
+ label = next;
+ else if (GET_CODE (next) != NOTE
+ || NOTE_LINE_NUMBER (next) == NOTE_INSN_LOOP_BEG
+ || NOTE_LINE_NUMBER (next) == NOTE_INSN_FUNCTION_END)
+ break;
+ }
+ XEXP (x, 0) = label;
+ ++LABEL_NUSES (label);
+ if (insn)
+ JUMP_LABEL (insn) = label;
+ return;
+ }
+
+ /* Do walk the labels in a vector,
+ but don't set its JUMP_LABEL. */
+ if (code == ADDR_VEC || code == ADDR_DIFF_VEC)
+ insn = 0;
+
+ fmt = GET_RTX_FORMAT (code);
+ for (i = GET_RTX_LENGTH (code); i >= 0; i--)
+ {
+ if (fmt[i] == 'e')
+ mark_jump_label (XEXP (x, i), insn, cross_jump);
+ else if (fmt[i] == 'E')
+ {
+ register int j;
+ for (j = 0; j < XVECLEN (x, i); j++)
+ mark_jump_label (XVECEXP (x, i, j), insn, cross_jump);
+ }
+ }
+}
+\f
+/* If all INSN does is set the pc, delete it,
+ and delete the insn that set the condition codes for it
+ if that's what the previous thing was. */
+
+static void
+delete_jump (insn)
+ rtx insn;
+{
+ register rtx x = PATTERN (insn);
+ register rtx prev;
+
+ if (GET_CODE (x) == SET
+ && GET_CODE (SET_DEST (x)) == PC)
+ {
+ prev = PREV_INSN (insn);
+ delete_insn (insn);
+ /* We assume that at this stage
+ CC's are always set explicitly
+ and always immediately before the jump that
+ will use them. So if the previous insn
+ exists to set the CC's, delete it
+ (unless it performs auto-increments, etc.). */
+ while (prev && GET_CODE (prev) == NOTE)
+ prev = PREV_INSN (prev);
+ if (prev && GET_CODE (prev) == INSN
+ && sets_cc0_p (PATTERN (prev)) > 0
+ && !find_reg_note (prev, REG_INC, 0))
+ delete_insn (prev);
+ }
+}
+\f
+/* Delete insn INSN from the chain of insns and update label ref counts.
+ May delete some following insns as a consequence; may even delete
+ a label elsewhere and insns that follow it.
+
+ Returns the first insn after INSN that was not deleted. */
+
+rtx
+delete_insn (insn)
+ register rtx insn;
+{
+ register rtx next = NEXT_INSN (insn);
+ register rtx prev = PREV_INSN (insn);
+
+ while (next && INSN_DELETED_P (next))
+ next = NEXT_INSN (next);
+
+ /* This insn is already deleted => return first following nondeleted. */
+ if (INSN_DELETED_P (insn))
+ return next;
+
+ /* Mark this insn as deleted. */
+
+ INSN_DELETED_P (insn) = 1;
+
+ /* If instruction is followed by a barrier,
+ delete the barrier too. */
+
+ if (next != 0 && GET_CODE (next) == BARRIER)
+ {
+ INSN_DELETED_P (next) = 1;
+ next = NEXT_INSN (next);
+ }
+
+ /* Patch out INSN (and the barrier if any) */
+
+ if (optimize)
+ {
+ if (prev)
+ NEXT_INSN (prev) = next;
+
+ if (next)
+ PREV_INSN (next)= prev;
+
+ if (prev && NEXT_INSN (prev) == 0)
+ set_last_insn (prev);
+ }
+
+ /* If deleting a jump, decrement the count of the label,
+ and delete the label if it is now unused. */
+
+ if (GET_CODE (insn) == JUMP_INSN && JUMP_LABEL (insn))
+ if (--LABEL_NUSES (JUMP_LABEL (insn)) == 0)
+ {
+ /* This can delete NEXT or PREV,
+ either directly if NEXT is JUMP_LABEL (INSN),
+ or indirectly through more levels of jumps. */
+ delete_insn (JUMP_LABEL (insn));
+ /* I feel a little doubtful about this loop,
+ but I see no clean and sure alternative way
+ to find the first insn after INSN that is not now deleted.
+ I hope this works. */
+ while (next && INSN_DELETED_P (next))
+ next = NEXT_INSN (next);
+ return next;
+ }
+
+ while (prev && (INSN_DELETED_P (prev) || GET_CODE (prev) == NOTE))
+ prev = PREV_INSN (prev);
+
+ /* If INSN was a label and a dispatch table follows it,
+ delete the dispatch table. The tablejump must have gone already.
+ It isn't useful to fall through into a table. */
+
+ if (GET_CODE (insn) == CODE_LABEL
+ && NEXT_INSN (insn) != 0
+ && GET_CODE (NEXT_INSN (insn)) == JUMP_INSN
+ && GET_CODE (PATTERN (NEXT_INSN (insn))) == ADDR_VEC)
+ next = delete_insn (NEXT_INSN (insn));
+
+ /* If INSN was a label, delete insns following it if now unreachable. */
+
+ if (GET_CODE (insn) == CODE_LABEL && prev
+ && GET_CODE (prev) == BARRIER)
+ {
+ register RTX_CODE code;
+ while (next != 0
+ && ((code = GET_CODE (next)) == INSN
+ || code == JUMP_INSN || code == CALL_INSN
+ || code == NOTE))
+ {
+ if (code == NOTE
+ && NOTE_LINE_NUMBER (next) != NOTE_INSN_FUNCTION_END)
+ next = NEXT_INSN (next);
+ else
+ /* Note: if this deletes a jump, it can cause more
+ deletion of unreachable code, after a different label.
+ As long as the value from this recursive call is correct,
+ this invocation functions correctly. */
+ next = delete_insn (next);
+ }
+ }
+
+ return next;
+}
+
+/* Advance from INSN till reaching something not deleted
+ then return that. May return INSN itself. */
+
+rtx
+next_nondeleted_insn (insn)
+ rtx insn;
+{
+ while (INSN_DELETED_P (insn))
+ insn = NEXT_INSN (insn);
+ return insn;
+}
+\f
+/* Delete a range of insns from FROM to TO, inclusive.
+ This is for the sake of peephole optimization, so assume
+ that whatever these insns do will still be done by a new
+ peephole insn that will replace them. */
+
+void
+delete_for_peephole (from, to)
+ register rtx from, to;
+{
+ register rtx insn = from;
+
+ while (1)
+ {
+ register rtx next = NEXT_INSN (insn);
+ register rtx prev = PREV_INSN (insn);
+
+ if (GET_CODE (insn) != NOTE)
+ {
+ INSN_DELETED_P (insn) = 1;
+
+ /* Patch this insn out of the chain. */
+ /* We don't do this all at once, because we
+ must preserve all NOTEs. */
+ if (prev)
+ NEXT_INSN (prev) = next;
+
+ if (next)
+ PREV_INSN (next) = prev;
+ }
+
+ if (insn == to)
+ break;
+ insn = next;
+ }
+
+ /* Note that if TO is an unconditional jump
+ we *do not* delete the BARRIER that follows,
+ since the peephole that replaces this sequence
+ is also an unconditional jump in that case. */
+}
+\f
+/* Invert the condition of the jump JUMP, and make it jump
+ to label NLABEL instead of where it jumps now. */
+
+void
+invert_jump (jump, nlabel)
+ rtx jump, nlabel;
+{
+ register rtx olabel = JUMP_LABEL (jump);
+ invert_exp (PATTERN (jump), olabel, nlabel);
+ JUMP_LABEL (jump) = nlabel;
+ ++LABEL_NUSES (nlabel);
+ INSN_CODE (jump) = -1;
+
+ if (--LABEL_NUSES (olabel) == 0)
+ delete_insn (olabel);
+}
+
+/* Invert the jump condition of rtx X,
+ and replace OLABEL with NLABEL throughout.
+ This is used in do_jump as well as in this file. */
+
+void
+invert_exp (x, olabel, nlabel)
+ rtx x;
+ rtx olabel, nlabel;
+{
+ register RTX_CODE code;
+ register int i;
+ register char *fmt;
+
+ if (x == 0)
+ return;
+
+ code = GET_CODE (x);
+ if (code == IF_THEN_ELSE)
+ {
+ /* Inverting the jump condition of an IF_THEN_ELSE
+ means exchanging the THEN-part with the ELSE-part. */
+ register rtx tem = XEXP (x, 1);
+ XEXP (x, 1) = XEXP (x, 2);
+ XEXP (x, 2) = tem;
+ }
+
+ if (code == LABEL_REF)
+ {
+ if (XEXP (x, 0) == olabel)
+ XEXP (x, 0) = nlabel;
+ return;
+ }
+
+ fmt = GET_RTX_FORMAT (code);
+ for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+ {
+ if (fmt[i] == 'e')
+ invert_exp (XEXP (x, i), olabel, nlabel);
+ if (fmt[i] == 'E')
+ {
+ register int j;
+ for (j = 0; j < XVECLEN (x, i); j++)
+ invert_exp (XVECEXP (x, i, j), olabel, nlabel);
+ }
+ }
+}
+\f
+/* Make jump JUMP jump to label NLABEL instead of where it jumps now.
+ If the old jump target label is unused as a result,
+ it and the code following it may be deleted. */
+
+void
+redirect_jump (jump, nlabel)
+ rtx jump, nlabel;
+{
+ register rtx olabel = JUMP_LABEL (jump);
+
+ if (nlabel == olabel)
+ return;
+
+ redirect_exp (PATTERN (jump), olabel, nlabel);
+ JUMP_LABEL (jump) = nlabel;
+ ++LABEL_NUSES (nlabel);
+ INSN_CODE (jump) = -1;
+
+ if (--LABEL_NUSES (olabel) == 0)
+ delete_insn (olabel);
+}
+
+/* Throughout the rtx X,
+ alter (LABEL_REF OLABEL) to (LABEL_REF NLABEL). */
+
+static void
+redirect_exp (x, olabel, nlabel)
+ rtx x;
+ rtx olabel, nlabel;
+{
+ register RTX_CODE code = GET_CODE (x);
+ register int i;
+ register char *fmt;
+
+ if (code == LABEL_REF)
+ {
+ if (XEXP (x, 0) == olabel)
+ XEXP (x, 0) = nlabel;
+ return;
+ }
+
+ fmt = GET_RTX_FORMAT (code);
+ for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+ {
+ if (fmt[i] == 'e')
+ redirect_exp (XEXP (x, i), olabel, nlabel);
+ if (fmt[i] == 'E')
+ {
+ register int j;
+ for (j = 0; j < XVECLEN (x, i); j++)
+ redirect_exp (XVECEXP (x, i, j), olabel, nlabel);
+ }
+ }
+}
+\f
+/* Like rtx_equal_p except that it considers two REGs as equal
+ if they renumber to the same value. */
+
+int
+rtx_renumbered_equal_p (x, y)
+ rtx x, y;
+{
+ register int i;
+ register RTX_CODE code = GET_CODE (x);
+ register char *fmt;
+
+ if (x == y)
+ return 1;
+ if ((code == REG || (code == SUBREG && GET_CODE (SUBREG_REG (x)) == REG))
+ && (GET_CODE (y) == REG || (GET_CODE (y) == SUBREG
+ && GET_CODE (SUBREG_REG (y)) == REG)))
+ {
+ register int j;
+
+ if (GET_MODE (x) != GET_MODE (y))
+ return 0;
+
+ if (code == SUBREG)
+ {
+ i = REGNO (SUBREG_REG (x));
+ if (reg_renumber[i] >= 0)
+ i = reg_renumber[i];
+ i += SUBREG_WORD (x);
+ }
+ else
+ {
+ i = REGNO (x);
+ if (reg_renumber[i] >= 0)
+ i = reg_renumber[i];
+ }
+ if (GET_CODE (y) == SUBREG)
+ {
+ j = REGNO (SUBREG_REG (y));
+ if (reg_renumber[j] >= 0)
+ j = reg_renumber[j];
+ j += SUBREG_WORD (y);
+ }
+ else
+ {
+ j = REGNO (y);
+ if (reg_renumber[j] >= 0)
+ j = reg_renumber[j];
+ }
+ return i == j;
+ }
+ /* Now we have disposed of all the cases
+ in which different rtx codes can match. */
+ if (code != GET_CODE (y))
+ return 0;
+ switch (code)
+ {
+ case PC:
+ case CC0:
+ case ADDR_VEC:
+ case ADDR_DIFF_VEC:
+ return 0;
+
+ case CONST_INT:
+ return XINT (x, 0) == XINT (y, 0);
+
+ case LABEL_REF:
+ /* Two label-refs are equivalent if they point at labels
+ in the same position in the instruction stream. */
+ return (next_real_insn (XEXP (x, 0))
+ == next_real_insn (XEXP (y, 0)));
+
+ case SYMBOL_REF:
+ return XSTR (x, 0) == XSTR (y, 0);
+ }
+
+ /* (MULT:SI x y) and (MULT:HI x y) are NOT equivalent. */
+
+ if (GET_MODE (x) != GET_MODE (y))
+ return 0;
+
+ /* Compare the elements. If any pair of corresponding elements
+ fail to match, return 0 for the whole things. */
+
+ fmt = GET_RTX_FORMAT (code);
+ for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+ {
+ register int j;
+ switch (fmt[i])
+ {
+ case 'i':
+ if (XINT (x, i) != XINT (y, i))
+ return 0;
+ break;
+
+ case 's':
+ if (strcmp (XSTR (x, i), XSTR (y, i)))
+ return 0;
+ break;
+
+ case 'e':
+ if (! rtx_renumbered_equal_p (XEXP (x, i), XEXP (y, i)))
+ return 0;
+ break;
+
+ case '0':
+ break;
+
+ case 'E':
+ if (XVECLEN (x, i) != XVECLEN (y, i))
+ return 0;
+ for (j = XVECLEN (x, i) - 1; j >= 0; j--)
+ if (!rtx_renumbered_equal_p (XVECEXP (x, i, j), XVECEXP (y, i, j)))
+ return 0;
+ break;
+
+ default:
+ abort ();
+ }
+ }
+ return 1;
+}
+\f
+/* If X is a hard register or equivalent to one or a subregister of one,
+ return the hard register number. Otherwise, return -1.
+ Any rtx is valid for X. */
+
+int
+true_regnum (x)
+ rtx x;
+{
+ if (GET_CODE (x) == REG)
+ {
+ if (REGNO (x) >= FIRST_PSEUDO_REGISTER)
+ return reg_renumber[REGNO (x)];
+ return REGNO (x);
+ }
+ if (GET_CODE (x) == SUBREG)
+ {
+ int base = true_regnum (SUBREG_REG (x));
+ if (base >= 0 && base < FIRST_PSEUDO_REGISTER)
+ return SUBREG_WORD (x) + base;
+ }
+ return -1;
+}