1#[cfg(feature = "arbitrary")]
4use arbitrary::{Arbitrary, Unstructured};
5use enum_dispatch::enum_dispatch;
6use primitive_types::U256;
7use zkevm_opcode_defs::erase_fat_pointer_metadata;
8
9use crate::{mode_requirements::ModeRequirements, predication::Predicate};
10
11pub(crate) trait Source {
12 fn get(args: &Arguments, state: &mut impl Addressable) -> U256 {
14 Self::get_with_pointer_flag_and_erasing(args, state).0
15 }
16
17 fn get_with_pointer_flag(args: &Arguments, state: &mut impl Addressable) -> (U256, bool) {
19 (Self::get(args, state), false)
20 }
21
22 fn get_with_pointer_flag_and_erasing(
27 args: &Arguments,
28 state: &mut impl Addressable,
29 ) -> (U256, bool) {
30 let (mut value, is_pointer) = Self::get_with_pointer_flag(args, state);
31 if is_pointer && !state.in_kernel_mode() {
32 erase_fat_pointer_metadata(&mut value);
33 }
34 (value, is_pointer && state.in_kernel_mode())
35 }
36}
37
38pub(crate) trait Destination {
39 fn set(args: &Arguments, state: &mut impl Addressable, value: U256);
41
42 fn set_fat_ptr(args: &Arguments, state: &mut impl Addressable, value: U256);
44}
45
46pub(crate) trait Addressable {
48 fn registers(&mut self) -> &mut [U256; 16];
49 fn register_pointer_flags(&mut self) -> &mut u16;
50
51 fn read_stack(&mut self, slot: u16) -> U256;
52 fn write_stack(&mut self, slot: u16, value: U256);
53 fn stack_pointer(&mut self) -> &mut u16;
54
55 fn read_stack_pointer_flag(&mut self, slot: u16) -> bool;
56 fn set_stack_pointer_flag(&mut self, slot: u16);
57 fn clear_stack_pointer_flag(&mut self, slot: u16);
58
59 fn mark_dst1_written(&mut self);
62
63 fn code_page(&self) -> &[U256];
64
65 fn in_kernel_mode(&self) -> bool;
66}
67
68#[enum_dispatch]
69pub(crate) trait SourceWriter {
70 fn write_source(&self, args: &mut Arguments);
71}
72
73impl<T: SourceWriter> SourceWriter for Option<T> {
74 fn write_source(&self, args: &mut Arguments) {
75 if let Some(x) = self {
76 x.write_source(args);
77 }
78 }
79}
80
81#[enum_dispatch]
82pub(crate) trait DestinationWriter {
83 fn write_destination(&self, args: &mut Arguments);
84}
85
86impl<T: DestinationWriter> DestinationWriter for Option<T> {
87 fn write_destination(&self, args: &mut Arguments) {
88 if let Some(x) = self {
89 x.write_destination(args);
90 }
91 }
92}
93
94#[derive(Debug)]
97pub struct Arguments {
98 source_registers: PackedRegisters,
99 destination_registers: PackedRegisters,
100 immediate1: u16,
101 immediate2: u16,
102 predicate_and_mode_requirements: u8,
103 static_gas_cost: u8,
104}
105
106pub(crate) const L1_MESSAGE_COST: u32 = 156_250;
107pub(crate) const SSTORE_COST: u32 = 5_511;
108pub(crate) const SLOAD_COST: u32 = 2_008;
109pub(crate) const INVALID_INSTRUCTION_COST: u32 = 4_294_967_295;
110
111impl Arguments {
112 #[allow(clippy::missing_panics_doc)] pub const fn new(
115 predicate: Predicate,
116 gas_cost: u32,
117 mode_requirements: ModeRequirements,
118 ) -> Self {
119 assert!(predicate as u8 & (0b11 << 6) == 0);
121 assert!(mode_requirements.0 & !0b11 == 0);
122
123 Self {
124 source_registers: PackedRegisters(0),
125 destination_registers: PackedRegisters(0),
126 immediate1: 0,
127 immediate2: 0,
128 predicate_and_mode_requirements: ((predicate as u8) << 2) | mode_requirements.0,
129 static_gas_cost: Self::encode_static_gas_cost(gas_cost),
130 }
131 }
132
133 #[allow(clippy::cast_possible_truncation)] const fn encode_static_gas_cost(x: u32) -> u8 {
135 match x {
136 L1_MESSAGE_COST => 1,
137 SSTORE_COST => 2,
138 SLOAD_COST => 3,
139 INVALID_INSTRUCTION_COST => 4,
140 1..=4 => panic!("Reserved gas cost values overlap with actual gas costs"),
141 x => {
142 if x > u8::MAX as u32 {
143 panic!("Gas cost doesn't fit into 8 bits");
144 } else {
145 x as u8
146 }
147 }
148 }
149 }
150
151 pub(crate) fn get_static_gas_cost(&self) -> u32 {
152 match self.static_gas_cost {
153 1 => L1_MESSAGE_COST,
154 2 => SSTORE_COST,
155 3 => SLOAD_COST,
156 4 => INVALID_INSTRUCTION_COST,
157 x => x.into(),
158 }
159 }
160
161 pub(crate) fn predicate(&self) -> Predicate {
162 unsafe { std::mem::transmute(self.predicate_and_mode_requirements >> 2) }
163 }
164
165 pub(crate) fn mode_requirements(&self) -> ModeRequirements {
166 ModeRequirements(self.predicate_and_mode_requirements & 0b11)
167 }
168
169 pub(crate) fn write_source(mut self, sw: &impl SourceWriter) -> Self {
170 sw.write_source(&mut self);
171 self
172 }
173
174 pub(crate) fn write_destination(mut self, sw: &impl DestinationWriter) -> Self {
175 sw.write_destination(&mut self);
176 self
177 }
178
179 pub(crate) fn dst1_register(&self) -> Register {
182 self.destination_registers.register2()
183 }
184}
185
186#[derive(Debug, Clone, Copy)]
191#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
192pub struct Register1(pub Register);
193
194#[derive(Debug, Clone, Copy)]
196#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
197pub struct Register2(pub Register);
198
199impl Source for Register1 {
200 fn get_with_pointer_flag(args: &Arguments, state: &mut impl Addressable) -> (U256, bool) {
201 let register = args.source_registers.register1();
202 (register.value(state), register.pointer_flag(state))
203 }
204}
205
206impl SourceWriter for Register1 {
207 fn write_source(&self, args: &mut Arguments) {
208 args.source_registers.set_register1(self.0);
209 }
210}
211
212impl Source for Register2 {
213 fn get_with_pointer_flag(args: &Arguments, state: &mut impl Addressable) -> (U256, bool) {
214 let register = args.source_registers.register2();
215 (register.value(state), register.pointer_flag(state))
216 }
217}
218
219impl SourceWriter for Register2 {
220 fn write_source(&self, args: &mut Arguments) {
221 args.source_registers.set_register2(self.0);
222 }
223}
224
225impl Destination for Register1 {
226 fn set(args: &Arguments, state: &mut impl Addressable, value: U256) {
227 args.destination_registers.register1().set(state, value);
228 }
229
230 fn set_fat_ptr(args: &Arguments, state: &mut impl Addressable, value: U256) {
231 args.destination_registers.register1().set_ptr(state, value);
232 }
233}
234
235impl DestinationWriter for Register1 {
236 fn write_destination(&self, args: &mut Arguments) {
237 args.destination_registers.set_register1(self.0);
238 }
239}
240
241impl Destination for Register2 {
242 fn set(args: &Arguments, state: &mut impl Addressable, value: U256) {
243 args.destination_registers.register2().set(state, value);
244 state.mark_dst1_written();
245 }
246
247 fn set_fat_ptr(args: &Arguments, state: &mut impl Addressable, value: U256) {
248 args.destination_registers.register2().set_ptr(state, value);
249 state.mark_dst1_written();
250 }
251}
252
253impl DestinationWriter for Register2 {
254 fn write_destination(&self, args: &mut Arguments) {
255 args.destination_registers.set_register2(self.0);
256 }
257}
258
259#[derive(Debug, Clone, Copy)]
261#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
262pub struct Immediate1(pub u16);
263
264#[derive(Debug, Clone, Copy)]
266#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
267pub struct Immediate2(pub u16);
268
269impl Immediate1 {
270 pub(crate) fn get_u16(args: &Arguments) -> u16 {
271 args.immediate1
272 }
273}
274
275impl Immediate2 {
276 pub(crate) fn get_u16(args: &Arguments) -> u16 {
277 args.immediate2
278 }
279}
280
281impl Source for Immediate1 {
282 fn get(args: &Arguments, _state: &mut impl Addressable) -> U256 {
283 U256([args.immediate1.into(), 0, 0, 0])
284 }
285}
286
287impl SourceWriter for Immediate1 {
288 fn write_source(&self, args: &mut Arguments) {
289 args.immediate1 = self.0;
290 }
291}
292
293impl Source for Immediate2 {
294 fn get(args: &Arguments, _state: &mut impl Addressable) -> U256 {
295 U256([args.immediate2.into(), 0, 0, 0])
296 }
297}
298
299impl SourceWriter for Immediate2 {
300 fn write_source(&self, args: &mut Arguments) {
301 args.immediate2 = self.0;
302 }
303}
304
305#[derive(Debug, Clone, Copy)]
308#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
309pub struct RegisterAndImmediate {
310 pub immediate: u16,
312 pub register: Register,
314}
315
316trait RegisterPlusImmediate {
319 fn inner(&self) -> &RegisterAndImmediate;
320}
321
322impl<T: RegisterPlusImmediate> SourceWriter for T {
323 fn write_source(&self, args: &mut Arguments) {
324 args.immediate1 = self.inner().immediate;
325 args.source_registers.set_register1(self.inner().register);
326 }
327}
328
329impl<T: RegisterPlusImmediate> DestinationWriter for T {
330 fn write_destination(&self, args: &mut Arguments) {
331 args.immediate2 = self.inner().immediate;
332 args.destination_registers
333 .set_register1(self.inner().register);
334 }
335}
336
337trait StackAddressing {
338 fn address_for_get(args: &Arguments, state: &mut impl Addressable) -> u16;
339 fn address_for_set(args: &Arguments, state: &mut impl Addressable) -> u16;
340}
341
342impl<T: StackAddressing> Source for T {
343 fn get_with_pointer_flag(args: &Arguments, state: &mut impl Addressable) -> (U256, bool) {
344 let address = Self::address_for_get(args, state);
345 (
346 state.read_stack(address),
347 state.read_stack_pointer_flag(address),
348 )
349 }
350}
351
352impl<T: StackAddressing> Destination for T {
353 fn set(args: &Arguments, state: &mut impl Addressable, value: U256) {
354 let address = Self::address_for_set(args, state);
355 state.write_stack(address, value);
356 state.clear_stack_pointer_flag(address);
357 }
358
359 fn set_fat_ptr(args: &Arguments, state: &mut impl Addressable, value: U256) {
360 let address = Self::address_for_set(args, state);
361 state.write_stack(address, value);
362 state.set_stack_pointer_flag(address);
363 }
364}
365
366fn source_stack_address(args: &Arguments, state: &mut impl Addressable) -> u16 {
367 compute_stack_address(state, args.source_registers.register1(), args.immediate1)
368}
369
370pub(crate) fn destination_stack_address(args: &Arguments, state: &mut impl Addressable) -> u16 {
371 compute_stack_address(
372 state,
373 args.destination_registers.register1(),
374 args.immediate2,
375 )
376}
377
378#[allow(clippy::cast_possible_truncation)]
381fn compute_stack_address(state: &mut impl Addressable, register: Register, immediate: u16) -> u16 {
382 (register.value(state).low_u32() as u16).wrapping_add(immediate)
383}
384
385#[derive(Debug, Clone, Copy)]
387#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
388pub struct AbsoluteStack(pub RegisterAndImmediate);
389
390impl RegisterPlusImmediate for AbsoluteStack {
391 fn inner(&self) -> &RegisterAndImmediate {
392 &self.0
393 }
394}
395
396impl StackAddressing for AbsoluteStack {
397 fn address_for_get(args: &Arguments, state: &mut impl Addressable) -> u16 {
398 source_stack_address(args, state)
399 }
400
401 fn address_for_set(args: &Arguments, state: &mut impl Addressable) -> u16 {
402 destination_stack_address(args, state)
403 }
404}
405
406#[derive(Debug, Clone, Copy)]
408#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
409pub struct RelativeStack(pub RegisterAndImmediate);
410
411impl RegisterPlusImmediate for RelativeStack {
412 fn inner(&self) -> &RegisterAndImmediate {
413 &self.0
414 }
415}
416
417impl StackAddressing for RelativeStack {
418 fn address_for_get(args: &Arguments, state: &mut impl Addressable) -> u16 {
419 state
420 .stack_pointer()
421 .wrapping_sub(source_stack_address(args, state))
422 }
423
424 fn address_for_set(args: &Arguments, state: &mut impl Addressable) -> u16 {
425 state
426 .stack_pointer()
427 .wrapping_sub(destination_stack_address(args, state))
428 }
429}
430
431#[derive(Debug, Clone, Copy)]
434#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
435pub struct AdvanceStackPointer(pub RegisterAndImmediate);
436
437impl RegisterPlusImmediate for AdvanceStackPointer {
438 fn inner(&self) -> &RegisterAndImmediate {
439 &self.0
440 }
441}
442
443impl StackAddressing for AdvanceStackPointer {
444 fn address_for_get(args: &Arguments, state: &mut impl Addressable) -> u16 {
445 let offset = source_stack_address(args, state);
446 let sp = state.stack_pointer();
447 *sp = sp.wrapping_sub(offset);
448 *sp
449 }
450
451 fn address_for_set(args: &Arguments, state: &mut impl Addressable) -> u16 {
452 let offset = destination_stack_address(args, state);
453 let sp = state.stack_pointer();
454 let address_to_set = *sp;
455 *sp = sp.wrapping_add(offset);
456 address_to_set
457 }
458}
459
460#[derive(Debug, Clone, Copy)]
462#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
463pub struct CodePage(pub RegisterAndImmediate);
464
465impl RegisterPlusImmediate for CodePage {
466 fn inner(&self) -> &RegisterAndImmediate {
467 &self.0
468 }
469}
470
471impl Source for CodePage {
472 fn get(args: &Arguments, state: &mut impl Addressable) -> U256 {
473 let address = source_stack_address(args, state);
474 state
475 .code_page()
476 .get(address as usize)
477 .copied()
478 .unwrap_or(U256::zero())
479 }
480}
481
482#[derive(Debug, Clone, Copy)]
484pub struct Register(u8);
485
486impl Register {
487 pub const fn new(n: u8) -> Self {
493 assert!(n < 16, "EraVM has 16 registers");
494 Self(n)
495 }
496
497 fn value(self, state: &mut impl Addressable) -> U256 {
498 unsafe { *state.registers().get_unchecked(self.0 as usize) }
499 }
500
501 fn pointer_flag(self, state: &mut impl Addressable) -> bool {
502 *state.register_pointer_flags() & (1 << self.0) != 0
503 }
504
505 fn set(self, state: &mut impl Addressable, value: U256) {
506 if self.0 != 0 {
507 unsafe { *state.registers().get_unchecked_mut(self.0 as usize) = value };
508 *state.register_pointer_flags() &= !(1 << self.0);
509 }
510 }
511
512 fn set_ptr(self, state: &mut impl Addressable, value: U256) {
513 if self.0 != 0 {
514 unsafe { *state.registers().get_unchecked_mut(self.0 as usize) = value };
515 *state.register_pointer_flags() |= 1 << self.0;
516 }
517 }
518
519 pub(crate) fn set_zero(self, state: &mut impl Addressable) {
521 self.set(state, U256::zero());
522 }
523}
524
525#[cfg(feature = "arbitrary")]
526impl<'a> Arbitrary<'a> for Register {
527 #[allow(clippy::cast_possible_truncation)] fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self, arbitrary::Error> {
529 Ok(Register(u.choose_index(16)? as u8))
530 }
531}
532
533#[derive(Hash, Debug)]
534struct PackedRegisters(u8);
535
536impl PackedRegisters {
537 fn register1(&self) -> Register {
538 Register(self.0 >> 4)
539 }
540 fn set_register1(&mut self, value: Register) {
541 self.0 &= 0xf;
542 self.0 |= value.0 << 4;
543 }
544 fn register2(&self) -> Register {
545 Register(self.0 & 0xf)
546 }
547 fn set_register2(&mut self, value: Register) {
548 self.0 &= 0xf0;
549 self.0 |= value.0;
550 }
551}
552
553#[enum_dispatch(SourceWriter)]
555#[derive(Debug, Clone, Copy)]
556#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
557pub enum AnySource {
558 Register1,
560 Immediate1,
562 AbsoluteStack,
564 RelativeStack,
566 AdvanceStackPointer,
568 CodePage,
570}
571
572#[enum_dispatch(SourceWriter)]
574#[derive(Debug, Clone, Copy)]
575#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
576pub enum RegisterOrImmediate {
577 Register1,
579 Immediate1,
581}
582
583#[derive(Debug)]
585pub struct NotRegisterOrImmediate;
586
587impl TryFrom<AnySource> for RegisterOrImmediate {
588 type Error = NotRegisterOrImmediate;
589
590 fn try_from(value: AnySource) -> Result<Self, Self::Error> {
591 match value {
592 AnySource::Register1(r) => Ok(RegisterOrImmediate::Register1(r)),
593 AnySource::Immediate1(r) => Ok(RegisterOrImmediate::Immediate1(r)),
594 _ => Err(NotRegisterOrImmediate),
595 }
596 }
597}
598
599#[enum_dispatch(DestinationWriter)]
601#[derive(Debug, Clone, Copy)]
602#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
603pub enum AnyDestination {
604 Register1,
606 AbsoluteStack,
608 RelativeStack,
610 AdvanceStackPointer,
612}