Skip to content

Commit bd6140f

Browse files
add qm31 operations to VM
1 parent ef1aa46 commit bd6140f

File tree

7 files changed

+446
-27
lines changed

7 files changed

+446
-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: 263 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,263 @@
1+
from starkware.cairo.common.bool import FALSE, TRUE
2+
3+
func main{}() {
4+
let qm31_op0_coordinates_a = 1414213562;
5+
let qm31_op0_coordinates_b = 1732050807;
6+
let qm31_op0_coordinates_c = 1618033988;
7+
let qm31_op0_coordinates_d = 1234567890;
8+
let qm31_op0 = qm31_op0_coordinates_a + qm31_op0_coordinates_b*(2**36) + qm31_op0_coordinates_c*(2**72) + qm31_op0_coordinates_d*(2**108);
9+
10+
let qm31_op1_coordinates_a = 1259921049;
11+
let qm31_op1_coordinates_b = 1442249570;
12+
let qm31_op1_coordinates_c = 1847759065;
13+
let qm31_op1_coordinates_d = 2094551481;
14+
let qm31_op1 = qm31_op1_coordinates_a + qm31_op1_coordinates_b*(2**36) + qm31_op1_coordinates_c*(2**72) + qm31_op1_coordinates_d*(2**108);
15+
static_assert qm31_op1==679720817185961464190715473544778505313945;
16+
17+
18+
let qm31_add_dst_coordinates_a = 526650964;
19+
let qm31_add_dst_coordinates_b = 1026816730;
20+
let qm31_add_dst_coordinates_c = 1318309406;
21+
let qm31_add_dst_coordinates_d = 1181635724;
22+
let qm31_add_dst = qm31_add_dst_coordinates_a + qm31_add_dst_coordinates_b*(2**36) + qm31_add_dst_coordinates_c*(2**72) + qm31_add_dst_coordinates_d*(2**108);
23+
24+
let qm31_mul_dst_coordinates_a = 947980980;
25+
let qm31_mul_dst_coordinates_b = 1510986506;
26+
let qm31_mul_dst_coordinates_c = 623360030;
27+
let qm31_mul_dst_coordinates_d = 1260310989;
28+
let qm31_mul_dst = qm31_mul_dst_coordinates_a + qm31_mul_dst_coordinates_b*(2**36) + qm31_mul_dst_coordinates_c*(2**72) + qm31_mul_dst_coordinates_d*(2**108);
29+
30+
let runner_output_mul_dst = run_qm31_operation_get_dst(is_mul=TRUE, op0=qm31_op0, op1=qm31_op1);
31+
assert runner_output_mul_dst = qm31_mul_dst;
32+
let runner_output_add_dst = run_qm31_operation_get_dst(is_mul=FALSE, op0=qm31_op0, op1=qm31_op1);
33+
assert runner_output_add_dst = qm31_add_dst;
34+
35+
let runner_output_mul_op1 = run_qm31_operation_get_op1(is_mul=TRUE, dst=qm31_mul_dst, op0=qm31_op0);
36+
assert runner_output_mul_op1 = qm31_op1;
37+
let runner_output_add_op1 = run_qm31_operation_get_op1(is_mul=FALSE, dst=qm31_add_dst, op0=qm31_op0);
38+
assert runner_output_add_op1 = qm31_op1;
39+
40+
let runner_output_mul_op0 = run_qm31_operation_get_op0(is_mul=TRUE, dst=qm31_mul_dst, op1=qm31_op1);
41+
assert runner_output_mul_op0 = qm31_op0;
42+
let runner_output_add_op0 = run_qm31_operation_get_op0(is_mul=FALSE, dst=qm31_add_dst, op1=qm31_op1);
43+
assert runner_output_add_op0 = qm31_op0;
44+
45+
let runner_output_mul_dst = run_qm31_operation_imm_op1_get_dst(is_mul=TRUE, op0=qm31_op0);
46+
assert runner_output_mul_dst = qm31_mul_dst;
47+
let runner_output_add_dst = run_qm31_operation_imm_op1_get_dst(is_mul=FALSE, op0=qm31_op0);
48+
assert runner_output_add_dst = qm31_add_dst;
49+
50+
let runner_output_mul_op0 = run_qm31_operation_imm_op1_get_op0(is_mul=TRUE, dst=qm31_mul_dst);
51+
assert runner_output_mul_op0 = qm31_op0;
52+
let runner_output_add_op0 = run_qm31_operation_imm_op1_get_op0(is_mul=FALSE, dst=qm31_add_dst);
53+
assert runner_output_add_op0 = qm31_op0;
54+
55+
return ();
56+
}
57+
58+
func run_qm31_operation_get_dst(
59+
is_mul: felt,
60+
op0: felt,
61+
op1: felt,
62+
) -> felt {
63+
let offset0 = 2**15;
64+
let offset1 = (2**15)-4;
65+
let offset2 = (2**15)-3;
66+
67+
let flag_dst_base_fp = 0;
68+
let flag_op0_base_fp = 1;
69+
let flag_op1_imm = 0;
70+
let flag_op1_base_fp = 1;
71+
let flag_op1_base_ap = 0;
72+
let flag_res_add = 1-is_mul;
73+
let flag_res_mul = is_mul;
74+
let flag_PC_update_jump = 0;
75+
let flag_PC_update_jump_rel = 0;
76+
let flag_PC_update_jnz = 0;
77+
let flag_ap_update_add = 0;
78+
let flag_ap_update_add_1 = 0;
79+
let flag_opcode_call = 0;
80+
let flag_opcode_ret = 0;
81+
let flag_opcode_assert_eq = 1;
82+
83+
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)+1*(2**5)+0*(2**6)+flag_opcode_assert_eq*(2**14);
84+
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)+0*(2**5)+1*(2**6)+flag_opcode_assert_eq*(2**14);
85+
let qm31_opcode_extension_num = 3;
86+
let qm31_add_instruction_num = offset0 + offset1*(2**16) + offset2*(2**32) + flag_num_qm31_add*(2**48) + qm31_opcode_extension_num*(2**63);
87+
let qm31_mul_instruction_num = offset0 + offset1*(2**16) + offset2*(2**32) + flag_num_qm31_mul*(2**48) + qm31_opcode_extension_num*(2**63);
88+
static_assert qm31_mul_instruction_num==32302772004019011584;
89+
static_assert qm31_add_instruction_num==32293764804764270592;
90+
91+
if (is_mul == TRUE) {
92+
dw 32302772004019011584;
93+
return [ap];
94+
}
95+
dw 32293764804764270592;
96+
return [ap];
97+
}
98+
99+
func run_qm31_operation_get_op1(
100+
is_mul: felt,
101+
dst: felt,
102+
op0: felt,
103+
) -> felt {
104+
let offset0 = (2**15)-4;
105+
let offset1 = (2**15)-3;
106+
let offset2 = 2**15;
107+
108+
let flag_dst_base_fp = 1;
109+
let flag_op0_base_fp = 1;
110+
let flag_op1_imm = 0;
111+
let flag_op1_base_fp = 0;
112+
let flag_op1_base_ap = 1;
113+
let flag_res_add = 1-is_mul;
114+
let flag_res_mul = is_mul;
115+
let flag_PC_update_jump = 0;
116+
let flag_PC_update_jump_rel = 0;
117+
let flag_PC_update_jnz = 0;
118+
let flag_ap_update_add = 0;
119+
let flag_ap_update_add_1 = 0;
120+
let flag_opcode_call = 0;
121+
let flag_opcode_ret = 0;
122+
let flag_opcode_assert_eq = 1;
123+
124+
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)+1*(2**5)+0*(2**6)+flag_opcode_assert_eq*(2**14);
125+
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)+0*(2**5)+1*(2**6)+flag_opcode_assert_eq*(2**14);
126+
let qm31_opcode_extension_num = 3;
127+
let qm31_add_instruction_num = offset0 + offset1*(2**16) + offset2*(2**32) + flag_num_qm31_add*(2**48) + qm31_opcode_extension_num*(2**63);
128+
let qm31_mul_instruction_num = offset0 + offset1*(2**16) + offset2*(2**32) + flag_num_qm31_mul*(2**48) + qm31_opcode_extension_num*(2**63);
129+
static_assert qm31_mul_instruction_num==32305305291694374908;
130+
static_assert qm31_add_instruction_num==32296298092439633916;
131+
132+
if (is_mul == TRUE) {
133+
dw 32305305291694374908;
134+
return [ap];
135+
}
136+
dw 32296298092439633916;
137+
return [ap];
138+
}
139+
140+
func run_qm31_operation_get_op0(
141+
is_mul: felt,
142+
dst: felt,
143+
op1: felt,
144+
) -> felt {
145+
let offset0 = (2**15)-4;
146+
let offset1 = 2**15;
147+
let offset2 = (2**15)-3;
148+
149+
let flag_dst_base_fp = 1;
150+
let flag_op0_base_fp = 0;
151+
let flag_op1_imm = 0;
152+
let flag_op1_base_fp = 1;
153+
let flag_op1_base_ap = 0;
154+
let flag_res_add = 1-is_mul;
155+
let flag_res_mul = is_mul;
156+
let flag_PC_update_jump = 0;
157+
let flag_PC_update_jump_rel = 0;
158+
let flag_PC_update_jnz = 0;
159+
let flag_ap_update_add = 0;
160+
let flag_ap_update_add_1 = 0;
161+
let flag_opcode_call = 0;
162+
let flag_opcode_ret = 0;
163+
let flag_opcode_assert_eq = 1;
164+
165+
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)+1*(2**5)+0*(2**6)+flag_opcode_assert_eq*(2**14);
166+
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)+0*(2**5)+1*(2**6)+flag_opcode_assert_eq*(2**14);
167+
let qm31_opcode_extension_num = 3;
168+
let qm31_add_instruction_num = offset0 + offset1*(2**16) + offset2*(2**32) + flag_num_qm31_add*(2**48) + qm31_opcode_extension_num*(2**63);
169+
let qm31_mul_instruction_num = offset0 + offset1*(2**16) + offset2*(2**32) + flag_num_qm31_mul*(2**48) + qm31_opcode_extension_num*(2**63);
170+
static_assert qm31_mul_instruction_num==32302490529042563068;
171+
static_assert qm31_add_instruction_num==32293483329787822076;
172+
173+
if (is_mul == TRUE) {
174+
dw 32302490529042563068;
175+
return [ap];
176+
}
177+
dw 32293483329787822076;
178+
return [ap];
179+
}
180+
181+
func run_qm31_operation_imm_op1_get_dst(
182+
is_mul: felt,
183+
op0: felt,
184+
) -> felt {
185+
let offset0 = 2**15;
186+
let offset1 = (2**15)-3;
187+
let offset2 = (2**15)+1;
188+
189+
let flag_dst_base_fp = 0;
190+
let flag_op0_base_fp = 1;
191+
let flag_op1_imm = 1;
192+
let flag_op1_base_fp = 0;
193+
let flag_op1_base_ap = 0;
194+
let flag_res_add = 1-is_mul;
195+
let flag_res_mul = is_mul;
196+
let flag_PC_update_jump = 0;
197+
let flag_PC_update_jump_rel = 0;
198+
let flag_PC_update_jnz = 0;
199+
let flag_ap_update_add = 0;
200+
let flag_ap_update_add_1 = 0;
201+
let flag_opcode_call = 0;
202+
let flag_opcode_ret = 0;
203+
let flag_opcode_assert_eq = 1;
204+
205+
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)+1*(2**5)+0*(2**6)+flag_opcode_assert_eq*(2**14);
206+
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)+0*(2**5)+1*(2**6)+flag_opcode_assert_eq*(2**14);
207+
let qm31_opcode_extension_num = 3;
208+
let qm31_add_instruction_num = offset0 + offset1*(2**16) + offset2*(2**32) + flag_num_qm31_add*(2**48) + qm31_opcode_extension_num*(2**63);
209+
let qm31_mul_instruction_num = offset0 + offset1*(2**16) + offset2*(2**32) + flag_num_qm31_mul*(2**48) + qm31_opcode_extension_num*(2**63);
210+
static_assert qm31_mul_instruction_num==32301646121292103680;
211+
static_assert qm31_add_instruction_num==32292638922037362688;
212+
213+
if (is_mul == TRUE) {
214+
dw 32301646121292103680;
215+
dw 679720817185961464190715473544778505313945;
216+
return [ap];
217+
}
218+
dw 32292638922037362688;
219+
dw 679720817185961464190715473544778505313945;
220+
return [ap];
221+
}
222+
223+
func run_qm31_operation_imm_op1_get_op0(
224+
is_mul: felt,
225+
dst: felt,
226+
) -> felt {
227+
let offset0 = (2**15)-3;
228+
let offset1 = 2**15;
229+
let offset2 = (2**15)+1;
230+
231+
let flag_dst_base_fp = 1;
232+
let flag_op0_base_fp = 0;
233+
let flag_op1_imm = 1;
234+
let flag_op1_base_fp = 0;
235+
let flag_op1_base_ap = 0;
236+
let flag_res_add = 1-is_mul;
237+
let flag_res_mul = is_mul;
238+
let flag_PC_update_jump = 0;
239+
let flag_PC_update_jump_rel = 0;
240+
let flag_PC_update_jnz = 0;
241+
let flag_ap_update_add = 0;
242+
let flag_ap_update_add_1 = 0;
243+
let flag_opcode_call = 0;
244+
let flag_opcode_ret = 0;
245+
let flag_opcode_assert_eq = 1;
246+
247+
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)+1*(2**5)+0*(2**6)+flag_opcode_assert_eq*(2**14);
248+
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)+0*(2**5)+1*(2**6)+flag_opcode_assert_eq*(2**14);
249+
let qm31_opcode_extension_num = 3;
250+
let qm31_add_instruction_num = offset0 + offset1*(2**16) + offset2*(2**32) + flag_num_qm31_add*(2**48) + qm31_opcode_extension_num*(2**63);
251+
let qm31_mul_instruction_num = offset0 + offset1*(2**16) + offset2*(2**32) + flag_num_qm31_mul*(2**48) + qm31_opcode_extension_num*(2**63);
252+
static_assert qm31_mul_instruction_num==32301364646315589629;
253+
static_assert qm31_add_instruction_num==32292357447060848637;
254+
255+
if (is_mul == TRUE) {
256+
dw 32301364646315589629;
257+
dw 679720817185961464190715473544778505313945;
258+
return [ap];
259+
}
260+
dw 32292357447060848637;
261+
dw 679720817185961464190715473544778505313945;
262+
return [ap];
263+
}

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+
QM31Operation,
8384
}
8485

