diff mbox

[1/2] Automatic context save/restore for regular interrupts.

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

Commit Message

Claudiu Zissulescu May 5, 2017, 11:02 a.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/
2017-05-05  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.
	* testsuite/gcc.target/arc/interrupt-5.c: Newfile.
	* testsuite/gcc.target/arc/interrupt-6.c: Likewise.
	* testsuite/gcc.target/arc/interrupt-7.c: Likewise.
	* testsuite/gcc.target/arc/interrupt-8.c: Likewise.
	* testsuite/gcc.target/arc/interrupt-9.c: Likewise.
---
 gcc/config/arc/arc.c                       | 329 ++++++++++++++++++++++++++---
 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, 421 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

Andrew Burgess May 8, 2017, 2:40 p.m. UTC | #1
* Claudiu Zissulescu <Claudiu.Zissulescu@synopsys.com> [2017-05-05 13:02:43 +0200]:

> 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/
> 2017-05-05  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.
> 	* testsuite/gcc.target/arc/interrupt-5.c: Newfile.
> 	* testsuite/gcc.target/arc/interrupt-6.c: Likewise.
> 	* testsuite/gcc.target/arc/interrupt-7.c: Likewise.
> 	* testsuite/gcc.target/arc/interrupt-8.c: Likewise.
> 	* testsuite/gcc.target/arc/interrupt-9.c: Likewise.
> ---
>  gcc/config/arc/arc.c                       | 329 ++++++++++++++++++++++++++---
>  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, 421 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
> 
> diff --git a/gcc/config/arc/arc.c b/gcc/config/arc/arc.c
> index 4574481..a61faef 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;

I'm pretty sure GNU style is to put the comments above each entry in
the struct, such as:

     /* Last register number used by IRQ_CTRL_SAVED aux_reg.  */
     short irq_save_last_reg;

I did have a quick look though GCC and couldn't see many examples of
comments to the right.

> +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) \
> @@ -806,11 +830,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.  */

The comment seems to be out of line with both the documentation and
the actual functionality of the function.

Everything else looks fine to me,

Thanks,
Andrew






