1use std::cell::RefCell;
4use std::collections::{HashMap, HashSet};
5use std::panic::RefUnwindSafe;
6use std::rc::Rc;
7
8use dfir_lang::graph::{DfirGraph, FlatGraphBuilder, FlatGraphBuilderOutput};
9use libloading::Library;
10use slotmap::SparseSecondaryMap;
11
12use super::builder::SimBuilder;
13use super::compiled::{CompiledSim, CompiledSimInstance};
14use super::graph::{SimDeploy, SimExternal, SimNode, compile_sim, create_sim_graph_trybuild};
15use crate::compile::ir::HydroRoot;
16use crate::location::LocationKey;
17use crate::location::dynamic::LocationId;
18use crate::prelude::Cluster;
19use crate::sim::graph::SimExternalPortRegistry;
20use crate::staging_util::Invariant;
21
22pub struct SimFlow<'a> {
24 pub(crate) ir: Vec<HydroRoot>,
25
26 pub(crate) processes: SparseSecondaryMap<LocationKey, SimNode>,
28 pub(crate) clusters: SparseSecondaryMap<LocationKey, SimNode>,
30 pub(crate) externals: SparseSecondaryMap<LocationKey, SimExternal>,
32
33 pub(crate) cluster_max_sizes: SparseSecondaryMap<LocationKey, usize>,
35 pub(crate) externals_port_registry: Rc<RefCell<SimExternalPortRegistry>>,
37
38 pub(crate) _phantom: Invariant<'a>,
39}
40
41impl<'a> SimFlow<'a> {
42 pub fn with_cluster_size<C>(mut self, cluster: &Cluster<'a, C>, max_size: usize) -> Self {
44 self.cluster_max_sizes.insert(cluster.key, max_size);
45 self
46 }
47
48 pub fn with_instance<T>(self, thunk: impl FnOnce(CompiledSimInstance) -> T) -> T {
50 self.compiled().with_instance(thunk)
51 }
52
53 pub fn fuzz(self, thunk: impl AsyncFn() + RefUnwindSafe) {
64 self.compiled().fuzz(thunk)
65 }
66
67 pub fn exhaustive(self, thunk: impl AsyncFnMut() + RefUnwindSafe) -> usize {
78 self.compiled().exhaustive(thunk)
79 }
80
81 pub fn compiled(mut self) -> CompiledSim {
83 use std::collections::BTreeMap;
84
85 use dfir_lang::graph::{eliminate_extra_unions_tees, partition_graph};
86
87 let mut sim_emit = SimBuilder {
88 process_graphs: BTreeMap::new(),
89 cluster_graphs: BTreeMap::new(),
90 process_tick_dfirs: BTreeMap::new(),
91 cluster_tick_dfirs: BTreeMap::new(),
92 extra_stmts_global: vec![],
93 extra_stmts_cluster: BTreeMap::new(),
94 next_hoff_id: 0,
95 };
96
97 self.externals.insert(
99 LocationKey::FIRST,
100 SimExternal {
101 shared_inner: self.externals_port_registry.clone(),
102 },
103 );
104
105 let mut seen_tees_instantiate: HashMap<_, _> = HashMap::new();
106 let mut seen_cluster_members = HashSet::new();
107 self.ir.iter_mut().for_each(|leaf| {
108 leaf.compile_network::<SimDeploy>(
109 &mut SparseSecondaryMap::new(),
110 &mut seen_tees_instantiate,
111 &mut seen_cluster_members,
112 &self.processes,
113 &self.clusters,
114 &self.externals,
115 &mut (),
116 );
117 });
118
119 let mut seen_tees = HashMap::new();
120 let mut built_tees = HashMap::new();
121 let mut next_stmt_id = 0;
122 for leaf in &mut self.ir {
123 leaf.emit(
124 &mut sim_emit,
125 &mut seen_tees,
126 &mut built_tees,
127 &mut next_stmt_id,
128 );
129 }
130
131 fn build_graphs(
132 graphs: BTreeMap<LocationId, FlatGraphBuilder>,
133 ) -> BTreeMap<LocationId, DfirGraph> {
134 graphs
135 .into_iter()
136 .map(|(l, g)| {
137 let FlatGraphBuilderOutput { mut flat_graph, .. } =
138 g.build().expect("Failed to build DFIR flat graph.");
139 eliminate_extra_unions_tees(&mut flat_graph);
140 (
141 l,
142 partition_graph(flat_graph).expect("Failed to partition (cycle detected)."),
143 )
144 })
145 .collect()
146 }
147
148 let process_graphs = build_graphs(sim_emit.process_graphs);
149 let cluster_graphs = build_graphs(sim_emit.cluster_graphs);
150 let process_tick_graphs = build_graphs(sim_emit.process_tick_dfirs);
151 let cluster_tick_graphs = build_graphs(sim_emit.cluster_tick_dfirs);
152
153 #[expect(
154 clippy::disallowed_methods,
155 reason = "nondeterministic iteration order, fine for checks"
156 )]
157 for c in self.clusters.keys() {
158 assert!(
159 self.cluster_max_sizes.contains_key(c),
160 "Cluster {:?} missing max size; call with_cluster_size() before compiled()",
161 c
162 );
163 }
164
165 let (bin, trybuild) = create_sim_graph_trybuild(
166 process_graphs,
167 cluster_graphs,
168 self.cluster_max_sizes,
169 process_tick_graphs,
170 cluster_tick_graphs,
171 sim_emit.extra_stmts_global,
172 sim_emit.extra_stmts_cluster,
173 );
174
175 let out = compile_sim(bin, trybuild).unwrap();
176 let lib = unsafe { Library::new(&out).unwrap() };
177
178 CompiledSim {
179 _path: out,
180 lib,
181 externals_port_registry: self.externals_port_registry.take(),
182 }
183 }
184}