Files
wasm2go/internal/codegen/f16_table_detect_test.go
8b87a233f0 Auto-detect runtime-built f16 tables; fail the build on stale asserts (#53)
* codegen: auto-detect runtime-built f16 tables; fail on stale asserts

The f16->f32 table's address had to be asserted from outside
(-f16-table): ggml-style engines build the table at runtime, so the
static byte-check cannot see it. That external magic number proved a
liability twice over — the address moves with any layout shift
(source changes, even build-cache state), a stale value silently
disables every table-keyed rewrite (the llama-wasm v0.2.1 release
shipped ~35% slower decode this way), and the existing staleness
warning never ran for multi-package modules because that layout
returns before the check.

Derive the address from the module's own initialization code instead:
an init loop that streams constant-strided stores from a constant
base and provably covers the table's whole 256KiB range. Detection is
structural over the lowered SSA — a loop-carried pointer phi with
constant start and advance, a counter phi with a computable trip
count, stores through the pointer — so it does not depend on how the
compiler vectorized the conversion arithmetic inside the loop. The
scan runs lazily on the first gather-site query.

-f16-table remains as an explicit override, now cross-checked: a set
address that contradicts a module-verified base FAILS the build,
naming the address to adopt; the no-evidence case keeps the old
warning, which now also runs on the multi-package path.

Verified against the llama engine module: flagless transpile
auto-detects the table and produces a byte-identical bundle to the
correctly-asserted build (121 FCVT conversions intact); a stale
assert aborts with the actionable error.

Co-Authored-By: Claude Fable 5 <[email protected]>

* codegen: drop the f16-table address input; keep only a boolean opt-out

With auto-detection proven equivalent (flagless transpile of the
engine module is byte-identical to the previously asserted build),
an address-carrying override has no remaining legitimate use — it
only reintroduces the stale-value hazard. -f16-table is gone;
-no-f16-table disables the table-keyed rewrites outright. The
end-of-translation diagnostic is now unconditional: gather sites
whose base verified neither statically nor through init-loop
coverage are reported, so a detection miss after a module change is
loud in every pipeline log.

Co-Authored-By: Claude Fable 5 <[email protected]>

---------

Co-authored-by: Claude Fable 5 <[email protected]>
2026-08-24 16:57:54 +09:00

42 lines
1.2 KiB
Go

package codegen
import (
"bytes"
"testing"
"github.com/goccy/wasm2go/internal/testfixture"
"github.com/goccy/wasm2go/internal/wasm"
)
// TestDetectRuntimeTables drives the init-loop detector over the
// fixture's two store loops: one covering a full f16-table range
// (verifies) and one covering half of it (must not).
func TestDetectRuntimeTables(t *testing.T) {
bin := testfixture.Wasm(t, "f16_init_loop.wat")
mod, err := wasm.Parse(bytes.NewReader(bin))
if err != nil {
t.Fatalf("parse: %v", err)
}
tr := &translator{mod: mod}
tr.detectRuntimeTables()
if len(tr.runtimeTables) == 0 {
t.Fatal("no init-loop store regions detected")
}
if !tr.runtimeInitCovered(4096) {
t.Fatalf("full-range init loop not covering base 4096: %+v", tr.runtimeTables)
}
if tr.runtimeInitCovered(4100) {
t.Fatal("misaligned base must not verify (range ends short)")
}
if tr.runtimeInitCovered(266240) {
t.Fatalf("half-range init loop must not verify: %+v", tr.runtimeTables)
}
// The detection feeds f16TableOK without any assertion.
if !tr.f16TableOK(4096) {
t.Fatal("f16TableOK rejected an init-covered base")
}
if tr.f16TableOK(266240) {
t.Fatal("f16TableOK accepted a half-covered base")
}
}