Skip to content

Commit

Permalink
Merge pull request #329 from 0xPolygonMiden/greenhat/i90-wasm-cm-carg…
Browse files Browse the repository at this point in the history
…o-miden

[1/x] feature(cargo-miden): support building Wasm component from a Cargo project
  • Loading branch information
bitwalker authored Oct 28, 2024
2 parents da89034 + 6a59e3d commit c201bf6
Show file tree
Hide file tree
Showing 86 changed files with 6,926 additions and 3,180 deletions.
142 changes: 70 additions & 72 deletions Cargo.lock

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,8 @@ cargo-miden = { version = "0.0.7", path = "tools/cargo-miden" }
miden-integration-tests = { version = "0.0.0", path = "tests/integration" }
wat = "1.0.69"
blake3 = "1.5"
tokio = { version = "1.39.2", features = ["rt", "time", "macros"] }
tokio-util = "0.7.11"

[profile.dev]
lto = false
Expand Down
5 changes: 3 additions & 2 deletions frontend-wasm/src/code_translator/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -194,12 +194,13 @@ pub fn translate_operator(
Operator::MemoryCopy { dst_mem, src_mem } => {
// See semantics at https://github.com/WebAssembly/bulk-memory-operations/blob/master/proposals/bulk-memory-operations/Overview.md#memorycopy-instruction
if *src_mem == 0 && src_mem == dst_mem {
let len = state.pop1();
let count_i32 = state.pop1();
let src_i32 = state.pop1();
let dst_i32 = state.pop1();
let count = builder.ins().bitcast(count_i32, Type::U32, span);
let dst = prepare_addr(dst_i32, &U8, None, builder, span);
let src = prepare_addr(src_i32, &U8, None, builder, span);
builder.ins().memcpy(src, dst, len, span);
builder.ins().memcpy(src, dst, count, span);
} else {
unsupported_diag!(diagnostics, "MemoryCopy: only single memory is supported");
}
Expand Down
11 changes: 6 additions & 5 deletions frontend-wasm/src/code_translator/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,11 +87,12 @@ fn memory_copy() {
(let (v0 i32) (const.i32 20))
(let (v1 i32) (const.i32 10))
(let (v2 i32) (const.i32 1))
(let (v3 u32) (bitcast v0))
(let (v4 (ptr u8)) (inttoptr v3))
(let (v5 u32) (bitcast v1))
(let (v6 (ptr u8)) (inttoptr v5))
(memcpy v6 v4 v2)
(let (v3 u32) (bitcast v2))
(let (v4 u32) (bitcast v0))
(let (v5 (ptr u8)) (inttoptr v4))
(let (v6 u32) (bitcast v1))
(let (v7 (ptr u8)) (inttoptr v6))
(memcpy v7 v5 v3)
"#]],
)
}
Expand Down
6 changes: 3 additions & 3 deletions frontend-wasm/src/component/build_ir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ mod tests {
let wasm = wat::parse_str(wat).unwrap();
let context = test_context();
let interface_function_ident = InterfaceFunctionIdent {
interface: InterfaceIdent::from_full_ident("miden:add/[email protected]".to_string()),
interface: InterfaceIdent::from_full_ident("miden:add/[email protected]"),
function: Symbol::intern("add"),
};
let import_metadata = [(
Expand Down Expand Up @@ -254,9 +254,9 @@ mod tests {
dbg!(ir.imports());
let component_import = ir.imports().get(&function_id).unwrap().unwrap_canon_abi_import();
assert_eq!(component_import.interface_function, interface_function_ident);
assert!(!component_import.function_ty.params.is_empty());
assert!(!component_import.function_ty().params.is_empty());
let expected_import_func_ty =
FunctionType::new_wasm(vec![Type::U32, Type::U32], vec![Type::U32]);
assert_eq!(component_import.function_ty, expected_import_func_ty);
assert_eq!(component_import.function_ty(), &expected_import_func_ty);
}
}
120 changes: 79 additions & 41 deletions frontend-wasm/src/component/translator.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use midenc_hir::{
cranelift_entity::PrimaryMap, diagnostics::Severity, CanonAbiImport, ComponentBuilder,
ComponentExport, FunctionIdent, FunctionType, Ident, InterfaceFunctionIdent, InterfaceIdent,
Symbol,
MidenAbiImport, Symbol,
};
use midenc_hir_type::Abi;
use midenc_session::Session;
Expand All @@ -16,6 +16,10 @@ use super::{
use crate::{
component::StringEncoding,
error::WasmResult,
intrinsics::{
intrinsics_conversion_result, is_miden_intrinsics_module, IntrinsicsConversionResult,
},
miden_abi::{is_miden_abi_module, miden_abi_function_type, recover_imported_masm_function_id},
module::{
build_ir::build_ir_module,
instance::ModuleArgument,
Expand Down Expand Up @@ -72,7 +76,7 @@ impl<'a, 'data> ComponentTranslator<'a, 'data> {
) -> WasmResult<midenc_hir::Component> {
let mut component_builder: midenc_hir::ComponentBuilder<'a> =
midenc_hir::ComponentBuilder::new(&self.session.diagnostics);
dbg!(&wasm_translation.component.initializers);
// dbg!(&wasm_translation.component.initializers);
for initializer in &wasm_translation.component.initializers {
match initializer {
GlobalInitializer::InstantiateModule(instantiate_module) => {
Expand Down Expand Up @@ -153,14 +157,15 @@ impl<'a, 'data> ComponentTranslator<'a, 'data> {
}
CoreDef::Trampoline(trampoline_idx) => {
let trampoline = &wasm_translation.trampolines[*trampoline_idx];
let arg = self.module_arg_from_trampoline(
if let Some(arg) = self.module_arg_from_trampoline(
trampoline,
module,
idx,
&wasm_translation.component,
component_builder,
)?;
module_args.push(arg);
)? {
module_args.push(arg)
}
}
}
}
Expand Down Expand Up @@ -191,14 +196,15 @@ impl<'a, 'data> ComponentTranslator<'a, 'data> {
}

/// Build a Wasm core module argument from the given trampoline (component import)
/// Returns `None` if the trampoline was an intrinsics that were converted
fn module_arg_from_trampoline(
&self,
trampoline: &Trampoline,
module: &Module,
idx: usize,
wasm_component: &LinearComponent,
component_builder: &mut ComponentBuilder<'_>,
) -> WasmResult<ModuleArgument> {
) -> WasmResult<Option<ModuleArgument>> {
match trampoline {
Trampoline::LowerImport {
index,
Expand All @@ -207,11 +213,20 @@ impl<'a, 'data> ComponentTranslator<'a, 'data> {
} => {
let module_import = module.imports.get(idx).expect("module import not found");
let runtime_import_idx = self.lower_imports[index];
let function_id = function_id_from_import(module, module_import);
let component_import =
self.translate_import(runtime_import_idx, *lower_ty, options, wasm_component)?;
component_builder.add_import(function_id, component_import.clone());
Ok(ModuleArgument::ComponentImport(component_import))
let function_id = function_id_from_import(module_import);
match self.translate_import(
runtime_import_idx,
*lower_ty,
options,
wasm_component,
)? {
Some(component_import) => {
component_builder.add_import(function_id, component_import.clone());
Ok(Some(ModuleArgument::ComponentImport(component_import)))
}

None => Ok(None),
}
}
_ => unsupported_diag!(
&self.session.diagnostics,
Expand Down Expand Up @@ -257,43 +272,70 @@ impl<'a, 'data> ComponentTranslator<'a, 'data> {
}

/// Translate the given runtime import to the Miden IR component import
/// Returns `None` if the import was an intrinsics that were converted
fn translate_import(
&self,
runtime_import_index: RuntimeImportIndex,
signature: TypeFuncIndex,
options: &CanonicalOptions,
wasm_component: &LinearComponent,
) -> WasmResult<midenc_hir::ComponentImport> {
) -> WasmResult<Option<midenc_hir::ComponentImport>> {
let (import_idx, import_names) = &wasm_component.imports[runtime_import_index];
if import_names.len() != 1 {
unsupported_diag!(&self.session.diagnostics, "multi-name imports not supported");
}
let import_func_name = import_names.first().unwrap();
let (full_interface_name, _) = wasm_component.import_types[*import_idx].clone();
let interface_function = InterfaceFunctionIdent {
interface: InterfaceIdent::from_full_ident(full_interface_name.clone()),
function: Symbol::intern(import_func_name),
};
let Some(import_metadata) = self.config.import_metadata.get(&interface_function) else {
return Err(self
.session
.diagnostics
.diagnostic(Severity::Error)
.with_message(format!(
"wasm error: import metadata for interface function {interface_function:?} \
not found"
))
.into_report());
};
let lifted_func_ty = convert_lifted_func_ty(&signature, &self.component_types);
let function_id = recover_imported_masm_function_id(&full_interface_name, import_func_name);
if is_miden_abi_module(function_id.module.as_symbol())
|| is_miden_intrinsics_module(function_id.module.as_symbol())
{
let function_ty = if is_miden_abi_module(function_id.module.as_symbol()) {
miden_abi_function_type(
function_id.module.as_symbol(),
function_id.function.as_symbol(),
)
} else if is_miden_intrinsics_module(function_id.module.as_symbol()) {
match intrinsics_conversion_result(&function_id) {
IntrinsicsConversionResult::FunctionType(function_ty) => function_ty,
IntrinsicsConversionResult::MidenVmOp => {
// Skip this import since it was converted to a Miden VM op(s)
return Ok(None);
}
}
} else {
panic!("no support for importing function from module {}", function_id.module);
};
let component_import =
midenc_hir::ComponentImport::MidenAbiImport(MidenAbiImport::new(function_ty));
Ok(Some(component_import))
} else {
let interface_function = InterfaceFunctionIdent {
interface: InterfaceIdent::from_full_ident(&full_interface_name),
function: Symbol::intern(import_func_name),
};
let Some(import_metadata) = self.config.import_metadata.get(&interface_function) else {
return Err(self
.session
.diagnostics
.diagnostic(Severity::Error)
.with_message(format!(
"wasm error: import metadata for interface function \
{interface_function:?} not found"
))
.into_report());
};
let lifted_func_ty = convert_lifted_func_ty(&signature, &self.component_types);

let component_import = midenc_hir::ComponentImport::CanonAbiImport(CanonAbiImport {
function_ty: lifted_func_ty,
interface_function,
digest: import_metadata.digest,
options: self.translate_canonical_options(options)?,
});
Ok(component_import)
let component_import =
midenc_hir::ComponentImport::CanonAbiImport(CanonAbiImport::new(
interface_function,
lifted_func_ty,
import_metadata.digest,
self.translate_canonical_options(options)?,
));
Ok(Some(component_import))
}
}

/// Build an IR Component export from the given Wasm component export
Expand Down Expand Up @@ -416,12 +458,8 @@ impl<'a, 'data> ComponentTranslator<'a, 'data> {
}

/// Get the function id from the given Wasm core module import
fn function_id_from_import(_module: &Module, module_import: &ModuleImport) -> FunctionIdent {
let function_id = FunctionIdent {
module: Ident::from(module_import.module.as_str()),
function: Ident::from(module_import.field.as_str()),
};
function_id
fn function_id_from_import(module_import: &ModuleImport) -> FunctionIdent {
recover_imported_masm_function_id(&module_import.module, module_import.field.as_str())
}

/// Get the function id from the given Wasm func_idx in the given Wasm core exporting_module
Expand Down
2 changes: 1 addition & 1 deletion frontend-wasm/src/component/types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1736,7 +1736,7 @@ pub fn interface_type_to_ir(
InterfaceType::U32 => midenc_hir_type::Type::U32,
InterfaceType::S64 => midenc_hir_type::Type::I64,
InterfaceType::U64 => midenc_hir_type::Type::U64,
InterfaceType::Float32 => todo!(),
InterfaceType::Float32 => midenc_hir_type::Type::Felt,
InterfaceType::Float64 => todo!(),
InterfaceType::Char => todo!(),
InterfaceType::String => todo!(),
Expand Down
2 changes: 1 addition & 1 deletion frontend-wasm/src/intrinsics/felt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use midenc_hir::{FunctionIdent, InstBuilder, SourceSpan, Type::*, Value};

use crate::module::function_builder_ext::FunctionBuilderExt;

pub(crate) const INTRINSICS_FELT_MODULE_NAME: &str = "miden:stdlib/intrinsics_felt";
pub(crate) const MODULE_ID: &str = "intrinsics::felt";

/// Convert a call to a felt op intrinsic function into instruction(s)
pub(crate) fn convert_felt_intrinsics(
Expand Down
30 changes: 27 additions & 3 deletions frontend-wasm/src/intrinsics/mem.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,31 @@
use midenc_hir::{AbiParam, FunctionIdent, InstBuilder, Signature, SourceSpan, Type, Value};
use midenc_hir::{
AbiParam, FunctionIdent, FunctionType, InstBuilder, Signature, SourceSpan, Type, Value,
};

use crate::module::function_builder_ext::FunctionBuilderExt;

pub const MODULE_ID: &str = "intrinsics::mem";

pub const HEAP_BASE: &str = "heap_base";

const HEAP_BASE_FUNC: ([Type; 0], [Type; 1]) = ([], [Type::U32]);

pub fn function_type(func_id: &FunctionIdent) -> FunctionType {
match func_id.function.as_symbol().as_str() {
HEAP_BASE => FunctionType::new(HEAP_BASE_FUNC.0, HEAP_BASE_FUNC.1),
_ => panic!("No memory intrinsics FunctionType found for {}", func_id),
}
}

fn signature(func_id: &FunctionIdent) -> Signature {
match func_id.function.as_symbol().as_str() {
HEAP_BASE => {
Signature::new(HEAP_BASE_FUNC.0.map(AbiParam::new), HEAP_BASE_FUNC.1.map(AbiParam::new))
}
_ => panic!("No memory intrinsics Signature found for {}", func_id),
}
}

/// Convert a call to a memory intrinsic function
pub(crate) fn convert_mem_intrinsics(
func_id: FunctionIdent,
Expand All @@ -10,14 +34,14 @@ pub(crate) fn convert_mem_intrinsics(
span: SourceSpan,
) -> Vec<Value> {
match func_id.function.as_symbol().as_str() {
"heap_base" => {
HEAP_BASE => {
assert_eq!(args.len(), 0, "{} takes no arguments", func_id);
if builder
.data_flow_graph()
.get_import_by_name(func_id.module, func_id.function)
.is_none()
{
let signature = Signature::new([], [AbiParam::new(Type::U32)]);
let signature = signature(&func_id);
let _ = builder.data_flow_graph_mut().import_function(
func_id.module,
func_id.function,
Expand Down
38 changes: 29 additions & 9 deletions frontend-wasm/src/intrinsics/mod.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
mod felt;
mod mem;
pub mod felt;
pub mod mem;

use std::{collections::HashSet, sync::OnceLock};

use midenc_hir::{FunctionIdent, SourceSpan, Symbol, Value};
use midenc_hir::{FunctionIdent, FunctionType, SourceSpan, Symbol, Value};

use crate::module::function_builder_ext::FunctionBuilderExt;

Expand All @@ -16,8 +16,8 @@ fn modules() -> &'static HashSet<&'static str> {
static MODULES: OnceLock<HashSet<&'static str>> = OnceLock::new();
MODULES.get_or_init(|| {
let mut s = HashSet::default();
s.insert("intrinsics::mem");
s.insert(felt::INTRINSICS_FELT_MODULE_NAME);
s.insert(mem::MODULE_ID);
s.insert(felt::MODULE_ID);
s
})
}
Expand All @@ -30,10 +30,30 @@ pub fn convert_intrinsics_call(
span: SourceSpan,
) -> Vec<Value> {
match func_id.module.as_symbol().as_str() {
"intrinsics::mem" => mem::convert_mem_intrinsics(func_id, args, builder, span),
felt::INTRINSICS_FELT_MODULE_NAME => {
felt::convert_felt_intrinsics(func_id, args, builder, span)
}
mem::MODULE_ID => mem::convert_mem_intrinsics(func_id, args, builder, span),
felt::MODULE_ID => felt::convert_felt_intrinsics(func_id, args, builder, span),
_ => panic!("No intrinsics found for {}", func_id),
}
}

fn intrinsic_function_type(func_id: &FunctionIdent) -> FunctionType {
match func_id.module.as_symbol().as_str() {
mem::MODULE_ID => mem::function_type(func_id),
_ => panic!("No intrinsics FunctionType found for {}", func_id),
}
}

pub enum IntrinsicsConversionResult {
FunctionType(FunctionType),
MidenVmOp,
}

pub fn intrinsics_conversion_result(func_id: &FunctionIdent) -> IntrinsicsConversionResult {
match func_id.module.as_symbol().as_str() {
mem::MODULE_ID => {
IntrinsicsConversionResult::FunctionType(intrinsic_function_type(func_id))
}
felt::MODULE_ID => IntrinsicsConversionResult::MidenVmOp,
_ => panic!("No intrinsics conversion result found for {}", func_id),
}
}
Loading

0 comments on commit c201bf6

Please sign in to comment.