diff mbox

[1/3,ARC] Automatic context save/restore for regular interrupts.

Message ID 1493125424-7298-2-git-send-email-claziss@synopsys.com
State New
Headers show

Commit Message

Claudiu Zissulescu April 25, 2017, 1:03 p.m. UTC
The AUX_IRQ_CTRL register controls the behavior of automated register
save and restore or prologue and epilogue sequences during a non-fast
interrupt entry and exit, and context save and restore instructions.

A user passes to the compiler the configuration of the AUX_IRQ_CTRL
register via mirq-ctrl-saved option.  This option, specifies
gneral-purposes registers that the processor saves/restores on
interrupt entry and exit, and it is only valid for ARC EM and ARC HS
cores.

gcc/
2016-10-03  Claudiu Zissulescu  <claziss@synopsys.com>

	* config/arc/arc.c (irq_ctrl_saved): New variable.
	(ARC_AUTOBLINK_IRQ_P): Define.
	(ARC_AUTOFP_IRQ_P): Likewise.
	(ARC_AUTO_IRQ_P): Likewise.
	(irq_range): New function.
	(arc_must_save_register): Likewise.
	(arc_must_save_return_addr): Likewise.
	(arc_dwarf_emit_irq_save_regs): Likewise.
	(arc_override_options): Handle deferred options.
	(MUST_SAVE_REGISTER): Deleted, replaced by arc_must_save_register.
	(MUST_SAVE_RETURN_ADDR): Deleted, replaced by
	arc_must_save_return_addr.
	(arc_compute_frame_size): Handle automated save and restore of
	registers.
	(arc_expand_prologue): Likewise.
	(arc_expand_epilogue): Likewise.
	* config/arc/arc.md (stack_irq_dwarf): New unspec instruction.
	* config/arc/arc.opt (mirq-ctrl-saved): New option.
	* doc/invoke.texi (mirq-ctrl-saved): Document option.

gcc/testsuite
2016-10-03  Claudiu Zissulescu  <claziss@synopsys.com>

	* gcc.target/arc/interrupt-5.c: Newfile.
	* gcc.target/arc/interrupt-6.c: Likewise.
	* gcc.target/arc/interrupt-7.c: Likewise.
	* gcc.target/arc/interrupt-8.c: Likewise.
	* gcc.target/arc/interrupt-9.c: Likewise.
---
 gcc/config/arc/arc.c                       | 320 ++++++++++++++++++++++++++---
 gcc/config/arc/arc.md                      |   8 +
 gcc/config/arc/arc.opt                     |   4 +
 gcc/doc/invoke.texi                        |  11 +-
 gcc/testsuite/gcc.target/arc/interrupt-5.c |  19 ++
 gcc/testsuite/gcc.target/arc/interrupt-6.c |  22 ++
 gcc/testsuite/gcc.target/arc/interrupt-7.c |  16 ++
 gcc/testsuite/gcc.target/arc/interrupt-8.c |  27 +++
 gcc/testsuite/gcc.target/arc/interrupt-9.c |  17 ++
 9 files changed, 412 insertions(+), 32 deletions(-)
 create mode 100644 gcc/testsuite/gcc.target/arc/interrupt-5.c
 create mode 100644 gcc/testsuite/gcc.target/arc/interrupt-6.c
 create mode 100644 gcc/testsuite/gcc.target/arc/interrupt-7.c
 create mode 100644 gcc/testsuite/gcc.target/arc/interrupt-8.c
 create mode 100644 gcc/testsuite/gcc.target/arc/interrupt-9.c

Comments

Sandra Loosemore April 25, 2017, 3:37 p.m. UTC | #1
On 04/25/2017 07:03 AM, Claudiu Zissulescu wrote:
> diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
> index 0eeea7b..cebafe6 100644
> --- a/gcc/doc/invoke.texi
> +++ b/gcc/doc/invoke.texi
> @@ -606,7 +606,7 @@ Objective-C and Objective-C++ Dialects}.
>   -mnorm  -mspfp  -mspfp-compact  -mspfp-fast  -msimd  -msoft-float  -mswap @gol
>   -mcrc  -mdsp-packa  -mdvbf  -mlock  -mmac-d16  -mmac-24  -mrtsc  -mswape @gol
>   -mtelephony  -mxy  -misize  -mannotate-align  -marclinux  -marclinux_prof @gol
> --mlong-calls  -mmedium-calls  -msdata @gol
> +-mlong-calls  -mmedium-calls  -msdata -mirq-ctrl-saved @gol
>   -mvolatile-cache  -mtp-regno=@var{regno} @gol
>   -malign-call  -mauto-modify-reg  -mbbit-peephole  -mno-brcc @gol
>   -mcase-vector-pcrel  -mcompact-casesi  -mno-cond-exec  -mearly-cbranchsi @gol
> @@ -14578,6 +14578,15 @@ hardware extensions.  Not available for ARC EM@.
>
>   @end table
>
> +@item -mirq-ctrl-saved="REGS"

