OSDN Git Service

gcc/
[pf3gnuchains/gcc-fork.git] / gcc / config / avr / avr.c
index 2a46292..0cb689a 100644 (file)
 
 /* Known address spaces.  The order must be the same as in the respective
    enum from avr.h (or designated initialized must be used).  */
-const avr_addrspace_t avr_addrspace[] =
-{
-    { ADDR_SPACE_RAM,  0, 2, ""     ,   0 },
-    { ADDR_SPACE_FLASH,  1, 2, "__flash",   0 },
-    { ADDR_SPACE_FLASH1, 1, 2, "__flash1",  1 },
-    { ADDR_SPACE_FLASH2, 1, 2, "__flash2",  2 },
-    { ADDR_SPACE_FLASH3, 1, 2, "__flash3",  3 },
-    { ADDR_SPACE_FLASH4, 1, 2, "__flash4",  4 },
-    { ADDR_SPACE_FLASH5, 1, 2, "__flash5",  5 },
-    { ADDR_SPACE_MEMX, 1, 3, "__memx",  0 },
-    { 0              , 0, 0, NULL,      0 }
+const avr_addrspace_t avr_addrspace[ADDR_SPACE_COUNT] =
+{
+  { ADDR_SPACE_RAM,  0, 2, "", 0, NULL },
+  { ADDR_SPACE_FLASH,  1, 2, "__flash",   0, ".progmem.data" },
+  { ADDR_SPACE_FLASH1, 1, 2, "__flash1",  1, ".progmem1.data" },
+  { ADDR_SPACE_FLASH2, 1, 2, "__flash2",  2, ".progmem2.data" },
+  { ADDR_SPACE_FLASH3, 1, 2, "__flash3",  3, ".progmem3.data" },
+  { ADDR_SPACE_FLASH4, 1, 2, "__flash4",  4, ".progmem4.data" },
+  { ADDR_SPACE_FLASH5, 1, 2, "__flash5",  5, ".progmem5.data" },
+  { ADDR_SPACE_MEMX, 1, 3, "__memx",  0, ".progmemx.data" },
 };
 
-/* Map 64-k Flash segment to section prefix.  */
-static const char* const progmem_section_prefix[6] =
-  {
-    ".progmem.data",
-    ".progmem1.data",
-    ".progmem2.data",
-    ".progmem3.data",
-    ".progmem4.data",
-    ".progmem5.data"
-  };
 
 /* Holding RAM addresses of some SFRs used by the compiler and that
    are unique over all devices in an architecture like 'avr4'.  */
@@ -138,12 +127,6 @@ static const char* out_movqi_mr_r (rtx, rtx[], int*);
 static const char* out_movhi_mr_r (rtx, rtx[], int*);
 static const char* out_movsi_mr_r (rtx, rtx[], int*);
 
-static int avr_naked_function_p (tree);
-static int interrupt_function_p (tree);
-static int signal_function_p (tree);
-static int avr_OS_task_function_p (tree);
-static int avr_OS_main_function_p (tree);
-static int avr_regs_to_save (HARD_REG_SET *);
 static int get_sequence_length (rtx insns);
 static int sequent_regs_live (void);
 static const char *ptrreg_to_str (int);
@@ -214,8 +197,9 @@ const struct mcu_type_s *avr_current_device;
 static GTY(()) section *progmem_swtable_section;
 
 /* Unnamed sections associated to __attribute__((progmem)) aka. PROGMEM
-   or to address space __flash*.  */
-static GTY(()) section *progmem_section[6];
+   or to address space __flash* or __memx.  Only used as singletons inside
+   avr_asm_select_section, but it must not be local there because of GTY.  */
+static GTY(()) section *progmem_section[ADDR_SPACE_COUNT];
 
 /* Condition for insns/expanders from avr-dimode.md.  */
 bool avr_have_dimode = true;
@@ -225,137 +209,6 @@ bool avr_need_clear_bss_p = false;
 bool avr_need_copy_data_p = false;
 
 \f
-/* Initialize the GCC target structure.  */
-#undef TARGET_ASM_ALIGNED_HI_OP
-#define TARGET_ASM_ALIGNED_HI_OP "\t.word\t"
-#undef TARGET_ASM_ALIGNED_SI_OP
-#define TARGET_ASM_ALIGNED_SI_OP "\t.long\t"
-#undef TARGET_ASM_UNALIGNED_HI_OP
-#define TARGET_ASM_UNALIGNED_HI_OP "\t.word\t"
-#undef TARGET_ASM_UNALIGNED_SI_OP
-#define TARGET_ASM_UNALIGNED_SI_OP "\t.long\t"
-#undef TARGET_ASM_INTEGER
-#define TARGET_ASM_INTEGER avr_assemble_integer
-#undef TARGET_ASM_FILE_START
-#define TARGET_ASM_FILE_START avr_file_start
-#undef TARGET_ASM_FILE_END
-#define TARGET_ASM_FILE_END avr_file_end
-
-#undef TARGET_ASM_FUNCTION_END_PROLOGUE
-#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_INSERT_ATTRIBUTES
-#define TARGET_INSERT_ATTRIBUTES avr_insert_attributes
-#undef TARGET_SECTION_TYPE_FLAGS
-#define TARGET_SECTION_TYPE_FLAGS avr_section_type_flags
-
-#undef TARGET_ASM_NAMED_SECTION
-#define TARGET_ASM_NAMED_SECTION avr_asm_named_section
-#undef TARGET_ASM_INIT_SECTIONS
-#define TARGET_ASM_INIT_SECTIONS avr_asm_init_sections
-#undef TARGET_ENCODE_SECTION_INFO
-#define TARGET_ENCODE_SECTION_INFO avr_encode_section_info
-#undef TARGET_ASM_SELECT_SECTION
-#define TARGET_ASM_SELECT_SECTION avr_asm_select_section
-
-#undef TARGET_REGISTER_MOVE_COST
-#define TARGET_REGISTER_MOVE_COST avr_register_move_cost
-#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_RETURN_IN_MEMORY
-#define TARGET_RETURN_IN_MEMORY avr_return_in_memory
-
-#undef TARGET_STRICT_ARGUMENT_NAMING
-#define TARGET_STRICT_ARGUMENT_NAMING hook_bool_CUMULATIVE_ARGS_true
-
-#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_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_CANNOT_MODIFY_JUMPS_P
-#define TARGET_CANNOT_MODIFY_JUMPS_P avr_cannot_modify_jumps_p
-
-#undef TARGET_FUNCTION_OK_FOR_SIBCALL
-#define TARGET_FUNCTION_OK_FOR_SIBCALL avr_function_ok_for_sibcall
-
-#undef TARGET_INIT_BUILTINS
-#define TARGET_INIT_BUILTINS avr_init_builtins
-
-#undef TARGET_EXPAND_BUILTIN
-#define TARGET_EXPAND_BUILTIN avr_expand_builtin
-
-#undef  TARGET_FOLD_BUILTIN
-#define TARGET_FOLD_BUILTIN avr_fold_builtin
-
-#undef TARGET_ASM_FUNCTION_RODATA_SECTION
-#define TARGET_ASM_FUNCTION_RODATA_SECTION avr_asm_function_rodata_section
-
-#undef  TARGET_SCALAR_MODE_SUPPORTED_P
-#define TARGET_SCALAR_MODE_SUPPORTED_P avr_scalar_mode_supported_p
-
-#undef  TARGET_ADDR_SPACE_SUBSET_P
-#define TARGET_ADDR_SPACE_SUBSET_P avr_addr_space_subset_p
-
-#undef  TARGET_ADDR_SPACE_CONVERT
-#define TARGET_ADDR_SPACE_CONVERT avr_addr_space_convert
-
-#undef  TARGET_ADDR_SPACE_ADDRESS_MODE
-#define TARGET_ADDR_SPACE_ADDRESS_MODE avr_addr_space_address_mode
-
-#undef  TARGET_ADDR_SPACE_POINTER_MODE
-#define TARGET_ADDR_SPACE_POINTER_MODE avr_addr_space_pointer_mode
-
-#undef  TARGET_ADDR_SPACE_LEGITIMATE_ADDRESS_P
-#define TARGET_ADDR_SPACE_LEGITIMATE_ADDRESS_P avr_addr_space_legitimate_address_p
-
-#undef TARGET_ADDR_SPACE_LEGITIMIZE_ADDRESS
-#define TARGET_ADDR_SPACE_LEGITIMIZE_ADDRESS avr_addr_space_legitimize_address
-
-#undef  TARGET_PRINT_OPERAND
-#define TARGET_PRINT_OPERAND avr_print_operand
-#undef  TARGET_PRINT_OPERAND_ADDRESS
-#define TARGET_PRINT_OPERAND_ADDRESS avr_print_operand_address
-#undef  TARGET_PRINT_OPERAND_PUNCT_VALID_P
-#define TARGET_PRINT_OPERAND_PUNCT_VALID_P avr_print_operand_punct_valid_p
-
-\f
 
 /* Custom function to count number of set bits.  */
 
@@ -622,7 +475,7 @@ avr_naked_function_p (tree func)
    by the "interrupt" attribute.  */
 
 static int
-interrupt_function_p (tree func)
+avr_interrupt_function_p (tree func)
 {
   return avr_lookup_function_attribute1 (func, "interrupt");
 }
@@ -631,7 +484,7 @@ interrupt_function_p (tree func)
    by the "signal" attribute.  */
 
 static int
-signal_function_p (tree func)
+avr_signal_function_p (tree func)
 {
   return avr_lookup_function_attribute1 (func, "signal");
 }
@@ -653,8 +506,92 @@ avr_OS_main_function_p (tree func)
 }
 
 
