https://gcc.gnu.org/g:7f04fddec3bc33b6fb418f3980995b9b7697e6b1
commit r12-10827-g7f04fddec3bc33b6fb418f3980995b9b7697e6b1 Author: Georg-Johann Lay <a...@gjlay.de> Date: Sat Nov 23 12:51:32 2024 +0100 AVR: target/117744 - Fix asm for partial clobber of address reg, gcc/ PR target/117744 * config/avr/avr.cc (out_movqi_r_mr): Fix code when a load only partially clobbers an address register due to changing the address register temporally to accommodate for faked addressing modes. (cherry picked from commit ee8e6784876aa050d2e01f54d1da4acf758b635a) Diff: --- gcc/config/avr/avr.cc | 76 ++++++++++++++++++++++++++++++++++----------------- 1 file changed, 51 insertions(+), 25 deletions(-) diff --git a/gcc/config/avr/avr.cc b/gcc/config/avr/avr.cc index c0f5242c0b2e..5d63c7153d6e 100644 --- a/gcc/config/avr/avr.cc +++ b/gcc/config/avr/avr.cc @@ -4241,11 +4241,11 @@ avr_out_movqi_r_mr_reg_disp_tiny (rtx_insn *insn, rtx op[], int *plen) rtx base2 = all_regs_rtx[1 ^ REGNO (dest)]; if (!reg_unused_after (insn, base2)) - avr_asm_len ("mov __tmp_reg__,%0" , &base2, plen, 1); + avr_asm_len ("mov __tmp_reg__,%0", &base2, plen, 1); avr_asm_len (TINY_ADIW (%I1, %J1, %o1) CR_TAB "ld %0,%b1", op, plen, 3); if (!reg_unused_after (insn, base2)) - avr_asm_len ("mov %0,__tmp_reg__" , &base2, plen, 1); + avr_asm_len ("mov %0,__tmp_reg__", &base2, plen, 1); } return ""; @@ -4272,40 +4272,66 @@ out_movqi_r_mr (rtx_insn *insn, rtx op[], int *plen) { /* memory access by reg+disp */ - int disp = INTVAL (XEXP (x, 1)); - if (AVR_TINY) return avr_out_movqi_r_mr_reg_disp_tiny (insn, op, plen); + if (plen) + *plen = 0; + + int disp = INTVAL (XEXP (x, 1)); + rtx base = XEXP (x, 0); + rtx base2 = all_regs_rtx[1 ^ REGNO (dest)]; + bool partial_clobber = (reg_overlap_mentioned_p (dest, base) + && ! reg_unused_after (insn, base2)); + if (disp - GET_MODE_SIZE (GET_MODE (src)) >= 63) - { - if (REGNO (XEXP (x, 0)) != REG_Y) - fatal_insn ("incorrect insn:",insn); + { + // PR117744: The base register overlaps dest and is + // only partially clobbered. + if (partial_clobber) + avr_asm_len ("mov __tmp_reg__,%0", &base2, plen, 1); - if (disp <= 63 + MAX_LD_OFFSET (GET_MODE (src))) - return avr_asm_len ("adiw r28,%o1-63" CR_TAB - "ldd %0,Y+63" CR_TAB - "sbiw r28,%o1-63", op, plen, -3); + if (REGNO (XEXP (x, 0)) != REG_Y) + fatal_insn ("incorrect insn:",insn); - return avr_asm_len ("subi r28,lo8(-%o1)" CR_TAB - "sbci r29,hi8(-%o1)" CR_TAB - "ld %0,Y" CR_TAB - "subi r28,lo8(%o1)" CR_TAB - "sbci r29,hi8(%o1)", op, plen, -5); - } + if (disp <= 63 + MAX_LD_OFFSET (GET_MODE (src))) + avr_asm_len ("adiw r28,%o1-63" CR_TAB + "ldd %0,Y+63" CR_TAB + "sbiw r28,%o1-63", op, plen, 3); + else + avr_asm_len ("subi r28,lo8(-%o1)" CR_TAB + "sbci r29,hi8(-%o1)" CR_TAB + "ld %0,Y" CR_TAB + "subi r28,lo8(%o1)" CR_TAB + "sbci r29,hi8(%o1)", op, plen, 5); + + if (partial_clobber) + avr_asm_len ("mov __tmp_reg__,%0", &base2, plen, 1); + + return ""; + } else if (REGNO (XEXP (x, 0)) == REG_X) { /* This is a paranoid case LEGITIMIZE_RELOAD_ADDRESS must exclude it but I have this situation with extremal optimizing options. */ - avr_asm_len ("adiw r26,%o1" CR_TAB - "ld %0,X", op, plen, -2); - - if (!reg_overlap_mentioned_p (dest, XEXP (x, 0)) - && !reg_unused_after (insn, XEXP (x, 0))) - { - avr_asm_len ("sbiw r26,%o1", op, plen, 1); - } + // PR117744: The base register overlaps dest and is + // only partially clobbered. + bool clobber_r26 = (partial_clobber + && REGNO (base) == (REGNO (base) & ~1)); + if (partial_clobber + && ! clobber_r26) + avr_asm_len ("mov __tmp_reg__,%0", &base2, plen, 1); + + avr_asm_len ("adiw r26,%o1" CR_TAB + "ld %0,X", op, plen, 2); + + if (clobber_r26) + avr_asm_len ("subi r26,lo8(%o1)", op, plen, 1); + else if (partial_clobber) + avr_asm_len ("mov %0,__tmp_reg__", &base2, plen, 1); + else if (! reg_unused_after (insn, base)) + avr_asm_len ("sbiw r26,%o1", op, plen, 1); return ""; }