Skip to content

Commit dc68223

Browse files
committed
intruction: better encoder
1 parent dd5acc0 commit dc68223

File tree

5 files changed

+109
-50
lines changed

5 files changed

+109
-50
lines changed

crates/leanVm/src/air/constant.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,17 @@ pub(crate) const COL_INDEX_DEREF: usize = 8;
4141
pub(crate) const COL_INDEX_JUZ: usize = 9;
4242
/// An auxiliary operand used for multi-purpose logic, like specifying the `res` type in DEREF.
4343
pub(crate) const COL_INDEX_AUX: usize = 10;
44+
/// The opcode flag that activates the Poseidon16 precompile's constraints.
45+
pub(crate) const COL_INDEX_POSEIDON_16: usize = 11;
46+
/// The opcode flag that activates the Poseidon24 precompile's constraints.
47+
pub(crate) const COL_INDEX_POSEIDON_24: usize = 12;
48+
/// The opcode flag that activates the DotProduct precompile's constraints.
49+
pub(crate) const COL_INDEX_DOT_PRODUCT: usize = 13;
50+
/// The opcode flag that activates the MultilinearEval precompile's constraints.
51+
pub(crate) const COL_INDEX_MULTILINEAR_EVAL: usize = 14;
52+
4453
/// The total number of columns representing a single decoded instruction from the bytecode.
45-
pub(crate) const N_BYTECODE_COLUMNS: usize = 11;
54+
pub(crate) const N_BYTECODE_COLUMNS: usize = 15;
4655

4756
// Execution trace columns (committed by the prover)
4857

crates/leanVm/src/bytecode/instruction/computation.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ pub struct ComputationInstruction {
1616
/// The first operand of the computation.
1717
pub arg_a: MemOrConstant,
1818
/// The second operand of the computation.
19-
pub arg_b: MemOrFp,
19+
pub arg_c: MemOrFp,
2020
/// The memory location or constant that the result must be equal to.
2121
pub res: MemOrConstant,
2222
}
@@ -38,7 +38,7 @@ impl ComputationInstruction {
3838
memory_manager: &mut MemoryManager,
3939
) -> Result<(), VirtualMachineError> {
4040
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);
41+
let val_b = run_context.value_from_mem_or_fp(&self.arg_c, memory_manager);
4242
let val_res = run_context.value_from_mem_or_constant(&self.res, memory_manager);
4343

