This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
Re: [PATCH 1/2] Automatic context save/restore for regular interrupts.
- From: Andrew Burgess <andrew dot burgess at embecosm dot com>
- To: Claudiu Zissulescu <Claudiu dot Zissulescu at synopsys dot com>
- Cc: gcc-patches at gcc dot gnu dot org, Francois dot Bedard at synopsys dot com, sandra at codesourcery dot com
- Date: Mon, 8 May 2017 15:40:04 +0100
- Subject: Re: [PATCH 1/2] Automatic context save/restore for regular interrupts.
- Authentication-results: sourceware.org; auth=none
- References: <58FF6D3F.7020707@codesourcery.com> <1493982164-29760-1-git-send-email-claziss@synopsys.com> <1493982164-29760-2-git-send-email-claziss@synopsys.com>
* 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
>