Skip to main content

vyre_reference/
flat_cpu.rs

1//! Flat byte adapter that turns every CPU reference into a uniform byte-in,
2//! byte-out contract.
3//!
4//! The parity engine compares raw bytes, not structured values. This module
5//! exists so primitive ops can be tested with the same binary-diff harness
6//! regardless of their internal Value representation.
7
8use vyre::ir::{BufferAccess, DataType, Program};
9
10use crate::reference_eval;
11use crate::value::Value;
12/// Execute a program from a concatenated single-case byte payload.
13///
14/// Fixed-width input buffers consume exactly their declared static element
15/// count from `input` (`count == 0` is treated as one element for legacy
16/// runtime-sized flat cases). Read-write output buffers are initialized to the
17/// same declared fixed-width storage size and appended to `output` after
18/// interpretation. Extra or truncated input bytes are rejected so malformed
19/// conformance vectors cannot be hidden by padding or ignored suffixes.
20///
21/// # Errors
22///
23/// Returns [`vyre::error::Error`] if the program is invalid or execution fails.
24///
25/// # Examples
26///
27/// ```rust,ignore
28/// let mut out = Vec::new();
29/// vyre::reference::flat_cpu::run_flat(&program, &input_bytes, &mut out)?;
30/// ```
31pub fn run_flat(program: &Program, input: &[u8], output: &mut Vec<u8>) -> Result<(), vyre::Error> {
32    let mut offset = 0usize;
33    let mut values = Vec::new();
34    for buffer in program.buffers() {
35        match buffer.access() {
36            BufferAccess::ReadOnly | BufferAccess::Uniform => {
37                let width = buffer_flat_width(buffer.name(), buffer.element(), buffer.count())?;
38                let remaining = input.len().saturating_sub(offset);
39                if remaining < width {
40                    return Err(vyre::Error::interp(format!(
41                        "flat CPU input for buffer `{}` is truncated: expected {width} byte(s), got {remaining}. Fix: provide the declared fixed-width element count for every ReadOnly/Uniform buffer before invoking the reference backend.",
42                        buffer.name()
43                    )));
44                }
45                let mut bytes = vec![0; width];
46                bytes.copy_from_slice(&input[offset..offset + width]);
47                offset += width;
48                values.push(Value::from(bytes));
49            }
50            BufferAccess::ReadWrite => {
51                values.push(Value::from(vec![
52                    0;
53                    buffer_flat_width(
54                        buffer.name(),
55                        buffer.element(),
56                        buffer.count()
57                    )?
58                ]));
59            }
60            BufferAccess::Workgroup => {}
61            _ => {}
62        }
63    }
64    if offset != input.len() {
65        let trailing = input.len() - offset;
66        return Err(vyre::Error::interp(format!(
67            "flat CPU input has {trailing} trailing byte(s) after consuming declared ReadOnly/Uniform buffers. Fix: provide exactly one fixed-width element per flat input buffer or split multi-case payloads before invoking the reference backend."
68        )));
69    }
70    let values = reference_eval(program, &values)?;
71    output.clear();
72    for value in values {
73        value.extend_bytes_width(0, output)?;
74    }
75    Ok(())
76}
77
78fn output_width(buffer_name: &str, data_type: DataType) -> Result<usize, vyre::Error> {
79    let min_bytes = data_type.min_bytes();
80    if min_bytes == 0 {
81        return Err(vyre::Error::interp(format!(
82            "flat CPU buffer `{buffer_name}` uses variable-width element type {data_type:?}. Fix: use a fixed-width element type or route dynamic buffers through the structured reference evaluator."
83        )));
84    }
85    Ok(min_bytes.max(4))
86}
87
88fn buffer_flat_width(
89    buffer_name: &str,
90    data_type: DataType,
91    count: u32,
92) -> Result<usize, vyre::Error> {
93    output_width(buffer_name, data_type)?
94        .checked_mul(count.max(1) as usize)
95        .ok_or_else(|| {
96            vyre::Error::interp(
97                "flat CPU buffer byte width overflows usize. Fix: split the flat conformance case or reduce the declared buffer count.",
98            )
99        })
100}