OSDN Git Service

Fix clearing ZERO_REG
[pf3gnuchains/gcc-fork.git] / gcc / config / avr / avr.c
index 128706d..4830632 100644 (file)
@@ -35,6 +35,7 @@
 #include "tree.h"
 #include "output.h"
 #include "expr.h"
+#include "c-family/c-common.h"
 #include "diagnostic-core.h"
 #include "obstack.h"
 #include "function.h"
 /* Maximal allowed offset for an address in the LD command */
 #define MAX_LD_OFFSET(MODE) (64 - (signed)GET_MODE_SIZE (MODE))
 
-static void avr_option_override (void);
+/* Return true if STR starts with PREFIX and false, otherwise.  */
+#define STR_PREFIX_P(STR,PREFIX) (0 == strncmp (STR, PREFIX, strlen (PREFIX)))
+
+/* The 4 bits starting at SECTION_MACH_DEP are reserved to store the
+   address space where data is to be located.
+   As the only non-generic address spaces are all located in Flash,
+   this can be used to test if data shall go into some .progmem* section.
+   This must be the rightmost field of machine dependent section flags.  */
+#define AVR_SECTION_PROGMEM (0xf * SECTION_MACH_DEP)
+
+/* Known address spaces.  The order must be the same as in the respective
+   enum from avr.h (or designated initialized must be used).  */
+const avr_addrspace_t avr_addrspace[] =
+{
+    { ADDR_SPACE_RAM,  0, 2, ""     ,   0 },
+    { ADDR_SPACE_PGM,  1, 2, "__pgm",   0 },
+    { ADDR_SPACE_PGM1, 1, 2, "__pgm1",  1 },
+    { ADDR_SPACE_PGM2, 1, 2, "__pgm2",  2 },
+    { ADDR_SPACE_PGM3, 1, 2, "__pgm3",  3 },
+    { ADDR_SPACE_PGM4, 1, 2, "__pgm4",  4 },
+    { ADDR_SPACE_PGM5, 1, 2, "__pgm5",  5 },
+    { ADDR_SPACE_PGMX, 1, 3, "__pgmx",  0 },
+    { 0              , 0, 0, NULL,      0 }
+};
+
+/* Map 64-k Flash segment to section prefix.  */
+static const char* const progmem_section_prefix[6] =
+  {
+    ".progmem.data",
+    ".progmem1.data",
+    ".progmem2.data",
+    ".progmem3.data",
+    ".progmem4.data",
+    ".progmem5.data"
+  };
+
+
+/* Prototypes for local helper functions.  */
+
+static const char* out_movqi_r_mr (rtx, rtx[], int*);
+static const char* out_movhi_r_mr (rtx, rtx[], int*);
+static const char* out_movsi_r_mr (rtx, rtx[], int*);
+static const char* out_movqi_mr_r (rtx, rtx[], int*);
+static const char* out_movhi_mr_r (rtx, rtx[], int*);
+static const char* out_movsi_mr_r (rtx, rtx[], int*);
+
 static int avr_naked_function_p (tree);
 static int interrupt_function_p (tree);
 static int signal_function_p (tree);
@@ -63,62 +109,42 @@ 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, const_tree);
-
-static RTX_CODE compare_condition (rtx insn);
-static rtx avr_legitimize_address (rtx, rtx, enum machine_mode);
-static int compare_sign_p (rtx insn);
-static tree avr_handle_progmem_attribute (tree *, tree, tree, int, bool *);
-static tree avr_handle_fndecl_attribute (tree *, tree, tree, int, bool *);
-static tree avr_handle_fntype_attribute (tree *, tree, tree, int, bool *);
-static bool avr_assemble_integer (rtx, unsigned int, int);
-static void avr_file_start (void);
-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);
-
-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 int avr_operand_rtx_cost (rtx, enum machine_mode, enum rtx_code,
+                                 int, bool);
+static void output_reload_in_const (rtx*, rtx, int*, bool);
 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_t , enum machine_mode,
-                            const_tree, bool);
-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);
+
+
+/* Prototypes for hook implementors if needed before their implementation.  */
+
+static bool avr_rtx_costs (rtx, int, int, int, int *, bool);
+
 
 /* Allocate registers from r25 to r8 for parameters for function calls.  */
 #define FIRST_CUM_REG 26
 
+/* Implicit target register of LPM instruction (R0) */
+static GTY(()) rtx lpm_reg_rtx;
+
+/* (Implicit) address register of LPM instruction (R31:R30 = Z) */
+static GTY(()) rtx lpm_addr_reg_rtx;
+
 /* Temporary register RTX (gen_rtx_REG (QImode, TMP_REGNO)) */
 static GTY(()) rtx tmp_reg_rtx;
 
 /* Zeroed register RTX (gen_rtx_REG (QImode, ZERO_REGNO)) */
 static GTY(()) rtx zero_reg_rtx;
 
+/* RAMPZ special function register */
+static GTY(()) rtx rampz_rtx;
+
+/* RTX containing the strings "" and "e", respectively */
+static GTY(()) rtx xstring_empty;
+static GTY(()) rtx xstring_e;
+
+/* RTXs for all general purpose registers as QImode */
+static GTY(()) rtx all_regs_rtx[32];
+
 /* AVR register names {"r0", "r1", ..., "r31"} */
 static const char *const avr_regnames[] = REGISTER_NAMES;
 
@@ -131,31 +157,20 @@ const struct base_arch_s *avr_current_arch;
 /* Current device.  */
 const struct mcu_type_s *avr_current_device;
 
-section *progmem_section;
+/* Section to put switch tables in.  */
+static GTY(()) section *progmem_swtable_section;
+
+/* Unnamed sections associated to __attribute__((progmem)) aka. PROGMEM
+   or to address space __pgm*.  */
+static GTY(()) section *progmem_section[6];
+
+/* Condition for insns/expanders from avr-dimode.md.  */
+bool avr_have_dimode = true;
 
 /* 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,
-       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.  */
 #undef TARGET_ASM_ALIGNED_HI_OP
@@ -187,19 +202,19 @@ static const struct attribute_spec avr_attribute_table[] =
 
 #undef TARGET_ATTRIBUTE_TABLE
 #define TARGET_ATTRIBUTE_TABLE avr_attribute_table
-#undef TARGET_ASM_FUNCTION_RODATA_SECTION
-#define TARGET_ASM_FUNCTION_RODATA_SECTION default_no_function_rodata_section
 #undef TARGET_INSERT_ATTRIBUTES
 #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_NAMED_SECTION
+#define TARGET_ASM_NAMED_SECTION avr_asm_named_section
 #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_ASM_SELECT_SECTION
+#define TARGET_ASM_SELECT_SECTION avr_asm_select_section
 
 #undef TARGET_REGISTER_MOVE_COST
 #define TARGET_REGISTER_MOVE_COST avr_register_move_cost
@@ -216,9 +231,6 @@ static const struct attribute_spec avr_attribute_table[] =
 #undef TARGET_FUNCTION_ARG_ADVANCE
 #define TARGET_FUNCTION_ARG_ADVANCE avr_function_arg_advance
 
-#undef TARGET_LEGITIMIZE_ADDRESS
-#define TARGET_LEGITIMIZE_ADDRESS avr_legitimize_address
-
 #undef TARGET_RETURN_IN_MEMORY
 #define TARGET_RETURN_IN_MEMORY avr_return_in_memory
 
@@ -233,9 +245,6 @@ static const struct attribute_spec avr_attribute_table[] =
 #undef TARGET_CASE_VALUES_THRESHOLD
 #define TARGET_CASE_VALUES_THRESHOLD avr_case_values_threshold
 
-#undef TARGET_LEGITIMATE_ADDRESS_P
-#define TARGET_LEGITIMATE_ADDRESS_P avr_legitimate_address_p
-
 #undef TARGET_FRAME_POINTER_REQUIRED
 #define TARGET_FRAME_POINTER_REQUIRED avr_frame_pointer_required_p
 #undef TARGET_CAN_ELIMINATE
@@ -259,39 +268,143 @@ static const struct attribute_spec avr_attribute_table[] =
 #undef TARGET_EXPAND_BUILTIN
 #define TARGET_EXPAND_BUILTIN avr_expand_builtin
 
+#undef TARGET_ASM_FUNCTION_RODATA_SECTION
+#define TARGET_ASM_FUNCTION_RODATA_SECTION avr_asm_function_rodata_section
+
+#undef  TARGET_SCALAR_MODE_SUPPORTED_P
+#define TARGET_SCALAR_MODE_SUPPORTED_P avr_scalar_mode_supported_p
+
+#undef  TARGET_ADDR_SPACE_SUBSET_P
+#define TARGET_ADDR_SPACE_SUBSET_P avr_addr_space_subset_p
+
+#undef  TARGET_ADDR_SPACE_CONVERT
+#define TARGET_ADDR_SPACE_CONVERT avr_addr_space_convert
+
+#undef  TARGET_ADDR_SPACE_ADDRESS_MODE
+#define TARGET_ADDR_SPACE_ADDRESS_MODE avr_addr_space_address_mode
+
+#undef  TARGET_ADDR_SPACE_POINTER_MODE
+#define TARGET_ADDR_SPACE_POINTER_MODE avr_addr_space_pointer_mode
+
+#undef  TARGET_ADDR_SPACE_LEGITIMATE_ADDRESS_P
+#define TARGET_ADDR_SPACE_LEGITIMATE_ADDRESS_P avr_addr_space_legitimate_address_p
+
+#undef TARGET_ADDR_SPACE_LEGITIMIZE_ADDRESS
+#define TARGET_ADDR_SPACE_LEGITIMIZE_ADDRESS avr_addr_space_legitimize_address
+
+#undef  TARGET_PRINT_OPERAND
+#define TARGET_PRINT_OPERAND avr_print_operand
+#undef  TARGET_PRINT_OPERAND_ADDRESS
+#define TARGET_PRINT_OPERAND_ADDRESS avr_print_operand_address
+#undef  TARGET_PRINT_OPERAND_PUNCT_VALID_P
+#define TARGET_PRINT_OPERAND_PUNCT_VALID_P avr_print_operand_punct_valid_p
 
-struct gcc_target targetm = TARGET_INITIALIZER;
 \f
+
+/* Custom function to replace string prefix.
+
+   Return a ggc-allocated string with strlen (OLD_PREFIX) characters removed
+   from the start of OLD_STR and then prepended with NEW_PREFIX.  */
+
+static inline const char*
+avr_replace_prefix (const char *old_str,
+                    const char *old_prefix, const char *new_prefix)
+{
+  char *new_str;
+  size_t len = strlen (old_str) + strlen (new_prefix) - strlen (old_prefix);
+
+  gcc_assert (strlen (old_prefix) <= strlen (old_str));
+
+  /* Unfortunately, ggc_alloc_string returns a const char* and thus cannot be
+     used here.  */
+     
+  new_str = (char*) ggc_alloc_atomic (1 + len);
+
+  strcat (stpcpy (new_str, new_prefix), old_str + strlen (old_prefix));
+  
+  return (const char*) new_str;
+}
+
+
+/* Custom function to count number of set bits.  */
+
+static inline int
+avr_popcount (unsigned int val)
+{
+  int pop = 0;
+
+  while (val)
+    {
+      val &= val-1;
+      pop++;
+    }
+
+  return pop;
+}
+
+
+/* Constraint helper function.  XVAL is a CONST_INT or a CONST_DOUBLE.
+   Return true if the least significant N_BYTES bytes of XVAL all have a
+   popcount in POP_MASK and false, otherwise.  POP_MASK represents a subset
+   of integers which contains an integer N iff bit N of POP_MASK is set.  */
+   
+bool
+avr_popcount_each_byte (rtx xval, int n_bytes, int pop_mask)
+{
+  int i;
+
+  enum machine_mode mode = GET_MODE (xval);
+
+  if (VOIDmode == mode)
+    mode = SImode;
+
+  for (i = 0; i < n_bytes; i++)
+    {
+      rtx xval8 = simplify_gen_subreg (QImode, xval, mode, i);
+      unsigned int val8 = UINTVAL (xval8) & GET_MODE_MASK (QImode);
+
+      if (0 == (pop_mask & (1 << avr_popcount (val8))))
+        return false;
+    }
+
+  return true;
+}
+
 static void
 avr_option_override (void)
 {
   flag_delete_null_pointer_checks = 0;
 
+  /* caller-save.c looks for call-clobbered hard registers that are assigned
+     to pseudos that cross calls and tries so save-restore them around calls
+     in order to reduce the number of stack slots needed.
+
+     This might leads to situations where reload is no more able to cope
+     with the challenge of AVR's very few address registers and fails to
+     perform the requested spills.  */
+  
+  if (avr_strict_X)
+    flag_caller_saves = 0;
+
+  /* Unwind tables currently require a frame pointer for correctness,
+     see toplev.c:process_options().  */
+
+  if ((flag_unwind_tables
+       || flag_non_call_exceptions
+       || flag_asynchronous_unwind_tables)
+      && !ACCUMULATE_OUTGOING_ARGS)
+    {
+      flag_omit_frame_pointer = 0;
+    }
+
   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;
 
-  tmp_reg_rtx  = gen_rtx_REG (QImode, TMP_REGNO);
-  zero_reg_rtx = gen_rtx_REG (QImode, ZERO_REGNO);
-
   init_machine_status = avr_init_machine_status;
-}
 
-/*  return register class from register number.  */
-
-static const enum reg_class reg_class_tab[]={
-  GENERAL_REGS,GENERAL_REGS,GENERAL_REGS,GENERAL_REGS,GENERAL_REGS,
-  GENERAL_REGS,GENERAL_REGS,GENERAL_REGS,GENERAL_REGS,GENERAL_REGS,
-  GENERAL_REGS,GENERAL_REGS,GENERAL_REGS,GENERAL_REGS,GENERAL_REGS,
-  GENERAL_REGS, /* r0 - r15 */
-  LD_REGS,LD_REGS,LD_REGS,LD_REGS,LD_REGS,LD_REGS,LD_REGS,
-  LD_REGS,                      /* r16 - 23 */
-  ADDW_REGS,ADDW_REGS,          /* r24,r25 */
-  POINTER_X_REGS,POINTER_X_REGS, /* r26,27 */
-  POINTER_Y_REGS,POINTER_Y_REGS, /* r28,r29 */
-  POINTER_Z_REGS,POINTER_Z_REGS, /* r30,r31 */
-  STACK_REG,STACK_REG           /* SPL,SPH */
-};
+  avr_log_set_avr_log();
+}
 
 /* Function to set up the backend function structure.  */
 
@@ -301,16 +414,135 @@ avr_init_machine_status (void)
   return ggc_alloc_cleared_machine_function ();
 }
 
+
+/* Implement `INIT_EXPANDERS'.  */
+/* The function works like a singleton.  */
+
+void
+avr_init_expanders (void)
+{
+  int regno;
+
+  static bool done = false;
+
+  if (done)
+    return;
+  else
+    done = true;
+
+  for (regno = 0; regno < 32; regno ++)
+    all_regs_rtx[regno] = gen_rtx_REG (QImode, regno);
+
+  lpm_reg_rtx  = all_regs_rtx[LPM_REGNO];
+  tmp_reg_rtx  = all_regs_rtx[TMP_REGNO];
+  zero_reg_rtx = all_regs_rtx[ZERO_REGNO];
+
+  lpm_addr_reg_rtx = gen_rtx_REG (HImode, REG_Z);
+
+  rampz_rtx = gen_rtx_MEM (QImode, GEN_INT (RAMPZ_ADDR));
+
+  xstring_empty = gen_rtx_CONST_STRING (VOIDmode, "");
+  xstring_e = gen_rtx_CONST_STRING (VOIDmode, "e");
+}
+
+
 /* Return register class for register R.  */
 
 enum reg_class
 avr_regno_reg_class (int r)
 {
+  static const enum reg_class reg_class_tab[] =
+    {
+      R0_REG,
+      /* r1 - r15 */
+      NO_LD_REGS, NO_LD_REGS, NO_LD_REGS,
+      NO_LD_REGS, NO_LD_REGS, NO_LD_REGS, NO_LD_REGS,
+      NO_LD_REGS, NO_LD_REGS, NO_LD_REGS, NO_LD_REGS,
+      NO_LD_REGS, NO_LD_REGS, NO_LD_REGS, NO_LD_REGS,
+      /* r16 - r23 */
+      SIMPLE_LD_REGS, SIMPLE_LD_REGS, SIMPLE_LD_REGS, SIMPLE_LD_REGS,
+      SIMPLE_LD_REGS, SIMPLE_LD_REGS, SIMPLE_LD_REGS, SIMPLE_LD_REGS,
+      /* r24, r25 */
+      ADDW_REGS, ADDW_REGS,
+      /* X: r26, 27 */
+      POINTER_X_REGS, POINTER_X_REGS,
+      /* Y: r28, r29 */
+      POINTER_Y_REGS, POINTER_Y_REGS,
+      /* Z: r30, r31 */
+      POINTER_Z_REGS, POINTER_Z_REGS,
+      /* SP: SPL, SPH */
+      STACK_REG, STACK_REG
+    };
+
   if (r <= 33)
     return reg_class_tab[r];
+  
   return ALL_REGS;
 }
 
+
+static bool
+avr_scalar_mode_supported_p (enum machine_mode mode)
+{
+  if (PSImode == mode)
+    return true;
+
+  return default_scalar_mode_supported_p (mode);
+}
+
+
+/* Return TRUE if DECL is a VAR_DECL located in Flash and FALSE, otherwise.  */
+
+static bool
+avr_decl_pgm_p (tree decl)
+{
+  if (TREE_CODE (decl) != VAR_DECL
+      || TREE_TYPE (decl) == error_mark_node)
+    {
+      return false;
+    }
+
+  return !ADDR_SPACE_GENERIC_P (TYPE_ADDR_SPACE (TREE_TYPE (decl)));
+}
+
+
+/* Return TRUE if DECL is a VAR_DECL located in the 24-bit Flash
+   address space and FALSE, otherwise.  */
+static bool
+avr_decl_pgmx_p (tree decl)
+{
+  if (TREE_CODE (decl) != VAR_DECL
+      || TREE_TYPE (decl) == error_mark_node)
+    {
+      return false;
+    }
+
+  return (ADDR_SPACE_PGMX == TYPE_ADDR_SPACE (TREE_TYPE (decl)));
+}
+
+
+/* Return TRUE if X is a MEM rtx located in Flash and FALSE, otherwise.  */
+
+bool
+avr_mem_pgm_p (rtx x)
+{
+  return (MEM_P (x)
+          && !ADDR_SPACE_GENERIC_P (MEM_ADDR_SPACE (x)));
+}
+
+
+/* Return TRUE if X is a MEM rtx located in the 24-bit Flash
+   address space and FALSE, otherwise.  */
+
+bool
+avr_mem_pgmx_p (rtx x)
+{
+  return (MEM_P (x)
+          && ADDR_SPACE_PGMX == MEM_ADDR_SPACE (x));
+}
+
+
 /* A helper for the subsequent function attribute used to dig for
    attribute 'name' in a FUNCTION_DECL or FUNCTION_TYPE */
 
@@ -359,7 +591,7 @@ signal_function_p (tree func)
   return avr_lookup_function_attribute1 (func, "signal");
 }
 
-/* Return nonzero if FUNC is a OS_task function.  */
+/* Return nonzero if FUNC is an OS_task function.  */
 
 static int
 avr_OS_task_function_p (tree func)
@@ -367,7 +599,7 @@ avr_OS_task_function_p (tree func)
   return avr_lookup_function_attribute1 (func, "OS_task");
 }
 
-/* Return nonzero if FUNC is a OS_main function.  */
+/* Return nonzero if FUNC is an OS_main function.  */
 
 static int
 avr_OS_main_function_p (tree func)
@@ -375,6 +607,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.  */
 
@@ -383,14 +656,14 @@ 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);
   count = 0;
 
   /* No need to save any registers if the function never returns or 
-     is have "OS_task" or "OS_main" attribute.  */
+     has the "OS_task" or "OS_main" attribute.  */
   if (TREE_THIS_VOLATILE (current_function_decl)
       || cfun->machine->is_OS_task
       || cfun->machine->is_OS_main)
@@ -399,33 +672,36 @@ 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;
 }
 
 /* Return true if register FROM can be eliminated via register TO.  */
 
-bool
+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.  */
@@ -439,9 +715,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);
     }
 }
 
@@ -450,10 +727,11 @@ avr_initial_elimination_offset (int from, int to)
    Using saved frame = virtual_stack_vars_rtx - STARTING_FRAME_OFFSET
    avoids creating add/sub of offset in nonlocal goto and setjmp.  */
 
-rtx avr_builtin_setjmp_frame_value (void)
+static rtx
+avr_builtin_setjmp_frame_value (void)
 {
   return gen_rtx_MINUS (Pmode, virtual_stack_vars_rtx, 
-                        gen_int_mode (STARTING_FRAME_OFFSET, Pmode));
+                        gen_int_mode (STARTING_FRAME_OFFSET, Pmode));
 }
 
 /* Return contents of MEM at frame pointer + stack size + 1 (+2 if 3 byte PC).
@@ -463,7 +741,7 @@ avr_return_addr_rtx (int count, rtx tem)
 {
   rtx r;
     
-  /* Can only return this functions return address. Others not supported.  */
+  /* Can only return this function's return address. Others not supported.  */
   if (count)
      return NULL;
 
@@ -487,12 +765,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.  */
@@ -506,6 +785,17 @@ sequent_regs_live (void)
 
   for (reg = 0; reg < 18; ++reg)
     {
+      if (fixed_regs[reg])
+        {
+          /* Don't recognize sequences that contain global register
+             variables.  */
+      
+          if (live_seq != 0)
+            return 0;
+          else
+            continue;
+        }
+      
       if (!call_used_regs[reg])
        {
          if (df_regs_ever_live_p (reg))
@@ -586,248 +876,291 @@ emit_push_byte (unsigned regno, bool frame_related_p)
   cfun->machine->stack_usage++;
 }
 
-
-/*  Output function prologue.  */
-
-void
-expand_prologue (void)
+static void
+avr_prologue_setup_frame (HOST_WIDE_INT size, HARD_REG_SET set)
 {
-  int live_seq;
-  HARD_REG_SET set;
-  int minimize;
-  HOST_WIDE_INT size = get_frame_size();
   rtx insn;
+  bool isr_p = cfun->machine->is_interrupt || cfun->machine->is_signal;
+  int live_seq = sequent_regs_live ();
+
+  bool minimize = (TARGET_CALL_PROLOGUES
+                   && live_seq
+                   && !isr_p
+                   && !cfun->machine->is_OS_task
+                   && !cfun->machine->is_OS_main);
   
-  /* Init cfun->machine.  */
-  cfun->machine->is_naked = avr_naked_function_p (current_function_decl);
-  cfun->machine->is_interrupt = interrupt_function_p (current_function_decl);
-  cfun->machine->is_signal = signal_function_p (current_function_decl);
-  cfun->machine->is_OS_task = avr_OS_task_function_p (current_function_decl);
-  cfun->machine->is_OS_main = avr_OS_main_function_p (current_function_decl);
-  cfun->machine->stack_usage = 0;
-  
-  /* Prologue: naked.  */
-  if (cfun->machine->is_naked)
-    {
-      return;
-    }
-
-  avr_regs_to_save (&set);
-  live_seq = sequent_regs_live ();
-  minimize = (TARGET_CALL_PROLOGUES
-             && !cfun->machine->is_interrupt
-             && !cfun->machine->is_signal
-             && !cfun->machine->is_OS_task
-             && !cfun->machine->is_OS_main
-             && live_seq);
-
-  if (cfun->machine->is_interrupt || cfun->machine->is_signal)
-    {
-      /* Enable interrupts.  */
-      if (cfun->machine->is_interrupt)
-       emit_insn (gen_enable_interrupt ());
-       
-      /* Push zero reg.  */
-      emit_push_byte (ZERO_REGNO, true);
-
-      /* Push tmp reg.  */
-      emit_push_byte (TMP_REGNO, true);
-
-      /* Push SREG.  */
-      /* ??? There's no dwarf2 column reserved for SREG.  */
-      emit_move_insn (tmp_reg_rtx, gen_rtx_MEM (QImode, GEN_INT (SREG_ADDR)));
-      emit_push_byte (TMP_REGNO, false);
-
-      /* Push RAMPZ.  */
-      /* ??? There's no dwarf2 column reserved for RAMPZ.  */
-      if (AVR_HAVE_RAMPZ 
-          && TEST_HARD_REG_BIT (set, REG_Z)
-          && TEST_HARD_REG_BIT (set, REG_Z + 1))
-        {
-          emit_move_insn (tmp_reg_rtx,
-                         gen_rtx_MEM (QImode, GEN_INT (RAMPZ_ADDR)));
-         emit_push_byte (TMP_REGNO, false);
-        }
-       
-      /* Clear zero reg.  */
-      emit_move_insn (zero_reg_rtx, const0_rtx);
-
-      /* Prevent any attempt to delete the setting of ZERO_REG!  */
-      emit_use (zero_reg_rtx);
-    }
-  if (minimize && (frame_pointer_needed 
-                  || (AVR_2_BYTE_PC && live_seq > 6)
-                  || live_seq > 7)) 
+  if (minimize
+      && (frame_pointer_needed
+          || avr_outgoing_args_size() > 8
+          || (AVR_2_BYTE_PC && live_seq > 6)
+          || live_seq > 7)) 
     {
+      rtx pattern;
       int first_reg, reg, offset;
 
       emit_move_insn (gen_rtx_REG (HImode, REG_X), 
                       gen_int_mode (size, HImode));
 
-      insn = emit_insn (gen_call_prologue_saves
-                       (gen_int_mode (live_seq, HImode),
-                        gen_int_mode (size + live_seq, HImode)));
+      pattern = gen_call_prologue_saves (gen_int_mode (live_seq, HImode),
+                                         gen_int_mode (live_seq+size, HImode));
+      insn = emit_insn (pattern);
       RTX_FRAME_RELATED_P (insn) = 1;
 
       /* Describe the effect of the unspec_volatile call to prologue_saves.
-        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.  */
+         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.  */
+         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))));
+                    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.  */
+         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;
+           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));
-       }
+          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
+  else /* !minimize */
     {
       int reg;
+      
       for (reg = 0; reg < 32; ++reg)
         if (TEST_HARD_REG_BIT (set, reg))
-         emit_push_byte (reg, true);
+          emit_push_byte (reg, true);
 
-      if (frame_pointer_needed)
+      if (frame_pointer_needed
+          && (!(cfun->machine->is_OS_task || cfun->machine->is_OS_main)))
         {
-         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);
-           }
+          /* 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);
 
-          if (!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)
             {
-              insn = emit_move_insn (frame_pointer_rtx, stack_pointer_rtx);
-              RTX_FRAME_RELATED_P (insn) = 1;
+              /* The high byte (r29) does not change:
+                 Prefer SUBI (1 cycle) over ABIW (2 cycles, same size).  */
+
+              my_fp = all_regs_rtx[FRAME_POINTER_REGNUM];
+            }
+
+          /************  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
             {
-              /*  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; 
+              insn = emit_move_insn (stack_pointer_rtx, fp);
+            }
 
-              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 
+          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)
                 {
-                  /*  Normal sized addition.  */
-                  myfp = frame_pointer_rtx;
+                  insn = emit_move_insn (fp, stack_pointer_rtx);
+                  RTX_FRAME_RELATED_P (insn) = 1;
                 }
 
-             /* Method 1-Adjust frame pointer.  */
-             start_sequence ();
+              sp_plus_insns = get_insns ();
+              end_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.  */
+              /************ 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);
+            }
 
-              emit_move_insn (frame_pointer_rtx, stack_pointer_rtx);
+          cfun->machine->stack_usage += size;
+        } /* !minimize && size != 0 */
+    } /* !minimize */
+}
 
-              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 ();
+/*  Output function prologue.  */
 
-             /* Method 2-Adjust Stack pointer.  */
-              if (size <= 6)
-                {
-                 rtx sp_plus_insns;
+void
+expand_prologue (void)
+{
+  HARD_REG_SET set;
+  HOST_WIDE_INT size;
+
+  size = get_frame_size() + avr_outgoing_args_size();
+  
+  /* Init cfun->machine.  */
+  cfun->machine->is_naked = avr_naked_function_p (current_function_decl);
+  cfun->machine->is_interrupt = interrupt_function_p (current_function_decl);
+  cfun->machine->is_signal = signal_function_p (current_function_decl);
+  cfun->machine->is_OS_task = avr_OS_task_function_p (current_function_decl);
+  cfun->machine->is_OS_main = avr_OS_main_function_p (current_function_decl);
+  cfun->machine->stack_usage = 0;
+  
+  /* Prologue: naked.  */
+  if (cfun->machine->is_naked)
+    {
+      return;
+    }
 
-                 start_sequence ();
+  avr_regs_to_save (&set);
 
-                 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;
+  if (cfun->machine->is_interrupt || cfun->machine->is_signal)
+    {
+      /* Enable interrupts.  */
+      if (cfun->machine->is_interrupt)
+        emit_insn (gen_enable_interrupt ());
+        
+      /* Push zero reg.  */
+      emit_push_byte (ZERO_REGNO, true);
 
-                 sp_plus_insns = get_insns ();
-                 end_sequence ();
+      /* Push tmp reg.  */
+      emit_push_byte (TMP_REGNO, true);
 
-                 /* 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);
+      /* Push SREG.  */
+      /* ??? There's no dwarf2 column reserved for SREG.  */
+      emit_move_insn (tmp_reg_rtx, gen_rtx_MEM (QImode, GEN_INT (SREG_ADDR)));
+      emit_push_byte (TMP_REGNO, false);
 
-             cfun->machine->stack_usage += size;
-            }
+      /* Push RAMPZ.  */
+      /* ??? There's no dwarf2 column reserved for RAMPZ.  */
+      if (AVR_HAVE_RAMPZ 
+          && TEST_HARD_REG_BIT (set, REG_Z)
+          && TEST_HARD_REG_BIT (set, REG_Z + 1))
+        {
+          emit_move_insn (tmp_reg_rtx, rampz_rtx);
+          emit_push_byte (TMP_REGNO, false);
         }
+        
+      /* Clear zero reg.  */
+      emit_move_insn (zero_reg_rtx, const0_rtx);
+
+      /* Prevent any attempt to delete the setting of ZERO_REG!  */
+      emit_use (zero_reg_rtx);
     }
 
+  avr_prologue_setup_frame (size, set);
+  
   if (flag_stack_usage_info)
     current_function_static_stack_size = cfun->machine->stack_usage;
 }
@@ -854,6 +1187,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",
@@ -899,7 +1237,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)
@@ -912,145 +1253,157 @@ 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.  */
-         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 = all_regs_rtx[FRAME_POINTER_REGNUM];
+        }
+              
+      /********** 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);
+        }
+
+      fp_plus_insns = get_insns ();
+      end_sequence ();        
+
+      /********** Method 2: Adjust Stack pointer  **********/
+      
+      if (avr_sp_immediate_operand (gen_int_mode (size, HImode), HImode))
+        {
+          rtx sp_plus_insns;
 
-                 start_sequence ();
+          start_sequence ();
 
-                 emit_move_insn (stack_pointer_rtx,
-                                 plus_constant (stack_pointer_rtx, size));
+          emit_move_insn (stack_pointer_rtx,
+                          plus_constant (stack_pointer_rtx, size));
 
-                 sp_plus_insns = get_insns ();
-                 end_sequence ();
+          sp_plus_insns = get_insns ();
+          end_sequence ();
 
-                 /* Use shortest method.  */
-                 if (get_sequence_length (sp_plus_insns) 
-                     < get_sequence_length (fp_plus_insns))
-                   emit_insn (sp_plus_insns);
-                 else
-                   emit_insn (fp_plus_insns);
-                }
-             else
-               emit_insn (fp_plus_insns);
-            }
-         if (!(cfun->machine->is_OS_task || cfun->machine->is_OS_main))
-           {
-              /* Restore previous frame_pointer.  See expand_prologue for
-                rationale for not using pophi.  */
-             emit_pop_byte (REG_Y + 1);
-             emit_pop_byte (REG_Y);
-           }
-       }
+          /************ 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 used registers.  */
-      for (reg = 31; reg >= 0; --reg)
-        if (TEST_HARD_REG_BIT (set, reg))
-          emit_pop_byte (reg);
+  /* Restore used registers.  */
+  
+  for (reg = 31; reg >= 0; --reg)
+    if (TEST_HARD_REG_BIT (set, reg))
+      emit_pop_byte (reg);
 
-      if (cfun->machine->is_interrupt || cfun->machine->is_signal)
+  if (isr_p)
+    {
+      /* Restore RAMPZ using tmp reg as scratch.  */
+      
+      if (AVR_HAVE_RAMPZ 
+          && TEST_HARD_REG_BIT (set, REG_Z)
+          && TEST_HARD_REG_BIT (set, REG_Z + 1))
         {
-          /* 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);
-           }
-
-          /* 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);
+          emit_move_insn (rampz_rtx, tmp_reg_rtx);
+        }
 
-          /* Restore tmp REG.  */
-          emit_pop_byte (TMP_REGNO);
+      /* 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 zero REG.  */
-          emit_pop_byte (ZERO_REGNO);
-        }
+      /* Restore tmp REG.  */
+      emit_pop_byte (TMP_REGNO);
 
-      if (!sibcall_p)
-        emit_jump_insn (gen_return ());
+      /* Restore zero REG.  */
+      emit_pop_byte (ZERO_REGNO);
     }
+
+  if (!sibcall_p)
+    emit_jump_insn (gen_return ());
 }
 
 /* Output summary messages at beginning of function epilogue.  */
@@ -1082,120 +1435,246 @@ avr_cannot_modify_jumps_p (void)
 }
 
 
+/* Helper function for `avr_legitimate_address_p'.  */
+
+static inline bool
+avr_reg_ok_for_addr_p (rtx reg, addr_space_t as,
+                       RTX_CODE outer_code, bool strict)
+{
+  return (REG_P (reg)
+          && (avr_regno_mode_code_ok_for_base_p (REGNO (reg), QImode,
+                                                 as, outer_code, UNKNOWN)
+              || (!strict
+                  && REGNO (reg) >= FIRST_PSEUDO_REGISTER)));
+}
+
+
 /* Return nonzero if X (an RTX) is a legitimate memory address on the target
    machine for a memory operand of mode MODE.  */
 
-bool
+static bool
 avr_legitimate_address_p (enum machine_mode mode, rtx x, bool strict)
 {
-  enum reg_class r = NO_REGS;
+  bool ok = CONSTANT_ADDRESS_P (x);
   
-  if (TARGET_ALL_DEBUG)
-    {
-      fprintf (stderr, "mode: (%s) %s %s %s %s:",
-              GET_MODE_NAME(mode),
-              strict ? "(strict)": "",
-              reload_completed ? "(reload_completed)": "",
-              reload_in_progress ? "(reload_in_progress)": "",
-              reg_renumber ? "(reg_renumber)" : "");
-      if (GET_CODE (x) == PLUS
-         && REG_P (XEXP (x, 0))
-         && GET_CODE (XEXP (x, 1)) == CONST_INT
-         && INTVAL (XEXP (x, 1)) >= 0
-         && INTVAL (XEXP (x, 1)) <= MAX_LD_OFFSET (mode)
-         && reg_renumber
-         )
-       fprintf (stderr, "(r%d ---> r%d)", REGNO (XEXP (x, 0)),
-                true_regnum (XEXP (x, 0)));
-      debug_rtx (x);
-    }
-  
-  if (REG_P (x) && (strict ? REG_OK_FOR_BASE_STRICT_P (x)
-                    : REG_OK_FOR_BASE_NOSTRICT_P (x)))
-    r = POINTER_REGS;
-  else if (CONSTANT_ADDRESS_P (x))
-    r = ALL_REGS;
-  else if (GET_CODE (x) == PLUS
-           && REG_P (XEXP (x, 0))
-          && GET_CODE (XEXP (x, 1)) == CONST_INT
-          && INTVAL (XEXP (x, 1)) >= 0)
-    {
-      int fit = INTVAL (XEXP (x, 1)) <= MAX_LD_OFFSET (mode);
-      if (fit)
-       {
-         if (! strict
-             || REGNO (XEXP (x,0)) == REG_X
-             || REGNO (XEXP (x,0)) == REG_Y
-             || REGNO (XEXP (x,0)) == REG_Z)
-           r = BASE_POINTER_REGS;
-         if (XEXP (x,0) == frame_pointer_rtx
-             || XEXP (x,0) == arg_pointer_rtx)
-           r = BASE_POINTER_REGS;
-       }
-      else if (frame_pointer_needed && XEXP (x,0) == frame_pointer_rtx)
-       r = POINTER_Y_REGS;
-    }
-  else if ((GET_CODE (x) == PRE_DEC || GET_CODE (x) == POST_INC)
-           && REG_P (XEXP (x, 0))
-           && (strict ? REG_OK_FOR_BASE_STRICT_P (XEXP (x, 0))
-               : REG_OK_FOR_BASE_NOSTRICT_P (XEXP (x, 0))))
+  switch (GET_CODE (x))
     {
-      r = POINTER_REGS;
+    case REG:
+      ok = avr_reg_ok_for_addr_p (x, ADDR_SPACE_GENERIC,
+                                  MEM, strict);
+
+      if (strict
+          && DImode == mode
+          && REG_X == REGNO (x))
+        {
+          ok = false;
+        }
+      break;
+
+    case POST_INC:
+    case PRE_DEC:
+      ok = avr_reg_ok_for_addr_p (XEXP (x, 0), ADDR_SPACE_GENERIC,
+                                  GET_CODE (x), strict);
+      break;
+
+    case PLUS:
+      {
+        rtx reg = XEXP (x, 0);
+        rtx op1 = XEXP (x, 1);
+        
+        if (REG_P (reg)
+            && CONST_INT_P (op1)
+            && INTVAL (op1) >= 0)
+          {
+            bool fit = IN_RANGE (INTVAL (op1), 0, MAX_LD_OFFSET (mode));
+
+            if (fit)
+              {
+                ok = (! strict
+                      || avr_reg_ok_for_addr_p (reg, ADDR_SPACE_GENERIC,
+                                                PLUS, strict));
+          
+                if (reg == frame_pointer_rtx
+                    || reg == arg_pointer_rtx)
+                  {
+                    ok = true;
+                  }
+              }
+            else if (frame_pointer_needed
+                     && reg == frame_pointer_rtx)
+              {
+                ok = true;
+              }
+          }
+      }
+      break;
+      
+    default:
+      break;
     }
-  if (TARGET_ALL_DEBUG)
+  
+  if (avr_log.legitimate_address_p)
     {
-      fprintf (stderr, "   ret = %c\n", r + '0');
+      avr_edump ("\n%?: ret=%d, mode=%m strict=%d "
+                 "reload_completed=%d reload_in_progress=%d %s:",
+                 ok, mode, strict, reload_completed, reload_in_progress,
+                 reg_renumber ? "(reg_renumber)" : "");
+      
+      if (GET_CODE (x) == PLUS
+          && REG_P (XEXP (x, 0))
+          && CONST_INT_P (XEXP (x, 1))
+          && IN_RANGE (INTVAL (XEXP (x, 1)), 0, MAX_LD_OFFSET (mode))
+          && reg_renumber)
+        {
+          avr_edump ("(r%d ---> r%d)", REGNO (XEXP (x, 0)),
+                     true_regnum (XEXP (x, 0)));
+        }
+      
+      avr_edump ("\n%r\n", x);
     }
-  return r == NO_REGS ? 0 : (int)r;
+  
+  return ok;
 }
 