+/* Implement `TARGET_SET_CURRENT_FUNCTION'.  */
+/* Sanity cheching for above function attributes.  */
+
+static void
+avr_set_current_function (tree decl)
+{
+  location_t loc;
+  const char *isr;
+
+  if (decl == NULL_TREE
+      || current_function_decl == NULL_TREE
+      || current_function_decl == error_mark_node
+      || cfun->machine->attributes_checked_p)
+    return;
+
+  loc = DECL_SOURCE_LOCATION (decl);
+
+  cfun->machine->is_naked = avr_naked_function_p (decl);
+  cfun->machine->is_signal = avr_signal_function_p (decl);
+  cfun->machine->is_interrupt = avr_interrupt_function_p (decl);
+  cfun->machine->is_OS_task = avr_OS_task_function_p (decl);
+  cfun->machine->is_OS_main = avr_OS_main_function_p (decl);
+
+  isr = cfun->machine->is_interrupt ? "interrupt" : "signal";
+
+  /* Too much attributes make no sense as they request conflicting features. */
+
+  if (cfun->machine->is_OS_task + cfun->machine->is_OS_main
+      + (cfun->machine->is_signal || cfun->machine->is_interrupt) > 1)
+    error_at (loc, "function attributes %qs, %qs and %qs are mutually"
+               " exclusive", "OS_task", "OS_main", isr);
+
+  /* 'naked' will hide effects of 'OS_task' and 'OS_main'.  */
+
+  if (cfun->machine->is_naked
+      && (cfun->machine->is_OS_task || cfun->machine->is_OS_main))
+    warning_at (loc, OPT_Wattributes, "function attributes %qs and %qs have"
+                " no effect on %qs function", "OS_task", "OS_main", "naked");
+
+  if (cfun->machine->is_interrupt || cfun->machine->is_signal)
+    {
+      tree args = TYPE_ARG_TYPES (TREE_TYPE (decl));
+      tree ret = TREE_TYPE (TREE_TYPE (decl));
+      const char *name;
+
+      name = DECL_ASSEMBLER_NAME_SET_P (decl)
+        ? IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl))
+        : IDENTIFIER_POINTER (DECL_NAME (decl));
+
+      /* Skip a leading '*' that might still prefix the assembler name,
+         e.g. in non-LTO runs.  */
+
+      name = default_strip_name_encoding (name);
+
+      /* Silently ignore 'signal' if 'interrupt' is present.  AVR-LibC startet
+         using this when it switched from SIGNAL and INTERRUPT to ISR.  */
+
+      if (cfun->machine->is_interrupt)
+        cfun->machine->is_signal = 0;
+
+      /* Interrupt handlers must be  void __vector (void)  functions.  */
+
+      if (args && TREE_CODE (TREE_VALUE (args)) != VOID_TYPE)
+        error_at (loc, "%qs function cannot have arguments", isr);
+
+      if (TREE_CODE (ret) != VOID_TYPE)
+        error_at (loc, "%qs function cannot return a value", isr);
+
+      /* If the function has the 'signal' or 'interrupt' attribute, ensure
+         that the name of the function is "__vector_NN" so as to catch
+         when the user misspells the vector name.  */
+
+      if (!STR_PREFIX_P (name, "__vector"))
+        warning_at (loc, 0, "%qs appears to be a misspelled %s handler",
+                    name, isr);
+    }
+
+  /* Avoid the above diagnosis to be printed more than once.  */
+  
+  cfun->machine->attributes_checked_p = 1;
+}
+
+
 /* Implement `ACCUMULATE_OUTGOING_ARGS'.  */
-bool
+
+int
 avr_accumulate_outgoing_args (void)
 {
   if (!cfun)
@@ -700,8 +637,7 @@ static int
 avr_regs_to_save (HARD_REG_SET *set)
 {
   int reg, count;
-  int int_or_sig_p = (interrupt_function_p (current_function_decl)
-                      || signal_function_p (current_function_decl));
+  int int_or_sig_p = cfun->machine->is_interrupt || cfun->machine->is_signal;
 
   if (set)
     CLEAR_HARD_REG_SET (*set);
@@ -737,6 +673,16 @@ avr_regs_to_save (HARD_REG_SET *set)
   return count;
 }
 
+
+/* Implement `TARGET_ALLOCATE_STACK_SLOTS_FOR_ARGS' */
+
+static bool
+avr_allocate_stack_slots_for_args (void)
+{
+  return !cfun->machine->is_naked;
+}
+
+
 /* Return true if register FROM can be eliminated via register TO.  */
 
 static bool
@@ -813,9 +759,9 @@ avr_simple_epilogue (void)
           && get_frame_size () == 0
           && avr_outgoing_args_size() == 0
           && avr_regs_to_save (NULL) == 0
-          && ! interrupt_function_p (current_function_decl)
-          && ! signal_function_p (current_function_decl)
-          && ! avr_naked_function_p (current_function_decl)
+          && ! cfun->machine->is_interrupt
+          && ! cfun->machine->is_signal
+          && ! cfun->machine->is_naked
           && ! TREE_THIS_VOLATILE (current_function_decl));
 }
 
@@ -957,7 +903,11 @@ avr_prologue_setup_frame (HOST_WIDE_INT size, HARD_REG_SET set)
   bool isr_p = cfun->machine->is_interrupt || cfun->machine->is_signal;
   int live_seq = sequent_regs_live ();
 
+  HOST_WIDE_INT size_max
+    = (HOST_WIDE_INT) GET_MODE_MASK (AVR_HAVE_8BIT_SP ? QImode : Pmode);
+
   bool minimize = (TARGET_CALL_PROLOGUES
+                   && size < size_max
                    && live_seq
                    && !isr_p
                    && !cfun->machine->is_OS_task
@@ -1063,6 +1013,7 @@ avr_prologue_setup_frame (HOST_WIDE_INT size, HARD_REG_SET set)
               leaf function and thus X has already been saved.  */
               
           int irq_state = -1;
+          HOST_WIDE_INT size_cfa = size, neg_size;
           rtx fp_plus_insns, fp, my_fp;
 
           gcc_assert (frame_pointer_needed
@@ -1081,6 +1032,28 @@ avr_prologue_setup_frame (HOST_WIDE_INT size, HARD_REG_SET set)
               my_fp = all_regs_rtx[FRAME_POINTER_REGNUM];
             }
 
+          /* Cut down size and avoid size = 0 so that we don't run
+             into ICE like PR52488 in the remainder.  */
+
+          if (size > size_max)
+            {
+              /* Don't error so that insane code from newlib still compiles
+                 and does not break building newlib.  As PR51345 is implemented
+                 now, there are multilib variants with -msp8.
+                 
+                 If user wants sanity checks he can use -Wstack-usage=
+                 or similar options.
+
+                 For CFA we emit the original, non-saturated size so that
+                 the generic machinery is aware of the real stack usage and
+                 will print the above diagnostic as expected.  */
+              
+              size = size_max;
+            }
+
+          size = trunc_int_for_mode (size, GET_MODE (my_fp));
+          neg_size = trunc_int_for_mode (-size, GET_MODE (my_fp));
+          
           /************  Method 1: Adjust frame pointer  ************/
           
           start_sequence ();
@@ -1099,13 +1072,13 @@ avr_prologue_setup_frame (HOST_WIDE_INT size, HARD_REG_SET set)
                             gen_rtx_SET (VOIDmode, fp, stack_pointer_rtx));
             }
 
-          insn = emit_move_insn (my_fp, plus_constant (my_fp, -size));
+          insn = emit_move_insn (my_fp, plus_constant (my_fp, neg_size));
           if (frame_pointer_needed)
             {
               RTX_FRAME_RELATED_P (insn) = 1;
               add_reg_note (insn, REG_CFA_ADJUST_CFA,
                             gen_rtx_SET (VOIDmode, fp,
-                                         plus_constant (fp, -size)));
+                                         plus_constant (fp, -size_cfa)));
             }
           
           /* Copy to stack pointer.  Note that since we've already
@@ -1133,7 +1106,7 @@ avr_prologue_setup_frame (HOST_WIDE_INT size, HARD_REG_SET set)
               add_reg_note (insn, REG_CFA_ADJUST_CFA,
                             gen_rtx_SET (VOIDmode, stack_pointer_rtx,
                                          plus_constant (stack_pointer_rtx,
-                                                        -size)));
+                                                        -size_cfa)));
             }
           
           fp_plus_insns = get_insns ();
@@ -1156,7 +1129,7 @@ avr_prologue_setup_frame (HOST_WIDE_INT size, HARD_REG_SET set)
               add_reg_note (insn, REG_CFA_ADJUST_CFA,
                             gen_rtx_SET (VOIDmode, stack_pointer_rtx,
                                          plus_constant (stack_pointer_rtx,
-                                                        -size)));
+                                                        -size_cfa)));
               if (frame_pointer_needed)
                 {
                   insn = emit_move_insn (fp, stack_pointer_rtx);
@@ -1178,7 +1151,7 @@ avr_prologue_setup_frame (HOST_WIDE_INT size, HARD_REG_SET set)
               emit_insn (fp_plus_insns);
             }
 
-          cfun->machine->stack_usage += size;
+          cfun->machine->stack_usage += size_cfa;
         } /* !minimize && size != 0 */
     } /* !minimize */
 }
@@ -1194,12 +1167,6 @@ expand_prologue (void)
 
   size = get_frame_size() + avr_outgoing_args_size();
   
-  /* Init cfun->machine.  */
-  cfun->machine->is_naked = avr_naked_function_p (current_function_decl);
-  cfun->machine->is_interrupt = interrupt_function_p (current_function_decl);
-  cfun->machine->is_signal = signal_function_p (current_function_decl);
-  cfun->machine->is_OS_task = avr_OS_task_function_p (current_function_decl);
-  cfun->machine->is_OS_main = avr_OS_main_function_p (current_function_decl);
   cfun->machine->stack_usage = 0;
   
   /* Prologue: naked.  */
@@ -1253,11 +1220,11 @@ expand_prologue (void)
           emit_push_sfr (rampy_rtx, false /* frame-related */, true /* clr */);
         }
 
-      if (AVR_HAVE_RAMPZ 
+      if (AVR_HAVE_RAMPZ
           && TEST_HARD_REG_BIT (set, REG_Z)
           && TEST_HARD_REG_BIT (set, REG_Z + 1))
         {
-          emit_push_sfr (rampz_rtx, false /* frame-related */, true /* clr */);
+          emit_push_sfr (rampz_rtx, false /* frame-related */, AVR_HAVE_RAMPD);
         }
     }  /* is_interrupt is_signal */
 
