From 9d917e46126e3527486bcf24f0d0c4512543ff2b Mon Sep 17 00:00:00 2001
From: Maxim Vezenov <mvezenov@gmail.com>
Date: Wed, 26 Feb 2025 00:39:52 +0000
Subject: [PATCH] Do not run opt passes on Brillig functions post Brillig gen

---
 compiler/noirc_evaluator/src/ssa.rs           |  2 +-
 .../src/ssa/opt/constant_folding.rs           |  5 +++++
 compiler/noirc_evaluator/src/ssa/opt/die.rs   | 21 ++++++++++++++-----
 .../src/ssa/opt/preprocess_fns.rs             |  2 +-
 4 files changed, 23 insertions(+), 7 deletions(-)

diff --git a/compiler/noirc_evaluator/src/ssa.rs b/compiler/noirc_evaluator/src/ssa.rs
index fce597d81bd..b3c509dc5cc 100644
--- a/compiler/noirc_evaluator/src/ssa.rs
+++ b/compiler/noirc_evaluator/src/ssa.rs
@@ -132,7 +132,7 @@ pub(crate) fn optimize_into_acir(
     // It could happen that we inlined all calls to a given brillig function.
     // In that case it's unused so we can remove it. This is what we check next.
     .run_pass(Ssa::remove_unreachable_functions, "Removing Unreachable Functions (4th)")
-    .run_pass(Ssa::dead_instruction_elimination, "Dead Instruction Elimination (3rd)")
+    .run_pass(Ssa::dead_instruction_elimination_acir, "Dead Instruction Elimination (3rd)")
     .finish();
 
     if !options.skip_underconstrained_check {
diff --git a/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs b/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs
index 05bd48b8830..373d99994a1 100644
--- a/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs
+++ b/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs
@@ -95,6 +95,11 @@ impl Ssa {
         let brillig_info = Some(BrilligInfo { brillig, brillig_functions: &brillig_functions });
 
         for function in self.functions.values_mut() {
+            // We have already performed our final Brillig generation, so constant folding
+            // Brillig functions is unnecessary work.
+            if function.dfg.runtime().is_brillig() {
+                continue;
+            }
             function.constant_fold(false, brillig_info);
         }
 
diff --git a/compiler/noirc_evaluator/src/ssa/opt/die.rs b/compiler/noirc_evaluator/src/ssa/opt/die.rs
index d23cfee8a14..c45e1886269 100644
--- a/compiler/noirc_evaluator/src/ssa/opt/die.rs
+++ b/compiler/noirc_evaluator/src/ssa/opt/die.rs
@@ -26,15 +26,21 @@ impl Ssa {
     /// This step should come after the flattening of the CFG and mem2reg.
     #[tracing::instrument(level = "trace", skip(self))]
     pub(crate) fn dead_instruction_elimination(self) -> Ssa {
-        self.dead_instruction_elimination_inner(true)
+        self.dead_instruction_elimination_inner(true, false)
     }
 
-    fn dead_instruction_elimination_inner(mut self, flattened: bool) -> Ssa {
+    /// Post the Brillig generation we do not need to run this pass on Brillig functions.
+    #[tracing::instrument(level = "trace", skip(self))]
+    pub(crate) fn dead_instruction_elimination_acir(self) -> Ssa {
+        self.dead_instruction_elimination_inner(true, true)
+    }
+
+    fn dead_instruction_elimination_inner(mut self, flattened: bool, skip_brillig: bool) -> Ssa {
         let mut used_globals_map: HashMap<_, _> = self
             .functions
             .par_iter_mut()
             .filter_map(|(id, func)| {
-                let set = func.dead_instruction_elimination(true, flattened);
+                let set = func.dead_instruction_elimination(true, flattened, skip_brillig);
                 if func.runtime().is_brillig() {
                     Some((*id, set))
                 } else {
@@ -79,7 +85,12 @@ impl Function {
         &mut self,
         insert_out_of_bounds_checks: bool,
         flattened: bool,
+        skip_brillig: bool,
     ) -> HashSet<ValueId> {
+        if skip_brillig && self.dfg.runtime().is_brillig() {
+            return HashSet::default();
+        }
+
         let mut context = Context { flattened, ..Default::default() };
 
         context.mark_function_parameter_arrays_as_used(self);
@@ -103,7 +114,7 @@ impl Function {
         // instructions (we don't want to remove those checks, or instructions that are
         // dependencies of those checks)
         if inserted_out_of_bounds_checks {
-            return self.dead_instruction_elimination(false, flattened);
+            return self.dead_instruction_elimination(false, flattened, skip_brillig);
         }
 
         context.remove_rc_instructions(&mut self.dfg);
@@ -1099,7 +1110,7 @@ mod test {
         let ssa = Ssa::from_str(src).unwrap();
 
         // Even though these ACIR functions only have 1 block, we have not inlined and flattened anything yet.
-        let ssa = ssa.dead_instruction_elimination_inner(false);
+        let ssa = ssa.dead_instruction_elimination_inner(false, false);
 
         let expected = "
           acir(inline) fn main f0 {
diff --git a/compiler/noirc_evaluator/src/ssa/opt/preprocess_fns.rs b/compiler/noirc_evaluator/src/ssa/opt/preprocess_fns.rs
index 764fb6dd65b..c4f09ac94fe 100644
--- a/compiler/noirc_evaluator/src/ssa/opt/preprocess_fns.rs
+++ b/compiler/noirc_evaluator/src/ssa/opt/preprocess_fns.rs
@@ -59,7 +59,7 @@ impl Ssa {
             // Try to reduce the number of blocks.
             function.simplify_function();
             // Remove leftover instructions.
-            function.dead_instruction_elimination(true, false);
+            function.dead_instruction_elimination(true, false, false);
 
             // Put it back into the SSA, so the next functions can pick it up.
             self.functions.insert(id, function);