miden_assembly/instruction/
procedures.rs

1use miden_assembly_syntax::{
2    Word,
3    ast::{InvocationTarget, InvokeKind},
4    diagnostics::Report,
5};
6use miden_core::{
7    Operation,
8    mast::{MastNodeExt, MastNodeId},
9};
10use smallvec::SmallVec;
11
12use crate::{
13    Assembler, GlobalProcedureIndex, basic_block_builder::BasicBlockBuilder,
14    mast_forest_builder::MastForestBuilder,
15};
16
17/// Procedure Invocation
18impl Assembler {
19    /// Returns the [`MastNodeId`] of the invoked procedure specified by `callee`.
20    ///
21    /// For example, given `exec.f`, this method would return the procedure body id of `f`. If the
22    /// only representation of `f` that we have is its MAST root, then this method will also insert
23    /// a [`core::mast::ExternalNode`] that wraps `f`'s MAST root and return the corresponding id.
24    pub(super) fn invoke(
25        &self,
26        kind: InvokeKind,
27        callee: &InvocationTarget,
28        caller: GlobalProcedureIndex,
29        mast_forest_builder: &mut MastForestBuilder,
30    ) -> Result<MastNodeId, Report> {
31        let resolved = self.resolve_target(kind, callee, caller, mast_forest_builder)?;
32
33        match kind {
34            InvokeKind::ProcRef | InvokeKind::Exec => Ok(resolved.node),
35            InvokeKind::Call => mast_forest_builder.ensure_call(resolved.node),
36            InvokeKind::SysCall => mast_forest_builder.ensure_syscall(resolved.node),
37        }
38    }
39
40    /// Creates a new DYN block for the dynamic code execution and return.
41    pub(super) fn dynexec(
42        &self,
43        mast_forest_builder: &mut MastForestBuilder,
44    ) -> Result<Option<MastNodeId>, Report> {
45        let dyn_node_id = mast_forest_builder.ensure_dyn()?;
46
47        Ok(Some(dyn_node_id))
48    }
49
50    /// Creates a new DYNCALL block for the dynamic function call and return.
51    pub(super) fn dyncall(
52        &self,
53        mast_forest_builder: &mut MastForestBuilder,
54    ) -> Result<Option<MastNodeId>, Report> {
55        let dyn_call_node_id = mast_forest_builder.ensure_dyncall()?;
56
57        Ok(Some(dyn_call_node_id))
58    }
59
60    pub(super) fn procref(
61        &self,
62        callee: &InvocationTarget,
63        caller: GlobalProcedureIndex,
64        block_builder: &mut BasicBlockBuilder,
65    ) -> Result<(), Report> {
66        let mast_root = {
67            let resolved = self.resolve_target(
68                InvokeKind::ProcRef,
69                callee,
70                caller,
71                block_builder.mast_forest_builder_mut(),
72            )?;
73            // Note: it's ok to `unwrap()` here since `proc_body_id` was returned from
74            // `mast_forest_builder`
75            block_builder
76                .mast_forest_builder()
77                .get_mast_node(resolved.node)
78                .unwrap()
79                .digest()
80        };
81
82        self.procref_mast_root(mast_root, block_builder)
83    }
84
85    fn procref_mast_root(
86        &self,
87        mast_root: Word,
88        block_builder: &mut BasicBlockBuilder,
89    ) -> Result<(), Report> {
90        // Create an array with `Push` operations containing root elements
91        let ops = mast_root
92            .iter()
93            .map(|elem| Operation::Push(*elem))
94            .collect::<SmallVec<[_; 4]>>();
95        block_builder.push_ops(ops);
96        Ok(())
97    }
98}