https://gcc.gnu.org/bugzilla/show_bug.cgi?id=127441
Bug ID: 127441
Summary: [M68K] [LRA] wrong code at -Os: va_list argument slot
updated with a stale stack offset after push
Product: gcc
Version: 16.2.0
Status: UNCONFIRMED
Keywords: wrong-code
Severity: normal
Priority: P3
Component: c
Assignee: unassigned at gcc dot gnu.org
Reporter: bernie at codewiz dot org
CC: pinskia at gcc dot gnu.org, [email protected]
Blocks: 113939
Target Milestone: ---
Host: x86_64-linux
Target: m68k-elf
gcc.c-torture/execute/va-arg-9.c fails at -Os with -mlra on m68k.
Reduced:
$ cat vaarg.c
#include <stdarg.h>
extern void abort (void);
extern void exit (int);
int __attribute__ ((noipa))
check (int a)
{
return a;
}
void
fap (int n, va_list ap)
{
while (n--)
if (check (va_arg (ap, int)) != n)
abort ();
}
void
f (int n, ...)
{
va_list ap;
va_start (ap, n);
fap (n, ap);
va_end (ap);
}
int
main (void)
{
f (4, 3, 2, 1, 0);
exit (0);
}
$ m68k-unknown-elf-gcc -Os -mlra -S vaarg.c
fap() keeps the va_list in its incoming argument slot.
Without the slot is bumped before the argument push:
move.l 12(%sp),%a0
addq.l #4,12(%sp)
move.l (%a0)+,-(%sp)
jsr check
With -mlra the incremented pointer is stored back after the push,
but with the offset that was correct before the push, so it lands
in the slot of the "n" argument (8(%sp) before the push) and the
va_list stays stale:
move.l 12(%sp),%a0
move.l (%a0)+,-(%sp)
move.l %a0,12(%sp) <- should be 16(%sp) after the push
jsr check
The store is the output reload of the auto-incremented address register
of the push insn. From -fdump-rtl-reload:
(insn 39 10 12 3 (set (reg/f:SI 8 a0 [41])
(mem/f/c:SI (plus:SI (reg/f:SI 15 sp)
(const_int 12 [0xc])) [1 ap+0 S4 A16])) ... {*movsi_m68k2}
(insn 12 39 40 3 (set (mem:SI (pre_dec:SI (reg/f:SI 15 sp)) [2 S4 A16])
(mem:SI (post_inc:SI (reg/f:SI 8 a0 [41])) ...)) ... {*movsi_m68k2}
(expr_list:REG_INC (reg/f:SI 8 a0 [41])
(nil)))
(insn 40 12 14 3 (set (mem/f/c:SI (plus:SI (reg/f:SI 15 sp)
(const_int 12 [0xc])) [1 ap+0 S4 A16])
(reg/f:SI 8 a0 [41])) ... {*movsi_m68k2}
Insn 40, the reload storing the incremented a0 back to the slot,
is placed after insn 12 but uses the same sp offset as insn 39 before it:
the pre_dec of sp inside insn 12 is not accounted for in the elimination
offset of the after-insn reload.
Old reload handled this by emitting "addq.l #4,12(%sp)" before the insn
instead.
FAIL: -mlra -Os
PASS: -mlra -Os -fno-omit-frame-pointer
PSSS: -mlra -O1/-O2/-O3 (because ap is kept in a register there)
Also reproduced in m68k-unknown-elf 16.1.0 via Compiler Explorer:
https://godbolt.org/z/s9j31Tqnb
Referenced Bugs:
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=113939
[Bug 113939] Switch m68k to LRA