Skip to content

Commit eb339d8

Browse files
add qm31 operations to VM
1 parent d6d9a96 commit eb339d8

File tree

7 files changed

+285
-27
lines changed

7 files changed

+285
-27
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
#### Upcoming Changes
44

5+
* feat: implement an opcode that computes QM31 arithmetics (add, sub, mul, div) in the VM [#1938](https://github.com/lambdaclass/cairo-vm/pull/1938)
6+
57
* feat: add functions that compute packed reduced qm31 arithmetics to `math_utils` [#1944](https://github.com/lambdaclass/cairo-vm/pull/1944)
68

79
* feat: set `encoded_instruction` to be u128 for opcode_extensions to come [#1940](https://github.com/lambdaclass/cairo-vm/pull/1940)
Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
from starkware.cairo.common.alloc import alloc
2+
from starkware.cairo.common.bool import FALSE, TRUE
3+
4+
func main{}() {
5+
alloc_locals;
6+
7+
let (local qm31_op0_coordinates) = alloc();
8+
assert qm31_op0_coordinates[0] = 1414213562;
9+
assert qm31_op0_coordinates[1] = 1732050807;
10+
assert qm31_op0_coordinates[2] = 1618033988;
11+
assert qm31_op0_coordinates[3] = 1234567890;
12+
let qm31_op0 = qm31_op0_coordinates[0] + qm31_op0_coordinates[1]*(2**36) + qm31_op0_coordinates[2]*(2**72) + qm31_op0_coordinates[3]*(2**108);
13+
14+
let (local qm31_op1_coordinates) = alloc();
15+
assert qm31_op1_coordinates[0] = 1259921049;
16+
assert qm31_op1_coordinates[1] = 1442249570;
17+
assert qm31_op1_coordinates[2] = 1847759065;
18+
assert qm31_op1_coordinates[3] = 2094551481;
19+
let qm31_op1 = qm31_op1_coordinates[0] + qm31_op1_coordinates[1]*(2**36) + qm31_op1_coordinates[2]*(2**72) + qm31_op1_coordinates[3]*(2**108);
20+
21+
let (local qm31_add_dst_coordinates) = alloc();
22+
assert qm31_add_dst_coordinates[0] = 526650964;
23+
assert qm31_add_dst_coordinates[1] = 1026816730;
24+
assert qm31_add_dst_coordinates[2] = 1318309406;
25+
assert qm31_add_dst_coordinates[3] = 1181635724;
26+
let qm31_add_dst = qm31_add_dst_coordinates[0] + qm31_add_dst_coordinates[1]*(2**36) + qm31_add_dst_coordinates[2]*(2**72) + qm31_add_dst_coordinates[3]*(2**108);
27+
28+
let (local qm31_mul_dst_coordinates) = alloc();
29+
assert qm31_mul_dst_coordinates[0] = 947980980;
30+
assert qm31_mul_dst_coordinates[1] = 1510986506;
31+
assert qm31_mul_dst_coordinates[2] = 623360030;
32+
assert qm31_mul_dst_coordinates[3] = 1260310989;
33+
let qm31_mul_dst = qm31_mul_dst_coordinates[0] + qm31_mul_dst_coordinates[1]*(2**36) + qm31_mul_dst_coordinates[2]*(2**72) + qm31_mul_dst_coordinates[3]*(2**108);
34+
35+
let runner_output_mul_dst = run_qm31_operation_get_dst(is_mul=TRUE, op0=qm31_op0, op1=qm31_op1);
36+
assert runner_output_mul_dst = qm31_mul_dst;
37+
let runner_output_add_dst = run_qm31_operation_get_dst(is_mul=FALSE, op0=qm31_op0, op1=qm31_op1);
38+
assert runner_output_add_dst = qm31_add_dst;
39+
40+
let runner_output_mul_op1 = run_qm31_operation_get_op1(is_mul=TRUE, dst=qm31_mul_dst, op0=qm31_op0);
41+
assert runner_output_mul_op1 = qm31_op1;
42+
let runner_output_add_op1 = run_qm31_operation_get_op1(is_mul=FALSE, dst=qm31_add_dst, op0=qm31_op0);
43+
assert runner_output_add_op1 = qm31_op1;
44+
45+
let runner_output_mul_op0 = run_qm31_operation_get_op0(is_mul=TRUE, dst=qm31_mul_dst, op1=qm31_op1);
46+
assert runner_output_mul_op0 = qm31_op0;
47+
let runner_output_add_op0 = run_qm31_operation_get_op0(is_mul=FALSE, dst=qm31_add_dst, op1=qm31_op1);
48+
assert runner_output_add_op0 = qm31_op0;
49+
50+
return ();
51+
}
52+
53+
func run_qm31_operation_get_dst(
54+
is_mul: felt,
55+
op0: felt,
56+
op1: felt,
57+
) -> felt {
58+
let offset0 = 2**15;
59+
let offset1 = (2**15)-4;
60+
let offset2 = (2**15)-3;
61+
62+
let flag_dst_base_fp = 0;
63+
let flag_op0_base_fp = 1;
64+
let flag_op1_imm = 0;
65+
let flag_op1_base_fp = 1;
66+
let flag_op1_base_ap = 0;
67+
let flag_res_add = 0;
68+
let flag_res_mul = is_mul;
69+
let flag_PC_update_jump = 0;
70+
let flag_PC_update_jump_rel = 0;
71+
let flag_PC_update_jnz = 0;
72+
let flag_ap_update_add = 0;
73+
let flag_ap_update_add_1 = 0;
74+
let flag_opcode_call = 0;
75+
let flag_opcode_ret = 0;
76+
let flag_opcode_assert_eq = 1;
77+
78+
let flag_num_qm31_add = flag_dst_base_fp+flag_op0_base_fp*(2**1)+flag_op1_imm*(2**2)+flag_op1_base_fp*(2**3)+flag_op1_base_ap*(2**4)+0*(2**6)+flag_opcode_assert_eq*(2**14);
79+
let flag_num_qm31_mul = flag_dst_base_fp+flag_op0_base_fp*(2**1)+flag_op1_imm*(2**2)+flag_op1_base_fp*(2**3)+flag_op1_base_ap*(2**4)+1*(2**6)+flag_opcode_assert_eq*(2**14);
80+
let qm31_opcode_extension_num = 3;
81+
let qm31_add_instruction_num = offset0 + offset1*(2**16) + offset2*(2**32) + flag_num_qm31_add*(2**48) + qm31_opcode_extension_num*(2**63);
82+
let qm31_mul_instruction_num = offset0 + offset1*(2**16) + offset2*(2**32) + flag_num_qm31_mul*(2**48) + qm31_opcode_extension_num*(2**63);
83+
static_assert qm31_mul_instruction_num==32302772004019011584;
84+
static_assert qm31_add_instruction_num==32284757605509529600;
85+
86+
if (is_mul == TRUE) {
87+
dw 32302772004019011584;
88+
return [ap];
89+
}
90+
dw 32284757605509529600;
91+
return [ap];
92+
}
93+
94+
func run_qm31_operation_get_op1(
95+
is_mul: felt,
96+
dst: felt,
97+
op0: felt,
98+
) -> felt {
99+
let offset0 = (2**15)-4;
100+
let offset1 = (2**15)-3;
101+
let offset2 = 2**15;
102+
103+
let flag_dst_base_fp = 1;
104+
let flag_op0_base_fp = 1;
105+
let flag_op1_imm = 0;
106+
let flag_op1_base_fp = 0;
107+
let flag_op1_base_ap = 1;
108+
let flag_res_add = 0;
109+
let flag_res_mul = is_mul;
110+
let flag_PC_update_jump = 0;
111+
let flag_PC_update_jump_rel = 0;
112+
let flag_PC_update_jnz = 0;
113+
let flag_ap_update_add = 0;
114+
let flag_ap_update_add_1 = 0;
115+
let flag_opcode_call = 0;
116+
let flag_opcode_ret = 0;
117+
let flag_opcode_assert_eq = 1;
118+
119+
let flag_num_qm31_add = flag_dst_base_fp+flag_op0_base_fp*(2**1)+flag_op1_imm*(2**2)+flag_op1_base_fp*(2**3)+flag_op1_base_ap*(2**4)+0*(2**6)+flag_opcode_assert_eq*(2**14);
120+
let flag_num_qm31_mul = flag_dst_base_fp+flag_op0_base_fp*(2**1)+flag_op1_imm*(2**2)+flag_op1_base_fp*(2**3)+flag_op1_base_ap*(2**4)+1*(2**6)+flag_opcode_assert_eq*(2**14);
121+
let qm31_opcode_extension_num = 3;
122+
let qm31_add_instruction_num = offset0 + offset1*(2**16) + offset2*(2**32) + flag_num_qm31_add*(2**48) + qm31_opcode_extension_num*(2**63);
123+
let qm31_mul_instruction_num = offset0 + offset1*(2**16) + offset2*(2**32) + flag_num_qm31_mul*(2**48) + qm31_opcode_extension_num*(2**63);
124+
static_assert qm31_mul_instruction_num==32305305291694374908;
125+
static_assert qm31_add_instruction_num==32287290893184892924;
126+
127+
if (is_mul == TRUE) {
128+
dw 32305305291694374908;
129+
return [ap];
130+
}
131+
dw 32287290893184892924;
132+
return [ap];
133+
}
134+
135+
func run_qm31_operation_get_op0(
136+
is_mul: felt,
137+
dst: felt,
138+
op1: felt,
139+
) -> felt {
140+
let offset0 = (2**15)-4;
141+
let offset1 = 2**15;
142+
let offset2 = (2**15)-3;
143+
144+
let flag_dst_base_fp = 1;
145+
let flag_op0_base_fp = 0;
146+
let flag_op1_imm = 0;
147+
let flag_op1_base_fp = 1;
148+
let flag_op1_base_ap = 0;
149+
let flag_res_add = 0;
150+
let flag_res_mul = is_mul;
151+
let flag_PC_update_jump = 0;
152+
let flag_PC_update_jump_rel = 0;
153+
let flag_PC_update_jnz = 0;
154+
let flag_ap_update_add = 0;
155+
let flag_ap_update_add_1 = 0;
156+
let flag_opcode_call = 0;
157+
let flag_opcode_ret = 0;
158+
let flag_opcode_assert_eq = 1;
159+
160+
let flag_num_qm31_add = flag_dst_base_fp+flag_op0_base_fp*(2**1)+flag_op1_imm*(2**2)+flag_op1_base_fp*(2**3)+flag_op1_base_ap*(2**4)+0*(2**6)+flag_opcode_assert_eq*(2**14);
161+
let flag_num_qm31_mul = flag_dst_base_fp+flag_op0_base_fp*(2**1)+flag_op1_imm*(2**2)+flag_op1_base_fp*(2**3)+flag_op1_base_ap*(2**4)+1*(2**6)+flag_opcode_assert_eq*(2**14);
162+
let qm31_opcode_extension_num = 3;
163+
let qm31_add_instruction_num = offset0 + offset1*(2**16) + offset2*(2**32) + flag_num_qm31_add*(2**48) + qm31_opcode_extension_num*(2**63);
164+
let qm31_mul_instruction_num = offset0 + offset1*(2**16) + offset2*(2**32) + flag_num_qm31_mul*(2**48) + qm31_opcode_extension_num*(2**63);
165+
static_assert qm31_mul_instruction_num==32302490529042563068;
166+
static_assert qm31_add_instruction_num==32284476130533081084;
167+
168+
if (is_mul == TRUE) {
169+
dw 32302490529042563068;
170+
return [ap];
171+
}
172+
dw 32284476130533081084;
173+
return [ap];
174+
}

vm/src/tests/cairo_run_test.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -568,6 +568,14 @@ fn blake2s_integration_tests() {
568568
run_program_simple(program_data.as_slice());
569569
}
570570

571+
#[test]
572+
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
573+
fn qm31_opcodes_test() {
574+
let program_data =
575+
include_bytes!("../../../cairo_programs/stwo_exclusive_programs/qm31_opcodes_test.json");
576+
run_program_simple(program_data.as_slice());
577+
}
578+
571579
#[test]
572580
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
573581
fn relocate_segments() {

vm/src/types/instruction.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ pub enum Opcode {
8080
#[derive(Clone, Debug, Copy, PartialEq, Eq)]
8181
pub enum OpcodeExtension {
8282
Stone,
83+
QM31Operations,
8384
}
8485

8586
impl Instruction {

vm/src/vm/decoding/decoder.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,16 @@ pub fn decode_instruction(encoded_instr: u128) -> Result<Instruction, VirtualMac
105105

106106
let opcode_extension = match opcode_extension_num {
107107
0 => OpcodeExtension::Stone,
108+
3 => {
109+
if res == Res::Add
110+
|| (op1_addr != Op1Addr::FP && op1_addr != Op1Addr::AP)
111+
|| pc_update != PcUpdate::Regular
112+
|| opcode != Opcode::AssertEq
113+
{
114+
return Err(VirtualMachineError::InvalidQM31AddMulFlags(flags));
115+
}
116+
OpcodeExtension::QM31Operations
117+
}
108118
_ => {
109119
return Err(VirtualMachineError::InvalidOpcodeExtension(
110120
opcode_extension_num,

vm/src/vm/errors/vm_errors.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,10 @@ pub enum VirtualMachineError {
6262
"Failed to compute Res.MUL: Could not complete computation of non pure values {} * {}", (*.0).0, (*.0).1
6363
)]
6464
ComputeResRelocatableMul(Box<(MaybeRelocatable, MaybeRelocatable)>),
65+
#[error(
66+
"Failed to compute Res.ADD for QM31Operations: Could not complete computation of non pure values {} * {}", (*.0).0, (*.0).1
67+
)]
68+
ComputeResRelocatableQM31Add(Box<(MaybeRelocatable, MaybeRelocatable)>),
6569
#[error("Couldn't compute operand {}. Unknown value for memory cell {}", (*.0).0, (*.0).1)]
6670
FailedToComputeOperands(Box<(String, Relocatable)>),
6771
#[error("An ASSERT_EQ instruction failed: {} != {}.", (*.0).0, (*.0).1)]
@@ -138,6 +142,8 @@ pub enum VirtualMachineError {
138142
RelocationNotFound(usize),
139143
#[error("{} batch size is not {}", (*.0).0, (*.0).1)]
140144
ModBuiltinBatchSize(Box<(BuiltinName, usize)>),
145+
#[error("QM31 add mul opcode invalid flags {0}")]
146+
InvalidQM31AddMulFlags(u128),
141147
}
142148

143149
#[cfg(test)]

0 commit comments

Comments
 (0)