zksync_vm2/instruction_handlers/
pointer.rs1use primitive_types::U256;
2use zksync_vm2_interface::{
3 opcodes::{PointerAdd, PointerPack, PointerShrink, PointerSub},
4 OpcodeType, Tracer,
5};
6
7use super::{
8 common::boilerplate,
9 monomorphization::{
10 match_boolean, match_destination, match_source, monomorphize, parameterize,
11 },
12 ret::spontaneous_panic,
13};
14use crate::{
15 addressing_modes::{
16 AbsoluteStack, AdvanceStackPointer, AnyDestination, AnySource, Arguments, CodePage,
17 Destination, Immediate1, Register1, Register2, RelativeStack, Source,
18 },
19 fat_pointer::FatPointer,
20 instruction::ExecutionStatus,
21 Instruction, VirtualMachine, World,
22};
23
24fn ptr<T: Tracer, W: World<T>, Op: PtrOp, In1: Source, Out: Destination, const SWAP: bool>(
25 vm: &mut VirtualMachine<T, W>,
26 world: &mut W,
27 tracer: &mut T,
28) -> ExecutionStatus {
29 boilerplate::<Op, _, _>(vm, world, tracer, |vm, args| {
30 let ((a, a_is_pointer), (b, b_is_pointer)) = if SWAP {
31 (
32 Register2::get_with_pointer_flag(args, &mut vm.state),
33 In1::get_with_pointer_flag_and_erasing(args, &mut vm.state),
34 )
35 } else {
36 (
37 In1::get_with_pointer_flag(args, &mut vm.state),
38 Register2::get_with_pointer_flag_and_erasing(args, &mut vm.state),
39 )
40 };
41
42 if !a_is_pointer || b_is_pointer {
43 vm.state.current_frame.pc = spontaneous_panic();
44 return;
45 }
46
47 let Some(result) = Op::perform(a, b) else {
48 vm.state.current_frame.pc = spontaneous_panic();
49 return;
50 };
51
52 Out::set_fat_ptr(args, &mut vm.state, result);
53 })
54}
55
56pub(crate) trait PtrOp: OpcodeType {
57 fn perform(in1: U256, in2: U256) -> Option<U256>;
58}
59
60impl PtrOp for PointerAdd {
61 #[inline(always)]
62 fn perform(in1: U256, in2: U256) -> Option<U256> {
63 ptr_add_sub::<true>(in1, in2)
64 }
65}
66
67impl PtrOp for PointerSub {
68 #[inline(always)]
69 fn perform(in1: U256, in2: U256) -> Option<U256> {
70 ptr_add_sub::<false>(in1, in2)
71 }
72}
73
74fn ptr_add_sub<const IS_ADD: bool>(mut in1: U256, in2: U256) -> Option<U256> {
75 if in2 > u32::MAX.into() {
76 return None;
77 }
78 let pointer: &mut FatPointer = (&mut in1).into();
79
80 let new_offset = if IS_ADD {
81 pointer.offset.checked_add(in2.low_u32())
82 } else {
83 pointer.offset.checked_sub(in2.low_u32())
84 }?;
85
86 pointer.offset = new_offset;
87
88 Some(in1)
89}
90
91impl PtrOp for PointerPack {
92 #[inline(always)]
93 fn perform(in1: U256, in2: U256) -> Option<U256> {
94 if in2.low_u128() != 0 {
95 None
96 } else {
97 Some(U256([in1.0[0], in1.0[1], in2.0[2], in2.0[3]]))
98 }
99 }
100}
101
102impl PtrOp for PointerShrink {
103 #[inline(always)]
104 fn perform(mut in1: U256, in2: U256) -> Option<U256> {
105 let pointer: &mut FatPointer = (&mut in1).into();
106 pointer.length = pointer.length.checked_sub(in2.low_u32())?;
107 Some(in1)
108 }
109}
110
111macro_rules! from_ptr_op {
112 ($name:ident <$binop:ty>) => {
113 #[doc = concat!("Creates a [`", stringify!($binop), "`] instruction with the provided params.")]
114 pub fn $name(
115 src1: AnySource,
116 src2: Register2,
117 out: AnyDestination,
118 arguments: Arguments,
119 swap: bool,
120 ) -> Self {
121 Self::from_ptr::<$binop>(src1, src2, out, arguments, swap)
122 }
123 };
124}
125
126impl<T: Tracer, W: World<T>> Instruction<T, W> {
128 from_ptr_op!(from_pointer_add<PointerAdd>);
129 from_ptr_op!(from_pointer_sub<PointerSub>);
130 from_ptr_op!(from_pointer_pack<PointerPack>);
131 from_ptr_op!(from_pointer_shrink<PointerShrink>);
132
133 pub(crate) fn from_ptr<Op: PtrOp>(
134 src1: AnySource,
135 src2: Register2,
136 out: AnyDestination,
137 arguments: Arguments,
138 swap: bool,
139 ) -> Self {
140 Self {
141 handler: monomorphize!(ptr [T W Op] match_source src1 match_destination out match_boolean swap),
142 arguments: arguments
143 .write_source(&src1)
144 .write_source(&src2)
145 .write_destination(&out),
146 }
147 }
148}