open-standards/wai/wai-ops-gpu/tests/mutants.rs
Transaction Science 836d0255df wai packaging stack: #426, #438 and #452's GPU tier, squashed onto main
One squashed history of three stacked PRs, onto main at b16816671:

- #426 (wai/pack-efficiency): a title's whole AV1 ladder in one package over one
  integer base; several floor rungs (packaging §7.1), a re-pack from a parent
  (§11.5), a floor that is the ladder's own CMAF (§7.7), a floor-only profile
  (§7.8), a streaming pack (§11.4), and the independent verifier at scale (§9.4).
- #438 (wai/cross-fabric): each pack and verify step on the fabric with the least
  measured energy (§11.6); the `fabrics` member of measurement claims
  (jwp-receipts §2.4.8, energy-measurement §6.3); the wai-pack-gpu crate;
  `wai-pack fabric`; `wai-pack floor-decode`.
- #452 (wai/gpu-int-tier), its GPU-tier part only: claiming decode-equivalence on a
  GPU (determinism-tiers §5), the WGSL integer semantics suite, the rules check on
  naga's IR, and the kernels' shift masks and min(max()) clamps.

Left out, by the review's split (EN-C takes it over): #452's platform decode in
dispatch, its commits 6ad92e2e8, 8695bc75f and fed8694f4 and its CHANGELOG entry:
extensions/platform-decode.md, platform-decode-conformance/, wai::platform_decode,
wai-web's platform_decode bundle and demo, and its two SPEC.md sentences.

#438's research note is not carried as written: it is rewritten as
wai/research/cross-fabric.md, with hardware named by architecture, PCI id and OS
version, and every table marked as one run. floor_decode.rs and a wai_pack.rs
comment name the platform's media engine and a full-length film the same way.

wai/CHANGELOG.md is the union of both sides' entries, the stack's first.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-24 20:31:28 -04:00

