hydro_lang/compile/
built.rs

1use std::cell::UnsafeCell;
2use std::collections::{BTreeMap, HashMap};
3use std::marker::PhantomData;
4
5use dfir_lang::graph::{DfirGraph, eliminate_extra_unions_tees, partition_graph};
6
7use super::compiled::CompiledFlow;
8use super::deploy::{DeployFlow, DeployResult};
9use super::deploy_provider::{ClusterSpec, Deploy, ExternalSpec, IntoProcessSpec};
10use super::ir::{HydroRoot, emit};
11use crate::location::{Cluster, External, Process};
12#[cfg(feature = "sim")]
13#[cfg(stageleft_runtime)]
14use crate::sim::{flow::SimFlow, graph::SimNode};
15use crate::staging_util::Invariant;
16#[cfg(feature = "viz")]
17use crate::viz::api::GraphApi;
18
19pub struct BuiltFlow<'a> {
20    pub(super) ir: Vec<HydroRoot>,
21    pub(super) process_id_name: Vec<(usize, String)>,
22    pub(super) cluster_id_name: Vec<(usize, String)>,
23    pub(super) external_id_name: Vec<(usize, String)>,
24
25    pub(super) _phantom: Invariant<'a>,
26}
27
28pub(crate) fn build_inner<'a, D: Deploy<'a>>(
29    ir: &mut Vec<HydroRoot>,
30) -> BTreeMap<usize, DfirGraph> {
31    emit::<D>(ir)
32        .into_iter()
33        .map(|(k, v)| {
34            let (mut flat_graph, _, _) = v.build();
35            eliminate_extra_unions_tees(&mut flat_graph);
36            let partitioned_graph =
37                partition_graph(flat_graph).expect("Failed to partition (cycle detected).");
38            (k, partitioned_graph)
39        })
40        .collect()
41}
42
43impl<'a> BuiltFlow<'a> {
44    pub fn ir(&self) -> &Vec<HydroRoot> {
45        &self.ir
46    }
47
48    pub fn process_id_name(&self) -> &Vec<(usize, String)> {
49        &self.process_id_name
50    }
51
52    pub fn cluster_id_name(&self) -> &Vec<(usize, String)> {
53        &self.cluster_id_name
54    }
55
56    pub fn external_id_name(&self) -> &Vec<(usize, String)> {
57        &self.external_id_name
58    }
59
60    /// Get a GraphApi instance for this built flow
61    #[cfg(feature = "viz")]
62    pub fn graph_api(&self) -> GraphApi<'_> {
63        GraphApi::new(
64            &self.ir,
65            &self.process_id_name,
66            &self.cluster_id_name,
67            &self.external_id_name,
68        )
69    }
70
71    // String generation methods
72    #[cfg(feature = "viz")]
73    pub fn mermaid_string(
74        &self,
75        show_metadata: bool,
76        show_location_groups: bool,
77        use_short_labels: bool,
78    ) -> String {
79        self.graph_api()
80            .mermaid_to_string(show_metadata, show_location_groups, use_short_labels)
81    }
82
83    #[cfg(feature = "viz")]
84    pub fn dot_string(
85        &self,
86        show_metadata: bool,
87        show_location_groups: bool,
88        use_short_labels: bool,
89    ) -> String {
90        self.graph_api()
91            .dot_to_string(show_metadata, show_location_groups, use_short_labels)
92    }
93
94    #[cfg(feature = "viz")]
95    pub fn hydroscope_string(
96        &self,
97        show_metadata: bool,
98        show_location_groups: bool,
99        use_short_labels: bool,
100    ) -> String {
101        self.graph_api()
102            .hydroscope_to_string(show_metadata, show_location_groups, use_short_labels)
103    }
104
105    // File generation methods
106    #[cfg(feature = "viz")]
107    pub fn mermaid_to_file(
108        &self,
109        filename: &str,
110        show_metadata: bool,
111        show_location_groups: bool,
112        use_short_labels: bool,
113    ) -> Result<(), Box<dyn std::error::Error>> {
114        self.graph_api().mermaid_to_file(
115            filename,
116            show_metadata,
117            show_location_groups,
118            use_short_labels,
119        )
120    }
121
122    #[cfg(feature = "viz")]
123    pub fn dot_to_file(
124        &self,
125        filename: &str,
126        show_metadata: bool,
127        show_location_groups: bool,
128        use_short_labels: bool,
129    ) -> Result<(), Box<dyn std::error::Error>> {
130        self.graph_api().dot_to_file(
131            filename,
132            show_metadata,
133            show_location_groups,
134            use_short_labels,
135        )
136    }
137
138    #[cfg(feature = "viz")]
139    pub fn hydroscope_to_file(
140        &self,
141        filename: &str,
142        show_metadata: bool,
143        show_location_groups: bool,
144        use_short_labels: bool,
145    ) -> Result<(), Box<dyn std::error::Error>> {
146        self.graph_api().hydroscope_to_file(
147            filename,
148            show_metadata,
149            show_location_groups,
150            use_short_labels,
151        )
152    }
153
154    // Browser generation methods
155    #[cfg(feature = "viz")]
156    pub fn mermaid_to_browser(
157        &self,
158        show_metadata: bool,
159        show_location_groups: bool,
160        use_short_labels: bool,
161        message_handler: Option<&dyn Fn(&str)>,
162    ) -> Result<(), Box<dyn std::error::Error>> {
163        self.graph_api().mermaid_to_browser(
164            show_metadata,
165            show_location_groups,
166            use_short_labels,
167            message_handler,
168        )
169    }
170
171    #[cfg(feature = "viz")]
172    pub fn dot_to_browser(
173        &self,
174        show_metadata: bool,
175        show_location_groups: bool,
176        use_short_labels: bool,
177        message_handler: Option<&dyn Fn(&str)>,
178    ) -> Result<(), Box<dyn std::error::Error>> {
179        self.graph_api().dot_to_browser(
180            show_metadata,
181            show_location_groups,
182            use_short_labels,
183            message_handler,
184        )
185    }
186
187    #[cfg(feature = "viz")]
188    pub fn hydroscope_to_browser(
189        &self,
190        show_metadata: bool,
191        show_location_groups: bool,
192        use_short_labels: bool,
193        message_handler: Option<&dyn Fn(&str)>,
194    ) -> Result<(), Box<dyn std::error::Error>> {
195        self.graph_api().hydroscope_to_browser(
196            show_metadata,
197            show_location_groups,
198            use_short_labels,
199            message_handler,
200        )
201    }
202
203    pub fn optimize_with(mut self, f: impl FnOnce(&mut [HydroRoot])) -> Self {
204        f(&mut self.ir);
205        BuiltFlow {
206            ir: std::mem::take(&mut self.ir),
207            process_id_name: std::mem::take(&mut self.process_id_name),
208            cluster_id_name: std::mem::take(&mut self.cluster_id_name),
209            external_id_name: std::mem::take(&mut self.external_id_name),
210            _phantom: PhantomData,
211        }
212    }
213
214    pub fn with_default_optimize<D: Deploy<'a>>(self) -> DeployFlow<'a, D> {
215        self.into_deploy()
216    }
217
218    #[cfg(feature = "sim")]
219    /// Creates a simulation for this builder, which can be used to run deterministic simulations
220    /// of the Hydro program.
221    pub fn sim(mut self) -> SimFlow<'a> {
222        use std::cell::{Cell, RefCell};
223        use std::rc::Rc;
224
225        use crate::sim::graph::SimExternal;
226
227        let external_ports = Rc::new(RefCell::new((vec![], 0)));
228
229        let global_port_counter = Rc::new(Cell::new(0));
230        let processes = self
231            .process_id_name
232            .iter()
233            .map(|id| {
234                (
235                    id.0,
236                    SimNode {
237                        port_counter: global_port_counter.clone(),
238                    },
239                )
240            })
241            .collect();
242
243        let clusters = self
244            .cluster_id_name
245            .iter()
246            .map(|id| {
247                (
248                    id.0,
249                    SimNode {
250                        port_counter: global_port_counter.clone(),
251                    },
252                )
253            })
254            .collect();
255
256        let all_external_registered = Rc::new(RefCell::new(HashMap::new()));
257        let externals = self
258            .external_id_name
259            .iter()
260            .map(|id| {
261                (
262                    id.0,
263                    SimExternal {
264                        external_ports: external_ports.clone(),
265                        registered: all_external_registered.clone(),
266                    },
267                )
268            })
269            .collect();
270
271        SimFlow {
272            ir: std::mem::take(&mut self.ir),
273            external_ports,
274            processes,
275            clusters,
276            cluster_max_sizes: HashMap::new(),
277            externals,
278            external_registered: all_external_registered.clone(),
279            _process_id_name: std::mem::take(&mut self.process_id_name),
280            _external_id_name: std::mem::take(&mut self.external_id_name),
281            _cluster_id_name: std::mem::take(&mut self.cluster_id_name),
282            _phantom: PhantomData,
283        }
284    }
285
286    pub fn into_deploy<D: Deploy<'a>>(mut self) -> DeployFlow<'a, D> {
287        let processes = if D::has_trivial_node() {
288            self.process_id_name
289                .iter()
290                .map(|id| (id.0, D::trivial_process(id.0)))
291                .collect()
292        } else {
293            HashMap::new()
294        };
295
296        let clusters = if D::has_trivial_node() {
297            self.cluster_id_name
298                .iter()
299                .map(|id| (id.0, D::trivial_cluster(id.0)))
300                .collect()
301        } else {
302            HashMap::new()
303        };
304
305        let externals = if D::has_trivial_node() {
306            self.external_id_name
307                .iter()
308                .map(|id| (id.0, D::trivial_external(id.0)))
309                .collect()
310        } else {
311            HashMap::new()
312        };
313
314        DeployFlow {
315            ir: UnsafeCell::new(std::mem::take(&mut self.ir)),
316            processes,
317            process_id_name: std::mem::take(&mut self.process_id_name),
318            clusters,
319            cluster_id_name: std::mem::take(&mut self.cluster_id_name),
320            externals,
321            external_id_name: std::mem::take(&mut self.external_id_name),
322            _phantom: PhantomData,
323        }
324    }
325
326    pub fn with_process<P, D: Deploy<'a>>(
327        self,
328        process: &Process<P>,
329        spec: impl IntoProcessSpec<'a, D>,
330    ) -> DeployFlow<'a, D> {
331        self.into_deploy().with_process(process, spec)
332    }
333
334    pub fn with_remaining_processes<D: Deploy<'a>, S: IntoProcessSpec<'a, D> + 'a>(
335        self,
336        spec: impl Fn() -> S,
337    ) -> DeployFlow<'a, D> {
338        self.into_deploy().with_remaining_processes(spec)
339    }
340
341    pub fn with_external<P, D: Deploy<'a>>(
342        self,
343        process: &External<P>,
344        spec: impl ExternalSpec<'a, D>,
345    ) -> DeployFlow<'a, D> {
346        self.into_deploy().with_external(process, spec)
347    }
348
349    pub fn with_remaining_externals<D: Deploy<'a>, S: ExternalSpec<'a, D> + 'a>(
350        self,
351        spec: impl Fn() -> S,
352    ) -> DeployFlow<'a, D> {
353        self.into_deploy().with_remaining_externals(spec)
354    }
355
356    pub fn with_cluster<C, D: Deploy<'a>>(
357        self,
358        cluster: &Cluster<C>,
359        spec: impl ClusterSpec<'a, D>,
360    ) -> DeployFlow<'a, D> {
361        self.into_deploy().with_cluster(cluster, spec)
362    }
363
364    pub fn with_remaining_clusters<D: Deploy<'a>, S: ClusterSpec<'a, D> + 'a>(
365        self,
366        spec: impl Fn() -> S,
367    ) -> DeployFlow<'a, D> {
368        self.into_deploy().with_remaining_clusters(spec)
369    }
370
371    pub fn compile<D: Deploy<'a>>(self) -> CompiledFlow<'a, D::GraphId> {
372        self.into_deploy::<D>().compile()
373    }
374
375    pub fn compile_no_network<D: Deploy<'a>>(self) -> CompiledFlow<'a, D::GraphId> {
376        self.into_deploy::<D>().compile_no_network()
377    }
378
379    pub fn deploy<D: Deploy<'a>>(self, env: &mut D::InstantiateEnv) -> DeployResult<'a, D> {
380        self.into_deploy::<D>().deploy(env)
381    }
382
383    #[cfg(feature = "viz")]
384    pub fn generate_all_files(
385        &self,
386        prefix: &str,
387        show_metadata: bool,
388        show_location_groups: bool,
389        use_short_labels: bool,
390    ) -> Result<(), Box<dyn std::error::Error>> {
391        self.graph_api().generate_all_files(
392            prefix,
393            show_metadata,
394            show_location_groups,
395            use_short_labels,
396        )
397    }
398
399    #[cfg(feature = "viz")]
400    pub fn generate_graph_with_config(
401        &self,
402        config: &crate::viz::config::GraphConfig,
403        message_handler: Option<&dyn Fn(&str)>,
404    ) -> Result<(), Box<dyn std::error::Error>> {
405        self.graph_api()
406            .generate_graph_with_config(config, message_handler)
407    }
408
409    #[cfg(feature = "viz")]
410    pub fn generate_all_files_with_config(
411        &self,
412        config: &crate::viz::config::GraphConfig,
413        prefix: &str,
414    ) -> Result<(), Box<dyn std::error::Error>> {
415        self.graph_api()
416            .generate_all_files_with_config(config, prefix)
417    }
418}