OSDN Git Service

PR target/46779
[pf3gnuchains/gcc-fork.git] / gcc / config / avr / avr.c
index c55e432..5d1a127 100644 (file)
@@ -1,6 +1,6 @@
 /* Subroutines for insn-output.c for ATMEL AVR micro controllers
    Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004, 2005, 2006, 2007, 2008,
-   2009, 2010 Free Software Foundation, Inc.
+   2009, 2010, 2011 Free Software Foundation, Inc.
    Contributed by Denis Chertykov (chertykov@gmail.com)
 
    This file is part of GCC.
 #include "insn-config.h"
 #include "conditions.h"
 #include "insn-attr.h"
+#include "insn-codes.h"
 #include "flags.h"
 #include "reload.h"
 #include "tree.h"
 #include "output.h"
 #include "expr.h"
 #include "diagnostic-core.h"
-#include "toplev.h"
 #include "obstack.h"
 #include "function.h"
 #include "recog.h"
+#include "optabs.h"
 #include "ggc.h"
+#include "langhooks.h"
 #include "tm_p.h"
 #include "target.h"
 #include "target-def.h"
@@ -60,7 +62,7 @@ static int get_sequence_length (rtx insns);
 static int sequent_regs_live (void);
 static const char *ptrreg_to_str (int);
 static const char *cond_string (enum rtx_code);
-static int avr_num_arg_regs (enum machine_mode, tree);
+static int avr_num_arg_regs (enum machine_mode, const_tree);
 
 static RTX_CODE compare_condition (rtx insn);
 static rtx avr_legitimize_address (rtx, rtx, enum machine_mode);
@@ -74,7 +76,10 @@ static void avr_file_end (void);
 static bool avr_legitimate_address_p (enum machine_mode, rtx, bool);
 static void avr_asm_function_end_prologue (FILE *);
 static void avr_asm_function_begin_epilogue (FILE *);
+static bool avr_cannot_modify_jumps_p (void);
 static rtx avr_function_value (const_tree, const_tree, bool);
+static rtx avr_libcall_value (enum machine_mode, const_rtx);
+static bool avr_function_value_regno_p (const unsigned int);
 static void avr_insert_attributes (tree, tree *);
 static void avr_asm_init_sections (void);
 static unsigned int avr_section_type_flags (tree, const char *, int);
@@ -82,21 +87,28 @@ static unsigned int avr_section_type_flags (tree, const char *, int);
 static void avr_reorg (void);
 static void avr_asm_out_ctor (rtx, int);
 static void avr_asm_out_dtor (rtx, int);
+static int avr_register_move_cost (enum machine_mode, reg_class_t, reg_class_t);
+static int avr_memory_move_cost (enum machine_mode, reg_class_t, bool);
 static int avr_operand_rtx_cost (rtx, enum machine_mode, enum rtx_code, bool);
 static bool avr_rtx_costs (rtx, int, int, int *, bool);
 static int avr_address_cost (rtx, bool);
 static bool avr_return_in_memory (const_tree, const_tree);
 static struct machine_function * avr_init_machine_status (void);
+static void avr_init_builtins (void);
+static rtx avr_expand_builtin (tree, rtx, rtx, enum machine_mode, int);
 static rtx avr_builtin_setjmp_frame_value (void);
 static bool avr_hard_regno_scratch_ok (unsigned int);
 static unsigned int avr_case_values_threshold (void);
 static bool avr_frame_pointer_required_p (void);
 static bool avr_can_eliminate (const int, const int);
 static bool avr_class_likely_spilled_p (reg_class_t c);
-static rtx avr_function_arg (CUMULATIVE_ARGS *, enum machine_mode,
+static rtx avr_function_arg (cumulative_args_t , enum machine_mode,
                             const_tree, bool);
-static void avr_function_arg_advance (CUMULATIVE_ARGS *, enum machine_mode,
+static void avr_function_arg_advance (cumulative_args_t, enum machine_mode,
                                      const_tree, bool);
+static bool avr_function_ok_for_sibcall (tree, tree);
+static void avr_asm_named_section (const char *name, unsigned int flags, tree decl);
+static void avr_encode_section_info (tree, rtx, int);
 
 /* Allocate registers from r25 to r8 for parameters for function calls.  */
 #define FIRST_CUM_REG 26
@@ -121,17 +133,28 @@ const struct mcu_type_s *avr_current_device;
 
 section *progmem_section;
 
+/* To track if code will use .bss and/or .data.  */
+bool avr_need_clear_bss_p = false;
+bool avr_need_copy_data_p = false;
+
 /* AVR attributes.  */
 static const struct attribute_spec avr_attribute_table[] =
 {
-  /* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler } */
-  { "progmem",   0, 0, false, false, false,  avr_handle_progmem_attribute },
-  { "signal",    0, 0, true,  false, false,  avr_handle_fndecl_attribute },
-  { "interrupt", 0, 0, true,  false, false,  avr_handle_fndecl_attribute },
-  { "naked",     0, 0, false, true,  true,   avr_handle_fntype_attribute },
-  { "OS_task",   0, 0, false, true,  true,   avr_handle_fntype_attribute },
-  { "OS_main",   0, 0, false, true,  true,   avr_handle_fntype_attribute },
-  { NULL,        0, 0, false, false, false, NULL }
+  /* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler,
+       affects_type_identity } */
+  { "progmem",   0, 0, false, false, false,  avr_handle_progmem_attribute,
+    false },
+  { "signal",    0, 0, true,  false, false,  avr_handle_fndecl_attribute,
+    false },
+  { "interrupt", 0, 0, true,  false, false,  avr_handle_fndecl_attribute,
+    false },
+  { "naked",     0, 0, false, true,  true,   avr_handle_fntype_attribute,
+    false },
+  { "OS_task",   0, 0, false, true,  true,   avr_handle_fntype_attribute,
+    false },
+  { "OS_main",   0, 0, false, true,  true,   avr_handle_fntype_attribute,
+    false },
+  { NULL,        0, 0, false, false, false, NULL, false }
 };
 \f
 /* Initialize the GCC target structure.  */
@@ -147,8 +170,6 @@ static const struct attribute_spec avr_attribute_table[] =
 #define TARGET_ASM_INTEGER avr_assemble_integer
 #undef TARGET_ASM_FILE_START
 #define TARGET_ASM_FILE_START avr_file_start
-#undef TARGET_ASM_FILE_START_FILE_DIRECTIVE
-#define TARGET_ASM_FILE_START_FILE_DIRECTIVE true
 #undef TARGET_ASM_FILE_END
 #define TARGET_ASM_FILE_END avr_file_end
 
@@ -156,8 +177,14 @@ static const struct attribute_spec avr_attribute_table[] =
 #define TARGET_ASM_FUNCTION_END_PROLOGUE avr_asm_function_end_prologue
 #undef TARGET_ASM_FUNCTION_BEGIN_EPILOGUE
 #define TARGET_ASM_FUNCTION_BEGIN_EPILOGUE avr_asm_function_begin_epilogue
+
 #undef TARGET_FUNCTION_VALUE
 #define TARGET_FUNCTION_VALUE avr_function_value
+#undef TARGET_LIBCALL_VALUE
+#define TARGET_LIBCALL_VALUE avr_libcall_value
+#undef TARGET_FUNCTION_VALUE_REGNO_P
+#define TARGET_FUNCTION_VALUE_REGNO_P avr_function_value_regno_p
+
 #undef TARGET_ATTRIBUTE_TABLE
 #define TARGET_ATTRIBUTE_TABLE avr_attribute_table
 #undef TARGET_ASM_FUNCTION_RODATA_SECTION
@@ -166,6 +193,18 @@ static const struct attribute_spec avr_attribute_table[] =
 #define TARGET_INSERT_ATTRIBUTES avr_insert_attributes
 #undef TARGET_SECTION_TYPE_FLAGS
 #define TARGET_SECTION_TYPE_FLAGS avr_section_type_flags
+
+/* `TARGET_ASM_NAMED_SECTION' must be defined in avr.h.  */
+
+#undef TARGET_ASM_INIT_SECTIONS
+#define TARGET_ASM_INIT_SECTIONS avr_asm_init_sections
+#undef TARGET_ENCODE_SECTION_INFO
+#define TARGET_ENCODE_SECTION_INFO avr_encode_section_info
+
+#undef TARGET_REGISTER_MOVE_COST
+#define TARGET_REGISTER_MOVE_COST avr_register_move_cost
+#undef TARGET_MEMORY_MOVE_COST
+#define TARGET_MEMORY_MOVE_COST avr_memory_move_cost
 #undef TARGET_RTX_COSTS
 #define TARGET_RTX_COSTS avr_rtx_costs
 #undef TARGET_ADDRESS_COST
@@ -208,28 +247,27 @@ static const struct attribute_spec avr_attribute_table[] =
 #undef TARGET_OPTION_OVERRIDE
 #define TARGET_OPTION_OVERRIDE avr_option_override
 
+#undef TARGET_CANNOT_MODIFY_JUMPS_P
+#define TARGET_CANNOT_MODIFY_JUMPS_P avr_cannot_modify_jumps_p
+
+#undef TARGET_FUNCTION_OK_FOR_SIBCALL
+#define TARGET_FUNCTION_OK_FOR_SIBCALL avr_function_ok_for_sibcall
+
+#undef TARGET_INIT_BUILTINS
+#define TARGET_INIT_BUILTINS avr_init_builtins
+
+#undef TARGET_EXPAND_BUILTIN
+#define TARGET_EXPAND_BUILTIN avr_expand_builtin
+
+
 struct gcc_target targetm = TARGET_INITIALIZER;
 \f
 static void
 avr_option_override (void)
 {
-  const struct mcu_type_s *t;
-
   flag_delete_null_pointer_checks = 0;
 
-  for (t = avr_mcu_types; t->name; t++)
-    if (strcmp (t->name, avr_mcu_name) == 0)
-      break;
-
-  if (!t->name)
-    {
-      fprintf (stderr, "unknown MCU '%s' specified\nKnown MCU names:\n",
-              avr_mcu_name);
-      for (t = avr_mcu_types; t->name; t++)
-       fprintf (stderr,"   %s\n", t->name);
-    }
-
-  avr_current_device = t;
+  avr_current_device = &avr_mcu_types[avr_mcu_index];
   avr_current_arch = &avr_arch_types[avr_current_device->arch];
   avr_extra_arch_macro = avr_current_device->macro;
 
@@ -273,17 +311,34 @@ avr_regno_reg_class (int r)
   return ALL_REGS;
 }
 
