Skip to content

Commit

Permalink
Merge branch 'next' into plafer-mast-library-compilation
Browse files Browse the repository at this point in the history
  • Loading branch information
plafer committed Jul 25, 2024
2 parents f12ce36 + b8a767f commit 5b81eec
Show file tree
Hide file tree
Showing 28 changed files with 237 additions and 496 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
- Added functions to `MastForestBuilder` to allow ensuring of nodes with fewer LOC (#1404)
- Make `Assembler` single-use (#1409)
- Remove `ProcedureCache` from the assembler (#1411).
- Add `Assembler::assemble_library()` (#1413)

#### Changed

Expand Down
8 changes: 4 additions & 4 deletions assembly/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,10 @@ use miden_assembly::Assembler;
let assembler = Assembler::default();

// Emit a program which pushes values 3 and 5 onto the stack and adds them
let program = assembler.assemble("begin push.3 push.5 add end").unwrap();
let program = assembler.assemble_program("begin push.3 push.5 add end").unwrap();

// Emit a program from some source code on disk (requires the `std` feature)
let program = assembler.assemble(&Path::new("./example.masm")).unwrap();
let program = assembler.assemble_program(&Path::new("./example.masm")).unwrap();
```

> [!NOTE]
Expand Down Expand Up @@ -119,7 +119,7 @@ Programs compiled by this assembler will be able to make calls to the
`foo` procedure by executing the `syscall` instruction, like so:

```rust
assembler.assemble("
assembler.assemble_program("
begin
syscall.foo
end
Expand Down Expand Up @@ -169,7 +169,7 @@ let assembler = Assembler::default()
.unwrap();

// Assemble our program
assembler.assemble("
assembler.assemble_program("
begin
push.1.2
syscall.foo
Expand Down
5 changes: 2 additions & 3 deletions assembly/src/assembler/basic_block_builder.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
use crate::AssemblyError;
use crate::{ast::Instruction, AssemblyError};

use super::{
mast_forest_builder::MastForestBuilder, BodyWrapper, Decorator, DecoratorList, Instruction,
ProcedureContext,
mast_forest_builder::MastForestBuilder, BodyWrapper, Decorator, DecoratorList, ProcedureContext,
};
use alloc::{borrow::Borrow, string::ToString, vec::Vec};
use vm_core::{mast::MastNodeId, AdviceInjector, AssemblyOp, Operation};
Expand Down
6 changes: 3 additions & 3 deletions assembly/src/assembler/instruction/mod.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
use super::{
ast::InvokeKind, mast_forest_builder::MastForestBuilder, Assembler, BasicBlockBuilder, Felt,
Instruction, Operation, ProcedureContext, ONE, ZERO,
Operation, ProcedureContext,
};
use crate::{diagnostics::Report, utils::bound_into_included_u64, AssemblyError};
use crate::{ast::Instruction, diagnostics::Report, utils::bound_into_included_u64, AssemblyError};
use core::ops::RangeBounds;
use vm_core::{mast::MastNodeId, Decorator};
use vm_core::{mast::MastNodeId, Decorator, ONE, ZERO};

mod adv_ops;
mod crypto_ops;
Expand Down
180 changes: 31 additions & 149 deletions assembly/src/assembler/mod.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,16 @@
use crate::{
ast::{
self, AliasTarget, Export, FullyQualifiedProcedureName, Instruction, InvocationTarget,
InvokeKind, Module, ModuleKind, ProcedureIndex,
},
compiled_library::{
CompiledFullyQualifiedProcedureName, CompiledLibrary, CompiledLibraryMetadata,
ProcedureInfo,
self, FullyQualifiedProcedureName, InvocationTarget, InvokeKind, ModuleKind, ProcedureIndex,
},
diagnostics::Report,
library::CompiledLibrary,
sema::SemanticAnalysisError,
AssemblyError, Compile, CompileOptions, Felt, Library, LibraryNamespace, LibraryPath,
RpoDigest, Spanned, ONE, ZERO,
AssemblyError, Compile, CompileOptions, Library, LibraryNamespace, LibraryPath, RpoDigest,
Spanned,
};
use alloc::{sync::Arc, vec::Vec};
use mast_forest_builder::MastForestBuilder;
use miette::miette;
use vm_core::{mast::MastNodeId, Decorator, DecoratorList, Kernel, Operation, Program};
use vm_core::{mast::MastNodeId, Decorator, DecoratorList, Felt, Kernel, Operation, Program};

mod basic_block_builder;
mod id;
Expand Down Expand Up @@ -47,11 +42,12 @@ use self::module_graph::{CallerInfo, ModuleGraph, ResolvedTarget};
/// Programs compiled with an empty kernel cannot use the `syscall` instruction.
/// </div>
///
/// * If you have a single executable module you want to compile, just call [Assembler::assemble].
/// * If you have a single executable module you want to compile, just call
/// [Assembler::assemble_program].
/// * If you want to link your executable to a few other modules that implement supporting
/// procedures, build the assembler with them first, using the various builder methods on
/// [Assembler], e.g. [Assembler::with_module], [Assembler::with_library], etc. Then, call
/// [Assembler::assemble] to get your compiled program.
/// [Assembler::assemble_program] to get your compiled program.
#[derive(Clone, Default)]
pub struct Assembler {
/// The global [ModuleGraph] for this assembler.
Expand Down Expand Up @@ -258,152 +254,52 @@ impl Assembler {
/// Compilation/Assembly
impl Assembler {
/// Assembles a set of modules into a library.
///
/// The returned library can be added to the assembler assembling a program that depends on the
/// library using [`Self::add_compiled_library`].
pub fn assemble_library(
mut self,
modules: impl Iterator<Item = impl Compile>,
metadata: CompiledLibraryMetadata, // name, version etc.
) -> Result<CompiledLibrary, Report> {
let module_ids: Vec<ModuleIndex> = modules
let module_indices: Vec<ModuleIndex> = modules
.map(|module| {
let module = module.compile_with_options(CompileOptions::for_library())?;

if module.path().namespace() != &metadata.name {
return Err(miette!(
"library namespace is {}, but module {} has namespace {}",
metadata.name,
module.name(),
module.path().namespace()
));
}

Ok(self.module_graph.add_ast_module(module)?)
})
.collect::<Result<_, Report>>()?;
self.module_graph.recompute()?;

let mut mast_forest_builder = MastForestBuilder::default();

self.assemble_graph(&mut mast_forest_builder)?;

let exports = {
let mut exports = Vec::new();
for module_id in module_ids {
let module = self.module_graph.get_module(module_id).unwrap();
let module_path = module.path();

let exports_in_module: Vec<CompiledFullyQualifiedProcedureName> =
self.get_module_exports(module_id, &mast_forest_builder).map(|procedures| {
procedures
.into_iter()
.map(|proc| {
CompiledFullyQualifiedProcedureName::new(
module_path.clone(),
proc.name,
)
})
.collect()
})?;

exports.extend(exports_in_module);
}

exports
};

Ok(CompiledLibrary::new(mast_forest_builder.build(), exports, metadata)?)
}
for module_idx in module_indices {
let module = self.module_graph[module_idx].unwrap_ast().clone();

/// Get the set of exported procedure infos of the given module.
///
/// Returns an error if the provided Miden Assembly is invalid.
fn get_module_exports(
&mut self,
module_index: ModuleIndex,
mast_forest_builder: &MastForestBuilder,
) -> Result<Vec<ProcedureInfo>, Report> {
assert!(self.module_graph.contains_module(module_index), "invalid module index");
for (proc_idx, procedure) in module.procedures().enumerate() {
// Only add exports; locals will be added if they are in the call graph rooted
// at those procedures
if !procedure.visibility().is_exported() {
continue;
}

let exports: Vec<ProcedureInfo> = match &self.module_graph[module_index] {
module_graph::WrappedModule::Ast(module) => {
self.get_module_exports_ast(module_index, module, mast_forest_builder)?
}
module_graph::WrappedModule::Info(module) => {
module.procedure_infos().map(|(_idx, proc)| proc).cloned().collect()
}
};
let gid = GlobalProcedureIndex {
module: module_idx,
index: ProcedureIndex::new(proc_idx),
};

Ok(exports)
}
self.compile_subgraph(gid, false, &mut mast_forest_builder)?;

/// Helper function for [`Self::get_module_exports`], specifically for when the inner
/// [`module_graph::WrappedModule`] is in `Ast` representation.
fn get_module_exports_ast(
&self,
module_index: ModuleIndex,
module: &Arc<Module>,
mast_forest_builder: &MastForestBuilder,
) -> Result<Vec<ProcedureInfo>, Report> {
let mut exports = Vec::new();
for (index, procedure) in module.procedures().enumerate() {
// Only add exports; locals will be added if they are in the call graph rooted
// at those procedures
if !procedure.visibility().is_exported() {
continue;
}
let gid = match procedure {
Export::Procedure(_) => GlobalProcedureIndex {
module: module_index,
index: ProcedureIndex::new(index),
},
Export::Alias(ref alias) => {
match alias.target() {
AliasTarget::MastRoot(digest) => {
self.module_graph.get_procedure_index_by_digest(digest)
.unwrap_or_else(|| {
panic!(
"compilation apparently succeeded, but did not find a \
entry in the procedure cache for alias '{}', i.e. '{}'",
alias.name(),
digest
);
})
}
AliasTarget::Path(ref name)=> {
self.module_graph.find(alias.source_file(), name)?
}
}
}
};
let proc = mast_forest_builder.get_procedure(gid).unwrap_or_else(|| match procedure {
Export::Procedure(ref proc) => {
panic!(
"compilation apparently succeeded, but did not find a \
entry in the procedure cache for '{}'",
proc.name()
)
}
Export::Alias(ref alias) => {
panic!(
"compilation apparently succeeded, but did not find a \
entry in the procedure cache for alias '{}', i.e. '{}'",
alias.name(),
alias.target()
);
exports.push(FullyQualifiedProcedureName::new(
module.path().clone(),
procedure.name().clone(),
));
}
});

let compiled_proc = ProcedureInfo {
name: proc.name().clone(),
digest: mast_forest_builder[proc.body_node_id()].digest(),
};
}

exports.push(compiled_proc);
}
exports
};

Ok(exports)
Ok(CompiledLibrary::new(mast_forest_builder.build(), exports)?)
}

/// Compiles the provided module into a [`Program`]. The resulting program can be executed on
Expand All @@ -413,7 +309,7 @@ impl Assembler {
///
/// Returns an error if parsing or compilation of the specified program fails, or if the source
/// doesn't have an entrypoint.
pub fn assemble(self, source: impl Compile) -> Result<Program, Report> {
pub fn assemble_program(self, source: impl Compile) -> Result<Program, Report> {
let opts = CompileOptions {
warnings_as_errors: self.warnings_as_errors,
..CompileOptions::default()
Expand Down Expand Up @@ -491,20 +387,6 @@ impl Assembler {
))
}

/// Compile all of the uncompiled procedures in the module graph, placing them
/// in the procedure cache once compiled.
///
/// Returns an error if any of the provided Miden Assembly is invalid.
fn assemble_graph(
&mut self,
mast_forest_builder: &mut MastForestBuilder,
) -> Result<(), Report> {
let mut worklist = self.module_graph.topological_sort().to_vec();
assert!(!worklist.is_empty());
self.process_graph_worklist(&mut worklist, None, mast_forest_builder)
.map(|_| ())
}

/// Compile the uncompiled procedure in the module graph which are members of the subgraph
/// rooted at `root`, placing them in the MAST forest builder once compiled.
///
Expand Down
Loading

0 comments on commit 5b81eec

Please sign in to comment.