8586
impl Instruction {

vm/src/vm/decoding/decoder.rs

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

109109
let opcode_extension = match opcode_extension_num {
110110
0 => OpcodeExtension::Stone,
111+
3 => {
112+
if (res != Res::Add && res != Res::Mul)
113+
|| op1_addr == Op1Addr::Op0
114+
|| pc_update != PcUpdate::Regular
115+
|| opcode != Opcode::AssertEq
116+
{
117+
return Err(VirtualMachineError::InvalidQM31AddMulFlags(flags & 0x7FFF));
118+
}
119+
OpcodeExtension::QM31Operation
120+
}
111121
_ => {
112122
return Err(VirtualMachineError::InvalidOpcodeExtension(
113123
opcode_extension_num,
@@ -429,4 +439,19 @@ mod decoder_test {
429439
let error = decode_instruction(0x9104800180018000);
430440
assert_matches!(error, Err(VirtualMachineError::InvalidOpcodeExtension(1)));
431441
}
442+
443+
#[test]
444+
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
445+
fn decode_qm31_operation_invalid_flags() {
446+
// opcode_extension| opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg
447+
// 79 ... 17 16 15| 14 13 12| 11 10| 9 8 7| 6 5|4 3 2| 1| 0
448+
// QM31Operation| CALL| REGULAR| JumpRel| Op1| FP| AP| AP
449+
// 1 1 0 0 1 0 0 0 1 0 0 0 0 1 0 0 0
450+
// 1 1001 0001 0000 1000 = 0x19108; off0 = 1, off1 = 1
451+
let error = decode_instruction(0x19108800180018001);
452+
assert_matches!(
453+
error,
454+
Err(VirtualMachineError::InvalidQM31AddMulFlags(0x1108))
455+
);
456+
}
432457
}

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 QM31Operation: 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)