1pub(super) mod debuginfo;
2mod error;
3mod product;
4
5use alloc::{boxed::Box, collections::BTreeMap, string::ToString, sync::Arc, vec::Vec};
6
7use debuginfo::DebugInfoSections;
8use miden_assembly_syntax::{
9 KernelLibrary, Library, MAX_REPEAT_COUNT, Parse, ParseOptions, SemanticAnalysisError,
10 ast::{
11 self, Ident, InvocationTarget, InvokeKind, ItemIndex, ModuleKind, SymbolResolution,
12 Visibility, types::FunctionType,
13 },
14 debuginfo::{DefaultSourceManager, SourceManager, SourceSpan, Spanned},
15 diagnostics::{IntoDiagnostic, RelatedLabel, Report},
16 library::{ConstantExport, ItemInfo, LibraryExport, ProcedureExport, TypeExport},
17};
18use miden_core::{
19 Word,
20 mast::{
21 DecoratorId, LoopNodeBuilder, MastForestContributor, MastNodeExt, MastNodeId,
22 SplitNodeBuilder,
23 },
24 operations::{AssemblyOp, Operation},
25 program::{Kernel, Program},
26};
27use miden_mast_package::PackageManifest;
28use miden_project::{Linkage, TargetType};
29
30use self::{error::AssemblerError, product::AssemblyProduct};
31use crate::{
32 GlobalItemIndex, ModuleIndex, Procedure, ProcedureContext,
33 ast::Path,
34 basic_block_builder::{BasicBlockBuilder, BasicBlockOrDecorators},
35 fmp::{fmp_end_frame_sequence, fmp_initialization_sequence, fmp_start_frame_sequence},
36 linker::{LinkLibrary, Linker, LinkerError, SymbolItem, SymbolResolutionContext},
37 mast_forest_builder::MastForestBuilder,
38};
39
40pub(crate) const MAX_CONTROL_FLOW_NESTING: usize = 256;
45
46#[derive(Clone)]
95pub struct Assembler {
96 source_manager: Arc<dyn SourceManager>,
98 linker: Box<Linker>,
100 pub(super) debug_info: DebugInfoSections,
102 warnings_as_errors: bool,
104 pub(super) emit_debug_info: bool,
106 pub(super) trim_paths: bool,
108}
109
110impl Default for Assembler {
111 fn default() -> Self {
112 let source_manager = Arc::new(DefaultSourceManager::default());
113 let linker = Box::new(Linker::new(source_manager.clone()));
114 Self {
115 source_manager,
116 linker,
117 debug_info: Default::default(),
118 warnings_as_errors: false,
119 emit_debug_info: true,
120 trim_paths: false,
121 }
122 }
123}
124
125impl Assembler {
128 pub fn new(source_manager: Arc<dyn SourceManager>) -> Self {
130 let linker = Box::new(Linker::new(source_manager.clone()));
131 Self {
132 source_manager,
133 linker,
134 debug_info: Default::default(),
135 warnings_as_errors: false,
136 emit_debug_info: true,
137 trim_paths: false,
138 }
139 }
140
141 pub fn with_kernel(source_manager: Arc<dyn SourceManager>, kernel_lib: KernelLibrary) -> Self {
143 let (kernel, kernel_module, _) = kernel_lib.into_parts();
144 let linker = Box::new(Linker::with_kernel(source_manager.clone(), kernel, kernel_module));
145 Self {
146 source_manager,
147 linker,
148 ..Default::default()
149 }
150 }
151
152 pub fn with_warnings_as_errors(mut self, yes: bool) -> Self {
156 self.warnings_as_errors = yes;
157 self
158 }
159
160 #[cfg(feature = "std")]
161 pub(crate) fn with_emit_debug_info(mut self, yes: bool) -> Self {
162 self.emit_debug_info = yes;
163 self
164 }
165
166 #[cfg(feature = "std")]
167 pub(crate) fn with_trim_paths(mut self, yes: bool) -> Self {
168 self.trim_paths = yes;
169 self
170 }
171}
172
173impl Assembler {
176 #[inline]
180 pub fn compile_and_statically_link(&mut self, module: impl Parse) -> Result<&mut Self, Report> {
181 self.compile_and_statically_link_all([module])
182 }
183
184 pub fn compile_and_statically_link_all(
189 &mut self,
190 modules: impl IntoIterator<Item = impl Parse>,
191 ) -> Result<&mut Self, Report> {
192 let modules = modules
193 .into_iter()
194 .map(|module| {
195 module.parse_with_options(
196 self.source_manager.clone(),
197 ParseOptions {
198 warnings_as_errors: self.warnings_as_errors,
199 ..ParseOptions::for_library()
200 },
201 )
202 })
203 .collect::<Result<Vec<_>, Report>>()?;
204
205 self.linker.link_modules(modules)?;
206
207 Ok(self)
208 }
209
210 #[cfg(feature = "std")]
242 pub fn compile_and_statically_link_from_dir(
243 &mut self,
244 dir: impl AsRef<std::path::Path>,
245 namespace: impl AsRef<Path>,
246 ) -> Result<(), Report> {
247 use miden_assembly_syntax::parser;
248
249 let namespace = namespace.as_ref();
250 let modules = parser::read_modules_from_dir(
251 dir,
252 namespace,
253 self.source_manager.clone(),
254 self.warnings_as_errors,
255 )?;
256 self.linker.link_modules(modules)?;
257 Ok(())
258 }
259
260 pub fn link_library(
274 &mut self,
275 library: impl AsRef<Library>,
276 linkage: Linkage,
277 ) -> Result<(), Report> {
278 self.linker
279 .link_library(LinkLibrary::from_library(library.as_ref()).with_linkage(linkage))
280 .map_err(Report::from)
281 }
282
283 pub fn link_dynamic_library(&mut self, library: impl AsRef<Library>) -> Result<(), Report> {
309 let library = LinkLibrary::from_library(library.as_ref()).with_linkage(Linkage::Dynamic);
310 self.linker.link_library(library).map_err(Report::from)
311 }
312
313 pub fn with_dynamic_library(mut self, library: impl AsRef<Library>) -> Result<Self, Report> {
317 self.link_dynamic_library(library)?;
318 Ok(self)
319 }
320
321 pub fn link_static_library(&mut self, library: impl AsRef<Library>) -> Result<(), Report> {
330 let library = LinkLibrary::from_library(library.as_ref()).with_linkage(Linkage::Static);
331 self.linker.link_library(library).map_err(Report::from)
332 }
333
334 pub fn with_static_library(mut self, library: impl AsRef<Library>) -> Result<Self, Report> {
338 self.link_static_library(library)?;
339 Ok(self)
340 }
341
342 pub fn link_package(
344 &mut self,
345 package: Arc<miden_mast_package::Package>,
346 linkage: Linkage,
347 ) -> Result<(), Report> {
348 match package.kind {
349 TargetType::Kernel => {
350 if !self.kernel().is_empty() {
351 return Err(Report::msg(format!(
352 "duplicate kernels present in the dependency graph: '{}@{
353 }' conflicts with another kernel we've already linked",
354 &package.name, &package.version
355 )));
356 }
357
358 let kernel_module = package.kernel_module_info()?;
359 let kernel = package.to_kernel()?;
360 self.linker.link_with_kernel(kernel, kernel_module)?;
361 Ok(())
362 },
363 TargetType::Executable => {
364 Err(Report::msg("cannot add executable packages to an assembler"))
365 },
366 _ => {
367 self.linker
368 .link_library(LinkLibrary::from_package(package).with_linkage(linkage))?;
369 Ok(())
370 },
371 }
372 }
373}
374
375impl Assembler {
378 pub fn warnings_as_errors(&self) -> bool {
380 self.warnings_as_errors
381 }
382
383 pub fn kernel(&self) -> &Kernel {
387 self.linker.kernel()
388 }
389
390 #[cfg(any(feature = "std", all(test, feature = "std")))]
391 pub(crate) fn source_manager(&self) -> Arc<dyn SourceManager> {
392 self.source_manager.clone()
393 }
394
395 #[cfg(any(test, feature = "testing"))]
396 #[doc(hidden)]
397 pub fn linker(&self) -> &Linker {
398 &self.linker
399 }
400}
401
402impl Assembler {
405 pub fn assemble_library(
411 self,
412 modules: impl IntoIterator<Item = impl Parse>,
413 ) -> Result<Arc<Library>, Report> {
414 let modules = modules
415 .into_iter()
416 .map(|module| {
417 module.parse_with_options(
418 self.source_manager.clone(),
419 ParseOptions {
420 warnings_as_errors: self.warnings_as_errors,
421 ..ParseOptions::for_library()
422 },
423 )
424 })
425 .collect::<Result<Vec<_>, Report>>()?;
426
427 Ok(self.assemble_library_modules(modules, TargetType::Library)?.into_artifact())
428 }
429
430 #[cfg(feature = "std")]
465 pub fn assemble_library_from_dir(
466 self,
467 dir: impl AsRef<std::path::Path>,
468 namespace: impl AsRef<Path>,
469 ) -> Result<Arc<Library>, Report> {
470 use miden_assembly_syntax::parser;
471
472 let dir = dir.as_ref();
473 let namespace = namespace.as_ref();
474
475 let source_manager = self.source_manager.clone();
476 let modules =
477 parser::read_modules_from_dir(dir, namespace, source_manager, self.warnings_as_errors)?;
478 self.assemble_library(modules)
479 }
480
481 pub fn assemble_kernel(self, module: impl Parse) -> Result<KernelLibrary, Report> {
487 let module = module.parse_with_options(
488 self.source_manager.clone(),
489 ParseOptions {
490 path: Some(Path::kernel_path().into()),
491 warnings_as_errors: self.warnings_as_errors,
492 ..ParseOptions::for_kernel()
493 },
494 )?;
495
496 self.assemble_kernel_module(module)?.into_kernel_library()
497 }
498
499 #[cfg(feature = "std")]
513 pub fn assemble_kernel_from_dir(
514 mut self,
515 sys_module_path: impl AsRef<std::path::Path>,
516 lib_dir: Option<impl AsRef<std::path::Path>>,
517 ) -> Result<KernelLibrary, Report> {
518 if let Some(lib_dir) = lib_dir {
520 self.compile_and_statically_link_from_dir(lib_dir, Path::kernel_path())?;
521 }
522
523 self.assemble_kernel(sys_module_path.as_ref())
524 }
525
526 fn assemble_library_product(
528 mut self,
529 module_indices: &[ModuleIndex],
530 kind: TargetType,
531 ) -> Result<AssemblyProduct, Report> {
532 let staticlibs = self.linker.libraries().filter_map(|lib| {
533 if matches!(lib.linkage, Linkage::Static) {
534 Some(lib.mast.as_ref())
535 } else {
536 None
537 }
538 });
539 let mut mast_forest_builder = MastForestBuilder::new(staticlibs)?;
540 let mut exports = {
541 let mut exports = BTreeMap::new();
542
543 for module_idx in module_indices.iter().copied() {
544 let module = &self.linker[module_idx];
545
546 if let Some(advice_map) = module.advice_map() {
547 mast_forest_builder.merge_advice_map(advice_map)?;
548 }
549
550 let module_kind = module.kind();
551 let module_path = module.path().clone();
552 for index in 0..module.symbols().len() {
553 let index = ItemIndex::new(index);
554 let gid = module_idx + index;
555
556 let path: Arc<Path> = {
557 let symbol = &self.linker[gid];
558 if !symbol.visibility().is_public() {
559 continue;
560 }
561 module_path.join(symbol.name()).into()
562 };
563 let export = self.export_symbol(
564 gid,
565 module_kind,
566 path.clone(),
567 &mut mast_forest_builder,
568 )?;
569 exports.insert(path, export);
570 }
571 }
572
573 exports
574 };
575
576 let (mast_forest, id_remappings) = mast_forest_builder.build();
577 for (_proc_name, export) in exports.iter_mut() {
578 match export {
579 LibraryExport::Procedure(export) => {
580 if let Some(&new_node_id) = id_remappings.get(&export.node) {
581 export.node = new_node_id;
582 }
583 },
584 LibraryExport::Constant(_) | LibraryExport::Type(_) => (),
585 }
586 }
587
588 self.finish_library_product(mast_forest, exports, kind)
589 }
590
591 fn export_symbol(
597 &mut self,
598 gid: GlobalItemIndex,
599 module_kind: ModuleKind,
600 symbol_path: Arc<Path>,
601 mast_forest_builder: &mut MastForestBuilder,
602 ) -> Result<LibraryExport, Report> {
603 log::trace!(target: "assembler::export_symbol", "exporting {} {symbol_path}", match self.linker[gid].item() {
604 SymbolItem::Compiled(ItemInfo::Procedure(_)) => "compiled procedure",
605 SymbolItem::Compiled(ItemInfo::Constant(_)) => "compiled constant",
606 SymbolItem::Compiled(ItemInfo::Type(_)) => "compiled type",
607 SymbolItem::Procedure(_) => "procedure",
608 SymbolItem::Constant(_) => "constant",
609 SymbolItem::Type(_) => "type",
610 SymbolItem::Alias { .. } => "alias",
611 });
612 let mut cache = crate::linker::ResolverCache::default();
613 let export = match self.linker[gid].item() {
614 SymbolItem::Compiled(ItemInfo::Procedure(item)) => {
615 let resolved = match mast_forest_builder.get_procedure(gid) {
616 Some(proc) => ResolvedProcedure {
617 node: proc.body_node_id(),
618 signature: proc.signature(),
619 },
620 None => {
623 let node = self.ensure_valid_procedure_mast_root(
624 InvokeKind::ProcRef,
625 SourceSpan::UNKNOWN,
626 item.digest,
627 mast_forest_builder,
628 )?;
629 ResolvedProcedure { node, signature: item.signature.clone() }
630 },
631 };
632 let digest = item.digest;
633 let ResolvedProcedure { node, signature } = resolved;
634 let attributes = item.attributes.clone();
635 let pctx = ProcedureContext::new(
636 gid,
637 false,
638 symbol_path.clone(),
639 Visibility::Public,
640 signature.clone(),
641 module_kind.is_kernel(),
642 self.source_manager.clone(),
643 );
644
645 let procedure = pctx.into_procedure(digest, node);
646 self.linker.register_procedure_root(gid, digest)?;
647 mast_forest_builder.insert_procedure(gid, procedure)?;
648 LibraryExport::Procedure(ProcedureExport {
649 node,
650 path: symbol_path,
651 signature: signature.map(|sig| (*sig).clone()),
652 attributes,
653 })
654 },
655 SymbolItem::Compiled(ItemInfo::Constant(item)) => {
656 LibraryExport::Constant(ConstantExport {
657 path: symbol_path,
658 value: item.value.clone(),
659 })
660 },
661 SymbolItem::Compiled(ItemInfo::Type(item)) => {
662 LibraryExport::Type(TypeExport { path: symbol_path, ty: item.ty.clone() })
663 },
664 SymbolItem::Procedure(_) => {
665 self.compile_subgraph(SubgraphRoot::not_as_entrypoint(gid), mast_forest_builder)?;
666 let node = mast_forest_builder
667 .get_procedure(gid)
668 .expect("compilation succeeded but root not found in cache")
669 .body_node_id();
670 let signature = self.linker.resolve_signature(gid)?;
671 let attributes = self.linker.resolve_attributes(gid)?;
672 LibraryExport::Procedure(ProcedureExport {
673 node,
674 path: symbol_path,
675 signature: signature.map(Arc::unwrap_or_clone),
676 attributes,
677 })
678 },
679 SymbolItem::Constant(item) => {
680 let value = self.linker.const_eval(gid, &item.value, &mut cache)?;
682
683 LibraryExport::Constant(ConstantExport { path: symbol_path, value })
684 },
685 SymbolItem::Type(item) => {
686 let ty = self.linker.resolve_type(item.span(), gid)?;
687 LibraryExport::Type(TypeExport { path: symbol_path, ty })
688 },
689
690 SymbolItem::Alias { alias, resolved } => {
691 let resolved = resolved.get().unwrap_or_else(|| {
693 panic!("unresolved alias {symbol_path} targeting: {}", alias.target())
694 });
695 return self.export_symbol(resolved, module_kind, symbol_path, mast_forest_builder);
696 },
697 };
698
699 Ok(export)
700 }
701
702 pub fn assemble_program(self, source: impl Parse) -> Result<Program, Report> {
710 let options = ParseOptions {
711 kind: ModuleKind::Executable,
712 warnings_as_errors: self.warnings_as_errors,
713 path: Some(Path::exec_path().into()),
714 };
715
716 let program = source.parse_with_options(self.source_manager.clone(), options)?;
717 assert!(program.is_executable());
718
719 self.assemble_executable_modules(program, [])?.into_program()
720 }
721
722 pub(crate) fn assemble_library_modules(
723 mut self,
724 modules: impl IntoIterator<Item = Box<ast::Module>>,
725 kind: TargetType,
726 ) -> Result<AssemblyProduct, Report> {
727 let module_indices = self.linker.link(modules)?;
728 self.assemble_library_product(&module_indices, kind)
729 }
730
731 pub(crate) fn assemble_kernel_module(
732 mut self,
733 module: Box<ast::Module>,
734 ) -> Result<AssemblyProduct, Report> {
735 let module_indices = self.linker.link_kernel(module)?;
736 self.assemble_library_product(&module_indices, TargetType::Kernel)
737 }
738
739 pub(crate) fn assemble_executable_modules(
740 mut self,
741 program: Box<ast::Module>,
742 support_modules: impl IntoIterator<Item = Box<ast::Module>>,
743 ) -> Result<AssemblyProduct, Report> {
744 self.linker.link_modules(support_modules)?;
745
746 let module_index = self.linker.link([program])?[0];
748
749 let entrypoint = self.linker[module_index]
752 .symbols()
753 .position(|symbol| symbol.name().as_str() == Ident::MAIN)
754 .map(|index| module_index + ItemIndex::new(index))
755 .ok_or(SemanticAnalysisError::MissingEntrypoint)?;
756
757 let staticlibs = self.linker.libraries().filter_map(|lib| {
759 if matches!(lib.linkage, Linkage::Static) {
760 Some(lib.mast.as_ref())
761 } else {
762 None
763 }
764 });
765 let mut mast_forest_builder = MastForestBuilder::new(staticlibs)?;
766
767 if let Some(advice_map) = self.linker[module_index].advice_map() {
768 mast_forest_builder.merge_advice_map(advice_map)?;
769 }
770
771 self.compile_subgraph(SubgraphRoot::with_entrypoint(entrypoint), &mut mast_forest_builder)?;
772 let entry_node_id = mast_forest_builder
773 .get_procedure(entrypoint)
774 .expect("compilation succeeded but root not found in cache")
775 .body_node_id();
776
777 let (mast_forest, id_remappings) = mast_forest_builder.build();
779 let entry_node_id = *id_remappings.get(&entry_node_id).unwrap_or(&entry_node_id);
780
781 self.finish_program_product(mast_forest, entry_node_id, self.linker.kernel().clone())
782 }
783
784 fn finish_library_product(
785 &self,
786 mut mast_forest: miden_core::mast::MastForest,
787 exports: BTreeMap<Arc<Path>, LibraryExport>,
788 kind: TargetType,
789 ) -> Result<AssemblyProduct, Report> {
790 self.apply_debug_options(&mut mast_forest);
791
792 let library = Library::new(Arc::new(mast_forest), exports)?;
793 let manifest = PackageManifest::from_library(&library);
794 let debug_info = self.emit_debug_info.then(|| {
795 #[cfg_attr(not(feature = "std"), expect(unused_mut))]
796 let mut debug_info = self.debug_info.clone();
797 #[cfg(feature = "std")]
798 if let Some(trimmer) = self.source_path_trimmer() {
799 debug_info.trim_paths(&trimmer);
800 }
801 debug_info
802 });
803
804 Ok(AssemblyProduct::new(kind, Arc::new(library), None, manifest, debug_info))
805 }
806
807 fn finish_program_product(
808 &self,
809 mut mast_forest: miden_core::mast::MastForest,
810 entrypoint: MastNodeId,
811 kernel: Kernel,
812 ) -> Result<AssemblyProduct, Report> {
813 self.apply_debug_options(&mut mast_forest);
814
815 let mast = Arc::new(mast_forest);
816 let entry: Arc<Path> =
817 ast::Path::exec_path().join(ast::ProcedureName::MAIN_PROC_NAME).into();
818 let entrypoint = LibraryExport::Procedure(ProcedureExport {
819 node: entrypoint,
820 path: entry.clone(),
821 signature: None,
822 attributes: Default::default(),
823 });
824 let library = Arc::new(Library::new(mast, BTreeMap::from_iter([(entry, entrypoint)]))?);
825 let manifest = PackageManifest::from_library(&library);
826 let debug_info = self.emit_debug_info.then(|| {
827 #[cfg_attr(not(feature = "std"), expect(unused_mut))]
828 let mut debug_info = self.debug_info.clone();
829 #[cfg(feature = "std")]
830 if let Some(trimmer) = self.source_path_trimmer() {
831 debug_info.trim_paths(&trimmer);
832 }
833 debug_info
834 });
835
836 Ok(AssemblyProduct::new(
837 TargetType::Executable,
838 library,
839 Some(kernel),
840 manifest,
841 debug_info,
842 ))
843 }
844
845 fn apply_debug_options(&self, mast_forest: &mut miden_core::mast::MastForest) {
846 if !self.emit_debug_info {
847 mast_forest.clear_debug_info();
848 return;
849 }
850
851 if self.trim_paths {
852 #[cfg(feature = "std")]
853 if let Some(trimmer) = self.source_path_trimmer() {
854 mast_forest.debug_info_mut().rewrite_source_locations(
855 |location| trimmer.trim_location(location),
856 |location| trimmer.trim_file_line_col(location),
857 );
858 }
859 }
860 }
861
862 #[cfg(feature = "std")]
863 fn source_path_trimmer(&self) -> Option<debuginfo::SourcePathTrimmer> {
864 if !self.trim_paths {
865 return None;
866 }
867
868 std::env::current_dir().ok().map(debuginfo::SourcePathTrimmer::new)
869 }
870
871 fn compile_subgraph(
876 &mut self,
877 root: SubgraphRoot,
878 mast_forest_builder: &mut MastForestBuilder,
879 ) -> Result<(), Report> {
880 let mut worklist: Vec<GlobalItemIndex> = self
881 .linker
882 .topological_sort_from_root(root.proc_id)
883 .map_err(|cycle| {
884 let iter = cycle.into_node_ids();
885 let mut nodes = Vec::with_capacity(iter.len());
886 for node in iter {
887 let module = self.linker[node.module].path();
888 let proc = self.linker[node].name();
889 nodes.push(format!("{}", module.join(proc)));
890 }
891 LinkerError::Cycle { nodes: nodes.into() }
892 })?
893 .into_iter()
894 .filter(|&gid| {
895 matches!(
896 self.linker[gid].item(),
897 SymbolItem::Procedure(_) | SymbolItem::Alias { .. }
898 )
899 })
900 .collect();
901
902 assert!(!worklist.is_empty());
903
904 self.process_graph_worklist(&mut worklist, &root, mast_forest_builder)
905 }
906
907 fn process_graph_worklist(
909 &mut self,
910 worklist: &mut Vec<GlobalItemIndex>,
911 root: &SubgraphRoot,
912 mast_forest_builder: &mut MastForestBuilder,
913 ) -> Result<(), Report> {
914 while let Some(procedure_gid) = worklist.pop() {
917 if let Some(proc) = mast_forest_builder.get_procedure(procedure_gid) {
919 self.linker.register_procedure_root(procedure_gid, proc.mast_root())?;
920 continue;
921 }
922 let (module_kind, module_path) = {
924 let module = &self.linker[procedure_gid.module];
925 (module.kind(), module.path().clone())
926 };
927 match self.linker[procedure_gid].item() {
928 SymbolItem::Procedure(proc) => {
929 let proc = proc.borrow();
930 let num_locals = proc.num_locals();
931 let path = Arc::<Path>::from(module_path.join(proc.name().as_str()));
932 let signature = self.linker.resolve_signature(procedure_gid)?;
933 let is_program_entrypoint =
934 root.is_program_entrypoint && root.proc_id == procedure_gid;
935
936 let pctx = ProcedureContext::new(
937 procedure_gid,
938 is_program_entrypoint,
939 path.clone(),
940 proc.visibility(),
941 signature.clone(),
942 module_kind.is_kernel(),
943 self.source_manager.clone(),
944 )
945 .with_num_locals(num_locals)
946 .with_span(proc.span());
947
948 let procedure = self.compile_procedure(pctx, mast_forest_builder)?;
950 self.debug_info
958 .register_procedure_debug_info(&procedure, self.source_manager.as_ref())?;
959
960 drop(proc);
962 self.linker.register_procedure_root(procedure_gid, procedure.mast_root())?;
963 mast_forest_builder.insert_procedure(procedure_gid, procedure)?;
964 },
965 SymbolItem::Alias { alias, resolved } => {
966 let procedure_gid = resolved.get().expect("resolved alias");
967 match self.linker[procedure_gid].item() {
968 SymbolItem::Procedure(_) | SymbolItem::Compiled(ItemInfo::Procedure(_)) => {
969 },
970 SymbolItem::Constant(_) | SymbolItem::Type(_) | SymbolItem::Compiled(_) => {
971 continue;
972 },
973 SymbolItem::Alias { .. } => unreachable!(),
977 }
978 let path = module_path.join(alias.name().as_str()).into();
979 let is_program_entrypoint = false;
981 let mut pctx = ProcedureContext::new(
982 procedure_gid,
983 is_program_entrypoint,
984 path,
985 ast::Visibility::Public,
986 None,
987 module_kind.is_kernel(),
988 self.source_manager.clone(),
989 )
990 .with_span(alias.span());
991
992 let Some(ResolvedProcedure { node: proc_node_id, signature }) = self
996 .resolve_target(
997 InvokeKind::ProcRef,
998 &alias.target().into(),
999 procedure_gid,
1000 mast_forest_builder,
1001 )?
1002 else {
1003 continue;
1004 };
1005
1006 pctx.set_signature(signature);
1007
1008 let proc_mast_root =
1009 mast_forest_builder.get_mast_node(proc_node_id).unwrap().digest();
1010
1011 let procedure = pctx.into_procedure(proc_mast_root, proc_node_id);
1012
1013 self.linker.register_procedure_root(procedure_gid, proc_mast_root)?;
1015 mast_forest_builder.insert_procedure(procedure_gid, procedure)?;
1016 },
1017 SymbolItem::Compiled(_) | SymbolItem::Constant(_) | SymbolItem::Type(_) => {
1018 },
1020 }
1021 }
1022
1023 Ok(())
1024 }
1025
1026 fn compile_procedure(
1028 &self,
1029 mut proc_ctx: ProcedureContext,
1030 mast_forest_builder: &mut MastForestBuilder,
1031 ) -> Result<Procedure, Report> {
1032 let gid = proc_ctx.id();
1034
1035 let num_locals = proc_ctx.num_locals();
1036
1037 let proc = match self.linker[gid].item() {
1038 SymbolItem::Procedure(proc) => proc.borrow(),
1039 _ => panic!("expected item to be a procedure AST"),
1040 };
1041 let body_wrapper = if proc_ctx.is_program_entrypoint() {
1042 assert!(num_locals == 0, "program entrypoint cannot have locals");
1043
1044 Some(BodyWrapper {
1045 prologue: fmp_initialization_sequence(),
1046 epilogue: Vec::new(),
1047 })
1048 } else if num_locals > 0 {
1049 Some(BodyWrapper {
1050 prologue: fmp_start_frame_sequence(num_locals),
1051 epilogue: fmp_end_frame_sequence(num_locals),
1052 })
1053 } else {
1054 None
1055 };
1056
1057 let proc_body_id =
1058 self.compile_body(proc.iter(), &mut proc_ctx, body_wrapper, mast_forest_builder, 0)?;
1059
1060 let proc_body_node = mast_forest_builder
1061 .get_mast_node(proc_body_id)
1062 .expect("no MAST node for compiled procedure");
1063 Ok(proc_ctx.into_procedure(proc_body_node.digest(), proc_body_id))
1064 }
1065
1066 fn create_asmop_decorator(
1068 &self,
1069 span: &SourceSpan,
1070 op_name: &str,
1071 proc_ctx: &ProcedureContext,
1072 ) -> AssemblyOp {
1073 let location = proc_ctx.source_manager().location(*span).ok();
1074 let context_name = proc_ctx.path().to_string();
1075 let num_cycles = 0;
1076 AssemblyOp::new(location, context_name, num_cycles, op_name.to_string())
1077 }
1078
1079 fn compile_body<'a, I>(
1080 &self,
1081 body: I,
1082 proc_ctx: &mut ProcedureContext,
1083 wrapper: Option<BodyWrapper>,
1084 mast_forest_builder: &mut MastForestBuilder,
1085 nesting_depth: usize,
1086 ) -> Result<MastNodeId, Report>
1087 where
1088 I: Iterator<Item = &'a ast::Op>,
1089 {
1090 use ast::Op;
1091
1092 let mut body_node_ids: Vec<MastNodeId> = Vec::new();
1093 let mut block_builder = BasicBlockBuilder::new(wrapper, mast_forest_builder);
1094
1095 for op in body {
1096 match op {
1097 Op::Inst(inst) => {
1098 if let Some(node_id) =
1099 self.compile_instruction(inst, &mut block_builder, proc_ctx)?
1100 {
1101 if let Some(basic_block_id) = block_builder.make_basic_block()? {
1102 body_node_ids.push(basic_block_id);
1103 } else if let Some(decorator_ids) = block_builder.drain_decorators() {
1104 block_builder
1105 .mast_forest_builder_mut()
1106 .append_before_enter(node_id, decorator_ids)
1107 .into_diagnostic()?;
1108 }
1109
1110 body_node_ids.push(node_id);
1111 }
1112 },
1113
1114 Op::If { then_blk, else_blk, span } => {
1115 if let Some(basic_block_id) = block_builder.make_basic_block()? {
1116 body_node_ids.push(basic_block_id);
1117 }
1118
1119 let next_depth = nesting_depth + 1;
1120 if next_depth > MAX_CONTROL_FLOW_NESTING {
1121 return Err(Report::new(AssemblerError::ControlFlowNestingDepthExceeded {
1122 span: *span,
1123 source_file: proc_ctx.source_manager().get(span.source_id()).ok(),
1124 max_depth: MAX_CONTROL_FLOW_NESTING,
1125 }));
1126 }
1127
1128 let then_blk = self.compile_body(
1129 then_blk.iter(),
1130 proc_ctx,
1131 None,
1132 block_builder.mast_forest_builder_mut(),
1133 next_depth,
1134 )?;
1135 let else_blk = self.compile_body(
1136 else_blk.iter(),
1137 proc_ctx,
1138 None,
1139 block_builder.mast_forest_builder_mut(),
1140 next_depth,
1141 )?;
1142
1143 let mut split_builder = SplitNodeBuilder::new([then_blk, else_blk]);
1144 if let Some(decorator_ids) = block_builder.drain_decorators() {
1145 split_builder.append_before_enter(decorator_ids);
1146 }
1147
1148 let split_node_id =
1149 block_builder.mast_forest_builder_mut().ensure_node(split_builder)?;
1150
1151 let asm_op = self.create_asmop_decorator(span, "if.true", proc_ctx);
1153 block_builder
1154 .mast_forest_builder_mut()
1155 .register_node_asm_op(split_node_id, asm_op)?;
1156
1157 body_node_ids.push(split_node_id);
1158 },
1159
1160 Op::Repeat { count, body, span } => {
1161 if let Some(basic_block_id) = block_builder.make_basic_block()? {
1162 body_node_ids.push(basic_block_id);
1163 }
1164
1165 let next_depth = nesting_depth + 1;
1166 if next_depth > MAX_CONTROL_FLOW_NESTING {
1167 return Err(Report::new(AssemblerError::ControlFlowNestingDepthExceeded {
1168 span: *span,
1169 source_file: proc_ctx.source_manager().get(span.source_id()).ok(),
1170 max_depth: MAX_CONTROL_FLOW_NESTING,
1171 }));
1172 }
1173
1174 let repeat_node_id = self.compile_body(
1175 body.iter(),
1176 proc_ctx,
1177 None,
1178 block_builder.mast_forest_builder_mut(),
1179 next_depth,
1180 )?;
1181
1182 let iteration_count = (*count).expect_value();
1183 if iteration_count == 0 {
1184 return Err(RelatedLabel::error("invalid repeat count")
1185 .with_help("repeat count must be greater than 0")
1186 .with_labeled_span(count.span(), "repeat count must be at least 1")
1187 .with_source_file(
1188 proc_ctx.source_manager().get(proc_ctx.span().source_id()).ok(),
1189 )
1190 .into());
1191 }
1192 if iteration_count > MAX_REPEAT_COUNT {
1193 return Err(RelatedLabel::error("invalid repeat count")
1194 .with_help(format!(
1195 "repeat count must be less than or equal to {MAX_REPEAT_COUNT}",
1196 ))
1197 .with_labeled_span(
1198 count.span(),
1199 format!("repeat count exceeds {MAX_REPEAT_COUNT}"),
1200 )
1201 .with_source_file(
1202 proc_ctx.source_manager().get(proc_ctx.span().source_id()).ok(),
1203 )
1204 .into());
1205 }
1206
1207 if let Some(decorator_ids) = block_builder.drain_decorators() {
1208 let first_repeat_builder = block_builder.mast_forest_builder()
1210 [repeat_node_id]
1211 .clone()
1212 .to_builder(block_builder.mast_forest_builder().mast_forest())
1213 .with_before_enter(decorator_ids);
1214 let first_repeat_node_id = block_builder
1215 .mast_forest_builder_mut()
1216 .ensure_node(first_repeat_builder)?;
1217
1218 body_node_ids.push(first_repeat_node_id);
1219 let remaining_iterations =
1220 iteration_count.checked_sub(1).ok_or_else(|| {
1221 Report::new(
1222 RelatedLabel::error("invalid repeat count")
1223 .with_help("repeat count must be greater than 0")
1224 .with_labeled_span(
1225 count.span(),
1226 "repeat count must be at least 1",
1227 )
1228 .with_source_file(
1229 proc_ctx
1230 .source_manager()
1231 .get(proc_ctx.span().source_id())
1232 .ok(),
1233 ),
1234 )
1235 })?;
1236 for _ in 0..remaining_iterations {
1237 body_node_ids.push(repeat_node_id);
1238 }
1239 } else {
1240 for _ in 0..iteration_count {
1241 body_node_ids.push(repeat_node_id);
1242 }
1243 }
1244 },
1245
1246 Op::While { body, span } => {
1247 if let Some(basic_block_id) = block_builder.make_basic_block()? {
1248 body_node_ids.push(basic_block_id);
1249 }
1250
1251 let next_depth = nesting_depth + 1;
1252 if next_depth > MAX_CONTROL_FLOW_NESTING {
1253 return Err(Report::new(AssemblerError::ControlFlowNestingDepthExceeded {
1254 span: *span,
1255 source_file: proc_ctx.source_manager().get(span.source_id()).ok(),
1256 max_depth: MAX_CONTROL_FLOW_NESTING,
1257 }));
1258 }
1259
1260 let loop_body_node_id = self.compile_body(
1261 body.iter(),
1262 proc_ctx,
1263 None,
1264 block_builder.mast_forest_builder_mut(),
1265 next_depth,
1266 )?;
1267 let mut loop_builder = LoopNodeBuilder::new(loop_body_node_id);
1268 if let Some(decorator_ids) = block_builder.drain_decorators() {
1269 loop_builder.append_before_enter(decorator_ids);
1270 }
1271
1272 let loop_node_id =
1273 block_builder.mast_forest_builder_mut().ensure_node(loop_builder)?;
1274
1275 let asm_op = self.create_asmop_decorator(span, "while.true", proc_ctx);
1277 block_builder
1278 .mast_forest_builder_mut()
1279 .register_node_asm_op(loop_node_id, asm_op)?;
1280
1281 body_node_ids.push(loop_node_id);
1282 },
1283 }
1284 }
1285
1286 let maybe_post_decorators: Option<Vec<DecoratorId>> =
1287 match block_builder.try_into_basic_block()? {
1288 BasicBlockOrDecorators::BasicBlock(basic_block_id) => {
1289 body_node_ids.push(basic_block_id);
1290 None
1291 },
1292 BasicBlockOrDecorators::Decorators(decorator_ids) => {
1293 Some(decorator_ids)
1295 },
1296 BasicBlockOrDecorators::Nothing => None,
1297 };
1298
1299 let procedure_body_id = if body_node_ids.is_empty() {
1300 if maybe_post_decorators.is_some() {
1304 return Err(Report::new(
1305 RelatedLabel::error("invalid procedure")
1306 .with_labeled_span(
1307 proc_ctx.span(),
1308 "body must contain at least one instruction if it has decorators",
1309 )
1310 .with_source_file(
1311 proc_ctx.source_manager().get(proc_ctx.span().source_id()).ok(),
1312 ),
1313 ));
1314 }
1315
1316 mast_forest_builder.ensure_block(
1317 vec![Operation::Noop],
1318 Vec::new(),
1319 vec![],
1320 vec![],
1321 vec![],
1322 )?
1323 } else {
1324 let asm_op = self.create_asmop_decorator(&proc_ctx.span(), "begin", proc_ctx);
1325 mast_forest_builder.join_nodes(body_node_ids, Some(asm_op))?
1326 };
1327
1328 if let Some(post_decorator_ids) = maybe_post_decorators {
1330 mast_forest_builder
1331 .append_after_exit(procedure_body_id, post_decorator_ids)
1332 .into_diagnostic()?;
1333 }
1334
1335 Ok(procedure_body_id)
1336 }
1337
1338 pub(super) fn resolve_target(
1345 &self,
1346 kind: InvokeKind,
1347 target: &InvocationTarget,
1348 caller_id: GlobalItemIndex,
1349 mast_forest_builder: &mut MastForestBuilder,
1350 ) -> Result<Option<ResolvedProcedure>, Report> {
1351 let caller = SymbolResolutionContext {
1352 span: target.span(),
1353 module: caller_id.module,
1354 kind: Some(kind),
1355 };
1356 let resolved = self.linker.resolve_invoke_target(&caller, target)?;
1357 match resolved {
1358 SymbolResolution::MastRoot(mast_root) => {
1359 let node = self.ensure_valid_procedure_mast_root(
1360 kind,
1361 target.span(),
1362 mast_root.into_inner(),
1363 mast_forest_builder,
1364 )?;
1365 Ok(Some(ResolvedProcedure { node, signature: None }))
1366 },
1367 SymbolResolution::Exact { gid, .. } => {
1368 match mast_forest_builder.get_procedure(gid) {
1369 Some(proc) => Ok(Some(ResolvedProcedure {
1370 node: proc.body_node_id(),
1371 signature: proc.signature(),
1372 })),
1373 None => match self.linker[gid].item() {
1376 SymbolItem::Compiled(ItemInfo::Procedure(p)) => {
1377 let node = self.ensure_valid_procedure_mast_root(
1378 kind,
1379 target.span(),
1380 p.digest,
1381 mast_forest_builder,
1382 )?;
1383 Ok(Some(ResolvedProcedure { node, signature: p.signature.clone() }))
1384 },
1385 SymbolItem::Procedure(_) => panic!(
1386 "AST procedure {gid:?} exists in the linker, but not in the MastForestBuilder"
1387 ),
1388 SymbolItem::Alias { .. } => {
1389 unreachable!("unexpected reference to ast alias item from {gid:?}")
1390 },
1391 SymbolItem::Compiled(_) | SymbolItem::Type(_) | SymbolItem::Constant(_) => {
1392 Ok(None)
1393 },
1394 },
1395 }
1396 },
1397 SymbolResolution::Module { .. }
1398 | SymbolResolution::External(_)
1399 | SymbolResolution::Local(_) => unreachable!(),
1400 }
1401 }
1402
1403 fn ensure_valid_procedure_mast_root(
1408 &self,
1409 kind: InvokeKind,
1410 span: SourceSpan,
1411 mast_root: Word,
1412 mast_forest_builder: &mut MastForestBuilder,
1413 ) -> Result<MastNodeId, Report> {
1414 let current_source_file = self.source_manager.get(span.source_id()).ok();
1416
1417 if matches!(kind, InvokeKind::SysCall) && self.linker.has_nonempty_kernel() {
1418 if !self.linker.kernel().contains_proc(mast_root) {
1422 let callee = mast_forest_builder
1423 .find_procedure_by_mast_root(&mast_root)
1424 .map(|proc| proc.path().clone())
1425 .unwrap_or_else(|| {
1426 let digest_path = format!("{mast_root}");
1427 Arc::<Path>::from(Path::new(&digest_path))
1428 });
1429 return Err(Report::new(LinkerError::InvalidSysCallTarget {
1430 span,
1431 source_file: current_source_file,
1432 callee,
1433 }));
1434 }
1435 }
1436
1437 mast_forest_builder.ensure_external_link(mast_root)
1438 }
1439}
1440
1441struct SubgraphRoot {
1449 proc_id: GlobalItemIndex,
1450 is_program_entrypoint: bool,
1451}
1452
1453impl SubgraphRoot {
1454 fn with_entrypoint(proc_id: GlobalItemIndex) -> Self {
1455 Self { proc_id, is_program_entrypoint: true }
1456 }
1457
1458 fn not_as_entrypoint(proc_id: GlobalItemIndex) -> Self {
1459 Self { proc_id, is_program_entrypoint: false }
1460 }
1461}
1462
1463pub(crate) struct BodyWrapper {
1466 pub prologue: Vec<Operation>,
1467 pub epilogue: Vec<Operation>,
1468}
1469
1470pub(super) struct ResolvedProcedure {
1471 pub node: MastNodeId,
1472 pub signature: Option<Arc<FunctionType>>,
1473}