Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

introduce opcode_extension to the structure of Instruction. #1933

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading