circomspect_program_structure/control_flow_graph/
cfg.rs1use log::debug;
2use std::collections::HashSet;
3use std::fmt;
4use std::time::{Instant, Duration};
5
6use crate::constants::UsefulConstants;
7use crate::file_definition::FileID;
8use crate::ir::declarations::{Declaration, Declarations};
9use crate::ir::degree_meta::{DegreeEnvironment, Degree, DegreeRange};
10use crate::ir::value_meta::ValueEnvironment;
11use crate::ir::variable_meta::VariableMeta;
12use crate::ir::{VariableName, VariableType, SignalType};
13use crate::ssa::dominator_tree::DominatorTree;
14use crate::ssa::errors::SSAResult;
15use crate::ssa::{insert_phi_statements, insert_ssa_variables};
16
17use super::basic_block::BasicBlock;
18use super::parameters::Parameters;
19use super::ssa_impl;
20use super::ssa_impl::{Config, Environment};
21
22pub type Index = usize;
24
25const MAX_ANALYSIS_DURATION: Duration = Duration::from_secs(10);
26
27#[derive(Clone)]
28pub enum DefinitionType {
29 Function,
30 Template,
31 CustomTemplate,
32}
33
34impl fmt::Display for DefinitionType {
35 fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
36 match self {
37 DefinitionType::Function => write!(f, "function"),
38 DefinitionType::Template => write!(f, "template"),
39 DefinitionType::CustomTemplate => write!(f, "custom template"),
40 }
41 }
42}
43
44pub struct Cfg {
45 name: String,
46 constants: UsefulConstants,
47 parameters: Parameters,
48 declarations: Declarations,
49 basic_blocks: Vec<BasicBlock>,
50 definition_type: DefinitionType,
51 dominator_tree: DominatorTree<BasicBlock>,
52}
53
54impl Cfg {
55 pub(crate) fn new(
56 name: String,
57 constants: UsefulConstants,
58 definition_type: DefinitionType,
59 parameters: Parameters,
60 declarations: Declarations,
61 basic_blocks: Vec<BasicBlock>,
62 dominator_tree: DominatorTree<BasicBlock>,
63 ) -> Cfg {
64 Cfg {
65 name,
66 constants,
67 parameters,
68 declarations,
69 basic_blocks,
70 definition_type,
71 dominator_tree,
72 }
73 }
74 #[must_use]
76 pub fn entry_block(&self) -> &BasicBlock {
77 &self.basic_blocks[Index::default()]
78 }
79
80 #[must_use]
81 pub fn get_basic_block(&self, index: Index) -> Option<&BasicBlock> {
82 self.basic_blocks.get(index)
83 }
84
85 #[must_use]
87 pub fn len(&self) -> usize {
88 self.basic_blocks.len()
89 }
90
91 #[must_use]
93 pub fn is_empty(&self) -> bool {
94 self.basic_blocks.is_empty()
95 }
96
97 pub fn into_ssa(mut self) -> SSAResult<Cfg> {
99 debug!("converting `{}` CFG to SSA", self.name());
100
101 let mut env = Environment::new(self.parameters(), self.declarations());
103 insert_phi_statements::<Config>(&mut self.basic_blocks, &self.dominator_tree, &mut env);
104 insert_ssa_variables::<Config>(&mut self.basic_blocks, &self.dominator_tree, &mut env)?;
105
106 for name in self.parameters.iter_mut() {
108 *name = name.with_version(0);
109 }
110
111 self.declarations =
113 ssa_impl::update_declarations(&mut self.basic_blocks, &self.parameters, &env);
114
115 self.propagate_types();
119 self.propagate_values();
120 self.propagate_degrees();
121 self.cache_variable_use();
122
123 for basic_block in self.basic_blocks.iter() {
125 debug!(
126 "basic block {}: (predecessors: {:?}, successors: {:?})",
127 basic_block.index(),
128 basic_block.predecessors(),
129 basic_block.successors(),
130 );
131 for stmt in basic_block.iter() {
132 debug!(" {stmt:?}")
133 }
134 }
135 Ok(self)
136 }
137
138 #[must_use]
140 pub fn name(&self) -> &str {
141 &self.name
142 }
143
144 #[must_use]
146 pub fn file_id(&self) -> &Option<FileID> {
147 self.parameters.file_id()
148 }
149
150 #[must_use]
151 pub fn definition_type(&self) -> &DefinitionType {
152 &self.definition_type
153 }
154
155 #[must_use]
156 pub fn constants(&self) -> &UsefulConstants {
157 &self.constants
158 }
159
160 #[must_use]
162 pub fn parameters(&self) -> &Parameters {
163 &self.parameters
164 }
165
166 #[must_use]
168 pub fn declarations(&self) -> &Declarations {
169 &self.declarations
170 }
171
172 pub fn variables(&self) -> impl Iterator<Item = &VariableName> {
174 self.declarations.iter().map(|(name, _)| name)
175 }
176
177 pub fn input_signals(&self) -> impl Iterator<Item = &VariableName> {
179 use SignalType::*;
180 use VariableType::*;
181 self.declarations.iter().filter_map(|(name, declaration)| {
182 match declaration.variable_type() {
183 Signal(Input, _) => Some(name),
184 _ => None,
185 }
186 })
187 }
188
189 pub fn output_signals(&self) -> impl Iterator<Item = &VariableName> {
191 use SignalType::*;
192 use VariableType::*;
193 self.declarations.iter().filter_map(|(name, declaration)| {
194 match declaration.variable_type() {
195 Signal(Output, _) => Some(name),
196 _ => None,
197 }
198 })
199 }
200
201 #[must_use]
203 pub fn get_declaration(&self, name: &VariableName) -> Option<&Declaration> {
204 self.declarations.get_declaration(name)
205 }
206
207 #[must_use]
209 pub fn get_type(&self, name: &VariableName) -> Option<&VariableType> {
210 self.declarations.get_type(name)
211 }
212
213 pub fn iter(&self) -> impl Iterator<Item = &BasicBlock> {
216 self.basic_blocks.iter()
217 }
218
219 pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut BasicBlock> {
221 self.basic_blocks.iter_mut()
222 }
223
224 #[must_use]
228 pub fn get_dominators(&self, basic_block: &BasicBlock) -> Vec<&BasicBlock> {
229 self.dominator_tree
230 .get_dominators(basic_block.index())
231 .iter()
232 .map(|&i| &self.basic_blocks[i])
233 .collect()
234 }
235
236 #[must_use]
239 pub fn get_immediate_dominator(&self, basic_block: &BasicBlock) -> Option<&BasicBlock> {
240 self.dominator_tree
241 .get_immediate_dominator(basic_block.index())
242 .map(|i| &self.basic_blocks[i])
243 }
244
245 #[must_use]
248 pub fn get_dominator_successors(&self, basic_block: &BasicBlock) -> Vec<&BasicBlock> {
249 self.dominator_tree
250 .get_dominator_successors(basic_block.index())
251 .iter()
252 .map(|&i| &self.basic_blocks[i])
253 .collect()
254 }
255
256 #[must_use]
261 pub fn get_dominance_frontier(&self, basic_block: &BasicBlock) -> Vec<&BasicBlock> {
262 self.dominator_tree
263 .get_dominance_frontier(basic_block.index())
264 .iter()
265 .map(|&i| &self.basic_blocks[i])
266 .collect()
267 }
268
269 pub fn get_predecessors(&self, basic_block: &BasicBlock) -> Vec<&BasicBlock> {
271 let mut predecessors = HashSet::new();
272 let mut update = HashSet::from([basic_block.index()]);
273 while !update.is_subset(&predecessors) {
274 predecessors.extend(update.iter().cloned());
275 update = update
276 .iter()
277 .flat_map(|index| {
278 self.get_basic_block(*index)
279 .expect("in control-flow graph")
280 .predecessors()
281 .iter()
282 .cloned()
283 })
284 .collect();
285 }
286 predecessors.remove(&basic_block.index());
288 predecessors
289 .iter()
290 .map(|index| self.get_basic_block(*index).expect("in control-flow graph"))
291 .collect::<Vec<_>>()
292 }
293
294 pub fn get_successors(&self, basic_block: &BasicBlock) -> Vec<&BasicBlock> {
296 let mut successors = HashSet::new();
297 let mut update = HashSet::from([basic_block.index()]);
298 while !update.is_subset(&successors) {
299 successors.extend(update.iter().cloned());
300 update = update
301 .iter()
302 .flat_map(|index| {
303 self.get_basic_block(*index)
304 .expect("in control-flow graph")
305 .successors()
306 .iter()
307 .cloned()
308 })
309 .collect();
310 }
311 successors.remove(&basic_block.index());
313 successors
314 .iter()
315 .map(|index| self.get_basic_block(*index).expect("in control-flow graph"))
316 .collect::<Vec<_>>()
317 }
318
319 pub fn get_interval(
323 &self,
324 start_block: &BasicBlock,
325 end_block: &BasicBlock,
326 ) -> Vec<&BasicBlock> {
327 let mut successors = HashSet::new();
329 let mut update = HashSet::from([start_block.index()]);
330 while !update.is_subset(&successors) {
331 successors.extend(update.iter().cloned());
332 update = update
333 .iter()
334 .flat_map(|index| {
335 self.get_basic_block(*index)
336 .expect("in control-flow graph")
337 .successors()
338 .iter()
339 .cloned()
340 })
341 .collect();
342 }
343
344 let mut predecessors = HashSet::new();
346 let mut update = HashSet::from([end_block.index()]);
347 while !update.is_subset(&predecessors) {
348 predecessors.extend(update.iter().cloned());
349 update = update
350 .iter()
351 .flat_map(|index| {
352 self.get_basic_block(*index)
353 .expect("in control-flow graph")
354 .predecessors()
355 .iter()
356 .cloned()
357 })
358 .collect();
359 }
360 predecessors.remove(&end_block.index());
361
362 successors
365 .intersection(&predecessors)
366 .map(|index| self.get_basic_block(*index).expect("in control-flow graph"))
367 .collect::<Vec<_>>()
368 }
369
370 pub fn get_true_branch(&self, header_block: &BasicBlock) -> Vec<&BasicBlock> {
377 use crate::ir::Statement::*;
378 if let Some(IfThenElse { true_index, .. }) = header_block.statements().last() {
379 let start_block = self.get_basic_block(*true_index).expect("in control-flow graph");
380 let end_blocks = self.get_dominance_frontier(start_block);
381
382 if end_blocks.is_empty() {
383 let mut result = self.get_successors(start_block);
385 result.push(start_block);
386 result
387 } else {
388 let mut result = Vec::new();
390 for end_block in end_blocks {
391 result.extend(self.get_interval(start_block, end_block))
392 }
393 result
394 }
395 } else {
396 panic!("the given header block does not end with an if-statement");
397 }
398 }
399
400 pub fn get_false_branch(&self, header_block: &BasicBlock) -> Vec<&BasicBlock> {
407 use crate::ir::Statement::*;
408 if let Some(IfThenElse { true_index, false_index, .. }) = header_block.statements().last() {
409 if let Some(false_index) = false_index {
410 if self.dominator_tree.get_dominance_frontier(*true_index).contains(false_index) {
411 return Vec::new();
413 }
414 let start_block =
415 self.get_basic_block(*false_index).expect("in control-flow graph");
416 let end_blocks = self.get_dominance_frontier(start_block);
417
418 if end_blocks.is_empty() {
419 let mut result = self.get_successors(start_block);
421 result.push(start_block);
422 result
423 } else {
424 let mut result = Vec::new();
426 for end_block in end_blocks {
427 result.extend(self.get_interval(start_block, end_block))
428 }
429 result
430 }
431 } else {
432 Vec::new()
433 }
434 } else {
435 panic!("the given header block does not end with an if-statement");
436 }
437 }
438
439 pub(crate) fn cache_variable_use(&mut self) {
441 debug!("computing variable use for `{}`", self.name());
442 for basic_block in self.iter_mut() {
443 basic_block.cache_variable_use();
444 }
445 }
446
447 pub(crate) fn propagate_degrees(&mut self) {
449 use Degree::*;
450 debug!("propagating expression degrees for `{}`", self.name());
451 let mut env = DegreeEnvironment::new();
452 for param in self.parameters().iter() {
453 env.set_type(param, &VariableType::Local);
454 if matches!(self.definition_type(), DefinitionType::Function) {
455 let range = DegreeRange::new(Constant, Linear);
457 env.set_degree(param, &range);
458 } else {
459 env.set_degree(param, &Constant.into());
461 }
462 }
463 let mut rerun = true;
464 let start = Instant::now();
465 while rerun {
466 rerun = false;
468 for basic_block in self.iter_mut() {
469 rerun = rerun || basic_block.propagate_degrees(&mut env);
470 }
471 if start.elapsed() > MAX_ANALYSIS_DURATION {
473 debug!("failed to propagate degrees within allotted time");
474 rerun = false;
475 }
476 }
477 }
478
479 pub(crate) fn propagate_values(&mut self) {
481 debug!("propagating constant values for `{}`", self.name());
482 let mut env = ValueEnvironment::new(&self.constants);
483 let mut rerun = true;
484 let start = Instant::now();
485 while rerun {
486 rerun = false;
488 for basic_block in self.iter_mut() {
489 rerun = rerun || basic_block.propagate_values(&mut env);
490 }
491 if start.elapsed() > MAX_ANALYSIS_DURATION {
493 debug!("failed to propagate values within allotted time");
494 rerun = false;
495 }
496 }
497 }
498
499 pub(crate) fn propagate_types(&mut self) {
501 debug!("propagating variable types for `{}`", self.name());
502 let declarations = self.declarations.clone();
505 for basic_block in self.iter_mut() {
506 basic_block.propagate_types(&declarations);
507 }
508 }
509}
510
511impl fmt::Debug for Cfg {
512 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
513 for basic_block in self.iter() {
514 writeln!(
515 f,
516 "basic block {}, predecessors: {:?}, successors: {:?}",
517 basic_block.index(),
518 basic_block.predecessors(),
519 basic_block.successors(),
520 )?;
521 write!(f, "{basic_block:?}")?;
522 }
523 Ok(())
524 }
525}