This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH] New MIPS interrupt handler patch


Hi All,

  Here is the updated patch, based on Richard's review and inputs from people.
Four attributes are supported.
interrupt
use_shadow_register_set
keep_interrupts_masked
use_debug_exception_return

  And as Maciej pointed out, this interrupt handler support is only for the EIC
(External Interrupt Controller) mode.  To support the interrupt compatibility
mode and the VI (Vector Interrupt) mode, GCC needs to generate slightly different
code in the prologue.

  For now, the attributes require -mips32r2 and -msoft-float.
  Please review the patch.  Thanks a lot!

Regards,
Chao-ying

gcc/ChangeLog
2009-03-05  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_interrupt_regs,
	acc_save_offset, interrupt_save_offset, acc_sp_offset, interrupt_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 regsiter to print $0 .. $31 correctly
	for GAS to process.
	(mips_register_interrupt_context_p): New function.
	(mips_cfun_call_saved_reg_p): For intrrupt handlers, we need to save
	accumulators and interrupt context registers when necessary.
	(mips_cfun_might_clobber_call_saved_reg_p): Likewise.
	(mips_compute_frame_info): Add supports for interrupt context.
	(mips_save_restore_acc): Declare for usage later.
	(mips_expand_prologue): Support interrupt handlers.
	(mips_save_restore_acc): New function.
	(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.
	(eret_internal, deret_internal, di_internal, ehb_internal,
	rdpgpr_internal, cop0_move_internal): New instrucitons.
	
	* 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.
	(EPILOGUE_USES): Change to a function call.
	
	* doc/extend.texi (Function Attributes): Document interrupt,
	at_vector, vector, naked for MIPS.

gcc/testsuite/ChangeLog
2009-03-05  Chao-ying Fu  <fu@mips.com>

        * gcc.target/mips/interrupt_handler.c: New test.

Index: gcc4x/gcc/gcc/config/mips/mips.c
===================================================================
--- gcc4x.orig/gcc/gcc/config/mips/mips.c	2009-03-02 16:35:42.000000000 -0800
+++ gcc4x/gcc/gcc/config/mips/mips.c	2009-03-05 18:13:56.873875000 -0800
@@ -261,18 +261,27 @@ struct mips_frame_info GTY(()) {
   /* Likewise FPR X.  */
   unsigned int fmask;
 
-  /* The number of GPRs and FPRs saved.  */
+  /* Likewise accumulator X.  */
+  unsigned int acc_mask;
+
+  /* The number of GPRs, FPRs, accumulators and interrupt registers saved.  */
   unsigned int num_gp;
   unsigned int num_fp;
+  unsigned int num_acc;
+  unsigned int num_interrupt_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, accumulators and interrupt regs 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 interrupt_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 interrupt_sp_offset;
 
   /* The offset of arg_pointer_rtx from frame_pointer_rtx.  */
   HOST_WIDE_INT arg_pointer_offset;
@@ -310,6 +319,18 @@ 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 we want to use shadow register set in interrupt handlers.  */
+  bool use_shadow_register_set_p;
+
+  /* True if we want to keep interrupts masked in interrupt handlers.  */
+  bool keep_interrupts_masked_p;
+
+  /* True if we want to use debug exception return in interrupt handlers.  */
+  bool use_debug_exception_return_p;
 };
 
 /* Information about a single argument.  */
@@ -554,6 +575,11 @@ const struct attribute_spec mips_attribu
      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 +1198,46 @@ 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)
