IROpName names 68 of 75 IR ops, so a missing backend arm for the other seven reports unknown
error: target xtensa: unsupported node in IR codegen: unknown
That is the whole diagnostic. The op was IR_CLASSREF.
Counted by parsing defs.inc's IR_* = n; constants against ir.inc's
IROpName case arms, not by reading them:
IR ops declared in defs.inc |
75 |
named by IROpName |
68 |
| unnamed | 7 |
IR_PROCADDR(38) IR_CLASSREF(39) IR_VMTADDR(58) IR_IMTADDR(59)
IR_SET_SIGNAL(65) IR_IO_LOCK(66) IR_IO_UNLOCK(67)
Why it matters more than a cosmetic string
IROpName has exactly one load-bearing caller: the unsupported node in IR codegen error every backend raises for an op it does not implement. So for
these seven ops the error names nothing, on every target, and the only
way to learn which op is missing is to edit the backend and self-compile.
That is not hypothetical — it is how this was found, and it cost a build. The
same shape as target xtensa: unsupported binary operator (div/mod/shifts pending), fixed on 2026-08-30 for the same reason: a diagnostic that cannot
name its own subject makes the reader guess, and the guess is usually the
message's own stale hint.
Fix
Seven lines in ir.inc's IROpName. The names are already the constant
identifiers; use the same lowercase-without-prefix convention the other 68 use
(procaddr, classref, vmtaddr, imtaddr, set_signal, io_lock,
io_unlock).
Worth adding with them: a compile-time or startup assertion that IROpName
covers every declared op, so the eighth gap cannot open silently. The count
above was produced by a parser in ten seconds; nothing in the tree runs it.
Why filed and not fixed
ir.inc is shared Track A ground and a Track S stop-line.
Bound
Static count at f19e16b67bad, by parsing both files. The claim that IROpName
has one load-bearing caller is from grepping its call sites. Not measured: how
many of the seven have a missing arm on any given backend today — only
IR_CLASSREF was observed to actually surface, on xtensa.
RESOLVED 2026-08-31 — frank-rust
Seven arms added; an independent parse of defs.inc against ir.inc now gives
75 declared, 75 named, 0 unnamed. The count was re-derived rather than taken
from the ticket, and matched it exactly.
The assertion the ticket asked for exists and is wired
"Worth adding with them: a compile-time or startup assertion that IROpName covers every declared op... The count above was produced by a parser in ten seconds; nothing in the tree runs it."
tools/iropname_lint.py, wired into gate.sh. Sub-second, parses two files,
builds nothing. "Nothing in the tree runs it" was the whole defect — the
parser already existed as a one-off; what was missing was a caller.
End-to-end, against pinned
PXXDBG=a.ir:* on --threadsafe hello.pas, which emits IR_IO_LOCK /
IR_IO_UNLOCK:
unknown |
io_lock |
io_unlock |
|
|---|---|---|---|
pinned |
80 | 0 | 0 |
| HEAD | 0 | 40 | 40 |
40 + 40 = 80 exactly, which is what makes it an identity rather than two numbers that happen to differ.
My first linter had the exact bug it was written to catch
The first version scanned the case body for IR_* tokens including
comments — and the comment I had just written above the seven new arms names
IR_CLASSREF. So deleting the IR_CLASSREF arm left the linter reporting
clean.
That is abi.inc's dead review grep, reproduced from scratch, hours after I
replaced it: a checker satisfied by prose. And it arrived by the same route
frankwasm identified — the better the surrounding comment, the more convincing
the dead check becomes. Writing a careful comment is what broke it.
The unit selftest did not catch it. Its synthetic fixtures had no comments,
so it passed while the tool was blind on the real file. It was caught only by a
real-tree control — delete a real arm, assert the file actually changed, then
require exit 1. Comment stripping and a comment_only control are now both in.
The transferable bit: a selftest over synthetic fixtures tests the logic, not the input. The fixture is written by the same person with the same blind spot, so it inherits the assumption instead of challenging it. Run the control against the real file.
Also fixed
ir_codegen_xtensa.inc:3391 said IROpName has no entry for this op — true when
written, false as of this commit, and it sits directly above the arm that exists
because of it. Rewritten to past tense with the resolution rather than deleted:
the original diagnostic is why that arm exists.
Log
- 2026-08-31 — resolved, commit 60f0f1982.