re PR target/49868 (Implement named address space to place/access data in flash memory)
authorGeorg-Johann Lay <avr@gjlay.de>
Fri, 18 Nov 2011 16:44:00 +0000 (16:44 +0000)
committerGeorg-Johann Lay <gjl@gcc.gnu.org>
Fri, 18 Nov 2011 16:44:00 +0000 (16:44 +0000)
gcc/
PR target/49868
* config/avr/avr.h (base_arch_s): Add field n_segments.
(ADDR_SPACE_PGM1, ADDR_SPACE_PGM2, ADDR_SPACE_PGM3,
ADDR_SPACE_PGM4, ADDR_SPACE_PGM5, ADDR_SPACE_PGMX): New address spaces.
(AVR_HAVE_ELPM, AVR_HAVE_ELPMX): New defines.
(INIT_EXPANDERS): New define.
* config/avr/avr-protos.h (avr_mem_pgmx_p): New.
(avr_init_expanders): New.
(avr_emit_movmemhi, avr_out_movmem): New.
(avr_xload_libgcc_p): New.
* config/avr/avr-c.c (avr_register_target_pragmas): Register
address spaces __pgm1, __pgm2,  __pgm3,  __pgm4  __pgm5,  __pgmx.
(avr_cpu_cpp_builtins): Add built-in defines __PGM1,
__PGM2, __PGM3, __PGM4, __PGM5, __PGMX.
* config/avr/avr-devices.c (avr_arch_types): Set field n_segments.

* config/avr/avr.c (AVR_SECTION_PROGMEM): Change define to cover
3 bits instead of just 1.
(xstring_empty, xstring_e, rampz_rtx): New static GTYed variables.
(progmem_section): Change from section to array of sections.
(progmem_section_prefix): New static variable.
(avr_file_start): Print set for __RAMPZ__
(avr_option_override): Move initialization of RTXes from here...
(avr_init_expanders): ...to this new function.
(avr_pgm_segment): New static function.
(avr_decl_pgm_p): Handle error_mark_node.
(avr_mem_pgmx_p, avr_decl_pgmx_p): New static functions.
(avr_out_xload, avr_find_unused_d_reg): New static functions.
(expand_prologue, expand_epilogue): Use rampz_rtx.
(print_operand): Hande CONST_STRING.
(avr_xload_libgcc_p): New static function.
(avr_out_lpm_no_lpmx, avr_out_lpm): Handle ELPM.
(avr_progmem_p): Return 2 for 24-bit flash address space.
(avr_out_sbxx_branch): Clean-up code from ASn macros.
(out_movqi_r_mr, out_movqi_mr_r): Ditto. And recognize RAMPZ's
address and print symbolically.
(avr_asm_named_section, avr_section_type_flags,
avr_encode_section_info, avr_asm_select_section,
avr_addr_space_address_mode, avr_addr_space_pointer_mode,
avr_addr_space_legitimate_address_p, avr_addr_space_convert,
avr_addr_space_legitimize_address): Handle new address spaces.
(avr_output_progmem_section_asm_op): New static function.
(avr_asm_init_sections): Initialize progmem_section[].
(adjust_insn_length): Handle ADJUST_LEN_XLOAD, ADJUST_LEN_MOVMEM.
(avr_const_address_lo16): New static function.
(avr_assemble_integer): Use it to handle 3-byte integers.
(avr_emit_movmemhi, avr_out_movmem): New functions.

* config/avr/predicates.md (nox_general_operand): Handle new
address spaces.
* config/avr/avr.md (unspec): Add UNSPEC_MOVMEM.
(adjust_len): Add xload, movmem.
(SP_ADDR): New define_constants.
(isa): Add "lpm", "lpmx", "elpm", "elpmx".
(enabled): Handle them.
(load<mode>_libgcc): New expander.
(*load.<mode>.libgcc): Rename to load_<mode>_libgcc.
(xload8_A, xload<mode>_A): New insn-and-splits.
(xload_8, xload_<mode>_libgcc, xload_<mode>, loadmem_elpm): New insns.
(mov<mode>): Handle new address spaces.
(movmemhi): Rewrite using avr_emit_movmemhi.
(MOVMEM_r_d): New mode attribute.
(movmem_<mode>, movmem_qi_elpm): New insns.
(setmemhi, *clrmemqi, *clrmemhi, strlenhi, *strlenhi): Unquote
C-code.  Use label instead of hard-coded instrunction lengths.

libgcc/
PR target/49868
* config/avr/t-avr (LIB1ASMFUNCS): Add _xload_2 _xload_3 _xload_4.
* config/avr/lib1funcs.S (__xload_2, __xload_3, __xload_4):
New functions.

From-SVN: r181482

gcc/ChangeLog
gcc/config/avr/avr-c.c
gcc/config/avr/avr-devices.c
gcc/config/avr/avr-protos.h
gcc/config/avr/avr.c
gcc/config/avr/avr.h
gcc/config/avr/avr.md
gcc/config/avr/predicates.md
libgcc/ChangeLog
libgcc/config/avr/lib1funcs.S
libgcc/config/avr/t-avr

index 6fb03faab6fa0981837de81b60de42e96f5f553f..57bba242ad32f7430fe990454f7cae29d42dd16a 100644 (file)
@@ -1,3 +1,71 @@
+2011-11-18  Georg-Johann Lay  <avr@gjlay.de>
+
+       PR target/49868
+       * config/avr/avr.h (base_arch_s): Add field n_segments.
+       (ADDR_SPACE_PGM1, ADDR_SPACE_PGM2, ADDR_SPACE_PGM3,
+       ADDR_SPACE_PGM4, ADDR_SPACE_PGM5, ADDR_SPACE_PGMX): New address spaces.
+       (AVR_HAVE_ELPM, AVR_HAVE_ELPMX): New defines.
+       (INIT_EXPANDERS): New define.
+       * config/avr/avr-protos.h (avr_mem_pgmx_p): New.
+       (avr_init_expanders): New.
+       (avr_emit_movmemhi, avr_out_movmem): New.
+       (avr_xload_libgcc_p): New.
+       * config/avr/avr-c.c (avr_register_target_pragmas): Register
+       address spaces __pgm1, __pgm2,  __pgm3,  __pgm4  __pgm5,  __pgmx.
+       (avr_cpu_cpp_builtins): Add built-in defines __PGM1,
+       __PGM2, __PGM3, __PGM4, __PGM5, __PGMX.
+       * config/avr/avr-devices.c (avr_arch_types): Set field n_segments.
+
+       * config/avr/avr.c (AVR_SECTION_PROGMEM): Change define to cover
+       3 bits instead of just 1.
+       (xstring_empty, xstring_e, rampz_rtx): New static GTYed variables.
+       (progmem_section): Change from section to array of sections.
+       (progmem_section_prefix): New static variable.
+       (avr_file_start): Print set for __RAMPZ__
+       (avr_option_override): Move initialization of RTXes from here...
+       (avr_init_expanders): ...to this new function.
+       (avr_pgm_segment): New static function.
+       (avr_decl_pgm_p): Handle error_mark_node.
+       (avr_mem_pgmx_p, avr_decl_pgmx_p): New static functions.
+       (avr_out_xload, avr_find_unused_d_reg): New static functions.
+       (expand_prologue, expand_epilogue): Use rampz_rtx.
+       (print_operand): Hande CONST_STRING.
+       (avr_xload_libgcc_p): New static function.
+       (avr_out_lpm_no_lpmx, avr_out_lpm): Handle ELPM.
+       (avr_progmem_p): Return 2 for 24-bit flash address space.
+       (avr_out_sbxx_branch): Clean-up code from ASn macros.
+       (out_movqi_r_mr, out_movqi_mr_r): Ditto. And recognize RAMPZ's
+       address and print symbolically.
+       (avr_asm_named_section, avr_section_type_flags,
+       avr_encode_section_info, avr_asm_select_section,
+       avr_addr_space_address_mode, avr_addr_space_pointer_mode,
+       avr_addr_space_legitimate_address_p, avr_addr_space_convert,
+       avr_addr_space_legitimize_address): Handle new address spaces.
+       (avr_output_progmem_section_asm_op): New static function.
+       (avr_asm_init_sections): Initialize progmem_section[].
+       (adjust_insn_length): Handle ADJUST_LEN_XLOAD, ADJUST_LEN_MOVMEM.
+       (avr_const_address_lo16): New static function.
+       (avr_assemble_integer): Use it to handle 3-byte integers.
+       (avr_emit_movmemhi, avr_out_movmem): New functions.
+       
+       * config/avr/predicates.md (nox_general_operand): Handle new
+       address spaces.
+       * config/avr/avr.md (unspec): Add UNSPEC_MOVMEM.
+       (adjust_len): Add xload, movmem.
+       (SP_ADDR): New define_constants.
+       (isa): Add "lpm", "lpmx", "elpm", "elpmx".
+       (enabled): Handle them.
+       (load<mode>_libgcc): New expander.
+       (*load.<mode>.libgcc): Rename to load_<mode>_libgcc.
+       (xload8_A, xload<mode>_A): New insn-and-splits.
+       (xload_8, xload_<mode>_libgcc, xload_<mode>, loadmem_elpm): New insns.
+       (mov<mode>): Handle new address spaces.
+       (movmemhi): Rewrite using avr_emit_movmemhi.
+       (MOVMEM_r_d): New mode attribute.
+       (movmem_<mode>, movmem_qi_elpm): New insns.
+       (setmemhi, *clrmemqi, *clrmemhi, strlenhi, *strlenhi): Unquote
+       C-code.  Use label instead of hard-coded instrunction lengths.
+
 2011-11-18  Martin Jambor  <mjambor@suse.cz>
 
        PR tree-optimization/50605
index 63408c033ca0dd3b971b62b1a62481f31a3167c1..5fbc51cc70dfdb2832a4c2c8c9576262a8e25ccb 100644 (file)
@@ -37,6 +37,12 @@ void
 avr_register_target_pragmas (void)
 {
   c_register_addr_space ("__pgm", ADDR_SPACE_PGM);
+  c_register_addr_space ("__pgm1", ADDR_SPACE_PGM1);
+  c_register_addr_space ("__pgm2", ADDR_SPACE_PGM2);
+  c_register_addr_space ("__pgm3", ADDR_SPACE_PGM3);
+  c_register_addr_space ("__pgm4", ADDR_SPACE_PGM4);
+  c_register_addr_space ("__pgm5", ADDR_SPACE_PGM5);
+  c_register_addr_space ("__pgmx", ADDR_SPACE_PGMX);
 }
 
 
@@ -109,6 +115,12 @@ avr_cpu_cpp_builtins (struct cpp_reader *pfile)
   if (!strcmp (lang_hooks.name, "GNU C"))
     {
       cpp_define (pfile, "__PGM=__pgm");
+      cpp_define (pfile, "__PGM1=__pgm1");
+      cpp_define (pfile, "__PGM2=__pgm2");
+      cpp_define (pfile, "__PGM3=__pgm3");
+      cpp_define (pfile, "__PGM4=__pgm4");
+      cpp_define (pfile, "__PGM5=__pgm5");
+      cpp_define (pfile, "__PGMX=__pgmx");
     }
 
   /* Define builtin macros so that the user can
index d0dda8536af90097c18361594ea78bdd82df35d7..e0e473833360df665851f99bb21af61bb4f6c85b 100644 (file)
 
 /* List of all known AVR MCU architectures.  */
 
