#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] = "";
int target_label;
};
+/* Status of the IRQ_CTRL_AUX register. */
+typedef struct irq_ctrl_saved_t
+{
+ /* Last register number used by IRQ_CTRL_SAVED aux_reg. */
+ short irq_save_last_reg;
+ /* True if BLINK is automatically saved. */
+ bool irq_save_blink;
+ /* True if LPCOUNT is automatically saved. */
+ bool irq_save_lpcount;
+} 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) \
}
}
+/* Parse -mirq-ctrl-saved=RegisterRange, blink, lp_copunt. The
+ register range is specified as two registers separated by a dash.
+ It always starts with r0, and its upper limit is fp register.
+ blink and lp_count registers are optional. */
+
+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;
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
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
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);
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;
}
}
}
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 */
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;
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
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);
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. */
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));
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);
}
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;
/* 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);
: 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;
& ~(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
*/