+
+/* Former implementation of TARGET_LEGITIMIZE_ADDRESS,
+   now only a helper for avr_addr_space_legitimize_address.  */
 /* Attempts to replace X with a valid
    memory address for an operand of mode MODE  */
 
-rtx
+static rtx
 avr_legitimize_address (rtx x, rtx oldx, enum machine_mode mode)
 {
+  bool big_offset_p = false;
+  
   x = oldx;
-  if (TARGET_ALL_DEBUG)
+  
+  if (GET_CODE (oldx) == PLUS
+      && REG_P (XEXP (oldx, 0)))
     {
-      fprintf (stderr, "legitimize_address mode: %s", GET_MODE_NAME(mode));
-      debug_rtx (oldx);
+      if (REG_P (XEXP (oldx, 1)))
+        x = force_reg (GET_MODE (oldx), oldx);
+      else if (CONST_INT_P (XEXP (oldx, 1)))
+        {
+         int offs = INTVAL (XEXP (oldx, 1));
+          if (frame_pointer_rtx != XEXP (oldx, 0)
+              && offs > MAX_LD_OFFSET (mode))
+            {
+              big_offset_p = true;
+              x = force_reg (GET_MODE (oldx), oldx);
+            }
+        }
     }
   
-  if (GET_CODE (oldx) == PLUS
-      && REG_P (XEXP (oldx,0)))
+  if (avr_log.legitimize_address)
     {
-      if (REG_P (XEXP (oldx,1)))
-       x = force_reg (GET_MODE (oldx), oldx);
-      else if (GET_CODE (XEXP (oldx, 1)) == CONST_INT)
-       {
-         int offs = INTVAL (XEXP (oldx,1));
-         if (frame_pointer_rtx != XEXP (oldx,0))
-           if (offs > MAX_LD_OFFSET (mode))
-             {
-               if (TARGET_ALL_DEBUG)
-                 fprintf (stderr, "force_reg (big offset)\n");
-               x = force_reg (GET_MODE (oldx), oldx);
-             }
-       }
+      avr_edump ("\n%?: mode=%m\n %r\n", mode, oldx);
+
+      if (x != oldx)
+        avr_edump (" %s --> %r\n", big_offset_p ? "(big offset)" : "", x);
     }
+
   return x;
 }
 
 
+/* Implement `LEGITIMIZE_RELOAD_ADDRESS'.  */
+/* This will allow register R26/27 to be used where it is no worse than normal
+   base pointers R28/29 or R30/31.  For example, if base offset is greater
+   than 63 bytes or for R++ or --R addressing.  */
+
+rtx
+avr_legitimize_reload_address (rtx *px, enum machine_mode mode,
+                               int opnum, int type, int addr_type,
+                               int ind_levels ATTRIBUTE_UNUSED,
+                               rtx (*mk_memloc)(rtx,int))
+{
+  rtx x = *px;
+  
+  if (avr_log.legitimize_reload_address)
+    avr_edump ("\n%?:%m %r\n", mode, x);
+  
+  if (1 && (GET_CODE (x) == POST_INC
+            || GET_CODE (x) == PRE_DEC))
+    {
+      push_reload (XEXP (x, 0), XEXP (x, 0), &XEXP (x, 0), &XEXP (x, 0),
+                   POINTER_REGS, GET_MODE (x), GET_MODE (x), 0, 0,
+                   opnum, RELOAD_OTHER);
+      
+      if (avr_log.legitimize_reload_address)
+        avr_edump (" RCLASS.1 = %R\n IN = %r\n OUT = %r\n",
+                   POINTER_REGS, XEXP (x, 0), XEXP (x, 0));
+      
+      return x;
+    }
+  
+  if (GET_CODE (x) == PLUS
+      && REG_P (XEXP (x, 0))
+      && 0 == reg_equiv_constant (REGNO (XEXP (x, 0)))
+      && CONST_INT_P (XEXP (x, 1))
+      && INTVAL (XEXP (x, 1)) >= 1)
+    {
+      bool fit = INTVAL (XEXP (x, 1)) <= MAX_LD_OFFSET (mode);
+      
+      if (fit)
+        {
+          if (reg_equiv_address (REGNO (XEXP (x, 0))) != 0)
+            {
+              int regno = REGNO (XEXP (x, 0));
+              rtx mem = mk_memloc (x, regno);
+              
+              push_reload (XEXP (mem, 0), NULL_RTX, &XEXP (mem, 0), NULL,
+                           POINTER_REGS, Pmode, VOIDmode, 0, 0,
+                           1, addr_type);
+              
+              if (avr_log.legitimize_reload_address)
+                avr_edump (" RCLASS.2 = %R\n IN = %r\n OUT = %r\n",
+                           POINTER_REGS, XEXP (mem, 0), NULL_RTX);
+              
+              push_reload (mem, NULL_RTX, &XEXP (x, 0), NULL,
+                           BASE_POINTER_REGS, GET_MODE (x), VOIDmode, 0, 0,
+                           opnum, type);
+              
+              if (avr_log.legitimize_reload_address)
+                avr_edump (" RCLASS.2 = %R\n IN = %r\n OUT = %r\n",
+                           BASE_POINTER_REGS, mem, NULL_RTX);
+              
+              return x;
+            }
+        }
+      else if (! (frame_pointer_needed
+                  && XEXP (x, 0) == frame_pointer_rtx))
+        {
+          push_reload (x, NULL_RTX, px, NULL,
+                       POINTER_REGS, GET_MODE (x), VOIDmode, 0, 0,
+                       opnum, type);
+          
+          if (avr_log.legitimize_reload_address)
+            avr_edump (" RCLASS.3 = %R\n IN = %r\n OUT = %r\n",
+                       POINTER_REGS, x, NULL_RTX);
+          
+          return x;
+        }
+    }
+  
+  return NULL_RTX;
+}
+
+
 /* Helper function to print assembler resp. track instruction
-   sequence lengths.
+   sequence lengths.  Always return "".
    
    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.
+       If N_WORDS >= 0  Add N_WORDS to *PLEN.
+       If N_WORDS < 0   Set *PLEN to -N_WORDS.
        Don't output anything.
 */
 
-static void
+static const char*
 avr_asm_len (const char* tpl, rtx* operands, int* plen, int n_words)
 {
   if (NULL == plen)
@@ -1204,8 +1683,13 @@ avr_asm_len (const char* tpl, rtx* operands, int* plen, int n_words)
     }
   else
     {
-      *plen += n_words;
+      if (n_words < 0)
+        *plen = -n_words;
+      else
+        *plen += n_words;
     }
+
+  return "";
 }
 
 
@@ -1220,7 +1704,8 @@ ptrreg_to_str (int regno)
     case REG_Y: return "Y";
     case REG_Z: return "Z";
     default:
-      output_operand_lossage ("address operand requires constraint for X, Y, or Z register");
+      output_operand_lossage ("address operand requires constraint for"
+                              " X, Y, or Z register");
     }
   return NULL;
 }
@@ -1254,12 +1739,16 @@ cond_string (enum rtx_code code)
     default:
       gcc_unreachable ();
     }
+
+  return "";
 }
 
+
+/* Implement `TARGET_PRINT_OPERAND_ADDRESS'.  */
 /* Output ADDR to FILE as address.  */
 
