]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
AVR: target/122527 -- Don't use __load_N to load from __flash1.
authorGeorg-Johann Lay <avr@gjlay.de>
Sun, 2 Nov 2025 14:12:59 +0000 (15:12 +0100)
committerGeorg-Johann Lay <avr@gjlay.de>
Sun, 2 Nov 2025 15:55:08 +0000 (16:55 +0100)
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.

(cherry picked from commit 0b2c031cca4017c5f87af519f0977b33b4bcb284)

gcc/config/avr/avr.cc

index cf80d5a15551d49141baec253e561749524046df..2b625e637170fdfcb1af0501ffaeac2d09745360 100644 (file)
@@ -3293,7 +3293,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);
 }
 
 
@@ -3645,6 +3646,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 */