diff --git a/crates/c-api/include/wasmtime.h b/crates/c-api/include/wasmtime.h index 7217d62a3c07..857a6a70ce79 100644 --- a/crates/c-api/include/wasmtime.h +++ b/crates/c-api/include/wasmtime.h @@ -920,24 +920,21 @@ WASM_API_EXTERN void wasmtime_externref_new_with_finalizer( WASM_API_EXTERN bool wasmtime_externref_data(wasm_val_t* val, void** datap); /** - * \brief This function will compile a WebAssembly binary and saves artifacts + * \brief This function serializes compiled module artifacts * as blob data. * - * \param engine this is engine that will provide the compiler. - * \param binary this it the input buffer with the WebAssembly Binary Format inside of - * it. This will be parsed and converted to the binary format. + * \param module the module * \param ret if the conversion is successful, this byte vector is filled in with * the serialized compiled module. * * \return a non-null error if parsing fails, or returns `NULL`. If parsing * fails then `ret` isn't touched. * - * This function does not take ownership of `binary` or `engine`, and the caller is + * This function does not take ownership of `module`, and the caller is * expected to deallocate the returned #wasmtime_error_t and #wasm_byte_vec_t. */ -WASM_API_EXTERN own wasmtime_error_t* wasmtime_compile_and_serialize( - wasm_engine_t* engine, - const wasm_byte_vec_t* binary, +WASM_API_EXTERN own wasmtime_error_t* wasmtime_module_serialize( + wasm_module_t* module, own wasm_byte_vec_t *ret ); diff --git a/crates/c-api/src/module.rs b/crates/c-api/src/module.rs index 16d1df34c8d8..451a893b6643 100644 --- a/crates/c-api/src/module.rs +++ b/crates/c-api/src/module.rs @@ -1,9 +1,9 @@ use crate::{ - handle_result, wasm_byte_vec_t, wasm_engine_t, wasm_exporttype_t, wasm_exporttype_vec_t, - wasm_importtype_t, wasm_importtype_vec_t, wasm_store_t, wasmtime_error_t, + handle_result, wasm_byte_vec_t, wasm_exporttype_t, wasm_exporttype_vec_t, wasm_importtype_t, + wasm_importtype_vec_t, wasm_store_t, wasmtime_error_t, }; use std::ptr; -use wasmtime::{compile_and_serialize, Engine, Module}; +use wasmtime::{Engine, Module}; #[repr(C)] #[derive(Clone)] @@ -132,18 +132,14 @@ pub extern "C" fn wasm_module_obtain( } #[no_mangle] -pub extern "C" fn wasmtime_compile_and_serialize( - engine: &wasm_engine_t, - binary: &wasm_byte_vec_t, +pub extern "C" fn wasmtime_module_serialize( + module: &wasm_module_t, ret: &mut wasm_byte_vec_t, ) -> Option> { let mut result = Vec::new(); - handle_result( - compile_and_serialize(&engine.engine, binary.as_slice(), &mut result), - |()| { - ret.set_buffer(result); - }, - ) + handle_result(module.module.serialize(&mut result), |()| { + ret.set_buffer(result); + }) } #[no_mangle] diff --git a/crates/environ/src/module.rs b/crates/environ/src/module.rs index 332754abd46d..07444bf94c4e 100644 --- a/crates/environ/src/module.rs +++ b/crates/environ/src/module.rs @@ -137,7 +137,7 @@ impl TablePlan { /// A translated WebAssembly module, excluding the function bodies and /// memory initializers. -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct Module { /// A unique identifier (within this process) for this module. #[serde(skip_serializing, skip_deserializing, default = "Module::next_id")] @@ -181,7 +181,7 @@ pub struct Module { /// This is stored within a `Module` and it implements `Hash`, unlike `Module`, /// and is used as part of the cache key when we load compiled modules from the /// global cache. -#[derive(Debug, Hash, Serialize, Deserialize)] +#[derive(Debug, Clone, Hash, Serialize, Deserialize)] pub struct ModuleLocal { /// Unprocessed signatures exactly as provided by `declare_signature()`. pub signatures: PrimaryMap, diff --git a/crates/jit/src/instantiate.rs b/crates/jit/src/instantiate.rs index 196d706e373a..0e50a927d6f8 100644 --- a/crates/jit/src/instantiate.rs +++ b/crates/jit/src/instantiate.rs @@ -158,6 +158,8 @@ pub struct CompiledModule { traps: Traps, stack_maps: StackMaps, address_transform: ModuleAddressMap, + obj: Box<[u8]>, + unwind_info: Box<[ObjectUnwindInfo]>, } impl CompiledModule { @@ -191,7 +193,7 @@ impl CompiledModule { // Allocate all of the compiled functions into executable memory, // copying over their contents. let (code_memory, code_range, finished_functions, trampolines) = - build_code_memory(isa, &obj, &module, unwind_info).map_err(|message| { + build_code_memory(isa, &obj, &module, &unwind_info).map_err(|message| { SetupError::Instantiate(InstantiationError::Resource(format!( "failed to build code memory for functions: {}", message @@ -225,9 +227,25 @@ impl CompiledModule { traps, stack_maps, address_transform, + obj, + unwind_info, }) } + /// Extracts `CompilationArtifacts` from the compiled module. + pub fn to_compilation_artifacts(&self) -> CompilationArtifacts { + CompilationArtifacts { + module: (*self.module).clone(), + obj: self.obj.clone(), + unwind_info: self.unwind_info.clone(), + data_initializers: self.data_initializers.clone(), + traps: self.traps.clone(), + stack_maps: self.stack_maps.clone(), + address_transform: self.address_transform.clone(), + debug_info: self.code.dbg_jit_registration.is_some(), + } + } + /// Crate an `Instance` from this `CompiledModule`. /// /// Note that if only one instance of this module is needed, it may be more @@ -336,7 +354,7 @@ impl CompiledModule { /// Similar to `DataInitializer`, but owns its own copy of the data rather /// than holding a slice of the original module. -#[derive(Serialize, Deserialize)] +#[derive(Clone, Serialize, Deserialize)] pub struct OwnedDataInitializer { /// The location where the initialization is to be performed. location: DataInitializerLocation, @@ -372,7 +390,7 @@ fn build_code_memory( isa: &dyn TargetIsa, obj: &[u8], module: &Module, - unwind_info: Box<[ObjectUnwindInfo]>, + unwind_info: &Box<[ObjectUnwindInfo]>, ) -> Result< ( CodeMemory, @@ -386,7 +404,7 @@ fn build_code_memory( let mut code_memory = CodeMemory::new(); - let allocation = code_memory.allocate_for_object(&obj, &unwind_info)?; + let allocation = code_memory.allocate_for_object(&obj, unwind_info)?; // Second, create a PrimaryMap from result vector of pointers. let mut finished_functions = PrimaryMap::new(); diff --git a/crates/wasmtime/src/lib.rs b/crates/wasmtime/src/lib.rs index 817a3ec0653e..987b8db6a336 100644 --- a/crates/wasmtime/src/lib.rs +++ b/crates/wasmtime/src/lib.rs @@ -251,7 +251,7 @@ pub use crate::frame_info::FrameInfo; pub use crate::func::*; pub use crate::instance::Instance; pub use crate::linker::*; -pub use crate::module::{compile_and_serialize, Module}; +pub use crate::module::Module; pub use crate::r#ref::ExternRef; pub use crate::runtime::*; pub use crate::trap::Trap; diff --git a/crates/wasmtime/src/module.rs b/crates/wasmtime/src/module.rs index bbe1cb94539c..725e47eb5310 100644 --- a/crates/wasmtime/src/module.rs +++ b/crates/wasmtime/src/module.rs @@ -309,6 +309,18 @@ impl Module { }) } + /// Serialize artifacts in I/O. + pub fn serialize(&self, w: impl std::io::Write) -> Result<()> { + let artifacts = ( + compiler_fingerprint(self.engine.config()), + self.compiled.to_compilation_artifacts(), + ); + + bincode::serialize_into(w, &artifacts)?; + + Ok(()) + } + /// Read compiled module from I/O. pub unsafe fn deserialize(engine: &Engine, r: impl std::io::Read) -> Result { let expected_fingerprint = compiler_fingerprint(engine.config()); @@ -566,26 +578,6 @@ fn compiler_fingerprint(config: &Config) -> u64 { hasher.finish() } -/// Compile and write artifacts in I/O. -pub fn compile_and_serialize( - engine: &Engine, - bytes: impl AsRef<[u8]>, - w: impl std::io::Write, -) -> Result<()> { - #[cfg(feature = "wat")] - let bytes = wat::parse_bytes(bytes.as_ref())?; - - Module::validate(engine, bytes.as_ref())?; - let artifacts = ( - compiler_fingerprint(engine.config()), - CompilationArtifacts::build(engine.compiler(), bytes.as_ref())?, - ); - - bincode::serialize_into(w, &artifacts)?; - - Ok(()) -} - fn _assert_send_sync() { fn _assert() {} _assert::(); diff --git a/examples/serialize.c b/examples/serialize.c index 5a22a51b8a5e..87cc0672d994 100644 --- a/examples/serialize.c +++ b/examples/serialize.c @@ -40,6 +40,11 @@ int serialize(wasm_byte_vec_t* buffer) { wasm_engine_t *engine = wasm_engine_new(); assert(engine != NULL); + // With an engine we can create a *store* which is a long-lived group of wasm + // modules. + wasm_store_t *store = wasm_store_new(engine); + assert(store != NULL); + // Read our input file, which in this case is a wasm text file. FILE* file = fopen("examples/hello.wat", "r"); assert(file != NULL); @@ -62,10 +67,14 @@ int serialize(wasm_byte_vec_t* buffer) { // and serialize into buffer. printf("Compiling and serializing module...\n"); wasm_module_t *module = NULL; - error = wasmtime_compile_and_serialize(engine, &wasm, buffer); + error = wasmtime_module_new(store, &wasm, &module); wasm_byte_vec_delete(&wasm); if (error != NULL) exit_with_error("failed to compile module", error, NULL); + error = wasmtime_module_serialize(module, buffer); + wasm_module_delete(module); + if (error != NULL) + exit_with_error("failed to serialize module", error, NULL); printf("Serialized.\n"); diff --git a/examples/serialize.rs b/examples/serialize.rs index 728a8031bd56..d98b55001d4b 100644 --- a/examples/serialize.rs +++ b/examples/serialize.rs @@ -4,7 +4,7 @@ // You can execute this example with `cargo run --example serialize` use anyhow::Result; -use std::fs::{self, File}; +use std::fs::File; use std::io::{Seek, SeekFrom}; use wasmtime::*; @@ -17,9 +17,9 @@ fn serialize() -> Result { // Compile the wasm binary into an in-memory instance of a `Module`. println!("Compiling module..."); - let wasm = fs::read("examples/hello.wat")?; + let module = Module::from_file(&engine, "examples/hello.wat")?; let mut file = tempfile::tempfile()?; - compile_and_serialize(&engine, wasm, file.try_clone()?)?; + module.serialize(file.try_clone()?)?; file.seek(SeekFrom::Start(0))?; @@ -35,7 +35,7 @@ fn deserialize(file: File) -> Result<()> { let store = Store::default(); // Compile the wasm binary into an in-memory instance of a `Module`. - println!("Compiling module..."); + println!("Deserialize module..."); let module = unsafe { Module::deserialize(store.engine(), file)? }; // Here we handle the imports of the module, which in this case is our