c4x.c (c4x_modified_between_p, [...]): Delete.

* config/c4x/c4x.c (c4x_modified_between_p, c4x_mem_set_p,
 	c4x_mem_set_p, c4x_mem_modified_between_p, c4x_insn_moveable_p,
	c4x_parallel_pack, c4x_parallel_find, c4x_update_info_reg,
	c4x_update_info_regs, c4x_copy_insn_after, c4x_copy_insns_after,
	c4x_merge_notes, c4x_parallel_process,
	c4x_combine_parallel_independent, c4x_combine_parallel_dependent,
	c4x_combine_parallel): Delete.

From-SVN: r23878
This commit is contained in:
Michael Hayes 1998-11-25 19:45:42 +00:00 committed by Michael Hayes
parent 51a6311276
commit 0fe69abaa1
2 changed files with 23 additions and 766 deletions

View File

@ -1,3 +1,26 @@
Thu Nov 26 16:37:59 1998 Michael Hayes <m.hayes@elec.canterbury.ac.nz>
* config/c4x/c4x.c (c4x_modified_between_p, c4x_mem_set_p,
c4x_mem_set_p, c4x_mem_modified_between_p, c4x_insn_moveable_p,
c4x_parallel_pack, c4x_parallel_find, c4x_update_info_reg,
c4x_update_info_regs, c4x_copy_insn_after, c4x_copy_insns_after,
c4x_merge_notes, c4x_parallel_process,
c4x_combine_parallel_independent, c4x_combine_parallel_dependent,
c4x_combine_parallel): Delete.
Thu Nov 26 15:16:05 1998 Michael Hayes <m.hayes@elec.canterbury.ac.nz>
* config/c4x/c4x.c: (c4x_override_options): For compatibility
with old target options clear flag_branch_on_count_reg if
-mno-rptb specified and set flag_argument_alias is -mno-aliases
specified.
(c4x_output_cbranch): Handle a sequence of insns rather than a
single insn.
(c4x_rptb_insert): Don not emit a RPTB insn if the RC register
has not been allocated as the loop counter.
(c4x_address_conflict):
Thu Nov 26 14:56:32 1998 Michael Hayes <m.hayes@elec.canterbury.ac.nz>
* config/c4x/c4x.h (TARGET_DEFAULT): Add PARALEL_MPY_FLAG.

View File

@ -4107,772 +4107,6 @@ c4x_valid_type_attribute_p (type, attributes, identifier, args)
}
/* 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)