use crate::ir::{BufferAccess, BufferDecl, Program};
pub(super) fn fallback_composition_key(prog: &Program) -> String {
let mut hasher = blake3::Hasher::new();
for buf in prog.buffers() {
hasher.update(buf.name().as_bytes());
hasher.update(&[0]);
}
for dim in prog.workgroup_size() {
hasher.update(&dim.to_le_bytes());
}
hasher.update(&(prog.entry().len() as u64).to_le_bytes());
format!("{}", hasher.finalize().to_hex())
}
pub(super) fn upgrade_buffer_access(buffer: &mut BufferDecl, other: &BufferAccess) {
let current = buffer.access();
buffer.access = match (¤t, &other) {
(BufferAccess::ReadWrite, _)
| (_, BufferAccess::ReadWrite)
| (BufferAccess::WriteOnly, BufferAccess::ReadOnly | BufferAccess::Uniform)
| (BufferAccess::ReadOnly | BufferAccess::Uniform, BufferAccess::WriteOnly) => {
BufferAccess::ReadWrite
}
(BufferAccess::WriteOnly, BufferAccess::WriteOnly) => BufferAccess::WriteOnly,
(BufferAccess::Uniform, _) | (_, BufferAccess::Uniform) => BufferAccess::Uniform,
(BufferAccess::Workgroup, _) | (_, BufferAccess::Workgroup) => BufferAccess::Workgroup,
_ => BufferAccess::ReadOnly,
};
buffer.kind = match buffer.access {
BufferAccess::ReadOnly => crate::ir::MemoryKind::Readonly,
BufferAccess::Uniform => crate::ir::MemoryKind::Uniform,
BufferAccess::Workgroup => crate::ir::MemoryKind::Shared,
_ => crate::ir::MemoryKind::Global,
};
}
#[cfg(test)]
mod tests {
use super::*;
use crate::execution_plan::fusion::collectors::collect_buffer_targets;
use crate::ir::{DataType, Expr, Ident, Node};
use rustc_hash::FxHashSet;
#[test]
fn upgrade_write_only_read_only_to_read_write() {
let mut buffer = BufferDecl::storage("tmp", 0, BufferAccess::WriteOnly, DataType::U32);
upgrade_buffer_access(&mut buffer, &BufferAccess::ReadOnly);
assert_eq!(buffer.access(), BufferAccess::ReadWrite);
assert_eq!(buffer.kind(), crate::ir::MemoryKind::Global);
}
fn collect_targets(node: &Node) -> (FxHashSet<Ident>, FxHashSet<Ident>, FxHashSet<Ident>) {
let mut loads = FxHashSet::default();
let mut stores = FxHashSet::default();
let mut atomics = FxHashSet::default();
collect_buffer_targets(node, &mut loads, &mut stores, &mut atomics);
(loads, stores, atomics)
}
#[test]
fn collect_buffer_targets_records_async_store_source_read_and_destination_write() {
let node = Node::AsyncStore {
source: Ident::from("staged"),
destination: Ident::from("out"),
offset: Box::new(Expr::u32(0)),
size: Box::new(Expr::u32(16)),
tag: Ident::from("t0"),
};
let (loads, stores, _atomics) = collect_targets(&node);
assert!(
loads.contains(&Ident::from("staged")),
"AsyncStore source must be recorded as a buffer read; got loads={loads:?}"
);
assert!(
stores.contains(&Ident::from("out")),
"AsyncStore destination must be recorded as a buffer write; got stores={stores:?}"
);
}
#[test]
fn collect_buffer_targets_records_async_load_source_read_and_destination_write() {
let node = Node::AsyncLoad {
source: Ident::from("global_in"),
destination: Ident::from("shared_tile"),
offset: Box::new(Expr::u32(0)),
size: Box::new(Expr::u32(16)),
tag: Ident::from("t1"),
};
let (loads, stores, _atomics) = collect_targets(&node);
assert!(
loads.contains(&Ident::from("global_in")),
"AsyncLoad source must be recorded as a buffer read; got loads={loads:?}"
);
assert!(
stores.contains(&Ident::from("shared_tile")),
"AsyncLoad destination must be recorded as a buffer write; got stores={stores:?}"
);
}
#[test]
fn collect_buffer_targets_records_indirect_dispatch_count_buffer_read() {
let node = Node::IndirectDispatch {
count_buffer: Ident::from("counts"),
count_offset: 0,
};
let (loads, _stores, _atomics) = collect_targets(&node);
assert!(
loads.contains(&Ident::from("counts")),
"IndirectDispatch count_buffer must be recorded as a buffer read; got loads={loads:?}"
);
}
}