This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: [Patch]: PR49868: Named address space support for AVR


Denis Chertykov wrote:
> 2011/10/28 Georg-Johann Lay <????>:
>> Georg-Johann Lay schrieb:
>>
>>> This patch adds named address space support to read data from flash (aka.
>>> progmem) to target AVR.
>>>
>>> The patch has two parts:
>>>
>>> The first part is a repost of Ulrich's work from
>>>    http://gcc.gnu.org/ml/gcc/2011-08/msg00131.html
>>> with the needed changes to ./gcc and ./gcc/doc
>>>
>>> This patch is needed because the target hooks MODE_CODE_BASE_REG_CLASS and
>>> REGNO_MODE_CODE_OK_FOR_BASE_P don't distinguish between different address
>>> spaces.  Ulrich's patch adds respective support to these hooks.
>>>
>>> The second part is the AVR dependent part that adds __pgm as address space
>>> qualifier for address space AS1.
>>>
>>> The AVR part is just the worker code.  If there is agreement that AS support
>>> for AVR is okay in principle and Ulrich's work will go into GCC, I will supply
>>> test programs and updates to the user manual, of course.
>>>
>>> The major drawbacks of the current AS implementation are:
>>>
>>> - It works only for C.
>>>   For C++, a language extension would be needed as indicated in
>>>      ISO/IEC DTR 18037
>>>      Annex F - C++ Compatibility and Migration issues
>>>      F.2 Multiple Address Spaces Support
>>>
>>> - Register allocation does not a good job. AS1 can only be addressed
>>>   byte-wise by one single address register (Z) as per *Z or *Z++.
>> This flaw from register allocator are filed as PR50775 now.
>>
>>> The AVR part does several things:
>>>
>>> - It locates data in AS1 into appropriate section, i.e. somewhere in
>>>   .progmem
>>>
>>> - It does early sanity checks to ensure that __pgm is always accompanied
>>>   with const so that writing to AS1 in not possible.
>>>
>>> - It prints LPM instructions to access flash memory.
>> The attached patch is an update merge so that it fits without conflicts.
>>
>> The patch requires Ulrich's works which is still in review
>>   http://gcc.gnu.org/bugzilla/show_bug.cgi?id=50775
>>
>> The regression tests run with this patch and the new ChangeLog enttry si
>> written as if Ulrich's patch was applied.
>>
>> Tests pass without regression.
>>
>> Besides the update to a nor up-to-date SVN version, the patch sets a built-in
>> define __PGM so that it is easy for users to test if or if not the feature is
>> available.
>>
>> Documentation and test cases will follow in separate patch.
>>
>> Ok for trunk after Ulrich's work has been approved?
>>
>> Johann
>>
>>        PR target/49868
>>        * config/avr/avr.h (ADDR_SPACE_PGM): New define for address space AS1.
>>        (REGISTER_TARGET_PRAGMAS): New define.
>>        * config/avr/avr-protos.h (avr_mem_pgm_p): New prototype.
>>        (avr_register_target_pragmas): New prototype.
>>        (avr_log_t): Add field "progmem".  Order alphabetically.
>>        * config/avr/avr-log.c (avr_log_set_avr_log): Set avr_log.progmem.
>>        * config/avr/avr-c.c (langhooks.h): New include.
>>        (avr_register_target_pragmas): New function. Register address
>>        space AS1 as "__pgm".
>>        (avr_cpu_cpp_builtins): Add built-in define __PGM.
>>        * config/avr/avr.c: Include "c-family/c-common.h".
>>        (TARGET_LEGITIMATE_ADDRESS_P): Remove define.
>>        (TARGET_LEGITIMIZE_ADDRESS): Remove define.
>>        (TARGET_ADDR_SPACE_SUBSET_P): Define to...
>>        (avr_addr_space_subset_p): ...this new static function.
>>        (TARGET_ADDR_SPACE_CONVERT): Define to...
>>        (avr_addr_space_convert): ...this new static function.
>>        (TARGET_ADDR_SPACE_ADDRESS_MODE): Define to...
>>        (avr_addr_space_address_mode): ...this new static function.
>>        (TARGET_ADDR_SPACE_POINTER_MODE): Define to...
>>        (avr_addr_space_pointer_mode): ...this new static function.
>>        (TARGET_ADDR_SPACE_LEGITIMATE_ADDRESS_P): Define to...
>>        (avr_addr_space_legitimate_address_p): ...this new static function.
>>        (TARGET_ADDR_SPACE_LEGITIMIZE_ADDRESS): Define to...
>>        (avr_addr_space_legitimize_address): ...this new static function.
>>        (avr_mode_code_base_reg_class): Handle AS1.
>>        (avr_regno_mode_code_ok_for_base_p): Handle AS1.
>>        (lpm_addr_reg_rtx, lpm_reg_rtx): New static GTYed variables.
>>        (avr_decl_pgm_p): New static function.
>>        (avr_mem_pgm_p): New function.
>>        (avr_asm_len): Return always "" instead of void.
>>        (avr_out_lpm_no_lpmx): New static function.
>>        (avr_out_lpm): New static function.
>>        (output_movqi, output_movhi, output_movsisf): Call avr_out_lpm to
>>        handle loads from progmem.
>>        (avr_progmem_p): Test if decl is in AS1.
>>        (avr_pgm_pointer_const_p): New static function.
>>        (avr_pgm_check_var_decl): New static function.
>>        (avr_insert_attributes): Use it.  Change error message to report
>>        cause (progmem or AS1) when code wants to write to AS1.
>>        (avr_section_type_flags): Unset section flag SECTION_BSS for
>>        data in progmem.
>>        * config/avr/avr.md (LPM_REGNO): New define_constants.
>>        (movqi, movhi, movsi, movsf): Skip if code would write to AS1.
>>        (movmemhi): Ditto.  Propagate address space information to newly
>>        created MEM.
>>        (split-lpmx): New split.
>>
> 
> Approved.
> 
> Denis.

The patch from above is still not upstream because it needs the following work
which is still pending review:

http://gcc.gnu.org/ml/gcc-patches/2011-10/msg01874.html

The patch you find in this message contains the above patch with the following
minor changes:

* The patch is rebased to a newer revision
* avr_out_lpm_no_lpmx and avr_out_lpm can also load the new 3-byte PSImode
  that got upstream meanwhile.
* If there is no LMPX instruction and there are more than 2 bytes to load,
  a libgcc function is called.

Moreover, the patch introduces the following major changes:

* There is support for a new 24-bit address space called __pgmx
* There is support for new 16-bit address spaces that represent
  the different 64k flash segments. Their names are __pgm2 .. __pgm5
* There are libgcc functions that load from flash for the expensive
  cases, i.e. there is no ELPMX.

The reason why the __pgm<n> address spaces are there is because there is very
little to do to support them and there is no need for 24-bit pointers.

The work is not yet complete, but I'd like to post it before stage 1 is over.
Sorry for the inconvenience for just another review of the matter...

What's missing is selection of sections for the numbered address spaces.

The libgcc is independent of the not-yet-reviewed patch from Ulrich.

Is the libgcc part ok for trunk?
Is the gcc part ok provided Ulrich had been reviewed?

I'd like to do the remaining as follow-up patches:

* Release Notes
* Documentation
* Test cases
* Section handling for numbered address spaces

Johann

gcc/
	PR target/49868
	* config/avr/avr.h (base_arch_s): Add field n_segments.
	(ADDR_SPACE_PGM, 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.
	(REGISTER_TARGET_PRAGMAS): New define.
	* config/avr/avr-protos.h (avr_mem_pgm_p, avr_mem_pgmx_p): New.
	(avr_out_xload, avr_xload_libgcc_p): New.
	(asm_output_external_libcall): Remove.
	(avr_register_target_pragmas): New.
	(avr_log_t): Add field "progmem".  Order alphabetically.
	* config/avr/avr-log.c (avr_log_set_avr_log): Set avr_log.progmem.
	* config/avr/avr-c.c (langhooks.h): New include.
	(avr_register_target_pragmas): New function. Register address
	spaces __pgm,  __pgm2,  __pgm3,  __pgm4  __pgm5,  __pgmx.
	(avr_cpu_cpp_builtins): Add built-in defines __PGM, __PGM1,
	__PGM2, __PGM3, __PGM4, __PGM5, __PGMX.
	* config/avr/avr-devices.c (avr_arch_types): Set field n_segments.
	
	* config/avr/avr.c: Include "c-family/c-common.h".
	(TARGET_LEGITIMATE_ADDRESS_P): Remove define.
	(TARGET_LEGITIMIZE_ADDRESS): Remove define.
	(TARGET_ADDR_SPACE_SUBSET_P): Define to...
	(avr_addr_space_subset_p): ...this new static function.
	(TARGET_ADDR_SPACE_CONVERT): Define to...
	(avr_addr_space_convert): ...this new static function.
	(TARGET_ADDR_SPACE_ADDRESS_MODE): Define to...
	(avr_addr_space_address_mode): ...this new static function.
	(TARGET_ADDR_SPACE_POINTER_MODE): Define to...
	(avr_addr_space_pointer_mode): ...this new static function.
	(TARGET_ADDR_SPACE_LEGITIMATE_ADDRESS_P): Define to...
	(avr_addr_space_legitimate_address_p): ...this new static function.
	(TARGET_ADDR_SPACE_LEGITIMIZE_ADDRESS): Define to...
	(avr_addr_space_legitimize_address): ...this new static function.
	(avr_mode_code_base_reg_class): Handle address spaces.
	(avr_regno_mode_code_ok_for_base_p): Ditto.
	(lpm_addr_reg_rtx, lpm_reg_rtx, xstring_empty, xstring_e,
	all_regs_rtx): New static GTYed variables.
	(avr_option_override): Initialize them.
	(avr_pgm_segment): New static function.
	(avr_decl_pgm_p, avr_decl_pgmx_p): New static functions.
	(avr_mem_pgm_p, avr_mem_pgmx_p): New functions.
	(avr_asm_len): Return always "" instead of void.
	(avr_out_lpm, avr_out_lpm_no_lpmx, avr_out_xload,
	avr_find_unused_d_reg): New static functions.
	(output_movqi, output_movhi, output_movsisf, avr_out_movpsi): Call
	avr_out_lpm to handle loads from progmem.
	(print_operand): Hande CONST_STRING.
	(avr_xload_libgcc_p): New static function.
	(avr_progmem_p): Test if decl is in flash.
	(avr_pgm_pointer_const_p): New static function.
	(avr_nonconst_pointer_addrspace): New static function.
	(avr_pgm_check_var_decl): New static function.
	(avr_insert_attributes): Use it.  Change error message to report
	cause (progmem or address space) when code wants to write to flash.
	(avr_section_type_flags): Unset section flag SECTION_BSS for
	data in progmem.
	(adjust_insn_length): Handle ADJUST_LEN_XLOAD.
	(avr_const_address_lo16): New static function.
	(avr_assemble_integer): Use it to handle 3-byte integers.
	(avr_file_star): Print set for __RAMPZ__.
	(avr_emit_move): New function.
	
	* config/avr/predicates.md (hh8_operand): New predicate.
	(nop_general_operand): New predicate.
	(nox_general_operand): New predicate.
	* config/avr/avr.md (LPM_REGNO): New define_constants.
	(adjust_len): Add xload.
	(load<mode>_libgcc): New expander.
	(xload8_A, xload<mode>_A, *xload.<mode>): New insns.
	(*load.<mode>.libgcc, *xload.qi, *xload.<mode>.libgcc): New
	insn-and-splits.
	(mov<mode>): Call avr_emit_move to expand.
	(movmemhi): Ditto.  Propagate address space information to newly
	created MEM.
	(movqi_insn, *movhi, *movpsi, *movsi, *movsf): Change predicate #1
	to nox_general_operand.
	(ashrqi3, ashrhi3, ashrsi3): Change predicate #1 to nop_general_operand.
	(ashlqi3, *ashlqi3, ashlhi3, ashlsi3): Ditto.
	(lshrqi3, *lshrqi3, lshrhi3, lshrsi3): Ditto.
	(split-lpmx): New split.
	(*ashlhi3_const, *ashlsi3_const, *ashrhi3_const, *ashrsi3_const,
	*lshrhi3_const, *lshrsi3_const): Indent, unquote C.
	(n_extendhipsi2): New insn-and-split.

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




diff --git a/config/avr/avr-c.c b/config/avr/avr-c.c
index 55de2d7..998c79e 100644
--- a/config/avr/avr-c.c
+++ b/config/avr/avr-c.c
@@ -18,6 +18,7 @@
    along with GCC; see the file COPYING3.  If not see
    <http://www.gnu.org/licenses/>.  */
 
+/* Not included in avr.c since this requires C front end.  */
 
 #include "config.h"
 #include "system.h"
@@ -27,8 +28,23 @@
 #include "cpplib.h"
 #include "tree.h"
 #include "c-family/c-common.h"
+#include "langhooks.h"
+
+
+/* Implement `REGISTER_TARGET_PRAGMAS'.  */
+
+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);
+}
 