-const struct base_arch_s avr_arch_types[] = {
-  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x0060, NULL,               "avr2" },  /* unknown device specified */
-  { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0x0060, "__AVR_ARCH__=1",   "avr1" },
-  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x0060, "__AVR_ARCH__=2",   "avr2" },
-  { 0, 0, 0, 1, 0, 0, 0, 0, 0, 0x0060, "__AVR_ARCH__=25",  "avr25" },
-  { 0, 0, 1, 0, 0, 0, 0, 0, 0, 0x0060, "__AVR_ARCH__=3",   "avr3" },
-  { 0, 0, 1, 0, 1, 0, 0, 0, 0, 0x0060, "__AVR_ARCH__=31",  "avr31" },
-  { 0, 0, 1, 1, 0, 0, 0, 0, 0, 0x0060, "__AVR_ARCH__=35",  "avr35" },
-  { 0, 1, 0, 1, 0, 0, 0, 0, 0, 0x0060, "__AVR_ARCH__=4",   "avr4" },
-  { 0, 1, 1, 1, 0, 0, 0, 0, 0, 0x0060, "__AVR_ARCH__=5",   "avr5" },
-  { 0, 1, 1, 1, 1, 1, 0, 0, 0, 0x0060, "__AVR_ARCH__=51",  "avr51" },
-  { 0, 1, 1, 1, 1, 1, 1, 0, 0, 0x0060, "__AVR_ARCH__=6",   "avr6" }
+const struct base_arch_s
+avr_arch_types[] =
+{
+  /* unknown device specified */
+  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x0060, 1, NULL,               "avr2" },
+  /*
+    A  M  J  L  E  E  E         d  S   # F
+    S  U  M  P  L  L  I         a  t   6 l 
+    M  L  P  M  P  P  J  -  -   t  a   4 a   
+             X  M  M  M         a  r     s
+                   X  P            t   k h  */
+  { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0x0060, 1, "__AVR_ARCH__=1",   "avr1" },
+  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x0060, 1, "__AVR_ARCH__=2",   "avr2" },
+  { 0, 0, 0, 1, 0, 0, 0, 0, 0, 0x0060, 1, "__AVR_ARCH__=25",  "avr25" },
+  { 0, 0, 1, 0, 0, 0, 0, 0, 0, 0x0060, 1, "__AVR_ARCH__=3",   "avr3" },
+  { 0, 0, 1, 0, 1, 0, 0, 0, 0, 0x0060, 2, "__AVR_ARCH__=31",  "avr31" },
+  { 0, 0, 1, 1, 0, 0, 0, 0, 0, 0x0060, 1, "__AVR_ARCH__=35",  "avr35" },
+  { 0, 1, 0, 1, 0, 0, 0, 0, 0, 0x0060, 1, "__AVR_ARCH__=4",   "avr4" },
+  { 0, 1, 1, 1, 0, 0, 0, 0, 0, 0x0060, 1, "__AVR_ARCH__=5",   "avr5" },
+  { 0, 1, 1, 1, 1, 1, 0, 0, 0, 0x0060, 2, "__AVR_ARCH__=51",  "avr51" },
+  { 0, 1, 1, 1, 1, 1, 1, 0, 0, 0x0060, 4, "__AVR_ARCH__=6",   "avr6" }
 };
 
 const struct mcu_type_s avr_mcu_types[] = {
index 6c017e19b409d58d19d74b1c7cd6bd84d8ebebe3..c71bc9df6812b211a58b1cefdf9994502c2f6d87 100644 (file)
@@ -34,6 +34,7 @@ extern int avr_hard_regno_rename_ok (unsigned int, unsigned int);
 extern rtx avr_return_addr_rtx (int count, rtx tem);
 extern void avr_register_target_pragmas (void);
 extern bool avr_accumulate_outgoing_args (void);
+extern void avr_init_expanders (void);
 
 #ifdef TREE_CODE
 extern void avr_asm_output_aligned_decl_common (FILE*, const_tree, const char*, unsigned HOST_WIDE_INT, unsigned int, bool);
@@ -84,6 +85,7 @@ extern bool avr_rotate_bytes (rtx operands[]);
 
 extern void expand_prologue (void);
 extern void expand_epilogue (bool);
+extern bool avr_emit_movmemhi (rtx*);
 extern int avr_epilogue_uses (int regno);
 extern int avr_starting_frame_offset (void);
 
@@ -94,6 +96,8 @@ extern const char* avr_out_bitop (rtx, rtx*, int*);
 extern const char* avr_out_plus (rtx*, int*, int*);
 extern const char* avr_out_plus_noclobber (rtx*, int*, int*);
 extern const char* avr_out_addto_sp (rtx*, int*);
+extern const char* avr_out_xload (rtx, rtx*, int*);
+extern const char* avr_out_movmem (rtx, rtx*, int*);
 extern bool avr_popcount_each_byte (rtx, int, int);
 
 extern int extra_constraint_Q (rtx x);
@@ -122,7 +126,9 @@ extern bool avr_regno_mode_code_ok_for_base_p (int, enum machine_mode, addr_spac
 extern rtx avr_incoming_return_addr_rtx (void);
 extern rtx avr_legitimize_reload_address (rtx*, enum machine_mode, int, int, int, int, rtx (*)(rtx,int));
 extern bool avr_mem_pgm_p (rtx);
+extern bool avr_mem_pgmx_p (rtx);
 extern bool avr_load_libgcc_p (rtx);
+extern bool avr_xload_libgcc_p (enum machine_mode);
 #endif /* RTX_CODE */
 
 #ifdef REAL_VALUE_TYPE
index f33c0c509035673a8c1890db0ca1c040387a85a4..e7b902d8534ae290e14dffc619dcd221ffd3c11f 100644 (file)
 /* Return true if STR starts with PREFIX and false, otherwise.  */
 #define STR_PREFIX_P(STR,PREFIX) (0 == strncmp (STR, PREFIX, strlen (PREFIX)))
 
-#define AVR_SECTION_PROGMEM (SECTION_MACH_DEP << 0)
+/* The 4 bits starting at SECTION_MACH_DEP are reverved to store
+   1 + flash segment where progmem data is to be located.
+   For example, data with __pgm2 is stored as (1+2) * SECTION_MACH_DEP.  */
+#define AVR_SECTION_PROGMEM (0xf * SECTION_MACH_DEP)
 
 
 /* Prototypes for local helper functions.  */
@@ -97,6 +100,13 @@ static GTY(()) rtx tmp_reg_rtx;
 /* Zeroed register RTX (gen_rtx_REG (QImode, ZERO_REGNO)) */
 static GTY(()) rtx zero_reg_rtx;
 
+/* RAMPZ special function register */
+static GTY(()) rtx rampz_rtx;
+
+/* RTX containing the strings "" and "e", respectively */
+static GTY(()) rtx xstring_empty;
+static GTY(()) rtx xstring_e;
+
 /* RTXs for all general purpose registers as QImode */
 static GTY(()) rtx all_regs_rtx[32];
 
@@ -116,7 +126,17 @@ const struct mcu_type_s *avr_current_device;
 static GTY(()) section *progmem_swtable_section;
 
 /* Unnamed section associated to __attribute__((progmem)) aka. PROGMEM.  */
-static GTY(()) section *progmem_section;
+static GTY(()) section *progmem_section[6];
+
+static const char * const progmem_section_prefix[6] =
+  {
+    ".progmem.data",
+    ".progmem1.data",
+    ".progmem2.data",
+    ".progmem3.data",
+    ".progmem4.data",
+    ".progmem5.data"
+  };
 
 /* To track if code will use .bss and/or .data.  */
 bool avr_need_clear_bss_p = false;
@@ -317,8 +337,6 @@ avr_popcount_each_byte (rtx xval, int n_bytes, int pop_mask)
 static void
 avr_option_override (void)
 {
-  int regno;
-  
   flag_delete_null_pointer_checks = 0;
 
   /* caller-save.c looks for call-clobbered hard registers that are assigned
@@ -347,15 +365,6 @@ avr_option_override (void)
   avr_current_arch = &avr_arch_types[avr_current_device->arch];
   avr_extra_arch_macro = avr_current_device->macro;
 
-  for (regno = 0; regno < 32; regno ++)
-    all_regs_rtx[regno] = gen_rtx_REG (QImode, regno);
-
-  lpm_reg_rtx  = all_regs_rtx[LPM_REGNO];
-  tmp_reg_rtx  = all_regs_rtx[TMP_REGNO];
-  zero_reg_rtx = all_regs_rtx[ZERO_REGNO];
-
-  lpm_addr_reg_rtx = gen_rtx_REG (HImode, REG_Z);
-
   init_machine_status = avr_init_machine_status;
 
   avr_log_set_avr_log();
@@ -369,6 +378,38 @@ avr_init_machine_status (void)
   return ggc_alloc_cleared_machine_function ();
 }
 
+
+/* Implement `INIT_EXPANDERS'.  */
+/* The function works like a singleton.  */
+
+void
+avr_init_expanders (void)
+{
+  int regno;
+
+  static bool done = false;
+
+  if (done)
+    return;
+  else
+    done = true;
+
+  for (regno = 0; regno < 32; regno ++)
+    all_regs_rtx[regno] = gen_rtx_REG (QImode, regno);
+
+  lpm_reg_rtx  = all_regs_rtx[LPM_REGNO];
+  tmp_reg_rtx  = all_regs_rtx[TMP_REGNO];
+  zero_reg_rtx = all_regs_rtx[ZERO_REGNO];
+
+  lpm_addr_reg_rtx = gen_rtx_REG (HImode, REG_Z);
+
+  rampz_rtx = gen_rtx_MEM (QImode, GEN_INT (RAMPZ_ADDR));
+
+  xstring_empty = gen_rtx_CONST_STRING (VOIDmode, "");
+  xstring_e = gen_rtx_CONST_STRING (VOIDmode, "e");
+}
+
+
 /* Return register class for register R.  */
 
 enum reg_class
@@ -414,18 +455,60 @@ avr_scalar_mode_supported_p (enum machine_mode mode)
 }
 
 
+/* Return the segment number of pgm address space AS, i.e.
+   the 64k block it lives in.
+   Return -1 if unknown, i.e. 24-bit AS in flash.
+   Return -2 for anything else.  */
+
+static int
+avr_pgm_segment (addr_space_t as)
+{
+  switch (as)
+    {
+    default: return -2;
+
+    case ADDR_SPACE_PGMX:  return -1;
+    case ADDR_SPACE_PGM:   return 0;
+    case ADDR_SPACE_PGM1:  return 1;
+    case ADDR_SPACE_PGM2:  return 2;
+    case ADDR_SPACE_PGM3:  return 3;
+    case ADDR_SPACE_PGM4:  return 4;
+    case ADDR_SPACE_PGM5:  return 5;
+    }
+}
+
+
 /* Return TRUE if DECL is a VAR_DECL located in Flash and FALSE, otherwise.  */
 
 static bool
 avr_decl_pgm_p (tree decl)
 {
-  if (TREE_CODE (decl) != VAR_DECL)
-    return false;
+  if (TREE_CODE (decl) != VAR_DECL
+      || TREE_TYPE (decl) == error_mark_node)
+    {
+      return false;
+    }
 
   return !ADDR_SPACE_GENERIC_P (TYPE_ADDR_SPACE (TREE_TYPE (decl)));
 }
 
 
+/* Return TRUE if DECL is a VAR_DECL located in the 24-bit Flash
+   address space and FALSE, otherwise.  */
+static bool
+avr_decl_pgmx_p (tree decl)
+{
+  if (TREE_CODE (decl) != VAR_DECL
+      || TREE_TYPE (decl) == error_mark_node)
+    {
+      return false;
+    }
+
+  return (ADDR_SPACE_PGMX == TYPE_ADDR_SPACE (TREE_TYPE (decl)));
+}
+
+
 /* Return TRUE if X is a MEM rtx located in Flash and FALSE, otherwise.  */
 
 bool
@@ -436,6 +519,17 @@ avr_mem_pgm_p (rtx x)
 }
 
 
+/* Return TRUE if X is a MEM rtx located in the 24-bit Flash
+   address space and FALSE, otherwise.  */
+
+bool
+avr_mem_pgmx_p (rtx x)
+{
+  return (MEM_P (x)
+          && ADDR_SPACE_PGMX == MEM_ADDR_SPACE (x));
+}
+
+
 /* A helper for the subsequent function attribute used to dig for
    attribute 'name' in a FUNCTION_DECL or FUNCTION_TYPE */
 
@@ -1041,8 +1135,7 @@ expand_prologue (void)
           && TEST_HARD_REG_BIT (set, REG_Z)
           && TEST_HARD_REG_BIT (set, REG_Z + 1))
         {
-          emit_move_insn (tmp_reg_rtx,
-                          gen_rtx_MEM (QImode, GEN_INT (RAMPZ_ADDR)));
+          emit_move_insn (tmp_reg_rtx, rampz_rtx);
           emit_push_byte (TMP_REGNO, false);
         }
         
@@ -1280,8 +1373,7 @@ expand_epilogue (bool sibcall_p)
           && TEST_HARD_REG_BIT (set, REG_Z + 1))
         {
           emit_pop_byte (TMP_REGNO);
-          emit_move_insn (gen_rtx_MEM (QImode, GEN_INT (RAMPZ_ADDR)), 
-                          tmp_reg_rtx);
+          emit_move_insn (rampz_rtx, tmp_reg_rtx);
         }
 
       /* Restore SREG using tmp reg as scratch.  */
@@ -1795,6 +1887,8 @@ print_operand (FILE *file, rtx x, int code)
       REAL_VALUE_TO_TARGET_SINGLE (rv, val);
       fprintf (file, "0x%lx", val);
     }
+  else if (GET_CODE (x) == CONST_STRING)
+    fputs (XSTR (x, 0), file);
   else if (code == 'j')
     fputs (cond_string (GET_CODE (x)), file);
   else if (code == 'k')
@@ -2247,6 +2341,70 @@ avr_load_libgcc_p (rtx op)
           && avr_mem_pgm_p (op));
 }
 