> +
> +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;
>  
> @@ -839,6 +962,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
> @@ -2235,14 +2380,52 @@ arc_compute_function_type (struct function *fun)
>     FIXME: This will not be needed if we used some arbitrary register
>     instead of r26.
>  */
> -#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) )
>  
> -#define MUST_SAVE_RETURN_ADDR \
> -  (cfun->machine->frame_info.save_return_addr)
> +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;
> +
> +  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;
> +
> +  return false;
> +}
> +
> +/* Helper function to wrap FRAME_POINTER_NEEDED.  We do this as
> +   FRAME_POINTER_NEEDED will not be true until the IRA (Integrated
> +   Register Allocator) pass, while we want to get the frame size
> +   correct earlier than the IRA pass.  */
> +static bool
> +arc_frame_pointer_needed (void)
> +{
> +  return (frame_pointer_needed);
> +}
> +
>  
>  /* Return non-zero if there are registers to be saved or loaded using
>     millicode thunks.  We can only use consecutive sequences starting
> @@ -2286,8 +2469,6 @@ arc_compute_frame_size (int size)	/* size = # of var. bytes allocated.  */
>    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 = &cfun->machine->frame_info;
>  
>    size = ARC_STACK_ALIGN (size);
> @@ -2306,15 +2487,13 @@ arc_compute_frame_size (int size)	/* size = # of var. bytes allocated.  */
>  
>    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;
>  	}
>      }
>  
> @@ -2330,9 +2509,9 @@ arc_compute_frame_size (int size)	/* size = # of var. bytes allocated.  */
>      }
>  
>    extra_size = 0;
> -  if (MUST_SAVE_RETURN_ADDR)
> +  if (arc_must_save_return_addr (cfun))
>      extra_size = 4;
> -  if (frame_pointer_needed)
> +  if (arc_frame_pointer_needed ())
>      extra_size += 4;
>  
>    /* 5) Space for variable arguments passed in registers */
> @@ -2357,7 +2536,7 @@ arc_compute_frame_size (int size)	/* size = # of var. bytes allocated.  */
>       Frame: pretend_size <blink> reg_size <fp> var_size args_size <--sp
>    */
>    reg_offset = (total_size - (pretend_size + reg_size + extra_size)
> -		+ (frame_pointer_needed ? 4 : 0));
> +		+ (arc_frame_pointer_needed () ? 4 : 0));
>  
>    /* Save computed information.  */
>    frame_info->total_size   = total_size;
> @@ -2548,6 +2727,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
> @@ -2561,6 +2811,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);
>  
>    size = ARC_STACK_ALIGN (size);
>  
> @@ -2588,16 +2839,25 @@ 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.  */
> @@ -2609,9 +2869,10 @@ arc_expand_prologue (void)
>        frame_size_to_allocate -= cfun->machine->frame_info.reg_size;
>      }
>  
> -
> -  /* Save frame pointer if needed.  */
> -  if (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));
> @@ -2621,6 +2882,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);
>      }
>  
> @@ -2675,13 +2941,13 @@ arc_expand_epilogue (int sibcall_p)
>       sp, but don't restore sp if we don't have to.  */
>  
>    if (!can_trust_sp_p)
> -    gcc_assert (frame_pointer_needed);
> +    gcc_assert (arc_frame_pointer_needed ());
>  
>    /* Restore stack pointer to the beginning of saved register area for
>       ARCompact ISA.  */
>    if (frame_size)
>      {
> -      if (frame_pointer_needed)
> +      if (arc_frame_pointer_needed ())
>  	frame_move (stack_pointer_rtx, frame_pointer_rtx);
>        else
>  	first_offset = frame_size;
> @@ -2692,7 +2958,8 @@ arc_expand_epilogue (int sibcall_p)
>  
>  
>    /* Restore any saved registers.  */
> -  if (frame_pointer_needed)
> +  if (arc_frame_pointer_needed ()
> +      && !ARC_AUTOFP_IRQ_P (fn_type))
>      {
>        rtx addr = gen_rtx_POST_INC (Pmode, stack_pointer_rtx);
>  
> @@ -2730,14 +2997,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;
> @@ -2802,7 +3070,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 802c3e9..2221fb5 100644
> --- a/gcc/config/arc/arc.md
> +++ b/gcc/config/arc/arc.md
> @@ -6241,6 +6241,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 68a558e..bd3b339 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
> @@ -14545,6 +14545,15 @@ hardware extensions.  Not available for ARC EM@.
>  
>  @end table
>  
> +@item -mirq-ctrl-saved=@var{register-range}, @var{blink}, @var{lp_count}
> +@opindex mirq-ctrl-saved
> +Specifies general-purposes registers that the processor automatically
> +saves/restores on interrupt entry and exit.  @var{register-range} is
> +specified as two registers separated by a dash.  The register range
> +always starts with @code{r0}, the upper limit is @code{fp} register.
> +@var{blink} and @var{lp_count} are optional.  This option is 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" } } */
> -- 
> 1.9.1
>
Claudiu Zissulescu May 9, 2017, 2:23 p.m. UTC | #2
Committed r247795 including the indicated fixes.

Thank you for your review,
Claudiu
diff mbox

Patch

diff --git a/gcc/config/arc/arc.c b/gcc/config/arc/arc.c
index 4574481..a61faef 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) \
@@ -806,11 +830,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;
 
@@ -839,6 +962,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
@@ -2235,14 +2380,52 @@  arc_compute_function_type (struct function *fun)
    FIXME: This will not be needed if we used some arbitrary register
    instead of r26.
 */
-#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) )
 
-#define MUST_SAVE_RETURN_ADDR \
-  (cfun->machine->frame_info.save_return_addr)
+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;
+
+  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;
+
+  return false;
+}
+
+/* Helper function to wrap FRAME_POINTER_NEEDED.  We do this as
+   FRAME_POINTER_NEEDED will not be true until the IRA (Integrated
+   Register Allocator) pass, while we want to get the frame size
+   correct earlier than the IRA pass.  */
+static bool
+arc_frame_pointer_needed (void)
+{
+  return (frame_pointer_needed);
+}
+
 
 /* Return non-zero if there are registers to be saved or loaded using
    millicode thunks.  We can only use consecutive sequences starting
@@ -2286,8 +2469,6 @@  arc_compute_frame_size (int size)	/* size = # of var. bytes allocated.  */
   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 = &cfun->machine->frame_info;
 
   size = ARC_STACK_ALIGN (size);
