Skip to content

Commit

Permalink
add qm31 operations to VM
Browse files Browse the repository at this point in the history
  • Loading branch information
ohad-nir-starkware committed Feb 12, 2025
1 parent ef1aa46 commit bd6140f
Show file tree
Hide file tree
Showing 7 changed files with 446 additions and 27 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

#### Upcoming Changes

* feat: implement an opcode that computes QM31 arithmetics (add, sub, mul, div) in the VM [#1938](https://github.com/lambdaclass/cairo-vm/pull/1938)

* feat: add functions that compute packed reduced qm31 arithmetics to `math_utils` [#1944](https://github.com/lambdaclass/cairo-vm/pull/1944)

* feat: set `encoded_instruction` to be u128 for opcode_extensions to come [#1940](https://github.com/lambdaclass/cairo-vm/pull/1940)
Expand Down
263 changes: 263 additions & 0 deletions cairo_programs/stwo_exclusive_programs/qm31_opcodes_test.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,263 @@
from starkware.cairo.common.bool import FALSE, TRUE

func main{}() {
let qm31_op0_coordinates_a = 1414213562;
let qm31_op0_coordinates_b = 1732050807;
let qm31_op0_coordinates_c = 1618033988;
let qm31_op0_coordinates_d = 1234567890;
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);

let qm31_op1_coordinates_a = 1259921049;
let qm31_op1_coordinates_b = 1442249570;
let qm31_op1_coordinates_c = 1847759065;
let qm31_op1_coordinates_d = 2094551481;
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);
static_assert qm31_op1==679720817185961464190715473544778505313945;


let qm31_add_dst_coordinates_a = 526650964;
let qm31_add_dst_coordinates_b = 1026816730;
let qm31_add_dst_coordinates_c = 1318309406;
let qm31_add_dst_coordinates_d = 1181635724;
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);

let qm31_mul_dst_coordinates_a = 947980980;
let qm31_mul_dst_coordinates_b = 1510986506;
let qm31_mul_dst_coordinates_c = 623360030;
let qm31_mul_dst_coordinates_d = 1260310989;
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);

let runner_output_mul_dst = run_qm31_operation_get_dst(is_mul=TRUE, op0=qm31_op0, op1=qm31_op1);
assert runner_output_mul_dst = qm31_mul_dst;
let runner_output_add_dst = run_qm31_operation_get_dst(is_mul=FALSE, op0=qm31_op0, op1=qm31_op1);
assert runner_output_add_dst = qm31_add_dst;

let runner_output_mul_op1 = run_qm31_operation_get_op1(is_mul=TRUE, dst=qm31_mul_dst, op0=qm31_op0);
assert runner_output_mul_op1 = qm31_op1;
let runner_output_add_op1 = run_qm31_operation_get_op1(is_mul=FALSE, dst=qm31_add_dst, op0=qm31_op0);
assert runner_output_add_op1 = qm31_op1;

let runner_output_mul_op0 = run_qm31_operation_get_op0(is_mul=TRUE, dst=qm31_mul_dst, op1=qm31_op1);
assert runner_output_mul_op0 = qm31_op0;
let runner_output_add_op0 = run_qm31_operation_get_op0(is_mul=FALSE, dst=qm31_add_dst, op1=qm31_op1);
assert runner_output_add_op0 = qm31_op0;

let runner_output_mul_dst = run_qm31_operation_imm_op1_get_dst(is_mul=TRUE, op0=qm31_op0);
assert runner_output_mul_dst = qm31_mul_dst;
let runner_output_add_dst = run_qm31_operation_imm_op1_get_dst(is_mul=FALSE, op0=qm31_op0);
assert runner_output_add_dst = qm31_add_dst;

let runner_output_mul_op0 = run_qm31_operation_imm_op1_get_op0(is_mul=TRUE, dst=qm31_mul_dst);
assert runner_output_mul_op0 = qm31_op0;
let runner_output_add_op0 = run_qm31_operation_imm_op1_get_op0(is_mul=FALSE, dst=qm31_add_dst);
assert runner_output_add_op0 = qm31_op0;