@@ -1391,6 +1358,7 @@ expand_epilogue (bool sibcall_p)
       int irq_state = -1;
       rtx fp, my_fp;
       rtx fp_plus_insns;
+      HOST_WIDE_INT size_max;
 
       gcc_assert (frame_pointer_needed
                   || !isr_p
@@ -1407,6 +1375,13 @@ expand_epilogue (bool sibcall_p)
                   
           my_fp = all_regs_rtx[FRAME_POINTER_REGNUM];
         }
+
+      /* For rationale see comment in prologue generation.  */
+
+      size_max = (HOST_WIDE_INT) GET_MODE_MASK (GET_MODE (my_fp));
+      if (size > size_max)
+        size = size_max;
+      size = trunc_int_for_mode (size, GET_MODE (my_fp));
               
       /********** Method 1: Adjust fp register  **********/
               
@@ -1477,12 +1452,12 @@ expand_epilogue (bool sibcall_p)
       /* Restore RAMPZ/Y/X/D using tmp_reg as scratch.
          The conditions to restore them must be tha same as in prologue.  */
       
-      if (AVR_HAVE_RAMPX
-          && TEST_HARD_REG_BIT (set, REG_X)
-          && TEST_HARD_REG_BIT (set, REG_X + 1))
+      if (AVR_HAVE_RAMPZ
+          && TEST_HARD_REG_BIT (set, REG_Z)
+          && TEST_HARD_REG_BIT (set, REG_Z + 1))
         {
           emit_pop_byte (TMP_REGNO);
-          emit_move_insn (rampx_rtx, tmp_reg_rtx);
+          emit_move_insn (rampz_rtx, tmp_reg_rtx);
         }
 
       if (AVR_HAVE_RAMPY
@@ -1494,12 +1469,12 @@ expand_epilogue (bool sibcall_p)
           emit_move_insn (rampy_rtx, tmp_reg_rtx);
         }
 
-      if (AVR_HAVE_RAMPZ
-          && TEST_HARD_REG_BIT (set, REG_Z)
-          && TEST_HARD_REG_BIT (set, REG_Z + 1))
+      if (AVR_HAVE_RAMPX
+          && TEST_HARD_REG_BIT (set, REG_X)
+          && TEST_HARD_REG_BIT (set, REG_X + 1))
         {
           emit_pop_byte (TMP_REGNO);
-          emit_move_insn (rampz_rtx, tmp_reg_rtx);
+          emit_move_insn (rampx_rtx, tmp_reg_rtx);
         }
 
       if (AVR_HAVE_RAMPD)
@@ -1553,6 +1528,22 @@ avr_cannot_modify_jumps_p (void)
 }
 
 
+/* Implement `TARGET_MODE_DEPENDENT_ADDRESS_P'.  */
+
+/* FIXME:  PSImode addresses are not mode-dependent in themselves.
+      This hook just serves to hack around PR rtl-optimization/52543 by
+      claiming that PSImode addresses (which are used for the 24-bit
+      address space __memx) were mode-dependent so that lower-subreg.s
+      will skip these addresses.  See also the similar FIXME comment along
+      with mov<mode> expanders in avr.md.  */
+
+static bool
+avr_mode_dependent_address_p (const_rtx addr)
+{
+  return GET_MODE (addr) != Pmode;
+}
+
+
 /* Helper function for `avr_legitimate_address_p'.  */
 
 static inline bool
@@ -2184,6 +2175,12 @@ notice_update_cc (rtx body ATTRIBUTE_UNUSED, rtx insn)
        }
       break;
 
+    case CC_SET_VZN:
+      /* Insn like INC, DEC, NEG that set Z,N,V.  We currently don't make use
+         of this combination, cf. also PR61055.  */
+      CC_STATUS_INIT;
+      break;
+
     case CC_SET_CZN:
       /* Insn sets the Z,N,C flags of CC to recog_operand[0].
          The V flag may or may not be known but that's ok because
@@ -2532,17 +2529,17 @@ avr_function_ok_for_sibcall (tree decl_callee, tree exp_callee)
 
   /* Ensure that caller and callee have compatible epilogues */
   
-  if (interrupt_function_p (current_function_decl)
-      || signal_function_p (current_function_decl)
+  if (cfun->machine->is_interrupt
+      || cfun->machine->is_signal
+      || cfun->machine->is_naked
       || 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))
+          != cfun->machine->is_OS_task)
       || (avr_OS_main_function_p (decl_callee)
-          != avr_OS_main_function_p (current_function_decl)))
+          != cfun->machine->is_OS_main))
     {
       return false;
     }
@@ -2565,7 +2562,8 @@ avr_load_libgcc_p (rtx op)
         
   return (n_bytes > 2
           && !AVR_HAVE_LPMX
-          && avr_mem_flash_p (op));
+          && MEM_P (op)
+          && MEM_ADDR_SPACE (op) == ADDR_SPACE_FLASH);
 }
 
 /* Return true if a value of mode MODE is read by __xload_* function.  */
@@ -2576,156 +2574,7 @@ avr_xload_libgcc_p (enum machine_mode mode)
   int n_bytes = GET_MODE_SIZE (mode);
   
   return (n_bytes > 1
-          || avr_current_arch->n_segments > 1);
-}
-
-
-/* Find an unused d-register to be used as scratch in INSN.
-   EXCLUDE is either NULL_RTX or some register. In the case where EXCLUDE
-   is a register, skip all possible return values that overlap EXCLUDE.
-   The policy for the returned register is similar to that of
-   `reg_unused_after', i.e. the returned register may overlap the SET_DEST
-   of INSN.
-
-   Return a QImode d-register or NULL_RTX if nothing found.  */
-
-static rtx
-avr_find_unused_d_reg (rtx insn, rtx exclude)
-{
-  int regno;
-  bool isr_p = (interrupt_function_p (current_function_decl)
-                || signal_function_p (current_function_decl));
-
-  for (regno = 16; regno < 32; regno++)
-    {
-      rtx reg = all_regs_rtx[regno];
-      
-      if ((exclude
-           && reg_overlap_mentioned_p (exclude, reg))
-          || fixed_regs[regno])
-        {
-          continue;
-        }
-
-      /* Try non-live register */
-
-      if (!df_regs_ever_live_p (regno)
-          && (TREE_THIS_VOLATILE (current_function_decl)
-              || cfun->machine->is_OS_task
-              || cfun->machine->is_OS_main
-              || (!isr_p && call_used_regs[regno])))
-        {
-          return reg;
-        }
-
-      /* Any live register can be used if it is unused after.
-         Prologue/epilogue will care for it as needed.  */
-      
-      if (df_regs_ever_live_p (regno)
-          && reg_unused_after (insn, reg))
-        {
-          return reg;
-        }
-    }
-
-  return NULL_RTX;
-}
-
-
-/* Helper function for the next function in the case where only restricted
-   version of LPM instruction is available.  */
-
-static const char*
-avr_out_lpm_no_lpmx (rtx insn, rtx *xop, int *plen)
-{
-  rtx dest = xop[0];
-  rtx addr = xop[1];
-  int n_bytes = GET_MODE_SIZE (GET_MODE (dest));
-  int regno_dest;
-
-  regno_dest = REGNO (dest);
-
-  /* The implicit target register of LPM.  */
-  xop[3] = lpm_reg_rtx;
-
-  switch (GET_CODE (addr))
-    {
-    default:
-      gcc_unreachable();
-
-    case REG:
-
-      gcc_assert (REG_Z == REGNO (addr));
-
-      switch (n_bytes)
-        {
-        default:
-          gcc_unreachable();
-
-        case 1:
-          avr_asm_len ("%4lpm", xop, plen, 1);
-
-          if (regno_dest != LPM_REGNO)
-            avr_asm_len ("mov %0,%3", xop, plen, 1);
-
-          return "";
-
-        case 2:
-          if (REGNO (dest) == REG_Z)
-            return avr_asm_len ("%4lpm"      CR_TAB
-                                "push %3"    CR_TAB
-                                "adiw %2,1"  CR_TAB
-                                "%4lpm"      CR_TAB
-                                "mov %B0,%3" CR_TAB
-                                "pop %A0", xop, plen, 6);
-          
-          avr_asm_len ("%4lpm"      CR_TAB
-                       "mov %A0,%3" CR_TAB
-                       "adiw %2,1"  CR_TAB
-                       "%4lpm"      CR_TAB
-                       "mov %B0,%3", xop, plen, 5);
-                
-          if (!reg_unused_after (insn, addr))
-            avr_asm_len ("sbiw %2,1", xop, plen, 1);
-          
-          break; /* 2 */
-        }
-      
-      break; /* REG */
-
-    case POST_INC:
-
-      gcc_assert (REG_Z == REGNO (XEXP (addr, 0))
-                  && n_bytes <= 4);
-
-      if (regno_dest == LPM_REGNO)
-        avr_asm_len ("%4lpm"      CR_TAB
-                     "adiw %2,1", xop, plen, 2);
-      else
-        avr_asm_len ("%4lpm"      CR_TAB
-                     "mov %A0,%3" CR_TAB
-                     "adiw %2,1", xop, plen, 3);
-
-      if (n_bytes >= 2)
-        avr_asm_len ("%4lpm"      CR_TAB
-                     "mov %B0,%3" CR_TAB
-                     "adiw %2,1", xop, plen, 3);
-
-      if (n_bytes >= 3)
-        avr_asm_len ("%4lpm"      CR_TAB
-                     "mov %C0,%3" CR_TAB
-                     "adiw %2,1", xop, plen, 3);
-
-      if (n_bytes >= 4)
-        avr_asm_len ("%4lpm"      CR_TAB
-                     "mov %D0,%3" CR_TAB
-                     "adiw %2,1", xop, plen, 3);
-
-      break; /* POST_INC */
-      
-    } /* switch CODE (addr) */
-      
-  return "";
+          || avr_current_device->n_flash > 1);
 }
 
 
