#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "tm.h"
#include "toplev.h"
#include "rtl.h"
#include "tm_p.h"
#include "expr.h"
#include "function.h"
#include "insn-config.h"
#include "conditions.h"
#include "hard-reg-set.h"
#include "basic-block.h"
#include "regs.h"
#include "recog.h"
#include "flags.h"
#include "output.h"
#include "obstack.h"
#include "insn-attr.h"
#include "resource.h"
#include "except.h"
#include "params.h"
#include "timevar.h"
#include "target.h"
#include "tree-pass.h"
#ifdef DELAY_SLOTS
#ifndef ANNUL_IFTRUE_SLOTS
#define eligible_for_annul_true(INSN, SLOTS, TRIAL, FLAGS) 0
#endif
#ifndef ANNUL_IFFALSE_SLOTS
#define eligible_for_annul_false(INSN, SLOTS, TRIAL, FLAGS) 0
#endif
static struct obstack unfilled_slots_obstack;
static rtx *unfilled_firstobj;
#define unfilled_slots_base \
((rtx *) obstack_base (&unfilled_slots_obstack))
#define unfilled_slots_next \
((rtx *) obstack_next_free (&unfilled_slots_obstack))
static rtx end_of_function_label;
static int *uid_to_ruid;
static int max_uid;
static int stop_search_p (rtx, int);
static int resource_conflicts_p (struct resources *, struct resources *);
static int insn_references_resource_p (rtx, struct resources *, int);
static int insn_sets_resource_p (rtx, struct resources *, int);
static rtx find_end_label (void);
static rtx emit_delay_sequence (rtx, rtx, int);
static rtx add_to_delay_list (rtx, rtx);
static rtx delete_from_delay_slot (rtx);
static void delete_scheduled_jump (rtx);
static void note_delay_statistics (int, int);
#if defined(ANNUL_IFFALSE_SLOTS) || defined(ANNUL_IFTRUE_SLOTS)
static rtx optimize_skip (rtx);
#endif
static int get_jump_flags (rtx, rtx);
static int rare_destination (rtx);
static int mostly_true_jump (rtx, rtx);
static rtx get_branch_condition (rtx, rtx);
static int condition_dominates_p (rtx, rtx);
static int redirect_with_delay_slots_safe_p (rtx, rtx, rtx);
static int redirect_with_delay_list_safe_p (rtx, rtx, rtx);
static int check_annul_list_true_false (int, rtx);
static rtx steal_delay_list_from_target (rtx, rtx, rtx, rtx,
struct resources *,
struct resources *,
struct resources *,
int, int *, int *, rtx *);
static rtx steal_delay_list_from_fallthrough (rtx, rtx, rtx, rtx,
struct resources *,
struct resources *,
struct resources *,
int, int *, int *);
static void try_merge_delay_insns (rtx, rtx);
static rtx redundant_insn (rtx, rtx, rtx);
static int own_thread_p (rtx, rtx, int);
static void update_block (rtx, rtx);
static int reorg_redirect_jump (rtx, rtx);
static void update_reg_dead_notes (rtx, rtx);
static void fix_reg_dead_note (rtx, rtx);
static void update_reg_unused_notes (rtx, rtx);
static void fill_simple_delay_slots (int);
static rtx fill_slots_from_thread (rtx, rtx, rtx, rtx, int, int, int, int,
int *, rtx);
static void fill_eager_delay_slots (void);
static void relax_delay_slots (rtx);
#ifdef HAVE_return
static void make_return_insns (rtx);
#endif
static int
stop_search_p (rtx insn, int labels_p)
{
if (insn == 0)
return 1;
if (can_throw_internal (insn))
return 1;
switch (GET_CODE (insn))
{
case NOTE:
case CALL_INSN:
return 0;
case CODE_LABEL:
return labels_p;
case JUMP_INSN:
case BARRIER:
return 1;
case INSN:
return (GET_CODE (PATTERN (insn)) == SEQUENCE
|| GET_CODE (PATTERN (insn)) == ASM_INPUT
|| asm_noperands (PATTERN (insn)) >= 0);
default:
gcc_unreachable ();
}
}
static int
resource_conflicts_p (struct resources *res1, struct resources *res2)
{
if ((res1->cc && res2->cc) || (res1->memory && res2->memory)
|| (res1->unch_memory && res2->unch_memory)
|| res1->volatil || res2->volatil)
return 1;
#ifdef HARD_REG_SET
return (res1->regs & res2->regs) != HARD_CONST (0);
#else
{
int i;
for (i = 0; i < HARD_REG_SET_LONGS; i++)
if ((res1->regs[i] & res2->regs[i]) != 0)
return 1;
return 0;
}
#endif
}
static int
insn_references_resource_p (rtx insn, struct resources *res,
int include_delayed_effects)
{
struct resources insn_res;
CLEAR_RESOURCE (&insn_res);
mark_referenced_resources (insn, &insn_res, include_delayed_effects);
return resource_conflicts_p (&insn_res, res);
}
static int
insn_sets_resource_p (rtx insn, struct resources *res,
int include_delayed_effects)
{
struct resources insn_sets;
CLEAR_RESOURCE (&insn_sets);
mark_set_resources (insn, &insn_sets, 0, include_delayed_effects);
return resource_conflicts_p (&insn_sets, res);
}
static rtx
find_end_label (void)
{
rtx insn;
if (end_of_function_label)
return end_of_function_label;
insn = get_last_insn ();
while (NOTE_P (insn)
|| (NONJUMP_INSN_P (insn)
&& (GET_CODE (PATTERN (insn)) == USE
|| GET_CODE (PATTERN (insn)) == CLOBBER)))
insn = PREV_INSN (insn);
if (BARRIER_P (insn)
&& JUMP_P (PREV_INSN (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;
while (GET_CODE (temp) == USE)
temp = PREV_INSN (temp);
emit_label_after (end_of_function_label, temp);
}
else if (LABEL_P (insn))
end_of_function_label = insn;
else
{
end_of_function_label = gen_label_rtx ();
LABEL_NUSES (end_of_function_label) = 0;
while (insn && ! (JUMP_P (insn)
&& (GET_CODE (PATTERN (insn)) == RETURN)))
insn = PREV_INSN (insn);
if (insn)
{
insn = PREV_INSN (insn);
while (GET_CODE (insn) == USE)
insn = PREV_INSN (insn);
emit_label_after (end_of_function_label, insn);
}
else
{
#ifdef HAVE_epilogue
if (HAVE_epilogue
#ifdef HAVE_return
&& ! HAVE_return
#endif
)
{
end_of_function_label = NULL_RTX;
return end_of_function_label;
}
#endif
emit_label (end_of_function_label);
#ifdef HAVE_return
if (current_function_epilogue_delay_list == NULL
&& HAVE_return)
{
rtx insn = gen_return ();
insn = emit_jump_insn (insn);
emit_barrier ();
if (num_delay_slots (insn) > 0)
obstack_ptr_grow (&unfilled_slots_obstack, insn);
}
#endif
}
}
++LABEL_NUSES (end_of_function_label);
return end_of_function_label;
}
static rtx
emit_delay_sequence (rtx insn, rtx list, int length)
{
int i = 1;
rtx li;
int had_barrier = 0;
rtvec seqv = rtvec_alloc (length + 1);
rtx seq = gen_rtx_SEQUENCE (VOIDmode, seqv);
rtx seq_insn = make_insn_raw (seq);
rtx first = get_insns ();
rtx last = get_last_insn ();
rtx delay_insn = copy_rtx (insn);
if (NEXT_INSN (insn) && BARRIER_P (NEXT_INSN (insn)))
{
delete_related_insns (NEXT_INSN (insn));
last = get_last_insn ();
had_barrier = 1;
}
NEXT_INSN (seq_insn) = NEXT_INSN (insn);
PREV_INSN (seq_insn) = PREV_INSN (insn);
if (insn != last)
PREV_INSN (NEXT_INSN (seq_insn)) = seq_insn;
if (insn != first)
NEXT_INSN (PREV_INSN (seq_insn)) = seq_insn;
if (insn == last)
set_new_first_and_last_insn (first, seq_insn);
if (insn == first)
set_new_first_and_last_insn (seq_insn, last);
XVECEXP (seq, 0, 0) = delay_insn;
INSN_DELETED_P (delay_insn) = 0;
PREV_INSN (delay_insn) = PREV_INSN (seq_insn);
for (li = list; li; li = XEXP (li, 1), i++)
{
rtx tem = XEXP (li, 0);
rtx note, next;
INSN_DELETED_P (tem) = 0;
XVECEXP (seq, 0, i) = tem;
PREV_INSN (tem) = XVECEXP (seq, 0, i - 1);
NEXT_INSN (XVECEXP (seq, 0, i - 1)) = tem;
if (INSN_LOCATOR (tem) && !INSN_LOCATOR (seq_insn))
INSN_LOCATOR (seq_insn) = INSN_LOCATOR (tem);
INSN_LOCATOR (tem) = 0;
for (note = REG_NOTES (tem); note; note = next)
{
next = XEXP (note, 1);
switch (REG_NOTE_KIND (note))
{
case REG_DEAD:
remove_note (tem, note);
break;
case REG_LABEL:
if (LABEL_P (XEXP (note, 0)))
LABEL_NUSES (XEXP (note, 0)) ++;
break;
default:
break;
}
}
}
NEXT_INSN (XVECEXP (seq, 0, length)) = NEXT_INSN (seq_insn);
if (PREV_INSN (seq_insn) && NONJUMP_INSN_P (PREV_INSN (seq_insn))
&& GET_CODE (PATTERN (PREV_INSN (seq_insn))) == SEQUENCE)
NEXT_INSN (XVECEXP (PATTERN (PREV_INSN (seq_insn)), 0,
XVECLEN (PATTERN (PREV_INSN (seq_insn)), 0) - 1))
= seq_insn;
if (NEXT_INSN (seq_insn) && NONJUMP_INSN_P (NEXT_INSN (seq_insn))
&& GET_CODE (PATTERN (NEXT_INSN (seq_insn))) == SEQUENCE)
PREV_INSN (XVECEXP (PATTERN (NEXT_INSN (seq_insn)), 0, 0)) = seq_insn;
if (had_barrier)
emit_barrier_after (seq_insn);
gcc_assert (i == length + 1);
return seq_insn;
}
static rtx
add_to_delay_list (rtx insn, rtx delay_list)
{
if (delay_list == 0)
{
clear_hashed_info_for_insn (insn);
return gen_rtx_INSN_LIST (VOIDmode, insn, NULL_RTX);
}
XEXP (delay_list, 1) = add_to_delay_list (insn, XEXP (delay_list, 1));
return delay_list;
}
static rtx
delete_from_delay_slot (rtx insn)
{
rtx trial, seq_insn, seq, prev;
rtx delay_list = 0;
int i;
int had_barrier = 0;
for (trial = insn;
PREV_INSN (NEXT_INSN (trial)) == trial;
trial = NEXT_INSN (trial))
;
seq_insn = PREV_INSN (NEXT_INSN (trial));
seq = PATTERN (seq_insn);
if (NEXT_INSN (seq_insn) && BARRIER_P (NEXT_INSN (seq_insn)))
had_barrier = 1;
if (XVECLEN (seq, 0) > 2)
for (i = 1; i < XVECLEN (seq, 0); i++)
if (XVECEXP (seq, 0, i) != insn)
delay_list = add_to_delay_list (XVECEXP (seq, 0, i), delay_list);
prev = PREV_INSN (seq_insn);
trial = XVECEXP (seq, 0, 0);
delete_related_insns (seq_insn);
add_insn_after (trial, prev);
if (had_barrier)
emit_barrier_after (trial);
if (delay_list)
trial = emit_delay_sequence (trial, delay_list, XVECLEN (seq, 0) - 2);
else if (INSN_P (trial))
INSN_ANNULLED_BRANCH_P (trial) = 0;
INSN_FROM_TARGET_P (insn) = 0;
obstack_ptr_grow (&unfilled_slots_obstack, trial);
return trial;
}
static void
delete_scheduled_jump (rtx insn)
{
#ifdef HAVE_cc0
if (reg_mentioned_p (cc0_rtx, insn))
{
rtx note = find_reg_note (insn, REG_CC_SETTER, NULL_RTX);
if (note)
{
if (! FIND_REG_INC_NOTE (XEXP (note, 0), NULL_RTX)
&& sets_cc0_p (PATTERN (XEXP (note, 0))) == 1)
delete_from_delay_slot (XEXP (note, 0));
}
else
{
rtx trial = previous_insn (insn);
if (NOTE_P (trial))
trial = prev_nonnote_insn (trial);
if (sets_cc0_p (PATTERN (trial)) != 1
|| FIND_REG_INC_NOTE (trial, NULL_RTX))
return;
if (PREV_INSN (NEXT_INSN (trial)) == trial)
delete_related_insns (trial);
else
delete_from_delay_slot (trial);
}
}
#endif
delete_related_insns (insn);
}
#define NUM_REORG_FUNCTIONS 2
#define MAX_DELAY_HISTOGRAM 3
#define MAX_REORG_PASSES 2
static int num_insns_needing_delays[NUM_REORG_FUNCTIONS][MAX_REORG_PASSES];
static int num_filled_delays[NUM_REORG_FUNCTIONS][MAX_DELAY_HISTOGRAM+1][MAX_REORG_PASSES];
static int reorg_pass_number;
static void
note_delay_statistics (int slots_filled, int index)
{
num_insns_needing_delays[index][reorg_pass_number]++;
if (slots_filled > MAX_DELAY_HISTOGRAM)
slots_filled = MAX_DELAY_HISTOGRAM;
num_filled_delays[index][slots_filled][reorg_pass_number]++;
}
#if defined(ANNUL_IFFALSE_SLOTS) || defined(ANNUL_IFTRUE_SLOTS)
static rtx
optimize_skip (rtx insn)
{
rtx trial = next_nonnote_insn (insn);
rtx next_trial = next_active_insn (trial);
rtx delay_list = 0;
int flags;
flags = get_jump_flags (insn, JUMP_LABEL (insn));
if (trial == 0
|| !NONJUMP_INSN_P (trial)
|| GET_CODE (PATTERN (trial)) == SEQUENCE
|| recog_memoized (trial) < 0
|| (! eligible_for_annul_false (insn, 0, trial, flags)
&& ! eligible_for_annul_true (insn, 0, trial, flags))
|| can_throw_internal (trial))
return 0;
if ((next_trial == next_active_insn (JUMP_LABEL (insn))
&& ! (next_trial == 0 && current_function_epilogue_delay_list != 0))
|| (next_trial != 0
&& JUMP_P (next_trial)
&& JUMP_LABEL (insn) == JUMP_LABEL (next_trial)
&& (simplejump_p (next_trial)
|| GET_CODE (PATTERN (next_trial)) == RETURN)))
{
if (eligible_for_annul_false (insn, 0, trial, flags))
{
if (invert_jump (insn, JUMP_LABEL (insn), 1))
INSN_FROM_TARGET_P (trial) = 1;
else if (! eligible_for_annul_true (insn, 0, trial, flags))
return 0;
}
delay_list = add_to_delay_list (trial, NULL_RTX);
next_trial = next_active_insn (trial);
update_block (trial, trial);
delete_related_insns (trial);
if (next_trial && JUMP_P (next_trial)
&& (simplejump_p (next_trial)
|| GET_CODE (PATTERN (next_trial)) == RETURN))
{
rtx target_label = JUMP_LABEL (next_trial);
if (target_label == 0)
target_label = find_end_label ();
if (target_label)
{
flags = get_jump_flags (insn, target_label);
if (eligible_for_annul_true (insn, 0, trial, flags))
reorg_redirect_jump (insn, target_label);
}
}
INSN_ANNULLED_BRANCH_P (insn) = 1;
}
return delay_list;
}
#endif
static int
get_jump_flags (rtx insn, rtx label)
{
int flags;
if (JUMP_P (insn)
&& (condjump_p (insn) || condjump_in_parallel_p (insn))
&& INSN_UID (insn) <= max_uid
&& label != 0
&& INSN_UID (label) <= max_uid)
flags
= (uid_to_ruid[INSN_UID (label)] > uid_to_ruid[INSN_UID (insn)])
? ATTR_FLAG_forward : ATTR_FLAG_backward;
else
flags = 0;
if (JUMP_P (insn)
&& (condjump_p (insn) || condjump_in_parallel_p (insn)))
{
int prediction;
prediction = mostly_true_jump (insn, get_branch_condition (insn, label));
switch (prediction)
{
case 2:
flags |= (ATTR_FLAG_very_likely | ATTR_FLAG_likely);
break;
case 1:
flags |= ATTR_FLAG_likely;
break;
case 0:
flags |= ATTR_FLAG_unlikely;
break;
case -1:
flags |= (ATTR_FLAG_very_unlikely | ATTR_FLAG_unlikely);
break;
default:
gcc_unreachable ();
}
}
else
flags |= (ATTR_FLAG_very_likely | ATTR_FLAG_likely);
return flags;
}
static int
rare_destination (rtx insn)
{
int jump_count = 0;
rtx next;
for (; insn; insn = next)
{
if (NONJUMP_INSN_P (insn) && GET_CODE (PATTERN (insn)) == SEQUENCE)
insn = XVECEXP (PATTERN (insn), 0, 0);
next = NEXT_INSN (insn);
switch (GET_CODE (insn))
{
case CODE_LABEL:
return 0;
case BARRIER:
return 2;
case JUMP_INSN:
if (GET_CODE (PATTERN (insn)) == RETURN)
return 1;
else if (simplejump_p (insn)
&& jump_count++ < 10)
next = JUMP_LABEL (insn);
else
return 0;
default:
break;
}
}
return 1;
}
static int
mostly_true_jump (rtx jump_insn, rtx condition)
{
rtx target_label = JUMP_LABEL (jump_insn);
rtx note;
int rare_dest, rare_fallthrough;
note = find_reg_note (jump_insn, REG_BR_PROB, 0);
if (note)
{
int prob = INTVAL (XEXP (note, 0));
if (prob >= REG_BR_PROB_BASE * 9 / 10)
return 2;
else if (prob >= REG_BR_PROB_BASE / 2)
return 1;
else if (prob >= REG_BR_PROB_BASE / 10)
return 0;
else
return -1;
}
rare_dest = rare_destination (target_label);
rare_fallthrough = rare_destination (NEXT_INSN (jump_insn));
switch (rare_fallthrough - rare_dest)
{
case -2:
return -1;
case -1:
return 0;
case 0:
break;
case 1:
return 1;
case 2:
return 2;
}
if (condition == 0)
return 0;
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)]));
}
static rtx
get_branch_condition (rtx insn, rtx target)
{
rtx pat = PATTERN (insn);
rtx src;
if (condjump_in_parallel_p (insn))
pat = XVECEXP (pat, 0, 0);
if (GET_CODE (pat) == RETURN)
return target == 0 ? const_true_rtx : 0;
else if (GET_CODE (pat) != SET || SET_DEST (pat) != pc_rtx)
return 0;
src = SET_SRC (pat);
if (GET_CODE (src) == LABEL_REF && XEXP (src, 0) == target)
return const_true_rtx;
else if (GET_CODE (src) == IF_THEN_ELSE
&& ((target == 0 && GET_CODE (XEXP (src, 1)) == RETURN)
|| (GET_CODE (XEXP (src, 1)) == LABEL_REF
&& XEXP (XEXP (src, 1), 0) == target))
&& XEXP (src, 2) == pc_rtx)
return XEXP (src, 0);
else if (GET_CODE (src) == IF_THEN_ELSE
&& ((target == 0 && GET_CODE (XEXP (src, 2)) == RETURN)
|| (GET_CODE (XEXP (src, 2)) == LABEL_REF
&& XEXP (XEXP (src, 2), 0) == target))
&& XEXP (src, 1) == pc_rtx)
{
enum rtx_code rev;
rev = reversed_comparison_code (XEXP (src, 0), insn);
if (rev != UNKNOWN)
return gen_rtx_fmt_ee (rev, GET_MODE (XEXP (src, 0)),
XEXP (XEXP (src, 0), 0),
XEXP (XEXP (src, 0), 1));
}
return 0;
}
static int
condition_dominates_p (rtx condition, rtx insn)
{
rtx other_condition = get_branch_condition (insn, JUMP_LABEL (insn));
enum rtx_code code = GET_CODE (condition);
enum rtx_code other_code;
if (rtx_equal_p (condition, other_condition)
|| other_condition == const_true_rtx)
return 1;
else if (condition == const_true_rtx || other_condition == 0)
return 0;
other_code = GET_CODE (other_condition);
if (GET_RTX_LENGTH (code) != 2 || GET_RTX_LENGTH (other_code) != 2
|| ! rtx_equal_p (XEXP (condition, 0), XEXP (other_condition, 0))
|| ! rtx_equal_p (XEXP (condition, 1), XEXP (other_condition, 1)))
return 0;
return comparison_dominates_p (code, other_code);
}
static int
redirect_with_delay_slots_safe_p (rtx jump, rtx newlabel, rtx seq)
{
int flags, i;
rtx pat = PATTERN (seq);
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));
}
static int
redirect_with_delay_list_safe_p (rtx jump, rtx newlabel, rtx delay_list)
{
int flags, i;
rtx li;
flags = get_jump_flags (jump, newlabel);
for (li = delay_list, i = 0; li; li = XEXP (li, 1), i++)
if (! (
#ifdef ANNUL_IFFALSE_SLOTS
(INSN_ANNULLED_BRANCH_P (jump)
&& INSN_FROM_TARGET_P (XEXP (li, 0)))
? eligible_for_annul_false (jump, i, XEXP (li, 0), flags) :
#endif
#ifdef ANNUL_IFTRUE_SLOTS
(INSN_ANNULLED_BRANCH_P (jump)
&& ! INSN_FROM_TARGET_P (XEXP (li, 0)))
? eligible_for_annul_true (jump, i, XEXP (li, 0), flags) :
#endif
eligible_for_delay (jump, i, XEXP (li, 0), flags)))
break;
return (li == NULL);
}
static int
check_annul_list_true_false (int annul_true_p, rtx delay_list)
{
rtx temp;
if (delay_list)
{
for (temp = delay_list; temp; temp = XEXP (temp, 1))
{
rtx trial = XEXP (temp, 0);
if ((annul_true_p && INSN_FROM_TARGET_P (trial))
|| (!annul_true_p && !INSN_FROM_TARGET_P (trial)))
return 0;
}
}
return 1;
}
static rtx
steal_delay_list_from_target (rtx insn, rtx condition, rtx seq,
rtx delay_list, struct resources *sets,
struct resources *needed,
struct resources *other_needed,
int slots_to_fill, int *pslots_filled,
int *pannul_p, rtx *pnew_thread)
{
rtx temp;
int slots_remaining = slots_to_fill - *pslots_filled;
int total_slots_filled = *pslots_filled;
rtx new_delay_list = 0;
int must_annul = *pannul_p;
int used_annul = 0;
int i;
struct resources cc_set;
CLEAR_RESOURCE (&cc_set);
for (temp = delay_list; temp; temp = XEXP (temp, 1))
{
rtx trial = XEXP (temp, 0);
mark_set_resources (trial, &cc_set, 0, MARK_SRC_DEST_CALL);
if (insn_references_resource_p (XVECEXP (seq , 0, 0), &cc_set, 0))
return delay_list;
}
if (XVECLEN (seq, 0) - 1 > slots_remaining
|| ! condition_dominates_p (condition, XVECEXP (seq, 0, 0))
|| ! single_set (XVECEXP (seq, 0, 0)))
return delay_list;
#ifdef MD_CAN_REDIRECT_BRANCH
if (! MD_CAN_REDIRECT_BRANCH (insn, XVECEXP (seq, 0, 0)))
return delay_list;
#endif
for (i = 1; i < XVECLEN (seq, 0); i++)
{
rtx trial = XVECEXP (seq, 0, i);
int flags;
if (insn_references_resource_p (trial, sets, 0)
|| insn_sets_resource_p (trial, needed, 0)
|| insn_sets_resource_p (trial, sets, 0)
#ifdef HAVE_cc0
|| find_reg_note (trial, REG_CC_USER, NULL_RTX)
#endif
|| (INSN_ANNULLED_BRANCH_P (XVECEXP (seq, 0, 0))
&& ! INSN_FROM_TARGET_P (trial)))
return delay_list;
if (redundant_insn (trial, insn, new_delay_list))
continue;
flags = get_jump_flags (insn, JUMP_LABEL (XVECEXP (seq, 0, 0)));
if (! must_annul
&& ((condition == const_true_rtx
|| (! insn_sets_resource_p (trial, other_needed, 0)
&& ! may_trap_or_fault_p (PATTERN (trial)))))
? eligible_for_delay (insn, total_slots_filled, trial, flags)
: (must_annul || (delay_list == NULL && new_delay_list == NULL))
&& (must_annul = 1,
check_annul_list_true_false (0, delay_list)
&& check_annul_list_true_false (0, new_delay_list)
&& eligible_for_annul_false (insn, total_slots_filled,
trial, flags)))
{
if (must_annul)
used_annul = 1;
temp = copy_rtx (trial);
INSN_FROM_TARGET_P (temp) = 1;
new_delay_list = add_to_delay_list (temp, new_delay_list);
total_slots_filled++;
if (--slots_remaining == 0)
break;
}
else
return delay_list;
}
*pnew_thread = next_active_insn (JUMP_LABEL (XVECEXP (seq, 0, 0)));
*pslots_filled = total_slots_filled;
if (used_annul)
*pannul_p = 1;
if (delay_list == 0)
return new_delay_list;
for (temp = new_delay_list; temp; temp = XEXP (temp, 1))
delay_list = add_to_delay_list (XEXP (temp, 0), delay_list);
return delay_list;
}
static rtx
steal_delay_list_from_fallthrough (rtx insn, rtx condition, rtx seq,
rtx delay_list, struct resources *sets,
struct resources *needed,
struct resources *other_needed,
int slots_to_fill, int *pslots_filled,
int *pannul_p)
{
int i;
int flags;
int must_annul = *pannul_p;
int used_annul = 0;
flags = get_jump_flags (insn, JUMP_LABEL (insn));
if (! simplejump_p (XVECEXP (seq, 0, 0))
&& GET_CODE (PATTERN (XVECEXP (seq, 0, 0))) != RETURN)
return delay_list;
for (i = 1; i < XVECLEN (seq, 0); i++)
{
rtx trial = XVECEXP (seq, 0, i);
if (insn_references_resource_p (trial, sets, 0)
|| insn_sets_resource_p (trial, needed, 0)
|| insn_sets_resource_p (trial, sets, 0)
#ifdef HAVE_cc0
|| sets_cc0_p (PATTERN (trial))
#endif
)
break;
if (redundant_insn (trial, insn, delay_list))
{
delete_from_delay_slot (trial);
continue;
}
if (! must_annul
&& ((condition == const_true_rtx
|| (! insn_sets_resource_p (trial, other_needed, 0)
&& ! may_trap_or_fault_p (PATTERN (trial)))))
? eligible_for_delay (insn, *pslots_filled, trial, flags)
: (must_annul || delay_list == NULL) && (must_annul = 1,
check_annul_list_true_false (1, delay_list)
&& eligible_for_annul_true (insn, *pslots_filled, trial, flags)))
{
if (must_annul)
used_annul = 1;
delete_from_delay_slot (trial);
delay_list = add_to_delay_list (trial, delay_list);
if (++(*pslots_filled) == slots_to_fill)
break;
}
else
break;
}
if (used_annul)
*pannul_p = 1;
return delay_list;
}
static void
try_merge_delay_insns (rtx insn, rtx thread)
{
rtx trial, next_trial;
rtx delay_insn = XVECEXP (PATTERN (insn), 0, 0);
int annul_p = INSN_ANNULLED_BRANCH_P (delay_insn);
int slot_number = 1;
int num_slots = XVECLEN (PATTERN (insn), 0);
rtx next_to_match = XVECEXP (PATTERN (insn), 0, slot_number);
struct resources set, needed;
rtx merged_insns = 0;
int i;
int flags;
flags = get_jump_flags (delay_insn, JUMP_LABEL (delay_insn));
CLEAR_RESOURCE (&needed);
CLEAR_RESOURCE (&set);
if (! annul_p)
for (i = 1 ; i < num_slots; i++)
if (XVECEXP (PATTERN (insn), 0, i))
mark_referenced_resources (XVECEXP (PATTERN (insn), 0, i), &needed, 1);
for (trial = thread; !stop_search_p (trial, 1); trial = next_trial)
{
rtx pat = PATTERN (trial);
rtx oldtrial = trial;
next_trial = next_nonnote_insn (trial);
if (NONJUMP_INSN_P (trial)
&& (GET_CODE (pat) == USE || GET_CODE (pat) == CLOBBER))
continue;
if (GET_CODE (next_to_match) == GET_CODE (trial)
#ifdef HAVE_cc0
&& ! sets_cc0_p (pat)
#endif
&& ! insn_references_resource_p (trial, &set, 1)
&& ! insn_sets_resource_p (trial, &set, 1)
&& ! insn_sets_resource_p (trial, &needed, 1)
&& (trial = try_split (pat, trial, 0)) != 0
&& (next_trial = next_nonnote_insn (trial))
&& (thread = oldtrial == thread ? trial : thread)
&& rtx_equal_p (PATTERN (next_to_match), PATTERN (trial))
&& eligible_for_delay (delay_insn, slot_number - 1, trial, flags))
{
if (! annul_p)
{
update_block (trial, thread);
if (trial == thread)
thread = next_active_insn (thread);
delete_related_insns (trial);
INSN_FROM_TARGET_P (next_to_match) = 0;
}
else
merged_insns = gen_rtx_INSN_LIST (VOIDmode, trial, merged_insns);
if (++slot_number == num_slots)
break;
next_to_match = XVECEXP (PATTERN (insn), 0, slot_number);
}
mark_set_resources (trial, &set, 0, MARK_SRC_DEST_CALL);
mark_referenced_resources (trial, &needed, 1);
}
if (slot_number != num_slots
&& trial && NONJUMP_INSN_P (trial)
&& GET_CODE (PATTERN (trial)) == SEQUENCE
&& ! INSN_ANNULLED_BRANCH_P (XVECEXP (PATTERN (trial), 0, 0)))
{
rtx pat = PATTERN (trial);
rtx filled_insn = XVECEXP (pat, 0, 0);
mark_set_resources (filled_insn, &set, 0, MARK_SRC_DEST_CALL);
mark_referenced_resources (filled_insn, &needed, 1);
for (i = 1; i < XVECLEN (pat, 0); i++)
{
rtx dtrial = XVECEXP (pat, 0, i);
if (! insn_references_resource_p (dtrial, &set, 1)
&& ! insn_sets_resource_p (dtrial, &set, 1)
&& ! insn_sets_resource_p (dtrial, &needed, 1)
#ifdef HAVE_cc0
&& ! sets_cc0_p (PATTERN (dtrial))
#endif
&& rtx_equal_p (PATTERN (next_to_match), PATTERN (dtrial))
&& eligible_for_delay (delay_insn, slot_number - 1, dtrial, flags))
{
if (! annul_p)
{
rtx new;
update_block (dtrial, thread);
new = delete_from_delay_slot (dtrial);
if (INSN_DELETED_P (thread))
thread = new;
INSN_FROM_TARGET_P (next_to_match) = 0;
}
else
merged_insns = gen_rtx_INSN_LIST (SImode, dtrial,
merged_insns);
if (++slot_number == num_slots)
break;
next_to_match = XVECEXP (PATTERN (insn), 0, slot_number);
}
else
{
mark_set_resources (dtrial, &set, 0, MARK_SRC_DEST_CALL);
mark_referenced_resources (dtrial, &needed, 1);
}
}
}
if (slot_number == num_slots && annul_p)
{
for (; merged_insns; merged_insns = XEXP (merged_insns, 1))
{
if (GET_MODE (merged_insns) == SImode)
{
rtx new;
update_block (XEXP (merged_insns, 0), thread);
new = delete_from_delay_slot (XEXP (merged_insns, 0));
if (INSN_DELETED_P (thread))
thread = new;
}
else
{
update_block (XEXP (merged_insns, 0), thread);
delete_related_insns (XEXP (merged_insns, 0));
}
}
INSN_ANNULLED_BRANCH_P (delay_insn) = 0;
for (i = 0; i < XVECLEN (PATTERN (insn), 0); i++)
INSN_FROM_TARGET_P (XVECEXP (PATTERN (insn), 0, i)) = 0;
}
}
static rtx
redundant_insn (rtx insn, rtx target, rtx delay_list)
{
rtx target_main = target;
rtx ipat = PATTERN (insn);
rtx trial, pat;
struct resources needed, set;
int i;
unsigned insns_to_search;
if (find_reg_note (insn, REG_UNUSED, NULL_RTX) != 0)
return 0;
for (trial = PREV_INSN (target),
insns_to_search = MAX_DELAY_SLOT_INSN_SEARCH;
trial && insns_to_search > 0;
trial = PREV_INSN (trial), --insns_to_search)
{
if (LABEL_P (trial))
return 0;
if (! INSN_P (trial))
continue;
pat = PATTERN (trial);
if (GET_CODE (pat) == USE || GET_CODE (pat) == CLOBBER)
continue;
if (GET_CODE (pat) == SEQUENCE)
{
if (CALL_P (XVECEXP (pat, 0, 0)))
return 0;
#ifdef INSN_SETS_ARE_DELAYED
if (INSN_SETS_ARE_DELAYED (XVECEXP (pat, 0, 0)))
return 0;
#endif
#ifdef INSN_REFERENCES_ARE_DELAYED
if (INSN_REFERENCES_ARE_DELAYED (XVECEXP (pat, 0, 0)))
return 0;
#endif
for (i = XVECLEN (pat, 0) - 1; i > 0; i--)
if (GET_CODE (XVECEXP (pat, 0, i)) == GET_CODE (insn)
&& rtx_equal_p (PATTERN (XVECEXP (pat, 0, i)), ipat)
&& ! find_reg_note (XVECEXP (pat, 0, i), REG_UNUSED, NULL_RTX))
break;
if (i > 0)
break;
}
else if (GET_CODE (trial) == GET_CODE (insn) && rtx_equal_p (pat, ipat)
&& ! find_reg_note (trial, REG_UNUSED, NULL_RTX))
break;
}
if (trial == 0)
return 0;
CLEAR_RESOURCE (&needed);
CLEAR_RESOURCE (&set);
mark_set_resources (insn, &set, 0, MARK_SRC_DEST_CALL);
mark_referenced_resources (insn, &needed, 1);
if (NONJUMP_INSN_P (target) && GET_CODE (PATTERN (target)) == SEQUENCE)
target_main = XVECEXP (PATTERN (target), 0, 0);
if (resource_conflicts_p (&needed, &set)
#ifdef HAVE_cc0
|| reg_mentioned_p (cc0_rtx, ipat)
#endif
|| insn_sets_resource_p (target_main, &needed, 1)
|| insn_sets_resource_p (target_main, &set, 1))
return 0;
needed.memory |= set.memory;
needed.unch_memory |= set.unch_memory;
IOR_HARD_REG_SET (needed.regs, set.regs);
while (delay_list)
{
if (insn_sets_resource_p (XEXP (delay_list, 0), &needed, 1))
return 0;
delay_list = XEXP (delay_list, 1);
}
if (NONJUMP_INSN_P (target) && GET_CODE (PATTERN (target)) == SEQUENCE)
for (i = 1; i < XVECLEN (PATTERN (target), 0); i++)
if (insn_sets_resource_p (XVECEXP (PATTERN (target), 0, i), &needed, 1))
return 0;
for (trial = PREV_INSN (target),
insns_to_search = MAX_DELAY_SLOT_INSN_SEARCH;
trial && !LABEL_P (trial) && insns_to_search > 0;
trial = PREV_INSN (trial), --insns_to_search)
{
if (!INSN_P (trial))
continue;
pat = PATTERN (trial);
if (GET_CODE (pat) == USE || GET_CODE (pat) == CLOBBER)
continue;
if (GET_CODE (pat) == SEQUENCE)
{
if (CALL_P (XVECEXP (pat, 0, 0)))
return 0;
#ifdef INSN_SETS_ARE_DELAYED
if (INSN_SETS_ARE_DELAYED (XVECEXP (pat, 0, 0)))
return 0;
#endif
#ifdef INSN_REFERENCES_ARE_DELAYED
if (INSN_REFERENCES_ARE_DELAYED (XVECEXP (pat, 0, 0)))
return 0;
#endif
for (i = XVECLEN (pat, 0) - 1; i > 0; i--)
{
rtx candidate = XVECEXP (pat, 0, i);
if (rtx_equal_p (PATTERN (candidate), ipat)
&& ! (INSN_ANNULLED_BRANCH_P (XVECEXP (pat, 0, 0))
&& INSN_FROM_TARGET_P (candidate)))
{
INSN_FROM_TARGET_P (candidate) = 0;
return candidate;
}
if ((! INSN_ANNULLED_BRANCH_P (XVECEXP (pat, 0, 0))
|| ! INSN_FROM_TARGET_P (candidate))
&& insn_sets_resource_p (candidate, &needed, 1))
return 0;
}
if (insn_sets_resource_p (XVECEXP (pat, 0, 0), &needed, 1))
return 0;
}
else
{
pat = PATTERN (trial);
if (rtx_equal_p (pat, ipat))
return trial;
if (insn_sets_resource_p (trial, &needed, 1))
return 0;
}
}
return 0;
}
static int
own_thread_p (rtx thread, rtx label, int allow_fallthrough)
{
rtx active_insn;
rtx insn;
if (thread == 0)
return 0;
active_insn = next_active_insn (PREV_INSN (thread));
for (insn = thread; insn != active_insn; insn = NEXT_INSN (insn))
if (LABEL_P (insn)
&& (insn != label || LABEL_NUSES (insn) != 1))
return 0;
if (allow_fallthrough)
return 1;
for (insn = prev_nonnote_insn (thread);
insn == 0 || !BARRIER_P (insn);
insn = prev_nonnote_insn (insn))
if (insn == 0
|| LABEL_P (insn)
|| (NONJUMP_INSN_P (insn)
&& GET_CODE (PATTERN (insn)) != USE
&& GET_CODE (PATTERN (insn)) != CLOBBER))
return 0;
return 1;
}
static void
update_block (rtx insn, rtx where)
{
if (INSN_FROM_TARGET_P (insn))
return;
emit_insn_before (gen_rtx_USE (VOIDmode, insn), where);
incr_ticks_for_insn (insn);
}
static int
reorg_redirect_jump (rtx jump, rtx nlabel)
{
incr_ticks_for_insn (jump);
return redirect_jump (jump, nlabel, 1);
}
static void
update_reg_dead_notes (rtx insn, rtx delayed_insn)
{
rtx p, link, next;
for (p = next_nonnote_insn (insn); p != delayed_insn;
p = next_nonnote_insn (p))
for (link = REG_NOTES (p); link; link = next)
{
next = XEXP (link, 1);
if (REG_NOTE_KIND (link) != REG_DEAD
|| !REG_P (XEXP (link, 0)))
continue;
if (reg_referenced_p (XEXP (link, 0), PATTERN (insn)))
{
remove_note (p, link);
XEXP (link, 1) = REG_NOTES (insn);
REG_NOTES (insn) = link;
}
}
}
static void
fix_reg_dead_note (rtx start_insn, rtx stop_insn)
{
rtx p, link, next;
for (p = next_nonnote_insn (start_insn); p != stop_insn;
p = next_nonnote_insn (p))
for (link = REG_NOTES (p); link; link = next)
{
next = XEXP (link, 1);
if (REG_NOTE_KIND (link) != REG_DEAD
|| !REG_P (XEXP (link, 0)))
continue;
if (reg_set_p (XEXP (link, 0), PATTERN (start_insn)))
{
remove_note (p, link);
return;
}
}
}
static void
update_reg_unused_notes (rtx insn, rtx redundant_insn)
{
rtx link, next;
for (link = REG_NOTES (insn); link; link = next)
{
next = XEXP (link, 1);
if (REG_NOTE_KIND (link) != REG_UNUSED
|| !REG_P (XEXP (link, 0)))
continue;
if (! find_regno_note (redundant_insn, REG_UNUSED,
REGNO (XEXP (link, 0))))
remove_note (insn, link);
}
}
static void
fill_simple_delay_slots (int non_jumps_p)
{
rtx insn, pat, trial, next_trial;
int i;
int num_unfilled_slots = unfilled_slots_next - unfilled_slots_base;
struct resources needed, set;
int slots_to_fill, slots_filled;
rtx delay_list;
for (i = 0; i < num_unfilled_slots; i++)
{
int flags;
insn = unfilled_slots_base[i];
if (insn == 0
|| INSN_DELETED_P (insn)
|| (NONJUMP_INSN_P (insn)
&& GET_CODE (PATTERN (insn)) == SEQUENCE)
|| (JUMP_P (insn) && non_jumps_p)
|| (!JUMP_P (insn) && ! non_jumps_p))
continue;
slots_to_fill = num_delay_slots (insn);
if (slots_to_fill == 0)
continue;
slots_filled = 0;
delay_list = 0;
if (JUMP_P (insn))
flags = get_jump_flags (insn, JUMP_LABEL (insn));
else
flags = get_jump_flags (insn, NULL_RTX);
if ((trial = next_active_insn (insn))
&& JUMP_P (trial)
&& simplejump_p (trial)
&& eligible_for_delay (insn, slots_filled, trial, flags)
&& no_labels_between_p (insn, trial)
&& ! can_throw_internal (trial))
{
rtx *tmp;
slots_filled++;
delay_list = add_to_delay_list (trial, delay_list);
tmp = &unfilled_slots_base[i + 1];
while (*tmp != trial && tmp != unfilled_slots_next)
tmp++;
if (*tmp == trial)
*tmp = 0;
{
rtx next = NEXT_INSN (trial);
rtx prev = PREV_INSN (trial);
if (prev)
NEXT_INSN (prev) = next;
if (next)
PREV_INSN (next) = prev;
}
}
if (slots_filled < slots_to_fill)
{
CLEAR_RESOURCE (&needed);
CLEAR_RESOURCE (&set);
mark_set_resources (insn, &set, 0, MARK_SRC_DEST);
mark_referenced_resources (insn, &needed, 0);
for (trial = prev_nonnote_insn (insn); ! stop_search_p (trial, 1);
trial = next_trial)
{
next_trial = prev_nonnote_insn (trial);
pat = PATTERN (trial);
if (GET_CODE (pat) == USE || GET_CODE (pat) == CLOBBER)
continue;
if (! insn_references_resource_p (trial, &set, 1)
&& ! insn_sets_resource_p (trial, &set, 1)
&& ! insn_sets_resource_p (trial, &needed, 1)
#ifdef HAVE_cc0
&& ! (reg_mentioned_p (cc0_rtx, pat) && ! sets_cc0_p (pat))
#endif
&& ! can_throw_internal (trial))
{
trial = try_split (pat, trial, 1);
next_trial = prev_nonnote_insn (trial);
if (eligible_for_delay (insn, slots_filled, trial, flags))
{
update_reg_dead_notes (trial, insn);
delay_list = gen_rtx_INSN_LIST (VOIDmode,
trial, delay_list);
update_block (trial, trial);
delete_related_insns (trial);
if (slots_to_fill == ++slots_filled)
break;
continue;
}
}
mark_set_resources (trial, &set, 0, MARK_SRC_DEST_CALL);
mark_referenced_resources (trial, &needed, 1);
}
}
#if defined(ANNUL_IFFALSE_SLOTS) || defined(ANNUL_IFTRUE_SLOTS)
if (slots_filled != slots_to_fill
&& delay_list == 0
&& JUMP_P (insn)
&& (condjump_p (insn) || condjump_in_parallel_p (insn)))
{
delay_list = optimize_skip (insn);
if (delay_list)
slots_filled += 1;
}
#endif
if (slots_filled != slots_to_fill
&& ! can_throw_internal (insn)
&& (!JUMP_P (insn)
|| ((condjump_p (insn) || condjump_in_parallel_p (insn))
&& ! simplejump_p (insn)
&& JUMP_LABEL (insn) != 0)))
{
rtx target = 0;
int maybe_never = 0;
rtx pat, trial_delay;
CLEAR_RESOURCE (&needed);
CLEAR_RESOURCE (&set);
if (CALL_P (insn))
{
mark_set_resources (insn, &set, 0, MARK_SRC_DEST_CALL);
mark_referenced_resources (insn, &needed, 1);
maybe_never = 1;
}
else
{
mark_set_resources (insn, &set, 0, MARK_SRC_DEST_CALL);
mark_referenced_resources (insn, &needed, 1);
if (JUMP_P (insn))
target = JUMP_LABEL (insn);
}
if (target == 0)
for (trial = next_nonnote_insn (insn); trial; trial = next_trial)
{
next_trial = next_nonnote_insn (trial);
if (LABEL_P (trial)
|| BARRIER_P (trial))
break;
pat = PATTERN (trial);
if (GET_CODE (pat) == USE || GET_CODE (pat) == CLOBBER)
continue;
if (GET_CODE (pat) == SEQUENCE)
trial_delay = XVECEXP (pat, 0, 0);
else
trial_delay = trial;
if (JUMP_P (trial_delay))
break;
if (GET_CODE (pat) != SEQUENCE
&& ! insn_references_resource_p (trial, &set, 1)
&& ! insn_sets_resource_p (trial, &set, 1)
&& ! insn_sets_resource_p (trial, &needed, 1)
#ifdef HAVE_cc0
&& ! (reg_mentioned_p (cc0_rtx, pat) && ! sets_cc0_p (pat))
#endif
&& ! (maybe_never && may_trap_or_fault_p (pat))
&& (trial = try_split (pat, trial, 0))
&& eligible_for_delay (insn, slots_filled, trial, flags)
&& ! can_throw_internal(trial))
{
next_trial = next_nonnote_insn (trial);
delay_list = add_to_delay_list (trial, delay_list);
#ifdef HAVE_cc0
if (reg_mentioned_p (cc0_rtx, pat))
link_cc0_insns (trial);
#endif
delete_related_insns (trial);
if (slots_to_fill == ++slots_filled)
break;
continue;
}
mark_set_resources (trial, &set, 0, MARK_SRC_DEST_CALL);
mark_referenced_resources (trial, &needed, 1);
set.cc = 1;
if (CALL_P (trial_delay)
|| JUMP_P (trial_delay))
maybe_never = 1;
}
if (slots_to_fill != slots_filled
&& trial
&& JUMP_P (trial)
&& simplejump_p (trial)
&& (target == 0 || JUMP_LABEL (trial) == target)
&& (next_trial = next_active_insn (JUMP_LABEL (trial))) != 0
&& ! (NONJUMP_INSN_P (next_trial)
&& GET_CODE (PATTERN (next_trial)) == SEQUENCE)
&& !JUMP_P (next_trial)
&& ! insn_references_resource_p (next_trial, &set, 1)
&& ! insn_sets_resource_p (next_trial, &set, 1)
&& ! insn_sets_resource_p (next_trial, &needed, 1)
#ifdef HAVE_cc0
&& ! reg_mentioned_p (cc0_rtx, PATTERN (next_trial))
#endif
&& ! (maybe_never && may_trap_or_fault_p (PATTERN (next_trial)))
&& (next_trial = try_split (PATTERN (next_trial), next_trial, 0))
&& eligible_for_delay (insn, slots_filled, next_trial, flags)
&& ! can_throw_internal (trial))
{
rtx new_label = next_real_insn (next_trial);
if (new_label != 0)
new_label = get_label_before (new_label);
else
new_label = find_end_label ();
if (new_label)
{
delay_list
= add_to_delay_list (copy_rtx (next_trial), delay_list);
slots_filled++;
reorg_redirect_jump (trial, new_label);
if (target)
reorg_redirect_jump (insn, new_label);
}
}
}
if (JUMP_P (insn)
&& simplejump_p (insn)
&& slots_filled != slots_to_fill)
delay_list
= fill_slots_from_thread (insn, const_true_rtx,
next_active_insn (JUMP_LABEL (insn)),
NULL, 1, 1,
own_thread_p (JUMP_LABEL (insn),
JUMP_LABEL (insn), 0),
slots_to_fill, &slots_filled,
delay_list);
if (delay_list)
unfilled_slots_base[i]
= emit_delay_sequence (insn, delay_list, slots_filled);
if (slots_to_fill == slots_filled)
unfilled_slots_base[i] = 0;
note_delay_statistics (slots_filled, 0);
}
#ifdef DELAY_SLOTS_FOR_EPILOGUE
if (current_function_epilogue_delay_list)
return;
slots_to_fill = DELAY_SLOTS_FOR_EPILOGUE;
if (slots_to_fill == 0)
return;
slots_filled = 0;
CLEAR_RESOURCE (&set);
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
if (! EXIT_IGNORE_STACK
|| current_function_sp_is_unchanging)
SET_HARD_REG_BIT (needed.regs, STACK_POINTER_REGNUM);
}
else
SET_HARD_REG_BIT (needed.regs, STACK_POINTER_REGNUM);
#ifdef EPILOGUE_USES
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
{
if (EPILOGUE_USES (i))
SET_HARD_REG_BIT (needed.regs, i);
}
#endif
for (trial = get_last_insn (); ! stop_search_p (trial, 1);
trial = PREV_INSN (trial))
{
if (NOTE_P (trial))
continue;
pat = PATTERN (trial);
if (GET_CODE (pat) == USE || GET_CODE (pat) == CLOBBER)
continue;
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
&& ! reg_mentioned_p (cc0_rtx, pat)
#endif
&& ! can_throw_internal (trial))
{
trial = try_split (pat, trial, 1);
if (ELIGIBLE_FOR_EPILOGUE_DELAY (trial, slots_filled))
{
current_function_epilogue_delay_list
= gen_rtx_INSN_LIST (VOIDmode, trial,
current_function_epilogue_delay_list);
mark_end_of_function_resources (trial, 1);
update_block (trial, trial);
delete_related_insns (trial);
INSN_DELETED_P (trial) = 0;
if (slots_to_fill == ++slots_filled)
break;
continue;
}
}
mark_set_resources (trial, &set, 0, MARK_SRC_DEST_CALL);
mark_referenced_resources (trial, &needed, 1);
}
note_delay_statistics (slots_filled, 0);
#endif
}
static rtx
fill_slots_from_thread (rtx insn, rtx condition, rtx thread,
rtx opposite_thread, int likely, int thread_if_true,
int own_thread, int slots_to_fill,
int *pslots_filled, rtx delay_list)
{
rtx new_thread;
struct resources opposite_needed, set, needed;
rtx trial;
int lose = 0;
int must_annul = 0;
int flags;
gcc_assert(condition != const_true_rtx || thread_if_true);
gcc_assert(own_thread || thread_if_true);
flags = get_jump_flags (insn, JUMP_LABEL (insn));
if (thread == 0)
return delay_list;
if (condition == const_true_rtx)
CLEAR_RESOURCE (&opposite_needed);
else
mark_target_live_regs (get_insns (), opposite_thread, &opposite_needed);
new_thread = thread = try_split (PATTERN (thread), thread, 0);
CLEAR_RESOURCE (&needed);
CLEAR_RESOURCE (&set);
for (trial = thread;
! stop_search_p (trial, ! thread_if_true) && (! lose || own_thread);
trial = next_nonnote_insn (trial))
{
rtx pat, old_trial;
if (LABEL_P (trial))
{
own_thread = 0;
continue;
}
pat = PATTERN (trial);
if (GET_CODE (pat) == USE || GET_CODE (pat) == CLOBBER)
continue;
if (! insn_references_resource_p (trial, &set, 1)
&& ! insn_sets_resource_p (trial, &set, 1)
&& ! insn_sets_resource_p (trial, &needed, 1)
#ifdef HAVE_cc0
&& ! (reg_mentioned_p (cc0_rtx, pat)
&& (! own_thread || ! sets_cc0_p (pat)))
#endif
&& ! can_throw_internal (trial))
{
rtx prior_insn;
if ((prior_insn = redundant_insn (trial, insn, delay_list)))
{
fix_reg_dead_note (prior_insn, insn);
if (own_thread)
{
update_block (trial, thread);
if (trial == thread)
{
thread = next_active_insn (thread);
if (new_thread == trial)
new_thread = thread;
}
delete_related_insns (trial);
}
else
{
update_reg_unused_notes (prior_insn, trial);
new_thread = next_active_insn (trial);
}
continue;
}
if (!must_annul
&& (condition == const_true_rtx
|| (! insn_sets_resource_p (trial, &opposite_needed, 1)
&& ! may_trap_or_fault_p (pat))))
{
old_trial = trial;
trial = try_split (pat, trial, 0);
if (new_thread == old_trial)
new_thread = trial;
if (thread == old_trial)
thread = trial;
pat = PATTERN (trial);
if (eligible_for_delay (insn, *pslots_filled, trial, flags))
goto winner;
}
else if (0
#ifdef ANNUL_IFTRUE_SLOTS
|| ! thread_if_true
#endif
#ifdef ANNUL_IFFALSE_SLOTS
|| thread_if_true
#endif
)
{
old_trial = trial;
trial = try_split (pat, trial, 0);
if (new_thread == old_trial)
new_thread = trial;
if (thread == old_trial)
thread = trial;
pat = PATTERN (trial);
if ((must_annul || delay_list == NULL) && (thread_if_true
? check_annul_list_true_false (0, delay_list)
&& eligible_for_annul_false (insn, *pslots_filled, trial, flags)
: check_annul_list_true_false (1, delay_list)
&& eligible_for_annul_true (insn, *pslots_filled, trial, flags)))
{
rtx temp;
must_annul = 1;
winner:
#ifdef HAVE_cc0
if (reg_mentioned_p (cc0_rtx, pat))
link_cc0_insns (trial);
#endif
if (own_thread)
{
rtx note;
update_block (trial, thread);
if (trial == thread)
{
thread = next_active_insn (thread);
if (new_thread == trial)
new_thread = thread;
}
note = find_reg_note (trial, REG_LABEL, 0);
if (note && LABEL_P (XEXP (note, 0)))
LABEL_NUSES (XEXP (note, 0))++;
delete_related_insns (trial);
if (note && LABEL_P (XEXP (note, 0)))
LABEL_NUSES (XEXP (note, 0))--;
}
else
new_thread = next_active_insn (trial);
temp = own_thread ? trial : copy_rtx (trial);
if (thread_if_true)
INSN_FROM_TARGET_P (temp) = 1;
delay_list = add_to_delay_list (temp, delay_list);
if (slots_to_fill == ++(*pslots_filled))
{
while (new_thread && ! own_thread
&& ! insn_sets_resource_p (new_thread, &set, 1)
&& ! insn_sets_resource_p (new_thread, &needed, 1)
&& ! insn_references_resource_p (new_thread,
&set, 1)
&& (prior_insn
= redundant_insn (new_thread, insn,
delay_list)))
{
fix_reg_dead_note (prior_insn, insn);
update_reg_unused_notes (prior_insn, new_thread);
new_thread = next_active_insn (new_thread);
}
break;
}
continue;
}
}
}
lose = 1;
mark_set_resources (trial, &set, 0, MARK_SRC_DEST_CALL);
mark_referenced_resources (trial, &needed, 1);
set.cc = 1;
if (NONJUMP_INSN_P (trial) && GET_CODE (pat) == SET
&& REG_P (SET_SRC (pat))
&& REG_P (SET_DEST (pat))
&& !reg_overlap_mentioned_p (SET_DEST (pat), SET_SRC (pat)))
{
rtx next = next_nonnote_insn (trial);
if (next && NONJUMP_INSN_P (next)
&& 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))
&& ! modified_in_p (SET_DEST (pat), next))
validate_replace_rtx (SET_DEST (pat), SET_SRC (pat), next);
}
}
if (trial && NONJUMP_INSN_P (trial)
&& GET_CODE (PATTERN (trial)) == SEQUENCE
&& JUMP_P (XVECEXP (PATTERN (trial), 0, 0)))
{
if (thread_if_true && trial == new_thread)
{
delay_list
= steal_delay_list_from_target (insn, condition, PATTERN (trial),
delay_list, &set, &needed,
&opposite_needed, slots_to_fill,
pslots_filled, &must_annul,
&new_thread);
if (own_thread && trial != new_thread)
own_thread = own_thread_p (new_thread, new_thread, 0);
}
else if (! thread_if_true)
delay_list
= steal_delay_list_from_fallthrough (insn, condition,
PATTERN (trial),
delay_list, &set, &needed,
&opposite_needed, slots_to_fill,
pslots_filled, &must_annul);
}
if (delay_list == 0 && likely && new_thread
&& NONJUMP_INSN_P (new_thread)
&& GET_CODE (PATTERN (new_thread)) != ASM_INPUT
&& asm_noperands (PATTERN (new_thread)) < 0)
{
rtx pat = PATTERN (new_thread);
rtx dest;
rtx src;
trial = new_thread;
pat = PATTERN (trial);
if (!NONJUMP_INSN_P (trial)
|| GET_CODE (pat) != SET
|| ! eligible_for_delay (insn, 0, trial, flags)
|| can_throw_internal (trial))
return 0;
dest = SET_DEST (pat), src = SET_SRC (pat);
if ((GET_CODE (src) == PLUS || GET_CODE (src) == MINUS)
&& rtx_equal_p (XEXP (src, 0), dest)
&& (!FLOAT_MODE_P (GET_MODE (src))
|| flag_unsafe_math_optimizations)
&& ! reg_overlap_mentioned_p (dest, XEXP (src, 1))
&& ! side_effects_p (pat))
{
rtx other = XEXP (src, 1);
rtx new_arith;
rtx ninsn;
if (GET_CODE (other) == CONST_INT)
new_arith = gen_rtx_fmt_ee (GET_CODE (src), GET_MODE (src), dest,
negate_rtx (GET_MODE (src), other));
else
new_arith = gen_rtx_fmt_ee (GET_CODE (src) == PLUS ? MINUS : PLUS,
GET_MODE (src), dest, other);
ninsn = emit_insn_after (gen_rtx_SET (VOIDmode, dest, new_arith),
insn);
if (recog_memoized (ninsn) < 0
|| (extract_insn (ninsn), ! constrain_operands (1)))
{
delete_related_insns (ninsn);
return 0;
}
if (own_thread)
{
update_block (trial, thread);
if (trial == thread)
{
thread = next_active_insn (thread);
if (new_thread == trial)
new_thread = thread;
}
delete_related_insns (trial);
}
else
new_thread = next_active_insn (trial);
ninsn = own_thread ? trial : copy_rtx (trial);
if (thread_if_true)
INSN_FROM_TARGET_P (ninsn) = 1;
delay_list = add_to_delay_list (ninsn, NULL_RTX);
(*pslots_filled)++;
}
}
if (delay_list && must_annul)
INSN_ANNULLED_BRANCH_P (insn) = 1;
if (new_thread != thread)
{
rtx label;
gcc_assert (thread_if_true);
if (new_thread && JUMP_P (new_thread)
&& (simplejump_p (new_thread)
|| GET_CODE (PATTERN (new_thread)) == RETURN)
&& redirect_with_delay_list_safe_p (insn,
JUMP_LABEL (new_thread),
delay_list))
new_thread = follow_jumps (JUMP_LABEL (new_thread));
if (new_thread == 0)
label = find_end_label ();
else if (LABEL_P (new_thread))
label = new_thread;
else
label = get_label_before (new_thread);
if (label)
reorg_redirect_jump (insn, label);
}
return delay_list;
}
static void
fill_eager_delay_slots (void)
{
rtx insn;
int i;
int num_unfilled_slots = unfilled_slots_next - unfilled_slots_base;
for (i = 0; i < num_unfilled_slots; i++)
{
rtx condition;
rtx target_label, insn_at_target, fallthrough_insn;
rtx delay_list = 0;
int own_target;
int own_fallthrough;
int prediction, slots_to_fill, slots_filled;
insn = unfilled_slots_base[i];
if (insn == 0
|| INSN_DELETED_P (insn)
|| !JUMP_P (insn)
|| ! (condjump_p (insn) || condjump_in_parallel_p (insn)))
continue;
slots_to_fill = num_delay_slots (insn);
if (slots_to_fill == 0)
continue;
slots_filled = 0;
target_label = JUMP_LABEL (insn);
condition = get_branch_condition (insn, target_label);
if (condition == 0)
continue;
insn_at_target = next_active_insn (target_label);
own_target = own_thread_p (target_label, target_label, 0);
if (condition == const_true_rtx)
{
own_fallthrough = 0;
fallthrough_insn = 0;
prediction = 2;
}
else
{
fallthrough_insn = next_active_insn (insn);
own_fallthrough = own_thread_p (NEXT_INSN (insn), NULL_RTX, 1);
prediction = mostly_true_jump (insn, condition);
}
if (prediction > 0)
{
delay_list
= fill_slots_from_thread (insn, condition, insn_at_target,
fallthrough_insn, prediction == 2, 1,
own_target,
slots_to_fill, &slots_filled, delay_list);
if (delay_list == 0 && own_fallthrough)
{
target_label = JUMP_LABEL (insn);
insn_at_target = next_active_insn (target_label);
delay_list
= fill_slots_from_thread (insn, condition, fallthrough_insn,
insn_at_target, 0, 0,
own_fallthrough,
slots_to_fill, &slots_filled,
delay_list);
}
}
else
{
if (own_fallthrough)
delay_list
= fill_slots_from_thread (insn, condition, fallthrough_insn,
insn_at_target, 0, 0,
own_fallthrough,
slots_to_fill, &slots_filled,
delay_list);
if (delay_list == 0)
delay_list
= fill_slots_from_thread (insn, condition, insn_at_target,
next_active_insn (insn), 0, 1,
own_target,
slots_to_fill, &slots_filled,
delay_list);
}
if (delay_list)
unfilled_slots_base[i]
= emit_delay_sequence (insn, delay_list, slots_filled);
if (slots_to_fill == slots_filled)
unfilled_slots_base[i] = 0;
note_delay_statistics (slots_filled, 1);
}
}
static void
relax_delay_slots (rtx first)
{
rtx insn, next, pat;
rtx trial, delay_insn, target_label;
for (insn = first; insn; insn = next)
{
rtx other;
next = next_active_insn (insn);
if (JUMP_P (insn)
&& (condjump_p (insn) || condjump_in_parallel_p (insn))
&& (target_label = JUMP_LABEL (insn)) != 0)
{
target_label = skip_consecutive_labels (follow_jumps (target_label));
if (target_label == 0)
target_label = find_end_label ();
if (target_label && next_active_insn (target_label) == next
&& ! condjump_in_parallel_p (insn))
{
delete_jump (insn);
continue;
}
if (target_label && target_label != JUMP_LABEL (insn))
reorg_redirect_jump (insn, target_label);
if (next && JUMP_P (next)
&& any_condjump_p (insn)
&& (simplejump_p (next) || GET_CODE (PATTERN (next)) == RETURN)
&& target_label
&& next_active_insn (target_label) == next_active_insn (next)
&& no_labels_between_p (insn, next))
{
rtx label = JUMP_LABEL (next);
++LABEL_NUSES (target_label);
if (label)
++LABEL_NUSES (label);
if (invert_jump (insn, label, 1))
{
delete_related_insns (next);
next = insn;
}
if (label)
--LABEL_NUSES (label);
if (--LABEL_NUSES (target_label) == 0)
delete_related_insns (target_label);
continue;
}
}
if (JUMP_P (insn)
&& (simplejump_p (insn) || GET_CODE (PATTERN (insn)) == RETURN)
&& (other = prev_active_insn (insn)) != 0
&& any_condjump_p (other)
&& no_labels_between_p (other, insn)
&& 0 > mostly_true_jump (other,
get_branch_condition (other,
JUMP_LABEL (other))))
{
rtx other_target = JUMP_LABEL (other);
target_label = JUMP_LABEL (insn);
if (invert_jump (other, target_label, 0))
reorg_redirect_jump (insn, other_target);
}
if (!NONJUMP_INSN_P (insn)
|| GET_CODE (PATTERN (insn)) != SEQUENCE)
continue;
pat = PATTERN (insn);
delay_insn = XVECEXP (pat, 0, 0);
if (redundant_insn (XVECEXP (pat, 0, 1), delay_insn, 0))
{
delete_from_delay_slot (XVECEXP (pat, 0, 1));
next = prev_active_insn (next);
continue;
}
if (optimize_size
&& GET_CODE (PATTERN (delay_insn)) == RETURN
&& next
&& JUMP_P (next)
&& GET_CODE (PATTERN (next)) == RETURN)
{
rtx after;
int i;
for (i = 0; i < XVECLEN (pat, 0); i++)
INSN_FROM_TARGET_P (XVECEXP (pat, 0, i)) = 0;
trial = PREV_INSN (insn);
delete_related_insns (insn);
gcc_assert (GET_CODE (pat) == SEQUENCE);
after = trial;
for (i = 0; i < XVECLEN (pat, 0); i++)
{
rtx this_insn = XVECEXP (pat, 0, i);
add_insn_after (this_insn, after);
after = this_insn;
}
delete_scheduled_jump (delay_insn);
continue;
}
if (!JUMP_P (XVECEXP (PATTERN (insn), 0, 0))
|| ! (condjump_p (XVECEXP (PATTERN (insn), 0, 0))
|| condjump_in_parallel_p (XVECEXP (PATTERN (insn), 0, 0))))
continue;
target_label = JUMP_LABEL (delay_insn);
if (target_label)
{
trial = skip_consecutive_labels (follow_jumps (target_label));
if (trial == 0)
trial = find_end_label ();
if (trial && trial != target_label
&& redirect_with_delay_slots_safe_p (delay_insn, trial, insn))
{
reorg_redirect_jump (delay_insn, trial);
target_label = trial;
}
trial = next_active_insn (target_label);
if (trial && GET_CODE (PATTERN (trial)) != SEQUENCE
&& redundant_insn (trial, insn, 0)
&& ! can_throw_internal (trial))
{
rtx tmp = next_active_insn (trial);
if (tmp == 0)
tmp = find_end_label ();
if (tmp)
{
update_block (trial, tmp);
target_label = get_label_before (PREV_INSN (tmp));
reorg_redirect_jump (delay_insn, target_label);
next = insn;
continue;
}
}
if (trial && GET_CODE (PATTERN (trial)) == SEQUENCE
&& XVECLEN (PATTERN (trial), 0) == 2
&& JUMP_P (XVECEXP (PATTERN (trial), 0, 0))
&& (simplejump_p (XVECEXP (PATTERN (trial), 0, 0))
|| GET_CODE (PATTERN (XVECEXP (PATTERN (trial), 0, 0))) == RETURN)
&& redundant_insn (XVECEXP (PATTERN (trial), 0, 1), insn, 0))
{
target_label = JUMP_LABEL (XVECEXP (PATTERN (trial), 0, 0));
if (target_label == 0)
target_label = find_end_label ();
if (target_label
&& redirect_with_delay_slots_safe_p (delay_insn, target_label,
insn))
{
reorg_redirect_jump (delay_insn, target_label);
next = insn;
continue;
}
}
}
if (! INSN_ANNULLED_BRANCH_P (delay_insn)
&& prev_active_insn (target_label) == insn
&& ! condjump_in_parallel_p (delay_insn)
#ifdef HAVE_cc0
&& ! find_reg_note (XVECEXP (pat, 0, XVECLEN (pat, 0) - 1),
REG_CC_USER, NULL_RTX)
#endif
)
{
rtx after;
int i;
for (i = 0; i < XVECLEN (pat, 0); i++)
INSN_FROM_TARGET_P (XVECEXP (pat, 0, i)) = 0;
trial = PREV_INSN (insn);
delete_related_insns (insn);
gcc_assert (GET_CODE (pat) == SEQUENCE);
after = trial;
for (i = 0; i < XVECLEN (pat, 0); i++)
{
rtx this_insn = XVECEXP (pat, 0, i);
add_insn_after (this_insn, after);
after = this_insn;
}
delete_scheduled_jump (delay_insn);
continue;
}
if (next && NONJUMP_INSN_P (next)
&& prev_label (next_active_insn (next)) == target_label
&& simplejump_p (insn)
&& XVECLEN (pat, 0) == 2
&& rtx_equal_p (PATTERN (next), PATTERN (XVECEXP (pat, 0, 1))))
{
delete_related_insns (insn);
continue;
}
if (! INSN_ANNULLED_BRANCH_P (delay_insn)
&& any_condjump_p (delay_insn)
&& next && JUMP_P (next)
&& (simplejump_p (next) || GET_CODE (PATTERN (next)) == RETURN)
&& next_active_insn (target_label) == next_active_insn (next)
&& no_labels_between_p (insn, next))
{
rtx label = JUMP_LABEL (next);
rtx old_label = JUMP_LABEL (delay_insn);
if (label == 0)
label = find_end_label ();
if (label
&& no_labels_between_p (insn, next)
&& redirect_with_delay_slots_safe_p (delay_insn, label, insn))
{
if (old_label)
++LABEL_NUSES (old_label);
if (invert_jump (delay_insn, label, 1))
{
int i;
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_related_insns (next);
next = insn;
}
if (old_label && --LABEL_NUSES (old_label) == 0)
delete_related_insns (old_label);
continue;
}
}
if (INSN_FROM_TARGET_P (XVECEXP (pat, 0, 1))
&& own_thread_p (NEXT_INSN (insn), 0, 1))
try_merge_delay_insns (insn, next);
else if (! INSN_FROM_TARGET_P (XVECEXP (pat, 0, 1))
&& own_thread_p (target_label, target_label, 0))
try_merge_delay_insns (insn, next_active_insn (target_label));
next = next_active_insn (insn);
}
}
#ifdef HAVE_return
static void
make_return_insns (rtx first)
{
rtx insn, jump_insn, pat;
rtx real_return_label = end_of_function_label;
int slots, i;
#ifdef DELAY_SLOTS_FOR_EPILOGUE
if (current_function_epilogue_delay_list != NULL)
return;
#endif
for (insn = first; insn; insn = NEXT_INSN (insn))
if (JUMP_P (insn) && GET_CODE (PATTERN (insn)) == RETURN)
{
real_return_label = get_label_before (insn);
break;
}
LABEL_NUSES (real_return_label)++;
obstack_free (&unfilled_slots_obstack, unfilled_firstobj);
for (insn = first; insn; insn = NEXT_INSN (insn))
{
int flags;
if (!NONJUMP_INSN_P (insn)
|| GET_CODE (PATTERN (insn)) != SEQUENCE
|| !JUMP_P (XVECEXP (PATTERN (insn), 0, 0))
|| JUMP_LABEL (XVECEXP (PATTERN (insn), 0, 0)) != end_of_function_label)
continue;
pat = PATTERN (insn);
jump_insn = XVECEXP (pat, 0, 0);
if (! reorg_redirect_jump (jump_insn, NULL_RTX))
{
if (redirect_with_delay_slots_safe_p (jump_insn,
real_return_label,
insn))
reorg_redirect_jump (jump_insn, real_return_label);
continue;
}
flags = get_jump_flags (jump_insn, JUMP_LABEL (jump_insn));
slots = num_delay_slots (jump_insn);
if (slots >= XVECLEN (pat, 0) - 1)
{
for (i = 1; i < XVECLEN (pat, 0); i++)
if (! (
#ifdef ANNUL_IFFALSE_SLOTS
(INSN_ANNULLED_BRANCH_P (jump_insn)
&& INSN_FROM_TARGET_P (XVECEXP (pat, 0, i)))
? eligible_for_annul_false (jump_insn, i - 1,
XVECEXP (pat, 0, i), flags) :
#endif
#ifdef ANNUL_IFTRUE_SLOTS
(INSN_ANNULLED_BRANCH_P (jump_insn)
&& ! INSN_FROM_TARGET_P (XVECEXP (pat, 0, i)))
? eligible_for_annul_true (jump_insn, i - 1,
XVECEXP (pat, 0, i), flags) :
#endif
eligible_for_delay (jump_insn, i - 1,
XVECEXP (pat, 0, i), flags)))
break;
}
else
i = 0;
if (i == XVECLEN (pat, 0))
continue;
if (GET_CODE (PATTERN (jump_insn)) == RETURN)
{
rtx prev = PREV_INSN (insn);
delete_related_insns (insn);
for (i = 1; i < XVECLEN (pat, 0); i++)
prev = emit_insn_after (PATTERN (XVECEXP (pat, 0, i)), prev);
insn = emit_jump_insn_after (PATTERN (jump_insn), prev);
emit_barrier_after (insn);
if (slots)
obstack_ptr_grow (&unfilled_slots_obstack, insn);
}
else
reorg_redirect_jump (jump_insn, real_return_label);
}
if (--LABEL_NUSES (real_return_label) == 0)
delete_related_insns (real_return_label);
fill_simple_delay_slots (1);
fill_simple_delay_slots (0);
}
#endif
void
dbr_schedule (rtx first)
{
rtx insn, next, epilogue_insn = 0;
int i;
if (n_basic_blocks == NUM_FIXED_BLOCKS)
return;
for (max_uid = 0, insn = first; insn; insn = NEXT_INSN (insn))
{
if (INSN_UID (insn) > max_uid)
max_uid = INSN_UID (insn);
if (NOTE_P (insn)
&& NOTE_LINE_NUMBER (insn) == NOTE_INSN_EPILOGUE_BEG)
epilogue_insn = insn;
}
uid_to_ruid = xmalloc ((max_uid + 1) * sizeof (int));
for (i = 0, insn = first; insn; i++, insn = NEXT_INSN (insn))
uid_to_ruid[INSN_UID (insn)] = i;
if (unfilled_firstobj == 0)
{
gcc_obstack_init (&unfilled_slots_obstack);
unfilled_firstobj = obstack_alloc (&unfilled_slots_obstack, 0);
}
for (insn = next_active_insn (first); insn; insn = next_active_insn (insn))
{
rtx target;
INSN_ANNULLED_BRANCH_P (insn) = 0;
INSN_FROM_TARGET_P (insn) = 0;
if (JUMP_P (insn)
&& (GET_CODE (PATTERN (insn)) == ADDR_VEC
|| GET_CODE (PATTERN (insn)) == ADDR_DIFF_VEC))
continue;
if (num_delay_slots (insn) > 0)
obstack_ptr_grow (&unfilled_slots_obstack, insn);
if (JUMP_P (insn)
&& (condjump_p (insn) || condjump_in_parallel_p (insn))
&& JUMP_LABEL (insn) != 0
&& ((target = skip_consecutive_labels (JUMP_LABEL (insn)))
!= JUMP_LABEL (insn)))
redirect_jump (insn, target, 1);
}
init_resource_info (epilogue_insn);
end_of_function_label = 0;
memset (num_insns_needing_delays, 0, sizeof num_insns_needing_delays);
memset (num_filled_delays, 0, sizeof num_filled_delays);
for (reorg_pass_number = 0;
reorg_pass_number < MAX_REORG_PASSES;
reorg_pass_number++)
{
fill_simple_delay_slots (1);
fill_simple_delay_slots (0);
fill_eager_delay_slots ();
relax_delay_slots (first);
}
for (insn = first; insn; insn = next)
{
next = NEXT_INSN (insn);
if (NONJUMP_INSN_P (insn) && GET_CODE (PATTERN (insn)) == USE
&& INSN_P (XEXP (PATTERN (insn), 0)))
next = delete_related_insns (insn);
}
if (end_of_function_label && --LABEL_NUSES (end_of_function_label) == 0)
delete_related_insns (end_of_function_label);
#ifdef HAVE_return
if (HAVE_return && end_of_function_label != 0)
make_return_insns (first);
#endif
obstack_free (&unfilled_slots_obstack, unfilled_firstobj);
unfilled_firstobj = obstack_alloc (&unfilled_slots_obstack, 0);
if (dump_file)
{
int i, j, need_comma;
int total_delay_slots[MAX_DELAY_HISTOGRAM + 1];
int total_annul_slots[MAX_DELAY_HISTOGRAM + 1];
for (reorg_pass_number = 0;
reorg_pass_number < MAX_REORG_PASSES;
reorg_pass_number++)
{
fprintf (dump_file, ";; Reorg pass #%d:\n", reorg_pass_number + 1);
for (i = 0; i < NUM_REORG_FUNCTIONS; i++)
{
need_comma = 0;
fprintf (dump_file, ";; Reorg function #%d\n", i);
fprintf (dump_file, ";; %d insns needing delay slots\n;; ",
num_insns_needing_delays[i][reorg_pass_number]);
for (j = 0; j < MAX_DELAY_HISTOGRAM + 1; j++)
if (num_filled_delays[i][j][reorg_pass_number])
{
if (need_comma)
fprintf (dump_file, ", ");
need_comma = 1;
fprintf (dump_file, "%d got %d delays",
num_filled_delays[i][j][reorg_pass_number], j);
}
fprintf (dump_file, "\n");
}
}
memset (total_delay_slots, 0, sizeof total_delay_slots);
memset (total_annul_slots, 0, sizeof total_annul_slots);
for (insn = first; insn; insn = NEXT_INSN (insn))
{
if (! INSN_DELETED_P (insn)
&& NONJUMP_INSN_P (insn)
&& GET_CODE (PATTERN (insn)) != USE
&& GET_CODE (PATTERN (insn)) != CLOBBER)
{
if (GET_CODE (PATTERN (insn)) == SEQUENCE)
{
j = XVECLEN (PATTERN (insn), 0) - 1;
if (j > MAX_DELAY_HISTOGRAM)
j = MAX_DELAY_HISTOGRAM;
if (INSN_ANNULLED_BRANCH_P (XVECEXP (PATTERN (insn), 0, 0)))
total_annul_slots[j]++;
else
total_delay_slots[j]++;
}
else if (num_delay_slots (insn) > 0)
total_delay_slots[0]++;
}
}
fprintf (dump_file, ";; Reorg totals: ");
need_comma = 0;
for (j = 0; j < MAX_DELAY_HISTOGRAM + 1; j++)
{
if (total_delay_slots[j])
{
if (need_comma)
fprintf (dump_file, ", ");
need_comma = 1;
fprintf (dump_file, "%d got %d delays", total_delay_slots[j], j);
}
}
fprintf (dump_file, "\n");
#if defined (ANNUL_IFTRUE_SLOTS) || defined (ANNUL_IFFALSE_SLOTS)
fprintf (dump_file, ";; Reorg annuls: ");
need_comma = 0;
for (j = 0; j < MAX_DELAY_HISTOGRAM + 1; j++)
{
if (total_annul_slots[j])
{
if (need_comma)
fprintf (dump_file, ", ");
need_comma = 1;
fprintf (dump_file, "%d got %d delays", total_annul_slots[j], j);
}
}
fprintf (dump_file, "\n");
#endif
fprintf (dump_file, "\n");
}
for (insn = first; insn; insn = NEXT_INSN (insn))
{
int pred_flags;
if (NONJUMP_INSN_P (insn))
{
rtx pat = PATTERN (insn);
if (GET_CODE (pat) == SEQUENCE)
insn = XVECEXP (pat, 0, 0);
}
if (!JUMP_P (insn))
continue;
pred_flags = get_jump_flags (insn, JUMP_LABEL (insn));
REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_BR_PRED,
GEN_INT (pred_flags),
REG_NOTES (insn));
}
free_resource_info ();
free (uid_to_ruid);
#ifdef DELAY_SLOTS_FOR_EPILOGUE
{
rtx link;
for (link = current_function_epilogue_delay_list;
link;
link = XEXP (link, 1))
INSN_LOCATOR (XEXP (link, 0)) = 0;
}
#endif
}
#endif
static bool
gate_handle_delay_slots (void)
{
#ifdef DELAY_SLOTS
return flag_delayed_branch;
#else
return 0;
#endif
}
static unsigned int
rest_of_handle_delay_slots (void)
{
#ifdef DELAY_SLOTS
dbr_schedule (get_insns ());
#endif
return 0;
}
struct tree_opt_pass pass_delay_slots =
{
"dbr",
gate_handle_delay_slots,
rest_of_handle_delay_slots,
NULL,
NULL,
0,
TV_DBR_SCHED,
0,
0,
0,
0,
TODO_dump_func |
TODO_ggc_collect,
'd'
};
static bool
gate_handle_machine_reorg (void)
{
return targetm.machine_dependent_reorg != 0;
}
static unsigned int
rest_of_handle_machine_reorg (void)
{
targetm.machine_dependent_reorg ();
return 0;
}
struct tree_opt_pass pass_machine_reorg =
{
"mach",
gate_handle_machine_reorg,
rest_of_handle_machine_reorg,
NULL,
NULL,
0,
TV_MACH_DEP,
0,
0,
0,
0,
TODO_dump_func |
TODO_ggc_collect,
'M'
};