Use @var{regs} instead of REGS.  I assume the quotes are part of the 
literal syntax?

> +@opindex mirq-ctrl-saved
> +Specifies gneral-purposes registers that the processor saves/restores

s/gneral-purposes/general-purpose/

> +on interrupt entry and exit.  Permited values: r0-r29, fp, blink, and

s/Permited/Permissible/

I think literal strings that are part of the option syntax should have 
@samp markup.

> +lp_count.  Registers needs to be specified as ranges such as "r0-r3".
> +A register range always starts with r0.  Registers blink and lp_count
> +can be specified individually.

You're contradicting yourself here:  you say that registers need to be 
specified as ranges, and then you say that some registers can be 
specified individually.  Can you come up with a better explanation of 
the syntax?  E.g., is it possible to specify multiple registers without 
using a range?  Or do you specify the option multiple times in that case?

> +Only valid for ARC EM and ARC HS
> +cores.
> +

I suggest a paragraph break here, or at least specify "This option is 
only valid...." so it's clear what the subject of this sentence is.

-Sandra
Claudiu Zissulescu April 26, 2017, 1:10 p.m. UTC | #2
> > +@item -mirq-ctrl-saved="REGS"
> 
> Use @var{regs} instead of REGS.  I assume the quotes are part of the
> literal syntax?

In fact here we should have a register range (e.g., -mirq-ctrl-saved=r0-r9).

> > +lp_count.  Registers needs to be specified as ranges such as "r0-r3".
> > +A register range always starts with r0.  Registers blink and lp_count
> > +can be specified individually.
> 
> You're contradicting yourself here:  you say that registers need to be
> specified as ranges, and then you say that some registers can be
> specified individually.  Can you come up with a better explanation of
> the syntax?  E.g., is it possible to specify multiple registers without
> using a range?  Or do you specify the option multiple times in that case?
> 

Blink (r31) and lp_count (r60) are special registers. The range can go from r0 up to r29.  Thus, the option is a sequence made of a register range starting with r0 to r29, and optional two individual registers: blink (r31) and lp_count (r60). Hence, such an option looks like this:
	-mirq-ctrl-saved=r0-r29,blink,lp_count


Thanks,
Claudiu
Claudiu Zissulescu May 5, 2017, 11:02 a.m. UTC | #3
From: claziss <claziss@synopsys.com>

Hi,

I've updated the two patches (out of three) as indicated by Sandra.

Ok to apply?
Claudiu

Claudiu Zissulescu (2):
  Automatic context save/restore for regular interrupts.
  Fast interrupts support.

 gcc/config/arc/arc.c                       | 407 ++++++++++++++++++++++++++---
 gcc/config/arc/arc.h                       |  13 +-
 gcc/config/arc/arc.md                      |  17 +-
 gcc/config/arc/arc.opt                     |   8 +
 gcc/doc/invoke.texi                        |  21 +-
 gcc/testsuite/gcc.target/arc/firq-1.c      |  27 ++
 gcc/testsuite/gcc.target/arc/firq-2.c      |  31 +++
 gcc/testsuite/gcc.target/arc/firq-3.c      |  40 +++
 gcc/testsuite/gcc.target/arc/firq-4.c      |  31 +++
 gcc/testsuite/gcc.target/arc/firq-5.c      |  15 ++
 gcc/testsuite/gcc.target/arc/firq-6.c      |  21 ++
 gcc/testsuite/gcc.target/arc/interrupt-5.c |  19 ++
 gcc/testsuite/gcc.target/arc/interrupt-6.c |  22 ++
 gcc/testsuite/gcc.target/arc/interrupt-7.c |  16 ++
 gcc/testsuite/gcc.target/arc/interrupt-8.c |  27 ++
 gcc/testsuite/gcc.target/arc/interrupt-9.c |  17 ++
 16 files changed, 684 insertions(+), 48 deletions(-)
 create mode 100644 gcc/testsuite/gcc.target/arc/firq-1.c
 create mode 100644 gcc/testsuite/gcc.target/arc/firq-2.c
 create mode 100644 gcc/testsuite/gcc.target/arc/firq-3.c
 create mode 100644 gcc/testsuite/gcc.target/arc/firq-4.c
 create mode 100644 gcc/testsuite/gcc.target/arc/firq-5.c
 create mode 100644 gcc/testsuite/gcc.target/arc/firq-6.c
 create mode 100644 gcc/testsuite/gcc.target/arc/interrupt-5.c
 create mode 100644 gcc/testsuite/gcc.target/arc/interrupt-6.c
 create mode 100644 gcc/testsuite/gcc.target/arc/interrupt-7.c
 create mode 100644 gcc/testsuite/gcc.target/arc/interrupt-8.c
 create mode 100644 gcc/testsuite/gcc.target/arc/interrupt-9.c