-/* Not included in avr.c since this requires C front end.  */
 
 /* Worker function for TARGET_CPU_CPP_BUILTINS.  */
 
@@ -90,6 +106,23 @@ avr_cpu_cpp_builtins (struct cpp_reader *pfile)
         cpp_define (pfile, "__AVR_ERRATA_SKIP_JMP_CALL__");
     }
 
+  /* Define builtin macros so that the user can easily query if or if not
+     non-generic address spaces (and which) are supported.
+     This is only supported for C as C+ will need a language
+     extension as of ISO/IEC DTR 18037; Annex F.2 which is not
+     implemented in GCC up to now.  */
+  
+  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
      easily query if or if not a specific builtin
      is available. */
diff --git a/config/avr/avr-devices.c b/config/avr/avr-devices.c
index d0dda85..944b6bb 100644
--- a/config/avr/avr-devices.c
+++ b/config/avr/avr-devices.c
@@ -25,18 +25,21 @@
 
 /* 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" },
+  { 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[] = {
diff --git a/config/avr/avr-log.c b/config/avr/avr-log.c
index cdeb669..b9ec18b 100644
--- a/config/avr/avr-log.c
+++ b/config/avr/avr-log.c
@@ -59,7 +59,9 @@
   F: caller (via __FUNCTION__)
   P: Pass name and number
   ?: Print caller, current function and pass info
-
+  !: Ditto, but only print if in a pass with static pass number,
+     else return.
+  
   == same as printf ==
   
   %: %
@@ -318,12 +320,13 @@ avr_log_set_avr_log (void)
                    || NULL != strstr (str, "," #S ",")                  \
                    || NULL != strstr (str, ",all,"))
 
-      SET_DUMP_DETAIL (rtx_costs);
+      SET_DUMP_DETAIL (address_cost);
+      SET_DUMP_DETAIL (constraints);
       SET_DUMP_DETAIL (legitimate_address_p);
       SET_DUMP_DETAIL (legitimize_address);
       SET_DUMP_DETAIL (legitimize_reload_address);
-      SET_DUMP_DETAIL (constraints);
-      SET_DUMP_DETAIL (address_cost);
+      SET_DUMP_DETAIL (progmem);
+      SET_DUMP_DETAIL (rtx_costs);
 
 #undef SET_DUMP_DETAIL
     }
diff --git a/config/avr/avr-protos.h b/config/avr/avr-protos.h
index d351833..e2fbdca 100644
--- a/config/avr/avr-protos.h
+++ b/config/avr/avr-protos.h
@@ -32,6 +32,7 @@ extern int avr_initial_elimination_offset (int from, int to);
 extern int avr_simple_epilogue (void);
 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);
 
 #ifdef TREE_CODE
@@ -47,7 +48,7 @@ extern void init_cumulative_args (CUMULATIVE_ARGS *cum, tree fntype,
 #endif /* TREE_CODE */
 
 #ifdef RTX_CODE
-extern void asm_output_external_libcall (FILE *file, rtx symref);
+extern bool avr_emit_move (rtx*);
 extern const char *output_movqi (rtx insn, rtx operands[], int *l);
 extern const char *output_movhi (rtx insn, rtx operands[], int *l);
 extern const char *out_movqi_r_mr (rtx insn, rtx op[], int *l);
@@ -121,6 +122,10 @@ extern reg_class_t avr_mode_code_base_reg_class (enum machine_mode, addr_space_t
 extern bool avr_regno_mode_code_ok_for_base_p (int, enum machine_mode, addr_space_t, RTX_CODE, RTX_CODE);
 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 const char * avr_out_xload (rtx, rtx*, int*);
+extern bool avr_xload_libgcc_p (enum machine_mode);
 #endif /* RTX_CODE */
 
 #ifdef REAL_VALUE_TYPE
@@ -139,12 +144,13 @@ extern void avr_log_set_avr_log (void);
 
 typedef struct
 {
-  unsigned rtx_costs :1;
+  unsigned address_cost :1;
+  unsigned constraints :1;
   unsigned legitimate_address_p :1;
   unsigned legitimize_address :1;
   unsigned legitimize_reload_address :1;
-  unsigned constraints :1;
-  unsigned address_cost :1;
+  unsigned progmem :1;
+  unsigned rtx_costs :1;
 } avr_log_t;
 
 extern avr_log_t avr_log;
diff --git a/config/avr/avr.c b/config/avr/avr.c
index 01f8376..8a942c1 100644
--- a/config/avr/avr.c
+++ b/config/avr/avr.c
@@ -35,6 +35,7 @@
 #include "tree.h"
 #include "output.h"
 #include "expr.h"
+#include "c-family/c-common.h"
 #include "diagnostic-core.h"
 #include "obstack.h"
 #include "function.h"
@@ -73,7 +74,7 @@ static int avr_num_arg_regs (enum machine_mode, const_tree);
 static int avr_operand_rtx_cost (rtx, enum machine_mode, enum rtx_code,
 				 int, bool);
 static struct machine_function * avr_init_machine_status (void);
-
+static rtx avr_find_unused_d_reg (rtx, rtx);
 
 /* Prototypes for hook implementors if needed before their implementation.  */
 
@@ -83,12 +84,25 @@ static bool avr_rtx_costs (rtx, int, int, int, int *, bool);
 /* Allocate registers from r25 to r8 for parameters for function calls.  */
 #define FIRST_CUM_REG 26
 
+/* Implicit target register of LPM instruction (R0) */
+static GTY(()) rtx lpm_reg_rtx;
+
+/* Implicit address register of LPM instruction (R31:R30 = Z) */
+static GTY(()) rtx lpm_addr_reg_rtx;
+
 /* Temporary register RTX (gen_rtx_REG (QImode, TMP_REGNO)) */
 static GTY(()) rtx tmp_reg_rtx;
 
 /* Zeroed register RTX (gen_rtx_REG (QImode, ZERO_REGNO)) */
 static GTY(()) rtx zero_reg_rtx;
 
+/* RTX containing the strings "" and "e" */
+static GTY(()) rtx xstring_empty;
+static GTY(()) rtx xstring_e;
+
+/* RTXs for all general purpose registers as QImode */
+static GTY(()) rtx all_regs_rtx[32];
+
 /* AVR register names {"r0", "r1", ..., "r31"} */
 static const char *const avr_regnames[] = REGISTER_NAMES;
 
@@ -171,9 +185,6 @@ bool avr_need_copy_data_p = false;
 #undef TARGET_FUNCTION_ARG_ADVANCE
 #define TARGET_FUNCTION_ARG_ADVANCE avr_function_arg_advance
 
-#undef TARGET_LEGITIMIZE_ADDRESS
-#define TARGET_LEGITIMIZE_ADDRESS avr_legitimize_address
-
 #undef TARGET_RETURN_IN_MEMORY
 #define TARGET_RETURN_IN_MEMORY avr_return_in_memory
 
@@ -188,9 +199,6 @@ bool avr_need_copy_data_p = false;
 #undef TARGET_CASE_VALUES_THRESHOLD
 #define TARGET_CASE_VALUES_THRESHOLD avr_case_values_threshold
 
-#undef TARGET_LEGITIMATE_ADDRESS_P
-#define TARGET_LEGITIMATE_ADDRESS_P avr_legitimate_address_p
-
 #undef TARGET_FRAME_POINTER_REQUIRED
 #define TARGET_FRAME_POINTER_REQUIRED avr_frame_pointer_required_p
 #undef TARGET_CAN_ELIMINATE
@@ -220,6 +228,24 @@ bool avr_need_copy_data_p = false;
 #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
+
 
 
 /* Custom function to replace string prefix.
@@ -294,6 +320,8 @@ 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
@@ -322,9 +350,18 @@ avr_option_override (void)
   avr_current_arch = &avr_arch_types[avr_current_device->arch];
   avr_extra_arch_macro = avr_current_device->macro;
 
-  tmp_reg_rtx  = gen_rtx_REG (QImode, TMP_REGNO);
-  zero_reg_rtx = gen_rtx_REG (QImode, ZERO_REGNO);
+  lpm_addr_reg_rtx = gen_rtx_REG (Pmode, REG_Z);
+
+  xstring_empty = gen_rtx_CONST_STRING (VOIDmode, "");
+  xstring_e = gen_rtx_CONST_STRING (VOIDmode, "e");
+
+  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];
+  
   init_machine_status = avr_init_machine_status;
 
   avr_log_set_avr_log();
@@ -383,6 +420,73 @@ 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 or AS is not in flash.  */
+
+static int
+avr_pgm_segment (addr_space_t as)
+{
+  switch (as)
+    {
+    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;
+    default: return  -1;
+    }
+}
+
+
+/* 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;
+
+  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)
+    return false;
+  
+  return (ADDR_SPACE_PGMX == TYPE_ADDR_SPACE (TREE_TYPE (decl)));
+}
+
+
+/* Return TRUE if X is a MEM rtx located in Flash and FALSE, otherwise.  */
+
+bool
+avr_mem_pgm_p (rtx x)
+{
+  return (MEM_P (x)
+          && !ADDR_SPACE_GENERIC_P (MEM_ADDR_SPACE (x)));
+}
+
+
+/* Return TRUE if X is a MEM rtx located in the 24-bit Flash
+   address space and FALSE, otherwise.  */
+
+bool
+avr_mem_pgmx_p (rtx x)
+{
+  return (MEM_P (x)
+          && ADDR_SPACE_PGMX == MEM_ADDR_SPACE (x));
+}
+
+
 /* A helper for the subsequent function attribute used to dig for
    attribute 'name' in a FUNCTION_DECL or FUNCTION_TYPE */
 
@@ -1378,6 +1482,9 @@ avr_legitimate_address_p (enum machine_mode mode, rtx x, bool strict)
   return ok;
 }
 
