miden_assembly/assembler/instruction/
mod.rs

1use vm_core::{Decorator, ONE, WORD_SIZE, ZERO, debuginfo::Spanned, mast::MastNodeId};
2
3use super::{Assembler, BasicBlockBuilder, Felt, Operation, ProcedureContext, ast::InvokeKind};
4use crate::{AssemblyError, Span, ast::Instruction};
5
6mod adv_ops;
7mod crypto_ops;
8mod env_ops;
9mod ext2_ops;
10mod field_ops;
11mod mem_ops;
12mod procedures;
13mod u32_ops;
14
15use self::u32_ops::U32OpMode::*;
16
17/// Instruction Compilation
18impl Assembler {
19    pub(super) fn compile_instruction(
20        &self,
21        instruction: &Span<Instruction>,
22        block_builder: &mut BasicBlockBuilder,
23        proc_ctx: &mut ProcedureContext,
24    ) -> Result<Option<MastNodeId>, AssemblyError> {
25        // if the assembler is in debug mode, start tracking the instruction about to be executed;
26        // this will allow us to map the instruction to the sequence of operations which were
27        // executed as a part of this instruction.
28        if self.in_debug_mode() {
29            block_builder.track_instruction(instruction, proc_ctx)?;
30        }
31
32        let new_node_id = self.compile_instruction_impl(instruction, block_builder, proc_ctx)?;
33
34        if self.in_debug_mode() {
35            // compute and update the cycle count of the instruction which just finished executing
36            let maybe_asm_op_id = block_builder.set_instruction_cycle_count();
37
38            if let Some(node_id) = new_node_id {
39                // New node was created, so we are done building the current block. We then want to
40                // add the assembly operation to the new node - for example call, dyncall, if/else
41                // statements, loops, etc. However, `exec` instructions are compiled away and not
42                // added to the trace, so we should ignore them. Theoretically, we
43                // could probably add them anyways, but it currently breaks the
44                // `VmStateIterator`.
45                if !matches!(instruction.inner(), &Instruction::Exec(_)) {
46                    let asm_op_id = maybe_asm_op_id.expect("no asmop decorator");
47
48                    // set the cycle count to 1
49                    {
50                        let assembly_op = &mut block_builder.mast_forest_builder_mut()[asm_op_id];
51                        if let Decorator::AsmOp(assembly_op) = assembly_op {
52                            assembly_op.set_num_cycles(1);
53                        } else {
54                            panic!("expected AsmOp decorator");
55                        }
56                    }
57
58                    block_builder
59                        .mast_forest_builder_mut()
60                        .append_before_enter(node_id, &[asm_op_id]);
61                }
62            }
63        }
64
65        Ok(new_node_id)
66    }
67
68    fn compile_instruction_impl(
69        &self,
70        instruction: &Span<Instruction>,
71        block_builder: &mut BasicBlockBuilder,
72        proc_ctx: &mut ProcedureContext,
73    ) -> Result<Option<MastNodeId>, AssemblyError> {
74        use Operation::*;
75
76        match &**instruction {
77            Instruction::Nop => block_builder.push_op(Noop),
78            Instruction::Assert => block_builder.push_op(Assert(ZERO)),
79            Instruction::AssertWithError(err_msg) => {
80                let error_code = block_builder.register_error(err_msg.expect_string());
81                block_builder.push_op(Assert(error_code))
82            },
83            Instruction::AssertEq => block_builder.push_ops([Eq, Assert(ZERO)]),
84            Instruction::AssertEqWithError(err_msg) => {
85                let error_code = block_builder.register_error(err_msg.expect_string());
86                block_builder.push_ops([Eq, Assert(error_code)])
87            },
88            Instruction::AssertEqw => field_ops::assertw(block_builder, ZERO),
89            Instruction::AssertEqwWithError(err_msg) => {
90                let error_code = block_builder.register_error(err_msg.expect_string());
91                field_ops::assertw(block_builder, error_code)
92            },
93            Instruction::Assertz => block_builder.push_ops([Eqz, Assert(ZERO)]),
94            Instruction::AssertzWithError(err_msg) => {
95                let error_code = block_builder.register_error(err_msg.expect_string());
96                block_builder.push_ops([Eqz, Assert(error_code)])
97            },
98
99            Instruction::Add => block_builder.push_op(Add),
100            Instruction::AddImm(imm) => field_ops::add_imm(block_builder, imm.expect_value()),
101            Instruction::Sub => block_builder.push_ops([Neg, Add]),
102            Instruction::SubImm(imm) => field_ops::sub_imm(block_builder, imm.expect_value()),
103            Instruction::Mul => block_builder.push_op(Mul),
104            Instruction::MulImm(imm) => field_ops::mul_imm(block_builder, imm.expect_value()),
105            Instruction::Div => block_builder.push_ops([Inv, Mul]),
106            Instruction::DivImm(imm) => {
107                field_ops::div_imm(block_builder, proc_ctx, imm.expect_spanned_value())?;
108            },
109            Instruction::Neg => block_builder.push_op(Neg),
110            Instruction::Inv => block_builder.push_op(Inv),
111            Instruction::Incr => block_builder.push_op(Incr),
112
113            Instruction::Pow2 => field_ops::pow2(block_builder),
114            Instruction::Exp => field_ops::exp(block_builder, proc_ctx, 64_u8)?,
115
116            Instruction::ExpImm(pow) => {
117                field_ops::exp_imm(block_builder, proc_ctx, pow.expect_value())?
118            },
119            Instruction::ExpBitLength(num_pow_bits) => {
120                field_ops::exp(block_builder, proc_ctx, *num_pow_bits)?
121            },
122            Instruction::ILog2 => field_ops::ilog2(block_builder),
123
124            Instruction::Not => block_builder.push_op(Not),
125            Instruction::And => block_builder.push_op(And),
126            Instruction::Or => block_builder.push_op(Or),
127            Instruction::Xor => block_builder.push_ops([Dup0, Dup2, Or, MovDn2, And, Not, And]),
128
129            Instruction::Eq => block_builder.push_op(Eq),
130            Instruction::EqImm(imm) => field_ops::eq_imm(block_builder, imm.expect_value()),
131            Instruction::Eqw => field_ops::eqw(block_builder),
132            Instruction::Neq => block_builder.push_ops([Eq, Not]),
133            Instruction::NeqImm(imm) => field_ops::neq_imm(block_builder, imm.expect_value()),
134            Instruction::Lt => field_ops::lt(block_builder),
135            Instruction::Lte => field_ops::lte(block_builder),
136            Instruction::Gt => field_ops::gt(block_builder),
137            Instruction::Gte => field_ops::gte(block_builder),
138            Instruction::IsOdd => field_ops::is_odd(block_builder),
139
140            // ----- ext2 instructions ------------------------------------------------------------
141            Instruction::Ext2Add => ext2_ops::ext2_add(block_builder),
142            Instruction::Ext2Sub => ext2_ops::ext2_sub(block_builder),
143            Instruction::Ext2Mul => ext2_ops::ext2_mul(block_builder),
144            Instruction::Ext2Div => ext2_ops::ext2_div(block_builder),
145            Instruction::Ext2Neg => ext2_ops::ext2_neg(block_builder),
146            Instruction::Ext2Inv => ext2_ops::ext2_inv(block_builder)?,
147
148            // ----- u32 manipulation -------------------------------------------------------------
149            Instruction::U32Test => block_builder.push_ops([Dup0, U32split, Swap, Drop, Eqz]),
150            Instruction::U32TestW => u32_ops::u32testw(block_builder),
151            Instruction::U32Assert => block_builder.push_ops([Pad, U32assert2(ZERO), Drop]),
152            Instruction::U32AssertWithError(err_msg) => {
153                let error_code = block_builder.register_error(err_msg.expect_string());
154                block_builder.push_ops([Pad, U32assert2(error_code), Drop])
155            },
156            Instruction::U32Assert2 => block_builder.push_op(U32assert2(ZERO)),
157            Instruction::U32Assert2WithError(err_msg) => {
158                let error_code = block_builder.register_error(err_msg.expect_string());
159                block_builder.push_op(U32assert2(error_code))
160            },
161            Instruction::U32AssertW => u32_ops::u32assertw(block_builder, ZERO),
162            Instruction::U32AssertWWithError(err_msg) => {
163                let error_code = block_builder.register_error(err_msg.expect_string());
164                u32_ops::u32assertw(block_builder, error_code)
165            },
166
167            Instruction::U32Cast => block_builder.push_ops([U32split, Drop]),
168            Instruction::U32Split => block_builder.push_op(U32split),
169
170            Instruction::U32OverflowingAdd => u32_ops::u32add(block_builder, Overflowing, None),
171            Instruction::U32OverflowingAddImm(v) => {
172                u32_ops::u32add(block_builder, Overflowing, Some(v.expect_value()))
173            },
174            Instruction::U32WrappingAdd => u32_ops::u32add(block_builder, Wrapping, None),
175            Instruction::U32WrappingAddImm(v) => {
176                u32_ops::u32add(block_builder, Wrapping, Some(v.expect_value()))
177            },
178            Instruction::U32OverflowingAdd3 => block_builder.push_op(U32add3),
179            Instruction::U32WrappingAdd3 => block_builder.push_ops([U32add3, Drop]),
180
181            Instruction::U32OverflowingSub => u32_ops::u32sub(block_builder, Overflowing, None),
182            Instruction::U32OverflowingSubImm(v) => {
183                u32_ops::u32sub(block_builder, Overflowing, Some(v.expect_value()))
184            },
185            Instruction::U32WrappingSub => u32_ops::u32sub(block_builder, Wrapping, None),
186            Instruction::U32WrappingSubImm(v) => {
187                u32_ops::u32sub(block_builder, Wrapping, Some(v.expect_value()))
188            },
189
190            Instruction::U32OverflowingMul => u32_ops::u32mul(block_builder, Overflowing, None),
191            Instruction::U32OverflowingMulImm(v) => {
192                u32_ops::u32mul(block_builder, Overflowing, Some(v.expect_value()))
193            },
194            Instruction::U32WrappingMul => u32_ops::u32mul(block_builder, Wrapping, None),
195            Instruction::U32WrappingMulImm(v) => {
196                u32_ops::u32mul(block_builder, Wrapping, Some(v.expect_value()))
197            },
198            Instruction::U32OverflowingMadd => block_builder.push_op(U32madd),
199            Instruction::U32WrappingMadd => block_builder.push_ops([U32madd, Drop]),
200
201            Instruction::U32Div => u32_ops::u32div(block_builder, proc_ctx, None)?,
202            Instruction::U32DivImm(v) => {
203                u32_ops::u32div(block_builder, proc_ctx, Some(v.expect_spanned_value()))?
204            },
205            Instruction::U32Mod => u32_ops::u32mod(block_builder, proc_ctx, None)?,
206            Instruction::U32ModImm(v) => {
207                u32_ops::u32mod(block_builder, proc_ctx, Some(v.expect_spanned_value()))?
208            },
209            Instruction::U32DivMod => u32_ops::u32divmod(block_builder, proc_ctx, None)?,
210            Instruction::U32DivModImm(v) => {
211                u32_ops::u32divmod(block_builder, proc_ctx, Some(v.expect_spanned_value()))?
212            },
213            Instruction::U32And => block_builder.push_op(U32and),
214            Instruction::U32Or => block_builder.push_ops([Dup1, Dup1, U32and, Neg, Add, Add]),
215            Instruction::U32Xor => block_builder.push_op(U32xor),
216            Instruction::U32Not => u32_ops::u32not(block_builder),
217            Instruction::U32Shl => u32_ops::u32shl(block_builder, proc_ctx, None)?,
218            Instruction::U32ShlImm(v) => {
219                u32_ops::u32shl(block_builder, proc_ctx, Some(v.expect_value()))?
220            },
221            Instruction::U32Shr => u32_ops::u32shr(block_builder, proc_ctx, None)?,
222            Instruction::U32ShrImm(v) => {
223                u32_ops::u32shr(block_builder, proc_ctx, Some(v.expect_value()))?
224            },
225            Instruction::U32Rotl => u32_ops::u32rotl(block_builder, proc_ctx, None)?,
226            Instruction::U32RotlImm(v) => {
227                u32_ops::u32rotl(block_builder, proc_ctx, Some(v.expect_value()))?
228            },
229            Instruction::U32Rotr => u32_ops::u32rotr(block_builder, proc_ctx, None)?,
230            Instruction::U32RotrImm(v) => {
231                u32_ops::u32rotr(block_builder, proc_ctx, Some(v.expect_value()))?
232            },
233            Instruction::U32Popcnt => u32_ops::u32popcnt(block_builder),
234            Instruction::U32Clz => u32_ops::u32clz(block_builder),
235            Instruction::U32Ctz => u32_ops::u32ctz(block_builder),
236            Instruction::U32Clo => u32_ops::u32clo(block_builder),
237            Instruction::U32Cto => u32_ops::u32cto(block_builder),
238            Instruction::U32Lt => u32_ops::u32lt(block_builder),
239            Instruction::U32Lte => u32_ops::u32lte(block_builder),
240            Instruction::U32Gt => u32_ops::u32gt(block_builder),
241            Instruction::U32Gte => u32_ops::u32gte(block_builder),
242            Instruction::U32Min => u32_ops::u32min(block_builder),
243            Instruction::U32Max => u32_ops::u32max(block_builder),
244
245            // ----- stack manipulation -----------------------------------------------------------
246            Instruction::Drop => block_builder.push_op(Drop),
247            Instruction::DropW => block_builder.push_ops([Drop; 4]),
248            Instruction::PadW => block_builder.push_ops([Pad; 4]),
249            Instruction::Dup0 => block_builder.push_op(Dup0),
250            Instruction::Dup1 => block_builder.push_op(Dup1),
251            Instruction::Dup2 => block_builder.push_op(Dup2),
252            Instruction::Dup3 => block_builder.push_op(Dup3),
253            Instruction::Dup4 => block_builder.push_op(Dup4),
254            Instruction::Dup5 => block_builder.push_op(Dup5),
255            Instruction::Dup6 => block_builder.push_op(Dup6),
256            Instruction::Dup7 => block_builder.push_op(Dup7),
257            Instruction::Dup8 => block_builder.push_ops([Pad, Dup9, Add]),
258            Instruction::Dup9 => block_builder.push_op(Dup9),
259            Instruction::Dup10 => block_builder.push_ops([Pad, Dup11, Add]),
260            Instruction::Dup11 => block_builder.push_op(Dup11),
261            Instruction::Dup12 => block_builder.push_ops([Pad, Dup13, Add]),
262            Instruction::Dup13 => block_builder.push_op(Dup13),
263            Instruction::Dup14 => block_builder.push_ops([Pad, Dup15, Add]),
264            Instruction::Dup15 => block_builder.push_op(Dup15),
265            Instruction::DupW0 => block_builder.push_ops([Dup3; 4]),
266            Instruction::DupW1 => block_builder.push_ops([Dup7; 4]),
267            Instruction::DupW2 => block_builder.push_ops([Dup11; 4]),
268            Instruction::DupW3 => block_builder.push_ops([Dup15; 4]),
269            Instruction::Swap1 => block_builder.push_op(Swap),
270            Instruction::Swap2 => block_builder.push_ops([Swap, MovUp2]),
271            Instruction::Swap3 => block_builder.push_ops([MovDn2, MovUp3]),
272            Instruction::Swap4 => block_builder.push_ops([MovDn3, MovUp4]),
273            Instruction::Swap5 => block_builder.push_ops([MovDn4, MovUp5]),
274            Instruction::Swap6 => block_builder.push_ops([MovDn5, MovUp6]),
275            Instruction::Swap7 => block_builder.push_ops([MovDn6, MovUp7]),
276            Instruction::Swap8 => block_builder.push_ops([MovDn7, MovUp8]),
277            Instruction::Swap9 => block_builder.push_ops([MovDn8, SwapDW, Swap, SwapDW, MovUp8]),
278            Instruction::Swap10 => {
279                block_builder.push_ops([MovDn8, SwapDW, Swap, MovUp2, SwapDW, MovUp8])
280            },
281            Instruction::Swap11 => {
282                block_builder.push_ops([MovDn8, SwapDW, MovDn2, MovUp3, SwapDW, MovUp8])
283            },
284            Instruction::Swap12 => {
285                block_builder.push_ops([MovDn8, SwapDW, MovDn3, MovUp4, SwapDW, MovUp8])
286            },
287            Instruction::Swap13 => {
288                block_builder.push_ops([MovDn8, SwapDW, MovDn4, MovUp5, SwapDW, MovUp8])
289            },
290            Instruction::Swap14 => {
291                block_builder.push_ops([MovDn8, SwapDW, MovDn5, MovUp6, SwapDW, MovUp8])
292            },
293            Instruction::Swap15 => {
294                block_builder.push_ops([MovDn8, SwapDW, MovDn6, MovUp7, SwapDW, MovUp8])
295            },
296            Instruction::SwapW1 => block_builder.push_op(SwapW),
297            Instruction::SwapW2 => block_builder.push_op(SwapW2),
298            Instruction::SwapW3 => block_builder.push_op(SwapW3),
299            Instruction::SwapDw => block_builder.push_op(SwapDW),
300            Instruction::MovUp2 => block_builder.push_op(MovUp2),
301            Instruction::MovUp3 => block_builder.push_op(MovUp3),
302            Instruction::MovUp4 => block_builder.push_op(MovUp4),
303            Instruction::MovUp5 => block_builder.push_op(MovUp5),
304            Instruction::MovUp6 => block_builder.push_op(MovUp6),
305            Instruction::MovUp7 => block_builder.push_op(MovUp7),
306            Instruction::MovUp8 => block_builder.push_op(MovUp8),
307            Instruction::MovUp9 => block_builder.push_ops([SwapDW, Swap, SwapDW, MovUp8]),
308            Instruction::MovUp10 => block_builder.push_ops([SwapDW, MovUp2, SwapDW, MovUp8]),
309            Instruction::MovUp11 => block_builder.push_ops([SwapDW, MovUp3, SwapDW, MovUp8]),
310            Instruction::MovUp12 => block_builder.push_ops([SwapDW, MovUp4, SwapDW, MovUp8]),
311            Instruction::MovUp13 => block_builder.push_ops([SwapDW, MovUp5, SwapDW, MovUp8]),
312            Instruction::MovUp14 => block_builder.push_ops([SwapDW, MovUp6, SwapDW, MovUp8]),
313            Instruction::MovUp15 => block_builder.push_ops([SwapDW, MovUp7, SwapDW, MovUp8]),
314            Instruction::MovUpW2 => block_builder.push_ops([SwapW, SwapW2]),
315            Instruction::MovUpW3 => block_builder.push_ops([SwapW, SwapW2, SwapW3]),
316            Instruction::MovDn2 => block_builder.push_op(MovDn2),
317            Instruction::MovDn3 => block_builder.push_op(MovDn3),
318            Instruction::MovDn4 => block_builder.push_op(MovDn4),
319            Instruction::MovDn5 => block_builder.push_op(MovDn5),
320            Instruction::MovDn6 => block_builder.push_op(MovDn6),
321            Instruction::MovDn7 => block_builder.push_op(MovDn7),
322            Instruction::MovDn8 => block_builder.push_op(MovDn8),
323            Instruction::MovDn9 => block_builder.push_ops([MovDn8, SwapDW, Swap, SwapDW]),
324            Instruction::MovDn10 => block_builder.push_ops([MovDn8, SwapDW, MovDn2, SwapDW]),
325            Instruction::MovDn11 => block_builder.push_ops([MovDn8, SwapDW, MovDn3, SwapDW]),
326            Instruction::MovDn12 => block_builder.push_ops([MovDn8, SwapDW, MovDn4, SwapDW]),
327            Instruction::MovDn13 => block_builder.push_ops([MovDn8, SwapDW, MovDn5, SwapDW]),
328            Instruction::MovDn14 => block_builder.push_ops([MovDn8, SwapDW, MovDn6, SwapDW]),
329            Instruction::MovDn15 => block_builder.push_ops([MovDn8, SwapDW, MovDn7, SwapDW]),
330            Instruction::MovDnW2 => block_builder.push_ops([SwapW2, SwapW]),
331            Instruction::MovDnW3 => block_builder.push_ops([SwapW3, SwapW2, SwapW]),
332
333            Instruction::CSwap => block_builder.push_op(CSwap),
334            Instruction::CSwapW => block_builder.push_op(CSwapW),
335            Instruction::CDrop => block_builder.push_ops([CSwap, Drop]),
336            Instruction::CDropW => block_builder.push_ops([CSwapW, Drop, Drop, Drop, Drop]),
337
338            // ----- input / output instructions --------------------------------------------------
339            Instruction::Push(imm) => env_ops::push_one(imm.expect_value(), block_builder),
340            Instruction::PushU8(imm) => env_ops::push_one(*imm, block_builder),
341            Instruction::PushU16(imm) => env_ops::push_one(*imm, block_builder),
342            Instruction::PushU32(imm) => env_ops::push_one(*imm, block_builder),
343            Instruction::PushFelt(imm) => env_ops::push_one(*imm, block_builder),
344            Instruction::PushWord(imms) => env_ops::push_many(imms, block_builder),
345            Instruction::PushU8List(imms) => env_ops::push_many(imms, block_builder),
346            Instruction::PushU16List(imms) => env_ops::push_many(imms, block_builder),
347            Instruction::PushU32List(imms) => env_ops::push_many(imms, block_builder),
348            Instruction::PushFeltList(imms) => env_ops::push_many(imms, block_builder),
349            Instruction::Sdepth => block_builder.push_op(SDepth),
350            Instruction::Caller => env_ops::caller(block_builder, proc_ctx, instruction.span())?,
351            Instruction::Clk => block_builder.push_op(Clk),
352            Instruction::AdvPipe => block_builder.push_op(Pipe),
353            Instruction::AdvPush(n) => {
354                adv_ops::adv_push(block_builder, proc_ctx, n.expect_value())?
355            },
356            Instruction::AdvLoadW => block_builder.push_op(AdvPopW),
357
358            Instruction::MemStream => block_builder.push_op(MStream),
359            Instruction::Locaddr(v) => {
360                env_ops::locaddr(block_builder, v.expect_value(), proc_ctx, instruction.span())?
361            },
362            Instruction::MemLoad => {
363                mem_ops::mem_read(block_builder, proc_ctx, None, false, true, instruction.span())?
364            },
365            Instruction::MemLoadImm(v) => mem_ops::mem_read(
366                block_builder,
367                proc_ctx,
368                Some(v.expect_value()),
369                false,
370                true,
371                instruction.span(),
372            )?,
373            Instruction::MemLoadW => {
374                mem_ops::mem_read(block_builder, proc_ctx, None, false, false, instruction.span())?
375            },
376            Instruction::MemLoadWImm(v) => mem_ops::mem_read(
377                block_builder,
378                proc_ctx,
379                Some(v.expect_value()),
380                false,
381                false,
382                instruction.span(),
383            )?,
384            Instruction::LocLoad(v) => mem_ops::mem_read(
385                block_builder,
386                proc_ctx,
387                Some(v.expect_value() as u32),
388                true,
389                true,
390                instruction.span(),
391            )?,
392            Instruction::LocLoadW(v) => {
393                let local_addr = v.expect_value();
394                if local_addr % WORD_SIZE as u16 != 0 {
395                    return Err(AssemblyError::InvalidLocalWordIndex {
396                        span: instruction.span(),
397                        source_file: proc_ctx
398                            .source_manager()
399                            .get(proc_ctx.span().source_id())
400                            .ok(),
401                        local_addr,
402                    });
403                }
404
405                mem_ops::mem_read(
406                    block_builder,
407                    proc_ctx,
408                    Some(local_addr as u32),
409                    true,
410                    false,
411                    instruction.span(),
412                )?
413            },
414            Instruction::MemStore => block_builder.push_ops([MStore, Drop]),
415            Instruction::MemStoreW => block_builder.push_ops([MStoreW]),
416            Instruction::MemStoreImm(v) => mem_ops::mem_write_imm(
417                block_builder,
418                proc_ctx,
419                v.expect_value(),
420                false,
421                true,
422                instruction.span(),
423            )?,
424            Instruction::MemStoreWImm(v) => mem_ops::mem_write_imm(
425                block_builder,
426                proc_ctx,
427                v.expect_value(),
428                false,
429                false,
430                instruction.span(),
431            )?,
432            Instruction::LocStore(v) => mem_ops::mem_write_imm(
433                block_builder,
434                proc_ctx,
435                v.expect_value() as u32,
436                true,
437                true,
438                instruction.span(),
439            )?,
440            Instruction::LocStoreW(v) => {
441                let local_addr = v.expect_value();
442                if local_addr % WORD_SIZE as u16 != 0 {
443                    return Err(AssemblyError::InvalidLocalWordIndex {
444                        span: instruction.span(),
445                        source_file: proc_ctx
446                            .source_manager()
447                            .get(proc_ctx.span().source_id())
448                            .ok(),
449                        local_addr,
450                    });
451                }
452
453                mem_ops::mem_write_imm(
454                    block_builder,
455                    proc_ctx,
456                    local_addr as u32,
457                    true,
458                    false,
459                    instruction.span(),
460                )?
461            },
462            Instruction::SysEvent(system_event) => {
463                block_builder.push_system_event(system_event.into())
464            },
465
466            // ----- cryptographic instructions ---------------------------------------------------
467            Instruction::Hash => crypto_ops::hash(block_builder),
468            Instruction::HPerm => block_builder.push_op(HPerm),
469            Instruction::HMerge => crypto_ops::hmerge(block_builder),
470            Instruction::MTreeGet => crypto_ops::mtree_get(block_builder),
471            Instruction::MTreeSet => crypto_ops::mtree_set(block_builder)?,
472            Instruction::MTreeMerge => crypto_ops::mtree_merge(block_builder),
473            Instruction::MTreeVerify => block_builder.push_op(MpVerify(ZERO)),
474            Instruction::MTreeVerifyWithError(err_msg) => {
475                let error_code = block_builder.register_error(err_msg.expect_string());
476                block_builder.push_op(MpVerify(error_code))
477            },
478
479            // ----- STARK proof verification -----------------------------------------------------
480            Instruction::FriExt2Fold4 => block_builder.push_op(FriE2F4),
481            Instruction::HornerBase => block_builder.push_op(HornerBase),
482            Instruction::HornerExt => block_builder.push_op(HornerExt),
483            Instruction::ArithmeticCircuitEval => {
484                block_builder.push_op(Operation::ArithmeticCircuitEval);
485            },
486
487            // ----- exec/call instructions -------------------------------------------------------
488            Instruction::Exec(callee) => {
489                return self
490                    .invoke(
491                        InvokeKind::Exec,
492                        callee,
493                        proc_ctx,
494                        block_builder.mast_forest_builder_mut(),
495                    )
496                    .map(Into::into);
497            },
498            Instruction::Call(callee) => {
499                return self
500                    .invoke(
501                        InvokeKind::Call,
502                        callee,
503                        proc_ctx,
504                        block_builder.mast_forest_builder_mut(),
505                    )
506                    .map(Into::into);
507            },
508            Instruction::SysCall(callee) => {
509                return self
510                    .invoke(
511                        InvokeKind::SysCall,
512                        callee,
513                        proc_ctx,
514                        block_builder.mast_forest_builder_mut(),
515                    )
516                    .map(Into::into);
517            },
518            Instruction::DynExec => return self.dynexec(block_builder.mast_forest_builder_mut()),
519            Instruction::DynCall => return self.dyncall(block_builder.mast_forest_builder_mut()),
520            Instruction::ProcRef(callee) => self.procref(callee, proc_ctx, block_builder)?,
521
522            // ----- debug decorators -------------------------------------------------------------
523            Instruction::Breakpoint => {
524                if self.in_debug_mode() {
525                    block_builder.push_op(Noop);
526                    block_builder.track_instruction(instruction, proc_ctx)?;
527                }
528            },
529
530            Instruction::Debug(options) => {
531                if self.in_debug_mode() {
532                    block_builder.push_decorator(Decorator::Debug(
533                        options.clone().try_into().expect("unresolved constant"),
534                    ))?;
535                }
536            },
537
538            // ----- emit instruction -------------------------------------------------------------
539            Instruction::Emit(event_id) => {
540                block_builder.push_op(Operation::Emit(event_id.expect_value()));
541            },
542
543            // ----- trace instruction ------------------------------------------------------------
544            Instruction::Trace(trace_id) => {
545                block_builder.push_decorator(Decorator::Trace(trace_id.expect_value()))?;
546            },
547        }
548
549        Ok(None)
550    }
551}
552
553// HELPER FUNCTIONS
554// ================================================================================================
555
556/// This is a helper function that appends a PUSH operation to the span block which puts the
557/// provided u32 value onto the stack.
558///
559/// When the value is 0, PUSH operation is replaced with PAD. When the value is 1, PUSH operation
560/// is replaced with PAD INCR because in most cases this will be more efficient than doing a PUSH.
561fn push_u32_value(span_builder: &mut BasicBlockBuilder, value: u32) {
562    use Operation::*;
563
564    if value == 0 {
565        span_builder.push_op(Pad);
566    } else if value == 1 {
567        span_builder.push_op(Pad);
568        span_builder.push_op(Incr);
569    } else {
570        span_builder.push_op(Push(Felt::from(value)));
571    }
572}
573
574/// This is a helper function that appends a PUSH operation to the span block which puts the
575/// provided field element onto the stack.
576///
577/// When the value is 0, PUSH operation is replaced with PAD. When the value is 1, PUSH operation
578/// is replaced with PAD INCR because in most cases this will be more efficient than doing a PUSH.
579fn push_felt(span_builder: &mut BasicBlockBuilder, value: Felt) {
580    use Operation::*;
581
582    if value == ZERO {
583        span_builder.push_op(Pad);
584    } else if value == ONE {
585        span_builder.push_op(Pad);
586        span_builder.push_op(Incr);
587    } else {
588        span_builder.push_op(Push(value));
589    }
590}