miden_assembly/instruction/
procedures.rs

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