From fdb07dbf2e70648ebfa4fcdef039e62dbb6ac51e Mon Sep 17 00:00:00 2001 From: Federica Date: Thu, 9 May 2024 17:50:08 -0300 Subject: [PATCH 01/12] Load arguments into VM instead of creating them via cairo instructions --- cairo1-run/src/cairo_run.rs | 124 ++++++++++++++++++++---------------- 1 file changed, 68 insertions(+), 56 deletions(-) diff --git a/cairo1-run/src/cairo_run.rs b/cairo1-run/src/cairo_run.rs index 5d4edfb799..c4a7f746e4 100644 --- a/cairo1-run/src/cairo_run.rs +++ b/cairo1-run/src/cairo_run.rs @@ -218,6 +218,13 @@ pub fn cairo_run_program( let mut runner = CairoRunner::new_v2(&program, cairo_run_config.layout, runner_mode)?; let mut vm = VirtualMachine::new(cairo_run_config.trace_enabled); let end = runner.initialize(&mut vm, cairo_run_config.proof_mode)?; + load_arguments( + &mut vm, + &runner, + &cairo_run_config, + main_func, + &sierra_program_registry, + )?; // Run it until the end / infinite loop in proof_mode runner.run_until_pc(end, &mut vm, &mut hint_processor)?; @@ -340,6 +347,62 @@ fn create_code_footer() -> Vec { .instructions } +fn load_arguments( + vm: &mut VirtualMachine, + runner: &CairoRunner, + cairo_run_config: &Cairo1RunConfig, + main_func: &Function, + sierra_program_registry: &ProgramRegistry, +) -> Result<(), Error> { + let got_segment_arena = main_func.signature.param_types.iter().any(|ty| { + get_info(sierra_program_registry, ty) + .map(|x| x.long_id.generic_id == SegmentArenaType::ID) + .unwrap_or_default() + }); + let append_output = cairo_run_config.append_return_values || cairo_run_config.proof_mode; + // builtin bases (minus output) + segment arena (aka 3 + builtins) + let mut ap_offset = runner.get_program().builtins_len(); + if append_output { + ap_offset -= 1; + } + if got_segment_arena { + ap_offset += 3; + if append_output { + ap_offset += runner.get_program().builtins_len(); + } + } + for arg in cairo_run_config.args { + match arg { + FuncArg::Array(args) => { + let array_start = vm.add_memory_segment(); + let array_end = vm.load_data( + array_start, + &args.iter().map(|f| f.into()).collect::>(), + )?; + vm.insert_value( + (vm.get_ap() + ap_offset).map_err(VirtualMachineError::Math)?, + array_start, + )?; + ap_offset += 1; + vm.insert_value( + (vm.get_ap() + ap_offset).map_err(VirtualMachineError::Math)?, + array_end, + )?; + ap_offset += 1; + } + FuncArg::Single(arg) => { + vm.insert_value( + (vm.get_ap() + ap_offset).map_err(VirtualMachineError::Math)?, + arg, + )?; + ap_offset += 1; + } + } + } + + Ok(()) +} + /// Returns the instructions to add to the beginning of the code to successfully call the main /// function, as well as the builtins required to execute the program. fn create_entry_code( @@ -381,30 +444,6 @@ fn create_entry_code( } casm_build_extend!(ctx, ap += builtins.len();); } - // Load all vecs to memory. - // Load all array args content to memory. - let mut array_args_data = vec![]; - for arg in config.args { - let FuncArg::Array(values) = arg else { - continue; - }; - casm_build_extend! {ctx, - tempvar arr; - hint AllocSegment {} into {dst: arr}; - ap += 1; - }; - array_args_data.push(arr); - for (i, v) in values.iter().enumerate() { - casm_build_extend! {ctx, - const cvalue = v.to_bigint(); - tempvar value = cvalue; - assert value = arr[i.to_i16().unwrap()]; - }; - } - } - let mut array_args_data_iter = array_args_data.into_iter(); - let mut arg_iter = config.args.iter().enumerate(); - let mut param_index = 0; let mut expected_arguments_size = 0; if got_segment_arena { // Allocating the segment arena and initializing it. @@ -443,39 +482,12 @@ fn create_entry_code( }; } else { let ty_size = type_sizes[ty]; - let mut param_accum_size = 0; + // We already loaded these arguments + casm_build_extend!(ctx, + ap+=ty_size as usize; + ); expected_arguments_size += ty_size; - while param_accum_size < ty_size { - let Some((arg_index, arg)) = arg_iter.next() else { - break; - }; - match arg { - FuncArg::Single(value) => { - casm_build_extend! {ctx, - const value = value.to_bigint(); - tempvar _value = value; - }; - param_accum_size += 1; - } - FuncArg::Array(values) => { - let var = array_args_data_iter.next().unwrap(); - casm_build_extend! {ctx, - const length = values.len(); - tempvar start = var; - tempvar end = var + length; - }; - param_accum_size += 2; - if param_accum_size > ty_size { - return Err(Error::ArgumentUnaligned { - param_index, - arg_index, - }); - } - } - } - } - param_index += 1; - }; + } } let actual_args_size = config .args From 6e6b6e7b69c83a2d29be5a76b8c6a5523568b8ab Mon Sep 17 00:00:00 2001 From: Federica Date: Thu, 9 May 2024 17:54:37 -0300 Subject: [PATCH 02/12] Update comment --- cairo1-run/src/cairo_run.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cairo1-run/src/cairo_run.rs b/cairo1-run/src/cairo_run.rs index c4a7f746e4..381368a63e 100644 --- a/cairo1-run/src/cairo_run.rs +++ b/cairo1-run/src/cairo_run.rs @@ -482,7 +482,7 @@ fn create_entry_code( }; } else { let ty_size = type_sizes[ty]; - // We already loaded these arguments + // We already loaded these arguments, so we just advance AP casm_build_extend!(ctx, ap+=ty_size as usize; ); From b96c14ca25bbfc153b747e26f4578ec8e06bbe08 Mon Sep 17 00:00:00 2001 From: Federica Date: Thu, 9 May 2024 17:57:12 -0300 Subject: [PATCH 03/12] Add program hash check test --- cairo1-run/src/cairo_run.rs | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/cairo1-run/src/cairo_run.rs b/cairo1-run/src/cairo_run.rs index 381368a63e..54d147ea16 100644 --- a/cairo1-run/src/cairo_run.rs +++ b/cairo1-run/src/cairo_run.rs @@ -1165,7 +1165,7 @@ mod tests { use cairo_lang_compiler::{ compile_prepared_db, db::RootDatabase, project::setup_project, CompilerConfig, }; - use cairo_vm::types::relocatable::Relocatable; + use cairo_vm::{program_hash::compute_program_hash_chain, types::relocatable::Relocatable}; use rstest::rstest; fn compile_to_sierra(filename: &str) -> SierraProgram { @@ -1253,4 +1253,35 @@ mod tests { .get_maybe(&Relocatable::from((2_isize, return_values.len()))) .is_none()); } + #[test] + fn check_program_hash_doesnt_change_based_on_arguments() { + let sierra_program = compile_to_sierra( + "../cairo_programs/cairo-1-programs/with_input/array_input_sum.cairo", + ); + let config_a = Cairo1RunConfig { + layout: LayoutName::all_cairo, + args: &[FuncArg::Single(Felt252::ONE), + FuncArg::Array(vec![Felt252::ONE, Felt252::TWO, Felt252::THREE]), + FuncArg::Single(Felt252::TWO), + FuncArg::Array(vec![Felt252::ONE, Felt252::TWO, Felt252::THREE])], + ..Default::default() + }; + let config_b = Cairo1RunConfig { + layout: LayoutName::all_cairo, + args: &[FuncArg::Single(Felt252::ZERO), + FuncArg::Array(vec![Felt252::THREE]), + FuncArg::Single(Felt252::ZERO), + FuncArg::Array(vec![Felt252::TWO])], + ..Default::default() + }; + let runner_a = cairo_run_program(&sierra_program, config_a).unwrap().0; + let runner_b = cairo_run_program(&sierra_program, config_b).unwrap().0; + let hash_a = + compute_program_hash_chain(&runner_a.get_program().get_stripped_program().unwrap(), 0) + .unwrap(); + let hash_b = + compute_program_hash_chain(&runner_b.get_program().get_stripped_program().unwrap(), 0) + .unwrap(); + assert_eq!(hash_a, hash_b) + } } From f206c8f7ed29ff63040646c34338174519ed7632 Mon Sep 17 00:00:00 2001 From: Federica Date: Thu, 9 May 2024 18:10:34 -0300 Subject: [PATCH 04/12] Doc migration --- cairo1-run/src/cairo_run.rs | 55 +++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/cairo1-run/src/cairo_run.rs b/cairo1-run/src/cairo_run.rs index 54d147ea16..7e20f03278 100644 --- a/cairo1-run/src/cairo_run.rs +++ b/cairo1-run/src/cairo_run.rs @@ -347,6 +347,61 @@ fn create_code_footer() -> Vec { .instructions } +// Loads the input arguments into the execution segment, leaving the necessary gaps for the values that will be written by +// the instructions in the entry_code (produced by `create_entry_code`) + +/* Example of execution segment before running the main function: +Before calling this function (after runner.initialize): +[ + (*1) output_builtin_base + builtin_base_0 + builtin_base_1 + return_fp + return_pc +] +After calling this function (before running the VM): +[ + (*1) output_builtin_base + builtin_base_0 + builtin_base_1 + return_fp + return_pc + (*1+2+3) gap + (*1+2) gap + (*1+2) gap + (*2) gap + (*2) gap + (*2) gap + gap + gap + (*2) gap + (*3) arg_0 + (*3) arg_1 +] + +After the entry_code (up until calling main) has been ran by the VM: +[ + (*1) output_builtin_base + builtin_base_0 + builtin_base_1 + return_fp + return_pc + (*1+2+3) gap (for output_builtin final ptr) + (*1+2) gap (for builtin_0 final ptr) + (*1+2) gap (for builtin_1 final ptr) + (*2) segment_arena_ptr + (*2) infos_ptr + (*2) 0 + builtin_base_0 + builtin_base_1 + (*2) segment_arena_ptr + 3 (segment_arena base) + (*3) arg_0 + (*3) arg_1 +] +(*1) if output builtin is added (if either proof_mode or append_return_values is enabled) +(*2) if segment arena is present +(*3) if args are used +*/ fn load_arguments( vm: &mut VirtualMachine, runner: &CairoRunner, From 8477cb94356854742e3502d5ff73e9654ef27e94 Mon Sep 17 00:00:00 2001 From: Federica Date: Thu, 9 May 2024 18:42:20 -0300 Subject: [PATCH 05/12] fmt --- cairo1-run/src/cairo_run.rs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/cairo1-run/src/cairo_run.rs b/cairo1-run/src/cairo_run.rs index 7e20f03278..fbddc426c3 100644 --- a/cairo1-run/src/cairo_run.rs +++ b/cairo1-run/src/cairo_run.rs @@ -347,7 +347,7 @@ fn create_code_footer() -> Vec { .instructions } -// Loads the input arguments into the execution segment, leaving the necessary gaps for the values that will be written by +// Loads the input arguments into the execution segment, leaving the necessary gaps for the values that will be written by // the instructions in the entry_code (produced by `create_entry_code`) /* Example of execution segment before running the main function: @@ -1315,18 +1315,22 @@ mod tests { ); let config_a = Cairo1RunConfig { layout: LayoutName::all_cairo, - args: &[FuncArg::Single(Felt252::ONE), + args: &[ + FuncArg::Single(Felt252::ONE), FuncArg::Array(vec![Felt252::ONE, Felt252::TWO, Felt252::THREE]), FuncArg::Single(Felt252::TWO), - FuncArg::Array(vec![Felt252::ONE, Felt252::TWO, Felt252::THREE])], + FuncArg::Array(vec![Felt252::ONE, Felt252::TWO, Felt252::THREE]), + ], ..Default::default() }; let config_b = Cairo1RunConfig { layout: LayoutName::all_cairo, - args: &[FuncArg::Single(Felt252::ZERO), + args: &[ + FuncArg::Single(Felt252::ZERO), FuncArg::Array(vec![Felt252::THREE]), FuncArg::Single(Felt252::ZERO), - FuncArg::Array(vec![Felt252::TWO])], + FuncArg::Array(vec![Felt252::TWO]), + ], ..Default::default() }; let runner_a = cairo_run_program(&sierra_program, config_a).unwrap().0; From e725f4723d0d21365b7ef65c1e663de7676d76ea Mon Sep 17 00:00:00 2001 From: Federica Date: Thu, 9 May 2024 19:07:34 -0300 Subject: [PATCH 06/12] Improve comments --- cairo1-run/src/cairo_run.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/cairo1-run/src/cairo_run.rs b/cairo1-run/src/cairo_run.rs index fbddc426c3..a4ce82b919 100644 --- a/cairo1-run/src/cairo_run.rs +++ b/cairo1-run/src/cairo_run.rs @@ -415,7 +415,14 @@ fn load_arguments( .unwrap_or_default() }); let append_output = cairo_run_config.append_return_values || cairo_run_config.proof_mode; - // builtin bases (minus output) + segment arena (aka 3 + builtins) + // This AP correction represents the memory slots taken up by the values created by `create_entry_code`: + // These include: + // * The builtin bases (not including output) + // * The segment arena values (if present), including: + // * segment_arena_ptr + // * info_segment_ptr + // * 0 + // * (Only if the output builtin is added) A gap for each builtin's final pointer let mut ap_offset = runner.get_program().builtins_len(); if append_output { ap_offset -= 1; From 518625c21f9b6fd2c70b0ae29316ea9f812c710d Mon Sep 17 00:00:00 2001 From: Federica Date: Thu, 9 May 2024 19:08:51 -0300 Subject: [PATCH 07/12] Add CHANGELOG entry --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b737d5152d..da957bb603 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ #### Upcoming Changes +* feat: Load arguments into VM instead of creating them via instructions in cairo1-run [#1759](https://github.com/lambdaclass/cairo-vm/pull/1759) + * feat: Add hint `U256InvModN` to `Cairo1HintProcessor` [#1744](https://github.com/lambdaclass/cairo-vm/pull/1744) * perf: use a more compact representation for `MemoryCell` [#1672](https://github.com/lambdaclass/cairo-vm/pull/1672) From 4438b50a55ee52ea7fd4b66d8733b236206a762d Mon Sep 17 00:00:00 2001 From: Federica Date: Thu, 9 May 2024 19:15:56 -0300 Subject: [PATCH 08/12] Remove uneeded arg --- cairo1-run/src/cairo_run.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/cairo1-run/src/cairo_run.rs b/cairo1-run/src/cairo_run.rs index a4ce82b919..b171a02481 100644 --- a/cairo1-run/src/cairo_run.rs +++ b/cairo1-run/src/cairo_run.rs @@ -223,7 +223,6 @@ pub fn cairo_run_program( &runner, &cairo_run_config, main_func, - &sierra_program_registry, )?; // Run it until the end / infinite loop in proof_mode @@ -407,12 +406,9 @@ fn load_arguments( runner: &CairoRunner, cairo_run_config: &Cairo1RunConfig, main_func: &Function, - sierra_program_registry: &ProgramRegistry, ) -> Result<(), Error> { let got_segment_arena = main_func.signature.param_types.iter().any(|ty| { - get_info(sierra_program_registry, ty) - .map(|x| x.long_id.generic_id == SegmentArenaType::ID) - .unwrap_or_default() + ty.debug_name.as_ref().is_some_and(|n| n == "SegmentArena") }); let append_output = cairo_run_config.append_return_values || cairo_run_config.proof_mode; // This AP correction represents the memory slots taken up by the values created by `create_entry_code`: From 01f40a23aab9b8878235c71c3e523c7e9a083c95 Mon Sep 17 00:00:00 2001 From: Federica Date: Thu, 9 May 2024 19:19:04 -0300 Subject: [PATCH 09/12] return early if inputless --- cairo1-run/src/cairo_run.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cairo1-run/src/cairo_run.rs b/cairo1-run/src/cairo_run.rs index b171a02481..8a70195014 100644 --- a/cairo1-run/src/cairo_run.rs +++ b/cairo1-run/src/cairo_run.rs @@ -407,6 +407,10 @@ fn load_arguments( cairo_run_config: &Cairo1RunConfig, main_func: &Function, ) -> Result<(), Error> { + if cairo_run_config.args.is_empty() { + // Nothing to be done + return Ok(()) + } let got_segment_arena = main_func.signature.param_types.iter().any(|ty| { ty.debug_name.as_ref().is_some_and(|n| n == "SegmentArena") }); From 6b14b7eb993f3220b7e57146ef81f7bc37f0cf7b Mon Sep 17 00:00:00 2001 From: Federica Date: Fri, 10 May 2024 10:18:08 -0300 Subject: [PATCH 10/12] fmt --- cairo1-run/src/cairo_run.rs | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/cairo1-run/src/cairo_run.rs b/cairo1-run/src/cairo_run.rs index 8a70195014..6380aff242 100644 --- a/cairo1-run/src/cairo_run.rs +++ b/cairo1-run/src/cairo_run.rs @@ -218,12 +218,7 @@ pub fn cairo_run_program( let mut runner = CairoRunner::new_v2(&program, cairo_run_config.layout, runner_mode)?; let mut vm = VirtualMachine::new(cairo_run_config.trace_enabled); let end = runner.initialize(&mut vm, cairo_run_config.proof_mode)?; - load_arguments( - &mut vm, - &runner, - &cairo_run_config, - main_func, - )?; + load_arguments(&mut vm, &runner, &cairo_run_config, main_func)?; // Run it until the end / infinite loop in proof_mode runner.run_until_pc(end, &mut vm, &mut hint_processor)?; @@ -409,11 +404,13 @@ fn load_arguments( ) -> Result<(), Error> { if cairo_run_config.args.is_empty() { // Nothing to be done - return Ok(()) + return Ok(()); } - let got_segment_arena = main_func.signature.param_types.iter().any(|ty| { - ty.debug_name.as_ref().is_some_and(|n| n == "SegmentArena") - }); + let got_segment_arena = main_func + .signature + .param_types + .iter() + .any(|ty| ty.debug_name.as_ref().is_some_and(|n| n == "SegmentArena")); let append_output = cairo_run_config.append_return_values || cairo_run_config.proof_mode; // This AP correction represents the memory slots taken up by the values created by `create_entry_code`: // These include: From 0f5e142af6db8f359927d3ec2b53b5cc6e7c0625 Mon Sep 17 00:00:00 2001 From: Federica Date: Fri, 10 May 2024 12:20:35 -0300 Subject: [PATCH 11/12] Update tests --- cairo1-run/src/cairo_run.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cairo1-run/src/cairo_run.rs b/cairo1-run/src/cairo_run.rs index b68080a26f..bb5df9f6b9 100644 --- a/cairo1-run/src/cairo_run.rs +++ b/cairo1-run/src/cairo_run.rs @@ -1337,8 +1337,8 @@ mod tests { ], ..Default::default() }; - let runner_a = cairo_run_program(&sierra_program, config_a).unwrap().0; - let runner_b = cairo_run_program(&sierra_program, config_b).unwrap().0; + let runner_a = cairo_run_program(&sierra_program, config_a).unwrap(); + let runner_b = cairo_run_program(&sierra_program, config_b).unwrap(); let hash_a = compute_program_hash_chain(&runner_a.get_program().get_stripped_program().unwrap(), 0) .unwrap(); From 69c20e9cc82e57182e896bf1bfaf5be3dc5363cf Mon Sep 17 00:00:00 2001 From: Federica Date: Fri, 10 May 2024 12:21:14 -0300 Subject: [PATCH 12/12] Revert "Update tests" This reverts commit 0f5e142af6db8f359927d3ec2b53b5cc6e7c0625. --- cairo1-run/src/cairo_run.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cairo1-run/src/cairo_run.rs b/cairo1-run/src/cairo_run.rs index bb5df9f6b9..b68080a26f 100644 --- a/cairo1-run/src/cairo_run.rs +++ b/cairo1-run/src/cairo_run.rs @@ -1337,8 +1337,8 @@ mod tests { ], ..Default::default() }; - let runner_a = cairo_run_program(&sierra_program, config_a).unwrap(); - let runner_b = cairo_run_program(&sierra_program, config_b).unwrap(); + let runner_a = cairo_run_program(&sierra_program, config_a).unwrap().0; + let runner_b = cairo_run_program(&sierra_program, config_b).unwrap().0; let hash_a = compute_program_hash_chain(&runner_a.get_program().get_stripped_program().unwrap(), 0) .unwrap();