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