Message ID | 1493982164-29760-2-git-send-email-claziss@synopsys.com |
---|---|
State | New |
Headers | show |
* 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 >
Committed r247795 including the indicated fixes. Thank you for your review, Claudiu
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" } } */