+
+/* Former implementation of TARGET_LEGITIMIZE_ADDRESS,
+   now only a helper for avr_addr_space_legitimize_address.  */
 /* Attempts to replace X with a valid
    memory address for an operand of mode MODE  */
 
@@ -1739,6 +1846,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')
@@ -2176,6 +2285,396 @@ avr_function_ok_for_sibcall (tree decl_callee, tree exp_callee)
 /***********************************************************************
   Functions for outputting various mov's for a various modes
 ************************************************************************/
+
+
+/* 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_current_arch->have_elpmx);
+}
+
+
+static rtx
+avr_find_unused_d_reg (rtx insn, rtx exclude)
+{
+  int regno;
+
+  for (regno = 16; regno < 32; regno ++)
+    {
+      rtx reg = all_regs_rtx[regno];
+      
+      if (exclude
+          && reg_overlap_mentioned_p (exclude, reg))
+        {
+          continue;
+        }
+
+      if (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:
+          return avr_asm_len ("%4lpm" CR_TAB
+                              "mov %0,%3", xop, plen, -2);
+
+        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);
+          else
+            {
+              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 */
+
+        case 3:
+          avr_asm_len ("%4lpm"      CR_TAB
+                       "mov %A0,%3" CR_TAB
+                       "adiw %2,1"  CR_TAB
+                       "%4lpm"      CR_TAB
+                       "mov %B0,%3" CR_TAB
+                       "adiw %2,1"  CR_TAB
+                       "%4lpm"      CR_TAB
+                       "mov %C0,%3", xop, plen, -8);
+          
+          if (REGNO (dest) != REG_Z - 2
+              || !reg_unused_after (insn, addr))
+            {
+              avr_asm_len ("sbiw %2,2", xop, plen, 1);
+            }
+
+          break; /* 3 */
+
+        case 4:
+          avr_asm_len ("%4lpm"      CR_TAB
+                       "mov %A0,%3" CR_TAB
+                       "adiw %2,1"  CR_TAB
+                       "%4lpm"      CR_TAB
+                       "mov %B0,%3" CR_TAB
+                       "adiw %2,1", xop, plen, -6);
+          
+          if (REGNO (dest) == REG_Z - 2)
+            return avr_asm_len ("%4lpm"      CR_TAB
+                                "push %3"    CR_TAB
+                                "adiw %2,1"  CR_TAB
+                                "%4lpm"      CR_TAB
+                                "mov %D0,%3" CR_TAB
+                                "pop %C0", xop, plen, 6);
+          else
+            {
+              avr_asm_len ("%4lpm"      CR_TAB
+                           "mov %C0,%3" CR_TAB
+                           "adiw %2,1"  CR_TAB
+                           "%4lpm"      CR_TAB
+                           "mov %D0,%3", xop, plen, 5);
+                
+              if (!reg_unused_after (insn, addr))
+                avr_asm_len ("sbiw %2,3", xop, plen, 1);
+            }
+
+          break; /* 4 */
+        }
+      
+      break; /* REG */
+
+    case POST_INC:
+
+      gcc_assert (REG_Z == REGNO (XEXP (addr, 0))
+                  && n_bytes <= 4);
+
+      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 "";
+}
+
+
+/* If PLEN == NULL: Ouput instructions to load a value from a memory location
+   OP[1] in AS1 to register OP[0].
+   If PLEN != 0 set *PLEN to the length in words of the instruction sequence.
+   Return "".  */
+
+static const char*
+avr_out_lpm (rtx insn, rtx *op, int *plen)
+{
+  rtx xop[5];
+  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;
+  
+  if (MEM_P (dest))
+    {
+      warning (0, "writing to address space %qs not supported",
+               c_addr_space_name (MEM_ADDR_SPACE (dest)));
+      
+      return "";
+    }
+
+  addr = XEXP (src, 0);
+
+  segment = avr_pgm_segment (MEM_ADDR_SPACE (src));
+
+  gcc_assert (REG_P (dest)
+              && ((REG_P (addr) && segment >= 0)
+                  || (GET_CODE (addr) == LO_SUM && segment == -1)));
+
+  if (segment == -1)
+    {
+      /* We are called from avr_out_xload because someone wrote
+         __pgmx on a devise with just one flash segment.  */
+
+      addr = XEXP (addr, 1);
+    }
+
+  xop[0] = dest;
+  xop[1] = addr;
+  xop[2] = lpm_addr_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_current_arch->n_segments;
+
+  if (segment == 0)
+    {
+      xop[4] = xstring_empty;
+    }
+  else
+    {
+      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 __tmp_reg__" CR_TAB
+                       "inc __tmp_reg__" CR_TAB
+                       "out __RAMPZ__,__tmp_reg__", xop, plen, 3);
+        }
+      else
+        {
+          avr_asm_len ("mov __tmp_reg__,%2" CR_TAB
+                       "ldi %2,%4"          CR_TAB
+                       "out __RAMPZ__,%2"   CR_TAB
+                       "mov %2,__tmp_reg__", 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);
+    }
+
+  switch (GET_CODE (addr))
+    {
+    default:
+      gcc_unreachable();
+
+    case REG:
+
+      gcc_assert (REG_Z == REGNO (addr));
+
+      switch (n_bytes)
+        {
+        default:
+          gcc_unreachable();
+
+        case 1:
+          return avr_asm_len ("%4lpm %0,%a2", xop, plen, -1);
+
+        case 2:
+          if (REGNO (dest) == REG_Z)
+            return avr_asm_len ("%4lpm __tmp_reg__,%a2+" CR_TAB
+                                "%4lpm %B0,%a2"          CR_TAB
+                                "mov %A0,__tmp_reg__", xop, plen, -3);
+          else
+            {
+              avr_asm_len ("%4lpm %A0,%a2+" CR_TAB
+                           "%4lpm %B0,%a2", xop, plen, -2);
+                
+              if (!reg_unused_after (insn, addr))
+                avr_asm_len ("sbiw %2,1", xop, plen, 1);
+            }
+          
+          break; /* 2 */
+
+        case 3:
+
+          avr_asm_len ("%4lpm %A0,%a2+" CR_TAB
+                       "%4lpm %B0,%a2+" CR_TAB
+                       "%4lpm %C0,%a2", xop, plen, -3);
+                
+          if (!reg_unused_after (insn, addr))
+            avr_asm_len ("sbiw %2,2", xop, plen, 1);
+
+          break; /* 3 */
+      
+        case 4:
+
+          avr_asm_len ("%4lpm %A0,%a2+" CR_TAB
+                       "%4lpm %B0,%a2+", xop, plen, -2);
+          
+          if (REGNO (dest) == REG_Z - 2)
+            return avr_asm_len ("%4lpm __tmp_reg__,%a2+" CR_TAB
+                                "%4lpm %C0,%a2"          CR_TAB
+                                "mov %D0,__tmp_reg__", xop, plen, 3);
+          else
+            {
+              avr_asm_len ("%4lpm %C0,%a2+" CR_TAB
+                           "%4lpm %D0,%a2", xop, plen, 2);
+                
+              if (!reg_unused_after (insn, addr))
+                avr_asm_len ("sbiw %2,3", xop, plen, 1);
+            }
+
+          break; /* 4 */
+        } /* n_bytes */
+      
+      break; /* REG */
+
+    case POST_INC:
+
+      gcc_assert (REG_Z == REGNO (XEXP (addr, 0))
+                  && n_bytes <= 4);
+
+      avr_asm_len                    ("%4lpm %A0,%a2+", xop, plen, -1);
+      if (n_bytes >= 2)  avr_asm_len ("%4lpm %B0,%a2+", xop, plen, 1);
+      if (n_bytes >= 3)  avr_asm_len ("%4lpm %C0,%a2+", xop, plen, 1);
+      if (n_bytes >= 4)  avr_asm_len ("%4lpm %D0,%a2+", xop, plen, 1);
+
+      break; /* POST_INC */
+
+    } /* switch CODE (addr) */
+      
+  return "";
+}
+
+
+/* Worker function for "*xload.<mode>" insns.  */
+
+const char*
+avr_out_xload (rtx insn, rtx *op, int *plen)
+{
+  rtx xop[4];
+  int n_bytes = GET_MODE_SIZE (GET_MODE (op[0]));
+
+  if (avr_current_arch->n_segments == 1)
+    return avr_out_lpm (insn, op, plen);
+
+  xop[0] = op[0];
+  xop[1] = op[1];
+  xop[2] = lpm_addr_reg_rtx;
+  xop[3] = lpm_reg_rtx;
+  
+  avr_asm_len ("out __RAMPZ__,%1", xop, plen, -1);
+  
+  if (1 == n_bytes)
+    {
+      if (avr_current_arch->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, 1);
+    }
+
+  /* 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 "";
+}
+
+
 const char *
 output_movqi (rtx insn, rtx operands[], int *l)
 {
@@ -2184,6 +2683,12 @@ output_movqi (rtx insn, rtx operands[], int *l)
   rtx src = operands[1];
   int *real_l = l;
   
+  if (avr_mem_pgm_p (src)
+      || avr_mem_pgm_p (dest))
+    {
+      return avr_out_lpm (insn, operands, real_l);
+    }
+
   if (!l)
     l = &dummy;
 
@@ -2276,6 +2781,12 @@ output_movhi (rtx insn, rtx operands[], int *l)
   rtx src = operands[1];
   int *real_l = l;
   
+  if (avr_mem_pgm_p (src)
+      || avr_mem_pgm_p (dest))
+    {
+      return avr_out_lpm (insn, operands, real_l);
+    }
+
   if (!l)
     l = &dummy;
   
@@ -2889,6 +3400,12 @@ output_movsisf (rtx insn, rtx operands[], int *l)
   rtx src = operands[1];
   int *real_l = l;
   
+  if (avr_mem_pgm_p (src)
+      || avr_mem_pgm_p (dest))
+    {
+      return avr_out_lpm (insn, operands, real_l);
+    }
+
   if (!l)
     l = &dummy;
   
@@ -3208,6 +3725,12 @@ avr_out_movpsi (rtx insn, rtx *op, int *plen)
   rtx dest = op[0];
   rtx src = op[1];
   
+  if (avr_mem_pgm_p (src)
+      || avr_mem_pgm_p (dest))
+    {
+      return avr_out_lpm (insn, op, plen);
+    }
+  
   if (register_operand (dest, VOIDmode))
     {
       if (register_operand (src, VOIDmode)) /* mov r,r */
@@ -3807,7 +4330,8 @@ out_shift_with_cnt (const char *templ, rtx insn, rtx operands[],
 
   if (GET_CODE (operands[2]) == CONST_INT)
     {
-      int scratch = (GET_CODE (PATTERN (insn)) == PARALLEL);
+      bool scratch = (GET_CODE (PATTERN (insn)) == PARALLEL
+                      && REG_P (operands[3]));
       int count = INTVAL (operands[2]);
       int max_len = 10;  /* If larger than this, always use a loop.  */
 
@@ -5938,7 +6462,8 @@ 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_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;
@@ -6083,23 +6608,85 @@ _reg_unused_after (rtx insn, rtx reg)
   return 1;
 }
 
-/* Target hook for assembling integer objects.  The AVR version needs
-   special handling for references to certain labels.  */
 
-static bool
-avr_assemble_integer (rtx x, unsigned int size, int aligned_p)
+/* 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)
 {
-  if (size == POINTER_SIZE / BITS_PER_UNIT && aligned_p
-      && text_segment_operand (x, VOIDmode) )
+  rtx lo16;
+  
+  switch (GET_CODE (x))
     {
-      fputs ("\t.word\tgs(", asm_out_file);
-      output_addr_const (asm_out_file, x);
-      fputs (")\n", asm_out_file);
-      return true;
+    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.  */
+
+static bool
+avr_assemble_integer (rtx x, unsigned int size, int aligned_p)
+{
+  if (size == POINTER_SIZE / BITS_PER_UNIT && aligned_p
+      && text_segment_operand (x, VOIDmode) )
+    {
+      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\"24-bit address needs assembler 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
@@ -6249,8 +6836,16 @@ avr_attribute_table[] =
   { NULL,        0, 0, false, false, false, NULL, false }
 };
 
-/* Look for attribute `progmem' in DECL
-   if found return 1, otherwise 0.  */
+
+/* Look if DECL shall be placed in program memory space by
+   means of attribute `progmem' or some address-space qualifier.
+   Return non-zero if DECL is data that must end up in Flash and
+   zero if the data lives in RAM (.bss, .data, .rodata, ...).
+   
+   Return 2   if DECL is located in 24-bit flash address-space
+   Return 1   if DECL is located in 16-bit flash address-space
+   Return -1  if attribute `progmem' occurs in DECL or ATTRIBUTES
+   Return 0   otherwise  */
 
 int
 avr_progmem_p (tree decl, tree attributes)
@@ -6260,11 +6855,18 @@ avr_progmem_p (tree decl, tree attributes)
   if (TREE_CODE (decl) != VAR_DECL)
     return 0;
 
+  if (avr_decl_pgmx_p (decl))
+    return 2;
+
+  if (avr_decl_pgm_p (decl))
+    return 1;
+
   if (NULL_TREE
       != lookup_attribute ("progmem", attributes))
-    return 1;
+    return -1;
 
-  a=decl;
+  a = decl;
+ 
   do
     a = TREE_TYPE(a);
   while (TREE_CODE (a) == ARRAY_TYPE);
@@ -6273,16 +6875,122 @@ avr_progmem_p (tree decl, tree attributes)
     return 0;
 
   if (NULL_TREE != lookup_attribute ("progmem", TYPE_ATTRIBUTES (a)))
-    return 1;
+    return -1;
   
   return 0;
 }
 
+
+/* Scan type TYP for pointer references to address space ASn.
+   Return ADDR_SPACE_GENERIC (i.e. 0) if all pointers targeting
+   the AS are also declared to be CONST.
+   Otherwise, return the respective addres space, i.e. a value != 0.  */
+   
+static addr_space_t
+avr_nonconst_pointer_addrspace (tree typ)
+{
+  while (ARRAY_TYPE == TREE_CODE (typ))
+    typ = TREE_TYPE (typ);
+
+  if (POINTER_TYPE_P (typ))
+    {
+      tree target = TREE_TYPE (typ);
+
+      /* Pointer to function: Test the function's return type.  */
+      
+      if (FUNCTION_TYPE == TREE_CODE (target))
+        return avr_nonconst_pointer_addrspace (TREE_TYPE (target));
+
+      /* "Ordinary" pointers... */
+
+      while (TREE_CODE (target) == ARRAY_TYPE)
+        target = TREE_TYPE (target);
+
+      if (!ADDR_SPACE_GENERIC_P (TYPE_ADDR_SPACE (target))
+          && !TYPE_READONLY (target))
+        {
+          /* Pointers to non-generic address space must be const.  */
+          
+          return TYPE_ADDR_SPACE (target);
+        }
+
+      /* Scan pointer's target type.  */
+      
+      return avr_nonconst_pointer_addrspace (target);
+    }
+
+  return ADDR_SPACE_GENERIC;
+}
+
+
+/* Sanity check NODE so that all pointers targeting address space AS1
+   go along with CONST qualifier.  Writing to this address space should
+   be detected and complained about as early as possible.  */
+
+static bool
+avr_pgm_check_var_decl (tree node)
+{
+  const char *reason = NULL;
+  
+  addr_space_t as = ADDR_SPACE_GENERIC;
+
+  gcc_assert (as == 0);
+  
+  if (avr_log.progmem)
+    avr_edump ("%?: %t\n", node);
+  
+  switch (TREE_CODE (node))
+    {
+    default:
+      break;
+
+    case VAR_DECL:
+      if (as = avr_nonconst_pointer_addrspace (TREE_TYPE (node)), as)
+        reason = "variable";
+      break;
+
+    case PARM_DECL:
+      if (as = avr_nonconst_pointer_addrspace (TREE_TYPE (node)), as)
+        reason = "function parameter";
+      break;
+        
+    case FIELD_DECL:
+      if (as = avr_nonconst_pointer_addrspace (TREE_TYPE (node)), as)
+        reason = "structure field";
+      break;
+        
+    case FUNCTION_DECL:
+      if (as = avr_nonconst_pointer_addrspace (TREE_TYPE (TREE_TYPE (node))), as)
+        reason = "return type of function";
+      break;
+
+    case POINTER_TYPE:
+      if (as = avr_nonconst_pointer_addrspace (node), as)
+        reason = "pointer";
+      break;
+    }
+
+  if (reason)
+    {
+      if (TYPE_P (node))
+        error ("pointer targeting address space %qs must be const in %qT",
+               c_addr_space_name (as), node);
+      else
+        error ("pointer targeting address space %qs must be const in %s %q+D",
+               c_addr_space_name (as), reason, node);
+    }
+
+  return reason == NULL;
+}
+
+
 /* Add the section attribute if the variable is in progmem.  */
 
 static void
 avr_insert_attributes (tree node, tree *attributes)
 {
+  avr_pgm_check_var_decl (node);
+
   if (TREE_CODE (node) == VAR_DECL
       && (TREE_STATIC (node) || DECL_EXTERNAL (node))
       && avr_progmem_p (node, *attributes))
@@ -6299,11 +7007,20 @@ avr_insert_attributes (tree node, tree *attributes)
       if (error_mark_node == node0)
         return;
       
-      if (!TYPE_READONLY (node0))
+      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))
+            reason = c_addr_space_name (as);
+          
+          if (avr_log.progmem)
+            avr_edump ("\n%?: %t\n%t\n", node, node0);
+          
           error ("variable %q+D must be const in order to be put into"
-                 " read-only section by means of %<__attribute__((progmem))%>",
-                 node);
+                 " read-only section by means of %qs", node, reason);
         }
     }
 }
