vyre_reference/
dialect_dispatch.rs1use crate::execution::call::invoke_cpu_ref;
21use vyre::{cpu_op::is_fallback_cpu_ref, Error, OpDef};
22
23pub fn dispatch_op(op_id: &str, input: &[u8], output: &mut Vec<u8>) -> Result<(), Error> {
37 let lookup = vyre::dialect_lookup().ok_or_else(|| {
38 Error::interp(format!(
39 "reference interpreter: no DialectLookup is installed. Fix: initialize vyre-driver before dispatching `{op_id}`."
40 ))
41 })?;
42 let interned = lookup.intern_op(op_id);
43 let op_def = lookup.lookup(interned).ok_or_else(|| {
44 Error::interp(format!(
45 "reference interpreter: op `{op_id}` is not registered. Fix: link the dialect crate that provides `{op_id}`."
46 ))
47 })?;
48
49 reject_unsupported_cpu_dispatch(op_id, op_def)?;
50
51 invoke_cpu_ref(op_id, op_def.lowerings.cpu_ref, input, output)
52}
53
54fn reject_unsupported_cpu_dispatch(op_id: &str, op_def: &OpDef) -> Result<(), Error> {
55 if op_def.dialect == "io" {
56 return Err(Error::interp(format!(
57 "unsupported capability for `{op_id}` on reference/CPU backend: Category C IO ops are registered for composition but require a backend lowering for zero-copy NVMe/GDS execution. Fix: select or register a backend that advertises the `io` dialect capability, or reject the program during capability negotiation before reference dispatch."
58 )));
59 }
60
61 if is_fallback_cpu_ref(op_def.lowerings.cpu_ref) {
62 return Err(Error::interp(format!(
63 "unsupported CPU reference dispatch for `{op_id}`: the op is registered with foundation's structured intrinsic fallback, not an executable flat-ABI CPU implementation. Fix: implement a typed reference adapter for `{op_id}` or route the program to a backend that declares a native lowering for this capability."
64 )));
65 }
66
67 Ok(())
68}
69
70pub const REFERENCE_BACKEND_NAME: &str = "reference";
78
79#[cfg(test)]
80mod tests {
81 use super::*;
82 use std::sync::Arc;
83 use vyre::{
84 install_dialect_lookup, intern_string, DialectLookup, InternedOpId, LoweringTable, OpDef,
85 };
86
87 struct TestLookup;
88
89 fn echo_ref(input: &[u8], output: &mut Vec<u8>) {
90 output.extend_from_slice(input);
91 }
92
93 fn panic_ref(_: &[u8], output: &mut Vec<u8>) {
94 output.extend_from_slice(&[0xDE, 0xAD]);
95 panic!("malformed primitive input");
96 }
97
98 static IO_DEF: std::sync::OnceLock<OpDef> = std::sync::OnceLock::new();
99 static ECHO_DEF: std::sync::OnceLock<OpDef> = std::sync::OnceLock::new();
100 static FALLBACK_DEF: std::sync::OnceLock<OpDef> = std::sync::OnceLock::new();
101 static PANIC_DEF: std::sync::OnceLock<OpDef> = std::sync::OnceLock::new();
102
103 impl vyre_foundation::dialect_lookup::private::Sealed for TestLookup {}
104
105 impl DialectLookup for TestLookup {
106 fn provider_id(&self) -> &'static str {
107 "vyre-reference::dialect_dispatch::TestLookup"
108 }
109
110 fn intern_op(&self, name: &str) -> InternedOpId {
111 intern_string(name)
112 }
113
114 fn lookup(&self, id: InternedOpId) -> Option<&'static OpDef> {
115 if id == intern_string("io.dma_from_nvme") {
116 return Some(IO_DEF.get_or_init(|| OpDef {
117 id: "io.dma_from_nvme",
118 dialect: "io",
119 lowerings: LoweringTable::new(echo_ref),
120 ..OpDef::default()
121 }));
122 }
123 if id == intern_string("test.echo") {
124 return Some(ECHO_DEF.get_or_init(|| OpDef {
125 id: "test.echo",
126 dialect: "test",
127 lowerings: LoweringTable::new(echo_ref),
128 ..OpDef::default()
129 }));
130 }
131 if id == intern_string("test.structured_fallback") {
132 return Some(FALLBACK_DEF.get_or_init(|| OpDef {
133 id: "test.structured_fallback",
134 dialect: "test",
135 lowerings: LoweringTable::empty(),
136 ..OpDef::default()
137 }));
138 }
139 if id == intern_string("test.panics") {
140 return Some(PANIC_DEF.get_or_init(|| OpDef {
141 id: "test.panics",
142 dialect: "test",
143 lowerings: LoweringTable::new(panic_ref),
144 ..OpDef::default()
145 }));
146 }
147 None
148 }
149 }
150
151 fn install_test_lookup() {
152 install_dialect_lookup(Arc::new(TestLookup))
153 .expect("Fix: test dialect lookup install should succeed or be idempotent");
154 }
155
156 #[test]
157 fn unknown_op_surfaces_clean_error() {
158 install_test_lookup();
159 let mut out = Vec::new();
160 let err = dispatch_op("nonexistent.op", &[], &mut out).expect_err("must fail");
161 let msg = format!("{err}");
162 assert!(
163 msg.contains("nonexistent.op"),
164 "error message must name the op: {msg}"
165 );
166 assert!(
167 msg.contains("Fix:"),
168 "error must carry actionable Fix: hint"
169 );
170 }
171
172 #[test]
173 fn registered_executable_cpu_ref_dispatches() {
174 install_test_lookup();
175 let mut out = Vec::new();
176 dispatch_op("test.echo", &[9, 8, 7], &mut out)
177 .expect("Fix: echo has executable cpu_ref; restore this invariant before continuing.");
178 assert_eq!(out, [9, 8, 7]);
179 }
180
181 #[test]
182 fn panicking_cpu_ref_returns_structured_error_without_output_drift() {
183 install_test_lookup();
184 let mut out = vec![0xAA];
185 let err = dispatch_op("test.panics", &[1, 2, 3], &mut out)
186 .expect_err("panicking cpu_ref must become a structured interpreter error");
187 let msg = format!("{err}");
188 assert!(
189 msg.contains("test.panics") && msg.contains("panicked") && msg.contains("Fix:"),
190 "panic error must name the op and stay actionable: {msg}"
191 );
192 assert_eq!(
193 out,
194 vec![0xAA],
195 "failed CPU refs must not leak partial output bytes"
196 );
197 }
198
199 #[test]
200 fn io_cat_c_op_refuses_reference_cpu_dispatch() {
201 install_test_lookup();
202 let mut out = vec![0xAA];
203 let err = dispatch_op("io.dma_from_nvme", &[], &mut out)
204 .expect_err("Category C io must not execute on reference/CPU");
205 let msg = format!("{err}");
206 assert!(
207 msg.contains("io.dma_from_nvme"),
208 "error message must name the op: {msg}"
209 );
210 assert!(
211 msg.contains("unsupported capability"),
212 "error must classify this as unsupported capability: {msg}"
213 );
214 assert!(
215 msg.contains("Category C IO"),
216 "error must identify Category C IO: {msg}"
217 );
218 assert!(
219 msg.contains("Fix:"),
220 "error must carry actionable Fix: hint: {msg}"
221 );
222 assert_eq!(
223 out,
224 vec![0xAA],
225 "dispatcher must reject before invoking the CPU sentinel"
226 );
227 }
228
229 #[test]
230 fn structured_cpu_fallback_refuses_reference_dispatch() {
231 install_test_lookup();
232 let mut out = vec![0xAA];
233 let err = dispatch_op("test.structured_fallback", &[1, 2], &mut out)
234 .expect_err("structured fallback must not look executable");
235 let msg = format!("{err}");
236 assert!(
237 msg.contains("test.structured_fallback"),
238 "error message must name the op: {msg}"
239 );
240 assert!(
241 msg.contains("structured intrinsic fallback"),
242 "error must name foundation's fallback path: {msg}"
243 );
244 assert!(
245 msg.contains("Fix:"),
246 "error must carry actionable Fix: hint: {msg}"
247 );
248 assert_eq!(
249 out,
250 vec![0xAA],
251 "dispatcher must reject before invoking structured_intrinsic_cpu"
252 );
253 }
254
255 #[test]
256 fn reference_backend_name_is_stable() {
257 install_test_lookup();
258 assert_eq!(REFERENCE_BACKEND_NAME, "reference");
259 }
260}