===================================================================
@@ -135,7 +135,7 @@ (define_predicate "single_zero_operand"
;;
(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"
===================================================================
@@ -62,8 +62,7 @@ (define_c_enum "unspec"
(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,16 @@ (define_expand "movhi"
}
}")
-(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))]
+(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))]
""
- "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))]
- ""
- "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 +789,15 @@ (define_insn "*addhi3_zero_extend1"
[(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
+
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.
===================================================================
@@ -32,6 +32,7 @@ extern int avr_initial_elimination_offse
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 operan
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);
===================================================================
@@ -98,11 +98,6 @@ (define_constraint "G"
(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 @@ (define_constraint "Cx4"
"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)")))
===================================================================
@@ -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 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,17 +702,238 @@ emit_push_byte (unsigned regno, bool fra
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,13 +950,6 @@ 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)
{
@@ -725,179 +985,9 @@ expand_prologue (void)
/* 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 *fil
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);
-
- if (minimize && (frame_pointer_needed || live_seq > 4))
- {
- 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
- {
+ && live_seq
+ && !isr_p
+ && !cfun->machine->is_OS_task
+ && !cfun->machine->is_OS_main);
+
+ if (minimize
+ && (live_seq > 4
+ || frame_pointer_needed
+ || size))
+ {
+ /* 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));
+
+ 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 ();
- emit_move_insn (myfp, plus_constant (myfp, size));
+ if (!frame_pointer_needed)
+ emit_move_insn (fp, stack_pointer_rtx);
- /* 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);
- }
+ emit_move_insn (my_fp, plus_constant (my_fp, size));
- fp_plus_insns = get_insns ();
- end_sequence ();
+ /* 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);
+ }
- /* Method 2-Adjust Stack pointer. */
- if (size <= 5)
- {
- rtx sp_plus_insns;
+ fp_plus_insns = get_insns ();
+ end_sequence ();
- start_sequence ();
+ /********** Method 2: Adjust Stack pointer **********/
+
+ if (avr_sp_immediate_operand (gen_int_mode (size, HImode), HImode))
+ {
+ rtx sp_plus_insns;
- emit_move_insn (stack_pointer_rtx,
- plus_constant (stack_pointer_rtx, size));
+ start_sequence ();
- 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);
- }
- 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()