vyre_reference/lib.rs
1#![forbid(unsafe_code)]
2#![allow(
3 clippy::only_used_in_recursion,
4 clippy::comparison_chain,
5 clippy::ptr_arg
6)]
7//! Pure Rust reference interpreter for vyre IR programs.
8//!
9//! This module is the executable specification for IR semantics. It is
10//! intentionally slow and direct: every current IR expression and node variant
11//! has a named evaluator function.
12
13extern crate vyre_foundation as vyre;
14
15/// Dual-reference trait and registry types.
16pub mod dual;
17/// Canonical dual implementations and reference evaluators.
18pub mod dual_impls;
19/// Runtime value representation for interpreter inputs and outputs.
20pub mod value;
21
22/// Atomic operation reference implementations.
23pub mod atomics;
24/// CPU operation traits used by concrete reference implementations.
25pub mod cpu_op;
26/// Registry-driven dispatch entry point (B-B4).
27///
28/// Routes an op id through the global `DialectRegistry` and invokes
29/// the registered `cpu_ref` function. Complements the execution-tree
30/// evaluators by giving external dialect crates a zero-patch path to run on
31/// the reference interpreter.
32pub mod dialect_dispatch;
33/// Canonical reference execution tree.
34pub mod execution;
35/// Flat byte adapter used by [`crate::cpu_op::CpuOp`].
36pub mod flat_cpu;
37/// IEEE 754 strict floating-point utilities.
38pub mod ieee754;
39/// Subgroup simulator for lane-collective Cat-C ops.
40pub mod subgroup;
41/// Workgroup simulation: invocation IDs, shared memory.
42pub mod workgroup;
43
44mod oob;
45mod ops;
46
47/// Test-only entry point that runs the hashmap interpreter over a Program.
48#[cfg(test)]
49pub use execution::eval_hashmap_reference;
50/// Execute a vyre Program on the pure Rust reference interpreter.
51pub use execution::{reference_eval, run_arena_reference, run_storage_graph};
52
53/// Resolve an operation ID to its two independently-written references.
54///
55/// # Examples
56///
57/// ```
58/// use vyre_reference::{dual_impls, resolve_dual};
59///
60/// let (reference_a, reference_b) =
61/// resolve_dual(dual_impls::bitwise::xor::OP_ID).expect("Fix: xor dual refs must be registered; restore this invariant before continuing.");
62///
63/// let input = [0b1010_1010_u8, 0b0101_0101];
64/// assert_eq!(reference_a(&input), reference_b(&input));
65/// ```
66pub fn resolve_dual(op_id: &str) -> Option<(dual::ReferenceFn, dual::ReferenceFn)> {
67 match op_id {
68 dual_impls::arith::add::OP_ID => Some((
69 dual_impls::arith::add::reference_a::reference,
70 dual_impls::arith::add::reference_b::reference,
71 )),
72 dual_impls::arith::mul::OP_ID => Some((
73 dual_impls::arith::mul::reference_a::reference,
74 dual_impls::arith::mul::reference_b::reference,
75 )),
76 dual_impls::bitwise::xor::OP_ID => Some((
77 dual_impls::bitwise::xor::reference_a::reference,
78 dual_impls::bitwise::xor::reference_b::reference,
79 )),
80 dual_impls::bitwise::and::OP_ID => Some((
81 dual_impls::bitwise::and::reference_a::reference,
82 dual_impls::bitwise::and::reference_b::reference,
83 )),
84 dual_impls::bitwise::or::OP_ID => Some((
85 dual_impls::bitwise::or::reference_a::reference,
86 dual_impls::bitwise::or::reference_b::reference,
87 )),
88 dual_impls::bitwise::not::OP_ID => Some((
89 dual_impls::bitwise::not::reference_a::reference,
90 dual_impls::bitwise::not::reference_b::reference,
91 )),
92 dual_impls::bitwise::shift_left::OP_ID => Some((
93 dual_impls::bitwise::shift_left::reference_a::reference,
94 dual_impls::bitwise::shift_left::reference_b::reference,
95 )),
96 dual_impls::bitwise::shift_right::OP_ID => Some((
97 dual_impls::bitwise::shift_right::reference_a::reference,
98 dual_impls::bitwise::shift_right::reference_b::reference,
99 )),
100 dual_impls::bitwise::popcount::OP_ID => Some((
101 dual_impls::bitwise::popcount::reference_a::reference,
102 dual_impls::bitwise::popcount::reference_b::reference,
103 )),
104 dual_impls::bitwise::clz::OP_ID => Some((
105 dual_impls::bitwise::clz::reference_a::reference,
106 dual_impls::bitwise::clz::reference_b::reference,
107 )),
108 dual_impls::compare::eq::OP_ID => Some((
109 dual_impls::compare::eq::reference_a::reference,
110 dual_impls::compare::eq::reference_b::reference,
111 )),
112 dual_impls::compare::lt::OP_ID => Some((
113 dual_impls::compare::lt::reference_a::reference,
114 dual_impls::compare::lt::reference_b::reference,
115 )),
116 _ => None,
117 }
118}
119
120/// Return the complete list of operation IDs that have dual references registered.
121///
122/// This is the canonical enumeration used by the differential fuzzing gate.
123/// Every new dual-reference pair MUST add its OP_ID here.
124pub fn dual_op_ids() -> &'static [&'static str] {
125 &[
126 dual_impls::arith::add::OP_ID,
127 dual_impls::arith::mul::OP_ID,
128 dual_impls::bitwise::xor::OP_ID,
129 dual_impls::bitwise::and::OP_ID,
130 dual_impls::bitwise::or::OP_ID,
131 dual_impls::bitwise::not::OP_ID,
132 dual_impls::bitwise::shift_left::OP_ID,
133 dual_impls::bitwise::shift_right::OP_ID,
134 dual_impls::bitwise::popcount::OP_ID,
135 dual_impls::bitwise::clz::OP_ID,
136 dual_impls::compare::eq::OP_ID,
137 dual_impls::compare::lt::OP_ID,
138 ]
139}
140
141/// The architecture of the `OpEntry` registry.
142///
143/// We are forced to split the global primitive registries into three separate
144/// buckets (Unary, Binary, Variadic) instead of a single unified registry.
145///
146/// This split is required because of Rust's trait object lifetime limits.
147/// When storing function pointers that take references (e.g., `&'a Node<'a>`),
148/// higher-ranked trait bounds (HRTB, `for<'a>`) fail to unify on function
149/// pointers with heterogeneous arities. A single registry `fn(&[Node])` slice
150/// signature would force heap allocation for binary/unary nodes to fit the slice,
151/// destroying the zero-allocation invariant of the reference interpreter.
152///
153/// Thus, we split by arity to allow zero-cost static dispatch.
154pub mod registry_architecture {}