+/* A helper for the subsequent function attribute used to dig for
+   attribute 'name' in a FUNCTION_DECL or FUNCTION_TYPE */
+
+static inline int
+avr_lookup_function_attribute1 (const_tree func, const char *name)
+{
+  if (FUNCTION_DECL == TREE_CODE (func))
+    {
+      if (NULL_TREE != lookup_attribute (name, DECL_ATTRIBUTES (func)))
+        {
+          return true;
+        }
+      
+      func = TREE_TYPE (func);
+    }
+
+  gcc_assert (TREE_CODE (func) == FUNCTION_TYPE
+              || TREE_CODE (func) == METHOD_TYPE);
+  
+  return NULL_TREE != lookup_attribute (name, TYPE_ATTRIBUTES (func));
+}
+
 /* Return nonzero if FUNC is a naked function.  */
 
 static int
 avr_naked_function_p (tree func)
 {
-  tree a;
-
-  gcc_assert (TREE_CODE (func) == FUNCTION_DECL);
-  
-  a = lookup_attribute ("naked", TYPE_ATTRIBUTES (TREE_TYPE (func)));
-  return a != NULL_TREE;
+  return avr_lookup_function_attribute1 (func, "naked");
 }
 
 /* Return nonzero if FUNC is an interrupt function as specified
@@ -292,13 +347,7 @@ avr_naked_function_p (tree func)
 static int
 interrupt_function_p (tree func)
 {
-  tree a;
-
-  if (TREE_CODE (func) != FUNCTION_DECL)
-    return 0;
-
-  a = lookup_attribute ("interrupt", DECL_ATTRIBUTES (func));
-  return a != NULL_TREE;
+  return avr_lookup_function_attribute1 (func, "interrupt");
 }
 
 /* Return nonzero if FUNC is a signal function as specified
@@ -307,13 +356,7 @@ interrupt_function_p (tree func)
 static int
 signal_function_p (tree func)
 {
-  tree a;
-
-  if (TREE_CODE (func) != FUNCTION_DECL)
-    return 0;
-
-  a = lookup_attribute ("signal", DECL_ATTRIBUTES (func));
-  return a != NULL_TREE;
+  return avr_lookup_function_attribute1 (func, "signal");
 }
 
 /* Return nonzero if FUNC is a OS_task function.  */
@@ -321,12 +364,7 @@ signal_function_p (tree func)
 static int
 avr_OS_task_function_p (tree func)
 {
-  tree a;
-
-  gcc_assert (TREE_CODE (func) == FUNCTION_DECL);
-  
-  a = lookup_attribute ("OS_task", TYPE_ATTRIBUTES (TREE_TYPE (func)));
-  return a != NULL_TREE;
+  return avr_lookup_function_attribute1 (func, "OS_task");
 }
 
 /* Return nonzero if FUNC is a OS_main function.  */
@@ -334,12 +372,7 @@ avr_OS_task_function_p (tree func)
 static int
 avr_OS_main_function_p (tree func)
 {
-  tree a;
-
-  gcc_assert (TREE_CODE (func) == FUNCTION_DECL);
-  
-  a = lookup_attribute ("OS_main", TYPE_ATTRIBUTES (TREE_TYPE (func)));
-  return a != NULL_TREE;
+  return avr_lookup_function_attribute1 (func, "OS_main");
 }
 
 /* Return the number of hard registers to push/pop in the prologue/epilogue
@@ -426,7 +459,7 @@ rtx avr_builtin_setjmp_frame_value (void)
 /* Return contents of MEM at frame pointer + stack size + 1 (+2 if 3 byte PC).
    This is return address of function.  */
 rtx 
-avr_return_addr_rtx (int count, const_rtx tem)
+avr_return_addr_rtx (int count, rtx tem)
 {
   rtx r;
     
@@ -525,6 +558,35 @@ get_sequence_length (rtx insns)
   return length;
 }
 
+/*  Implement INCOMING_RETURN_ADDR_RTX.  */
+
+rtx
+avr_incoming_return_addr_rtx (void)
+{
+  /* The return address is at the top of the stack.  Note that the push
+     was via post-decrement, which means the actual address is off by one.  */
+  return gen_frame_mem (HImode, plus_constant (stack_pointer_rtx, 1));
+}
+
+/*  Helper for expand_prologue.  Emit a push of a byte register.  */
+
+static void
+emit_push_byte (unsigned regno, bool frame_related_p)
+{
+  rtx mem, reg, insn;
+
+  mem = gen_rtx_POST_DEC (HImode, stack_pointer_rtx);
+  mem = gen_frame_mem (QImode, mem);
+  reg = gen_rtx_REG (QImode, regno);
+
+  insn = emit_insn (gen_rtx_SET (VOIDmode, mem, reg));
+  if (frame_related_p)
+    RTX_FRAME_RELATED_P (insn) = 1;
+
+  cfun->machine->stack_usage++;
+}
+
+
 /*  Output function prologue.  */
 
 void
@@ -534,11 +596,6 @@ expand_prologue (void)
   HARD_REG_SET set;
   int minimize;
   HOST_WIDE_INT size = get_frame_size();
-  /* Define templates for push instructions.  */
-  rtx pushbyte = gen_rtx_MEM (QImode,
-                  gen_rtx_POST_DEC (HImode, stack_pointer_rtx));
-  rtx pushword = gen_rtx_MEM (HImode,
-                  gen_rtx_POST_DEC (HImode, stack_pointer_rtx));
   rtx insn;
   
   /* Init cfun->machine.  */
@@ -566,46 +623,34 @@ expand_prologue (void)
 
   if (cfun->machine->is_interrupt || cfun->machine->is_signal)
     {
+      /* Enable interrupts.  */
       if (cfun->machine->is_interrupt)
-        {
-          /* Enable interrupts.  */
-          insn = emit_insn (gen_enable_interrupt ());
-          RTX_FRAME_RELATED_P (insn) = 1;
-        }
+       emit_insn (gen_enable_interrupt ());
        
       /* Push zero reg.  */
-      insn = emit_move_insn (pushbyte, zero_reg_rtx);
-      RTX_FRAME_RELATED_P (insn) = 1;
-      cfun->machine->stack_usage++;
+      emit_push_byte (ZERO_REGNO, true);
 
       /* Push tmp reg.  */
-      insn = emit_move_insn (pushbyte, tmp_reg_rtx);
-      RTX_FRAME_RELATED_P (insn) = 1;
-      cfun->machine->stack_usage++;
+      emit_push_byte (TMP_REGNO, true);
 
       /* Push SREG.  */
-      insn = emit_move_insn (tmp_reg_rtx, 
-                             gen_rtx_MEM (QImode, GEN_INT (SREG_ADDR)));
-      RTX_FRAME_RELATED_P (insn) = 1;
-      insn = emit_move_insn (pushbyte, tmp_reg_rtx);
-      RTX_FRAME_RELATED_P (insn) = 1;
-      cfun->machine->stack_usage++;
+      /* ??? 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.  */
-      if(AVR_HAVE_RAMPZ 
-         && (TEST_HARD_REG_BIT (set, REG_Z) && TEST_HARD_REG_BIT (set, REG_Z + 1)))
+      /* ??? 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))
         {
-          insn = emit_move_insn (tmp_reg_rtx, 
-                                 gen_rtx_MEM (QImode, GEN_INT (RAMPZ_ADDR)));
-          RTX_FRAME_RELATED_P (insn) = 1;
-          insn = emit_move_insn (pushbyte, tmp_reg_rtx);
-          RTX_FRAME_RELATED_P (insn) = 1;
-          cfun->machine->stack_usage++;
+          emit_move_insn (tmp_reg_rtx,
+                         gen_rtx_MEM (QImode, GEN_INT (RAMPZ_ADDR)));
+         emit_push_byte (TMP_REGNO, false);
         }
        
       /* Clear zero reg.  */
-      insn = emit_move_insn (zero_reg_rtx, const0_rtx);
-      RTX_FRAME_RELATED_P (insn) = 1;
+      emit_move_insn (zero_reg_rtx, const0_rtx);
 
       /* Prevent any attempt to delete the setting of ZERO_REG!  */
       emit_use (zero_reg_rtx);
@@ -614,37 +659,63 @@ expand_prologue (void)
                   || (AVR_2_BYTE_PC && live_seq > 6)
                   || live_seq > 7)) 
     {
-      insn = emit_move_insn (gen_rtx_REG (HImode, REG_X), 
-                             gen_int_mode (size, HImode));
-      RTX_FRAME_RELATED_P (insn) = 1;
+      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)));
+      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 of register to save.  */
-              insn=emit_move_insn (pushbyte, gen_rtx_REG (QImode, reg));
-              RTX_FRAME_RELATED_P (insn) = 1;
-              cfun->machine->stack_usage++;
-            }
-        }
+        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.  */
-             insn = emit_move_insn (pushword, frame_pointer_rtx);
-              RTX_FRAME_RELATED_P (insn) = 1;
-             cfun->machine->stack_usage += 2;
+              /* 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)
@@ -667,13 +738,12 @@ expand_prologue (void)
               is selected.  */
               rtx myfp;
              rtx fp_plus_insns; 
-             rtx sp_plus_insns = NULL_RTX;
 
               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, REGNO (frame_pointer_rtx));
+                  /* 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 
                 {
@@ -684,41 +754,43 @@ expand_prologue (void)
              /* Method 1-Adjust frame pointer.  */
              start_sequence ();
 
