re PR target/50887 ([avr] Support ACCUMULATE_OUTGOING_ARGS)
authorGeorg-Johann Lay <avr@gjlay.de>
Sat, 29 Oct 2011 14:35:59 +0000 (14:35 +0000)
committerGeorg-Johann Lay <gjl@gcc.gnu.org>
Sat, 29 Oct 2011 14:35:59 +0000 (14:35 +0000)
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

gcc/ChangeLog
gcc/config/avr/avr-protos.h
gcc/config/avr/avr.c
gcc/config/avr/avr.h
gcc/config/avr/avr.md
gcc/config/avr/avr.opt
gcc/config/avr/constraints.md
gcc/config/avr/predicates.md

index be5a91a..f25b3f0 100644 (file)
@@ -1,3 +1,36 @@
+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.
index 817169e..b32e697 100644 (file)
@@ -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);
index 87ecdfe..187dcb1 100644 (file)
@@ -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.  */
index b9ce56f..50f6488 100644 (file)
@@ -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()
index 064db2c..faf1879 100644 (file)
@@ -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
     }
 }")
 
-(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
   [(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"
index 277b600..8de929d 100644 (file)
@@ -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.
index f02c197..d26bff3 100644 (file)
   (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")
   "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)")))
index 1881e8b..55a25b8 100644 (file)
 ;;
 (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"