feat(wasm-dist): k_mutex_unlock wasm-cross-LTO module (complete: build + consume)#60
Conversation
…mpoline + dist build) Promotes the silicon-validated mutex unlock to a shippable wasm-cross-LTO module mirroring the sem module (#59), now that synth v0.11.41 fixes the #331 spill-slot collision that previously deadlocked it. - zephyr/wasm/mutex_unlock_shim.c: production shim (faithful Zephyr v4.4.0 k_mutex layout owner@8/lock_count@12; seam = gale_k_mutex_unlock_decide inside the bundle). - zephyr/wasm/gale_wasm_mutex_tramp.S: r11=0 trampoline (--native-pointer-abi). - scripts/build-wasm-dist.sh: refactored into a build_module() helper; now emits BOTH sem and mutex modules + a combined manifest. mutex uses --native-pointer-abi. Verified: build produces a valid ET_REL mutex .o — synth_k_mutex_unlock_body global, gale_k_mutex_unlock_decide localized, all kernel imports renamed to gale_w_*, no un-renamed imports; arg0-home spill slot write-once (synth#331 signature absent). First silicon measurement: k_mutex_unlock = 501 cyc (G474RE, synth 0.11.41), SELFCHECK rc=0 owner=0. Testbed primitives lane GREEN. Follow-up (separate PR): Kconfig CONFIG_GALE_WASM_LTO_MUTEX + CMakeLists consumption wiring + release-wasm.yml attach, mirroring the sem module's. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ild; consumption wiring follow-up)
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
…odule symbol fix Completes the k_mutex_unlock wasm-cross-LTO module (build #60) into a consumable feature, mirroring the sem module: - zephyr/gale_mutex.c: #ifndef GALE_WASM_LTO_OVERRIDE_MUTEX_UNLOCK guard around the native z_impl_k_mutex_unlock (mirrors gale_sem.c). - zephyr/Kconfig: CONFIG_GALE_WASM_LTO_MUTEX. - zephyr/CMakeLists.txt: consumption block under CONFIG_GALE_KERNEL_MUTEX (artifact discovery via GALE_WASM_LTO_OBJ_DIR / _MUTEX_OBJ; links the .o + r11=0 tramp). gale_wasm_wrappers.c added only when CONFIG_GALE_WASM_LTO_SEM didn't (shared gale_w_* defs — avoids double-definition when both modules are on). - release-wasm.yml: job name (sem + mutex); dist/* already attaches both. multi-module symbol fix (build-wasm-dist.sh): export ONLY the body entry via objcopy --keep-global-symbol=<body>, localizing the decide AND synth's internal func_N helpers. Without this, sem.o and mutex.o each carry global func_7/func_8 and collide at final link — caught by building tests/kernel/mutex/mutex_api with BOTH CONFIG_GALE_WASM_LTO_{SEM,MUTEX}=y. #59 never hit it (sem shipped alone). Verified on nucleo_g474re (cortex-m4f): tests/kernel/mutex/mutex_api links with CONFIG_GALE_WASM_LTO_MUTEX=y (native unlock compiled out, synth_k_mutex_unlock_body + trampoline linked) AND with both sem+mutex wasm on (no duplicate symbols). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The option landed inside the 'if GALE_KERNEL_SEM ... endif' block, so it was unreachable unless the SEM kernel was also enabled. Moved it under GALE_KERNEL_MUTEX with 'depends on GALE_KERNEL_MUTEX'. Verified: with CONFIG_GALE_KERNEL_SEM unset + CONFIG_GALE_KERNEL_MUTEX=y, tests/kernel/mutex/ mutex_api now accepts CONFIG_GALE_WASM_LTO_MUTEX=y and links (nucleo_g474re). Also exercises the sem-off branch of the gale_wasm_wrappers.c dedup guard. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… building (func_N collision, Kconfig mis-gate)
|
…_globals corrupted at Zephyr link, 64KB .data); -> draft, characterized, owed root-cause
Root-cause narrowing (firing follow-up)Ruled out two suspects:
So the corruption is link-context-specific: the The deeper lever: the dissolved object shouldn't carry 64 KB of wasm linear memory for a decide that uses a handful of constants — that's both the source of these absolute relocs and a non-starter on a 128 KB-RAM target. Trimming the emitted linmem to what the decide actually references (synth-side) would remove the reloc shape entirely, the way the sem module has none. Holding #60 draft; this is the gate. |
Root cause pinned — and the fix is on our side (mirror the sem module)The corruption traces to the decide's return ABI, not the linker per se:
Fix (gale-side, mirrors sem): change (Also avoids the 64 KB-RAM cost entirely — a separate non-starter on a 128 KB target.) |
Correction — tested the u64-repack hypothesis at the artifact level; it's promising-but-partial, not a full fixI prototyped the packed-
So returning Where that leaves it:
Reverted the scratch; #60 stays draft. Honest status: root cause is the link-context |
|
Filed the synth-side fix lever as pulseengine/synth#345 — dissolved |
…full mutex_api on G474RE, both complex_inversion + recursive, VCP autodetect) One-command verification to run the moment synth#345 (.bss + PC-rel) lands: rebuilds the dist mutex .o (--native-pointer-abi) -> builds upstream tests/kernel/mutex/mutex_api with CONFIG_GALE_WASM_LTO_MUTEX=y -> flash G474RE -> assert PROJECT EXECUTION SUCCESSFUL, no FAULT. The no-waiter microbench only exercised UNLOCKED; this runs the full suite that caught the real faults (complex_inversion link-corruption + the without-flag recursive MPU fault). Run from the feat/wasm-mutex-module branch (needs the shim + CMakeLists wiring) until PR #60 merges. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ted prior 3-arg error) — sem-shaped/seam-folded, drop-in-ready; full module queued (modest value, #60 blocked) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… shim (gale#62) The dissolved z_impl_k_mutex_unlock omitted the priority restoration the real unlock does via adjust_owner_prio — the unlocking thread kept its inherited boost, failing test_mutex_priority_inheritance (wasm-LTO FAIL / native PASS). Add two gale_w_* wrappers (k_thread is opaque to the wasm shim): - gale_w_adjust_thread_prio: z_thread_prio_set(thread, new_prio) if changed - gale_w_thread_prio: read base.prio Shim UNLOCKED path now restores cur to owner_orig_prio BEFORE handoff (while mutex->owner is still cur), and records the new owner's orig prio on handoff. Verified on G474RE silicon (synth v0.11.43): mutex_api 4/4 + mutex_api_1cpu 5/5 PASS incl test_mutex_priority_inheritance (6.001s, was FAIL 0.520s), no fault. Module shape unchanged (.data=4, MOVW_ABS=0). Closes #62. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Marking ready for review — both blockers resolved and the module is silicon-verified-correct. Blockers cleared:
Verified on real G474RE silicon (synth v0.11.43, CI: 60/60 green (the lone earlier Mutex joins sem (give+take) + pipe (rd/wr) as a verified-correct dissolved primitive; this one is full-suite-correct on hardware, not just shape. |
Promotes the silicon-validated
k_mutex_unlockto a complete, consumable wasm-cross-LTO module, mirroring the sem module (#59), now that synth v0.11.41 fixes the #331 spill-slot collision that previously deadlocked it on hardware.Build pipeline + shim
zephyr/wasm/mutex_unlock_shim.c— production shim (faithful Zephyr v4.4.0k_mutexlayout: owner@8 / lock_count@12). Seam (gale_k_mutex_unlock_decide) inside the bundle so loom inlines it.zephyr/wasm/gale_wasm_mutex_tramp.S— ther11=0native-pointer trampoline.scripts/build-wasm-dist.sh— refactored intobuild_module(); emits both sem + mutex + a combined manifest.Consumption wiring (mirrors sem)
zephyr/gale_mutex.c—#ifndef GALE_WASM_LTO_OVERRIDE_MUTEX_UNLOCKguard compiles out the native unlock when the module is linked.zephyr/Kconfig—CONFIG_GALE_WASM_LTO_MUTEX.zephyr/CMakeLists.txt— consumption block underCONFIG_GALE_KERNEL_MUTEX(artifact discovery viaGALE_WASM_LTO_OBJ_DIR/_MUTEX_OBJ; links the.o+ trampoline).gale_wasm_wrappers.cadded only when the sem block didn't (sharedgale_w_*defs).release-wasm.yml— job name;dist/*already attaches both modules.Multi-module symbol fix (found by building, not assuming)
Building
tests/kernel/mutex/mutex_apiwith bothCONFIG_GALE_WASM_LTO_{SEM,MUTEX}=yrevealed that sem.o and mutex.o each carry synth's internalfunc_7/func_8helpers as global symbols → multiple-definition at final link. #59 never hit this (sem shipped alone). Fix:build-wasm-dist.shnow exports only the body entry (objcopy --keep-global-symbol=<body>), localizing the decide + allfunc_Ninternals.Verification (nucleo_g474re, cortex-m4f, synth 0.11.41)
synth_k_{sem_give,mutex_unlock}_body(func_N localized).tests/kernel/mutex/mutex_apilinks withCONFIG_GALE_WASM_LTO_MUTEX=y— native unlock compiled out,synth_k_mutex_unlock_body+ trampoline linked.k_mutex_unlock = 501cyc,SELFCHECK rc=0 owner=0. synth#331 signature absent (arg0 home write-once); testbed primitives lane GREEN. Repro: jessrepro/synth-331/.🤖 Generated with Claude Code