@@ -6503,6 +7220,7 @@ avr_section_type_flags (tree decl, const char *name, int reloc)
       && avr_progmem_p (decl, DECL_ATTRIBUTES (decl)))
     {
       flags &= ~SECTION_WRITE;
+      flags &= ~SECTION_BSS;
       flags |= AVR_SECTION_PROGMEM;
     }
   
@@ -6578,9 +7296,11 @@ avr_file_start (void)
   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);
+  fprintf (asm_out_file,
+           "__SREG__ = 0x3f\n"
+           "__SP_H__ = 0x3e\n"
+           "__SP_L__ = 0x3d\n"
+           "__RAMPZ__ = 0x%02x\n", RAMPZ_ADDR - 0x20);
   
   fputs ("__tmp_reg__ = 0\n" 
          "__zero_reg__ = 1\n", asm_out_file);
@@ -8090,10 +8810,15 @@ avr_hard_regno_mode_ok (int regno, enum machine_mode mode)
 
 reg_class_t
 avr_mode_code_base_reg_class (enum machine_mode mode ATTRIBUTE_UNUSED,
-                              addr_space_t as ATTRIBUTE_UNUSED,
+                              addr_space_t as,
                               RTX_CODE outer_code,
                               RTX_CODE index_code ATTRIBUTE_UNUSED)
 {
+  if (!ADDR_SPACE_GENERIC_P (as))
+    {
+      return POINTER_Z_REGS;
+    }
+ 
   if (!avr_strict_X)
     return reload_completed ? BASE_POINTER_REGS : POINTER_REGS;
 
@@ -8112,6 +8837,27 @@ avr_regno_mode_code_ok_for_base_p (int regno,
 {
   bool ok = false;
   
+  if (!ADDR_SPACE_GENERIC_P (as))
+    {
+      if (regno < FIRST_PSEUDO_REGISTER
+          && regno == REG_Z)
+        {
+          return true;
+        }
+      
+      if (reg_renumber)
+        {
+          regno = reg_renumber[regno];
+          
+          if (regno == REG_Z)
+            {
+              return true;
+            }
+        }
+      
+      return false;
+    }
+
   if (regno < FIRST_PSEUDO_REGISTER
       && (regno == REG_X
           || regno == REG_Y
@@ -8651,6 +9397,268 @@ avr_case_values_threshold (void)
   return (!AVR_HAVE_JMP_CALL || TARGET_CALL_PROLOGUES) ? 8 : 17;
 }
 
+
+/* Implement `TARGET_ADDR_SPACE_ADDRESS_MODE'.  */
+
+static enum machine_mode
+avr_addr_space_address_mode (addr_space_t as ATTRIBUTE_UNUSED)
+{
+  return as == ADDR_SPACE_PGMX ? PSImode : Pmode;
+}
+
+
+/* Implement `TARGET_ADDR_SPACE_POINTER_MODE'.  */
+
+static enum machine_mode
+avr_addr_space_pointer_mode (addr_space_t as ATTRIBUTE_UNUSED)
+{
+  return as == ADDR_SPACE_PGMX ? PSImode : Pmode;
+}
+
+
+/* Helper for following function.  */
+
+static bool
+avr_reg_ok_for_pgm_addr (rtx reg, bool strict)
+{
+  gcc_assert (REG_P (reg));
+
+  if (strict)
+    {
+      return REGNO (reg) == REG_Z;
+    }
+  
+  /* Avoid combine to propagate hard regs.  */
+  
+  if (can_create_pseude_p()
+      && REGNO (reg) < REG_Z)
+    {
+      return false;
+    }
+  
+  return true;
+}
+
+
+/* Implement `TARGET_ADDR_SPACE_LEGITIMATE_ADDRESS_P'.  */
+
+static bool
+avr_addr_space_legitimate_address_p (enum machine_mode mode, rtx x,
+                                     bool strict, addr_space_t as)
+{
+  bool ok = false;
+
+  switch (as)
+    {
+    default:
+      gcc_unreachable();
+      
+    case ADDR_SPACE_GENERIC:
+      return avr_legitimate_address_p (mode, x, strict);
+
+    case ADDR_SPACE_PGM:
+    case ADDR_SPACE_PGM1:
+    case ADDR_SPACE_PGM2:
+    case ADDR_SPACE_PGM3:
+    case ADDR_SPACE_PGM4:
+    case ADDR_SPACE_PGM5:
+
+      switch (GET_CODE (x))
+        {
+        case REG:
+          ok = avr_reg_ok_for_pgm_addr (x, strict);
+          break;
+          
+        case POST_INC:
+          ok = avr_reg_ok_for_pgm_addr (XEXP (x, 0), strict);
+          break;
+          
+        default:
+          break;
+        }
+
+      break; /* PGM */
+      
+    case ADDR_SPACE_PGMX:
+      if (REG_P (x))
+        ok = (!strict
+              && can_create_pseudo_p());
+
+      if (LO_SUM == GET_CODE (x))
+        {
+          rtx hi = XEXP (x, 0);
+          rtx lo = XEXP (x, 1);
+
+          ok = (REG_P (hi)
+                && (!strict || REGNO (hi) < FIRST_PSEUDO_REGISTER)
+                && REG_P (lo)
+                && REGNO (lo) == REG_Z);
+        }
+      
+      break; /* PGMX */
+    }
+
+  if (avr_log.legitimate_address_p)
+    {
+      avr_edump ("\n%?: ret=%b, mode=%m strict=%d "
+                 "reload_completed=%d reload_in_progress=%d %s:",
+                 ok, mode, strict, reload_completed, reload_in_progress,
+                 reg_renumber ? "(reg_renumber)" : "");
+      
+      if (GET_CODE (x) == PLUS
+          && REG_P (XEXP (x, 0))
+          && CONST_INT_P (XEXP (x, 1))
+          && IN_RANGE (INTVAL (XEXP (x, 1)), 0, MAX_LD_OFFSET (mode))
+          && reg_renumber)
+        {
+          avr_edump ("(r%d ---> r%d)", REGNO (XEXP (x, 0)),
+                     true_regnum (XEXP (x, 0)));
+        }
+      
+      avr_edump ("\n%r\n", x);
+    }
+
+  return ok;
+}
+
+
+/* Implement `TARGET_ADDR_SPACE_LEGITIMIZE_ADDRESS'.  */
+
+static rtx
+avr_addr_space_legitimize_address (rtx x, rtx old_x,
+                                   enum machine_mode mode, addr_space_t as)
+{
+  if (ADDR_SPACE_GENERIC_P (as))
+    return avr_legitimize_address (x, old_x, mode);
+
+  if (avr_log.legitimize_address)
+    {
+      avr_edump ("\n%?: mode=%m\n %r\n", mode, old_x);
+    }
+
+  return old_x;
+}
+
+
+/* Implement `TARGET_ADDR_SPACE_CONVERT'.  */
+
+static rtx
+avr_addr_space_convert (rtx src, tree type_from, tree type_to)
+{
+  addr_space_t as_from = TYPE_ADDR_SPACE (TREE_TYPE (type_from));
+  addr_space_t as_to = TYPE_ADDR_SPACE (TREE_TYPE (type_to));
+
+  if (avr_log.progmem)
+    avr_edump ("\n%!: op = %r\nfrom = %t\nto = %t\n",
+               src, type_from, type_to);
+
+  if (as_from != ADDR_SPACE_PGMX
+      && as_to == ADDR_SPACE_PGMX)
+    {
+      src = force_reg (Pmode, src);
+      
+      if (ADDR_SPACE_GENERIC_P (as_from)
+          || as_from == ADDR_SPACE_PGM)
+        {
+          return gen_rtx_ZERO_EXTEND (PSImode, src);
+        }
+      else
+        {
+          int hh8 = avr_pgm_segment (as_from) << 16;
+          src = SET_SRC (gen_n_extendhipsi2 (src, src, GEN_INT (hh8)));
+        }
+    }
+  
+  return src;
+}
+
+
+/* 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)
+{
+  if (subset == ADDR_SPACE_PGMX
+      && superset != ADDR_SPACE_PGMX)
+    {
+      return false;
+    }
+  
+  return true;
+}
+
+
+/* Helper function for the MOVMODE move expanders in the machine description.
+   Return true:   The expansion of the move is complete.
+   Return false:  The move still needs expansion.  */
+
+bool
+avr_emit_move (rtx *xop)
+{
+  rtx (*fun)(rtx,rtx);
+  rtx dest = xop[0];
+  rtx src  = xop[1]; 
+  enum machine_mode mode = GET_MODE (dest);
+  
+  /* One of the operands has to be in a register.  */
+  
+  if (!register_operand (dest, mode)
+      && !(register_operand (src, mode)
+           || src == CONST0_RTX (mode)))
+    {
+      src = xop[1] = copy_to_mode_reg (mode, src);
+    }
+  
+  if (avr_mem_pgm_p (dest))
+      return true;
+
+  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));
+
+      switch (mode)
+        {
+        default: gcc_unreachable();
+        case QImode:  fun = gen_xload8_A; break;
+        case HImode:  fun = gen_xloadhi_A; break;
+        case PSImode: fun = gen_xloadpsi_A; break;
+        case SImode:  fun = gen_xloadsi_A; break;
+        case SFmode:  fun = gen_xloadsf_A; break;
+        }
+      
+      emit_insn (fun (dest, src));
+
+      return true;
+    }
+
+  if (GET_MODE_SIZE (mode) > 2
+      && avr_current_arch->n_segments == 1
+      && !AVR_HAVE_LPMX
+      && avr_mem_pgm_p (src))
+    {
+      /* For the small devices, do loads per libgcc call.  */
+
+      switch (mode)
+        {
+        default: gcc_unreachable();
+        case PSImode: fun = gen_loadpsi_libgcc; break;
+        case SImode:  fun = gen_loadsi_libgcc; break;
+        case SFmode:  fun = gen_loadsf_libgcc; break;
+        }
+      
+      emit_insn (fun (dest, src));
+
+      return true;
+    }
+
+  return false;
+}
+
+
 /* Helper for __builtin_avr_delay_cycles */
 
 static void
diff --git a/config/avr/avr.h b/config/avr/avr.h
index 7a3bc73..a80b554 100644
--- a/config/avr/avr.h
+++ b/config/avr/avr.h
@@ -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)
@@ -391,6 +396,20 @@ typedef struct avr_args {
 
 #define NO_FUNCTION_CSE
 
+
+#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 {                                                                  \
+    avr_register_target_pragmas();                                      \
+  }while (0);
+
 #define TEXT_SECTION_ASM_OP "\t.text"
 
 #define DATA_SECTION_ASM_OP "\t.data"
