Skip to content

Commit

Permalink
introduce opcode_extension to the structure of Instruction.
Browse files Browse the repository at this point in the history
  • Loading branch information
ohad-nir-starkware committed Feb 5, 2025
1 parent df12864 commit e15c6e6
Show file tree
Hide file tree
Showing 6 changed files with 139 additions and 63 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: add the field `opcode_extension` to the structure of `Instruction` [#1933](https://github.com/lambdaclass/cairo-vm/pull/1933)

* fix(BREAKING): Fix no trace padding flow in proof mode [#1909](https://github.com/lambdaclass/cairo-vm/pull/1909)

* refactor: Limit ret opcode decodeing to Cairo0's standards. [#1925](https://github.com/lambdaclass/cairo-vm/pull/1925)
Expand Down
6 changes: 6 additions & 0 deletions vm/src/types/instruction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ pub struct Instruction {
pub ap_update: ApUpdate,
pub fp_update: FpUpdate,
pub opcode: Opcode,
pub opcode_extension: OpcodeExtension,
}

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
Expand Down Expand Up @@ -76,6 +77,11 @@ pub enum Opcode {
Ret,
}

#[derive(Clone, Debug, Copy, PartialEq, Eq)]
pub enum OpcodeExtension {
Stone,
}

impl Instruction {
pub fn size(&self) -> usize {
match self.op1_addr {
Expand Down
13 changes: 12 additions & 1 deletion vm/src/vm/context/run_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ mod tests {
use super::*;
use crate::relocatable;
use crate::stdlib::string::ToString;
use crate::types::instruction::{ApUpdate, FpUpdate, Opcode, PcUpdate, Res};
use crate::types::instruction::{ApUpdate, FpUpdate, Opcode, OpcodeExtension, PcUpdate, Res};
use crate::utils::test_utils::mayberelocatable;
use crate::vm::errors::memory_errors::MemoryError;
use crate::Felt252;
Expand All @@ -130,6 +130,7 @@ mod tests {
ap_update: ApUpdate::Regular,
fp_update: FpUpdate::Regular,
opcode: Opcode::NOp,
opcode_extension: OpcodeExtension::Stone,
};

let run_context = RunContext {
Expand Down Expand Up @@ -158,6 +159,7 @@ mod tests {
ap_update: ApUpdate::Regular,
fp_update: FpUpdate::Regular,
opcode: Opcode::NOp,
opcode_extension: OpcodeExtension::Stone,
};

let run_context = RunContext {
Expand Down Expand Up @@ -187,6 +189,7 @@ mod tests {
ap_update: ApUpdate::Regular,
fp_update: FpUpdate::Regular,
opcode: Opcode::NOp,
opcode_extension: OpcodeExtension::Stone,
};

let run_context = RunContext {
Expand Down Expand Up @@ -215,6 +218,7 @@ mod tests {
ap_update: ApUpdate::Regular,
fp_update: FpUpdate::Regular,
opcode: Opcode::NOp,
opcode_extension: OpcodeExtension::Stone,
};

let run_context = RunContext {
Expand Down Expand Up @@ -243,6 +247,7 @@ mod tests {
ap_update: ApUpdate::Regular,
fp_update: FpUpdate::Regular,
opcode: Opcode::NOp,
opcode_extension: OpcodeExtension::Stone,
};

let run_context = RunContext {
Expand Down Expand Up @@ -271,6 +276,7 @@ mod tests {
ap_update: ApUpdate::Regular,
fp_update: FpUpdate::Regular,
opcode: Opcode::NOp,
opcode_extension: OpcodeExtension::Stone,
};

let run_context = RunContext {
Expand Down Expand Up @@ -299,6 +305,7 @@ mod tests {
ap_update: ApUpdate::Regular,
fp_update: FpUpdate::Regular,
opcode: Opcode::NOp,
opcode_extension: OpcodeExtension::Stone,
};

let run_context = RunContext {
Expand Down Expand Up @@ -327,6 +334,7 @@ mod tests {
ap_update: ApUpdate::Regular,
fp_update: FpUpdate::Regular,
opcode: Opcode::NOp,
opcode_extension: OpcodeExtension::Stone,
};

let run_context = RunContext {
Expand Down Expand Up @@ -358,6 +366,7 @@ mod tests {
ap_update: ApUpdate::Regular,
fp_update: FpUpdate::Regular,
opcode: Opcode::NOp,
opcode_extension: OpcodeExtension::Stone,
};

let run_context = RunContext {
Expand Down Expand Up @@ -388,6 +397,7 @@ mod tests {
ap_update: ApUpdate::Regular,
fp_update: FpUpdate::Regular,
opcode: Opcode::NOp,
opcode_extension: OpcodeExtension::Stone,
};

let run_context = RunContext {
Expand Down Expand Up @@ -420,6 +430,7 @@ mod tests {
ap_update: ApUpdate::Regular,
fp_update: FpUpdate::Regular,
opcode: Opcode::NOp,
opcode_extension: OpcodeExtension::Stone,
};

let run_context = RunContext {
Expand Down
124 changes: 62 additions & 62 deletions vm/src/vm/decoding/decoder.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
use crate::{
types::instruction::{
ApUpdate, FpUpdate, Instruction, Op1Addr, Opcode, PcUpdate, Register, Res,
ApUpdate, FpUpdate, Instruction, Op1Addr, Opcode, OpcodeExtension, PcUpdate, Register, Res,
},
vm::errors::vm_errors::VirtualMachineError,
};

// 0| opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg
// 15|14 13 12| 11 10| 9 8 7| 6 5|4 3 2| 1| 0
// opcode_extension| opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg
// ... 15|14 13 12| 11 10| 9 8 7| 6 5|4 3 2| 1| 0

/// Decodes an instruction. The encoding is little endian, so flags go from bit 63 to 48.
/// Decodes an instruction. The encoding is little endian, so flags go from bit 64 to 48.
pub fn decode_instruction(encoded_instr: u64) -> Result<Instruction, VirtualMachineError> {
const HIGH_BIT: u64 = 1u64 << 63;
const DST_REG_MASK: u64 = 0x0001;
const DST_REG_OFF: u64 = 0;
const OP0_REG_MASK: u64 = 0x0002;
Expand All @@ -25,25 +24,23 @@ pub fn decode_instruction(encoded_instr: u64) -> Result<Instruction, VirtualMach
const AP_UPDATE_OFF: u64 = 10;
const OPCODE_MASK: u64 = 0x7000;
const OPCODE_OFF: u64 = 12;
const OPCODE_EXTENSION_OFF: u64 = 63;

// Flags start on the 48th bit.
const FLAGS_OFFSET: u64 = 48;
const FLAGS_MASK: u64 = 0x7FFF;
const OFF0_OFF: u64 = 0;
const OFF1_OFF: u64 = 16;
const OFF2_OFF: u64 = 32;
const OFFX_MASK: u64 = 0xFFFF;

if encoded_instr & HIGH_BIT != 0 {
return Err(VirtualMachineError::InstructionNonZeroHighBit);
}

// Grab offsets and convert them from little endian format.
let off0 = decode_offset(encoded_instr >> OFF0_OFF & OFFX_MASK);
let off1 = decode_offset(encoded_instr >> OFF1_OFF & OFFX_MASK);
let off2 = decode_offset(encoded_instr >> OFF2_OFF & OFFX_MASK);

// Grab flags
let flags = encoded_instr >> FLAGS_OFFSET;
let flags = (encoded_instr >> FLAGS_OFFSET) & FLAGS_MASK;
// Grab individual flags
let dst_reg_num = (flags & DST_REG_MASK) >> DST_REG_OFF;
let op0_reg_num = (flags & OP0_REG_MASK) >> OP0_REG_OFF;
Expand All @@ -53,6 +50,9 @@ pub fn decode_instruction(encoded_instr: u64) -> Result<Instruction, VirtualMach
let ap_update_num = (flags & AP_UPDATE_MASK) >> AP_UPDATE_OFF;
let opcode_num = (flags & OPCODE_MASK) >> OPCODE_OFF;

// Grab opcode_extension
let opcode_extension_num = encoded_instr >> OPCODE_EXTENSION_OFF;

// Match each flag to its corresponding enum value
let dst_register = if dst_reg_num == 1 {
Register::FP
Expand Down Expand Up @@ -98,6 +98,15 @@ pub fn decode_instruction(encoded_instr: u64) -> Result<Instruction, VirtualMach
_ => return Err(VirtualMachineError::InvalidOpcode(opcode_num)),
};

let opcode_extension = match opcode_extension_num {
0 => OpcodeExtension::Stone,
_ => {
return Err(VirtualMachineError::InvalidOpcodeExtension(
opcode_extension_num,
))
}
};

let ap_update = match (ap_update_num, opcode == Opcode::Call) {
(0, true) => ApUpdate::Add2,
(0, false) => ApUpdate::Regular,
Expand Down Expand Up @@ -145,6 +154,7 @@ pub fn decode_instruction(encoded_instr: u64) -> Result<Instruction, VirtualMach
ap_update,
fp_update,
opcode,
opcode_extension,
})
}

Expand All @@ -165,16 +175,6 @@ mod decoder_test {
#[cfg(target_arch = "wasm32")]
use wasm_bindgen_test::*;

#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn non_zero_high_bit() {
let error = decode_instruction(0x94A7800080008000);
assert_eq!(
error.unwrap_err().to_string(),
"Instruction MSB should be 0",
)
}

#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn invalid_op1_reg() {
Expand Down Expand Up @@ -221,10 +221,10 @@ mod decoder_test {
#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn decode_flags_nop_add_jmp_add_imm_fp_fp() {
// 0| opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg
// 15|14 13 12| 11 10| 9 8 7| 6 5|4 3 2| 1| 0
// | NOp| ADD| JUMP| ADD| IMM| FP| FP
// 0 0 0 0 0 1 0 0 1 0 1 0 0 1 1 1
// opcode_extension| opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg
// ... 15| 14 13 12| 11 10| 9 8 7| 6 5|4 3 2| 1| 0
// Stone| NOp| ADD| JUMP| ADD| IMM| FP| FP
// 0 0 0 0 0 1 0 0 1 0 1 0 0 1 1 1
// 0000 0100 1010 0111 = 0x04A7; offx = 0
let inst = decode_instruction(0x04A7800080008000).unwrap();
assert_matches!(inst.dst_register, Register::FP);
Expand All @@ -240,10 +240,10 @@ mod decoder_test {
#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn decode_flags_nop_add1_jmp_rel_mul_fp_ap_ap() {
// 0| opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg
// 15|14 13 12| 11 10| 9 8 7| 6 5|4 3 2| 1| 0
// | NOp| ADD1| JUMP_REL| MUL| FP| AP| AP
// 0 0 0 0 1 0 0 1 0 1 0 0 1 0 0 0
// opcode_extension| opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg
// ... 15| 14 13 12| 11 10| 9 8 7| 6 5|4 3 2| 1| 0
// Stone| NOp| ADD1| JUMP_REL| MUL| FP| AP| AP
// 0 0 0 0 1 0 0 1 0 1 0 0 1 0 0 0
// 0000 1001 0100 1000 = 0x0948; offx = 0
let inst = decode_instruction(0x0948800080008000).unwrap();
assert_matches!(inst.dst_register, Register::AP);
Expand All @@ -259,10 +259,10 @@ mod decoder_test {
#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn decode_flags_assrt_add_regular_mul_ap_ap_ap() {
// 0| opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg
// 15|14 13 12| 11 10| 9 8 7| 6 5|4 3 2| 1| 0
// |ASSRT_EQ| ADD| REGULAR| MUL| AP| AP| AP
// 0 1 0 0 1 0 0 0 0 1 0 1 0 0 0 0
// opcode_extension| opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg
// ... 15| 14 13 12| 11 10| 9 8 7| 6 5|4 3 2| 1| 0
// Stone| ASSRT_EQ| ADD| REGULAR| MUL| AP| AP| AP
// 0 1 0 0 1 0 0 0 0 1 0 1 0 0 0 0
// 0100 1000 0101 0000 = 0x4850; offx = 0
let inst = decode_instruction(0x4850800080008000).unwrap();
assert_matches!(inst.dst_register, Register::AP);
Expand All @@ -278,10 +278,10 @@ mod decoder_test {
#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn decode_flags_assrt_add2_jnz_uncon_op0_ap_ap() {
// 0| opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg
// 15|14 13 12| 11 10| 9 8 7| 6 5|4 3 2| 1| 0
// |ASSRT_EQ| ADD2| JNZ|UNCONSTRD| OP0| AP| AP
// 0 1 0 0 0 0 1 0 0 0 0 0 0 0 0 0
// opcode_extension| opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg
// ... 15| 14 13 12| 11 10| 9 8 7| 6 5|4 3 2| 1| 0
// Stone| ASSRT_EQ| ADD2| JNZ|UNCONSTRD| OP0| AP| AP
// 0 1 0 0 0 0 1 0 0 0 0 0 0 0 0 0
// 0100 0010 0000 0000 = 0x4200; offx = 0
let inst = decode_instruction(0x4200800080008000).unwrap();
assert_matches!(inst.dst_register, Register::AP);
Expand All @@ -297,10 +297,10 @@ mod decoder_test {
#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn decode_flags_nop_regu_regu_op1_op0_ap_ap() {
// 0| opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg
// 15|14 13 12| 11 10| 9 8 7| 6 5|4 3 2| 1| 0
// | NOP| REGULAR| REGULAR| OP1| OP0| AP| AP
// 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
// opcode_extension| opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg
// ... 15| 14 13 12| 11 10| 9 8 7| 6 5|4 3 2| 1| 0
// Stone| NOP| REGULAR| REGULAR| OP1| OP0| AP| AP
// 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
// 0000 0000 0000 0000 = 0x0000; offx = 0
let inst = decode_instruction(0x0000800080008000).unwrap();
assert_matches!(inst.dst_register, Register::AP);
Expand All @@ -316,10 +316,10 @@ mod decoder_test {
#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn decode_offset_negative() {
// 0| opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg
// 15|14 13 12| 11 10| 9 8 7| 6 5|4 3 2| 1| 0
// | NOP| REGULAR| REGULAR| OP1| OP0| AP| AP
// 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
// opcode_extension| opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg
// ... 15| 14 13 12| 11 10| 9 8 7| 6 5|4 3 2| 1| 0
// Stone| NOP| REGULAR| REGULAR| OP1| OP0| AP| AP
// 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
// 0000 0000 0000 0000 = 0x0000; offx = 0
let inst = decode_instruction(0x0000800180007FFF).unwrap();
assert_eq!(inst.off0, -1);
Expand All @@ -330,10 +330,10 @@ mod decoder_test {
#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn decode_ret_cairo_standard() {
// 0| opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg
// 15|14 13 12| 11 10| 9 8 7| 6 5|4 3 2| 1| 0
// | RET| REGULAR| JUMP| Op1| FP| FP| FP
// 0 0 1 0 0 0 0 0 1 0 0 0 1 0 1 1
// opcode_extension| opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg
// ... 15| 14 13 12| 11 10| 9 8 7| 6 5|4 3 2| 1| 0
// Stone| RET| REGULAR| JUMP| Op1| FP| FP| FP
// 0 0 1 0 0 0 0 0 1 0 0 0 1 0 1 1
// 0010 0000 1000 1011 = 0x208b; off0 = -2, off1 = -1
let inst = decode_instruction(0x208b7fff7fff7ffe).unwrap();
assert_matches!(inst.opcode, Opcode::Ret);
Expand All @@ -351,11 +351,11 @@ mod decoder_test {
#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn decode_call_cairo_standard() {
// 0| opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg
// 15|14 13 12| 11 10| 9 8 7| 6 5|4 3 2| 1| 0
// | CALL| Add2| JumpRel| Op1| FP| FP| FP
// 0 0 0 1 0 0 0 1 0 0 0 0 0 1 0 0
// 0001 0001 0000 0100 = 0x208b; off0 = 0, off1 = 1
// opcode_extension| opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg
// ... 15| 14 13 12| 11 10| 9 8 7| 6 5|4 3 2| 1| 0
// Stone| CALL| Add2| JumpRel| Op1| FP| FP| FP
// 0 0 0 1 0 0 0 1 0 0 0 0 0 1 0 0
// 0001 0001 0000 0100 = 0x1104; off0 = 0, off1 = 1
let inst = decode_instruction(0x1104800180018000).unwrap();
assert_matches!(inst.opcode, Opcode::Call);
assert_matches!(inst.off0, 0);
Expand All @@ -372,10 +372,10 @@ mod decoder_test {
#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn decode_ret_opcode_error() {
// 0| opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg
// 15|14 13 12| 11 10| 9 8 7| 6 5|4 3 2| 1| 0
// | RET| REGULAR| JUMP| Op1| FP| FP| FP
// 0 0 1 0 0 0 0 0 1 0 0 0 1 0 1 1
// opcode_extension| opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg
// ... 15| 14 13 12| 11 10| 9 8 7| 6 5|4 3 2| 1| 0
// Stone| RET| REGULAR| JUMP| Op1| FP| FP| FP
// 0 0 1 0 0 0 0 0 1 0 0 0 1 0 1 1
// 0010 0000 1000 1011 = 0x208b; off0 = -1, off1 = -1
let error = decode_instruction(0x208b7fff7fff7fff);
assert_matches!(error, Err(VirtualMachineError::InvalidOpcode(2)));
Expand All @@ -384,11 +384,11 @@ mod decoder_test {
#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn decode_call_opcode_error() {
// 0| opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg
// 15|14 13 12| 11 10| 9 8 7| 6 5|4 3 2| 1| 0
// | CALL| Add2| JumpRel| Op1| FP| FP| FP
// 0 0 0 1 0 0 0 1 0 0 0 0 0 1 0 0
// 0001 0001 0000 0100 = 0x208b; off0 = 1, off1 = 1
// opcode_extension| opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg
// ... 15| 14 13 12| 11 10| 9 8 7| 6 5|4 3 2| 1| 0
// Stone| CALL| Add2| JumpRel| Op1| FP| FP| FP
// 0 0 0 1 0 0 0 1 0 0 0 0 0 1 0 0
// 0001 0001 0000 0100 = 0x1104; off0 = 1, off1 = 1
let error = decode_instruction(0x1104800180018001);
assert_matches!(error, Err(VirtualMachineError::InvalidOpcode(1)));
}
Expand Down
2 changes: 2 additions & 0 deletions vm/src/vm/errors/vm_errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ pub enum VirtualMachineError {
InvalidRes(u64),
#[error("Invalid opcode value: {0}")]
InvalidOpcode(u64),
#[error("Invalid opcode extension value: {0}")]
InvalidOpcodeExtension(u64),
#[error("This is not implemented")]
NotImplemented,
#[error("Inconsistent auto-deduction for {}, expected {}, got {:?}", (*.0).0, (*.0).1, (*.0).2)]
Expand Down
Loading

0 comments on commit e15c6e6

Please sign in to comment.