95 lines
5.9 KiB
Rust
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//! The corpus catches an edited kernel. Each mutant below changes one expression of
//! one shader module, the kind of slip an integer kernel makes: a rounding shift off by
//! one, a wrong saturation bound, a dropped carry or cross term, a sign rule, an index
//! rule. A device running the mutant must give at least one result of the corpus
//! (`tests/corpus.rs`) that differs from the committed one. The arithmetic probe is left
//! as shipped, so the device opens and the corpus alone has to catch the edit.
//!
//! Each adapter's result is one line, `ops-gpu mutants: key=… killed N of N`, appended
//! to `WAI_OPS_GPU_REPORT` when it is set.
#[path = "support/corpus.rs"]
mod corpus;
use std::io::Write;
use wai_ops_gpu::{blocking, kernels::Sources};
#[derive(Clone, Copy)]
enum Module {
Ops32,
Ops64,
Move,
}
/// `(name, module, the text, its replacement)`; the text must occur in the module.
const MUTANTS: &[(&str, Module, &str, &str)] = &[
("half_up addend off by one bit (64-bit rounding, acc32)", Module::Ops32, "let h = w_pow2(s - 1u);", "let h = w_pow2(s);"),
("half_up addend off by one bit (128-bit rounding, acc64)", Module::Ops64, "let h = q_pow2(s - 1u);", "let h = q_pow2(s);"),
("rounding shift one short (acc64)", Module::Ops64, "return q_sar(q_add(p, h), s);", "return q_sar(q_add(p, h), s - 1u);"),
("half_even ties to odd (acc64)", Module::Ops64, "(q_eq(rem, h) && (q.x & 1u) == 1u)", "(q_eq(rem, h) && (q.x & 1u) == 0u)"),
("half_even ties to odd (acc32)", Module::Ops32, "(w_eq(rem, h) && (q.x & 1u) == 1u)", "(w_eq(rem, h) && (q.x & 1u) == 0u)"),
("clamp operator saturates one below its top (acc64)", Module::Ops64, "put64(t, w_clamp(a64(t), lo, hi));", "put64(t, w_clamp(a64(t), lo, w_sub(hi, W(1u, 0u))));"),
("clamp policy saturates one above its bottom (acc32)", Module::Ops32, "O[t] = bitcast<u32>(min(max(v, lo), hi));", "O[t] = bitcast<u32>(min(max(v, lo + 1), hi));"),
("check policy misses values above its range (acc64)", Module::Ops64, "} else if (w_lt(v, lo) || w_lt(hi, v)) {", "} else if (w_lt(v, lo)) {"),
// In the 32-bit kernels the 32 × 32-bit product only ever sees magnitudes of at most
// 2^31, for which neither carry of its middle sum can be set (a0·b1 + a1·b0 stays
// below 2^32 − 2^17), so dropping one there is an equivalent mutant: it is dropped
// in the 64-bit kernels, whose products take full 32-bit words.
("product's middle carry dropped (acc64)", Module::Ops64, "let hi = p11 + (mid2 >> 16u) + ((c1 + c2) << 16u);", "let hi = p11 + (mid2 >> 16u) + (c2 << 16u);"),
("64-bit product's cross term dropped (acc64)", Module::Ops64, "return W(p.x, p.y + a.x * b.y + a.y * b.x);", "return W(p.x, p.y + a.x * b.y);"),
("signed product's sign rule (acc32)", Module::Ops32, "return select(m, w_neg(m), na != nb);", "return select(m, w_neg(m), na);"),
("post bias dropped (acc32)", Module::Ops32, "y = w_add(y, w_sext(m32(P.b_off + 2u * o)));", "y = y;"),
("transposed convolution's residue (acc64)", Module::Ops64, "for (var i = yy % P.sh; i < P.kh && i <= yy; i += P.sh) {", "for (var i = (yy + 1u) % P.sh; i < P.kh && i <= yy; i += P.sh) {"),
("convolution's bottom padding edge (acc32)", Module::Ops32, "if (iy < P.ph || iy - P.ph >= P.h) {", "if (iy < P.ph || iy - P.ph > P.h) {"),
("igdn term floored one bit further (acc64)", Module::Ops64, "n = q_add(n, q_sar(gx2, P.f));", "n = q_add(n, q_sar(gx2, P.f + 1u));"),
("leaky shift of 32 or more (acc32)", Module::Ops32, "if (s >= 32u) {\n return bitcast<u32>(bitcast<i32>(a) >> 31u);", "if (s > 32u) {\n return bitcast<u32>(bitcast<i32>(a) >> 31u);"),
("pixel_shuffle's sub-position order", Module::Move, "(y % r) * r + x % r", "(x % r) * r + y % r"),
("subpos_merge's column offset", Module::Move, "copy_el((c * P.ho + 2u * y + P.k0) * P.wo + 2u * x + P.k1, t);", "copy_el((c * P.ho + 2u * y + P.k1) * P.wo + 2u * x + P.k0, t);"),
];
fn report(line: &str) {
println!("{line}");
if let Ok(p) = std::env::var("WAI_OPS_GPU_REPORT") {
let mut f = std::fs::OpenOptions::new().create(true).append(true).open(&p).unwrap();
writeln!(f, "{line}").unwrap();
}
}
#[test]
fn every_mutant_is_caught_on_every_adapter() {
let d = blocking::discover();
if d.found.is_empty() {
report("ops-gpu mutants: SKIPPED: no adapter");
assert!(std::env::var_os("WAI_OPS_GPU_REQUIRE").is_none(), "WAI_OPS_GPU_REQUIRE is set and no adapter was found");
return;
}
let keys: Vec<String> = d.found.iter().map(|g| g.adapter().key()).collect();
let vs = corpus::vectors();
let mut killed = vec![0usize; keys.len()];
for &(name, module, from, to) in MUTANTS {
let mut src = Sources::shipped();
let text = match module {
Module::Ops32 => &mut src.ops32,
Module::Ops64 => &mut src.ops64,
Module::Move => &mut src.mv,
};
assert!(text.contains(from), "mutant {name:?}: its text is not in the module");
*text = text.replace(from, to);
let m = blocking::discover_with(&src, name);
assert_eq!(m.found.len(), keys.len(), "mutant {name:?}: the adapters did not all open: {:?}", m.refused.iter().map(|(_, w)| w).collect::<Vec<_>>());
for g in &m.found {
let k = keys.iter().position(|x| *x == g.adapter().key()).expect("the same adapters");
let t = corpus::run_all(g, &vs, 1);
let caught = !t.mismatches.is_empty() || !t.errors.is_empty();
println!("ops-gpu mutant: key={} {name:?}: {}", keys[k], t.mismatches.first().map_or_else(|| "SURVIVED".to_string(), |m| format!("caught: {m}")));
killed[k] += caught as usize;
}
}
for (k, key) in keys.iter().enumerate() {
report(&format!("ops-gpu mutants: key={key} killed {} of {}", killed[k], MUTANTS.len()));
}
for (k, key) in keys.iter().enumerate() {
assert_eq!(killed[k], MUTANTS.len(), "{key}: a mutant survived");
}
}