-void
-print_operand_address (FILE *file, rtx addr)
+static void
+avr_print_operand_address (FILE *file, rtx addr)
 {
   switch (GET_CODE (addr))
     {
@@ -1285,14 +1774,15 @@ print_operand_address (FILE *file, rtx addr)
          if (GET_CODE (x) == PLUS && GET_CODE (XEXP (x,1)) == CONST_INT)
            {
              /* Assembler gs() will implant word address. Make offset 
-                a byte offset inside gs() for assembler. This is 
-                needed because the more logical (constant+gs(sym)) is not 
-                accepted by gas. For 128K and lower devices this is ok. For
-                large devices it will create a Trampoline to offset from symbol 
-                which may not be what the user really wanted.  */
+                 a byte offset inside gs() for assembler. This is 
+                 needed because the more logical (constant+gs(sym)) is not 
+                 accepted by gas. For 128K and lower devices this is ok.
+                 For large devices it will create a Trampoline to offset
+                 from symbol which may not be what the user really wanted.  */
              fprintf (file, "gs(");
              output_addr_const (file, XEXP (x,0));
-             fprintf (file,"+" HOST_WIDE_INT_PRINT_DEC ")", 2 * INTVAL (XEXP (x,1)));
+              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"))
                  {
@@ -1313,10 +1803,21 @@ print_operand_address (FILE *file, rtx addr)
 }
 
 
-/* Output X as assembler operand to file FILE.  */
-     
-void
-print_operand (FILE *file, rtx x, int code)
+/* Implement `TARGET_PRINT_OPERAND_PUNCT_VALID_P'.  */
+
+static bool
+avr_print_operand_punct_valid_p (unsigned char code)
+{
+  return code == '~' || code == '!';
+}
+
+
+/* Implement `TARGET_PRINT_OPERAND'.  */
+/* Output X as assembler operand to file FILE.
+   For a description of supported %-codes, see top of avr.md.  */
+
+static void
+avr_print_operand (FILE *file, rtx x, int code)
 {
   int abcd = 0;
 
@@ -1333,6 +1834,31 @@ print_operand (FILE *file, rtx x, int code)
       if (AVR_HAVE_EIJMP_EICALL)
        fputc ('e', file);
     }
+  else if (code == 't'
+           || code == 'T')
+    {
+      static int t_regno = -1;
+      static int t_nbits = -1;
+
+      if (REG_P (x) && t_regno < 0 && code == 'T')
+        {
+          t_regno = REGNO (x);
+          t_nbits = GET_MODE_BITSIZE (GET_MODE (x));
+        }
+      else if (CONST_INT_P (x) && t_regno >= 0
+               && IN_RANGE (INTVAL (x), 0, t_nbits - 1))
+        {
+          int bpos = INTVAL (x);
+
+          fprintf (file, "%s", reg_names[t_regno + bpos / 8]);
+          if (code == 'T')
+            fprintf (file, ",%d", bpos % 8);
+
+          t_regno = -1;
+        }
+      else
+        fatal_insn ("operands to %T/%t must be reg + const_int:", x);
+    }
   else if (REG_P (x))
     {
       if (x == zero_reg_rtx)
@@ -1340,30 +1866,59 @@ print_operand (FILE *file, rtx x, int code)
       else
        fprintf (file, reg_names[true_regnum (x) + abcd]);
     }
-  else if (GET_CODE (x) == CONST_INT)
-    fprintf (file, HOST_WIDE_INT_PRINT_DEC, INTVAL (x) + abcd);
-  else if (GET_CODE (x) == MEM)
+  else if (CONST_INT_P (x))
+    {
+      HOST_WIDE_INT ival = INTVAL (x);
+        
+      if ('i' != code)
+        fprintf (file, HOST_WIDE_INT_PRINT_DEC, ival + abcd);
+      else if (low_io_address_operand (x, VOIDmode)
+               || high_io_address_operand (x, VOIDmode))
+        {
+          switch (ival)
+            {
+            case RAMPZ_ADDR: fprintf (file, "__RAMPZ__"); break;
+            case SREG_ADDR: fprintf (file, "__SREG__"); break;
+            case SP_ADDR:   fprintf (file, "__SP_L__"); break;
+            case SP_ADDR+1: fprintf (file, "__SP_H__"); break;
+              
+            default:
+              fprintf (file, HOST_WIDE_INT_PRINT_HEX,
+                       ival - avr_current_arch->sfr_offset);
+              break;
+            }
+        }
+      else
+        fatal_insn ("bad address, not an I/O address:", x);
+    }
+  else if (MEM_P (x))
     {
-      rtx addr = XEXP (x,0);
+      rtx addr = XEXP (x, 0);
+      
       if (code == 'm')
        {
-          if (!CONSTANT_P (addr))
-           fatal_insn ("bad address, not a constant):", addr);
-         /* Assembler template with m-code is data - not progmem section */
-         if (text_segment_operand (addr, VOIDmode))
-           if (warning ( 0, "accessing data memory with program memory address"))
-             {
-               output_addr_const (stderr, addr);
-               fprintf(stderr,"\n");
-             }
-         output_addr_const (file, addr);
+          if (!CONSTANT_P (addr))
+            fatal_insn ("bad address, not a constant:", addr);
+          /* Assembler template with m-code is data - not progmem section */
+          if (text_segment_operand (addr, VOIDmode))
+            if (warning (0, "accessing data memory with"
+                         " program memory address"))
+              {
+                output_addr_const (stderr, addr);
+                fprintf(stderr,"\n");
+              }
+          output_addr_const (file, addr);
        }
+      else if (code == 'i')
+        {
+          avr_print_operand (file, addr, 'i');
+        }
       else if (code == 'o')
        {
          if (GET_CODE (addr) != PLUS)
            fatal_insn ("bad address, not (reg+disp):", addr);
 
-         print_operand (file, XEXP (addr, 1), 0);
+         avr_print_operand (file, XEXP (addr, 1), 0);
        }
       else if (code == 'p' || code == 'r')
         {
@@ -1371,27 +1926,32 @@ print_operand (FILE *file, rtx x, int code)
             fatal_insn ("bad address, not post_inc or pre_dec:", addr);
           
           if (code == 'p')
-            print_operand_address (file, XEXP (addr, 0));  /* X, Y, Z */
+            avr_print_operand_address (file, XEXP (addr, 0));  /* X, Y, Z */
           else
-            print_operand (file, XEXP (addr, 0), 0);  /* r26, r28, r30 */
+            avr_print_operand (file, XEXP (addr, 0), 0);  /* r26, r28, r30 */
         }
       else if (GET_CODE (addr) == PLUS)
        {
-         print_operand_address (file, XEXP (addr,0));
+         avr_print_operand_address (file, XEXP (addr,0));
          if (REGNO (XEXP (addr, 0)) == REG_X)
            fatal_insn ("internal compiler error.  Bad address:"
                        ,addr);
          fputc ('+', file);
-         print_operand (file, XEXP (addr,1), code);
+         avr_print_operand (file, XEXP (addr,1), code);
        }
       else
-       print_operand_address (file, addr);
+       avr_print_operand_address (file, addr);
+    }
+  else if (code == 'i')
+    {
+      fatal_insn ("bad address, not an I/O address:", x);
     }
   else if (code == 'x')
     {
       /* Constant progmem address - like used in jmp or call */
       if (0 == text_segment_operand (x, VOIDmode))
-           if (warning ( 0, "accessing program  memory with data memory address"))
+        if (warning (0, "accessing program memory"
+                     " with data memory address"))
          {
            output_addr_const (stderr, x);
            fprintf(stderr,"\n");
@@ -1409,12 +1969,14 @@ print_operand (FILE *file, rtx x, int code)
       REAL_VALUE_TO_TARGET_SINGLE (rv, val);
       fprintf (file, "0x%lx", val);
     }
+  else if (GET_CODE (x) == CONST_STRING)
+    fputs (XSTR (x, 0), file);
   else if (code == 'j')
     fputs (cond_string (GET_CODE (x)), file);
   else if (code == 'k')
     fputs (cond_string (reverse_condition (GET_CODE (x))), file);
   else
-    print_operand_address (file, x);
+    avr_print_operand_address (file, x);
 }
 
 /* Update the condition code in the INSN.  */
@@ -1423,9 +1985,63 @@ void
 notice_update_cc (rtx body ATTRIBUTE_UNUSED, rtx insn)
 {
   rtx set;
+  enum attr_cc cc = get_attr_cc (insn);
   
-  switch (get_attr_cc (insn))
+  switch (cc)
+    {
+    default:
+      break;
+
+    case CC_OUT_PLUS:
+    case CC_OUT_PLUS_NOCLOBBER:
+    case CC_LDI:
+      {
+        rtx *op = recog_data.operand;
+        int len_dummy, icc;
+        
+        /* Extract insn's operands.  */
+        extract_constrain_insn_cached (insn);
+
+        switch (cc)
+          {
+          default:
+            gcc_unreachable();
+            
+          case CC_OUT_PLUS:
+            avr_out_plus (op, &len_dummy, &icc);
+            cc = (enum attr_cc) icc;
+            break;
+            
+          case CC_OUT_PLUS_NOCLOBBER:
+            avr_out_plus_noclobber (op, &len_dummy, &icc);
+            cc = (enum attr_cc) icc;
+            break;
+
+          case CC_LDI:
+
+            cc = (op[1] == CONST0_RTX (GET_MODE (op[0]))
+                  && reg_overlap_mentioned_p (op[0], zero_reg_rtx))
+              /* Loading zero-reg with 0 uses CLI and thus clobbers cc0.  */
+              ? CC_CLOBBER
+              /* Any other "r,rL" combination does not alter cc0.  */
+              : CC_NONE;
+            
+            break;
+          } /* inner switch */
+
+        break;
+      }
+    } /* outer swicth */
+
+  switch (cc)
     {
+    default:
+      /* Special values like CC_OUT_PLUS from above have been
+         mapped to "standard" CC_* values so we never come here.  */
+      
+      gcc_unreachable();
+      break;
+      
     case CC_NONE:
       /* Insn does not affect CC at all.  */
       break;
@@ -1467,39 +2083,10 @@ notice_update_cc (rtx body ATTRIBUTE_UNUSED, rtx insn)
     case CC_CLOBBER:
       /* Insn doesn't leave CC in a usable state.  */
       CC_STATUS_INIT;
-
-      /* Correct CC for the ashrqi3 with the shift count as CONST_INT != 6 */
-      set = single_set (insn);
-      if (set)
-       {
-         rtx src = SET_SRC (set);
-         
-         if (GET_CODE (src) == ASHIFTRT
-             && GET_MODE (src) == QImode)
-           {
-             rtx x = XEXP (src, 1);
-
-             if (CONST_INT_P (x)
-                 && IN_RANGE (INTVAL (x), 1, 5))
-               {
-                 cc_status.value1 = SET_DEST (set);
-                 cc_status.flags |= CC_OVERFLOW_UNUSABLE;
-               }
-           }
-       }
       break;
     }
 }
 
-/* Return maximum number of consecutive registers of
-   class CLASS needed to hold a value of mode MODE.  */
-
-int
-class_max_nregs (enum reg_class rclass ATTRIBUTE_UNUSED,enum machine_mode mode)
-{
-  return ((GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1) / UNITS_PER_WORD);
-}
-
 /* Choose mode for jump insn:
    1 - relative jump in range -63 <= x <= 62 ;
    2 - relative jump in range -2046 <= x <= 2045 ;
@@ -1625,25 +2212,23 @@ ret_cond_branch (rtx x, int len, int reverse)
   return "";
 }
 
-/* Predicate function for immediate operand which fits to byte (8bit) */
-
-int
-byte_immediate_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
-{
-  return (GET_CODE (op) == CONST_INT
-          && INTVAL (op) <= 0xff && INTVAL (op) >= 0);
-}
-
 /* Output insn cost for next insn.  */
 
 void
 final_prescan_insn (rtx insn, rtx *operand ATTRIBUTE_UNUSED,
-                   int num_operands ATTRIBUTE_UNUSED)
+                    int num_operands ATTRIBUTE_UNUSED)
 {
-  if (TARGET_ALL_DEBUG)
+  if (avr_log.rtx_costs)
     {
-      fprintf (asm_out_file, "/* DEBUG: cost = %d.  */\n",
-              rtx_cost (PATTERN (insn), INSN, !optimize_size));
+      rtx set = single_set (insn);
+
+      if (set)
+        fprintf (asm_out_file, "/* DEBUG: cost = %d.  */\n",
+                 set_src_cost (SET_SRC (set), optimize_insn_for_speed_p ()));
+      else
+        fprintf (asm_out_file, "/* DEBUG: pattern-cost = %d.  */\n",
+                 rtx_cost (PATTERN (insn), INSN, 0,
+                          optimize_insn_for_speed_p()));
     }
 }
 
@@ -1654,6 +2239,7 @@ avr_simplify_comparison_p (enum machine_mode mode, RTX_CODE op, rtx x)
 {
   unsigned int max = (mode == QImode ? 0xff :
                       mode == HImode ? 0xffff :
+                      mode == PSImode ? 0xffffff :
                       mode == SImode ? 0xffffffff : 0);
   if (max && op && GET_CODE (x) == CONST_INT)
     {
@@ -1761,7 +2347,7 @@ avr_function_arg_advance (cumulative_args_t cum_v, enum machine_mode mode,
 
   /* 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.  */
+     call will clobber that fixed register.  See PR45099 for an example.  */
   
   if (cum->regno >= 8
       && cum->nregs >= 0)
@@ -1770,8 +2356,8 @@ avr_function_arg_advance (cumulative_args_t cum_v, enum machine_mode mode,
 
       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]);
+          warning (0, "fixed register %s used to pass parameter to function",
+                   reg_names[regno]);
     }
       
   if (cum->nregs <= 0)
@@ -1843,319 +2429,631 @@ avr_function_ok_for_sibcall (tree decl_callee, tree exp_callee)
 /***********************************************************************
   Functions for outputting various mov's for a various modes
 ************************************************************************/
-const char *
-output_movqi (rtx insn, rtx operands[], int *l)
+
+/* Return true if a value of mode MODE is read from flash by
+   __load_* function from libgcc.  */
+
+bool
+avr_load_libgcc_p (rtx op)
 {
-  int dummy;
-  rtx dest = operands[0];
-  rtx src = operands[1];
-  int *real_l = l;
-  
-  if (!l)
-    l = &dummy;
+  enum machine_mode mode = GET_MODE (op);
+  int n_bytes = GET_MODE_SIZE (mode);
+        
+  return (n_bytes > 2
+          && !AVR_HAVE_LPMX
+          && avr_mem_pgm_p (op));
+}
 
-  *l = 1;
+/* Return true if a value of mode MODE is read by __xload_* function.  */
+
+bool
+avr_xload_libgcc_p (enum machine_mode mode)
+{
+  int n_bytes = GET_MODE_SIZE (mode);
   
-  if (register_operand (dest, QImode))
+  return (n_bytes > 1
+          && avr_current_arch->n_segments > 1
+          && !AVR_HAVE_ELPMX);
+}
+
+
+/* Find an unused d-register to be used as scratch in INSN.
+   EXCLUDE is either NULL_RTX or some register. In the case where EXCLUDE
+   is a register, skip all possible return values that overlap EXCLUDE.
+   The policy for the returned register is similar to that of
+   `reg_unused_after', i.e. the returned register may overlap the SET_DEST
+   of INSN.
+
+   Return a QImode d-register or NULL_RTX if nothing found.  */
+
+static rtx
+avr_find_unused_d_reg (rtx insn, rtx exclude)
+{
+  int regno;
+  bool isr_p = (interrupt_function_p (current_function_decl)
+                || signal_function_p (current_function_decl));
+
+  for (regno = 16; regno < 32; regno++)
     {
-      if (register_operand (src, QImode)) /* mov r,r */
-       {
-         if (test_hard_reg_class (STACK_REG, dest))
-           return AS2 (out,%0,%1);
-         else if (test_hard_reg_class (STACK_REG, src))
-           return AS2 (in,%0,%1);
-         
-         return AS2 (mov,%0,%1);
-       }
-      else if (CONSTANT_P (src))
-       {
-         if (test_hard_reg_class (LD_REGS, dest)) /* ldi d,i */
-           return AS2 (ldi,%0,lo8(%1));
-         
-         if (GET_CODE (src) == CONST_INT)
-           {
-             if (src == const0_rtx) /* mov r,L */
-               return AS1 (clr,%0);
-             else if (src == const1_rtx)
-               {
-                 *l = 2;
-                 return (AS1 (clr,%0) CR_TAB
-                         AS1 (inc,%0));
-               }
-             else if (src == constm1_rtx)
-               {
-                 /* Immediate constants -1 to any register */
-                 *l = 2;
-                 return (AS1 (clr,%0) CR_TAB
-                         AS1 (dec,%0));
-               }
-             else
-               {
-                 int bit_nr = exact_log2 (INTVAL (src));
+      rtx reg = all_regs_rtx[regno];
+      
+      if ((exclude
+           && reg_overlap_mentioned_p (exclude, reg))
+          || fixed_regs[regno])
+        {
+          continue;
+        }
 
-                 if (bit_nr >= 0)
-                   {
-                     *l = 3;
-                     if (!real_l)
-                       output_asm_insn ((AS1 (clr,%0) CR_TAB
-                                         "set"), operands);
-                     if (!real_l)
-                       avr_output_bld (operands, bit_nr);
-
-                     return "";
-                   }
-               }
-           }
-         
-         /* Last resort, larger than loading from memory.  */
-         *l = 4;
-         return (AS2 (mov,__tmp_reg__,r31) CR_TAB
-                 AS2 (ldi,r31,lo8(%1))     CR_TAB
-                 AS2 (mov,%0,r31)          CR_TAB
-                 AS2 (mov,r31,__tmp_reg__));
-       }
-      else if (GET_CODE (src) == MEM)
-       return out_movqi_r_mr (insn, operands, real_l); /* mov r,m */
+      /* Try non-live register */
+
+      if (!df_regs_ever_live_p (regno)
+          && (TREE_THIS_VOLATILE (current_function_decl)
+              || cfun->machine->is_OS_task
+              || cfun->machine->is_OS_main
+              || (!isr_p && call_used_regs[regno])))
+        {
+          return reg;
+        }
+
+      /* Any live register can be used if it is unused after.
+         Prologue/epilogue will care for it as needed.  */
+      
+      if (df_regs_ever_live_p (regno)
+          && reg_unused_after (insn, reg))
+        {
+          return reg;
+        }
     }
-  else if (GET_CODE (dest) == MEM)
+
+  return NULL_RTX;
+}
+
+
+/* Helper function for the next function in the case where only restricted
+   version of LPM instruction is available.  */
+
+static const char*
+avr_out_lpm_no_lpmx (rtx insn, rtx *xop, int *plen)
+{
+  rtx dest = xop[0];
+  rtx addr = xop[1];
+  int n_bytes = GET_MODE_SIZE (GET_MODE (dest));
+  int regno_dest;
+
+  regno_dest = REGNO (dest);
+
+  /* The implicit target register of LPM.  */
+  xop[3] = lpm_reg_rtx;
+
+  switch (GET_CODE (addr))
     {
-      const char *templ;
+    default:
+      gcc_unreachable();
+
+    case REG:
 
-      if (src == const0_rtx)
-       operands[1] = zero_reg_rtx;
+      gcc_assert (REG_Z == REGNO (addr));
 
-      templ = out_movqi_mr_r (insn, operands, real_l);
+      switch (n_bytes)
+        {
+        default:
+          gcc_unreachable();
 
-      if (!real_l)
-       output_asm_insn (templ, operands);
+        case 1:
+          avr_asm_len ("%4lpm", xop, plen, 1);
 
-      operands[1] = src;
-    }
+          if (regno_dest != LPM_REGNO)
+            avr_asm_len ("mov %0,%3", xop, plen, 1);
+
+          return "";
+
+        case 2:
+          if (REGNO (dest) == REG_Z)
+            return avr_asm_len ("%4lpm"      CR_TAB
+                                "push %3"    CR_TAB
+                                "adiw %2,1"  CR_TAB
+                                "%4lpm"      CR_TAB
+                                "mov %B0,%3" CR_TAB
+                                "pop %A0", xop, plen, 6);
+          
+          avr_asm_len ("%4lpm"      CR_TAB
+                       "mov %A0,%3" CR_TAB
+                       "adiw %2,1"  CR_TAB
+                       "%4lpm"      CR_TAB
+                       "mov %B0,%3", xop, plen, 5);
+                
+          if (!reg_unused_after (insn, addr))
+            avr_asm_len ("sbiw %2,1", xop, plen, 1);
+          
+          break; /* 2 */
+        }
+      
+      break; /* REG */
+
+    case POST_INC:
+
+      gcc_assert (REG_Z == REGNO (XEXP (addr, 0))
+                  && n_bytes <= 4);
+
+      if (regno_dest == LPM_REGNO)
+        avr_asm_len ("%4lpm"      CR_TAB
+                     "adiw %2,1", xop, plen, 2);
+      else
+        avr_asm_len ("%4lpm"      CR_TAB
+                     "mov %A0,%3" CR_TAB
+                     "adiw %2,1", xop, plen, 3);
+
+      if (n_bytes >= 2)
+        avr_asm_len ("%4lpm"      CR_TAB
+                     "mov %B0,%3" CR_TAB
+                     "adiw %2,1", xop, plen, 3);
+
+      if (n_bytes >= 3)
+        avr_asm_len ("%4lpm"      CR_TAB
+                     "mov %C0,%3" CR_TAB
+                     "adiw %2,1", xop, plen, 3);
+
+      if (n_bytes >= 4)
+        avr_asm_len ("%4lpm"      CR_TAB
+                     "mov %D0,%3" CR_TAB
+                     "adiw %2,1", xop, plen, 3);
+
+      break; /* POST_INC */
+      
+    } /* switch CODE (addr) */
+      
   return "";
 }
 
 
-const char *
-output_movhi (rtx insn, rtx operands[], int *l)
+/* If PLEN == NULL: Ouput instructions to load a value from a memory location
+   OP[1] in AS1 to register OP[0].
+   If PLEN != 0 set *PLEN to the length in words of the instruction sequence.
+   Return "".  */
+
+static const char*
+avr_out_lpm (rtx insn, rtx *op, int *plen)
 {
-  int dummy;
-  rtx dest = operands[0];
-  rtx src = operands[1];
-  int *real_l = l;
+  rtx xop[6];
+  rtx dest = op[0];
+  rtx src = SET_SRC (single_set (insn));
+  rtx addr;
+  int n_bytes = GET_MODE_SIZE (GET_MODE (dest));
+  int regno_dest;
+  int segment;
+  RTX_CODE code;
   
-  if (!l)
-    l = &dummy;
+  addr_space_t as;
+
+  if (plen)
+    *plen = 0;
   
-  if (register_operand (dest, HImode))
+  if (MEM_P (dest))
     {
-      if (register_operand (src, HImode)) /* mov r,r */
-       {
-         if (test_hard_reg_class (STACK_REG, dest))
-           {
-             if (AVR_HAVE_8BIT_SP)
-               return *l = 1, AS2 (out,__SP_L__,%A1);
-              /* Use simple load of stack pointer if no interrupts are 
-                used.  */
-             else if (TARGET_NO_INTERRUPTS)
-               return *l = 2, (AS2 (out,__SP_H__,%B1) CR_TAB
-                               AS2 (out,__SP_L__,%A1));
-             *l = 5;
-             return (AS2 (in,__tmp_reg__,__SREG__)  CR_TAB
-                     "cli"                          CR_TAB
-                     AS2 (out,__SP_H__,%B1)         CR_TAB
-                     AS2 (out,__SREG__,__tmp_reg__) CR_TAB
-                     AS2 (out,__SP_L__,%A1));
-           }
-         else if (test_hard_reg_class (STACK_REG, src))
-           {
-             *l = 2;   
-             return (AS2 (in,%A0,__SP_L__) CR_TAB
-                     AS2 (in,%B0,__SP_H__));
-           }
+      warning (0, "writing to address space %qs not supported",
+               avr_addrspace[MEM_ADDR_SPACE (dest)].name);
+      
+      return "";
+    }
 
-         if (AVR_HAVE_MOVW)
-           {
-             *l = 1;
-             return (AS2 (movw,%0,%1));
-           }
-         else
-           {
-             *l = 2;
-             return (AS2 (mov,%A0,%A1) CR_TAB
-                     AS2 (mov,%B0,%B1));
-           }
-       }
-      else if (CONSTANT_P (src))
-       {
-         if (test_hard_reg_class (LD_REGS, dest)) /* ldi d,i */
-           {
-             *l = 2;
-             return (AS2 (ldi,%A0,lo8(%1)) CR_TAB
-                     AS2 (ldi,%B0,hi8(%1)));
-           }
-         
-         if (GET_CODE (src) == CONST_INT)
-           {
-             if (src == const0_rtx) /* mov r,L */
-               {
-                 *l = 2;
-                 return (AS1 (clr,%A0) CR_TAB
-                         AS1 (clr,%B0));
-               }
-             else if (src == const1_rtx)
-               {
-                 *l = 3;
-                 return (AS1 (clr,%A0) CR_TAB
-                         AS1 (clr,%B0) CR_TAB
-                         AS1 (inc,%A0));
-               }
-             else if (src == constm1_rtx)
-               {
-                 /* Immediate constants -1 to any register */
-                 *l = 3;
-                 return (AS1 (clr,%0)  CR_TAB
-                         AS1 (dec,%A0) CR_TAB
-                         AS2 (mov,%B0,%A0));
-               }
-             else
-               {
-                 int bit_nr = exact_log2 (INTVAL (src));
+  as = MEM_ADDR_SPACE (src);
 
-                 if (bit_nr >= 0)
-                   {
-                     *l = 4;
-                     if (!real_l)
-                       output_asm_insn ((AS1 (clr,%A0) CR_TAB
-                                         AS1 (clr,%B0) CR_TAB
-                                         "set"), operands);
-                     if (!real_l)
-                       avr_output_bld (operands, bit_nr);
-
-                     return "";
-                   }
-               }
+  addr = XEXP (src, 0);
+  code = GET_CODE (addr);
 
-             if ((INTVAL (src) & 0xff) == 0)
-               {
-                 *l = 5;
-                 return (AS2 (mov,__tmp_reg__,r31) CR_TAB
-                         AS1 (clr,%A0)             CR_TAB
-                         AS2 (ldi,r31,hi8(%1))     CR_TAB
-                         AS2 (mov,%B0,r31)         CR_TAB
-                         AS2 (mov,r31,__tmp_reg__));
-               }
-             else if ((INTVAL (src) & 0xff00) == 0)
-               {
-                 *l = 5;
-                 return (AS2 (mov,__tmp_reg__,r31) CR_TAB
-                         AS2 (ldi,r31,lo8(%1))     CR_TAB
-                         AS2 (mov,%A0,r31)         CR_TAB
-                         AS1 (clr,%B0)             CR_TAB
-                         AS2 (mov,r31,__tmp_reg__));
-               }
-           }
+  gcc_assert (REG_P (dest));
+  
+  if (as == ADDR_SPACE_PGMX)
+    {
+      /* We are called from avr_out_xload because someone wrote
+         __pgmx on a device with just one flash segment.  */
+
+      gcc_assert (LO_SUM == code);
+
+      addr = XEXP (addr, 1);
+    }
+  else
+    gcc_assert (REG == code || POST_INC == code);
+
+  xop[0] = dest;
+  xop[1] = addr;
+  xop[2] = lpm_addr_reg_rtx;
+  xop[4] = xstring_empty;
+  xop[5] = tmp_reg_rtx;
+
+  regno_dest = REGNO (dest);
+
+  /* Cut down segment number to a number the device actually supports.
+     We do this late to preserve the address space's name for diagnostics.  */
+
+  segment = avr_addrspace[as].segment % avr_current_arch->n_segments;
+
+  /* Set RAMPZ as needed.  */
+
+  if (segment)
+    {
+      xop[4] = GEN_INT (segment);
+      
+      if (xop[3] = avr_find_unused_d_reg (insn, lpm_addr_reg_rtx),
+          xop[3])
+        {
+          avr_asm_len ("ldi %3,%4" CR_TAB
+                       "out __RAMPZ__,%3", xop, plen, 2);
+        }
+      else if (segment == 1)
+        {
+          avr_asm_len ("clr %5" CR_TAB
+                       "inc %5" CR_TAB
+                       "out __RAMPZ__,%5", xop, plen, 3);
+        }
+      else
+        {
+          avr_asm_len ("mov %5,%2"         CR_TAB
+                       "ldi %2,%4"         CR_TAB
+                       "out __RAMPZ__,%2"  CR_TAB
+                       "mov %2,%5", xop, plen, 4);
+        }
+      
+      xop[4] = xstring_e;
+
+      if (!AVR_HAVE_ELPMX)
+        return avr_out_lpm_no_lpmx (insn, xop, plen);
+    }
+  else if (!AVR_HAVE_LPMX)
+    {
+      return avr_out_lpm_no_lpmx (insn, xop, plen);
+    }
+
+  /* We have [E]LPMX: Output reading from Flash the comfortable way.  */
+
+  switch (GET_CODE (addr))
+    {
+    default:
+      gcc_unreachable();
+
+    case REG:
+
+      gcc_assert (REG_Z == REGNO (addr));
+
+      switch (n_bytes)
+        {
+        default:
+          gcc_unreachable();
+
+        case 1:
+          return avr_asm_len ("%4lpm %0,%a2", xop, plen, 1);
+
+        case 2:
+          if (REGNO (dest) == REG_Z)
+            return avr_asm_len ("%4lpm %5,%a2+" CR_TAB
+                                "%4lpm %B0,%a2" CR_TAB
+                                "mov %A0,%5", xop, plen, 3);
+          else
+            {
+              avr_asm_len ("%4lpm %A0,%a2+" CR_TAB
+                           "%4lpm %B0,%a2", xop, plen, 2);
+                
+              if (!reg_unused_after (insn, addr))
+                avr_asm_len ("sbiw %2,1", xop, plen, 1);
+            }
+          
+          break; /* 2 */
+
+        case 3:
+
+          avr_asm_len ("%4lpm %A0,%a2+" CR_TAB
+                       "%4lpm %B0,%a2+" CR_TAB
+                       "%4lpm %C0,%a2", xop, plen, 3);
+                
+          if (!reg_unused_after (insn, addr))
+            avr_asm_len ("sbiw %2,2", xop, plen, 1);
+
+          break; /* 3 */
+      
+        case 4:
+
+          avr_asm_len ("%4lpm %A0,%a2+" CR_TAB
+                       "%4lpm %B0,%a2+", xop, plen, 2);
+          
+          if (REGNO (dest) == REG_Z - 2)
+            return avr_asm_len ("%4lpm %5,%a2+" CR_TAB
+                                "%4lpm %C0,%a2"          CR_TAB
+                                "mov %D0,%5", xop, plen, 3);
+          else
+            {
+              avr_asm_len ("%4lpm %C0,%a2+" CR_TAB
+                           "%4lpm %D0,%a2", xop, plen, 2);
+                
+              if (!reg_unused_after (insn, addr))
+                avr_asm_len ("sbiw %2,3", xop, plen, 1);
+            }
+
+          break; /* 4 */
+        } /* n_bytes */
+      
+      break; /* REG */
+
+    case POST_INC:
+
+      gcc_assert (REG_Z == REGNO (XEXP (addr, 0))
+                  && n_bytes <= 4);
+
+      avr_asm_len                    ("%4lpm %A0,%a2+", xop, plen, 1);
+      if (n_bytes >= 2)  avr_asm_len ("%4lpm %B0,%a2+", xop, plen, 1);
+      if (n_bytes >= 3)  avr_asm_len ("%4lpm %C0,%a2+", xop, plen, 1);
+      if (n_bytes >= 4)  avr_asm_len ("%4lpm %D0,%a2+", xop, plen, 1);
+
+      break; /* POST_INC */
+
+    } /* switch CODE (addr) */
+      
+  return "";
+}
+
+
+/* Worker function for xload_<mode> and xload_8 insns.  */
+
+const char*
+avr_out_xload (rtx insn, rtx *op, int *plen)
+{
+  rtx xop[5];
+  rtx reg = op[0];
+  int n_bytes = GET_MODE_SIZE (GET_MODE (reg));
+  unsigned int regno = REGNO (reg);
+
+  if (avr_current_arch->n_segments == 1)
+    return avr_out_lpm (insn, op, plen);
+
+  xop[0] = reg;
+  xop[1] = op[1];
+  xop[2] = lpm_addr_reg_rtx;
+  xop[3] = lpm_reg_rtx;
+  xop[4] = tmp_reg_rtx;
+  
+  avr_asm_len ("out __RAMPZ__,%1", xop, plen, -1);
+  
+  if (1 == n_bytes)
+    {
+      if (AVR_HAVE_ELPMX)
+        return avr_asm_len ("elpm %0,%a2", xop, plen, 1);
+      else
+        return avr_asm_len ("elpm" CR_TAB
+                            "mov %0,%3", xop, plen, 2);
+    }
+
+  gcc_assert (AVR_HAVE_ELPMX);
+  
+  if (!reg_overlap_mentioned_p (reg, lpm_addr_reg_rtx))
+    {
+      /* Insn clobbers the Z-register so we can use post-increment.  */
+      
+      avr_asm_len                    ("elpm %A0,%a2+", xop, plen, 1);
+      if (n_bytes >= 2)  avr_asm_len ("elpm %B0,%a2+", xop, plen, 1);
+      if (n_bytes >= 3)  avr_asm_len ("elpm %C0,%a2+", xop, plen, 1);
+      if (n_bytes >= 4)  avr_asm_len ("elpm %D0,%a2+", xop, plen, 1);
+
+      return "";
+    }
+
+  switch (n_bytes)
+    {
+    default:
+      gcc_unreachable();
+      
+    case 2:
+      gcc_assert (regno == REGNO (lpm_addr_reg_rtx));
+
+      return avr_asm_len ("elpm %4,%a2+" CR_TAB
+                          "elpm %B0,%a2" CR_TAB
+                          "mov %A0,%4", xop, plen, 3);
+
+    case 3:
+    case 4:
+      gcc_assert (regno + 2 == REGNO (lpm_addr_reg_rtx));
+      
+      avr_asm_len ("elpm %A0,%a2+" CR_TAB
+                   "elpm %B0,%a2+", xop, plen, 2);
+
+      if (n_bytes == 3)
+        return avr_asm_len ("elpm %C0,%a2", xop, plen, 1);
+      else
+        return avr_asm_len ("elpm %4,%a2+" CR_TAB
+                            "elpm %D0,%a2" CR_TAB
+                            "mov %C0,%4", xop, plen, 3);
+    }
+  
+  return "";
+}
+
+
+const char *
+output_movqi (rtx insn, rtx operands[], int *l)
+{
+  int dummy;
+  rtx dest = operands[0];
+  rtx src = operands[1];
+  int *real_l = l;
+  
+  if (avr_mem_pgm_p (src)
+      || avr_mem_pgm_p (dest))
+    {
+      return avr_out_lpm (insn, operands, real_l);
+    }
+
+  if (!l)
+    l = &dummy;
+
+  *l = 1;
+  
+  if (register_operand (dest, QImode))
+    {
+      if (register_operand (src, QImode)) /* mov r,r */
+       {
+         if (test_hard_reg_class (STACK_REG, dest))
+           return AS2 (out,%0,%1);
+         else if (test_hard_reg_class (STACK_REG, src))
+           return AS2 (in,%0,%1);
          
-         /* Last resort, equal to loading from memory.  */
-         *l = 6;
-         return (AS2 (mov,__tmp_reg__,r31) CR_TAB
-                 AS2 (ldi,r31,lo8(%1))     CR_TAB
-                 AS2 (mov,%A0,r31)         CR_TAB
-                 AS2 (ldi,r31,hi8(%1))     CR_TAB
-                 AS2 (mov,%B0,r31)         CR_TAB
-                 AS2 (mov,r31,__tmp_reg__));
+         return AS2 (mov,%0,%1);
        }
+      else if (CONSTANT_P (src))
+        {
+          output_reload_in_const (operands, NULL_RTX, real_l, false);
+          return "";
+        }
       else if (GET_CODE (src) == MEM)
-       return out_movhi_r_mr (insn, operands, real_l); /* mov r,m */
+       return out_movqi_r_mr (insn, operands, real_l); /* mov r,m */
     }
   else if (GET_CODE (dest) == MEM)
     {
-      const char *templ;
+      rtx xop[2];
 
-      if (src == const0_rtx)
-       operands[1] = zero_reg_rtx;
+      xop[0] = dest;
+      xop[1] = src == const0_rtx ? zero_reg_rtx : src;
 
-      templ = out_movhi_mr_r (insn, operands, real_l);
+      return out_movqi_mr_r (insn, xop, real_l);
+    }
+  return "";
+}
 
-      if (!real_l)
-       output_asm_insn (templ, operands);
 
-      operands[1] = src;
-      return "";
+const char *
+output_movhi (rtx insn, rtx xop[], int *plen)
+{
+  rtx dest = xop[0];
+  rtx src = xop[1];
+
+  gcc_assert (GET_MODE_SIZE (GET_MODE (dest)) == 2);
+  
+  if (avr_mem_pgm_p (src)
+      || avr_mem_pgm_p (dest))
+    {
+      return avr_out_lpm (insn, xop, plen);
     }
+
+  if (REG_P (dest))
+    {
+      if (REG_P (src)) /* mov r,r */
+        {
+          if (test_hard_reg_class (STACK_REG, dest))
+            {
+              if (AVR_HAVE_8BIT_SP)
+                return avr_asm_len ("out __SP_L__,%A1", xop, plen, -1);
+              
+              /* Use simple load of SP if no interrupts are  used.  */
+              
+              return TARGET_NO_INTERRUPTS
+                ? avr_asm_len ("out __SP_H__,%B1" CR_TAB
+                               "out __SP_L__,%A1", xop, plen, -2)
+
+                : avr_asm_len ("in __tmp_reg__,__SREG__"  CR_TAB
+                               "cli"                      CR_TAB
+                               "out __SP_H__,%B1"         CR_TAB
+                               "out __SREG__,__tmp_reg__" CR_TAB
+                               "out __SP_L__,%A1", xop, plen, -5);
+            }
+          else if (test_hard_reg_class (STACK_REG, src))
+            {
+              return AVR_HAVE_8BIT_SP
+                ? avr_asm_len ("in %A0,__SP_L__" CR_TAB
+                               "clr %B0", xop, plen, -2)
+                
+                : avr_asm_len ("in %A0,__SP_L__" CR_TAB
+                               "in %B0,__SP_H__", xop, plen, -2);
+            }
+
+          return AVR_HAVE_MOVW
+            ? avr_asm_len ("movw %0,%1", xop, plen, -1)
+
+            : avr_asm_len ("mov %A0,%A1" CR_TAB
+                           "mov %B0,%B1", xop, plen, -2);
+        } /* REG_P (src) */
+      else if (CONSTANT_P (src))
+        {
+          return output_reload_inhi (xop, NULL, plen);
+        }
+      else if (MEM_P (src))
+        {
+          return out_movhi_r_mr (insn, xop, plen); /* mov r,m */
+        }
+    }
+  else if (MEM_P (dest))
+    {
+      rtx xop[2];
+
+      xop[0] = dest;
+      xop[1] = src == const0_rtx ? zero_reg_rtx : src;
+
+      return out_movhi_mr_r (insn, xop, plen);
+    }
+  
   fatal_insn ("invalid insn:", insn);
+  
   return "";
 }
 
-const char *
-out_movqi_r_mr (rtx insn, rtx op[], int *l)
+static const char*
+out_movqi_r_mr (rtx insn, rtx op[], int *plen)
 {
   rtx dest = op[0];
   rtx src = op[1];
   rtx x = XEXP (src, 0);
-  int dummy;
-  
-  if (!l)
-    l = &dummy;
   
   if (CONSTANT_ADDRESS_P (x))
     {
-      if (CONST_INT_P (x) && INTVAL (x) == SREG_ADDR)
-       {
-         *l = 1;
-         return AS2 (in,%0,__SREG__);
-       }
-      if (optimize > 0 && io_address_operand (x, QImode))
-       {
-         *l = 1;
-         return AS2 (in,%0,%m1-0x20);
-       }
-      *l = 2;
-      return AS2 (lds,%0,%m1);
+      return optimize > 0 && io_address_operand (x, QImode)
+        ? avr_asm_len ("in %0,%i1", op, plen, -1)
+        : avr_asm_len ("lds %0,%m1", op, plen, -2);
     }
-  /* memory access by reg+disp */
   else if (GET_CODE (x) == PLUS
-      && REG_P (XEXP (x,0))
-      && GET_CODE (XEXP (x,1)) == CONST_INT)
+           && REG_P (XEXP (x, 0))
+           && CONST_INT_P (XEXP (x, 1)))
     {
-      if ((INTVAL (XEXP (x,1)) - GET_MODE_SIZE (GET_MODE (src))) >= 63)
-       {
-         int disp = INTVAL (XEXP (x,1));
-         if (REGNO (XEXP (x,0)) != REG_Y)
-           fatal_insn ("incorrect insn:",insn);
+      /* memory access by reg+disp */
 
-         if (disp <= 63 + MAX_LD_OFFSET (GET_MODE (src)))
-           return *l = 3, (AS2 (adiw,r28,%o1-63) CR_TAB
-                           AS2 (ldd,%0,Y+63)     CR_TAB
-                           AS2 (sbiw,r28,%o1-63));
+      int disp = INTVAL (XEXP (x, 1));
+      
+      if (disp - GET_MODE_SIZE (GET_MODE (src)) >= 63)
+        {
+          if (REGNO (XEXP (x, 0)) != REG_Y)
+            fatal_insn ("incorrect insn:",insn);
+
+          if (disp <= 63 + MAX_LD_OFFSET (GET_MODE (src)))
+            return avr_asm_len ("adiw r28,%o1-63" CR_TAB
+                                "ldd %0,Y+63"     CR_TAB
+                                "sbiw r28,%o1-63", op, plen, -3);
+
+          return avr_asm_len ("subi r28,lo8(-%o1)" CR_TAB
+                              "sbci r29,hi8(-%o1)" CR_TAB
+                              "ld %0,Y"            CR_TAB
+                              "subi r28,lo8(%o1)"  CR_TAB
+                              "sbci r29,hi8(%o1)", op, plen, -5);
+        }
+      else if (REGNO (XEXP (x, 0)) == REG_X)
+        {
+          /* This is a paranoid case LEGITIMIZE_RELOAD_ADDRESS must exclude
+             it but I have this situation with extremal optimizing options.  */
+          
+          avr_asm_len ("adiw r26,%o1" CR_TAB
+                       "ld %0,X", op, plen, -2);
+          
+          if (!reg_overlap_mentioned_p (dest, XEXP (x,0))
+              && !reg_unused_after (insn, XEXP (x,0)))
+            {
+              avr_asm_len ("sbiw r26,%o1", op, plen, 1);
+            }
 
-         return *l = 5, (AS2 (subi,r28,lo8(-%o1)) CR_TAB
-                         AS2 (sbci,r29,hi8(-%o1)) CR_TAB
-                         AS2 (ld,%0,Y)            CR_TAB
-                         AS2 (subi,r28,lo8(%o1))  CR_TAB
-                         AS2 (sbci,r29,hi8(%o1)));
-       }
-      else if (REGNO (XEXP (x,0)) == REG_X)
-       {
-         /* This is a paranoid case LEGITIMIZE_RELOAD_ADDRESS must exclude
-            it but I have this situation with extremal optimizing options.  */
-         if (reg_overlap_mentioned_p (dest, XEXP (x,0))
-             || reg_unused_after (insn, XEXP (x,0)))
-           return *l = 2, (AS2 (adiw,r26,%o1) CR_TAB
-                           AS2 (ld,%0,X));
-
-         return *l = 3, (AS2 (adiw,r26,%o1) CR_TAB
-                         AS2 (ld,%0,X)      CR_TAB
-                         AS2 (sbiw,r26,%o1));
-       }
-      *l = 1;
-      return AS2 (ldd,%0,%1);
+          return "";
+        }
+
+      return avr_asm_len ("ldd %0,%1", op, plen, -1);
     }
-  *l = 1;
-  return AS2 (ld,%0,%1);
+  
+  return avr_asm_len ("ld %0,%1", op, plen, -1);
 }
 
-const char *
-out_movhi_r_mr (rtx insn, rtx op[], int *l)
+static const char*
+out_movhi_r_mr (rtx insn, rtx op[], int *plen)
 {
   rtx dest = op[0];
   rtx src = op[1];
@@ -2165,39 +3063,25 @@ out_movhi_r_mr (rtx insn, rtx op[], int *l)
   /* "volatile" forces reading low byte first, even if less efficient,
      for correct operation with 16-bit I/O registers.  */
   int mem_volatile_p = MEM_VOLATILE_P (src);
-  int tmp;
-
-  if (!l)
-    l = &tmp;
 
   if (reg_base > 0)
     {
       if (reg_dest == reg_base)         /* R = (R) */
-       {
-         *l = 3;
-         return (AS2 (ld,__tmp_reg__,%1+) CR_TAB
-                 AS2 (ld,%B0,%1) CR_TAB
-                 AS2 (mov,%A0,__tmp_reg__));
-       }
-      else if (reg_base == REG_X)        /* (R26) */
-        {
-          if (reg_unused_after (insn, base))
-           {
-             *l = 2;
-             return (AS2 (ld,%A0,X+) CR_TAB
-                     AS2 (ld,%B0,X));
-           }
-         *l  = 3;
-         return (AS2 (ld,%A0,X+) CR_TAB
-                 AS2 (ld,%B0,X) CR_TAB
-                 AS2 (sbiw,r26,1));
-        }
-      else                      /* (R)  */
-       {
-         *l = 2;
-         return (AS2 (ld,%A0,%1)    CR_TAB
-                 AS2 (ldd,%B0,%1+1));
-       }
+        return avr_asm_len ("ld __tmp_reg__,%1+" CR_TAB
+                            "ld %B0,%1"          CR_TAB
+                            "mov %A0,__tmp_reg__", op, plen, -3);
+
+      if (reg_base != REG_X)
+        return avr_asm_len ("ld %A0,%1" CR_TAB
+                            "ldd %B0,%1+1", op, plen, -2);
+      
+      avr_asm_len ("ld %A0,X+" CR_TAB
+                   "ld %B0,X", op, plen, -2);
+          
+      if (!reg_unused_after (insn, base))
+        avr_asm_len ("sbiw r26,1", op, plen, 1);
+
+      return "";
     }
   else if (GET_CODE (base) == PLUS) /* (R + i) */
     {
@@ -2205,109 +3089,90 @@ out_movhi_r_mr (rtx insn, rtx op[], int *l)
       int reg_base = true_regnum (XEXP (base, 0));
       
       if (disp > MAX_LD_OFFSET (GET_MODE (src)))
-       {
-         if (REGNO (XEXP (base, 0)) != REG_Y)
-           fatal_insn ("incorrect insn:",insn);
-         
-         if (disp <= 63 + MAX_LD_OFFSET (GET_MODE (src)))
-           return *l = 4, (AS2 (adiw,r28,%o1-62) CR_TAB
-                           AS2 (ldd,%A0,Y+62)    CR_TAB
-                           AS2 (ldd,%B0,Y+63)    CR_TAB
-                           AS2 (sbiw,r28,%o1-62));
-
-         return *l = 6, (AS2 (subi,r28,lo8(-%o1)) CR_TAB
-                         AS2 (sbci,r29,hi8(-%o1)) CR_TAB
-                         AS2 (ld,%A0,Y)           CR_TAB
-                         AS2 (ldd,%B0,Y+1)        CR_TAB
-                         AS2 (subi,r28,lo8(%o1))  CR_TAB
-                         AS2 (sbci,r29,hi8(%o1)));
-       }
-      if (reg_base == REG_X)
-       {
-         /* This is a paranoid case. LEGITIMIZE_RELOAD_ADDRESS must exclude
-            it but I have this situation with extremal
-            optimization options.  */
-         
-         *l = 4;
-         if (reg_base == reg_dest)
-           return (AS2 (adiw,r26,%o1)      CR_TAB
-                   AS2 (ld,__tmp_reg__,X+) CR_TAB
-                   AS2 (ld,%B0,X)          CR_TAB
-                   AS2 (mov,%A0,__tmp_reg__));
+        {
+          if (REGNO (XEXP (base, 0)) != REG_Y)
+            fatal_insn ("incorrect insn:",insn);
+          
+          return disp <= 63 + MAX_LD_OFFSET (GET_MODE (src))
+            ? avr_asm_len ("adiw r28,%o1-62" CR_TAB
+                           "ldd %A0,Y+62"    CR_TAB
+                           "ldd %B0,Y+63"    CR_TAB
+                           "sbiw r28,%o1-62", op, plen, -4)
+
+            : avr_asm_len ("subi r28,lo8(-%o1)" CR_TAB
+                           "sbci r29,hi8(-%o1)" CR_TAB
+                           "ld %A0,Y"           CR_TAB
+                           "ldd %B0,Y+1"        CR_TAB
+                           "subi r28,lo8(%o1)"  CR_TAB
+                           "sbci r29,hi8(%o1)", op, plen, -6);
+        }
 
-         return (AS2 (adiw,r26,%o1) CR_TAB
-                 AS2 (ld,%A0,X+)    CR_TAB
-                 AS2 (ld,%B0,X)     CR_TAB
-                 AS2 (sbiw,r26,%o1+1));
-       }
+      /* This is a paranoid case. LEGITIMIZE_RELOAD_ADDRESS must exclude
+         it but I have this situation with extremal
+         optimization options.  */
 
-      if (reg_base == reg_dest)
-       {
-         *l = 3;
-         return (AS2 (ldd,__tmp_reg__,%A1) CR_TAB
-                 AS2 (ldd,%B0,%B1)         CR_TAB
-                 AS2 (mov,%A0,__tmp_reg__));
-       }
-      
-      *l = 2;
-      return (AS2 (ldd,%A0,%A1) CR_TAB
-             AS2 (ldd,%B0,%B1));
+      if (reg_base == REG_X)
+        return reg_base == reg_dest
+          ? avr_asm_len ("adiw r26,%o1"      CR_TAB
+                         "ld __tmp_reg__,X+" CR_TAB
+                         "ld %B0,X"          CR_TAB
+                         "mov %A0,__tmp_reg__", op, plen, -4)
+
+          : avr_asm_len ("adiw r26,%o1" CR_TAB
+                         "ld %A0,X+"    CR_TAB
+                         "ld %B0,X"     CR_TAB
+                         "sbiw r26,%o1+1", op, plen, -4);
+
+      return reg_base == reg_dest
+        ? avr_asm_len ("ldd __tmp_reg__,%A1" CR_TAB
+                       "ldd %B0,%B1"         CR_TAB
+                       "mov %A0,__tmp_reg__", op, plen, -3)
+
+        : avr_asm_len ("ldd %A0,%A1" CR_TAB
+                       "ldd %B0,%B1", op, plen, -2);
     }
   else if (GET_CODE (base) == PRE_DEC) /* (--R) */
     {
       if (reg_overlap_mentioned_p (dest, XEXP (base, 0)))
-       fatal_insn ("incorrect insn:", insn);
+        fatal_insn ("incorrect insn:", insn);
 
-      if (mem_volatile_p)
-        {
-          if (REGNO (XEXP (base, 0)) == REG_X)
-            {
-              *l = 4;
-              return (AS2 (sbiw,r26,2)  CR_TAB
-                      AS2 (ld,%A0,X+)   CR_TAB
-                      AS2 (ld,%B0,X)    CR_TAB
-                      AS2 (sbiw,r26,1));
-            }
-          else
-            {
-              *l = 3;
-              return (AS2 (sbiw,%r1,2)   CR_TAB
-                      AS2 (ld,%A0,%p1)  CR_TAB
-                      AS2 (ldd,%B0,%p1+1));
-            }
-        }
-
-      *l = 2;
-      return (AS2 (ld,%B0,%1) CR_TAB
-             AS2 (ld,%A0,%1));
+      if (!mem_volatile_p)
+        return avr_asm_len ("ld %B0,%1" CR_TAB
+                            "ld %A0,%1", op, plen, -2);
+      
+      return REGNO (XEXP (base, 0)) == REG_X
+        ? avr_asm_len ("sbiw r26,2"  CR_TAB
+                       "ld %A0,X+"   CR_TAB
+                       "ld %B0,X"    CR_TAB
+                       "sbiw r26,1", op, plen, -4)
+        
+        : avr_asm_len ("sbiw %r1,2"  CR_TAB
+                       "ld %A0,%p1"  CR_TAB
+                       "ldd %B0,%p1+1", op, plen, -3);
     }
   else if (GET_CODE (base) == POST_INC) /* (R++) */
     {
       if (reg_overlap_mentioned_p (dest, XEXP (base, 0)))
-       fatal_insn ("incorrect insn:", insn);
+        fatal_insn ("incorrect insn:", insn);
 
-      *l = 2;
-      return (AS2 (ld,%A0,%1)  CR_TAB
-             AS2 (ld,%B0,%1));
+      return avr_asm_len ("ld %A0,%1"  CR_TAB
+                          "ld %B0,%1", op, plen, -2);
     }
   else if (CONSTANT_ADDRESS_P (base))
     {
-      if (optimize > 0 && io_address_operand (base, HImode))
-       {
-         *l = 2;
-         return (AS2 (in,%A0,%m1-0x20) CR_TAB
-                 AS2 (in,%B0,%m1+1-0x20));
-       }
-      *l = 4;
-      return (AS2 (lds,%A0,%m1) CR_TAB
-             AS2 (lds,%B0,%m1+1));
+      return optimize > 0 && io_address_operand (base, HImode)
+        ? avr_asm_len ("in %A0,%i1" CR_TAB
+                       "in %B0,%i1+1", op, plen, -2)
+
+        : avr_asm_len ("lds %A0,%m1" CR_TAB
+                       "lds %B0,%m1+1", op, plen, -4);
     }
   
   fatal_insn ("unknown move insn:",insn);
   return "";
 }
 
-const char *
+static const char*
 out_movsi_r_mr (rtx insn, rtx op[], int *l)
 {
   rtx dest = op[0];
@@ -2468,7 +3333,7 @@ out_movsi_r_mr (rtx insn, rtx op[], int *l)
   return "";
 }
 
-const char *
+static const char*
 out_movsi_mr_r (rtx insn, rtx op[], int *l)
 {
   rtx dest = op[0];
@@ -2624,13 +3489,19 @@ out_movsi_mr_r (rtx insn, rtx op[], int *l)
 }
 
 const char *
-output_movsisf (rtx insn, rtx operands[], rtx clobber_reg, int *l)
+output_movsisf (rtx insn, rtx operands[], int *l)
 {
   int dummy;
   rtx dest = operands[0];
   rtx src = operands[1];
   int *real_l = l;
   
+  if (avr_mem_pgm_p (src)
+      || avr_mem_pgm_p (dest))
+    {
+      return avr_out_lpm (insn, operands, real_l);
+    }
+
   if (!l)
     l = &dummy;
   
@@ -2667,138 +3538,383 @@ output_movsisf (rtx insn, rtx operands[], rtx clobber_reg, 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 */
-           {
-             *l = 4;
-             return (AS2 (ldi,%A0,lo8(%1))  CR_TAB
-                     AS2 (ldi,%B0,hi8(%1))  CR_TAB
-                     AS2 (ldi,%C0,hlo8(%1)) CR_TAB
-                     AS2 (ldi,%D0,hhi8(%1)));
-           }
-         /* Last resort, better than loading from memory.  */
-         *l = 10;
-         return (AS2 (mov,__tmp_reg__,r31) CR_TAB
-                 AS2 (ldi,r31,lo8(%1))     CR_TAB
-                 AS2 (mov,%A0,r31)         CR_TAB
-                 AS2 (ldi,r31,hi8(%1))     CR_TAB
-                 AS2 (mov,%B0,r31)         CR_TAB
-                 AS2 (ldi,r31,hlo8(%1))    CR_TAB
-                 AS2 (mov,%C0,r31)         CR_TAB
-                 AS2 (ldi,r31,hhi8(%1))    CR_TAB
-                 AS2 (mov,%D0,r31)         CR_TAB
-                 AS2 (mov,r31,__tmp_reg__));
-       }
+          return output_reload_insisf (operands, NULL_RTX, real_l);
+        }
       else if (GET_CODE (src) == MEM)
        return out_movsi_r_mr (insn, operands, real_l); /* mov r,m */
     }
-  else if (GET_CODE (dest) == MEM)
+  else if (GET_CODE (dest) == MEM)
+    {
+      const char *templ;
+
+      if (src == CONST0_RTX (GET_MODE (dest)))
+         operands[1] = zero_reg_rtx;
+
+      templ = out_movsi_mr_r (insn, operands, real_l);
+
+      if (!real_l)
+       output_asm_insn (templ, operands);
+
+      operands[1] = src;
+      return "";
+    }
+  fatal_insn ("invalid insn:", insn);
+  return "";
+}
+
+
+/* Handle loads of 24-bit types from memory to register.  */
+
+static const char*
+avr_out_load_psi (rtx insn, rtx *op, int *plen)
+{
+  rtx dest = op[0];
+  rtx src = op[1];
+  rtx base = XEXP (src, 0);
+  int reg_dest = true_regnum (dest);
+  int reg_base = true_regnum (base);
+  
+  if (reg_base > 0)
+    {
+      if (reg_base == REG_X)        /* (R26) */
+        {
+          if (reg_dest == REG_X)
+            /* "ld r26,-X" is undefined */
+            return avr_asm_len ("adiw r26,2"        CR_TAB
+                                "ld r28,X"          CR_TAB
+                                "ld __tmp_reg__,-X" CR_TAB
+                                "sbiw r26,1"        CR_TAB
+                                "ld r26,X"          CR_TAB
+                                "mov r27,__tmp_reg__", op, plen, -6);
+          else
+            {
+              avr_asm_len ("ld %A0,X+" CR_TAB
+                           "ld %B0,X+" CR_TAB
+                           "ld %C0,X", op, plen, -3);
+
+              if (reg_dest != REG_X - 2
+                  && !reg_unused_after (insn, base))
+                {
+                  avr_asm_len ("sbiw r26,2", op, plen, 1);
+                }
+
+              return "";
+            }
+        }
+      else /* reg_base != REG_X */
+        {
+          if (reg_dest == reg_base)
+            return avr_asm_len ("ldd %C0,%1+2"          CR_TAB
+                                "ldd __tmp_reg__,%1+1"  CR_TAB
+                                "ld  %A0,%1"            CR_TAB
+                                "mov %B0,__tmp_reg__", op, plen, -4);
+          else
+            return avr_asm_len ("ld  %A0,%1"    CR_TAB
+                                "ldd %B0,%1+1"  CR_TAB
+                                "ldd %C0,%1+2", op, plen, -3);
+        }
+    }
+  else if (GET_CODE (base) == PLUS) /* (R + i) */
+    {
+      int disp = INTVAL (XEXP (base, 1));
+      
+      if (disp > MAX_LD_OFFSET (GET_MODE (src)))
+        {
+          if (REGNO (XEXP (base, 0)) != REG_Y)
+            fatal_insn ("incorrect insn:",insn);
+
+          if (disp <= 63 + MAX_LD_OFFSET (GET_MODE (src)))
+            return avr_asm_len ("adiw r28,%o1-61" CR_TAB
+                                "ldd %A0,Y+61"    CR_TAB
+                                "ldd %B0,Y+62"    CR_TAB
+                                "ldd %C0,Y+63"    CR_TAB
+                                "sbiw r28,%o1-61", op, plen, -5);
+
+          return avr_asm_len ("subi r28,lo8(-%o1)" CR_TAB
+                              "sbci r29,hi8(-%o1)" CR_TAB
+                              "ld  %A0,Y"           CR_TAB
+                              "ldd %B0,Y+1"        CR_TAB
+                              "ldd %C0,Y+2"        CR_TAB
+                              "subi r28,lo8(%o1)"  CR_TAB
+                              "sbci r29,hi8(%o1)", op, plen, -7);
+        }
+
+      reg_base = true_regnum (XEXP (base, 0));
+      if (reg_base == REG_X)
+        {
+          /* R = (X + d) */
+          if (reg_dest == REG_X)
+            {
+              /* "ld r26,-X" is undefined */
+              return avr_asm_len ("adiw r26,%o1+2"     CR_TAB
+                                  "ld  r28,X"          CR_TAB
+                                  "ld  __tmp_reg__,-X" CR_TAB
+                                  "sbiw r26,1"         CR_TAB
+                                  "ld  r26,X"          CR_TAB
+                                  "mov r27,__tmp_reg__", op, plen, -6);
+            }
+          
+            avr_asm_len ("adiw r26,%o1"      CR_TAB
+                         "ld r24,X+"         CR_TAB
+                         "ld r25,X+"         CR_TAB
+                         "ld r26,X", op, plen, -4);
+
+            if (reg_dest != REG_X - 2)
+              avr_asm_len ("sbiw r26,%o1+2", op, plen, 1);
+
+            return "";
+        }
+      
+      if (reg_dest == reg_base)
+        return avr_asm_len ("ldd %C0,%C1" CR_TAB
+                            "ldd __tmp_reg__,%B1"  CR_TAB
+                            "ldd %A0,%A1" CR_TAB
+                            "mov %B0,__tmp_reg__", op, plen, -4);
+
+        return avr_asm_len ("ldd %A0,%A1" CR_TAB
+                            "ldd %B0,%B1" CR_TAB
+                            "ldd %C0,%C1", op, plen, -3);
+    }
+  else if (GET_CODE (base) == PRE_DEC) /* (--R) */
+    return avr_asm_len ("ld %C0,%1" CR_TAB
+                        "ld %B0,%1" CR_TAB
+                        "ld %A0,%1", op, plen, -3);
+  else if (GET_CODE (base) == POST_INC) /* (R++) */
+    return avr_asm_len ("ld %A0,%1" CR_TAB
+                        "ld %B0,%1" CR_TAB
+                        "ld %C0,%1", op, plen, -3);
+
+  else if (CONSTANT_ADDRESS_P (base))
+    return avr_asm_len ("lds %A0,%m1" CR_TAB
+                        "lds %B0,%m1+1" CR_TAB
+                        "lds %C0,%m1+2", op, plen , -6);
+  
+  fatal_insn ("unknown move insn:",insn);
+  return "";
+}
+
+/* Handle store of 24-bit type from register or zero to memory.  */
+
+static const char*
+avr_out_store_psi (rtx insn, rtx *op, int *plen)
+{
+  rtx dest = op[0];
+  rtx src = op[1];
+  rtx base = XEXP (dest, 0);
+  int reg_base = true_regnum (base);
+  
+  if (CONSTANT_ADDRESS_P (base))
+    return avr_asm_len ("sts %m0,%A1"   CR_TAB
+                        "sts %m0+1,%B1" CR_TAB
+                        "sts %m0+2,%C1", op, plen, -6);
+  
+  if (reg_base > 0)                 /* (r) */
+    {
+      if (reg_base == REG_X)        /* (R26) */
+        {
+          gcc_assert (!reg_overlap_mentioned_p (base, src));
+          
+          avr_asm_len ("st %0+,%A1"  CR_TAB
+                       "st %0+,%B1" CR_TAB
+                       "st %0,%C1", op, plen, -3);
+
+          if (!reg_unused_after (insn, base))
+            avr_asm_len ("sbiw r26,2", op, plen, 1);
+
+          return "";
+        }
+      else
+        return avr_asm_len ("st %0,%A1"    CR_TAB
+                            "std %0+1,%B1" CR_TAB
+                            "std %0+2,%C1", op, plen, -3);
+    }
+  else if (GET_CODE (base) == PLUS) /* (R + i) */
+    {
+      int disp = INTVAL (XEXP (base, 1));
+      reg_base = REGNO (XEXP (base, 0));
+
+      if (disp > MAX_LD_OFFSET (GET_MODE (dest)))
+        {
+          if (reg_base != REG_Y)
+            fatal_insn ("incorrect insn:",insn);
+
+          if (disp <= 63 + MAX_LD_OFFSET (GET_MODE (dest)))
+            return avr_asm_len ("adiw r28,%o0-61" CR_TAB
+                                "std Y+61,%A1"    CR_TAB
+                                "std Y+62,%B1"    CR_TAB
+                                "std Y+63,%C1"    CR_TAB
+                                "sbiw r28,%o0-60", op, plen, -5);
+
+          return avr_asm_len ("subi r28,lo8(-%o0)" CR_TAB
+                              "sbci r29,hi8(-%o0)" CR_TAB
+                              "st Y,%A1"           CR_TAB
+                              "std Y+1,%B1"        CR_TAB
+                              "std Y+2,%C1"        CR_TAB
+                              "subi r28,lo8(%o0)"  CR_TAB
+                              "sbci r29,hi8(%o0)", op, plen, -7);
+        }
+      if (reg_base == REG_X)
+        {
+          /* (X + d) = R */
+          gcc_assert (!reg_overlap_mentioned_p (XEXP (base, 0), src));
+          
+          avr_asm_len ("adiw r26,%o0" CR_TAB
+                       "st X+,%A1"    CR_TAB
+                       "st X+,%B1"    CR_TAB
+                       "st X,%C1", op, plen, -4);
+
+          if (!reg_unused_after (insn, XEXP (base, 0)))
+            avr_asm_len ("sbiw r26,%o0+2", op, plen, 1);
+
+          return "";
+        }
+      
+      return avr_asm_len ("std %A0,%A1" CR_TAB
+                          "std %B0,%B1" CR_TAB
+                          "std %C0,%C1", op, plen, -3);
+    }
+  else if (GET_CODE (base) == PRE_DEC) /* (--R) */
+    return avr_asm_len ("st %0,%C1" CR_TAB
+                        "st %0,%B1" CR_TAB
+                        "st %0,%A1", op, plen, -3);
+  else if (GET_CODE (base) == POST_INC) /* (R++) */
+    return avr_asm_len ("st %0,%A1" CR_TAB
+                        "st %0,%B1" CR_TAB
+                        "st %0,%C1", op, plen, -3);
+
+  fatal_insn ("unknown move insn:",insn);
+  return "";
+}
+
+
+/* Move around 24-bit stuff.  */
+
+const char *
+avr_out_movpsi (rtx insn, rtx *op, int *plen)
+{
+  rtx dest = op[0];
+  rtx src = op[1];
+  
+  if (avr_mem_pgm_p (src)
+      || avr_mem_pgm_p (dest))
+    {
+      return avr_out_lpm (insn, op, plen);
+    }
+  
+  if (register_operand (dest, VOIDmode))
+    {
+      if (register_operand (src, VOIDmode)) /* mov r,r */
+        {
+          if (true_regnum (dest) > true_regnum (src))
+            {
+              avr_asm_len ("mov %C0,%C1", op, plen, -1);
+
+              if (AVR_HAVE_MOVW)
+                return avr_asm_len ("movw %A0,%A1", op, plen, 1);
+              else
+                return avr_asm_len ("mov %B0,%B1"  CR_TAB
+                                    "mov %A0,%A1", op, plen, 2);
+            }
+          else
+            {
+              if (AVR_HAVE_MOVW)
+                avr_asm_len ("movw %A0,%A1", op, plen, -1);
+              else
+                avr_asm_len ("mov %A0,%A1"  CR_TAB
+                             "mov %B0,%B1", op, plen, -2);
+              
+              return avr_asm_len ("mov %C0,%C1", op, plen, 1);
+            }
+        }
+      else if (CONSTANT_P (src))
+        {
+          return avr_out_reload_inpsi (op, NULL_RTX, plen);
+        }
+      else if (MEM_P (src))
+        return avr_out_load_psi (insn, op, plen); /* mov r,m */
+    }
+  else if (MEM_P (dest))
     {
-      const char *templ;
-
-      if (src == CONST0_RTX (GET_MODE (dest)))
-         operands[1] = zero_reg_rtx;
-
-      templ = out_movsi_mr_r (insn, operands, real_l);
-
-      if (!real_l)
-       output_asm_insn (templ, operands);
+      rtx xop[2];
 
-      operands[1] = src;
-      return "";
+      xop[0] = dest;
+      xop[1] = src == CONST0_RTX (GET_MODE (dest)) ? zero_reg_rtx : src;
+      
+      return avr_out_store_psi (insn, xop, plen);
     }
+    
   fatal_insn ("invalid insn:", insn);
   return "";
 }
 
