diff mbox

[MIPS] Support new interrupt handler options

Message ID B5E67142681B53468FAF6B7C31356562441AB2FF@hhmail02.hh.imgtec.org
State New
Headers show

Commit Message

Robert Suchanek July 14, 2015, 3:13 p.m. UTC
Hi Catherine,

> I'm getting build errors with the current TOT and your patch.
> 
> The first errors that I encounter are:
> gcc/config/mips/mips.c:1355:1: warning: 'mips_int_mask
> mips_interrupt_mask(tree)' defined but not used [-Wunused-function]
> gcc/config/mips/mips.c:1392:1: warning: 'mips_shadow_set
> mips_use_shadow_register_set(tree)' defined but not used [-Wunused-function]
> 
> Removing these two functions results in further errors that I have not
> investigated.
> Will you try applying and building your patch again?

I have no explanation why this could happen.  My guess is that a part of the patch
did not apply correctly.  Those functions are used in mips_compute_frame_info().

I did notice and fixed warnings about unused variables/arguments.

> 
> I have a couple of further comments on the existing patch, see below.

Comments added.  Please have a look at the attached revised patch.
Tested against r225768.

Regards,
Robert


gcc/
	* config/mips/mips.c (mips_int_mask): New enum.
	(mips_shadow_set): Likewise.
	(int_mask): New variable.
	(use_shadow_register_set_p): Change type to enum mips_shadow_set.
	(machine_function): Add int_mask and use_shadow_register_set.
	(mips_attribute_table): Add attribute handlers for interrupt and
	use_shadow_register_set.
	(mips_interrupt_mask): New static function.
	(mips_handle_interrupt_attr): Likewise.
	(mips_handle_use_shadow_register_set_attr): Likewise.
	(mips_use_shadow_register_set): Change return type to enum
	mips_shadow_set.  Add argument handling for use_shadow_register_set
	attribute.
	(mips_interrupt_extra_called_saved_reg_p): Update the conditional to
	compare with mips_shadow_set enum.
	(mips_compute_frame_info): Add interrupt mask and
	use_shadow_register_set to per-function information structure.
	Add a stack slot for EPC unconditionally.
	(mips_expand_prologue): Compare use_shadow_register_set value
	with mips_shadow_set enum.  Save EPC always in K1, clobber only K1 for
	masked interrupt register but in EIC mode use K0 and save Cause in K0.
	EPC saved and restored unconditionally.  Use PMODE_INSN macro when
	copying the stack pointer from the shadow register set.
	* config/mips/mips.h (SR_IM0): New define.
	* config/mips/mips.md (mips_rdpgpr): Rename to...
	(mips_rdpgpr_<mode>): ...this.  Use the Pmode iterator.
	* doc/extend.texi (Declaring Attributes of Functions): Document
	optional arguments for interrupt and use_shadow_register_set
	attributes.

gcc/testsuite/
	* gcc.target/mips/interrupt_handler-4.c: New test.
---
 gcc/config/mips/mips.c                             | 286 +++++++++++++++++----
 gcc/config/mips/mips.h                             |   3 +
 gcc/config/mips/mips.md                            |  10 +-
 gcc/doc/extend.texi                                |  22 +-
 .../gcc.target/mips/interrupt_handler-4.c          |  31 +++
 5 files changed, 290 insertions(+), 62 deletions(-)
 create mode 100644 gcc/testsuite/gcc.target/mips/interrupt_handler-4.c

Comments

Moore, Catherine July 14, 2015, 9:24 p.m. UTC | #1
> -----Original Message-----
> From: Robert Suchanek [mailto:Robert.Suchanek@imgtec.com]
> Sent: Tuesday, July 14, 2015 11:14 AM
> To: Moore, Catherine; Matthew Fortune; gcc-patches@gcc.gnu.org
> Subject: RE: [PATCH, MIPS] Support new interrupt handler options
> 
> Hi Catherine,
> 
> > I'm getting build errors with the current TOT and your patch.
> >
> > The first errors that I encounter are:
> > gcc/config/mips/mips.c:1355:1: warning: 'mips_int_mask
> > mips_interrupt_mask(tree)' defined but not used [-Wunused-function]
> > gcc/config/mips/mips.c:1392:1: warning: 'mips_shadow_set
> > mips_use_shadow_register_set(tree)' defined but not used
> > [-Wunused-function]
> >
> > Removing these two functions results in further errors that I have not
> > investigated.
> > Will you try applying and building your patch again?
> 
> I have no explanation why this could happen.  My guess is that a part of the
> patch did not apply correctly.  Those functions are used in
> mips_compute_frame_info().
> 
> I did notice and fixed warnings about unused variables/arguments.

