mirror of
git://gcc.gnu.org/git/gcc.git
synced 2025-04-21 14:41:07 +08:00
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:
parent
608f7b2ec6
commit
e19da24c65
@ -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.
|
||||
|
@ -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 */
|
||||
|
@ -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", ®_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
|
||||
|
@ -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. */
|
||||
|
@ -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"))]
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user