@@ -2737,13 +2586,11 @@ avr_out_lpm_no_lpmx (rtx insn, rtx *xop, int *plen)
 static const char*
 avr_out_lpm (rtx insn, rtx *op, int *plen)
 {
-  rtx xop[6];
+  rtx xop[3];
   rtx dest = op[0];
   rtx src = SET_SRC (single_set (insn));
   rtx addr;
   int n_bytes = GET_MODE_SIZE (GET_MODE (dest));
-  int regno_dest;
-  int segment;
   RTX_CODE code;
   addr_space_t as = MEM_ADDR_SPACE (src);
 
@@ -2764,58 +2611,18 @@ avr_out_lpm (rtx insn, rtx *op, int *plen)
   gcc_assert (REG_P (dest));
   gcc_assert (REG == code || POST_INC == code);
 
-  xop[0] = dest;
-  xop[1] = addr;
-  xop[2] = lpm_addr_reg_rtx;
-  xop[4] = xstring_empty;
-  xop[5] = tmp_reg_rtx;
-
-  regno_dest = REGNO (dest);
-
-  /* Cut down segment number to a number the device actually supports.
-     We do this late to preserve the address space's name for diagnostics.  */
-
-  segment = avr_addrspace[as].segment % avr_current_arch->n_segments;
-
-  /* Set RAMPZ as needed.  */
-
-  if (segment)
-    {
-      xop[4] = GEN_INT (segment);
-      
-      if (xop[3] = avr_find_unused_d_reg (insn, lpm_addr_reg_rtx),
-          xop[3])
-        {
-          avr_asm_len ("ldi %3,%4" CR_TAB
-                       "out __RAMPZ__,%3", xop, plen, 2);
-        }
-      else if (segment == 1)
-        {
-          avr_asm_len ("clr %5" CR_TAB
-                       "inc %5" CR_TAB
-                       "out __RAMPZ__,%5", xop, plen, 3);
-        }
-      else
-        {
-          avr_asm_len ("mov %5,%2"         CR_TAB
-                       "ldi %2,%4"         CR_TAB
-                       "out __RAMPZ__,%2"  CR_TAB
-                       "mov %2,%5", xop, plen, 4);
-        }
-      
-      xop[4] = xstring_e;
-
-      if (!AVR_HAVE_ELPMX)
-        return avr_out_lpm_no_lpmx (insn, xop, plen);
-    }
-  else if (!AVR_HAVE_LPMX)
-    {
-      return avr_out_lpm_no_lpmx (insn, xop, plen);
-    }
+  /* Only 1-byte moves from __flash are representes as open coded
+     mov insns.  All other loads from flash are not handled here but
+     by some UNSPEC instead, see respective FIXME in machine description.  */
+  
+  gcc_assert (as == ADDR_SPACE_FLASH);
+  gcc_assert (n_bytes == 1);
 
-  /* We have [E]LPMX: Output reading from Flash the comfortable way.  */
+  xop[0] = dest;
+  xop[1] = lpm_addr_reg_rtx;
+  xop[2] = lpm_reg_rtx;
 
-  switch (GET_CODE (addr))
+  switch (code)
     {
     default:
       gcc_unreachable();
@@ -2823,79 +2630,107 @@ avr_out_lpm (rtx insn, rtx *op, int *plen)
     case REG:
 
       gcc_assert (REG_Z == REGNO (addr));
+      
+      return AVR_HAVE_LPMX
+        ? avr_asm_len ("lpm %0,%a1", xop, plen, 1)
+        : avr_asm_len ("lpm" CR_TAB
+                       "mov %0,%2", xop, plen, 2);
+      
+    case POST_INC:
+      
+      gcc_assert (REG_Z == REGNO (XEXP (addr, 0)));
 
-      switch (n_bytes)
-        {
-        default:
-          gcc_unreachable();
-
-        case 1:
-          return avr_asm_len ("%4lpm %0,%a2", xop, plen, 1);
+      return AVR_HAVE_LPMX
+        ? avr_asm_len ("lpm %0,%a1+", xop, plen, 1)
+        : avr_asm_len ("lpm"        CR_TAB
+                       "adiw %1, 1" CR_TAB
+                       "mov %0,%2", xop, plen, 3);
+    }
 
-        case 2:
-          if (REGNO (dest) == REG_Z)
-            return avr_asm_len ("%4lpm %5,%a2+" CR_TAB
-                                "%4lpm %B0,%a2" CR_TAB
-                                "mov %A0,%5", xop, plen, 3);
-          else
-            {
-              avr_asm_len ("%4lpm %A0,%a2+" CR_TAB
-                           "%4lpm %B0,%a2", xop, plen, 2);
-                
-              if (!reg_unused_after (insn, addr))
-                avr_asm_len ("sbiw %2,1", xop, plen, 1);
-            }
-          
-          break; /* 2 */
+  return "";
+}
 
-        case 3:
 
-          avr_asm_len ("%4lpm %A0,%a2+" CR_TAB
-                       "%4lpm %B0,%a2+" CR_TAB
-                       "%4lpm %C0,%a2", xop, plen, 3);
-                
-          if (!reg_unused_after (insn, addr))
-            avr_asm_len ("sbiw %2,2", xop, plen, 1);
+/* If PLEN == NULL: Ouput instructions to load $0 with a value from
+   flash address $1:Z.  If $1 = 0 we can use LPM to read, otherwise
+   use ELPM.
+   If PLEN != 0 set *PLEN to the length in words of the instruction sequence.
+   Return "".  */
 
-          break; /* 3 */
+const char*
+avr_load_lpm (rtx insn, rtx *op, int *plen)
+{
+  rtx xop[4];
+  int n, n_bytes = GET_MODE_SIZE (GET_MODE (op[0]));
+  rtx xsegment = op[1];
+  bool clobber_z = PARALLEL == GET_CODE (PATTERN (insn));
+  bool r30_in_tmp = false;
+  
+  if (plen)
+    *plen = 0;
+  
+  xop[1] = lpm_addr_reg_rtx;
+  xop[2] = lpm_reg_rtx;
+  xop[3] = xstring_empty;
+  
+  /* Set RAMPZ as needed.  */
+  
+  if (REG_P (xsegment))
+    {
+      avr_asm_len ("out __RAMPZ__,%0", &xsegment, plen, 1);
+      xop[3] = xstring_e;
+    }
+  
+  /* Load the individual bytes from LSB to MSB.  */
+  
+  for (n = 0; n < n_bytes; n++)
+    {
+      xop[0] = all_regs_rtx[REGNO (op[0]) + n];
       
-        case 4:
-
-          avr_asm_len ("%4lpm %A0,%a2+" CR_TAB
-                       "%4lpm %B0,%a2+", xop, plen, 2);
-          
-          if (REGNO (dest) == REG_Z - 2)
-            return avr_asm_len ("%4lpm %5,%a2+" CR_TAB
-                                "%4lpm %C0,%a2"          CR_TAB
-                                "mov %D0,%5", xop, plen, 3);
-          else
+      if ((CONST_INT_P (xsegment) && AVR_HAVE_LPMX)
+          || (REG_P (xsegment) && AVR_HAVE_ELPMX))
+        {
+          if (n == n_bytes-1)
+            avr_asm_len ("%3lpm %0,%a1", xop, plen, 1);
+          else if (REGNO (xop[0]) == REG_Z)
             {
-              avr_asm_len ("%4lpm %C0,%a2+" CR_TAB
-                           "%4lpm %D0,%a2", xop, plen, 2);
-                
-              if (!reg_unused_after (insn, addr))
-                avr_asm_len ("sbiw %2,3", xop, plen, 1);
+              avr_asm_len ("%3lpm %2,%a1+", xop, plen, 1);
+              r30_in_tmp = true;
             }
+          else
+            avr_asm_len ("%3lpm %0,%a1+", xop, plen, 1);
+        }
+      else
+        {
+          gcc_assert (clobber_z);
+          
+          avr_asm_len ("%3lpm" CR_TAB
+                       "mov %0,%2", xop, plen, 2);
 
-          break; /* 4 */
-        } /* n_bytes */
+          if (n != n_bytes-1)
+            avr_asm_len ("adiw %1,1", xop, plen, 1);
+        }
+    }
+  
+  if (r30_in_tmp)
+    avr_asm_len ("mov %1,%2", xop, plen, 1);
+  
+  if (!clobber_z
+      && n_bytes > 1
+      && !reg_unused_after (insn, lpm_addr_reg_rtx)
+      && !reg_overlap_mentioned_p (op[0], lpm_addr_reg_rtx))
+    {
+      xop[2] = GEN_INT (n_bytes-1);
+      avr_asm_len ("sbiw %1,%2", xop, plen, 1);
+    }
+  
+  if (REG_P (xsegment) && AVR_HAVE_RAMPD)
+    {
+      /* Reset RAMPZ to 0 so that EBI devices don't read garbage from RAM */
       
-      break; /* REG */
-
-    case POST_INC:
-
-      gcc_assert (REG_Z == REGNO (XEXP (addr, 0))
-                  && n_bytes <= 4);
-
-      avr_asm_len                    ("%4lpm %A0,%a2+", xop, plen, 1);
-      if (n_bytes >= 2)  avr_asm_len ("%4lpm %B0,%a2+", xop, plen, 1);
-      if (n_bytes >= 3)  avr_asm_len ("%4lpm %C0,%a2+", xop, plen, 1);
-      if (n_bytes >= 4)  avr_asm_len ("%4lpm %D0,%a2+", xop, plen, 1);
-
-      break; /* POST_INC */
+      avr_asm_len ("out __RAMPZ__,__zero_reg__", xop, plen, 1);
+    }
 
-    } /* switch CODE (addr) */
-      
   return "";
 }
 
@@ -2915,8 +2750,9 @@ avr_out_xload (rtx insn ATTRIBUTE_UNUSED, rtx *op, int *plen)
   if (plen)
     *plen = 0;
 
-  avr_asm_len ("ld %3,%a2" CR_TAB
-               "sbrs %1,7", xop, plen, 2);
+  avr_asm_len ("sbrc %1,7" CR_TAB
+               "ld %3,%a2" CR_TAB
+               "sbrs %1,7", xop, plen, 3);
 
   avr_asm_len (AVR_HAVE_LPMX ? "lpm %3,%a2" : "lpm", xop, plen, 1);
 
