Skip to content

Commit ee6ba48

Browse files
set the encoded instruction to be u128 for opcode_extensions to come
1 parent 23b291c commit ee6ba48

File tree

6 files changed

+72
-53
lines changed

6 files changed

+72
-53
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
#### Upcoming Changes
44

5+
* feat: set `encoded_instruction` to be u128 for opcode_extensions to come [#1940](https://github.com/lambdaclass/cairo-vm/pull/1940)
6+
57
* 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)
68

79
* feat: add the field `opcode_extension` to the structure of `Instruction` [#1933](https://github.com/lambdaclass/cairo-vm/pull/1933)

cairo-vm-tracer/src/tracer_data.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ impl TracerData {
143143
let (instruction_encoding, _) =
144144
get_instruction_encoding(entry.pc, &memory, program.prime())?;
145145

146-
let instruction_encoding = instruction_encoding.to_u64();
146+
let instruction_encoding = instruction_encoding.to_u128();
147147
if instruction_encoding.is_none() {
148148
return Err(TraceDataError::FailedToConvertInstructionEncoding);
149149
}

vm/src/types/instruction.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -93,11 +93,11 @@ impl Instruction {
9393

9494
// Returns True if the given instruction looks like a call instruction
9595
pub(crate) fn is_call_instruction(encoded_instruction: &Felt252) -> bool {
96-
let encoded_i64_instruction = match encoded_instruction.to_u64() {
96+
let encoded_u128_instruction = match encoded_instruction.to_u128() {
9797
Some(num) => num,
9898
None => return false,
9999
};
100-
let instruction = match decode_instruction(encoded_i64_instruction) {
100+
let instruction = match decode_instruction(encoded_u128_instruction) {
101101
Ok(inst) => inst,
102102
Err(_) => return false,
103103
};
@@ -140,7 +140,7 @@ mod tests {
140140
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
141141
fn instruction_size() {
142142
let encoded_instruction = Felt252::from(1226245742482522112_i64);
143-
let instruction = decode_instruction(encoded_instruction.to_u64().unwrap()).unwrap();
143+
let instruction = decode_instruction(encoded_instruction.to_u128().unwrap()).unwrap();
144144
assert_eq!(instruction.size(), 2);
145145
}
146146
}

vm/src/vm/decoding/decoder.rs

Lines changed: 56 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -5,36 +5,41 @@ use crate::{
55
vm::errors::vm_errors::VirtualMachineError,
66
};
77

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

11-
/// Decodes an instruction. The encoding is little endian, so flags go from bit 63 to 48.
12-
/// The bits 64 and beyond are reserved for the opcode extension.
11+
/// Decodes an instruction. The encoding is little endian, so flags go from bit 127 to 48.
12+
/// The bits 63 and beyond are reserved for the opcode extension.
1313
/// opcode_extension_num=0 means the instruction is a Stone instruction.
14-
/// opcode_extension_num>1 is for new Stwo opcodes.
15-
pub fn decode_instruction(encoded_instr: u64) -> Result<Instruction, VirtualMachineError> {
16-
const DST_REG_MASK: u64 = 0x0001;
17-
const DST_REG_OFF: u64 = 0;
18-
const OP0_REG_MASK: u64 = 0x0002;
19-
const OP0_REG_OFF: u64 = 1;
20-
const OP1_SRC_MASK: u64 = 0x001C;
21-
const OP1_SRC_OFF: u64 = 2;
22-
const RES_LOGIC_MASK: u64 = 0x0060;
23-
const RES_LOGIC_OFF: u64 = 5;
24-
const PC_UPDATE_MASK: u64 = 0x0380;
25-
const PC_UPDATE_OFF: u64 = 7;
26-
const AP_UPDATE_MASK: u64 = 0x0C00;
27-
const AP_UPDATE_OFF: u64 = 10;
28-
const OPCODE_MASK: u64 = 0x7000;
29-
const OPCODE_OFF: u64 = 12;
30-
const OPCODE_EXTENSION_OFF: u64 = 63;
14+
/// opcode_extension_num>0 is for new Stwo opcodes.
15+
pub fn decode_instruction(encoded_instr: u128) -> Result<Instruction, VirtualMachineError> {
16+
const HIGH_BITS: u128 = ((1 << 127) - (1 << 64)) << 1;
17+
const DST_REG_MASK: u128 = 0x0001;
18+
const DST_REG_OFF: u128 = 0;
19+
const OP0_REG_MASK: u128 = 0x0002;
20+
const OP0_REG_OFF: u128 = 1;
21+
const OP1_SRC_MASK: u128 = 0x001C;
22+
const OP1_SRC_OFF: u128 = 2;
23+
const RES_LOGIC_MASK: u128 = 0x0060;
24+
const RES_LOGIC_OFF: u128 = 5;
25+
const PC_UPDATE_MASK: u128 = 0x0380;
26+
const PC_UPDATE_OFF: u128 = 7;
27+
const AP_UPDATE_MASK: u128 = 0x0C00;
28+
const AP_UPDATE_OFF: u128 = 10;
29+
const OPCODE_MASK: u128 = 0x7000;
30+
const OPCODE_OFF: u128 = 12;
31+
const OPCODE_EXTENSION_OFF: u128 = 63;
3132

3233
// Flags start on the 48th bit.
33-
const FLAGS_OFFSET: u64 = 48;
34-
const OFF0_OFF: u64 = 0;
35-
const OFF1_OFF: u64 = 16;
36-
const OFF2_OFF: u64 = 32;
37-
const OFFX_MASK: u64 = 0xFFFF;
34+
const FLAGS_OFFSET: u128 = 48;
35+
const OFF0_OFF: u128 = 0;
36+
const OFF1_OFF: u128 = 16;
37+
const OFF2_OFF: u128 = 32;
38+
const OFFX_MASK: u128 = 0xFFFF;
39+
40+
if (encoded_instr & HIGH_BITS) != 0 {
41+
return Err(VirtualMachineError::NonZeroReservedBits);
42+
}
3843

3944
// Grab offsets and convert them from little endian format.
4045
let off0 = decode_offset(encoded_instr >> OFF0_OFF & OFFX_MASK);
@@ -160,8 +165,8 @@ pub fn decode_instruction(encoded_instr: u64) -> Result<Instruction, VirtualMach
160165
})
161166
}
162167

163-
fn decode_offset(offset: u64) -> isize {
164-
let vectorized_offset: [u8; 8] = offset.to_le_bytes();
168+
fn decode_offset(offset: u128) -> isize {
169+
let vectorized_offset: [u8; 8] = (offset as u64).to_le_bytes();
165170
let offset_16b_encoded = u16::from_le_bytes([vectorized_offset[0], vectorized_offset[1]]);
166171
let complement_const = 0x8000u16;
167172
let (offset_16b, _) = offset_16b_encoded.overflowing_sub(complement_const);
@@ -177,6 +182,16 @@ mod decoder_test {
177182
#[cfg(target_arch = "wasm32")]
178183
use wasm_bindgen_test::*;
179184

185+
#[test]
186+
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
187+
fn non_zero_high_bits() {
188+
let error = decode_instruction(0x214a7800080008000);
189+
assert_eq!(
190+
error.unwrap_err().to_string(),
191+
"Reserved instruction bits must be 0",
192+
)
193+
}
194+
180195
#[test]
181196
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
182197
fn invalid_op1_reg() {
@@ -224,7 +239,7 @@ mod decoder_test {
224239
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
225240
fn decode_flags_nop_add_jmp_add_imm_fp_fp() {
226241
// opcode_extension| opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg
227-
// ... 15| 14 13 12| 11 10| 9 8 7| 6 5|4 3 2| 1| 0
242+
// 79 ... 17 16 15| 14 13 12| 11 10| 9 8 7| 6 5|4 3 2| 1| 0
228243
// Stone| NOp| ADD| JUMP| ADD| IMM| FP| FP
229244
// 0 0 0 0 0 1 0 0 1 0 1 0 0 1 1 1
230245
// 0000 0100 1010 0111 = 0x04A7; offx = 0
@@ -244,7 +259,7 @@ mod decoder_test {
244259
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
245260
fn decode_flags_nop_add1_jmp_rel_mul_fp_ap_ap() {
246261
// opcode_extension| opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg
247-
// ... 15| 14 13 12| 11 10| 9 8 7| 6 5|4 3 2| 1| 0
262+
// 79 ... 17 16 15| 14 13 12| 11 10| 9 8 7| 6 5|4 3 2| 1| 0
248263
// Stone| NOp| ADD1| JUMP_REL| MUL| FP| AP| AP
249264
// 0 0 0 0 1 0 0 1 0 1 0 0 1 0 0 0
250265
// 0000 1001 0100 1000 = 0x0948; offx = 0
@@ -264,7 +279,7 @@ mod decoder_test {
264279
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
265280
fn decode_flags_assrt_add_regular_mul_ap_ap_ap() {
266281
// opcode_extension| opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg
267-
// ... 15| 14 13 12| 11 10| 9 8 7| 6 5|4 3 2| 1| 0
282+
// 79 ... 17 16 15| 14 13 12| 11 10| 9 8 7| 6 5|4 3 2| 1| 0
268283
// Stone| ASSRT_EQ| ADD| REGULAR| MUL| AP| AP| AP
269284
// 0 1 0 0 1 0 0 0 0 1 0 1 0 0 0 0
270285
// 0100 1000 0101 0000 = 0x4850; offx = 0
@@ -284,7 +299,7 @@ mod decoder_test {
284299
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
285300
fn decode_flags_assrt_add2_jnz_uncon_op0_ap_ap() {
286301
// opcode_extension| opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg
287-
// ... 15| 14 13 12| 11 10| 9 8 7| 6 5|4 3 2| 1| 0
302+
// 79 ... 17 16 15| 14 13 12| 11 10| 9 8 7| 6 5|4 3 2| 1| 0
288303
// Stone| ASSRT_EQ| ADD2| JNZ|UNCONSTRD| OP0| AP| AP
289304
// 0 1 0 0 0 0 1 0 0 0 0 0 0 0 0 0
290305
// 0100 0010 0000 0000 = 0x4200; offx = 0
@@ -304,7 +319,7 @@ mod decoder_test {
304319
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
305320
fn decode_flags_nop_regu_regu_op1_op0_ap_ap() {
306321
// opcode_extension| opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg
307-
// ... 15| 14 13 12| 11 10| 9 8 7| 6 5|4 3 2| 1| 0
322+
// 79 ... 17 16 15| 14 13 12| 11 10| 9 8 7| 6 5|4 3 2| 1| 0
308323
// Stone| NOP| REGULAR| REGULAR| OP1| OP0| AP| AP
309324
// 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
310325
// 0000 0000 0000 0000 = 0x0000; offx = 0
@@ -324,7 +339,7 @@ mod decoder_test {
324339
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
325340
fn decode_offset_negative() {
326341
// opcode_extension| opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg
327-
// ... 15| 14 13 12| 11 10| 9 8 7| 6 5|4 3 2| 1| 0
342+
// 79 ... 17 16 15| 14 13 12| 11 10| 9 8 7| 6 5|4 3 2| 1| 0
328343
// Stone| NOP| REGULAR| REGULAR| OP1| OP0| AP| AP
329344
// 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
330345
// 0000 0000 0000 0000 = 0x0000; offx = 0
@@ -338,7 +353,7 @@ mod decoder_test {
338353
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
339354
fn decode_ret_cairo_standard() {
340355
// opcode_extension| opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg
341-
// ... 15| 14 13 12| 11 10| 9 8 7| 6 5|4 3 2| 1| 0
356+
// 79 ... 17 16 15| 14 13 12| 11 10| 9 8 7| 6 5|4 3 2| 1| 0
342357
// Stone| RET| REGULAR| JUMP| Op1| FP| FP| FP
343358
// 0 0 1 0 0 0 0 0 1 0 0 0 1 0 1 1
344359
// 0010 0000 1000 1011 = 0x208b; off0 = -2, off1 = -1
@@ -360,8 +375,8 @@ mod decoder_test {
360375
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
361376
fn decode_call_cairo_standard() {
362377
// opcode_extension| opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg
363-
// ... 15| 14 13 12| 11 10| 9 8 7| 6 5|4 3 2| 1| 0
364-
// Stone| CALL| Add2| JumpRel| Op1| IMM| FP| FP
378+
// 79 ... 17 16 15| 14 13 12| 11 10| 9 8 7| 6 5|4 3 2| 1| 0
379+
// Stone| CALL| Regular| JumpRel| Op1| FP| FP| FP
365380
// 0 0 0 1 0 0 0 1 0 0 0 0 0 1 0 0
366381
// 0001 0001 0000 0100 = 0x1104; off0 = 0, off1 = 1
367382
let inst = decode_instruction(0x1104800180018000).unwrap();
@@ -382,7 +397,7 @@ mod decoder_test {
382397
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
383398
fn decode_ret_opcode_error() {
384399
// opcode_extension| opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg
385-
// ... 15| 14 13 12| 11 10| 9 8 7| 6 5|4 3 2| 1| 0
400+
// 79 ... 17 16 15| 14 13 12| 11 10| 9 8 7| 6 5|4 3 2| 1| 0
386401
// Stone| RET| REGULAR| JUMP| Op1| FP| FP| FP
387402
// 0 0 1 0 0 0 0 0 1 0 0 0 1 0 1 1
388403
// 0010 0000 1000 1011 = 0x208b; off0 = -1, off1 = -1
@@ -394,8 +409,8 @@ mod decoder_test {
394409
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
395410
fn decode_call_opcode_error() {
396411
// opcode_extension| opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg
397-
// ... 15| 14 13 12| 11 10| 9 8 7| 6 5|4 3 2| 1| 0
398-
// Stone| CALL| Add2| JumpRel| Op1| FP| FP| FP
412+
// 79 ... 17 16 15| 14 13 12| 11 10| 9 8 7| 6 5|4 3 2| 1| 0
413+
// Stone| CALL| REGULAR| JumpRel| Op1| IMM| AP| AP
399414
// 0 0 0 1 0 0 0 1 0 0 0 0 0 1 0 0
400415
// 0001 0001 0000 0100 = 0x1104; off0 = 1, off1 = 1
401416
let error = decode_instruction(0x1104800180018001);
@@ -406,7 +421,7 @@ mod decoder_test {
406421
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
407422
fn decode_invalid_opcode_extension_error() {
408423
// opcode_extension| opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg
409-
// ... 15| 14 13 12| 11 10| 9 8 7| 6 5|4 3 2| 1| 0
424+
// 79 ... 17 16 15| 14 13 12| 11 10| 9 8 7| 6 5|4 3 2| 1| 0
410425
// ???| CALL| Add2| JumpRel| Op1| IMM| FP| FP
411426
// 1 0 0 1 0 0 0 1 0 0 0 0 0 1 0 0
412427
// 1001 0001 0000 0100 = 0x9104; off0 = 0, off1 = 1

vm/src/vm/errors/vm_errors.rs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,18 +34,20 @@ pub enum VirtualMachineError {
3434
MainScopeError(#[from] ExecScopeError),
3535
#[error(transparent)]
3636
Other(anyhow::Error),
37+
#[error("Reserved instruction bits must be 0")]
38+
NonZeroReservedBits,
3739
#[error("Instruction should be an int")]
3840
InvalidInstructionEncoding,
3941
#[error("Invalid op1_register value: {0}")]
40-
InvalidOp1Reg(u64),
42+
InvalidOp1Reg(u128),
4143
#[error("In immediate mode, off2 should be 1")]
4244
ImmShouldBe1,
4345
#[error("op0 must be known in double dereference")]
4446
UnknownOp0,
4547
#[error("Invalid ap_update value: {0}")]
46-
InvalidApUpdate(u64),
48+
InvalidApUpdate(u128),
4749
#[error("Invalid pc_update value: {0}")]
48-
InvalidPcUpdate(u64),
50+
InvalidPcUpdate(u128),
4951
#[error("Res.UNCONSTRAINED cannot be used with ApUpdate.ADD")]
5052
UnconstrainedResAdd,
5153
#[error("Res.UNCONSTRAINED cannot be used with PcUpdate.JUMP")]
@@ -71,11 +73,11 @@ pub enum VirtualMachineError {
7173
#[error("Couldn't get or load dst")]
7274
NoDst,
7375
#[error("Invalid res value: {0}")]
74-
InvalidRes(u64),
76+
InvalidRes(u128),
7577
#[error("Invalid opcode value: {0}")]
76-
InvalidOpcode(u64),
78+
InvalidOpcode(u128),
7779
#[error("Invalid opcode extension value: {0}")]
78-
InvalidOpcodeExtension(u64),
80+
InvalidOpcodeExtension(u128),
7981
#[error("This is not implemented")]
8082
NotImplemented,
8183
#[error("Inconsistent auto-deduction for {}, expected {}, got {:?}", (*.0).0, (*.0).1, (*.0).2)]

vm/src/vm/vm_core.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -452,7 +452,7 @@ impl VirtualMachine {
452452
.segments
453453
.memory
454454
.get_integer(self.run_context.pc)?
455-
.to_u64()
455+
.to_u128()
456456
.ok_or(VirtualMachineError::InvalidInstructionEncoding)?;
457457
decode_instruction(instruction)
458458
}
@@ -4187,7 +4187,7 @@ mod tests {
41874187
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
41884188
fn decode_current_instruction_invalid_encoding() {
41894189
let mut vm = vm!();
4190-
vm.segments = segments![((0, 0), ("112233445566778899", 16))];
4190+
vm.segments = segments![((0, 0), ("112233445566778899112233445566778899", 16))];
41914191
assert_matches!(
41924192
vm.decode_current_instruction(),
41934193
Err(VirtualMachineError::InvalidInstructionEncoding)

0 commit comments

Comments
 (0)