From 39e014aafb5b579d42698034d7028b4a013de318 Mon Sep 17 00:00:00 2001 From: Nathan Horrigan Date: Wed, 11 Aug 2021 21:23:08 +0100 Subject: [PATCH 1/4] Add iOS support for Headless Wasmer --- Cargo.lock | 1 + Makefile | 4 ++ examples/engine_headless.rs | 57 ++++++++++--------- lib/api/Cargo.toml | 1 + lib/c-api/Cargo.toml | 2 + lib/c-api/build.rs | 2 +- lib/c-api/src/wasm_c_api/engine.rs | 1 - .../wasm_c_api/unstable/parser/operator.rs | 2 +- lib/compiler-llvm/src/translator/stackmap.rs | 2 +- lib/emscripten/src/syscalls/unix.rs | 32 +++++------ lib/emscripten/src/time.rs | 6 +- lib/vm/src/libcalls.rs | 4 +- lib/vm/src/trap/traphandlers.rs | 10 ++-- lib/wasi/src/syscalls/mod.rs | 4 +- 14 files changed, 68 insertions(+), 60 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d2903de4a28..47276364150 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2492,6 +2492,7 @@ dependencies = [ "wasmer-middlewares", "wasmer-types", "wasmer-wasi", + "wasmparser 0.78.2", ] [[package]] diff --git a/Makefile b/Makefile index f0d1a1fb813..1872aa5e7b0 100644 --- a/Makefile +++ b/Makefile @@ -483,6 +483,10 @@ build-capi-headless-all: capi-setup RUSTFLAGS="${RUSTFLAGS}" cargo build --manifest-path lib/c-api/Cargo.toml --release \ --no-default-features --features universal,dylib,staticlib,wasi +build-capi-headless-ios: capi-setup + RUSTFLAGS="${RUSTFLAGS}" cargo lipo --manifest-path lib/c-api/Cargo.toml --release \ + --no-default-features --features dylib,wasi + ##### # # Testing. diff --git a/examples/engine_headless.rs b/examples/engine_headless.rs index 41f8b3fcded..472f306474f 100644 --- a/examples/engine_headless.rs +++ b/examples/engine_headless.rs @@ -45,19 +45,26 @@ //! //! Ready? +use std::fs::File; +use std::path::Path; +use std::str::FromStr; use tempfile::NamedTempFile; use wasmer::imports; use wasmer::wat2wasm; use wasmer::Instance; use wasmer::Module; +use wasmer::RuntimeError; use wasmer::Store; use wasmer::Value; +use wasmer_compiler::{CpuFeature, Target, Triple}; use wasmer_compiler_cranelift::Cranelift; +use wasmer_compiler_llvm::LLVM; use wasmer_engine_dylib::Dylib; fn main() -> Result<(), Box> { // First step, let's compile the Wasm module and serialize it. // Note: we need a compiler here. + let mut dylib_file = Path::new("./sum.dylib"); let serialized_module_file = { // Let's declare the Wasm module with the text representation. let wasm_bytes = wat2wasm( @@ -73,23 +80,20 @@ fn main() -> Result<(), Box> { .as_bytes(), )?; - // Define a compiler configuration. - // - // In this situation, the compiler is - // `wasmer_compiler_cranelift`. The compiler is responsible to - // compile the Wasm module into executable code. - let compiler_config = Cranelift::default(); + let compiler_config = LLVM::default(); + let triple = Triple::from_str("aarch64-apple-ios") + .map_err(|error| RuntimeError::new(error.to_string()))?; + + // Let's define a CPU feature. + let mut cpu_feature = CpuFeature::set(); + cpu_feature.insert(CpuFeature::from_str("sse2")?); + + // Let's build the target. + let target = Target::new(triple, cpu_feature); + println!("Chosen target: {:?}", target); println!("Creating Dylib engine..."); - // Define the engine that will drive everything. - // - // In this case, the engine is `wasmer_engine_dylib` which - // means that a shared object is going to be generated. So - // when we are going to serialize the compiled Wasm module, we - // are going to store it in a file with the `.so` extension - // for example (or `.dylib`, or `.dll` depending of the - // platform). - let engine = Dylib::new(compiler_config).engine(); + let engine = Dylib::new(compiler_config).target(target).engine(); // Create a store, that holds the engine. let store = Store::new(&engine); @@ -101,10 +105,7 @@ fn main() -> Result<(), Box> { println!("Serializing module..."); // Here we go. Let's serialize the compiled Wasm module in a // file. - let serialized_module_file = NamedTempFile::new()?; - module.serialize_to_file(&serialized_module_file)?; - - serialized_module_file + module.serialize_to_file(dylib_file)?; }; // Second step, deserialize the compiled Wasm module, and execute @@ -122,8 +123,8 @@ fn main() -> Result<(), Box> { // because Wasmer can't assert the bytes are valid (see the // `wasmer::Module::deserialize`'s documentation to learn // more). - let module = unsafe { Module::deserialize_from_file(&store, serialized_module_file) }?; - + let module = unsafe { Module::deserialize_from_file(&store, dylib_file) }?; + println!("Done..."); // Congrats, the Wasm module has been deserialized! Now let's // execute it for the sake of having a complete example. @@ -131,17 +132,17 @@ fn main() -> Result<(), Box> { // any imports, it's an empty object. let import_object = imports! {}; - println!("Instantiating module..."); + // println!("Instantiating module..."); // Let's instantiate the Wasm module. - let instance = Instance::new(&module, &import_object)?; + // let instance = Instance::new(&module, &import_object)?; - println!("Calling `sum` function..."); + // println!("Calling `sum` function..."); // The Wasm module exports a function called `sum`. - let sum = instance.exports.get_function("sum")?; - let results = sum.call(&[Value::I32(1), Value::I32(2)])?; + // let sum = instance.exports.get_function("sum")?; + // let results = sum.call(&[Value::I32(1), Value::I32(2)])?; - println!("Results: {:?}", results); - assert_eq!(results.to_vec(), vec![Value::I32(3)]); + // println!("Results: {:?}", results); + // assert_eq!(results.to_vec(), vec![Value::I32(3)]); } Ok(()) diff --git a/lib/api/Cargo.toml b/lib/api/Cargo.toml index e6eeb661898..f72c4c7e1c1 100644 --- a/lib/api/Cargo.toml +++ b/lib/api/Cargo.toml @@ -28,6 +28,7 @@ thiserror = "1.0" more-asserts = "0.2" # - Optional shared dependencies. wat = { version = "1.0", optional = true } +wasmparser = { version = "0.78", default-features = false, optional = true } # Dependencies and Development Dependencies for `sys`. [target.'cfg(not(target_arch = "wasm32"))'.dependencies] diff --git a/lib/c-api/Cargo.toml b/lib/c-api/Cargo.toml index 5335789a708..c1b06dc9651 100644 --- a/lib/c-api/Cargo.toml +++ b/lib/c-api/Cargo.toml @@ -23,6 +23,8 @@ crate-type = ["cdylib", "rlib", "staticlib"] # We rename `wasmer` to `wasmer-api` to avoid the conflict with this # library name (see `[lib]`). wasmer-api = { version = "2.0.0", path = "../api", default-features = false, package = "wasmer" } +wasmparser = { version = "0.78", default-features = false, optional = false } + wasmer-compiler-cranelift = { version = "2.0.0", path = "../compiler-cranelift", optional = true } wasmer-compiler-singlepass = { version = "2.0.0", path = "../compiler-singlepass", optional = true } wasmer-compiler-llvm = { version = "2.0.0", path = "../compiler-llvm", optional = true } diff --git a/lib/c-api/build.rs b/lib/c-api/build.rs index ce423ac12e1..1af83f9ff71 100644 --- a/lib/c-api/build.rs +++ b/lib/c-api/build.rs @@ -255,7 +255,7 @@ fn build_inline_c_env_vars() { shared_object_dir = shared_object_dir, lib = if cfg!(target_os = "windows") { "wasmer.dll".to_string() - } else if cfg!(target_os = "macos") { + } else if cfg!(target_vendor = "apple") { "libwasmer.dylib".to_string() } else { let path = format!( diff --git a/lib/c-api/src/wasm_c_api/engine.rs b/lib/c-api/src/wasm_c_api/engine.rs index 058697393b0..0cfee66c441 100644 --- a/lib/c-api/src/wasm_c_api/engine.rs +++ b/lib/c-api/src/wasm_c_api/engine.rs @@ -23,7 +23,6 @@ use wasmer_engine_universal::Universal; /// /// This is a Wasmer-specific type with Wasmer-specific functions for /// manipulating it. -#[cfg(feature = "compiler")] #[derive(Debug, Copy, Clone)] #[repr(C)] pub enum wasmer_compiler_t { diff --git a/lib/c-api/src/wasm_c_api/unstable/parser/operator.rs b/lib/c-api/src/wasm_c_api/unstable/parser/operator.rs index 377fa332f9a..3349984a361 100644 --- a/lib/c-api/src/wasm_c_api/unstable/parser/operator.rs +++ b/lib/c-api/src/wasm_c_api/unstable/parser/operator.rs @@ -1,4 +1,4 @@ -use wasmer_api::wasmparser::Operator; +use wasmparser::Operator; #[repr(C)] #[allow(non_camel_case_types)] diff --git a/lib/compiler-llvm/src/translator/stackmap.rs b/lib/compiler-llvm/src/translator/stackmap.rs index d23ebd1a57e..cb6abc48fb5 100644 --- a/lib/compiler-llvm/src/translator/stackmap.rs +++ b/lib/compiler-llvm/src/translator/stackmap.rs @@ -54,7 +54,7 @@ pub enum StackmapEntryKind { impl StackmapEntry { #[cfg(all( - any(target_os = "freebsd", target_os = "linux", target_os = "macos"), + any(target_os = "freebsd", target_os = "linux", target_vendor = "apple"), target_arch = "x86_64" ))] pub fn populate_msm( diff --git a/lib/emscripten/src/syscalls/unix.rs b/lib/emscripten/src/syscalls/unix.rs index ee4874acf65..265aabf28a3 100644 --- a/lib/emscripten/src/syscalls/unix.rs +++ b/lib/emscripten/src/syscalls/unix.rs @@ -1,5 +1,5 @@ use crate::{ptr::WasmPtr, varargs::VarArgs, LibcDirWrapper}; -#[cfg(target_os = "macos")] +#[cfg(target_vendor = "apple")] use libc::size_t; /// NOTE: TODO: These syscalls only support wasm_32 for now because they assume offsets are u32 /// Syscall list: https://www.cs.utexas.edu/~bismith/test/syscalls/syscalls32.html @@ -121,7 +121,7 @@ use std::io::Error; use std::mem; // Linking to functions that are not provided by rust libc -#[cfg(target_os = "macos")] +#[cfg(target_vendor = "apple")] #[link(name = "c")] extern "C" { pub fn wait4(pid: pid_t, status: *mut c_int, options: c_int, rusage: *mut rusage) -> pid_t; @@ -140,19 +140,19 @@ extern "C" { pub fn lstat(path: *const libc::c_char, buf: *mut stat) -> c_int; } -#[cfg(not(any(target_os = "freebsd", target_os = "macos", target_os = "android")))] +#[cfg(not(any(target_os = "freebsd", target_vendor = "apple", target_os = "android")))] use libc::fallocate; #[cfg(target_os = "freebsd")] use libc::madvise; -#[cfg(not(any(target_os = "freebsd", target_os = "macos")))] +#[cfg(not(any(target_os = "freebsd", target_vendor = "apple")))] use libc::{fdatasync, ftruncate64, lstat, madvise, wait4}; // Another conditional constant for name resolution: Macos et iOS use // SO_NOSIGPIPE as a setsockopt flag to disable SIGPIPE emission on socket. // Other platforms do otherwise. -#[cfg(target_os = "macos")] +#[cfg(target_vendor = "apple")] use libc::SO_NOSIGPIPE; -#[cfg(not(target_os = "macos"))] +#[cfg(not(target_vendor = "apple"))] const SO_NOSIGPIPE: c_int = 0; /// open @@ -269,7 +269,7 @@ pub fn ___syscall194(ctx: &EmEnv, _which: c_int, mut varargs: VarArgs) -> c_int debug!("emscripten::___syscall194 (ftruncate64) {}", _which); let _fd: c_int = varargs.get(ctx); let _length: i64 = varargs.get(ctx); - #[cfg(not(any(target_os = "freebsd", target_os = "macos")))] + #[cfg(not(any(target_os = "freebsd", target_vendor = "apple")))] unsafe { ftruncate64(_fd, _length) } @@ -277,7 +277,7 @@ pub fn ___syscall194(ctx: &EmEnv, _which: c_int, mut varargs: VarArgs) -> c_int unsafe { ftruncate(_fd, _length) } - #[cfg(target_os = "macos")] + #[cfg(target_vendor = "apple")] unimplemented!("emscripten::___syscall194 (ftruncate64) {}", _which) } @@ -639,7 +639,7 @@ pub fn ___syscall102(ctx: &EmEnv, _which: c_int, mut varargs: VarArgs) -> c_int let mut host_address: sockaddr = sockaddr { sa_family: Default::default(), sa_data: Default::default(), - #[cfg(any(target_os = "freebsd", target_os = "macos"))] + #[cfg(any(target_os = "freebsd", target_vendor = "apple"))] sa_len: Default::default(), }; let fd = unsafe { accept(socket, &mut host_address, address_len_addr) }; @@ -672,7 +672,7 @@ pub fn ___syscall102(ctx: &EmEnv, _which: c_int, mut varargs: VarArgs) -> c_int let mut sock_addr_host: sockaddr = sockaddr { sa_family: Default::default(), sa_data: Default::default(), - #[cfg(any(target_os = "freebsd", target_os = "macos"))] + #[cfg(any(target_os = "freebsd", target_vendor = "apple"))] sa_len: Default::default(), }; let ret = unsafe { @@ -1013,14 +1013,14 @@ pub fn ___syscall196(ctx: &EmEnv, _which: i32, mut varargs: VarArgs) -> i32 { unsafe { let mut stat: stat = std::mem::zeroed(); - #[cfg(target_os = "macos")] + #[cfg(target_vendor = "apple")] let stat_ptr = &mut stat as *mut stat as *mut c_void; - #[cfg(not(target_os = "macos"))] + #[cfg(not(target_vendor = "apple"))] let stat_ptr = &mut stat as *mut stat; - #[cfg(target_os = "macos")] + #[cfg(target_vendor = "apple")] let ret = lstat64(real_path, stat_ptr); - #[cfg(not(target_os = "macos"))] + #[cfg(not(target_vendor = "apple"))] let ret = lstat(real_path, stat_ptr); debug!("ret: {}", ret); @@ -1131,11 +1131,11 @@ pub fn ___syscall324(ctx: &EmEnv, _which: c_int, mut varargs: VarArgs) -> c_int let _mode: c_int = varargs.get(ctx); let _offset: off_t = varargs.get(ctx); let _len: off_t = varargs.get(ctx); - #[cfg(not(any(target_os = "freebsd", target_os = "macos", target_os = "android")))] + #[cfg(not(any(target_os = "freebsd", target_vendor = "apple", target_os = "android")))] unsafe { fallocate(_fd, _mode, _offset, _len) } - #[cfg(any(target_os = "freebsd", target_os = "macos", target_os = "android"))] + #[cfg(any(target_os = "freebsd", target_vendor = "apple", target_os = "android"))] { unimplemented!("emscripten::___syscall324 (fallocate) {}", _which) } diff --git a/lib/emscripten/src/time.rs b/lib/emscripten/src/time.rs index 1827b89b1ee..7bb57e68d0c 100644 --- a/lib/emscripten/src/time.rs +++ b/lib/emscripten/src/time.rs @@ -35,11 +35,11 @@ use libc::{CLOCK_MONOTONIC, CLOCK_REALTIME}; #[cfg(target_os = "freebsd")] const CLOCK_MONOTONIC_COARSE: clockid_t = 6; -#[cfg(target_os = "macos")] +#[cfg(target_vendor = "apple")] use libc::CLOCK_REALTIME; -#[cfg(target_os = "macos")] +#[cfg(target_vendor = "apple")] const CLOCK_MONOTONIC: clockid_t = 1; -#[cfg(target_os = "macos")] +#[cfg(target_vendor = "apple")] const CLOCK_MONOTONIC_COARSE: clockid_t = 6; // some assumptions about the constants when targeting windows diff --git a/lib/vm/src/libcalls.rs b/lib/vm/src/libcalls.rs index e60704d02ed..301568e4b2e 100644 --- a/lib/vm/src/libcalls.rs +++ b/lib/vm/src/libcalls.rs @@ -858,9 +858,9 @@ impl LibCall { Self::RaiseTrap => "wasmer_vm_raise_trap", // We have to do this because macOS requires a leading `_` and it's not // a normal function, it's a static variable, so we have to do it manually. - #[cfg(target_os = "macos")] + #[cfg(target_vendor = "apple")] Self::Probestack => "_wasmer_vm_probestack", - #[cfg(not(target_os = "macos"))] + #[cfg(not(target_vendor = "apple"))] Self::Probestack => "wasmer_vm_probestack", } } diff --git a/lib/vm/src/trap/traphandlers.rs b/lib/vm/src/trap/traphandlers.rs index aff2b443a23..4a4c7a0f1f3 100644 --- a/lib/vm/src/trap/traphandlers.rs +++ b/lib/vm/src/trap/traphandlers.rs @@ -82,12 +82,12 @@ cfg_if::cfg_if! { // On ARM, handle Unaligned Accesses. // On Darwin, guard page accesses are raised as SIGBUS. - if cfg!(target_arch = "arm") || cfg!(target_os = "macos") { + if cfg!(target_arch = "arm") || cfg!(target_vendor = "apple") { register(&mut PREV_SIGBUS, libc::SIGBUS); } } - #[cfg(target_os = "macos")] + #[cfg(target_vendor = "apple")] unsafe fn thread_stack() -> (usize, usize) { let this_thread = libc::pthread_self(); let stackaddr = libc::pthread_get_stackaddr_np(this_thread); @@ -95,7 +95,7 @@ cfg_if::cfg_if! { (stackaddr as usize - stacksize, stacksize) } - #[cfg(not(target_os = "macos"))] + #[cfg(not(target_vendor = "apple"))] unsafe fn thread_stack() -> (usize, usize) { let this_thread = libc::pthread_self(); let mut thread_attrs: libc::pthread_attr_t = mem::zeroed(); @@ -219,10 +219,10 @@ cfg_if::cfg_if! { } else if #[cfg(all(target_os = "android", target_arch = "aarch64"))] { let cx = &*(cx as *const libc::ucontext_t); cx.uc_mcontext.pc as *const u8 - } else if #[cfg(all(target_os = "macos", target_arch = "x86_64"))] { + } else if #[cfg(all(target_vendor = "apple", target_arch = "x86_64"))] { let cx = &*(cx as *const libc::ucontext_t); (*cx.uc_mcontext).__ss.__rip as *const u8 - } else if #[cfg(all(target_os = "macos", target_arch = "aarch64"))] { + } else if #[cfg(all(target_vendor = "apple", target_arch = "aarch64"))] { use std::mem; // TODO: This should be integrated into rust/libc // Related issue: https://github.com/rust-lang/libc/issues/1977 diff --git a/lib/wasi/src/syscalls/mod.rs b/lib/wasi/src/syscalls/mod.rs index e172ca5782e..921ef315d06 100644 --- a/lib/wasi/src/syscalls/mod.rs +++ b/lib/wasi/src/syscalls/mod.rs @@ -8,7 +8,7 @@ pub mod types { target_os = "freebsd", target_os = "linux", target_os = "android", - target_os = "macos" + target_vendor = "apple" ))] pub mod unix; #[cfg(any(target_os = "windows"))] @@ -36,7 +36,7 @@ use wasmer::{Memory, RuntimeError, Value, WasmCell}; target_os = "freebsd", target_os = "linux", target_os = "android", - target_os = "macos" + target_vendor = "apple" ))] pub use unix::*; From ab3b0b5c0c286037c47f49269be7b51a2edc5243 Mon Sep 17 00:00:00 2001 From: Nathan Horrigan Date: Wed, 11 Aug 2021 21:27:28 +0100 Subject: [PATCH 2/4] Revert `engine_headless.rs` --- examples/engine_headless.rs | 57 ++++++------ examples/engine_headless_ios.rs | 155 ++++++++++++++++++++++++++++++++ 2 files changed, 183 insertions(+), 29 deletions(-) create mode 100644 examples/engine_headless_ios.rs diff --git a/examples/engine_headless.rs b/examples/engine_headless.rs index 472f306474f..41f8b3fcded 100644 --- a/examples/engine_headless.rs +++ b/examples/engine_headless.rs @@ -45,26 +45,19 @@ //! //! Ready? -use std::fs::File; -use std::path::Path; -use std::str::FromStr; use tempfile::NamedTempFile; use wasmer::imports; use wasmer::wat2wasm; use wasmer::Instance; use wasmer::Module; -use wasmer::RuntimeError; use wasmer::Store; use wasmer::Value; -use wasmer_compiler::{CpuFeature, Target, Triple}; use wasmer_compiler_cranelift::Cranelift; -use wasmer_compiler_llvm::LLVM; use wasmer_engine_dylib::Dylib; fn main() -> Result<(), Box> { // First step, let's compile the Wasm module and serialize it. // Note: we need a compiler here. - let mut dylib_file = Path::new("./sum.dylib"); let serialized_module_file = { // Let's declare the Wasm module with the text representation. let wasm_bytes = wat2wasm( @@ -80,20 +73,23 @@ fn main() -> Result<(), Box> { .as_bytes(), )?; - let compiler_config = LLVM::default(); - let triple = Triple::from_str("aarch64-apple-ios") - .map_err(|error| RuntimeError::new(error.to_string()))?; - - // Let's define a CPU feature. - let mut cpu_feature = CpuFeature::set(); - cpu_feature.insert(CpuFeature::from_str("sse2")?); - - // Let's build the target. - let target = Target::new(triple, cpu_feature); - println!("Chosen target: {:?}", target); + // Define a compiler configuration. + // + // In this situation, the compiler is + // `wasmer_compiler_cranelift`. The compiler is responsible to + // compile the Wasm module into executable code. + let compiler_config = Cranelift::default(); println!("Creating Dylib engine..."); - let engine = Dylib::new(compiler_config).target(target).engine(); + // Define the engine that will drive everything. + // + // In this case, the engine is `wasmer_engine_dylib` which + // means that a shared object is going to be generated. So + // when we are going to serialize the compiled Wasm module, we + // are going to store it in a file with the `.so` extension + // for example (or `.dylib`, or `.dll` depending of the + // platform). + let engine = Dylib::new(compiler_config).engine(); // Create a store, that holds the engine. let store = Store::new(&engine); @@ -105,7 +101,10 @@ fn main() -> Result<(), Box> { println!("Serializing module..."); // Here we go. Let's serialize the compiled Wasm module in a // file. - module.serialize_to_file(dylib_file)?; + let serialized_module_file = NamedTempFile::new()?; + module.serialize_to_file(&serialized_module_file)?; + + serialized_module_file }; // Second step, deserialize the compiled Wasm module, and execute @@ -123,8 +122,8 @@ fn main() -> Result<(), Box> { // because Wasmer can't assert the bytes are valid (see the // `wasmer::Module::deserialize`'s documentation to learn // more). - let module = unsafe { Module::deserialize_from_file(&store, dylib_file) }?; - println!("Done..."); + let module = unsafe { Module::deserialize_from_file(&store, serialized_module_file) }?; + // Congrats, the Wasm module has been deserialized! Now let's // execute it for the sake of having a complete example. @@ -132,17 +131,17 @@ fn main() -> Result<(), Box> { // any imports, it's an empty object. let import_object = imports! {}; - // println!("Instantiating module..."); + println!("Instantiating module..."); // Let's instantiate the Wasm module. - // let instance = Instance::new(&module, &import_object)?; + let instance = Instance::new(&module, &import_object)?; - // println!("Calling `sum` function..."); + println!("Calling `sum` function..."); // The Wasm module exports a function called `sum`. - // let sum = instance.exports.get_function("sum")?; - // let results = sum.call(&[Value::I32(1), Value::I32(2)])?; + let sum = instance.exports.get_function("sum")?; + let results = sum.call(&[Value::I32(1), Value::I32(2)])?; - // println!("Results: {:?}", results); - // assert_eq!(results.to_vec(), vec![Value::I32(3)]); + println!("Results: {:?}", results); + assert_eq!(results.to_vec(), vec![Value::I32(3)]); } Ok(()) diff --git a/examples/engine_headless_ios.rs b/examples/engine_headless_ios.rs new file mode 100644 index 00000000000..472f306474f --- /dev/null +++ b/examples/engine_headless_ios.rs @@ -0,0 +1,155 @@ +//! Defining an engine in Wasmer is one of the fundamental steps. +//! +//! This example illustrates a neat feature of engines: their ability +//! to run in a headless mode. At the time of writing, all engines +//! have a headless mode, but it's not a requirement of the `Engine` +//! trait (defined in the `wasmer_engine` crate). +//! +//! What problem does it solve, and what does it mean? +//! +//! Once a Wasm module is compiled into executable code and stored +//! somewhere (e.g. in memory with the Universal engine, or in a +//! shared object file with the Dylib engine), the module can be +//! instantiated and executed. But imagine for a second the following +//! scenario: +//! +//! * Modules are compiled ahead of time, to be instantiated later +//! on. +//! * Modules are cross-compiled on a machine ahead of time +//! to be run on another machine later one. +//! +//! In both scenarios, the environment where the compiled Wasm module +//! will be executed can be very constrained. For such particular +//! contexts, Wasmer can be compiled _without_ the compilers, so that +//! the `wasmer` binary is as small as possible. Indeed, there is no +//! need for a compiler since the Wasm module is already compiled. All +//! we need is an engine that _only_ drives the instantiation and +//! execution of the Wasm module. +//! +//! And that, that's a headless engine. +//! +//! To achieve such a scenario, a Wasm module must be compiled, then +//! serialized —for example into a file—, then later, potentially on +//! another machine, deserialized. The next steps are classical: The +//! Wasm module is instantiated and executed. +//! +//! This example uses a `compiler` because it illustrates the entire +//! workflow, but keep in mind the compiler isn't required after the +//! compilation step. +//! +//! You can run the example directly by executing in Wasmer root: +//! +//! ```shell +//! cargo run --example engine-headless --release --features "cranelift" +//! ``` +//! +//! Ready? + +use std::fs::File; +use std::path::Path; +use std::str::FromStr; +use tempfile::NamedTempFile; +use wasmer::imports; +use wasmer::wat2wasm; +use wasmer::Instance; +use wasmer::Module; +use wasmer::RuntimeError; +use wasmer::Store; +use wasmer::Value; +use wasmer_compiler::{CpuFeature, Target, Triple}; +use wasmer_compiler_cranelift::Cranelift; +use wasmer_compiler_llvm::LLVM; +use wasmer_engine_dylib::Dylib; + +fn main() -> Result<(), Box> { + // First step, let's compile the Wasm module and serialize it. + // Note: we need a compiler here. + let mut dylib_file = Path::new("./sum.dylib"); + let serialized_module_file = { + // Let's declare the Wasm module with the text representation. + let wasm_bytes = wat2wasm( + r#" +(module + (type $sum_t (func (param i32 i32) (result i32))) + (func $sum_f (type $sum_t) (param $x i32) (param $y i32) (result i32) + local.get $x + local.get $y + i32.add) + (export "sum" (func $sum_f))) +"# + .as_bytes(), + )?; + + let compiler_config = LLVM::default(); + let triple = Triple::from_str("aarch64-apple-ios") + .map_err(|error| RuntimeError::new(error.to_string()))?; + + // Let's define a CPU feature. + let mut cpu_feature = CpuFeature::set(); + cpu_feature.insert(CpuFeature::from_str("sse2")?); + + // Let's build the target. + let target = Target::new(triple, cpu_feature); + println!("Chosen target: {:?}", target); + + println!("Creating Dylib engine..."); + let engine = Dylib::new(compiler_config).target(target).engine(); + + // Create a store, that holds the engine. + let store = Store::new(&engine); + + println!("Compiling module..."); + // Let's compile the Wasm module. + let module = Module::new(&store, wasm_bytes)?; + + println!("Serializing module..."); + // Here we go. Let's serialize the compiled Wasm module in a + // file. + module.serialize_to_file(dylib_file)?; + }; + + // Second step, deserialize the compiled Wasm module, and execute + // it, for example with Wasmer without a compiler. + { + println!("Creating headless Dylib engine..."); + // We create a headless Dylib engine. + let engine = Dylib::headless().engine(); + let store = Store::new(&engine); + + println!("Deserializing module..."); + // Here we go. + // + // Deserialize the compiled Wasm module. This code is unsafe + // because Wasmer can't assert the bytes are valid (see the + // `wasmer::Module::deserialize`'s documentation to learn + // more). + let module = unsafe { Module::deserialize_from_file(&store, dylib_file) }?; + println!("Done..."); + // Congrats, the Wasm module has been deserialized! Now let's + // execute it for the sake of having a complete example. + + // Create an import object. Since our Wasm module didn't declare + // any imports, it's an empty object. + let import_object = imports! {}; + + // println!("Instantiating module..."); + // Let's instantiate the Wasm module. + // let instance = Instance::new(&module, &import_object)?; + + // println!("Calling `sum` function..."); + // The Wasm module exports a function called `sum`. + // let sum = instance.exports.get_function("sum")?; + // let results = sum.call(&[Value::I32(1), Value::I32(2)])?; + + // println!("Results: {:?}", results); + // assert_eq!(results.to_vec(), vec![Value::I32(3)]); + } + + Ok(()) +} + +#[test] +#[cfg(not(any(windows, target_arch = "aarch64", target_env = "musl")))] +fn test_engine_headless() -> Result<(), Box> { + main() +} From 3c8d8bd97a8be09df0799b199933fad22ef13473 Mon Sep 17 00:00:00 2001 From: Nathan Horrigan Date: Wed, 11 Aug 2021 21:31:12 +0100 Subject: [PATCH 3/4] Add dedicated iOS example file --- Cargo.toml | 5 +++++ examples/engine_headless_ios.rs | 8 ++++---- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c9caf5d3754..8cff8153616 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -177,6 +177,11 @@ name = "engine-headless" path = "examples/engine_headless.rs" required-features = ["cranelift"] +[[example]] +name = "engine-headless-ios" +path = "examples/engine_headless_ios.rs" +required-features = ["cranelift"] + [[example]] name = "cross-compilation" path = "examples/engine_cross_compilation.rs" diff --git a/examples/engine_headless_ios.rs b/examples/engine_headless_ios.rs index 472f306474f..1877d6d76b7 100644 --- a/examples/engine_headless_ios.rs +++ b/examples/engine_headless_ios.rs @@ -64,7 +64,6 @@ use wasmer_engine_dylib::Dylib; fn main() -> Result<(), Box> { // First step, let's compile the Wasm module and serialize it. // Note: we need a compiler here. - let mut dylib_file = Path::new("./sum.dylib"); let serialized_module_file = { // Let's declare the Wasm module with the text representation. let wasm_bytes = wat2wasm( @@ -101,11 +100,12 @@ fn main() -> Result<(), Box> { println!("Compiling module..."); // Let's compile the Wasm module. let module = Module::new(&store, wasm_bytes)?; - - println!("Serializing module..."); // Here we go. Let's serialize the compiled Wasm module in a // file. + println!("Serializing module..."); + let mut dylib_file = Path::new("./sum.dylib"); module.serialize_to_file(dylib_file)?; + dylib_file }; // Second step, deserialize the compiled Wasm module, and execute @@ -150,6 +150,6 @@ fn main() -> Result<(), Box> { #[test] #[cfg(not(any(windows, target_arch = "aarch64", target_env = "musl")))] -fn test_engine_headless() -> Result<(), Box> { +fn test_engine_headless_ios() -> Result<(), Box> { main() } From 35fd710c1f523e4ca63ef651479b872e92b62ca8 Mon Sep 17 00:00:00 2001 From: Nathan Horrigan Date: Wed, 11 Aug 2021 21:57:46 +0100 Subject: [PATCH 4/4] Remove cargo changes --- Cargo.lock | 1 - lib/api/Cargo.toml | 1 - lib/c-api/Cargo.toml | 2 -- 3 files changed, 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 47276364150..d2903de4a28 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2492,7 +2492,6 @@ dependencies = [ "wasmer-middlewares", "wasmer-types", "wasmer-wasi", - "wasmparser 0.78.2", ] [[package]] diff --git a/lib/api/Cargo.toml b/lib/api/Cargo.toml index f72c4c7e1c1..e6eeb661898 100644 --- a/lib/api/Cargo.toml +++ b/lib/api/Cargo.toml @@ -28,7 +28,6 @@ thiserror = "1.0" more-asserts = "0.2" # - Optional shared dependencies. wat = { version = "1.0", optional = true } -wasmparser = { version = "0.78", default-features = false, optional = true } # Dependencies and Development Dependencies for `sys`. [target.'cfg(not(target_arch = "wasm32"))'.dependencies] diff --git a/lib/c-api/Cargo.toml b/lib/c-api/Cargo.toml index c1b06dc9651..5335789a708 100644 --- a/lib/c-api/Cargo.toml +++ b/lib/c-api/Cargo.toml @@ -23,8 +23,6 @@ crate-type = ["cdylib", "rlib", "staticlib"] # We rename `wasmer` to `wasmer-api` to avoid the conflict with this # library name (see `[lib]`). wasmer-api = { version = "2.0.0", path = "../api", default-features = false, package = "wasmer" } -wasmparser = { version = "0.78", default-features = false, optional = false } - wasmer-compiler-cranelift = { version = "2.0.0", path = "../compiler-cranelift", optional = true } wasmer-compiler-singlepass = { version = "2.0.0", path = "../compiler-singlepass", optional = true } wasmer-compiler-llvm = { version = "2.0.0", path = "../compiler-llvm", optional = true }