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