-const char *
-out_movqi_mr_r (rtx insn, rtx op[], int *l)
+
+static const char*
+out_movqi_mr_r (rtx insn, rtx op[], int *plen)
 {
   rtx dest = op[0];
   rtx src = op[1];
   rtx x = XEXP (dest, 0);
-  int dummy;
-
-  if (!l)
-    l = &dummy;
   
   if (CONSTANT_ADDRESS_P (x))
     {
-      if (CONST_INT_P (x) && INTVAL (x) == SREG_ADDR)
-       {
-         *l = 1;
-         return AS2 (out,__SREG__,%1);
-       }
-      if (optimize > 0 && io_address_operand (x, QImode))
-       {
-         *l = 1;
-         return AS2 (out,%m0-0x20,%1);
-       }
-      *l = 2;
-      return AS2 (sts,%m0,%1);
+      return optimize > 0 && io_address_operand (x, QImode)
+        ? avr_asm_len ("out %i0,%1", op, plen, -1)
+        : avr_asm_len ("sts %m0,%1", op, plen, -2);
     }
-  /* memory access by reg+disp */
-  else if (GET_CODE (x) == PLUS        
-      && REG_P (XEXP (x,0))
-      && GET_CODE (XEXP (x,1)) == CONST_INT)
+  else if (GET_CODE (x) == PLUS
+           && REG_P (XEXP (x, 0))
+           && CONST_INT_P (XEXP (x, 1)))
     {
-      if ((INTVAL (XEXP (x,1)) - GET_MODE_SIZE (GET_MODE (dest))) >= 63)
-       {
-         int disp = INTVAL (XEXP (x,1));
-         if (REGNO (XEXP (x,0)) != REG_Y)
-           fatal_insn ("incorrect insn:",insn);
+      /* memory access by reg+disp */
 
-         if (disp <= 63 + MAX_LD_OFFSET (GET_MODE (dest)))
-           return *l = 3, (AS2 (adiw,r28,%o0-63) CR_TAB
-                           AS2 (std,Y+63,%1)     CR_TAB
-                           AS2 (sbiw,r28,%o0-63));
+      int disp = INTVAL (XEXP (x, 1));
 
-         return *l = 5, (AS2 (subi,r28,lo8(-%o0)) CR_TAB
-                         AS2 (sbci,r29,hi8(-%o0)) CR_TAB
-                         AS2 (st,Y,%1)            CR_TAB
-                         AS2 (subi,r28,lo8(%o0))  CR_TAB
-                         AS2 (sbci,r29,hi8(%o0)));
-       }
+      if (disp - GET_MODE_SIZE (GET_MODE (dest)) >= 63)
+        {
+          if (REGNO (XEXP (x, 0)) != REG_Y)
+            fatal_insn ("incorrect insn:",insn);
+
+          if (disp <= 63 + MAX_LD_OFFSET (GET_MODE (dest)))
+            return avr_asm_len ("adiw r28,%o0-63" CR_TAB
+                                "std Y+63,%1"     CR_TAB
+                                "sbiw r28,%o0-63", op, plen, -3);
+
+          return avr_asm_len ("subi r28,lo8(-%o0)" CR_TAB
+                              "sbci r29,hi8(-%o0)" CR_TAB
+                              "st Y,%1"            CR_TAB
+                              "subi r28,lo8(%o0)"  CR_TAB
+                              "sbci r29,hi8(%o0)", op, plen, -5);
+        }
       else if (REGNO (XEXP (x,0)) == REG_X)
-       {
-         if (reg_overlap_mentioned_p (src, XEXP (x, 0)))
-           {
-             if (reg_unused_after (insn, XEXP (x,0)))
-               return *l = 3, (AS2 (mov,__tmp_reg__,%1) CR_TAB
-                               AS2 (adiw,r26,%o0)       CR_TAB
-                               AS2 (st,X,__tmp_reg__));
-
-             return *l = 4, (AS2 (mov,__tmp_reg__,%1) CR_TAB
-                             AS2 (adiw,r26,%o0)       CR_TAB
-                             AS2 (st,X,__tmp_reg__)   CR_TAB
-                             AS2 (sbiw,r26,%o0));
-           }
-         else
-           {
-             if (reg_unused_after (insn, XEXP (x,0)))
-               return *l = 2, (AS2 (adiw,r26,%o0) CR_TAB
-                               AS2 (st,X,%1));
+        {
+          if (reg_overlap_mentioned_p (src, XEXP (x, 0)))
+            {
+              avr_asm_len ("mov __tmp_reg__,%1" CR_TAB
+                           "adiw r26,%o0"       CR_TAB
+                           "st X,__tmp_reg__", op, plen, -3);
+            }
+          else
+            {
+              avr_asm_len ("adiw r26,%o0" CR_TAB
+                           "st X,%1", op, plen, -2);
+            }
+          
+          if (!reg_unused_after (insn, XEXP (x,0)))
+            avr_asm_len ("sbiw r26,%o0", op, plen, 1);
 
-             return *l = 3, (AS2 (adiw,r26,%o0) CR_TAB
-                             AS2 (st,X,%1)      CR_TAB
-                             AS2 (sbiw,r26,%o0));
-           }
-       }
-      *l = 1;
-      return AS2 (std,%0,%1);
+          return "";
+        }
+      
+      return avr_asm_len ("std %0,%1", op, plen, 1);
     }
-  *l = 1;
-  return AS2 (st,%0,%1);
+  
+  return avr_asm_len ("st %0,%1", op, plen, 1);
 }
 
-const char *
-out_movhi_mr_r (rtx insn, rtx op[], int *l)
+static const char*
+out_movhi_mr_r (rtx insn, rtx op[], int *plen)
 {
   rtx dest = op[0];
   rtx src = op[1];
@@ -2808,127 +3924,103 @@ out_movhi_mr_r (rtx insn, rtx op[], int *l)
   /* "volatile" forces writing high byte first, even if less efficient,
      for correct operation with 16-bit I/O registers.  */
   int mem_volatile_p = MEM_VOLATILE_P (dest);
-  int tmp;
 
-  if (!l)
-    l = &tmp;
   if (CONSTANT_ADDRESS_P (base))
-    {
-      if (optimize > 0 && io_address_operand (base, HImode))
-       {
-         *l = 2;
-         return (AS2 (out,%m0+1-0x20,%B1) CR_TAB
-                 AS2 (out,%m0-0x20,%A1));
-       }
-      return *l = 4, (AS2 (sts,%m0+1,%B1) CR_TAB
-                     AS2 (sts,%m0,%A1));
-    }
+    return optimize > 0 && io_address_operand (base, HImode)
+      ? avr_asm_len ("out %i0+1,%B1" CR_TAB
+                     "out %i0,%A1", op, plen, -2)
+
+      : avr_asm_len ("sts %m0+1,%B1" CR_TAB
+                     "sts %m0,%A1", op, plen, -4);
+  
   if (reg_base > 0)
     {
-      if (reg_base == REG_X)
-        {
-          if (reg_src == REG_X)
-            {
-              /* "st X+,r26" and "st -X,r26" are undefined.  */
-              if (!mem_volatile_p && reg_unused_after (insn, src))
-               return *l=4, (AS2 (mov,__tmp_reg__,r27) CR_TAB
-                             AS2 (st,X,r26)            CR_TAB
-                             AS2 (adiw,r26,1)          CR_TAB
-                             AS2 (st,X,__tmp_reg__));
-              else
-               return *l=5, (AS2 (mov,__tmp_reg__,r27) CR_TAB
-                             AS2 (adiw,r26,1)          CR_TAB
-                             AS2 (st,X,__tmp_reg__)    CR_TAB
-                              AS2 (sbiw,r26,1)          CR_TAB
-                              AS2 (st,X,r26));
-            }
-          else
-            {
-              if (!mem_volatile_p && reg_unused_after (insn, base))
-                return *l=2, (AS2 (st,X+,%A1) CR_TAB
-                              AS2 (st,X,%B1));
-              else
-                return *l=3, (AS2 (adiw,r26,1) CR_TAB
-                              AS2 (st,X,%B1)   CR_TAB
-                              AS2 (st,-X,%A1));
-            }
-        }
-      else
-        return  *l=2, (AS2 (std,%0+1,%B1) CR_TAB
-                       AS2 (st,%0,%A1));
+      if (reg_base != REG_X)
+        return avr_asm_len ("std %0+1,%B1" CR_TAB
+                            "st %0,%A1", op, plen, -2);
+      
+      if (reg_src == REG_X)
+        /* "st X+,r26" and "st -X,r26" are undefined.  */
+        return !mem_volatile_p && reg_unused_after (insn, src)
+          ? avr_asm_len ("mov __tmp_reg__,r27" CR_TAB
+                         "st X,r26"            CR_TAB
+                         "adiw r26,1"          CR_TAB
+                         "st X,__tmp_reg__", op, plen, -4)
+
+          : avr_asm_len ("mov __tmp_reg__,r27" CR_TAB
+                         "adiw r26,1"          CR_TAB
+                         "st X,__tmp_reg__"    CR_TAB
+                         "sbiw r26,1"          CR_TAB
+                         "st X,r26", op, plen, -5);
+      
+      return !mem_volatile_p && reg_unused_after (insn, base)
+        ? avr_asm_len ("st X+,%A1" CR_TAB
+                       "st X,%B1", op, plen, -2)
+        : avr_asm_len ("adiw r26,1" CR_TAB
+                       "st X,%B1"   CR_TAB
+                       "st -X,%A1", op, plen, -3);
     }
   else if (GET_CODE (base) == PLUS)
     {
       int disp = INTVAL (XEXP (base, 1));
       reg_base = REGNO (XEXP (base, 0));
       if (disp > MAX_LD_OFFSET (GET_MODE (dest)))
-       {
-         if (reg_base != REG_Y)
-           fatal_insn ("incorrect insn:",insn);
-
-         if (disp <= 63 + MAX_LD_OFFSET (GET_MODE (dest)))
-           return *l = 4, (AS2 (adiw,r28,%o0-62) CR_TAB
-                           AS2 (std,Y+63,%B1)    CR_TAB
-                           AS2 (std,Y+62,%A1)    CR_TAB
-                           AS2 (sbiw,r28,%o0-62));
-
-         return *l = 6, (AS2 (subi,r28,lo8(-%o0)) CR_TAB
-                         AS2 (sbci,r29,hi8(-%o0)) CR_TAB
-                         AS2 (std,Y+1,%B1)        CR_TAB
-                         AS2 (st,Y,%A1)           CR_TAB
-                         AS2 (subi,r28,lo8(%o0))  CR_TAB
-                         AS2 (sbci,r29,hi8(%o0)));
-       }
-      if (reg_base == REG_X)
-       {
-         /* (X + d) = R */
-         if (reg_src == REG_X)
-            {
-             *l = 7;
-             return (AS2 (mov,__tmp_reg__,r26)  CR_TAB
-                     AS2 (mov,__zero_reg__,r27) CR_TAB
-                      AS2 (adiw,r26,%o0+1)       CR_TAB
-                     AS2 (st,X,__zero_reg__)    CR_TAB
-                     AS2 (st,-X,__tmp_reg__)    CR_TAB
-                     AS1 (clr,__zero_reg__)     CR_TAB
-                      AS2 (sbiw,r26,%o0));
-           }
-         *l = 4;
-          return (AS2 (adiw,r26,%o0+1) CR_TAB
-                  AS2 (st,X,%B1)       CR_TAB
-                  AS2 (st,-X,%A1)      CR_TAB
-                  AS2 (sbiw,r26,%o0));
-       }
-      return *l=2, (AS2 (std,%B0,%B1)    CR_TAB
-                    AS2 (std,%A0,%A1));
+        {
+          if (reg_base != REG_Y)
+            fatal_insn ("incorrect insn:",insn);
+          
+          return disp <= 63 + MAX_LD_OFFSET (GET_MODE (dest))
+            ? avr_asm_len ("adiw r28,%o0-62" CR_TAB
+                           "std Y+63,%B1"    CR_TAB
+                           "std Y+62,%A1"    CR_TAB
+                           "sbiw r28,%o0-62", op, plen, -4)
+
+            : avr_asm_len ("subi r28,lo8(-%o0)" CR_TAB
+                           "sbci r29,hi8(-%o0)" CR_TAB
+                           "std Y+1,%B1"        CR_TAB
+                           "st Y,%A1"           CR_TAB
+                           "subi r28,lo8(%o0)"  CR_TAB
+                           "sbci r29,hi8(%o0)", op, plen, -6);
+        }
+      
+      if (reg_base != REG_X)
+        return avr_asm_len ("std %B0,%B1" CR_TAB
+                            "std %A0,%A1", op, plen, -2);
+      /* (X + d) = R */
+      return reg_src == REG_X
+        ? avr_asm_len ("mov __tmp_reg__,r26"  CR_TAB
+                       "mov __zero_reg__,r27" CR_TAB
+                       "adiw r26,%o0+1"       CR_TAB
+                       "st X,__zero_reg__"    CR_TAB
+                       "st -X,__tmp_reg__"    CR_TAB
+                       "clr __zero_reg__"     CR_TAB
+                       "sbiw r26,%o0", op, plen, -7)
+
+        : avr_asm_len ("adiw r26,%o0+1" CR_TAB
+                       "st X,%B1"       CR_TAB
+                       "st -X,%A1"      CR_TAB
+                       "sbiw r26,%o0", op, plen, -4);
     }
   else if (GET_CODE (base) == PRE_DEC) /* (--R) */
-    return *l=2, (AS2 (st,%0,%B1) CR_TAB
-                 AS2 (st,%0,%A1));
+    {
+      return avr_asm_len ("st %0,%B1" CR_TAB
+                          "st %0,%A1", op, plen, -2);
+    }
   else if (GET_CODE (base) == POST_INC) /* (R++) */
     {
-      if (mem_volatile_p)
-        {
-          if (REGNO (XEXP (base, 0)) == REG_X)
-            {
-              *l = 4;
-              return (AS2 (adiw,r26,1)  CR_TAB
-                      AS2 (st,X,%B1)    CR_TAB
-                      AS2 (st,-X,%A1)   CR_TAB
-                      AS2 (adiw,r26,2));
-            }
-          else
-            {
-              *l = 3;
-              return (AS2 (std,%p0+1,%B1) CR_TAB
-                      AS2 (st,%p0,%A1)    CR_TAB
-                      AS2 (adiw,%r0,2));
-            }
-        }
-
-      *l = 2;
-      return (AS2 (st,%0,%A1)  CR_TAB
-            AS2 (st,%0,%B1));
+      if (!mem_volatile_p)
+        return avr_asm_len ("st %0,%A1"  CR_TAB
+                            "st %0,%B1", op, plen, -2);
+      
+      return REGNO (XEXP (base, 0)) == REG_X
+        ? avr_asm_len ("adiw r26,1"  CR_TAB
+                       "st X,%B1"    CR_TAB
+                       "st -X,%A1"   CR_TAB
+                       "adiw r26,2", op, plen, -4)
+
+        : avr_asm_len ("std %p0+1,%B1" CR_TAB
+                       "st %p0,%A1"    CR_TAB
+                       "adiw %r0,2", op, plen, -3);
     }
   fatal_insn ("unknown move insn:",insn);
   return "";
@@ -2936,12 +4028,14 @@ out_movhi_mr_r (rtx insn, rtx op[], int *l)
 
 /* Return 1 if frame pointer for current function required.  */
 
-bool
+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.  */
@@ -2950,39 +4044,43 @@ static RTX_CODE
 compare_condition (rtx insn)
 {
   rtx next = next_real_insn (insn);
-  RTX_CODE cond = UNKNOWN;
-  if (next && GET_CODE (next) == JUMP_INSN)
+
+  if (next && JUMP_P (next))
     {
       rtx pat = PATTERN (next);
       rtx src = SET_SRC (pat);
-      rtx t = XEXP (src, 0);
-      cond = GET_CODE (t);
+      
+      if (IF_THEN_ELSE == GET_CODE (src))
+        return GET_CODE (XEXP (src, 0));
     }
-  return cond;
+  
+  return UNKNOWN;
 }
 
-/* Returns nonzero if INSN is a tst insn that only tests the sign.  */
 
-static int
+/* Returns true iff INSN is a tst insn that only tests the sign.  */
+
+static bool
 compare_sign_p (rtx insn)
 {
   RTX_CODE cond = compare_condition (insn);
   return (cond == GE || cond == LT);
 }
 
-/* Returns nonzero if the next insn is a JUMP_INSN with a condition
+
+/* Returns true iff the next insn is a JUMP_INSN with a condition
    that needs to be swapped (GT, GTU, LE, LEU).  */
 
-int
+static bool
 compare_diff_p (rtx insn)
 {
   RTX_CODE cond = compare_condition (insn);
   return (cond == GT || cond == GTU || cond == LE || cond == LEU) ? cond : 0;
 }
 
-/* Returns nonzero if INSN is a compare insn with the EQ or NE condition.  */
+/* Returns true iff INSN is a compare insn with the EQ or NE condition.  */
 
-int
+static bool
 compare_eq_p (rtx insn)
 {
   RTX_CODE cond = compare_condition (insn);
@@ -2990,200 +4088,370 @@ compare_eq_p (rtx insn)
 }
 
 
+/* Output compare instruction
+
+      compare (XOP[0], XOP[1])
+
+   for an HI/SI register XOP[0] and an integer XOP[1].  Return "".
+   XOP[2] is an 8-bit scratch register as needed.
+
+   PLEN == NULL:  Output instructions.
+   PLEN != NULL:  Set *PLEN to the length (in words) of the sequence.
+                  Don't output anything.  */
+
+const char*
+avr_out_compare (rtx insn, rtx *xop, int *plen)
+{
+  /* Register to compare and value to compare against. */
+  rtx xreg = xop[0];
+  rtx xval = xop[1];
+  
+  /* MODE of the comparison.  */
+  enum machine_mode mode = GET_MODE (xreg);
+
+  /* Number of bytes to operate on.  */
+  int i, n_bytes = GET_MODE_SIZE (mode);
+
+  /* Value (0..0xff) held in clobber register xop[2] or -1 if unknown.  */
+  int clobber_val = -1;
+
+  gcc_assert (REG_P (xreg));
+  gcc_assert ((CONST_INT_P (xval) && n_bytes <= 4)
+              || (const_double_operand (xval, VOIDmode) && n_bytes == 8));
+  
+  if (plen)
+    *plen = 0;
+
+  /* Comparisons == +/-1 and != +/-1 can be done similar to camparing
+     against 0 by ORing the bytes.  This is one instruction shorter.
+     Notice that DImode comparisons are always against reg:DI 18
+     and therefore don't use this.  */
+
+  if (!test_hard_reg_class (LD_REGS, xreg)
+      && compare_eq_p (insn)
+      && reg_unused_after (insn, xreg))
+    {
+      if (xval == const1_rtx)
+        {
+          avr_asm_len ("dec %A0" CR_TAB
+                       "or %A0,%B0", xop, plen, 2);
+          
+          if (n_bytes >= 3)
+            avr_asm_len ("or %A0,%C0", xop, plen, 1);
+
+          if (n_bytes >= 4)
+            avr_asm_len ("or %A0,%D0", xop, plen, 1);
+
+          return "";
+        }
+      else if (xval == constm1_rtx)
+        {
+          if (n_bytes >= 4)
+            avr_asm_len ("and %A0,%D0", xop, plen, 1);
+          
+          if (n_bytes >= 3)
+            avr_asm_len ("and %A0,%C0", xop, plen, 1);
+          
+          return avr_asm_len ("and %A0,%B0" CR_TAB
+                              "com %A0", xop, plen, 2);
+        }
+    }
+
+  for (i = 0; i < n_bytes; i++)
+    {
+      /* We compare byte-wise.  */
+      rtx reg8 = simplify_gen_subreg (QImode, xreg, mode, i);
+      rtx xval8 = simplify_gen_subreg (QImode, xval, mode, i);
+
+      /* 8-bit value to compare with this byte.  */
+      unsigned int val8 = UINTVAL (xval8) & GET_MODE_MASK (QImode);
+
+      /* Registers R16..R31 can operate with immediate.  */
+      bool ld_reg_p = test_hard_reg_class (LD_REGS, reg8);
+
+      xop[0] = reg8;
+      xop[1] = gen_int_mode (val8, QImode);
+
+      /* Word registers >= R24 can use SBIW/ADIW with 0..63.  */
+
+      if (i == 0
+          && test_hard_reg_class (ADDW_REGS, reg8))
+        {
+          int val16 = trunc_int_for_mode (INTVAL (xval), HImode);
+          
+          if (IN_RANGE (val16, 0, 63)
+              && (val8 == 0
+                  || reg_unused_after (insn, xreg)))
+            {
+              avr_asm_len ("sbiw %0,%1", xop, plen, 1);
+              i++;
+              continue;
+            }
+
+          if (n_bytes == 2
+              && IN_RANGE (val16, -63, -1)
+              && compare_eq_p (insn)
+              && reg_unused_after (insn, xreg))
+            {
+              return avr_asm_len ("adiw %0,%n1", xop, plen, 1);
+            }
+        }
+
+      /* Comparing against 0 is easy.  */
+      
+      if (val8 == 0)
+        {
+          avr_asm_len (i == 0
+                       ? "cp %0,__zero_reg__"
+                       : "cpc %0,__zero_reg__", xop, plen, 1);
+          continue;
+        }
+
+      /* Upper registers can compare and subtract-with-carry immediates.
+         Notice that compare instructions do the same as respective subtract
+         instruction; the only difference is that comparisons don't write
+         the result back to the target register.  */
+
+      if (ld_reg_p)
+        {
+          if (i == 0)
+            {
+              avr_asm_len ("cpi %0,%1", xop, plen, 1);
+              continue;
+            }
+          else if (reg_unused_after (insn, xreg))
+            {
+              avr_asm_len ("sbci %0,%1", xop, plen, 1);
+              continue;
+            }
+        }
+
+      /* Must load the value into the scratch register.  */
+
+      gcc_assert (REG_P (xop[2]));
+              
+      if (clobber_val != (int) val8)
+        avr_asm_len ("ldi %2,%1", xop, plen, 1);
+      clobber_val = (int) val8;
+              
+      avr_asm_len (i == 0
+                   ? "cp %0,%2"
+                   : "cpc %0,%2", xop, plen, 1);
+    }
+
+  return "";
+}
+
+
+/* Prepare operands of compare_const_di2 to be used with avr_out_compare.  */
+
+const char*
+avr_out_compare64 (rtx insn, rtx *op, int *plen)
+{
+  rtx xop[3];
+
+  xop[0] = gen_rtx_REG (DImode, 18);
+  xop[1] = op[0];
+  xop[2] = op[1];
+
+  return avr_out_compare (insn, xop, plen);
+}
+
 /* Output test instruction for HImode.  */
 
-const char *
-out_tsthi (rtx insn, rtx op, int *l)
+const char*
+avr_out_tsthi (rtx insn, rtx *op, int *plen)
+{
+  if (compare_sign_p (insn))
+    {
+      avr_asm_len ("tst %B0", op, plen, -1);
+    }
+  else if (reg_unused_after (insn, op[0])
+           && compare_eq_p (insn))
+    {
+      /* Faster than sbiw if we can clobber the operand.  */
+      avr_asm_len ("or %A0,%B0", op, plen, -1);
+    }
+  else
+    {
+      avr_out_compare (insn, op, plen);
+    }
+
+  return "";
+}
+
+
+/* Output test instruction for PSImode.  */
+
+const char*
+avr_out_tstpsi (rtx insn, rtx *op, int *plen)
 {
   if (compare_sign_p (insn))
     {
-      if (l) *l = 1;
-      return AS1 (tst,%B0);
+      avr_asm_len ("tst %C0", op, plen, -1);
     }
-  if (reg_unused_after (insn, op)
-      && compare_eq_p (insn))
+  else if (reg_unused_after (insn, op[0])
+           && compare_eq_p (insn))
     {
       /* Faster than sbiw if we can clobber the operand.  */
-      if (l) *l = 1;
-      return "or %A0,%B0";
+      avr_asm_len ("or %A0,%B0" CR_TAB
+                   "or %A0,%C0", op, plen, -2);
     }
-  if (test_hard_reg_class (ADDW_REGS, op))
+  else
     {
-      if (l) *l = 1;
-      return AS2 (sbiw,%0,0);
+      avr_out_compare (insn, op, plen);
     }
-  if (l) *l = 2;
-  return (AS2 (cp,%A0,__zero_reg__) CR_TAB
-          AS2 (cpc,%B0,__zero_reg__));
+
+  return "";
 }
 
 
 /* Output test instruction for SImode.  */
 
-const char *
-out_tstsi (rtx insn, rtx op, int *l)
+const char*
+avr_out_tstsi (rtx insn, rtx *op, int *plen)
 {
   if (compare_sign_p (insn))
     {
-      if (l) *l = 1;
-      return AS1 (tst,%D0);
+      avr_asm_len ("tst %D0", op, plen, -1);
+    }
+  else if (reg_unused_after (insn, op[0])
+           && compare_eq_p (insn))
+    {
+      /* Faster than sbiw if we can clobber the operand.  */
+      avr_asm_len ("or %A0,%B0" CR_TAB
+                   "or %A0,%C0" CR_TAB
+                   "or %A0,%D0", op, plen, -3);
     }
-  if (test_hard_reg_class (ADDW_REGS, op))
+  else
     {
-      if (l) *l = 3;
-      return (AS2 (sbiw,%A0,0) CR_TAB
-              AS2 (cpc,%C0,__zero_reg__) CR_TAB
-              AS2 (cpc,%D0,__zero_reg__));
+      avr_out_compare (insn, op, plen);
     }
-  if (l) *l = 4;
-  return (AS2 (cp,%A0,__zero_reg__) CR_TAB
-          AS2 (cpc,%B0,__zero_reg__) CR_TAB
-          AS2 (cpc,%C0,__zero_reg__) CR_TAB
-          AS2 (cpc,%D0,__zero_reg__));
+
+  return "";
 }
 
 
-/* Generate asm equivalent for various shifts.
-   Shift count is a CONST_INT, MEM or REG.
-   This only handles cases that are not already
-   carefully hand-optimized in ?sh??i3_out.  */
+/* Generate asm equivalent for various shifts.  This only handles cases
+   that are not already carefully hand-optimized in ?sh??i3_out.
+
+   OPERANDS[0] resp. %0 in TEMPL is the operand to be shifted.
+   OPERANDS[2] is the shift count as CONST_INT, MEM or REG.
+   OPERANDS[3] is a QImode scratch register from LD regs if
+               available and SCRATCH, otherwise (no scratch available)
+
+   TEMPL is an assembler template that shifts by one position.
+   T_LEN is the length of this template.  */
 
 void
 out_shift_with_cnt (const char *templ, rtx insn, rtx operands[],
-                   int *len, int t_len)
+                   int *plen, int t_len)
 {
-  rtx op[10];
-  char str[500];
-  int second_label = 1;
-  int saved_in_tmp = 0;
-  int use_zero_reg = 0;
+  bool second_label = true;
+  bool saved_in_tmp = false;
+  bool use_zero_reg = false;
+  rtx op[5];
 
   op[0] = operands[0];
   op[1] = operands[1];
   op[2] = operands[2];
   op[3] = operands[3];
-  str[0] = 0;
 
-  if (len)
-    *len = 1;
+  if (plen)
+    *plen = 0;
 
-  if (GET_CODE (operands[2]) == CONST_INT)
+  if (CONST_INT_P (operands[2]))
     {
-      int scratch = (GET_CODE (PATTERN (insn)) == PARALLEL);
+      bool scratch = (GET_CODE (PATTERN (insn)) == PARALLEL
+                      && REG_P (operands[3]));
       int count = INTVAL (operands[2]);
       int max_len = 10;  /* If larger than this, always use a loop.  */
 
       if (count <= 0)
-       {
-         if (len)
-           *len = 0;
-         return;
-       }
+          return;
 
       if (count < 8 && !scratch)
-       use_zero_reg = 1;
+        use_zero_reg = true;
 
       if (optimize_size)
-       max_len = t_len + (scratch ? 3 : (use_zero_reg ? 4 : 5));
+        max_len = t_len + (scratch ? 3 : (use_zero_reg ? 4 : 5));
 
       if (t_len * count <= max_len)
-       {
-         /* Output shifts inline with no loop - faster.  */
-         if (len)
-           *len = t_len * count;
-         else
-           {
-             while (count-- > 0)
-               output_asm_insn (templ, op);
-           }
+        {
+          /* Output shifts inline with no loop - faster.  */
+          
+          while (count-- > 0)
+            avr_asm_len (templ, op, plen, t_len);
 
-         return;
-       }
+          return;
+        }
 
       if (scratch)
-       {
-         if (!len)
-           strcat (str, AS2 (ldi,%3,%2));
-       }
+        {
+          avr_asm_len ("ldi %3,%2", op, plen, 1);
+        }
       else if (use_zero_reg)
-       {
-         /* Hack to save one word: use __zero_reg__ as loop counter.
-            Set one bit, then shift in a loop until it is 0 again.  */
+        {
+          /* Hack to save one word: use __zero_reg__ as loop counter.
+             Set one bit, then shift in a loop until it is 0 again.  */
 
-         op[3] = zero_reg_rtx;
-         if (len)
-           *len = 2;
-         else
-           strcat (str, ("set" CR_TAB
-                         AS2 (bld,%3,%2-1)));
-       }
+          op[3] = zero_reg_rtx;
+
+          avr_asm_len ("set" CR_TAB
+                       "bld %3,%2-1", op, plen, 2);
+        }
       else
-       {
-         /* No scratch register available, use one from LD_REGS (saved in
-            __tmp_reg__) that doesn't overlap with registers to shift.  */
+        {
+          /* No scratch register available, use one from LD_REGS (saved in
+             __tmp_reg__) that doesn't overlap with registers to shift.  */
 
-         op[3] = gen_rtx_REG (QImode,
-                          ((true_regnum (operands[0]) - 1) & 15) + 16);
-         op[4] = tmp_reg_rtx;
-         saved_in_tmp = 1;
+          op[3] = all_regs_rtx[((REGNO (op[0]) - 1) & 15) + 16];
+          op[4] = tmp_reg_rtx;
+          saved_in_tmp = true;
 
-         if (len)
-           *len = 3;  /* Includes "mov %3,%4" after the loop.  */
-         else
-           strcat (str, (AS2 (mov,%4,%3) CR_TAB
-                         AS2 (ldi,%3,%2)));
-       }
+          avr_asm_len ("mov %4,%3" CR_TAB
+                       "ldi %3,%2", op, plen, 2);
+        }
 
-      second_label = 0;
+      second_label = false;
     }
-  else if (GET_CODE (operands[2]) == MEM)
+  else if (MEM_P (op[2]))
     {
-      rtx op_mov[10];
+      rtx op_mov[2];
       
-      op[3] = op_mov[0] = tmp_reg_rtx;
+      op_mov[0] = op[3] = tmp_reg_rtx;
       op_mov[1] = op[2];
 
-      if (len)
-       out_movqi_r_mr (insn, op_mov, len);
-      else
-       output_asm_insn (out_movqi_r_mr (insn, op_mov, NULL), op_mov);
+      out_movqi_r_mr (insn, op_mov, plen);
     }
-  else if (register_operand (operands[2], QImode))
+  else if (register_operand (op[2], QImode))
     {
-      if (reg_unused_after (insn, operands[2]))
-       op[3] = op[2];
-      else
-       {
-         op[3] = tmp_reg_rtx;
-         if (!len)
-           strcat (str, (AS2 (mov,%3,%2) CR_TAB));
-       }
+      op[3] = op[2];
+      
+      if (!reg_unused_after (insn, op[2])
+          || reg_overlap_mentioned_p (op[0], op[2]))
+        {
+          op[3] = tmp_reg_rtx;
+          avr_asm_len ("mov %3,%2", op, plen, 1);
+        }
     }
   else
     fatal_insn ("bad shift insn:", insn);
 
   if (second_label)
-    {
-      if (len)
-       ++*len;
-      else
-       strcat (str, AS1 (rjmp,2f));
-    }
+      avr_asm_len ("rjmp 2f", op, plen, 1);
 
-  if (len)
-    *len += t_len + 2;  /* template + dec + brXX */
-  else
-    {
-      strcat (str, "\n1:\t");
-      strcat (str, templ);
-      strcat (str, second_label ? "\n2:\t" : "\n\t");
-      strcat (str, use_zero_reg ? AS1 (lsr,%3) : AS1 (dec,%3));
-      strcat (str, CR_TAB);
-      strcat (str, second_label ? AS1 (brpl,1b) : AS1 (brne,1b));
-      if (saved_in_tmp)
-       strcat (str, (CR_TAB AS2 (mov,%3,%4)));
-      output_asm_insn (str, op);
-    }
+  avr_asm_len ("1:", op, plen, 0);
+  avr_asm_len (templ, op, plen, t_len);
+
+  if (second_label)
+    avr_asm_len ("2:", op, plen, 0);
+
+  avr_asm_len (use_zero_reg ? "lsr %3" : "dec %3", op, plen, 1);
+  avr_asm_len (second_label ? "brpl 1b" : "brne 1b", op, plen, 1);
+
+  if (saved_in_tmp)
+    avr_asm_len ("mov %3,%4", op, plen, 1);
 }
 
 
