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 3d23abd3dd)
This commit is contained in:
Georg-Johann Lay 2024-07-01 12:31:01 +02:00
parent 1d6c409fdf
commit 53305588cf
3 changed files with 138 additions and 3 deletions

View File

@ -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);

View File

@ -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;
}

View File

@ -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;
}