@@ -2927,13 +2763,11 @@ avr_out_xload (rtx insn ATTRIBUTE_UNUSED, rtx *op, int *plen)
 }
 
 
-const char *
-output_movqi (rtx insn, rtx operands[], int *l)
+const char*
+output_movqi (rtx insn, rtx operands[], int *real_l)
 {
-  int dummy;
   rtx dest = operands[0];
   rtx src = operands[1];
-  int *real_l = l;
   
   if (avr_mem_flash_p (src)
       || avr_mem_flash_p (dest))
@@ -2941,10 +2775,8 @@ output_movqi (rtx insn, rtx operands[], int *l)
       return avr_out_lpm (insn, operands, real_l);
     }
 
-  if (!l)
-    l = &dummy;
-
-  *l = 1;
+  if (real_l)
+    *real_l = 1;
   
   if (register_operand (dest, QImode))
     {
@@ -2962,10 +2794,10 @@ output_movqi (rtx insn, rtx operands[], int *l)
           output_reload_in_const (operands, NULL_RTX, real_l, false);
           return "";
         }
-      else if (GET_CODE (src) == MEM)
+      else if (MEM_P (src))
        return out_movqi_r_mr (insn, operands, real_l); /* mov r,m */
     }
-  else if (GET_CODE (dest) == MEM)
+  else if (MEM_P (dest))
     {
       rtx xop[2];
 
@@ -3019,7 +2851,7 @@ output_movhi (rtx insn, rtx xop[], int *plen)
             }
           else if (test_hard_reg_class (STACK_REG, src))
             {
-              return AVR_HAVE_8BIT_SP
+              return !AVR_HAVE_SPH
                 ? avr_asm_len ("in %A0,__SP_L__" CR_TAB
                                "clr %B0", xop, plen, -2)
                 
@@ -3721,15 +3553,16 @@ avr_out_load_psi (rtx insn, rtx *op, int *plen)
                                   "mov r27,__tmp_reg__", op, plen, -6);
             }
           
-            avr_asm_len ("adiw r26,%o1"      CR_TAB
-                         "ld r24,X+"         CR_TAB
-                         "ld r25,X+"         CR_TAB
-                         "ld r26,X", op, plen, -4);
+          avr_asm_len ("adiw r26,%o1" CR_TAB
+                       "ld %A0,X+"    CR_TAB
+                       "ld %B0,X+"    CR_TAB
+                       "ld %C0,X", op, plen, -4);
 
-            if (reg_dest != REG_X - 2)
-              avr_asm_len ("sbiw r26,%o1+2", op, plen, 1);
+          if (reg_dest != REG_W
+              && !reg_unused_after (insn, XEXP (base, 0)))
+            avr_asm_len ("sbiw r26,%o1+2", op, plen, 1);
 
-            return "";
+          return "";
         }
       
       if (reg_dest == reg_base)
@@ -4018,8 +3851,8 @@ avr_out_movhi_mr_r_xmega (rtx insn, rtx op[], int *plen)
       else
         avr_asm_len ("st X+,%A1" CR_TAB
                      "st X,%B1", op, plen, -2);
-            
-      return reg_unused_after (insn, src)
+
+      return reg_unused_after (insn, base)
         ? ""
         : avr_asm_len ("sbiw r26,1", op, plen, 1);
     }
@@ -6122,7 +5955,7 @@ avr_out_plus_1 (rtx *xop, int *plen, enum rtx_code code, int *pcc)
                                op, plen, 1);
 
                   if (n_bytes == 2 && PLUS == code)
-                      *pcc = CC_SET_ZN;
+                      *pcc = CC_SET_CZN;
                 }
 
               i++;
@@ -6144,6 +5977,7 @@ avr_out_plus_1 (rtx *xop, int *plen, enum rtx_code code, int *pcc)
         {
           avr_asm_len ((code == PLUS) ^ (val8 == 1) ? "dec %0" : "inc %0",
                        op, plen, 1);
+          *pcc = CC_CLOBBER;
           break;
         }
 
@@ -6666,6 +6500,7 @@ adjust_insn_length (rtx insn, int len)
     case ADJUST_LEN_MOV32: output_movsisf (insn, op, &len); break;
     case ADJUST_LEN_MOVMEM: avr_out_movmem (insn, op, &len); break;
     case ADJUST_LEN_XLOAD: avr_out_xload (insn, op, &len); break;
+    case ADJUST_LEN_LOAD_LPM: avr_load_lpm (insn, op, &len); break;
 
     case ADJUST_LEN_TSTHI: avr_out_tsthi (insn, op, &len); break;
     case ADJUST_LEN_TSTPSI: avr_out_tstpsi (insn, op, &len); break;
@@ -6893,40 +6728,6 @@ avr_assemble_integer (rtx x, unsigned int size, int 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 (!STR_PREFIX_P (name, "__vector"))
-        {
-          warning_at (DECL_SOURCE_LOCATION (decl), 0,
-                      "%qs appears to be a misspelled interrupt handler",
-                      name);
-        }
-    }
-  else if (cfun->machine->is_signal)
-    {
-      if (!STR_PREFIX_P (name, "__vector"))
-        {
-           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);
-}
-
-
 /* Return value is nonzero if pseudos that have been
    assigned to registers of class CLASS would likely be spilled
    because registers of CLASS are needed for spill registers.  */
