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