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