← board

Labels-as-values on the remaining 32-bit backends

6eea46f7c (x86-64) and its aarch64 follow-up added AN_LABELADDR / IR_LABELADDR and AN_GOTO_INDIRECT / IR_JUMP_INDIRECT. Measured on test/c_labels_as_values.c, 2026-09-02:

i386     pascal26:32: error: target i386: IR op not yet supported: labeladdr
arm32    pascal26:32: error: target arm32: IR op not yet supported: labeladdr
riscv32  pascal26:32: error: target riscv32: unsupported node in IR codegen: labeladdr
xtensa   pascal26:5: error: C program entry stub not implemented for this target yet
wasm32   pascal26:5: error: C program entry stub not implemented for this target yet

A named refusal on each — that is IROpName doing its job, and it is why this ticket could be written from one command instead of from a backend edit.

What each one needs

Why the priority is low

test-lua-cross runs aarch64, arm32, i386 and riscv32; the Makefile's own comment says the last three "await their variadic-ABI bring-up (they build-fail early)". So implementing this on them turns one early build failure into a later one and makes no job green. It becomes real the moment a target's variadic ABI lands, or the moment a corpus program other than lua wants a computed goto.

Measured: it is the WHOLE blocker, not the first of several (frankZ, 2026-09-02)

Binary 135bb8fec65f1271, commit 2cf53df52, gate.sh quick GREEN, reseeded from stable_linux_amd64/default/pinned (converged after 2 round(s)).

make test-lua-cross here reproduces seven's red exactly — aarch64 6/6, and all three of the others stop on the same node:

target i386:    IR op not yet supported: labeladdr
target arm32:   IR op not yet supported: labeladdr
target riscv32: unsupported node in IR codegen: labeladdr

Not a variadic-ABI failure. Compiling the identical runner with -DLUA_USE_JUMPTABLE=0, which is the one flag that stops lua emitting &&label:

target builds lua suite under qemu
i386 yes 6 pass, 0 fail
arm32 yes 6 pass, 0 fail
riscv32 yes 6 pass, 0 fail

So every other part of the port already works on all three, and IR_LABELADDR plus IR_JUMP_INDIRECT is the entire distance between here and a green test-lua-cross.

Scoping this claim honestly, because the same suite has already fooled someone once. frankD established that these six lua programs do NOT discriminate the two interpreter paths — a -DLUA_USE_JUMPTABLE=0 build passes 6/6 on x86-64 as well. So the table above is NOT evidence that the jump-table interpreter works on these targets; it cannot be, since that is precisely the build it excludes. It is evidence about the REST of the port: that nothing else is missing behind the node that stops the compile. Whoever implements the node still owes a run of the real (jump-table) build, and a binary comparison at the same flags is the control that tells the two apart.

What it blocks

test-lua-cross#src:tools/compiler_srchash.sh — 1 of the 16 jobs red in seven's full tier at 0f4d2c907d54. Wired to [[umbrella-one-full-tier-run-with-no-red-tier]].

Done — all three, and test-lua-cross is 24/24 (frankD, 2026-09-02)

Binary 5df66928aa39, converged after 1 round(s).

make test-lua-cross now passes every script on every target in LUA_CROSS_TARGETS: aarch64, arm32, i386, riscv32 — 6 each, 0 fail, 0 skip. test/c_labels_as_values.c prints gcc's eight lines under all three new targets and is wired as a row in test-i386, test-arm32 and test-riscv32.

The residual question this ticket left open is closed. frankZ's table was scoped honestly to "nothing ELSE in the port is missing", because the six lua scripts do not discriminate the two interpreter paths and the =0 build was the one measured. The control that does discriminate is binary identity at the same flags, and it was run on the two targets whose encodings are new work:

target default vs -DLUA_USE_JUMPTABLE=1 default vs -DLUA_USE_JUMPTABLE=0
i386 identical differs
riscv32 identical differs

So the binary that passed 6/6 IS the jump-table binary, and the =0 row is the positive control proving the flag reaches the code at all — without it, "identical" would also be what a flag that does nothing produces.

A second, independent control comes free from the failure this ticket describes: IR op not yet supported: labeladdr can only be raised by a source that emits &&label. The same runner that raised it now builds. Two readings that fail differently.

What each backend got

The i386 route deliberately diverges from this ticket's own advice, which said to prefer the existing absolute IR_PROCADDR shape over "inventing a thunk". That advice was written before the position-dependence work: an absolute address patched at finalize is exactly what feature-a-x86-64-object-output-is-position-dependent exists to remove, and --emit-obj on i386 would need a relocation for every &&label. The thunk needs none and costs two bytes plus a stack round-trip once per label reference, not per dispatch. The advice was reasonable and is now stale; it is left above rather than edited, since the reasoning is the point.

Its fixup base is the one thing that does not transfer from the other four backends: every other patch site is target - (pos+4), but here the value in eax is the address the call pushed, which sits at pos-2. The first version used pos-1 and segfaulted under qemu on the first dispatch — caught because the eight-row test runs, not because anything looked wrong.

xtensa and wasm32 remain out of scope for the reason stated above: no C program links on them yet, and wasm32 has no indirect branch to an arbitrary code address at all.

Log