@@ -2306,15 +2487,13 @@  arc_compute_frame_size (int size)	/* size = # of var. bytes allocated.  */
 
   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;
 	}
     }
 
@@ -2330,9 +2509,9 @@  arc_compute_frame_size (int size)	/* size = # of var. bytes allocated.  */
     }
 
   extra_size = 0;
-  if (MUST_SAVE_RETURN_ADDR)
+  if (arc_must_save_return_addr (cfun))
     extra_size = 4;
-  if (frame_pointer_needed)
+  if (arc_frame_pointer_needed ())
     extra_size += 4;
 
   /* 5) Space for variable arguments passed in registers */
@@ -2357,7 +2536,7 @@  arc_compute_frame_size (int size)	/* size = # of var. bytes allocated.  */
      Frame: pretend_size <blink> reg_size <fp> var_size args_size <--sp
   */
   reg_offset = (total_size - (pretend_size + reg_size + extra_size)
-		+ (frame_pointer_needed ? 4 : 0));
+		+ (arc_frame_pointer_needed () ? 4 : 0));
 
   /* Save computed information.  */
   frame_info->total_size   = total_size;
@@ -2548,6 +2727,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
@@ -2561,6 +2811,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);
 
   size = ARC_STACK_ALIGN (size);
 
@@ -2588,16 +2839,25 @@  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.  */
@@ -2609,9 +2869,10 @@  arc_expand_prologue (void)
       frame_size_to_allocate -= cfun->machine->frame_info.reg_size;
     }
 
-
-  /* Save frame pointer if needed.  */
-  if (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));
@@ -2621,6 +2882,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);
     }
 
@@ -2675,13 +2941,13 @@  arc_expand_epilogue (int sibcall_p)
      sp, but don't restore sp if we don't have to.  */
 
   if (!can_trust_sp_p)
-    gcc_assert (frame_pointer_needed);
+    gcc_assert (arc_frame_pointer_needed ());
 
   /* Restore stack pointer to the beginning of saved register area for
      ARCompact ISA.  */
   if (frame_size)
     {
-      if (frame_pointer_needed)
+      if (arc_frame_pointer_needed ())
 	frame_move (stack_pointer_rtx, frame_pointer_rtx);
       else
 	first_offset = frame_size;
@@ -2692,7 +2958,8 @@  arc_expand_epilogue (int sibcall_p)
 
 
   /* Restore any saved registers.  */
-  if (frame_pointer_needed)
+  if (arc_frame_pointer_needed ()
+      && !ARC_AUTOFP_IRQ_P (fn_type))
     {
       rtx addr = gen_rtx_POST_INC (Pmode, stack_pointer_rtx);
 
@@ -2730,14 +2997,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;
@@ -2802,7 +3070,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 802c3e9..2221fb5 100644
--- a/gcc/config/arc/arc.md
+++ b/gcc/config/arc/arc.md
@@ -6241,6 +6241,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 68a558e..bd3b339 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
@@ -14545,6 +14545,15 @@  hardware extensions.  Not available for ARC EM@.
 
 @end table
 
+@item -mirq-ctrl-saved=@var{register-range}, @var{blink}, @var{lp_count}
+@opindex mirq-ctrl-saved
+Specifies general-purposes registers that the processor automatically
+saves/restores on interrupt entry and exit.  @var{register-range} is
+specified as two registers separated by a dash.  The register range
+always starts with @code{r0}, the upper limit is @code{fp} register.
+@var{blink} and @var{lp_count} are optional.  This option is 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" } } */