I re-ran patch with the verbose option and found that it was silently discarding hunks (starting with #7) because it thought it was garbage.

> 
> >
> > I have a couple of further comments on the existing patch, see below.
> 
> Comments added.  Please have a look at the attached revised patch.
> Tested against r225768.
> 
> Regards,
> Robert
> 
> 
> gcc/
> 	* config/mips/mips.c (mips_int_mask): New enum.
> 	(mips_shadow_set): Likewise.
> 	(int_mask): New variable.
> 	(use_shadow_register_set_p): Change type to enum
> mips_shadow_set.
> 	(machine_function): Add int_mask and use_shadow_register_set.
> 	(mips_attribute_table): Add attribute handlers for interrupt and
> 	use_shadow_register_set.
> 	(mips_interrupt_mask): New static function.
> 	(mips_handle_interrupt_attr): Likewise.
> 	(mips_handle_use_shadow_register_set_attr): Likewise.
> 	(mips_use_shadow_register_set): Change return type to enum
> 	mips_shadow_set.  Add argument handling for
> use_shadow_register_set
> 	attribute.
> 	(mips_interrupt_extra_called_saved_reg_p): Update the conditional
> to
> 	compare with mips_shadow_set enum.
> 	(mips_compute_frame_info): Add interrupt mask and
> 	use_shadow_register_set to per-function information structure.
> 	Add a stack slot for EPC unconditionally.
> 	(mips_expand_prologue): Compare use_shadow_register_set value
> 	with mips_shadow_set enum.  Save EPC always in K1, clobber only K1
> for
> 	masked interrupt register but in EIC mode use K0 and save Cause in
> K0.
> 	EPC saved and restored unconditionally.  Use PMODE_INSN macro
> when
> 	copying the stack pointer from the shadow register set.
> 	* config/mips/mips.h (SR_IM0): New define.
> 	* config/mips/mips.md (mips_rdpgpr): Rename to...
> 	(mips_rdpgpr_<mode>): ...this.  Use the Pmode iterator.
> 	* doc/extend.texi (Declaring Attributes of Functions): Document
> 	optional arguments for interrupt and use_shadow_register_set
> 	attributes.
> 
> gcc/testsuite/
> 	* gcc.target/mips/interrupt_handler-4.c: New test.

This is now OK to commit.
Catherine
Robert Suchanek July 15, 2015, 11:47 a.m. UTC | #2
Hi Catherine,

> This is now OK to commit.
> Catherine

Committed as r225819.

Robert
diff mbox

Patch

diff --git a/gcc/config/mips/mips.c b/gcc/config/mips/mips.c
index 671fed8..70240f7 100644
--- a/gcc/config/mips/mips.c
+++ b/gcc/config/mips/mips.c
@@ -382,6 +382,30 @@  struct GTY(())  mips_frame_info {
   HOST_WIDE_INT hard_frame_pointer_offset;
 };
 
+/* Enumeration for masked vectored (VI) and non-masked (EIC) interrupts.  */
+enum mips_int_mask
+{
+  INT_MASK_EIC = -1,
+  INT_MASK_SW0 = 0,
+  INT_MASK_SW1 = 1,
+  INT_MASK_HW0 = 2,
+  INT_MASK_HW1 = 3,
+  INT_MASK_HW2 = 4,
+  INT_MASK_HW3 = 5,
+  INT_MASK_HW4 = 6,
+  INT_MASK_HW5 = 7
+};
+
+/* Enumeration to mark the existence of the shadow register set.
+   SHADOW_SET_INTSTACK indicates a shadow register set with a valid stack
+   pointer.  */
+enum mips_shadow_set
+{
+  SHADOW_SET_NO,
+  SHADOW_SET_YES,
+  SHADOW_SET_INTSTACK
+};
+
 struct GTY(())  machine_function {
   /* The next floating-point condition-code register to allocate
      for ISA_HAS_8CC targets, relative to ST_REG_FIRST.  */
@@ -434,8 +458,12 @@  struct GTY(())  machine_function {
   /* 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;
+  /* Records the way in which interrupts should be masked.  Only used if
+     interrupts are not kept masked.  */
+  enum mips_int_mask int_mask;
+
+  /* Records if this is an interrupt handler that uses shadow registers.  */
+  enum mips_shadow_set use_shadow_register_set;
 
   /* True if this is an interrupt handler that should keep interrupts
      masked.  */
@@ -717,6 +745,10 @@  const enum reg_class mips_regno_to_class[FIRST_PSEUDO_REGISTER] = {
   ALL_REGS,	ALL_REGS,	ALL_REGS,	ALL_REGS
 };
 
+static tree mips_handle_interrupt_attr (tree *, tree, tree, int, bool *);
+static tree mips_handle_use_shadow_register_set_attr (tree *, tree, tree, int,
+						      bool *);
+
 /* The value of TARGET_ATTRIBUTE_TABLE.  */
 static const struct attribute_spec mips_attribute_table[] = {
   /* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler,
@@ -734,8 +766,10 @@  static const struct attribute_spec mips_attribute_table[] = {
   { "nomicromips", 0, 0, true,  false, false, NULL, false },
   { "nocompression", 0, 0, true,  false, false, NULL, false },
   /* Allow functions to be specified as interrupt handlers */
-  { "interrupt",   0, 0, false, true,  true, NULL, false },
-  { "use_shadow_register_set",	0, 0, false, true,  true, NULL, false },
+  { "interrupt",   0, 1, false, true,  true, mips_handle_interrupt_attr,
+    false },
+  { "use_shadow_register_set",	0, 1, false, true,  true,
+    mips_handle_use_shadow_register_set_attr, false },
   { "keep_interrupts_masked",	0, 0, false, true,  true, NULL, false },
   { "use_debug_exception_return", 0, 0, false, true,  true, NULL, false },
   { NULL,	   0, 0, false, false, false, NULL, false }
@@ -1317,13 +1351,65 @@  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.  */
+/* Return the mask for the "interrupt" attribute.  */
 
-static bool
-mips_use_shadow_register_set_p (tree type)
+static enum mips_int_mask
+mips_interrupt_mask (tree type)
 {
-  return lookup_attribute ("use_shadow_register_set",
-			   TYPE_ATTRIBUTES (type)) != NULL;
+  tree attr = lookup_attribute ("interrupt", TYPE_ATTRIBUTES (type));
+  tree args, cst;
+  const char *str;
+
+  /* For missing attributes or no arguments then return 'eic' as a safe
+     fallback.  */
+  if (attr == NULL)
+    return INT_MASK_EIC;
+
+  args = TREE_VALUE (attr);
+
+  if (args == NULL)
+    return INT_MASK_EIC;
+
+  cst = TREE_VALUE (args);
+
+  if (strcmp (TREE_STRING_POINTER (cst), "eic") == 0)
+    return INT_MASK_EIC;
+
+  /* The validation code in mips_handle_interrupt_attr guarantees that the
+     argument is now in the form:
+     vector=(sw0|sw1|hw0|hw1|hw2|hw3|hw4|hw5).  */
+  str = TREE_STRING_POINTER (cst);
+
+  gcc_assert (strlen (str) == strlen ("vector=sw0"));
+
+  if (str[7] == 's')
+    return (enum mips_int_mask) (INT_MASK_SW0 + (str[9] - '0'));
+
+  return (enum mips_int_mask) (INT_MASK_HW0 + (str[9] - '0'));
+}
+
+/* Return the mips_shadow_set if the "use_shadow_register_set" attribute is
+   set for a function.  */
+
+static enum mips_shadow_set
+mips_use_shadow_register_set (tree type)
+{
+  tree attr = lookup_attribute ("use_shadow_register_set",
+				TYPE_ATTRIBUTES (type));
+  tree args;
+
+  /* The validation code in mips_handle_use_shadow_register_set_attr guarantees
+     that if an argument is present then it means: Assume the shadow register
+     set has a valid stack pointer in it.  */
+  if (attr == NULL)
+    return SHADOW_SET_NO;
+
+  args = TREE_VALUE (attr);
+
+  if (args == NULL)
+    return SHADOW_SET_YES;
+
+  return SHADOW_SET_INTSTACK;
 }
 
 /* Check if the attribute to keep interrupts masked is set for a function.  */
@@ -1529,6 +1615,92 @@  mips_can_inline_p (tree caller, tree callee)
     return false;
   return default_target_can_inline_p (caller, callee);
 }
+
+/* Handle an "interrupt" attribute with an optional argument.  */
+
+static tree
+mips_handle_interrupt_attr (tree *node ATTRIBUTE_UNUSED, tree name, tree args,
+			    int flags ATTRIBUTE_UNUSED, bool *no_add_attrs)
+{
+  /* Check for an argument.  */
+  if (is_attribute_p ("interrupt", name) && args != NULL)
+    {
+      tree cst;
+
+      cst = TREE_VALUE (args);
+      if (TREE_CODE (cst) != STRING_CST)
+	{
+	  warning (OPT_Wattributes,
+		   "%qE attribute requires a string argument",
+		   name);
+	  *no_add_attrs = true;
+	}
+      else if (strcmp (TREE_STRING_POINTER (cst), "eic") != 0
+	       && strncmp (TREE_STRING_POINTER (cst), "vector=", 7) != 0)
+	{
+	  warning (OPT_Wattributes,
+		   "argument to %qE attribute is neither eic, nor "
+		   "vector=<line>", name);
+	  *no_add_attrs = true;
+	}
+      else if (strncmp (TREE_STRING_POINTER (cst), "vector=", 7) == 0)
+	{
+	  const char *arg = TREE_STRING_POINTER (cst) + 7;
+
+	  /* Acceptable names are: sw0,sw1,hw0,hw1,hw2,hw3,hw4,hw5.  */
+	  if (strlen (arg) != 3
+	      || (arg[0] != 's' && arg[0] != 'h')
+	      || arg[1] != 'w'
+	      || (arg[0] == 's' && arg[2] != '0' && arg[2] != '1')
+	      || (arg[0] == 'h' && (arg[2] < '0' || arg[2] > '5')))
+	    {
+	      warning (OPT_Wattributes,
+		       "interrupt vector to %qE attribute is not "
+		       "vector=(sw0|sw1|hw0|hw1|hw2|hw3|hw4|hw5)",
+		       name);
+	      *no_add_attrs = true;
+	    }
+	}
+
+      return NULL_TREE;
+    }
+
+  return NULL_TREE;
+}
+
+/* Handle a "use_shadow_register_set" attribute with an optional argument.  */
+
+static tree
+mips_handle_use_shadow_register_set_attr (tree *node ATTRIBUTE_UNUSED,
+					  tree name, tree args,
+					  int flags ATTRIBUTE_UNUSED,
+					  bool *no_add_attrs)
+{
+  /* Check for an argument.  */
+  if (is_attribute_p ("use_shadow_register_set", name) && args != NULL)
+    {
+      tree cst;
+
+      cst = TREE_VALUE (args);
+      if (TREE_CODE (cst) != STRING_CST)
+	{
+	  warning (OPT_Wattributes,
+		   "%qE attribute requires a string argument",
+		   name);
+	  *no_add_attrs = true;
+	}
+      else if (strcmp (TREE_STRING_POINTER (cst), "intstack") != 0)
+	{
+	  warning (OPT_Wattributes,
+		   "argument to %qE attribute is not intstack", name);
+	  *no_add_attrs = true;
+	}
+
+      return NULL_TREE;
+    }
+
+  return NULL_TREE;
+}
 

 /* If X is a PLUS of a CONST_INT, return the two terms in *BASE_PTR
    and *OFFSET_PTR.  Return X in *BASE_PTR and 0 in *OFFSET_PTR otherwise.  */
@@ -10047,7 +10219,8 @@  mips_interrupt_extra_call_saved_reg_p (unsigned int regno)
   if (TARGET_DSP && DSP_ACC_REG_P (regno))
     return true;
 
-  if (GP_REG_P (regno) && !cfun->machine->use_shadow_register_set_p)
+  if (GP_REG_P (regno)
+      && cfun->machine->use_shadow_register_set == SHADOW_SET_NO)
     {
       /* $0 is hard-wired.  */
       if (regno == GP_REG_FIRST)
@@ -10259,8 +10432,10 @@  mips_compute_frame_info (void)
       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->int_mask =
+	    mips_interrupt_mask (TREE_TYPE (current_function_decl));
+	  cfun->machine->use_shadow_register_set =
+	    mips_use_shadow_register_set (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 =
@@ -10375,9 +10550,9 @@  mips_compute_frame_info (void)
       /* 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++;
+      /* We need to save EPC regardless of whether interrupts remain masked
+	 as exceptions will corrupt EPC.  */
+      frame->num_cop0_regs++;
     }
 
   /* Move above the accumulator save area.  */
@@ -11418,21 +11593,21 @@  mips_expand_prologue (void)
 
 	      /* 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->use_shadow_register_set == SHADOW_SET_YES)
+		emit_insn (PMODE_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)));
+		  if (cfun->machine->int_mask == INT_MASK_EIC)
+		    /* 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.  */
 	      rtx insn = gen_add3_insn (stack_pointer_rtx, stack_pointer_rtx,
@@ -11443,15 +11618,13 @@  mips_expand_prologue (void)
 
 	      /* 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 (Pmode, stack_pointer_rtx,
-						      offset));
-		  mips_emit_move (mem, gen_rtx_REG (word_mode, K1_REG_NUM));
-		  offset -= UNITS_PER_WORD;
-		}
+
+	      /* Push EPC into its stack slot.  */
+	      mem = gen_frame_mem (word_mode,
+				   plus_constant (Pmode, 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),
@@ -11459,7 +11632,8 @@  mips_expand_prologue (void)
 						     COP0_STATUS_REG_NUM)));
 
 	      /* Right justify the RIPL in k0.  */
-	      if (!cfun->machine->keep_interrupts_masked_p)
+	      if (!cfun->machine->keep_interrupts_masked_p
+		  && cfun->machine->int_mask == INT_MASK_EIC)
 		emit_insn (gen_lshrsi3 (gen_rtx_REG (SImode, K0_REG_NUM),
 					gen_rtx_REG (SImode, K0_REG_NUM),
 					GEN_INT (CAUSE_IPL)));
@@ -11472,12 +11646,22 @@  mips_expand_prologue (void)
 	      offset -= UNITS_PER_WORD;
 
 	      /* Insert the RIPL into our copy of SR (k1) as the new IPL.  */
-	      if (!cfun->machine->keep_interrupts_masked_p)
+	      if (!cfun->machine->keep_interrupts_masked_p
+		  && cfun->machine->int_mask == INT_MASK_EIC)
 		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)));
 
