Skip to content

Commit 33f050b

Browse files
committed
instruction: documentation
1 parent 99c9b40 commit 33f050b

File tree

1 file changed

+113
-18
lines changed

1 file changed

+113
-18
lines changed

crates/leanVm/src/types/instruction.rs

Lines changed: 113 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,89 +2,184 @@ use std::collections::BTreeMap;
22

33
type Label = String;
44

5+
/// Represents the compiled bytecode of a program for the zkVM.
56
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
67
pub struct Bytecode<F> {
8+
/// A vector of instructions that form the executable part of the program.
9+
///
10+
/// The Program Counter (pc) iterates over this vector.
711
pub instructions: Vec<Instruction<F>>,
8-
pub hints: BTreeMap<usize, Vec<Hint<F>>>, // pc -> hints
12+
13+
/// A map from a program counter (pc) value to a list of `Hint`s.
14+
///
15+
/// Hints are auxiliary, non-deterministic instructions executed only by the prover.
16+
///
17+
/// In this zkVM, they are crucial for managing memory allocations in the absence
18+
/// of an `ap` register.
19+
pub hints: BTreeMap<usize, Vec<Hint<F>>>,
20+
21+
/// The memory offset from the frame pointer (fp) where the public input for the program begins.
922
pub public_input_start: usize,
23+
24+
/// The program counter (pc) value at which the program execution is considered complete.
1025
pub ending_pc: usize,
1126
}
1227

28+
/// Represents a value that can either be a constant or a value from memory.
1329
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
1430
pub enum MemOrConstant<F> {
31+
/// A constant value (a field element).
1532
Constant(F),
16-
MemoryAfterFp { shift: usize }, // m[fp + shift]
33+
/// A memory location specified by a positive offset from the frame pointer (`fp`).
34+
///
35+
/// Represents the scalar value at `m[fp + shift]`.
36+
MemoryAfterFp {
37+
/// The offset from `fp` where the memory location is located.
38+
shift: usize,
39+
},
1740
}
1841

42+
/// Represents a value that can be a memory location, the `fp` register itself, or a constant.
1943
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
2044
pub enum MemOrFpOrConstant<F> {
21-
MemoryAfterFp { shift: usize }, // m[fp + shift]
45+
/// A memory location specified by a positive offset from `fp`. Represents `m[fp + shift]`.
46+
MemoryAfterFp {
47+
/// The offset from `fp` where the memory location is located.
48+
shift: usize,
49+
},
50+
/// The value of the frame pointer (`fp`) register itself.
2251
Fp,
52+
/// A constant value (a field element).
2353
Constant(F),
2454
}
2555

56+
/// Represents a value that is either a memory location or the `fp` register itself.
2657
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
2758
pub enum MemOrFp {
28-
MemoryAfterFp { shift: usize }, // m[fp + shift]
59+
/// A memory location specified by a positive offset from `fp`. Represents `m[fp + shift]`.
60+
MemoryAfterFp {
61+
/// The offset from `fp` where the memory location is located.
62+
shift: usize,
63+
},
64+
/// The value of the frame pointer (`fp`) register itself.
2965
Fp,
3066
}
3167

68+
/// The basic arithmetic operations supported by the VM's `Computation` instruction.
3269
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
3370
pub enum Operation {
71+
/// Field addition in the base field.
3472
Add,
73+
/// Field multiplication in the base field.
3574
Mul,
3675
}
3776

