diff --git a/crates/noirc_evaluator/src/ssa/acir_gen/acir_mem.rs b/crates/noirc_evaluator/src/ssa/acir_gen/acir_mem.rs index 704de442eb2..ae0a7277ca0 100644 --- a/crates/noirc_evaluator/src/ssa/acir_gen/acir_mem.rs +++ b/crates/noirc_evaluator/src/ssa/acir_gen/acir_mem.rs @@ -117,6 +117,95 @@ impl ArrayHeap { self.staged.insert(index, (value, op)); } + /// This helper function transforms an expression into a single witness representing the expression + fn normalize_expression(expr: &Expression, evaluator: &mut Evaluator) -> Expression { + expr.to_witness() + .unwrap_or_else(|| evaluator.create_intermediate_variable(expr.clone())) + .into() + } + + /// Decide which opcode to use, depending on the backend support + fn acir_gen( + &self, + evaluator: &mut Evaluator, + array_id: ArrayId, + array_len: u32, + is_opcode_supported: OpcodeSupported, + ) { + //sanity check + if array_len == 0 || self.trace.is_empty() { + return; + } + let dummy = MemoryBlock { id: AcirBlockId(0), len: 0, trace: Vec::new() }; + let trace_len = match self.typ { + ArrayType::ReadOnly(Some(len)) | ArrayType::ReadWrite(Some(len)) => len, + _ => self.trace.len(), + }; + + if is_opcode_supported(&AcirOpcode::ROM(dummy.clone())) { + // If the backend support ROM and the array is read-only, we generate the ROM opcode + if matches!(self.typ, ArrayType::ReadOnly(_)) { + self.add_rom_opcode(evaluator, array_id, array_len, trace_len); + return; + } + } + if is_opcode_supported(&AcirOpcode::Block(dummy.clone())) { + self.add_block_opcode(evaluator, array_id, array_len); + } else if is_opcode_supported(&AcirOpcode::RAM(dummy)) { + self.add_ram_opcode(evaluator, array_id, array_len, trace_len); + } else { + self.generate_permutation_constraints(evaluator, array_id, array_len); + } + } + + fn add_block_opcode(&self, evaluator: &mut Evaluator, array_id: ArrayId, array_len: u32) { + evaluator.opcodes.push(AcirOpcode::Block(MemoryBlock { + id: AcirBlockId(array_id.as_u32()), + len: array_len, + trace: self.trace.clone(), + })); + } + + fn add_rom_opcode( + &self, + evaluator: &mut Evaluator, + array_id: ArrayId, + array_len: u32, + trace_len: usize, + ) { + let mut trace = Vec::with_capacity(trace_len); + for op in self.trace.iter().take(trace_len) { + let index = Self::normalize_expression(&op.index, evaluator); + let value = Self::normalize_expression(&op.value, evaluator); + trace.push(MemOp { operation: op.operation.clone(), index, value }); + } + evaluator.opcodes.push(AcirOpcode::ROM(MemoryBlock { + id: AcirBlockId(array_id.as_u32()), + len: array_len, + trace, + })); + } + + fn add_ram_opcode( + &self, + evaluator: &mut Evaluator, + array_id: ArrayId, + array_len: u32, + trace_len: usize, + ) { + let mut trace = Vec::with_capacity(trace_len); + for op in self.trace.iter().take(trace_len) { + let index = Self::normalize_expression(&op.index, evaluator); + let value = Self::normalize_expression(&op.value, evaluator); + trace.push(MemOp { operation: op.operation.clone(), index, value }); + } + evaluator.opcodes.push(AcirOpcode::RAM(MemoryBlock { + id: AcirBlockId(array_id.as_u32()), + len: array_len, + trace, + })); + } + fn generate_outputs( inputs: Vec, bits: &mut Vec, @@ -130,22 +219,21 @@ impl ArrayHeap { } outputs } - - pub(crate) fn acir_gen(&self, evaluator: &mut Evaluator, array_id: ArrayId, array_len: u32) { + fn generate_permutation_constraints( + &self, + evaluator: &mut Evaluator, + array_id: ArrayId, + array_len: u32, + ) { let (len, read_write) = match self.typ { ArrayType::Init(_, _) | ArrayType::WriteOnly => (0, true), ArrayType::ReadOnly(last) => (last.unwrap_or(self.trace.len()), false), ArrayType::ReadWrite(last) => (last.unwrap_or(self.trace.len()), true), }; - if len == 0 { return; } - evaluator.opcodes.push(AcirOpcode::Block(MemoryBlock { - id: AcirBlockId(array_id.as_u32()), - len: array_len, - trace: self.trace.clone(), - })); + self.add_block_opcode(evaluator, array_id, array_len); let len_bits = AcirMem::bits(len); // permutations let mut in_counter = Vec::new(); @@ -262,6 +350,17 @@ impl AcirMem { } } + //Ensure we do not optimise writes when the array is returned + pub(crate) fn return_array(&mut self, array_id: ArrayId) { + let heap = self.array_heap_mut(array_id); + match heap.typ { + ArrayType::ReadOnly(_) | ArrayType::ReadWrite(_) => { + heap.typ = ArrayType::ReadWrite(None); + } + _ => (), + } + } + // Load array values into InternalVars pub(super) fn load_array( &mut self, @@ -327,9 +426,18 @@ impl AcirMem { self.array_heap_mut(*array_id).push(item); } pub(crate) fn acir_gen(&self, evaluator: &mut Evaluator, ctx: &SsaContext) { + //Temporary hack - We hardcode Barretenberg support here. + //TODO: to remove once opcodesupported usage is clarified + let is_opcode_supported: OpcodeSupported = |o| match o { + AcirOpcode::Block(_) => false, + AcirOpcode::ROM(_) | AcirOpcode::RAM(_) => true, + _ => unreachable!(), + }; for mem in &self.virtual_memory { let array = &ctx.mem[*mem.0]; - mem.1.acir_gen(evaluator, array.id, array.len); + mem.1.acir_gen(evaluator, array.id, array.len, is_opcode_supported); } } } + +type OpcodeSupported = fn(&AcirOpcode) -> bool; diff --git a/crates/noirc_evaluator/src/ssa/acir_gen/operations/return.rs b/crates/noirc_evaluator/src/ssa/acir_gen/operations/return.rs index aa7586fb944..c3e935a1065 100644 --- a/crates/noirc_evaluator/src/ssa/acir_gen/operations/return.rs +++ b/crates/noirc_evaluator/src/ssa/acir_gen/operations/return.rs @@ -32,6 +32,7 @@ pub(crate) fn evaluate( let objects = match Memory::deref(ctx, *node_id) { Some(a) => { let array = &ctx.mem[a]; + memory_map.return_array(a); memory_map.load_array(array, evaluator) } None => { diff --git a/flake.lock b/flake.lock index 10839befbc2..c9047f7d52a 100644 --- a/flake.lock +++ b/flake.lock @@ -10,11 +10,11 @@ ] }, "locked": { - "lastModified": 1685812470, - "narHash": "sha256-sJYVipq1EthnjSxVIZnZF15wy9LDMHNPfIJKRHyZrws=", + "lastModified": 1686677483, + "narHash": "sha256-mpsCXzHMaqSveQcD/SA9k3NH4pF167KqR5/oYJJjKE8=", "owner": "AztecProtocol", "repo": "barretenberg", - "rev": "193ce1a45ef5eab6a8522178cf918e45320f3de8", + "rev": "65e651d04c6092cb5ca079cd9e12ed9b5846fa3a", "type": "github" }, "original": {