use acir::circuit::{Circuit, Opcode};
mod general;
mod redundant_range;
mod unused_memory;
pub(crate) use general::GeneralOptimizer;
pub(crate) use redundant_range::RangeOptimizer;
use tracing::info;
use self::unused_memory::UnusedMemoryOptimizer;
use super::{transform_assert_messages, AcirTransformationMap};
pub fn optimize(acir: Circuit) -> (Circuit, AcirTransformationMap) {
let (mut acir, new_opcode_positions) = optimize_internal(acir);
let transformation_map = AcirTransformationMap::new(new_opcode_positions);
acir.assert_messages = transform_assert_messages(acir.assert_messages, &transformation_map);
(acir, transformation_map)
}
#[tracing::instrument(level = "trace", name = "optimize_acir" skip(acir))]
pub(super) fn optimize_internal(acir: Circuit) -> (Circuit, Vec<usize>) {
let acir_opcode_positions = (0..acir.opcodes.len()).collect();
if acir.opcodes.len() == 1 && matches!(acir.opcodes[0], Opcode::BrilligCall { .. }) {
info!("Program is fully unconstrained, skipping optimization pass");
return (acir, acir_opcode_positions);
}
info!("Number of opcodes before: {}", acir.opcodes.len());
let opcodes: Vec<Opcode> = acir
.opcodes
.into_iter()
.map(|opcode| {
if let Opcode::AssertZero(arith_expr) = opcode {
Opcode::AssertZero(GeneralOptimizer::optimize(arith_expr))
} else {
opcode
}
})
.collect();
let acir = Circuit { opcodes, ..acir };
let memory_optimizer = UnusedMemoryOptimizer::new(acir);
let (acir, acir_opcode_positions) =
memory_optimizer.remove_unused_memory_initializations(acir_opcode_positions);
let range_optimizer = RangeOptimizer::new(acir);
let (acir, acir_opcode_positions) =
range_optimizer.replace_redundant_ranges(acir_opcode_positions);
info!("Number of opcodes after: {}", acir.opcodes.len());
(acir, acir_opcode_positions)
}