-              insn = emit_move_insn (frame_pointer_rtx, stack_pointer_rtx);
-              RTX_FRAME_RELATED_P (insn) = 1;
+             /* 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 (myfp,
-                               gen_rtx_PLUS (GET_MODE(myfp), myfp, 
-                                             gen_int_mode (-size, 
-                                                           GET_MODE(myfp))));
-              RTX_FRAME_RELATED_P (insn) = 1;
+              emit_move_insn (frame_pointer_rtx, stack_pointer_rtx);
 
-             /* Copy to stack pointer.  */
+              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)
                {
-                 insn = emit_move_insn (stack_pointer_rtx, frame_pointer_rtx);
-                 RTX_FRAME_RELATED_P (insn) = 1;
+                 emit_move_insn (stack_pointer_rtx, frame_pointer_rtx);
                }
              else if (TARGET_NO_INTERRUPTS 
                       || cfun->machine->is_signal
                       || cfun->machine->is_OS_main)
                {
-                 insn = 
-                   emit_insn (gen_movhi_sp_r_irq_off (stack_pointer_rtx, 
-                                                      frame_pointer_rtx));
-                 RTX_FRAME_RELATED_P (insn) = 1;               
+                 emit_insn (gen_movhi_sp_r_irq_off (stack_pointer_rtx, 
+                                                    frame_pointer_rtx));
                }
              else if (cfun->machine->is_interrupt)
                {
-                 insn = emit_insn (gen_movhi_sp_r_irq_on (stack_pointer_rtx, 
-                                                          frame_pointer_rtx));
-                 RTX_FRAME_RELATED_P (insn) = 1;
+                 emit_insn (gen_movhi_sp_r_irq_on (stack_pointer_rtx, 
+                                                   frame_pointer_rtx));
                }
              else
                {
-                 insn = emit_move_insn (stack_pointer_rtx, frame_pointer_rtx);
-                 RTX_FRAME_RELATED_P (insn) = 1;
+                 emit_move_insn (stack_pointer_rtx, frame_pointer_rtx);
                }
 
              fp_plus_insns = get_insns ();
@@ -727,36 +799,36 @@ expand_prologue (void)
              /* Method 2-Adjust Stack pointer.  */
               if (size <= 6)
                 {
+                 rtx sp_plus_insns;
+
                  start_sequence ();
 
-                 insn = 
-                   emit_move_insn (stack_pointer_rtx,
-                                   gen_rtx_PLUS (HImode, 
-                                                 stack_pointer_rtx, 
-                                                 gen_int_mode (-size, 
-                                                               HImode)));
+                 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);
+                 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 (size <= 6 && (get_sequence_length (sp_plus_insns) 
-                                < get_sequence_length (fp_plus_insns)))
-               emit_insn (sp_plus_insns);
-              else
+                 /* 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;
             }
         }
     }
 
-  if (flag_stack_usage)
+  if (flag_stack_usage_info)
     current_function_static_stack_size = cfun->machine->stack_usage;
 }
 
@@ -804,10 +876,24 @@ avr_epilogue_uses (int regno ATTRIBUTE_UNUSED)
   return 0;
 }
 
+/*  Helper for expand_epilogue.  Emit a pop of a byte register.  */
+
+static void
+emit_pop_byte (unsigned regno)
+{
+  rtx mem, reg;
+
+  mem = gen_rtx_PRE_INC (HImode, stack_pointer_rtx);
+  mem = gen_frame_mem (QImode, mem);
+  reg = gen_rtx_REG (QImode, regno);
+
+  emit_insn (gen_rtx_SET (VOIDmode, reg, mem));
+}
+
 /*  Output RTL epilogue.  */
 
 void
-expand_epilogue (void)
+expand_epilogue (bool sibcall_p)
 {
   int reg;
   int live_seq;
@@ -818,6 +904,8 @@ expand_epilogue (void)
   /* epilogue: naked  */
   if (cfun->machine->is_naked)
     {
+      gcc_assert (!sibcall_p);
+      
       emit_jump_insn (gen_return ());
       return;
     }
@@ -856,13 +944,12 @@ expand_epilogue (void)
               /* Try two methods to adjust stack and select shortest.  */
              rtx myfp;
              rtx fp_plus_insns;
-             rtx sp_plus_insns = NULL_RTX;
-             
+
              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, REGNO (frame_pointer_rtx));
+                  myfp = gen_rtx_REG (QImode, FRAME_POINTER_REGNUM);
                 }
               else 
                 {
@@ -873,10 +960,7 @@ expand_epilogue (void)
               /* Method 1-Adjust frame pointer.  */
              start_sequence ();
 
-             emit_move_insn (myfp,
-                             gen_rtx_PLUS (GET_MODE (myfp), myfp,
-                                           gen_int_mode (size, 
-                                                         GET_MODE(myfp))));
+             emit_move_insn (myfp, plus_constant (myfp, size));
 
              /* Copy to stack pointer.  */
              if (AVR_HAVE_8BIT_SP)
@@ -905,61 +989,67 @@ expand_epilogue (void)
               /* Method 2-Adjust Stack pointer.  */
               if (size <= 5)
                 {
+                 rtx sp_plus_insns;
+
                  start_sequence ();
 
                  emit_move_insn (stack_pointer_rtx,
-                                 gen_rtx_PLUS (HImode, stack_pointer_rtx,
-                                               gen_int_mode (size, 
-                                                             HImode)));
+                                 plus_constant (stack_pointer_rtx, size));
 
                  sp_plus_insns = get_insns ();
                  end_sequence ();
-                }
 
-              /* Use shortest method.  */
-              if (size <= 5 && (get_sequence_length (sp_plus_insns) 
-                                < get_sequence_length (fp_plus_insns)))
-               emit_insn (sp_plus_insns);
-              else
+                 /* 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.  */
-             emit_insn (gen_pophi (frame_pointer_rtx));
+              /* 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_insn (gen_popqi (gen_rtx_REG (QImode, reg)));
-        }
+        if (TEST_HARD_REG_BIT (set, reg))
+          emit_pop_byte (reg);
+
       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)))
+         if (AVR_HAVE_RAMPZ 
+              && TEST_HARD_REG_BIT (set, REG_Z)
+             && TEST_HARD_REG_BIT (set, REG_Z + 1))
             {
-             emit_insn (gen_popqi (tmp_reg_rtx));
-             emit_move_insn (gen_rtx_MEM(QImode, GEN_INT(RAMPZ_ADDR)), 
+             emit_pop_byte (TMP_REGNO);
+             emit_move_insn (gen_rtx_MEM (QImode, GEN_INT (RAMPZ_ADDR)), 
                              tmp_reg_rtx);
            }
 
           /* Restore SREG using tmp reg as scratch.  */
-          emit_insn (gen_popqi (tmp_reg_rtx));
+          emit_pop_byte (TMP_REGNO);
       
-          emit_move_insn (gen_rtx_MEM(QImode, GEN_INT(SREG_ADDR)), 
+          emit_move_insn (gen_rtx_MEM (QImode, GEN_INT (SREG_ADDR)), 
                          tmp_reg_rtx);
 
           /* Restore tmp REG.  */
-          emit_insn (gen_popqi (tmp_reg_rtx));
+          emit_pop_byte (TMP_REGNO);
 
           /* Restore zero REG.  */
-          emit_insn (gen_popqi (zero_reg_rtx));
+          emit_pop_byte (ZERO_REGNO);
         }
 
-      emit_jump_insn (gen_return ());
+      if (!sibcall_p)
+        emit_jump_insn (gen_return ());
     }
 }
 
@@ -971,6 +1061,27 @@ avr_asm_function_begin_epilogue (FILE *file)
   fprintf (file, "/* epilogue start */\n");
 }
 
+
+/* Implement TARGET_CANNOT_MODITY_JUMPS_P */
+
+static bool
+avr_cannot_modify_jumps_p (void)
+{
+
+  /* Naked Functions must not have any instructions after
+     their epilogue, see PR42240 */
+     
+  if (reload_completed
+      && cfun->machine
+      && cfun->machine->is_naked)
+    {
+      return true;
+    }
+
+  return false;
+}
+
+
 /* Return nonzero if X (an RTX) is a legitimate memory address on the target
    machine for a memory operand of mode MODE.  */
 
@@ -998,8 +1109,7 @@ avr_legitimate_address_p (enum machine_mode mode, rtx x, bool strict)
                 true_regnum (XEXP (x, 0)));
       debug_rtx (x);
     }
-  if (!strict && GET_CODE (x) == SUBREG)
-       x = SUBREG_REG (x);
+  
   if (REG_P (x) && (strict ? REG_OK_FOR_BASE_STRICT_P (x)
                     : REG_OK_FOR_BASE_NOSTRICT_P (x)))
     r = POINTER_REGS;
@@ -1073,6 +1183,32 @@ avr_legitimize_address (rtx x, rtx oldx, enum machine_mode mode)
 }
 
 
+/* Helper function to print assembler resp. track instruction
+   sequence lengths.
+   
+   If PLEN == NULL:
+       Output assembler code from template TPL with operands supplied
+       by OPERANDS.  This is just forwarding to output_asm_insn.
+   
+   If PLEN != NULL:
+       Add N_WORDS to *PLEN.
+       Don't output anything.
+*/
+
+static void
+avr_asm_len (const char* tpl, rtx* operands, int* plen, int n_words)
+{
+  if (NULL == plen)
+    {
+      output_asm_insn (tpl, operands);
+    }
+  else
+    {
+      *plen += n_words;
+    }
+}
+
+
 /* Return a pointer register name as a string.  */
 
 static const char *
