diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a9b3942fd0..4052797e53a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ ## **[Unreleased]** ### Added +- [#2072](https://github.com/wasmerio/wasmer/pull/2072) Add `wasm_config_set_target`, along with `wasm_target_t`, `wasm_triple_t` and `wasm_cpu_features_t` in the unstable C API. - [#2059](https://github.com/wasmerio/wasmer/pull/2059) Ability to capture `stdout` and `stderr` with WASI in the C API. - [#2040](https://github.com/wasmerio/wasmer/pull/2040) Add `InstanceHandle::vmoffsets` to expose the offsets of the `vmctx` region. - [#2026](https://github.com/wasmerio/wasmer/pull/2010) Expose trap code of a `RuntimeError`, if it's a `Trap`. diff --git a/Cargo.lock b/Cargo.lock index 33805ac77e2..7c1c8a915db 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2321,6 +2321,7 @@ version = "1.0.1" dependencies = [ "cbindgen", "cfg-if 1.0.0", + "enumset", "inline-c", "lazy_static", "libc", diff --git a/lib/c-api/Cargo.toml b/lib/c-api/Cargo.toml index 9a6e0fb1065..da37986cef1 100644 --- a/lib/c-api/Cargo.toml +++ b/lib/c-api/Cargo.toml @@ -27,6 +27,7 @@ wasmer-engine-native = { version = "1.0.1", path = "../engine-native", optional wasmer-engine-object-file = { version = "1.0.1", path = "../engine-object-file", optional = true } wasmer-wasi = { version = "1.0.1", path = "../wasi", optional = true } wasmer-types = { version = "1.0.1", path = "../wasmer-types" } +enumset = "1.0" cfg-if = "1.0" lazy_static = "1.4" libc = { version = "^0.2", default-features = false } diff --git a/lib/c-api/build.rs b/lib/c-api/build.rs index 8212c3bfb37..8e9c17f259c 100644 --- a/lib/c-api/build.rs +++ b/lib/c-api/build.rs @@ -464,8 +464,20 @@ fn exclude_items_from_wasm_c_api(builder: Builder) -> Builder { .exclude_item("wasi_version_t") .exclude_item("wasm_config_set_compiler") .exclude_item("wasm_config_set_engine") + .exclude_item("wasm_config_set_target") + .exclude_item("wasm_cpu_features_add") + .exclude_item("wasm_cpu_features_delete") + .exclude_item("wasm_cpu_features_new") + .exclude_item("wasm_cpu_features_t") .exclude_item("wasm_module_name") .exclude_item("wasm_module_set_name") + .exclude_item("wasm_target_delete") + .exclude_item("wasm_target_new") + .exclude_item("wasm_target_t") + .exclude_item("wasm_triple_delete") + .exclude_item("wasm_triple_new") + .exclude_item("wasm_triple_new_from_host") + .exclude_item("wasm_triple_t") .exclude_item("wasmer_compiler_t") .exclude_item("wasmer_engine_t") .exclude_item("wat2wasm") diff --git a/lib/c-api/src/wasm_c_api/engine.rs b/lib/c-api/src/wasm_c_api/engine.rs index dd3b6fa416a..bfc3e4f09d7 100644 --- a/lib/c-api/src/wasm_c_api/engine.rs +++ b/lib/c-api/src/wasm_c_api/engine.rs @@ -1,3 +1,5 @@ +pub use super::unstable::engine::wasm_config_set_target; +use super::unstable::target_lexicon::wasm_target_t; use crate::error::{update_last_error, CApiError}; use cfg_if::cfg_if; use std::sync::Arc; @@ -93,6 +95,7 @@ pub struct wasm_config_t { engine: wasmer_engine_t, #[cfg(feature = "compiler")] compiler: wasmer_compiler_t, + pub(super) target: Option>, } /// Create a new default Wasmer configuration. @@ -396,7 +399,7 @@ pub unsafe extern "C" fn wasm_engine_delete(_engine: Option>) /// cbindgen:ignore #[no_mangle] pub extern "C" fn wasm_engine_new_with_config( - config: Box, + config: Option>, ) -> Option> { #[allow(dead_code)] fn return_with_error(msg: M) -> Option> @@ -408,7 +411,9 @@ pub extern "C" fn wasm_engine_new_with_config( }); return None; - }; + } + + let config = config?; cfg_if! { if #[cfg(feature = "compiler")] { @@ -447,7 +452,13 @@ pub extern "C" fn wasm_engine_new_with_config( wasmer_engine_t::JIT => { cfg_if! { if #[cfg(feature = "jit")] { - Arc::new(JIT::new(compiler_config).engine()) + let mut builder = JIT::new(compiler_config); + + if let Some(target) = config.target { + builder = builder.target(target.inner); + } + + Arc::new(builder.engine()) } else { return return_with_error("Wasmer has not been compiled with the `jit` feature."); } @@ -456,7 +467,13 @@ pub extern "C" fn wasm_engine_new_with_config( wasmer_engine_t::NATIVE => { cfg_if! { if #[cfg(feature = "native")] { - Arc::new(Native::new(compiler_config).engine()) + let mut builder = Native::new(compiler_config); + + if let Some(target) = config.target { + builder = builder.target(target.inner); + } + + Arc::new(builder.engine()) } else { return return_with_error("Wasmer has not been compiled with the `native` feature."); } @@ -467,7 +484,13 @@ pub extern "C" fn wasm_engine_new_with_config( // There are currently no uses of the object-file engine + compiler from the C API. // So we run in headless mode. if #[cfg(feature = "object-file")] { - Arc::new(ObjectFile::headless().engine()) + let mut builder = ObjectFile::headless(); + + if let Some(target) = config.target { + builder = builder.target(target.inner); + } + + Arc::new(builder.engine()) } else { return return_with_error("Wasmer has not been compiled with the `object-file` feature."); } @@ -480,7 +503,13 @@ pub extern "C" fn wasm_engine_new_with_config( wasmer_engine_t::JIT => { cfg_if! { if #[cfg(feature = "jit")] { - Arc::new(JIT::headless().engine()) + let mut builder = JIT::headless(); + + if let Some(target) = config.target { + builder = builder.target(target.inner); + } + + Arc::new(builder.engine()) } else { return return_with_error("Wasmer has not been compiled with the `jit` feature."); } @@ -489,7 +518,13 @@ pub extern "C" fn wasm_engine_new_with_config( wasmer_engine_t::NATIVE => { cfg_if! { if #[cfg(feature = "native")] { - Arc::new(Native::headless().engine()) + let mut builder = Native::headless(); + + if let Some(target) = config.target { + builder = builder.target(target.inner); + } + + Arc::new(builder.engine()) } else { return return_with_error("Wasmer has not been compiled with the `native` feature."); } @@ -498,7 +533,13 @@ pub extern "C" fn wasm_engine_new_with_config( wasmer_engine_t::OBJECT_FILE => { cfg_if! { if #[cfg(feature = "object-file")] { - Arc::new(ObjectFile::headless().engine()) + let mut builder = ObjectFile::headless(); + + if let Some(target) = config.target { + builder = builder.target(target.inner); + } + + Arc::new(builder.engine()) } else { return return_with_error("Wasmer has not been compiled with the `object-file` feature."); } diff --git a/lib/c-api/src/wasm_c_api/externals/function.rs b/lib/c-api/src/wasm_c_api/externals/function.rs index d8fff4f2a2d..e6020e35d42 100644 --- a/lib/c-api/src/wasm_c_api/externals/function.rs +++ b/lib/c-api/src/wasm_c_api/externals/function.rs @@ -106,7 +106,7 @@ pub unsafe extern "C" fn wasm_func_new_with_env( struct WrapperEnv { env: *mut c_void, finalizer: Arc>, - }; + } // Only relevant when using multiple threads in the C API; // Synchronization will be done via the C API / on the C side. diff --git a/lib/c-api/src/wasm_c_api/macros.rs b/lib/c-api/src/wasm_c_api/macros.rs index c1e8955e036..dc85f6a9662 100644 --- a/lib/c-api/src/wasm_c_api/macros.rs +++ b/lib/c-api/src/wasm_c_api/macros.rs @@ -467,16 +467,19 @@ macro_rules! wasm_declare_own { #[macro_export] macro_rules! c_try { - ($expr:expr) => {{ + ($expr:expr; otherwise $return:expr) => {{ let res: Result<_, _> = $expr; match res { Ok(val) => val, Err(err) => { crate::error::update_last_error(err); - return None; + return $return; } } }}; + ($expr:expr) => {{ + c_try!($expr; otherwise None) + }}; ($expr:expr, $e:expr) => {{ let opt: Option<_> = $expr; c_try!(opt.ok_or_else(|| $e)) diff --git a/lib/c-api/src/wasm_c_api/unstable/engine.rs b/lib/c-api/src/wasm_c_api/unstable/engine.rs new file mode 100644 index 00000000000..92264a9a6e8 --- /dev/null +++ b/lib/c-api/src/wasm_c_api/unstable/engine.rs @@ -0,0 +1,49 @@ +//! Unstable non-standard Wasmer-specific types for the +//! `wasm_engine_t` and siblings. + +use super::super::engine::wasm_config_t; +use super::target_lexicon::wasm_target_t; + +/// Unstable non-standard Wasmer-specific API to update the +/// configuration to specify a particular target for the engine. +/// +/// # Example +/// +/// ```rust +/// # use inline_c::assert_c; +/// # fn main() { +/// # (assert_c! { +/// # #include "tests/wasmer_wasm.h" +/// # +/// int main() { +/// // Create the configuration. +/// wasm_config_t* config = wasm_config_new(); +/// +/// // Set the target. +/// { +/// wasm_triple_t* triple = wasm_triple_new_from_host(); +/// wasm_cpu_features_t* cpu_features = wasm_cpu_features_new(); +/// wasm_target_t* target = wasm_target_new(triple, cpu_features); +/// +/// wasm_config_set_target(config, target); +/// } +/// +/// // Create the engine. +/// wasm_engine_t* engine = wasm_engine_new_with_config(config); +/// +/// // Check we have an engine! +/// assert(engine); +/// +/// // Free everything. +/// wasm_engine_delete(engine); +/// +/// return 0; +/// } +/// # }) +/// # .success(); +/// # } +/// ``` +#[no_mangle] +pub extern "C" fn wasm_config_set_target(config: &mut wasm_config_t, target: Box) { + config.target = Some(target); +} diff --git a/lib/c-api/src/wasm_c_api/unstable/mod.rs b/lib/c-api/src/wasm_c_api/unstable/mod.rs index cc57a848e88..5b673aec6ec 100644 --- a/lib/c-api/src/wasm_c_api/unstable/mod.rs +++ b/lib/c-api/src/wasm_c_api/unstable/mod.rs @@ -1 +1,3 @@ +pub mod engine; pub mod module; +pub mod target_lexicon; diff --git a/lib/c-api/src/wasm_c_api/unstable/target_lexicon.rs b/lib/c-api/src/wasm_c_api/unstable/target_lexicon.rs new file mode 100644 index 00000000000..ccba407834c --- /dev/null +++ b/lib/c-api/src/wasm_c_api/unstable/target_lexicon.rs @@ -0,0 +1,314 @@ +//! Unstable non-standard Wasmer-specific API that contains everything +//! to create a target with a triple and CPU features. +//! +//! This is useful for cross-compilation. +//! +//! # Example +//! +//! ```rust +//! # use inline_c::assert_c; +//! # fn main() { +//! # (assert_c! { +//! # #include "tests/wasmer_wasm.h" +//! # +//! int main() { +//! // Declare the target triple. +//! wasm_triple_t* triple; +//! +//! { +//! wasm_name_t triple_name; +//! wasm_name_new_from_string(&triple_name, "x86_64-apple-darwin"); +//! +//! triple = wasm_triple_new(&triple_name); +//! +//! wasm_name_delete(&triple_name); +//! } +//! +//! assert(triple); +//! +//! // Declare the target CPU features. +//! wasm_cpu_features_t* cpu_features = wasm_cpu_features_new(); +//! +//! { +//! wasm_name_t cpu_feature_name; +//! wasm_name_new_from_string(&cpu_feature_name, "sse2"); +//! +//! wasm_cpu_features_add(cpu_features, &cpu_feature_name); +//! +//! wasm_name_delete(&cpu_feature_name); +//! } +//! +//! assert(cpu_features); +//! +//! // Create the target! +//! wasm_target_t* target = wasm_target_new(triple, cpu_features); +//! assert(target); +//! +//! wasm_target_delete(target); +//! +//! return 0; +//! } +//! # }) +//! # .success(); +//! # } +//! ``` + +use super::super::types::wasm_name_t; +use crate::error::CApiError; +use enumset::EnumSet; +use std::slice; +use std::str::{self, FromStr}; +use wasmer_compiler::{CpuFeature, Target, Triple}; + +/// Unstable non-standard Wasmer-specific API to represent a triple + +/// CPU features pair. +/// +/// # Example +/// +/// See the module's documentation. +#[derive(Debug)] +#[allow(non_camel_case_types)] +pub struct wasm_target_t { + pub(crate) inner: Target, +} + +/// Creates a new [`wasm_target_t`]. +/// +/// It takes ownership of `triple` and `cpu_features`. +/// +/// # Example +/// +/// See the module's documentation. +#[no_mangle] +pub extern "C" fn wasm_target_new( + triple: Option>, + cpu_features: Option>, +) -> Option> { + let triple = triple?; + let cpu_features = cpu_features?; + + Some(Box::new(wasm_target_t { + inner: Target::new(triple.inner.clone(), cpu_features.inner.clone()), + })) +} + +/// Delete a [`wasm_target_t`]. +/// +/// # Example +/// +/// See the module's documentation. +#[no_mangle] +pub extern "C" fn wasm_target_delete(_target: Option>) {} + +/// Unstable non-standard Wasmer-specific API to represent a target +/// “triple”. +/// +/// Historically such things had three fields, though they have added +/// additional fields over time. +/// +/// # Example +/// +/// ```rust +/// # use inline_c::assert_c; +/// # fn main() { +/// # (assert_c! { +/// # #include "tests/wasmer_wasm.h" +/// # +/// int main() { +/// wasm_name_t triple_name; +/// wasm_name_new_from_string(&triple_name, "x86_64-apple-darwin"); +/// +/// wasm_triple_t* triple = wasm_triple_new(&triple_name); +/// assert(triple); +/// +/// wasm_triple_delete(triple); +/// wasm_name_delete(&triple_name); +/// +/// return 0; +/// } +/// # }) +/// # .success(); +/// # } +/// ``` +/// +/// See also [`wasm_triple_new_from_host`]. +#[allow(non_camel_case_types)] +pub struct wasm_triple_t { + inner: Triple, +} + +/// Create a new [`wasm_triple_t`] based on a triple string. +/// +/// # Example +/// +/// See [`wasm_triple_t`] or [`wasm_triple_new_from_host`]. +#[no_mangle] +pub unsafe extern "C" fn wasm_triple_new( + triple: Option<&wasm_name_t>, +) -> Option> { + let triple = triple?; + let triple = c_try!(str::from_utf8(slice::from_raw_parts( + triple.data, + triple.size + ))); + + Some(Box::new(wasm_triple_t { + inner: c_try!(Triple::from_str(triple).map_err(|e| CApiError { msg: e.to_string() })), + })) +} + +/// Create the [`wasm_triple_t`] for the current host. +/// +/// # Example +/// +/// ```rust +/// # use inline_c::assert_c; +/// # fn main() { +/// # (assert_c! { +/// # #include "tests/wasmer_wasm.h" +/// # +/// int main() { +/// wasm_triple_t* triple = wasm_triple_new_from_host(); +/// assert(triple); +/// +/// wasm_triple_delete(triple); +/// +/// return 0; +/// } +/// # }) +/// # .success(); +/// # } +/// ``` +/// +/// See also [`wasm_triple_new`]. +#[no_mangle] +pub extern "C" fn wasm_triple_new_from_host() -> Box { + Box::new(wasm_triple_t { + inner: Triple::host(), + }) +} + +/// Delete a [`wasm_triple_t`]. +/// +/// # Example +/// +/// See [`wasm_triple_t`]. +#[no_mangle] +pub extern "C" fn wasm_triple_delete(_triple: Option>) {} + +/// Unstable non-standard Wasmer-specific API to represent a set of +/// CPU features. +/// +/// CPU features are identified by their stringified names. The +/// reference is the GCC options: +/// +/// * , +/// * , +/// * . +/// +/// At the time of writing this documentation (it might be outdated in +/// the future), the supported features are the following: +/// +/// * `sse2`, +/// * `sse3`, +/// * `ssse3`, +/// * `sse4.1`, +/// * `sse4.2`, +/// * `popcnt`, +/// * `avx`, +/// * `bmi`, +/// * `bmi2`, +/// * `avx2`, +/// * `avx512dq`, +/// * `avx512vl`, +/// * `lzcnt`. +/// +/// # Example +/// +/// ```rust +/// # use inline_c::assert_c; +/// # fn main() { +/// # (assert_c! { +/// # #include "tests/wasmer_wasm.h" +/// # +/// int main() { +/// // Create a new CPU feature set. +/// wasm_cpu_features_t* cpu_features = wasm_cpu_features_new(); +/// +/// // Create a new feature name, here `sse2`, and add it to the set. +/// { +/// wasm_name_t cpu_feature_name; +/// wasm_name_new_from_string(&cpu_feature_name, "sse2"); +/// +/// wasm_cpu_features_add(cpu_features, &cpu_feature_name); +/// +/// wasm_name_delete(&cpu_feature_name); +/// } +/// +/// wasm_cpu_features_delete(cpu_features); +/// +/// return 0; +/// } +/// # }) +/// # .success(); +/// # } +/// ``` +#[allow(non_camel_case_types)] +pub struct wasm_cpu_features_t { + inner: EnumSet, +} + +/// Create a new [`wasm_cpu_features_t`]. +/// +/// # Example +/// +/// See [`wasm_cpu_features_t`]. +#[no_mangle] +pub extern "C" fn wasm_cpu_features_new() -> Box { + Box::new(wasm_cpu_features_t { + inner: CpuFeature::set(), + }) +} + +/// Delete a [`wasm_cpu_features_t`]. +/// +/// # Example +/// +/// See [`wasm_cpu_features_t`]. +#[no_mangle] +pub extern "C" fn wasm_cpu_features_delete(_cpu_features: Option>) {} + +/// Add a new CPU feature into the set represented by +/// [`wasm_cpu_features_t`]. +/// +/// # Example +/// +/// See [`wasm_cpu_features_t`]. +#[no_mangle] +pub unsafe extern "C" fn wasm_cpu_features_add( + cpu_features: Option<&mut wasm_cpu_features_t>, + feature: Option<&wasm_name_t>, +) -> bool { + let cpu_features = match cpu_features { + Some(cpu_features) => cpu_features, + _ => return false, + }; + let feature = match feature { + Some(feature) => feature, + _ => return false, + }; + let feature = c_try!( + str::from_utf8(slice::from_raw_parts( + feature.data, + feature.size + 1, + )); + otherwise false + ); + + cpu_features.inner.insert(c_try!( + CpuFeature::from_str(feature); + otherwise false + )); + + true +} diff --git a/lib/c-api/wasmer_wasm.h b/lib/c-api/wasmer_wasm.h index 7c6d1b8baf1..5dd1584a642 100644 --- a/lib/c-api/wasmer_wasm.h +++ b/lib/c-api/wasmer_wasm.h @@ -134,6 +134,12 @@ typedef struct wasi_config_t wasi_config_t; typedef struct wasi_env_t wasi_env_t; #endif +typedef struct wasm_cpu_features_t wasm_cpu_features_t; + +typedef struct wasm_target_t wasm_target_t; + +typedef struct wasm_triple_t wasm_triple_t; + #ifdef __cplusplus extern "C" { #endif // __cplusplus @@ -227,10 +233,28 @@ void wasm_config_set_compiler(wasm_config_t *config, wasmer_compiler_t compiler) void wasm_config_set_engine(wasm_config_t *config, wasmer_engine_t engine); +void wasm_config_set_target(wasm_config_t *config, wasm_target_t *target); + +bool wasm_cpu_features_add(wasm_cpu_features_t *cpu_features, const wasm_name_t *feature); + +void wasm_cpu_features_delete(wasm_cpu_features_t *_cpu_features); + +wasm_cpu_features_t *wasm_cpu_features_new(void); + void wasm_module_name(const wasm_module_t *module, wasm_name_t *out); bool wasm_module_set_name(wasm_module_t *module, const wasm_name_t *name); +void wasm_target_delete(wasm_target_t *_target); + +wasm_target_t *wasm_target_new(wasm_triple_t *triple, wasm_cpu_features_t *cpu_features); + +void wasm_triple_delete(wasm_triple_t *_triple); + +wasm_triple_t *wasm_triple_new(const wasm_name_t *triple); + +wasm_triple_t *wasm_triple_new_from_host(void); + int wasmer_last_error_length(void); int wasmer_last_error_message(char *buffer, int length);