Skip to content

Commit 51c2811

Browse files
authored
vm core: implement run_instruction (#6)
* vm core: implement run_instruction * rm core for now
1 parent 4b5c217 commit 51c2811

File tree

10 files changed

+389
-30
lines changed

10 files changed

+389
-30
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,3 +42,4 @@ p3-koala-bear = { git = "https://github.com/Plonky3/Plonky3.git", rev = "d0c4a36
4242

4343
thiserror = "2.0"
4444
proptest = "1.7"
45+
num-traits = "0.2"

crates/leanVm/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ p3-koala-bear.workspace = true
1414
p3-field.workspace = true
1515

1616
thiserror.workspace = true
17+
num-traits.workspace = true
1718

1819
[dev-dependencies]
1920
proptest.workspace = true

crates/leanVm/src/context/run_context.rs

Lines changed: 93 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use p3_field::PrimeField64;
33
use crate::{
44
errors::{memory::MemoryError, vm::VirtualMachineError},
55
memory::{address::MemoryAddress, manager::MemoryManager, val::MemoryValue},
6-
types::instruction::MemOrConstant,
6+
types::instruction::{MemOrConstant, MemOrFp, MemOrFpOrConstant},
77
};
88

99
#[derive(Debug, Default)]
@@ -47,7 +47,56 @@ impl RunContext {
4747
match operand {
4848
MemOrConstant::Constant(val) => Ok(MemoryValue::Int(*val)),
4949
MemOrConstant::MemoryAfterFp { shift } => {
50-
let addr = (self.fp + *shift)?;
50+
let addr = self.fp.add_usize(*shift)?;
51+
memory
52+
.get(addr)
53+
.ok_or_else(|| MemoryError::UninitializedMemory(addr).into())
54+
}
55+
}
56+
}
57+
58+
/// Resolves a `MemOrFp` operand to its final value.
59+
///
60+
/// - If the operand is the frame pointer `Fp`, it returns the `fp` address itself.
61+
/// - If it's a memory location, it computes the address relative to `fp` and fetches the value.
62+
pub fn get_value_from_mem_or_fp<F>(
63+
&self,
64+
operand: &MemOrFp,
65+
memory: &MemoryManager,
66+
) -> Result<MemoryValue<F>, VirtualMachineError<F>>
67+
where
68+
F: PrimeField64,
69+
{
70+
match operand {
71+
MemOrFp::Fp => Ok(MemoryValue::Address(self.fp)),
72+
MemOrFp::MemoryAfterFp { shift } => {
73+
let addr = self.fp.add_usize(*shift)?;
74+
memory
75+
.get(addr)
76+
.ok_or_else(|| MemoryError::UninitializedMemory(addr).into())
77+
}
78+
}
79+
}
80+
81+
/// Resolves a `MemOrFpOrConstant` operand to its final value.
82+
///
83+
/// This is a comprehensive resolver that handles all three potential operand types:
84+
/// - a constant value,
85+
/// - a memory location relative to `fp`,
86+
/// - the `fp` register itself.
87+
pub fn get_value_from_mem_or_fp_or_constant<F>(
88+
&self,
89+
operand: &MemOrFpOrConstant<F>,
90+
memory: &MemoryManager,
91+
) -> Result<MemoryValue<F>, VirtualMachineError<F>>
92+
where
93+
F: PrimeField64,
94+
{
95+
match operand {
96+
MemOrFpOrConstant::Constant(val) => Ok(MemoryValue::Int(*val)),
97+
MemOrFpOrConstant::Fp => Ok(MemoryValue::Address(self.fp)),
98+
MemOrFpOrConstant::MemoryAfterFp { shift } => {
99+
let addr = self.fp.add_usize(*shift)?;
51100
memory
52101
.get(addr)
53102
.ok_or_else(|| MemoryError::UninitializedMemory(addr).into())
@@ -159,4 +208,46 @@ mod tests {
159208
other => panic!("Unexpected error: {other:?}"),
160209
}
161210
}
211+
212+
#[test]
213+
fn test_get_value_from_mem_or_fp_or_constant_is_constant() {
214+
let ctx = RunContext::new(MemoryAddress::new(0, 0), MemoryAddress::new(1, 0));
215+
let operand = MemOrFpOrConstant::Constant(F::from_u64(123));
216+
let memory = MemoryManager::default();
217+
let result = ctx
218+
.get_value_from_mem_or_fp_or_constant(&operand, &memory)
219+
.unwrap();
220+
assert_eq!(result, MemoryValue::Int(F::from_u64(123)));
221+
}
222+
223+
#[test]
224+
fn test_get_value_from_mem_or_fp_or_constant_is_fp() {
225+
let fp_addr = MemoryAddress::new(1, 10);
226+
let ctx = RunContext::new(MemoryAddress::new(0, 0), fp_addr);
227+
let operand = MemOrFpOrConstant::<F>::Fp;
228+
let memory = MemoryManager::default();
229+
let result = ctx
230+
.get_value_from_mem_or_fp_or_constant(&operand, &memory)
231+
.unwrap();
232+
assert_eq!(result, MemoryValue::Address(fp_addr));
233+
}
234+
235+
#[test]
236+
fn test_get_value_from_mem_or_fp_or_constant_is_mem_success() {
237+
let mut memory = MemoryManager::default();
238+
let fp = memory.add();
239+
let addr_to_read = fp.add_usize::<F>(7).unwrap();
240+
let expected_val = MemoryValue::<F>::Address(MemoryAddress::new(5, 5));
241+
memory
242+
.memory
243+
.insert(addr_to_read, expected_val.clone())
244+
.unwrap();
245+
246+
let ctx = RunContext::new(MemoryAddress::new(0, 0), fp);
247+
let operand = MemOrFpOrConstant::MemoryAfterFp { shift: 7 };
248+
let result = ctx
249+
.get_value_from_mem_or_fp_or_constant(&operand, &memory)
250+
.unwrap();
251+
assert_eq!(result, expected_val);
252+
}
162253
}

crates/leanVm/src/core.rs

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ impl VirtualMachine {
5050

5151
if is_zero {
5252
// **Condition is zero**: The jump is not taken. Advance the `pc` by one.
53-
(*self.run_context.pc() + 1)?
53+
self.run_context.pc().add_usize(1)?
5454
} else {
5555
// **Condition is non-zero**: Execute the jump.
5656
//
@@ -64,7 +64,7 @@ impl VirtualMachine {
6464
}
6565
} else {
6666
// For any instruction other than `JumpIfNotZero`, advance the `pc` by one.
67-
(*self.run_context.pc() + 1)?
67+
self.run_context.pc().add_usize(1)?
6868
};
6969

7070
// Update the virtual machine's program counter with the calculated next address.
@@ -86,7 +86,7 @@ impl VirtualMachine {
8686
MemOrFp::Fp => self.run_context.fp,
8787
// The instruction specifies updating `fp` to a value from memory.
8888
MemOrFp::MemoryAfterFp { shift } => {
89-
let addr = (*self.run_context.fp() + *shift)?;
89+
let addr = self.run_context.fp().add_usize(*shift)?;
9090
let value = self
9191
.memory_manager
9292
.get(addr)
@@ -189,7 +189,11 @@ mod tests {
189189
let pc = MemoryAddress::new(0, 10);
190190
let fp = MemoryAddress::new(1, 5);
191191
// Pre-load memory with a zero value at the address `fp + 1`, which will be our condition.
192-
let mut vm = setup_vm(pc, fp, &[((fp + 1).unwrap(), MemoryValue::Int(F::ZERO))]);
192+
let mut vm = setup_vm(
193+
pc,
194+
fp,
195+
&[(fp.add_usize::<F>(1).unwrap(), MemoryValue::Int(F::ZERO))],
196+
);
193197
// Define a JNZ instruction where the condition points to the zero value.
194198
let instruction = Instruction::JumpIfNotZero::<F> {
195199
condition: MemOrConstant::MemoryAfterFp { shift: 1 },
@@ -215,9 +219,15 @@ mod tests {
215219
fp,
216220
&[
217221
// The condition value (non-zero).
218-
((fp + 1).unwrap(), MemoryValue::Int(F::from_u64(42))),
222+
(
223+
fp.add_usize::<F>(1).unwrap(),
224+
MemoryValue::Int(F::from_u64(42)),
225+
),
219226
// The destination address for the jump.
220-
((fp + 2).unwrap(), MemoryValue::Address(jump_target)),
227+
(
228+
fp.add_usize::<F>(2).unwrap(),
229+
MemoryValue::Address(jump_target),
230+
),
221231
],
222232
);
223233
// Define a JNZ instruction pointing to the condition and destination.
@@ -242,7 +252,7 @@ mod tests {
242252
pc,
243253
fp,
244254
&[(
245-
(fp + 1).unwrap(),
255+
fp.add_usize::<F>(1).unwrap(),
246256
MemoryValue::Address(MemoryAddress::new(8, 8)),
247257
)],
248258
);
@@ -287,7 +297,7 @@ mod tests {
287297
let mut vm = setup_vm(
288298
MemoryAddress::new(0, 0),
289299
fp,
290-
&[((fp + 3).unwrap(), MemoryValue::Address(new_fp))],
300+
&[(fp.add_usize::<F>(3).unwrap(), MemoryValue::Address(new_fp))],
291301
);
292302
// Define a JNZ instruction where `updated_fp` points to the new address in memory.
293303
let instruction = Instruction::JumpIfNotZero::<F> {
@@ -309,7 +319,10 @@ mod tests {
309319
let mut vm = setup_vm(
310320
MemoryAddress::new(0, 0),
311321
fp,
312-
&[((fp + 3).unwrap(), MemoryValue::Int(F::from_u64(99)))],
322+
&[(
323+
fp.add_usize::<F>(3).unwrap(),
324+
MemoryValue::Int(F::from_u64(99)),
325+
)],
313326
);
314327
// Define a JNZ instruction where `updated_fp` points to this integer value.
315328
let instruction = Instruction::JumpIfNotZero::<F> {

crates/leanVm/src/errors/math.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,15 @@
1+
use p3_field::PrimeField64;
12
use thiserror::Error;
23

34
use crate::memory::address::MemoryAddress;
45

56
#[derive(Debug, Error, Eq, PartialEq)]
6-
pub enum MathError {
7+
pub enum MathError<F>
8+
where
9+
F: PrimeField64,
10+
{
711
#[error("Operation failed: {} + {}, maximum offset value exceeded", 0.0, 0.1)]
812
MemoryAddressAddUsizeOffsetExceeded(Box<(MemoryAddress, usize)>),
13+
#[error("Operation failed: {} + {}, maximum offset value exceeded", 0.0, 0.1)]
14+
MemoryAddressAddFieldOffsetExceeded(Box<(MemoryAddress, F)>),
915
}

crates/leanVm/src/errors/memory.rs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use std::fmt::Debug;
22

3+
use p3_field::PrimeField64;
34
use thiserror::Error;
45

56
use super::math::MathError;
@@ -8,7 +9,7 @@ use crate::memory::{address::MemoryAddress, val::MemoryValue};
89
#[derive(Debug, Eq, PartialEq, Error)]
910
pub enum MemoryError<F>
1011
where
11-
F: Debug,
12+
F: PrimeField64,
1213
{
1314
/// Error for when an operation targets a memory segment that has not been allocated.
1415
#[error(
@@ -35,7 +36,7 @@ where
3536

3637
/// Error related to mathematical operations.
3738
#[error(transparent)]
38-
Math(#[from] MathError),
39+
Math(#[from] MathError<F>),
3940

4041
/// Error when a memory value is expected to be an integer, but it is an address to another memory location.
4142
#[error("Memory value should be an integer.")]
@@ -47,6 +48,12 @@ where
4748
#[error("Memory address is expected but we got an integer.")]
4849
ExpectedMemoryAddress,
4950

50-
#[error("Inteer is expected but we got a memory address.")]
51+
#[error("Integer is expected but we got a memory address.")]
5152
ExpectedInteger,
53+
54+
#[error("Operation failed: {} + {}, can't add two address values", 0.0, 0.1)]
55+
MemoryAddressAdd(Box<(MemoryAddress, MemoryAddress)>),
56+
57+
#[error("Operation failed: {} * {}, can't multiply these two values", 0.0, 0.1)]
58+
InvalidMul(Box<(MemoryValue<F>, MemoryValue<F>)>),
5259
}

crates/leanVm/src/errors/vm.rs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,27 @@
11
use std::fmt::Debug;
22

3+
use p3_field::PrimeField64;
34
use thiserror::Error;
45

56
use super::{math::MathError, memory::MemoryError};
7+
use crate::memory::val::MemoryValue;
68

79
#[derive(Debug, Error)]
810
pub enum VirtualMachineError<F>
911
where
10-
F: Debug,
12+
F: PrimeField64,
1113
{
1214
#[error(transparent)]
1315
Memory(#[from] MemoryError<F>),
1416
#[error(transparent)]
15-
Math(#[from] MathError),
17+
Math(#[from] MathError<F>),
18+
#[error(
19+
"Assertion failed: computed value '{:?}' != expected value '{:?}'.",
20+
computed,
21+
expected
22+
)]
23+
AssertEqFailed {
24+
computed: MemoryValue<F>,
25+
expected: MemoryValue<F>,
26+
},
1627
}

0 commit comments

Comments
 (0)