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>
* gcc-ar.c (target_machine): Add.
......@@ -32,6 +32,7 @@ extern int avr_initial_elimination_offset (int from, int to);
extern int avr_simple_epilogue (void);
extern int avr_hard_regno_rename_ok (unsigned int, unsigned int);
extern rtx avr_return_addr_rtx (int count, rtx tem);
extern bool avr_accumulate_outgoing_args (void);
#ifdef TREE_CODE
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[]);
extern void expand_prologue (void);
extern void expand_epilogue (bool);
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_addr_vec_elt (FILE *stream, int value);
......
......@@ -433,6 +433,47 @@ avr_OS_main_function_p (tree func)
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
of the current function, and optionally store these registers in SET. */
......@@ -441,7 +482,7 @@ avr_regs_to_save (HARD_REG_SET *set)
{
int reg, count;
int int_or_sig_p = (interrupt_function_p (current_function_decl)
|| signal_function_p (current_function_decl));
|| signal_function_p (current_function_decl));
if (set)
CLEAR_HARD_REG_SET (*set);
......@@ -457,20 +498,22 @@ avr_regs_to_save (HARD_REG_SET *set)
for (reg = 0; reg < 32; reg++)
{
/* Do not push/pop __tmp_reg__, __zero_reg__, as well as
any global register variables. */
any global register variables. */
if (fixed_regs[reg])
continue;
continue;
if ((int_or_sig_p && !current_function_is_leaf && call_used_regs[reg])
|| (df_regs_ever_live_p (reg)
&& (int_or_sig_p || !call_used_regs[reg])
&& !(frame_pointer_needed
&& (reg == REG_Y || reg == (REG_Y+1)))))
{
if (set)
SET_HARD_REG_BIT (*set, reg);
count++;
}
|| (df_regs_ever_live_p (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
&& (reg == REG_Y || reg == (REG_Y+1)))))
{
if (set)
SET_HARD_REG_BIT (*set, reg);
count++;
}
}
return count;
}
......@@ -481,9 +524,10 @@ static bool
avr_can_eliminate (const int from, const int to)
{
return ((from == ARG_POINTER_REGNUM && to == FRAME_POINTER_REGNUM)
|| ((from == FRAME_POINTER_REGNUM
|| from == FRAME_POINTER_REGNUM + 1)
&& !frame_pointer_needed));
|| (frame_pointer_needed && to == FRAME_POINTER_REGNUM)
|| ((from == FRAME_POINTER_REGNUM
|| from == FRAME_POINTER_REGNUM + 1)
&& !frame_pointer_needed));
}
/* Compute offset between arg_pointer and frame_pointer. */
......@@ -497,9 +541,10 @@ avr_initial_elimination_offset (int from, int to)
{
int offset = frame_pointer_needed ? 2 : 0;
int avr_pc_size = AVR_HAVE_EIJMP_EICALL ? 3 : 2;
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);
}
}
......@@ -546,12 +591,13 @@ int
avr_simple_epilogue (void)
{
return (! frame_pointer_needed
&& get_frame_size () == 0
&& avr_regs_to_save (NULL) == 0
&& ! interrupt_function_p (current_function_decl)
&& ! signal_function_p (current_function_decl)
&& ! avr_naked_function_p (current_function_decl)
&& ! TREE_THIS_VOLATILE (current_function_decl));
&& get_frame_size () == 0
&& avr_outgoing_args_size() == 0
&& avr_regs_to_save (NULL) == 0
&& ! interrupt_function_p (current_function_decl)
&& ! signal_function_p (current_function_decl)
&& ! avr_naked_function_p (current_function_decl)
&& ! TREE_THIS_VOLATILE (current_function_decl));
}
/* This function checks sequence of live registers. */
......@@ -656,17 +702,238 @@ emit_push_byte (unsigned regno, bool frame_related_p)
cfun->machine->stack_usage++;
}
static void
avr_prologue_setup_frame (HOST_WIDE_INT size, HARD_REG_SET set)
{
rtx insn;
bool isr_p = cfun->machine->is_interrupt || cfun->machine->is_signal;
int live_seq = sequent_regs_live ();
bool minimize = (TARGET_CALL_PROLOGUES
&& live_seq
&& !isr_p
&& !cfun->machine->is_OS_task
&& !cfun->machine->is_OS_main);
if (minimize
&& (frame_pointer_needed
|| avr_outgoing_args_size() > 8
|| (AVR_2_BYTE_PC && live_seq > 6)
|| live_seq > 7))
{
rtx pattern;
int first_reg, reg, offset;
emit_move_insn (gen_rtx_REG (HImode, REG_X),
gen_int_mode (size, HImode));
pattern = gen_call_prologue_saves (gen_int_mode (live_seq, HImode),
gen_int_mode (live_seq+size, HImode));
insn = emit_insn (pattern);
RTX_FRAME_RELATED_P (insn) = 1;
/* Describe the effect of the unspec_volatile call to prologue_saves.
Note that this formulation assumes that add_reg_note pushes the
notes to the front. Thus we build them in the reverse order of
how we want dwarf2out to process them. */
/* The function does always set frame_pointer_rtx, but whether that
is going to be permanent in the function is frame_pointer_needed. */
add_reg_note (insn, REG_CFA_ADJUST_CFA,
gen_rtx_SET (VOIDmode, (frame_pointer_needed
? frame_pointer_rtx
: stack_pointer_rtx),
plus_constant (stack_pointer_rtx,
-(size + live_seq))));
/* Note that live_seq always contains r28+r29, but the other
registers to be saved are all below 18. */
first_reg = 18 - (live_seq - 2);
for (reg = 29, offset = -live_seq + 1;
reg >= first_reg;
reg = (reg == 28 ? 17 : reg - 1), ++offset)
{
rtx m, r;
m = gen_rtx_MEM (QImode, plus_constant (stack_pointer_rtx, offset));
r = gen_rtx_REG (QImode, reg);
add_reg_note (insn, REG_CFA_OFFSET, gen_rtx_SET (VOIDmode, m, r));
}
cfun->machine->stack_usage += size + live_seq;
}
else /* !minimize */
{
int reg;
for (reg = 0; reg < 32; ++reg)
if (TEST_HARD_REG_BIT (set, reg))
emit_push_byte (reg, true);
if (frame_pointer_needed
&& (!(cfun->machine->is_OS_task || cfun->machine->is_OS_main)))
{
/* Push frame pointer. Always be consistent about the
ordering of pushes -- epilogue_restores expects the
register pair to be pushed low byte first. */
emit_push_byte (REG_Y, true);
emit_push_byte (REG_Y + 1, true);
}
if (frame_pointer_needed
&& size == 0)
{
insn = emit_move_insn (frame_pointer_rtx, stack_pointer_rtx);
RTX_FRAME_RELATED_P (insn) = 1;
}
if (size != 0)
{
/* Creating a frame can be done by direct manipulation of the
stack or via the frame pointer. These two methods are:
fp = sp
fp -= size
sp = fp
or
sp -= size
fp = sp (*)
the optimum method depends on function type, stack and
frame size. To avoid a complex logic, both methods are
tested and shortest is selected.
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)
{
/* The high byte (r29) does not change:
Prefer SUBI (1 cycle) over ABIW (2 cycles, same size). */
my_fp = simplify_gen_subreg (QImode, fp, Pmode, 0);
}
/************ Method 1: Adjust frame pointer ************/
start_sequence ();
/* Normally, the dwarf2out frame-related-expr interpreter does
not expect to have the CFA change once the frame pointer is
set up. Thus, we avoid marking the move insn below and
instead indicate that the entire operation is complete after
the frame pointer subtraction is done. */
insn = emit_move_insn (fp, stack_pointer_rtx);
if (!frame_pointer_needed)
RTX_FRAME_RELATED_P (insn) = 1;
insn = emit_move_insn (my_fp, plus_constant (my_fp, -size));
RTX_FRAME_RELATED_P (insn) = 1;
if (frame_pointer_needed)
{
add_reg_note (insn, REG_CFA_ADJUST_CFA,
gen_rtx_SET (VOIDmode, fp, sp_minus_size));
}
/* Copy to stack pointer. Note that since we've already
changed the CFA to the frame pointer this operation
need not be annotated if frame pointer is needed. */
if (AVR_HAVE_8BIT_SP)
{
insn = emit_move_insn (stack_pointer_rtx, fp);
}
else if (TARGET_NO_INTERRUPTS
|| isr_p
|| cfun->machine->is_OS_main)
{
rtx irqs_are_on = GEN_INT (!!cfun->machine->is_interrupt);
insn = emit_insn (gen_movhi_sp_r (stack_pointer_rtx,
fp, irqs_are_on));
}
else
{
insn = emit_move_insn (stack_pointer_rtx, fp);
}
if (!frame_pointer_needed)
RTX_FRAME_RELATED_P (insn) = 1;
fp_plus_insns = get_insns ();
end_sequence ();
/************ Method 2: Adjust Stack pointer ************/
/* 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;
start_sequence ();
insn = emit_move_insn (stack_pointer_rtx, sp_minus_size);
RTX_FRAME_RELATED_P (insn) = 1;
if (frame_pointer_needed)
{
insn = emit_move_insn (fp, stack_pointer_rtx);
RTX_FRAME_RELATED_P (insn) = 1;
}
sp_plus_insns = get_insns ();
end_sequence ();
/************ Use shortest method ************/
emit_insn (get_sequence_length (sp_plus_insns)
< get_sequence_length (fp_plus_insns)
? sp_plus_insns
: fp_plus_insns);
}
else
{
emit_insn (fp_plus_insns);
}
cfun->machine->stack_usage += size;
} /* !minimize && size != 0 */
} /* !minimize */
}
/* Output function prologue. */
void
expand_prologue (void)
{
int live_seq;
HARD_REG_SET set;
int minimize;
HOST_WIDE_INT size = get_frame_size();
rtx insn;
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);
......@@ -683,20 +950,13 @@ expand_prologue (void)
}
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_main
&& live_seq);
if (cfun->machine->is_interrupt || cfun->machine->is_signal)
{
/* Enable interrupts. */
if (cfun->machine->is_interrupt)
emit_insn (gen_enable_interrupt ());
emit_insn (gen_enable_interrupt ());
/* Push zero reg. */
emit_push_byte (ZERO_REGNO, true);
......@@ -715,189 +975,19 @@ expand_prologue (void)
&& 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);
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)
|| live_seq > 7))
{
int first_reg, reg, offset;
emit_move_insn (gen_rtx_REG (HImode, REG_X),
gen_int_mode (size, HImode));
insn = emit_insn (gen_call_prologue_saves
(gen_int_mode (live_seq, HImode),
gen_int_mode (size + live_seq, HImode)));
RTX_FRAME_RELATED_P (insn) = 1;
/* Describe the effect of the unspec_volatile call to prologue_saves.
Note that this formulation assumes that add_reg_note pushes the
notes to the front. Thus we build them in the reverse order of
how we want dwarf2out to process them. */
/* The function does always set frame_pointer_rtx, but whether that
is going to be permanent in the function is frame_pointer_needed. */
add_reg_note (insn, REG_CFA_ADJUST_CFA,
gen_rtx_SET (VOIDmode,
(frame_pointer_needed
? frame_pointer_rtx : stack_pointer_rtx),
plus_constant (stack_pointer_rtx,
-(size + live_seq))));
/* Note that live_seq always contains r28+r29, but the other
registers to be saved are all below 18. */
first_reg = 18 - (live_seq - 2);
for (reg = 29, offset = -live_seq + 1;
reg >= first_reg;
reg = (reg == 28 ? 17 : reg - 1), ++offset)
{
rtx m, r;
m = gen_rtx_MEM (QImode, plus_constant (stack_pointer_rtx, offset));
r = gen_rtx_REG (QImode, reg);
add_reg_note (insn, REG_CFA_OFFSET, gen_rtx_SET (VOIDmode, m, r));
}
cfun->machine->stack_usage += size + live_seq;
}
else
{
int reg;
for (reg = 0; reg < 32; ++reg)
if (TEST_HARD_REG_BIT (set, reg))
emit_push_byte (reg, true);
if (frame_pointer_needed)
{
if (!(cfun->machine->is_OS_task || cfun->machine->is_OS_main))
{
/* Push frame pointer. Always be consistent about the
ordering of pushes -- epilogue_restores expects the
register pair to be pushed low byte first. */
emit_push_byte (REG_Y, true);
emit_push_byte (REG_Y + 1, true);
}
if (!size)
{
insn = emit_move_insn (frame_pointer_rtx, stack_pointer_rtx);
RTX_FRAME_RELATED_P (insn) = 1;
}
else
{
/* Creating a frame can be done by direct manipulation of the
stack or via the frame pointer. These two methods are:
fp=sp
fp-=size
sp=fp
OR
sp-=size
fp=sp
the optimum method depends on function type, stack and frame size.
To avoid a complex logic, both methods are tested and shortest
is selected. */
rtx myfp;
rtx fp_plus_insns;
if (AVR_HAVE_8BIT_SP)
{
/* The high byte (r29) doesn't change. Prefer 'subi'
(1 cycle) over 'sbiw' (2 cycles, same size). */
myfp = gen_rtx_REG (QImode, FRAME_POINTER_REGNUM);
}
else
{
/* Normal sized addition. */
myfp = frame_pointer_rtx;
}
/* Method 1-Adjust frame pointer. */
start_sequence ();
/* Normally the dwarf2out frame-related-expr interpreter does
not expect to have the CFA change once the frame pointer is
set up. Thus we avoid marking the move insn below and
instead indicate that the entire operation is complete after
the frame pointer subtraction is done. */
emit_move_insn (frame_pointer_rtx, stack_pointer_rtx);
insn = emit_move_insn (myfp, plus_constant (myfp, -size));
RTX_FRAME_RELATED_P (insn) = 1;
add_reg_note (insn, REG_CFA_ADJUST_CFA,
gen_rtx_SET (VOIDmode, frame_pointer_rtx,
plus_constant (stack_pointer_rtx,
-size)));
/* Copy to stack pointer. Note that since we've already
changed the CFA to the frame pointer this operation
need not be annotated at all. */
if (AVR_HAVE_8BIT_SP)
{
emit_move_insn (stack_pointer_rtx, frame_pointer_rtx);
}
else if (TARGET_NO_INTERRUPTS
|| cfun->machine->is_signal
|| 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,
frame_pointer_rtx));
}
else
{
emit_move_insn (stack_pointer_rtx, frame_pointer_rtx);
}
fp_plus_insns = get_insns ();
end_sequence ();
/* Method 2-Adjust Stack pointer. */
if (size <= 6)
{
rtx sp_plus_insns;
start_sequence ();
insn = plus_constant (stack_pointer_rtx, -size);
insn = emit_move_insn (stack_pointer_rtx, insn);
RTX_FRAME_RELATED_P (insn) = 1;
insn = emit_move_insn (frame_pointer_rtx, stack_pointer_rtx);
RTX_FRAME_RELATED_P (insn) = 1;
sp_plus_insns = get_insns ();
end_sequence ();
/* Use shortest method. */
if (get_sequence_length (sp_plus_insns)
< get_sequence_length (fp_plus_insns))
emit_insn (sp_plus_insns);
else
emit_insn (fp_plus_insns);
}
else
emit_insn (fp_plus_insns);
cfun->machine->stack_usage += size;
}
}
}
avr_prologue_setup_frame (size, set);
if (flag_stack_usage_info)
current_function_static_stack_size = cfun->machine->stack_usage;
}
......@@ -924,6 +1014,11 @@ avr_asm_function_end_prologue (FILE *file)
else
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",
get_frame_size());
fprintf (file, "/* stack size = %d */\n",
......@@ -969,7 +1064,10 @@ expand_epilogue (bool sibcall_p)
int live_seq;
HARD_REG_SET set;
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 */
if (cfun->machine->is_naked)
......@@ -982,146 +1080,158 @@ expand_epilogue (bool sibcall_p)
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_main
&& live_seq);
&& live_seq
&& !isr_p
&& !cfun->machine->is_OS_task
&& !cfun->machine->is_OS_main);
if (minimize && (frame_pointer_needed || live_seq > 4))
if (minimize
&& (live_seq > 4
|| frame_pointer_needed
|| size))
{
if (frame_pointer_needed)
{
/* Get rid of frame. */
if (size)
emit_move_insn (frame_pointer_rtx,
gen_rtx_PLUS (HImode, frame_pointer_rtx,
gen_int_mode (size, HImode)));
}
else
{
/* Get rid of frame. */
if (!frame_pointer_needed)
{
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)));
return;
}
else
if (size)
{
if (frame_pointer_needed)
{
if (size)
{
/* Try two methods to adjust stack and select shortest. */
rtx myfp;
rtx fp_plus_insns;
/* Try two methods to adjust stack and select shortest. */
rtx fp, my_fp;
rtx fp_plus_insns;
if (AVR_HAVE_8BIT_SP)
{
/* The high byte (r29) doesn't change - prefer 'subi'
(1 cycle) over 'sbiw' (2 cycles, same size). */
myfp = gen_rtx_REG (QImode, FRAME_POINTER_REGNUM);
}
else
{
/* Normal sized addition. */
myfp = frame_pointer_rtx;
}
/* Method 1-Adjust frame pointer. */
start_sequence ();
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));
emit_move_insn (myfp, plus_constant (myfp, size));
if (AVR_HAVE_8BIT_SP)
{
/* The high byte (r29) does not change:
Prefer SUBI (1 cycle) over SBIW (2 cycles). */
my_fp = simplify_gen_subreg (QImode, fp, Pmode, 0);
}
/********** Method 1: Adjust fp register **********/
start_sequence ();
/* Copy to stack pointer. */
if (AVR_HAVE_8BIT_SP)
{
emit_move_insn (stack_pointer_rtx, frame_pointer_rtx);
}
else if (TARGET_NO_INTERRUPTS
|| cfun->machine->is_signal)
{
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,
frame_pointer_rtx));
}
else
{
emit_move_insn (stack_pointer_rtx, frame_pointer_rtx);
}
if (!frame_pointer_needed)
emit_move_insn (fp, stack_pointer_rtx);
fp_plus_insns = get_insns ();
end_sequence ();
emit_move_insn (my_fp, plus_constant (my_fp, size));
/* Method 2-Adjust Stack pointer. */
if (size <= 5)
{
rtx sp_plus_insns;
/* Copy to stack pointer. */
if (AVR_HAVE_8BIT_SP)
{
emit_move_insn (stack_pointer_rtx, fp);
}
else if (TARGET_NO_INTERRUPTS
|| isr_p
|| cfun->machine->is_OS_main)
{
rtx irqs_are_on = GEN_INT (!!cfun->machine->is_interrupt);
emit_insn (gen_movhi_sp_r (stack_pointer_rtx, fp, irqs_are_on));
}
else
{
emit_move_insn (stack_pointer_rtx, fp);
}
start_sequence ();
fp_plus_insns = get_insns ();
end_sequence ();
emit_move_insn (stack_pointer_rtx,
plus_constant (stack_pointer_rtx, size));
/********** Method 2: Adjust Stack pointer **********/
if (avr_sp_immediate_operand (gen_int_mode (size, HImode), HImode))
{
rtx sp_plus_insns;
sp_plus_insns = get_insns ();
end_sequence ();
start_sequence ();
/* Use shortest method. */
if (get_sequence_length (sp_plus_insns)
< get_sequence_length (fp_plus_insns))
emit_insn (sp_plus_insns);
else
emit_insn (fp_plus_insns);
}
else
emit_insn (fp_plus_insns);
}
if (!(cfun->machine->is_OS_task || cfun->machine->is_OS_main))
{
/* Restore previous frame_pointer. See expand_prologue for
rationale for not using pophi. */
emit_pop_byte (REG_Y + 1);
emit_pop_byte (REG_Y);
}
}
emit_move_insn (stack_pointer_rtx,
plus_constant (stack_pointer_rtx, size));
/* Restore used registers. */
for (reg = 31; reg >= 0; --reg)
if (TEST_HARD_REG_BIT (set, reg))
emit_pop_byte (reg);
sp_plus_insns = get_insns ();
end_sequence ();
if (cfun->machine->is_interrupt || cfun->machine->is_signal)
{
/* Restore RAMPZ using tmp reg as scratch. */
if (AVR_HAVE_RAMPZ
&& TEST_HARD_REG_BIT (set, REG_Z)
&& TEST_HARD_REG_BIT (set, REG_Z + 1))
{
emit_pop_byte (TMP_REGNO);
emit_move_insn (gen_rtx_MEM (QImode, GEN_INT (RAMPZ_ADDR)),
tmp_reg_rtx);
}
/************ Use shortest method ************/
emit_insn (get_sequence_length (sp_plus_insns)
< get_sequence_length (fp_plus_insns)
? sp_plus_insns
: fp_plus_insns);
}
else
emit_insn (fp_plus_insns);
} /* size != 0 */
if (frame_pointer_needed
&& !(cfun->machine->is_OS_task || cfun->machine->is_OS_main))
{
/* Restore previous frame_pointer. See expand_prologue for
rationale for not using pophi. */
emit_pop_byte (REG_Y + 1);
emit_pop_byte (REG_Y);
}
/* Restore SREG using tmp reg as scratch. */
emit_pop_byte (TMP_REGNO);
emit_move_insn (gen_rtx_MEM (QImode, GEN_INT (SREG_ADDR)),
tmp_reg_rtx);
/* Restore used registers. */
for (reg = 31; reg >= 0; --reg)
if (TEST_HARD_REG_BIT (set, reg))
emit_pop_byte (reg);
/* Restore tmp REG. */
if (isr_p)
{
/* Restore RAMPZ using tmp reg as scratch. */
if (AVR_HAVE_RAMPZ
&& TEST_HARD_REG_BIT (set, REG_Z)
&& TEST_HARD_REG_BIT (set, REG_Z + 1))
{
emit_pop_byte (TMP_REGNO);
/* Restore zero REG. */
emit_pop_byte (ZERO_REGNO);
emit_move_insn (gen_rtx_MEM (QImode, GEN_INT (RAMPZ_ADDR)),
tmp_reg_rtx);
}
if (!sibcall_p)
emit_jump_insn (gen_return ());
/* Restore SREG using tmp reg as scratch. */
emit_pop_byte (TMP_REGNO);
emit_move_insn (gen_rtx_MEM (QImode, GEN_INT (SREG_ADDR)),
tmp_reg_rtx);
/* Restore tmp REG. */
emit_pop_byte (TMP_REGNO);
/* Restore zero REG. */
emit_pop_byte (ZERO_REGNO);
}
if (!sibcall_p)
emit_jump_insn (gen_return ());
}
/* Output summary messages at beginning of function epilogue. */
......@@ -3069,8 +3179,10 @@ static bool
avr_frame_pointer_required_p (void)
{
return (cfun->calls_alloca
|| crtl->args.info.nregs == 0
|| get_frame_size () > 0);
|| cfun->calls_setjmp
|| cfun->has_nonlocal_label
|| crtl->args.info.nregs == 0
|| get_frame_size () > 0);
}
/* Returns the condition of compare insn INSN, or UNKNOWN. */
......
......@@ -324,7 +324,7 @@ enum reg_class {
#define STACK_GROWS_DOWNWARD
#define STARTING_FRAME_OFFSET 1
#define STARTING_FRAME_OFFSET avr_starting_frame_offset()
#define STACK_POINTER_OFFSET 1
......@@ -635,3 +635,5 @@ struct GTY(()) machine_function
/* AVR does not round pushes, but the existance of this macro is
required in order for pushes to be generated. */
#define PUSH_ROUNDING(X) (X)
#define ACCUMULATE_OUTGOING_ARGS avr_accumulate_outgoing_args()
......@@ -62,8 +62,7 @@
(define_c_enum "unspecv"
[UNSPECV_PROLOGUE_SAVES
UNSPECV_EPILOGUE_RESTORES
UNSPECV_WRITE_SP_IRQ_ON
UNSPECV_WRITE_SP_IRQ_OFF
UNSPECV_WRITE_SP
UNSPECV_GOTO_RECEIVER
UNSPECV_ENABLE_IRQS
UNSPECV_NOP
......@@ -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"
[(set (match_operand:HI 0 "stack_register_operand" "=q")
(unspec_volatile:HI [(match_operand:HI 1 "register_operand" "r")]
UNSPECV_WRITE_SP_IRQ_ON))]
;; Move register $1 to the Stack Pointer register SP.
;; This insn is emit during function prologue/epilogue generation.
;; $2 = 0: We know that IRQs are off
;; $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
sei
out __SP_L__, %A1"
[(set_attr "length" "4")
"@
out __SP_H__,%B1\;out __SP_L__,%A1
cli\;out __SP_H__,%B1\;sei\;out __SP_L__,%A1"
[(set_attr "length" "2,4")
(set_attr "cc" "none")])
(define_peephole2
......@@ -800,15 +797,15 @@
[(set_attr "length" "2")
(set_attr "cc" "set_n")])
(define_insn "*addhi3_sp_R"
[(set (match_operand:HI 1 "stack_register_operand" "=q")
(plus:HI (match_operand:HI 2 "stack_register_operand" "q")
(match_operand:HI 0 "avr_sp_immediate_operand" "R")))]
(define_insn "*addhi3_sp"
[(set (match_operand:HI 1 "stack_register_operand" "=q")
(plus:HI (match_operand:HI 2 "stack_register_operand" "q")
(match_operand:HI 0 "avr_sp_immediate_operand" "Csp")))]
""
{
return avr_out_addto_sp (operands, NULL);
}
[(set_attr "length" "5")
[(set_attr "length" "6")
(set_attr "adjust_len" "addto_sp")])
(define_insn "*addhi3"
......
......@@ -62,6 +62,10 @@ mpmem-wrap-around
Target Report
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
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.
......@@ -98,11 +98,6 @@
(and (match_code "const_double")
(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"
"A memory address based on Y or Z pointer with displacement."
(and (match_code "mem")
......@@ -162,3 +157,8 @@
"Constant 4-byte integer that allows XOR without clobber register."
(and (match_code "const_int")
(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 @@
;;
(define_predicate "avr_sp_immediate_operand"
(and (match_code "const_int")
(match_test "INTVAL (op) >= -6 && INTVAL (op) <= 5")))
(match_test "satisfies_constraint_Csp (op)")))
;; True for EQ & NE
(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