Skip to content

Commit 8daae06

Browse files
fmolettapefontana
andauthored
Allow running instructions from pcs outside the program segment (#1493)
* Add back RelocataedTrace struct * Move relocated trace back to runner * Remove trace relocation from vm * Fix test * Update tests * clippy * clippy * Remove dbg print + add changelog entry * Add test check * Add test check * Add missing wasm import * Only use instruction cache when running from program segment * Add test * Update test * Add unit tests * Fmt * Add changelog entry * Fix pr num * Make relocate_trace public * List breaking changes * List pr purpose in changelog * Fix tests --------- Co-authored-by: Pedro Fontana <[email protected]>
1 parent 0941386 commit 8daae06

File tree

3 files changed

+216
-32
lines changed

3 files changed

+216
-32
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
#### Upcoming Changes
44

5+
* feat: Allow running instructions from pcs outside the program segement [#1493](https://github.com/lambdaclass/cairo-vm/pull/14923)
6+
57
* feat: Add HintProcessor::execute_hint_extensive + refactor hint_ranges [#1491](https://github.com/lambdaclass/cairo-vm/pull/1491)
68

79
* Add trait method `HintProcessorLogic::execute_hint_extensive`:

fuzzer/Cargo.lock

+8-16
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

vm/src/vm/vm_core.rs

+206-16
Original file line numberDiff line numberDiff line change
@@ -475,27 +475,41 @@ impl VirtualMachine {
475475
}
476476

477477
pub fn step_instruction(&mut self) -> Result<(), VirtualMachineError> {
478-
let pc = self.run_context.pc.offset;
478+
if self.run_context.pc.segment_index == 0 {
479+
// Run instructions from program segment, using instruction cache
480+
let pc = self.run_context.pc.offset;
479481

480-
if self.segments.memory.data[0].len() <= pc {
481-
return Err(MemoryError::UnknownMemoryCell(Box::new((0, pc).into())))?;
482-
}
482+
if self.segments.memory.data[0].len() <= pc {
483+
return Err(MemoryError::UnknownMemoryCell(Box::new((0, pc).into())))?;
484+
}
483485

484-
let mut inst_cache = core::mem::take(&mut self.instruction_cache);
485-
inst_cache.resize((pc + 1).max(inst_cache.len()), None);
486+
let mut inst_cache = core::mem::take(&mut self.instruction_cache);
487+
inst_cache.resize((pc + 1).max(inst_cache.len()), None);
486488

487-
let instruction = inst_cache.get_mut(pc).unwrap();
488-
if instruction.is_none() {
489-
*instruction = Some(self.decode_current_instruction()?);
490-
}
491-
let instruction = instruction.as_ref().unwrap();
492-
if !self.skip_instruction_execution {
493-
self.run_instruction(instruction)?;
489+
let instruction = inst_cache.get_mut(pc).unwrap();
490+
if instruction.is_none() {
491+
*instruction = Some(self.decode_current_instruction()?);
492+
}
493+
let instruction = instruction.as_ref().unwrap();
494+
495+
if !self.skip_instruction_execution {
496+
self.run_instruction(instruction)?;
497+
} else {
498+
self.run_context.pc += instruction.size();
499+
self.skip_instruction_execution = false;
500+
}
501+
self.instruction_cache = inst_cache;
494502
} else {
495-
self.run_context.pc += instruction.size();
496-
self.skip_instruction_execution = false;
503+
// Run instructions from programs loaded in other segments, without instruction cache
504+
let instruction = self.decode_current_instruction()?;
505+
506+
if !self.skip_instruction_execution {
507+
self.run_instruction(&instruction)?;
508+
} else {
509+
self.run_context.pc += instruction.size();
510+
self.skip_instruction_execution = false;
511+
}
497512
}
498-
self.instruction_cache = inst_cache;
499513
Ok(())
500514
}
501515

@@ -4301,4 +4315,180 @@ mod tests {
43014315
.is_err());
43024316
}
43034317
}
4318+
4319+
#[test]
4320+
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
4321+
/// Test for a simple program execution
4322+
/// Used program code:
4323+
/// func main():
4324+
/// let a = 1
4325+
/// let b = 2
4326+
/// let c = a + b
4327+
/// return()
4328+
/// end
4329+
/// Memory taken from original vm
4330+
/// {RelocatableValue(segment_index=0, offset=0): 2345108766317314046,
4331+
/// RelocatableValue(segment_index=1, offset=0): RelocatableValue(segment_index=2, offset=0),
4332+
/// RelocatableValue(segment_index=1, offset=1): RelocatableValue(segment_index=3, offset=0)}
4333+
/// Current register values:
4334+
/// AP 1:2
4335+
/// FP 1:2
4336+
/// PC 0:0
4337+
fn test_step_for_preset_memory_program_loaded_into_user_segment() {
4338+
let mut vm = vm!(true);
4339+
4340+
let mut hint_processor = BuiltinHintProcessor::new_empty();
4341+
4342+
run_context!(vm, 0, 2, 2);
4343+
4344+
vm.segments = segments![
4345+
((2, 0), 2345108766317314046_u64), // Load program into new segment
4346+
((1, 0), (2, 0)),
4347+
((1, 1), (3, 0))
4348+
];
4349+
// set starting pc on new segemt to run loaded program
4350+
vm.run_context.pc.segment_index = 2;
4351+
4352+
assert_matches!(
4353+
vm.step(
4354+
&mut hint_processor,
4355+
exec_scopes_ref!(),
4356+
&mut Vec::new(),
4357+
&mut HashMap::new(),
4358+
&HashMap::new()
4359+
),
4360+
Ok(())
4361+
);
4362+
let trace = vm.trace.unwrap();
4363+
trace_check(&trace, &[((2, 0).into(), 2, 2)]);
4364+
4365+
assert_eq!(vm.run_context.pc, Relocatable::from((3, 0)));
4366+
assert_eq!(vm.run_context.ap, 2);
4367+
assert_eq!(vm.run_context.fp, 0);
4368+
4369+
//Check that the following addresses have been accessed:
4370+
// Addresses have been copied from python execution:
4371+
let mem = vm.segments.memory.data;
4372+
assert!(mem[1][0].as_ref().unwrap().is_accessed());
4373+
assert!(mem[1][1].as_ref().unwrap().is_accessed());
4374+
}
4375+
4376+
#[test]
4377+
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
4378+
/*
4379+
Test for a simple program execution
4380+
Used program code:
4381+
func myfunc(a: felt) -> (r: felt):
4382+
let b = a * 2
4383+
return(b)
4384+
end
4385+
func main():
4386+
let a = 1
4387+
let b = myfunc(a)
4388+
return()
4389+
end
4390+
Memory taken from original vm:
4391+
{RelocatableValue(segment_index=0, offset=0): 5207990763031199744,
4392+
RelocatableValue(segment_index=0, offset=1): 2,
4393+
RelocatableValue(segment_index=0, offset=2): 2345108766317314046,
4394+
RelocatableValue(segment_index=0, offset=3): 5189976364521848832,
4395+
RelocatableValue(segment_index=0, offset=4): 1,
4396+
RelocatableValue(segment_index=0, offset=5): 1226245742482522112,
4397+
RelocatableValue(segment_index=0, offset=6): 3618502788666131213697322783095070105623107215331596699973092056135872020476,
4398+
RelocatableValue(segment_index=0, offset=7): 2345108766317314046,
4399+
RelocatableValue(segment_index=1, offset=0): RelocatableValue(segment_index=2, offset=0),
4400+
RelocatableValue(segment_index=1, offset=1): RelocatableValue(segment_index=3, offset=0)}
4401+
Current register values:
4402+
AP 1:2
4403+
FP 1:2
4404+
PC 0:3
4405+
Final Pc (not executed): 3:0
4406+
This program consists of 5 steps
4407+
*/
4408+
fn test_step_for_preset_memory_function_call_program_loaded_into_user_segment() {
4409+
let mut vm = vm!(true);
4410+
4411+
run_context!(vm, 3, 2, 2);
4412+
// set starting pc on new segemt to run loaded program
4413+
vm.run_context.pc.segment_index = 4;
4414+
4415+
//Insert values into memory
4416+
vm.segments.memory =
4417+
memory![
4418+
// Load program into new segment
4419+
((4, 0), 5207990763031199744_i64),
4420+
((4, 1), 2),
4421+
((4, 2), 2345108766317314046_i64),
4422+
((4, 3), 5189976364521848832_i64),
4423+
((4, 4), 1),
4424+
((4, 5), 1226245742482522112_i64),
4425+
(
4426+
(4, 6),
4427+
("3618502788666131213697322783095070105623107215331596699973092056135872020476",10)
4428+
),
4429+
((4, 7), 2345108766317314046_i64),
4430+
((1, 0), (2, 0)),
4431+
((1, 1), (3, 0))
4432+
];
4433+
4434+
let final_pc = Relocatable::from((3, 0));
4435+
let mut hint_processor = BuiltinHintProcessor::new_empty();
4436+
//Run steps
4437+
while vm.run_context.pc != final_pc {
4438+
assert_matches!(
4439+
vm.step(
4440+
&mut hint_processor,
4441+
exec_scopes_ref!(),
4442+
&mut Vec::new(),
4443+
&mut HashMap::new(),
4444+
&HashMap::new()
4445+
),
4446+
Ok(())
4447+
);
4448+
}
4449+
4450+
//Check final register values
4451+
assert_eq!(vm.run_context.pc, Relocatable::from((3, 0)));
4452+
4453+
assert_eq!(vm.run_context.ap, 6);
4454+
4455+
assert_eq!(vm.run_context.fp, 0);
4456+
//Check each TraceEntry in trace
4457+
let trace = vm.trace.unwrap();
4458+
assert_eq!(trace.len(), 5);
4459+
trace_check(
4460+
&trace,
4461+
&[
4462+
((4, 3).into(), 2, 2),
4463+
((4, 5).into(), 3, 2),
4464+
((4, 0).into(), 5, 5),
4465+
((4, 2).into(), 6, 5),
4466+
((4, 7).into(), 6, 2),
4467+
],
4468+
);
4469+
//Check that the following addresses have been accessed:
4470+
// Addresses have been copied from python execution:
4471+
let mem = &vm.segments.memory.data;
4472+
assert!(mem[4][1].as_ref().unwrap().is_accessed());
4473+
assert!(mem[4][4].as_ref().unwrap().is_accessed());
4474+
assert!(mem[4][6].as_ref().unwrap().is_accessed());
4475+
assert!(mem[1][0].as_ref().unwrap().is_accessed());
4476+
assert!(mem[1][1].as_ref().unwrap().is_accessed());
4477+
assert!(mem[1][2].as_ref().unwrap().is_accessed());
4478+
assert!(mem[1][3].as_ref().unwrap().is_accessed());
4479+
assert!(mem[1][4].as_ref().unwrap().is_accessed());
4480+
assert!(mem[1][5].as_ref().unwrap().is_accessed());
4481+
assert_eq!(
4482+
vm.segments
4483+
.memory
4484+
.get_amount_of_accessed_addresses_for_segment(4),
4485+
Some(3)
4486+
);
4487+
assert_eq!(
4488+
vm.segments
4489+
.memory
4490+
.get_amount_of_accessed_addresses_for_segment(1),
4491+
Some(6)
4492+
);
4493+
}
43044494
}

0 commit comments

Comments
 (0)