+{
+  tree attr = lookup_attribute ("interrupt", TYPE_ATTRIBUTES (type));
+  return attr != 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)
+{
+  tree attr = lookup_attribute ("use_shadow_register_set",
+				TYPE_ATTRIBUTES (type));
+  return attr != NULL;
+}
+
+/* Check if the attribute to keep interrupts masked is set for a function.  */
+
+static bool
+mips_keep_interrupts_masked_p (tree type)
+{
+  tree attr = lookup_attribute ("keep_interrupts_masked",
+				TYPE_ATTRIBUTES (type));
+  return attr != NULL;
+}
+
+/* Check if the attribute to use debug exeception return is set for
+   a function.  */
+
+static bool
+mips_use_debug_exception_return_p (tree type)
+{
+  tree attr = lookup_attribute ("use_debug_exception_return",
+				TYPE_ATTRIBUTES (type));
+  return attr != 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,
   if (!TARGET_SIBCALLS)
     return false;
 
+  /* Cannot handle sibcalls from interrupt handler functions since
+     we need extra epilogue code.  */
+  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, 
 		|| (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,45 @@ mips_global_pointer (void)
   return GLOBAL_POINTER_REGNUM;
 }
 
+/* Returns true if regno is a register ordinarilly not callee saved which
+   must nevertheless be preserved by an interrupt handler function.  */
+
+static bool
+mips_register_interrupt_context_p (unsigned int regno)
+{
+  return GP_REG_P (regno)
+	 && regno != GP_REG_FIRST
+	 && !KERNEL_REG_P (regno)
+	 && regno != STACK_POINTER_REGNUM;
+}
+
 /* Return true if the current function should treat register REGNO
    as call-saved.  */
 
 static bool
 mips_cfun_call_saved_reg_p (unsigned int regno)
 {
+  if (cfun->machine->interrupt_handler_p)
+    {
+      /* Return true for all accumulators.  */
+      if (ACC_REG_P (regno))
+	return true;
+
+      if (cfun->machine->use_shadow_register_set_p)
+	{
+	  /* We're using a shadow register set, we don't need to save
+	     any GPRs.  */
+	  if (GP_REG_P (regno))
+	    return false;
+	}
+      else
+	{
+	  /* We need to save all interrupt context registers.  */
+	  if (mips_register_interrupt_context_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 +8598,31 @@ mips_cfun_might_clobber_call_saved_reg_p
   if (regno == GP_REG_FIRST + 31 && mips16_cfun_returns_in_fpr_p ())
     return true;
 
+  /* For interrupt handlers, we need to check accumulators and interrupt
+     context registers.  */
+  if (cfun->machine->interrupt_handler_p)
+    {
+      /* If the function is not a leaf function, we assume that the
+	 called function uses all accumulators and all interrupt
+	 context registers.  We might need to save them all.  */
+      if (!current_function_is_leaf)
+	{
+	  /* Save accumulator 0 (HI/LO).  */
+	  if (MD_REG_P (regno))
+	    return true;
+
+	  /* Save accumulator 1, 2, 3, when TARGET_DSP is true.  */
+	  if (TARGET_DSP && DSP_ACC_REG_P (regno))
+	    return true;
+
+	  /* If we don't use shadow register set, we need to save
+	     interrupt context registers.  */
+	  if (!cfun->machine->use_shadow_register_set_p
+	      && mips_register_interrupt_context_p (regno))
+	    return true;
+	}
+    }
+
   return false;
 }
 
@@ -8545,6 +8678,14 @@ mips_save_reg_p (unsigned int regno)
       C |  callee-allocated save area   |
 	|  for register varargs         |
 	|                               |
+	+-------------------------------+ <-- frame_pointer_rtx
+	|                               |       + interrupt_sp_offset
+	|  interrupt regs save area     |	+ UNITS_PER_WORD
+	|                               |
+	+-------------------------------+ <-- frame_pointer_rtx + acc_sp_offset
+	|                               |       + UNITS_PER_WORD
+	|  accumulators save area       |
+	|                               |
 	+-------------------------------+ <-- frame_pointer_rtx + fp_sp_offset
 	|                               |       + UNITS_PER_HWFPVALUE
 	|  FPR save area                |
@@ -8588,6 +8729,30 @@ mips_compute_frame_info (void)
   HOST_WIDE_INT offset, size;
   unsigned int regno, i;
 
+  /* Set interrupt handler attributes, only when we use soft-float
+     and MIPS32R2.  */
+  if (mips_interrupt_type_p (TREE_TYPE (current_function_decl)))
+    {
+      if (!TARGET_HARD_FLOAT && ISA_MIPS32R2)
+	cfun->machine->interrupt_handler_p = true;
+      else
+	warning (0, "cannot use interrupt attributes for the funciton: -msoft-float and -mips32r2 are required.");
+
+      if (cfun->machine->interrupt_handler_p)
+	{
+	  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 ();
@@ -8673,6 +8838,49 @@ 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 = 1; i < 4; i ++)
+	{
+	  if (mips_save_reg_p (DSP_ACC_REG_FIRST + ((i - 1) << 1))
+	      || mips_save_reg_p (DSP_ACC_REG_FIRST + ((i - 1) << 1) + 1))
+	    {
+	      frame->num_acc ++;
+	      frame->acc_mask |= (1 << i);
+	     }
+	}
+
+      /* All interrupt context functions need space to preserve STATUS.  */
+      frame->num_interrupt_regs++;
+
+      /* If we don't keep interrupts masked, we need to save EPC.  */
+      if (!cfun->machine->keep_interrupts_masked_p)
+	frame->num_interrupt_regs++;
+    }
+
+  /* Move above the accumulators save area.  */
+  if (frame->num_acc > 0)
+    {
+      offset += frame->num_acc * UNITS_PER_WORD;
+      frame->acc_sp_offset = offset - UNITS_PER_WORD;
+    }
+
+  /* Move above the interrupt registers save area.  */
+  if (frame->num_interrupt_regs > 0)
+    {
+      offset += frame->num_interrupt_regs * UNITS_PER_WORD;
+      frame->interrupt_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 +8894,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_interrupt_regs > 0)
+    frame->interrupt_save_offset = frame->interrupt_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
@@ -9153,6 +9365,10 @@ mips_emit_loadgp (void)
     emit_insn (gen_loadgp_blockage ());
 }
 
+static void
+mips_save_restore_acc (unsigned int acc_mask, HOST_WIDE_INT offset,
+		       bool save_p, unsigned int scratch_regno);
+
 /* Expand the "prologue" pattern.  */
 
 void
@@ -9172,11 +9388,112 @@ 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_interrupt_regs > 0)
     {
       HOST_WIDE_INT step1;
 
       step1 = MIN (size, MIPS_MAX_FIRST_STACK_STEP);
+
+      if (cfun->machine->interrupt_handler_p)
+	{
+	  HOST_WIDE_INT offset;
+
+	  /* 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_rdpgpr_internal (stack_pointer_rtx,
+					    stack_pointer_rtx));
+
+	  if (!cfun->machine->keep_interrupts_masked_p)
+	    {
+	      /* Move from Cop0 Cause to K0.  */
+	      emit_insn (gen_cop0_move_internal
+			  (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_internal
+			  (gen_rtx_REG (SImode, K1_REG_NUM),
+			   gen_rtx_REG (SImode, COP0_EPC_REG_NUM)));
+	    }
+
+	  /* Allocate the stack space to save the registers.  If more space
+	     needs to be allocated, the expand_prologue() function handled
+	     it.  */
+	  insn = gen_add3_insn (stack_pointer_rtx,
+				stack_pointer_rtx,
+				GEN_INT (-step1));
+	  RTX_FRAME_RELATED_P (emit_insn (insn)) = 1;
+
+	  /* Start at the uppermost location for saving.  */
+	  offset = cfun->machine->frame.interrupt_sp_offset;
+	  if (!cfun->machine->keep_interrupts_masked_p)
+	    {
+	      /* Push EPC into its stack slot.  */
+	      mips_save_restore_reg (word_mode, K1_REG_NUM, offset,
+				     mips_save_reg);
+	      offset -= UNITS_PER_WORD;
+	    }
+
+	  /* Move from Cop0 Status to K1.  */
+	  emit_insn (gen_cop0_move_internal
+		      (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.  */
+	  mips_save_restore_reg (word_mode, K1_REG_NUM, offset,
+				 mips_save_reg);
+	  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)));
+	    }
+
+	  /* Save accumulators to the stack.  */
+	  offset = cfun->machine->frame.acc_sp_offset;
+	  mips_save_restore_acc (cfun->machine->frame.acc_mask, offset,
+				 true, 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)));
+	    }
+
+	  /* Move K1 to Cop0 Status.  */
+	  emit_insn (gen_cop0_move_internal
+		      (gen_rtx_REG (SImode, COP0_STATUS_REG_NUM),
+		       gen_rtx_REG (SImode, K1_REG_NUM)));
+	}
+
       if (GENERATE_MIPS16E_SAVE_RESTORE)
  	{
  	  HOST_WIDE_INT offset;
@@ -9203,10 +9520,16 @@ 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;
+	  /* If this is an interrupt function, this first stack allocation
+	     will be performed by the static frame code and doesn't need
+	     to be done here.  */
+	  if (!cfun->machine->interrupt_handler_p)
+	    {
+	      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);
 	}