+	      /* Clear all interrupt mask bits up to and including the
+		 handler's interrupt line.  */
+	      if (!cfun->machine->keep_interrupts_masked_p
+		  && cfun->machine->int_mask != INT_MASK_EIC)
+		emit_insn (gen_insvsi (gen_rtx_REG (SImode, K1_REG_NUM),
+				       GEN_INT (cfun->machine->int_mask + 1),
+				       GEN_INT (SR_IM0),
+				       gen_rtx_REG (SImode, GP_REG_FIRST)));
+
 	      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
@@ -11846,29 +12030,27 @@  mips_expand_epilogue (bool sibcall_p)
 	  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 (Pmode, 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 EPC.  */
+	  mem = gen_frame_mem (word_mode,
+			       plus_constant (Pmode, stack_pointer_rtx,
+					      offset));
+	  mips_emit_move (gen_rtx_REG (word_mode, K1_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, K1_REG_NUM)));
 
 	  /* Restore the original Status.  */
 	  mem = gen_frame_mem (word_mode,
 			       plus_constant (Pmode, stack_pointer_rtx,
 					      offset));
-	  mips_emit_move (gen_rtx_REG (word_mode, K0_REG_NUM), mem);
+	  mips_emit_move (gen_rtx_REG (word_mode, K1_REG_NUM), mem);
 	  offset -= UNITS_PER_WORD;
 
 	  /* If we don't use shadow register set, we need to update SP.  */
