open-standards/wai/wai-ops-gpu/tests/arith.rs
Transaction Science 98f2755d7f ci: wai-ops-gpu, the GPU equality on the macos runner's GPU and in its browser
The metal job runs the arithmetic probe, the corpus at both widths and the
mutants on the host's GPU, every capability that runs a wai-ops/1 graph with
its graphs there (the jpegai/v1 inputs fetched and required), and the corpus
in a headless browser's WebGPU. WAI_OPS_GPU_EXPECT declares the adapter and
WAI_OPS_GPU_REQUIRE makes its absence a failure; each step then requires its
report lines with their counts. The checks job, on a runner with no GPU,
requires that the device tests skip without WAI_OPS_GPU_REQUIRE and fail with
it, and builds the browser module, wai-rs's GPU target and the bench tool.

The arith test now appends its result line to the report as the others do.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-21 04:37:32 -04:00

64 lines
2.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 kernels' arithmetic on the device (`wgsl/arith.wgsl`): every 64-bit and 128-bit
//! function, from 32-bit words, against the host's 128-bit integers, on the edges of
//! every width pairwise, every shift under every rounding mode, and a million SplitMix64
//! cases, on every adapter found. Then an edit of one function (a carry dropped from the
//! 32 × 32-bit product) is shown to be caught: discovery refuses the adapter.
use std::io::Write;
use wai_ops_gpu::probe::{self, ProbeIn};
use wai_ops_gpu::{blocking, kernels::Sources};
/// A result line, also appended to `WAI_OPS_GPU_REPORT` when it is set.
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 the_arithmetic_is_the_hosts_on_every_adapter() {
let d = blocking::discover();
if d.found.is_empty() {
println!("ops-gpu arith: SKIPPED: no adapter: {:?}", d.refused.iter().map(|(a, w)| (a.key(), w.clone())).collect::<Vec<_>>());
assert!(std::env::var_os("WAI_OPS_GPU_REQUIRE").is_none(), "WAI_OPS_GPU_REQUIRE is set and no adapter was found");
return;
}
let mut cases: Vec<ProbeIn> = probe::cases(0, 0);
for seed in 0..16u64 {
let c = probe::cases(65536, 0xA417_0000 + seed);
cases.extend_from_slice(&c[c.len() - 65536..]);
}
for g in &d.found {
let mut checked = 0usize;
for chunk in cases.chunks(1 << 16) {
let got = blocking::probe(g, chunk).unwrap_or_else(|e| panic!("{}: {e}", g.adapter().key()));
assert_eq!(probe::first_mismatch(chunk, &got), None, "{}", g.adapter().key());
checked += chunk.len();
}
report(&format!("ops-gpu arith: key={} {} cases={checked} words={} equal", g.adapter().key(), g.adapter().describe(), checked * probe::OUT_WORDS));
}
}
#[test]
fn an_edited_product_is_caught_by_the_probe() {
let d = blocking::discover();
if d.found.is_empty() {
println!("ops-gpu arith mutant: 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 mut src = Sources::shipped();
let from = "let hi = p11 + (mid2 >> 16u) + ((c1 + c2) << 16u);";
assert!(src.probe.contains(from));
src.probe = src.probe.replace(from, "let hi = p11 + (mid2 >> 16u) + (c2 << 16u);");
let m = blocking::discover_with(&src, "mutant: a carry dropped");
assert!(m.found.is_empty(), "an adapter passed the probe with a carry dropped");
for (a, why) in &m.refused {
assert!(why.contains("probe"), "{}: refused for another reason: {why}", a.key());
println!("ops-gpu arith mutant: key={} refused: {why}", a.key());
}
assert_eq!(m.refused.len(), d.found.len() + d.refused.len());
}