@@ -9337,6 +9660,92 @@ mips_expand_before_return (void)
     emit_clobber (pic_offset_table_rtx);
 }
 
+/* Save or restore accumulators. ACC_MASK is a bit mask for four accumuatlors.
+   OFFSET is the offset of its save slot from the current stack pointer.
+   SAVE_P is true for saving, and false for restoring.
+   SCRATCH_REGNO is the scratch reg for usage.  */
+
+static void
+mips_save_restore_acc (unsigned int acc_mask, HOST_WIDE_INT offset,
+		       bool save_p, unsigned int scratch_regno)
+{
+  unsigned int i;
+
+  /* For HI/LO.  */
+  if (BITSET_P (acc_mask, 0))
+    {
+      if (save_p)
+	{
+	  /* Move L0 to reg.  */
+	  emit_move_insn (gen_rtx_REG (SImode, scratch_regno),
+			  gen_rtx_REG (SImode, LO_REGNUM));
+	  /* Save reg to stack.  */
+	  mips_save_restore_reg (word_mode, scratch_regno, offset,
+				 mips_save_reg);
+	  offset -= UNITS_PER_WORD;
+
+	  /* Move HI to reg.  */
+	  emit_insn (gen_mfhisi_di (gen_rtx_REG (SImode, scratch_regno),
+				    gen_rtx_REG (DImode, MD_REG_FIRST)));
+	  /* Save reg to stack.  */
+	  mips_save_restore_reg (word_mode, scratch_regno, offset,
+				 mips_save_reg);
+	  offset -= UNITS_PER_WORD;
+	}
+      else
+	{
+	  /* Restore reg from stack.  */
+	  mips_save_restore_reg (word_mode, scratch_regno, offset,
+				 mips_restore_reg);
+	  offset -= UNITS_PER_WORD;
+
+	  /* Move reg to LO.  */
+	  emit_move_insn (gen_rtx_REG (SImode, LO_REGNUM),
+			  gen_rtx_REG (SImode, scratch_regno));
+
+	  /* Restore reg from stack.  */
+	  mips_save_restore_reg (word_mode, scratch_regno, offset,
+				 mips_restore_reg);
+	  offset -= UNITS_PER_WORD;
+
+	  /* Move reg to HI.  */
+	  emit_insn (gen_mthisi_di (gen_rtx_REG (DImode, MD_REG_FIRST),
+				    gen_rtx_REG (SImode, scratch_regno),
+				    gen_rtx_REG (SImode, LO_REGNUM)));
+	}
+    }
+
+  /* For accumulators 1, 2, 3.  */
+  for (i = 2; i < 8; i++)
+    {
+      if (BITSET_P (cfun->machine->frame.acc_mask, i >> 1))
+	{
+	  if (save_p)
+	    {
+	      /* Move accumulator HI/LO to reg.  */
+	      emit_move_insn (gen_rtx_REG (SImode, scratch_regno),
+			      gen_rtx_REG (SImode, DSP_ACC_REG_FIRST + i - 2));
+
+	      /* Save reg to stack.  */
+	      mips_save_restore_reg (word_mode, scratch_regno, offset,
+				     mips_save_reg);
+	      offset -= UNITS_PER_WORD;
+	    }
+	  else
+	    {
+	      /* Restore reg from stack.  */
+	      mips_save_restore_reg (word_mode, scratch_regno, offset,
+				     mips_restore_reg);
+	      offset -= UNITS_PER_WORD;
+
+	      /* Move reg to accumulator HI/LO.  */
+	      emit_move_insn (gen_rtx_REG (SImode, DSP_ACC_REG_FIRST + i - 2),
+			      gen_rtx_REG (SImode, scratch_regno));
+	    }
+	}
+    }
+}
+
 /* Expand an "epilogue" or "sibcall_epilogue" pattern; SIBCALL_P
    says which.  */
 
