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

Ohadn/u128 encoded instr #1940

Merged
merged 1 commit into from
Feb 11, 2025
Merged
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: 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 @@
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
Loading