+/* Return true if a value of mode MODE is read by __xload_* function.  */
+
+bool
+avr_xload_libgcc_p (enum machine_mode mode)
+{
+  int n_bytes = GET_MODE_SIZE (mode);
+  
+  return (n_bytes > 1
+          && avr_current_arch->n_segments > 1
+          && !AVR_HAVE_ELPMX);
+}
+
+
+/* Find an unused d-register to be used as scratch in INSN.
+   EXCLUDE is either NULL_RTX or some register. In the case where EXCLUDE
+   is a register, skip all possible return values that overlap EXCLUDE.
+   The policy for the returned register is similar to that of
+   `reg_unused_after', i.e. the returned register may overlap the SET_DEST
+   of INSN.
+
+   Return a QImode d-register or NULL_RTX if nothing found.  */
+
+static rtx
+avr_find_unused_d_reg (rtx insn, rtx exclude)
+{
+  int regno;
+  bool isr_p = (interrupt_function_p (current_function_decl)
+                || signal_function_p (current_function_decl));
+
+  for (regno = 16; regno < 32; regno++)
+    {
+      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.  */
@@ -2279,28 +2437,30 @@ avr_out_lpm_no_lpmx (rtx insn, rtx *xop, int *plen)
           gcc_unreachable();
 
         case 1:
-          return avr_asm_len ("lpm" CR_TAB
-                              "mov %0,%3", xop, plen, 2);
+          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 ("lpm"        CR_TAB
+            return avr_asm_len ("%4lpm"      CR_TAB
                                 "push %3"    CR_TAB
                                 "adiw %2,1"  CR_TAB
-                                "lpm"        CR_TAB
+                                "%4lpm"      CR_TAB
                                 "mov %B0,%3" CR_TAB
                                 "pop %A0", xop, plen, 6);
-          else
-            {
-              avr_asm_len ("lpm"        CR_TAB
-                           "mov %A0,%3" CR_TAB
-                           "adiw %2,1"  CR_TAB
-                           "lpm"        CR_TAB
-                           "mov %B0,%3", xop, plen, 5);
+          
+          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);
-            }
+          if (!reg_unused_after (insn, addr))
+            avr_asm_len ("sbiw %2,1", xop, plen, 1);
           
           break; /* 2 */
         }
@@ -2310,17 +2470,31 @@ avr_out_lpm_no_lpmx (rtx insn, rtx *xop, int *plen)
     case POST_INC:
 
       gcc_assert (REG_Z == REGNO (XEXP (addr, 0))
-                  && n_bytes <= 2);
+                  && n_bytes <= 4);
 
-      avr_asm_len ("lpm"        CR_TAB
-                   "mov %A0,%3" CR_TAB
-                   "adiw %2,1", xop, plen, 3);
+      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 ("lpm"        CR_TAB
+        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) */
@@ -2337,12 +2511,13 @@ 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[5];
+  rtx xop[6];
   rtx dest = op[0];
   rtx src = SET_SRC (single_set (insn));
   rtx addr;
   int n_bytes = GET_MODE_SIZE (GET_MODE (dest));
   int regno_dest;
+  int segment;
 
   if (plen)
     *plen = 0;
@@ -2357,17 +2532,66 @@ avr_out_lpm (rtx insn, rtx *op, int *plen)
 
   addr = XEXP (src, 0);
 
-  gcc_assert (!avr_load_libgcc_p (src)
-              && REG_P (dest)
-              && (REG_P (addr) || POST_INC == GET_CODE (addr)));
+  segment = avr_pgm_segment (MEM_ADDR_SPACE (src));
+
+  gcc_assert (REG_P (dest)
+              && ((segment >= 0
+                   && (REG_P (addr) || POST_INC == GET_CODE (addr)))
+                  || (GET_CODE (addr) == LO_SUM && segment == -1)));
+
+  if (segment == -1)
+    {
+      /* We are called from avr_out_xload because someone wrote
+         __pgmx on a device with just one flash segment.  */
+
+      addr = XEXP (addr, 1);
+    }
 
   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);
 
-  if (!AVR_HAVE_LPMX)
+  /* 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_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 ((segment == 0 && !AVR_HAVE_LPMX)
+      || (segment != 0 && !AVR_HAVE_ELPMX))
     {
       return avr_out_lpm_no_lpmx (insn, xop, plen);
     }
@@ -2387,17 +2611,17 @@ avr_out_lpm (rtx insn, rtx *op, int *plen)
           gcc_unreachable();
 
         case 1:
-          return avr_asm_len ("lpm %0,%a2", xop, plen, -1);
+          return avr_asm_len ("%4lpm %0,%a2", xop, plen, 1);
 
         case 2:
           if (REGNO (dest) == REG_Z)
-            return avr_asm_len ("lpm __tmp_reg__,%a2+" CR_TAB
-                                "lpm %B0,%a2"          CR_TAB
-                                "mov %A0,__tmp_reg__", xop, plen, -3);
+            return avr_asm_len ("%4lpm %5,%a2+" CR_TAB
+                                "%4lpm %B0,%a2" CR_TAB
+                                "mov %A0,%5", xop, plen, 3);
           else
             {
-              avr_asm_len ("lpm %A0,%a2+" CR_TAB
-                           "lpm %B0,%a2", xop, plen, -2);
+              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);
@@ -2407,9 +2631,9 @@ avr_out_lpm (rtx insn, rtx *op, int *plen)
 
         case 3:
 
-          avr_asm_len ("lpm %A0,%a2+" CR_TAB
-                       "lpm %B0,%a2+" CR_TAB
-                       "lpm %C0,%a2", xop, plen, -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);
@@ -2418,17 +2642,17 @@ avr_out_lpm (rtx insn, rtx *op, int *plen)
       
         case 4:
 
-          avr_asm_len ("lpm %A0,%a2+" CR_TAB
-                       "lpm %B0,%a2+", xop, plen, -2);
+          avr_asm_len ("%4lpm %A0,%a2+" CR_TAB
+                       "%4lpm %B0,%a2+", xop, plen, 2);
           
           if (REGNO (dest) == REG_Z - 2)
-            return avr_asm_len ("lpm __tmp_reg__,%a2+" CR_TAB
-                                "lpm %C0,%a2"          CR_TAB
-                                "mov %D0,__tmp_reg__", xop, plen, 3);
+            return avr_asm_len ("%4lpm %5,%a2+" CR_TAB
+                                "%4lpm %C0,%a2"          CR_TAB
+                                "mov %D0,%5", xop, plen, 3);
           else
             {
-              avr_asm_len ("lpm %C0,%a2+" CR_TAB
-                           "lpm %D0,%a2", xop, plen, 2);
+              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);
@@ -2444,10 +2668,10 @@ avr_out_lpm (rtx insn, rtx *op, int *plen)
       gcc_assert (REG_Z == REGNO (XEXP (addr, 0))
                   && n_bytes <= 4);
 
-      avr_asm_len                    ("lpm %A0,%a2+", xop, plen, -1);
-      if (n_bytes >= 2)  avr_asm_len ("lpm %B0,%a2+", xop, plen, 1);
-      if (n_bytes >= 3)  avr_asm_len ("lpm %C0,%a2+", xop, plen, 1);
-      if (n_bytes >= 4)  avr_asm_len ("lpm %D0,%a2+", xop, plen, 1);
+      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 */
 
@@ -2457,6 +2681,81 @@ avr_out_lpm (rtx insn, rtx *op, int *plen)
 }
 
 
+/* Worker function for xload_<mode> and xload_8 insns.  */
+
+const char*
+avr_out_xload (rtx insn, rtx *op, int *plen)
+{
+  rtx xop[5];
+  rtx reg = op[0];
+  int n_bytes = GET_MODE_SIZE (GET_MODE (reg));
+  unsigned int regno = REGNO (reg);
+
+  if (avr_current_arch->n_segments == 1)
+    return avr_out_lpm (insn, op, plen);
+
+  xop[0] = reg;
+  xop[1] = op[1];
+  xop[2] = lpm_addr_reg_rtx;
+  xop[3] = lpm_reg_rtx;
+  xop[4] = tmp_reg_rtx;
+  
+  avr_asm_len ("out __RAMPZ__,%1", xop, plen, -1);
+  
+  if (1 == n_bytes)
+    {
+      if (AVR_HAVE_ELPMX)
+        return avr_asm_len ("elpm %0,%a2", xop, plen, 1);
+      else
+        return avr_asm_len ("elpm" CR_TAB
+                            "mov %0,%3", xop, plen, 2);
+    }
+
+  gcc_assert (AVR_HAVE_ELPMX);
+  
+  if (!reg_overlap_mentioned_p (reg, lpm_addr_reg_rtx))
+    {
+      /* Insn clobbers the Z-register so we can use post-increment.  */
+      
+      avr_asm_len                    ("elpm %A0,%a2+", xop, plen, 1);
+      if (n_bytes >= 2)  avr_asm_len ("elpm %B0,%a2+", xop, plen, 1);
+      if (n_bytes >= 3)  avr_asm_len ("elpm %C0,%a2+", xop, plen, 1);
+      if (n_bytes >= 4)  avr_asm_len ("elpm %D0,%a2+", xop, plen, 1);
+
+      return "";
+    }
+
+  switch (n_bytes)
+    {
+    default:
+      gcc_unreachable();
+      
+    case 2:
+      gcc_assert (regno == REGNO (lpm_addr_reg_rtx));
+
+      return avr_asm_len ("elpm %4,%a2+" CR_TAB
+                          "elpm %B0,%a2" CR_TAB
+                          "mov %A0,%4", xop, plen, 3);
+
+    case 3:
+    case 4:
+      gcc_assert (regno + 2 == REGNO (lpm_addr_reg_rtx));
+      
+      avr_asm_len ("elpm %A0,%a2+" CR_TAB
+                   "elpm %B0,%a2+", xop, plen, 2);
+
+      if (n_bytes == 3)
+        return avr_asm_len ("elpm %C0,%a2", xop, plen, 1);
+      else
+        return avr_asm_len ("elpm %4,%a2+" CR_TAB
+                            "elpm %D0,%a2" CR_TAB
+                            "mov %C0,%4", xop, plen, 3);
+    }
+  
+  return "";
+}
+
+
 const char *
 output_movqi (rtx insn, rtx operands[], int *l)
 {
@@ -2596,71 +2895,73 @@ output_movhi (rtx insn, rtx operands[], int *l)
 }
 
 const char *