@@ -3542,6 +4810,69 @@ ashlhi3_out (rtx insn, rtx operands[], int *len)
 }
 
 
+/* 24-bit shift left */
+
+const char*
+avr_out_ashlpsi3 (rtx insn, rtx *op, int *plen)
+{
+  if (plen)
+    *plen = 0;
+  
+  if (CONST_INT_P (op[2]))
+    {
+      switch (INTVAL (op[2]))
+        {
+        default:
+          if (INTVAL (op[2]) < 24)
+            break;
+
+          return avr_asm_len ("clr %A0" CR_TAB
+                              "clr %B0" CR_TAB
+                              "clr %C0", op, plen, 3);
+
+        case 8:
+          {
+            int reg0 = REGNO (op[0]);
+            int reg1 = REGNO (op[1]);
+            
+            if (reg0 >= reg1)
+              return avr_asm_len ("mov %C0,%B1"  CR_TAB
+                                  "mov %B0,%A1"  CR_TAB
+                                  "clr %A0", op, plen, 3);
+            else
+              return avr_asm_len ("clr %A0"      CR_TAB
+                                  "mov %B0,%A1"  CR_TAB
+                                  "mov %C0,%B1", op, plen, 3);
+          }
+
+        case 16:
+          {
+            int reg0 = REGNO (op[0]);
+            int reg1 = REGNO (op[1]);
+
+            if (reg0 + 2 != reg1)
+              avr_asm_len ("mov %C0,%A0", op, plen, 1);
+            
+            return avr_asm_len ("clr %B0"  CR_TAB
+                                "clr %A0", op, plen, 2);
+          }
+
+        case 23:
+          return avr_asm_len ("clr %C0" CR_TAB
+                              "lsr %A0" CR_TAB
+                              "ror %C0" CR_TAB
+                              "clr %B0" CR_TAB
+                              "clr %A0", op, plen, 5);
+        }
+    }
+  
+  out_shift_with_cnt ("lsl %A0" CR_TAB
+                      "rol %B0" CR_TAB
+                      "rol %C0", insn, op, plen, 3);
+  return "";
+}
+
+
 /* 32bit shift left ((long)x << i)   */
 
 const char *
@@ -3868,6 +5199,65 @@ ashrhi3_out (rtx insn, rtx operands[], int *len)
 }
 
 
+/* 24-bit arithmetic shift right */
+
+const char*
+avr_out_ashrpsi3 (rtx insn, rtx *op, int *plen)
+{
+  int dest = REGNO (op[0]);
+  int src = REGNO (op[1]);
+
+  if (CONST_INT_P (op[2]))
+    {
+      if (plen)
+        *plen = 0;
+      
+      switch (INTVAL (op[2]))
+        {
+        case 8:
+          if (dest <= src)
+            return avr_asm_len ("mov %A0,%B1" CR_TAB
+                                "mov %B0,%C1" CR_TAB
+                                "clr %C0"     CR_TAB
+                                "sbrc %B0,7"  CR_TAB
+                                "dec %C0", op, plen, 5);
+          else
+            return avr_asm_len ("clr %C0"     CR_TAB
+                                "sbrc %C1,7"  CR_TAB
+                                "dec %C0"     CR_TAB
+                                "mov %B0,%C1" CR_TAB
+                                "mov %A0,%B1", op, plen, 5);
+          
+        case 16:
+          if (dest != src + 2)
+            avr_asm_len ("mov %A0,%C1", op, plen, 1);
+          
+          return avr_asm_len ("clr %B0"     CR_TAB
+                              "sbrc %A0,7"  CR_TAB
+                              "com %B0"     CR_TAB
+                              "mov %C0,%B0", op, plen, 4);
+
+        default:
+          if (INTVAL (op[2]) < 24)
+            break;
+
+          /* fall through */
+
+        case 23:
+          return avr_asm_len ("lsl %C0"     CR_TAB
+                              "sbc %A0,%A0" CR_TAB
+                              "mov %B0,%A0" CR_TAB
+                              "mov %C0,%A0", op, plen, 4);
+        } /* switch */
+    }
+  
+  out_shift_with_cnt ("asr %C0" CR_TAB
+                      "ror %B0" CR_TAB
+                      "ror %A0", insn, op, plen, 3);
+  return "";
+}
+
+
 /* 32bit arithmetic shift right  ((signed long)x >> i) */
 
 const char *
@@ -4318,6 +5708,61 @@ lshrhi3_out (rtx insn, rtx operands[], int *len)
   return "";
 }
 
+
+/* 24-bit logic shift right */
+
+const char*
+avr_out_lshrpsi3 (rtx insn, rtx *op, int *plen)
+{
+  int dest = REGNO (op[0]);
+  int src = REGNO (op[1]);
+
+  if (CONST_INT_P (op[2]))
+    {
+      if (plen)
+        *plen = 0;
+      
+      switch (INTVAL (op[2]))
+        {
+        case 8:
+          if (dest <= src)
+            return avr_asm_len ("mov %A0,%B1" CR_TAB
+                                "mov %B0,%C1" CR_TAB
+                                "clr %C0", op, plen, 3);
+          else
+            return avr_asm_len ("clr %C0"     CR_TAB
+                                "mov %B0,%C1" CR_TAB
+                                "mov %A0,%B1", op, plen, 3);
+          
+        case 16:
+          if (dest != src + 2)
+            avr_asm_len ("mov %A0,%C1", op, plen, 1);
+          
+          return avr_asm_len ("clr %B0"  CR_TAB
+                              "clr %C0", op, plen, 2);
+
+        default:
+          if (INTVAL (op[2]) < 24)
+            break;
+
+          /* fall through */
+
+        case 23:
+          return avr_asm_len ("clr %A0"    CR_TAB
+                              "sbrc %C0,7" CR_TAB
+                              "inc %A0"    CR_TAB
+                              "clr %B0"    CR_TAB
+                              "clr %C0", op, plen, 5);
+        } /* switch */
+    }
+  
+  out_shift_with_cnt ("lsr %C0" CR_TAB
+                      "ror %B0" CR_TAB
+                      "ror %A0", insn, op, plen, 3);
+  return "";
+}
+
+
 /* 32bit logic shift right ((unsigned int)x >> i) */
 
 const char *
@@ -4389,25 +5834,453 @@ lshrsi3_out (rtx insn, rtx operands[], int *len)
                            AS1 (clr,%C0)     CR_TAB
                            AS1 (clr,%D0));
 
-       case 31:
-         *len = 6;
-         return (AS1 (clr,%A0)    CR_TAB
-                 AS2 (sbrc,%D0,7) CR_TAB
-                 AS1 (inc,%A0)    CR_TAB
-                 AS1 (clr,%B0)    CR_TAB
-                 AS1 (clr,%C0)    CR_TAB
-                 AS1 (clr,%D0));
-       }
-      len = t;
+       case 31:
+         *len = 6;
+         return (AS1 (clr,%A0)    CR_TAB
+                 AS2 (sbrc,%D0,7) CR_TAB
+                 AS1 (inc,%A0)    CR_TAB
+                 AS1 (clr,%B0)    CR_TAB
+                 AS1 (clr,%C0)    CR_TAB
+                 AS1 (clr,%D0));
+       }
+      len = t;
+    }
+  out_shift_with_cnt ((AS1 (lsr,%D0) CR_TAB
+                      AS1 (ror,%C0) CR_TAB
+                      AS1 (ror,%B0) CR_TAB
+                      AS1 (ror,%A0)),
+                     insn, operands, len, 4);
+  return "";
+}
+
+
+/* Output addition of register XOP[0] and compile time constant XOP[2]:
+
+      XOP[0] = XOP[0] + XOP[2]
+
+   and return "".  If PLEN == NULL, print assembler instructions to perform the
+   addition; otherwise, set *PLEN to the length of the instruction sequence (in
+   words) printed with PLEN == NULL.  XOP[3] is an 8-bit scratch register.
+   CODE == PLUS:  perform addition by using ADD instructions.
+   CODE == MINUS: perform addition by using SUB instructions.
+   Set *PCC to effect on cc0 according to respective CC_* insn attribute.  */
+
+static void
+avr_out_plus_1 (rtx *xop, int *plen, enum rtx_code code, int *pcc)
+{
+  /* MODE of the operation.  */
+  enum machine_mode mode = GET_MODE (xop[0]);
+
+  /* Number of bytes to operate on.  */
+  int i, n_bytes = GET_MODE_SIZE (mode);
+
+  /* Value (0..0xff) held in clobber register op[3] or -1 if unknown.  */
+  int clobber_val = -1;
+
+  /* op[0]: 8-bit destination register
+     op[1]: 8-bit const int
+     op[2]: 8-bit scratch register */
+  rtx op[3];
+
+  /* Started the operation?  Before starting the operation we may skip
+     adding 0.  This is no more true after the operation started because
+     carry must be taken into account.  */
+  bool started = false;
+
+  /* Value to add.  There are two ways to add VAL: R += VAL and R -= -VAL.  */
+  rtx xval = xop[2];
+
+  /* Except in the case of ADIW with 16-bit register (see below)
+     addition does not set cc0 in a usable way.  */
+  
+  *pcc = (MINUS == code) ? CC_SET_CZN : CC_CLOBBER;
+
+  if (MINUS == code)
+    xval = simplify_unary_operation (NEG, mode, xval, mode);
+
+  op[2] = xop[3];
+
+  if (plen)
+    *plen = 0;
+
+  for (i = 0; i < n_bytes; i++)
+    {
+      /* We operate byte-wise on the destination.  */
+      rtx reg8 = simplify_gen_subreg (QImode, xop[0], mode, i);
+      rtx xval8 = simplify_gen_subreg (QImode, xval, mode, i);
+
+      /* 8-bit value to operate with this byte. */
+      unsigned int val8 = UINTVAL (xval8) & GET_MODE_MASK (QImode);
+
+      /* Registers R16..R31 can operate with immediate.  */
+      bool ld_reg_p = test_hard_reg_class (LD_REGS, reg8);
+
+      op[0] = reg8;
+      op[1] = gen_int_mode (val8, QImode);
+
+      /* To get usable cc0 no low-bytes must have been skipped.  */
+      
+      if (i && !started)
+        *pcc = CC_CLOBBER;
+      
+      if (!started
+          && i % 2 == 0
+          && i + 2 <= n_bytes
+          && test_hard_reg_class (ADDW_REGS, reg8))
+        {
+          rtx xval16 = simplify_gen_subreg (HImode, xval, mode, i);
+          unsigned int val16 = UINTVAL (xval16) & GET_MODE_MASK (HImode);
+
+          /* Registers R24, X, Y, Z can use ADIW/SBIW with constants < 64
+             i.e. operate word-wise.  */
+
+          if (val16 < 64)
+            {
+              if (val16 != 0)
+                {
+                  started = true;
+                  avr_asm_len (code == PLUS ? "adiw %0,%1" : "sbiw %0,%1",
+                               op, plen, 1);
+
+                  if (n_bytes == 2 && PLUS == code)
+                      *pcc = CC_SET_ZN;
+                }
+
+              i++;
+              continue;
+            }
+        }
+
+      if (val8 == 0)
+        {
+          if (started)
+            avr_asm_len (code == PLUS
+                         ? "adc %0,__zero_reg__" : "sbc %0,__zero_reg__",
+                         op, plen, 1);
+          continue;
+        }
+      else if ((val8 == 1 || val8 == 0xff)
+               && !started
+               && i == n_bytes - 1)
+        {
+          avr_asm_len ((code == PLUS) ^ (val8 == 1) ? "dec %0" : "inc %0",
+                       op, plen, 1);
+          break;
+        }
+
+      switch (code)
+        {
+        case PLUS:
+
+          gcc_assert (plen != NULL || REG_P (op[2]));
+
+          if (clobber_val != (int) val8)
+            avr_asm_len ("ldi %2,%1", op, plen, 1);
+          clobber_val = (int) val8;
+              
+          avr_asm_len (started ? "adc %0,%2" : "add %0,%2", op, plen, 1);
+
+          break; /* PLUS */
+
+        case MINUS:
+
+          if (ld_reg_p)
+            avr_asm_len (started ? "sbci %0,%1" : "subi %0,%1", op, plen, 1);
+          else
+            {
+              gcc_assert (plen != NULL || REG_P (op[2]));
+
+              if (clobber_val != (int) val8)
+                avr_asm_len ("ldi %2,%1", op, plen, 1);
+              clobber_val = (int) val8;
+              
+              avr_asm_len (started ? "sbc %0,%2" : "sub %0,%2", op, plen, 1);
+            }
+
+          break; /* MINUS */
+          
+        default:
+          /* Unknown code */
+          gcc_unreachable();
+        }
+
+      started = true;
+
+    } /* for all sub-bytes */
+
+  /* No output doesn't change cc0.  */
+  
+  if (plen && *plen == 0)
+    *pcc = CC_NONE;
+}
+
+
+/* Output addition of register XOP[0] and compile time constant XOP[2]:
+
+      XOP[0] = XOP[0] + XOP[2]
+
+   and return "".  If PLEN == NULL, print assembler instructions to perform the
+   addition; otherwise, set *PLEN to the length of the instruction sequence (in
+   words) printed with PLEN == NULL.
+   If PCC != 0 then set *PCC to the the instruction sequence's effect on the
+   condition code (with respect to XOP[0]).  */
+
+const char*
+avr_out_plus (rtx *xop, int *plen, int *pcc)
+{
+  int len_plus, len_minus;
+  int cc_plus, cc_minus, cc_dummy;
+
+  if (!pcc)
+    pcc = &cc_dummy;
+                                   
+  /* Work out if  XOP[0] += XOP[2]  is better or  XOP[0] -= -XOP[2].  */
+  
+  avr_out_plus_1 (xop, &len_plus, PLUS, &cc_plus);
+  avr_out_plus_1 (xop, &len_minus, MINUS, &cc_minus);
+
+  /* Prefer MINUS over PLUS if size is equal because it sets cc0.  */
+  
+  if (plen)
+    {
+      *plen = (len_minus <= len_plus) ? len_minus : len_plus;
+      *pcc  = (len_minus <= len_plus) ? cc_minus : cc_plus;
+    }
+  else if (len_minus <= len_plus)
+    avr_out_plus_1 (xop, NULL, MINUS, pcc);
+  else
+    avr_out_plus_1 (xop, NULL, PLUS, pcc);
+
+  return "";
+}
+
+
+/* Same as above but XOP has just 3 entries.
+   Supply a dummy 4th operand.  */
+
+const char*
+avr_out_plus_noclobber (rtx *xop, int *plen, int *pcc)
+{
+  rtx op[4];
+
+  op[0] = xop[0];
+  op[1] = xop[1];
+  op[2] = xop[2];
+  op[3] = NULL_RTX;
+
+  return avr_out_plus (op, plen, pcc);
+}
+
+
+/* Prepare operands of adddi3_const_insn to be used with avr_out_plus_1.  */
+
+const char*
+avr_out_plus64 (rtx addend, int *plen)
+{
+  int cc_dummy;
+  rtx op[4];
+
+  op[0] = gen_rtx_REG (DImode, 18);
+  op[1] = op[0];
+  op[2] = addend;
+  op[3] = NULL_RTX;
+
+  avr_out_plus_1 (op, plen, MINUS, &cc_dummy);
+
+  return "";
+}
+
+/* Output bit operation (IOR, AND, XOR) with register XOP[0] and compile
+   time constant XOP[2]:
+
+      XOP[0] = XOP[0] <op> XOP[2]
+
+   and return "".  If PLEN == NULL, print assembler instructions to perform the
+   operation; otherwise, set *PLEN to the length of the instruction sequence
+   (in words) printed with PLEN == NULL.  XOP[3] is either an 8-bit clobber
+   register or SCRATCH if no clobber register is needed for the operation.  */
+
+const char*
+avr_out_bitop (rtx insn, rtx *xop, int *plen)
+{
+  /* CODE and MODE of the operation.  */
+  enum rtx_code code = GET_CODE (SET_SRC (single_set (insn)));
+  enum machine_mode mode = GET_MODE (xop[0]);
+
+  /* Number of bytes to operate on.  */
+  int i, n_bytes = GET_MODE_SIZE (mode);
+
+  /* Value of T-flag (0 or 1) or -1 if unknow.  */
+  int set_t = -1;
+
+  /* Value (0..0xff) held in clobber register op[3] or -1 if unknown.  */
+  int clobber_val = -1;
+
+  /* op[0]: 8-bit destination register
+     op[1]: 8-bit const int
+     op[2]: 8-bit clobber register or SCRATCH
+     op[3]: 8-bit register containing 0xff or NULL_RTX  */
+  rtx op[4];
+
+  op[2] = xop[3];
+  op[3] = NULL_RTX;
+
+  if (plen)
+    *plen = 0;
+
+  for (i = 0; i < n_bytes; i++)
+    {
+      /* We operate byte-wise on the destination.  */
+      rtx reg8 = simplify_gen_subreg (QImode, xop[0], mode, i);
+      rtx xval8 = simplify_gen_subreg (QImode, xop[2], mode, i);
+
+      /* 8-bit value to operate with this byte. */
+      unsigned int val8 = UINTVAL (xval8) & GET_MODE_MASK (QImode);
+
+      /* Number of bits set in the current byte of the constant.  */
+      int pop8 = avr_popcount (val8);
+
+      /* Registers R16..R31 can operate with immediate.  */
+      bool ld_reg_p = test_hard_reg_class (LD_REGS, reg8);
+
+      op[0] = reg8;
+      op[1] = GEN_INT (val8);
+    
+      switch (code)
+        {
+        case IOR:
+
+          if (0 == pop8)
+            continue;
+          else if (ld_reg_p)
+            avr_asm_len ("ori %0,%1", op, plen, 1);
+          else if (1 == pop8)
+            {
+              if (set_t != 1)
+                avr_asm_len ("set", op, plen, 1);
+              set_t = 1;
+              
+              op[1] = GEN_INT (exact_log2 (val8));
+              avr_asm_len ("bld %0,%1", op, plen, 1);
+            }
+          else if (8 == pop8)
+            {
+              if (op[3] != NULL_RTX)
+                avr_asm_len ("mov %0,%3", op, plen, 1);
+              else
+                avr_asm_len ("clr %0" CR_TAB
+                             "dec %0", op, plen, 2);
+
+              op[3] = op[0];
+            }
+          else
+            {
+              if (clobber_val != (int) val8)
+                avr_asm_len ("ldi %2,%1", op, plen, 1);
+              clobber_val = (int) val8;
+              
+              avr_asm_len ("or %0,%2", op, plen, 1);
+            }
+
+          continue; /* IOR */
+
+        case AND:
+
+          if (8 == pop8)
+            continue;
+          else if (0 == pop8)
+            avr_asm_len ("clr %0", op, plen, 1);
+          else if (ld_reg_p)
+            avr_asm_len ("andi %0,%1", op, plen, 1);
+          else if (7 == pop8)
+            {
+              if (set_t != 0)
+                avr_asm_len ("clt", op, plen, 1);
+              set_t = 0;
+              
+              op[1] = GEN_INT (exact_log2 (GET_MODE_MASK (QImode) & ~val8));
+              avr_asm_len ("bld %0,%1", op, plen, 1);
+            }
+          else
+            {
+              if (clobber_val != (int) val8)
+                avr_asm_len ("ldi %2,%1", op, plen, 1);
+              clobber_val = (int) val8;
+              
+              avr_asm_len ("and %0,%2", op, plen, 1);
+            }
+
+          continue; /* AND */
+          
+        case XOR:
+
+          if (0 == pop8)
+            continue;
+          else if (8 == pop8)
+            avr_asm_len ("com %0", op, plen, 1);
+          else if (ld_reg_p && val8 == (1 << 7))
+            avr_asm_len ("subi %0,%1", op, plen, 1);
+          else
+            {
+              if (clobber_val != (int) val8)
+                avr_asm_len ("ldi %2,%1", op, plen, 1);
+              clobber_val = (int) val8;
+              
+              avr_asm_len ("eor %0,%2", op, plen, 1);
+            }
+
+          continue; /* XOR */
+          
+        default:
+          /* Unknown rtx_code */
+          gcc_unreachable();
+        }
+    } /* for all sub-bytes */
+
+  return "";
+}
+
+
+/* PLEN == NULL: Output code to add CONST_INT OP[0] to SP.
+   PLEN != NULL: Set *PLEN to the length of that sequence.
+   Return "".  */
+
+const char*
+avr_out_addto_sp (rtx *op, int *plen)
+{
+  int pc_len = AVR_2_BYTE_PC ? 2 : 3;
+  int addend = INTVAL (op[0]);
+
+  if (plen)
+    *plen = 0;
+
+  if (addend < 0)
+    {
+      if (flag_verbose_asm || flag_print_asm_name)
+        avr_asm_len (ASM_COMMENT_START "SP -= %n0", op, plen, 0);
+  
+      while (addend <= -pc_len)
+        {
+          addend += pc_len;
+          avr_asm_len ("rcall .", op, plen, 1);
+        }
+
+      while (addend++ < 0)
+        avr_asm_len ("push __zero_reg__", op, plen, 1);
     }
-  out_shift_with_cnt ((AS1 (lsr,%D0) CR_TAB
-                      AS1 (ror,%C0) CR_TAB
-                      AS1 (ror,%B0) CR_TAB
-                      AS1 (ror,%A0)),
-                     insn, operands, len, 4);
+  else if (addend > 0)
+    {
+      if (flag_verbose_asm || flag_print_asm_name)
+        avr_asm_len (ASM_COMMENT_START "SP += %0", op, plen, 0);
+
+      while (addend-- > 0)
+        avr_asm_len ("pop __tmp_reg__", op, plen, 1);
+    }
+
   return "";
 }
 
+
 /* Create RTL split patterns for byte sized rotate expressions.  This
   produces a series of move instructions and considers overlap situations.
   Overlapping non-HImode operands need a scratch register.  */
@@ -4438,7 +6311,9 @@ avr_rotate_bytes (rtx operands[])
     if (mode == DImode)
       move_mode = QImode;
     /* Make scratch smaller if needed.  */
-    if (GET_MODE (scratch) == HImode && move_mode == QImode)
+    if (SCRATCH != GET_CODE (scratch)
+        && HImode == GET_MODE (scratch)
+        && QImode == move_mode)
       scratch = simplify_gen_subreg (move_mode, scratch, HImode, 0); 
 
     move_size = GET_MODE_SIZE (move_mode);
@@ -4534,6 +6409,8 @@ avr_rotate_bytes (rtx operands[])
                   When this move occurs, it will break chain deadlock.
                   The scratch register is substituted for real move.  */
 
+               gcc_assert (SCRATCH != GET_CODE (scratch));
+
                move[size].src = move[blocked].dst;
                move[size].dst =  scratch;
                /* Scratch move is never blocked.  */
@@ -4553,151 +6430,94 @@ avr_rotate_bytes (rtx operands[])
 }
 
 /* Modifies the length assigned to instruction INSN
- LEN is the initially computed length of the insn.  */
  LEN is the initially computed length of the insn.  */
 
 int
 adjust_insn_length (rtx insn, int len)
 {
-  rtx patt = PATTERN (insn);
-  rtx set;
+  rtx *op = recog_data.operand;
+  enum attr_adjust_len adjust_len;
 
-  if (GET_CODE (patt) == SET)
+  /* Some complex insns don't need length adjustment and therefore
+     the length need not/must not be adjusted for these insns.
+     It is easier to state this in an insn attribute "adjust_len" than
+     to clutter up code here...  */
+  
+  if (-1 == recog_memoized (insn))
     {
-      rtx op[10];
-      op[1] = SET_SRC (patt);
-      op[0] = SET_DEST (patt);
-      if (general_operand (op[1], VOIDmode)
-         && general_operand (op[0], VOIDmode))
-       {
-         switch (GET_MODE (op[0]))
-           {
-           case QImode:
-             output_movqi (insn, op, &len);
-             break;
-           case HImode:
-             output_movhi (insn, op, &len);
-             break;
-           case SImode:
-           case SFmode:
-             output_movsisf (insn, op, NULL_RTX, &len);
-             break;
-           default:
-             break;
-           }
-       }
-      else if (op[0] == cc0_rtx && REG_P (op[1]))
-       {
-         switch (GET_MODE (op[1]))
-           {
-           case HImode: out_tsthi (insn, op[1], &len); break;
-           case SImode: out_tstsi (insn, op[1], &len); break;
-           default: break;
-           }
-       }
-      else if (GET_CODE (op[1]) == AND)
-       {
-         if (GET_CODE (XEXP (op[1],1)) == CONST_INT)
-           {
-             HOST_WIDE_INT mask = INTVAL (XEXP (op[1],1));
-             if (GET_MODE (op[1]) == SImode)
-               len = (((mask & 0xff) != 0xff)
-                      + ((mask & 0xff00) != 0xff00)
-                      + ((mask & 0xff0000L) != 0xff0000L)
-                      + ((mask & 0xff000000L) != 0xff000000L));
-             else if (GET_MODE (op[1]) == HImode)
-               len = (((mask & 0xff) != 0xff)
-                      + ((mask & 0xff00) != 0xff00));
-           }
-       }
-      else if (GET_CODE (op[1]) == IOR)
-       {
-         if (GET_CODE (XEXP (op[1],1)) == CONST_INT)
-           {
-             HOST_WIDE_INT mask = INTVAL (XEXP (op[1],1));
-             if (GET_MODE (op[1]) == SImode)
-               len = (((mask & 0xff) != 0)
-                      + ((mask & 0xff00) != 0)
-                      + ((mask & 0xff0000L) != 0)
-                      + ((mask & 0xff000000L) != 0));
-             else if (GET_MODE (op[1]) == HImode)
-               len = (((mask & 0xff) != 0)
-                      + ((mask & 0xff00) != 0));
-           }
-       }
+      return len;
     }
-  set = single_set (insn);
-  if (set)
+
+  /* Read from insn attribute "adjust_len" if/how length is to be adjusted.  */
+
+  adjust_len = get_attr_adjust_len (insn);
+
+  if (adjust_len == ADJUST_LEN_NO)
+    {
+      /* Nothing to adjust: The length from attribute "length" is fine.
+         This is the default.  */
+      
+      return len;
+    }
+  
+  /* Extract insn's operands.  */
+  
+  extract_constrain_insn_cached (insn);
+  
+  /* Dispatch to right function.  */
+  
+  switch (adjust_len)
     {
-      rtx op[10];
+    case ADJUST_LEN_RELOAD_IN16: output_reload_inhi (op, op[2], &len); break;
+    case ADJUST_LEN_RELOAD_IN24: avr_out_reload_inpsi (op, op[2], &len); break;
+    case ADJUST_LEN_RELOAD_IN32: output_reload_insisf (op, op[2], &len); break;
+      
+    case ADJUST_LEN_OUT_BITOP: avr_out_bitop (insn, op, &len); break;
+      
+    case ADJUST_LEN_OUT_PLUS: avr_out_plus (op, &len, NULL); break;
+    case ADJUST_LEN_PLUS64: avr_out_plus64 (op[0], &len); break;
+    case ADJUST_LEN_OUT_PLUS_NOCLOBBER:
+      avr_out_plus_noclobber (op, &len, NULL); break;
 
-      op[1] = SET_SRC (set);
-      op[0] = SET_DEST (set);
+    case ADJUST_LEN_ADDTO_SP: avr_out_addto_sp (op, &len); break;
+      
+    case ADJUST_LEN_MOV8:  output_movqi (insn, op, &len); break;
+    case ADJUST_LEN_MOV16: output_movhi (insn, op, &len); break;
+    case ADJUST_LEN_MOV24: avr_out_movpsi (insn, op, &len); break;
+    case ADJUST_LEN_MOV32: output_movsisf (insn, op, &len); break;
+    case ADJUST_LEN_MOVMEM: avr_out_movmem (insn, op, &len); break;
+    case ADJUST_LEN_XLOAD: avr_out_xload (insn, op, &len); break;
+
+    case ADJUST_LEN_TSTHI: avr_out_tsthi (insn, op, &len); break;
+    case ADJUST_LEN_TSTPSI: avr_out_tstpsi (insn, op, &len); break;
+    case ADJUST_LEN_TSTSI: avr_out_tstsi (insn, op, &len); break;
+    case ADJUST_LEN_COMPARE: avr_out_compare (insn, op, &len); break;
+    case ADJUST_LEN_COMPARE64: avr_out_compare64 (insn, op, &len); break;
+
+    case ADJUST_LEN_LSHRQI: lshrqi3_out (insn, op, &len); break;
+    case ADJUST_LEN_LSHRHI: lshrhi3_out (insn, op, &len); break;
+    case ADJUST_LEN_LSHRSI: lshrsi3_out (insn, op, &len); break;
+
+    case ADJUST_LEN_ASHRQI: ashrqi3_out (insn, op, &len); break;
+    case ADJUST_LEN_ASHRHI: ashrhi3_out (insn, op, &len); break;
+    case ADJUST_LEN_ASHRSI: ashrsi3_out (insn, op, &len); break;
+
+    case ADJUST_LEN_ASHLQI: ashlqi3_out (insn, op, &len); break;
+    case ADJUST_LEN_ASHLHI: ashlhi3_out (insn, op, &len); break;
+    case ADJUST_LEN_ASHLSI: ashlsi3_out (insn, op, &len); break;
+      
+    case ADJUST_LEN_ASHLPSI: avr_out_ashlpsi3 (insn, op, &len); break;
+    case ADJUST_LEN_ASHRPSI: avr_out_ashrpsi3 (insn, op, &len); break;
+    case ADJUST_LEN_LSHRPSI: avr_out_lshrpsi3 (insn, op, &len); break;
 
-      if (GET_CODE (patt) == PARALLEL
-         && general_operand (op[1], VOIDmode)
-         && general_operand (op[0], VOIDmode))
-       {
-         if (XVECLEN (patt, 0) == 2)
-           op[2] = XVECEXP (patt, 0, 1);
+    case ADJUST_LEN_CALL: len = AVR_HAVE_JMP_CALL ? 2 : 1; break;
 
-         switch (GET_MODE (op[0]))
-           {
-           case QImode:
-             len = 2;
-             break;
-           case HImode:
-             output_reload_inhi (insn, op, &len);
-             break;
-           case SImode:
-           case SFmode:
-             output_reload_insisf (insn, op, XEXP (op[2], 0), &len);
-             break;
-           default:
-             break;
-           }
-       }
-      else if (GET_CODE (op[1]) == ASHIFT
-         || GET_CODE (op[1]) == ASHIFTRT
-         || GET_CODE (op[1]) == LSHIFTRT)
-       {
-         rtx ops[10];
-         ops[0] = op[0];
-         ops[1] = XEXP (op[1],0);
-         ops[2] = XEXP (op[1],1);
-         switch (GET_CODE (op[1]))
-           {
-           case ASHIFT:
-             switch (GET_MODE (op[0]))
-               {
-               case QImode: ashlqi3_out (insn,ops,&len); break;
-               case HImode: ashlhi3_out (insn,ops,&len); break;
-               case SImode: ashlsi3_out (insn,ops,&len); break;
-               default: break;
-               }
-             break;
-           case ASHIFTRT:
-             switch (GET_MODE (op[0]))
-               {
-               case QImode: ashrqi3_out (insn,ops,&len); break;
-               case HImode: ashrhi3_out (insn,ops,&len); break;
-               case SImode: ashrsi3_out (insn,ops,&len); break;
-               default: break;
-               }
-             break;
-           case LSHIFTRT:
-             switch (GET_MODE (op[0]))
-               {
-               case QImode: lshrqi3_out (insn,ops,&len); break;
-               case HImode: lshrhi3_out (insn,ops,&len); break;
-               case SImode: lshrsi3_out (insn,ops,&len); break;
-               default: break;
-               }
-             break;
-           default:
-             break;
-           }
-       }
+    case ADJUST_LEN_MAP_BITS: avr_out_map_bits (insn, op, &len); break;
+
+    default:
+      gcc_unreachable();
     }
+  
   return len;
 }
 
@@ -4815,6 +6635,49 @@ _reg_unused_after (rtx insn, rtx reg)
   return 1;
 }
 
+
+/* Return RTX that represents the lower 16 bits of a constant address.
+   Unfortunately, simplify_gen_subreg does not handle this case.  */
+
+static rtx
+avr_const_address_lo16 (rtx x)
+{
+  rtx lo16;
+  
+  switch (GET_CODE (x))
+    {
+    default:
+      break;
+      
+    case CONST:
+      if (PLUS == GET_CODE (XEXP (x, 0))
+          && SYMBOL_REF == GET_CODE (XEXP (XEXP (x, 0), 0))
+          && CONST_INT_P (XEXP (XEXP (x, 0), 1)))
+        {
+          HOST_WIDE_INT offset = INTVAL (XEXP (XEXP (x, 0), 1));
+          const char *name = XSTR (XEXP (XEXP (x, 0), 0), 0);
+          
+          lo16 = gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (name));
+          lo16 = gen_rtx_CONST (Pmode, plus_constant (lo16, offset));
+          
+          return lo16;
+        }
+      
+      break;
+      
+    case SYMBOL_REF:
+      {
+        const char *name = XSTR (x, 0);
+        
+        return gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (name));
+      }
+    }
+  
+  avr_edump ("\n%?: %r\n", x);
+  gcc_unreachable();
+}
+
+
 /* Target hook for assembling integer objects.  The AVR version needs
    special handling for references to certain labels.  */
 
@@ -4827,11 +6690,30 @@ avr_assemble_integer (rtx x, unsigned int size, int aligned_p)
       fputs ("\t.word\tgs(", asm_out_file);
       output_addr_const (asm_out_file, x);
       fputs (")\n", asm_out_file);
+      
+      return true;
+    }
+  else if (GET_MODE (x) == PSImode)
+    {
+      default_assemble_integer (avr_const_address_lo16 (x),
+                                GET_MODE_SIZE (HImode), aligned_p);
+      
+      fputs ("\t.warning\t\"assembling 24-bit address needs binutils extension for hh8(",
+             asm_out_file);
+      output_addr_const (asm_out_file, x);
+      fputs (")\"\n", asm_out_file);
+      
+      fputs ("\t.byte\t0\t" ASM_COMMENT_START " hh8(", asm_out_file);
+      output_addr_const (asm_out_file, x);
+      fputs (")\n", asm_out_file);
+      
       return true;
     }
+  
   return default_assemble_integer (x, size, aligned_p);
 }
 
+
 /* Worker function for ASM_DECLARE_FUNCTION_NAME.  */
 
 void
