use crate::ir_inner::model::program::{BufferDecl, LinearType, Program};
use crate::validate::{err, ValidationError};
#[must_use]
pub fn check_linear_types(program: &Program) -> Vec<ValidationError> {
if program
.buffers()
.iter()
.all(|b| b.linear_type() == LinearType::Unrestricted)
{
return Vec::new();
}
let facts = crate::optimizer::program_soa::ProgramFacts::build_cached(program);
let mut errors = Vec::new();
for buffer in program.buffers() {
let lt = buffer.linear_type();
if lt == LinearType::Unrestricted {
continue;
}
let uses = u32::try_from(facts.buffer_refs_of(buffer.name()).len()).unwrap_or(u32::MAX);
if let Some(message) = violation_message(buffer, lt, uses) {
errors.push(err(message));
}
}
errors
}
fn violation_message(buffer: &BufferDecl, lt: LinearType, uses: u32) -> Option<String> {
match lt {
LinearType::Linear => {
if uses == 1 {
None
} else {
Some(format!(
"buffer `{}` declared `LinearType::Linear` must be used exactly once but was used {uses} time(s). Fix: ensure the program reads or writes this buffer exactly once on every path, or change the discipline to Affine / Relevant / Unrestricted.",
buffer.name()
))
}
}
LinearType::Affine => {
if uses > 1 {
Some(format!(
"buffer `{}` declared `LinearType::Affine` must be used at most once but was used {uses} time(s). Fix: drop the redundant references, or change the discipline to Relevant / Unrestricted to allow re-use.",
buffer.name()
))
} else {
None
}
}
LinearType::Relevant => {
if uses == 0 {
Some(format!(
"buffer `{}` declared `LinearType::Relevant` must be used at least once but was unused. Fix: add a read or write of this buffer, or change the discipline to Affine / Unrestricted.",
buffer.name()
))
} else {
None
}
}
LinearType::Unrestricted => None,
}
}