diff --git a/.aztec-sync-commit b/.aztec-sync-commit index 379f006b5c0..5e6d9f5f197 100644 --- a/.aztec-sync-commit +++ b/.aztec-sync-commit @@ -1 +1 @@ -a26419f00f5f082a9ed856346addf6276fbdb4d7 +91042c7bcebfebeb4e629162f44988e2cda1ed41 diff --git a/acvm-repo/acir/codegen/acir.cpp b/acvm-repo/acir/codegen/acir.cpp index 232b3ba12cf..b16baf14d06 100644 --- a/acvm-repo/acir/codegen/acir.cpp +++ b/acvm-repo/acir/codegen/acir.cpp @@ -1042,6 +1042,8 @@ namespace Program { }; struct CallData { + uint32_t value; + friend bool operator==(const CallData&, const CallData&); std::vector bincodeSerialize() const; static CallData bincodeDeserialize(std::vector); @@ -4683,6 +4685,7 @@ Program::BlockType::Memory serde::Deserializable::de namespace Program { inline bool operator==(const BlockType::CallData &lhs, const BlockType::CallData &rhs) { + if (!(lhs.value == rhs.value)) { return false; } return true; } @@ -4706,12 +4709,14 @@ namespace Program { template <> template void serde::Serializable::serialize(const Program::BlockType::CallData &obj, Serializer &serializer) { + serde::Serializable::serialize(obj.value, serializer); } template <> template Program::BlockType::CallData serde::Deserializable::deserialize(Deserializer &deserializer) { Program::BlockType::CallData obj; + obj.value = serde::Deserializable::deserialize(deserializer); return obj; } diff --git a/acvm-repo/acir/src/circuit/opcodes.rs b/acvm-repo/acir/src/circuit/opcodes.rs index 17b5388faa0..b1fdc5e0080 100644 --- a/acvm-repo/acir/src/circuit/opcodes.rs +++ b/acvm-repo/acir/src/circuit/opcodes.rs @@ -15,13 +15,13 @@ pub use memory_operation::{BlockId, MemOp}; #[derive(Clone, PartialEq, Eq, Serialize, Deserialize)] pub enum BlockType { Memory, - CallData, + CallData(u32), ReturnData, } impl BlockType { pub fn is_databus(&self) -> bool { - matches!(self, BlockType::CallData | BlockType::ReturnData) + matches!(self, BlockType::CallData(_) | BlockType::ReturnData) } } @@ -183,7 +183,7 @@ impl std::fmt::Display for Opcode { Opcode::MemoryInit { block_id, init, block_type: databus } => { match databus { BlockType::Memory => write!(f, "INIT ")?, - BlockType::CallData => write!(f, "INIT CALLDATA ")?, + BlockType::CallData(id) => write!(f, "INIT CALLDATA {} ", id)?, BlockType::ReturnData => write!(f, "INIT RETURNDATA ")?, } write!(f, "(id: {}, len: {}) ", block_id.0, init.len()) diff --git a/acvm-repo/acvm_js/src/execute.rs b/acvm-repo/acvm_js/src/execute.rs index 94ef30604e2..c596dcf9614 100644 --- a/acvm-repo/acvm_js/src/execute.rs +++ b/acvm-repo/acvm_js/src/execute.rs @@ -57,7 +57,7 @@ pub async fn execute_circuit_with_return_witness( console_error_panic_hook::set_once(); let program: Program = Program::deserialize_program(&program) - .map_err(|_| JsExecutionError::new("Failed to deserialize circuit. This is likely due to differing serialization formats between ACVM_JS and your compiler".to_string(), None, None))?; + .map_err(|_| JsExecutionError::new("Failed to deserialize circuit. This is likely due to differing serialization formats between ACVM_JS and your compiler".to_string(), None, None, None))?; let mut witness_stack = execute_program_with_native_program_and_return( &program, @@ -71,7 +71,7 @@ pub async fn execute_circuit_with_return_witness( let main_circuit = &program.functions[0]; let return_witness = extract_indices(&solved_witness, main_circuit.return_values.0.iter().copied().collect()) - .map_err(|err| JsExecutionError::new(err, None, None))?; + .map_err(|err| JsExecutionError::new(err, None, None, None))?; Ok((solved_witness, return_witness).into()) } @@ -106,7 +106,8 @@ async fn execute_program_with_native_type_return( .map_err(|_| JsExecutionError::new( "Failed to deserialize circuit. This is likely due to differing serialization formats between ACVM_JS and your compiler".to_string(), None, - None))?; + None, + None))?; execute_program_with_native_program_and_return(&program, initial_witness, foreign_call_executor) .await @@ -205,6 +206,14 @@ impl<'a, B: BlackBoxFunctionSolver> ProgramExecutor<'a, B> { } _ => None, }; + + let brillig_function_id = match &error { + OpcodeResolutionError::BrilligFunctionFailed { + function_id, .. + } => Some(*function_id), + _ => None, + }; + // If the failed opcode has an assertion message, integrate it into the error message for backwards compatibility. // Otherwise, pass the raw assertion payload as is. let (message, raw_assertion_payload) = match error { @@ -230,6 +239,7 @@ impl<'a, B: BlackBoxFunctionSolver> ProgramExecutor<'a, B> { message, call_stack, raw_assertion_payload, + brillig_function_id, ) .into()); } @@ -253,7 +263,7 @@ impl<'a, B: BlackBoxFunctionSolver> ProgramExecutor<'a, B> { call_resolved_outputs.push(*return_value); } else { // TODO: look at changing this call stack from None - return Err(JsExecutionError::new(format!("Failed to read from solved witness of ACIR call at witness {}", return_witness_index), None, None).into()); + return Err(JsExecutionError::new(format!("Failed to read from solved witness of ACIR call at witness {}", return_witness_index), None, None, None).into()); } } acvm.resolve_pending_acir_call(call_resolved_outputs); diff --git a/acvm-repo/acvm_js/src/js_execution_error.rs b/acvm-repo/acvm_js/src/js_execution_error.rs index e51a912a63a..23da88247fc 100644 --- a/acvm-repo/acvm_js/src/js_execution_error.rs +++ b/acvm-repo/acvm_js/src/js_execution_error.rs @@ -1,5 +1,5 @@ use acvm::{ - acir::circuit::{OpcodeLocation, RawAssertionPayload}, + acir::circuit::{brillig::BrilligFunctionId, OpcodeLocation, RawAssertionPayload}, FieldElement, }; use gloo_utils::format::JsValueSerdeExt; @@ -12,9 +12,11 @@ export type RawAssertionPayload = { selector: string; data: string[]; }; + export type ExecutionError = Error & { callStack?: string[]; rawAssertionPayload?: RawAssertionPayload; + brilligFunctionId?: number; }; "#; @@ -38,6 +40,7 @@ impl JsExecutionError { message: String, call_stack: Option>, assertion_payload: Option>, + brillig_function_id: Option, ) -> Self { let mut error = JsExecutionError::constructor(JsString::from(message)); let js_call_stack = match call_stack { @@ -56,8 +59,17 @@ impl JsExecutionError { None => JsValue::UNDEFINED, }; + let brillig_function_id = match brillig_function_id { + Some(function_id) => { + ::from_serde(&function_id) + .expect("Cannot serialize Brillig function id") + } + None => JsValue::UNDEFINED, + }; + error.set_property("callStack", js_call_stack); error.set_property("rawAssertionPayload", assertion_payload); + error.set_property("brilligFunctionId", brillig_function_id); error } diff --git a/aztec_macros/src/transforms/note_interface.rs b/aztec_macros/src/transforms/note_interface.rs index 6fccded45ef..7f5c0e8b48b 100644 --- a/aztec_macros/src/transforms/note_interface.rs +++ b/aztec_macros/src/transforms/note_interface.rs @@ -202,13 +202,13 @@ pub fn generate_note_interface_impl( trait_impl.items.push(TraitImplItem::Function(get_note_type_id_fn)); } - if !check_trait_method_implemented(trait_impl, "compute_note_content_hash") { - let compute_note_content_hash_fn = generate_compute_note_content_hash( + if !check_trait_method_implemented(trait_impl, "compute_note_hiding_point") { + let compute_note_hiding_point_fn = generate_compute_note_hiding_point( ¬e_type, note_interface_impl_span, empty_spans, )?; - trait_impl.items.push(TraitImplItem::Function(compute_note_content_hash_fn)); + trait_impl.items.push(TraitImplItem::Function(compute_note_hiding_point_fn)); } if !check_trait_method_implemented(trait_impl, "to_be_bytes") { @@ -512,29 +512,35 @@ fn generate_note_properties_fn( Ok(noir_fn) } -// Automatically generate the method to compute the note's content hash as: -// fn compute_note_content_hash(self: NoteType) -> Field { -// aztec::hash::pedersen_hash(self.serialize_content(), aztec::protocol_types::constants::GENERATOR_INDEX__NOTE_CONTENT_HASH) +// Automatically generate the method to compute the note's hiding point as: +// fn compute_note_hiding_point(self: NoteType) -> Point { +// aztec::hash::pedersen_commitment(self.serialize_content(), aztec::protocol_types::constants::GENERATOR_INDEX__NOTE_HIDING_POINT) // } // -fn generate_compute_note_content_hash( +fn generate_compute_note_hiding_point( note_type: &String, impl_span: Option, empty_spans: bool, ) -> Result { + // TODO(#7771): update this to do only 1 MSM call let function_source = format!( - " - fn compute_note_content_hash(self: {}) -> Field {{ - aztec::hash::pedersen_hash(self.serialize_content(), aztec::protocol_types::constants::GENERATOR_INDEX__NOTE_CONTENT_HASH) + r#" + fn compute_note_hiding_point(self: {}) -> aztec::protocol_types::point::Point {{ + assert(self.header.storage_slot != 0, "Storage slot must be set before computing note hiding point"); + let slot_scalar = dep::std::hash::from_field_unsafe(self.header.storage_slot); + + let point_before_slotting = aztec::hash::pedersen_commitment(self.serialize_content(), aztec::protocol_types::constants::GENERATOR_INDEX__NOTE_HIDING_POINT); + let slot_point = dep::std::embedded_curve_ops::multi_scalar_mul([dep::aztec::generators::G_slot], [slot_scalar]); + point_before_slotting + slot_point }} - ", + "#, note_type ); let (function_ast, errors) = parse_program(&function_source, empty_spans); if !errors.is_empty() { dbg!(errors); return Err(AztecMacroError::CouldNotImplementNoteInterface { - secondary_message: Some("Failed to parse Noir macro code (fn compute_note_content_hash). This is either a bug in the compiler or the Noir macro code".to_string()), + secondary_message: Some("Failed to parse Noir macro code (fn compute_note_hiding_point). This is either a bug in the compiler or the Noir macro code".to_string()), span: impl_span }); } diff --git a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs index 4487187de3b..76ab613ed1f 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs @@ -943,7 +943,7 @@ impl<'block> BrilligBlock<'block> { if let Some((index_register, value_variable)) = opt_index_and_value { // Then set the value in the newly created array - self.store_variable_in_array( + self.brillig_context.codegen_store_variable_in_array( destination_pointer, SingleAddrVariable::new_usize(index_register), value_variable, @@ -954,47 +954,6 @@ impl<'block> BrilligBlock<'block> { source_size_as_register } - pub(crate) fn store_variable_in_array_with_ctx( - ctx: &mut BrilligContext, - destination_pointer: MemoryAddress, - index_register: SingleAddrVariable, - value_variable: BrilligVariable, - ) { - match value_variable { - BrilligVariable::SingleAddr(value_variable) => { - ctx.codegen_array_set(destination_pointer, index_register, value_variable.address); - } - BrilligVariable::BrilligArray(_) => { - let reference: MemoryAddress = ctx.allocate_register(); - ctx.codegen_allocate_array_reference(reference); - ctx.codegen_store_variable(reference, value_variable); - ctx.codegen_array_set(destination_pointer, index_register, reference); - ctx.deallocate_register(reference); - } - BrilligVariable::BrilligVector(_) => { - let reference = ctx.allocate_register(); - ctx.codegen_allocate_vector_reference(reference); - ctx.codegen_store_variable(reference, value_variable); - ctx.codegen_array_set(destination_pointer, index_register, reference); - ctx.deallocate_register(reference); - } - } - } - - pub(crate) fn store_variable_in_array( - &mut self, - destination_pointer: MemoryAddress, - index_variable: SingleAddrVariable, - value_variable: BrilligVariable, - ) { - Self::store_variable_in_array_with_ctx( - self.brillig_context, - destination_pointer, - index_variable, - value_variable, - ); - } - /// Convert the SSA slice operations to brillig slice operations fn convert_ssa_slice_intrinsic_call( &mut self, @@ -1753,10 +1712,19 @@ impl<'block> BrilligBlock<'block> { subitem_to_repeat_variables.push(self.convert_ssa_value(subitem_id, dfg)); } - let data_length_variable = self + // Initialize loop bound with the array length + let end_pointer_variable = self .brillig_context .make_usize_constant_instruction((item_count * item_types.len()).into()); + // Add the pointer to the array length + self.brillig_context.memory_op_instruction( + end_pointer_variable.address, + pointer, + end_pointer_variable.address, + BrilligBinaryOp::Add, + ); + // If this is an array with complex subitems, we need a custom step in the loop to write all the subitems while iterating. if item_types.len() > 1 { let step_variable = @@ -1765,33 +1733,54 @@ impl<'block> BrilligBlock<'block> { let subitem_pointer = SingleAddrVariable::new_usize(self.brillig_context.allocate_register()); - let initializer_fn = |ctx: &mut BrilligContext<_>, iterator: SingleAddrVariable| { - ctx.mov_instruction(subitem_pointer.address, iterator.address); - for subitem in subitem_to_repeat_variables.into_iter() { - Self::store_variable_in_array_with_ctx(ctx, pointer, subitem_pointer, subitem); - ctx.codegen_usize_op_in_place(subitem_pointer.address, BrilligBinaryOp::Add, 1); - } - }; + let one = self.brillig_context.make_usize_constant_instruction(1_usize.into()); + + // Initializes a single subitem + let initializer_fn = + |ctx: &mut BrilligContext<_>, subitem_start_pointer: SingleAddrVariable| { + ctx.mov_instruction(subitem_pointer.address, subitem_start_pointer.address); + for (subitem_index, subitem) in + subitem_to_repeat_variables.into_iter().enumerate() + { + ctx.codegen_store_variable_in_pointer(subitem_pointer.address, subitem); + if subitem_index != item_types.len() - 1 { + ctx.memory_op_instruction( + subitem_pointer.address, + one.address, + subitem_pointer.address, + BrilligBinaryOp::Add, + ); + } + } + }; - self.brillig_context.codegen_loop_with_bound_and_step( - data_length_variable.address, - step_variable.address, + // for (let subitem_start_pointer = pointer; subitem_start_pointer < pointer + data_length; subitem_start_pointer += step) { initializer_fn(iterator) } + self.brillig_context.codegen_for_loop( + Some(pointer), + end_pointer_variable.address, + Some(step_variable.address), initializer_fn, ); self.brillig_context.deallocate_single_addr(step_variable); self.brillig_context.deallocate_single_addr(subitem_pointer); + self.brillig_context.deallocate_single_addr(one); } else { let subitem = subitem_to_repeat_variables.into_iter().next().unwrap(); - let initializer_fn = |ctx: &mut _, iterator_register| { - Self::store_variable_in_array_with_ctx(ctx, pointer, iterator_register, subitem); + let initializer_fn = |ctx: &mut BrilligContext<_>, item_pointer: SingleAddrVariable| { + ctx.codegen_store_variable_in_pointer(item_pointer.address, subitem); }; - self.brillig_context.codegen_loop(data_length_variable.address, initializer_fn); + // for (let item_pointer = pointer; item_pointer < pointer + data_length; item_pointer += 1) { initializer_fn(iterator) } + self.brillig_context.codegen_for_loop( + Some(pointer), + end_pointer_variable.address, + None, + initializer_fn, + ); } - - self.brillig_context.deallocate_single_addr(data_length_variable); + self.brillig_context.deallocate_single_addr(end_pointer_variable); } fn initialize_constant_array_comptime( @@ -1801,22 +1790,29 @@ impl<'block> BrilligBlock<'block> { pointer: MemoryAddress, ) { // Allocate a register for the iterator - let iterator_register = - self.brillig_context.make_usize_constant_instruction(0_usize.into()); + let write_pointer_register = self.brillig_context.allocate_register(); + let one = self.brillig_context.make_usize_constant_instruction(1_usize.into()); - for element_id in data.iter() { + self.brillig_context.mov_instruction(write_pointer_register, pointer); + + for (element_idx, element_id) in data.iter().enumerate() { let element_variable = self.convert_ssa_value(*element_id, dfg); // Store the item in memory - self.store_variable_in_array(pointer, iterator_register, element_variable); - // Increment the iterator - self.brillig_context.codegen_usize_op_in_place( - iterator_register.address, - BrilligBinaryOp::Add, - 1, - ); + self.brillig_context + .codegen_store_variable_in_pointer(write_pointer_register, element_variable); + if element_idx != data.len() - 1 { + // Increment the write_pointer_register + self.brillig_context.memory_op_instruction( + write_pointer_register, + one.address, + write_pointer_register, + BrilligBinaryOp::Add, + ); + } } - self.brillig_context.deallocate_single_addr(iterator_register); + self.brillig_context.deallocate_register(write_pointer_register); + self.brillig_context.deallocate_single_addr(one); } /// Converts an SSA `ValueId` into a `MemoryAddress`. Initializes if necessary. @@ -1901,7 +1897,7 @@ impl<'block> BrilligBlock<'block> { let inner_array = self.allocate_nested_array(element_type, None); let idx = self.brillig_context.make_usize_constant_instruction(index.into()); - self.store_variable_in_array(array.pointer, idx, inner_array); + self.brillig_context.codegen_store_variable_in_array(array.pointer, idx, inner_array); } Type::Slice(_) => unreachable!("ICE: unsupported slice type in allocate_nested_array(), expects an array or a numeric type"), _ => (), diff --git a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_slice_ops.rs b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_slice_ops.rs index d17b15a13b5..55679432b1f 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_slice_ops.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_slice_ops.rs @@ -38,7 +38,11 @@ impl<'block> BrilligBlock<'block> { target_index.address, BrilligBinaryOp::Add, ); - self.store_variable_in_array(target_vector.pointer, target_index, *variable); + self.brillig_context.codegen_store_variable_in_array( + target_vector.pointer, + target_index, + *variable, + ); self.brillig_context.deallocate_single_addr(target_index); } } @@ -79,7 +83,11 @@ impl<'block> BrilligBlock<'block> { // Then we write the items to insert at the start for (index, variable) in variables_to_insert.iter().enumerate() { let target_index = self.brillig_context.make_usize_constant_instruction(index.into()); - self.store_variable_in_array(target_vector.pointer, target_index, *variable); + self.brillig_context.codegen_store_variable_in_array( + target_vector.pointer, + target_index, + *variable, + ); self.brillig_context.deallocate_single_addr(target_index); } @@ -239,7 +247,11 @@ impl<'block> BrilligBlock<'block> { target_index.address, BrilligBinaryOp::Add, ); - self.store_variable_in_array(target_vector.pointer, target_index, *variable); + self.brillig_context.codegen_store_variable_in_array( + target_vector.pointer, + target_index, + *variable, + ); self.brillig_context.deallocate_single_addr(target_index); } diff --git a/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_control_flow.rs b/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_control_flow.rs index 5741089a497..8e52cf072b4 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_control_flow.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_control_flow.rs @@ -38,15 +38,28 @@ impl BrilligContext { self.stop_instruction(); } - /// This codegen will issue a loop do for (let iterator_register = 0; i < loop_bound; i += step) + /// This codegen will issue a loop for (let iterator_register = loop_start; i < loop_bound; i += step) /// The body of the loop should be issued by the caller in the on_iteration closure. - pub(crate) fn codegen_loop_with_bound_and_step( + pub(crate) fn codegen_for_loop( &mut self, + loop_start: Option, // Defaults to zero loop_bound: MemoryAddress, - step: MemoryAddress, + step: Option, // Defaults to 1 on_iteration: impl FnOnce(&mut BrilligContext, SingleAddrVariable), ) { - let iterator_register = self.make_usize_constant_instruction(0_u128.into()); + let iterator_register = if let Some(loop_start) = loop_start { + let iterator_register = SingleAddrVariable::new_usize(self.allocate_register()); + self.mov_instruction(iterator_register.address, loop_start); + iterator_register + } else { + self.make_usize_constant_instruction(0_usize.into()) + }; + + let step_register = if let Some(step) = step { + step + } else { + self.make_usize_constant_instruction(1_usize.into()).address + }; let (loop_section, loop_label) = self.reserve_next_section_label(); self.enter_section(loop_section); @@ -76,7 +89,7 @@ impl BrilligContext { // Add step to the iterator register self.memory_op_instruction( iterator_register.address, - step, + step_register, iterator_register.address, BrilligBinaryOp::Add, ); @@ -89,18 +102,20 @@ impl BrilligContext { // Deallocate our temporary registers self.deallocate_single_addr(iterator_less_than_iterations); self.deallocate_single_addr(iterator_register); + // Only deallocate step if we allocated it + if step.is_none() { + self.deallocate_register(step_register); + } } - /// This codegen will issue a loop that will iterate iteration_count times + /// This codegen will issue a loop that will iterate from 0 to iteration_count /// The body of the loop should be issued by the caller in the on_iteration closure. pub(crate) fn codegen_loop( &mut self, iteration_count: MemoryAddress, on_iteration: impl FnOnce(&mut BrilligContext, SingleAddrVariable), ) { - let step = self.make_usize_constant_instruction(1_u128.into()); - self.codegen_loop_with_bound_and_step(iteration_count, step.address, on_iteration); - self.deallocate_single_addr(step); + self.codegen_for_loop(None, iteration_count, None, on_iteration); } /// This codegen will issue an if-then branch that will check if the condition is true diff --git a/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_memory.rs b/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_memory.rs index 81c1c3847b1..d20f736ee6d 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_memory.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_memory.rs @@ -112,6 +112,50 @@ impl BrilligContext { self.deallocate_register(index_of_element_in_memory); } + pub(crate) fn codegen_store_variable_in_array( + &mut self, + array_pointer: MemoryAddress, + index: SingleAddrVariable, + value_variable: BrilligVariable, + ) { + assert!(index.bit_size == BRILLIG_MEMORY_ADDRESSING_BIT_SIZE); + let final_pointer_register = self.allocate_register(); + self.memory_op_instruction( + array_pointer, + index.address, + final_pointer_register, + BrilligBinaryOp::Add, + ); + self.codegen_store_variable_in_pointer(final_pointer_register, value_variable); + self.deallocate_register(final_pointer_register); + } + + pub(crate) fn codegen_store_variable_in_pointer( + &mut self, + destination_pointer: MemoryAddress, + value_variable: BrilligVariable, + ) { + match value_variable { + BrilligVariable::SingleAddr(value_variable) => { + self.store_instruction(destination_pointer, value_variable.address); + } + BrilligVariable::BrilligArray(_) => { + let reference: MemoryAddress = self.allocate_register(); + self.codegen_allocate_array_reference(reference); + self.codegen_store_variable(reference, value_variable); + self.store_instruction(destination_pointer, reference); + self.deallocate_register(reference); + } + BrilligVariable::BrilligVector(_) => { + let reference = self.allocate_register(); + self.codegen_allocate_vector_reference(reference); + self.codegen_store_variable(reference, value_variable); + self.store_instruction(destination_pointer, reference); + self.deallocate_register(reference); + } + } + } + /// Copies the values of an array pointed by source with length stored in `num_elements_register` /// Into the array pointed by destination pub(crate) fn codegen_copy_array( diff --git a/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs b/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs index 3e855ecbf9b..22b13aeae79 100644 --- a/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs @@ -1732,10 +1732,10 @@ impl<'a> Context<'a> { { databus = BlockType::ReturnData; } - for array_id in self.data_bus.call_data_array() { + for (call_data_id, array_id) in self.data_bus.call_data_array() { if self.block_id(&array_id) == array { assert!(databus == BlockType::Memory); - databus = BlockType::CallData; + databus = BlockType::CallData(call_data_id); break; } } diff --git a/compiler/noirc_evaluator/src/ssa/function_builder/data_bus.rs b/compiler/noirc_evaluator/src/ssa/function_builder/data_bus.rs index 0cdeed6153c..9f964cf048d 100644 --- a/compiler/noirc_evaluator/src/ssa/function_builder/data_bus.rs +++ b/compiler/noirc_evaluator/src/ssa/function_builder/data_bus.rs @@ -10,7 +10,7 @@ use serde::{Deserialize, Serialize}; use super::FunctionBuilder; -#[derive(Clone)] +#[derive(Clone, Debug, Eq, PartialEq)] pub(crate) enum DatabusVisibility { None, CallData(u32), @@ -18,11 +18,13 @@ pub(crate) enum DatabusVisibility { } /// Used to create a data bus, which is an array of private inputs /// replacing public inputs +#[derive(Clone, Debug)] pub(crate) struct DataBusBuilder { pub(crate) values: im::Vector, index: usize, pub(crate) map: HashMap, pub(crate) databus: Option, + call_data_id: Option, } impl DataBusBuilder { @@ -32,10 +34,11 @@ impl DataBusBuilder { map: HashMap::default(), databus: None, values: im::Vector::new(), + call_data_id: None, } } - /// Generates a vector telling which (ssa) parameters from the given function signature + /// Generates a vector telling which flattened parameters from the given function signature /// are tagged with databus visibility pub(crate) fn is_databus(main_signature: &FunctionSignature) -> Vec { let mut params_is_databus = Vec::new(); @@ -55,6 +58,8 @@ impl DataBusBuilder { #[derive(Clone, Debug, Serialize, Deserialize)] pub(crate) struct CallData { + /// The id to this calldata assigned by the user + pub(crate) call_data_id: u32, pub(crate) array_id: ValueId, pub(crate) index_map: HashMap, } @@ -76,14 +81,18 @@ impl DataBus { for (k, v) in cd.index_map.iter() { call_data_map.insert(f(*k), *v); } - CallData { array_id: f(cd.array_id), index_map: call_data_map } + CallData { + array_id: f(cd.array_id), + index_map: call_data_map, + call_data_id: cd.call_data_id, + } }) .collect(); DataBus { call_data, return_data: self.return_data.map(&mut f) } } - pub(crate) fn call_data_array(&self) -> Vec { - self.call_data.iter().map(|cd| cd.array_id).collect() + pub(crate) fn call_data_array(&self) -> Vec<(u32, ValueId)> { + self.call_data.iter().map(|cd| (cd.call_data_id, cd.array_id)).collect() } /// Construct a databus from call_data and return_data data bus builders pub(crate) fn get_data_bus( @@ -92,9 +101,10 @@ impl DataBus { ) -> DataBus { let mut call_data_args = Vec::new(); for call_data_item in call_data { - if let Some(array_id) = call_data_item.databus { - call_data_args.push(CallData { array_id, index_map: call_data_item.map }); - } + let array_id = call_data_item.databus.expect("Call data should have an array id"); + let call_data_id = + call_data_item.call_data_id.expect("Call data should have a user id"); + call_data_args.push(CallData { array_id, call_data_id, index_map: call_data_item.map }); } DataBus { call_data: call_data_args, return_data: return_data.databus } @@ -137,6 +147,7 @@ impl FunctionBuilder { &mut self, values: &[ValueId], mut databus: DataBusBuilder, + call_data_id: Option, ) -> DataBusBuilder { for value in values { self.add_to_data_bus(*value, &mut databus); @@ -151,18 +162,29 @@ impl FunctionBuilder { None }; - DataBusBuilder { index: 0, map: databus.map, databus: array, values: im::Vector::new() } + DataBusBuilder { + index: 0, + map: databus.map, + databus: array, + values: im::Vector::new(), + call_data_id, + } } /// Generate the data bus for call-data, based on the parameters of the entry block /// and a vector telling which ones are call-data pub(crate) fn call_data_bus( &mut self, - is_params_databus: Vec, + flattened_databus_visibilities: Vec, ) -> Vec { //filter parameters of the first block that have call-data visibility let first_block = self.current_function.entry_block(); let params = self.current_function.dfg[first_block].parameters(); + + // Reshape the is_params_databus to map to the SSA-level parameters + let is_params_databus = + self.deflatten_databus_visibilities(params, flattened_databus_visibilities); + let mut databus_param: BTreeMap> = BTreeMap::new(); for (param, databus_attribute) in params.iter().zip(is_params_databus) { match databus_attribute { @@ -182,9 +204,45 @@ impl FunctionBuilder { let mut result = Vec::new(); for id in databus_param.keys() { let builder = DataBusBuilder::new(); - let call_databus = self.initialize_data_bus(&databus_param[id], builder); + let call_databus = self.initialize_data_bus(&databus_param[id], builder, Some(*id)); result.push(call_databus); } result } + + /// This function takes the flattened databus visibilities and generates the databus visibility for each ssa parameter + /// asserting that an ssa parameter is not assigned two different databus visibilities + fn deflatten_databus_visibilities( + &self, + ssa_params: &[ValueId], + flattened_params_databus_visibility: Vec, + ) -> Vec { + // To do so, create a vec the size of the flattened arguments where the items are the ssa param index they correspond to + let ssa_param_indices: Vec<_> = ssa_params + .iter() + .enumerate() + .flat_map(|(ssa_param_index, ssa_param)| { + let flattened_size = + self.current_function.dfg[*ssa_param].get_type().flattened_size(); + std::iter::repeat(ssa_param_index).take(flattened_size) + }) + .collect(); + + let mut is_ssa_params_databus = Vec::with_capacity(ssa_params.len()); + assert!(flattened_params_databus_visibility.len() == ssa_param_indices.len()); + for (databus_visibility, ssa_index) in + flattened_params_databus_visibility.into_iter().zip(ssa_param_indices) + { + if let Some(previous_databus_visibility) = is_ssa_params_databus.get(ssa_index) { + assert!( + *previous_databus_visibility == databus_visibility, + "inconsistent databus visibility for ssa param" + ); + } else { + assert!(ssa_index == is_ssa_params_databus.len()); + is_ssa_params_databus.push(databus_visibility); + } + } + is_ssa_params_databus + } } diff --git a/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs b/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs index 468a8573307..1373d1bc46e 100644 --- a/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs @@ -83,8 +83,11 @@ pub(crate) fn generate_ssa( _ => unreachable!("ICE - expect return on the last block"), }; - return_data = - function_context.builder.initialize_data_bus(&return_data_values, return_data); + return_data = function_context.builder.initialize_data_bus( + &return_data_values, + return_data, + None, + ); } let return_instruction = function_context.builder.current_function.dfg[block].unwrap_terminator_mut(); diff --git a/compiler/wasm/src/types/noir_artifact.ts b/compiler/wasm/src/types/noir_artifact.ts index 6ecc3ccd56f..b7b42186c1a 100644 --- a/compiler/wasm/src/types/noir_artifact.ts +++ b/compiler/wasm/src/types/noir_artifact.ts @@ -141,6 +141,10 @@ export interface SourceCodeLocation { */ export type OpcodeLocation = string; +export type BrilligFunctionId = number; + +export type OpcodeToLocationsMap = Record; + /** * The debug information for a given function. */ @@ -148,7 +152,11 @@ export interface DebugInfo { /** * A map of the opcode location to the source code location. */ - locations: Record; + locations: OpcodeToLocationsMap; + /** + * For each Brillig function, we have a map of the opcode location to the source code location. + */ + brillig_locations: Record; } /** diff --git a/noir_stdlib/src/hash/mod.nr b/noir_stdlib/src/hash/mod.nr index 84ad7c22bb3..88dbf83e5bd 100644 --- a/noir_stdlib/src/hash/mod.nr +++ b/noir_stdlib/src/hash/mod.nr @@ -51,7 +51,7 @@ fn pedersen_commitment_with_separator(input: [Field; N], separator: fn pedersen_commitment_with_separator_noir(input: [Field; N], separator: u32) -> EmbeddedCurvePoint { let mut points = [EmbeddedCurveScalar { lo: 0, hi: 0 }; N]; for i in 0..N { - // we use the unsafe version because the multi_scalar_mul will constraint the scalars. + // we use the unsafe version because the multi_scalar_mul will constrain the scalars. points[i] = from_field_unsafe(input[i]); } let generators = derive_generators("DEFAULT_DOMAIN_SEPARATOR".as_bytes(), separator); diff --git a/scripts/install_bb.sh b/scripts/install_bb.sh index 65a449be543..91b9180f138 100755 --- a/scripts/install_bb.sh +++ b/scripts/install_bb.sh @@ -1,6 +1,6 @@ #!/bin/bash -VERSION="0.47.1" +VERSION="0.48.0" BBUP_PATH=~/.bb/bbup diff --git a/test_programs/execution_success/databus_two_calldata/Nargo.toml b/test_programs/execution_success/databus_two_calldata/Nargo.toml new file mode 100644 index 00000000000..15d4b01ac44 --- /dev/null +++ b/test_programs/execution_success/databus_two_calldata/Nargo.toml @@ -0,0 +1,6 @@ +[package] +name = "databus_two_calldata" +type = "bin" +authors = [""] + +[dependencies] diff --git a/test_programs/execution_success/databus_two_calldata/Prover.toml b/test_programs/execution_success/databus_two_calldata/Prover.toml new file mode 100644 index 00000000000..1229857d3f5 --- /dev/null +++ b/test_programs/execution_success/databus_two_calldata/Prover.toml @@ -0,0 +1,3 @@ +x = [0,1,2,3] +y = [0,2,4] +z = [1,3,5,7] diff --git a/test_programs/execution_success/databus_two_calldata/src/main.nr b/test_programs/execution_success/databus_two_calldata/src/main.nr new file mode 100644 index 00000000000..75df2a0953c --- /dev/null +++ b/test_programs/execution_success/databus_two_calldata/src/main.nr @@ -0,0 +1,11 @@ +// An simple program demonstrating two calldata array inputs and a single return data array. As an arbitrary example, +// the return data is computed as a linear combination of the calldata. +fn main(mut x: [u32; 4], y: call_data(0) [u32; 3], z: call_data(1) [u32; 4]) -> return_data [u32; 4] { + let mut result = [0; 4]; + for i in 0..3 { + let idx = x[i]; + result[idx] = y[idx] + z[idx]; + } + result[x[3]] = z[x[3]]; + result +} diff --git a/test_programs/execution_success/databus_two_calldata_simple/Nargo.toml b/test_programs/execution_success/databus_two_calldata_simple/Nargo.toml new file mode 100644 index 00000000000..5104029c08e --- /dev/null +++ b/test_programs/execution_success/databus_two_calldata_simple/Nargo.toml @@ -0,0 +1,6 @@ +[package] +name = "databus_two_calldata_simple" +type = "bin" +authors = [""] + +[dependencies] diff --git a/test_programs/execution_success/databus_two_calldata_simple/Prover.toml b/test_programs/execution_success/databus_two_calldata_simple/Prover.toml new file mode 100644 index 00000000000..58257d1fe14 --- /dev/null +++ b/test_programs/execution_success/databus_two_calldata_simple/Prover.toml @@ -0,0 +1,3 @@ +idx = "1" +y = [7, 9] +z = [1,2,3,4] diff --git a/test_programs/execution_success/databus_two_calldata_simple/src/main.nr b/test_programs/execution_success/databus_two_calldata_simple/src/main.nr new file mode 100644 index 00000000000..2477f0006c8 --- /dev/null +++ b/test_programs/execution_success/databus_two_calldata_simple/src/main.nr @@ -0,0 +1,5 @@ +fn main(mut idx: u32, y: call_data(0) [u32; 2], z: call_data(1) [u32; 4]) -> return_data u32 { + let a = y[idx]; + let b = z[idx]; + a + b +} diff --git a/tooling/acvm_cli/src/cli/execute_cmd.rs b/tooling/acvm_cli/src/cli/execute_cmd.rs index ac3af03684f..c453936568c 100644 --- a/tooling/acvm_cli/src/cli/execute_cmd.rs +++ b/tooling/acvm_cli/src/cli/execute_cmd.rs @@ -39,7 +39,7 @@ pub(crate) struct ExecuteCommand { fn run_command(args: ExecuteCommand) -> Result { let bytecode = read_bytecode_from_file(&args.working_directory, &args.bytecode)?; let circuit_inputs = read_inputs_from_file(&args.working_directory, &args.input_witness)?; - let output_witness = execute_program_from_witness(circuit_inputs, &bytecode, None)?; + let output_witness = execute_program_from_witness(circuit_inputs, &bytecode)?; assert_eq!(output_witness.length(), 1, "ACVM CLI only supports a witness stack of size 1"); let output_witness_string = create_output_witness_string( &output_witness.peek().expect("Should have a witness stack item").witness, @@ -66,7 +66,6 @@ pub(crate) fn run(args: ExecuteCommand) -> Result { pub(crate) fn execute_program_from_witness( inputs_map: WitnessMap, bytecode: &[u8], - foreign_call_resolver_url: Option<&str>, ) -> Result, CliError> { let program: Program = Program::deserialize_program(bytecode) .map_err(|_| CliError::CircuitDeserializationError())?; @@ -74,7 +73,7 @@ pub(crate) fn execute_program_from_witness( &program, inputs_map, &Bn254BlackBoxSolver, - &mut DefaultForeignCallExecutor::new(true, foreign_call_resolver_url), + &mut DefaultForeignCallExecutor::new(true, None, None, None), ) .map_err(CliError::CircuitExecutionError) } diff --git a/tooling/debugger/src/foreign_calls.rs b/tooling/debugger/src/foreign_calls.rs index 62443d4065c..6a773a4b348 100644 --- a/tooling/debugger/src/foreign_calls.rs +++ b/tooling/debugger/src/foreign_calls.rs @@ -49,7 +49,7 @@ pub struct DefaultDebugForeignCallExecutor { impl DefaultDebugForeignCallExecutor { pub fn new(show_output: bool) -> Self { Self { - executor: DefaultForeignCallExecutor::new(show_output, None), + executor: DefaultForeignCallExecutor::new(show_output, None, None, None), debug_vars: DebugVars::default(), } } diff --git a/tooling/lsp/src/requests/test_run.rs b/tooling/lsp/src/requests/test_run.rs index fc4054633e2..5f4f9cd98d0 100644 --- a/tooling/lsp/src/requests/test_run.rs +++ b/tooling/lsp/src/requests/test_run.rs @@ -88,6 +88,8 @@ fn on_test_run_request_inner( &test_function, false, None, + Some(workspace.root_dir.clone()), + Some(package.name.to_string()), &CompileOptions::default(), ); let result = match test_result { diff --git a/tooling/nargo/src/errors.rs b/tooling/nargo/src/errors.rs index 86bb767b9fc..f9668653d0b 100644 --- a/tooling/nargo/src/errors.rs +++ b/tooling/nargo/src/errors.rs @@ -64,31 +64,25 @@ impl NargoError { &self, error_types: &BTreeMap, ) -> Option { - let execution_error = match self { - NargoError::ExecutionError(error) => error, - _ => return None, - }; - - match execution_error { - ExecutionError::AssertionFailed(payload, _) => match payload { - ResolvedAssertionPayload::String(message) => Some(message.to_string()), - ResolvedAssertionPayload::Raw(raw) => { - let abi_type = error_types.get(&raw.selector)?; - let decoded = display_abi_error(&raw.data, abi_type.clone()); - Some(decoded.to_string()) - } - }, - ExecutionError::SolvingError(error, _) => match error { - OpcodeResolutionError::IndexOutOfBounds { .. } - | OpcodeResolutionError::OpcodeNotSolvable(_) - | OpcodeResolutionError::UnsatisfiedConstrain { .. } - | OpcodeResolutionError::AcirMainCallAttempted { .. } - | OpcodeResolutionError::BrilligFunctionFailed { .. } - | OpcodeResolutionError::AcirCallOutputsMismatch { .. } => None, - OpcodeResolutionError::BlackBoxFunctionFailed(_, reason) => { - Some(reason.to_string()) - } + match self { + NargoError::ExecutionError(error) => match error { + ExecutionError::AssertionFailed(payload, _) => match payload { + ResolvedAssertionPayload::String(message) => Some(message.to_string()), + ResolvedAssertionPayload::Raw(raw) => { + let abi_type = error_types.get(&raw.selector)?; + let decoded = display_abi_error(&raw.data, abi_type.clone()); + Some(decoded.to_string()) + } + }, + ExecutionError::SolvingError(error, _) => match error { + OpcodeResolutionError::BlackBoxFunctionFailed(_, reason) => { + Some(reason.to_string()) + } + _ => None, + }, }, + NargoError::ForeignCallError(error) => Some(error.to_string()), + _ => None, } } } diff --git a/tooling/nargo/src/ops/foreign_calls.rs b/tooling/nargo/src/ops/foreign_calls.rs index 987c7dd9cb9..30785949a46 100644 --- a/tooling/nargo/src/ops/foreign_calls.rs +++ b/tooling/nargo/src/ops/foreign_calls.rs @@ -1,3 +1,5 @@ +use std::path::PathBuf; + use acvm::{ acir::brillig::{ForeignCallParam, ForeignCallResult}, pwg::ForeignCallWaitInfo, @@ -112,6 +114,10 @@ pub struct DefaultForeignCallExecutor { show_output: bool, /// JSON RPC client to resolve foreign calls external_resolver: Option, + /// Root path to the program or workspace in execution. + root_path: Option, + /// Name of the package in execution + package_name: Option, } #[derive(Debug, Serialize, Deserialize)] @@ -126,10 +132,22 @@ struct ResolveForeignCallRequest { #[serde(flatten)] /// The foreign call which the external RPC server is to provide a response for. function_call: ForeignCallWaitInfo, + + #[serde(skip_serializing_if = "Option::is_none")] + /// Root path to the program or workspace in execution. + root_path: Option, + #[serde(skip_serializing_if = "Option::is_none")] + /// Name of the package in execution + package_name: Option, } impl DefaultForeignCallExecutor { - pub fn new(show_output: bool, resolver_url: Option<&str>) -> Self { + pub fn new( + show_output: bool, + resolver_url: Option<&str>, + root_path: Option, + package_name: Option, + ) -> Self { let oracle_resolver = resolver_url.map(|resolver_url| { let mut transport_builder = Builder::new().url(resolver_url).expect("Invalid oracle resolver URL"); @@ -148,6 +166,8 @@ impl DefaultForeignCallExecutor { id: rand::thread_rng().gen(), mocked_responses: Vec::new(), last_mock_id: 0, + root_path, + package_name, } } } @@ -302,6 +322,11 @@ impl Deserialize<'a>> ForeignCallExecutor let encoded_params = vec![build_json_rpc_arg(ResolveForeignCallRequest { session_id: self.id, function_call: foreign_call.clone(), + root_path: self + .root_path + .clone() + .map(|path| path.to_str().unwrap().to_string()), + package_name: self.package_name.clone(), })]; let req = @@ -402,7 +427,8 @@ mod tests { fn test_oracle_resolver_echo() { let (server, url) = build_oracle_server(); - let mut executor = DefaultForeignCallExecutor::::new(false, Some(&url)); + let mut executor = + DefaultForeignCallExecutor::::new(false, Some(&url), None, None); let foreign_call = ForeignCallWaitInfo { function: "echo".to_string(), @@ -419,7 +445,7 @@ mod tests { fn test_oracle_resolver_sum() { let (server, url) = build_oracle_server(); - let mut executor = DefaultForeignCallExecutor::new(false, Some(&url)); + let mut executor = DefaultForeignCallExecutor::new(false, Some(&url), None, None); let foreign_call = ForeignCallWaitInfo { function: "sum".to_string(), @@ -436,7 +462,8 @@ mod tests { fn foreign_call_executor_id_is_persistent() { let (server, url) = build_oracle_server(); - let mut executor = DefaultForeignCallExecutor::::new(false, Some(&url)); + let mut executor = + DefaultForeignCallExecutor::::new(false, Some(&url), None, None); let foreign_call = ForeignCallWaitInfo { function: "id".to_string(), inputs: Vec::new() }; @@ -451,8 +478,10 @@ mod tests { fn oracle_resolver_rpc_can_distinguish_executors() { let (server, url) = build_oracle_server(); - let mut executor_1 = DefaultForeignCallExecutor::::new(false, Some(&url)); - let mut executor_2 = DefaultForeignCallExecutor::::new(false, Some(&url)); + let mut executor_1 = + DefaultForeignCallExecutor::::new(false, Some(&url), None, None); + let mut executor_2 = + DefaultForeignCallExecutor::::new(false, Some(&url), None, None); let foreign_call = ForeignCallWaitInfo { function: "id".to_string(), inputs: Vec::new() }; diff --git a/tooling/nargo/src/ops/test.rs b/tooling/nargo/src/ops/test.rs index 0faae5ff506..efe648e09b0 100644 --- a/tooling/nargo/src/ops/test.rs +++ b/tooling/nargo/src/ops/test.rs @@ -1,3 +1,5 @@ +use std::path::PathBuf; + use acvm::{ acir::native_types::{WitnessMap, WitnessStack}, BlackBoxFunctionSolver, FieldElement, @@ -23,12 +25,15 @@ impl TestStatus { } } +#[allow(clippy::too_many_arguments)] pub fn run_test>( blackbox_solver: &B, context: &mut Context, test_function: &TestFunction, show_output: bool, foreign_call_resolver_url: Option<&str>, + root_path: Option, + package_name: Option, config: &CompileOptions, ) -> TestStatus { let test_function_has_no_arguments = context @@ -47,7 +52,12 @@ pub fn run_test>( &compiled_program.program, WitnessMap::new(), blackbox_solver, - &mut DefaultForeignCallExecutor::new(show_output, foreign_call_resolver_url), + &mut DefaultForeignCallExecutor::new( + show_output, + foreign_call_resolver_url, + root_path, + package_name, + ), ); test_status_program_compile_pass( test_function, @@ -83,6 +93,8 @@ pub fn run_test>( &mut DefaultForeignCallExecutor::::new( false, foreign_call_resolver_url, + root_path.clone(), + package_name.clone(), ), ) .map_err(|err| err.to_string()) diff --git a/tooling/nargo_cli/src/cli/execute_cmd.rs b/tooling/nargo_cli/src/cli/execute_cmd.rs index cf9dc1141a1..0f7773d7ab7 100644 --- a/tooling/nargo_cli/src/cli/execute_cmd.rs +++ b/tooling/nargo_cli/src/cli/execute_cmd.rs @@ -1,3 +1,5 @@ +use std::path::PathBuf; + use acvm::acir::native_types::WitnessStack; use acvm::FieldElement; use bn254_blackbox_solver::Bn254BlackBoxSolver; @@ -65,13 +67,16 @@ pub(crate) fn run(args: ExecuteCommand, config: NargoConfig) -> Result<(), CliEr let binary_packages = workspace.into_iter().filter(|package| package.is_binary()); for package in binary_packages { let program_artifact_path = workspace.package_build_path(package); - let program: CompiledProgram = read_program_from_file(program_artifact_path)?.into(); + let program: CompiledProgram = + read_program_from_file(program_artifact_path.clone())?.into(); let (return_value, witness_stack) = execute_program_and_decode( program, package, &args.prover_name, args.oracle_resolver.as_deref(), + Some(workspace.root_dir.clone()), + Some(package.name.to_string()), )?; println!("[{}] Circuit witness successfully solved", package.name); @@ -92,11 +97,14 @@ fn execute_program_and_decode( package: &Package, prover_name: &str, foreign_call_resolver_url: Option<&str>, + root_path: Option, + package_name: Option, ) -> Result<(Option, WitnessStack), CliError> { // Parse the initial witness values from Prover.toml let (inputs_map, _) = read_inputs_from_file(&package.root_dir, prover_name, Format::Toml, &program.abi)?; - let witness_stack = execute_program(&program, &inputs_map, foreign_call_resolver_url)?; + let witness_stack = + execute_program(&program, &inputs_map, foreign_call_resolver_url, root_path, package_name)?; // Get the entry point witness for the ABI let main_witness = &witness_stack.peek().expect("Should have at least one witness on the stack").witness; @@ -109,6 +117,8 @@ pub(crate) fn execute_program( compiled_program: &CompiledProgram, inputs_map: &InputMap, foreign_call_resolver_url: Option<&str>, + root_path: Option, + package_name: Option, ) -> Result, CliError> { let initial_witness = compiled_program.abi.encode(inputs_map, None)?; @@ -116,7 +126,12 @@ pub(crate) fn execute_program( &compiled_program.program, initial_witness, &Bn254BlackBoxSolver, - &mut DefaultForeignCallExecutor::new(true, foreign_call_resolver_url), + &mut DefaultForeignCallExecutor::new( + true, + foreign_call_resolver_url, + root_path, + package_name, + ), ); match solved_witness_stack_err { Ok(solved_witness_stack) => Ok(solved_witness_stack), diff --git a/tooling/nargo_cli/src/cli/test_cmd.rs b/tooling/nargo_cli/src/cli/test_cmd.rs index 399b8eb5635..0d7c8fc8bf7 100644 --- a/tooling/nargo_cli/src/cli/test_cmd.rs +++ b/tooling/nargo_cli/src/cli/test_cmd.rs @@ -1,4 +1,4 @@ -use std::io::Write; +use std::{io::Write, path::PathBuf}; use acvm::{BlackBoxFunctionSolver, FieldElement}; use bn254_blackbox_solver::Bn254BlackBoxSolver; @@ -91,6 +91,8 @@ pub(crate) fn run(args: TestCommand, config: NargoConfig) -> Result<(), CliError pattern, args.show_output, args.oracle_resolver.as_deref(), + Some(workspace.root_dir.clone()), + Some(package.name.to_string()), &args.compile_options, ) }) @@ -119,6 +121,7 @@ pub(crate) fn run(args: TestCommand, config: NargoConfig) -> Result<(), CliError } } +#[allow(clippy::too_many_arguments)] fn run_tests + Default>( file_manager: &FileManager, parsed_files: &ParsedFiles, @@ -126,6 +129,8 @@ fn run_tests + Default>( fn_name: FunctionNameMatch, show_output: bool, foreign_call_resolver_url: Option<&str>, + root_path: Option, + package_name: Option, compile_options: &CompileOptions, ) -> Result, CliError> { let test_functions = @@ -146,6 +151,8 @@ fn run_tests + Default>( &test_name, show_output, foreign_call_resolver_url, + root_path.clone(), + package_name.clone(), compile_options, ); @@ -157,6 +164,7 @@ fn run_tests + Default>( Ok(test_report) } +#[allow(clippy::too_many_arguments)] fn run_test + Default>( file_manager: &FileManager, parsed_files: &ParsedFiles, @@ -164,6 +172,8 @@ fn run_test + Default>( fn_name: &str, show_output: bool, foreign_call_resolver_url: Option<&str>, + root_path: Option, + package_name: Option, compile_options: &CompileOptions, ) -> TestStatus { // This is really hacky but we can't share `Context` or `S` across threads. @@ -185,6 +195,8 @@ fn run_test + Default>( test_function, show_output, foreign_call_resolver_url, + root_path, + package_name, compile_options, ) } diff --git a/tooling/nargo_cli/tests/stdlib-tests.rs b/tooling/nargo_cli/tests/stdlib-tests.rs index e29b4a68c15..847250c1dc0 100644 --- a/tooling/nargo_cli/tests/stdlib-tests.rs +++ b/tooling/nargo_cli/tests/stdlib-tests.rs @@ -53,6 +53,8 @@ fn run_stdlib_tests() { &test_function, false, None, + Some(dummy_package.root_dir.clone()), + Some(dummy_package.name.to_string()), &CompileOptions::default(), ); (test_name, status) diff --git a/tooling/noir_js_backend_barretenberg/package.json b/tooling/noir_js_backend_barretenberg/package.json index c18f9fe2c71..661d71fb9c3 100644 --- a/tooling/noir_js_backend_barretenberg/package.json +++ b/tooling/noir_js_backend_barretenberg/package.json @@ -41,7 +41,7 @@ "lint": "NODE_NO_WARNINGS=1 eslint . --ext .ts --ignore-path ./.eslintignore --max-warnings 0" }, "dependencies": { - "@aztec/bb.js": "0.47.1", + "@aztec/bb.js": "0.48.0", "@noir-lang/types": "workspace:*", "fflate": "^0.8.0" }, diff --git a/tooling/profiler/src/cli/gates_flamegraph_cmd.rs b/tooling/profiler/src/cli/gates_flamegraph_cmd.rs index 98e89e42015..e072c63f274 100644 --- a/tooling/profiler/src/cli/gates_flamegraph_cmd.rs +++ b/tooling/profiler/src/cli/gates_flamegraph_cmd.rs @@ -1,13 +1,15 @@ use std::path::{Path, PathBuf}; +use acir::circuit::OpcodeLocation; use clap::Args; use color_eyre::eyre::{self, Context}; use noirc_artifacts::debug::DebugArtifact; -use crate::flamegraph::{FlamegraphGenerator, InfernoFlamegraphGenerator}; +use crate::flamegraph::{FlamegraphGenerator, InfernoFlamegraphGenerator, Sample}; use crate::fs::read_program_from_file; use crate::gates_provider::{BackendGatesProvider, GatesProvider}; +use crate::opcode_formatter::AcirOrBrilligOpcode; #[derive(Debug, Clone, Args)] pub(crate) struct GatesFlamegraphCommand { @@ -19,6 +21,10 @@ pub(crate) struct GatesFlamegraphCommand { #[clap(long, short)] backend_path: String, + /// Command to get a gates report from the backend. Defaults to "gates" + #[clap(long, short, default_value = "gates")] + backend_gates_command: String, + #[arg(trailing_var_arg = true, allow_hyphen_values = true)] backend_extra_args: Vec, @@ -32,6 +38,7 @@ pub(crate) fn run(args: GatesFlamegraphCommand) -> eyre::Result<()> { &PathBuf::from(args.artifact_path), &BackendGatesProvider { backend_path: PathBuf::from(args.backend_path), + gates_command: args.backend_gates_command, extra_args: args.backend_extra_args, }, &InfernoFlamegraphGenerator { count_name: "gates".to_string() }, @@ -71,9 +78,20 @@ fn run_with_provider( func_gates.circuit_size ); + let samples = func_gates + .gates_per_opcode + .into_iter() + .zip(bytecode.opcodes) + .enumerate() + .map(|(index, (gates, opcode))| Sample { + opcode: AcirOrBrilligOpcode::Acir(opcode), + call_stack: vec![OpcodeLocation::Acir(index)], + count: gates, + }) + .collect(); + flamegraph_generator.generate_flamegraph( - func_gates.gates_per_opcode, - bytecode.opcodes, + samples, &debug_artifact.debug_symbols[func_idx], &debug_artifact, artifact_path.to_str().unwrap(), @@ -87,7 +105,10 @@ fn run_with_provider( #[cfg(test)] mod tests { - use acir::circuit::{Circuit, Opcode, Program}; + use acir::{ + circuit::{Circuit, Program}, + AcirField, + }; use color_eyre::eyre::{self}; use fm::codespan_files::Files; use noirc_artifacts::program::ProgramArtifact; @@ -97,7 +118,10 @@ mod tests { path::{Path, PathBuf}, }; - use crate::gates_provider::{BackendGatesReport, BackendGatesResponse, GatesProvider}; + use crate::{ + flamegraph::Sample, + gates_provider::{BackendGatesReport, BackendGatesResponse, GatesProvider}, + }; struct TestGateProvider { mock_responses: HashMap, @@ -118,10 +142,9 @@ mod tests { struct TestFlamegraphGenerator {} impl super::FlamegraphGenerator for TestFlamegraphGenerator { - fn generate_flamegraph<'files, F>( + fn generate_flamegraph<'files, F: AcirField>( &self, - _samples_per_opcode: Vec, - _opcodes: Vec>, + _samples: Vec>, _debug_symbols: &DebugInfo, _files: &'files impl Files<'files, FileId = fm::FileId>, _artifact_name: &str, diff --git a/tooling/profiler/src/cli/mod.rs b/tooling/profiler/src/cli/mod.rs index 4c2503fbe4f..7cfc6ed7c9e 100644 --- a/tooling/profiler/src/cli/mod.rs +++ b/tooling/profiler/src/cli/mod.rs @@ -30,7 +30,4 @@ pub(crate) fn start_cli() -> eyre::Result<()> { ProfilerCommand::GatesFlamegraph(args) => gates_flamegraph_cmd::run(args), ProfilerCommand::OpcodesFlamegraph(args) => opcodes_flamegraph_cmd::run(args), } - .map_err(|err| eyre::eyre!("{}", err))?; - - Ok(()) } diff --git a/tooling/profiler/src/cli/opcodes_flamegraph_cmd.rs b/tooling/profiler/src/cli/opcodes_flamegraph_cmd.rs index e1dc1464f6f..f91e50d8716 100644 --- a/tooling/profiler/src/cli/opcodes_flamegraph_cmd.rs +++ b/tooling/profiler/src/cli/opcodes_flamegraph_cmd.rs @@ -1,12 +1,14 @@ use std::path::{Path, PathBuf}; +use acir::circuit::{Circuit, Opcode, OpcodeLocation}; use clap::Args; use color_eyre::eyre::{self, Context}; use noirc_artifacts::debug::DebugArtifact; -use crate::flamegraph::{FlamegraphGenerator, InfernoFlamegraphGenerator}; +use crate::flamegraph::{FlamegraphGenerator, InfernoFlamegraphGenerator, Sample}; use crate::fs::read_program_from_file; +use crate::opcode_formatter::AcirOrBrilligOpcode; #[derive(Debug, Clone, Args)] pub(crate) struct OpcodesFlamegraphCommand { @@ -17,6 +19,10 @@ pub(crate) struct OpcodesFlamegraphCommand { /// The output folder for the flamegraph svg files #[clap(long, short)] output: String, + + /// Wether to skip brillig functions + #[clap(long, short, action)] + skip_brillig: bool, } pub(crate) fn run(args: OpcodesFlamegraphCommand) -> eyre::Result<()> { @@ -24,6 +30,7 @@ pub(crate) fn run(args: OpcodesFlamegraphCommand) -> eyre::Result<()> { &PathBuf::from(args.artifact_path), &InfernoFlamegraphGenerator { count_name: "opcodes".to_string() }, &PathBuf::from(args.output), + args.skip_brillig, ) } @@ -31,6 +38,7 @@ fn run_with_generator( artifact_path: &Path, flamegraph_generator: &Generator, output_path: &Path, + skip_brillig: bool, ) -> eyre::Result<()> { let mut program = read_program_from_file(artifact_path).context("Error reading program from file")?; @@ -42,41 +50,116 @@ fn run_with_generator( let debug_artifact: DebugArtifact = program.into(); for (func_idx, (func_name, bytecode)) in - function_names.into_iter().zip(bytecode.functions).enumerate() + function_names.into_iter().zip(bytecode.functions.iter()).enumerate() { - println!("Opcode count: {}", bytecode.opcodes.len()); + println!("Opcode count for {}: {}", func_name, bytecode.opcodes.len()); + + let samples = bytecode + .opcodes + .iter() + .enumerate() + .map(|(index, opcode)| Sample { + opcode: AcirOrBrilligOpcode::Acir(opcode.clone()), + call_stack: vec![OpcodeLocation::Acir(index)], + count: 1, + }) + .collect(); flamegraph_generator.generate_flamegraph( - bytecode.opcodes.iter().map(|_op| 1).collect(), - bytecode.opcodes, + samples, &debug_artifact.debug_symbols[func_idx], &debug_artifact, artifact_path.to_str().unwrap(), &func_name, - &Path::new(&output_path).join(Path::new(&format!("{}_opcodes.svg", &func_name))), + &Path::new(&output_path).join(Path::new(&format!("{}_acir_opcodes.svg", &func_name))), )?; } + if !skip_brillig { + for (brillig_fn_index, brillig_bytecode) in + bytecode.unconstrained_functions.into_iter().enumerate() + { + let acir_location = locate_brillig_call(brillig_fn_index, &bytecode.functions); + let Some((acir_fn_index, acir_opcode_index)) = acir_location else { + continue; + }; + + println!( + "Opcode count for brillig_{}: {}", + brillig_fn_index, + brillig_bytecode.bytecode.len() + ); + + let samples = brillig_bytecode + .bytecode + .into_iter() + .enumerate() + .map(|(brillig_index, opcode)| Sample { + opcode: AcirOrBrilligOpcode::Brillig(opcode), + call_stack: vec![OpcodeLocation::Brillig { + acir_index: acir_opcode_index, + brillig_index, + }], + count: 1, + }) + .collect(); + + flamegraph_generator.generate_flamegraph( + samples, + &debug_artifact.debug_symbols[acir_fn_index], + &debug_artifact, + artifact_path.to_str().unwrap(), + &format!("brillig_{}", brillig_fn_index), + &Path::new(&output_path) + .join(Path::new(&format!("{}_brillig_opcodes.svg", &brillig_fn_index))), + )?; + } + } + Ok(()) } +fn locate_brillig_call( + brillig_fn_index: usize, + acir_functions: &[Circuit], +) -> Option<(usize, usize)> { + for (acir_fn_index, acir_fn) in acir_functions.iter().enumerate() { + for (acir_opcode_index, acir_opcode) in acir_fn.opcodes.iter().enumerate() { + match acir_opcode { + Opcode::BrilligCall { id, .. } if id.as_usize() == brillig_fn_index => { + return Some((acir_fn_index, acir_opcode_index)) + } + _ => {} + } + } + } + None +} + #[cfg(test)] mod tests { - use acir::circuit::{Circuit, Opcode, Program}; + use acir::{ + circuit::{ + brillig::{BrilligBytecode, BrilligFunctionId}, + Circuit, Opcode, Program, + }, + AcirField, FieldElement, + }; use color_eyre::eyre::{self}; use fm::codespan_files::Files; use noirc_artifacts::program::ProgramArtifact; use noirc_errors::debug_info::{DebugInfo, ProgramDebugInfo}; use std::{collections::BTreeMap, path::Path}; + use crate::flamegraph::Sample; + #[derive(Default)] struct TestFlamegraphGenerator {} impl super::FlamegraphGenerator for TestFlamegraphGenerator { - fn generate_flamegraph<'files, F>( + fn generate_flamegraph<'files, F: AcirField>( &self, - _samples_per_opcode: Vec, - _opcodes: Vec>, + _samples: Vec>, _debug_symbols: &DebugInfo, _files: &'files impl Files<'files, FileId = fm::FileId>, _artifact_name: &str, @@ -113,11 +196,54 @@ mod tests { let flamegraph_generator = TestFlamegraphGenerator::default(); - super::run_with_generator(&artifact_path, &flamegraph_generator, temp_dir.path()) + super::run_with_generator(&artifact_path, &flamegraph_generator, temp_dir.path(), true) .expect("should run without errors"); // Check that the output file was written to - let output_file = temp_dir.path().join("main_opcodes.svg"); + let output_file = temp_dir.path().join("main_acir_opcodes.svg"); + assert!(output_file.exists()); + } + + #[test] + fn brillig_test() { + let temp_dir = tempfile::tempdir().unwrap(); + + let artifact_path = temp_dir.path().join("test.json"); + + let acir: Vec> = vec![Opcode::BrilligCall { + id: BrilligFunctionId(0), + inputs: vec![], + outputs: vec![], + predicate: None, + }]; + + let artifact = ProgramArtifact { + noir_version: "0.0.0".to_string(), + hash: 27, + abi: noirc_abi::Abi::default(), + bytecode: Program { + functions: vec![Circuit { opcodes: acir, ..Circuit::default() }], + unconstrained_functions: vec![BrilligBytecode::default()], + }, + debug_symbols: ProgramDebugInfo { debug_infos: vec![DebugInfo::default()] }, + file_map: BTreeMap::default(), + names: vec!["main".to_string()], + }; + + // Write the artifact to a file + let artifact_file = std::fs::File::create(&artifact_path).unwrap(); + serde_json::to_writer(artifact_file, &artifact).unwrap(); + + let flamegraph_generator = TestFlamegraphGenerator::default(); + + super::run_with_generator(&artifact_path, &flamegraph_generator, temp_dir.path(), false) + .expect("should run without errors"); + + // Check that the output files ware written to + let output_file = temp_dir.path().join("main_acir_opcodes.svg"); + assert!(output_file.exists()); + + let output_file = temp_dir.path().join("0_brillig_opcodes.svg"); assert!(output_file.exists()); } } diff --git a/tooling/profiler/src/flamegraph.rs b/tooling/profiler/src/flamegraph.rs index 0fdc65d8920..da76f9b9938 100644 --- a/tooling/profiler/src/flamegraph.rs +++ b/tooling/profiler/src/flamegraph.rs @@ -1,7 +1,8 @@ use std::path::Path; use std::{collections::BTreeMap, io::BufWriter}; -use acir::circuit::{Opcode, OpcodeLocation}; +use acir::circuit::OpcodeLocation; +use acir::AcirField; use color_eyre::eyre::{self}; use fm::codespan_files::Files; use inferno::flamegraph::{from_lines, Options, TextTruncateDirection}; @@ -9,8 +10,17 @@ use noirc_errors::debug_info::DebugInfo; use noirc_errors::reporter::line_and_column_from_span; use noirc_errors::Location; +use crate::opcode_formatter::AcirOrBrilligOpcode; + use super::opcode_formatter::format_opcode; +#[derive(Debug)] +pub(crate) struct Sample { + pub(crate) opcode: AcirOrBrilligOpcode, + pub(crate) call_stack: Vec, + pub(crate) count: usize, +} + #[derive(Debug, Default)] pub(crate) struct FoldedStackItem { pub(crate) total_samples: usize, @@ -19,10 +29,9 @@ pub(crate) struct FoldedStackItem { pub(crate) trait FlamegraphGenerator { #[allow(clippy::too_many_arguments)] - fn generate_flamegraph<'files, F>( + fn generate_flamegraph<'files, F: AcirField>( &self, - samples_per_opcode: Vec, - opcodes: Vec>, + samples: Vec>, debug_symbols: &DebugInfo, files: &'files impl Files<'files, FileId = fm::FileId>, artifact_name: &str, @@ -36,19 +45,16 @@ pub(crate) struct InfernoFlamegraphGenerator { } impl FlamegraphGenerator for InfernoFlamegraphGenerator { - fn generate_flamegraph<'files, F>( + fn generate_flamegraph<'files, F: AcirField>( &self, - samples_per_opcode: Vec, - opcodes: Vec>, + samples: Vec>, debug_symbols: &DebugInfo, files: &'files impl Files<'files, FileId = fm::FileId>, artifact_name: &str, function_name: &str, output_path: &Path, ) -> eyre::Result<()> { - let folded_lines = - generate_folded_sorted_lines(samples_per_opcode, opcodes, debug_symbols, files); - + let folded_lines = generate_folded_sorted_lines(samples, debug_symbols, files); let flamegraph_file = std::fs::File::create(output_path)?; let flamegraph_writer = BufWriter::new(flamegraph_file); @@ -72,28 +78,29 @@ impl FlamegraphGenerator for InfernoFlamegraphGenerator { } } -fn generate_folded_sorted_lines<'files, F>( - samples_per_opcode: Vec, - opcodes: Vec>, +fn generate_folded_sorted_lines<'files, F: AcirField>( + samples: Vec>, debug_symbols: &DebugInfo, files: &'files impl Files<'files, FileId = fm::FileId>, ) -> Vec { // Create a nested hashmap with the stack items, folding the gates for all the callsites that are equal let mut folded_stack_items = BTreeMap::new(); - samples_per_opcode.into_iter().enumerate().for_each(|(opcode_index, gates)| { - let call_stack = debug_symbols.locations.get(&OpcodeLocation::Acir(opcode_index)); - let location_names = if let Some(call_stack) = call_stack { - call_stack - .iter() - .map(|location| location_to_callsite_label(*location, files)) - .chain(std::iter::once(format_opcode(&opcodes[opcode_index]))) - .collect::>() - } else { - vec!["unknown".to_string()] - }; + samples.into_iter().for_each(|sample| { + let mut location_names: Vec = sample + .call_stack + .into_iter() + .flat_map(|opcode_location| debug_symbols.locations.get(&opcode_location)) + .flatten() + .map(|location| location_to_callsite_label(*location, files)) + .collect(); + + if location_names.is_empty() { + location_names.push("unknown".to_string()); + } + location_names.push(format_opcode(&sample.opcode)); - add_locations_to_folded_stack_items(&mut folded_stack_items, location_names, gates); + add_locations_to_folded_stack_items(&mut folded_stack_items, location_names, sample.count); }); to_folded_sorted_lines(&folded_stack_items, Default::default()) @@ -129,7 +136,7 @@ fn location_to_callsite_label<'files>( fn add_locations_to_folded_stack_items( stack_items: &mut BTreeMap, locations: Vec, - gates: usize, + count: usize, ) { let mut child_map = stack_items; for (index, location) in locations.iter().enumerate() { @@ -138,7 +145,7 @@ fn add_locations_to_folded_stack_items( child_map = &mut current_item.nested_items; if index == locations.len() - 1 { - current_item.total_samples += gates; + current_item.total_samples += count; } } } @@ -180,7 +187,7 @@ fn to_folded_sorted_lines( #[cfg(test)] mod tests { use acir::{ - circuit::{opcodes::BlockId, Opcode, OpcodeLocation}, + circuit::{opcodes::BlockId, Opcode as AcirOpcode, OpcodeLocation}, native_types::Expression, FieldElement, }; @@ -188,6 +195,8 @@ mod tests { use noirc_errors::{debug_info::DebugInfo, Location, Span}; use std::{collections::BTreeMap, path::Path}; + use crate::{flamegraph::Sample, opcode_formatter::AcirOrBrilligOpcode}; + use super::generate_folded_sorted_lines; fn find_spans_for(source: &str, needle: &str) -> Vec { @@ -272,30 +281,36 @@ mod tests { BTreeMap::default(), ); - let samples_per_opcode = vec![10, 20, 30]; - - let expected_folded_sorted_lines = vec![ - "main.nr:2:9::fn main();main.nr:3:13::foo();main.nr:8:13::baz();main.nr:14:13::whatever();opcode::arithmetic 10".to_string(), - "main.nr:2:9::fn main();main.nr:4:13::bar();main.nr:11:13::whatever();opcode::arithmetic 20".to_string(), - "main.nr:2:9::fn main();main.nr:5:13::whatever();opcode::memory::init 30".to_string(), + let samples: Vec> = vec![ + Sample { + opcode: AcirOrBrilligOpcode::Acir(AcirOpcode::AssertZero(Expression::default())), + call_stack: vec![OpcodeLocation::Acir(0)], + count: 10, + }, + Sample { + opcode: AcirOrBrilligOpcode::Acir(AcirOpcode::AssertZero(Expression::default())), + call_stack: vec![OpcodeLocation::Acir(1)], + count: 20, + }, + Sample { + opcode: AcirOrBrilligOpcode::Acir(AcirOpcode::MemoryInit { + block_id: BlockId(0), + init: vec![], + block_type: acir::circuit::opcodes::BlockType::Memory, + }), + call_stack: vec![OpcodeLocation::Acir(2)], + count: 30, + }, ]; - let opcodes: Vec> = vec![ - Opcode::AssertZero(Expression::default()), - Opcode::AssertZero(Expression::default()), - Opcode::MemoryInit { - block_id: BlockId(0), - init: vec![], - block_type: acir::circuit::opcodes::BlockType::Memory, - }, + let expected_folded_sorted_lines = vec![ + "main.nr:2:9::fn main();main.nr:3:13::foo();main.nr:8:13::baz();main.nr:14:13::whatever();acir::arithmetic 10".to_string(), + "main.nr:2:9::fn main();main.nr:4:13::bar();main.nr:11:13::whatever();acir::arithmetic 20".to_string(), + "main.nr:2:9::fn main();main.nr:5:13::whatever();acir::memory::init 30".to_string(), ]; - let actual_folded_sorted_lines = generate_folded_sorted_lines( - samples_per_opcode, - opcodes, - &debug_info, - fm.as_file_map(), - ); + let actual_folded_sorted_lines = + generate_folded_sorted_lines(samples, &debug_info, fm.as_file_map()); assert_eq!(expected_folded_sorted_lines, actual_folded_sorted_lines); } diff --git a/tooling/profiler/src/gates_provider.rs b/tooling/profiler/src/gates_provider.rs index f96b1292987..3f07f3e4be6 100644 --- a/tooling/profiler/src/gates_provider.rs +++ b/tooling/profiler/src/gates_provider.rs @@ -10,6 +10,7 @@ pub(crate) trait GatesProvider { pub(crate) struct BackendGatesProvider { pub(crate) backend_path: PathBuf, + pub(crate) gates_command: String, pub(crate) extra_args: Vec, } @@ -17,7 +18,7 @@ impl GatesProvider for BackendGatesProvider { fn get_gates(&self, artifact_path: &Path) -> eyre::Result { let mut backend_gates_cmd = Command::new(&self.backend_path); - backend_gates_cmd.arg("gates").arg("-b").arg(artifact_path); + backend_gates_cmd.arg(self.gates_command.clone()).arg("-b").arg(artifact_path); for arg in &self.extra_args { backend_gates_cmd.arg(arg); diff --git a/tooling/profiler/src/main.rs b/tooling/profiler/src/main.rs index 215feb0a4e7..b0b42e6ee41 100644 --- a/tooling/profiler/src/main.rs +++ b/tooling/profiler/src/main.rs @@ -33,7 +33,7 @@ fn main() { } if let Err(report) = cli::start_cli() { - eprintln!("{report}"); + eprintln!("{report:?}"); std::process::exit(1); } } diff --git a/tooling/profiler/src/opcode_formatter.rs b/tooling/profiler/src/opcode_formatter.rs index a33de42a0ff..772c87ad1cb 100644 --- a/tooling/profiler/src/opcode_formatter.rs +++ b/tooling/profiler/src/opcode_formatter.rs @@ -1,4 +1,12 @@ -use acir::circuit::{directives::Directive, opcodes::BlackBoxFuncCall, Opcode}; +use acir::brillig::{BinaryFieldOp, BinaryIntOp, BlackBoxOp, Opcode as BrilligOpcode}; +use acir::circuit::{directives::Directive, opcodes::BlackBoxFuncCall, Opcode as AcirOpcode}; +use acir::AcirField; + +#[derive(Debug)] +pub(crate) enum AcirOrBrilligOpcode { + Acir(AcirOpcode), + Brillig(BrilligOpcode), +} fn format_blackbox_function(call: &BlackBoxFuncCall) -> String { match call { @@ -30,24 +38,115 @@ fn format_blackbox_function(call: &BlackBoxFuncCall) -> String { } } +fn format_blackbox_op(call: &BlackBoxOp) -> String { + match call { + BlackBoxOp::AES128Encrypt { .. } => "aes128_encrypt".to_string(), + BlackBoxOp::Sha256 { .. } => "sha256".to_string(), + BlackBoxOp::Blake2s { .. } => "blake2s".to_string(), + BlackBoxOp::Blake3 { .. } => "blake3".to_string(), + BlackBoxOp::SchnorrVerify { .. } => "schnorr_verify".to_string(), + BlackBoxOp::PedersenCommitment { .. } => "pedersen_commitment".to_string(), + BlackBoxOp::PedersenHash { .. } => "pedersen_hash".to_string(), + BlackBoxOp::EcdsaSecp256k1 { .. } => "ecdsa_secp256k1".to_string(), + BlackBoxOp::EcdsaSecp256r1 { .. } => "ecdsa_secp256r1".to_string(), + BlackBoxOp::MultiScalarMul { .. } => "multi_scalar_mul".to_string(), + BlackBoxOp::EmbeddedCurveAdd { .. } => "embedded_curve_add".to_string(), + BlackBoxOp::Keccak256 { .. } => "keccak256".to_string(), + BlackBoxOp::Keccakf1600 { .. } => "keccakf1600".to_string(), + BlackBoxOp::BigIntAdd { .. } => "big_int_add".to_string(), + BlackBoxOp::BigIntSub { .. } => "big_int_sub".to_string(), + BlackBoxOp::BigIntMul { .. } => "big_int_mul".to_string(), + BlackBoxOp::BigIntDiv { .. } => "big_int_div".to_string(), + BlackBoxOp::BigIntFromLeBytes { .. } => "big_int_from_le_bytes".to_string(), + BlackBoxOp::BigIntToLeBytes { .. } => "big_int_to_le_bytes".to_string(), + BlackBoxOp::Poseidon2Permutation { .. } => "poseidon2_permutation".to_string(), + BlackBoxOp::Sha256Compression { .. } => "sha256_compression".to_string(), + BlackBoxOp::ToRadix { .. } => "to_radix".to_string(), + } +} + fn format_directive_kind(directive: &Directive) -> String { match directive { Directive::ToLeRadix { .. } => "to_le_radix".to_string(), } } -fn format_opcode_kind(opcode: &Opcode) -> String { +fn format_acir_opcode_kind(opcode: &AcirOpcode) -> String { + match opcode { + AcirOpcode::AssertZero(_) => "arithmetic".to_string(), + AcirOpcode::BlackBoxFuncCall(call) => { + format!("blackbox::{}", format_blackbox_function(call)) + } + AcirOpcode::MemoryOp { .. } => "memory::op".to_string(), + AcirOpcode::MemoryInit { .. } => "memory::init".to_string(), + AcirOpcode::Directive(directive) => { + format!("directive::{}", format_directive_kind(directive)) + } + AcirOpcode::BrilligCall { id, .. } => format!("brillig_call({id})"), + AcirOpcode::Call { .. } => "acir_call".to_string(), + } +} + +fn format_binary_field_op(op: &BinaryFieldOp) -> String { + match op { + BinaryFieldOp::Add => "add".to_string(), + BinaryFieldOp::Sub => "sub".to_string(), + BinaryFieldOp::Mul => "mul".to_string(), + BinaryFieldOp::Div => "fdiv".to_string(), + BinaryFieldOp::IntegerDiv => "div".to_string(), + BinaryFieldOp::Equals => "eq".to_string(), + BinaryFieldOp::LessThan => "lt".to_string(), + BinaryFieldOp::LessThanEquals => "lte".to_string(), + } +} + +fn format_binary_int(op: &acir::brillig::BinaryIntOp) -> String { + match op { + BinaryIntOp::Add => "add".to_string(), + BinaryIntOp::Sub => "sub".to_string(), + BinaryIntOp::Mul => "mul".to_string(), + BinaryIntOp::Div => "div".to_string(), + BinaryIntOp::Equals => "eq".to_string(), + BinaryIntOp::LessThan => "lt".to_string(), + BinaryIntOp::LessThanEquals => "lte".to_string(), + BinaryIntOp::And => "and".to_string(), + BinaryIntOp::Or => "or".to_string(), + BinaryIntOp::Xor => "xor".to_string(), + BinaryIntOp::Shl => "shl".to_string(), + BinaryIntOp::Shr => "shr".to_string(), + } +} + +fn format_brillig_opcode_kind(opcode: &BrilligOpcode) -> String { match opcode { - Opcode::AssertZero(_) => "arithmetic".to_string(), - Opcode::BlackBoxFuncCall(call) => format!("blackbox::{}", format_blackbox_function(call)), - Opcode::MemoryOp { .. } => "memory::op".to_string(), - Opcode::MemoryInit { .. } => "memory::init".to_string(), - Opcode::Directive(directive) => format!("directive::{}", format_directive_kind(directive)), - Opcode::BrilligCall { .. } => "brillig_call".to_string(), - Opcode::Call { .. } => "acir_call".to_string(), + BrilligOpcode::BinaryFieldOp { op, .. } => format!("field::{}", format_binary_field_op(op)), + BrilligOpcode::BinaryIntOp { op, bit_size, .. } => { + format!("{bit_size}::{}", format_binary_int(op)) + } + BrilligOpcode::BlackBox(func) => format!("blackbox::{}", format_blackbox_op(func)), + BrilligOpcode::Call { .. } => "call".to_string(), + BrilligOpcode::CalldataCopy { .. } => "calldata_copy".to_string(), + BrilligOpcode::Cast { .. } => "cast".to_string(), + BrilligOpcode::ConditionalMov { .. } => "cmov".to_string(), + BrilligOpcode::Const { .. } => "const".to_string(), + BrilligOpcode::ForeignCall { function, .. } => format!("foreign_call({})", function), + BrilligOpcode::Jump { .. } => "jump".to_string(), + BrilligOpcode::JumpIf { .. } => "jump_if".to_string(), + BrilligOpcode::JumpIfNot { .. } => "jump_if_not".to_string(), + BrilligOpcode::Load { .. } => "load".to_string(), + BrilligOpcode::Mov { .. } => "mov".to_string(), + BrilligOpcode::Return => "return".to_string(), + BrilligOpcode::Stop { .. } => "stop".to_string(), + BrilligOpcode::Store { .. } => "store".to_string(), + BrilligOpcode::Trap { .. } => "trap".to_string(), } } -pub(crate) fn format_opcode(opcode: &Opcode) -> String { - format!("opcode::{}", format_opcode_kind(opcode)) +pub(crate) fn format_opcode(opcode: &AcirOrBrilligOpcode) -> String { + match opcode { + AcirOrBrilligOpcode::Acir(opcode) => format!("acir::{}", format_acir_opcode_kind(opcode)), + AcirOrBrilligOpcode::Brillig(opcode) => { + format!("brillig::{}", format_brillig_opcode_kind(opcode)) + } + } } diff --git a/yarn.lock b/yarn.lock index 40d6ccc55e6..f10b5b2cb67 100644 --- a/yarn.lock +++ b/yarn.lock @@ -221,9 +221,9 @@ __metadata: languageName: node linkType: hard -"@aztec/bb.js@npm:0.47.1": - version: 0.47.1 - resolution: "@aztec/bb.js@npm:0.47.1" +"@aztec/bb.js@npm:0.48.0": + version: 0.48.0 + resolution: "@aztec/bb.js@npm:0.48.0" dependencies: comlink: ^4.4.1 commander: ^10.0.1 @@ -231,7 +231,7 @@ __metadata: tslib: ^2.4.0 bin: bb.js: dest/node/main.js - checksum: fa06d2ab58b2a23bacc578df7654f5c7eb90553229fc9730aaaf7479bc96b39f10f24a4f3a7eae8f73df3cdd8a3ffb07627cad61dff9896cabdb275ce5b6f09b + checksum: e06b864a5acea4299dfa350f732dd05a807968678fd3bc3b9c699f9bc50aef1525e2492dfacf9965270082c23b04653f55c40a34f75ead11a52e3fc5512ddce7 languageName: node linkType: hard @@ -4161,7 +4161,7 @@ __metadata: version: 0.0.0-use.local resolution: "@noir-lang/backend_barretenberg@workspace:tooling/noir_js_backend_barretenberg" dependencies: - "@aztec/bb.js": 0.47.1 + "@aztec/bb.js": 0.48.0 "@noir-lang/types": "workspace:*" "@types/node": ^20.6.2 "@types/prettier": ^3