-	  if (!cfun->machine->use_shadow_register_set_p)
+	  if (cfun->machine->use_shadow_register_set == SHADOW_SET_NO)
 	    mips_deallocate_stack (stack_pointer_rtx, GEN_INT (step2), 0);
 	  else
 	    /* The choice of position is somewhat arbitrary in this case.  */
@@ -11876,7 +12058,7 @@  mips_expand_epilogue (bool sibcall_p)
 
 	  /* Move to COP0 Status.  */
 	  emit_insn (gen_cop0_move (gen_rtx_REG (SImode, COP0_STATUS_REG_NUM),
-				    gen_rtx_REG (SImode, K0_REG_NUM)));
+				    gen_rtx_REG (SImode, K1_REG_NUM)));
 	}
       else if (TARGET_MICROMIPS
 	       && !crtl->calls_eh_return
diff --git a/gcc/config/mips/mips.h b/gcc/config/mips/mips.h
index 9d10a28..37f5b54 100644
--- a/gcc/config/mips/mips.h
+++ b/gcc/config/mips/mips.h
@@ -1811,6 +1811,9 @@  FP_ASM_SPEC "\
 #define SR_COP1         29
 /* Interrupt Priority Level is from bit 10 to bit 15 of the status register.  */
 #define SR_IPL		10
+/* Interrupt masks start with IM0 at bit 8 to IM7 at bit 15 of the status
+   register.  */
+#define SR_IM0		8
 /* Exception Level is at bit 1 of the status register.  */
 #define SR_EXL		1
 /* Interrupt Enable is at bit 0 of the status register.  */
diff --git a/gcc/config/mips/mips.md b/gcc/config/mips/mips.md
index 4f5692c..ad8ad9f 100644
--- a/gcc/config/mips/mips.md
+++ b/gcc/config/mips/mips.md
@@ -6564,14 +6564,14 @@  (define_insn "mips_ehb"
    (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))]
