https://gcc.gnu.org/g:53305588cfbf74604bafcc27902e1eded5677ae6
commit r12-10591-g53305588cfbf74604bafcc27902e1eded5677ae6 Author: Georg-Johann Lay <a...@gjlay.de> Date: Mon Jul 1 12:31:01 2024 +0200 AVR: target/88236, target/115726 - Fix __memx code generation. PR target/88236 PR target/115726 gcc/ * config/avr/avr.md (mov<mode>) [avr_mem_memx_p]: Expand in such a way that the destination does not overlap with any hard register clobbered / used by xload8qi_A resp. xload<mode>_A. * config/avr/avr.cc (avr_out_xload): Avoid early-clobber situation for Z by executing just one load when the output register overlaps with Z. gcc/testsuite/ * gcc.target/avr/torture/pr88236-pr115726.c: New test. (cherry picked from commit 3d23abd3dd9c8c226ea302203b214b346f4fe8d7) Diff: --- gcc/config/avr/avr.cc | 8 +- gcc/config/avr/avr.md | 18 +++- .../gcc.target/avr/torture/pr88236-pr115726.c | 115 +++++++++++++++++++++ 3 files changed, 138 insertions(+), 3 deletions(-) diff --git a/gcc/config/avr/avr.cc b/gcc/config/avr/avr.cc index bc15017c61c..ee033d3204d 100644 --- a/gcc/config/avr/avr.cc +++ b/gcc/config/avr/avr.cc @@ -4071,7 +4071,13 @@ avr_out_xload (rtx_insn *insn ATTRIBUTE_UNUSED, rtx *op, int *plen) xop[2] = lpm_addr_reg_rtx; xop[3] = AVR_HAVE_LPMX ? op[0] : lpm_reg_rtx; - avr_asm_len (AVR_HAVE_LPMX ? "lpm %3,%a2" : "lpm", xop, plen, -1); + if (plen) + *plen = 0; + + if (reg_overlap_mentioned_p (xop[3], lpm_addr_reg_rtx)) + avr_asm_len ("sbrs %1,7", xop, plen, 1); + + avr_asm_len (AVR_HAVE_LPMX ? "lpm %3,%a2" : "lpm", xop, plen, 1); avr_asm_len ("sbrc %1,7" CR_TAB "ld %3,%a2", xop, plen, 2); diff --git a/gcc/config/avr/avr.md b/gcc/config/avr/avr.md index de029476908..f76249340b8 100644 --- a/gcc/config/avr/avr.md +++ b/gcc/config/avr/avr.md @@ -715,12 +715,26 @@ if (!REG_P (addr)) src = replace_equiv_address (src, copy_to_mode_reg (PSImode, addr)); + rtx dest2 = reg_overlap_mentioned_p (dest, lpm_addr_reg_rtx) + ? gen_reg_rtx (<MODE>mode) + : dest; + if (!avr_xload_libgcc_p (<MODE>mode)) /* ; No <mode> here because gen_xload8<mode>_A only iterates over ALL1. ; insn-emit does not depend on the mode, it's all about operands. */ - emit_insn (gen_xload8qi_A (dest, src)); + emit_insn (gen_xload8qi_A (dest2, src)); else - emit_insn (gen_xload<mode>_A (dest, src)); + { + rtx reg_22 = gen_rtx_REG (<MODE>mode, 22); + if (reg_overlap_mentioned_p (dest2, reg_22) + || reg_overlap_mentioned_p (dest2, all_regs_rtx[21])) + dest2 = gen_reg_rtx (<MODE>mode); + + emit_insn (gen_xload<mode>_A (dest2, src)); + } + + if (dest2 != dest) + emit_move_insn (dest, dest2); DONE; } diff --git a/gcc/testsuite/gcc.target/avr/torture/pr88236-pr115726.c b/gcc/testsuite/gcc.target/avr/torture/pr88236-pr115726.c new file mode 100644 index 00000000000..9fd5fd3b5f5 --- /dev/null +++ b/gcc/testsuite/gcc.target/avr/torture/pr88236-pr115726.c @@ -0,0 +1,115 @@ +/* { dg-do run { target { ! avr_tiny } } } */ +/* { dg-additional-options "-std=gnu99" } */ + +const __flash char fvals8[] = { 1, 2, 3 }; +char rvals8[] = { 0, 2, 4 }; + +const __flash int fvals16[] = { 1, 2, 3 }; +int rvals16[] = { 0, 2, 4 }; + +__attribute__((noinline, noclone)) +char xload8_r30 (const __memx char *pc) +{ + register char c __asm ("r30"); + c = *pc; + __asm (";;" : "+r" (c)); + return c; +} + +__attribute__((noinline, noclone)) +int xload16_r30 (const __memx int *pc) +{ + register int c __asm ("r30"); + c = *pc; + __asm (";;" : "+r" (c)); + return c; +} + +__attribute__((noinline, noclone)) +char xload8_r22 (const __memx char *pc) +{ + register char c __asm ("r22"); + c = *pc; + __asm (";;" : "+r" (c)); + return c; +} + +__attribute__((noinline, noclone)) +int xload16_r22 (const __memx int *pc) +{ + register int c __asm ("r22"); + c = *pc; + __asm (";;" : "+r" (c)); + return c; +} + +__attribute__((noinline, noclone)) +int xload16_r20 (const __memx int *pc) +{ + register int c __asm ("r20"); + c = *pc; + __asm (";;" : "+r" (c)); + return c; +} + +void test8 (void) +{ + char c; + for (int i = 0; i < 3; ++i) + { + c = xload8_r30 (fvals8 + i); + if (c != 1 + i) + __builtin_exit (__LINE__); + + c = xload8_r22 (fvals8 + i); + if (c != 1 + i) + __builtin_exit (__LINE__); + + c = xload8_r30 (rvals8 + i); + if (c != 2 * i) + __builtin_exit (__LINE__); + + c = xload8_r22 (rvals8 + i); + if (c != 2 * i) + __builtin_exit (__LINE__); + } +} + +void test16 (void) +{ + int c; + for (int i = 0; i < 3; ++i) + { + c = xload16_r30 (fvals16 + i); + if (c != 1 + i) + __builtin_exit (__LINE__); + + c = xload16_r22 (fvals16 + i); + if (c != 1 + i) + __builtin_exit (__LINE__); + + c = xload16_r20 (fvals16 + i); + if (c != 1 + i) + __builtin_exit (__LINE__); + + c = xload16_r30 (rvals16 + i); + if (c != 2 * i) + __builtin_exit (__LINE__); + + c = xload16_r22 (rvals16 + i); + if (c != 2 * i) + __builtin_exit (__LINE__); + + c = xload16_r20 (rvals16 + i); + if (c != 2 * i) + __builtin_exit (__LINE__); + } +} + +int main (void) +{ + test8(); + test16(); + + return 0; +}