-out_movqi_r_mr (rtx insn, rtx op[], int *l)
+out_movqi_r_mr (rtx insn, rtx op[], int *plen)
 {
   rtx dest = op[0];
   rtx src = op[1];
   rtx x = XEXP (src, 0);
-  int dummy;
-  
-  if (!l)
-    l = &dummy;
   
   if (CONSTANT_ADDRESS_P (x))
     {
-      if (CONST_INT_P (x) && INTVAL (x) == SREG_ADDR)
-       {
-         *l = 1;
-         return AS2 (in,%0,__SREG__);
-       }
+      if (CONST_INT_P (x))
+        {
+          if (SREG_ADDR == INTVAL (x))
+            return avr_asm_len ("in %0,__SREG__", op, plen, -1);
+
+          if (RAMPZ_ADDR == INTVAL (x))
+            return avr_asm_len ("in %0,__RAMPZ__", op, plen, -1);
+        }
+      
       if (optimize > 0 && io_address_operand (x, QImode))
-       {
-         *l = 1;
-         return AS2 (in,%0,%m1-0x20);
-       }
-      *l = 2;
-      return AS2 (lds,%0,%m1);
+        return avr_asm_len ("in %0,%m1-0x20", op, plen, -1);
+
+      return avr_asm_len ("lds %0,%m1", op, plen, -2);
     }
-  /* memory access by reg+disp */
   else if (GET_CODE (x) == PLUS
-      && REG_P (XEXP (x,0))
-      && GET_CODE (XEXP (x,1)) == CONST_INT)
+           && REG_P (XEXP (x, 0))
+           && CONST_INT_P (XEXP (x, 1)))
     {
-      if ((INTVAL (XEXP (x,1)) - GET_MODE_SIZE (GET_MODE (src))) >= 63)
-       {
-         int disp = INTVAL (XEXP (x,1));
-         if (REGNO (XEXP (x,0)) != REG_Y)
-           fatal_insn ("incorrect insn:",insn);
+      /* memory access by reg+disp */
 
-         if (disp <= 63 + MAX_LD_OFFSET (GET_MODE (src)))
-           return *l = 3, (AS2 (adiw,r28,%o1-63) CR_TAB
-                           AS2 (ldd,%0,Y+63)     CR_TAB
-                           AS2 (sbiw,r28,%o1-63));
+      int disp = INTVAL (XEXP (x, 1));
+      
+      if (disp - GET_MODE_SIZE (GET_MODE (src)) >= 63)
+        {
+          if (REGNO (XEXP (x, 0)) != REG_Y)
+            fatal_insn ("incorrect insn:",insn);
 
-         return *l = 5, (AS2 (subi,r28,lo8(-%o1)) CR_TAB
-                         AS2 (sbci,r29,hi8(-%o1)) CR_TAB
-                         AS2 (ld,%0,Y)            CR_TAB
-                         AS2 (subi,r28,lo8(%o1))  CR_TAB
-                         AS2 (sbci,r29,hi8(%o1)));
-       }
-      else if (REGNO (XEXP (x,0)) == REG_X)
-       {
-         /* This is a paranoid case LEGITIMIZE_RELOAD_ADDRESS must exclude
-            it but I have this situation with extremal optimizing options.  */
-         if (reg_overlap_mentioned_p (dest, XEXP (x,0))
-             || reg_unused_after (insn, XEXP (x,0)))
-           return *l = 2, (AS2 (adiw,r26,%o1) CR_TAB
-                           AS2 (ld,%0,X));
-
-         return *l = 3, (AS2 (adiw,r26,%o1) CR_TAB
-                         AS2 (ld,%0,X)      CR_TAB
-                         AS2 (sbiw,r26,%o1));
-       }
-      *l = 1;
-      return AS2 (ldd,%0,%1);
+          if (disp <= 63 + MAX_LD_OFFSET (GET_MODE (src)))
+            return avr_asm_len ("adiw r28,%o1-63" CR_TAB
+                                "ldd %0,Y+63"     CR_TAB
+                                "sbiw r28,%o1-63", op, plen, -3);
+
+          return avr_asm_len ("subi r28,lo8(-%o1)" CR_TAB
+                              "sbci r29,hi8(-%o1)" CR_TAB
+                              "ld %0,Y"            CR_TAB
+                              "subi r28,lo8(%o1)"  CR_TAB
+                              "sbci r29,hi8(%o1)", op, plen, -5);
+        }
+      else if (REGNO (XEXP (x, 0)) == REG_X)
+        {
+          /* This is a paranoid case LEGITIMIZE_RELOAD_ADDRESS must exclude
+             it but I have this situation with extremal optimizing options.  */
+          
+          avr_asm_len ("adiw r26,%o1" CR_TAB
+                       "ld %0,X", op, plen, -2);
+          
+          if (!reg_overlap_mentioned_p (dest, XEXP (x,0))
+              && !reg_unused_after (insn, XEXP (x,0)))
+            {
+              avr_asm_len ("sbiw r26,%o1", op, plen, 1);
+            }
+
+          return "";
+        }
+
+      return avr_asm_len ("ldd %0,%1", op, plen, -1);
     }
-  *l = 1;
-  return AS2 (ld,%0,%1);
+  
+  return avr_asm_len ("ld %0,%1", op, plen, -1);
 }
 
 const char *
@@ -3539,83 +3840,76 @@ avr_out_movpsi (rtx insn, rtx *op, int *plen)
 
 
 const char *
-out_movqi_mr_r (rtx insn, rtx op[], int *l)
+out_movqi_mr_r (rtx insn, rtx op[], int *plen)
 {
   rtx dest = op[0];
   rtx src = op[1];
   rtx x = XEXP (dest, 0);
-  int dummy;
-
-  if (!l)
-    l = &dummy;
   
   if (CONSTANT_ADDRESS_P (x))
     {
-      if (CONST_INT_P (x) && INTVAL (x) == SREG_ADDR)
-       {
-         *l = 1;
-         return AS2 (out,__SREG__,%1);
-       }
+      if (CONST_INT_P (x))
+        {
+          if (SREG_ADDR == INTVAL (x))
+            return avr_asm_len ("out __SREG__,%1", op, plen, -1);
+
+          if (RAMPZ_ADDR == INTVAL (x))
+            return avr_asm_len ("out __RAMPZ__,%1", op, plen, -1);
+        }
+      
       if (optimize > 0 && io_address_operand (x, QImode))
-       {
-         *l = 1;
-         return AS2 (out,%m0-0x20,%1);
-       }
-      *l = 2;
-      return AS2 (sts,%m0,%1);
+        avr_asm_len ("out %m0-0x20,%1", op, plen, -1);
+
+      return avr_asm_len ("sts %m0,%1", op, plen, -2);
     }
-  /* memory access by reg+disp */
-  else if (GET_CODE (x) == PLUS        
-      && REG_P (XEXP (x,0))
-      && GET_CODE (XEXP (x,1)) == CONST_INT)
+  else if (GET_CODE (x) == PLUS
+           && REG_P (XEXP (x, 0))
+           && CONST_INT_P (XEXP (x, 1)))
     {
-      if ((INTVAL (XEXP (x,1)) - GET_MODE_SIZE (GET_MODE (dest))) >= 63)
-       {
-         int disp = INTVAL (XEXP (x,1));
-         if (REGNO (XEXP (x,0)) != REG_Y)
-           fatal_insn ("incorrect insn:",insn);
+      /* memory access by reg+disp */
 
-         if (disp <= 63 + MAX_LD_OFFSET (GET_MODE (dest)))
-           return *l = 3, (AS2 (adiw,r28,%o0-63) CR_TAB
-                           AS2 (std,Y+63,%1)     CR_TAB
-                           AS2 (sbiw,r28,%o0-63));
+      int disp = INTVAL (XEXP (x, 1));
 
-         return *l = 5, (AS2 (subi,r28,lo8(-%o0)) CR_TAB
-                         AS2 (sbci,r29,hi8(-%o0)) CR_TAB
-                         AS2 (st,Y,%1)            CR_TAB
-                         AS2 (subi,r28,lo8(%o0))  CR_TAB
-                         AS2 (sbci,r29,hi8(%o0)));
-       }
+      if (disp - GET_MODE_SIZE (GET_MODE (dest)) >= 63)
+        {
+          if (REGNO (XEXP (x, 0)) != REG_Y)
+            fatal_insn ("incorrect insn:",insn);
+
+          if (disp <= 63 + MAX_LD_OFFSET (GET_MODE (dest)))
+            return avr_asm_len ("adiw r28,%o0-63" CR_TAB
+                                "std Y+63,%1"     CR_TAB
+                                "sbiw r28,%o0-63", op, plen, -3);
+
+          return avr_asm_len ("subi r28,lo8(-%o0)" CR_TAB
+                              "sbci r29,hi8(-%o0)" CR_TAB
+                              "st Y,%1"            CR_TAB
+                              "subi r28,lo8(%o0)"  CR_TAB
+                              "sbci r29,hi8(%o0)", op, plen, -5);
+        }
       else if (REGNO (XEXP (x,0)) == REG_X)
-       {
-         if (reg_overlap_mentioned_p (src, XEXP (x, 0)))
-           {
-             if (reg_unused_after (insn, XEXP (x,0)))
-               return *l = 3, (AS2 (mov,__tmp_reg__,%1) CR_TAB
-                               AS2 (adiw,r26,%o0)       CR_TAB
-                               AS2 (st,X,__tmp_reg__));
-
-             return *l = 4, (AS2 (mov,__tmp_reg__,%1) CR_TAB
-                             AS2 (adiw,r26,%o0)       CR_TAB
-                             AS2 (st,X,__tmp_reg__)   CR_TAB
-                             AS2 (sbiw,r26,%o0));
-           }
-         else
-           {
-             if (reg_unused_after (insn, XEXP (x,0)))
-               return *l = 2, (AS2 (adiw,r26,%o0) CR_TAB
-                               AS2 (st,X,%1));
+        {
+          if (reg_overlap_mentioned_p (src, XEXP (x, 0)))
+            {
+              avr_asm_len ("mov __tmp_reg__,%1" CR_TAB
+                           "adiw r26,%o0"       CR_TAB
+                           "st X,__tmp_reg__", op, plen, -3);
+            }
+          else
+            {
+              avr_asm_len ("adiw r26,%o0" CR_TAB
+                           "st X,%1", op, plen, -2);
+            }
+          
+          if (!reg_unused_after (insn, XEXP (x,0)))
+            avr_asm_len ("sbiw r26,%o0", op, plen, 1);
 
-             return *l = 3, (AS2 (adiw,r26,%o0) CR_TAB
-                             AS2 (st,X,%1)      CR_TAB
-                             AS2 (sbiw,r26,%o0));
-           }
-       }
-      *l = 1;
-      return AS2 (std,%0,%1);
+          return "";
+        }
+      
+      return avr_asm_len ("std %0,%1", op, plen, 1);
     }
-  *l = 1;
-  return AS2 (st,%0,%1);
+  
+  return avr_asm_len ("st %0,%1", op, plen, 1);
 }
 
 const char *
@@ -6201,7 +6495,9 @@ adjust_insn_length (rtx insn, int len)
     case ADJUST_LEN_MOV16: output_movhi (insn, op, &len); break;
     case ADJUST_LEN_MOV24: avr_out_movpsi (insn, op, &len); break;
     case ADJUST_LEN_MOV32: output_movsisf (insn, op, &len); break;
-      
+    case ADJUST_LEN_MOVMEM: avr_out_movmem (insn, op, &len); break;
+    case ADJUST_LEN_XLOAD: avr_out_xload (insn, op, &len); break;
+
     case ADJUST_LEN_TSTHI: avr_out_tsthi (insn, op, &len); break;
     case ADJUST_LEN_TSTPSI: avr_out_tstpsi (insn, op, &len); break;
     case ADJUST_LEN_TSTSI: avr_out_tstsi (insn, op, &len); break;
@@ -6346,6 +6642,49 @@ _reg_unused_after (rtx insn, rtx reg)
   return 1;
 }
 
+
+/* Return RTX that represents the lower 16 bits of a constant address.
+   Unfortunately, simplify_gen_subreg does not handle this case.  */
+
+static rtx
+avr_const_address_lo16 (rtx x)
+{
+  rtx lo16;
+  
+  switch (GET_CODE (x))
+    {
+    default:
+      break;
+      
+    case CONST:
+      if (PLUS == GET_CODE (XEXP (x, 0))
+          && SYMBOL_REF == GET_CODE (XEXP (XEXP (x, 0), 0))
+          && CONST_INT_P (XEXP (XEXP (x, 0), 1)))
+        {
+          HOST_WIDE_INT offset = INTVAL (XEXP (XEXP (x, 0), 1));
+          const char *name = XSTR (XEXP (XEXP (x, 0), 0), 0);
+          
+          lo16 = gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (name));
+          lo16 = gen_rtx_CONST (Pmode, plus_constant (lo16, offset));
+          
+          return lo16;
+        }
+      
+      break;
+      
+    case SYMBOL_REF:
+      {
+        const char *name = XSTR (x, 0);
+        
+        return gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (name));
+      }
+    }
+  
+  avr_edump ("\n%?: %r\n", x);
+  gcc_unreachable();
+}
+
+
 /* Target hook for assembling integer objects.  The AVR version needs
    special handling for references to certain labels.  */
 
@@ -6358,11 +6697,30 @@ avr_assemble_integer (rtx x, unsigned int size, int aligned_p)
       fputs ("\t.word\tgs(", asm_out_file);
       output_addr_const (asm_out_file, x);
       fputs (")\n", asm_out_file);
+      
+      return true;
+    }
+  else if (GET_MODE (x) == PSImode)
+    {
+      default_assemble_integer (avr_const_address_lo16 (x),
+                                GET_MODE_SIZE (HImode), aligned_p);
+      
+      fputs ("\t.warning\t\"assembling 24-bit address needs binutils extension for hh8(",
+             asm_out_file);
+      output_addr_const (asm_out_file, x);
+      fputs (")\"\n", asm_out_file);
+      
+      fputs ("\t.byte\t0\t" ASM_COMMENT_START " hh8(", asm_out_file);
+      output_addr_const (asm_out_file, x);
+      fputs (")\n", asm_out_file);
+      
       return true;
     }
+  
   return default_assemble_integer (x, size, aligned_p);
 }
 
+
 /* Worker function for ASM_DECLARE_FUNCTION_NAME.  */
 
 void
@@ -6518,6 +6876,7 @@ avr_attribute_table[] =
    Return non-zero if DECL is data that must end up in Flash and
    zero if the data lives in RAM (.bss, .data, .rodata, ...).
    
