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 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}