CRIS epilogue as RTL.

* config/cris/cris.md: Change all 0 in unspec 0 to
	CRIS_UNSPEC_PLT.
 	(CRIS_UNSPEC_PLT, CRIS_UNSPEC_FRAME_DEALLOC): New constants.
	("*cris_load_multiple", "cris_frame_deallocated_barrier"): New
	patterns.
	("return"): Change to define_expand.  Call cris_expand_return for
	actual expansion.
	("*return_expanded"): New pattern.
	("epilogue"): New define_expand.
	* config/cris/cris.h (PREDICATE_CODES): Add
	cris_load_multiple_op.
	* config/cris/cris.c (ASSERT_PLT_UNSPEC): Correct test for unspec
	type.
	(enum cris_retinsn_type): New.
	(struct machine_function): New member return_type.
	(TARGET_ASM_FUNCTION_EPILOGUE): Don't override.
	(cris_target_asm_function_epilogue): Remove, moving RTLified
	contents to...
	(cris_expand_epilogue): New function.
	(cris_reg_saved_in_regsave_area, cris_movem_load_rest_p,
	(cris_gen_movem_load, cris_load_multiple_op)
	(cris_return_address_on_stack_for_return, cris_expand_return): New
	functions.
	(cris_target_asm_function_prologue)
	(cris_initial_frame_pointer_offset): Call
	cris_reg_saved_in_regsave_area instead of complicated expression.
	Call cris_return_address_on_stack instead of an expression.
	(cris_print_operand) <case 'o', case 'O'>: New cases.
	(cris_return_address_on_stack): Change return-type to bool.
	(cris_simple_epilogue): Ditto.  Return false if registers are
	saved.
	* config/cris/cris-protos.h (cris_simple_epilogue)
	(cris_return_address_on_stack): Adjust prototype return type.
	(cris_gen_movem_load, cris_expand_epilogue, cris_expand_return)
	(cris_return_address_on_stack_for_return): New prototypes.

From-SVN: r97580
This commit is contained in:
Hans-Peter Nilsson 2005-04-04 22:43:34 +00:00 committed by Hans-Peter Nilsson
parent 87cd358654
commit 0453995463
5 changed files with 565 additions and 362 deletions

View File

@ -1,3 +1,42 @@
2005-04-05 Hans-Peter Nilsson <hp@axis.com>
CRIS epilogue as RTL.
* config/cris/cris.md: Change all 0 in unspec 0 to
CRIS_UNSPEC_PLT.
(CRIS_UNSPEC_PLT, CRIS_UNSPEC_FRAME_DEALLOC): New constants.
("*cris_load_multiple", "cris_frame_deallocated_barrier"): New
patterns.
("return"): Change to define_expand. Call cris_expand_return for
actual expansion.
("*return_expanded"): New pattern.
("epilogue"): New define_expand.
* config/cris/cris.h (PREDICATE_CODES): Add
cris_load_multiple_op.
* config/cris/cris.c (ASSERT_PLT_UNSPEC): Correct test for unspec
type.
(enum cris_retinsn_type): New.
(struct machine_function): New member return_type.
(TARGET_ASM_FUNCTION_EPILOGUE): Don't override.
(cris_target_asm_function_epilogue): Remove, moving RTLified
contents to...
(cris_expand_epilogue): New function.
(cris_reg_saved_in_regsave_area, cris_movem_load_rest_p,
(cris_gen_movem_load, cris_load_multiple_op)
(cris_return_address_on_stack_for_return, cris_expand_return): New
functions.
(cris_target_asm_function_prologue)
(cris_initial_frame_pointer_offset): Call
cris_reg_saved_in_regsave_area instead of complicated expression.
Call cris_return_address_on_stack instead of an expression.
(cris_print_operand) <case 'o', case 'O'>: New cases.
(cris_return_address_on_stack): Change return-type to bool.
(cris_simple_epilogue): Ditto. Return false if registers are
saved.
* config/cris/cris-protos.h (cris_simple_epilogue)
(cris_return_address_on_stack): Adjust prototype return type.
(cris_gen_movem_load, cris_expand_epilogue, cris_expand_return)
(cris_return_address_on_stack_for_return): New prototypes.
2005-04-04 Kazu Hirata <kazu@cs.umass.edu>
* config/frv/frv.h (PREDICATE_CODES): Add CONST to

View File

@ -27,7 +27,7 @@ Boston, MA 02111-1307, USA. */
#endif
extern void cris_conditional_register_usage (void);
extern int cris_simple_epilogue (void);
extern bool cris_simple_epilogue (void);
#ifdef RTX_CODE
extern const char *cris_op_str (rtx);
extern void cris_notice_update_cc (rtx, rtx);
@ -44,12 +44,14 @@ extern int cris_symbol (rtx);
extern void cris_asm_output_symbol_ref (FILE *, rtx);
extern bool cris_output_addr_const_extra (FILE *, rtx);
extern int cris_cfun_uses_pic_table (void);
extern rtx cris_gen_movem_load (rtx, rtx, int);
#endif /* RTX_CODE */
extern void cris_asm_output_label_ref (FILE *, char *);
extern void cris_target_asm_named_section (const char *, unsigned int, tree);
extern int cris_return_address_on_stack (void);
extern void cris_expand_epilogue (void);
extern void cris_expand_return (bool);
extern bool cris_return_address_on_stack_for_return (void);
extern bool cris_return_address_on_stack (void);
extern void cris_pragma_expand_mul (struct cpp_reader *);
/* Need one that returns an int; usable in expressions. */

View File

@ -54,7 +54,7 @@ Boston, MA 02111-1307, USA. */
#define ASSERT_PLT_UNSPEC(x) \
do \
{ \
if (XEXP (x, 1) != NULL_RTX \
if (XINT (x, 1) != CRIS_UNSPEC_PLT \
|| (GET_CODE (XVECEXP (x, 0, 0)) != SYMBOL_REF \
&& GET_CODE (XVECEXP (x, 0, 0)) != LABEL_REF)) \
abort (); \
@ -67,10 +67,14 @@ Boston, MA 02111-1307, USA. */
return; \
} while (0)
enum cris_retinsn_type
{ CRIS_RETINSN_UNKNOWN = 0, CRIS_RETINSN_RET, CRIS_RETINSN_JUMP };
/* Per-function machine data. */
struct machine_function GTY(())
{
int needs_return_address_on_stack;
enum cris_retinsn_type return_type;
};
/* This little fix suppresses the 'u' or 's' when '%e' in assembly
@ -109,10 +113,12 @@ static int saved_regs_mentioned (rtx);
static void cris_target_asm_function_prologue (FILE *, HOST_WIDE_INT);
static void cris_target_asm_function_epilogue (FILE *, HOST_WIDE_INT);
static void cris_operand_lossage (const char *, rtx);
static int cris_reg_saved_in_regsave_area (unsigned int, bool);
static int cris_movem_load_rest_p (rtx, int);
static void cris_asm_output_mi_thunk
(FILE *, tree, HOST_WIDE_INT, HOST_WIDE_INT, tree);
@ -168,9 +174,6 @@ int cris_cpu_version = CRIS_DEFAULT_CPU_VERSION;
#undef TARGET_ASM_FUNCTION_PROLOGUE
#define TARGET_ASM_FUNCTION_PROLOGUE cris_target_asm_function_prologue
#undef TARGET_ASM_FUNCTION_EPILOGUE
#define TARGET_ASM_FUNCTION_EPILOGUE cris_target_asm_function_epilogue
#undef TARGET_ASM_OUTPUT_MI_THUNK
#define TARGET_ASM_OUTPUT_MI_THUNK cris_asm_output_mi_thunk
#undef TARGET_ASM_CAN_OUTPUT_MI_THUNK
@ -454,7 +457,7 @@ cris_general_operand_or_plt_symbol (rtx op, enum machine_mode mode)
(MEM (cris_general_operand_or_symbol)). The second one isn't a valid
memory_operand, so we need this predicate to recognize call
destinations before we change them to a PLT operand (by wrapping in
UNSPEC 0). */
UNSPEC CRIS_UNSPEC_PLT). */
int
cris_mem_call_operand (rtx op, enum machine_mode mode)
@ -472,6 +475,94 @@ cris_mem_call_operand (rtx op, enum machine_mode mode)
return cris_general_operand_or_symbol (xmem, GET_MODE (op));
}
/* Helper for cris_load_multiple_op and cris_ret_movem_op. */
static int
cris_movem_load_rest_p (rtx op, int offs)
{
unsigned int reg_count = XVECLEN (op, 0) - offs;
rtx src_addr;
int i;
rtx elt;
int setno;
int regno_dir = 1;
unsigned int regno = 0;
/* Perform a quick check so we don't blow up below. FIXME: Adjust for
other than (MEM reg). */
if (reg_count <= 1
|| GET_CODE (XVECEXP (op, 0, offs)) != SET
|| GET_CODE (SET_DEST (XVECEXP (op, 0, offs))) != REG
|| GET_CODE (SET_SRC (XVECEXP (op, 0, offs))) != MEM)
return 0;
/* Check a possible post-inc indicator. */
if (GET_CODE (SET_SRC (XVECEXP (op, 0, offs + 1))) == PLUS)
{
rtx reg = XEXP (SET_SRC (XVECEXP (op, 0, offs + 1)), 0);
rtx inc = XEXP (SET_SRC (XVECEXP (op, 0, offs + 1)), 1);
reg_count--;
if (reg_count == 1
|| !REG_P (reg)
|| !REG_P (SET_DEST (XVECEXP (op, 0, offs + 1)))
|| REGNO (reg) != REGNO (SET_DEST (XVECEXP (op, 0, offs + 1)))
|| GET_CODE (inc) != CONST_INT
|| INTVAL (inc) != (HOST_WIDE_INT) reg_count * 4)
return 0;
i = offs + 2;
}
else
i = offs + 1;
/* FIXME: These two only for pre-v32. */
regno_dir = -1;
regno = reg_count - 1;
elt = XVECEXP (op, 0, offs);
src_addr = XEXP (SET_SRC (elt), 0);
if (GET_CODE (elt) != SET
|| GET_CODE (SET_DEST (elt)) != REG
|| GET_MODE (SET_DEST (elt)) != SImode
|| REGNO (SET_DEST (elt)) != regno
|| GET_CODE (SET_SRC (elt)) != MEM
|| GET_MODE (SET_SRC (elt)) != SImode
|| !memory_address_p (SImode, src_addr))
return 0;
for (setno = 1; i < XVECLEN (op, 0); setno++, i++)
{
rtx elt = XVECEXP (op, 0, i);
regno += regno_dir;
if (GET_CODE (elt) != SET
|| GET_CODE (SET_DEST (elt)) != REG
|| GET_MODE (SET_DEST (elt)) != SImode
|| REGNO (SET_DEST (elt)) != regno
|| GET_CODE (SET_SRC (elt)) != MEM
|| GET_MODE (SET_SRC (elt)) != SImode
|| GET_CODE (XEXP (SET_SRC (elt), 0)) != PLUS
|| ! rtx_equal_p (XEXP (XEXP (SET_SRC (elt), 0), 0), src_addr)
|| GET_CODE (XEXP (XEXP (SET_SRC (elt), 0), 1)) != CONST_INT
|| INTVAL (XEXP (XEXP (SET_SRC (elt), 0), 1)) != setno * 4)
return 0;
}
return 1;
}
/* Predicate for the parallel contents in a movem from-memory. */
int
cris_load_multiple_op (op, mode)
rtx op;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
return cris_movem_load_rest_p (op, 0);
}
/* The CONDITIONAL_REGISTER_USAGE worker. */
void
@ -657,6 +748,33 @@ cris_fatal (char *arg)
return 0;
}
/* Return nonzero if REGNO is an ordinary register that *needs* to be
saved together with other registers, possibly by a MOVEM instruction,
or is saved for target-independent reasons. There may be
target-dependent reasons to save the register anyway; this is just a
wrapper for a complicated conditional. */
static int
cris_reg_saved_in_regsave_area (unsigned int regno, bool got_really_used)
{
return
(((regs_ever_live[regno]
&& !call_used_regs[regno])
|| (regno == PIC_OFFSET_TABLE_REGNUM
&& (got_really_used
/* It is saved anyway, if there would be a gap. */
|| (flag_pic
&& regs_ever_live[regno + 1]
&& !call_used_regs[regno + 1]))))
&& (regno != FRAME_POINTER_REGNUM || !frame_pointer_needed)
&& regno != CRIS_SRP_REGNUM)
|| (current_function_calls_eh_return
&& (regno == EH_RETURN_DATA_REGNO (0)
|| regno == EH_RETURN_DATA_REGNO (1)
|| regno == EH_RETURN_DATA_REGNO (2)
|| regno == EH_RETURN_DATA_REGNO (3)));
}
/* This variable belongs to cris_target_asm_function_prologue but must
be located outside it for GTY reasons. */
static GTY(()) unsigned long cfa_label_num = 0;
@ -676,9 +794,8 @@ cris_target_asm_function_prologue (FILE *file, HOST_WIDE_INT size)
int faked_args_size = 0;
int cfa_write_offset = 0;
static char cfa_label[30];
int return_address_on_stack
= regs_ever_live[CRIS_SRP_REGNUM]
|| cfun->machine->needs_return_address_on_stack != 0;
bool return_address_on_stack = cris_return_address_on_stack ();
bool got_really_used = current_function_uses_pic_offset_table;
/* Don't do anything if no prologues or epilogues are wanted. */
if (!TARGET_PROLOGUE_EPILOGUE)
@ -771,21 +888,7 @@ cris_target_asm_function_prologue (FILE *file, HOST_WIDE_INT size)
to be saved. */
for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
{
if ((((regs_ever_live[regno]
&& !call_used_regs[regno])
|| (regno == (int) PIC_OFFSET_TABLE_REGNUM
&& (current_function_uses_pic_offset_table
/* It is saved anyway, if there would be a gap. */
|| (flag_pic
&& regs_ever_live[regno + 1]
&& !call_used_regs[regno + 1]))))
&& (regno != FRAME_POINTER_REGNUM || !frame_pointer_needed)
&& regno != CRIS_SRP_REGNUM)
|| (current_function_calls_eh_return
&& (regno == EH_RETURN_DATA_REGNO (0)
|| regno == EH_RETURN_DATA_REGNO (1)
|| regno == EH_RETURN_DATA_REGNO (2)
|| regno == EH_RETURN_DATA_REGNO (3))))
if (cris_reg_saved_in_regsave_area (regno, got_really_used))
{
/* Check if movem may be used for registers so far. */
if (regno == last_movem_reg + 1)
@ -997,239 +1100,6 @@ saved_regs_mentioned (rtx x)
return 0;
}
/* Textual function epilogue. */
static void
cris_target_asm_function_epilogue (FILE *file, HOST_WIDE_INT size)
{
int regno;
int last_movem_reg = -1;
rtx insn = get_last_insn ();
int argspace_offset = current_function_outgoing_args_size;
int pretend = current_function_pretend_args_size;
int return_address_on_stack
= regs_ever_live[CRIS_SRP_REGNUM]
|| cfun->machine->needs_return_address_on_stack != 0;
char save_last[80];
save_last[0] = 0;
if (!TARGET_PROLOGUE_EPILOGUE)
return;
if (TARGET_PDEBUG)
fprintf (file, ";;\n");
/* Align byte count of stack frame. */
if (TARGET_STACK_ALIGN)
size = TARGET_ALIGN_BY_32 ? (size + 3) & ~3 : (size + 1) & ~1;
/* If the last insn was a BARRIER, we don't have to write any code,
then all returns were covered by "return" insns. */
if (GET_CODE (insn) == NOTE)
insn = prev_nonnote_insn (insn);
if (insn
&& (GET_CODE (insn) == BARRIER
/* We must make sure that the insn really is a "return" and
not a conditional branch. Try to match the return exactly,
and if it doesn't match, assume it is a conditional branch
(and output an epilogue). */
|| (GET_CODE (insn) == JUMP_INSN
&& GET_CODE (PATTERN (insn)) == RETURN)))
{
if (TARGET_PDEBUG)
fprintf (file, ";;;;;\n");
return;
}
/* Check how many saved regs we can movem. They start at r0 and must
be contiguous. */
for (regno = 0;
regno < FIRST_PSEUDO_REGISTER;
regno++)
if ((((regs_ever_live[regno]
&& !call_used_regs[regno])
|| (regno == (int) PIC_OFFSET_TABLE_REGNUM
&& (current_function_uses_pic_offset_table
/* It is saved anyway, if there would be a gap. */
|| (flag_pic
&& regs_ever_live[regno + 1]
&& !call_used_regs[regno + 1]))))
&& (regno != FRAME_POINTER_REGNUM || !frame_pointer_needed)
&& regno != CRIS_SRP_REGNUM)
|| (current_function_calls_eh_return
&& (regno == EH_RETURN_DATA_REGNO (0)
|| regno == EH_RETURN_DATA_REGNO (1)
|| regno == EH_RETURN_DATA_REGNO (2)
|| regno == EH_RETURN_DATA_REGNO (3))))
{
if (regno == last_movem_reg + 1)
last_movem_reg++;
else
break;
}
for (regno = FIRST_PSEUDO_REGISTER - 1;
regno > last_movem_reg;
regno--)
if ((((regs_ever_live[regno]
&& !call_used_regs[regno])
|| (regno == (int) PIC_OFFSET_TABLE_REGNUM
&& (current_function_uses_pic_offset_table
/* It is saved anyway, if there would be a gap. */
|| (flag_pic
&& regs_ever_live[regno + 1]
&& !call_used_regs[regno + 1]))))
&& (regno != FRAME_POINTER_REGNUM || !frame_pointer_needed)
&& regno != CRIS_SRP_REGNUM)
|| (current_function_calls_eh_return
&& (regno == EH_RETURN_DATA_REGNO (0)
|| regno == EH_RETURN_DATA_REGNO (1)
|| regno == EH_RETURN_DATA_REGNO (2)
|| regno == EH_RETURN_DATA_REGNO (3))))
{
if (argspace_offset)
{
/* There is an area for outgoing parameters located before
the saved registers. We have to adjust for that. */
fprintf (file, "\tAdd%s %d,$sp\n",
ADDITIVE_SIZE_MODIFIER (argspace_offset),
argspace_offset);
/* Make sure we only do this once. */
argspace_offset = 0;
}
/* Flush previous non-movem:ed registers. */
if (*save_last)
fprintf (file, save_last);
sprintf (save_last, "\tPop $%s\n", reg_names[regno]);
}
if (last_movem_reg != -1)
{
if (argspace_offset)
{
/* Adjust for the outgoing parameters area, if that's not
handled yet. */
if (*save_last)
{
fprintf (file, save_last);
*save_last = 0;
}
fprintf (file, "\tAdd%s %d,$sp\n",
ADDITIVE_SIZE_MODIFIER (argspace_offset),
argspace_offset);
argspace_offset = 0;
}
/* Flush previous non-movem:ed registers. */
else if (*save_last)
fprintf (file, save_last);
sprintf (save_last, "\tmovem [$sp+],$%s\n", reg_names[last_movem_reg]);
}
/* Restore frame pointer if necessary. */
if (frame_pointer_needed)
{
if (*save_last)
fprintf (file, save_last);
fprintf (file, "\tmove.d $%s,$sp\n",
reg_names[FRAME_POINTER_REGNUM]);
sprintf (save_last, "\tPop $%s\n",
reg_names[FRAME_POINTER_REGNUM]);
}
else
{
/* If there was no frame-pointer to restore sp from, we must
explicitly deallocate local variables. */
/* Handle space for outgoing parameters that hasn't been handled
yet. */
size += argspace_offset;
if (size)
{
if (*save_last)
fprintf (file, save_last);
sprintf (save_last, "\tadd%s "HOST_WIDE_INT_PRINT_DEC",$sp\n",
ADDITIVE_SIZE_MODIFIER (size), size);
}
/* If the size was not in the range for a "quick", we must flush
it here. */
if (size > 63)
{
fprintf (file, save_last);
*save_last = 0;
}
}
/* If this function has no pushed register parameters
(stdargs/varargs), and if it is not a leaf function, then we can
just jump-return here. */
if (return_address_on_stack && pretend == 0)
{
if (*save_last)
fprintf (file, save_last);
*save_last = 0;
if (current_function_calls_eh_return)
{
/* The installed EH-return address is in *this* frame, so we
need to pop it before we return. */
fprintf (file, "\tpop $srp\n");
fprintf (file, "\tret\n");
fprintf (file, "\tadd.d $%s,$sp\n", reg_names[CRIS_STACKADJ_REG]);
}
else
fprintf (file, "\tJump [$sp+]\n");
return;
}
/* Rather than add current_function_calls_eh_return conditions
everywhere in the following code (and not be able to test it
thoroughly), assert the assumption that all usage of
__builtin_eh_return are handled above. */
if (current_function_calls_eh_return)
internal_error ("unexpected function type needing stack adjustment for\
__builtin_eh_return");
/* If we pushed some register parameters, then adjust the stack for
them. */
if (pretend)
{
/* Since srp is stored on the way, we need to restore it first. */
if (return_address_on_stack)
{
if (*save_last)
fprintf (file, save_last);
*save_last = 0;
fprintf (file, "\tpop $srp\n");
}
if (*save_last)
fprintf (file, save_last);
sprintf (save_last, "\tadd%s %d,$sp\n",
ADDITIVE_SIZE_MODIFIER (pretend), pretend);
}
fprintf (file, "\tRet\n");
/* If the GCC did not do it, we have to use whatever insn we have, or
a nop. */
if (*save_last)
fprintf (file, save_last);
else
fprintf (file, "\tnOp\n");
}
/* The PRINT_OPERAND worker. */
void
@ -1272,6 +1142,49 @@ cris_print_operand (FILE *file, rtx x, int code)
cris_pic_sympart_only--;
return;
case 'o':
{
/* A movem modifier working on a parallel; output the register
name. */
int regno;
if (GET_CODE (x) != PARALLEL)
LOSE_AND_RETURN ("invalid operand for 'o' modifier", x);
/* The second item can be (set reg (plus reg const)) to denote a
postincrement. */
regno
= (GET_CODE (SET_SRC (XVECEXP (x, 0, 1))) == PLUS
? XVECLEN (x, 0) - 2
: XVECLEN (x, 0) - 1);
fprintf (file, "$%s", reg_names [regno]);
}
return;
case 'O':
{
/* A similar movem modifier; output the memory operand. */
rtx addr;
if (GET_CODE (x) != PARALLEL)
LOSE_AND_RETURN ("invalid operand for 'O' modifier", x);
/* The lowest mem operand is in the first item, but perhaps it
needs to be output as postincremented. */
addr = GET_CODE (SET_SRC (XVECEXP (x, 0, 0))) == MEM
? XEXP (SET_SRC (XVECEXP (x, 0, 0)), 0)
: XEXP (SET_DEST (XVECEXP (x, 0, 0)), 0);
/* The second item can be a (set reg (plus reg const)) to denote a
post-increment. */
if (GET_CODE (SET_SRC (XVECEXP (x, 0, 1))) == PLUS)
addr = gen_rtx_POST_INC (SImode, addr);
output_address (addr);
}
return;
case 'P':
/* Print the PIC register. Applied to a GOT-less PIC symbol for
sanity. */
@ -1647,10 +1560,21 @@ cris_return_addr_rtx (int count, rtx frameaddr ATTRIBUTE_UNUSED)
/* Accessor used in cris.md:return because cfun->machine isn't available
there. */
int
bool
cris_return_address_on_stack ()
{
return cfun->machine->needs_return_address_on_stack;
return regs_ever_live[CRIS_SRP_REGNUM]
|| cfun->machine->needs_return_address_on_stack;
}
/* Accessor used in cris.md:return because cfun->machine isn't available
there. */
bool
cris_return_address_on_stack_for_return ()
{
return cfun->machine->return_type == CRIS_RETINSN_RET ? false
: cris_return_address_on_stack ();
}
/* This used to be the INITIAL_FRAME_POINTER_OFFSET worker; now only
@ -1663,24 +1587,11 @@ cris_initial_frame_pointer_offset (void)
/* Initial offset is 0 if we don't have a frame pointer. */
int offs = 0;
bool got_really_used = current_function_uses_pic_offset_table;
/* And 4 for each register pushed. */
for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
if ((((regs_ever_live[regno]
&& !call_used_regs[regno])
|| (regno == (int) PIC_OFFSET_TABLE_REGNUM
&& (current_function_uses_pic_offset_table
/* It is saved anyway, if there would be a gap. */
|| (flag_pic
&& regs_ever_live[regno + 1]
&& !call_used_regs[regno + 1]))))
&& (regno != FRAME_POINTER_REGNUM || !frame_pointer_needed)
&& regno != CRIS_SRP_REGNUM)
|| (current_function_calls_eh_return
&& (regno == EH_RETURN_DATA_REGNO (0)
|| regno == EH_RETURN_DATA_REGNO (1)
|| regno == EH_RETURN_DATA_REGNO (2)
|| regno == EH_RETURN_DATA_REGNO (3))))
if (cris_reg_saved_in_regsave_area (regno, got_really_used))
offs += 4;
/* And then, last, we add the locals allocated. */
@ -1709,9 +1620,7 @@ cris_initial_elimination_offset (int fromreg, int toreg)
/* We should be able to use regs_ever_live and related prologue
information here, or alpha should not as well. */
int return_address_on_stack
= regs_ever_live[CRIS_SRP_REGNUM]
|| cfun->machine->needs_return_address_on_stack != 0;
bool return_address_on_stack = cris_return_address_on_stack ();
/* Here we act as if the frame-pointer were needed. */
int ap_fp_offset = 4 + (return_address_on_stack ? 4 : 0);
@ -2003,15 +1912,15 @@ cris_notice_update_cc (rtx exp, rtx insn)
}
/* Return != 0 if the return sequence for the current function is short,
like "ret" or "jump [sp+]". Prior to reloading, we can't tell how
many registers must be saved, so return 0 then. */
like "ret" or "jump [sp+]". Prior to reloading, we can't tell if
registers must be saved, so return 0 then. */
int
bool
cris_simple_epilogue (void)
{
int regno;
int reglimit = STACK_POINTER_REGNUM;
int lastreg = -1;
unsigned int regno;
unsigned int reglimit = STACK_POINTER_REGNUM;
bool got_really_used = current_function_uses_pic_offset_table;
if (! reload_completed
|| frame_pointer_needed
@ -2024,25 +1933,36 @@ cris_simple_epilogue (void)
/* If we're not supposed to emit prologue and epilogue, we must
not emit return-type instructions. */
|| !TARGET_PROLOGUE_EPILOGUE)
return 0;
return false;
/* We allow a "movem [sp+],rN" to sit in front if the "jump [sp+]" or
in the delay-slot of the "ret". */
/* No simple epilogue if there are saved registers. */
for (regno = 0; regno < reglimit; regno++)
if ((regs_ever_live[regno] && ! call_used_regs[regno])
|| (regno == (int) PIC_OFFSET_TABLE_REGNUM
&& (current_function_uses_pic_offset_table
/* It is saved anyway, if there would be a gap. */
|| (flag_pic
&& regs_ever_live[regno + 1]
&& !call_used_regs[regno + 1]))))
{
if (lastreg != regno - 1)
return 0;
lastreg = regno;
}
if (cris_reg_saved_in_regsave_area (regno, got_really_used))
return false;
return 1;
return true;
}
/* Expand a return insn (just one insn) marked as using SRP or stack
slot depending on parameter ON_STACK. */
void
cris_expand_return (bool on_stack)
{
/* FIXME: emit a parallel with a USE for SRP or the stack-slot, to
tell "ret" from "jump [sp+]". Some, but not all, other parts of
GCC expect just (return) to do the right thing when optimizing, so
we do that until they're fixed. Currently, all return insns in a
function must be the same (not really a limiting factor) so we need
to check that it doesn't change half-way through. */
emit_jump_insn (gen_rtx_RETURN (VOIDmode));
if ((cfun->machine->return_type == CRIS_RETINSN_RET && on_stack)
|| (cfun->machine->return_type == CRIS_RETINSN_JUMP && !on_stack))
abort ();
cfun->machine->return_type
= on_stack ? CRIS_RETINSN_JUMP : CRIS_RETINSN_RET;
}
/* Compute a (partial) cost for rtx X. Return true if the complete
@ -2886,6 +2806,239 @@ cris_split_movdx (rtx *operands)
return val;
}
/* The expander for the epilogue pattern. */
void
cris_expand_epilogue (void)
{
int regno;
int size = get_frame_size ();
int last_movem_reg = -1;
int argspace_offset = current_function_outgoing_args_size;
int pretend = current_function_pretend_args_size;
rtx mem;
bool return_address_on_stack = cris_return_address_on_stack ();
/* A reference may have been optimized out
(like the abort () in fde_split in unwind-dw2-fde.c, at least 3.2.1)
so check that it's still used. */
int got_really_used = current_function_uses_pic_offset_table;
int n_movem_regs = 0;
if (!TARGET_PROLOGUE_EPILOGUE)
return;
/* Align byte count of stack frame. */
if (TARGET_STACK_ALIGN)
size = TARGET_ALIGN_BY_32 ? (size + 3) & ~3 : (size + 1) & ~1;
/* Check how many saved regs we can movem. They start at r0 and must
be contiguous. */
for (regno = 0;
regno < FIRST_PSEUDO_REGISTER;
regno++)
if (cris_reg_saved_in_regsave_area (regno, got_really_used))
{
n_movem_regs++;
if (regno == last_movem_reg + 1)
last_movem_reg = regno;
else
break;
}
/* If there was only one register that really needed to be saved
through movem, don't use movem. */
if (n_movem_regs == 1)
last_movem_reg = -1;
/* Now emit "normal" move insns for all regs higher than the movem
regs. */
for (regno = FIRST_PSEUDO_REGISTER - 1;
regno > last_movem_reg;
regno--)
if (cris_reg_saved_in_regsave_area (regno, got_really_used))
{
if (argspace_offset)
{
/* There is an area for outgoing parameters located before
the saved registers. We have to adjust for that. */
emit_insn (gen_rtx_SET (VOIDmode,
stack_pointer_rtx,
plus_constant (stack_pointer_rtx,
argspace_offset)));
/* Make sure we only do this once. */
argspace_offset = 0;
}
mem = gen_rtx_MEM (SImode, gen_rtx_POST_INC (SImode,
stack_pointer_rtx));
set_mem_alias_set (mem, get_frame_alias_set ());
emit_move_insn (gen_rtx_raw_REG (SImode, regno), mem);
}
/* If we have any movem-restore, do it now. */
if (last_movem_reg != -1)
{
if (argspace_offset)
{
emit_insn (gen_rtx_SET (VOIDmode,
stack_pointer_rtx,
plus_constant (stack_pointer_rtx,
argspace_offset)));
argspace_offset = 0;
}
mem = gen_rtx_MEM (SImode,
gen_rtx_POST_INC (SImode, stack_pointer_rtx));
set_mem_alias_set (mem, get_frame_alias_set ());
emit_insn (cris_gen_movem_load (mem, GEN_INT (last_movem_reg + 1), 0));
}
/* If we don't clobber all of the allocated stack area (we've already
deallocated saved registers), GCC might want to schedule loads from
the stack to *after* the stack-pointer restore, which introduces an
interrupt race condition. This happened for the initial-value
SRP-restore for g++.dg/eh/registers1.C (noticed by inspection of
other failure for that test). It also happened for the stack slot
for the return value in (one version of)
linux/fs/dcache.c:__d_lookup, at least with "-O2
-fno-omit-frame-pointer". */
/* Restore frame pointer if necessary. */
if (frame_pointer_needed)
{
emit_insn (gen_cris_frame_deallocated_barrier ());
emit_move_insn (stack_pointer_rtx, frame_pointer_rtx);
mem = gen_rtx_MEM (SImode, gen_rtx_POST_INC (SImode,
stack_pointer_rtx));
set_mem_alias_set (mem, get_frame_alias_set ());
emit_move_insn (frame_pointer_rtx, mem);
}
else if ((size + argspace_offset) != 0)
{
emit_insn (gen_cris_frame_deallocated_barrier ());
/* If there was no frame-pointer to restore sp from, we must
explicitly deallocate local variables. */
/* Handle space for outgoing parameters that hasn't been handled
yet. */
size += argspace_offset;
emit_insn (gen_rtx_SET (VOIDmode,
stack_pointer_rtx,
plus_constant (stack_pointer_rtx, size)));
}
/* If this function has no pushed register parameters
(stdargs/varargs), and if it is not a leaf function, then we have
the return address on the stack. */
if (return_address_on_stack && pretend == 0)
{
if (current_function_calls_eh_return)
{
rtx mem;
rtx srpreg = gen_rtx_raw_REG (SImode, CRIS_SRP_REGNUM);
mem = gen_rtx_MEM (SImode,
gen_rtx_POST_INC (SImode,
stack_pointer_rtx));
set_mem_alias_set (mem, get_frame_alias_set ());
emit_move_insn (srpreg, mem);
emit_insn (gen_addsi3 (stack_pointer_rtx,
stack_pointer_rtx,
gen_rtx_raw_REG (SImode,
CRIS_STACKADJ_REG)));
cris_expand_return (false);
}
else
cris_expand_return (true);
return;
}
/* If we pushed some register parameters, then adjust the stack for
them. */
if (pretend != 0)
{
/* If SRP is stored on the way, we need to restore it first. */
if (return_address_on_stack)
{
rtx mem;
rtx srpreg = gen_rtx_raw_REG (SImode, CRIS_SRP_REGNUM);
mem = gen_rtx_MEM (SImode,
gen_rtx_POST_INC (SImode,
stack_pointer_rtx));
set_mem_alias_set (mem, get_frame_alias_set ());
emit_move_insn (srpreg, mem);
}
emit_insn (gen_rtx_SET (VOIDmode,
stack_pointer_rtx,
plus_constant (stack_pointer_rtx, pretend)));
}
/* Perform the "physical" unwinding that the EH machinery calculated. */
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);
}
/* Worker function for generating movem from mem for load_multiple. */
rtx
cris_gen_movem_load (rtx osrc, rtx nregs_rtx, int nprefix)
{
int nregs = INTVAL (nregs_rtx);
rtvec vec;
int eltno = 1;
int i;
rtx srcreg = XEXP (osrc, 0);
rtx src = osrc;
unsigned int regno = nregs - 1;
int regno_inc = -1;
if (GET_CODE (srcreg) == POST_INC)
srcreg = XEXP (srcreg, 0);
if (!REG_P (srcreg))
abort ();
/* Don't use movem for just one insn. The insns are equivalent except
for the pipeline hazard; movem does not forward the loaded
registers so there's a three cycles penalty for use. */
if (nregs == 1)
return gen_movsi (gen_rtx_REG (SImode, regno), osrc);
vec = rtvec_alloc (nprefix + nregs
+ (GET_CODE (XEXP (osrc, 0)) == POST_INC));
src = replace_equiv_address (osrc, srcreg);
RTVEC_ELT (vec, nprefix)
= gen_rtx_SET (VOIDmode, gen_rtx_REG (SImode, regno), src);
regno += regno_inc;
if (GET_CODE (XEXP (osrc, 0)) == POST_INC)
{
RTVEC_ELT (vec, nprefix + 1)
= gen_rtx_SET (VOIDmode, srcreg, plus_constant (srcreg, nregs * 4));
eltno++;
}
for (i = 1; i < nregs; i++, eltno++)
{
RTVEC_ELT (vec, nprefix + eltno)
= gen_rtx_SET (VOIDmode, gen_rtx_REG (SImode, regno),
adjust_address_nv (src, SImode, i * 4));
regno += regno_inc;
}
return gen_rtx_PARALLEL (VOIDmode, vec);
}
/* Use from within code, from e.g. PRINT_OPERAND and
PRINT_OPERAND_ADDRESS. Macros used in output_addr_const need to emit
different things depending on whether code operand or constant is

View File

@ -1627,6 +1627,8 @@ struct cum_args {int regs;};
{PLUS, UMIN}}, \
{"cris_mem_op", \
{MEM}}, \
{"cris_load_multiple_op", \
{PARALLEL}}, \
{"cris_bdap_operand", \
{SUBREG, REG, LABEL_REF, SYMBOL_REF, MEM, CONST_INT, \
CONST_DOUBLE, CONST, SIGN_EXTEND}}, \

View File

@ -58,6 +58,11 @@
;; UNSPEC Usage:
;; 0 PLT reference from call expansion: operand 0 is the address,
;; the mode is VOIDmode. Always wrapped in CONST.
;; 1 Stack frame deallocation barrier.
(define_constants
[(CRIS_UNSPEC_PLT 0)
(CRIS_UNSPEC_FRAME_DEALLOC 1)])
;; Register numbers.
@ -1381,6 +1386,24 @@
move %1,%0
move %1,%0"
[(set_attr "slottable" "yes,yes,yes,yes,yes,no,no,no,yes,yes,yes,no,yes,no")])
;; Note that the order of the registers is the reverse of that of the
;; standard pattern "load_multiple".
(define_insn "*cris_load_multiple"
[(match_parallel 0 "cris_load_multiple_op"
[(set (match_operand:SI 1 "register_operand" "=r,r")
(match_operand:SI 2 "memory_operand" "Q,m"))])]
""
"movem %O0,%o0"
[(set_attr "cc" "none")
(set_attr "slottable" "yes,no")
;; Not true, but setting the length to 0 causes return sequences (ret
;; movem) to have the cost they had when (return) included the movem
;; and reduces the performance penalty taken for needing to emit an
;; epilogue (in turn copied by bb-reorder) instead of return patterns.
;; FIXME: temporary change until all insn lengths are correctly
;; described. FIXME: have better target control over bb-reorder.
(set_attr "length" "0")])
;; Sign- and zero-extend insns with standard names.
@ -3467,69 +3490,37 @@
"jump %0")
;; Return insn. Used whenever the epilogue is very simple; if it is only
;; a single ret or jump [sp+] or a contiguous sequence of movem:able saved
;; registers. No allocated stack space is allowed.
;; a single ret or jump [sp+]. No allocated stack space or saved
;; registers are allowed.
;; Note that for this pattern, although named, it is ok to check the
;; context of the insn in the test, not only compiler switches.
(define_insn "return"
(define_expand "return"
[(return)]
"cris_simple_epilogue ()"
"*
"cris_expand_return (cris_return_address_on_stack ()); DONE;")
(define_insn "*return_expanded"
[(return)]
""
{
int i;
/* Just needs to hold a 'movem [sp+],rN'. */
char rd[sizeof (\"movem [$sp+],$r99\")];
*rd = 0;
/* Start from the last call-saved register. We know that we have a
simple epilogue, so we just have to find the last register in the
movem sequence. */
for (i = 8; i >= 0; i--)
if (regs_ever_live[i]
|| (i == PIC_OFFSET_TABLE_REGNUM
&& current_function_uses_pic_offset_table))
break;
if (i >= 0)
sprintf (rd, \"movem [$sp+],$%s\", reg_names [i]);
if (regs_ever_live[CRIS_SRP_REGNUM]
|| cris_return_address_on_stack ())
{
if (*rd)
output_asm_insn (rd, operands);
return \"jump [$sp+]\";
}
if (*rd)
{
output_asm_insn (\"reT\", operands);
output_asm_insn (rd, operands);
return \"\";
}
return \"ret%#\";
}"
return cris_return_address_on_stack_for_return ()
? "jump [$sp+]" : "ret%#";
}
[(set (attr "slottable")
(if_then_else
(ne (symbol_ref
"(regs_ever_live[CRIS_SRP_REGNUM]
|| cris_return_address_on_stack ())")
(const_int 0))
(const_string "no") ; If jump then not slottable.
(if_then_else
(ne (symbol_ref
"(regs_ever_live[0]
|| (flag_pic != 0 && regs_ever_live[1])
|| (PIC_OFFSET_TABLE_REGNUM == 0
&& cris_cfun_uses_pic_table ()))")
(const_int 0))
(const_string "no") ; ret+movem [sp+],rx: slot already filled.
(const_string "has_slot")))) ; If ret then need to fill a slot.
(set_attr "cc" "none")])
(if_then_else
(ne (symbol_ref
"(cris_return_address_on_stack_for_return ())")
(const_int 0))
(const_string "no")
(const_string "has_slot")))])
;; Note that the (return) from the expander itself is always the last
;; insn in the epilogue.
(define_expand "epilogue"
[(const_int 0)]
""
"cris_expand_epilogue (); DONE;")
;; Conditional branches.
@ -3929,7 +3920,8 @@
gen_rtx_CONST
(VOIDmode,
gen_rtx_UNSPEC (VOIDmode,
gen_rtvec (1, op0), 0)));
gen_rtvec (1, op0),
CRIS_UNSPEC_PLT)));
else
abort ();
@ -3994,7 +3986,8 @@
gen_rtx_CONST
(VOIDmode,
gen_rtx_UNSPEC (VOIDmode,
gen_rtvec (1, op1), 0)));
gen_rtvec (1, op1),
CRIS_UNSPEC_PLT)));
else
abort ();
@ -4040,6 +4033,20 @@
"nop"
[(set_attr "cc" "none")])
;; We need to stop accesses to the stack after the memory is
;; deallocated. Unfortunately, reorg doesn't look at naked clobbers,
;; e.g. (insn ... (clobber (mem:BLK (stack_pointer_rtx)))) and we don't
;; want to use a naked (unspec_volatile) as that would stop any
;; scheduling in the epilogue. Hence we model it as a "real" insn that
;; sets the memory in an unspecified manner. FIXME: Unfortunately it
;; still has the effect of an unspec_volatile.
(define_insn "cris_frame_deallocated_barrier"
[(set (mem:BLK (reg:SI CRIS_SP_REGNUM))
(unspec:BLK [(const_int 0)] CRIS_UNSPEC_FRAME_DEALLOC))]
""
""
[(set_attr "length" "0")])
;; We expand on casesi so we can use "bound" and "add offset fetched from
;; a table to pc" (adds.w [pc+%0.w],pc).