4444
match (val_a, val_b, val_res) {

crates/leanVm/src/bytecode/instruction/mod.rs

Lines changed: 60 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,13 @@ use p3_symmetric::Permutation;
88
use poseidon16::Poseidon2_16Instruction;
99
use poseidon24::Poseidon2_24Instruction;
1010

11-
use super::operand::{MemOrConstant, MemOrFp, MemOrFpOrConstant};
11+
use super::operand::MemOrFpOrConstant;
1212
use crate::{
1313
air::constant::{
14-
COL_INDEX_ADD, COL_INDEX_AUX, COL_INDEX_DEREF, COL_INDEX_FLAG_A, COL_INDEX_FLAG_B,
15-
COL_INDEX_FLAG_C, COL_INDEX_JUZ, COL_INDEX_MUL, COL_INDEX_OPERAND_A, COL_INDEX_OPERAND_B,
16-
COL_INDEX_OPERAND_C, N_INSTRUCTION_COLUMNS,
14+
COL_INDEX_ADD, COL_INDEX_AUX, COL_INDEX_DEREF, COL_INDEX_DOT_PRODUCT, COL_INDEX_FLAG_A,
15+
COL_INDEX_FLAG_B, COL_INDEX_FLAG_C, COL_INDEX_JUZ, COL_INDEX_MUL,
16+
COL_INDEX_MULTILINEAR_EVAL, COL_INDEX_OPERAND_A, COL_INDEX_OPERAND_B, COL_INDEX_OPERAND_C,
17+
COL_INDEX_POSEIDON_16, COL_INDEX_POSEIDON_24, N_INSTRUCTION_COLUMNS,
1718
},
1819
bytecode::operation::Operation,
1920
constant::{DIMENSION, F},
@@ -161,26 +162,18 @@ impl Instruction {
161162
Self::Computation(ComputationInstruction {
162163
operation,
163164
arg_a,
164-
arg_b,
165+
arg_c,
165166
res,
166167
}) => {
167168
// Set the appropriate opcode flag for ADD or MUL.
168169
match operation {
169170
Operation::Add => repr[COL_INDEX_ADD] = F::ONE,
170171
Operation::Mul => repr[COL_INDEX_MUL] = F::ONE,
171172
}
172-
// Convert operands to their field and flag representations.
173-
let (op_a, flag_a) = op_to_field(arg_a);
174-
let (op_b, flag_b) = op_to_field_fp(arg_b);
175-
let (op_c, flag_c) = op_to_field(res);
176-
177-
// Populate the representation vector.
178-
repr[COL_INDEX_OPERAND_A] = op_a;
179-
repr[COL_INDEX_FLAG_A] = flag_a;
180-
repr[COL_INDEX_OPERAND_B] = op_b;
181-
repr[COL_INDEX_FLAG_B] = flag_b;
182-
repr[COL_INDEX_OPERAND_C] = op_c;
183-
repr[COL_INDEX_FLAG_C] = flag_c;
173+
// Convert operands to their field and flag representations using the helper methods.
174+
(repr[COL_INDEX_OPERAND_A], repr[COL_INDEX_FLAG_A]) = arg_a.to_field_and_flag();
175+
(repr[COL_INDEX_OPERAND_B], repr[COL_INDEX_FLAG_B]) = res.to_field_and_flag();
176+
(repr[COL_INDEX_OPERAND_C], repr[COL_INDEX_FLAG_C]) = arg_c.to_field_and_flag();
184177
}
185178
Self::Deref(DerefInstruction {
186179
shift_0,
@@ -221,37 +214,58 @@ impl Instruction {
221214
// Set the JUZ opcode flag.
222215
repr[COL_INDEX_JUZ] = F::ONE;
223216
// Convert operands to their field and flag representations.
224-
let (op_a, flag_a) = op_to_field(condition);
225-
let (op_b, flag_b) = op_to_field_fp(updated_fp);
226-
let (op_c, flag_c) = op_to_field(dest);
227-
228-
// Populate the representation vector.
229-
repr[COL_INDEX_OPERAND_A] = op_a;
230-
repr[COL_INDEX_FLAG_A] = flag_a;
231-
repr[COL_INDEX_OPERAND_B] = op_b;
232-
repr[COL_INDEX_FLAG_B] = flag_b;
233-
repr[COL_INDEX_OPERAND_C] = op_c;
234-
repr[COL_INDEX_FLAG_C] = flag_c;
217+
(repr[COL_INDEX_OPERAND_A], repr[COL_INDEX_FLAG_A]) = condition.to_field_and_flag();
218+
(repr[COL_INDEX_OPERAND_B], repr[COL_INDEX_FLAG_B]) =
219+
updated_fp.to_field_and_flag();
220+
(repr[COL_INDEX_OPERAND_C], repr[COL_INDEX_FLAG_C]) = dest.to_field_and_flag();
221+
}
222+
Self::Poseidon2_16(Poseidon2_16Instruction { arg_a, arg_b, res }) => {
223+
// Set the Poseidon16 opcode flag.
224+
repr[COL_INDEX_POSEIDON_16] = F::ONE;
225+
// Convert operands to their field and flag representations.
226+
(repr[COL_INDEX_OPERAND_A], repr[COL_INDEX_FLAG_A]) = arg_a.to_field_and_flag();
227+
(repr[COL_INDEX_OPERAND_B], repr[COL_INDEX_FLAG_B]) = arg_b.to_field_and_flag();
228+
(repr[COL_INDEX_OPERAND_C], repr[COL_INDEX_FLAG_C]) = res.to_field_and_flag();
229+
}
230+
Self::Poseidon2_24(Poseidon2_24Instruction { arg_a, arg_b, res }) => {
231+
// Set the Poseidon24 opcode flag.
232+
repr[COL_INDEX_POSEIDON_24] = F::ONE;
233+
// Convert operands to their field and flag representations.
234+
(repr[COL_INDEX_OPERAND_A], repr[COL_INDEX_FLAG_A]) = arg_a.to_field_and_flag();
235+
(repr[COL_INDEX_OPERAND_B], repr[COL_INDEX_FLAG_B]) = arg_b.to_field_and_flag();
236+
(repr[COL_INDEX_OPERAND_C], repr[COL_INDEX_FLAG_C]) = res.to_field_and_flag();
237+
}
238+
Self::DotProduct(DotProductInstruction {
239+
arg0,
240+
arg1,
241+
res,
242+
size,
243+
}) => {
244+
// Set the DotProduct opcode flag.
245+
repr[COL_INDEX_DOT_PRODUCT] = F::ONE;
246+
// Convert operands to their field and flag representations.
247+
(repr[COL_INDEX_OPERAND_A], repr[COL_INDEX_FLAG_A]) = arg0.to_field_and_flag();
248+
(repr[COL_INDEX_OPERAND_B], repr[COL_INDEX_FLAG_B]) = arg1.to_field_and_flag();
249+
(repr[COL_INDEX_OPERAND_C], repr[COL_INDEX_FLAG_C]) = res.to_field_and_flag();
250+
// Use the AUX column to store the size of the vectors.
251+
repr[COL_INDEX_AUX] = F::from_usize(*size);
252+
}
253+
Self::MultilinearEval(MultilinearEvalInstruction {
254+
coeffs,
255+
point,
256+
res,
257+
n_vars,
258+
}) => {
259+
// Set the MultilinearEval opcode flag.
260+
repr[COL_INDEX_MULTILINEAR_EVAL] = F::ONE;
261+
// Convert operands to their field and flag representations.
262+
(repr[COL_INDEX_OPERAND_A], repr[COL_INDEX_FLAG_A]) = coeffs.to_field_and_flag();
263+
(repr[COL_INDEX_OPERAND_B], repr[COL_INDEX_FLAG_B]) = point.to_field_and_flag();
264+
(repr[COL_INDEX_OPERAND_C], repr[COL_INDEX_FLAG_C]) = res.to_field_and_flag();
265+
// Use the AUX column to store the number of variables.
266+
repr[COL_INDEX_AUX] = F::from_usize(*n_vars);
235267
}
236-
// Precompiles do not set flags in the main instruction columns.
237-
_ => {}
238268
}
239269
repr
240270
}
241271
}
242-
243-
/// Helper to convert a `MemOrConstant` operand into its (value, flag) pair.
244-
fn op_to_field(op: &MemOrConstant) -> (F, F) {
245-
match op {
246-
MemOrConstant::Constant(c) => (*c, F::ONE),
247-
MemOrConstant::MemoryAfterFp { offset } => (F::from_usize(*offset), F::ZERO),
248-
}
249-
}
250-
251-
/// Helper to convert a `MemOrFp` operand into its (value, flag) pair.
252-
fn op_to_field_fp(op: &MemOrFp) -> (F, F) {
253-
match op {
254-
MemOrFp::Fp => (F::ZERO, F::ONE), // Represents the `fp` register itself.
255-
MemOrFp::MemoryAfterFp { offset } => (F::from_usize(*offset), F::ZERO),
256-
}
257-
}

crates/leanVm/src/bytecode/operand.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use p3_field::PrimeCharacteristicRing;
2+
13
use crate::constant::F;
24

35
/// Represents a value that can either be a constant or a value from memory.
@@ -14,6 +16,23 @@ pub enum MemOrConstant {
1416
},
1517
}
1618

19+
impl MemOrConstant {
20+
/// Converts the operand into its raw field representation for the trace.
21+
///
22+
/// Returns a tuple `(operand, flag)` where:
23+
/// - `operand`: The field element representing the constant value or memory offset.
24+
/// - `flag`: A flag that is `1` for a constant and `0` for a memory access.
25+
#[must_use]
26+
pub fn to_field_and_flag(&self) -> (F, F) {
27+
match self {
28+
// If it's a constant, the flag is 1 and the value is the constant itself.
29+
Self::Constant(c) => (*c, F::ONE),
30+
// If it's a memory location, the flag is 0 and the value is the offset.
31+
Self::MemoryAfterFp { offset } => (F::from_usize(*offset), F::ZERO),
32+
}
33+
}
34+
}
35+
1736
/// Represents a value that can be a memory location, the `fp` register itself, or a constant.
1837
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
1938
pub enum MemOrFpOrConstant {
@@ -39,3 +58,20 @@ pub enum MemOrFp {
3958
/// The value of the frame pointer (`fp`) register itself.
4059
Fp,
4160
}
61+
62+
impl MemOrFp {
63+
/// Converts the operand into its raw field representation for the trace.
64+
///
65+
/// Returns a tuple `(operand, flag)` where:
66+
/// - `operand`: The field element representing the memory offset (or 0 if `Fp`).
67+
/// - `flag`: A flag that is `1` for the `Fp` register and `0` for a memory access.
68+
#[must_use]
69+
pub fn to_field_and_flag(&self) -> (F, F) {
70+
match self {
71+
// If it's the frame pointer, the flag is 1 and the operand value is 0.
72+
Self::Fp => (F::ZERO, F::ONE),
73+
// If it's a memory location, the flag is 0 and the value is the offset.
74+
Self::MemoryAfterFp { offset } => (F::from_usize(*offset), F::ZERO),
75+
}
76+
}
77+
}

crates/leanVm/src/core.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -473,7 +473,7 @@ mod tests {
473473
let instruction = Instruction::Computation(ComputationInstruction {
474474
operation: Operation::Add,
475475
arg_a: MemOrConstant::Constant(F::ONE),
476-
arg_b: MemOrFp::Fp,
476+
arg_c: MemOrFp::Fp,
477477
res: MemOrConstant::MemoryAfterFp { offset: 0 },
478478
});
479479
// Execute: Update the PC based on this instruction.

0 commit comments

Comments
 (0)