@@ -9378,7 +9787,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_interrupt_regs > 0)
     {
       step2 = MIN (step1, MIPS_MAX_FIRST_STACK_STEP);
       step1 -= step2;
@@ -9442,11 +9852,60 @@ mips_expand_epilogue (bool sibcall_p)
       /* Restore the registers.  */
       mips_for_each_saved_reg (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;
+
+	  /* Disable interrupts for handlers that don't keep interrupts
+	     masked.  */
+	  if (!cfun->machine->keep_interrupts_masked_p)
+	    {
+	      emit_insn (gen_di_internal ());
+	      emit_insn (gen_ehb_internal ());
+	    }
+
+	  offset = cfun->machine->frame.acc_sp_offset;
+	  mips_save_restore_acc (cfun->machine->frame.acc_mask, offset,
+				 false, K0_REG_NUM);
+
+	  offset = cfun->machine->frame.interrupt_sp_offset;
+	  if (!cfun->machine->keep_interrupts_masked_p)
+	    {
+	      /* Restore the original EPC.  */
+	      mips_save_restore_reg (word_mode, K0_REG_NUM, offset,
+				     mips_restore_reg);
+	      offset -= UNITS_PER_WORD;
+
+	      /* Move to Cop0 EPC.  */
+	      emit_insn (gen_cop0_move_internal
+			  (gen_rtx_REG (SImode, COP0_EPC_REG_NUM),
+			   gen_rtx_REG (SImode, K0_REG_NUM)));
+	    }
+
+	  /* Restore the original Status.  */
+	  mips_save_restore_reg (word_mode, K0_REG_NUM, offset,
+				 mips_restore_reg);
+	  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_internal
+		      (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
@@ -9480,7 +9939,17 @@ mips_expand_epilogue (bool sibcall_p)
       else
 	regno = GP_REG_FIRST + 31;
       mips_expand_before_return ();
-      emit_jump_insn (gen_return_internal (gen_rtx_REG (Pmode, regno)));
+
+      /* Interrupt handlers generate eret or deret.  */
+      if (cfun->machine->interrupt_handler_p)
+	{
+	  if (cfun->machine->use_debug_exception_return_p)
+	    emit_jump_insn (gen_deret_internal ());
+	  else
+	    emit_jump_insn (gen_eret_internal ());
+	}
+      else
+	emit_jump_insn (gen_return_internal (gen_rtx_REG (Pmode, regno)));
     }
 }
 
@@ -9491,6 +9960,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 +14715,37 @@ 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;
+
+  if (cfun->machine->interrupt_handler_p)
+    {
+      /* True for all accumulators.  */
+      if (ACC_REG_P (regno))
+	return true;
+
+      /* If the register is part of the GPRs that's saved for interrupt context,
+	 we need to mark it as used by the epilogue.  */
+      if (!cfun->machine->use_shadow_register_set_p)
+        return mips_register_interrupt_context_p (regno);
+    }
+
+  return false;
+}
 
 /* Initialize the GCC target structure.  */
 #undef TARGET_ASM_ALIGNED_HI_OP
