From 7ecb75417b9fa04e328ed52563ae1dd9cb315f8a Mon Sep 17 00:00:00 2001 From: guipublic Date: Fri, 13 Sep 2024 10:20:44 +0000 Subject: [PATCH] disable side-effects for no_predicates functions --- .../src/ssa/opt/flatten_cfg.rs | 53 ++++++++++++++++--- .../Nargo.toml | 5 ++ .../Prover.toml | 2 + .../src/main.nr | 25 +++++++++ 4 files changed, 77 insertions(+), 8 deletions(-) create mode 100644 test_programs/execution_success/regression_unsafe_no_predicates/Nargo.toml create mode 100644 test_programs/execution_success/regression_unsafe_no_predicates/Prover.toml create mode 100644 test_programs/execution_success/regression_unsafe_no_predicates/src/main.nr diff --git a/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs b/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs index d5fb98c7adc..613a5df4063 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs @@ -142,7 +142,7 @@ use crate::ssa::{ basic_block::BasicBlockId, cfg::ControlFlowGraph, dfg::{CallStack, InsertInstructionResult}, - function::Function, + function::{Function, FunctionId}, function_inserter::FunctionInserter, instruction::{BinaryOp, Instruction, InstructionId, Intrinsic, TerminatorInstruction}, types::Type, @@ -164,8 +164,14 @@ impl Ssa { /// For more information, see the module-level comment at the top of this file. #[tracing::instrument(level = "trace", skip(self))] pub(crate) fn flatten_cfg(mut self) -> Ssa { + // Retrieve the 'no_predicates' attribute of the functions in a map, to avoid problems with borrowing + let mut no_predicates = HashMap::default(); + for function in self.functions.values() { + no_predicates.insert(function.id(), function.is_no_predicates()); + } + for function in self.functions.values_mut() { - flatten_function_cfg(function); + flatten_function_cfg(function, &no_predicates); } self } @@ -244,7 +250,7 @@ struct ConditionalContext { call_stack: CallStack, } -fn flatten_function_cfg(function: &mut Function) { +fn flatten_function_cfg(function: &mut Function, no_predicates: &HashMap) { // This pass may run forever on a brillig function. // Analyze will check if the predecessors have been processed and push the block to the back of // the queue. This loops forever if there are still any loops present in the program. @@ -264,18 +270,18 @@ fn flatten_function_cfg(function: &mut Function) { condition_stack: Vec::new(), arguments_stack: Vec::new(), }; - context.flatten(); + context.flatten(no_predicates); } impl<'f> Context<'f> { - fn flatten(&mut self) { + fn flatten(&mut self, no_predicates: &HashMap) { // Flatten the CFG by inlining all instructions from the queued blocks // until all blocks have been flattened. // We follow the terminator of each block to determine which blocks to // process next let mut queue = vec![self.inserter.function.entry_block()]; while let Some(block) = queue.pop() { - self.inline_block(block); + self.inline_block(block, no_predicates); let to_process = self.handle_terminator(block, &queue); for incoming_block in to_process { if !queue.contains(&incoming_block) { @@ -307,8 +313,23 @@ impl<'f> Context<'f> { }) } + /// Use the provided map to say if the instruction is a call to a no_predicates function + fn is_no_predicate( + &self, + no_predicates: &HashMap, + instruction: &InstructionId, + ) -> bool { + let mut result = false; + if let Instruction::Call { func, .. } = self.inserter.function.dfg[*instruction] { + if let Value::Function(fid) = self.inserter.function.dfg[func] { + result = *no_predicates.get(&fid).unwrap_or(&false); + } + } + result + } + // Inline all instructions from the given block into the entry block, and track slice capacities - fn inline_block(&mut self, block: BasicBlockId) { + fn inline_block(&mut self, block: BasicBlockId, no_predicates: &HashMap) { if self.inserter.function.entry_block() == block { // we do not inline the entry block into itself // for the outer block before we start inlining @@ -322,7 +343,23 @@ impl<'f> Context<'f> { // unnecessary, when removing it actually causes an aliasing/mutability error. let instructions = self.inserter.function.dfg[block].instructions().to_vec(); for instruction in instructions.iter() { - self.push_instruction(*instruction); + if self.is_no_predicate(no_predicates, instruction) { + // disable side effect for no_predicate functions + let one = self + .inserter + .function + .dfg + .make_constant(FieldElement::one(), Type::unsigned(1)); + self.insert_instruction_with_typevars( + Instruction::EnableSideEffectsIf { condition: one }, + None, + im::Vector::new(), + ); + self.push_instruction(*instruction); + self.insert_current_side_effects_enabled(); + } else { + self.push_instruction(*instruction); + } } } diff --git a/test_programs/execution_success/regression_unsafe_no_predicates/Nargo.toml b/test_programs/execution_success/regression_unsafe_no_predicates/Nargo.toml new file mode 100644 index 00000000000..7cd42687c34 --- /dev/null +++ b/test_programs/execution_success/regression_unsafe_no_predicates/Nargo.toml @@ -0,0 +1,5 @@ +[package] +name = "regression_unsafe_no_predicates" +type = "bin" +authors = [""] +[dependencies] diff --git a/test_programs/execution_success/regression_unsafe_no_predicates/Prover.toml b/test_programs/execution_success/regression_unsafe_no_predicates/Prover.toml new file mode 100644 index 00000000000..aaa42715899 --- /dev/null +++ b/test_programs/execution_success/regression_unsafe_no_predicates/Prover.toml @@ -0,0 +1,2 @@ +x = 239 +nest = false \ No newline at end of file diff --git a/test_programs/execution_success/regression_unsafe_no_predicates/src/main.nr b/test_programs/execution_success/regression_unsafe_no_predicates/src/main.nr new file mode 100644 index 00000000000..63c2493fc89 --- /dev/null +++ b/test_programs/execution_success/regression_unsafe_no_predicates/src/main.nr @@ -0,0 +1,25 @@ +fn main(x: u8, nest: bool) { + if nest { + let foo = unsafe_assert([x]); + assert(foo != 0); + } +} + +#[no_predicates] +pub fn unsafe_assert(msg: [u8; N]) -> u8 { + let block = unsafe { + get_block(msg) + }; + verify_block(msg, block); + block[0] +} + +unconstrained fn get_block(msg: [u8; N]) -> [u8; 2] { + let mut block: [u8; 2] = [0; 2]; + block[0] = msg[0]; + block +} + +fn verify_block(msg: [u8; N], block: [u8; 2]) { + assert_eq(block[0], msg[0]); +}