Commit d702f362 by Georg-Johann Lay Committed by Georg-Johann Lay

re PR target/50887 ([avr] Support ACCUMULATE_OUTGOING_ARGS)

	PR target/50887
	* config/avr/avr.opt (-maccumulate-args): New option.
	* config/avr/avr.h (STARTING_FRAME_OFFSET): Redefine to
	avr_starting_frame_offset.
	(ACCUMULATE_OUTGOING_ARGS): Define to avr_accumulate_outgoing_args.
	* config/avr/avr.md (UNSPECV_WRITE_SP_IRQ_ON): Remove.
	(UNSPECV_WRITE_SP_IRQ_OFF): Remove.
	(UNSPECV_WRITE_SP): New constant.
	(*addhi3_sp_R): Rewrite to...
	(*addhi3_sp): ...this new insn.
	(movhi_sp_r_irq_off, movhi_sp_r_irq_on): Combine to...
	(movhi_sp_r): ...this new insn.
	* config/avr/avr-protos.h (avr_accumulate_outgoing_args): New.
	(avr_starting_frame_offset): New.
	* config/avr/avr.c (avr_accumulate_outgoing_args): New function.
	(avr_starting_frame_offset): New function.
	(avr_outgoing_args_size): New static function.
	(avr_initial_elimination_offset): Use it.
	(avr_simple_epilogue): Use it.
	(avr_asm_function_end_prologue): Use it.
	(expand_epilogue): Use it.
	(expand_prologue): Use it.  Break out code to...
	(avr_prologue_setup_frame): ...this new static function.
	(avr_can_eliminate): Allow eliminating to frame pointer if there
	is one.
	(avr_frame_pointer_required_p): Use frame pointer if target has a
	nonlocal label.
	* config/avr/constraints.md (R): Remove.
	(Csp): New constraint.
	* config/avr/predicates.md (avr_sp_immediate_operand): Use it.