@@ -7100,6 +6901,7 @@ avr_nonconst_pointer_addrspace (tree typ)
 
   if (POINTER_TYPE_P (typ))
     {
+      addr_space_t as;
       tree target = TREE_TYPE (typ);
 
       /* Pointer to function: Test the function's return type.  */
@@ -7112,12 +6914,16 @@ avr_nonconst_pointer_addrspace (tree typ)
       while (TREE_CODE (target) == ARRAY_TYPE)
         target = TREE_TYPE (target);
 
-      if (!ADDR_SPACE_GENERIC_P (TYPE_ADDR_SPACE (target))
-          && !TYPE_READONLY (target))
-        {
-          /* Pointers to non-generic address space must be const.  */
+      /* Pointers to non-generic address space must be const.
+         Refuse address spaces outside the device's flash.  */
           
-          return TYPE_ADDR_SPACE (target);
+      as = TYPE_ADDR_SPACE (target);
+        
+      if (!ADDR_SPACE_GENERIC_P (as)
+          && (!TYPE_READONLY (target)
+              || avr_addrspace[as].segment >= avr_current_device->n_flash))
+        {
+          return as;
         }
 
       /* Scan pointer's target type.  */
@@ -7179,12 +6985,26 @@ avr_pgm_check_var_decl (tree node)
 
   if (reason)
     {
-      if (TYPE_P (node))
-        error ("pointer targeting address space %qs must be const in %qT",
-               avr_addrspace[as].name, node);
+      if (avr_addrspace[as].segment >= avr_current_device->n_flash)
+        {
+          if (TYPE_P (node))
+            error ("%qT uses address space %qs beyond flash of %qs",
+                   node, avr_addrspace[as].name, avr_current_device->name);
+          else
+            error ("%s %q+D uses address space %qs beyond flash of %qs",
+                   reason, node, avr_addrspace[as].name,
+                   avr_current_device->name);
+        }
       else
-        error ("pointer targeting address space %qs must be const in %s %q+D",
-               avr_addrspace[as].name, reason, node);
+        {
+          if (TYPE_P (node))
+            error ("pointer targeting address space %qs must be const in %qT",
+                   avr_addrspace[as].name, node);
+          else
+            error ("pointer targeting address space %qs must be const"
+                   " in %s %q+D",
+                   avr_addrspace[as].name, reason, node);
+        }
     }
 
   return reason == NULL;
@@ -7202,6 +7022,7 @@ avr_insert_attributes (tree node, tree *attributes)
       && (TREE_STATIC (node) || DECL_EXTERNAL (node))
       && avr_progmem_p (node, *attributes))
     {
+      addr_space_t as;
       tree node0 = node;
 
       /* For C++, we have to peel arrays in order to get correct
@@ -7213,11 +7034,19 @@ avr_insert_attributes (tree node, tree *attributes)
 
       if (error_mark_node == node0)
         return;
+
+      as = TYPE_ADDR_SPACE (TREE_TYPE (node));
+
+      if (avr_addrspace[as].segment >= avr_current_device->n_flash)
+        {
+          error ("variable %q+D located in address space %qs"
+                 " beyond flash of %qs",
+                 node, avr_addrspace[as].name, avr_current_device->name);
+        }
       
       if (!TYPE_READONLY (node0)
           && !TREE_READONLY (node))
         {
-          addr_space_t as = TYPE_ADDR_SPACE (TREE_TYPE (node));
           const char *reason = "__attribute__((progmem))";
 
           if (!ADDR_SPACE_GENERIC_P (as))
@@ -7244,7 +7073,11 @@ avr_asm_output_aligned_decl_common (FILE * stream,
                                     unsigned HOST_WIDE_INT size,
                                     unsigned int align, bool local_p)
 {
-  avr_need_clear_bss_p = true;
+  /* __gnu_lto_v1 etc. are just markers for the linker injected by toplev.c.
+     There is no need to trigger __do_clear_bss code for them.  */
+
+  if (!STR_PREFIX_P (name, "__gnu_lto"))
+    avr_need_clear_bss_p = true;
 
   if (local_p)
     ASM_OUTPUT_ALIGNED_LOCAL (stream, name, size, align);
@@ -7294,8 +7127,6 @@ avr_output_progmem_section_asm_op (const void *data)
 static void
 avr_asm_init_sections (void)
 {
-  unsigned int n;
-  
   /* Set up a section for jump tables.  Alignment is handled by
      ASM_OUTPUT_BEFORE_CASE_LABEL.  */
   
@@ -7314,13 +7145,6 @@ avr_asm_init_sections (void)
                                ",\"ax\",@progbits");
     }
 
-  for (n = 0; n < sizeof (progmem_section) / sizeof (*progmem_section); n++)
-    {
-      progmem_section[n]
-        = get_unnamed_section (0, avr_output_progmem_section_asm_op,
-                               progmem_section_prefix[n]);
-    }
-  
   /* Override section callbacks to keep track of `avr_need_clear_bss_p'
      resp. `avr_need_copy_data_p'.  */
   
@@ -7398,10 +7222,9 @@ avr_asm_named_section (const char *name, unsigned int flags, tree decl)
   if (flags & AVR_SECTION_PROGMEM)
     {
       addr_space_t as = (flags & AVR_SECTION_PROGMEM) / SECTION_MACH_DEP;
-      int segment = avr_addrspace[as].segment % avr_current_arch->n_segments;
       const char *old_prefix = ".rodata";
-      const char *new_prefix = progmem_section_prefix[segment];
-      
+      const char *new_prefix = avr_addrspace[as].section_name;
+
       if (STR_PREFIX_P (name, old_prefix))
         {
           const char *sname = ACONCAT ((new_prefix,
@@ -7513,13 +7336,18 @@ avr_asm_select_section (tree decl, int reloc, unsigned HOST_WIDE_INT align)
       && avr_progmem_p (decl, DECL_ATTRIBUTES (decl)))
     {
       addr_space_t as = TYPE_ADDR_SPACE (TREE_TYPE (decl));
-      int segment = avr_addrspace[as].segment % avr_current_arch->n_segments;
+
+      /* __progmem__ goes in generic space but shall be allocated to
+         .progmem.data  */
+
+      if (ADDR_SPACE_GENERIC_P (as))
+        as = ADDR_SPACE_FLASH;
       
       if (sect->common.flags & SECTION_NAMED)
         {
           const char * name = sect->named.name;
           const char * old_prefix = ".rodata";
-          const char * new_prefix = progmem_section_prefix[segment];
+          const char * new_prefix = avr_addrspace[as].section_name;
 
           if (STR_PREFIX_P (name, old_prefix))
             {
@@ -7528,8 +7356,15 @@ avr_asm_select_section (tree decl, int reloc, unsigned HOST_WIDE_INT align)
               return get_section (sname, sect->common.flags, sect->named.decl);
             }
         }
-          
-      return progmem_section[segment];
+
+      if (!progmem_section[as])
+        {
+          progmem_section[as]
+            = get_unnamed_section (0, avr_output_progmem_section_asm_op,
+                                   avr_addrspace[as].section_name);
+        }
+
+      return progmem_section[as];
     }
 
   return sect;
@@ -7550,7 +7385,7 @@ avr_file_start (void)
 
   /* Print I/O addresses of some SFRs used with IN and OUT.  */
 
-  if (!AVR_HAVE_8BIT_SP)
+  if (AVR_HAVE_SPH)
     fprintf (asm_out_file, "__SP_H__ = 0x%02x\n", avr_addr.sp_h - sfr_offset);
 
   fprintf (asm_out_file, "__SP_L__ = 0x%02x\n", avr_addr.sp_l - sfr_offset);
@@ -9071,9 +8906,31 @@ avr_hard_regno_mode_ok (int regno, enum machine_mode mode)
 }
 
 
+/* Implement `HARD_REGNO_CALL_PART_CLOBBERED'.  */
+
+int
+avr_hard_regno_call_part_clobbered (unsigned regno, enum machine_mode mode)
+{
+  /* FIXME: This hook gets called with MODE:REGNO combinations that don't
+        represent valid hard registers like, e.g. HI:29.  Returning TRUE
+        for such registers can lead to performance degradation as mentioned
+        in PR53595.  Thus, report invalid hard registers as FALSE.  */
+  
+  if (!avr_hard_regno_mode_ok (regno, mode))
+    return 0;
+  
+  /* Return true if any of the following boundaries is crossed:
+     17/18, 27/28 and 29/30.  */
+  
+  return ((regno < 18 && regno + GET_MODE_SIZE (mode) > 18)
+          || (regno < REG_Y && regno + GET_MODE_SIZE (mode) > REG_Y)
+          || (regno < REG_Z && regno + GET_MODE_SIZE (mode) > REG_Z));
+}
+
+
 /* Implement `MODE_CODE_BASE_REG_CLASS'.  */
 
-reg_class_t
+enum reg_class
 avr_mode_code_base_reg_class (enum machine_mode mode ATTRIBUTE_UNUSED,
                               addr_space_t as, RTX_CODE outer_code,
                               RTX_CODE index_code ATTRIBUTE_UNUSED)
@@ -9470,15 +9327,6 @@ avr_out_reload_inpsi (rtx *op, rtx clobber_reg, int *len)
   return "";
 }
 
-void
-avr_output_bld (rtx operands[], int bit_nr)
-{
-  static char s[] = "bld %A0,0";
-
-  s[5] = 'A' + (bit_nr >> 3);
-  s[8] = '0' + (bit_nr & 7);
-  output_asm_insn (s, operands);
-}
 
 void
 avr_output_addr_vec_elt (FILE *stream, int value)
@@ -9593,23 +9441,10 @@ avr_out_sbxx_branch (rtx insn, rtx operands[])
 
     case REG:
 
-      if (GET_MODE (operands[1]) == QImode)
-        {
-          if (comp == EQ)
-            output_asm_insn ("sbrs %1,%2", operands);
-          else
-            output_asm_insn ("sbrc %1,%2", operands);
-        }
-      else  /* HImode, PSImode or SImode */
-        {
-          static char buf[] = "sbrc %A1,0";
-          unsigned int bit_nr = UINTVAL (operands[2]);
-
-          buf[3] = (comp == EQ) ? 's' : 'c';
-          buf[6] = 'A' + (bit_nr / 8);
-          buf[9] = '0' + (bit_nr % 8);
-          output_asm_insn (buf, operands);
-        }
+      if (comp == EQ)
+        output_asm_insn ("sbrs %T1%T2", operands);
+      else
+        output_asm_insn ("sbrc %T1%T2", operands);
 
       break; /* REG */
     }        /* switch */
@@ -9688,7 +9523,8 @@ avr_addr_space_pointer_mode (addr_space_t as)
 static bool
 avr_reg_ok_for_pgm_addr (rtx reg, bool strict)
 {
-  gcc_assert (REG_P (reg));
+  if (!REG_P (reg))
+    return false;
 
   if (strict)
     {
@@ -9847,7 +9683,7 @@ avr_addr_space_convert (rtx src, tree type_from, tree type_to)
              
       msb = ADDR_SPACE_GENERIC_P (as_from)
         ? 0x80
-        : avr_addrspace[as_from].segment % avr_current_arch->n_segments;
+        : avr_addrspace[as_from].segment;
 
       src = force_reg (Pmode, src);
       
@@ -9902,7 +9738,7 @@ avr_emit_movmemhi (rtx *xop)
   HOST_WIDE_INT count;
   enum machine_mode loop_mode;
   addr_space_t as = MEM_ADDR_SPACE (xop[1]);
-  rtx loop_reg, addr0, addr1, a_src, a_dest, insn, xas, reg_x;
+  rtx loop_reg, addr1, a_src, a_dest, insn, xas;
   rtx a_hi8 = NULL_RTX;
 
   if (avr_mem_flash_p (xop[0]))
@@ -9931,10 +9767,10 @@ avr_emit_movmemhi (rtx *xop)
     }
   else
     {
-      int segment = avr_addrspace[as].segment % avr_current_arch->n_segments;
+      int segment = avr_addrspace[as].segment;
       
       if (segment
-          && avr_current_arch->n_segments > 1)
+          && avr_current_device->n_flash > 1)
         {
           a_hi8 = GEN_INT (segment);
           emit_move_insn (rampz_rtx, a_hi8 = copy_to_mode_reg (QImode, a_hi8));
@@ -9958,11 +9794,7 @@ avr_emit_movmemhi (rtx *xop)
         X = destination address  */
 
   emit_move_insn (lpm_addr_reg_rtx, addr1);
-  addr1 = lpm_addr_reg_rtx;
-
-  reg_x = gen_rtx_REG (HImode, REG_X);
-  emit_move_insn (reg_x, a_dest);
-  addr0 = reg_x;
+  emit_move_insn (gen_rtx_REG (HImode, REG_X), a_dest);
 
   /* FIXME: Register allocator does a bad job and might spill address
         register(s) inside the loop leading to additional move instruction
@@ -9977,23 +9809,19 @@ avr_emit_movmemhi (rtx *xop)
       /* Load instruction ([E]LPM or LD) is known at compile time:
          Do the copy-loop inline.  */
       
-      rtx (*fun) (rtx, rtx, rtx, rtx, rtx, rtx, rtx, rtx)
+      rtx (*fun) (rtx, rtx, rtx)
         = QImode == loop_mode ? gen_movmem_qi : gen_movmem_hi;
 
-      insn = fun (addr0, addr1, xas, loop_reg,
-                  addr0, addr1, tmp_reg_rtx, loop_reg);
+      insn = fun (xas, loop_reg, loop_reg);
     }
   else
     {
-      rtx loop_reg16 = gen_rtx_REG (HImode, 24);
-      rtx r23 = gen_rtx_REG (QImode, 23);
-      rtx (*fun) (rtx, rtx, rtx, rtx, rtx, rtx, rtx, rtx, rtx, rtx, rtx)
+      rtx (*fun) (rtx, rtx)
         = QImode == loop_mode ? gen_movmemx_qi : gen_movmemx_hi;
 
-      emit_move_insn (r23, a_hi8);
+      emit_move_insn (gen_rtx_REG (QImode, 23), a_hi8);
       
-      insn = fun (addr0, addr1, xas, loop_reg, addr0, addr1,
-                  lpm_reg_rtx, loop_reg16, r23, r23, GEN_INT (avr_addr.rampz));
+      insn = fun (xas, GEN_INT (avr_addr.rampz));
     }
 
   set_mem_addr_space (SET_SRC (XVECEXP (insn, 0, 0)), as);
@@ -10004,32 +9832,27 @@ avr_emit_movmemhi (rtx *xop)
 
 
 /* Print assembler for movmem_qi, movmem_hi insns...
-       $0, $4 : & dest
-       $1, $5 : & src
-       $2     : Address Space
-       $3, $7 : Loop register
-       $6     : Scratch register
-
-   ...and movmem_qi_elpm, movmem_hi_elpm insns.
-   
-       $8, $9 : hh8 (& src)
-       $10    : RAMPZ_ADDR
+       $0     : Address Space
+       $1, $2 : Loop register
+       Z      : Source address
+       X      : Destination address
 */
 
 const char*
-avr_out_movmem (rtx insn ATTRIBUTE_UNUSED, rtx *xop, int *plen)
+avr_out_movmem (rtx insn ATTRIBUTE_UNUSED, rtx *op, int *plen)
 {
-  addr_space_t as = (addr_space_t) INTVAL (xop[2]);
-  enum machine_mode loop_mode = GET_MODE (xop[3]);
-
-  bool sbiw_p = test_hard_reg_class (ADDW_REGS, xop[3]);
-
-  gcc_assert (REG_X == REGNO (xop[0])
-              && REG_Z == REGNO (xop[1]));
+  addr_space_t as = (addr_space_t) INTVAL (op[0]);
+  enum machine_mode loop_mode = GET_MODE (op[1]);
+  bool sbiw_p = test_hard_reg_class (ADDW_REGS, op[1]);
+  rtx xop[3];
 
   if (plen)
     *plen = 0;
 
+  xop[0] = op[0];
+  xop[1] = op[1];
+  xop[2] = tmp_reg_rtx;
+
   /* Loop label */
 
   avr_asm_len ("0:", xop, plen, 0);
@@ -10043,16 +9866,16 @@ avr_out_movmem (rtx insn ATTRIBUTE_UNUSED, rtx *xop, int *plen)
       
     case ADDR_SPACE_GENERIC:
 
-      avr_asm_len ("ld %6,%a1+", xop, plen, 1);
+      avr_asm_len ("ld %2,Z+", xop, plen, 1);
       break;
       
     case ADDR_SPACE_FLASH:
 
       if (AVR_HAVE_LPMX)
-        avr_asm_len ("lpm %6,%a1+", xop, plen, 1);
+        avr_asm_len ("lpm %2,Z+", xop, plen, 1);
       else
         avr_asm_len ("lpm" CR_TAB
-                     "adiw %1,1", xop, plen, 2);
+                     "adiw r30,1", xop, plen, 2);
       break;
       
     case ADDR_SPACE_FLASH1:
@@ -10062,31 +9885,31 @@ avr_out_movmem (rtx insn ATTRIBUTE_UNUSED, rtx *xop, int *plen)
     case ADDR_SPACE_FLASH5:
 
       if (AVR_HAVE_ELPMX)
-        avr_asm_len ("elpm %6,%a1+", xop, plen, 1);
+        avr_asm_len ("elpm %2,Z+", xop, plen, 1);
       else
         avr_asm_len ("elpm" CR_TAB
-                     "adiw %1,1", xop, plen, 2);
+                     "adiw r30,1", xop, plen, 2);
       break;
     }
 
   /* Store with post-increment */
 
