OSDN Git Service

* common.opt (flag_stack_usage_info): New variable.
[pf3gnuchains/gcc-fork.git] / gcc / config / avr / avr.c
index d185a25..5a565b0 100644 (file)
@@ -1,7 +1,7 @@
 /* Subroutines for insn-output.c for ATMEL AVR micro controllers
-   Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004, 2005, 2006, 2007, 2008
-   Free Software Foundation, Inc.
-   Contributed by Denis Chertykov (denisc@overta.ru)
+   Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004, 2005, 2006, 2007, 2008,
+   2009, 2010, 2011 Free Software Foundation, Inc.
+   Contributed by Denis Chertykov (chertykov@gmail.com)
 
    This file is part of GCC.
 
 #include "rtl.h"
 #include "regs.h"
 #include "hard-reg-set.h"
-#include "real.h"
 #include "insn-config.h"
 #include "conditions.h"
 #include "insn-attr.h"
+#include "insn-codes.h"
 #include "flags.h"
 #include "reload.h"
 #include "tree.h"
 #include "output.h"
 #include "expr.h"
-#include "toplev.h"
+#include "diagnostic-core.h"
 #include "obstack.h"
 #include "function.h"
 #include "recog.h"
+#include "optabs.h"
 #include "ggc.h"
+#include "langhooks.h"
 #include "tm_p.h"
 #include "target.h"
 #include "target-def.h"
+#include "params.h"
 #include "df.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);
 static int avr_naked_function_p (tree);
 static int interrupt_function_p (tree);
 static int signal_function_p (tree);
@@ -58,19 +62,24 @@ static int get_sequence_length (rtx insns);
 static int sequent_regs_live (void);
 static const char *ptrreg_to_str (int);
 static const char *cond_string (enum rtx_code);
-static int avr_num_arg_regs (enum machine_mode, tree);
+static int avr_num_arg_regs (enum machine_mode, const_tree);
 
 static RTX_CODE compare_condition (rtx insn);
+static rtx avr_legitimize_address (rtx, rtx, enum machine_mode);
 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 *);
-const struct attribute_spec avr_attribute_table[];
 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);
@@ -78,12 +87,28 @@ static unsigned int avr_section_type_flags (tree, const char *, int);
 static void avr_reorg (void);
 static void avr_asm_out_ctor (rtx, int);
 static void avr_asm_out_dtor (rtx, int);
-static int avr_operand_rtx_cost (rtx, enum machine_mode, enum rtx_code);
-static bool avr_rtx_costs (rtx, int, int, int *);
-static int avr_address_cost (rtx);
+static int avr_register_move_cost (enum machine_mode, reg_class_t, reg_class_t);
+static int avr_memory_move_cost (enum machine_mode, reg_class_t, bool);
+static int avr_operand_rtx_cost (rtx, enum machine_mode, enum rtx_code, bool);
+static bool avr_rtx_costs (rtx, int, int, int *, bool);
+static int avr_address_cost (rtx, bool);
 static bool avr_return_in_memory (const_tree, const_tree);
 static struct machine_function * avr_init_machine_status (void);
+static void avr_init_builtins (void);
+static rtx avr_expand_builtin (tree, rtx, rtx, enum machine_mode, int);
 static rtx avr_builtin_setjmp_frame_value (void);
+static bool avr_hard_regno_scratch_ok (unsigned int);
+static unsigned int avr_case_values_threshold (void);
+static bool avr_frame_pointer_required_p (void);
+static bool avr_can_eliminate (const int, const int);
+static bool avr_class_likely_spilled_p (reg_class_t c);
+static rtx avr_function_arg (CUMULATIVE_ARGS *, enum machine_mode,
+                            const_tree, bool);
+static void avr_function_arg_advance (CUMULATIVE_ARGS *, enum machine_mode,
+                                     const_tree, bool);
+static void avr_help (void);
+static bool avr_function_ok_for_sibcall (tree, tree);
+static void avr_asm_named_section (const char *name, unsigned int flags, tree decl);
 
 /* Allocate registers from r25 to r8 for parameters for function calls.  */
 #define FIRST_CUM_REG 26
@@ -97,191 +122,47 @@ static GTY(()) rtx zero_reg_rtx;
 /* AVR register names {"r0", "r1", ..., "r31"} */
 static const char *const avr_regnames[] = REGISTER_NAMES;
 
-/* This holds the last insn address.  */
-static int last_insn_address = 0;
-
 /* Preprocessor macros to define depending on MCU type.  */
 const char *avr_extra_arch_macro;
 
 /* Current architecture.  */
 const struct base_arch_s *avr_current_arch;
 
-section *progmem_section;
-
-static const struct base_arch_s avr_arch_types[] = {
-  { 1, 0, 0, 0, 0, 0, 0, 0, NULL },  /* unknown device specified */
-  { 1, 0, 0, 0, 0, 0, 0, 0, "__AVR_ARCH__=1"   },
-  { 0, 0, 0, 0, 0, 0, 0, 0, "__AVR_ARCH__=2"   },
-  { 0, 0, 0, 1, 0, 0, 0, 0, "__AVR_ARCH__=25"  },
-  { 0, 0, 1, 0, 0, 0, 0, 0, "__AVR_ARCH__=3"   },
-  { 0, 0, 1, 0, 1, 0, 0, 0, "__AVR_ARCH__=31"  },
-  { 0, 0, 1, 1, 0, 0, 0, 0, "__AVR_ARCH__=35"  },
-  { 0, 1, 0, 1, 0, 0, 0, 0, "__AVR_ARCH__=4"   },
-  { 0, 1, 1, 1, 0, 0, 0, 0, "__AVR_ARCH__=5"   },
-  { 0, 1, 1, 1, 1, 1, 0, 0, "__AVR_ARCH__=51"  },
-  { 0, 1, 1, 1, 1, 1, 1, 0, "__AVR_ARCH__=6"   }
-};
+/* Current device.  */
+const struct mcu_type_s *avr_current_device;
 
-/* These names are used as the index into the avr_arch_types[] table 
-   above.  */
-
-enum avr_arch
-{
-  ARCH_UNKNOWN,
-  ARCH_AVR1,
-  ARCH_AVR2,
-  ARCH_AVR25,
-  ARCH_AVR3,
-  ARCH_AVR31,
-  ARCH_AVR35,
-  ARCH_AVR4,
-  ARCH_AVR5,
-  ARCH_AVR51,
-  ARCH_AVR6
-};
+section *progmem_section;
 
-struct mcu_type_s {
-  const char *const name;
-  int arch;  /* index in avr_arch_types[] */
-  /* Must lie outside user's namespace.  NULL == no macro.  */
-  const char *const macro;
-};
+/* To track if code will use .bss and/or .data.  */
+bool avr_need_clear_bss_p = false;
+bool avr_need_copy_data_p = false;
 
-/* List of all known AVR MCU types - if updated, it has to be kept
-   in sync in several places (FIXME: is there a better way?):
-    - here
-    - avr.h (CPP_SPEC, LINK_SPEC, CRT_BINUTILS_SPECS)
-    - t-avr (MULTILIB_MATCHES)
-    - gas/config/tc-avr.c
-    - avr-libc  */
-
-static const struct mcu_type_s avr_mcu_types[] = {
-    /* Classic, <= 8K.  */
-  { "avr2",         ARCH_AVR2, NULL },
-  { "at90s2313",    ARCH_AVR2, "__AVR_AT90S2313__" },
-  { "at90s2323",    ARCH_AVR2, "__AVR_AT90S2323__" },
-  { "at90s2333",    ARCH_AVR2, "__AVR_AT90S2333__" },
-  { "at90s2343",    ARCH_AVR2, "__AVR_AT90S2343__" },
-  { "attiny22",     ARCH_AVR2, "__AVR_ATtiny22__" },
-  { "attiny26",     ARCH_AVR2, "__AVR_ATtiny26__" },
-  { "at90s4414",    ARCH_AVR2, "__AVR_AT90S4414__" },
-  { "at90s4433",    ARCH_AVR2, "__AVR_AT90S4433__" },
-  { "at90s4434",    ARCH_AVR2, "__AVR_AT90S4434__" },
-  { "at90s8515",    ARCH_AVR2, "__AVR_AT90S8515__" },
-  { "at90c8534",    ARCH_AVR2, "__AVR_AT90C8534__" },
-  { "at90s8535",    ARCH_AVR2, "__AVR_AT90S8535__" },
-    /* Classic + MOVW, <= 8K.  */
-  { "avr25",        ARCH_AVR25, NULL },
-  { "attiny13",     ARCH_AVR25, "__AVR_ATtiny13__" },
-  { "attiny2313",   ARCH_AVR25, "__AVR_ATtiny2313__" },
-  { "attiny24",     ARCH_AVR25, "__AVR_ATtiny24__" },
-  { "attiny44",     ARCH_AVR25, "__AVR_ATtiny44__" },
-  { "attiny84",     ARCH_AVR25, "__AVR_ATtiny84__" },
-  { "attiny25",     ARCH_AVR25, "__AVR_ATtiny25__" },
-  { "attiny45",     ARCH_AVR25, "__AVR_ATtiny45__" },
-  { "attiny85",     ARCH_AVR25, "__AVR_ATtiny85__" },
-  { "attiny261",    ARCH_AVR25, "__AVR_ATtiny261__" },
-  { "attiny461",    ARCH_AVR25, "__AVR_ATtiny461__" },
-  { "attiny861",    ARCH_AVR25, "__AVR_ATtiny861__" },
-  { "attiny43u",    ARCH_AVR25, "__AVR_ATtiny43U__" },
-  { "attiny48",     ARCH_AVR25, "__AVR_ATtiny48__" },
-  { "attiny88",     ARCH_AVR25, "__AVR_ATtiny88__" },
-  { "at86rf401",    ARCH_AVR25, "__AVR_AT86RF401__" },
-    /* Classic, > 8K, <= 64K.  */
-  { "avr3",         ARCH_AVR3, NULL },
-  { "at43usb320",   ARCH_AVR3, "__AVR_AT43USB320__" },
-  { "at43usb355",   ARCH_AVR3, "__AVR_AT43USB355__" },
-  { "at76c711",     ARCH_AVR3, "__AVR_AT76C711__" },
-    /* Classic, == 128K.  */
-  { "avr31",        ARCH_AVR31, NULL },
-  { "atmega103",    ARCH_AVR31, "__AVR_ATmega103__" },
-    /* Classic + MOVW + JMP/CALL.  */
-  { "avr35",        ARCH_AVR35, NULL },
-  { "at90usb82",    ARCH_AVR35, "__AVR_AT90USB82__" },
-  { "at90usb162",   ARCH_AVR35, "__AVR_AT90USB162__" },
-    /* Enhanced, <= 8K.  */
-  { "avr4",         ARCH_AVR4, NULL },
-  { "atmega8",      ARCH_AVR4, "__AVR_ATmega8__" },
-  { "atmega48",     ARCH_AVR4, "__AVR_ATmega48__" },
-  { "atmega48p",    ARCH_AVR4, "__AVR_ATmega48P__" },
-  { "atmega88",     ARCH_AVR4, "__AVR_ATmega88__" },
-  { "atmega88p",    ARCH_AVR4, "__AVR_ATmega88P__" },
-  { "atmega8515",   ARCH_AVR4, "__AVR_ATmega8515__" },
-  { "atmega8535",   ARCH_AVR4, "__AVR_ATmega8535__" },
-  { "atmega8hva",   ARCH_AVR4, "__AVR_ATmega8HVA__" },
-  { "at90pwm1",     ARCH_AVR4, "__AVR_AT90PWM1__" },
-  { "at90pwm2",     ARCH_AVR4, "__AVR_AT90PWM2__" },
-  { "at90pwm2b",    ARCH_AVR4, "__AVR_AT90PWM2B__" },
-  { "at90pwm3",     ARCH_AVR4, "__AVR_AT90PWM3__" },
-  { "at90pwm3b",    ARCH_AVR4, "__AVR_AT90PWM3B__" },
-    /* Enhanced, > 8K, <= 64K.  */
-  { "avr5",         ARCH_AVR5, NULL },
-  { "atmega16",     ARCH_AVR5, "__AVR_ATmega16__" },
-  { "atmega161",    ARCH_AVR5, "__AVR_ATmega161__" },
-  { "atmega162",    ARCH_AVR5, "__AVR_ATmega162__" },
-  { "atmega163",    ARCH_AVR5, "__AVR_ATmega163__" },
-  { "atmega164p",   ARCH_AVR5, "__AVR_ATmega164P__" },
-  { "atmega165",    ARCH_AVR5, "__AVR_ATmega165__" },
-  { "atmega165p",   ARCH_AVR5, "__AVR_ATmega165P__" },
-  { "atmega168",    ARCH_AVR5, "__AVR_ATmega168__" },
-  { "atmega168p",   ARCH_AVR5, "__AVR_ATmega168P__" },
-  { "atmega169",    ARCH_AVR5, "__AVR_ATmega169__" },
-  { "atmega169p",   ARCH_AVR5, "__AVR_ATmega169P__" },
-  { "atmega32",     ARCH_AVR5, "__AVR_ATmega32__" },
-  { "atmega323",    ARCH_AVR5, "__AVR_ATmega323__" },
-  { "atmega324p",   ARCH_AVR5, "__AVR_ATmega324P__" },
-  { "atmega325",    ARCH_AVR5, "__AVR_ATmega325__" },
-  { "atmega325p",   ARCH_AVR5, "__AVR_ATmega325P__" },
-  { "atmega3250",   ARCH_AVR5, "__AVR_ATmega3250__" },
-  { "atmega3250p",  ARCH_AVR5, "__AVR_ATmega3250P__" },
-  { "atmega328p",   ARCH_AVR5, "__AVR_ATmega328P__" },
-  { "atmega329",    ARCH_AVR5, "__AVR_ATmega329__" },
-  { "atmega329p",   ARCH_AVR5, "__AVR_ATmega329P__" },
-  { "atmega3290",   ARCH_AVR5, "__AVR_ATmega3290__" },
-  { "atmega3290p",  ARCH_AVR5, "__AVR_ATmega3290P__" },
-  { "atmega32hvb",  ARCH_AVR5, "__AVR_ATmega32HVB__" },
-  { "atmega406",    ARCH_AVR5, "__AVR_ATmega406__" },
-  { "atmega64",     ARCH_AVR5, "__AVR_ATmega64__" },
-  { "atmega640",    ARCH_AVR5, "__AVR_ATmega640__" },
-  { "atmega644",    ARCH_AVR5, "__AVR_ATmega644__" },
-  { "atmega644p",   ARCH_AVR5, "__AVR_ATmega644P__" },
-  { "atmega645",    ARCH_AVR5, "__AVR_ATmega645__" },
-  { "atmega6450",   ARCH_AVR5, "__AVR_ATmega6450__" },
-  { "atmega649",    ARCH_AVR5, "__AVR_ATmega649__" },
-  { "atmega6490",   ARCH_AVR5, "__AVR_ATmega6490__" },
-  { "atmega16hva",  ARCH_AVR5, "__AVR_ATmega16HVA__" },
-  { "at90can32",    ARCH_AVR5, "__AVR_AT90CAN32__" },
-  { "at90can64",    ARCH_AVR5, "__AVR_AT90CAN64__" },
-  { "at90pwm216",   ARCH_AVR5, "__AVR_AT90PWM216__" },
-  { "at90pwm316",   ARCH_AVR5, "__AVR_AT90PWM316__" },
-  { "atmega32m1",   ARCH_AVR5, "__AVR_ATmega32M1__" },
-  { "at90usb646",   ARCH_AVR5, "__AVR_AT90USB646__" },
-  { "at90usb647",   ARCH_AVR5, "__AVR_AT90USB647__" },
-  { "at94k",        ARCH_AVR5, "__AVR_AT94K__" },
-    /* Enhanced, == 128K.  */
-  { "avr51",        ARCH_AVR51, NULL },
-  { "atmega128",    ARCH_AVR51, "__AVR_ATmega128__" },
-  { "atmega1280",   ARCH_AVR51, "__AVR_ATmega1280__" },
-  { "atmega1281",   ARCH_AVR51, "__AVR_ATmega1281__" },
-  { "atmega1284p",  ARCH_AVR51, "__AVR_ATmega1284P__" },
-  { "at90can128",   ARCH_AVR51, "__AVR_AT90CAN128__" },
-  { "at90usb1286",  ARCH_AVR51, "__AVR_AT90USB1286__" },
-  { "at90usb1287",  ARCH_AVR51, "__AVR_AT90USB1287__" },
-    /* 3-Byte PC.  */
-  { "avr6",         ARCH_AVR6, NULL },
-  { "atmega2560",   ARCH_AVR6, "__AVR_ATmega2560__" },
-  { "atmega2561",   ARCH_AVR6, "__AVR_ATmega2561__" },
-    /* Assembler only.  */
-  { "avr1",         ARCH_AVR1, NULL },
-  { "at90s1200",    ARCH_AVR1, "__AVR_AT90S1200__" },
-  { "attiny11",     ARCH_AVR1, "__AVR_ATtiny11__" },
-  { "attiny12",     ARCH_AVR1, "__AVR_ATtiny12__" },
-  { "attiny15",     ARCH_AVR1, "__AVR_ATtiny15__" },
-  { "attiny28",     ARCH_AVR1, "__AVR_ATtiny28__" },
-  { NULL,           ARCH_UNKNOWN, NULL }
+/* 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 }
 };
 
-int avr_case_values_threshold = 30000;
+/* Implement TARGET_OPTION_OPTIMIZATION_TABLE.  */
+static const struct default_options avr_option_optimization_table[] =
+  {
+    { OPT_LEVELS_1_PLUS, OPT_fomit_frame_pointer, NULL, 1 },
+    { OPT_LEVELS_NONE, 0, NULL, 0 }
+  };
 \f
 /* Initialize the GCC target structure.  */
 #undef TARGET_ASM_ALIGNED_HI_OP
@@ -305,6 +186,14 @@ int avr_case_values_threshold = 30000;
 #define TARGET_ASM_FUNCTION_END_PROLOGUE avr_asm_function_end_prologue
 #undef TARGET_ASM_FUNCTION_BEGIN_EPILOGUE
 #define TARGET_ASM_FUNCTION_BEGIN_EPILOGUE avr_asm_function_begin_epilogue
+
+#undef TARGET_FUNCTION_VALUE
+#define TARGET_FUNCTION_VALUE avr_function_value
+#undef TARGET_LIBCALL_VALUE
+#define TARGET_LIBCALL_VALUE avr_libcall_value
+#undef TARGET_FUNCTION_VALUE_REGNO_P
+#define TARGET_FUNCTION_VALUE_REGNO_P avr_function_value_regno_p
+
 #undef TARGET_ATTRIBUTE_TABLE
 #define TARGET_ATTRIBUTE_TABLE avr_attribute_table
 #undef TARGET_ASM_FUNCTION_RODATA_SECTION
@@ -313,12 +202,29 @@ int avr_case_values_threshold = 30000;
 #define TARGET_INSERT_ATTRIBUTES avr_insert_attributes
 #undef TARGET_SECTION_TYPE_FLAGS
 #define TARGET_SECTION_TYPE_FLAGS avr_section_type_flags
+
+/* `TARGET_ASM_NAMED_SECTION' must be defined in avr.h.  */
+
+#undef TARGET_ASM_INIT_SECTIONS
+#define TARGET_ASM_INIT_SECTIONS avr_asm_init_sections
+
+#undef TARGET_REGISTER_MOVE_COST
+#define TARGET_REGISTER_MOVE_COST avr_register_move_cost
+#undef TARGET_MEMORY_MOVE_COST
+#define TARGET_MEMORY_MOVE_COST avr_memory_move_cost
 #undef TARGET_RTX_COSTS
 #define TARGET_RTX_COSTS avr_rtx_costs
 #undef TARGET_ADDRESS_COST
 #define TARGET_ADDRESS_COST avr_address_cost
 #undef TARGET_MACHINE_DEPENDENT_REORG
 #define TARGET_MACHINE_DEPENDENT_REORG avr_reorg
+#undef TARGET_FUNCTION_ARG
+#define TARGET_FUNCTION_ARG avr_function_arg
+#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
@@ -329,10 +235,51 @@ int avr_case_values_threshold = 30000;
 #undef TARGET_BUILTIN_SETJMP_FRAME_VALUE
 #define TARGET_BUILTIN_SETJMP_FRAME_VALUE avr_builtin_setjmp_frame_value
 
+#undef TARGET_HARD_REGNO_SCRATCH_OK
+#define TARGET_HARD_REGNO_SCRATCH_OK avr_hard_regno_scratch_ok
+#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
+#define TARGET_CAN_ELIMINATE avr_can_eliminate
+
+#undef TARGET_CLASS_LIKELY_SPILLED_P
+#define TARGET_CLASS_LIKELY_SPILLED_P avr_class_likely_spilled_p
+
+#undef TARGET_OPTION_OVERRIDE
+#define TARGET_OPTION_OVERRIDE avr_option_override
+
+#undef TARGET_OPTION_OPTIMIZATION_TABLE
+#define TARGET_OPTION_OPTIMIZATION_TABLE avr_option_optimization_table
+
+#undef TARGET_CANNOT_MODIFY_JUMPS_P
+#define TARGET_CANNOT_MODIFY_JUMPS_P avr_cannot_modify_jumps_p
+
+#undef TARGET_HELP
+#define TARGET_HELP avr_help
+
+#undef TARGET_EXCEPT_UNWIND_INFO
+#define TARGET_EXCEPT_UNWIND_INFO sjlj_except_unwind_info
+
+#undef TARGET_FUNCTION_OK_FOR_SIBCALL
+#define TARGET_FUNCTION_OK_FOR_SIBCALL avr_function_ok_for_sibcall
+
+#undef TARGET_INIT_BUILTINS
+#define TARGET_INIT_BUILTINS avr_init_builtins
+
+#undef TARGET_EXPAND_BUILTIN
+#define TARGET_EXPAND_BUILTIN avr_expand_builtin
+
+
 struct gcc_target targetm = TARGET_INITIALIZER;
 \f
-void
-avr_override_options (void)
+static void
+avr_option_override (void)
 {
   const struct mcu_type_s *t;
 
@@ -344,18 +291,13 @@ avr_override_options (void)
 
   if (!t->name)
     {
-      fprintf (stderr, "unknown MCU '%s' specified\nKnown MCU names:\n",
-              avr_mcu_name);
-      for (t = avr_mcu_types; t->name; t++)
-       fprintf (stderr,"   %s\n", t->name);
+      error ("unrecognized argument to -mmcu= option: %qs", avr_mcu_name);
+      inform (input_location,  "See --target-help for supported MCUs");
     }
 
-  avr_current_arch = &avr_arch_types[t->arch];
-  avr_extra_arch_macro = t->macro;
-
-  if (optimize && !TARGET_NO_TABLEJUMP)
-    avr_case_values_threshold = 
-      (!AVR_HAVE_JMP_CALL || TARGET_CALL_PROLOGUES) ? 8 : 17;
+  avr_current_device = t;
+  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);
@@ -363,9 +305,45 @@ avr_override_options (void)
   init_machine_status = avr_init_machine_status;
 }
 
+/* Implement TARGET_HELP */
+/* Report extra information for --target-help */
+
+static void
+avr_help (void)
+{
+  const struct mcu_type_s *t;
+  const char * const indent = "  ";
+  int len;
+
+  /* Give a list of MCUs that are accepted by -mmcu=* .
+     Note that MCUs supported by the compiler might differ from
+     MCUs supported by binutils. */
+
+  len = strlen (indent);
+  printf ("Known MCU names:\n%s", indent);
+
+  /* Print a blank-separated list of all supported MCUs */
+
+  for (t = avr_mcu_types; t->name; t++)
+    {
+      printf ("%s ", t->name);
+      len += 1 + strlen (t->name);
+
+      /* Break long lines */
+      
+      if (len > 66 && (t+1)->name)
+        {
+          printf ("\n%s", indent);
+          len = strlen (indent);
+        }
+    }
+
+  printf ("\n\n");
+}
+
 /*  return register class from register number.  */
 
-static const int reg_class_tab[]={
+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,
@@ -384,8 +362,7 @@ static const int reg_class_tab[]={
 static struct machine_function *
 avr_init_machine_status (void)
 {
-  return ((struct machine_function *) 
-          ggc_alloc_cleared (sizeof (struct machine_function)));
+  return ggc_alloc_cleared_machine_function ();
 }
 
 /* Return register class for register R.  */
@@ -398,17 +375,34 @@ avr_regno_reg_class (int r)
   return ALL_REGS;
 }
 
+/* A helper for the subsequent function attribute used to dig for
+   attribute 'name' in a FUNCTION_DECL or FUNCTION_TYPE */
+
+static inline int
+avr_lookup_function_attribute1 (const_tree func, const char *name)
+{
+  if (FUNCTION_DECL == TREE_CODE (func))
+    {
+      if (NULL_TREE != lookup_attribute (name, DECL_ATTRIBUTES (func)))
+        {
+          return true;
+        }
+      
+      func = TREE_TYPE (func);
+    }
+
+  gcc_assert (TREE_CODE (func) == FUNCTION_TYPE
+              || TREE_CODE (func) == METHOD_TYPE);
+  
+  return NULL_TREE != lookup_attribute (name, TYPE_ATTRIBUTES (func));
+}
+
 /* Return nonzero if FUNC is a naked function.  */
 
 static int
 avr_naked_function_p (tree func)
 {
-  tree a;
-
-  gcc_assert (TREE_CODE (func) == FUNCTION_DECL);
-  
-  a = lookup_attribute ("naked", TYPE_ATTRIBUTES (TREE_TYPE (func)));
-  return a != NULL_TREE;
+  return avr_lookup_function_attribute1 (func, "naked");
 }
 
 /* Return nonzero if FUNC is an interrupt function as specified
@@ -417,13 +411,7 @@ avr_naked_function_p (tree func)
 static int
 interrupt_function_p (tree func)
 {
-  tree a;
-
-  if (TREE_CODE (func) != FUNCTION_DECL)
-    return 0;
-
-  a = lookup_attribute ("interrupt", DECL_ATTRIBUTES (func));
-  return a != NULL_TREE;
+  return avr_lookup_function_attribute1 (func, "interrupt");
 }
 
 /* Return nonzero if FUNC is a signal function as specified
@@ -432,13 +420,7 @@ interrupt_function_p (tree func)
 static int
 signal_function_p (tree func)
 {
-  tree a;
-
-  if (TREE_CODE (func) != FUNCTION_DECL)
-    return 0;
-
-  a = lookup_attribute ("signal", DECL_ATTRIBUTES (func));
-  return a != NULL_TREE;
+  return avr_lookup_function_attribute1 (func, "signal");
 }
 
 /* Return nonzero if FUNC is a OS_task function.  */
@@ -446,12 +428,7 @@ signal_function_p (tree func)
 static int
 avr_OS_task_function_p (tree func)
 {
-  tree a;
-
-  gcc_assert (TREE_CODE (func) == FUNCTION_DECL);
-  
-  a = lookup_attribute ("OS_task", TYPE_ATTRIBUTES (TREE_TYPE (func)));
-  return a != NULL_TREE;
+  return avr_lookup_function_attribute1 (func, "OS_task");
 }
 
 /* Return nonzero if FUNC is a OS_main function.  */
@@ -459,12 +436,7 @@ avr_OS_task_function_p (tree func)
 static int
 avr_OS_main_function_p (tree func)
 {
-  tree a;
-
-  gcc_assert (TREE_CODE (func) == FUNCTION_DECL);
-  
-  a = lookup_attribute ("OS_main", TYPE_ATTRIBUTES (TREE_TYPE (func)));
-  return a != NULL_TREE;
+  return avr_lookup_function_attribute1 (func, "OS_main");
 }
 
 /* Return the number of hard registers to push/pop in the prologue/epilogue
@@ -477,9 +449,6 @@ avr_regs_to_save (HARD_REG_SET *set)
   int int_or_sig_p = (interrupt_function_p (current_function_decl)
                      || signal_function_p (current_function_decl));
 
-  if (!reload_completed)
-    cfun->machine->is_leaf = leaf_function_p ();
-
   if (set)
     CLEAR_HARD_REG_SET (*set);
   count = 0;
@@ -498,7 +467,7 @@ avr_regs_to_save (HARD_REG_SET *set)
       if (fixed_regs[reg])
        continue;
 
-      if ((int_or_sig_p && !cfun->machine->is_leaf && call_used_regs[reg])
+      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
@@ -512,10 +481,21 @@ avr_regs_to_save (HARD_REG_SET *set)
   return count;
 }
 
+/* Return true if register FROM can be eliminated via register TO.  */
+
+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));
+}
+
 /* Compute offset between arg_pointer and frame_pointer.  */
 
 int
-initial_elimination_offset (int from, int to)
+avr_initial_elimination_offset (int from, int to)
 {
   if (from == FRAME_POINTER_REGNUM && to == STACK_POINTER_REGNUM)
     return 0;
@@ -540,6 +520,31 @@ rtx avr_builtin_setjmp_frame_value (void)
                         gen_int_mode (STARTING_FRAME_OFFSET, Pmode));
 }
 
+/* Return contents of MEM at frame pointer + stack size + 1 (+2 if 3 byte PC).
+   This is return address of function.  */
+rtx 
+avr_return_addr_rtx (int count, rtx tem)
+{
+  rtx r;
+    
+  /* Can only return this functions return address. Others not supported.  */
+  if (count)
+     return NULL;
+
+  if (AVR_3_BYTE_PC)
+    {
+      r = gen_rtx_SYMBOL_REF (Pmode, ".L__stack_usage+2");
+      warning (0, "'builtin_return_address' contains only 2 bytes of address");
+    }
+  else
+    r = gen_rtx_SYMBOL_REF (Pmode, ".L__stack_usage+1");
+
+  r = gen_rtx_PLUS (Pmode, tem, r);
+  r = gen_frame_mem (Pmode, memory_address (Pmode, r));
+  r = gen_rtx_ROTATE (HImode, r, GEN_INT (8));
+  return  r;
+}
+
 /* Return 1 if the function epilogue is just a single "ret".  */
 
 int
@@ -617,6 +622,35 @@ get_sequence_length (rtx insns)
   return length;
 }
 
+/*  Implement INCOMING_RETURN_ADDR_RTX.  */
+
+rtx
+avr_incoming_return_addr_rtx (void)
+{
+  /* The return address is at the top of the stack.  Note that the push
+     was via post-decrement, which means the actual address is off by one.  */
+  return gen_frame_mem (HImode, plus_constant (stack_pointer_rtx, 1));
+}
+
+/*  Helper for expand_prologue.  Emit a push of a byte register.  */
+
+static void
+emit_push_byte (unsigned regno, bool frame_related_p)
+{
+  rtx mem, reg, insn;
+
+  mem = gen_rtx_POST_DEC (HImode, stack_pointer_rtx);
+  mem = gen_frame_mem (QImode, mem);
+  reg = gen_rtx_REG (QImode, regno);
+
+  insn = emit_insn (gen_rtx_SET (VOIDmode, mem, reg));
+  if (frame_related_p)
+    RTX_FRAME_RELATED_P (insn) = 1;
+
+  cfun->machine->stack_usage++;
+}
+
+
 /*  Output function prologue.  */
 
 void
@@ -626,14 +660,7 @@ expand_prologue (void)
   HARD_REG_SET set;
   int minimize;
   HOST_WIDE_INT size = get_frame_size();
-  /* Define templates for push instructions.  */
-  rtx pushbyte = gen_rtx_MEM (QImode,
-                  gen_rtx_POST_DEC (HImode, stack_pointer_rtx));
-  rtx pushword = gen_rtx_MEM (HImode,
-                  gen_rtx_POST_DEC (HImode, stack_pointer_rtx));
   rtx insn;
-
-  last_insn_address = 0;
   
   /* Init cfun->machine.  */
   cfun->machine->is_naked = avr_naked_function_p (current_function_decl);
@@ -641,6 +668,7 @@ expand_prologue (void)
   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)