@@ -4844,7 +6726,7 @@ avr_asm_declare_function_name (FILE *file, const char *name, tree decl)
 
   if (cfun->machine->is_interrupt)
     {
-      if (strncmp (name, "__vector", strlen ("__vector")) != 0)
+      if (!STR_PREFIX_P (name, "__vector"))
         {
           warning_at (DECL_SOURCE_LOCATION (decl), 0,
                       "%qs appears to be a misspelled interrupt handler",
@@ -4853,7 +6735,7 @@ avr_asm_declare_function_name (FILE *file, const char *name, tree decl)
     }
   else if (cfun->machine->is_signal)
     {
-      if (strncmp (name, "__vector", strlen ("__vector")) != 0)
+      if (!STR_PREFIX_P (name, "__vector"))
         {
            warning_at (DECL_SOURCE_LOCATION (decl), 0,
                        "%qs appears to be a misspelled signal handler",
@@ -4959,8 +6841,38 @@ avr_handle_fntype_attribute (tree *node, tree name,
   return NULL_TREE;
 }
 
-/* Look for attribute `progmem' in DECL
-   if found return 1, otherwise 0.  */
+
+/* AVR attributes.  */
+static const struct attribute_spec
+avr_attribute_table[] =
+{
+  /* { 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 }
+};
+
+
+/* Look if DECL shall be placed in program memory space by
+   means of attribute `progmem' or some address-space qualifier.
+   Return non-zero if DECL is data that must end up in Flash and
+   zero if the data lives in RAM (.bss, .data, .rodata, ...).
+   
+   Return 2   if DECL is located in 24-bit flash address-space
+   Return 1   if DECL is located in 16-bit flash address-space
+   Return -1  if attribute `progmem' occurs in DECL or ATTRIBUTES
+   Return 0   otherwise  */
 
 int
 avr_progmem_p (tree decl, tree attributes)
@@ -4970,11 +6882,18 @@ avr_progmem_p (tree decl, tree attributes)
   if (TREE_CODE (decl) != VAR_DECL)
     return 0;
 
+  if (avr_decl_pgmx_p (decl))
+    return 2;
+
+  if (avr_decl_pgm_p (decl))
+    return 1;
+
   if (NULL_TREE
       != lookup_attribute ("progmem", attributes))
-    return 1;
+    return -1;
 
-  a=decl;
+  a = decl;
   do
     a = TREE_TYPE(a);
   while (TREE_CODE (a) == ARRAY_TYPE);
@@ -4983,16 +6902,123 @@ avr_progmem_p (tree decl, tree attributes)
     return 0;
 
   if (NULL_TREE != lookup_attribute ("progmem", TYPE_ATTRIBUTES (a)))
-    return 1;
+    return -1;
   
   return 0;
 }
 
+
+/* Scan type TYP for pointer references to address space ASn.
+   Return ADDR_SPACE_GENERIC (i.e. 0) if all pointers targeting
+   the AS are also declared to be CONST.
+   Otherwise, return the respective addres space, i.e. a value != 0.  */
+   
+static addr_space_t
+avr_nonconst_pointer_addrspace (tree typ)
+{
+  while (ARRAY_TYPE == TREE_CODE (typ))
+    typ = TREE_TYPE (typ);
+
+  if (POINTER_TYPE_P (typ))
+    {
+      tree target = TREE_TYPE (typ);
+
+      /* Pointer to function: Test the function's return type.  */
+      
+      if (FUNCTION_TYPE == TREE_CODE (target))
+        return avr_nonconst_pointer_addrspace (TREE_TYPE (target));
+
+      /* "Ordinary" pointers... */
+
+      while (TREE_CODE (target) == ARRAY_TYPE)
+        target = TREE_TYPE (target);
+
+      if (!ADDR_SPACE_GENERIC_P (TYPE_ADDR_SPACE (target))
+          && !TYPE_READONLY (target))
+        {
+          /* Pointers to non-generic address space must be const.  */
+          
+          return TYPE_ADDR_SPACE (target);
+        }
+
+      /* Scan pointer's target type.  */
+      
+      return avr_nonconst_pointer_addrspace (target);
+    }
+
+  return ADDR_SPACE_GENERIC;
+}
+
+
+/* Sanity check NODE so that all pointers targeting address space AS1
+   go along with CONST qualifier.  Writing to this address space should
+   be detected and complained about as early as possible.  */
+
+static bool
+avr_pgm_check_var_decl (tree node)
+{
+  const char *reason = NULL;
+  
+  addr_space_t as = ADDR_SPACE_GENERIC;
+
+  gcc_assert (as == 0);
+  
+  if (avr_log.progmem)
+    avr_edump ("%?: %t\n", node);
+  
+  switch (TREE_CODE (node))
+    {
+    default:
+      break;
+
+    case VAR_DECL:
+      if (as = avr_nonconst_pointer_addrspace (TREE_TYPE (node)), as)
+        reason = "variable";
+      break;
+
+    case PARM_DECL:
+      if (as = avr_nonconst_pointer_addrspace (TREE_TYPE (node)), as)
+        reason = "function parameter";
+      break;
+        
+    case FIELD_DECL:
+      if (as = avr_nonconst_pointer_addrspace (TREE_TYPE (node)), as)
+        reason = "structure field";
+      break;
+        
+    case FUNCTION_DECL:
+      if (as = avr_nonconst_pointer_addrspace (TREE_TYPE (TREE_TYPE (node))),
+          as)
+        reason = "return type of function";
+      break;
+
+    case POINTER_TYPE:
+      if (as = avr_nonconst_pointer_addrspace (node), as)
+        reason = "pointer";
+      break;
+    }
+
+  if (reason)
+    {
+      if (TYPE_P (node))
+        error ("pointer targeting address space %qs must be const in %qT",
+               avr_addrspace[as].name, node);
+      else
+        error ("pointer targeting address space %qs must be const in %s %q+D",
+               avr_addrspace[as].name, reason, node);
+    }
+
+  return reason == NULL;
+}
+
+
 /* Add the section attribute if the variable is in progmem.  */
 
 static void
 avr_insert_attributes (tree node, tree *attributes)
 {
+  avr_pgm_check_var_decl (node);
+
   if (TREE_CODE (node) == VAR_DECL
       && (TREE_STATIC (node) || DECL_EXTERNAL (node))
       && avr_progmem_p (node, *attributes))
@@ -5009,43 +7035,34 @@ avr_insert_attributes (tree node, tree *attributes)
       if (error_mark_node == node0)
         return;
       
-      if (TYPE_READONLY (node0))
+      if (!TYPE_READONLY (node0)
+          && !TREE_READONLY (node))
         {
-          static const char dsec[] = ".progmem.data";
+          addr_space_t as = TYPE_ADDR_SPACE (TREE_TYPE (node));
+          const char *reason = "__attribute__((progmem))";
 
-          *attributes = tree_cons (get_identifier ("section"),
-                                   build_tree_list (NULL, build_string (strlen (dsec), dsec)),
-                                   *attributes);
-        }
-      else
-        {
+          if (!ADDR_SPACE_GENERIC_P (as))
+            reason = avr_addrspace[as].name;
+          
+          if (avr_log.progmem)
+            avr_edump ("\n%?: %t\n%t\n", node, node0);
+          
           error ("variable %q+D must be const in order to be put into"
-                 " read-only section by means of %<__attribute__((progmem))%>",
-                 node);
+                 " read-only section by means of %qs", node, reason);
         }
     }
 }
 
-/* A get_unnamed_section callback for switching to progmem_section.  */
-
-static void
-avr_output_progmem_section_asm_op (const void *arg ATTRIBUTE_UNUSED)
-{
-  fprintf (asm_out_file,
-          "\t.section .progmem.gcc_sw_table, \"%s\", @progbits\n",
-          AVR_HAVE_JMP_CALL ? "a" : "ax");
-  /* Should already be aligned, this is just to be safe if it isn't.  */
-  fprintf (asm_out_file, "\t.p2align 1\n");
-}
-
 
 /* 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,
+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;
@@ -5070,47 +7087,161 @@ avr_output_data_section_asm_op (const void *data)
 }
 
 
-/* Unnamed section callback for bss_section
-   to track need of __do_clear_bss.  */
+/* 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);
+}
+
+
+/* Unnamed section callback for progmem*.data sections.  */
+
+static void
+avr_output_progmem_section_asm_op (const void *data)
+{
+  fprintf (asm_out_file, "\t.section\t%s,\"a\",@progbits\n",
+           (const char*) data);
+}
+
+
+/* Implement `TARGET_ASM_INIT_SECTIONS'.  */
+
+static void
+avr_asm_init_sections (void)
+{
+  unsigned int n;
+  
+  /* Set up a section for jump tables.  Alignment is handled by
+     ASM_OUTPUT_BEFORE_CASE_LABEL.  */
+  
+  if (AVR_HAVE_JMP_CALL)
+    {
+      progmem_swtable_section
+        = get_unnamed_section (0, output_section_asm_op,
+                               "\t.section\t.progmem.gcc_sw_table"
+                               ",\"a\",@progbits");
+    }
+  else
+    {
+      progmem_swtable_section
+        = get_unnamed_section (SECTION_CODE, output_section_asm_op,
+                               "\t.section\t.progmem.gcc_sw_table"
+                               ",\"ax\",@progbits");
+    }
+
+  for (n = 0; n < sizeof (progmem_section) / sizeof (*progmem_section); n++)
+    {
+      progmem_section[n]
+        = get_unnamed_section (0, avr_output_progmem_section_asm_op,
+                               progmem_section_prefix[n]);
+    }
+  
+  /* Override section callbacks to keep track of `avr_need_clear_bss_p'
+     resp. `avr_need_copy_data_p'.  */
+  
+  readonly_data_section->unnamed.callback = avr_output_data_section_asm_op;
+  data_section->unnamed.callback = avr_output_data_section_asm_op;
+  bss_section->unnamed.callback = avr_output_bss_section_asm_op;
+}
+
+
+/* Implement `TARGET_ASM_FUNCTION_RODATA_SECTION'.  */
 
-static void
-avr_output_bss_section_asm_op (const void *data)
+static section*
+avr_asm_function_rodata_section (tree decl)
 {
-  avr_need_clear_bss_p = true;
-  
-  /* Dispatch to default.  */
-  output_section_asm_op (data);
-}
+  /* If a function is unused and optimized out by -ffunction-sections
+     and --gc-sections, ensure that the same will happen for its jump
+     tables by putting them into individual sections.  */
 
+  unsigned int flags;
+  section * frodata;
 
-/* Implement `TARGET_ASM_INIT_SECTIONS'.  */
+  /* Get the frodata section from the default function in varasm.c
+     but treat function-associated data-like jump tables as code
+     rather than as user defined data.  AVR has no constant pools.  */
+  {
+    int fdata = flag_data_sections;
 
-static void
-avr_asm_init_sections (void)
-{
-  progmem_section = get_unnamed_section (AVR_HAVE_JMP_CALL ? 0 : SECTION_CODE,
-                                        avr_output_progmem_section_asm_op,
-                                        NULL);
-  readonly_data_section = data_section;
+    flag_data_sections = flag_function_sections;
+    frodata = default_function_rodata_section (decl);
+    flag_data_sections = fdata;
+    flags = frodata->common.flags;
+  }
 
-  data_section->unnamed.callback = avr_output_data_section_asm_op;
-  bss_section->unnamed.callback = avr_output_bss_section_asm_op;
+  if (frodata != readonly_data_section
+      && flags & SECTION_NAMED)
+    {
+      /* Adjust section flags and replace section name prefix.  */
+
+      unsigned int i;
+
+      static const char* const prefix[] =
+        {
+          ".rodata",          ".progmem.gcc_sw_table",
+          ".gnu.linkonce.r.", ".gnu.linkonce.t."
+        };
+
+      for (i = 0; i < sizeof (prefix) / sizeof (*prefix); i += 2)
+        {
+          const char * old_prefix = prefix[i];
+          const char * new_prefix = prefix[i+1];
+          const char * name = frodata->named.name;
+
+          if (STR_PREFIX_P (name, old_prefix))
+            {
+              const char *rname = avr_replace_prefix (name,
+                                                      old_prefix, new_prefix);
+
+              flags &= ~SECTION_CODE;
+              flags |= AVR_HAVE_JMP_CALL ? 0 : SECTION_CODE;
+              
+              return get_section (rname, flags, frodata->named.decl);
+            }
+        }
+    }
+        
+  return progmem_swtable_section;
 }
 
 
 /* Implement `TARGET_ASM_NAMED_SECTION'.  */
 /* Track need of __do_clear_bss, __do_copy_data for named sections.  */
 
-void
+static void
 avr_asm_named_section (const char *name, unsigned int flags, tree decl)
 {
+  if (flags & AVR_SECTION_PROGMEM)
+    {
+      addr_space_t as = (flags & AVR_SECTION_PROGMEM) / SECTION_MACH_DEP;
+      int segment = avr_addrspace[as].segment % avr_current_arch->n_segments;
+      const char *old_prefix = ".rodata";
+      const char *new_prefix = progmem_section_prefix[segment];
+      const char *sname = new_prefix;
+      
+      if (STR_PREFIX_P (name, old_prefix))
+        {
+          sname = avr_replace_prefix (name, old_prefix, new_prefix);
+        }
+
+      default_elf_asm_named_section (sname, flags, decl);
+
+      return;
+    }
+  
   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));
+    avr_need_copy_data_p = (STR_PREFIX_P (name, ".data")
+                            || STR_PREFIX_P (name, ".rodata")
+                            || STR_PREFIX_P (name, ".gnu.linkonce.d"));
   
   if (!avr_need_clear_bss_p)
-    avr_need_clear_bss_p = (0 == strncmp (name, ".bss", 4));
+    avr_need_clear_bss_p = STR_PREFIX_P (name, ".bss");
   
   default_elf_asm_named_section (name, flags, decl);
 }
@@ -5120,7 +7251,7 @@ avr_section_type_flags (tree decl, const char *name, int reloc)
 {
   unsigned int flags = default_section_type_flags (decl, name, reloc);
 
-  if (strncmp (name, ".noinit", 7) == 0)
+  if (STR_PREFIX_P (name, ".noinit"))
     {
       if (decl && TREE_CODE (decl) == VAR_DECL
          && DECL_INITIAL (decl) == NULL_TREE)
@@ -5130,8 +7261,22 @@ 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;
+  if (decl && DECL_P (decl)
+      && avr_progmem_p (decl, DECL_ATTRIBUTES (decl)))
+    {
+      addr_space_t as = TYPE_ADDR_SPACE (TREE_TYPE (decl));
+
+      /* Attribute progmem puts data in generic address space.
+         Set section flags as if it was in __pgm to get the right
+         section prefix in the remainder.  */
+
+      if (ADDR_SPACE_GENERIC_P (as))
+        as = ADDR_SPACE_PGM;
+
+      flags |= as * SECTION_MACH_DEP;
+      flags &= ~SECTION_WRITE;
+      flags &= ~SECTION_BSS;
+    }
   
   return flags;
 }
@@ -5140,8 +7285,7 @@ avr_section_type_flags (tree decl, const char *name, int reloc)
 /* Implement `TARGET_ENCODE_SECTION_INFO'.  */
 
 static void
-avr_encode_section_info (tree decl, rtx rtl,
-                         int new_decl_p)
+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
@@ -5161,25 +7305,69 @@ avr_encode_section_info (tree decl, rtx rtl,
 }
 
 
+/* Implement `TARGET_ASM_SELECT_SECTION' */
+
+static section *
+avr_asm_select_section (tree decl, int reloc, unsigned HOST_WIDE_INT align)
+{
+  section * sect = default_elf_select_section (decl, reloc, align);
+  
+  if (decl && DECL_P (decl)
+      && avr_progmem_p (decl, DECL_ATTRIBUTES (decl)))
+    {
+      addr_space_t as = TYPE_ADDR_SPACE (TREE_TYPE (decl));
+      int segment = avr_addrspace[as].segment % avr_current_arch->n_segments;
+      
+      if (sect->common.flags & SECTION_NAMED)
+        {
+          const char * name = sect->named.name;
+          const char * old_prefix = ".rodata";
+          const char * new_prefix = progmem_section_prefix[segment];
+
+          if (STR_PREFIX_P (name, old_prefix))
+            {
+              const char *sname = avr_replace_prefix (name,
+                                                      old_prefix, new_prefix);
+
+              return get_section (sname, sect->common.flags, sect->named.decl);
+            }
+        }
+          
+      return progmem_section[segment];
+    }
+
+  return sect;
+}
+
 /* Implement `TARGET_ASM_FILE_START'.  */
-/* Outputs some appropriate text to go at the start of an assembler
-   file.  */
+/* Outputs some text at the start of each assembler file.  */
 
 static void
 avr_file_start (void)
 {
+  int sfr_offset = avr_current_arch->sfr_offset;
+
   if (avr_current_arch->asm_only)
     error ("MCU %qs supported for assembler only", avr_current_device->name);
 
   default_file_start ();
 
-/*  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);
+  if (!AVR_HAVE_8BIT_SP)
+    fprintf (asm_out_file,
+             "__SP_H__ = 0x%02x\n",
+             -sfr_offset + SP_ADDR + 1);
+
+  fprintf (asm_out_file,
+           "__SP_L__ = 0x%02x\n"
+           "__SREG__ = 0x%02x\n"
+           "__RAMPZ__ = 0x%02x\n"
+           "__tmp_reg__ = %d\n" 
+           "__zero_reg__ = %d\n",
+           -sfr_offset + SP_ADDR,
+           -sfr_offset + SREG_ADDR,
+           -sfr_offset + RAMPZ_ADDR,
+           TMP_REGNO,
+           ZERO_REGNO);
 }
 
 
@@ -5275,7 +7463,8 @@ avr_register_move_cost (enum machine_mode mode ATTRIBUTE_UNUSED,
 /* Implement `TARGET_MEMORY_MOVE_COST' */
 
 static int
-avr_memory_move_cost (enum machine_mode mode, reg_class_t rclass ATTRIBUTE_UNUSED,
+avr_memory_move_cost (enum machine_mode mode,
+                      reg_class_t rclass ATTRIBUTE_UNUSED,
                       bool in ATTRIBUTE_UNUSED)
 {
   return (mode == QImode ? 2
@@ -5293,7 +7482,7 @@ avr_memory_move_cost (enum machine_mode mode, reg_class_t rclass ATTRIBUTE_UNUSE
 
 static int
 avr_operand_rtx_cost (rtx x, enum machine_mode mode, enum rtx_code outer,
-                     bool speed)
+                     int opno, bool speed)
 {
   enum rtx_code code = GET_CODE (x);
   int total;
@@ -5313,18 +7502,19 @@ avr_operand_rtx_cost (rtx x, enum machine_mode mode, enum rtx_code outer,
     }
 
   total = 0;
-  avr_rtx_costs (x, code, outer, &total, speed);
+  avr_rtx_costs (x, code, outer, opno, &total, speed);
   return total;
 }
 
-/* The AVR backend's rtx_cost function.  X is rtx expression whose cost
-   is to be calculated.  Return true if the complete cost has been
-   computed, and false if subexpressions should be scanned.  In either
-   case, *TOTAL contains the cost result.  */
+/* Worker function for AVR backend's rtx_cost function.
+   X is rtx expression whose cost is to be calculated.
+   Return true if the complete cost has been computed.
+   Return false if subexpressions should be scanned.
+   In either case, *TOTAL contains the cost result.  */
 
 static bool
-avr_rtx_costs (rtx x, int codearg, int outer_code ATTRIBUTE_UNUSED, int *total,
-              bool speed)
+avr_rtx_costs_1 (rtx x, int codearg, int outer_code ATTRIBUTE_UNUSED,
+                 int opno ATTRIBUTE_UNUSED, int *total, bool speed)
 {
   enum rtx_code code = (enum rtx_code) codearg;
   enum machine_mode mode = GET_MODE (x);
@@ -5334,14 +7524,14 @@ avr_rtx_costs (rtx x, int codearg, int outer_code ATTRIBUTE_UNUSED, int *total,
     {
     case CONST_INT:
     case CONST_DOUBLE:
+    case SYMBOL_REF:
+    case CONST:
+    case LABEL_REF:
       /* Immediate constants are as cheap as registers.  */
       *total = 0;
       return true;
 
     case MEM:
-    case CONST:
-    case LABEL_REF:
-    case SYMBOL_REF:
       *total = COSTS_N_INSNS (GET_MODE_SIZE (mode));
       return true;
 
@@ -5353,18 +7543,16 @@ avr_rtx_costs (rtx x, int codearg, int outer_code ATTRIBUTE_UNUSED, int *total,
          *total = COSTS_N_INSNS (1);
          break;
 
-       case HImode:
-         *total = COSTS_N_INSNS (3);
-         break;
-
-       case SImode:
-         *total = COSTS_N_INSNS (7);
-         break;
+        case HImode:
+        case PSImode:
+        case SImode:
+          *total = COSTS_N_INSNS (2 * GET_MODE_SIZE (mode) - 1);
+          break;
 
        default:
          return false;
        }
-      *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, speed);
+      *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, 0, speed);
       return true;
 
     case ABS:
@@ -5378,40 +7566,66 @@ avr_rtx_costs (rtx x, int codearg, int outer_code ATTRIBUTE_UNUSED, int *total,
        default:
          return false;
        }
-      *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, speed);
+      *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, 0, speed);
       return true;
 
     case NOT:
       *total = COSTS_N_INSNS (GET_MODE_SIZE (mode));
-      *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, speed);
+      *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, 0, speed);
       return true;
 
     case ZERO_EXTEND:
       *total = COSTS_N_INSNS (GET_MODE_SIZE (mode)
                              - GET_MODE_SIZE (GET_MODE (XEXP (x, 0))));
-      *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, speed);
+      *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, 0, speed);
       return true;
 
     case SIGN_EXTEND:
       *total = COSTS_N_INSNS (GET_MODE_SIZE (mode) + 2
                              - GET_MODE_SIZE (GET_MODE (XEXP (x, 0))));
-      *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, speed);
+      *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, 0, speed);
       return true;
 
     case PLUS:
       switch (mode)
        {
        case QImode:
+          if (AVR_HAVE_MUL
+              && MULT == GET_CODE (XEXP (x, 0))
+              && register_operand (XEXP (x, 1), QImode))
+            {
+              /* multiply-add */
+              *total = COSTS_N_INSNS (speed ? 4 : 3);
+              /* multiply-add with constant: will be split and load constant. */
+              if (CONST_INT_P (XEXP (XEXP (x, 0), 1)))
+                *total = COSTS_N_INSNS (1) + *total;
+              return true;
+            }
          *total = COSTS_N_INSNS (1);
          if (GET_CODE (XEXP (x, 1)) != CONST_INT)
-           *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, speed);
+           *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, 1, speed);
          break;
 
        case HImode:
+          if (AVR_HAVE_MUL
+              && (MULT == GET_CODE (XEXP (x, 0))
+                  || ASHIFT == GET_CODE (XEXP (x, 0)))
+              && register_operand (XEXP (x, 1), HImode)
+              && (ZERO_EXTEND == GET_CODE (XEXP (XEXP (x, 0), 0))
+                  || SIGN_EXTEND == GET_CODE (XEXP (XEXP (x, 0), 0))))
+            {
+              /* multiply-add */
+              *total = COSTS_N_INSNS (speed ? 5 : 4);
+              /* multiply-add with constant: will be split and load constant. */
+              if (CONST_INT_P (XEXP (XEXP (x, 0), 1)))
+                *total = COSTS_N_INSNS (1) + *total;
+              return true;
+            }
          if (GET_CODE (XEXP (x, 1)) != CONST_INT)
            {
              *total = COSTS_N_INSNS (2);
-             *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, speed);
+             *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, 1,
+                                             speed);
            }
          else if (INTVAL (XEXP (x, 1)) >= -63 && INTVAL (XEXP (x, 1)) <= 63)
            *total = COSTS_N_INSNS (1);
@@ -5419,11 +7633,25 @@ avr_rtx_costs (rtx x, int codearg, int outer_code ATTRIBUTE_UNUSED, int *total,
            *total = COSTS_N_INSNS (2);
          break;
 
+        case PSImode:
+          if (!CONST_INT_P (XEXP (x, 1)))
+            {
+              *total = COSTS_N_INSNS (3);
+              *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, 1,
+                                              speed);
+            }
+          else if (INTVAL (XEXP (x, 1)) >= -63 && INTVAL (XEXP (x, 1)) <= 63)
+            *total = COSTS_N_INSNS (2);
+          else
+            *total = COSTS_N_INSNS (3);
+          break;
+
        case SImode:
          if (GET_CODE (XEXP (x, 1)) != CONST_INT)
            {
              *total = COSTS_N_INSNS (4);
-             *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, speed);
+             *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, 1,
+                                             speed);
            }
          else if (INTVAL (XEXP (x, 1)) >= -63 && INTVAL (XEXP (x, 1)) <= 63)
            *total = COSTS_N_INSNS (1);
@@ -5434,22 +7662,50 @@ avr_rtx_costs (rtx x, int codearg, int outer_code ATTRIBUTE_UNUSED, int *total,
        default:
          return false;
        }
-      *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, speed);
+      *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, 0, speed);
       return true;
 
     case MINUS:
+      if (AVR_HAVE_MUL
+          && QImode == mode
+          && register_operand (XEXP (x, 0), QImode)
+          && MULT == GET_CODE (XEXP (x, 1)))
+        {
+          /* multiply-sub */
+          *total = COSTS_N_INSNS (speed ? 4 : 3);
+          /* multiply-sub with constant: will be split and load constant. */
+          if (CONST_INT_P (XEXP (XEXP (x, 1), 1)))
+            *total = COSTS_N_INSNS (1) + *total;
+          return true;
+        }
+      if (AVR_HAVE_MUL
+          && HImode == mode
+          && register_operand (XEXP (x, 0), HImode)
+          && (MULT == GET_CODE (XEXP (x, 1))
+              || ASHIFT == GET_CODE (XEXP (x, 1)))
+          && (ZERO_EXTEND == GET_CODE (XEXP (XEXP (x, 1), 0))
+              || SIGN_EXTEND == GET_CODE (XEXP (XEXP (x, 1), 0))))
+        {
+          /* multiply-sub */
+          *total = COSTS_N_INSNS (speed ? 5 : 4);
+          /* multiply-sub with constant: will be split and load constant. */
+          if (CONST_INT_P (XEXP (XEXP (x, 1), 1)))
+            *total = COSTS_N_INSNS (1) + *total;
+          return true;
+        }
+      /* FALLTHRU */
     case AND:
     case IOR:
       *total = COSTS_N_INSNS (GET_MODE_SIZE (mode));
-      *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, speed);
+      *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, 0, speed);
       if (GET_CODE (XEXP (x, 1)) != CONST_INT)
-          *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, speed);
+       *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, 1, speed);
       return true;
 
     case XOR:
       *total = COSTS_N_INSNS (GET_MODE_SIZE (mode));
-      *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, speed);
-      *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, speed);
+      *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, 0, speed);
+      *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, 1, speed);
       return true;
 
     case MULT:
@@ -5466,18 +7722,88 @@ avr_rtx_costs (rtx x, int codearg, int outer_code ATTRIBUTE_UNUSED, int *total,
 
        case HImode:
          if (AVR_HAVE_MUL)
-           *total = COSTS_N_INSNS (!speed ? 7 : 10);
+            {
+              rtx op0 = XEXP (x, 0);
+              rtx op1 = XEXP (x, 1);
+              enum rtx_code code0 = GET_CODE (op0);
+              enum rtx_code code1 = GET_CODE (op1);
+              bool ex0 = SIGN_EXTEND == code0 || ZERO_EXTEND == code0;
+              bool ex1 = SIGN_EXTEND == code1 || ZERO_EXTEND == code1;
+
+              if (ex0
+                  && (u8_operand (op1, HImode)
+                      || s8_operand (op1, HImode)))
+                {
+                  *total = COSTS_N_INSNS (!speed ? 4 : 6);
+                  return true;
+                }
+              if (ex0
+                  && register_operand (op1, HImode))
+                {
+                  *total = COSTS_N_INSNS (!speed ? 5 : 8);
+                  return true;
+                }
+              else if (ex0 || ex1)
+                {
+                  *total = COSTS_N_INSNS (!speed ? 3 : 5);
+                  return true;
+                }
+              else if (register_operand (op0, HImode)
+                       && (u8_operand (op1, HImode)
+                           || s8_operand (op1, HImode)))
+                {
+                  *total = COSTS_N_INSNS (!speed ? 6 : 9);
+                  return true;
+                }
+              else
+                *total = COSTS_N_INSNS (!speed ? 7 : 10);
+            }
          else if (!speed)
            *total = COSTS_N_INSNS (AVR_HAVE_JMP_CALL ? 2 : 1);
          else
            return false;
          break;
 
+        case PSImode:
+          if (!speed)
+            *total = COSTS_N_INSNS (AVR_HAVE_JMP_CALL ? 2 : 1);
+          else
+            *total = 10;
+          break;
+
+       case SImode:
+         if (AVR_HAVE_MUL)
+            {
+              if (!speed)
+                {
+                  /* Add some additional costs besides CALL like moves etc.  */
+
+                  *total = COSTS_N_INSNS (AVR_HAVE_JMP_CALL ? 5 : 4);
+                }
+              else
+                {
+                  /* Just a rough estimate.  Even with -O2 we don't want bulky
+                     code expanded inline.  */
+
+                  *total = COSTS_N_INSNS (25);
+                }
+            }
+          else
+            {
+              if (speed)
+                *total = COSTS_N_INSNS (300);
+              else
+                /* Add some additional costs besides CALL like moves etc.  */
+                *total = COSTS_N_INSNS (AVR_HAVE_JMP_CALL ? 5 : 4);
+            }
+          
+          return true;
+          
        default:
          return false;
        }
-      *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, speed);
-      *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, speed);
+      *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, 0, speed);
+      *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, 1, speed);
       return true;
 
     case DIV:
@@ -5485,11 +7811,16 @@ avr_rtx_costs (rtx x, int codearg, int outer_code ATTRIBUTE_UNUSED, int *total,
     case UDIV:
     case UMOD:
       if (!speed)
-       *total = COSTS_N_INSNS (AVR_HAVE_JMP_CALL ? 2 : 1);
+        *total = COSTS_N_INSNS (AVR_HAVE_JMP_CALL ? 2 : 1);
       else
-       return false;
-      *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, speed);
-      *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, speed);
+        *total = COSTS_N_INSNS (15 * GET_MODE_SIZE (mode));
+      *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, 0, speed);
+      /* For div/mod with const-int divisor we have at least the cost of
+         loading the divisor. */
+      if (CONST_INT_P (XEXP (x, 1)))
+        *total += COSTS_N_INSNS (GET_MODE_SIZE (mode));
+      /* Add some overall penaly for clobbering and moving around registers */
+      *total += COSTS_N_INSNS (2);
       return true;
 
     case ROTATE:
@@ -5524,7 +7855,7 @@ avr_rtx_costs (rtx x, int codearg, int outer_code ATTRIBUTE_UNUSED, int *total,
        default:
          return false;
        }
-      *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, speed);
+      *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, 0, speed);
       return true;    
 
     case ASHIFT:
@@ -5534,7 +7865,8 @@ avr_rtx_costs (rtx x, int codearg, int outer_code ATTRIBUTE_UNUSED, int *total,
          if (GET_CODE (XEXP (x, 1)) != CONST_INT)
            {
              *total = COSTS_N_INSNS (!speed ? 4 : 17);
-             *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, speed);
+             *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, 1,
+                                             speed);
            }
          else
            {
@@ -5549,10 +7881,29 @@ avr_rtx_costs (rtx x, int codearg, int outer_code ATTRIBUTE_UNUSED, int *total,
          break;
 
        case HImode:
+          if (AVR_HAVE_MUL)
+            {
+              if (const_2_to_7_operand (XEXP (x, 1), HImode)
+                  && (SIGN_EXTEND == GET_CODE (XEXP (x, 0))
+                      || ZERO_EXTEND == GET_CODE (XEXP (x, 0))))
+                {
+                  *total = COSTS_N_INSNS (!speed ? 4 : 6);
+                  return true;
+                }
+            }
+          
+          if (const1_rtx == (XEXP (x, 1))
+              && SIGN_EXTEND == GET_CODE (XEXP (x, 0)))
+            {
+              *total = COSTS_N_INSNS (2);
+              return true;
+            }
+          
          if (GET_CODE (XEXP (x, 1)) != CONST_INT)
            {
              *total = COSTS_N_INSNS (!speed ? 5 : 41);
-             *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, speed);
+             *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, 1,
+                                             speed);
            }
          else
            switch (INTVAL (XEXP (x, 1)))
@@ -5589,15 +7940,42 @@ avr_rtx_costs (rtx x, int codearg, int outer_code ATTRIBUTE_UNUSED, int *total,
                break;
              default:
                *total = COSTS_N_INSNS (!speed ? 5 : 41);
-               *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, speed);
+               *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, 1,
+                                               speed);
              }
          break;
 
+        case PSImode:
+          if (!CONST_INT_P (XEXP (x, 1)))
+            {
+              *total = COSTS_N_INSNS (!speed ? 6 : 73);
+            }
+          else
+            switch (INTVAL (XEXP (x, 1)))
+              {
+              case 0:
+                *total = 0;
+                break;
+              case 1:
+              case 8:
+              case 16:
+                *total = COSTS_N_INSNS (3);
+                break;
+              case 23:
+                *total = COSTS_N_INSNS (5);
+                break;
+              default:
+                *total = COSTS_N_INSNS (!speed ? 5 : 3 * INTVAL (XEXP (x, 1)));
+                break;
+              }
+          break;
+
        case SImode:
          if (GET_CODE (XEXP (x, 1)) != CONST_INT)
            {
              *total = COSTS_N_INSNS (!speed ? 7 : 113);
-             *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, speed);
+             *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, 1,
+                                             speed);
            }
          else
            switch (INTVAL (XEXP (x, 1)))
@@ -5621,14 +7999,15 @@ avr_rtx_costs (rtx x, int codearg, int outer_code ATTRIBUTE_UNUSED, int *total,
                break;
              default:
                *total = COSTS_N_INSNS (!speed ? 7 : 113);
-               *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, speed);
+               *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, 1,
+                                               speed);
              }
          break;
 
        default:
          return false;
        }
-      *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, speed);
+      *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, 0, speed);
       return true;
 
     case ASHIFTRT:
@@ -5638,7 +8017,8 @@ avr_rtx_costs (rtx x, int codearg, int outer_code ATTRIBUTE_UNUSED, int *total,
          if (GET_CODE (XEXP (x, 1)) != CONST_INT)
            {
              *total = COSTS_N_INSNS (!speed ? 4 : 17);
-             *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, speed);
+             *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, 1,
+                                             speed);
            }
          else
            {
@@ -5658,7 +8038,8 @@ avr_rtx_costs (rtx x, int codearg, int outer_code ATTRIBUTE_UNUSED, int *total,
          if (GET_CODE (XEXP (x, 1)) != CONST_INT)
            {
              *total = COSTS_N_INSNS (!speed ? 5 : 41);
-             *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, speed);
+             *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, 1,
+                                             speed);
            }
          else
            switch (INTVAL (XEXP (x, 1)))
@@ -5694,15 +8075,44 @@ avr_rtx_costs (rtx x, int codearg, int outer_code ATTRIBUTE_UNUSED, int *total,
                break;
              default:
                *total = COSTS_N_INSNS (!speed ? 5 : 41);
-               *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, speed);
+               *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, 1,
+                                               speed);
              }
          break;
 
+        case PSImode:
+          if (!CONST_INT_P (XEXP (x, 1)))
+            {
+              *total = COSTS_N_INSNS (!speed ? 6 : 73);
+            }
+          else
+            switch (INTVAL (XEXP (x, 1)))
+              {
+              case 0:
+                *total = 0;
+                break;
+              case 1:
+                *total = COSTS_N_INSNS (3);
+                break;
+              case 16:
+              case 8:
+                *total = COSTS_N_INSNS (5);
+                break;
+              case 23:
+                *total = COSTS_N_INSNS (4);
+                break;
+              default:
+                *total = COSTS_N_INSNS (!speed ? 5 : 3 * INTVAL (XEXP (x, 1)));
+                break;
+              }
+          break;
+
        case SImode:
          if (GET_CODE (XEXP (x, 1)) != CONST_INT)
            {
              *total = COSTS_N_INSNS (!speed ? 7 : 113);
-             *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, speed);
+             *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, 1,
+                                             speed);
            }
          else
            switch (INTVAL (XEXP (x, 1)))
@@ -5726,14 +8136,15 @@ avr_rtx_costs (rtx x, int codearg, int outer_code ATTRIBUTE_UNUSED, int *total,
                break;
              default:
                *total = COSTS_N_INSNS (!speed ? 7 : 113);
-               *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, speed);
+               *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, 1,
+                                               speed);
              }
          break;
 
        default:
          return false;
        }
-      *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, speed);
+      *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, 0, speed);
       return true;
 
     case LSHIFTRT:
@@ -5743,7 +8154,8 @@ avr_rtx_costs (rtx x, int codearg, int outer_code ATTRIBUTE_UNUSED, int *total,
          if (GET_CODE (XEXP (x, 1)) != CONST_INT)
            {
              *total = COSTS_N_INSNS (!speed ? 4 : 17);
-             *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, speed);
+             *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, 1,
+                                             speed);
            }
          else
            {
@@ -5761,7 +8173,8 @@ avr_rtx_costs (rtx x, int codearg, int outer_code ATTRIBUTE_UNUSED, int *total,
          if (GET_CODE (XEXP (x, 1)) != CONST_INT)
            {
              *total = COSTS_N_INSNS (!speed ? 5 : 41);
-             *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, speed);
+             *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, 1,
+                                             speed);
            }
          else
            switch (INTVAL (XEXP (x, 1)))
@@ -5800,15 +8213,42 @@ avr_rtx_costs (rtx x, int codearg, int outer_code ATTRIBUTE_UNUSED, int *total,
                break;
              default:
                *total = COSTS_N_INSNS (!speed ? 5 : 41);
-               *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, speed);
+               *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, 1,
+                                               speed);
              }
          break;
 
+        case PSImode:
+          if (!CONST_INT_P (XEXP (x, 1)))
+            {
+              *total = COSTS_N_INSNS (!speed ? 6 : 73);
+            }
+          else
+            switch (INTVAL (XEXP (x, 1)))
+              {
+              case 0:
+                *total = 0;
+                break;
+              case 1:
+              case 8:
+              case 16:
+                *total = COSTS_N_INSNS (3);
+                break;
+              case 23:
+                *total = COSTS_N_INSNS (5);
+                break;
+              default:
+                *total = COSTS_N_INSNS (!speed ? 5 : 3 * INTVAL (XEXP (x, 1)));
+                break;
+              }
+          break;
+
        case SImode:
          if (GET_CODE (XEXP (x, 1)) != CONST_INT)
            {
              *total = COSTS_N_INSNS (!speed ? 7 : 113);
-             *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, speed);
+             *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, 1,
+                                             speed);
            }
          else
            switch (INTVAL (XEXP (x, 1)))