-  avr_asm_len ("st %a0+,%6", xop, plen, 1);
+  avr_asm_len ("st X+,%2", xop, plen, 1);
 
   /* Decrement loop-counter and set Z-flag */
 
   if (QImode == loop_mode)
     {
-      avr_asm_len ("dec %3", xop, plen, 1);
+      avr_asm_len ("dec %1", xop, plen, 1);
     }
   else if (sbiw_p)
     {
-      avr_asm_len ("sbiw %3,1", xop, plen, 1);
+      avr_asm_len ("sbiw %1,1", xop, plen, 1);
     }
   else
     {
-      avr_asm_len ("subi %A3,1" CR_TAB
-                   "sbci %B3,0", xop, plen, 2);
+      avr_asm_len ("subi %A1,1" CR_TAB
+                   "sbci %B1,0", xop, plen, 2);
     }
 
   /* Loop until zero */
@@ -10098,10 +9921,18 @@ avr_out_movmem (rtx insn ATTRIBUTE_UNUSED, rtx *xop, int *plen)
 \f
 /* Helper for __builtin_avr_delay_cycles */
 
+static rtx
+avr_mem_clobber (void)
+{
+  rtx mem = gen_rtx_MEM (BLKmode, gen_rtx_SCRATCH (Pmode));
+  MEM_VOLATILE_P (mem) = 1;
+  return mem;
+}
+
 static void
 avr_expand_delay_cycles (rtx operands0)
 {
-  unsigned HOST_WIDE_INT cycles = UINTVAL (operands0);
+  unsigned HOST_WIDE_INT cycles = UINTVAL (operands0) & GET_MODE_MASK (SImode);
   unsigned HOST_WIDE_INT cycles_used;
   unsigned HOST_WIDE_INT loop_count;
   
@@ -10109,7 +9940,8 @@ avr_expand_delay_cycles (rtx operands0)
     {
       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)));
+      emit_insn (gen_delay_cycles_4 (gen_int_mode (loop_count, SImode),
+                                     avr_mem_clobber()));
       cycles -= cycles_used;
     }
   
@@ -10119,7 +9951,8 @@ avr_expand_delay_cycles (rtx operands0)
       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)));
+      emit_insn (gen_delay_cycles_3 (gen_int_mode (loop_count, SImode),
+                                     avr_mem_clobber()));
       cycles -= cycles_used;
     }
   
@@ -10129,7 +9962,8 @@ avr_expand_delay_cycles (rtx operands0)
       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)));
+      emit_insn (gen_delay_cycles_2 (gen_int_mode (loop_count, HImode),
+                                     avr_mem_clobber()));
       cycles -= cycles_used;
     }
   
@@ -10139,7 +9973,8 @@ avr_expand_delay_cycles (rtx operands0)
       if (loop_count > 255) 
         loop_count = 255;
       cycles_used = loop_count * 3;
-      emit_insn (gen_delay_cycles_1 (gen_int_mode (loop_count, QImode)));
+      emit_insn (gen_delay_cycles_1 (gen_int_mode (loop_count, QImode),
+                                     avr_mem_clobber()));
       cycles -= cycles_used;
       }
   
@@ -10508,19 +10343,50 @@ avr_out_insert_bits (rtx *op, int *plen)
 
 enum avr_builtin_id
   {
-    AVR_BUILTIN_NOP,
-    AVR_BUILTIN_SEI,
-    AVR_BUILTIN_CLI,
-    AVR_BUILTIN_WDR,
-    AVR_BUILTIN_SLEEP,
-    AVR_BUILTIN_SWAP,
-    AVR_BUILTIN_INSERT_BITS,
-    AVR_BUILTIN_FMUL,
-    AVR_BUILTIN_FMULS,
-    AVR_BUILTIN_FMULSU,
-    AVR_BUILTIN_DELAY_CYCLES
+    
+#define DEF_BUILTIN(NAME, N_ARGS, ID, TYPE, CODE) ID,
+#include "builtins.def"  
+#undef DEF_BUILTIN
+
+    AVR_BUILTIN_COUNT
+  };
+
+struct GTY(()) avr_builtin_description
+{
+  enum insn_code icode;
+  const char *name;
+  int n_args;
+  tree fndecl;
+};
+
+
+/* Notice that avr_bdesc[] and avr_builtin_id are initialized in such a way
+   that a built-in's ID can be used to access the built-in by means of
+   avr_bdesc[ID]  */
+
+static GTY(()) struct avr_builtin_description
+avr_bdesc[AVR_BUILTIN_COUNT] =
+  {
+
+#define DEF_BUILTIN(NAME, N_ARGS, ID, TYPE, ICODE)      \
+    { ICODE, NAME, N_ARGS, NULL_TREE },
+#include "builtins.def"  
+#undef DEF_BUILTIN
   };
 
+
+/* Implement `TARGET_BUILTIN_DECL'.  */
+
+static tree
+avr_builtin_decl (unsigned id, bool initialize_p ATTRIBUTE_UNUSED)
+{
+  if (id < AVR_BUILTIN_COUNT)
+    return avr_bdesc[id].fndecl;
+
+  return error_mark_node;
+}
+
+
 static void
 avr_init_builtin_int24 (void)
 {
@@ -10531,13 +10397,6 @@ avr_init_builtin_int24 (void)
   (*lang_hooks.types.register_builtin_type) (uint24_type, "__uint24");
 }
 
-#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.  */
@@ -10578,57 +10437,29 @@ avr_init_builtins (void)
                                 unsigned_char_type_node,
                                 NULL_TREE);
 
-  DEF_BUILTIN ("__builtin_avr_nop", void_ftype_void, AVR_BUILTIN_NOP);
-  DEF_BUILTIN ("__builtin_avr_sei", void_ftype_void, AVR_BUILTIN_SEI);
-  DEF_BUILTIN ("__builtin_avr_cli", void_ftype_void, AVR_BUILTIN_CLI);
-  DEF_BUILTIN ("__builtin_avr_wdr", void_ftype_void, AVR_BUILTIN_WDR);
-  DEF_BUILTIN ("__builtin_avr_sleep", void_ftype_void, AVR_BUILTIN_SLEEP);
-  DEF_BUILTIN ("__builtin_avr_swap", uchar_ftype_uchar, AVR_BUILTIN_SWAP);
-  DEF_BUILTIN ("__builtin_avr_delay_cycles", void_ftype_ulong, 
-               AVR_BUILTIN_DELAY_CYCLES);
-
-  DEF_BUILTIN ("__builtin_avr_fmul", uint_ftype_uchar_uchar, 
-               AVR_BUILTIN_FMUL);
-  DEF_BUILTIN ("__builtin_avr_fmuls", int_ftype_char_char, 
-               AVR_BUILTIN_FMULS);
-  DEF_BUILTIN ("__builtin_avr_fmulsu", int_ftype_char_uchar, 
-               AVR_BUILTIN_FMULSU);
-
-  DEF_BUILTIN ("__builtin_avr_insert_bits", uchar_ftype_ulong_uchar_uchar,
-               AVR_BUILTIN_INSERT_BITS);
+  tree const_memx_void_node
+      = build_qualified_type (void_type_node,
+                              TYPE_QUAL_CONST
+                              | ENCODE_QUAL_ADDR_SPACE (ADDR_SPACE_MEMX));
 