+   Return 2   if DECL is located in 24-bit flash address-space
    Return 1   if DECL is located in 16-bit flash address-space
    Return -1  if attribute `progmem' occurs in DECL or ATTRIBUTES
    Return 0   otherwise  */
@@ -6530,6 +6889,9 @@ avr_progmem_p (tree decl, tree attributes)
   if (TREE_CODE (decl) != VAR_DECL)
     return 0;
 
+  if (avr_decl_pgmx_p (decl))
+    return 2;
+
   if (avr_decl_pgm_p (decl))
     return 1;
 
@@ -6745,11 +7107,23 @@ avr_output_bss_section_asm_op (const void *data)
 }
 
 
+/* Unnamed section callback for progmem*.data sections.  */
+
+static void
+avr_output_progmem_section_asm_op (const void *data)
+{
+  fprintf (asm_out_file, "\t.section\t%s,\"a\",@progbits\n",
+           (const char*) data);
+}
+
+
 /* Implement `TARGET_ASM_INIT_SECTIONS'.  */
 
 static void
 avr_asm_init_sections (void)
 {
+  unsigned int n;
+  
   /* Set up a section for jump tables.  Alignment is handled by
      ASM_OUTPUT_BEFORE_CASE_LABEL.  */
   
@@ -6768,9 +7142,12 @@ avr_asm_init_sections (void)
                                ",\"ax\",@progbits");
     }
 
