using the compiler

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) and the lane shuffles: v128.shuffle<T>, the per-width i8x16/i16x8/ i32x4/f32x4.shuffle, and i8x16.swizzle. Two rules make one source serve two artifacts:

integrate.as — one source, two artifacts
// 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
}
lanes.as — wide-only helpers (compiles under the flag)
// 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);
}
shuffle.as — lane shuffles and byte swizzles
// A shuffle picks lanes from its two operands laid end to end: 0..7 for
// f32 lanes, 0..31 for bytes. Every lane index is a compile-time constant
// and every width lowers to the one wasm byte shuffle. i8x16.swizzle takes
// its byte selectors from a vector at run time; a selector past 15 reads 0.
export function spread(): f32 {
    // Two points as (x0, y0, x1, y1); a 2D transform wants the xs and the
    // ys spread across lanes, one shuffle each.
    let p = f32x4.replace_lane(f32x4.replace_lane(
        f32x4.replace_lane(f32x4.splat(1), 1, 2), 2, 3), 3, 4);
    let xs = v128.shuffle<f32>(p, p, 0, 0, 2, 2);   // (1, 1, 3, 3)
    let ys = v128.shuffle<f32>(p, p, 1, 1, 3, 3);   // (2, 2, 4, 4)
    let r = f32x4.add(f32x4.mul(xs, f32x4.splat(10)), ys);
    return f32x4.extract_lane(r, 2) + f32x4.extract_lane(r, 3);   // 34 + 34
}

export function reverse(): i32 {
    // Byte i holds i; lanes 16..31 would read from the second operand.
    let v = i32x4.replace_lane(i32x4.replace_lane(i32x4.replace_lane(
        i32x4.splat(0x03020100), 1, 0x07060504), 2, 0x0b0a0908), 3, 0x0f0e0d0c);
    let r = i8x16.shuffle(v, v, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0);
    return i32x4.extract_lane(r, 0);   // bytes 15..12, 0x0c0d0e0f
}

A lane index that is not a constant, or is outside the doubled lane count, is a compile error at that argument; v128.shuffle without its lane type is one too.

--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.