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
146pub(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
241impl<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}