1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
use zkevm_opcode_defs::{
    decoding::{EncodingModeProduction, VmEncodingMode},
    ImmMemHandlerFlags, Opcode,
    Operand::{Full, RegOnly, RegOrImm},
    RegOrImmFlags, FAR_CALL_SHARD_FLAG_IDX, FAR_CALL_STATIC_FLAG_IDX, FIRST_MESSAGE_FLAG_IDX,
    RET_TO_LABEL_BIT_IDX, SET_FLAGS_FLAG_IDX, SWAP_OPERANDS_FLAG_IDX_FOR_ARITH_OPCODES,
    SWAP_OPERANDS_FLAG_IDX_FOR_PTR_OPCODE, UMA_INCREMENT_FLAG_IDX,
};
use zksync_vm2_interface::{
    opcodes::{
        self, Add, And, Div, Mul, Or, PointerAdd, PointerPack, PointerShrink, PointerSub,
        RotateLeft, RotateRight, ShiftLeft, ShiftRight, Sub, Xor,
    },
    Tracer,
};

use crate::{
    addressing_modes::{
        AbsoluteStack, AdvanceStackPointer, AnyDestination, AnySource, Arguments, CodePage,
        Immediate1, Immediate2, Register, Register1, Register2, RegisterAndImmediate,
        RelativeStack, SourceWriter,
    },
    instruction::{ExecutionEnd, ExecutionStatus},
    mode_requirements::ModeRequirements,
    Instruction, Predicate, VirtualMachine, World,
};

fn unimplemented_instruction<T, W>(variant: Opcode) -> Instruction<T, W> {
    let mut arguments = Arguments::new(Predicate::Always, 0, ModeRequirements::none());
    let variant_as_number: u16 = unsafe { std::mem::transmute(variant) };
    Immediate1(variant_as_number).write_source(&mut arguments);
    Instruction {
        handler: unimplemented_handler,
        arguments,
    }
}

fn unimplemented_handler<T, W>(
    vm: &mut VirtualMachine<T, W>,
    _: &mut W,
    _: &mut T,
) -> ExecutionStatus {
    let variant: Opcode = unsafe {
        std::mem::transmute(Immediate1::get_u16(&(*vm.state.current_frame.pc).arguments))
    };
    eprintln!("Unimplemented instruction: {variant:?}");
    ExecutionStatus::Stopped(ExecutionEnd::Panicked)
}

