circomspect_program_structure/control_flow_graph/
basic_block.rs

1use log::trace;
2use std::collections::HashSet;
3use std::fmt;
4
5use crate::ir::declarations::Declarations;
6use crate::ir::degree_meta::DegreeEnvironment;
7use crate::ir::value_meta::ValueEnvironment;
8use crate::ssa::traits::DirectedGraphNode;
9
10use crate::ir::variable_meta::{VariableMeta, VariableUses};
11use crate::ir::{Meta, Statement};
12
13type Index = usize;
14type IndexSet = HashSet<Index>;
15
16#[derive(Clone)]
17pub struct BasicBlock {
18    index: Index,
19    meta: Meta,
20    loop_depth: usize,
21    stmts: Vec<Statement>,
22    predecessors: IndexSet,
23    successors: IndexSet,
24}
25
26impl BasicBlock {
27    #[must_use]
28    pub fn new(meta: Meta, index: Index, loop_depth: usize) -> BasicBlock {
29        trace!("creating basic block {index}");
30        BasicBlock {
31            meta,
32            index,
33            loop_depth,
34            stmts: Vec::new(),
35            predecessors: IndexSet::new(),
36            successors: IndexSet::new(),
37        }
38    }
39
40    #[must_use]
41    pub fn from_raw_parts(
42        index: Index,
43        meta: Meta,
44        loop_depth: usize,
45        stmts: Vec<Statement>,
46        predecessors: IndexSet,
47        successors: IndexSet,
48    ) -> BasicBlock {
49        BasicBlock { index, meta, loop_depth, stmts, predecessors, successors }
50    }
51
52    #[must_use]
53    pub fn len(&self) -> usize {
54        self.stmts.len()
55    }
56
57    #[must_use]
58    pub fn is_empty(&self) -> bool {
59        self.len() == 0
60    }
61
62    #[must_use]
63    pub fn in_loop(&self) -> bool {
64        self.loop_depth > 0
65    }
66
67    #[must_use]
68    pub fn loop_depth(&self) -> usize {
69        self.loop_depth
70    }
71
72    pub fn iter(&self) -> impl Iterator<Item = &Statement> {
73        self.stmts.iter()
74    }
75
76    pub(crate) fn iter_mut(&mut self) -> impl Iterator<Item = &mut Statement> {
77        self.stmts.iter_mut()
78    }
79    #[must_use]
80    pub fn index(&self) -> Index {
81        self.index
82    }
83
84    #[must_use]
85    pub fn meta(&self) -> &Meta {
86        &self.meta
87    }
88
89    #[must_use]
90    pub(crate) fn meta_mut(&mut self) -> &mut Meta {
91        &mut self.meta
92    }
93
94    #[must_use]
95    pub fn statements(&self) -> &Vec<Statement> {
96        &self.stmts
97    }
98
99    #[must_use]
100    pub(crate) fn statements_mut(&mut self) -> &mut Vec<Statement> {
101        &mut self.stmts
102    }
103
104    pub(crate) fn prepend_statement(&mut self, stmt: Statement) {
105        self.stmts.insert(0, stmt);
106    }
107
108    pub(crate) fn append_statement(&mut self, stmt: Statement) {
109        self.stmts.push(stmt);
110    }
111
112    #[must_use]
113    pub fn predecessors(&self) -> &IndexSet {
114        &self.predecessors
115    }
116
117    #[must_use]
118    pub fn successors(&self) -> &IndexSet {
119        &self.successors
120    }
121
122    pub(crate) fn add_predecessor(&mut self, predecessor: Index) {
123        trace!("adding predecessor {} to basic block {}", predecessor, self.index);
124        self.predecessors.insert(predecessor);
125    }
126
127    pub(crate) fn add_successor(&mut self, successor: Index) {
128        trace!("adding successor {} to basic block {}", successor, self.index);
129        self.successors.insert(successor);
130    }
131
132    pub fn propagate_degrees(&mut self, env: &mut DegreeEnvironment) -> bool {
133        trace!("propagating degree ranges for basic block {}", self.index());
134        let mut result = false;
135        for stmt in self.iter_mut() {
136            result = result || stmt.propagate_degrees(env);
137        }
138        result
139    }
140
141    pub fn propagate_values(&mut self, env: &mut ValueEnvironment) -> bool {
142        trace!("propagating values for basic block {}", self.index());
143        let mut result = false;
144        for stmt in self.iter_mut() {
145            result = result || stmt.propagate_values(env);
146        }
147        result
148    }
149
150    pub fn propagate_types(&mut self, vars: &Declarations) {
151        trace!("propagating variable types for basic block {}", self.index());
152        for stmt in self.iter_mut() {
153            stmt.propagate_types(vars);
154        }
155    }
156}
157
158impl DirectedGraphNode for BasicBlock {
159    fn index(&self) -> Index {
160        self.index
161    }
162    fn predecessors(&self) -> &IndexSet {
163        &self.predecessors
164    }
165    fn successors(&self) -> &IndexSet {
166        &self.successors
167    }
168}
169
170impl VariableMeta for BasicBlock {
171    fn cache_variable_use(&mut self) {
172        trace!("computing variable use for basic block {}", self.index());
173        // Variable use for the block is simply the union of the variable use
174        // over all statements in the block.
175        for stmt in self.iter_mut() {
176            stmt.cache_variable_use();
177        }
178
179        // Cache variables read.
180        let locals_read = self.iter().flat_map(|stmt| stmt.locals_read()).cloned().collect();
181
182        // Cache variables written.
183        let locals_written = self.iter().flat_map(|stmt| stmt.locals_written()).cloned().collect();
184
185        // Cache signals read.
186        let signals_read = self.iter().flat_map(|stmt| stmt.signals_read()).cloned().collect();
187
188        // Cache signals written.
189        let signals_written =
190            self.iter().flat_map(|stmt| stmt.signals_written()).cloned().collect();
191
192        // Cache components read.
193        let components_read =
194            self.iter().flat_map(|stmt| stmt.components_read()).cloned().collect();
195
196        // Cache components written.
197        let components_written =
198            self.iter().flat_map(|stmt| stmt.components_written()).cloned().collect();
199
200        self.meta_mut()
201            .variable_knowledge_mut()
202            .set_locals_read(&locals_read)
203            .set_locals_written(&locals_written)
204            .set_signals_read(&signals_read)
205            .set_signals_written(&signals_written)
206            .set_components_read(&components_read)
207            .set_components_written(&components_written);
208    }
209
210    fn locals_read(&self) -> &VariableUses {
211        self.meta().variable_knowledge().locals_read()
212    }
213
214    fn locals_written(&self) -> &VariableUses {
215        self.meta().variable_knowledge().locals_written()
216    }
217
218    fn signals_read(&self) -> &VariableUses {
219        self.meta().variable_knowledge().signals_read()
220    }
221
222    fn signals_written(&self) -> &VariableUses {
223        self.meta().variable_knowledge().signals_written()
224    }
225
226    fn components_read(&self) -> &VariableUses {
227        self.meta().variable_knowledge().components_read()
228    }
229
230    fn components_written(&self) -> &VariableUses {
231        self.meta().variable_knowledge().components_written()
232    }
233}
234
235impl fmt::Debug for BasicBlock {
236    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
237        let lines = self.iter().map(|stmt| format!("{stmt:?}")).collect::<Vec<_>>();
238        let width = lines.iter().map(|line| line.len()).max().unwrap_or_default();
239        let border = format!("+{}+", (0..width + 2).map(|_| '-').collect::<String>());
240
241        writeln!(f, "{}", &border)?;
242        for line in lines {
243            writeln!(f, "| {line:width$} |")?;
244        }
245        writeln!(f, "{}", &border)
246    }
247}