-  progmem_section
-    = get_unnamed_section (0, output_section_asm_op,
-                           "\t.section\t.progmem.data,\"a\",@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'.  */
@@ -6849,8 +7226,9 @@ avr_asm_named_section (const char *name, unsigned int flags, tree decl)
 {
   if (flags & AVR_SECTION_PROGMEM)
     {
+      int segment = (flags & AVR_SECTION_PROGMEM) / SECTION_MACH_DEP - 1;
       const char *old_prefix = ".rodata";
-      const char *new_prefix = ".progmem.data";
+      const char *new_prefix = progmem_section_prefix[segment];
       const char *sname = new_prefix;
       
       if (STR_PREFIX_P (name, old_prefix))
@@ -6877,6 +7255,7 @@ avr_asm_named_section (const char *name, unsigned int flags, tree decl)
 static unsigned int
 avr_section_type_flags (tree decl, const char *name, int reloc)
 {
+  int prog;
   unsigned int flags = default_section_type_flags (decl, name, reloc);
 
   if (STR_PREFIX_P (name, ".noinit"))
@@ -6890,11 +7269,16 @@ avr_section_type_flags (tree decl, const char *name, int reloc)
     }
 
   if (decl && DECL_P (decl)
-      && avr_progmem_p (decl, DECL_ATTRIBUTES (decl)))
+      && (prog = avr_progmem_p (decl, DECL_ATTRIBUTES (decl)), prog))
     {
+      int segment = 0;
+
+      if (prog == 1)
+        segment = avr_pgm_segment (TYPE_ADDR_SPACE (TREE_TYPE (decl)));
+
       flags &= ~SECTION_WRITE;
       flags &= ~SECTION_BSS;
-      flags |= AVR_SECTION_PROGMEM;
+      flags |= (1 + segment % avr_current_arch->n_segments) * SECTION_MACH_DEP;
     }
   
   return flags;
@@ -6930,16 +7314,25 @@ avr_encode_section_info (tree decl, rtx rtl,
 static section *
 avr_asm_select_section (tree decl, int reloc, unsigned HOST_WIDE_INT align)
 {
+  int prog;
+  
   section * sect = default_elf_select_section (decl, reloc, align);
   
   if (decl && DECL_P (decl)
-      && avr_progmem_p (decl, DECL_ATTRIBUTES (decl)))
+      && (prog = avr_progmem_p (decl, DECL_ATTRIBUTES (decl)), prog))
     {
+      int segment = 0;
+      
+      if (prog == 1)
+        segment = avr_pgm_segment (TYPE_ADDR_SPACE (TREE_TYPE (decl)));
+
+      segment %= avr_current_arch->n_segments;
+      
       if (sect->common.flags & SECTION_NAMED)
         {
           const char * name = sect->named.name;
           const char * old_prefix = ".rodata";
-          const char * new_prefix = ".progmem.data";
+          const char * new_prefix = progmem_section_prefix[segment];
 
           if (STR_PREFIX_P (name, old_prefix))
             {
@@ -6950,31 +7343,36 @@ avr_asm_select_section (tree decl, int reloc, unsigned HOST_WIDE_INT align)
             }
         }
           
-      return progmem_section;
+      return progmem_section[segment];
     }
 
   return sect;
 }
 
 /* Implement `TARGET_ASM_FILE_START'.  */
-/* Outputs some appropriate text to go at the start of an assembler
-   file.  */
+/* Outputs some text at the start of each assembler file.  */
 
 static void
 avr_file_start (void)
 {
+  int sfr_offset = 0x20;
+
   if (avr_current_arch->asm_only)
     error ("MCU %qs supported for assembler only", avr_current_device->name);
 
   default_file_start ();
 
-/*  fprintf (asm_out_file, "\t.arch %s\n", avr_current_device->name);*/
-  fputs ("__SREG__ = 0x3f\n"
-        "__SP_H__ = 0x3e\n"
-        "__SP_L__ = 0x3d\n", asm_out_file);
-  
-  fputs ("__tmp_reg__ = 0\n" 
-         "__zero_reg__ = 1\n", asm_out_file);
+  fprintf (asm_out_file,
+           "__SREG__ = 0x%02x\n"
+           "__SP_H__ = 0x%02x\n"
+           "__SP_L__ = 0x%02x\n"
+           "__RAMPZ__ = 0x%02x\n"
+           "__tmp_reg__ = 0\n" 
+           "__zero_reg__ = 1\n",
+           -sfr_offset + SREG_ADDR,
+           -sfr_offset + SP_ADDR + 1,
+           -sfr_offset + SP_ADDR,
+           -sfr_offset + RAMPZ_ADDR);
 }
 
 
@@ -8954,8 +9352,8 @@ const char *
 avr_out_sbxx_branch (rtx insn, rtx operands[])
 {
   enum rtx_code comp = GET_CODE (operands[0]);
-  int long_jump = (get_attr_length (insn) >= 4);
-  int reverse = long_jump || jump_over_one_insn_p (insn, operands[3]);
+  bool long_jump = get_attr_length (insn) >= 4;
+  bool reverse = long_jump || jump_over_one_insn_p (insn, operands[3]);
 
   if (comp == GE)
     comp = EQ;
@@ -8965,49 +9363,61 @@ avr_out_sbxx_branch (rtx insn, rtx operands[])
   if (reverse)
     comp = reverse_condition (comp);
 
-  if (GET_CODE (operands[1]) == CONST_INT)
+  switch (GET_CODE (operands[1]))
     {
-      if (INTVAL (operands[1]) < 0x40)
-       {
-         if (comp == EQ)
-           output_asm_insn (AS2 (sbis,%m1-0x20,%2), operands);
-         else
-           output_asm_insn (AS2 (sbic,%m1-0x20,%2), operands);
-       }
+    default:
+      gcc_unreachable();
+      
+    case CONST_INT:
+
+      if (low_io_address_operand (operands[1], QImode))
+        {
+          if (comp == EQ)
+            output_asm_insn ("sbis %m1-0x20,%2", operands);
+          else
+            output_asm_insn ("sbic %m1-0x20,%2", operands);
+        }
       else
-       {
-         output_asm_insn (AS2 (in,__tmp_reg__,%m1-0x20), operands);
-         if (comp == EQ)
-           output_asm_insn (AS2 (sbrs,__tmp_reg__,%2), operands);
-         else
-           output_asm_insn (AS2 (sbrc,__tmp_reg__,%2), operands);
-       }
-    }
-  else  /* GET_CODE (operands[1]) == REG */
-    {
+        {
+          output_asm_insn ("in __tmp_reg__,%m1-0x20", operands);
+          if (comp == EQ)
+            output_asm_insn ("sbrs __tmp_reg__,%2", operands);
+          else
+            output_asm_insn ("sbrc __tmp_reg__,%2", operands);
+        }
+
+      break; /* CONST_INT */
+
+    case REG:
+
       if (GET_MODE (operands[1]) == QImode)
-       {
-         if (comp == EQ)
-           output_asm_insn (AS2 (sbrs,%1,%2), operands);
-         else
-           output_asm_insn (AS2 (sbrc,%1,%2), operands);
-       }
-      else  /* HImode or SImode */
-       {
-         static char buf[] = "sbrc %A1,0";
-         int bit_nr = INTVAL (operands[2]);
-         buf[3] = (comp == EQ) ? 's' : 'c';
-         buf[6] = 'A' + (bit_nr >> 3);
-         buf[9] = '0' + (bit_nr & 7);
-         output_asm_insn (buf, operands);
-       }
-    }
+        {
+          if (comp == EQ)
+            output_asm_insn ("sbrs %1,%2", operands);
+          else
+            output_asm_insn ("sbrc %1,%2", operands);
+        }
+      else  /* HImode, PSImode or SImode */
+        {
+          static char buf[] = "sbrc %A1,0";
+          unsigned int bit_nr = UINTVAL (operands[2]);
+
+          buf[3] = (comp == EQ) ? 's' : 'c';
+          buf[6] = 'A' + (bit_nr / 8);
+          buf[9] = '0' + (bit_nr % 8);
+          output_asm_insn (buf, operands);
+        }
+
+      break; /* REG */
+    }        /* switch */
 
   if (long_jump)
-    return (AS1 (rjmp,.+4) CR_TAB
-           AS1 (jmp,%x3));
+    return ("rjmp .+4" CR_TAB
+            "jmp %x3");
+
   if (!reverse)
-    return AS1 (rjmp,%x3);
+    return "rjmp %x3";
+
   return "";
 }
 
@@ -9055,18 +9465,18 @@ avr_case_values_threshold (void)
 /* Implement `TARGET_ADDR_SPACE_ADDRESS_MODE'.  */
 
 static enum machine_mode
-avr_addr_space_address_mode (addr_space_t as ATTRIBUTE_UNUSED)
+avr_addr_space_address_mode (addr_space_t as)
 {
-  return HImode;
+  return as == ADDR_SPACE_PGMX ? PSImode : HImode;
 }
 
 
 /* Implement `TARGET_ADDR_SPACE_POINTER_MODE'.  */
 
 static enum machine_mode
-avr_addr_space_pointer_mode (addr_space_t as ATTRIBUTE_UNUSED)
+avr_addr_space_pointer_mode (addr_space_t as)
 {
-  return HImode;
+  return as == ADDR_SPACE_PGMX ? PSImode : HImode;
 }
 
 
@@ -9111,6 +9521,11 @@ avr_addr_space_legitimate_address_p (enum machine_mode mode, rtx x,
       return avr_legitimate_address_p (mode, x, strict);
 
     case ADDR_SPACE_PGM:
+    case ADDR_SPACE_PGM1:
+    case ADDR_SPACE_PGM2:
+    case ADDR_SPACE_PGM3:
+    case ADDR_SPACE_PGM4:
+    case ADDR_SPACE_PGM5:
 
       switch (GET_CODE (x))
         {
@@ -9119,8 +9534,7 @@ avr_addr_space_legitimate_address_p (enum machine_mode mode, rtx x,
           break;
           
         case POST_INC:
-          ok = (!avr_load_libgcc_p (x)
-                && avr_reg_ok_for_pgm_addr (XEXP (x, 0), strict));
+          ok = avr_reg_ok_for_pgm_addr (XEXP (x, 0), strict);
           break;
           
         default:
@@ -9128,6 +9542,24 @@ avr_addr_space_legitimate_address_p (enum machine_mode mode, rtx x,
         }
 
       break; /* PGM */
+      
+    case ADDR_SPACE_PGMX:
+      if (REG_P (x))
+        ok = (!strict
+              && can_create_pseudo_p());
+
+      if (LO_SUM == GET_CODE (x))
+        {
+          rtx hi = XEXP (x, 0);
+          rtx lo = XEXP (x, 1);
+
+          ok = (REG_P (hi)
+                && (!strict || REGNO (hi) < FIRST_PSEUDO_REGISTER)
+                && REG_P (lo)
+                && REGNO (lo) == REG_Z);
+        }
+      
+      break; /* PGMX */
     }
 
   if (avr_log.legitimate_address_p)
@@ -9177,10 +9609,60 @@ avr_addr_space_legitimize_address (rtx x, rtx old_x,
 static rtx
 avr_addr_space_convert (rtx src, tree type_from, tree type_to)
 {
+  addr_space_t as_from = TYPE_ADDR_SPACE (TREE_TYPE (type_from));
+  addr_space_t as_to = TYPE_ADDR_SPACE (TREE_TYPE (type_to));
+
   if (avr_log.progmem)
     avr_edump ("\n%!: op = %r\nfrom = %t\nto = %t\n",
                src, type_from, type_to);
 
+  if (as_from != ADDR_SPACE_PGMX
+      && as_to == ADDR_SPACE_PGMX)
+    {
+      rtx new_src;
+      int n_segments = avr_current_arch->n_segments;
+      RTX_CODE code = GET_CODE (src);
+
+      if (CONST == code
+          && PLUS == GET_CODE (XEXP (src, 0))
+          && SYMBOL_REF == GET_CODE (XEXP (XEXP (src, 0), 0))
+          && CONST_INT_P (XEXP (XEXP (src, 0), 1)))
+        {
+          HOST_WIDE_INT offset = INTVAL (XEXP (XEXP (src, 0), 1));
+          const char *name = XSTR (XEXP (XEXP (src, 0), 0), 0);
+          
+          new_src = gen_rtx_SYMBOL_REF (PSImode, ggc_strdup (name));
+          new_src = gen_rtx_CONST (PSImode,
+                                   plus_constant (new_src, offset));
+          return new_src;
+        }
+
+      if (SYMBOL_REF == code)
+          {
+            const char *name = XSTR (src, 0);
+            
+            return gen_rtx_SYMBOL_REF (PSImode, ggc_strdup (name));
+          }
+      
+      src = force_reg (Pmode, src);
+      
+      if (ADDR_SPACE_GENERIC_P (as_from)
+          || as_from == ADDR_SPACE_PGM
+          || n_segments == 1)
+        {
+          return gen_rtx_ZERO_EXTEND (PSImode, src);
+        }
+      else
+        {
+          int segment = avr_pgm_segment (as_from) % n_segments;
+
+          new_src = gen_reg_rtx (PSImode);
+          emit_insn (gen_n_extendhipsi2 (new_src, GEN_INT (segment), src));
+
+          return new_src;
+        }
+    }
+  
   return src;
 }
 
@@ -9188,13 +9670,242 @@ avr_addr_space_convert (rtx src, tree type_from, tree type_to)
 /* Implement `TARGET_ADDR_SPACE_SUBSET_P'.  */
 
 static bool
-avr_addr_space_subset_p (addr_space_t subset ATTRIBUTE_UNUSED,
-                         addr_space_t superset ATTRIBUTE_UNUSED)
+avr_addr_space_subset_p (addr_space_t subset, addr_space_t superset)
 {
+  if (subset == ADDR_SPACE_PGMX
+      && superset != ADDR_SPACE_PGMX)
+    {
+      return false;
+    }
+  
+  return true;
+}
+
+
+/* Worker function for movmemhi insn.
+   XOP[0]  Destination as MEM:BLK
+   XOP[1]  Source      "     "
+   XOP[2]  # Bytes to copy
+
+   Return TRUE  if the expansion is accomplished.
+   Return FALSE if the operand compination is not supported.  */
+
+bool
+avr_emit_movmemhi (rtx *xop)
+{
+  HOST_WIDE_INT count;
+  enum machine_mode loop_mode;
+  addr_space_t as = MEM_ADDR_SPACE (xop[1]);
+  rtx loop_reg, addr0, addr1, a_src, a_dest, insn, xas, reg_x;
+  rtx a_hi8 = NULL_RTX;
+
+  if (avr_mem_pgm_p (xop[0]))
+    return false;
+
+  if (!CONST_INT_P (xop[2]))
+    return false;
+
+  count = INTVAL (xop[2]);
+  if (count <= 0)
+    return false;
+
+  a_src  = XEXP (xop[1], 0);
+  a_dest = XEXP (xop[0], 0);
+
+  /* See if constant fits in 8 bits.  */
+
+  loop_mode = (count <= 0x100) ? QImode : HImode;
+
+  if (PSImode == GET_MODE (a_src))
+    {
+      addr1 = simplify_gen_subreg (HImode, a_src, PSImode, 0);
+      a_hi8 = simplify_gen_subreg (QImode, a_src, PSImode, 2);
+    }
+  else
+    {
+      int seg = avr_pgm_segment (as);
+      
+      addr1 = a_src;
+
+      if (seg > 0
+          && seg % avr_current_arch->n_segments > 0)
+        {
+          a_hi8 = GEN_INT (seg % avr_current_arch->n_segments);
+        }
+    }
+
+  if (a_hi8
+      && avr_current_arch->n_segments > 1)
+    {
+      emit_move_insn (rampz_rtx, a_hi8 = copy_to_mode_reg (QImode, a_hi8));
+    }
+  else if (!ADDR_SPACE_GENERIC_P (as))
+    {
+      as = ADDR_SPACE_PGM;
+    }
+
+  xas = GEN_INT (as);
+
+  /* Create loop counter register */
+
+  loop_reg = copy_to_mode_reg (loop_mode, gen_int_mode (count, loop_mode));
+
+  /* Copy pointers into new pseudos - they will be changed */
+
+  addr0 = copy_to_mode_reg (HImode, a_dest);
+  addr1 = copy_to_mode_reg (HImode, addr1);
+
+  /* FIXME: Register allocator might come up with spill fails if it is left
+        on its own.  Thus, we allocate the pointer registers by hand.  */
+
+  emit_move_insn (lpm_addr_reg_rtx, addr1);
+  addr1 = lpm_addr_reg_rtx;
+
+  reg_x = gen_rtx_REG (HImode, REG_X);
+  emit_move_insn (reg_x, addr0);
+  addr0 = reg_x;
+
+  /* FIXME: Register allocator does a bad job and might spill address
+        register(s) inside the loop leading to additional move instruction
+        to/from stack which could clobber tmp_reg.  Thus, do *not* emit
+        load and store as seperate insns.  Instead, we perform the copy
+        by means of one monolithic insn.  */
+
+  if (ADDR_SPACE_GENERIC_P (as))
+    {
+      rtx (*fun) (rtx, rtx, rtx, rtx, rtx, rtx, rtx, rtx)
+        = QImode == loop_mode ? gen_movmem_qi : gen_movmem_hi;
+
+      insn = fun (addr0, addr1, xas, loop_reg,
+                  addr0, addr1, tmp_reg_rtx, loop_reg);
+    }
+  else if (as == ADDR_SPACE_PGM)
+    {
+      rtx (*fun) (rtx, rtx, rtx, rtx, rtx, rtx, rtx, rtx)
+        = QImode == loop_mode ? gen_movmem_qi : gen_movmem_hi;
+
+      insn = fun (addr0, addr1, xas, loop_reg, addr0, addr1,
+                  AVR_HAVE_LPMX ? tmp_reg_rtx : lpm_reg_rtx, loop_reg);
+    }
+  else
+    {
+      rtx (*fun) (rtx, rtx, rtx, rtx, rtx, rtx, rtx, rtx, rtx, rtx, rtx)
+        = QImode == loop_mode ? gen_movmem_qi_elpm : gen_movmem_hi_elpm;
+      
+      insn = fun (addr0, addr1, xas, loop_reg, addr0, addr1,
+                  AVR_HAVE_ELPMX ? tmp_reg_rtx : lpm_reg_rtx, loop_reg,
+                  a_hi8, a_hi8, GEN_INT (RAMPZ_ADDR));
+    }
+
+  set_mem_addr_space (SET_SRC (XVECEXP (insn, 0, 0)), as);
+  emit_insn (insn);
+
   return true;
 }
 
 
+/* 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
+*/
+
+const char*
+avr_out_movmem (rtx insn ATTRIBUTE_UNUSED, rtx *xop, 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]));
+
+  if (plen)
+    *plen = 0;
+
+  /* Loop label */
+
+  avr_asm_len ("0:", xop, plen, 0);
+
+  /* Load with post-increment */
+
+  switch (as)
+    {
+    default:
+      gcc_unreachable();
+      
+    case ADDR_SPACE_GENERIC:
+
+      avr_asm_len ("ld %6,%a1+", xop, plen, 1);
+      break;
+      
+    case ADDR_SPACE_PGM:
+
+      if (AVR_HAVE_LPMX)
+        avr_asm_len ("lpm %6,%a1+", xop, plen, 1);
+      else
+        avr_asm_len ("lpm" CR_TAB
+                     "adiw %1,1", xop, plen, 2);
+      break;
+      
+    case ADDR_SPACE_PGM1:
+    case ADDR_SPACE_PGM2:
+    case ADDR_SPACE_PGM3:
+    case ADDR_SPACE_PGM4:
+    case ADDR_SPACE_PGM5:
+    case ADDR_SPACE_PGMX:
+
+      if (AVR_HAVE_ELPMX)
+        avr_asm_len ("elpm %6,%a1+", xop, plen, 1);
+      else
+        avr_asm_len ("elpm" CR_TAB
+                     "adiw %1,1", xop, plen, 2);
+      
+      if (as == ADDR_SPACE_PGMX
+          && !AVR_HAVE_ELPMX)
+        {
+          avr_asm_len ("adc %8,__zero_reg__" CR_TAB
+                       "out __RAMPZ__,%8", xop, plen, 2);
+        }
+      
+      break;
+    }
+
+  /* Store with post-increment */
+
+  avr_asm_len ("st %a0+,%6", xop, plen, 1);
+
+  /* Decrement loop-counter and set Z-flag */
+
+  if (QImode == loop_mode)
+    {
+      avr_asm_len ("dec %3", xop, plen, 1);
+    }
+  else if (sbiw_p)
+    {
+      avr_asm_len ("sbiw %3,1", xop, plen, 1);
+    }
+  else
+    {
+      avr_asm_len ("subi %A3,1" CR_TAB
+                   "sbci %B3,0", xop, plen, 2);
+    }
+
+  /* Loop until zero */
+  
+  return avr_asm_len ("brne 0b", xop, plen, 1);
+}
+
+
+\f
 /* Helper for __builtin_avr_delay_cycles */
 
 static void
index 24a687b309b60630df66a0b88a4ffd34f4bad706..30bca35349a38a77025a8c70f9a66fb26c77778d 100644 (file)
@@ -54,6 +54,9 @@ struct base_arch_s {
   /* Default start of data section address for architecture.  */
   int default_data_section_start;
 
+  /* Number of 64k segments in the flash.  */
+  int n_segments;
+
   const char *const macro;
   
   /* Architecture name.  */
@@ -131,6 +134,8 @@ extern const struct base_arch_s avr_arch_types[];
 #define AVR_HAVE_MUL (avr_current_arch->have_mul)
 #define AVR_HAVE_MOVW (avr_current_arch->have_movw_lpmx)
 #define AVR_HAVE_LPMX (avr_current_arch->have_movw_lpmx)
+#define AVR_HAVE_ELPM (avr_current_arch->have_elpm)
+#define AVR_HAVE_ELPMX (avr_current_arch->have_elpmx)
 #define AVR_HAVE_RAMPZ (avr_current_arch->have_elpm)
 #define AVR_HAVE_EIJMP_EICALL (avr_current_arch->have_eijmp_eicall)
 #define AVR_HAVE_8BIT_SP (avr_current_device->short_sp || TARGET_TINY_STACK)
@@ -393,6 +398,12 @@ typedef struct avr_args {
 
 
 #define ADDR_SPACE_PGM  1
+#define ADDR_SPACE_PGM1 2
+#define ADDR_SPACE_PGM2 3
+#define ADDR_SPACE_PGM3 4
+#define ADDR_SPACE_PGM4 5
+#define ADDR_SPACE_PGM5 6
+#define ADDR_SPACE_PGMX 7
 
 #define REGISTER_TARGET_PRAGMAS()                                       \
   do {                                                                  \
@@ -645,3 +656,5 @@ struct GTY(()) machine_function
 #define PUSH_ROUNDING(X)       (X)
 
 #define ACCUMULATE_OUTGOING_ARGS avr_accumulate_outgoing_args()
+
+#define INIT_EXPANDERS avr_init_expanders()
index 78ffe866bbcc02fc671a26f56166ee8c60ca6cdc..9e7fc9a920483f2a4ea17856396faefbb09877d6 100644 (file)
    (ZERO_REGNO 1)      ; zero register r1
    
    (SREG_ADDR   0x5F)
+   (SP_ADDR     0x5D)
+
+   ;; Register holding the address' high part when loading via ELPM
    (RAMPZ_ADDR  0x5B)
    ])
 
 (define_c_enum "unspec"
   [UNSPEC_STRLEN
+   UNSPEC_MOVMEM
    UNSPEC_INDEX_JMP
    UNSPEC_FMUL
    UNSPEC_FMULS
   "out_bitop, out_plus, out_plus_noclobber, addto_sp,
    tsthi, tstpsi, tstsi, compare, call,
    mov8, mov16, mov24, mov32, reload_in16, reload_in24, reload_in32,
+   xload, movmem,
    ashlqi, ashrqi, lshrqi,
    ashlhi, ashrhi, lshrhi,
    ashlsi, ashrsi, lshrsi,
 
 ;; Flavours of instruction set architecture (ISA), used in enabled attribute
 
-;; mov:   ISA has no MOVW
-;; movw:  ISA has MOVW
-;; rjmp:  ISA has no CALL/JMP
-;; jmp:   ISA has CALL/JMP
-;; ijmp:  ISA has no EICALL/EIJMP
-;; eijmp: ISA has EICALL/EIJMP
+;; mov  : ISA has no MOVW                movw  : ISA has MOVW
+;; rjmp : ISA has no CALL/JMP            jmp   : ISA has CALL/JMP
+;; ijmp : ISA has no EICALL/EIJMP        eijmp : ISA has EICALL/EIJMP
+;; lpm  : ISA has no LPMX                lpmx  : ISA has LPMX
+;; elpm : ISA has ELPM but no ELPMX      elpmx : ISA has ELPMX
 
 (define_attr "isa"
-  "mov,movw, rjmp,jmp, ijmp,eijmp,
+  "mov,movw, rjmp,jmp, ijmp,eijmp, lpm,lpmx, elpm,elpmx,
    standard"
   (const_string "standard"))
 
          (and (eq_attr "isa" "eijmp")
               (match_test "AVR_HAVE_EIJMP_EICALL"))
          (const_int 1)
+
+         (and (eq_attr "isa" "lpm")
+              (match_test "!AVR_HAVE_LPMX"))
+         (const_int 1)
+
+         (and (eq_attr "isa" "lpmx")
+              (match_test "AVR_HAVE_LPMX"))
+         (const_int 1)
+
+         (and (eq_attr "isa" "elpm")
+              (match_test "AVR_HAVE_ELPM && !AVR_HAVE_ELPMX"))
+         (const_int 1)
+
+         (and (eq_attr "isa" "elpmx")
+              (match_test "AVR_HAVE_ELPMX"))
+         (const_int 1)
          ] (const_int 0)))
 
 
 ;; even though its function is identical to that in builtins.c
 
 (define_expand "nonlocal_goto"
-  [
-  (use (match_operand 0 "general_operand"))
-  (use (match_operand 1 "general_operand"))
-  (use (match_operand 2 "general_operand"))
-  (use (match_operand 3 "general_operand"))
-  ]
+  [(use (match_operand 0 "general_operand"))
+   (use (match_operand 1 "general_operand"))
+   (use (match_operand 2 "general_operand"))
+   (use (match_operand 3 "general_operand"))]
   ""
 {
   rtx r_label = copy_to_reg (operands[1]);
     set_mem_addr_space (operands[1], ADDR_SPACE_PGM);
   })
     
-(define_insn "*load.<mode>.libgcc"
+(define_insn "load_<mode>_libgcc"
   [(set (reg:MOVMODE 22)
         (match_operand:MOVMODE 0 "memory_operand" "m,m"))]
   "avr_load_libgcc_p (operands[0])
    (set_attr "cc" "clobber")])
 
 
+(define_insn_and_split "xload8_A"
+  [(set (match_operand:QI 0 "register_operand" "=r")
+        (match_operand:QI 1 "memory_operand"    "m"))
+   (clobber (reg:HI REG_Z))]
+  "can_create_pseudo_p()
+   && avr_mem_pgmx_p (operands[1])
+   && REG_P (XEXP (operands[1], 0))"
+  { gcc_unreachable(); }
+  "&& 1"
+  [(clobber (const_int 0))]
+  {
+    rtx insn, addr = XEXP (operands[1], 0);
+    rtx hi8 = gen_reg_rtx (QImode);
+    rtx reg_z = gen_rtx_REG (HImode, REG_Z);
+
+    emit_move_insn (reg_z, simplify_gen_subreg (HImode, addr, PSImode, 0));
+    emit_move_insn (hi8, simplify_gen_subreg (QImode, addr, PSImode, 2));
+
+    insn = emit_insn (gen_xload_8 (operands[0], hi8));
+    set_mem_addr_space (SET_SRC (single_set (insn)),
+                                 MEM_ADDR_SPACE (operands[1]));
+    DONE;
+  })
+
+(define_insn_and_split "xload<mode>_A"
+  [(set (match_operand:MOVMODE 0 "register_operand" "=r")
+        (match_operand:MOVMODE 1 "memory_operand"    "m"))
+   (clobber (reg:QI 21))
+   (clobber (reg:HI REG_Z))]
+  "QImode != <MODE>mode
+   && can_create_pseudo_p()
+   && avr_mem_pgmx_p (operands[1])
+   && REG_P (XEXP (operands[1], 0))"
+  { gcc_unreachable(); }
+  "&& 1"
+  [(clobber (const_int 0))]
+  {
+    rtx addr = XEXP (operands[1], 0);
+    rtx reg_z = gen_rtx_REG (HImode, REG_Z);
+    rtx addr_hi8 = simplify_gen_subreg (QImode, addr, PSImode, 2);
+    addr_space_t as = MEM_ADDR_SPACE (operands[1]);
+    rtx hi8, insn;
+
+    emit_move_insn (reg_z, simplify_gen_subreg (HImode, addr, PSImode, 0));
+
+    if (avr_xload_libgcc_p (<MODE>mode))
+      {
+        emit_move_insn (gen_rtx_REG (QImode, 21), addr_hi8);
+        insn = emit_insn (gen_xload_<mode>_libgcc ());
+        emit_move_insn (operands[0], gen_rtx_REG (<MODE>mode, 22));
+      }
+    else if (avr_current_arch->n_segments == 1
+             && GET_MODE_SIZE (<MODE>mode) > 2
+             && !AVR_HAVE_LPMX)
+      {
+        rtx src = gen_rtx_MEM (<MODE>mode, reg_z);
+
+        as = ADDR_SPACE_PGM;
+        insn = emit_insn (gen_load_<mode>_libgcc (src));
+        emit_move_insn (operands[0], gen_rtx_REG (<MODE>mode, 22));
+      }
+    else
+      {
+        hi8 = gen_reg_rtx (QImode);
+        emit_move_insn (hi8, addr_hi8);
+        insn = emit_insn (gen_xload_<mode> (operands[0], hi8));
+      }
+
+    set_mem_addr_space (SET_SRC (single_set (insn)), as);
+
+    DONE;
+  })
+
+;; Move value from address space pgmx to a register
+;; These insns must be prior to respective generic move insn.
+
+(define_insn "xload_8"
+  [(set (match_operand:QI 0 "register_operand"                    "=r")
+        (mem:QI (lo_sum:PSI (match_operand:QI 1 "register_operand" "r")
+                            (reg:HI REG_Z))))]
+  ""
+  {
+    return avr_out_xload (insn, operands, NULL);
+  }
+  [(set_attr "adjust_len" "xload")
+   (set_attr "cc" "clobber")])
+
+;; "xload_hi_libgcc"
+;; "xload_psi_libgcc"
+;; "xload_si_libgcc"
+;; "xload_sf_libgcc"
+(define_insn "xload_<mode>_libgcc"
+  [(set (reg:MOVMODE 22)
+        (mem:MOVMODE (lo_sum:PSI (reg:QI 21)
+                                 (reg:HI REG_Z))))
+   (clobber (reg:QI 21))
+   (clobber (reg:HI REG_Z))]
+  "<MODE>mode != QImode
+   && avr_xload_libgcc_p (<MODE>mode)"
+  {
+    rtx x_bytes = GEN_INT (GET_MODE_SIZE (<MODE>mode));
+
+    /* Devices with ELPM* also have CALL.  */
+
+    output_asm_insn ("call __xload_%0", &x_bytes);
+    return "";
+  }
+  [(set_attr "length" "2")
+   (set_attr "cc" "clobber")])
+
+;; "xload_hi"
+;; "xload_psi"
+;; "xload_si"
+;; "xload_sf"
+(define_insn "xload_<mode>"
+  [(set (match_operand:MOVMODE 0 "register_operand"                    "=r")
+        (mem:MOVMODE (lo_sum:PSI (match_operand:QI 1 "register_operand" "r")
+                                 (reg:HI REG_Z))))
+   (clobber (scratch:HI))
+   (clobber (reg:HI REG_Z))]
+  "<MODE>mode != QImode
+   && !avr_xload_libgcc_p (<MODE>mode)"
+  {
+    return avr_out_xload (insn, operands, NULL);
+  }
+  [(set_attr "adjust_len" "xload")
+   (set_attr "cc" "clobber")])
+
+
 ;; General move expanders
 
 ;; "movqi"
         operands[1] = src = copy_to_mode_reg (<MODE>mode, src);
       }
 
