Skip to content

Commit 95f1983

Browse files
NathanFlurryclaude
andcommitted
wasm-gui: Root-1 course-correction — typed-func-refs is WRONG; fix = fpcast arity tuning + marshallers
A deep feasibility research agent (read the toolchain + V8 runtime) proved the user-named "typed-function-references" does NOT fix the ~13.4s GObject cascade: call_ref type-checks exactly like call_indirect, so a GObject UB cast mismatch still traps -- typed-func-refs/GC cannot run the mismatched calls faster. The real cost is binaryen fpcast-emu uniform max-arity padding + per-call trampoline, not the call opcode. V8-here runs GC-wasm fine (V8 130 default-on), so not blocked, just the wrong layer. Also corrected: no max-func-params@128 in-repo; link-xapp.sh:24 uses BARE --fpcast-emu (binaryen default arity). REAL Root-1 fix (cheapest-first): (1) QUICK WIN hours/one-line -- --pass-arg=max-func-params@<measured-true-max> (padding scales with arity; GObject closures 1-4 ptr args); (2) days -- force per-signature C marshallers to kill g_cclosure_marshal_generic then drop/scope --fpcast-emu; (3) weeks -- per-arity trampoline tables. Spec Root-1 item + PERF-FINDINGS.md + status-log T69 updated. Also captured: #2 in-place pwrite caveat is real (RootFileSystem uses the default VirtualFileSystem::pwrite read_file-clone via self.overlay; fix needs dispatch-chain forwarding). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent fb63dc6 commit 95f1983

3 files changed

Lines changed: 40 additions & 4 deletions

File tree

experiments/wasm-gui/M8-STATUS-LOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,3 +282,4 @@
282282
2026-06-26T67 (gmodule CONSOLIDATED -- one shim, duplication removed): thunar-sbr now loads through the EXISTING shared toolchain/gmodule-shim.c + a generated table (gmodule-plugins-thunar.gen.c from build-thunar.sh), the SAME mechanism the xfce4-panel plugins use. The table's references to the 3 entry points pull the SBR from its archive + keep it past --gc-sections, so the keep-shim/symwrap/sentinel were unnecessary -- DELETED (gmodule-static-shim.c, thunar-sbr-symwrap.c, thunar-sbr-keep.c). The one shared-shim change is additive (accept a path if the name is registered for xfce_panel_module_init/construct OR thunar_extension_initialize; the per-binary table makes these mutually exclusive -> no panel regression, verified: shim compiles, panel table has 0 thunar entries). Re-verified thunar bulk-rename renamers load via the shared shim (xu5-thunar-sbr-consolidated.png). Full panel rebuild-verify kicked off. One gmodule mechanism for both plugin families.
283283
2026-06-26T67b (panel regression-verify PASSED): full xfce4-panel rebuild with the shared gmodule-shim.c additive change -> fresh 54MB binary, exit 0, 0 undefined symbols, 0 thunar refs leaking into the panel link. The consolidation is fully verified: thunar-sbr renamers load via the shared shim AND the panel is unaffected. gmodule chapter closed.
284284
2026-06-26T68 (thunar-sbr renamers FUNCTIONAL, not just rendered): XTEST-injected "RENAMED_" into the bulk-rename Text field (button 1 + focus + type, POST_INJECT_DELAY 40s, proven --timeout 120) -> the New Name column LIVE-PREVIEWS RENAMED_vacation-001.jpg / RENAMED_vacation-002.jpg / RENAMED_notes.txt and the Apply button enables. Proof: ~/tmp/gui-progress/2026-06-26T17/xu5-thunar-sbr-functional.png. So the gmodule-loaded thunar-sbr plugin RUNS its rename logic, not just draws the dialog -- a functional capstone to the gmodule consolidation (T65-67). (FB-capture note: the first attempt at --timeout 175 emptied the FB; the proven --timeout 120 captured it -- a harness timing detail, not a guest issue.)
285+
2026-06-26T69 (PERF PIVOT — Root-1 course-corrected by feasibility research): the user-named "typed-function-references" is the WRONG fix for the ~13.4s GObject cascade. A deep research agent (read toolchain+V8) proved call_ref type-checks exactly like call_indirect (the GObject cast mismatch still traps), so typed-func-refs/GC do NOT remove the cost — the cost is binaryen fpcast-emu's UNIFORM max-arity padding + per-call trampoline. V8-here runs GC-wasm fine (V8 130 default-on), so not blocked, just the wrong layer. CORRECTION: no max-func-params@128 exists in-repo; link-xapp.sh:24 uses BARE --fpcast-emu (binaryen default arity). REAL FIX cheapest-first: (1) QUICK WIN hours/one-line — --pass-arg=max-func-params@<measured-true-max> (padding scales with arity; GObject closures 1-4 ptr args); (2) days — force per-signature C marshallers to kill g_cclosure_marshal_generic, then drop/scope --fpcast-emu; (3) weeks — per-arity trampoline tables. Captured in PERF-FINDINGS.md. Also (concrete win #2 in-place pwrite): the analysis caveat is REAL — RootFileSystem uses the DEFAULT VirtualFileSystem::pwrite (vfs.rs:335, read_file full-clone + write_file) via self.overlay (root_fs.rs:307/323); a MemoryFileSystem-only override would be dead — the fix needs pwrite forwarding down the dispatch chain (RootFileSystem→overlay→inner) + an in-place inner impl.

