Skip to main content

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 {}