@@ -659,42 +687,34 @@ expand_prologue (void)
 
   if (cfun->machine->is_interrupt || cfun->machine->is_signal)
     {
+      /* Enable interrupts.  */
       if (cfun->machine->is_interrupt)
-        {
-          /* Enable interrupts.  */
-          insn = emit_insn (gen_enable_interrupt ());
-          RTX_FRAME_RELATED_P (insn) = 1;
-        }
+       emit_insn (gen_enable_interrupt ());
        
       /* Push zero reg.  */
-      insn = emit_move_insn (pushbyte, zero_reg_rtx);
-      RTX_FRAME_RELATED_P (insn) = 1;
+      emit_push_byte (ZERO_REGNO, true);
 
       /* Push tmp reg.  */
-      insn = emit_move_insn (pushbyte, tmp_reg_rtx);
-      RTX_FRAME_RELATED_P (insn) = 1;
+      emit_push_byte (TMP_REGNO, true);
 
       /* Push SREG.  */
-      insn = emit_move_insn (tmp_reg_rtx, 
-                             gen_rtx_MEM (QImode, GEN_INT (SREG_ADDR)));
-      RTX_FRAME_RELATED_P (insn) = 1;
-      insn = emit_move_insn (pushbyte, tmp_reg_rtx);
-      RTX_FRAME_RELATED_P (insn) = 1;
+      /* ??? There's no dwarf2 column reserved for SREG.  */
+      emit_move_insn (tmp_reg_rtx, gen_rtx_MEM (QImode, GEN_INT (SREG_ADDR)));
+      emit_push_byte (TMP_REGNO, false);
 
       /* Push RAMPZ.  */
-      if(AVR_HAVE_RAMPZ 
-         && (TEST_HARD_REG_BIT (set, REG_Z) && TEST_HARD_REG_BIT (set, REG_Z + 1)))
+      /* ??? There's no dwarf2 column reserved for RAMPZ.  */
+      if (AVR_HAVE_RAMPZ 
+          && TEST_HARD_REG_BIT (set, REG_Z)
+          && TEST_HARD_REG_BIT (set, REG_Z + 1))
         {
-          insn = emit_move_insn (tmp_reg_rtx, 
-                                 gen_rtx_MEM (QImode, GEN_INT (RAMPZ_ADDR)));
-          RTX_FRAME_RELATED_P (insn) = 1;
-          insn = emit_move_insn (pushbyte, tmp_reg_rtx);
-          RTX_FRAME_RELATED_P (insn) = 1;
+          emit_move_insn (tmp_reg_rtx,
+                         gen_rtx_MEM (QImode, GEN_INT (RAMPZ_ADDR)));
+         emit_push_byte (TMP_REGNO, false);
         }
        
       /* Clear zero reg.  */
-      insn = emit_move_insn (zero_reg_rtx, const0_rtx);
-      RTX_FRAME_RELATED_P (insn) = 1;
+      emit_move_insn (zero_reg_rtx, const0_rtx);
 
       /* Prevent any attempt to delete the setting of ZERO_REG!  */
       emit_use (zero_reg_rtx);
@@ -703,34 +723,63 @@ expand_prologue (void)
                   || (AVR_2_BYTE_PC && live_seq > 6)
                   || live_seq > 7)) 
     {
-      insn = emit_move_insn (gen_rtx_REG (HImode, REG_X), 
-                             gen_int_mode (size, HImode));
-      RTX_FRAME_RELATED_P (insn) = 1;
+      int first_reg, reg, offset;
+
+      emit_move_insn (gen_rtx_REG (HImode, REG_X), 
+                      gen_int_mode (size, HImode));
 
-      insn = 
-        emit_insn (gen_call_prologue_saves (gen_int_mode (live_seq, HImode),
-                                           gen_int_mode (size + live_seq, HImode)));
+      insn = emit_insn (gen_call_prologue_saves
+                       (gen_int_mode (live_seq, HImode),
+                        gen_int_mode (size + live_seq, HImode)));
       RTX_FRAME_RELATED_P (insn) = 1;
+
+      /* Describe the effect of the unspec_volatile call to prologue_saves.
+        Note that this formulation assumes that add_reg_note pushes the
+        notes to the front.  Thus we build them in the reverse order of
+        how we want dwarf2out to process them.  */
+
+      /* The function does always set frame_pointer_rtx, but whether that
+        is going to be permanent in the function is frame_pointer_needed.  */
+      add_reg_note (insn, REG_CFA_ADJUST_CFA,
+                   gen_rtx_SET (VOIDmode,
+                                (frame_pointer_needed
+                                 ? frame_pointer_rtx : stack_pointer_rtx),
+                                plus_constant (stack_pointer_rtx,
+                                               -(size + live_seq))));
+
+      /* Note that live_seq always contains r28+r29, but the other
+        registers to be saved are all below 18.  */
+      first_reg = 18 - (live_seq - 2);
+
+      for (reg = 29, offset = -live_seq + 1;
+          reg >= first_reg;
+          reg = (reg == 28 ? 17 : reg - 1), ++offset)
+       {
+         rtx m, r;
+
+         m = gen_rtx_MEM (QImode, plus_constant (stack_pointer_rtx, offset));
+         r = gen_rtx_REG (QImode, reg);
+         add_reg_note (insn, REG_CFA_OFFSET, gen_rtx_SET (VOIDmode, m, r));
+       }
+
+      cfun->machine->stack_usage += size + live_seq;
     }
   else
     {
       int reg;
       for (reg = 0; reg < 32; ++reg)
-        {
-          if (TEST_HARD_REG_BIT (set, reg))
-            {
-              /* Emit push of register to save.  */
-              insn=emit_move_insn (pushbyte, gen_rtx_REG (QImode, reg));
-              RTX_FRAME_RELATED_P (insn) = 1;
-            }
-        }
+        if (TEST_HARD_REG_BIT (set, reg))
+         emit_push_byte (reg, true);
+
       if (frame_pointer_needed)
         {
          if (!(cfun->machine->is_OS_task || cfun->machine->is_OS_main))
            {
-              /* Push frame pointer.  */
-             insn = emit_move_insn (pushword, frame_pointer_rtx);
-              RTX_FRAME_RELATED_P (insn) = 1;
+              /* Push frame pointer.  Always be consistent about the
+                ordering of pushes -- epilogue_restores expects the
+                register pair to be pushed low byte first.  */
+             emit_push_byte (REG_Y, true);
+             emit_push_byte (REG_Y + 1, true);
            }
 
           if (!size)
@@ -753,13 +802,12 @@ expand_prologue (void)
               is selected.  */
               rtx myfp;
              rtx fp_plus_insns; 
-             rtx sp_plus_insns = NULL_RTX;
 
-              if (TARGET_TINY_STACK)
+              if (AVR_HAVE_8BIT_SP)
                 {
-                  /* The high byte (r29) doesn't change - prefer 'subi' (1 cycle)
-                     over 'sbiw' (2 cycles, same size).  */
-                  myfp = gen_rtx_REG (QImode, REGNO (frame_pointer_rtx));
+                  /* The high byte (r29) doesn't change.  Prefer 'subi'
+                    (1 cycle) over 'sbiw' (2 cycles, same size).  */
+                  myfp = gen_rtx_REG (QImode, FRAME_POINTER_REGNUM);
                 }
               else 
                 {
@@ -770,41 +818,43 @@ expand_prologue (void)
              /* Method 1-Adjust frame pointer.  */
              start_sequence ();
 
-              insn = emit_move_insn (frame_pointer_rtx, stack_pointer_rtx);
-              RTX_FRAME_RELATED_P (insn) = 1;
+             /* Normally the dwarf2out frame-related-expr interpreter does
+                not expect to have the CFA change once the frame pointer is
+                set up.  Thus we avoid marking the move insn below and
+                instead indicate that the entire operation is complete after
+                the frame pointer subtraction is done.  */
 
-              insn = 
-               emit_move_insn (myfp,
-                               gen_rtx_PLUS (GET_MODE(myfp), myfp, 
-                                             gen_int_mode (-size, 
-                                                           GET_MODE(myfp))));
-              RTX_FRAME_RELATED_P (insn) = 1;
+              emit_move_insn (frame_pointer_rtx, stack_pointer_rtx);
 
-             /* Copy to stack pointer.  */
-             if (TARGET_TINY_STACK)
+              insn = emit_move_insn (myfp, plus_constant (myfp, -size));
+              RTX_FRAME_RELATED_P (insn) = 1;
+             add_reg_note (insn, REG_CFA_ADJUST_CFA,
+                           gen_rtx_SET (VOIDmode, frame_pointer_rtx,
+                                        plus_constant (stack_pointer_rtx,
+                                                       -size)));
+
+             /* Copy to stack pointer.  Note that since we've already
+                changed the CFA to the frame pointer this operation
+                need not be annotated at all.  */
+             if (AVR_HAVE_8BIT_SP)
                {
-                 insn = emit_move_insn (stack_pointer_rtx, frame_pointer_rtx);
-                 RTX_FRAME_RELATED_P (insn) = 1;
+                 emit_move_insn (stack_pointer_rtx, frame_pointer_rtx);
                }
              else if (TARGET_NO_INTERRUPTS 
                       || cfun->machine->is_signal
                       || cfun->machine->is_OS_main)
                {
-                 insn = 
-                   emit_insn (gen_movhi_sp_r_irq_off (stack_pointer_rtx, 
-                                                      frame_pointer_rtx));
-                 RTX_FRAME_RELATED_P (insn) = 1;               
+                 emit_insn (gen_movhi_sp_r_irq_off (stack_pointer_rtx, 
+                                                    frame_pointer_rtx));
                }
              else if (cfun->machine->is_interrupt)
                {
-                 insn = emit_insn (gen_movhi_sp_r_irq_on (stack_pointer_rtx, 
-                                                          frame_pointer_rtx));
-                 RTX_FRAME_RELATED_P (insn) = 1;
+                 emit_insn (gen_movhi_sp_r_irq_on (stack_pointer_rtx, 
+                                                   frame_pointer_rtx));
                }
              else
                {
-                 insn = emit_move_insn (stack_pointer_rtx, frame_pointer_rtx);
-                 RTX_FRAME_RELATED_P (insn) = 1;
+                 emit_move_insn (stack_pointer_rtx, frame_pointer_rtx);
                }
 
              fp_plus_insns = get_insns ();
@@ -813,33 +863,37 @@ expand_prologue (void)
              /* Method 2-Adjust Stack pointer.  */
               if (size <= 6)
                 {
+                 rtx sp_plus_insns;
+
                  start_sequence ();
 
-                 insn = 
-                   emit_move_insn (stack_pointer_rtx,
-                                   gen_rtx_PLUS (HImode, 
-                                                 stack_pointer_rtx, 
-                                                 gen_int_mode (-size, 
-                                                               HImode)));
+                 insn = plus_constant (stack_pointer_rtx, -size);
+                 insn = emit_move_insn (stack_pointer_rtx, insn);
                  RTX_FRAME_RELATED_P (insn) = 1;
                  
-                 insn = 
-                   emit_move_insn (frame_pointer_rtx, stack_pointer_rtx);
+                 insn = emit_move_insn (frame_pointer_rtx, stack_pointer_rtx);
                  RTX_FRAME_RELATED_P (insn) = 1;
 
                  sp_plus_insns = get_insns ();
                  end_sequence ();
-                }
 
-              /* Use shortest method.  */
-              if (size <= 6 && (get_sequence_length (sp_plus_insns) 
-                                < get_sequence_length (fp_plus_insns)))
-               emit_insn (sp_plus_insns);
-              else
+                 /* Use shortest method.  */
+                 if (get_sequence_length (sp_plus_insns) 
+                     < get_sequence_length (fp_plus_insns))
+                   emit_insn (sp_plus_insns);
+                 else
+                   emit_insn (fp_plus_insns);
+                }
+             else
                emit_insn (fp_plus_insns);
+
+             cfun->machine->stack_usage += size;
             }
         }
     }
+
+  if (flag_stack_usage_info)
+    current_function_static_stack_size = cfun->machine->stack_usage;
 }
 
 /* Output summary at end of function prologue.  */
@@ -866,6 +920,11 @@ avr_asm_function_end_prologue (FILE *file)
     }
   fprintf (file, "/* frame size = " HOST_WIDE_INT_PRINT_DEC " */\n",
                  get_frame_size());
+  fprintf (file, "/* stack size = %d */\n",
+                 cfun->machine->stack_usage);
+  /* Create symbol stack offset here so all functions have it. Add 1 to stack
+     usage for offset so that SP + .L__stack_offset = return address.  */
+  fprintf (file, ".L__stack_usage = %d\n", cfun->machine->stack_usage);
 }
 
 
@@ -881,10 +940,24 @@ avr_epilogue_uses (int regno ATTRIBUTE_UNUSED)
   return 0;
 }
 
+/*  Helper for expand_epilogue.  Emit a pop of a byte register.  */
+
+static void
+emit_pop_byte (unsigned regno)
+{
+  rtx mem, reg;
+
+  mem = gen_rtx_PRE_INC (HImode, stack_pointer_rtx);
+  mem = gen_frame_mem (QImode, mem);
+  reg = gen_rtx_REG (QImode, regno);
+
+  emit_insn (gen_rtx_SET (VOIDmode, reg, mem));
+}
+
 /*  Output RTL epilogue.  */
 
 void
-expand_epilogue (void)
+expand_epilogue (bool sibcall_p)
 {
   int reg;
   int live_seq;
@@ -895,6 +968,8 @@ expand_epilogue (void)
   /* epilogue: naked  */
   if (cfun->machine->is_naked)
     {
+      gcc_assert (!sibcall_p);
+      
       emit_jump_insn (gen_return ());
       return;
     }
@@ -933,13 +1008,12 @@ expand_epilogue (void)
               /* Try two methods to adjust stack and select shortest.  */
              rtx myfp;
              rtx fp_plus_insns;
-             rtx sp_plus_insns = NULL_RTX;
-             
-             if (TARGET_TINY_STACK)
+
+             if (AVR_HAVE_8BIT_SP)
                 {
                   /* The high byte (r29) doesn't change - prefer 'subi' 
                      (1 cycle) over 'sbiw' (2 cycles, same size).  */
-                  myfp = gen_rtx_REG (QImode, REGNO (frame_pointer_rtx));
+                  myfp = gen_rtx_REG (QImode, FRAME_POINTER_REGNUM);
                 }
               else 
                 {
@@ -950,13 +1024,10 @@ expand_epilogue (void)
               /* Method 1-Adjust frame pointer.  */
              start_sequence ();
 
-             emit_move_insn (myfp,
-                             gen_rtx_PLUS (HImode, myfp,
-                                           gen_int_mode (size, 
-                                                         GET_MODE(myfp))));
+             emit_move_insn (myfp, plus_constant (myfp, size));
 
              /* Copy to stack pointer.  */
-             if (TARGET_TINY_STACK)
+             if (AVR_HAVE_8BIT_SP)
                {
                  emit_move_insn (stack_pointer_rtx, frame_pointer_rtx);
                }
@@ -982,61 +1053,67 @@ expand_epilogue (void)
               /* Method 2-Adjust Stack pointer.  */
               if (size <= 5)
                 {
+                 rtx sp_plus_insns;
+
                  start_sequence ();
 
                  emit_move_insn (stack_pointer_rtx,
-                                 gen_rtx_PLUS (HImode, stack_pointer_rtx,
-                                               gen_int_mode (size, 
-                                                             HImode)));
+                                 plus_constant (stack_pointer_rtx, size));
 
                  sp_plus_insns = get_insns ();
                  end_sequence ();
-                }
 
-              /* Use shortest method.  */
-              if (size <= 5 && (get_sequence_length (sp_plus_insns) 
-                                < get_sequence_length (fp_plus_insns)))
-               emit_insn (sp_plus_insns);
-              else
+                 /* Use shortest method.  */
+                 if (get_sequence_length (sp_plus_insns) 
+                     < get_sequence_length (fp_plus_insns))
+                   emit_insn (sp_plus_insns);
+                 else
+                   emit_insn (fp_plus_insns);
+                }
+             else
                emit_insn (fp_plus_insns);
             }
          if (!(cfun->machine->is_OS_task || cfun->machine->is_OS_main))
            {
-              /* Restore previous frame_pointer.  */
-             emit_insn (gen_pophi (frame_pointer_rtx));
+              /* Restore previous frame_pointer.  See expand_prologue for
+                rationale for not using pophi.  */
+             emit_pop_byte (REG_Y + 1);
+             emit_pop_byte (REG_Y);
            }
        }
+
       /* Restore used registers.  */
       for (reg = 31; reg >= 0; --reg)
-        {
-          if (TEST_HARD_REG_BIT (set, reg))
-              emit_insn (gen_popqi (gen_rtx_REG (QImode, reg)));
-        }
+        if (TEST_HARD_REG_BIT (set, reg))
+          emit_pop_byte (reg);
+
       if (cfun->machine->is_interrupt || cfun->machine->is_signal)
         {
           /* Restore RAMPZ using tmp reg as scratch.  */
-         if(AVR_HAVE_RAMPZ 
-             && (TEST_HARD_REG_BIT (set, REG_Z) && TEST_HARD_REG_BIT (set, REG_Z + 1)))
+         if (AVR_HAVE_RAMPZ 
+              && TEST_HARD_REG_BIT (set, REG_Z)
+             && TEST_HARD_REG_BIT (set, REG_Z + 1))
             {
-             emit_insn (gen_popqi (tmp_reg_rtx));
-             emit_move_insn (gen_rtx_MEM(QImode, GEN_INT(RAMPZ_ADDR)), 
+             emit_pop_byte (TMP_REGNO);
+             emit_move_insn (gen_rtx_MEM (QImode, GEN_INT (RAMPZ_ADDR)), 
                              tmp_reg_rtx);
            }
 
           /* Restore SREG using tmp reg as scratch.  */
-          emit_insn (gen_popqi (tmp_reg_rtx));
+          emit_pop_byte (TMP_REGNO);
       
-          emit_move_insn (gen_rtx_MEM(QImode, GEN_INT(SREG_ADDR)), 
+          emit_move_insn (gen_rtx_MEM (QImode, GEN_INT (SREG_ADDR)), 
                          tmp_reg_rtx);
 
           /* Restore tmp REG.  */
-          emit_insn (gen_popqi (tmp_reg_rtx));
+          emit_pop_byte (TMP_REGNO);
 
           /* Restore zero REG.  */
-          emit_insn (gen_popqi (zero_reg_rtx));
+          emit_pop_byte (ZERO_REGNO);
         }
 
-      emit_jump_insn (gen_return ());
+      if (!sibcall_p)
+        emit_jump_insn (gen_return ());
     }
 }
 
