From b95bb47625eb2571bc3897a65b42ef1ab7f3d130 Mon Sep 17 00:00:00 2001 From: jewelofchaos9 Date: Wed, 6 Aug 2025 16:40:20 +0000 Subject: [PATCH 01/10] a --- .../fuzzer/src/fuzz_lib/block_context.rs | 10 +++++++++ .../fuzzer/src/fuzz_lib/function_context.rs | 21 +++---------------- .../fuzzer/src/fuzz_lib/instruction.rs | 6 ++++++ tooling/ssa_fuzzer/fuzzer/src/fuzz_target.rs | 15 +++++++++---- 4 files changed, 30 insertions(+), 22 deletions(-) diff --git a/tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/block_context.rs b/tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/block_context.rs index 5304b0a33f3..37030d651a5 100644 --- a/tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/block_context.rs +++ b/tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/block_context.rs @@ -66,6 +66,16 @@ impl BlockContext { } } + pub(crate) fn new_from_context(context: &BlockContext) -> Self { + Self { + stored_values: context.stored_values.clone(), + memory_addresses: context.memory_addresses.clone(), + parent_blocks_history: context.parent_blocks_history.clone(), + children_blocks: context.children_blocks.clone(), + options: context.options.clone(), + } + } + /// Inserts an instruction that takes a single argument fn insert_instruction_with_single_arg( &mut self, diff --git a/tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/function_context.rs b/tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/function_context.rs index 0d24ad74630..82562e1002c 100644 --- a/tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/function_context.rs +++ b/tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/function_context.rs @@ -324,18 +324,8 @@ impl<'a> FuzzerFunctionContext<'a> { // creates new contexts of created blocks let mut parent_blocks_history = self.current_block.context.parent_blocks_history.clone(); parent_blocks_history.push_front(self.current_block.block_id); - let mut block_then_context = BlockContext::new( - self.current_block.context.stored_values.clone(), - self.current_block.context.memory_addresses.clone(), - parent_blocks_history.clone(), - SsaBlockOptions::from(self.function_context_options.clone()), - ); - let mut block_else_context = BlockContext::new( - self.current_block.context.stored_values.clone(), - self.current_block.context.memory_addresses.clone(), - parent_blocks_history, - SsaBlockOptions::from(self.function_context_options.clone()), - ); + let mut block_then_context = BlockContext::new_from_context(&self.current_block.context); + let mut block_else_context = BlockContext::new_from_context(&self.current_block.context); // inserts instructions into created blocks self.switch_to_block(block_then_id); @@ -582,12 +572,7 @@ impl<'a> FuzzerFunctionContext<'a> { self.insert_jmp_instruction(block_if_id, vec![start_id.clone()]); // fill body block with instructions - let mut block_body_context = BlockContext::new( - self.current_block.context.stored_values.clone(), - self.current_block.context.memory_addresses.clone(), - self.current_block.context.parent_blocks_history.clone(), - SsaBlockOptions::from(self.function_context_options.clone()), - ); + let mut block_body_context = BlockContext::new_from_context(&self.current_block.context); self.switch_to_block(block_body_id); block_body_context.insert_instructions( self.acir_builder, diff --git a/tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/instruction.rs b/tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/instruction.rs index c13bfabe149..40879241b7e 100644 --- a/tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/instruction.rs +++ b/tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/instruction.rs @@ -3,6 +3,12 @@ use libfuzzer_sys::arbitrary::Arbitrary; use noir_ssa_fuzzer::typed_value::ValueType; use serde::{Deserialize, Serialize}; +#[derive(Arbitrary, Debug, Clone, Copy, Serialize, Deserialize)] +pub(crate) struct Array { + pub(crate) size: usize, + pub(crate) element_type: ValueType, +} + #[derive(Arbitrary, Debug, Clone, Copy, Serialize, Deserialize)] pub(crate) struct Argument { /// Index of the argument in the context of stored variables of this type diff --git a/tooling/ssa_fuzzer/fuzzer/src/fuzz_target.rs b/tooling/ssa_fuzzer/fuzzer/src/fuzz_target.rs index c96df26b019..bc3b20338b5 100644 --- a/tooling/ssa_fuzzer/fuzzer/src/fuzz_target.rs +++ b/tooling/ssa_fuzzer/fuzzer/src/fuzz_target.rs @@ -7,7 +7,7 @@ mod utils; use bincode::serde::{borrow_decode_from_slice, encode_to_vec}; use fuzz_lib::fuzz_target_lib::fuzz_target; use fuzz_lib::fuzzer::FuzzerData; -use fuzz_lib::options::{FuzzerOptions, InstructionOptions}; +use fuzz_lib::options::{FuzzerCommandOptions, FuzzerOptions, InstructionOptions}; use libfuzzer_sys::Corpus; use mutations::mutate; use noirc_driver::CompileOptions; @@ -47,9 +47,16 @@ libfuzzer_sys::fuzz_target!(|data: &[u8]| -> Corpus { alloc_enabled: false, ..InstructionOptions::default() }; - let modes = vec![FuzzerMode::NonConstant, FuzzerMode::NonConstantWithoutDIE]; - let options = - FuzzerOptions { compile_options, instruction_options, modes, ..FuzzerOptions::default() }; + let modes = vec![FuzzerMode::NonConstant]; + let fuzzer_command_options = + FuzzerCommandOptions { loops_enabled: false, ..FuzzerCommandOptions::default() }; + let options = FuzzerOptions { + compile_options, + instruction_options, + modes, + fuzzer_command_options, + ..FuzzerOptions::default() + }; let fuzzer_data = borrow_decode_from_slice(data, bincode::config::legacy()) .unwrap_or((FuzzerData::default(), 1337)) .0; From f574e8a657f8d2f61a4030241c69ed3af5ec3515 Mon Sep 17 00:00:00 2001 From: jewelofchaos9 Date: Wed, 6 Aug 2025 17:15:32 +0000 Subject: [PATCH 02/10] smol refactor --- .../fuzzer/src/fuzz_lib/block_context.rs | 10 ---- .../fuzzer/src/fuzz_lib/function_context.rs | 55 ++++++++++--------- 2 files changed, 30 insertions(+), 35 deletions(-) diff --git a/tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/block_context.rs b/tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/block_context.rs index 37030d651a5..5304b0a33f3 100644 --- a/tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/block_context.rs +++ b/tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/block_context.rs @@ -66,16 +66,6 @@ impl BlockContext { } } - pub(crate) fn new_from_context(context: &BlockContext) -> Self { - Self { - stored_values: context.stored_values.clone(), - memory_addresses: context.memory_addresses.clone(), - parent_blocks_history: context.parent_blocks_history.clone(), - children_blocks: context.children_blocks.clone(), - options: context.options.clone(), - } - } - /// Inserts an instruction that takes a single argument fn insert_instruction_with_single_arg( &mut self, diff --git a/tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/function_context.rs b/tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/function_context.rs index 82562e1002c..30a9be210f7 100644 --- a/tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/function_context.rs +++ b/tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/function_context.rs @@ -324,8 +324,16 @@ impl<'a> FuzzerFunctionContext<'a> { // creates new contexts of created blocks let mut parent_blocks_history = self.current_block.context.parent_blocks_history.clone(); parent_blocks_history.push_front(self.current_block.block_id); - let mut block_then_context = BlockContext::new_from_context(&self.current_block.context); - let mut block_else_context = BlockContext::new_from_context(&self.current_block.context); + let mut block_then_context = BlockContext { + children_blocks: vec![], + parent_blocks_history: parent_blocks_history.clone(), + ..self.current_block.context.clone() + }; + let mut block_else_context = BlockContext { + children_blocks: vec![], + parent_blocks_history, + ..self.current_block.context.clone() + }; // inserts instructions into created blocks self.switch_to_block(block_then_id); @@ -421,12 +429,11 @@ impl<'a> FuzzerFunctionContext<'a> { let mut parent_blocks_history = self.current_block.context.parent_blocks_history.clone(); parent_blocks_history.push_front(self.current_block.block_id); self.switch_to_block(destination_block_id); - let mut destination_block_context = BlockContext::new( - self.current_block.context.stored_values.clone(), - self.current_block.context.memory_addresses.clone(), + let mut destination_block_context = BlockContext { + children_blocks: vec![], parent_blocks_history, - SsaBlockOptions::from(self.function_context_options.clone()), - ); + ..self.current_block.context.clone() + }; // inserts instructions into the new block destination_block_context.insert_instructions( @@ -572,7 +579,8 @@ impl<'a> FuzzerFunctionContext<'a> { self.insert_jmp_instruction(block_if_id, vec![start_id.clone()]); // fill body block with instructions - let mut block_body_context = BlockContext::new_from_context(&self.current_block.context); + let mut block_body_context = + BlockContext { children_blocks: vec![], ..self.current_block.context.clone() }; self.switch_to_block(block_body_id); block_body_context.insert_instructions( self.acir_builder, @@ -590,12 +598,11 @@ impl<'a> FuzzerFunctionContext<'a> { self.current_block.context.clone() }; // end block does not share variables with body block, so we copy them from the current block - let block_end_context = BlockContext::new( - end_context.stored_values.clone(), - end_context.memory_addresses.clone(), - block_body_context.parent_blocks_history.clone(), - SsaBlockOptions::from(self.function_context_options.clone()), - ); + let block_end_context = BlockContext { + children_blocks: vec![], + parent_blocks_history: block_body_context.parent_blocks_history.clone(), + ..end_context + }; let end_block_stored = StoredBlock { context: block_end_context, block_id: block_end_id }; // connect end block with the current block @@ -703,12 +710,11 @@ impl<'a> FuzzerFunctionContext<'a> { let closest_parent = self.find_closest_parent(&first_block, &second_block); let closest_parent_block = self.stored_blocks[&closest_parent].clone(); - let merged_block_context = BlockContext::new( - closest_parent_block.context.stored_values.clone(), - closest_parent_block.context.memory_addresses.clone(), + let merged_block_context = BlockContext { + children_blocks: vec![], parent_blocks_history, - SsaBlockOptions::from(self.function_context_options.clone()), - ); + ..closest_parent_block.context.clone() + }; self.switch_to_block(first_block.block_id); first_block.context.finalize_block_with_jmp( self.acir_builder, @@ -900,12 +906,11 @@ impl<'a> FuzzerFunctionContext<'a> { // add instructions to the return block self.switch_to_block(return_block_id); - let mut return_block_context = BlockContext::new( - last_block.context.stored_values.clone(), - last_block.context.memory_addresses.clone(), - VecDeque::new(), - SsaBlockOptions::from(self.function_context_options.clone()), - ); + let mut return_block_context = BlockContext { + children_blocks: vec![], + parent_blocks_history: VecDeque::new(), + ..last_block.context.clone() + }; return_block_context.insert_instructions( self.acir_builder, self.brillig_builder, From 1bb9cf4985416474c64a9f6e88a00369d0d42b5b Mon Sep 17 00:00:00 2001 From: jewelofchaos9 Date: Wed, 6 Aug 2025 19:04:18 +0000 Subject: [PATCH 03/10] smolololo change --- .../fuzzer/src/fuzz_lib/block_context.rs | 223 +++++++++++++++--- .../fuzzer/src/fuzz_lib/function_context.rs | 6 +- .../fuzzer/src/fuzz_lib/instruction.rs | 11 +- .../ssa_fuzzer/fuzzer/src/fuzz_lib/options.rs | 6 + .../instructions/instruction_mutator.rs | 1 + tooling/ssa_fuzzer/src/builder.rs | 87 +++++++ 6 files changed, 301 insertions(+), 33 deletions(-) diff --git a/tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/block_context.rs b/tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/block_context.rs index 5304b0a33f3..e6870fcde59 100644 --- a/tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/block_context.rs +++ b/tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/block_context.rs @@ -7,21 +7,31 @@ use noir_ssa_fuzzer::{ use noirc_evaluator::ssa::ir::basic_block::BasicBlockId; use noirc_evaluator::ssa::ir::function::Function; use noirc_evaluator::ssa::ir::map::Id; +use noirc_evaluator::ssa::ir::value::ValueId; use std::collections::{HashMap, VecDeque}; use std::iter::zip; +#[derive(Debug, Clone)] +struct StoredArray { + array_id: TypedValue, + element_type: ValueType, + is_references: bool, +} + /// Main context for the ssa block containing both ACIR and Brillig builders and their state /// It works with indices of variables Ids, because it cannot handle Ids logic for ACIR and Brillig #[derive(Debug, Clone)] pub(crate) struct BlockContext { /// Ids of the Program variables stored as TypedValue separated by type - pub(crate) stored_values: HashMap>, + pub(crate) stored_variables: HashMap>, /// Ids of typed addresses of memory (mutable variables) pub(crate) memory_addresses: HashMap>, /// Parent blocks history pub(crate) parent_blocks_history: VecDeque, /// Children blocks pub(crate) children_blocks: Vec, + /// Arrays stored in the block + pub(crate) stored_arrays: Vec, /// Options for the block pub(crate) options: SsaBlockOptions, } @@ -52,16 +62,17 @@ fn append_typed_value_to_map( impl BlockContext { pub(crate) fn new( - stored_values: HashMap>, + stored_variables: HashMap>, memory_addresses: HashMap>, parent_blocks_history: VecDeque, options: SsaBlockOptions, ) -> Self { Self { - stored_values, + stored_variables, memory_addresses, parent_blocks_history, children_blocks: Vec::new(), + stored_arrays: Vec::new(), options, } } @@ -74,7 +85,7 @@ impl BlockContext { arg: Argument, instruction: InstructionWithOneArg, ) { - let value = get_typed_value_from_map(&self.stored_values, &arg.value_type, arg.index); + let value = get_typed_value_from_map(&self.stored_variables, &arg.value_type, arg.index); let value = match value { Some(value) => value, _ => return, @@ -83,7 +94,7 @@ impl BlockContext { // insert to brillig, assert id is the same assert_eq!(acir_result.value_id, instruction(brillig_builder, value).value_id); append_typed_value_to_map( - &mut self.stored_values, + &mut self.stored_variables, &acir_result.to_value_type(), acir_result, ); @@ -98,9 +109,11 @@ impl BlockContext { rhs: Argument, instruction: InstructionWithTwoArgs, ) { - let instr_lhs = get_typed_value_from_map(&self.stored_values, &lhs.value_type, lhs.index); + let instr_lhs = + get_typed_value_from_map(&self.stored_variables, &lhs.value_type, lhs.index); // We ignore type of the second argument, because all binary instructions must use the same type - let instr_rhs = get_typed_value_from_map(&self.stored_values, &lhs.value_type, rhs.index); + let instr_rhs = + get_typed_value_from_map(&self.stored_variables, &lhs.value_type, rhs.index); let (instr_lhs, instr_rhs) = match (instr_lhs, instr_rhs) { (Some(acir_lhs), Some(acir_rhs)) => (acir_lhs, acir_rhs), _ => return, @@ -108,7 +121,7 @@ impl BlockContext { let result = instruction(acir_builder, instr_lhs.clone(), instr_rhs.clone()); // insert to brillig, assert id of return is the same assert_eq!(result.value_id, instruction(brillig_builder, instr_lhs, instr_rhs).value_id); - append_typed_value_to_map(&mut self.stored_values, &result.to_value_type(), result); + append_typed_value_to_map(&mut self.stored_variables, &result.to_value_type(), result); } /// Inserts an instruction into both ACIR and Brillig programs @@ -184,7 +197,7 @@ impl BlockContext { return; } let value = - get_typed_value_from_map(&self.stored_values, &lhs.value_type, lhs.index); + get_typed_value_from_map(&self.stored_variables, &lhs.value_type, lhs.index); let value = match value { Some(value) => value, _ => return, @@ -195,11 +208,12 @@ impl BlockContext { brillig_builder.insert_cast(value.clone(), type_).value_id ); // Cast can return the same value as the original value, if cast type is forbidden, so we skip it - if self.stored_values.get(&value.to_value_type()).unwrap().contains(&acir_result) { + if self.stored_variables.get(&value.to_value_type()).unwrap().contains(&acir_result) + { return; } append_typed_value_to_map( - &mut self.stored_values, + &mut self.stored_variables, &acir_result.to_value_type(), acir_result, ); @@ -307,8 +321,8 @@ impl BlockContext { Instruction::AddSubConstrain { lhs, rhs } => { // inserts lhs' = lhs + rhs let lhs_orig = - get_typed_value_from_map(&self.stored_values, &ValueType::Field, lhs); - let rhs = get_typed_value_from_map(&self.stored_values, &ValueType::Field, rhs); + get_typed_value_from_map(&self.stored_variables, &ValueType::Field, lhs); + let rhs = get_typed_value_from_map(&self.stored_variables, &ValueType::Field, rhs); let (lhs_orig, rhs) = match (lhs_orig, rhs) { (Some(lhs_orig), Some(rhs)) => (lhs_orig, rhs), _ => return, @@ -343,8 +357,8 @@ impl BlockContext { } Instruction::MulDivConstrain { lhs, rhs } => { let lhs_orig = - get_typed_value_from_map(&self.stored_values, &ValueType::Field, lhs); - let rhs = get_typed_value_from_map(&self.stored_values, &ValueType::Field, rhs); + get_typed_value_from_map(&self.stored_variables, &ValueType::Field, lhs); + let rhs = get_typed_value_from_map(&self.stored_variables, &ValueType::Field, rhs); let (lhs_orig, rhs) = match (lhs_orig, rhs) { (Some(lhs_orig), Some(rhs)) => (lhs_orig, rhs), _ => return, @@ -379,12 +393,14 @@ impl BlockContext { return; } - let value = - match get_typed_value_from_map(&self.stored_values, &lhs.value_type, lhs.index) - { - Some(value) => value, - _ => return, - }; + let value = match get_typed_value_from_map( + &self.stored_variables, + &lhs.value_type, + lhs.index, + ) { + Some(value) => value, + _ => return, + }; let addr = acir_builder.insert_add_to_memory(value.clone()); assert_eq!( @@ -415,7 +431,7 @@ impl BlockContext { "load from memory differs in ACIR and Brillig" ); append_typed_value_to_map( - &mut self.stored_values, + &mut self.stored_variables, &value.to_value_type(), value.clone(), ); @@ -433,8 +449,11 @@ impl BlockContext { Some(addr) => addr, _ => return, }; - let value = - get_typed_value_from_map(&self.stored_values, &value.value_type, value.index); + let value = get_typed_value_from_map( + &self.stored_variables, + &value.value_type, + value.index, + ); let value = match value { Some(value) => value, _ => return, @@ -442,6 +461,148 @@ impl BlockContext { acir_builder.insert_set_to_memory(addr.clone(), value.clone()); brillig_builder.insert_set_to_memory(addr.clone(), value); } + + Instruction::CreateArray { elements_indices, element_type, is_references } => { + if !self.options.instruction_options.create_array_enabled { + return; + } + // if we storing references, take values from memory addresses, otherwise from stored variables + let elements = if !is_references { + elements_indices + .iter() + .map(|index| { + get_typed_value_from_map(&self.stored_variables, &element_type, *index) + }) + .collect::>>() + } else { + elements_indices + .iter() + .map(|index| { + get_typed_value_from_map(&self.memory_addresses, &element_type, *index) + }) + .collect::>>() + }; + + let elements = match elements { + Some(elements) => elements, + _ => return, + }; + // insert to both acir and brillig builders + let array = acir_builder.insert_array(elements.clone(), element_type.to_ssa_type()); + assert_eq!( + array.value_id, + brillig_builder.insert_array(elements, element_type.to_ssa_type()).value_id, + ); + self.stored_arrays.push(StoredArray { + array_id: array, + element_type, + is_references, + }); + } + Instruction::ArrayGet { array_index, index } => { + if !self.options.instruction_options.array_get_enabled { + return; + } + // get the array from the stored arrays + let stored_array = self.stored_arrays.get(array_index % self.stored_arrays.len()); + let stored_array = match stored_array { + Some(stored_array) => stored_array, + _ => return, + }; + let array_id = stored_array.array_id.clone(); + + // get the index from the stored variables + let index = + get_typed_value_from_map(&self.stored_variables, &ValueType::U32, index.index); + let index = match index { + Some(index) => index, + _ => return, + }; + // cast the index to u32 + let index_casted = acir_builder.insert_cast(index.clone(), ValueType::U32); + assert_eq!( + index_casted.value_id, + brillig_builder.insert_cast(index.clone(), ValueType::U32).value_id + ); + // insert array get to both acir and brillig builders + let value = acir_builder.insert_array_get(array_id.clone(), index_casted.clone()); + assert_eq!( + value.value_id, + brillig_builder + .insert_array_get(array_id.clone(), index_casted.clone()) + .value_id, + ); + // if we storing references, append the value to memory addresses, otherwise to stored variables + if stored_array.is_references { + append_typed_value_to_map( + &mut self.memory_addresses, + &value.to_value_type(), + value.clone(), + ); + } else { + append_typed_value_to_map( + &mut self.stored_variables, + &value.to_value_type(), + value.clone(), + ); + } + } + Instruction::ArraySet { array_index, index, value_index, mutable } => { + if !self.options.instruction_options.array_set_enabled { + return; + } + // get the array from the stored arrays + let stored_array = self.stored_arrays.get(array_index % self.stored_arrays.len()); + let stored_array = match stored_array { + Some(stored_array) => stored_array, + _ => return, + }; + let array_id = stored_array.array_id.clone(); + + // get the index from the stored variables + let index = + get_typed_value_from_map(&self.stored_variables, &ValueType::U32, index.index); + let index = match index { + Some(index) => index, + _ => return, + }; + // cast the index to u32 + let index_casted = acir_builder.insert_cast(index.clone(), ValueType::U32); + assert_eq!( + index_casted.value_id, + brillig_builder.insert_cast(index.clone(), ValueType::U32).value_id + ); + + // get the value from the stored variables + let value = get_typed_value_from_map( + &self.stored_variables, + &stored_array.element_type, + value_index, + ); + let value = match value { + Some(value) => value, + _ => return, + }; + // insert array set to both acir and brillig builders + let new_array = acir_builder.insert_array_set( + array_id.clone(), + index_casted.clone(), + value.clone(), + mutable, + ); + assert_eq!( + new_array.value_id, + brillig_builder + .insert_array_set(array_id, index_casted, value, mutable) + .value_id + ); + + self.stored_arrays.push(StoredArray { + array_id: new_array, + element_type: stored_array.element_type, + is_references: stored_array.is_references, + }); + } } } @@ -452,7 +613,7 @@ impl BlockContext { instructions: &Vec, ) { for instruction in instructions { - self.insert_instruction(acir_builder, brillig_builder, *instruction); + self.insert_instruction(acir_builder, brillig_builder, instruction.clone()); } } @@ -463,7 +624,7 @@ impl BlockContext { brillig_builder: &mut FuzzerBuilder, return_type: ValueType, ) { - let array_of_values_with_return_type = self.stored_values.get(&return_type); + let array_of_values_with_return_type = self.stored_variables.get(&return_type); let return_value = match array_of_values_with_return_type { Some(arr) => arr.iter().last(), _ => None, @@ -476,7 +637,8 @@ impl BlockContext { _ => { // If no last value was set, we take a boolean that is definitely set and cast it to the return type let boolean_value = - get_typed_value_from_map(&self.stored_values, &ValueType::Boolean, 0).unwrap(); + get_typed_value_from_map(&self.stored_variables, &ValueType::Boolean, 0) + .unwrap(); let return_value = acir_builder.insert_cast(boolean_value.clone(), return_type); assert_eq!(brillig_builder.insert_cast(boolean_value, return_type), return_value); acir_builder.finalize_function(&return_value); @@ -506,7 +668,7 @@ impl BlockContext { ) { // takes last boolean variable as condition let condition = self - .stored_values + .stored_variables .get(&ValueType::Boolean) .and_then(|values| values.last().cloned()) .expect("Should have at least one boolean") @@ -535,7 +697,8 @@ impl BlockContext { // If we don't have some value of type of the argument, we skip the function call let mut values = vec![]; for (value_type, index) in zip(function_signature.input_types, args) { - let value = match get_typed_value_from_map(&self.stored_values, &value_type, *index) { + let value = match get_typed_value_from_map(&self.stored_variables, &value_type, *index) + { Some(value) => value, None => return, }; @@ -556,7 +719,7 @@ impl BlockContext { }; // Append the return value to stored_values map append_typed_value_to_map( - &mut self.stored_values, + &mut self.stored_variables, &function_signature.return_type, typed_ret_val, ); diff --git a/tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/function_context.rs b/tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/function_context.rs index 30a9be210f7..a970ac9eb47 100644 --- a/tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/function_context.rs +++ b/tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/function_context.rs @@ -289,8 +289,10 @@ impl<'a> FuzzerFunctionContext<'a> { /// SSA block can use variables from predecessor that is not in branch. /// Look [Self::find_closest_parent] for more details. fn store_variables(&mut self) { - self.stored_variables_for_block - .insert(self.current_block.block_id, self.current_block.context.stored_values.clone()); + self.stored_variables_for_block.insert( + self.current_block.block_id, + self.current_block.context.stored_variables.clone(), + ); } fn process_jmp_if_command(&mut self, block_then_idx: usize, block_else_idx: usize) { diff --git a/tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/instruction.rs b/tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/instruction.rs index 40879241b7e..f68706d7b38 100644 --- a/tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/instruction.rs +++ b/tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/instruction.rs @@ -24,7 +24,7 @@ pub(crate) struct Argument { /// Represents set of instructions /// /// For operations that take two arguments we ignore type of the second argument. -#[derive(Arbitrary, Debug, Clone, Copy, Serialize, Deserialize)] +#[derive(Arbitrary, Debug, Clone, Serialize, Deserialize)] pub(crate) enum Instruction { /// Addition of two values AddChecked { lhs: Argument, rhs: Argument }, @@ -74,6 +74,15 @@ pub(crate) enum Instruction { /// Store value to mutable memory /// Stores value to memory with insert_store SetToMemory { memory_addr_index: usize, value: Argument }, + + /// Create array, only type of first argument is used + /// Other elements will be taken from stored variables of the same type + CreateArray { elements_indices: Vec, element_type: ValueType, is_references: bool }, + /// Get element from array, index will be casted to u32 + ArrayGet { array_index: usize, index: Argument }, + /// Set element in array, index will be casted to u32 + /// Value will be casted to the type of the array + ArraySet { array_index: usize, index: Argument, value_index: usize, mutable: bool }, } /// Represents set of instructions diff --git a/tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/options.rs b/tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/options.rs index 37bb808e7c4..382ff56b348 100644 --- a/tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/options.rs +++ b/tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/options.rs @@ -20,6 +20,9 @@ pub(crate) struct InstructionOptions { pub(crate) load_enabled: bool, pub(crate) store_enabled: bool, pub(crate) alloc_enabled: bool, + pub(crate) create_array_enabled: bool, + pub(crate) array_get_enabled: bool, + pub(crate) array_set_enabled: bool, } impl Default for InstructionOptions { @@ -42,6 +45,9 @@ impl Default for InstructionOptions { load_enabled: true, store_enabled: true, alloc_enabled: true, + create_array_enabled: true, + array_get_enabled: true, + array_set_enabled: true, } } } diff --git a/tooling/ssa_fuzzer/fuzzer/src/mutations/instructions/instruction_mutator.rs b/tooling/ssa_fuzzer/fuzzer/src/mutations/instructions/instruction_mutator.rs index 706fd4c556d..9751cde412e 100644 --- a/tooling/ssa_fuzzer/fuzzer/src/mutations/instructions/instruction_mutator.rs +++ b/tooling/ssa_fuzzer/fuzzer/src/mutations/instructions/instruction_mutator.rs @@ -105,6 +105,7 @@ impl InstructionMutator for InstructionArgumentsMutation { } } } + _ => {} } } } diff --git a/tooling/ssa_fuzzer/src/builder.rs b/tooling/ssa_fuzzer/src/builder.rs index 5e46d5c11c0..2b0e20f5ec0 100644 --- a/tooling/ssa_fuzzer/src/builder.rs +++ b/tooling/ssa_fuzzer/src/builder.rs @@ -5,6 +5,7 @@ use noirc_driver::{CompileOptions, CompiledProgram}; use noirc_evaluator::ssa::function_builder::FunctionBuilder; use noirc_evaluator::ssa::ir::basic_block::BasicBlockId; use noirc_evaluator::ssa::ir::function::{Function, RuntimeType}; +use noirc_evaluator::ssa::ir::instruction::ArrayOffset; use noirc_evaluator::ssa::ir::instruction::BinaryOp; use noirc_evaluator::ssa::ir::map::Id; use noirc_evaluator::ssa::ir::types::{NumericType, Type}; @@ -12,6 +13,7 @@ use noirc_evaluator::ssa::ir::value::Value; use noirc_frontend::monomorphization::ast::InlineType; use noirc_frontend::monomorphization::ast::InlineType as FrontendInlineType; use std::panic::AssertUnwindSafe; +use std::sync::Arc; use thiserror::Error; #[derive(Debug, Error)] @@ -365,4 +367,89 @@ impl FuzzerBuilder { .first() .unwrap() } + + /// Inserts a new array with the given elements and type + pub fn insert_array(&mut self, elements: Vec, typ: Type) -> TypedValue { + let array_length = elements.len() as u32; + assert!( + elements.iter().all(|e| e.type_of_variable == typ.clone()), + "All elements must have the same type" + ); + let res = self + .builder + .insert_make_array(elements.into_iter().map(|e| e.value_id).collect(), typ.clone()); + TypedValue::new(res, Type::Array(Arc::new(vec![typ]), array_length)) + } + + /*fn insert_index_lt_array_length(&mut self, index: TypedValue, array: TypedValue) -> TypedValue { + match array.type_of_variable { + Type::Array(_, array_length) => { + let array_length_id = self + .builder + .numeric_constant(array_length, NumericType::Unsigned { bit_size: 32 }); + let true_id = + self.builder.numeric_constant(1_u32, NumericType::Unsigned { bit_size: 1 }); + let index_lt_length = + self.builder.insert_binary(index.value_id, BinaryOp::Lt, array_length_id); + self.builder.insert_constrain(index_lt_length, true_id, None); + TypedValue::new(index_lt_length, ValueType::Boolean.to_ssa_type()) + } + _ => unreachable!("Array type expected"), + } + } */ + + fn insert_index_mod_array_length( + &mut self, + index: TypedValue, + array: TypedValue, + ) -> TypedValue { + match array.type_of_variable.clone() { + Type::Array(_, array_length) => { + let array_length_id = self + .builder + .numeric_constant(array_length, NumericType::Unsigned { bit_size: 32 }); + let index_mod_length = + self.builder.insert_binary(index.value_id, BinaryOp::Mod, array_length_id); + TypedValue::new(index_mod_length, ValueType::U32.to_ssa_type()) + } + _ => unreachable!("Array type expected"), + } + } + + pub fn insert_array_get(&mut self, array: TypedValue, index: TypedValue) -> TypedValue { + assert!(matches!(array.type_of_variable, Type::Array(_, _))); + assert!(index.type_of_variable == Type::Numeric(NumericType::Unsigned { bit_size: 32 })); + let index_mod_len = self.insert_index_mod_array_length(index.clone(), array.clone()); + let res = self.builder.insert_array_get( + array.value_id, + index_mod_len.value_id, + ArrayOffset::None, + array.type_of_variable.clone(), + ); + TypedValue::new(res, array.type_of_variable.clone()) + } + + pub fn insert_array_set( + &mut self, + array: TypedValue, + index: TypedValue, + value: TypedValue, + mutable: bool, + ) -> TypedValue { + assert!(matches!(array.type_of_variable, Type::Array(_, _))); + assert!( + value.type_of_variable == array.type_of_variable, + "Value type must match array type" + ); + assert!(index.type_of_variable == Type::Numeric(NumericType::Unsigned { bit_size: 32 })); + let index_mod_len = self.insert_index_mod_array_length(index.clone(), array.clone()); + let res = self.builder.insert_array_set( + array.value_id, + index_mod_len.value_id, + value.value_id, + mutable, + ArrayOffset::None, + ); + TypedValue::new(res, array.type_of_variable.clone()) + } } From de23621130f137a4fa4c11fc23b83778ccac6322 Mon Sep 17 00:00:00 2001 From: jewelofchaos9 Date: Wed, 6 Aug 2025 20:02:26 +0000 Subject: [PATCH 04/10] smol tests --- .../fuzzer/src/fuzz_lib/block_context.rs | 38 +++-- .../fuzzer/src/fuzz_lib/fuzz_target_lib.rs | 132 ++++++++++++++++++ .../ssa_fuzzer/fuzzer/src/fuzz_lib/options.rs | 2 +- tooling/ssa_fuzzer/src/builder.rs | 51 ++++--- 4 files changed, 184 insertions(+), 39 deletions(-) diff --git a/tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/block_context.rs b/tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/block_context.rs index e6870fcde59..bc16fc11e1f 100644 --- a/tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/block_context.rs +++ b/tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/block_context.rs @@ -7,12 +7,11 @@ use noir_ssa_fuzzer::{ use noirc_evaluator::ssa::ir::basic_block::BasicBlockId; use noirc_evaluator::ssa::ir::function::Function; use noirc_evaluator::ssa::ir::map::Id; -use noirc_evaluator::ssa::ir::value::ValueId; use std::collections::{HashMap, VecDeque}; use std::iter::zip; #[derive(Debug, Clone)] -struct StoredArray { +pub(crate) struct StoredArray { array_id: TypedValue, element_type: ValueType, is_references: bool, @@ -488,10 +487,10 @@ impl BlockContext { _ => return, }; // insert to both acir and brillig builders - let array = acir_builder.insert_array(elements.clone(), element_type.to_ssa_type()); + let array = acir_builder.insert_array(elements.clone(), is_references); assert_eq!( array.value_id, - brillig_builder.insert_array(elements, element_type.to_ssa_type()).value_id, + brillig_builder.insert_array(elements, is_references).value_id, ); self.stored_arrays.push(StoredArray { array_id: array, @@ -510,10 +509,13 @@ impl BlockContext { _ => return, }; let array_id = stored_array.array_id.clone(); - + println!("array_id: {:?}", array_id); // get the index from the stored variables - let index = - get_typed_value_from_map(&self.stored_variables, &ValueType::U32, index.index); + let index = get_typed_value_from_map( + &self.stored_variables, + &index.value_type, + index.index, + ); let index = match index { Some(index) => index, _ => return, @@ -524,12 +526,21 @@ impl BlockContext { index_casted.value_id, brillig_builder.insert_cast(index.clone(), ValueType::U32).value_id ); + println!("index: {:?}", index); // insert array get to both acir and brillig builders - let value = acir_builder.insert_array_get(array_id.clone(), index_casted.clone()); + let value = acir_builder.insert_array_get( + array_id.clone(), + index_casted.clone(), + stored_array.element_type.to_ssa_type(), + ); assert_eq!( value.value_id, brillig_builder - .insert_array_get(array_id.clone(), index_casted.clone()) + .insert_array_get( + array_id.clone(), + index_casted.clone(), + stored_array.element_type.to_ssa_type(), + ) .value_id, ); // if we storing references, append the value to memory addresses, otherwise to stored variables @@ -546,6 +557,7 @@ impl BlockContext { value.clone(), ); } + println!("array_get: {:?}", value); } Instruction::ArraySet { array_index, index, value_index, mutable } => { if !self.options.instruction_options.array_set_enabled { @@ -557,11 +569,15 @@ impl BlockContext { Some(stored_array) => stored_array, _ => return, }; + println!("stored_array: {:?}", stored_array); let array_id = stored_array.array_id.clone(); // get the index from the stored variables - let index = - get_typed_value_from_map(&self.stored_variables, &ValueType::U32, index.index); + let index = get_typed_value_from_map( + &self.stored_variables, + &index.value_type, + index.index, + ); let index = match index { Some(index) => index, _ => return, diff --git a/tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/fuzz_target_lib.rs b/tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/fuzz_target_lib.rs index 85165d065f1..b081cdac282 100644 --- a/tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/fuzz_target_lib.rs +++ b/tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/fuzz_target_lib.rs @@ -764,4 +764,136 @@ mod tests { None => panic!("Program failed to execute"), } } + + /// Test array get and set + /// fn main f0 { + /// b0(v0: Field, v1: Field, v2: Field, v3: Field, v4: Field, v5: u1, v6: u1): + /// v7 = make_array [v0, v1, v2, v3, v4] : [Field; 5] + /// v8 = truncate v0 to 32 bits, max_bit_size: 254 + /// v9 = cast v8 as u32 + /// v11 = mod v9, u32 5 + /// v12 = array_set v7, index v11, value v4 + /// v13 = truncate v0 to 32 bits, max_bit_size: 254 + /// v14 = cast v13 as u32 + /// v15 = mod v14, u32 5 + /// v16 = array_get v12, index v15 -> Field + /// return v16 + /// } + #[test] + fn array_get_and_set() { + let arg_0_field = Argument { index: 0, value_type: ValueType::Field }; + // create array [v0, v1] + let create_array_block = InstructionBlock { + instructions: vec![Instruction::CreateArray { + elements_indices: vec![0, 1, 2, 3, 4], + element_type: ValueType::Field, + is_references: false, + }], + }; + // create new array setting new_array[0] = v4 + let array_set_block = InstructionBlock { + instructions: vec![Instruction::ArraySet { + array_index: 0, + index: arg_0_field, + value_index: 4, // set v4 + mutable: false, + }], + }; + // get new_array[0] + let array_get_block = InstructionBlock { + instructions: vec![Instruction::ArrayGet { array_index: 1, index: arg_0_field }], + }; + let instructions_blocks = vec![create_array_block, array_set_block, array_get_block]; + let commands = vec![ + FuzzerFunctionCommand::InsertSimpleInstructionBlock { instruction_block_idx: 0 }, + FuzzerFunctionCommand::InsertSimpleInstructionBlock { instruction_block_idx: 1 }, + ]; + let main_func = FunctionData { + commands, + return_instruction_block_idx: 2, + return_type: ValueType::Field, + }; + let fuzzer_data = FuzzerData { + instruction_blocks: instructions_blocks, + functions: vec![main_func], + initial_witness: default_witness(), + }; + let result = fuzz_target(fuzzer_data, FuzzerOptions::default()); + match result { + Some(result) => assert_eq!(result.get_return_value(), FieldElement::from(4_u32)), + None => panic!("Program failed to execute"), + } + } + + /// Test that references in arrays work + /// fn main f0 { + /// b0(v0: Field, v1: Field, v2: Field, v3: Field, v4: Field, v5: u1, v6: u1): + /// v7 = allocate -> &mut Field + /// store v0 at v7 + /// v8 = make_array [v7] : [&mut Field; 1] + /// store v1 at v7 + /// v9 = truncate v0 to 32 bits, max_bit_size: 254 + /// v10 = cast v9 as u32 + /// jmp b1() + /// b1(): + /// v11 = load v7 -> Field + /// return v11 + /// } + #[test] + fn test_reference_in_array() { + let _ = env_logger::try_init(); + let arg_0_field = Argument { index: 0, value_type: ValueType::Field }; + let arg_1_field = Argument { index: 1, value_type: ValueType::Field }; + + let load_to_memory_block = + InstructionBlock { instructions: vec![Instruction::AddToMemory { lhs: arg_0_field }] }; + let set_to_memory_block = InstructionBlock { + instructions: vec![Instruction::SetToMemory { + memory_addr_index: 0, + value: arg_1_field, + }], + }; + let create_array_block = InstructionBlock { + instructions: vec![Instruction::CreateArray { + elements_indices: vec![0], + element_type: ValueType::Field, + is_references: true, + }], + }; + let get_from_array_block = InstructionBlock { + instructions: vec![Instruction::ArrayGet { array_index: 0, index: arg_0_field }], + }; + let typed_memory_1 = Argument { index: 1, value_type: ValueType::Field }; + let load_from_memory_block = InstructionBlock { + instructions: vec![Instruction::LoadFromMemory { memory_addr: typed_memory_1 }], + }; + let instructions_blocks = vec![ + load_to_memory_block, + create_array_block, + set_to_memory_block, + get_from_array_block, + load_from_memory_block, + ]; + let commands = vec![ + FuzzerFunctionCommand::InsertSimpleInstructionBlock { instruction_block_idx: 0 }, + FuzzerFunctionCommand::InsertSimpleInstructionBlock { instruction_block_idx: 1 }, + FuzzerFunctionCommand::InsertSimpleInstructionBlock { instruction_block_idx: 2 }, + FuzzerFunctionCommand::InsertSimpleInstructionBlock { instruction_block_idx: 3 }, + ]; + let main_func = FunctionData { + commands, + return_instruction_block_idx: 4, + return_type: ValueType::Field, + }; + let fuzzer_data = FuzzerData { + instruction_blocks: instructions_blocks, + functions: vec![main_func], + initial_witness: default_witness(), + }; + let result = fuzz_target(fuzzer_data, FuzzerOptions::default()); + match result { + Some(result) => assert_eq!(result.get_return_value(), FieldElement::from(1_u32)), + None => panic!("Program failed to execute"), + } + } } diff --git a/tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/options.rs b/tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/options.rs index 382ff56b348..36e69ec5cb5 100644 --- a/tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/options.rs +++ b/tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/options.rs @@ -143,7 +143,7 @@ pub(crate) struct FuzzerOptions { impl Default for FuzzerOptions { fn default() -> Self { Self { - compile_options: CompileOptions { show_ssa: false, ..Default::default() }, + compile_options: CompileOptions { show_ssa: true, ..Default::default() }, max_ssa_blocks_num: 100, max_instructions_num: 1000, max_iterations_num: 1000, diff --git a/tooling/ssa_fuzzer/src/builder.rs b/tooling/ssa_fuzzer/src/builder.rs index 2b0e20f5ec0..e2b3f966804 100644 --- a/tooling/ssa_fuzzer/src/builder.rs +++ b/tooling/ssa_fuzzer/src/builder.rs @@ -369,10 +369,21 @@ impl FuzzerBuilder { } /// Inserts a new array with the given elements and type - pub fn insert_array(&mut self, elements: Vec, typ: Type) -> TypedValue { + pub fn insert_array(&mut self, elements: Vec, is_references: bool) -> TypedValue { let array_length = elements.len() as u32; + assert!(array_length > 0, "Array must have at least one element"); + let element_type = elements[0].type_of_variable.clone(); + let array_elements_type = if is_references { + Type::Array( + Arc::new(vec![Type::Reference(Arc::new(element_type.clone()))]), + array_length, + ) + } else { + Type::Array(Arc::new(vec![element_type.clone()]), array_length) + }; + let typ = array_elements_type.clone(); assert!( - elements.iter().all(|e| e.type_of_variable == typ.clone()), + elements.iter().all(|e| e.type_of_variable == element_type), "All elements must have the same type" ); let res = self @@ -381,23 +392,8 @@ impl FuzzerBuilder { TypedValue::new(res, Type::Array(Arc::new(vec![typ]), array_length)) } - /*fn insert_index_lt_array_length(&mut self, index: TypedValue, array: TypedValue) -> TypedValue { - match array.type_of_variable { - Type::Array(_, array_length) => { - let array_length_id = self - .builder - .numeric_constant(array_length, NumericType::Unsigned { bit_size: 32 }); - let true_id = - self.builder.numeric_constant(1_u32, NumericType::Unsigned { bit_size: 1 }); - let index_lt_length = - self.builder.insert_binary(index.value_id, BinaryOp::Lt, array_length_id); - self.builder.insert_constrain(index_lt_length, true_id, None); - TypedValue::new(index_lt_length, ValueType::Boolean.to_ssa_type()) - } - _ => unreachable!("Array type expected"), - } - } */ - + /// Inserts a modulo operation between the index and the array length + /// Returns the id of the index modulo the array length fn insert_index_mod_array_length( &mut self, index: TypedValue, @@ -416,17 +412,22 @@ impl FuzzerBuilder { } } - pub fn insert_array_get(&mut self, array: TypedValue, index: TypedValue) -> TypedValue { - assert!(matches!(array.type_of_variable, Type::Array(_, _))); + pub fn insert_array_get( + &mut self, + array: TypedValue, + index: TypedValue, + element_type: Type, + ) -> TypedValue { assert!(index.type_of_variable == Type::Numeric(NumericType::Unsigned { bit_size: 32 })); let index_mod_len = self.insert_index_mod_array_length(index.clone(), array.clone()); + println!("index_mod_len: {:?}", index_mod_len); let res = self.builder.insert_array_get( array.value_id, index_mod_len.value_id, ArrayOffset::None, - array.type_of_variable.clone(), + element_type.clone(), ); - TypedValue::new(res, array.type_of_variable.clone()) + TypedValue::new(res, element_type) } pub fn insert_array_set( @@ -437,10 +438,6 @@ impl FuzzerBuilder { mutable: bool, ) -> TypedValue { assert!(matches!(array.type_of_variable, Type::Array(_, _))); - assert!( - value.type_of_variable == array.type_of_variable, - "Value type must match array type" - ); assert!(index.type_of_variable == Type::Numeric(NumericType::Unsigned { bit_size: 32 })); let index_mod_len = self.insert_index_mod_array_length(index.clone(), array.clone()); let res = self.builder.insert_array_set( From e6d21ce068d527e27ff3e54660d53ed4d9072c4f Mon Sep 17 00:00:00 2001 From: jewelofchaos9 Date: Wed, 6 Aug 2025 20:25:39 +0000 Subject: [PATCH 05/10] a --- tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/block_context.rs | 9 +++++++++ .../ssa_fuzzer/fuzzer/src/fuzz_lib/fuzz_target_lib.rs | 1 + 2 files changed, 10 insertions(+) diff --git a/tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/block_context.rs b/tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/block_context.rs index bc16fc11e1f..f199a367755 100644 --- a/tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/block_context.rs +++ b/tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/block_context.rs @@ -486,6 +486,9 @@ impl BlockContext { Some(elements) => elements, _ => return, }; + if elements.is_empty() { + return; + } // insert to both acir and brillig builders let array = acir_builder.insert_array(elements.clone(), is_references); assert_eq!( @@ -502,6 +505,9 @@ impl BlockContext { if !self.options.instruction_options.array_get_enabled { return; } + if self.stored_arrays.is_empty() { + return; + } // get the array from the stored arrays let stored_array = self.stored_arrays.get(array_index % self.stored_arrays.len()); let stored_array = match stored_array { @@ -563,6 +569,9 @@ impl BlockContext { if !self.options.instruction_options.array_set_enabled { return; } + if self.stored_arrays.is_empty() { + return; + } // get the array from the stored arrays let stored_array = self.stored_arrays.get(array_index % self.stored_arrays.len()); let stored_array = match stored_array { diff --git a/tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/fuzz_target_lib.rs b/tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/fuzz_target_lib.rs index b081cdac282..dc48b5ee231 100644 --- a/tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/fuzz_target_lib.rs +++ b/tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/fuzz_target_lib.rs @@ -839,6 +839,7 @@ mod tests { /// v11 = load v7 -> Field /// return v11 /// } + /// assert that return value is v1 #[test] fn test_reference_in_array() { let _ = env_logger::try_init(); From 8c980d5bd12dfe91268a3e9a9b13e71d2814f25a Mon Sep 17 00:00:00 2001 From: jewelofchaos9 Date: Thu, 7 Aug 2025 10:48:02 +0000 Subject: [PATCH 06/10] a --- .../fuzzer/src/fuzz_lib/block_context.rs | 139 +++++++++++++++--- .../fuzzer/src/fuzz_lib/fuzz_target_lib.rs | 38 +++-- .../fuzzer/src/fuzz_lib/instruction.rs | 14 +- tooling/ssa_fuzzer/src/builder.rs | 1 - 4 files changed, 160 insertions(+), 32 deletions(-) diff --git a/tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/block_context.rs b/tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/block_context.rs index f199a367755..9222d156a43 100644 --- a/tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/block_context.rs +++ b/tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/block_context.rs @@ -514,8 +514,11 @@ impl BlockContext { Some(stored_array) => stored_array, _ => return, }; + // references are not supported for array get with dynamic index + if stored_array.is_references { + return; + } let array_id = stored_array.array_id.clone(); - println!("array_id: {:?}", array_id); // get the index from the stored variables let index = get_typed_value_from_map( &self.stored_variables, @@ -532,7 +535,6 @@ impl BlockContext { index_casted.value_id, brillig_builder.insert_cast(index.clone(), ValueType::U32).value_id ); - println!("index: {:?}", index); // insert array get to both acir and brillig builders let value = acir_builder.insert_array_get( array_id.clone(), @@ -549,21 +551,11 @@ impl BlockContext { ) .value_id, ); - // if we storing references, append the value to memory addresses, otherwise to stored variables - if stored_array.is_references { - append_typed_value_to_map( - &mut self.memory_addresses, - &value.to_value_type(), - value.clone(), - ); - } else { - append_typed_value_to_map( - &mut self.stored_variables, - &value.to_value_type(), - value.clone(), - ); - } - println!("array_get: {:?}", value); + append_typed_value_to_map( + &mut self.stored_variables, + &value.to_value_type(), + value.clone(), + ); } Instruction::ArraySet { array_index, index, value_index, mutable } => { if !self.options.instruction_options.array_set_enabled { @@ -578,7 +570,10 @@ impl BlockContext { Some(stored_array) => stored_array, _ => return, }; - println!("stored_array: {:?}", stored_array); + // references are not supported for array set with dynamic index + if stored_array.is_references { + return; + } let array_id = stored_array.array_id.clone(); // get the index from the stored variables @@ -628,6 +623,113 @@ impl BlockContext { is_references: stored_array.is_references, }); } + Instruction::ArrayGetWithConstantIndex { array_index, index } => { + if !self.options.instruction_options.array_get_enabled { + return; + } + println!("array_index: {:?}", array_index); + println!("index: {:?}", index); + if self.stored_arrays.is_empty() { + return; + } + println!("stored_arrays: {:?}", self.stored_arrays); + // get the array from the stored arrays + let stored_array = self.stored_arrays.get(array_index % self.stored_arrays.len()); + let stored_array = match stored_array { + Some(stored_array) => stored_array, + _ => return, + }; + let index_id = acir_builder.insert_constant(index, ValueType::U32); + assert_eq!( + index_id.value_id, + brillig_builder.insert_constant(index, ValueType::U32).value_id + ); + let value = acir_builder.insert_array_get( + stored_array.array_id.clone(), + index_id.clone(), + stored_array.element_type.to_ssa_type(), + ); + assert_eq!( + value.value_id, + brillig_builder + .insert_array_get( + stored_array.array_id.clone(), + index_id.clone(), + stored_array.element_type.to_ssa_type() + ) + .value_id + ); + if !stored_array.is_references { + append_typed_value_to_map( + &mut self.stored_variables, + &value.to_value_type(), + value.clone(), + ); + } else { + append_typed_value_to_map( + &mut self.memory_addresses, + &value.to_value_type(), + value.clone(), + ); + } + println!("value: {:?}", value); + println!("memory_addresses: {:?}", self.memory_addresses); + } + Instruction::ArraySetWithConstantIndex { array_index, index, value_index, mutable } => { + if !self.options.instruction_options.array_set_enabled { + return; + } + if self.stored_arrays.is_empty() { + return; + } + // get the array from the stored arrays + let stored_array = self.stored_arrays.get(array_index % self.stored_arrays.len()); + let stored_array = match stored_array { + Some(stored_array) => stored_array, + _ => return, + }; + let index_id = acir_builder.insert_constant(index, ValueType::U32); + assert_eq!( + index_id.value_id, + brillig_builder.insert_constant(index, ValueType::U32).value_id + ); + // get the value from the stored variables if not references, otherwise from memory addresses + let value = if !stored_array.is_references { + get_typed_value_from_map( + &self.stored_variables, + &stored_array.element_type, + value_index, + ) + } else { + get_typed_value_from_map( + &self.memory_addresses, + &stored_array.element_type, + value_index, + ) + }; + let value = match value { + Some(value) => value, + _ => return, + }; + // insert array set to both acir and brillig builders + let new_array = acir_builder.insert_array_set( + stored_array.array_id.clone(), + index_id.clone(), + value.clone(), + mutable, + ); + assert_eq!( + new_array.value_id, + brillig_builder + .insert_array_set(stored_array.array_id.clone(), index_id, value, mutable) + .value_id + ); + self.stored_arrays.push(StoredArray { + array_id: new_array, + element_type: stored_array.element_type, + is_references: stored_array.is_references, + }); + } } } @@ -638,6 +740,7 @@ impl BlockContext { instructions: &Vec, ) { for instruction in instructions { + println!("instruction: {:?}", instruction); self.insert_instruction(acir_builder, brillig_builder, instruction.clone()); } } diff --git a/tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/fuzz_target_lib.rs b/tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/fuzz_target_lib.rs index dc48b5ee231..72d140db14d 100644 --- a/tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/fuzz_target_lib.rs +++ b/tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/fuzz_target_lib.rs @@ -830,13 +830,14 @@ mod tests { /// b0(v0: Field, v1: Field, v2: Field, v3: Field, v4: Field, v5: u1, v6: u1): /// v7 = allocate -> &mut Field /// store v0 at v7 - /// v8 = make_array [v7] : [&mut Field; 1] + /// v8 = allocate -> &mut Field + /// store v2 at v8 + /// v9 = make_array [v7, v8, v7] : [&mut Field; 3] /// store v1 at v7 - /// v9 = truncate v0 to 32 bits, max_bit_size: 254 - /// v10 = cast v9 as u32 + /// v10 = make_array [v7, v7, v7] : [&mut Field; 3] <----- set v9, index 1, value v7 /// jmp b1() /// b1(): - /// v11 = load v7 -> Field + /// v11 = load v7 -> Field <---- its optimized load v10[1] /// return v11 /// } /// assert that return value is v1 @@ -845,33 +846,46 @@ mod tests { let _ = env_logger::try_init(); let arg_0_field = Argument { index: 0, value_type: ValueType::Field }; let arg_1_field = Argument { index: 1, value_type: ValueType::Field }; + let arg_2_field = Argument { index: 2, value_type: ValueType::Field }; - let load_to_memory_block = + let add_to_memory_block = InstructionBlock { instructions: vec![Instruction::AddToMemory { lhs: arg_0_field }] }; + let add_to_memory_block_2 = + InstructionBlock { instructions: vec![Instruction::AddToMemory { lhs: arg_2_field }] }; let set_to_memory_block = InstructionBlock { instructions: vec![Instruction::SetToMemory { memory_addr_index: 0, value: arg_1_field, }], }; + let set_to_array_block = InstructionBlock { + instructions: vec![Instruction::ArraySetWithConstantIndex { + array_index: 0, + index: 1, + value_index: 0, + mutable: false, + }], + }; let create_array_block = InstructionBlock { instructions: vec![Instruction::CreateArray { - elements_indices: vec![0], + elements_indices: vec![0, 1, 2], element_type: ValueType::Field, is_references: true, }], }; let get_from_array_block = InstructionBlock { - instructions: vec![Instruction::ArrayGet { array_index: 0, index: arg_0_field }], + instructions: vec![Instruction::ArrayGetWithConstantIndex { array_index: 1, index: 1 }], }; - let typed_memory_1 = Argument { index: 1, value_type: ValueType::Field }; + let typed_memory_2 = Argument { index: 2, value_type: ValueType::Field }; let load_from_memory_block = InstructionBlock { - instructions: vec![Instruction::LoadFromMemory { memory_addr: typed_memory_1 }], + instructions: vec![Instruction::LoadFromMemory { memory_addr: typed_memory_2 }], }; let instructions_blocks = vec![ - load_to_memory_block, + add_to_memory_block, + add_to_memory_block_2, create_array_block, set_to_memory_block, + set_to_array_block, get_from_array_block, load_from_memory_block, ]; @@ -880,10 +894,12 @@ mod tests { FuzzerFunctionCommand::InsertSimpleInstructionBlock { instruction_block_idx: 1 }, FuzzerFunctionCommand::InsertSimpleInstructionBlock { instruction_block_idx: 2 }, FuzzerFunctionCommand::InsertSimpleInstructionBlock { instruction_block_idx: 3 }, + FuzzerFunctionCommand::InsertSimpleInstructionBlock { instruction_block_idx: 4 }, + FuzzerFunctionCommand::InsertSimpleInstructionBlock { instruction_block_idx: 5 }, ]; let main_func = FunctionData { commands, - return_instruction_block_idx: 4, + return_instruction_block_idx: 6, return_type: ValueType::Field, }; let fuzzer_data = FuzzerData { diff --git a/tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/instruction.rs b/tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/instruction.rs index f68706d7b38..71436b490b1 100644 --- a/tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/instruction.rs +++ b/tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/instruction.rs @@ -78,11 +78,21 @@ pub(crate) enum Instruction { /// Create array, only type of first argument is used /// Other elements will be taken from stored variables of the same type CreateArray { elements_indices: Vec, element_type: ValueType, is_references: bool }, - /// Get element from array, index will be casted to u32 + /// Get element from array, index will be casted to u32, only for arrays without references ArrayGet { array_index: usize, index: Argument }, - /// Set element in array, index will be casted to u32 + /// Set element in array, index will be casted to u32, only for arrays without references /// Value will be casted to the type of the array ArraySet { array_index: usize, index: Argument, value_index: usize, mutable: bool }, + /// Get element from array, index is constant + ArrayGetWithConstantIndex { array_index: usize, index: usize }, + /// Set element in array, index is constant + /// Value will be casted to the type of the array + ArraySetWithConstantIndex { + array_index: usize, + index: usize, + value_index: usize, + mutable: bool, + }, } /// Represents set of instructions diff --git a/tooling/ssa_fuzzer/src/builder.rs b/tooling/ssa_fuzzer/src/builder.rs index e2b3f966804..6d78a0ee48e 100644 --- a/tooling/ssa_fuzzer/src/builder.rs +++ b/tooling/ssa_fuzzer/src/builder.rs @@ -420,7 +420,6 @@ impl FuzzerBuilder { ) -> TypedValue { assert!(index.type_of_variable == Type::Numeric(NumericType::Unsigned { bit_size: 32 })); let index_mod_len = self.insert_index_mod_array_length(index.clone(), array.clone()); - println!("index_mod_len: {:?}", index_mod_len); let res = self.builder.insert_array_get( array.value_id, index_mod_len.value_id, From faf027035195cd643ab377d97f435df9fb885844 Mon Sep 17 00:00:00 2001 From: jewelofchaos9 Date: Thu, 7 Aug 2025 10:59:02 +0000 Subject: [PATCH 07/10] b --- tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/block_context.rs | 6 ------ tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/fuzz_target_lib.rs | 2 +- tooling/ssa_fuzzer/src/builder.rs | 6 ++++++ 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/block_context.rs b/tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/block_context.rs index 9222d156a43..0ed16008656 100644 --- a/tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/block_context.rs +++ b/tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/block_context.rs @@ -627,12 +627,9 @@ impl BlockContext { if !self.options.instruction_options.array_get_enabled { return; } - println!("array_index: {:?}", array_index); - println!("index: {:?}", index); if self.stored_arrays.is_empty() { return; } - println!("stored_arrays: {:?}", self.stored_arrays); // get the array from the stored arrays let stored_array = self.stored_arrays.get(array_index % self.stored_arrays.len()); let stored_array = match stored_array { @@ -672,8 +669,6 @@ impl BlockContext { value.clone(), ); } - println!("value: {:?}", value); - println!("memory_addresses: {:?}", self.memory_addresses); } Instruction::ArraySetWithConstantIndex { array_index, index, value_index, mutable } => { if !self.options.instruction_options.array_set_enabled { @@ -740,7 +735,6 @@ impl BlockContext { instructions: &Vec, ) { for instruction in instructions { - println!("instruction: {:?}", instruction); self.insert_instruction(acir_builder, brillig_builder, instruction.clone()); } } diff --git a/tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/fuzz_target_lib.rs b/tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/fuzz_target_lib.rs index 72d140db14d..4f6cde6db0b 100644 --- a/tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/fuzz_target_lib.rs +++ b/tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/fuzz_target_lib.rs @@ -837,7 +837,7 @@ mod tests { /// v10 = make_array [v7, v7, v7] : [&mut Field; 3] <----- set v9, index 1, value v7 /// jmp b1() /// b1(): - /// v11 = load v7 -> Field <---- its optimized load v10[1] + /// v11 = load v7 -> Field <---- its simplified from v11 = v10[1]; v12 = load v11 /// return v11 /// } /// assert that return value is v1 diff --git a/tooling/ssa_fuzzer/src/builder.rs b/tooling/ssa_fuzzer/src/builder.rs index 6d78a0ee48e..29af693ef2b 100644 --- a/tooling/ssa_fuzzer/src/builder.rs +++ b/tooling/ssa_fuzzer/src/builder.rs @@ -412,6 +412,9 @@ impl FuzzerBuilder { } } + /// Inserts an array get instruction + /// + /// Index must be u32 and will be taken modulo the array length pub fn insert_array_get( &mut self, array: TypedValue, @@ -429,6 +432,9 @@ impl FuzzerBuilder { TypedValue::new(res, element_type) } + /// Inserts an array set instruction + /// + /// Index must be u32 and will be taken modulo the array length pub fn insert_array_set( &mut self, array: TypedValue, From 62e9550f83dfe644ae95268520e8272292ed88e9 Mon Sep 17 00:00:00 2001 From: defkit <84741533+defkit@users.noreply.github.com> Date: Thu, 7 Aug 2025 13:25:37 +0000 Subject: [PATCH 08/10] Update tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/instruction.rs Co-authored-by: Innokentii Sennovskii --- tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/instruction.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/instruction.rs b/tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/instruction.rs index 71436b490b1..b789201ddef 100644 --- a/tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/instruction.rs +++ b/tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/instruction.rs @@ -81,7 +81,7 @@ pub(crate) enum Instruction { /// Get element from array, index will be casted to u32, only for arrays without references ArrayGet { array_index: usize, index: Argument }, /// Set element in array, index will be casted to u32, only for arrays without references - /// Value will be casted to the type of the array + /// Value will be cast to the type of the array ArraySet { array_index: usize, index: Argument, value_index: usize, mutable: bool }, /// Get element from array, index is constant ArrayGetWithConstantIndex { array_index: usize, index: usize }, From 827a12701107fe0a4e6405a0263d368251c5b9ae Mon Sep 17 00:00:00 2001 From: defkit <84741533+defkit@users.noreply.github.com> Date: Thu, 7 Aug 2025 13:25:51 +0000 Subject: [PATCH 09/10] Update tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/instruction.rs Co-authored-by: Innokentii Sennovskii --- tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/instruction.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/instruction.rs b/tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/instruction.rs index b789201ddef..7c829a530fd 100644 --- a/tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/instruction.rs +++ b/tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/instruction.rs @@ -86,7 +86,7 @@ pub(crate) enum Instruction { /// Get element from array, index is constant ArrayGetWithConstantIndex { array_index: usize, index: usize }, /// Set element in array, index is constant - /// Value will be casted to the type of the array + /// Value will be cast to the type of the array ArraySetWithConstantIndex { array_index: usize, index: usize, From d304ae436da48e6faac7a27f1c05fcd1dc3da653 Mon Sep 17 00:00:00 2001 From: jewelofchaos9 Date: Fri, 8 Aug 2025 11:52:07 +0000 Subject: [PATCH 10/10] final --- .../fuzzer/src/fuzz_lib/block_context.rs | 44 ++++++++++++++----- .../fuzzer/src/fuzz_lib/fuzz_target_lib.rs | 14 +++++- .../fuzzer/src/fuzz_lib/instruction.rs | 17 +++++-- .../instructions/instruction_mutator.rs | 8 +++- tooling/ssa_fuzzer/src/builder.rs | 24 +++++++--- 5 files changed, 85 insertions(+), 22 deletions(-) diff --git a/tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/block_context.rs b/tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/block_context.rs index 0ed16008656..267d0fba9b1 100644 --- a/tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/block_context.rs +++ b/tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/block_context.rs @@ -501,8 +501,10 @@ impl BlockContext { is_references, }); } - Instruction::ArrayGet { array_index, index } => { - if !self.options.instruction_options.array_get_enabled { + Instruction::ArrayGet { array_index, index, safe_index } => { + if !self.options.instruction_options.array_get_enabled + || !self.options.instruction_options.create_array_enabled + { return; } if self.stored_arrays.is_empty() { @@ -540,6 +542,7 @@ impl BlockContext { array_id.clone(), index_casted.clone(), stored_array.element_type.to_ssa_type(), + safe_index, ); assert_eq!( value.value_id, @@ -548,6 +551,7 @@ impl BlockContext { array_id.clone(), index_casted.clone(), stored_array.element_type.to_ssa_type(), + safe_index, ) .value_id, ); @@ -557,7 +561,7 @@ impl BlockContext { value.clone(), ); } - Instruction::ArraySet { array_index, index, value_index, mutable } => { + Instruction::ArraySet { array_index, index, value_index, mutable, safe_index } => { if !self.options.instruction_options.array_set_enabled { return; } @@ -609,11 +613,12 @@ impl BlockContext { index_casted.clone(), value.clone(), mutable, + safe_index, ); assert_eq!( new_array.value_id, brillig_builder - .insert_array_set(array_id, index_casted, value, mutable) + .insert_array_set(array_id, index_casted, value, mutable, safe_index) .value_id ); @@ -623,8 +628,10 @@ impl BlockContext { is_references: stored_array.is_references, }); } - Instruction::ArrayGetWithConstantIndex { array_index, index } => { - if !self.options.instruction_options.array_get_enabled { + Instruction::ArrayGetWithConstantIndex { array_index, index, safe_index } => { + if !self.options.instruction_options.array_get_enabled + || !self.options.instruction_options.create_array_enabled + { return; } if self.stored_arrays.is_empty() { @@ -645,6 +652,7 @@ impl BlockContext { stored_array.array_id.clone(), index_id.clone(), stored_array.element_type.to_ssa_type(), + safe_index, ); assert_eq!( value.value_id, @@ -652,7 +660,8 @@ impl BlockContext { .insert_array_get( stored_array.array_id.clone(), index_id.clone(), - stored_array.element_type.to_ssa_type() + stored_array.element_type.to_ssa_type(), + safe_index, ) .value_id ); @@ -670,8 +679,16 @@ impl BlockContext { ); } } - Instruction::ArraySetWithConstantIndex { array_index, index, value_index, mutable } => { - if !self.options.instruction_options.array_set_enabled { + Instruction::ArraySetWithConstantIndex { + array_index, + index, + value_index, + mutable, + safe_index, + } => { + if !self.options.instruction_options.array_set_enabled + || !self.options.instruction_options.create_array_enabled + { return; } if self.stored_arrays.is_empty() { @@ -712,11 +729,18 @@ impl BlockContext { index_id.clone(), value.clone(), mutable, + safe_index, ); assert_eq!( new_array.value_id, brillig_builder - .insert_array_set(stored_array.array_id.clone(), index_id, value, mutable) + .insert_array_set( + stored_array.array_id.clone(), + index_id, + value, + mutable, + safe_index, + ) .value_id ); self.stored_arrays.push(StoredArray { diff --git a/tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/fuzz_target_lib.rs b/tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/fuzz_target_lib.rs index 4f6cde6db0b..530c7c8ce20 100644 --- a/tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/fuzz_target_lib.rs +++ b/tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/fuzz_target_lib.rs @@ -797,11 +797,16 @@ mod tests { index: arg_0_field, value_index: 4, // set v4 mutable: false, + safe_index: true, }], }; // get new_array[0] let array_get_block = InstructionBlock { - instructions: vec![Instruction::ArrayGet { array_index: 1, index: arg_0_field }], + instructions: vec![Instruction::ArrayGet { + array_index: 1, + index: arg_0_field, + safe_index: true, + }], }; let instructions_blocks = vec![create_array_block, array_set_block, array_get_block]; let commands = vec![ @@ -864,6 +869,7 @@ mod tests { index: 1, value_index: 0, mutable: false, + safe_index: true, }], }; let create_array_block = InstructionBlock { @@ -874,7 +880,11 @@ mod tests { }], }; let get_from_array_block = InstructionBlock { - instructions: vec![Instruction::ArrayGetWithConstantIndex { array_index: 1, index: 1 }], + instructions: vec![Instruction::ArrayGetWithConstantIndex { + array_index: 1, + index: 1, + safe_index: true, + }], }; let typed_memory_2 = Argument { index: 2, value_type: ValueType::Field }; let load_from_memory_block = InstructionBlock { diff --git a/tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/instruction.rs b/tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/instruction.rs index 7c829a530fd..95b71fab8cb 100644 --- a/tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/instruction.rs +++ b/tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/instruction.rs @@ -79,19 +79,30 @@ pub(crate) enum Instruction { /// Other elements will be taken from stored variables of the same type CreateArray { elements_indices: Vec, element_type: ValueType, is_references: bool }, /// Get element from array, index will be casted to u32, only for arrays without references - ArrayGet { array_index: usize, index: Argument }, + /// If safe_index is true, index will be taken modulo the size of the array + ArrayGet { array_index: usize, index: Argument, safe_index: bool }, /// Set element in array, index will be casted to u32, only for arrays without references /// Value will be cast to the type of the array - ArraySet { array_index: usize, index: Argument, value_index: usize, mutable: bool }, + /// If safe_index is true, index will be taken modulo the size of the array + ArraySet { + array_index: usize, + index: Argument, + value_index: usize, + mutable: bool, + safe_index: bool, + }, /// Get element from array, index is constant - ArrayGetWithConstantIndex { array_index: usize, index: usize }, + /// If safe_index is true, index will be taken modulo the size of the array + ArrayGetWithConstantIndex { array_index: usize, index: usize, safe_index: bool }, /// Set element in array, index is constant /// Value will be cast to the type of the array + /// If safe_index is true, index will be taken modulo the size of the array ArraySetWithConstantIndex { array_index: usize, index: usize, value_index: usize, mutable: bool, + safe_index: bool, }, } diff --git a/tooling/ssa_fuzzer/fuzzer/src/mutations/instructions/instruction_mutator.rs b/tooling/ssa_fuzzer/fuzzer/src/mutations/instructions/instruction_mutator.rs index 9751cde412e..3c1f88e1c3a 100644 --- a/tooling/ssa_fuzzer/fuzzer/src/mutations/instructions/instruction_mutator.rs +++ b/tooling/ssa_fuzzer/fuzzer/src/mutations/instructions/instruction_mutator.rs @@ -105,7 +105,13 @@ impl InstructionMutator for InstructionArgumentsMutation { } } } - _ => {} + + // TODO(sn): Implement mutations for array instructions + Instruction::ArrayGet { .. } + | Instruction::ArraySet { .. } + | Instruction::CreateArray { .. } + | Instruction::ArrayGetWithConstantIndex { .. } + | Instruction::ArraySetWithConstantIndex { .. } => {} } } } diff --git a/tooling/ssa_fuzzer/src/builder.rs b/tooling/ssa_fuzzer/src/builder.rs index 29af693ef2b..1286d587325 100644 --- a/tooling/ssa_fuzzer/src/builder.rs +++ b/tooling/ssa_fuzzer/src/builder.rs @@ -414,18 +414,24 @@ impl FuzzerBuilder { /// Inserts an array get instruction /// - /// Index must be u32 and will be taken modulo the array length + /// Index must be u32 + /// If safe_index is true, index will be taken modulo the array length pub fn insert_array_get( &mut self, array: TypedValue, index: TypedValue, element_type: Type, + safe_index: bool, ) -> TypedValue { assert!(index.type_of_variable == Type::Numeric(NumericType::Unsigned { bit_size: 32 })); - let index_mod_len = self.insert_index_mod_array_length(index.clone(), array.clone()); + let index = if safe_index { + self.insert_index_mod_array_length(index.clone(), array.clone()) + } else { + index + }; let res = self.builder.insert_array_get( array.value_id, - index_mod_len.value_id, + index.value_id, ArrayOffset::None, element_type.clone(), ); @@ -434,20 +440,26 @@ impl FuzzerBuilder { /// Inserts an array set instruction /// - /// Index must be u32 and will be taken modulo the array length + /// Index must be u32 + /// If safe_index is true, index will be taken modulo the array length pub fn insert_array_set( &mut self, array: TypedValue, index: TypedValue, value: TypedValue, mutable: bool, + safe_index: bool, ) -> TypedValue { assert!(matches!(array.type_of_variable, Type::Array(_, _))); assert!(index.type_of_variable == Type::Numeric(NumericType::Unsigned { bit_size: 32 })); - let index_mod_len = self.insert_index_mod_array_length(index.clone(), array.clone()); + let index = if safe_index { + self.insert_index_mod_array_length(index.clone(), array.clone()) + } else { + index + }; let res = self.builder.insert_array_set( array.value_id, - index_mod_len.value_id, + index.value_id, value.value_id, mutable, ArrayOffset::None,