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)")))