return ();
}

func run_qm31_operation_get_dst(
is_mul: felt,
op0: felt,
op1: felt,
) -> felt {
let offset0 = 2**15;
let offset1 = (2**15)-4;
let offset2 = (2**15)-3;
let flag_dst_base_fp = 0;
let flag_op0_base_fp = 1;
let flag_op1_imm = 0;
let flag_op1_base_fp = 1;
let flag_op1_base_ap = 0;
let flag_res_add = 1-is_mul;
let flag_res_mul = is_mul;
let flag_PC_update_jump = 0;
let flag_PC_update_jump_rel = 0;
let flag_PC_update_jnz = 0;
let flag_ap_update_add = 0;
let flag_ap_update_add_1 = 0;
let flag_opcode_call = 0;
let flag_opcode_ret = 0;
let flag_opcode_assert_eq = 1;
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);
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);
let qm31_opcode_extension_num = 3;
let qm31_add_instruction_num = offset0 + offset1*(2**16) + offset2*(2**32) + flag_num_qm31_add*(2**48) + qm31_opcode_extension_num*(2**63);
let qm31_mul_instruction_num = offset0 + offset1*(2**16) + offset2*(2**32) + flag_num_qm31_mul*(2**48) + qm31_opcode_extension_num*(2**63);
static_assert qm31_mul_instruction_num==32302772004019011584;
static_assert qm31_add_instruction_num==32293764804764270592;
if (is_mul == TRUE) {
dw 32302772004019011584;
return [ap];
}
dw 32293764804764270592;
return [ap];
}

func run_qm31_operation_get_op1(
is_mul: felt,
dst: felt,
op0: felt,
) -> felt {
let offset0 = (2**15)-4;
let offset1 = (2**15)-3;
let offset2 = 2**15;
let flag_dst_base_fp = 1;
let flag_op0_base_fp = 1;
let flag_op1_imm = 0;
let flag_op1_base_fp = 0;
let flag_op1_base_ap = 1;
let flag_res_add = 1-is_mul;
let flag_res_mul = is_mul;
let flag_PC_update_jump = 0;
let flag_PC_update_jump_rel = 0;
let flag_PC_update_jnz = 0;
let flag_ap_update_add = 0;
let flag_ap_update_add_1 = 0;
let flag_opcode_call = 0;
let flag_opcode_ret = 0;
let flag_opcode_assert_eq = 1;
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);
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);
let qm31_opcode_extension_num = 3;
let qm31_add_instruction_num = offset0 + offset1*(2**16) + offset2*(2**32) + flag_num_qm31_add*(2**48) + qm31_opcode_extension_num*(2**63);
let qm31_mul_instruction_num = offset0 + offset1*(2**16) + offset2*(2**32) + flag_num_qm31_mul*(2**48) + qm31_opcode_extension_num*(2**63);
static_assert qm31_mul_instruction_num==32305305291694374908;
static_assert qm31_add_instruction_num==32296298092439633916;
if (is_mul == TRUE) {
dw 32305305291694374908;
return [ap];
}
dw 32296298092439633916;
return [ap];
}

