https://gcc.gnu.org/g:a56bab18b048d0edf8f8d81735982dee8b08bc78
commit r15-10476-ga56bab18b048d0edf8f8d81735982dee8b08bc78 Author: Georg-Johann Lay <[email protected]> Date: Sun Nov 2 15:12:59 2025 +0100 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. (cherry picked from commit 0b2c031cca4017c5f87af519f0977b33b4bcb284) Diff: --- gcc/config/avr/avr.cc | 43 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/gcc/config/avr/avr.cc b/gcc/config/avr/avr.cc index cf80d5a15551..2b625e637170 100644 --- a/gcc/config/avr/avr.cc +++ b/gcc/config/avr/avr.cc @@ -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 */
