calyx_ir/
flat_guard.rs

1use crate::Nothing;
2
3use super::guard::{Guard, PortComp};
4use super::{Port, RRC};
5
6#[derive(Debug, Copy, Clone)]
7pub struct GuardRef(u32);
8
9impl GuardRef {
10    /// Check whether this refers to a `FlatGuard::True`. (We can do this because the first guard
11    /// in the pool is always `True`.)
12    pub fn is_true(&self) -> bool {
13        self.0 == 0
14    }
15
16    /// Get the underlying number for this reference. Clients should only rely on this being unique
17    /// for non-equal guards in a single pool; no other aspects of the number are relevant.
18    pub fn index(&self) -> u32 {
19        self.0
20    }
21}
22
23#[derive(Debug, Clone)]
24pub enum FlatGuard {
25    Or(GuardRef, GuardRef),
26    And(GuardRef, GuardRef),
27    Not(GuardRef),
28    True,
29    CompOp(PortComp, RRC<Port>, RRC<Port>),
30    Port(RRC<Port>),
31}
32
33impl FlatGuard {
34    pub fn is_true(&self) -> bool {
35        match self {
36            FlatGuard::True => true,
37            FlatGuard::Port(p) => p.borrow().is_constant(1, 1),
38            _ => false,
39        }
40    }
41}
42
43/// A `GuardPool` is an "arena"-style storage area for `FlatGuard`s.
44///
45/// Some invariants for the underlying vector:
46/// * `GuardRefs` are always within the same pool (obviously).
47/// * The underlyings numbers in `GuardRef`s can only go "backward," in the sense that
48///   they refer to smaller indices than the current `FlatGuard`.
49/// * The first `FlatGuard` is always `FlatGuard::True`.
50///
51/// This could be used to do some interesting hash-consing/deduplication; it currently does the
52/// weakest possible form of that: deduplicating `True` guards only.
53pub struct GuardPool(Vec<FlatGuard>);
54
55impl GuardPool {
56    pub fn new() -> Self {
57        let mut vec = Vec::<FlatGuard>::with_capacity(1024);
58        vec.push(FlatGuard::True);
59        Self(vec)
60    }
61
62    fn add(&mut self, guard: FlatGuard) -> GuardRef {
63        // `True` is always the first guard.
64        if guard.is_true() {
65            return GuardRef(0);
66        }
67
68        self.0.push(guard);
69        GuardRef(
70            (self.0.len() - 1)
71                .try_into()
72                .expect("too many guards in the pool"),
73        )
74    }
75
76    pub fn flatten(&mut self, old: &Guard<Nothing>) -> GuardRef {
77        match old {
78            Guard::Or(l, r) => {
79                let flat_l = self.flatten(l);
80                let flat_r = self.flatten(r);
81                self.add(FlatGuard::Or(flat_l, flat_r))
82            }
83            Guard::And(l, r) => {
84                let flat_l = self.flatten(l);
85                let flat_r = self.flatten(r);
86                self.add(FlatGuard::And(flat_l, flat_r))
87            }
88            Guard::Not(g) => {
89                let flat_g = self.flatten(g);
90                self.add(FlatGuard::Not(flat_g))
91            }
92            Guard::True => self.add(FlatGuard::True),
93            Guard::CompOp(op, l, r) => {
94                self.add(FlatGuard::CompOp(op.clone(), l.clone(), r.clone()))
95            }
96            Guard::Port(p) => self.add(FlatGuard::Port(p.clone())),
97            Guard::Info(_) => {
98                panic!("flat guard sees info, think about this more")
99            }
100        }
101    }
102
103    pub fn get(&self, guard: GuardRef) -> &FlatGuard {
104        &self.0[guard.0 as usize]
105    }
106
107    #[cfg(debug_assertions)]
108    pub fn display(&self, guard: &FlatGuard) -> String {
109        match guard {
110            FlatGuard::Or(l, r) => format!(
111                "({} | {})",
112                self.display(self.get(*l)),
113                self.display(self.get(*r))
114            ),
115            FlatGuard::And(l, r) => format!(
116                "({} & {})",
117                self.display(self.get(*l)),
118                self.display(self.get(*r))
119            ),
120            FlatGuard::Not(g) => format!("!{}", self.display(self.get(*g))),
121            FlatGuard::True => "true".to_string(),
122            FlatGuard::CompOp(op, l, r) => {
123                let op_str = match op {
124                    PortComp::Eq => "==",
125                    PortComp::Neq => "!=",
126                    PortComp::Lt => "<",
127                    PortComp::Leq => "<=",
128                    PortComp::Gt => ">",
129                    PortComp::Geq => ">=",
130                };
131                format!(
132                    "({} {} {})",
133                    l.borrow().canonical(),
134                    op_str,
135                    r.borrow().canonical()
136                )
137            }
138            FlatGuard::Port(p) => format!("{}", p.borrow().canonical()),
139        }
140    }
141
142    /// Iterate over *all* the guards in the pool.
143    pub fn iter(&self) -> impl Iterator<Item = (GuardRef, &FlatGuard)> {
144        self.0
145            .iter()
146            .enumerate()
147            .map(|(i, g)| (GuardRef(i.try_into().unwrap()), g))
148    }
149}
150
151impl Default for GuardPool {
152    fn default() -> Self {
153        Self::new()
154    }
155}