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