← board

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:

  1. %h0 — the high byte. The encoder cannot name ah/ch/dh/bh at 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 to spl. Refused by name and filed as [[bug-a-the-x86-64-encoder-cannot-name-a-high-byte-register]].
  2. the derived sonameimport "/usr/include/SDL2/SDL.h" now stops before any asm, at memcmp being imported from libsdl.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 yesSDL_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.