Skip to main content

zksync_vm2/instruction_handlers/
binop.rs

1use primitive_types::U256;
2use zksync_vm2_interface::{
3    opcodes::{Add, And, Div, Mul, Or, RotateLeft, RotateRight, ShiftLeft, ShiftRight, Sub, Xor},
4    OpcodeType, Tracer,
5};
6
7use super::{
8    common::boilerplate,
9    monomorphization::{
10        match_boolean, match_destination, match_source, monomorphize, parameterize,
11    },
12};
13use crate::{
14    addressing_modes::{
15        AbsoluteStack, Addressable, AdvanceStackPointer, AnyDestination, AnySource, Arguments,
16        CodePage, Destination, DestinationWriter, Immediate1, Register1, Register2, RelativeStack,
17        Source,
18    },
19    instruction::{ExecutionStatus, Instruction},
20    predication::Flags,
21    VirtualMachine, World,
22};
23
24fn binop<T, W, Op, In1, Out, const SWAP: bool, const SET_FLAGS: bool>(
25    vm: &mut VirtualMachine<T, W>,
26    world: &mut W,
27    tracer: &mut T,
28) -> ExecutionStatus
29where
30    T: Tracer,
31    W: World<T>,
32    Op: Binop,
33    In1: Source,
34    Out: Destination,
35{
36    boilerplate::<Op, _, _>(vm, world, tracer, |vm, args| {
37        let a = In1::get(args, &mut vm.state);
38        let b = Register2::get(args, &mut vm.state);
39        let (a, b) = if SWAP { (b, a) } else { (a, b) };
40
41        let (result, out2, flags) = Op::perform(&a, &b);
42        Out::set(args, &mut vm.state, result);
43        out2.write(args, &mut vm.state);
44        if SET_FLAGS {
45            vm.state.flags = flags;
46        }
47    })
48}
49
50pub(crate) trait Binop: OpcodeType {
51    type Out2: SecondOutput;
52    fn perform(a: &U256, b: &U256) -> (U256, Self::Out2, Flags);
53}
54
55impl Binop for Add {
56    #[inline(always)]
57    fn perform(a: &U256, b: &U256) -> (U256, (), Flags) {
58        let (result, overflow) = a.overflowing_add(*b);
59        (
60            result,
61            (),
62            Flags::new(overflow, result.is_zero(), !(overflow || result.is_zero())),
63        )
64    }
65    type Out2 = ();
66}
67
68impl Binop for Sub {
69    #[inline(always)]
70    fn perform(a: &U256, b: &U256) -> (U256, (), Flags) {
71        let (result, overflow) = a.overflowing_sub(*b);
72        (
73            result,
74            (),
75            Flags::new(overflow, result.is_zero(), !(overflow || result.is_zero())),
76        )
77    }
78    type Out2 = ();
79}
80
81impl Binop for And {
82    #[inline(always)]
83    fn perform(a: &U256, b: &U256) -> (U256, (), Flags) {
84        let result = *a & *b;
85        (result, (), Flags::new(false, result.is_zero(), false))
86    }
87    type Out2 = ();
88}
89
90impl Binop for Or {
91    #[inline(always)]
92    fn perform(a: &U256, b: &U256) -> (U256, (), Flags) {
93        let result = *a | *b;
94        (result, (), Flags::new(false, result.is_zero(), false))
95    }
96    type Out2 = ();
97}
98
99impl Binop for Xor {
100    #[inline(always)]
101    fn perform(a: &U256, b: &U256) -> (U256, (), Flags) {
102        let result = *a ^ *b;
103        (result, (), Flags::new(false, result.is_zero(), false))
104    }
105    type Out2 = ();
106}
107
108impl Binop for ShiftLeft {
109    #[inline(always)]
110    fn perform(a: &U256, b: &U256) -> (U256, (), Flags) {
111        let result = *a << (b.low_u32() % 256);
112        (result, (), Flags::new(false, result.is_zero(), false))
113    }
114    type Out2 = ();
115}
116
117impl Binop for ShiftRight {
118    #[inline(always)]
119    fn perform(a: &U256, b: &U256) -> (U256, (), Flags) {
120        let result = *a >> (b.low_u32() % 256);
121        (result, (), Flags::new(false, result.is_zero(), false))
122    }
123    type Out2 = ();
124}
125
126impl Binop for RotateLeft {
127    #[inline(always)]
128    fn perform(a: &U256, b: &U256) -> (U256, (), Flags) {
129        let shift = b.low_u32() % 256;
130        let result = (*a << shift) | (*a >> (256 - shift));
131        (result, (), Flags::new(false, result.is_zero(), false))
132    }
133    type Out2 = ();
134}
135
136impl Binop for RotateRight {
137    #[inline(always)]
138    fn perform(a: &U256, b: &U256) -> (U256, (), Flags) {
139        let shift = b.low_u32() % 256;
140        let result = (*a >> shift) | (*a << (256 - shift));
141        (result, (), Flags::new(false, result.is_zero(), false))
142    }
143    type Out2 = ();
144}
145
146/// Second output of a binary operation.
147pub(crate) trait SecondOutput {
148    type Destination: DestinationWriter;
149    fn write(self, args: &Arguments, state: &mut impl Addressable);
150}
151
152impl SecondOutput for () {
153    type Destination = ();
154    fn write(self, _: &Arguments, _: &mut impl Addressable) {}
155}
156
157impl DestinationWriter for () {
158    fn write_destination(&self, _: &mut Arguments) {}
159}
160
161impl SecondOutput for U256 {
162    type Destination = Register2;
163    fn write(self, args: &Arguments, state: &mut impl Addressable) {
164        Self::Destination::set(args, state, self);
165    }
166}
167
168impl Binop for Mul {
169    fn perform(a: &U256, b: &U256) -> (U256, Self::Out2, Flags) {
170        let res = a.full_mul(*b);
171        let (low_slice, high_slice) = res.0.split_at(4);
172
173        let mut low_arr = [0; 4];
174        low_arr.copy_from_slice(low_slice);
175        let low = U256(low_arr);
176
177        let mut high_arr = [0; 4];
178        high_arr.copy_from_slice(high_slice);
179        let high = U256(high_arr);
180
181        (
182            low,
183            high,
184            Flags::new(
185                !high.is_zero(),
186                low.is_zero(),
187                high.is_zero() && !low.is_zero(),
188            ),
189        )
190    }
191    type Out2 = U256;
192}
193
194impl Binop for Div {
195    fn perform(a: &U256, b: &U256) -> (U256, Self::Out2, Flags) {
196        if b.is_zero() {
197            (U256::zero(), U256::zero(), Flags::new(true, false, false))
198        } else {
199            let (quotient, remainder) = a.div_mod(*b);
200            (
201                quotient,
202                remainder,
203                Flags::new(false, quotient.is_zero(), remainder.is_zero()),
204            )
205        }
206    }
207    type Out2 = U256;
208}
209
210macro_rules! from_binop {
211    ($name:ident <$binop:ty>) => {
212        #[doc = concat!("Creates [`", stringify!($binop), "`] instruction with the provided params.")]
213        pub fn $name(
214            src1: AnySource,
215            src2: Register2,
216            out: AnyDestination,
217            arguments: Arguments,
218            swap: bool,
219            set_flags: bool,
220        ) -> Self {
221            Self::from_binop::<$binop>(src1, src2, out, &(), arguments, swap, set_flags)
222        }
223    };
224
225    ($name:ident <$binop:ty, $out2: ty>) => {
226        #[doc = concat!("Creates [`", stringify!($binop), "`] instruction with the provided params.")]
227        pub fn $name(
228            src1: AnySource,
229            src2: Register2,
230            out: AnyDestination,
231            out2: $out2,
232            arguments: Arguments,
233            swap: bool,
234            set_flags: bool,
235        ) -> Self {
236            Self::from_binop::<$binop>(src1, src2, out, &out2, arguments, swap, set_flags)
237        }
238    };
239}
240
241/// Instructions for binary operations.
242impl<T: Tracer, W: World<T>> Instruction<T, W> {
243    pub(crate) fn from_binop<Op: Binop>(
244        src1: AnySource,
245        src2: Register2,
246        out: AnyDestination,
247        out2: &<Op::Out2 as SecondOutput>::Destination,
248        arguments: Arguments,
249        swap: bool,
250        set_flags: bool,
251    ) -> Self {
252        Self {
253            handler: monomorphize!(binop [T W Op] match_source src1 match_destination out match_boolean swap match_boolean set_flags),
254            arguments: arguments
255                .write_source(&src1)
256                .write_source(&src2)
257                .write_destination(&out)
258                .write_destination(out2),
259        }
260    }
261
262    from_binop!(from_add<Add>);
263    from_binop!(from_sub<Sub>);
264    from_binop!(from_and<And>);
265    from_binop!(from_or<Or>);
266    from_binop!(from_xor<Xor>);
267    from_binop!(from_shift_left<ShiftLeft>);
268    from_binop!(from_shift_right<ShiftRight>);
269    from_binop!(from_rotate_left<RotateLeft>);
270    from_binop!(from_rotate_right<RotateRight>);
271
272    from_binop!(from_mul <Mul, Register2>);
273    from_binop!(from_div <Div, Register2>);
274}