@@ -1143,7 +1279,9 @@ print_operand_address (FILE *file, rtx addr)
       if (CONSTANT_ADDRESS_P (addr)
          && text_segment_operand (addr, VOIDmode))
        {
-         rtx x = XEXP (addr,0);
+         rtx x = addr;
+         if (GET_CODE (x) == CONST)
+           x = XEXP (x, 0);
          if (GET_CODE (x) == PLUS && GET_CODE (XEXP (x,1)) == CONST_INT)
            {
              /* Assembler gs() will implant word address. Make offset 
@@ -1156,7 +1294,7 @@ print_operand_address (FILE *file, rtx addr)
              output_addr_const (file, XEXP (x,0));
              fprintf (file,"+" HOST_WIDE_INT_PRINT_DEC ")", 2 * INTVAL (XEXP (x,1)));
              if (AVR_3_BYTE_PC)
-               if (warning ( 0, "Pointer offset from symbol maybe incorrect."))
+               if (warning (0, "pointer offset from symbol maybe incorrect"))
                  {
                    output_addr_const (stderr, addr);
                    fprintf(stderr,"\n");
@@ -1551,12 +1689,16 @@ init_cumulative_args (CUMULATIVE_ARGS *cum, tree fntype, rtx libname,
   cum->regno = FIRST_CUM_REG;
   if (!libname && stdarg_p (fntype))
     cum->nregs = 0;
+
+  /* Assume the calle may be tail called */
+  
+  cfun->machine->sibcall_fails = 0;
 }
 
 /* Returns the number of registers to allocate for a function argument.  */
 
 static int
-avr_num_arg_regs (enum machine_mode mode, tree type)
+avr_num_arg_regs (enum machine_mode mode, const_tree type)
 {
   int size;
 
@@ -1575,9 +1717,10 @@ avr_num_arg_regs (enum machine_mode mode, tree type)
    in a register, and which register.  */
 
 static rtx
-avr_function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode,
+avr_function_arg (cumulative_args_t cum_v, enum machine_mode mode,
                  const_tree type, bool named ATTRIBUTE_UNUSED)
 {
+  CUMULATIVE_ARGS *cum = get_cumulative_args (cum_v);
   int bytes = avr_num_arg_regs (mode, type);
 
   if (cum->nregs && bytes <= cum->nregs)
@@ -1590,14 +1733,48 @@ avr_function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode,
    in the argument list.  */
    
 static void
-avr_function_arg_advance (CUMULATIVE_ARGS *cum, enum machine_mode mode,
+avr_function_arg_advance (cumulative_args_t cum_v, enum machine_mode mode,
                          const_tree type, bool named ATTRIBUTE_UNUSED)
 {
+  CUMULATIVE_ARGS *cum = get_cumulative_args (cum_v);
   int bytes = avr_num_arg_regs (mode, type);
 
   cum->nregs -= bytes;
   cum->regno -= bytes;
 
+  /* A parameter is being passed in a call-saved register. As the original
+     contents of these regs has to be restored before leaving the function,
+     a function must not pass arguments in call-saved regs in order to get
+     tail-called. */
+  
+  if (cum->regno >= 8
+      && cum->nregs >= 0
+      && !call_used_regs[cum->regno])
+    {
+      /* FIXME: We ship info on failing tail-call in struct machine_function.
+         This uses internals of calls.c:expand_call() and the way args_so_far
+         is used. targetm.function_ok_for_sibcall() needs to be extended to
+         pass &args_so_far, too. At present, CUMULATIVE_ARGS is target
+         dependent so that such an extension is not wanted. */
+      
+      cfun->machine->sibcall_fails = 1;
+    }
+
+  /* Test if all registers needed by the ABI are actually available.  If the
+     user has fixed a GPR needed to pass an argument, an (implicit) function
+     call would clobber that fixed register.  See PR45099 for an example.  */
+  
+  if (cum->regno >= 8
+      && cum->nregs >= 0)
+    {
+      int regno;
+
+      for (regno = cum->regno; regno < cum->regno + bytes; regno++)
+        if (fixed_regs[regno])
+          error ("Register %s is needed to pass a parameter but is fixed",
+                 reg_names[regno]);
+    }
+      
   if (cum->nregs <= 0)
     {
       cum->nregs = 0;
@@ -1605,6 +1782,65 @@ avr_function_arg_advance (CUMULATIVE_ARGS *cum, enum machine_mode mode,
     }
 }
 
+/* Implement `TARGET_FUNCTION_OK_FOR_SIBCALL' */
+/* Decide whether we can make a sibling call to a function.  DECL is the
+   declaration of the function being targeted by the call and EXP is the
+   CALL_EXPR representing the call. */
+
+static bool
+avr_function_ok_for_sibcall (tree decl_callee, tree exp_callee)
+{
+  tree fntype_callee;
+
+  /* Tail-calling must fail if callee-saved regs are used to pass
+     function args.  We must not tail-call when `epilogue_restores'
+     is used.  Unfortunately, we cannot tell at this point if that
+     actually will happen or not, and we cannot step back from
+     tail-calling. Thus, we inhibit tail-calling with -mcall-prologues. */
+  
+  if (cfun->machine->sibcall_fails
+      || TARGET_CALL_PROLOGUES)
+    {
+      return false;
+    }
+  
+  fntype_callee = TREE_TYPE (CALL_EXPR_FN (exp_callee));
+
+  if (decl_callee)
+    {
+      decl_callee = TREE_TYPE (decl_callee);
+    }
+  else
+    {
+      decl_callee = fntype_callee;
+      
+      while (FUNCTION_TYPE != TREE_CODE (decl_callee)
+             && METHOD_TYPE != TREE_CODE (decl_callee))
+        {
+          decl_callee = TREE_TYPE (decl_callee);
+        }
+    }
+
+  /* Ensure that caller and callee have compatible epilogues */
+  
+  if (interrupt_function_p (current_function_decl)
+      || signal_function_p (current_function_decl)
+      || avr_naked_function_p (decl_callee)
+      || avr_naked_function_p (current_function_decl)
+      /* FIXME: For OS_task and OS_main, we are over-conservative.
+         This is due to missing documentation of these attributes
+         and what they actually should do and should not do. */
+      || (avr_OS_task_function_p (decl_callee)
+          != avr_OS_task_function_p (current_function_decl))
+      || (avr_OS_main_function_p (decl_callee)
+          != avr_OS_main_function_p (current_function_decl)))
+    {
+      return false;
+    }
+  return true;
+}
+
 /***********************************************************************
   Functions for outputting various mov's for a various modes
 ************************************************************************/
@@ -2389,7 +2625,7 @@ out_movsi_mr_r (rtx insn, rtx op[], int *l)
 }
 
 const char *
-output_movsisf(rtx insn, rtx operands[], int *l)
+output_movsisf (rtx insn, rtx operands[], rtx clobber_reg, int *l)
 {
   int dummy;
   rtx dest = operands[0];
@@ -2432,6 +2668,11 @@ output_movsisf(rtx insn, rtx operands[], int *l)
                      AS2 (mov,%D0,%D1));
            }
        }
+      else if (CONST_INT_P (src)
+               || CONST_DOUBLE_P (src))
+        {
+          return output_reload_insisf (insn, operands, clobber_reg, real_l);
+        }
       else if (CONSTANT_P (src))
        {
          if (test_hard_reg_class (LD_REGS, dest)) /* ldi d,i */
@@ -2442,68 +2683,6 @@ output_movsisf(rtx insn, rtx operands[], int *l)
                      AS2 (ldi,%C0,hlo8(%1)) CR_TAB
                      AS2 (ldi,%D0,hhi8(%1)));
            }
-         
-         if (GET_CODE (src) == CONST_INT)
-           {
-             const char *const clr_op0 =
-               AVR_HAVE_MOVW ? (AS1 (clr,%A0) CR_TAB
-                               AS1 (clr,%B0) CR_TAB
-                               AS2 (movw,%C0,%A0))
-                            : (AS1 (clr,%A0) CR_TAB
-                               AS1 (clr,%B0) CR_TAB
-                               AS1 (clr,%C0) CR_TAB
-                               AS1 (clr,%D0));
-
-             if (src == const0_rtx) /* mov r,L */
-               {
-                 *l = AVR_HAVE_MOVW ? 3 : 4;
-                 return clr_op0;
-               }
-             else if (src == const1_rtx)
-               {
-                 if (!real_l)
-                   output_asm_insn (clr_op0, operands);
-                 *l = AVR_HAVE_MOVW ? 4 : 5;
-                 return AS1 (inc,%A0);
-               }
-             else if (src == constm1_rtx)
-               {
-                 /* Immediate constants -1 to any register */
-                 if (AVR_HAVE_MOVW)
-                   {
-                     *l = 4;
-                     return (AS1 (clr,%A0)     CR_TAB
-                             AS1 (dec,%A0)     CR_TAB
-                             AS2 (mov,%B0,%A0) CR_TAB
-                             AS2 (movw,%C0,%A0));
-                   }
-                 *l = 5;
-                 return (AS1 (clr,%A0)     CR_TAB
-                         AS1 (dec,%A0)     CR_TAB
-                         AS2 (mov,%B0,%A0) CR_TAB
-                         AS2 (mov,%C0,%A0) CR_TAB
-                         AS2 (mov,%D0,%A0));
-               }
-             else
-               {
-                 int bit_nr = exact_log2 (INTVAL (src));
-
-                 if (bit_nr >= 0)
-                   {
-                     *l = AVR_HAVE_MOVW ? 5 : 6;
-                     if (!real_l)
-                       {
-                         output_asm_insn (clr_op0, operands);
-                         output_asm_insn ("set", operands);
-                       }
-                     if (!real_l)
-                       avr_output_bld (operands, bit_nr);
-
-                     return "";
-                   }
-               }
-           }
-         
          /* Last resort, better than loading from memory.  */
          *l = 10;
          return (AS2 (mov,__tmp_reg__,r31) CR_TAB
@@ -2524,7 +2703,7 @@ output_movsisf(rtx insn, rtx operands[], int *l)
     {
       const char *templ;
 
-      if (src == const0_rtx)
+      if (src == CONST0_RTX (GET_MODE (dest)))
          operands[1] = zero_reg_rtx;
 
       templ = out_movsi_mr_r (insn, operands, real_l);
@@ -4246,6 +4425,8 @@ avr_rotate_bytes (rtx operands[])
     /* Work out if byte or word move is needed.  Odd byte rotates need QImode.
        Word move if no scratch is needed, otherwise use size of scratch.  */
     enum machine_mode move_mode = QImode;
+    int move_size, offset, size;
+
     if (num & 0xf)
       move_mode = QImode;
     else if ((mode == SImode && !same_reg) || !overlapped)
@@ -4261,11 +4442,11 @@ avr_rotate_bytes (rtx operands[])
     if (GET_MODE (scratch) == HImode && move_mode == QImode)
       scratch = simplify_gen_subreg (move_mode, scratch, HImode, 0); 
 
-    int move_size = GET_MODE_SIZE (move_mode);
+    move_size = GET_MODE_SIZE (move_mode);
     /* Number of bytes/words to rotate.  */
-    int offset = (num  >> 3) / move_size;
+    offset = (num  >> 3) / move_size;
     /* Number of moves needed.  */
-    int size = GET_MODE_SIZE (mode) / move_size;
+    size = GET_MODE_SIZE (mode) / move_size;
     /* Himode byte swap is special case to avoid a scratch register.  */
     if (mode == HImode && same_reg)
       {
@@ -4282,12 +4463,15 @@ avr_rotate_bytes (rtx operands[])
       }    
     else  
       {
+#define MAX_SIZE 8 /* GET_MODE_SIZE (DImode) / GET_MODE_SIZE (QImode)  */
        /* Create linked list of moves to determine move order.  */
        struct {
          rtx src, dst;
          int links;
-       } move[size + 8];
+       } move[MAX_SIZE + 8];
+       int blocked, moves;
 
+       gcc_assert (size <= MAX_SIZE);
        /* Generate list of subreg moves.  */
        for (i = 0; i < size; i++)
          {
@@ -4313,8 +4497,8 @@ avr_rotate_bytes (rtx operands[])
                    break;
                  }
 
-       int blocked = -1;
-       int moves = 0;
+       blocked = -1;
+       moves = 0;
        /* Go through move list and perform non-conflicting moves.  As each
           non-overlapping move is made, it may remove other conflicts
           so the process is repeated until no conflicts remain.  */
@@ -4326,18 +4510,21 @@ avr_rotate_bytes (rtx operands[])
               src already.  */
            for (i = 0; i < size; i++)
              if (move[i].src != NULL_RTX)
-               if  (move[i].links == -1 || move[move[i].links].src == NULL_RTX)
-                 {
-                   moves++;
-                   /* Ignore NOP moves to self.  */
-                   if (!rtx_equal_p (move[i].dst, move[i].src))
-                     emit_move_insn (move[i].dst, move[i].src);
+               {
+                 if (move[i].links == -1
+                     || move[move[i].links].src == NULL_RTX)
+                   {
+                     moves++;
+                     /* Ignore NOP moves to self.  */
+                     if (!rtx_equal_p (move[i].dst, move[i].src))
+                       emit_move_insn (move[i].dst, move[i].src);
 
-                   /* Remove  conflict from list.  */
-                   move[i].src = NULL_RTX;
-                 }
-               else
-                 blocked = i;
+                     /* Remove  conflict from list.  */
+                     move[i].src = NULL_RTX;
+                   }
+                 else
+                   blocked = i;
+               }
 
            /* Check for deadlock. This is when no moves occurred and we have
               at least one blocked move.  */
@@ -4393,7 +4580,7 @@ adjust_insn_length (rtx insn, int len)
              break;
            case SImode:
            case SFmode:
-             output_movsisf (insn, op, &len);
+             output_movsisf (insn, op, NULL_RTX, &len);
              break;
            default:
              break;
@@ -4464,7 +4651,7 @@ adjust_insn_length (rtx insn, int len)
              break;
            case SImode:
            case SFmode:
-             output_reload_insisf (insn, op, &len);
+             output_reload_insisf (insn, op, XEXP (op[2], 0), &len);
              break;
            default:
              break;
@@ -4679,108 +4866,15 @@ avr_asm_declare_function_name (FILE *file, const char *name, tree decl)
   ASM_OUTPUT_LABEL (file, name);
 }
 
-/* The routine used to output NUL terminated strings.  We use a special
-   version of this for most svr4 targets because doing so makes the
-   generated assembly code more compact (and thus faster to assemble)
-   as well as more readable, especially for targets like the i386
-   (where the only alternative is to output character sequences as
-   comma separated lists of numbers).  */
 
-void
-gas_output_limited_string(FILE *file, const char *str)
+/* Return value is nonzero if pseudos that have been
+   assigned to registers of class CLASS would likely be spilled
+   because registers of CLASS are needed for spill registers.  */
+
+static bool
+avr_class_likely_spilled_p (reg_class_t c)
 {
-  const unsigned char *_limited_str = (const unsigned char *) str;
-  unsigned ch;
-  fprintf (file, "%s\"", STRING_ASM_OP);
-  for (; (ch = *_limited_str); _limited_str++)
-    {
-      int escape;
-      switch (escape = ESCAPES[ch])
-       {
-       case 0:
-         putc (ch, file);
-         break;
-       case 1:
-         fprintf (file, "\\%03o", ch);
-         break;
-       default:
-         putc ('\\', file);
-         putc (escape, file);
-         break;
-       }
-    }
-  fprintf (file, "\"\n");
-}
-
-/* The routine used to output sequences of byte values.  We use a special
-   version of this for most svr4 targets because doing so makes the
-   generated assembly code more compact (and thus faster to assemble)
-   as well as more readable.  Note that if we find subparts of the
-   character sequence which end with NUL (and which are shorter than
-   STRING_LIMIT) we output those using ASM_OUTPUT_LIMITED_STRING.  */
-
-void
-gas_output_ascii(FILE *file, const char *str, size_t length)
-{
-  const unsigned char *_ascii_bytes = (const unsigned char *) str;
-  const unsigned char *limit = _ascii_bytes + length;
-  unsigned bytes_in_chunk = 0;
-  for (; _ascii_bytes < limit; _ascii_bytes++)
-    {
-      const unsigned char *p;
-      if (bytes_in_chunk >= 60)
-       {
-         fprintf (file, "\"\n");
-         bytes_in_chunk = 0;
-       }
-      for (p = _ascii_bytes; p < limit && *p != '\0'; p++)
-       continue;
-      if (p < limit && (p - _ascii_bytes) <= (signed)STRING_LIMIT)
-       {
-         if (bytes_in_chunk > 0)
-           {
-             fprintf (file, "\"\n");
-             bytes_in_chunk = 0;
-           }
-         gas_output_limited_string (file, (const char*)_ascii_bytes);
-         _ascii_bytes = p;
-       }
-      else
-       {
-         int escape;
-         unsigned ch;
-         if (bytes_in_chunk == 0)
-           fprintf (file, "\t.ascii\t\"");
-         switch (escape = ESCAPES[ch = *_ascii_bytes])
-           {
-           case 0:
-             putc (ch, file);
-             bytes_in_chunk++;
-             break;
-           case 1:
-             fprintf (file, "\\%03o", ch);
-             bytes_in_chunk += 4;
-             break;
-           default:
-             putc ('\\', file);
-             putc (escape, file);
-             bytes_in_chunk += 2;
-             break;
-           }
-       }
-    }
-  if (bytes_in_chunk > 0)
-    fprintf (file, "\"\n");
-}
-
-/* Return value is nonzero if pseudos that have been
-   assigned to registers of class CLASS would likely be spilled
-   because registers of CLASS are needed for spill registers.  */
-
-static bool
-avr_class_likely_spilled_p (reg_class_t c)
-{
-  return (c != ALL_REGS && c != ADDW_REGS);
+  return (c != ALL_REGS && c != ADDW_REGS);
 }
 
 /* Valid attributes:
@@ -4818,12 +4912,7 @@ avr_handle_progmem_attribute (tree *node, tree name,
        }
       else if (TREE_STATIC (*node) || DECL_EXTERNAL (*node))
        {
-         if (DECL_INITIAL (*node) == NULL_TREE && !DECL_EXTERNAL (*node))
-           {
-             warning (0, "only initialized variables can be placed into "
-                      "program memory area");
-             *no_add_attrs = true;
-           }
+          *no_add_attrs = false;
        }
       else
        {
@@ -4909,14 +4998,32 @@ avr_insert_attributes (tree node, tree *attributes)
       && (TREE_STATIC (node) || DECL_EXTERNAL (node))
       && avr_progmem_p (node, *attributes))
     {
-      static const char dsec[] = ".progmem.data";
-      *attributes = tree_cons (get_identifier ("section"),
-               build_tree_list (NULL, build_string (strlen (dsec), dsec)),
-               *attributes);
+      tree node0 = node;
 
-      /* ??? This seems sketchy.  Why can't the user declare the
-        thing const in the first place?  */
-      TREE_READONLY (node) = 1;
+      /* For C++, we have to peel arrays in order to get correct
+         determination of readonlyness.  */
+      
+      do
+        node0 = TREE_TYPE (node0);
+      while (TREE_CODE (node0) == ARRAY_TYPE);
+
+      if (error_mark_node == node0)
+        return;
+      
+      if (TYPE_READONLY (node0))
+        {
+          static const char dsec[] = ".progmem.data";
+
+          *attributes = tree_cons (get_identifier ("section"),
+                                   build_tree_list (NULL, build_string (strlen (dsec), dsec)),
+                                   *attributes);
+        }
+      else
+        {
+          error ("variable %q+D must be const in order to be put into"
+                 " read-only section by means of %<__attribute__((progmem))%>",
+                 node);
+        }
     }
 }
 