-  avr_init_builtin_int24 ();
-}
+  tree const_memx_ptr_type_node
+      = build_pointer_type_for_mode (const_memx_void_node, PSImode, false);
+  
+  tree char_ftype_const_memx_ptr
+      = build_function_type_list (char_type_node,
+                                  const_memx_ptr_type_node,
+                                  NULL);
 
+#define DEF_BUILTIN(NAME, N_ARGS, ID, TYPE, CODE)                       \
+  gcc_assert (ID < AVR_BUILTIN_COUNT);                                  \
+  avr_bdesc[ID].fndecl                                                  \
+    = add_builtin_function (NAME, TYPE, ID, BUILT_IN_MD, NULL, NULL_TREE);
+#include "builtins.def"  
 #undef DEF_BUILTIN
+  
+  avr_init_builtin_int24 ();
+}
 
-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 }
-  };
-
-static const struct avr_builtin_description
-bdesc_3arg[] =
-  {
-    { CODE_FOR_insert_bits, "__builtin_avr_insert_bits",
-      AVR_BUILTIN_INSERT_BITS }
-  };
 
 /* Subroutine of avr_expand_builtin to take care of unop insns.  */
 
@@ -10798,6 +10629,7 @@ avr_expand_triop_builtin (enum insn_code icode, tree exp, rtx target)
 }
 
 
+/* Implement `TARGET_EXPAND_BUILTIN'.  */
 /* 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).
@@ -10810,45 +10642,31 @@ avr_expand_builtin (tree exp, rtx target,
                     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);
   const char* bname = IDENTIFIER_POINTER (DECL_NAME (fndecl));
   unsigned int id = DECL_FUNCTION_CODE (fndecl);
+  const struct avr_builtin_description *d = &avr_bdesc[id];
   tree arg0;
   rtx op0;
 
+  gcc_assert (id < AVR_BUILTIN_COUNT);
+
   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))
+        if (!CONST_INT_P (op0))
           error ("%s expects a compile time integer constant", bname);
+        else
+          avr_expand_delay_cycles (op0);
 
-        avr_expand_delay_cycles (op0);
         return 0;
       }
 
@@ -10866,18 +10684,24 @@ avr_expand_builtin (tree exp, rtx target,
       }
     }
 
-  for (i = 0, d = bdesc_1arg; i < ARRAY_SIZE (bdesc_1arg); i++, d++)
-    if (d->id == id)
+  /* No special treatment needed: vanilla expand.  */
+  
+  switch (d->n_args)
+    {
+    case 0:
+      emit_insn ((GEN_FCN (d->icode)) (target));
+      return 0;
+      
+    case 1:
       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)
+      
+    case 2:
       return avr_expand_binop_builtin (d->icode, exp, target);
-
-  for (i = 0, d = bdesc_3arg; i < ARRAY_SIZE (bdesc_3arg); i++, d++)
-    if (d->id == id)
+      
+    case 3:
       return avr_expand_triop_builtin (d->icode, exp, target);
-
+    }
+  
   gcc_unreachable ();
 }
 
@@ -10899,17 +10723,32 @@ avr_fold_builtin (tree fndecl, int n_args ATTRIBUTE_UNUSED, tree *arg,
     default:
       break;
 
+    case AVR_BUILTIN_SWAP:
+      {
+        return fold_build2 (LROTATE_EXPR, val_type, arg[0],
+                            build_int_cst (val_type, 4));
+      }
+  
     case AVR_BUILTIN_INSERT_BITS:
       {
         tree tbits = arg[1];
         tree tval = arg[2];
         tree tmap;
         tree map_type = TREE_VALUE (TYPE_ARG_TYPES (TREE_TYPE (fndecl)));
-        double_int map = tree_to_double_int (arg[0]);
+        double_int map;
         bool changed = false;
         unsigned i;
         avr_map_op_t best_g;
+
+        if (TREE_CODE (arg[0]) != INTEGER_CST)
+          {
+            /* No constant as first argument: Don't fold this and run into
+               error in avr_expand_builtin.  */
+            
+            break;
+          }
         
+        map = tree_to_double_int (arg[0]);
         tmap = double_int_to_tree (map_type, map);
 
         if (TREE_CODE (tval) != INTEGER_CST
@@ -11023,8 +10862,153 @@ avr_fold_builtin (tree fndecl, int n_args ATTRIBUTE_UNUSED, tree *arg,
   return NULL_TREE;
 }
 
-
 \f
+
+/* Initialize the GCC target structure.  */
+
+#undef  TARGET_ASM_ALIGNED_HI_OP
+#define TARGET_ASM_ALIGNED_HI_OP "\t.word\t"
+#undef  TARGET_ASM_ALIGNED_SI_OP
+#define TARGET_ASM_ALIGNED_SI_OP "\t.long\t"
+#undef  TARGET_ASM_UNALIGNED_HI_OP
+#define TARGET_ASM_UNALIGNED_HI_OP "\t.word\t"
+#undef  TARGET_ASM_UNALIGNED_SI_OP
+#define TARGET_ASM_UNALIGNED_SI_OP "\t.long\t"
+#undef  TARGET_ASM_INTEGER
+#define TARGET_ASM_INTEGER avr_assemble_integer
+#undef  TARGET_ASM_FILE_START
+#define TARGET_ASM_FILE_START avr_file_start
+#undef  TARGET_ASM_FILE_END
+#define TARGET_ASM_FILE_END avr_file_end
+
+#undef  TARGET_ASM_FUNCTION_END_PROLOGUE
+#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_INSERT_ATTRIBUTES
+#define TARGET_INSERT_ATTRIBUTES avr_insert_attributes
+#undef  TARGET_SECTION_TYPE_FLAGS
+#define TARGET_SECTION_TYPE_FLAGS avr_section_type_flags
+
+#undef  TARGET_ASM_NAMED_SECTION
+#define TARGET_ASM_NAMED_SECTION avr_asm_named_section
+#undef  TARGET_ASM_INIT_SECTIONS
+#define TARGET_ASM_INIT_SECTIONS avr_asm_init_sections
+#undef  TARGET_ENCODE_SECTION_INFO
+#define TARGET_ENCODE_SECTION_INFO avr_encode_section_info
+#undef  TARGET_ASM_SELECT_SECTION
+#define TARGET_ASM_SELECT_SECTION avr_asm_select_section
+
+#undef  TARGET_REGISTER_MOVE_COST
+#define TARGET_REGISTER_MOVE_COST avr_register_move_cost
+#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_SET_CURRENT_FUNCTION
+#define TARGET_SET_CURRENT_FUNCTION avr_set_current_function
+
+#undef  TARGET_RETURN_IN_MEMORY
+#define TARGET_RETURN_IN_MEMORY avr_return_in_memory
+
+#undef  TARGET_STRICT_ARGUMENT_NAMING
+#define TARGET_STRICT_ARGUMENT_NAMING hook_bool_CUMULATIVE_ARGS_true
+
+#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_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_ALLOCATE_STACK_SLOTS_FOR_ARGS
+#define TARGET_ALLOCATE_STACK_SLOTS_FOR_ARGS avr_allocate_stack_slots_for_args
+
+#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_CANNOT_MODIFY_JUMPS_P
+#define TARGET_CANNOT_MODIFY_JUMPS_P avr_cannot_modify_jumps_p
+
+#undef  TARGET_FUNCTION_OK_FOR_SIBCALL
+#define TARGET_FUNCTION_OK_FOR_SIBCALL avr_function_ok_for_sibcall
+
+#undef  TARGET_INIT_BUILTINS
+#define TARGET_INIT_BUILTINS avr_init_builtins
+
+#undef  TARGET_BUILTIN_DECL
+#define TARGET_BUILTIN_DECL avr_builtin_decl
+
+#undef  TARGET_EXPAND_BUILTIN
+#define TARGET_EXPAND_BUILTIN avr_expand_builtin
+
+#undef  TARGET_FOLD_BUILTIN
+#define TARGET_FOLD_BUILTIN avr_fold_builtin
+
+#undef  TARGET_ASM_FUNCTION_RODATA_SECTION
+#define TARGET_ASM_FUNCTION_RODATA_SECTION avr_asm_function_rodata_section
+
+#undef  TARGET_SCALAR_MODE_SUPPORTED_P
+#define TARGET_SCALAR_MODE_SUPPORTED_P avr_scalar_mode_supported_p
+
+#undef  TARGET_ADDR_SPACE_SUBSET_P
+#define TARGET_ADDR_SPACE_SUBSET_P avr_addr_space_subset_p
+
+#undef  TARGET_ADDR_SPACE_CONVERT
+#define TARGET_ADDR_SPACE_CONVERT avr_addr_space_convert
+
+#undef  TARGET_ADDR_SPACE_ADDRESS_MODE
+#define TARGET_ADDR_SPACE_ADDRESS_MODE avr_addr_space_address_mode
+
+#undef  TARGET_ADDR_SPACE_POINTER_MODE
+#define TARGET_ADDR_SPACE_POINTER_MODE avr_addr_space_pointer_mode
+
+#undef  TARGET_ADDR_SPACE_LEGITIMATE_ADDRESS_P
+#define TARGET_ADDR_SPACE_LEGITIMATE_ADDRESS_P  \
+  avr_addr_space_legitimate_address_p
+
+#undef  TARGET_ADDR_SPACE_LEGITIMIZE_ADDRESS
+#define TARGET_ADDR_SPACE_LEGITIMIZE_ADDRESS avr_addr_space_legitimize_address
+
+#undef  TARGET_MODE_DEPENDENT_ADDRESS_P
+#define TARGET_MODE_DEPENDENT_ADDRESS_P avr_mode_dependent_address_p
+
+#undef  TARGET_PRINT_OPERAND
+#define TARGET_PRINT_OPERAND avr_print_operand
+#undef  TARGET_PRINT_OPERAND_ADDRESS
+#define TARGET_PRINT_OPERAND_ADDRESS avr_print_operand_address
+#undef  TARGET_PRINT_OPERAND_PUNCT_VALID_P
+#define TARGET_PRINT_OPERAND_PUNCT_VALID_P avr_print_operand_punct_valid_p
+
 struct gcc_target targetm = TARGET_INITIALIZER;
 
+\f
 #include "gt-avr.h"