Repro
$ printf 'import "/usr/include/SDL2/SDL.h"\nprint("reached")\n' > h.py
$ ./compiler/pascal26 h.py out
pascal26:167: error: C: inline asm constraint "=Q" is not supported
SDL_endian.h:166: __asm__("xchgb %b0,%h0": "=Q"(x):"0"(x)); — SDL's
16-bit byteswap.
What Q means, and why the machinery is probably already there
In GCC's x86 constraint set, Q is any register addressable as rl/rh —
i.e. one of a, b, c, d. The diagnostic's own list says this frontend
already reads the fixed-register letters a b c d. So Q is the
"any one of the four" form of letters that already exist, rather than a new
register class.
That is an argument for where to look, not a claim that the fix is one line. The difference between a fixed letter and a choose-one-of-four letter is allocation, and this ticket has not read that code.
Scope beyond SDL
Q is common in hand-written x86 headers wherever a byte-half register is
needed. Whoever fixes it should check the sibling letters in the same family
(R, q, l) rather than adding one row — the neighbours are the same
mechanism and the second path is the one that stays broken.
Same family as the resolved
bug-c-inline-asm-is-x86-64-only-so-five-busybox-tus-refuse-on-i386.
Resolved 2026-09-10, frankH — and the ticket was right about the letter and wrong about the consequence
What landed
Q and q in cparser.inc as a RESTRICTED register class, which is the thing
the ticket correctly said it was: "the difference between a fixed letter and a
choose-one-of-four letter is allocation, and this ticket has not read that
code." That sentence was the accurate part. CAsmFixedReg could not carry it —
it answers with one register — so there is a third allocation phase between the
fixed pins and the general pool, taking c/a/d before the callee-saved b.
Both letters are honoured as the same four. On x86-64 q legally permits all
sixteen and is narrowed here, for the same reason g is honoured as r: a
subset of what a constraint permits is a choice it allows. One mechanism
instead of two that differ per target.
Then %b and %w, which were the NEXT wall on the same source line. A comment
in asmatt.inc read "%N, or a modifier like %b0 / %w0 / %k0 / %l0" and the
code read only a bare digit — the comment was the aspiration, the code was the
truth. Decided by measuring rather than reconciling: across every header on this
box the modifiers that occur are %w1 (12), %b0 (6), %w0 (5), %h0 (4),
and no %k, %l, %q or %z. R and l constraint letters do not occur
either, so they stay refused by name.
The title's second clause is the part to correct
"…and it blocks every SDL header" — it did, and it was not alone, which the ticket had no way to see because the compiler stops at the first error. Two more walls stand between here and an SDL2 import, and neither is this one:
%h0— the high byte. The encoder cannot nameah/ch/dh/bhat all: at byte width it FORCES a REX prefix for register numbers 4..7, and REX is precisely what makes the high-byte forms unencodable. Emitting one anyway assembles quietly tospl. Refused by name and filed as [[bug-a-the-x86-64-encoder-cannot-name-a-high-byte-register]].- the derived soname —
import "/usr/include/SDL2/SDL.h"now stops before any asm, atmemcmpbeing imported fromlibsdl.so, a name the compiler invented from the header's stem. That guard is correct and is not this ticket's.
So =Q was a queue position, not a size — which is what
umbrella-pxx-compiles-fpc-itself measured four times today and what CLAUDE.md
now says about first-failure censuses generally.
A measurement for the soname wall, since it is now the front of the queue
CORRECTION, 2026-09-10, same day: an earlier version of this section said the
dynsym ticket had been DECLINED and that its counterexample "does not survive".
Both were wrong.
feature-n-derive-a-header-s-library-from-its-directory-and-verify-it-against-the-library-s-own-dynsym
is OPEN in backlog-nilpy, and net/if.h / libnet.so.9 is stated inside
it, as the reason a plain directory fallback is unsafe and a dynsym check is
what makes it safe. Its own summary: "That upgrades the guard from does this library exist to does this library answer, catches net/if.h, and is what
SDL2 ... needs." There was no argument to win and I was preparing to win it.
Where it came from: frankB wrote "deliberately NOT landed and filed with its counterexample", meaning the naive fallback was not landed and a ticket was filed instead. I read "not landed" as applying to the ticket. A verb about the CODE was carried over to the TICKET, and the correction cost a peer a message.
What the measurement is actually worth is confirmation with numbers, on this box:
| header | derived | exports the header's symbols? |
|---|---|---|
SDL2/SDL.h |
libSDL2-2.0.so.0 |
yes — SDL_Init, SDL_CreateWindow |
net/if.h |
libnet.so.9 |
no — zero of them; if_nametoindex is in libc |
Which is that ticket's central claim, now measured rather than reasoned.
Test: test/casm_byte_classes.c, four rows against gcc on identical source.
b_wrap is the row that separates a byte add from a 32-bit one (0x12FF + 1 is
0x1200, where a 32-bit add carries into the second byte). The pre-fix compiler
refuses the file by name, verified by revert and rebuild.