diff mbox

Patch

diff --git a/gcc/config/arc/arc.c b/gcc/config/arc/arc.c
index 696e4a0..35c5295 100644
--- a/gcc/config/arc/arc.c
+++ b/gcc/config/arc/arc.c
@@ -63,6 +63,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "builtins.h"
 #include "rtl-iter.h"
 #include "alias.h"
+#include "opts.h"
 
 /* Which cpu we're compiling for (ARC600, ARC601, ARC700).  */
 static char arc_cpu_name[10] = "";
@@ -111,6 +112,29 @@  struct GTY (()) arc_ccfsm
   int target_label;
 };
 
+/* Status of the IRQ_CTRL_AUX register.  */
+typedef struct irq_ctrl_saved_t
+{
+  short irq_save_last_reg;      /* Last register number used by
+				   IRQ_CTRL_SAVED aux_reg.  */
+  bool  irq_save_blink;         /* True if BLINK is automatically
+				   saved.  */
+  bool  irq_save_lpcount;       /* True if LPCOUNT is automatically
+				   saved.  */
+} irq_ctrl_saved_t;
+static irq_ctrl_saved_t irq_ctrl_saved;
+
+#define ARC_AUTOBLINK_IRQ_P(FNTYPE)				\
+  (ARC_INTERRUPT_P (FNTYPE) && irq_ctrl_saved.irq_save_blink)
+
+#define ARC_AUTOFP_IRQ_P(FNTYPE)					\
+  (ARC_INTERRUPT_P (FNTYPE) && (irq_ctrl_saved.irq_save_last_reg > 26))
+
+#define ARC_AUTO_IRQ_P(FNTYPE)				\
+  (ARC_INTERRUPT_P (FNTYPE)				\
+   && (irq_ctrl_saved.irq_save_blink			\
+       || (irq_ctrl_saved.irq_save_last_reg >= 0)))
+
 #define arc_ccfsm_current cfun->machine->ccfsm_current
 
 #define ARC_CCFSM_BRANCH_DELETED_P(STATE) \
@@ -809,11 +833,110 @@  arc_init (void)
     }
 }
 
+/* Parse -mirq-ctrl-saved= option string.  Registers may be specified
+   individually, or as ranges such as "r0-r3".  Registers accepted are
+   r0 through r31 and lp_count.  Registers and ranges must be
+   comma-separated.  */
+
+static void
+irq_range (const char *cstr)
+{
+  int i, first, last, blink, lpcount, xreg;
+  char *str, *dash, *comma;
+
+  i = strlen (cstr);
+  str = (char *) alloca (i + 1);
+  memcpy (str, cstr, i + 1);
+  blink = -1;
+  lpcount = -1;
+
+  dash = strchr (str, '-');
+  if (!dash)
+    {
+      warning (0, "value of -mirq-ctrl-saved must have form R0-REGx");
+      return;
+    }
+  *dash = '\0';
+
+  comma = strchr (dash + 1, ',');
+  if (comma)
+    *comma = '\0';
+
+  first = decode_reg_name (str);
+  if (first != 0)
+    {
+      warning (0, "first register must be R0");
+      return;
+    }
+
+  /* At this moment we do not have the register names initialized
+     accordingly.  */
+  if (!strcmp (dash + 1, "ilink"))
+    last = 29;
+  else
+    last = decode_reg_name (dash + 1);
+
+  if (last < 0)
+    {
+      warning (0, "unknown register name: %s", dash + 1);
+      return;
+    }
+
+  if (!(last & 0x01))
+    {
+      warning (0, "last register name %s must be an odd register", dash + 1);
+      return;
+    }
+
+  *dash = '-';
+
+  if (first > last)
+    {
+      warning (0, "%s-%s is an empty range", str, dash + 1);
+      return;
+    }
+
+  while (comma)
+    {
+      *comma = ',';
+      str = comma + 1;
+
+      comma = strchr (str, ',');
+      if (comma)
+	*comma = '\0';
+
+      xreg = decode_reg_name (str);
+      switch (xreg)
+	{
+	case 31:
+	  blink = 31;
+	  break;
+
+	case 60:
+	  lpcount = 60;
+	  break;
+
+	default:
+	  warning (0, "unknown register name: %s", str);
+	  return;
+	}
+    }
+
+  irq_ctrl_saved.irq_save_last_reg = last;
+  irq_ctrl_saved.irq_save_blink    = (blink == 31) || (last == 31);
+  irq_ctrl_saved.irq_save_lpcount  = (lpcount == 60);
+}
+
 /* Check ARC options, generate derived target attributes.  */
 
 static void
 arc_override_options (void)
 {
+  unsigned int i;
+  cl_deferred_option *opt;
+  vec<cl_deferred_option> *vopt
+    = (vec<cl_deferred_option> *) arc_deferred_options;
+
   if (arc_cpu == PROCESSOR_NONE)
     arc_cpu = TARGET_CPU_DEFAULT;
 
@@ -842,6 +965,28 @@  arc_override_options (void)
       gcc_unreachable ();
     }
 