#[allow(clippy::too_many_lines)]
pub(crate) fn decode<T: Tracer, W: World<T>>(raw: u64, is_bootloader: bool) -> Instruction<T, W> {
    let (parsed, _) = EncodingModeProduction::parse_preliminary_variant_and_absolute_number(raw);

    let predicate = match parsed.condition {
        zkevm_opcode_defs::Condition::Always => Predicate::Always,
        zkevm_opcode_defs::Condition::Gt => Predicate::IfGT,
        zkevm_opcode_defs::Condition::Lt => Predicate::IfLT,
        zkevm_opcode_defs::Condition::Eq => Predicate::IfEQ,
        zkevm_opcode_defs::Condition::Ge => Predicate::IfGE,
        zkevm_opcode_defs::Condition::Le => Predicate::IfLE,
        zkevm_opcode_defs::Condition::Ne => Predicate::IfNotEQ,
        zkevm_opcode_defs::Condition::GtOrLt => Predicate::IfGTOrLT,
    };
    let arguments = Arguments::new(
        predicate,
        parsed.variant.ergs_price(),
        ModeRequirements::new(
            parsed.variant.requires_kernel_mode(),
            !parsed.variant.can_be_used_in_static_context(),
        ),
    );

    let stack_in = RegisterAndImmediate {
        immediate: parsed.imm_0,
        register: Register::new(parsed.src0_reg_idx),
    };
    let src1: AnySource = match parsed.variant.src0_operand_type {
        RegOnly | RegOrImm(RegOrImmFlags::UseRegOnly) | Full(ImmMemHandlerFlags::UseRegOnly) => {
            Register1(Register::new(parsed.src0_reg_idx)).into()
        }
        RegOrImm(RegOrImmFlags::UseImm16Only) | Full(ImmMemHandlerFlags::UseImm16Only) => {
            Immediate1(parsed.imm_0).into()
        }
        Full(ImmMemHandlerFlags::UseAbsoluteOnStack) => AbsoluteStack(stack_in).into(),
        Full(ImmMemHandlerFlags::UseStackWithPushPop) => AdvanceStackPointer(stack_in).into(),
        Full(ImmMemHandlerFlags::UseStackWithOffset) => RelativeStack(stack_in).into(),
        Full(ImmMemHandlerFlags::UseCodePage) => CodePage(stack_in).into(),
    };

    let stack_out = RegisterAndImmediate {
        immediate: parsed.imm_1,
        register: Register::new(parsed.dst0_reg_idx),
    };
    let out: AnyDestination = match parsed.variant.dst0_operand_type {
        RegOnly | RegOrImm(RegOrImmFlags::UseRegOnly) | Full(ImmMemHandlerFlags::UseRegOnly) => {
            Register1(Register::new(parsed.dst0_reg_idx)).into()
        }
        RegOrImm(RegOrImmFlags::UseImm16Only) | Full(ImmMemHandlerFlags::UseImm16Only) => {
            panic!("Parser wants to output to immediate")
        }
        Full(ImmMemHandlerFlags::UseAbsoluteOnStack) => AbsoluteStack(stack_out).into(),
        Full(ImmMemHandlerFlags::UseStackWithPushPop) => AdvanceStackPointer(stack_out).into(),
        Full(ImmMemHandlerFlags::UseStackWithOffset) => RelativeStack(stack_out).into(),
        Full(ImmMemHandlerFlags::UseCodePage) => panic!("Parser wants to write to code page"),
    };

    let src2 = Register2(Register::new(parsed.src1_reg_idx));
    let out2 = Register2(Register::new(parsed.dst1_reg_idx));

    macro_rules! binop {
        ($op: ident, $snd: tt) => {
            Instruction::from_binop::<$op>(
                src1,
                src2,
                out,
                &$snd,
                arguments,
                parsed.variant.flags[SWAP_OPERANDS_FLAG_IDX_FOR_ARITH_OPCODES],
                parsed.variant.flags[SET_FLAGS_FLAG_IDX],
            )
        };
    }

    macro_rules! ptr {
        ($op: ident) => {
            Instruction::from_ptr::<$op>(
                src1,
                src2,
                out,
                arguments,
                parsed.variant.flags[SWAP_OPERANDS_FLAG_IDX_FOR_PTR_OPCODE],
            )
        };
    }

    match parsed.variant.opcode {
        Opcode::Add(_) => binop!(Add, ()),
        Opcode::Sub(_) => binop!(Sub, ()),
        Opcode::Mul(_) => binop!(Mul, out2),
        Opcode::Div(_) => binop!(Div, out2),
        Opcode::Binop(x) => match x {
            zkevm_opcode_defs::BinopOpcode::Xor => binop!(Xor, ()),
            zkevm_opcode_defs::BinopOpcode::And => binop!(And, ()),
            zkevm_opcode_defs::BinopOpcode::Or => binop!(Or, ()),
        },
        Opcode::Shift(x) => match x {
            zkevm_opcode_defs::ShiftOpcode::Shl => binop!(ShiftLeft, ()),
            zkevm_opcode_defs::ShiftOpcode::Shr => binop!(ShiftRight, ()),
            zkevm_opcode_defs::ShiftOpcode::Rol => binop!(RotateLeft, ()),
            zkevm_opcode_defs::ShiftOpcode::Ror => binop!(RotateRight, ()),
        },
        Opcode::Jump(_) => Instruction::from_jump(src1, out.try_into().unwrap(), arguments),
        Opcode::Context(x) => match x {
            zkevm_opcode_defs::ContextOpcode::This => {
                Instruction::from_this(out.try_into().unwrap(), arguments)
            }
            zkevm_opcode_defs::ContextOpcode::Caller => {
                Instruction::from_caller(out.try_into().unwrap(), arguments)
            }
            zkevm_opcode_defs::ContextOpcode::CodeAddress => {
                Instruction::from_code_address(out.try_into().unwrap(), arguments)
            }
            zkevm_opcode_defs::ContextOpcode::ErgsLeft => {
                Instruction::from_ergs_left(out.try_into().unwrap(), arguments)
            }
            zkevm_opcode_defs::ContextOpcode::GetContextU128 => {
                Instruction::from_context_u128(out.try_into().unwrap(), arguments)
            }
            zkevm_opcode_defs::ContextOpcode::SetContextU128 => {
                Instruction::from_set_context_u128(src1.try_into().unwrap(), arguments)
            }
            zkevm_opcode_defs::ContextOpcode::Sp => {
                Instruction::from_context_sp(out.try_into().unwrap(), arguments)
            }
            zkevm_opcode_defs::ContextOpcode::Meta => {
                Instruction::from_context_meta(out.try_into().unwrap(), arguments)
            }
            zkevm_opcode_defs::ContextOpcode::IncrementTxNumber => {
                Instruction::from_increment_tx_number(arguments)
            }
            zkevm_opcode_defs::ContextOpcode::AuxMutating0 => {
                Instruction::from_aux_mutating(arguments)
            }
        },
        Opcode::Ptr(x) => match x {
            zkevm_opcode_defs::PtrOpcode::Add => ptr!(PointerAdd),
            zkevm_opcode_defs::PtrOpcode::Sub => ptr!(PointerSub),
            zkevm_opcode_defs::PtrOpcode::Pack => ptr!(PointerPack),
            zkevm_opcode_defs::PtrOpcode::Shrink => ptr!(PointerShrink),
        },
        Opcode::NearCall(_) => Instruction::from_near_call(
            Register1(Register::new(parsed.src0_reg_idx)),
            Immediate1(parsed.imm_0),
            Immediate2(parsed.imm_1),
            arguments,
        ),
        Opcode::FarCall(kind) => {
            let constructor = match kind {
                zkevm_opcode_defs::FarCallOpcode::Normal => {
                    Instruction::from_far_call::<opcodes::Normal>
                }
                zkevm_opcode_defs::FarCallOpcode::Delegate => {
                    Instruction::from_far_call::<opcodes::Delegate>
                }
                zkevm_opcode_defs::FarCallOpcode::Mimic => {
                    Instruction::from_far_call::<opcodes::Mimic>
                }
            };
            constructor(
                src1.try_into().unwrap(),
                src2,
                Immediate1(parsed.imm_0),
                parsed.variant.flags[FAR_CALL_STATIC_FLAG_IDX],
                parsed.variant.flags[FAR_CALL_SHARD_FLAG_IDX],
                arguments,
            )
        }
        Opcode::Ret(kind) => {
            let to_label = parsed.variant.flags[RET_TO_LABEL_BIT_IDX];
            let label = if to_label {
                Some(Immediate1(parsed.imm_0))
            } else {
                None
            };
            match kind {
                zkevm_opcode_defs::RetOpcode::Ok => {
                    Instruction::from_ret(src1.try_into().unwrap(), label, arguments)
                }
                zkevm_opcode_defs::RetOpcode::Revert => {
                    Instruction::from_revert(src1.try_into().unwrap(), label, arguments)
                }
                zkevm_opcode_defs::RetOpcode::Panic => Instruction::from_panic(label, arguments),
            }
        }
        Opcode::Log(x) => match x {
            zkevm_opcode_defs::LogOpcode::StorageRead => Instruction::from_storage_read(
                src1.try_into().unwrap(),
                out.try_into().unwrap(),
                arguments,
            ),
            zkevm_opcode_defs::LogOpcode::TransientStorageRead => {
                Instruction::from_transient_storage_read(
                    src1.try_into().unwrap(),
                    out.try_into().unwrap(),
                    arguments,
                )
            }

            zkevm_opcode_defs::LogOpcode::StorageWrite => {
                Instruction::from_storage_write(src1.try_into().unwrap(), src2, arguments)
            }

            zkevm_opcode_defs::LogOpcode::TransientStorageWrite => {
                Instruction::from_transient_storage_write(src1.try_into().unwrap(), src2, arguments)
            }

            zkevm_opcode_defs::LogOpcode::ToL1Message => Instruction::from_l2_to_l1_message(
                src1.try_into().unwrap(),
                src2,
                parsed.variant.flags[FIRST_MESSAGE_FLAG_IDX],
                arguments,
            ),
            zkevm_opcode_defs::LogOpcode::Event => Instruction::from_event(
                src1.try_into().unwrap(),
                src2,
                parsed.variant.flags[FIRST_MESSAGE_FLAG_IDX],
                arguments,
            ),
            zkevm_opcode_defs::LogOpcode::PrecompileCall => Instruction::from_precompile_call(
                src1.try_into().unwrap(),
                src2,
                out.try_into().unwrap(),
                arguments,
            ),
            zkevm_opcode_defs::LogOpcode::Decommit => Instruction::from_decommit(
                src1.try_into().unwrap(),
                src2,
                out.try_into().unwrap(),
                arguments,
            ),
        },
        Opcode::UMA(x) => {
            let increment = parsed.variant.flags[UMA_INCREMENT_FLAG_IDX];
            match x {
                zkevm_opcode_defs::UMAOpcode::HeapRead => Instruction::from_heap_read(
                    src1.try_into().unwrap(),
                    out.try_into().unwrap(),
                    increment.then_some(out2),
                    arguments,
                ),
                zkevm_opcode_defs::UMAOpcode::HeapWrite => Instruction::from_heap_write(
                    src1.try_into().unwrap(),
                    src2,
                    increment.then_some(out.try_into().unwrap()),
                    arguments,
                    is_bootloader,
                ),
                zkevm_opcode_defs::UMAOpcode::AuxHeapRead => Instruction::from_aux_heap_read(
                    src1.try_into().unwrap(),
                    out.try_into().unwrap(),
                    increment.then_some(out2),
                    arguments,
                ),
                zkevm_opcode_defs::UMAOpcode::AuxHeapWrite => Instruction::from_aux_heap_store(
                    src1.try_into().unwrap(),
                    src2,
                    increment.then_some(out.try_into().unwrap()),
                    arguments,
                ),
                zkevm_opcode_defs::UMAOpcode::FatPointerRead => Instruction::from_pointer_read(
                    src1.try_into().unwrap(),
                    out.try_into().unwrap(),
                    increment.then_some(out2),
                    arguments,
                ),
                zkevm_opcode_defs::UMAOpcode::StaticMemoryRead => unimplemented_instruction(
                    Opcode::UMA(zkevm_opcode_defs::UMAOpcode::StaticMemoryRead),
                ),
                zkevm_opcode_defs::UMAOpcode::StaticMemoryWrite => unimplemented_instruction(
                    Opcode::UMA(zkevm_opcode_defs::UMAOpcode::StaticMemoryWrite),
                ),
            }
        }
        Opcode::Invalid(_) => Instruction::from_invalid(),
        Opcode::Nop(_) => {
            let no_sp_movement = AdvanceStackPointer(RegisterAndImmediate {
                immediate: 0,
                register: Register::new(0),
            });
            Instruction::from_nop(
                if let AnySource::AdvanceStackPointer(pop) = src1 {
                    pop
                } else {
                    no_sp_movement
                },
                if let AnyDestination::AdvanceStackPointer(push) = out {
                    push
                } else {
                    no_sp_movement
                },
                arguments,
            )
        }
    }
}