@@ -1048,11 +1125,32 @@ avr_asm_function_begin_epilogue (FILE *file)
   fprintf (file, "/* epilogue start */\n");
 }
 
+
+/* Implement TARGET_CANNOT_MODITY_JUMPS_P */
+
+static bool
+avr_cannot_modify_jumps_p (void)
+{
+
+  /* Naked Functions must not have any instructions after
+     their epilogue, see PR42240 */
+     
+  if (reload_completed
+      && cfun->machine
+      && cfun->machine->is_naked)
+    {
+      return true;
+    }
+
+  return false;
+}
+
+
 /* Return nonzero if X (an RTX) is a legitimate memory address on the target
    machine for a memory operand of mode MODE.  */
 
-int
-legitimate_address_p (enum machine_mode mode, rtx x, int strict)
+bool
+avr_legitimate_address_p (enum machine_mode mode, rtx x, bool strict)
 {
   enum reg_class r = NO_REGS;
   
@@ -1075,6 +1173,8 @@ legitimate_address_p (enum machine_mode mode, rtx x, int strict)
                 true_regnum (XEXP (x, 0)));
       debug_rtx (x);
     }
+  if (!strict && GET_CODE (x) == SUBREG)
+       x = SUBREG_REG (x);
   if (REG_P (x) && (strict ? REG_OK_FOR_BASE_STRICT_P (x)
                     : REG_OK_FOR_BASE_NOSTRICT_P (x)))
     r = POINTER_REGS;
@@ -1089,6 +1189,7 @@ legitimate_address_p (enum machine_mode mode, rtx x, int strict)
       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;
@@ -1117,7 +1218,7 @@ legitimate_address_p (enum machine_mode mode, rtx x, int strict)
    memory address for an operand of mode MODE  */
 
 rtx
-legitimize_address (rtx x, rtx oldx, enum machine_mode mode)
+avr_legitimize_address (rtx x, rtx oldx, enum machine_mode mode)
 {
   x = oldx;
   if (TARGET_ALL_DEBUG)
@@ -1215,12 +1316,35 @@ print_operand_address (FILE *file, rtx addr)
 
     default:
       if (CONSTANT_ADDRESS_P (addr)
-         && ((GET_CODE (addr) == SYMBOL_REF && SYMBOL_REF_FUNCTION_P (addr))
-             || GET_CODE (addr) == LABEL_REF))
+         && text_segment_operand (addr, VOIDmode))
        {
-         fprintf (file, "gs(");
-         output_addr_const (file,addr);
-         fprintf (file ,")");
+         rtx x = addr;
+         if (GET_CODE (x) == CONST)
+           x = XEXP (x, 0);
+         if (GET_CODE (x) == PLUS && GET_CODE (XEXP (x,1)) == CONST_INT)
+           {
+             /* Assembler gs() will implant word address. Make offset 
+                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)));
+             if (AVR_3_BYTE_PC)
+               if (warning (0, "pointer offset from symbol maybe incorrect"))
+                 {
+                   output_addr_const (stderr, addr);
+                   fprintf(stderr,"\n");
+                 }
+           }
+         else
+           {
+             fprintf (file, "gs(");
+             output_addr_const (file, addr);
+             fprintf (file, ")");
+           }
        }
       else
        output_addr_const (file, addr);
@@ -1260,12 +1384,18 @@ print_operand (FILE *file, rtx x, int code)
   else if (GET_CODE (x) == MEM)
     {
       rtx addr = XEXP (x,0);
-
-      if (CONSTANT_P (addr) && abcd)
+      if (code == 'm')
        {
-         fputc ('(', file);
-         output_address (addr);
-         fprintf (file, ")+%d", abcd);
+          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 == 'o')
        {
@@ -1296,6 +1426,18 @@ print_operand (FILE *file, rtx x, int code)
       else
        print_operand_address (file, addr);
     }
+  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"))
+         {
+           output_addr_const (stderr, x);
+           fprintf(stderr,"\n");
+         }
+      /* Use normal symbol for direct address no linker trampoline needed */
+      output_addr_const (file, x);
+    }
   else if (GET_CODE (x) == CONST_DOUBLE)
     {
       long val;
@@ -1393,7 +1535,7 @@ notice_update_cc (rtx body ATTRIBUTE_UNUSED, rtx insn)
    class CLASS needed to hold a value of mode MODE.  */
 
 int
-class_max_nregs (enum reg_class class ATTRIBUTE_UNUSED,enum machine_mode mode)
+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);
 }