77+
/// Defines the instruction set for this zkVM, specialized for the `AggregateMerge` logic.
78+
///
79+
/// The ISA is minimal and includes basic arithmetic, memory operations, control flow,
80+
/// and powerful precompiles for hashing and extension field arithmetic.
3881
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
3982
pub enum Instruction<F> {
83+
/// Performs a basic arithmetic computation: `res = arg_a op arg_b`.
84+
///
85+
/// This corresponds to the `ADD` and `MUL` opcodes in the design document.
4086
Computation {
87+
/// The arithmetic operation to perform (`Add` or `Mul`).
4188
operation: Operation,
89+
/// The first operand of the computation.
4290
arg_a: MemOrConstant<F>,
91+
/// The second operand of the computation.
4392
arg_b: MemOrFp,
93+
/// The memory location or constant that the result must be equal to.
4494
res: MemOrConstant<F>,
4595
},
96+
/// Performs a memory dereference: `res = m[m[fp + shift_0] + shift_1]`.
97+
///
98+
/// This corresponds to the `DEREF` opcode.
4699
Deref {
100+
/// The offset from `fp` for the first memory access, which yields a pointer.
47101
shift_0: usize,
102+
/// The offset added to the pointer from the first access to get the final address.
48103
shift_1: usize,
104+
/// The value that the result of the double dereference must be equal to.
49105
res: MemOrFpOrConstant<F>,
50-
}, // res = m[m[fp + shift_0] + shift_1]
106+
},
107+
/// A conditional jump, called `JUZ` (Jump Unless Zero)
108+
/// .
109+
/// Changes the `pc` if `condition` is non-zero.
51110
JumpIfNotZero {
111+
/// The value to check. The jump is taken if this value is not zero.
52112
condition: MemOrConstant<F>,
113+
/// The destination `pc` for the jump.
53114
dest: MemOrConstant<F>,
115+
/// The new value for the frame pointer (`fp`) after the instruction.
54116
updated_fp: MemOrFp,
55117
},
118+
/// **Precompile** for a Poseidon2 permutation over 16 base field elements.
119+
///
120+
/// This is used for hashing operations within the `AggregateMerge` algorithm.
121+
/// The precompile performs: `Poseidon2(m'[m[fp+s]], m'[m[fp+s+1]]) = (m'[m[fp+s+2]], m'[m[fp+s+3]])`,
122+
/// where:
123+
/// - `s` is the shift,
124+
/// - `m` is scalar memory,
125+
/// - `m'` is vectorized memory access (a chunk of 8 base field elements, representing a degree-8 extension field element).
56126
Poseidon2_16 {
127+
/// The starting offset `s` from `fp`. The instruction reads 4 pointers from `m[fp+s]` to `m[fp+s+3]`.
57128
shift: usize,
58-
}, /*
59-
Read 4 vectorized pointers from stack:
60-
Poseidon2(m[8 * m[fp + shift]] .. 8 * (1 + m[fp + shift])] | m[8 * m[fp + shift + 1]] .. 8 * (1 + m[fp + shift + 1])])
61-
= m[8 * m[fp + shift + 2]] .. 8 * (1 + m[fp + shift + 2])] | m[8 * m[fp + shift + 3]] .. 8 * (1 + m[fp + shift + 3])]
62-
*/
129+
},
130+
/// **Precompile** for a Poseidon2 permutation over 24 base field elements.
131+
///
132+
/// This operates similarly to `Poseidon2_16` but on 3 concatenated input vectors and 3 output vectors.
133+
///
134+
/// It reads 6 pointers from memory, starting at `m[fp+shift]`.
63135
Poseidon2_24 {
136+
/// The starting offset from `fp`. The instruction reads 6 pointers from `m[fp+shift]` to `m[fp+shift+5]`.
64137
shift: usize,
65-
}, // same as above, but with 24 field elements
138+
},
139+
/// **Precompile** for multiplication in the degree-8 extension field.
140+
///
141+
/// This is important for speeding up recursive proof verification (`snark_verify`).
66142
ExtensionMul {
67-
args: [usize; 3], // offset after fp
143+
/// An array of three offsets from `fp`. These point to the start of the 8-cell memory blocks
144+
/// for the two input extension field elements and the resulting output element.
145+
args: [usize; 3],
68146
},
69147
}
70148

71-
/// Hints does not appear in the verified bytecode, but are useful during execution
149+
/// Hints are special instructions for the prover to resolve non-determinism.
150+
///
151+
/// They are not part of the verified computation trace.
72152
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
73153
pub enum Hint<F> {
154+
/// A hint for the prover to allocate a new memory segment for a function's stack frame.
155+
///
156+
/// This is the core mechanism for memory management in a VM without an `ap` (allocation pointer)
157+
/// register. The compiler pre-calculates the required memory size for each function.
74158
RequestMemory {
75-
offset: usize, // the pointer to the allocated memory range will be stored at m[fp + offset]
159+
/// The offset from `fp` where the pointer to the newly allocated segment will be stored.
160+
offset: usize,
161+
/// The requested size of the memory segment in scalar field elements.
76162
size: MemOrConstant<F>,
77-
/// if vectorized == true, the start of the allocated memory will be aligned to 8 field elements
78-
/// m[8X...] and we set m[fp + offset] = X
163+
/// If true, the start of the allocated memory is aligned to an 8-element boundary
164+
/// to facilitate vectorized memory access for extension field operations.
165+
/// The value stored at `m[fp + offset]` will be the aligned address divided by 8.
79166
vectorized: bool,
80167
},
168+
/// A hint for the prover to compute the bit decomposition of a base field element.
169+
///
170+
/// This is a non-deterministic operation used for operations like range checks
171+
/// or other logic required by the XMSS signature scheme.
81172
DecomposeBits {
82-
res_offset: usize, // m[fp + res_offset..fp + res_offset + 31] will contain the decomposed bits
173+
/// The starting offset from `fp` where the resulting bits will be stored.
174+
res_offset: usize,
175+
/// The field element that needs to be decomposed into its bits.
83176
to_decompose: MemOrConstant<F>,
84177
},
85-
// Debug purpose
178+
/// A hint used for debugging to print values from memory during execution.
86179
Print {
180+
/// A string containing line information (e.g., file and line number) for context.
87181
line_info: String,
182+
/// A list of memory locations or constants whose values should be printed.
88183
content: Vec<MemOrConstant<F>>,
89184
},
90185
}

0 commit comments

Comments
 (0)