+  irq_ctrl_saved.irq_save_last_reg = -1;
+  irq_ctrl_saved.irq_save_blink    = false;
+  irq_ctrl_saved.irq_save_lpcount  = false;
+
+  /* Handle the deferred options.  */
+  if (vopt)
+    FOR_EACH_VEC_ELT (*vopt, i, opt)
+      {
+	switch (opt->opt_index)
+	  {
+	  case OPT_mirq_ctrl_saved_:
+	    if (TARGET_V2)
+	      irq_range (opt->arg);
+	    else
+	      warning (0, "option -mirq-ctrl-saved valid only for ARC v2 processors");
+	    break;
+
+	  default:
+	    gcc_unreachable();
+	  }
+      }
+
   /* Set cpu flags accordingly to architecture/selected cpu.  The cpu
      specific flags are set in arc-common.c.  The architecture forces
      the default hardware configurations in, regardless what command
@@ -2254,15 +2399,43 @@  arc_compute_function_type (struct function *fun)
    code like this.  The number of frames that use __builtin_eh_return
    is pretty low, so optimising them is not critical right now.  */
 
-#define MUST_SAVE_REGISTER(regno, interrupt_p)				\
-  (((regno) != RETURN_ADDR_REGNUM && (regno) != FRAME_POINTER_REGNUM	\
-    && (df_regs_ever_live_p (regno)					\
-	&& (!call_used_regs[regno] || interrupt_p)))			\
-   || (flag_pic && crtl->uses_pic_offset_table				\
-       && regno == PIC_OFFSET_TABLE_REGNUM)				\
-   || (crtl->calls_eh_return && (regno > 2 && regno < 27)))
+static bool
+arc_must_save_register (int regno, struct function *func)
+{
+  enum arc_function_type fn_type = arc_compute_function_type (func);
+  bool irq_auto_save_p = ((irq_ctrl_saved.irq_save_last_reg >= regno)
+			  && ARC_INTERRUPT_P (fn_type));
+
+  if ((regno) != RETURN_ADDR_REGNUM
+      && (regno) != FRAME_POINTER_REGNUM
+      && df_regs_ever_live_p (regno)
+      && (!call_used_regs[regno]
+	  || ARC_INTERRUPT_P (fn_type))
+      /* Do not emit code for auto saved regs.  */
+      && !irq_auto_save_p)
+    return true;
+
+  if (flag_pic && crtl->uses_pic_offset_table
+      && regno == PIC_OFFSET_TABLE_REGNUM)
+    return true;
+
+  if (crtl->calls_eh_return && (regno > 2 && regno < 27))
+    return true;
+
+  return false;
+}
+
+/* Return true if the return address must be saved in the current function,
+   otherwise return false.  */
+
+static bool
+arc_must_save_return_addr (struct function *func)
+{
+  if (func->machine->frame_info.save_return_addr)
+    return true;
 
-#define MUST_SAVE_RETURN_ADDR	(cfun->machine->frame_info.save_return_addr)
+  return false;
+}
 
 /* Helper function to wrap FRAME_POINTER_NEEDED.  We do this as
    FRAME_POINTER_NEEDED will not be true until the IRA (Integrated
@@ -2280,9 +2453,9 @@  arc_compute_function_type (struct function *fun)
 
    As the frame pointer is handled as a special case in our prologue
    and epilogue code it must not be saved and restored using the
-   MUST_SAVE_REGISTER mechanism otherwise we run into issues where GCC
-   believes that the function is not using a frame pointer and that
-   the value in the fp register is the frame pointer, while the
+   arc_must_save_register mechanism otherwise we run into issues where
+   GCC believes that the function is not using a frame pointer and
+   that the value in the fp register is the frame pointer, while the
    prologue and epilogue are busy saving and restoring the fp
    register.  This issue is fixed in this commit too.
 
@@ -2352,8 +2525,6 @@  arc_compute_frame_size (void)
   unsigned int total_size, var_size, args_size, pretend_size, extra_size;
   unsigned int reg_size, reg_offset;
   unsigned int gmask;
-  enum arc_function_type fn_type;
-  int interrupt_p;
   struct arc_frame_info *frame_info;
   int size;
 
@@ -2378,15 +2549,13 @@  arc_compute_frame_size (void)
 
   reg_size = 0;
   gmask = 0;
-  fn_type = arc_compute_function_type (cfun);
-  interrupt_p = ARC_INTERRUPT_P (fn_type);
 
   for (regno = 0; regno <= 31; regno++)
     {
-      if (MUST_SAVE_REGISTER (regno, interrupt_p))
+      if (arc_must_save_register (regno, cfun))
 	{
 	  reg_size += UNITS_PER_WORD;
-	  gmask |= 1 << regno;
+	  gmask |= 1L << regno;
 	}
     }
 
@@ -2419,7 +2588,7 @@  arc_compute_frame_size (void)
     }
 
   extra_size = 0;
-  if (MUST_SAVE_RETURN_ADDR)
+  if (arc_must_save_return_addr (cfun))
     extra_size = 4;
   if (arc_frame_pointer_needed ())
     extra_size += 4;
@@ -2641,6 +2810,77 @@  arc_save_restore (rtx base_reg,
 int arc_return_address_regs[4]
   = {0, RETURN_ADDR_REGNUM, ILINK1_REGNUM, ILINK2_REGNUM};
 
+
+/* Build dwarf information when the context is saved via AUX_IRQ_CTRL
+   mechanism.  */
+
+static void
+arc_dwarf_emit_irq_save_regs (void)
+{
+  rtx tmp, par, insn, reg;
+  int i, offset, j;
+
+  par = gen_rtx_SEQUENCE (VOIDmode,
+			  rtvec_alloc (irq_ctrl_saved.irq_save_last_reg + 1
+				       + irq_ctrl_saved.irq_save_blink
+				       + irq_ctrl_saved.irq_save_lpcount
+				       + 1));
+
+  /* Build the stack adjustment note for unwind info.  */
+  j = 0;
+  offset = UNITS_PER_WORD * (irq_ctrl_saved.irq_save_last_reg + 1
+			     + irq_ctrl_saved.irq_save_blink
+			     + irq_ctrl_saved.irq_save_lpcount);
+  tmp = plus_constant (Pmode, stack_pointer_rtx, -1 * offset);
+  tmp = gen_rtx_SET (stack_pointer_rtx, tmp);
+  RTX_FRAME_RELATED_P (tmp) = 1;
+  XVECEXP (par, 0, j++) = tmp;
+
+  offset -= UNITS_PER_WORD;
+
+  /* 1st goes LP_COUNT.  */
+  if (irq_ctrl_saved.irq_save_lpcount)
+    {
+      reg = gen_rtx_REG (SImode, 60);
+      tmp = plus_constant (Pmode, stack_pointer_rtx, offset);
+      tmp = gen_frame_mem (SImode, tmp);
+      tmp = gen_rtx_SET (tmp, reg);
+      RTX_FRAME_RELATED_P (tmp) = 1;
+      XVECEXP (par, 0, j++) = tmp;
+      offset -= UNITS_PER_WORD;
+    }
+
+  /* 2nd goes BLINK.  */
+  if (irq_ctrl_saved.irq_save_blink)
+    {
+      reg = gen_rtx_REG (SImode, 31);
+      tmp = plus_constant (Pmode, stack_pointer_rtx, offset);
+      tmp = gen_frame_mem (SImode, tmp);
+      tmp = gen_rtx_SET (tmp, reg);
+      RTX_FRAME_RELATED_P (tmp) = 1;
+      XVECEXP (par, 0, j++) = tmp;
+      offset -= UNITS_PER_WORD;
+    }
+
+  /* Build the parallel of the remaining registers recorded as saved
+     for unwind.  */
+  for (i = irq_ctrl_saved.irq_save_last_reg; i >= 0; i--)
+    {
+      reg = gen_rtx_REG (SImode, i);
+      tmp = plus_constant (Pmode, stack_pointer_rtx, offset);
+      tmp = gen_frame_mem (SImode, tmp);
+      tmp = gen_rtx_SET (tmp, reg);
+      RTX_FRAME_RELATED_P (tmp) = 1;
+      XVECEXP (par, 0, j++) = tmp;
+      offset -= UNITS_PER_WORD;
+    }
+
+  /* Dummy insn used to anchor the dwarf info.  */
+  insn = emit_insn (gen_stack_irq_dwarf());
+  add_reg_note (insn, REG_FRAME_RELATED_EXPR, par);
+  RTX_FRAME_RELATED_P (insn) = 1;
+}
+
 /* Set up the stack and frame pointer (if desired) for the function.  */
 
 void
