Skip to main content

miden_assembly/instruction/
procedures.rs

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