Add option to enable pulley support for pre-compilation and runtime of modules and components#358
Add option to enable pulley support for pre-compilation and runtime of modules and components#358dblnz wants to merge 2 commits intohyperlight-dev:mainfrom
Conversation
There was a problem hiding this comment.
Pull request overview
This PR adds support for the Pulley interpreter as an alternative to Cranelift for WebAssembly compilation and execution in Hyperlight. Pulley is an interpreted execution mode for WebAssembly that can be enabled at both AOT compilation time and runtime.
Changes:
- Added
--pulleyflag to the AOT compilation tool to target the pulley64 architecture - Added
pulleyfeature flag to enable pulley runtime support in the wasm_runtime and hyperlight_wasm crates - Added Justfile recipes to build and test modules/components with pulley support
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| src/wasm_runtime/src/module.rs | Configures wasmtime engine to use pulley64 target when pulley feature is enabled |
| src/wasm_runtime/src/component.rs | Configures wasmtime engine to use pulley64 target when pulley feature is enabled for component model |
| src/wasm_runtime/build.rs | Adds cfg alias for pulley feature flag |
| src/wasm_runtime/Cargo.toml | Adds pulley feature that enables wasmtime's pulley support |
| src/hyperlight_wasm_aot/src/main.rs | Adds --pulley command-line flag and logic to compile for pulley64 target |
| src/hyperlight_wasm_aot/Cargo.toml | Unconditionally enables pulley feature in wasmtime since AOT tool needs both targets |
| src/hyperlight_wasm/build.rs | Propagates pulley feature flag to wasm_runtime build |
| src/hyperlight_wasm/Cargo.toml | Adds pulley feature flag |
| Justfile | Adds recipes to build and test pulley-compiled modules and components |
Comments suppressed due to low confidence (1)
src/hyperlight_wasm_aot/src/main.rs:194
- When compiling for the pulley target with debug enabled, the code still calls
config.cranelift_opt_level(OptLevel::None). Since pulley is an interpreter that doesn't use Cranelift, this setting may be ignored or could potentially cause issues. Consider wrapping Cranelift-specific configuration in a conditional check to only apply when not using pulley (e.g.,if !pulley { config.cranelift_opt_level(OptLevel::None); }).
if debug {
config.debug_info(true);
config.cranelift_opt_level(OptLevel::None);
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Signed-off-by: Doru Blânzeanu <dblnz@pm.me>
Signed-off-by: Doru Blânzeanu <dblnz@pm.me>
simongdavies
left a comment
There was a problem hiding this comment.
one small comment about version checking
src/hyperlight_wasm_aot/src/main.rs
Outdated
| // load the file into wasmtime, check that it is aot compiled and extract the version of wasmtime used to compile it from its metadata | ||
| let bytes = std::fs::read(&file).unwrap(); | ||
| let config = get_config(debug, false); | ||
| let config = get_config(debug, false, false); |
There was a problem hiding this comment.
I think this means that the version number check does not work with pulley compiled files, not sure if this is what we want? Wonder if we can detect the type by parsing the file?
There was a problem hiding this comment.
I added a --pulley option for check-wasmtime-version command.
There isn't an intended way to use the Engine to verify whether the file is targeted for Pulley or not.
We could parse the files, but it doesn't look good 😄
If you really think that is something we should do, I can open up an issue and take that on at a later time
There was a problem hiding this comment.
I think it would be nice to have but since its not immediately available that's probably find. I would assume wasmtime checks this somewhere when running so at the very least the user would get an error when trying to run the wrongly compiled version?
There was a problem hiding this comment.
We could parse the files, but it doesn't look good 😄
It looks like it is very simple to check, as it's just the elf header: e_machine == ET_RISCV and e_flags contains wasmtime_environ::obj::EF_WASMTIME_PULLEY64. Apparently other crates like wasmtime-objdump also check this, so I imagine it's safe to use.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 9 out of 9 changed files in this pull request and generated 3 comments.
Comments suppressed due to low confidence (1)
src/hyperlight_wasm_aot/src/main.rs:202
- When compiling for pulley64 target with debug enabled, the code sets
cranelift_opt_level(OptLevel::None)which is specific to the Cranelift compiler. This may cause unexpected behavior or errors when using the pulley interpreter. Consider conditionally applying this setting only when not using pulley, or verify that this setting is safely ignored by pulley.
if debug {
config.debug_info(true);
config.cranelift_opt_level(OptLevel::None);
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| .map_err(|_| { | ||
| HyperlightGuestError::new( | ||
| ErrorCode::GuestError, | ||
| "Failed to set wasmtime target: pulley64".to_string(), | ||
| ) | ||
| }) | ||
| .unwrap(); |
There was a problem hiding this comment.
The map_err followed by .unwrap() is redundant. Since this function doesn't return a Result, the custom error created by map_err will never be used. Consider either removing the map_err and keeping just .unwrap() to match the pattern on line 124, or provide a more descriptive panic message using .expect("Failed to set wasmtime target: pulley64").
| .map_err(|_| { | |
| HyperlightGuestError::new( | |
| ErrorCode::GuestError, | |
| "Failed to set wasmtime target: pulley64".to_string(), | |
| ) | |
| }) | |
| .unwrap(); | |
| .expect("Failed to set wasmtime target: pulley64"); |
| build-pulley-rust-wasm-examples target=default-target features="": (mkdir-redist target) | ||
| rustup target add wasm32-unknown-unknown | ||
| cd ./src/rust_wasm_samples && cargo build --target wasm32-unknown-unknown --profile={{ if target == "debug" {"dev"} else { target } }} | ||
| cargo run {{ if features =="" {''} else if features=="no-default-features" {"--no-default-features" } else {"--features " + features } }} -p hyperlight-wasm-aot compile --pulley {{ if features =~ "gdb" {"--debug"} else {""} }} ./src/rust_wasm_samples/target/wasm32-unknown-unknown/{{ target }}/rust_wasm_samples.wasm ./x64/{{ target }}/rust_wasm_samples.aot |
There was a problem hiding this comment.
The pulley-compiled binaries write to the same output path as the regular cranelift-compiled binaries (e.g., ./x64/{{ target }}/rust_wasm_samples.aot). This means running build-pulley-rust-wasm-examples will overwrite the output from build-rust-wasm-examples. Consider using distinct output paths (e.g., ./x64/{{ target }}/pulley/rust_wasm_samples.aot) to avoid accidental overwrites and allow both versions to coexist.
| # we also explicitly target wasm32-unknown-unknown since cargo component might try to pull in wasi imports https://github.com/bytecodealliance/cargo-component/issues/290 | ||
| rustup target add wasm32-unknown-unknown | ||
| cd ./src/component_sample && cargo component build --target wasm32-unknown-unknown --profile={{ if target == "debug" {"dev"} else { target } }} | ||
| cargo run {{ if features =="" {''} else if features=="no-default-features" {"--no-default-features" } else {"--features " + features } }} -p hyperlight-wasm-aot compile --pulley {{ if features =~ "gdb" {"--debug"} else {""} }} --component ./src/component_sample/target/wasm32-unknown-unknown/{{ target }}/component_sample.wasm ./x64/{{ target }}/component_sample.aot |
There was a problem hiding this comment.
The pulley-compiled component writes to the same output path as the regular cranelift-compiled component (./x64/{{ target }}/component_sample.aot). This means running build-pulley-rust-component-examples will overwrite the output from build-rust-component-examples. Consider using distinct output paths (e.g., ./x64/{{ target }}/pulley/component_sample.aot) to avoid accidental overwrites and allow both versions to coexist.
| cargo run {{ if features =="" {''} else if features=="no-default-features" {"--no-default-features" } else {"--features " + features } }} -p hyperlight-wasm-aot compile --pulley {{ if features =~ "gdb" {"--debug"} else {""} }} --component ./src/component_sample/target/wasm32-unknown-unknown/{{ target }}/component_sample.wasm ./x64/{{ target }}/component_sample.aot | |
| mkdir {{ mkdir-arg }} x64/{{ target }}/pulley | |
| cargo run {{ if features =="" {''} else if features=="no-default-features" {"--no-default-features" } else {"--features " + features } }} -p hyperlight-wasm-aot compile --pulley {{ if features =~ "gdb" {"--debug"} else {""} }} --component ./src/component_sample/target/wasm32-unknown-unknown/{{ target }}/component_sample.wasm ./x64/{{ target }}/pulley/component_sample.aot |
This PR adds an option to enable
pulleyinterpreter instead of the defaultcraneliftfor wasmtime.To use
pulleyone needs to:pulley64target. It can be done usinghyperlight-wasm-aot compile --pulleycommandpulleyfeature forhyperlight-wasmto build thewasm_runtimewithpulleyfeature that correctly sets theconfig.target("pulley64")for theConfigpassed to the wasmtimeEngine.