vyre_driver_reference/
lib.rs1#![forbid(unsafe_code)]
2
3use std::sync::Arc;
6
7use vyre_driver::backend::private;
8use vyre_driver::backend::{
9 core_supported_ops, BackendCapability, BackendError, BackendPrecedence, BackendRegistration,
10};
11use vyre_driver::{DispatchConfig, VyreBackend};
12use vyre_foundation::ir::{BufferAccess, BufferDecl, Program};
13use vyre_reference::value::Value;
14
15pub const CPU_REF_BACKEND_ID: &str = "cpu-ref";
17
18#[derive(Debug, Default, Clone, Copy)]
20pub struct CpuRefBackend;
21
22impl private::Sealed for CpuRefBackend {}
23
24impl VyreBackend for CpuRefBackend {
25 fn id(&self) -> &'static str {
26 CPU_REF_BACKEND_ID
27 }
28
29 fn version(&self) -> &'static str {
30 env!("CARGO_PKG_VERSION")
31 }
32
33 fn dispatch(
34 &self,
35 program: &Program,
36 inputs: &[Vec<u8>],
37 config: &DispatchConfig,
38 ) -> Result<Vec<Vec<u8>>, BackendError> {
39 let values = reference_values(program, inputs)?;
40 let result = match config.dispatch_elements {
50 Some(elements) => {
51 vyre_reference::reference_eval_with_dispatch(program, &values, elements)
52 }
53 None => vyre_reference::reference_eval(program, &values),
54 };
55 result
56 .map(|outputs| outputs.iter().map(Value::to_bytes).collect())
57 .map_err(|error| {
58 BackendError::new(format!(
59 "cpu-ref reference dispatch failed: {error}. Fix: validate the Program and input buffer ABI before dispatch."
60 ))
61 })
62 }
63
64 fn supported_ops(&self) -> &std::collections::HashSet<vyre_foundation::ir::OpId> {
65 core_supported_ops()
66 }
67
68 fn max_workgroup_size(&self) -> [u32; 3] {
69 [1024, 1, 1]
70 }
71
72 fn max_compute_workgroups_per_dimension(&self) -> u32 {
73 u32::MAX
74 }
75}
76
77fn reference_values(program: &Program, inputs: &[Vec<u8>]) -> Result<Vec<Value>, BackendError> {
78 let logical_input_count = program
82 .buffers()
83 .iter()
84 .filter(|buffer| {
85 buffer.access() != BufferAccess::Workgroup && !buffer.is_backend_allocated_output()
86 })
87 .count();
88 let legacy_input_count = program
89 .buffers()
90 .iter()
91 .filter(|buffer| buffer.access() != BufferAccess::Workgroup)
92 .count();
93 let legacy_input_mode =
94 inputs.len() == legacy_input_count && inputs.len() != logical_input_count;
95 let mut next_input = 0usize;
96 let mut values = Vec::new();
97 for buffer in program.buffers() {
98 if buffer.access() == BufferAccess::Workgroup {
99 continue;
100 }
101 let bytes = if buffer.is_backend_allocated_output() {
102 if legacy_input_mode {
103 let _legacy_initializer = inputs.get(next_input).ok_or_else(|| {
104 BackendError::new(format!(
105 "cpu-ref missing legacy output initializer for buffer `{}`. Fix: pass one buffer for every non-workgroup declaration or migrate to logical backend inputs.",
106 buffer.name()
107 ))
108 })?;
109 next_input += 1;
110 }
111 synthesized_zero_buffer(buffer, "backend-allocated output")?
112 } else if let Some(input) = inputs.get(next_input) {
113 next_input += 1;
114 input.clone()
115 } else {
116 synthesized_zero_buffer(buffer, "missing input")?
117 };
118 values.push(Value::Bytes(Arc::from(bytes)));
119 }
120 if next_input != inputs.len() {
121 return Err(BackendError::new(format!(
122 "cpu-ref received {} extra input buffer(s). Fix: pass inputs in Program::buffers order without trailing buffers.",
123 inputs.len() - next_input
124 )));
125 }
126 Ok(values)
127}
128
129fn synthesized_zero_buffer(
130 buffer: &BufferDecl,
131 role: &'static str,
132) -> Result<Vec<u8>, BackendError> {
133 let element_size = buffer.element().size_bytes().ok_or_else(|| {
134 BackendError::new(format!(
135 "cpu-ref cannot synthesize {role} buffer `{}` because its element type is unsized. Fix: declare fixed-width buffers or pass an explicit input buffer.",
136 buffer.name()
137 ))
138 })?;
139 let byte_len = usize::try_from(buffer.count())
140 .ok()
141 .and_then(|count| count.checked_mul(element_size))
142 .ok_or_else(|| {
143 BackendError::new(format!(
144 "cpu-ref {role} buffer `{}` size overflows usize. Fix: use a representable buffer size.",
145 buffer.name()
146 ))
147 })?;
148 Ok(vec![0u8; byte_len])
149}
150
151fn acquire_cpu_ref() -> Result<Box<dyn VyreBackend>, BackendError> {
152 Ok(Box::new(CpuRefBackend))
153}
154
155inventory::submit! {
156 BackendRegistration {
157 id: CPU_REF_BACKEND_ID,
158 factory: acquire_cpu_ref,
159 supported_ops: core_supported_ops,
160 }
161}
162
163inventory::submit! {
164 BackendCapability {
165 id: CPU_REF_BACKEND_ID,
166 dispatches: true,
167 }
168}
169
170inventory::submit! {
171 BackendPrecedence {
172 id: CPU_REF_BACKEND_ID,
173 rank: 900,
174 }
175}