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 assert!(x <= u8::MAX as u32, "Gas cost doesn't fit into 8 bits");
143 x as u8
144 }
145 }
146 }
147
148 pub(crate) fn get_static_gas_cost(&self) -> u32 {
149 match self.static_gas_cost {
150 1 => L1_MESSAGE_COST,
151 2 => SSTORE_COST,
152 3 => SLOAD_COST,
153 4 => INVALID_INSTRUCTION_COST,
154 x => x.into(),
155 }
156 }
157
158 pub(crate) fn predicate(&self) -> Predicate {
159 unsafe { std::mem::transmute(self.predicate_and_mode_requirements >> 2) }
160 }
161
162 pub(crate) fn mode_requirements(&self) -> ModeRequirements {
163 ModeRequirements(self.predicate_and_mode_requirements & 0b11)
164 }
165
166 pub(crate) fn write_source(mut self, sw: &impl SourceWriter) -> Self {
167 sw.write_source(&mut self);
168 self
169 }
170
171 pub(crate) fn write_destination(mut self, sw: &impl DestinationWriter) -> Self {
172 sw.write_destination(&mut self);
173 self
174 }
175
176 pub(crate) fn dst1_register(&self) -> Register {
179 self.destination_registers.register2()
180 }
181}
182
183#[derive(Debug, Clone, Copy)]
188#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
189pub struct Register1(pub Register);
190
191#[derive(Debug, Clone, Copy)]
193#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
194pub struct Register2(pub Register);
195
196impl Source for Register1 {
197 fn get_with_pointer_flag(args: &Arguments, state: &mut impl Addressable) -> (U256, bool) {
198 let register = args.source_registers.register1();
199 (register.value(state), register.pointer_flag(state))
200 }
201}
202
203impl SourceWriter for Register1 {
204 fn write_source(&self, args: &mut Arguments) {
205 args.source_registers.set_register1(self.0);
206 }
207}
208
209impl Source for Register2 {
210 fn get_with_pointer_flag(args: &Arguments, state: &mut impl Addressable) -> (U256, bool) {
211 let register = args.source_registers.register2();
212 (register.value(state), register.pointer_flag(state))
213 }
214}
215
216impl SourceWriter for Register2 {
217 fn write_source(&self, args: &mut Arguments) {
218 args.source_registers.set_register2(self.0);
219 }
220}
221
222impl Destination for Register1 {
223 fn set(args: &Arguments, state: &mut impl Addressable, value: U256) {
224 args.destination_registers.register1().set(state, value);
225 }
226
227 fn set_fat_ptr(args: &Arguments, state: &mut impl Addressable, value: U256) {
228 args.destination_registers.register1().set_ptr(state, value);
229 }
230}
231
232impl DestinationWriter for Register1 {
233 fn write_destination(&self, args: &mut Arguments) {
234 args.destination_registers.set_register1(self.0);
235 }
236}
237
238impl Destination for Register2 {
239 fn set(args: &Arguments, state: &mut impl Addressable, value: U256) {
240 args.destination_registers.register2().set(state, value);
241 state.mark_dst1_written();
242 }
243
244 fn set_fat_ptr(args: &Arguments, state: &mut impl Addressable, value: U256) {
245 args.destination_registers.register2().set_ptr(state, value);
246 state.mark_dst1_written();
247 }
248}
249
250impl DestinationWriter for Register2 {
251 fn write_destination(&self, args: &mut Arguments) {
252 args.destination_registers.set_register2(self.0);
253 }
254}
255
256#[derive(Debug, Clone, Copy)]
258#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
259pub struct Immediate1(pub u16);
260
261#[derive(Debug, Clone, Copy)]
263#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
264pub struct Immediate2(pub u16);
265
266impl Immediate1 {
267 pub(crate) fn get_u16(args: &Arguments) -> u16 {
268 args.immediate1
269 }
270}
271
272impl Immediate2 {
273 pub(crate) fn get_u16(args: &Arguments) -> u16 {
274 args.immediate2
275 }
276}
277
278impl Source for Immediate1 {
279 fn get(args: &Arguments, _state: &mut impl Addressable) -> U256 {
280 U256([args.immediate1.into(), 0, 0, 0])
281 }
282}
283
284impl SourceWriter for Immediate1 {
285 fn write_source(&self, args: &mut Arguments) {
286 args.immediate1 = self.0;
287 }
288}
289
290impl Source for Immediate2 {
291 fn get(args: &Arguments, _state: &mut impl Addressable) -> U256 {
292 U256([args.immediate2.into(), 0, 0, 0])
293 }
294}
295
296impl SourceWriter for Immediate2 {
297 fn write_source(&self, args: &mut Arguments) {
298 args.immediate2 = self.0;
299 }
300}
301
302#[derive(Debug, Clone, Copy)]
305#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
306pub struct RegisterAndImmediate {
307 pub immediate: u16,
309 pub register: Register,
311}
312
313trait RegisterPlusImmediate {
316 fn inner(&self) -> &RegisterAndImmediate;
317}
318
319impl<T: RegisterPlusImmediate> SourceWriter for T {
320 fn write_source(&self, args: &mut Arguments) {
321 args.immediate1 = self.inner().immediate;
322 args.source_registers.set_register1(self.inner().register);
323 }
324}
325
326impl<T: RegisterPlusImmediate> DestinationWriter for T {
327 fn write_destination(&self, args: &mut Arguments) {
328 args.immediate2 = self.inner().immediate;
329 args.destination_registers
330 .set_register1(self.inner().register);
331 }
332}
333
334trait StackAddressing {
335 fn address_for_get(args: &Arguments, state: &mut impl Addressable) -> u16;
336 fn address_for_set(args: &Arguments, state: &mut impl Addressable) -> u16;
337}
338
339impl<T: StackAddressing> Source for T {
340 fn get_with_pointer_flag(args: &Arguments, state: &mut impl Addressable) -> (U256, bool) {
341 let address = Self::address_for_get(args, state);
342 (
343 state.read_stack(address),
344 state.read_stack_pointer_flag(address),
345 )
346 }
347}
348
349impl<T: StackAddressing> Destination for T {
350 fn set(args: &Arguments, state: &mut impl Addressable, value: U256) {
351 let address = Self::address_for_set(args, state);
352 state.write_stack(address, value);
353 state.clear_stack_pointer_flag(address);
354 }
355
356 fn set_fat_ptr(args: &Arguments, state: &mut impl Addressable, value: U256) {
357 let address = Self::address_for_set(args, state);
358 state.write_stack(address, value);
359 state.set_stack_pointer_flag(address);
360 }
361}
362
363fn source_stack_address(args: &Arguments, state: &mut impl Addressable) -> u16 {
364 compute_stack_address(state, args.source_registers.register1(), args.immediate1)
365}
366
367pub(crate) fn destination_stack_address(args: &Arguments, state: &mut impl Addressable) -> u16 {
368 compute_stack_address(
369 state,
370 args.destination_registers.register1(),
371 args.immediate2,
372 )
373}
374
375#[allow(clippy::cast_possible_truncation)]
378fn compute_stack_address(state: &mut impl Addressable, register: Register, immediate: u16) -> u16 {
379 (register.value(state).low_u32() as u16).wrapping_add(immediate)
380}
381
382#[derive(Debug, Clone, Copy)]
384#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
385pub struct AbsoluteStack(pub RegisterAndImmediate);
386
387impl RegisterPlusImmediate for AbsoluteStack {
388 fn inner(&self) -> &RegisterAndImmediate {
389 &self.0
390 }
391}
392
393impl StackAddressing for AbsoluteStack {
394 fn address_for_get(args: &Arguments, state: &mut impl Addressable) -> u16 {
395 source_stack_address(args, state)
396 }
397
398 fn address_for_set(args: &Arguments, state: &mut impl Addressable) -> u16 {
399 destination_stack_address(args, state)
400 }
401}
402
403#[derive(Debug, Clone, Copy)]
405#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
406pub struct RelativeStack(pub RegisterAndImmediate);
407
408impl RegisterPlusImmediate for RelativeStack {
409 fn inner(&self) -> &RegisterAndImmediate {
410 &self.0
411 }
412}
413
414impl StackAddressing for RelativeStack {
415 fn address_for_get(args: &Arguments, state: &mut impl Addressable) -> u16 {
416 state
417 .stack_pointer()
418 .wrapping_sub(source_stack_address(args, state))
419 }
420
421 fn address_for_set(args: &Arguments, state: &mut impl Addressable) -> u16 {
422 state
423 .stack_pointer()
424 .wrapping_sub(destination_stack_address(args, state))
425 }
426}
427
428#[derive(Debug, Clone, Copy)]
431#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
432pub struct AdvanceStackPointer(pub RegisterAndImmediate);
433
434impl RegisterPlusImmediate for AdvanceStackPointer {
435 fn inner(&self) -> &RegisterAndImmediate {
436 &self.0
437 }
438}
439
440impl StackAddressing for AdvanceStackPointer {
441 fn address_for_get(args: &Arguments, state: &mut impl Addressable) -> u16 {
442 let offset = source_stack_address(args, state);
443 let sp = state.stack_pointer();
444 *sp = sp.wrapping_sub(offset);
445 *sp
446 }
447
448 fn address_for_set(args: &Arguments, state: &mut impl Addressable) -> u16 {
449 let offset = destination_stack_address(args, state);
450 let sp = state.stack_pointer();
451 let address_to_set = *sp;
452 *sp = sp.wrapping_add(offset);
453 address_to_set
454 }
455}
456
457#[derive(Debug, Clone, Copy)]
459#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
460pub struct CodePage(pub RegisterAndImmediate);
461
462impl RegisterPlusImmediate for CodePage {
463 fn inner(&self) -> &RegisterAndImmediate {
464 &self.0
465 }
466}
467
468impl Source for CodePage {
469 fn get(args: &Arguments, state: &mut impl Addressable) -> U256 {
470 let address = source_stack_address(args, state);
471 state
472 .code_page()
473 .get(address as usize)
474 .copied()
475 .unwrap_or(U256::zero())
476 }
477}
478
479#[derive(Debug, Clone, Copy)]
481pub struct Register(u8);
482
483impl Register {
484 pub const fn new(n: u8) -> Self {
490 assert!(n < 16, "EraVM has 16 registers");
491 Self(n)
492 }
493
494 fn value(self, state: &mut impl Addressable) -> U256 {
495 unsafe { *state.registers().get_unchecked(self.0 as usize) }
496 }
497
498 fn pointer_flag(self, state: &mut impl Addressable) -> bool {
499 *state.register_pointer_flags() & (1 << self.0) != 0
500 }
501
502 fn set(self, state: &mut impl Addressable, value: U256) {
503 if self.0 != 0 {
504 unsafe { *state.registers().get_unchecked_mut(self.0 as usize) = value };
505 *state.register_pointer_flags() &= !(1 << self.0);
506 }
507 }
508
509 fn set_ptr(self, state: &mut impl Addressable, value: U256) {
510 if self.0 != 0 {
511 unsafe { *state.registers().get_unchecked_mut(self.0 as usize) = value };
512 *state.register_pointer_flags() |= 1 << self.0;
513 }
514 }
515
516 pub(crate) fn set_zero(self, state: &mut impl Addressable) {
518 self.set(state, U256::zero());
519 }
520}
521
522#[cfg(feature = "arbitrary")]
523impl<'a> Arbitrary<'a> for Register {
524 #[allow(clippy::cast_possible_truncation)] fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self, arbitrary::Error> {
526 Ok(Register(u.choose_index(16)? as u8))
527 }
528}
529
530#[derive(Hash, Debug)]
531struct PackedRegisters(u8);
532
533impl PackedRegisters {
534 fn register1(&self) -> Register {
535 Register(self.0 >> 4)
536 }
537 fn set_register1(&mut self, value: Register) {
538 self.0 &= 0xf;
539 self.0 |= value.0 << 4;
540 }
541 fn register2(&self) -> Register {
542 Register(self.0 & 0xf)
543 }
544 fn set_register2(&mut self, value: Register) {
545 self.0 &= 0xf0;
546 self.0 |= value.0;
547 }
548}
549
550#[enum_dispatch(SourceWriter)]
552#[derive(Debug, Clone, Copy)]
553#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
554pub enum AnySource {
555 Register1,
557 Immediate1,
559 AbsoluteStack,
561 RelativeStack,
563 AdvanceStackPointer,
565 CodePage,
567}
568
569#[enum_dispatch(SourceWriter)]
571#[derive(Debug, Clone, Copy)]
572#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
573pub enum RegisterOrImmediate {
574 Register1,
576 Immediate1,
578}
579
580#[derive(Debug)]
582pub struct NotRegisterOrImmediate;
583
584impl TryFrom<AnySource> for RegisterOrImmediate {
585 type Error = NotRegisterOrImmediate;
586
587 fn try_from(value: AnySource) -> Result<Self, Self::Error> {
588 match value {
589 AnySource::Register1(r) => Ok(RegisterOrImmediate::Register1(r)),
590 AnySource::Immediate1(r) => Ok(RegisterOrImmediate::Immediate1(r)),
591 _ => Err(NotRegisterOrImmediate),
592 }
593 }
594}
595
596#[enum_dispatch(DestinationWriter)]
598#[derive(Debug, Clone, Copy)]
599#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
600pub enum AnyDestination {
601 Register1,
603 AbsoluteStack,
605 RelativeStack,
607 AdvanceStackPointer,
609}