diff --git a/compiler/noirc_evaluator/src/ssa.rs b/compiler/noirc_evaluator/src/ssa.rs index 0e3076923e..e2da5652fa 100644 --- a/compiler/noirc_evaluator/src/ssa.rs +++ b/compiler/noirc_evaluator/src/ssa.rs @@ -63,6 +63,7 @@ pub(crate) fn optimize_into_acir( .run_pass(Ssa::mem2reg, "After Mem2Reg:") .run_pass(Ssa::fold_constants, "After Constant Folding:") .run_pass(Ssa::dead_instruction_elimination, "After Dead Instruction Elimination:") + .run_pass(Ssa::bubble_up_constrains, "After Constraint Bubbling:") .finish(); let brillig = ssa.to_brillig(print_brillig_trace); diff --git a/compiler/noirc_evaluator/src/ssa/opt/bubble_up_constrains.rs b/compiler/noirc_evaluator/src/ssa/opt/bubble_up_constrains.rs new file mode 100644 index 0000000000..e0144005a0 --- /dev/null +++ b/compiler/noirc_evaluator/src/ssa/opt/bubble_up_constrains.rs @@ -0,0 +1,43 @@ +use crate::ssa::{ir::instruction::Instruction, ssa_gen::Ssa}; + +impl Ssa { + /// A simple SSA pass to go through each instruction and move every `Instruction::Constrain` to immediately + /// after when all of its inputs are available. + #[tracing::instrument(level = "trace", skip(self))] + pub(crate) fn bubble_up_constrains(mut self) -> Ssa { + for function in self.functions.values_mut() { + for block in function.reachable_blocks() { + let instructions = function.dfg[block].take_instructions(); + let mut filtered_instructions = Vec::with_capacity(instructions.len()); + + let dfg = &function.dfg; + for instruction in instructions { + let (lhs, rhs) = match dfg[instruction] { + Instruction::Constrain(lhs, rhs, ..) => (lhs, rhs), + _ => { + filtered_instructions.push(instruction); + continue; + } + }; + + let index = filtered_instructions + .iter() + .rev() + .position(|instruction_id| { + let results = dfg.instruction_results(*instruction_id).to_vec(); + results.contains(&lhs) || results.contains(&rhs) + }) + // We iterate through the previous instructions in reverse order so the index is from the + // back of the vector. Subtract from vector length to get correct index. + .map(|reversed_index| filtered_instructions.len() - reversed_index) + .unwrap_or(filtered_instructions.len()); + + filtered_instructions.insert(index, instruction); + } + + *function.dfg[block].instructions_mut() = filtered_instructions; + } + } + self + } +} diff --git a/compiler/noirc_evaluator/src/ssa/opt/mod.rs b/compiler/noirc_evaluator/src/ssa/opt/mod.rs index 4d003c0594..71725422a7 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/mod.rs @@ -5,6 +5,7 @@ //! Generally, these passes are also expected to minimize the final amount of instructions. mod array_use; mod assert_constant; +mod bubble_up_constrains; mod constant_folding; mod defunctionalize; mod die;