miden_assembly/instruction/
mod.rs

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