Skip to main content

zksync_vm2/
decode.rs

1use zkevm_opcode_defs::{
2    decoding::{EncodingModeProduction, VmEncodingMode},
3    ImmMemHandlerFlags, Opcode,
4    Operand::{Full, RegOnly, RegOrImm},
5    RegOrImmFlags, FAR_CALL_SHARD_FLAG_IDX, FAR_CALL_STATIC_FLAG_IDX, FIRST_MESSAGE_FLAG_IDX,
6    RET_TO_LABEL_BIT_IDX, SET_FLAGS_FLAG_IDX, SWAP_OPERANDS_FLAG_IDX_FOR_ARITH_OPCODES,
7    SWAP_OPERANDS_FLAG_IDX_FOR_PTR_OPCODE, UMA_INCREMENT_FLAG_IDX,
8};
9use zksync_vm2_interface::{
10    opcodes::{
11        self, Add, And, Div, Mul, Or, PointerAdd, PointerPack, PointerShrink, PointerSub,
12        RotateLeft, RotateRight, ShiftLeft, ShiftRight, Sub, Xor,
13    },
14    Tracer,
15};
16
17use crate::{
18    addressing_modes::{
19        AbsoluteStack, AdvanceStackPointer, AnyDestination, AnySource, Arguments, CodePage,
20        Immediate1, Immediate2, Register, Register1, Register2, RegisterAndImmediate,
21        RelativeStack,
22    },
23    mode_requirements::ModeRequirements,
24    Instruction, Predicate, World,
25};
26
27#[allow(clippy::too_many_lines)]
28pub(crate) fn decode<T: Tracer, W: World<T>>(raw: u64, is_bootloader: bool) -> Instruction<T, W> {
29    let (parsed, _) = EncodingModeProduction::parse_preliminary_variant_and_absolute_number(raw);
30
31    let predicate = match parsed.condition {
32        zkevm_opcode_defs::Condition::Always => Predicate::Always,
33        zkevm_opcode_defs::Condition::Gt => Predicate::IfGT,
34        zkevm_opcode_defs::Condition::Lt => Predicate::IfLT,
35        zkevm_opcode_defs::Condition::Eq => Predicate::IfEQ,
36        zkevm_opcode_defs::Condition::Ge => Predicate::IfGE,
37        zkevm_opcode_defs::Condition::Le => Predicate::IfLE,
38        zkevm_opcode_defs::Condition::Ne => Predicate::IfNotEQ,
39        zkevm_opcode_defs::Condition::GtOrLt => Predicate::IfGTOrLT,
40    };
41    let arguments = Arguments::new(
42        predicate,
43        parsed.variant.ergs_price(),
44        ModeRequirements::new(
45            parsed.variant.requires_kernel_mode(),
46            !parsed.variant.can_be_used_in_static_context(),
47        ),
48    );
49
50    let stack_in = RegisterAndImmediate {
51        immediate: parsed.imm_0,
52        register: Register::new(parsed.src0_reg_idx),
53    };
54    let src1: AnySource = match parsed.variant.src0_operand_type {
55        RegOnly | RegOrImm(RegOrImmFlags::UseRegOnly) | Full(ImmMemHandlerFlags::UseRegOnly) => {
56            Register1(Register::new(parsed.src0_reg_idx)).into()
57        }
58        RegOrImm(RegOrImmFlags::UseImm16Only) | Full(ImmMemHandlerFlags::UseImm16Only) => {
59            Immediate1(parsed.imm_0).into()
60        }
61        Full(ImmMemHandlerFlags::UseAbsoluteOnStack) => AbsoluteStack(stack_in).into(),
62        Full(ImmMemHandlerFlags::UseStackWithPushPop) => AdvanceStackPointer(stack_in).into(),
63        Full(ImmMemHandlerFlags::UseStackWithOffset) => RelativeStack(stack_in).into(),
64        Full(ImmMemHandlerFlags::UseCodePage) => CodePage(stack_in).into(),
65    };
66
67    let stack_out = RegisterAndImmediate {
68        immediate: parsed.imm_1,
69        register: Register::new(parsed.dst0_reg_idx),
70    };
71    let out: AnyDestination = match parsed.variant.dst0_operand_type {
72        RegOnly | RegOrImm(RegOrImmFlags::UseRegOnly) | Full(ImmMemHandlerFlags::UseRegOnly) => {
73            Register1(Register::new(parsed.dst0_reg_idx)).into()
74        }
75        RegOrImm(RegOrImmFlags::UseImm16Only) | Full(ImmMemHandlerFlags::UseImm16Only) => {
76            panic!("Parser wants to output to immediate")
77        }
78        Full(ImmMemHandlerFlags::UseAbsoluteOnStack) => AbsoluteStack(stack_out).into(),
79        Full(ImmMemHandlerFlags::UseStackWithPushPop) => AdvanceStackPointer(stack_out).into(),
80        Full(ImmMemHandlerFlags::UseStackWithOffset) => RelativeStack(stack_out).into(),
81        Full(ImmMemHandlerFlags::UseCodePage) => panic!("Parser wants to write to code page"),
82    };
83
84    let src2 = Register2(Register::new(parsed.src1_reg_idx));
85    let out2 = Register2(Register::new(parsed.dst1_reg_idx));
86
87    // Store the `dst1` register for every opcode so `full_boilerplate` can clear it when the
88    // instruction writes no second output. Opcodes that do write `dst1` (mul/div and UMA reads
89    // with increment) re-write the same register below, so this is a no-op for them.
90    let arguments = arguments.write_destination(&out2);
91
92    macro_rules! binop {
93        ($op: ident, $snd: tt) => {
94            Instruction::from_binop::<$op>(
95                src1,
96                src2,
97                out,
98                &$snd,
99                arguments,
100                parsed.variant.flags[SWAP_OPERANDS_FLAG_IDX_FOR_ARITH_OPCODES],
101                parsed.variant.flags[SET_FLAGS_FLAG_IDX],
102            )
103        };
104    }
105
106    macro_rules! ptr {
107        ($op: ident) => {
108            Instruction::from_ptr::<$op>(
109                src1,
110                src2,
111                out,
112                arguments,
113                parsed.variant.flags[SWAP_OPERANDS_FLAG_IDX_FOR_PTR_OPCODE],
114            )
115        };
116    }
117
118    match parsed.variant.opcode {
119        Opcode::Add(_) => binop!(Add, ()),
120        Opcode::Sub(_) => binop!(Sub, ()),
121        Opcode::Mul(_) => binop!(Mul, out2),
122        Opcode::Div(_) => binop!(Div, out2),
123        Opcode::Binop(x) => match x {
124            zkevm_opcode_defs::BinopOpcode::Xor => binop!(Xor, ()),
125            zkevm_opcode_defs::BinopOpcode::And => binop!(And, ()),
126            zkevm_opcode_defs::BinopOpcode::Or => binop!(Or, ()),
127        },
128        Opcode::Shift(x) => match x {
129            zkevm_opcode_defs::ShiftOpcode::Shl => binop!(ShiftLeft, ()),
130            zkevm_opcode_defs::ShiftOpcode::Shr => binop!(ShiftRight, ()),
131            zkevm_opcode_defs::ShiftOpcode::Rol => binop!(RotateLeft, ()),
132            zkevm_opcode_defs::ShiftOpcode::Ror => binop!(RotateRight, ()),
133        },
134        Opcode::Jump(_) => Instruction::from_jump(src1, out.try_into().unwrap(), arguments),
135        Opcode::Context(x) => match x {
136            zkevm_opcode_defs::ContextOpcode::This => {
137                Instruction::from_this(out.try_into().unwrap(), arguments)
138            }
139            zkevm_opcode_defs::ContextOpcode::Caller => {
140                Instruction::from_caller(out.try_into().unwrap(), arguments)
141            }
142            zkevm_opcode_defs::ContextOpcode::CodeAddress => {
143                Instruction::from_code_address(out.try_into().unwrap(), arguments)
144            }
145            zkevm_opcode_defs::ContextOpcode::ErgsLeft => {
146                Instruction::from_ergs_left(out.try_into().unwrap(), arguments)
147            }
148            zkevm_opcode_defs::ContextOpcode::GetContextU128 => {
149                Instruction::from_context_u128(out.try_into().unwrap(), arguments)
150            }
151            zkevm_opcode_defs::ContextOpcode::SetContextU128 => {
152                Instruction::from_set_context_u128(src1.try_into().unwrap(), arguments)
153            }
154            zkevm_opcode_defs::ContextOpcode::Sp => {
155                Instruction::from_context_sp(out.try_into().unwrap(), arguments)
156            }
157            zkevm_opcode_defs::ContextOpcode::Meta => {
158                Instruction::from_context_meta(out.try_into().unwrap(), arguments)
159            }
160            zkevm_opcode_defs::ContextOpcode::IncrementTxNumber => {
161                Instruction::from_increment_tx_number(arguments)
162            }
163            zkevm_opcode_defs::ContextOpcode::AuxMutating0 => {
164                Instruction::from_aux_mutating(arguments)
165            }
166        },
167        Opcode::Ptr(x) => match x {
168            zkevm_opcode_defs::PtrOpcode::Add => ptr!(PointerAdd),
169            zkevm_opcode_defs::PtrOpcode::Sub => ptr!(PointerSub),
170            zkevm_opcode_defs::PtrOpcode::Pack => ptr!(PointerPack),
171            zkevm_opcode_defs::PtrOpcode::Shrink => ptr!(PointerShrink),
172        },
173        Opcode::NearCall(_) => Instruction::from_near_call(
174            Register1(Register::new(parsed.src0_reg_idx)),
175            Immediate1(parsed.imm_0),
176            Immediate2(parsed.imm_1),
177            arguments,
178        ),
179        Opcode::FarCall(kind) => {
180            let constructor = match kind {
181                zkevm_opcode_defs::FarCallOpcode::Normal => {
182                    Instruction::from_far_call::<opcodes::Normal>
183                }
184                zkevm_opcode_defs::FarCallOpcode::Delegate => {
185                    Instruction::from_far_call::<opcodes::Delegate>
186                }
187                zkevm_opcode_defs::FarCallOpcode::Mimic => {
188                    Instruction::from_far_call::<opcodes::Mimic>
189                }
190            };
191            constructor(
192                src1.try_into().unwrap(),
193                src2,
194                Immediate1(parsed.imm_0),
195                parsed.variant.flags[FAR_CALL_STATIC_FLAG_IDX],
196                parsed.variant.flags[FAR_CALL_SHARD_FLAG_IDX],
197                arguments,
198            )
199        }
200        Opcode::Ret(kind) => {
201            let to_label = parsed.variant.flags[RET_TO_LABEL_BIT_IDX];
202            let label = if to_label {
203                Some(Immediate1(parsed.imm_0))
204            } else {
205                None
206            };
207            match kind {
208                zkevm_opcode_defs::RetOpcode::Ok => {
209                    Instruction::from_ret(src1.try_into().unwrap(), label, arguments)
210                }
211                zkevm_opcode_defs::RetOpcode::Revert => {
212                    Instruction::from_revert(src1.try_into().unwrap(), label, arguments)
213                }
214                zkevm_opcode_defs::RetOpcode::Panic => {
215                    Instruction::from_panic(src1.try_into().unwrap(), label, arguments)
216                }
217            }
218        }
219        Opcode::Log(x) => match x {
220            zkevm_opcode_defs::LogOpcode::StorageRead => Instruction::from_storage_read(
221                src1.try_into().unwrap(),
222                out.try_into().unwrap(),
223                arguments,
224            ),
225            zkevm_opcode_defs::LogOpcode::TransientStorageRead => {
226                Instruction::from_transient_storage_read(
227                    src1.try_into().unwrap(),
228                    out.try_into().unwrap(),
229                    arguments,
230                )
231            }
232
233            zkevm_opcode_defs::LogOpcode::StorageWrite => {
234                Instruction::from_storage_write(src1.try_into().unwrap(), src2, arguments)
235            }
236
237            zkevm_opcode_defs::LogOpcode::TransientStorageWrite => {
238                Instruction::from_transient_storage_write(src1.try_into().unwrap(), src2, arguments)
239            }
240
241            zkevm_opcode_defs::LogOpcode::ToL1Message => Instruction::from_l2_to_l1_message(
242                src1.try_into().unwrap(),
243                src2,
244                parsed.variant.flags[FIRST_MESSAGE_FLAG_IDX],
245                arguments,
246            ),
247            zkevm_opcode_defs::LogOpcode::Event => Instruction::from_event(
248                src1.try_into().unwrap(),
249                src2,
250                parsed.variant.flags[FIRST_MESSAGE_FLAG_IDX],
251                arguments,
252            ),
253            zkevm_opcode_defs::LogOpcode::PrecompileCall => Instruction::from_precompile_call(
254                src1.try_into().unwrap(),
255                src2,
256                out.try_into().unwrap(),
257                arguments,
258            ),
259            zkevm_opcode_defs::LogOpcode::Decommit => Instruction::from_decommit(
260                src1.try_into().unwrap(),
261                src2,
262                out.try_into().unwrap(),
263                arguments,
264            ),
265        },
266        Opcode::UMA(x) => {
267            let increment = parsed.variant.flags[UMA_INCREMENT_FLAG_IDX];
268            match x {
269                zkevm_opcode_defs::UMAOpcode::HeapRead => Instruction::from_heap_read(
270                    src1.try_into().unwrap(),
271                    out.try_into().unwrap(),
272                    increment.then_some(out2),
273                    arguments,
274                ),
275                zkevm_opcode_defs::UMAOpcode::HeapWrite => Instruction::from_heap_write(
276                    src1.try_into().unwrap(),
277                    src2,
278                    increment.then_some(out.try_into().unwrap()),
279                    arguments,
280                    is_bootloader,
281                ),
282                zkevm_opcode_defs::UMAOpcode::AuxHeapRead => Instruction::from_aux_heap_read(
283                    src1.try_into().unwrap(),
284                    out.try_into().unwrap(),
285                    increment.then_some(out2),
286                    arguments,
287                ),
288                zkevm_opcode_defs::UMAOpcode::AuxHeapWrite => Instruction::from_aux_heap_store(
289                    src1.try_into().unwrap(),
290                    src2,
291                    increment.then_some(out.try_into().unwrap()),
292                    arguments,
293                ),
294                zkevm_opcode_defs::UMAOpcode::FatPointerRead => Instruction::from_pointer_read(
295                    src1.try_into().unwrap(),
296                    out.try_into().unwrap(),
297                    increment.then_some(out2),
298                    arguments,
299                ),
300                zkevm_opcode_defs::UMAOpcode::StaticMemoryRead => {
301                    Instruction::from_static_memory_read(
302                        src1.try_into().unwrap(),
303                        out.try_into().unwrap(),
304                        increment.then_some(out2),
305                        arguments,
306                    )
307                }
308                zkevm_opcode_defs::UMAOpcode::StaticMemoryWrite => {
309                    Instruction::from_static_memory_write(
310                        src1.try_into().unwrap(),
311                        src2,
312                        increment.then_some(out.try_into().unwrap()),
313                        arguments,
314                    )
315                }
316            }
317        }
318        Opcode::Invalid(_) => Instruction::from_invalid(),
319        Opcode::Nop(_) => {
320            let no_sp_movement = AdvanceStackPointer(RegisterAndImmediate {
321                immediate: 0,
322                register: Register::new(0),
323            });
324            Instruction::from_nop(
325                if let AnySource::AdvanceStackPointer(pop) = src1 {
326                    pop
327                } else {
328                    no_sp_movement
329                },
330                if let AnyDestination::AdvanceStackPointer(push) = out {
331                    push
332                } else {
333                    no_sp_movement
334                },
335                arguments,
336            )
337        }
338    }
339}