@@ -5832,14 +8272,15 @@ avr_rtx_costs (rtx x, int codearg, int outer_code ATTRIBUTE_UNUSED, int *total,
                break;
              default:
                *total = COSTS_N_INSNS (!speed ? 7 : 113);
-               *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, speed);
+               *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, 1,
+                                               speed);
              }
          break;
 
        default:
          return false;
        }
-      *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, speed);
+      *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, 0, speed);
       return true;
 
     case COMPARE:
@@ -5848,21 +8289,27 @@ avr_rtx_costs (rtx x, int codearg, int outer_code ATTRIBUTE_UNUSED, int *total,
        case QImode:
          *total = COSTS_N_INSNS (1);
          if (GET_CODE (XEXP (x, 1)) != CONST_INT)
-           *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, speed);
+           *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, 1, speed);
          break;
 
         case HImode:
          *total = COSTS_N_INSNS (2);
          if (GET_CODE (XEXP (x, 1)) != CONST_INT)
-            *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, speed);
+            *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, 1, speed);
          else if (INTVAL (XEXP (x, 1)) != 0)
            *total += COSTS_N_INSNS (1);
           break;
 
+        case PSImode:
+          *total = COSTS_N_INSNS (3);
+          if (CONST_INT_P (XEXP (x, 1)) && INTVAL (XEXP (x, 1)) != 0)
+            *total += COSTS_N_INSNS (2);
+          break;
+
         case SImode:
           *total = COSTS_N_INSNS (4);
           if (GET_CODE (XEXP (x, 1)) != CONST_INT)
-            *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, speed);
+            *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, 1, speed);
          else if (INTVAL (XEXP (x, 1)) != 0)
            *total += COSTS_N_INSNS (3);
           break;
@@ -5870,163 +8317,391 @@ avr_rtx_costs (rtx x, int codearg, int outer_code ATTRIBUTE_UNUSED, int *total,
        default:
          return false;
        }
-      *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, speed);
+      *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, 0, speed);
       return true;
 
+    case TRUNCATE:
+      if (AVR_HAVE_MUL
+          && LSHIFTRT == GET_CODE (XEXP (x, 0))
+          && MULT == GET_CODE (XEXP (XEXP (x, 0), 0))
+          && CONST_INT_P (XEXP (XEXP (x, 0), 1)))
+        {
+          if (QImode == mode || HImode == mode)
+            {
+              *total = COSTS_N_INSNS (2);
+              return true;
+            }
+        }
+      break;
+
     default:
       break;
     }
   return false;
 }
 
-/* Calculate the cost of a memory address.  */
+
+/* Implement `TARGET_RTX_COSTS'.  */
+
+static bool
+avr_rtx_costs (rtx x, int codearg, int outer_code,
+              int opno, int *total, bool speed)
+{
+  bool done = avr_rtx_costs_1 (x, codearg, outer_code,
+                               opno, total, speed);
+
+  if (avr_log.rtx_costs)
+    {
+      avr_edump ("\n%?=%b (%s) total=%d, outer=%C:\n%r\n",
+                 done, speed ? "speed" : "size", *total, outer_code, x);
+    }
+
+  return done;
+}
+
+
+/* Implement `TARGET_ADDRESS_COST'.  */
 
 static int
 avr_address_cost (rtx x, bool speed ATTRIBUTE_UNUSED)
 {
+  int cost = 4;
+  
   if (GET_CODE (x) == PLUS
-      && GET_CODE (XEXP (x,1)) == CONST_INT
-      && (REG_P (XEXP (x,0)) || GET_CODE (XEXP (x,0)) == SUBREG)
-      && INTVAL (XEXP (x,1)) >= 61)
-    return 18;
-  if (CONSTANT_ADDRESS_P (x))
+      && CONST_INT_P (XEXP (x, 1))
+      && (REG_P (XEXP (x, 0))
+          || GET_CODE (XEXP (x, 0)) == SUBREG))
+    {
+      if (INTVAL (XEXP (x, 1)) >= 61)
+        cost = 18;
+    }
+  else if (CONSTANT_ADDRESS_P (x))
+    {
+      if (optimize > 0
+          && io_address_operand (x, QImode))
+        cost = 2;
+    }
+
+  if (avr_log.address_cost)
+    avr_edump ("\n%?: %d = %r\n", cost, x);
+  
+  return cost;
+}
+
+/* Test for extra memory constraint 'Q'.
+   It's a memory address based on Y or Z pointer with valid displacement.  */
+
+int
+extra_constraint_Q (rtx x)
+{
+  int ok = 0;
+  
+  if (GET_CODE (XEXP (x,0)) == PLUS
+      && REG_P (XEXP (XEXP (x,0), 0))
+      && GET_CODE (XEXP (XEXP (x,0), 1)) == CONST_INT
+      && (INTVAL (XEXP (XEXP (x,0), 1))
+         <= MAX_LD_OFFSET (GET_MODE (x))))
+    {
+      rtx xx = XEXP (XEXP (x,0), 0);
+      int regno = REGNO (xx);
+      
+      ok = (/* allocate pseudos */
+            regno >= FIRST_PSEUDO_REGISTER
+            /* strictly check */
+            || regno == REG_Z || regno == REG_Y
+            /* XXX frame & arg pointer checks */
+            || xx == frame_pointer_rtx
+            || xx == arg_pointer_rtx);
+      
+      if (avr_log.constraints)
+        avr_edump ("\n%?=%d reload_completed=%d reload_in_progress=%d\n %r\n",
+                   ok, reload_completed, reload_in_progress, x);
+    }
+
+  return ok;
+}
+
+/* Convert condition code CONDITION to the valid AVR condition code.  */
+
+RTX_CODE
+avr_normalize_condition (RTX_CODE condition)
+{
+  switch (condition)
+    {
+    case GT:
+      return GE;
+    case GTU:
+      return GEU;
+    case LE:
+      return LT;
+    case LEU:
+      return LTU;
+    default:
+      gcc_unreachable ();
+    }
+}
+
+/* Helper function for `avr_reorg'.  */
+
+static rtx
+avr_compare_pattern (rtx insn)
+{
+  rtx pattern = single_set (insn);
+
+  if (pattern
+      && NONJUMP_INSN_P (insn)
+      && SET_DEST (pattern) == cc0_rtx
+      && GET_CODE (SET_SRC (pattern)) == COMPARE
+      && DImode != GET_MODE (XEXP (SET_SRC (pattern), 0))
+      && DImode != GET_MODE (XEXP (SET_SRC (pattern), 1)))
+    {
+      return pattern;
+    }
+
+  return NULL_RTX;
+}
+
+/* Helper function for `avr_reorg'.  */
+
+/* Expansion of switch/case decision trees leads to code like
+
+       cc0 = compare (Reg, Num)
+       if (cc0 == 0)
+         goto L1
+         
+       cc0 = compare (Reg, Num)
+       if (cc0 > 0)
+         goto L2
+
+   The second comparison is superfluous and can be deleted.
+   The second jump condition can be transformed from a
+   "difficult" one to a "simple" one because "cc0 > 0" and
+   "cc0 >= 0" will have the same effect here.
+
+   This function relies on the way switch/case is being expaned
+   as binary decision tree.  For example code see PR 49903.
+         
+   Return TRUE if optimization performed.
+   Return FALSE if nothing changed.
+
+   INSN1 is a comparison, i.e. avr_compare_pattern != 0.
+
+   We don't want to do this in text peephole because it is
+   tedious to work out jump offsets there and the second comparison
+   might have been transormed by `avr_reorg'.
+
+   RTL peephole won't do because peephole2 does not scan across
+   basic blocks.  */                
+                        
+static bool
+avr_reorg_remove_redundant_compare (rtx insn1)
+{
+  rtx comp1, ifelse1, xcond1, branch1;
+  rtx comp2, ifelse2, xcond2, branch2, insn2;
+  enum rtx_code code;
+  rtx jump, target, cond;
+  
+  /* Look out for:  compare1 - branch1 - compare2 - branch2  */
+
+  branch1 = next_nonnote_nondebug_insn (insn1);
+  if (!branch1 || !JUMP_P (branch1))
+    return false;
+
+  insn2 = next_nonnote_nondebug_insn (branch1);
+  if (!insn2 || !avr_compare_pattern (insn2))
+    return false;
+
+  branch2 = next_nonnote_nondebug_insn (insn2);
+  if (!branch2 || !JUMP_P (branch2))
+    return false;
+
+  comp1 = avr_compare_pattern (insn1);
+  comp2 = avr_compare_pattern (insn2);
+  xcond1 = single_set (branch1);
+  xcond2 = single_set (branch2);
+  
+  if (!comp1 || !comp2
+      || !rtx_equal_p (comp1, comp2)
+      || !xcond1 || SET_DEST (xcond1) != pc_rtx
+      || !xcond2 || SET_DEST (xcond2) != pc_rtx
+      || IF_THEN_ELSE != GET_CODE (SET_SRC (xcond1))
+      || IF_THEN_ELSE != GET_CODE (SET_SRC (xcond2)))
     {
-      if (optimize > 0 && io_address_operand (x, QImode))
-       return 2;
-      return 4;
+      return false;
     }
-  return 4;
-}
-
-/* Test for extra memory constraint 'Q'.
-   It's a memory address based on Y or Z pointer with valid displacement.  */
 
-int
-extra_constraint_Q (rtx x)
-{
-  if (GET_CODE (XEXP (x,0)) == PLUS
-      && REG_P (XEXP (XEXP (x,0), 0))
-      && GET_CODE (XEXP (XEXP (x,0), 1)) == CONST_INT
-      && (INTVAL (XEXP (XEXP (x,0), 1))
-         <= MAX_LD_OFFSET (GET_MODE (x))))
+  comp1 = SET_SRC (comp1);
+  ifelse1 = SET_SRC (xcond1);
+  ifelse2 = SET_SRC (xcond2);
+
+  /* comp<n> is COMPARE now and ifelse<n> is IF_THEN_ELSE.  */
+
+  if (EQ != GET_CODE (XEXP (ifelse1, 0))
+      || !REG_P (XEXP (comp1, 0))
+      || !CONST_INT_P (XEXP (comp1, 1))
+      || XEXP (ifelse1, 2) != pc_rtx
+      || XEXP (ifelse2, 2) != pc_rtx
+      || LABEL_REF != GET_CODE (XEXP (ifelse1, 1))
+      || LABEL_REF != GET_CODE (XEXP (ifelse2, 1))
+      || !COMPARISON_P (XEXP (ifelse2, 0))
+      || cc0_rtx != XEXP (XEXP (ifelse1, 0), 0)
+      || cc0_rtx != XEXP (XEXP (ifelse2, 0), 0)
+      || const0_rtx != XEXP (XEXP (ifelse1, 0), 1)
+      || const0_rtx != XEXP (XEXP (ifelse2, 0), 1))
     {
-      rtx xx = XEXP (XEXP (x,0), 0);
-      int regno = REGNO (xx);
-      if (TARGET_ALL_DEBUG)
-       {
-         fprintf (stderr, ("extra_constraint:\n"
-                           "reload_completed: %d\n"
-                           "reload_in_progress: %d\n"),
-                  reload_completed, reload_in_progress);
-         debug_rtx (x);
-       }
-      if (regno >= FIRST_PSEUDO_REGISTER)
-       return 1;               /* allocate pseudos */
-      else if (regno == REG_Z || regno == REG_Y)
-       return 1;               /* strictly check */
-      else if (xx == frame_pointer_rtx
-              || xx == arg_pointer_rtx)
-       return 1;               /* XXX frame & arg pointer checks */
+      return false;
     }
-  return 0;
-}
 
-/* Convert condition code CONDITION to the valid AVR condition code.  */
+  /* We filtered the insn sequence to look like
+
+        (set (cc0)
+             (compare (reg:M N)
+                      (const_int VAL)))
+        (set (pc)
+             (if_then_else (eq (cc0)
+                               (const_int 0))
+                           (label_ref L1)
+                           (pc)))
+                           
+        (set (cc0)
+             (compare (reg:M N)
+                      (const_int VAL)))
+        (set (pc)
+             (if_then_else (CODE (cc0)
+                                 (const_int 0))
+                           (label_ref L2)
+                           (pc)))
+  */
+
+  code = GET_CODE (XEXP (ifelse2, 0));
+
+  /* Map GT/GTU to GE/GEU which is easier for AVR.
+     The first two instructions compare/branch on EQ
+     so we may replace the difficult
+        
+        if (x == VAL)   goto L1;
+        if (x > VAL)    goto L2;
 
-RTX_CODE
-avr_normalize_condition (RTX_CODE condition)
-{
-  switch (condition)
+     with easy
+         
+         if (x == VAL)   goto L1;
+         if (x >= VAL)   goto L2;
+
+     Similarly, replace LE/LEU by LT/LTU.  */
+  
+  switch (code)
     {
-    case GT:
-      return GE;
-    case GTU:
-      return GEU;
-    case LE:
-      return LT;
-    case LEU:
-      return LTU;
+    case EQ:
+    case LT:  case LTU:
+    case GE:  case GEU:
+      break;
+
+    case LE:  case LEU:
+    case GT:  case GTU:
+      code = avr_normalize_condition (code);
+      break;
+      
     default:
-      gcc_unreachable ();
+      return false;
     }
+
+  /* Wrap the branches into UNSPECs so they won't be changed or
+     optimized in the remainder.  */
+
+  target = XEXP (XEXP (ifelse1, 1), 0);
+  cond = XEXP (ifelse1, 0);
+  jump = emit_jump_insn_after (gen_branch_unspec (target, cond), insn1);
+
+  JUMP_LABEL (jump) = JUMP_LABEL (branch1);
+
+  target = XEXP (XEXP (ifelse2, 1), 0);
+  cond = gen_rtx_fmt_ee (code, VOIDmode, cc0_rtx, const0_rtx);
+  jump = emit_jump_insn_after (gen_branch_unspec (target, cond), insn2);
+
+  JUMP_LABEL (jump) = JUMP_LABEL (branch2);
+
+  /* The comparisons in insn1 and insn2 are exactly the same;
+     insn2 is superfluous so delete it.  */
+     
+  delete_insn (insn2);
+  delete_insn (branch1);
+  delete_insn (branch2);
+
+  return true;
 }
 
-/* This function optimizes conditional jumps.  */
+
+/* Implement `TARGET_MACHINE_DEPENDENT_REORG'.  */
+/* Optimize conditional jumps.  */
 
 static void
 avr_reorg (void)
 {
-  rtx insn, pattern;
+  rtx insn = get_insns();
   
-  for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
+  for (insn = next_real_insn (insn); insn; insn = next_real_insn (insn))
     {
-      if (! (GET_CODE (insn) == INSN
-            || GET_CODE (insn) == CALL_INSN
-            || GET_CODE (insn) == JUMP_INSN)
-         || !single_set (insn))
-       continue;
+      rtx pattern = avr_compare_pattern (insn);
+      
+      if (!pattern)
+        continue;
 
-      pattern = PATTERN (insn);
+      if (optimize
+          && avr_reorg_remove_redundant_compare (insn))
+        {
+          continue;
+        }
 
-      if (GET_CODE (pattern) == PARALLEL)
-       pattern = XVECEXP (pattern, 0, 0);
-      if (GET_CODE (pattern) == SET
-         && SET_DEST (pattern) == cc0_rtx
-         && compare_diff_p (insn))
+      if (compare_diff_p (insn))
        {
-         if (GET_CODE (SET_SRC (pattern)) == COMPARE)
-           {
-             /* Now we work under compare insn.  */
-             
-             pattern = SET_SRC (pattern);
-             if (true_regnum (XEXP (pattern,0)) >= 0
-                 && true_regnum (XEXP (pattern,1)) >= 0 )
-               {
-                 rtx x = XEXP (pattern,0);
-                 rtx next = next_real_insn (insn);
-                 rtx pat = PATTERN (next);
-                 rtx src = SET_SRC (pat);
-                 rtx t = XEXP (src,0);
-                 PUT_CODE (t, swap_condition (GET_CODE (t)));
-                 XEXP (pattern,0) = XEXP (pattern,1);
-                 XEXP (pattern,1) = x;
-                 INSN_CODE (next) = -1;
-               }
-             else if (true_regnum (XEXP (pattern, 0)) >= 0
-                      && XEXP (pattern, 1) == const0_rtx)
-               {
-                 /* This is a tst insn, we can reverse it.  */
-                 rtx next = next_real_insn (insn);
-                 rtx pat = PATTERN (next);
-                 rtx src = SET_SRC (pat);
-                 rtx t = XEXP (src,0);
+          /* Now we work under compare insn with difficult branch.  */
+          
+          rtx next = next_real_insn (insn);
+          rtx pat = PATTERN (next);
+
+          pattern = SET_SRC (pattern);
+          
+          if (true_regnum (XEXP (pattern, 0)) >= 0
+              && true_regnum (XEXP (pattern, 1)) >= 0)
+            {
+              rtx x = XEXP (pattern, 0);
+              rtx src = SET_SRC (pat);
+              rtx t = XEXP (src,0);
+              PUT_CODE (t, swap_condition (GET_CODE (t)));
+              XEXP (pattern, 0) = XEXP (pattern, 1);
+              XEXP (pattern, 1) = x;
+              INSN_CODE (next) = -1;
+            }
+          else if (true_regnum (XEXP (pattern, 0)) >= 0
+                   && XEXP (pattern, 1) == const0_rtx)
+            {
+              /* This is a tst insn, we can reverse it.  */
+              rtx src = SET_SRC (pat);
+              rtx t = XEXP (src,0);
     
-                 PUT_CODE (t, swap_condition (GET_CODE (t)));
-                 XEXP (pattern, 1) = XEXP (pattern, 0);
-                 XEXP (pattern, 0) = const0_rtx;
-                 INSN_CODE (next) = -1;
-                 INSN_CODE (insn) = -1;
-               }
-             else if (true_regnum (XEXP (pattern,0)) >= 0
-                      && GET_CODE (XEXP (pattern,1)) == CONST_INT)
-               {
-                 rtx x = XEXP (pattern,1);
-                 rtx next = next_real_insn (insn);
-                 rtx pat = PATTERN (next);
-                 rtx src = SET_SRC (pat);
-                 rtx t = XEXP (src,0);
-                 enum machine_mode mode = GET_MODE (XEXP (pattern, 0));
-
-                 if (avr_simplify_comparison_p (mode, GET_CODE (t), x))
-                   {
-                     XEXP (pattern, 1) = gen_int_mode (INTVAL (x) + 1, mode);
-                     PUT_CODE (t, avr_normalize_condition (GET_CODE (t)));
-                     INSN_CODE (next) = -1;
-                     INSN_CODE (insn) = -1;
-                   }
-               }
-           }
-       }
+              PUT_CODE (t, swap_condition (GET_CODE (t)));
+              XEXP (pattern, 1) = XEXP (pattern, 0);
+              XEXP (pattern, 0) = const0_rtx;
+              INSN_CODE (next) = -1;
+              INSN_CODE (insn) = -1;
+            }
+          else if (true_regnum (XEXP (pattern, 0)) >= 0
+                   && CONST_INT_P (XEXP (pattern, 1)))
+            {
+              rtx x = XEXP (pattern, 1);
+              rtx src = SET_SRC (pat);
+              rtx t = XEXP (src,0);
+              enum machine_mode mode = GET_MODE (XEXP (pattern, 0));
+              
+              if (avr_simplify_comparison_p (mode, GET_CODE (t), x))
+                {
+                  XEXP (pattern, 1) = gen_int_mode (INTVAL (x) + 1, mode);
+                  PUT_CODE (t, avr_normalize_condition (GET_CODE (t)));
+                  INSN_CODE (next) = -1;
+                  INSN_CODE (insn) = -1;
+                }
+            }
+        }
     }
 }
 
@@ -6054,8 +8729,10 @@ avr_libcall_value (enum machine_mode mode,
                   const_rtx func ATTRIBUTE_UNUSED)
 {
   int offs = GET_MODE_SIZE (mode);
-  if (offs < 2)
-    offs = 2;
+  
+  if (offs <= 4)
+    offs = (offs + 1) & ~1;
+  
   return gen_rtx_REG (mode, avr_ret_register () + 2 - offs);
 }
 
@@ -6097,6 +8774,53 @@ test_hard_reg_class (enum reg_class rclass, rtx x)
 }
 
 
+/* Helper for jump_over_one_insn_p:  Test if INSN is a 2-word instruction
+   and thus is suitable to be skipped by CPSE, SBRC, etc.  */
+
+static bool
+avr_2word_insn_p (rtx insn)
+{
+  if (avr_current_device->errata_skip
+      || !insn
+      || 2 != get_attr_length (insn))
+    {
+      return false;
+    }
+
+  switch (INSN_CODE (insn))
+    {
+    default:
+      return false;
+      
+    case CODE_FOR_movqi_insn:
+      {
+        rtx set  = single_set (insn);
+        rtx src  = SET_SRC (set);
+        rtx dest = SET_DEST (set);
+        
+        /* Factor out LDS and STS from movqi_insn.  */
+        
+        if (MEM_P (dest)
+            && (REG_P (src) || src == const0_rtx))
+          {
+            return CONSTANT_ADDRESS_P (XEXP (dest, 0));
+          }
+        else if (REG_P (dest)
+                 && MEM_P (src))
+          {
+            return CONSTANT_ADDRESS_P (XEXP (src, 0));
+          }
+        
+        return false;
+      }
+
+    case CODE_FOR_call_insn:
+    case CODE_FOR_call_value_insn:
+      return true;
+    }
+}
+
+
 int
 jump_over_one_insn_p (rtx insn, rtx dest)
 {
@@ -6105,7 +8829,11 @@ jump_over_one_insn_p (rtx insn, rtx dest)
                      : dest);
   int jump_addr = INSN_ADDRESSES (INSN_UID (insn));
   int dest_addr = INSN_ADDRESSES (uid);
-  return dest_addr - jump_addr == get_attr_length (insn) + 1;
+  int jump_offset = dest_addr - jump_addr - get_attr_length (insn);
+  
+  return (jump_offset == 1
+          || (jump_offset == 2
+              && avr_2word_insn_p (next_active_insn (insn))));
 }
 
 /* Returns 1 if a value of mode MODE can be stored starting with hard
@@ -6143,61 +8871,107 @@ avr_hard_regno_mode_ok (int regno, enum machine_mode mode)
   return !(regno & 1);
 }
 
-const char *
-output_reload_inhi (rtx insn ATTRIBUTE_UNUSED, rtx *operands, int *len)
+
+/* Implement `MODE_CODE_BASE_REG_CLASS'.  */
+
+reg_class_t
+avr_mode_code_base_reg_class (enum machine_mode mode ATTRIBUTE_UNUSED,
+                              addr_space_t as, RTX_CODE outer_code,
+                              RTX_CODE index_code ATTRIBUTE_UNUSED)
 {
-  int tmp;
-  if (!len)
-    len = &tmp;
+  if (!ADDR_SPACE_GENERIC_P (as))
+    {
+      return POINTER_Z_REGS;
+    }
+  if (!avr_strict_X)
+    return reload_completed ? BASE_POINTER_REGS : POINTER_REGS;
+
+  return PLUS == outer_code ? BASE_POINTER_REGS : POINTER_REGS;
+}
+
+
+/* Implement `REGNO_MODE_CODE_OK_FOR_BASE_P'.  */
+
+bool
+avr_regno_mode_code_ok_for_base_p (int regno,
+                                   enum machine_mode mode ATTRIBUTE_UNUSED,
+                                   addr_space_t as ATTRIBUTE_UNUSED,
+                                   RTX_CODE outer_code,
+                                   RTX_CODE index_code ATTRIBUTE_UNUSED)
+{
+  bool ok = false;
+  
+  if (!ADDR_SPACE_GENERIC_P (as))
+    {
+      if (regno < FIRST_PSEUDO_REGISTER
+          && regno == REG_Z)
+        {
+          return true;
+        }
+      
+      if (reg_renumber)
+        {
+          regno = reg_renumber[regno];
+          
+          if (regno == REG_Z)
+            {
+              return true;
+            }
+        }
       
-  if (GET_CODE (operands[1]) == CONST_INT)
+      return false;
+    }
+
+  if (regno < FIRST_PSEUDO_REGISTER
+      && (regno == REG_X
+          || regno == REG_Y
+          || regno == REG_Z
+          || regno == ARG_POINTER_REGNUM))
     {
-      int val = INTVAL (operands[1]);
-      if ((val & 0xff) == 0)
-       {
-         *len = 3;
-         return (AS2 (mov,%A0,__zero_reg__) CR_TAB
-                 AS2 (ldi,%2,hi8(%1))       CR_TAB
-                 AS2 (mov,%B0,%2));
-       }
-      else if ((val & 0xff00) == 0)
-       {
-         *len = 3;
-         return (AS2 (ldi,%2,lo8(%1)) CR_TAB
-                 AS2 (mov,%A0,%2)     CR_TAB
-                 AS2 (mov,%B0,__zero_reg__));
-       }
-      else if ((val & 0xff) == ((val & 0xff00) >> 8))
-       {
-         *len = 3;
-         return (AS2 (ldi,%2,lo8(%1)) CR_TAB
-                 AS2 (mov,%A0,%2)     CR_TAB
-                 AS2 (mov,%B0,%2));
-       }
+      ok = true;
+    }
+  else if (reg_renumber)
+    {
+      regno = reg_renumber[regno];
+
+      if (regno == REG_X
+          || regno == REG_Y
+          || regno == REG_Z
+          || regno == ARG_POINTER_REGNUM)
+        {
+          ok = true;
+        }
+    }
+
+  if (avr_strict_X
+      && PLUS == outer_code
+      && regno == REG_X)
+    {
+      ok = false;
     }
-  *len = 4;
-  return (AS2 (ldi,%2,lo8(%1)) CR_TAB
-         AS2 (mov,%A0,%2)     CR_TAB
-         AS2 (ldi,%2,hi8(%1)) CR_TAB
-         AS2 (mov,%B0,%2));
+
+  return ok;
 }
 
 
-/* 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.
+/* A helper for `output_reload_insisf' and `output_reload_inhi'.  */
+/* Set 32-bit register OP[0] to compile-time constant OP[1].
+   CLOBBER_REG is a QI clobber register or NULL_RTX.
+   LEN == NULL: output instructions.
+   LEN != NULL: set *LEN to the length of the instruction sequence
+                (in words) printed with LEN = NULL.
+   If CLEAR_P is true, OP[0] had been cleard to Zero already.
+   If CLEAR_P is false, nothing is known about OP[0].
 
-   LEN == NULL: Output instructions.
-   
-   LEN != NULL: Output nothing.  Increment *LEN by number of words occupied
-                by the insns printed.
+   The effect on cc0 is as follows:
 
-   Return "".  */
+   Load 0 to any register except ZERO_REG : NONE
+   Load ld register with any value        : NONE
+   Anything else:                         : CLOBBER  */
 