@@ -2654,6 +2894,7 @@  arc_expand_prologue (void)
      Change the stack layout so that we rather store a high register with the
      PRE_MODIFY, thus enabling more short insn generation.)  */
   int first_offset = 0;
+  enum arc_function_type fn_type = arc_compute_function_type (cfun);
 
   /* Compute total frame size.  */
   size = arc_compute_frame_size ();
@@ -2677,30 +2918,41 @@  arc_expand_prologue (void)
       frame_size_to_allocate -= cfun->machine->frame_info.pretend_size;
     }
 
+  /* IRQ using automatic save mechanism will save the register before
+     anything we do.  */
+  if (ARC_AUTO_IRQ_P (fn_type))
+    {
+      arc_dwarf_emit_irq_save_regs ();
+    }
+
   /* The home-grown ABI says link register is saved first.  */
-  if (MUST_SAVE_RETURN_ADDR)
+  if (arc_must_save_return_addr (cfun)
+      && !ARC_AUTOBLINK_IRQ_P (fn_type))
     {
       rtx ra = gen_rtx_REG (SImode, RETURN_ADDR_REGNUM);
-      rtx mem = gen_frame_mem (Pmode, gen_rtx_PRE_DEC (Pmode, stack_pointer_rtx));
+      rtx mem = gen_frame_mem (Pmode,
+			       gen_rtx_PRE_DEC (Pmode,
+						stack_pointer_rtx));
 
       frame_move_inc (mem, ra, stack_pointer_rtx, 0);
       frame_size_to_allocate -= UNITS_PER_WORD;
-
-    } /* MUST_SAVE_RETURN_ADDR */
+    }
 
   /* Save any needed call-saved regs (and call-used if this is an
      interrupt handler) for ARCompact ISA.  */
   if (cfun->machine->frame_info.reg_size)
     {
       first_offset = -cfun->machine->frame_info.reg_size;
+
       /* N.B. FRAME_POINTER_MASK and RETURN_ADDR_MASK are cleared in gmask.  */
       arc_save_restore (stack_pointer_rtx, gmask, 0, &first_offset);
       frame_size_to_allocate -= cfun->machine->frame_info.reg_size;
     }
 