@@ -1406,7 +1548,7 @@ class_max_nregs (enum reg_class class ATTRIBUTE_UNUSED,enum machine_mode mode)
 int
 avr_jump_mode (rtx x, rtx insn)
 {
-  int dest_addr = INSN_ADDRESSES (INSN_UID (GET_MODE (x) == LABEL_REF
+  int dest_addr = INSN_ADDRESSES (INSN_UID (GET_CODE (x) == LABEL_REF
                                            ? XEXP (x, 0) : x));
   int cur_addr = INSN_ADDRESSES (INSN_UID (insn));
   int jump_distance = cur_addr - dest_addr;
@@ -1532,38 +1674,30 @@ byte_immediate_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
           && INTVAL (op) <= 0xff && INTVAL (op) >= 0);
 }
 
-/* Output all insn addresses and their sizes into the assembly language
-   output file.  This is helpful for debugging whether the length attributes
-   in the md file are correct.
-   Output insn cost for next insn.  */
+/* Output insn cost for next insn.  */
 
 void
 final_prescan_insn (rtx insn, rtx *operand ATTRIBUTE_UNUSED,
                    int num_operands ATTRIBUTE_UNUSED)
 {
-  int uid = INSN_UID (insn);
-
-  if (TARGET_INSN_SIZE_DUMP || TARGET_ALL_DEBUG)
+  if (TARGET_ALL_DEBUG)
     {
-      fprintf (asm_out_file, "/*DEBUG: 0x%x\t\t%d\t%d */\n",
-              INSN_ADDRESSES (uid),
-               INSN_ADDRESSES (uid) - last_insn_address,
-              rtx_cost (PATTERN (insn), INSN));
+      fprintf (asm_out_file, "/* DEBUG: cost = %d.  */\n",
+              rtx_cost (PATTERN (insn), INSN, !optimize_size));
     }
-  last_insn_address = INSN_ADDRESSES (uid);
 }
 
 /* Return 0 if undefined, 1 if always true or always false.  */
 
 int
-avr_simplify_comparison_p (enum machine_mode mode, RTX_CODE operator, rtx x)
+avr_simplify_comparison_p (enum machine_mode mode, RTX_CODE op, rtx x)
 {
   unsigned int max = (mode == QImode ? 0xff :
                       mode == HImode ? 0xffff :
                       mode == SImode ? 0xffffffff : 0);
-  if (max && operator && GET_CODE (x) == CONST_INT)
+  if (max && op && GET_CODE (x) == CONST_INT)
     {
-      if (unsigned_condition (operator) != operator)
+      if (unsigned_condition (op) != op)
        max >>= 1;
 
       if (max != (INTVAL (x) & max)
@@ -1592,20 +1726,18 @@ init_cumulative_args (CUMULATIVE_ARGS *cum, tree fntype, rtx libname,
 {
   cum->nregs = 18;
   cum->regno = FIRST_CUM_REG;
-  if (!libname && fntype)
-    {
-      int stdarg = (TYPE_ARG_TYPES (fntype) != 0
-                    && (TREE_VALUE (tree_last (TYPE_ARG_TYPES (fntype)))
-                        != void_type_node));
-      if (stdarg)
-        cum->nregs = 0;
-    }
+  if (!libname && stdarg_p (fntype))
+    cum->nregs = 0;
+
+  /* Assume the calle may be tail called */
+  
+  cfun->machine->sibcall_fails = 0;
 }
 
 /* Returns the number of registers to allocate for a function argument.  */
 
 static int
-avr_num_arg_regs (enum machine_mode mode, tree type)
+avr_num_arg_regs (enum machine_mode mode, const_tree type)
 {
   int size;
 
@@ -1623,9 +1755,9 @@ avr_num_arg_regs (enum machine_mode mode, tree type)
 /* Controls whether a function argument is passed
    in a register, and which register.  */
 
-rtx
-function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode, tree type,
-             int named ATTRIBUTE_UNUSED)
+static rtx
+avr_function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode,
+                 const_tree type, bool named ATTRIBUTE_UNUSED)
 {
   int bytes = avr_num_arg_regs (mode, type);
 
@@ -1638,15 +1770,46 @@ function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode, tree type,
 /* Update the summarizer variable CUM to advance past an argument
    in the argument list.  */
    
-void
-function_arg_advance (CUMULATIVE_ARGS *cum, enum machine_mode mode, tree type,
-                     int named ATTRIBUTE_UNUSED)
+static void
+avr_function_arg_advance (CUMULATIVE_ARGS *cum, enum machine_mode mode,
+                         const_tree type, bool named ATTRIBUTE_UNUSED)
 {
   int bytes = avr_num_arg_regs (mode, type);
 
   cum->nregs -= bytes;
   cum->regno -= bytes;
 
+  /* A parameter is being passed in a call-saved register. As the original
+     contents of these regs has to be restored before leaving the function,
+     a function must not pass arguments in call-saved regs in order to get
+     tail-called. */
+  
+  if (cum->regno >= 0
+      && !call_used_regs[cum->regno])
+    {
+      /* FIXME: We ship info on failing tail-call in struct machine_function.
+         This uses internals of calls.c:expand_call() and the way args_so_far
+         is used. targetm.function_ok_for_sibcall() needs to be extended to
+         pass &args_so_far, too. At present, CUMULATIVE_ARGS is target
+         dependent so that such an extension is not wanted. */
+      
+      cfun->machine->sibcall_fails = 1;
+    }
+
+  /* Test if all registers needed by the ABI are actually available.  If the
+     user has fixed a GPR needed to pass an argument, an (implicit) function
+     call would clobber that fixed register.  See PR45099 for an example.  */
+  
+  if (cum->regno >= 0)
+    {
+      int regno;
+
+      for (regno = cum->regno; regno < cum->regno + bytes; regno++)
+        if (fixed_regs[regno])
+          error ("Register %s is needed to pass a parameter but is fixed",
+                 reg_names[regno]);
+    }
+      
   if (cum->nregs <= 0)
     {
       cum->nregs = 0;
@@ -1654,6 +1817,65 @@ function_arg_advance (CUMULATIVE_ARGS *cum, enum machine_mode mode, tree type,
     }
 }
 
+/* Implement `TARGET_FUNCTION_OK_FOR_SIBCALL' */
+/* Decide whether we can make a sibling call to a function.  DECL is the
+   declaration of the function being targeted by the call and EXP is the
+   CALL_EXPR representing the call. */
+
+static bool
+avr_function_ok_for_sibcall (tree decl_callee, tree exp_callee)
+{
+  tree fntype_callee;
+
+  /* Tail-calling must fail if callee-saved regs are used to pass
+     function args.  We must not tail-call when `epilogue_restores'
+     is used.  Unfortunately, we cannot tell at this point if that
+     actually will happen or not, and we cannot step back from
+     tail-calling. Thus, we inhibit tail-calling with -mcall-prologues. */
+  
+  if (cfun->machine->sibcall_fails
+      || TARGET_CALL_PROLOGUES)
+    {
+      return false;
+    }
+  
+  fntype_callee = TREE_TYPE (CALL_EXPR_FN (exp_callee));
+
+  if (decl_callee)
+    {
+      decl_callee = TREE_TYPE (decl_callee);
+    }
+  else
+    {
+      decl_callee = fntype_callee;
+      
+      while (FUNCTION_TYPE != TREE_CODE (decl_callee)
+             && METHOD_TYPE != TREE_CODE (decl_callee))
+        {
+          decl_callee = TREE_TYPE (decl_callee);
+        }
+    }
+
+  /* Ensure that caller and callee have compatible epilogues */
+  
+  if (interrupt_function_p (current_function_decl)
+      || signal_function_p (current_function_decl)
+      || avr_naked_function_p (decl_callee)
+      || avr_naked_function_p (current_function_decl)
+      /* FIXME: For OS_task and OS_main, we are over-conservative.
+         This is due to missing documentation of these attributes
+         and what they actually should do and should not do. */
+      || (avr_OS_task_function_p (decl_callee)
+          != avr_OS_task_function_p (current_function_decl))
+      || (avr_OS_main_function_p (decl_callee)
+          != avr_OS_main_function_p (current_function_decl)))
+    {
+      return false;
+    }
+  return true;
+}
+
 /***********************************************************************
   Functions for outputting various mov's for a various modes
 ************************************************************************/
@@ -1733,15 +1955,15 @@ output_movqi (rtx insn, rtx operands[], int *l)
     }
   else if (GET_CODE (dest) == MEM)
     {
-      const char *template;
+      const char *templ;
 
       if (src == const0_rtx)
        operands[1] = zero_reg_rtx;
 
-      template = out_movqi_mr_r (insn, operands, real_l);
+      templ = out_movqi_mr_r (insn, operands, real_l);
 
       if (!real_l)
-       output_asm_insn (template, operands);
+       output_asm_insn (templ, operands);
 
       operands[1] = src;
     }
@@ -1766,7 +1988,7 @@ output_movhi (rtx insn, rtx operands[], int *l)
        {
          if (test_hard_reg_class (STACK_REG, dest))
            {
-             if (TARGET_TINY_STACK)
+             if (AVR_HAVE_8BIT_SP)
                return *l = 1, AS2 (out,__SP_L__,%A1);
               /* Use simple load of stack pointer if no interrupts are 
                 used.  */
@@ -1883,15 +2105,15 @@ output_movhi (rtx insn, rtx operands[], int *l)
     }
   else if (GET_CODE (dest) == MEM)
     {
-      const char *template;
+      const char *templ;
 
       if (src == const0_rtx)
        operands[1] = zero_reg_rtx;
 
-      template = out_movhi_mr_r (insn, operands, real_l);
+      templ = out_movhi_mr_r (insn, operands, real_l);
 
       if (!real_l)
-       output_asm_insn (template, operands);
+       output_asm_insn (templ, operands);
 
       operands[1] = src;
       return "";
@@ -1921,10 +2143,10 @@ out_movqi_r_mr (rtx insn, rtx op[], int *l)
       if (optimize > 0 && io_address_operand (x, QImode))
        {
          *l = 1;
-         return AS2 (in,%0,%1-0x20);
+         return AS2 (in,%0,%m1-0x20);
        }
       *l = 2;
-      return AS2 (lds,%0,%1);
+      return AS2 (lds,%0,%m1);
     }
   /* memory access by reg+disp */
   else if (GET_CODE (x) == PLUS
@@ -2109,12 +2331,12 @@ out_movhi_r_mr (rtx insn, rtx op[], int *l)
       if (optimize > 0 && io_address_operand (base, HImode))
        {
          *l = 2;
-         return (AS2 (in,%A0,%A1-0x20) CR_TAB
-                 AS2 (in,%B0,%B1-0x20));
+         return (AS2 (in,%A0,%m1-0x20) CR_TAB
+                 AS2 (in,%B0,%m1+1-0x20));
        }
       *l = 4;
-      return (AS2 (lds,%A0,%A1) CR_TAB
-             AS2 (lds,%B0,%B1));
+      return (AS2 (lds,%A0,%m1) CR_TAB
+             AS2 (lds,%B0,%m1+1));
     }
   
   fatal_insn ("unknown move insn:",insn);
@@ -2273,10 +2495,10 @@ out_movsi_r_mr (rtx insn, rtx op[], int *l)
                  AS2 (ld,%C0,%1) CR_TAB
                  AS2 (ld,%D0,%1));
   else if (CONSTANT_ADDRESS_P (base))
-      return *l=8, (AS2 (lds,%A0,%A1) CR_TAB
-                   AS2 (lds,%B0,%B1) CR_TAB
-                   AS2 (lds,%C0,%C1) CR_TAB
-                   AS2 (lds,%D0,%D1));
+      return *l=8, (AS2 (lds,%A0,%m1) CR_TAB
+                   AS2 (lds,%B0,%m1+1) CR_TAB
+                   AS2 (lds,%C0,%m1+2) CR_TAB
+                   AS2 (lds,%D0,%m1+3));
     
   fatal_insn ("unknown move insn:",insn);
   return "";
@@ -2296,10 +2518,10 @@ out_movsi_mr_r (rtx insn, rtx op[], int *l)
     l = &tmp;
   
   if (CONSTANT_ADDRESS_P (base))
-    return *l=8,(AS2 (sts,%A0,%A1) CR_TAB
-                AS2 (sts,%B0,%B1) CR_TAB
-                AS2 (sts,%C0,%C1) CR_TAB
-                AS2 (sts,%D0,%D1));
+    return *l=8,(AS2 (sts,%m0,%A1) CR_TAB
+                AS2 (sts,%m0+1,%B1) CR_TAB
+                AS2 (sts,%m0+2,%C1) CR_TAB
+                AS2 (sts,%m0+3,%D1));
   if (reg_base > 0)                 /* (r) */
     {
       if (reg_base == REG_X)                /* (R26) */
@@ -2571,15 +2793,15 @@ output_movsisf(rtx insn, rtx operands[], int *l)
     }
   else if (GET_CODE (dest) == MEM)
     {
-      const char *template;
+      const char *templ;
 
       if (src == const0_rtx)
          operands[1] = zero_reg_rtx;
 
-      template = out_movsi_mr_r (insn, operands, real_l);
+      templ = out_movsi_mr_r (insn, operands, real_l);
 
       if (!real_l)
-       output_asm_insn (template, operands);
+       output_asm_insn (templ, operands);
 
       operands[1] = src;
       return "";
@@ -2609,10 +2831,10 @@ out_movqi_mr_r (rtx insn, rtx op[], int *l)
       if (optimize > 0 && io_address_operand (x, QImode))
        {
          *l = 1;
-         return AS2 (out,%0-0x20,%1);
+         return AS2 (out,%m0-0x20,%1);
        }
       *l = 2;
-      return AS2 (sts,%0,%1);
+      return AS2 (sts,%m0,%1);
     }
   /* memory access by reg+disp */
   else if (GET_CODE (x) == PLUS        
@@ -2688,11 +2910,11 @@ out_movhi_mr_r (rtx insn, rtx op[], int *l)
       if (optimize > 0 && io_address_operand (base, HImode))
        {
          *l = 2;
-         return (AS2 (out,%B0-0x20,%B1) CR_TAB
-                 AS2 (out,%A0-0x20,%A1));
+         return (AS2 (out,%m0+1-0x20,%B1) CR_TAB
+                 AS2 (out,%m0-0x20,%A1));
        }
-      return *l = 4, (AS2 (sts,%B0,%B1) CR_TAB
-                     AS2 (sts,%A0,%A1));
+      return *l = 4, (AS2 (sts,%m0+1,%B1) CR_TAB
+                     AS2 (sts,%m0,%A1));
     }
   if (reg_base > 0)
     {
@@ -2807,8 +3029,8 @@ out_movhi_mr_r (rtx insn, rtx op[], int *l)
 
 /* Return 1 if frame pointer for current function required.  */
 
-int
-frame_pointer_required_p (void)
+bool
+avr_frame_pointer_required_p (void)
 {
   return (cfun->calls_alloca
          || crtl->args.info.nregs == 0
@@ -2864,21 +3086,21 @@ compare_eq_p (rtx insn)
 /* Output test instruction for HImode.  */
 
 const char *
-out_tsthi (rtx insn, int *l)
+out_tsthi (rtx insn, rtx op, int *l)
 {
   if (compare_sign_p (insn))
     {
       if (l) *l = 1;
       return AS1 (tst,%B0);
     }
-  if (reg_unused_after (insn, SET_SRC (PATTERN (insn)))
+  if (reg_unused_after (insn, op)
       && compare_eq_p (insn))
     {
       /* Faster than sbiw if we can clobber the operand.  */
       if (l) *l = 1;
-      return AS2 (or,%A0,%B0);
+      return "or %A0,%B0";
     }
-  if (test_hard_reg_class (ADDW_REGS, SET_SRC (PATTERN (insn))))
+  if (test_hard_reg_class (ADDW_REGS, op))
     {
       if (l) *l = 1;
       return AS2 (sbiw,%0,0);
@@ -2892,14 +3114,14 @@ out_tsthi (rtx insn, int *l)
 /* Output test instruction for SImode.  */
 
 const char *
-out_tstsi (rtx insn, int *l)
+out_tstsi (rtx insn, rtx op, int *l)
 {
   if (compare_sign_p (insn))
     {
       if (l) *l = 1;
       return AS1 (tst,%D0);
     }
-  if (test_hard_reg_class (ADDW_REGS, SET_SRC (PATTERN (insn))))
+  if (test_hard_reg_class (ADDW_REGS, op))
     {
       if (l) *l = 3;
       return (AS2 (sbiw,%A0,0) CR_TAB
@@ -2920,7 +3142,7 @@ out_tstsi (rtx insn, int *l)
    carefully hand-optimized in ?sh??i3_out.  */
 
 void
-out_shift_with_cnt (const char *template, rtx insn, rtx operands[],
+out_shift_with_cnt (const char *templ, rtx insn, rtx operands[],
                    int *len, int t_len)
 {
   rtx op[10];
@@ -2965,7 +3187,7 @@ out_shift_with_cnt (const char *template, rtx insn, rtx operands[],
          else
            {
              while (count-- > 0)
-               output_asm_insn (template, op);
+               output_asm_insn (templ, op);
            }
 
          return;
@@ -3046,7 +3268,7 @@ out_shift_with_cnt (const char *template, rtx insn, rtx operands[],
   else
     {
       strcat (str, "\n1:\t");
-      strcat (str, template);
+      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);
@@ -3199,9 +3421,9 @@ ashlhi3_out (rtx insn, rtx operands[], int *len)
              return (AS1 (swap,%A0)    CR_TAB
                      AS1 (swap,%B0)    CR_TAB
                      AS2 (ldi,%3,0xf0) CR_TAB
-                     AS2 (and,%B0,%3)  CR_TAB
+                     "and %B0,%3"      CR_TAB
                      AS2 (eor,%B0,%A0) CR_TAB
-                     AS2 (and,%A0,%3)  CR_TAB
+                     "and %A0,%3"      CR_TAB
                      AS2 (eor,%B0,%A0));
            }
          break;  /* optimize_size ? 6 : 8 */
@@ -3229,9 +3451,9 @@ ashlhi3_out (rtx insn, rtx operands[], int *len)
                      AS1 (swap,%A0)    CR_TAB
                      AS1 (swap,%B0)    CR_TAB
                      AS2 (ldi,%3,0xf0) CR_TAB
-                     AS2 (and,%B0,%3)  CR_TAB
+                     "and %B0,%3"      CR_TAB
                      AS2 (eor,%B0,%A0) CR_TAB
-                     AS2 (and,%A0,%3)  CR_TAB
+                     "and %A0,%3"      CR_TAB
                      AS2 (eor,%B0,%A0));
            }
          break;  /* 10 */
@@ -3299,7 +3521,7 @@ ashlhi3_out (rtx insn, rtx operands[], int *len)
                      AS1 (clr,%A0)     CR_TAB
                      AS1 (swap,%B0)    CR_TAB
                      AS2 (ldi,%3,0xf0) CR_TAB
-                     AS2 (and,%B0,%3));
+                     "and %B0,%3");
            }
          *len = 6;
          return (AS2 (mov,%B0,%A0) CR_TAB
@@ -3338,7 +3560,7 @@ ashlhi3_out (rtx insn, rtx operands[], int *len)
                      AS1 (swap,%B0)    CR_TAB
                      AS1 (lsl,%B0)     CR_TAB
                      AS2 (ldi,%3,0xe0) CR_TAB
-                     AS2 (and,%B0,%3));
+                     "and %B0,%3");
            }
          if (AVR_HAVE_MUL)
            {
@@ -3976,9 +4198,9 @@ lshrhi3_out (rtx insn, rtx operands[], int *len)
              return (AS1 (swap,%B0)    CR_TAB
                      AS1 (swap,%A0)    CR_TAB
                      AS2 (ldi,%3,0x0f) CR_TAB
-                     AS2 (and,%A0,%3)  CR_TAB
+                     "and %A0,%3"      CR_TAB
                      AS2 (eor,%A0,%B0) CR_TAB
-                     AS2 (and,%B0,%3)  CR_TAB
+                     "and %B0,%3"      CR_TAB
                      AS2 (eor,%A0,%B0));
            }
          break;  /* optimize_size ? 6 : 8 */
@@ -4006,9 +4228,9 @@ lshrhi3_out (rtx insn, rtx operands[], int *len)
                      AS1 (swap,%B0)    CR_TAB
                      AS1 (swap,%A0)    CR_TAB
                      AS2 (ldi,%3,0x0f) CR_TAB
-                     AS2 (and,%A0,%3)  CR_TAB
+                     "and %A0,%3"      CR_TAB
                      AS2 (eor,%A0,%B0) CR_TAB
-                     AS2 (and,%B0,%3)  CR_TAB
+                     "and %B0,%3"      CR_TAB
                      AS2 (eor,%A0,%B0));
            }
          break;  /* 10 */
@@ -4076,7 +4298,7 @@ lshrhi3_out (rtx insn, rtx operands[], int *len)
                      AS1 (clr,%B0)     CR_TAB
                      AS1 (swap,%A0)    CR_TAB
                      AS2 (ldi,%3,0x0f) CR_TAB
-                     AS2 (and,%A0,%3));
+                     "and %A0,%3");
            }
          *len = 6;
          return (AS2 (mov,%A0,%B0) CR_TAB
@@ -4115,7 +4337,7 @@ lshrhi3_out (rtx insn, rtx operands[], int *len)
                      AS1 (swap,%A0)    CR_TAB
                      AS1 (lsr,%A0)     CR_TAB
                      AS2 (ldi,%3,0x07) CR_TAB
-                     AS2 (and,%A0,%3));
+                     "and %A0,%3");
            }
          if (AVR_HAVE_MUL)
            {
@@ -4279,17 +4501,161 @@ lshrsi3_out (rtx insn, rtx operands[], int *len)
   return "";
 }
 
-/* Modifies the length assigned to instruction INSN
- LEN is the initially computed length of the insn.  */
+/* 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.  */
 
-int
-adjust_insn_length (rtx insn, int len)
+bool
+avr_rotate_bytes (rtx operands[])
 {
-  rtx patt = PATTERN (insn);
-  rtx set;
+    int i, j;
+    enum machine_mode mode = GET_MODE (operands[0]);
+    bool overlapped = reg_overlap_mentioned_p (operands[0], operands[1]);
+    bool same_reg = rtx_equal_p (operands[0], operands[1]);
+    int num = INTVAL (operands[2]);
+    rtx scratch = operands[3];
+    /* Work out if byte or word move is needed.  Odd byte rotates need QImode.
+       Word move if no scratch is needed, otherwise use size of scratch.  */
+    enum machine_mode move_mode = QImode;
+    int move_size, offset, size;
+
+    if (num & 0xf)
+      move_mode = QImode;
+    else if ((mode == SImode && !same_reg) || !overlapped)
+      move_mode = HImode;
+    else
+      move_mode = GET_MODE (scratch);
+
+    /* Force DI rotate to use QI moves since other DI moves are currently split
+       into QI moves so forward propagation works better.  */
+    if (mode == DImode)
+      move_mode = QImode;
+    /* Make scratch smaller if needed.  */
+    if (GET_MODE (scratch) == HImode && move_mode == QImode)
+      scratch = simplify_gen_subreg (move_mode, scratch, HImode, 0); 
+
+    move_size = GET_MODE_SIZE (move_mode);
+    /* Number of bytes/words to rotate.  */
+    offset = (num  >> 3) / move_size;
+    /* Number of moves needed.  */
+    size = GET_MODE_SIZE (mode) / move_size;
+    /* Himode byte swap is special case to avoid a scratch register.  */
+    if (mode == HImode && same_reg)
+      {
+       /* HImode byte swap, using xor.  This is as quick as using scratch.  */
+       rtx src, dst;
+       src = simplify_gen_subreg (move_mode, operands[1], mode, 0);
+       dst = simplify_gen_subreg (move_mode, operands[0], mode, 1);
+       if (!rtx_equal_p (dst, src))
+         {
+            emit_move_insn (dst, gen_rtx_XOR (QImode, dst, src));
+            emit_move_insn (src, gen_rtx_XOR (QImode, src, dst));
+            emit_move_insn (dst, gen_rtx_XOR (QImode, dst, src));
+         }
+      }    
+    else  
+      {
+#define MAX_SIZE 8 /* GET_MODE_SIZE (DImode) / GET_MODE_SIZE (QImode)  */
+       /* Create linked list of moves to determine move order.  */
+       struct {
+         rtx src, dst;
+         int links;
+       } move[MAX_SIZE + 8];
+       int blocked, moves;
+
+       gcc_assert (size <= MAX_SIZE);
+       /* Generate list of subreg moves.  */
+       for (i = 0; i < size; i++)
+         {
+           int from = i;
+           int to = (from + offset) % size;          
+           move[i].src = simplify_gen_subreg (move_mode, operands[1],
+                                               mode, from * move_size);
+           move[i].dst = simplify_gen_subreg (move_mode, operands[0],
+                                               mode, to   * move_size);
+           move[i].links = -1;
+          }
+       /* Mark dependence where a dst of one move is the src of another move.
+          The first move is a conflict as it must wait until second is
+          performed.  We ignore moves to self - we catch this later.  */
+       if (overlapped)
+         for (i = 0; i < size; i++)
+           if (reg_overlap_mentioned_p (move[i].dst, operands[1]))
+             for (j = 0; j < size; j++)
+               if (j != i && rtx_equal_p (move[j].src, move[i].dst))
+                 {
+                   /* The dst of move i is the src of move j.  */
+                   move[i].links = j;
+                   break;
+                 }
+
+       blocked = -1;
+       moves = 0;
+       /* Go through move list and perform non-conflicting moves.  As each
+          non-overlapping move is made, it may remove other conflicts
+          so the process is repeated until no conflicts remain.  */
+       do
+         {
+           blocked = -1;
+           moves = 0;
+           /* Emit move where dst is not also a src or we have used that
+              src already.  */
+           for (i = 0; i < size; i++)
+             if (move[i].src != NULL_RTX)
+               {
+                 if (move[i].links == -1
+                     || move[move[i].links].src == NULL_RTX)
+                   {
+                     moves++;
+                     /* Ignore NOP moves to self.  */
+                     if (!rtx_equal_p (move[i].dst, move[i].src))
+                       emit_move_insn (move[i].dst, move[i].src);
 
-  if (GET_CODE (patt) == SET)
-    {
+                     /* Remove  conflict from list.  */
+                     move[i].src = NULL_RTX;
+                   }
+                 else
+                   blocked = i;
+               }
+
+           /* Check for deadlock. This is when no moves occurred and we have
+              at least one blocked move.  */
+           if (moves == 0 && blocked != -1)
+             {
+               /* Need to use scratch register to break deadlock.
+                  Add move to put dst of blocked move into scratch.
+                  When this move occurs, it will break chain deadlock.
+                  The scratch register is substituted for real move.  */
+
+               move[size].src = move[blocked].dst;
+               move[size].dst =  scratch;
+               /* Scratch move is never blocked.  */
+               move[size].links = -1; 
+               /* Make sure we have valid link.  */
+               gcc_assert (move[blocked].links != -1);
+               /* Replace src of  blocking move with scratch reg.  */
+               move[move[blocked].links].src = scratch;
+               /* Make dependent on scratch move occuring.  */
+               move[blocked].links = size; 
+               size=size+1;
+             }
+         }
+       while (blocked != -1);
+      }
+    return true;
+}
+
+/* Modifies the length assigned to instruction INSN
+ LEN is the initially computed length of the insn.  */
+
+int
+adjust_insn_length (rtx insn, int len)
+{
+  rtx patt = PATTERN (insn);
+  rtx set;
+
+  if (GET_CODE (patt) == SET)
+    {
       rtx op[10];
       op[1] = SET_SRC (patt);
       op[0] = SET_DEST (patt);
@@ -4316,8 +4682,8 @@ adjust_insn_length (rtx insn, int len)
        {
          switch (GET_MODE (op[1]))
            {
-           case HImode: out_tsthi (insn,&len); break;
-           case SImode: out_tstsi (insn,&len); break;
+           case HImode: out_tsthi (insn, op[1], &len); break;
+           case SImode: out_tstsi (insn, op[1], &len); break;
            default: break;
            }
        }
@@ -4549,8 +4915,7 @@ static bool
 avr_assemble_integer (rtx x, unsigned int size, int aligned_p)
 {
   if (size == POINTER_SIZE / BITS_PER_UNIT && aligned_p
-      && ((GET_CODE (x) == SYMBOL_REF && SYMBOL_REF_FUNCTION_P (x))
-         || GET_CODE (x) == LABEL_REF))
+      && text_segment_operand (x, VOIDmode) )
     {
       fputs ("\t.word\tgs(", asm_out_file);
       output_addr_const (asm_out_file, x);
@@ -4560,6 +4925,39 @@ avr_assemble_integer (rtx x, unsigned int size, int aligned_p)
   return default_assemble_integer (x, size, aligned_p);
 }
 
+/* Worker function for ASM_DECLARE_FUNCTION_NAME.  */
+
+void
+avr_asm_declare_function_name (FILE *file, const char *name, tree decl)
+{
+
+  /* If the function has the 'signal' or 'interrupt' attribute, test to
+     make sure that the name of the function is "__vector_NN" so as to
+     catch when the user misspells the interrupt vector name.  */
+
+  if (cfun->machine->is_interrupt)
+    {
+      if (strncmp (name, "__vector", strlen ("__vector")) != 0)
+        {
+          warning_at (DECL_SOURCE_LOCATION (decl), 0,
+                      "%qs appears to be a misspelled interrupt handler",
+                      name);
+        }
+    }
+  else if (cfun->machine->is_signal)
+    {
+      if (strncmp (name, "__vector", strlen ("__vector")) != 0)
+        {
+           warning_at (DECL_SOURCE_LOCATION (decl), 0,
+                       "%qs appears to be a misspelled signal handler",
+                       name);
+        }
+    }
+
+  ASM_OUTPUT_TYPE_DIRECTIVE (file, name, "function");
+  ASM_OUTPUT_LABEL (file, name);
+}
+
 /* The routine used to output NUL terminated strings.  We use a special
    version of this for most svr4 targets because doing so makes the
    generated assembly code more compact (and thus faster to assemble)
@@ -4658,8 +5056,8 @@ gas_output_ascii(FILE *file, const char *str, size_t length)
    assigned to registers of class CLASS would likely be spilled
    because registers of CLASS are needed for spill registers.  */
 
-enum reg_class
-class_likely_spilled_p (int c)
+static bool
+avr_class_likely_spilled_p (reg_class_t c)
 {
   return (c != ALL_REGS && c != ADDW_REGS);
 }
@@ -4674,18 +5072,6 @@ class_likely_spilled_p (int c)
 
    Only `progmem' attribute valid for type.  */
 
-const struct attribute_spec avr_attribute_table[] =
-{
-  /* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler } */
-  { "progmem",   0, 0, false, false, false,  avr_handle_progmem_attribute },
-  { "signal",    0, 0, true,  false, false,  avr_handle_fndecl_attribute },
-  { "interrupt", 0, 0, true,  false, false,  avr_handle_fndecl_attribute },
-  { "naked",     0, 0, false, true,  true,   avr_handle_fntype_attribute },
-  { "OS_task",   0, 0, false, true,  true,   avr_handle_fntype_attribute },
-  { "OS_main",   0, 0, false, true,  true,   avr_handle_fntype_attribute },
-  { NULL,        0, 0, false, false, false, NULL }
-};
-
 /* Handle a "progmem" attribute; arguments as in
    struct attribute_spec.handler.  */
 static tree
@@ -4720,8 +5106,8 @@ avr_handle_progmem_attribute (tree *node, tree name,
        }
       else
        {
-         warning (OPT_Wattributes, "%qs attribute ignored",
-                  IDENTIFIER_POINTER (name));
+         warning (OPT_Wattributes, "%qE attribute ignored",
+                  name);
          *no_add_attrs = true;
        }
     }
@@ -4740,36 +5126,10 @@ avr_handle_fndecl_attribute (tree *node, tree name,
 {
   if (TREE_CODE (*node) != FUNCTION_DECL)
     {
-      warning (OPT_Wattributes, "%qs attribute only applies to functions",
-              IDENTIFIER_POINTER (name));
+      warning (OPT_Wattributes, "%qE attribute only applies to functions",
+              name);
       *no_add_attrs = true;
     }
-  else
-    {
-      const char *func_name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (*node));
-      const char *attr = IDENTIFIER_POINTER (name);
-
-      /* If the function has the 'signal' or 'interrupt' attribute, test to
-         make sure that the name of the function is "__vector_NN" so as to
-         catch when the user misspells the interrupt vector name.  */
-
-      if (strncmp (attr, "interrupt", strlen ("interrupt")) == 0)
-        {
-          if (strncmp (func_name, "__vector", strlen ("__vector")) != 0)
-            {
-              warning (0, "%qs appears to be a misspelled interrupt handler",
-                       func_name);
-            }
-        }
-      else if (strncmp (attr, "signal", strlen ("signal")) == 0)
-        {
-          if (strncmp (func_name, "__vector", strlen ("__vector")) != 0)
-            {
-              warning (0, "%qs appears to be a misspelled signal handler",
-                       func_name);
-            }
-        }
-    }
 
   return NULL_TREE;
 }
@@ -4782,8 +5142,8 @@ avr_handle_fntype_attribute (tree *node, tree name,
 {
   if (TREE_CODE (*node) != FUNCTION_TYPE)
     {
-      warning (OPT_Wattributes, "%qs attribute only applies to functions",
-              IDENTIFIER_POINTER (name));
+      warning (OPT_Wattributes, "%qE attribute only applies to functions",
+              name);
       *no_add_attrs = true;
     }
 
@@ -4828,14 +5188,20 @@ avr_insert_attributes (tree node, tree *attributes)
       && (TREE_STATIC (node) || DECL_EXTERNAL (node))
       && avr_progmem_p (node, *attributes))
     {
-      static const char dsec[] = ".progmem.data";
-      *attributes = tree_cons (get_identifier ("section"),
-               build_tree_list (NULL, build_string (strlen (dsec), dsec)),
-               *attributes);
+      if (TREE_READONLY (node)) 
+        {
+          static const char dsec[] = ".progmem.data";
 
-      /* ??? This seems sketchy.  Why can't the user declare the
-        thing const in the first place?  */
-      TREE_READONLY (node) = 1;
+          *attributes = tree_cons (get_identifier ("section"),
+                                   build_tree_list (NULL, build_string (strlen (dsec), dsec)),
+                                   *attributes);
+        }
+      else
+        {
+          error ("variable %q+D must be const in order to be put into"
+                 " read-only section by means of %<__attribute__((progmem))%>",
+                 node);
+        }
     }
 }
 
@@ -4851,7 +5217,60 @@ avr_output_progmem_section_asm_op (const void *arg ATTRIBUTE_UNUSED)
   fprintf (asm_out_file, "\t.p2align 1\n");
 }
 
-/* Implement TARGET_ASM_INIT_SECTIONS.  */
+
+/* Implement `ASM_OUTPUT_ALIGNED_DECL_LOCAL'.  */
+/* Implement `ASM_OUTPUT_ALIGNED_DECL_COMMON'.  */
+/* Track need of __do_clear_bss.  */
+
+void
+avr_asm_output_aligned_decl_common (FILE * stream, const_tree decl ATTRIBUTE_UNUSED,
+                                    const char *name, unsigned HOST_WIDE_INT size,
+                                    unsigned int align, bool local_p)
+{
+  avr_need_clear_bss_p = true;
+
+  if (local_p)
+    {
+      fputs ("\t.local\t", stream);
+      assemble_name (stream, name);
+      fputs ("\n", stream);
+    }
+  
+  fputs ("\t.comm\t", stream);
+  assemble_name (stream, name);
+  fprintf (stream,
+           "," HOST_WIDE_INT_PRINT_UNSIGNED ",%u\n",
+           size, align / BITS_PER_UNIT);
+}
+
+
+/* Unnamed section callback for data_section
+   to track need of __do_copy_data.  */
+
+static void
+avr_output_data_section_asm_op (const void *data)
+{
+  avr_need_copy_data_p = true;
+  
+  /* Dispatch to default.  */
+  output_section_asm_op (data);
+}
+
+
+/* Unnamed section callback for bss_section
+   to track need of __do_clear_bss.  */
+
+static void
+avr_output_bss_section_asm_op (const void *data)
+{
+  avr_need_clear_bss_p = true;
+  
+  /* Dispatch to default.  */
+  output_section_asm_op (data);
+}
+
+
+/* Implement `TARGET_ASM_INIT_SECTIONS'.  */
 
 static void
 avr_asm_init_sections (void)
@@ -4860,6 +5279,27 @@ avr_asm_init_sections (void)
                                         avr_output_progmem_section_asm_op,
                                         NULL);
   readonly_data_section = data_section;
+
+  data_section->unnamed.callback = avr_output_data_section_asm_op;
+  bss_section->unnamed.callback = avr_output_bss_section_asm_op;
+}
+
+
+/* Implement `TARGET_ASM_NAMED_SECTION'.  */
+/* Track need of __do_clear_bss, __do_copy_data for named sections.  */
+
+void
+avr_asm_named_section (const char *name, unsigned int flags, tree decl)
+{
+  if (!avr_need_copy_data_p)
+    avr_need_copy_data_p = (0 == strncmp (name, ".data", 5)
+                            || 0 == strncmp (name, ".rodata", 7)
+                            || 0 == strncmp (name, ".gnu.linkonce.d", 15));
+  
+  if (!avr_need_clear_bss_p)
+    avr_need_clear_bss_p = (0 == strncmp (name, ".bss", 4));
+  
+  default_elf_asm_named_section (name, flags, decl);
 }
 
 static unsigned int
@@ -4880,6 +5320,8 @@ avr_section_type_flags (tree decl, const char *name, int reloc)
   return flags;
 }
 
+
+/* Implement `TARGET_ASM_FILE_START'.  */
 /* Outputs some appropriate text to go at the start of an assembler
    file.  */
 
@@ -4898,20 +5340,27 @@ avr_file_start (void)
   
   fputs ("__tmp_reg__ = 0\n" 
          "__zero_reg__ = 1\n", asm_out_file);
-
-  /* FIXME: output these only if there is anything in the .data / .bss
-     sections - some code size could be saved by not linking in the
-     initialization code from libgcc if one or both sections are empty.  */
-  fputs ("\t.global __do_copy_data\n", asm_out_file);
-  fputs ("\t.global __do_clear_bss\n", asm_out_file);
 }
 
+
+/* Implement `TARGET_ASM_FILE_END'.  */
 /* Outputs to the stdio stream FILE some
    appropriate text to go at the end of an assembler file.  */
 
 static void
 avr_file_end (void)
 {
+  /* Output these only if there is anything in the
+     .data* / .rodata* / .gnu.linkonce.* resp. .bss*
+     input section(s) - some code size can be saved by not
+     linking in the initialization code from libgcc if resp.
+     sections are empty.  */
+
+  if (avr_need_copy_data_p)
+    fputs (".global __do_copy_data\n", asm_out_file);
+
+  if (avr_need_clear_bss_p)
+    fputs (".global __do_clear_bss\n", asm_out_file);
 }
 
 /* Choose the order in which to allocate hard registers for
@@ -4971,13 +5420,40 @@ order_regs_for_local_alloc (void)
 }
 
 
+/* Implement `TARGET_REGISTER_MOVE_COST' */
+
+static int
+avr_register_move_cost (enum machine_mode mode ATTRIBUTE_UNUSED,
+                        reg_class_t from, reg_class_t to)
+{
+  return (from == STACK_REG ? 6
+          : to == STACK_REG ? 12
+          : 2);
+}
+
+
+/* Implement `TARGET_MEMORY_MOVE_COST' */
+
+static int
+avr_memory_move_cost (enum machine_mode mode, reg_class_t rclass ATTRIBUTE_UNUSED,
+                      bool in ATTRIBUTE_UNUSED)
+{
+  return (mode == QImode ? 2
+          : mode == HImode ? 4
+          : mode == SImode ? 8
+          : mode == SFmode ? 8
+          : 16);
+}
+
+
 /* Mutually recursive subroutine of avr_rtx_cost for calculating the
    cost of an RTX operand given its context.  X is the rtx of the
    operand, MODE is its mode, and OUTER is the rtx_code of this
    operand's parent operator.  */
 
 static int
-avr_operand_rtx_cost (rtx x, enum machine_mode mode, enum rtx_code outer)
+avr_operand_rtx_cost (rtx x, enum machine_mode mode, enum rtx_code outer,
+                     bool speed)
 {
   enum rtx_code code = GET_CODE (x);
   int total;
@@ -4997,7 +5473,7 @@ avr_operand_rtx_cost (rtx x, enum machine_mode mode, enum rtx_code outer)
     }
 
   total = 0;
-  avr_rtx_costs (x, code, outer, &total);
+  avr_rtx_costs (x, code, outer, &total, speed);
   return total;
 }
 
@@ -5007,8 +5483,10 @@ avr_operand_rtx_cost (rtx x, enum machine_mode mode, enum rtx_code outer)
    case, *TOTAL contains the cost result.  */
 
 static bool
-avr_rtx_costs (rtx x, int code, int outer_code ATTRIBUTE_UNUSED, int *total)
+avr_rtx_costs (rtx x, int codearg, int outer_code ATTRIBUTE_UNUSED, int *total,
+              bool speed)
 {
+  enum rtx_code code = (enum rtx_code) codearg;
   enum machine_mode mode = GET_MODE (x);
   HOST_WIDE_INT val;
 
@@ -5046,7 +5524,7 @@ avr_rtx_costs (rtx x, int code, int outer_code ATTRIBUTE_UNUSED, int *total)
        default:
          return false;
        }
-      *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code);
+      *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, speed);
       return true;
 
     case ABS:
