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

Reply via email to