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.