miden_assembly/instruction/
mod.rs

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