Skip to main content

zksync_vm2/instruction_handlers/
pointer.rs

1use 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
126/// Pointer-related instructions.
127impl<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}