Skip to content

Commit

Permalink
Try #2516:
Browse files Browse the repository at this point in the history
  • Loading branch information
bors[bot] authored Aug 12, 2021
2 parents 22634fa + 35fd710 commit a7f6f38
Show file tree
Hide file tree
Showing 12 changed files with 195 additions and 32 deletions.
5 changes: 5 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
4 changes: 4 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
155 changes: 155 additions & 0 deletions examples/engine_headless_ios.rs
Original file line number Diff line number Diff line change
@@ -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<dyn std::error::Error>> {
// First step, let's compile the Wasm module and serialize it.
// Note: we need a compiler here.
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)?;
// 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
// 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_ios() -> Result<(), Box<dyn std::error::Error>> {
main()
}
2 changes: 1 addition & 1 deletion lib/c-api/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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!(
Expand Down
1 change: 0 additions & 1 deletion lib/c-api/src/wasm_c_api/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion lib/c-api/src/wasm_c_api/unstable/parser/operator.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use wasmer_api::wasmparser::Operator;
use wasmparser::Operator;

#[repr(C)]
#[allow(non_camel_case_types)]
Expand Down
2 changes: 1 addition & 1 deletion lib/compiler-llvm/src/translator/stackmap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
32 changes: 16 additions & 16 deletions lib/emscripten/src/syscalls/unix.rs
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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;
Expand All @@ -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
Expand Down Expand Up @@ -269,15 +269,15 @@ 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)
}
#[cfg(target_os = "freebsd")]
unsafe {
ftruncate(_fd, _length)
}
#[cfg(target_os = "macos")]
#[cfg(target_vendor = "apple")]
unimplemented!("emscripten::___syscall194 (ftruncate64) {}", _which)
}

Expand Down Expand Up @@ -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) };
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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)
}
Expand Down
6 changes: 3 additions & 3 deletions lib/emscripten/src/time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions lib/vm/src/libcalls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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",
}
}
Expand Down
10 changes: 5 additions & 5 deletions lib/vm/src/trap/traphandlers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,20 +82,20 @@ 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);
let stacksize = libc::pthread_get_stacksize_np(this_thread);
(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();
Expand Down Expand Up @@ -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
Expand Down
Loading

0 comments on commit a7f6f38

Please sign in to comment.