diff --git a/config/avr/avr.md b/config/avr/avr.md
index 12e9485..3656eef 100644
--- a/config/avr/avr.md
+++ b/config/avr/avr.md
@@ -42,8 +42,10 @@
    (REG_Z	30)
    (REG_W	24)
    (REG_SP	32)
+   (LPM_REGNO	0)	; implicit target register of LPM
    (TMP_REGNO	0)	; temporary register r0
    (ZERO_REGNO	1)	; zero register r1
+   ;; register holding the address' high part when loading
    
    (SREG_ADDR   0x5F)
    (RAMPZ_ADDR  0x5B)
@@ -127,6 +129,7 @@
   "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,
    ashlqi, ashrqi, lshrqi,
    ashlhi, ashrhi, lshrhi,
    ashlsi, ashrsi, lshrsi,
@@ -315,6 +318,175 @@
   "")
 
 ;;========================================================================
+;; Move stuff around
+
+(define_expand "load<mode>_libgcc"
+  [(set (reg:HI REG_Z)
+        (match_dup 2))
+   (parallel [(set (reg:MOVMODE 22)
+                   (match_operand:MOVMODE 1 "memory_operand" ""))
+              (use (const_int 0))])
+   (set (match_operand:MOVMODE 0 "register_operand" "")
+        (reg:MOVMODE 22))]
+  "GET_MODE_SIZE (<MODE>mode) > 2
+   && !AVR_HAVE_LPMX"
+  {
+    operands[2] = XEXP (operands[1], 0);
+    operands[1] = replace_equiv_address (operands[1], gen_rtx_REG (Pmode, REG_Z));
+    set_mem_addr_space (operands[1], ADDR_SPACE_PGM);
+  })
+    
+(define_insn "*load.<mode>.libgcc"
+  [(set (reg:MOVMODE 22)
+        (mem:MOVMODE (reg:HI REG_Z)))
+   (use (const_int 0))]
+  "GET_MODE_SIZE (<MODE>mode) > 2
+   && !AVR_HAVE_LPMX"
+  {
+    rtx x_bytes = GEN_INT (GET_MODE_SIZE (<MODE>mode));
+    output_asm_insn ("%~call __load_%0", &x_bytes);
+    return "";
+  }
+  [(set_attr "length" "2")
+   (set_attr "adjust_len" "call")
+   (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(); }
+  ""
+  [(set (reg:HI REG_Z)
+        (match_dup 4))
+   (set (match_dup 2)
+        (match_dup 3))
+   ;; xload8
+   (set (match_dup 0)
+        (mem:QI (lo_sum:PSI (match_dup 2)
+                            (reg:HI REG_Z))))]
+  {
+    rtx addr = XEXP (operands[1], 0);
+    operands[2] = gen_reg_rtx (QImode);
+    operands[3] = simplify_gen_subreg (QImode, addr, PSImode, 2);
+    operands[4] = simplify_gen_subreg (Pmode, addr, PSImode, 0);
+  })
+
+(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(); }
+  ""
+  [(set (reg:HI REG_Z)
+        (match_dup 4))
+   (set (match_dup 2)
+        (match_dup 3))
+   ;; *xload.<mode>
+   ;; *xload.<mode>.libgcc
+   (parallel [(set (match_dup 5)
+                   (mem:MOVMODE (lo_sum:PSI (match_dup 2)
+                                            (reg:HI REG_Z))))
+              (clobber (match_dup 6))
+              (clobber (reg:HI REG_Z))])
+   ;; no-op move in the case of inlined code
+   (set (match_dup 0)
+        (match_dup 5))]
+  {
+    rtx addr = XEXP (operands[1], 0);
+
+    operands[3] = simplify_gen_subreg (QImode, addr, PSImode, 2);
+    operands[4] = simplify_gen_subreg (Pmode, addr, PSImode, 0);
+
+    if (avr_xload_libgcc_p (<MODE>mode))
+      {
+        operands[2] = gen_rtx_REG (QImode, 21);
+        operands[5] = gen_rtx_REG (<MODE>mode, 22);
+        operands[6] = operands[2];
+      }
+    else
+      {
+        if (avr_current_arch->n_segments == 1)
+          {
+            operands[2] = gen_rtx_REG (QImode, TMP_REGNO);
+            operands[3] = operands[2];
+          }
+        else
+          {
+            operands[2] = gen_reg_rtx (QImode);
+          }
+
+        operands[5] = operands[0];
+        operands[6] = gen_rtx_SCRATCH (HImode);
+      }
+  })
+
+;; Move value from address space pgmx to a register
+;; These insns must be prior to respective generic move insn.
+
+(define_insn "*xload.qi"
+  [(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"
 ;; "movhi"
@@ -327,13 +499,8 @@
         (match_operand:MOVMODE 1 "general_operand" ""))]
   ""
   {
-    /* One of the ops has to be in a register.  */
-    if (!register_operand (operands[0], <MODE>mode)
-        && !(register_operand (operands[1], <MODE>mode)
-             || CONST0_RTX (<MODE>mode) == operands[1]))
-      {
-        operands[1] = copy_to_mode_reg (<MODE>mode, operands[1]);
-      }
+    if (avr_emit_move (operands))
+      DONE;
   })
 
 ;;========================================================================