@@ -4932,7 +5039,52 @@ avr_output_progmem_section_asm_op (const void *arg ATTRIBUTE_UNUSED)
   fprintf (asm_out_file, "\t.p2align 1\n");
 }
 
-/* Implement TARGET_ASM_INIT_SECTIONS.  */
+
+/* Implement `ASM_OUTPUT_ALIGNED_DECL_LOCAL'.  */
+/* Implement `ASM_OUTPUT_ALIGNED_DECL_COMMON'.  */
+/* Track need of __do_clear_bss.  */
+
+void
+avr_asm_output_aligned_decl_common (FILE * stream, const_tree decl ATTRIBUTE_UNUSED,
+                                    const char *name, unsigned HOST_WIDE_INT size,
+                                    unsigned int align, bool local_p)
+{
+  avr_need_clear_bss_p = true;
+
+  if (local_p)
+    ASM_OUTPUT_ALIGNED_LOCAL (stream, name, size, align);
+  else
+    ASM_OUTPUT_ALIGNED_COMMON (stream, name, size, align);
+}
+
+
+/* Unnamed section callback for data_section
+   to track need of __do_copy_data.  */
+
+static void
+avr_output_data_section_asm_op (const void *data)
+{
+  avr_need_copy_data_p = true;
+  
+  /* Dispatch to default.  */
+  output_section_asm_op (data);
+}
+
+
+/* Unnamed section callback for bss_section
+   to track need of __do_clear_bss.  */
+
+static void
+avr_output_bss_section_asm_op (const void *data)
+{
+  avr_need_clear_bss_p = true;
+  
+  /* Dispatch to default.  */
+  output_section_asm_op (data);
+}
+
+
+/* Implement `TARGET_ASM_INIT_SECTIONS'.  */
 
 static void
 avr_asm_init_sections (void)
@@ -4941,6 +5093,27 @@ avr_asm_init_sections (void)
                                         avr_output_progmem_section_asm_op,
                                         NULL);
   readonly_data_section = data_section;
+
+  data_section->unnamed.callback = avr_output_data_section_asm_op;
+  bss_section->unnamed.callback = avr_output_bss_section_asm_op;
+}
+
+
+/* Implement `TARGET_ASM_NAMED_SECTION'.  */
+/* Track need of __do_clear_bss, __do_copy_data for named sections.  */
+
+void
+avr_asm_named_section (const char *name, unsigned int flags, tree decl)
+{
+  if (!avr_need_copy_data_p)
+    avr_need_copy_data_p = (0 == strncmp (name, ".data", 5)
+                            || 0 == strncmp (name, ".rodata", 7)
+                            || 0 == strncmp (name, ".gnu.linkonce.d", 15));
+  
+  if (!avr_need_clear_bss_p)
+    avr_need_clear_bss_p = (0 == strncmp (name, ".bss", 4));
+  
+  default_elf_asm_named_section (name, flags, decl);
 }
 
 static unsigned int
