From 504aad0a43fb31fd51596b7caa907611022d59da Mon Sep 17 00:00:00 2001 From: vezenovm Date: Wed, 9 Aug 2023 21:11:31 +0000 Subject: [PATCH 01/62] cleanup load instruction search w/ predecessors methods --- .../execution_success/references/src/main.nr | 18 ++ crates/noirc_evaluator/src/ssa.rs | 4 + crates/noirc_evaluator/src/ssa/ir/function.rs | 6 +- .../src/ssa/ir/instruction/call.rs | 1 + crates/noirc_evaluator/src/ssa/opt/mem2reg.rs | 269 ++++++++++++++---- .../noirc_evaluator/src/ssa/opt/unrolling.rs | 10 +- .../src/monomorphization/mod.rs | 9 +- 7 files changed, 239 insertions(+), 78 deletions(-) diff --git a/crates/nargo_cli/tests/execution_success/references/src/main.nr b/crates/nargo_cli/tests/execution_success/references/src/main.nr index aeed8e0b901..96b94c6873f 100644 --- a/crates/nargo_cli/tests/execution_success/references/src/main.nr +++ b/crates/nargo_cli/tests/execution_success/references/src/main.nr @@ -1,4 +1,6 @@ fn main(mut x: Field) { + regression_2218(x, 10); + add1(&mut x); assert(x == 3); @@ -97,3 +99,19 @@ fn regression_2030() { let _ = *array[0]; *array[0] = 1; } + +fn regression_2218(x: Field, y: Field) { + let q = &mut &mut 0; + let q1 = *q; + let q2 = *q; + + if x != y { + // Make sure that we correct load reference aliases through multiple blocks + if x != 20 { + *q1 = 1; + *q2 = 2; // now we'd expect q1 == q2 == 2 + + assert(*q1 == 2); + } + } +} diff --git a/crates/noirc_evaluator/src/ssa.rs b/crates/noirc_evaluator/src/ssa.rs index 81982e463a0..e689241f9d0 100644 --- a/crates/noirc_evaluator/src/ssa.rs +++ b/crates/noirc_evaluator/src/ssa.rs @@ -49,12 +49,16 @@ pub(crate) fn optimize_into_acir( ssa = ssa .inline_functions() .print(print_ssa_passes, "After Inlining:") + // Run mem2reg with the CFG separated into blocks + .mem2reg() + .print(print_ssa_passes, "After Mem2Reg:") .unroll_loops() .print(print_ssa_passes, "After Unrolling:") .simplify_cfg() .print(print_ssa_passes, "After Simplifying:") .flatten_cfg() .print(print_ssa_passes, "After Flattening:") + // Run mem2reg once more with the flattened CFG to catch any remaining loads/stores .mem2reg() .print(print_ssa_passes, "After Mem2Reg:") .fold_constants() diff --git a/crates/noirc_evaluator/src/ssa/ir/function.rs b/crates/noirc_evaluator/src/ssa/ir/function.rs index ab04c1497bd..ab97d418783 100644 --- a/crates/noirc_evaluator/src/ssa/ir/function.rs +++ b/crates/noirc_evaluator/src/ssa/ir/function.rs @@ -1,4 +1,4 @@ -use std::collections::HashSet; +use std::collections::BTreeSet; use iter_extended::vecmap; @@ -107,8 +107,8 @@ impl Function { /// Note that self.dfg.basic_blocks_iter() iterates over all blocks, /// whether reachable or not. This function should be used if you /// want to iterate only reachable blocks. - pub(crate) fn reachable_blocks(&self) -> HashSet { - let mut blocks = HashSet::new(); + pub(crate) fn reachable_blocks(&self) -> BTreeSet { + let mut blocks = BTreeSet::new(); let mut stack = vec![self.entry_block]; while let Some(block) = stack.pop() { diff --git a/crates/noirc_evaluator/src/ssa/ir/instruction/call.rs b/crates/noirc_evaluator/src/ssa/ir/instruction/call.rs index a28f2d88bcb..18d45228d55 100644 --- a/crates/noirc_evaluator/src/ssa/ir/instruction/call.rs +++ b/crates/noirc_evaluator/src/ssa/ir/instruction/call.rs @@ -110,6 +110,7 @@ pub(super) fn simplify_call( } } Intrinsic::SlicePopFront => { + dbg!(arguments[0]); let slice = dfg.get_array_constant(arguments[0]); if let Some((mut slice, typ)) = slice { let element_count = typ.element_size(); diff --git a/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs b/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs index b9e849bb77c..91bc1f3069a 100644 --- a/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs +++ b/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs @@ -3,37 +3,57 @@ //! mutable variables into values that are easier to manipulate. use std::collections::{BTreeMap, HashMap, HashSet}; -use iter_extended::vecmap; - use crate::ssa::{ ir::{ basic_block::BasicBlockId, + cfg::ControlFlowGraph, dfg::DataFlowGraph, + dom::DominatorTree, + function::Function, instruction::{Instruction, InstructionId, TerminatorInstruction}, + post_order::PostOrder, value::{Value, ValueId}, }, ssa_gen::Ssa, }; +use super::unrolling::{find_all_loops, Loops}; + impl Ssa { /// Attempts to remove any load instructions that recover values that are already available in - /// scope, and attempts to remove store that are subsequently redundant, as long as they are - /// not stores on memory that will be passed into a function call or returned. + /// scope, and attempts to remove stores that are subsequently redundant. + /// As long as they are not stores on memory used inside of loops pub(crate) fn mem2reg(mut self) -> Ssa { for function in self.functions.values_mut() { let mut all_protected_allocations = HashSet::new(); - let contexts = vecmap(function.reachable_blocks(), |block| { - let mut context = PerBlockContext::new(block); - let allocations_protected_by_block = - context.analyze_allocations_and_eliminate_known_loads(&mut function.dfg); + + let mut context = PerFunctionContext::new(function); + + for block in function.reachable_blocks() { + let cfg: ControlFlowGraph = ControlFlowGraph::with_function(function); + + // Maps Load instruction id -> value to replace the result of the load with + let mut loads_to_substitute_per_block = BTreeMap::new(); + + // Maps Load result id -> value to replace the result of the load with + let mut load_values_to_substitute = BTreeMap::new(); + + let allocations_protected_by_block = context + .analyze_allocations_and_eliminate_known_loads( + &mut function.dfg, + &mut loads_to_substitute_per_block, + &mut load_values_to_substitute, + block, + &cfg, + ); all_protected_allocations.extend(allocations_protected_by_block.into_iter()); - context - }); + } + // Now that we have a comprehensive list of used allocations across all the // function's blocks, it is safe to remove any stores that do not touch such // allocations. - for context in contexts { - context.remove_unused_stores(&mut function.dfg, &all_protected_allocations); + for block in function.reachable_blocks() { + context.remove_unused_stores(&mut function.dfg, &all_protected_allocations, block); } } @@ -41,10 +61,27 @@ impl Ssa { } } -struct PerBlockContext { - block_id: BasicBlockId, - last_stores: BTreeMap, +struct PerFunctionContext { + last_stores_with_block: BTreeMap<(AllocId, BasicBlockId), ValueId>, + // Maps Load result id -> (value, block_id) + // Used to replace the result of a load with the appropriate block + load_values_to_substitute_per_func: BTreeMap, store_ids: Vec, + cfg: ControlFlowGraph, + post_order: PostOrder, + loops: Loops, +} +impl PerFunctionContext { + fn new(function: &Function) -> Self { + PerFunctionContext { + last_stores_with_block: BTreeMap::new(), + load_values_to_substitute_per_func: BTreeMap::new(), + store_ids: Vec::new(), + cfg: ControlFlowGraph::with_function(function), + post_order: PostOrder::with_function(function), + loops: find_all_loops(function), + } + } } /// An AllocId is the ValueId returned from an allocate instruction. E.g. v0 in v0 = allocate. @@ -52,50 +89,50 @@ struct PerBlockContext { /// an allocate instruction. type AllocId = ValueId; -impl PerBlockContext { - fn new(block_id: BasicBlockId) -> Self { - PerBlockContext { block_id, last_stores: BTreeMap::new(), store_ids: Vec::new() } - } - +impl PerFunctionContext { // Attempts to remove load instructions for which the result is already known from previous // store instructions to the same address in the same block. fn analyze_allocations_and_eliminate_known_loads( &mut self, dfg: &mut DataFlowGraph, + loads_to_substitute: &mut BTreeMap, + load_values_to_substitute_per_block: &mut BTreeMap, + block_id: BasicBlockId, + cfg: &ControlFlowGraph, ) -> HashSet { let mut protected_allocations = HashSet::new(); - let block = &dfg[self.block_id]; - - // Maps Load instruction id -> value to replace the result of the load with - let mut loads_to_substitute = HashMap::new(); - - // Maps Load result id -> value to replace the result of the load with - let mut load_values_to_substitute = HashMap::new(); + let block = &dfg[block_id]; for instruction_id in block.instructions() { match &dfg[*instruction_id] { Instruction::Store { mut address, value } => { - if let Some(value) = load_values_to_substitute.get(&address) { - address = *value; - } - - self.last_stores.insert(address, *value); + address = self.fetch_load_value_to_substitute_recursively( + cfg, + block_id, + address, + &mut HashSet::new(), + ); + + self.last_stores_with_block.insert((address, block_id), *value); self.store_ids.push(*instruction_id); } Instruction::Load { mut address } => { - if let Some(value) = load_values_to_substitute.get(&address) { - address = *value; - } - - if let Some(last_value) = self.last_stores.get(&address) { - let result_value = *dfg - .instruction_results(*instruction_id) - .first() - .expect("ICE: Load instructions should have single result"); - - loads_to_substitute.insert(*instruction_id, *last_value); - load_values_to_substitute.insert(result_value, *last_value); - } else { + address = self.fetch_load_value_to_substitute_recursively( + cfg, + block_id, + address, + &mut HashSet::new(), + ); + + let found_last_value = self.find_load_to_substitute( + block_id, + dfg, + address, + instruction_id, + loads_to_substitute, + load_values_to_substitute_per_block, + ); + if !found_last_value { protected_allocations.insert(address); } } @@ -122,21 +159,128 @@ impl PerBlockContext { } // Substitute load result values - for (result_value, new_value) in load_values_to_substitute { - let result_value = dfg.resolve(result_value); - dfg.set_value_from_id(result_value, new_value); + for (result_value, new_value) in load_values_to_substitute_per_block { + let result_value = dfg.resolve(*result_value); + dfg.set_value_from_id(result_value, *new_value); } // Delete load instructions // Even though we could let DIE handle this, doing it here makes the debug output easier // to read. - dfg[self.block_id] + dfg[block_id] .instructions_mut() .retain(|instruction| !loads_to_substitute.contains_key(instruction)); protected_allocations } + fn fetch_load_value_to_substitute_recursively( + &self, + cfg: &ControlFlowGraph, + block_id: BasicBlockId, + address: ValueId, + checked_blocks: &mut HashSet, + ) -> ValueId { + checked_blocks.insert(block_id); + + if let Some((value, load_block_id)) = self.load_values_to_substitute_per_func.get(&address) + { + if *load_block_id == block_id { + return *value; + } + } + + let mut dom_tree = DominatorTree::with_cfg_and_post_order(cfg, &self.post_order); + + let predecessors = cfg.predecessors(block_id); + for predecessor in predecessors { + if dom_tree.is_reachable(predecessor) && dom_tree.dominates(predecessor, block_id) { + if let Some((value, load_block_id)) = + self.load_values_to_substitute_per_func.get(&address) + { + if *load_block_id == predecessor { + return *value; + } + } + + if !checked_blocks.contains(&predecessor) { + return self.fetch_load_value_to_substitute_recursively( + cfg, + predecessor, + address, + checked_blocks, + ); + } + } + } + address + } + + fn find_load_to_substitute( + &mut self, + block_id: BasicBlockId, + dfg: &DataFlowGraph, + address: ValueId, + instruction_id: &InstructionId, + loads_to_substitute: &mut BTreeMap, + load_values_to_substitute_per_block: &mut BTreeMap, + ) -> bool { + let mut stack = vec![block_id]; + let mut visited = HashSet::new(); + + let mut dom_tree = DominatorTree::with_cfg_and_post_order(&self.cfg, &self.post_order); + while let Some(block) = stack.pop() { + visited.insert(block); + + for l in self.loops.yet_to_unroll.iter() { + // We do not want to substitute loads that take place within loops as this pass + // can occur before loop unrolling + if block == l.header { + return false; + } + } + + if let Some(last_value) = self.last_stores_with_block.get(&(address, block)) { + let result_value = *dfg + .instruction_results(*instruction_id) + .first() + .expect("ICE: Load instructions should have single result"); + + loads_to_substitute.insert(*instruction_id, *last_value); + load_values_to_substitute_per_block.insert(result_value, *last_value); + self.load_values_to_substitute_per_func.insert(result_value, (*last_value, block)); + return true; + } + + let predecessors = self.cfg.predecessors(block); + for predecessor in predecessors { + // TODO: Do I need is_reachable here? We are looping over only the reachable blocks but does + // that include a reachable block's predecessors? + if dom_tree.is_reachable(predecessor) && dom_tree.dominates(predecessor, block) { + if let Some(last_value) = + self.last_stores_with_block.get(&(address, predecessor)) + { + let result_value = *dfg + .instruction_results(*instruction_id) + .first() + .expect("ICE: Load instructions should have single result"); + + loads_to_substitute.insert(*instruction_id, *last_value); + load_values_to_substitute_per_block.insert(result_value, *last_value); + self.load_values_to_substitute_per_func + .insert(result_value, (*last_value, block)); + return true; + } + // Only recurse further if the predecessor dominates the current block we are checking + if !visited.contains(&predecessor) { + stack.push(predecessor); + } + } + } + } + false + } + /// Checks whether the given value id refers to an allocation. fn value_is_from_allocation(value: ValueId, dfg: &DataFlowGraph) -> bool { match &dfg[value] { @@ -150,9 +294,10 @@ impl PerBlockContext { /// Removes all store instructions identified during analysis that aren't present in the /// provided `protected_allocations` `HashSet`. fn remove_unused_stores( - self, + &self, dfg: &mut DataFlowGraph, protected_allocations: &HashSet, + block_id: BasicBlockId, ) { // Scan for unused stores let mut stores_to_remove = HashSet::new(); @@ -169,7 +314,7 @@ impl PerBlockContext { } // Delete unused stores - dfg[self.block_id] + dfg[block_id] .instructions_mut() .retain(|instruction| !stores_to_remove.contains(instruction)); } @@ -320,7 +465,7 @@ mod tests { .count() } - // Test that loads across multiple blocks are not removed + // Test that loads across multiple blocks are removed #[test] fn multiple_blocks() { // fn main { @@ -364,24 +509,22 @@ mod tests { // fn main { // b0(): // v0 = allocate - // store v0, Field 5 - // jmp b1(Field 5): // Optimized to constant 5 - // b1(v2: Field): - // v3 = load v0 // kept in program - // store v0, Field 6 - // return v2, v3, Field 6 // Optimized to constant 6 + // jmp b1(Field 5) + // b1(v3: Field): + // return v3, Field 5, Field 6 // Optimized to constants 5 and 6 // } let ssa = ssa.mem2reg(); + let main = ssa.main(); assert_eq!(main.reachable_blocks().len(), 2); - // Only the load from the entry block should be removed + // The loads should be removed assert_eq!(count_loads(main.entry_block(), &main.dfg), 0); - assert_eq!(count_loads(b1, &main.dfg), 1); + assert_eq!(count_loads(b1, &main.dfg), 0); - // All stores should be kept - assert_eq!(count_stores(main.entry_block(), &main.dfg), 1); - assert_eq!(count_stores(b1, &main.dfg), 1); + // All stores should be removed + assert_eq!(count_stores(main.entry_block(), &main.dfg), 0); + assert_eq!(count_stores(b1, &main.dfg), 0); // The jmp to b1 should also be a constant 5 now match main.dfg[main.entry_block()].terminator() { diff --git a/crates/noirc_evaluator/src/ssa/opt/unrolling.rs b/crates/noirc_evaluator/src/ssa/opt/unrolling.rs index f6d7c952277..6795133dfe6 100644 --- a/crates/noirc_evaluator/src/ssa/opt/unrolling.rs +++ b/crates/noirc_evaluator/src/ssa/opt/unrolling.rs @@ -34,10 +34,10 @@ impl Ssa { } } -struct Loop { +pub(crate) struct Loop { /// The header block of a loop is the block which dominates all the /// other blocks in the loop. - header: BasicBlockId, + pub(crate) header: BasicBlockId, /// The start of the back_edge n -> d is the block n at the end of /// the loop that jumps back to the header block d which restarts the loop. @@ -47,12 +47,12 @@ struct Loop { blocks: HashSet, } -struct Loops { +pub(crate) struct Loops { /// The loops that failed to be unrolled so that we do not try to unroll them again. /// Each loop is identified by its header block id. failed_to_unroll: HashSet, - yet_to_unroll: Vec, + pub(crate) yet_to_unroll: Vec, modified_blocks: HashSet, cfg: ControlFlowGraph, dom_tree: DominatorTree, @@ -60,7 +60,7 @@ struct Loops { /// Find a loop in the program by finding a node that dominates any predecessor node. /// The edge where this happens will be the back-edge of the loop. -fn find_all_loops(function: &Function) -> Loops { +pub(crate) fn find_all_loops(function: &Function) -> Loops { let cfg = ControlFlowGraph::with_function(function); let post_order = PostOrder::with_function(function); let mut dom_tree = DominatorTree::with_cfg_and_post_order(&cfg, &post_order); diff --git a/crates/noirc_frontend/src/monomorphization/mod.rs b/crates/noirc_frontend/src/monomorphization/mod.rs index 77503d19bc3..463164f61c5 100644 --- a/crates/noirc_frontend/src/monomorphization/mod.rs +++ b/crates/noirc_frontend/src/monomorphization/mod.rs @@ -957,14 +957,9 @@ impl<'interner> Monomorphizer<'interner> { ast::Expression::Assign(ast::Assign { expression, lvalue }) } - fn lvalue( - &mut self, - lvalue: HirLValue, - ) -> ast::LValue { + fn lvalue(&mut self, lvalue: HirLValue) -> ast::LValue { match lvalue { - HirLValue::Ident(ident, _) => { - ast::LValue::Ident(self.local_ident(&ident).unwrap()) - } + HirLValue::Ident(ident, _) => ast::LValue::Ident(self.local_ident(&ident).unwrap()), HirLValue::MemberAccess { object, field_index, .. } => { let field_index = field_index.unwrap(); let object = Box::new(self.lvalue(*object)); From d129a0a0392e36e401485e9afa9bd5c19476804d Mon Sep 17 00:00:00 2001 From: vezenovm Date: Wed, 9 Aug 2023 21:16:09 +0000 Subject: [PATCH 02/62] update regression_2218 test --- .../nargo_cli/tests/execution_success/references/src/main.nr | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/nargo_cli/tests/execution_success/references/src/main.nr b/crates/nargo_cli/tests/execution_success/references/src/main.nr index 96b94c6873f..a6fc5433fd2 100644 --- a/crates/nargo_cli/tests/execution_success/references/src/main.nr +++ b/crates/nargo_cli/tests/execution_success/references/src/main.nr @@ -106,9 +106,10 @@ fn regression_2218(x: Field, y: Field) { let q2 = *q; if x != y { + *q1 = 1; // Make sure that we correct load reference aliases through multiple blocks if x != 20 { - *q1 = 1; + *q1 = 10; *q2 = 2; // now we'd expect q1 == q2 == 2 assert(*q1 == 2); From 51abb143c91e8cd3c9ddac6382ed74ba0cc8482c Mon Sep 17 00:00:00 2001 From: vezenovm Date: Thu, 10 Aug 2023 01:31:53 +0000 Subject: [PATCH 03/62] initial merge slices work, weird bug right now with intrinsics, not getting correct array len --- .../execution_success/slices/src/main.nr | 114 ++++++++++------ .../src/ssa/ir/instruction/call.rs | 128 +++++++++++++++--- .../src/ssa/opt/flatten_cfg.rs | 77 ++++++++++- .../src/ssa/ssa_gen/context.rs | 6 +- crates/noirc_evaluator/src/ssa/ssa_gen/mod.rs | 70 +++++++++- 5 files changed, 322 insertions(+), 73 deletions(-) diff --git a/crates/nargo_cli/tests/execution_success/slices/src/main.nr b/crates/nargo_cli/tests/execution_success/slices/src/main.nr index ca1c4ac2966..c706ed9ec34 100644 --- a/crates/nargo_cli/tests/execution_success/slices/src/main.nr +++ b/crates/nargo_cli/tests/execution_success/slices/src/main.nr @@ -4,51 +4,75 @@ fn main(x : Field, y : pub Field) { /// TODO(#1889): Using slices in if statements where the condition is a witness /// is not yet supported - let mut slice = [0; 2]; - assert(slice[0] == 0); - assert(slice[0] != 1); - slice[0] = x; - assert(slice[0] == x); - - let slice_plus_10 = slice.push_back(y); - assert(slice_plus_10[2] == 10); - assert(slice_plus_10[2] != 8); - assert(slice_plus_10.len() == 3); - - let mut new_slice = []; - for i in 0..5 { - new_slice = new_slice.push_back(i); - } - assert(new_slice.len() == 5); - - new_slice = new_slice.push_front(20); - assert(new_slice[0] == 20); - assert(new_slice.len() == 6); - - let (popped_slice, last_elem) = new_slice.pop_back(); - assert(last_elem == 4); - assert(popped_slice.len() == 5); - - let (first_elem, rest_of_slice) = popped_slice.pop_front(); - assert(first_elem == 20); - assert(rest_of_slice.len() == 4); - - new_slice = rest_of_slice.insert(2, 100); - assert(new_slice[2] == 100); - assert(new_slice[4] == 3); - assert(new_slice.len() == 5); - - let (remove_slice, removed_elem) = new_slice.remove(3); - assert(removed_elem == 2); - assert(remove_slice[3] == 3); - assert(remove_slice.len() == 4); - - let append = [1, 2].append([3, 4, 5]); - assert(append.len() == 5); - assert(append[0] == 1); - assert(append[4] == 5); - - regression_2083(); + // Simple + let slice = [0; 2]; + std::println(slice.len()); + let slice = if x != y { + slice.push_back(y) + } else { + slice + }; + std::println(slice.len()); + // let slice = [0; 2]; + // let slice = if x != y { + // if x != 20 { + // let x = slice.push_back(y); + // std::println(x.len()); + // x + // } else { + // slice + // } + // } else { + // slice + // }; + // assert(slice[2] == 10); + // assert(slice.len() == 3); + + // let mut slice = [0; 2]; + // assert(slice[0] == 0); + // assert(slice[0] != 1); + // slice[0] = x; + // assert(slice[0] == x); + + // let slice_plus_10 = slice.push_back(y); + // assert(slice_plus_10[2] == 10); + // assert(slice_plus_10[2] != 8); + // assert(slice_plus_10.len() == 3); + + // let mut new_slice = []; + // for i in 0..5 { + // new_slice = new_slice.push_back(i); + // } + // assert(new_slice.len() == 5); + + // new_slice = new_slice.push_front(20); + // assert(new_slice[0] == 20); + // assert(new_slice.len() == 6); + + // let (popped_slice, last_elem) = new_slice.pop_back(); + // assert(last_elem == 4); + // assert(popped_slice.len() == 5); + + // let (first_elem, rest_of_slice) = popped_slice.pop_front(); + // assert(first_elem == 20); + // assert(rest_of_slice.len() == 4); + + // new_slice = rest_of_slice.insert(2, 100); + // assert(new_slice[2] == 100); + // assert(new_slice[4] == 3); + // assert(new_slice.len() == 5); + + // let (remove_slice, removed_elem) = new_slice.remove(3); + // assert(removed_elem == 2); + // assert(remove_slice[3] == 3); + // assert(remove_slice.len() == 4); + + // let append = [1, 2].append([3, 4, 5]); + // assert(append.len() == 5); + // assert(append[0] == 1); + // assert(append[4] == 5); + + // regression_2083(); } // Ensure that slices of struct/tuple values work. diff --git a/crates/noirc_evaluator/src/ssa/ir/instruction/call.rs b/crates/noirc_evaluator/src/ssa/ir/instruction/call.rs index 18d45228d55..881a01b0816 100644 --- a/crates/noirc_evaluator/src/ssa/ir/instruction/call.rs +++ b/crates/noirc_evaluator/src/ssa/ir/instruction/call.rs @@ -12,7 +12,7 @@ use crate::ssa::ir::{ value::{Value, ValueId}, }; -use super::{Endian, SimplifyResult}; +use super::{Endian, SimplifyResult, Binary, Instruction, BinaryOp}; /// Try to simplify this call instruction. If the instruction can be simplified to a known value, /// that value is returned. Otherwise None is returned. @@ -52,10 +52,26 @@ pub(super) fn simplify_call( } } Intrinsic::ArrayLen => { - let slice = dfg.get_array_constant(arguments[0]); - if let Some((slice, typ)) = slice { - let length = FieldElement::from((slice.len() / typ.element_size()) as u128); - SimplifyResult::SimplifiedTo(dfg.make_constant(length, Type::field())) + dbg!("ARRAYLEN"); + // let slice = dfg.get_array_constant(arguments[0]); + dbg!(arguments.len()); + let slice = match dfg.type_of_value(arguments[1]) { + Type::Slice(_) => { + dfg.get_array_constant(arguments[1]) + } + _ => None, + }; + if let (Some((_, typ)), length) = (slice, dfg.get_numeric_constant(arguments[0])) { + dbg!(length); + // let length = FieldElement::from((slice.len() / typ.element_size()) as u128); + if let Some(length) = length { + // let length = length / FieldElement::from(typ.element_size() as u128); + SimplifyResult::SimplifiedTo(dfg.make_constant(length, Type::field())) + } else { + // TODO: divide by the typ.element_size() + SimplifyResult::SimplifiedTo(arguments[0]) + // SimplifyResult::None + } } else if let Some(length) = dfg.try_get_array_length(arguments[0]) { let length = FieldElement::from(length as u128); SimplifyResult::SimplifiedTo(dfg.make_constant(length, Type::field())) @@ -64,31 +80,55 @@ pub(super) fn simplify_call( } } Intrinsic::SlicePushBack => { - let slice = dfg.get_array_constant(arguments[0]); + let slice = dfg.get_array_constant(arguments[1]); if let Some((mut slice, element_type)) = slice { - for elem in &arguments[1..] { + for elem in &arguments[2..] { slice.push_back(*elem); } + + // We must codegen the slice length here in-case it has been generated by + // a merger of slices based upon witness values + let one = dfg.make_constant(FieldElement::one(), Type::field()); + let block = dfg.make_block(); + let new_slice_length = dfg.insert_instruction_and_results( + Instruction::Binary(Binary { lhs: arguments[0], operator: BinaryOp::Sub, rhs: one }), + block, + None, + None, + ).first(); + let new_slice = dfg.make_array(slice, element_type); - SimplifyResult::SimplifiedTo(new_slice) + SimplifyResult::SimplifiedToMultiple(vec![new_slice_length, new_slice]) } else { SimplifyResult::None } } Intrinsic::SlicePushFront => { - let slice = dfg.get_array_constant(arguments[0]); + let slice = dfg.get_array_constant(arguments[1]); if let Some((mut slice, element_type)) = slice { - for elem in arguments[1..].iter().rev() { + for elem in arguments[2..].iter().rev() { slice.push_front(*elem); } + + // We must codegen the slice length here in-case it has been generated by + // a merger of slices based upon witness values + let one = dfg.make_constant(FieldElement::one(), Type::field()); + let block = dfg.make_block(); + let new_slice_length = dfg.insert_instruction_and_results( + Instruction::Binary(Binary { lhs: arguments[0], operator: BinaryOp::Sub, rhs: one }), + block, + None, + None, + ).first(); + let new_slice = dfg.make_array(slice, element_type); - SimplifyResult::SimplifiedTo(new_slice) + SimplifyResult::SimplifiedToMultiple(vec![new_slice_length, new_slice]) } else { SimplifyResult::None } } Intrinsic::SlicePopBack => { - let slice = dfg.get_array_constant(arguments[0]); + let slice = dfg.get_array_constant(arguments[1]); if let Some((mut slice, typ)) = slice { let element_count = typ.element_size(); let mut results = VecDeque::with_capacity(element_count + 1); @@ -104,6 +144,18 @@ pub(super) fn simplify_call( let new_slice = dfg.make_array(slice, typ); results.push_front(new_slice); + // We must codegen the slice length here in-case it has been generated by + // a merger of slices based upon witness values + let one = dfg.make_constant(FieldElement::one(), Type::field()); + let block = dfg.make_block(); + let new_slice_length = dfg.insert_instruction_and_results( + Instruction::Binary(Binary { lhs: arguments[0], operator: BinaryOp::Sub, rhs: one }), + block, + None, + None, + ).first(); + + results.push_front(new_slice_length); SimplifyResult::SimplifiedToMultiple(results.into()) } else { SimplifyResult::None @@ -111,7 +163,7 @@ pub(super) fn simplify_call( } Intrinsic::SlicePopFront => { dbg!(arguments[0]); - let slice = dfg.get_array_constant(arguments[0]); + let slice = dfg.get_array_constant(arguments[1]); if let Some((mut slice, typ)) = slice { let element_count = typ.element_size(); @@ -120,6 +172,18 @@ pub(super) fn simplify_call( slice.pop_front().expect("There are no elements in this slice to be removed") }); + // We must codegen the slice length here in-case it has been generated by + // a merger of slices based upon witness values + let one = dfg.make_constant(FieldElement::one(), Type::field()); + let block = dfg.make_block(); + let new_slice_length = dfg.insert_instruction_and_results( + Instruction::Binary(Binary { lhs: arguments[0], operator: BinaryOp::Sub, rhs: one }), + block, + None, + None, + ).first(); + results.push(new_slice_length); + let new_slice = dfg.make_array(slice, typ); // The slice is the last item returned for pop_front @@ -130,26 +194,37 @@ pub(super) fn simplify_call( } } Intrinsic::SliceInsert => { - let slice = dfg.get_array_constant(arguments[0]); - let index = dfg.get_numeric_constant(arguments[1]); + let slice = dfg.get_array_constant(arguments[1]); + let index = dfg.get_numeric_constant(arguments[2]); if let (Some((mut slice, typ)), Some(index)) = (slice, index) { - let elements = &arguments[2..]; + let elements = &arguments[3..]; let mut index = index.to_u128() as usize * elements.len(); - for elem in &arguments[2..] { + for elem in &arguments[3..] { slice.insert(index, *elem); index += 1; } + // We must codegen the slice length here in-case it has been generated by + // a merger of slices based upon witness values + let one = dfg.make_constant(FieldElement::one(), Type::field()); + let block = dfg.make_block(); + let new_slice_length = dfg.insert_instruction_and_results( + Instruction::Binary(Binary { lhs: arguments[0], operator: BinaryOp::Add, rhs: one }), + block, + None, + None, + ).first(); + let new_slice = dfg.make_array(slice, typ); - SimplifyResult::SimplifiedTo(new_slice) + SimplifyResult::SimplifiedToMultiple(vec![new_slice_length, new_slice]) } else { SimplifyResult::None } } Intrinsic::SliceRemove => { - let slice = dfg.get_array_constant(arguments[0]); - let index = dfg.get_numeric_constant(arguments[1]); + let slice = dfg.get_array_constant(arguments[1]); + let index = dfg.get_numeric_constant(arguments[2]); if let (Some((mut slice, typ)), Some(index)) = (slice, index) { let element_count = typ.element_size(); let mut results = Vec::with_capacity(element_count + 1); @@ -161,6 +236,19 @@ pub(super) fn simplify_call( let new_slice = dfg.make_array(slice, typ); results.insert(0, new_slice); + + // We must codegen the slice length here in-case it has been generated by + // a merger of slices based upon witness values + let one = dfg.make_constant(FieldElement::one(), Type::field()); + let block = dfg.make_block(); + let new_slice_length = dfg.insert_instruction_and_results( + Instruction::Binary(Binary { lhs: arguments[0], operator: BinaryOp::Sub, rhs: one }), + block, + None, + None, + ).first(); + results.insert(0, new_slice_length); + SimplifyResult::SimplifiedToMultiple(results) } else { SimplifyResult::None diff --git a/crates/noirc_evaluator/src/ssa/opt/flatten_cfg.rs b/crates/noirc_evaluator/src/ssa/opt/flatten_cfg.rs index 8481f5610bb..2060a102030 100644 --- a/crates/noirc_evaluator/src/ssa/opt/flatten_cfg.rs +++ b/crates/noirc_evaluator/src/ssa/opt/flatten_cfg.rs @@ -146,7 +146,7 @@ use crate::ssa::{ function_inserter::FunctionInserter, instruction::{BinaryOp, Instruction, InstructionId, TerminatorInstruction}, types::Type, - value::ValueId, + value::{ValueId, Value}, }, ssa_gen::Ssa, }; @@ -397,12 +397,85 @@ impl<'f> Context<'f> { self.merge_array_values(typ, then_condition, else_condition, then_value, else_value) } // TODO(#1889) - Type::Slice(_) => panic!("Cannot return slices from an if expression"), + typ @ Type::Slice(_) => { + self.merge_slice_values(typ, then_condition, else_condition, then_value, else_value) + // panic!("Cannot return slices from an if expression") + } Type::Reference => panic!("Cannot return references from an if expression"), Type::Function => panic!("Cannot return functions from an if expression"), } } + fn merge_slice_values( + &mut self, + typ: Type, + then_condition: ValueId, + else_condition: ValueId, + then_value_id: ValueId, + else_value_id: ValueId, + ) -> ValueId { + let mut merged = im::Vector::new(); + + let element_types = match &typ { + Type::Slice(elements) => elements, + _ => panic!("Expected slice type"), + }; + + // TODO: Handle when these are instructions for SlicePushBack rather than + // already converted to slices + let then_value = self.inserter.function.dfg[then_value_id].clone(); + let else_value = self.inserter.function.dfg[else_value_id].clone(); + dbg!(then_value.clone()); + dbg!(else_value.clone()); + + let len = match then_value { + Value::Array { array, .. } => { + array.len() + } + _ => panic!("Expected array value"), + }; + + let else_len = match else_value { + Value::Array { array, .. } => { + array.len() + } + _ => panic!("Expected array value"), + }; + + let len = if len > else_len { len } else { else_len }; + + for i in 0..len { + for (element_index, element_type) in element_types.iter().enumerate() { + let index_value = ((i * element_types.len() + element_index) as u128).into(); + let index = self.inserter.function.dfg.make_constant(index_value, Type::field()); + + let typevars = Some(vec![element_type.clone()]); + + // Works but leads to slices with more values at the end + let mut get_element = |array, typevars, len| { + if (len - 1) < index_value.to_u128() as usize { + self.inserter.function.dfg.make_constant(FieldElement::zero(), Type::field()) + } else { + let get = Instruction::ArrayGet { array, index }; + self.insert_instruction_with_typevars(get, typevars).first() + } + }; + + let then_element = get_element(then_value_id, typevars.clone(), len); + let else_element = get_element(else_value_id, typevars, else_len); + + merged.push_back(self.merge_values( + then_condition, + else_condition, + then_element, + else_element, + )); + } + } + dbg!(merged.clone()); + self.inserter.function.dfg.make_array(merged, typ) + } + /// Given an if expression that returns an array: `if c { array1 } else { array2 }`, /// this function will recursively merge array1 and array2 into a single resulting array /// by creating a new array containing the result of self.merge_values for each element. diff --git a/crates/noirc_evaluator/src/ssa/ssa_gen/context.rs b/crates/noirc_evaluator/src/ssa/ssa_gen/context.rs index 6de804a37b8..7a0553245c7 100644 --- a/crates/noirc_evaluator/src/ssa/ssa_gen/context.rs +++ b/crates/noirc_evaluator/src/ssa/ssa_gen/context.rs @@ -186,6 +186,10 @@ impl<'a> FunctionContext<'a> { let fmt_str_tuple = ast::Type::Tuple(final_fmt_str_fields); Self::map_type_helper(&fmt_str_tuple, f) } + ast::Type::Slice(elements) => { + let element_types = Self::convert_type(elements).flatten(); + Tree::Branch(vec![Tree::Leaf(f(Type::field())), Tree::Leaf(f(Type::Slice(Rc::new(element_types))))]) + } other => Tree::Leaf(f(Self::convert_non_tuple_type(other))), } } @@ -590,7 +594,7 @@ impl<'a> FunctionContext<'a> { } ast::LValue::Index { array, index, element_type, location } => { let (old_array, index, index_lvalue) = self.index_lvalue(array, index); - let element = self.codegen_array_index(old_array, index, element_type, *location); + let element = self.codegen_array_index(old_array, index, element_type, *location, None); (element, index_lvalue) } ast::LValue::MemberAccess { object, field_index: index } => { diff --git a/crates/noirc_evaluator/src/ssa/ssa_gen/mod.rs b/crates/noirc_evaluator/src/ssa/ssa_gen/mod.rs index 11c42d8b051..4bed39f3fdf 100644 --- a/crates/noirc_evaluator/src/ssa/ssa_gen/mod.rs +++ b/crates/noirc_evaluator/src/ssa/ssa_gen/mod.rs @@ -9,6 +9,8 @@ use iter_extended::vecmap; use noirc_errors::Location; use noirc_frontend::monomorphization::ast::{self, Expression, Program}; +use crate::ssa::ir::types::NumericType; + use self::{ context::FunctionContext, value::{Tree, Values}, @@ -117,9 +119,25 @@ impl<'a> FunctionContext<'a> { fn codegen_literal(&mut self, literal: &ast::Literal) -> Values { match literal { ast::Literal::Array(array) => { - let elements = vecmap(&array.contents, |element| self.codegen_expression(element)); + // let elements = vecmap(&array.contents, |element| self.codegen_expression(element)); let typ = Self::convert_non_tuple_type(&array.typ); - self.codegen_array(elements, typ) + + let new_convert_type = Self::convert_type(&array.typ); + dbg!(new_convert_type.clone()); + if new_convert_type.count_leaves() > 1 { + let slice_length = ast::Literal::Integer( + (array.contents.len() as u128).into(), + ast::Type::Field, + ); + dbg!(slice_length); + let slice_length = self.codegen_literal(&slice_length); + let elements = vecmap(&array.contents, |element| self.codegen_expression(element)); + let slice_contents = self.codegen_array(elements, typ); + Tree::Branch(vec![slice_length, slice_contents]) + } else { + let elements = vecmap(&array.contents, |element| self.codegen_expression(element)); + self.codegen_array(elements, typ) + } } ast::Literal::Integer(value, typ) => { let typ = Self::convert_non_tuple_type(typ); @@ -233,9 +251,27 @@ impl<'a> FunctionContext<'a> { } fn codegen_index(&mut self, index: &ast::Index) -> Values { - let array = self.codegen_non_tuple_expression(&index.collection); - let index_value = self.codegen_non_tuple_expression(&index.index); - self.codegen_array_index(array, index_value, &index.element_type, index.location) + // let array = self.codegen_non_tuple_expression(&index.collection); + // let index_value = self.codegen_non_tuple_expression(&index.index); + // self.codegen_array_index(array, index_value, &index.element_type, index.location) + dbg!("codegen_index"); + let array_or_slice = self.codegen_expression(&index.collection); + // dbg!(array_or_slice.clone()); + if array_or_slice.count_leaves() > 1 { + let index_value = self.codegen_non_tuple_expression(&index.index); + match &array_or_slice { + Tree::Branch(values) => { + let slice_length = values[0].clone().into_leaf().eval(self); + let slice = values[1].clone().into_leaf().eval(self); + self.codegen_array_index(slice, index_value, &index.element_type, index.location, Some(slice_length)) + } + Tree::Leaf(_) => panic!("Nooo"), + } + } else { + let array = self.codegen_non_tuple_expression(&index.collection); + let index_value = self.codegen_non_tuple_expression(&index.index); + self.codegen_array_index(array, index_value, &index.element_type, index.location, None) + } } /// This is broken off from codegen_index so that it can also be @@ -250,7 +286,14 @@ impl<'a> FunctionContext<'a> { index: super::ir::value::ValueId, element_type: &ast::Type, location: Location, + max_length: Option, ) -> Values { + let array_value = &self.builder.current_function.dfg[array]; + + dbg!(array_value.clone()); + let len = &self.builder.current_function.dfg.try_get_array_length(array); + dbg!(len); + // base_index = index * type_size let type_size = Self::convert_type(element_type).size_of_type(); let type_size = self.builder.field_constant(type_size as u128); @@ -261,6 +304,23 @@ impl<'a> FunctionContext<'a> { Self::map_type(element_type, |typ| { let offset = self.make_offset(base_index, field_index); field_index += 1; + let array_type = &self.builder.type_of_value(array); + match array_type { + Type::Slice(_) => { + let array_len = max_length.expect("ICE: a length must be supplied for indexing slices"); + // If the index and the array_len are both Fields we will not be able to perform a less than comparison on them + // Thus, we cast the array len to a u64 before performing the less than comparison + let array_len_int = self.builder.insert_cast(array_len, Type::Numeric(NumericType::Unsigned { bit_size: 64 })); + + let is_offset_out_of_bounds = self.builder.insert_binary(index, BinaryOp::Lt, array_len_int); + self.builder.insert_constrain(is_offset_out_of_bounds); + } + Type::Array(..) => { + // Nothing needs to done to prepare an array get on an array + } + _ => unreachable!("must have array or slice but got {array_type}"), + + } self.builder.insert_array_get(array, offset, typ).into() }) } From 17d66aae791775ff66b4e027f02ee7f242d00a9a Mon Sep 17 00:00:00 2001 From: vezenovm Date: Thu, 10 Aug 2023 01:32:55 +0000 Subject: [PATCH 04/62] cargo clippy --- crates/noirc_evaluator/src/ssa/opt/mem2reg.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs b/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs index 91bc1f3069a..a55294a4725 100644 --- a/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs +++ b/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs @@ -1,7 +1,7 @@ //! mem2reg implements a pass for promoting values stored in memory to values in registers where //! possible. This is particularly important for converting our memory-based representation of //! mutable variables into values that are easier to manipulate. -use std::collections::{BTreeMap, HashMap, HashSet}; +use std::collections::{BTreeMap, HashSet}; use crate::ssa::{ ir::{ From 06d777306cefd066acf1b2242eac31bb0b05c605 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Thu, 10 Aug 2023 01:56:12 +0000 Subject: [PATCH 05/62] working slice merger, but broken index_lvalue and append --- .../execution_success/slices/src/main.nr | 97 +++++++++---------- .../src/ssa/ir/instruction/call.rs | 9 +- .../src/ssa/ssa_gen/context.rs | 11 ++- crates/noirc_evaluator/src/ssa/ssa_gen/mod.rs | 7 +- 4 files changed, 68 insertions(+), 56 deletions(-) diff --git a/crates/nargo_cli/tests/execution_success/slices/src/main.nr b/crates/nargo_cli/tests/execution_success/slices/src/main.nr index c706ed9ec34..2fa0bc265af 100644 --- a/crates/nargo_cli/tests/execution_success/slices/src/main.nr +++ b/crates/nargo_cli/tests/execution_success/slices/src/main.nr @@ -1,78 +1,75 @@ use dep::std::slice; use dep::std; fn main(x : Field, y : pub Field) { - /// TODO(#1889): Using slices in if statements where the condition is a witness - /// is not yet supported - - // Simple let slice = [0; 2]; - std::println(slice.len()); let slice = if x != y { - slice.push_back(y) + if x != 20 { + std::println(slice.len()); + slice.push_back(y) + } else { + slice + } } else { slice }; - std::println(slice.len()); - // let slice = [0; 2]; - // let slice = if x != y { - // if x != 20 { - // let x = slice.push_back(y); - // std::println(x.len()); - // x - // } else { - // slice - // } - // } else { - // slice - // }; - // assert(slice[2] == 10); - // assert(slice.len() == 3); - - // let mut slice = [0; 2]; + assert(slice[2] == 10); + assert(slice.len() == 3); + + let mut slice = [0; 2]; + if x != y { + slice = slice.push_back(y); + slice = slice.push_back(x); + } else { + slice = slice.push_back(x); + } + assert(slice[3] == 5); + assert(slice.len() == 4); + + let mut slice = [0; 2]; // assert(slice[0] == 0); // assert(slice[0] != 1); // slice[0] = x; // assert(slice[0] == x); - // let slice_plus_10 = slice.push_back(y); - // assert(slice_plus_10[2] == 10); - // assert(slice_plus_10[2] != 8); - // assert(slice_plus_10.len() == 3); + let slice_plus_10 = slice.push_back(y); + assert(slice_plus_10[2] == 10); + assert(slice_plus_10[2] != 8); + assert(slice_plus_10.len() == 3); - // let mut new_slice = []; - // for i in 0..5 { - // new_slice = new_slice.push_back(i); - // } - // assert(new_slice.len() == 5); + let mut new_slice = []; + for i in 0..5 { + new_slice = new_slice.push_back(i); + } + assert(new_slice.len() == 5); - // new_slice = new_slice.push_front(20); - // assert(new_slice[0] == 20); - // assert(new_slice.len() == 6); + new_slice = new_slice.push_front(20); + assert(new_slice[0] == 20); + assert(new_slice.len() == 6); - // let (popped_slice, last_elem) = new_slice.pop_back(); - // assert(last_elem == 4); - // assert(popped_slice.len() == 5); + let (popped_slice, last_elem) = new_slice.pop_back(); + assert(last_elem == 4); + assert(popped_slice.len() == 5); - // let (first_elem, rest_of_slice) = popped_slice.pop_front(); - // assert(first_elem == 20); - // assert(rest_of_slice.len() == 4); + let (first_elem, rest_of_slice) = popped_slice.pop_front(); + assert(first_elem == 20); + assert(rest_of_slice.len() == 4); - // new_slice = rest_of_slice.insert(2, 100); - // assert(new_slice[2] == 100); - // assert(new_slice[4] == 3); - // assert(new_slice.len() == 5); + new_slice = rest_of_slice.insert(2, 100); + assert(new_slice[2] == 100); + assert(new_slice[4] == 3); + assert(new_slice.len() == 5); - // let (remove_slice, removed_elem) = new_slice.remove(3); - // assert(removed_elem == 2); - // assert(remove_slice[3] == 3); - // assert(remove_slice.len() == 4); + let (remove_slice, removed_elem) = new_slice.remove(3); + assert(removed_elem == 2); + assert(remove_slice[3] == 3); + assert(remove_slice.len() == 4); // let append = [1, 2].append([3, 4, 5]); // assert(append.len() == 5); // assert(append[0] == 1); // assert(append[4] == 5); - // regression_2083(); + regression_2083(); } // Ensure that slices of struct/tuple values work. diff --git a/crates/noirc_evaluator/src/ssa/ir/instruction/call.rs b/crates/noirc_evaluator/src/ssa/ir/instruction/call.rs index 881a01b0816..3aafc98ce54 100644 --- a/crates/noirc_evaluator/src/ssa/ir/instruction/call.rs +++ b/crates/noirc_evaluator/src/ssa/ir/instruction/call.rs @@ -61,7 +61,7 @@ pub(super) fn simplify_call( } _ => None, }; - if let (Some((_, typ)), length) = (slice, dfg.get_numeric_constant(arguments[0])) { + if let (Some((_, _)), length) = (slice, dfg.get_numeric_constant(arguments[0])) { dbg!(length); // let length = FieldElement::from((slice.len() / typ.element_size()) as u128); if let Some(length) = length { @@ -81,17 +81,18 @@ pub(super) fn simplify_call( } Intrinsic::SlicePushBack => { let slice = dfg.get_array_constant(arguments[1]); + dbg!(slice.clone()); if let Some((mut slice, element_type)) = slice { for elem in &arguments[2..] { slice.push_back(*elem); } - + dbg!(slice.clone()); // We must codegen the slice length here in-case it has been generated by // a merger of slices based upon witness values let one = dfg.make_constant(FieldElement::one(), Type::field()); let block = dfg.make_block(); let new_slice_length = dfg.insert_instruction_and_results( - Instruction::Binary(Binary { lhs: arguments[0], operator: BinaryOp::Sub, rhs: one }), + Instruction::Binary(Binary { lhs: arguments[0], operator: BinaryOp::Add, rhs: one }), block, None, None, @@ -115,7 +116,7 @@ pub(super) fn simplify_call( let one = dfg.make_constant(FieldElement::one(), Type::field()); let block = dfg.make_block(); let new_slice_length = dfg.insert_instruction_and_results( - Instruction::Binary(Binary { lhs: arguments[0], operator: BinaryOp::Sub, rhs: one }), + Instruction::Binary(Binary { lhs: arguments[0], operator: BinaryOp::Add, rhs: one }), block, None, None, diff --git a/crates/noirc_evaluator/src/ssa/ssa_gen/context.rs b/crates/noirc_evaluator/src/ssa/ssa_gen/context.rs index 7a0553245c7..7d0cd181e33 100644 --- a/crates/noirc_evaluator/src/ssa/ssa_gen/context.rs +++ b/crates/noirc_evaluator/src/ssa/ssa_gen/context.rs @@ -580,7 +580,15 @@ impl<'a> FunctionContext<'a> { index: &ast::Expression, ) -> (ValueId, ValueId, LValue) { let (old_array, array_lvalue) = self.extract_current_value_recursive(array); - let old_array = old_array.into_leaf().eval(self); + // TODO: handle slices here + let old_array = if old_array.count_leaves() > 1 { + let x = old_array.into_value_list(self); + dbg!(x.clone()); + x[1] + } else { + old_array.into_leaf().eval(self) + }; + // let old_array = old_array.into_leaf().eval(self); let array_lvalue = Box::new(array_lvalue); let index = self.codegen_non_tuple_expression(index); (old_array, index, LValue::Index { old_array, index, array_lvalue }) @@ -594,6 +602,7 @@ impl<'a> FunctionContext<'a> { } ast::LValue::Index { array, index, element_type, location } => { let (old_array, index, index_lvalue) = self.index_lvalue(array, index); + dbg!("in LValue::Index"); let element = self.codegen_array_index(old_array, index, element_type, *location, None); (element, index_lvalue) } diff --git a/crates/noirc_evaluator/src/ssa/ssa_gen/mod.rs b/crates/noirc_evaluator/src/ssa/ssa_gen/mod.rs index 4bed39f3fdf..4a49ff8f6ff 100644 --- a/crates/noirc_evaluator/src/ssa/ssa_gen/mod.rs +++ b/crates/noirc_evaluator/src/ssa/ssa_gen/mod.rs @@ -129,7 +129,6 @@ impl<'a> FunctionContext<'a> { (array.contents.len() as u128).into(), ast::Type::Field, ); - dbg!(slice_length); let slice_length = self.codegen_literal(&slice_length); let elements = vecmap(&array.contents, |element| self.codegen_expression(element)); let slice_contents = self.codegen_array(elements, typ); @@ -261,6 +260,11 @@ impl<'a> FunctionContext<'a> { let index_value = self.codegen_non_tuple_expression(&index.index); match &array_or_slice { Tree::Branch(values) => { + dbg!(values.clone()); + for value in values { + let x = value.clone().into_leaf().eval(self); + dbg!(&self.builder.current_function.dfg[x]); + } let slice_length = values[0].clone().into_leaf().eval(self); let slice = values[1].clone().into_leaf().eval(self); self.codegen_array_index(slice, index_value, &index.element_type, index.location, Some(slice_length)) @@ -307,6 +311,7 @@ impl<'a> FunctionContext<'a> { let array_type = &self.builder.type_of_value(array); match array_type { Type::Slice(_) => { + dbg!("got slice"); let array_len = max_length.expect("ICE: a length must be supplied for indexing slices"); // If the index and the array_len are both Fields we will not be able to perform a less than comparison on them // Thus, we cast the array len to a u64 before performing the less than comparison From a92d2c92265b34252b7d4c9789f670bba90d7d50 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Thu, 10 Aug 2023 14:14:09 +0000 Subject: [PATCH 06/62] merge slices working w/ updated slice representation, have to fix some acir gen --- .../execution_success/slices/src/main.nr | 80 ++++++++++++------- crates/noirc_evaluator/src/ssa.rs | 4 + .../noirc_evaluator/src/ssa/acir_gen/mod.rs | 5 +- crates/noirc_evaluator/src/ssa/opt/mem2reg.rs | 2 +- .../src/ssa/ssa_gen/context.rs | 58 +++++++++----- 5 files changed, 98 insertions(+), 51 deletions(-) diff --git a/crates/nargo_cli/tests/execution_success/slices/src/main.nr b/crates/nargo_cli/tests/execution_success/slices/src/main.nr index 2fa0bc265af..9f817be3b74 100644 --- a/crates/nargo_cli/tests/execution_success/slices/src/main.nr +++ b/crates/nargo_cli/tests/execution_success/slices/src/main.nr @@ -1,35 +1,13 @@ use dep::std::slice; use dep::std; fn main(x : Field, y : pub Field) { - let slice = [0; 2]; - let slice = if x != y { - if x != 20 { - std::println(slice.len()); - slice.push_back(y) - } else { - slice - } - } else { - slice - }; - assert(slice[2] == 10); - assert(slice.len() == 3); + regression_merge_slices(x, y); let mut slice = [0; 2]; - if x != y { - slice = slice.push_back(y); - slice = slice.push_back(x); - } else { - slice = slice.push_back(x); - } - assert(slice[3] == 5); - assert(slice.len() == 4); - - let mut slice = [0; 2]; - // assert(slice[0] == 0); - // assert(slice[0] != 1); - // slice[0] = x; - // assert(slice[0] == x); + assert(slice[0] == 0); + assert(slice[0] != 1); + slice[0] = x; + assert(slice[0] == x); let slice_plus_10 = slice.push_back(y); assert(slice_plus_10[2] == 10); @@ -64,10 +42,10 @@ fn main(x : Field, y : pub Field) { assert(remove_slice[3] == 3); assert(remove_slice.len() == 4); - // let append = [1, 2].append([3, 4, 5]); - // assert(append.len() == 5); - // assert(append[0] == 1); - // assert(append[4] == 5); + let append = [1, 2].append([3, 4, 5]); + assert(append.len() == 5); + assert(append[0] == 1); + assert(append[4] == 5); regression_2083(); } @@ -104,3 +82,43 @@ fn regression_2083() { assert(x.0 == 5); assert(x.1 == 6); } + +// The parameters to this function must come from witness values (inputs to main) +fn regression_merge_slices(x: Field, y: Field) { + // Test returning a merged slice without a mutation + let slice = [0; 2]; + let slice = if x != y { + if x != 20 { + slice.push_back(y) + } else { + slice + } + } else { + slice + }; + assert(slice[2] == 10); + assert(slice.len() == 3); + + // Test mutating a slice inside of an if statement + let mut slice = [0; 2]; + if x != y { + slice = slice.push_back(y); + slice = slice.push_back(x); + } else { + slice = slice.push_back(x); + } + assert(slice[3] == 5); + assert(slice.len() == 4); + + // Test mutating a slice inside of a loop in an if statement + let mut slice = [0; 2]; + if x != y { + for i in 0..5 { + slice = slice.push_back(i); + } + } else { + slice = slice.push_back(x); + } + assert(slice[6] == 4); + assert(slice.len() == 7); +} diff --git a/crates/noirc_evaluator/src/ssa.rs b/crates/noirc_evaluator/src/ssa.rs index e5009607ee9..ebe8c1eaa67 100644 --- a/crates/noirc_evaluator/src/ssa.rs +++ b/crates/noirc_evaluator/src/ssa.rs @@ -56,6 +56,10 @@ pub(crate) fn optimize_into_acir( .print(print_ssa_passes, "After Unrolling:") .simplify_cfg() .print(print_ssa_passes, "After Simplifying:") + // Run mem2reg with unrolled loops before flattening. + // If this pass is missed slice merging will fail inside of flattening + .mem2reg() + .print(print_ssa_passes, "After Mem2Reg:") .flatten_cfg() .print(print_ssa_passes, "After Flattening:") // Run mem2reg once more with the flattened CFG to catch any remaining loads/stores diff --git a/crates/noirc_evaluator/src/ssa/acir_gen/mod.rs b/crates/noirc_evaluator/src/ssa/acir_gen/mod.rs index 7409a199641..caf94bd93bb 100644 --- a/crates/noirc_evaluator/src/ssa/acir_gen/mod.rs +++ b/crates/noirc_evaluator/src/ssa/acir_gen/mod.rs @@ -321,6 +321,7 @@ impl Context { ) -> Result<(), RuntimeError> { let instruction = &dfg[instruction_id]; self.acir_context.set_location(dfg.get_location(&instruction_id)); + dbg!(instruction.clone()); match instruction { Instruction::Binary(binary) => { let result_acir_var = self.convert_ssa_binary(binary, dfg)?; @@ -988,7 +989,8 @@ impl Context { Intrinsic::ToBits(endian) => { let field = self.convert_value(arguments[0], dfg).into_var()?; let bit_size = self.convert_value(arguments[1], dfg).into_var()?; - let result_type = Self::array_element_type(dfg, result_ids[0]); + dbg!(result_ids.clone()); + let result_type = Self::array_element_type(dfg, result_ids[1]); self.acir_context.bit_decompose(endian, field, bit_size, result_type) } @@ -1036,6 +1038,7 @@ impl Context { /// Given an array value, return the numerical type of its element. /// Panics if the given value is not an array or has a non-numeric element type. fn array_element_type(dfg: &DataFlowGraph, value: ValueId) -> AcirType { + dbg!(dfg.type_of_value(value)); match dfg.type_of_value(value) { Type::Array(elements, _) => { assert_eq!(elements.len(), 1); diff --git a/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs b/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs index 91bc1f3069a..a55294a4725 100644 --- a/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs +++ b/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs @@ -1,7 +1,7 @@ //! mem2reg implements a pass for promoting values stored in memory to values in registers where //! possible. This is particularly important for converting our memory-based representation of //! mutable variables into values that are easier to manipulate. -use std::collections::{BTreeMap, HashMap, HashSet}; +use std::collections::{BTreeMap, HashSet}; use crate::ssa::{ ir::{ diff --git a/crates/noirc_evaluator/src/ssa/ssa_gen/context.rs b/crates/noirc_evaluator/src/ssa/ssa_gen/context.rs index 7d0cd181e33..b0831f4b453 100644 --- a/crates/noirc_evaluator/src/ssa/ssa_gen/context.rs +++ b/crates/noirc_evaluator/src/ssa/ssa_gen/context.rs @@ -572,26 +572,31 @@ impl<'a> FunctionContext<'a> { } /// Compile the given `array[index]` expression as a reference. - /// This will return a triple of (array, index, lvalue_ref) where the lvalue_ref records the + /// This will return a triple of (array, index, lvalue_ref, Option) where the lvalue_ref records the /// structure of the lvalue expression for use by `assign_new_value`. + /// The optional max length is for the case where we are indexing a slice rather than an array as slices + /// are representing as the following tuple: (length, slice contents). fn index_lvalue( &mut self, array: &ast::LValue, index: &ast::Expression, - ) -> (ValueId, ValueId, LValue) { + ) -> (ValueId, ValueId, LValue, Option) { + dbg!(array.clone()); let (old_array, array_lvalue) = self.extract_current_value_recursive(array); - // TODO: handle slices here - let old_array = if old_array.count_leaves() > 1 { - let x = old_array.into_value_list(self); - dbg!(x.clone()); - x[1] - } else { - old_array.into_leaf().eval(self) - }; - // let old_array = old_array.into_leaf().eval(self); - let array_lvalue = Box::new(array_lvalue); + dbg!(array_lvalue.clone()); let index = self.codegen_non_tuple_expression(index); - (old_array, index, LValue::Index { old_array, index, array_lvalue }) + let array_lvalue = Box::new(array_lvalue); + // A slice is represented as a tuple (length, slice contents) + // We need to fetch the second + if old_array.count_leaves() > 1 { + let slice_values = old_array.clone().into_value_list(self); + let slice_lvalue = LValue::SliceIndex { old_slice: old_array, index, slice_lvalue: array_lvalue }; + (slice_values[1], index, slice_lvalue, Some(slice_values[0])) + + } else { + let old_array = old_array.into_leaf().eval(self); + (old_array, index, LValue::Index { old_array, index, array_lvalue }, None) + } } fn extract_current_value_recursive(&mut self, lvalue: &ast::LValue) -> (Values, LValue) { @@ -601,9 +606,8 @@ impl<'a> FunctionContext<'a> { (variable.clone(), LValue::Ident(variable)) } ast::LValue::Index { array, index, element_type, location } => { - let (old_array, index, index_lvalue) = self.index_lvalue(array, index); - dbg!("in LValue::Index"); - let element = self.codegen_array_index(old_array, index, element_type, *location, None); + let (old_array, index, index_lvalue, max_length) = self.index_lvalue(array, index); + let element = self.codegen_array_index(old_array, index, element_type, *location, max_length); (element, index_lvalue) } ast::LValue::MemberAccess { object, field_index: index } => { @@ -640,9 +644,26 @@ impl<'a> FunctionContext<'a> { array = self.builder.insert_array_set(array, index, value); index = self.builder.insert_binary(index, BinaryOp::Add, one); }); - self.assign_new_value(*array_lvalue, array.into()); } + LValue::SliceIndex { old_slice: slice, index, slice_lvalue } => { + let mut slice_values = slice.into_value_list(self); + + let element_size = self.builder.field_constant(self.element_size(slice_values[1])); + + // The actual base index is the user's index * the array element type's size + let mut index = self.builder.insert_binary(index, BinaryOp::Mul, element_size); + let one = self.builder.field_constant(FieldElement::one()); + + new_value.for_each(|value| { + let value = value.eval(self); + slice_values[1] = self.builder.insert_array_set(slice_values[1], index, value); + index = self.builder.insert_binary(index, BinaryOp::Add, one); + }); + // The size of the slice does not change in an assign so we can reuse the same length value + let new_slice = Tree::Branch(vec![slice_values[0].into(), slice_values[1].into()]); + self.assign_new_value(*slice_lvalue, new_slice); + } LValue::MemberAccess { old_object, index, object_lvalue } => { let new_object = Self::replace_field(old_object, index, new_value); self.assign_new_value(*object_lvalue, new_object); @@ -822,10 +843,11 @@ impl SharedContext { } /// Used to remember the results of each step of extracting a value from an ast::LValue -#[derive(Debug)] +#[derive(Debug, Clone)] pub(super) enum LValue { Ident(Values), Index { old_array: ValueId, index: ValueId, array_lvalue: Box }, + SliceIndex { old_slice: Values, index: ValueId, slice_lvalue: Box }, MemberAccess { old_object: Values, index: usize, object_lvalue: Box }, Dereference { reference: Values }, } From e32e665ae2eeea2eea3e6942e3f96e8c3e5bca0d Mon Sep 17 00:00:00 2001 From: vezenovm Date: Thu, 10 Aug 2023 14:19:34 +0000 Subject: [PATCH 07/62] clean up recursive load fetch method --- crates/noirc_evaluator/src/ssa/opt/mem2reg.rs | 21 +++++-------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs b/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs index a55294a4725..f7f2b4092fb 100644 --- a/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs +++ b/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs @@ -30,8 +30,6 @@ impl Ssa { let mut context = PerFunctionContext::new(function); for block in function.reachable_blocks() { - let cfg: ControlFlowGraph = ControlFlowGraph::with_function(function); - // Maps Load instruction id -> value to replace the result of the load with let mut loads_to_substitute_per_block = BTreeMap::new(); @@ -44,7 +42,6 @@ impl Ssa { &mut loads_to_substitute_per_block, &mut load_values_to_substitute, block, - &cfg, ); all_protected_allocations.extend(allocations_protected_by_block.into_iter()); } @@ -98,7 +95,6 @@ impl PerFunctionContext { loads_to_substitute: &mut BTreeMap, load_values_to_substitute_per_block: &mut BTreeMap, block_id: BasicBlockId, - cfg: &ControlFlowGraph, ) -> HashSet { let mut protected_allocations = HashSet::new(); let block = &dfg[block_id]; @@ -107,10 +103,8 @@ impl PerFunctionContext { match &dfg[*instruction_id] { Instruction::Store { mut address, value } => { address = self.fetch_load_value_to_substitute_recursively( - cfg, block_id, address, - &mut HashSet::new(), ); self.last_stores_with_block.insert((address, block_id), *value); @@ -118,10 +112,8 @@ impl PerFunctionContext { } Instruction::Load { mut address } => { address = self.fetch_load_value_to_substitute_recursively( - cfg, block_id, address, - &mut HashSet::new(), ); let found_last_value = self.find_load_to_substitute( @@ -176,12 +168,11 @@ impl PerFunctionContext { fn fetch_load_value_to_substitute_recursively( &self, - cfg: &ControlFlowGraph, block_id: BasicBlockId, address: ValueId, - checked_blocks: &mut HashSet, ) -> ValueId { - checked_blocks.insert(block_id); + let mut visited = HashSet::new(); + visited.insert(block_id); if let Some((value, load_block_id)) = self.load_values_to_substitute_per_func.get(&address) { @@ -190,9 +181,9 @@ impl PerFunctionContext { } } - let mut dom_tree = DominatorTree::with_cfg_and_post_order(cfg, &self.post_order); + let mut dom_tree = DominatorTree::with_cfg_and_post_order(&self.cfg, &self.post_order); - let predecessors = cfg.predecessors(block_id); + let predecessors = self.cfg.predecessors(block_id); for predecessor in predecessors { if dom_tree.is_reachable(predecessor) && dom_tree.dominates(predecessor, block_id) { if let Some((value, load_block_id)) = @@ -203,12 +194,10 @@ impl PerFunctionContext { } } - if !checked_blocks.contains(&predecessor) { + if !visited.contains(&predecessor) { return self.fetch_load_value_to_substitute_recursively( - cfg, predecessor, address, - checked_blocks, ); } } From ada6129d6e6231f361dbe46c7501b8fbd479c9c1 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Thu, 10 Aug 2023 14:46:19 +0000 Subject: [PATCH 08/62] add test to mem2reg pass --- crates/noirc_evaluator/src/ssa/opt/mem2reg.rs | 108 +++++++++++++++--- 1 file changed, 95 insertions(+), 13 deletions(-) diff --git a/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs b/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs index f7f2b4092fb..d477d1c642a 100644 --- a/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs +++ b/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs @@ -102,19 +102,13 @@ impl PerFunctionContext { for instruction_id in block.instructions() { match &dfg[*instruction_id] { Instruction::Store { mut address, value } => { - address = self.fetch_load_value_to_substitute_recursively( - block_id, - address, - ); + address = self.fetch_load_value_to_substitute_recursively(block_id, address); self.last_stores_with_block.insert((address, block_id), *value); self.store_ids.push(*instruction_id); } Instruction::Load { mut address } => { - address = self.fetch_load_value_to_substitute_recursively( - block_id, - address, - ); + address = self.fetch_load_value_to_substitute_recursively(block_id, address); let found_last_value = self.find_load_to_substitute( block_id, @@ -195,10 +189,7 @@ impl PerFunctionContext { } if !visited.contains(&predecessor) { - return self.fetch_load_value_to_substitute_recursively( - predecessor, - address, - ); + return self.fetch_load_value_to_substitute_recursively(predecessor, address); } } } @@ -321,7 +312,7 @@ mod tests { basic_block::BasicBlockId, dfg::DataFlowGraph, function::RuntimeType, - instruction::{Instruction, Intrinsic, TerminatorInstruction}, + instruction::{BinaryOp, Instruction, Intrinsic, TerminatorInstruction}, map::Id, types::Type, }, @@ -526,4 +517,95 @@ mod tests { _ => unreachable!(), }; } + + // Test that a load in a predecessor block has been removed if the value + // is later stored in a successor block + #[test] + fn store_with_load_in_predecessor_block() { + // fn main { + // b0(): + // v0 = allocate + // store Field 0 at v0 + // v2 = allocate + // store v0 at v2 + // v3 = load v2 + // v4 = load v2 + // jmp b1() + // b1(): + // store Field 1 at v3 + // store Field 2 at v4 + // v8 = load v3 + // v9 = eq v8, Field 2 + // return + // } + let main_id = Id::test_new(0); + let mut builder = FunctionBuilder::new("main".into(), main_id, RuntimeType::Acir); + + let v0 = builder.insert_allocate(); + + let zero = builder.field_constant(0u128); + builder.insert_store(v0, zero); + + let v2 = builder.insert_allocate(); + builder.insert_store(v2, v0); + + let v3 = builder.insert_load(v2, Type::field()); + let v4 = builder.insert_load(v2, Type::field()); + let b1 = builder.insert_block(); + builder.terminate_with_jmp(b1, vec![]); + + builder.switch_to_block(b1); + + let one = builder.field_constant(1u128); + builder.insert_store(v3, one); + + let two = builder.field_constant(2u128); + builder.insert_store(v4, two); + + let v8 = builder.insert_load(v3, Type::field()); + let _ = builder.insert_binary(v8, BinaryOp::Eq, two); + + builder.terminate_with_return(vec![]); + + let ssa = builder.finish(); + assert_eq!(ssa.main().reachable_blocks().len(), 2); + + // Expected result: + // fn main { + // b0(): + // v0 = allocate + // v2 = allocate + // jmp b1() + // b1(): + // v8 = eq Field 2, Field 2 + // return + // } + let ssa = ssa.mem2reg(); + + let main = ssa.main(); + assert_eq!(main.reachable_blocks().len(), 2); + + // All loads should be removed + assert_eq!(count_loads(main.entry_block(), &main.dfg), 0); + assert_eq!(count_loads(b1, &main.dfg), 0); + + // All stores should be removed + assert_eq!(count_stores(main.entry_block(), &main.dfg), 0); + assert_eq!(count_stores(b1, &main.dfg), 0); + + let b1_instructions = main.dfg[b1].instructions(); + // The first instruction should be a binary operation + match &main.dfg[b1_instructions[0]] { + Instruction::Binary(binary) => { + let lhs = + main.dfg.get_numeric_constant(binary.lhs).expect("Expected constant value"); + let rhs = + main.dfg.get_numeric_constant(binary.rhs).expect("Expected constant value"); + + assert_eq!(lhs, rhs); + assert_eq!(lhs, FieldElement::from(2u128)); + } + _ => unreachable!(), + } + } } From bedfa2af951fa0b78f9c8aa710546569408940cb Mon Sep 17 00:00:00 2001 From: vezenovm Date: Thu, 10 Aug 2023 14:57:00 +0000 Subject: [PATCH 09/62] cleaner search methods --- crates/noirc_evaluator/src/ssa/opt/mem2reg.rs | 44 ++++++------------- 1 file changed, 13 insertions(+), 31 deletions(-) diff --git a/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs b/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs index d477d1c642a..bfcb28ce452 100644 --- a/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs +++ b/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs @@ -160,6 +160,8 @@ impl PerFunctionContext { protected_allocations } + // TODO: switch to while-loop based search so I am not recomputing the dom tree or pass it directly outside of the function + // I would rather have this func match `find_load_to_substitute` fn fetch_load_value_to_substitute_recursively( &self, block_id: BasicBlockId, @@ -168,6 +170,7 @@ impl PerFunctionContext { let mut visited = HashSet::new(); visited.insert(block_id); + // If the load value to substitute is in the current block we substitute it if let Some((value, load_block_id)) = self.load_values_to_substitute_per_func.get(&address) { if *load_block_id == block_id { @@ -179,18 +182,11 @@ impl PerFunctionContext { let predecessors = self.cfg.predecessors(block_id); for predecessor in predecessors { - if dom_tree.is_reachable(predecessor) && dom_tree.dominates(predecessor, block_id) { - if let Some((value, load_block_id)) = - self.load_values_to_substitute_per_func.get(&address) - { - if *load_block_id == predecessor { - return *value; - } - } - - if !visited.contains(&predecessor) { - return self.fetch_load_value_to_substitute_recursively(predecessor, address); - } + if dom_tree.is_reachable(predecessor) + && dom_tree.dominates(predecessor, block_id) + && !visited.contains(&predecessor) + { + return self.fetch_load_value_to_substitute_recursively(predecessor, address); } } address @@ -236,25 +232,11 @@ impl PerFunctionContext { for predecessor in predecessors { // TODO: Do I need is_reachable here? We are looping over only the reachable blocks but does // that include a reachable block's predecessors? - if dom_tree.is_reachable(predecessor) && dom_tree.dominates(predecessor, block) { - if let Some(last_value) = - self.last_stores_with_block.get(&(address, predecessor)) - { - let result_value = *dfg - .instruction_results(*instruction_id) - .first() - .expect("ICE: Load instructions should have single result"); - - loads_to_substitute.insert(*instruction_id, *last_value); - load_values_to_substitute_per_block.insert(result_value, *last_value); - self.load_values_to_substitute_per_func - .insert(result_value, (*last_value, block)); - return true; - } - // Only recurse further if the predecessor dominates the current block we are checking - if !visited.contains(&predecessor) { - stack.push(predecessor); - } + if dom_tree.is_reachable(predecessor) + && dom_tree.dominates(predecessor, block) + && !visited.contains(&predecessor) + { + stack.push(predecessor); } } } From 3d422e131f8d682398c0f49e5b62a9bca28b0ade Mon Sep 17 00:00:00 2001 From: vezenovm Date: Thu, 10 Aug 2023 15:05:14 +0000 Subject: [PATCH 10/62] switch from recursive to while-loop based search for loads --- crates/noirc_evaluator/src/ssa/opt/mem2reg.rs | 49 +++++++++---------- 1 file changed, 23 insertions(+), 26 deletions(-) diff --git a/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs b/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs index bfcb28ce452..f77a5b11542 100644 --- a/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs +++ b/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs @@ -102,18 +102,18 @@ impl PerFunctionContext { for instruction_id in block.instructions() { match &dfg[*instruction_id] { Instruction::Store { mut address, value } => { - address = self.fetch_load_value_to_substitute_recursively(block_id, address); + address = self.fetch_load_value_to_substitute(block_id, address); self.last_stores_with_block.insert((address, block_id), *value); self.store_ids.push(*instruction_id); } Instruction::Load { mut address } => { - address = self.fetch_load_value_to_substitute_recursively(block_id, address); + address = self.fetch_load_value_to_substitute(block_id, address); let found_last_value = self.find_load_to_substitute( block_id, - dfg, address, + dfg, instruction_id, loads_to_substitute, load_values_to_substitute_per_block, @@ -160,33 +160,30 @@ impl PerFunctionContext { protected_allocations } - // TODO: switch to while-loop based search so I am not recomputing the dom tree or pass it directly outside of the function - // I would rather have this func match `find_load_to_substitute` - fn fetch_load_value_to_substitute_recursively( - &self, - block_id: BasicBlockId, - address: ValueId, - ) -> ValueId { + fn fetch_load_value_to_substitute(&self, block_id: BasicBlockId, address: ValueId) -> ValueId { + let mut stack = vec![block_id]; let mut visited = HashSet::new(); - visited.insert(block_id); - - // If the load value to substitute is in the current block we substitute it - if let Some((value, load_block_id)) = self.load_values_to_substitute_per_func.get(&address) - { - if *load_block_id == block_id { - return *value; - } - } let mut dom_tree = DominatorTree::with_cfg_and_post_order(&self.cfg, &self.post_order); + while let Some(block) = stack.pop() { + visited.insert(block); - let predecessors = self.cfg.predecessors(block_id); - for predecessor in predecessors { - if dom_tree.is_reachable(predecessor) - && dom_tree.dominates(predecessor, block_id) - && !visited.contains(&predecessor) + if let Some((value, load_block_id)) = + self.load_values_to_substitute_per_func.get(&address) { - return self.fetch_load_value_to_substitute_recursively(predecessor, address); + if *load_block_id == block { + return *value; + } + } + + let predecessors = self.cfg.predecessors(block); + for predecessor in predecessors { + if dom_tree.is_reachable(predecessor) + && dom_tree.dominates(predecessor, block) + && !visited.contains(&predecessor) + { + stack.push(predecessor); + } } } address @@ -195,8 +192,8 @@ impl PerFunctionContext { fn find_load_to_substitute( &mut self, block_id: BasicBlockId, - dfg: &DataFlowGraph, address: ValueId, + dfg: &DataFlowGraph, instruction_id: &InstructionId, loads_to_substitute: &mut BTreeMap, load_values_to_substitute_per_block: &mut BTreeMap, From c12c5bbd85238b021681ead38669d2d1560a1ecb Mon Sep 17 00:00:00 2001 From: vezenovm Date: Thu, 10 Aug 2023 15:22:54 +0000 Subject: [PATCH 11/62] add comments to mem2reg pass --- crates/noirc_evaluator/src/ssa/opt/mem2reg.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs b/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs index f77a5b11542..9b3862ca5ec 100644 --- a/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs +++ b/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs @@ -160,6 +160,10 @@ impl PerFunctionContext { protected_allocations } + // This method finds the load values to substitute for a given address + // The search starts at the block supplied as a parameter. If there is not a load to substitute + // the CFG is analyzed to determine whether a predecessor block has a load value to substitute. + // If there is no load value to substitute the original address is returned. fn fetch_load_value_to_substitute(&self, block_id: BasicBlockId, address: ValueId) -> ValueId { let mut stack = vec![block_id]; let mut visited = HashSet::new(); @@ -168,6 +172,8 @@ impl PerFunctionContext { while let Some(block) = stack.pop() { visited.insert(block); + // Check whether there is a load value to substitute in the current block. + // Return the value if found. if let Some((value, load_block_id)) = self.load_values_to_substitute_per_func.get(&address) { @@ -176,6 +182,7 @@ impl PerFunctionContext { } } + // If no load values to substitute have been found in the current block, check the block's predecessors. let predecessors = self.cfg.predecessors(block); for predecessor in predecessors { if dom_tree.is_reachable(predecessor) @@ -189,6 +196,10 @@ impl PerFunctionContext { address } + // This method determines which loads should be substituted. + // Starting at the block supplied as a parameter, we check whether a store has occurred with the given address. + // If no store has occurred in the supplied block, the CFG is analyzed to determine whether + // a predecessor block has a store at the given address. fn find_load_to_substitute( &mut self, block_id: BasicBlockId, @@ -213,6 +224,8 @@ impl PerFunctionContext { } } + // Check whether there has been a store instruction in the current block + // If there has been a store, add a load to be substituted. if let Some(last_value) = self.last_stores_with_block.get(&(address, block)) { let result_value = *dfg .instruction_results(*instruction_id) @@ -225,6 +238,7 @@ impl PerFunctionContext { return true; } + // If no stores have been found in the current block, check the block's predecessors. let predecessors = self.cfg.predecessors(block); for predecessor in predecessors { // TODO: Do I need is_reachable here? We are looping over only the reachable blocks but does From 9988d913cbadbd47b768c8b0e5dcc2d0c694c62f Mon Sep 17 00:00:00 2001 From: vezenovm Date: Thu, 10 Aug 2023 15:24:08 +0000 Subject: [PATCH 12/62] fmt --- crates/noirc_evaluator/src/ssa/opt/mem2reg.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs b/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs index 9b3862ca5ec..55439fe881a 100644 --- a/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs +++ b/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs @@ -68,6 +68,7 @@ struct PerFunctionContext { post_order: PostOrder, loops: Loops, } + impl PerFunctionContext { fn new(function: &Function) -> Self { PerFunctionContext { From 7cc6afe27135a08b9dcb3c4e390feefe181c19e6 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Thu, 10 Aug 2023 15:30:38 +0000 Subject: [PATCH 13/62] remove dbg --- crates/noirc_evaluator/src/ssa/ir/instruction/call.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/noirc_evaluator/src/ssa/ir/instruction/call.rs b/crates/noirc_evaluator/src/ssa/ir/instruction/call.rs index 18d45228d55..a28f2d88bcb 100644 --- a/crates/noirc_evaluator/src/ssa/ir/instruction/call.rs +++ b/crates/noirc_evaluator/src/ssa/ir/instruction/call.rs @@ -110,7 +110,6 @@ pub(super) fn simplify_call( } } Intrinsic::SlicePopFront => { - dbg!(arguments[0]); let slice = dfg.get_array_constant(arguments[0]); if let Some((mut slice, typ)) = slice { let element_count = typ.element_size(); From 24b2fe638dec74fc52af6bc52622fb2053cceb5e Mon Sep 17 00:00:00 2001 From: vezenovm Date: Thu, 10 Aug 2023 17:15:38 +0000 Subject: [PATCH 14/62] fixed ec_baby_jubjub but need fix to mem2reg pass to make it efficient --- .../src/ssa/acir_gen/acir_ir/acir_variable.rs | 5 ++++- .../noirc_evaluator/src/ssa/acir_gen/mod.rs | 6 +++-- .../src/ssa/ir/instruction/call.rs | 22 ++++++++++++++++--- crates/noirc_evaluator/src/ssa/opt/mem2reg.rs | 3 ++- crates/noirc_evaluator/src/ssa/ssa_gen/mod.rs | 9 +++++++- 5 files changed, 37 insertions(+), 8 deletions(-) diff --git a/crates/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs b/crates/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs index 03dc414f3fb..7aa011be239 100644 --- a/crates/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs +++ b/crates/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs @@ -786,7 +786,10 @@ impl AcirContext { limb_vars.push(AcirValue::Var(zero, result_element_type.clone())); } - Ok(vec![AcirValue::Array(limb_vars.into())]) + // `Intrinsic::ToRadix` returns slices which are represented + // by tuples with the structure (length, slice contents) + dbg!(limb_vars.len()); + Ok(vec![AcirValue::Var(self.add_constant(FieldElement::from(limb_vars.len() as u128)), AcirType::field()), AcirValue::Array(limb_vars.into())]) } /// Returns `AcirVar`s constrained to be the bit decomposition of the provided input diff --git a/crates/noirc_evaluator/src/ssa/acir_gen/mod.rs b/crates/noirc_evaluator/src/ssa/acir_gen/mod.rs index caf94bd93bb..478f792abab 100644 --- a/crates/noirc_evaluator/src/ssa/acir_gen/mod.rs +++ b/crates/noirc_evaluator/src/ssa/acir_gen/mod.rs @@ -601,6 +601,7 @@ impl Context { // Use the SSA ID to get or create its block ID let block_id = self.block_id(&array); + dbg!(dfg.type_of_value(array)); // Every array has a length in its type, so we fetch that from // the SSA IR. let len = match dfg.type_of_value(array) { @@ -982,14 +983,15 @@ impl Context { let field = self.convert_value(arguments[0], dfg).into_var()?; let radix = self.convert_value(arguments[1], dfg).into_var()?; let limb_size = self.convert_value(arguments[2], dfg).into_var()?; - let result_type = Self::array_element_type(dfg, result_ids[0]); + let result_type = Self::array_element_type(dfg, result_ids[1]); + dbg!(result_type.clone()); self.acir_context.radix_decompose(endian, field, radix, limb_size, result_type) } Intrinsic::ToBits(endian) => { let field = self.convert_value(arguments[0], dfg).into_var()?; let bit_size = self.convert_value(arguments[1], dfg).into_var()?; - dbg!(result_ids.clone()); + let result_type = Self::array_element_type(dfg, result_ids[1]); self.acir_context.bit_decompose(endian, field, bit_size, result_type) diff --git a/crates/noirc_evaluator/src/ssa/ir/instruction/call.rs b/crates/noirc_evaluator/src/ssa/ir/instruction/call.rs index fbfd64337a7..cfed8a2e8b5 100644 --- a/crates/noirc_evaluator/src/ssa/ir/instruction/call.rs +++ b/crates/noirc_evaluator/src/ssa/ir/instruction/call.rs @@ -34,7 +34,15 @@ pub(super) fn simplify_call( if let Some(constant_args) = constant_args { let field = constant_args[0]; let limb_count = constant_args[1].to_u128() as u32; - SimplifyResult::SimplifiedTo(constant_to_radix(endian, field, 2, limb_count, dfg)) + + let result_slice = constant_to_radix(endian, field, 2, limb_count, dfg); + + let length = dfg.try_get_array_length(result_slice).expect("ICE: a constant array should have an associated length"); + let len_value = dfg.make_constant(FieldElement::from(length as u128), Type::field()); + + // `Intrinsic::ToBits` returns slices which are represented + // by tuples with the structure (length, slice contents) + SimplifyResult::SimplifiedToMultiple(vec![len_value, result_slice]) } else { SimplifyResult::None } @@ -44,9 +52,17 @@ pub(super) fn simplify_call( let field = constant_args[0]; let radix = constant_args[1].to_u128() as u32; let limb_count = constant_args[2].to_u128() as u32; - SimplifyResult::SimplifiedTo(constant_to_radix( + + let result_slice = constant_to_radix( endian, field, radix, limb_count, dfg, - )) + ); + + let length = dfg.try_get_array_length(result_slice).expect("ICE: a constant array should have an associated length"); + let len_value = dfg.make_constant(FieldElement::from(length as u128), Type::field()); + + // `Intrinsic::ToRadix` returns slices which are represented + // by tuples with the structure (length, slice contents) + SimplifyResult::SimplifiedToMultiple(vec![len_value, result_slice]) } else { SimplifyResult::None } diff --git a/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs b/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs index 55439fe881a..8a9eaae485c 100644 --- a/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs +++ b/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs @@ -24,6 +24,7 @@ impl Ssa { /// scope, and attempts to remove stores that are subsequently redundant. /// As long as they are not stores on memory used inside of loops pub(crate) fn mem2reg(mut self) -> Ssa { + dbg!("started mem2reg"); for function in self.functions.values_mut() { let mut all_protected_allocations = HashSet::new(); @@ -53,7 +54,7 @@ impl Ssa { context.remove_unused_stores(&mut function.dfg, &all_protected_allocations, block); } } - + dbg!("done mem2reg"); self } } diff --git a/crates/noirc_evaluator/src/ssa/ssa_gen/mod.rs b/crates/noirc_evaluator/src/ssa/ssa_gen/mod.rs index 4a49ff8f6ff..822a3b2779e 100644 --- a/crates/noirc_evaluator/src/ssa/ssa_gen/mod.rs +++ b/crates/noirc_evaluator/src/ssa/ssa_gen/mod.rs @@ -67,7 +67,11 @@ impl<'a> FunctionContext<'a> { Expression::Block(block) => self.codegen_block(block), Expression::Unary(unary) => self.codegen_unary(unary), Expression::Binary(binary) => self.codegen_binary(binary), - Expression::Index(index) => self.codegen_index(index), + Expression::Index(index) => { + let values = self.codegen_index(index); + dbg!(values.clone()); + values + } Expression::Cast(cast) => self.codegen_cast(cast), Expression::For(for_expr) => self.codegen_for(for_expr), Expression::If(if_expr) => self.codegen_if(if_expr), @@ -98,6 +102,7 @@ impl<'a> FunctionContext<'a> { /// to reassign to it. Note that mutable references `let x = &mut ...;` do not require this /// since they are not automatically loaded from and must be explicitly dereferenced. fn codegen_ident_reference(&mut self, ident: &ast::Ident) -> Values { + dbg!(ident.definition.clone()); match &ident.definition { ast::Definition::Local(id) => self.lookup(*id), ast::Definition::Function(id) => self.get_or_queue_function(*id), @@ -315,6 +320,7 @@ impl<'a> FunctionContext<'a> { let array_len = max_length.expect("ICE: a length must be supplied for indexing slices"); // If the index and the array_len are both Fields we will not be able to perform a less than comparison on them // Thus, we cast the array len to a u64 before performing the less than comparison + dbg!(array_len); let array_len_int = self.builder.insert_cast(array_len, Type::Numeric(NumericType::Unsigned { bit_size: 64 })); let is_offset_out_of_bounds = self.builder.insert_binary(index, BinaryOp::Lt, array_len_int); @@ -326,6 +332,7 @@ impl<'a> FunctionContext<'a> { _ => unreachable!("must have array or slice but got {array_type}"), } + dbg!("about to insert array get"); self.builder.insert_array_get(array, offset, typ).into() }) } From fc7327a3ee07c522f74a2d818a4ecd856d2ac59f Mon Sep 17 00:00:00 2001 From: vezenovm Date: Thu, 10 Aug 2023 17:23:12 +0000 Subject: [PATCH 15/62] move dom tree per func --- crates/noirc_evaluator/src/ssa/opt/mem2reg.rs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs b/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs index 55439fe881a..68825d64729 100644 --- a/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs +++ b/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs @@ -28,6 +28,8 @@ impl Ssa { let mut all_protected_allocations = HashSet::new(); let mut context = PerFunctionContext::new(function); + let post_order = PostOrder::with_function(function); + let mut dom_tree = DominatorTree::with_cfg_and_post_order(&context.cfg, &post_order); for block in function.reachable_blocks() { // Maps Load instruction id -> value to replace the result of the load with @@ -42,6 +44,7 @@ impl Ssa { &mut loads_to_substitute_per_block, &mut load_values_to_substitute, block, + &mut dom_tree, ); all_protected_allocations.extend(allocations_protected_by_block.into_iter()); } @@ -53,7 +56,6 @@ impl Ssa { context.remove_unused_stores(&mut function.dfg, &all_protected_allocations, block); } } - self } } @@ -65,7 +67,6 @@ struct PerFunctionContext { load_values_to_substitute_per_func: BTreeMap, store_ids: Vec, cfg: ControlFlowGraph, - post_order: PostOrder, loops: Loops, } @@ -76,7 +77,6 @@ impl PerFunctionContext { load_values_to_substitute_per_func: BTreeMap::new(), store_ids: Vec::new(), cfg: ControlFlowGraph::with_function(function), - post_order: PostOrder::with_function(function), loops: find_all_loops(function), } } @@ -96,6 +96,7 @@ impl PerFunctionContext { loads_to_substitute: &mut BTreeMap, load_values_to_substitute_per_block: &mut BTreeMap, block_id: BasicBlockId, + dom_tree: &mut DominatorTree, ) -> HashSet { let mut protected_allocations = HashSet::new(); let block = &dfg[block_id]; @@ -103,13 +104,13 @@ impl PerFunctionContext { for instruction_id in block.instructions() { match &dfg[*instruction_id] { Instruction::Store { mut address, value } => { - address = self.fetch_load_value_to_substitute(block_id, address); + address = self.fetch_load_value_to_substitute(block_id, address, dom_tree); self.last_stores_with_block.insert((address, block_id), *value); self.store_ids.push(*instruction_id); } Instruction::Load { mut address } => { - address = self.fetch_load_value_to_substitute(block_id, address); + address = self.fetch_load_value_to_substitute(block_id, address, dom_tree); let found_last_value = self.find_load_to_substitute( block_id, @@ -118,6 +119,7 @@ impl PerFunctionContext { instruction_id, loads_to_substitute, load_values_to_substitute_per_block, + dom_tree, ); if !found_last_value { protected_allocations.insert(address); @@ -165,11 +167,11 @@ impl PerFunctionContext { // The search starts at the block supplied as a parameter. If there is not a load to substitute // the CFG is analyzed to determine whether a predecessor block has a load value to substitute. // If there is no load value to substitute the original address is returned. - fn fetch_load_value_to_substitute(&self, block_id: BasicBlockId, address: ValueId) -> ValueId { + fn fetch_load_value_to_substitute(&self, block_id: BasicBlockId, address: ValueId, dom_tree: &mut DominatorTree) -> ValueId { let mut stack = vec![block_id]; let mut visited = HashSet::new(); - let mut dom_tree = DominatorTree::with_cfg_and_post_order(&self.cfg, &self.post_order); + // let mut dom_tree = DominatorTree::with_cfg_and_post_order(&self.cfg, &self.post_order); while let Some(block) = stack.pop() { visited.insert(block); @@ -209,11 +211,11 @@ impl PerFunctionContext { instruction_id: &InstructionId, loads_to_substitute: &mut BTreeMap, load_values_to_substitute_per_block: &mut BTreeMap, + dom_tree: &mut DominatorTree, ) -> bool { let mut stack = vec![block_id]; let mut visited = HashSet::new(); - let mut dom_tree = DominatorTree::with_cfg_and_post_order(&self.cfg, &self.post_order); while let Some(block) = stack.pop() { visited.insert(block); From 0bb6401187cfc5bbdc3cef5bd168aae9a32cbcd7 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Thu, 10 Aug 2023 17:23:26 +0000 Subject: [PATCH 16/62] cargo fmt --- crates/noirc_evaluator/src/ssa/opt/mem2reg.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs b/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs index 68825d64729..6f65d3eebd0 100644 --- a/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs +++ b/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs @@ -167,7 +167,12 @@ impl PerFunctionContext { // The search starts at the block supplied as a parameter. If there is not a load to substitute // the CFG is analyzed to determine whether a predecessor block has a load value to substitute. // If there is no load value to substitute the original address is returned. - fn fetch_load_value_to_substitute(&self, block_id: BasicBlockId, address: ValueId, dom_tree: &mut DominatorTree) -> ValueId { + fn fetch_load_value_to_substitute( + &self, + block_id: BasicBlockId, + address: ValueId, + dom_tree: &mut DominatorTree, + ) -> ValueId { let mut stack = vec![block_id]; let mut visited = HashSet::new(); From 5baf9e8f2252afbc2876d4b7c639ff3d4b21ba12 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Thu, 10 Aug 2023 17:26:46 +0000 Subject: [PATCH 17/62] make dom tree part of the func context --- crates/noirc_evaluator/src/ssa/opt/mem2reg.rs | 28 +++++++++---------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs b/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs index 6f65d3eebd0..a2aa75da4d4 100644 --- a/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs +++ b/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs @@ -28,8 +28,6 @@ impl Ssa { let mut all_protected_allocations = HashSet::new(); let mut context = PerFunctionContext::new(function); - let post_order = PostOrder::with_function(function); - let mut dom_tree = DominatorTree::with_cfg_and_post_order(&context.cfg, &post_order); for block in function.reachable_blocks() { // Maps Load instruction id -> value to replace the result of the load with @@ -44,7 +42,6 @@ impl Ssa { &mut loads_to_substitute_per_block, &mut load_values_to_substitute, block, - &mut dom_tree, ); all_protected_allocations.extend(allocations_protected_by_block.into_iter()); } @@ -67,16 +64,21 @@ struct PerFunctionContext { load_values_to_substitute_per_func: BTreeMap, store_ids: Vec, cfg: ControlFlowGraph, + dom_tree: DominatorTree, loops: Loops, } impl PerFunctionContext { fn new(function: &Function) -> Self { + let cfg = ControlFlowGraph::with_function(function); + let post_order = PostOrder::with_function(function); + let dom_tree = DominatorTree::with_cfg_and_post_order(&cfg, &post_order); PerFunctionContext { last_stores_with_block: BTreeMap::new(), load_values_to_substitute_per_func: BTreeMap::new(), store_ids: Vec::new(), - cfg: ControlFlowGraph::with_function(function), + cfg, + dom_tree, loops: find_all_loops(function), } } @@ -96,7 +98,6 @@ impl PerFunctionContext { loads_to_substitute: &mut BTreeMap, load_values_to_substitute_per_block: &mut BTreeMap, block_id: BasicBlockId, - dom_tree: &mut DominatorTree, ) -> HashSet { let mut protected_allocations = HashSet::new(); let block = &dfg[block_id]; @@ -104,13 +105,13 @@ impl PerFunctionContext { for instruction_id in block.instructions() { match &dfg[*instruction_id] { Instruction::Store { mut address, value } => { - address = self.fetch_load_value_to_substitute(block_id, address, dom_tree); + address = self.fetch_load_value_to_substitute(block_id, address); self.last_stores_with_block.insert((address, block_id), *value); self.store_ids.push(*instruction_id); } Instruction::Load { mut address } => { - address = self.fetch_load_value_to_substitute(block_id, address, dom_tree); + address = self.fetch_load_value_to_substitute(block_id, address); let found_last_value = self.find_load_to_substitute( block_id, @@ -119,7 +120,6 @@ impl PerFunctionContext { instruction_id, loads_to_substitute, load_values_to_substitute_per_block, - dom_tree, ); if !found_last_value { protected_allocations.insert(address); @@ -168,10 +168,9 @@ impl PerFunctionContext { // the CFG is analyzed to determine whether a predecessor block has a load value to substitute. // If there is no load value to substitute the original address is returned. fn fetch_load_value_to_substitute( - &self, + &mut self, block_id: BasicBlockId, address: ValueId, - dom_tree: &mut DominatorTree, ) -> ValueId { let mut stack = vec![block_id]; let mut visited = HashSet::new(); @@ -193,8 +192,8 @@ impl PerFunctionContext { // If no load values to substitute have been found in the current block, check the block's predecessors. let predecessors = self.cfg.predecessors(block); for predecessor in predecessors { - if dom_tree.is_reachable(predecessor) - && dom_tree.dominates(predecessor, block) + if self.dom_tree.is_reachable(predecessor) + && self.dom_tree.dominates(predecessor, block) && !visited.contains(&predecessor) { stack.push(predecessor); @@ -216,7 +215,6 @@ impl PerFunctionContext { instruction_id: &InstructionId, loads_to_substitute: &mut BTreeMap, load_values_to_substitute_per_block: &mut BTreeMap, - dom_tree: &mut DominatorTree, ) -> bool { let mut stack = vec![block_id]; let mut visited = HashSet::new(); @@ -251,8 +249,8 @@ impl PerFunctionContext { for predecessor in predecessors { // TODO: Do I need is_reachable here? We are looping over only the reachable blocks but does // that include a reachable block's predecessors? - if dom_tree.is_reachable(predecessor) - && dom_tree.dominates(predecessor, block) + if self.dom_tree.is_reachable(predecessor) + && self.dom_tree.dominates(predecessor, block) && !visited.contains(&predecessor) { stack.push(predecessor); From 06256c6e8ccf5cb33e29650bee826ea003302969 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Thu, 10 Aug 2023 17:31:12 +0000 Subject: [PATCH 18/62] remove old ocmment --- crates/noirc_evaluator/src/ssa/opt/mem2reg.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs b/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs index a2aa75da4d4..f73b66a9839 100644 --- a/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs +++ b/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs @@ -24,6 +24,7 @@ impl Ssa { /// scope, and attempts to remove stores that are subsequently redundant. /// As long as they are not stores on memory used inside of loops pub(crate) fn mem2reg(mut self) -> Ssa { + dbg!("start mem2reg"); for function in self.functions.values_mut() { let mut all_protected_allocations = HashSet::new(); @@ -53,6 +54,7 @@ impl Ssa { context.remove_unused_stores(&mut function.dfg, &all_protected_allocations, block); } } + dbg!("ended mem2reg"); self } } @@ -175,7 +177,6 @@ impl PerFunctionContext { let mut stack = vec![block_id]; let mut visited = HashSet::new(); - // let mut dom_tree = DominatorTree::with_cfg_and_post_order(&self.cfg, &self.post_order); while let Some(block) = stack.pop() { visited.insert(block); From 1f8d4bdcc4dbac1f000fc9089b2c3bc38127f903 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Fri, 11 Aug 2023 01:40:50 +0000 Subject: [PATCH 19/62] fixed mem2reg before flattening --- crates/noirc_evaluator/src/errors.rs | 1 + .../src/ssa/acir_gen/acir_ir/acir_variable.rs | 10 +- .../noirc_evaluator/src/ssa/acir_gen/mod.rs | 29 +-- crates/noirc_evaluator/src/ssa/ir/dom.rs | 2 +- .../src/ssa/ir/instruction/call.rs | 203 +++++++++++------- .../src/ssa/opt/flatten_cfg.rs | 23 +- crates/noirc_evaluator/src/ssa/opt/mem2reg.rs | 48 +++-- .../noirc_evaluator/src/ssa/opt/unrolling.rs | 7 +- .../src/ssa/ssa_gen/context.rs | 20 +- crates/noirc_evaluator/src/ssa/ssa_gen/mod.rs | 60 +++--- 10 files changed, 252 insertions(+), 151 deletions(-) diff --git a/crates/noirc_evaluator/src/errors.rs b/crates/noirc_evaluator/src/errors.rs index 981f0c9f8a7..88b16f5d819 100644 --- a/crates/noirc_evaluator/src/errors.rs +++ b/crates/noirc_evaluator/src/errors.rs @@ -77,6 +77,7 @@ impl RuntimeError { impl From for FileDiagnostic { fn from(error: RuntimeError) -> Self { + dbg!(error.clone()); let file_id = error.location().map(|loc| loc.file).unwrap(); FileDiagnostic { file_id, diagnostic: error.into() } } diff --git a/crates/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs b/crates/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs index 7aa011be239..4085eed985c 100644 --- a/crates/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs +++ b/crates/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs @@ -786,10 +786,16 @@ impl AcirContext { limb_vars.push(AcirValue::Var(zero, result_element_type.clone())); } - // `Intrinsic::ToRadix` returns slices which are represented + // `Intrinsic::ToRadix` returns slices which are represented // by tuples with the structure (length, slice contents) dbg!(limb_vars.len()); - Ok(vec![AcirValue::Var(self.add_constant(FieldElement::from(limb_vars.len() as u128)), AcirType::field()), AcirValue::Array(limb_vars.into())]) + Ok(vec![ + AcirValue::Var( + self.add_constant(FieldElement::from(limb_vars.len() as u128)), + AcirType::field(), + ), + AcirValue::Array(limb_vars.into()), + ]) } /// Returns `AcirVar`s constrained to be the bit decomposition of the provided input diff --git a/crates/noirc_evaluator/src/ssa/acir_gen/mod.rs b/crates/noirc_evaluator/src/ssa/acir_gen/mod.rs index 478f792abab..ad4c29b69dc 100644 --- a/crates/noirc_evaluator/src/ssa/acir_gen/mod.rs +++ b/crates/noirc_evaluator/src/ssa/acir_gen/mod.rs @@ -321,7 +321,7 @@ impl Context { ) -> Result<(), RuntimeError> { let instruction = &dfg[instruction_id]; self.acir_context.set_location(dfg.get_location(&instruction_id)); - dbg!(instruction.clone()); + // dbg!(instruction.clone()); match instruction { Instruction::Binary(binary) => { let result_acir_var = self.convert_ssa_binary(binary, dfg)?; @@ -941,18 +941,23 @@ impl Context { dfg: &DataFlowGraph, ) -> Result { let mut var = self.convert_numeric_value(value_id, dfg)?; - let truncation_target = match &dfg[value_id] { - Value::Instruction { instruction, .. } => &dfg[*instruction], - _ => unreachable!("ICE: Truncates are only ever applied to the result of a binary op"), + match &dfg[value_id] { + Value::Instruction { instruction, .. } => { + if matches!(&dfg[*instruction], Instruction::Binary(Binary { operator: BinaryOp::Sub, .. })) + { + // Subtractions must first have the integer modulus added before truncation can be + // applied. This is done in order to prevent underflow. + let integer_modulus = + self.acir_context.add_constant(FieldElement::from(2_u128.pow(bit_size))); + var = self.acir_context.add_var(var, integer_modulus)?; + } + } + Value::Param { .. } => { + // Binary operations on params may have been entirely simplified if the operation + // give back the identity of the param + } + _ => unreachable!("ICE: Truncates are only ever applied to the result of a binary op or a param"), }; - if matches!(truncation_target, Instruction::Binary(Binary { operator: BinaryOp::Sub, .. })) - { - // Subtractions must first have the integer modulus added before truncation can be - // applied. This is done in order to prevent underflow. - let integer_modulus = - self.acir_context.add_constant(FieldElement::from(2_u128.pow(bit_size))); - var = self.acir_context.add_var(var, integer_modulus)?; - } self.acir_context.truncate_var(var, bit_size, max_bit_size) } diff --git a/crates/noirc_evaluator/src/ssa/ir/dom.rs b/crates/noirc_evaluator/src/ssa/ir/dom.rs index b7b1728d035..4c9bbb74a30 100644 --- a/crates/noirc_evaluator/src/ssa/ir/dom.rs +++ b/crates/noirc_evaluator/src/ssa/ir/dom.rs @@ -11,7 +11,7 @@ use super::{ }; /// Dominator tree node. We keep one of these per reachable block. -#[derive(Clone, Default)] +#[derive(Clone, Default, Debug)] struct DominatorTreeNode { /// The block's idx in the control flow graph's reverse post-order reverse_post_order_idx: u32, diff --git a/crates/noirc_evaluator/src/ssa/ir/instruction/call.rs b/crates/noirc_evaluator/src/ssa/ir/instruction/call.rs index cfed8a2e8b5..a7779ea3bd5 100644 --- a/crates/noirc_evaluator/src/ssa/ir/instruction/call.rs +++ b/crates/noirc_evaluator/src/ssa/ir/instruction/call.rs @@ -12,7 +12,7 @@ use crate::ssa::ir::{ value::{Value, ValueId}, }; -use super::{Endian, SimplifyResult, Binary, Instruction, BinaryOp}; +use super::{Binary, BinaryOp, Endian, Instruction, SimplifyResult}; /// Try to simplify this call instruction. If the instruction can be simplified to a known value, /// that value is returned. Otherwise None is returned. @@ -37,10 +37,13 @@ pub(super) fn simplify_call( let result_slice = constant_to_radix(endian, field, 2, limb_count, dfg); - let length = dfg.try_get_array_length(result_slice).expect("ICE: a constant array should have an associated length"); - let len_value = dfg.make_constant(FieldElement::from(length as u128), Type::field()); + let length = dfg + .try_get_array_length(result_slice) + .expect("ICE: a constant array should have an associated length"); + let len_value = + dfg.make_constant(FieldElement::from(length as u128), Type::field()); - // `Intrinsic::ToBits` returns slices which are represented + // `Intrinsic::ToBits` returns slices which are represented // by tuples with the structure (length, slice contents) SimplifyResult::SimplifiedToMultiple(vec![len_value, result_slice]) } else { @@ -53,14 +56,15 @@ pub(super) fn simplify_call( let radix = constant_args[1].to_u128() as u32; let limb_count = constant_args[2].to_u128() as u32; - let result_slice = constant_to_radix( - endian, field, radix, limb_count, dfg, - ); + let result_slice = constant_to_radix(endian, field, radix, limb_count, dfg); - let length = dfg.try_get_array_length(result_slice).expect("ICE: a constant array should have an associated length"); - let len_value = dfg.make_constant(FieldElement::from(length as u128), Type::field()); + let length = dfg + .try_get_array_length(result_slice) + .expect("ICE: a constant array should have an associated length"); + let len_value = + dfg.make_constant(FieldElement::from(length as u128), Type::field()); - // `Intrinsic::ToRadix` returns slices which are represented + // `Intrinsic::ToRadix` returns slices which are represented // by tuples with the structure (length, slice contents) SimplifyResult::SimplifiedToMultiple(vec![len_value, result_slice]) } else { @@ -70,49 +74,72 @@ pub(super) fn simplify_call( Intrinsic::ArrayLen => { dbg!("ARRAYLEN"); // let slice = dfg.get_array_constant(arguments[0]); + // if let Some((slice, typ)) = slice { + // let length = FieldElement::from((slice.len() / typ.element_size()) as u128); + // SimplifyResult::SimplifiedTo(dfg.make_constant(length, Type::field())) + // } dbg!(arguments.len()); - let slice = match dfg.type_of_value(arguments[1]) { - Type::Slice(_) => { - dfg.get_array_constant(arguments[1]) - } - _ => None, - }; - if let (Some((_, _)), length) = (slice, dfg.get_numeric_constant(arguments[0])) { - dbg!(length); - // let length = FieldElement::from((slice.len() / typ.element_size()) as u128); - if let Some(length) = length { - // let length = length / FieldElement::from(typ.element_size() as u128); - SimplifyResult::SimplifiedTo(dfg.make_constant(length, Type::field())) - } else { - // TODO: divide by the typ.element_size() - SimplifyResult::SimplifiedTo(arguments[0]) - // SimplifyResult::None - } - } else if let Some(length) = dfg.try_get_array_length(arguments[0]) { + // Probably need to check this + // arguments.len() > 1 && + if let Some(length) = dfg.try_get_array_length(arguments[0]) { let length = FieldElement::from(length as u128); + // let length = length / FieldElement::from(typ.element_size() as u128); SimplifyResult::SimplifiedTo(dfg.make_constant(length, Type::field())) + } else if let Some(length) = dfg.get_numeric_constant(arguments[0]) { + SimplifyResult::SimplifiedTo(dfg.make_constant(length, Type::field())) + } else if matches!(dfg.type_of_value(arguments[1]), Type::Slice(_)) { + SimplifyResult::SimplifiedTo(arguments[0]) } else { SimplifyResult::None } + + // if let Some(length) = dfg.get_numeric_constant(arguments[0]) { + // dbg!(length); + // match dfg.type_of_value(arguments[1]) { + // Type::Slice(_) => (), + // _ => unreachable!("ICE: a numeric constant must be followed by a slice for inputs to Intrinsic::ArrayLen"), + // }; + // SimplifyResult::SimplifiedTo(dfg.make_constant(length, Type::field())) + // let length = FieldElement::from((slice.len() / typ.element_size()) as u128); + // if let Some(length) = length { + // // let length = length / FieldElement::from(typ.element_size() as u128); + // SimplifyResult::SimplifiedTo(dfg.make_constant(length, Type::field())) + // } else { + // // TODO: divide by the typ.element_size() + // SimplifyResult::SimplifiedTo(arguments[0]) + // // SimplifyResult::None + // } + // } else if let Some(length) = dfg.try_get_array_length(arguments[0]) { + // let length = FieldElement::from(length as u128); + // SimplifyResult::SimplifiedTo(dfg.make_constant(length, Type::field())) + // } else { + // SimplifyResult::None + // } } Intrinsic::SlicePushBack => { let slice = dfg.get_array_constant(arguments[1]); - dbg!(slice.clone()); + // dbg!(slice.clone()); if let Some((mut slice, element_type)) = slice { for elem in &arguments[2..] { slice.push_back(*elem); } - dbg!(slice.clone()); - // We must codegen the slice length here in-case it has been generated by + // dbg!(slice.clone()); + // We must codegen the slice length here in-case it has been generated by // a merger of slices based upon witness values let one = dfg.make_constant(FieldElement::one(), Type::field()); let block = dfg.make_block(); - let new_slice_length = dfg.insert_instruction_and_results( - Instruction::Binary(Binary { lhs: arguments[0], operator: BinaryOp::Add, rhs: one }), - block, - None, - None, - ).first(); + let new_slice_length = dfg + .insert_instruction_and_results( + Instruction::Binary(Binary { + lhs: arguments[0], + operator: BinaryOp::Add, + rhs: one, + }), + block, + None, + None, + ) + .first(); let new_slice = dfg.make_array(slice, element_type); SimplifyResult::SimplifiedToMultiple(vec![new_slice_length, new_slice]) @@ -127,16 +154,22 @@ pub(super) fn simplify_call( slice.push_front(*elem); } - // We must codegen the slice length here in-case it has been generated by + // We must codegen the slice length here in-case it has been generated by // a merger of slices based upon witness values let one = dfg.make_constant(FieldElement::one(), Type::field()); let block = dfg.make_block(); - let new_slice_length = dfg.insert_instruction_and_results( - Instruction::Binary(Binary { lhs: arguments[0], operator: BinaryOp::Add, rhs: one }), - block, - None, - None, - ).first(); + let new_slice_length = dfg + .insert_instruction_and_results( + Instruction::Binary(Binary { + lhs: arguments[0], + operator: BinaryOp::Add, + rhs: one, + }), + block, + None, + None, + ) + .first(); let new_slice = dfg.make_array(slice, element_type); SimplifyResult::SimplifiedToMultiple(vec![new_slice_length, new_slice]) @@ -161,16 +194,22 @@ pub(super) fn simplify_call( let new_slice = dfg.make_array(slice, typ); results.push_front(new_slice); - // We must codegen the slice length here in-case it has been generated by + // We must codegen the slice length here in-case it has been generated by // a merger of slices based upon witness values let one = dfg.make_constant(FieldElement::one(), Type::field()); let block = dfg.make_block(); - let new_slice_length = dfg.insert_instruction_and_results( - Instruction::Binary(Binary { lhs: arguments[0], operator: BinaryOp::Sub, rhs: one }), - block, - None, - None, - ).first(); + let new_slice_length = dfg + .insert_instruction_and_results( + Instruction::Binary(Binary { + lhs: arguments[0], + operator: BinaryOp::Sub, + rhs: one, + }), + block, + None, + None, + ) + .first(); results.push_front(new_slice_length); SimplifyResult::SimplifiedToMultiple(results.into()) @@ -188,16 +227,22 @@ pub(super) fn simplify_call( slice.pop_front().expect("There are no elements in this slice to be removed") }); - // We must codegen the slice length here in-case it has been generated by + // We must codegen the slice length here in-case it has been generated by // a merger of slices based upon witness values let one = dfg.make_constant(FieldElement::one(), Type::field()); let block = dfg.make_block(); - let new_slice_length = dfg.insert_instruction_and_results( - Instruction::Binary(Binary { lhs: arguments[0], operator: BinaryOp::Sub, rhs: one }), - block, - None, - None, - ).first(); + let new_slice_length = dfg + .insert_instruction_and_results( + Instruction::Binary(Binary { + lhs: arguments[0], + operator: BinaryOp::Sub, + rhs: one, + }), + block, + None, + None, + ) + .first(); results.push(new_slice_length); let new_slice = dfg.make_array(slice, typ); @@ -221,16 +266,22 @@ pub(super) fn simplify_call( index += 1; } - // We must codegen the slice length here in-case it has been generated by + // We must codegen the slice length here in-case it has been generated by // a merger of slices based upon witness values let one = dfg.make_constant(FieldElement::one(), Type::field()); let block = dfg.make_block(); - let new_slice_length = dfg.insert_instruction_and_results( - Instruction::Binary(Binary { lhs: arguments[0], operator: BinaryOp::Add, rhs: one }), - block, - None, - None, - ).first(); + let new_slice_length = dfg + .insert_instruction_and_results( + Instruction::Binary(Binary { + lhs: arguments[0], + operator: BinaryOp::Add, + rhs: one, + }), + block, + None, + None, + ) + .first(); let new_slice = dfg.make_array(slice, typ); SimplifyResult::SimplifiedToMultiple(vec![new_slice_length, new_slice]) @@ -253,16 +304,22 @@ pub(super) fn simplify_call( let new_slice = dfg.make_array(slice, typ); results.insert(0, new_slice); - // We must codegen the slice length here in-case it has been generated by + // We must codegen the slice length here in-case it has been generated by // a merger of slices based upon witness values let one = dfg.make_constant(FieldElement::one(), Type::field()); let block = dfg.make_block(); - let new_slice_length = dfg.insert_instruction_and_results( - Instruction::Binary(Binary { lhs: arguments[0], operator: BinaryOp::Sub, rhs: one }), - block, - None, - None, - ).first(); + let new_slice_length = dfg + .insert_instruction_and_results( + Instruction::Binary(Binary { + lhs: arguments[0], + operator: BinaryOp::Sub, + rhs: one, + }), + block, + None, + None, + ) + .first(); results.insert(0, new_slice_length); SimplifyResult::SimplifiedToMultiple(results) @@ -287,8 +344,10 @@ fn simplify_black_box_func( BlackBoxFunc::SHA256 => simplify_hash(dfg, arguments, acvm::blackbox_solver::sha256), BlackBoxFunc::Blake2s => simplify_hash(dfg, arguments, acvm::blackbox_solver::blake2s), BlackBoxFunc::Keccak256 => { + dbg!("got to simplify keccak"); match (dfg.get_array_constant(arguments[0]), dfg.get_numeric_constant(arguments[1])) { (Some((input, _)), Some(num_bytes)) if array_is_constant(dfg, &input) => { + dbg!("simplifying keccak"); let input_bytes: Vec = to_u8_vec(dfg, input); let num_bytes = num_bytes.to_u128() as usize; diff --git a/crates/noirc_evaluator/src/ssa/opt/flatten_cfg.rs b/crates/noirc_evaluator/src/ssa/opt/flatten_cfg.rs index 2060a102030..ba5230391a3 100644 --- a/crates/noirc_evaluator/src/ssa/opt/flatten_cfg.rs +++ b/crates/noirc_evaluator/src/ssa/opt/flatten_cfg.rs @@ -146,7 +146,7 @@ use crate::ssa::{ function_inserter::FunctionInserter, instruction::{BinaryOp, Instruction, InstructionId, TerminatorInstruction}, types::Type, - value::{ValueId, Value}, + value::{Value, ValueId}, }, ssa_gen::Ssa, }; @@ -425,20 +425,16 @@ impl<'f> Context<'f> { // already converted to slices let then_value = self.inserter.function.dfg[then_value_id].clone(); let else_value = self.inserter.function.dfg[else_value_id].clone(); - dbg!(then_value.clone()); - dbg!(else_value.clone()); + // dbg!(then_value.clone()); + // dbg!(else_value.clone()); let len = match then_value { - Value::Array { array, .. } => { - array.len() - } + Value::Array { array, .. } => array.len(), _ => panic!("Expected array value"), }; let else_len = match else_value { - Value::Array { array, .. } => { - array.len() - } + Value::Array { array, .. } => array.len(), _ => panic!("Expected array value"), }; @@ -454,7 +450,10 @@ impl<'f> Context<'f> { // Works but leads to slices with more values at the end let mut get_element = |array, typevars, len| { if (len - 1) < index_value.to_u128() as usize { - self.inserter.function.dfg.make_constant(FieldElement::zero(), Type::field()) + self.inserter + .function + .dfg + .make_constant(FieldElement::zero(), Type::field()) } else { let get = Instruction::ArrayGet { array, index }; self.insert_instruction_with_typevars(get, typevars).first() @@ -471,8 +470,8 @@ impl<'f> Context<'f> { else_element, )); } - } - dbg!(merged.clone()); + } + // dbg!(merged.clone()); self.inserter.function.dfg.make_array(merged, typ) } diff --git a/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs b/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs index f73b66a9839..dc605c2c6e4 100644 --- a/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs +++ b/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs @@ -180,6 +180,12 @@ impl PerFunctionContext { while let Some(block) = stack.pop() { visited.insert(block); + // for l in self.loops.yet_to_unroll.iter() { + // if block == l.header { + // return address; + // } + // } + // Check whether there is a load value to substitute in the current block. // Return the value if found. if let Some((value, load_block_id)) = @@ -191,8 +197,18 @@ impl PerFunctionContext { } // If no load values to substitute have been found in the current block, check the block's predecessors. - let predecessors = self.cfg.predecessors(block); - for predecessor in predecessors { + // let predecessors = self.cfg.predecessors(block); + // for predecessor in predecessors { + // if self.dom_tree.is_reachable(predecessor) + // && self.dom_tree.dominates(predecessor, block) + // && !visited.contains(&predecessor) + // { + // stack.push(predecessor); + // } + // } + let mut predecessors = self.cfg.predecessors(block); + if predecessors.len() == 1 { + let predecessor = predecessors.next().unwrap(); if self.dom_tree.is_reachable(predecessor) && self.dom_tree.dominates(predecessor, block) && !visited.contains(&predecessor) @@ -223,14 +239,6 @@ impl PerFunctionContext { while let Some(block) = stack.pop() { visited.insert(block); - for l in self.loops.yet_to_unroll.iter() { - // We do not want to substitute loads that take place within loops as this pass - // can occur before loop unrolling - if block == l.header { - return false; - } - } - // Check whether there has been a store instruction in the current block // If there has been a store, add a load to be substituted. if let Some(last_value) = self.last_stores_with_block.get(&(address, block)) { @@ -245,11 +253,21 @@ impl PerFunctionContext { return true; } - // If no stores have been found in the current block, check the block's predecessors. - let predecessors = self.cfg.predecessors(block); - for predecessor in predecessors { - // TODO: Do I need is_reachable here? We are looping over only the reachable blocks but does - // that include a reachable block's predecessors? + + // If no stores have been found in the current block, check the block's predecessor + // let predecessors = self.cfg.predecessors(block); + // for predecessor in predecessors { + // if self.dom_tree.is_reachable(predecessor) + // && self.dom_tree.dominates(predecessor, block) + // && !visited.contains(&predecessor) + // { + // stack.push(predecessor); + // } + // } + + let mut predecessors = self.cfg.predecessors(block); + if predecessors.len() == 1 { + let predecessor = predecessors.next().unwrap(); if self.dom_tree.is_reachable(predecessor) && self.dom_tree.dominates(predecessor, block) && !visited.contains(&predecessor) diff --git a/crates/noirc_evaluator/src/ssa/opt/unrolling.rs b/crates/noirc_evaluator/src/ssa/opt/unrolling.rs index 20df50d5780..6557a2d895c 100644 --- a/crates/noirc_evaluator/src/ssa/opt/unrolling.rs +++ b/crates/noirc_evaluator/src/ssa/opt/unrolling.rs @@ -46,6 +46,7 @@ impl Ssa { } } +#[derive(Debug)] pub(crate) struct Loop { /// The header block of a loop is the block which dominates all the /// other blocks in the loop. @@ -56,16 +57,16 @@ pub(crate) struct Loop { back_edge_start: BasicBlockId, /// All the blocks contained within the loop, including `header` and `back_edge_start`. - blocks: HashSet, + pub(crate) blocks: HashSet, } pub(crate) struct Loops { /// The loops that failed to be unrolled so that we do not try to unroll them again. /// Each loop is identified by its header block id. - failed_to_unroll: HashSet, + pub(crate) failed_to_unroll: HashSet, pub(crate) yet_to_unroll: Vec, - modified_blocks: HashSet, + pub(crate) modified_blocks: HashSet, cfg: ControlFlowGraph, dom_tree: DominatorTree, } diff --git a/crates/noirc_evaluator/src/ssa/ssa_gen/context.rs b/crates/noirc_evaluator/src/ssa/ssa_gen/context.rs index b0831f4b453..5e6d71cae0d 100644 --- a/crates/noirc_evaluator/src/ssa/ssa_gen/context.rs +++ b/crates/noirc_evaluator/src/ssa/ssa_gen/context.rs @@ -188,7 +188,10 @@ impl<'a> FunctionContext<'a> { } ast::Type::Slice(elements) => { let element_types = Self::convert_type(elements).flatten(); - Tree::Branch(vec![Tree::Leaf(f(Type::field())), Tree::Leaf(f(Type::Slice(Rc::new(element_types))))]) + Tree::Branch(vec![ + Tree::Leaf(f(Type::field())), + Tree::Leaf(f(Type::Slice(Rc::new(element_types)))), + ]) } other => Tree::Leaf(f(Self::convert_non_tuple_type(other))), } @@ -575,24 +578,24 @@ impl<'a> FunctionContext<'a> { /// This will return a triple of (array, index, lvalue_ref, Option) where the lvalue_ref records the /// structure of the lvalue expression for use by `assign_new_value`. /// The optional max length is for the case where we are indexing a slice rather than an array as slices - /// are representing as the following tuple: (length, slice contents). + /// are representing as the following tuple: (length, slice contents). fn index_lvalue( &mut self, array: &ast::LValue, index: &ast::Expression, ) -> (ValueId, ValueId, LValue, Option) { - dbg!(array.clone()); + // dbg!(array.clone()); let (old_array, array_lvalue) = self.extract_current_value_recursive(array); - dbg!(array_lvalue.clone()); + // dbg!(array_lvalue.clone()); let index = self.codegen_non_tuple_expression(index); let array_lvalue = Box::new(array_lvalue); // A slice is represented as a tuple (length, slice contents) - // We need to fetch the second + // We need to fetch the second if old_array.count_leaves() > 1 { let slice_values = old_array.clone().into_value_list(self); - let slice_lvalue = LValue::SliceIndex { old_slice: old_array, index, slice_lvalue: array_lvalue }; + let slice_lvalue = + LValue::SliceIndex { old_slice: old_array, index, slice_lvalue: array_lvalue }; (slice_values[1], index, slice_lvalue, Some(slice_values[0])) - } else { let old_array = old_array.into_leaf().eval(self); (old_array, index, LValue::Index { old_array, index, array_lvalue }, None) @@ -607,7 +610,8 @@ impl<'a> FunctionContext<'a> { } ast::LValue::Index { array, index, element_type, location } => { let (old_array, index, index_lvalue, max_length) = self.index_lvalue(array, index); - let element = self.codegen_array_index(old_array, index, element_type, *location, max_length); + let element = + self.codegen_array_index(old_array, index, element_type, *location, max_length); (element, index_lvalue) } ast::LValue::MemberAccess { object, field_index: index } => { diff --git a/crates/noirc_evaluator/src/ssa/ssa_gen/mod.rs b/crates/noirc_evaluator/src/ssa/ssa_gen/mod.rs index 822a3b2779e..915192f1211 100644 --- a/crates/noirc_evaluator/src/ssa/ssa_gen/mod.rs +++ b/crates/noirc_evaluator/src/ssa/ssa_gen/mod.rs @@ -67,11 +67,7 @@ impl<'a> FunctionContext<'a> { Expression::Block(block) => self.codegen_block(block), Expression::Unary(unary) => self.codegen_unary(unary), Expression::Binary(binary) => self.codegen_binary(binary), - Expression::Index(index) => { - let values = self.codegen_index(index); - dbg!(values.clone()); - values - } + Expression::Index(index) => self.codegen_index(index), Expression::Cast(cast) => self.codegen_cast(cast), Expression::For(for_expr) => self.codegen_for(for_expr), Expression::If(if_expr) => self.codegen_if(if_expr), @@ -102,7 +98,7 @@ impl<'a> FunctionContext<'a> { /// to reassign to it. Note that mutable references `let x = &mut ...;` do not require this /// since they are not automatically loaded from and must be explicitly dereferenced. fn codegen_ident_reference(&mut self, ident: &ast::Ident) -> Values { - dbg!(ident.definition.clone()); + // dbg!(ident.definition.clone()); match &ident.definition { ast::Definition::Local(id) => self.lookup(*id), ast::Definition::Function(id) => self.get_or_queue_function(*id), @@ -128,18 +124,20 @@ impl<'a> FunctionContext<'a> { let typ = Self::convert_non_tuple_type(&array.typ); let new_convert_type = Self::convert_type(&array.typ); - dbg!(new_convert_type.clone()); + // dbg!(new_convert_type.clone()); if new_convert_type.count_leaves() > 1 { let slice_length = ast::Literal::Integer( (array.contents.len() as u128).into(), ast::Type::Field, ); let slice_length = self.codegen_literal(&slice_length); - let elements = vecmap(&array.contents, |element| self.codegen_expression(element)); + let elements = + vecmap(&array.contents, |element| self.codegen_expression(element)); let slice_contents = self.codegen_array(elements, typ); Tree::Branch(vec![slice_length, slice_contents]) } else { - let elements = vecmap(&array.contents, |element| self.codegen_expression(element)); + let elements = + vecmap(&array.contents, |element| self.codegen_expression(element)); self.codegen_array(elements, typ) } } @@ -258,21 +256,27 @@ impl<'a> FunctionContext<'a> { // let array = self.codegen_non_tuple_expression(&index.collection); // let index_value = self.codegen_non_tuple_expression(&index.index); // self.codegen_array_index(array, index_value, &index.element_type, index.location) - dbg!("codegen_index"); + // dbg!("codegen_index"); let array_or_slice = self.codegen_expression(&index.collection); // dbg!(array_or_slice.clone()); if array_or_slice.count_leaves() > 1 { let index_value = self.codegen_non_tuple_expression(&index.index); match &array_or_slice { Tree::Branch(values) => { - dbg!(values.clone()); - for value in values { - let x = value.clone().into_leaf().eval(self); - dbg!(&self.builder.current_function.dfg[x]); - } + // dbg!(values.clone()); + // for value in values { + // let x = value.clone().into_leaf().eval(self); + // dbg!(&self.builder.current_function.dfg[x]); + // } let slice_length = values[0].clone().into_leaf().eval(self); let slice = values[1].clone().into_leaf().eval(self); - self.codegen_array_index(slice, index_value, &index.element_type, index.location, Some(slice_length)) + self.codegen_array_index( + slice, + index_value, + &index.element_type, + index.location, + Some(slice_length), + ) } Tree::Leaf(_) => panic!("Nooo"), } @@ -297,11 +301,11 @@ impl<'a> FunctionContext<'a> { location: Location, max_length: Option, ) -> Values { - let array_value = &self.builder.current_function.dfg[array]; + // let array_value = &self.builder.current_function.dfg[array]; - dbg!(array_value.clone()); - let len = &self.builder.current_function.dfg.try_get_array_length(array); - dbg!(len); + // dbg!(array_value.clone()); + // let len = &self.builder.current_function.dfg.try_get_array_length(array); + // dbg!(len); // base_index = index * type_size let type_size = Self::convert_type(element_type).size_of_type(); @@ -317,22 +321,26 @@ impl<'a> FunctionContext<'a> { match array_type { Type::Slice(_) => { dbg!("got slice"); - let array_len = max_length.expect("ICE: a length must be supplied for indexing slices"); + let array_len = + max_length.expect("ICE: a length must be supplied for indexing slices"); // If the index and the array_len are both Fields we will not be able to perform a less than comparison on them // Thus, we cast the array len to a u64 before performing the less than comparison dbg!(array_len); - let array_len_int = self.builder.insert_cast(array_len, Type::Numeric(NumericType::Unsigned { bit_size: 64 })); - - let is_offset_out_of_bounds = self.builder.insert_binary(index, BinaryOp::Lt, array_len_int); + let array_len_int = self.builder.insert_cast( + array_len, + Type::Numeric(NumericType::Unsigned { bit_size: 64 }), + ); + + let is_offset_out_of_bounds = + self.builder.insert_binary(index, BinaryOp::Lt, array_len_int); self.builder.insert_constrain(is_offset_out_of_bounds); } Type::Array(..) => { // Nothing needs to done to prepare an array get on an array } _ => unreachable!("must have array or slice but got {array_type}"), - } - dbg!("about to insert array get"); + // dbg!("about to insert array get"); self.builder.insert_array_get(array, offset, typ).into() }) } From c8f9317d90b9f8083f5b6e6cb135584b18c7fe6d Mon Sep 17 00:00:00 2001 From: vezenovm Date: Fri, 11 Aug 2023 02:03:51 +0000 Subject: [PATCH 20/62] remove loops from mem2reg function context --- crates/noirc_evaluator/src/ssa/opt/mem2reg.rs | 56 +++++++++---------- 1 file changed, 26 insertions(+), 30 deletions(-) diff --git a/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs b/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs index f73b66a9839..5f14d62b425 100644 --- a/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs +++ b/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs @@ -67,7 +67,6 @@ struct PerFunctionContext { store_ids: Vec, cfg: ControlFlowGraph, dom_tree: DominatorTree, - loops: Loops, } impl PerFunctionContext { @@ -81,7 +80,6 @@ impl PerFunctionContext { store_ids: Vec::new(), cfg, dom_tree, - loops: find_all_loops(function), } } } @@ -190,15 +188,8 @@ impl PerFunctionContext { } } - // If no load values to substitute have been found in the current block, check the block's predecessors. - let predecessors = self.cfg.predecessors(block); - for predecessor in predecessors { - if self.dom_tree.is_reachable(predecessor) - && self.dom_tree.dominates(predecessor, block) - && !visited.contains(&predecessor) - { - stack.push(predecessor); - } + if let Some(predecessor) = self.block_has_predecessor(block, &visited) { + stack.push(predecessor); } } address @@ -223,14 +214,6 @@ impl PerFunctionContext { while let Some(block) = stack.pop() { visited.insert(block); - for l in self.loops.yet_to_unroll.iter() { - // We do not want to substitute loads that take place within loops as this pass - // can occur before loop unrolling - if block == l.header { - return false; - } - } - // Check whether there has been a store instruction in the current block // If there has been a store, add a load to be substituted. if let Some(last_value) = self.last_stores_with_block.get(&(address, block)) { @@ -245,22 +228,35 @@ impl PerFunctionContext { return true; } - // If no stores have been found in the current block, check the block's predecessors. - let predecessors = self.cfg.predecessors(block); - for predecessor in predecessors { - // TODO: Do I need is_reachable here? We are looping over only the reachable blocks but does - // that include a reachable block's predecessors? - if self.dom_tree.is_reachable(predecessor) - && self.dom_tree.dominates(predecessor, block) - && !visited.contains(&predecessor) - { - stack.push(predecessor); - } + if let Some(predecessor) = self.block_has_predecessor(block, &visited) { + stack.push(predecessor); } } false } + // If no loads or stores have been found in the current block, check the block's predecessors. + // Only check blocks with one predecessor as otherwise a constant value could be propogated + // through successor blocks with multiple branches that rely on other simplification passes + // such as loop unrolling or flattening of the CFG. + fn block_has_predecessor( + &mut self, + block_id: BasicBlockId, + visited: &HashSet, + ) -> Option { + let mut predecessors = self.cfg.predecessors(block_id); + if predecessors.len() == 1 { + let predecessor = predecessors.next().unwrap(); + if self.dom_tree.is_reachable(predecessor) + && self.dom_tree.dominates(predecessor, block_id) + && !visited.contains(&predecessor) + { + return Some(predecessor); + } + } + None + } + /// Checks whether the given value id refers to an allocation. fn value_is_from_allocation(value: ValueId, dfg: &DataFlowGraph) -> bool { match &dfg[value] { From a824ed3d2f3bcf8b5e2362bb95f208ed965f8daf Mon Sep 17 00:00:00 2001 From: vezenovm Date: Fri, 11 Aug 2023 02:41:25 +0000 Subject: [PATCH 21/62] remove import --- crates/noirc_evaluator/src/ssa/opt/mem2reg.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs b/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs index 5f14d62b425..191ed8a0d93 100644 --- a/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs +++ b/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs @@ -17,8 +17,6 @@ use crate::ssa::{ ssa_gen::Ssa, }; -use super::unrolling::{find_all_loops, Loops}; - impl Ssa { /// Attempts to remove any load instructions that recover values that are already available in /// scope, and attempts to remove stores that are subsequently redundant. From e345f8cced4943f68985951407a2d410c3b29772 Mon Sep 17 00:00:00 2001 From: jfecher Date: Fri, 11 Aug 2023 11:46:43 -0500 Subject: [PATCH 22/62] Update crates/noirc_evaluator/src/ssa/opt/mem2reg.rs --- crates/noirc_evaluator/src/ssa/opt/mem2reg.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs b/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs index 191ed8a0d93..0e75eb22231 100644 --- a/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs +++ b/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs @@ -52,7 +52,6 @@ impl Ssa { context.remove_unused_stores(&mut function.dfg, &all_protected_allocations, block); } } - dbg!("ended mem2reg"); self } } From fb53610927e748dd2461995f3f98cfe050f8ac50 Mon Sep 17 00:00:00 2001 From: jfecher Date: Fri, 11 Aug 2023 11:46:52 -0500 Subject: [PATCH 23/62] Update crates/noirc_evaluator/src/ssa/opt/mem2reg.rs --- crates/noirc_evaluator/src/ssa/opt/mem2reg.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs b/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs index 0e75eb22231..c020bcb65cc 100644 --- a/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs +++ b/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs @@ -22,7 +22,6 @@ impl Ssa { /// scope, and attempts to remove stores that are subsequently redundant. /// As long as they are not stores on memory used inside of loops pub(crate) fn mem2reg(mut self) -> Ssa { - dbg!("start mem2reg"); for function in self.functions.values_mut() { let mut all_protected_allocations = HashSet::new(); From e07c999d558867ce0c4bf1404a6c54d84c0f0733 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Sat, 12 Aug 2023 22:22:42 +0000 Subject: [PATCH 24/62] m em2reg debug work --- .../array_dynamic/Prover.toml | 3 +- .../array_dynamic/src/main.nr | 36 +-- .../execution_success/references/src/main.nr | 187 ++++++++++++--- .../execution_success/regression/src/main.nr | 1 + .../execution_success/sha2_blocks/src/main.nr | 26 +- crates/noirc_evaluator/src/ssa.rs | 4 + crates/noirc_evaluator/src/ssa/ir/cfg.rs | 9 +- .../noirc_evaluator/src/ssa/ir/post_order.rs | 1 + crates/noirc_evaluator/src/ssa/opt/mem2reg.rs | 225 +++++++++++++++++- .../noirc_evaluator/src/ssa/opt/unrolling.rs | 4 +- 10 files changed, 412 insertions(+), 84 deletions(-) diff --git a/crates/nargo_cli/tests/execution_success/array_dynamic/Prover.toml b/crates/nargo_cli/tests/execution_success/array_dynamic/Prover.toml index 750b3129ec9..957a044a1a5 100644 --- a/crates/nargo_cli/tests/execution_success/array_dynamic/Prover.toml +++ b/crates/nargo_cli/tests/execution_success/array_dynamic/Prover.toml @@ -1,5 +1,6 @@ x = [104, 101, 108, 108, 111] -z = "59" +# z = "59" +z = "4" t = "10" index = [0,1,2,3,4] index2 = [0,1,2,3,4] diff --git a/crates/nargo_cli/tests/execution_success/array_dynamic/src/main.nr b/crates/nargo_cli/tests/execution_success/array_dynamic/src/main.nr index b27c96b30c3..d9411bfb5fe 100644 --- a/crates/nargo_cli/tests/execution_success/array_dynamic/src/main.nr +++ b/crates/nargo_cli/tests/execution_success/array_dynamic/src/main.nr @@ -1,32 +1,38 @@ fn main(x: [u32; 5], mut z: u32, t: u32, index: [Field;5], index2: [Field;5], offset: Field, sublen: Field) { - let idx = (z - 5*t - 5) as Field; + // let idx = (z - 5*t - 5) as Field; + let idx = z as Field; + // dep::std::println(idx); //dynamic array test dyn_array(x, idx, idx - 3); //regression for issue 1283 - let mut s = 0; - let x3 = [246,159,32,176,8]; - for i in 0..5 { - s += x3[index[i]]; - } - assert(s!=0); + // let mut s = 0; + // let x3 = [246,159,32,176,8]; + // for i in 0..5 { + // s += x3[index[i]]; + // } + // assert(s!=0); - if 3 < (sublen as u32) { - assert(index[offset + 3] == index2[3]); - } + // if 3 < (sublen as u32) { + // assert(index[offset + 3] == index2[3]); + // } } fn dyn_array(mut x: [u32; 5], y: Field, z: Field) { - assert(x[y] == 111); - assert(x[z] == 101); - x[z] = 0; - assert(x[y] == 111); - assert(x[1] == 0); + // assert(x[y] == 111); + // assert(x[z] == 101); + // x[z] = 0; + // assert(x[y] == 111); + // assert(x[1] == 0); if y as u32 < 10 { + // dep::std::println(x); + let q = x[y] - 2; + // dep::std::println(q); x[y] = x[y] - 2; } else { x[y] = 0; } + // dep::std::println(x); assert(x[4] == 109); } \ No newline at end of file diff --git a/crates/nargo_cli/tests/execution_success/references/src/main.nr b/crates/nargo_cli/tests/execution_success/references/src/main.nr index a6fc5433fd2..e8ee19e33f0 100644 --- a/crates/nargo_cli/tests/execution_success/references/src/main.nr +++ b/crates/nargo_cli/tests/execution_success/references/src/main.nr @@ -1,41 +1,48 @@ fn main(mut x: Field) { - regression_2218(x, 10); - - add1(&mut x); - assert(x == 3); - - let mut s = S { y: x }; - s.add2(); - assert(s.y == 5); - - // Regression for #1946: Method resolution error when calling &mut methods with a variable of type &mut T - let s_ref = &mut s; - s_ref.add2(); - assert(s.y == 7); - - // Test that normal mutable variables are still copied - let mut a = 0; - mutate_copy(a); - assert(a == 0); - - // Test something 3 allocations deep - let mut nested_allocations = Nested { y: &mut &mut 0 }; - add1(*nested_allocations.y); - assert(**nested_allocations.y == 1); - - // Test nested struct allocations with a mutable reference to an array. - let mut c = C { - foo: 0, - bar: &mut C2 { - array: &mut [1, 2], - }, - }; - *c.bar.array = [3, 4]; - assert(*c.bar.array == [3, 4]); - - regression_1887(); - regression_2054(); - regression_2030(); + // add1(&mut x); + // assert(x == 3); + + // let mut s = S { y: x }; + // s.add2(); + // assert(s.y == 5); + + // // Regression for #1946: Method resolution error when calling &mut methods with a variable of type &mut T + // let s_ref = &mut s; + // s_ref.add2(); + // assert(s.y == 7); + + // // Test that normal mutable variables are still copied + // let mut a = 0; + // mutate_copy(a); + // assert(a == 0); + + // // Test something 3 allocations deep + // let mut nested_allocations = Nested { y: &mut &mut 0 }; + // add1(*nested_allocations.y); + // assert(**nested_allocations.y == 1); + + // // Test nested struct allocations with a mutable reference to an array. + // let mut c = C { + // foo: 0, + // bar: &mut C2 { + // array: &mut [1, 2], + // }, + // }; + // *c.bar.array = [3, 4]; + // assert(*c.bar.array == [3, 4]); + + // regression_1887(); + // regression_2054(); + // regression_2030(); + + + // Compile time works + // regression_2218(5, 10); + // This breaks, thus we should fix our mem2reg here + // regression_2218(x, 10); + regression_2218_old(x, 10); + // regression_2218_loop(x, 10); + // regression_2221(true); } fn add1(x: &mut Field) { @@ -111,8 +118,112 @@ fn regression_2218(x: Field, y: Field) { if x != 20 { *q1 = 10; *q2 = 2; // now we'd expect q1 == q2 == 2 - + // dep::std::println(*q1); assert(*q1 == 2); + } else { + *q2 = 15; + assert(*q1 == 15); + } + } + // TODO: this breaks mem2reg per func + else { + *q2 = 20; + assert(*q1 == 20); + } + // dep::std::println(*q1); + assert(*q1 == 2); +} + +fn regression_2218_old(x: Field, y: Field) { + let q = &mut &mut 0; + let q1 = *q; + let q2 = *q; + + if x != y { + *q1 = 1; + // Make sure that we correct load reference aliases through multiple blocks + if x != 20 { + *q1 = 10; + *q2 = 2; // now we'd expect q1 == q2 == 2 + // dep::std::println(*q1); + assert(*q1 == 2); } } + // dep::std::println(*q1); + assert(*q1 == 2); } + +fn regression_2218_loop(x: Field, y: Field) { + let q = &mut &mut 0; + let q1 = *q; + let q2 = *q; + + for _ in 0..1 { + if x != y { + *q1 = 10; + *q2 = 2; // now we'd expect q1 == q2 == 2 + + assert(*q1 == 2); + } + } + // dep::std::println(*q1); + assert(*q1 == 2); + + // for _ in 0..1 { + // for _ in 0..5 { + // if x != y { + // *q1 = 1; + // // Make sure that we correct load reference aliases through multiple blocks + // if x != 20 { + // *q1 = 10; + // *q2 = 2; // now we'd expect q1 == q2 == 2 + + // assert(*q1 == 2); + // } + // } + // } + // if x != y { + // *q1 = 1; + // for _ in 0..5 { + // // Make sure that we correct load reference aliases through multiple blocks + // if x != 20 { + // *q1 = 10; + // *q2 = 2; // now we'd expect q1 == q2 == 2 + + // assert(*q1 == 2); + // } + // } + // } + // } + // assert(*q1 == 2); + + // if x != y { + // for _ in 0..5 { + // if x != y { + // *q1 = 1; + // // Make sure that we correct load reference aliases through multiple blocks + // if x != 20 { + // *q1 = 10; + // *q2 = 2; // now we'd expect q1 == q2 == 2 + + // assert(*q1 == 2); + // } + // } + // } + // } + // assert(*q1 == 2); +} + +fn regression_2221(x: bool) { + let ref = &mut 0; + let mut array = [ref, ref]; + + if x { + *array[0] = 1; + } else { + *array[0] = 2; + } + + assert(*array[0] == 1); + // dep::std::println(*array[0]); +} \ No newline at end of file diff --git a/crates/nargo_cli/tests/execution_success/regression/src/main.nr b/crates/nargo_cli/tests/execution_success/regression/src/main.nr index 54769c39709..103161482b4 100644 --- a/crates/nargo_cli/tests/execution_success/regression/src/main.nr +++ b/crates/nargo_cli/tests/execution_success/regression/src/main.nr @@ -76,6 +76,7 @@ fn main(x: [u8; 5], z: Field) //Issue 1144 let (nib, len) = compact_decode(x,z); assert(len == 5); + // dep::std::println(nib); assert([nib[0], nib[1], nib[2], nib[3], nib[4]] == [15, 1, 12, 11, 8]); // Issue 1169 diff --git a/crates/nargo_cli/tests/execution_success/sha2_blocks/src/main.nr b/crates/nargo_cli/tests/execution_success/sha2_blocks/src/main.nr index fcdcdb8684f..f1afea12dae 100644 --- a/crates/nargo_cli/tests/execution_success/sha2_blocks/src/main.nr +++ b/crates/nargo_cli/tests/execution_success/sha2_blocks/src/main.nr @@ -3,20 +3,20 @@ use dep::std; fn main(x: [u8; 3], result256: [u8; 32], result512: [u8; 64]) { - // One-block tests. - let mut digest256 = std::sha256::digest(x); - assert(digest256 == result256); + // // One-block tests. + // let mut digest256 = std::sha256::digest(x); + // assert(digest256 == result256); - let mut digest512 = std::sha512::digest(x); - assert(digest512 == result512); + // let mut digest512 = std::sha512::digest(x); + // assert(digest512 == result512); - // Two-block SHA256 test. Taken from https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Standards-and-Guidelines/documents/examples/SHA256.pdf - let y: [u8; 56] = [97,98,99,100,98,99,100,101,99,100,101,102,100,101,102,103,101,102,103,104,102,103,104,105,103,104,105,106,104,105,106,107,105,106,107,108,106,107,108,109,107,108,109,110,108,109,110,111,109,110,111,112,110,111,112,113]; // "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" - digest256 = std::sha256::digest(y); - assert(digest256 == [36,141,106,97,210,6,56,184,229,192,38,147,12,62,96,57,163,60,228,89,100,255,33,103,246,236,237,212,25,219,6,193]); + // // Two-block SHA256 test. Taken from https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Standards-and-Guidelines/documents/examples/SHA256.pdf + // let y: [u8; 56] = [97,98,99,100,98,99,100,101,99,100,101,102,100,101,102,103,101,102,103,104,102,103,104,105,103,104,105,106,104,105,106,107,105,106,107,108,106,107,108,109,107,108,109,110,108,109,110,111,109,110,111,112,110,111,112,113]; // "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" + // digest256 = std::sha256::digest(y); + // assert(digest256 == [36,141,106,97,210,6,56,184,229,192,38,147,12,62,96,57,163,60,228,89,100,255,33,103,246,236,237,212,25,219,6,193]); - // Two-block SHA256 test. Taken from https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Standards-and-Guidelines/documents/examples/SHA512.pdf - let z: [u8; 112] = [97,98,99,100,101,102,103,104,98,99,100,101,102,103,104,105,99,100,101,102,103,104,105,106,100,101,102,103,104,105,106,107,101,102,103,104,105,106,107,108,102,103,104,105,106,107,108,109,103,104,105,106,107,108,109,110,104,105,106,107,108,109,110,111,105,106,107,108,109,110,111,112,106,107,108,109,110,111,112,113,107,108,109,110,111,112,113,114,108,109,110,111,112,113,114,115,109,110,111,112,113,114,115,116,110,111,112,113,114,115,116,117]; // "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu" - digest512 = std::sha512::digest(z); - assert(digest512 == [142,149,155,117,218,227,19,218,140,244,247,40,20,252,20,63,143,119,121,198,235,159,127,161,114,153,174,173,182,136,144,24,80,29,40,158,73,0,247,228,51,27,153,222,196,181,67,58,199,211,41,238,182,221,38,84,94,150,229,91,135,75,233,9]); + // // Two-block SHA256 test. Taken from https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Standards-and-Guidelines/documents/examples/SHA512.pdf + // let z: [u8; 112] = [97,98,99,100,101,102,103,104,98,99,100,101,102,103,104,105,99,100,101,102,103,104,105,106,100,101,102,103,104,105,106,107,101,102,103,104,105,106,107,108,102,103,104,105,106,107,108,109,103,104,105,106,107,108,109,110,104,105,106,107,108,109,110,111,105,106,107,108,109,110,111,112,106,107,108,109,110,111,112,113,107,108,109,110,111,112,113,114,108,109,110,111,112,113,114,115,109,110,111,112,113,114,115,116,110,111,112,113,114,115,116,117]; // "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu" + // digest512 = std::sha512::digest(z); + // assert(digest512 == [142,149,155,117,218,227,19,218,140,244,247,40,20,252,20,63,143,119,121,198,235,159,127,161,114,153,174,173,182,136,144,24,80,29,40,158,73,0,247,228,51,27,153,222,196,181,67,58,199,211,41,238,182,221,38,84,94,150,229,91,135,75,233,9]); } diff --git a/crates/noirc_evaluator/src/ssa.rs b/crates/noirc_evaluator/src/ssa.rs index e5009607ee9..6e67315d0a0 100644 --- a/crates/noirc_evaluator/src/ssa.rs +++ b/crates/noirc_evaluator/src/ssa.rs @@ -56,6 +56,10 @@ pub(crate) fn optimize_into_acir( .print(print_ssa_passes, "After Unrolling:") .simplify_cfg() .print(print_ssa_passes, "After Simplifying:") + // Run mem2reg before flattening to handle any promotion + // of values that can be accessed after loop unrolling + .mem2reg() + .print(print_ssa_passes, "After Mem2Reg:") .flatten_cfg() .print(print_ssa_passes, "After Flattening:") // Run mem2reg once more with the flattened CFG to catch any remaining loads/stores diff --git a/crates/noirc_evaluator/src/ssa/ir/cfg.rs b/crates/noirc_evaluator/src/ssa/ir/cfg.rs index 8faa0fa8565..26b473eb068 100644 --- a/crates/noirc_evaluator/src/ssa/ir/cfg.rs +++ b/crates/noirc_evaluator/src/ssa/ir/cfg.rs @@ -1,4 +1,4 @@ -use std::collections::{HashMap, HashSet}; +use std::collections::{HashMap, BTreeSet}; use super::{ basic_block::{BasicBlock, BasicBlockId}, @@ -10,13 +10,14 @@ use super::{ struct CfgNode { /// Set of blocks that containing jumps that target this block. /// The predecessor set has no meaningful order. - pub(crate) predecessors: HashSet, + pub(crate) predecessors: BTreeSet, /// Set of blocks that are the targets of jumps in this block. /// The successors set has no meaningful order. - pub(crate) successors: HashSet, + pub(crate) successors: BTreeSet, } +#[derive(Clone)] /// The Control Flow Graph maintains a mapping of blocks to their predecessors /// and successors where predecessors are basic blocks and successors are /// basic blocks. @@ -31,7 +32,7 @@ impl ControlFlowGraph { // therefore we must ensure that a node exists for the entry block, regardless of whether // it later comes to describe any edges after calling compute. let entry_block = func.entry_block(); - let empty_node = CfgNode { predecessors: HashSet::new(), successors: HashSet::new() }; + let empty_node = CfgNode { predecessors: BTreeSet::new(), successors: BTreeSet::new() }; let data = HashMap::from([(entry_block, empty_node)]); let mut cfg = ControlFlowGraph { data }; diff --git a/crates/noirc_evaluator/src/ssa/ir/post_order.rs b/crates/noirc_evaluator/src/ssa/ir/post_order.rs index e3bdbd491df..e405fc46042 100644 --- a/crates/noirc_evaluator/src/ssa/ir/post_order.rs +++ b/crates/noirc_evaluator/src/ssa/ir/post_order.rs @@ -13,6 +13,7 @@ enum Visit { Last, } +#[derive(Clone)] pub(crate) struct PostOrder(Vec); impl PostOrder { diff --git a/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs b/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs index 191ed8a0d93..9c82804725e 100644 --- a/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs +++ b/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs @@ -17,6 +17,8 @@ use crate::ssa::{ ssa_gen::Ssa, }; +use super::unrolling::{Loops, find_all_loops}; + impl Ssa { /// Attempts to remove any load instructions that recover values that are already available in /// scope, and attempts to remove stores that are subsequently redundant. @@ -27,8 +29,22 @@ impl Ssa { let mut all_protected_allocations = HashSet::new(); let mut context = PerFunctionContext::new(function); - - for block in function.reachable_blocks() { + // let post_order_slice = .iter().rev(); + let post_order = PostOrder::with_function(function); + let post_order_slice = post_order.as_slice(); + + for b in post_order_slice.iter().rev() { + println!("BLOCK: {b}"); + let predecessors = context.cfg.predecessors(*b); + for p in predecessors { + println!("predecessor: {p}"); + } + } + dbg!(post_order_slice); + // TODO: for array_dynamic the normal post_order works, but for regression_2218 we need it reverse + let post_order_iter = post_order_slice.iter().rev(); + // let post_order_iter = post_order_slice.iter(); + for block in post_order_iter.clone() { // Maps Load instruction id -> value to replace the result of the load with let mut loads_to_substitute_per_block = BTreeMap::new(); @@ -40,16 +56,26 @@ impl Ssa { &mut function.dfg, &mut loads_to_substitute_per_block, &mut load_values_to_substitute, - block, + *block, ); + // dbg!(all_protected_allocations.clone()); all_protected_allocations.extend(allocations_protected_by_block.into_iter()); + + // Broken strategy because the alias will not be recognized, just don't mem2reg compile time blocks + // let remaining_loads = context.find_remaining_loads(&mut function.dfg, *block); + // dbg!(remaining_loads.clone()); + // let mut new_remaining_loads = HashSet::new(); + // for load in remaining_loads.clone() { + // let result_value = function.dfg.resolve(load); + // new_remaining_loads.insert(result_value); + // } + // dbg!(new_remaining_loads.clone()); + // all_protected_allocations.extend(remaining_loads.into_iter()); + // all_protected_allocations.extend(new_remaining_loads.into_iter()); } - // Now that we have a comprehensive list of used allocations across all the - // function's blocks, it is safe to remove any stores that do not touch such - // allocations. - for block in function.reachable_blocks() { - context.remove_unused_stores(&mut function.dfg, &all_protected_allocations, block); + for block in post_order_iter { + context.remove_unused_stores(&mut function.dfg, &all_protected_allocations, *block); } } dbg!("ended mem2reg"); @@ -64,7 +90,9 @@ struct PerFunctionContext { load_values_to_substitute_per_func: BTreeMap, store_ids: Vec, cfg: ControlFlowGraph, + post_order: PostOrder, dom_tree: DominatorTree, + loops: Loops, } impl PerFunctionContext { @@ -77,7 +105,9 @@ impl PerFunctionContext { load_values_to_substitute_per_func: BTreeMap::new(), store_ids: Vec::new(), cfg, + post_order, dom_tree, + loops: find_all_loops(function), } } } @@ -99,18 +129,43 @@ impl PerFunctionContext { ) -> HashSet { let mut protected_allocations = HashSet::new(); let block = &dfg[block_id]; + // println!("BLOCK: {block_id}"); for instruction_id in block.instructions() { + // dbg!(&dfg[*instruction_id]); match &dfg[*instruction_id] { Instruction::Store { mut address, value } => { + println!("STORE: {address}"); address = self.fetch_load_value_to_substitute(block_id, address); + println!("address after: {address}"); + // println!("about to insert store: {address}, block_id: {block_id}, value: {value}"); self.last_stores_with_block.insert((address, block_id), *value); self.store_ids.push(*instruction_id); } Instruction::Load { mut address } => { - address = self.fetch_load_value_to_substitute(block_id, address); + // dbg!(load_values_to_substitute_per_block.get(&address).is_some()); + println!("LOAD: {address}"); + address = self.fetch_load_value_to_substitute(block_id, address); + println!("address after: {address}"); + + let predecessors = self.cfg.predecessors(block_id); + let p_len = predecessors.len(); + dbg!(p_len); + drop(predecessors); + + // let mut found_last_value = false; + // if p_len != 2 { + // found_last_value = self.find_load_to_substitute( + // block_id, + // address, + // dfg, + // instruction_id, + // loads_to_substitute, + // load_values_to_substitute_per_block, + // ); + // } let found_last_value = self.find_load_to_substitute( block_id, address, @@ -119,8 +174,51 @@ impl PerFunctionContext { loads_to_substitute, load_values_to_substitute_per_block, ); + dbg!(found_last_value); if !found_last_value { + // We want to protect allocations that do not have a load to substitute + dbg!(address); + let resolved_address = dfg.resolve(address); + dbg!(resolved_address); protected_allocations.insert(address); + + // Need this to solve regression 2218 with loops + let found_last_value = self.find_load_to_substitute( + block_id, + resolved_address, + dfg, + instruction_id, + loads_to_substitute, + load_values_to_substitute_per_block, + ); + dbg!(found_last_value); + if found_last_value { + protected_allocations.insert(resolved_address); + } + + // We also want to check for allocations that share a value we are substituting + // with the one we are protecting. + // This check prevents the pass from removing stores to reference aliases across + // multiple blocks. + // TODO: this feels like a cop-out where I am restricting + // functionality so the pass has to be run again rather than checking stores across blocks + // but it may be needed that we can only do these removals after flattening + // NOTE: not the cause of the extra allocate in regression test + if let Some((value, outer_block)) = + self.load_values_to_substitute_per_func.get(&address) + { + println!("value: {value}"); + for (address, (inner_value, block)) in &self.load_values_to_substitute_per_func { + println!("inner_value: {inner_value}"); + println!("address: {address}"); + dbg!(*inner_value == *value); + dbg!(*outer_block == *block); + if *inner_value == *value && *outer_block == *block { + dbg!(address); + protected_allocations.insert(*address); + } + } + } } } Instruction::Call { arguments, .. } => { @@ -161,7 +259,25 @@ impl PerFunctionContext { protected_allocations } - // This method finds the load values to substitute for a given address + fn find_remaining_loads( + &mut self, + dfg: &mut DataFlowGraph, + block_id: BasicBlockId, + ) -> HashSet { + let mut protected_allocations = HashSet::new(); + let block = &dfg[block_id]; + + for instruction_id in block.instructions() { + if let Instruction::Load { address } = &dfg[*instruction_id] { + protected_allocations.insert(*address); + + } + } + + protected_allocations + } + + // This method will fetch already saved load values to substitute for a given address. // The search starts at the block supplied as a parameter. If there is not a load to substitute // the CFG is analyzed to determine whether a predecessor block has a load value to substitute. // If there is no load value to substitute the original address is returned. @@ -176,6 +292,22 @@ impl PerFunctionContext { while let Some(block) = stack.pop() { visited.insert(block); + // TODO: check that any of the predecssor blocks are loops + for l in self.loops.yet_to_unroll.iter() { + // We do not want to substitute loads that take place within loops as this pass + // can occur before loop unrolling + // The pass should be ran again following loop unrolling as new values + if l.blocks.contains(&block) { + return address; + } + } + + // let predecessors = self.cfg.predecessors(block); + // dbg!(predecessors.len()); + // if predecessors.len() == 2 { + // return address; + // } + // Check whether there is a load value to substitute in the current block. // Return the value if found. if let Some((value, load_block_id)) = @@ -189,11 +321,27 @@ impl PerFunctionContext { if let Some(predecessor) = self.block_has_predecessor(block, &visited) { stack.push(predecessor); } + + // If no load values to substitute have been found in the current block, check the block's predecessors. + // let predecessors = self.cfg.predecessors(block); + // for predecessor in predecessors { + // // if self.dom_tree.is_reachable(predecessor) + // // && self.dom_tree.dominates(predecessor, block) + // // && !visited.contains(&predecessor) { + // // stack.push(predecessor); + // // } + // if self.dom_tree.is_reachable(predecessor) + // && !visited.contains(&predecessor) + // { + // stack.push(predecessor); + // } + // } } address } - // This method determines which loads should be substituted. + // This method determines which loads should be substituted and saves them + // to be substituted further in the pass. // Starting at the block supplied as a parameter, we check whether a store has occurred with the given address. // If no store has occurred in the supplied block, the CFG is analyzed to determine whether // a predecessor block has a store at the given address. @@ -210,25 +358,74 @@ impl PerFunctionContext { let mut visited = HashSet::new(); while let Some(block) = stack.pop() { + println!("BLOCK in find_load_to_substitute: {block}"); + // let predecessors = self.cfg.predecessors(block); + // for predecessor in predecessors { + // println!("predecessor: {predecessor}"); + // } visited.insert(block); + for l in self.loops.yet_to_unroll.iter() { + // We do not want to substitute loads that take place within loops as this pass + // can occur before loop unrolling + // The pass should be ran again following loop unrolling as new values + // dbg!(l.blocks.clone()); + if l.blocks.contains(&block) { + dbg!(block); + return false; + } + } + + // let predecessors = self.cfg.clone().predecessors(block_id); + // dbg!(predecessors.len()); + // if predecessors.len() == 2 { + // return false; + // } + // Check whether there has been a store instruction in the current block // If there has been a store, add a load to be substituted. if let Some(last_value) = self.last_stores_with_block.get(&(address, block)) { + // println!("last_value: {last_value}"); let result_value = *dfg .instruction_results(*instruction_id) .first() .expect("ICE: Load instructions should have single result"); + // println!("result_value: {result_value}"); loads_to_substitute.insert(*instruction_id, *last_value); load_values_to_substitute_per_block.insert(result_value, *last_value); self.load_values_to_substitute_per_func.insert(result_value, (*last_value, block)); return true; } + // TODO: some situations we only need to check block with one predecessor and in some need to check more if let Some(predecessor) = self.block_has_predecessor(block, &visited) { stack.push(predecessor); } + + // If no load values to substitute have been found in the current block, check the block's predecessors. + // let predecessors = self.cfg.predecessors(block); + // // dbg!(predecessors.len()); + // for predecessor in predecessors { + // // dbg!(predecessor); + // // check out the post order here see if there is a match + // // perhaps it is not dominating as it doesn't have to flow through + // // dbg!(self.dom_tree.dominates(predecessor, block)); + + // // Need this for array_dynamic + // // if self.dom_tree.is_reachable(predecessor) + // // && self.dom_tree.dominates(predecessor, block) + // // && !visited.contains(&predecessor) { + // // stack.push(predecessor); + // // } + // // TODO: references regression 2218 with loops working with this and blocks constructed from successors + // // only a few tests failing after that + // if self.dom_tree.is_reachable(predecessor) + // && !visited.contains(&predecessor) + // { + // stack.push(predecessor); + // } + // } } false } @@ -243,6 +440,7 @@ impl PerFunctionContext { visited: &HashSet, ) -> Option { let mut predecessors = self.cfg.predecessors(block_id); + // dbg!(predecessors.len()); if predecessors.len() == 1 { let predecessor = predecessors.next().unwrap(); if self.dom_tree.is_reachable(predecessor) @@ -251,6 +449,11 @@ impl PerFunctionContext { { return Some(predecessor); } + // if self.dom_tree.is_reachable(predecessor) + // && !visited.contains(&predecessor) + // { + // return Some(predecessor); + // } } None } diff --git a/crates/noirc_evaluator/src/ssa/opt/unrolling.rs b/crates/noirc_evaluator/src/ssa/opt/unrolling.rs index 20df50d5780..def9d2fa20b 100644 --- a/crates/noirc_evaluator/src/ssa/opt/unrolling.rs +++ b/crates/noirc_evaluator/src/ssa/opt/unrolling.rs @@ -49,14 +49,14 @@ impl Ssa { pub(crate) struct Loop { /// The header block of a loop is the block which dominates all the /// other blocks in the loop. - pub(crate) header: BasicBlockId, + header: BasicBlockId, /// The start of the back_edge n -> d is the block n at the end of /// the loop that jumps back to the header block d which restarts the loop. back_edge_start: BasicBlockId, /// All the blocks contained within the loop, including `header` and `back_edge_start`. - blocks: HashSet, + pub(crate) blocks: HashSet, } pub(crate) struct Loops { From 265b88381b088110ba156081070180241e2536b9 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Tue, 15 Aug 2023 14:36:40 +0000 Subject: [PATCH 25/62] working func mem2reg for various reference alias cases --- .../execution_success/references/src/main.nr | 232 ++++++++-------- crates/noirc_evaluator/src/ssa.rs | 2 +- crates/noirc_evaluator/src/ssa/ir/cfg.rs | 2 +- crates/noirc_evaluator/src/ssa/opt/mem2reg.rs | 254 +++++------------- 4 files changed, 187 insertions(+), 303 deletions(-) diff --git a/crates/nargo_cli/tests/execution_success/references/src/main.nr b/crates/nargo_cli/tests/execution_success/references/src/main.nr index e8ee19e33f0..f5ac47c65a2 100644 --- a/crates/nargo_cli/tests/execution_success/references/src/main.nr +++ b/crates/nargo_cli/tests/execution_success/references/src/main.nr @@ -1,48 +1,46 @@ fn main(mut x: Field) { - // add1(&mut x); - // assert(x == 3); - - // let mut s = S { y: x }; - // s.add2(); - // assert(s.y == 5); - - // // Regression for #1946: Method resolution error when calling &mut methods with a variable of type &mut T - // let s_ref = &mut s; - // s_ref.add2(); - // assert(s.y == 7); - - // // Test that normal mutable variables are still copied - // let mut a = 0; - // mutate_copy(a); - // assert(a == 0); - - // // Test something 3 allocations deep - // let mut nested_allocations = Nested { y: &mut &mut 0 }; - // add1(*nested_allocations.y); - // assert(**nested_allocations.y == 1); - - // // Test nested struct allocations with a mutable reference to an array. - // let mut c = C { - // foo: 0, - // bar: &mut C2 { - // array: &mut [1, 2], - // }, - // }; - // *c.bar.array = [3, 4]; - // assert(*c.bar.array == [3, 4]); - - // regression_1887(); - // regression_2054(); - // regression_2030(); - - - // Compile time works - // regression_2218(5, 10); - // This breaks, thus we should fix our mem2reg here - // regression_2218(x, 10); - regression_2218_old(x, 10); - // regression_2218_loop(x, 10); - // regression_2221(true); + add1(&mut x); + assert(x == 3); + + let mut s = S { y: x }; + s.add2(); + assert(s.y == 5); + + // Regression for #1946: Method resolution error when calling &mut methods with a variable of type &mut T + let s_ref = &mut s; + s_ref.add2(); + assert(s.y == 7); + + // Test that normal mutable variables are still copied + let mut a = 0; + mutate_copy(a); + assert(a == 0); + + // Test something 3 allocations deep + let mut nested_allocations = Nested { y: &mut &mut 0 }; + add1(*nested_allocations.y); + assert(**nested_allocations.y == 1); + + // Test nested struct allocations with a mutable reference to an array. + let mut c = C { + foo: 0, + bar: &mut C2 { + array: &mut [1, 2], + }, + }; + *c.bar.array = [3, 4]; + assert(*c.bar.array == [3, 4]); + + regression_1887(); + regression_2054(); + regression_2030(); + + // x == 3 at this point + regression_2218_if_inner_if(x, 10); + regression_2218_if_inner_else(20, x); + regression_2218_else(x, 3); + + regression_2218_loop(x, 10); } fn add1(x: &mut Field) { @@ -107,7 +105,7 @@ fn regression_2030() { *array[0] = 1; } -fn regression_2218(x: Field, y: Field) { +fn regression_2218(x: Field, y: Field) -> Field { let q = &mut &mut 0; let q1 = *q; let q2 = *q; @@ -118,39 +116,34 @@ fn regression_2218(x: Field, y: Field) { if x != 20 { *q1 = 10; *q2 = 2; // now we'd expect q1 == q2 == 2 - // dep::std::println(*q1); + assert(*q1 == 2); } else { *q2 = 15; assert(*q1 == 15); } - } - // TODO: this breaks mem2reg per func - else { + } else { *q2 = 20; assert(*q1 == 20); } - // dep::std::println(*q1); - assert(*q1 == 2); + // Have to assign value to return it + let value = *q1; + value } -fn regression_2218_old(x: Field, y: Field) { - let q = &mut &mut 0; - let q1 = *q; - let q2 = *q; +fn regression_2218_if_inner_if(x: Field, y: Field) { + let value = regression_2218(x, y); + assert(value == 2); +} - if x != y { - *q1 = 1; - // Make sure that we correct load reference aliases through multiple blocks - if x != 20 { - *q1 = 10; - *q2 = 2; // now we'd expect q1 == q2 == 2 - // dep::std::println(*q1); - assert(*q1 == 2); - } - } - // dep::std::println(*q1); - assert(*q1 == 2); +fn regression_2218_if_inner_else(x: Field, y: Field) { + let value = regression_2218(x, y); + assert(value == 15); +} + +fn regression_2218_else(x: Field, y: Field) { + let value = regression_2218(x, y); + assert(value == 20); } fn regression_2218_loop(x: Field, y: Field) { @@ -164,66 +157,63 @@ fn regression_2218_loop(x: Field, y: Field) { *q2 = 2; // now we'd expect q1 == q2 == 2 assert(*q1 == 2); - } + } else { + *q2 = 20; + assert(*q1 == 20); + } } - // dep::std::println(*q1); assert(*q1 == 2); - // for _ in 0..1 { - // for _ in 0..5 { - // if x != y { - // *q1 = 1; - // // Make sure that we correct load reference aliases through multiple blocks - // if x != 20 { - // *q1 = 10; - // *q2 = 2; // now we'd expect q1 == q2 == 2 + for _ in 0..1 { + for _ in 0..5 { + if x != y { + *q1 = 1; + // Make sure that we correct load reference aliases through multiple blocks + if x != 20 { + *q1 = 10; + *q2 = 2; // now we'd expect q1 == q2 == 2 - // assert(*q1 == 2); - // } - // } - // } - // if x != y { - // *q1 = 1; - // for _ in 0..5 { - // // Make sure that we correct load reference aliases through multiple blocks - // if x != 20 { - // *q1 = 10; - // *q2 = 2; // now we'd expect q1 == q2 == 2 + assert(*q1 == 2); + } + } else { + *q2 = 20; + assert(*q1 == 20); + } + } + if x != y { + *q1 = 1; + for _ in 0..5 { + // Make sure that we correct load reference aliases through multiple blocks + if x != 20 { + *q1 = 10; + *q2 = 2; // now we'd expect q1 == q2 == 2 - // assert(*q1 == 2); - // } - // } - // } - // } - // assert(*q1 == 2); - - // if x != y { - // for _ in 0..5 { - // if x != y { - // *q1 = 1; - // // Make sure that we correct load reference aliases through multiple blocks - // if x != 20 { - // *q1 = 10; - // *q2 = 2; // now we'd expect q1 == q2 == 2 - - // assert(*q1 == 2); - // } - // } - // } - // } - // assert(*q1 == 2); -} - -fn regression_2221(x: bool) { - let ref = &mut 0; - let mut array = [ref, ref]; + assert(*q1 == 2); + } + } + } else { + *q2 = 20; + assert(*q1 == 20); + } + } + assert(*q1 == 2); - if x { - *array[0] = 1; + if x != y { + for _ in 0..5 { + if x != y { + *q1 = 1; + // Make sure that we correct load reference aliases through multiple blocks + if x != 20 { + *q1 = 10; + *q2 = 2; // now we'd expect q1 == q2 == 2 + + assert(*q1 == 2); + } + } + } } else { - *array[0] = 2; + *q2 = 20; + assert(*q1 == 20); } - - assert(*array[0] == 1); - // dep::std::println(*array[0]); + assert(*q1 == 2); } \ No newline at end of file diff --git a/crates/noirc_evaluator/src/ssa.rs b/crates/noirc_evaluator/src/ssa.rs index 6e67315d0a0..a2e46265f94 100644 --- a/crates/noirc_evaluator/src/ssa.rs +++ b/crates/noirc_evaluator/src/ssa.rs @@ -58,7 +58,7 @@ pub(crate) fn optimize_into_acir( .print(print_ssa_passes, "After Simplifying:") // Run mem2reg before flattening to handle any promotion // of values that can be accessed after loop unrolling - .mem2reg() + .mem2reg() .print(print_ssa_passes, "After Mem2Reg:") .flatten_cfg() .print(print_ssa_passes, "After Flattening:") diff --git a/crates/noirc_evaluator/src/ssa/ir/cfg.rs b/crates/noirc_evaluator/src/ssa/ir/cfg.rs index 26b473eb068..62c08335891 100644 --- a/crates/noirc_evaluator/src/ssa/ir/cfg.rs +++ b/crates/noirc_evaluator/src/ssa/ir/cfg.rs @@ -1,4 +1,4 @@ -use std::collections::{HashMap, BTreeSet}; +use std::collections::{BTreeSet, HashMap}; use super::{ basic_block::{BasicBlock, BasicBlockId}, diff --git a/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs b/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs index 9c82804725e..8c07c9ed298 100644 --- a/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs +++ b/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs @@ -17,14 +17,11 @@ use crate::ssa::{ ssa_gen::Ssa, }; -use super::unrolling::{Loops, find_all_loops}; - impl Ssa { /// Attempts to remove any load instructions that recover values that are already available in /// scope, and attempts to remove stores that are subsequently redundant. /// As long as they are not stores on memory used inside of loops pub(crate) fn mem2reg(mut self) -> Ssa { - dbg!("start mem2reg"); for function in self.functions.values_mut() { let mut all_protected_allocations = HashSet::new(); @@ -33,14 +30,6 @@ impl Ssa { let post_order = PostOrder::with_function(function); let post_order_slice = post_order.as_slice(); - for b in post_order_slice.iter().rev() { - println!("BLOCK: {b}"); - let predecessors = context.cfg.predecessors(*b); - for p in predecessors { - println!("predecessor: {p}"); - } - } - dbg!(post_order_slice); // TODO: for array_dynamic the normal post_order works, but for regression_2218 we need it reverse let post_order_iter = post_order_slice.iter().rev(); // let post_order_iter = post_order_slice.iter(); @@ -58,27 +47,13 @@ impl Ssa { &mut load_values_to_substitute, *block, ); - // dbg!(all_protected_allocations.clone()); all_protected_allocations.extend(allocations_protected_by_block.into_iter()); - - // Broken strategy because the alias will not be recognized, just don't mem2reg compile time blocks - // let remaining_loads = context.find_remaining_loads(&mut function.dfg, *block); - // dbg!(remaining_loads.clone()); - // let mut new_remaining_loads = HashSet::new(); - // for load in remaining_loads.clone() { - // let result_value = function.dfg.resolve(load); - // new_remaining_loads.insert(result_value); - // } - // dbg!(new_remaining_loads.clone()); - // all_protected_allocations.extend(remaining_loads.into_iter()); - // all_protected_allocations.extend(new_remaining_loads.into_iter()); } for block in post_order_iter { context.remove_unused_stores(&mut function.dfg, &all_protected_allocations, *block); } } - dbg!("ended mem2reg"); self } } @@ -90,9 +65,11 @@ struct PerFunctionContext { load_values_to_substitute_per_func: BTreeMap, store_ids: Vec, cfg: ControlFlowGraph, - post_order: PostOrder, + post_order: PostOrder, dom_tree: DominatorTree, - loops: Loops, + // Maps block id -> bool stating whether the block shares a common successor + // with one of its predecessors + has_common_successor: BTreeMap, } impl PerFunctionContext { @@ -107,7 +84,7 @@ impl PerFunctionContext { cfg, post_order, dom_tree, - loops: find_all_loops(function), + has_common_successor: BTreeMap::new(), } } } @@ -129,43 +106,27 @@ impl PerFunctionContext { ) -> HashSet { let mut protected_allocations = HashSet::new(); let block = &dfg[block_id]; - // println!("BLOCK: {block_id}"); + + let has_common_successor = self.has_common_successor(block_id); + // Maintain a map for whether a block has a common successor to avoid + // analyzing the CFG for successors on every store or load + self.has_common_successor.insert(block_id, has_common_successor); for instruction_id in block.instructions() { - // dbg!(&dfg[*instruction_id]); match &dfg[*instruction_id] { Instruction::Store { mut address, value } => { - println!("STORE: {address}"); address = self.fetch_load_value_to_substitute(block_id, address); - println!("address after: {address}"); - // println!("about to insert store: {address}, block_id: {block_id}, value: {value}"); self.last_stores_with_block.insert((address, block_id), *value); self.store_ids.push(*instruction_id); + + if has_common_successor { + protected_allocations.insert(address); + } } Instruction::Load { mut address } => { - // dbg!(load_values_to_substitute_per_block.get(&address).is_some()); - - println!("LOAD: {address}"); address = self.fetch_load_value_to_substitute(block_id, address); - println!("address after: {address}"); - - let predecessors = self.cfg.predecessors(block_id); - let p_len = predecessors.len(); - dbg!(p_len); - drop(predecessors); - - // let mut found_last_value = false; - // if p_len != 2 { - // found_last_value = self.find_load_to_substitute( - // block_id, - // address, - // dfg, - // instruction_id, - // loads_to_substitute, - // load_values_to_substitute_per_block, - // ); - // } + let found_last_value = self.find_load_to_substitute( block_id, address, @@ -174,51 +135,18 @@ impl PerFunctionContext { loads_to_substitute, load_values_to_substitute_per_block, ); - dbg!(found_last_value); if !found_last_value { - // We want to protect allocations that do not have a load to substitute - dbg!(address); - let resolved_address = dfg.resolve(address); - dbg!(resolved_address); + // We want to protect allocations that do not have a load to substitute protected_allocations.insert(address); + // We also want to check for allocations that share a value + // with the one we are protecting. + // This check prevents the pass from removing stores to a value that + // is used by reference aliases in different blocks + protected_allocations.insert(dfg.resolve(address)); + } - // Need this to solve regression 2218 with loops - let found_last_value = self.find_load_to_substitute( - block_id, - resolved_address, - dfg, - instruction_id, - loads_to_substitute, - load_values_to_substitute_per_block, - ); - dbg!(found_last_value); - if found_last_value { - protected_allocations.insert(resolved_address); - } - - // We also want to check for allocations that share a value we are substituting - // with the one we are protecting. - // This check prevents the pass from removing stores to reference aliases across - // multiple blocks. - // TODO: this feels like a cop-out where I am restricting - // functionality so the pass has to be run again rather than checking stores across blocks - // but it may be needed that we can only do these removals after flattening - // NOTE: not the cause of the extra allocate in regression test - if let Some((value, outer_block)) = - self.load_values_to_substitute_per_func.get(&address) - { - println!("value: {value}"); - for (address, (inner_value, block)) in &self.load_values_to_substitute_per_func { - println!("inner_value: {inner_value}"); - println!("address: {address}"); - dbg!(*inner_value == *value); - dbg!(*outer_block == *block); - if *inner_value == *value && *outer_block == *block { - dbg!(address); - protected_allocations.insert(*address); - } - } - } + if has_common_successor { + protected_allocations.insert(address); } } Instruction::Call { arguments, .. } => { @@ -270,7 +198,6 @@ impl PerFunctionContext { for instruction_id in block.instructions() { if let Instruction::Load { address } = &dfg[*instruction_id] { protected_allocations.insert(*address); - } } @@ -292,22 +219,14 @@ impl PerFunctionContext { while let Some(block) = stack.pop() { visited.insert(block); - // TODO: check that any of the predecssor blocks are loops - for l in self.loops.yet_to_unroll.iter() { - // We do not want to substitute loads that take place within loops as this pass - // can occur before loop unrolling - // The pass should be ran again following loop unrolling as new values - if l.blocks.contains(&block) { - return address; - } + // We do not want to substitute loads that take place within loops or yet to be simplified branches + // as this pass can occur before loop unrolling and flattening. + // The mem2reg pass should be ran again following all optimization passes as new values + // may be able to be promoted + if self.has_common_successor[&block_id] { + return address; } - // let predecessors = self.cfg.predecessors(block); - // dbg!(predecessors.len()); - // if predecessors.len() == 2 { - // return address; - // } - // Check whether there is a load value to substitute in the current block. // Return the value if found. if let Some((value, load_block_id)) = @@ -318,29 +237,15 @@ impl PerFunctionContext { } } + // If no load values to substitute have been found in the current block, check the block's predecessors. if let Some(predecessor) = self.block_has_predecessor(block, &visited) { stack.push(predecessor); } - - // If no load values to substitute have been found in the current block, check the block's predecessors. - // let predecessors = self.cfg.predecessors(block); - // for predecessor in predecessors { - // // if self.dom_tree.is_reachable(predecessor) - // // && self.dom_tree.dominates(predecessor, block) - // // && !visited.contains(&predecessor) { - // // stack.push(predecessor); - // // } - // if self.dom_tree.is_reachable(predecessor) - // && !visited.contains(&predecessor) - // { - // stack.push(predecessor); - // } - // } } address } - // This method determines which loads should be substituted and saves them + // This method determines which loads should be substituted and saves them // to be substituted further in the pass. // Starting at the block supplied as a parameter, we check whether a store has occurred with the given address. // If no store has occurred in the supplied block, the CFG is analyzed to determine whether @@ -358,74 +263,34 @@ impl PerFunctionContext { let mut visited = HashSet::new(); while let Some(block) = stack.pop() { - println!("BLOCK in find_load_to_substitute: {block}"); - // let predecessors = self.cfg.predecessors(block); - // for predecessor in predecessors { - // println!("predecessor: {predecessor}"); - // } visited.insert(block); - for l in self.loops.yet_to_unroll.iter() { - // We do not want to substitute loads that take place within loops as this pass - // can occur before loop unrolling - // The pass should be ran again following loop unrolling as new values - // dbg!(l.blocks.clone()); - if l.blocks.contains(&block) { - dbg!(block); - return false; - } + // We do not want to substitute loads that take place within loops or yet to be simplified branches + // as this pass can occur before loop unrolling and flattening. + // The mem2reg pass should be ran again following all optimization passes as new values + // may be able to be promoted + if self.has_common_successor[&block_id] { + return false; } - // let predecessors = self.cfg.clone().predecessors(block_id); - // dbg!(predecessors.len()); - // if predecessors.len() == 2 { - // return false; - // } - // Check whether there has been a store instruction in the current block // If there has been a store, add a load to be substituted. if let Some(last_value) = self.last_stores_with_block.get(&(address, block)) { - // println!("last_value: {last_value}"); let result_value = *dfg .instruction_results(*instruction_id) .first() .expect("ICE: Load instructions should have single result"); - // println!("result_value: {result_value}"); loads_to_substitute.insert(*instruction_id, *last_value); load_values_to_substitute_per_block.insert(result_value, *last_value); self.load_values_to_substitute_per_func.insert(result_value, (*last_value, block)); return true; } - // TODO: some situations we only need to check block with one predecessor and in some need to check more + // If no load values to substitute have been found in the current block, check the block's predecessors. if let Some(predecessor) = self.block_has_predecessor(block, &visited) { stack.push(predecessor); } - - // If no load values to substitute have been found in the current block, check the block's predecessors. - // let predecessors = self.cfg.predecessors(block); - // // dbg!(predecessors.len()); - // for predecessor in predecessors { - // // dbg!(predecessor); - // // check out the post order here see if there is a match - // // perhaps it is not dominating as it doesn't have to flow through - // // dbg!(self.dom_tree.dominates(predecessor, block)); - - // // Need this for array_dynamic - // // if self.dom_tree.is_reachable(predecessor) - // // && self.dom_tree.dominates(predecessor, block) - // // && !visited.contains(&predecessor) { - // // stack.push(predecessor); - // // } - // // TODO: references regression 2218 with loops working with this and blocks constructed from successors - // // only a few tests failing after that - // if self.dom_tree.is_reachable(predecessor) - // && !visited.contains(&predecessor) - // { - // stack.push(predecessor); - // } - // } } false } @@ -440,7 +305,6 @@ impl PerFunctionContext { visited: &HashSet, ) -> Option { let mut predecessors = self.cfg.predecessors(block_id); - // dbg!(predecessors.len()); if predecessors.len() == 1 { let predecessor = predecessors.next().unwrap(); if self.dom_tree.is_reachable(predecessor) @@ -449,15 +313,45 @@ impl PerFunctionContext { { return Some(predecessor); } - // if self.dom_tree.is_reachable(predecessor) - // && !visited.contains(&predecessor) - // { - // return Some(predecessor); - // } } None } + fn has_common_successor(&mut self, block_id: BasicBlockId) -> bool { + let mut predecessors = self.cfg.predecessors(block_id); + if let Some(predecessor) = predecessors.next() { + let pred_successors = self.find_all_successors(predecessor); + let current_successors: HashSet<_> = self.cfg.successors(block_id).collect(); + return pred_successors.into_iter().any(|b| current_successors.contains(&b)); + } + false + } + + fn find_all_successors(&self, block_id: BasicBlockId) -> HashSet { + let mut stack = vec![]; + let mut visited = HashSet::new(); + + // Fetch initial block successors + let successors = self.cfg.successors(block_id); + for successor in successors { + if !visited.contains(&successor) { + stack.push(successor); + } + } + + // Follow the CFG to fetch the remaining successors + while let Some(block) = stack.pop() { + visited.insert(block); + let successors = self.cfg.successors(block); + for successor in successors { + if !visited.contains(&successor) { + stack.push(successor); + } + } + } + visited + } + /// Checks whether the given value id refers to an allocation. fn value_is_from_allocation(value: ValueId, dfg: &DataFlowGraph) -> bool { match &dfg[value] { From 3906ca248466ee29bafc49dcfeeafe0bcbc4dfa5 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Tue, 15 Aug 2023 14:37:43 +0000 Subject: [PATCH 26/62] array dynamic back to original --- .../array_dynamic/Prover.toml | 3 +- .../array_dynamic/src/main.nr | 36 ++++++++----------- 2 files changed, 16 insertions(+), 23 deletions(-) diff --git a/crates/nargo_cli/tests/execution_success/array_dynamic/Prover.toml b/crates/nargo_cli/tests/execution_success/array_dynamic/Prover.toml index 957a044a1a5..750b3129ec9 100644 --- a/crates/nargo_cli/tests/execution_success/array_dynamic/Prover.toml +++ b/crates/nargo_cli/tests/execution_success/array_dynamic/Prover.toml @@ -1,6 +1,5 @@ x = [104, 101, 108, 108, 111] -# z = "59" -z = "4" +z = "59" t = "10" index = [0,1,2,3,4] index2 = [0,1,2,3,4] diff --git a/crates/nargo_cli/tests/execution_success/array_dynamic/src/main.nr b/crates/nargo_cli/tests/execution_success/array_dynamic/src/main.nr index d9411bfb5fe..69e5fdc93db 100644 --- a/crates/nargo_cli/tests/execution_success/array_dynamic/src/main.nr +++ b/crates/nargo_cli/tests/execution_success/array_dynamic/src/main.nr @@ -1,38 +1,32 @@ fn main(x: [u32; 5], mut z: u32, t: u32, index: [Field;5], index2: [Field;5], offset: Field, sublen: Field) { - // let idx = (z - 5*t - 5) as Field; - let idx = z as Field; - // dep::std::println(idx); + let idx = (z - 5*t - 5) as Field; //dynamic array test dyn_array(x, idx, idx - 3); //regression for issue 1283 - // let mut s = 0; - // let x3 = [246,159,32,176,8]; - // for i in 0..5 { - // s += x3[index[i]]; - // } - // assert(s!=0); + let mut s = 0; + let x3 = [246,159,32,176,8]; + for i in 0..5 { + s += x3[index[i]]; + } + assert(s!=0); - // if 3 < (sublen as u32) { - // assert(index[offset + 3] == index2[3]); - // } + if 3 < (sublen as u32) { + assert(index[offset + 3] == index2[3]); + } } fn dyn_array(mut x: [u32; 5], y: Field, z: Field) { - // assert(x[y] == 111); - // assert(x[z] == 101); - // x[z] = 0; - // assert(x[y] == 111); - // assert(x[1] == 0); + assert(x[y] == 111); + assert(x[z] == 101); + x[z] = 0; + assert(x[y] == 111); + assert(x[1] == 0); if y as u32 < 10 { - // dep::std::println(x); - let q = x[y] - 2; - // dep::std::println(q); x[y] = x[y] - 2; } else { x[y] = 0; } - // dep::std::println(x); assert(x[4] == 109); } \ No newline at end of file From 7ed502d7b367ce9e2e6ae92967c3ea3cd8ef0c6e Mon Sep 17 00:00:00 2001 From: vezenovm Date: Tue, 15 Aug 2023 14:42:55 +0000 Subject: [PATCH 27/62] use reachable blocks --- crates/noirc_evaluator/src/ssa/opt/mem2reg.rs | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs b/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs index 8c07c9ed298..252e8e82fa6 100644 --- a/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs +++ b/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs @@ -26,14 +26,8 @@ impl Ssa { let mut all_protected_allocations = HashSet::new(); let mut context = PerFunctionContext::new(function); - // let post_order_slice = .iter().rev(); - let post_order = PostOrder::with_function(function); - let post_order_slice = post_order.as_slice(); - - // TODO: for array_dynamic the normal post_order works, but for regression_2218 we need it reverse - let post_order_iter = post_order_slice.iter().rev(); - // let post_order_iter = post_order_slice.iter(); - for block in post_order_iter.clone() { + + for block in function.reachable_blocks() { // Maps Load instruction id -> value to replace the result of the load with let mut loads_to_substitute_per_block = BTreeMap::new(); @@ -45,13 +39,13 @@ impl Ssa { &mut function.dfg, &mut loads_to_substitute_per_block, &mut load_values_to_substitute, - *block, + block, ); all_protected_allocations.extend(allocations_protected_by_block.into_iter()); } - for block in post_order_iter { - context.remove_unused_stores(&mut function.dfg, &all_protected_allocations, *block); + for block in function.reachable_blocks() { + context.remove_unused_stores(&mut function.dfg, &all_protected_allocations, block); } } self From 71c5693d024496a17de5820019e675fec108bf56 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Tue, 15 Aug 2023 15:11:46 +0000 Subject: [PATCH 28/62] restore sha2_blocks test --- .../execution_success/sha2_blocks/src/main.nr | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/crates/nargo_cli/tests/execution_success/sha2_blocks/src/main.nr b/crates/nargo_cli/tests/execution_success/sha2_blocks/src/main.nr index f1afea12dae..fcdcdb8684f 100644 --- a/crates/nargo_cli/tests/execution_success/sha2_blocks/src/main.nr +++ b/crates/nargo_cli/tests/execution_success/sha2_blocks/src/main.nr @@ -3,20 +3,20 @@ use dep::std; fn main(x: [u8; 3], result256: [u8; 32], result512: [u8; 64]) { - // // One-block tests. - // let mut digest256 = std::sha256::digest(x); - // assert(digest256 == result256); + // One-block tests. + let mut digest256 = std::sha256::digest(x); + assert(digest256 == result256); - // let mut digest512 = std::sha512::digest(x); - // assert(digest512 == result512); + let mut digest512 = std::sha512::digest(x); + assert(digest512 == result512); - // // Two-block SHA256 test. Taken from https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Standards-and-Guidelines/documents/examples/SHA256.pdf - // let y: [u8; 56] = [97,98,99,100,98,99,100,101,99,100,101,102,100,101,102,103,101,102,103,104,102,103,104,105,103,104,105,106,104,105,106,107,105,106,107,108,106,107,108,109,107,108,109,110,108,109,110,111,109,110,111,112,110,111,112,113]; // "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" - // digest256 = std::sha256::digest(y); - // assert(digest256 == [36,141,106,97,210,6,56,184,229,192,38,147,12,62,96,57,163,60,228,89,100,255,33,103,246,236,237,212,25,219,6,193]); + // Two-block SHA256 test. Taken from https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Standards-and-Guidelines/documents/examples/SHA256.pdf + let y: [u8; 56] = [97,98,99,100,98,99,100,101,99,100,101,102,100,101,102,103,101,102,103,104,102,103,104,105,103,104,105,106,104,105,106,107,105,106,107,108,106,107,108,109,107,108,109,110,108,109,110,111,109,110,111,112,110,111,112,113]; // "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" + digest256 = std::sha256::digest(y); + assert(digest256 == [36,141,106,97,210,6,56,184,229,192,38,147,12,62,96,57,163,60,228,89,100,255,33,103,246,236,237,212,25,219,6,193]); - // // Two-block SHA256 test. Taken from https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Standards-and-Guidelines/documents/examples/SHA512.pdf - // let z: [u8; 112] = [97,98,99,100,101,102,103,104,98,99,100,101,102,103,104,105,99,100,101,102,103,104,105,106,100,101,102,103,104,105,106,107,101,102,103,104,105,106,107,108,102,103,104,105,106,107,108,109,103,104,105,106,107,108,109,110,104,105,106,107,108,109,110,111,105,106,107,108,109,110,111,112,106,107,108,109,110,111,112,113,107,108,109,110,111,112,113,114,108,109,110,111,112,113,114,115,109,110,111,112,113,114,115,116,110,111,112,113,114,115,116,117]; // "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu" - // digest512 = std::sha512::digest(z); - // assert(digest512 == [142,149,155,117,218,227,19,218,140,244,247,40,20,252,20,63,143,119,121,198,235,159,127,161,114,153,174,173,182,136,144,24,80,29,40,158,73,0,247,228,51,27,153,222,196,181,67,58,199,211,41,238,182,221,38,84,94,150,229,91,135,75,233,9]); + // Two-block SHA256 test. Taken from https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Standards-and-Guidelines/documents/examples/SHA512.pdf + let z: [u8; 112] = [97,98,99,100,101,102,103,104,98,99,100,101,102,103,104,105,99,100,101,102,103,104,105,106,100,101,102,103,104,105,106,107,101,102,103,104,105,106,107,108,102,103,104,105,106,107,108,109,103,104,105,106,107,108,109,110,104,105,106,107,108,109,110,111,105,106,107,108,109,110,111,112,106,107,108,109,110,111,112,113,107,108,109,110,111,112,113,114,108,109,110,111,112,113,114,115,109,110,111,112,113,114,115,116,110,111,112,113,114,115,116,117]; // "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu" + digest512 = std::sha512::digest(z); + assert(digest512 == [142,149,155,117,218,227,19,218,140,244,247,40,20,252,20,63,143,119,121,198,235,159,127,161,114,153,174,173,182,136,144,24,80,29,40,158,73,0,247,228,51,27,153,222,196,181,67,58,199,211,41,238,182,221,38,84,94,150,229,91,135,75,233,9]); } From c612b45592e4d5b20b7fcf08095c7c9e0c790f20 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Tue, 15 Aug 2023 15:13:11 +0000 Subject: [PATCH 29/62] remove unnecessary clone and unused methods --- crates/noirc_evaluator/src/ssa/ir/post_order.rs | 1 - crates/noirc_evaluator/src/ssa/opt/mem2reg.rs | 17 ----------------- 2 files changed, 18 deletions(-) diff --git a/crates/noirc_evaluator/src/ssa/ir/post_order.rs b/crates/noirc_evaluator/src/ssa/ir/post_order.rs index e405fc46042..e3bdbd491df 100644 --- a/crates/noirc_evaluator/src/ssa/ir/post_order.rs +++ b/crates/noirc_evaluator/src/ssa/ir/post_order.rs @@ -13,7 +13,6 @@ enum Visit { Last, } -#[derive(Clone)] pub(crate) struct PostOrder(Vec); impl PostOrder { diff --git a/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs b/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs index 252e8e82fa6..569462dd898 100644 --- a/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs +++ b/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs @@ -181,23 +181,6 @@ impl PerFunctionContext { protected_allocations } - fn find_remaining_loads( - &mut self, - dfg: &mut DataFlowGraph, - block_id: BasicBlockId, - ) -> HashSet { - let mut protected_allocations = HashSet::new(); - let block = &dfg[block_id]; - - for instruction_id in block.instructions() { - if let Instruction::Load { address } = &dfg[*instruction_id] { - protected_allocations.insert(*address); - } - } - - protected_allocations - } - // This method will fetch already saved load values to substitute for a given address. // The search starts at the block supplied as a parameter. If there is not a load to substitute // the CFG is analyzed to determine whether a predecessor block has a load value to substitute. From ef10232435483d3057acd936328e432a1c4670a0 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Tue, 15 Aug 2023 15:33:13 +0000 Subject: [PATCH 30/62] bring back loops check inside fetch methods --- crates/noirc_evaluator/src/ssa/opt/mem2reg.rs | 44 ++++++++++++------- crates/noirc_frontend/src/graph/mod.rs | 2 +- 2 files changed, 30 insertions(+), 16 deletions(-) diff --git a/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs b/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs index 569462dd898..06751859949 100644 --- a/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs +++ b/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs @@ -17,6 +17,8 @@ use crate::ssa::{ ssa_gen::Ssa, }; +use super::unrolling::{find_all_loops, Loops}; + impl Ssa { /// Attempts to remove any load instructions that recover values that are already available in /// scope, and attempts to remove stores that are subsequently redundant. @@ -61,9 +63,7 @@ struct PerFunctionContext { cfg: ControlFlowGraph, post_order: PostOrder, dom_tree: DominatorTree, - // Maps block id -> bool stating whether the block shares a common successor - // with one of its predecessors - has_common_successor: BTreeMap, + loops: Loops, } impl PerFunctionContext { @@ -78,7 +78,7 @@ impl PerFunctionContext { cfg, post_order, dom_tree, - has_common_successor: BTreeMap::new(), + loops: find_all_loops(function), } } } @@ -101,14 +101,16 @@ impl PerFunctionContext { let mut protected_allocations = HashSet::new(); let block = &dfg[block_id]; + // Check whether the block has a common successor here to avoid analyzing + // the CFG for every block instruction. let has_common_successor = self.has_common_successor(block_id); - // Maintain a map for whether a block has a common successor to avoid - // analyzing the CFG for successors on every store or load - self.has_common_successor.insert(block_id, has_common_successor); for instruction_id in block.instructions() { match &dfg[*instruction_id] { Instruction::Store { mut address, value } => { + if has_common_successor { + protected_allocations.insert(address); + } address = self.fetch_load_value_to_substitute(block_id, address); self.last_stores_with_block.insert((address, block_id), *value); @@ -138,10 +140,6 @@ impl PerFunctionContext { // is used by reference aliases in different blocks protected_allocations.insert(dfg.resolve(address)); } - - if has_common_successor { - protected_allocations.insert(address); - } } Instruction::Call { arguments, .. } => { for arg in arguments { @@ -200,8 +198,16 @@ impl PerFunctionContext { // as this pass can occur before loop unrolling and flattening. // The mem2reg pass should be ran again following all optimization passes as new values // may be able to be promoted - if self.has_common_successor[&block_id] { - return address; + // if self.has_common_successor[&block_id] { + // return address; + // } + for l in self.loops.yet_to_unroll.iter() { + // We do not want to substitute loads that take place within loops as this pass + // can occur before loop unrolling + // The pass should be ran again following loop unrolling as new values + if l.blocks.contains(&block) { + return address; + } } // Check whether there is a load value to substitute in the current block. @@ -246,8 +252,16 @@ impl PerFunctionContext { // as this pass can occur before loop unrolling and flattening. // The mem2reg pass should be ran again following all optimization passes as new values // may be able to be promoted - if self.has_common_successor[&block_id] { - return false; + // if self.has_common_successor[&block_id] { + // return false; + // } + for l in self.loops.yet_to_unroll.iter() { + // We do not want to substitute loads that take place within loops as this pass + // can occur before loop unrolling + // The pass should be ran again following loop unrolling as new values + if l.blocks.contains(&block) { + return false; + } } // Check whether there has been a store instruction in the current block diff --git a/crates/noirc_frontend/src/graph/mod.rs b/crates/noirc_frontend/src/graph/mod.rs index 7a1daef45fd..90490c47c36 100644 --- a/crates/noirc_frontend/src/graph/mod.rs +++ b/crates/noirc_frontend/src/graph/mod.rs @@ -30,7 +30,7 @@ impl CrateId { pub struct CrateName(SmolStr); impl CrateName { - fn is_valid_name(name: &str) -> bool { + fn is_valid_name(name: &str) -> bool { !name.is_empty() && name.chars().all(|n| !CHARACTER_BLACK_LIST.contains(&n)) } } From 8d535eb0a73bca0b9cdbde7445354028d87ca7cf Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Tue, 15 Aug 2023 21:02:26 +0100 Subject: [PATCH 31/62] Update crates/nargo_cli/tests/execution_success/regression/src/main.nr Co-authored-by: jfecher --- crates/nargo_cli/tests/execution_success/regression/src/main.nr | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/nargo_cli/tests/execution_success/regression/src/main.nr b/crates/nargo_cli/tests/execution_success/regression/src/main.nr index 103161482b4..54769c39709 100644 --- a/crates/nargo_cli/tests/execution_success/regression/src/main.nr +++ b/crates/nargo_cli/tests/execution_success/regression/src/main.nr @@ -76,7 +76,6 @@ fn main(x: [u8; 5], z: Field) //Issue 1144 let (nib, len) = compact_decode(x,z); assert(len == 5); - // dep::std::println(nib); assert([nib[0], nib[1], nib[2], nib[3], nib[4]] == [15, 1, 12, 11, 8]); // Issue 1169 From bd678a9f1ededbb736b064b38f585b2eeb35f342 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Tue, 15 Aug 2023 20:09:27 +0000 Subject: [PATCH 32/62] all to_bits methods working for acir, fixing up brillig with new slice structure --- .../brillig_slices/src/main.nr | 115 +++++++++--------- .../src/brillig/brillig_gen/brillig_block.rs | 37 +++++- .../noirc_evaluator/src/brillig/brillig_ir.rs | 2 + .../noirc_evaluator/src/ssa/acir_gen/mod.rs | 7 +- .../noirc_evaluator/src/ssa/ir/instruction.rs | 3 +- .../src/ssa/ssa_gen/context.rs | 9 +- crates/noirc_evaluator/src/ssa/ssa_gen/mod.rs | 58 +++------ .../src/monomorphization/mod.rs | 3 +- 8 files changed, 126 insertions(+), 108 deletions(-) diff --git a/crates/nargo_cli/tests/execution_success/brillig_slices/src/main.nr b/crates/nargo_cli/tests/execution_success/brillig_slices/src/main.nr index 2d871bc4b2f..1701e8fa3cc 100644 --- a/crates/nargo_cli/tests/execution_success/brillig_slices/src/main.nr +++ b/crates/nargo_cli/tests/execution_success/brillig_slices/src/main.nr @@ -9,65 +9,66 @@ unconstrained fn main(x: Field, y: Field) { assert(slice.len() == 3); assert(slice[0] == y); assert(slice[1] == x); + // dep::std::println(slice[2]); assert(slice[2] == 7); - // Array set on slice target - slice[0] = x; - slice[1] = y; - slice[2] = 1; - - assert(slice[0] == x); - assert(slice[1] == y); - assert(slice[2] == 1); - - slice = push_front_to_slice(slice, 2); - assert(slice.len() == 4); - assert(slice[0] == 2); - assert(slice[1] == x); - assert(slice[2] == y); - assert(slice[3] == 1); - - let (item, popped_front_slice) = slice.pop_front(); - slice = popped_front_slice; - assert(item == 2); - - assert(slice.len() == 3); - assert(slice[0] == x); - assert(slice[1] == y); - assert(slice[2] == 1); - - let (popped_back_slice, another_item) = slice.pop_back(); - slice = popped_back_slice; - assert(another_item == 1); - - assert(slice.len() == 2); - assert(slice[0] == x); - assert(slice[1] == y); - - slice = slice.insert(1, 2); - assert(slice.len() == 3); - assert(slice[0] == x); - assert(slice[1] == 2); - assert(slice[2] == y); - - let (removed_slice, should_be_2) = slice.remove(1); - slice = removed_slice; - assert(should_be_2 == 2); - - assert(slice.len() == 2); - assert(slice[0] == x); - assert(slice[1] == y); - - let (slice_with_only_x, should_be_y) = slice.remove(1); - slice = slice_with_only_x; - assert(should_be_y == y); - - assert(slice.len() == 1); - assert(slice[0] == x); - - let (empty_slice, should_be_x) = slice.remove(0); - assert(should_be_x == x); - assert(empty_slice.len() == 0); + // // Array set on slice target + // slice[0] = x; + // slice[1] = y; + // slice[2] = 1; + + // assert(slice[0] == x); + // assert(slice[1] == y); + // assert(slice[2] == 1); + + // slice = push_front_to_slice(slice, 2); + // assert(slice.len() == 4); + // assert(slice[0] == 2); + // assert(slice[1] == x); + // assert(slice[2] == y); + // assert(slice[3] == 1); + + // let (item, popped_front_slice) = slice.pop_front(); + // slice = popped_front_slice; + // assert(item == 2); + + // assert(slice.len() == 3); + // assert(slice[0] == x); + // assert(slice[1] == y); + // assert(slice[2] == 1); + + // let (popped_back_slice, another_item) = slice.pop_back(); + // slice = popped_back_slice; + // assert(another_item == 1); + + // assert(slice.len() == 2); + // assert(slice[0] == x); + // assert(slice[1] == y); + + // slice = slice.insert(1, 2); + // assert(slice.len() == 3); + // assert(slice[0] == x); + // assert(slice[1] == 2); + // assert(slice[2] == y); + + // let (removed_slice, should_be_2) = slice.remove(1); + // slice = removed_slice; + // assert(should_be_2 == 2); + + // assert(slice.len() == 2); + // assert(slice[0] == x); + // assert(slice[1] == y); + + // let (slice_with_only_x, should_be_y) = slice.remove(1); + // slice = slice_with_only_x; + // assert(should_be_y == y); + + // assert(slice.len() == 1); + // assert(slice[0] == x); + + // let (empty_slice, should_be_x) = slice.remove(0); + // assert(should_be_x == x); + // assert(empty_slice.len() == 0); } // Tests slice passing to/from functions diff --git a/crates/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs b/crates/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs index 943826201d2..2b4df281090 100644 --- a/crates/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs +++ b/crates/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs @@ -304,7 +304,17 @@ impl<'block> BrilligBlock<'block> { dfg, ); let param_id = arguments[0]; - self.convert_ssa_array_len(param_id, result_register, dfg); + dbg!(dfg.type_of_value(param_id)); + dbg!(&dfg[arguments[0]]); + if let Type::Numeric(numeric_type) = dfg.type_of_value(param_id) { + dbg!(numeric_type); + // self.brillig_context + // .const_instruction(result_register, (constant).into()); + let len = self.convert_ssa_value(arguments[0], dfg); + dbg!(len); + } else { + self.convert_ssa_array_len(arguments[0], result_register, dfg); + } } Value::Intrinsic( Intrinsic::SlicePushBack @@ -587,23 +597,38 @@ impl<'block> BrilligBlock<'block> { instruction_id: InstructionId, arguments: &[ValueId], ) { - let slice_id = arguments[0]; + let slice_id = arguments[1]; + dbg!(&dfg.type_of_value(slice_id)); let element_size = dfg.type_of_value(slice_id).element_size(); let source_variable = self.convert_ssa_value(slice_id, dfg); let source_vector = self.convert_array_or_vector_to_vector(source_variable); - + dbg!(intrinsic.clone()); match intrinsic { Value::Intrinsic(Intrinsic::SlicePushBack) => { + let results = dfg.instruction_results(instruction_id); + // TODO: maybe we do not want this + match self.function_context.create_variable( + self.brillig_context, + results[0], + dfg, + ) { + RegisterOrMemory::RegisterIndex(register_index) => register_index, + _ => unreachable!("ICE: first value of a slice must be a register index"), + }; + let target_variable = self.function_context.create_variable( self.brillig_context, - dfg.instruction_results(instruction_id)[0], + results[1], dfg, ); + dbg!("about to extract heap vector"); + let target_vector = self.brillig_context.extract_heap_vector(target_variable); - let item_values = vecmap(&arguments[1..element_size + 1], |arg| { + let item_values = vecmap(&arguments[2..element_size + 1], |arg| { self.convert_ssa_value(*arg, dfg) }); self.slice_push_back_operation(target_vector, source_vector, &item_values); + // self.brillig_context.usize_op_in_place(slice_len, BinaryIntOp::Add, 1); } Value::Intrinsic(Intrinsic::SlicePushFront) => { let target_variable = self.function_context.create_variable( @@ -612,7 +637,7 @@ impl<'block> BrilligBlock<'block> { dfg, ); let target_vector = self.brillig_context.extract_heap_vector(target_variable); - let item_values = vecmap(&arguments[1..element_size + 1], |arg| { + let item_values = vecmap(&arguments[2..element_size + 1], |arg| { self.convert_ssa_value(*arg, dfg) }); self.slice_push_front_operation(target_vector, source_vector, &item_values); diff --git a/crates/noirc_evaluator/src/brillig/brillig_ir.rs b/crates/noirc_evaluator/src/brillig/brillig_ir.rs index 047e8b7edf8..a6d9a32807f 100644 --- a/crates/noirc_evaluator/src/brillig/brillig_ir.rs +++ b/crates/noirc_evaluator/src/brillig/brillig_ir.rs @@ -935,9 +935,11 @@ impl BrilligContext { } pub(crate) fn extract_heap_vector(&mut self, variable: RegisterOrMemory) -> HeapVector { + dbg!(variable.clone()); match variable { RegisterOrMemory::HeapVector(vector) => vector, RegisterOrMemory::HeapArray(array) => { + dbg!(array.size); let size = self.allocate_register(); self.const_instruction(size, array.size.into()); HeapVector { pointer: array.pointer, size } diff --git a/crates/noirc_evaluator/src/ssa/acir_gen/mod.rs b/crates/noirc_evaluator/src/ssa/acir_gen/mod.rs index ad4c29b69dc..729d642b228 100644 --- a/crates/noirc_evaluator/src/ssa/acir_gen/mod.rs +++ b/crates/noirc_evaluator/src/ssa/acir_gen/mod.rs @@ -473,8 +473,11 @@ impl Context { last_array_uses: &HashMap, ) -> Result<(), RuntimeError> { let index_const = dfg.get_numeric_constant(index); - - match self.convert_value(array, dfg) { + let value_id = dfg.resolve(array); + let value = &dfg[value_id]; + dbg!(value); + let acir_value = self.convert_value(array, dfg); + match acir_value { AcirValue::Var(acir_var, _) => { return Err(RuntimeError::InternalError(InternalError::UnExpected { expected: "an array value".to_string(), diff --git a/crates/noirc_evaluator/src/ssa/ir/instruction.rs b/crates/noirc_evaluator/src/ssa/ir/instruction.rs index 31521def27f..d12501d0246 100644 --- a/crates/noirc_evaluator/src/ssa/ir/instruction.rs +++ b/crates/noirc_evaluator/src/ssa/ir/instruction.rs @@ -305,9 +305,10 @@ impl Instruction { let index = index.try_to_u64().expect("Expected array index to fit in u64") as usize; if index < array.len() { + // dbg!(array[index]); return SimplifiedTo(array[index]); } - } + } None } Instruction::ArraySet { array, index, value } => { diff --git a/crates/noirc_evaluator/src/ssa/ssa_gen/context.rs b/crates/noirc_evaluator/src/ssa/ssa_gen/context.rs index 926d037cc28..ad24a025631 100644 --- a/crates/noirc_evaluator/src/ssa/ssa_gen/context.rs +++ b/crates/noirc_evaluator/src/ssa/ssa_gen/context.rs @@ -263,8 +263,10 @@ impl<'a> FunctionContext<'a> { if let Type::Numeric(NumericType::Unsigned { bit_size }) = typ { let to_bits = self.builder.import_intrinsic_id(Intrinsic::ToBits(Endian::Little)); let length = self.builder.field_constant(FieldElement::from(bit_size as i128)); - let result_types = vec![Type::Array(Rc::new(vec![Type::bool()]), bit_size as usize)]; - let rhs_bits = self.builder.insert_call(to_bits, vec![rhs, length], result_types)[0]; + let result_types = vec![Type::field(), Type::Array(Rc::new(vec![Type::bool()]), bit_size as usize)]; + let rhs_bits = self.builder.insert_call(to_bits, vec![rhs, length], result_types); + dbg!(rhs_bits.len()); + let rhs_bits = rhs_bits[1]; let one = self.builder.field_constant(FieldElement::one()); let mut r = one; for i in 1..bit_size + 1 { @@ -364,8 +366,9 @@ impl<'a> FunctionContext<'a> { location: Location, ) -> Values { let lhs_type = self.builder.type_of_value(lhs); + dbg!(lhs_type.clone()); let rhs_type = self.builder.type_of_value(rhs); - + dbg!(rhs_type.clone()); let (array_length, element_type) = match (lhs_type, rhs_type) { ( Type::Array(lhs_composite_type, lhs_length), diff --git a/crates/noirc_evaluator/src/ssa/ssa_gen/mod.rs b/crates/noirc_evaluator/src/ssa/ssa_gen/mod.rs index 78df0747c37..1892654909d 100644 --- a/crates/noirc_evaluator/src/ssa/ssa_gen/mod.rs +++ b/crates/noirc_evaluator/src/ssa/ssa_gen/mod.rs @@ -120,24 +120,25 @@ impl<'a> FunctionContext<'a> { fn codegen_literal(&mut self, literal: &ast::Literal) -> Values { match literal { ast::Literal::Array(array) => { - // let elements = vecmap(&array.contents, |element| self.codegen_expression(element)); + let elements = vecmap(&array.contents, |element| self.codegen_expression(element)); let typ = Self::convert_non_tuple_type(&array.typ); - + // dbg!(typ.clone()); let new_convert_type = Self::convert_type(&array.typ); // dbg!(new_convert_type.clone()); if new_convert_type.count_leaves() > 1 { + dbg!("got here"); let slice_length = ast::Literal::Integer( (array.contents.len() as u128).into(), ast::Type::Field, ); let slice_length = self.codegen_literal(&slice_length); - let elements = - vecmap(&array.contents, |element| self.codegen_expression(element)); + // let elements = + // vecmap(&array.contents, |element| self.codegen_expression(element)); let slice_contents = self.codegen_array(elements, typ); Tree::Branch(vec![slice_length, slice_contents]) } else { - let elements = - vecmap(&array.contents, |element| self.codegen_expression(element)); + // let elements = + // vecmap(&array.contents, |element| self.codegen_expression(element)); self.codegen_array(elements, typ) } } @@ -261,37 +262,20 @@ impl<'a> FunctionContext<'a> { } fn codegen_index(&mut self, index: &ast::Index) -> Values { - // let array = self.codegen_non_tuple_expression(&index.collection); - // let index_value = self.codegen_non_tuple_expression(&index.index); - // self.codegen_array_index(array, index_value, &index.element_type, index.location) - // dbg!("codegen_index"); - let array_or_slice = self.codegen_expression(&index.collection); - // dbg!(array_or_slice.clone()); - if array_or_slice.count_leaves() > 1 { - let index_value = self.codegen_non_tuple_expression(&index.index); - match &array_or_slice { - Tree::Branch(values) => { - // dbg!(values.clone()); - // for value in values { - // let x = value.clone().into_leaf().eval(self); - // dbg!(&self.builder.current_function.dfg[x]); - // } - let slice_length = values[0].clone().into_leaf().eval(self); - let slice = values[1].clone().into_leaf().eval(self); - self.codegen_array_index( - slice, - index_value, - &index.element_type, - index.location, - Some(slice_length), - ) - } - Tree::Leaf(_) => panic!("Nooo"), - } + let array_or_slice = self.codegen_expression(&index.collection).into_value_list(self); + let index_value = self.codegen_non_tuple_expression(&index.index); + if array_or_slice.len() > 1 { + let slice_length = array_or_slice[0]; + let slice = array_or_slice[1]; + self.codegen_array_index( + slice, + index_value, + &index.element_type, + index.location, + Some(slice_length), + ) } else { - let array = self.codegen_non_tuple_expression(&index.collection); - let index_value = self.codegen_non_tuple_expression(&index.index); - self.codegen_array_index(array, index_value, &index.element_type, index.location, None) + self.codegen_array_index(array_or_slice[0], index_value, &index.element_type, index.location, None) } } @@ -328,12 +312,10 @@ impl<'a> FunctionContext<'a> { let array_type = &self.builder.type_of_value(array); match array_type { Type::Slice(_) => { - dbg!("got slice"); let array_len = max_length.expect("ICE: a length must be supplied for indexing slices"); // If the index and the array_len are both Fields we will not be able to perform a less than comparison on them // Thus, we cast the array len to a u64 before performing the less than comparison - dbg!(array_len); let array_len_int = self.builder.insert_cast( array_len, Type::Numeric(NumericType::Unsigned { bit_size: 64 }), diff --git a/crates/noirc_frontend/src/monomorphization/mod.rs b/crates/noirc_frontend/src/monomorphization/mod.rs index 998f3093d49..cfc3ec5c8a3 100644 --- a/crates/noirc_frontend/src/monomorphization/mod.rs +++ b/crates/noirc_frontend/src/monomorphization/mod.rs @@ -430,6 +430,7 @@ impl<'interner> Monomorphizer<'interner> { constructor: HirConstructorExpression, id: node_interner::ExprId, ) -> ast::Expression { + dbg!(constructor.r#type.borrow().name.clone()); let typ = self.interner.id_type(id); let field_types = unwrap_struct_type(&typ); @@ -447,7 +448,7 @@ impl<'interner> Monomorphizer<'interner> { field_vars.insert(field_name.0.contents.clone(), (new_id, typ)); let expression = Box::new(self.expr(expr_id)); - + dbg!(new_id); new_exprs.push(ast::Expression::Let(ast::Let { id: new_id, mutable: false, From f989fe931dac51ec3d99ccbcae9f15007114f9a6 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Tue, 15 Aug 2023 20:11:27 +0000 Subject: [PATCH 33/62] guarantee that x == 3 in references regressio nfor 2218 --- crates/nargo_cli/tests/execution_success/references/src/main.nr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/nargo_cli/tests/execution_success/references/src/main.nr b/crates/nargo_cli/tests/execution_success/references/src/main.nr index 8b45e130ebf..ec23f2f3a38 100644 --- a/crates/nargo_cli/tests/execution_success/references/src/main.nr +++ b/crates/nargo_cli/tests/execution_success/references/src/main.nr @@ -36,7 +36,7 @@ fn main(mut x: Field) { regression_2030(); regression_2255(); - // x == 3 at this point + assert(x == 3); regression_2218_if_inner_if(x, 10); regression_2218_if_inner_else(20, x); regression_2218_else(x, 3); From 12ee7ad486090b23813f7fbb33e8c7d28e6df375 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Tue, 15 Aug 2023 20:18:54 +0000 Subject: [PATCH 34/62] remove leftover comments --- crates/noirc_evaluator/src/ssa/opt/mem2reg.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs b/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs index 06751859949..be0ded802b3 100644 --- a/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs +++ b/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs @@ -198,9 +198,6 @@ impl PerFunctionContext { // as this pass can occur before loop unrolling and flattening. // The mem2reg pass should be ran again following all optimization passes as new values // may be able to be promoted - // if self.has_common_successor[&block_id] { - // return address; - // } for l in self.loops.yet_to_unroll.iter() { // We do not want to substitute loads that take place within loops as this pass // can occur before loop unrolling @@ -252,9 +249,6 @@ impl PerFunctionContext { // as this pass can occur before loop unrolling and flattening. // The mem2reg pass should be ran again following all optimization passes as new values // may be able to be promoted - // if self.has_common_successor[&block_id] { - // return false; - // } for l in self.loops.yet_to_unroll.iter() { // We do not want to substitute loads that take place within loops as this pass // can occur before loop unrolling From 6149e48225e434350d668a849ab89e06578d9f69 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Tue, 15 Aug 2023 22:31:32 +0000 Subject: [PATCH 35/62] all brillig sslice intrinsics working except insert and remove --- .../brillig_slices/src/main.nr | 56 ++++++------ .../src/brillig/brillig_gen/brillig_block.rs | 90 +++++++++++++++---- .../brillig/brillig_gen/brillig_slice_ops.rs | 39 ++++++++ .../noirc_evaluator/src/brillig/brillig_ir.rs | 1 - crates/noirc_evaluator/src/ssa.rs | 4 + .../noirc_evaluator/src/ssa/ir/instruction.rs | 1 + .../src/ssa/ir/instruction/call.rs | 8 +- 7 files changed, 151 insertions(+), 48 deletions(-) diff --git a/crates/nargo_cli/tests/execution_success/brillig_slices/src/main.nr b/crates/nargo_cli/tests/execution_success/brillig_slices/src/main.nr index 1701e8fa3cc..c726ef0121e 100644 --- a/crates/nargo_cli/tests/execution_success/brillig_slices/src/main.nr +++ b/crates/nargo_cli/tests/execution_success/brillig_slices/src/main.nr @@ -3,47 +3,49 @@ use dep::std; unconstrained fn main(x: Field, y: Field) { let mut slice: [Field] = [y, x]; + std::println(slice.len()); assert(slice.len() == 2); slice = slice.push_back(7); + std::println(slice.len()); assert(slice.len() == 3); assert(slice[0] == y); assert(slice[1] == x); - // dep::std::println(slice[2]); assert(slice[2] == 7); - // // Array set on slice target - // slice[0] = x; - // slice[1] = y; - // slice[2] = 1; + // Array set on slice target + slice[0] = x; + slice[1] = y; + slice[2] = 1; - // assert(slice[0] == x); - // assert(slice[1] == y); - // assert(slice[2] == 1); + assert(slice[0] == x); + assert(slice[1] == y); + assert(slice[2] == 1); - // slice = push_front_to_slice(slice, 2); - // assert(slice.len() == 4); - // assert(slice[0] == 2); - // assert(slice[1] == x); - // assert(slice[2] == y); - // assert(slice[3] == 1); + slice = push_front_to_slice(slice, 2); + assert(slice.len() == 4); + assert(slice[0] == 2); + assert(slice[1] == x); + assert(slice[2] == y); + assert(slice[3] == 1); - // let (item, popped_front_slice) = slice.pop_front(); - // slice = popped_front_slice; - // assert(item == 2); + let (item, popped_front_slice) = slice.pop_front(); + slice = popped_front_slice; + assert(item == 2); + std::println(popped_front_slice.len()); - // assert(slice.len() == 3); - // assert(slice[0] == x); - // assert(slice[1] == y); - // assert(slice[2] == 1); + assert(slice.len() == 3); + assert(slice[0] == x); + assert(slice[1] == y); + assert(slice[2] == 1); - // let (popped_back_slice, another_item) = slice.pop_back(); - // slice = popped_back_slice; - // assert(another_item == 1); + let (popped_back_slice, another_item) = slice.pop_back(); + slice = popped_back_slice; + assert(another_item == 1); - // assert(slice.len() == 2); - // assert(slice[0] == x); - // assert(slice[1] == y); + assert(slice.len() == 2); + assert(slice[0] == x); + assert(slice[1] == y); // slice = slice.insert(1, 2); // assert(slice.len() == 3); diff --git a/crates/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs b/crates/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs index 2b4df281090..da08b139e4a 100644 --- a/crates/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs +++ b/crates/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs @@ -202,7 +202,8 @@ impl<'block> BrilligBlock<'block> { /// Converts an SSA instruction into a sequence of Brillig opcodes. fn convert_ssa_instruction(&mut self, instruction_id: InstructionId, dfg: &DataFlowGraph) { let instruction = &dfg[instruction_id]; - + // dbg!(instruction.clone()); + // dbg!(dfg.instruction_results(instruction_id)); match instruction { Instruction::Binary(binary) => { let result_register = self.function_context.create_register_variable( @@ -232,6 +233,9 @@ impl<'block> BrilligBlock<'block> { self.brillig_context.store_variable_instruction(address_register, source_variable); } Instruction::Load { address } => { + // println!("LOAD: {address}"); + // dbg!(dfg.type_of_value(*address)); + // dbg!(&dfg[*address]); let target_variable = self.function_context.create_variable( self.brillig_context, dfg.instruction_results(instruction_id)[0], @@ -252,7 +256,9 @@ impl<'block> BrilligBlock<'block> { let bit_size = Self::get_bit_size_from_ssa_type(dfg.type_of_value(*value)); self.brillig_context.not_instruction(condition_register, bit_size, result_register); } - Instruction::Call { func, arguments } => match &dfg[*func] { + Instruction::Call { func, arguments } => { + // dbg!(&dfg[*func]); + match &dfg[*func] { Value::ForeignFunction(func_name) => { let result_ids = dfg.instruction_results(instruction_id); @@ -298,6 +304,7 @@ impl<'block> BrilligBlock<'block> { ); } Value::Intrinsic(Intrinsic::ArrayLen) => { + dbg!("got into ArrayLen"); let result_register = self.function_context.create_register_variable( self.brillig_context, dfg.instruction_results(instruction_id)[0], @@ -373,6 +380,7 @@ impl<'block> BrilligBlock<'block> { _ => { unreachable!("unsupported function call type {:?}", dfg[*func]) } + } }, Instruction::Truncate { value, .. } => { let result_ids = dfg.instruction_results(instruction_id); @@ -598,16 +606,17 @@ impl<'block> BrilligBlock<'block> { arguments: &[ValueId], ) { let slice_id = arguments[1]; - dbg!(&dfg.type_of_value(slice_id)); let element_size = dfg.type_of_value(slice_id).element_size(); let source_variable = self.convert_ssa_value(slice_id, dfg); let source_vector = self.convert_array_or_vector_to_vector(source_variable); dbg!(intrinsic.clone()); + dbg!(arguments.len()); match intrinsic { Value::Intrinsic(Intrinsic::SlicePushBack) => { let results = dfg.instruction_results(instruction_id); - // TODO: maybe we do not want this - match self.function_context.create_variable( + + // TODO: cleanup the source and target len + let target_len = match self.function_context.get_or_create_variable( self.brillig_context, results[0], dfg, @@ -615,62 +624,107 @@ impl<'block> BrilligBlock<'block> { RegisterOrMemory::RegisterIndex(register_index) => register_index, _ => unreachable!("ICE: first value of a slice must be a register index"), }; + let source_len = match self.function_context.get_or_create_variable(self.brillig_context, arguments[0], dfg) { + RegisterOrMemory::RegisterIndex(register_index) => register_index, + _ => unreachable!("ICE: first value of a slice must be a register index"), + }; let target_variable = self.function_context.create_variable( self.brillig_context, results[1], dfg, ); - dbg!("about to extract heap vector"); let target_vector = self.brillig_context.extract_heap_vector(target_variable); - let item_values = vecmap(&arguments[2..element_size + 1], |arg| { + let item_values = vecmap(&arguments[2..element_size + 2], |arg| { self.convert_ssa_value(*arg, dfg) }); - self.slice_push_back_operation(target_vector, source_vector, &item_values); - // self.brillig_context.usize_op_in_place(slice_len, BinaryIntOp::Add, 1); + self.slice_push_back_operation(target_vector, source_vector, &item_values, target_len, source_len); + dbg!("finished slice_push_back"); } Value::Intrinsic(Intrinsic::SlicePushFront) => { + let results = dfg.instruction_results(instruction_id); + + let target_len = match self.function_context.get_or_create_variable( + self.brillig_context, + results[0], + dfg, + ) { + RegisterOrMemory::RegisterIndex(register_index) => register_index, + _ => unreachable!("ICE: first value of a slice must be a register index"), + }; + let source_len = match self.function_context.get_or_create_variable(self.brillig_context, arguments[0], dfg) { + RegisterOrMemory::RegisterIndex(register_index) => register_index, + _ => unreachable!("ICE: first value of a slice must be a register index"), + }; + let target_variable = self.function_context.create_variable( self.brillig_context, - dfg.instruction_results(instruction_id)[0], + results[1], dfg, ); let target_vector = self.brillig_context.extract_heap_vector(target_variable); - let item_values = vecmap(&arguments[2..element_size + 1], |arg| { + let item_values = vecmap(&arguments[2..element_size + 2], |arg| { self.convert_ssa_value(*arg, dfg) - }); - self.slice_push_front_operation(target_vector, source_vector, &item_values); + }); + + self.slice_push_front_operation(target_vector, source_vector, &item_values, target_len, source_len); } Value::Intrinsic(Intrinsic::SlicePopBack) => { let results = dfg.instruction_results(instruction_id); + let target_len = match self.function_context.get_or_create_variable( + self.brillig_context, + results[0], + dfg, + ) { + RegisterOrMemory::RegisterIndex(register_index) => register_index, + _ => unreachable!("ICE: first value of a slice must be a register index"), + }; + let source_len = match self.function_context.get_or_create_variable(self.brillig_context, arguments[0], dfg) { + RegisterOrMemory::RegisterIndex(register_index) => register_index, + _ => unreachable!("ICE: first value of a slice must be a register index"), + }; + let target_variable = - self.function_context.create_variable(self.brillig_context, results[0], dfg); + self.function_context.create_variable(self.brillig_context, results[1], dfg); let target_vector = self.brillig_context.extract_heap_vector(target_variable); - let pop_variables = vecmap(&results[1..element_size + 1], |result| { + let pop_variables = vecmap(&results[2..element_size + 2], |result| { self.function_context.create_variable(self.brillig_context, *result, dfg) }); - self.slice_pop_back_operation(target_vector, source_vector, &pop_variables); + self.slice_pop_back_operation(target_vector, source_vector, &pop_variables, target_len, source_len); } Value::Intrinsic(Intrinsic::SlicePopFront) => { let results = dfg.instruction_results(instruction_id); + let target_len = match self.function_context.get_or_create_variable( + self.brillig_context, + results[element_size], + dfg, + ) { + RegisterOrMemory::RegisterIndex(register_index) => register_index, + _ => unreachable!("ICE: first value of a slice must be a register index"), + }; + let source_len = match self.function_context.get_or_create_variable(self.brillig_context, arguments[0], dfg) { + RegisterOrMemory::RegisterIndex(register_index) => register_index, + _ => unreachable!("ICE: first value of a slice must be a register index"), + }; + let pop_variables = vecmap(&results[0..element_size], |result| { self.function_context.create_variable(self.brillig_context, *result, dfg) }); let target_variable = self.function_context.create_variable( self.brillig_context, - results[element_size], + results[element_size + 1], dfg, ); let target_vector = self.brillig_context.extract_heap_vector(target_variable); - self.slice_pop_front_operation(target_vector, source_vector, &pop_variables); + self.slice_pop_front_operation(target_vector, source_vector, &pop_variables, target_len, source_len); } Value::Intrinsic(Intrinsic::SliceInsert) => { let results = dfg.instruction_results(instruction_id); diff --git a/crates/noirc_evaluator/src/brillig/brillig_gen/brillig_slice_ops.rs b/crates/noirc_evaluator/src/brillig/brillig_gen/brillig_slice_ops.rs index facc4766722..3b0162f7f65 100644 --- a/crates/noirc_evaluator/src/brillig/brillig_gen/brillig_slice_ops.rs +++ b/crates/noirc_evaluator/src/brillig/brillig_gen/brillig_slice_ops.rs @@ -8,7 +8,10 @@ impl<'block> BrilligBlock<'block> { target_vector: HeapVector, source_vector: HeapVector, variables_to_insert: &[RegisterOrMemory], + target_len: RegisterIndex, + source_len: RegisterIndex, ) { + dbg!(variables_to_insert.len()); // First we need to allocate the target vector incrementing the size by variables_to_insert.len() self.brillig_context.usize_op( source_vector.size, @@ -16,6 +19,12 @@ impl<'block> BrilligBlock<'block> { BinaryIntOp::Add, variables_to_insert.len(), ); + self.brillig_context.usize_op( + source_len, + target_len, + BinaryIntOp::Add, + variables_to_insert.len(), + ); self.brillig_context.allocate_array_instruction(target_vector.pointer, target_vector.size); // Now we copy the source vector into the target vector @@ -43,7 +52,10 @@ impl<'block> BrilligBlock<'block> { target_vector: HeapVector, source_vector: HeapVector, variables_to_insert: &[RegisterOrMemory], + target_len: RegisterIndex, + source_len: RegisterIndex, ) { + dbg!(variables_to_insert.len()); // First we need to allocate the target vector incrementing the size by variables_to_insert.len() self.brillig_context.usize_op( source_vector.size, @@ -51,6 +63,12 @@ impl<'block> BrilligBlock<'block> { BinaryIntOp::Add, variables_to_insert.len(), ); + self.brillig_context.usize_op( + source_len, + target_len, + BinaryIntOp::Add, + variables_to_insert.len(), + ); self.brillig_context.allocate_array_instruction(target_vector.pointer, target_vector.size); // Now we offset the target pointer by variables_to_insert.len() @@ -84,6 +102,8 @@ impl<'block> BrilligBlock<'block> { target_vector: HeapVector, source_vector: HeapVector, removed_items: &[RegisterOrMemory], + target_len: RegisterIndex, + source_len: RegisterIndex, ) { // First we need to allocate the target vector decrementing the size by removed_items.len() self.brillig_context.usize_op( @@ -92,6 +112,12 @@ impl<'block> BrilligBlock<'block> { BinaryIntOp::Sub, removed_items.len(), ); + self.brillig_context.usize_op( + source_len, + target_len, + BinaryIntOp::Sub, + removed_items.len(), + ); self.brillig_context.allocate_array_instruction(target_vector.pointer, target_vector.size); // Now we offset the source pointer by removed_items.len() @@ -124,6 +150,8 @@ impl<'block> BrilligBlock<'block> { target_vector: HeapVector, source_vector: HeapVector, removed_items: &[RegisterOrMemory], + target_len: RegisterIndex, + source_len: RegisterIndex, ) { // First we need to allocate the target vector decrementing the size by removed_items.len() self.brillig_context.usize_op( @@ -132,6 +160,12 @@ impl<'block> BrilligBlock<'block> { BinaryIntOp::Sub, removed_items.len(), ); + self.brillig_context.usize_op( + source_len, + target_len, + BinaryIntOp::Sub, + removed_items.len(), + ); self.brillig_context.allocate_array_instruction(target_vector.pointer, target_vector.size); // Now we copy all elements except the last items into the target vector @@ -342,6 +376,7 @@ mod tests { #[test] fn test_slice_push_operation() { + // TODO: handle these tests fn test_case_push( push_back: bool, array: Vec, @@ -377,12 +412,16 @@ mod tests { HeapVector { pointer: copied_array_pointer, size: copied_array_size }, HeapVector { pointer: array_pointer, size: array_size }, &[RegisterOrMemory::RegisterIndex(item_to_insert)], + array_size, + array_size, ); } else { block.slice_push_front_operation( HeapVector { pointer: copied_array_pointer, size: copied_array_size }, HeapVector { pointer: array_pointer, size: array_size }, &[RegisterOrMemory::RegisterIndex(item_to_insert)], + array_size, + array_size, ); } diff --git a/crates/noirc_evaluator/src/brillig/brillig_ir.rs b/crates/noirc_evaluator/src/brillig/brillig_ir.rs index a6d9a32807f..5e5303ce24c 100644 --- a/crates/noirc_evaluator/src/brillig/brillig_ir.rs +++ b/crates/noirc_evaluator/src/brillig/brillig_ir.rs @@ -935,7 +935,6 @@ impl BrilligContext { } pub(crate) fn extract_heap_vector(&mut self, variable: RegisterOrMemory) -> HeapVector { - dbg!(variable.clone()); match variable { RegisterOrMemory::HeapVector(vector) => vector, RegisterOrMemory::HeapArray(array) => { diff --git a/crates/noirc_evaluator/src/ssa.rs b/crates/noirc_evaluator/src/ssa.rs index 591e5c4239c..470fead8a46 100644 --- a/crates/noirc_evaluator/src/ssa.rs +++ b/crates/noirc_evaluator/src/ssa.rs @@ -43,6 +43,10 @@ pub(crate) fn optimize_into_acir( .print(print_ssa_passes, "Initial SSA:") .defunctionalize() .print(print_ssa_passes, "After Defunctionalization:"); + // .mem2reg() + // .print(print_ssa_passes, "After Mem2Reg:") + // .fold_constants() + // .print(print_ssa_passes, "After Constant Folding:"); let brillig = ssa.to_brillig(print_brillig_trace); if let RuntimeType::Acir = ssa.main().runtime() { diff --git a/crates/noirc_evaluator/src/ssa/ir/instruction.rs b/crates/noirc_evaluator/src/ssa/ir/instruction.rs index d12501d0246..c48ce8939b5 100644 --- a/crates/noirc_evaluator/src/ssa/ir/instruction.rs +++ b/crates/noirc_evaluator/src/ssa/ir/instruction.rs @@ -772,6 +772,7 @@ impl std::fmt::Display for BinaryOp { } } +#[derive(Debug, Clone)] /// Contains the result to Instruction::simplify, specifying how the instruction /// should be simplified. pub(crate) enum SimplifyResult { diff --git a/crates/noirc_evaluator/src/ssa/ir/instruction/call.rs b/crates/noirc_evaluator/src/ssa/ir/instruction/call.rs index f26b7f08f57..befa4f2a2b8 100644 --- a/crates/noirc_evaluator/src/ssa/ir/instruction/call.rs +++ b/crates/noirc_evaluator/src/ssa/ir/instruction/call.rs @@ -81,7 +81,7 @@ pub(super) fn simplify_call( dbg!(arguments.len()); // Probably need to check this // arguments.len() > 1 && - if let Some(length) = dfg.try_get_array_length(arguments[0]) { + let simplify_res = if let Some(length) = dfg.try_get_array_length(arguments[0]) { let length = FieldElement::from(length as u128); // let length = length / FieldElement::from(typ.element_size() as u128); SimplifyResult::SimplifiedTo(dfg.make_constant(length, Type::field())) @@ -89,9 +89,12 @@ pub(super) fn simplify_call( SimplifyResult::SimplifiedTo(dfg.make_constant(length, Type::field())) } else if matches!(dfg.type_of_value(arguments[1]), Type::Slice(_)) { SimplifyResult::SimplifiedTo(arguments[0]) + // SimplifyResult::None } else { SimplifyResult::None - } + }; + dbg!(simplify_res.clone()); + simplify_res // if let Some(length) = dfg.get_numeric_constant(arguments[0]) { // dbg!(length); @@ -149,6 +152,7 @@ pub(super) fn simplify_call( } Intrinsic::SlicePushFront => { let slice = dfg.get_array_constant(arguments[1]); + dbg!(slice.clone()); if let Some((mut slice, element_type)) = slice { for elem in arguments[2..].iter().rev() { slice.push_front(*elem); From 2a1da3e8fb93282c9100cec8a4850c0c38432323 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Wed, 16 Aug 2023 13:48:28 +0000 Subject: [PATCH 36/62] working brillig_nested_slices --- .../brillig_slices/src/main.nr | 48 +++---- .../src/brillig/brillig_gen/brillig_block.rs | 120 ++++++++++++++++-- .../brillig/brillig_gen/brillig_slice_ops.rs | 46 ++++--- .../noirc_evaluator/src/brillig/brillig_ir.rs | 4 +- crates/noirc_evaluator/src/ssa/ssa_gen/mod.rs | 24 +++- 5 files changed, 176 insertions(+), 66 deletions(-) diff --git a/crates/nargo_cli/tests/execution_success/brillig_slices/src/main.nr b/crates/nargo_cli/tests/execution_success/brillig_slices/src/main.nr index c726ef0121e..1f09435c8be 100644 --- a/crates/nargo_cli/tests/execution_success/brillig_slices/src/main.nr +++ b/crates/nargo_cli/tests/execution_success/brillig_slices/src/main.nr @@ -47,30 +47,30 @@ unconstrained fn main(x: Field, y: Field) { assert(slice[0] == x); assert(slice[1] == y); - // slice = slice.insert(1, 2); - // assert(slice.len() == 3); - // assert(slice[0] == x); - // assert(slice[1] == 2); - // assert(slice[2] == y); - - // let (removed_slice, should_be_2) = slice.remove(1); - // slice = removed_slice; - // assert(should_be_2 == 2); - - // assert(slice.len() == 2); - // assert(slice[0] == x); - // assert(slice[1] == y); - - // let (slice_with_only_x, should_be_y) = slice.remove(1); - // slice = slice_with_only_x; - // assert(should_be_y == y); - - // assert(slice.len() == 1); - // assert(slice[0] == x); - - // let (empty_slice, should_be_x) = slice.remove(0); - // assert(should_be_x == x); - // assert(empty_slice.len() == 0); + slice = slice.insert(1, 2); + assert(slice.len() == 3); + assert(slice[0] == x); + assert(slice[1] == 2); + assert(slice[2] == y); + + let (removed_slice, should_be_2) = slice.remove(1); + slice = removed_slice; + assert(should_be_2 == 2); + + assert(slice.len() == 2); + assert(slice[0] == x); + assert(slice[1] == y); + + let (slice_with_only_x, should_be_y) = slice.remove(1); + slice = slice_with_only_x; + assert(should_be_y == y); + + assert(slice.len() == 1); + assert(slice[0] == x); + + let (empty_slice, should_be_x) = slice.remove(0); + assert(should_be_x == x); + assert(empty_slice.len() == 0); } // Tests slice passing to/from functions diff --git a/crates/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs b/crates/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs index da08b139e4a..8f65991ec57 100644 --- a/crates/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs +++ b/crates/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs @@ -267,7 +267,7 @@ impl<'block> BrilligBlock<'block> { let output_registers = vecmap(result_ids, |value_id| { self.allocate_external_call_result(*value_id, dfg) }); - + dbg!(output_registers.len()); self.brillig_context.foreign_call_instruction( func_name.to_owned(), &input_registers, @@ -342,14 +342,28 @@ impl<'block> BrilligBlock<'block> { let source = self.convert_ssa_register_value(arguments[0], dfg); let radix = self.convert_ssa_register_value(arguments[1], dfg); let limb_count = self.convert_ssa_register_value(arguments[2], dfg); + + let results = dfg.instruction_results(instruction_id); + dbg!(results); + let target_len = match self.function_context.get_or_create_variable( + self.brillig_context, + results[0], + dfg, + ) { + RegisterOrMemory::RegisterIndex(register_index) => register_index, + _ => unreachable!("ICE: first value of a slice must be a register index"), + }; + let target_slice = self.function_context.create_variable( self.brillig_context, - dfg.instruction_results(instruction_id)[0], + results[1], dfg, ); + let heap_vec = self.brillig_context.extract_heap_vector(target_slice); self.brillig_context.radix_instruction( source, + target_len, heap_vec, radix, limb_count, @@ -359,9 +373,21 @@ impl<'block> BrilligBlock<'block> { Value::Intrinsic(Intrinsic::ToBits(endianness)) => { let source = self.convert_ssa_register_value(arguments[0], dfg); let limb_count = self.convert_ssa_register_value(arguments[1], dfg); + + let results = dfg.instruction_results(instruction_id); + + let target_len = match self.function_context.get_or_create_variable( + self.brillig_context, + results[0], + dfg, + ) { + RegisterOrMemory::RegisterIndex(register_index) => register_index, + _ => unreachable!("ICE: first value of a slice must be a register index"), + }; + let target_slice = self.function_context.create_variable( self.brillig_context, - dfg.instruction_results(instruction_id)[0], + results[1], dfg, ); @@ -369,6 +395,7 @@ impl<'block> BrilligBlock<'block> { let heap_vec = self.brillig_context.extract_heap_vector(target_slice); self.brillig_context.radix_instruction( source, + target_len, heap_vec, radix, limb_count, @@ -639,6 +666,15 @@ impl<'block> BrilligBlock<'block> { let item_values = vecmap(&arguments[2..element_size + 2], |arg| { self.convert_ssa_value(*arg, dfg) }); + dbg!(item_values.len()); + dbg!(item_values.len() / element_size); + self.brillig_context.usize_op( + source_len, + target_len, + BinaryIntOp::Add, + item_values.len() / element_size, + ); + self.slice_push_back_operation(target_vector, source_vector, &item_values, target_len, source_len); dbg!("finished slice_push_back"); } @@ -667,6 +703,12 @@ impl<'block> BrilligBlock<'block> { let item_values = vecmap(&arguments[2..element_size + 2], |arg| { self.convert_ssa_value(*arg, dfg) }); + self.brillig_context.usize_op( + source_len, + target_len, + BinaryIntOp::Add, + item_values.len() / element_size, + ); self.slice_push_front_operation(target_vector, source_vector, &item_values, target_len, source_len); } @@ -694,6 +736,12 @@ impl<'block> BrilligBlock<'block> { let pop_variables = vecmap(&results[2..element_size + 2], |result| { self.function_context.create_variable(self.brillig_context, *result, dfg) }); + self.brillig_context.usize_op( + source_len, + target_len, + BinaryIntOp::Sub, + pop_variables.len() / element_size, + ); self.slice_pop_back_operation(target_vector, source_vector, &pop_variables, target_len, source_len); } @@ -716,6 +764,12 @@ impl<'block> BrilligBlock<'block> { let pop_variables = vecmap(&results[0..element_size], |result| { self.function_context.create_variable(self.brillig_context, *result, dfg) }); + self.brillig_context.usize_op( + source_len, + target_len, + BinaryIntOp::Sub, + pop_variables.len() / element_size, + ); let target_variable = self.function_context.create_variable( self.brillig_context, @@ -728,7 +782,21 @@ impl<'block> BrilligBlock<'block> { } Value::Intrinsic(Intrinsic::SliceInsert) => { let results = dfg.instruction_results(instruction_id); - let target_id = results[0]; + + let target_len = match self.function_context.get_or_create_variable( + self.brillig_context, + results[0], + dfg, + ) { + RegisterOrMemory::RegisterIndex(register_index) => register_index, + _ => unreachable!("ICE: first value of a slice must be a register index"), + }; + let source_len = match self.function_context.get_or_create_variable(self.brillig_context, arguments[0], dfg) { + RegisterOrMemory::RegisterIndex(register_index) => register_index, + _ => unreachable!("ICE: first value of a slice must be a register index"), + }; + + let target_id = results[1]; let target_variable = self.function_context.create_variable(self.brillig_context, target_id, dfg); @@ -736,7 +804,7 @@ impl<'block> BrilligBlock<'block> { // Remove if indexing in insert is changed to flattened indexing // https://github.com/noir-lang/noir/issues/1889#issuecomment-1668048587 - let user_index = self.convert_ssa_register_value(arguments[1], dfg); + let user_index = self.convert_ssa_register_value(arguments[2], dfg); let converted_index = self.brillig_context.make_constant(element_size.into()); @@ -746,17 +814,37 @@ impl<'block> BrilligBlock<'block> { converted_index, BinaryIntOp::Mul, ); - - let items = vecmap(&arguments[2..element_size + 2], |arg| { + dbg!(element_size + 3); + let items = vecmap(&arguments[3..element_size + 3], |arg| { self.convert_ssa_value(*arg, dfg) }); + self.brillig_context.usize_op( + source_len, + target_len, + BinaryIntOp::Add, + items.len() / element_size, + ); - self.slice_insert_operation(target_vector, source_vector, converted_index, &items); + self.slice_insert_operation(target_vector, source_vector, converted_index, &items, target_len, source_len); self.brillig_context.deallocate_register(converted_index); } Value::Intrinsic(Intrinsic::SliceRemove) => { let results = dfg.instruction_results(instruction_id); - let target_id = results[0]; + + let target_len = match self.function_context.get_or_create_variable( + self.brillig_context, + results[0], + dfg, + ) { + RegisterOrMemory::RegisterIndex(register_index) => register_index, + _ => unreachable!("ICE: first value of a slice must be a register index"), + }; + let source_len = match self.function_context.get_or_create_variable(self.brillig_context, arguments[0], dfg) { + RegisterOrMemory::RegisterIndex(register_index) => register_index, + _ => unreachable!("ICE: first value of a slice must be a register index"), + }; + + let target_id = results[1]; let target_variable = self.function_context.create_variable(self.brillig_context, target_id, dfg); @@ -764,7 +852,7 @@ impl<'block> BrilligBlock<'block> { // Remove if indexing in remove is changed to flattened indexing // https://github.com/noir-lang/noir/issues/1889#issuecomment-1668048587 - let user_index = self.convert_ssa_register_value(arguments[1], dfg); + let user_index = self.convert_ssa_register_value(arguments[2], dfg); let converted_index = self.brillig_context.make_constant(element_size.into()); self.brillig_context.memory_op( @@ -774,15 +862,23 @@ impl<'block> BrilligBlock<'block> { BinaryIntOp::Mul, ); - let removed_items = vecmap(&results[1..element_size + 1], |result| { + let removed_items = vecmap(&results[2..element_size + 2], |result| { self.function_context.create_variable(self.brillig_context, *result, dfg) }); + self.brillig_context.usize_op( + source_len, + target_len, + BinaryIntOp::Sub, + removed_items.len() / element_size, + ); self.slice_remove_operation( target_vector, source_vector, converted_index, &removed_items, + target_len, + source_len, ); self.brillig_context.deallocate_register(converted_index); @@ -976,7 +1072,7 @@ impl<'block> BrilligBlock<'block> { let vector = self.brillig_context.extract_heap_vector(variable); // Set the pointer to the current stack frame - // The stack pointer will then be update by the caller of this method + // The stack pointer will then be updated by the caller of this method // once the external call is resolved and the array size is known self.brillig_context.set_array_pointer(vector.pointer); variable diff --git a/crates/noirc_evaluator/src/brillig/brillig_gen/brillig_slice_ops.rs b/crates/noirc_evaluator/src/brillig/brillig_gen/brillig_slice_ops.rs index 3b0162f7f65..fe2eae075ab 100644 --- a/crates/noirc_evaluator/src/brillig/brillig_gen/brillig_slice_ops.rs +++ b/crates/noirc_evaluator/src/brillig/brillig_gen/brillig_slice_ops.rs @@ -19,12 +19,6 @@ impl<'block> BrilligBlock<'block> { BinaryIntOp::Add, variables_to_insert.len(), ); - self.brillig_context.usize_op( - source_len, - target_len, - BinaryIntOp::Add, - variables_to_insert.len(), - ); self.brillig_context.allocate_array_instruction(target_vector.pointer, target_vector.size); // Now we copy the source vector into the target vector @@ -63,12 +57,6 @@ impl<'block> BrilligBlock<'block> { BinaryIntOp::Add, variables_to_insert.len(), ); - self.brillig_context.usize_op( - source_len, - target_len, - BinaryIntOp::Add, - variables_to_insert.len(), - ); self.brillig_context.allocate_array_instruction(target_vector.pointer, target_vector.size); // Now we offset the target pointer by variables_to_insert.len() @@ -112,12 +100,6 @@ impl<'block> BrilligBlock<'block> { BinaryIntOp::Sub, removed_items.len(), ); - self.brillig_context.usize_op( - source_len, - target_len, - BinaryIntOp::Sub, - removed_items.len(), - ); self.brillig_context.allocate_array_instruction(target_vector.pointer, target_vector.size); // Now we offset the source pointer by removed_items.len() @@ -160,12 +142,6 @@ impl<'block> BrilligBlock<'block> { BinaryIntOp::Sub, removed_items.len(), ); - self.brillig_context.usize_op( - source_len, - target_len, - BinaryIntOp::Sub, - removed_items.len(), - ); self.brillig_context.allocate_array_instruction(target_vector.pointer, target_vector.size); // Now we copy all elements except the last items into the target vector @@ -194,6 +170,8 @@ impl<'block> BrilligBlock<'block> { source_vector: HeapVector, index: RegisterIndex, items: &[RegisterOrMemory], + target_len: RegisterIndex, + source_len: RegisterIndex, ) { // First we need to allocate the target vector incrementing the size by items.len() self.brillig_context.usize_op( @@ -264,6 +242,8 @@ impl<'block> BrilligBlock<'block> { source_vector: HeapVector, index: RegisterIndex, removed_items: &[RegisterOrMemory], + target_len: RegisterIndex, + source_len: RegisterIndex, ) { // First we need to allocate the target vector decrementing the size by removed_items.len() self.brillig_context.usize_op( @@ -503,19 +483,27 @@ mod tests { let copied_array_size = context.allocate_register(); + let slice_size = context.make_constant(array.len().into()); + let copied_slice_size = context.allocate_register(); + let mut block = create_brillig_block(&mut function_context, &mut context); + if pop_back { block.slice_pop_back_operation( HeapVector { pointer: copied_array_pointer, size: copied_array_size }, HeapVector { pointer: array_pointer, size: array_size }, &[RegisterOrMemory::RegisterIndex(removed_item)], + copied_slice_size, + slice_size, ); } else { block.slice_pop_front_operation( HeapVector { pointer: copied_array_pointer, size: copied_array_size }, HeapVector { pointer: array_pointer, size: array_size }, &[RegisterOrMemory::RegisterIndex(removed_item)], + copied_slice_size, + slice_size, ); } @@ -601,6 +589,9 @@ mod tests { let copied_array_size = context.allocate_register(); + let slice_size = context.make_constant(array.len().into()); + let copied_slice_size = context.allocate_register(); + let mut block = create_brillig_block(&mut function_context, &mut context); block.slice_insert_operation( @@ -608,6 +599,8 @@ mod tests { HeapVector { pointer: array_pointer, size: array_size }, index_to_insert, &[RegisterOrMemory::RegisterIndex(item_to_insert)], + copied_slice_size, + slice_size, ); context.return_instruction(&[copied_array_pointer, copied_array_size]); @@ -724,6 +717,9 @@ mod tests { let copied_array_size = context.allocate_register(); + let slice_size = context.make_constant(array.len().into()); + let copied_slice_size = context.allocate_register(); + let mut block = create_brillig_block(&mut function_context, &mut context); block.slice_remove_operation( @@ -731,6 +727,8 @@ mod tests { HeapVector { pointer: array_pointer, size: array_size }, index_to_insert, &[RegisterOrMemory::RegisterIndex(removed_item)], + copied_slice_size, + slice_size, ); context.return_instruction(&[copied_array_pointer, copied_array_size, removed_item]); diff --git a/crates/noirc_evaluator/src/brillig/brillig_ir.rs b/crates/noirc_evaluator/src/brillig/brillig_ir.rs index 5e5303ce24c..76d35d1da7a 100644 --- a/crates/noirc_evaluator/src/brillig/brillig_ir.rs +++ b/crates/noirc_evaluator/src/brillig/brillig_ir.rs @@ -853,11 +853,14 @@ impl BrilligContext { pub(crate) fn radix_instruction( &mut self, source: RegisterIndex, + target_vector_len: RegisterIndex, target_vector: HeapVector, radix: RegisterIndex, limb_count: RegisterIndex, big_endian: bool, ) { + self.mov_instruction(target_vector_len, limb_count); + self.usize_op_in_place(target_vector_len, BinaryIntOp::Add, 1usize); self.mov_instruction(target_vector.size, limb_count); self.allocate_array_instruction(target_vector.pointer, target_vector.size); @@ -938,7 +941,6 @@ impl BrilligContext { match variable { RegisterOrMemory::HeapVector(vector) => vector, RegisterOrMemory::HeapArray(array) => { - dbg!(array.size); let size = self.allocate_register(); self.const_instruction(size, array.size.into()); HeapVector { pointer: array.pointer, size } diff --git a/crates/noirc_evaluator/src/ssa/ssa_gen/mod.rs b/crates/noirc_evaluator/src/ssa/ssa_gen/mod.rs index 1892654909d..d86cf7c15a7 100644 --- a/crates/noirc_evaluator/src/ssa/ssa_gen/mod.rs +++ b/crates/noirc_evaluator/src/ssa/ssa_gen/mod.rs @@ -316,13 +316,27 @@ impl<'a> FunctionContext<'a> { max_length.expect("ICE: a length must be supplied for indexing slices"); // If the index and the array_len are both Fields we will not be able to perform a less than comparison on them // Thus, we cast the array len to a u64 before performing the less than comparison - let array_len_int = self.builder.insert_cast( - array_len, - Type::Numeric(NumericType::Unsigned { bit_size: 64 }), - ); + let array_len = match self.builder.type_of_value(index) { + Type::Numeric(numeric_type) => { + match numeric_type { + NumericType::Unsigned { .. } | NumericType::Signed { .. } => array_len, + NumericType::NativeField => { + self.builder.insert_cast( + array_len, + Type::Numeric(NumericType::Unsigned { bit_size: 64 }), + ) + } + } + } + _ => unreachable!("ICE: array index must be a numeric type"), + }; + // let array_len_int = self.builder.insert_cast( + // array_len, + // Type::Numeric(NumericType::Unsigned { bit_size: 64 }), + // ); let is_offset_out_of_bounds = - self.builder.insert_binary(index, BinaryOp::Lt, array_len_int); + self.builder.insert_binary(index, BinaryOp::Lt, array_len); self.builder.insert_constrain(is_offset_out_of_bounds); } Type::Array(..) => { From 2a95fbf4fbfc06bedbe010c2cc5f9ab1eede9ed8 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Wed, 16 Aug 2023 13:49:28 +0000 Subject: [PATCH 37/62] missed save --- crates/noirc_evaluator/src/ssa/ir/instruction/call.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/noirc_evaluator/src/ssa/ir/instruction/call.rs b/crates/noirc_evaluator/src/ssa/ir/instruction/call.rs index befa4f2a2b8..1127e791a73 100644 --- a/crates/noirc_evaluator/src/ssa/ir/instruction/call.rs +++ b/crates/noirc_evaluator/src/ssa/ir/instruction/call.rs @@ -89,7 +89,6 @@ pub(super) fn simplify_call( SimplifyResult::SimplifiedTo(dfg.make_constant(length, Type::field())) } else if matches!(dfg.type_of_value(arguments[1]), Type::Slice(_)) { SimplifyResult::SimplifiedTo(arguments[0]) - // SimplifyResult::None } else { SimplifyResult::None }; From c05e92c51ae954ee1d1b48d375b6ddb66ac92c76 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Wed, 16 Aug 2023 15:05:13 +0000 Subject: [PATCH 38/62] working brillig oracle with new slices --- crates/nargo/src/ops/foreign_calls.rs | 10 +++-- .../src/brillig/brillig_gen/brillig_block.rs | 39 ++++++++----------- .../brillig/brillig_gen/brillig_slice_ops.rs | 29 -------------- .../src/ssa/ssa_builder/mod.rs | 20 +++++++--- 4 files changed, 37 insertions(+), 61 deletions(-) diff --git a/crates/nargo/src/ops/foreign_calls.rs b/crates/nargo/src/ops/foreign_calls.rs index 4d2f5988e38..aa5121d9329 100644 --- a/crates/nargo/src/ops/foreign_calls.rs +++ b/crates/nargo/src/ops/foreign_calls.rs @@ -1,5 +1,5 @@ use acvm::{ - acir::brillig::{ForeignCallResult, Value}, + acir::brillig::{ForeignCallResult, ForeignCallOutput, Value}, pwg::ForeignCallWaitInfo, }; use iter_extended::vecmap; @@ -54,13 +54,15 @@ impl ForeignCall { } Some(ForeignCall::Sequence) => { let sequence_length: u128 = foreign_call.inputs[0][0].to_field().to_u128(); - - Ok(vecmap(0..sequence_length, Value::from).into()) + let sequence = vecmap(0..sequence_length, Value::from); + + Ok(ForeignCallResult { values: vec![ForeignCallOutput::Single(sequence_length.into()), ForeignCallOutput::Array(sequence) ] }) } Some(ForeignCall::ReverseSequence) => { let sequence_length: u128 = foreign_call.inputs[0][0].to_field().to_u128(); + let sequence = vecmap((0..sequence_length).rev(), Value::from); - Ok(vecmap((0..sequence_length).rev(), Value::from).into()) + Ok(ForeignCallResult { values: vec![ForeignCallOutput::Single(sequence_length.into()), ForeignCallOutput::Array(sequence) ] }) } None => panic!("unexpected foreign call {:?}", foreign_call_name), } diff --git a/crates/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs b/crates/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs index 8f65991ec57..abd10a4738a 100644 --- a/crates/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs +++ b/crates/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs @@ -233,9 +233,6 @@ impl<'block> BrilligBlock<'block> { self.brillig_context.store_variable_instruction(address_register, source_variable); } Instruction::Load { address } => { - // println!("LOAD: {address}"); - // dbg!(dfg.type_of_value(*address)); - // dbg!(&dfg[*address]); let target_variable = self.function_context.create_variable( self.brillig_context, dfg.instruction_results(instruction_id)[0], @@ -274,13 +271,21 @@ impl<'block> BrilligBlock<'block> { &output_registers, ); - for output_register in output_registers { + for (i, output_register) in output_registers.iter().enumerate() { if let RegisterOrMemory::HeapVector(HeapVector { size, .. }) = output_register { // Update the stack pointer so that we do not overwrite // dynamic memory returned from other external calls - self.brillig_context.update_stack_pointer(size); + self.brillig_context.update_stack_pointer(*size); + + if let RegisterOrMemory::RegisterIndex(len_index) = output_registers[i - 1] { + let element_size = dfg[result_ids[i]].get_type().element_size(); + self.brillig_context.mov_instruction(len_index, *size); + self.brillig_context.usize_op_in_place(len_index, BinaryIntOp::UnsignedDiv, element_size); + } else { + unreachable!("ICE: a vector must be preceded by a register containing its length"); + } } // Single values and allocation of fixed sized arrays has already been handled // inside of `allocate_external_call_result` @@ -304,21 +309,14 @@ impl<'block> BrilligBlock<'block> { ); } Value::Intrinsic(Intrinsic::ArrayLen) => { - dbg!("got into ArrayLen"); let result_register = self.function_context.create_register_variable( self.brillig_context, dfg.instruction_results(instruction_id)[0], dfg, ); let param_id = arguments[0]; - dbg!(dfg.type_of_value(param_id)); - dbg!(&dfg[arguments[0]]); - if let Type::Numeric(numeric_type) = dfg.type_of_value(param_id) { - dbg!(numeric_type); - // self.brillig_context - // .const_instruction(result_register, (constant).into()); - let len = self.convert_ssa_value(arguments[0], dfg); - dbg!(len); + if let Type::Numeric(_) = dfg.type_of_value(param_id) { + self.convert_ssa_value(arguments[0], dfg); } else { self.convert_ssa_array_len(arguments[0], result_register, dfg); } @@ -675,8 +673,7 @@ impl<'block> BrilligBlock<'block> { item_values.len() / element_size, ); - self.slice_push_back_operation(target_vector, source_vector, &item_values, target_len, source_len); - dbg!("finished slice_push_back"); + self.slice_push_back_operation(target_vector, source_vector, &item_values); } Value::Intrinsic(Intrinsic::SlicePushFront) => { let results = dfg.instruction_results(instruction_id); @@ -710,7 +707,7 @@ impl<'block> BrilligBlock<'block> { item_values.len() / element_size, ); - self.slice_push_front_operation(target_vector, source_vector, &item_values, target_len, source_len); + self.slice_push_front_operation(target_vector, source_vector, &item_values); } Value::Intrinsic(Intrinsic::SlicePopBack) => { let results = dfg.instruction_results(instruction_id); @@ -743,7 +740,7 @@ impl<'block> BrilligBlock<'block> { pop_variables.len() / element_size, ); - self.slice_pop_back_operation(target_vector, source_vector, &pop_variables, target_len, source_len); + self.slice_pop_back_operation(target_vector, source_vector, &pop_variables); } Value::Intrinsic(Intrinsic::SlicePopFront) => { let results = dfg.instruction_results(instruction_id); @@ -778,7 +775,7 @@ impl<'block> BrilligBlock<'block> { ); let target_vector = self.brillig_context.extract_heap_vector(target_variable); - self.slice_pop_front_operation(target_vector, source_vector, &pop_variables, target_len, source_len); + self.slice_pop_front_operation(target_vector, source_vector, &pop_variables); } Value::Intrinsic(Intrinsic::SliceInsert) => { let results = dfg.instruction_results(instruction_id); @@ -825,7 +822,7 @@ impl<'block> BrilligBlock<'block> { items.len() / element_size, ); - self.slice_insert_operation(target_vector, source_vector, converted_index, &items, target_len, source_len); + self.slice_insert_operation(target_vector, source_vector, converted_index, &items); self.brillig_context.deallocate_register(converted_index); } Value::Intrinsic(Intrinsic::SliceRemove) => { @@ -877,8 +874,6 @@ impl<'block> BrilligBlock<'block> { source_vector, converted_index, &removed_items, - target_len, - source_len, ); self.brillig_context.deallocate_register(converted_index); diff --git a/crates/noirc_evaluator/src/brillig/brillig_gen/brillig_slice_ops.rs b/crates/noirc_evaluator/src/brillig/brillig_gen/brillig_slice_ops.rs index fe2eae075ab..8a9b68f44ba 100644 --- a/crates/noirc_evaluator/src/brillig/brillig_gen/brillig_slice_ops.rs +++ b/crates/noirc_evaluator/src/brillig/brillig_gen/brillig_slice_ops.rs @@ -8,10 +8,7 @@ impl<'block> BrilligBlock<'block> { target_vector: HeapVector, source_vector: HeapVector, variables_to_insert: &[RegisterOrMemory], - target_len: RegisterIndex, - source_len: RegisterIndex, ) { - dbg!(variables_to_insert.len()); // First we need to allocate the target vector incrementing the size by variables_to_insert.len() self.brillig_context.usize_op( source_vector.size, @@ -46,10 +43,7 @@ impl<'block> BrilligBlock<'block> { target_vector: HeapVector, source_vector: HeapVector, variables_to_insert: &[RegisterOrMemory], - target_len: RegisterIndex, - source_len: RegisterIndex, ) { - dbg!(variables_to_insert.len()); // First we need to allocate the target vector incrementing the size by variables_to_insert.len() self.brillig_context.usize_op( source_vector.size, @@ -90,8 +84,6 @@ impl<'block> BrilligBlock<'block> { target_vector: HeapVector, source_vector: HeapVector, removed_items: &[RegisterOrMemory], - target_len: RegisterIndex, - source_len: RegisterIndex, ) { // First we need to allocate the target vector decrementing the size by removed_items.len() self.brillig_context.usize_op( @@ -132,8 +124,6 @@ impl<'block> BrilligBlock<'block> { target_vector: HeapVector, source_vector: HeapVector, removed_items: &[RegisterOrMemory], - target_len: RegisterIndex, - source_len: RegisterIndex, ) { // First we need to allocate the target vector decrementing the size by removed_items.len() self.brillig_context.usize_op( @@ -170,8 +160,6 @@ impl<'block> BrilligBlock<'block> { source_vector: HeapVector, index: RegisterIndex, items: &[RegisterOrMemory], - target_len: RegisterIndex, - source_len: RegisterIndex, ) { // First we need to allocate the target vector incrementing the size by items.len() self.brillig_context.usize_op( @@ -242,8 +230,6 @@ impl<'block> BrilligBlock<'block> { source_vector: HeapVector, index: RegisterIndex, removed_items: &[RegisterOrMemory], - target_len: RegisterIndex, - source_len: RegisterIndex, ) { // First we need to allocate the target vector decrementing the size by removed_items.len() self.brillig_context.usize_op( @@ -392,16 +378,12 @@ mod tests { HeapVector { pointer: copied_array_pointer, size: copied_array_size }, HeapVector { pointer: array_pointer, size: array_size }, &[RegisterOrMemory::RegisterIndex(item_to_insert)], - array_size, - array_size, ); } else { block.slice_push_front_operation( HeapVector { pointer: copied_array_pointer, size: copied_array_size }, HeapVector { pointer: array_pointer, size: array_size }, &[RegisterOrMemory::RegisterIndex(item_to_insert)], - array_size, - array_size, ); } @@ -494,16 +476,12 @@ mod tests { HeapVector { pointer: copied_array_pointer, size: copied_array_size }, HeapVector { pointer: array_pointer, size: array_size }, &[RegisterOrMemory::RegisterIndex(removed_item)], - copied_slice_size, - slice_size, ); } else { block.slice_pop_front_operation( HeapVector { pointer: copied_array_pointer, size: copied_array_size }, HeapVector { pointer: array_pointer, size: array_size }, &[RegisterOrMemory::RegisterIndex(removed_item)], - copied_slice_size, - slice_size, ); } @@ -599,8 +577,6 @@ mod tests { HeapVector { pointer: array_pointer, size: array_size }, index_to_insert, &[RegisterOrMemory::RegisterIndex(item_to_insert)], - copied_slice_size, - slice_size, ); context.return_instruction(&[copied_array_pointer, copied_array_size]); @@ -717,9 +693,6 @@ mod tests { let copied_array_size = context.allocate_register(); - let slice_size = context.make_constant(array.len().into()); - let copied_slice_size = context.allocate_register(); - let mut block = create_brillig_block(&mut function_context, &mut context); block.slice_remove_operation( @@ -727,8 +700,6 @@ mod tests { HeapVector { pointer: array_pointer, size: array_size }, index_to_insert, &[RegisterOrMemory::RegisterIndex(removed_item)], - copied_slice_size, - slice_size, ); context.return_instruction(&[copied_array_pointer, copied_array_size, removed_item]); diff --git a/crates/noirc_evaluator/src/ssa/ssa_builder/mod.rs b/crates/noirc_evaluator/src/ssa/ssa_builder/mod.rs index 61daa06e37b..5c6a79b7cf2 100644 --- a/crates/noirc_evaluator/src/ssa/ssa_builder/mod.rs +++ b/crates/noirc_evaluator/src/ssa/ssa_builder/mod.rs @@ -399,15 +399,23 @@ mod tests { let input = builder.numeric_constant(FieldElement::from(7_u128), Type::field()); let length = builder.numeric_constant(FieldElement::from(8_u128), Type::field()); let result_types = vec![Type::Array(Rc::new(vec![Type::bool()]), 8)]; - let call_result = builder.insert_call(to_bits_id, vec![input, length], result_types)[0]; + let call_results = builder.insert_call(to_bits_id, vec![input, length], result_types).into_owned(); - let array = match &builder.current_function.dfg[call_result] { + let slice_len = match &builder.current_function.dfg[call_results[0]] { + Value::NumericConstant { constant, .. } => { + *constant + } + _ => panic!(), + }; + assert_eq!(slice_len, FieldElement::from(256u128)); + + let slice = match &builder.current_function.dfg[call_results[1]] { Value::Array { array, .. } => array, _ => panic!(), }; - assert_eq!(array[0], one); - assert_eq!(array[1], one); - assert_eq!(array[2], one); - assert_eq!(array[3], zero); + assert_eq!(slice[0], one); + assert_eq!(slice[1], one); + assert_eq!(slice[2], one); + assert_eq!(slice[3], zero); } } From 3f370043e018ba141a9951195c8aa26c89aa0e57 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Wed, 16 Aug 2023 15:28:29 +0000 Subject: [PATCH 39/62] cleanup comments and dbgs --- .../src/brillig/brillig_gen/brillig_block.rs | 15 ++----- .../brillig/brillig_gen/brillig_slice_ops.rs | 10 +---- crates/noirc_evaluator/src/errors.rs | 1 - crates/noirc_evaluator/src/ssa.rs | 4 -- .../noirc_evaluator/src/ssa/acir_gen/mod.rs | 13 ++---- .../src/ssa/ir/instruction/call.rs | 42 ++----------------- .../src/ssa/opt/flatten_cfg.rs | 9 +--- .../src/ssa/ssa_gen/context.rs | 3 +- crates/noirc_evaluator/src/ssa/ssa_gen/mod.rs | 20 +-------- .../src/monomorphization/mod.rs | 3 +- 10 files changed, 18 insertions(+), 102 deletions(-) diff --git a/crates/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs b/crates/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs index abd10a4738a..159589b55e4 100644 --- a/crates/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs +++ b/crates/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs @@ -253,9 +253,7 @@ impl<'block> BrilligBlock<'block> { let bit_size = Self::get_bit_size_from_ssa_type(dfg.type_of_value(*value)); self.brillig_context.not_instruction(condition_register, bit_size, result_register); } - Instruction::Call { func, arguments } => { - // dbg!(&dfg[*func]); - match &dfg[*func] { + Instruction::Call { func, arguments } => match &dfg[*func] { Value::ForeignFunction(func_name) => { let result_ids = dfg.instruction_results(instruction_id); @@ -264,7 +262,6 @@ impl<'block> BrilligBlock<'block> { let output_registers = vecmap(result_ids, |value_id| { self.allocate_external_call_result(*value_id, dfg) }); - dbg!(output_registers.len()); self.brillig_context.foreign_call_instruction( func_name.to_owned(), &input_registers, @@ -342,7 +339,6 @@ impl<'block> BrilligBlock<'block> { let limb_count = self.convert_ssa_register_value(arguments[2], dfg); let results = dfg.instruction_results(instruction_id); - dbg!(results); let target_len = match self.function_context.get_or_create_variable( self.brillig_context, results[0], @@ -405,7 +401,6 @@ impl<'block> BrilligBlock<'block> { _ => { unreachable!("unsupported function call type {:?}", dfg[*func]) } - } }, Instruction::Truncate { value, .. } => { let result_ids = dfg.instruction_results(instruction_id); @@ -634,8 +629,7 @@ impl<'block> BrilligBlock<'block> { let element_size = dfg.type_of_value(slice_id).element_size(); let source_variable = self.convert_ssa_value(slice_id, dfg); let source_vector = self.convert_array_or_vector_to_vector(source_variable); - dbg!(intrinsic.clone()); - dbg!(arguments.len()); + match intrinsic { Value::Intrinsic(Intrinsic::SlicePushBack) => { let results = dfg.instruction_results(instruction_id); @@ -664,8 +658,7 @@ impl<'block> BrilligBlock<'block> { let item_values = vecmap(&arguments[2..element_size + 2], |arg| { self.convert_ssa_value(*arg, dfg) }); - dbg!(item_values.len()); - dbg!(item_values.len() / element_size); + self.brillig_context.usize_op( source_len, target_len, @@ -811,7 +804,7 @@ impl<'block> BrilligBlock<'block> { converted_index, BinaryIntOp::Mul, ); - dbg!(element_size + 3); + let items = vecmap(&arguments[3..element_size + 3], |arg| { self.convert_ssa_value(*arg, dfg) }); diff --git a/crates/noirc_evaluator/src/brillig/brillig_gen/brillig_slice_ops.rs b/crates/noirc_evaluator/src/brillig/brillig_gen/brillig_slice_ops.rs index 8a9b68f44ba..c6f2a499bd7 100644 --- a/crates/noirc_evaluator/src/brillig/brillig_gen/brillig_slice_ops.rs +++ b/crates/noirc_evaluator/src/brillig/brillig_gen/brillig_slice_ops.rs @@ -342,7 +342,6 @@ mod tests { #[test] fn test_slice_push_operation() { - // TODO: handle these tests fn test_case_push( push_back: bool, array: Vec, @@ -465,12 +464,8 @@ mod tests { let copied_array_size = context.allocate_register(); - let slice_size = context.make_constant(array.len().into()); - let copied_slice_size = context.allocate_register(); - let mut block = create_brillig_block(&mut function_context, &mut context); - if pop_back { block.slice_pop_back_operation( HeapVector { pointer: copied_array_pointer, size: copied_array_size }, @@ -566,10 +561,7 @@ mod tests { let copied_array_pointer = context.allocate_register(); let copied_array_size = context.allocate_register(); - - let slice_size = context.make_constant(array.len().into()); - let copied_slice_size = context.allocate_register(); - + let mut block = create_brillig_block(&mut function_context, &mut context); block.slice_insert_operation( diff --git a/crates/noirc_evaluator/src/errors.rs b/crates/noirc_evaluator/src/errors.rs index 8fac556b22c..aebadfcd0ae 100644 --- a/crates/noirc_evaluator/src/errors.rs +++ b/crates/noirc_evaluator/src/errors.rs @@ -80,7 +80,6 @@ impl RuntimeError { impl From for FileDiagnostic { fn from(error: RuntimeError) -> Self { - dbg!(error.clone()); let file_id = error.location().map(|loc| loc.file).unwrap(); FileDiagnostic { file_id, diagnostic: error.into() } } diff --git a/crates/noirc_evaluator/src/ssa.rs b/crates/noirc_evaluator/src/ssa.rs index 470fead8a46..591e5c4239c 100644 --- a/crates/noirc_evaluator/src/ssa.rs +++ b/crates/noirc_evaluator/src/ssa.rs @@ -43,10 +43,6 @@ pub(crate) fn optimize_into_acir( .print(print_ssa_passes, "Initial SSA:") .defunctionalize() .print(print_ssa_passes, "After Defunctionalization:"); - // .mem2reg() - // .print(print_ssa_passes, "After Mem2Reg:") - // .fold_constants() - // .print(print_ssa_passes, "After Constant Folding:"); let brillig = ssa.to_brillig(print_brillig_trace); if let RuntimeType::Acir = ssa.main().runtime() { diff --git a/crates/noirc_evaluator/src/ssa/acir_gen/mod.rs b/crates/noirc_evaluator/src/ssa/acir_gen/mod.rs index 729d642b228..6991769dde2 100644 --- a/crates/noirc_evaluator/src/ssa/acir_gen/mod.rs +++ b/crates/noirc_evaluator/src/ssa/acir_gen/mod.rs @@ -473,11 +473,8 @@ impl Context { last_array_uses: &HashMap, ) -> Result<(), RuntimeError> { let index_const = dfg.get_numeric_constant(index); - let value_id = dfg.resolve(array); - let value = &dfg[value_id]; - dbg!(value); - let acir_value = self.convert_value(array, dfg); - match acir_value { + + match self.convert_value(array, dfg) { AcirValue::Var(acir_var, _) => { return Err(RuntimeError::InternalError(InternalError::UnExpected { expected: "an array value".to_string(), @@ -604,7 +601,6 @@ impl Context { // Use the SSA ID to get or create its block ID let block_id = self.block_id(&array); - dbg!(dfg.type_of_value(array)); // Every array has a length in its type, so we fetch that from // the SSA IR. let len = match dfg.type_of_value(array) { @@ -957,7 +953,7 @@ impl Context { } Value::Param { .. } => { // Binary operations on params may have been entirely simplified if the operation - // give back the identity of the param + // results in the identity of the parameter } _ => unreachable!("ICE: Truncates are only ever applied to the result of a binary op or a param"), }; @@ -993,7 +989,7 @@ impl Context { let limb_size = self.convert_value(arguments[2], dfg).into_var()?; let result_type = Self::array_element_type(dfg, result_ids[1]); - dbg!(result_type.clone()); + self.acir_context.radix_decompose(endian, field, radix, limb_size, result_type) } Intrinsic::ToBits(endian) => { @@ -1048,7 +1044,6 @@ impl Context { /// Given an array value, return the numerical type of its element. /// Panics if the given value is not an array or has a non-numeric element type. fn array_element_type(dfg: &DataFlowGraph, value: ValueId) -> AcirType { - dbg!(dfg.type_of_value(value)); match dfg.type_of_value(value) { Type::Array(elements, _) => { assert_eq!(elements.len(), 1); diff --git a/crates/noirc_evaluator/src/ssa/ir/instruction/call.rs b/crates/noirc_evaluator/src/ssa/ir/instruction/call.rs index 1127e791a73..8372ee1b629 100644 --- a/crates/noirc_evaluator/src/ssa/ir/instruction/call.rs +++ b/crates/noirc_evaluator/src/ssa/ir/instruction/call.rs @@ -72,18 +72,9 @@ pub(super) fn simplify_call( } } Intrinsic::ArrayLen => { - dbg!("ARRAYLEN"); - // let slice = dfg.get_array_constant(arguments[0]); - // if let Some((slice, typ)) = slice { - // let length = FieldElement::from((slice.len() / typ.element_size()) as u128); - // SimplifyResult::SimplifiedTo(dfg.make_constant(length, Type::field())) - // } - dbg!(arguments.len()); - // Probably need to check this - // arguments.len() > 1 && - let simplify_res = if let Some(length) = dfg.try_get_array_length(arguments[0]) { + if let Some(length) = dfg.try_get_array_length(arguments[0]) { let length = FieldElement::from(length as u128); - // let length = length / FieldElement::from(typ.element_size() as u128); + let length = length / FieldElement::from(typ.element_size() as u128); SimplifyResult::SimplifiedTo(dfg.make_constant(length, Type::field())) } else if let Some(length) = dfg.get_numeric_constant(arguments[0]) { SimplifyResult::SimplifiedTo(dfg.make_constant(length, Type::field())) @@ -91,41 +82,14 @@ pub(super) fn simplify_call( SimplifyResult::SimplifiedTo(arguments[0]) } else { SimplifyResult::None - }; - dbg!(simplify_res.clone()); - simplify_res - - // if let Some(length) = dfg.get_numeric_constant(arguments[0]) { - // dbg!(length); - // match dfg.type_of_value(arguments[1]) { - // Type::Slice(_) => (), - // _ => unreachable!("ICE: a numeric constant must be followed by a slice for inputs to Intrinsic::ArrayLen"), - // }; - // SimplifyResult::SimplifiedTo(dfg.make_constant(length, Type::field())) - // let length = FieldElement::from((slice.len() / typ.element_size()) as u128); - // if let Some(length) = length { - // // let length = length / FieldElement::from(typ.element_size() as u128); - // SimplifyResult::SimplifiedTo(dfg.make_constant(length, Type::field())) - // } else { - // // TODO: divide by the typ.element_size() - // SimplifyResult::SimplifiedTo(arguments[0]) - // // SimplifyResult::None - // } - // } else if let Some(length) = dfg.try_get_array_length(arguments[0]) { - // let length = FieldElement::from(length as u128); - // SimplifyResult::SimplifiedTo(dfg.make_constant(length, Type::field())) - // } else { - // SimplifyResult::None - // } + } } Intrinsic::SlicePushBack => { let slice = dfg.get_array_constant(arguments[1]); - // dbg!(slice.clone()); if let Some((mut slice, element_type)) = slice { for elem in &arguments[2..] { slice.push_back(*elem); } - // dbg!(slice.clone()); // We must codegen the slice length here in-case it has been generated by // a merger of slices based upon witness values let one = dfg.make_constant(FieldElement::one(), Type::field()); diff --git a/crates/noirc_evaluator/src/ssa/opt/flatten_cfg.rs b/crates/noirc_evaluator/src/ssa/opt/flatten_cfg.rs index ba5230391a3..9ce4c9a4bb4 100644 --- a/crates/noirc_evaluator/src/ssa/opt/flatten_cfg.rs +++ b/crates/noirc_evaluator/src/ssa/opt/flatten_cfg.rs @@ -396,10 +396,8 @@ impl<'f> Context<'f> { typ @ Type::Array(_, _) => { self.merge_array_values(typ, then_condition, else_condition, then_value, else_value) } - // TODO(#1889) typ @ Type::Slice(_) => { self.merge_slice_values(typ, then_condition, else_condition, then_value, else_value) - // panic!("Cannot return slices from an if expression") } Type::Reference => panic!("Cannot return references from an if expression"), Type::Function => panic!("Cannot return functions from an if expression"), @@ -421,12 +419,8 @@ impl<'f> Context<'f> { _ => panic!("Expected slice type"), }; - // TODO: Handle when these are instructions for SlicePushBack rather than - // already converted to slices let then_value = self.inserter.function.dfg[then_value_id].clone(); let else_value = self.inserter.function.dfg[else_value_id].clone(); - // dbg!(then_value.clone()); - // dbg!(else_value.clone()); let len = match then_value { Value::Array { array, .. } => array.len(), @@ -447,8 +441,9 @@ impl<'f> Context<'f> { let typevars = Some(vec![element_type.clone()]); - // Works but leads to slices with more values at the end let mut get_element = |array, typevars, len| { + // The smaller slice is filled with placeholder information. Codegen for slice accesses must + // include checks against the dynamic slice length if (len - 1) < index_value.to_u128() as usize { self.inserter .function diff --git a/crates/noirc_evaluator/src/ssa/ssa_gen/context.rs b/crates/noirc_evaluator/src/ssa/ssa_gen/context.rs index ad24a025631..7ea9cc0a5b2 100644 --- a/crates/noirc_evaluator/src/ssa/ssa_gen/context.rs +++ b/crates/noirc_evaluator/src/ssa/ssa_gen/context.rs @@ -595,11 +595,10 @@ impl<'a> FunctionContext<'a> { array: &ast::LValue, index: &ast::Expression, ) -> (ValueId, ValueId, LValue, Option) { - // dbg!(array.clone()); let (old_array, array_lvalue) = self.extract_current_value_recursive(array); - // dbg!(array_lvalue.clone()); let index = self.codegen_non_tuple_expression(index); let array_lvalue = Box::new(array_lvalue); + // A slice is represented as a tuple (length, slice contents) // We need to fetch the second if old_array.count_leaves() > 1 { diff --git a/crates/noirc_evaluator/src/ssa/ssa_gen/mod.rs b/crates/noirc_evaluator/src/ssa/ssa_gen/mod.rs index d86cf7c15a7..485d86cd6b6 100644 --- a/crates/noirc_evaluator/src/ssa/ssa_gen/mod.rs +++ b/crates/noirc_evaluator/src/ssa/ssa_gen/mod.rs @@ -122,23 +122,17 @@ impl<'a> FunctionContext<'a> { ast::Literal::Array(array) => { let elements = vecmap(&array.contents, |element| self.codegen_expression(element)); let typ = Self::convert_non_tuple_type(&array.typ); - // dbg!(typ.clone()); let new_convert_type = Self::convert_type(&array.typ); - // dbg!(new_convert_type.clone()); + if new_convert_type.count_leaves() > 1 { - dbg!("got here"); let slice_length = ast::Literal::Integer( (array.contents.len() as u128).into(), ast::Type::Field, ); let slice_length = self.codegen_literal(&slice_length); - // let elements = - // vecmap(&array.contents, |element| self.codegen_expression(element)); let slice_contents = self.codegen_array(elements, typ); Tree::Branch(vec![slice_length, slice_contents]) } else { - // let elements = - // vecmap(&array.contents, |element| self.codegen_expression(element)); self.codegen_array(elements, typ) } } @@ -293,12 +287,6 @@ impl<'a> FunctionContext<'a> { location: Location, max_length: Option, ) -> Values { - // let array_value = &self.builder.current_function.dfg[array]; - - // dbg!(array_value.clone()); - // let len = &self.builder.current_function.dfg.try_get_array_length(array); - // dbg!(len); - // base_index = index * type_size let type_size = Self::convert_type(element_type).size_of_type(); let type_size = self.builder.field_constant(type_size as u128); @@ -330,11 +318,7 @@ impl<'a> FunctionContext<'a> { } _ => unreachable!("ICE: array index must be a numeric type"), }; - // let array_len_int = self.builder.insert_cast( - // array_len, - // Type::Numeric(NumericType::Unsigned { bit_size: 64 }), - // ); - + let is_offset_out_of_bounds = self.builder.insert_binary(index, BinaryOp::Lt, array_len); self.builder.insert_constrain(is_offset_out_of_bounds); diff --git a/crates/noirc_frontend/src/monomorphization/mod.rs b/crates/noirc_frontend/src/monomorphization/mod.rs index cfc3ec5c8a3..998f3093d49 100644 --- a/crates/noirc_frontend/src/monomorphization/mod.rs +++ b/crates/noirc_frontend/src/monomorphization/mod.rs @@ -430,7 +430,6 @@ impl<'interner> Monomorphizer<'interner> { constructor: HirConstructorExpression, id: node_interner::ExprId, ) -> ast::Expression { - dbg!(constructor.r#type.borrow().name.clone()); let typ = self.interner.id_type(id); let field_types = unwrap_struct_type(&typ); @@ -448,7 +447,7 @@ impl<'interner> Monomorphizer<'interner> { field_vars.insert(field_name.0.contents.clone(), (new_id, typ)); let expression = Box::new(self.expr(expr_id)); - dbg!(new_id); + new_exprs.push(ast::Expression::Let(ast::Let { id: new_id, mutable: false, From c89bb7682324e276f3c216448194b65bb664ee89 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Wed, 16 Aug 2023 15:41:13 +0000 Subject: [PATCH 40/62] fixes to simplify_call --- .../noirc_evaluator/src/ssa/ir/instruction/call.rs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/crates/noirc_evaluator/src/ssa/ir/instruction/call.rs b/crates/noirc_evaluator/src/ssa/ir/instruction/call.rs index 8667a944391..0bc1fa0c68d 100644 --- a/crates/noirc_evaluator/src/ssa/ir/instruction/call.rs +++ b/crates/noirc_evaluator/src/ssa/ir/instruction/call.rs @@ -74,7 +74,6 @@ pub(super) fn simplify_call( Intrinsic::ArrayLen => { if let Some(length) = dfg.try_get_array_length(arguments[0]) { let length = FieldElement::from(length as u128); - let length = length / FieldElement::from(typ.element_size() as u128); SimplifyResult::SimplifiedTo(dfg.make_constant(length, Type::field())) } else if let Some(length) = dfg.get_numeric_constant(arguments[0]) { SimplifyResult::SimplifiedTo(dfg.make_constant(length, Type::field())) @@ -103,7 +102,7 @@ pub(super) fn simplify_call( }), block, None, - None, + dfg.get_value_call_stack(arguments[0]), ) .first(); @@ -134,7 +133,7 @@ pub(super) fn simplify_call( }), block, None, - None, + dfg.get_value_call_stack(arguments[0]), ) .first(); @@ -174,7 +173,7 @@ pub(super) fn simplify_call( }), block, None, - None, + dfg.get_value_call_stack(arguments[0]), ) .first(); @@ -207,7 +206,7 @@ pub(super) fn simplify_call( }), block, None, - None, + dfg.get_value_call_stack(arguments[0]), ) .first(); results.push(new_slice_length); @@ -246,7 +245,7 @@ pub(super) fn simplify_call( }), block, None, - None, + dfg.get_value_call_stack(arguments[0]), ) .first(); @@ -284,7 +283,7 @@ pub(super) fn simplify_call( }), block, None, - None, + dfg.get_value_call_stack(arguments[0]), ) .first(); results.insert(0, new_slice_length); From 48c8514afe4e1463c81f46c9c78c5d9f0cd06b72 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Wed, 16 Aug 2023 16:20:35 +0000 Subject: [PATCH 41/62] cleanup updating slice length in brillig block --- crates/nargo/src/ops/foreign_calls.rs | 18 ++- .../brillig_slices/src/main.nr | 3 - .../execution_success/slices/src/main.nr | 4 +- .../src/brillig/brillig_gen/brillig_block.rs | 139 ++++++------------ .../brillig/brillig_gen/brillig_slice_ops.rs | 2 +- .../noirc_evaluator/src/brillig/brillig_ir.rs | 1 + .../src/ssa/acir_gen/acir_ir/acir_variable.rs | 1 - .../noirc_evaluator/src/ssa/acir_gen/mod.rs | 10 +- crates/noirc_evaluator/src/ssa/ir/dom.rs | 2 +- .../noirc_evaluator/src/ssa/ir/instruction.rs | 2 +- .../src/ssa/ssa_builder/mod.rs | 7 +- .../src/ssa/ssa_gen/context.rs | 5 +- crates/noirc_evaluator/src/ssa/ssa_gen/mod.rs | 28 ++-- 13 files changed, 97 insertions(+), 125 deletions(-) diff --git a/crates/nargo/src/ops/foreign_calls.rs b/crates/nargo/src/ops/foreign_calls.rs index aa5121d9329..e1768d71c44 100644 --- a/crates/nargo/src/ops/foreign_calls.rs +++ b/crates/nargo/src/ops/foreign_calls.rs @@ -1,5 +1,5 @@ use acvm::{ - acir::brillig::{ForeignCallResult, ForeignCallOutput, Value}, + acir::brillig::{ForeignCallOutput, ForeignCallResult, Value}, pwg::ForeignCallWaitInfo, }; use iter_extended::vecmap; @@ -55,14 +55,24 @@ impl ForeignCall { Some(ForeignCall::Sequence) => { let sequence_length: u128 = foreign_call.inputs[0][0].to_field().to_u128(); let sequence = vecmap(0..sequence_length, Value::from); - - Ok(ForeignCallResult { values: vec![ForeignCallOutput::Single(sequence_length.into()), ForeignCallOutput::Array(sequence) ] }) + + Ok(ForeignCallResult { + values: vec![ + ForeignCallOutput::Single(sequence_length.into()), + ForeignCallOutput::Array(sequence), + ], + }) } Some(ForeignCall::ReverseSequence) => { let sequence_length: u128 = foreign_call.inputs[0][0].to_field().to_u128(); let sequence = vecmap((0..sequence_length).rev(), Value::from); - Ok(ForeignCallResult { values: vec![ForeignCallOutput::Single(sequence_length.into()), ForeignCallOutput::Array(sequence) ] }) + Ok(ForeignCallResult { + values: vec![ + ForeignCallOutput::Single(sequence_length.into()), + ForeignCallOutput::Array(sequence), + ], + }) } None => panic!("unexpected foreign call {:?}", foreign_call_name), } diff --git a/crates/nargo_cli/tests/execution_success/brillig_slices/src/main.nr b/crates/nargo_cli/tests/execution_success/brillig_slices/src/main.nr index 1f09435c8be..2d871bc4b2f 100644 --- a/crates/nargo_cli/tests/execution_success/brillig_slices/src/main.nr +++ b/crates/nargo_cli/tests/execution_success/brillig_slices/src/main.nr @@ -3,11 +3,9 @@ use dep::std; unconstrained fn main(x: Field, y: Field) { let mut slice: [Field] = [y, x]; - std::println(slice.len()); assert(slice.len() == 2); slice = slice.push_back(7); - std::println(slice.len()); assert(slice.len() == 3); assert(slice[0] == y); assert(slice[1] == x); @@ -32,7 +30,6 @@ unconstrained fn main(x: Field, y: Field) { let (item, popped_front_slice) = slice.pop_front(); slice = popped_front_slice; assert(item == 2); - std::println(popped_front_slice.len()); assert(slice.len() == 3); assert(slice[0] == x); diff --git a/crates/nargo_cli/tests/execution_success/slices/src/main.nr b/crates/nargo_cli/tests/execution_success/slices/src/main.nr index 9f817be3b74..90cd67f65e6 100644 --- a/crates/nargo_cli/tests/execution_success/slices/src/main.nr +++ b/crates/nargo_cli/tests/execution_success/slices/src/main.nr @@ -1,8 +1,6 @@ use dep::std::slice; use dep::std; fn main(x : Field, y : pub Field) { - regression_merge_slices(x, y); - let mut slice = [0; 2]; assert(slice[0] == 0); assert(slice[0] != 1); @@ -48,6 +46,8 @@ fn main(x : Field, y : pub Field) { assert(append[4] == 5); regression_2083(); + // The parameters to this function must come from witness values (inputs to main) + regression_merge_slices(x, y); } // Ensure that slices of struct/tuple values work. diff --git a/crates/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs b/crates/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs index 8eb24cdfad2..41c973d05b8 100644 --- a/crates/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs +++ b/crates/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs @@ -202,8 +202,7 @@ impl<'block> BrilligBlock<'block> { /// Converts an SSA instruction into a sequence of Brillig opcodes. fn convert_ssa_instruction(&mut self, instruction_id: InstructionId, dfg: &DataFlowGraph) { let instruction = &dfg[instruction_id]; - // dbg!(instruction.clone()); - // dbg!(dfg.instruction_results(instruction_id)); + match instruction { Instruction::Binary(binary) => { let result_register = self.function_context.create_register_variable( @@ -275,11 +274,18 @@ impl<'block> BrilligBlock<'block> { // Update the stack pointer so that we do not overwrite // dynamic memory returned from other external calls self.brillig_context.update_stack_pointer(*size); - - if let RegisterOrMemory::RegisterIndex(len_index) = output_registers[i - 1] { + + // Update the dynamic slice length maintained in SSA + if let RegisterOrMemory::RegisterIndex(len_index) = + output_registers[i - 1] + { let element_size = dfg[result_ids[i]].get_type().element_size(); self.brillig_context.mov_instruction(len_index, *size); - self.brillig_context.usize_op_in_place(len_index, BinaryIntOp::UnsignedDiv, element_size); + self.brillig_context.usize_op_in_place( + len_index, + BinaryIntOp::UnsignedDiv, + element_size, + ); } else { unreachable!("ICE: a vector must be preceded by a register containing its length"); } @@ -339,6 +345,7 @@ impl<'block> BrilligBlock<'block> { let limb_count = self.convert_ssa_register_value(arguments[2], dfg); let results = dfg.instruction_results(instruction_id); + let target_len = match self.function_context.get_or_create_variable( self.brillig_context, results[0], @@ -369,7 +376,7 @@ impl<'block> BrilligBlock<'block> { let limb_count = self.convert_ssa_register_value(arguments[1], dfg); let results = dfg.instruction_results(instruction_id); - + let target_len = match self.function_context.get_or_create_variable( self.brillig_context, results[0], @@ -630,11 +637,9 @@ impl<'block> BrilligBlock<'block> { let source_variable = self.convert_ssa_value(slice_id, dfg); let source_vector = self.convert_array_or_vector_to_vector(source_variable); + let results = dfg.instruction_results(instruction_id); match intrinsic { Value::Intrinsic(Intrinsic::SlicePushBack) => { - let results = dfg.instruction_results(instruction_id); - - // TODO: cleanup the source and target len let target_len = match self.function_context.get_or_create_variable( self.brillig_context, results[0], @@ -643,34 +648,20 @@ impl<'block> BrilligBlock<'block> { RegisterOrMemory::RegisterIndex(register_index) => register_index, _ => unreachable!("ICE: first value of a slice must be a register index"), }; - let source_len = match self.function_context.get_or_create_variable(self.brillig_context, arguments[0], dfg) { - RegisterOrMemory::RegisterIndex(register_index) => register_index, - _ => unreachable!("ICE: first value of a slice must be a register index"), - }; - let target_variable = self.function_context.create_variable( - self.brillig_context, - results[1], - dfg, - ); + let target_variable = + self.function_context.create_variable(self.brillig_context, results[1], dfg); let target_vector = self.brillig_context.extract_heap_vector(target_variable); let item_values = vecmap(&arguments[2..element_size + 2], |arg| { self.convert_ssa_value(*arg, dfg) }); - self.brillig_context.usize_op( - source_len, - target_len, - BinaryIntOp::Add, - item_values.len() / element_size, - ); + self.update_slice_length(target_len, arguments[0], dfg, BinaryIntOp::Add); self.slice_push_back_operation(target_vector, source_vector, &item_values); } Value::Intrinsic(Intrinsic::SlicePushFront) => { - let results = dfg.instruction_results(instruction_id); - let target_len = match self.function_context.get_or_create_variable( self.brillig_context, results[0], @@ -679,32 +670,19 @@ impl<'block> BrilligBlock<'block> { RegisterOrMemory::RegisterIndex(register_index) => register_index, _ => unreachable!("ICE: first value of a slice must be a register index"), }; - let source_len = match self.function_context.get_or_create_variable(self.brillig_context, arguments[0], dfg) { - RegisterOrMemory::RegisterIndex(register_index) => register_index, - _ => unreachable!("ICE: first value of a slice must be a register index"), - }; - let target_variable = self.function_context.create_variable( - self.brillig_context, - results[1], - dfg, - ); + let target_variable = + self.function_context.create_variable(self.brillig_context, results[1], dfg); let target_vector = self.brillig_context.extract_heap_vector(target_variable); let item_values = vecmap(&arguments[2..element_size + 2], |arg| { self.convert_ssa_value(*arg, dfg) - }); - self.brillig_context.usize_op( - source_len, - target_len, - BinaryIntOp::Add, - item_values.len() / element_size, - ); + }); + + self.update_slice_length(target_len, arguments[0], dfg, BinaryIntOp::Add); self.slice_push_front_operation(target_vector, source_vector, &item_values); } Value::Intrinsic(Intrinsic::SlicePopBack) => { - let results = dfg.instruction_results(instruction_id); - let target_len = match self.function_context.get_or_create_variable( self.brillig_context, results[0], @@ -713,10 +691,6 @@ impl<'block> BrilligBlock<'block> { RegisterOrMemory::RegisterIndex(register_index) => register_index, _ => unreachable!("ICE: first value of a slice must be a register index"), }; - let source_len = match self.function_context.get_or_create_variable(self.brillig_context, arguments[0], dfg) { - RegisterOrMemory::RegisterIndex(register_index) => register_index, - _ => unreachable!("ICE: first value of a slice must be a register index"), - }; let target_variable = self.function_context.create_variable(self.brillig_context, results[1], dfg); @@ -726,18 +700,12 @@ impl<'block> BrilligBlock<'block> { let pop_variables = vecmap(&results[2..element_size + 2], |result| { self.function_context.create_variable(self.brillig_context, *result, dfg) }); - self.brillig_context.usize_op( - source_len, - target_len, - BinaryIntOp::Sub, - pop_variables.len() / element_size, - ); + + self.update_slice_length(target_len, arguments[0], dfg, BinaryIntOp::Sub); self.slice_pop_back_operation(target_vector, source_vector, &pop_variables); } Value::Intrinsic(Intrinsic::SlicePopFront) => { - let results = dfg.instruction_results(instruction_id); - let target_len = match self.function_context.get_or_create_variable( self.brillig_context, results[element_size], @@ -746,20 +714,10 @@ impl<'block> BrilligBlock<'block> { RegisterOrMemory::RegisterIndex(register_index) => register_index, _ => unreachable!("ICE: first value of a slice must be a register index"), }; - let source_len = match self.function_context.get_or_create_variable(self.brillig_context, arguments[0], dfg) { - RegisterOrMemory::RegisterIndex(register_index) => register_index, - _ => unreachable!("ICE: first value of a slice must be a register index"), - }; let pop_variables = vecmap(&results[0..element_size], |result| { self.function_context.create_variable(self.brillig_context, *result, dfg) }); - self.brillig_context.usize_op( - source_len, - target_len, - BinaryIntOp::Sub, - pop_variables.len() / element_size, - ); let target_variable = self.function_context.create_variable( self.brillig_context, @@ -768,11 +726,11 @@ impl<'block> BrilligBlock<'block> { ); let target_vector = self.brillig_context.extract_heap_vector(target_variable); + self.update_slice_length(target_len, arguments[0], dfg, BinaryIntOp::Sub); + self.slice_pop_front_operation(target_vector, source_vector, &pop_variables); } Value::Intrinsic(Intrinsic::SliceInsert) => { - let results = dfg.instruction_results(instruction_id); - let target_len = match self.function_context.get_or_create_variable( self.brillig_context, results[0], @@ -781,10 +739,6 @@ impl<'block> BrilligBlock<'block> { RegisterOrMemory::RegisterIndex(register_index) => register_index, _ => unreachable!("ICE: first value of a slice must be a register index"), }; - let source_len = match self.function_context.get_or_create_variable(self.brillig_context, arguments[0], dfg) { - RegisterOrMemory::RegisterIndex(register_index) => register_index, - _ => unreachable!("ICE: first value of a slice must be a register index"), - }; let target_id = results[1]; let target_variable = @@ -808,19 +762,13 @@ impl<'block> BrilligBlock<'block> { let items = vecmap(&arguments[3..element_size + 3], |arg| { self.convert_ssa_value(*arg, dfg) }); - self.brillig_context.usize_op( - source_len, - target_len, - BinaryIntOp::Add, - items.len() / element_size, - ); + + self.update_slice_length(target_len, arguments[0], dfg, BinaryIntOp::Add); self.slice_insert_operation(target_vector, source_vector, converted_index, &items); self.brillig_context.deallocate_register(converted_index); } Value::Intrinsic(Intrinsic::SliceRemove) => { - let results = dfg.instruction_results(instruction_id); - let target_len = match self.function_context.get_or_create_variable( self.brillig_context, results[0], @@ -829,10 +777,6 @@ impl<'block> BrilligBlock<'block> { RegisterOrMemory::RegisterIndex(register_index) => register_index, _ => unreachable!("ICE: first value of a slice must be a register index"), }; - let source_len = match self.function_context.get_or_create_variable(self.brillig_context, arguments[0], dfg) { - RegisterOrMemory::RegisterIndex(register_index) => register_index, - _ => unreachable!("ICE: first value of a slice must be a register index"), - }; let target_id = results[1]; @@ -855,12 +799,8 @@ impl<'block> BrilligBlock<'block> { let removed_items = vecmap(&results[2..element_size + 2], |result| { self.function_context.create_variable(self.brillig_context, *result, dfg) }); - self.brillig_context.usize_op( - source_len, - target_len, - BinaryIntOp::Sub, - removed_items.len() / element_size, - ); + + self.update_slice_length(target_len, arguments[0], dfg, BinaryIntOp::Sub); self.slice_remove_operation( target_vector, @@ -875,6 +815,25 @@ impl<'block> BrilligBlock<'block> { } } + fn update_slice_length( + &mut self, + target_len: RegisterIndex, + source_value: ValueId, + dfg: &DataFlowGraph, + binary_op: BinaryIntOp, + ) { + let source_len = match self.function_context.get_or_create_variable( + self.brillig_context, + source_value, + dfg, + ) { + RegisterOrMemory::RegisterIndex(register_index) => register_index, + _ => unreachable!("ICE: first value of a slice must be a register index"), + }; + + self.brillig_context.usize_op(source_len, target_len, binary_op, 1); + } + /// Converts an SSA cast to a sequence of Brillig opcodes. /// Casting is only necessary when shrinking the bit size of a numeric value. fn convert_cast( diff --git a/crates/noirc_evaluator/src/brillig/brillig_gen/brillig_slice_ops.rs b/crates/noirc_evaluator/src/brillig/brillig_gen/brillig_slice_ops.rs index c6f2a499bd7..facc4766722 100644 --- a/crates/noirc_evaluator/src/brillig/brillig_gen/brillig_slice_ops.rs +++ b/crates/noirc_evaluator/src/brillig/brillig_gen/brillig_slice_ops.rs @@ -561,7 +561,7 @@ mod tests { let copied_array_pointer = context.allocate_register(); let copied_array_size = context.allocate_register(); - + let mut block = create_brillig_block(&mut function_context, &mut context); block.slice_insert_operation( diff --git a/crates/noirc_evaluator/src/brillig/brillig_ir.rs b/crates/noirc_evaluator/src/brillig/brillig_ir.rs index 76d35d1da7a..f9f93db36b0 100644 --- a/crates/noirc_evaluator/src/brillig/brillig_ir.rs +++ b/crates/noirc_evaluator/src/brillig/brillig_ir.rs @@ -860,6 +860,7 @@ impl BrilligContext { big_endian: bool, ) { self.mov_instruction(target_vector_len, limb_count); + // The full array that is returned is going to be the limb count + 1 self.usize_op_in_place(target_vector_len, BinaryIntOp::Add, 1usize); self.mov_instruction(target_vector.size, limb_count); self.allocate_array_instruction(target_vector.pointer, target_vector.size); diff --git a/crates/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs b/crates/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs index 5006f5a75d6..0196635bfb7 100644 --- a/crates/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs +++ b/crates/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs @@ -792,7 +792,6 @@ impl AcirContext { // `Intrinsic::ToRadix` returns slices which are represented // by tuples with the structure (length, slice contents) - dbg!(limb_vars.len()); Ok(vec![ AcirValue::Var( self.add_constant(FieldElement::from(limb_vars.len() as u128)), diff --git a/crates/noirc_evaluator/src/ssa/acir_gen/mod.rs b/crates/noirc_evaluator/src/ssa/acir_gen/mod.rs index 45770e1ded4..3124cfc885e 100644 --- a/crates/noirc_evaluator/src/ssa/acir_gen/mod.rs +++ b/crates/noirc_evaluator/src/ssa/acir_gen/mod.rs @@ -942,8 +942,10 @@ impl Context { let mut var = self.convert_numeric_value(value_id, dfg)?; match &dfg[value_id] { Value::Instruction { instruction, .. } => { - if matches!(&dfg[*instruction], Instruction::Binary(Binary { operator: BinaryOp::Sub, .. })) - { + if matches!( + &dfg[*instruction], + Instruction::Binary(Binary { operator: BinaryOp::Sub, .. }) + ) { // Subtractions must first have the integer modulus added before truncation can be // applied. This is done in order to prevent underflow. let integer_modulus = @@ -955,7 +957,9 @@ impl Context { // Binary operations on params may have been entirely simplified if the operation // results in the identity of the parameter } - _ => unreachable!("ICE: Truncates are only ever applied to the result of a binary op or a param"), + _ => unreachable!( + "ICE: Truncates are only ever applied to the result of a binary op or a param" + ), }; self.acir_context.truncate_var(var, bit_size, max_bit_size) diff --git a/crates/noirc_evaluator/src/ssa/ir/dom.rs b/crates/noirc_evaluator/src/ssa/ir/dom.rs index 4c9bbb74a30..b7b1728d035 100644 --- a/crates/noirc_evaluator/src/ssa/ir/dom.rs +++ b/crates/noirc_evaluator/src/ssa/ir/dom.rs @@ -11,7 +11,7 @@ use super::{ }; /// Dominator tree node. We keep one of these per reachable block. -#[derive(Clone, Default, Debug)] +#[derive(Clone, Default)] struct DominatorTreeNode { /// The block's idx in the control flow graph's reverse post-order reverse_post_order_idx: u32, diff --git a/crates/noirc_evaluator/src/ssa/ir/instruction.rs b/crates/noirc_evaluator/src/ssa/ir/instruction.rs index 852453b692c..09d75d3d34b 100644 --- a/crates/noirc_evaluator/src/ssa/ir/instruction.rs +++ b/crates/noirc_evaluator/src/ssa/ir/instruction.rs @@ -366,7 +366,7 @@ impl Instruction { // dbg!(array[index]); return SimplifiedTo(array[index]); } - } + } None } Instruction::ArraySet { array, index, value } => { diff --git a/crates/noirc_evaluator/src/ssa/ssa_builder/mod.rs b/crates/noirc_evaluator/src/ssa/ssa_builder/mod.rs index 778d995a0f4..6a5b24e3e9f 100644 --- a/crates/noirc_evaluator/src/ssa/ssa_builder/mod.rs +++ b/crates/noirc_evaluator/src/ssa/ssa_builder/mod.rs @@ -397,12 +397,11 @@ mod tests { let input = builder.numeric_constant(FieldElement::from(7_u128), Type::field()); let length = builder.numeric_constant(FieldElement::from(8_u128), Type::field()); let result_types = vec![Type::Array(Rc::new(vec![Type::bool()]), 8)]; - let call_results = builder.insert_call(to_bits_id, vec![input, length], result_types).into_owned(); + let call_results = + builder.insert_call(to_bits_id, vec![input, length], result_types).into_owned(); let slice_len = match &builder.current_function.dfg[call_results[0]] { - Value::NumericConstant { constant, .. } => { - *constant - } + Value::NumericConstant { constant, .. } => *constant, _ => panic!(), }; assert_eq!(slice_len, FieldElement::from(256u128)); diff --git a/crates/noirc_evaluator/src/ssa/ssa_gen/context.rs b/crates/noirc_evaluator/src/ssa/ssa_gen/context.rs index 7ea9cc0a5b2..b614d9c3b2b 100644 --- a/crates/noirc_evaluator/src/ssa/ssa_gen/context.rs +++ b/crates/noirc_evaluator/src/ssa/ssa_gen/context.rs @@ -263,7 +263,8 @@ impl<'a> FunctionContext<'a> { if let Type::Numeric(NumericType::Unsigned { bit_size }) = typ { let to_bits = self.builder.import_intrinsic_id(Intrinsic::ToBits(Endian::Little)); let length = self.builder.field_constant(FieldElement::from(bit_size as i128)); - let result_types = vec![Type::field(), Type::Array(Rc::new(vec![Type::bool()]), bit_size as usize)]; + let result_types = + vec![Type::field(), Type::Array(Rc::new(vec![Type::bool()]), bit_size as usize)]; let rhs_bits = self.builder.insert_call(to_bits, vec![rhs, length], result_types); dbg!(rhs_bits.len()); let rhs_bits = rhs_bits[1]; @@ -598,7 +599,7 @@ impl<'a> FunctionContext<'a> { let (old_array, array_lvalue) = self.extract_current_value_recursive(array); let index = self.codegen_non_tuple_expression(index); let array_lvalue = Box::new(array_lvalue); - + // A slice is represented as a tuple (length, slice contents) // We need to fetch the second if old_array.count_leaves() > 1 { diff --git a/crates/noirc_evaluator/src/ssa/ssa_gen/mod.rs b/crates/noirc_evaluator/src/ssa/ssa_gen/mod.rs index bcf6c0d7035..7b6015b338a 100644 --- a/crates/noirc_evaluator/src/ssa/ssa_gen/mod.rs +++ b/crates/noirc_evaluator/src/ssa/ssa_gen/mod.rs @@ -269,7 +269,13 @@ impl<'a> FunctionContext<'a> { Some(slice_length), ) } else { - self.codegen_array_index(array_or_slice[0], index_value, &index.element_type, index.location, None) + self.codegen_array_index( + array_or_slice[0], + index_value, + &index.element_type, + index.location, + None, + ) } } @@ -305,20 +311,16 @@ impl<'a> FunctionContext<'a> { // If the index and the array_len are both Fields we will not be able to perform a less than comparison on them // Thus, we cast the array len to a u64 before performing the less than comparison let array_len = match self.builder.type_of_value(index) { - Type::Numeric(numeric_type) => { - match numeric_type { - NumericType::Unsigned { .. } | NumericType::Signed { .. } => array_len, - NumericType::NativeField => { - self.builder.insert_cast( - array_len, - Type::Numeric(NumericType::Unsigned { bit_size: 64 }), - ) - } - } - } + Type::Numeric(numeric_type) => match numeric_type { + NumericType::Unsigned { .. } | NumericType::Signed { .. } => array_len, + NumericType::NativeField => self.builder.insert_cast( + array_len, + Type::Numeric(NumericType::Unsigned { bit_size: 64 }), + ), + }, _ => unreachable!("ICE: array index must be a numeric type"), }; - + let is_offset_out_of_bounds = self.builder.insert_binary(index, BinaryOp::Lt, array_len); self.builder.insert_constrain(is_offset_out_of_bounds); From ff631e2b9f9391079aba2d63b1433097f04b88d5 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Wed, 16 Aug 2023 16:45:33 +0000 Subject: [PATCH 42/62] cleanup in simplify_call and cleanup --- .../noirc_evaluator/src/ssa/ir/instruction.rs | 2 - .../src/ssa/ir/instruction/call.rs | 122 ++++-------------- .../src/ssa/ssa_gen/context.rs | 4 +- crates/noirc_evaluator/src/ssa/ssa_gen/mod.rs | 13 +- 4 files changed, 33 insertions(+), 108 deletions(-) diff --git a/crates/noirc_evaluator/src/ssa/ir/instruction.rs b/crates/noirc_evaluator/src/ssa/ir/instruction.rs index 09d75d3d34b..8f3cd0cfe3c 100644 --- a/crates/noirc_evaluator/src/ssa/ir/instruction.rs +++ b/crates/noirc_evaluator/src/ssa/ir/instruction.rs @@ -363,7 +363,6 @@ impl Instruction { let index = index.try_to_u64().expect("Expected array index to fit in u64") as usize; if index < array.len() { - // dbg!(array[index]); return SimplifiedTo(array[index]); } } @@ -830,7 +829,6 @@ impl std::fmt::Display for BinaryOp { } } -#[derive(Debug, Clone)] /// Contains the result to Instruction::simplify, specifying how the instruction /// should be simplified. pub(crate) enum SimplifyResult { diff --git a/crates/noirc_evaluator/src/ssa/ir/instruction/call.rs b/crates/noirc_evaluator/src/ssa/ir/instruction/call.rs index 0bc1fa0c68d..28f4619be84 100644 --- a/crates/noirc_evaluator/src/ssa/ir/instruction/call.rs +++ b/crates/noirc_evaluator/src/ssa/ir/instruction/call.rs @@ -89,22 +89,8 @@ pub(super) fn simplify_call( for elem in &arguments[2..] { slice.push_back(*elem); } - // We must codegen the slice length here in-case it has been generated by - // a merger of slices based upon witness values - let one = dfg.make_constant(FieldElement::one(), Type::field()); - let block = dfg.make_block(); - let new_slice_length = dfg - .insert_instruction_and_results( - Instruction::Binary(Binary { - lhs: arguments[0], - operator: BinaryOp::Add, - rhs: one, - }), - block, - None, - dfg.get_value_call_stack(arguments[0]), - ) - .first(); + + let new_slice_length = update_slice_length(arguments[0], dfg, BinaryOp::Add); let new_slice = dfg.make_array(slice, element_type); SimplifyResult::SimplifiedToMultiple(vec![new_slice_length, new_slice]) @@ -120,22 +106,7 @@ pub(super) fn simplify_call( slice.push_front(*elem); } - // We must codegen the slice length here in-case it has been generated by - // a merger of slices based upon witness values - let one = dfg.make_constant(FieldElement::one(), Type::field()); - let block = dfg.make_block(); - let new_slice_length = dfg - .insert_instruction_and_results( - Instruction::Binary(Binary { - lhs: arguments[0], - operator: BinaryOp::Add, - rhs: one, - }), - block, - None, - dfg.get_value_call_stack(arguments[0]), - ) - .first(); + let new_slice_length = update_slice_length(arguments[0], dfg, BinaryOp::Add); let new_slice = dfg.make_array(slice, element_type); SimplifyResult::SimplifiedToMultiple(vec![new_slice_length, new_slice]) @@ -160,22 +131,7 @@ pub(super) fn simplify_call( let new_slice = dfg.make_array(slice, typ); results.push_front(new_slice); - // We must codegen the slice length here in-case it has been generated by - // a merger of slices based upon witness values - let one = dfg.make_constant(FieldElement::one(), Type::field()); - let block = dfg.make_block(); - let new_slice_length = dfg - .insert_instruction_and_results( - Instruction::Binary(Binary { - lhs: arguments[0], - operator: BinaryOp::Sub, - rhs: one, - }), - block, - None, - dfg.get_value_call_stack(arguments[0]), - ) - .first(); + let new_slice_length = update_slice_length(arguments[0], dfg, BinaryOp::Sub); results.push_front(new_slice_length); SimplifyResult::SimplifiedToMultiple(results.into()) @@ -193,22 +149,8 @@ pub(super) fn simplify_call( slice.pop_front().expect("There are no elements in this slice to be removed") }); - // We must codegen the slice length here in-case it has been generated by - // a merger of slices based upon witness values - let one = dfg.make_constant(FieldElement::one(), Type::field()); - let block = dfg.make_block(); - let new_slice_length = dfg - .insert_instruction_and_results( - Instruction::Binary(Binary { - lhs: arguments[0], - operator: BinaryOp::Sub, - rhs: one, - }), - block, - None, - dfg.get_value_call_stack(arguments[0]), - ) - .first(); + let new_slice_length = update_slice_length(arguments[0], dfg, BinaryOp::Sub); + results.push(new_slice_length); let new_slice = dfg.make_array(slice, typ); @@ -232,22 +174,7 @@ pub(super) fn simplify_call( index += 1; } - // We must codegen the slice length here in-case it has been generated by - // a merger of slices based upon witness values - let one = dfg.make_constant(FieldElement::one(), Type::field()); - let block = dfg.make_block(); - let new_slice_length = dfg - .insert_instruction_and_results( - Instruction::Binary(Binary { - lhs: arguments[0], - operator: BinaryOp::Add, - rhs: one, - }), - block, - None, - dfg.get_value_call_stack(arguments[0]), - ) - .first(); + let new_slice_length = update_slice_length(arguments[0], dfg, BinaryOp::Add); let new_slice = dfg.make_array(slice, typ); SimplifyResult::SimplifiedToMultiple(vec![new_slice_length, new_slice]) @@ -270,22 +197,8 @@ pub(super) fn simplify_call( let new_slice = dfg.make_array(slice, typ); results.insert(0, new_slice); - // We must codegen the slice length here in-case it has been generated by - // a merger of slices based upon witness values - let one = dfg.make_constant(FieldElement::one(), Type::field()); - let block = dfg.make_block(); - let new_slice_length = dfg - .insert_instruction_and_results( - Instruction::Binary(Binary { - lhs: arguments[0], - operator: BinaryOp::Sub, - rhs: one, - }), - block, - None, - dfg.get_value_call_stack(arguments[0]), - ) - .first(); + let new_slice_length = update_slice_length(arguments[0], dfg, BinaryOp::Sub); + results.insert(0, new_slice_length); SimplifyResult::SimplifiedToMultiple(results) @@ -310,6 +223,21 @@ pub(super) fn simplify_call( } } +// Slices have a tuple structure (slice length, slice contents) to enable logic +// that uses dynamic slice lengths (such as with merging slices in the flattening pass). +// This method codegens an update to the slice length. +fn update_slice_length(slice_len: ValueId, dfg: &mut DataFlowGraph, operator: BinaryOp) -> ValueId { + let one = dfg.make_constant(FieldElement::one(), Type::field()); + let block = dfg.make_block(); + dfg.insert_instruction_and_results( + Instruction::Binary(Binary { lhs: slice_len, operator, rhs: one }), + block, + None, + dfg.get_value_call_stack(slice_len), + ) + .first() +} + /// Try to simplify this black box call. If the call can be simplified to a known value, /// that value is returned. Otherwise [`SimplifyResult::None`] is returned. fn simplify_black_box_func( @@ -321,10 +249,8 @@ fn simplify_black_box_func( BlackBoxFunc::SHA256 => simplify_hash(dfg, arguments, acvm::blackbox_solver::sha256), BlackBoxFunc::Blake2s => simplify_hash(dfg, arguments, acvm::blackbox_solver::blake2s), BlackBoxFunc::Keccak256 => { - dbg!("got to simplify keccak"); match (dfg.get_array_constant(arguments[0]), dfg.get_numeric_constant(arguments[1])) { (Some((input, _)), Some(num_bytes)) if array_is_constant(dfg, &input) => { - dbg!("simplifying keccak"); let input_bytes: Vec = to_u8_vec(dfg, input); let num_bytes = num_bytes.to_u128() as usize; diff --git a/crates/noirc_evaluator/src/ssa/ssa_gen/context.rs b/crates/noirc_evaluator/src/ssa/ssa_gen/context.rs index b614d9c3b2b..8afcd54c112 100644 --- a/crates/noirc_evaluator/src/ssa/ssa_gen/context.rs +++ b/crates/noirc_evaluator/src/ssa/ssa_gen/context.rs @@ -266,7 +266,6 @@ impl<'a> FunctionContext<'a> { let result_types = vec![Type::field(), Type::Array(Rc::new(vec![Type::bool()]), bit_size as usize)]; let rhs_bits = self.builder.insert_call(to_bits, vec![rhs, length], result_types); - dbg!(rhs_bits.len()); let rhs_bits = rhs_bits[1]; let one = self.builder.field_constant(FieldElement::one()); let mut r = one; @@ -367,9 +366,8 @@ impl<'a> FunctionContext<'a> { location: Location, ) -> Values { let lhs_type = self.builder.type_of_value(lhs); - dbg!(lhs_type.clone()); let rhs_type = self.builder.type_of_value(rhs); - dbg!(rhs_type.clone()); + let (array_length, element_type) = match (lhs_type, rhs_type) { ( Type::Array(lhs_composite_type, lhs_length), diff --git a/crates/noirc_evaluator/src/ssa/ssa_gen/mod.rs b/crates/noirc_evaluator/src/ssa/ssa_gen/mod.rs index 7b6015b338a..37ce534a332 100644 --- a/crates/noirc_evaluator/src/ssa/ssa_gen/mod.rs +++ b/crates/noirc_evaluator/src/ssa/ssa_gen/mod.rs @@ -98,7 +98,6 @@ impl<'a> FunctionContext<'a> { /// to reassign to it. Note that mutable references `let x = &mut ...;` do not require this /// since they are not automatically loaded from and must be explicitly dereferenced. fn codegen_ident_reference(&mut self, ident: &ast::Ident) -> Values { - // dbg!(ident.definition.clone()); match &ident.definition { ast::Definition::Local(id) => self.lookup(*id), ast::Definition::Function(id) => self.get_or_queue_function(*id), @@ -303,16 +302,21 @@ impl<'a> FunctionContext<'a> { Self::map_type(element_type, |typ| { let offset = self.make_offset(base_index, field_index); field_index += 1; + let array_type = &self.builder.type_of_value(array); match array_type { + // Prepare a slice access + // Check that the index being used is less than the dynamic slice length Type::Slice(_) => { let array_len = max_length.expect("ICE: a length must be supplied for indexing slices"); - // If the index and the array_len are both Fields we will not be able to perform a less than comparison on them - // Thus, we cast the array len to a u64 before performing the less than comparison + // Check the type of the index value for valid comparisons let array_len = match self.builder.type_of_value(index) { Type::Numeric(numeric_type) => match numeric_type { + // If the index itself is an integer, keep the array length as a Field NumericType::Unsigned { .. } | NumericType::Signed { .. } => array_len, + // If the index and the array length are both Fields we will not be able to perform a less than comparison on them. + // Thus, we cast the array length to a u64 before performing the less than comparison NumericType::NativeField => self.builder.insert_cast( array_len, Type::Numeric(NumericType::Unsigned { bit_size: 64 }), @@ -326,11 +330,10 @@ impl<'a> FunctionContext<'a> { self.builder.insert_constrain(is_offset_out_of_bounds); } Type::Array(..) => { - // Nothing needs to done to prepare an array get on an array + // Nothing needs to done to prepare an array access on an array } _ => unreachable!("must have array or slice but got {array_type}"), } - // dbg!("about to insert array get"); self.builder.insert_array_get(array, offset, typ).into() }) } From c55befcbbce1ad0ed3fa47f5eb4a74303582ab41 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Wed, 16 Aug 2023 17:03:12 +0000 Subject: [PATCH 43/62] more integration tests for merge_slices --- .../slice_access_failure/Nargo.toml | 7 ++ .../slice_access_failure/Prover.toml | 2 + .../slice_access_failure/src/main.nr | 19 +++++ .../brillig_slices/src/main.nr | 76 +++++++++++++++++++ .../execution_success/slices/src/main.nr | 56 +++++++++++--- .../src/ssa/ir/instruction/call.rs | 1 - 6 files changed, 149 insertions(+), 12 deletions(-) create mode 100644 crates/nargo_cli/tests/compile_failure/slice_access_failure/Nargo.toml create mode 100644 crates/nargo_cli/tests/compile_failure/slice_access_failure/Prover.toml create mode 100644 crates/nargo_cli/tests/compile_failure/slice_access_failure/src/main.nr diff --git a/crates/nargo_cli/tests/compile_failure/slice_access_failure/Nargo.toml b/crates/nargo_cli/tests/compile_failure/slice_access_failure/Nargo.toml new file mode 100644 index 00000000000..fee471dffd7 --- /dev/null +++ b/crates/nargo_cli/tests/compile_failure/slice_access_failure/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "slice_access_failure" +type = "bin" +authors = [""] +compiler_version = "0.10.2" + +[dependencies] \ No newline at end of file diff --git a/crates/nargo_cli/tests/compile_failure/slice_access_failure/Prover.toml b/crates/nargo_cli/tests/compile_failure/slice_access_failure/Prover.toml new file mode 100644 index 00000000000..f28f2f8cc48 --- /dev/null +++ b/crates/nargo_cli/tests/compile_failure/slice_access_failure/Prover.toml @@ -0,0 +1,2 @@ +x = "5" +y = "10" diff --git a/crates/nargo_cli/tests/compile_failure/slice_access_failure/src/main.nr b/crates/nargo_cli/tests/compile_failure/slice_access_failure/src/main.nr new file mode 100644 index 00000000000..fa23d24379f --- /dev/null +++ b/crates/nargo_cli/tests/compile_failure/slice_access_failure/src/main.nr @@ -0,0 +1,19 @@ +fn main(x : Field, y : pub Field) { + let mut slice = [0; 2]; + if x == y { + slice = slice.push_back(y); + slice = slice.push_back(x); + } else { + slice = slice.push_back(x); + } + // This constraint should fail as the slice length is 2 and the index is 2 + assert(slice[2] == 0); +} + +#[test] +fn test_main() { + main(1, 2); + + // Uncomment to make test fail + // main(1, 1); +} diff --git a/crates/nargo_cli/tests/execution_success/brillig_slices/src/main.nr b/crates/nargo_cli/tests/execution_success/brillig_slices/src/main.nr index 2d871bc4b2f..4455acc02f8 100644 --- a/crates/nargo_cli/tests/execution_success/brillig_slices/src/main.nr +++ b/crates/nargo_cli/tests/execution_success/brillig_slices/src/main.nr @@ -68,9 +68,85 @@ unconstrained fn main(x: Field, y: Field) { let (empty_slice, should_be_x) = slice.remove(0); assert(should_be_x == x); assert(empty_slice.len() == 0); + + regression_merge_slices(x, y); } // Tests slice passing to/from functions unconstrained fn push_front_to_slice(slice: [T], item: T) -> [T] { slice.push_front(item) } + +// The parameters to this function must come from witness values (inputs to main) +unconstrained fn regression_merge_slices(x: Field, y: Field) { + merge_slices_if(x, y); + merge_slices_else(x); +} + +unconstrained fn merge_slices_if(x: Field, y: Field) { + let slice = merge_slices_return(x, y); + assert(slice[2] == 10); + assert(slice.len() == 3); + + let slice = merge_slices_mutate(x, y); + assert(slice[3] == 5); + assert(slice.len() == 4); + + let slice = merge_slices_mutate_in_loop(x, y); + assert(slice[6] == 4); + assert(slice.len() == 7); +} + +unconstrained fn merge_slices_else(x: Field) { + let slice = merge_slices_return(x, 5); + assert(slice[0] == 0); + assert(slice[1] == 0); + assert(slice.len() == 2); + + let slice = merge_slices_mutate(x, 5); + assert(slice[2] == 5); + assert(slice.len() == 3); + + let slice = merge_slices_mutate_in_loop(x, 5); + assert(slice[2] == 5); + assert(slice.len() == 3); +} + +// Test returning a merged slice without a mutation +unconstrained fn merge_slices_return(x: Field, y: Field) -> [Field] { + let slice = [0; 2]; + if x != y { + if x != 20 { + slice.push_back(y) + } else { + slice + } + } else { + slice + } +} + +// Test mutating a slice inside of an if statement +unconstrained fn merge_slices_mutate(x: Field, y: Field) -> [Field] { + let mut slice = [0; 2]; + if x != y { + slice = slice.push_back(y); + slice = slice.push_back(x); + } else { + slice = slice.push_back(x); + } + slice +} + +// Test mutating a slice inside of a loop in an if statement +unconstrained fn merge_slices_mutate_in_loop(x: Field, y: Field) -> [Field] { + let mut slice = [0; 2]; + if x != y { + for i in 0..5 { + slice = slice.push_back(i); + } + } else { + slice = slice.push_back(x); + } + slice +} diff --git a/crates/nargo_cli/tests/execution_success/slices/src/main.nr b/crates/nargo_cli/tests/execution_success/slices/src/main.nr index 90cd67f65e6..8fbf0a19fc5 100644 --- a/crates/nargo_cli/tests/execution_success/slices/src/main.nr +++ b/crates/nargo_cli/tests/execution_success/slices/src/main.nr @@ -85,9 +85,43 @@ fn regression_2083() { // The parameters to this function must come from witness values (inputs to main) fn regression_merge_slices(x: Field, y: Field) { - // Test returning a merged slice without a mutation + merge_slices_if(x, y); + merge_slices_else(x); +} + +fn merge_slices_if(x: Field, y: Field) { + let slice = merge_slices_return(x, y); + assert(slice[2] == 10); + assert(slice.len() == 3); + + let slice = merge_slices_mutate(x, y); + assert(slice[3] == 5); + assert(slice.len() == 4); + + let slice = merge_slices_mutate_in_loop(x, y); + assert(slice[6] == 4); + assert(slice.len() == 7); +} + +fn merge_slices_else(x: Field) { + let slice = merge_slices_return(x, 5); + assert(slice[0] == 0); + assert(slice[1] == 0); + assert(slice.len() == 2); + + let slice = merge_slices_mutate(x, 5); + assert(slice[2] == 5); + assert(slice.len() == 3); + + let slice = merge_slices_mutate_in_loop(x, 5); + assert(slice[2] == 5); + assert(slice.len() == 3); +} + +// Test returning a merged slice without a mutation +fn merge_slices_return(x: Field, y: Field) -> [Field] { let slice = [0; 2]; - let slice = if x != y { + if x != y { if x != 20 { slice.push_back(y) } else { @@ -95,11 +129,11 @@ fn regression_merge_slices(x: Field, y: Field) { } } else { slice - }; - assert(slice[2] == 10); - assert(slice.len() == 3); + } +} - // Test mutating a slice inside of an if statement +// Test mutating a slice inside of an if statement +fn merge_slices_mutate(x: Field, y: Field) -> [Field] { let mut slice = [0; 2]; if x != y { slice = slice.push_back(y); @@ -107,10 +141,11 @@ fn regression_merge_slices(x: Field, y: Field) { } else { slice = slice.push_back(x); } - assert(slice[3] == 5); - assert(slice.len() == 4); + slice +} - // Test mutating a slice inside of a loop in an if statement +// Test mutating a slice inside of a loop in an if statement +fn merge_slices_mutate_in_loop(x: Field, y: Field) -> [Field] { let mut slice = [0; 2]; if x != y { for i in 0..5 { @@ -119,6 +154,5 @@ fn regression_merge_slices(x: Field, y: Field) { } else { slice = slice.push_back(x); } - assert(slice[6] == 4); - assert(slice.len() == 7); + slice } diff --git a/crates/noirc_evaluator/src/ssa/ir/instruction/call.rs b/crates/noirc_evaluator/src/ssa/ir/instruction/call.rs index 28f4619be84..6fc87c55e2b 100644 --- a/crates/noirc_evaluator/src/ssa/ir/instruction/call.rs +++ b/crates/noirc_evaluator/src/ssa/ir/instruction/call.rs @@ -100,7 +100,6 @@ pub(super) fn simplify_call( } Intrinsic::SlicePushFront => { let slice = dfg.get_array_constant(arguments[1]); - dbg!(slice.clone()); if let Some((mut slice, element_type)) = slice { for elem in arguments[2..].iter().rev() { slice.push_front(*elem); From 394b5c5a02a9119066c0d4ade8a560ec3a9c1cf5 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Wed, 16 Aug 2023 17:05:17 +0000 Subject: [PATCH 44/62] small comments --- crates/noirc_evaluator/src/ssa/opt/flatten_cfg.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/noirc_evaluator/src/ssa/opt/flatten_cfg.rs b/crates/noirc_evaluator/src/ssa/opt/flatten_cfg.rs index 29ff288509a..b464c28ea10 100644 --- a/crates/noirc_evaluator/src/ssa/opt/flatten_cfg.rs +++ b/crates/noirc_evaluator/src/ssa/opt/flatten_cfg.rs @@ -437,8 +437,8 @@ impl<'f> Context<'f> { let typevars = Some(vec![element_type.clone()]); let mut get_element = |array, typevars, len| { - // The smaller slice is filled with placeholder information. Codegen for slice accesses must - // include checks against the dynamic slice length + // The smaller slice is filled with placeholder data. Codegen for slice accesses must + // include checks against the dynamic slice length so that this placeholder data is not incorrectly accessed. if (len - 1) < index_value.to_u128() as usize { self.inserter .function From 14b676de95053f071738087f769292f193d42b81 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Wed, 16 Aug 2023 17:05:27 +0000 Subject: [PATCH 45/62] remove dbg --- crates/noirc_evaluator/src/ssa/opt/flatten_cfg.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/noirc_evaluator/src/ssa/opt/flatten_cfg.rs b/crates/noirc_evaluator/src/ssa/opt/flatten_cfg.rs index b464c28ea10..97b42c3f74d 100644 --- a/crates/noirc_evaluator/src/ssa/opt/flatten_cfg.rs +++ b/crates/noirc_evaluator/src/ssa/opt/flatten_cfg.rs @@ -461,7 +461,7 @@ impl<'f> Context<'f> { )); } } - // dbg!(merged.clone()); + self.inserter.function.dfg.make_array(merged, typ) } From 79999963fd5f278f9d247daa22f5515030888a30 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Wed, 16 Aug 2023 17:59:42 +0000 Subject: [PATCH 46/62] remove unnecessary noir test --- .../compile_failure/slice_access_failure/src/main.nr | 8 -------- 1 file changed, 8 deletions(-) diff --git a/crates/nargo_cli/tests/compile_failure/slice_access_failure/src/main.nr b/crates/nargo_cli/tests/compile_failure/slice_access_failure/src/main.nr index fa23d24379f..4dac79d0656 100644 --- a/crates/nargo_cli/tests/compile_failure/slice_access_failure/src/main.nr +++ b/crates/nargo_cli/tests/compile_failure/slice_access_failure/src/main.nr @@ -9,11 +9,3 @@ fn main(x : Field, y : pub Field) { // This constraint should fail as the slice length is 2 and the index is 2 assert(slice[2] == 0); } - -#[test] -fn test_main() { - main(1, 2); - - // Uncomment to make test fail - // main(1, 1); -} From ffda9cc42de4c3442cac8a69a51d067b64cca72e Mon Sep 17 00:00:00 2001 From: vezenovm Date: Wed, 16 Aug 2023 18:01:15 +0000 Subject: [PATCH 47/62] remove check on failed_to_unroll --- crates/noirc_evaluator/src/ssa/opt/unrolling.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/noirc_evaluator/src/ssa/opt/unrolling.rs b/crates/noirc_evaluator/src/ssa/opt/unrolling.rs index c25a66c0875..9bf62bda1cb 100644 --- a/crates/noirc_evaluator/src/ssa/opt/unrolling.rs +++ b/crates/noirc_evaluator/src/ssa/opt/unrolling.rs @@ -60,7 +60,7 @@ pub(crate) struct Loop { pub(crate) struct Loops { /// The loops that failed to be unrolled so that we do not try to unroll them again. /// Each loop is identified by its header block id. - pub(crate) failed_to_unroll: HashSet, + failed_to_unroll: HashSet, pub(crate) yet_to_unroll: Vec, modified_blocks: HashSet, From 9e4a065ee878ed2ce8bdd1b552c08833938c5e66 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Wed, 16 Aug 2023 18:02:59 +0000 Subject: [PATCH 48/62] cleanup index_lvalue --- crates/noirc_evaluator/src/ssa/ssa_gen/context.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/crates/noirc_evaluator/src/ssa/ssa_gen/context.rs b/crates/noirc_evaluator/src/ssa/ssa_gen/context.rs index 8afcd54c112..7cdc1df2c91 100644 --- a/crates/noirc_evaluator/src/ssa/ssa_gen/context.rs +++ b/crates/noirc_evaluator/src/ssa/ssa_gen/context.rs @@ -597,17 +597,16 @@ impl<'a> FunctionContext<'a> { let (old_array, array_lvalue) = self.extract_current_value_recursive(array); let index = self.codegen_non_tuple_expression(index); let array_lvalue = Box::new(array_lvalue); - + let array_values = old_array.clone().into_value_list(self); + // A slice is represented as a tuple (length, slice contents) // We need to fetch the second - if old_array.count_leaves() > 1 { - let slice_values = old_array.clone().into_value_list(self); + if array_values.len() > 1 { let slice_lvalue = LValue::SliceIndex { old_slice: old_array, index, slice_lvalue: array_lvalue }; - (slice_values[1], index, slice_lvalue, Some(slice_values[0])) + (array_values[1], index, slice_lvalue, Some(array_values[0])) } else { - let old_array = old_array.into_leaf().eval(self); - (old_array, index, LValue::Index { old_array, index, array_lvalue }, None) + (array_values[0], index, LValue::Index { old_array: array_values[0], index, array_lvalue }, None) } } From 2b82aca8eaece0ec824a57a750d15656761e7893 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Wed, 16 Aug 2023 18:06:06 +0000 Subject: [PATCH 49/62] cleanup codegen_literal --- crates/noirc_evaluator/src/ssa/ssa_gen/context.rs | 4 ++-- crates/noirc_evaluator/src/ssa/ssa_gen/mod.rs | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/crates/noirc_evaluator/src/ssa/ssa_gen/context.rs b/crates/noirc_evaluator/src/ssa/ssa_gen/context.rs index 7cdc1df2c91..f5b4d7c581d 100644 --- a/crates/noirc_evaluator/src/ssa/ssa_gen/context.rs +++ b/crates/noirc_evaluator/src/ssa/ssa_gen/context.rs @@ -598,7 +598,7 @@ impl<'a> FunctionContext<'a> { let index = self.codegen_non_tuple_expression(index); let array_lvalue = Box::new(array_lvalue); let array_values = old_array.clone().into_value_list(self); - + // A slice is represented as a tuple (length, slice contents) // We need to fetch the second if array_values.len() > 1 { @@ -860,7 +860,7 @@ impl SharedContext { } /// Used to remember the results of each step of extracting a value from an ast::LValue -#[derive(Debug, Clone)] +#[derive(Debug)] pub(super) enum LValue { Ident, Index { old_array: ValueId, index: ValueId, array_lvalue: Box }, diff --git a/crates/noirc_evaluator/src/ssa/ssa_gen/mod.rs b/crates/noirc_evaluator/src/ssa/ssa_gen/mod.rs index 37ce534a332..35eaa240deb 100644 --- a/crates/noirc_evaluator/src/ssa/ssa_gen/mod.rs +++ b/crates/noirc_evaluator/src/ssa/ssa_gen/mod.rs @@ -120,19 +120,19 @@ impl<'a> FunctionContext<'a> { match literal { ast::Literal::Array(array) => { let elements = vecmap(&array.contents, |element| self.codegen_expression(element)); - let typ = Self::convert_non_tuple_type(&array.typ); - let new_convert_type = Self::convert_type(&array.typ); - - if new_convert_type.count_leaves() > 1 { + let contents_typ = Self::convert_non_tuple_type(&array.typ); + + let typ = Self::convert_type(&array.typ); + if typ.count_leaves() > 1 { let slice_length = ast::Literal::Integer( (array.contents.len() as u128).into(), ast::Type::Field, ); let slice_length = self.codegen_literal(&slice_length); - let slice_contents = self.codegen_array(elements, typ); + let slice_contents = self.codegen_array(elements, contents_typ); Tree::Branch(vec![slice_length, slice_contents]) } else { - self.codegen_array(elements, typ) + self.codegen_array(elements, contents_typ) } } ast::Literal::Integer(value, typ) => { From 89e553e1789d7968941904e4407aeacd9e19ef66 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Thu, 17 Aug 2023 13:59:43 +0000 Subject: [PATCH 50/62] remove radix padding --- .../brillig_to_le_bytes/src/main.nr | 15 +++++++-------- .../execution_success/to_le_bytes/src/main.nr | 4 +++- crates/noirc_evaluator/src/brillig/brillig_ir.rs | 2 -- .../src/ssa/acir_gen/acir_ir/acir_variable.rs | 10 ---------- 4 files changed, 10 insertions(+), 21 deletions(-) diff --git a/crates/nargo_cli/tests/execution_success/brillig_to_le_bytes/src/main.nr b/crates/nargo_cli/tests/execution_success/brillig_to_le_bytes/src/main.nr index 3afa65d9eff..36c5f0c4047 100644 --- a/crates/nargo_cli/tests/execution_success/brillig_to_le_bytes/src/main.nr +++ b/crates/nargo_cli/tests/execution_success/brillig_to_le_bytes/src/main.nr @@ -1,14 +1,13 @@ use dep::std; -unconstrained fn main(x : Field) -> pub [u8; 4] { +unconstrained fn main(x : Field) -> pub [u8; 31] { // The result of this byte array will be little-endian let byte_array = x.to_le_bytes(31); - let mut first_four_bytes = [0; 4]; - for i in 0..4 { - first_four_bytes[i] = byte_array[i]; + assert(byte_array.len() == 31); + + let mut bytes = [0; 31]; + for i in 0..31 { + bytes[i] = byte_array[i]; } - // Issue #617 fix - // We were incorrectly mapping our output array from bit decomposition functions during acir generation - first_four_bytes[3] = byte_array[31]; - first_four_bytes + bytes } diff --git a/crates/nargo_cli/tests/execution_success/to_le_bytes/src/main.nr b/crates/nargo_cli/tests/execution_success/to_le_bytes/src/main.nr index a5476ec13bf..5fd3538bdef 100644 --- a/crates/nargo_cli/tests/execution_success/to_le_bytes/src/main.nr +++ b/crates/nargo_cli/tests/execution_success/to_le_bytes/src/main.nr @@ -3,12 +3,14 @@ use dep::std; fn main(x : Field) -> pub [u8; 4] { // The result of this byte array will be little-endian let byte_array = x.to_le_bytes(31); + assert(byte_array.len() == 31); + let mut first_four_bytes = [0; 4]; for i in 0..4 { first_four_bytes[i] = byte_array[i]; } // Issue #617 fix // We were incorrectly mapping our output array from bit decomposition functions during acir generation - first_four_bytes[3] = byte_array[31]; + first_four_bytes[3] = byte_array[30]; first_four_bytes } diff --git a/crates/noirc_evaluator/src/brillig/brillig_ir.rs b/crates/noirc_evaluator/src/brillig/brillig_ir.rs index f9f93db36b0..4faaaa38d94 100644 --- a/crates/noirc_evaluator/src/brillig/brillig_ir.rs +++ b/crates/noirc_evaluator/src/brillig/brillig_ir.rs @@ -860,8 +860,6 @@ impl BrilligContext { big_endian: bool, ) { self.mov_instruction(target_vector_len, limb_count); - // The full array that is returned is going to be the limb count + 1 - self.usize_op_in_place(target_vector_len, BinaryIntOp::Add, 1usize); self.mov_instruction(target_vector.size, limb_count); self.allocate_array_instruction(target_vector.pointer, target_vector.size); diff --git a/crates/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs b/crates/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs index 0196635bfb7..d682c30d5ab 100644 --- a/crates/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs +++ b/crates/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs @@ -780,16 +780,6 @@ impl AcirContext { limb_vars.reverse(); } - // For legacy reasons (see #617) the to_radix interface supports 256 bits even though - // FieldElement::max_num_bits() is only 254 bits. Any limbs beyond the specified count - // become zero padding. - let max_decomposable_bits: u32 = 256; - let limb_count_with_padding = max_decomposable_bits / bit_size; - let zero = self.add_constant(FieldElement::zero()); - while limb_vars.len() < limb_count_with_padding as usize { - limb_vars.push(AcirValue::Var(zero, result_element_type.clone())); - } - // `Intrinsic::ToRadix` returns slices which are represented // by tuples with the structure (length, slice contents) Ok(vec![ From 7ba3be51fa1cdcb1a713b82a6e0b514e60987a32 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Fri, 18 Aug 2023 14:38:22 +0000 Subject: [PATCH 51/62] some pr comments and formatting --- .../slice_access_failure/src/main.nr | 6 +- crates/noirc_evaluator/src/ssa.rs | 5 +- .../src/ssa/ir/instruction/call.rs | 12 +-- .../src/ssa/ssa_gen/context.rs | 55 +++++++------- crates/noirc_evaluator/src/ssa/ssa_gen/mod.rs | 76 ++++++++++--------- 5 files changed, 83 insertions(+), 71 deletions(-) diff --git a/crates/nargo_cli/tests/compile_failure/slice_access_failure/src/main.nr b/crates/nargo_cli/tests/compile_failure/slice_access_failure/src/main.nr index 4dac79d0656..dc651cd514d 100644 --- a/crates/nargo_cli/tests/compile_failure/slice_access_failure/src/main.nr +++ b/crates/nargo_cli/tests/compile_failure/slice_access_failure/src/main.nr @@ -6,6 +6,8 @@ fn main(x : Field, y : pub Field) { } else { slice = slice.push_back(x); } - // This constraint should fail as the slice length is 2 and the index is 2 - assert(slice[2] == 0); + // This constraint should fail as the slice length is 3 and the index is 3 + // The right hand side AND case ensures that the circuit inputs have not changed + // and we always hit the else case in the if statement above. + assert((slice[3] == 0) & (slice[2] != y)); } diff --git a/crates/noirc_evaluator/src/ssa.rs b/crates/noirc_evaluator/src/ssa.rs index 6f9ab219c99..46ee26fc839 100644 --- a/crates/noirc_evaluator/src/ssa.rs +++ b/crates/noirc_evaluator/src/ssa.rs @@ -58,8 +58,9 @@ pub(crate) fn optimize_into_acir( .simplify_cfg() .print(print_ssa_passes, "After Simplifying:") // Run mem2reg before flattening to handle any promotion - // of values that can be accessed after loop unrolling - // If this pass is missed slice merging will fail inside of flattening + // of values that can be accessed after loop unrolling. + // If there are slice mergers uncovered by loop unrolling + // and this pass is missed, slice merging will fail inside of flattening. .mem2reg() .print(print_ssa_passes, "After Mem2Reg:") .flatten_cfg() diff --git a/crates/noirc_evaluator/src/ssa/ir/instruction/call.rs b/crates/noirc_evaluator/src/ssa/ir/instruction/call.rs index 6fc87c55e2b..9a996618ff9 100644 --- a/crates/noirc_evaluator/src/ssa/ir/instruction/call.rs +++ b/crates/noirc_evaluator/src/ssa/ir/instruction/call.rs @@ -75,8 +75,6 @@ pub(super) fn simplify_call( if let Some(length) = dfg.try_get_array_length(arguments[0]) { let length = FieldElement::from(length as u128); SimplifyResult::SimplifiedTo(dfg.make_constant(length, Type::field())) - } else if let Some(length) = dfg.get_numeric_constant(arguments[0]) { - SimplifyResult::SimplifiedTo(dfg.make_constant(length, Type::field())) } else if matches!(dfg.type_of_value(arguments[1]), Type::Slice(_)) { SimplifyResult::SimplifiedTo(arguments[0]) } else { @@ -222,9 +220,13 @@ pub(super) fn simplify_call( } } -// Slices have a tuple structure (slice length, slice contents) to enable logic -// that uses dynamic slice lengths (such as with merging slices in the flattening pass). -// This method codegens an update to the slice length. +/// Slices have a tuple structure (slice length, slice contents) to enable logic +/// that uses dynamic slice lengths (such as with merging slices in the flattening pass). +/// This method codegens an update to the slice length. +/// +/// The binary operation performed on the slice length is always an addition or subtraction of `1`. +/// This is because the slice length holds the user length (length as displayed by a `.len()` call), +/// and not a flattened length used in SSA internally to represent arrays of tuples. fn update_slice_length(slice_len: ValueId, dfg: &mut DataFlowGraph, operator: BinaryOp) -> ValueId { let one = dfg.make_constant(FieldElement::one(), Type::field()); let block = dfg.make_block(); diff --git a/crates/noirc_evaluator/src/ssa/ssa_gen/context.rs b/crates/noirc_evaluator/src/ssa/ssa_gen/context.rs index f5b4d7c581d..21cead13a4f 100644 --- a/crates/noirc_evaluator/src/ssa/ssa_gen/context.rs +++ b/crates/noirc_evaluator/src/ssa/ssa_gen/context.rs @@ -226,10 +226,7 @@ impl<'a> FunctionContext<'a> { ast::Type::Unit => panic!("convert_non_tuple_type called on a unit type"), ast::Type::Tuple(_) => panic!("convert_non_tuple_type called on a tuple: {typ}"), ast::Type::Function(_, _, _) => Type::Function, - ast::Type::Slice(element) => { - let element_types = Self::convert_type(element).flatten(); - Type::Slice(Rc::new(element_types)) - } + ast::Type::Slice(_) => panic!("convert_non_tuple_type called on a slice: {typ}"), ast::Type::MutableReference(element) => { // Recursive call to panic if element is a tuple Self::convert_non_tuple_type(element); @@ -606,7 +603,12 @@ impl<'a> FunctionContext<'a> { LValue::SliceIndex { old_slice: old_array, index, slice_lvalue: array_lvalue }; (array_values[1], index, slice_lvalue, Some(array_values[0])) } else { - (array_values[0], index, LValue::Index { old_array: array_values[0], index, array_lvalue }, None) + ( + array_values[0], + index, + LValue::Index { old_array: array_values[0], index, array_lvalue }, + None, + ) } } @@ -650,33 +652,14 @@ impl<'a> FunctionContext<'a> { match lvalue { LValue::Ident => unreachable!("Cannot assign to a variable without a reference"), LValue::Index { old_array: mut array, index, array_lvalue } => { - let element_size = self.builder.field_constant(self.element_size(array)); - - // The actual base index is the user's index * the array element type's size - let mut index = self.builder.insert_binary(index, BinaryOp::Mul, element_size); - let one = self.builder.field_constant(FieldElement::one()); - - new_value.for_each(|value| { - let value = value.eval(self); - array = self.builder.insert_array_set(array, index, value); - index = self.builder.insert_binary(index, BinaryOp::Add, one); - }); + array = self.assign_lvalue_index(new_value, array, index); self.assign_new_value(*array_lvalue, array.into()); } LValue::SliceIndex { old_slice: slice, index, slice_lvalue } => { let mut slice_values = slice.into_value_list(self); - let element_size = self.builder.field_constant(self.element_size(slice_values[1])); + slice_values[1] = self.assign_lvalue_index(new_value, slice_values[1], index); - // The actual base index is the user's index * the array element type's size - let mut index = self.builder.insert_binary(index, BinaryOp::Mul, element_size); - let one = self.builder.field_constant(FieldElement::one()); - - new_value.for_each(|value| { - let value = value.eval(self); - slice_values[1] = self.builder.insert_array_set(slice_values[1], index, value); - index = self.builder.insert_binary(index, BinaryOp::Add, one); - }); // The size of the slice does not change in an assign so we can reuse the same length value let new_slice = Tree::Branch(vec![slice_values[0].into(), slice_values[1].into()]); self.assign_new_value(*slice_lvalue, new_slice); @@ -691,6 +674,26 @@ impl<'a> FunctionContext<'a> { } } + fn assign_lvalue_index( + &mut self, + new_value: Values, + mut array: ValueId, + index: ValueId, + ) -> ValueId { + let element_size = self.builder.field_constant(self.element_size(array)); + + // The actual base index is the user's index * the array element type's size + let mut index = self.builder.insert_binary(index, BinaryOp::Mul, element_size); + let one = self.builder.field_constant(FieldElement::one()); + + new_value.for_each(|value| { + let value = value.eval(self); + array = self.builder.insert_array_set(array, index, value); + index = self.builder.insert_binary(index, BinaryOp::Add, one); + }); + array + } + fn element_size(&self, array: ValueId) -> FieldElement { let size = self.builder.type_of_value(array).element_size(); FieldElement::from(size as u128) diff --git a/crates/noirc_evaluator/src/ssa/ssa_gen/mod.rs b/crates/noirc_evaluator/src/ssa/ssa_gen/mod.rs index 35eaa240deb..d0963a3c413 100644 --- a/crates/noirc_evaluator/src/ssa/ssa_gen/mod.rs +++ b/crates/noirc_evaluator/src/ssa/ssa_gen/mod.rs @@ -120,19 +120,17 @@ impl<'a> FunctionContext<'a> { match literal { ast::Literal::Array(array) => { let elements = vecmap(&array.contents, |element| self.codegen_expression(element)); - let contents_typ = Self::convert_non_tuple_type(&array.typ); - - let typ = Self::convert_type(&array.typ); - if typ.count_leaves() > 1 { - let slice_length = ast::Literal::Integer( - (array.contents.len() as u128).into(), - ast::Type::Field, - ); - let slice_length = self.codegen_literal(&slice_length); - let slice_contents = self.codegen_array(elements, contents_typ); - Tree::Branch(vec![slice_length, slice_contents]) - } else { - self.codegen_array(elements, contents_typ) + + let typ = Self::convert_type(&array.typ).flatten(); + match array.typ { + ast::Type::Array(_, _) => self.codegen_array(elements, typ[0].clone()), + ast::Type::Slice(_) => { + let slice_length = + self.builder.field_constant(array.contents.len() as u128); + let slice_contents = self.codegen_array(elements, typ[1].clone()); + Tree::Branch(vec![slice_length.into(), slice_contents]) + } + _ => unreachable!("ICE: array literal type but an array or a slice"), } } ast::Literal::Integer(value, typ) => { @@ -290,7 +288,7 @@ impl<'a> FunctionContext<'a> { index: super::ir::value::ValueId, element_type: &ast::Type, location: Location, - max_length: Option, + length: Option, ) -> Values { // base_index = index * type_size let type_size = Self::convert_type(element_type).size_of_type(); @@ -305,29 +303,8 @@ impl<'a> FunctionContext<'a> { let array_type = &self.builder.type_of_value(array); match array_type { - // Prepare a slice access - // Check that the index being used is less than the dynamic slice length Type::Slice(_) => { - let array_len = - max_length.expect("ICE: a length must be supplied for indexing slices"); - // Check the type of the index value for valid comparisons - let array_len = match self.builder.type_of_value(index) { - Type::Numeric(numeric_type) => match numeric_type { - // If the index itself is an integer, keep the array length as a Field - NumericType::Unsigned { .. } | NumericType::Signed { .. } => array_len, - // If the index and the array length are both Fields we will not be able to perform a less than comparison on them. - // Thus, we cast the array length to a u64 before performing the less than comparison - NumericType::NativeField => self.builder.insert_cast( - array_len, - Type::Numeric(NumericType::Unsigned { bit_size: 64 }), - ), - }, - _ => unreachable!("ICE: array index must be a numeric type"), - }; - - let is_offset_out_of_bounds = - self.builder.insert_binary(index, BinaryOp::Lt, array_len); - self.builder.insert_constrain(is_offset_out_of_bounds); + self.codegen_slice_access_check(index, length); } Type::Array(..) => { // Nothing needs to done to prepare an array access on an array @@ -338,6 +315,33 @@ impl<'a> FunctionContext<'a> { }) } + /// Prepare a slice access. + /// Check that the index being used to access a slice element + /// is less than the dynamic slice length. + fn codegen_slice_access_check( + &mut self, + index: super::ir::value::ValueId, + length: Option, + ) { + let array_len = length.expect("ICE: a length must be supplied for indexing slices"); + // Check the type of the index value for valid comparisons + let array_len = match self.builder.type_of_value(index) { + Type::Numeric(numeric_type) => match numeric_type { + // If the index itself is an integer, keep the array length as a Field + NumericType::Unsigned { .. } | NumericType::Signed { .. } => array_len, + // If the index and the array length are both Fields we will not be able to perform a less than comparison on them. + // Thus, we cast the array length to a u64 before performing the less than comparison + NumericType::NativeField => self + .builder + .insert_cast(array_len, Type::Numeric(NumericType::Unsigned { bit_size: 64 })), + }, + _ => unreachable!("ICE: array index must be a numeric type"), + }; + + let is_offset_out_of_bounds = self.builder.insert_binary(index, BinaryOp::Lt, array_len); + self.builder.insert_constrain(is_offset_out_of_bounds); + } + fn codegen_cast(&mut self, cast: &ast::Cast) -> Values { let lhs = self.codegen_non_tuple_expression(&cast.lhs); let typ = Self::convert_non_tuple_type(&cast.r#type); From caebc6b20fe71df6cdecf5f09a3b7e69b114d47b Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Fri, 18 Aug 2023 15:38:55 +0100 Subject: [PATCH 52/62] Update crates/nargo_cli/tests/execution_success/brillig_to_le_bytes/src/main.nr Co-authored-by: jfecher --- .../tests/execution_success/brillig_to_le_bytes/src/main.nr | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/nargo_cli/tests/execution_success/brillig_to_le_bytes/src/main.nr b/crates/nargo_cli/tests/execution_success/brillig_to_le_bytes/src/main.nr index 36c5f0c4047..a72b13dcdf5 100644 --- a/crates/nargo_cli/tests/execution_success/brillig_to_le_bytes/src/main.nr +++ b/crates/nargo_cli/tests/execution_success/brillig_to_le_bytes/src/main.nr @@ -4,7 +4,6 @@ unconstrained fn main(x : Field) -> pub [u8; 31] { // The result of this byte array will be little-endian let byte_array = x.to_le_bytes(31); assert(byte_array.len() == 31); - let mut bytes = [0; 31]; for i in 0..31 { bytes[i] = byte_array[i]; From a2a08989e28ce7e41cb33520aecbbeacb75bc166 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Fri, 18 Aug 2023 15:40:19 +0100 Subject: [PATCH 53/62] Update crates/noirc_evaluator/src/ssa/ir/instruction/call.rs Co-authored-by: jfecher --- crates/noirc_evaluator/src/ssa/ir/instruction/call.rs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/crates/noirc_evaluator/src/ssa/ir/instruction/call.rs b/crates/noirc_evaluator/src/ssa/ir/instruction/call.rs index 9a996618ff9..8196e5ab92d 100644 --- a/crates/noirc_evaluator/src/ssa/ir/instruction/call.rs +++ b/crates/noirc_evaluator/src/ssa/ir/instruction/call.rs @@ -230,13 +230,9 @@ pub(super) fn simplify_call( fn update_slice_length(slice_len: ValueId, dfg: &mut DataFlowGraph, operator: BinaryOp) -> ValueId { let one = dfg.make_constant(FieldElement::one(), Type::field()); let block = dfg.make_block(); - dfg.insert_instruction_and_results( - Instruction::Binary(Binary { lhs: slice_len, operator, rhs: one }), - block, - None, - dfg.get_value_call_stack(slice_len), - ) - .first() + let instruction = Instruction::Binary(Binary { lhs: slice_len, operator, rhs: one }); + let call_stack = dfg.get_value_call_stack(slice_len); + dfg.insert_instruction_and_results(instruction, block, None, call_stack).first() } /// Try to simplify this black box call. If the call can be simplified to a known value, From 5327345b6ca0d5713c27f6d3411a0cf740d0a01a Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Fri, 18 Aug 2023 15:40:31 +0100 Subject: [PATCH 54/62] Update crates/noirc_evaluator/src/ssa/ssa_gen/mod.rs Co-authored-by: jfecher --- crates/noirc_evaluator/src/ssa/ssa_gen/mod.rs | 23 ++++--------------- 1 file changed, 5 insertions(+), 18 deletions(-) diff --git a/crates/noirc_evaluator/src/ssa/ssa_gen/mod.rs b/crates/noirc_evaluator/src/ssa/ssa_gen/mod.rs index d0963a3c413..acb87e1c196 100644 --- a/crates/noirc_evaluator/src/ssa/ssa_gen/mod.rs +++ b/crates/noirc_evaluator/src/ssa/ssa_gen/mod.rs @@ -255,25 +255,12 @@ impl<'a> FunctionContext<'a> { fn codegen_index(&mut self, index: &ast::Index) -> Values { let array_or_slice = self.codegen_expression(&index.collection).into_value_list(self); let index_value = self.codegen_non_tuple_expression(&index.index); - if array_or_slice.len() > 1 { - let slice_length = array_or_slice[0]; - let slice = array_or_slice[1]; - self.codegen_array_index( - slice, - index_value, - &index.element_type, - index.location, - Some(slice_length), - ) + let (array, slice_length) = if array_or_slice.len() > 1 { + (array_or_slice[1], Some(array_or_slice[0])) } else { - self.codegen_array_index( - array_or_slice[0], - index_value, - &index.element_type, - index.location, - None, - ) - } + (array_or_slice[0], None) + }; + self.codegen_array_index(array, index_value, &index.element_type, index.location, slice_length) } /// This is broken off from codegen_index so that it can also be From cca2e3417070b7ed58cd0353b791cad27751dc7a Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Fri, 18 Aug 2023 15:41:04 +0100 Subject: [PATCH 55/62] Update crates/noirc_evaluator/src/ssa/opt/flatten_cfg.rs Co-authored-by: jfecher --- crates/noirc_evaluator/src/ssa/opt/flatten_cfg.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/noirc_evaluator/src/ssa/opt/flatten_cfg.rs b/crates/noirc_evaluator/src/ssa/opt/flatten_cfg.rs index 97b42c3f74d..f3b525d8a8b 100644 --- a/crates/noirc_evaluator/src/ssa/opt/flatten_cfg.rs +++ b/crates/noirc_evaluator/src/ssa/opt/flatten_cfg.rs @@ -427,7 +427,7 @@ impl<'f> Context<'f> { _ => panic!("Expected array value"), }; - let len = if len > else_len { len } else { else_len }; + let len = len.max(else_len); for i in 0..len { for (element_index, element_type) in element_types.iter().enumerate() { From 892b435a37f3f46c4cb2e6e52f025b11d978050d Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Fri, 18 Aug 2023 15:41:30 +0100 Subject: [PATCH 56/62] Update crates/noirc_evaluator/src/ssa/opt/flatten_cfg.rs Co-authored-by: jfecher --- crates/noirc_evaluator/src/ssa/opt/flatten_cfg.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/crates/noirc_evaluator/src/ssa/opt/flatten_cfg.rs b/crates/noirc_evaluator/src/ssa/opt/flatten_cfg.rs index f3b525d8a8b..1640afabe26 100644 --- a/crates/noirc_evaluator/src/ssa/opt/flatten_cfg.rs +++ b/crates/noirc_evaluator/src/ssa/opt/flatten_cfg.rs @@ -440,10 +440,8 @@ impl<'f> Context<'f> { // The smaller slice is filled with placeholder data. Codegen for slice accesses must // include checks against the dynamic slice length so that this placeholder data is not incorrectly accessed. if (len - 1) < index_value.to_u128() as usize { - self.inserter - .function - .dfg - .make_constant(FieldElement::zero(), Type::field()) + let zero = FieldElement::zero(); + self.inserter.function.dfg.make_constant(zero, Type::field()) } else { let get = Instruction::ArrayGet { array, index }; self.insert_instruction_with_typevars(get, typevars).first() From eaa6e9ce37c681a9cc517da357bdec569d55cb46 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Fri, 18 Aug 2023 15:42:26 +0100 Subject: [PATCH 57/62] Update crates/noirc_evaluator/src/ssa/ssa_gen/context.rs Co-authored-by: jfecher --- crates/noirc_evaluator/src/ssa/ssa_gen/context.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/noirc_evaluator/src/ssa/ssa_gen/context.rs b/crates/noirc_evaluator/src/ssa/ssa_gen/context.rs index 21cead13a4f..26dce30b323 100644 --- a/crates/noirc_evaluator/src/ssa/ssa_gen/context.rs +++ b/crates/noirc_evaluator/src/ssa/ssa_gen/context.rs @@ -584,8 +584,8 @@ impl<'a> FunctionContext<'a> { /// Compile the given `array[index]` expression as a reference. /// This will return a triple of (array, index, lvalue_ref, Option) where the lvalue_ref records the /// structure of the lvalue expression for use by `assign_new_value`. - /// The optional max length is for the case where we are indexing a slice rather than an array as slices - /// are representing as the following tuple: (length, slice contents). + /// The optional max length is for indexing slices rather than arrays since slices + /// are represented as a tuple in the form: (length, slice contents). fn index_lvalue( &mut self, array: &ast::LValue, From bfea32434d5842036ebe122eeda16c4a0d64e2a9 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Fri, 18 Aug 2023 14:47:26 +0000 Subject: [PATCH 58/62] comments --- crates/noirc_evaluator/src/ssa/ssa_gen/context.rs | 8 ++++---- crates/noirc_evaluator/src/ssa/ssa_gen/mod.rs | 10 +++++++++- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/crates/noirc_evaluator/src/ssa/ssa_gen/context.rs b/crates/noirc_evaluator/src/ssa/ssa_gen/context.rs index 26dce30b323..a5cfd32b528 100644 --- a/crates/noirc_evaluator/src/ssa/ssa_gen/context.rs +++ b/crates/noirc_evaluator/src/ssa/ssa_gen/context.rs @@ -582,9 +582,9 @@ impl<'a> FunctionContext<'a> { } /// Compile the given `array[index]` expression as a reference. - /// This will return a triple of (array, index, lvalue_ref, Option) where the lvalue_ref records the + /// This will return a triple of (array, index, lvalue_ref, Option) where the lvalue_ref records the /// structure of the lvalue expression for use by `assign_new_value`. - /// The optional max length is for indexing slices rather than arrays since slices + /// The optional length is for indexing slices rather than arrays since slices /// are represented as a tuple in the form: (length, slice contents). fn index_lvalue( &mut self, @@ -596,8 +596,8 @@ impl<'a> FunctionContext<'a> { let array_lvalue = Box::new(array_lvalue); let array_values = old_array.clone().into_value_list(self); - // A slice is represented as a tuple (length, slice contents) - // We need to fetch the second + // A slice is represented as a tuple (length, slice contents). + // We need to fetch the second value. if array_values.len() > 1 { let slice_lvalue = LValue::SliceIndex { old_slice: old_array, index, slice_lvalue: array_lvalue }; diff --git a/crates/noirc_evaluator/src/ssa/ssa_gen/mod.rs b/crates/noirc_evaluator/src/ssa/ssa_gen/mod.rs index acb87e1c196..610957abe0b 100644 --- a/crates/noirc_evaluator/src/ssa/ssa_gen/mod.rs +++ b/crates/noirc_evaluator/src/ssa/ssa_gen/mod.rs @@ -255,12 +255,20 @@ impl<'a> FunctionContext<'a> { fn codegen_index(&mut self, index: &ast::Index) -> Values { let array_or_slice = self.codegen_expression(&index.collection).into_value_list(self); let index_value = self.codegen_non_tuple_expression(&index.index); + // Slices are represented as a tuple in the form: (length, slice contents). + // Thus, slices require two value ids for their representation. let (array, slice_length) = if array_or_slice.len() > 1 { (array_or_slice[1], Some(array_or_slice[0])) } else { (array_or_slice[0], None) }; - self.codegen_array_index(array, index_value, &index.element_type, index.location, slice_length) + self.codegen_array_index( + array, + index_value, + &index.element_type, + index.location, + slice_length, + ) } /// This is broken off from codegen_index so that it can also be From f8c9c70c605b8a8ecdeb7e24f0e56a5341b1c2b0 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Fri, 18 Aug 2023 15:01:45 +0000 Subject: [PATCH 59/62] cleanup and comments from pr review --- .../src/brillig/brillig_gen/brillig_block.rs | 54 +++++++++++-------- .../noirc_evaluator/src/brillig/brillig_ir.rs | 2 - .../src/ssa/ir/instruction/call.rs | 2 +- 3 files changed, 34 insertions(+), 24 deletions(-) diff --git a/crates/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs b/crates/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs index 41c973d05b8..38df0374a96 100644 --- a/crates/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs +++ b/crates/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs @@ -318,8 +318,14 @@ impl<'block> BrilligBlock<'block> { dfg, ); let param_id = arguments[0]; + // Slices are represented as a tuple in the form: (length, slice contents). + // Thus, we can expect the first argument to a field in the case of a slice + // or an array in the case of an array. if let Type::Numeric(_) = dfg.type_of_value(param_id) { - self.convert_ssa_value(arguments[0], dfg); + let len_variable = self.convert_ssa_value(arguments[0], dfg); + let len_register_index = + self.function_context.extract_register(len_variable); + self.brillig_context.mov_instruction(result_register, len_register_index); } else { self.convert_ssa_array_len(arguments[0], result_register, dfg); } @@ -346,14 +352,12 @@ impl<'block> BrilligBlock<'block> { let results = dfg.instruction_results(instruction_id); - let target_len = match self.function_context.get_or_create_variable( + let target_len_variable = self.function_context.get_or_create_variable( self.brillig_context, results[0], dfg, - ) { - RegisterOrMemory::RegisterIndex(register_index) => register_index, - _ => unreachable!("ICE: first value of a slice must be a register index"), - }; + ); + let target_len = self.function_context.extract_register(target_len_variable); let target_slice = self.function_context.create_variable( self.brillig_context, @@ -362,9 +366,12 @@ impl<'block> BrilligBlock<'block> { ); let heap_vec = self.brillig_context.extract_heap_vector(target_slice); + + // Update the user-facing slice length + self.brillig_context.mov_instruction(target_len, limb_count); + self.brillig_context.radix_instruction( source, - target_len, heap_vec, radix, limb_count, @@ -377,14 +384,12 @@ impl<'block> BrilligBlock<'block> { let results = dfg.instruction_results(instruction_id); - let target_len = match self.function_context.get_or_create_variable( + let target_len_variable = self.function_context.get_or_create_variable( self.brillig_context, results[0], dfg, - ) { - RegisterOrMemory::RegisterIndex(register_index) => register_index, - _ => unreachable!("ICE: first value of a slice must be a register index"), - }; + ); + let target_len = self.function_context.extract_register(target_len_variable); let target_slice = self.function_context.create_variable( self.brillig_context, @@ -394,9 +399,12 @@ impl<'block> BrilligBlock<'block> { let radix = self.brillig_context.make_constant(2_usize.into()); let heap_vec = self.brillig_context.extract_heap_vector(target_slice); + + // Update the user-facing slice length + self.brillig_context.mov_instruction(target_len, limb_count); + self.brillig_context.radix_instruction( source, - target_len, heap_vec, radix, limb_count, @@ -815,6 +823,15 @@ impl<'block> BrilligBlock<'block> { } } + /// Slices have a tuple structure (slice length, slice contents) to enable logic + /// that uses dynamic slice lengths (such as with merging slices in the flattening pass). + /// This method codegens an update to the slice length. + /// + /// The binary operation performed on the slice length is always an addition or subtraction of `1`. + /// This is because the slice length holds the user length (length as displayed by a `.len()` call), + /// and not a flattened length used internally to represent arrays of tuples. + /// The length inside of `RegisterOrMemory::HeapVector` represents the entire flattened number + /// of fields in the vector. fn update_slice_length( &mut self, target_len: RegisterIndex, @@ -822,14 +839,9 @@ impl<'block> BrilligBlock<'block> { dfg: &DataFlowGraph, binary_op: BinaryIntOp, ) { - let source_len = match self.function_context.get_or_create_variable( - self.brillig_context, - source_value, - dfg, - ) { - RegisterOrMemory::RegisterIndex(register_index) => register_index, - _ => unreachable!("ICE: first value of a slice must be a register index"), - }; + let source_len_variable = + self.function_context.get_or_create_variable(self.brillig_context, source_value, dfg); + let source_len = self.function_context.extract_register(source_len_variable); self.brillig_context.usize_op(source_len, target_len, binary_op, 1); } diff --git a/crates/noirc_evaluator/src/brillig/brillig_ir.rs b/crates/noirc_evaluator/src/brillig/brillig_ir.rs index 4faaaa38d94..047e8b7edf8 100644 --- a/crates/noirc_evaluator/src/brillig/brillig_ir.rs +++ b/crates/noirc_evaluator/src/brillig/brillig_ir.rs @@ -853,13 +853,11 @@ impl BrilligContext { pub(crate) fn radix_instruction( &mut self, source: RegisterIndex, - target_vector_len: RegisterIndex, target_vector: HeapVector, radix: RegisterIndex, limb_count: RegisterIndex, big_endian: bool, ) { - self.mov_instruction(target_vector_len, limb_count); self.mov_instruction(target_vector.size, limb_count); self.allocate_array_instruction(target_vector.pointer, target_vector.size); diff --git a/crates/noirc_evaluator/src/ssa/ir/instruction/call.rs b/crates/noirc_evaluator/src/ssa/ir/instruction/call.rs index 8196e5ab92d..e6b6f19026a 100644 --- a/crates/noirc_evaluator/src/ssa/ir/instruction/call.rs +++ b/crates/noirc_evaluator/src/ssa/ir/instruction/call.rs @@ -226,7 +226,7 @@ pub(super) fn simplify_call( /// /// The binary operation performed on the slice length is always an addition or subtraction of `1`. /// This is because the slice length holds the user length (length as displayed by a `.len()` call), -/// and not a flattened length used in SSA internally to represent arrays of tuples. +/// and not a flattened length used internally to represent arrays of tuples. fn update_slice_length(slice_len: ValueId, dfg: &mut DataFlowGraph, operator: BinaryOp) -> ValueId { let one = dfg.make_constant(FieldElement::one(), Type::field()); let block = dfg.make_block(); From b3a54c6ffea4347bb1c19cfc7ebabcb547d3e17c Mon Sep 17 00:00:00 2001 From: vezenovm Date: Fri, 18 Aug 2023 15:05:37 +0000 Subject: [PATCH 60/62] more specific comment for LValue::SliceIndex assign --- crates/noirc_evaluator/src/ssa/ssa_gen/context.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/noirc_evaluator/src/ssa/ssa_gen/context.rs b/crates/noirc_evaluator/src/ssa/ssa_gen/context.rs index a5cfd32b528..2584a6d6c2c 100644 --- a/crates/noirc_evaluator/src/ssa/ssa_gen/context.rs +++ b/crates/noirc_evaluator/src/ssa/ssa_gen/context.rs @@ -660,7 +660,7 @@ impl<'a> FunctionContext<'a> { slice_values[1] = self.assign_lvalue_index(new_value, slice_values[1], index); - // The size of the slice does not change in an assign so we can reuse the same length value + // The size of the slice does not change in a slice index assignment so we can reuse the same length value let new_slice = Tree::Branch(vec![slice_values[0].into(), slice_values[1].into()]); self.assign_new_value(*slice_lvalue, new_slice); } From 838c598acec338c87302be95decfb9b7375391f3 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Fri, 18 Aug 2023 15:07:39 +0000 Subject: [PATCH 61/62] improve error in codegen_literal --- crates/noirc_evaluator/src/ssa/ssa_gen/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/noirc_evaluator/src/ssa/ssa_gen/mod.rs b/crates/noirc_evaluator/src/ssa/ssa_gen/mod.rs index 610957abe0b..cc3a7c02a75 100644 --- a/crates/noirc_evaluator/src/ssa/ssa_gen/mod.rs +++ b/crates/noirc_evaluator/src/ssa/ssa_gen/mod.rs @@ -130,7 +130,7 @@ impl<'a> FunctionContext<'a> { let slice_contents = self.codegen_array(elements, typ[1].clone()); Tree::Branch(vec![slice_length.into(), slice_contents]) } - _ => unreachable!("ICE: array literal type but an array or a slice"), + _ => unreachable!("ICE: array literal type must be an array or a slice, but got {}", array.typ), } } ast::Literal::Integer(value, typ) => { From 99b1c2002e9154732b679d1374c5c8113936d4c2 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Fri, 18 Aug 2023 17:54:26 +0100 Subject: [PATCH 62/62] Update crates/noirc_evaluator/src/ssa/ssa_gen/context.rs Co-authored-by: jfecher --- crates/noirc_evaluator/src/ssa/ssa_gen/context.rs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/crates/noirc_evaluator/src/ssa/ssa_gen/context.rs b/crates/noirc_evaluator/src/ssa/ssa_gen/context.rs index 2584a6d6c2c..b04e4263f07 100644 --- a/crates/noirc_evaluator/src/ssa/ssa_gen/context.rs +++ b/crates/noirc_evaluator/src/ssa/ssa_gen/context.rs @@ -603,12 +603,8 @@ impl<'a> FunctionContext<'a> { LValue::SliceIndex { old_slice: old_array, index, slice_lvalue: array_lvalue }; (array_values[1], index, slice_lvalue, Some(array_values[0])) } else { - ( - array_values[0], - index, - LValue::Index { old_array: array_values[0], index, array_lvalue }, - None, - ) + let array_lvalue = LValue::Index { old_array: array_values[0], index, array_lvalue }; + (array_values[0], index, array_lvalue, None) } }