Commit c31500c2 by Hans-Peter Nilsson

cris.c (ASSERT_PLT_UNSPEC): Remove unused macro.

	* config/cris/cris.c (ASSERT_PLT_UNSPEC): Remove unused macro.
	(cris_movem_load_rest_p, cris_store_multiple_op_p): Remove FIXME.
	Change regno_dir and regno only if !TARGET_V32.
	(cris_conditional_register_usage): If TARGET_V32, set
	reg_alloc_order as per REG_ALLOC_ORDER_V32 and make
	CRIS_ACR_REGNUM non-fixed.
	(cris_print_base): Add gcc_assert for post_inc on CRIS_ACR_REGNUM.
	(cris_print_operand) <case 'Z', case 'u'>: New cases.
	<case REG of case 'H'>: Allow for CRIS_SRP_REGNUM.
	(cris_reload_address_legitimized): Always return false for
	TARGET_V32.
	(cris_register_move_cost): New function, guts from
	REGISTER_MOVE_COST adjusted for CRIS v32.
	(cris_normal_notice_update_cc): New function split out from...
	(cris_notice_update_cc): Set cc_status.flags CC_REVERSED for
	TARGET_CCINIT.  Call cris_normal_notice_update_cc for CC_REV,
	CC_NOOV32 and CC_NORMAL, but set cc_status.flags CC_NO_OVERFLOW
	for CC_NOOV32 and TARGET_V32.
	(cris_simple_epilogue): Always return false for TARGET_V32 if
	cris_return_address_on_stack yields true.
	(cris_cc0_user_requires_cmp): New function.
	(cris_valid_pic_const): Add argument ANY_OPERAND.  All callers
	changed.  Handle CRIS_UNSPEC_PLT_PCREL and CRIS_UNSPEC_PCREL.
	(cris_asm_output_case_end): New function, guts from
	ASM_OUTPUT_CASE_END adjusted for CRIS v32.
	(cris_override_options): Adjust for CRIS v32.  Mask out
	TARGET_SIDE_EFFECT_PREFIXES and TARGET_MUL_BUG if v32.
	(cris_asm_output_mi_thunk, cris_expand_epilogue)
	(cris_gen_movem_load, cris_emit_movem_store)
	(cris_expand_pic_call_address, cris_asm_output_symbol_ref)
	(cris_asm_output_label_ref, cris_output_addr_const_extra): Adjust
	for CRIS v32.
	(cris_split_movdx): Copy re-used MEM.