func run_qm31_operation_get_op0(
is_mul: felt,
dst: felt,
op1: felt,
) -> felt {
let offset0 = (2**15)-4;
let offset1 = 2**15;
let offset2 = (2**15)-3;
let flag_dst_base_fp = 1;
let flag_op0_base_fp = 0;
let flag_op1_imm = 0;
let flag_op1_base_fp = 1;
let flag_op1_base_ap = 0;
let flag_res_add = 1-is_mul;
let flag_res_mul = is_mul;
let flag_PC_update_jump = 0;
let flag_PC_update_jump_rel = 0;
let flag_PC_update_jnz = 0;
let flag_ap_update_add = 0;
let flag_ap_update_add_1 = 0;
let flag_opcode_call = 0;
let flag_opcode_ret = 0;
let flag_opcode_assert_eq = 1;
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);
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);
let qm31_opcode_extension_num = 3;
let qm31_add_instruction_num = offset0 + offset1*(2**16) + offset2*(2**32) + flag_num_qm31_add*(2**48) + qm31_opcode_extension_num*(2**63);
let qm31_mul_instruction_num = offset0 + offset1*(2**16) + offset2*(2**32) + flag_num_qm31_mul*(2**48) + qm31_opcode_extension_num*(2**63);
static_assert qm31_mul_instruction_num==32302490529042563068;
static_assert qm31_add_instruction_num==32293483329787822076;
if (is_mul == TRUE) {
dw 32302490529042563068;
return [ap];
}
dw 32293483329787822076;
return [ap];
}

func run_qm31_operation_imm_op1_get_dst(
is_mul: felt,
op0: felt,
) -> felt {
let offset0 = 2**15;
let offset1 = (2**15)-3;
let offset2 = (2**15)+1;
let flag_dst_base_fp = 0;
let flag_op0_base_fp = 1;
let flag_op1_imm = 1;
let flag_op1_base_fp = 0;
let flag_op1_base_ap = 0;
let flag_res_add = 1-is_mul;
let flag_res_mul = is_mul;
let flag_PC_update_jump = 0;
let flag_PC_update_jump_rel = 0;
let flag_PC_update_jnz = 0;
let flag_ap_update_add = 0;
let flag_ap_update_add_1 = 0;
let flag_opcode_call = 0;
let flag_opcode_ret = 0;
let flag_opcode_assert_eq = 1;
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);
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);
let qm31_opcode_extension_num = 3;
let qm31_add_instruction_num = offset0 + offset1*(2**16) + offset2*(2**32) + flag_num_qm31_add*(2**48) + qm31_opcode_extension_num*(2**63);
let qm31_mul_instruction_num = offset0 + offset1*(2**16) + offset2*(2**32) + flag_num_qm31_mul*(2**48) + qm31_opcode_extension_num*(2**63);
static_assert qm31_mul_instruction_num==32301646121292103680;
static_assert qm31_add_instruction_num==32292638922037362688;
if (is_mul == TRUE) {
dw 32301646121292103680;
dw 679720817185961464190715473544778505313945;
return [ap];
}
dw 32292638922037362688;
dw 679720817185961464190715473544778505313945;
return [ap];
}

func run_qm31_operation_imm_op1_get_op0(
is_mul: felt,
dst: felt,
) -> felt {
let offset0 = (2**15)-3;
let offset1 = 2**15;
let offset2 = (2**15)+1;
let flag_dst_base_fp = 1;
let flag_op0_base_fp = 0;
let flag_op1_imm = 1;
let flag_op1_base_fp = 0;
let flag_op1_base_ap = 0;
let flag_res_add = 1-is_mul;
let flag_res_mul = is_mul;
let flag_PC_update_jump = 0;
let flag_PC_update_jump_rel = 0;
let flag_PC_update_jnz = 0;
let flag_ap_update_add = 0;
let flag_ap_update_add_1 = 0;
let flag_opcode_call = 0;
let flag_opcode_ret = 0;
let flag_opcode_assert_eq = 1;
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);
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);
let qm31_opcode_extension_num = 3;
let qm31_add_instruction_num = offset0 + offset1*(2**16) + offset2*(2**32) + flag_num_qm31_add*(2**48) + qm31_opcode_extension_num*(2**63);
let qm31_mul_instruction_num = offset0 + offset1*(2**16) + offset2*(2**32) + flag_num_qm31_mul*(2**48) + qm31_opcode_extension_num*(2**63);
static_assert qm31_mul_instruction_num==32301364646315589629;
static_assert qm31_add_instruction_num==32292357447060848637;
if (is_mul == TRUE) {
dw 32301364646315589629;
dw 679720817185961464190715473544778505313945;
return [ap];
}
dw 32292357447060848637;
dw 679720817185961464190715473544778505313945;
return [ap];
}
8 changes: 8 additions & 0 deletions vm/src/tests/cairo_run_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -568,6 +568,14 @@ fn blake2s_integration_tests() {
run_program_simple(program_data.as_slice());
}

