miden_assembly/
procedure.rs

1use alloc::sync::Arc;
2
3use miden_assembly_syntax::{
4    ast::{Path, Visibility, types::FunctionType},
5    debuginfo::{SourceManager, SourceSpan, Spanned},
6};
7use miden_core::{Word, mast::MastNodeId};
8
9use super::GlobalItemIndex;
10
11// PROCEDURE CONTEXT
12// ================================================================================================
13
14/// Information about a procedure currently being compiled.
15pub struct ProcedureContext {
16    source_manager: Arc<dyn SourceManager>,
17    gid: GlobalItemIndex,
18    is_program_entrypoint: bool,
19    span: SourceSpan,
20    path: Arc<Path>,
21    signature: Option<Arc<FunctionType>>,
22    visibility: Visibility,
23    is_kernel: bool,
24    num_locals: u16,
25}
26
27// ------------------------------------------------------------------------------------------------
28/// Constructors
29impl ProcedureContext {
30    pub fn new(
31        gid: GlobalItemIndex,
32        is_program_entrypoint: bool,
33        path: Arc<Path>,
34        visibility: Visibility,
35        signature: Option<Arc<FunctionType>>,
36        is_kernel: bool,
37        source_manager: Arc<dyn SourceManager>,
38    ) -> Self {
39        Self {
40            source_manager,
41            gid,
42            is_program_entrypoint,
43            span: SourceSpan::UNKNOWN,
44            path,
45            visibility,
46            signature,
47            is_kernel,
48            num_locals: 0,
49        }
50    }
51
52    /// Sets the number of locals to allocate for the procedure.
53    pub fn with_num_locals(mut self, num_locals: u16) -> Self {
54        self.num_locals = num_locals;
55        self
56    }
57
58    pub fn with_span(mut self, span: SourceSpan) -> Self {
59        self.span = span;
60        self
61    }
62}
63
64// ------------------------------------------------------------------------------------------------
65/// Public accessors
66impl ProcedureContext {
67    pub fn id(&self) -> GlobalItemIndex {
68        self.gid
69    }
70
71    pub fn is_program_entrypoint(&self) -> bool {
72        self.is_program_entrypoint
73    }
74
75    pub fn path(&self) -> &Arc<Path> {
76        &self.path
77    }
78
79    pub fn signature(&self) -> Option<Arc<FunctionType>> {
80        self.signature.clone()
81    }
82
83    pub fn set_signature(&mut self, signature: Option<Arc<FunctionType>>) {
84        self.signature = signature;
85    }
86
87    pub fn num_locals(&self) -> u16 {
88        self.num_locals
89    }
90
91    pub fn module(&self) -> &Path {
92        self.path.parent().unwrap()
93    }
94
95    /// Returns true if the procedure is being assembled for a kernel.
96    pub fn is_kernel(&self) -> bool {
97        self.is_kernel
98    }
99
100    #[inline(always)]
101    pub fn source_manager(&self) -> &dyn SourceManager {
102        self.source_manager.as_ref()
103    }
104}
105
106// ------------------------------------------------------------------------------------------------
107/// State mutators
108impl ProcedureContext {
109    /// Transforms this procedure context into a [Procedure].
110    ///
111    /// The passed-in `mast_root` defines the MAST root of the procedure's body while
112    /// `mast_node_id` specifies the ID of the procedure's body node in the MAST forest in
113    /// which the procedure is defined. Note that if the procedure is re-exported (i.e., the body
114    /// of the procedure is defined in some other MAST forest) `mast_node_id` will point to a
115    /// single `External` node.
116    ///
117    /// <div class="warning">
118    /// `mast_root` and `mast_node_id` must be consistent. That is, the node located in the MAST
119    /// forest under `mast_node_id` must have the digest equal to the `mast_root`.
120    /// </div>
121    pub fn into_procedure(self, mast_root: Word, mast_node_id: MastNodeId) -> Procedure {
122        let is_syscall = self.is_kernel && self.visibility.is_public();
123        Procedure::new(
124            self.path,
125            self.visibility,
126            self.signature,
127            is_syscall,
128            self.num_locals as u32,
129            mast_root,
130            mast_node_id,
131        )
132        .with_span(self.span)
133    }
134}
135
136impl Spanned for ProcedureContext {
137    fn span(&self) -> SourceSpan {
138        self.span
139    }
140}
141
142// PROCEDURE
143// ================================================================================================
144
145/// A compiled Miden Assembly procedure, consisting of MAST info and basic metadata.
146///
147/// Procedure metadata includes:
148///
149/// - Fully-qualified path of the procedure in Miden Assembly (if known).
150/// - Number of procedure locals to allocate.
151/// - The visibility of the procedure (e.g. public/private/syscall)
152/// - The set of MAST roots invoked by this procedure.
153/// - The original source span and file of the procedure (if available).
154#[derive(Clone, Debug)]
155pub struct Procedure {
156    span: SourceSpan,
157    path: Arc<Path>,
158    signature: Option<Arc<FunctionType>>,
159    visibility: Visibility,
160    is_syscall: bool,
161    num_locals: u32,
162    /// The MAST root of the procedure.
163    mast_root: Word,
164    /// The MAST node id which resolves to the above MAST root.
165    body_node_id: MastNodeId,
166}
167
168// ------------------------------------------------------------------------------------------------
169/// Constructors
170impl Procedure {
171    fn new(
172        path: Arc<Path>,
173        visibility: Visibility,
174        signature: Option<Arc<FunctionType>>,
175        is_syscall: bool,
176        num_locals: u32,
177        mast_root: Word,
178        body_node_id: MastNodeId,
179    ) -> Self {
180        Self {
181            span: SourceSpan::default(),
182            path,
183            visibility,
184            signature,
185            is_syscall,
186            num_locals,
187            mast_root,
188            body_node_id,
189        }
190    }
191
192    pub(crate) fn with_span(mut self, span: SourceSpan) -> Self {
193        self.span = span;
194        self
195    }
196}
197
198// ------------------------------------------------------------------------------------------------
199/// Public accessors
200impl Procedure {
201    /// Returns a reference to the fully-qualified name of this procedure
202    pub fn path(&self) -> &Arc<Path> {
203        &self.path
204    }
205
206    /// Returns true if this procedure is a syscallable procedure
207    #[inline(always)]
208    pub const fn is_syscall(&self) -> bool {
209        self.is_syscall
210    }
211
212    /// Returns the visibility of this procedure as expressed in the original source code
213    pub fn visibility(&self) -> Visibility {
214        self.visibility
215    }
216
217    /// Returns a reference to the fully-qualified module path of this procedure
218    pub fn module(&self) -> &Path {
219        self.path.parent().unwrap()
220    }
221
222    /// Returns a reference to the type signature of this procedure
223    pub fn signature(&self) -> Option<Arc<FunctionType>> {
224        self.signature.clone()
225    }
226
227    /// Returns the number of memory locals reserved by the procedure.
228    pub fn num_locals(&self) -> u32 {
229        self.num_locals
230    }
231
232    /// Returns the root of this procedure's MAST.
233    pub fn mast_root(&self) -> Word {
234        self.mast_root
235    }
236
237    /// Returns a reference to the MAST node ID of this procedure.
238    pub fn body_node_id(&self) -> MastNodeId {
239        self.body_node_id
240    }
241}
242
243impl Spanned for Procedure {
244    fn span(&self) -> SourceSpan {
245        self.span
246    }
247}