This fixes a wrong code bug on avr31 (ELPM but no ELPMx) for
loading values >= 3 bytes from __flash1.
Some condition lead to __load_3/4 which loads from __flash and
hence is wrong.  This occurs for two old devices from avr31.

Applied as obvious.

Johann

--

    AVR: target/122527 -- Don't use __load_N to load from __flash1.

    This patch fixes a case where a 3 byte or 4 byte load from __flash1
    uses __load_3/4 to read the value, which is wrong.

    This only occurred when the device has ELPM but not ELPMx (avr31).

            PR target/122527
    gcc/
            * config/avr/avr.cc (avr_load_libgcc_p): Return false if
            the address-space is not ADDR_SPACE_FLASH.
(avr_out_lpm_no_lpmx [addr=REG]): Handle sizes of 3 and 4 bytes.
    AVR: target/122527 -- Don't use __load_N to load from __flash1.
    
    This patch fixes a case where a 3 byte or 4 byte load from __flash1
    uses __load_3/4 to read the value, which is wrong.
    
    This only occured when the device has ELPM but not ELPMx (avr31).
    
            PR target/122527
    gcc/
            * config/avr/avr.cc (avr_load_libgcc_p): Return false if
            the address-space is not ADDR_SPACE_FLASH.
            (avr_out_lpm_no_lpmx [addr=REG]): Handle sizes of 3 and 4 bytes.

diff --git a/gcc/config/avr/avr.cc b/gcc/config/avr/avr.cc
index 227c12a9b32..0bdba554575 100644
--- a/gcc/config/avr/avr.cc
+++ b/gcc/config/avr/avr.cc
@@ -3272,7 +3272,8 @@ avr_load_libgcc_p (rtx op)
 
   return (n_bytes > 2
 	  && !AVR_HAVE_LPMX
-	  && avr_mem_flash_p (op));
+	  && avr_mem_flash_p (op)
+	  && MEM_ADDR_SPACE (op) == ADDR_SPACE_FLASH);
 }
 
 
@@ -3624,6 +3625,46 @@ avr_out_lpm_no_lpmx (rtx_insn *insn, rtx *xop, int *plen)
 	    avr_asm_len ("sbiw %2,1", xop, plen, 1);
 
 	  break; /* 2 */
+
+	/* cases 3 and 4 are only needed with ELPM but no ELPMx.  */
+
+	case 3:
+	  if (REGNO (dest) == REG_Z - 2
+	      && !reg_unused_after (insn, all_regs_rtx[REG_31]))
+	    avr_asm_len ("push r31", xop, plen, 1);
+
+	  avr_asm_len ("%4lpm $ mov %A0,%3 $ adiw %2,1", xop, plen, 3);
+	  avr_asm_len ("%4lpm $ mov %B0,%3 $ adiw %2,1", xop, plen, 3);
+	  avr_asm_len ("%4lpm $ mov %C0,%3", xop, plen, 2);
+
+	  if (REGNO (dest) == REG_Z - 2)
+	    {
+	      if (!reg_unused_after (insn, all_regs_rtx[REG_31]))
+		avr_asm_len ("pop r31", xop, plen, 1);
+	    }
+	  else if (!reg_unused_after (insn, addr))
+	    avr_asm_len ("sbiw %2,2", xop, plen, 1);
+
+	  break; /* 3 */
+
+	case 4:
+	  avr_asm_len ("%4lpm $ mov %A0,%3 $ adiw %2,1", xop, plen, 3);
+	  avr_asm_len ("%4lpm $ mov %B0,%3 $ adiw %2,1", xop, plen, 3);
+	  if (REGNO (dest) != REG_Z - 2)
+	    {
+	      avr_asm_len ("%4lpm $ mov %C0,%3 $ adiw %2,1", xop, plen, 3);
+	      avr_asm_len ("%4lpm $ mov %D0,%3", xop, plen, 2);
+	      if (!reg_unused_after (insn, addr))
+		avr_asm_len ("sbiw %2,3", xop, plen, 1);
+	    }
+	  else
+	    {
+	      avr_asm_len ("%4lpm $ push %3 $ adiw %2,1", xop, plen, 3);
+	      avr_asm_len ("%4lpm $ mov %D0,%3", xop, plen, 2);
+	      avr_asm_len ("pop $C0", xop, plen, 1);
+	    }
+
+	  break; /* 4 */
 	}
 
       break; /* REG */

Reply via email to