experiments/wasm-gui/PERF-FINDINGS.md

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ real near-term native wins are the autonomous findings here; the big structural
5858

5959
## Relation to the B roadmap
6060

61-
- **Root-1 (typed-function-references)**confirmed: the ~13.4s GObject CSS cascade is indirect-call density
61+
- **Root-1 (fpcast arity tuning + per-signature marshallers)** — the ~13.4s GObject CSS cascade is fpcast-emu uniform-arity padding (NOT the call opcode; typed-func-refs was disproven — see the Root-1 CORRECTION below). Biggest single-guest win. KEEP, start here.
6262
under `--fpcast-emu`. Biggest single-guest win. KEEP, start here.
6363
- **Root-2 (thread-safe kernel + multiplex)** — the parallelism wall. KEEP.
6464
- **T1 (postMessage→SAB)** — DEMOTED for native (see above). The serialization wins on native are #5/#6/#9.
@@ -71,3 +71,33 @@ Grab the cheap autonomous framebuffer/throughput wins (#2 pwrite, #6 iov, #1 tim
7171
**Root-1** investigation; they're quick, measurable, and hit the desktop hot path — then **Root-2** for the
7272
multi-guest parallelism. T1's SAB-ring work is reframed as in-process clone/base64 elimination (#5/#9), not a
7373
postMessage replacement, and only the browser executor benefits from a request-side ring.
74+
75+
## ★★ Root-1 CORRECTION (2026-06-26): typed-function-references is the WRONG fix
76+
77+
A deep feasibility agent (read the toolchain + V8 runtime) found the named approach does not work:
78+
- **`call_ref` type-checks exactly like `call_indirect`** — a GObject cast mismatch still traps. Typed
79+
function references / wasm-GC do NOT remove the mismatched-signature cost; they just move it. There is no wasm
80+
typing feature that lets one call site dispatch to genuinely-different runtime signatures without per-signature
81+
adaptation (true in MVP and GC).
82+
- **The real cost is fpcast-emu's UNIFORM max-arity padding + per-call trampoline**, not the call opcode.
83+
binaryen's `FuncCastEmulation` pads EVERY indirect-callable function + EVERY call site to one wide uniform
84+
signature (all params → i64, fixed arity), so every GObject closure/marshal/vfunc call shuffles the full padded
85+
arity. Cost ≈ (#indirect-calls) × (arity coercions + extra dispatch). GObject is almost all indirect calls.
86+
- **V8-here CAN run GC-wasm** (V8 130, GC + typed-func-refs default-on, no flags set — `isolate.rs:102-109`), so
87+
the approach is not blocked by the runtime; it's just aimed at the wrong layer. NOT the fix.
88+
- **Correction to the prior framing:** there is NO `max-func-params@128` tuning anywhere in-repo. `link-xapp.sh:24`
89+
uses BARE `--fpcast-emu` → binaryen's DEFAULT arity. So we're paying the default padding on every call.
90+
91+
### The ACTUAL Root-1 fix (cheapest-first)
92+
1. **QUICK WIN (hours, one line):** `--fpcast-emu --pass-arg=max-func-params@<measured-true-max>` at
93+
`link-xapp.sh:24`. The padding cost scales with the fixed arity; GObject closures are mostly 1–4 pointer args,
94+
so lowering from binaryen's default to the measured true max should remove a large fraction of the ~12s.
95+
**Risk:** a genuinely-indirect function with more params than N breaks → MEASURE the true max, keep an
96+
app-still-runs assertion. Highest value-per-effort; do FIRST.
97+
2. **ROOT CAUSE (days):** kill the generic-marshaller path. The UB-cast traffic is GObject's *generic* marshaller
98+
(`g_cclosure_marshal_generic`, libffi-style arbitrary-signature). Force per-signature C marshallers
99+
(`g_cclosure_marshal_VOID__*`) for the signals `show_all` exercises → correctly-typed `call_indirect`, no
100+
emulation → drop/scope `--fpcast-emu` narrowly. Biggest structural win.
101+
3. (weeks) per-arity trampoline tables (custom binaryen). Only if 1+2 insufficient.
102+
103+
So Root-1's roadmap entry becomes "fpcast arity tuning + per-signature marshallers", NOT typed-function-references.

experiments/wasm-gui/XUBUNTU-SPEC.md

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,14 @@ needed**. (Still do the security work correctly: write the hostile-input securit
2525
kernel validates all guest-written ring data — and treat the concurrency refactor with TCB-grade care. The
2626
never-self-approve list — D-Bus-to-host, host-fd, GPU, host-network — is unchanged and still needs sign-off.)
2727
Start with Root-1:
28-
1. **Root-1 — typed-function-references.** Replace `--fpcast-emu` for GObject's mismatched-signature function
29-
pointers. Cuts the ~13s GObject construction cascade (the per-guest compute root); speeds every guest AND
30-
relieves Root-2. No new attack surface. *(Start here.)*
28+
1. **Root-1 — fpcast arity tuning + per-signature marshallers** *(was "typed-function-references" — feasibility
29+
research proved that WRONG: `call_ref` type-checks like `call_indirect`, so it can't run GObject's UB casts any
30+
faster; the real cost is binaryen fpcast-emu's uniform max-arity padding, not the opcode. See `PERF-FINDINGS.md`)*.
31+
The ~13s GObject cascade is fpcast-emu padding every indirect call to one wide uniform arity. **Quick win
32+
(hours):** `link-xapp.sh:24` uses BARE `--fpcast-emu` (binaryen default arity) — add
33+
`--pass-arg=max-func-params@<measured-true-max>` (GObject closures are 1–4 ptr args). **Root cause (days):**
34+
force per-signature C marshallers to kill `g_cclosure_marshal_generic`, then drop/scope `--fpcast-emu`. Speeds
35+
every guest AND relieves Root-2. No new attack surface. *(Start here.)*
3136
2. **T1 — in-process serialization elimination** *(was "SAB ring transport"; DEMOTED/REFRAMED by the ultracode
3237
analysis — see `PERF-FINDINGS.md`)*. **postMessage is BROWSER-ONLY**; the native production path (what the
3338
desktop runs on) is fully in-process (V8 isolate ↔ Rust kernel share one address space) — **no postMessage, no

0 commit comments

Comments
 (0)