@@ -5060,24 +5538,24 @@ avr_rtx_costs (rtx x, int code, int outer_code ATTRIBUTE_UNUSED, int *total)
        default:
          return false;
        }
-      *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code);
+      *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, speed);
       return true;
 
     case NOT:
       *total = COSTS_N_INSNS (GET_MODE_SIZE (mode));
-      *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code);
+      *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, 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);
+      *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, 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);
+      *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, speed);
       return true;
 
     case PLUS:
@@ -5086,14 +5564,14 @@ avr_rtx_costs (rtx x, int code, 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);
+           *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, speed);
          break;
 
        case HImode:
          if (GET_CODE (XEXP (x, 1)) != CONST_INT)
            {
              *total = COSTS_N_INSNS (2);
-             *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code);
+             *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, speed);
            }
          else if (INTVAL (XEXP (x, 1)) >= -63 && INTVAL (XEXP (x, 1)) <= 63)
            *total = COSTS_N_INSNS (1);
@@ -5105,7 +5583,7 @@ avr_rtx_costs (rtx x, int code, int outer_code ATTRIBUTE_UNUSED, int *total)
          if (GET_CODE (XEXP (x, 1)) != CONST_INT)
            {
              *total = COSTS_N_INSNS (4);
-             *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code);
+             *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, speed);
            }
          else if (INTVAL (XEXP (x, 1)) >= -63 && INTVAL (XEXP (x, 1)) <= 63)
            *total = COSTS_N_INSNS (1);
@@ -5116,22 +5594,22 @@ avr_rtx_costs (rtx x, int code, int outer_code ATTRIBUTE_UNUSED, int *total)
        default:
          return false;
        }
-      *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code);
+      *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, speed);
       return true;
 
     case MINUS:
     case AND:
     case IOR:
       *total = COSTS_N_INSNS (GET_MODE_SIZE (mode));
-      *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code);
+      *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, speed);
       if (GET_CODE (XEXP (x, 1)) != CONST_INT)
-          *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code);
+          *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, speed);
       return true;
 
     case XOR:
       *total = COSTS_N_INSNS (GET_MODE_SIZE (mode));
-      *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code);
-      *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code);
+      *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, speed);
+      *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, speed);
       return true;
 
     case MULT:
