/* Perform instruction reorganizations for delay slot filling.
- Copyright (C) 1992, 1993, 1994 Free Software Foundation, Inc.
+ Copyright (C) 1992, 1993, 1994, 1995 Free Software Foundation, Inc.
Contributed by Richard Kenner (kenner@vlsi1.ultra.nyu.edu).
Hacked by Michael Tiemann (tiemann@cygnus.com).
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. */
/* Instruction reorganization pass.
struct resources
{
char memory; /* Insn sets or needs a memory location. */
+ char unch_memory; /* Insn sets of needs a "unchanging" MEM. */
char volatil; /* Insn sets or needs a volatile memory loc. */
char cc; /* Insn sets or needs the condition codes. */
HARD_REG_SET regs; /* Which registers are set or needed. */
/* Macro to clear all resources. */
#define CLEAR_RESOURCE(RES) \
- do { (RES)->memory = (RES)->volatil = (RES)->cc = 0; \
+ do { (RES)->memory = (RES)->unch_memory = (RES)->volatil = (RES)->cc = 0; \
CLEAR_HARD_REG_SET ((RES)->regs); } while (0)
/* Indicates what resources are required at the beginning of the epilogue. */
case MEM:
/* If this memory shouldn't change, it really isn't referencing
memory. */
- if (! RTX_UNCHANGING_P (x))
+ if (RTX_UNCHANGING_P (x))
+ res->unch_memory = 1;
+ else
res->memory = 1;
res->volatil = MEM_VOLATILE_P (x);
rtx insn = PREV_INSN (x);
rtx sequence = 0;
int seq_size = 0;
+ rtx next = NEXT_INSN (x);
int i;
/* If we are part of a delay slot sequence, point at the SEQUENCE. */
if (NEXT_INSN (insn) != x)
{
+ next = NEXT_INSN (NEXT_INSN (insn));
sequence = PATTERN (NEXT_INSN (insn));
seq_size = XVECLEN (sequence, 0);
if (GET_CODE (sequence) != SEQUENCE)
if (global_regs[i])
SET_HARD_REG_BIT (res->regs, i);
+ /* Check for a NOTE_INSN_SETJMP. If it exists, then we must
+ assume that this call can need any register.
+
+ This is done to be more conservative about how we handle setjmp.
+ We assume that they both use and set all registers. Using all
+ registers ensures that a register will not be considered dead
+ just because it crosses a setjmp call. A register should be
+ considered dead only if the setjmp call returns non-zero. */
+ if (next && GET_CODE (next) == NOTE
+ && NOTE_LINE_NUMBER (next) == NOTE_INSN_SETJMP)
+ SET_HARD_REG_SET (res->regs);
+
{
rtx link;
if (in_dest)
{
res->memory = 1;
+ res->unch_memory = RTX_UNCHANGING_P (x);
res->volatil = MEM_VOLATILE_P (x);
}
struct resources *res1, *res2;
{
if ((res1->cc && res2->cc) || (res1->memory && res2->memory)
+ || (res1->unch_memory && res2->unch_memory)
|| res1->volatil || res2->volatil)
return 1;
return 1;
}
- /* Look at the relative rarities of the fallthough and destination. If
+ /* Look at the relative rarities of the fallthrough and destination. If
they differ, we can predict the branch that way. */
switch (rare_fallthrough - rare_dest)
/* We can't do anything if there are more delay slots in SEQ than we
can handle, or if we don't know that it will be a taken branch.
-
We know that it will be a taken branch if it is either an unconditional
- branch or a conditional branch with a stricter branch condition. */
+ branch or a conditional branch with a stricter branch condition.
+
+ Also, exit if the branch has more than one set, since then it is computing
+ other results that can't be ignored, e.g. the HPPA mov&branch instruction.
+ ??? It may be possible to move other sets into INSN in addition to
+ moving the instructions in the delay slots. */
if (XVECLEN (seq, 0) - 1 > slots_remaining
- || ! condition_dominates_p (condition, XVECEXP (seq, 0, 0)))
+ || ! condition_dominates_p (condition, XVECEXP (seq, 0, 0))
+ || ! single_set (XVECEXP (seq, 0, 0)))
return delay_list;
for (i = 1; i < XVECLEN (seq, 0); i++)
/* Insns we pass may not set either NEEDED or SET, so merge them for
simpler tests. */
needed.memory |= set.memory;
+ needed.unch_memory |= set.unch_memory;
IOR_HARD_REG_SET (needed.regs, set.regs);
/* This insn isn't redundant if it conflicts with an insn that either is
/* We have to assume memory is needed, but the CC isn't. */
res->memory = 1;
- res->volatil = 0;
+ res->volatil = res->unch_memory = 0;
res->cc = 0;
/* See if we have computed this value already. */
&& eligible_for_delay (insn, slots_filled, trial, flags)
&& no_labels_between_p (insn, trial))
{
+ rtx *tmp;
slots_filled++;
delay_list = add_to_delay_list (trial, delay_list);
+
+ /* TRIAL may have had its delay slot filled, then unfilled. When
+ the delay slot is unfilled, TRIAL is placed back on the unfilled
+ slots obstack. Unfortunately, it is placed on the end of the
+ obstack, not in its original location. Therefore, we must search
+ from entry i + 1 to the end of the unfilled slots obstack to
+ try and find TRIAL. */
+ tmp = &unfilled_slots_base[i + 1];
+ while (*tmp != trial && tmp != unfilled_slots_next)
+ tmp++;
+
/* Remove the unconditional jump from consideration for delay slot
- filling and unthread it. */
- if (unfilled_slots_base[i + 1] == trial)
- unfilled_slots_base[i + 1] = 0;
+ filling and unthread it. */
+ if (*tmp == trial)
+ *tmp = 0;
{
rtx next = NEXT_INSN (trial);
rtx prev = PREV_INSN (trial);
is a CALL_INSN (or a CALL_INSN is passed), cannot trap (because the
call might not return).
- If this is a conditional jump, see if it merges back to us early
- enough for us to pick up insns from the merge point. Don't do
- this if there is another branch to our label unless we pass all of
- them.
-
- Another similar merge is if we jump to the same place that a
- later unconditional jump branches to. In that case, we don't
- care about the number of uses of our label. */
+ There used to be code which continued past the target label if
+ we saw all uses of the target label. This code did not work,
+ because it failed to account for some instructions which were
+ both annulled and marked as from the target. This can happen as a
+ result of optimize_skip. Since this code was redundant with
+ fill_eager_delay_slots anyways, it was just deleted. */
if (slots_filled != slots_to_fill
&& (GET_CODE (insn) != JUMP_INSN
{
rtx target = 0;
int maybe_never = 0;
- int passed_label = 0;
- int target_uses;
struct resources needed_at_jump;
CLEAR_RESOURCE (&needed);
mark_set_resources (insn, &set, 0, 1);
mark_referenced_resources (insn, &needed, 1);
if (GET_CODE (insn) == JUMP_INSN)
- {
- /* Get our target and show how many more uses we want to
- see before we hit the label. */
- target = JUMP_LABEL (insn);
- target_uses = LABEL_NUSES (target) - 1;
- }
-
+ target = JUMP_LABEL (insn);
}
for (trial = next_nonnote_insn (insn); trial; trial = next_trial)
next_trial = next_nonnote_insn (trial);
- if (GET_CODE (trial) == CODE_LABEL)
- {
- passed_label = 1;
-
- /* If this is our target, see if we have seen all its uses.
- If so, indicate we have passed our target and ignore it.
- All other labels cause us to stop our search. */
- if (trial == target && target_uses == 0)
- {
- target = 0;
- continue;
- }
- else
- break;
- }
- else if (GET_CODE (trial) == BARRIER)
+ if (GET_CODE (trial) == CODE_LABEL
+ || GET_CODE (trial) == BARRIER)
break;
/* We must have an INSN, JUMP_INSN, or CALL_INSN. */
{
if (target == 0)
break;
- else if (JUMP_LABEL (trial_delay) == target)
- target_uses--;
- else
+ else if (JUMP_LABEL (trial_delay) != target)
{
mark_target_live_regs
(next_active_insn (JUMP_LABEL (trial_delay)),
&needed_at_jump);
needed.memory |= needed_at_jump.memory;
+ needed.unch_memory |= needed_at_jump.unch_memory;
IOR_HARD_REG_SET (needed.regs, needed_at_jump.regs);
}
}
link_cc0_insns (trial);
#endif
- if (passed_label)
- update_block (trial, trial);
delete_insn (trial);
if (slots_to_fill == ++slots_filled)
break;
set.cc = 1;
/* If this is a call or jump, we might not get here. */
- if (GET_CODE (trial) == CALL_INSN
- || GET_CODE (trial) == JUMP_INSN)
+ if (GET_CODE (trial_delay) == CALL_INSN
+ || GET_CODE (trial_delay) == JUMP_INSN)
maybe_never = 1;
}
/* If there are slots left to fill and our search was stopped by an
unconditional branch, try the insn at the branch target. We can
- redirect the branch if it works. */
+ redirect the branch if it works.
+
+ Don't do this if the insn at the branch target is a branch. */
if (slots_to_fill != slots_filled
&& trial
&& GET_CODE (trial) == JUMP_INSN
&& (next_trial = next_active_insn (JUMP_LABEL (trial))) != 0
&& ! (GET_CODE (next_trial) == INSN
&& GET_CODE (PATTERN (next_trial)) == SEQUENCE)
+ && GET_CODE (next_trial) != JUMP_INSN
&& ! insn_references_resource_p (next_trial, &set, 1)
&& ! insn_sets_resource_p (next_trial, &set, 1)
&& ! insn_sets_resource_p (next_trial, &needed, 1)
return;
slots_filled = 0;
- needed = end_of_function_needs;
CLEAR_RESOURCE (&set);
+ /* The frame pointer and stack pointer are needed at the beginning of
+ the epilogue, so instructions setting them can not be put in the
+ epilogue delay slot. However, everything else needed at function
+ end is safe, so we don't want to use end_of_function_needs here. */
+ CLEAR_RESOURCE (&needed);
+ if (frame_pointer_needed)
+ {
+ SET_HARD_REG_BIT (needed.regs, FRAME_POINTER_REGNUM);
+#if HARD_FRAME_POINTER_REGNUM != FRAME_POINTER_REGNUM
+ SET_HARD_REG_BIT (needed.regs, HARD_FRAME_POINTER_REGNUM);
+#endif
+#ifdef EXIT_IGNORE_STACK
+ if (! EXIT_IGNORE_STACK)
+#endif
+ SET_HARD_REG_BIT (needed.regs, STACK_POINTER_REGNUM);
+ }
+ else
+ SET_HARD_REG_BIT (needed.regs, STACK_POINTER_REGNUM);
+
for (trial = get_last_insn (); ! stop_search_p (trial, 1);
trial = PREV_INSN (trial))
{
if (! insn_references_resource_p (trial, &set, 1)
&& ! insn_sets_resource_p (trial, &needed, 1)
+ && ! insn_sets_resource_p (trial, &set, 1)
#ifdef HAVE_cc0
/* Don't want to mess with cc0 here. */
&& ! reg_mentioned_p (cc0_rtx, pat)
trial = try_split (pat, trial, 0);
if (new_thread == old_trial)
new_thread = trial;
+ if (thread == old_trial)
+ thread = trial;
pat = PATTERN (trial);
if ((thread_if_true
? eligible_for_annul_false (insn, *pslots_filled, trial, flags)
if (condition == 0)
continue;
- /* Get the next active fallthough and target insns and see if we own
+ /* Get the next active fallthrough and target insns and see if we own
them. Then see whether the branch is likely true. We don't need
to do a lot of this for unconditional branches. */
/* If this jump goes to another unconditional jump, thread it, but
don't convert a jump into a RETURN here. */
trial = follow_jumps (target_label);
- trial = prev_label (next_active_insn (trial));
+ /* We use next_real_insn instead of next_active_insn, so that
+ the special USE insns emitted by reorg won't be ignored.
+ If they are ignored, then they will get deleted if target_label
+ is now unreachable, and that would cause mark_target_live_regs
+ to fail. */
+ trial = prev_label (next_real_insn (trial));
if (trial == 0 && target_label != 0)
trial = find_end_label ();
if (invert_jump (delay_insn, label))
{
+ int i;
+
+ /* Must update the INSN_FROM_TARGET_P bits now that
+ the branch is reversed, so that mark_target_live_regs
+ will handle the delay slot insn correctly. */
+ for (i = 1; i < XVECLEN (PATTERN (insn), 0); i++)
+ {
+ rtx slot = XVECEXP (PATTERN (insn), 0, i);
+ INSN_FROM_TARGET_P (slot) = ! INSN_FROM_TARGET_P (slot);
+ }
+
delete_insn (next);
next = insn;
}
end_of_function_needs.cc = 0;
end_of_function_needs.memory = 1;
+ end_of_function_needs.unch_memory = 0;
CLEAR_HARD_REG_SET (end_of_function_needs.regs);
if (frame_pointer_needed)
target_hash_table
= (struct target_info **) alloca ((TARGET_HASH_PRIME
* sizeof (struct target_info *)));
- bzero (target_hash_table, TARGET_HASH_PRIME * sizeof (struct target_info *));
+ bzero ((char *) target_hash_table,
+ TARGET_HASH_PRIME * sizeof (struct target_info *));
bb_ticks = (int *) alloca (n_basic_blocks * sizeof (int));
- bzero (bb_ticks, n_basic_blocks * sizeof (int));
+ bzero ((char *) bb_ticks, n_basic_blocks * sizeof (int));
/* Initialize the statistics for this function. */
- bzero (num_insns_needing_delays, sizeof num_insns_needing_delays);
- bzero (num_filled_delays, sizeof num_filled_delays);
+ bzero ((char *) num_insns_needing_delays, sizeof num_insns_needing_delays);
+ bzero ((char *) num_filled_delays, sizeof num_filled_delays);
/* Now do the delay slot filling. Try everything twice in case earlier
changes make more slots fillable. */