acvm/compiler/optimizers/
mod.rs1use std::collections::BTreeMap;
2
3use acir::{
4 AcirField,
5 circuit::{Circuit, Opcode, brillig::BrilligFunctionId},
6};
7
8mod common_subexpression;
9mod general;
10mod redundant_range;
11mod unused_memory;
12
13pub(crate) use general::GeneralOptimizer;
14pub(crate) use redundant_range::RangeOptimizer;
15use tracing::info;
16
17use self::unused_memory::UnusedMemoryOptimizer;
18
19use super::{AcirTransformationMap, transform_assert_messages};
20
21pub fn optimize<F: AcirField>(
23 acir: Circuit<F>,
24 brillig_side_effects: &BTreeMap<BrilligFunctionId, bool>,
25) -> (Circuit<F>, AcirTransformationMap) {
26 let acir_opcode_positions = (0..acir.opcodes.len()).collect();
31
32 let (mut acir, new_opcode_positions) =
37 optimize_internal(acir, acir_opcode_positions, brillig_side_effects);
38
39 let transformation_map = AcirTransformationMap::new(&new_opcode_positions);
40
41 acir.assert_messages = transform_assert_messages(acir.assert_messages, &transformation_map);
42
43 (acir, transformation_map)
44}
45
46#[tracing::instrument(level = "trace", name = "optimize_acir" skip(acir, acir_opcode_positions))]
54pub(super) fn optimize_internal<F: AcirField>(
55 acir: Circuit<F>,
56 acir_opcode_positions: Vec<usize>,
57 brillig_side_effects: &BTreeMap<BrilligFunctionId, bool>,
58) -> (Circuit<F>, Vec<usize>) {
59 if acir.opcodes.len() == 1 && matches!(acir.opcodes[0], Opcode::BrilligCall { .. }) {
60 info!("Program is fully unconstrained, skipping optimization pass");
61 return (acir, acir_opcode_positions);
62 }
63
64 info!("Number of opcodes before: {}", acir.opcodes.len());
65
66 let (opcodes, acir_opcode_positions): (Vec<_>, Vec<_>) = acir
68 .opcodes
69 .into_iter()
70 .zip(acir_opcode_positions)
71 .filter_map(|(opcode, position)| {
72 if let Opcode::AssertZero(arith_expr) = opcode {
73 let optimized = GeneralOptimizer::optimize(arith_expr);
74 if optimized.is_zero() {
75 return None;
76 }
77 Some((Opcode::AssertZero(optimized), position))
78 } else {
79 Some((opcode, position))
80 }
81 })
82 .unzip();
83 let acir = Circuit { opcodes, ..acir };
84
85 let memory_optimizer = UnusedMemoryOptimizer::new(acir);
87 let (acir, acir_opcode_positions) =
88 memory_optimizer.remove_unused_memory_initializations(acir_opcode_positions);
89
90 let range_optimizer = RangeOptimizer::new(acir, brillig_side_effects);
92 let (acir, acir_opcode_positions) =
93 range_optimizer.replace_redundant_ranges(acir_opcode_positions);
94
95 let max_transformer_passes_or_default = None;
96 let (acir, acir_opcode_positions, _opcodes_hash_stabilized) =
97 common_subexpression::transform_internal(
98 acir,
99 acir_opcode_positions,
100 brillig_side_effects,
101 max_transformer_passes_or_default,
102 );
103
104 info!("Number of opcodes after: {}", acir.opcodes.len());
105
106 (acir, acir_opcode_positions)
107}
108
109#[cfg(test)]
110mod tests {
111 use acir::{FieldElement, circuit::Circuit};
112 use std::collections::BTreeMap;
113
114 use crate::{assert_circuit_snapshot, compiler::optimizers::optimize_internal};
115
116 #[test]
117 fn removes_empty_assert_zero_opcodes() {
118 let src = "
119 private parameters: [w0, w1]
120 public parameters: []
121 return values: []
122 ASSERT w0*w1 - w1*w0 = 0
123 ";
124 let circuit = Circuit::<FieldElement>::from_str(src).unwrap();
125 let acir_opcode_positions = (0..circuit.opcodes.len()).collect();
126 let (optimized, _) = optimize_internal(circuit, acir_opcode_positions, &BTreeMap::new());
127 assert_circuit_snapshot!(optimized, @r"
128 private parameters: [w0, w1]
129 public parameters: []
130 return values: []
131 ");
132 }
133}