use crate::ir::validate::{err, ValidationError};
pub const DEFAULT_MAX_CALL_DEPTH: usize = 32;
pub const DEFAULT_MAX_NESTING_DEPTH: usize = 64;
pub const DEFAULT_MAX_NODE_COUNT: usize = 100_000;
#[derive(Debug, Default)]
pub struct LimitState {
pub node_count: usize,
pub nesting_reported: bool,
pub node_count_reported: bool,
}
#[inline]
pub fn check_limits(limits: &mut LimitState, depth: usize, errors: &mut Vec<ValidationError>) {
limits.node_count = limits.node_count.saturating_add(1);
if limits.node_count > DEFAULT_MAX_NODE_COUNT && !limits.node_count_reported {
limits.node_count_reported = true;
errors.push(err(format!(
"V019: program has more than {DEFAULT_MAX_NODE_COUNT} statement nodes. Fix: split the program into smaller kernels or run an optimization pass before lowering."
)));
}
if depth > DEFAULT_MAX_NESTING_DEPTH && !limits.nesting_reported {
limits.nesting_reported = true;
errors.push(err(format!(
"V018: program nesting depth {depth} exceeds max {DEFAULT_MAX_NESTING_DEPTH}. Fix: flatten nested If/Loop/Block structures or split the program before lowering."
)));
}
}
#[inline]
pub fn max_call_depth(op_id: &str, depth: usize) -> Result<usize, usize> {
if depth > DEFAULT_MAX_CALL_DEPTH {
return Err(depth);
}
let Some(spec) = crate::ops::registry::lookup(op_id) else {
return Ok(depth);
};
let Some(prog) = spec.program() else {
return Ok(depth);
};
let mut max_depth = depth;
let call_ops = crate::ir::transform::visit::collect_call_op_ids(&prog);
for call_op in call_ops {
match max_call_depth(&call_op, depth + 1) {
Ok(d) => max_depth = max_depth.max(d),
Err(e) => return Err(e),
}
}
Ok(max_depth)
}