@@ -5139,8 +5617,8 @@ avr_rtx_costs (rtx x, int code, int outer_code ATTRIBUTE_UNUSED, int *total)
        {
        case QImode:
          if (AVR_HAVE_MUL)
-           *total = COSTS_N_INSNS (optimize_size ? 3 : 4);
-         else if (optimize_size)
+           *total = COSTS_N_INSNS (!speed ? 3 : 4);
+         else if (!speed)
            *total = COSTS_N_INSNS (AVR_HAVE_JMP_CALL ? 2 : 1);
          else
            return false;
@@ -5148,8 +5626,8 @@ avr_rtx_costs (rtx x, int code, int outer_code ATTRIBUTE_UNUSED, int *total)
 
        case HImode:
          if (AVR_HAVE_MUL)
-           *total = COSTS_N_INSNS (optimize_size ? 7 : 10);
-         else if (optimize_size)
+           *total = COSTS_N_INSNS (!speed ? 7 : 10);
+         else if (!speed)
            *total = COSTS_N_INSNS (AVR_HAVE_JMP_CALL ? 2 : 1);
          else
            return false;
@@ -5158,30 +5636,65 @@ avr_rtx_costs (rtx x, int code, int outer_code ATTRIBUTE_UNUSED, int *total)
        default:
          return false;
        }
-      *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code);
-      *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code);
+      *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, speed);
+      *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, speed);
       return true;
 
     case DIV:
     case MOD:
     case UDIV:
     case UMOD:
-      if (optimize_size)
+      if (!speed)
        *total = COSTS_N_INSNS (AVR_HAVE_JMP_CALL ? 2 : 1);
       else
        return false;
-      *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code);
-      *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code);
+      *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, speed);
+      *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, speed);
       return true;
 
+    case ROTATE:
+      switch (mode)
+       {
+       case QImode:
+         if (CONST_INT_P (XEXP (x, 1)) && INTVAL (XEXP (x, 1)) == 4)
+           *total = COSTS_N_INSNS (1);
+
+         break;
+
+       case HImode:
+         if (CONST_INT_P (XEXP (x, 1)) && INTVAL (XEXP (x, 1)) == 8)
+           *total = COSTS_N_INSNS (3);
+
+         break;
+
+       case SImode:
+         if (CONST_INT_P (XEXP (x, 1)))
+           switch (INTVAL (XEXP (x, 1)))
+             {
+             case 8:
+             case 24:
+               *total = COSTS_N_INSNS (5);
+               break;
+             case 16:
+               *total = COSTS_N_INSNS (AVR_HAVE_MOVW ? 4 : 6);
+               break;
+             }
+         break;
+
+       default:
+         return false;
+       }
+      *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, speed);
+      return true;    
+
     case ASHIFT:
       switch (mode)
        {
        case QImode:
          if (GET_CODE (XEXP (x, 1)) != CONST_INT)
            {
-             *total = COSTS_N_INSNS (optimize_size ? 4 : 17);
-             *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code);
+             *total = COSTS_N_INSNS (!speed ? 4 : 17);
+             *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, speed);
            }
          else
            {
@@ -5198,8 +5711,8 @@ avr_rtx_costs (rtx x, int code, int outer_code ATTRIBUTE_UNUSED, int *total)
        case HImode:
          if (GET_CODE (XEXP (x, 1)) != CONST_INT)
            {
-             *total = COSTS_N_INSNS (optimize_size ? 5 : 41);
-             *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code);
+             *total = COSTS_N_INSNS (!speed ? 5 : 41);
+             *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, speed);
            }
          else
            switch (INTVAL (XEXP (x, 1)))
@@ -5226,25 +5739,25 @@ avr_rtx_costs (rtx x, int code, int outer_code ATTRIBUTE_UNUSED, int *total)
                *total = COSTS_N_INSNS (5);
                break;
              case 4:
-               *total = COSTS_N_INSNS (optimize_size ? 5 : 8);
+               *total = COSTS_N_INSNS (!speed ? 5 : 8);
                break;
              case 6:
-               *total = COSTS_N_INSNS (optimize_size ? 5 : 9);
+               *total = COSTS_N_INSNS (!speed ? 5 : 9);
                break;
              case 5:
-               *total = COSTS_N_INSNS (optimize_size ? 5 : 10);
+               *total = COSTS_N_INSNS (!speed ? 5 : 10);
                break;
              default:
-               *total = COSTS_N_INSNS (optimize_size ? 5 : 41);
-               *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code);
+               *total = COSTS_N_INSNS (!speed ? 5 : 41);
+               *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, speed);
              }
          break;
 
        case SImode:
          if (GET_CODE (XEXP (x, 1)) != CONST_INT)
            {
-             *total = COSTS_N_INSNS (optimize_size ? 7 : 113);
-             *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code);
+             *total = COSTS_N_INSNS (!speed ? 7 : 113);
+             *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, speed);
            }
          else
            switch (INTVAL (XEXP (x, 1)))
@@ -5264,18 +5777,18 @@ avr_rtx_costs (rtx x, int code, int outer_code ATTRIBUTE_UNUSED, int *total)
                *total = COSTS_N_INSNS (6);
                break;
              case 2:
-               *total = COSTS_N_INSNS (optimize_size ? 7 : 8);
+               *total = COSTS_N_INSNS (!speed ? 7 : 8);
                break;
              default:
-               *total = COSTS_N_INSNS (optimize_size ? 7 : 113);
-               *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code);
+               *total = COSTS_N_INSNS (!speed ? 7 : 113);
+               *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, speed);
              }
          break;
 
        default:
          return false;
        }
-      *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code);
+      *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, speed);
       return true;
 
     case ASHIFTRT:
@@ -5284,8 +5797,8 @@ avr_rtx_costs (rtx x, int code, int outer_code ATTRIBUTE_UNUSED, int *total)
        case QImode:
          if (GET_CODE (XEXP (x, 1)) != CONST_INT)
            {
-             *total = COSTS_N_INSNS (optimize_size ? 4 : 17);
-             *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code);
+             *total = COSTS_N_INSNS (!speed ? 4 : 17);
+             *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, speed);
            }
          else
            {
@@ -5304,8 +5817,8 @@ avr_rtx_costs (rtx x, int code, int outer_code ATTRIBUTE_UNUSED, int *total)
        case HImode:
          if (GET_CODE (XEXP (x, 1)) != CONST_INT)
            {
-             *total = COSTS_N_INSNS (optimize_size ? 5 : 41);
-             *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code);
+             *total = COSTS_N_INSNS (!speed ? 5 : 41);
+             *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, speed);
            }
          else
            switch (INTVAL (XEXP (x, 1)))
@@ -5330,26 +5843,26 @@ avr_rtx_costs (rtx x, int code, int outer_code ATTRIBUTE_UNUSED, int *total)
                *total = COSTS_N_INSNS (5);
                break;
               case 11:
-                *total = COSTS_N_INSNS (optimize_size ? 5 : 6);
+                *total = COSTS_N_INSNS (!speed ? 5 : 6);
                break;
               case 12:
-                *total = COSTS_N_INSNS (optimize_size ? 5 : 7);
+                *total = COSTS_N_INSNS (!speed ? 5 : 7);
                break;
               case 6:
              case 13:
-                *total = COSTS_N_INSNS (optimize_size ? 5 : 8);
+                *total = COSTS_N_INSNS (!speed ? 5 : 8);
                break;
              default:
-               *total = COSTS_N_INSNS (optimize_size ? 5 : 41);
-               *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code);
+               *total = COSTS_N_INSNS (!speed ? 5 : 41);
+               *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, speed);
              }
          break;
 
        case SImode:
          if (GET_CODE (XEXP (x, 1)) != CONST_INT)
            {
-             *total = COSTS_N_INSNS (optimize_size ? 7 : 113);
-             *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code);
+             *total = COSTS_N_INSNS (!speed ? 7 : 113);
+             *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, speed);
            }
          else
            switch (INTVAL (XEXP (x, 1)))
@@ -5366,21 +5879,21 @@ avr_rtx_costs (rtx x, int code, int outer_code ATTRIBUTE_UNUSED, int *total)
                *total = COSTS_N_INSNS (6);
                break;
              case 2:
-               *total = COSTS_N_INSNS (optimize_size ? 7 : 8);
+               *total = COSTS_N_INSNS (!speed ? 7 : 8);
                break;
              case 31:
                *total = COSTS_N_INSNS (AVR_HAVE_MOVW ? 4 : 5);
                break;
              default:
-               *total = COSTS_N_INSNS (optimize_size ? 7 : 113);
-               *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code);
+               *total = COSTS_N_INSNS (!speed ? 7 : 113);
+               *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, speed);
              }
          break;
 
        default:
          return false;
        }
-      *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code);
+      *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, speed);
       return true;
 
     case LSHIFTRT:
@@ -5389,8 +5902,8 @@ avr_rtx_costs (rtx x, int code, int outer_code ATTRIBUTE_UNUSED, int *total)
        case QImode:
          if (GET_CODE (XEXP (x, 1)) != CONST_INT)
            {
-             *total = COSTS_N_INSNS (optimize_size ? 4 : 17);
-             *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code);
+             *total = COSTS_N_INSNS (!speed ? 4 : 17);
+             *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, speed);
            }
          else
            {
@@ -5407,8 +5920,8 @@ avr_rtx_costs (rtx x, int code, int outer_code ATTRIBUTE_UNUSED, int *total)
        case HImode:
          if (GET_CODE (XEXP (x, 1)) != CONST_INT)
            {
-             *total = COSTS_N_INSNS (optimize_size ? 5 : 41);
-             *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code);
+             *total = COSTS_N_INSNS (!speed ? 5 : 41);
+             *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, speed);
            }
          else
            switch (INTVAL (XEXP (x, 1)))
@@ -5436,26 +5949,26 @@ avr_rtx_costs (rtx x, int code, int outer_code ATTRIBUTE_UNUSED, int *total)
              case 12:
              case 13:
              case 14:
-               *total = COSTS_N_INSNS (optimize_size ? 5 : 6);
+               *total = COSTS_N_INSNS (!speed ? 5 : 6);
                break;
              case 4:
-               *total = COSTS_N_INSNS (optimize_size ? 5 : 7);
+               *total = COSTS_N_INSNS (!speed ? 5 : 7);
                break;
              case 5:
              case 6:
-               *total = COSTS_N_INSNS (optimize_size ? 5 : 9);
+               *total = COSTS_N_INSNS (!speed ? 5 : 9);
                break;
              default:
-               *total = COSTS_N_INSNS (optimize_size ? 5 : 41);
-               *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code);
+               *total = COSTS_N_INSNS (!speed ? 5 : 41);
+               *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, speed);
              }
          break;
 
        case SImode:
          if (GET_CODE (XEXP (x, 1)) != CONST_INT)
            {
-             *total = COSTS_N_INSNS (optimize_size ? 7 : 113);
-             *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code);
+             *total = COSTS_N_INSNS (!speed ? 7 : 113);
+             *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, speed);
            }
          else
            switch (INTVAL (XEXP (x, 1)))
@@ -5467,7 +5980,7 @@ avr_rtx_costs (rtx x, int code, int outer_code ATTRIBUTE_UNUSED, int *total)
                *total = COSTS_N_INSNS (4);
                break;
              case 2:
-               *total = COSTS_N_INSNS (optimize_size ? 7 : 8);
+               *total = COSTS_N_INSNS (!speed ? 7 : 8);
                break;
              case 8:
              case 16:
@@ -5478,15 +5991,15 @@ avr_rtx_costs (rtx x, int code, int outer_code ATTRIBUTE_UNUSED, int *total)
                *total = COSTS_N_INSNS (6);
                break;
              default:
-               *total = COSTS_N_INSNS (optimize_size ? 7 : 113);
-               *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code);
+               *total = COSTS_N_INSNS (!speed ? 7 : 113);
+               *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, speed);
              }
          break;
 
        default:
          return false;
        }
-      *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code);
+      *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, speed);
       return true;
 
     case COMPARE:
@@ -5495,13 +6008,13 @@ avr_rtx_costs (rtx x, int code, 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);
+           *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, 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);
+            *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, speed);
          else if (INTVAL (XEXP (x, 1)) != 0)
            *total += COSTS_N_INSNS (1);
           break;
@@ -5509,7 +6022,7 @@ avr_rtx_costs (rtx x, int code, int outer_code ATTRIBUTE_UNUSED, int *total)
         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);
+            *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, speed);
          else if (INTVAL (XEXP (x, 1)) != 0)
            *total += COSTS_N_INSNS (3);
           break;
@@ -5517,7 +6030,7 @@ avr_rtx_costs (rtx x, int code, int outer_code ATTRIBUTE_UNUSED, int *total)
        default:
          return false;
        }
-      *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code);
+      *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, speed);
       return true;
 
     default:
@@ -5529,7 +6042,7 @@ avr_rtx_costs (rtx x, int code, int outer_code ATTRIBUTE_UNUSED, int *total)
 /* Calculate the cost of a memory address.  */
 
 static int
-avr_address_cost (rtx x)
+avr_address_cost (rtx x, bool speed ATTRIBUTE_UNUSED)
 {
   if (GET_CODE (x) == PLUS
       && GET_CODE (XEXP (x,1)) == CONST_INT
@@ -5639,6 +6152,21 @@ avr_reorg (void)
                  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);
+    
+                 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)
                {
@@ -5658,54 +6186,51 @@ avr_reorg (void)
                    }
                }
            }
-         else if (true_regnum (SET_SRC (pattern)) >= 0)
-           {
-             /* This is a tst insn */
-             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)));
-             SET_SRC (pattern) = gen_rtx_NEG (GET_MODE (SET_SRC (pattern)),
-                                              SET_SRC (pattern));
-             INSN_CODE (next) = -1;
-             INSN_CODE (insn) = -1;
-           }
        }
     }
 }
 
 /* Returns register number for function return value.*/
 
-int
+static inline unsigned int
 avr_ret_register (void)
 {
   return 24;
 }
 
+/* Worker function for TARGET_FUNCTION_VALUE_REGNO_P.  */
+
+static bool
+avr_function_value_regno_p (const unsigned int regno)
+{
+  return (regno == avr_ret_register ());
+}
+
 /* Create an RTX representing the place where a
    library function returns a value of mode MODE.  */
 
-rtx
-avr_libcall_value (enum machine_mode mode)
+static rtx
+avr_libcall_value (enum machine_mode mode,
+                  const_rtx func ATTRIBUTE_UNUSED)
 {
   int offs = GET_MODE_SIZE (mode);
   if (offs < 2)
     offs = 2;
-  return gen_rtx_REG (mode, RET_REGISTER + 2 - offs);
+  return gen_rtx_REG (mode, avr_ret_register () + 2 - offs);
 }
 
 /* Create an RTX representing the place where a
    function returns a value of data type VALTYPE.  */
 
-rtx
-avr_function_value (const_tree type, const_tree func ATTRIBUTE_UNUSED)
+static rtx
+avr_function_value (const_tree type,
+                    const_tree fn_decl_or_type ATTRIBUTE_UNUSED,
+                    bool outgoing ATTRIBUTE_UNUSED)
 {
   unsigned int offs;
-  
+
   if (TYPE_MODE (type) != BLKmode)
-    return avr_libcall_value (TYPE_MODE (type));
+    return avr_libcall_value (TYPE_MODE (type), NULL_RTX);
   
   offs = int_size_in_bytes (type);
   if (offs < 2)
@@ -5715,27 +6240,17 @@ avr_function_value (const_tree type, const_tree func ATTRIBUTE_UNUSED)
   else if (offs > GET_MODE_SIZE (SImode) && offs < GET_MODE_SIZE (DImode))
     offs = GET_MODE_SIZE (DImode);
   
-  return gen_rtx_REG (BLKmode, RET_REGISTER + 2 - offs);
-}
-
-/* Places additional restrictions on the register class to
-   use when it is necessary to copy value X into a register
-   in class CLASS.  */
-
-enum reg_class
-preferred_reload_class (rtx x ATTRIBUTE_UNUSED, enum reg_class class)
-{
-  return class;
+  return gen_rtx_REG (BLKmode, avr_ret_register () + 2 - offs);
 }
 
 int
-test_hard_reg_class (enum reg_class class, rtx x)
+test_hard_reg_class (enum reg_class rclass, rtx x)
 {
   int regno = true_regnum (x);
   if (regno < 0)
     return 0;
 
-  if (TEST_HARD_REG_CLASS (class, regno))
+  if (TEST_HARD_REG_CLASS (rclass, regno))
     return 1;
 
   return 0;
@@ -5894,27 +6409,20 @@ avr_output_addr_vec_elt (FILE *stream, int value)
     fprintf (stream, "\trjmp .L%d\n", value);
 }
 
-/* Returns 1 if SCRATCH are safe to be allocated as a scratch
+/* Returns true if SCRATCH are safe to be allocated as a scratch
    registers (for a define_peephole2) in the current function.  */
 
