#![allow(clippy::unwrap_used)]
use dxil_reader::module::{Module, Op};
fn module(name: &str) -> Module {
let path = std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
.join("tests/fixtures")
.join(name);
let bytes = std::fs::read(path).unwrap();
let chunks = dxcontainer::chunks(&bytes).unwrap();
let dxil = chunks.iter().find(|c| &c.kind == b"DXIL").unwrap();
let part = dxil_reader::program(dxil.data).unwrap();
let blocks = dxil_reader::bitstream::read(part.bitcode, *b"BC\xc0\xde").unwrap();
Module::read(&blocks).unwrap()
}
fn count(m: &Module, pred: impl Fn(&Op, usize) -> bool) -> usize {
m.functions
.iter()
.flat_map(|f| f.instructions.iter())
.filter(|i| pred(&i.op, i.block))
.count()
}
#[test]
fn a_switch_is_a_switch_with_its_cases() {
for name in ["switch.dxil", "switch.od.dxil"] {
let m = module(name);
assert_eq!(count(&m, |op, _| matches!(op, Op::Switch { .. })), 2, "{name}");
let widest = m
.functions
.iter()
.flat_map(|f| &f.instructions)
.filter_map(|i| match &i.op {
Op::Switch { cases, .. } => Some(cases.len()),
_ => None,
})
.max();
assert_eq!(
widest,
Some(4),
"{name}: the first switch has the labels 0, 1, 2 and 5"
);
}
}
#[test]
fn a_local_array_is_memory() {
for name in ["arrays.dxil", "arrays.od.dxil"] {
let m = module(name);
assert_eq!(count(&m, |op, _| matches!(op, Op::Alloca { .. })), 1, "{name}");
assert!(count(&m, |op, _| matches!(op, Op::Store { .. })) >= 4, "{name}");
assert!(count(&m, |op, _| matches!(op, Op::Gep { .. })) >= 4, "{name}");
let tables = m
.values
.iter()
.filter(|v| {
matches!(
v,
dxil_reader::module::Value::Global {
init: Some(_),
constant: true,
..
}
)
})
.count();
assert!(tables >= 1, "{name}: {tables} constant tables");
}
}
#[test]
fn every_fixture_reads() {
let dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures");
let mut n = 0;
for e in std::fs::read_dir(dir).unwrap() {
let p = e.unwrap().path();
if p.extension().is_some_and(|x| x == "dxil") {
module(&p.file_name().unwrap().to_string_lossy());
n += 1;
}
}
assert!(n >= 17, "{n} fixtures");
}
#[test]
fn a_pointer_type_keeps_its_address_space() {
use dxil_reader::module::{Type, Value};
for name in ["compute_basic.dxil", "compute_basic.od.dxil"] {
let m = module(name);
let mut spaces = std::collections::BTreeSet::new();
for v in &m.values {
if let Value::Global {
ty,
value_ty,
address_space,
..
} = v
{
assert_eq!(m.types[*ty], Type::Pointer(*value_ty, *address_space), "{name}");
spaces.insert(*address_space);
}
}
assert!(
spaces.contains(&3),
"{name}: group shared globals are in space 3, found {spaces:?}"
);
let pointers: Vec<_> = m
.types
.iter()
.filter_map(|t| match t {
Type::Pointer(to, space) => Some((*to, *space)),
_ => None,
})
.collect();
let mut seen = std::collections::BTreeSet::new();
assert!(
pointers.iter().all(|p| seen.insert(*p)),
"{name}: a pointer type is in the table twice"
);
if name == "compute_basic.od.dxil" {
assert!(
pointers
.iter()
.any(|&(to, space)| space == 3 && pointers.contains(&(to, 0))),
"{name}: {pointers:?}"
);
}
}
}