-
-  /* Save frame pointer if needed.  */
-  if (arc_frame_pointer_needed ())
+  /* Save frame pointer if needed.  First save the FP on stack, if not
+     autosaved.  */
+  if (arc_frame_pointer_needed ()
+      && !ARC_AUTOFP_IRQ_P (fn_type))
     {
       rtx addr = gen_rtx_PLUS (Pmode, stack_pointer_rtx,
 			       GEN_INT (-UNITS_PER_WORD + first_offset));
@@ -2710,6 +2962,11 @@  arc_expand_prologue (void)
       frame_move_inc (mem, frame_pointer_rtx, stack_pointer_rtx, 0);
       frame_size_to_allocate -= UNITS_PER_WORD;
       first_offset = 0;
+    }
+
+  /* Emit mov fp,sp.  */
+  if (arc_frame_pointer_needed ())
+    {
       frame_move (frame_pointer_rtx, stack_pointer_rtx);
     }
 
@@ -2778,7 +3035,8 @@  arc_expand_epilogue (int sibcall_p)
 
 
   /* Restore any saved registers.  */
-  if (arc_frame_pointer_needed ())
+  if (arc_frame_pointer_needed ()
+      && !ARC_AUTOFP_IRQ_P (fn_type))
     {
       rtx addr = gen_rtx_POST_INC (Pmode, stack_pointer_rtx);
 
@@ -2816,14 +3074,15 @@  arc_expand_epilogue (int sibcall_p)
 	    : satisfies_constraint_C2a (GEN_INT (first_offset))))
        /* Also do this if we have both gprs and return
 	  address to restore, and they both would need a LIMM.  */
-       || (MUST_SAVE_RETURN_ADDR
-	   && !SMALL_INT ((cfun->machine->frame_info.reg_size + first_offset) >> 2)
-	   && cfun->machine->frame_info.gmask))
+      || (arc_must_save_return_addr (cfun)
+	  && !SMALL_INT ((cfun->machine->frame_info.reg_size + first_offset) >> 2)
+	  && cfun->machine->frame_info.gmask))
     {
       frame_stack_add (first_offset);
       first_offset = 0;
     }
-  if (MUST_SAVE_RETURN_ADDR)
+  if (arc_must_save_return_addr (cfun)
+      && !ARC_AUTOBLINK_IRQ_P (fn_type))
     {
       rtx ra = gen_rtx_REG (Pmode, RETURN_ADDR_REGNUM);
       int ra_offs = cfun->machine->frame_info.reg_size + first_offset;
@@ -2888,7 +3147,6 @@  arc_expand_epilogue (int sibcall_p)
 			   & ~(FRAME_POINTER_MASK | RETURN_ADDR_MASK), 1, &first_offset);
     }
 
-
   /* The rest of this function does the following:
      ARCompact    : handle epilogue_delay, restore sp (phase-2), return
   */
