diff --git a/crates/wiggle/wasmtime/macro/src/lib.rs b/crates/wiggle/wasmtime/macro/src/lib.rs index c60b8d9c82..446bcaf7d0 100644 --- a/crates/wiggle/wasmtime/macro/src/lib.rs +++ b/crates/wiggle/wasmtime/macro/src/lib.rs @@ -1,6 +1,6 @@ use proc_macro::TokenStream; -use proc_macro2::{Ident, TokenStream as TokenStream2}; -use quote::{format_ident, quote}; +use proc_macro2::TokenStream as TokenStream2; +use quote::quote; use syn::parse_macro_input; use wiggle_generate::Names; @@ -32,13 +32,6 @@ pub fn define_wasmtime_integration(args: TokenStream) -> TokenStream { quote!( #(#modules)* ).into() } -enum Abi { - I32, - I64, - F32, - F64, -} - fn generate_module( module: &witx::Module, module_conf: &ModuleConf, @@ -67,177 +60,16 @@ fn generate_module( } }); - let runtime = names.runtime_mod(); let target_path = &target_conf.path; - let missing_mem_err = &missing_mem_conf.err; let module_id = names.module(&module.name); + let target_module = quote! { #target_path::#module_id }; let ctor_externs = module.funcs().map(|f| { - let name_ident = names.func(&f.name); if let Some(func_override) = module_conf.function_override.find(&f.name.as_str()) { + let name_ident = names.func(&f.name); quote! { let #name_ident = wasmtime::Func::wrap(store, #func_override); } } else { - let mut shim_arg_decls = Vec::new(); - let mut params = Vec::new(); - let mut hostcall_args = Vec::new(); - - for param in f.params.iter() { - let name = names.func_param(¶m.name); - - // Registers a new parameter to the shim we're making with the - // given `name`, the `abi_ty` wasm type - // - // This will register a whole bunch of things: - // - // * The cranelift type for the parameter - // * Syntax to specify the actual function parameter - // * How to actually pass this argument to the host - // implementation, converting as necessary. - let mut add_param = |name: &Ident, abi_ty: Abi| { - match abi_ty { - Abi::I32 => { - params.push(quote! { types::I32 }); - shim_arg_decls.push(quote! { #name: i32 }); - } - Abi::I64 => { - params.push(quote! { types::I64 }); - shim_arg_decls.push(quote! { #name: i64 }); - } - Abi::F32 => { - params.push(quote! { types::F32 }); - shim_arg_decls.push(quote! { #name: f32 }); - } - Abi::F64 => { - params.push(quote! { types::F64 }); - shim_arg_decls.push(quote! { #name: f64 }); - } - } - hostcall_args.push(quote! { #name as _ }); - }; - - match &*param.tref.type_() { - witx::Type::Int(e) => match e.repr { - witx::IntRepr::U64 => add_param(&name, Abi::I64), - _ => add_param(&name, Abi::I32), - }, - - witx::Type::Enum(e) => match e.repr { - witx::IntRepr::U64 => add_param(&name, Abi::I64), - _ => add_param(&name, Abi::I32), - }, - - witx::Type::Flags(f) => match f.repr { - witx::IntRepr::U64 => add_param(&name, Abi::I64), - _ => add_param(&name, Abi::I32), - }, - - witx::Type::Builtin(witx::BuiltinType::Char8) - | witx::Type::Builtin(witx::BuiltinType::S8) - | witx::Type::Builtin(witx::BuiltinType::U8) - | witx::Type::Builtin(witx::BuiltinType::S16) - | witx::Type::Builtin(witx::BuiltinType::U16) - | witx::Type::Builtin(witx::BuiltinType::S32) - | witx::Type::Builtin(witx::BuiltinType::U32) - | witx::Type::Builtin(witx::BuiltinType::USize) => { - add_param(&name, Abi::I32); - } - - witx::Type::Builtin(witx::BuiltinType::S64) - | witx::Type::Builtin(witx::BuiltinType::U64) => { - add_param(&name, Abi::I64); - } - - witx::Type::Builtin(witx::BuiltinType::F32) => { - add_param(&name, Abi::F32); - } - - witx::Type::Builtin(witx::BuiltinType::F64) => { - add_param(&name, Abi::F64); - } - - // strings/arrays have an extra ABI parameter for the length - // of the array passed. - witx::Type::Builtin(witx::BuiltinType::String) | witx::Type::Array(_) => { - add_param(&name, Abi::I32); - let len = format_ident!("{}_len", name); - add_param(&len, Abi::I32); - } - - witx::Type::ConstPointer(_) - | witx::Type::Handle(_) - | witx::Type::Pointer(_) => { - add_param(&name, Abi::I32); - } - - witx::Type::Struct(_) | witx::Type::Union(_) => { - panic!("unsupported argument type") - } - } - } - - let mut results = f.results.iter(); - let mut ret_ty = quote! { () }; - let mut cvt_ret = quote! {}; - let mut returns = Vec::new(); - let mut handle_early_error = quote! { panic!("error: {:?}", e) }; - - // The first result is returned bare right now... - if let Some(ret) = results.next() { - handle_early_error = quote! { return e.into() }; - match &*ret.tref.type_() { - // Eventually we'll want to add support for more returned - // types, but for now let's just conform to what `*.witx` - // definitions currently use. - witx::Type::Enum(e) => match e.repr { - witx::IntRepr::U16 => { - returns.push(quote! { types::I32 }); - ret_ty = quote! { i32 }; - cvt_ret = quote! { .into() } - } - other => panic!("unsupported ret enum repr {:?}", other), - }, - other => panic!("unsupported first return {:?}", other), - } - } - - // ... and all remaining results are returned via out-poiners - for result in results { - let name = format_ident!("{}", result.name.as_str()); - params.push(quote! { types::I32 }); - shim_arg_decls.push(quote! { #name: i32 }); - hostcall_args.push(quote! { #name }); - } - - quote! { - let my_cx = cx.clone(); - let #name_ident = wasmtime::Func::wrap( - store, - move |caller: wasmtime::Caller<'_> #(,#shim_arg_decls)*| -> #ret_ty { - unsafe { - let mem = match caller.get_export("memory") { - Some(wasmtime::Extern::Memory(m)) => m, - _ => { - log::warn!("callee does not export a memory as \"memory\""); - let e = { #missing_mem_err }; - #handle_early_error - } - }; - // Wiggle does not expose any methods for - // functions to re-enter the WebAssembly module, - // or expose the memory via non-wiggle mechanisms. - // Therefore, creating a new BorrowChecker at the - // root of each function invocation is correct. - let bc = #runtime::BorrowChecker::new(); - let mem = #runtime::WasmtimeGuestMemory::new( mem, bc ); - #target_path::#module_id::#name_ident( - &mut my_cx.borrow_mut(), - &mem, - #(#hostcall_args),* - ) #cvt_ret - } - } - ); - } + generate_func(&f, names, missing_mem_conf, &target_module) } }); @@ -295,3 +127,68 @@ contained in the `cx` parameter.", } } } + +fn generate_func( + func: &witx::InterfaceFunc, + names: &Names, + missing_mem_conf: &MissingMemoryConf, + target_module: &TokenStream2, +) -> TokenStream2 { + let missing_mem_err = &missing_mem_conf.err; + let name_ident = names.func(&func.name); + + let coretype = func.core_type(); + + let arg_decls = coretype.args.iter().map(|arg| { + let name = names.func_core_arg(arg); + let atom = names.atom_type(arg.repr()); + quote! { #name: #atom } + }); + let arg_names = coretype.args.iter().map(|arg| names.func_core_arg(arg)); + + let (ret_ty, handle_early_error) = if let Some(ret) = &coretype.ret { + let ret_ty = match ret.signifies { + witx::CoreParamSignifies::Value(atom) => names.atom_type(atom), + _ => unreachable!("coretype ret should always be passed by value"), + }; + (quote! { #ret_ty }, quote! { return e.into(); }) + } else { + ( + quote! {()}, + quote! { panic!("unrecoverable error in {}: {}", stringify!(#name_ident), e) }, + ) + }; + + let runtime = names.runtime_mod(); + + quote! { + let my_cx = cx.clone(); + let #name_ident = wasmtime::Func::wrap( + store, + move |caller: wasmtime::Caller<'_> #(,#arg_decls)*| -> #ret_ty { + unsafe { + let mem = match caller.get_export("memory") { + Some(wasmtime::Extern::Memory(m)) => m, + _ => { + log::warn!("callee does not export a memory as \"memory\""); + let e = { #missing_mem_err }; + #handle_early_error + } + }; + // Wiggle does not expose any methods for + // functions to re-enter the WebAssembly module, + // or expose the memory via non-wiggle mechanisms. + // Therefore, creating a new BorrowChecker at the + // root of each function invocation is correct. + let bc = #runtime::BorrowChecker::new(); + let mem = #runtime::WasmtimeGuestMemory::new( mem, bc ); + #target_module::#name_ident( + &mut my_cx.borrow_mut(), + &mem, + #(#arg_names),* + ) + } + } + ); + } +}