Skip to content

Commit

Permalink
set the encoded instruction to be u128 for opcode_extensions to come
Browse files Browse the repository at this point in the history
  • Loading branch information
ohad-nir-starkware committed Feb 11, 2025
1 parent 1a135db commit 43f4ba4
Show file tree
Hide file tree
Showing 6 changed files with 73 additions and 53 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: set `encoded_instruction` to be u128 for opcode_extensions to come [#1940](https://github.com/lambdaclass/cairo-vm/pull/1940)

* feat: add `get_u32_range` to `impl VirtualMachine` add `get_u32` and `get_u32_range` to `impl Memory` [#1936](https://github.com/lambdaclass/cairo-vm/pull/1936)

* feat: add the field `opcode_extension` to the structure of `Instruction` [#1933](https://github.com/lambdaclass/cairo-vm/pull/1933)
Expand Down
2 changes: 1 addition & 1 deletion cairo-vm-tracer/src/tracer_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ impl TracerData {
let (instruction_encoding, _) =
get_instruction_encoding(entry.pc, &memory, program.prime())?;

let instruction_encoding = instruction_encoding.to_u64();
let instruction_encoding = instruction_encoding.to_u128();

Check warning on line 146 in cairo-vm-tracer/src/tracer_data.rs

View check run for this annotation

Codecov / codecov/patch

cairo-vm-tracer/src/tracer_data.rs#L146

Added line #L146 was not covered by tests
if instruction_encoding.is_none() {
return Err(TraceDataError::FailedToConvertInstructionEncoding);
}
Expand Down
6 changes: 3 additions & 3 deletions vm/src/types/instruction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,11 +93,11 @@ impl Instruction {

// Returns True if the given instruction looks like a call instruction
pub(crate) fn is_call_instruction(encoded_instruction: &Felt252) -> bool {
let encoded_i64_instruction = match encoded_instruction.to_u64() {
let encoded_u128_instruction = match encoded_instruction.to_u128() {
Some(num) => num,
None => return false,
};
let instruction = match decode_instruction(encoded_i64_instruction) {
let instruction = match decode_instruction(encoded_u128_instruction) {
Ok(inst) => inst,
Err(_) => return false,
};
Expand Down Expand Up @@ -140,7 +140,7 @@ mod tests {
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn instruction_size() {
let encoded_instruction = Felt252::from(1226245742482522112_i64);
let instruction = decode_instruction(encoded_instruction.to_u64().unwrap()).unwrap();
let instruction = decode_instruction(encoded_instruction.to_u128().unwrap()).unwrap();
assert_eq!(instruction.size(), 2);
}
}
98 changes: 57 additions & 41 deletions vm/src/vm/decoding/decoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,36 +5,42 @@ use crate::{
vm::errors::vm_errors::VirtualMachineError,
};

// 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
// 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

/// Decodes an instruction. The encoding is little endian, so flags go from bit 63 to 48.
/// The bits 64 and beyond are reserved for the opcode extension.
/// Decodes an instruction. The encoding is little endian, so flags go from bit 127 to 48.
/// The bits 63 and beyond are reserved for the opcode extension.
/// opcode_extension_num=0 means the instruction is a Stone instruction.
/// opcode_extension_num>1 is for new Stwo opcodes.
pub fn decode_instruction(encoded_instr: u64) -> Result<Instruction, VirtualMachineError> {
const DST_REG_MASK: u64 = 0x0001;
const DST_REG_OFF: u64 = 0;
const OP0_REG_MASK: u64 = 0x0002;
const OP0_REG_OFF: u64 = 1;
const OP1_SRC_MASK: u64 = 0x001C;
const OP1_SRC_OFF: u64 = 2;
const RES_LOGIC_MASK: u64 = 0x0060;
const RES_LOGIC_OFF: u64 = 5;
const PC_UPDATE_MASK: u64 = 0x0380;
const PC_UPDATE_OFF: u64 = 7;
const AP_UPDATE_MASK: u64 = 0x0C00;
const AP_UPDATE_OFF: u64 = 10;
const OPCODE_MASK: u64 = 0x7000;
const OPCODE_OFF: u64 = 12;
const OPCODE_EXTENSION_OFF: u64 = 63;
/// opcode_extension_num>0 is for new Stwo opcodes.
pub fn decode_instruction(encoded_instr: u128) -> Result<Instruction, VirtualMachineError> {
// HIGH_BITS_MASK is a mask to extract the high bits that are yet to be used in any opcode extension.
const HIGH_BITS_MASK: u128 = ((1 << 127) - (1 << 64)) << 1;
const DST_REG_MASK: u128 = 0x0001;
const DST_REG_OFF: u128 = 0;
const OP0_REG_MASK: u128 = 0x0002;
const OP0_REG_OFF: u128 = 1;
const OP1_SRC_MASK: u128 = 0x001C;
const OP1_SRC_OFF: u128 = 2;
const RES_LOGIC_MASK: u128 = 0x0060;
const RES_LOGIC_OFF: u128 = 5;
const PC_UPDATE_MASK: u128 = 0x0380;
const PC_UPDATE_OFF: u128 = 7;
const AP_UPDATE_MASK: u128 = 0x0C00;
const AP_UPDATE_OFF: u128 = 10;
const OPCODE_MASK: u128 = 0x7000;
const OPCODE_OFF: u128 = 12;
const OPCODE_EXTENSION_OFF: u128 = 63;

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

if (encoded_instr & HIGH_BITS_MASK) != 0 {
return Err(VirtualMachineError::NonZeroReservedBits);
}

// Grab offsets and convert them from little endian format.
let off0 = decode_offset(encoded_instr >> OFF0_OFF & OFFX_MASK);
Expand Down Expand Up @@ -160,8 +166,8 @@ pub fn decode_instruction(encoded_instr: u64) -> Result<Instruction, VirtualMach
})
}

fn decode_offset(offset: u64) -> isize {
let vectorized_offset: [u8; 8] = offset.to_le_bytes();
fn decode_offset(offset: u128) -> isize {
let vectorized_offset: [u8; 16] = offset.to_le_bytes();
let offset_16b_encoded = u16::from_le_bytes([vectorized_offset[0], vectorized_offset[1]]);
let complement_const = 0x8000u16;
let (offset_16b, _) = offset_16b_encoded.overflowing_sub(complement_const);
Expand All @@ -177,6 +183,16 @@ mod decoder_test {
#[cfg(target_arch = "wasm32")]
use wasm_bindgen_test::*;

#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn non_zero_high_bits() {
let error = decode_instruction(0x214a7800080008000);
assert_eq!(
error.unwrap_err().to_string(),
"Reserved instruction bits must be 0",
)
}

#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn invalid_op1_reg() {
Expand Down Expand Up @@ -224,7 +240,7 @@ mod decoder_test {
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn decode_flags_nop_add_jmp_add_imm_fp_fp() {
// 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
// 79 ... 17 16 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
Expand All @@ -244,7 +260,7 @@ mod decoder_test {
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn decode_flags_nop_add1_jmp_rel_mul_fp_ap_ap() {
// 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
// 79 ... 17 16 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
Expand All @@ -264,7 +280,7 @@ mod decoder_test {
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn decode_flags_assrt_add_regular_mul_ap_ap_ap() {
// 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
// 79 ... 17 16 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
Expand All @@ -284,7 +300,7 @@ mod decoder_test {
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn decode_flags_assrt_add2_jnz_uncon_op0_ap_ap() {
// 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
// 79 ... 17 16 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
Expand All @@ -304,7 +320,7 @@ mod decoder_test {
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn decode_flags_nop_regu_regu_op1_op0_ap_ap() {
// 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
// 79 ... 17 16 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
Expand All @@ -324,7 +340,7 @@ mod decoder_test {
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn decode_offset_negative() {
// 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
// 79 ... 17 16 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
Expand All @@ -338,7 +354,7 @@ mod decoder_test {
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn decode_ret_cairo_standard() {
// 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
// 79 ... 17 16 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
Expand All @@ -360,8 +376,8 @@ mod decoder_test {
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn decode_call_cairo_standard() {
// 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| IMM| FP| FP
// 79 ... 17 16 15| 14 13 12| 11 10| 9 8 7| 6 5|4 3 2| 1| 0
// Stone| CALL| Regular| 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();
Expand All @@ -382,7 +398,7 @@ mod decoder_test {
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn decode_ret_opcode_error() {
// 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
// 79 ... 17 16 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
Expand All @@ -394,8 +410,8 @@ mod decoder_test {
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn decode_call_opcode_error() {
// 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
// 79 ... 17 16 15| 14 13 12| 11 10| 9 8 7| 6 5|4 3 2| 1| 0
// Stone| CALL| REGULAR| JumpRel| Op1| IMM| AP| AP
// 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);
Expand All @@ -406,7 +422,7 @@ mod decoder_test {
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn decode_invalid_opcode_extension_error() {
// 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
// 79 ... 17 16 15| 14 13 12| 11 10| 9 8 7| 6 5|4 3 2| 1| 0
// ???| CALL| Add2| JumpRel| Op1| IMM| FP| FP
// 1 0 0 1 0 0 0 1 0 0 0 0 0 1 0 0
// 1001 0001 0000 0100 = 0x9104; off0 = 0, off1 = 1
Expand Down
14 changes: 8 additions & 6 deletions vm/src/vm/errors/vm_errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,18 +34,20 @@ pub enum VirtualMachineError {
MainScopeError(#[from] ExecScopeError),
#[error(transparent)]
Other(anyhow::Error),
#[error("Reserved instruction bits must be 0")]
NonZeroReservedBits,
#[error("Instruction should be an int")]
InvalidInstructionEncoding,
#[error("Invalid op1_register value: {0}")]
InvalidOp1Reg(u64),
InvalidOp1Reg(u128),
#[error("In immediate mode, off2 should be 1")]
ImmShouldBe1,
#[error("op0 must be known in double dereference")]
UnknownOp0,
#[error("Invalid ap_update value: {0}")]
InvalidApUpdate(u64),
InvalidApUpdate(u128),
#[error("Invalid pc_update value: {0}")]
InvalidPcUpdate(u64),
InvalidPcUpdate(u128),
#[error("Res.UNCONSTRAINED cannot be used with ApUpdate.ADD")]
UnconstrainedResAdd,
#[error("Res.UNCONSTRAINED cannot be used with PcUpdate.JUMP")]
Expand All @@ -71,11 +73,11 @@ pub enum VirtualMachineError {
#[error("Couldn't get or load dst")]
NoDst,
#[error("Invalid res value: {0}")]
InvalidRes(u64),
InvalidRes(u128),
#[error("Invalid opcode value: {0}")]
InvalidOpcode(u64),
InvalidOpcode(u128),
#[error("Invalid opcode extension value: {0}")]
InvalidOpcodeExtension(u64),
InvalidOpcodeExtension(u128),
#[error("This is not implemented")]
NotImplemented,
#[error("Inconsistent auto-deduction for {}, expected {}, got {:?}", (*.0).0, (*.0).1, (*.0).2)]
Expand Down
4 changes: 2 additions & 2 deletions vm/src/vm/vm_core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -452,7 +452,7 @@ impl VirtualMachine {
.segments
.memory
.get_integer(self.run_context.pc)?
.to_u64()
.to_u128()
.ok_or(VirtualMachineError::InvalidInstructionEncoding)?;
decode_instruction(instruction)
}
Expand Down Expand Up @@ -4187,7 +4187,7 @@ mod tests {
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn decode_current_instruction_invalid_encoding() {
let mut vm = vm!();
vm.segments = segments![((0, 0), ("112233445566778899", 16))];
vm.segments = segments![((0, 0), ("112233445566778899112233445566778899", 16))];
assert_matches!(
vm.decode_current_instruction(),
Err(VirtualMachineError::InvalidInstructionEncoding)
Expand Down

0 comments on commit 43f4ba4

Please sign in to comment.