https://gcc.gnu.org/bugzilla/show_bug.cgi?id=126552

            Bug ID: 126552
           Summary: const-qualified local register variable used as an asm
                    operand is silently discarded
           Product: gcc
           Version: 16.1.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: middle-end
          Assignee: unassigned at gcc dot gnu.org
          Reporter: bernie at codewiz dot org
  Target Milestone: ---

Created attachment 65196
  --> https://gcc.gnu.org/bugzilla/attachment.cgi?id=65196&action=edit
repro testcase

The attached testcase a0-repro.c mimics the expansion of NDK macros used when
calling AmigaOS dynamic library functions which take arguments in specified
m68k registers.

Godbolt playground: https://godbolt.org/z/36aszb4ox

A library call like this:

  BltPattern(rp, NULL, xmin, ymin, xmax, ymax, mask);

Expands to:

  {
    struct RastPort *_v1 = rp;
    const PLANEPTR _v2 = 0L;
    register int _d0 __asm("d0");
    register int _d1 __asm("d1");
    register struct RastPort *_n1 __asm("a1") = _v1;
    register const PLANEPTR _n2 __asm("a0") = _v2;  // VANISHES!
    register void *const _bn __asm("a6") = GfxBase;

    __asm volatile("jsr %%a6@(-0x138:W)"
                   : "=r"(_d0), "=r"(_d1)
                   : "r"(_bn), "rf"(_n1), "rf"(_n2)
                   : "fp0", "fp1", "cc", "memory");
  }

Compiled for m68k at -O1 and above, the const case never writes A0:

  call_const:
        move.l %a6,-(%sp)
        move.l 8(%sp),%a1
        move.l GfxBase,%a6
        clr.l %d0           // Did you mean A0?
        jsr %a6@(-0x138:W)
        move.l (%sp)+,%a6
        rts

  call_const_nonconst_var:
        move.l %a6,-(%sp)
        move.l 8(%sp),%a1
        sub.l %a0,%a0       // OK
        move.l GfxBase,%a6
        jsr %a6@(-0x138:W)
        move.l (%sp)+,%a6
        rts


This affects GCC versions 13.4 through 16.1. GCC 6.5.0 clears A0 in both cases,
at any optimization level.

The manual documents that a const-qualified local register variable may have
its initializer substituted into an asm statement, causing the operand to
appear in a different register:

  https://gcc.gnu.org/onlinedocs/gcc/Local-Register-Variables.html

That may be what happens above, but it happens silently: the operand is placed
in
a register the programmer did not ask for, and nothing is diagnosed at any
warning level.

That could be a legitimate use of the latitude the docs reserve, so this is
primarily filed as a request for a warning, not as wrong code.

A diagnostic matters because the AmigaOS NDK inline headers generate exactly
this construct for every library call, takeing the type of the register
variable from a const-qualified parameter:

  register const PLANEPTR _n2 __asm("a0") = _v2;

Every such call passing a NULL pointer therefore passes whatever A0 happened to
hold, leading to memory corruption and very unpleasant debugging sessions.

Reply via email to