vyre_primitives/lib.rs
1// Crate policy: unsafe is DENIED by default (was `forbid`), so the crate stays
2// unsafe-free everywhere except call sites that carry an explicit
3// `#[allow(unsafe_code)]` plus a `// SAFETY:` proof. The sole current exception
4// is `wire::fill_le_words_into`, where eliminating a redundant pre-copy
5// zero-fill on the GPU-readback decode hot path is worth a single, audited
6// uninitialized-write. `deny` (not `forbid`) is required so that one annotated
7// exception can compile; every other `unsafe` in the crate still hard-errors.
8#![deny(unsafe_code)]
9//! `vyre-primitives` - compositional primitives for vyre.
10//!
11//! Shape (mirrors Linux kernel `fs/` / `mm/` / `net/` - subsystem
12//! directories under one crate, feature-gated for consumers):
13//!
14//! ```text
15//! vyre-primitives/
16//! src/
17//! lib.rs # subsystem table (this file)
18//! markers.rs # unit-struct marker types, always on
19//! text/ # feature = "text"
20//! mod.rs
21//! char_class.rs
22//! utf8_validate.rs
23//! line_index.rs
24//! matching/ # feature = "matching"
25//! mod.rs
26//! bracket_match.rs
27//! bitset/ # feature = "bitset"
28//! fixpoint/ # feature = "fixpoint"
29//! graph/ # feature = "graph" (CSR + BFS + SCC + motif + toposort)
30//! hash/ # feature = "hash"
31//! label/ # feature = "label"
32//! math/ # feature = "math"
33//! nn/ # feature = "nn"
34//! parsing/ # feature = "parsing"
35//! predicate/ # feature = "predicate"
36//! reduce/ # feature = "reduce"
37//! ```
38//!
39//! Two kinds of primitive live here:
40//!
41//! 1. **Marker types** (`markers`, always on, zero deps) - unit
42//! structs the reference interpreter and backend emitters dispatch
43//! on.
44//!
45//! 2. **Tier 2.5 substrate** (per-domain feature flags) - shared
46//! `fn(...) -> Program` primitives reused by ≥ 2 Tier-3 dialects.
47//! Each domain is one folder + one feature flag. Tier 3 crates
48//! depend on `vyre-primitives` and enable only the domains they
49//! need.
50//!
51//! The path IS the interface. Subsystem `mod.rs` exposes sub-modules,
52//! not a flat namespace - callers write
53//! `vyre_primitives::text::char_class::char_class(...)` so the LEGO
54//! chain is visible at every call site.
55//!
56//! See `docs/lego-block-rule.md` and `docs/lego-block-rule.md` for
57//! the tier rule, admission criteria, and Gate 1 enforcement.
58
59#[cfg(feature = "vyre-foundation")]
60pub mod ir_safe;
61mod markers;
62pub mod wire;
63#[cfg(feature = "vyre-foundation")]
64use std::sync::Arc;
65
66pub use markers::{
67 ArithAdd, ArithMul, BitwiseAnd, BitwiseOr, BitwiseXor, Clz, CombineOp, CompareEq, CompareLt,
68 Gather, HashBlake3, HashFnv1a, PatternMatchDfa, PatternMatchLiteral, Popcount, Reduce,
69 RegionId, Scan, Scatter, ShiftLeft, ShiftRight, Shuffle,
70};
71#[cfg(feature = "vyre-foundation")]
72use vyre_foundation::ir::model::expr::Ident;
73#[cfg(feature = "vyre-foundation")]
74use vyre_foundation::ir::{BufferDecl, DataType, Expr, Node, Program};
75
76/// Build a scalar trap program for invalid primitive builder inputs.
77///
78/// Primitive constructors are intentionally infallible for composition with
79/// registry fixtures and generated dialect code. Invalid user-controlled
80/// shapes must therefore become explicit IR traps, not host panics.
81#[cfg(feature = "vyre-foundation")]
82pub(crate) fn invalid_output_program(
83 op_id: &'static str,
84 output: &str,
85 data_type: DataType,
86 message: String,
87) -> Program {
88 Program::wrapped(
89 vec![BufferDecl::output(output, 0, data_type).with_count(1)],
90 [1, 1, 1],
91 vec![Node::Region {
92 generator: Ident::from(op_id),
93 source_region: None,
94 body: Arc::new(vec![Node::trap(Expr::u32(0), message)]),
95 }],
96 )
97}
98
99#[cfg(feature = "vyre-foundation")]
100pub(crate) fn demote_intermediate_outputs(program: Program, final_output: &str) -> Program {
101 let buffers = program
102 .buffers()
103 .iter()
104 .map(|buffer| {
105 let mut buffer = buffer.clone();
106 if buffer.name() != final_output && buffer.is_output() {
107 buffer.is_output = false;
108 buffer.pipeline_live_out = true;
109 }
110 buffer
111 })
112 .collect();
113 program.with_rewritten_buffers(buffers)
114}
115
116/// Return `(left * right) >> 16` for unsigned 16.16 fixed-point lanes without
117/// losing the high half of the product to 32-bit overflow.
118#[cfg(any(feature = "graph", feature = "math", feature = "geom", feature = "opt"))]
119pub(crate) fn fixed_mul_16_16_expr(left: Expr, right: Expr) -> Expr {
120 // 16.16 fixed-point is a SIGNED number format: operands are two's-complement i32 in a u32, so a
121 // negative value is stored wrapped (`-v` → `2^32 - |v|·2^16`). Extracting the 16.16 product as
122 // `(low >> 16) | (high << 16)` requires the SIGNED 64-bit high word. `Expr::mulhi` is UNSIGNED, so
123 // reconstruct the signed high word with the standard correction:
124 // signed_high = unsigned_high − (left < 0 ? right : 0) − (right < 0 ? left : 0)
125 // (A wrong all-unsigned `mulhi` treats a negative operand as ~2^32 and produces a garbage giant
126 // product, the exact silent-corruption bug that made the fixed-point AMG V-cycle diverge from its
127 // f64 reference the moment a residual `b − A·x` went negative. See BACKLOG
128 // `LIMITATION-amg-fixed-path-unsigned-mul-negatives`.) For NON-NEGATIVE operands (|v| < 2^31, i.e.
129 // every legitimate 16.16 magnitude) both corrections are zero, so this is bit-identical to the old
130 // unsigned form (a strict correctness superset that leaves the non-negative kernels unchanged).
131 let low = Expr::mul(left.clone(), right.clone());
132 let unsigned_high = Expr::mulhi(left.clone(), right.clone());
133 // `0 - (x >> 31)` is an all-ones mask when `x`'s sign bit is set, else zero (logical u32 shift).
134 let left_sign_mask = Expr::sub(Expr::u32(0), Expr::shr(left.clone(), Expr::u32(31)));
135 let right_sign_mask = Expr::sub(Expr::u32(0), Expr::shr(right.clone(), Expr::u32(31)));
136 let correction_left = Expr::bitand(left_sign_mask, right);
137 let correction_right = Expr::bitand(right_sign_mask, left);
138 let signed_high = Expr::sub(Expr::sub(unsigned_high, correction_left), correction_right);
139 Expr::bitor(
140 Expr::shr(low, Expr::u32(16)),
141 Expr::shl(signed_high, Expr::u32(16)),
142 )
143}
144
145/// SIGNED integer division of a two's-complement `numerator` by a KNOWN-POSITIVE `denominator`
146/// (truncating toward zero), for use in fixed-point kernels whose numerator may be negative.
147///
148/// `Expr::div` is UNSIGNED, so dividing a wrapped-negative 16.16 numerator (e.g. a Jacobi residual
149/// `b − A·x` that went negative) by a small positive integer yields garbage, the second half of the
150/// silent-corruption bug behind `LIMITATION-amg-fixed-path-unsigned-mul-negatives` (the first half being
151/// [`fixed_mul_16_16_expr`]). This computes `sign·(|numerator| / denominator)` via the branchless
152/// mask-abs idiom: `mask = numerator >> 31` broadcast to all-ones on a negative value, `abs = (n ^ m) − m`,
153/// `q = abs / d` (now a genuine unsigned divide of a non-negative magnitude), then reapply the sign
154/// `(q ^ m) − m`. For a NON-NEGATIVE numerator `mask == 0`, so this reduces to plain `Expr::div`, a
155/// strict correctness superset that leaves non-negative kernels unchanged. The denominator MUST be
156/// positive (all callers pass `diag_units ≥ 1`); a negative denominator is not handled.
157#[cfg(any(feature = "graph", feature = "math", feature = "geom", feature = "opt"))]
158pub(crate) fn fixed_sdiv_by_positive_expr(numerator: Expr, denominator: Expr) -> Expr {
159 // `numerator >> 31` is 0 or 1 (logical u32 shift); `0 - that` broadcasts to the all-ones sign mask.
160 let sign_mask = Expr::sub(Expr::u32(0), Expr::shr(numerator.clone(), Expr::u32(31)));
161 // abs(numerator) = (numerator ^ sign_mask) - sign_mask (two's-complement branchless absolute value).
162 let magnitude = Expr::sub(
163 Expr::bitxor(numerator, sign_mask.clone()),
164 sign_mask.clone(),
165 );
166 let quotient = Expr::div(magnitude, denominator);
167 // Reapply the original sign: (quotient ^ sign_mask) - sign_mask.
168 Expr::sub(Expr::bitxor(quotient, sign_mask.clone()), sign_mask)
169}
170
171#[cfg(any(feature = "graph", feature = "math"))]
172pub(crate) mod fixed_u32_matmul;
173
174#[cfg(any(feature = "label", feature = "predicate"))]
175pub(crate) mod nodeset_filter;
176
177/// Derived view over canonical primitive operation registrations.
178#[cfg(feature = "inventory-registry")]
179pub mod operation_catalog;
180
181/// Text primitives.
182#[cfg(feature = "text")]
183pub mod text;
184
185/// Pattern-matching primitives.
186#[cfg(feature = "matching")]
187pub mod matching;
188
189/// Decode primitives.
190#[cfg(feature = "decode")]
191pub mod decode;
192
193/// NFA primitives - subgroup-cooperative simulator (G1 GPU perf).
194#[cfg(feature = "nfa")]
195pub mod nfa;
196
197/// Hash primitives (FNV-1a 32/64, CRC-32).
198#[cfg(feature = "hash")]
199pub mod hash;
200
201/// Math primitives (dot, scan, reduce, broadcast).
202#[cfg(feature = "math")]
203pub mod math;
204
205/// Parsing primitives (optimizer and AST scan kernels).
206#[cfg(feature = "parsing")]
207pub mod parsing;
208
209/// Neural-network primitives (attention and normalization sub-kernels).
210#[cfg(feature = "nn")]
211pub mod nn;
212
213/// Graph primitives (topological sort, reachability, CSR traversal,
214/// SCC decomposition, path reconstruction - the Tier 2.5 substrate
215/// that a external analyzer's stdlib rules compose against).
216#[cfg(feature = "graph")]
217pub mod graph;
218
219/// Geometric / Clifford-algebra primitives (#8). Multivector products
220/// for equivariant NNs, physics simulation, robotics, 3D vision.
221#[cfg(feature = "geom")]
222pub mod geom;
223
224/// Optimization primitives (#9, #14, #46). Homotopy continuation,
225/// SOS, matroid intersection. Self: vyre's megakernel scheduler.
226#[cfg(feature = "opt")]
227pub mod opt;
228
229/// Topological-data-analysis primitives (#15, #32). Vietoris-Rips
230/// filtration + simplicial complex operations. User: TDA, persistent
231/// landscape features, call-graph topological signatures.
232#[cfg(feature = "topology")]
233pub mod topology;
234
235/// Visual pixel-map primitives. Shared packed-RGBA invocation skeletons
236/// reused by higher-level image-processing compositions.
237#[cfg(feature = "visual")]
238pub mod visual;
239
240/// Effects-typed pipeline primitives (P-1.0-V1.x).
241/// Pure-data substrate: `EffectRow` bitmask, `Handler` over a row,
242/// `handler_apply` discharges effects, `handler_compose` builds a
243/// joint handler. Reference for the foundation effects-typed
244/// `lower` pipeline (V1.3).
245#[cfg(feature = "effects")]
246pub mod effects;
247
248/// Type-discipline primitives (P-PRIM-14, …). Substructural
249/// (linear/affine/relevant/unrestricted) checks the foundation
250/// validate pipeline consumes per buffer.
251#[cfg(feature = "types")]
252pub mod types;
253
254/// Categorical primitives (P-PRIM-16/17/18). Yoneda embedding,
255/// adjoint-pair detection, Kan extension over finite categories.
256/// Consumed by the optimizer's functorial_pass_composition substrate.
257#[cfg(feature = "cat")]
258pub mod cat;
259
260/// ZX-calculus rewrite primitives (P-PRIM-5). Spider fusion,
261/// identity removal, color change. Pure-CPU on a `Vec<ZxSpider>` +
262/// edge multiset; no FP, no IR-builder dep.
263#[cfg(feature = "zx")]
264pub mod zx;
265
266/// d-DNNF (decomposable / deterministic NNF) compiler primitive
267/// (P-PRIM-6). Host-side CNF → d-DNNF via Shannon decomposition,
268/// linear-time model counting on the result. Used by
269/// `knowledge_compile_pass_precondition` to turn pass-precondition
270/// formulae into linear-cost evaluators.
271#[cfg(feature = "dnnf")]
272pub mod dnnf;
273
274/// Bitset primitives - `and`/`or`/`not`/`xor`/`popcount`/`any`/
275/// `contains` over packed u32 bitsets. The NodeSet / ValueSet
276/// representation every graph primitive consumes.
277#[cfg(feature = "bitset")]
278pub mod bitset;
279
280/// Reduction primitives - `count`/`min`/`max`/`sum` over bitsets and
281/// fixed-width ValueSets. Backs source-query dialect aggregates.
282#[cfg(feature = "reduce")]
283pub mod reduce;
284
285/// Label → NodeSet resolver - turn a TagFamily bitmask into a
286/// NodeSet bitset. Implements the `@family` lookup that a external analyzer's
287/// label surface surfaces.
288#[cfg(feature = "label")]
289pub mod label;
290
291/// Frozen predicate primitives - the ~10 engine primitives (call_to,
292/// return_value_of, arg_of, size_argument_of, edge, in_function,
293/// in_file, in_package, literal_of, node_kind) that source-query dialect stdlib
294/// rules compose into every higher-level query.
295#[cfg(feature = "predicate")]
296pub mod predicate;
297
298/// Deterministic fixpoint primitive (ping-pong with convergence
299/// flag). Composes `csr_forward_traverse` + bitset OR into the
300/// transitive-closure driver every stdlib taint rule needs.
301#[cfg(feature = "fixpoint")]
302pub mod fixpoint;
303
304/// Virtual File System DMA primitives. Uses `vyre_foundation::ir`
305/// so it's gated behind the same set of features that pull
306/// vyre-foundation in as an optional dep. Any of the domain
307/// features enables vfs.
308#[cfg(any(
309 feature = "text",
310 feature = "matching",
311 feature = "decode",
312 feature = "math",
313 feature = "nn",
314 feature = "hash",
315 feature = "parsing",
316 feature = "graph",
317 feature = "bitset",
318 feature = "reduce",
319 feature = "label",
320 feature = "predicate",
321 feature = "fixpoint",
322))]
323pub mod vfs;
324
325/// Wire-format envelope re-exported from vyre-foundation.
326///
327/// Every primitive that ships its own `to_bytes` / `from_bytes` (today:
328/// `CompiledDfa`; future: serializable region tables, hash tables,
329/// parser plans) composes this envelope. Re-exporting at the
330/// vyre-primitives root keeps the import path uniform for consumers:
331/// `vyre_primitives::serial_data::WireWriter` regardless of whether
332/// the type lives at the primitive layer or higher up.
333///
334/// Available when any feature that pulls vyre-foundation is enabled
335/// (every primitive domain enables it).
336#[cfg(feature = "vyre-foundation")]
337pub mod serial_data {
338 pub use vyre_foundation::serial::envelope::{
339 test_helpers, EnvelopeError, WireReader, WireWriter,
340 };
341}
342
343/// Curated prelude - the byte-pack/decode primitives every consumer
344/// needs for GPU buffer construction and readback, plus the shared
345/// envelope types when vyre-foundation is in play.
346///
347/// `use vyre_primitives::prelude::*;` should be the only import a
348/// caller needs for the common pack/unpack surface. Adding new wire
349/// primitives must keep this list in sync.
350pub mod prelude {
351 pub use crate::wire::{
352 append_f32_slice_le_bytes, append_packed_byte_lane, append_u32_slice_le_bytes,
353 decode_f32_le_bytes_all, decode_i32_le_bytes_all, decode_u16_le_bytes_all,
354 decode_u32_le_bytes_all, decode_u64_le_bytes_all, pack_bytes_as_u32_slice,
355 pack_bytes_as_u32_slice_min_words, pack_f32_slice, pack_f32_slice_into,
356 pack_f32_slice_into_uninit, pack_i32_slice, pack_i32_slice_into, pack_u16_slice,
357 pack_u16_slice_into, pack_u32_slice, pack_u32_slice_into, pack_u32_slice_into_uninit,
358 pack_u32_slice_min_words_into, pack_u64_slice, pack_u64_slice_into, read_f32_le_word,
359 read_u32_le_word, unpack_f32_slice, unpack_f32_slice_into, unpack_u32_slice_into,
360 };
361}