Skip to main content

weir/
lib.rs

1//! Weir dataflow analysis crate.
2//!
3//! This crate owns dataflow analyses and reusable fixed-point execution
4//! scaffolding. CPU functions in module internals are parity oracles only; the
5//! production surface builds Vyre programs for GPU dispatch.
6//!
7//! # Example: dominators on a diamond CFG
8//!
9//! ```
10//! use std::collections::{HashMap, HashSet};
11//! use weir::ssa::{try_compute_dominators, Block, Cfg};
12//!
13//! // CFG:  0 → {1, 2} → 3
14//! let mut blocks = HashMap::new();
15//! for (id, preds, succs) in [
16//!     (0u32, vec![],     vec![1, 2]),
17//!     (1,    vec![0],    vec![3]),
18//!     (2,    vec![0],    vec![3]),
19//!     (3,    vec![1, 2], vec![]),
20//! ] {
21//!     blocks.insert(id, Block { id, preds, succs, defs: HashSet::new(), uses: HashSet::new() });
22//! }
23//!
24//! let idoms = try_compute_dominators(&Cfg { entry: 0, blocks }).unwrap();
25//! assert_eq!(idoms[&1], 0);
26//! assert_eq!(idoms[&2], 0);
27//! assert_eq!(idoms[&3], 0); // merge point is dominated by the diamond head
28//! ```
29
30/// Unifying trait and generic drivers for Weir's bitset fixed-point families.
31///
32/// This module is primarily used via the [`define_analysis_family!`] macro.
33/// See [`reaching`](crate::reaching), [`live`](crate::live),
34/// [`points_to`](crate::points_to), and [`slice`](crate::slice) for
35/// concrete families.
36pub mod analysis_family;
37
38/// Call-graph construction with indirect resolution (DF-5).
39///
40/// Build a callgraph propagation Program:
41///
42/// ```no_run
43/// use weir::callgraph::callgraph_build;
44///
45/// let program = callgraph_build("direct", "indirect", "pts", "cg_out");
46/// ```
47pub mod callgraph;
48
49#[cfg(any(test, feature = "cpu-parity"))]
50/// Bitset closure oracle for CPU parity verification.
51///
52/// ```ignore
53/// use weir::bitset_closure_oracle;
54/// ```
55pub mod bitset_closure_oracle;
56
57/// Control-dependence query (does `b` execute on every path through `a`?).
58///
59/// Build a control-dependence Program:
60///
61/// ```no_run
62/// use weir::control_dependence::control_dependence;
63///
64/// let program = control_dependence(4, "successor_pdom", "pdom_n", "out");
65/// ```
66pub mod control_dependence;
67
68/// Cross-language FFI reachability analysis.
69///
70/// Build a cross-language reach Program:
71///
72/// ```no_run
73/// use weir::cross_language::cross_language;
74///
75/// let program = cross_language(
76///     4, "source", "sink", "post_cross",
77///     "current", "next", "changed", "seed", "out",
78/// ).unwrap();
79/// ```
80pub mod cross_language;
81
82/// Def-use chain queries over SSA form (DF-11).
83///
84/// Look up uses of an SSA definition:
85///
86/// ```
87/// use std::collections::HashMap;
88/// use weir::def_use::try_def_use_chain;
89/// use weir::ssa::SsaForm;
90///
91/// let form = SsaForm {
92///     phi_nodes: HashMap::new(),
93///     renamed_usages: HashMap::new(),
94///     def_use_chains: {
95///         let mut m = HashMap::new();
96///         m.insert(7, vec![3, 5, 9]);
97///         m
98///     },
99/// };
100///
101/// assert_eq!(try_def_use_chain(&form, 7).unwrap(), vec![3, 5, 9]);
102/// ```
103pub mod def_use;
104
105mod dense_domain;
106pub mod dispatch_decode;
107mod dispatch_input_cache;
108pub mod resident_cache_identity;
109
110/// Dominator-tree construction and query (DF-1).
111///
112/// Build a dominator predicate Program:
113///
114/// ```no_run
115/// use weir::dominators::dominates;
116///
117/// let program = dominates(4, "dom_set", "target", "out");
118/// ```
119pub mod dominators;
120
121mod error_format;
122
123/// Escape analysis: does a pointer leave its defining frame? (DF-8).
124///
125/// Build an escape-propagation Program:
126///
127/// ```no_run
128/// use weir::escape::escape_analyze_with_count;
129///
130/// let program = escape_analyze_with_count("pts", "cg", "out", 64);
131/// ```
132pub mod escape;
133
134/// Escape-set query over points-to sets.
135///
136/// Build an escape-query Program:
137///
138/// ```no_run
139/// use weir::escapes::escapes;
140///
141/// let program = escapes(4, "pts", "escape_set", "out");
142/// ```
143pub mod escapes;
144
145/// Batch fixed-point execution over multiple seed sets.
146///
147/// ```ignore
148/// use weir::fixed_point_batch::FixedPointBatch;
149/// ```
150pub mod fixed_point_batch;
151
152mod fixed_point_closure;
153
154/// Scale-aware execution planning for fixed-point analyses.
155///
156/// Build an execution plan from graph shape:
157///
158/// ```
159/// use weir::fixed_point_execution_plan::{plan_from_parts, FixedPointExecutionPlan};
160/// use weir::fixed_point_scratch::FrontierDensityTelemetry;
161///
162/// let telemetry = FrontierDensityTelemetry::default();
163/// let plan = plan_from_parts(128, 16, 0, 2048, telemetry).unwrap();
164/// assert!(!plan.should_use_resident_graph());
165/// ```
166pub mod fixed_point_execution_plan;
167
168/// LRU cache for reusable fixed-point execution plans.
169///
170/// ```ignore
171/// use weir::fixed_point_execution_plan_cache::FixedPointExecutionPlanCache;
172/// ```
173pub mod fixed_point_execution_plan_cache;
174
175/// Packed forward CSR graph buffers for fixed-point GPU wrappers.
176///
177/// Build a prepared graph for generic forward analysis:
178///
179/// ```
180/// use weir::fixed_point_graph::FixedPointForwardGraph;
181///
182/// let graph = FixedPointForwardGraph::new(
183///     "example", 2, &[0, 1, 1], &[1], &[0xFFFF_FFFF],
184/// ).unwrap();
185/// assert_eq!(graph.node_count(), 2);
186/// assert_eq!(graph.edge_count(), 1);
187/// ```
188pub mod fixed_point_graph;
189
190/// Resident-GPU fixed-point closure drivers.
191///
192/// ```ignore
193/// use weir::fixed_point_resident::FixedPointResidentGraph;
194/// ```
195pub mod fixed_point_resident;
196
197/// Batch resident fixed-point execution.
198///
199/// ```ignore
200/// use weir::fixed_point_resident_batch::FixedPointResidentBatch;
201/// ```
202pub mod fixed_point_resident_batch;
203
204/// Cache for resident fixed-point graph and plan reuse.
205///
206/// ```ignore
207/// use weir::fixed_point_resident_cache::FixedPointResidentGraphCache;
208/// ```
209pub mod fixed_point_resident_cache;
210
211/// Reusable resident frontier scratch buffers.
212///
213/// ```ignore
214/// use weir::fixed_point_resident_frontier::FixedPointResidentFrontierScratch;
215/// ```
216pub mod fixed_point_resident_frontier;
217
218/// Resident execution plan construction.
219///
220/// ```ignore
221/// use weir::fixed_point_resident_plan::FixedPointResidentPlan;
222/// ```
223pub mod fixed_point_resident_plan;
224
225mod fixed_point_resident_sequence;
226
227/// Reusable host-side scratch for fixed-point graph preparation.
228///
229/// ```
230/// use weir::fixed_point_scratch::FixedPointScratch;
231///
232/// let scratch = FixedPointScratch::default();
233/// ```
234pub mod fixed_point_scratch;
235
236/// Shared graph-layout contracts for CSR normalization.
237///
238/// Normalize a raw CSR into a canonical layout:
239///
240/// ```
241/// use weir::graph_layout::{CsrGraph, NormalizedCsrGraph, CsrGraphNormalizationScratch};
242///
243/// let graph = CsrGraph::new(2, &[0, 1, 1], &[1], &[0xFFFF_FFFF]);
244/// let normalized = graph.normalize("example").unwrap();
245/// assert_eq!(normalized.node_count(), 2);
246/// assert_eq!(normalized.edge_count(), 1);
247/// ```
248pub mod graph_layout;
249
250/// IFDS/IDE interprocedural dataflow framework (DF-4).
251///
252/// Build one reachability step over the exploded supergraph:
253///
254/// ```no_run
255/// use weir::ifds::ifds_reach_step;
256/// use vyre_primitives::graph::program_graph::ProgramGraphShape;
257///
258/// let shape = ProgramGraphShape::new(4, 4);
259/// let step = ifds_reach_step(shape, "fin", "fout");
260/// ```
261pub mod ifds;
262
263mod ifds_borrowed_solve;
264#[cfg(any(test, feature = "cpu-parity"))]
265mod ifds_cpu_oracle;
266#[cfg(test)]
267mod ifds_cpu_oracle_tests;
268mod ifds_csr_prepare;
269mod ifds_csr_split;
270mod ifds_frontier_decode;
271
272/// GPU-native IFDS/IDE driver (G3).
273///
274/// Build a BFS step over the exploded supergraph:
275///
276/// ```no_run
277/// use weir::ifds_gpu::{ifds_gpu_step, IfdsShape};
278///
279/// let shape = IfdsShape {
280///     num_procs: 1, blocks_per_proc: 4, facts_per_proc: 4, edge_count: 4,
281/// };
282/// let step = ifds_gpu_step(shape, "fin", "fout").unwrap();
283/// ```
284pub mod ifds_gpu;
285
286mod ifds_gpu_bytes;
287mod ifds_resident_alloc;
288
289/// Batch resident IFDS execution.
290///
291/// ```ignore
292/// use weir::ifds_resident_batch::ResidentIfdsBatch;
293/// ```
294pub mod ifds_resident_batch;
295
296/// Cache for resident IFDS graph reuse.
297///
298/// ```ignore
299/// use weir::ifds_resident_cache::ResidentIfdsCache;
300/// ```
301pub mod ifds_resident_cache;
302
303/// Direct-resident IFDS batch facade.
304///
305/// ```ignore
306/// use weir::ifds_resident_direct_batch::DirectResidentIfdsBatch;
307/// ```
308pub mod ifds_resident_direct_batch;
309
310/// Direct resident IFDS CSR preparation.
311///
312/// ```ignore
313/// use weir::ifds_resident_direct_prepare::prepare_ifds_csr_resident_direct_via;
314/// ```
315pub mod ifds_resident_direct_prepare;
316
317/// Reusable host scratch for direct-resident IFDS CSR preparation.
318///
319/// ```ignore
320/// use weir::ifds_resident_direct_prepare_scratch::DirectResidentIfdsPrepareScratch;
321/// ```
322pub mod ifds_resident_direct_prepare_scratch;
323
324mod ifds_resident_solve;
325mod ifds_resident_types;
326mod ifds_seed_frontiers;
327mod ifds_seed_pack;
328mod ifds_shape;
329
330/// Adapter from the common Vyre resident backend contract to Weir IFDS.
331///
332/// ```ignore
333/// use weir::ifds_vyre_resident::VyreResidentIfds;
334/// ```
335pub mod ifds_vyre_resident;
336
337/// Live-variable analysis (DF-2 backward variant).
338///
339/// Prepare a graph for backward live-variable closure:
340///
341/// ```
342/// use weir::live::prepare_live_graph;
343///
344/// let graph = prepare_live_graph(
345///     2,
346///     &[0, 1, 1],
347///     &[1],
348///     &[0xFFFF_FFFF],
349/// ).unwrap();
350/// assert_eq!(graph.node_count(), 2);
351/// ```
352pub mod live;
353
354/// Liveness query: is variable `v` live at node `n`?
355///
356/// Build a live-at query Program:
357///
358/// ```no_run
359/// use weir::live_at::live_at;
360///
361/// let program = live_at(4, "live_in", "query", "out");
362/// ```
363pub mod live_at;
364
365/// Loop summarization with widening+narrowing (DF-10).
366///
367/// Build a loop-summary Program:
368///
369/// ```no_run
370/// use weir::loop_sum::loop_summarize;
371///
372/// let program = loop_summarize("prev", "next", "out");
373/// ```
374pub mod loop_sum;
375
376/// May-alias query packed as a bitset.
377///
378/// Build a may-alias Program:
379///
380/// ```no_run
381/// use weir::may_alias::may_alias;
382///
383/// let program = may_alias(4, "pts_p", "pts_q", "scratch", "out");
384/// ```
385pub mod may_alias;
386
387/// Must-initialization analysis.
388///
389/// Build a must-init query Program:
390///
391/// ```no_run
392/// use weir::must_init::must_init;
393///
394/// let program = must_init(4, "def_dominates", "use_set", "out");
395/// ```
396pub mod must_init;
397
398#[cfg(any(test, feature = "cpu-parity"))]
399/// CPU parity oracle dispatch surface.
400///
401/// ```ignore
402/// use weir::oracle;
403/// ```
404pub mod oracle;
405
406/// Relation import/export fixtures for IFDS and differential evidence.
407///
408/// ```ignore
409/// use weir::relation_interchange::{
410///     export_relation_interchange_bytes, import_relation_interchange_bytes,
411/// };
412/// ```
413pub mod relation_interchange;
414
415mod output_scratch;
416
417/// Andersen points-to analysis (DF-3).
418///
419/// Prepare a graph for points-to subset closure:
420///
421/// ```
422/// use weir::points_to::prepare_points_to_subset_graph;
423///
424/// let graph = prepare_points_to_subset_graph(
425///     2,
426///     &[0, 1, 1],
427///     &[1],
428///     &[0xFFFF_FFFF],
429/// ).unwrap();
430/// assert_eq!(graph.node_count(), 2);
431/// ```
432pub mod points_to;
433
434/// Post-dominator tree query.
435///
436/// Build a post-dominates query Program:
437///
438/// ```no_run
439/// use weir::post_dominates::post_dominates;
440///
441/// let program = post_dominates(4, "pdom_set", "target_set", "out");
442/// ```
443pub mod post_dominates;
444
445/// Interval range propagation (DF-7).
446///
447/// Build a range-propagation Program:
448///
449/// ```no_run
450/// use weir::range::range_propagate;
451///
452/// let program = range_propagate("defs_in", "edges_in", "ranges_out");
453/// ```
454pub mod range;
455
456/// Interval bound check for VSA results.
457///
458/// Build a range-check Program:
459///
460/// ```no_run
461/// use weir::range_check::range_check;
462///
463/// let program = range_check(4, "interval_hi", "bound", "out");
464/// ```
465pub mod range_check;
466
467/// Witness extraction from IFDS reachability results.
468///
469/// Prepare a witness graph and extract a source-to-sink path:
470///
471/// ```
472/// use weir::reachability_witness::{
473///     prepare_witness_graph, extract_path_prepared, NodeAttr, PathSeed,
474/// };
475///
476/// let attrs = vec![
477///     NodeAttr { byte_start: 0, byte_end: 4, file_idx: 0 },
478///     NodeAttr { byte_start: 5, byte_end: 9, file_idx: 0 },
479/// ];
480/// let files = vec!["example.c".to_string()];
481/// let descriptions = vec!["source".to_string(), "sink".to_string()];
482/// let edge_offsets = vec![0, 1, 1];
483/// let edge_targets = vec![1];
484/// let edge_kind_mask = vec![1u32];
485///
486/// let graph = prepare_witness_graph(
487///     &edge_offsets, &edge_targets, &edge_kind_mask,
488///     &attrs, &files, &descriptions, "c-c11",
489/// );
490///
491/// let seed = PathSeed {
492///     source_file: "example.c".to_string(),
493///     source_node: 0,
494///     sink_file: "example.c".to_string(),
495///     sink_node: 1,
496/// };
497/// let source_reach = vec![u32::MAX; 1];
498/// let path = extract_path_prepared(&seed, &source_reach, &[], &graph).unwrap();
499/// assert_eq!(path.statements.len(), 2);
500/// ```
501pub mod reachability_witness;
502
503/// Reaching definitions analysis (DF-2).
504///
505/// Prepare a graph for forward reaching-definitions closure:
506///
507/// ```
508/// use weir::reaching::prepare_reaching_graph;
509///
510/// let graph = prepare_reaching_graph(
511///     2,
512///     &[0, 1, 1],
513///     &[1],
514///     &[0xFFFF_FFFF],
515/// ).unwrap();
516/// assert_eq!(graph.node_count(), 2);
517/// assert_eq!(graph.edge_count(), 1);
518/// ```
519pub mod reaching;
520
521/// Reaching-definitions query packed as a bitset.
522///
523/// Build a reaching-def query Program:
524///
525/// ```no_run
526/// use weir::reaching_def::reaching_def;
527///
528/// let program = reaching_def(4, "gen_kill_in", "use_set", "out");
529/// ```
530pub mod reaching_def;
531
532/// Compact reaching-definition summary queries.
533///
534/// Fold partial summary triples into a single summary:
535///
536/// ```
537/// use weir::reaching_def_summary::fold_summary_partials;
538///
539/// let partials = [3, 1, 0xAB, 2, 0, 0xCD, 5, 1, 0xEF];
540/// let [count, any, checksum] = fold_summary_partials(&partials);
541/// assert_eq!(count, 10);
542/// assert_eq!(any, 1);
543/// assert_eq!(checksum, 0xAB ^ 0xEF);
544/// ```
545pub mod reaching_def_summary;
546
547mod resident_cache_lru;
548
549/// Strongly-connected-component membership query.
550///
551/// Build an SCC-membership query Program:
552///
553/// ```no_run
554/// use weir::scc_query::scc_query;
555///
556/// let program = scc_query(4, "same_scc", "query", "out");
557/// ```
558pub mod scc_query;
559
560/// Backward program slicer (DF-6).
561///
562/// Prepare a graph for backward slicing:
563///
564/// ```
565/// use weir::slice::prepare_slice_graph;
566///
567/// let graph = prepare_slice_graph(
568///     2,
569///     &[0, 1, 1],
570///     &[1],
571///     &[0xFFFF_FFFF],
572/// ).unwrap();
573/// assert_eq!(graph.node_count(), 2);
574/// ```
575pub mod slice;
576
577/// Soundness regime markers and evidence types.
578///
579/// Tag a dataflow result with its soundness regime:
580///
581/// ```
582/// use weir::soundness::{DataflowEvidence, Soundness, PrimitiveSoundness, PrecisionContract};
583///
584/// let evidence = DataflowEvidence::new(vec![1u32, 2, 3], Soundness::Exact);
585/// assert_eq!(evidence.soundness, Soundness::Exact);
586///
587/// let primitive = PrimitiveSoundness::new("weir::ssa", Soundness::Exact);
588/// assert_eq!(primitive.op_id, "weir::ssa");
589/// let contract = PrecisionContract::ZeroFalsePositive;
590/// ```
591pub mod soundness;
592
593/// SSA construction via Cytron's algorithm (DF-1).
594///
595/// Compute immediate dominators for a diamond CFG:
596///
597/// ```
598/// use std::collections::{HashMap, HashSet};
599/// use weir::ssa::{try_compute_dominators, Block, Cfg};
600///
601/// let mut blocks = HashMap::new();
602/// for (id, preds, succs) in [
603///     (0u32, vec![], vec![1, 2]),
604///     (1, vec![0], vec![3]),
605///     (2, vec![0], vec![3]),
606///     (3, vec![1, 2], vec![]),
607/// ] {
608///     blocks.insert(id, Block { id, preds, succs, defs: HashSet::new(), uses: HashSet::new() });
609/// }
610///
611/// let idoms = try_compute_dominators(&Cfg { entry: 0, blocks }).unwrap();
612/// assert_eq!(idoms[&1], 0);
613/// assert_eq!(idoms[&2], 0);
614/// assert_eq!(idoms[&3], 0);
615/// ```
616pub mod ssa;
617
618mod staging_reserve;
619
620/// Persistent procedure summaries (DF-9).
621///
622/// Build a summary-computation Program:
623///
624/// ```no_run
625/// use weir::summary::summarize_function;
626///
627/// let program = summarize_function(
628///     "fn_ast_in", "callgraph_in", "cached_summary_in", "summary_out",
629/// );
630/// ```
631pub mod summary;
632
633#[cfg(any(test, feature = "test-harness"))]
634/// Test harness for parity and conformance testing.
635///
636/// ```ignore
637/// use weir::test_harness;
638/// ```
639#[cfg(feature = "test-harness")]
640pub mod test_harness;
641
642/// Forward and backward CFG traversal step builders.
643///
644/// Build a forward CFG propagation step:
645///
646/// ```no_run
647/// use weir::traversal_step::forward_cfg_step;
648/// use vyre_primitives::graph::program_graph::ProgramGraphShape;
649///
650/// let shape = ProgramGraphShape::new(4, 4);
651/// let step = forward_cfg_step(shape, "fin", "fout");
652/// ```
653pub mod traversal_step;
654
655/// Constant-value-set reachability query.
656///
657/// Build a value-set query Program:
658///
659/// ```no_run
660/// use weir::value_set::value_set;
661///
662/// let program = value_set(4, "const_in", "query", "out");
663/// ```
664pub mod value_set;
665
666#[cfg(test)]
667mod tests {
668    mod test_ssa;
669
670    #[test]
671    fn crate_private_cpu_helpers_are_explicit_reference_oracles() {
672        let manifest_dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR"));
673        let src = manifest_dir.join("src");
674        let mut stack = vec![src];
675        let mut findings = Vec::new();
676        while let Some(path) = stack.pop() {
677            let entries = std::fs::read_dir(&path)
678                .unwrap_or_else(|error| panic!("failed to read {}: {error}", path.display()));
679            for entry in entries {
680                let entry = entry.unwrap_or_else(|error| {
681                    panic!(
682                        "failed to read directory entry under {}: {error}",
683                        path.display()
684                    )
685                });
686                let path = entry.path();
687                if path.is_dir() {
688                    stack.push(path);
689                    continue;
690                }
691                if path.extension().and_then(std::ffi::OsStr::to_str) != Some("rs") {
692                    continue;
693                }
694                let source = std::fs::read_to_string(&path)
695                    .unwrap_or_else(|error| panic!("failed to read {}: {error}", path.display()));
696                let lines = source.lines().collect::<Vec<_>>();
697                for (line_index, line) in lines.iter().enumerate() {
698                    let trimmed = line.trim_start();
699                    if !trimmed.starts_with("pub(crate) fn ") || !trimmed.contains("cpu") {
700                        continue;
701                    }
702                    let context_start = line_index.saturating_sub(4);
703                    let context = lines[context_start..=line_index].join("\n");
704                    if !context.contains("#[deprecated")
705                        || !context.contains("reference oracle only")
706                    {
707                        findings.push(format!(
708                            "{}:{}: {line}",
709                            path.strip_prefix(manifest_dir).unwrap_or(&path).display(),
710                            line_index + 1
711                        ));
712                    }
713                }
714            }
715        }
716        assert!(
717            findings.is_empty(),
718            "crate-private CPU helpers must be explicit deprecated reference oracles, never production fallback hooks:\n{}",
719            findings.join("\n")
720        );
721    }
722
723    #[test]
724    fn production_modules_do_not_call_cpu_oracles_directly() {
725        const CPU_ORACLE_NAMES: &[&str] = &[
726            "callgraph_build_cpu",
727            "compute_cpu",
728            "cpu_ref",
729            "cpu_ref_sharded",
730            "cpu_subset_closure",
731            "ifds_reach_closure_cpu",
732            "live_closure_cpu",
733            "loop_summarize_cpu",
734            "range_propagate_cpu",
735            "reaching_closure_cpu",
736            "slice_closure_cpu",
737            "solve_cpu",
738            "summarize_function_cpu",
739        ];
740
741        let manifest_dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR"));
742        let src = manifest_dir.join("src");
743        let mut stack = vec![src];
744        let mut findings = Vec::new();
745        while let Some(path) = stack.pop() {
746            let entries = std::fs::read_dir(&path)
747                .unwrap_or_else(|error| panic!("failed to read {}: {error}", path.display()));
748            for entry in entries {
749                let entry = entry.unwrap_or_else(|error| {
750                    panic!(
751                        "failed to read directory entry under {}: {error}",
752                        path.display()
753                    )
754                });
755                let path = entry.path();
756                if path.is_dir() {
757                    stack.push(path);
758                    continue;
759                }
760                if path.extension().and_then(std::ffi::OsStr::to_str) != Some("rs") {
761                    continue;
762                }
763                let relative = path.strip_prefix(manifest_dir).unwrap_or(&path);
764                let relative_text = relative.to_string_lossy();
765                if relative_text.contains("/oracle/")
766                    || relative_text.contains("_tests/")
767                    || relative_text.contains("/test_harness/")
768                    || relative_text.ends_with("_tests.rs")
769                    || relative_text.ends_with("/tests.rs")
770                    || relative_text.ends_with("_cpu_oracle.rs")
771                    || relative_text.ends_with("/cpu_oracle.rs")
772                    || relative_text.ends_with("/oracle.rs")
773                {
774                    continue;
775                }
776                let source = std::fs::read_to_string(&path)
777                    .unwrap_or_else(|error| panic!("failed to read {}: {error}", path.display()));
778                let mut deprecated_attr_body = false;
779                let mut pending_reference_oracle_deprecation = false;
780                let mut reference_oracle_body_depth = 0i32;
781                let mut pending_test_cfg = false;
782                let mut test_module_depth = 0i32;
783                for (line_index, line) in source.lines().enumerate() {
784                    let trimmed = line.trim_start();
785                    if test_module_depth > 0 {
786                        test_module_depth += super::brace_delta(line);
787                        continue;
788                    }
789                    if reference_oracle_body_depth > 0 {
790                        reference_oracle_body_depth += super::brace_delta(line);
791                        continue;
792                    }
793                    if trimmed.starts_with("#[cfg(test)]") {
794                        pending_test_cfg = true;
795                        continue;
796                    }
797                    if pending_test_cfg {
798                        if trimmed.starts_with("mod ") {
799                            test_module_depth = super::brace_delta(line).max(1);
800                            pending_test_cfg = false;
801                            continue;
802                        }
803                        if !trimmed.starts_with("#[") {
804                            pending_test_cfg = false;
805                        }
806                    }
807                    if deprecated_attr_body {
808                        pending_reference_oracle_deprecation |=
809                            trimmed.contains("reference oracle only");
810                        deprecated_attr_body = !trimmed.contains(")]");
811                        continue;
812                    }
813                    if trimmed.starts_with("#[deprecated") {
814                        pending_reference_oracle_deprecation =
815                            trimmed.contains("reference oracle only");
816                        deprecated_attr_body = !trimmed.contains(")]");
817                        continue;
818                    }
819                    if pending_reference_oracle_deprecation && trimmed.starts_with("pub(crate) fn ")
820                    {
821                        reference_oracle_body_depth = super::brace_delta(line).max(1);
822                        pending_reference_oracle_deprecation = false;
823                        continue;
824                    }
825                    if !trimmed.starts_with("#[") {
826                        pending_reference_oracle_deprecation = false;
827                    }
828                    if trimmed.starts_with("pub(crate) fn ")
829                        || trimmed.starts_with("pub(crate) use ")
830                        || trimmed.starts_with("use ")
831                        || trimmed.starts_with("pub use ")
832                        || trimmed.starts_with("///")
833                        || trimmed.starts_with("//!")
834                    {
835                        continue;
836                    }
837                    if CPU_ORACLE_NAMES.iter().any(|name| trimmed.contains(name)) {
838                        findings.push(format!("{}:{}: {line}", relative.display(), line_index + 1));
839                    }
840                }
841            }
842        }
843        assert!(
844            findings.is_empty(),
845            "production Weir modules must dispatch GPU Programs and may only reach CPU oracles through weir::oracle parity APIs:\n{}",
846            findings.join("\n")
847        );
848    }
849}
850
851#[cfg(test)]
852fn brace_delta(line: &str) -> i32 {
853    line.bytes().fold(0i32, |depth, byte| match byte {
854        b'{' => depth + 1,
855        b'}' => depth - 1,
856        _ => depth,
857    })
858}
859
860pub use soundness::{
861    validate_dynamic_pipeline, validate_dynamic_primitive, validate_pipeline, validate_primitive,
862    DataflowEvidence, DynamicPrimitiveSoundness, DynamicSoundnessViolation, PrecisionContract,
863    PrimitiveSoundness, Soundness, SoundnessTagged, SoundnessViolation,
864};