From-SVN: r130966
parent 99c5227a
......@@ -52,11 +52,6 @@ along with GCC; see the file COPYING3. If not see
#define ADDITIVE_SIZE_MODIFIER(size) \
((size) <= 63 ? "q" : (size) <= 255 ? "u.b" : (size) <= 65535 ? "u.w" : ".d")
#define ASSERT_PLT_UNSPEC(x) \
CRIS_ASSERT (XINT (x, 1) == CRIS_UNSPEC_PLT \
&& ((GET_CODE (XVECEXP (x, 0, 0)) == SYMBOL_REF) \
|| GET_CODE (XVECEXP (x, 0, 0)) == LABEL_REF))
#define LOSE_AND_RETURN(msgid, x) \
do \
{ \
......@@ -229,9 +224,11 @@ cris_movem_load_rest_p (rtx op, int offs)
else
i = offs + 1;
/* FIXME: These two only for pre-v32. */
regno_dir = -1;
regno = reg_count - 1;
if (!TARGET_V32)
{
regno_dir = -1;
regno = reg_count - 1;
}
elt = XVECEXP (op, 0, offs);
src_addr = XEXP (SET_SRC (elt), 0);
......@@ -331,9 +328,11 @@ cris_store_multiple_op_p (rtx op)
else
i = 1;
/* FIXME: These two only for pre-v32. */
regno_dir = -1;
regno = reg_count - 1;
if (!TARGET_V32)
{
regno_dir = -1;
regno = reg_count - 1;
}
if (GET_CODE (elt) != SET
|| !REG_P (SET_SRC (elt))
......@@ -390,6 +389,20 @@ cris_conditional_register_usage (void)
fixed_regs[PIC_OFFSET_TABLE_REGNUM]
= call_used_regs[PIC_OFFSET_TABLE_REGNUM] = 1;
/* Allow use of ACR (PC in pre-V32) and tweak order. */
if (TARGET_V32)
{
static const int reg_alloc_order_v32[] = REG_ALLOC_ORDER_V32;
unsigned int i;
fixed_regs[CRIS_ACR_REGNUM] = 0;
for (i = 0;
i < sizeof (reg_alloc_order_v32)/sizeof (reg_alloc_order_v32[0]);
i++)
reg_alloc_order[i] = reg_alloc_order_v32[i];
}
if (TARGET_HAS_MUL_INSNS)
fixed_regs[CRIS_MOF_REGNUM] = 0;
......@@ -551,7 +564,10 @@ cris_print_base (rtx base, FILE *file)
if (REG_P (base))
fprintf (file, "$%s", reg_names[REGNO (base)]);
else if (GET_CODE (base) == POST_INC)
fprintf (file, "$%s+", reg_names[REGNO (XEXP (base, 0))]);
{
gcc_assert (REGNO (XEXP (base, 0)) != CRIS_ACR_REGNUM);
fprintf (file, "$%s+", reg_names[REGNO (XEXP (base, 0))]);
}
else
cris_operand_lossage ("unexpected base-type in cris_print_base",
base);
......@@ -781,6 +797,16 @@ cris_print_operand (FILE *file, rtx x, int code)
putc (INTVAL (x) >= -128 && INTVAL (x) <= 255 ? 'b' : 'w', file);
return;
case 'Z':
/* If this is a GOT-symbol, print the size-letter corresponding to
-fpic/-fPIC. For everything else, print "d". */
putc ((flag_pic == 1
&& GET_CODE (x) == CONST
&& GET_CODE (XEXP (x, 0)) == UNSPEC
&& XINT (XEXP (x, 0), 1) == CRIS_UNSPEC_GOTREAD)
? 'w' : 'd', file);
return;
case '#':
/* Output a 'nop' if there's nothing for the delay slot.
This method stolen from the sparc files. */
......@@ -835,8 +861,12 @@ cris_print_operand (FILE *file, rtx x, int code)
case REG:
/* Print reg + 1. Check that there's not an attempt to print
high-parts of registers like stack-pointer or higher. */
if (REGNO (operand) > STACK_POINTER_REGNUM - 2)
high-parts of registers like stack-pointer or higher, except
for SRP (where the "high part" is MOF). */
if (REGNO (operand) > STACK_POINTER_REGNUM - 2
&& (REGNO (operand) != CRIS_SRP_REGNUM
|| CRIS_SRP_REGNUM + 1 != CRIS_MOF_REGNUM
|| fixed_regs[CRIS_MOF_REGNUM] != 0))
LOSE_AND_RETURN ("bad register", operand);
fprintf (file, "$%s", reg_names[REGNO (operand) + 1]);
return;
......@@ -964,6 +994,17 @@ cris_print_operand (FILE *file, rtx x, int code)
fprintf (file, "%s", mults[INTVAL (operand)]);
return;
case 'u':
/* Print "u.w" if a GOT symbol and flag_pic == 1, else ".d". */
if (flag_pic == 1
&& GET_CODE (operand) == CONST
&& GET_CODE (XEXP (operand, 0)) == UNSPEC
&& XINT (XEXP (operand, 0), 1) == CRIS_UNSPEC_GOTREAD)
fprintf (file, "u.w");
else
fprintf (file, ".d");
return;
case 0:
/* No code, print as usual. */
break;
......@@ -1227,6 +1268,9 @@ cris_reload_address_legitimized (rtx x,
if (GET_CODE (x) != PLUS)
return false;
if (TARGET_V32)
return false;
op0 = XEXP (x, 0);
op0p = &XEXP (x, 0);
op1 = XEXP (x, 1);
......@@ -1284,187 +1328,160 @@ cris_reload_address_legitimized (rtx x,
return false;
}
/* This function looks into the pattern to see how this insn affects
condition codes.
Used when to eliminate test insns before a condition-code user,
such as a "scc" insn or a conditional branch. This includes
checking if the entities that cc was updated by, are changed by the
operation.
Currently a jumble of the old peek-inside-the-insn and the newer
check-cc-attribute methods. */
/* Worker function for REGISTER_MOVE_COST. */
void
cris_notice_update_cc (rtx exp, rtx insn)
int
cris_register_move_cost (enum machine_mode mode ATTRIBUTE_UNUSED,
enum reg_class from, enum reg_class to)
{
/* Check if user specified "-mcc-init" as a bug-workaround. FIXME:
TARGET_CCINIT does not work; we must set CC_REVERSED as below.
Several testcases will otherwise fail, for example
gcc.c-torture/execute/20000217-1.c -O0 and -O1. */
if (TARGET_CCINIT)
{
CC_STATUS_INIT;
return;
}
/* Slowly, we're converting to using attributes to control the setting
of condition-code status. */
switch (get_attr_cc (insn))
{
case CC_NONE:
/* Even if it is "none", a setting may clobber a previous
cc-value, so check. */
if (GET_CODE (exp) == SET)
{
if (cc_status.value1
&& modified_in_p (cc_status.value1, insn))
cc_status.value1 = 0;
if (!TARGET_V32)
{
/* Pretend that classes that we don't support are ALL_REGS, so
we give them the highest cost. */
if (from != SPECIAL_REGS && from != MOF_REGS
&& from != GENERAL_REGS && from != GENNONACR_REGS)
from = ALL_REGS;
if (to != SPECIAL_REGS && to != MOF_REGS
&& to != GENERAL_REGS && to != GENNONACR_REGS)
to = ALL_REGS;
}
/* Can't move to and from a SPECIAL_REGS register, so we have to say
their move cost within that class is higher. How about 7? That's 3
for a move to a GENERAL_REGS register, 3 for the move from the
GENERAL_REGS register, and 1 for the increased register pressure.
Also, it's higher than the memory move cost, which is in order.
We also do this for ALL_REGS, since we don't want that class to be
preferred (even to memory) at all where GENERAL_REGS doesn't fit.
Whenever it's about to be used, it's for SPECIAL_REGS. If we don't
present a higher cost for ALL_REGS than memory, a SPECIAL_REGS may be
used when a GENERAL_REGS should be used, even if there are call-saved
GENERAL_REGS left to allocate. This is because the fall-back when
the most preferred register class isn't available, isn't the next
(or next good) wider register class, but the *most widest* register
class. */
if ((reg_classes_intersect_p (from, SPECIAL_REGS)
&& reg_classes_intersect_p (to, SPECIAL_REGS))
|| from == ALL_REGS || to == ALL_REGS)
return 7;
if (reg_classes_intersect_p (from, SPECIAL_REGS)
|| reg_classes_intersect_p (to, SPECIAL_REGS))
return 3;
return 2;
}
if (cc_status.value2
&& modified_in_p (cc_status.value2, insn))
cc_status.value2 = 0;
}
return;
/* Worker for cris_notice_update_cc; handles the "normal" cases.
FIXME: this code is historical; its functionality should be
refactored to look at insn attributes and moved to
cris_notice_update_cc. Except, we better lose cc0 entirely. */
case CC_CLOBBER:
CC_STATUS_INIT;
break;
static void
cris_normal_notice_update_cc (rtx exp, rtx insn)
{
/* "Normal" means, for:
(set (cc0) (...)):
CC is (...).
case CC_NORMAL:
/* Which means, for:
(set (cc0) (...)):
CC is (...).
(set (reg) (...)):
CC is (reg) and (...) - unless (...) is 0 or reg is a special
register or (v32 and (...) is -32..-1), then CC does not change.
CC_NO_OVERFLOW unless (...) is reg or mem.
(set (reg) (...)):
CC is (reg) and (...) - unless (...) is 0, then CC does not change.
CC_NO_OVERFLOW unless (...) is reg or mem.
(set (mem) (...)):
CC does not change.
(set (mem) (...)):
CC does not change.
(set (pc) (...)):
CC does not change.
(set (pc) (...)):
CC does not change.
(parallel
(set (reg1) (mem (bdap/biap)))
(set (reg2) (bdap/biap))):
CC is (reg1) and (mem (reg2))
(parallel
(set (reg1) (mem (bdap/biap)))
(set (reg2) (bdap/biap))):
CC is (reg1) and (mem (reg2))
(parallel
(set (mem (bdap/biap)) (reg1)) [or 0]
(set (reg2) (bdap/biap))):
CC does not change.
(parallel
(set (mem (bdap/biap)) (reg1)) [or 0]
(set (reg2) (bdap/biap))):
CC does not change.
(where reg and mem includes strict_low_parts variants thereof)
(where reg and mem includes strict_low_parts variants thereof)
For all others, assume CC is clobbered.
Note that we do not have to care about setting CC_NO_OVERFLOW,
since the overflow flag is set to 0 (i.e. right) for
instructions where it does not have any sane sense, but where
other flags have meanings. (This includes shifts; the carry is
not set by them).
For all others, assume CC is clobbered.
Note that we do not have to care about setting CC_NO_OVERFLOW,
since the overflow flag is set to 0 (i.e. right) for
instructions where it does not have any sane sense, but where
other flags have meanings. (This includes shifts; the carry is
not set by them).
Note that there are other parallel constructs we could match,
but we don't do that yet. */
Note that there are other parallel constructs we could match,
but we don't do that yet. */
if (GET_CODE (exp) == SET)
{
/* FIXME: Check when this happens. It looks like we should
actually do a CC_STATUS_INIT here to be safe. */
if (SET_DEST (exp) == pc_rtx)
return;
if (GET_CODE (exp) == SET)
/* Record CC0 changes, so we do not have to output multiple
test insns. */
if (SET_DEST (exp) == cc0_rtx)
{
/* FIXME: Check when this happens. It looks like we should
actually do a CC_STATUS_INIT here to be safe. */
if (SET_DEST (exp) == pc_rtx)
return;
CC_STATUS_INIT;
cc_status.value1 = SET_SRC (exp);
/* Record CC0 changes, so we do not have to output multiple
test insns. */
if (SET_DEST (exp) == cc0_rtx)
/* Handle flags for the special btstq on one bit. */
if (GET_CODE (SET_SRC (exp)) == ZERO_EXTRACT
&& XEXP (SET_SRC (exp), 1) == const1_rtx)
{
cc_status.value1 = SET_SRC (exp);
cc_status.value2 = 0;
/* Handle flags for the special btstq on one bit. */
if (GET_CODE (SET_SRC (exp)) == ZERO_EXTRACT
&& XEXP (SET_SRC (exp), 1) == const1_rtx)
{
if (CONST_INT_P (XEXP (SET_SRC (exp), 0)))
/* Using cmpq. */
cc_status.flags = CC_INVERTED;
else
/* A one-bit btstq. */
cc_status.flags = CC_Z_IN_NOT_N;
}
if (CONST_INT_P (XEXP (SET_SRC (exp), 0)))
/* Using cmpq. */
cc_status.flags = CC_INVERTED;
else
cc_status.flags = 0;
if (GET_CODE (SET_SRC (exp)) == COMPARE)
{
if (!REG_P (XEXP (SET_SRC (exp), 0))
&& XEXP (SET_SRC (exp), 1) != const0_rtx)
/* For some reason gcc will not canonicalize compare
operations, reversing the sign by itself if
operands are in wrong order. */
/* (But NOT inverted; eq is still eq.) */
cc_status.flags = CC_REVERSED;
/* This seems to be overlooked by gcc. FIXME: Check again.
FIXME: Is it really safe? */
cc_status.value2
= gen_rtx_MINUS (GET_MODE (SET_SRC (exp)),
XEXP (SET_SRC (exp), 0),
XEXP (SET_SRC (exp), 1));
}
return;
/* A one-bit btstq. */
cc_status.flags = CC_Z_IN_NOT_N;
}
else if (REG_P (SET_DEST (exp))
|| (GET_CODE (SET_DEST (exp)) == STRICT_LOW_PART
&& REG_P (XEXP (SET_DEST (exp), 0))))
{
/* A register is set; normally CC is set to show that no
test insn is needed. Catch the exceptions. */
/* If not to cc0, then no "set"s in non-natural mode give
ok cc0... */
if (GET_MODE_SIZE (GET_MODE (SET_DEST (exp))) > UNITS_PER_WORD
|| GET_MODE_CLASS (GET_MODE (SET_DEST (exp))) == MODE_FLOAT)
{
/* ... except add:s and sub:s in DImode. */
if (GET_MODE (SET_DEST (exp)) == DImode
&& (GET_CODE (SET_SRC (exp)) == PLUS
|| GET_CODE (SET_SRC (exp)) == MINUS))
{
cc_status.flags = 0;
cc_status.value1 = SET_DEST (exp);
cc_status.value2 = SET_SRC (exp);
if (cris_reg_overlap_mentioned_p (cc_status.value1,
cc_status.value2))
cc_status.value2 = 0;
/* Add and sub may set V, which gets us
unoptimizable results in "gt" and "le" condition
codes. */
cc_status.flags |= CC_NO_OVERFLOW;
return;
}
}
else if (SET_SRC (exp) == const0_rtx)
{
/* There's no CC0 change when clearing a register or
memory. Just check for overlap. */
if (cc_status.value1
&& modified_in_p (cc_status.value1, insn))
cc_status.value1 = 0;
if (cc_status.value2
&& modified_in_p (cc_status.value2, insn))
cc_status.value2 = 0;
if (GET_CODE (SET_SRC (exp)) == COMPARE)
{
if (!REG_P (XEXP (SET_SRC (exp), 0))
&& XEXP (SET_SRC (exp), 1) != const0_rtx)
/* For some reason gcc will not canonicalize compare
operations, reversing the sign by itself if
operands are in wrong order. */
/* (But NOT inverted; eq is still eq.) */
cc_status.flags = CC_REVERSED;
/* This seems to be overlooked by gcc. FIXME: Check again.
FIXME: Is it really safe? */
cc_status.value2
= gen_rtx_MINUS (GET_MODE (SET_SRC (exp)),
XEXP (SET_SRC (exp), 0),
XEXP (SET_SRC (exp), 1));
}
return;
}
else if (REG_P (SET_DEST (exp))
|| (GET_CODE (SET_DEST (exp)) == STRICT_LOW_PART
&& REG_P (XEXP (SET_DEST (exp), 0))))
{
/* A register is set; normally CC is set to show that no
test insn is needed. Catch the exceptions. */
return;
}
else
/* If not to cc0, then no "set"s in non-natural mode give
ok cc0... */
if (GET_MODE_SIZE (GET_MODE (SET_DEST (exp))) > UNITS_PER_WORD
|| GET_MODE_CLASS (GET_MODE (SET_DEST (exp))) == MODE_FLOAT)
{
/* ... except add:s and sub:s in DImode. */
if (GET_MODE (SET_DEST (exp)) == DImode
&& (GET_CODE (SET_SRC (exp)) == PLUS
|| GET_CODE (SET_SRC (exp)) == MINUS))
{
cc_status.flags = 0;
CC_STATUS_INIT;
cc_status.value1 = SET_DEST (exp);
cc_status.value2 = SET_SRC (exp);
......@@ -1472,23 +1489,25 @@ cris_notice_update_cc (rtx exp, rtx insn)
cc_status.value2))
cc_status.value2 = 0;
/* Some operations may set V, which gets us
/* Add and sub may set V, which gets us
unoptimizable results in "gt" and "le" condition
codes. */
if (GET_CODE (SET_SRC (exp)) == PLUS
|| GET_CODE (SET_SRC (exp)) == MINUS
|| GET_CODE (SET_SRC (exp)) == NEG)
cc_status.flags |= CC_NO_OVERFLOW;
cc_status.flags |= CC_NO_OVERFLOW;
return;
}
}
else if (MEM_P (SET_DEST (exp))
|| (GET_CODE (SET_DEST (exp)) == STRICT_LOW_PART
&& MEM_P (XEXP (SET_DEST (exp), 0))))
else if (SET_SRC (exp) == const0_rtx
|| (REG_P (SET_SRC (exp))
&& (REGNO (SET_SRC (exp))
> CRIS_LAST_GENERAL_REGISTER))
|| (TARGET_V32
&& GET_CODE (SET_SRC (exp)) == CONST_INT
&& CONST_OK_FOR_LETTER_P (INTVAL (SET_SRC (exp)),
'I')))
{
/* When SET to MEM, then CC is not changed (except for
overlap). */
/* There's no CC0 change for this case. Just check
for overlap. */
if (cc_status.value1
&& modified_in_p (cc_status.value1, insn))
cc_status.value1 = 0;
......@@ -1499,50 +1518,166 @@ cris_notice_update_cc (rtx exp, rtx insn)
return;
}
else
{
CC_STATUS_INIT;
cc_status.value1 = SET_DEST (exp);
cc_status.value2 = SET_SRC (exp);
if (cris_reg_overlap_mentioned_p (cc_status.value1,
cc_status.value2))
cc_status.value2 = 0;
/* Some operations may set V, which gets us
unoptimizable results in "gt" and "le" condition
codes. */
if (GET_CODE (SET_SRC (exp)) == PLUS
|| GET_CODE (SET_SRC (exp)) == MINUS
|| GET_CODE (SET_SRC (exp)) == NEG)
cc_status.flags |= CC_NO_OVERFLOW;
/* For V32, nothing with a register destination sets
C and V usefully. */
if (TARGET_V32)
cc_status.flags |= CC_NO_OVERFLOW;
return;
}
}
else if (MEM_P (SET_DEST (exp))
|| (GET_CODE (SET_DEST (exp)) == STRICT_LOW_PART
&& MEM_P (XEXP (SET_DEST (exp), 0))))
{
/* When SET to MEM, then CC is not changed (except for
overlap). */
if (cc_status.value1
&& modified_in_p (cc_status.value1, insn))
cc_status.value1 = 0;
if (cc_status.value2
&& modified_in_p (cc_status.value2, insn))
cc_status.value2 = 0;
return;
}
else if (GET_CODE (exp) == PARALLEL)
}
else if (GET_CODE (exp) == PARALLEL)
{
if (GET_CODE (XVECEXP (exp, 0, 0)) == SET
&& GET_CODE (XVECEXP (exp, 0, 1)) == SET
&& REG_P (XEXP (XVECEXP (exp, 0, 1), 0)))
{
if (GET_CODE (XVECEXP (exp, 0, 0)) == SET
&& GET_CODE (XVECEXP (exp, 0, 1)) == SET
&& REG_P (XEXP (XVECEXP (exp, 0, 1), 0)))
if (REG_P (XEXP (XVECEXP (exp, 0, 0), 0))
&& MEM_P (XEXP (XVECEXP (exp, 0, 0), 1)))
{
if (REG_P (XEXP (XVECEXP (exp, 0, 0), 0))
&& MEM_P (XEXP (XVECEXP (exp, 0, 0), 1)))
{
/* For "move.S [rx=ry+o],rz", say CC reflects
value1=rz and value2=[rx] */
cc_status.value1 = XEXP (XVECEXP (exp, 0, 0), 0);
cc_status.value2
= replace_equiv_address (XEXP (XVECEXP (exp, 0, 0), 1),
XEXP (XVECEXP (exp, 0, 1), 0));
cc_status.flags = 0;
/* Huh? A side-effect cannot change the destination
register. */
if (cris_reg_overlap_mentioned_p (cc_status.value1,
cc_status.value2))
internal_error ("internal error: sideeffect-insn affecting main effect");
return;
}
else if ((REG_P (XEXP (XVECEXP (exp, 0, 0), 1))
|| XEXP (XVECEXP (exp, 0, 0), 1) == const0_rtx)
&& MEM_P (XEXP (XVECEXP (exp, 0, 0), 0)))
{
/* For "move.S rz,[rx=ry+o]" and "clear.S [rx=ry+o]",
say flags are not changed, except for overlap. */
if (cc_status.value1
&& modified_in_p (cc_status.value1, insn))
cc_status.value1 = 0;
if (cc_status.value2
&& modified_in_p (cc_status.value2, insn))
cc_status.value2 = 0;
CC_STATUS_INIT;
/* For "move.S [rx=ry+o],rz", say CC reflects
value1=rz and value2=[rx] */
cc_status.value1 = XEXP (XVECEXP (exp, 0, 0), 0);
cc_status.value2
= replace_equiv_address (XEXP (XVECEXP (exp, 0, 0), 1),
XEXP (XVECEXP (exp, 0, 1), 0));
/* Huh? A side-effect cannot change the destination
register. */
if (cris_reg_overlap_mentioned_p (cc_status.value1,
cc_status.value2))
internal_error ("internal error: sideeffect-insn affecting main effect");
/* For V32, moves to registers don't set C and V. */
if (TARGET_V32)
cc_status.flags |= CC_NO_OVERFLOW;
return;
}
else if ((REG_P (XEXP (XVECEXP (exp, 0, 0), 1))
|| XEXP (XVECEXP (exp, 0, 0), 1) == const0_rtx)
&& MEM_P (XEXP (XVECEXP (exp, 0, 0), 0)))
{
/* For "move.S rz,[rx=ry+o]" and "clear.S [rx=ry+o]",
say flags are not changed, except for overlap. */
if (cc_status.value1
&& modified_in_p (cc_status.value1, insn))
cc_status.value1 = 0;
return;
}
if (cc_status.value2
&& modified_in_p (cc_status.value2, insn))
cc_status.value2 = 0;
return;
}
}
break;
}
/* If we got here, the case wasn't covered by the code above. */
CC_STATUS_INIT;
}
/* This function looks into the pattern to see how this insn affects
condition codes.
Used when to eliminate test insns before a condition-code user,
such as a "scc" insn or a conditional branch. This includes
checking if the entities that cc was updated by, are changed by the
operation.
Currently a jumble of the old peek-inside-the-insn and the newer
check-cc-attribute methods. */
void
cris_notice_update_cc (rtx exp, rtx insn)
{
enum attr_cc attrval = get_attr_cc (insn);
/* Check if user specified "-mcc-init" as a bug-workaround. Remember
to still set CC_REVERSED as below, since that's required by some
compare insn alternatives. (FIXME: GCC should do this virtual
operand swap by itself.) A test-case that may otherwise fail is
gcc.c-torture/execute/20000217-1.c -O0 and -O1. */
if (TARGET_CCINIT)
{
CC_STATUS_INIT;
if (attrval == CC_REV)
cc_status.flags = CC_REVERSED;
return;
}
/* Slowly, we're converting to using attributes to control the setting
of condition-code status. */
switch (attrval)
{
case CC_NONE:
/* Even if it is "none", a setting may clobber a previous
cc-value, so check. */
if (GET_CODE (exp) == SET)
{
if (cc_status.value1
&& modified_in_p (cc_status.value1, insn))
cc_status.value1 = 0;
if (cc_status.value2
&& modified_in_p (cc_status.value2, insn))
cc_status.value2 = 0;
}
return;
case CC_CLOBBER:
CC_STATUS_INIT;
return;
case CC_REV:
case CC_NOOV32:
case CC_NORMAL:
cris_normal_notice_update_cc (exp, insn);
/* The "test" insn doesn't clear (carry and) overflow on V32. We
can change bge => bpl and blt => bmi by passing on to the cc0
user that V should not be considered; bgt and ble are taken
care of by other methods (see {tst,cmp}{si,hi,qi}). */
if (attrval == CC_NOOV32 && TARGET_V32)
cc_status.flags |= CC_NO_OVERFLOW;
return;
default:
internal_error ("unknown cc_attr value");
......@@ -1575,6 +1710,10 @@ cris_simple_epilogue (void)
|| !TARGET_PROLOGUE_EPILOGUE)
return false;
/* Can't return from stacked return address with v32. */
if (TARGET_V32 && cris_return_address_on_stack ())
return false;
if (current_function_uses_pic_offset_table)
{
push_topmost_sequence ();
......@@ -1901,6 +2040,49 @@ cris_side_effect_mode_ok (enum rtx_code code, rtx *ops,
internal_error ("internal error: cris_side_effect_mode_ok with bad operands");
}
/* Whether next_cc0_user of insn is LE or GT or requires a real compare
insn for other reasons. */
bool
cris_cc0_user_requires_cmp (rtx insn)
{
rtx cc0_user = NULL;
rtx body;
rtx set;
gcc_assert (insn != NULL);
if (!TARGET_V32)
return false;
cc0_user = next_cc0_user (insn);
if (cc0_user == NULL)
return false;
body = PATTERN (cc0_user);
set = single_set (cc0_user);
/* Users can be sCC and bCC. */
if (JUMP_P (cc0_user)
&& GET_CODE (body) == SET
&& SET_DEST (body) == pc_rtx
&& GET_CODE (SET_SRC (body)) == IF_THEN_ELSE
&& XEXP (XEXP (SET_SRC (body), 0), 0) == cc0_rtx)
{
return
GET_CODE (XEXP (SET_SRC (body), 0)) == GT
|| GET_CODE (XEXP (SET_SRC (body), 0)) == LE;
}
else if (set)
{
return
GET_CODE (SET_SRC (body)) == GT
|| GET_CODE (SET_SRC (body)) == LE;
}
gcc_unreachable ();
}
/* The function reg_overlap_mentioned_p in CVS (still as of 2001-05-16)
does not handle the case where the IN operand is strict_low_part; it
does handle it for X. Test-case in Axis-20010516. This function takes
......@@ -1931,10 +2113,12 @@ cris_target_asm_named_section (const char *name, unsigned int flags,
default_elf_asm_named_section (name, flags, decl);
}
/* Return TRUE iff X is a CONST valid for e.g. indexing. */
/* Return TRUE iff X is a CONST valid for e.g. indexing.
ANY_OPERAND is 0 if X is in a CALL_P insn or movsi, 1
elsewhere. */
bool
cris_valid_pic_const (rtx x)
cris_valid_pic_const (rtx x, bool any_operand)
{
gcc_assert (flag_pic);
......@@ -1955,14 +2139,20 @@ cris_valid_pic_const (rtx x)
/* Handle (const (plus (unspec .. UNSPEC_GOTREL) (const_int ...))). */
if (GET_CODE (x) == PLUS
&& GET_CODE (XEXP (x, 0)) == UNSPEC
&& XINT (XEXP (x, 0), 1) == CRIS_UNSPEC_GOTREL
&& (XINT (XEXP (x, 0), 1) == CRIS_UNSPEC_GOTREL
|| XINT (XEXP (x, 0), 1) == CRIS_UNSPEC_PCREL)
&& CONST_INT_P (XEXP (x, 1)))
x = XEXP (x, 0);
if (GET_CODE (x) == UNSPEC)
switch (XINT (x, 1))
{
case CRIS_UNSPEC_PLT:
/* A PCREL operand is only valid for call and movsi. */
case CRIS_UNSPEC_PLT_PCREL:
case CRIS_UNSPEC_PCREL:
return !any_operand;
case CRIS_UNSPEC_PLT_GOTREL:
case CRIS_UNSPEC_PLTGOTREAD:
case CRIS_UNSPEC_GOTREAD:
case CRIS_UNSPEC_GOTREL:
......@@ -1984,10 +2174,10 @@ cris_pic_symbol_type_of (rtx x)
{
case SYMBOL_REF:
return SYMBOL_REF_LOCAL_P (x)
? cris_gotrel_symbol : cris_got_symbol;
? cris_rel_symbol : cris_got_symbol;
case LABEL_REF:
return cris_gotrel_symbol;
return cris_rel_symbol;
case CONST:
return cris_pic_symbol_type_of (XEXP (x, 0));
......@@ -2028,7 +2218,45 @@ int
cris_legitimate_pic_operand (rtx x)
{
/* Symbols are not valid PIC operands as-is; just constants. */
return cris_valid_pic_const (x);
return cris_valid_pic_const (x, true);
}
/* The ASM_OUTPUT_CASE_END worker. */
void
cris_asm_output_case_end (FILE *stream, int num, rtx table)
{
if (TARGET_V32)
{
rtx whole_jump_insn = PATTERN (PREV_INSN (PREV_INSN (table)));
/* This can be a SEQUENCE, meaning the delay-slot of the jump is
filled. */
rtx parallel_jump
= (GET_CODE (whole_jump_insn) == SEQUENCE
? PATTERN (XVECEXP (whole_jump_insn, 0, 0)) : whole_jump_insn);
asm_fprintf (stream,
"\t.word %LL%d-.%s\n",
CODE_LABEL_NUMBER (XEXP (XEXP (XEXP (XVECEXP
(parallel_jump, 0, 0),
1), 2), 0)),
(TARGET_PDEBUG ? "; default" : ""));
return;
}
asm_fprintf (stream,
"\t.word %LL%d-%LL%d%s\n",
CODE_LABEL_NUMBER (XEXP
(XEXP
(XEXP
(XVECEXP
(PATTERN
(PREV_INSN
(PREV_INSN (table))), 0, 0), 1),
2), 0)),
num,
(TARGET_PDEBUG ? "; default" : ""));
}
/* TARGET_HANDLE_OPTION worker. We just store the values into local
......@@ -2128,7 +2356,7 @@ cris_override_options (void)
|| strcmp ("etrax100lx", cris_cpu_str) == 0)
cris_cpu_version = 10;
if (cris_cpu_version < 0 || cris_cpu_version > 10)
if (cris_cpu_version < 0 || cris_cpu_version > 32)
error ("unknown CRIS version specification in -march= or -mcpu= : %s",
cris_cpu_str);
......@@ -2164,7 +2392,7 @@ cris_override_options (void)
|| strcmp ("etrax100lx", cris_tune_str) == 0)
cris_tune = 10;
if (cris_tune < 0 || cris_tune > 10)
if (cris_tune < 0 || cris_tune > 32)
error ("unknown CRIS cpu version specification in -mtune= : %s",
cris_tune_str);
......@@ -2176,6 +2404,9 @@ cris_override_options (void)
| MASK_DATA_ALIGN | MASK_ALIGN_BY_32);
}
if (cris_cpu_version >= CRIS_CPU_V32)
target_flags &= ~(MASK_SIDE_EFFECT_PREFIXES|MASK_MUL_BUG);
if (flag_pic)
{
/* Use error rather than warning, so invalid use is easily
......@@ -2229,15 +2460,28 @@ cris_asm_output_mi_thunk (FILE *stream,
const char *name = XSTR (XEXP (DECL_RTL (funcdecl), 0), 0);
name = (* targetm.strip_name_encoding) (name);
fprintf (stream, "add.d ");
assemble_name (stream, name);
fprintf (stream, "%s,$pc\n", CRIS_PLT_PCOFFSET_SUFFIX);
if (TARGET_V32)
{
fprintf (stream, "\tba ");
assemble_name (stream, name);
fprintf (stream, "%s\n", CRIS_PLT_PCOFFSET_SUFFIX);
}
else
{
fprintf (stream, "add.d ");
assemble_name (stream, name);
fprintf (stream, "%s,$pc\n", CRIS_PLT_PCOFFSET_SUFFIX);
}
}
else
{
fprintf (stream, "jump ");
assemble_name (stream, XSTR (XEXP (DECL_RTL (funcdecl), 0), 0));
fprintf (stream, "\n");
if (TARGET_V32)
fprintf (stream, "\tnop\n");
}
}
......@@ -2374,7 +2618,7 @@ cris_split_movdx (rtx *operands)
= alloc_EXPR_LIST (REG_INC, XEXP (XEXP (mem, 0), 0),
REG_NOTES (insn));
mem = change_address (src, SImode, addr);
mem = copy_rtx (mem);
insn
= gen_rtx_SET (VOIDmode,
operand_subword (dest, 1, TRUE, mode), mem);
......@@ -2438,7 +2682,7 @@ cris_split_movdx (rtx *operands)
= alloc_EXPR_LIST (REG_INC, XEXP (XEXP (mem, 0), 0),
REG_NOTES (insn));
mem = change_address (dest, SImode, addr);
mem = copy_rtx (mem);
insn
= gen_rtx_SET (VOIDmode,
mem,
......@@ -2924,7 +3168,7 @@ cris_expand_epilogue (void)
the return address on the stack. */
if (return_address_on_stack && pretend == 0)
{
if (current_function_calls_eh_return)
if (TARGET_V32 || current_function_calls_eh_return)
{
rtx mem;
rtx insn;
......@@ -2940,10 +3184,11 @@ cris_expand_epilogue (void)
REG_NOTES (insn)
= alloc_EXPR_LIST (REG_INC, stack_pointer_rtx, REG_NOTES (insn));
emit_insn (gen_addsi3 (stack_pointer_rtx,
stack_pointer_rtx,
gen_rtx_raw_REG (SImode,
CRIS_STACKADJ_REG)));
if (current_function_calls_eh_return)
emit_insn (gen_addsi3 (stack_pointer_rtx,
stack_pointer_rtx,
gen_rtx_raw_REG (SImode,
CRIS_STACKADJ_REG)));
cris_expand_return (false);
}
else
......@@ -3002,6 +3247,12 @@ cris_gen_movem_load (rtx src, rtx nregs_rtx, int nprefix)
unsigned int regno = nregs - 1;
int regno_inc = -1;
if (TARGET_V32)
{
regno = 0;
regno_inc = 1;
}
if (GET_CODE (srcreg) == POST_INC)
srcreg = XEXP (srcreg, 0);
......@@ -3055,6 +3306,12 @@ cris_emit_movem_store (rtx dest, rtx nregs_rtx, int increment,
unsigned int regno = nregs - 1;
int regno_inc = -1;
if (TARGET_V32)
{
regno = 0;
regno_inc = 1;
}
if (GET_CODE (destreg) == POST_INC)
increment += nregs * 4;
......@@ -3191,32 +3448,61 @@ cris_expand_pic_call_address (rtx *opp)
/* For local symbols (non-PLT), just get the plain symbol
reference into a register. For symbols that can be PLT, make
them PLT. */
if (t == cris_gotrel_symbol)
op = force_reg (Pmode, op);
if (t == cris_rel_symbol)
{
/* For v32, we're fine as-is; just PICify the symbol. Forcing
into a register caused performance regression for 3.2.1,
observable in __floatdidf and elsewhere in libgcc. */
if (TARGET_V32)
{
rtx sym = GET_CODE (op) != CONST ? op : get_related_value (op);
HOST_WIDE_INT offs = get_integer_term (op);
/* We can't get calls to sym+N, N integer, can we? */
gcc_assert (offs == 0);
op = gen_rtx_CONST (Pmode,
gen_rtx_UNSPEC (Pmode, gen_rtvec (1, sym),
CRIS_UNSPEC_PCREL));
}
else
op = force_reg (Pmode, op);
}
else if (t == cris_got_symbol)
{
if (TARGET_AVOID_GOTPLT)
{
/* Change a "jsr sym" into (allocate register rM, rO)
"move.d (const (unspec [sym] CRIS_UNSPEC_PLT)),rM"
"add.d rPIC,rM,rO", "jsr rO". */
"move.d (const (unspec [sym rPIC] CRIS_UNSPEC_PLT_GOTREL)),rM"
"add.d rPIC,rM,rO", "jsr rO" for pre-v32 and
"jsr (const (unspec [sym rPIC] CRIS_UNSPEC_PLT_PCREL))"
for v32. */
rtx tem, rm, ro;
gcc_assert (can_create_pseudo_p ());
current_function_uses_pic_offset_table = 1;
tem = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, op), CRIS_UNSPEC_PLT);
rm = gen_reg_rtx (Pmode);
emit_move_insn (rm, gen_rtx_CONST (Pmode, tem));
ro = gen_reg_rtx (Pmode);
if (expand_binop (Pmode, add_optab, rm,
pic_offset_table_rtx,
ro, 0, OPTAB_LIB_WIDEN) != ro)
internal_error ("expand_binop failed in movsi got");
op = ro;
tem = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, op),
TARGET_V32
? CRIS_UNSPEC_PLT_PCREL
: CRIS_UNSPEC_PLT_GOTREL);
tem = gen_rtx_CONST (Pmode, tem);
if (TARGET_V32)
op = tem;
else
{
rm = gen_reg_rtx (Pmode);
emit_move_insn (rm, tem);
ro = gen_reg_rtx (Pmode);
if (expand_binop (Pmode, add_optab, rm,
pic_offset_table_rtx,
ro, 0, OPTAB_LIB_WIDEN) != ro)
internal_error ("expand_binop failed in movsi got");
op = ro;
}
}
else
{
/* Change a "jsr sym" into (allocate register rM, rO)
"move.d (const (unspec [sym] CRIS_UNSPEC_PLTGOT)),rM"
"move.d (const (unspec [sym] CRIS_UNSPEC_PLTGOTREAD)),rM"
"add.d rPIC,rM,rO" "jsr [rO]" with the memory access
marked as not trapping and not aliasing. No "move.d
[rO],rP" as that would invite to re-use of a value
......@@ -3300,7 +3586,7 @@ cris_asm_output_symbol_ref (FILE *file, rtx x)
assemble_name (file, str);
/* Sanity check. */
if (! current_function_uses_pic_offset_table)
if (!TARGET_V32 && !current_function_uses_pic_offset_table)
output_operand_lossage ("PIC register isn't set up");
}
else
......@@ -3317,7 +3603,7 @@ cris_asm_output_label_ref (FILE *file, char *buf)
assemble_name (file, buf);
/* Sanity check. */
if (! current_function_uses_pic_offset_table)
if (!TARGET_V32 && !current_function_uses_pic_offset_table)
internal_error ("emitting PIC operand, but PIC register isn't set up");
}
else
......@@ -3341,11 +3627,25 @@ cris_output_addr_const_extra (FILE *file, rtx xconst)
output_addr_const (file, x);
switch (XINT (xconst, 1))
{
case CRIS_UNSPEC_PLT:
case CRIS_UNSPEC_PCREL:
/* We only get this with -fpic/PIC to tell it apart from an
invalid symbol. We can't tell here, but it should only
be the operand of a call or movsi. */
gcc_assert (TARGET_V32 && flag_pic);
break;
case CRIS_UNSPEC_PLT_PCREL:
gcc_assert (TARGET_V32);
fprintf (file, ":PLT");
break;
case CRIS_UNSPEC_PLT_GOTREL:
gcc_assert (!TARGET_V32);
fprintf (file, ":PLTG");
break;
case CRIS_UNSPEC_GOTREL:
gcc_assert (!TARGET_V32);
fprintf (file, ":GOTOFF");
break;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment