Skip to content

Commit 9ff96c8

Browse files
committed
hint: move execution logic to hint file
1 parent 52c2fcc commit 9ff96c8

File tree

2 files changed

+93
-84
lines changed

2 files changed

+93
-84
lines changed

crates/leanVm/src/bytecode/hint.rs

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
1+
use p3_field::{Field, PrimeCharacteristicRing, PrimeField64};
2+
13
use super::operand::MemOrConstant;
4+
use crate::{
5+
constant::F, context::run_context::RunContext, errors::vm::VirtualMachineError,
6+
memory::manager::MemoryManager,
7+
};
28

39
/// Hints are special instructions for the prover to resolve non-determinism.
410
///
@@ -37,3 +43,89 @@ pub enum Hint {
3743
content: Vec<MemOrConstant>,
3844
},
3945
}
46+
47+
impl Hint {
48+
/// Executes a single hint emitted by the compiler during bytecode interpretation.
49+
///
50+
/// Hints are runtime-only helper directives used for:
51+
/// - Allocating memory dynamically (`RequestMemory`)
52+
/// - Decomposing a field element into bits (`DecomposeBits`)
53+
/// - (Eventually) printing debug information (`Print`)
54+
///
55+
/// These are not part of the trace or AIR and are only used by the prover for state setup or inspection.
56+
/// The verifier does not need to observe these effects.
57+
fn execute(
58+
&self,
59+
memory_manager: &mut MemoryManager,
60+
run_context: &mut RunContext,
61+
) -> Result<(), VirtualMachineError> {
62+
match self {
63+
Self::RequestMemory {
64+
offset,
65+
size,
66+
vectorized,
67+
} => {
68+
// Resolve the `size` operand to a concrete field element.
69+
let size: F = run_context
70+
.value_from_mem_or_constant(size, memory_manager)?
71+
.try_into()?;
72+
// Convert the field element to a canonical `usize`.
73+
let size = size.as_canonical_u64() as usize;
74+
75+
// Compute the address where the allocated pointer will be stored: `fp + offset`.
76+
let addr = (run_context.fp + *offset)?;
77+
78+
if *vectorized {
79+
// Store the current vectorized allocation pointer (`ap_vectorized`) at `addr`.
80+
memory_manager
81+
.memory
82+
.insert(addr, F::from_usize(run_context.ap_vectorized))?;
83+
84+
// Increase the vectorized allocation pointer by `size` (number of vectors).
85+
run_context.ap_vectorized += size;
86+
} else {
87+
// Store the current scalar allocation pointer (`ap`) at `addr`.
88+
memory_manager
89+
.memory
90+
.insert(addr, F::from_usize(run_context.ap))?;
91+
92+
// Increase the scalar allocation pointer by `size` (number of scalars).
93+
run_context.ap += size;
94+
}
95+
}
96+
Self::DecomposeBits {
97+
res_offset,
98+
to_decompose,
99+
} => {
100+
// Resolve the operand to be decomposed into a concrete field element.
101+
let to_decompose: F = run_context
102+
.value_from_mem_or_constant(to_decompose, memory_manager)?
103+
.try_into()?;
104+
105+
// Convert the field element to a native `u64` for bit-level manipulation.
106+
let to_decompose = to_decompose.as_canonical_u64();
107+
108+
// For each bit position (up to the number of bits supported by the field):
109+
for i in 0..F::bits() {
110+
// - Extract the i-th bit from the value using bit masking.
111+
let bit = if to_decompose & (1 << i) != 0 {
112+
F::ONE
113+
} else {
114+
F::ZERO
115+
};
116+
117+
// - Compute the address at `fp + res_offset + i`.
118+
// - Write the extracted bit to that memory location.
119+
memory_manager
120+
.memory
121+
.insert(((run_context.fp + *res_offset)? + i)?, bit)?;
122+
}
123+
}
124+
Self::Print { .. } => {
125+
// TODO: implement
126+
// This is for debugging purposes only so this is not urgent for now.
127+
}
128+
}
129+
Ok(())
130+
}
131+
}

crates/leanVm/src/core.rs

Lines changed: 1 addition & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
1-
use p3_field::{BasedVectorSpace, Field, PrimeCharacteristicRing, PrimeField64};
1+
use p3_field::{BasedVectorSpace, Field, PrimeField64};
22
use p3_symmetric::Permutation;
33

44
use crate::{
55
bytecode::{
6-
hint::Hint,
76
instruction::Instruction,
87
operand::{MemOrConstant, MemOrFp, MemOrFpOrConstant},
98
operation::Operation,
@@ -32,88 +31,6 @@ impl<PERM16, PERM24> VirtualMachine<PERM16, PERM24> {
3231
}
3332
}
3433

35-
/// Executes a single hint emitted by the compiler during bytecode interpretation.
36-
///
37-
/// Hints are runtime-only helper directives used for:
38-
/// - Allocating memory dynamically (`RequestMemory`)
39-
/// - Decomposing a field element into bits (`DecomposeBits`)
40-
/// - (Eventually) printing debug information (`Print`)
41-
///
42-
/// These are not part of the trace or AIR and are only used by the prover for state setup or inspection.
43-
/// The verifier does not need to observe these effects.
44-
fn execute_hint(&mut self, hint: &Hint) -> Result<(), VirtualMachineError> {
45-
match hint {
46-
Hint::RequestMemory {
47-
offset,
48-
size,
49-
vectorized,
50-
} => {
51-
// Resolve the `size` operand to a concrete field element.
52-
let size: F = self
53-
.run_context
54-
.value_from_mem_or_constant(size, &self.memory_manager)?
55-
.try_into()?;
56-
// Convert the field element to a canonical `usize`.
57-
let size = size.as_canonical_u64() as usize;
58-
59-
// Compute the address where the allocated pointer will be stored: `fp + offset`.
60-
let addr = (self.run_context.fp + *offset)?;
61-
62-
if *vectorized {
63-
// Store the current vectorized allocation pointer (`ap_vectorized`) at `addr`.
64-
self.memory_manager
65-
.memory
66-
.insert(addr, F::from_usize(self.run_context.ap_vectorized))?;
67-
68-
// Increase the vectorized allocation pointer by `size` (number of vectors).
69-
self.run_context.ap_vectorized += size;
70-
} else {
71-
// Store the current scalar allocation pointer (`ap`) at `addr`.
72-
self.memory_manager
73-
.memory
74-
.insert(addr, F::from_usize(self.run_context.ap))?;
75-
76-
// Increase the scalar allocation pointer by `size` (number of scalars).
77-
self.run_context.ap += size;
78-
}
79-
}
80-
Hint::DecomposeBits {
81-
res_offset,
82-
to_decompose,
83-
} => {
84-
// Resolve the operand to be decomposed into a concrete field element.
85-
let to_decompose: F = self
86-
.run_context
87-
.value_from_mem_or_constant(to_decompose, &self.memory_manager)?
88-
.try_into()?;
89-
90-
// Convert the field element to a native `u64` for bit-level manipulation.
91-
let to_decompose = to_decompose.as_canonical_u64();
92-
93-
// For each bit position (up to the number of bits supported by the field):
94-
for i in 0..F::bits() {
95-
// - Extract the i-th bit from the value using bit masking.
96-
let bit = if to_decompose & (1 << i) != 0 {
97-
F::ONE
98-
} else {
99-
F::ZERO
100-
};
101-
102-
// - Compute the address at `fp + res_offset + i`.
103-
// - Write the extracted bit to that memory location.
104-
self.memory_manager
105-
.memory
106-
.insert(((self.run_context.fp + *res_offset)? + i)?, bit)?;
107-
}
108-
}
109-
Hint::Print { .. } => {
110-
// TODO: implement
111-
// This is for debugging purposes only so this is not urgent for now.
112-
}
113-
}
114-
Ok(())
115-
}
116-
11734
/// Advances the program counter (`pc`) to the next instruction.
11835
///
11936
/// This function embodies the control flow logic of the zkVM. For most instructions,

0 commit comments

Comments
 (0)