From-SVN: r180654
parent f439d6ba
2011-10-29 Georg-Johann Lay <avr@gjlay.de>
PR target/50887
* config/avr/avr.opt (-maccumulate-args): New option.
* config/avr/avr.h (STARTING_FRAME_OFFSET): Redefine to
avr_starting_frame_offset.
(ACCUMULATE_OUTGOING_ARGS): Define to avr_accumulate_outgoing_args.
* config/avr/avr.md (UNSPECV_WRITE_SP_IRQ_ON): Remove.
(UNSPECV_WRITE_SP_IRQ_OFF): Remove.
(UNSPECV_WRITE_SP): New constant.
(*addhi3_sp_R): Rewrite to...
(*addhi3_sp): ...this new insn.
(movhi_sp_r_irq_off, movhi_sp_r_irq_on): Combine to...
(movhi_sp_r): ...this new insn.
* config/avr/avr-protos.h (avr_accumulate_outgoing_args): New.
(avr_starting_frame_offset): New.
* config/avr/avr.c (avr_accumulate_outgoing_args): New function.
(avr_starting_frame_offset): New function.
(avr_outgoing_args_size): New static function.
(avr_initial_elimination_offset): Use it.
(avr_simple_epilogue): Use it.
(avr_asm_function_end_prologue): Use it.
(expand_epilogue): Use it.
(expand_prologue): Use it. Break out code to...
(avr_prologue_setup_frame): ...this new static function.
(avr_can_eliminate): Allow eliminating to frame pointer if there
is one.
(avr_frame_pointer_required_p): Use frame pointer if target has a
nonlocal label.
* config/avr/constraints.md (R): Remove.
(Csp): New constraint.
* config/avr/predicates.md (avr_sp_immediate_operand): Use it.
2011-10-29 Andi Kleen <ak@linux.intel.com> 2011-10-29 Andi Kleen <ak@linux.intel.com>
* gcc-ar.c (target_machine): Add. * gcc-ar.c (target_machine): Add.
...@@ -32,6 +32,7 @@ extern int avr_initial_elimination_offset (int from, int to); ...@@ -32,6 +32,7 @@ extern int avr_initial_elimination_offset (int from, int to);
extern int avr_simple_epilogue (void); extern int avr_simple_epilogue (void);
extern int avr_hard_regno_rename_ok (unsigned int, unsigned int); extern int avr_hard_regno_rename_ok (unsigned int, unsigned int);
extern rtx avr_return_addr_rtx (int count, rtx tem); extern rtx avr_return_addr_rtx (int count, rtx tem);
extern bool avr_accumulate_outgoing_args (void);
#ifdef TREE_CODE #ifdef TREE_CODE
extern void avr_asm_output_aligned_decl_common (FILE*, const_tree, const char*, unsigned HOST_WIDE_INT, unsigned int, bool); extern void avr_asm_output_aligned_decl_common (FILE*, const_tree, const char*, unsigned HOST_WIDE_INT, unsigned int, bool);
...@@ -77,6 +78,7 @@ extern bool avr_rotate_bytes (rtx operands[]); ...@@ -77,6 +78,7 @@ extern bool avr_rotate_bytes (rtx operands[]);
extern void expand_prologue (void); extern void expand_prologue (void);
extern void expand_epilogue (bool); extern void expand_epilogue (bool);
extern int avr_epilogue_uses (int regno); extern int avr_epilogue_uses (int regno);
extern int avr_starting_frame_offset (void);
extern void avr_output_bld (rtx operands[], int bit_nr); extern void avr_output_bld (rtx operands[], int bit_nr);
extern void avr_output_addr_vec_elt (FILE *stream, int value); extern void avr_output_addr_vec_elt (FILE *stream, int value);
......
...@@ -433,6 +433,47 @@ avr_OS_main_function_p (tree func) ...@@ -433,6 +433,47 @@ avr_OS_main_function_p (tree func)
return avr_lookup_function_attribute1 (func, "OS_main"); return avr_lookup_function_attribute1 (func, "OS_main");
} }
/* Implement `ACCUMULATE_OUTGOING_ARGS'. */
bool
avr_accumulate_outgoing_args (void)
{
if (!cfun)
return TARGET_ACCUMULATE_OUTGOING_ARGS;
/* FIXME: For setjmp and in avr_builtin_setjmp_frame_value we don't know
what offset is correct. In some cases it is relative to
virtual_outgoing_args_rtx and in others it is relative to
virtual_stack_vars_rtx. For example code see
gcc.c-torture/execute/built-in-setjmp.c
gcc.c-torture/execute/builtins/sprintf-chk.c */
return (TARGET_ACCUMULATE_OUTGOING_ARGS
&& !(cfun->calls_setjmp
|| cfun->has_nonlocal_label));
}
/* Report contribution of accumulated outgoing arguments to stack size. */
static inline int
avr_outgoing_args_size (void)
{
return ACCUMULATE_OUTGOING_ARGS ? crtl->outgoing_args_size : 0;
}
/* Implement `STARTING_FRAME_OFFSET'. */
/* This is the offset from the frame pointer register to the first stack slot
that contains a variable living in the frame. */
int
avr_starting_frame_offset (void)
{
return 1 + avr_outgoing_args_size ();
}
/* Return the number of hard registers to push/pop in the prologue/epilogue /* Return the number of hard registers to push/pop in the prologue/epilogue
of the current function, and optionally store these registers in SET. */ of the current function, and optionally store these registers in SET. */
...@@ -464,6 +505,8 @@ avr_regs_to_save (HARD_REG_SET *set) ...@@ -464,6 +505,8 @@ avr_regs_to_save (HARD_REG_SET *set)
if ((int_or_sig_p && !current_function_is_leaf && call_used_regs[reg]) if ((int_or_sig_p && !current_function_is_leaf && call_used_regs[reg])
|| (df_regs_ever_live_p (reg) || (df_regs_ever_live_p (reg)
&& (int_or_sig_p || !call_used_regs[reg]) && (int_or_sig_p || !call_used_regs[reg])
/* Don't record frame pointer registers here. They are treated
indivitually in prologue. */
&& !(frame_pointer_needed && !(frame_pointer_needed
&& (reg == REG_Y || reg == (REG_Y+1))))) && (reg == REG_Y || reg == (REG_Y+1)))))
{ {
...@@ -481,6 +524,7 @@ static bool ...@@ -481,6 +524,7 @@ static bool
avr_can_eliminate (const int from, const int to) avr_can_eliminate (const int from, const int to)
{ {
return ((from == ARG_POINTER_REGNUM && to == FRAME_POINTER_REGNUM) return ((from == ARG_POINTER_REGNUM && to == FRAME_POINTER_REGNUM)
|| (frame_pointer_needed && to == FRAME_POINTER_REGNUM)
|| ((from == FRAME_POINTER_REGNUM || ((from == FRAME_POINTER_REGNUM
|| from == FRAME_POINTER_REGNUM + 1) || from == FRAME_POINTER_REGNUM + 1)
&& !frame_pointer_needed)); && !frame_pointer_needed));
...@@ -499,7 +543,8 @@ avr_initial_elimination_offset (int from, int to) ...@@ -499,7 +543,8 @@ avr_initial_elimination_offset (int from, int to)
int avr_pc_size = AVR_HAVE_EIJMP_EICALL ? 3 : 2; int avr_pc_size = AVR_HAVE_EIJMP_EICALL ? 3 : 2;
offset += avr_regs_to_save (NULL); offset += avr_regs_to_save (NULL);
return get_frame_size () + (avr_pc_size) + 1 + offset; return (get_frame_size () + avr_outgoing_args_size()
+ avr_pc_size + 1 + offset);
} }
} }
...@@ -547,6 +592,7 @@ avr_simple_epilogue (void) ...@@ -547,6 +592,7 @@ avr_simple_epilogue (void)
{ {
return (! frame_pointer_needed return (! frame_pointer_needed
&& get_frame_size () == 0 && get_frame_size () == 0
&& avr_outgoing_args_size() == 0
&& avr_regs_to_save (NULL) == 0 && avr_regs_to_save (NULL) == 0
&& ! interrupt_function_p (current_function_decl) && ! interrupt_function_p (current_function_decl)
&& ! signal_function_p (current_function_decl) && ! signal_function_p (current_function_decl)
...@@ -656,87 +702,34 @@ emit_push_byte (unsigned regno, bool frame_related_p) ...@@ -656,87 +702,34 @@ emit_push_byte (unsigned regno, bool frame_related_p)
cfun->machine->stack_usage++; cfun->machine->stack_usage++;
} }
static void
/* Output function prologue. */ avr_prologue_setup_frame (HOST_WIDE_INT size, HARD_REG_SET set)
void
expand_prologue (void)
{ {
int live_seq;
HARD_REG_SET set;
int minimize;
HOST_WIDE_INT size = get_frame_size();
rtx insn; rtx insn;
bool isr_p = cfun->machine->is_interrupt || cfun->machine->is_signal;
int live_seq = sequent_regs_live ();
/* Init cfun->machine. */ bool minimize = (TARGET_CALL_PROLOGUES
cfun->machine->is_naked = avr_naked_function_p (current_function_decl); && live_seq
cfun->machine->is_interrupt = interrupt_function_p (current_function_decl); && !isr_p
cfun->machine->is_signal = signal_function_p (current_function_decl);
cfun->machine->is_OS_task = avr_OS_task_function_p (current_function_decl);
cfun->machine->is_OS_main = avr_OS_main_function_p (current_function_decl);
cfun->machine->stack_usage = 0;
/* Prologue: naked. */
if (cfun->machine->is_naked)
{
return;
}
avr_regs_to_save (&set);
live_seq = sequent_regs_live ();
minimize = (TARGET_CALL_PROLOGUES
&& !cfun->machine->is_interrupt
&& !cfun->machine->is_signal
&& !cfun->machine->is_OS_task && !cfun->machine->is_OS_task
&& !cfun->machine->is_OS_main && !cfun->machine->is_OS_main);
&& live_seq);
if (cfun->machine->is_interrupt || cfun->machine->is_signal)
{
/* Enable interrupts. */
if (cfun->machine->is_interrupt)
emit_insn (gen_enable_interrupt ());
/* Push zero reg. */
emit_push_byte (ZERO_REGNO, true);
/* Push tmp reg. */
emit_push_byte (TMP_REGNO, true);
/* Push SREG. */ if (minimize
/* ??? There's no dwarf2 column reserved for SREG. */ && (frame_pointer_needed
emit_move_insn (tmp_reg_rtx, gen_rtx_MEM (QImode, GEN_INT (SREG_ADDR))); || avr_outgoing_args_size() > 8
emit_push_byte (TMP_REGNO, false);
/* Push RAMPZ. */
/* ??? There's no dwarf2 column reserved for RAMPZ. */
if (AVR_HAVE_RAMPZ
&& TEST_HARD_REG_BIT (set, REG_Z)
&& TEST_HARD_REG_BIT (set, REG_Z + 1))
{
emit_move_insn (tmp_reg_rtx,
gen_rtx_MEM (QImode, GEN_INT (RAMPZ_ADDR)));
emit_push_byte (TMP_REGNO, false);
}
/* Clear zero reg. */
emit_move_insn (zero_reg_rtx, const0_rtx);
/* Prevent any attempt to delete the setting of ZERO_REG! */
emit_use (zero_reg_rtx);
}
if (minimize && (frame_pointer_needed
|| (AVR_2_BYTE_PC && live_seq > 6) || (AVR_2_BYTE_PC && live_seq > 6)
|| live_seq > 7)) || live_seq > 7))
{ {
rtx pattern;
int first_reg, reg, offset; int first_reg, reg, offset;
emit_move_insn (gen_rtx_REG (HImode, REG_X), emit_move_insn (gen_rtx_REG (HImode, REG_X),
gen_int_mode (size, HImode)); gen_int_mode (size, HImode));
insn = emit_insn (gen_call_prologue_saves pattern = gen_call_prologue_saves (gen_int_mode (live_seq, HImode),
(gen_int_mode (live_seq, HImode), gen_int_mode (live_seq+size, HImode));
gen_int_mode (size + live_seq, HImode))); insn = emit_insn (pattern);
RTX_FRAME_RELATED_P (insn) = 1; RTX_FRAME_RELATED_P (insn) = 1;
/* Describe the effect of the unspec_volatile call to prologue_saves. /* Describe the effect of the unspec_volatile call to prologue_saves.
...@@ -746,15 +739,17 @@ expand_prologue (void) ...@@ -746,15 +739,17 @@ expand_prologue (void)
/* The function does always set frame_pointer_rtx, but whether that /* The function does always set frame_pointer_rtx, but whether that
is going to be permanent in the function is frame_pointer_needed. */ is going to be permanent in the function is frame_pointer_needed. */
add_reg_note (insn, REG_CFA_ADJUST_CFA, add_reg_note (insn, REG_CFA_ADJUST_CFA,
gen_rtx_SET (VOIDmode, gen_rtx_SET (VOIDmode, (frame_pointer_needed
(frame_pointer_needed ? frame_pointer_rtx
? frame_pointer_rtx : stack_pointer_rtx), : stack_pointer_rtx),
plus_constant (stack_pointer_rtx, plus_constant (stack_pointer_rtx,
-(size + live_seq)))); -(size + live_seq))));
/* Note that live_seq always contains r28+r29, but the other /* Note that live_seq always contains r28+r29, but the other
registers to be saved are all below 18. */ registers to be saved are all below 18. */
first_reg = 18 - (live_seq - 2); first_reg = 18 - (live_seq - 2);
for (reg = 29, offset = -live_seq + 1; for (reg = 29, offset = -live_seq + 1;
...@@ -770,134 +765,229 @@ expand_prologue (void) ...@@ -770,134 +765,229 @@ expand_prologue (void)
cfun->machine->stack_usage += size + live_seq; cfun->machine->stack_usage += size + live_seq;
} }
else else /* !minimize */
{ {
int reg; int reg;
for (reg = 0; reg < 32; ++reg) for (reg = 0; reg < 32; ++reg)
if (TEST_HARD_REG_BIT (set, reg)) if (TEST_HARD_REG_BIT (set, reg))
emit_push_byte (reg, true); emit_push_byte (reg, true);
if (frame_pointer_needed) if (frame_pointer_needed
{ && (!(cfun->machine->is_OS_task || cfun->machine->is_OS_main)))
if (!(cfun->machine->is_OS_task || cfun->machine->is_OS_main))
{ {
/* Push frame pointer. Always be consistent about the /* Push frame pointer. Always be consistent about the
ordering of pushes -- epilogue_restores expects the ordering of pushes -- epilogue_restores expects the
register pair to be pushed low byte first. */ register pair to be pushed low byte first. */
emit_push_byte (REG_Y, true); emit_push_byte (REG_Y, true);
emit_push_byte (REG_Y + 1, true); emit_push_byte (REG_Y + 1, true);
} }
if (!size) if (frame_pointer_needed
&& size == 0)
{ {
insn = emit_move_insn (frame_pointer_rtx, stack_pointer_rtx); insn = emit_move_insn (frame_pointer_rtx, stack_pointer_rtx);
RTX_FRAME_RELATED_P (insn) = 1; RTX_FRAME_RELATED_P (insn) = 1;
} }
else
if (size != 0)
{ {
/* Creating a frame can be done by direct manipulation of the /* Creating a frame can be done by direct manipulation of the
stack or via the frame pointer. These two methods are: stack or via the frame pointer. These two methods are:
fp=sp fp = sp
fp-=size fp -= size
sp=fp sp = fp
OR or
sp-=size sp -= size
fp=sp fp = sp (*)
the optimum method depends on function type, stack and frame size. the optimum method depends on function type, stack and
To avoid a complex logic, both methods are tested and shortest frame size. To avoid a complex logic, both methods are
is selected. */ tested and shortest is selected.
rtx myfp;
rtx fp_plus_insns; There is also the case where SIZE != 0 and no frame pointer is
needed; this can occur if ACCUMULATE_OUTGOING_ARGS is on.
In that case, insn (*) is not needed in that case.
We use the X register as scratch. This is save because in X
is call-clobbered.
In an interrupt routine, the case of SIZE != 0 together with
!frame_pointer_needed can only occur if the function is not a
leaf function and thus X has already been saved. */
rtx fp_plus_insns, fp, my_fp;
rtx sp_minus_size = plus_constant (stack_pointer_rtx, -size);
gcc_assert (frame_pointer_needed
|| !isr_p
|| !current_function_is_leaf);
fp = my_fp = (frame_pointer_needed
? frame_pointer_rtx
: gen_rtx_REG (Pmode, REG_X));
if (AVR_HAVE_8BIT_SP) if (AVR_HAVE_8BIT_SP)
{ {
/* The high byte (r29) doesn't change. Prefer 'subi' /* The high byte (r29) does not change:
(1 cycle) over 'sbiw' (2 cycles, same size). */ Prefer SUBI (1 cycle) over ABIW (2 cycles, same size). */
myfp = gen_rtx_REG (QImode, FRAME_POINTER_REGNUM);
} my_fp = simplify_gen_subreg (QImode, fp, Pmode, 0);
else
{
/* Normal sized addition. */
myfp = frame_pointer_rtx;
} }
/* Method 1-Adjust frame pointer. */ /************ Method 1: Adjust frame pointer ************/
start_sequence (); start_sequence ();
/* Normally the dwarf2out frame-related-expr interpreter does /* Normally, the dwarf2out frame-related-expr interpreter does
not expect to have the CFA change once the frame pointer is not expect to have the CFA change once the frame pointer is
set up. Thus we avoid marking the move insn below and set up. Thus, we avoid marking the move insn below and
instead indicate that the entire operation is complete after instead indicate that the entire operation is complete after
the frame pointer subtraction is done. */ the frame pointer subtraction is done. */
emit_move_insn (frame_pointer_rtx, stack_pointer_rtx); insn = emit_move_insn (fp, stack_pointer_rtx);
if (!frame_pointer_needed)
RTX_FRAME_RELATED_P (insn) = 1;
insn = emit_move_insn (myfp, plus_constant (myfp, -size)); insn = emit_move_insn (my_fp, plus_constant (my_fp, -size));
RTX_FRAME_RELATED_P (insn) = 1; RTX_FRAME_RELATED_P (insn) = 1;
if (frame_pointer_needed)
{
add_reg_note (insn, REG_CFA_ADJUST_CFA, add_reg_note (insn, REG_CFA_ADJUST_CFA,
gen_rtx_SET (VOIDmode, frame_pointer_rtx, gen_rtx_SET (VOIDmode, fp, sp_minus_size));
plus_constant (stack_pointer_rtx, }
-size)));
/* Copy to stack pointer. Note that since we've already /* Copy to stack pointer. Note that since we've already
changed the CFA to the frame pointer this operation changed the CFA to the frame pointer this operation
need not be annotated at all. */ need not be annotated if frame pointer is needed. */
if (AVR_HAVE_8BIT_SP) if (AVR_HAVE_8BIT_SP)
{ {
emit_move_insn (stack_pointer_rtx, frame_pointer_rtx); insn = emit_move_insn (stack_pointer_rtx, fp);
} }
else if (TARGET_NO_INTERRUPTS else if (TARGET_NO_INTERRUPTS
|| cfun->machine->is_signal || isr_p
|| cfun->machine->is_OS_main) || cfun->machine->is_OS_main)
{ {
emit_insn (gen_movhi_sp_r_irq_off (stack_pointer_rtx, rtx irqs_are_on = GEN_INT (!!cfun->machine->is_interrupt);
frame_pointer_rtx));
} insn = emit_insn (gen_movhi_sp_r (stack_pointer_rtx,
else if (cfun->machine->is_interrupt) fp, irqs_are_on));
{
emit_insn (gen_movhi_sp_r_irq_on (stack_pointer_rtx,
frame_pointer_rtx));
} }
else else
{ {
emit_move_insn (stack_pointer_rtx, frame_pointer_rtx); insn = emit_move_insn (stack_pointer_rtx, fp);
} }
if (!frame_pointer_needed)
RTX_FRAME_RELATED_P (insn) = 1;
fp_plus_insns = get_insns (); fp_plus_insns = get_insns ();
end_sequence (); end_sequence ();
/* Method 2-Adjust Stack pointer. */ /************ Method 2: Adjust Stack pointer ************/
if (size <= 6)
/* Stack adjustment by means of RCALL . and/or PUSH __TMP_REG__
can only handle specific offsets. */
if (avr_sp_immediate_operand (gen_int_mode (-size, HImode), HImode))
{ {
rtx sp_plus_insns; rtx sp_plus_insns;
start_sequence (); start_sequence ();
insn = plus_constant (stack_pointer_rtx, -size); insn = emit_move_insn (stack_pointer_rtx, sp_minus_size);
insn = emit_move_insn (stack_pointer_rtx, insn);
RTX_FRAME_RELATED_P (insn) = 1; RTX_FRAME_RELATED_P (insn) = 1;
insn = emit_move_insn (frame_pointer_rtx, stack_pointer_rtx); if (frame_pointer_needed)
{
insn = emit_move_insn (fp, stack_pointer_rtx);
RTX_FRAME_RELATED_P (insn) = 1; RTX_FRAME_RELATED_P (insn) = 1;
}
sp_plus_insns = get_insns (); sp_plus_insns = get_insns ();
end_sequence (); end_sequence ();
/* Use shortest method. */ /************ Use shortest method ************/
if (get_sequence_length (sp_plus_insns)
< get_sequence_length (fp_plus_insns)) emit_insn (get_sequence_length (sp_plus_insns)
emit_insn (sp_plus_insns); < get_sequence_length (fp_plus_insns)
else ? sp_plus_insns
emit_insn (fp_plus_insns); : fp_plus_insns);
} }
else else
{
emit_insn (fp_plus_insns); emit_insn (fp_plus_insns);
}
cfun->machine->stack_usage += size; cfun->machine->stack_usage += size;
} /* !minimize && size != 0 */
} /* !minimize */
}
/* Output function prologue. */
void
expand_prologue (void)
{
HARD_REG_SET set;
HOST_WIDE_INT size;
size = get_frame_size() + avr_outgoing_args_size();
/* Init cfun->machine. */
cfun->machine->is_naked = avr_naked_function_p (current_function_decl);
cfun->machine->is_interrupt = interrupt_function_p (current_function_decl);
cfun->machine->is_signal = signal_function_p (current_function_decl);
cfun->machine->is_OS_task = avr_OS_task_function_p (current_function_decl);
cfun->machine->is_OS_main = avr_OS_main_function_p (current_function_decl);
cfun->machine->stack_usage = 0;
/* Prologue: naked. */
if (cfun->machine->is_naked)
{
return;
} }
avr_regs_to_save (&set);
if (cfun->machine->is_interrupt || cfun->machine->is_signal)
{
/* Enable interrupts. */
if (cfun->machine->is_interrupt)
emit_insn (gen_enable_interrupt ());
/* Push zero reg. */
emit_push_byte (ZERO_REGNO, true);
/* Push tmp reg. */
emit_push_byte (TMP_REGNO, true);
/* Push SREG. */
/* ??? There's no dwarf2 column reserved for SREG. */
emit_move_insn (tmp_reg_rtx, gen_rtx_MEM (QImode, GEN_INT (SREG_ADDR)));
emit_push_byte (TMP_REGNO, false);
/* Push RAMPZ. */
/* ??? There's no dwarf2 column reserved for RAMPZ. */
if (AVR_HAVE_RAMPZ
&& TEST_HARD_REG_BIT (set, REG_Z)
&& TEST_HARD_REG_BIT (set, REG_Z + 1))
{
emit_move_insn (tmp_reg_rtx,
gen_rtx_MEM (QImode, GEN_INT (RAMPZ_ADDR)));
emit_push_byte (TMP_REGNO, false);
} }
/* Clear zero reg. */
emit_move_insn (zero_reg_rtx, const0_rtx);
/* Prevent any attempt to delete the setting of ZERO_REG! */
emit_use (zero_reg_rtx);
} }
avr_prologue_setup_frame (size, set);
if (flag_stack_usage_info) if (flag_stack_usage_info)
current_function_static_stack_size = cfun->machine->stack_usage; current_function_static_stack_size = cfun->machine->stack_usage;
} }
...@@ -924,6 +1014,11 @@ avr_asm_function_end_prologue (FILE *file) ...@@ -924,6 +1014,11 @@ avr_asm_function_end_prologue (FILE *file)
else else
fputs ("/* prologue: function */\n", file); fputs ("/* prologue: function */\n", file);
} }
if (ACCUMULATE_OUTGOING_ARGS)
fprintf (file, "/* outgoing args size = %d */\n",
avr_outgoing_args_size());
fprintf (file, "/* frame size = " HOST_WIDE_INT_PRINT_DEC " */\n", fprintf (file, "/* frame size = " HOST_WIDE_INT_PRINT_DEC " */\n",
get_frame_size()); get_frame_size());
fprintf (file, "/* stack size = %d */\n", fprintf (file, "/* stack size = %d */\n",
...@@ -969,7 +1064,10 @@ expand_epilogue (bool sibcall_p) ...@@ -969,7 +1064,10 @@ expand_epilogue (bool sibcall_p)
int live_seq; int live_seq;
HARD_REG_SET set; HARD_REG_SET set;
int minimize; int minimize;
HOST_WIDE_INT size = get_frame_size(); HOST_WIDE_INT size;
bool isr_p = cfun->machine->is_interrupt || cfun->machine->is_signal;
size = get_frame_size() + avr_outgoing_args_size();
/* epilogue: naked */ /* epilogue: naked */
if (cfun->machine->is_naked) if (cfun->machine->is_naked)
...@@ -982,83 +1080,92 @@ expand_epilogue (bool sibcall_p) ...@@ -982,83 +1080,92 @@ expand_epilogue (bool sibcall_p)
avr_regs_to_save (&set); avr_regs_to_save (&set);
live_seq = sequent_regs_live (); live_seq = sequent_regs_live ();
minimize = (TARGET_CALL_PROLOGUES minimize = (TARGET_CALL_PROLOGUES
&& !cfun->machine->is_interrupt && live_seq
&& !cfun->machine->is_signal && !isr_p
&& !cfun->machine->is_OS_task && !cfun->machine->is_OS_task
&& !cfun->machine->is_OS_main && !cfun->machine->is_OS_main);
&& live_seq);
if (minimize && (frame_pointer_needed || live_seq > 4)) if (minimize
{ && (live_seq > 4
if (frame_pointer_needed) || frame_pointer_needed
|| size))
{ {
/* Get rid of frame. */ /* Get rid of frame. */
if (size)
emit_move_insn (frame_pointer_rtx, if (!frame_pointer_needed)
gen_rtx_PLUS (HImode, frame_pointer_rtx,
gen_int_mode (size, HImode)));
}
else
{ {
emit_move_insn (frame_pointer_rtx, stack_pointer_rtx); emit_move_insn (frame_pointer_rtx, stack_pointer_rtx);
} }
if (size)
{
emit_move_insn (frame_pointer_rtx,
plus_constant (frame_pointer_rtx, size));
}
emit_insn (gen_epilogue_restores (gen_int_mode (live_seq, HImode))); emit_insn (gen_epilogue_restores (gen_int_mode (live_seq, HImode)));
return;
} }
else
{
if (frame_pointer_needed)
{
if (size) if (size)
{ {
/* Try two methods to adjust stack and select shortest. */ /* Try two methods to adjust stack and select shortest. */
rtx myfp;
rtx fp, my_fp;
rtx fp_plus_insns; rtx fp_plus_insns;
gcc_assert (frame_pointer_needed
|| !isr_p
|| !current_function_is_leaf);
fp = my_fp = (frame_pointer_needed
? frame_pointer_rtx
: gen_rtx_REG (Pmode, REG_X));
if (AVR_HAVE_8BIT_SP) if (AVR_HAVE_8BIT_SP)
{ {
/* The high byte (r29) doesn't change - prefer 'subi' /* The high byte (r29) does not change:
(1 cycle) over 'sbiw' (2 cycles, same size). */ Prefer SUBI (1 cycle) over SBIW (2 cycles). */
myfp = gen_rtx_REG (QImode, FRAME_POINTER_REGNUM);
} my_fp = simplify_gen_subreg (QImode, fp, Pmode, 0);
else
{
/* Normal sized addition. */
myfp = frame_pointer_rtx;
} }
/* Method 1-Adjust frame pointer. */ /********** Method 1: Adjust fp register **********/
start_sequence (); start_sequence ();
emit_move_insn (myfp, plus_constant (myfp, size)); if (!frame_pointer_needed)
emit_move_insn (fp, stack_pointer_rtx);
emit_move_insn (my_fp, plus_constant (my_fp, size));
/* Copy to stack pointer. */ /* Copy to stack pointer. */
if (AVR_HAVE_8BIT_SP) if (AVR_HAVE_8BIT_SP)
{ {
emit_move_insn (stack_pointer_rtx, frame_pointer_rtx); emit_move_insn (stack_pointer_rtx, fp);
} }
else if (TARGET_NO_INTERRUPTS else if (TARGET_NO_INTERRUPTS
|| cfun->machine->is_signal) || isr_p
{ || cfun->machine->is_OS_main)
emit_insn (gen_movhi_sp_r_irq_off (stack_pointer_rtx,
frame_pointer_rtx));
}
else if (cfun->machine->is_interrupt)
{ {
emit_insn (gen_movhi_sp_r_irq_on (stack_pointer_rtx, rtx irqs_are_on = GEN_INT (!!cfun->machine->is_interrupt);
frame_pointer_rtx));
emit_insn (gen_movhi_sp_r (stack_pointer_rtx, fp, irqs_are_on));
} }
else else
{ {
emit_move_insn (stack_pointer_rtx, frame_pointer_rtx); emit_move_insn (stack_pointer_rtx, fp);
} }
fp_plus_insns = get_insns (); fp_plus_insns = get_insns ();
end_sequence (); end_sequence ();
/* Method 2-Adjust Stack pointer. */ /********** Method 2: Adjust Stack pointer **********/
if (size <= 5)
if (avr_sp_immediate_operand (gen_int_mode (size, HImode), HImode))
{ {
rtx sp_plus_insns; rtx sp_plus_insns;
...@@ -1070,33 +1177,37 @@ expand_epilogue (bool sibcall_p) ...@@ -1070,33 +1177,37 @@ expand_epilogue (bool sibcall_p)
sp_plus_insns = get_insns (); sp_plus_insns = get_insns ();
end_sequence (); end_sequence ();
/* Use shortest method. */ /************ Use shortest method ************/
if (get_sequence_length (sp_plus_insns)
< get_sequence_length (fp_plus_insns)) emit_insn (get_sequence_length (sp_plus_insns)
emit_insn (sp_plus_insns); < get_sequence_length (fp_plus_insns)
else ? sp_plus_insns
emit_insn (fp_plus_insns); : fp_plus_insns);
} }
else else
emit_insn (fp_plus_insns); emit_insn (fp_plus_insns);
} } /* size != 0 */
if (!(cfun->machine->is_OS_task || cfun->machine->is_OS_main))
if (frame_pointer_needed
&& !(cfun->machine->is_OS_task || cfun->machine->is_OS_main))
{ {
/* Restore previous frame_pointer. See expand_prologue for /* Restore previous frame_pointer. See expand_prologue for
rationale for not using pophi. */ rationale for not using pophi. */
emit_pop_byte (REG_Y + 1); emit_pop_byte (REG_Y + 1);
emit_pop_byte (REG_Y); emit_pop_byte (REG_Y);
} }
}
/* Restore used registers. */ /* Restore used registers. */
for (reg = 31; reg >= 0; --reg) for (reg = 31; reg >= 0; --reg)
if (TEST_HARD_REG_BIT (set, reg)) if (TEST_HARD_REG_BIT (set, reg))
emit_pop_byte (reg); emit_pop_byte (reg);
if (cfun->machine->is_interrupt || cfun->machine->is_signal) if (isr_p)
{ {
/* Restore RAMPZ using tmp reg as scratch. */ /* Restore RAMPZ using tmp reg as scratch. */
if (AVR_HAVE_RAMPZ if (AVR_HAVE_RAMPZ
&& TEST_HARD_REG_BIT (set, REG_Z) && TEST_HARD_REG_BIT (set, REG_Z)
&& TEST_HARD_REG_BIT (set, REG_Z + 1)) && TEST_HARD_REG_BIT (set, REG_Z + 1))
...@@ -1107,8 +1218,8 @@ expand_epilogue (bool sibcall_p) ...@@ -1107,8 +1218,8 @@ expand_epilogue (bool sibcall_p)
} }
/* Restore SREG using tmp reg as scratch. */ /* Restore SREG using tmp reg as scratch. */
emit_pop_byte (TMP_REGNO);
emit_pop_byte (TMP_REGNO);
emit_move_insn (gen_rtx_MEM (QImode, GEN_INT (SREG_ADDR)), emit_move_insn (gen_rtx_MEM (QImode, GEN_INT (SREG_ADDR)),
tmp_reg_rtx); tmp_reg_rtx);
...@@ -1121,7 +1232,6 @@ expand_epilogue (bool sibcall_p) ...@@ -1121,7 +1232,6 @@ expand_epilogue (bool sibcall_p)
if (!sibcall_p) if (!sibcall_p)
emit_jump_insn (gen_return ()); emit_jump_insn (gen_return ());
}
} }
/* Output summary messages at beginning of function epilogue. */ /* Output summary messages at beginning of function epilogue. */
...@@ -3069,6 +3179,8 @@ static bool ...@@ -3069,6 +3179,8 @@ static bool
avr_frame_pointer_required_p (void) avr_frame_pointer_required_p (void)
{ {
return (cfun->calls_alloca return (cfun->calls_alloca
|| cfun->calls_setjmp
|| cfun->has_nonlocal_label
|| crtl->args.info.nregs == 0 || crtl->args.info.nregs == 0
|| get_frame_size () > 0); || get_frame_size () > 0);
} }
......
...@@ -324,7 +324,7 @@ enum reg_class { ...@@ -324,7 +324,7 @@ enum reg_class {
#define STACK_GROWS_DOWNWARD #define STACK_GROWS_DOWNWARD
#define STARTING_FRAME_OFFSET 1 #define STARTING_FRAME_OFFSET avr_starting_frame_offset()
#define STACK_POINTER_OFFSET 1 #define STACK_POINTER_OFFSET 1
...@@ -635,3 +635,5 @@ struct GTY(()) machine_function ...@@ -635,3 +635,5 @@ struct GTY(()) machine_function
/* AVR does not round pushes, but the existance of this macro is /* AVR does not round pushes, but the existance of this macro is
required in order for pushes to be generated. */ required in order for pushes to be generated. */
#define PUSH_ROUNDING(X) (X) #define PUSH_ROUNDING(X) (X)
#define ACCUMULATE_OUTGOING_ARGS avr_accumulate_outgoing_args()
...@@ -62,8 +62,7 @@ ...@@ -62,8 +62,7 @@
(define_c_enum "unspecv" (define_c_enum "unspecv"
[UNSPECV_PROLOGUE_SAVES [UNSPECV_PROLOGUE_SAVES
UNSPECV_EPILOGUE_RESTORES UNSPECV_EPILOGUE_RESTORES
UNSPECV_WRITE_SP_IRQ_ON UNSPECV_WRITE_SP
UNSPECV_WRITE_SP_IRQ_OFF
UNSPECV_GOTO_RECEIVER UNSPECV_GOTO_RECEIVER
UNSPECV_ENABLE_IRQS UNSPECV_ENABLE_IRQS
UNSPECV_NOP UNSPECV_NOP
...@@ -380,26 +379,24 @@ ...@@ -380,26 +379,24 @@
} }
}") }")
(define_insn "movhi_sp_r_irq_off"
[(set (match_operand:HI 0 "stack_register_operand" "=q")
(unspec_volatile:HI [(match_operand:HI 1 "register_operand" "r")]
UNSPECV_WRITE_SP_IRQ_OFF))]
""
"out __SP_H__, %B1
out __SP_L__, %A1"
[(set_attr "length" "2")
(set_attr "cc" "none")])
(define_insn "movhi_sp_r_irq_on" ;; Move register $1 to the Stack Pointer register SP.
[(set (match_operand:HI 0 "stack_register_operand" "=q") ;; This insn is emit during function prologue/epilogue generation.
(unspec_volatile:HI [(match_operand:HI 1 "register_operand" "r")] ;; $2 = 0: We know that IRQs are off
UNSPECV_WRITE_SP_IRQ_ON))] ;; $2 = 1: We know that IRQs are on
;; Remaining cases when the state of the I-Flag is unknown are
;; handled by generic movhi insn.
(define_insn "movhi_sp_r"
[(set (match_operand:HI 0 "stack_register_operand" "=q,q")
(unspec_volatile:HI [(match_operand:HI 1 "register_operand" "r,r")
(match_operand:HI 2 "const_int_operand" "L,P")]
UNSPECV_WRITE_SP))]
"" ""
"cli "@
out __SP_H__, %B1 out __SP_H__,%B1\;out __SP_L__,%A1
sei cli\;out __SP_H__,%B1\;sei\;out __SP_L__,%A1"
out __SP_L__, %A1" [(set_attr "length" "2,4")
[(set_attr "length" "4")
(set_attr "cc" "none")]) (set_attr "cc" "none")])
(define_peephole2 (define_peephole2
...@@ -800,15 +797,15 @@ ...@@ -800,15 +797,15 @@
[(set_attr "length" "2") [(set_attr "length" "2")
(set_attr "cc" "set_n")]) (set_attr "cc" "set_n")])
(define_insn "*addhi3_sp_R" (define_insn "*addhi3_sp"
[(set (match_operand:HI 1 "stack_register_operand" "=q") [(set (match_operand:HI 1 "stack_register_operand" "=q")
(plus:HI (match_operand:HI 2 "stack_register_operand" "q") (plus:HI (match_operand:HI 2 "stack_register_operand" "q")
(match_operand:HI 0 "avr_sp_immediate_operand" "R")))] (match_operand:HI 0 "avr_sp_immediate_operand" "Csp")))]
"" ""
{ {
return avr_out_addto_sp (operands, NULL); return avr_out_addto_sp (operands, NULL);
} }
[(set_attr "length" "5") [(set_attr "length" "6")
(set_attr "adjust_len" "addto_sp")]) (set_attr "adjust_len" "addto_sp")])
(define_insn "*addhi3" (define_insn "*addhi3"
......
...@@ -62,6 +62,10 @@ mpmem-wrap-around ...@@ -62,6 +62,10 @@ mpmem-wrap-around
Target Report Target Report
Make the linker relaxation machine assume that a program counter wrap-around occurs. Make the linker relaxation machine assume that a program counter wrap-around occurs.
maccumulate-args
Target Report Mask(ACCUMULATE_OUTGOING_ARGS)
Accumulate outgoing function arguments and acquire/release the needed stack space for outpoing function arguments in function prologue/epilogue. Without this option, outgoing arguments are pushed before calling a function and popped afterwards. This option can lead to reduced code size for functions that call many functions that get their arguments on the stack like, for example printf.
mstrict-X mstrict-X
Target Report Var(avr_strict_X) Init(0) Target Report Var(avr_strict_X) Init(0)
When accessing RAM, use X as imposed by the hardware, i.e. just use pre-decrement, post-increment and indirect addressing with the X register. Without this option, the compiler may assume that there is an addressing mode X+const similar to Y+const and Z+const and emit instructions to emulate such an addressing mode for X. When accessing RAM, use X as imposed by the hardware, i.e. just use pre-decrement, post-increment and indirect addressing with the X register. Without this option, the compiler may assume that there is an addressing mode X+const similar to Y+const and Z+const and emit instructions to emulate such an addressing mode for X.
...@@ -98,11 +98,6 @@ ...@@ -98,11 +98,6 @@
(and (match_code "const_double") (and (match_code "const_double")
(match_test "op == CONST0_RTX (SFmode)"))) (match_test "op == CONST0_RTX (SFmode)")))
(define_constraint "R"
"Integer constant in the range -6 @dots{} 5."
(and (match_code "const_int")
(match_test "ival >= -6 && ival <= 5")))
(define_memory_constraint "Q" (define_memory_constraint "Q"
"A memory address based on Y or Z pointer with displacement." "A memory address based on Y or Z pointer with displacement."
(and (match_code "mem") (and (match_code "mem")
...@@ -162,3 +157,8 @@ ...@@ -162,3 +157,8 @@
"Constant 4-byte integer that allows XOR without clobber register." "Constant 4-byte integer that allows XOR without clobber register."
(and (match_code "const_int") (and (match_code "const_int")
(match_test "avr_popcount_each_byte (op, 4, (1<<0) | (1<<8))"))) (match_test "avr_popcount_each_byte (op, 4, (1<<0) | (1<<8))")))
(define_constraint "Csp"
"Integer constant in the range -6 @dots{} 6."
(and (match_code "const_int")
(match_test "IN_RANGE (ival, -6, 6)")))
...@@ -135,7 +135,7 @@ ...@@ -135,7 +135,7 @@
;; ;;
(define_predicate "avr_sp_immediate_operand" (define_predicate "avr_sp_immediate_operand"
(and (match_code "const_int") (and (match_code "const_int")
(match_test "INTVAL (op) >= -6 && INTVAL (op) <= 5"))) (match_test "satisfies_constraint_Csp (op)")))
;; True for EQ & NE ;; True for EQ & NE
(define_predicate "eqne_operator" (define_predicate "eqne_operator"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment