miden_assembly/assembler/instruction/
mod.rs

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