public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [Patch]: PR49868: Named address space support for AVR
@ 2011-10-06 15:16 Georg-Johann Lay
  2011-10-28 11:00 ` Georg-Johann Lay
  0 siblings, 1 reply; 11+ messages in thread
From: Georg-Johann Lay @ 2011-10-06 15:16 UTC (permalink / raw)
  To: gcc-patches
  Cc: Ulrich Weigand, Denis Chertykov, Eric Weddington, Joerg Wunsch,
	Anatoly Sokolov

[-- Attachment #1: Type: text/plain, Size: 4468 bytes --]

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++.

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.

Hint on how to proceed with this are appreciated, likewise I'd like to know if
such extension is not appropriate so that I can focus on other stuff or if
there are hints to improve the patch.

Thanks, Johann

	PR target/49868
	* config/avr/avr.h (ADDR_SPACE_PGM): New define for address space AS1.
	(REGISTER_TARGET_PRAGMAS): New define.
	(MODE_CODE_BASE_REG_CLASS): New define.
	(REGNO_MODE_CODE_OK_FOR_BASE_P): New define.
	(BASE_REG_CLASS): Remove define.
	(REGNO_OK_FOR_BASE_P): Remove define.
	(REG_OK_FOR_BASE_NOSTRICT_P): Remove define.
	(REG_OK_FOR_BASE_STRICT_P): Remove define.
	* config/avr/avr-protos.h (avr_mem_pgm_p): New prototype.
	(avr_register_target_pragmas): New prototype.
	(avr_mode_code_base_reg_class): New prototype.
	(avr_regno_mode_code_ok_for_base_p): 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 (avr_register_target_pragmas): New function.
	Register address space AS1 as "__pgm".
	* config/avr/avr.c: Include "c-family/c-common.h".
	(TARGET_LEGITIMATE_ADDRESS_P): Don't define this macro any more.
	(TARGET_LEGITIMIZE_ADDRESS): Don't define this macro any more.
	(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): New function.
	(avr_regno_mode_code_ok_for_base_p): New function.
	(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_reg_ok_for_addr): New static function.
	(avr_legitimate_address_p): Use it.
	(avr_asm_len): Return "" 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 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.

[-- Attachment #2: pgm-v6-avr.diff --]
[-- Type: text/x-patch, Size: 37393 bytes --]

Index: config/avr/avr.md
===================================================================
--- config/avr/avr.md	(revision 179594)
+++ config/avr/avr.md	(working copy)
@@ -42,6 +42,7 @@ (define_constants
    (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
    
@@ -289,11 +290,17 @@ (define_expand "movqi"
   [(set (match_operand:QI 0 "nonimmediate_operand" "")
 	(match_operand:QI 1 "general_operand" ""))]
   ""
-  "/* One of the ops has to be in a register.  */
-   if (!register_operand(operand0, QImode)
-       && ! (register_operand(operand1, QImode) || const0_rtx == operand1))
-       operands[1] = copy_to_mode_reg(QImode, operand1);
-  ")
+  {
+    if (avr_mem_pgm_p (operands[0]))
+      DONE;
+
+    /* One of the operands has to be in a register.  */
+    if (!register_operand (operands[0], QImode)
+        && !(register_operand (operands[1], QImode) || const0_rtx == operands[1]))
+      {
+        operands[1] = copy_to_mode_reg (QImode, operands[1]);
+      }
+  })
 
 (define_insn "*movqi"
   [(set (match_operand:QI 0 "nonimmediate_operand" "=r,d,Qm,r,q,r,*r")
@@ -336,15 +343,17 @@ (define_expand "movhi"
   [(set (match_operand:HI 0 "nonimmediate_operand" "")
         (match_operand:HI 1 "general_operand"       ""))]
   ""
-  "
-{
-   /* One of the ops has to be in a register.  */
-  if (!register_operand(operand0, HImode)
-      && !(register_operand(operand1, HImode) || const0_rtx == operands[1]))
-    {
-      operands[1] = copy_to_mode_reg(HImode, operand1);
-    }
-}")
+  {
+    if (avr_mem_pgm_p (operands[0]))
+      DONE;
+
+    /* One of the operands has to be in a register.  */
+    if (!register_operand (operands[0], HImode)
+        && !(register_operand (operands[1], HImode) || const0_rtx == operands[1]))
+      {
+        operands[1] = copy_to_mode_reg (HImode, operands[1]);
+      }
+  })
 
 (define_insn "movhi_sp_r_irq_off"
   [(set (match_operand:HI 0 "stack_register_operand" "=q")
@@ -429,6 +438,39 @@ (define_peephole2 ; movw_r
     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);
+  })
 ;;==========================================================================
 ;; move double word (32 bit)
 
@@ -436,15 +478,17 @@ (define_expand "movsi"
   [(set (match_operand:SI 0 "nonimmediate_operand" "")
         (match_operand:SI 1 "general_operand"  ""))]
   ""
-  "
-{
-  /* One of the ops has to be in a register.  */
-  if (!register_operand (operand0, SImode)
-      && !(register_operand (operand1, SImode) || const0_rtx == operand1))
-    {
-      operands[1] = copy_to_mode_reg (SImode, operand1);
-    }
-}")
+  {
+    if (avr_mem_pgm_p (operands[0]))
+      DONE;
+  
+    /* One of the operands has to be in a register.  */
+    if (!register_operand (operands[0], SImode)
+        && !(register_operand (operands[1], SImode) || const0_rtx == operands[1]))
+      {
+        operands[1] = copy_to_mode_reg (SImode, operands[1]);
+      }
+  })
 
 
 
@@ -492,15 +536,17 @@ (define_expand "movsf"
   [(set (match_operand:SF 0 "nonimmediate_operand" "")
         (match_operand:SF 1 "general_operand"  ""))]
   ""
-  "
-{
-  /* One of the ops has to be in a register.  */
-  if (!register_operand (operand1, SFmode)
-      && !register_operand (operand0, SFmode))
-    {
-      operands[1] = copy_to_mode_reg (SFmode, operand1);
-    }
-}")
+  {
+    if (avr_mem_pgm_p (operands[0]))
+      DONE;
+
+    /* One of the operands has to be in a register.  */
+    if (!register_operand (operands[1], SFmode)
+        && !register_operand (operands[0], SFmode))
+      {
+        operands[1] = copy_to_mode_reg (SFmode, operands[1]);
+      }
+  })
 
 (define_insn "*movsf"
   [(set (match_operand:SF 0 "nonimmediate_operand" "=r,r,r,Qm,!d,r")
@@ -555,7 +601,7 @@ (define_expand "movmemhi"
   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));
@@ -564,6 +610,9 @@ (define_expand "movmemhi"
   /* 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;
 
@@ -584,7 +633,9 @@ (define_expand "movmemhi"
   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.  */
Index: config/avr/avr-log.c
===================================================================
--- config/avr/avr-log.c	(revision 179594)
+++ config/avr/avr-log.c	(working copy)
@@ -318,12 +318,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
     }
Index: config/avr/avr-c.c
===================================================================
--- config/avr/avr-c.c	(revision 179594)
+++ config/avr/avr-c.c	(working copy)
@@ -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"
@@ -28,7 +29,15 @@
 #include "tree.h"
 #include "c-family/c-common.h"
 
-/* Not included in avr.c since this requires C front end.  */
+
+/* Implement `REGISTER_TARGET_PRAGMAS'.  */
+
+void
+avr_register_target_pragmas (void)
+{
+  c_register_addr_space ("__pgm", ADDR_SPACE_PGM);
+}
+
 
 /* Worker function for TARGET_CPU_CPP_BUILTINS.  */
 
Index: config/avr/avr-protos.h
===================================================================
--- config/avr/avr-protos.h	(revision 179594)
+++ config/avr/avr-protos.h	(working copy)
@@ -32,6 +32,7 @@ extern int avr_initial_elimination_offse
 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);
 
 #ifdef TREE_CODE
 extern void avr_asm_output_aligned_decl_common (FILE*, const_tree, const char*, unsigned HOST_WIDE_INT, unsigned int, bool);
@@ -106,8 +107,11 @@ extern int avr_simplify_comparison_p (en
 extern RTX_CODE avr_normalize_condition (RTX_CODE condition);
 extern void out_shift_with_cnt (const char *templ, rtx insn,
 				rtx operands[], int *len, int t_len);
+extern reg_class_t avr_mode_code_base_reg_class (enum machine_mode, addr_space_t, RTX_CODE, RTX_CODE);
+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);
 #endif /* RTX_CODE */
 
 #ifdef REAL_VALUE_TYPE
@@ -126,12 +130,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;
Index: config/avr/avr.c
===================================================================
--- config/avr/avr.c	(revision 179594)
+++ config/avr/avr.c	(working copy)
@@ -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"
@@ -69,14 +70,12 @@ static const char *ptrreg_to_str (int);
 static const char *cond_string (enum rtx_code);
 static int avr_num_arg_regs (enum machine_mode, const_tree);
 
-static rtx avr_legitimize_address (rtx, rtx, enum machine_mode);
 static tree avr_handle_progmem_attribute (tree *, tree, tree, int, bool *);
 static tree avr_handle_fndecl_attribute (tree *, tree, tree, int, bool *);
 static tree avr_handle_fntype_attribute (tree *, tree, tree, int, bool *);
 static bool avr_assemble_integer (rtx, unsigned int, int);
 static void avr_file_start (void);
 static void avr_file_end (void);
-static bool avr_legitimate_address_p (enum machine_mode, rtx, bool);
 static void avr_asm_function_end_prologue (FILE *);
 static void avr_asm_function_begin_epilogue (FILE *);
 static bool avr_cannot_modify_jumps_p (void);
@@ -112,6 +111,13 @@ static void avr_function_arg_advance (cu
 				      const_tree, bool);
 static bool avr_function_ok_for_sibcall (tree, tree);
 static void avr_asm_named_section (const char *name, unsigned int flags, tree decl);
+static enum machine_mode avr_addr_space_address_mode (addr_space_t);
+static enum machine_mode avr_addr_space_pointer_mode (addr_space_t);
+static bool avr_reg_ok_for_pgm_addr (rtx, bool);
+static bool avr_addr_space_legitimate_address_p (enum machine_mode mode, rtx, bool, addr_space_t);
+static rtx avr_addr_space_legitimize_address (rtx, rtx, enum machine_mode, addr_space_t);
+static rtx avr_addr_space_convert (rtx, tree, tree);
+static bool avr_addr_space_subset_p (addr_space_t, addr_space_t);
 static void avr_encode_section_info (tree, rtx, int);
 static section* avr_asm_function_rodata_section (tree);
 static section* avr_asm_select_section (tree, int, unsigned HOST_WIDE_INT);
@@ -119,6 +125,12 @@ static section* avr_asm_select_section (
 /* 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;
 
@@ -228,9 +240,6 @@ static const struct attribute_spec avr_a
 #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
 
@@ -245,9 +254,6 @@ static const struct attribute_spec avr_a
 #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
@@ -274,6 +280,24 @@ static const struct attribute_spec avr_a
 #undef TARGET_ASM_FUNCTION_RODATA_SECTION
 #define TARGET_ASM_FUNCTION_RODATA_SECTION avr_asm_function_rodata_section
 
+#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
+
 struct gcc_target targetm = TARGET_INITIALIZER;
 \f
 
@@ -355,6 +379,8 @@ avr_option_override (void)
   avr_current_arch = &avr_arch_types[avr_current_device->arch];
   avr_extra_arch_macro = avr_current_device->macro;
 
+  lpm_addr_reg_rtx = gen_rtx_REG (Pmode, REG_Z);
+  lpm_reg_rtx  = gen_rtx_REG (QImode, LPM_REGNO);
   tmp_reg_rtx  = gen_rtx_REG (QImode, TMP_REGNO);
   zero_reg_rtx = gen_rtx_REG (QImode, ZERO_REGNO);
 
@@ -405,6 +431,30 @@ avr_regno_reg_class (int r)
   return ALL_REGS;
 }
 
+
+/* Return TRUE if DECL is a VAR_DECL which is located in AS1
+   and FALSE, otherwise.  */
+
+static bool
+avr_decl_pgm_p (tree decl)
+{
+  if (TREE_CODE (decl) != VAR_DECL)
+    return false;
+
+  return (ADDR_SPACE_PGM == TYPE_ADDR_SPACE (TREE_TYPE (decl)));
+}
+
+
+/* Return TRUE if X is a MEM rtx located in AS1 and FALSE, otherwise.  */
+
+bool
+avr_mem_pgm_p (rtx x)
+{
+  return (MEM_P (x)
+          && ADDR_SPACE_PGM == MEM_ADDR_SPACE (x));
+}
+
+
 /* A helper for the subsequent function attribute used to dig for
    attribute 'name' in a FUNCTION_DECL or FUNCTION_TYPE */
 
@@ -1187,17 +1237,32 @@ avr_cannot_modify_jumps_p (void)
 }
 
 
-/* Return nonzero if X (an RTX) is a legitimate memory address on the target
-   machine for a memory operand of mode MODE.  */
+/* Helper function for `avr_legitimate_address_p'.  */
 