@@ -4958,9 +5131,38 @@ avr_section_type_flags (tree decl, const char *name, int reloc)
                 ".noinit section");
     }
 
+  if (0 == strncmp (name, ".progmem.data", strlen (".progmem.data")))
+    flags &= ~SECTION_WRITE;
+  
   return flags;
 }
 
+
+/* Implement `TARGET_ENCODE_SECTION_INFO'.  */
+
+static void
+avr_encode_section_info (tree decl, rtx rtl,
+                         int new_decl_p)
+{
+  /* In avr_handle_progmem_attribute, DECL_INITIAL is not yet
+     readily available, see PR34734.  So we postpone the warning
+     about uninitialized data in program memory section until here.  */
+   
+  if (new_decl_p
+      && decl && DECL_P (decl)
+      && NULL_TREE == DECL_INITIAL (decl)
+      && avr_progmem_p (decl, DECL_ATTRIBUTES (decl)))
+    {
+      warning (OPT_Wuninitialized,
+               "uninitialized variable %q+D put into "
+               "program memory area", decl);
+    }
+
+  default_encode_section_info (decl, rtl, new_decl_p);
+}
+
+
+/* Implement `TARGET_ASM_FILE_START'.  */
 /* Outputs some appropriate text to go at the start of an assembler
    file.  */
 
@@ -4968,31 +5170,38 @@ static void
 avr_file_start (void)
 {
   if (avr_current_arch->asm_only)
-    error ("MCU %qs supported for assembler only", avr_mcu_name);
+    error ("MCU %qs supported for assembler only", avr_current_device->name);
 
   default_file_start ();
 
-/*  fprintf (asm_out_file, "\t.arch %s\n", avr_mcu_name);*/
+/*  fprintf (asm_out_file, "\t.arch %s\n", avr_current_device->name);*/
   fputs ("__SREG__ = 0x3f\n"
         "__SP_H__ = 0x3e\n"
         "__SP_L__ = 0x3d\n", asm_out_file);
   
   fputs ("__tmp_reg__ = 0\n" 
          "__zero_reg__ = 1\n", asm_out_file);
-
-  /* FIXME: output these only if there is anything in the .data / .bss
-     sections - some code size could be saved by not linking in the
-     initialization code from libgcc if one or both sections are empty.  */
-  fputs ("\t.global __do_copy_data\n", asm_out_file);
-  fputs ("\t.global __do_clear_bss\n", asm_out_file);
 }
 
+
+/* Implement `TARGET_ASM_FILE_END'.  */
 /* Outputs to the stdio stream FILE some
    appropriate text to go at the end of an assembler file.  */
 
 static void
 avr_file_end (void)
 {
+  /* Output these only if there is anything in the
+     .data* / .rodata* / .gnu.linkonce.* resp. .bss*
+     input section(s) - some code size can be saved by not
+     linking in the initialization code from libgcc if resp.
+     sections are empty.  */
+
+  if (avr_need_copy_data_p)
+    fputs (".global __do_copy_data\n", asm_out_file);
+
+  if (avr_need_clear_bss_p)
+    fputs (".global __do_clear_bss\n", asm_out_file);
 }
 
 /* Choose the order in which to allocate hard registers for
@@ -5052,6 +5261,32 @@ order_regs_for_local_alloc (void)
 }
 
 
+/* Implement `TARGET_REGISTER_MOVE_COST' */
+
+static int
+avr_register_move_cost (enum machine_mode mode ATTRIBUTE_UNUSED,
+                        reg_class_t from, reg_class_t to)
+{
+  return (from == STACK_REG ? 6
+          : to == STACK_REG ? 12
+          : 2);
+}
+
+
+/* Implement `TARGET_MEMORY_MOVE_COST' */
+
+static int
+avr_memory_move_cost (enum machine_mode mode, reg_class_t rclass ATTRIBUTE_UNUSED,
+                      bool in ATTRIBUTE_UNUSED)
+{
+  return (mode == QImode ? 2
+          : mode == HImode ? 4
+          : mode == SImode ? 8
+          : mode == SFmode ? 8
+          : 16);
+}
+
+
 /* Mutually recursive subroutine of avr_rtx_cost for calculating the
    cost of an RTX operand given its context.  X is the rtx of the
    operand, MODE is its mode, and OUTER is the rtx_code of this
@@ -5798,36 +6033,45 @@ avr_reorg (void)
 
 /* Returns register number for function return value.*/
 
-int
+static inline unsigned int
 avr_ret_register (void)
 {
   return 24;
 }
 
+/* Worker function for TARGET_FUNCTION_VALUE_REGNO_P.  */
+
+static bool
+avr_function_value_regno_p (const unsigned int regno)
+{
+  return (regno == avr_ret_register ());
+}
+
 /* Create an RTX representing the place where a
    library function returns a value of mode MODE.  */
 
-rtx
-avr_libcall_value (enum machine_mode mode)
+static rtx
+avr_libcall_value (enum machine_mode mode,
+                  const_rtx func ATTRIBUTE_UNUSED)
 {
   int offs = GET_MODE_SIZE (mode);
   if (offs < 2)
     offs = 2;
-  return gen_rtx_REG (mode, RET_REGISTER + 2 - offs);
+  return gen_rtx_REG (mode, avr_ret_register () + 2 - offs);
 }
 
 /* Create an RTX representing the place where a
    function returns a value of data type VALTYPE.  */
 
-rtx
-avr_function_value (const_tree type, 
-                   const_tree func ATTRIBUTE_UNUSED, 
-                   bool outgoing ATTRIBUTE_UNUSED)
+static rtx
+avr_function_value (const_tree type,
+                    const_tree fn_decl_or_type ATTRIBUTE_UNUSED,
+                    bool outgoing ATTRIBUTE_UNUSED)
 {
   unsigned int offs;
-  
+
   if (TYPE_MODE (type) != BLKmode)
-    return avr_libcall_value (TYPE_MODE (type));
+    return avr_libcall_value (TYPE_MODE (type), NULL_RTX);
   
   offs = int_size_in_bytes (type);
   if (offs < 2)
@@ -5837,7 +6081,7 @@ avr_function_value (const_tree type,
   else if (offs > GET_MODE_SIZE (SImode) && offs < GET_MODE_SIZE (DImode))
     offs = GET_MODE_SIZE (DImode);
   
-  return gen_rtx_REG (BLKmode, RET_REGISTER + 2 - offs);
+  return gen_rtx_REG (BLKmode, avr_ret_register () + 2 - offs);
 }
 
 int
@@ -5873,26 +6117,30 @@ jump_over_one_insn_p (rtx insn, rtx dest)
 int
 avr_hard_regno_mode_ok (int regno, enum machine_mode mode)
 {
-  /* Disallow QImode in stack pointer regs.  */
-  if ((regno == REG_SP || regno == (REG_SP + 1)) && mode == QImode)
-    return 0;
-
-  /* The only thing that can go into registers r28:r29 is a Pmode.  */
-  if (regno == REG_Y && mode == Pmode)
-    return 1;
-
-  /* Otherwise disallow all regno/mode combinations that span r28:r29.  */
-  if (regno <= (REG_Y + 1) && (regno + GET_MODE_SIZE (mode)) >= (REG_Y + 1))
-    return 0;
-
-  if (mode == QImode)
+  /* NOTE: 8-bit values must not be disallowed for R28 or R29.
+        Disallowing QI et al. in these regs might lead to code like
+            (set (subreg:QI (reg:HI 28) n) ...)
+        which will result in wrong code because reload does not
+        handle SUBREGs of hard regsisters like this.
+        This could be fixed in reload.  However, it appears
+        that fixing reload is not wanted by reload people.  */
+  
+  /* Any GENERAL_REGS register can hold 8-bit values.  */
+  
+  if (GET_MODE_SIZE (mode) == 1)
     return 1;
 
-  /* Modes larger than QImode occupy consecutive registers.  */
-  if (regno + GET_MODE_SIZE (mode) > FIRST_PSEUDO_REGISTER)
+  /* FIXME: Ideally, the following test is not needed.
+        However, it turned out that it can reduce the number
+        of spill fails.  AVR and it's poor endowment with
+        address registers is extreme stress test for reload.  */
+  
+  if (GET_MODE_SIZE (mode) >= 4
+      && regno >= REG_X)
     return 0;
 
-  /* All modes larger than QImode should start in an even register.  */
+  /* All modes larger than 8 bits should start in an even register.  */
+  
   return !(regno & 1);
 }
 
@@ -5936,53 +6184,199 @@ output_reload_inhi (rtx insn ATTRIBUTE_UNUSED, rtx *operands, int *len)
 }
 
 
+/* Reload a SI or SF compile time constant (OP[1]) into a GPR (OP[0]).
+   CLOBBER_REG is a QI clobber reg needed to move vast majority of consts
+   into a NO_LD_REGS.  If CLOBBER_REG is NULL_RTX we either don't need a
+   clobber reg or have to cook one up.
+
+   LEN == NULL: Output instructions.
+   
+   LEN != NULL: Output nothing.  Increment *LEN by number of words occupied
+                by the insns printed.
+
+   Return "".  */
+
 const char *
