}
-/* This is a modified version of modified_between_p that doesn't give
- up if a changing MEM is found. It checks all insns between START
- and END to see if any registers mentioned in X are set. */
-static int
-c4x_modified_between_p (x, start, end)
- rtx x;
- rtx start, end;
-{
- enum rtx_code code = GET_CODE (x);
- char *fmt;
- int i, j;
-
- switch (code)
- {
- case CONST_INT:
- case CONST_DOUBLE:
- case CONST:
- case SYMBOL_REF:
- case LABEL_REF:
- return 0;
-
- case PC:
- case CC0:
- return 1;
-
- case MEM:
- break;
-
- case REG:
- return reg_set_between_p (x, start, end);
-
- default:
- break;
- }
-
- fmt = GET_RTX_FORMAT (code);
- for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
- {
- if (fmt[i] == 'e' && c4x_modified_between_p (XEXP (x, i), start, end))
- return 1;
-
- if (fmt[i] == 'E')
- for (j = XVECLEN (x, i) - 1; j >= 0; j--)
- if (c4x_modified_between_p (XVECEXP (x, i, j), start, end))
- return 1;
- }
-
- return 0;
-}
-
-/* Return 1 if rtx X references memory that is changing. */
-static int
-c4x_mem_ref_p (x)
- rtx x;
-{
- enum rtx_code code = GET_CODE (x);
- char *fmt;
- int i, j;
-
- if (code == MEM && !RTX_UNCHANGING_P (x))
- return 1;
-
- fmt = GET_RTX_FORMAT (code);
- for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
- {
- if (fmt[i] == 'e' && c4x_mem_ref_p (XEXP (x, i)))
- return 1;
-
- if (fmt[i] == 'E')
- for (j = XVECLEN (x, i) - 1; j >= 0; j--)
- if (c4x_mem_ref_p (XVECEXP (x, i, j)))
- return 1;
- }
-
- return 0;
-}
-
-/* Return 1 if rtx X sets or clobbers memory. */
-static int
-c4x_mem_set_p (x)
- rtx x;
-{
- enum rtx_code code = GET_CODE (x);
- char *fmt;
- int i, j;
-
- if ((code == SET || code == CLOBBER)
- && (GET_CODE (SET_DEST (x)) == MEM))
- return 1;
-
- fmt = GET_RTX_FORMAT (code);
- for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
- {
- if (fmt[i] == 'e' && c4x_mem_set_p (XEXP (x, i)))
- return 1;
-
- if (fmt[i] == 'E')
- for (j = XVECLEN (x, i) - 1; j >= 0; j--)
- if (c4x_mem_set_p (XVECEXP (x, i, j)))
- return 1;
- }
-
- return 0;
-}
-
-
-/* Return 1 if any insns between START and END (exclusive) sets
- or clobbers memory. */
-static int
-c4x_mem_modified_between_p (start, end)
- rtx start, end;
-{
- rtx insn;
-
- if (start == end)
- return 0;
-
- for (insn = NEXT_INSN (start); insn != end; insn = NEXT_INSN (insn))
- if (GET_RTX_CLASS (GET_CODE (insn)) == 'i'
- && c4x_mem_set_p (PATTERN (insn)))
- return 1;
- return 0;
-}
-
-
-/* Returns 1 if INSN can be moved past all the insns between START and
- END exclusive. If TARGET_ALIASES is not set and a memory store is
- detected, then 0 is returned. */
-static int
-c4x_insn_moveable_p (insn, start, end)
- rtx insn;
- rtx start, end;
-{
- if (start == end)
- return 1;
-
- /* We can't use modified_between_p since this will
- return 1 if set1 contains a MEM. */
- if (c4x_modified_between_p (insn, start, end))
- return 0;
-
- return 1;
-}
-
-
-/* See if the insns INSN1 and INSN2 can be packed into a PARALLEL.
- Return 0 if the insns cannot be packed or the rtx of the packed
- insn (with clobbers added as necessary). If DEPEND is non zero,
- then the destination register of INSN1 must be used by INSN2. */
-static rtx
-c4x_parallel_pack (insn1, insn2, depend)
- rtx insn1;
- rtx insn2;
- int depend;
-{
- rtx set1;
- rtx set2;
- rtx pack;
- enum machine_mode mode1;
- enum machine_mode mode2;
- int num_clobbers;
- int insn_code_number;
-
- /* We could generalise things to not just rely on single sets. */
- if (!(set1 = single_set (insn1))
- || !(set2 = single_set (insn2)))
- return 0;
-
- mode1 = GET_MODE (SET_DEST (set1));
- mode2 = GET_MODE (SET_DEST (set2));
- if (mode1 != mode2)
- return 0;
-
- if (depend)
- {
- rtx dst1;
-
- /* Require insn2 to be dependent upon the result of insn1. */
- dst1 = SET_DEST (set1);
-
- if (!REG_P (dst1))
- return 0;
-
- if (!reg_mentioned_p (dst1, set2))
- return 0;
-
- /* The dependent register must die in insn2 since a parallel
- insn will generate a new value. */
- if (!find_regno_note (insn2, REG_DEAD, REGNO (dst1)))
- return 0;
- }
-
- pack = gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, set1, set2));
- num_clobbers = 0;
- if ((insn_code_number = recog (pack, pack, &num_clobbers)) < 0)
- return 0;
-
- if (num_clobbers != 0)
- {
- rtx newpack;
- int i;
-
- newpack = gen_rtx_PARALLEL (VOIDmode,
- gen_rtvec (GET_CODE (pack) == PARALLEL
- ? XVECLEN (pack, 0) + num_clobbers
- : num_clobbers + 1));
-
- if (GET_CODE (pack) == PARALLEL)
- for (i = 0; i < XVECLEN (pack, 0); i++)
- XVECEXP (newpack, 0, i) = XVECEXP (pack, 0, i);
- else
- XVECEXP (newpack, 0, 0) = pack;
-
- add_clobbers (newpack, insn_code_number);
- pack = newpack;
- }
-
- return pack;
-}
-
-
-static rtx
-c4x_parallel_find (insn1, loop_end, depend, insn2)
- rtx insn1;
- rtx loop_end;
- int depend;
- rtx *insn2;
-{
- rtx insn;
- rtx pack;
-
- /* We could use the logical links if depend is non zero? */
-
- for (insn = NEXT_INSN (insn1); insn != loop_end; insn = NEXT_INSN(insn))
- {
- switch (GET_CODE (insn))
- {
- default:
- case JUMP_INSN:
- case CALL_INSN:
- case NOTE:
- break;
-
- case INSN:
- if (!(pack = c4x_parallel_pack (insn1, insn, depend)))
- break;
-
- /* What if insn1 or insn2 sets cc and is required by another
- insn? */
-
-#if 0
- /* Check that nothing between insn1 and insn will spoil the
- show. */
- if (NEXT_INSN (insn1) != insn
- && c4x_modified_between_p (insn, NEXT_INSN (insn1), insn))
- return 0;
-#else
- /* This will do in the interim. If the insns between
- insn1 and insn are harmless, we can move things around
- if we're careful. */
- if (next_nonnote_insn (insn1) != insn)
- return 0;
-#endif
-
- /* Do some checks here... */
- *insn2 = insn;
- return pack;
- }
- }
- return 0;
-}
-
-
-/* Update the register info for reg REG found in the basic block BB,
- where SET is 1 if the register is being set. */
-static void
-c4x_update_info_reg (reg, set, bb)
- rtx reg;
- int set;
- int bb;
-{
- int regno;
-
- if (!REG_P (reg))
- fatal_insn ("Expecting register rtx", reg);
-
- regno = REGNO (reg);
-
- /* REGNO_FIRST_UID and REGNO_LAST_UID don't need setting. */
-
- SET_REGNO_REG_SET (basic_block_live_at_start[bb], regno);
- REG_BASIC_BLOCK (regno) = REG_BLOCK_GLOBAL;
- if (set)
- REG_N_SETS (regno)++;
- else
- REG_N_REFS (regno)++;
-}
-
-
-/* Update the register info for all the regs in X found in the basic
- block BB. */
-static void
-c4x_update_info_regs(x, bb)
- rtx x;
- int bb;
-{
- enum rtx_code code;
- char *fmt;
- int i, j;
-
- if (!x)
- return;
-
- code = GET_CODE (x);
- switch (code)
- {
- case CLOBBER:
-#if 0
- if (REG_P (SET_DEST (x)))
- return;
- break;
-#endif
-
- case SET:
- if (REG_P (SET_DEST (x)))
- c4x_update_info_reg (SET_DEST (x), 1, bb);
- else
- c4x_update_info_regs (SET_DEST (x), bb);
-
- if (code == SET)
- c4x_update_info_regs (SET_SRC (x), bb);
- return;
-
- case REG:
- c4x_update_info_reg (x, 0, bb);
- return;
-
- default:
- break;
- }
-
- fmt = GET_RTX_FORMAT (code);
- for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
- {
- if (fmt[i] == 'e')
- c4x_update_info_regs (XEXP (x, i), bb);
- else if (fmt[i] == 'E')
- for (j = XVECLEN (x, i) - 1; j >= 0; j--)
- c4x_update_info_regs (XVECEXP (x, i, j), bb);
- }
-}
-
-
-static void
-c4x_copy_insn_after(insn, prev, bb)
- rtx insn;
- rtx prev;
- int bb;
-{
- rtx note;
- rtx new;
-
- emit_insn_after (copy_rtx (PATTERN (insn)), prev);
-
- new = NEXT_INSN (prev);
-
- /* Copy the REG_NOTES from insn to the new insn. */
- for (note = REG_NOTES (insn); note; note = XEXP (note, 1))
- REG_NOTES (new) = gen_rtx (GET_CODE (note),
- REG_NOTE_KIND (note),
- XEXP (note, 0),
- REG_NOTES (new));
-
- /* Handle all the registers within insn and update the reg info. */
- c4x_update_info_regs (PATTERN (insn), bb);
-}
-
-
-static void
-c4x_copy_insns_after(start, end, pprev, bb)
- rtx start;
- rtx end;
- rtx *pprev;
- int bb;
-{
- rtx insn;
-
- for (insn = start; insn != NEXT_INSN (end); insn = NEXT_INSN(insn))
- {
- switch (GET_CODE (insn))
- {
- case CALL_INSN:
- /* We could allow a libcall with no side effects??? */
- fatal_insn("Repeat block loop contains a call", insn);
- break;
-
- case INSN:
- c4x_copy_insn_after(insn, *pprev, bb - 1);
- *pprev = NEXT_INSN (*pprev);
- break;
-
- default:
- break;
- }
- }
-}
-
-
-/* Merge the notes of insn2 with the notes of insn. */
-static void
-c4x_merge_notes(insn, insn2)
- rtx insn;
- rtx insn2;
-{
- rtx note;
-
- for (note = REG_NOTES (insn2); note; note = XEXP (note, 1))
- {
- rtx link;
-
- for (link = REG_NOTES (insn); link; link = XEXP (link, 1))
- if (REG_NOTE_KIND (note) == REG_NOTE_KIND (link)
- && XEXP (note, 0) == XEXP (link, 0))
- remove_note (insn, note);
- }
- for (note = REG_NOTES (insn2); note; note = XEXP (note, 1))
- REG_NOTES (insn) = gen_rtx (GET_CODE (note),
- REG_NOTE_KIND (note),
- XEXP (note, 0),
- REG_NOTES (insn));
-}
-
-
-/* This pass must update information that subsequent passes expect to be
- correct. Namely: reg_n_refs, reg_n_sets, reg_n_deaths,
- reg_n_calls_crossed, and reg_live_length. Also, basic_block_head,
- basic_block_end. */
-
-static int
-c4x_parallel_process (loop_start, loop_end)
- rtx loop_start;
- rtx loop_end;
-{
- rtx insn;
- rtx insn2;
- rtx pack;
- rtx hoist_pos;
- rtx sink_pos;
- rtx loop_count;
- rtx loop_count_set;
- rtx loop_count_reg;
- rtx jump_insn;
- rtx end_label;
- int num_packs;
- int bb;
-
- jump_insn = PREV_INSN (loop_end);
-
- /* The loop must have a calculable number of iterations
- since we need to reduce the loop count by one.
-
- For now, only process repeat block loops, since we can tell that
- these have a calculable number of iterations.
-
- The loop count must be at least 2? */
-
- loop_count = PREV_INSN (loop_start);
-
- if (!(loop_count_set = single_set (loop_count)))
- return 0;
-
-#if 0
- /* Disable this optimisation until REG_LOOP_COUNT note
- added. */
- if (!find_reg_note (loop_count, REG_LOOP_COUNT, NULL_RTX))
- return 0;
-#else
- return 0;
-#endif
-
- loop_count_reg = SET_DEST (loop_count_set);
-
- /* Determine places to hoist and sink insns out of the loop.
- We need to hoist insns before the label at the top of the loop.
- We'll have to update basic_block_head. */
-
- /* Place in the rtx where we hoist insns after. */
- hoist_pos = loop_count;
-
- /* Place in the rtx where we sink insns after. */
- sink_pos = loop_end;
-
- /* There must be an easier way to work out which basic block we are
- in. */
- for (bb = 0; bb < n_basic_blocks; bb++)
- if (basic_block_head[bb] == NEXT_INSN (loop_end))
- break;
-
- if (bb >= n_basic_blocks)
- fatal_insn("Cannot find basic block for insn", NEXT_INSN (loop_end));
-
- /* Skip to label at top of loop. */
- for (; GET_CODE (loop_start) != CODE_LABEL;
- loop_start = NEXT_INSN(loop_start));
-
- num_packs = 0;
- for (insn = loop_start; insn != loop_end; insn = NEXT_INSN(insn))
- {
- switch (GET_CODE (insn))
- {
- default:
- case JUMP_INSN:
- case CALL_INSN:
- case NOTE:
- break;
-
- case INSN:
-
- /* Look for potential insns to combine where the second one
- is dependent upon the first. We could have another pass
- that tries combining independent insns but that is not so
- important. We could do this afterwards as a more generic
- peepholer. */
-
- if ((pack = c4x_parallel_find(insn, loop_end, 1, &insn2)))
- {
- rtx set1;
- rtx set2;
- rtx note;
- rtx seq_start;
-
- set1 = single_set (insn);
- set2 = single_set (insn2);
-
- /* We need to hoist a copy of insn1 out of the loop and
- to sink a copy insn2 out of the loop. We can avoid
- the latter if the destination of insn2 is used
- by a following insn within the loop.
-
- We cannot hoist insn1 out of the loop if any of the
- preceeding insns within the loop modifies the destination
- of insn1 or modifies any of the operands of insn1. */
-
- /* If the user has flagged that there are potential aliases,
- then we can't move the insn if it references memory
- past any insns that modify memory. */
- if (TARGET_ALIASES
- && c4x_mem_ref_p (PATTERN (insn))
- && c4x_mem_modified_between_p (loop_start, loop_end))
- break;
-
- /* None of the registers used in insn can be modified by
- any of the insns from the start of the loop until insn. */
- if (!c4x_insn_moveable_p (set1, loop_start, insn))
- break;
-
- /* None of the registers used in insn can be modified by
- any of the insns after insn2 until the end of the
- loop, especially the result which needs to be saved
- for the next iteration. */
- if (!c4x_insn_moveable_p (set1, insn2, loop_end))
- break;
-
- /* We need to hoist all the insns from the loop top
- to and including insn. */
- c4x_copy_insns_after (NEXT_INSN (loop_start), insn,
- &hoist_pos, bb);
-
- /* We need to sink all the insns after insn to
- loop_end. */
- c4x_copy_insns_after (NEXT_INSN (insn), PREV_INSN (jump_insn),
- &sink_pos, bb + 1);
-
- /* Change insn to the new parallel insn, retaining the notes
- of the old insn. */
- if (!validate_change (insn, &PATTERN (insn), pack, 0))
- fatal_insn("Cannot replace insn with parallel insn", pack);
-
- /* Copy the REG_NOTES from insn2 to the new insn
- avoiding duplicates. */
- c4x_merge_notes (insn, insn2);
-
- delete_insn (insn2);
-
- /* The destination register of insn1 no longer dies in
- this composite insn. Don't use remove_death since that
- alters REG_N_DEATHS. The REG_DEAD note has just been
- moved. */
- note = find_regno_note (insn, REG_DEAD, REGNO (SET_DEST (set1)));
- if (note)
- remove_note (insn, note);
-
- /* ??? Do we have to modify the LOG_LINKS? */
-
- /* We need to decrement the loop count. We probably
- should test if the loop count is negative and branch
- to end label if so. */
- if (GET_CODE (SET_SRC (loop_count_set)) == CONST_INT)
- {
- /* The loop count must be more than 1 surely? */
- SET_SRC (loop_count_set)
- = GEN_INT (INTVAL (SET_SRC (loop_count_set)) - 1);
- }
- else if (GET_CODE (SET_SRC (loop_count_set)) == PLUS
- && GET_CODE (XEXP (SET_SRC (loop_count_set), 1))
- == CONST_INT)
- {
- XEXP (SET_SRC (loop_count_set), 1)
- = GEN_INT (INTVAL (XEXP (SET_SRC (loop_count_set), 1))
- - 1);
- }
- else
- {
- start_sequence ();
- expand_binop (QImode, sub_optab, loop_count_reg,
- GEN_INT (1), loop_count_reg,
- 1, OPTAB_DIRECT);
- seq_start = get_insns ();
- end_sequence ();
- emit_insns_after (seq_start, loop_count);
-
- /* Check this. What if we emit more than one insn?
- Can we emit more than one insn? */
- REG_NOTES (seq_start)
- = gen_rtx_EXPR_LIST (REG_UNUSED,
- loop_count_reg,
- REG_NOTES (seq_start));
- }
-
- if (GET_CODE (SET_SRC (loop_count_set)) != CONST_INT)
- {
- end_label = gen_label_rtx();
- start_sequence ();
- emit_cmp_insn (loop_count_reg,
- const0_rtx, LT, NULL_RTX, word_mode, 0, 0);
- emit_jump_insn (gen_blt (end_label));
- seq_start = get_insns ();
- end_sequence ();
- emit_insns_after (seq_start, hoist_pos);
- emit_label_after (end_label, sink_pos);
-
-#if 0
- /* This is a bit of a hack...but why was it necessary? */
- REG_NOTES (NEXT_INSN (seq_start))
- = gen_rtx_EXPR_LIST (REG_DEAD,
- loop_count_reg,
- REG_NOTES (NEXT_INSN (seq_start)));
-#endif
- }
-
- if (TARGET_DEVEL)
- debug_rtx(insn);
-
- num_packs ++;
-
-#if 1
- /* If we want to pack more than one parallel insn
- we will have to tag which insns have been
- hoisted/sunk/paired. We might need a recursive approach. */
-
- return num_packs;
-#endif
- }
- break;
- }
- }
- return num_packs;
-}
-
-
-static void
-c4x_combine_parallel_independent (insns)
- rtx insns ATTRIBUTE_UNUSED;
-{
- /* Combine independent insns like
- (set (mem (reg 0)) (reg 1))
- (set (reg 2) (mem (reg 3)))
- where (reg 1) != (reg 2) unless there is a REG_DEAD note
- on the first insn. */
-
-}
-
-static void
-c4x_combine_parallel_dependent (insns)
- rtx insns;
-{
- rtx insn;
- rtx loop_start;
- rtx loop_end;
- int num_jumps;
- int num_insns;
-
- /* Find the innermost loop and check that it is unjumped. */
- loop_start = NULL_RTX;
- num_jumps = 0;
- for (insn = insns; insn; insn = NEXT_INSN(insn))
- {
- switch (GET_CODE (insn))
- {
- case INSN:
- num_insns++;
- break;
-
- case CALL_INSN:
- /* We could allow a libcall with no side effects??? */
- case JUMP_INSN:
- num_jumps++;
- break;
-
- case NOTE:
- switch (NOTE_LINE_NUMBER (insn))
- {
- case NOTE_INSN_LOOP_BEG:
- loop_start = insn;
- num_jumps = 0;
- num_insns = 0;
- break;
-
- case NOTE_INSN_LOOP_CONT:
- if (!loop_start)
- break;
- /* We can't handle a loop with jumps or calls.
- If there are too many insns, we are unlikely
- to be able to find a suitable case for optimisation.
- The maximum number of insns may require tweaking. */
- if (!num_jumps && num_insns < 20)
- {
- /* Skip to end of loop. */
- loop_end = NULL_RTX;
- for (; insn; insn = NEXT_INSN(insn))
- if (GET_CODE (insn) == NOTE
- && NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_END)
- break;
- loop_end = insn;
- if (!loop_end)
- fatal_insn("Could not find note at end of loop",
- loop_start);
- c4x_parallel_process(loop_start, loop_end);
- }
- loop_start = NULL_RTX;
- break;
-
- default:
- break;
- }
- default:
- break;
- }
- }
-}
-
-
-void
-c4x_combine_parallel (insns)
- rtx insns;
-{
- /* Only let people who know how to shoot themselves in the foot do so! */
- if (!TARGET_PARALLEL_PACK)
- return;
-
- c4x_combine_parallel_dependent (insns);
-
- c4x_combine_parallel_independent (insns);
-}
-
-
/* !!! FIXME to emit RPTS correctly. */
int
c4x_rptb_rpts_p (insn, op)