@@ -346,11 +513,14 @@
 ;; so this may still be a win for registers live across function calls.
 
 (define_insn "movqi_insn"
-  [(set (match_operand:QI 0 "nonimmediate_operand" "=r,d,Qm,r,q,r,*r")
-	(match_operand:QI 1 "general_operand"       "rL,i,rL,Qm,r,q,i"))]
-  "(register_operand (operands[0],QImode)
-    || register_operand (operands[1], QImode) || const0_rtx == operands[1])"
-  "* return output_movqi (insn, operands, NULL);"
+  [(set (match_operand:QI 0 "nonimmediate_operand" "=r ,d,Qm,r ,q,r,*r")
+        (match_operand:QI 1 "nox_general_operand"   "rL,i,rL,Qm,r,q,i"))]
+  "register_operand (operands[0], QImode)
+   || register_operand (operands[1], QImode)
+   || const0_rtx == operands[1]"
+  {
+    return output_movqi (insn, operands, NULL);
+  }
   [(set_attr "length" "1,1,5,5,1,1,4")
    (set_attr "adjust_len" "mov8")
    (set_attr "cc" "none,none,clobber,clobber,none,none,clobber")])
@@ -425,11 +595,14 @@
    (set_attr "cc" "none")])
 
 (define_insn "*movhi"
-  [(set (match_operand:HI 0 "nonimmediate_operand" "=r,r,r,m,d,*r,q,r")
-        (match_operand:HI 1 "general_operand"       "r,L,m,rL,i,i,r,q"))]
-  "(register_operand (operands[0],HImode)
-    || register_operand (operands[1],HImode) || const0_rtx == operands[1])"
-  "* return output_movhi (insn, operands, NULL);"
+  [(set (match_operand:HI 0 "nonimmediate_operand" "=r,r,r,m ,d,*r,q,r")
+        (match_operand:HI 1 "nox_general_operand"   "r,L,m,rL,i,i ,r,q"))]
+  "register_operand (operands[0], HImode)
+   || register_operand (operands[1], HImode)
+   || const0_rtx == operands[1]"
+  {
+    return output_movhi (insn, operands, NULL);
+  }
   [(set_attr "length" "2,2,6,7,2,6,5,2")
    (set_attr "adjust_len" "mov16")
    (set_attr "cc" "none,clobber,clobber,clobber,none,clobber,none,none")])
@@ -462,6 +635,40 @@
     operands[5] = gen_rtx_REG (HImode, REGNO (operands[3]));
   })
 
+;; For LPM loads from AS1 we split 
+;;    R = *Z
+;; to
+;;    R = *Z++
+;;    Z = Z - sizeof (R)
+;;
+;; so that the second instruction can be optimized out.
+
+(define_split ; "split-lpmx"
+  [(set (match_operand:HISI 0 "register_operand" "")
+        (match_operand:HISI 1 "memory_operand" ""))]
+  "reload_completed
+   && AVR_HAVE_LPMX"
+  [(set (match_dup 0)
+        (match_dup 2))
+   (set (match_dup 3)
+        (plus:HI (match_dup 3)
+                 (match_dup 4)))]
+  {
+     rtx addr = XEXP (operands[1], 0);
+
+     if (ADDR_SPACE_PGM != MEM_ADDR_SPACE (operands[1])
+         || !REG_P (addr)
+         || reg_overlap_mentioned_p (addr, operands[0]))
+       {
+         FAIL;
+       }
+
+    operands[2] = replace_equiv_address (operands[1],
+                                         gen_rtx_POST_INC (Pmode, addr));
+    operands[3] = addr;
+    operands[4] = gen_int_mode (-GET_MODE_SIZE (<MODE>mode), HImode);
+  })
+
 ;;==========================================================================
 ;; xpointer move (24 bit)
   
@@ -492,7 +699,7 @@
 
 (define_insn "*movpsi"
   [(set (match_operand:PSI 0 "nonimmediate_operand" "=r,r,r ,Qm,!d,r")
-        (match_operand:PSI 1 "general_operand"       "r,L,Qm,rL,i ,i"))]
+        (match_operand:PSI 1 "nox_general_operand"   "r,L,Qm,rL,i ,i"))]
   "register_operand (operands[0], PSImode)
    || register_operand (operands[1], PSImode)
    || const0_rtx == operands[1]"
@@ -532,10 +739,11 @@
 
 
 (define_insn "*movsi"
-  [(set (match_operand:SI 0 "nonimmediate_operand" "=r,r,r,Qm,!d,r")
-        (match_operand:SI 1 "general_operand"       "r,L,Qm,rL,i,i"))]
-  "(register_operand (operands[0],SImode)
-    || register_operand (operands[1],SImode) || const0_rtx == operands[1])"
+  [(set (match_operand:SI 0 "nonimmediate_operand" "=r,r,r ,Qm,!d,r")
+        (match_operand:SI 1 "nox_general_operand"   "r,L,Qm,rL,i ,i"))]
+  "register_operand (operands[0], SImode)
+   || register_operand (operands[1], SImode)
+   || const0_rtx == operands[1]"
   {
     return output_movsisf (insn, operands, NULL);
   }