-bool
+static inline int
+avr_reg_ok_for_addr (rtx reg, addr_space_t as, int strict)
+{
+  return (REG_P (reg)
+          && (avr_regno_mode_code_ok_for_base_p (REGNO (reg), as,
+                                                 QImode, MEM, UNKNOWN)
+              || (!strict
+                  && REGNO (reg) >= FIRST_PSEUDO_REGISTER)));
+}
+
+
+/* Former implementation of TARGET_LEGITIMATE_ADDRESS_P,
+   now only a helper for avr_addr_space_legitimate_address_p.  */
+
+static bool
 avr_legitimate_address_p (enum machine_mode mode, rtx x, bool strict)
 {
   reg_class_t r = NO_REGS;
   
-  if (REG_P (x) && (strict ? REG_OK_FOR_BASE_STRICT_P (x)
-                    : REG_OK_FOR_BASE_NOSTRICT_P (x)))
-    r = POINTER_REGS;
+  if (REG_P (x)
+      && avr_reg_ok_for_addr (x, ADDR_SPACE_GENERIC, strict))
+    {
+      r = POINTER_REGS;
+    }
   else if (CONSTANT_ADDRESS_P (x))
     r = ALL_REGS;
   else if (GET_CODE (x) == PLUS
@@ -1222,8 +1287,7 @@ avr_legitimate_address_p (enum machine_m
     }
   else if ((GET_CODE (x) == PRE_DEC || GET_CODE (x) == POST_INC)
            && REG_P (XEXP (x, 0))
-           && (strict ? REG_OK_FOR_BASE_STRICT_P (XEXP (x, 0))
-               : REG_OK_FOR_BASE_NOSTRICT_P (XEXP (x, 0))))
+           && avr_reg_ok_for_addr (XEXP (x, 0), ADDR_SPACE_GENERIC, strict))
     {
       r = POINTER_REGS;
     }
@@ -1251,10 +1315,13 @@ avr_legitimate_address_p (enum machine_m
   return r == NO_REGS ? 0 : (int)r;
 }
 
+
+/* Former implementation of TARGET_LEGITIMIZE_ADDRESS,
+   now only a helper for avr_addr_space_legitimize_address.  */
 /* Attempts to replace X with a valid
    memory address for an operand of mode MODE  */
 
-rtx
+static rtx
 avr_legitimize_address (rtx x, rtx oldx, enum machine_mode mode)
 {
   bool big_offset_p = false;
@@ -1372,7 +1439,7 @@ avr_legitimize_reload_address (rtx x, en
 
 
 /* Helper function to print assembler resp. track instruction
-   sequence lengths.
+   sequence lengths.  Always returns "".
    
    If PLEN == NULL:
        Output assembler code from template TPL with operands supplied
@@ -1384,7 +1451,7 @@ avr_legitimize_reload_address (rtx x, en
        Don't output anything.
 */
 
-static void
+static const char*
 avr_asm_len (const char* tpl, rtx* operands, int* plen, int n_words)
 {
   if (NULL == plen)
@@ -1398,6 +1465,8 @@ avr_asm_len (const char* tpl, rtx* opera
       else
         *plen += n_words;
     }
+
+  return "";
 }
 
 
@@ -2004,6 +2073,263 @@ avr_function_ok_for_sibcall (tree decl_c
 /***********************************************************************
   Functions for outputting various mov's for a various modes
 ************************************************************************/
+
+
+/* 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 src = xop[1];
+  rtx addr;
+  int n_bytes = GET_MODE_SIZE (GET_MODE (dest));
+  int regno_dest;
+
+  addr = XEXP (src, 0);
+  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 ("lpm" CR_TAB
+                              "mov %0,%3", xop, plen, -2);
+
+        case 2:
+          if (REGNO (dest) == REG_Z)
+            return avr_asm_len ("lpm"        CR_TAB
+                                "push %3"    CR_TAB
+                                "adiw %2,1"  CR_TAB
+                                "lpm"        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);
+                
+              if (!reg_unused_after (insn, addr))
+                avr_asm_len ("sbiw %2,1", xop, plen, 1);
+            }
+          
+          break; /* 2 */
+
+        case 4:
+          avr_asm_len ("lpm"        CR_TAB
+                       "mov %A0,%3" CR_TAB
+                       "adiw %2,1"  CR_TAB
+                       "lpm"        CR_TAB
+                       "mov %B0,%3" CR_TAB
+                       "adiw %2,1", xop, plen, -6);
+          
+          if (REGNO (dest) == REG_Z - 2)
+            return avr_asm_len ("lpm"        CR_TAB
+                                "push %3"    CR_TAB
+                                "adiw %2,1"  CR_TAB
+                                "lpm"        CR_TAB
+                                "mov %D0,%3" CR_TAB
+                                "pop %C0", xop, plen, 6);
+          else
+            {
+              avr_asm_len ("lpm"        CR_TAB
+                           "mov %C0,%3" CR_TAB
+                           "adiw %2,1"  CR_TAB
+                           "lpm"        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)));
+      
+      switch (n_bytes)
+        {
+        default:
+          gcc_unreachable();
+
+        case 1:
+          return avr_asm_len ("lpm"        CR_TAB
+                              "mov %A0,%3" CR_TAB
+                              "adiw %2,1", xop, plen, -3);
+          
+        case 2:
+          return avr_asm_len ("lpm"        CR_TAB
+                              "mov %A0,%3" CR_TAB
+                              "adiw %2,1"  CR_TAB
+                              "lpm"        CR_TAB
+                              "mov %B0,%3" CR_TAB
+                              "adiw %2,1", xop, plen, -6);
+          
+        case 4:
+          return avr_asm_len ("lpm"        CR_TAB
+                              "mov %A0,%3" CR_TAB
+                              "adiw %2,1"  CR_TAB
+                              "lpm"        CR_TAB
+                              "mov %B0,%3" CR_TAB
+                              "adiw %2,1"  CR_TAB
+                              "lpm"        CR_TAB
+                              "mov %C0,%3" CR_TAB
+                              "adiw %2,1"  CR_TAB
+                              "lpm"        CR_TAB
+                              "mov %D0,%3" CR_TAB
+                              "adiw %2,1", xop, plen, -12);
+        } /* switch (n_bytes) */
+    } /* 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[4];
+  rtx dest = op[0];
+  rtx src = op[1];
+  rtx addr;
+  int n_bytes = GET_MODE_SIZE (GET_MODE (dest));
+  int regno_dest;
+
+  if (plen)
+    *plen = 0;
+  
+  if (MEM_P (dest))
+    {
+      warning (0, "writing to address space %qs not supported",
+               c_addr_space_name (ADDR_SPACE_PGM));
+      
+      return "";
+    }
+
+  gcc_assert (REG_P (dest)
+              && avr_mem_pgm_p (src));
+
+  xop[0] = op[0];
+  xop[1] = op[1];
+  xop[2] = lpm_addr_reg_rtx;
+
+  if (!AVR_HAVE_LPMX)
+    return avr_out_lpm_no_lpmx (insn, xop, plen);
+
+  addr = XEXP (src, 0);
+  regno_dest = REGNO (dest);
+
+  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 ("lpm %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);
+          else
+            {
+              avr_asm_len ("lpm %A0,%a2+" CR_TAB
+                           "lpm %B0,%a2", xop, plen, -2);
+                
+              if (!reg_unused_after (insn, addr))
+                avr_asm_len ("sbiw %2,1", xop, plen, 1);
+            }
+          
+          break; /* 2 */
+
+        case 4:
+
+          avr_asm_len ("lpm %A0,%a2+" CR_TAB
+                       "lpm %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);
+          else
+            {
+              avr_asm_len ("lpm %C0,%a2+" CR_TAB
+                           "lpm %D0,%a2", xop, plen, 2);
+                
+              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)));
+      
+      switch (n_bytes)
+        {
+        default:
+          gcc_unreachable();
+
+        case 1:
+          return avr_asm_len ("lpm %A0,%a2+", xop, plen, -1);
+          
+        case 2:
+          return avr_asm_len ("lpm %A0,%a2+" CR_TAB
+                              "lpm %B0,%a2+", xop, plen, -2);
+          
+        case 4:
+          return avr_asm_len ("lpm %A0,%a2+" CR_TAB
+                              "lpm %B0,%a2+" CR_TAB
+                              "lpm %C0,%a2+" CR_TAB
+                              "lpm %D0,%a2+", xop, plen, -4);
+        } /* switch (n_bytes) */
+    } /* switch CODE (addr) */
+      
+  return "";
+}
+
+
 const char *
 output_movqi (rtx insn, rtx operands[], int *l)
 {
@@ -2012,6 +2338,12 @@ output_movqi (rtx insn, rtx operands[],
   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;
 
@@ -2104,6 +2436,12 @@ output_movhi (rtx insn, rtx operands[],
   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;
   
@@ -2717,6 +3055,12 @@ output_movsisf (rtx insn, rtx operands[]
   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;
   
@@ -5483,8 +5827,10 @@ avr_handle_fntype_attribute (tree *node,
   return NULL_TREE;
 }
 
-/* Look for attribute `progmem' in DECL
-   if found return 1, otherwise 0.  */
+
+/* Return 2 if DECL is located in address space AS1.
+   Return 1 if attribute `progmem' occurs in DECL or ATTRIBUTES.
+   Return 0, otherwise.  */
 
 int
 avr_progmem_p (tree decl, tree attributes)
@@ -5494,6 +5840,9 @@ avr_progmem_p (tree decl, tree attribute
   if (TREE_CODE (decl) != VAR_DECL)
     return 0;
 
+  if (avr_decl_pgm_p (decl))
+    return 2;
+
   if (NULL_TREE
       != lookup_attribute ("progmem", attributes))
     return 1;
@@ -5512,11 +5861,104 @@ avr_progmem_p (tree decl, tree attribute
   return 0;
 }
 
+
+/* Scan type TYP for pointer references to address space AS1.
+   Return TRUE if all pointers targeting AS1 are also declared
+   to be CONST and FALSE, otherwise.  */
+   
+static bool
+avr_pgm_pointer_const_p (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_pgm_pointer_const_p (TREE_TYPE (target));
+
+      /* "Ordinary" pointers... */
+      
+      return ((/* ...must point to generic address space...  */
+               ADDR_SPACE_GENERIC_P (TYPE_ADDR_SPACE (target))
+               /* ...or point to read-only locations.  */
+               || TYPE_READONLY (target))
+              /* Scan pointer's target type.  */
+              && avr_pgm_pointer_const_p (target));
+    }
+
+  return true;
+}
+
+
+/* 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;
+  
+  if (avr_log.progmem)
+    avr_edump ("%?: %t\n", node);
+  
+  switch (TREE_CODE (node))
+    {
+    default:
+      break;
+
+    case VAR_DECL:
+      if (!avr_pgm_pointer_const_p (TREE_TYPE (node)))
+        reason = "variable";
+      break;
+
+    case PARM_DECL:
+      if (!avr_pgm_pointer_const_p (TREE_TYPE (node)))
+        reason = "function parameter";
+      break;
+        
+    case FIELD_DECL:
+      if (!avr_pgm_pointer_const_p (TREE_TYPE (node)))
+        reason = "structure field";
+      break;
+        
+    case FUNCTION_DECL:
+      if (!avr_pgm_pointer_const_p (TREE_TYPE (TREE_TYPE (node))))
+        reason = "return type of function";
+      break;
+
+    case POINTER_TYPE:
+      if (!avr_pgm_pointer_const_p (node))
+        reason = "pointer";
+      break;
+    }
+
+  if (reason)
+    {
+      if (TYPE_P (node))
+        error ("pointer targeting address space %qs must be const in %qT",
+               c_addr_space_name (ADDR_SPACE_PGM), node);
+      else
+        error ("pointer targeting address space %qs must be const in %s %q+D",
+               c_addr_space_name (ADDR_SPACE_PGM), 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))
@@ -5533,11 +5975,20 @@ avr_insert_attributes (tree node, tree *
       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);
         }
     }
 }
@@ -5734,6 +6185,7 @@ avr_section_type_flags (tree decl, const
       && avr_progmem_p (decl, DECL_ATTRIBUTES (decl)))
     {
       flags &= ~SECTION_WRITE;
+      flags &= ~SECTION_BSS;
       flags |= AVR_SECTION_PROGMEM;
     }
   
@@ -7155,6 +7607,79 @@ avr_hard_regno_mode_ok (int regno, enum
 }
 
 
+/* Implement `MODE_CODE_BASE_REG_CLASS'.  */
+
+reg_class_t
+avr_mode_code_base_reg_class (enum machine_mode mode ATTRIBUTE_UNUSED,
+                              addr_space_t as,
+                              RTX_CODE outer_code ATTRIBUTE_UNUSED,
+                              RTX_CODE index_code ATTRIBUTE_UNUSED)
+{
+  if (ADDR_SPACE_PGM == as)
+    {
+      return POINTER_Z_REGS;
+    }
+  
+  return reload_completed ? BASE_POINTER_REGS : POINTER_REGS;
+}
+
+
+/* Implement `REGNO_MODE_CODE_OK_FOR_BASE_P'.  */
+
+bool
+avr_regno_mode_code_ok_for_base_p (int regno,
+                                   enum machine_mode mode ATTRIBUTE_UNUSED,
+                                   addr_space_t as,
+                                   RTX_CODE outer_code ATTRIBUTE_UNUSED,
+                                   RTX_CODE index_code ATTRIBUTE_UNUSED)
+{
+  if (ADDR_SPACE_PGM == 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
+          || regno == REG_Z
+          || regno == ARG_POINTER_REGNUM))
+    {
+      return true;
+    }
+
+  if (reg_renumber)
+    {
+      regno = reg_renumber[regno];
+
+      if (regno == REG_X
+          || regno == REG_Y
+          || regno == REG_Z
+          || regno == ARG_POINTER_REGNUM)
+        {
+          return true;
+        }
+    }
+  
+  return false;
+}
+
+
 /* A helper for `output_reload_insisf' and `output_reload_inhi'.  */
 /* Set 32-bit register OP[0] to compile-time constant OP[1].
    CLOBBER_REG is a QI clobber register or NULL_RTX.
@@ -7649,6 +8174,140 @@ unsigned int avr_case_values_threshold (
   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 Pmode;
+}
+
+
+/* Implement `'.  */
+
+static enum machine_mode
+avr_addr_space_pointer_mode (addr_space_t as ATTRIBUTE_UNUSED)
+{
+  return 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 ((!reload_completed || !reload_in_progress)
+      && 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;
+  
+  if (ADDR_SPACE_GENERIC_P (as))
+    return avr_legitimate_address_p (mode, x, strict);
+
+  gcc_assert (ADDR_SPACE_PGM == as);
+
+  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;
+    }
+
+  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 op, tree from_type ATTRIBUTE_UNUSED,
+                        tree to_type ATTRIBUTE_UNUSED)
+{
+  if (avr_log.progmem)
+    avr_edump ("\n%?: op=%r\nfrom=%t\nto=%t\n", op, from_type, to_type);
+  return op;
+}
+
+
+/* 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)
+{
+  return true;
+}
+
+
 /* Helper for __builtin_avr_delay_cycles */
 
 static void
Index: config/avr/avr.h
===================================================================
--- config/avr/avr.h	(revision 179594)
+++ config/avr/avr.h	(working copy)
@@ -308,21 +308,13 @@ enum reg_class {
 
 #define REGNO_REG_CLASS(R) avr_regno_reg_class(R)
 
-#define BASE_REG_CLASS (reload_completed ? BASE_POINTER_REGS : POINTER_REGS)
+#define MODE_CODE_BASE_REG_CLASS(mode, address_space, outer_code, index_code) \
+  avr_mode_code_base_reg_class (mode, address_space, outer_code, index_code)
 
 #define INDEX_REG_CLASS NO_REGS
 
-#define REGNO_OK_FOR_BASE_P(r) (((r) < FIRST_PSEUDO_REGISTER		\
-				 && ((r) == REG_X			\
-				     || (r) == REG_Y			\
-				     || (r) == REG_Z			\
-				     || (r) == ARG_POINTER_REGNUM))	\
-				|| (reg_renumber			\
-				    && (reg_renumber[r] == REG_X	\
-					|| reg_renumber[r] == REG_Y	\
-					|| reg_renumber[r] == REG_Z	\
-					|| (reg_renumber[r]		\
-					    == ARG_POINTER_REGNUM))))
+#define REGNO_MODE_CODE_OK_FOR_BASE_P(num, mode, address_space, outer_code, index_code) \
+  avr_regno_mode_code_ok_for_base_p (num, mode, address_space, outer_code, index_code)
 
 #define REGNO_OK_FOR_INDEX_P(NUM) 0
 
@@ -381,10 +373,6 @@ typedef struct avr_args {
 
 #define MAX_REGS_PER_ADDRESS 1
 
-#define REG_OK_FOR_BASE_NOSTRICT_P(X) \
-  (REGNO (X) >= FIRST_PSEUDO_REGISTER || REG_OK_FOR_BASE_STRICT_P(X))
-
-#define REG_OK_FOR_BASE_STRICT_P(X) REGNO_OK_FOR_BASE_P (REGNO (X))
 #define LEGITIMIZE_RELOAD_ADDRESS(X,MODE,OPNUM,TYPE,IND_L,WIN)          \
   do {                                                                  \
     rtx new_x = avr_legitimize_reload_address (X, MODE, OPNUM, TYPE,    \
@@ -403,6 +391,14 @@ typedef struct avr_args {
 
 #define NO_FUNCTION_CSE
 
+
+#define ADDR_SPACE_PGM	1
+
+#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"

[-- Attachment #3: pgm-v6-weigand.diff --]
[-- Type: text/x-patch, Size: 43228 bytes --]

Index: doc/tm.texi
===================================================================
--- doc/tm.texi	(revision 179594)
+++ doc/tm.texi	(working copy)
@@ -2453,12 +2453,13 @@ register address.  You should define thi
 addresses have different requirements than other base register uses.
 @end defmac
 
-@defmac MODE_CODE_BASE_REG_CLASS (@var{mode}, @var{outer_code}, @var{index_code})
+@defmac MODE_CODE_BASE_REG_CLASS (@var{mode}, @var{address_space}, @var{outer_code}, @var{index_code})
 A C expression whose value is the register class to which a valid
-base register must belong.  @var{outer_code} and @var{index_code} define the
-context in which the base register occurs.  @var{outer_code} is the code of
-the immediately enclosing expression (@code{MEM} for the top level of an
-address, @code{ADDRESS} for something that occurs in an
+base register for a memory reference in mode @var{mode} to address
+space @var{address_space} must belong.  @var{outer_code} and @var{index_code}
+define the context in which the base register occurs.  @var{outer_code} is
+the code of the immediately enclosing expression (@code{MEM} for the top level
+of an address, @code{ADDRESS} for something that occurs in an
 @code{address_operand}).  @var{index_code} is the code of the corresponding
 index expression if @var{outer_code} is @code{PLUS}; @code{SCRATCH} otherwise.
 @end defmac
@@ -2498,8 +2499,11 @@ Use of this macro is deprecated; please
 @code{REGNO_MODE_CODE_OK_FOR_BASE_P}.
 @end defmac
 
-@defmac REGNO_MODE_CODE_OK_FOR_BASE_P (@var{num}, @var{mode}, @var{outer_code}, @var{index_code})
-A C expression that is just like @code{REGNO_MODE_OK_FOR_BASE_P}, except
+@defmac REGNO_MODE_CODE_OK_FOR_BASE_P (@var{num}, @var{mode}, @var{address_space}, @var{outer_code}, @var{index_code})
+A C expression which is nonzero if register number @var{num} is
+suitable for use as a base register in operand addresses, accessing
+memory in mode @var{mode} in address space @var{address_space}.
+This is similar to @code{REGNO_MODE_OK_FOR_BASE_P}, except
 that that expression may examine the context in which the register
 appears in the memory reference.  @var{outer_code} is the code of the
 immediately enclosing expression (@code{MEM} if at the top level of the
Index: doc/tm.texi.in
===================================================================
--- doc/tm.texi.in	(revision 179594)
+++ doc/tm.texi.in	(working copy)
@@ -2441,12 +2441,13 @@ register address.  You should define thi
 addresses have different requirements than other base register uses.
 @end defmac
 
-@defmac MODE_CODE_BASE_REG_CLASS (@var{mode}, @var{outer_code}, @var{index_code})
+@defmac MODE_CODE_BASE_REG_CLASS (@var{mode}, @var{address_space}, @var{outer_code}, @var{index_code})
 A C expression whose value is the register class to which a valid
-base register must belong.  @var{outer_code} and @var{index_code} define the
-context in which the base register occurs.  @var{outer_code} is the code of
-the immediately enclosing expression (@code{MEM} for the top level of an
-address, @code{ADDRESS} for something that occurs in an
+base register for a memory reference in mode @var{mode} to address
+space @var{address_space} must belong.  @var{outer_code} and @var{index_code}
+define the context in which the base register occurs.  @var{outer_code} is
+the code of the immediately enclosing expression (@code{MEM} for the top level
+of an address, @code{ADDRESS} for something that occurs in an
 @code{address_operand}).  @var{index_code} is the code of the corresponding
 index expression if @var{outer_code} is @code{PLUS}; @code{SCRATCH} otherwise.
 @end defmac
@@ -2486,8 +2487,11 @@ Use of this macro is deprecated; please
 @code{REGNO_MODE_CODE_OK_FOR_BASE_P}.
 @end defmac
 
-@defmac REGNO_MODE_CODE_OK_FOR_BASE_P (@var{num}, @var{mode}, @var{outer_code}, @var{index_code})
-A C expression that is just like @code{REGNO_MODE_OK_FOR_BASE_P}, except
+@defmac REGNO_MODE_CODE_OK_FOR_BASE_P (@var{num}, @var{mode}, @var{address_space}, @var{outer_code}, @var{index_code})
+A C expression which is nonzero if register number @var{num} is
+suitable for use as a base register in operand addresses, accessing
+memory in mode @var{mode} in address space @var{address_space}.
+This is similar to @code{REGNO_MODE_OK_FOR_BASE_P}, except
 that that expression may examine the context in which the register
 appears in the memory reference.  @var{outer_code} is the code of the
 immediately enclosing expression (@code{MEM} if at the top level of the
Index: caller-save.c
===================================================================
--- caller-save.c	(revision 179594)
+++ caller-save.c	(working copy)
@@ -231,7 +231,8 @@ init_caller_save (void)
   for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
     if (TEST_HARD_REG_BIT
 	(reg_class_contents
-	 [(int) base_reg_class (regno_save_mode[i][1], PLUS, CONST_INT)], i))
+	 [(int) base_reg_class (regno_save_mode[i][1], ADDR_SPACE_GENERIC,
+				PLUS, CONST_INT)], i))
       break;
 
   gcc_assert (i < FIRST_PSEUDO_REGISTER);
Index: ira-conflicts.c
===================================================================
--- ira-conflicts.c	(revision 179594)
+++ ira-conflicts.c	(working copy)
@@ -845,6 +845,7 @@ ira_debug_conflicts (bool reg_p)
 void
 ira_build_conflicts (void)
 {
+  enum reg_class base;
   ira_allocno_t a;
   ira_allocno_iterator ai;
   HARD_REG_SET temp_hard_reg_set;
@@ -874,13 +875,12 @@ ira_build_conflicts (void)
 	  ira_free (conflicts);
 	}
     }
-  if (! targetm.class_likely_spilled_p (base_reg_class (VOIDmode, ADDRESS,
-							SCRATCH)))
+  base = base_reg_class (VOIDmode, ADDR_SPACE_GENERIC, ADDRESS, SCRATCH);
+  if (! targetm.class_likely_spilled_p (base))
     CLEAR_HARD_REG_SET (temp_hard_reg_set);
   else
     {
-      COPY_HARD_REG_SET (temp_hard_reg_set,
-			 reg_class_contents[base_reg_class (VOIDmode, ADDRESS, SCRATCH)]);
+      COPY_HARD_REG_SET (temp_hard_reg_set, reg_class_contents[base]);
       AND_COMPL_HARD_REG_SET (temp_hard_reg_set, ira_no_alloc_regs);
       AND_HARD_REG_SET (temp_hard_reg_set, call_used_reg_set);
     }
Index: ira-costs.c
===================================================================
--- ira-costs.c	(revision 179594)
+++ ira-costs.c	(working copy)
@@ -637,7 +637,8 @@ record_reg_classes (int n_alts, int n_op
 		     base of an address, i.e. BASE_REG_CLASS.  */
 		  classes[i]
 		    = ira_reg_class_subunion[classes[i]]
-		      [base_reg_class (VOIDmode, ADDRESS, SCRATCH)];
+		      [base_reg_class (VOIDmode, ADDR_SPACE_GENERIC,
+				       ADDRESS, SCRATCH)];
 		  break;
 
 		case 'm':  case 'o':  case 'V':
@@ -752,7 +753,8 @@ record_reg_classes (int n_alts, int n_op
 			 i.e. BASE_REG_CLASS.  */
 		      classes[i]
 			= ira_reg_class_subunion[classes[i]]
-			  [base_reg_class (VOIDmode, ADDRESS, SCRATCH)];
+			  [base_reg_class (VOIDmode, ADDR_SPACE_GENERIC,
+					   ADDRESS, SCRATCH)];
 		    }
 #endif
 		  break;
@@ -996,14 +998,14 @@ ok_for_index_p_nonstrict (rtx reg)
    pseudo-registers should count as OK.  Arguments as for
    regno_ok_for_base_p.  */
 static inline bool
-ok_for_base_p_nonstrict (rtx reg, enum machine_mode mode,
+ok_for_base_p_nonstrict (rtx reg, enum machine_mode mode, addr_space_t as,
 			 enum rtx_code outer_code, enum rtx_code index_code)
 {
   unsigned regno = REGNO (reg);
 
   if (regno >= FIRST_PSEUDO_REGISTER)
     return true;
-  return ok_for_base_p_1 (regno, mode, outer_code, index_code);
+  return ok_for_base_p_1 (regno, mode, as, outer_code, index_code);
 }
 
 /* Record the pseudo registers we must reload into hard registers in a
@@ -1012,16 +1014,16 @@ ok_for_base_p_nonstrict (rtx reg, enum m
    If CONTEXT is 0, we are looking at the base part of an address,
    otherwise we are looking at the index part.
 
-   MODE is the mode of the memory reference; OUTER_CODE and INDEX_CODE
-   give the context that the rtx appears in.  These three arguments
-   are passed down to base_reg_class.
+   MODE and AS are the mode and address space of the memory reference;
+   OUTER_CODE and INDEX_CODE give the context that the rtx appears in.
+   These four arguments are passed down to base_reg_class.
 
    SCALE is twice the amount to multiply the cost by (it is twice so
    we can represent half-cost adjustments).  */
 static void
-record_address_regs (enum machine_mode mode, rtx x, int context,
-		     enum rtx_code outer_code, enum rtx_code index_code,
-		     int scale)
+record_address_regs (enum machine_mode mode, addr_space_t as, rtx x,
+		     int context, enum rtx_code outer_code,
+		     enum rtx_code index_code, int scale)
 {
   enum rtx_code code = GET_CODE (x);
   enum reg_class rclass;
@@ -1029,7 +1031,7 @@ record_address_regs (enum machine_mode m
   if (context == 1)
     rclass = INDEX_REG_CLASS;
   else
-    rclass = base_reg_class (mode, outer_code, index_code);
+    rclass = base_reg_class (mode, as, outer_code, index_code);
 
   switch (code)
     {
@@ -1068,67 +1070,68 @@ record_address_regs (enum machine_mode m
 	/* If this machine only allows one register per address, it
 	   must be in the first operand.  */
 	if (MAX_REGS_PER_ADDRESS == 1)
-	  record_address_regs (mode, arg0, 0, PLUS, code1, scale);
+	  record_address_regs (mode, as, arg0, 0, PLUS, code1, scale);
 
 	/* If index and base registers are the same on this machine,
 	   just record registers in any non-constant operands.  We
 	   assume here, as well as in the tests below, that all
 	   addresses are in canonical form.  */
-	else if (INDEX_REG_CLASS == base_reg_class (VOIDmode, PLUS, SCRATCH))
+	else if (INDEX_REG_CLASS
+		 == base_reg_class (VOIDmode, as, PLUS, SCRATCH))
 	  {
-	    record_address_regs (mode, arg0, context, PLUS, code1, scale);
+	    record_address_regs (mode, as, arg0, context, PLUS, code1, scale);
 	    if (! CONSTANT_P (arg1))
-	      record_address_regs (mode, arg1, context, PLUS, code0, scale);
+	      record_address_regs (mode, as, arg1, context, PLUS, code0, scale);
 	  }
 
 	/* If the second operand is a constant integer, it doesn't
 	   change what class the first operand must be.  */
 	else if (code1 == CONST_INT || code1 == CONST_DOUBLE)
-	  record_address_regs (mode, arg0, context, PLUS, code1, scale);
+	  record_address_regs (mode, as, arg0, context, PLUS, code1, scale);
 	/* If the second operand is a symbolic constant, the first
 	   operand must be an index register.  */
 	else if (code1 == SYMBOL_REF || code1 == CONST || code1 == LABEL_REF)
-	  record_address_regs (mode, arg0, 1, PLUS, code1, scale);
+	  record_address_regs (mode, as, arg0, 1, PLUS, code1, scale);
 	/* If both operands are registers but one is already a hard
 	   register of index or reg-base class, give the other the
 	   class that the hard register is not.  */
 	else if (code0 == REG && code1 == REG
 		 && REGNO (arg0) < FIRST_PSEUDO_REGISTER
-		 && (ok_for_base_p_nonstrict (arg0, mode, PLUS, REG)
+		 && (ok_for_base_p_nonstrict (arg0, mode, as, PLUS, REG)
 		     || ok_for_index_p_nonstrict (arg0)))
-	  record_address_regs (mode, arg1,
-			       ok_for_base_p_nonstrict (arg0, mode, PLUS, REG)
-			       ? 1 : 0,
+	  record_address_regs (mode, as, arg1,
+			       ok_for_base_p_nonstrict (arg0, mode, as,
+							PLUS, REG) ? 1 : 0,
 			       PLUS, REG, scale);
 	else if (code0 == REG && code1 == REG
 		 && REGNO (arg1) < FIRST_PSEUDO_REGISTER
-		 && (ok_for_base_p_nonstrict (arg1, mode, PLUS, REG)
+		 && (ok_for_base_p_nonstrict (arg1, mode, as, PLUS, REG)
 		     || ok_for_index_p_nonstrict (arg1)))
-	  record_address_regs (mode, arg0,
-			       ok_for_base_p_nonstrict (arg1, mode, PLUS, REG)
-			       ? 1 : 0,
+	  record_address_regs (mode, as, arg0,
+			       ok_for_base_p_nonstrict (arg1, mode, as,
+							PLUS, REG) ? 1 : 0,
 			       PLUS, REG, scale);
 	/* If one operand is known to be a pointer, it must be the
 	   base with the other operand the index.  Likewise if the
 	   other operand is a MULT.  */
 	else if ((code0 == REG && REG_POINTER (arg0)) || code1 == MULT)
 	  {
-	    record_address_regs (mode, arg0, 0, PLUS, code1, scale);
-	    record_address_regs (mode, arg1, 1, PLUS, code0, scale);
+	    record_address_regs (mode, as, arg0, 0, PLUS, code1, scale);
+	    record_address_regs (mode, as, arg1, 1, PLUS, code0, scale);
 	  }
 	else if ((code1 == REG && REG_POINTER (arg1)) || code0 == MULT)
 	  {
-	    record_address_regs (mode, arg0, 1, PLUS, code1, scale);
-	    record_address_regs (mode, arg1, 0, PLUS, code0, scale);
+	    record_address_regs (mode, as, arg0, 1, PLUS, code1, scale);
+	    record_address_regs (mode, as, arg1, 0, PLUS, code0, scale);
 	  }
 	/* Otherwise, count equal chances that each might be a base or
 	   index register.  This case should be rare.  */
 	else
 	  {
-	    record_address_regs (mode, arg0, 0, PLUS, code1, scale / 2);
-	    record_address_regs (mode, arg0, 1, PLUS, code1, scale / 2);
-	    record_address_regs (mode, arg1, 0, PLUS, code0, scale / 2);
-	    record_address_regs (mode, arg1, 1, PLUS, code0, scale / 2);
+	    record_address_regs (mode, as, arg0, 0, PLUS, code1, scale / 2);
+	    record_address_regs (mode, as, arg0, 1, PLUS, code1, scale / 2);
+	    record_address_regs (mode, as, arg1, 0, PLUS, code0, scale / 2);
+	    record_address_regs (mode, as, arg1, 1, PLUS, code0, scale / 2);
 	  }
       }
       break;
@@ -1138,10 +1141,10 @@ record_address_regs (enum machine_mode m
 	 up in the wrong place.  */
     case POST_MODIFY:
     case PRE_MODIFY:
-      record_address_regs (mode, XEXP (x, 0), 0, code,
+      record_address_regs (mode, as, XEXP (x, 0), 0, code,
 			   GET_CODE (XEXP (XEXP (x, 1), 1)), 2 * scale);
       if (REG_P (XEXP (XEXP (x, 1), 1)))
-	record_address_regs (mode, XEXP (XEXP (x, 1), 1), 1, code, REG,
+	record_address_regs (mode, as, XEXP (XEXP (x, 1), 1), 1, code, REG,
 			     2 * scale);
       break;
 
@@ -1152,7 +1155,7 @@ record_address_regs (enum machine_mode m
       /* Double the importance of an allocno that is incremented or
 	 decremented, since it would take two extra insns if it ends
 	 up in the wrong place.  */
-      record_address_regs (mode, XEXP (x, 0), 0, code, SCRATCH, 2 * scale);
+      record_address_regs (mode, as, XEXP (x, 0), 0, code, SCRATCH, 2 * scale);
       break;
 
     case REG:
@@ -1200,7 +1203,7 @@ record_address_regs (enum machine_mode m
 	int i;
 	for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
 	  if (fmt[i] == 'e')
-	    record_address_regs (mode, XEXP (x, i), context, code, SCRATCH,
+	    record_address_regs (mode, as, XEXP (x, i), context, code, SCRATCH,
 				 scale);
       }
     }
@@ -1236,13 +1239,15 @@ record_operand_costs (rtx insn, enum reg
 
       if (MEM_P (recog_data.operand[i]))
 	record_address_regs (GET_MODE (recog_data.operand[i]),
+			     MEM_ADDR_SPACE (recog_data.operand[i]),
 			     XEXP (recog_data.operand[i], 0),
 			     0, MEM, SCRATCH, frequency * 2);
       else if (constraints[i][0] == 'p'
 	       || EXTRA_ADDRESS_CONSTRAINT (constraints[i][0],
 					    constraints[i]))
-	record_address_regs (VOIDmode, recog_data.operand[i], 0, ADDRESS,
-			     SCRATCH, frequency * 2);
+	record_address_regs (VOIDmode, ADDR_SPACE_GENERIC,
+			     recog_data.operand[i], 0, ADDRESS, SCRATCH,
+			     frequency * 2);
     }
   
   /* Check for commutative in a separate loop so everything will have
@@ -1316,8 +1321,10 @@ scan_one_insn (rtx insn)
 
       COSTS (costs, num)->mem_cost
 	-= ira_memory_move_cost[GET_MODE (reg)][cl][1] * frequency;
-      record_address_regs (GET_MODE (SET_SRC (set)), XEXP (SET_SRC (set), 0),
-			   0, MEM, SCRATCH, frequency * 2);
+      record_address_regs (GET_MODE (SET_SRC (set)),
+			   MEM_ADDR_SPACE (SET_SRC (set)),
+			   XEXP (SET_SRC (set), 0), 0, MEM, SCRATCH,
+			   frequency * 2);
       counted_mem = true;
     }
 
Index: recog.c
===================================================================
--- recog.c	(revision 179594)
+++ recog.c	(working copy)
@@ -2281,7 +2281,8 @@ preprocess_constraints (void)
 		case 'p':
 		  op_alt[j].is_address = 1;
 		  op_alt[j].cl = reg_class_subunion[(int) op_alt[j].cl]
-		      [(int) base_reg_class (VOIDmode, ADDRESS, SCRATCH)];
+		      [(int) base_reg_class (VOIDmode, ADDR_SPACE_GENERIC,
+					     ADDRESS, SCRATCH)];
 		  break;
 
 		case 'g':
@@ -2302,8 +2303,8 @@ preprocess_constraints (void)
 		      op_alt[j].cl
 			= (reg_class_subunion
 			   [(int) op_alt[j].cl]
-			   [(int) base_reg_class (VOIDmode, ADDRESS,
-						  SCRATCH)]);
+			   [(int) base_reg_class (VOIDmode, ADDR_SPACE_GENERIC,
+						  ADDRESS, SCRATCH)]);
 		      break;
 		    }
 
Index: regcprop.c
===================================================================
--- regcprop.c	(revision 179594)
+++ regcprop.c	(working copy)
@@ -98,7 +98,7 @@ static rtx find_oldest_value_reg (enum r
 static bool replace_oldest_value_reg (rtx *, enum reg_class, rtx,
 				      struct value_data *);
 static bool replace_oldest_value_addr (rtx *, enum reg_class,
-				       enum machine_mode, rtx,
+				       enum machine_mode, addr_space_t, rtx,
 				       struct value_data *);
 static bool replace_oldest_value_mem (rtx, rtx, struct value_data *);
 static bool copyprop_hardreg_forward_1 (basic_block, struct value_data *);
@@ -515,8 +515,8 @@ replace_oldest_value_reg (rtx *loc, enum
 
 static bool
 replace_oldest_value_addr (rtx *loc, enum reg_class cl,
-			   enum machine_mode mode, rtx insn,
-			   struct value_data *vd)
+			   enum machine_mode mode, addr_space_t as,
+			   rtx insn, struct value_data *vd)
 {
   rtx x = *loc;
   RTX_CODE code = GET_CODE (x);
@@ -585,15 +585,15 @@ replace_oldest_value_addr (rtx *loc, enu
 	    unsigned regno0 = REGNO (op0), regno1 = REGNO (op1);
 
 	    if (REGNO_OK_FOR_INDEX_P (regno1)
-		&& regno_ok_for_base_p (regno0, mode, PLUS, REG))
+		&& regno_ok_for_base_p (regno0, mode, as, PLUS, REG))
 	      index_op = 1;
 	    else if (REGNO_OK_FOR_INDEX_P (regno0)
-		     && regno_ok_for_base_p (regno1, mode, PLUS, REG))
+		     && regno_ok_for_base_p (regno1, mode, as, PLUS, REG))
 	      index_op = 0;
-	    else if (regno_ok_for_base_p (regno0, mode, PLUS, REG)
+	    else if (regno_ok_for_base_p (regno0, mode, as, PLUS, REG)
 		     || REGNO_OK_FOR_INDEX_P (regno1))
 	      index_op = 1;
-	    else if (regno_ok_for_base_p (regno1, mode, PLUS, REG))
+	    else if (regno_ok_for_base_p (regno1, mode, as, PLUS, REG))
 	      index_op = 0;
 	    else
 	      index_op = 1;
@@ -616,13 +616,13 @@ replace_oldest_value_addr (rtx *loc, enu
 	  }
 
 	if (locI)
-	  changed |= replace_oldest_value_addr (locI, INDEX_REG_CLASS, mode,
-						insn, vd);
+	  changed |= replace_oldest_value_addr (locI, INDEX_REG_CLASS,
+						mode, as, insn, vd);
 	if (locB)
 	  changed |= replace_oldest_value_addr (locB,
-						base_reg_class (mode, PLUS,
+						base_reg_class (mode, as, PLUS,
 								index_code),
-						mode, insn, vd);
+						mode, as, insn, vd);
 	return changed;
       }
 
@@ -648,12 +648,12 @@ replace_oldest_value_addr (rtx *loc, enu
   for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
     {
       if (fmt[i] == 'e')
-	changed |= replace_oldest_value_addr (&XEXP (x, i), cl, mode,
+	changed |= replace_oldest_value_addr (&XEXP (x, i), cl, mode, as,
 					      insn, vd);
       else if (fmt[i] == 'E')
 	for (j = XVECLEN (x, i) - 1; j >= 0; j--)
 	  changed |= replace_oldest_value_addr (&XVECEXP (x, i, j), cl,
-						mode, insn, vd);
+						mode, as, insn, vd);
     }
 
   return changed;
@@ -669,10 +669,11 @@ replace_oldest_value_mem (rtx x, rtx ins
   if (DEBUG_INSN_P (insn))
     cl = ALL_REGS;
   else
-    cl = base_reg_class (GET_MODE (x), MEM, SCRATCH);
+    cl = base_reg_class (GET_MODE (x), MEM_ADDR_SPACE (x), MEM, SCRATCH);
 
   return replace_oldest_value_addr (&XEXP (x, 0), cl,
-				    GET_MODE (x), insn, vd);
+				    GET_MODE (x), MEM_ADDR_SPACE (x),
+				    insn, vd);
 }
 
 /* Apply all queued updates for DEBUG_INSNs that change some reg to
@@ -751,7 +752,7 @@ copyprop_hardreg_forward_1 (basic_block
 	      if (!VAR_LOC_UNKNOWN_P (loc))
 		replace_oldest_value_addr (&INSN_VAR_LOCATION_LOC (insn),
 					   ALL_REGS, GET_MODE (loc),
-					   insn, vd);
+					   ADDR_SPACE_GENERIC, insn, vd);
 	    }
 
 	  if (insn == BB_END (bb))
@@ -893,7 +894,8 @@ copyprop_hardreg_forward_1 (basic_block
 		replaced[i]
 		  = replace_oldest_value_addr (recog_data.operand_loc[i],
 					       recog_op_alt[i][alt].cl,
-					       VOIDmode, insn, vd);
+					       VOIDmode, ADDR_SPACE_GENERIC,
+					       insn, vd);
 	      else if (REG_P (recog_data.operand[i]))
 		replaced[i]
 		  = replace_oldest_value_reg (recog_data.operand_loc[i],
Index: regrename.c
===================================================================
--- regrename.c	(revision 179594)
+++ regrename.c	(working copy)
@@ -145,6 +145,11 @@ static int this_tick = 0;
 static struct obstack rename_obstack;
 
 static void do_replace (struct du_head *, int);
+static void scan_rtx_reg (rtx, rtx *, enum reg_class,
+			  enum scan_actions, enum op_type);
+static void scan_rtx_address (rtx, rtx *, enum reg_class,
+			      enum scan_actions, enum machine_mode,
+			      addr_space_t);
 static void scan_rtx (rtx, rtx *, enum reg_class, enum scan_actions,
 		      enum op_type);
 static bool build_def_use (basic_block);
@@ -1177,7 +1182,8 @@ scan_rtx_reg (rtx insn, rtx *loc, enum r
 
 static void
 scan_rtx_address (rtx insn, rtx *loc, enum reg_class cl,
-		  enum scan_actions action, enum machine_mode mode)
+		  enum scan_actions action, enum machine_mode mode,
+		  addr_space_t as)
 {
   rtx x = *loc;
   RTX_CODE code = GET_CODE (x);
@@ -1245,15 +1251,15 @@ scan_rtx_address (rtx insn, rtx *loc, en
 	    unsigned regno0 = REGNO (op0), regno1 = REGNO (op1);
 
 	    if (REGNO_OK_FOR_INDEX_P (regno1)
-		&& regno_ok_for_base_p (regno0, mode, PLUS, REG))
+		&& regno_ok_for_base_p (regno0, mode, as, PLUS, REG))
 	      index_op = 1;
 	    else if (REGNO_OK_FOR_INDEX_P (regno0)
-		     && regno_ok_for_base_p (regno1, mode, PLUS, REG))
+		     && regno_ok_for_base_p (regno1, mode, as, PLUS, REG))
 	      index_op = 0;
-	    else if (regno_ok_for_base_p (regno0, mode, PLUS, REG)
+	    else if (regno_ok_for_base_p (regno0, mode, as, PLUS, REG)
 		     || REGNO_OK_FOR_INDEX_P (regno1))
 	      index_op = 1;
-	    else if (regno_ok_for_base_p (regno1, mode, PLUS, REG))
+	    else if (regno_ok_for_base_p (regno1, mode, as, PLUS, REG))
 	      index_op = 0;
 	    else
 	      index_op = 1;
@@ -1276,10 +1282,11 @@ scan_rtx_address (rtx insn, rtx *loc, en
 	  }
 
 	if (locI)
-	  scan_rtx_address (insn, locI, INDEX_REG_CLASS, action, mode);
+	  scan_rtx_address (insn, locI, INDEX_REG_CLASS, action, mode, as);
 	if (locB)
-	  scan_rtx_address (insn, locB, base_reg_class (mode, PLUS, index_code),
-			    action, mode);
+	  scan_rtx_address (insn, locB,
+			    base_reg_class (mode, as, PLUS, index_code),
+			    action, mode, as);
 
 	return;
       }
@@ -1299,8 +1306,9 @@ scan_rtx_address (rtx insn, rtx *loc, en
 
     case MEM:
       scan_rtx_address (insn, &XEXP (x, 0),
-			base_reg_class (GET_MODE (x), MEM, SCRATCH), action,
-			GET_MODE (x));
+			base_reg_class (GET_MODE (x), MEM_ADDR_SPACE (x),
+					MEM, SCRATCH),
+			action, GET_MODE (x), MEM_ADDR_SPACE (x));
       return;
 
     case REG:
@@ -1315,10 +1323,10 @@ scan_rtx_address (rtx insn, rtx *loc, en
   for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
     {
       if (fmt[i] == 'e')
-	scan_rtx_address (insn, &XEXP (x, i), cl, action, mode);
+	scan_rtx_address (insn, &XEXP (x, i), cl, action, mode, as);
       else if (fmt[i] == 'E')
 	for (j = XVECLEN (x, i) - 1; j >= 0; j--)
-	  scan_rtx_address (insn, &XVECEXP (x, i, j), cl, action, mode);
+	  scan_rtx_address (insn, &XVECEXP (x, i, j), cl, action, mode, as);
     }
 }
 
@@ -1351,8 +1359,9 @@ scan_rtx (rtx insn, rtx *loc, enum reg_c
 
     case MEM:
       scan_rtx_address (insn, &XEXP (x, 0),
-			base_reg_class (GET_MODE (x), MEM, SCRATCH), action,
-			GET_MODE (x));
+			base_reg_class (GET_MODE (x), MEM_ADDR_SPACE (x),
+					MEM, SCRATCH),
+			action, GET_MODE (x), MEM_ADDR_SPACE (x));
       return;
 
     case SET:
@@ -1675,7 +1684,8 @@ build_def_use (basic_block bb)
 		continue;
 
 	      if (recog_op_alt[opn][alt].is_address)
-		scan_rtx_address (insn, loc, cl, mark_read, VOIDmode);
+		scan_rtx_address (insn, loc, cl, mark_read,
+				  VOIDmode, ADDR_SPACE_GENERIC);
 	      else
 		scan_rtx (insn, loc, cl, mark_read, type);
 	    }
Index: reload1.c
===================================================================
--- reload1.c	(revision 179594)
+++ reload1.c	(working copy)
@@ -1422,7 +1422,8 @@ maybe_fix_stack_asms (void)
 
 		case 'p':
 		  cls = (int) reg_class_subunion[cls]
-		      [(int) base_reg_class (VOIDmode, ADDRESS, SCRATCH)];
+		      [(int) base_reg_class (VOIDmode, ADDR_SPACE_GENERIC,
+					     ADDRESS, SCRATCH)];
 		  break;
 
 		case 'g':
@@ -1433,7 +1434,8 @@ maybe_fix_stack_asms (void)
 		default:
 		  if (EXTRA_ADDRESS_CONSTRAINT (c, p))
 		    cls = (int) reg_class_subunion[cls]
-		      [(int) base_reg_class (VOIDmode, ADDRESS, SCRATCH)];
+		      [(int) base_reg_class (VOIDmode, ADDR_SPACE_GENERIC,
+					     ADDRESS, SCRATCH)];
 		  else
 		    cls = (int) reg_class_subunion[cls]
 		      [(int) REG_CLASS_FROM_CONSTRAINT (c, p)];
Index: reload.c
===================================================================
--- reload.c	(revision 179594)
+++ reload.c	(working copy)
@@ -278,7 +278,7 @@ static int find_reloads_address (enum ma
 static rtx subst_reg_equivs (rtx, rtx);
 static rtx subst_indexed_address (rtx);
 static void update_auto_inc_notes (rtx, int, int);
-static int find_reloads_address_1 (enum machine_mode, rtx, int,
+static int find_reloads_address_1 (enum machine_mode, addr_space_t, rtx, int,
 				   enum rtx_code, enum rtx_code, rtx *,
 				   int, enum reload_type,int, rtx);
 static void find_reloads_address_part (rtx, rtx *, enum reg_class,
@@ -3242,8 +3242,9 @@ find_reloads (rtx insn, int replace, int
 	      case 'p':
 		/* All necessary reloads for an address_operand
 		   were handled in find_reloads_address.  */
-		this_alternative[i] = base_reg_class (VOIDmode, ADDRESS,
-						      SCRATCH);
+		this_alternative[i]
+		  = base_reg_class (VOIDmode, ADDR_SPACE_GENERIC,
+				    ADDRESS, SCRATCH);
 		win = 1;
 		badop = 0;
 		break;
@@ -3448,9 +3449,9 @@ find_reloads (rtx insn, int replace, int
 
 			/* If we didn't already win, we can reload
 			   the address into a base register.  */
-			this_alternative[i] = base_reg_class (VOIDmode,
-							      ADDRESS,
-							      SCRATCH);
+			this_alternative[i]
+			  = base_reg_class (VOIDmode, ADDR_SPACE_GENERIC,
+					    ADDRESS, SCRATCH);
 			badop = 0;
 			break;
 		      }
@@ -3980,18 +3981,16 @@ find_reloads (rtx insn, int replace, int
 	    /* If the address to be reloaded is a VOIDmode constant,
 	       use the default address mode as mode of the reload register,
 	       as would have been done by find_reloads_address.  */
+	    addr_space_t as = MEM_ADDR_SPACE (recog_data.operand[i]);
 	    enum machine_mode address_mode;
 	    address_mode = GET_MODE (XEXP (recog_data.operand[i], 0));
 	    if (address_mode == VOIDmode)
-	      {
-		addr_space_t as = MEM_ADDR_SPACE (recog_data.operand[i]);
-		address_mode = targetm.addr_space.address_mode (as);
-	      }
+	      address_mode = targetm.addr_space.address_mode (as);
 
 	    operand_reloadnum[i]
 	      = push_reload (XEXP (recog_data.operand[i], 0), NULL_RTX,
 			     &XEXP (recog_data.operand[i], 0), (rtx*) 0,
-			     base_reg_class (VOIDmode, MEM, SCRATCH),
+			     base_reg_class (VOIDmode, as, MEM, SCRATCH),
 			     address_mode,
 			     VOIDmode, 0, 0, i, RELOAD_FOR_INPUT);
 	    rld[operand_reloadnum[i]].inc
@@ -4888,7 +4887,7 @@ find_reloads_address (enum machine_mode
       if (reg_equiv_constant (regno) != 0)
 	{
 	  find_reloads_address_part (reg_equiv_constant (regno), loc,
-				     base_reg_class (mode, MEM, SCRATCH),
+				     base_reg_class (mode, as, MEM, SCRATCH),
 				     GET_MODE (ad), opnum, type, ind_levels);
 	  return 1;
 	}
@@ -4951,12 +4950,13 @@ find_reloads_address (enum machine_mode
 	 subject of a CLOBBER in this insn.  */
 
       else if (regno < FIRST_PSEUDO_REGISTER
-	       && regno_ok_for_base_p (regno, mode, MEM, SCRATCH)
+	       && regno_ok_for_base_p (regno, mode, as, MEM, SCRATCH)
 	       && ! regno_clobbered_p (regno, this_insn, mode, 0))
 	return 0;
 
       /* If we do not have one of the cases above, we must do the reload.  */
-      push_reload (ad, NULL_RTX, loc, (rtx*) 0, base_reg_class (mode, MEM, SCRATCH),
+      push_reload (ad, NULL_RTX, loc, (rtx*) 0,
+		   base_reg_class (mode, as, MEM, SCRATCH),
 		   GET_MODE (ad), VOIDmode, 0, 0, opnum, type);
       return 1;
     }
@@ -5057,7 +5057,7 @@ find_reloads_address (enum machine_mode
 	  /* Must use TEM here, not AD, since it is the one that will
 	     have any subexpressions reloaded, if needed.  */
 	  push_reload (tem, NULL_RTX, loc, (rtx*) 0,
-		       base_reg_class (mode, MEM, SCRATCH), GET_MODE (tem),
+		       base_reg_class (mode, as, MEM, SCRATCH), GET_MODE (tem),
 		       VOIDmode, 0,
 		       0, opnum, type);
 	  return ! removed_and;
@@ -5075,7 +5075,7 @@ find_reloads_address (enum machine_mode
 	   && REG_P (XEXP (ad, 0))
 	   && REGNO (XEXP (ad, 0)) < FIRST_PSEUDO_REGISTER
 	   && CONST_INT_P (XEXP (ad, 1))
-	   && (regno_ok_for_base_p (REGNO (XEXP (ad, 0)), mode, PLUS,
+	   && (regno_ok_for_base_p (REGNO (XEXP (ad, 0)), mode, as, PLUS,
 				    CONST_INT)
 	       /* Similarly, if we were to reload the base register and the
 		  mem+offset address is still invalid, then we want to reload
@@ -5094,7 +5094,7 @@ find_reloads_address (enum machine_mode
 	}
 
       if (double_reg_address_ok
-	  && regno_ok_for_base_p (REGNO (XEXP (ad, 0)), mode,
+	  && regno_ok_for_base_p (REGNO (XEXP (ad, 0)), mode, as,
 				  PLUS, CONST_INT))
 	{
 	  /* Unshare the sum as well.  */
@@ -5113,7 +5113,7 @@ find_reloads_address (enum machine_mode
 	     reload the sum into a base reg.
 	     That will at least work.  */
 	  find_reloads_address_part (ad, loc,
-				     base_reg_class (mode, MEM, SCRATCH),
+				     base_reg_class (mode, as, MEM, SCRATCH),
 				     GET_MODE (ad), opnum, type, ind_levels);
 	}
       return ! removed_and;
@@ -5165,7 +5165,7 @@ find_reloads_address (enum machine_mode
 
       addend = XEXP (XEXP (ad, 0), 1 - op_index);
 
-      if ((regno_ok_for_base_p (REGNO (operand), mode, inner_code,
+      if ((regno_ok_for_base_p (REGNO (operand), mode, as, inner_code,
 				GET_CODE (addend))
 	   || operand == frame_pointer_rtx
 #if !HARD_FRAME_POINTER_IS_FRAME_POINTER
@@ -5194,11 +5194,11 @@ find_reloads_address (enum machine_mode
 				 op_index == 0 ? addend : offset_reg);
 	  *loc = ad;
 
-	  cls = base_reg_class (mode, MEM, GET_CODE (addend));
+	  cls = base_reg_class (mode, as, MEM, GET_CODE (addend));
 	  find_reloads_address_part (XEXP (ad, op_index),
 				     &XEXP (ad, op_index), cls,
 				     GET_MODE (ad), opnum, type, ind_levels);
-	  find_reloads_address_1 (mode,
+	  find_reloads_address_1 (mode, as,
 				  XEXP (ad, 1 - op_index), 1, GET_CODE (ad),
 				  GET_CODE (XEXP (ad, op_index)),
 				  &XEXP (ad, 1 - op_index), opnum,
@@ -5251,13 +5251,14 @@ find_reloads_address (enum machine_mode
 	    loc = &XEXP (*loc, 0);
 	}
 
-      find_reloads_address_part (ad, loc, base_reg_class (mode, MEM, SCRATCH),
+      find_reloads_address_part (ad, loc,
+				 base_reg_class (mode, as, MEM, SCRATCH),
 				 address_mode, opnum, type, ind_levels);
       return ! removed_and;
     }
 
-  return find_reloads_address_1 (mode, ad, 0, MEM, SCRATCH, loc, opnum, type,
-				 ind_levels, insn);
+  return find_reloads_address_1 (mode, as, ad, 0, MEM, SCRATCH, loc,
+				 opnum, type, ind_levels, insn);
 }
 \f
 /* Find all pseudo regs appearing in AD
@@ -5490,14 +5491,15 @@ update_auto_inc_notes (rtx insn ATTRIBUT
    handles those cases gracefully.  */
 
 static int
-find_reloads_address_1 (enum machine_mode mode, rtx x, int context,
+find_reloads_address_1 (enum machine_mode mode, addr_space_t as,
+			rtx x, int context,
 			enum rtx_code outer_code, enum rtx_code index_code,
 			rtx *loc, int opnum, enum reload_type type,
 			int ind_levels, rtx insn)
 {
-#define REG_OK_FOR_CONTEXT(CONTEXT, REGNO, MODE, OUTER, INDEX)		\
+#define REG_OK_FOR_CONTEXT(CONTEXT, REGNO, MODE, AS, OUTER, INDEX)	\
   ((CONTEXT) == 0							\
-   ? regno_ok_for_base_p (REGNO, MODE, OUTER, INDEX)			\
+   ? regno_ok_for_base_p (REGNO, MODE, AS, OUTER, INDEX)		\
    : REGNO_OK_FOR_INDEX_P (REGNO))
 
   enum reg_class context_reg_class;
@@ -5506,7 +5508,7 @@ find_reloads_address_1 (enum machine_mod
   if (context == 1)
     context_reg_class = INDEX_REG_CLASS;
   else
-    context_reg_class = base_reg_class (mode, outer_code, index_code);
+    context_reg_class = base_reg_class (mode, as, outer_code, index_code);
 
   switch (code)
     {
@@ -5563,10 +5565,10 @@ find_reloads_address_1 (enum machine_mod
 	if (code0 == MULT || code0 == SIGN_EXTEND || code0 == TRUNCATE
 	    || code0 == ZERO_EXTEND || code1 == MEM)
 	  {
-	    find_reloads_address_1 (mode, orig_op0, 1, PLUS, SCRATCH,
+	    find_reloads_address_1 (mode, as, orig_op0, 1, PLUS, SCRATCH,
 				    &XEXP (x, 0), opnum, type, ind_levels,
 				    insn);
-	    find_reloads_address_1 (mode, orig_op1, 0, PLUS, code0,
+	    find_reloads_address_1 (mode, as, orig_op1, 0, PLUS, code0,
 				    &XEXP (x, 1), opnum, type, ind_levels,
 				    insn);
 	  }
@@ -5574,56 +5576,56 @@ find_reloads_address_1 (enum machine_mod
 	else if (code1 == MULT || code1 == SIGN_EXTEND || code1 == TRUNCATE
 		 || code1 == ZERO_EXTEND || code0 == MEM)
 	  {
-	    find_reloads_address_1 (mode, orig_op0, 0, PLUS, code1,
+	    find_reloads_address_1 (mode, as, orig_op0, 0, PLUS, code1,
 				    &XEXP (x, 0), opnum, type, ind_levels,
 				    insn);
-	    find_reloads_address_1 (mode, orig_op1, 1, PLUS, SCRATCH,
+	    find_reloads_address_1 (mode, as, orig_op1, 1, PLUS, SCRATCH,
 				    &XEXP (x, 1), opnum, type, ind_levels,
 				    insn);
 	  }
 
 	else if (code0 == CONST_INT || code0 == CONST
 		 || code0 == SYMBOL_REF || code0 == LABEL_REF)
-	  find_reloads_address_1 (mode, orig_op1, 0, PLUS, code0,
+	  find_reloads_address_1 (mode, as, orig_op1, 0, PLUS, code0,
 				  &XEXP (x, 1), opnum, type, ind_levels,
 				  insn);
 
 	else if (code1 == CONST_INT || code1 == CONST
 		 || code1 == SYMBOL_REF || code1 == LABEL_REF)
-	  find_reloads_address_1 (mode, orig_op0, 0, PLUS, code1,
+	  find_reloads_address_1 (mode, as, orig_op0, 0, PLUS, code1,
 				  &XEXP (x, 0), opnum, type, ind_levels,
 				  insn);
 
 	else if (code0 == REG && code1 == REG)
 	  {
 	    if (REGNO_OK_FOR_INDEX_P (REGNO (op1))
-		&& regno_ok_for_base_p (REGNO (op0), mode, PLUS, REG))
+		&& regno_ok_for_base_p (REGNO (op0), mode, as, PLUS, REG))
 	      return 0;
 	    else if (REGNO_OK_FOR_INDEX_P (REGNO (op0))
-		     && regno_ok_for_base_p (REGNO (op1), mode, PLUS, REG))
+		     && regno_ok_for_base_p (REGNO (op1), mode, as, PLUS, REG))
 	      return 0;
-	    else if (regno_ok_for_base_p (REGNO (op0), mode, PLUS, REG))
-	      find_reloads_address_1 (mode, orig_op1, 1, PLUS, SCRATCH,
+	    else if (regno_ok_for_base_p (REGNO (op0), mode, as, PLUS, REG))
+	      find_reloads_address_1 (mode, as, orig_op1, 1, PLUS, SCRATCH,
 				      &XEXP (x, 1), opnum, type, ind_levels,
 				      insn);
 	    else if (REGNO_OK_FOR_INDEX_P (REGNO (op1)))
-	      find_reloads_address_1 (mode, orig_op0, 0, PLUS, REG,
+	      find_reloads_address_1 (mode, as, orig_op0, 0, PLUS, REG,
 				      &XEXP (x, 0), opnum, type, ind_levels,
 				      insn);
-	    else if (regno_ok_for_base_p (REGNO (op1), mode, PLUS, REG))
-	      find_reloads_address_1 (mode, orig_op0, 1, PLUS, SCRATCH,
+	    else if (regno_ok_for_base_p (REGNO (op1), mode, as, PLUS, REG))
+	      find_reloads_address_1 (mode, as, orig_op0, 1, PLUS, SCRATCH,
 				      &XEXP (x, 0), opnum, type, ind_levels,
 				      insn);
 	    else if (REGNO_OK_FOR_INDEX_P (REGNO (op0)))
-	      find_reloads_address_1 (mode, orig_op1, 0, PLUS, REG,
+	      find_reloads_address_1 (mode, as, orig_op1, 0, PLUS, REG,
 				      &XEXP (x, 1), opnum, type, ind_levels,
 				      insn);
 	    else
 	      {
-		find_reloads_address_1 (mode, orig_op0, 0, PLUS, REG,
+		find_reloads_address_1 (mode, as, orig_op0, 0, PLUS, REG,
 					&XEXP (x, 0), opnum, type, ind_levels,
 					insn);
-		find_reloads_address_1 (mode, orig_op1, 1, PLUS, SCRATCH,
+		find_reloads_address_1 (mode, as, orig_op1, 1, PLUS, SCRATCH,
 					&XEXP (x, 1), opnum, type, ind_levels,
 					insn);
 	      }
@@ -5631,20 +5633,20 @@ find_reloads_address_1 (enum machine_mod
 
 	else if (code0 == REG)
 	  {
-	    find_reloads_address_1 (mode, orig_op0, 1, PLUS, SCRATCH,
+	    find_reloads_address_1 (mode, as, orig_op0, 1, PLUS, SCRATCH,
 				    &XEXP (x, 0), opnum, type, ind_levels,
 				    insn);
-	    find_reloads_address_1 (mode, orig_op1, 0, PLUS, REG,
+	    find_reloads_address_1 (mode, as, orig_op1, 0, PLUS, REG,
 				    &XEXP (x, 1), opnum, type, ind_levels,
 				    insn);
 	  }
 
 	else if (code1 == REG)
 	  {
-	    find_reloads_address_1 (mode, orig_op1, 1, PLUS, SCRATCH,
+	    find_reloads_address_1 (mode, as, orig_op1, 1, PLUS, SCRATCH,
 				    &XEXP (x, 1), opnum, type, ind_levels,
 				    insn);
-	    find_reloads_address_1 (mode, orig_op0, 0, PLUS, REG,
+	    find_reloads_address_1 (mode, as, orig_op0, 0, PLUS, REG,
 				    &XEXP (x, 0), opnum, type, ind_levels,
 				    insn);
 	  }
@@ -5686,7 +5688,7 @@ find_reloads_address_1 (enum machine_mod
 	if ((REG_P (XEXP (op1, 1))
 	     && !REGNO_OK_FOR_INDEX_P (REGNO (XEXP (op1, 1))))
 	    || GET_CODE (XEXP (op1, 1)) == PLUS)
-	  find_reloads_address_1 (mode, XEXP (op1, 1), 1, code, SCRATCH,
+	  find_reloads_address_1 (mode, as, XEXP (op1, 1), 1, code, SCRATCH,
 				  &XEXP (op1, 1), opnum, RELOAD_OTHER,
 				  ind_levels, insn);
 
@@ -5728,8 +5730,8 @@ find_reloads_address_1 (enum machine_mod
 		   register.  */
 		reloadnum = push_reload (tem, tem, &XEXP (x, 0),
 					 &XEXP (op1, 0),
-					 base_reg_class (mode, code,
-							 index_code),
+					 base_reg_class (mode, as,
+							 code, index_code),
 					 GET_MODE (x), GET_MODE (x), 0,
 					 0, opnum, RELOAD_OTHER);
 
@@ -5742,11 +5744,12 @@ find_reloads_address_1 (enum machine_mod
 	  regno = reg_renumber[regno];
 
 	/* We require a base register here...  */
-	if (!regno_ok_for_base_p (regno, GET_MODE (x), code, index_code))
+	if (!regno_ok_for_base_p (regno, GET_MODE (x), as, code, index_code))
 	  {
 	    reloadnum = push_reload (XEXP (op1, 0), XEXP (x, 0),
 				     &XEXP (op1, 0), &XEXP (x, 0),
-				     base_reg_class (mode, code, index_code),
+				     base_reg_class (mode, as,
+						     code, index_code),
 				     GET_MODE (x), GET_MODE (x), 0, 0,
 				     opnum, RELOAD_OTHER);
 
@@ -5812,7 +5815,7 @@ find_reloads_address_1 (enum machine_mod
 	  if (reg_renumber[regno] >= 0)
 	    regno = reg_renumber[regno];
 	  if (regno >= FIRST_PSEUDO_REGISTER
-	      || !REG_OK_FOR_CONTEXT (context, regno, mode, code,
+	      || !REG_OK_FOR_CONTEXT (context, regno, mode, as, code,
 				      index_code))
 	    {
 	      int reloadnum;
@@ -5881,7 +5884,7 @@ find_reloads_address_1 (enum machine_mod
 	 reloaded.  Targets that are better off reloading just either part
 	 (or perhaps even a different part of an outer expression), should
 	 define LEGITIMIZE_RELOAD_ADDRESS.  */
-      find_reloads_address_1 (GET_MODE (XEXP (x, 0)), XEXP (x, 0),
+      find_reloads_address_1 (GET_MODE (XEXP (x, 0)), as, XEXP (x, 0),
 			      context, code, SCRATCH, &XEXP (x, 0), opnum,
 			      type, ind_levels, insn);
       push_reload (x, NULL_RTX, loc, (rtx*) 0,
@@ -5952,7 +5955,7 @@ find_reloads_address_1 (enum machine_mod
 	  regno = reg_renumber[regno];
 
 	if (regno >= FIRST_PSEUDO_REGISTER
-	    || !REG_OK_FOR_CONTEXT (context, regno, mode, outer_code,
+	    || !REG_OK_FOR_CONTEXT (context, regno, mode, as, outer_code,
 				    index_code))
 	  {
 	    push_reload (x, NULL_RTX, loc, (rtx*) 0,
@@ -5985,7 +5988,7 @@ find_reloads_address_1 (enum machine_mod
 	    {
 	      int regno ATTRIBUTE_UNUSED = subreg_regno (x);
 
-	      if (!REG_OK_FOR_CONTEXT (context, regno, mode, outer_code,
+	      if (!REG_OK_FOR_CONTEXT (context, regno, mode, as, outer_code,
 				       index_code))
 		{
 		  push_reload (x, NULL_RTX, loc, (rtx*) 0,
@@ -6026,8 +6029,9 @@ find_reloads_address_1 (enum machine_mod
 	if (fmt[i] == 'e')
 	  /* Pass SCRATCH for INDEX_CODE, since CODE can never be a PLUS once
 	     we get here.  */
-	  find_reloads_address_1 (mode, XEXP (x, i), context, code, SCRATCH,
-				  &XEXP (x, i), opnum, type, ind_levels, insn);
+	  find_reloads_address_1 (mode, as, XEXP (x, i), context,
+				  code, SCRATCH, &XEXP (x, i),
+				  opnum, type, ind_levels, insn);
       }
   }
 
@@ -6204,7 +6208,9 @@ find_reloads_subreg_address (rtx x, int
 			 MEM_ADDR_SPACE (reg_equiv_mem (regno))))
 		{
 		  push_reload (XEXP (tem, 0), NULL_RTX, &XEXP (tem, 0), (rtx*) 0,
-			       base_reg_class (GET_MODE (tem), MEM, SCRATCH),
+			       base_reg_class (GET_MODE (tem),
+					       MEM_ADDR_SPACE (tem),
+					       MEM, SCRATCH),
 			       GET_MODE (XEXP (tem, 0)), VOIDmode, 0, 0,
 			       opnum, type);
 		  reloaded = 1;
Index: addresses.h
===================================================================
--- addresses.h	(revision 179594)
+++ addresses.h	(working copy)
@@ -23,11 +23,12 @@ along with GCC; see the file COPYING3.
 
 static inline enum reg_class
 base_reg_class (enum machine_mode mode ATTRIBUTE_UNUSED,
+		addr_space_t as ATTRIBUTE_UNUSED,
 		enum rtx_code outer_code ATTRIBUTE_UNUSED,
 		enum rtx_code index_code ATTRIBUTE_UNUSED)
 {
 #ifdef MODE_CODE_BASE_REG_CLASS
-  return MODE_CODE_BASE_REG_CLASS (mode, outer_code, index_code);
+  return MODE_CODE_BASE_REG_CLASS (mode, as, outer_code, index_code);
 #else
 #ifdef MODE_BASE_REG_REG_CLASS
   if (index_code == REG)
@@ -49,11 +50,13 @@ base_reg_class (enum machine_mode mode A
 static inline bool
 ok_for_base_p_1 (unsigned regno ATTRIBUTE_UNUSED,
 		 enum machine_mode mode ATTRIBUTE_UNUSED,
+		 addr_space_t as ATTRIBUTE_UNUSED,
 		 enum rtx_code outer_code ATTRIBUTE_UNUSED,
 		 enum rtx_code index_code ATTRIBUTE_UNUSED)
 {
 #ifdef REGNO_MODE_CODE_OK_FOR_BASE_P
-  return REGNO_MODE_CODE_OK_FOR_BASE_P (regno, mode, outer_code, index_code);
+  return REGNO_MODE_CODE_OK_FOR_BASE_P (regno, mode, as,
+					outer_code, index_code);
 #else
 #ifdef REGNO_MODE_OK_FOR_REG_BASE_P
   if (index_code == REG)
@@ -71,11 +74,11 @@ ok_for_base_p_1 (unsigned regno ATTRIBUT
    complete.  Arguments as for the called function.  */
 
 static inline bool
-regno_ok_for_base_p (unsigned regno, enum machine_mode mode,
+regno_ok_for_base_p (unsigned regno, enum machine_mode mode, addr_space_t as,
 		     enum rtx_code outer_code, enum rtx_code index_code)
 {
   if (regno >= FIRST_PSEUDO_REGISTER && reg_renumber[regno] >= 0)
     regno = reg_renumber[regno];
 
-  return ok_for_base_p_1 (regno, mode, outer_code, index_code);
+  return ok_for_base_p_1 (regno, mode, as, outer_code, index_code);
 }

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [Patch]: PR49868: Named address space support for AVR
  2011-10-06 15:16 [Patch]: PR49868: Named address space support for AVR Georg-Johann Lay
@ 2011-10-28 11:00 ` Georg-Johann Lay
  2011-10-28 11:21   ` Georg-Johann Lay
  2011-10-28 11:35   ` Denis Chertykov
  0 siblings, 2 replies; 11+ messages in thread
From: Georg-Johann Lay @ 2011-10-28 11:00 UTC (permalink / raw)
  To: gcc-patches
  Cc: Ulrich Weigand, Denis Chertykov, Eric Weddington, Joerg Wunsch,
	Anatoly Sokolov, Ulrich Weigand

[-- Attachment #1: Type: text/plain, Size: 4680 bytes --]

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.

[-- Attachment #2: pgm-v7-avr.diff --]
[-- Type: text/x-patch, Size: 34556 bytes --]

Index: config/avr/avr.md
===================================================================
--- config/avr/avr.md	(revision 180605)
+++ config/avr/avr.md	(working copy)
@@ -42,6 +42,7 @@ (define_constants
    (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
    
@@ -323,11 +324,17 @@ (define_expand "movqi"
   [(set (match_operand:QI 0 "nonimmediate_operand" "")
 	(match_operand:QI 1 "general_operand" ""))]
   ""
-  "/* One of the ops has to be in a register.  */
-   if (!register_operand(operand0, QImode)
-       && ! (register_operand(operand1, QImode) || const0_rtx == operand1))
-       operands[1] = copy_to_mode_reg(QImode, operand1);
-  ")
+  {
+    if (avr_mem_pgm_p (operands[0]))
+      DONE;
+
+    /* One of the operands has to be in a register.  */
+    if (!register_operand (operands[0], QImode)
+        && !(register_operand (operands[1], QImode) || const0_rtx == operands[1]))
+      {
+        operands[1] = copy_to_mode_reg (QImode, operands[1]);
+      }
+  })
 
 (define_insn "movqi_insn"
   [(set (match_operand:QI 0 "nonimmediate_operand" "=r,d,Qm,r,q,r,*r")
@@ -370,15 +377,17 @@ (define_expand "movhi"
   [(set (match_operand:HI 0 "nonimmediate_operand" "")
         (match_operand:HI 1 "general_operand"       ""))]
   ""
-  "
-{
-   /* One of the ops has to be in a register.  */
-  if (!register_operand(operand0, HImode)
-      && !(register_operand(operand1, HImode) || const0_rtx == operands[1]))
-    {
-      operands[1] = copy_to_mode_reg(HImode, operand1);
-    }
-}")
+  {
+    if (avr_mem_pgm_p (operands[0]))
+      DONE;
+
+    /* One of the operands has to be in a register.  */
+    if (!register_operand (operands[0], HImode)
+        && !(register_operand (operands[1], HImode) || const0_rtx == operands[1]))
+      {
+        operands[1] = copy_to_mode_reg (HImode, operands[1]);
+      }
+  })
 
 (define_insn "movhi_sp_r_irq_off"
   [(set (match_operand:HI 0 "stack_register_operand" "=q")
@@ -463,6 +472,39 @@ (define_peephole2 ; movw_r
     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);
+  })
 ;;==========================================================================
 ;; move double word (32 bit)
 
@@ -470,15 +512,17 @@ (define_expand "movsi"
   [(set (match_operand:SI 0 "nonimmediate_operand" "")
         (match_operand:SI 1 "general_operand"  ""))]
   ""
-  "
-{
-  /* One of the ops has to be in a register.  */
-  if (!register_operand (operand0, SImode)
-      && !(register_operand (operand1, SImode) || const0_rtx == operand1))
-    {
-      operands[1] = copy_to_mode_reg (SImode, operand1);
-    }
-}")
+  {
+    if (avr_mem_pgm_p (operands[0]))
+      DONE;
+  
+    /* One of the operands has to be in a register.  */
+    if (!register_operand (operands[0], SImode)
+        && !(register_operand (operands[1], SImode) || const0_rtx == operands[1]))
+      {
+        operands[1] = copy_to_mode_reg (SImode, operands[1]);
+      }
+  })
 
 
 
@@ -526,15 +570,17 @@ (define_expand "movsf"
   [(set (match_operand:SF 0 "nonimmediate_operand" "")
         (match_operand:SF 1 "general_operand"  ""))]
   ""
-  "
-{
-  /* One of the ops has to be in a register.  */
-  if (!register_operand (operand1, SFmode)
-      && !register_operand (operand0, SFmode))
-    {
-      operands[1] = copy_to_mode_reg (SFmode, operand1);
-    }
-}")
+  {
+    if (avr_mem_pgm_p (operands[0]))
+      DONE;
+
+    /* One of the operands has to be in a register.  */
+    if (!register_operand (operands[1], SFmode)
+        && !register_operand (operands[0], SFmode))
+      {
+        operands[1] = copy_to_mode_reg (SFmode, operands[1]);
+      }
+  })
 
 (define_insn "*movsf"
   [(set (match_operand:SF 0 "nonimmediate_operand" "=r,r,r,Qm,!d,r")
@@ -589,7 +635,7 @@ (define_expand "movmemhi"
   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));
@@ -598,6 +644,9 @@ (define_expand "movmemhi"
   /* 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;
 
@@ -618,7 +667,9 @@ (define_expand "movmemhi"
   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.  */
Index: config/avr/avr-log.c
===================================================================
--- config/avr/avr-log.c	(revision 180605)
+++ config/avr/avr-log.c	(working copy)
@@ -318,12 +318,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
     }
Index: config/avr/avr-c.c
===================================================================
--- config/avr/avr-c.c	(revision 180605)
+++ config/avr/avr-c.c	(working copy)
@@ -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,17 @@
 #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);
+}
 
-/* Not included in avr.c since this requires C front end.  */
 
 /* Worker function for TARGET_CPU_CPP_BUILTINS.  */
 
@@ -90,6 +100,17 @@ avr_cpu_cpp_builtins (struct cpp_reader
         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");
+    }
+
   /* Define builtin macros so that the user can
      easily query if or if not a specific builtin
      is available. */
Index: config/avr/avr-protos.h
===================================================================
--- config/avr/avr-protos.h	(revision 180605)
+++ config/avr/avr-protos.h	(working copy)
@@ -32,6 +32,7 @@ extern int avr_initial_elimination_offse
 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);
 
 #ifdef TREE_CODE
 extern void avr_asm_output_aligned_decl_common (FILE*, const_tree, const char*, unsigned HOST_WIDE_INT, unsigned int, bool);
@@ -107,10 +108,11 @@ extern int avr_simplify_comparison_p (en
 extern RTX_CODE avr_normalize_condition (RTX_CODE condition);
 extern void out_shift_with_cnt (const char *templ, rtx insn,
 				rtx operands[], int *len, int t_len);
-extern reg_class_t avr_mode_code_base_reg_class (enum machine_mode, RTX_CODE, RTX_CODE);
-extern bool avr_regno_mode_code_ok_for_base_p (int, enum machine_mode, RTX_CODE, RTX_CODE);
+extern reg_class_t avr_mode_code_base_reg_class (enum machine_mode, addr_space_t, RTX_CODE, RTX_CODE);
+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);
 #endif /* RTX_CODE */
 
 #ifdef REAL_VALUE_TYPE
@@ -129,12 +131,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;
Index: config/avr/avr.c
===================================================================
--- config/avr/avr.c	(revision 180605)
+++ config/avr/avr.c	(working copy)
@@ -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"
@@ -83,6 +84,12 @@ static bool avr_rtx_costs (rtx, int, int
 /* 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;
 
@@ -171,9 +178,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 +192,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
@@ -217,6 +218,24 @@ bool avr_need_copy_data_p = false;
 #undef TARGET_ASM_FUNCTION_RODATA_SECTION
 #define TARGET_ASM_FUNCTION_RODATA_SECTION avr_asm_function_rodata_section
 
+#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
+
 \f
 
 /* Custom function to replace string prefix.
@@ -319,6 +338,8 @@ avr_option_override (void)
   avr_current_arch = &avr_arch_types[avr_current_device->arch];
   avr_extra_arch_macro = avr_current_device->macro;
 
+  lpm_addr_reg_rtx = gen_rtx_REG (Pmode, REG_Z);
+  lpm_reg_rtx  = gen_rtx_REG (QImode, LPM_REGNO);
   tmp_reg_rtx  = gen_rtx_REG (QImode, TMP_REGNO);
   zero_reg_rtx = gen_rtx_REG (QImode, ZERO_REGNO);
 
@@ -369,6 +390,30 @@ avr_regno_reg_class (int r)
   return ALL_REGS;
 }
 
+
+/* Return TRUE if DECL is a VAR_DECL which is located in AS1
+   and FALSE, otherwise.  */
+
+static bool
+avr_decl_pgm_p (tree decl)
+{
+  if (TREE_CODE (decl) != VAR_DECL)
+    return false;
+
+  return (ADDR_SPACE_PGM == TYPE_ADDR_SPACE (TREE_TYPE (decl)));
+}
+
+
+/* Return TRUE if X is a MEM rtx located in AS1 and FALSE, otherwise.  */
+
+bool
+avr_mem_pgm_p (rtx x)
+{
+  return (MEM_P (x)
+          && ADDR_SPACE_PGM == MEM_ADDR_SPACE (x));
+}
+
+
 /* A helper for the subsequent function attribute used to dig for
    attribute 'name' in a FUNCTION_DECL or FUNCTION_TYPE */
 
@@ -1156,19 +1201,19 @@ avr_cannot_modify_jumps_p (void)
 /* Helper function for `avr_legitimate_address_p'.  */
 
 static inline bool
-avr_reg_ok_for_addr_p (rtx reg, addr_space_t as ATTRIBUTE_UNUSED,
+avr_reg_ok_for_addr_p (rtx reg, addr_space_t as,
                        RTX_CODE outer_code, bool strict)
 {
   return (REG_P (reg)
-          && (avr_regno_mode_code_ok_for_base_p (REGNO (reg),
+          && (avr_regno_mode_code_ok_for_base_p (REGNO (reg), as,
                                                  QImode, outer_code, UNKNOWN)
               || (!strict
                   && REGNO (reg) >= FIRST_PSEUDO_REGISTER)));
 }
 
 
-/* Return nonzero if X (an RTX) is a legitimate memory address on the target
-   machine for a memory operand of mode MODE.  */
+/* Former implementation of TARGET_LEGITIMATE_ADDRESS_P,
+   now only a helper for avr_addr_space_legitimate_address_p.  */
 
 static bool
 avr_legitimate_address_p (enum machine_mode mode, rtx x, bool strict)
@@ -1254,6 +1299,9 @@ avr_legitimate_address_p (enum machine_m
   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  */
 
@@ -1377,7 +1425,7 @@ avr_legitimize_reload_address (rtx *px,
 
 
 /* Helper function to print assembler resp. track instruction
-   sequence lengths.
+   sequence lengths.  Always returns "".
    
    If PLEN == NULL:
        Output assembler code from template TPL with operands supplied
@@ -1389,7 +1437,7 @@ avr_legitimize_reload_address (rtx *px,
        Don't output anything.
 */
 
-static void
+static const char*
 avr_asm_len (const char* tpl, rtx* operands, int* plen, int n_words)
 {
   if (NULL == plen)
@@ -1403,6 +1451,8 @@ avr_asm_len (const char* tpl, rtx* opera
       else
         *plen += n_words;
     }
+
+  return "";
 }
 
 
@@ -2047,6 +2097,263 @@ avr_function_ok_for_sibcall (tree decl_c
 /***********************************************************************
   Functions for outputting various mov's for a various modes
 ************************************************************************/
+
+
+/* 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 src = xop[1];
+  rtx addr;
+  int n_bytes = GET_MODE_SIZE (GET_MODE (dest));
+  int regno_dest;
+
+  addr = XEXP (src, 0);
+  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 ("lpm" CR_TAB
+                              "mov %0,%3", xop, plen, -2);
+
+        case 2:
+          if (REGNO (dest) == REG_Z)
+            return avr_asm_len ("lpm"        CR_TAB
+                                "push %3"    CR_TAB
+                                "adiw %2,1"  CR_TAB
+                                "lpm"        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);
+                
+              if (!reg_unused_after (insn, addr))
+                avr_asm_len ("sbiw %2,1", xop, plen, 1);
+            }
+          
+          break; /* 2 */
+
+        case 4:
+          avr_asm_len ("lpm"        CR_TAB
+                       "mov %A0,%3" CR_TAB
+                       "adiw %2,1"  CR_TAB
+                       "lpm"        CR_TAB
+                       "mov %B0,%3" CR_TAB
+                       "adiw %2,1", xop, plen, -6);
+          
+          if (REGNO (dest) == REG_Z - 2)
+            return avr_asm_len ("lpm"        CR_TAB
+                                "push %3"    CR_TAB
+                                "adiw %2,1"  CR_TAB
+                                "lpm"        CR_TAB
+                                "mov %D0,%3" CR_TAB
+                                "pop %C0", xop, plen, 6);
+          else
+            {
+              avr_asm_len ("lpm"        CR_TAB
+                           "mov %C0,%3" CR_TAB
+                           "adiw %2,1"  CR_TAB
+                           "lpm"        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)));
+      
+      switch (n_bytes)
+        {
+        default:
+          gcc_unreachable();
+
+        case 1:
+          return avr_asm_len ("lpm"        CR_TAB
+                              "mov %A0,%3" CR_TAB
+                              "adiw %2,1", xop, plen, -3);
+          
+        case 2:
+          return avr_asm_len ("lpm"        CR_TAB
+                              "mov %A0,%3" CR_TAB
+                              "adiw %2,1"  CR_TAB
+                              "lpm"        CR_TAB
+                              "mov %B0,%3" CR_TAB
+                              "adiw %2,1", xop, plen, -6);
+          
+        case 4:
+          return avr_asm_len ("lpm"        CR_TAB
+                              "mov %A0,%3" CR_TAB
+                              "adiw %2,1"  CR_TAB
+                              "lpm"        CR_TAB
+                              "mov %B0,%3" CR_TAB
+                              "adiw %2,1"  CR_TAB
+                              "lpm"        CR_TAB
+                              "mov %C0,%3" CR_TAB
+                              "adiw %2,1"  CR_TAB
+                              "lpm"        CR_TAB
+                              "mov %D0,%3" CR_TAB
+                              "adiw %2,1", xop, plen, -12);
+        } /* switch (n_bytes) */
+    } /* 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[4];
+  rtx dest = op[0];
+  rtx src = op[1];
+  rtx addr;
+  int n_bytes = GET_MODE_SIZE (GET_MODE (dest));
+  int regno_dest;
+
+  if (plen)
+    *plen = 0;
+  
+  if (MEM_P (dest))
+    {
+      warning (0, "writing to address space %qs not supported",
+               c_addr_space_name (ADDR_SPACE_PGM));
+      
+      return "";
+    }
+
+  gcc_assert (REG_P (dest)
+              && avr_mem_pgm_p (src));
+
+  xop[0] = op[0];
+  xop[1] = op[1];
+  xop[2] = lpm_addr_reg_rtx;
+
+  if (!AVR_HAVE_LPMX)
+    return avr_out_lpm_no_lpmx (insn, xop, plen);
+
+  addr = XEXP (src, 0);
+  regno_dest = REGNO (dest);
+
+  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 ("lpm %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);
+          else
+            {
+              avr_asm_len ("lpm %A0,%a2+" CR_TAB
+                           "lpm %B0,%a2", xop, plen, -2);
+                
+              if (!reg_unused_after (insn, addr))
+                avr_asm_len ("sbiw %2,1", xop, plen, 1);
+            }
+          
+          break; /* 2 */
+
+        case 4:
+
+          avr_asm_len ("lpm %A0,%a2+" CR_TAB
+                       "lpm %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);
+          else
+            {
+              avr_asm_len ("lpm %C0,%a2+" CR_TAB
+                           "lpm %D0,%a2", xop, plen, 2);
+                
+              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)));
+      
+      switch (n_bytes)
+        {
+        default:
+          gcc_unreachable();
+
+        case 1:
+          return avr_asm_len ("lpm %A0,%a2+", xop, plen, -1);
+          
+        case 2:
+          return avr_asm_len ("lpm %A0,%a2+" CR_TAB
+                              "lpm %B0,%a2+", xop, plen, -2);
+          
+        case 4:
+          return avr_asm_len ("lpm %A0,%a2+" CR_TAB
+                              "lpm %B0,%a2+" CR_TAB
+                              "lpm %C0,%a2+" CR_TAB
+                              "lpm %D0,%a2+", xop, plen, -4);
+        } /* switch (n_bytes) */
+    } /* switch CODE (addr) */
+      
+  return "";
+}
+
+
 const char *
 output_movqi (rtx insn, rtx operands[], int *l)
 {
@@ -2055,6 +2362,12 @@ output_movqi (rtx insn, rtx operands[],
   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;
 
@@ -2147,6 +2460,12 @@ output_movhi (rtx insn, rtx operands[],
   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;
   
@@ -2760,6 +3079,12 @@ output_movsisf (rtx insn, rtx operands[]
   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;
   
@@ -5609,6 +5934,10 @@ avr_attribute_table[] =
 /* Look for attribute `progmem' in DECL
    if found return 1, otherwise 0.  */
 
+/* Return 2 if DECL is located in address space AS1.
+   Return 1 if attribute `progmem' occurs in DECL or ATTRIBUTES.
+   Return 0, otherwise.  */
+
 int
 avr_progmem_p (tree decl, tree attributes)
 {
@@ -5617,6 +5946,9 @@ avr_progmem_p (tree decl, tree attribute
   if (TREE_CODE (decl) != VAR_DECL)
     return 0;
 
+  if (avr_decl_pgm_p (decl))
+    return 2;
+
   if (NULL_TREE
       != lookup_attribute ("progmem", attributes))
     return 1;
@@ -5635,11 +5967,104 @@ avr_progmem_p (tree decl, tree attribute
   return 0;
 }
 
+
+/* Scan type TYP for pointer references to address space AS1.
+   Return TRUE if all pointers targeting AS1 are also declared
+   to be CONST and FALSE, otherwise.  */
+   
+static bool
+avr_pgm_pointer_const_p (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_pgm_pointer_const_p (TREE_TYPE (target));
+
+      /* "Ordinary" pointers... */
+      
+      return ((/* ...must point to generic address space...  */
+               ADDR_SPACE_GENERIC_P (TYPE_ADDR_SPACE (target))
+               /* ...or point to read-only locations.  */
+               || TYPE_READONLY (target))
+              /* Scan pointer's target type.  */
+              && avr_pgm_pointer_const_p (target));
+    }
+
+  return true;
+}
+
+
+/* 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;
+  
+  if (avr_log.progmem)
+    avr_edump ("%?: %t\n", node);
+  
+  switch (TREE_CODE (node))
+    {
+    default:
+      break;
+
+    case VAR_DECL:
+      if (!avr_pgm_pointer_const_p (TREE_TYPE (node)))
+        reason = "variable";
+      break;
+
+    case PARM_DECL:
+      if (!avr_pgm_pointer_const_p (TREE_TYPE (node)))
+        reason = "function parameter";
+      break;
+        
+    case FIELD_DECL:
+      if (!avr_pgm_pointer_const_p (TREE_TYPE (node)))
+        reason = "structure field";
+      break;
+        
+    case FUNCTION_DECL:
+      if (!avr_pgm_pointer_const_p (TREE_TYPE (TREE_TYPE (node))))
+        reason = "return type of function";
+      break;
+
+    case POINTER_TYPE:
+      if (!avr_pgm_pointer_const_p (node))
+        reason = "pointer";
+      break;
+    }
+
+  if (reason)
+    {
+      if (TYPE_P (node))
+        error ("pointer targeting address space %qs must be const in %qT",
+               c_addr_space_name (ADDR_SPACE_PGM), node);
+      else
+        error ("pointer targeting address space %qs must be const in %s %q+D",
+               c_addr_space_name (ADDR_SPACE_PGM), 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))
@@ -5656,11 +6081,20 @@ avr_insert_attributes (tree node, tree *
       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);
         }
     }
 }
@@ -5860,6 +6294,7 @@ avr_section_type_flags (tree decl, const
       && avr_progmem_p (decl, DECL_ATTRIBUTES (decl)))
     {
       flags &= ~SECTION_WRITE;
+      flags &= ~SECTION_BSS;
       flags |= AVR_SECTION_PROGMEM;
     }
   
@@ -7338,9 +7773,15 @@ avr_hard_regno_mode_ok (int regno, enum
 
 reg_class_t
 avr_mode_code_base_reg_class (enum machine_mode mode ATTRIBUTE_UNUSED,
-                              RTX_CODE outer_code,
+                              addr_space_t as,
+                              RTX_CODE outer_code ATTRIBUTE_UNUSED,
                               RTX_CODE index_code ATTRIBUTE_UNUSED)
 {
+  if (ADDR_SPACE_PGM == as)
+    {
+      return POINTER_Z_REGS;
+    }
+
   if (!avr_strict_X)
     return reload_completed ? BASE_POINTER_REGS : POINTER_REGS;
 
@@ -7353,11 +7794,33 @@ avr_mode_code_base_reg_class (enum machi
 bool
 avr_regno_mode_code_ok_for_base_p (int regno,
                                    enum machine_mode mode ATTRIBUTE_UNUSED,
-                                   RTX_CODE outer_code,
+                                   addr_space_t as,
+                                   RTX_CODE outer_code ATTRIBUTE_UNUSED,
                                    RTX_CODE index_code ATTRIBUTE_UNUSED)
 {
   bool ok = false;
   
+  if (ADDR_SPACE_PGM == 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
@@ -7885,6 +8348,140 @@ 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 Pmode;
+}
+
+
+/* Implement `'.  */
+
+static enum machine_mode
+avr_addr_space_pointer_mode (addr_space_t as ATTRIBUTE_UNUSED)
+{
+  return 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 ((!reload_completed || !reload_in_progress)
+      && 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;
+  
+  if (ADDR_SPACE_GENERIC_P (as))
+    return avr_legitimate_address_p (mode, x, strict);
+
+  gcc_assert (ADDR_SPACE_PGM == as);
+
+  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;
+    }
+
+  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 op, tree from_type ATTRIBUTE_UNUSED,
+                        tree to_type ATTRIBUTE_UNUSED)
+{
+  if (avr_log.progmem)
+    avr_edump ("\n%?: op=%r\nfrom=%t\nto=%t\n", op, from_type, to_type);
+  return op;
+}
+
+
+/* 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)
+{
+  return true;
+}
+
+
 /* Helper for __builtin_avr_delay_cycles */
 
 static void
Index: config/avr/avr.h
===================================================================
--- config/avr/avr.h	(revision 180605)
+++ config/avr/avr.h	(working copy)
@@ -308,13 +308,13 @@ enum reg_class {
 
 #define REGNO_REG_CLASS(R) avr_regno_reg_class(R)
 
-#define MODE_CODE_BASE_REG_CLASS(mode, outer_code, index_code) \
-  avr_mode_code_base_reg_class (mode, outer_code, index_code)
+#define MODE_CODE_BASE_REG_CLASS(mode, address_space, outer_code, index_code) \
+  avr_mode_code_base_reg_class (mode, address_space, outer_code, index_code)
 
 #define INDEX_REG_CLASS NO_REGS
 
-#define REGNO_MODE_CODE_OK_FOR_BASE_P(num, mode, outer_code, index_code) \
-  avr_regno_mode_code_ok_for_base_p (num, mode, outer_code, index_code)
+#define REGNO_MODE_CODE_OK_FOR_BASE_P(num, mode, address_space, outer_code, index_code) \
+  avr_regno_mode_code_ok_for_base_p (num, mode, address_space, outer_code, index_code)
 
 #define REGNO_OK_FOR_INDEX_P(NUM) 0
 
@@ -391,6 +391,14 @@ typedef struct avr_args {
 
 #define NO_FUNCTION_CSE
 
+
+#define ADDR_SPACE_PGM	1
+
+#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"

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [Patch]: PR49868: Named address space support for AVR
  2011-10-28 11:00 ` Georg-Johann Lay
@ 2011-10-28 11:21   ` Georg-Johann Lay
  2011-10-28 11:35   ` Denis Chertykov
  1 sibling, 0 replies; 11+ messages in thread
From: Georg-Johann Lay @ 2011-10-28 11:21 UTC (permalink / raw)
  To: gcc-patches; +Cc: Ulrich Weigand, Denis Chertykov, Eric Weddington

> The patch requires Ulrich's works which is still in review

Now the correct link:

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

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [Patch]: PR49868: Named address space support for AVR
  2011-10-28 11:00 ` Georg-Johann Lay
  2011-10-28 11:21   ` Georg-Johann Lay
@ 2011-10-28 11:35   ` Denis Chertykov
  2011-11-06 18:53     ` Georg-Johann Lay
  1 sibling, 1 reply; 11+ messages in thread
From: Denis Chertykov @ 2011-10-28 11:35 UTC (permalink / raw)
  To: Georg-Johann Lay
  Cc: gcc-patches, Ulrich Weigand, Eric Weddington, Joerg Wunsch,
	Anatoly Sokolov

2011/10/28 Georg-Johann Lay <avr@gjlay.de>:
> 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.

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [Patch]: PR49868: Named address space support for AVR
  2011-10-28 11:35   ` Denis Chertykov
@ 2011-11-06 18:53     ` Georg-Johann Lay
  2011-11-07 11:34       ` Georg-Johann Lay
  0 siblings, 1 reply; 11+ messages in thread
From: Georg-Johann Lay @ 2011-11-06 18:53 UTC (permalink / raw)
  To: Denis Chertykov
  Cc: gcc-patches, Ulrich Weigand, Eric Weddington, Joerg Wunsch,
	Anatoly Sokolov

[-- Attachment #1: Type: text/plain, Size: 11213 bytes --]

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.





[-- Attachment #2: pgmx-4.diff --]
[-- Type: text/x-patch, Size: 71952 bytes --]

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
+
 \f
 
 /* 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)")))

[-- Attachment #3: pgmx-4-libgcc.diff --]
[-- Type: text/x-patch, Size: 2927 bytes --]

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 */
+
 \f
 .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 \

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [Patch]: PR49868: Named address space support for AVR
  2011-11-06 18:53     ` Georg-Johann Lay
@ 2011-11-07 11:34       ` Georg-Johann Lay
  2011-11-07 13:53         ` Denis Chertykov
  0 siblings, 1 reply; 11+ messages in thread
From: Georg-Johann Lay @ 2011-11-07 11:34 UTC (permalink / raw)
  To: Denis Chertykov
  Cc: gcc-patches, Eric Weddington, Joerg Wunsch, Anatoly Sokolov

[-- Attachment #1: Type: text/plain, Size: 12948 bytes --]

Georg-Johann Lay wrote:
> 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.

This is a follow-up patch atop
http://gcc.gnu.org/ml/gcc-patches/2011-11/msg00806.html

Changes are:

* avr_out_lpm_no_lpmx must not reset *plen
* n_extendhipsi2 is rewritten so that hh8_operand is not needed any more
* fix an assertion in avr_out_lpm
* Rewrite of avr_find_unused_d_reg as follows

Johann

--

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;
}

[-- Attachment #2: pgmx-4-a.diff --]
[-- Type: text/x-patch, Size: 8092 bytes --]

diff --git a/config/avr/avr.c b/config/avr/avr.c
index 8a942c1..c12234e 100644
--- a/config/avr/avr.c
+++ b/config/avr/avr.c
@@ -2300,28 +2300,58 @@ avr_xload_libgcc_p (enum machine_mode mode)
 }
 
 
+/* 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 ++)
+  for (regno = 16; regno < 32; regno++)
     {
       rtx reg = all_regs_rtx[regno];
       
-      if (exclude
-          && reg_overlap_mentioned_p (exclude, reg))
+      if ((exclude
+           && reg_overlap_mentioned_p (exclude, reg))
+          || fixed_regs[regno])
         {
           continue;
         }
 
-      if (reg_unused_after (insn, reg))
-        return reg;
+      /* 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.  */
 
@@ -2354,7 +2384,7 @@ avr_out_lpm_no_lpmx (rtx insn, rtx *xop, int *plen)
 
         case 1:
           return avr_asm_len ("%4lpm" CR_TAB
-                              "mov %0,%3", xop, plen, -2);
+                              "mov %0,%3", xop, plen, 2);
 
         case 2:
           if (REGNO (dest) == REG_Z)
@@ -2363,14 +2393,14 @@ avr_out_lpm_no_lpmx (rtx insn, rtx *xop, int *plen)
                                 "adiw %2,1"  CR_TAB
                                 "%4lpm"      CR_TAB
                                 "mov %B0,%3" CR_TAB
-                                "pop %A0", xop, plen, -6);
+                                "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);
+                           "mov %B0,%3", xop, plen, 5);
                 
               if (!reg_unused_after (insn, addr))
                 avr_asm_len ("sbiw %2,1", xop, plen, 1);
@@ -2386,7 +2416,7 @@ avr_out_lpm_no_lpmx (rtx insn, rtx *xop, int *plen)
                        "mov %B0,%3" CR_TAB
                        "adiw %2,1"  CR_TAB
                        "%4lpm"      CR_TAB
-                       "mov %C0,%3", xop, plen, -8);
+                       "mov %C0,%3", xop, plen, 8);
           
           if (REGNO (dest) != REG_Z - 2
               || !reg_unused_after (insn, addr))
@@ -2402,7 +2432,7 @@ avr_out_lpm_no_lpmx (rtx insn, rtx *xop, int *plen)
                        "adiw %2,1"  CR_TAB
                        "%4lpm"      CR_TAB
                        "mov %B0,%3" CR_TAB
-                       "adiw %2,1", xop, plen, -6);
+                       "adiw %2,1", xop, plen, 6);
           
           if (REGNO (dest) == REG_Z - 2)
             return avr_asm_len ("%4lpm"      CR_TAB
@@ -2435,7 +2465,7 @@ avr_out_lpm_no_lpmx (rtx insn, rtx *xop, int *plen)
 
       avr_asm_len ("%4lpm"      CR_TAB
                    "mov %A0,%3" CR_TAB
-                   "adiw %2,1", xop, plen, -3);
+                   "adiw %2,1", xop, plen, 3);
 
       if (n_bytes >= 2)
         avr_asm_len ("%4lpm"      CR_TAB
@@ -2492,7 +2522,8 @@ avr_out_lpm (rtx insn, rtx *op, int *plen)
   segment = avr_pgm_segment (MEM_ADDR_SPACE (src));
 
   gcc_assert (REG_P (dest)
-              && ((REG_P (addr) && segment >= 0)
+              && ((segment >= 0
+                   && (REG_P (addr) || POST_INC == GET_CODE (addr)))
                   || (GET_CODE (addr) == LO_SUM && segment == -1)));
 
   if (segment == -1)
@@ -2515,6 +2546,8 @@ avr_out_lpm (rtx insn, rtx *op, int *plen)
 
   segment %= avr_current_arch->n_segments;
 
+  /* Set RAMPZ as needed.  */
+
   if (segment == 0)
     {
       xop[4] = xstring_empty;
@@ -9430,7 +9463,7 @@ avr_reg_ok_for_pgm_addr (rtx reg, bool strict)
   
   /* Avoid combine to propagate hard regs.  */
   
-  if (can_create_pseude_p()
+  if (can_create_pseudo_p()
       && REGNO (reg) < REG_Z)
     {
       return false;
@@ -9555,17 +9588,24 @@ avr_addr_space_convert (rtx src, tree type_from, tree type_to)
   if (as_from != ADDR_SPACE_PGMX
       && as_to == ADDR_SPACE_PGMX)
     {
+      int n_segments = avr_current_arch->n_segments;
+      
       src = force_reg (Pmode, src);
       
       if (ADDR_SPACE_GENERIC_P (as_from)
-          || as_from == ADDR_SPACE_PGM)
+          || as_from == ADDR_SPACE_PGM
+          || n_segments == 1)
         {
           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)));
+          rtx new_src = gen_reg_rtx (PSImode);
+          int segment = avr_pgm_segment (as_from) % n_segments;
+
+          emit_insn (gen_n_extendhipsi2 (new_src, GEN_INT (segment), src));
+
+          return new_src;
         }
     }
   
diff --git a/config/avr/avr.md b/config/avr/avr.md
index 3656eef..d341755 100644
--- a/config/avr/avr.md
+++ b/config/avr/avr.md
@@ -3845,18 +3845,24 @@
   })
 
 (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")))]
+  [(set (match_operand:PSI 0 "register_operand"            "=r,r,d,r")
+        (lo_sum:PSI (match_operand:QI 1 "const_int_operand" "L,P,n,n")
+                    (match_operand:HI 2 "register_operand"  "r,r,r,r")))
+   (clobber (match_scratch:QI 3                            "=X,X,X,&d"))]
   ""
   "#"
   "reload_completed"
-  [(set (match_dup 3) (match_dup 1))
-   (set (match_dup 4) (match_dup 5))]
+  [(set (match_dup 4) (match_dup 2))
+   (set (match_dup 3) (match_dup 6))
+   ; no-op move in the case where no scratch is needed
+   (set (match_dup 5) (match_dup 3))]
   {
-    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);
+    operands[4] = simplify_gen_subreg (HImode, operands[0], PSImode, 0);
+    operands[5] = simplify_gen_subreg (QImode, operands[0], PSImode, 2);
+    operands[6] = operands[1];
+
+    if (GET_CODE (operands[3]) == SCRATCH)
+      operands[3] = operands[5];
   })
 
 (define_insn_and_split "zero_extendhisi2"
diff --git a/config/avr/predicates.md b/config/avr/predicates.md
index f2a6a70..2db6f3b 100644
--- a/config/avr/predicates.md
+++ b/config/avr/predicates.md
@@ -249,8 +249,3 @@
 (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)")))

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [Patch]: PR49868: Named address space support for AVR
  2011-11-07 11:34       ` Georg-Johann Lay
@ 2011-11-07 13:53         ` Denis Chertykov
  2011-11-14 20:44           ` [Patch]: PR49868: Named address space support for AVR, #4 Georg-Johann Lay
  2011-11-17 14:02           ` [Patch]: PR49868: Named address space support for AVR, #5 Georg-Johann Lay
  0 siblings, 2 replies; 11+ messages in thread
From: Denis Chertykov @ 2011-11-07 13:53 UTC (permalink / raw)
  To: Georg-Johann Lay
  Cc: gcc-patches, Eric Weddington, Joerg Wunsch, Anatoly Sokolov

Let's wait for
http://gcc.gnu.org/ml/gcc-patches/2011-10/msg01874.html
Denis.

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [Patch]: PR49868: Named address space support for AVR, #4
  2011-11-07 13:53         ` Denis Chertykov
@ 2011-11-14 20:44           ` Georg-Johann Lay
  2011-11-15  7:41             ` Denis Chertykov
  2011-11-17 14:02           ` [Patch]: PR49868: Named address space support for AVR, #5 Georg-Johann Lay
  1 sibling, 1 reply; 11+ messages in thread
From: Georg-Johann Lay @ 2011-11-14 20:44 UTC (permalink / raw)
  To: Denis Chertykov
  Cc: gcc-patches, Eric Weddington, Joerg Wunsch, Anatoly Sokolov

[-- Attachment #1: Type: text/plain, Size: 4092 bytes --]

Denis Chertykov wrote:

> Let's wait for
> http://gcc.gnu.org/ml/gcc-patches/2011-10/msg01874.html

As that patch is now upstream, here the updated version.

Difference to the prior implementation of AS1 is:

o If no LPMX instruction is available and more than 2 bytes
  have to be loaded, a libgcc call is used.  This also makes
  the implementation simpler.


o Some functions are more general than actually needed.
  This will reduce the number of changes for the 24-bit
  address spaces.

o There are 2 new predicates to filter out loads from
  non-generic address space. This is needed in shift
  insns for example because they allow mem.

Tested without regressions.

Ok for trunk?

Johann

gcc/
	PR target/49868
	* config/avr/avr.h (ADDR_SPACE_PGM): New address spaces.
	(REGISTER_TARGET_PRAGMAS): New define.
	* config/avr/avr-protos.h (avr_mem_pgm_p): New.
	(avr_load_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
	space __pgm.
	(avr_cpu_cpp_builtins): Add built-in define __PGM.
	* 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, all_regs_rtx): New static variables.
	(avr_option_override): Initialize them.
	(output_reload_in_const): Use all_regs_rtx. Fix signedness of loop
	variables.
	(avr_pgm_segment): New static function.
	(avr_decl_pgm_p, avr_mem_pgm_p): New static functions.
	(avr_out_lpm, avr_out_lpm_no_lpmx): New static functions.
	(output_movqi, output_movhi, output_movsisf, avr_out_movpsi): Call
	avr_out_lpm to handle loads from progmem.
	(avr_load_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.
	
	* config/avr/predicates.md (nop_general_operand): New predicate.
	(nox_general_operand): New predicate.
	* config/avr/avr.md (LPM_REGNO): New define_constant.
	(load<mode>_libgcc): New expander.
	(*load.<mode>.libgcc): New insn.
	(mov<mode>): Handle loads from non-generic AS.
	(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.

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

[-- Attachment #2: pgmx-8.diff --]
[-- Type: text/x-patch, Size: 52468 bytes --]

Index: config/avr/predicates.md
===================================================================
--- config/avr/predicates.md	(revision 181349)
+++ config/avr/predicates.md	(working copy)
@@ -57,6 +57,17 @@ (define_predicate "io_address_operand"
   (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")
+       (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.
+(define_predicate "nox_general_operand"
+  (and (match_operand 0 "general_operand")
+       (match_test "!avr_load_libgcc_p (op)")))
+
 ;; Return 1 if OP is the zero constant for MODE.
 (define_predicate "const0_operand"
   (and (match_code "const_int,const_double")
Index: config/avr/avr.md
===================================================================
--- config/avr/avr.md	(revision 181349)
+++ config/avr/avr.md	(working copy)
@@ -42,6 +42,7 @@ (define_constants
    (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
    
@@ -315,6 +316,39 @@ (define_split
   "")
 
 ;;========================================================================
+;; Move stuff around
+
+(define_expand "load<mode>_libgcc"
+  [(set (match_dup 3)
+        (match_dup 2))
+   (set (reg:MOVMODE 22)
+        (match_operand:MOVMODE 1 "memory_operand" ""))
+   (set (match_operand:MOVMODE 0 "register_operand" "")
+        (reg:MOVMODE 22))]
+  "avr_load_libgcc_p (operands[1])"
+  {
+    operands[3] = gen_rtx_REG (HImode, REG_Z);
+    operands[2] = force_operand (XEXP (operands[1], 0), NULL_RTX);
+    operands[1] = replace_equiv_address (operands[1], operands[3]);
+    set_mem_addr_space (operands[1], ADDR_SPACE_PGM);
+  })
+    
+(define_insn "*load.<mode>.libgcc"
+  [(set (reg:MOVMODE 22)
+        (match_operand:MOVMODE 0 "memory_operand" "m,m"))]
+  "avr_load_libgcc_p (operands[0])
+   && REG_P (XEXP (operands[0], 0))
+   && REG_Z == REGNO (XEXP (operands[0], 0))"
+  {
+    operands[0] = GEN_INT (GET_MODE_SIZE (<MODE>mode));
+    return "%~call __load_%0";
+  }
+  [(set_attr "length" "1,2")
+   (set_attr "isa" "rjmp,jmp")
+   (set_attr "cc" "clobber")])
+
+
+;; General move expanders
 
 ;; "movqi"
 ;; "movhi"
@@ -327,12 +361,25 @@ (define_expand "mov<mode>"
         (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]))
+    rtx dest = operands[0];
+    rtx src  = operands[1]; 
+    
+    if (avr_mem_pgm_p (dest))
+      DONE;
+  
+    /* One of the operands has to be in a register.  */
+    if (!register_operand (dest, <MODE>mode)
+        && !(register_operand (src, <MODE>mode)
+             || src == CONST0_RTX (<MODE>mode)))
+      {
+        operands[1] = src = copy_to_mode_reg (<MODE>mode, src);
+      }
+
+    if (avr_load_libgcc_p (src))
       {
-        operands[1] = copy_to_mode_reg (<MODE>mode, operands[1]);
+        /* For the small devices, do loads per libgcc call.  */
+        emit_insn (gen_load<mode>_libgcc (dest, src));
+        DONE;
       }
   })
 
@@ -346,11 +393,14 @@ (define_expand "mov<mode>"
 ;; 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 +475,14 @@ (define_insn "*reload_inhi"
    (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 +515,40 @@ (define_peephole2 ; movw_r
     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 (!avr_mem_pgm_p (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 +579,7 @@ (define_insn "*reload_inpsi"
 
 (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 +619,11 @@ (define_insn "*reload_insi"
 
 
 (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 +635,8 @@ (define_insn "*movsi"
 ;; 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 +687,7 @@ (define_expand "movmemhi"
   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 +696,9 @@ (define_expand "movmemhi"
   /* 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 +719,9 @@ (define_expand "movmemhi"
   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.  */
@@ -2868,10 +2961,8 @@ (define_insn_and_split "*rotb<mode>"
 
 (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" "")
@@ -2903,21 +2994,25 @@ (define_split ; ashlqi3_const6
   "")
 
 (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")])
@@ -3005,11 +3100,13 @@ (define_peephole2
 
 
 (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")])
@@ -3063,11 +3160,13 @@ (define_peephole2
 
 (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")])
@@ -3084,11 +3183,13 @@ (define_peephole2
 
 (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")])
@@ -3109,21 +3210,25 @@ (define_insn "ashlpsi3"
 ;; 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")])
@@ -3141,11 +3246,13 @@ (define_insn "ashrpsi3"
    (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")])
@@ -3164,11 +3271,13 @@ (define_peephole2
 
 (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")])
@@ -3185,11 +3294,13 @@ (define_peephole2
 
 (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")])
@@ -3198,11 +3309,9 @@ (define_insn "*ashrsi3_const"
 ;; 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" "")
@@ -3234,21 +3343,25 @@ (define_split	; lshrqi3_const6
   "")
 
 (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")])
@@ -3266,11 +3379,13 @@ (define_insn "lshrpsi3"
    (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")])
@@ -3324,11 +3439,13 @@ (define_peephole2
 
 (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")])
@@ -3345,11 +3462,13 @@ (define_peephole2
 
 (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")])
@@ -3615,6 +3734,27 @@ (define_insn_and_split "zero_extendhipsi
     operands[3] = simplify_gen_subreg (QImode, operands[0], PSImode, 2);
   })
 
+(define_insn_and_split "n_extendhipsi2"
+  [(set (match_operand:PSI 0 "register_operand"            "=r,r,d,r")
+        (lo_sum:PSI (match_operand:QI 1 "const_int_operand" "L,P,n,n")
+                    (match_operand:HI 2 "register_operand"  "r,r,r,r")))
+   (clobber (match_scratch:QI 3                            "=X,X,X,&d"))]
+  ""
+  "#"
+  "reload_completed"
+  [(set (match_dup 4) (match_dup 2))
+   (set (match_dup 3) (match_dup 6))
+   ; no-op move in the case where no scratch is needed
+   (set (match_dup 5) (match_dup 3))]
+  {
+    operands[4] = simplify_gen_subreg (HImode, operands[0], PSImode, 0);
+    operands[5] = simplify_gen_subreg (QImode, operands[0], PSImode, 2);
+    operands[6] = operands[1];
+
+    if (GET_CODE (operands[3]) == SCRATCH)
+      operands[3] = operands[5];
+  })
+
 (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")))]
Index: config/avr/avr-log.c
===================================================================
--- config/avr/avr-log.c	(revision 181349)
+++ config/avr/avr-log.c	(working copy)
@@ -59,6 +59,8 @@
   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
     }
Index: config/avr/avr-c.c
===================================================================
--- config/avr/avr-c.c	(revision 181349)
+++ config/avr/avr-c.c	(working copy)
@@ -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,17 @@
 #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);
+}
 
-/* Not included in avr.c since this requires C front end.  */
 
 /* Worker function for TARGET_CPU_CPP_BUILTINS.  */
 
@@ -90,6 +100,17 @@ avr_cpu_cpp_builtins (struct cpp_reader
         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.  For C++, a language extension is needed
+     (as mentioned in 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");
+    }
+
   /* Define builtin macros so that the user can
      easily query if or if not a specific builtin
      is available. */
Index: config/avr/avr-protos.h
===================================================================
--- config/avr/avr-protos.h	(revision 181349)
+++ config/avr/avr-protos.h	(working copy)
@@ -32,6 +32,7 @@ extern int avr_initial_elimination_offse
 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,6 @@ extern void init_cumulative_args (CUMULA
 #endif /* TREE_CODE */
 
 #ifdef RTX_CODE
-extern void asm_output_external_libcall (FILE *file, rtx symref);
 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 +121,8 @@ extern reg_class_t avr_mode_code_base_re
 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_load_libgcc_p (rtx);
 #endif /* RTX_CODE */
 
 #ifdef REAL_VALUE_TYPE
@@ -139,12 +141,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;
Index: config/avr/avr.c
===================================================================
--- config/avr/avr.c	(revision 181349)
+++ config/avr/avr.c	(working copy)
@@ -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"
@@ -84,12 +85,21 @@ static bool avr_rtx_costs (rtx, int, int
 /* 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;
 
+/* 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;
 
@@ -172,9 +182,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
 
@@ -189,9 +196,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
@@ -221,6 +225,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
+
 \f
 
 /* Custom function to replace string prefix.
@@ -295,6 +317,8 @@ avr_popcount_each_byte (rtx xval, int n_
 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
@@ -323,8 +347,14 @@ 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);
+  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;
 
@@ -384,6 +414,28 @@ avr_scalar_mode_supported_p (enum machin
 }
 
 
+/* 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 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)));
+}
+
+
 /* A helper for the subsequent function attribute used to dig for
    attribute 'name' in a FUNCTION_DECL or FUNCTION_TYPE */
 
@@ -1379,6 +1431,9 @@ avr_legitimate_address_p (enum machine_m
   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  */
 
@@ -2177,6 +2232,231 @@ avr_function_ok_for_sibcall (tree decl_c
 /***********************************************************************
   Functions for outputting various mov's for a various modes
 ************************************************************************/
+
+/* Return true if a value of mode MODE is read from flash by
+   __load_* function from libgcc.  */
+
+bool
+avr_load_libgcc_p (rtx op)
+{
+  enum machine_mode mode = GET_MODE (op);
+  int n_bytes = GET_MODE_SIZE (mode);
+        
+  return (n_bytes > 2
+          && !AVR_HAVE_LPMX
+          && avr_mem_pgm_p (op));
+}
+
+
+/* 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 ("lpm" CR_TAB
+                              "mov %0,%3", xop, plen, 2);
+
+        case 2:
+          if (REGNO (dest) == REG_Z)
+            return avr_asm_len ("lpm"        CR_TAB
+                                "push %3"    CR_TAB
+                                "adiw %2,1"  CR_TAB
+                                "lpm"        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);
+                
+              if (!reg_unused_after (insn, addr))
+                avr_asm_len ("sbiw %2,1", xop, plen, 1);
+            }
+          
+          break; /* 2 */
+        }
+      
+      break; /* REG */
+
+    case POST_INC:
+
+      gcc_assert (REG_Z == REGNO (XEXP (addr, 0))
+                  && n_bytes <= 2);
+
+      avr_asm_len ("lpm"        CR_TAB
+                   "mov %A0,%3" CR_TAB
+                   "adiw %2,1", xop, plen, 3);
+
+      if (n_bytes >= 2)
+        avr_asm_len ("lpm"        CR_TAB
+                     "mov %B0,%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;
+
+  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);
+
+  gcc_assert (!avr_load_libgcc_p (src)
+              && REG_P (dest)
+              && (REG_P (addr) || POST_INC == GET_CODE (addr)));
+
+  xop[0] = dest;
+  xop[1] = addr;
+  xop[2] = lpm_addr_reg_rtx;
+
+  regno_dest = REGNO (dest);
+
+  if (!AVR_HAVE_LPMX)
+    {
+      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 ("lpm %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);
+          else
+            {
+              avr_asm_len ("lpm %A0,%a2+" CR_TAB
+                           "lpm %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 ("lpm %A0,%a2+" CR_TAB
+                       "lpm %B0,%a2+" CR_TAB
+                       "lpm %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 ("lpm %A0,%a2+" CR_TAB
+                       "lpm %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);
+          else
+            {
+              avr_asm_len ("lpm %C0,%a2+" CR_TAB
+                           "lpm %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                    ("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);
+
+      break; /* POST_INC */
+
+    } /* switch CODE (addr) */
+      
+  return "";
+}
+
+
 const char *
 output_movqi (rtx insn, rtx operands[], int *l)
 {
@@ -2185,6 +2465,12 @@ output_movqi (rtx insn, rtx operands[],
   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;
 
@@ -2235,6 +2521,12 @@ output_movhi (rtx insn, rtx operands[],
   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;
   
@@ -2848,6 +3140,12 @@ output_movsisf (rtx insn, rtx operands[]
   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;
   
@@ -3167,6 +3465,12 @@ avr_out_movpsi (rtx insn, rtx *op, int *
   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 */
@@ -3764,9 +4068,10 @@ out_shift_with_cnt (const char *templ, r
   if (len)
     *len = 1;
 
-  if (GET_CODE (operands[2]) == CONST_INT)
+  if (CONST_INT_P (operands[2]))
     {
-      int scratch = (GET_CODE (PATTERN (insn)) == PARALLEL);
+      bool scratch = (GET_CODE (PATTERN (insn)) == PARALLEL
+                      && REG_P (operands[3]));
       int count = INTVAL (operands[2]);
       int max_len = 10;  /* If larger than this, always use a loop.  */
 
@@ -3819,8 +4124,7 @@ out_shift_with_cnt (const char *templ, r
 	  /* No scratch register available, use one from LD_REGS (saved in
 	     __tmp_reg__) that doesn't overlap with registers to shift.  */
 
-	  op[3] = gen_rtx_REG (QImode,
-			   ((true_regnum (operands[0]) - 1) & 15) + 16);
+	  op[3] = all_regs_rtx[((REGNO (operands[0]) - 1) & 15) + 16];
 	  op[4] = tmp_reg_rtx;
 	  saved_in_tmp = 1;
 
@@ -6208,8 +6512,15 @@ 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 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)
@@ -6219,11 +6530,15 @@ avr_progmem_p (tree decl, tree attribute
   if (TREE_CODE (decl) != VAR_DECL)
     return 0;
 
+  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);
@@ -6232,16 +6547,123 @@ avr_progmem_p (tree decl, tree attribute
     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))
@@ -6258,11 +6680,20 @@ avr_insert_attributes (tree node, tree *
       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);
         }
     }
 }
@@ -6462,6 +6893,7 @@ avr_section_type_flags (tree decl, const
       && avr_progmem_p (decl, DECL_ATTRIBUTES (decl)))
     {
       flags &= ~SECTION_WRITE;
+      flags &= ~SECTION_BSS;
       flags |= AVR_SECTION_PROGMEM;
     }
   
@@ -8049,10 +8481,14 @@ avr_hard_regno_mode_ok (int regno, enum
 
 reg_class_t
 avr_mode_code_base_reg_class (enum machine_mode mode ATTRIBUTE_UNUSED,
-                              addr_space_t as ATTRIBUTE_UNUSED,
-                              RTX_CODE outer_code,
+                              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;
 
@@ -8071,6 +8507,27 @@ avr_regno_mode_code_ok_for_base_p (int r
 {
   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
@@ -8122,9 +8579,8 @@ output_reload_in_const (rtx *op, rtx clo
   int clobber_val = 1234;
   bool cooked_clobber_p = false;
   bool set_p = false;
-  unsigned int n;
   enum machine_mode mode = GET_MODE (dest);
-  int n_bytes = GET_MODE_SIZE (mode);
+  int n, n_bytes = GET_MODE_SIZE (mode);
   
   gcc_assert (REG_P (dest)
               && CONSTANT_P (src));
@@ -8138,7 +8594,7 @@ output_reload_in_const (rtx *op, rtx clo
   if (REGNO (dest) < 16
       && REGNO (dest) + GET_MODE_SIZE (mode) > 16)
     {
-      clobber_reg = gen_rtx_REG (QImode, REGNO (dest) + n_bytes - 1);
+      clobber_reg = all_regs_rtx[REGNO (dest) + n_bytes - 1];
     }
 
   /* We might need a clobber reg but don't have one.  Look at the value to
@@ -8155,7 +8611,7 @@ output_reload_in_const (rtx *op, rtx clo
          That's cheaper than loading from constant pool.  */
       
       cooked_clobber_p = true;
-      clobber_reg = gen_rtx_REG (QImode, REG_Z + 1);
+      clobber_reg = all_regs_rtx[REG_Z + 1];
       avr_asm_len ("mov __tmp_reg__,%0", &clobber_reg, len, 1);
     }
 
@@ -8165,7 +8621,7 @@ output_reload_in_const (rtx *op, rtx clo
     {
       int ldreg_p;
       bool done_byte = false;
-      unsigned int j;
+      int j;
       rtx xop[3];
 
       /* Crop the n-th destination byte.  */
@@ -8595,6 +9051,150 @@ 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 HImode;
+}
+
+
+/* Implement `TARGET_ADDR_SPACE_POINTER_MODE'.  */
+
+static enum machine_mode
+avr_addr_space_pointer_mode (addr_space_t as ATTRIBUTE_UNUSED)
+{
+  return HImode;
+}
+
+
+/* Helper for following function.  */
+
+static bool
+avr_reg_ok_for_pgm_addr (rtx reg, bool strict)
+{
+  gcc_assert (REG_P (reg));
+
+  if (strict)
+    {
+      return REGNO (reg) == REG_Z;
+    }
+  
+  /* Avoid combine to propagate hard regs.  */
+  
+  if (can_create_pseudo_p()
+      && REGNO (reg) < REG_Z)
+    {
+      return false;
+    }
+  
+  return true;
+}
+
+
+/* Implement `TARGET_ADDR_SPACE_LEGITIMATE_ADDRESS_P'.  */
+
+static bool
+avr_addr_space_legitimate_address_p (enum machine_mode mode, rtx x,
+                                     bool strict, addr_space_t as)
+{
+  bool ok = false;
+
+  switch (as)
+    {
+    default:
+      gcc_unreachable();
+      
+    case ADDR_SPACE_GENERIC:
+      return avr_legitimate_address_p (mode, x, strict);
+
+    case ADDR_SPACE_PGM:
+
+      switch (GET_CODE (x))
+        {
+        case REG:
+          ok = avr_reg_ok_for_pgm_addr (x, strict);
+          break;
+          
+        case POST_INC:
+          ok = (!avr_load_libgcc_p (x)
+                && avr_reg_ok_for_pgm_addr (XEXP (x, 0), strict));
+          break;
+          
+        default:
+          break;
+        }
+
+      break; /* PGM */
+    }
+
+  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)
+{
+  if (avr_log.progmem)
+    avr_edump ("\n%!: op = %r\nfrom = %t\nto = %t\n",
+               src, type_from, type_to);
+
+  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)
+{
+  return true;
+}
+
+
 /* Helper for __builtin_avr_delay_cycles */
 
 static void
Index: config/avr/avr.h
===================================================================
--- config/avr/avr.h	(revision 181349)
+++ config/avr/avr.h	(working copy)
@@ -391,6 +391,14 @@ typedef struct avr_args {
 
 #define NO_FUNCTION_CSE
 
+
+#define ADDR_SPACE_PGM  1
+
+#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"

[-- Attachment #3: pgmx-8-libgcc.diff --]
[-- Type: text/x-patch, Size: 1547 bytes --]

Index: ../libgcc/config/avr/lib1funcs.S
===================================================================
--- ../libgcc/config/avr/lib1funcs.S	(revision 181349)
+++ ../libgcc/config/avr/lib1funcs.S	(working copy)
@@ -1181,6 +1181,52 @@ DEFUN __tablejump_elpm__
 ENDF __tablejump_elpm__
 #endif /* defined (L_tablejump_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 */
+
 \f
 .section .text.libgcc.builtins, "ax", @progbits
 
Index: ../libgcc/config/avr/t-avr
===================================================================
--- ../libgcc/config/avr/t-avr	(revision 181349)
+++ ../libgcc/config/avr/t-avr	(working copy)
@@ -21,6 +21,7 @@ LIB1ASMFUNCS = \
 	_cleanup \
 	_tablejump \
 	_tablejump_elpm \
+	_load_3 _load_4 \
 	_copy_data \
 	_clear_bss \
 	_ctors \

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [Patch]: PR49868: Named address space support for AVR, #4
  2011-11-14 20:44           ` [Patch]: PR49868: Named address space support for AVR, #4 Georg-Johann Lay
@ 2011-11-15  7:41             ` Denis Chertykov
  0 siblings, 0 replies; 11+ messages in thread
From: Denis Chertykov @ 2011-11-15  7:41 UTC (permalink / raw)
  To: Georg-Johann Lay
  Cc: gcc-patches, Eric Weddington, Joerg Wunsch, Anatoly Sokolov

2011/11/15 Georg-Johann Lay <avr@gjlay.de>:
> Denis Chertykov wrote:
>
>> Let's wait for
>> http://gcc.gnu.org/ml/gcc-patches/2011-10/msg01874.html
>
> As that patch is now upstream, here the updated version.
>
> Difference to the prior implementation of AS1 is:
>
> o If no LPMX instruction is available and more than 2 bytes
>  have to be loaded, a libgcc call is used.  This also makes
>  the implementation simpler.
>
>
> o Some functions are more general than actually needed.
>  This will reduce the number of changes for the 24-bit
>  address spaces.
>
> o There are 2 new predicates to filter out loads from
>  non-generic address space. This is needed in shift
>  insns for example because they allow mem.
>
> Tested without regressions.
>
> Ok for trunk?
>
> Johann
>
> gcc/
>        PR target/49868
>        * config/avr/avr.h (ADDR_SPACE_PGM): New address spaces.
>        (REGISTER_TARGET_PRAGMAS): New define.
>        * config/avr/avr-protos.h (avr_mem_pgm_p): New.
>        (avr_load_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
>        space __pgm.
>        (avr_cpu_cpp_builtins): Add built-in define __PGM.
>        * 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, all_regs_rtx): New static variables.
>        (avr_option_override): Initialize them.
>        (output_reload_in_const): Use all_regs_rtx. Fix signedness of loop
>        variables.
>        (avr_pgm_segment): New static function.
>        (avr_decl_pgm_p, avr_mem_pgm_p): New static functions.
>        (avr_out_lpm, avr_out_lpm_no_lpmx): New static functions.
>        (output_movqi, output_movhi, output_movsisf, avr_out_movpsi): Call
>        avr_out_lpm to handle loads from progmem.
>        (avr_load_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.
>
>        * config/avr/predicates.md (nop_general_operand): New predicate.
>        (nox_general_operand): New predicate.
>        * config/avr/avr.md (LPM_REGNO): New define_constant.
>        (load<mode>_libgcc): New expander.
>        (*load.<mode>.libgcc): New insn.
>        (mov<mode>): Handle loads from non-generic AS.
>        (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.
>
> libgcc/
>        PR target/49868
>        * config/avr/t-avr (LIB1ASMFUNCS): Add _load_3,  _load_4.
>        * config/avr/lib1funcs.S (__load_3, __load_4, __xload_2): New functions.
>


Please commit.

Denis.

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [Patch]: PR49868: Named address space support for AVR, #5
  2011-11-07 13:53         ` Denis Chertykov
  2011-11-14 20:44           ` [Patch]: PR49868: Named address space support for AVR, #4 Georg-Johann Lay
@ 2011-11-17 14:02           ` Georg-Johann Lay
  2011-11-18 16:17             ` Denis Chertykov
  1 sibling, 1 reply; 11+ messages in thread
From: Georg-Johann Lay @ 2011-11-17 14:02 UTC (permalink / raw)
  To: Denis Chertykov
  Cc: gcc-patches, Eric Weddington, Joerg Wunsch, Anatoly Sokolov

[-- Attachment #1: Type: text/plain, Size: 5581 bytes --]

Denis Chertykov wrote:

> Let's wait for
> http://gcc.gnu.org/ml/gcc-patches/2011-10/msg01874.html
> Denis.

This are yet more intrinsic named address spaces:

* __pgm1, ... __pgm5 are 16-bit address spaces that refer to
  the n-th 64k chunk of flash. Counting starts at 0.
  The 0-th address space __pgm is already upstream.

* __pgmx is a 24-bit address space located in flash.


The annoyance in this patch is the movmemhi insn:

* Register allocator does a bad job and might lead to spills
  inside the copy loop so that it is no more guaranteed that
  tmp_reg contains the value to be copied because move insns
  use that register implicitly.  Besides that, spilling in
  the copy loop leads to unfortunate code. See respective
  FIXMEs in avr.c:avr_emit_movmemhi()

* Using match_dup in respective patterns shreds web.c to that
  the insns need up to with 11 operands.

Besides that there are caveats and binutils is missing some support:

* To place variables in __pgm1 ... __pgm5 in appropriate sections
  a custom linker script is needed.  Data is put in sections
  progmem1.data ... __progmem5.data, respectively, and these
  sections must be treated in the linker script.

* Address computation for the 24-bit address space is performed
  as signed 16-bit.  Thus, accessing an array var[i] for example
  it is not possible to reach locations that are farther away
  than +/- 32768 bytes from var[0].

* It is not possible to assemble a 24-bit address, see the
  assembler warning generated in avr.c:avr_assemble_integer().

  This warning is triggered for code like

	extern const __pgmx int ivar;
	const __pgmx void * var = &ivar;

	.global	var
		.data
		.type	var, @object
		.size	var, 3
	var:
		.word	ivar
		.warning	"24-bit address needs binutils extension for hh8(ivar)"
		.byte	0	 ;  hh8(ivar)



The patch passes C tests with one FAIL less (SVN 181349):

gcc.dg/pr43300.c (internal compiler error)

./gcc.dg/pr43300.c: In function 'foo':
./gcc.dg/pr43300.c:19:1: internal compiler error: in commit_one_edge_insertion,
at cfgrtl.c:1582

This tests passes now; seems that the changes to movmemhi allow that test to
PASS now (for whatever reason).

Ok for trunk?

Johann

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/constraints.md (Cpp): New constraint.
	* 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, n_extendhipsi2): 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.


[-- Attachment #2: pgmx-8-b.diff --]
[-- Type: text/x-patch, Size: 74276 bytes --]

Index: config/avr/predicates.md
===================================================================
--- config/avr/predicates.md	(revision 181378)
+++ config/avr/predicates.md	(working copy)
@@ -63,10 +63,11 @@ (define_predicate "nop_general_operand"
        (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: config/avr/avr.md
===================================================================
--- config/avr/avr.md	(revision 181378)
+++ config/avr/avr.md	(working copy)
@@ -45,13 +45,16 @@ (define_constants
    (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)
+   (SP_ADDR     0x5D)
    (RAMPZ_ADDR  0x5B)
    ])
 
 (define_c_enum "unspec"
   [UNSPEC_STRLEN
+   UNSPEC_MOVMEM
    UNSPEC_INDEX_JMP
    UNSPEC_FMUL
    UNSPEC_FMULS
@@ -128,6 +131,7 @@ (define_attr "adjust_len"
   "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,
@@ -137,15 +141,14 @@ (define_attr "adjust_len"
 
 ;; 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"))
 
@@ -176,6 +179,22 @@ (define_attr "enabled" ""
          (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)))
 
 
@@ -243,12 +262,10 @@ (define_expand "nonlocal_goto_receiver"
 ;; 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]);
@@ -333,7 +350,7 @@ (define_expand "load<mode>_libgcc"
     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])
@@ -348,6 +365,135 @@ (define_insn "*load.<mode>.libgcc"
    (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"
@@ -375,6 +521,21 @@ (define_expand "mov<mode>"
         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.  */
@@ -677,167 +838,161 @@ (define_insn "*reload_insf"
 
 (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" ""))])]
-  ""
-  "{
-  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;
+                   (match_operand:BLK 1 "memory_operand" ""))
+              (use (match_operand:HI 2 "const_int_operand" ""))
+              (use (match_operand:HI 3 "const_int_operand" ""))])]
+  ""
+  {
+    if (avr_emit_movmemhi (operands))
+      DONE;
 
-  if (GET_CODE (operands[2]) != CONST_INT)
     FAIL;
+  })
 
-  count = INTVAL (operands[2]);
-  if (count <= 0)
-    FAIL;
+(define_mode_attr MOVMEM_r_d [(QI "r")
+                              (HI "d")])
 
-  /* Work out branch probability for latter use.  */
-  prob = REG_BR_PROB_BASE - REG_BR_PROB_BASE / count;
+;; $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")])
 
-  /* 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;
-}")
+;; 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")])
 
-;; =%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))])]
-  ""
-  "{
-  rtx addr0;
-  enum machine_mode mode;
-
-  /* If value to set is not zero, use the library routine.  */
-  if (operands[2] != const0_rtx)
-    FAIL;
-
-  if (!CONST_INT_P (operands[1]))
-    FAIL;
+                   (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;
+
+    /* If value to set is not zero, use the library routine.  */
+    if (operands[2] != const0_rtx)
+      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: config/avr/avr-devices.c
===================================================================
--- config/avr/avr-devices.c	(revision 181377)
+++ config/avr/avr-devices.c	(working copy)
@@ -25,18 +25,27 @@
 
 /* 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: config/avr/avr-c.c
===================================================================
--- config/avr/avr-c.c	(revision 181378)
+++ config/avr/avr-c.c	(working copy)
@@ -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
   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: config/avr/avr-protos.h
===================================================================
--- config/avr/avr-protos.h	(revision 181378)
+++ config/avr/avr-protos.h	(working copy)
@@ -34,6 +34,7 @@ extern int avr_hard_regno_rename_ok (uns
 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 operan
 
 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, r
 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_b
 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: config/avr/constraints.md
===================================================================
--- config/avr/constraints.md	(revision 181377)
+++ config/avr/constraints.md	(working copy)
@@ -103,6 +103,11 @@ (define_memory_constraint "Q"
   (and (match_code "mem")
        (match_test "extra_constraint_Q (op)")))
 
+(define_constraint "Cpp"
+  "Positive integer constant."
+  (and (match_code "const_int")
+       (match_test "ival > 0")))
+
 (define_constraint "Cm2"
   "Constant integer @minus{}2."
   (and (match_code "const_int")
Index: config/avr/avr.c
===================================================================
--- config/avr/avr.c	(revision 181378)
+++ config/avr/avr.c	(working copy)
@@ -55,7 +55,10 @@
 /* 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_dev
 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_
 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 machin
 }
 
 
+/* 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 co
       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,
           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,
     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,
 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 *ple
 
   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 *ple
           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 *ple
 
         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 *ple
       
         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 *ple
       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 *ple
 }
 
 
+/* 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[],
 }
 
 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 *
 
 
 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 in
       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 attribute
   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 voi
 }
 
 
+/* 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,
 {
   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,
 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
     }
 
   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
 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 r
             }
         }
           
-      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 opera
   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 (enu
       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 (enu
           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 (enu
         }
 
       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
 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,9 +9670,14 @@ avr_addr_space_convert (rtx src, tree ty
 /* 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;
 }
 
@@ -9461,6 +9948,227 @@ avr_expand_binop_builtin (enum insn_code
 }
 
 
+/* Worker function for movmemhi insn.
+   XOP[0]  Destination as MEM:BLK
+   XOP[1]  Source      "     "
+   XOP[2]  # Bytes to copy  */
+
+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 ("brne 1f"          CR_TAB
+                       "inc %8"           CR_TAB
+                       "out __RAMPZ__,%8" CR_TAB
+                       "1:", xop, plen, 3);
+        }
+      
+      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);
+}
+
 /* Expand an expression EXP that calls a built-in function,
    with result going to TARGET if that's convenient
    (and in mode MODE if that's convenient).
Index: config/avr/avr.h
===================================================================
--- config/avr/avr.h	(revision 181378)
+++ config/avr/avr.h	(working copy)
@@ -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
 #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()

[-- Attachment #3: pgmx-8-b-libgcc.diff --]
[-- Type: text/x-patch, Size: 2056 bytes --]

Index: ../libgcc/config/avr/lib1funcs.S
===================================================================
--- ../libgcc/config/avr/lib1funcs.S	(revision 181378)
+++ ../libgcc/config/avr/lib1funcs.S	(working copy)
@@ -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: ../libgcc/config/avr/t-avr
===================================================================
--- ../libgcc/config/avr/t-avr	(revision 181378)
+++ ../libgcc/config/avr/t-avr	(working copy)
@@ -22,6 +22,7 @@ LIB1ASMFUNCS = \
 	_tablejump \
 	_tablejump_elpm \
 	_load_3 _load_4 \
+	_xload_2 _xload_3 _xload_4 \
 	_copy_data \
 	_clear_bss \
 	_ctors \

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [Patch]: PR49868: Named address space support for AVR, #5
  2011-11-17 14:02           ` [Patch]: PR49868: Named address space support for AVR, #5 Georg-Johann Lay
@ 2011-11-18 16:17             ` Denis Chertykov
  0 siblings, 0 replies; 11+ messages in thread
From: Denis Chertykov @ 2011-11-18 16:17 UTC (permalink / raw)
  To: Georg-Johann Lay
  Cc: gcc-patches, Eric Weddington, Joerg Wunsch, Anatoly Sokolov

2011/11/17 Georg-Johann Lay <avr@gjlay.de>:
> Denis Chertykov wrote:
>
>> Let's wait for
>> http://gcc.gnu.org/ml/gcc-patches/2011-10/msg01874.html
>> Denis.
>
> This are yet more intrinsic named address spaces:
>
> * __pgm1, ... __pgm5 are 16-bit address spaces that refer to
>  the n-th 64k chunk of flash. Counting starts at 0.
>  The 0-th address space __pgm is already upstream.
>
> * __pgmx is a 24-bit address space located in flash.
>
>
> The annoyance in this patch is the movmemhi insn:
>
> * Register allocator does a bad job and might lead to spills
>  inside the copy loop so that it is no more guaranteed that
>  tmp_reg contains the value to be copied because move insns
>  use that register implicitly.  Besides that, spilling in
>  the copy loop leads to unfortunate code. See respective
>  FIXMEs in avr.c:avr_emit_movmemhi()
>
> * Using match_dup in respective patterns shreds web.c to that
>  the insns need up to with 11 operands.
>
> Besides that there are caveats and binutils is missing some support:
>
> * To place variables in __pgm1 ... __pgm5 in appropriate sections
>  a custom linker script is needed.  Data is put in sections
>  progmem1.data ... __progmem5.data, respectively, and these
>  sections must be treated in the linker script.
>
> * Address computation for the 24-bit address space is performed
>  as signed 16-bit.  Thus, accessing an array var[i] for example
>  it is not possible to reach locations that are farther away
>  than +/- 32768 bytes from var[0].
>
> * It is not possible to assemble a 24-bit address, see the
>  assembler warning generated in avr.c:avr_assemble_integer().
>
>  This warning is triggered for code like
>
>        extern const __pgmx int ivar;
>        const __pgmx void * var = &ivar;
>
>        .global var
>                .data
>                .type   var, @object
>                .size   var, 3
>        var:
>                .word   ivar
>                .warning        "24-bit address needs binutils extension for hh8(ivar)"
>                .byte   0        ;  hh8(ivar)
>
>
>
> The patch passes C tests with one FAIL less (SVN 181349):
>
> gcc.dg/pr43300.c (internal compiler error)
>
> ./gcc.dg/pr43300.c: In function 'foo':
> ./gcc.dg/pr43300.c:19:1: internal compiler error: in commit_one_edge_insertion,
> at cfgrtl.c:1582
>
> This tests passes now; seems that the changes to movmemhi allow that test to
> PASS now (for whatever reason).
>
> Ok for trunk?
>
> Johann
>
> 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/constraints.md (Cpp): New constraint.
>        * 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, n_extendhipsi2): 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.
>
>

Approved.

Denis.

^ permalink raw reply	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2011-11-18 14:53 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-10-06 15:16 [Patch]: PR49868: Named address space support for AVR Georg-Johann Lay
2011-10-28 11:00 ` Georg-Johann Lay
2011-10-28 11:21   ` Georg-Johann Lay
2011-10-28 11:35   ` Denis Chertykov
2011-11-06 18:53     ` Georg-Johann Lay
2011-11-07 11:34       ` Georg-Johann Lay
2011-11-07 13:53         ` Denis Chertykov
2011-11-14 20:44           ` [Patch]: PR49868: Named address space support for AVR, #4 Georg-Johann Lay
2011-11-15  7:41             ` Denis Chertykov
2011-11-17 14:02           ` [Patch]: PR49868: Named address space support for AVR, #5 Georg-Johann Lay
2011-11-18 16:17             ` Denis Chertykov

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).