-int
-avr_peep2_scratch_safe (rtx scratch)
+bool
+avr_hard_regno_scratch_ok (unsigned int regno)
 {
-  if ((interrupt_function_p (current_function_decl)
-       || signal_function_p (current_function_decl))
-      && leaf_function_p ())
-    {
-      int first_reg = true_regnum (scratch);
-      int last_reg = first_reg + GET_MODE_SIZE (GET_MODE (scratch)) - 1;
-      int reg;
+  /* Interrupt functions can only use registers that have already been saved
+     by the prologue, even if they would normally be call-clobbered.  */
 
-      for (reg = first_reg; reg <= last_reg; reg++)
-       {
-         if (!df_regs_ever_live_p (reg))
-           return 0;
-       }
-    }
-  return 1;
+  if ((cfun->machine->is_interrupt || cfun->machine->is_signal)
+      && !df_regs_ever_live_p (regno))
+    return false;
+
+  return true;
 }
 
 /* Return nonzero if register OLD_REG can be renamed to register NEW_REG.  */
@@ -5934,12 +6442,12 @@ avr_hard_regno_rename_ok (unsigned int old_reg ATTRIBUTE_UNUSED,
   return 1;
 }
 
-/* Output a branch that tests a single bit of a register (QI, HI or SImode)
+/* Output a branch that tests a single bit of a register (QI, HI, SI or DImode)
    or memory location in the I/O space (QImode only).
 
    Operand 0: comparison operator (must be EQ or NE, compare bit to zero).
    Operand 1: register operand to test, or CONST_INT memory address.
-   Operand 2: bit number (for QImode operand) or mask (HImode, SImode).
+   Operand 2: bit number.
    Operand 3: label to jump to if the test is true.  */
 
 const char *
@@ -5962,13 +6470,13 @@ avr_out_sbxx_branch (rtx insn, rtx operands[])
       if (INTVAL (operands[1]) < 0x40)
        {
          if (comp == EQ)
-           output_asm_insn (AS2 (sbis,%1-0x20,%2), operands);
+           output_asm_insn (AS2 (sbis,%m1-0x20,%2), operands);
          else
-           output_asm_insn (AS2 (sbic,%1-0x20,%2), operands);
+           output_asm_insn (AS2 (sbic,%m1-0x20,%2), operands);
        }
       else
        {
-         output_asm_insn (AS2 (in,__tmp_reg__,%1-0x20), operands);
+         output_asm_insn (AS2 (in,__tmp_reg__,%m1-0x20), operands);
          if (comp == EQ)
            output_asm_insn (AS2 (sbrs,__tmp_reg__,%2), operands);
          else
@@ -5987,9 +6495,7 @@ avr_out_sbxx_branch (rtx insn, rtx operands[])
       else  /* HImode or SImode */
        {
          static char buf[] = "sbrc %A1,0";
-         int bit_nr = exact_log2 (INTVAL (operands[2])
-                                  & GET_MODE_MASK (GET_MODE (operands[1])));
-
+         int bit_nr = INTVAL (operands[2]);
          buf[3] = (comp == EQ) ? 's' : 'c';
          buf[6] = 'A' + (bit_nr >> 3);
          buf[9] = '0' + (bit_nr & 7);
@@ -5999,9 +6505,9 @@ avr_out_sbxx_branch (rtx insn, rtx operands[])
 
   if (long_jump)
     return (AS1 (rjmp,.+4) CR_TAB
-           AS1 (jmp,%3));
+           AS1 (jmp,%x3));
   if (!reverse)
-    return AS1 (rjmp,%3);
+    return AS1 (rjmp,%x3);
   return "";
 }
 
@@ -6037,4 +6543,338 @@ avr_return_in_memory (const_tree type, const_tree fntype ATTRIBUTE_UNUSED)
     return false;
 }
 
+/* Worker function for CASE_VALUES_THRESHOLD.  */
+
+unsigned int avr_case_values_threshold (void)
+{
+  return (!AVR_HAVE_JMP_CALL || TARGET_CALL_PROLOGUES) ? 8 : 17;
+}
+
+/* Helper for __builtin_avr_delay_cycles */
+
+static void
+avr_expand_delay_cycles (rtx operands0)
+{
+  unsigned HOST_WIDE_INT cycles = UINTVAL (operands0);
+  unsigned HOST_WIDE_INT cycles_used;
+  unsigned HOST_WIDE_INT loop_count;
+  
+  if (IN_RANGE (cycles, 83886082, 0xFFFFFFFF))
+    {
+      loop_count = ((cycles - 9) / 6) + 1;
+      cycles_used = ((loop_count - 1) * 6) + 9;
+      emit_insn (gen_delay_cycles_4 (gen_int_mode (loop_count, SImode)));
+      cycles -= cycles_used;
+    }
+  
+  if (IN_RANGE (cycles, 262145, 83886081))
+    {
+      loop_count = ((cycles - 7) / 5) + 1;
+      if (loop_count > 0xFFFFFF)
+        loop_count = 0xFFFFFF;
+      cycles_used = ((loop_count - 1) * 5) + 7;
+      emit_insn (gen_delay_cycles_3 (gen_int_mode (loop_count, SImode)));
+      cycles -= cycles_used;
+    }
+  
+  if (IN_RANGE (cycles, 768, 262144))
+    {
+      loop_count = ((cycles - 5) / 4) + 1;
+      if (loop_count > 0xFFFF)
+        loop_count = 0xFFFF;
+      cycles_used = ((loop_count - 1) * 4) + 5;
+      emit_insn (gen_delay_cycles_2 (gen_int_mode (loop_count, HImode)));
+      cycles -= cycles_used;
+    }
+  
+  if (IN_RANGE (cycles, 6, 767))
+    {
+      loop_count = cycles / 3;
+      if (loop_count > 255) 
+        loop_count = 255;
+      cycles_used = loop_count * 3;
+      emit_insn (gen_delay_cycles_1 (gen_int_mode (loop_count, QImode)));
+      cycles -= cycles_used;
+      }
+  
+  while (cycles >= 2)
+    {
+      emit_insn (gen_nopv (GEN_INT(2)));
+      cycles -= 2;
+    }
+
+  if (cycles == 1)
+    {
+      emit_insn (gen_nopv (GEN_INT(1)));
+      cycles--;
+    }
+}
+
+/* IDs for all the AVR builtins.  */
+
+enum avr_builtin_id
+  {
+    AVR_BUILTIN_NOP,
+    AVR_BUILTIN_SEI,
+    AVR_BUILTIN_CLI,
+    AVR_BUILTIN_WDR,
+    AVR_BUILTIN_SLEEP,
+    AVR_BUILTIN_SWAP,
+    AVR_BUILTIN_FMUL,
+    AVR_BUILTIN_FMULS,
+    AVR_BUILTIN_FMULSU,
+    AVR_BUILTIN_DELAY_CYCLES
+  };
+
+#define DEF_BUILTIN(NAME, TYPE, CODE)                                   \
+  do                                                                    \
+    {                                                                   \
+      add_builtin_function ((NAME), (TYPE), (CODE), BUILT_IN_MD,        \
+                            NULL, NULL_TREE);                           \
+    } while (0)
+
+
+/* Implement `TARGET_INIT_BUILTINS' */
+/* Set up all builtin functions for this target.  */
+
+static void
+avr_init_builtins (void)
+{
+  tree void_ftype_void
+    = build_function_type_list (void_type_node, NULL_TREE);
+  tree uchar_ftype_uchar
+    = build_function_type_list (unsigned_char_type_node, 
+                                unsigned_char_type_node,
+                                NULL_TREE);
+  tree uint_ftype_uchar_uchar
+    = build_function_type_list (unsigned_type_node, 
+                                unsigned_char_type_node,
+                                unsigned_char_type_node, 
+                                NULL_TREE);
+  tree int_ftype_char_char
+    = build_function_type_list (integer_type_node, 
+                                char_type_node,
+                                char_type_node, 
+                                NULL_TREE);
+  tree int_ftype_char_uchar
+    = build_function_type_list (integer_type_node, 
+                                char_type_node,
+                                unsigned_char_type_node, 
+                                NULL_TREE);
+  tree void_ftype_ulong
+    = build_function_type_list (void_type_node, 
+                                long_unsigned_type_node,
+                                NULL_TREE);
+
+  DEF_BUILTIN ("__builtin_avr_nop", void_ftype_void, AVR_BUILTIN_NOP);
+  DEF_BUILTIN ("__builtin_avr_sei", void_ftype_void, AVR_BUILTIN_SEI);
+  DEF_BUILTIN ("__builtin_avr_cli", void_ftype_void, AVR_BUILTIN_CLI);
+  DEF_BUILTIN ("__builtin_avr_wdr", void_ftype_void, AVR_BUILTIN_WDR);
+  DEF_BUILTIN ("__builtin_avr_sleep", void_ftype_void, AVR_BUILTIN_SLEEP);
+  DEF_BUILTIN ("__builtin_avr_swap", uchar_ftype_uchar, AVR_BUILTIN_SWAP);
+  DEF_BUILTIN ("__builtin_avr_delay_cycles", void_ftype_ulong, 
+               AVR_BUILTIN_DELAY_CYCLES);
+
+  if (AVR_HAVE_MUL)
+    {
+      /* FIXME: If !AVR_HAVE_MUL, make respective functions available
+         in libgcc. For fmul and fmuls this is straight forward with
+         upcoming fixed point support. */
+      
+      DEF_BUILTIN ("__builtin_avr_fmul", uint_ftype_uchar_uchar, 
+                   AVR_BUILTIN_FMUL);
+      DEF_BUILTIN ("__builtin_avr_fmuls", int_ftype_char_char, 
+                   AVR_BUILTIN_FMULS);
+      DEF_BUILTIN ("__builtin_avr_fmulsu", int_ftype_char_uchar, 
+                   AVR_BUILTIN_FMULSU);
+    }
+}
+
+#undef DEF_BUILTIN
+
+struct avr_builtin_description
+{
+  const enum insn_code icode;
+  const char *const name;
+  const enum avr_builtin_id id;
+};
+
+static const struct avr_builtin_description
+bdesc_1arg[] =
+  {
+    { CODE_FOR_rotlqi3_4, "__builtin_avr_swap", AVR_BUILTIN_SWAP }
+  };
+
+static const struct avr_builtin_description
+bdesc_2arg[] =
+  {
+    { CODE_FOR_fmul, "__builtin_avr_fmul", AVR_BUILTIN_FMUL },
+    { CODE_FOR_fmuls, "__builtin_avr_fmuls", AVR_BUILTIN_FMULS },
+    { CODE_FOR_fmulsu, "__builtin_avr_fmulsu", AVR_BUILTIN_FMULSU }
+  };
+
+/* Subroutine of avr_expand_builtin to take care of unop insns.  */
+
+static rtx
+avr_expand_unop_builtin (enum insn_code icode, tree exp,
+                         rtx target)
+{
+  rtx pat;
+  tree arg0 = CALL_EXPR_ARG (exp, 0);
+  rtx op0 = expand_expr (arg0, NULL_RTX, VOIDmode, EXPAND_NORMAL);
+  enum machine_mode op0mode = GET_MODE (op0);
+  enum machine_mode tmode = insn_data[icode].operand[0].mode;
+  enum machine_mode mode0 = insn_data[icode].operand[1].mode;
+
+  if (! target
+      || GET_MODE (target) != tmode
+      || ! (*insn_data[icode].operand[0].predicate) (target, tmode))
+    {
+      target = gen_reg_rtx (tmode);
+    }
+
+  if (op0mode == SImode && mode0 == HImode)
+    {
+      op0mode = HImode;
+      op0 = gen_lowpart (HImode, op0);
+    }
+  
+  gcc_assert (op0mode == mode0 || op0mode == VOIDmode);
+
+  if (! (*insn_data[icode].operand[1].predicate) (op0, mode0))
+    op0 = copy_to_mode_reg (mode0, op0);
+
+  pat = GEN_FCN (icode) (target, op0);
+  if (! pat)
+    return 0;
+  
+  emit_insn (pat);
+  
+  return target;
+}
+
+
+/* Subroutine of avr_expand_builtin to take care of binop insns.  */
+
+static rtx
+avr_expand_binop_builtin (enum insn_code icode, tree exp, rtx target)
+{
+  rtx pat;
+  tree arg0 = CALL_EXPR_ARG (exp, 0);
+  tree arg1 = CALL_EXPR_ARG (exp, 1);
+  rtx op0 = expand_expr (arg0, NULL_RTX, VOIDmode, EXPAND_NORMAL);
+  rtx op1 = expand_expr (arg1, NULL_RTX, VOIDmode, EXPAND_NORMAL);
+  enum machine_mode op0mode = GET_MODE (op0);
+  enum machine_mode op1mode = GET_MODE (op1);
+  enum machine_mode tmode = insn_data[icode].operand[0].mode;
+  enum machine_mode mode0 = insn_data[icode].operand[1].mode;
+  enum machine_mode mode1 = insn_data[icode].operand[2].mode;
+
+  if (! target
+      || GET_MODE (target) != tmode
+      || ! (*insn_data[icode].operand[0].predicate) (target, tmode))
+    {
+      target = gen_reg_rtx (tmode);
+    }
+
+  if ((op0mode == SImode || op0mode == VOIDmode) && mode0 == HImode)
+    {
+      op0mode = HImode;
+      op0 = gen_lowpart (HImode, op0);
+    }
+  
+  if ((op1mode == SImode || op1mode == VOIDmode) && mode1 == HImode)
+    {
+      op1mode = HImode;
+      op1 = gen_lowpart (HImode, op1);
+    }
+  
+  /* In case the insn wants input operands in modes different from
+     the result, abort.  */
+  
+  gcc_assert ((op0mode == mode0 || op0mode == VOIDmode)
+              && (op1mode == mode1 || op1mode == VOIDmode));
+
+  if (! (*insn_data[icode].operand[1].predicate) (op0, mode0))
+    op0 = copy_to_mode_reg (mode0, op0);
+  
+  if (! (*insn_data[icode].operand[2].predicate) (op1, mode1))
+    op1 = copy_to_mode_reg (mode1, op1);
+
+  pat = GEN_FCN (icode) (target, op0, op1);
+  
+  if (! pat)
+    return 0;
+
+  emit_insn (pat);
+  return target;
+}
+
+
+/* Expand an expression EXP that calls a built-in function,
+   with result going to TARGET if that's convenient
+   (and in mode MODE if that's convenient).
+   SUBTARGET may be used as the target for computing one of EXP's operands.
+   IGNORE is nonzero if the value is to be ignored.  */
+
+static rtx
+avr_expand_builtin (tree exp, rtx target,
+                    rtx subtarget ATTRIBUTE_UNUSED,
+                    enum machine_mode mode ATTRIBUTE_UNUSED,
+                    int ignore ATTRIBUTE_UNUSED)
+{
+  size_t i;
+  const struct avr_builtin_description *d;
+  tree fndecl = TREE_OPERAND (CALL_EXPR_FN (exp), 0);
+  unsigned int id = DECL_FUNCTION_CODE (fndecl);
+  tree arg0;
+  rtx op0;
+
+  switch (id)
+    {
+    case AVR_BUILTIN_NOP:
+      emit_insn (gen_nopv (GEN_INT(1)));
+      return 0;
+      
+    case AVR_BUILTIN_SEI:
+      emit_insn (gen_enable_interrupt ());
+      return 0;
+      
+    case AVR_BUILTIN_CLI:
+      emit_insn (gen_disable_interrupt ());
+      return 0;
+      
+    case AVR_BUILTIN_WDR:
+      emit_insn (gen_wdr ());
+      return 0;
+      
+    case AVR_BUILTIN_SLEEP:
+      emit_insn (gen_sleep ());
+      return 0;
+      
+    case AVR_BUILTIN_DELAY_CYCLES:
+      {
+        arg0 = CALL_EXPR_ARG (exp, 0);
+        op0 = expand_expr (arg0, NULL_RTX, VOIDmode, EXPAND_NORMAL);
+
+        if (! CONST_INT_P (op0))
+          error ("__builtin_avr_delay_cycles expects a compile time integer constant.");
+
+        avr_expand_delay_cycles (op0);
+        return 0;
+      }
+    }
+
+  for (i = 0, d = bdesc_1arg; i < ARRAY_SIZE (bdesc_1arg); i++, d++)
+    if (d->id == id)
+      return avr_expand_unop_builtin (d->icode, exp, target);
+
+  for (i = 0, d = bdesc_2arg; i < ARRAY_SIZE (bdesc_2arg); i++, d++)
+    if (d->id == id)
+      return avr_expand_binop_builtin (d->icode, exp, target);
+
+  gcc_unreachable ();
+}
+
+
 #include "gt-avr.h"