Skip to content

Commit

Permalink
Merge pull request #1079 from 0xPolygonMiden/andrew-compile-modules-w…
Browse files Browse the repository at this point in the history
…ithout-lib-paths

Enable compiling modules without library paths
  • Loading branch information
Fumuran authored Oct 6, 2023
2 parents 73d9334 + b5aea22 commit 821beae
Show file tree
Hide file tree
Showing 7 changed files with 209 additions and 110 deletions.
17 changes: 3 additions & 14 deletions assembly/src/assembler/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -432,18 +432,7 @@ impl ModuleContext {
/// This also updates module callset to include the callset of the newly compiled procedure.
pub fn complete_proc(&mut self, code: CodeBlock) {
let proc_context = self.proc_stack.pop().expect("no procedures");

// build an ID for the procedure as follows:
// - for exported procedures: hash("module_path::proc_name")
// - for internal procedures: hash("module_path::proc_index")
let proc_id = if proc_context.is_export {
ProcedureId::from_name(&proc_context.name, &self.path)
} else {
let proc_idx = self.compiled_procs.len() as u16;
ProcedureId::from_index(proc_idx, &self.path)
};

let proc = proc_context.into_procedure(proc_id, code);
let proc = proc_context.into_procedure(code);
self.callset.append(proc.callset());
self.compiled_procs.push(proc);
}
Expand Down Expand Up @@ -560,14 +549,14 @@ impl ProcedureContext {
&self.name
}

pub fn into_procedure(self, id: ProcedureId, code_root: CodeBlock) -> NamedProcedure {
pub fn into_procedure(self, code_root: CodeBlock) -> NamedProcedure {
let Self {
name,
is_export,
num_locals,
callset,
} = self;

NamedProcedure::new(id, name, is_export, num_locals as u32, code_root, callset)
NamedProcedure::new(name, is_export, num_locals as u32, code_root, callset)
}
}
68 changes: 50 additions & 18 deletions assembly/src/assembler/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ impl Assembler {
// compile the kernel; this adds all exported kernel procedures to the procedure cache
let mut context = AssemblyContext::for_module(true);
let kernel = Module::kernel(module);
self.compile_module(&kernel, &mut context)?;
self.compile_module(&kernel.ast, Some(&kernel.path), &mut context)?;

// convert the context into Kernel; this builds the kernel from hashes of procedures
// exported form the kernel module
Expand Down Expand Up @@ -186,38 +186,48 @@ impl Assembler {
/// - If a lock to the [ProcedureCache] can not be attained.
pub fn compile_module(
&self,
module: &Module,
module: &ModuleAst,
path: Option<&LibraryPath>,
context: &mut AssemblyContext,
) -> Result<Vec<RpoDigest>, AssemblyError> {
// a variable to track MAST roots of all procedures exported from this module
let mut proc_roots = Vec::new();
context.begin_module(&module.path, &module.ast)?;
context.begin_module(path.unwrap_or(&LibraryPath::anon_path()), module)?;

// process all re-exported procedures
for reexporteed_proc in module.ast.reexported_procs().iter() {
for reexporteed_proc in module.reexported_procs().iter() {
// make sure the re-exported procedure is loaded into the procedure cache
let ref_proc_id = reexporteed_proc.proc_id();
self.ensure_procedure_is_in_cache(&ref_proc_id, context).map_err(|_| {
AssemblyError::ReExportedProcModuleNotFound(reexporteed_proc.clone())
})?;

// build procedure ID for the alias, and add it to the procedure cache
let proc_name = reexporteed_proc.name();
let alias_proc_id = ProcedureId::from_name(proc_name, &module.path);
let proc_mast_root = self
.proc_cache
.try_borrow_mut()
.map_err(|_| AssemblyError::InvalidCacheLock)?
.insert_proc_alias(alias_proc_id, ref_proc_id)?;
// if the library path is provided, build procedure ID for the alias and add it to the
// procedure cache
let proc_mast_root = if let Some(path) = path {
let proc_name = reexporteed_proc.name();
let alias_proc_id = ProcedureId::from_name(proc_name, path);
self.proc_cache
.try_borrow_mut()
.map_err(|_| AssemblyError::InvalidCacheLock)?
.insert_proc_alias(alias_proc_id, ref_proc_id)?
} else {
self.proc_cache
.try_borrow_mut()
.map_err(|_| AssemblyError::InvalidCacheLock)?
.get_proc_root_by_id(&ref_proc_id)
.expect("procedure ID not in cache")
};

// add the MAST root of the re-exported procedure to the set of procedures exported
// from this module
proc_roots.push(proc_mast_root);
}

// compile all local procedures in the module; once the compilation is complete, we get
// all compiled procedures (and their combined callset) from the context
for proc_ast in module.ast.procs().iter() {
// compile all local (internal end exported) procedures in the module; once the compilation
// is complete, we get all compiled procedures (and their combined callset) from the
// context
for proc_ast in module.procs().iter() {
self.compile_procedure(proc_ast, context)?;
}
let (module_procs, module_callset) = context.complete_module();
Expand All @@ -227,17 +237,20 @@ impl Assembler {
// - a procedure is exported from the module, or
// - a procedure is present in the combined callset - i.e., it is an internal procedure
// which has been invoked via a local call instruction.
for proc in module_procs.into_iter() {
for (proc_index, proc) in module_procs.into_iter().enumerate() {
if proc.is_export() {
proc_roots.push(proc.mast_root());
}

if proc.is_export() || module_callset.contains(&proc.mast_root()) {
// build the procedure ID if this module has the library path
let proc_id = build_procedure_id(path, &proc, proc_index);

// this is safe because we fail if the cache is borrowed.
self.proc_cache
.try_borrow_mut()
.map_err(|_| AssemblyError::InvalidCacheLock)?
.insert(proc)?;
.insert(proc, proc_id)?;
}
}

Expand Down Expand Up @@ -372,7 +385,7 @@ impl Assembler {
let proc_name = context.get_imported_procedure_name(proc_id);
AssemblyError::imported_proc_module_not_found(proc_id, proc_name)
})?;
self.compile_module(module, context)?;
self.compile_module(&module.ast, Some(&module.path), context)?;
// if the procedure is still not in cache, then there was some error
if !self.proc_cache.borrow().contains_id(proc_id) {
return Err(AssemblyError::imported_proc_not_found_in_module(
Expand Down Expand Up @@ -480,3 +493,22 @@ fn combine_spans(spans: &mut Vec<CodeBlock>) -> CodeBlock {
});
CodeBlock::new_span_with_decorators(ops, decorators)
}

/// Builds a procedure ID based on the provided parameters.
///
/// Returns [ProcedureId] if `path` is provided, [None] otherwise.
fn build_procedure_id(
path: Option<&LibraryPath>,
proc: &NamedProcedure,
proc_index: usize,
) -> Option<ProcedureId> {
let mut proc_id = None;
if let Some(path) = path {
if proc.is_export() {
proc_id = Some(ProcedureId::from_name(proc.name(), path));
} else {
proc_id = Some(ProcedureId::from_index(proc_index as u16, path))
}
}
proc_id
}
23 changes: 18 additions & 5 deletions assembly/src/assembler/procedure_cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@ impl ProcedureCache {
self.procedures.get(mast_root)
}

/// Returns a MAST root ([RpoDigest]) reference corresponding to the provided [ProcedureId].
pub fn get_proc_root_by_id(&self, id: &ProcedureId) -> Option<RpoDigest> {
self.proc_id_map.get(id).cloned()
}

/// Returns true if the [ProcedureCache] contains a [Procedure] for the specified
/// [ProcedureId].
pub fn contains_id(&self, id: &ProcedureId) -> bool {
Expand All @@ -58,10 +63,14 @@ impl ProcedureCache {
/// - A procedure with the same ID is already in the cache.
/// - A procedure with the same MAST root but conflicting procedure metadata exists in the
/// cache.
pub fn insert(&mut self, proc: NamedProcedure) -> Result<(), AssemblyError> {
pub fn insert(
&mut self,
proc: NamedProcedure,
id: Option<ProcedureId>,
) -> Result<(), AssemblyError> {
// if a procedure with the same id is already in the cache, return an error
if self.contains_id(proc.id()) {
return Err(AssemblyError::duplicate_proc_id(proc.id()));
if id.is_some_and(|id| self.contains_id(&id)) {
return Err(AssemblyError::duplicate_proc_id(&id.unwrap()));
}

// If the entry is `Vacant` then insert the Procedure. If the procedure with the same MAST
Expand All @@ -72,12 +81,16 @@ impl ProcedureCache {
if proc.num_locals() != cached_proc.num_locals() {
Err(AssemblyError::conflicting_num_locals(proc.name()))
} else {
self.proc_id_map.insert(*proc.id(), proc.mast_root());
if let Some(id) = id {
self.proc_id_map.insert(id, proc.mast_root());
}
Ok(())
}
}
Entry::Vacant(entry) => {
self.proc_id_map.insert(*proc.id(), proc.mast_root());
if let Some(id) = id {
self.proc_id_map.insert(id, proc.mast_root());
}
entry.insert(proc.into_inner());
Ok(())
}
Expand Down
3 changes: 1 addition & 2 deletions assembly/src/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -384,8 +384,7 @@ impl fmt::Display for ProgramAst {
/// An abstract syntax tree of a Miden module.
///
/// A module AST consists of a list of procedure ASTs, a list of re-exported procedures, a list of
/// imports, a map from procedure ids to procedure names for imported procedures used in the module,
/// and module documentation. Local procedures could be internal or exported.
/// imports, and module documentation. Local procedures could be internal or exported.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ModuleAst {
local_procs: Vec<ProcedureAst>,
Expand Down
11 changes: 11 additions & 0 deletions assembly/src/library/path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ impl LibraryPath {
/// Path for an executable module.
pub const EXEC_PATH: &'static str = "#exec";

/// Path for a module without library path.
pub const ANON_PATH: &'static str = "#anon";

// CONSTRUCTORS
// --------------------------------------------------------------------------------------------

Expand Down Expand Up @@ -72,6 +75,14 @@ impl LibraryPath {
}
}

/// Returns a path for a module without library path.
pub fn anon_path() -> Self {
Self {
path: Self::ANON_PATH.into(),
num_components: 1,
}
}

// PUBLIC ACCESSORS
// --------------------------------------------------------------------------------------------

Expand Down
10 changes: 0 additions & 10 deletions assembly/src/procedures/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,13 +54,10 @@ impl Procedure {
///
/// Procedure metadata includes:
/// - Procedure name.
/// - Procedure ID which is computed as a hash of the procedure's fully qualified path.
/// - A boolean flag indicating whether the procedure is exported from a module.
/// - Number of procedure locals available to the procedure.
/// - A set of MAST roots of procedures which are invoked from this procedure.
#[derive(Clone, Debug)]
pub struct NamedProcedure {
id: ProcedureId,
name: ProcedureName,
is_export: bool,
procedure: Procedure,
Expand All @@ -71,15 +68,13 @@ impl NamedProcedure {
// --------------------------------------------------------------------------------------------
/// Returns a new [Procedure] instantiated with the specified properties.
pub fn new(
id: ProcedureId,
name: ProcedureName,
is_export: bool,
num_locals: u32,
code: CodeBlock,
callset: CallSet,
) -> Self {
NamedProcedure {
id,
name,
is_export,
procedure: Procedure {
Expand All @@ -93,11 +88,6 @@ impl NamedProcedure {
// PUBLIC ACCESSORS
// --------------------------------------------------------------------------------------------

/// Returns ID of this procedure.
pub fn id(&self) -> &ProcedureId {
&self.id
}

/// Returns a label of this procedure.
pub fn name(&self) -> &ProcedureName {
&self.name
Expand Down
Loading

0 comments on commit 821beae

Please sign in to comment.