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            .ok_or_else(|| self.invalid_invoke_target_report(kind, callee, caller))?;
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                .ok_or_else(|| {
105                    self.invalid_invoke_target_report(InvokeKind::ProcRef, callee, caller)
106                })?;
107            // Note: it's ok to `unwrap()` here since `proc_body_id` was returned from
108            // `mast_forest_builder`
109            block_builder
110                .mast_forest_builder()
111                .get_mast_node(resolved.node)
112                .unwrap()
113                .digest()
114        };
115
116        self.procref_mast_root(mast_root, block_builder)
117    }
118
119    fn procref_mast_root(
120        &self,
121        mast_root: Word,
122        block_builder: &mut BasicBlockBuilder,
123    ) -> Result<(), Report> {
124        // Create an array with `Push` operations containing root elements.
125        // Push in reverse order so that mast_root[0] ends up on top.
126        let ops = mast_root
127            .iter()
128            .rev()
129            .map(|elem| Operation::Push(*elem))
130            .collect::<SmallVec<[_; 4]>>();
131        block_builder.push_ops(ops);
132        Ok(())
133    }
134}