/* 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;
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;
&& (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));
We could handle BLKmode if (1) emit_store_flag could
and (2) we could find the size reliably. */
&& GET_MODE (XEXP (temp4, 0)) != BLKmode
- /* No point in doing any of this if branches are cheap or we
- don't have conditional moves. */
- && (BRANCH_COST >= 2
-#ifdef HAVE_conditional_move
- || 1
-#endif
- )
+ /* 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. */
end_sequence ();
emit_insns_before (seq1, temp5);
- emit_insns_before (seq2, insn);
+ /* 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. */
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
+ && (((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
else if (ultimate && GET_CODE (ultimate) != RETURN)
ultimate = XEXP (ultimate, 0);
- if (ultimate)
+ if (ultimate && JUMP_LABEL(insn) != ultimate)
changed |= redirect_jump (insn, ultimate);
}
}
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. */
+ 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
- && (NOTE_LINE_NUMBER (range2after)
- == NOTE_INSN_LOOP_END))
+ if (GET_CODE (range2after) == NOTE)
{
- 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;
+ 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;
/* 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
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:
#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;
(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++)
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
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;