Skip to main content

miden_assembly/instruction/
procedures.rs

1use alloc::vec::Vec;
2
3use miden_assembly_syntax::{
4    Word,
5    ast::{InvocationTarget, InvokeKind},
6    diagnostics::Report,
7};
8use miden_core::operations::{AssemblyOp, Operation};
9use miden_mast_package::debug_info::DebugSourceInlineCall;
10use smallvec::SmallVec;
11
12use crate::{
13    Assembler, GlobalItemIndex,
14    basic_block_builder::BasicBlockBuilder,
15    mast_forest_builder::{MastForestBuilder, MastNodeUse},
16};
17
18/// Procedure Invocation
19impl Assembler {
20    /// Returns the [`MastNodeRef`] of the invoked procedure specified by `callee`.
21    ///
22    /// For example, given `exec.f`, this method would return the procedure body id of `f`. If the
23    /// only representation of `f` that we have is its MAST root, then this method will also insert
24    /// a [`core::mast::ExternalNode`] that wraps `f`'s MAST root and return the corresponding id.
25    pub(super) fn invoke(
26        &self,
27        kind: InvokeKind,
28        callee: &InvocationTarget,
29        caller: GlobalItemIndex,
30        mast_forest_builder: &mut MastForestBuilder,
31        asm_op: Option<AssemblyOp>,
32        inline_calls: Vec<DebugSourceInlineCall>,
33    ) -> Result<MastNodeUse, Report> {
34        let resolved = self.resolve_target(kind, callee, caller.module, mast_forest_builder)?;
35
36        match kind {
37            InvokeKind::ProcRef => Ok(resolved.node),
38            InvokeKind::Exec => {
39                mast_forest_builder.record_exec_inline_calls(resolved.node, &inline_calls)
40            },
41            InvokeKind::Call | InvokeKind::SysCall => mast_forest_builder.ensure_call_node_use(
42                resolved.node,
43                matches!(kind, InvokeKind::SysCall),
44                asm_op.expect("call and syscall invocations must provide an AssemblyOp"),
45                inline_calls,
46            ),
47        }
48    }
49
50    /// Creates a new DYN block for the dynamic code execution and return.
51    pub(super) fn dynexec(
52        &self,
53        mast_forest_builder: &mut MastForestBuilder,
54        asm_op: AssemblyOp,
55        inline_calls: Vec<DebugSourceInlineCall>,
56    ) -> Result<Option<MastNodeUse>, Report> {
57        let dyn_node_ref = mast_forest_builder.ensure_dyn_node_use(false, asm_op, inline_calls)?;
58
59        Ok(Some(dyn_node_ref))
60    }
61
62    /// Creates a new DYNCALL block for the dynamic function call and return.
63    pub(super) fn dyncall(
64        &self,
65        mast_forest_builder: &mut MastForestBuilder,
66        asm_op: AssemblyOp,
67        inline_calls: Vec<DebugSourceInlineCall>,
68    ) -> Result<Option<MastNodeUse>, Report> {
69        let dyn_call_node_ref =
70            mast_forest_builder.ensure_dyn_node_use(true, asm_op, inline_calls)?;
71
72        Ok(Some(dyn_call_node_ref))
73    }
74
75    pub(super) fn procref(
76        &self,
77        callee: &InvocationTarget,
78        caller: GlobalItemIndex,
79        block_builder: &mut BasicBlockBuilder,
80    ) -> Result<(), Report> {
81        let mast_root = {
82            let resolved = self.resolve_target(
83                InvokeKind::ProcRef,
84                callee,
85                caller.module,
86                block_builder.mast_forest_builder_mut(),
87            )?;
88            // Note: it's ok to `unwrap()` here since `proc_body_id` was returned from
89            // `mast_forest_builder`
90            block_builder
91                .mast_forest_builder()
92                .mast_root_for_ref(resolved.node.node_ref())
93                .unwrap()
94        };
95
96        self.procref_mast_root(mast_root, block_builder);
97        Ok(())
98    }
99
100    fn procref_mast_root(&self, mast_root: Word, block_builder: &mut BasicBlockBuilder) {
101        // Create an array with `Push` operations containing root elements.
102        // Push in reverse order so that mast_root[0] ends up on top.
103        let ops = mast_root
104            .iter()
105            .rev()
106            .map(|elem| Operation::Push(*elem))
107            .collect::<SmallVec<[_; 4]>>();
108        block_builder.push_ops(ops);
109    }
110}