-output_reload_insisf (rtx insn ATTRIBUTE_UNUSED, rtx *operands, int *len)
+output_reload_insisf (rtx insn ATTRIBUTE_UNUSED,
+                      rtx *op, rtx clobber_reg, int *len)
 {
-  rtx src = operands[1];
-  int cnst = (GET_CODE (src) == CONST_INT);
+  rtx src = op[1];
+  rtx dest = op[0];
+  rtx xval, xdest[4];
+  int ival[4];
+  int clobber_val = 1234;
+  bool cooked_clobber_p = false;
+  bool set_p = false;
+  unsigned int n;
+  enum machine_mode mode = GET_MODE (dest);
+  
+  gcc_assert (REG_P (dest));
 
   if (len)
+    *len = 0;
+  
+  /* (REG:SI 14) is special: It's neither in LD_REGS nor in NO_LD_REGS
+     but has some subregs that are in LD_REGS.  Use the MSB (REG:QI 17).  */
+  
+  if (14 == REGNO (dest))
     {
-      if (cnst)
-       *len = 4 + ((INTVAL (src) & 0xff) != 0)
-               + ((INTVAL (src) & 0xff00) != 0)
-               + ((INTVAL (src) & 0xff0000) != 0)
-               + ((INTVAL (src) & 0xff000000) != 0);
-      else
-       *len = 8;
-
-      return "";
+      clobber_reg = gen_rtx_REG (QImode, 17);
     }
 
-  if (cnst && ((INTVAL (src) & 0xff) == 0))
-    output_asm_insn (AS2 (mov, %A0, __zero_reg__), operands);
-  else
-    {
-      output_asm_insn (AS2 (ldi, %2, lo8(%1)), operands);
-      output_asm_insn (AS2 (mov, %A0, %2), operands);
-    }
-  if (cnst && ((INTVAL (src) & 0xff00) == 0))
-    output_asm_insn (AS2 (mov, %B0, __zero_reg__), operands);
-  else
+  /* We might need a clobber reg but don't have one.  Look at the value
+     to be loaded more closely.  A clobber is only needed if it contains
+     a byte that is neither 0, -1 or a power of 2.  */
+  
+  if (NULL_RTX == clobber_reg
+      && !test_hard_reg_class (LD_REGS, dest))
     {
-      output_asm_insn (AS2 (ldi, %2, hi8(%1)), operands);
-      output_asm_insn (AS2 (mov, %B0, %2), operands);
+      for (n = 0; n < GET_MODE_SIZE (mode); n++)
+        {
+          xval = simplify_gen_subreg (QImode, src, mode, n);
+
+          if (!(const0_rtx == xval
+                || constm1_rtx == xval
+                || single_one_operand (xval, QImode)))
+            {
+              /* We have no clobber reg but need one.  Cook one up.
+                 That's cheaper than loading from constant pool.  */
+              
+              cooked_clobber_p = true;
+              clobber_reg = gen_rtx_REG (QImode, REG_Z + 1);
+              avr_asm_len ("mov __tmp_reg__,%0", &clobber_reg, len, 1);
+              break;
+            }
+        }
     }
-  if (cnst && ((INTVAL (src) & 0xff0000) == 0))
-    output_asm_insn (AS2 (mov, %C0, __zero_reg__), operands);
-  else
+
+  /* Now start filling DEST from LSB to MSB.  */
+  
+  for (n = 0; n < GET_MODE_SIZE (mode); n++)
     {
-      output_asm_insn (AS2 (ldi, %2, hlo8(%1)), operands);
-      output_asm_insn (AS2 (mov, %C0, %2), operands);
+      bool done_byte = false;
+      unsigned int j;
+      rtx xop[3];
+
+      /* Crop the n-th sub-byte.  */
+      
+      xval = simplify_gen_subreg (QImode, src, mode, n);
+      xdest[n] = simplify_gen_subreg (QImode, dest, mode, n);
+      ival[n] = INTVAL (xval);
+
+      /* Look if we can reuse the low word by means of MOVW.  */
+      
+      if (n == 2
+          && AVR_HAVE_MOVW)
+        {
+          rtx lo16 = simplify_gen_subreg (HImode, src, mode, 0);
+          rtx hi16 = simplify_gen_subreg (HImode, src, mode, 2);
+
+          if (INTVAL (lo16) == INTVAL (hi16))
+            {
+              avr_asm_len ("movw %C0,%A0", &op[0], len, 1);
+              break;
+            }
+        }
+
+      /* Use CLR to zero a value so that cc0 is set as expected
+         for zero.  */
+      
+      if (ival[n] == 0)
+        {
+          avr_asm_len ("clr %0", &xdest[n], len, 1);
+          continue;
+        }
+
+      if (clobber_val == ival[n]
+          && REGNO (clobber_reg) == REGNO (xdest[n]))
+        {
+          continue;
+        }
+
+      /* LD_REGS can use LDI to move a constant value */
+      
+      if (test_hard_reg_class (LD_REGS, xdest[n]))
+        {
+          xop[0] = xdest[n];
+          xop[1] = xval;
+          avr_asm_len ("ldi %0,lo8(%1)", xop, len, 1);
+          continue;
+        }
+
+      /* Try to reuse value already loaded in some lower byte. */
+      
+      for (j = 0; j < n; j++)
+        if (ival[j] == ival[n])
+          {
+            xop[0] = xdest[n];
+            xop[1] = xdest[j];
+            
+            avr_asm_len ("mov %0,%1", xop, len, 1);
+            done_byte = true;
+            break;
+          }
+
+      if (done_byte)
+        continue;
+
+      /* Need no clobber reg for -1: Use CLR/DEC */
+      
+      if (-1 == ival[n])
+        {
+          avr_asm_len ("clr %0" CR_TAB
+                       "dec %0", &xdest[n], len, 2);
+          continue;
+        }
+
+      /* Use T flag or INC to manage powers of 2 if we have
+         no clobber reg.  */
+
+      if (NULL_RTX == clobber_reg
+          && single_one_operand (xval, QImode))
+        {
+          if (1 == ival[n])
+            {
+              avr_asm_len ("clr %0" CR_TAB
+                           "inc %0", &xdest[n], len, 2);
+              continue;
+            }
+          
+          xop[0] = xdest[n];
+          xop[1] = GEN_INT (exact_log2 (ival[n] & GET_MODE_MASK (QImode)));
+
+          gcc_assert (constm1_rtx != xop[1]);
+
+          if (!set_p)
+            {
+              set_p = true;
+              avr_asm_len ("set", xop, len, 1);
+            }
+
+          avr_asm_len ("clr %0" CR_TAB
+                       "bld %0,%1", xop, len, 2);
+          continue;
+        }
+
+      /* We actually need the LD_REGS clobber reg.  */
+
+      gcc_assert (NULL_RTX != clobber_reg);
+        
+      xop[0] = xdest[n];
+      xop[1] = xval;
+      xop[2] = clobber_reg;
+      clobber_val = ival[n];
+        
+      avr_asm_len ("ldi %2,lo8(%1)" CR_TAB
+                   "mov %0,%2", xop, len, 2);
     }
-  if (cnst && ((INTVAL (src) & 0xff000000) == 0))
-    output_asm_insn (AS2 (mov, %D0, __zero_reg__), operands);
-  else
+  
+  /* If we cooked up a clobber reg above, restore it.  */
+  
+  if (cooked_clobber_p)
     {
-      output_asm_insn (AS2 (ldi, %2, hhi8(%1)), operands);
-      output_asm_insn (AS2 (mov, %D0, %2), operands);
+      avr_asm_len ("mov %0,__tmp_reg__", &clobber_reg, len, 1);
     }
+  
   return "";
 }
 
@@ -6019,13 +6413,23 @@ avr_hard_regno_scratch_ok (unsigned int regno)
       && !df_regs_ever_live_p (regno))
     return false;
 
+  /* Don't allow hard registers that might be part of the frame pointer.
+     Some places in the compiler just test for [HARD_]FRAME_POINTER_REGNUM
+     and don't care for a frame pointer that spans more than one register.  */
+
+  if ((!reload_completed || frame_pointer_needed)
+      && (regno == REG_Y || regno == REG_Y + 1))
+    {
+      return false;
+    }
+
   return true;
 }
 
 /* Return nonzero if register OLD_REG can be renamed to register NEW_REG.  */
 
 int
-avr_hard_regno_rename_ok (unsigned int old_reg ATTRIBUTE_UNUSED,
+avr_hard_regno_rename_ok (unsigned int old_reg,
                          unsigned int new_reg)
 {
   /* Interrupt functions can only use registers that have already been
@@ -6036,6 +6440,17 @@ avr_hard_regno_rename_ok (unsigned int old_reg ATTRIBUTE_UNUSED,
       && !df_regs_ever_live_p (new_reg))
     return 0;
 
+  /* Don't allow hard registers that might be part of the frame pointer.
+     Some places in the compiler just test for [HARD_]FRAME_POINTER_REGNUM
+     and don't care for a frame pointer that spans more than one register.  */
+
+  if ((!reload_completed || frame_pointer_needed)
+      && (old_reg == REG_Y || old_reg == REG_Y + 1
+          || new_reg == REG_Y || new_reg == REG_Y + 1))
+    {
+      return 0;
+    }
+  
   return 1;
 }
 
@@ -6147,4 +6562,324 @@ unsigned int avr_case_values_threshold (void)
   return (!AVR_HAVE_JMP_CALL || TARGET_CALL_PROLOGUES) ? 8 : 17;
 }
 
