/* Optimize jump instructions, for GNU compiler.
- Copyright (C) 1987, 88, 89, 91, 92, 93, 1994 Free Software Foundation, Inc.
+ Copyright (C) 1987, 88, 89, 91-95, 1996 Free Software Foundation, Inc.
This file is part of GNU CC.
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. */
+the Free Software Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA. */
/* This is the jump-optimization pass of the compiler.
#include "flags.h"
#include "hard-reg-set.h"
#include "regs.h"
-#include "expr.h"
#include "insn-config.h"
#include "insn-flags.h"
+#include "expr.h"
#include "real.h"
+#include "except.h"
/* ??? Eventually must record somehow the labels used by jumps
from nested functions. */
Normally they are not significant, because of A and B jump to C,
and R dies in A, it must die in B. But this might not be true after
stack register conversion, and we must compare death notes in that
- case. */
+ case. */
static int cross_jump_death_matters = 0;
we make. */
max_jump_chain = max_uid * 14 / 10;
jump_chain = (rtx *) alloca (max_jump_chain * sizeof (rtx));
- bzero (jump_chain, max_jump_chain * sizeof (rtx));
+ bzero ((char *) jump_chain, max_jump_chain * sizeof (rtx));
/* Mark the label each jump jumps to.
Combine consecutive labels, and count uses of labels.
for (insn = forced_labels; insn; insn = XEXP (insn, 1))
LABEL_NUSES (XEXP (insn, 0))++;
+ check_exception_handler_labels ();
+
+ /* Keep track of labels used for marking handlers for exception
+ regions; they cannot usually be deleted. */
+
+ for (insn = exception_handler_labels; insn; insn = XEXP (insn, 1))
+ LABEL_NUSES (XEXP (insn, 0))++;
+
+ exception_optimize ();
+
/* Delete all labels already not referenced.
Also find the last insn. */
then one of them follows the note. */
|| (GET_CODE (insn) == JUMP_INSN
&& GET_CODE (PATTERN (insn)) == RETURN)
+ /* A barrier can follow the return insn. */
+ || GET_CODE (insn) == BARRIER
/* Other kinds of notes can follow also. */
|| (GET_CODE (insn) == NOTE
&& NOTE_LINE_NUMBER (insn) != NOTE_INSN_FUNCTION_END)))
sreg, NULL_PTR, 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)))
{
/* DREG may have been the target of a REG_DEAD note in
the insn which makes INSN redundant. If so, reorg
would still think it is dead. So search for such a
note and delete it if we find it. */
- for (trial = prev_nonnote_insn (insn);
- trial && GET_CODE (trial) != CODE_LABEL;
- trial = prev_nonnote_insn (trial))
- if (find_regno_note (trial, REG_DEAD, dreg))
- {
- remove_death (dreg, trial);
- break;
- }
-
- if (tem != 0
- && GET_MODE (tem) == GET_MODE (SET_DEST (body)))
+ if (! find_regno_note (insn, REG_UNUSED, dreg))
+ for (trial = prev_nonnote_insn (insn);
+ trial && GET_CODE (trial) != CODE_LABEL;
+ trial = prev_nonnote_insn (trial))
+ if (find_regno_note (trial, REG_DEAD, dreg))
+ {
+ remove_death (dreg, trial);
+ break;
+ }
+#ifdef PRESERVE_DEATH_INFO_REGNO_P
+ /* Deleting insn could lose a death-note for SREG
+ so don't do it if final needs accurate
+ death-notes. */
+ if (PRESERVE_DEATH_INFO_REGNO_P (sreg)
+ && (trial = find_regno_note (insn, REG_DEAD, sreg)))
+ {
+ /* Change this into a USE so that we won't emit
+ code for it, but still can keep the note. */
+ PATTERN (insn)
+ = gen_rtx (USE, VOIDmode, XEXP (trial, 0));
+ /* Remove all reg notes but the REG_DEAD one. */
+ REG_NOTES (insn) = trial;
+ XEXP (trial, 1) = NULL_RTX;
+ }
+ else
+#endif
delete_insn (insn);
}
}
else if (GET_CODE (body) == PARALLEL)
{
/* If each part is a set between two identical registers or
- a USE or CLOBBER, delete the insn. */
+ a USE or CLOBBER, delete the insn. */
int i, sreg, dreg;
rtx tem;
if (i < 0)
delete_insn (insn);
}
-#if !BYTES_BIG_ENDIAN /* Not worth the hair to detect this
- in the big-endian case. */
/* Also delete insns to store bit fields if they are no-ops. */
- else if (GET_CODE (body) == SET
+ /* Not worth the hair to detect this in the big-endian case. */
+ else if (! BYTES_BIG_ENDIAN
+ && GET_CODE (body) == SET
&& GET_CODE (SET_DEST (body)) == ZERO_EXTRACT
&& XEXP (SET_DEST (body), 2) == const0_rtx
&& XEXP (SET_DEST (body), 0) == SET_SRC (body)
&& ! (GET_CODE (SET_SRC (body)) == MEM
&& MEM_VOLATILE_P (SET_SRC (body))))
delete_insn (insn);
-#endif /* not BYTES_BIG_ENDIAN */
}
insn = next;
}
of a reg that's used in notes. A subsequent optimization
might arrange to use that reg for real. */
&& regno_last_note_uid[REGNO (SET_DEST (set))] == INSN_UID (insn)
- && ! side_effects_p (SET_SRC (set)))
+ && ! side_effects_p (SET_SRC (set))
+ && ! find_reg_note (insn, REG_RETVAL, 0))
delete_insn (insn);
}
rtx temp, temp1, temp2, temp3, temp4, temp5, temp6;
rtx nlabel;
int this_is_simplejump, this_is_condjump, reversep;
+ int this_is_condjump_in_parallel;
#if 0
/* If NOT the first iteration, if this is the last jump pass
(just before final), do the special peephole optimizations.
this_is_simplejump = simplejump_p (insn);
this_is_condjump = condjump_p (insn);
+ this_is_condjump_in_parallel = condjump_in_parallel_p (insn);
/* Tension the labels in dispatch tables. */
/* Detect jump to following insn. */
if (reallabelprev == insn && condjump_p (insn))
{
+ next = next_real_insn (JUMP_LABEL (insn));
delete_jump (insn);
changed = 1;
continue;
&& (temp1 = prev_nonnote_insn (JUMP_LABEL (insn))) != 0
&& (GET_CODE (temp1) == BARRIER
|| (GET_CODE (temp1) == INSN
- && rtx_equal_p (PATTERN (temp), PATTERN (temp1)))))
+ && rtx_equal_p (PATTERN (temp), PATTERN (temp1))))
+ /* Don't do this optimization if we have a loop containing only
+ the USE instruction, and the loop start label has a usage
+ count of 1. This is because we will redo this optimization
+ everytime through the outer loop, and jump opt will never
+ exit. */
+ && ! ((temp2 = prev_nonnote_insn (temp)) != 0
+ && temp2 == JUMP_LABEL (insn)
+ && LABEL_NUSES (temp2) == 1))
{
if (GET_CODE (temp1) == BARRIER)
{
}
}
+ /* Simplify if (...) { x = a; goto l; } x = b; by converting it
+ to x = a; if (...) goto l; x = b;
+ if A is sufficiently simple, the test doesn't involve X,
+ and nothing in the test modifies A or X.
+
+ If we have small register classes, we also can't do this if X
+ is a hard register.
+
+ If the "x = a;" insn has any REG_NOTES, we don't do this because
+ of the possibility that we are running after CSE and there is a
+ REG_EQUAL note that is only valid if the branch has already been
+ taken. If we move the insn with the REG_EQUAL note, we may
+ fold the comparison to always be false in a later CSE pass.
+ (We could also delete the REG_NOTES when moving the insn, but it
+ seems simpler to not move it.) An exception is that we can move
+ the insn if the only note is a REG_EQUAL or REG_EQUIV whose
+ value is the same as "a".
+
+ INSN is the goto.
+
+ We set:
+
+ TEMP to the jump insn preceding "x = a;"
+ TEMP1 to X
+ TEMP2 to the insn that sets "x = b;"
+ TEMP3 to the insn that sets "x = a;"
+ TEMP4 to the set of "x = a"; */
+
+ if (this_is_simplejump
+ && (temp2 = next_active_insn (insn)) != 0
+ && GET_CODE (temp2) == INSN
+ && (temp4 = single_set (temp2)) != 0
+ && GET_CODE (temp1 = SET_DEST (temp4)) == REG
+#ifdef SMALL_REGISTER_CLASSES
+ && REGNO (temp1) >= FIRST_PSEUDO_REGISTER
+#endif
+
+ && (temp3 = prev_active_insn (insn)) != 0
+ && GET_CODE (temp3) == INSN
+ && (temp4 = single_set (temp3)) != 0
+ && rtx_equal_p (SET_DEST (temp4), temp1)
+ && (GET_CODE (SET_SRC (temp4)) == REG
+ || GET_CODE (SET_SRC (temp4)) == SUBREG
+ || CONSTANT_P (SET_SRC (temp4)))
+ && (REG_NOTES (temp3) == 0
+ || ((REG_NOTE_KIND (REG_NOTES (temp3)) == REG_EQUAL
+ || REG_NOTE_KIND (REG_NOTES (temp3)) == REG_EQUIV)
+ && XEXP (REG_NOTES (temp3), 1) == 0
+ && rtx_equal_p (XEXP (REG_NOTES (temp3), 0),
+ SET_SRC (temp4))))
+ && (temp = prev_active_insn (temp3)) != 0
+ && condjump_p (temp) && ! simplejump_p (temp)
+ /* TEMP must skip over the "x = a;" insn */
+ && prev_real_insn (JUMP_LABEL (temp)) == insn
+ && no_labels_between_p (temp, insn))
+ {
+ rtx prev_label = JUMP_LABEL (temp);
+ rtx insert_after = prev_nonnote_insn (temp);
+
+#ifdef HAVE_cc0
+ /* We cannot insert anything between a set of cc and its use. */
+ if (insert_after && GET_RTX_CLASS (GET_CODE (insert_after)) == 'i'
+ && sets_cc0_p (PATTERN (insert_after)))
+ insert_after = prev_nonnote_insn (insert_after);
+#endif
+ ++LABEL_NUSES (prev_label);
+
+ if (insert_after
+ && no_labels_between_p (insert_after, temp)
+ && ! reg_referenced_between_p (temp1, insert_after, temp3)
+ && ! reg_referenced_between_p (temp1, temp3,
+ NEXT_INSN (temp2))
+ && ! reg_set_between_p (temp1, insert_after, temp)
+ && (GET_CODE (SET_SRC (temp4)) == CONST_INT
+ || ! reg_set_between_p (SET_SRC (temp4),
+ insert_after, temp))
+ && invert_jump (temp, JUMP_LABEL (insn)))
+ {
+ emit_insn_after_with_line_notes (PATTERN (temp3),
+ insert_after, temp3);
+ delete_insn (temp3);
+ delete_insn (insn);
+ /* Set NEXT to an insn that we know won't go away. */
+ next = temp2;
+ changed = 1;
+ }
+ if (prev_label && --LABEL_NUSES (prev_label) == 0)
+ delete_insn (prev_label);
+ if (changed)
+ continue;
+ }
+
#ifndef HAVE_cc0
/* If we have if (...) x = exp; and branches are expensive,
EXP is a single insn, does not have any side effects, cannot
&& GET_CODE (SET_SRC (temp1)) != CONST_INT
&& ! side_effects_p (SET_SRC (temp1))
&& ! may_trap_p (SET_SRC (temp1))
- && rtx_cost (SET_SRC (temp1)) < 10)
+ && rtx_cost (SET_SRC (temp1), SET) < 10)
{
rtx new = gen_reg_rtx (GET_MODE (temp2));
#endif
&& ! side_effects_p (SET_SRC (temp1))
&& ! may_trap_p (SET_SRC (temp1))
- && rtx_cost (SET_SRC (temp1)) < 10
+ && rtx_cost (SET_SRC (temp1), SET) < 10
&& (temp4 = single_set (temp3)) != 0
&& rtx_equal_p (SET_DEST (temp4), temp2)
&& ! side_effects_p (SET_SRC (temp4))
&& ! may_trap_p (SET_SRC (temp4))
- && rtx_cost (SET_SRC (temp4)) < 10)
+ && rtx_cost (SET_SRC (temp4), SET) < 10)
{
rtx new = gen_reg_rtx (GET_MODE (temp2));
/* Finally, handle the case where two insns are used to
compute EXP but a temporary register is used. Here we must
- ensure that the temporary register is not used anywhere else. */
+ ensure that the temporary register is not used anywhere else. */
if (! reload_completed
&& after_regscan
&& regno_last_uid[REGNO (temp5)] == INSN_UID (temp3)
&& ! side_effects_p (SET_SRC (temp1))
&& ! may_trap_p (SET_SRC (temp1))
- && rtx_cost (SET_SRC (temp1)) < 10
+ && rtx_cost (SET_SRC (temp1), SET) < 10
&& (temp4 = single_set (temp3)) != 0
&& (temp2 = SET_DEST (temp4), GET_CODE (temp2) == REG)
&& GET_MODE_CLASS (GET_MODE (temp2)) == MODE_INT
&& rtx_equal_p (SET_DEST (temp4), temp2)
&& ! side_effects_p (SET_SRC (temp4))
&& ! may_trap_p (SET_SRC (temp4))
- && rtx_cost (SET_SRC (temp4)) < 10)
+ && rtx_cost (SET_SRC (temp4), SET) < 10)
{
rtx new = gen_reg_rtx (GET_MODE (temp2));
}
#endif /* HAVE_cc0 */
- /* We deal with four cases:
+ /* Try to use a conditional move (if the target has them), or a
+ store-flag insn. The general case is:
- 1) x = a; if (...) x = b; and either A or B is zero,
- 2) if (...) x = 0; and jumps are expensive,
- 3) x = a; if (...) x = b; and A and B are constants where all the
- set bits in A are also set in B and jumps are expensive, and
- 4) x = a; if (...) x = b; and A and B non-zero, and jumps are
- more expensive.
- 5) if (...) x = b; if jumps are even more expensive.
+ 1) x = a; if (...) x = b; and
+ 2) if (...) x = b;
- In each of these try to use a store-flag insn to avoid the jump.
- (If the jump would be faster, the machine should not have
- defined the scc insns!). These cases are often made by the
+ If the jump would be faster, the machine should not have defined
+ the movcc or scc insns!. These cases are often made by the
previous optimization.
+ The second case is treated as x = x; if (...) x = b;.
+
INSN here is the jump around the store. We set:
TEMP to the "x = b;" insn.
TEMP1 to X.
- TEMP2 to B (const0_rtx in the second case).
+ TEMP2 to B.
TEMP3 to A (X in the second case).
TEMP4 to the condition being tested.
TEMP5 to the earliest insn used to find the condition. */
#ifdef SMALL_REGISTER_CLASSES
&& REGNO (temp1) >= FIRST_PSEUDO_REGISTER
#endif
- && GET_MODE_CLASS (GET_MODE (temp1)) == MODE_INT
&& (GET_CODE (temp2 = SET_SRC (PATTERN (temp))) == REG
|| GET_CODE (temp2) == SUBREG
+ /* ??? How about floating point constants? */
|| GET_CODE (temp2) == CONST_INT)
/* Allow either form, but prefer the former if both apply.
There is no point in using the old value of TEMP1 if
it is a register, since cse will alias them. It can
lose if the old value were a hard register since CSE
won't replace hard registers. */
- && (((temp3 = reg_set_last (temp1, insn)) != 0
- && GET_CODE (temp3) == CONST_INT)
- /* Make the latter case look like x = x; if (...) x = 0; */
- || (temp3 = temp1,
- ((BRANCH_COST >= 2
- && temp2 == const0_rtx)
-#ifdef HAVE_conditional_move
- || 1
-#endif
- || BRANCH_COST >= 3)))
+ && (((temp3 = reg_set_last (temp1, insn)) != 0)
+ /* Make the latter case look like x = x; if (...) x = b; */
+ || (temp3 = temp1, 1))
/* INSN must either branch to the insn after TEMP or the insn
after TEMP must branch to the same place as INSN. */
&& (reallabelprev == temp
We could handle BLKmode if (1) emit_store_flag could
and (2) we could find the size reliably. */
&& GET_MODE (XEXP (temp4, 0)) != BLKmode
-
- /* If B is zero, OK; if A is zero, can only do (1) if we
- can reverse the condition. See if (3) applies possibly
- by reversing the condition. Prefer reversing to (4) when
- branches are very expensive. */
- && ((reversep = 0, temp2 == const0_rtx)
- || (temp3 == const0_rtx
- && (reversep = can_reverse_comparison_p (temp4, insn)))
- || (BRANCH_COST >= 2
- && GET_CODE (temp2) == CONST_INT
- && GET_CODE (temp3) == CONST_INT
- && ((INTVAL (temp2) & INTVAL (temp3)) == INTVAL (temp2)
- || ((INTVAL (temp2) & INTVAL (temp3)) == INTVAL (temp3)
- && (reversep = can_reverse_comparison_p (temp4,
- insn)))))
-#ifdef HAVE_conditional_move
- || 1
-#endif
- || BRANCH_COST >= 3)
+ /* Even if branches are cheap, the store_flag optimization
+ can win when the operation to be performed can be
+ expressed directly. */
#ifdef HAVE_cc0
/* If the previous insn sets CC0 and something else, we can't
do this since we are going to delete that insn. */
#endif
)
{
- enum rtx_code code = GET_CODE (temp4);
- rtx uval, cval, var = temp1;
- int normalizep;
- rtx target;
-
- /* If necessary, reverse the condition. */
- if (reversep)
- code = reverse_condition (code), uval = temp2, cval = temp3;
- else
- uval = temp3, cval = temp2;
-
- /* See if we can do this with a store-flag insn. */
- start_sequence ();
+#ifdef HAVE_conditional_move
+ /* First try a conditional move. */
+ {
+ enum rtx_code code = GET_CODE (temp4);
+ rtx var = temp1;
+ rtx cond0, cond1, aval, bval;
+ rtx target;
+
+ /* Copy the compared variables into cond0 and cond1, so that
+ any side effects performed in or after the old comparison,
+ will not affect our compare which will come later. */
+ /* ??? Is it possible to just use the comparison in the jump
+ insn? After all, we're going to delete it. We'd have
+ to modify emit_conditional_move to take a comparison rtx
+ instead or write a new function. */
+ cond0 = gen_reg_rtx (GET_MODE (XEXP (temp4, 0)));
+ /* We want the target to be able to simplify comparisons with
+ zero (and maybe other constants as well), so don't create
+ pseudos for them. There's no need to either. */
+ if (GET_CODE (XEXP (temp4, 1)) == CONST_INT
+ || GET_CODE (XEXP (temp4, 1)) == CONST_DOUBLE)
+ cond1 = XEXP (temp4, 1);
+ else
+ cond1 = gen_reg_rtx (GET_MODE (XEXP (temp4, 1)));
- /* If CVAL is non-zero, normalize to -1. Otherwise,
- if UVAL is the constant 1, it is best to just compute
- the result directly. If UVAL is constant and STORE_FLAG_VALUE
- includes all of its bits, it is best to compute the flag
- value unnormalized and `and' it with UVAL. Otherwise,
- normalize to -1 and `and' with UVAL. */
- normalizep = (cval != const0_rtx ? -1
- : (uval == const1_rtx ? 1
- : (GET_CODE (uval) == CONST_INT
- && (INTVAL (uval) & ~STORE_FLAG_VALUE) == 0)
- ? 0 : -1));
-
- /* We will be putting the store-flag insn immediately in
- front of the comparison that was originally being done,
- so we know all the variables in TEMP4 will be valid.
- However, this might be in front of the assignment of
- A to VAR. If it is, it would clobber the store-flag
- we will be emitting.
-
- Therefore, emit into a temporary which will be copied to
- VAR immediately after TEMP. */
-
- target = emit_store_flag (gen_reg_rtx (GET_MODE (var)), code,
- XEXP (temp4, 0), XEXP (temp4, 1),
- VOIDmode,
- (code == LTU || code == LEU
- || code == GEU || code == GTU),
- normalizep);
- if (target)
- {
- rtx before = insn;
- rtx seq;
+ aval = temp3;
+ bval = temp2;
- /* Put the store-flag insns in front of the first insn
- used to compute the condition to ensure that we
- use the same values of them as the current
- comparison. However, the remainder of the insns we
- generate will be placed directly in front of the
- jump insn, in case any of the pseudos we use
- are modified earlier. */
+ start_sequence ();
+ target = emit_conditional_move (var, code,
+ cond0, cond1, VOIDmode,
+ aval, bval, GET_MODE (var),
+ (code == LTU || code == GEU
+ || code == LEU || code == GTU));
- seq = get_insns ();
+ if (target)
+ {
+ rtx seq1,seq2;
+
+ /* Save the conditional move sequence but don't emit it
+ yet. On some machines, like the alpha, it is possible
+ that temp5 == insn, so next generate the sequence that
+ saves the compared values and then emit both
+ sequences ensuring seq1 occurs before seq2. */
+ seq2 = get_insns ();
+ end_sequence ();
+
+ /* Now that we can't fail, generate the copy insns that
+ preserve the compared values. */
+ start_sequence ();
+ emit_move_insn (cond0, XEXP (temp4, 0));
+ if (cond1 != XEXP (temp4, 1))
+ emit_move_insn (cond1, XEXP (temp4, 1));
+ seq1 = get_insns ();
+ end_sequence ();
+
+ emit_insns_before (seq1, temp5);
+ /* Insert conditional move after insn, to be sure that
+ the jump and a possible compare won't be separated */
+ emit_insns_after (seq2, insn);
+
+ /* ??? We can also delete the insn that sets X to A.
+ Flow will do it too though. */
+ delete_insn (temp);
+ next = NEXT_INSN (insn);
+ delete_jump (insn);
+ changed = 1;
+ continue;
+ }
+ else
end_sequence ();
+ }
+#endif
- emit_insns_before (seq, temp5);
+ /* That didn't work, try a store-flag insn.
+
+ We further divide the cases into:
+
+ 1) x = a; if (...) x = b; and either A or B is zero,
+ 2) if (...) x = 0; and jumps are expensive,
+ 3) x = a; if (...) x = b; and A and B are constants where all
+ the set bits in A are also set in B and jumps are expensive,
+ 4) x = a; if (...) x = b; and A and B non-zero, and jumps are
+ more expensive, and
+ 5) if (...) x = b; if jumps are even more expensive. */
+
+ if (GET_MODE_CLASS (GET_MODE (temp1)) == MODE_INT
+ && ((GET_CODE (temp3) == CONST_INT)
+ /* Make the latter case look like
+ x = x; if (...) x = 0; */
+ || (temp3 = temp1,
+ ((BRANCH_COST >= 2
+ && temp2 == const0_rtx)
+ || BRANCH_COST >= 3)))
+ /* If B is zero, OK; if A is zero, can only do (1) if we
+ can reverse the condition. See if (3) applies possibly
+ by reversing the condition. Prefer reversing to (4) when
+ branches are very expensive. */
+ && (((BRANCH_COST >= 2
+ || STORE_FLAG_VALUE == -1
+ || (STORE_FLAG_VALUE == 1
+ /* Check that the mask is a power of two,
+ so that it can probably be generated
+ with a shift. */
+ && exact_log2 (INTVAL (temp3)) >= 0))
+ && (reversep = 0, temp2 == const0_rtx))
+ || ((BRANCH_COST >= 2
+ || STORE_FLAG_VALUE == -1
+ || (STORE_FLAG_VALUE == 1
+ && exact_log2 (INTVAL (temp2)) >= 0))
+ && temp3 == const0_rtx
+ && (reversep = can_reverse_comparison_p (temp4, insn)))
+ || (BRANCH_COST >= 2
+ && GET_CODE (temp2) == CONST_INT
+ && GET_CODE (temp3) == CONST_INT
+ && ((INTVAL (temp2) & INTVAL (temp3)) == INTVAL (temp2)
+ || ((INTVAL (temp2) & INTVAL (temp3)) == INTVAL (temp3)
+ && (reversep = can_reverse_comparison_p (temp4,
+ insn)))))
+ || BRANCH_COST >= 3)
+ )
+ {
+ enum rtx_code code = GET_CODE (temp4);
+ rtx uval, cval, var = temp1;
+ int normalizep;
+ rtx target;
- start_sequence ();
+ /* If necessary, reverse the condition. */
+ if (reversep)
+ code = reverse_condition (code), uval = temp2, cval = temp3;
+ else
+ uval = temp3, cval = temp2;
+
+ /* If CVAL is non-zero, normalize to -1. Otherwise, if UVAL
+ is the constant 1, it is best to just compute the result
+ directly. If UVAL is constant and STORE_FLAG_VALUE
+ includes all of its bits, it is best to compute the flag
+ value unnormalized and `and' it with UVAL. Otherwise,
+ normalize to -1 and `and' with UVAL. */
+ normalizep = (cval != const0_rtx ? -1
+ : (uval == const1_rtx ? 1
+ : (GET_CODE (uval) == CONST_INT
+ && (INTVAL (uval) & ~STORE_FLAG_VALUE) == 0)
+ ? 0 : -1));
+
+ /* We will be putting the store-flag insn immediately in
+ front of the comparison that was originally being done,
+ so we know all the variables in TEMP4 will be valid.
+ However, this might be in front of the assignment of
+ A to VAR. If it is, it would clobber the store-flag
+ we will be emitting.
+
+ Therefore, emit into a temporary which will be copied to
+ VAR immediately after TEMP. */
- /* Both CVAL and UVAL are non-zero. */
- if (cval != const0_rtx && uval != const0_rtx)
+ start_sequence ();
+ target = emit_store_flag (gen_reg_rtx (GET_MODE (var)), code,
+ XEXP (temp4, 0), XEXP (temp4, 1),
+ VOIDmode,
+ (code == LTU || code == LEU
+ || code == GEU || code == GTU),
+ normalizep);
+ if (target)
{
- rtx tem1, tem2;
+ rtx seq;
+ rtx before = insn;
- tem1 = expand_and (uval, target, NULL_RTX);
- if (GET_CODE (cval) == CONST_INT
- && GET_CODE (uval) == CONST_INT
- && (INTVAL (cval) & INTVAL (uval)) == INTVAL (cval))
- tem2 = cval;
- else
- {
- tem2 = expand_unop (GET_MODE (var), one_cmpl_optab,
- target, NULL_RTX, 0);
- tem2 = expand_and (cval, tem2,
- (GET_CODE (tem2) == REG
- ? tem2 : 0));
- }
+ seq = get_insns ();
+ end_sequence ();
- /* If we usually make new pseudos, do so here. This
- turns out to help machines that have conditional
- move insns. */
+ /* Put the store-flag insns in front of the first insn
+ used to compute the condition to ensure that we
+ use the same values of them as the current
+ comparison. However, the remainder of the insns we
+ generate will be placed directly in front of the
+ jump insn, in case any of the pseudos we use
+ are modified earlier. */
- if (flag_expensive_optimizations)
- target = 0;
+ emit_insns_before (seq, temp5);
- target = expand_binop (GET_MODE (var), ior_optab,
- tem1, tem2, target,
- 1, OPTAB_WIDEN);
- }
- else if (normalizep != 1)
- {
- /* We know that either CVAL or UVAL is zero. If
- UVAL is zero, negate TARGET and `and' with CVAL.
- Otherwise, `and' with UVAL. */
- if (uval == const0_rtx)
+ start_sequence ();
+
+ /* Both CVAL and UVAL are non-zero. */
+ if (cval != const0_rtx && uval != const0_rtx)
{
- target = expand_unop (GET_MODE (var), one_cmpl_optab,
- target, NULL_RTX, 0);
- uval = cval;
+ rtx tem1, tem2;
+
+ tem1 = expand_and (uval, target, NULL_RTX);
+ if (GET_CODE (cval) == CONST_INT
+ && GET_CODE (uval) == CONST_INT
+ && (INTVAL (cval) & INTVAL (uval)) == INTVAL (cval))
+ tem2 = cval;
+ else
+ {
+ tem2 = expand_unop (GET_MODE (var), one_cmpl_optab,
+ target, NULL_RTX, 0);
+ tem2 = expand_and (cval, tem2,
+ (GET_CODE (tem2) == REG
+ ? tem2 : 0));
+ }
+
+ /* If we usually make new pseudos, do so here. This
+ turns out to help machines that have conditional
+ move insns. */
+ /* ??? Conditional moves have already been handled.
+ This may be obsolete. */
+
+ if (flag_expensive_optimizations)
+ target = 0;
+
+ target = expand_binop (GET_MODE (var), ior_optab,
+ tem1, tem2, target,
+ 1, OPTAB_WIDEN);
}
+ else if (normalizep != 1)
+ {
+ /* We know that either CVAL or UVAL is zero. If
+ UVAL is zero, negate TARGET and `and' with CVAL.
+ Otherwise, `and' with UVAL. */
+ if (uval == const0_rtx)
+ {
+ target = expand_unop (GET_MODE (var), one_cmpl_optab,
+ target, NULL_RTX, 0);
+ uval = cval;
+ }
- target = expand_and (uval, target,
- (GET_CODE (target) == REG
- && ! preserve_subexpressions_p ()
- ? target : NULL_RTX));
- }
+ target = expand_and (uval, target,
+ (GET_CODE (target) == REG
+ && ! preserve_subexpressions_p ()
+ ? target : NULL_RTX));
+ }
- emit_move_insn (var, target);
- seq = get_insns ();
- end_sequence ();
-
+ emit_move_insn (var, target);
+ seq = get_insns ();
+ end_sequence ();
#ifdef HAVE_cc0
- /* If INSN uses CC0, we must not separate it from the
- insn that sets cc0. */
-
- if (reg_mentioned_p (cc0_rtx, PATTERN (before)))
- before = prev_nonnote_insn (before);
+ /* If INSN uses CC0, we must not separate it from the
+ insn that sets cc0. */
+ if (reg_mentioned_p (cc0_rtx, PATTERN (before)))
+ before = prev_nonnote_insn (before);
#endif
+ emit_insns_before (seq, before);
- emit_insns_before (seq, before);
-
- delete_insn (temp);
- next = NEXT_INSN (insn);
-
- delete_jump (insn);
- changed = 1;
- continue;
+ delete_insn (temp);
+ next = NEXT_INSN (insn);
+ delete_jump (insn);
+ changed = 1;
+ continue;
+ }
+ else
+ end_sequence ();
}
- else
- end_sequence ();
}
/* If branches are expensive, convert
&& (XEXP (SET_SRC (temp1), 1) == const1_rtx
|| XEXP (SET_SRC (temp1), 1) == constm1_rtx)
&& rtx_equal_p (temp2, XEXP (SET_SRC (temp1), 0))
+ && ! side_effects_p (temp2)
+ && ! may_trap_p (temp2)
/* INSN must either branch to the insn after TEMP or the insn
after TEMP must branch to the same place as INSN. */
&& (reallabelprev == temp
else if (ultimate && GET_CODE (ultimate) != RETURN)
ultimate = XEXP (ultimate, 0);
- if (ultimate)
+ if (ultimate && JUMP_LABEL(insn) != ultimate)
changed |= redirect_jump (insn, ultimate);
}
}
}
/* Detect a conditional jump jumping over an unconditional jump. */
- else if (this_is_condjump && ! this_is_simplejump
+ else if ((this_is_condjump || this_is_condjump_in_parallel)
+ && ! this_is_simplejump
&& reallabelprev != 0
&& GET_CODE (reallabelprev) == JUMP_INSN
&& prev_active_insn (reallabelprev) == insn
rtx range2beg = next_active_insn (label1);
rtx range1after, range2after;
rtx range1before, range2before;
+ rtx rangenext;
/* Include in each range any notes before it, to be
sure that we get the line number note if any, even
PREV_INSN (range1beg) = range2before;
NEXT_INSN (range1end) = range2after;
PREV_INSN (range2after) = range1end;
+
+ /* Check for a loop end note between the end of
+ range2, and the next code label. If there is one,
+ then what we have really seen is
+ if (foo) break; end_of_loop;
+ and moved the break sequence outside the loop.
+ We must move the LOOP_END note to where the
+ loop really ends now, or we will confuse loop
+ optimization. Stop if we find a LOOP_BEG note
+ first, since we don't want to move the LOOP_END
+ note in that case. */
+ for (;range2after != label2; range2after = rangenext)
+ {
+ rangenext = NEXT_INSN (range2after);
+ if (GET_CODE (range2after) == NOTE)
+ {
+ if (NOTE_LINE_NUMBER (range2after)
+ == NOTE_INSN_LOOP_END)
+ {
+ NEXT_INSN (PREV_INSN (range2after))
+ = rangenext;
+ PREV_INSN (rangenext)
+ = PREV_INSN (range2after);
+ PREV_INSN (range2after)
+ = PREV_INSN (range1beg);
+ NEXT_INSN (range2after) = range1beg;
+ NEXT_INSN (PREV_INSN (range1beg))
+ = range2after;
+ PREV_INSN (range1beg) = range2after;
+ }
+ else if (NOTE_LINE_NUMBER (range2after)
+ == NOTE_INSN_LOOP_BEG)
+ break;
+ }
+ }
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. */
+ before the jump and before its target label. */
/* First, cross jumping of conditional jumps: */
INSN_CODE (insn) = -1;
emit_barrier_after (insn);
/* Add to jump_chain unless this is a new label
- whose UID is too large. */
+ whose UID is too large. */
if (INSN_UID (JUMP_LABEL (insn)) < max_jump_chain)
{
jump_chain[INSN_UID (insn)]
then one of them follows the note. */
|| (GET_CODE (insn) == JUMP_INSN
&& GET_CODE (PATTERN (insn)) == RETURN)
+ /* A barrier can follow the return insn. */
+ || GET_CODE (insn) == BARRIER
/* Other kinds of notes can follow also. */
|| (GET_CODE (insn) == NOTE
&& NOTE_LINE_NUMBER (insn) != NOTE_INSN_FUNCTION_END)))
followed by a jump to the exit of the loop. Then delete the unconditional
jump after INSN.
- Note that it is possible we can get confused here if the jump immediately
- after the loop start branches outside the loop but within an outer loop.
- If we are near the exit of that loop, we will copy its exit test. This
- will not generate incorrect code, but could suppress some optimizations.
- However, such cases are degenerate loops anyway.
-
Return 1 if we made the change, else 0.
This is only safe immediately after a regscan pass because it uses the
duplicate_loop_exit_test (loop_start)
rtx loop_start;
{
- rtx insn, set, p;
- rtx copy, link;
+ rtx insn, set, reg, p, link;
+ rtx copy = 0;
int num_insns = 0;
rtx exitcode = NEXT_INSN (JUMP_LABEL (next_nonnote_insn (loop_start)));
rtx lastexit;
case CALL_INSN:
return 0;
case NOTE:
+ /* We could be in front of the wrong NOTE_INSN_LOOP_END if there is
+ a jump immediately after the loop start that branches outside
+ the loop but within an outer loop, near the exit test.
+ If we copied this exit test and created a phony
+ NOTE_INSN_LOOP_VTOP, this could make instructions immediately
+ before the exit test look like these could be safely moved
+ out of the loop even if they actually may be never executed.
+ This can be avoided by checking here for NOTE_INSN_LOOP_CONT. */
+
if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_BEG
|| NOTE_LINE_NUMBER (insn) == NOTE_INSN_BLOCK_BEG
- || NOTE_LINE_NUMBER (insn) == NOTE_INSN_BLOCK_END)
+ || NOTE_LINE_NUMBER (insn) == NOTE_INSN_BLOCK_END
+ || NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_CONT)
return 0;
break;
case JUMP_INSN:
for (insn = exitcode; insn != lastexit; insn = NEXT_INSN (insn))
if (GET_CODE (insn) == INSN
&& (set = single_set (insn)) != 0
- && GET_CODE (SET_DEST (set)) == REG
- && REGNO (SET_DEST (set)) >= FIRST_PSEUDO_REGISTER
- && regno_first_uid[REGNO (SET_DEST (set))] == INSN_UID (insn))
+ && ((reg = SET_DEST (set), GET_CODE (reg) == REG)
+ || (GET_CODE (reg) == SUBREG
+ && (reg = SUBREG_REG (reg), GET_CODE (reg) == REG)))
+ && REGNO (reg) >= FIRST_PSEUDO_REGISTER
+ && regno_first_uid[REGNO (reg)] == INSN_UID (insn))
{
for (p = NEXT_INSN (insn); p != lastexit; p = NEXT_INSN (p))
- if (regno_last_uid[REGNO (SET_DEST (set))] == INSN_UID (p))
+ if (regno_last_uid[REGNO (reg)] == INSN_UID (p))
break;
if (p != lastexit)
if (reg_map == 0)
{
reg_map = (rtx *) alloca (max_reg * sizeof (rtx));
- bzero (reg_map, max_reg * sizeof (rtx));
+ bzero ((char *) reg_map, max_reg * sizeof (rtx));
}
- REG_LOOP_TEST_P (SET_DEST (set)) = 1;
+ REG_LOOP_TEST_P (reg) = 1;
- reg_map[REGNO (SET_DEST (set))]
- = gen_reg_rtx (GET_MODE (SET_DEST (set)));
+ reg_map[REGNO (reg)] = gen_reg_rtx (GET_MODE (reg));
}
}
/* Now clean up by emitting a jump to the end label and deleting the jump
at the start of the loop. */
- if (GET_CODE (copy) != BARRIER)
+ if (! copy || GET_CODE (copy) != BARRIER)
{
copy = emit_jump_insn_before (gen_jump (get_label_after (insn)),
loop_start);
emit_barrier_before (loop_start);
}
- delete_insn (next_nonnote_insn (loop_start));
-
/* Mark the exit code as the virtual top of the converted loop. */
emit_note_before (NOTE_INSN_LOOP_VTOP, exitcode);
+ delete_insn (next_nonnote_insn (loop_start));
+
return 1;
}
\f
#ifdef STACK_REGS
/* If cross_jump_death_matters is not 0, the insn's mode
indicates whether or not the insn contains any stack-like
- regs. */
+ regs. */
if (!lose && cross_jump_death_matters && GET_MODE (i1) == QImode)
{
/* If register stack conversion has already been done, then
death notes must also be compared before it is certain that
- the two instruction streams match. */
+ the two instruction streams match. */
rtx note;
HARD_REG_SET i1_regset, i2_regset;
return 0;
}
+/* Return nonzero if INSN is a (possibly) conditional jump
+ and nothing more. */
+
+int
+condjump_in_parallel_p (insn)
+ rtx insn;
+{
+ register rtx x = PATTERN (insn);
+
+ if (GET_CODE (x) != PARALLEL)
+ return 0;
+ else
+ x = XVECEXP (x, 0, 0);
+
+ 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
+ || GET_CODE (XEXP (SET_SRC (x), 1)) == RETURN))
+ return 1;
+ if (XEXP (SET_SRC (x), 1) == pc_rtx
+ && (GET_CODE (XEXP (SET_SRC (x), 2)) == LABEL_REF
+ || GET_CODE (XEXP (SET_SRC (x), 2)) == RETURN))
+ 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,
(depth < 10
&& (insn = next_active_insn (value)) != 0
&& GET_CODE (insn) == JUMP_INSN
- && (JUMP_LABEL (insn) != 0 || GET_CODE (PATTERN (insn)) == RETURN)
+ && ((JUMP_LABEL (insn) != 0 && simplejump_p (insn))
+ || GET_CODE (PATTERN (insn)) == RETURN)
&& (next = NEXT_INSN (insn))
&& GET_CODE (next) == BARRIER);
depth++)
return next;
}
+ /* Likewise if we're deleting a dispatch table. */
+
+ if (GET_CODE (insn) == JUMP_INSN
+ && (GET_CODE (PATTERN (insn)) == ADDR_VEC
+ || GET_CODE (PATTERN (insn)) == ADDR_DIFF_VEC))
+ {
+ rtx pat = PATTERN (insn);
+ int i, diff_vec_p = GET_CODE (pat) == ADDR_DIFF_VEC;
+ int len = XVECLEN (pat, diff_vec_p);
+
+ for (i = 0; i < len; i++)
+ if (--LABEL_NUSES (XEXP (XVECEXP (pat, diff_vec_p, i), 0)) == 0)
+ delete_insn (XEXP (XVECEXP (pat, diff_vec_p, i), 0));
+ 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);
register RTX_CODE code;
while (next != 0
&& (GET_RTX_CLASS (code = GET_CODE (next)) == 'i'
- || code == NOTE
+ || code == NOTE || code == BARRIER
|| (code == CODE_LABEL && INSN_DELETED_P (next))))
{
if (code == NOTE
bzero (modified_regs, max_reg * sizeof (char));
modified_mem = 0;
- bcopy (all_reset, same_regs, max_reg * sizeof (int));
+ bcopy ((char *) all_reset, (char *) same_regs,
+ max_reg * sizeof (int));
num_same_regs = 0;
label = JUMP_LABEL (b1);
case MEM:
/* If memory modified or either volatile, not equivalent.
- Else, check address. */
+ Else, check address. */
if (modified_mem || MEM_VOLATILE_P (x) || MEM_VOLATILE_P (y))
return 0;