Skip to main content

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