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