-const char *
-output_reload_insisf (rtx insn ATTRIBUTE_UNUSED,
-                      rtx *op, rtx clobber_reg, int *len)
+static void
+output_reload_in_const (rtx *op, rtx clobber_reg, int *len, bool clear_p)
 {
   rtx src = op[1];
   rtx dest = op[0];
@@ -6206,10 +8980,11 @@ output_reload_insisf (rtx insn ATTRIBUTE_UNUSED,
   int clobber_val = 1234;
   bool cooked_clobber_p = false;
   bool set_p = false;
-  unsigned int n;
   enum machine_mode mode = GET_MODE (dest);
+  int n, n_bytes = GET_MODE_SIZE (mode);
   
-  gcc_assert (REG_P (dest));
+  gcc_assert (REG_P (dest)
+              && CONSTANT_P (src));
 
   if (len)
     *len = 0;
@@ -6217,54 +8992,75 @@ output_reload_insisf (rtx insn ATTRIBUTE_UNUSED,
   /* (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 (REGNO (dest) < 16
+      && REGNO (dest) + GET_MODE_SIZE (mode) > 16)
     {
-      clobber_reg = gen_rtx_REG (QImode, 17);
+      clobber_reg = all_regs_rtx[REGNO (dest) + n_bytes - 1];
     }
 
-  /* 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.  */
+  /* 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 is a symbol
+     or 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))
+      && !test_hard_reg_class (LD_REGS, dest)
+      && (! (CONST_INT_P (src) || CONST_DOUBLE_P (src))
+          || !avr_popcount_each_byte (src, n_bytes,
+                                      (1 << 0) | (1 << 1) | (1 << 8))))
     {
-      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;
-            }
-        }
+      /* We have no clobber register but need one.  Cook one up.
+         That's cheaper than loading from constant pool.  */
+      
+      cooked_clobber_p = true;
+      clobber_reg = all_regs_rtx[REG_Z + 1];
+      avr_asm_len ("mov __tmp_reg__,%0", &clobber_reg, len, 1);
     }
 
   /* Now start filling DEST from LSB to MSB.  */
   
-  for (n = 0; n < GET_MODE_SIZE (mode); n++)
+  for (n = 0; n < n_bytes; n++)
     {
+      int ldreg_p;
       bool done_byte = false;
-      unsigned int j;
+      int j;
       rtx xop[3];
 
-      /* Crop the n-th sub-byte.  */
-      
-      xval = simplify_gen_subreg (QImode, src, mode, n);
+      /* Crop the n-th destination byte.  */
+
       xdest[n] = simplify_gen_subreg (QImode, dest, mode, n);
+      ldreg_p = test_hard_reg_class (LD_REGS, xdest[n]);
+
+      if (!CONST_INT_P (src)
+          && !CONST_DOUBLE_P (src))
+        {
+          static const char* const asm_code[][2] =
+            {
+              { "ldi %2,lo8(%1)"  CR_TAB "mov %0,%2",    "ldi %0,lo8(%1)"  },
+              { "ldi %2,hi8(%1)"  CR_TAB "mov %0,%2",    "ldi %0,hi8(%1)"  },
+              { "ldi %2,hlo8(%1)" CR_TAB "mov %0,%2",    "ldi %0,hlo8(%1)" },
+              { "ldi %2,hhi8(%1)" CR_TAB "mov %0,%2",    "ldi %0,hhi8(%1)" }
+            };
+          
+          xop[0] = xdest[n];
+          xop[1] = src;
+          xop[2] = clobber_reg;
+
+          if (n >= 2 + (avr_current_arch->n_segments > 1))
+            avr_asm_len ("mov %0,__zero_reg__", xop, len, 1);
+          else
+            avr_asm_len (asm_code[n][ldreg_p], xop, len, ldreg_p ? 1 : 2);
+          continue;
+        }
+
+      /* Crop the n-th source byte.  */
+
+      xval = simplify_gen_subreg (QImode, src, mode, n);
       ival[n] = INTVAL (xval);
 
       /* Look if we can reuse the low word by means of MOVW.  */
       
       if (n == 2
+          && n_bytes >= 4
           && AVR_HAVE_MOVW)
         {
           rtx lo16 = simplify_gen_subreg (HImode, src, mode, 0);
@@ -6272,17 +9068,25 @@ output_reload_insisf (rtx insn ATTRIBUTE_UNUSED,
 
           if (INTVAL (lo16) == INTVAL (hi16))
             {
-              avr_asm_len ("movw %C0,%A0", &op[0], len, 1);
+              if (0 != INTVAL (lo16)
+                  || !clear_p)
+                {
+                  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.  */
+      /* Don't use CLR so that cc0 is set as expected.  */
       
       if (ival[n] == 0)
         {
-          avr_asm_len ("clr %0", &xdest[n], len, 1);
+          if (!clear_p)
+            avr_asm_len (ldreg_p ? "ldi %0,0"
+                         : ZERO_REGNO == REGNO (xdest[n]) ? "clr %0"
+                         : "mov %0,__zero_reg__",
+                         &xdest[n], len, 1);
           continue;
         }
 
@@ -6294,7 +9098,7 @@ output_reload_insisf (rtx insn ATTRIBUTE_UNUSED,
 
       /* LD_REGS can use LDI to move a constant value */
       
-      if (test_hard_reg_class (LD_REGS, xdest[n]))
+      if (ldreg_p)
         {
           xop[0] = xdest[n];
           xop[1] = xval;
@@ -6322,8 +9126,18 @@ output_reload_insisf (rtx insn ATTRIBUTE_UNUSED,
       
       if (-1 == ival[n])
         {
-          avr_asm_len ("clr %0" CR_TAB
-                       "dec %0", &xdest[n], len, 2);
+          if (!clear_p)
+            avr_asm_len ("clr %0", &xdest[n], len, 1);
+          
+          avr_asm_len ("dec %0", &xdest[n], len, 1);
+          continue;
+        }
+      else if (1 == ival[n])
+        {
+          if (!clear_p)
+            avr_asm_len ("clr %0", &xdest[n], len, 1);
+          
+          avr_asm_len ("inc %0", &xdest[n], len, 1);
           continue;
         }
 
@@ -6333,13 +9147,6 @@ output_reload_insisf (rtx insn ATTRIBUTE_UNUSED,
       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)));
 
@@ -6351,31 +9158,118 @@ output_reload_insisf (rtx insn ATTRIBUTE_UNUSED,
               avr_asm_len ("set", xop, len, 1);
             }
 
-          avr_asm_len ("clr %0" CR_TAB
-                       "bld %0,%1", xop, len, 2);
-          continue;
+          if (!clear_p)
+            avr_asm_len ("clr %0", xop, len, 1);
+          
+          avr_asm_len ("bld %0,%1", xop, len, 1);
+          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 we cooked up a clobber reg above, restore it.  */
+  
+  if (cooked_clobber_p)
+    {
+      avr_asm_len ("mov %0,__tmp_reg__", &clobber_reg, len, 1);
+    }
+}
+
+
+/* Reload the constant OP[1] into the HI register OP[0].
+   CLOBBER_REG is a QI clobber reg needed to move vast majority of consts
+   into a NO_LD_REGS register.  If CLOBBER_REG is NULL_RTX we either don't
+   need a clobber reg or have to cook one up.
+
+   PLEN == NULL: Output instructions.
+   PLEN != NULL: Output nothing.  Set *PLEN to number of words occupied
+                 by the insns printed.
+
+   Return "".  */
+
+const char*
+output_reload_inhi (rtx *op, rtx clobber_reg, int *plen)
+{
+  output_reload_in_const (op, clobber_reg, plen, false);
+  return "";
+}
+
+
+/* Reload a SI or SF compile time constant OP[1] into the register OP[0].
+   CLOBBER_REG is a QI clobber reg needed to move vast majority of consts
+   into a NO_LD_REGS register.  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.  Set *LEN to number of words occupied
+                by the insns printed.
+
+   Return "".  */
+
+const char *
+output_reload_insisf (rtx *op, rtx clobber_reg, int *len)
+{
+  if (AVR_HAVE_MOVW
+      && !test_hard_reg_class (LD_REGS, op[0])
+      && (CONST_INT_P (op[1])
+          || CONST_DOUBLE_P (op[1])))
+    {
+      int len_clr, len_noclr;
+      
+      /* In some cases it is better to clear the destination beforehand, e.g.
+
+             CLR R2   CLR R3   MOVW R4,R2   INC R2
+
+         is shorther than
+
+             CLR R2   INC R2   CLR  R3      CLR R4   CLR R5
+
+         We find it too tedious to work that out in the print function.
+         Instead, we call the print function twice to get the lengths of
+         both methods and use the shortest one.  */
+         
+      output_reload_in_const (op, clobber_reg, &len_clr, true);
+      output_reload_in_const (op, clobber_reg, &len_noclr, false);
+      
+      if (len_noclr - len_clr == 4)
+        {
+          /* Default needs 4 CLR instructions: clear register beforehand.  */
+          
+          avr_asm_len ("mov %A0,__zero_reg__" CR_TAB
+                       "mov %B0,__zero_reg__" CR_TAB
+                       "movw %C0,%A0", &op[0], len, 3);
+          
+          output_reload_in_const (op, clobber_reg, len, true);
+          
+          if (len)
+            *len += 3;
+
+          return "";
         }
+    }
 
-      /* We actually need the LD_REGS clobber reg.  */
+  /* Default: destination not pre-cleared.  */
 
-      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 we cooked up a clobber reg above, restore it.  */
-  
-  if (cooked_clobber_p)
-    {
-      avr_asm_len ("mov %0,__tmp_reg__", &clobber_reg, len, 1);
-    }
-  
+  output_reload_in_const (op, clobber_reg, len, false);
+  return "";
+}
+
+const char *
+avr_out_reload_inpsi (rtx *op, rtx clobber_reg, int *len)
+{
+  output_reload_in_const (op, clobber_reg, len, false);
   return "";
 }
 
@@ -6392,7 +9286,6 @@ avr_output_bld (rtx operands[], int bit_nr)
 void
 avr_output_addr_vec_elt (FILE *stream, int value)
 {
-  switch_to_section (progmem_section);
   if (AVR_HAVE_JMP_CALL)
     fprintf (stream, "\t.word gs(.L%d)\n", value);
   else
@@ -6402,7 +9295,7 @@ avr_output_addr_vec_elt (FILE *stream, int value)
 /* Returns true if SCRATCH are safe to be allocated as a scratch
    registers (for a define_peephole2) in the current function.  */
 
-bool
+static bool
 avr_hard_regno_scratch_ok (unsigned int regno)
 {
   /* Interrupt functions can only use registers that have already been saved
@@ -6465,8 +9358,8 @@ const char *
 avr_out_sbxx_branch (rtx insn, rtx operands[])
 {
   enum rtx_code comp = GET_CODE (operands[0]);
-  int long_jump = (get_attr_length (insn) >= 4);
-  int reverse = long_jump || jump_over_one_insn_p (insn, operands[3]);
+  bool long_jump = get_attr_length (insn) >= 4;
+  bool reverse = long_jump || jump_over_one_insn_p (insn, operands[3]);
 
   if (comp == GE)
     comp = EQ;
@@ -6476,91 +9369,546 @@ avr_out_sbxx_branch (rtx insn, rtx operands[])
   if (reverse)
     comp = reverse_condition (comp);
 
-  if (GET_CODE (operands[1]) == CONST_INT)
+  switch (GET_CODE (operands[1]))
     {
-      if (INTVAL (operands[1]) < 0x40)
-       {
-         if (comp == EQ)
-           output_asm_insn (AS2 (sbis,%m1-0x20,%2), operands);
-         else
-           output_asm_insn (AS2 (sbic,%m1-0x20,%2), operands);
-       }
+    default:
+      gcc_unreachable();
+      
+    case CONST_INT:
+
+      if (low_io_address_operand (operands[1], QImode))
+        {
+          if (comp == EQ)
+            output_asm_insn ("sbis %i1,%2", operands);
+          else
+            output_asm_insn ("sbic %i1,%2", operands);
+        }
       else
-       {
-         output_asm_insn (AS2 (in,__tmp_reg__,%m1-0x20), operands);
-         if (comp == EQ)
-           output_asm_insn (AS2 (sbrs,__tmp_reg__,%2), operands);
-         else
-           output_asm_insn (AS2 (sbrc,__tmp_reg__,%2), operands);
-       }
+        {
+          output_asm_insn ("in __tmp_reg__,%i1", operands);
+          if (comp == EQ)
+            output_asm_insn ("sbrs __tmp_reg__,%2", operands);
+          else
+            output_asm_insn ("sbrc __tmp_reg__,%2", operands);
+        }
+
+      break; /* CONST_INT */
+
+    case REG:
+
+      if (GET_MODE (operands[1]) == QImode)
+        {
+          if (comp == EQ)
+            output_asm_insn ("sbrs %1,%2", operands);
+          else
+            output_asm_insn ("sbrc %1,%2", operands);
+        }
+      else  /* HImode, PSImode or SImode */
+        {
+          static char buf[] = "sbrc %A1,0";
+          unsigned int bit_nr = UINTVAL (operands[2]);
+
+          buf[3] = (comp == EQ) ? 's' : 'c';
+          buf[6] = 'A' + (bit_nr / 8);
+          buf[9] = '0' + (bit_nr % 8);
+          output_asm_insn (buf, operands);
+        }
+
+      break; /* REG */
+    }        /* switch */
+
+  if (long_jump)
+    return ("rjmp .+4" CR_TAB
+            "jmp %x3");
+
+  if (!reverse)
+    return "rjmp %x3";
+
+  return "";
+}
+
+/* Worker function for TARGET_ASM_CONSTRUCTOR.  */
+
+static void
+avr_asm_out_ctor (rtx symbol, int priority)
+{
+  fputs ("\t.global __do_global_ctors\n", asm_out_file);
+  default_ctor_section_asm_out_constructor (symbol, priority);
+}
+
+/* Worker function for TARGET_ASM_DESTRUCTOR.  */
+
+static void
+avr_asm_out_dtor (rtx symbol, int priority)
+{
+  fputs ("\t.global __do_global_dtors\n", asm_out_file);
+  default_dtor_section_asm_out_destructor (symbol, priority);
+}
+
+/* Worker function for TARGET_RETURN_IN_MEMORY.  */
+
+static bool
+avr_return_in_memory (const_tree type, const_tree fntype ATTRIBUTE_UNUSED)
+{
+  if (TYPE_MODE (type) == BLKmode)
+    {
+      HOST_WIDE_INT size = int_size_in_bytes (type);
+      return (size == -1 || size > 8);
+    }
+  else
+    return false;
+}
+
+/* Worker function for CASE_VALUES_THRESHOLD.  */
+
+static unsigned int
+avr_case_values_threshold (void)
+{
+  return (!AVR_HAVE_JMP_CALL || TARGET_CALL_PROLOGUES) ? 8 : 17;
+}
+
+
+/* Implement `TARGET_ADDR_SPACE_ADDRESS_MODE'.  */
+
+static enum machine_mode
+avr_addr_space_address_mode (addr_space_t as)
+{
+  return avr_addrspace[as].pointer_size == 3 ? PSImode : HImode;
+}
+
+
+/* Implement `TARGET_ADDR_SPACE_POINTER_MODE'.  */
+
+static enum machine_mode
+avr_addr_space_pointer_mode (addr_space_t as)
+{
+  return avr_addr_space_address_mode (as);
+}
+
+
+/* Helper for following function.  */
+
+static bool
+avr_reg_ok_for_pgm_addr (rtx reg, bool strict)
+{
+  gcc_assert (REG_P (reg));
+
+  if (strict)
+    {
+      return REGNO (reg) == REG_Z;
+    }
+  
+  /* Avoid combine to propagate hard regs.  */
+  
+  if (can_create_pseudo_p()
+      && REGNO (reg) < REG_Z)
+    {
+      return false;
+    }
+  
+  return true;
+}
+
+
+/* Implement `TARGET_ADDR_SPACE_LEGITIMATE_ADDRESS_P'.  */
+
+static bool
+avr_addr_space_legitimate_address_p (enum machine_mode mode, rtx x,
+                                     bool strict, addr_space_t as)
+{
+  bool ok = false;
+
+  switch (as)
+    {
+    default:
+      gcc_unreachable();
+      
+    case ADDR_SPACE_GENERIC:
+      return avr_legitimate_address_p (mode, x, strict);
+
+    case ADDR_SPACE_PGM:
+    case ADDR_SPACE_PGM1:
+    case ADDR_SPACE_PGM2:
+    case ADDR_SPACE_PGM3:
+    case ADDR_SPACE_PGM4:
+    case ADDR_SPACE_PGM5:
+
+      switch (GET_CODE (x))
+        {
+        case REG:
+          ok = avr_reg_ok_for_pgm_addr (x, strict);
+          break;
+          
+        case POST_INC:
+          ok = avr_reg_ok_for_pgm_addr (XEXP (x, 0), strict);
+          break;
+          
+        default:
+          break;
+        }
+
+      break; /* PGM */
+      
+    case ADDR_SPACE_PGMX:
+      if (REG_P (x))
+        ok = (!strict
+              && can_create_pseudo_p());
+
+      if (LO_SUM == GET_CODE (x))
+        {
+          rtx hi = XEXP (x, 0);
+          rtx lo = XEXP (x, 1);
+
+          ok = (REG_P (hi)
+                && (!strict || REGNO (hi) < FIRST_PSEUDO_REGISTER)
+                && REG_P (lo)
+                && REGNO (lo) == REG_Z);
+        }
+      
+      break; /* PGMX */
+    }
+
+  if (avr_log.legitimate_address_p)
+    {
+      avr_edump ("\n%?: ret=%b, mode=%m strict=%d "
+                 "reload_completed=%d reload_in_progress=%d %s:",
+                 ok, mode, strict, reload_completed, reload_in_progress,
+                 reg_renumber ? "(reg_renumber)" : "");
+      
+      if (GET_CODE (x) == PLUS
+          && REG_P (XEXP (x, 0))
+          && CONST_INT_P (XEXP (x, 1))
+          && IN_RANGE (INTVAL (XEXP (x, 1)), 0, MAX_LD_OFFSET (mode))
+          && reg_renumber)
+        {
+          avr_edump ("(r%d ---> r%d)", REGNO (XEXP (x, 0)),
+                     true_regnum (XEXP (x, 0)));
+        }
+      
+      avr_edump ("\n%r\n", x);
+    }
+
+  return ok;
+}
+
+
+/* Implement `TARGET_ADDR_SPACE_LEGITIMIZE_ADDRESS'.  */
+
+static rtx
+avr_addr_space_legitimize_address (rtx x, rtx old_x,
+                                   enum machine_mode mode, addr_space_t as)
+{
+  if (ADDR_SPACE_GENERIC_P (as))
+    return avr_legitimize_address (x, old_x, mode);
+
+  if (avr_log.legitimize_address)
+    {
+      avr_edump ("\n%?: mode=%m\n %r\n", mode, old_x);
+    }
+
+  return old_x;
+}
+
+
+/* Implement `TARGET_ADDR_SPACE_CONVERT'.  */
+
+static rtx
+avr_addr_space_convert (rtx src, tree type_from, tree type_to)
+{
+  addr_space_t as_from = TYPE_ADDR_SPACE (TREE_TYPE (type_from));
+  addr_space_t as_to = TYPE_ADDR_SPACE (TREE_TYPE (type_to));
+
+  if (avr_log.progmem)
+    avr_edump ("\n%!: op = %r\nfrom = %t\nto = %t\n",
+               src, type_from, type_to);
+
+  if (as_from != ADDR_SPACE_PGMX
+      && as_to == ADDR_SPACE_PGMX)
+    {
+      rtx new_src;
+      int n_segments = avr_current_arch->n_segments;
+      RTX_CODE code = GET_CODE (src);
+
+      if (CONST == code
+          && PLUS == GET_CODE (XEXP (src, 0))
+          && SYMBOL_REF == GET_CODE (XEXP (XEXP (src, 0), 0))
+          && CONST_INT_P (XEXP (XEXP (src, 0), 1)))
+        {
+          HOST_WIDE_INT offset = INTVAL (XEXP (XEXP (src, 0), 1));
+          const char *name = XSTR (XEXP (XEXP (src, 0), 0), 0);
+          
+          new_src = gen_rtx_SYMBOL_REF (PSImode, ggc_strdup (name));
+          new_src = gen_rtx_CONST (PSImode,
+                                   plus_constant (new_src, offset));
+          return new_src;
+        }
+
+      if (SYMBOL_REF == code)
+          {
+            const char *name = XSTR (src, 0);
+            
+            return gen_rtx_SYMBOL_REF (PSImode, ggc_strdup (name));
+          }
+      
+      src = force_reg (Pmode, src);
+      
+      if (ADDR_SPACE_GENERIC_P (as_from)
+          || as_from == ADDR_SPACE_PGM
+          || n_segments == 1)
+        {
+          return gen_rtx_ZERO_EXTEND (PSImode, src);
+        }
+      else
+        {
+          int segment = avr_addrspace[as_from].segment % n_segments;
+
+          new_src = gen_reg_rtx (PSImode);
+          emit_insn (gen_n_extendhipsi2 (new_src, GEN_INT (segment), src));
+
+          return new_src;
+        }
+    }
+  
+  return src;
+}
+
+
+/* Implement `TARGET_ADDR_SPACE_SUBSET_P'.  */
+
+static bool
+avr_addr_space_subset_p (addr_space_t subset, addr_space_t superset)
+{
+  if (subset == ADDR_SPACE_PGMX
+      && superset != ADDR_SPACE_PGMX)
+    {
+      return false;
+    }
+  
+  return true;
+}
+
+
+/* Worker function for movmemhi insn.
+   XOP[0]  Destination as MEM:BLK
+   XOP[1]  Source      "     "
+   XOP[2]  # Bytes to copy
+
+   Return TRUE  if the expansion is accomplished.
+   Return FALSE if the operand compination is not supported.  */
+
+bool
+avr_emit_movmemhi (rtx *xop)
+{
+  HOST_WIDE_INT count;
+  enum machine_mode loop_mode;
+  addr_space_t as = MEM_ADDR_SPACE (xop[1]);
+  rtx loop_reg, addr0, addr1, a_src, a_dest, insn, xas, reg_x;
+  rtx a_hi8 = NULL_RTX;
+
+  if (avr_mem_pgm_p (xop[0]))
+    return false;
+
+  if (!CONST_INT_P (xop[2]))
+    return false;
+
+  count = INTVAL (xop[2]);
+  if (count <= 0)
+    return false;
+
+  a_src  = XEXP (xop[1], 0);
+  a_dest = XEXP (xop[0], 0);
+
+  /* See if constant fits in 8 bits.  */
+
+  loop_mode = (count <= 0x100) ? QImode : HImode;
+
+  if (PSImode == GET_MODE (a_src))
+    {
+      addr1 = simplify_gen_subreg (HImode, a_src, PSImode, 0);
+      a_hi8 = simplify_gen_subreg (QImode, a_src, PSImode, 2);
+    }
+  else
+    {
+      int segment = avr_addrspace[as].segment % avr_current_arch->n_segments;
+      
+      addr1 = a_src;
+
+      if (segment)
+        a_hi8 = GEN_INT (segment);
+    }
+
+  if (a_hi8
+      && avr_current_arch->n_segments > 1)
+    {
+      emit_move_insn (rampz_rtx, a_hi8 = copy_to_mode_reg (QImode, a_hi8));
+    }
+  else if (!ADDR_SPACE_GENERIC_P (as))
+    {
+      as = ADDR_SPACE_PGM;
+    }
+
+  xas = GEN_INT (as);
+
+  /* Create loop counter register */
+
+  loop_reg = copy_to_mode_reg (loop_mode, gen_int_mode (count, loop_mode));
+
+  /* Copy pointers into new pseudos - they will be changed */
+
+  addr0 = copy_to_mode_reg (HImode, a_dest);
+  addr1 = copy_to_mode_reg (HImode, addr1);
+
+  /* FIXME: Register allocator might come up with spill fails if it is left
+        on its own.  Thus, we allocate the pointer registers by hand.  */
+
+  emit_move_insn (lpm_addr_reg_rtx, addr1);
+  addr1 = lpm_addr_reg_rtx;
+
+  reg_x = gen_rtx_REG (HImode, REG_X);
+  emit_move_insn (reg_x, addr0);
+  addr0 = reg_x;
+
+  /* FIXME: Register allocator does a bad job and might spill address
+        register(s) inside the loop leading to additional move instruction
+        to/from stack which could clobber tmp_reg.  Thus, do *not* emit
+        load and store as seperate insns.  Instead, we perform the copy
+        by means of one monolithic insn.  */
+
+  if (ADDR_SPACE_GENERIC_P (as))
+    {
+      rtx (*fun) (rtx, rtx, rtx, rtx, rtx, rtx, rtx, rtx)
+        = QImode == loop_mode ? gen_movmem_qi : gen_movmem_hi;
+
+      insn = fun (addr0, addr1, xas, loop_reg,
+                  addr0, addr1, tmp_reg_rtx, loop_reg);
     }
-  else  /* GET_CODE (operands[1]) == REG */
+  else if (as == ADDR_SPACE_PGM)
     {
-      if (GET_MODE (operands[1]) == QImode)
-       {
-         if (comp == EQ)
-           output_asm_insn (AS2 (sbrs,%1,%2), operands);
-         else
-           output_asm_insn (AS2 (sbrc,%1,%2), operands);
-       }
-      else  /* HImode or SImode */
-       {
-         static char buf[] = "sbrc %A1,0";
-         int bit_nr = INTVAL (operands[2]);
-         buf[3] = (comp == EQ) ? 's' : 'c';
-         buf[6] = 'A' + (bit_nr >> 3);
-         buf[9] = '0' + (bit_nr & 7);
-         output_asm_insn (buf, operands);
-       }
+      rtx (*fun) (rtx, rtx, rtx, rtx, rtx, rtx, rtx, rtx)
+        = QImode == loop_mode ? gen_movmem_qi : gen_movmem_hi;
+
+      insn = fun (addr0, addr1, xas, loop_reg, addr0, addr1,
+                  AVR_HAVE_LPMX ? tmp_reg_rtx : lpm_reg_rtx, loop_reg);
+    }
+  else
+    {
+      rtx (*fun) (rtx, rtx, rtx, rtx, rtx, rtx, rtx, rtx, rtx, rtx, rtx)
+        = QImode == loop_mode ? gen_movmem_qi_elpm : gen_movmem_hi_elpm;
+      
+      insn = fun (addr0, addr1, xas, loop_reg, addr0, addr1,
+                  AVR_HAVE_ELPMX ? tmp_reg_rtx : lpm_reg_rtx, loop_reg,
+                  a_hi8, a_hi8, GEN_INT (RAMPZ_ADDR));
     }
 
-  if (long_jump)
-    return (AS1 (rjmp,.+4) CR_TAB
-           AS1 (jmp,%x3));
-  if (!reverse)
-    return AS1 (rjmp,%x3);
-  return "";
+  set_mem_addr_space (SET_SRC (XVECEXP (insn, 0, 0)), as);
+  emit_insn (insn);
+
+  return true;
 }
 
-/* Worker function for TARGET_ASM_CONSTRUCTOR.  */
 
-static void
-avr_asm_out_ctor (rtx symbol, int priority)
-{
-  fputs ("\t.global __do_global_ctors\n", asm_out_file);
-  default_ctor_section_asm_out_constructor (symbol, priority);
-}
+/* Print assembler for movmem_qi, movmem_hi insns...
+       $0, $4 : & dest
+       $1, $5 : & src
+       $2     : Address Space
+       $3, $7 : Loop register
+       $6     : Scratch register
 
-/* Worker function for TARGET_ASM_DESTRUCTOR.  */
+   ...and movmem_qi_elpm, movmem_hi_elpm insns.
+   
+       $8, $9 : hh8 (& src)
+       $10    : RAMPZ_ADDR
+*/
 
-static void
-avr_asm_out_dtor (rtx symbol, int priority)
+const char*
+avr_out_movmem (rtx insn ATTRIBUTE_UNUSED, rtx *xop, int *plen)
 {
-  fputs ("\t.global __do_global_dtors\n", asm_out_file);
-  default_dtor_section_asm_out_destructor (symbol, priority);
-}
+  addr_space_t as = (addr_space_t) INTVAL (xop[2]);
+  enum machine_mode loop_mode = GET_MODE (xop[3]);
 
-/* Worker function for TARGET_RETURN_IN_MEMORY.  */
+  bool sbiw_p = test_hard_reg_class (ADDW_REGS, xop[3]);
 
-static bool
-avr_return_in_memory (const_tree type, const_tree fntype ATTRIBUTE_UNUSED)
-{
-  if (TYPE_MODE (type) == BLKmode)
+  gcc_assert (REG_X == REGNO (xop[0])
+              && REG_Z == REGNO (xop[1]));
+
+  if (plen)
+    *plen = 0;
+
+  /* Loop label */
+
+  avr_asm_len ("0:", xop, plen, 0);
+
+  /* Load with post-increment */
+
+  switch (as)
     {
-      HOST_WIDE_INT size = int_size_in_bytes (type);
-      return (size == -1 || size > 8);
+    default:
+      gcc_unreachable();
+      
+    case ADDR_SPACE_GENERIC:
+
+      avr_asm_len ("ld %6,%a1+", xop, plen, 1);
+      break;
+      
+    case ADDR_SPACE_PGM:
+
+      if (AVR_HAVE_LPMX)
+        avr_asm_len ("lpm %6,%a1+", xop, plen, 1);
+      else
+        avr_asm_len ("lpm" CR_TAB
+                     "adiw %1,1", xop, plen, 2);
+      break;
+      
+    case ADDR_SPACE_PGM1:
+    case ADDR_SPACE_PGM2:
+    case ADDR_SPACE_PGM3:
+    case ADDR_SPACE_PGM4:
+    case ADDR_SPACE_PGM5:
+    case ADDR_SPACE_PGMX:
+
+      if (AVR_HAVE_ELPMX)
+        avr_asm_len ("elpm %6,%a1+", xop, plen, 1);
+      else
+        avr_asm_len ("elpm" CR_TAB
+                     "adiw %1,1", xop, plen, 2);
+      
+      if (as == ADDR_SPACE_PGMX
+          && !AVR_HAVE_ELPMX)
+        {
+          avr_asm_len ("adc %8,__zero_reg__" CR_TAB
+                       "out __RAMPZ__,%8", xop, plen, 2);
+        }
+      
+      break;
     }
-  else
-    return false;
-}
 
-/* Worker function for CASE_VALUES_THRESHOLD.  */
+  /* Store with post-increment */
 
-unsigned int avr_case_values_threshold (void)
-{
-  return (!AVR_HAVE_JMP_CALL || TARGET_CALL_PROLOGUES) ? 8 : 17;
+  avr_asm_len ("st %a0+,%6", xop, plen, 1);
+
+  /* Decrement loop-counter and set Z-flag */
+
+  if (QImode == loop_mode)
+    {
+      avr_asm_len ("dec %3", xop, plen, 1);
+    }
+  else if (sbiw_p)
+    {
+      avr_asm_len ("sbiw %3,1", xop, plen, 1);
+    }
+  else
+    {
+      avr_asm_len ("subi %A3,1" CR_TAB
+                   "sbci %B3,0", xop, plen, 2);
+    }
+
+  /* Loop until zero */
+  
+  return avr_asm_len ("brne 0b", xop, plen, 1);
 }
 
+
+\f
 /* Helper for __builtin_avr_delay_cycles */
 
 static void
@@ -6621,6 +9969,345 @@ avr_expand_delay_cycles (rtx operands0)
     }
 }
 
+
+/* Return VAL * BASE + DIGIT.  BASE = 0 is shortcut for BASE = 2^{32}   */
+
+static double_int
+avr_double_int_push_digit (double_int val, int base,
+                           unsigned HOST_WIDE_INT digit)
+{
+  val = 0 == base
+    ? double_int_lshift (val, 32, 64, false)
+    : double_int_mul (val, uhwi_to_double_int (base));
+  
+  return double_int_add (val, uhwi_to_double_int (digit));
+}
+
+
+/* Compute the image of x under f, i.e. perform   x --> f(x)    */
+
+static int
+avr_map (double_int f, int x)
+{
+  return 0xf & double_int_to_uhwi (double_int_rshift (f, 4*x, 64, false));
+}
+
+
+/* Return the map R that reverses the bits of byte B.
+
+   R(0)  =  (0  7)  o  (1  6)  o   (2  5)  o   (3  4)
+   R(1)  =  (8 15)  o  (9 14)  o  (10 13)  o  (11 12)
+            
+   Notice that R o R = id.  */
+
+static double_int
+avr_revert_map (int b)
+{
+  int i;
+  double_int r = double_int_zero;
+
+  for (i = 16-1; i >= 0; i--)
+    r = avr_double_int_push_digit (r, 16, i >> 3 == b ? i ^ 7 : i);
+
+  return r;
+}
+
+
+/* Return the map R that swaps bit-chunks of size SIZE in byte B.
+
+   R(1,0)  =  (0 1)  o   (2  3)  o   (4  5)  o   (6  7)
+   R(1,1)  =  (8 9)  o  (10 11)  o  (12 13)  o  (14 15)
+
+   R(4,0)  =  (0  4)  o  (1  5)  o   (2  6)  o   (3  7)
+   R(4,1)  =  (8 12)  o  (9 13)  o  (10 14)  o  (11 15)
+
+   Notice that R o R = id.  */
+
+static double_int
+avr_swap_map (int size, int b)
+{
+  int i;
+  double_int r = double_int_zero;
+
+  for (i = 16-1; i >= 0; i--)
+    r = avr_double_int_push_digit (r, 16, i ^ (i >> 3 == b ? size : 0));
+
+  return r;
+}
+
+
+/* Return Identity.  */
+
+static double_int
+avr_id_map (void)
+{
+  int i;
+  double_int r = double_int_zero;
+
+  for (i = 16-1; i >= 0; i--)
+    r = avr_double_int_push_digit (r, 16, i);
+
+  return r;
+}
+
+
+enum
+  {
+    SIG_ID        = 0,
+    /* for QI and HI */
+    SIG_ROL       = 0xf,
+    SIG_REVERT_0  = 1 << 4,
+    SIG_SWAP1_0   = 1 << 5,
+    /* HI only */
+    SIG_REVERT_1  = 1 << 6,
+    SIG_SWAP1_1   = 1 << 7,
+    SIG_SWAP4_0   = 1 << 8,
+    SIG_SWAP4_1   = 1 << 9
+  };
+
+
+/* Return basic map with signature SIG.  */
+
+static double_int
+avr_sig_map (int n ATTRIBUTE_UNUSED, int sig)
+{
+  if (sig == SIG_ID)            return avr_id_map ();
+  else if (sig == SIG_REVERT_0) return avr_revert_map (0);
+  else if (sig == SIG_REVERT_1) return avr_revert_map (1);
+  else if (sig == SIG_SWAP1_0)  return avr_swap_map (1, 0);
+  else if (sig == SIG_SWAP1_1)  return avr_swap_map (1, 1);
+  else if (sig == SIG_SWAP4_0)  return avr_swap_map (4, 0);
+  else if (sig == SIG_SWAP4_1)  return avr_swap_map (4, 1);
+  else
+    gcc_unreachable();
+}
+
+
+/* Return the Hamming distance between the B-th byte of A and C.  */
+
+static bool
+avr_map_hamming_byte (int n, int b, double_int a, double_int c, bool strict)
+{
+  int i, hamming = 0;
+
+  for (i = 8*b; i < n && i < 8*b + 8; i++)
+    {
+      int ai = avr_map (a, i);
+      int ci = avr_map (c, i);
+
+      hamming += ai != ci && (strict || (ai < n && ci < n));
+    }
+  
+  return hamming;
+}
+
+
+/* Return the non-strict Hamming distance between A and B.  */
+
+#define avr_map_hamming_nonstrict(N,A,B)              \
+  (+ avr_map_hamming_byte (N, 0, A, B, false)         \
+   + avr_map_hamming_byte (N, 1, A, B, false))
+
+
+/* Return TRUE iff A and B represent the same mapping.  */
+
+#define avr_map_equal_p(N,A,B) (0 == avr_map_hamming_nonstrict (N, A, B))
+
+
+/* Return TRUE iff A is a map of signature S.  Notice that there is no
+   1:1 correspondance between maps and signatures and thus this is
+   only supported for basic signatures recognized by avr_sig_map().  */
+
+#define avr_map_sig_p(N,A,S) avr_map_equal_p (N, A, avr_sig_map (N, S))
+
+
+/* Swap odd/even bits of ld-reg %0:  %0 = bit-swap (%0)  */
+
+static const char*
+avr_out_swap_bits (rtx *xop, int *plen)
+{
+  xop[1] = tmp_reg_rtx;
+  
+  return avr_asm_len ("mov %1,%0"    CR_TAB
+                      "andi %0,0xaa" CR_TAB
+                      "eor %1,%0"    CR_TAB
+                      "lsr %0"       CR_TAB
+                      "lsl %1"       CR_TAB
+                      "or %0,%1", xop, plen, 6);
+}
+
+/* Revert bit order:  %0 = Revert (%1) with %0 != %1 and clobber %1  */
+
+static const char*
+avr_out_revert_bits (rtx *xop, int *plen)
+{
+  return avr_asm_len ("inc __zero_reg__" "\n"
+                      "0:\tror %1"       CR_TAB
+                      "rol %0"           CR_TAB
+                      "lsl __zero_reg__" CR_TAB
+                      "brne 0b", xop, plen, 5);
+}
+
+
+/* If OUT_P = true:  Output BST/BLD instruction according to MAP.
+   If OUT_P = false: Just dry-run and fix XOP[1] to resolve
+                     early-clobber conflicts if XOP[0] = XOP[1].  */
+
+static void
+avr_move_bits (rtx *xop, double_int map, int n_bits, bool out_p, int *plen)
+{
+  int bit_dest, b, clobber = 0;
+
+  /* T-flag contains this bit of the source, i.e. of XOP[1]  */
+  int t_bit_src = -1;
+
+  if (!optimize && !out_p)
+    {
+      avr_asm_len ("mov __tmp_reg__,%1", xop, plen, 1);
+      xop[1] = tmp_reg_rtx;
+      return;
+    }
+  
+  /* We order the operations according to the requested source bit b.  */
+  
+  for (b = 0; b < n_bits; b++)
+    for (bit_dest = 0; bit_dest < n_bits; bit_dest++)
+      {
+        int bit_src = avr_map (map, bit_dest);
+        
+        if (b != bit_src
+            /* Same position: No need to copy as the caller did MOV.  */
+            || bit_dest == bit_src
+            /* Accessing bits 8..f for 8-bit version is void. */
+            || bit_src >= n_bits)
+          continue;
+
+        if (t_bit_src != bit_src)
+          {
+            /* Source bit is not yet in T: Store it to T.  */
+              
+            t_bit_src = bit_src;
+
+            if (out_p)
+              {
+                xop[2] = GEN_INT (bit_src);
+                avr_asm_len ("bst %T1%T2", xop, plen, 1);
+              }
+            else if (clobber & (1 << bit_src))
+              {
+                /* Bit to be read was written already: Backup input
+                   to resolve early-clobber conflict.  */
+               
+                avr_asm_len ("mov __tmp_reg__,%1", xop, plen, 1);
+                xop[1] = tmp_reg_rtx;
+                return;
+              }
+          }
+
+        /* Load destination bit with T.  */
+        
+        if (out_p)
+          {
+            xop[2] = GEN_INT (bit_dest);
+            avr_asm_len ("bld %T0%T2", xop, plen, 1);
+          }
+        
+        clobber |= 1 << bit_dest;
+      }
+}
+
+
+/* Print assembler code for `map_bitsqi' and `map_bitshi'.  */
+
+const char*
+avr_out_map_bits (rtx insn, rtx *operands, int *plen)
+{
+  bool copy_0, copy_1;
+  int n_bits = GET_MODE_BITSIZE (GET_MODE (operands[0]));
+  double_int map = rtx_to_double_int (operands[1]);
+  rtx xop[3];
+
+  xop[0] = operands[0];
+  xop[1] = operands[2];
+
+  if (plen)
+    *plen = 0;
+  else if (flag_print_asm_name)
+    avr_fdump (asm_out_file, ASM_COMMENT_START "%X\n", map);
+
+  switch (n_bits)
+    {
+    default:
+      gcc_unreachable();
+      
+    case 8:
+      if (avr_map_sig_p (n_bits, map, SIG_SWAP1_0))
+        {
+          return avr_out_swap_bits (xop, plen);
+        }
+      else if (avr_map_sig_p (n_bits, map, SIG_REVERT_0))
+        {
+          if (REGNO (xop[0]) == REGNO (xop[1])
+              || !reg_unused_after (insn, xop[1]))
+            {
+              avr_asm_len ("mov __tmp_reg__,%1", xop, plen, 1);
+              xop[1] = tmp_reg_rtx;
+            }
+          
+          return avr_out_revert_bits (xop, plen);
+        }
+      
+      break; /* 8 */
+
+    case 16:
+      
+      break; /* 16 */
+    }
+
+  /* Copy whole byte is cheaper than moving bits that stay at the same
+     position.  Some bits in a byte stay at the same position iff the
+     strict Hamming distance to Identity is not 8.  */
+
+  copy_0 = 8 != avr_map_hamming_byte (n_bits, 0, map, avr_id_map(), true);
+  copy_1 = 8 != avr_map_hamming_byte (n_bits, 1, map, avr_id_map(), true);
+     
+  /* Perform the move(s) just worked out.  */
+
+  if (n_bits == 8)
+    {
+      if (REGNO (xop[0]) == REGNO (xop[1]))
+        {
+          /* Fix early-clobber clashes.
+             Notice XOP[0] hat no eary-clobber in its constraint.  */
+          
+          avr_move_bits (xop, map, n_bits, false, plen);
+        }
+      else if (copy_0)
+        {
+          avr_asm_len ("mov %0,%1", xop, plen, 1);
+        }
+    }
+  else if (AVR_HAVE_MOVW && copy_0 && copy_1)
+    {
+      avr_asm_len ("movw %A0,%A1", xop, plen, 1);
+    }
+  else
+    {
+      if (copy_0)
+        avr_asm_len ("mov %A0,%A1", xop, plen, 1);
+
+      if (copy_1)
+        avr_asm_len ("mov %B0,%B1", xop, plen, 1);
+    }
+
+  /* Move individual bits.  */
+
+  avr_move_bits (xop, map, n_bits, true, plen);
+  
+  return "";
+}
+
+
 /* IDs for all the AVR builtins.  */
 
 enum avr_builtin_id
@@ -6631,12 +10318,24 @@ enum avr_builtin_id
     AVR_BUILTIN_WDR,
     AVR_BUILTIN_SLEEP,
     AVR_BUILTIN_SWAP,
+    AVR_BUILTIN_MAP8,
+    AVR_BUILTIN_MAP16,
     AVR_BUILTIN_FMUL,
     AVR_BUILTIN_FMULS,
     AVR_BUILTIN_FMULSU,
     AVR_BUILTIN_DELAY_CYCLES
   };
 
+static void
+avr_init_builtin_int24 (void)
+{
+  tree int24_type  = make_signed_type (GET_MODE_BITSIZE (PSImode));
+  tree uint24_type = make_unsigned_type (GET_MODE_BITSIZE (PSImode));
+
+  (*lang_hooks.types.register_builtin_type) (int24_type, "__int24");
+  (*lang_hooks.types.register_builtin_type) (uint24_type, "__uint24");
+}
+
 #define DEF_BUILTIN(NAME, TYPE, CODE)                                   \
   do                                                                    \
     {                                                                   \
@@ -6677,6 +10376,18 @@ avr_init_builtins (void)
                                 long_unsigned_type_node,
                                 NULL_TREE);
 
+  tree uchar_ftype_ulong_uchar
+    = build_function_type_list (unsigned_char_type_node,
+                                long_unsigned_type_node,
+                                unsigned_char_type_node,
+                                NULL_TREE);
+
+  tree uint_ftype_ullong_uint
+    = build_function_type_list (unsigned_type_node,
+                                long_long_unsigned_type_node,
+                                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);
@@ -6692,6 +10403,13 @@ avr_init_builtins (void)
                AVR_BUILTIN_FMULS);
   DEF_BUILTIN ("__builtin_avr_fmulsu", int_ftype_char_uchar, 
                AVR_BUILTIN_FMULSU);
+
+  DEF_BUILTIN ("__builtin_avr_map8", uchar_ftype_ulong_uchar, 
+               AVR_BUILTIN_MAP8);
+  DEF_BUILTIN ("__builtin_avr_map16", uint_ftype_ullong_uint, 
+               AVR_BUILTIN_MAP16);
+
+  avr_init_builtin_int24 ();
 }
 
 #undef DEF_BUILTIN
@@ -6714,7 +10432,9 @@ 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 }
+    { CODE_FOR_fmulsu, "__builtin_avr_fmulsu", AVR_BUILTIN_FMULSU },
+    { CODE_FOR_map_bitsqi, "__builtin_avr_map8", AVR_BUILTIN_MAP8 },
+    { CODE_FOR_map_bitshi, "__builtin_avr_map16", AVR_BUILTIN_MAP16 }
   };
 
 /* Subroutine of avr_expand_builtin to take care of unop insns.  */
@@ -6830,6 +10550,7 @@ avr_expand_builtin (tree exp, rtx target,
   size_t i;
   const struct avr_builtin_description *d;
   tree fndecl = TREE_OPERAND (CALL_EXPR_FN (exp), 0);
+  const char* bname = IDENTIFIER_POINTER (DECL_NAME (fndecl));
   unsigned int id = DECL_FUNCTION_CODE (fndecl);
   tree arg0;
   rtx op0;
@@ -6862,11 +10583,37 @@ avr_expand_builtin (tree exp, rtx target,
         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.");
+          error ("%s expects a compile time integer constant", bname);
 
         avr_expand_delay_cycles (op0);
         return 0;
       }
+
+    case AVR_BUILTIN_MAP8:
+      {
+        arg0 = CALL_EXPR_ARG (exp, 0);
+        op0 = expand_expr (arg0, NULL_RTX, VOIDmode, EXPAND_NORMAL);
+
+        if (!CONST_INT_P (op0))
+          {
+            error ("%s expects a compile time long integer constant"
+                   " as first argument", bname);
+            return target;
+          }
+      }
+
+    case AVR_BUILTIN_MAP16:
+      {
+        arg0 = CALL_EXPR_ARG (exp, 0);
+        op0 = expand_expr (arg0, NULL_RTX, VOIDmode, EXPAND_NORMAL);
+
+        if (!const_double_operand (op0, VOIDmode))
+          {
+            error ("%s expects a compile time long long integer constant"
+                   " as first argument", bname);
+            return target;
+          }
+      }
     }
 
   for (i = 0, d = bdesc_1arg; i < ARRAY_SIZE (bdesc_1arg); i++, d++)
@@ -6880,5 +10627,6 @@ avr_expand_builtin (tree exp, rtx target,
   gcc_unreachable ();
 }
 
+struct gcc_target targetm = TARGET_INITIALIZER;
 
 #include "gt-avr.h"