Index: gcc4x/gcc/gcc/config/mips/mips.md
===================================================================
--- gcc4x.orig/gcc/gcc/config/mips/mips.md	2009-03-02 16:35:42.000000000 -0800
+++ gcc4x/gcc/gcc/config/mips/mips.md	2009-03-05 18:10:16.343627000 -0800
@@ -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 "eret_internal"
+  [(return)
+   (unspec_volatile [(const_int 0)] UNSPEC_ERET)]
+  ""
+  "eret"
+  [(set_attr "type"	"trap")
+   (set_attr "mode"	"none")])
+
+;; Debug exception return.
+(define_insn "deret_internal"
+  [(return)
+   (unspec_volatile [(const_int 0)] UNSPEC_DERET)]
+  ""
+  "deret"
+  [(set_attr "type"	"trap")
+   (set_attr "mode"	"none")])
+
+;; Disable interrupt.
+(define_insn "di_internal"
+  [(unspec_volatile [(const_int 0)] UNSPEC_DI)]
+  ""
+  "di"
+  [(set_attr "type"	"trap")
+   (set_attr "mode"	"none")])
+
+;; Execution hazard barrier.
+(define_insn "ehb_internal"
+  [(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 "rdpgpr_internal"
+  [(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_internal"
+  [(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"))]
Index: gcc4x/gcc/gcc/config/mips/mips-protos.h
===================================================================
--- gcc4x.orig/gcc/gcc/config/mips/mips-protos.h	2009-03-02 16:35:42.000000000 -0800
+++ gcc4x/gcc/gcc/config/mips/mips-protos.h	2009-03-02 17:02:55.000000000 -0800
@@ -332,4 +332,6 @@ extern void mips_expand_atomic_qihi (uni
 
 extern void mips_expand_vector_init (rtx, rtx);
 
+extern bool mips_epilogue_uses (unsigned int);
+
 #endif /* ! GCC_MIPS_PROTOS_H */
Index: gcc4x/gcc/gcc/config/mips/mips.h
===================================================================
--- gcc4x.orig/gcc/gcc/config/mips/mips.h	2009-03-02 16:35:42.000000000 -0800
+++ gcc4x/gcc/gcc/config/mips/mips.h	2009-03-05 18:05:20.146120000 -0800
@@ -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
+   cause register for the EIC interrupt mode.  */
+#define CAUSE_IPL	10
+/* Interrupt Priority Level is from bit 10 to bit 15 of status register.  */
+#define SR_IPL		10
+/* Exception Level is at bit 1 of status register.  */
+#define SR_EXL		1
+/* Interrupt Enable is at bit 0 of 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.  */
@@ -2289,9 +2307,10 @@ typedef struct mips_args {
    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))
+   See the comment above load_call<mode> for details.
+
+   For interrupt handlers, registers in interrupt context are used.  */
+#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.  */
Index: gcc4x/gcc/gcc/doc/extend.texi
===================================================================
--- gcc4x.orig/gcc/gcc/doc/extend.texi	2009-02-24 14:00:09.000000000 -0800
+++ gcc4x/gcc/gcc/doc/extend.texi	2009-03-05 18:20:05.768308000 -0800
@@ -2398,7 +2398,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
@@ -2421,6 +2421,26 @@ Permissible values for this parameter ar
 On ARMv7-M the interrupt type is ignored, and the attribute means the function
 may be called with a word aligned stack pointer.
 
+Note, for the MIPS, you can specify the behavior of interrupt by
+adding more attributes in addition to the interrupt attribute.
+These attributes are use_shadow_register_set, keep_interrupts_masked,
+and use_debug_exception_return.
+Without these attributes, the default behaviors are as follows.
+Don't use shadow register set (use normal registers);
+don't keep interrupts masked (enable nested interrupts);
+don't use debug exception return instruction (use exception return instruction).
+
+@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
Index: gcc4x/gcc/gcc/testsuite/gcc.target/mips/interrupt_handler.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ gcc4x/gcc/gcc/testsuite/gcc.target/mips/interrupt_handler.c	2009-03-05 18:03:00.470938000 -0800
@@ -0,0 +1,23 @@
+/* Test attributes for interrupt handlers */
+/* { dg-do compile } */
+/* { dg-options "-mips32r2 -msoft-float" } */
+
+void f () { }
+
+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 () { }
+
+void __attribute__ ((interrupt)) w0 () { t(); }
+void __attribute__ ((interrupt, use_shadow_register_set)) w1 () { t(); }
+void __attribute__ ((interrupt, keep_interrupts_masked)) w2 () { t(); }
+void __attribute__ ((interrupt, use_debug_exception_return)) w3 () { t(); }
+void __attribute__ ((interrupt, use_shadow_register_set, keep_interrupts_masked)) w4 () { t(); }
+void __attribute__ ((interrupt, use_shadow_register_set, use_debug_exception_return)) w5 () { t(); }
+void __attribute__ ((interrupt, keep_interrupts_masked, use_debug_exception_return)) w6 () { t(); }
+void __attribute__ ((interrupt, use_shadow_register_set, keep_interrupts_masked, use_debug_exception_return)) w7 () { t(); }


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]