The install steps' comment said every WAI workflow that runs wasmtime pins one
version and passes both determinism settings, but nothing enforced it: a step
that ran a stock or differently pinned wasmtime would have merged green.
scripts/check-wasmtime-pin.py now fails, in the repository checks, any workflow
that
1. downloads a wasmtime release other than 50.0.1, or checks it against any
SHA-256 but that archive's;
2. installs wasmtime another way (cargo install, the install script, an action);
3. runs wasmtime (run, serve, compile, or a module given to it) or names it as
a CARGO_TARGET_*_RUNNER without -W nan-canonicalization=y and
-W relaxed-simd-deterministic=y.
It also fails when it finds no download or no run, so a check that looked in
the wrong place cannot pass. On this tree it finds the downloads and runs it
should. Its self-test (scripts/check-wasmtime-pin-selftest.sh) passes a scratch
workflow shaped like these and fails each way of breaking it that the
self-test lists, matching the reason in the output.
The comment's relaxed_madd claim was measured on arm64 only. It now rests on
both architectures, measured on an arm64 macOS host and an x86_64 Linux host
with a small probe (wasmtime 50.0.1's release for the host, checked against
the digest the release lists):
host Darwin arm64; wasmtime 50.0.1 (c307ec81d 2026-10-29)
f32.add(bits 0x7fa00001, 1.0) [no flags]: 0x7fe00001
f32x4.relaxed_madd(1+2^-12, 1+2^-12, -(1+2^-11)) [no flags]: 0x33800000
f32.add(bits 0x7fa00001, 1.0) [both flags]: 0x7fc00000
f32x4.relaxed_madd(1+2^-12, 1+2^-12, -(1+2^-11)) [both flags]: 0x33800000
an unknown -W option is refused: error: failed to parse -W / --wasm option:
unknown option: no-such-option
host Linux x86_64; wasmtime 50.0.1 (c307ec81d 2026-10-29)
(the same five lines, bit for bit)
0x33800000 is 2^-24, the fused result; the unfused fadd(fmul) gives 0. So the
NaN setting takes effect on both, and relaxed_madd is fused on both with and
without the relaxed setting. That matches 50.0.1's source (the
wasmtime-internal-cranelift crate, read from the registry): the translator
lowers F32x4/F64x2RelaxedMadd through fma_f32x4/fma_f64x2
(src/translate/code_translator.rs L2530-2539), which emit a native fma on a
host that has one, a fused library call under relaxed-simd-deterministic on a
host that has none, and the unfused fmul and fadd only on a host with no FMA
and the setting off (src/func_environ.rs L5907-5925). The setting is what
makes the result fused on every host. And since an unknown -W option stops
wasmtime, a run that exits 0 with the two flags has parsed them.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
107 lines
5.1 KiB
Shell
Executable file
107 lines
5.1 KiB
Shell
Executable file
#!/usr/bin/env bash
|
|
#
|
|
# check-wasmtime-pin-selftest.sh — check-wasmtime-pin.py can fail.
|
|
#
|
|
# A check that cannot fail is decoration. This builds a scratch tree with one workflow
|
|
# that installs the pinned wasmtime and runs it the ways the repository's workflows do,
|
|
# asserts that the check passes it, and then breaks it each way the property can break
|
|
# and asserts that the check fails, saying why.
|
|
set -uo pipefail
|
|
|
|
here=$(cd "$(dirname "$0")" && pwd)
|
|
check="$here/check-wasmtime-pin.py"
|
|
tmp=$(mktemp -d)
|
|
trap 'rm -rf "$tmp"' EXIT
|
|
W="$tmp/.github/workflows/w.yml"
|
|
|
|
mk() {
|
|
rm -rf "$tmp/.github"
|
|
mkdir -p "$tmp/.github/workflows"
|
|
cat > "$W" <<'YML'
|
|
jobs:
|
|
t:
|
|
steps:
|
|
# Every run passes -W nan-canonicalization=y -W relaxed-simd-deterministic=y.
|
|
- name: Install wasmtime 50.0.1
|
|
run: |
|
|
v=50.0.1
|
|
f="wasmtime-v$v-x86_64-linux.tar.xz"
|
|
curl -sSfL -o "$RUNNER_TEMP/$f" \
|
|
"https://github.com/bytecodealliance/wasmtime/releases/download/v$v/$f"
|
|
echo "685466455dd42a14774a86c36c8f2e45cbb9bd6d92a850139315df60a75498a5 $RUNNER_TEMP/$f" | sha256sum -c -
|
|
- name: tests under wasmtime
|
|
env:
|
|
CARGO_TARGET_WASM32_WASIP2_RUNNER: wasmtime run -W nan-canonicalization=y -W relaxed-simd-deterministic=y
|
|
run: cargo test --target wasm32-wasip2
|
|
- name: the corpus under wasmtime
|
|
run: |
|
|
W=$(pwd)
|
|
CARGO_TARGET_WASM32_WASIP2_RUNNER="wasmtime run -W nan-canonicalization=y -W relaxed-simd-deterministic=y --dir $W::$W" \
|
|
cargo test --release --target wasm32-wasip2
|
|
wasmtime --version
|
|
wasmtime run -W nan-canonicalization=y -W relaxed-simd-deterministic=y --dir .::/w \
|
|
x.wasm verify /w
|
|
wasmtime run \
|
|
-W nan-canonicalization=y -W relaxed-simd-deterministic=y x.wasm
|
|
cmp a b || { echo "::error::the output differs under wasmtime"; exit 1; }
|
|
YML
|
|
}
|
|
|
|
rep() { # rep <old> <new>: replace the first occurrence in the scratch workflow
|
|
python3 -I -c 'import sys; p, a, b = sys.argv[1:]; s = open(p).read(); assert a in s, a; open(p, "w").write(s.replace(a, b, 1))' "$W" "$1" "$2"
|
|
}
|
|
|
|
fails=0
|
|
expect() { # expect <pass|fail> <what> [text the output must hold]
|
|
local want=$1 what=$2 needle=${3:-} out rc
|
|
out=$(python3 -I "$check" --root "$tmp" 2>&1); rc=$?
|
|
if [ "$want" = pass ] && [ $rc -ne 0 ]; then echo "FAIL $what: the check failed"; printf '%s\n' "$out" | sed 's/^/ /'; fails=$((fails+1)); return; fi
|
|
if [ "$want" = fail ] && [ $rc -eq 0 ]; then echo "FAIL $what: the check passed"; fails=$((fails+1)); return; fi
|
|
if [ -n "$needle" ] && ! grep -qF -- "$needle" <<<"$out"; then echo "FAIL $what: the output does not say '$needle'"; printf '%s\n' "$out" | sed 's/^/ /'; fails=$((fails+1)); return; fi
|
|
echo "ok $what"
|
|
}
|
|
|
|
mk; expect pass "the pinned release, run every way with both settings, passes"
|
|
|
|
mk; rep "v=50.0.1" "v=49.0.2"
|
|
expect fail "another version" "downloads wasmtime 49.0.2"
|
|
|
|
mk; rep '/download/v$v/$f"' '/download/v38.0.3/wasmtime-v38.0.3-x86_64-linux.tar.xz"'; rep "v=50.0.1" ":"
|
|
expect fail "another version, named in the URL" "downloads wasmtime 38.0.3"
|
|
|
|
mk; rep "685466455dd4" "685466455dd5"
|
|
expect fail "another digest" "not its pinned digest"
|
|
|
|
mk; rep '| sha256sum -c -' '> /dev/null'
|
|
expect fail "no digest checked" "checks no SHA-256"
|
|
|
|
mk; rep "x86_64-linux.tar.xz" "aarch64-linux.tar.xz"
|
|
expect fail "an archive with no pinned digest" "has no pinned digest here"
|
|
|
|
mk; rep " steps:" " steps:
|
|
- run: cargo install wasmtime-cli --locked"
|
|
expect fail "wasmtime built from source" "installs wasmtime some other way"
|
|
|
|
mk; rep "RUNNER: wasmtime run -W nan-canonicalization=y -W relaxed-simd-deterministic=y" "RUNNER: wasmtime"
|
|
expect fail "a cargo runner with neither setting" "runs wasmtime without -W nan-canonicalization=y and -W relaxed-simd-deterministic=y"
|
|
|
|
mk; rep '--dir $W::$W"' '--dir $W::$W -W nan-canonicalization=y"'; rep 'RUNNER="wasmtime run -W nan-canonicalization=y -W relaxed-simd-deterministic=y' 'RUNNER="wasmtime run'
|
|
expect fail "a runner with one setting" "runs wasmtime without -W relaxed-simd-deterministic=y"
|
|
|
|
mk; rep "-W relaxed-simd-deterministic=y --dir .::/w" "--dir .::/w"
|
|
expect fail "a run without deterministic relaxed SIMD" "runs wasmtime without -W relaxed-simd-deterministic=y"
|
|
|
|
mk; rep " -W nan-canonicalization=y -W relaxed-simd-deterministic=y x.wasm" " x.wasm"
|
|
expect fail "a run continued over two lines, with neither setting" "runs wasmtime without"
|
|
|
|
mk; rep "wasmtime --version" "wasmtime x.wasm"
|
|
expect fail "a module given to wasmtime with no subcommand" "runs wasmtime without"
|
|
|
|
mk; rep "wasmtime --version" "wasmtime serve -W nan-canonicalization=y app.wasm"
|
|
expect fail "a serve without deterministic relaxed SIMD" "runs wasmtime without -W relaxed-simd-deterministic=y"
|
|
|
|
mk; printf 'jobs:\n t:\n steps:\n - run: true\n' > "$W"
|
|
expect fail "no workflow that runs wasmtime" "looked in the wrong place"
|
|
|
|
if [ $fails -ne 0 ]; then echo "check-wasmtime-pin-selftest: $fails failure(s)"; exit 1; fi
|
|
echo "check-wasmtime-pin-selftest: the check passes the pinned, flagged runs and fails each other."
|