mips.c (mips_frame_info): Add acc_mask...

2009-04-02  Chao-ying Fu  <fu@mips.com>
	    James Grosbach <james.grosbach@microchip.com>

	* config/mips/mips.c (mips_frame_info): Add acc_mask, num_acc,
	num_cop0_regs, acc_save_offset, cop0_save_offset, acc_sp_offset,
	cop0_sp_offset.
	(machine_function): Add interrupt_handler_p, use_shadow_register_set_p,
	keep_interrupts_masked_p, use_debug_exception_return_p.
	(mips_attribute_table): Add interrupt, use_shadow_register_set,
	keep_interrupts_masked, use_debug_exception_return.
	(mips_interrupt_type_p, mips_use_shadow_register_set_p,
	mips_keep_interrupts_masked_p, mips_use_debug_exception_return_p):
	New functions.
	(mips_function_ok_for_sibcall): Return false for interrupt handlers.
	(mips_print_operand): Process COP0 registers to print $0 .. $31
	correctly for GAS to process.
	(mips_interrupt_extra_call_saved_reg_p): New function.
	(mips_cfun_call_saved_reg_p): For interrupt handlers, we need to check
	extra registers.
	(mips_cfun_might_clobber_call_saved_reg_p): Likewise.
	(mips_compute_frame_info): Add supports for interrupt context that
	includes doubleword accumulators and COP0 registers.
	(mips_for_each_saved_acc): New function.
	(mips_for_each_saved_gpr_and_fpr): Change the function name from
	mips_for_each_saved_reg.
	(mips_save_reg): Save accumulators.
	(mips_kernel_reg_p): A new for_each_rtx callback.
	(mips_expand_prologue): Support interrupt handlers.
	(mips_restore_reg): Restore accumulators.
	(mips_expand_epilogue): Support interrupt handlers.
	(mips_can_use_return_insn): Return false for interrupt handlers.
	(mips_epilogue_uses): New function.
	* config/mips/mips.md (UNSPEC_ERET, UNSPEC_DERET, UNSPEC_DI,
	UNSPEC_EHB, UNSPEC_RDPGPR, UNSPEC_COP0): New UNSPEC.
	(mips_eret, mips_deret, mips_di, mips_ehb, mips_rdpgpr,
	cop0_move): New instructions.
	* config/mips/mips-protos.h (mips_epilogue_uses): Declare.
	* config/mips/mips.h (K0_REG_NUM, K1_REG_NUM, KERNEL_REG_P): New
	defines.
	(COP0_STATUS_REG_NUM, COP0_CAUSE_REG_NUM, COP0_EPC_REG_NUM):
	New defines.
	(CAUSE_IPL, SR_IPL, SR_EXL, SR_IE): New defines.
	(MIPS_PROLOGUE_TEMP_REGNUM, MIPS_EPILOGUE_TEMP_REGNUM): For
	interrupt handlers, we use K0 as the temporary register.
	(EPILOGUE_USES): Change to a function call.
	* config/mips/sde.h (MIPS_EPILOGUE_TEMP_REGNUM): For interrupt
	handlers, we use K0 as the temporary register.

	* doc/extend.texi (Function Attributes): Document interrupt,
	use_shadow_register_set, keep_interrupts_masked,
	use_debug_exception_return for MIPS attributes.

Co-Authored-By: James Grosbach <james.grosbach@microchip.com>

From-SVN: r145481
This commit is contained in:
Chao-ying Fu 2009-04-02 21:57:41 +00:00 committed by Chao-ying Fu
parent 608f7b2ec6
commit e19da24c65
7 changed files with 676 additions and 52 deletions

View File

@ -1,3 +1,55 @@
2009-04-02 Chao-ying Fu <fu@mips.com>
James Grosbach <james.grosbach@microchip.com>
* config/mips/mips.c (mips_frame_info): Add acc_mask, num_acc,
num_cop0_regs, acc_save_offset, cop0_save_offset, acc_sp_offset,
cop0_sp_offset.
(machine_function): Add interrupt_handler_p, use_shadow_register_set_p,
keep_interrupts_masked_p, use_debug_exception_return_p.
(mips_attribute_table): Add interrupt, use_shadow_register_set,
keep_interrupts_masked, use_debug_exception_return.
(mips_interrupt_type_p, mips_use_shadow_register_set_p,
mips_keep_interrupts_masked_p, mips_use_debug_exception_return_p):
New functions.
(mips_function_ok_for_sibcall): Return false for interrupt handlers.
(mips_print_operand): Process COP0 registers to print $0 .. $31
correctly for GAS to process.
(mips_interrupt_extra_call_saved_reg_p): New function.
(mips_cfun_call_saved_reg_p): For interrupt handlers, we need to check
extra registers.
(mips_cfun_might_clobber_call_saved_reg_p): Likewise.
(mips_compute_frame_info): Add supports for interrupt context that
includes doubleword accumulators and COP0 registers.
(mips_for_each_saved_acc): New function.
(mips_for_each_saved_gpr_and_fpr): Change the function name from
mips_for_each_saved_reg.
(mips_save_reg): Save accumulators.
(mips_kernel_reg_p): A new for_each_rtx callback.
(mips_expand_prologue): Support interrupt handlers.
(mips_restore_reg): Restore accumulators.
(mips_expand_epilogue): Support interrupt handlers.
(mips_can_use_return_insn): Return false for interrupt handlers.
(mips_epilogue_uses): New function.
* config/mips/mips.md (UNSPEC_ERET, UNSPEC_DERET, UNSPEC_DI,
UNSPEC_EHB, UNSPEC_RDPGPR, UNSPEC_COP0): New UNSPEC.
(mips_eret, mips_deret, mips_di, mips_ehb, mips_rdpgpr,
cop0_move): New instructions.
* config/mips/mips-protos.h (mips_epilogue_uses): Declare.
* config/mips/mips.h (K0_REG_NUM, K1_REG_NUM, KERNEL_REG_P): New
defines.
(COP0_STATUS_REG_NUM, COP0_CAUSE_REG_NUM, COP0_EPC_REG_NUM):
New defines.
(CAUSE_IPL, SR_IPL, SR_EXL, SR_IE): New defines.
(MIPS_PROLOGUE_TEMP_REGNUM, MIPS_EPILOGUE_TEMP_REGNUM): For
interrupt handlers, we use K0 as the temporary register.
(EPILOGUE_USES): Change to a function call.
* config/mips/sde.h (MIPS_EPILOGUE_TEMP_REGNUM): For interrupt
handlers, we use K0 as the temporary register.
* doc/extend.texi (Function Attributes): Document interrupt,
use_shadow_register_set, keep_interrupts_masked,
use_debug_exception_return for MIPS attributes.
2009-04-03 Alan Modra <amodra@bigpond.net.au>
* config.gcc (powerpc64-*-gnu*): Add rs6000/default64.h to tm_file.

