calyx_opt/passes/
static_inference.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
use crate::analysis::{GoDone, InferenceAnalysis};
use crate::traversal::{
    Action, ConstructVisitor, Named, Order, VisResult, Visitor,
};
use calyx_ir::{self as ir, LibrarySignatures};
use calyx_utils::CalyxResult;
use itertools::Itertools;

/// Infer @promotable annotation
/// for groups and control.
/// Inference occurs whenever possible.
pub struct StaticInference {
    /// Takes static information.
    inference_analysis: InferenceAnalysis,
}

// Override constructor to build latency_data information from the primitives
// library.
impl ConstructVisitor for StaticInference {
    fn from(ctx: &ir::Context) -> CalyxResult<Self> {
        Ok(StaticInference {
            inference_analysis: InferenceAnalysis::from_ctx(ctx),
        })
    }

    // This pass shared information between components
    fn clear_data(&mut self) {}
}

impl Named for StaticInference {
    fn name() -> &'static str {
        "static-inference"
    }

    fn description() -> &'static str {
        "infer when dynamic control programs are promotable"
    }
}

impl Visitor for StaticInference {
    // Require post order traversal of components to ensure `invoke` nodes
    // get timing information for components.
    fn iteration_order() -> Order {
        Order::Post
    }

    fn finish(
        &mut self,
        comp: &mut ir::Component,
        _lib: &LibrarySignatures,
        _comps: &[ir::Component],
    ) -> VisResult {
        if comp.name != "main" {
            // If the entire component's control is promotable.
            if let Some(val) =
                InferenceAnalysis::get_possible_latency(&comp.control.borrow())
            {
                let comp_sig = comp.signature.borrow();
                let mut go_ports: Vec<_> =
                    comp_sig.find_all_with_attr(ir::NumAttr::Go).collect();
                // Insert @promotable attribute on the go ports.
                for go_port in &mut go_ports {
                    go_port
                        .borrow_mut()
                        .attributes
                        .insert(ir::NumAttr::Promotable, val);
                }
                let mut done_ports: Vec<_> =
                    comp_sig.find_all_with_attr(ir::NumAttr::Done).collect();
                // Update `latency_data`.
                go_ports.sort_by_key(|port| {
                    port.borrow().attributes.get(ir::NumAttr::Go).unwrap()
                });
                done_ports.sort_by_key(|port| {
                    port.borrow().attributes.get(ir::NumAttr::Done).unwrap()
                });
                let zipped: Vec<_> =
                    go_ports.iter().zip(done_ports.iter()).collect();
                let go_done_ports = zipped
                    .into_iter()
                    .map(|(go_port, done_port)| {
                        (go_port.borrow().name, done_port.borrow().name, val)
                    })
                    .collect_vec();
                self.inference_analysis.add_component((
                    comp.name,
                    val,
                    GoDone::new(go_done_ports),
                ));
            }
        }
        Ok(Action::Continue)
    }

    fn start(
        &mut self,
        comp: &mut ir::Component,
        _sigs: &LibrarySignatures,
        _comps: &[ir::Component],
    ) -> VisResult {
        // ``Fix up the timing'', but with the updated_components argument as
        // and empty HashMap. This just performs inference.
        self.inference_analysis.fixup_timing(comp);
        Ok(Action::Continue)
    }
}