+/* Helper for __builtin_avr_delay_cycles */
+
+static void
+avr_expand_delay_cycles (rtx operands0)
+{
+  unsigned HOST_WIDE_INT cycles = UINTVAL (operands0);
+  unsigned HOST_WIDE_INT cycles_used;
+  unsigned HOST_WIDE_INT loop_count;
+  
+  if (IN_RANGE (cycles, 83886082, 0xFFFFFFFF))
+    {
+      loop_count = ((cycles - 9) / 6) + 1;
+      cycles_used = ((loop_count - 1) * 6) + 9;
+      emit_insn (gen_delay_cycles_4 (gen_int_mode (loop_count, SImode)));
+      cycles -= cycles_used;
+    }
+  
+  if (IN_RANGE (cycles, 262145, 83886081))
+    {
+      loop_count = ((cycles - 7) / 5) + 1;
+      if (loop_count > 0xFFFFFF)
+        loop_count = 0xFFFFFF;
+      cycles_used = ((loop_count - 1) * 5) + 7;
+      emit_insn (gen_delay_cycles_3 (gen_int_mode (loop_count, SImode)));
+      cycles -= cycles_used;
+    }
+  
+  if (IN_RANGE (cycles, 768, 262144))
+    {
+      loop_count = ((cycles - 5) / 4) + 1;
+      if (loop_count > 0xFFFF)
+        loop_count = 0xFFFF;
+      cycles_used = ((loop_count - 1) * 4) + 5;
+      emit_insn (gen_delay_cycles_2 (gen_int_mode (loop_count, HImode)));
+      cycles -= cycles_used;
+    }
+  
+  if (IN_RANGE (cycles, 6, 767))
+    {
+      loop_count = cycles / 3;
+      if (loop_count > 255) 
+        loop_count = 255;
+      cycles_used = loop_count * 3;
+      emit_insn (gen_delay_cycles_1 (gen_int_mode (loop_count, QImode)));
+      cycles -= cycles_used;
+      }
+  
+  while (cycles >= 2)
+    {
+      emit_insn (gen_nopv (GEN_INT(2)));
+      cycles -= 2;
+    }
+
+  if (cycles == 1)
+    {
+      emit_insn (gen_nopv (GEN_INT(1)));
+      cycles--;
+    }
+}
+
+/* IDs for all the AVR builtins.  */
+
+enum avr_builtin_id
+  {
+    AVR_BUILTIN_NOP,
+    AVR_BUILTIN_SEI,
+    AVR_BUILTIN_CLI,
+    AVR_BUILTIN_WDR,
+    AVR_BUILTIN_SLEEP,
+    AVR_BUILTIN_SWAP,
+    AVR_BUILTIN_FMUL,
+    AVR_BUILTIN_FMULS,
+    AVR_BUILTIN_FMULSU,
+    AVR_BUILTIN_DELAY_CYCLES
+  };
+
+#define DEF_BUILTIN(NAME, TYPE, CODE)                                   \
+  do                                                                    \
+    {                                                                   \
+      add_builtin_function ((NAME), (TYPE), (CODE), BUILT_IN_MD,        \
+                            NULL, NULL_TREE);                           \
+    } while (0)
+
+
+/* Implement `TARGET_INIT_BUILTINS' */
+/* Set up all builtin functions for this target.  */
+
+static void
+avr_init_builtins (void)
+{
+  tree void_ftype_void
+    = build_function_type_list (void_type_node, NULL_TREE);
+  tree uchar_ftype_uchar
+    = build_function_type_list (unsigned_char_type_node, 
+                                unsigned_char_type_node,
+                                NULL_TREE);
+  tree uint_ftype_uchar_uchar
+    = build_function_type_list (unsigned_type_node, 
+                                unsigned_char_type_node,
+                                unsigned_char_type_node, 
+                                NULL_TREE);
+  tree int_ftype_char_char
+    = build_function_type_list (integer_type_node, 
+                                char_type_node,
+                                char_type_node, 
+                                NULL_TREE);
+  tree int_ftype_char_uchar
+    = build_function_type_list (integer_type_node, 
+                                char_type_node,
+                                unsigned_char_type_node, 
+                                NULL_TREE);
+  tree void_ftype_ulong
+    = build_function_type_list (void_type_node, 
+                                long_unsigned_type_node,
+                                NULL_TREE);
+
+  DEF_BUILTIN ("__builtin_avr_nop", void_ftype_void, AVR_BUILTIN_NOP);
+  DEF_BUILTIN ("__builtin_avr_sei", void_ftype_void, AVR_BUILTIN_SEI);
+  DEF_BUILTIN ("__builtin_avr_cli", void_ftype_void, AVR_BUILTIN_CLI);
+  DEF_BUILTIN ("__builtin_avr_wdr", void_ftype_void, AVR_BUILTIN_WDR);
+  DEF_BUILTIN ("__builtin_avr_sleep", void_ftype_void, AVR_BUILTIN_SLEEP);
+  DEF_BUILTIN ("__builtin_avr_swap", uchar_ftype_uchar, AVR_BUILTIN_SWAP);
+  DEF_BUILTIN ("__builtin_avr_delay_cycles", void_ftype_ulong, 
+               AVR_BUILTIN_DELAY_CYCLES);
+
+  DEF_BUILTIN ("__builtin_avr_fmul", uint_ftype_uchar_uchar, 
+               AVR_BUILTIN_FMUL);
+  DEF_BUILTIN ("__builtin_avr_fmuls", int_ftype_char_char, 
+               AVR_BUILTIN_FMULS);
+  DEF_BUILTIN ("__builtin_avr_fmulsu", int_ftype_char_uchar, 
+               AVR_BUILTIN_FMULSU);
+}
+
+#undef DEF_BUILTIN
+
+struct avr_builtin_description
+{
+  const enum insn_code icode;
+  const char *const name;
+  const enum avr_builtin_id id;
+};
+
+static const struct avr_builtin_description
+bdesc_1arg[] =
+  {
+    { CODE_FOR_rotlqi3_4, "__builtin_avr_swap", AVR_BUILTIN_SWAP }
+  };
+
+static const struct avr_builtin_description
+bdesc_2arg[] =
+  {
+    { CODE_FOR_fmul, "__builtin_avr_fmul", AVR_BUILTIN_FMUL },
+    { CODE_FOR_fmuls, "__builtin_avr_fmuls", AVR_BUILTIN_FMULS },
+    { CODE_FOR_fmulsu, "__builtin_avr_fmulsu", AVR_BUILTIN_FMULSU }
+  };
+
+/* Subroutine of avr_expand_builtin to take care of unop insns.  */
+
+static rtx
+avr_expand_unop_builtin (enum insn_code icode, tree exp,
+                         rtx target)
+{
+  rtx pat;
+  tree arg0 = CALL_EXPR_ARG (exp, 0);
+  rtx op0 = expand_expr (arg0, NULL_RTX, VOIDmode, EXPAND_NORMAL);
+  enum machine_mode op0mode = GET_MODE (op0);
+  enum machine_mode tmode = insn_data[icode].operand[0].mode;
+  enum machine_mode mode0 = insn_data[icode].operand[1].mode;
+
+  if (! target
+      || GET_MODE (target) != tmode
+      || ! (*insn_data[icode].operand[0].predicate) (target, tmode))
+    {
+      target = gen_reg_rtx (tmode);
+    }
+
+  if (op0mode == SImode && mode0 == HImode)
+    {
+      op0mode = HImode;
+      op0 = gen_lowpart (HImode, op0);
+    }
+  
+  gcc_assert (op0mode == mode0 || op0mode == VOIDmode);
+
+  if (! (*insn_data[icode].operand[1].predicate) (op0, mode0))
+    op0 = copy_to_mode_reg (mode0, op0);
+
+  pat = GEN_FCN (icode) (target, op0);
+  if (! pat)
+    return 0;
+  
+  emit_insn (pat);
+  
+  return target;
+}
+
+
+/* Subroutine of avr_expand_builtin to take care of binop insns.  */
+
+static rtx
+avr_expand_binop_builtin (enum insn_code icode, tree exp, rtx target)
+{
+  rtx pat;
+  tree arg0 = CALL_EXPR_ARG (exp, 0);
+  tree arg1 = CALL_EXPR_ARG (exp, 1);
+  rtx op0 = expand_expr (arg0, NULL_RTX, VOIDmode, EXPAND_NORMAL);
+  rtx op1 = expand_expr (arg1, NULL_RTX, VOIDmode, EXPAND_NORMAL);
+  enum machine_mode op0mode = GET_MODE (op0);
+  enum machine_mode op1mode = GET_MODE (op1);
+  enum machine_mode tmode = insn_data[icode].operand[0].mode;
+  enum machine_mode mode0 = insn_data[icode].operand[1].mode;
+  enum machine_mode mode1 = insn_data[icode].operand[2].mode;
+
+  if (! target
+      || GET_MODE (target) != tmode
+      || ! (*insn_data[icode].operand[0].predicate) (target, tmode))
+    {
+      target = gen_reg_rtx (tmode);
+    }
+
+  if ((op0mode == SImode || op0mode == VOIDmode) && mode0 == HImode)
+    {
+      op0mode = HImode;
+      op0 = gen_lowpart (HImode, op0);
+    }
+  
+  if ((op1mode == SImode || op1mode == VOIDmode) && mode1 == HImode)
+    {
+      op1mode = HImode;
+      op1 = gen_lowpart (HImode, op1);
+    }
+  
+  /* In case the insn wants input operands in modes different from
+     the result, abort.  */
+  
+  gcc_assert ((op0mode == mode0 || op0mode == VOIDmode)
+              && (op1mode == mode1 || op1mode == VOIDmode));
+
+  if (! (*insn_data[icode].operand[1].predicate) (op0, mode0))
+    op0 = copy_to_mode_reg (mode0, op0);
+  
+  if (! (*insn_data[icode].operand[2].predicate) (op1, mode1))
+    op1 = copy_to_mode_reg (mode1, op1);
+
+  pat = GEN_FCN (icode) (target, op0, op1);
+  
+  if (! pat)
+    return 0;
+
+  emit_insn (pat);
+  return target;
+}
+
+
+/* Expand an expression EXP that calls a built-in function,
+   with result going to TARGET if that's convenient
+   (and in mode MODE if that's convenient).
+   SUBTARGET may be used as the target for computing one of EXP's operands.
+   IGNORE is nonzero if the value is to be ignored.  */
+
+static rtx
+avr_expand_builtin (tree exp, rtx target,
+                    rtx subtarget ATTRIBUTE_UNUSED,
+                    enum machine_mode mode ATTRIBUTE_UNUSED,
+                    int ignore ATTRIBUTE_UNUSED)
+{
+  size_t i;
+  const struct avr_builtin_description *d;
+  tree fndecl = TREE_OPERAND (CALL_EXPR_FN (exp), 0);
+  unsigned int id = DECL_FUNCTION_CODE (fndecl);
+  tree arg0;
+  rtx op0;
+
+  switch (id)
+    {
+    case AVR_BUILTIN_NOP:
+      emit_insn (gen_nopv (GEN_INT(1)));
+      return 0;
+      
+    case AVR_BUILTIN_SEI:
+      emit_insn (gen_enable_interrupt ());
+      return 0;
+      
+    case AVR_BUILTIN_CLI:
+      emit_insn (gen_disable_interrupt ());
+      return 0;
+      
+    case AVR_BUILTIN_WDR:
+      emit_insn (gen_wdr ());
+      return 0;
+      
+    case AVR_BUILTIN_SLEEP:
+      emit_insn (gen_sleep ());
+      return 0;
+      
+    case AVR_BUILTIN_DELAY_CYCLES:
+      {
+        arg0 = CALL_EXPR_ARG (exp, 0);
+        op0 = expand_expr (arg0, NULL_RTX, VOIDmode, EXPAND_NORMAL);
+
+        if (! CONST_INT_P (op0))
+          error ("__builtin_avr_delay_cycles expects a compile time integer constant.");
+
+        avr_expand_delay_cycles (op0);
+        return 0;
+      }
+    }
+
+  for (i = 0, d = bdesc_1arg; i < ARRAY_SIZE (bdesc_1arg); i++, d++)
+    if (d->id == id)
+      return avr_expand_unop_builtin (d->icode, exp, target);
+
+  for (i = 0, d = bdesc_2arg; i < ARRAY_SIZE (bdesc_2arg); i++, d++)
+    if (d->id == id)
+      return avr_expand_binop_builtin (d->icode, exp, target);
+
+  gcc_unreachable ();
+}
+
+
 #include "gt-avr.h"