this to be no less than the 1/p */
#define MAX_INSNS_SKIPPED 3
+/* ZOL control registers. */
+#define AUX_LP_START 0x02
+#define AUX_LP_END 0x03
+
+/* FPX AUX registers. */
+#define AUX_DPFP_START 0x301
+
/* A nop is needed between a 4 byte insn that sets the condition codes and
a branch that uses them (the same isn't true for an 8 byte insn that sets
the condition codes). Set by arc_ccfsm_advance. Used by
/* Globally visible information about currently selected cpu. */
const arc_cpu_t *arc_selected_cpu;
+/* Traditionally, we push saved registers first in the prologue,
+ then we allocate the rest of the frame - and reverse in the epilogue.
+ This has still its merits for ease of debugging, or saving code size
+ or even execution time if the stack frame is so large that some accesses
+ can't be encoded anymore with offsets in the instruction code when using
+ a different scheme.
+ Also, it would be a good starting point if we got instructions to help
+ with register save/restore.
+
+ However, often stack frames are small, and the pushing / popping has
+ some costs:
+ - the stack modification prevents a lot of scheduling.
+ - frame allocation / deallocation may need extra instructions.
+ - we need to place a memory barrier after frame allocation to avoid
+ the delay slot scheduler to reschedule a frame related info and
+ messing up with dwarf unwinding. The barrier before deallocation
+ is for flushing all pending sp operations.
+
+ Thus, for small frames, we'd like to use a different scheme:
+ - The frame is allocated in full with the first prologue instruction,
+ and deallocated in full with the last epilogue instruction.
+ Thus, the instructions in-between can be freely scheduled.
+ - If the function has no outgoing arguments on the stack, we can allocate
+ one register save slot at the top of the stack. This register can then
+ be saved simultaneously with frame allocation, and restored with
+ frame deallocation.
+ This register can be picked depending on scheduling considerations,
+ although same though should go into having some set of registers
+ to be potentially lingering after a call, and others to be available
+ immediately - i.e. in the absence of interprocedual optimization, we
+ can use an ABI-like convention for register allocation to reduce
+ stalls after function return. */
+
+/* ARCompact stack frames look like:
+
+ Before call After call
+ high +-----------------------+ +-----------------------+
+ mem | reg parm save area | | reg parm save area |
+ | only created for | | only created for |
+ | variable arg fns | | variable arg fns |
+ AP +-----------------------+ +-----------------------+
+ | return addr register | | return addr register |
+ | (if required) | | (if required) |
+ +-----------------------+ +-----------------------+
+ | | | |
+ | reg save area | | reg save area |
+ | | | |
+ +-----------------------+ +-----------------------+
+ | frame pointer | | frame pointer |
+ | (if required) | | (if required) |
+ FP +-----------------------+ +-----------------------+
+ | | | |
+ | local/temp variables | | local/temp variables |
+ | | | |
+ +-----------------------+ +-----------------------+
+ | | | |
+ | arguments on stack | | arguments on stack |
+ | | | |
+ SP +-----------------------+ +-----------------------+
+ | reg parm save area |
+ | only created for |
+ | variable arg fns |
+ AP +-----------------------+
+ | return addr register |
+ | (if required) |
+ +-----------------------+
+ | |
+ | reg save area |
+ | |
+ +-----------------------+
+ | frame pointer |
+ | (if required) |
+ FP +-----------------------+
+ | |
+ | local/temp variables |
+ | |
+ +-----------------------+
+ | |
+ | arguments on stack |
+ low | |
+ mem SP +-----------------------+
+
+Notes:
+1) The "reg parm save area" does not exist for non variable argument fns.
+ The "reg parm save area" can be eliminated completely if we created our
+ own va-arc.h, but that has tradeoffs as well (so it's not done). */
+
+/* Structure to be filled in by arc_compute_frame_size with register
+ save masks, and offsets for the current function. */
+struct GTY (()) arc_frame_info
+{
+ unsigned int total_size; /* # bytes that the entire frame takes up. */
+ unsigned int extra_size; /* # bytes of extra stuff. */
+ unsigned int pretend_size; /* # bytes we push and pretend caller did. */
+ unsigned int args_size; /* # bytes that outgoing arguments take up. */
+ unsigned int reg_size; /* # bytes needed to store regs. */
+ unsigned int var_size; /* # bytes that variables take up. */
+ uint64_t gmask; /* Mask of saved gp registers. */
+ bool initialized; /* FALSE if frame size already calculated. */
+ short millicode_start_reg;
+ short millicode_end_reg;
+ bool save_return_addr;
+};
+
+/* GMASK bit length -1. */
+#define GMASK_LEN 63
+
+/* Defining data structures for per-function information. */
+
+typedef struct GTY (()) machine_function
+{
+ unsigned int fn_type;
+ struct arc_frame_info frame_info;
+ /* To keep track of unalignment caused by short insns. */
+ int unalign;
+ struct arc_ccfsm ccfsm_current;
+ /* Map from uid to ccfsm state during branch shortening. */
+ rtx ccfsm_current_insn;
+ char arc_reorg_started;
+ char prescan_initialized;
+} machine_function;
+
+
/* Given a symbol RTX (const (symb <+ const_int>), returns its
alignment. */
return NULL_TREE;
}
+/* Type of function DECL.
+
+ The result is cached. To reset the cache at the end of a function,
+ call with DECL = NULL_TREE. */
+
+static unsigned int
+arc_compute_function_type (struct function *fun)
+{
+ tree attr, decl = fun->decl;
+ unsigned int fn_type = fun->machine->fn_type;
+
+ if (fn_type != ARC_FUNCTION_UNKNOWN)
+ return fn_type;
+
+ /* Check if it is a naked function. */
+ if (lookup_attribute ("naked", DECL_ATTRIBUTES (decl)) != NULL_TREE)
+ fn_type |= ARC_FUNCTION_NAKED;
+ else
+ fn_type |= ARC_FUNCTION_NORMAL;
+
+ /* Now see if this is an interrupt handler. */
+ attr = lookup_attribute ("interrupt", DECL_ATTRIBUTES (decl));
+ if (attr != NULL_TREE)
+ {
+ tree value, args = TREE_VALUE (attr);
+
+ gcc_assert (list_length (args) == 1);
+ value = TREE_VALUE (args);
+ gcc_assert (TREE_CODE (value) == STRING_CST);
+
+ if (!strcmp (TREE_STRING_POINTER (value), "ilink1")
+ || !strcmp (TREE_STRING_POINTER (value), "ilink"))
+ fn_type |= ARC_FUNCTION_ILINK1;
+ else if (!strcmp (TREE_STRING_POINTER (value), "ilink2"))
+ fn_type |= ARC_FUNCTION_ILINK2;
+ else if (!strcmp (TREE_STRING_POINTER (value), "firq"))
+ fn_type |= ARC_FUNCTION_FIRQ;
+ else
+ gcc_unreachable ();
+ }
+
+ return fun->machine->fn_type = fn_type;
+}
+
/* Implement `TARGET_ALLOCATE_STACK_SLOTS_FOR_ARGS' */
static bool
return frame_add (stack_pointer_rtx, offset);
}
-/* Traditionally, we push saved registers first in the prologue,
- then we allocate the rest of the frame - and reverse in the epilogue.
- This has still its merits for ease of debugging, or saving code size
- or even execution time if the stack frame is so large that some accesses
- can't be encoded anymore with offsets in the instruction code when using
- a different scheme.
- Also, it would be a good starting point if we got instructions to help
- with register save/restore.
-
- However, often stack frames are small, and the pushing / popping has
- some costs:
- - the stack modification prevents a lot of scheduling.
- - frame allocation / deallocation needs extra instructions.
- - unless we know that we compile ARC700 user code, we need to put
- a memory barrier after frame allocation / before deallocation to
- prevent interrupts clobbering our data in the frame.
- In particular, we don't have any such guarantees for library functions,
- which tend to, on the other hand, to have small frames.
-
- Thus, for small frames, we'd like to use a different scheme:
- - The frame is allocated in full with the first prologue instruction,
- and deallocated in full with the last epilogue instruction.
- Thus, the instructions in-betwen can be freely scheduled.
- - If the function has no outgoing arguments on the stack, we can allocate
- one register save slot at the top of the stack. This register can then
- be saved simultanously with frame allocation, and restored with
- frame deallocation.
- This register can be picked depending on scheduling considerations,
- although same though should go into having some set of registers
- to be potentially lingering after a call, and others to be available
- immediately - i.e. in the absence of interprocedual optimization, we
- can use an ABI-like convention for register allocation to reduce
- stalls after function return. */
-/* Function prologue/epilogue handlers. */
-
-/* ARCompact stack frames look like:
-
- Before call After call
- high +-----------------------+ +-----------------------+
- mem | reg parm save area | | reg parm save area |
- | only created for | | only created for |
- | variable arg fns | | variable arg fns |
- AP +-----------------------+ +-----------------------+
- | return addr register | | return addr register |
- | (if required) | | (if required) |
- +-----------------------+ +-----------------------+
- | | | |
- | reg save area | | reg save area |
- | | | |
- +-----------------------+ +-----------------------+
- | frame pointer | | frame pointer |
- | (if required) | | (if required) |
- FP +-----------------------+ +-----------------------+
- | | | |
- | local/temp variables | | local/temp variables |
- | | | |
- +-----------------------+ +-----------------------+
- | | | |
- | arguments on stack | | arguments on stack |
- | | | |
- SP +-----------------------+ +-----------------------+
- | reg parm save area |
- | only created for |
- | variable arg fns |
- AP +-----------------------+
- | return addr register |
- | (if required) |
- +-----------------------+
- | |
- | reg save area |
- | |
- +-----------------------+
- | frame pointer |
- | (if required) |
- FP +-----------------------+
- | |
- | local/temp variables |
- | |
- +-----------------------+
- | |
- | arguments on stack |
- low | |
- mem SP +-----------------------+
-
-Notes:
-1) The "reg parm save area" does not exist for non variable argument fns.
- The "reg parm save area" can be eliminated completely if we created our
- own va-arc.h, but that has tradeoffs as well (so it's not done). */
-
-/* Structure to be filled in by arc_compute_frame_size with register
- save masks, and offsets for the current function. */
-struct GTY (()) arc_frame_info
-{
- unsigned int total_size; /* # bytes that the entire frame takes up. */
- unsigned int extra_size; /* # bytes of extra stuff. */
- unsigned int pretend_size; /* # bytes we push and pretend caller did. */
- unsigned int args_size; /* # bytes that outgoing arguments take up. */
- unsigned int reg_size; /* # bytes needed to store regs. */
- unsigned int var_size; /* # bytes that variables take up. */
- unsigned int gmask; /* Mask of saved gp registers. */
- bool initialized; /* FALSE if frame size already calculated. */
- short millicode_start_reg;
- short millicode_end_reg;
- bool save_return_addr;
-};
-
-/* GMASK bit length -1. */
-#define GMASK_LEN 31
-
-/* Defining data structures for per-function information. */
-
-typedef struct GTY (()) machine_function
-{
- unsigned int fn_type;
- struct arc_frame_info frame_info;
- /* To keep track of unalignment caused by short insns. */
- int unalign;
- struct arc_ccfsm ccfsm_current;
- /* Map from uid to ccfsm state during branch shortening. */
- rtx ccfsm_current_insn;
- char arc_reorg_started;
- char prescan_initialized;
-} machine_function;
-
-/* Type of function DECL.
-
- The result is cached. To reset the cache at the end of a function,
- call with DECL = NULL_TREE. */
-
-unsigned int
-arc_compute_function_type (struct function *fun)
-{
- tree attr, decl = fun->decl;
- unsigned int fn_type = fun->machine->fn_type;
-
- if (fn_type != ARC_FUNCTION_UNKNOWN)
- return fn_type;
-
- /* Check if it is a naked function. */
- if (lookup_attribute ("naked", DECL_ATTRIBUTES (decl)) != NULL_TREE)
- fn_type |= ARC_FUNCTION_NAKED;
- else
- fn_type |= ARC_FUNCTION_NORMAL;
-
- /* Now see if this is an interrupt handler. */
- attr = lookup_attribute ("interrupt", DECL_ATTRIBUTES (decl));
- if (attr != NULL_TREE)
- {
- tree value, args = TREE_VALUE (attr);
-
- gcc_assert (list_length (args) == 1);
- value = TREE_VALUE (args);
- gcc_assert (TREE_CODE (value) == STRING_CST);
-
- if (!strcmp (TREE_STRING_POINTER (value), "ilink1")
- || !strcmp (TREE_STRING_POINTER (value), "ilink"))
- fn_type |= ARC_FUNCTION_ILINK1;
- else if (!strcmp (TREE_STRING_POINTER (value), "ilink2"))
- fn_type |= ARC_FUNCTION_ILINK2;
- else if (!strcmp (TREE_STRING_POINTER (value), "firq"))
- fn_type |= ARC_FUNCTION_FIRQ;
- else
- gcc_unreachable ();
- }
-
- return fun->machine->fn_type = fn_type;
-}
-
/* 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
}
/* Tell prologue and epilogue if register REGNO should be saved /
- restored. The return address, stack pointer and frame pointer are
- treated separately. Don't consider them here. Addition for pic:
- The gp register needs to be saved if the current function changes
- it to access gotoff variables. FIXME: This will not be needed if
- we used some arbitrary register instead of r26. */
+ restored. The SPECIAL_P is true when the register may need special
+ ld/st sequence. The return address, and stack pointer are treated
+ separately. Don't consider them here. */
static bool
-arc_must_save_register (int regno, struct function *func)
+arc_must_save_register (int regno, struct function *func, bool special_p)
{
unsigned int fn_type = arc_compute_function_type (func);
bool irq_auto_save_p = ((irq_ctrl_saved.irq_save_last_reg >= regno)
switch (regno)
{
+ case ILINK1_REG:
case RETURN_ADDR_REGNUM:
case STACK_POINTER_REGNUM:
+ /* The stack pointer and the return address are handled
+ separately. */
+ return false;
+
+ case R30_REG:
+ /* r30 is either used as ilink2 by ARCv1 or as a free register
+ by ARCv2. */
+ if (!TARGET_V2)
+ return false;
+ break;
+
+ case R40_REG:
+ case R41_REG:
+ case R42_REG:
+ case R43_REG:
+ case R44_REG:
+ /* If those ones are used by the FPX machinery, we handle them
+ separately. */
+ if (TARGET_DPFP && !special_p)
+ return false;
+ /* FALLTHRU. */
+
+ case R32_REG:
+ case R33_REG:
+ case R34_REG:
+ case R35_REG:
+ case R36_REG:
+ case R37_REG:
+ case R38_REG:
+ case R39_REG:
+ case R45_REG:
+ case R46_REG:
+ case R47_REG:
+ case R48_REG:
+ case R49_REG:
+ case R50_REG:
+ case R51_REG:
+ case R52_REG:
+ case R53_REG:
+ case R54_REG:
+ case R55_REG:
+ case R56_REG:
+ case R57_REG:
+ case R58_REG:
+ case R59_REG:
+ /* The Extension Registers. */
+ if (ARC_INTERRUPT_P (fn_type)
+ && (df_regs_ever_live_p (RETURN_ADDR_REGNUM)
+ || df_regs_ever_live_p (regno))
+ /* Not all extension registers are available, choose the
+ real ones. */
+ && !fixed_regs[regno])
+ return true;
+ return false;
+
+ case 61:
+ case 62:
+ case 63:
+ /* Fixed/control register, nothing to do. LP_COUNT is
+ different. */
return false;
case HARD_FRAME_POINTER_REGNUM:
regular reg. */
if (arc_frame_pointer_needed ())
return false;
+ break;
- /* FALLTHRU */
default:
- if (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
- && !firq_auto_save_p)
- return true;
+ break;
}
+ if (((df_regs_ever_live_p (regno) && !call_used_regs[regno])
+ /* In an interrupt save everything. */
+ || (ARC_INTERRUPT_P (fn_type)
+ && (df_regs_ever_live_p (RETURN_ADDR_REGNUM)
+ || df_regs_ever_live_p (regno))))
+ /* Do not emit code for auto saved regs. */
+ && !irq_auto_save_p
+ && !firq_auto_save_p)
+ return true;
return false;
}
of registers to be saved / restored with a millicode call. */
static int
-arc_compute_millicode_save_restore_regs (unsigned int gmask,
+arc_compute_millicode_save_restore_regs (uint64_t gmask,
struct arc_frame_info *frame)
{
int regno;
int start_reg = 13, end_reg = 25;
- for (regno = start_reg; regno <= end_reg && (gmask & (1L << regno));)
+ for (regno = start_reg; regno <= end_reg && (gmask & (1ULL << regno));)
regno++;
end_reg = regno - 1;
/* There is no point in using millicode thunks if we don't save/restore
int regno;
unsigned int total_size, var_size, args_size, pretend_size, extra_size;
unsigned int reg_size;
- unsigned int gmask;
+ uint64_t gmask;
struct arc_frame_info *frame_info;
int size;
unsigned int extra_plus_reg_size;
reg_size = 0;
gmask = 0;
- for (regno = 0; regno <= 31; regno++)
+ /* The last 4 regs are special, avoid them. */
+ for (regno = 0; regno <= (GMASK_LEN - 4); regno++)
{
- if (arc_must_save_register (regno, cfun))
+ if (arc_must_save_register (regno, cfun, false))
{
reg_size += UNITS_PER_WORD;
- gmask |= 1L << regno;
+ gmask |= 1ULL << regno;
}
}
for (regno = 0; EH_RETURN_DATA_REGNO (regno) != INVALID_REGNUM; regno++)
{
reg_size += UNITS_PER_WORD;
- gmask |= 1L << regno;
+ gmask |= 1ULL << regno;
}
/* Check if we need to save the return address. */
/* Saving blink reg for millicode thunk calls. */
if (TARGET_MILLICODE_THUNK_SET
- && !crtl->calls_eh_return)
+ && !crtl->calls_eh_return
+ && !ARC_INTERRUPT_P (arc_compute_function_type (cfun)))
{
if (arc_compute_millicode_save_restore_regs (gmask, frame_info))
frame_info->save_return_addr = true;
}
+ /* Save lp_count, lp_start and lp_end. */
+ if (arc_lpcwidth != 0 && arc_must_save_register (LP_COUNT, cfun, true))
+ reg_size += UNITS_PER_WORD * 3;
+
+ /* Check for the special R40-R44 regs used by FPX extension. */
+ if (arc_must_save_register (TARGET_BIG_ENDIAN ? R41_REG : R40_REG,
+ cfun, TARGET_DPFP))
+ reg_size += UNITS_PER_WORD * 2;
+ if (arc_must_save_register (TARGET_BIG_ENDIAN ? R43_REG : R42_REG,
+ cfun, TARGET_DPFP))
+ reg_size += UNITS_PER_WORD * 2;
+
/* 4) Calculate extra size made up of the blink + fp size. */
extra_size = 0;
if (arc_must_save_return_addr (cfun))
return GET_MODE_SIZE (GET_MODE (reg)) - offset;
}
+/* Helper used when saving AUX regs during ISR. */
+
+static int
+push_reg (rtx reg)
+{
+ rtx stkslot = gen_rtx_MEM (GET_MODE (reg), gen_rtx_PRE_DEC (Pmode,
+ stack_pointer_rtx));
+ rtx insn = emit_move_insn (stkslot, reg);
+ RTX_FRAME_RELATED_P (insn) = 1;
+ add_reg_note (insn, REG_CFA_ADJUST_CFA,
+ gen_rtx_SET (stack_pointer_rtx,
+ plus_constant (Pmode, stack_pointer_rtx,
+ -GET_MODE_SIZE (GET_MODE (reg)))));
+ return GET_MODE_SIZE (GET_MODE (reg));
+}
+
/* Helper for epilogue: emit frame load with post_modify or post_inc
to restore register REG from stack. The initial offset is passed
via OFFSET. */
return GET_MODE_SIZE (GET_MODE (reg)) + offset;
}
+/* Helper used when restoring AUX regs during ISR. */
+
+static int
+pop_reg (rtx reg)
+{
+ rtx stkslot = gen_rtx_MEM (GET_MODE (reg), gen_rtx_POST_INC (Pmode,
+ stack_pointer_rtx));
+ rtx insn = emit_move_insn (reg, stkslot);
+ RTX_FRAME_RELATED_P (insn) = 1;
+ add_reg_note (insn, REG_CFA_ADJUST_CFA,
+ gen_rtx_SET (stack_pointer_rtx,
+ plus_constant (Pmode, stack_pointer_rtx,
+ GET_MODE_SIZE (GET_MODE (reg)))));
+ return GET_MODE_SIZE (GET_MODE (reg));
+}
+
+
/* Check if we have a continous range to be save/restored with the
help of enter/leave instructions. A vaild register range starts
from $r13 and is up to (including) $r26. */
static bool
-arc_enter_leave_p (unsigned int gmask)
+arc_enter_leave_p (uint64_t gmask)
{
int regno;
unsigned int rmask = 0;
return false;
for (regno = ENTER_LEAVE_START_REG;
- regno <= ENTER_LEAVE_END_REG && (gmask & (1L << regno)); regno++)
- rmask |= 1L << regno;
+ regno <= ENTER_LEAVE_END_REG && (gmask & (1ULL << regno)); regno++)
+ rmask |= 1ULL << regno;
if (rmask ^ gmask)
return false;
instructions. */
static int
-arc_save_callee_saves (unsigned int gmask,
+arc_save_callee_saves (uint64_t gmask,
bool save_blink,
bool save_fp,
HOST_WIDE_INT offset)
{
rtx reg;
int frame_allocated = 0;
+ int i;
/* The home-grown ABI says link register is saved first. */
if (save_blink)
/* N.B. FRAME_POINTER_MASK and RETURN_ADDR_MASK are cleared in gmask. */
if (gmask)
- for (int i = 31; i >= 0; i--)
+ for (i = GMASK_LEN; i >= 0; i--)
{
machine_mode save_mode = SImode;
if (TARGET_LL64
&& ((i - 1) % 2 == 0)
- && ((gmask & (1L << i)) != 0)
- && ((gmask & (1L << (i - 1))) != 0))
+ && ((gmask & (1ULL << i)) != 0)
+ && ((gmask & (1ULL << (i - 1))) != 0))
{
save_mode = DImode;
--i;
}
- else if ((gmask & (1L << i)) == 0)
+ else if ((gmask & (1ULL << i)) == 0)
continue;
reg = gen_rtx_REG (save_mode, i);
offset = 0;
}
+ /* Check if we need to save the ZOL machinery. */
+ if (arc_lpcwidth != 0 && arc_must_save_register (LP_COUNT, cfun, true))
+ {
+ rtx reg0 = gen_rtx_REG (SImode, R0_REG);
+ emit_insn (gen_rtx_SET (reg0,
+ gen_rtx_UNSPEC_VOLATILE
+ (Pmode, gen_rtvec (1, GEN_INT (AUX_LP_START)),
+ VUNSPEC_ARC_LR)));
+ frame_allocated += push_reg (reg0);
+ emit_insn (gen_rtx_SET (reg0,
+ gen_rtx_UNSPEC_VOLATILE
+ (Pmode, gen_rtvec (1, GEN_INT (AUX_LP_END)),
+ VUNSPEC_ARC_LR)));
+ frame_allocated += push_reg (reg0);
+ emit_move_insn (reg0, gen_rtx_REG (SImode, LP_COUNT));
+ frame_allocated += push_reg (reg0);
+ }
+
+ /* Save AUX regs used by FPX machinery. */
+ if (arc_must_save_register (TARGET_BIG_ENDIAN ? R41_REG : R40_REG,
+ cfun, TARGET_DPFP))
+ {
+ rtx reg0 = gen_rtx_REG (SImode, R0_REG);
+
+ for (i = 0; i < 4; i++)
+ {
+ emit_insn (gen_rtx_SET (reg0,
+ gen_rtx_UNSPEC_VOLATILE
+ (Pmode, gen_rtvec (1, GEN_INT (AUX_DPFP_START
+ + i)),
+ VUNSPEC_ARC_LR)));
+ frame_allocated += push_reg (reg0);
+ }
+ }
+
/* Save frame pointer if needed. First save the FP on stack, if not
autosaved. Unfortunately, I cannot add it to gmask and use the
above loop to save fp because our ABI states fp goes aftert all
if it is for an interrupt handler) using LD/LDD instructions. */
static int
-arc_restore_callee_saves (unsigned int gmask,
+arc_restore_callee_saves (uint64_t gmask,
bool restore_blink,
bool restore_fp,
HOST_WIDE_INT offset,
int frame_deallocated = 0;
HOST_WIDE_INT offs = cfun->machine->frame_info.reg_size;
bool early_blink_restore;
+ int i;
/* Emit mov fp,sp. */
if (arc_frame_pointer_needed () && offset)
frame_deallocated += frame_restore_reg (hard_frame_pointer_rtx, 0);
}
+ /* Restore AUX-regs used by FPX machinery. */
+ if (arc_must_save_register (TARGET_BIG_ENDIAN ? R41_REG : R40_REG,
+ cfun, TARGET_DPFP))
+ {
+ rtx reg0 = gen_rtx_REG (SImode, R0_REG);
+
+ gcc_assert (offset == 0);
+ for (i = 0; i < 4; i++)
+ {
+ frame_deallocated += pop_reg (reg0);
+ emit_insn (gen_rtx_UNSPEC_VOLATILE
+ (VOIDmode, gen_rtvec (2, reg0, GEN_INT (AUX_DPFP_START
+ + i)),
+ VUNSPEC_ARC_SR));
+ }
+ }
+
+ /* Check if we need to restore the ZOL machinery. */
+ if (arc_lpcwidth !=0 && arc_must_save_register (LP_COUNT, cfun, true))
+ {
+ rtx reg0 = gen_rtx_REG (SImode, R0_REG);
+
+ gcc_assert (offset == 0);
+ frame_deallocated += pop_reg (reg0);
+ emit_move_insn (gen_rtx_REG (SImode, LP_COUNT), reg0);
+
+ frame_deallocated += pop_reg (reg0);
+ emit_insn (gen_rtx_UNSPEC_VOLATILE
+ (VOIDmode, gen_rtvec (2, reg0, GEN_INT (AUX_LP_END)),
+ VUNSPEC_ARC_SR));
+
+ frame_deallocated += pop_reg (reg0);
+ emit_insn (gen_rtx_UNSPEC_VOLATILE
+ (VOIDmode, gen_rtvec (2, reg0, GEN_INT (AUX_LP_START)),
+ VUNSPEC_ARC_SR));
+ }
+
if (offset)
{
/* No $fp involved, we need to do an add to set the $sp to the
}
/* When we do not optimize for size, restore first blink. */
- early_blink_restore = restore_blink && !optimize_size && offs;
+ early_blink_restore = restore_blink && !optimize_size && offs
+ && !ARC_INTERRUPT_P (arc_compute_function_type (cfun));
if (early_blink_restore)
{
rtx addr = plus_constant (Pmode, stack_pointer_rtx, offs);
/* N.B. FRAME_POINTER_MASK and RETURN_ADDR_MASK are cleared in gmask. */
if (gmask)
- for (int i = 0; i <= GMASK_LEN; i++)
+ for (i = 0; i <= GMASK_LEN; i++)
{
machine_mode restore_mode = SImode;
if (TARGET_LL64
&& ((i % 2) == 0)
- && ((gmask & (1L << i)) != 0)
- && ((gmask & (1L << (i + 1))) != 0))
+ && ((gmask & (1ULL << i)) != 0)
+ && ((gmask & (1ULL << (i + 1))) != 0))
restore_mode = DImode;
- else if ((gmask & (1L << i)) == 0)
+ else if ((gmask & (1ULL << i)) == 0)
continue;
reg = gen_rtx_REG (restore_mode, i);
switch (restore_mode)
{
case E_DImode:
- if ((GMASK_LEN - __builtin_clz (gmask)) == (i + 1)
+ if ((GMASK_LEN - __builtin_clzll (gmask)) == (i + 1)
&& early_blink_restore)
offs = 4;
break;
case E_SImode:
- if ((GMASK_LEN - __builtin_clz (gmask)) == i
+ if ((GMASK_LEN - __builtin_clzll (gmask)) == i
&& early_blink_restore)
offs = 4;
break;
register. */
static int
-arc_save_callee_enter (unsigned int gmask,
+arc_save_callee_enter (uint64_t gmask,
bool save_blink,
bool save_fp,
HOST_WIDE_INT offset)
rtx insn, reg, mem;
int frame_allocated = 0;
- for (regno = start_reg; regno <= end_reg && (gmask & (1L << regno));)
+ for (regno = start_reg; regno <= end_reg && (gmask & (1ULL << regno));)
regno++;
end_reg = regno - 1;
off));
XVECEXP (insn, 0, indx) = gen_rtx_SET (mem, reg);
RTX_FRAME_RELATED_P (XVECEXP (insn, 0, indx)) = 1;
- gmask = gmask & ~(1L << regno);
+ gmask = gmask & ~(1ULL << regno);
}
if (save_fp)
(RESTORE_FP), and can automatic return (RETURN_P). */
static int
-arc_restore_callee_leave (unsigned int gmask,
+arc_restore_callee_leave (uint64_t gmask,
bool restore_blink,
bool restore_fp,
bool return_p,
rtx insn, reg, mem;
int frame_allocated = 0;
- for (regno = start_reg; regno <= end_reg && (gmask & (1L << regno));)
+ for (regno = start_reg; regno <= end_reg && (gmask & (1ULL << regno));)
regno++;
end_reg = regno - 1;
off));
XVECEXP (insn, 0, indx) = gen_rtx_SET (reg, mem);
RTX_FRAME_RELATED_P (XVECEXP (insn, 0, indx)) = 1;
- gmask = gmask & ~(1L << regno);
+ gmask = gmask & ~(1ULL << regno);
}
if (restore_fp)
*/
static int
-arc_save_callee_milli (unsigned int gmask,
+arc_save_callee_milli (uint64_t gmask,
bool save_blink,
bool save_fp,
HOST_WIDE_INT offset,
rtx insn, reg, mem;
int frame_allocated = 0;
- for (regno = start_reg; regno <= end_reg && (gmask & (1L << regno));)
+ for (regno = start_reg; regno <= end_reg && (gmask & (1ULL << regno));)
regno++;
end_reg = regno - 1;
off));
XVECEXP (insn, 0, indx) = gen_rtx_SET (mem, reg);
RTX_FRAME_RELATED_P (XVECEXP (insn, 0, indx)) = 1;
- gmask = gmask & ~(1L << regno);
+ gmask = gmask & ~(1ULL << regno);
}
insn = frame_insn (insn);
}
/* Save remaining registers using st instructions. */
- for (regno = 0; regno <= 31; regno++)
+ for (regno = 0; regno <= GMASK_LEN; regno++)
{
- if ((gmask & (1L << regno)) == 0)
+ if ((gmask & (1ULL << regno)) == 0)
continue;
reg = gen_rtx_REG (SImode, regno);
/* Like the previous function but restore. */
static int
-arc_restore_callee_milli (unsigned int gmask,
+arc_restore_callee_milli (uint64_t gmask,
bool restore_blink,
bool restore_fp,
bool return_p,
rtx insn, reg, mem;
int frame_allocated = 0;
- for (regno = start_reg; regno <= end_reg && (gmask & (1L << regno));)
+ for (regno = start_reg; regno <= end_reg && (gmask & (1ULL << regno));)
regno++;
end_reg = regno - 1;
off));
XVECEXP (insn, 0, indx) = gen_rtx_SET (reg, mem);
RTX_FRAME_RELATED_P (XVECEXP (insn, 0, indx)) = 1;
- gmask = gmask & ~(1L << regno);
+ gmask = gmask & ~(1ULL << regno);
}
/* Restore remaining registers using LD instructions. */
- for (regno = 0; regno <= 31; regno++)
+ for (regno = 0; regno <= GMASK_LEN; regno++)
{
- if ((gmask & (1L << regno)) == 0)
+ if ((gmask & (1ULL << regno)) == 0)
continue;
reg = gen_rtx_REG (SImode, regno);
arc_expand_prologue (void)
{
int size;
- unsigned int gmask = cfun->machine->frame_info.gmask;
+ uint64_t gmask = cfun->machine->frame_info.gmask;
struct arc_frame_info *frame = &cfun->machine->frame_info;
unsigned int frame_size_to_allocate;
int first_offset = 0;
emit_insn (gen_blockage ());
}
+/* Return the register number of the register holding the return address
+ for a function of type TYPE. */
+
+static int
+arc_return_address_register (unsigned int fn_type)
+{
+ int regno = 0;
+
+ if (ARC_INTERRUPT_P (fn_type))
+ {
+ if ((fn_type & (ARC_FUNCTION_ILINK1 | ARC_FUNCTION_FIRQ)) != 0)
+ regno = ILINK1_REG;
+ else if ((fn_type & ARC_FUNCTION_ILINK2) != 0)
+ regno = ILINK2_REG;
+ else
+ gcc_unreachable ();
+ }
+ else if (ARC_NORMAL_P (fn_type) || ARC_NAKED_P (fn_type))
+ regno = RETURN_ADDR_REGNUM;
+
+ gcc_assert (regno != 0);
+ return regno;
+}
+
/* Do any necessary cleanup after a function to restore stack, frame,
and regs. */
bool restore_fp = arc_frame_pointer_needed () && !ARC_AUTOFP_IRQ_P (fn_type);
bool restore_blink = arc_must_save_return_addr (cfun)
&& !ARC_AUTOBLINK_IRQ_P (fn_type);
- unsigned int gmask = cfun->machine->frame_info.gmask;
+ uint64_t gmask = cfun->machine->frame_info.gmask;
bool return_p = !sibcall_p && fn_type == ARC_FUNCTION_NORMAL
&& !cfun->machine->frame_info.pretend_size;
struct arc_frame_info *frame = &cfun->machine->frame_info;
-
/* Naked functions don't have epilogue. */
if (ARC_NAKED_P (fn_type))
return;
EH_RETURN_STACKADJ_RTX));
/* Emit the return instruction. */
- if (sibcall_p == FALSE)
+ if (ARC_INTERRUPT_P (fn_type))
+ {
+ rtx ra = gen_rtx_REG (Pmode, arc_return_address_register (fn_type));
+
+ if (TARGET_V2)
+ emit_jump_insn (gen_rtie ());
+ else if (TARGET_ARC700)
+ emit_jump_insn (gen_rtie ());
+ else
+ emit_jump_insn (gen_arc600_rtie (ra));
+ }
+ else if (sibcall_p == FALSE)
emit_jump_insn (gen_simple_return ());
}
/* Accessor functions for cfun->machine->unalign. */
-int
-arc_get_unalign (void)
-{
- return cfun->machine->unalign;
-}
-
void
arc_clear_unalign (void)
{
return true;
}
-/* Return the register number of the register holding the return address
- for a function of type TYPE. */
-
-int
-arc_return_address_register (unsigned int fn_type)
-{
- int regno = 0;
-
- if (ARC_INTERRUPT_P (fn_type))
- {
- if ((fn_type & (ARC_FUNCTION_ILINK1 | ARC_FUNCTION_FIRQ)) != 0)
- regno = ILINK1_REG;
- else if ((fn_type & ARC_FUNCTION_ILINK2) != 0)
- regno = ILINK2_REG;
- else
- gcc_unreachable ();
- }
- else if (ARC_NORMAL_P (fn_type) || ARC_NAKED_P (fn_type))
- regno = RETURN_ADDR_REGNUM;
-
- gcc_assert (regno != 0);
- return regno;
-}
/* Implement EPILOGUE_USES.
Return true if REGNO should be added to the deemed uses of the epilogue.
arc_epilogue_uses (int regno)
{
unsigned int fn_type;
+ fn_type = arc_compute_function_type (cfun);
if (regno == arc_tp_regno)
return true;
- fn_type = arc_compute_function_type (cfun);
- if (reload_completed)
+ if (regno == RETURN_ADDR_REGNUM)
+ return true;
+
+ if (regno == arc_return_address_register (fn_type))
+ return true;
+
+ if (epilogue_completed && ARC_INTERRUPT_P (fn_type))
{
- if (ARC_INTERRUPT_P (cfun->machine->fn_type))
- {
- if (!fixed_regs[regno])
- return true;
- return ((regno == arc_return_address_register (fn_type))
- || (regno == RETURN_ADDR_REGNUM));
- }
- else
- return regno == RETURN_ADDR_REGNUM;
+ /* An interrupt function restores more registers. */
+ if (df_regs_ever_live_p (regno) || call_used_regs[regno])
+ return true;
}
- else
- return regno == arc_return_address_register (fn_type);
+
+ return false;
}
/* Helper for EH_USES macro. */
return false;
}
+/* Return nonzero if this function is known to have a null epilogue.
+ This allows the optimizer to omit jumps to jumps if no stack
+ was created. */
+
+bool
+arc_can_use_return_insn (void)
+{
+ return (reload_completed && cfun->machine->frame_info.total_size == 0
+ && !ARC_INTERRUPT_P (arc_compute_function_type (cfun)));
+}
#undef TARGET_USE_ANCHORS_FOR_SYMBOL_P
#define TARGET_USE_ANCHORS_FOR_SYMBOL_P arc_use_anchors_for_symbol_p
VUNSPEC_ARC_LL
VUNSPEC_ARC_BLOCKAGE
VUNSPEC_ARC_EH_RETURN
+ VUNSPEC_ARC_ARC600_RTIE
])
(define_constants
(R30_REG 30)
(RETURN_ADDR_REGNUM 31)
(R32_REG 32)
+ (R33_REG 33)
+ (R34_REG 34)
+ (R35_REG 35)
+ (R36_REG 36)
+ (R37_REG 37)
+ (R38_REG 38)
+ (R39_REG 39)
(R40_REG 40)
(R41_REG 41)
(R42_REG 42)
(R43_REG 43)
(R44_REG 44)
+ (R45_REG 45)
+ (R46_REG 46)
+ (R47_REG 47)
+ (R48_REG 48)
+ (R49_REG 49)
+ (R50_REG 50)
+ (R51_REG 51)
+ (R52_REG 52)
+ (R53_REG 53)
+ (R54_REG 54)
+ (R55_REG 55)
+ (R56_REG 56)
(R57_REG 57)
+ (R58_REG 58)
+ (R59_REG 59)
+
(MUL64_OUT_REG 58)
(MUL32x16_REG 56)
(ARCV2_ACC 58)
- (R59_REG 59)
-
(LP_COUNT 60)
(CC_REG 61)
(PCL_REG 63)
(define_attr "type"
"move,load,store,cmove,unary,binary,compare,shift,uncond_branch,jump,branch,
- brcc,brcc_no_delay_slot,call,sfunc,call_no_delay_slot,
+ brcc,brcc_no_delay_slot,call,sfunc,call_no_delay_slot,rtie,
multi,umulti, two_cycle_core,lr,sr,divaw,loop_setup,loop_end,return,
misc,spfp,dpfp_mult,dpfp_addsub,mulmac_600,cc_arith,
simd_vload, simd_vload128, simd_vstore, simd_vmove, simd_vmove_else_zero,
(cond [(eq_attr "in_delay_slot" "false")
(const_string "no")
(match_test "regno_clobbered_p
- (arc_return_address_register
- (arc_compute_function_type (cfun)),
- insn, SImode, 1)")
+ (RETURN_ADDR_REGNUM, insn, SImode, 1)")
(const_string "no")]
(const_string "yes")))
; execution must reflect this, lest out-of-range branches are created.
; the iscompact attribute allows the epilogue expander to know for which
; insns it should lengthen the return insn.
-(define_insn_and_split "*movsi_insn" ; 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
- [(set (match_operand:SI 0 "move_dest_operand" "=q, q,r,q, h,rl, r, r, r, r, ?r, r, q, h, rl, q, S, Us<,RcqRck,!*x, r,!*Rsd,!*Rcd,r,Ucm, Usd,m, m,VUsc")
- (match_operand:SI 1 "move_src_operand" "rL,rP,q,P,hCm1,rL, I,Clo,Chi,Cbi,Cpc,Clb,Cax,Cal,Cal,Uts,Rcq,RcqRck, Us>,Usd,Ucm, Usd, Ucd,m, r,!*Rzd,r,Cm3, C32"))]
+(define_insn_and_split "*movsi_insn" ; 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
+ [(set (match_operand:SI 0 "move_dest_operand" "=q, q,r,q, h, rl,r, r, r, r, ?r, r, q, h, rl, q, S, Us<,RcqRck,!*x, r,!*Rsd,!*Rcd,r,Ucm, Usd,m, m,VUsc")
+ (match_operand:SI 1 "move_src_operand" "rL,rP,q,P,hCm1,rLl,I,Clo,Chi,Cbi,Cpc,Clb,Cax,Cal,Cal,Uts,Rcq,RcqRck, Us>,Usd,Ucm, Usd, Ucd,m, r,!*Rzd,r,Cm3, C32"))]
"register_operand (operands[0], SImode)
|| register_operand (operands[1], SImode)
|| (CONSTANT_P (operands[1])
mov%?\\t%0,%1 ;4
mov%?\\t%0,%1 ;5
mov%?\\t%0,%1 ;6
- movl.cl\\t %0,%1 ;7
- movh.cl\\t %0,%L1>>16 ;8
+ movl.cl\\t%0,%1 ;7
+ movh.cl\\t%0,%L1>>16 ;8
* return INTVAL (operands[1]) & 0xffffff ? \"movbi.cl\\t%0,%1 >> %p1,%p1,8;9\" : \"movbi.cl\\t%0,%L1 >> 24,24,8;9\";
add\\t%0,%1 ;10
add\\t%0,pcl,%1@pcl ;11
mov%?\\t%0,%j1 ;13
mov%?\\t%0,%j1 ;14
ld%?\\t%0,%1 ;15
- st%?\\t %1,%0 ;16
+ st%?\\t%1,%0 ;16
* return arc_short_long (insn, \"push%?\\t%1%&\", \"st%U0\\t%1,%0%&\");
* return arc_short_long (insn, \"pop%?\\t%0%&\", \"ld%U1\\t%0,%1%&\");
ld%?\\t%0,%1 ;19
(set_attr "type" "misc")])
(define_insn "rtie"
- [(unspec_volatile [(match_operand:SI 0 "immediate_operand" "N")]
- VUNSPEC_ARC_RTIE)]
- ""
+ [(return)
+ (unspec_volatile [(const_int 0)] VUNSPEC_ARC_RTIE)]
+ "!TARGET_ARC600_FAMILY"
"rtie"
[(set_attr "length" "4")
- (set_attr "type" "misc")
- (set_attr "cond" "clob")])
+ (set_attr "type" "rtie")
+ (set_attr "cond" "clob")])
(define_insn "sync"
[(unspec_volatile [(match_operand:SI 0 "immediate_operand" "N")]
; forbid instructions that change blink in the return / sibcall delay slot.
(define_insn "simple_return"
[(simple_return)]
- "reload_completed"
-{
- rtx reg
- = gen_rtx_REG (Pmode,
- arc_return_address_register (arc_compute_function_type
- (cfun)));
+ ""
+ "j%!%*\\t[blink]"
+ [(set_attr "type" "return")
+ (set_attr "cond" "canuse")
+ (set_attr "iscompact" "maybe")
+ (set_attr "length" "*")])
- if (TARGET_V2
- && ARC_INTERRUPT_P (arc_compute_function_type (cfun)))
- {
- return \"rtie\";
- }
- output_asm_insn (\"j%!%* [%0]%&\", ®);
- return \"\";
-}
- [(set (attr "type")
- (cond [(and (match_test "ARC_INTERRUPT_P (arc_compute_function_type (cfun))")
- (match_test "TARGET_V2"))
- (const_string "brcc_no_delay_slot")]
- (const_string "return")))
- ; predicable won't help here since the canonical rtl looks different
- ; for branches.
- (set (attr "cond")
- (cond [(and (eq (symbol_ref "arc_compute_function_type (cfun)")
- (symbol_ref "ARC_FUNCTION_ILINK1"))
- (match_test "TARGET_V2"))
- (const_string "nocond")]
- (const_string "canuse")))
- (set (attr "iscompact")
- (cond [(eq (symbol_ref "arc_compute_function_type (cfun)")
- (symbol_ref "ARC_FUNCTION_NORMAL"))
- (const_string "maybe")]
- (const_string "false")))
- (set (attr "length")
- (cond [(ne (symbol_ref "arc_compute_function_type (cfun)")
- (symbol_ref "ARC_FUNCTION_NORMAL"))
- (const_int 4)]
- (const_int 2)))])
+(define_insn "arc600_rtie"
+ [(return)
+ (unspec_volatile [(match_operand 0 "pmode_register_operand" "")]
+ VUNSPEC_ARC_ARC600_RTIE)]
+ "TARGET_ARC600_FAMILY"
+ "j.f\\t[%0]"
+ [(set_attr "length" "4")
+ (set_attr "type" "rtie")
+ (set_attr "cond" "clob")])
(define_insn "p_return_i"
[(set (pc)
(if_then_else (match_operator 0 "proper_comparison_operator"
[(reg CC_REG) (const_int 0)])
(simple_return) (pc)))]
- "reload_completed
- && !(TARGET_V2
- && ARC_INTERRUPT_P (arc_compute_function_type (cfun)))"
+ "reload_completed"
{
- rtx xop[2];
- xop[0] = operands[0];
- xop[1]
- = gen_rtx_REG (Pmode,
- arc_return_address_register (arc_compute_function_type
- (cfun)));
-
- output_asm_insn (\"j%d0%!%# [%1]%&\", xop);
+ output_asm_insn (\"j%d0%!%#\\t[blink]\", operands);
/* record the condition in case there is a delay insn. */
- arc_ccfsm_record_condition (xop[0], false, insn, 0);
+ arc_ccfsm_record_condition (operands[0], false, insn, 0);
return \"\";
}
[(set_attr "type" "return")
(set_attr "cond" "use")
- (set (attr "iscompact")
- (cond [(eq (symbol_ref "arc_compute_function_type (cfun)")
- (symbol_ref "ARC_FUNCTION_NORMAL"))
- (const_string "maybe")]
- (const_string "false")))
+ (set_attr "iscompact" "maybe" )
(set (attr "length")
- (cond [(ne (symbol_ref "arc_compute_function_type (cfun)")
- (symbol_ref "ARC_FUNCTION_NORMAL"))
- (const_int 4)
- (not (match_operand 0 "equality_comparison_operator" ""))
+ (cond [(not (match_operand 0 "equality_comparison_operator" ""))
(const_int 4)
(eq_attr "delay_slot_filled" "yes")
(const_int 4)]
(const_int 2)))])
-;; ??? #ifdefs in function.c require the presence of this pattern, with a
-;; non-constant predicate.
+;; Return nonzero if this function is known to have a null epilogue.
+;; This allows the optimizer to omit jumps to jumps if no stack
+;; was created.
(define_expand "return"
[(return)]
- "optimize < 0")
+ "arc_can_use_return_insn ()"
+ "")
;; Comment in final.c (insn_current_reference_address) says
;; forward branch addresses are calculated from the next insn after branch