@@ -547,8 +755,8 @@
 ;; move floating point numbers (32 bit)
 
 (define_insn "*movsf"
-  [(set (match_operand:SF 0 "nonimmediate_operand" "=r,r,r,Qm,!d,r")
-        (match_operand:SF 1 "general_operand"       "r,G,Qm,rG,F,F"))]
+  [(set (match_operand:SF 0 "nonimmediate_operand" "=r,r,r ,Qm,!d,r")
+        (match_operand:SF 1 "nox_general_operand"   "r,G,Qm,rG,F ,F"))]
   "register_operand (operands[0], SFmode)
    || register_operand (operands[1], SFmode)
    || operands[1] == CONST0_RTX (SFmode)"
@@ -599,7 +807,7 @@
   enum machine_mode mode;
   rtx label = gen_label_rtx ();
   rtx loop_reg;
-  rtx jump;
+  rtx jump, src;
 
   /* Copy pointers into new psuedos - they will be changed.  */
   rtx addr0 = copy_to_mode_reg (Pmode, XEXP (operands[0], 0));
@@ -608,6 +816,9 @@
   /* 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)
     FAIL;
 
@@ -628,7 +839,9 @@
   emit_label (label);
 
   /* Move one byte into scratch and inc pointer.  */
-  emit_move_insn (tmp_reg_rtx, gen_rtx_MEM (QImode, addr1));
+  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.  */
@@ -2866,10 +3079,8 @@
 
 (define_expand "ashlqi3"
   [(set (match_operand:QI 0 "register_operand"            "")
-	(ashift:QI (match_operand:QI 1 "register_operand" "")
-		   (match_operand:QI 2 "general_operand"  "")))]
-  ""
-  "")
+        (ashift:QI (match_operand:QI 1 "register_operand" "")
+                   (match_operand:QI 2 "nop_general_operand" "")))])
 
 (define_split ; ashlqi3_const4
   [(set (match_operand:QI 0 "d_register_operand" "")
@@ -2901,21 +3112,25 @@
   "")
 
 (define_insn "*ashlqi3"
-  [(set (match_operand:QI 0 "register_operand"           "=r,r,r,r,!d,r,r")
-	(ashift:QI (match_operand:QI 1 "register_operand" "0,0,0,0,0,0,0")
-		   (match_operand:QI 2 "general_operand"  "r,L,P,K,n,n,Qm")))]
+  [(set (match_operand:QI 0 "register_operand"              "=r,r,r,r,!d,r,r")
+        (ashift:QI (match_operand:QI 1 "register_operand"    "0,0,0,0,0 ,0,0")
+                   (match_operand:QI 2 "nop_general_operand" "r,L,P,K,n ,n,Qm")))]
   ""
-  "* return ashlqi3_out (insn, operands, NULL);"
+  {
+    return ashlqi3_out (insn, operands, NULL);
+  }
   [(set_attr "length" "5,0,1,2,4,6,9")
    (set_attr "adjust_len" "ashlqi")
    (set_attr "cc" "clobber,none,set_czn,set_czn,set_czn,set_czn,clobber")])
 
 (define_insn "ashlhi3"
-  [(set (match_operand:HI 0 "register_operand"           "=r,r,r,r,r,r,r")
-	(ashift:HI (match_operand:HI 1 "register_operand" "0,0,0,r,0,0,0")
-		   (match_operand:QI 2 "general_operand"  "r,L,P,O,K,n,Qm")))]
+  [(set (match_operand:HI 0 "register_operand"              "=r,r,r,r,r,r,r")
+        (ashift:HI (match_operand:HI 1 "register_operand"    "0,0,0,r,0,0,0")
+                   (match_operand:QI 2 "nop_general_operand" "r,L,P,O,K,n,Qm")))]
   ""
-  "* return ashlhi3_out (insn, operands, NULL);"
+  {
+    return ashlhi3_out (insn, operands, NULL);
+  }
   [(set_attr "length" "6,0,2,2,4,10,10")
    (set_attr "adjust_len" "ashlhi")
    (set_attr "cc" "clobber,none,set_n,clobber,set_n,clobber,clobber")])
@@ -3003,11 +3218,13 @@
 
 
 (define_insn "ashlsi3"
-  [(set (match_operand:SI 0 "register_operand"           "=r,r,r,r,r,r,r")
-	(ashift:SI (match_operand:SI 1 "register_operand" "0,0,0,r,0,0,0")
-		   (match_operand:QI 2 "general_operand"  "r,L,P,O,K,n,Qm")))]
+  [(set (match_operand:SI 0 "register_operand"              "=r,r,r,r,r,r,r")
+        (ashift:SI (match_operand:SI 1 "register_operand"    "0,0,0,r,0,0,0")
+                   (match_operand:QI 2 "nop_general_operand" "r,L,P,O,K,n,Qm")))]
   ""
-  "* return ashlsi3_out (insn, operands, NULL);"
+  {
+    return ashlsi3_out (insn, operands, NULL);
+  }
   [(set_attr "length" "8,0,4,4,8,10,12")
    (set_attr "adjust_len" "ashlsi")
    (set_attr "cc" "clobber,none,set_n,clobber,set_n,clobber,clobber")])
@@ -3061,11 +3278,13 @@
 
 (define_insn "*ashlhi3_const"
   [(set (match_operand:HI 0 "register_operand"            "=r,r,r,r,r")
-	(ashift:HI (match_operand:HI 1 "register_operand"  "0,0,r,0,0")
-		   (match_operand:QI 2 "const_int_operand" "L,P,O,K,n")))
-   (clobber (match_scratch:QI 3 "=X,X,X,X,&d"))]
+        (ashift:HI (match_operand:HI 1 "register_operand"  "0,0,r,0,0")
+                   (match_operand:QI 2 "const_int_operand" "L,P,O,K,n")))
+   (clobber (match_scratch:QI 3                           "=X,X,X,X,&d"))]
   "reload_completed"
-  "* return ashlhi3_out (insn, operands, NULL);"
+  {
+    return ashlhi3_out (insn, operands, NULL);
+  }
   [(set_attr "length" "0,2,2,4,10")
    (set_attr "adjust_len" "ashlhi")
    (set_attr "cc" "none,set_n,clobber,set_n,clobber")])
@@ -3082,11 +3301,13 @@
 
 (define_insn "*ashlsi3_const"
   [(set (match_operand:SI 0 "register_operand"            "=r,r,r,r")
-	(ashift:SI (match_operand:SI 1 "register_operand"  "0,0,r,0")
-		   (match_operand:QI 2 "const_int_operand" "L,P,O,n")))
-   (clobber (match_scratch:QI 3 "=X,X,X,&d"))]
+        (ashift:SI (match_operand:SI 1 "register_operand"  "0,0,r,0")
+                   (match_operand:QI 2 "const_int_operand" "L,P,O,n")))
+   (clobber (match_scratch:QI 3                           "=X,X,X,&d"))]
   "reload_completed"
-  "* return ashlsi3_out (insn, operands, NULL);"
+  {
+    return ashlsi3_out (insn, operands, NULL);
+  }
   [(set_attr "length" "0,4,4,10")
    (set_attr "adjust_len" "ashlsi")
    (set_attr "cc" "none,set_n,clobber,clobber")])
@@ -3107,21 +3328,25 @@
 ;; arithmetic shift right
 
 (define_insn "ashrqi3"
-  [(set (match_operand:QI 0 "register_operand"             "=r,r,r,r,r          ,r      ,r")
-        (ashiftrt:QI (match_operand:QI 1 "register_operand" "0,0,0,0,0          ,0      ,0")
-                     (match_operand:QI 2 "general_operand"  "r,L,P,K,C03 C04 C05,C06 C07,Qm")))]
+  [(set (match_operand:QI 0 "register_operand"                "=r,r,r,r,r          ,r      ,r")
+        (ashiftrt:QI (match_operand:QI 1 "register_operand"    "0,0,0,0,0          ,0      ,0")
+                     (match_operand:QI 2 "nop_general_operand" "r,L,P,K,C03 C04 C05,C06 C07,Qm")))]
   ""
-  "* return ashrqi3_out (insn, operands, NULL);"
+  {
+    return ashrqi3_out (insn, operands, NULL);
+  }
   [(set_attr "length" "5,0,1,2,5,4,9")
    (set_attr "adjust_len" "ashrqi")
    (set_attr "cc" "clobber,none,set_czn,set_czn,set_czn,clobber,clobber")])
 
 (define_insn "ashrhi3"
-  [(set (match_operand:HI 0 "register_operand"             "=r,r,r,r,r,r,r")
-	(ashiftrt:HI (match_operand:HI 1 "register_operand" "0,0,0,r,0,0,0")
-		     (match_operand:QI 2 "general_operand"  "r,L,P,O,K,n,Qm")))]
+  [(set (match_operand:HI 0 "register_operand"                "=r,r,r,r,r,r,r")
+        (ashiftrt:HI (match_operand:HI 1 "register_operand"    "0,0,0,r,0,0,0")
+                     (match_operand:QI 2 "nop_general_operand" "r,L,P,O,K,n,Qm")))]
   ""
-  "* return ashrhi3_out (insn, operands, NULL);"
+  {
+    return ashrhi3_out (insn, operands, NULL);
+  }
   [(set_attr "length" "6,0,2,4,4,10,10")
    (set_attr "adjust_len" "ashrhi")
    (set_attr "cc" "clobber,none,clobber,set_n,clobber,clobber,clobber")])
@@ -3139,11 +3364,13 @@
    (set_attr "cc" "clobber")])
 
 (define_insn "ashrsi3"
-  [(set (match_operand:SI 0 "register_operand"             "=r,r,r,r,r,r,r")
-	(ashiftrt:SI (match_operand:SI 1 "register_operand" "0,0,0,r,0,0,0")
-		     (match_operand:QI 2 "general_operand"  "r,L,P,O,K,n,Qm")))]
+  [(set (match_operand:SI 0 "register_operand"                "=r,r,r,r,r,r,r")
+        (ashiftrt:SI (match_operand:SI 1 "register_operand"    "0,0,0,r,0,0,0")
+                     (match_operand:QI 2 "nop_general_operand" "r,L,P,O,K,n,Qm")))]
   ""
-  "* return ashrsi3_out (insn, operands, NULL);"
+  {
+    return ashrsi3_out (insn, operands, NULL);
+  }
   [(set_attr "length" "8,0,4,6,8,10,12")
    (set_attr "adjust_len" "ashrsi")
    (set_attr "cc" "clobber,none,clobber,set_n,clobber,clobber,clobber")])
@@ -3162,11 +3389,13 @@
 
 (define_insn "*ashrhi3_const"
   [(set (match_operand:HI 0 "register_operand"              "=r,r,r,r,r")
-	(ashiftrt:HI (match_operand:HI 1 "register_operand"  "0,0,r,0,0")
-		     (match_operand:QI 2 "const_int_operand" "L,P,O,K,n")))
-   (clobber (match_scratch:QI 3 "=X,X,X,X,&d"))]
+        (ashiftrt:HI (match_operand:HI 1 "register_operand"  "0,0,r,0,0")
+                     (match_operand:QI 2 "const_int_operand" "L,P,O,K,n")))
+   (clobber (match_scratch:QI 3                             "=X,X,X,X,&d"))]
   "reload_completed"
-  "* return ashrhi3_out (insn, operands, NULL);"
+  {
+    return ashrhi3_out (insn, operands, NULL);
+  }
   [(set_attr "length" "0,2,4,4,10")
    (set_attr "adjust_len" "ashrhi")
    (set_attr "cc" "none,clobber,set_n,clobber,clobber")])
@@ -3183,11 +3412,13 @@
 
 (define_insn "*ashrsi3_const"
   [(set (match_operand:SI 0 "register_operand"              "=r,r,r,r")
-	(ashiftrt:SI (match_operand:SI 1 "register_operand"  "0,0,r,0")
-		     (match_operand:QI 2 "const_int_operand" "L,P,O,n")))
-   (clobber (match_scratch:QI 3 "=X,X,X,&d"))]
+        (ashiftrt:SI (match_operand:SI 1 "register_operand"  "0,0,r,0")
+                     (match_operand:QI 2 "const_int_operand" "L,P,O,n")))
+   (clobber (match_scratch:QI 3                             "=X,X,X,&d"))]
   "reload_completed"
-  "* return ashrsi3_out (insn, operands, NULL);"
+  {
+    return ashrsi3_out (insn, operands, NULL);
+  }
   [(set_attr "length" "0,4,4,10")
    (set_attr "adjust_len" "ashrsi")
    (set_attr "cc" "none,clobber,set_n,clobber")])
