From 046593acf12ba06560d9f70dcb37b4a8f2491c4b Mon Sep 17 00:00:00 2001 From: Joseph Ferano Date: Fri, 11 Sep 2026 19:43:42 +0700 Subject: [PATCH] Say which joint is glued, so the next session does not trust it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Item 6 said the builtins archive has to come from wasi-sdk. It does not have to, and what is standing in its place is emscripten's compiler-rt for a different triple — which works, and is worth writing down as a substitution rather than leaving as "wasm32 works". --- NEXT.md | 71 ++++++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 58 insertions(+), 13 deletions(-) diff --git a/NEXT.md b/NEXT.md index 117f9ef..d44c047 100644 --- a/NEXT.md +++ b/NEXT.md @@ -161,6 +161,7 @@ reader ✅ → parse ✅ → load ✅ → check ✅ → emit ✅ → clang ✅ | `test/programs/restarts.flan` | **`restart-case` and `invoke-restart`: the transfer, across two frames** | | `test/test_emacs.ml` | **the client, driven against a real daemon and a real program** | | `test/reload_host.c` | the C host that loads and installs two rebuilds, in one process | +| `test/wasm-run.mjs` | **a WASI host in twenty lines of `node:wasi`, so the table can run a wasm32 build** | ``` $ flan run calc-me.flan "1 + 2 * (3 - 0.5) / 2" @@ -1392,19 +1393,63 @@ painful to retrofit: Deferred until after the dev loop: -6. **wasm32.** The user installed `wasi-libc-devel` and `wasi-libc-static`; the - sysroot is `/usr/wasm32-wasi` and `wasm-ld` is present. `clang - --target=wasm32-wasi --sysroot=/usr/wasm32-wasi` gets past the headers and - then **fails to link**: it wants - `lib/clang/20/lib/wasm32-unknown-wasi/libclang_rt.builtins.a`, which no - Fedora package provides (`dnf provides '*libclang_rt.builtins*wasm*'` finds - nothing). It has to come from a wasi-sdk release, dropped into clang's - resource directory. After that: teach `build.ml` `--sysroot`, and run the - acceptance table — `sand-headless.flan` included, which is exactly why it - does not import raylib — on both targets in CI. - Note plan.org has the *web* build linking raylib via emscripten, which - brings its own sysroot: wasi-sdk is right for the headless table, not - necessarily for the eventual game build. +6. ~~**wasm32.**~~ **Done, with one glued joint.** `flan build + --target=wasm32-wasi` produces a module, and `test/programs/sand-headless.flan` + prints `2256461126764447066` under it — the same hash as native, byte for + byte, at `-O2` and at `-O0`. That is the milestone: the RNG is ours rather + than libc's precisely so that number can be compared across targets, and it + compares equal. `values.flan` and `machine.flan` run there too, which is + where a 32-bit pointer would have shown. The acceptance table runs all four, + and skips them by *probing* — it builds the smallest program and runs it — + rather than by looking for a binary on PATH. + + Three things this cost that were not in the old note: + + - **The entry point is not `main`.** wasi-libc's start code calls + `__main_argc_argv`; clang renames C's argc/argv `main` to that, and the + `.ll` `Emit` writes says `@main` literally. The link succeeds and the + program traps on a signature-mismatched weak stub. `Build.wasm_main_source` + is a two-line C shim that bridges it, and the `__asm__("main")` label in it + is load-bearing: spelling the callee `main` makes clang rename *that* too + and the shim becomes an infinite self-call. + - **The target has to reach the C compiles, not just the link.** `flan_rt.c` + includes ``; without `--sysroot` it never gets that far. + `target_flags` is computed once and passed to both, and the whole flag + list — not just the triple — is in the object cache key, so repointing a + sysroot cannot serve a stale `.o`. + - **Fedora's sysroot is one level deeper** than wasi-sdk's: + `include/wasm32-wasi/stdio.h`, not `include/stdio.h`. Both shapes count. + + **The glued joint, and the one thing this contradicts in the old note.** The + old note said the builtins archive has to come from a wasi-sdk release. It + does not have to: emscripten builds the same compiler-rt for wasm32 and + calls it `libcompiler_rt.a`, and dropping that in as + `libclang_rt.builtins.a` links and runs. It is a different triple + (`wasm32-unknown-emscripten`) built by a different clang (22 against + Fedora's 20), so it is *substituting*, and wasi-sdk is still the proper + article. `build.ml` looks for `FLAN_WASM_BUILTINS`, then + `/opt/wasi-sdk/...`, then emscripten's beside `emcc` on PATH, and refuses by + name listing every path it tried when none is there. clang's resource + directory is root-owned, so the archive is not dropped into it — a shadow + resource directory is built under the object cache, named by a digest of + clang's own resource dir plus the archive's path, size and mtime, with the + real `include` symlinked in. + + **The runtime is Node.** No `wasmtime` and no `wasmer` on this machine; + `test/wasm-run.mjs` is twenty lines of `node:wasi` and the table prefers + `wasmtime` or `wasmer` if either appears. `--no-warnings`, because + `node:wasi` writes an `ExperimentalWarning` to stderr on every run and the + harness compares combined output. + + **Refused by name, not half-supported:** `--dev` with a wasm target (the + reload path is `dlopen`), `Build.shared` with one (same reason), and `flan + run --target=` (a `.wasm` is not something this host execs — build it and + point a runtime at it). + + Still open: raylib on wasm, which plan.org wants through emscripten and its + own sysroot. wasi-sdk is right for the headless table; it is not necessarily + right for the eventual game build. + 7. **Loose ends from milestone 4**, none of them blocking: block-scoped `defer`; package visibility, so `rl/get-color-raw` is not callable; a package importing a package; imported unions.