On Sun, Sep 6, 2026 at 4:37 PM Matt Turner <[email protected]> wrote:
>
> On Sun, Sep 6, 2026 at 10:02 AM Matt Turner <[email protected]> wrote:
> >
> > On Sun, Sep 6, 2026 at 2:34 AM Richard Biener
> > <[email protected]> wrote:
> > >
> > >
> > >
> > > > Am 06.09.2026 um 04:59 schrieb Matt Turner <[email protected]>:
> > > >
> > > > Using a cast as an lvalue is a GCC extension that clang does not
> > > > implement. Clang rejects it with -Winvalid-gnu-asm-cast, which is an
> > > > error by default, so these blocks fail to build with clang.
> > > >
> > > > The casts do nothing.
> > >
> > > That’s not true. The casts serve as typing the macros, with wrong type
> > > argument you get a diagnostic.
> > >
> > > So NACK.
> >
> > Ah, I hadn't thought about that. I'll try to come up with a way of
> > keeping the type safety.
>
> Okay, dug into this a bit more.
>
> For a genuine size mismatch, the cast doesn't give a helpful
> diagnostic, it just refuses to compile:
>
> unsigned short s;
> __asm__(... : "=r" ((UWtype)s) : ...); // error: lvalue required in
> 'asm' statement
>
> GCC only accepts the cast-as-lvalue extension when it's a no-op (same
> size); anything else is a blanket rejection, not a targeted "wrong
> type" diagnostic. And without the cast, the same wrong-size code
> frequently compiles clean too, only failing if the assembler happens
> to catch a register-size clash in that particular instruction, which
> is arch- and instruction-dependent, not something the compiler
> guarantees.
>
> More importantly, for same-size type mismatches (e.g. passing a
> pointer where UWtype is expected, or wrong signedness), which is the
> more realistic bug in these macros, the cast catches nothing at all,
> with or without it:
But same-size is a sufficient check. Sure, there is no nice error.
> void *p;
> __asm__("mov %1,%0" : "=r" (p) : "r"(in)); // compiles, no warning
> __asm__("mov %1,%0" : "=r" ((UWtype)p) : "r"(in)); // compiles, no
> warning, identical asm
>
> Both produce identical output. So the cast isn't providing the type
> safety being claimed; it only accepts or rejects based on operand
> size, and even that check is a hard compile error rather than a
> diagnostic pointing at the actual problem.
>
> This also matches GMP's own history here: GMP's longlong.h dropped
> most of these casts in 1997 (commit 3c47be392, "Don't cast result
> operands"), though a stray one on an output operand survives to this
> day (the ARM umul_ppmm block), which looks like an oversight.
>
> I'm not sure a replacement type-safety mechanism is practical for this
> header specifically. longlong.h is copied and independently maintained
> by several other projects (GMP, libgcrypt, and formerly glibc, which
> dropped its copy entirely in favor of gmp-arch.h), so it probably
> can't depend on anything from gcc/system.h or other GCC-internal
> facilities? A portable substitute (e.g. an old-style
> negative-array-size sizeof check) would only recover the same
> size-only check the cast already gave, which as shown above isn't
> catching the mismatches that actually matter, at the cost of macro
> clutter at every call site.
>
> Given all that, and that clang rejects the construct outright via
> -Winvalid-gnu-asm-cast (an error by default) -- which is a real, live
> problem in libgcrypt's own longlong.h today, not a hypothetical -- I
> think dropping the casts without a replacement is the right call.
> Happy to discuss if there's a different failure mode you've hit that
> the cast was actually catching.
I'll note that GCC defines how __asm__ works, if clang doesn't implement
the extension correctly (in a compatible way), it's a clang bug.
> If we weren't concerned with compatibility, we could do something like
>
> #define add_ssaaaa(sh, sl, ah, al, bh, bl) \
> do { \
> _Static_assert (__builtin_types_compatible_p (__typeof__ (sh),
> UWtype), "sh must be UWtype"); \
> _Static_assert (__builtin_types_compatible_p (__typeof__ (sl),
> UWtype), "sl must be UWtype"); \
> __asm__ ("addl %5,%1\n\tadcl %3,%0" \
> : "=r" (sh), "=&r" (sl) \
> : "0" (ah), "g" (bh), "%1" (al), "g" (bl)); \
> } while (0)
I was hoping _Generic could somehow come to the rescue? Also
since the macros use __asm__ they can assume it's GCC (compatible),
so a __builtin_types_compatible_p check should be fine. Worrying
about compatibility in the face of __asm__ is a bit odd (but then
you are asking for compatibility with clang ...).
Also about the "wrong" cast you removed for the unused target,
can you instead drop the code completely rather than leaving it
even "more" broken? Possibly split that out.
Richard.