+  if (avr_mem_pgmx_p (src))
+    {
+      rtx addr = XEXP (src, 0);
+
+      if (!REG_P (addr))
+        src = replace_equiv_address (src, copy_to_mode_reg (PSImode, addr));
+
+      if (QImode == <MODE>mode)
+        emit_insn (gen_xload8_A (dest, src));
+      else
+        emit_insn (gen_xload<mode>_A (dest, src));
+
+      DONE;
+    }
+
     if (avr_load_libgcc_p (src))
       {
         /* For the small devices, do loads per libgcc call.  */
 
 ;;=========================================================================
 ;; move string (like memcpy)
-;; implement as RTL loop
 
 (define_expand "movmemhi"
   [(parallel [(set (match_operand:BLK 0 "memory_operand" "")
-          (match_operand:BLK 1 "memory_operand" ""))
-          (use (match_operand:HI 2 "const_int_operand" ""))
-          (use (match_operand:HI 3 "const_int_operand" ""))])]
+                   (match_operand:BLK 1 "memory_operand" ""))
+              (use (match_operand:HI 2 "const_int_operand" ""))
+              (use (match_operand:HI 3 "const_int_operand" ""))])]
   ""
-  "{
-  int prob;
-  HOST_WIDE_INT count;
-  enum machine_mode mode;
-  rtx label = gen_label_rtx ();
-  rtx loop_reg;
-  rtx jump, src;
-
-  /* Copy pointers into new psuedos - they will be changed.  */
-  rtx addr0 = copy_to_mode_reg (Pmode, XEXP (operands[0], 0));
-  rtx addr1 = copy_to_mode_reg (Pmode, XEXP (operands[1], 0));
-
-  /* Create rtx for tmp register - we use this as scratch.  */
-  rtx tmp_reg_rtx  = gen_rtx_REG (QImode, TMP_REGNO);
-
-  if (avr_mem_pgm_p (operands[0]))
-    DONE;
-
-  if (GET_CODE (operands[2]) != CONST_INT)
+  {
+    if (avr_emit_movmemhi (operands))
+      DONE;
+    
     FAIL;
+  })
 
-  count = INTVAL (operands[2]);
-  if (count <= 0)
-    FAIL;
+(define_mode_attr MOVMEM_r_d [(QI "r")
+                              (HI "d")])
+
+;; $0, $4 : & dest
+;; $1, $5 : & src
+;; $2     : Address Space
+;; $3, $7 : Loop register
+;; $6     : Scratch register
+
+;; "movmem_qi"
+;; "movmem_hi"
+(define_insn "movmem_<mode>"
+  [(set (mem:BLK (match_operand:HI 0 "register_operand" "x"))
+        (mem:BLK (match_operand:HI 1 "register_operand" "z")))
+   (unspec [(match_operand:QI 2 "const_int_operand"     "LP")]
+           UNSPEC_MOVMEM)
+   (use (match_operand:QIHI 3 "register_operand"       "<MOVMEM_r_d>"))
+   (clobber (match_operand:HI 4 "register_operand"     "=0"))
+   (clobber (match_operand:HI 5 "register_operand"     "=1"))
+   (clobber (match_operand:QI 6 "register_operand"     "=&r"))
+   (clobber (match_operand:QIHI 7 "register_operand"   "=3"))]
+  ""
+  {
+    return avr_out_movmem (insn, operands, NULL);
+  }
+  [(set_attr "adjust_len" "movmem")
+   (set_attr "cc" "clobber")])
+
+;; Ditto and
+;; $8, $9 : hh8 (& src)
+;; $10    : RAMPZ_ADDR
+
+;; "movmem_qi_elpm"
+;; "movmem_hi_elpm"
+(define_insn "movmem_<mode>_elpm"
+  [(set (mem:BLK (match_operand:HI 0 "register_operand"             "x"))
+        (mem:BLK (lo_sum:PSI (match_operand:QI 8 "register_operand" "r")
+                             (match_operand:HI 1 "register_operand" "z"))))
+   (unspec [(match_operand:QI 2 "const_int_operand"                 "n")]
+           UNSPEC_MOVMEM)
+   (use (match_operand:QIHI 3 "register_operand"                   "<MOVMEM_r_d>"))
+   (clobber (match_operand:HI 4 "register_operand"                 "=0"))
+   (clobber (match_operand:HI 5 "register_operand"                 "=1"))
+   (clobber (match_operand:QI 6 "register_operand"                 "=&r"))
+   (clobber (match_operand:QIHI 7 "register_operand"               "=3"))
+   (clobber (match_operand:QI 9 "register_operand"                 "=8"))
+   (clobber (mem:QI (match_operand:QI 10 "io_address_operand"       "n")))]
+  ""
+  {
+    return avr_out_movmem (insn, operands, NULL);
+  }
+  [(set_attr "adjust_len" "movmem")
+   (set_attr "cc" "clobber")])
 
