Fixed in F159 — the pass now LOWERS the bundled-glibc-msort VLA shape (no more reason=vla)
Root cause — why the pass refused
The classic glibc merge sort msort_r (bundled verbatim in the wild: GLib 2.56 gqsort.c, the Midnight Commander wall) allocates its merge temp as a VLA (dynamic alloca), stores it into the local msort_param, and merges through memcpy while the user comparator — which can suspend on this substrate — is dispatched indirectly.
At the whole-program glibc vantage memcpy is defined, and glibc's generic C memcpy (string/memcpy.c + sysdeps/generic/memcopy.h + wordcopy.c) converts both pointer args to integers up front (dstp = (long) dstpp), byte-copies through raw (unmasked) inttoptr, and hands the address-integers onward as integer arguments to the defined _wordcopy_fwd_* helpers. Every prior ptrtoint recognizer rightly failed on this: Q5/Q2/G-ptr-confined ban inttoptr; F134 requires an align-DOWN mask before the inttoptr and has no integer-call-arg form; F130 blesses only raw syscall args. So the deep leaf ptrtoint ptr %1 inside @memcpy refused msort_r with:
[hwjs-coro] REFUSE fn=msort_r reason=vla/dynamic-alloca-address-escape-opaque-sink ...
wasmld: FATAL (HWJS-3 coro arm): ... a refused def is a live ABI chimera. Stop-and-report.
and every glibc-carrying qsort/msort user died at the whole-program link.
What F159 does — and why it is frame-safe across suspends
F159 (pass/coroutinize/CoroutinizePass.cpp, ~lines 3513–3535 and 3678–3868; recognizer ptrtointWordcopyReaddress / wordcopyChainOk) is a classification-precision recognizer in the escape walk's ptrtoint arm — mechanism-based, no name table, so any bundled copy of the idiom in any package lowers identically. The integer chain from the region ptrtoint is followed through non-publishing scalar derivations (binop / trunc / *ext / select / phi / freeze / pure-int intrinsics, with the F156 shift-amount and F157 GEP-index terminals), accepting two new forms the prior recognizers refuse:
- (i) a plain (unmasked)
inttoptr reconstitution — the reconstituted pointer rejoins the main escape walk as an ordinary region derivation, so loads/stores through it are in-place linear-memory accesses and a downstream publish/host-sink still refuses (the never-silent boundary is inherited, not re-implemented); and
- (ii) a chain integer passed as an integer argument of a direct call to a defined, non-vararg function (the
_wordcopy_fwd_aligned(dstp, srcp, n) shape) — descended under the same rules, inner inttoptrs classified by a fresh callee-mode escape walk (Opaque / returned-region → fail).
Anything else still fails the chain — a store of the integer, a ret of the integer, a declared/indirect/vararg callee — and the region falls to the loud, counted Opaque catch-all (fail-safe; fixture controls on all three).
Frame-safety: the VLA merge temp DS-LOWERs to a frame-resident region (the same DS6 lowering as any coroutinized alloca), so the merge-temp storage lives in the coroutine frame and survives every suspend of the comparator. The address only ever leaves pointer form into (a) integers confined to non-publishing scalar arithmetic inside defined, module-local glibc code, and (b) pointers reconstituted from those integers that rejoin the ordinary walk and are used solely for in-place accesses to delta=0-copied linear memory. Same delta=0 witnessed-sound in-MEMORY bar as F130/F134 — classification-precision only, no emission/ABI change.
Now lowered vs. still loudly refused
- Lowered: a dynamic-alloca region merged through the glibc integer-laundering
memcpy/_wordcopy idiom (msort_r's p.t), including the s>32 indirect lane with pointer entries living inside the region.
- Still loudly refused (unchanged): a genuine escape of the dynamic-alloca address to an opaque/host sink outside delta=0 linear memory — a store of the address-integer, a
ret of it, or a publish through the reconstituted pointer. The never-silent wall is intact.
RED → GREEN evidence
Direct reproduction of the wall (pre-fix plugin, hwjs-cc 7d47bb1, no knobs):
[hwjs-coro] REFUSE fn=msort_r reason=vla/dynamic-alloca-address-escape-opaque-sink escaped=1 opaque_escape=1 ... suspends=4
wasmld: FATAL (HWJS-3 coro arm): ... a refused def is a live ABI chimera. Stop-and-report.
New superproject gate run-hwjscc2-gqsort-gate.mjs (verbatim GLib 2.56 gqsort.c fixture) — 9/9 on the final F159 plugin:
- GREEN: whole-program
wasmcc-glibc link SUCCEEDS; DS-LOWER fn=msort_r lowered=1/1; msort_r NOT refused.
- RUNTIME:
SORT OK, exit 0 — both the direct (s=4) and the s>32 indirect (pointers-in-region) msort lanes sort correctly with the comparator parking on every compare (66 parks); the VLA merge temp survives every park. CONTROL (no-park) also sorts.
- RED (genuinely flipping):
HWJS_CORO_NO_WORDCOPY_READDRESS=1 re-exposes the exact ticket refusal (reason=vla/dynamic-alloca-address-escape-opaque-sink) + the "live ABI chimera" fatal — verified behaviour-identical to a 7d47bb1-built plugin.
Also green on the final re-derived plugin (PLUGIN-PROVENANCE commit=60d95279c880 dirty=0): provenance gate 6/6, coroutinize pass suite 177 pass / 0 fail (7 environmental skips).
All distros green (one lineage forward together)
Kernel + all four distros rebuilt on the F159 plugin lineage; in the real glibc distro link the merge-sort family (msort_with_tmp, __qsort_r, qsort_r_mergesort, indirect_msort_with_tmp, qsort, bsearch) coroutinizes cleanly (refused=0):
linux-6.12-glibc-bash-coreutils — green (vmlinux.wasm 16,484,338 bytes)
linux-6.12-musl-bash-coreutils — green
linux-6.12-musl-busybox — green
wabuntu-24.04 — green (composition distro)
Substrate regressions clean on the final artifacts — no regressions from F159: musl malloc-churn (#67) GREEN + RED flips; glibc malloc-fatal (#73) GREEN + both RED arms flip; hardwarejs r6 + dlopen M1/M2-dlclose/M2-fork/M3-dispatch/gates (23 vitest tests) pass.
All three live machines deployed (additive): brintos/linux-6.12-{glibc-bash-coreutils,musl-bash-coreutils,musl-busybox}.
SHAs
- Pass:
brintos/hwjs-cc @ 60d95279c880ce9d9ec7fcc9800c5388e3b140b2 (F159), pushed to joel-dev (7d47bb1..60d9527).
- Superproject:
brintos/linux-in-the-browser @ 9bbdfbd (advances the toolchains/hwjs-cc gitlink 7d47bb1 → 60d95279 + adds the gqsort gate), pushed to joel-dev (8ef0121..9bbdfbd).
The recipe-side workaround (dropping gqsort.o and shipping a no-VLA heapsort) is no longer required — the bundled glibc msort links and runs as-is.
Closing as completed.