@@ -3196,11 +3427,9 @@
 ;; logical shift right
 
 (define_expand "lshrqi3"
-  [(set (match_operand:QI 0 "register_operand"              "")
-	(lshiftrt:QI (match_operand:QI 1 "register_operand" "")
-		     (match_operand:QI 2 "general_operand"  "")))]
-  ""
-  "")
+  [(set (match_operand:QI 0 "register_operand" "")
+        (lshiftrt:QI (match_operand:QI 1 "register_operand" "")
+                     (match_operand:QI 2 "nop_general_operand" "")))])
 
 (define_split	; lshrqi3_const4
   [(set (match_operand:QI 0 "d_register_operand" "")
@@ -3232,21 +3461,25 @@
   "")
 
 (define_insn "*lshrqi3"
-  [(set (match_operand:QI 0 "register_operand"             "=r,r,r,r,!d,r,r")
-	(lshiftrt:QI (match_operand:QI 1 "register_operand" "0,0,0,0,0,0,0")
-		     (match_operand:QI 2 "general_operand"  "r,L,P,K,n,n,Qm")))]
+  [(set (match_operand:QI 0 "register_operand"                "=r,r,r,r,!d,r,r")
+        (lshiftrt:QI (match_operand:QI 1 "register_operand"    "0,0,0,0,0 ,0,0")
+                     (match_operand:QI 2 "nop_general_operand" "r,L,P,K,n ,n,Qm")))]
   ""
-  "* return lshrqi3_out (insn, operands, NULL);"
+  {
+    return lshrqi3_out (insn, operands, NULL);
+  }
   [(set_attr "length" "5,0,1,2,4,6,9")
    (set_attr "adjust_len" "lshrqi")
    (set_attr "cc" "clobber,none,set_czn,set_czn,set_czn,set_czn,clobber")])
 
 (define_insn "lshrhi3"
-  [(set (match_operand:HI 0 "register_operand"             "=r,r,r,r,r,r,r")
-	(lshiftrt:HI (match_operand:HI 1 "register_operand" "0,0,0,r,0,0,0")
-		     (match_operand:QI 2 "general_operand"  "r,L,P,O,K,n,Qm")))]
+  [(set (match_operand:HI 0 "register_operand"                "=r,r,r,r,r,r,r")
+        (lshiftrt:HI (match_operand:HI 1 "register_operand"    "0,0,0,r,0,0,0")
+                     (match_operand:QI 2 "nop_general_operand" "r,L,P,O,K,n,Qm")))]
   ""
-  "* return lshrhi3_out (insn, operands, NULL);"
+  {
+    return lshrhi3_out (insn, operands, NULL);
+  }
   [(set_attr "length" "6,0,2,2,4,10,10")
    (set_attr "adjust_len" "lshrhi")
    (set_attr "cc" "clobber,none,clobber,clobber,clobber,clobber,clobber")])
@@ -3264,11 +3497,13 @@
    (set_attr "cc" "clobber")])
 
 (define_insn "lshrsi3"
-  [(set (match_operand:SI 0 "register_operand"             "=r,r,r,r,r,r,r")
-	(lshiftrt:SI (match_operand:SI 1 "register_operand" "0,0,0,r,0,0,0")
-		     (match_operand:QI 2 "general_operand"  "r,L,P,O,K,n,Qm")))]
+  [(set (match_operand:SI 0 "register_operand"                "=r,r,r,r,r,r,r")
+        (lshiftrt:SI (match_operand:SI 1 "register_operand"    "0,0,0,r,0,0,0")
+                     (match_operand:QI 2 "nop_general_operand" "r,L,P,O,K,n,Qm")))]
   ""
-  "* return lshrsi3_out (insn, operands, NULL);"
+  {
+    return lshrsi3_out (insn, operands, NULL);
+  }
   [(set_attr "length" "8,0,4,4,8,10,12")
    (set_attr "adjust_len" "lshrsi")
    (set_attr "cc" "clobber,none,clobber,clobber,clobber,clobber,clobber")])
@@ -3322,11 +3557,13 @@
 
 (define_insn "*lshrhi3_const"
   [(set (match_operand:HI 0 "register_operand"              "=r,r,r,r,r")
-	(lshiftrt:HI (match_operand:HI 1 "register_operand"  "0,0,r,0,0")
-		     (match_operand:QI 2 "const_int_operand" "L,P,O,K,n")))
-   (clobber (match_scratch:QI 3 "=X,X,X,X,&d"))]
+        (lshiftrt:HI (match_operand:HI 1 "register_operand"  "0,0,r,0,0")
+                     (match_operand:QI 2 "const_int_operand" "L,P,O,K,n")))
+   (clobber (match_scratch:QI 3                             "=X,X,X,X,&d"))]
   "reload_completed"
-  "* return lshrhi3_out (insn, operands, NULL);"
+  {
+    return lshrhi3_out (insn, operands, NULL);
+  }
   [(set_attr "length" "0,2,2,4,10")
    (set_attr "adjust_len" "lshrhi")
    (set_attr "cc" "none,clobber,clobber,clobber,clobber")])
@@ -3343,11 +3580,13 @@
 
 (define_insn "*lshrsi3_const"
   [(set (match_operand:SI 0 "register_operand"              "=r,r,r,r")
-	(lshiftrt:SI (match_operand:SI 1 "register_operand"  "0,0,r,0")
-		     (match_operand:QI 2 "const_int_operand" "L,P,O,n")))
-   (clobber (match_scratch:QI 3 "=X,X,X,&d"))]
+        (lshiftrt:SI (match_operand:SI 1 "register_operand"  "0,0,r,0")
+                     (match_operand:QI 2 "const_int_operand" "L,P,O,n")))
+   (clobber (match_scratch:QI 3                             "=X,X,X,&d"))]
   "reload_completed"
-  "* return lshrsi3_out (insn, operands, NULL);"
+  {
+    return lshrsi3_out (insn, operands, NULL);
+  }
   [(set_attr "length" "0,4,4,10")
    (set_attr "adjust_len" "lshrsi")
    (set_attr "cc" "none,clobber,clobber,clobber")])
@@ -3605,6 +3844,21 @@
     operands[3] = simplify_gen_subreg (QImode, operands[0], PSImode, 2);
   })
 
+(define_insn_and_split "n_extendhipsi2"
+  [(set (match_operand:PSI 0 "register_operand"                         "=d")
+        (ior:PSI (zero_extend:PSI (match_operand:HI 1 "register_operand" "r"))
+                 (match_operand:PSI 2 "hh8_operand"                      "n")))]
+  ""
+  "#"
+  "reload_completed"
+  [(set (match_dup 3) (match_dup 1))
+   (set (match_dup 4) (match_dup 5))]
+  {
+    operands[3] = simplify_gen_subreg (HImode, operands[0], PSImode, 0);
+    operands[4] = simplify_gen_subreg (QImode, operands[0], PSImode, 2);
+    operands[5] = simplify_gen_subreg (QImode, operands[2], PSImode, 2);
+  })
+
 (define_insn_and_split "zero_extendhisi2"
   [(set (match_operand:SI 0 "register_operand"                               "=r")
         (zero_extend:SI (match_operand:HI 1 "combine_pseudo_register_operand" "r")))]
diff --git a/config/avr/predicates.md b/config/avr/predicates.md
index 55a25b8..f2a6a70 100644
--- a/config/avr/predicates.md
+++ b/config/avr/predicates.md
@@ -57,6 +57,22 @@
   (and (match_code "const_int")
        (match_test "IN_RANGE((INTVAL (op)), 0x20, (0x60 - GET_MODE_SIZE(mode)))")))
 
+;; Return 1 if OP is a general operand not in program memory
+(define_predicate "nop_general_operand"
+  (and (match_operand 0 "general_operand")
+       (not (match_test "avr_mem_pgm_p (op)"))))
+
+
+;; Return 1 if OP is a general operand not in extended program memory
+;; Moreover; deny loads from flash that are performed via libgcc call.
+(define_predicate "nox_general_operand"
+  (and (match_operand 0 "general_operand")
+       (not (match_test "avr_mem_pgmx_p (op)"))
+       (not (match_test "GET_MODE_SIZE (mode) > 2
+                         && avr_current_arch->n_segments == 1
+                         && !AVR_HAVE_LPMX
+                         && avr_mem_pgm_p (op)"))))
+
 ;; Return 1 if OP is the zero constant for MODE.
 (define_predicate "const0_operand"
   (and (match_code "const_int,const_double")
@@ -233,3 +249,8 @@
 (define_predicate "o16_operand"
   (and (match_code "const_int")
        (match_test "IN_RANGE (INTVAL (op), -(1<<16), -1)")))
+
+;; CONST_INT were ony sub-byte #2 may have non-zero value.
+(define_predicate "hh8_operand"
+  (and (match_code "const_int")
+       (match_test "IN_RANGE (INTVAL (op), 0x00010000, 0x00ff0000)")))
Index: ../libgcc/config/avr/lib1funcs.S
===================================================================
--- ../libgcc/config/avr/lib1funcs.S	(revision 180962)
+++ ../libgcc/config/avr/lib1funcs.S	(working copy)
@@ -1175,6 +1175,120 @@ DEFUN __tablejump_elpm__
 ENDF __tablejump_elpm__
 #endif /* defined (L_tablejump_elpm) */
 
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; 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_* && ELPM */
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Loading n bytes from Flash; n = 3,4
+;; R22... = Flash[Z]
+;; Clobbers: __tmp_reg__
+
+#if (defined (L_load_3)        \
+     || defined (L_load_4))    \
+    && !defined (__AVR_HAVE_LPMX__)
+
+;; Destination
+#define D0  22
+#define D1  D0+1
+#define D2  D0+2
+#define D3  D0+3
+
+.macro  .load dest, n
+    lpm
+    mov     \dest, r0
+.if \dest != D0+\n-1
+    adiw    r30, 1
+.else
+    sbiw    r30, \n-1
+.endif
+.endm
+
+#if defined (L_load_3)
+DEFUN __load_3
+    push  D3
+    XCALL __load_4
+    pop   D3
+    ret
+ENDF __load_3
+#endif /* L_load_3 */
+
+#if defined (L_load_4)
+DEFUN __load_4
+    .load D0, 4
+    .load D1, 4
+    .load D2, 4
+    .load D3, 4
+    ret
+ENDF __load_4
+#endif /* L_load_4 */
+
+#endif /* L_load_3 || L_load_3 */
+
 
 .section .text.libgcc.builtins, "ax", @progbits
 
Index: ../libgcc/config/avr/t-avr
===================================================================
--- ../libgcc/config/avr/t-avr	(revision 180962)
+++ ../libgcc/config/avr/t-avr	(working copy)
@@ -21,6 +21,8 @@ LIB1ASMFUNCS = \
 	_cleanup \
 	_tablejump \
 	_tablejump_elpm \
+	_load_3 _load_4 \
+	_xload_2 _xload_3 _xload_4 \
 	_copy_data \
 	_clear_bss \
 	_ctors \

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]