Skip to content

Commit 1c734a7

Browse files
committed
instruction: better organization for the execution
1 parent ffe698c commit 1c734a7

File tree

9 files changed

+587
-467
lines changed

9 files changed

+587
-467
lines changed

crates/leanVm/src/bytecode/instruction.rs

Lines changed: 0 additions & 76 deletions
This file was deleted.
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
use crate::{
2+
bytecode::{
3+
operand::{MemOrConstant, MemOrFp},
4+
operation::Operation,
5+
},
6+
context::run_context::RunContext,
7+
errors::{math::MathError, vm::VirtualMachineError},
8+
memory::{manager::MemoryManager, val::MemoryValue},
9+
};
10+
11+
/// Performs a basic arithmetic computation: `res = arg_a op arg_b`.
12+
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
13+
pub struct ComputationInstruction {
14+
/// The arithmetic operation to perform (`Add` or `Mul`).
15+
pub operation: Operation,
16+
/// The first operand of the computation.
17+
pub arg_a: MemOrConstant,
18+
/// The second operand of the computation.
19+
pub arg_b: MemOrFp,
20+
/// The memory location or constant that the result must be equal to.
21+
pub res: MemOrConstant,
22+
}
23+
24+
impl ComputationInstruction {
25+
/// Executes a computation instruction (`res = arg_a op arg_b`), handling deduction and assertion.
26+
///
27+
/// This function implements the core logic for `ADD` and `MUL` instructions. It follows
28+
/// a "constraint satisfaction" model:
29+
///
30+
/// 1. **Deduction:** If exactly one of the three operands (`res`, `arg_a`, `arg_b`) is unknown
31+
/// (i.e., its memory cell is uninitialized), the function computes its value from the other
32+
/// two and writes it to memory.
33+
/// 2. **Assertion:** If all three operands are already known, the function computes `arg_a op arg_b`
34+
/// and asserts that it equals the value of `res`.
35+
pub fn execute(
36+
&self,
37+
run_context: &RunContext,
38+
memory_manager: &mut MemoryManager,
39+
) -> Result<(), VirtualMachineError> {
40+
let val_a = run_context.value_from_mem_or_constant(&self.arg_a, memory_manager);
41+
let val_b = run_context.value_from_mem_or_fp(&self.arg_b, memory_manager);
42+
let val_res = run_context.value_from_mem_or_constant(&self.res, memory_manager);
43+
44+
match (val_a, val_b, val_res) {
45+
// Case 1: a OP b = c — compute c
46+
(Ok(MemoryValue::Int(a)), Ok(MemoryValue::Int(b)), Ok(MemoryValue::Address(addr))) => {
47+
let c = self.operation.compute(a, b);
48+
memory_manager.memory.insert(addr, c)?;
49+
}
50+
// Case 2: a OP b = c — recover b
51+
(Ok(MemoryValue::Int(a)), Ok(MemoryValue::Address(addr)), Ok(MemoryValue::Int(c))) => {
52+
let b = self
53+
.operation
54+
.inverse_compute(c, a)
55+
.ok_or(MathError::DivisionByZero)?;
56+
memory_manager.memory.insert(addr, b)?;
57+
}
58+
// Case 3: a OP b = c — recover a
59+
(Ok(MemoryValue::Address(addr)), Ok(MemoryValue::Int(b)), Ok(MemoryValue::Int(c))) => {
60+
let a = self
61+
.operation
62+
.inverse_compute(c, b)
63+
.ok_or(MathError::DivisionByZero)?;
64+
memory_manager.memory.insert(addr, a)?;
65+
}
66+
// Case 4: a OP b = c — assert equality
67+
(Ok(MemoryValue::Int(a)), Ok(MemoryValue::Int(b)), Ok(MemoryValue::Int(c))) => {
68+
let computed = self.operation.compute(a, b);
69+
if computed != c {
70+
return Err(VirtualMachineError::AssertEqFailed {
71+
computed: computed.into(),
72+
expected: c.into(),
73+
});
74+
}
75+
}
76+
_ => return Err(VirtualMachineError::TooManyUnknownOperands),
77+
}
78+
79+
Ok(())
80+
}
81+
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
use crate::{
2+
bytecode::operand::MemOrFpOrConstant,
3+
context::run_context::RunContext,
4+
errors::{memory::MemoryError, vm::VirtualMachineError},
5+
memory::{address::MemoryAddress, manager::MemoryManager, val::MemoryValue},
6+
};
7+
8+
/// Performs a double-dereference memory access: `res = m[m[fp + shift_0] + shift_1]`.
9+
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
10+
pub struct DerefInstruction {
11+
/// The offset from `fp` for the first memory access, which yields a pointer.
12+
pub shift_0: usize,
13+
/// The offset added to the pointer from the first access to get the final address.
14+
pub shift_1: usize,
15+
/// The value that the result of the double dereference must be equal to.
16+
pub res: MemOrFpOrConstant,
17+
}
18+
19+
impl DerefInstruction {
20+
/// Executes the `DEREF` instruction: `res = m[m[fp + shift_0] + shift_1]`.
21+
///
22+
/// It operates using a constraint satisfaction model with two primary modes:
23+
///
24+
/// 1. **Deduction of `res`**: If the `res` operand points to an unwritten memory
25+
/// location, this function performs the double-dereference to find the final
26+
/// value and writes it into `res`'s location.
27+
///
28+
/// 2. **Constraint of `m[...]`**: If `res` holds a known value, that value is
29+
/// written to the final dereferenced address. The underlying memory model
30+
/// ensures this write is consistent, effectively asserting that
31+
/// `m[m[...]]` must equal the known `res` value.
32+
pub fn execute(
33+
&self,
34+
run_context: &RunContext,
35+
memory_manager: &mut MemoryManager,
36+
) -> Result<(), VirtualMachineError> {
37+
// Resolve the `res` operand first to determine which execution path to take.
38+
//
39+
// This will either be
40+
// - a known `Int`,
41+
// - an `Address` to an unwritten cell.
42+
let res_lookup_result =
43+
run_context.value_from_mem_or_fp_or_constant(&self.res, memory_manager)?;
44+
45+
// Calculate the address of the first-level pointer, `fp + shift_0`.
46+
let ptr_shift_0_addr = (run_context.fp + self.shift_0)?;
47+
48+
// Read the pointer from memory. It must be a `MemoryAddress` type.
49+
let ptr: MemoryAddress = memory_manager.memory.get_as(ptr_shift_0_addr)?;
50+
51+
// Calculate the final, second-level address: `ptr + shift_1`.
52+
let ptr_shift_1_addr = (ptr + self.shift_1)?;
53+
54+
// Branch execution based on whether `res` was a known value or an unwritten address.
55+
match res_lookup_result {
56+
// Case 1: `res` is an unwritten memory location.
57+
//
58+
// We deduce its value by reading from the final address.
59+
MemoryValue::Address(addr) => {
60+
// Read the value from the final dereferenced address `m[ptr + shift_1]`.
61+
let value = memory_manager
62+
.get(ptr_shift_1_addr)
63+
.ok_or(MemoryError::UninitializedMemory(ptr_shift_1_addr))?;
64+
65+
// Write the deduced value into `res`'s memory location.
66+
memory_manager.memory.insert(addr, value)?;
67+
}
68+
// Case 2: `res` is a known integer value.
69+
//
70+
// We use this value to constrain the memory at the final address.
71+
MemoryValue::Int(value) => {
72+
// Write the known `res` value to the final dereferenced address.
73+
memory_manager.memory.insert(ptr_shift_1_addr, value)?;
74+
}
75+
}
76+
77+
Ok(())
78+
}
79+
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
use p3_field::BasedVectorSpace;
2+
3+
use crate::{
4+
constant::{DIMENSION, EF, F},
5+
context::run_context::RunContext,
6+
errors::vm::VirtualMachineError,
7+
memory::{address::MemoryAddress, manager::MemoryManager},
8+
};
9+
10+
/// Degree-8 extension field multiplication of 2 elements -> 1 result.
11+
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
12+
pub struct ExtensionMulInstruction {
13+
/// An array of three offsets from `fp`. These point to the start of the 8-cell memory blocks
14+
/// for the two input extension field elements and the resulting output element.
15+
args: [usize; 3],
16+
}
17+
18+
impl ExtensionMulInstruction {
19+
/// Executes the `ExtensionMul` instruction.
20+
///
21+
/// Multiplies two extension field elements stored in memory and writes the result back.
22+
///
23+
/// ## Memory Layout
24+
/// The instruction takes three argument offsets `[a, b, c]` from the frame pointer `fp`:
25+
///
26+
/// - `fp + a`: address of a pointer to the first operand
27+
/// - `fp + b`: address of a pointer to the second operand
28+
/// - `fp + c`: address of a pointer to the output location
29+
///
30+
/// The multiplication is:
31+
///
32+
/// ```text
33+
/// m_vec[ptr_out] = EF::from(m_vec[ptr_lhs]) * EF::from(m_vec[ptr_rhs])
34+
/// ```
35+
///
36+
/// where `ptr_lhs`, `ptr_rhs`, and `ptr_out` are memory addresses stored at `fp + args[0..=2]`.
37+
pub fn execute(
38+
&self,
39+
run_context: &RunContext,
40+
memory_manager: &mut MemoryManager,
41+
) -> Result<(), VirtualMachineError> {
42+
// Get the frame pointer.
43+
let fp = run_context.fp;
44+
45+
// Read the memory addresses where the operands and result will reside.
46+
let ptr_lhs: MemoryAddress = memory_manager.memory.get_as((fp + self.args[0])?)?;
47+
let ptr_rhs: MemoryAddress = memory_manager.memory.get_as((fp + self.args[1])?)?;
48+
let ptr_out: MemoryAddress = memory_manager.memory.get_as((fp + self.args[2])?)?;
49+
50+
// Load the `[F; EF::DIMENSION]` input arrays from memory and convert them into EF elements.
51+
let lhs = EF::from_basis_coefficients_slice(
52+
&memory_manager
53+
.memory
54+
.get_array_as::<F, DIMENSION>(ptr_lhs)?,
55+
)
56+
.unwrap();
57+
58+
let rhs = EF::from_basis_coefficients_slice(
59+
&memory_manager
60+
.memory
61+
.get_array_as::<F, DIMENSION>(ptr_rhs)?,
62+
)
63+
.unwrap();
64+
65+
// Perform the field multiplication in the extension field.
66+
let product = lhs * rhs;
67+
68+
// Write the result converted back into `[F; EF::DIMENSION]` to memory.
69+
let result_coeffs: &[F] = product.as_basis_coefficients_slice();
70+
memory_manager.load_data(ptr_out, result_coeffs)?;
71+
72+
Ok(())
73+
}
74+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
use crate::bytecode::operand::{MemOrConstant, MemOrFp};
2+
3+
/// Conditional jump: `JumpUnlessZero`. Jumps if `condition != 0`.
4+
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
5+
pub struct JumpIfNotZeroInstruction {
6+
/// The value to check. The jump is taken if this value is not zero.
7+
pub condition: MemOrConstant,
8+
/// The destination `pc` for the jump.
9+
pub dest: MemOrConstant,
10+
/// The new value for the frame pointer (`fp`) after the instruction.
11+
pub updated_fp: MemOrFp,
12+
}

0 commit comments

Comments
 (0)