miden_assembly/assembler/instruction/
mod.rs

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