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. */
......@@ -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])
|| (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)))))
{
......@@ -481,6 +524,7 @@ static bool
avr_can_eliminate (const int from, const int to)
{
return ((from == ARG_POINTER_REGNUM && to == FRAME_POINTER_REGNUM)
|| (frame_pointer_needed && to == FRAME_POINTER_REGNUM)
|| ((from == FRAME_POINTER_REGNUM
|| from == FRAME_POINTER_REGNUM + 1)
&& !frame_pointer_needed));
......@@ -499,7 +543,8 @@ avr_initial_elimination_offset (int from, int to)
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);
}
}
......@@ -547,6 +592,7 @@ avr_simple_epilogue (void)
{
return (! frame_pointer_needed
&& 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)
......@@ -656,87 +702,34 @@ emit_push_byte (unsigned regno, bool frame_related_p)
cfun->machine->stack_usage++;
}
/* Output function prologue. */
void
expand_prologue (void)
static void
avr_prologue_setup_frame (HOST_WIDE_INT size, HARD_REG_SET set)
{
int live_seq;
HARD_REG_SET set;
int minimize;
HOST_WIDE_INT size = get_frame_size();
rtx insn;
bool isr_p = cfun->machine->is_interrupt || cfun->machine->is_signal;
int live_seq = sequent_regs_live ();
/* 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);
live_seq = sequent_regs_live ();
minimize = (TARGET_CALL_PROLOGUES
&& !cfun->machine->is_interrupt
&& !cfun->machine->is_signal
bool minimize = (TARGET_CALL_PROLOGUES
&& live_seq
&& !isr_p
&& !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 ());
/* Push zero reg. */
emit_push_byte (ZERO_REGNO, true);
/* Push tmp reg. */
emit_push_byte (TMP_REGNO, true);
&& !cfun->machine->is_OS_main);
/* 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);
}
if (minimize && (frame_pointer_needed
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));
insn = emit_insn (gen_call_prologue_saves
(gen_int_mode (live_seq, HImode),
gen_int_mode (size + live_seq, 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.
......@@ -746,15 +739,17 @@ expand_prologue (void)
/* 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),
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;
......@@ -770,134 +765,229 @@ expand_prologue (void)
cfun->machine->stack_usage += size + live_seq;
}
else
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)
{
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)))
{
/* 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)
if (frame_pointer_needed
&& size == 0)
{
insn = emit_move_insn (frame_pointer_rtx, stack_pointer_rtx);
RTX_FRAME_RELATED_P (insn) = 1;
}
else
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. */
rtx myfp;
rtx fp_plus_insns;
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) 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;
/* 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. */
/************ Method 1: Adjust frame pointer ************/
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
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
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;
if (frame_pointer_needed)
{
add_reg_note (insn, REG_CFA_ADJUST_CFA,
gen_rtx_SET (VOIDmode, frame_pointer_rtx,
plus_constant (stack_pointer_rtx,
-size)));
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 at all. */
need not be annotated if frame pointer is needed. */
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
|| 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,
frame_pointer_rtx));
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
{
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 ();
end_sequence ();
/* Method 2-Adjust Stack pointer. */
if (size <= 6)
/************ 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 = plus_constant (stack_pointer_rtx, -size);
insn = emit_move_insn (stack_pointer_rtx, insn);
insn = emit_move_insn (stack_pointer_rtx, sp_minus_size);
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;
}
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);
/************ 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)
{
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)
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,83 +1080,92 @@ 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
&& live_seq
&& !isr_p
&& !cfun->machine->is_OS_task
&& !cfun->machine->is_OS_main
&& live_seq);
&& !cfun->machine->is_OS_main);
if (minimize && (frame_pointer_needed || live_seq > 4))
{
if (frame_pointer_needed)
if (minimize
&& (live_seq > 4
|| frame_pointer_needed
|| size))
{
/* 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
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 (frame_pointer_needed)
{
if (size)
{
/* Try two methods to adjust stack and select shortest. */
rtx myfp;
rtx fp, my_fp;
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)
{
/* 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;
/* 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 frame pointer. */
/********** Method 1: Adjust fp register **********/
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. */
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
|| cfun->machine->is_signal)
{
emit_insn (gen_movhi_sp_r_irq_off (stack_pointer_rtx,
frame_pointer_rtx));
}
else if (cfun->machine->is_interrupt)
|| isr_p
|| cfun->machine->is_OS_main)
{
emit_insn (gen_movhi_sp_r_irq_on (stack_pointer_rtx,
frame_pointer_rtx));
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, frame_pointer_rtx);
emit_move_insn (stack_pointer_rtx, fp);
}
fp_plus_insns = get_insns ();
end_sequence ();
/* Method 2-Adjust Stack pointer. */
if (size <= 5)
/********** Method 2: Adjust Stack pointer **********/
if (avr_sp_immediate_operand (gen_int_mode (size, HImode), HImode))
{
rtx sp_plus_insns;
......@@ -1070,33 +1177,37 @@ expand_epilogue (bool sibcall_p)
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);
/************ 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);
}
if (!(cfun->machine->is_OS_task || cfun->machine->is_OS_main))
} /* 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 used registers. */
for (reg = 31; reg >= 0; --reg)
if (TEST_HARD_REG_BIT (set, reg))
emit_pop_byte (reg);
if (cfun->machine->is_interrupt || cfun->machine->is_signal)
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))
......@@ -1107,8 +1218,8 @@ expand_epilogue (bool sibcall_p)
}
/* 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)),
tmp_reg_rtx);
......@@ -1121,7 +1232,6 @@ expand_epilogue (bool sibcall_p)
if (!sibcall_p)
emit_jump_insn (gen_return ());
}
}
/* Output summary messages at beginning of function epilogue. */
......@@ -3069,6 +3179,8 @@ static bool
avr_frame_pointer_required_p (void)
{
return (cfun->calls_alloca
|| cfun->calls_setjmp
|| cfun->has_nonlocal_label
|| crtl->args.info.nregs == 0
|| get_frame_size () > 0);
}
......
......@@ -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"
(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" "R")))]
(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