View File

@ -332,4 +332,6 @@ extern void mips_expand_atomic_qihi (union mips_gen_fn_ptrs,
extern void mips_expand_vector_init (rtx, rtx);
extern bool mips_epilogue_uses (unsigned int);
#endif /* ! GCC_MIPS_PROTOS_H */

View File

@ -261,18 +261,29 @@ struct mips_frame_info GTY(()) {
/* Likewise FPR X. */
unsigned int fmask;
/* The number of GPRs and FPRs saved. */
/* Likewise doubleword accumulator X ($acX). */
unsigned int acc_mask;
/* The number of GPRs, FPRs, doubleword accumulators and COP0
registers saved. */
unsigned int num_gp;
unsigned int num_fp;
unsigned int num_acc;
unsigned int num_cop0_regs;
/* The offset of the topmost GPR and FPR save slots from the top of
the frame, or zero if no such slots are needed. */
/* The offset of the topmost GPR, FPR, accumulator and COP0-register
save slots from the top of the frame, or zero if no such slots are
needed. */
HOST_WIDE_INT gp_save_offset;
HOST_WIDE_INT fp_save_offset;
HOST_WIDE_INT acc_save_offset;
HOST_WIDE_INT cop0_save_offset;
/* Likewise, but giving offsets from the bottom of the frame. */
HOST_WIDE_INT gp_sp_offset;
HOST_WIDE_INT fp_sp_offset;
HOST_WIDE_INT acc_sp_offset;
HOST_WIDE_INT cop0_sp_offset;
/* The offset of arg_pointer_rtx from frame_pointer_rtx. */
HOST_WIDE_INT arg_pointer_offset;
@ -310,6 +321,20 @@ struct machine_function GTY(()) {
/* True if we have emitted an instruction to initialize
mips16_gp_pseudo_rtx. */
bool initialized_mips16_gp_pseudo_p;
/* True if this is an interrupt handler. */
bool interrupt_handler_p;
/* True if this is an interrupt handler that uses shadow registers. */
bool use_shadow_register_set_p;
/* True if this is an interrupt handler that should keep interrupts
masked. */
bool keep_interrupts_masked_p;
/* True if this is an interrupt handler that should use DERET
instead of ERET. */
bool use_debug_exception_return_p;
};
/* Information about a single argument. */
@ -554,6 +579,11 @@ const struct attribute_spec mips_attribute_table[] = {
code generation but don't carry other semantics. */
{ "mips16", 0, 0, true, false, false, NULL },
{ "nomips16", 0, 0, true, false, false, NULL },
/* Allow functions to be specified as interrupt handlers */
{ "interrupt", 0, 0, false, true, true, NULL },
{ "use_shadow_register_set", 0, 0, false, true, true, NULL },
{ "keep_interrupts_masked", 0, 0, false, true, true, NULL },
{ "use_debug_exception_return", 0, 0, false, true, true, NULL },
{ NULL, 0, 0, false, false, false, NULL }
};
@ -1172,6 +1202,42 @@ mips_nomips16_decl_p (const_tree decl)
return lookup_attribute ("nomips16", DECL_ATTRIBUTES (decl)) != NULL;
}
/* Check if the interrupt attribute is set for a function. */
static bool
mips_interrupt_type_p (tree type)
{
return lookup_attribute ("interrupt", TYPE_ATTRIBUTES (type)) != NULL;
}
/* Check if the attribute to use shadow register set is set for a function. */
static bool
mips_use_shadow_register_set_p (tree type)
{
return lookup_attribute ("use_shadow_register_set",
TYPE_ATTRIBUTES (type)) != NULL;
}
/* Check if the attribute to keep interrupts masked is set for a function. */
static bool
mips_keep_interrupts_masked_p (tree type)
{
return lookup_attribute ("keep_interrupts_masked",
TYPE_ATTRIBUTES (type)) != NULL;
}
/* Check if the attribute to use debug exception return is set for
a function. */
static bool
mips_use_debug_exception_return_p (tree type)
{
return lookup_attribute ("use_debug_exception_return",
TYPE_ATTRIBUTES (type)) != NULL;
}
/* Return true if function DECL is a MIPS16 function. Return the ambient
setting if DECL is null. */
@ -6188,6 +6254,11 @@ mips_function_ok_for_sibcall (tree decl, tree exp ATTRIBUTE_UNUSED)
if (!TARGET_SIBCALLS)
return false;
/* Interrupt handlers need special epilogue code and therefore can't
use sibcalls. */
if (mips_interrupt_type_p (TREE_TYPE (current_function_decl)))
return false;
/* We can't do a sibcall if the called function is a MIPS16 function
because there is no direct "jx" instruction equivalent to "jalx" to
switch the ISA mode. We only care about cases where the sibling
@ -7229,7 +7300,11 @@ mips_print_operand (FILE *file, rtx op, int letter)
|| (letter == 'L' && TARGET_BIG_ENDIAN)
|| letter == 'D')
regno++;
fprintf (file, "%s", reg_names[regno]);
/* We need to print $0 .. $31 for COP0 registers. */
if (COP0_REG_P (regno))
fprintf (file, "$%s", &reg_names[regno][4]);
else
fprintf (file, "%s", reg_names[regno]);
}
break;
@ -8436,12 +8511,53 @@ mips_global_pointer (void)
return GLOBAL_POINTER_REGNUM;
}
/* Return true if REGNO is a register that is ordinarily call-clobbered
but must nevertheless be preserved by an interrupt handler. */
static bool
mips_interrupt_extra_call_saved_reg_p (unsigned int regno)
{
if (MD_REG_P (regno))
return true;
if (TARGET_DSP && DSP_ACC_REG_P (regno))
return true;
if (GP_REG_P (regno) && !cfun->machine->use_shadow_register_set_p)
{
/* $0 is hard-wired. */
if (regno == GP_REG_FIRST)
return false;
/* The interrupt handler can treat kernel registers as
scratch registers. */
if (KERNEL_REG_P (regno))
return false;
/* The function will return the stack pointer to its original value
anyway. */
if (regno == STACK_POINTER_REGNUM)
return false;
/* Otherwise, return true for registers that aren't ordinarily
call-clobbered. */
return call_really_used_regs[regno];
}
return false;
}
/* Return true if the current function should treat register REGNO
as call-saved. */
static bool
mips_cfun_call_saved_reg_p (unsigned int regno)
{
/* Interrupt handlers need to save extra registers. */
if (cfun->machine->interrupt_handler_p
&& mips_interrupt_extra_call_saved_reg_p (regno))
return true;
/* call_insns preserve $28 unless they explicitly say otherwise,
so call_really_used_regs[] treats $28 as call-saved. However,
we want the ABI property rather than the default call_insn
@ -8490,6 +8606,13 @@ mips_cfun_might_clobber_call_saved_reg_p (unsigned int regno)
if (regno == GP_REG_FIRST + 31 && mips16_cfun_returns_in_fpr_p ())
return true;
/* If REGNO is ordinarily call-clobbered, we must assume that any
called function could modify it. */
if (cfun->machine->interrupt_handler_p
&& !current_function_is_leaf
&& mips_interrupt_extra_call_saved_reg_p (regno))
return true;
return false;
}
@ -8545,6 +8668,14 @@ mips_save_reg_p (unsigned int regno)
C | callee-allocated save area |
| for register varargs |
| |
+-------------------------------+ <-- frame_pointer_rtx
| | + cop0_sp_offset
| COP0 reg save area | + UNITS_PER_WORD
| |
+-------------------------------+ <-- frame_pointer_rtx + acc_sp_offset
| | + UNITS_PER_WORD
| accumulator save area |
| |
+-------------------------------+ <-- frame_pointer_rtx + fp_sp_offset
| | + UNITS_PER_HWFPVALUE
| FPR save area |
@ -8588,6 +8719,28 @@ mips_compute_frame_info (void)
HOST_WIDE_INT offset, size;
unsigned int regno, i;
/* Set this function's interrupt properties. */
if (mips_interrupt_type_p (TREE_TYPE (current_function_decl)))
{
if (!ISA_MIPS32R2)
error ("the %<interrupt%> attribute requires a MIPS32r2 processor");
else if (TARGET_HARD_FLOAT)
error ("the %<interrupt%> attribute requires %<-msoft-float%>");
else if (TARGET_MIPS16)
error ("interrupt handlers cannot be MIPS16 functions");
else
{
cfun->machine->interrupt_handler_p = true;
cfun->machine->use_shadow_register_set_p =
mips_use_shadow_register_set_p (TREE_TYPE (current_function_decl));
cfun->machine->keep_interrupts_masked_p =
mips_keep_interrupts_masked_p (TREE_TYPE (current_function_decl));
cfun->machine->use_debug_exception_return_p =
mips_use_debug_exception_return_p (TREE_TYPE
(current_function_decl));
}
}
frame = &cfun->machine->frame;
memset (frame, 0, sizeof (*frame));
size = get_frame_size ();
@ -8657,7 +8810,7 @@ mips_compute_frame_info (void)
}
/* Find out which FPRs we need to save. This loop must iterate over
the same space as its companion in mips_for_each_saved_reg. */
the same space as its companion in mips_for_each_saved_gpr_and_fpr. */
if (TARGET_HARD_FLOAT)
for (regno = FP_REG_FIRST; regno <= FP_REG_LAST; regno += MAX_FPRS_PER_FMT)
if (mips_save_reg_p (regno))
@ -8673,6 +8826,47 @@ mips_compute_frame_info (void)
frame->fp_sp_offset = offset - UNITS_PER_HWFPVALUE;
}
/* Add in space for the interrupt context information. */
if (cfun->machine->interrupt_handler_p)
{
/* Check HI/LO. */
if (mips_save_reg_p (LO_REGNUM) || mips_save_reg_p (HI_REGNUM))
{
frame->num_acc++;
frame->acc_mask |= (1 << 0);
}
/* Check accumulators 1, 2, 3. */
for (i = DSP_ACC_REG_FIRST; i <= DSP_ACC_REG_LAST; i += 2)
if (mips_save_reg_p (i) || mips_save_reg_p (i + 1))
{
frame->num_acc++;
frame->acc_mask |= 1 << (((i - DSP_ACC_REG_FIRST) / 2) + 1);
}
/* All interrupt context functions need space to preserve STATUS. */
frame->num_cop0_regs++;
/* If we don't keep interrupts masked, we need to save EPC. */
if (!cfun->machine->keep_interrupts_masked_p)
frame->num_cop0_regs++;
}
/* Move above the accumulator save area. */
if (frame->num_acc > 0)
{
/* Each accumulator needs 2 words. */
offset += frame->num_acc * 2 * UNITS_PER_WORD;
frame->acc_sp_offset = offset - UNITS_PER_WORD;
}
/* Move above the COP0 register save area. */
if (frame->num_cop0_regs > 0)
{
offset += frame->num_cop0_regs * UNITS_PER_WORD;
frame->cop0_sp_offset = offset - UNITS_PER_WORD;
}
/* Move above the callee-allocated varargs save area. */
offset += MIPS_STACK_ALIGN (cfun->machine->varargs_size);
frame->arg_pointer_offset = offset;
@ -8686,6 +8880,10 @@ mips_compute_frame_info (void)
frame->gp_save_offset = frame->gp_sp_offset - offset;
if (frame->fp_sp_offset > 0)
frame->fp_save_offset = frame->fp_sp_offset - offset;
if (frame->acc_sp_offset > 0)
frame->acc_save_offset = frame->acc_sp_offset - offset;
if (frame->num_cop0_regs > 0)
frame->cop0_save_offset = frame->cop0_sp_offset - offset;
/* MIPS16 code offsets the frame pointer by the size of the outgoing
arguments. This tends to increase the chances of using unextended
@ -8882,12 +9080,41 @@ mips_save_restore_reg (enum machine_mode mode, int regno,
fn (gen_rtx_REG (mode, regno), mem);
}
/* Call FN for each accumlator that is saved by the current function.
SP_OFFSET is the offset of the current stack pointer from the start
of the frame. */
static void
mips_for_each_saved_acc (HOST_WIDE_INT sp_offset, mips_save_restore_fn fn)
{
HOST_WIDE_INT offset;
int regno;
offset = cfun->machine->frame.acc_sp_offset - sp_offset;
if (BITSET_P (cfun->machine->frame.acc_mask, 0))
{
mips_save_restore_reg (word_mode, LO_REGNUM, offset, fn);
offset -= UNITS_PER_WORD;
mips_save_restore_reg (word_mode, HI_REGNUM, offset, fn);
offset -= UNITS_PER_WORD;
}
for (regno = DSP_ACC_REG_FIRST; regno <= DSP_ACC_REG_LAST; regno++)
if (BITSET_P (cfun->machine->frame.acc_mask,
((regno - DSP_ACC_REG_FIRST) / 2) + 1))
{
mips_save_restore_reg (word_mode, regno, offset, fn);
offset -= UNITS_PER_WORD;
}
}
/* Call FN for each register that is saved by the current function.
SP_OFFSET is the offset of the current stack pointer from the start
of the frame. */
static void
mips_for_each_saved_reg (HOST_WIDE_INT sp_offset, mips_save_restore_fn fn)
mips_for_each_saved_gpr_and_fpr (HOST_WIDE_INT sp_offset,
mips_save_restore_fn fn)
{
enum machine_mode fpr_mode;
HOST_WIDE_INT offset;
@ -9075,13 +9302,24 @@ mips_save_reg (rtx reg, rtx mem)
}
else
{
if (TARGET_MIPS16
&& REGNO (reg) != GP_REG_FIRST + 31
&& !M16_REG_P (REGNO (reg)))
if (REGNO (reg) == HI_REGNUM)
{
/* Save a non-MIPS16 register by moving it through a temporary.
We don't need to do this for $31 since there's a special
instruction for it. */
if (TARGET_64BIT)
emit_insn (gen_mfhidi_ti (MIPS_PROLOGUE_TEMP (DImode),
gen_rtx_REG (TImode, MD_REG_FIRST)));
else
emit_insn (gen_mfhisi_di (MIPS_PROLOGUE_TEMP (SImode),
gen_rtx_REG (DImode, MD_REG_FIRST)));
mips_emit_move (mem, MIPS_PROLOGUE_TEMP (GET_MODE (reg)));
}
else if ((TARGET_MIPS16
&& REGNO (reg) != GP_REG_FIRST + 31
&& !M16_REG_P (REGNO (reg)))
|| ACC_REG_P (REGNO (reg)))
{
/* If the register has no direct store instruction, move it
through a temporary. Note that there's a special MIPS16
instruction to save $31. */
mips_emit_move (MIPS_PROLOGUE_TEMP (GET_MODE (reg)), reg);
mips_emit_move (mem, MIPS_PROLOGUE_TEMP (GET_MODE (reg)));
}
@ -9153,6 +9391,14 @@ mips_emit_loadgp (void)
emit_insn (gen_loadgp_blockage ());
}
/* A for_each_rtx callback. Stop the search if *X is a kernel register. */
static int
mips_kernel_reg_p (rtx *x, void *data ATTRIBUTE_UNUSED)
{
return GET_CODE (*x) == REG && KERNEL_REG_P (REGNO (*x));
}
/* Expand the "prologue" pattern. */
void
@ -9172,7 +9418,8 @@ mips_expand_prologue (void)
/* Save the registers. Allocate up to MIPS_MAX_FIRST_STACK_STEP
bytes beforehand; this is enough to cover the register save area
without going out of range. */
if ((frame->mask | frame->fmask) != 0)
if (((frame->mask | frame->fmask | frame->acc_mask) != 0)
|| frame->num_cop0_regs > 0)
{
HOST_WIDE_INT step1;
@ -9203,12 +9450,97 @@ mips_expand_prologue (void)
}
else
{
insn = gen_add3_insn (stack_pointer_rtx,
stack_pointer_rtx,
GEN_INT (-step1));
RTX_FRAME_RELATED_P (emit_insn (insn)) = 1;
size -= step1;
mips_for_each_saved_reg (size, mips_save_reg);
if (cfun->machine->interrupt_handler_p)
{
HOST_WIDE_INT offset;
rtx mem;
/* If this interrupt is using a shadow register set, we need to
get the stack pointer from the previous register set. */
if (cfun->machine->use_shadow_register_set_p)
emit_insn (gen_mips_rdpgpr (stack_pointer_rtx,
stack_pointer_rtx));
if (!cfun->machine->keep_interrupts_masked_p)
{
/* Move from COP0 Cause to K0. */
emit_insn (gen_cop0_move (gen_rtx_REG (SImode, K0_REG_NUM),
gen_rtx_REG (SImode,
COP0_CAUSE_REG_NUM)));
/* Move from COP0 EPC to K1. */
emit_insn (gen_cop0_move (gen_rtx_REG (SImode, K1_REG_NUM),
gen_rtx_REG (SImode,
COP0_EPC_REG_NUM)));
}
/* Allocate the first part of the frame. */
insn = gen_add3_insn (stack_pointer_rtx, stack_pointer_rtx,
GEN_INT (-step1));
RTX_FRAME_RELATED_P (emit_insn (insn)) = 1;
size -= step1;
/* Start at the uppermost location for saving. */
offset = frame->cop0_sp_offset - size;
if (!cfun->machine->keep_interrupts_masked_p)
{
/* Push EPC into its stack slot. */
mem = gen_frame_mem (word_mode,
plus_constant (stack_pointer_rtx,
offset));
mips_emit_move (mem, gen_rtx_REG (word_mode, K1_REG_NUM));
offset -= UNITS_PER_WORD;
}
/* Move from COP0 Status to K1. */
emit_insn (gen_cop0_move (gen_rtx_REG (SImode, K1_REG_NUM),
gen_rtx_REG (SImode,
COP0_STATUS_REG_NUM)));
/* Right justify the RIPL in k0. */
if (!cfun->machine->keep_interrupts_masked_p)
emit_insn (gen_lshrsi3 (gen_rtx_REG (SImode, K0_REG_NUM),
gen_rtx_REG (SImode, K0_REG_NUM),
GEN_INT (CAUSE_IPL)));
/* Push Status into its stack slot. */
mem = gen_frame_mem (word_mode,
plus_constant (stack_pointer_rtx, offset));
mips_emit_move (mem, gen_rtx_REG (word_mode, K1_REG_NUM));
offset -= UNITS_PER_WORD;
/* Insert the RIPL into our copy of SR (k1) as the new IPL. */
if (!cfun->machine->keep_interrupts_masked_p)
emit_insn (gen_insvsi (gen_rtx_REG (SImode, K1_REG_NUM),
GEN_INT (6),
GEN_INT (SR_IPL),
gen_rtx_REG (SImode, K0_REG_NUM)));
if (!cfun->machine->keep_interrupts_masked_p)
/* Enable interrupts by clearing the KSU ERL and EXL bits.
IE is already the correct value, so we don't have to do
anything explicit. */
emit_insn (gen_insvsi (gen_rtx_REG (SImode, K1_REG_NUM),
GEN_INT (4),
GEN_INT (SR_EXL),
gen_rtx_REG (SImode, GP_REG_FIRST)));
else
/* Disable interrupts by clearing the KSU, ERL, EXL,
and IE bits. */
emit_insn (gen_insvsi (gen_rtx_REG (SImode, K1_REG_NUM),
GEN_INT (5),
GEN_INT (SR_IE),
gen_rtx_REG (SImode, GP_REG_FIRST)));
}
else
{
insn = gen_add3_insn (stack_pointer_rtx,
stack_pointer_rtx,
GEN_INT (-step1));
RTX_FRAME_RELATED_P (emit_insn (insn)) = 1;
size -= step1;
}
mips_for_each_saved_acc (size, mips_save_reg);
mips_for_each_saved_gpr_and_fpr (size, mips_save_reg);
}
}
@ -9293,6 +9625,20 @@ mips_expand_prologue (void)
pic_offset_table_rtx);
}
/* We need to search back to the last use of K0 or K1. */
if (cfun->machine->interrupt_handler_p)
{
for (insn = get_last_insn (); insn != NULL_RTX; insn = PREV_INSN (insn))
if (INSN_P (insn)
&& for_each_rtx (&PATTERN (insn), mips_kernel_reg_p, NULL))
break;
/* Emit a move from K1 to COP0 Status after insn. */
gcc_assert (insn != NULL_RTX);
emit_insn_after (gen_cop0_move (gen_rtx_REG (SImode, COP0_STATUS_REG_NUM),
gen_rtx_REG (SImode, K1_REG_NUM)),
insn);
}
/* If we are profiling, make sure no instructions are scheduled before
the call to mcount. */
if (crtl->profile)
@ -9309,7 +9655,20 @@ mips_restore_reg (rtx reg, rtx mem)
if (TARGET_MIPS16 && REGNO (reg) == GP_REG_FIRST + 31)
reg = gen_rtx_REG (GET_MODE (reg), GP_REG_FIRST + 7);
if (TARGET_MIPS16 && !M16_REG_P (REGNO (reg)))
if (REGNO (reg) == HI_REGNUM)
{
mips_emit_move (MIPS_EPILOGUE_TEMP (GET_MODE (reg)), mem);
if (TARGET_64BIT)
emit_insn (gen_mthisi_di (gen_rtx_REG (TImode, MD_REG_FIRST),
MIPS_EPILOGUE_TEMP (DImode),
gen_rtx_REG (DImode, LO_REGNUM)));
else
emit_insn (gen_mthisi_di (gen_rtx_REG (DImode, MD_REG_FIRST),
MIPS_EPILOGUE_TEMP (SImode),
gen_rtx_REG (SImode, LO_REGNUM)));
}
else if ((TARGET_MIPS16 && !M16_REG_P (REGNO (reg)))
|| ACC_REG_P (REGNO (reg)))
{
/* Can't restore directly; move through a temporary. */
mips_emit_move (MIPS_EPILOGUE_TEMP (GET_MODE (reg)), mem);
@ -9345,7 +9704,7 @@ mips_expand_epilogue (bool sibcall_p)
{
const struct mips_frame_info *frame;
HOST_WIDE_INT step1, step2;
rtx base, target;
rtx base, target, insn;
if (!sibcall_p && mips_can_use_return_insn ())
{
@ -9378,7 +9737,8 @@ mips_expand_epilogue (bool sibcall_p)
/* If we need to restore registers, deallocate as much stack as
possible in the second step without going out of range. */
if ((frame->mask | frame->fmask) != 0)
if ((frame->mask | frame->fmask | frame->acc_mask) != 0
|| frame->num_cop0_regs > 0)
{
step2 = MIN (step1, MIPS_MAX_FIRST_STACK_STEP);
step1 -= step2;
@ -9440,13 +9800,53 @@ mips_expand_epilogue (bool sibcall_p)
else
{
/* Restore the registers. */
mips_for_each_saved_reg (frame->total_size - step2, mips_restore_reg);
mips_for_each_saved_acc (frame->total_size - step2, mips_restore_reg);
mips_for_each_saved_gpr_and_fpr (frame->total_size - step2,
mips_restore_reg);
/* Deallocate the final bit of the frame. */
if (step2 > 0)
emit_insn (gen_add3_insn (stack_pointer_rtx,
stack_pointer_rtx,
GEN_INT (step2)));
if (cfun->machine->interrupt_handler_p)
{
HOST_WIDE_INT offset;
rtx mem;
offset = frame->cop0_sp_offset - (frame->total_size - step2);
if (!cfun->machine->keep_interrupts_masked_p)
{
/* Restore the original EPC. */
mem = gen_frame_mem (word_mode,
plus_constant (stack_pointer_rtx, offset));
mips_emit_move (gen_rtx_REG (word_mode, K0_REG_NUM), mem);
offset -= UNITS_PER_WORD;
/* Move to COP0 EPC. */
emit_insn (gen_cop0_move (gen_rtx_REG (SImode, COP0_EPC_REG_NUM),
gen_rtx_REG (SImode, K0_REG_NUM)));
}
/* Restore the original Status. */
mem = gen_frame_mem (word_mode,
plus_constant (stack_pointer_rtx, offset));
mips_emit_move (gen_rtx_REG (word_mode, K0_REG_NUM), mem);
offset -= UNITS_PER_WORD;
/* If we don't use shoadow register set, we need to update SP. */
if (!cfun->machine->use_shadow_register_set_p && step2 > 0)
emit_insn (gen_add3_insn (stack_pointer_rtx,
stack_pointer_rtx,
GEN_INT (step2)));
/* Move to COP0 Status. */
emit_insn (gen_cop0_move (gen_rtx_REG (SImode, COP0_STATUS_REG_NUM),
gen_rtx_REG (SImode, K0_REG_NUM)));
}
else
{
/* Deallocate the final bit of the frame. */
if (step2 > 0)
emit_insn (gen_add3_insn (stack_pointer_rtx,
stack_pointer_rtx,
GEN_INT (step2)));
}
}
/* Add in the __builtin_eh_return stack adjustment. We need to
@ -9469,18 +9869,44 @@ mips_expand_epilogue (bool sibcall_p)
if (!sibcall_p)
{
unsigned int regno;
/* When generating MIPS16 code, the normal mips_for_each_saved_reg
path will restore the return address into $7 rather than $31. */
if (TARGET_MIPS16
&& !GENERATE_MIPS16E_SAVE_RESTORE
&& BITSET_P (frame->mask, 31))
regno = GP_REG_FIRST + 7;
else
regno = GP_REG_FIRST + 31;
mips_expand_before_return ();
emit_jump_insn (gen_return_internal (gen_rtx_REG (Pmode, regno)));
if (cfun->machine->interrupt_handler_p)
{
/* Interrupt handlers generate eret or deret. */
if (cfun->machine->use_debug_exception_return_p)
emit_jump_insn (gen_mips_deret ());
else
emit_jump_insn (gen_mips_eret ());
}
else
{
unsigned int regno;
/* When generating MIPS16 code, the normal
mips_for_each_saved_gpr_and_fpr path will restore the return
address into $7 rather than $31. */
if (TARGET_MIPS16
&& !GENERATE_MIPS16E_SAVE_RESTORE
&& BITSET_P (frame->mask, 31))
regno = GP_REG_FIRST + 7;
else
regno = GP_REG_FIRST + 31;
emit_jump_insn (gen_return_internal (gen_rtx_REG (Pmode, regno)));
}
}
/* Search from the beginning to the first use of K0 or K1. */
if (cfun->machine->interrupt_handler_p
&& !cfun->machine->keep_interrupts_masked_p)
{
for (insn = get_insns (); insn != NULL_RTX; insn = NEXT_INSN (insn))
if (INSN_P (insn)
&& for_each_rtx (&PATTERN(insn), mips_kernel_reg_p, NULL))
break;
gcc_assert (insn != NULL_RTX);
/* Insert disable interrupts before the first use of K0 or K1. */
emit_insn_before (gen_mips_di (), insn);
emit_insn_before (gen_mips_ehb (), insn);
}
}
@ -9491,6 +9917,10 @@ mips_expand_epilogue (bool sibcall_p)
bool
mips_can_use_return_insn (void)
{
/* Interrupt handlers need to go through the epilogue. */
if (cfun->machine->interrupt_handler_p)
return false;
if (!reload_completed)
return false;
@ -14242,6 +14672,31 @@ mips_order_regs_for_local_alloc (void)
reg_alloc_order[24] = 0;
}
}
/* Implement EPILOGUE_USES. */
bool
mips_epilogue_uses (unsigned int regno)
{
/* Say that the epilogue uses the return address register. Note that
in the case of sibcalls, the values "used by the epilogue" are
considered live at the start of the called function. */
if (regno == 31)
return true;
/* If using a GOT, say that the epilogue also uses GOT_VERSION_REGNUM.
See the comment above load_call<mode> for details. */
if (TARGET_USE_GOT && (regno) == GOT_VERSION_REGNUM)
return true;
/* An interrupt handler must preserve some registers that are
ordinarily call-clobbered. */
if (cfun->machine->interrupt_handler_p
&& mips_interrupt_extra_call_saved_reg_p (regno))
return true;
return false;
}
/* Initialize the GCC target structure. */
#undef TARGET_ASM_ALIGNED_HI_OP

View File

@ -1622,6 +1622,9 @@ enum mips_code_readable_setting {
#define GP_REG_LAST 31
#define GP_REG_NUM (GP_REG_LAST - GP_REG_FIRST + 1)
#define GP_DBX_FIRST 0
#define K0_REG_NUM (GP_REG_FIRST + 26)
#define K1_REG_NUM (GP_REG_FIRST + 27)
#define KERNEL_REG_P(REGNO) (IN_RANGE (REGNO, K0_REG_NUM, K1_REG_NUM))
#define FP_REG_FIRST 32
#define FP_REG_LAST 63
@ -1649,6 +1652,10 @@ enum mips_code_readable_setting {
#define COP0_REG_LAST 111
#define COP0_REG_NUM (COP0_REG_LAST - COP0_REG_FIRST + 1)
#define COP0_STATUS_REG_NUM (COP0_REG_FIRST + 12)
#define COP0_CAUSE_REG_NUM (COP0_REG_FIRST + 13)
#define COP0_EPC_REG_NUM (COP0_REG_FIRST + 14)
#define COP2_REG_FIRST 112
#define COP2_REG_LAST 143
#define COP2_REG_NUM (COP2_REG_LAST - COP2_REG_FIRST + 1)
@ -1667,6 +1674,17 @@ enum mips_code_readable_setting {
#define HI_REGNUM (TARGET_BIG_ENDIAN ? MD_REG_FIRST : MD_REG_FIRST + 1)
#define LO_REGNUM (TARGET_BIG_ENDIAN ? MD_REG_FIRST + 1 : MD_REG_FIRST)
/* A few bitfield locations for the coprocessor registers. */
/* Request Interrupt Priority Level is from bit 10 to bit 15 of
the cause register for the EIC interrupt mode. */
#define CAUSE_IPL 10
/* Interrupt Priority Level is from bit 10 to bit 15 of the status register. */
#define SR_IPL 10
/* Exception Level is at bit 1 of the status register. */
#define SR_EXL 1
/* Interrupt Enable is at bit 0 of the status register. */
#define SR_IE 0
/* FPSW_REGNUM is the single condition code used if !ISA_HAS_8CC.
If ISA_HAS_8CC, it should not be used, and an arbitrary ST_REG
should be used instead. */
@ -1754,11 +1772,18 @@ enum mips_code_readable_setting {
incoming arguments, the static chain pointer, or the frame pointer.
The epilogue temporary mustn't conflict with the return registers,
the PIC call register ($25), the frame pointer, the EH stack adjustment,
or the EH data registers. */
or the EH data registers.
If we're generating interrupt handlers, we use K0 as a temporary register
in prologue/epilogue code. */
#define MIPS16_PIC_TEMP_REGNUM (GP_REG_FIRST + 2)
#define MIPS_PROLOGUE_TEMP_REGNUM (GP_REG_FIRST + 3)
#define MIPS_EPILOGUE_TEMP_REGNUM (GP_REG_FIRST + (TARGET_MIPS16 ? 6 : 8))
#define MIPS_PROLOGUE_TEMP_REGNUM \
(cfun->machine->interrupt_handler_p ? K0_REG_NUM : GP_REG_FIRST + 3)
#define MIPS_EPILOGUE_TEMP_REGNUM \
(cfun->machine->interrupt_handler_p \
? K0_REG_NUM \
: GP_REG_FIRST + (TARGET_MIPS16 ? 6 : 8))
#define MIPS16_PIC_TEMP gen_rtx_REG (Pmode, MIPS16_PIC_TEMP_REGNUM)
#define MIPS_PROLOGUE_TEMP(MODE) gen_rtx_REG (MODE, MIPS_PROLOGUE_TEMP_REGNUM)
@ -2284,14 +2309,7 @@ typedef struct mips_args {
(mips_abi == ABI_EABI && UNITS_PER_FPVALUE >= UNITS_PER_DOUBLE)
/* Say that the epilogue uses the return address register. Note that
in the case of sibcalls, the values "used by the epilogue" are
considered live at the start of the called function.
If using a GOT, say that the epilogue also uses GOT_VERSION_REGNUM.
See the comment above load_call<mode> for details. */
#define EPILOGUE_USES(REGNO) \
((REGNO) == 31 || (TARGET_USE_GOT && (REGNO) == GOT_VERSION_REGNUM))
#define EPILOGUE_USES(REGNO) mips_epilogue_uses (REGNO)
/* Treat LOC as a byte offset from the stack pointer and round it up
to the next fully-aligned offset. */

View File

@ -67,6 +67,12 @@
(UNSPEC_SET_GOT_VERSION 46)
(UNSPEC_UPDATE_GOT_VERSION 47)
(UNSPEC_COPYGP 48)
(UNSPEC_ERET 49)
(UNSPEC_DERET 50)
(UNSPEC_DI 51)
(UNSPEC_EHB 52)
(UNSPEC_RDPGPR 53)
(UNSPEC_COP0 54)
(UNSPEC_ADDRESS_FIRST 100)
@ -5679,6 +5685,60 @@
[(set_attr "type" "jump")
(set_attr "mode" "none")])
;; Exception return.
(define_insn "mips_eret"
[(return)
(unspec_volatile [(const_int 0)] UNSPEC_ERET)]
""
"eret"
[(set_attr "type" "trap")
(set_attr "mode" "none")])
;; Debug exception return.
(define_insn "mips_deret"
[(return)
(unspec_volatile [(const_int 0)] UNSPEC_DERET)]
""
"deret"
[(set_attr "type" "trap")
(set_attr "mode" "none")])
;; Disable interrupts.
(define_insn "mips_di"
[(unspec_volatile [(const_int 0)] UNSPEC_DI)]
""
"di"
[(set_attr "type" "trap")
(set_attr "mode" "none")])
;; Execution hazard barrier.
(define_insn "mips_ehb"
[(unspec_volatile [(const_int 0)] UNSPEC_EHB)]
""
"ehb"
[(set_attr "type" "trap")
(set_attr "mode" "none")])
;; Read GPR from previous shadow register set.
(define_insn "mips_rdpgpr"
[(set (match_operand:SI 0 "register_operand" "=d")
(unspec_volatile:SI [(match_operand:SI 1 "register_operand" "d")]
UNSPEC_RDPGPR))]
""
"rdpgpr\t%0,%1"
[(set_attr "type" "move")
(set_attr "mode" "SI")])
;; Move involving COP0 registers.
(define_insn "cop0_move"
[(set (match_operand:SI 0 "register_operand" "=B,d")
(unspec_volatile:SI [(match_operand:SI 1 "register_operand" "d,B")]
UNSPEC_COP0))]
""
{ return mips_output_move (operands[0], operands[1]); }
[(set_attr "type" "mtc,mfc")
(set_attr "mode" "SI")])
;; This is used in compiling the unwind routines.
(define_expand "eh_return"
[(use (match_operand 0 "general_operand"))]

View File

@ -90,7 +90,8 @@ along with GCC; see the file COPYING3. If not see
/* Use $5 as a temporary for both MIPS16 and non-MIPS16. */
#undef MIPS_EPILOGUE_TEMP_REGNUM
#define MIPS_EPILOGUE_TEMP_REGNUM (GP_REG_FIRST + 5)
#define MIPS_EPILOGUE_TEMP_REGNUM \
(cfun->machine->interrupt_handler_p ? K0_REG_NUM : GP_REG_FIRST + 5)
/* Using long will always be right for size_t and ptrdiff_t, since
sizeof(long) must equal sizeof(void *), following from the setting

View File

@ -2402,7 +2402,7 @@ This attribute is ignored for R8C target.
@item interrupt
@cindex interrupt handler functions
Use this attribute on the ARM, AVR, CRX, M32C, M32R/D, m68k,
Use this attribute on the ARM, AVR, CRX, M32C, M32R/D, m68k, MIPS
and Xstormy16 ports to indicate that the specified function is an
interrupt handler. The compiler will generate function entry and exit
sequences suitable for use in an interrupt handler when this attribute
@ -2425,6 +2425,42 @@ Permissible values for this parameter are: IRQ, FIQ, SWI, ABORT and UNDEF@.
On ARMv7-M the interrupt type is ignored, and the attribute means the function
may be called with a word aligned stack pointer.
On MIPS targets, you can use the following attributes to modify the behavior
of an interrupt handler:
@table @code
@item use_shadow_register_set
@cindex @code{use_shadow_register_set} attribute
Assume that the handler uses a shadow register set, instead of
the main general-purpose registers.
@item keep_interrupts_masked
@cindex @code{keep_interrupts_masked} attribute
Keep interrupts masked for the whole function. Without this attribute,
GCC tries to reenable interrupts for as much of the function as it can.
@item use_debug_exception_return
@cindex @code{use_debug_exception_return} attribute
Return using the @code{deret} instruction. Interrupt handlers that don't
have this attribute return using @code{eret} instead.
@end table
You can use any combination of these attributes, as shown below:
@smallexample
void __attribute__ ((interrupt)) v0 ();
void __attribute__ ((interrupt, use_shadow_register_set)) v1 ();
void __attribute__ ((interrupt, keep_interrupts_masked)) v2 ();
void __attribute__ ((interrupt, use_debug_exception_return)) v3 ();
void __attribute__ ((interrupt, use_shadow_register_set,
keep_interrupts_masked)) v4 ();
void __attribute__ ((interrupt, use_shadow_register_set,
use_debug_exception_return)) v5 ();
void __attribute__ ((interrupt, keep_interrupts_masked,
use_debug_exception_return)) v6 ();
void __attribute__ ((interrupt, use_shadow_register_set,
keep_interrupts_masked,
use_debug_exception_return)) v7 ();
@end smallexample
@item interrupt_handler
@cindex interrupt handler functions on the Blackfin, m68k, H8/300 and SH processors
Use this attribute on the Blackfin, m68k, H8/300, H8/300H, H8S, and SH to