/* Perform instruction reorganizations for delay slot filling.
- Copyright (C) 1992 Free Software Foundation, Inc.
+ Copyright (C) 1992, 1993 Free Software Foundation, Inc.
Contributed by Richard Kenner (kenner@nyu.edu).
Hacked by Michael Tiemann (tiemann@cygnus.com).
static int own_thread_p PROTO((rtx, rtx, int));
static int find_basic_block PROTO((rtx));
static void update_block PROTO((rtx, rtx));
+static int reorg_redirect_jump PROTO((rtx, rtx));
static void update_reg_dead_notes PROTO((rtx, rtx));
static void update_live_status PROTO((rtx, rtx));
static rtx next_insn_no_annul PROTO((rtx));
|| GET_CODE (PATTERN (insn)) == CLOBBER)))
insn = PREV_INSN (insn);
- if (GET_CODE (insn) == CODE_LABEL)
- end_of_function_label = insn;
+ /* When a target threads its epilogue we might already have a
+ suitable return insn. If so put a label before it for the
+ end_of_function_label. */
+ if (GET_CODE (insn) == BARRIER
+ && GET_CODE (PREV_INSN (insn)) == JUMP_INSN
+ && GET_CODE (PATTERN (PREV_INSN (insn))) == RETURN)
+ {
+ rtx temp = PREV_INSN (PREV_INSN (insn));
+ end_of_function_label = gen_label_rtx ();
+ LABEL_NUSES (end_of_function_label) = 0;
+
+ /* Put the label before an USE insns that may proceed the RETURN insn. */
+ while (GET_CODE (temp) == USE)
+ temp = PREV_INSN (temp);
+
+ emit_label_after (end_of_function_label, temp);
+ }
+
+ else if (GET_CODE (insn) == CODE_LABEL)
+ end_of_function_label = insn;
else
{
/* Otherwise, make a new label and emit a RETURN and BARRIER,
if (HAVE_return)
{
/* The return we make may have delay slots too. */
- rtx insn = gen_return();
- emit_jump_insn (insn);
+ rtx insn = gen_return ();
+ insn = emit_jump_insn (insn);
emit_barrier ();
if (num_delay_slots (insn) > 0)
obstack_ptr_grow (&unfilled_slots_obstack, insn);
rtx insn;
rtx delay_list;
{
- /* If we have an empty list, just make a new list element. */
+ /* If we have an empty list, just make a new list element. If
+ INSN has it's block number recorded, clear it since we may
+ be moving the insn to a new block. */
+
if (delay_list == 0)
- return gen_rtx (INSN_LIST, VOIDmode, insn, NULL_RTX);
+ {
+ struct target_info *tinfo;
+
+ for (tinfo = target_hash_table[INSN_UID (insn) % TARGET_HASH_PRIME];
+ tinfo; tinfo = tinfo->next)
+ if (tinfo->uid == INSN_UID (insn))
+ break;
+
+ if (tinfo)
+ tinfo->block = -1;
+
+ return gen_rtx (INSN_LIST, VOIDmode, insn, NULL_RTX);
+ }
/* Otherwise this must be an INSN_LIST. Add INSN to the end of the
list. */
target_label = JUMP_LABEL (next_trial);
if (target_label == 0)
target_label = find_end_label ();
- redirect_jump (insn, target_label);
+ reorg_redirect_jump (insn, target_label);
}
INSN_ANNULLED_BRANCH_P (insn) = 1;
/* Predict backward branches usually take, forward branches usually not. If
we don't know whether this is forward or backward, assume the branch
will be taken, since most are. */
- return (INSN_UID (jump_insn) > max_uid || INSN_UID (target_label) > max_uid
+ return (target_label == 0 || INSN_UID (jump_insn) > max_uid
+ || INSN_UID (target_label) > max_uid
|| (uid_to_ruid[INSN_UID (jump_insn)]
> uid_to_ruid[INSN_UID (target_label)]));;
}
return comparison_dominates_p (code, other_code);
}
+
+/* Return non-zero if redirecting JUMP to NEWLABEL does not invalidate
+ any insns already in the delay slot of JUMP. */
+
+static int
+redirect_with_delay_slots_safe_p (jump, newlabel, seq)
+ rtx jump, newlabel, seq;
+{
+ int flags, slots, i;
+ rtx pat = PATTERN (seq);
+
+ /* Make sure all the delay slots of this jump would still
+ be valid after threading the jump. If they are still
+ valid, then return non-zero. */
+
+ flags = get_jump_flags (jump, newlabel);
+ for (i = 1; i < XVECLEN (pat, 0); i++)
+ if (! (
+#ifdef ANNUL_IFFALSE_SLOTS
+ (INSN_ANNULLED_BRANCH_P (jump)
+ && INSN_FROM_TARGET_P (XVECEXP (pat, 0, i)))
+ ? eligible_for_annul_false (jump, i - 1,
+ XVECEXP (pat, 0, i), flags) :
+#endif
+#ifdef ANNUL_IFTRUE_SLOTS
+ (INSN_ANNULLED_BRANCH_P (jump)
+ && ! INSN_FROM_TARGET_P (XVECEXP (pat, 0, i)))
+ ? eligible_for_annul_true (jump, i - 1,
+ XVECEXP (pat, 0, i), flags) :
+#endif
+ eligible_for_delay (jump, i -1, XVECEXP (pat, 0, i), flags)))
+ break;
+
+ return (i == XVECLEN (pat, 0));
+}
+
\f
/* INSN branches to an insn whose pattern SEQ is a SEQUENCE. Given that
the condition tested by INSN is CONDITION and the resources shown in
int i;
int flags;
- flags = get_jump_flags (insn, JUMP_LABEL (insn));
+ flags = get_jump_flags (delay_insn, JUMP_LABEL (delay_insn));
CLEAR_RESOURCE (&needed);
CLEAR_RESOURCE (&set);
bb_ticks[b]++;
}
+/* Similar to REDIRECT_JUMP except that we update the BB_TICKS entry for
+ the basic block containing the jump. */
+
+static int
+reorg_redirect_jump (jump, nlabel)
+ rtx jump;
+ rtx nlabel;
+{
+ int b = find_basic_block (jump);
+
+ if (b != -1)
+ bb_ticks[b]++;
+
+ return redirect_jump (jump, nlabel);
+}
+
/* Called when INSN is being moved forward into a delay slot of DELAYED_INSN.
We check every instruction between INSN and DELAYED_INSN for REG_DEAD notes
that reference values used in INSN. If we find one, then we move the
|| (GET_CODE (insn) != JUMP_INSN && ! non_jumps_p))
continue;
- flags = get_jump_flags (insn, JUMP_LABEL (insn));
+ if (GET_CODE (insn) == JUMP_INSN)
+ flags = get_jump_flags (insn, JUMP_LABEL (insn));
+ else
+ flags = get_jump_flags (insn, NULL_RTX);
slots_to_fill = num_delay_slots (insn);
if (slots_to_fill == 0)
abort ();
the unconditional jump. This is done first to avoid having the
delay slot of the call filled in the backward scan. Also, since
the unconditional jump is likely to also have a delay slot, that
- insn must exist when it is subsequently scanned. */
+ insn must exist when it is subsequently scanned.
+
+ This is tried on each insn with delay slots as some machines
+ have insns which perform calls, but are not represented as
+ CALL_INSNs. */
slots_filled = 0;
delay_list = 0;
- if (GET_CODE (insn) == CALL_INSN
- && (trial = next_active_insn (insn))
+ if ((trial = next_active_insn (insn))
&& GET_CODE (trial) == JUMP_INSN
&& simplejump_p (trial)
&& eligible_for_delay (insn, slots_filled, trial, flags)
delay_list
= add_to_delay_list (copy_rtx (next_trial), delay_list);
slots_filled++;
- redirect_jump (trial, new_label);
+ reorg_redirect_jump (trial, new_label);
/* If we merged because we both jumped to the same place,
redirect the original insn also. */
if (target)
- redirect_jump (insn, new_label);
+ reorg_redirect_jump (insn, new_label);
}
}
! stop_search_p (trial, ! thread_if_true) && (! lose || own_thread);
trial = next_nonnote_insn (trial))
{
- rtx pat;
+ rtx pat, old_trial;
/* If we have passed a label, we no longer own this thread. */
if (GET_CODE (trial) == CODE_LABEL)
|| (! insn_sets_resource_p (trial, &opposite_needed, 1)
&& ! may_trap_p (pat)))
{
+ old_trial = trial;
trial = try_split (pat, trial, 0);
+ if (new_thread == old_trial)
+ new_thread = trial;
pat = PATTERN (trial);
if (eligible_for_delay (insn, *pslots_filled, trial, flags))
goto winner;
#endif
)
{
+ old_trial = trial;
trial = try_split (pat, trial, 0);
+ if (new_thread == old_trial)
+ new_thread = trial;
pat = PATTERN (trial);
if ((thread_if_true
? eligible_for_annul_false (insn, *pslots_filled, trial, flags)
but it doesn't seem worth it. It might also be a good idea to try
to swap the two insns. That might do better.
- We can't do this if the next insn modifies our source, because that
- would make the replacement into the insn invalid. This also
- prevents updating the contents of a PRE_INC. */
+ We can't do this if the next insn modifies our destination, because
+ that would make the replacement into the insn invalid. We also can't
+ do this if it modifies our source, because it might be an earlyclobber
+ operand. This latter test also prevents updating the contents of
+ a PRE_INC. */
if (GET_CODE (trial) == INSN && GET_CODE (pat) == SET
&& GET_CODE (SET_SRC (pat)) == REG
if (next && GET_CODE (next) == INSN
&& GET_CODE (PATTERN (next)) != USE
&& ! reg_set_p (SET_DEST (pat), next)
+ && ! reg_set_p (SET_SRC (pat), next)
&& reg_referenced_p (SET_DEST (pat), PATTERN (next)))
validate_replace_rtx (SET_DEST (pat), SET_SRC (pat), next);
}
else
label = get_label_before (new_thread);
- redirect_jump (insn, label);
+ reorg_redirect_jump (insn, label);
}
return delay_list;
}
if (target_label != JUMP_LABEL (insn))
- redirect_jump (insn, target_label);
+ reorg_redirect_jump (insn, target_label);
/* See if this jump branches around a unconditional jump.
If so, invert this jump and point it to the target of the
JUMP_LABEL (other))))
{
rtx other_target = JUMP_LABEL (other);
+ target_label = JUMP_LABEL (insn);
/* Increment the count of OTHER_TARGET, so it doesn't get deleted
as we move the label. */
++LABEL_NUSES (other_target);
if (invert_jump (other, target_label))
- redirect_jump (insn, other_target);
+ reorg_redirect_jump (insn, other_target);
if (other_target)
--LABEL_NUSES (other_target);
if (trial == 0 && target_label != 0)
trial = find_end_label ();
- if (trial != target_label)
+ if (trial != target_label
+ && redirect_with_delay_slots_safe_p (delay_insn, trial, insn))
{
- redirect_jump (delay_insn, trial);
+ reorg_redirect_jump (delay_insn, trial);
target_label = trial;
}
target_label = find_end_label ();
else
target_label = get_label_before (trial);
- redirect_jump (delay_insn, target_label);
+ reorg_redirect_jump (delay_insn, target_label);
next = insn;
continue;
}
target_label = JUMP_LABEL (XVECEXP (PATTERN (trial), 0, 0));
if (target_label == 0)
target_label = find_end_label ();
- redirect_jump (delay_insn, target_label);
- next = insn;
- continue;
+
+ if (redirect_with_delay_slots_safe_p (delay_insn, target_label,
+ insn))
+ {
+ reorg_redirect_jump (delay_insn, target_label);
+ next = insn;
+ continue;
+ }
}
}
if (label == 0)
label = find_end_label ();
- /* Be careful how we do this to avoid deleting code or labels
- that are momentarily dead. See similar optimization in jump.c */
- if (old_label)
- ++LABEL_NUSES (old_label);
-
- if (invert_jump (delay_insn, label))
+ if (redirect_with_delay_slots_safe_p (delay_insn, label, insn))
{
- delete_insn (next);
- next = insn;
- }
+ /* Be careful how we do this to avoid deleting code or labels
+ that are momentarily dead. See similar optimization in
+ jump.c */
+ if (old_label)
+ ++LABEL_NUSES (old_label);
- if (old_label && --LABEL_NUSES (old_label) == 0)
- delete_insn (old_label);
- continue;
+ if (invert_jump (delay_insn, label))
+ {
+ delete_insn (next);
+ next = insn;
+ }
+
+ if (old_label && --LABEL_NUSES (old_label) == 0)
+ delete_insn (old_label);
+ continue;
+ }
}
/* If we own the thread opposite the way this insn branches, see if we
/* If we can't make the jump into a RETURN, redirect it to the best
RETURN and go on to the next insn. */
- if (! redirect_jump (jump_insn, NULL_RTX))
+ if (! reorg_redirect_jump (jump_insn, NULL_RTX))
{
- redirect_jump (jump_insn, real_return_label);
+ reorg_redirect_jump (jump_insn, real_return_label);
continue;
}
else
/* It is probably more efficient to keep this with its current
delay slot as a branch to a RETURN. */
- redirect_jump (jump_insn, real_return_label);
+ reorg_redirect_jump (jump_insn, real_return_label);
}
/* Now delete REAL_RETURN_LABEL if we never used it. Then try to fill any
flag_no_peephole = old_flag_no_peephole;
#endif
+ /* If the current function has no insns other than the prologue and
+ epilogue, then do not try to fill any delay slots. */
+ if (n_basic_blocks == 0)
+ return;
+
/* Find the highest INSN_UID and allocate and initialize our map from
INSN_UID's to position in code. */
for (max_uid = 0, insn = first; insn; insn = NEXT_INSN (insn))