#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn qm31_opcodes_test() {
let program_data =
include_bytes!("../../../cairo_programs/stwo_exclusive_programs/qm31_opcodes_test.json");
run_program_simple(program_data.as_slice());
}

#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn relocate_segments() {
Expand Down
1 change: 1 addition & 0 deletions vm/src/types/instruction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ pub enum Opcode {
#[derive(Clone, Debug, Copy, PartialEq, Eq)]
pub enum OpcodeExtension {
Stone,
QM31Operation,
}

impl Instruction {
Expand Down
25 changes: 25 additions & 0 deletions vm/src/vm/decoding/decoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,16 @@ pub fn decode_instruction(encoded_instr: u128) -> Result<Instruction, VirtualMac

let opcode_extension = match opcode_extension_num {
0 => OpcodeExtension::Stone,
3 => {
if (res != Res::Add && res != Res::Mul)
|| op1_addr == Op1Addr::Op0
|| pc_update != PcUpdate::Regular
|| opcode != Opcode::AssertEq
{
return Err(VirtualMachineError::InvalidQM31AddMulFlags(flags & 0x7FFF));
}
OpcodeExtension::QM31Operation
}
_ => {
return Err(VirtualMachineError::InvalidOpcodeExtension(
opcode_extension_num,
Expand Down Expand Up @@ -429,4 +439,19 @@ mod decoder_test {
let error = decode_instruction(0x9104800180018000);
assert_matches!(error, Err(VirtualMachineError::InvalidOpcodeExtension(1)));
}

#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn decode_qm31_operation_invalid_flags() {
// opcode_extension| opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg
// 79 ... 17 16 15| 14 13 12| 11 10| 9 8 7| 6 5|4 3 2| 1| 0
// QM31Operation| CALL| REGULAR| JumpRel| Op1| FP| AP| AP
// 1 1 0 0 1 0 0 0 1 0 0 0 0 1 0 0 0
// 1 1001 0001 0000 1000 = 0x19108; off0 = 1, off1 = 1
let error = decode_instruction(0x19108800180018001);
assert_matches!(
error,
Err(VirtualMachineError::InvalidQM31AddMulFlags(0x1108))
);
}
}
6 changes: 6 additions & 0 deletions vm/src/vm/errors/vm_errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,10 @@ pub enum VirtualMachineError {
"Failed to compute Res.MUL: Could not complete computation of non pure values {} * {}", (*.0).0, (*.0).1
)]
ComputeResRelocatableMul(Box<(MaybeRelocatable, MaybeRelocatable)>),
#[error(
"Failed to compute Res.ADD for QM31Operation: Could not complete computation of non pure values {} * {}", (*.0).0, (*.0).1
)]
ComputeResRelocatableQM31Add(Box<(MaybeRelocatable, MaybeRelocatable)>),
#[error("Couldn't compute operand {}. Unknown value for memory cell {}", (*.0).0, (*.0).1)]
FailedToComputeOperands(Box<(String, Relocatable)>),
#[error("An ASSERT_EQ instruction failed: {} != {}.", (*.0).0, (*.0).1)]
Expand Down Expand Up @@ -138,6 +142,8 @@ pub enum VirtualMachineError {
RelocationNotFound(usize),
#[error("{} batch size is not {}", (*.0).0, (*.0).1)]
ModBuiltinBatchSize(Box<(BuiltinName, usize)>),
#[error("QM31 add mul opcode invalid flags {0}")]
InvalidQM31AddMulFlags(u128),
}

#[cfg(test)]
Expand Down
Loading

0 comments on commit bd6140f

Please sign in to comment.