/* FPX AUX registers. */
#define AUX_DPFP_START 0x301
+/* ARC600 MULHI register. */
+#define AUX_MULHI 0x12
+
/* 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
this way, we don't have to carry clobbers of that reg around in every
isntruction that modifies mlo and/or mhi. */
strcpy (rname57, "");
- strcpy (rname58, TARGET_BIG_ENDIAN ? "mhi" : "mlo");
- strcpy (rname59, TARGET_BIG_ENDIAN ? "mlo" : "mhi");
+ strcpy (rname58, "mlo");
+ strcpy (rname59, "mhi");
}
/* The nature of arc_tp_regno is actually something more like a global
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)
return true;
return false;
+ case R58_REG:
+ case R59_REG:
+ /* ARC600 specifies those ones as mlo/mhi registers, otherwise
+ just handle them like any other extension register. */
+ 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] && !special_p)
+ || (TARGET_MUL64_SET && special_p)))
+ return true;
+ return false;
+
case 61:
case 62:
case 63:
int size;
unsigned int extra_plus_reg_size;
unsigned int extra_plus_reg_size_aligned;
+ unsigned int fn_type = arc_compute_function_type (cfun);
/* The answer might already be known. */
if (cfun->machine->frame_info.initialized)
/* Saving blink reg for millicode thunk calls. */
if (TARGET_MILLICODE_THUNK_SET
- && !crtl->calls_eh_return
- && !ARC_INTERRUPT_P (arc_compute_function_type (cfun)))
+ && !ARC_INTERRUPT_P (fn_type)
+ && !crtl->calls_eh_return)
{
if (arc_compute_millicode_save_restore_regs (gmask, frame_info))
frame_info->save_return_addr = true;
cfun, TARGET_DPFP))
reg_size += UNITS_PER_WORD * 2;
+ /* Check for special MLO/MHI case used by ARC600' MUL64
+ extension. */
+ if (arc_must_save_register (R58_REG, cfun, TARGET_MUL64_SET))
+ 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))
extra_size = 4;
- if (arc_frame_pointer_needed ())
+ /* Add FP size only when it is not autosaved. */
+ if (arc_frame_pointer_needed ()
+ && !ARC_AUTOFP_IRQ_P (fn_type))
extra_size += 4;
/* 5) Space for variable arguments passed in registers */
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. */
arc_save_callee_saves (uint64_t gmask,
bool save_blink,
bool save_fp,
- HOST_WIDE_INT offset)
+ HOST_WIDE_INT offset,
+ bool emit_move)
{
rtx reg;
int frame_allocated = 0;
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
}
/* Emit mov fp,sp. */
- if (arc_frame_pointer_needed ())
+ if (emit_move)
frame_move (hard_frame_pointer_rtx, stack_pointer_rtx);
return frame_allocated;
rtx reg;
int frame_deallocated = 0;
HOST_WIDE_INT offs = cfun->machine->frame_info.reg_size;
+ unsigned int fn_type = arc_compute_function_type (cfun);
bool early_blink_restore;
int i;
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
offset = 0;
}
- /* When we do not optimize for size, restore first blink. */
+ /* When we do not optimize for size or we aren't in an interrupt,
+ restore first blink. */
early_blink_restore = restore_blink && !optimize_size && offs
- && !ARC_INTERRUPT_P (arc_compute_function_type (cfun));
+ && !ARC_INTERRUPT_P (fn_type);
if (early_blink_restore)
{
rtx addr = plus_constant (Pmode, stack_pointer_rtx, offs);
unsigned int fn_type = arc_compute_function_type (cfun);
bool save_blink = false;
bool save_fp = false;
+ bool emit_move = false;
/* Naked functions don't have prologue. */
if (ARC_NAKED_P (fn_type))
save_blink = arc_must_save_return_addr (cfun)
&& !ARC_AUTOBLINK_IRQ_P (fn_type);
- save_fp = arc_frame_pointer_needed () && !ARC_AUTOFP_IRQ_P (fn_type);
+ save_fp = arc_frame_pointer_needed () && !ARC_AUTOFP_IRQ_P (fn_type)
+ && !ARC_INTERRUPT_P (fn_type);
+ emit_move = arc_frame_pointer_needed () && !ARC_INTERRUPT_P (fn_type);
/* Use enter/leave only for non-interrupt functions. */
if (TARGET_CODE_DENSITY
frame->reg_size);
else
frame_size_to_allocate -= arc_save_callee_saves (gmask, save_blink, save_fp,
- first_offset);
+ first_offset, emit_move);
+
+ /* 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_size_to_allocate -= 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_size_to_allocate -= push_reg (reg0);
+ emit_move_insn (reg0, gen_rtx_REG (SImode, LP_COUNT));
+ frame_size_to_allocate -= 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);
+ int i;
+
+ 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_size_to_allocate -= push_reg (reg0);
+ }
+ }
+
+ /* Save ARC600' MUL64 registers. */
+ if (arc_must_save_register (R58_REG, cfun, true))
+ frame_size_to_allocate -= arc_save_callee_saves (3ULL << 58,
+ false, false, 0, false);
+
+ if (arc_frame_pointer_needed () && ARC_INTERRUPT_P (fn_type))
+ {
+ /* Just save fp at the end of the saving context. */
+ frame_size_to_allocate -=
+ arc_save_callee_saves (0, false, !ARC_AUTOFP_IRQ_P (fn_type), 0, true);
+ }
/* Allocate the stack frame. */
if (frame_size_to_allocate > 0)
if (size)
emit_insn (gen_blockage ());
+ if (ARC_INTERRUPT_P (fn_type) && restore_fp)
+ {
+ /* We need to restore FP before any SP operation in an
+ interrupt. */
+ size_to_deallocate -= arc_restore_callee_saves (0, false,
+ restore_fp,
+ first_offset,
+ size_to_deallocate);
+ restore_fp = false;
+ first_offset = 0;
+ }
+
+ /* Restore ARC600' MUL64 registers. */
+ if (arc_must_save_register (R58_REG, cfun, true))
+ {
+ rtx insn;
+ rtx reg0 = gen_rtx_REG (SImode, R0_REG);
+ rtx reg1 = gen_rtx_REG (SImode, R1_REG);
+ size_to_deallocate -= pop_reg (reg0);
+ size_to_deallocate -= pop_reg (reg1);
+
+ insn = emit_insn (gen_mulu64 (reg0, const1_rtx));
+ add_reg_note (insn, REG_CFA_RESTORE, gen_rtx_REG (SImode, R58_REG));
+ RTX_FRAME_RELATED_P (insn) = 1;
+ emit_insn (gen_arc600_stall ());
+ insn = emit_insn (gen_rtx_UNSPEC_VOLATILE
+ (VOIDmode, gen_rtvec (2, reg1, GEN_INT (AUX_MULHI)),
+ VUNSPEC_ARC_SR));
+ add_reg_note (insn, REG_CFA_RESTORE, gen_rtx_REG (SImode, R59_REG));
+ RTX_FRAME_RELATED_P (insn) = 1;
+ }
+
+ /* 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);
+ int i;
+
+ for (i = 0; i < 4; i++)
+ {
+ size_to_deallocate -= 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);
+
+ size_to_deallocate -= pop_reg (reg0);
+ emit_move_insn (gen_rtx_REG (SImode, LP_COUNT), reg0);
+
+ size_to_deallocate -= pop_reg (reg0);
+ emit_insn (gen_rtx_UNSPEC_VOLATILE
+ (VOIDmode, gen_rtvec (2, reg0, GEN_INT (AUX_LP_END)),
+ VUNSPEC_ARC_SR));
+
+ size_to_deallocate -= pop_reg (reg0);
+ emit_insn (gen_rtx_UNSPEC_VOLATILE
+ (VOIDmode, gen_rtvec (2, reg0, GEN_INT (AUX_LP_START)),
+ VUNSPEC_ARC_SR));
+ }
+
if (TARGET_CODE_DENSITY
&& TARGET_CODE_DENSITY_FRAME
&& !ARC_AUTOFP_IRQ_P (fn_type)
return gen_rtx_REG (SImode, TARGET_BIG_ENDIAN ? 57: 56);
}
-/* Return a REG rtx for mlo. N.B. the gcc-internal representation may
- differ from the hardware register number in order to allow the generic
- code to correctly split the concatenation of mhi and mlo. */
-
-rtx
-gen_mlo (void)
-{
- return gen_rtx_REG (SImode, TARGET_BIG_ENDIAN ? 59: 58);
-}
-
-/* Return a REG rtx for mhi. N.B. the gcc-internal representation may
- differ from the hardware register number in order to allow the generic
- code to correctly split the concatenation of mhi and mlo. */
-
-rtx
-gen_mhi (void)
-{
- return gen_rtx_REG (SImode, TARGET_BIG_ENDIAN ? 58: 59);
-}
-
/* FIXME: a parameter should be added, and code added to final.c,
to reproduce this functionality in shorten_branches. */
#if 0
VUNSPEC_ARC_BLOCKAGE
VUNSPEC_ARC_EH_RETURN
VUNSPEC_ARC_ARC600_RTIE
+ VUNSPEC_ARC_ARC600_STALL
VUNSPEC_ARC_LDDI
VUNSPEC_ARC_STDI
])
(set_attr "predicable" "no, no, yes")
(set_attr "cond" "nocond, canuse_limm, canuse")])
+; The gcc-internal representation may differ from the hardware
+; register number in order to allow the generic code to correctly
+; split the concatenation of mhi and mlo.
(define_insn_and_split "mulsi64"
[(set (match_operand:SI 0 "register_operand" "=w")
(mult:SI (match_operand:SI 1 "register_operand" "%c")
"#"
"TARGET_MUL64_SET && reload_completed"
[(const_int 0)]
-{
- emit_insn (gen_mulsi_600 (operands[1], operands[2],
- gen_mlo (), gen_mhi ()));
- emit_move_insn (operands[0], gen_mlo ());
- DONE;
-}
+ {
+ rtx mhi = gen_rtx_REG (SImode, R59_REG);
+ rtx mlo = gen_rtx_REG (SImode, R58_REG);
+ emit_insn (gen_mulsi_600 (operands[1], operands[2], mlo, mhi));
+ emit_move_insn (operands[0], mlo);
+ DONE;
+ }
[(set_attr "type" "multi")
(set_attr "length" "8")])
(match_operand:SI 1 "nonmemory_operand" "Rcq#q,cL,I,Cal")))
(clobber (match_operand:SI 3 "mhi_operand" ""))]
"TARGET_MUL64_SET"
-; The assembler mis-assembles mul64 / mulu64 with "I" constraint constants,
-; using a machine code pattern that only allows "L" constraint constants.
-; "mul64%? \t0, %0, %1%&"
-{
- if (satisfies_constraint_I (operands[1])
- && !satisfies_constraint_L (operands[1]))
- {
- /* MUL64 <0,>b,s12 00101bbb10000100 0BBBssssssSSSSSS */
- int n = true_regnum (operands[0]);
- int i = INTVAL (operands[1]);
- asm_fprintf (asm_out_file, "\t.short %d`", 0x2884 + ((n & 7) << 8));
- asm_fprintf (asm_out_file, "\t.short %d`",
- ((i & 0x3f) << 6) + ((i >> 6) & 0x3f) + ((n & 070) << 9));
- return "; mul64%? \t0, %0, %1%&";
- }
- return "mul64%? \t0, %0, %1%&";
-}
+ "mul64%?\\t0,%0,%1"
[(set_attr "length" "*,4,4,8")
(set_attr "iscompact" "maybe,false,false,false")
(set_attr "type" "multi,multi,multi,multi")
(set_attr "type" "block")]
)
+(define_insn "arc600_stall"
+ [(unspec_volatile [(const_int 0)] VUNSPEC_ARC_ARC600_STALL)]
+ "TARGET_MUL64_SET"
+ "mov\\t0,mlo\t;wait until multiply complete."
+ [(set_attr "length" "4")
+ (set_attr "type" "move")]
+)
+
;; Split up troublesome insns for better scheduling.
;; Peepholes go at the end.