diff --git a/gcc/config/arc/arc.md b/gcc/config/arc/arc.md
index 42ea9a7..0f03170 100644
--- a/gcc/config/arc/arc.md
+++ b/gcc/config/arc/arc.md
@@ -6208,6 +6208,14 @@ 
   [(set (zero_extract:SI (match_dup 3) (match_dup 1) (match_dup 2))
 	(zero_extract:SI (match_dup 0) (match_dup 1) (match_dup 2)))])
 
+;; Dummy pattern used as a place holder for automatically saved
+;; registers.
+(define_insn "stack_irq_dwarf"
+  [(unspec_volatile [(const_int 1)] VUNSPEC_ARC_STACK_IRQ)]
+  ""
+  ""
+  [(set_attr "length" "0")])
+
 ;; include the arc-FPX instructions
 (include "fpx.md")
 
diff --git a/gcc/config/arc/arc.opt b/gcc/config/arc/arc.opt
index 6060ded..483470d 100644
--- a/gcc/config/arc/arc.opt
+++ b/gcc/config/arc/arc.opt
@@ -486,3 +486,7 @@  Enable use of NPS400 xld/xst extension.
 munaligned-access
 Target Report Var(unaligned_access) Init(UNALIGNED_ACCESS_DEFAULT)
 Enable unaligned word and halfword accesses to packed data.
+
+mirq-ctrl-saved=
+Target RejectNegative Joined Var(arc_deferred_options) Defer
+Specifies the registers that the processor saves on an interrupt entry and exit.
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 0eeea7b..cebafe6 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -606,7 +606,7 @@  Objective-C and Objective-C++ Dialects}.
 -mnorm  -mspfp  -mspfp-compact  -mspfp-fast  -msimd  -msoft-float  -mswap @gol
 -mcrc  -mdsp-packa  -mdvbf  -mlock  -mmac-d16  -mmac-24  -mrtsc  -mswape @gol
 -mtelephony  -mxy  -misize  -mannotate-align  -marclinux  -marclinux_prof @gol
--mlong-calls  -mmedium-calls  -msdata @gol
+-mlong-calls  -mmedium-calls  -msdata -mirq-ctrl-saved @gol
 -mvolatile-cache  -mtp-regno=@var{regno} @gol
 -malign-call  -mauto-modify-reg  -mbbit-peephole  -mno-brcc @gol
 -mcase-vector-pcrel  -mcompact-casesi  -mno-cond-exec  -mearly-cbranchsi @gol
@@ -14578,6 +14578,15 @@  hardware extensions.  Not available for ARC EM@.
 
 @end table
 
+@item -mirq-ctrl-saved="REGS"
+@opindex mirq-ctrl-saved
+Specifies gneral-purposes registers that the processor saves/restores
+on interrupt entry and exit.  Permited values: r0-r29, fp, blink, and
+lp_count.  Registers needs to be specified as ranges such as "r0-r3".
+A register range always starts with r0.  Registers blink and lp_count
+can be specified individually.  Only valid for ARC EM and ARC HS
+cores.
+
 @end table
 
 The following options are passed through to the assembler, and also
