//! WGSL's integer semantics on every adapter (`src/semantics.rs`): the committed vectors //! of `wai/wgsl-conformance/` are the WGSL text's results, and every adapter found gives //! them, with SplitMix64 cases of every operation besides. A case the translators do not //! guard (`semantics::UNGUARDED`) is run and reported, and does not fail the adapter: no //! kernel depends on one (`tests/wgsl_rules.rs`). Then an edited kernel is shown to be //! caught: discovery refuses every adapter. //! //! Each adapter's result is one line, `ops-gpu semantics: key=…`, appended to //! `WAI_OPS_GPU_REPORT` when it is set. No adapter is not a pass: the test prints //! SKIPPED, and fails under `WAI_OPS_GPU_REQUIRE`. use std::io::Write; use std::path::PathBuf; use wai_ops_gpu::semantics::{self, Case}; use wai_ops_gpu::{blocking, kernels::Sources}; 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_or_else(|e| panic!("{p}: {e}")); writeln!(f, "{line}").unwrap(); } } fn committed() -> (PathBuf, String) { let p = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../wgsl-conformance/integer.jsonl"); let text = std::fs::read_to_string(&p).unwrap_or_else(|e| panic!("{}: {e}", p.display())); (p, text) } /// The committed file is what `wai-wgsl-vectors` writes, byte for byte, and each of its /// results is the reference's. #[test] fn the_committed_vectors_are_the_wgsl_texts_results() { let (p, text) = committed(); let want = semantics::vector_file(&semantics::edge_cases()); assert!(text == want, "{} is not what `cargo run --bin wai-wgsl-vectors -- {}` writes", p.display(), p.display()); let cases = semantics::parse_vector_file(&text).unwrap(); assert_eq!(cases.len(), semantics::edge_cases().len()); for (c, r) in &cases { assert_eq!(*r, semantics::reference(c), "{c:?}"); } let unguarded = cases.iter().filter(|(c, _)| !c.guarded()).count(); assert!(unguarded > 0 && unguarded < cases.len() / 4, "{unguarded} unguarded cases"); } #[test] fn every_adapter_gives_wgsls_integer_semantics() { let d = blocking::discover(); for (a, why) in &d.refused { report(&format!("ops-gpu semantics adapter refused: key={} {} reason={why:?}", a.key(), a.describe())); } if d.found.is_empty() { report("ops-gpu semantics: 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 (_, text) = committed(); let mut cases: Vec = semantics::parse_vector_file(&text).unwrap().into_iter().map(|(c, _)| c).collect(); let n_committed = cases.len(); cases.extend(semantics::random_cases(4096, 0xA417_5E00)); let expect: Vec = cases.iter().map(semantics::reference).collect(); for g in &d.found { let got = blocking::semantics(g, &cases).unwrap_or_else(|e| panic!("{}: {e}", g.adapter().key())); let o = semantics::compare(&cases, &expect, &got); for d in o.guarded_differ.iter().take(20) { println!("MISMATCH {}: {d}", g.adapter().key()); } let mut shown = std::collections::BTreeSet::new(); for d in &o.unguarded_differ { if shown.insert(d.case.op) { println!("ops-gpu semantics unguarded: key={} {d}", g.adapter().key()); } } report(&format!( "ops-gpu semantics: key={} {} committed={n_committed} cases={} guarded={} differ={} unguarded={} unguarded_differ={} unguarded_ops={}", g.adapter().key(), g.adapter().describe(), cases.len(), o.guarded, o.guarded_differ.len(), o.unguarded, o.unguarded_differ.len(), if o.unguarded_ops().is_empty() { "none".to_string() } else { o.unguarded_ops().join(",") }, )); assert!(o.guarded_differ.is_empty(), "{}: {} guarded cases differ from WGSL, the first: {}", g.adapter().key(), o.guarded_differ.len(), o.guarded_differ[0]); } } /// A remainder written out by hand, `a - (a / b) * b`, gives `a` where WGSL gives 0 (a /// zero divisor): an adapter running that kernel must be refused at discovery, for the /// semantics suite, on every adapter. #[test] fn an_edited_semantics_kernel_is_caught() { let d = blocking::discover(); if d.found.is_empty() { println!("ops-gpu semantics 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 = "case 10u: { r = bitcast(ia % ib); }"; assert!(src.semantics.contains(from)); src.semantics = src.semantics.replace(from, "case 10u: { r = bitcast(ia - (ia / ib) * ib); }"); let m = blocking::discover_with(&src, "mutant: a remainder by hand"); assert!(m.found.is_empty(), "an adapter passed the semantics suite with a remainder written by hand"); for (a, why) in &m.refused { assert!(why.contains("WGSL semantics suite") && why.contains("rem_i32"), "{}: refused for another reason: {why}", a.key()); report(&format!("ops-gpu semantics mutant: key={} refused: {why}", a.key())); } assert_eq!(m.refused.len(), d.found.len() + d.refused.len()); }