From b666a54602717f87933580d3ffc54ad046e3977e Mon Sep 17 00:00:00 2001 From: James Sturtevant Date: Thu, 21 Aug 2025 19:26:25 +0000 Subject: [PATCH 1/2] Fixes FP rounding issue and adds tests The issue was caused becuase cranelift compilation assume SSE2 even for the where as rust compiler doesn't include these instructions by default. This means when transitioning through wasmtime libcalls the parameters are lost since wasm is using SSE2 instructions and wasmtime isn't. The more advance SSE intructions require a seperate pr in HL core that is needed to enable them. Signed-off-by: James Sturtevant --- Justfile | 3 +- .../examples/c-component/main.rs | 68 +++++++++++++++++++ .../examples/helloworld/main.rs | 32 +++++++++ src/wasm_runtime/.cargo/config.toml | 2 + src/wasmsamples/RunWasm.c | 10 +++ src/wasmsamples/components/runcomponent.c | 8 +++ src/wasmsamples/components/runcomponent.wit | 1 + 7 files changed, 123 insertions(+), 1 deletion(-) create mode 100644 src/hyperlight_wasm/examples/c-component/main.rs diff --git a/Justfile b/Justfile index a844c8e6..e51be757 100644 --- a/Justfile +++ b/Justfile @@ -99,7 +99,8 @@ examples-ci target=default-target features="": (build-rust-wasm-examples target) cargo run {{ if features =="" {"--no-default-features --features kvm,mshv3"} else {"--no-default-features -F function_call_metrics," + features } }} --profile={{ if target == "debug" {"dev"} else { target } }} --example metrics examples-components target=default-target features="": (build-rust-component-examples target) - {{ wit-world }} cargo run {{ if features =="" {''} else {"--no-default-features -F " + features } }} --profile={{ if target == "debug" {"dev"} else { target } }} --example component_example + {{ wit-world }} cargo run {{ if features =="" {''} else {"--no-default-features -F kvm -F " + features } }} --profile={{ if target == "debug" {"dev"} else { target } }} --example component_example + {{ wit-world-c }} cargo run {{ if features =="" {''} else {"--no-default-features -F kvm -F " + features } }} --profile={{ if target == "debug" {"dev"} else { target } }} --example c-component # warning, compares to and then OVERWRITES the given baseline bench-ci baseline target="release" features="": diff --git a/src/hyperlight_wasm/examples/c-component/main.rs b/src/hyperlight_wasm/examples/c-component/main.rs new file mode 100644 index 00000000..1a6de5fc --- /dev/null +++ b/src/hyperlight_wasm/examples/c-component/main.rs @@ -0,0 +1,68 @@ +use examples_common::get_wasm_module_path; +use hyperlight_wasm::SandboxBuilder; + +use crate::bindings::example::runcomponent::Guest; + +extern crate alloc; +mod bindings { + hyperlight_component_macro::host_bindgen!( + "../../src/wasmsamples/components/runcomponent-world.wasm" + ); +} + +pub struct State {} +impl State { + pub fn new() -> Self { + State {} + } +} + +impl Default for State { + fn default() -> Self { + Self::new() + } +} + +impl bindings::example::runcomponent::Host for State { + fn r#get_time_since_boot_microsecond(&mut self) -> i64 { + let res = std::time::SystemTime::now() + .duration_since(std::time::SystemTime::UNIX_EPOCH) + .unwrap() + .as_micros(); + i64::try_from(res).unwrap() + } +} + +impl bindings::example::runcomponent::RuncomponentImports for State { + type Host = State; + + fn r#host(&mut self) -> impl ::core::borrow::BorrowMut { + self + } +} + +fn main() { + let state = State::new(); + let mut sandbox = SandboxBuilder::new() + .with_guest_input_buffer_size(70000000) + .with_guest_heap_size(200000000) + .with_guest_stack_size(100000000) + //.with_debugging_enabled(8080) + .build() + .unwrap(); + let rt = bindings::register_host_functions(&mut sandbox, state); + + let sb = sandbox.load_runtime().unwrap(); + + let mod_path = get_wasm_module_path("runcomponent.aot").unwrap(); + let sb = sb.load_module(mod_path).unwrap(); + + let mut wrapped = bindings::RuncomponentSandbox { sb, rt }; + let instance = bindings::example::runcomponent::RuncomponentExports::guest(&mut wrapped); + let echo = instance.echo("Hello World!".to_string()); + println!("{}", echo); + + let result = instance.round_to_nearest_int(1.331, 24.0); + println!("rounded result {}", result); + assert_eq!(result, 32); +} diff --git a/src/hyperlight_wasm/examples/helloworld/main.rs b/src/hyperlight_wasm/examples/helloworld/main.rs index fe21a02e..730844d3 100644 --- a/src/hyperlight_wasm/examples/helloworld/main.rs +++ b/src/hyperlight_wasm/examples/helloworld/main.rs @@ -83,5 +83,37 @@ fn main() -> Result<()> { ); } } + + let tests = [ + (1.331, 24.0, 32), + (std::f32::consts::PI, std::f32::consts::E, 9), + (-5.7, 10.3, -59), + (0.0, 0.0, 0), + (99.999, 0.001, 0), + (-std::f32::consts::PI, -2.86, 9), + (1.5, 1.5, 2), + ]; + let mut sandbox = SandboxBuilder::new().build()?; + sandbox + .register( + "GetTimeSinceBootMicrosecond", + get_time_since_boot_microsecond, + ) + .unwrap(); + let wasm_sandbox = sandbox.load_runtime()?; + let mod_path = get_wasm_module_path("RunWasm.aot")?; + let mut loaded_wasm_sandbox = wasm_sandbox.load_module(mod_path)?; + let snapshot = loaded_wasm_sandbox.snapshot()?; + + for (idx, case) in tests.iter().enumerate() { + let (a, b, expected_result): (f32, f32, i32) = *case; + let result: i32 = loaded_wasm_sandbox.call_guest_function("RoundToNearestInt", (a, b))?; + assert_eq!( + result, expected_result, + "RoundToInt test case {idx} failed: got {}, expected {}", + result, expected_result + ); + loaded_wasm_sandbox.restore(&snapshot)? + } Ok(()) } diff --git a/src/wasm_runtime/.cargo/config.toml b/src/wasm_runtime/.cargo/config.toml index a8e6c8ec..fc984db5 100644 --- a/src/wasm_runtime/.cargo/config.toml +++ b/src/wasm_runtime/.cargo/config.toml @@ -7,6 +7,8 @@ rustflags = [ "code-model=small", "-C", "link-args=-e entrypoint", + "-C", + "target-feature=-soft-float,+sse,+sse2" ] linker = "rust-lld" diff --git a/src/wasmsamples/RunWasm.c b/src/wasmsamples/RunWasm.c index 3d40682d..6736e5c6 100644 --- a/src/wasmsamples/RunWasm.c +++ b/src/wasmsamples/RunWasm.c @@ -20,6 +20,7 @@ limitations under the License. #include #include #include +#include int HostPrint(char* msg); // Implementation of this will be available in the native host @@ -129,3 +130,12 @@ int KeepCPUBusy(int ms) printf("Kept CPU busy for %d ms using %d iterations of fib(10) %d|toreach max = %d|", ms, iter, INT_MAX, INT_MAX-iter); return ms; } + +__attribute__((export_name("RoundToNearestInt"))) +int RoundToNearestInt(float a, float b) +{ + float c = a*b; + float r = lrintf(c); + printf("rounded answer: %f\n", r); + return r; +} \ No newline at end of file diff --git a/src/wasmsamples/components/runcomponent.c b/src/wasmsamples/components/runcomponent.c index 0d3d43e7..c958a4dd 100644 --- a/src/wasmsamples/components/runcomponent.c +++ b/src/wasmsamples/components/runcomponent.c @@ -1,6 +1,7 @@ #include "bindings/runcomponent.h" #include #include +#include void exports_example_runcomponent_guest_echo(runcomponent_string_t *msg, runcomponent_string_t *ret) { @@ -8,4 +9,11 @@ void exports_example_runcomponent_guest_echo(runcomponent_string_t *msg, runcomp ret->ptr = (uint8_t *) malloc(ret->len); memcpy(ret->ptr, msg->ptr, ret->len); runcomponent_string_free(msg); +} + +int32_t exports_example_runcomponent_guest_round_to_nearest_int(float a, float b) +{ + float c = a*b; + float r = lrintf(c); + return r; } \ No newline at end of file diff --git a/src/wasmsamples/components/runcomponent.wit b/src/wasmsamples/components/runcomponent.wit index 941fcbfe..876efdb5 100644 --- a/src/wasmsamples/components/runcomponent.wit +++ b/src/wasmsamples/components/runcomponent.wit @@ -2,6 +2,7 @@ package example:runcomponent; interface guest { echo: func(msg: string) -> string; + round-to-nearest-int: func(a: f32, b: f32) -> s32; } interface host { From 4f345d9c71394bde5ba3a0bdc17fba0e648ba19f Mon Sep 17 00:00:00 2001 From: James Sturtevant Date: Thu, 4 Sep 2025 21:55:19 +0000 Subject: [PATCH 2/2] Updates for cargo plugin Signed-off-by: James Sturtevant --- .github/workflows/Benchmarks.yml | 4 +++- .github/workflows/dep_rust.yml | 5 +++-- Justfile | 13 ++++++++----- src/hyperlight_wasm/build.rs | 23 +++++------------------ src/wasm_runtime/.cargo/config.toml | 23 ----------------------- src/wasm_runtime/build.rs | 12 +----------- 6 files changed, 20 insertions(+), 60 deletions(-) delete mode 100644 src/wasm_runtime/.cargo/config.toml diff --git a/.github/workflows/Benchmarks.yml b/.github/workflows/Benchmarks.yml index 8152bf77..57760cc2 100644 --- a/.github/workflows/Benchmarks.yml +++ b/.github/workflows/Benchmarks.yml @@ -40,7 +40,9 @@ jobs: - name: Build Wasm Runtime Binary working-directory: ./src/hyperlight_wasm - run: just build-wasm-runtime ${{ matrix.config }} + run: | + just ensure-tools + just build-wasm-runtime ${{ matrix.config }} - uses: dtolnay/rust-toolchain@1.89 with: diff --git a/.github/workflows/dep_rust.yml b/.github/workflows/dep_rust.yml index 9415c080..07d798eb 100644 --- a/.github/workflows/dep_rust.yml +++ b/.github/workflows/dep_rust.yml @@ -69,7 +69,9 @@ jobs: - name: Build Wasm Runtime Binary working-directory: ./src/hyperlight_wasm - run: just build-wasm-runtime ${{ matrix.config }} + run: | + just ensure-tools + just build-wasm-runtime ${{ matrix.config }} - name: Download Wasm Modules uses: actions/download-artifact@v5 @@ -81,7 +83,6 @@ jobs: run: | # this must be build before the formatting and other jobs run # because the component model example depends on the wasm component built here - just ensure-tools just compile-wit just build-rust-component-examples ${{ matrix.config }} diff --git a/Justfile b/Justfile index e51be757..e4d03f94 100644 --- a/Justfile +++ b/Justfile @@ -18,8 +18,9 @@ ensure-tools: cargo install wasm-tools --locked --version 1.235.0 cargo install cargo-component --locked --version 0.21.1 cargo install wit-bindgen-cli --locked --version 0.43.0 + cargo install --git https://github.com/hyperlight-dev/cargo-hyperlight cargo-hyperlight -build-all target=default-target features="": (build target features) (build-wasm-examples target features) (build-rust-wasm-examples target features) (build-rust-component-examples target) (build-wasm-runtime target features) +build-all target=default-target features="": (build target features) (build-examples target features) (build-wasm-runtime target features) build target=default-target features="": (fmt-check) cargo build {{ if features =="" {''} else if features=="no-default-features" {"--no-default-features" } else {"--no-default-features -F " + features } }} --verbose --profile={{ if target == "debug" {"dev"} else { target } }} @@ -33,9 +34,11 @@ compile-wit: wasm-tools component wit ./src/component_sample/wit/example.wit -w -o ./src/component_sample/wit/component-world.wasm build-wasm-runtime target=default-target features="": - cd ./src/wasm_runtime && cargo build --verbose {{ if features =="" {''} else if features=="no-default-features" {"--no-default-features" } else {"--features " + features } }} --profile={{ if target == "debug" {"dev"} else { target } }} && rm -R target + cd ./src/wasm_runtime && cargo hyperlight build --verbose {{ if features =="" {''} else if features=="no-default-features" {"--no-default-features" } else {"--features " + features } }} --profile={{ if target == "debug" {"dev"} else { target } }} && rm -R target -build-wasm-examples target=default-target features="": (compile-wit) +build-examples target=default-target features="": (build-wasm-examples target features) (build-rust-wasm-examples target features) (build-rust-component-examples target features) + +build-wasm-examples target=default-target features="": (compile-wit) {{ build-wasm-examples-command }} {{target}} {{features}} build-rust-wasm-examples target=default-target features="": (mkdir-redist target) @@ -54,7 +57,7 @@ check target=default-target: cargo check --profile={{ if target == "debug" {"dev"} else { target } }} cd src/rust_wasm_samples && cargo check --profile={{ if target == "debug" {"dev"} else { target } }} cd src/component_sample && cargo check --profile={{ if target == "debug" {"dev"} else { target } }} - cd src/wasm_runtime && cargo check --profile={{ if target == "debug" {"dev"} else { target } }} + cd src/wasm_runtime && cargo hyperlight check --profile={{ if target == "debug" {"dev"} else { target } }} cd src/hyperlight_wasm_macro && cargo check --profile={{ if target == "debug" {"dev"} else { target } }} fmt-check: @@ -79,7 +82,7 @@ clippy target=default-target: (check target) cargo clippy --profile={{ if target == "debug" {"dev"} else { target } }} --all-targets --all-features -- -D warnings cd src/rust_wasm_samples && cargo clippy --profile={{ if target == "debug" {"dev"} else { target } }} --all-targets --all-features -- -D warnings cd src/component_sample && cargo clippy --profile={{ if target == "debug" {"dev"} else { target } }} --all-targets --all-features -- -D warnings - cd src/wasm_runtime && cargo clippy --profile={{ if target == "debug" {"dev"} else { target } }} --all-targets --all-features -- -D warnings + cd src/wasm_runtime && cargo hyperlight clippy --profile={{ if target == "debug" {"dev"} else { target } }} --all-targets --all-features -- -D warnings cd src/hyperlight_wasm_macro && cargo clippy --profile={{ if target == "debug" {"dev"} else { target } }} --all-targets --all-features -- -D warnings # TESTING diff --git a/src/hyperlight_wasm/build.rs b/src/hyperlight_wasm/build.rs index 535925c3..0096c333 100644 --- a/src/hyperlight_wasm/build.rs +++ b/src/hyperlight_wasm/build.rs @@ -22,24 +22,14 @@ limitations under the License. // this file is included in lib.rs. // The wasm_runtime binary is expected to be in the x64/{config} directory. -use std::ffi::OsString; use std::fs::OpenOptions; use std::io::Write; -use std::iter::once; use std::path::{Path, PathBuf}; use std::{env, fs}; use anyhow::Result; use built::write_built_file; -fn path_with(path: impl Into) -> OsString { - let path = path.into(); - let paths = env::var_os("PATH").unwrap_or_default(); - let paths = env::split_paths(&paths); - let paths = once(path).chain(paths); - env::join_paths(paths).unwrap() -} - fn get_wasm_runtime_path() -> PathBuf { let manifest_dir = env::var_os("CARGO_MANIFEST_DIR").unwrap(); let manifest_dir = PathBuf::from(manifest_dir); @@ -105,7 +95,6 @@ fn build_wasm_runtime() -> PathBuf { let out_dir = env::var_os("OUT_DIR").unwrap(); let target_dir = Path::new("").join(&out_dir).join("target"); - let toolchain_dir = Path::new("").join(&out_dir).join("toolchain"); let in_repo_dir = get_wasm_runtime_path(); @@ -126,19 +115,17 @@ fn build_wasm_runtime() -> PathBuf { let mut cargo_cmd = std::process::Command::new(&cargo_bin); let mut cmd = cargo_cmd + .arg("hyperlight") .arg("build") + .arg("--target-dir") + .arg(&target_dir) .arg("--profile") .arg(cargo_profile) .arg("-v") - .arg("--target-dir") - .arg(&target_dir) .current_dir(&in_repo_dir) .env_clear() - // On windows when `gdb` features is enabled this is not set correctly - .env("CFLAGS_x86_64_unknown_none", "-fPIC") .envs(env_vars) - .env("PATH", path_with(&toolchain_dir)) - .env("HYPERLIGHT_GUEST_TOOLCHAIN_ROOT", &toolchain_dir); + .env("CFLAGS_x86_64_unknown_none", "-fPIC"); // Should this go on cargo hyperlight // Add --features gdb if the gdb feature is enabled for this build script if std::env::var("CARGO_FEATURE_GDB").is_ok() { @@ -156,7 +143,7 @@ fn build_wasm_runtime() -> PathBuf { panic!("could not compile wasm_runtime"); } let resource = target_dir - .join("x86_64-unknown-none") + .join("x86_64-hyperlight-none") .join(profile) .join("wasm_runtime"); diff --git a/src/wasm_runtime/.cargo/config.toml b/src/wasm_runtime/.cargo/config.toml deleted file mode 100644 index fc984db5..00000000 --- a/src/wasm_runtime/.cargo/config.toml +++ /dev/null @@ -1,23 +0,0 @@ -[build] -target = "x86_64-unknown-none" - -[target.x86_64-unknown-none] -rustflags = [ - "-C", - "code-model=small", - "-C", - "link-args=-e entrypoint", - "-C", - "target-feature=-soft-float,+sse,+sse2" -] -linker = "rust-lld" - -[env] -HYPERLIGHT_GUEST_TOOLCHAIN_ROOT = { value = "guest-toolchain", relative = true } - -[profile.release] -panic = "abort" - -[profile.dev] -opt-level = 0 -panic = "abort" diff --git a/src/wasm_runtime/build.rs b/src/wasm_runtime/build.rs index 8780d60b..2aa4b60c 100644 --- a/src/wasm_runtime/build.rs +++ b/src/wasm_runtime/build.rs @@ -14,21 +14,12 @@ See the License for the specific language governing permissions and limitations under the License. */ -use std::path::{Path, PathBuf}; +use std::path::Path; use std::{env, fs}; use cargo_metadata::{MetadataCommand, Package}; fn main() { - let toolchain_dir = env::var_os("HYPERLIGHT_GUEST_TOOLCHAIN_ROOT").unwrap(); - let toolchain_dir = PathBuf::from(toolchain_dir); - let clang_path = toolchain_dir.join("clang"); - - assert!( - clang_path.exists(), - "could not find clang at {clang_path:?}" - ); - println!("cargo:rerun-if-changed=."); let mut cfg = cc::Build::new(); @@ -63,7 +54,6 @@ fn main() { cfg.include("src/include"); cfg.file("src/platform.c"); - cfg.compiler(clang_path); if cfg!(windows) { env::set_var("AR_x86_64_unknown_none", "llvm-ar"); }