SIMD
Single instruction, multiple data via WebAssembly’s native 128-bit vector type.
--enable-simd adds v128 with an
f32x4/i32x4 subset (splat, lanes, arithmetic, compares,
min/max, bit ops, load/store). Two rules make one source
serve two artifacts:
ASC_FEATURE_SIMDfolds to 1 under the flag and 0 without it, and constantifconditions never compile their dead branch — so scalar and wide backends live in the same file.v128cannot appear in any function signature without the flag, since declarations always type-check — keep wide code in function bodies behind the constant guard.
// Scalar without --enable-simd, four lanes wide with it (this repl
// compiles with the flag); the dead branch never compiles, so the
// scalar artifact stays v128-free and loads on non-simd runtimes.
// Raw loads put this behind the unsafe gate.
export unsafe function integrate(xs: usize, vs: usize,
dt: f32, count: i32): void {
if (ASC_FEATURE_SIMD) {
for (let i = 0; i < count; i += 4) {
let x = v128.load(xs + (<usize>i << 2));
let v = v128.load(vs + (<usize>i << 2));
v128.store(xs + (<usize>i << 2),
f32x4.add(x, f32x4.mul(v, f32x4.splat(dt))));
}
return;
}
for (let i = 0; i < count; i++) {
let p = xs + (<usize>i << 2);
store<f32>(p, load<f32>(p) + load<f32>(vs + (<usize>i << 2)) * dt);
}
}
export unsafe function demo(): f32 {
let xs = __alloc(16 << 2);
let vs = __alloc(16 << 2);
for (let i = 0; i < 16; i++) {
store<f32>(xs + (<usize>i << 2), f32(i));
store<f32>(vs + (<usize>i << 2), 0.5);
}
integrate(xs, vs, 2.0, 16);
let sum: f32 = 0;
for (let i = 0; i < 16; i++) {
sum += load<f32>(xs + (<usize>i << 2));
}
return sum; // 120 + 16 × 0.5 × 2.0 = 136, both artifacts
}
// f32x4.min/max diverge from the scalar ternary on NaN and ±0;
// compare + bitselect matches scalar bit-for-bit, which is what keeps
// differential harnesses exact (the Box2D W lane uses this shape).
// v128 in a signature is legal only under the flag.
function min4(a: v128, b: v128): v128 {
return v128.bitselect(a, b, f32x4.le(a, b));
}
export function step(px: f32, py: f32): f32 {
let c = f32x4.splat(px);
c = f32x4.replace_lane(c, 1, py);
let best = min4(c, f32x4.splat(100.0));
if (ASC_FEATURE_RELAXED_SIMD) {
// Fused, single-rounding: deterministic against an fmaf
// reference, but a different golden than mul+add.
best = f32x4.relaxed_madd(best, f32x4.splat(0.5), f32x4.splat(1.0));
} else {
best = f32x4.add(f32x4.mul(best, f32x4.splat(0.5)), f32x4.splat(1.0));
}
return f32x4.extract_lane(best, 0);
}
--enable-relaxed-simd adds f32x4.relaxed_madd /
relaxed_nmadd and ASC_FEATURE_RELAXED_SIMD. Fused fma is
single-rounding deterministic, so the relaxed artifact carries its own bit-exact
contract — deterministic output stays byte-identical, just to a different
golden.