-  /* Work out branch probability for latter use.  */
-  prob = REG_BR_PROB_BASE - REG_BR_PROB_BASE / count;
-
-  /* See if constant fit 8 bits.  */
-  mode = (count < 0x100) ? QImode : HImode;
-  /* Create loop counter register.  */
-  loop_reg = copy_to_mode_reg (mode, gen_int_mode (count, mode));
-
-  /* Now create RTL code for move loop.  */
-  /* Label at top of loop.  */
-  emit_label (label);
-
-  /* Move one byte into scratch and inc pointer.  */
-  src = gen_rtx_MEM (QImode, addr1);
-  set_mem_addr_space (src, MEM_ADDR_SPACE (operands[1]));
-  emit_move_insn (tmp_reg_rtx, src);
-  emit_move_insn (addr1, gen_rtx_PLUS (Pmode, addr1, const1_rtx));
-
-  /* Move to mem and inc pointer.  */
-  emit_move_insn (gen_rtx_MEM (QImode, addr0), tmp_reg_rtx);
-  emit_move_insn (addr0, gen_rtx_PLUS (Pmode, addr0, const1_rtx));
-
-  /* Decrement count.  */
-  emit_move_insn (loop_reg, gen_rtx_PLUS (mode, loop_reg, constm1_rtx));
-
-  /* Compare with zero and jump if not equal. */
-  emit_cmp_and_jump_insns (loop_reg, const0_rtx, NE, NULL_RTX, mode, 1,
-                           label);
-  /* Set jump probability based on loop count.  */
-  jump = get_last_insn ();
-  add_reg_note (jump, REG_BR_PROB, GEN_INT (prob));
-  DONE;
-}")
 
-;; =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2
+;; =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2
 ;; memset (%0, %2, %1)
 
 (define_expand "setmemhi"
   [(parallel [(set (match_operand:BLK 0 "memory_operand" "")
-                  (match_operand 2 "const_int_operand" ""))
-             (use (match_operand:HI 1 "const_int_operand" ""))
-             (use (match_operand:HI 3 "const_int_operand" "n"))
-             (clobber (match_scratch:HI 4 ""))
-             (clobber (match_dup 5))])]
+                   (match_operand 2 "const_int_operand" ""))
+              (use (match_operand:HI 1 "const_int_operand" ""))
+              (use (match_operand:HI 3 "const_int_operand" ""))
+              (clobber (match_scratch:HI 4 ""))
+              (clobber (match_dup 5))])]
   ""
-  "{
-  rtx addr0;
-  enum machine_mode mode;
+  {
+    rtx addr0;
+    enum machine_mode mode;
 
-  /* If value to set is not zero, use the library routine.  */
-  if (operands[2] != const0_rtx)
-    FAIL;
+    /* If value to set is not zero, use the library routine.  */
+    if (operands[2] != const0_rtx)
+      FAIL;
 
-  if (!CONST_INT_P (operands[1]))
-    FAIL;
+    if (!CONST_INT_P (operands[1]))
+      FAIL;
+
+    mode = u8_operand (operands[1], VOIDmode) ? QImode : HImode;
+    operands[5] = gen_rtx_SCRATCH (mode);
+    operands[1] = copy_to_mode_reg (mode,
+                                    gen_int_mode (INTVAL (operands[1]), mode));
+    addr0 = copy_to_mode_reg (Pmode, XEXP (operands[0], 0));
+    operands[0] = gen_rtx_MEM (BLKmode, addr0);
+  })
 
-  mode = u8_operand (operands[1], VOIDmode) ? QImode : HImode;
-  operands[5] = gen_rtx_SCRATCH (mode);
-  operands[1] = copy_to_mode_reg (mode,
-                                  gen_int_mode (INTVAL (operands[1]), mode));
-  addr0 = copy_to_mode_reg (Pmode, XEXP (operands[0], 0));
-  operands[0] = gen_rtx_MEM (BLKmode, addr0);
-}")
 
 (define_insn "*clrmemqi"
   [(set (mem:BLK (match_operand:HI 0 "register_operand" "e"))
-       (const_int 0))
+        (const_int 0))
    (use (match_operand:QI 1 "register_operand" "r"))
    (use (match_operand:QI 2 "const_int_operand" "n"))
    (clobber (match_scratch:HI 3 "=0"))
    (clobber (match_scratch:QI 4 "=&1"))]
   ""
-  "st %a0+,__zero_reg__
-        dec %1
-       brne .-6"
+  "0:\;st %a0+,__zero_reg__\;dec %1\;brne 0b"
   [(set_attr "length" "3")
    (set_attr "cc" "clobber")])
 
+
 (define_insn "*clrmemhi"
   [(set (mem:BLK (match_operand:HI 0 "register_operand" "e,e"))
-       (const_int 0))
+        (const_int 0))
    (use (match_operand:HI 1 "register_operand" "!w,d"))
    (use (match_operand:HI 2 "const_int_operand" "n,n"))
    (clobber (match_scratch:HI 3 "=0,0"))
    (clobber (match_scratch:HI 4 "=&1,&1"))]
   ""
-  "*{
-     if (which_alternative==0)
-       return (AS2 (st,%a0+,__zero_reg__) CR_TAB
-              AS2 (sbiw,%A1,1) CR_TAB
-              AS1 (brne,.-6));
-     else
-       return (AS2 (st,%a0+,__zero_reg__) CR_TAB
-              AS2 (subi,%A1,1) CR_TAB
-              AS2 (sbci,%B1,0) CR_TAB
-              AS1 (brne,.-8));
-}"
+  "@
+       0:\;st %a0+,__zero_reg__\;sbiw %A1,1\;brne 0b
+       0:\;st %a0+,__zero_reg__\;subi %A1,1\;sbci %B1,0\;brne 0b"
   [(set_attr "length" "3,4")
    (set_attr "cc" "clobber,clobber")])
 
 (define_expand "strlenhi"
-    [(set (match_dup 4)
-         (unspec:HI [(match_operand:BLK 1 "memory_operand" "")
-                     (match_operand:QI 2 "const_int_operand" "")
-                     (match_operand:HI 3 "immediate_operand" "")]
-                    UNSPEC_STRLEN))
-     (set (match_dup 4) (plus:HI (match_dup 4)
-                                (const_int -1)))
-     (set (match_operand:HI 0 "register_operand" "")
-         (minus:HI (match_dup 4)
-                   (match_dup 5)))]
-   ""
-   "{
-  rtx addr;
-  if (operands[2] != const0_rtx)
-    FAIL;
-  addr = copy_to_mode_reg (Pmode, XEXP (operands[1],0));
-  operands[1] = gen_rtx_MEM (BLKmode, addr); 
-  operands[5] = addr;
-  operands[4] = gen_reg_rtx (HImode);
-}")
+  [(set (match_dup 4)
+        (unspec:HI [(match_operand:BLK 1 "memory_operand" "")
+                    (match_operand:QI 2 "const_int_operand" "")
+                    (match_operand:HI 3 "immediate_operand" "")]
+                   UNSPEC_STRLEN))
+   (set (match_dup 4)
+        (plus:HI (match_dup 4)
+                 (const_int -1)))
+   (set (match_operand:HI 0 "register_operand" "")
+        (minus:HI (match_dup 4)
+                  (match_dup 5)))]
+  ""
+  {
+    rtx addr;
+    if (operands[2] != const0_rtx)
+      FAIL;
+    addr = copy_to_mode_reg (Pmode, XEXP (operands[1], 0));
+    operands[1] = gen_rtx_MEM (BLKmode, addr); 
+    operands[5] = addr;
+    operands[4] = gen_reg_rtx (HImode);
+  })
 
 (define_insn "*strlenhi"
-  [(set (match_operand:HI 0 "register_operand" "=e")
-       (unspec:HI [(mem:BLK (match_operand:HI 1 "register_operand" "%0"))
-                   (const_int 0)
-                   (match_operand:HI 2 "immediate_operand" "i")]
-                  UNSPEC_STRLEN))]
-  ""
-  "ld __tmp_reg__,%a0+
-       tst __tmp_reg__
-       brne .-6"
+  [(set (match_operand:HI 0 "register_operand"                      "=e")
+        (unspec:HI [(mem:BLK (match_operand:HI 1 "register_operand"  "0"))
+                    (const_int 0)
+                    (match_operand:HI 2 "immediate_operand"          "i")]
+                   UNSPEC_STRLEN))]
+  ""
+  "0:\;ld __tmp_reg__,%a0+\;tst __tmp_reg__\;brne 0b"
   [(set_attr "length" "3")
    (set_attr "cc" "clobber")])
 
index 9f595cbfb2922c9f0838ca3aa36eb0458f011781..fff34b58464e2f6e7006d4192cc950d13bb8c3d4 100644 (file)
        (match_test "!avr_mem_pgm_p (op)")))
 
 ;; Return 1 if OP is an "ordinary" general operand, i.e. a general
-;; operand whose load is not handled by a libgcc call.
+;; operand whose load is not handled by a libgcc call or ELPM.
 (define_predicate "nox_general_operand"
   (and (match_operand 0 "general_operand")
-       (match_test "!avr_load_libgcc_p (op)")))
+       (not (match_test "avr_load_libgcc_p (op)"))
+       (not (match_test "avr_mem_pgmx_p (op)"))))
 
 ;; Return 1 if OP is the zero constant for MODE.
 (define_predicate "const0_operand"
index 57fcc5400194ad9122a60caf56126036bf58b20a..86b482f9950f20baf46cd88f30a5da48b2ae19d0 100644 (file)
@@ -1,3 +1,10 @@
+2011-11-18  Georg-Johann Lay  <avr@gjlay.de>
+
+       PR target/49868
+       * config/avr/t-avr (LIB1ASMFUNCS): Add _xload_2 _xload_3 _xload_4.
+       * config/avr/lib1funcs.S (__xload_2, __xload_3, __xload_4):
+       New functions.
+
 2011-11-16  Matthew Gretton-Dann  <matthew.gretton-dann@arm.com>
 
        * config/arm/lib1funcs.asm (udivsi3): Add support for divide
index 573761b619a9c54295a69671d39f555f005a2af1..aee69e50d8e6179c7ffb799c8e9e6c674e3d862d 100644 (file)
@@ -1227,6 +1227,73 @@ ENDF __load_4
 
 #endif /* L_load_3 || L_load_3 */
 
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Loading n bytes from Flash; n = 2,3,4
+;; R22... = Flash[R21:Z]
+;; Clobbers: __tmp_reg__, R21, R30, R31
+
+#if (defined (L_xload_2)            \
+     || defined (L_xload_3)         \
+     || defined (L_xload_4))        \
+    && defined (__AVR_HAVE_ELPM__)  \
+    && !defined (__AVR_HAVE_ELPMX__)
+
+#if !defined (__AVR_HAVE_RAMPZ__)
+#error Need RAMPZ
+#endif /* have RAMPZ */
+
+;; Destination
+#define D0  22
+#define D1  D0+1
+#define D2  D0+2
+#define D3  D0+3
+
+;; Register containing bits 16+ of the address
+
+#define HHI8  21
+
+.macro  .xload dest, n
+    elpm
+    mov     \dest, r0
+.if \dest != D0+\n-1
+    adiw    r30, 1
+    adc     HHI8, __zero_reg__
+    out     __RAMPZ__, HHI8
+.endif
+.endm
+
+#if defined (L_xload_2)
+DEFUN __xload_2
+    out     __RAMPZ__, HHI8
+    .xload D0, 2
+    .xload D1, 2
+    ret
+ENDF __xload_2
+#endif /* L_xload_2 */
+
+#if defined (L_xload_3)
+DEFUN __xload_3
+    out     __RAMPZ__, HHI8
+    .xload D0, 3
+    .xload D1, 3
+    .xload D2, 3
+    ret
+ENDF __xload_3
+#endif /* L_xload_3 */
+
+#if defined (L_xload_4)
+DEFUN __xload_4
+    out     __RAMPZ__, HHI8
+    .xload D0, 4
+    .xload D1, 4
+    .xload D2, 4
+    .xload D3, 4
+    ret
+ENDF __xload_4
+#endif /* L_xload_4 */
+
+#endif /* L_xload_{2|3|4} && ELPM */
+
 \f
 .section .text.libgcc.builtins, "ax", @progbits
 
index 486214cf31b4b9a7eb96e9c193b66682e2ff30b7..5ebb17e4eb5805fbaeec7e818574048cdfacd454 100644 (file)
@@ -22,6 +22,7 @@ LIB1ASMFUNCS = \
        _tablejump \
        _tablejump_elpm \
        _load_3 _load_4 \
+       _xload_2 _xload_3 _xload_4 \
        _copy_data \
        _clear_bss \
        _ctors \