miden_assembly/instruction/
mod.rs

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