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::{
9    mast::{CallNodeBuilder, DynNodeBuilder, MastForestContributor, MastNodeExt, MastNodeId},
10    operations::{AssemblyOp, Operation},
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        asm_op: Option<AssemblyOp>,
34    ) -> Result<MastNodeId, Report> {
35        let resolved = self
36            .resolve_target(kind, callee, caller, mast_forest_builder)?
37            .expect("invocation target is not a procedure");
38
39        match kind {
40            InvokeKind::ProcRef | InvokeKind::Exec => Ok(resolved.node),
41            InvokeKind::Call => mast_forest_builder.ensure_node_with_asm_op(
42                CallNodeBuilder::new(resolved.node)
43                    .with_before_enter(before_enter)
44                    .with_after_exit(vec![]),
45                asm_op.expect("call invocations must provide an AssemblyOp"),
46            ),
47            InvokeKind::SysCall => mast_forest_builder.ensure_node_with_asm_op(
48                CallNodeBuilder::new_syscall(resolved.node)
49                    .with_before_enter(before_enter)
50                    .with_after_exit(vec![]),
51                asm_op.expect("syscall invocations must provide an AssemblyOp"),
52            ),
53        }
54    }
55
56    /// Creates a new DYN block for the dynamic code execution and return.
57    pub(super) fn dynexec(
58        &self,
59        mast_forest_builder: &mut MastForestBuilder,
60        before_enter: Vec<miden_core::mast::DecoratorId>,
61        asm_op: AssemblyOp,
62    ) -> Result<Option<MastNodeId>, Report> {
63        let dyn_node_id = mast_forest_builder.ensure_node_with_asm_op(
64            DynNodeBuilder::new_dyn()
65                .with_before_enter(before_enter)
66                .with_after_exit(vec![]),
67            asm_op,
68        )?;
69
70        Ok(Some(dyn_node_id))
71    }
72
73    /// Creates a new DYNCALL block for the dynamic function call and return.
74    pub(super) fn dyncall(
75        &self,
76        mast_forest_builder: &mut MastForestBuilder,
77        before_enter: Vec<miden_core::mast::DecoratorId>,
78        asm_op: AssemblyOp,
79    ) -> Result<Option<MastNodeId>, Report> {
80        let dyn_call_node_id = mast_forest_builder.ensure_node_with_asm_op(
81            DynNodeBuilder::new_dyncall()
82                .with_before_enter(before_enter)
83                .with_after_exit(vec![]),
84            asm_op,
85        )?;
86
87        Ok(Some(dyn_call_node_id))
88    }
89
90    pub(super) fn procref(
91        &self,
92        callee: &InvocationTarget,
93        caller: GlobalItemIndex,
94        block_builder: &mut BasicBlockBuilder,
95    ) -> Result<(), Report> {
96        let mast_root = {
97            let resolved = self
98                .resolve_target(
99                    InvokeKind::ProcRef,
100                    callee,
101                    caller,
102                    block_builder.mast_forest_builder_mut(),
103                )?
104                .expect("invocation target is not a procedure");
105            // Note: it's ok to `unwrap()` here since `proc_body_id` was returned from
106            // `mast_forest_builder`
107            block_builder
108                .mast_forest_builder()
109                .get_mast_node(resolved.node)
110                .unwrap()
111                .digest()
112        };
113
114        self.procref_mast_root(mast_root, block_builder)
115    }
116
117    fn procref_mast_root(
118        &self,
119        mast_root: Word,
120        block_builder: &mut BasicBlockBuilder,
121    ) -> Result<(), Report> {
122        // Create an array with `Push` operations containing root elements.
123        // Push in reverse order so that mast_root[0] ends up on top.
124        let ops = mast_root
125            .iter()
126            .rev()
127            .map(|elem| Operation::Push(*elem))
128            .collect::<SmallVec<[_; 4]>>();
129        block_builder.push_ops(ops);
130        Ok(())
131    }
132}