diff --git a/programs/rol.zasm b/programs/rol.zasm new file mode 100644 index 00000000..6ceb6cee --- /dev/null +++ b/programs/rol.zasm @@ -0,0 +1,13 @@ + .text + .file "rol.zasm" + .globl __entry +__entry: +.func_begin0: + add 1, r0, r1 + add 4, r0, r2 + rol r1, r2, r3 + sstore r0, r3 + ret +.func_end0: + .note.GNU-stack + .rodata diff --git a/programs/rol_conditional_eq.zasm b/programs/rol_conditional_eq.zasm new file mode 100644 index 00000000..4aaee521 --- /dev/null +++ b/programs/rol_conditional_eq.zasm @@ -0,0 +1,13 @@ + .text + .file "rol_conditional_eq.zasm" + .globl __entry +__entry: +.func_begin0: + add 1, r0, r1 + add 4, r0, r2 + rol.eq r1, r2, r3 + sstore r0, r3 + ret +.func_end0: + .note.GNU-stack + .rodata diff --git a/programs/rol_greater_than_256.zasm b/programs/rol_greater_than_256.zasm new file mode 100644 index 00000000..5c0a5a90 --- /dev/null +++ b/programs/rol_greater_than_256.zasm @@ -0,0 +1,12 @@ + .text + .file "rol_greater_than_256.zasm" + .globl __entry +__entry: +.func_begin0: + ; test sets r1 = 1, r2 = 258 + rol r1, r2, r3 + sstore r0, r3 + ret +.func_end0: + .note.GNU-stack + .rodata diff --git a/programs/rol_sets_eq_flag.zasm b/programs/rol_sets_eq_flag.zasm new file mode 100644 index 00000000..eb0289bf --- /dev/null +++ b/programs/rol_sets_eq_flag.zasm @@ -0,0 +1,16 @@ + .text + .file "rol_sets_eq_flag.zasm" + .globl __entry +__entry: + +.func_begin0: + ; EQ is set if the result is zero + add 0, r0, r1 + add 0, r0, r2 + rol! r1, r2, r1 + sstore r0, r1 + ret + +.func_end0: + .note.GNU-stack + .rodata diff --git a/programs/rol_stack.zasm b/programs/rol_stack.zasm new file mode 100644 index 00000000..a08ab18b --- /dev/null +++ b/programs/rol_stack.zasm @@ -0,0 +1,17 @@ + .text + .file "rol_stack.zasm" + .globl __entry +__entry: +.func_begin0: + ; grow stack + add 1, r0, stack+=[2] + ; set stack value + add 1, r0, stack[0] + add 4, r0, r2 + ; left rotation + rol stack[0], r2, r3 + sstore r0, r3 + ret +.func_end0: + .note.GNU-stack + .rodata diff --git a/programs/ror.zasm b/programs/ror.zasm new file mode 100644 index 00000000..43dab630 --- /dev/null +++ b/programs/ror.zasm @@ -0,0 +1,13 @@ + .text + .file "ror.zasm" + .globl __entry +__entry: +.func_begin0: + add 16, r0, r1 + add 4, r0, r2 + ror r1, r2, r3 + sstore r0, r3 + ret +.func_end0: + .note.GNU-stack + .rodata diff --git a/programs/ror_conditional_eq.zasm b/programs/ror_conditional_eq.zasm new file mode 100644 index 00000000..237903c4 --- /dev/null +++ b/programs/ror_conditional_eq.zasm @@ -0,0 +1,13 @@ + .text + .file "ror_conditional_eq.zasm" + .globl __entry +__entry: +.func_begin0: + add 16, r0, r1 + add 4, r0, r2 + ror.eq r1, r2, r3 + sstore r0, r3 + ret +.func_end0: + .note.GNU-stack + .rodata diff --git a/programs/ror_greater_than_256.zasm b/programs/ror_greater_than_256.zasm new file mode 100644 index 00000000..ed050d27 --- /dev/null +++ b/programs/ror_greater_than_256.zasm @@ -0,0 +1,12 @@ + .text + .file "ror_greater_than_256.zasm" + .globl __entry +__entry: +.func_begin0: + ; test sets r1 = 16, r2 = 258 + ror r1, r2, r3 + sstore r0, r3 + ret +.func_end0: + .note.GNU-stack + .rodata diff --git a/programs/ror_sets_eq_flag.zasm b/programs/ror_sets_eq_flag.zasm new file mode 100644 index 00000000..5c752045 --- /dev/null +++ b/programs/ror_sets_eq_flag.zasm @@ -0,0 +1,16 @@ + .text + .file "ror_sets_eq_flag.zasm" + .globl __entry +__entry: + +.func_begin0: + ; EQ is set if the result is zero + add 0, r0, r1 + add 0, r0, r2 + ror! r1, r2, r1 + sstore r0, r1 + ret + +.func_end0: + .note.GNU-stack + .rodata diff --git a/programs/ror_stack.zasm b/programs/ror_stack.zasm new file mode 100644 index 00000000..6683f9dd --- /dev/null +++ b/programs/ror_stack.zasm @@ -0,0 +1,17 @@ + .text + .file "ror_stack.zasm" + .globl __entry +__entry: +.func_begin0: + ; grow stack + add 1, r0, stack+=[2] + ; set stack value + add 16, r0, stack[0] + add 4, r0, r2 + ; right rotation + ror stack[0], r2, r3 + sstore r0, r3 + ret +.func_end0: + .note.GNU-stack + .rodata diff --git a/programs/shl.zasm b/programs/shl.zasm new file mode 100644 index 00000000..8bfbbc77 --- /dev/null +++ b/programs/shl.zasm @@ -0,0 +1,13 @@ + .text + .file "shl.zasm" + .globl __entry +__entry: +.func_begin0: + add 1, r0, r1 + add 2, r0, r2 + shl r1, r2, r3 + sstore r0, r3 + ret +.func_end0: + .note.GNU-stack + .rodata diff --git a/programs/shl_conditional_eq.zasm b/programs/shl_conditional_eq.zasm new file mode 100644 index 00000000..348fd730 --- /dev/null +++ b/programs/shl_conditional_eq.zasm @@ -0,0 +1,13 @@ + .text + .file "shl_conditional_eq.zasm" + .globl __entry +__entry: +.func_begin0: + add 4, r0, r1 + add 1, r0, r2 + shl.eq r1, r2, r3 + sstore r0, r3 + ret +.func_end0: + .note.GNU-stack + .rodata diff --git a/programs/shl_greater_than_256.zasm b/programs/shl_greater_than_256.zasm new file mode 100644 index 00000000..41b2696f --- /dev/null +++ b/programs/shl_greater_than_256.zasm @@ -0,0 +1,12 @@ + .text + .file "shl_greater_than_256.zasm" + .globl __entry +__entry: +.func_begin0: + ; test sets r1 = 1, r2 = 258 + shl r1, r2, r3 + sstore r0, r3 + ret +.func_end0: + .note.GNU-stack + .rodata diff --git a/programs/shl_sets_eq_flag.zasm b/programs/shl_sets_eq_flag.zasm new file mode 100644 index 00000000..7a1bdd98 --- /dev/null +++ b/programs/shl_sets_eq_flag.zasm @@ -0,0 +1,16 @@ + .text + .file "shl_sets_eq_flag.zasm" + .globl __entry +__entry: + +.func_begin0: + ; EQ is set if the result is zero + add 0, r0, r1 + add 0, r0, r2 + shl! r1, r2, r1 + sstore r0, r1 + ret + +.func_end0: + .note.GNU-stack + .rodata diff --git a/programs/shl_stack.zasm b/programs/shl_stack.zasm new file mode 100644 index 00000000..2e4581b4 --- /dev/null +++ b/programs/shl_stack.zasm @@ -0,0 +1,17 @@ + .text + .file "shl_stack.zasm" + .globl __entry +__entry: +.func_begin0: + ; grow stack + add 1, r0, stack+=[2] + ; set stack value + add 4, r0, stack[0] + add 2, r0, r2 + ; shift left + shl stack[0], r2, r3 + sstore r0, r3 + ret +.func_end0: + .note.GNU-stack + .rodata diff --git a/programs/shr.zasm b/programs/shr.zasm new file mode 100644 index 00000000..b13a5fe4 --- /dev/null +++ b/programs/shr.zasm @@ -0,0 +1,13 @@ + .text + .file "shr.zasm" + .globl __entry +__entry: +.func_begin0: + add 8, r0, r1 + add 2, r0, r2 + shr r1, r2, r3 + sstore r0, r3 + ret +.func_end0: + .note.GNU-stack + .rodata diff --git a/programs/shr_conditional_eq.zasm b/programs/shr_conditional_eq.zasm new file mode 100644 index 00000000..6a4b0b44 --- /dev/null +++ b/programs/shr_conditional_eq.zasm @@ -0,0 +1,13 @@ + .text + .file "shr_conditional_eq.zasm" + .globl __entry +__entry: +.func_begin0: + add 8, r0, r1 + add 2, r0, r2 + shr.eq r1, r2, r3 + sstore r0, r3 + ret +.func_end0: + .note.GNU-stack + .rodata diff --git a/programs/shr_greater_than_256.zasm b/programs/shr_greater_than_256.zasm new file mode 100644 index 00000000..478df3d3 --- /dev/null +++ b/programs/shr_greater_than_256.zasm @@ -0,0 +1,12 @@ + .text + .file "shr_greater_than_256.zasm" + .globl __entry +__entry: +.func_begin0: + ; test sets r1 = 16, r2 = 258 + shr r1, r2, r3 + sstore r0, r3 + ret +.func_end0: + .note.GNU-stack + .rodata diff --git a/programs/shr_sets_eq_flag.zasm b/programs/shr_sets_eq_flag.zasm new file mode 100644 index 00000000..45d6aafa --- /dev/null +++ b/programs/shr_sets_eq_flag.zasm @@ -0,0 +1,16 @@ + .text + .file "shr_sets_eq_flag.zasm" + .globl __entry +__entry: + +.func_begin0: + ; EQ is set if the result is zero + add 0, r0, r1 + add 0, r0, r2 + shr! r1, r2, r1 + sstore r0, r1 + ret + +.func_end0: + .note.GNU-stack + .rodata diff --git a/programs/shr_stack.zasm b/programs/shr_stack.zasm new file mode 100644 index 00000000..3ba0b64a --- /dev/null +++ b/programs/shr_stack.zasm @@ -0,0 +1,17 @@ + .text + .file "shr_stack.zasm" + .globl __entry +__entry: +.func_begin0: + ; grow stack + add 1, r0, stack+=[2] + ; set stack value + add 4, r0, stack[0] + add 2, r0, r2 + ; shift right + shr stack[0], r2, r3 + sstore r0, r3 + ret +.func_end0: + .note.GNU-stack + .rodata diff --git a/src/lib.rs b/src/lib.rs index cab37ee1..02e4ebaa 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -27,6 +27,10 @@ use op_handlers::ptr_add::_ptr_add; use op_handlers::ptr_pack::_ptr_pack; use op_handlers::ptr_shrink::_ptr_shrink; use op_handlers::ptr_sub::_ptr_sub; +use op_handlers::shift::_rol; +use op_handlers::shift::_ror; +use op_handlers::shift::_shl; +use op_handlers::shift::_shr; use op_handlers::sub::_sub; use op_handlers::xor::_xor; pub use opcode::Opcode; @@ -39,6 +43,7 @@ use zkevm_opcode_defs::ISAVersion; use zkevm_opcode_defs::LogOpcode; use zkevm_opcode_defs::Opcode as Variant; use zkevm_opcode_defs::PtrOpcode; +use zkevm_opcode_defs::ShiftOpcode; pub fn program_from_file(bin_path: &str) -> Vec { let program = std::fs::read(bin_path).unwrap(); @@ -112,7 +117,13 @@ pub fn run(mut vm: VMState) -> (U256, VMState) { Variant::Mul(_) => _mul(&mut vm, &opcode), Variant::Div(_) => _div(&mut vm, &opcode), Variant::Context(_) => todo!(), - Variant::Shift(_) => todo!(), + Variant::Shift(_) => match opcode.variant { + Variant::Shift(ShiftOpcode::Shl) => _shl(&mut vm, &opcode), + Variant::Shift(ShiftOpcode::Shr) => _shr(&mut vm, &opcode), + Variant::Shift(ShiftOpcode::Rol) => _rol(&mut vm, &opcode), + Variant::Shift(ShiftOpcode::Ror) => _ror(&mut vm, &opcode), + _ => unreachable!(), + }, Variant::Binop(binop) => match binop { BinopOpcode::Xor => _xor(&mut vm, &opcode), BinopOpcode::And => _and(&mut vm, &opcode), diff --git a/src/op_handlers/mod.rs b/src/op_handlers/mod.rs index a1e4aec7..f90e714c 100644 --- a/src/op_handlers/mod.rs +++ b/src/op_handlers/mod.rs @@ -10,5 +10,6 @@ pub mod ptr_add; pub mod ptr_pack; pub mod ptr_shrink; pub mod ptr_sub; +pub mod shift; pub mod sub; pub mod xor; diff --git a/src/op_handlers/shift.rs b/src/op_handlers/shift.rs new file mode 100644 index 00000000..e1ffa62d --- /dev/null +++ b/src/op_handlers/shift.rs @@ -0,0 +1,63 @@ +use crate::address_operands::{address_operands_read, address_operands_store}; +use crate::value::TaggedValue; +use crate::{opcode::Opcode, state::VMState}; + +pub fn _shl(vm: &mut VMState, opcode: &Opcode) { + let (src0_t, src1_t) = address_operands_read(vm, opcode); + let (src0, src1) = (src0_t.value, src1_t.value); + let shift = src1 % 256; + let res = src0 << shift; + if opcode.alters_vm_flags { + // Eq is set if result == 0 + vm.flag_eq |= res.is_zero(); + // other flags are reset + vm.flag_lt_of = false; + vm.flag_gt = false; + } + address_operands_store(vm, opcode, TaggedValue::new_raw_integer(res)); +} + +pub fn _shr(vm: &mut VMState, opcode: &Opcode) { + let (src0_t, src1_t) = address_operands_read(vm, opcode); + let (src0, src1) = (src0_t.value, src1_t.value); + let shift = src1 % 256; + let res = src0 >> shift; + if opcode.alters_vm_flags { + // Eq is set if result == 0 + vm.flag_eq |= res.is_zero(); + // other flags are reset + vm.flag_lt_of = false; + vm.flag_gt = false; + } + address_operands_store(vm, opcode, TaggedValue::new_raw_integer(res)); +} + +pub fn _rol(vm: &mut VMState, opcode: &Opcode) { + let (src0_t, src1_t) = address_operands_read(vm, opcode); + let (src0, src1) = (src0_t.value, src1_t.value); + let shift = src1.low_u32() % 256; + let result = (src0 << shift) | (src0 >> (256 - shift)); + if opcode.alters_vm_flags { + // Eq is set if result == 0 + vm.flag_eq |= result.is_zero(); + // other flags are reset + vm.flag_lt_of = false; + vm.flag_gt = false; + } + address_operands_store(vm, opcode, TaggedValue::new_raw_integer(result)); +} + +pub fn _ror(vm: &mut VMState, opcode: &Opcode) { + let (src0_t, src1_t) = address_operands_read(vm, opcode); + let (src0, src1) = (src0_t.value, src1_t.value); + let shift = src1.low_u32() % 256; + let result = (src0 >> shift) | (src0 << (256 - shift)); + if opcode.alters_vm_flags { + // Eq is set if result == 0 + vm.flag_eq |= result.is_zero(); + // other flags are reset + vm.flag_lt_of = false; + vm.flag_gt = false; + } + address_operands_store(vm, opcode, TaggedValue::new_raw_integer(result)); +} diff --git a/tests/integration_test.rs b/tests/integration_test.rs index dc605f92..a9c29665 100644 --- a/tests/integration_test.rs +++ b/tests/integration_test.rs @@ -1137,3 +1137,195 @@ fn test_ptr_pack_in_stack() { U256::from_str_radix("0x100000000000000000000000000000000", 16).unwrap() ); } + +#[test] +fn test_shl_asm() { + let bin_path = make_bin_path_asm("shl"); + let (_, vm) = run_program(&bin_path); + let result = vm.get_register(3); + + assert_eq!(result.value, U256::from(4)); // 1 << 2 = 4 +} + +#[test] +fn test_shr_asm() { + let bin_path = make_bin_path_asm("shr"); + let (_, vm) = run_program(&bin_path); + let result = vm.get_register(3); + + assert_eq!(result.value, U256::from(2)); // 8 >> 2 = 2 +} + +#[test] +fn test_shl_stack() { + let bin_path = make_bin_path_asm("shl_stack"); + let (result, _) = run_program(&bin_path); + assert_eq!(result, U256::from(16)); // 4 << 2 = 16 +} + +#[test] +fn test_shr_stack() { + let bin_path = make_bin_path_asm("shr_stack"); + let (result, _) = run_program(&bin_path); + assert_eq!(result, U256::from(1)); // 4 >> 2 = 1 +} + +#[test] +fn test_shl_conditional_eq_set() { + let bin_path = make_bin_path_asm("shl_conditional_eq"); + let vm_with_custom_flags = VMStateBuilder::new().eq_flag(true).build(); + let (result, _final_vm_state) = run_program_with_custom_state(&bin_path, vm_with_custom_flags); + assert_eq!(result, U256::from(8)); // 4 << 1 = 8 +} + +#[test] +fn test_shr_conditional_eq_set() { + let bin_path = make_bin_path_asm("shr_conditional_eq"); + let vm_with_custom_flags = VMStateBuilder::new().eq_flag(true).build(); + let (result, _) = run_program_with_custom_state(&bin_path, vm_with_custom_flags); + assert_eq!(result, U256::from(2)); // 8 >> 2 = 2 +} + +#[test] +fn test_shl_set_eq_flag() { + let bin_path = make_bin_path_asm("shl_sets_eq_flag"); + let (_, vm) = run_program(&bin_path); + + assert!(vm.flag_eq); +} + +#[test] +fn test_shr_set_eq_flag() { + let bin_path = make_bin_path_asm("shr_sets_eq_flag"); + let (_, vm) = run_program(&bin_path); + + assert!(vm.flag_eq); +} + +#[test] +fn test_rol_asm() { + let bin_path = make_bin_path_asm("rol"); + let (_, vm) = run_program(&bin_path); + let result = vm.get_register(3); + + assert_eq!(result.value, U256::from(16)); // 1 rol 4 = 16 +} + +#[test] +fn test_ror_asm() { + let bin_path = make_bin_path_asm("ror"); + let (_, vm) = run_program(&bin_path); + let result = vm.get_register(3); + + assert_eq!(result.value, U256::from(1)); // 16 ror 4 = 1 +} + +#[test] +fn test_rol_stack() { + let bin_path = make_bin_path_asm("rol_stack"); + let (result, _) = run_program(&bin_path); + assert_eq!(result, U256::from(16)); // 1 rol 4 = 16 +} + +#[test] +fn test_ror_stack() { + let bin_path = make_bin_path_asm("ror_stack"); + let (result, _) = run_program(&bin_path); + assert_eq!(result, U256::from(1)); // 16 ror 4 = 1 +} + +#[test] +fn test_rol_conditional_eq_set() { + let bin_path = make_bin_path_asm("rol_conditional_eq"); + let vm_with_custom_flags = VMStateBuilder::new().eq_flag(true).build(); + let (result, _final_vm_state) = run_program_with_custom_state(&bin_path, vm_with_custom_flags); + assert_eq!(result, U256::from(16)); // 1 rol 4 = 16 +} + +#[test] +fn test_ror_conditional_eq_set() { + let bin_path = make_bin_path_asm("ror_conditional_eq"); + let vm_with_custom_flags = VMStateBuilder::new().eq_flag(true).build(); + let (result, _) = run_program_with_custom_state(&bin_path, vm_with_custom_flags); + assert_eq!(result, U256::from(1)); // 16 ror 4 = 1 +} + +#[test] +fn test_rol_set_eq_flag() { + let bin_path = make_bin_path_asm("rol_sets_eq_flag"); + let (_, vm) = run_program(&bin_path); + + assert!(vm.flag_eq); +} + +#[test] +fn test_ror_set_eq_flag() { + let bin_path = make_bin_path_asm("ror_sets_eq_flag"); + let (_, vm) = run_program(&bin_path); + + assert!(vm.flag_eq); +} + +#[test] +fn test_shl_asm_greater_than_256() { + let bin_path = make_bin_path_asm("shl_greater_than_256"); + let r1 = TaggedValue::new_raw_integer(U256::from(1)); + let r2 = TaggedValue::new_raw_integer(U256::from(258)); // Shift amount greater than 256 + let mut registers: [TaggedValue; 15] = [TaggedValue::default(); 15]; + registers[0] = r1; + registers[1] = r2; + let vm_with_custom_flags = VMStateBuilder::new().with_registers(registers).build(); + + let (_, vm) = run_program_with_custom_state(&bin_path, vm_with_custom_flags); + let result = vm.get_register(3); + + assert_eq!(result.value, U256::from(4)); // 1 >> (258 % 256) = 1 >> 2 = 4 +} + +#[test] +fn test_shr_asm_greater_than_256() { + let bin_path = make_bin_path_asm("shr_greater_than_256"); + let r1 = TaggedValue::new_raw_integer(U256::from(16)); + let r2 = TaggedValue::new_raw_integer(U256::from(258)); // Shift amount greater than 256 + let mut registers: [TaggedValue; 15] = [TaggedValue::default(); 15]; + registers[0] = r1; + registers[1] = r2; + let vm_with_custom_flags = VMStateBuilder::new().with_registers(registers).build(); + + let (_, vm) = run_program_with_custom_state(&bin_path, vm_with_custom_flags); + let result = vm.get_register(3); + + assert_eq!(result.value, U256::from(4)); // 16 >> (258 % 256) = 16 >> 2 = 4 +} + +#[test] +fn test_rol_asm_greater_than_256() { + let bin_path = make_bin_path_asm("rol_greater_than_256"); + let r1 = TaggedValue::new_raw_integer(U256::from(1)); + let r2 = TaggedValue::new_raw_integer(U256::from(258)); // Shift amount greater than 256 + let mut registers: [TaggedValue; 15] = [TaggedValue::default(); 15]; + registers[0] = r1; + registers[1] = r2; + let vm_with_custom_flags = VMStateBuilder::new().with_registers(registers).build(); + + let (_, vm) = run_program_with_custom_state(&bin_path, vm_with_custom_flags); + let result = vm.get_register(3); + + assert_eq!(result.value, U256::from(4)); // 1 rol 258 % 256 = 1 rol 2 = 4 +} + +#[test] +fn test_ror_asm_greater_than_256() { + let bin_path = make_bin_path_asm("ror_greater_than_256"); + let r1 = TaggedValue::new_raw_integer(U256::from(16)); + let r2 = TaggedValue::new_raw_integer(U256::from(258)); // Shift amount greater than 256 + let mut registers: [TaggedValue; 15] = [TaggedValue::default(); 15]; + registers[0] = r1; + registers[1] = r2; + let vm_with_custom_flags = VMStateBuilder::new().with_registers(registers).build(); + + let (_, vm) = run_program_with_custom_state(&bin_path, vm_with_custom_flags); + let result = vm.get_register(3); + + assert_eq!(result.value, U256::from(4)); // 16 ror 258 % 256 = 16 ror 2 = 4 +}