diff --git a/gcc/testsuite/gcc.target/arc/interrupt-5.c b/gcc/testsuite/gcc.target/arc/interrupt-5.c
new file mode 100644
index 0000000..ee01d76
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arc/interrupt-5.c
@@ -0,0 +1,19 @@ 
+/* { dg-do compile } */
+/* { dg-skip-if "Not available for ARCv1" { arc700 ||  arc6xx } } */
+/* { dg-options "-O2 -mirq-ctrl-saved=r0-r3,blink" } */
+
+/* Check if the registers R0-R3,blink are automatically saved. */
+
+extern int bar (void *);
+
+void  __attribute__ ((interrupt("ilink")))
+foo(void)
+{
+  bar (0);
+  __asm__ volatile ( "" : : : "r0","r1","r2","r3");
+}
+/* { dg-final { scan-assembler-not "st.*r0,\\\[sp" } } */
+/* { dg-final { scan-assembler-not "st.*r1,\\\[sp" } } */
+/* { dg-final { scan-assembler-not "st.*r2,\\\[sp" } } */
+/* { dg-final { scan-assembler-not "st.*r3,\\\[sp" } } */
+/* { dg-final { scan-assembler-not "push_s blink" } } */
diff --git a/gcc/testsuite/gcc.target/arc/interrupt-6.c b/gcc/testsuite/gcc.target/arc/interrupt-6.c
new file mode 100644
index 0000000..509ff30
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arc/interrupt-6.c
@@ -0,0 +1,22 @@ 
+/* { dg-do compile } */
+/* { dg-skip-if "Not available for ARCv1" { arc700 || arc6xx } } */
+/* { dg-options "-O2 -mirq-ctrl-saved=r0-ilink" } */
+
+#include <alloca.h>
+
+/* Check if ilink is recognized. Check how FP and BLINK are saved.
+   BLINK is saved last on the stack because the IRQ autosave will do
+   first r0-ilink.  To avoid this ABI exception, one needs to autosave
+   always blink when using the IRQ autosave feature.  */
+
+extern int bar (void *);
+
+void  __attribute__ ((interrupt("ilink")))
+foo(void)
+{
+  int *p = alloca (10);
+  bar (p);
+}
+/* { dg-final { scan-assembler-not ".*fp,\\\[sp" } } */
+/* { dg-final { scan-assembler "ld.*blink,\\\[sp\\\]" } } */
+/* { dg-final { scan-assembler "push_s.*blink" } } */
diff --git a/gcc/testsuite/gcc.target/arc/interrupt-7.c b/gcc/testsuite/gcc.target/arc/interrupt-7.c
new file mode 100644
index 0000000..547dfd3
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arc/interrupt-7.c
@@ -0,0 +1,16 @@ 
+/* { dg-do compile } */
+/* { dg-skip-if "Not available for ARCv1" { arc700 ||  arc6xx } } */
+/* { dg-options "-O2 -mirq-ctrl-saved=r0-r17,blink" } */
+
+/* Check if the registers R0-R17,blink are automatically saved. */
+
+void  __attribute__ ((interrupt("ilink")))
+foo(void)
+{
+  __asm__ volatile ( "" : : : "r13","r14","r15","r16");
+}
+/* { dg-final { scan-assembler-not "st.*r13,\\\[sp" } } */
+/* { dg-final { scan-assembler-not "st.*r14,\\\[sp" } } */
+/* { dg-final { scan-assembler-not "st.*r15,\\\[sp" } } */
+/* { dg-final { scan-assembler-not "st.*r16,\\\[sp" } } */
+/* { dg-final { scan-assembler-not "push_s blink" } } */
diff --git a/gcc/testsuite/gcc.target/arc/interrupt-8.c b/gcc/testsuite/gcc.target/arc/interrupt-8.c
new file mode 100644
index 0000000..60fd87b
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arc/interrupt-8.c
@@ -0,0 +1,27 @@ 
+/* { dg-do compile } */
+/* { dg-skip-if "Not available for ARCv1" { arc700 || arc6xx } } */
+/* { dg-options "-O2 -mirq-ctrl-saved=r0-r17" } */
+
+/* Check if the registers R0-R17 are automatically saved.  GP is saved
+   by the compiler.  */
+
+int a;
+
+void  __attribute__ ((interrupt("ilink")))
+foo(void)
+{
+  __asm__ volatile ( "" : : : "r0","r1","r2","r3");
+  __asm__ volatile ( "" : : : "r13","r14","r15","r16");
+  a++;
+}
+/* { dg-final { scan-assembler-not "st.*r13,\\\[sp" } } */
+/* { dg-final { scan-assembler-not "st.*r14,\\\[sp" } } */
+/* { dg-final { scan-assembler-not "st.*r15,\\\[sp" } } */
+/* { dg-final { scan-assembler-not "st.*r16,\\\[sp" } } */
+/* { dg-final { scan-assembler "st.*gp,\\\[sp,-4\\\]" } } */
+/* { dg-final { scan-assembler "ld.*gp,\\\[sp\\\]" } } */
+/* { dg-final { scan-assembler-not "st.*r0,\\\[sp" } } */
+/* { dg-final { scan-assembler-not "st.*r1,\\\[sp" } } */
+/* { dg-final { scan-assembler-not "st.*r2,\\\[sp" } } */
+/* { dg-final { scan-assembler-not "st.*r3,\\\[sp" } } */
+/* { dg-final { scan-assembler "rtie" } } */
diff --git a/gcc/testsuite/gcc.target/arc/interrupt-9.c b/gcc/testsuite/gcc.target/arc/interrupt-9.c
new file mode 100644
index 0000000..4547fef
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arc/interrupt-9.c
@@ -0,0 +1,17 @@ 
+/* { dg-do compile } */
+/* { dg-require-effective-target archs }*/
+/* { dg-options "-O0 -mirq-ctrl-saved=r0-fp" } */
+
+/* Check if we get the move operation between fp and sp.  */
+
+void __attribute__ ((interrupt("ilink")))
+handler1 (void)
+{
+  asm (""
+       :
+       :
+       : "r0", "r1", "r2", "r3", "r4",
+         "r5", "r6", "r7", "r8", "r9");
+}
+/* { dg-final { scan-assembler "mov.*fp,sp" } } */
+/* { dg-final { scan-assembler-not ".*fp,\\\[sp" } } */