+(define_insn "mips_rdpgpr_<mode>"
+  [(set (match_operand:P 0 "register_operand" "=d")
+	(unspec_volatile:P [(match_operand:P 1 "register_operand" "d")]
+			   UNSPEC_RDPGPR))]
   ""
   "rdpgpr\t%0,%1"
   [(set_attr "type"	"move")
-   (set_attr "mode"	"SI")])
+   (set_attr "mode"	"<MODE>")])
 
 ;; Move involving COP0 registers.
 (define_insn "cop0_move"
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index bb858a8..b2b1dc9 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -4090,10 +4090,18 @@  These function attributes are supported by the MIPS back end:
 @table @code
 @item interrupt
 @cindex @code{interrupt} function attribute, MIPS
-Use this attribute to indicate
-that the specified function is an interrupt handler.  The compiler generates
-function entry and exit sequences suitable for use in an interrupt handler
-when this attribute is present.
+Use this attribute to indicate that the specified function is an interrupt
+handler.  The compiler generates function entry and exit sequences suitable
+for use in an interrupt handler when this attribute is present.
+An optional argument is supported for the interrupt attribute which allows
+the interrupt mode to be described.  By default GCC assumes the external
+interrupt controller (EIC) mode is in use, this can be explicitly set using
+@code{eic}.  When interrupts are non-masked then the requested Interrupt
+Priority Level (IPL) is copied to the current IPL which has the effect of only
+enabling higher priority interrupts.  To use vectored interrupt mode use
+the argument @code{vector=[sw0|sw1|hw0|hw1|hw2|hw3|hw4|hw5]}, this will change
+the behaviour of the non-masked interrupt support and GCC will arrange to mask
+all interrupts from sw0 up to and including the specified interrupt vector.
 
 You can use the following attributes to modify the behavior
 of an interrupt handler:
@@ -4101,7 +4109,9 @@  of an interrupt handler:
 @item use_shadow_register_set
 @cindex @code{use_shadow_register_set} function attribute, MIPS
 Assume that the handler uses a shadow register set, instead of
-the main general-purpose registers.
+the main general-purpose registers.  An optional argument @code{intstack} is
+supported to indicate that the shadow register set contains a valid stack
+pointer.
 
 @item keep_interrupts_masked
 @cindex @code{keep_interrupts_masked} function attribute, MIPS
@@ -4129,6 +4139,8 @@  void __attribute__ ((interrupt, keep_interrupts_masked,
 void __attribute__ ((interrupt, use_shadow_register_set,
                      keep_interrupts_masked,
                      use_debug_exception_return)) v7 ();
+void __attribute__ ((interrupt("eic"))) v8 ();
+void __attribute__ ((interrupt("vector=hw3"))) v9 ();
 @end smallexample
 
 @item long_call
diff --git a/gcc/testsuite/gcc.target/mips/interrupt_handler-4.c b/gcc/testsuite/gcc.target/mips/interrupt_handler-4.c
new file mode 100644
index 0000000..6e7c45c
--- /dev/null
+++ b/gcc/testsuite/gcc.target/mips/interrupt_handler-4.c
@@ -0,0 +1,31 @@ 
+/* Test optional argument for interrupt and use_shadow_register_set
+   attributes.  */
+/* { dg-do compile } */
+/* { dg-options "isa_rev>=2" } */
+/* { dg-final { scan-assembler "e0:.*ins\t\\\$27,\\\$26,10,6.*\.end\te0" } } */
+/* { dg-final { scan-assembler-times "mfc0\t\\\$26,\\\$13" 3 } } */
+/* { dg-final { scan-assembler-times "mfc0\t\\\$27,\\\$14" 11 } } */
+/* { dg-final { scan-assembler "v0:.*ins\t\\\$27,\\\$0,8,1.*\.end\tv0" } } */
+/* { dg-final { scan-assembler "v1:.*ins\t\\\$27,\\\$0,8,2.*\.end\tv1" } } */
+/* { dg-final { scan-assembler "v2:.*ins\t\\\$27,\\\$0,8,3.*\.end\tv2" } } */
+/* { dg-final { scan-assembler "v3:.*ins\t\\\$27,\\\$0,8,4.*\.end\tv3" } } */
+/* { dg-final { scan-assembler "v4:.*ins\t\\\$27,\\\$0,8,5.*\.end\tv4" } } */
+/* { dg-final { scan-assembler "v5:.*ins\t\\\$27,\\\$0,8,6.*\.end\tv5" } } */
+/* { dg-final { scan-assembler "v6:.*ins\t\\\$27,\\\$0,8,7.*\.end\tv6" } } */
+/* { dg-final { scan-assembler "v7:.*ins\t\\\$27,\\\$0,8,8.*\.end\tv7" } } */
+
+/* { dg-final { scan-assembler-times "rdpgpr\t\\\$sp,\\\$sp" 1 } } */
+/* { dg-final { scan-assembler-not "s1:.*rdpgpr\t\\\$sp,\\\$sp.*\.end\ts1" } } */
+
+NOMIPS16 void __attribute__ ((interrupt("eic"))) e0 () { }
+NOMIPS16 void __attribute__ ((interrupt("vector=sw0"))) v0 () { }
+NOMIPS16 void __attribute__ ((interrupt("vector=sw1"))) v1 () { }
+NOMIPS16 void __attribute__ ((interrupt("vector=hw0"))) v2 () { }
+NOMIPS16 void __attribute__ ((interrupt("vector=hw1"))) v3 () { }
+NOMIPS16 void __attribute__ ((interrupt("vector=hw2"))) v4 () { }
+NOMIPS16 void __attribute__ ((interrupt("vector=hw3"))) v5 () { }
+NOMIPS16 void __attribute__ ((interrupt("vector=hw4"))) v6 () { }
+NOMIPS16 void __attribute__ ((interrupt("vector=hw5"))) v7 () { }
+
+NOMIPS16 void __attribute__ ((interrupt, use_shadow_register_set)) s0 () { }
+NOMIPS16 void __attribute__ ((interrupt, use_shadow_register_set("intstack"))) s1 () { }