winch: Initial integration with wasmtime (#6119)
* Adding in trampoline compiling method for ISA * Adding support for indirect call to memory address * Refactoring frame to externalize defined locals, so it removes WASM depedencies in trampoline case * Adding initial version of trampoline for testing * Refactoring trampoline to be re-used by other architectures * Initial wiring for winch with wasmtime * Add a Wasmtime CLI option to select `winch` This is effectively an option to select the `Strategy` enumeration. * Implement `Compiler::compile_function` for Winch Hook this into the `TargetIsa::compile_function` hook as well. Currently this doesn't take into account `Tunables`, but that's left as a TODO for later. * Filling out Winch append_code method * Adding back in changes from previous branch Most of these are a WIP. It's missing trampolines for x64, but a basic one exists for aarch64. It's missing the handling of arguments that exist on the stack. It currently imports `cranelift_wasm::WasmFuncType` since it's what's passed to the `Compiler` trait. It's a bit awkward to use in the `winch_codegen` crate since it mostly operates on `wasmparser` types. I've had to hack in a conversion to get things working. Long term, I'm not sure it's wise to rely on this type but it seems like it's easier on the Cranelift side when creating the stub IR. * Small API changes to make integration easier * Adding in new FuncEnv, only a stub for now * Removing unneeded parts of the old PoC, and refactoring trampoline code * Moving FuncEnv into a separate file * More comments for trampolines * Adding in winch integration tests for first pass * Using new addressing method to fix stack pointer error * Adding test for stack arguments * Only run tests on x86 for now, it's more complete for winch * Add in missing documentation after rebase * Updating based on feedback in draft PR * Fixing formatting on doc comment for argv register * Running formatting * Lock updates, and turning on winch feature flags during tests * Updating configuration with comments to no longer gate Strategy enum * Using the winch-environ FuncEnv, but it required changing the sig * Proper comment formatting * Removing wasmtime-winch from dev-dependencies, adding the winch feature makes this not necessary * Update doc attr to include winch check * Adding winch feature to doc generation, which seems to fix the feature error in CI * Add the `component-model` feature to the cargo doc invocation in CI To match the metadata used by the docs.rs invocation when building docs. * Add a comment clarifying the usage of `component-model` for docs.rs * Correctly order wasmtime-winch and winch-environ in the publish script * Ensure x86 test dependencies are included in cfg(target_arch) * Further constrain Winch tests to x86_64 _and_ unix --------- Co-authored-by: Alex Crichton <alex@alexcrichton.com> Co-authored-by: Saúl Cabrera <saulecabrera@gmail.com>
This commit is contained in:
8
.github/workflows/main.yml
vendored
8
.github/workflows/main.yml
vendored
@@ -204,12 +204,16 @@ jobs:
|
||||
- run: (cd docs/rust_wasi_markdown_parser && cargo build)
|
||||
- run: (cd docs && mdbook test -L ../target/debug/deps,./rust_wasi_markdown_parser/target/debug/deps)
|
||||
|
||||
# Build Rust API documentation
|
||||
# Build Rust API documentation.
|
||||
# We pass in the `component-model` feature
|
||||
# to match the docs.rs metadata in
|
||||
# crates/wasmtime/Cargo.toml.
|
||||
- run: |
|
||||
cargo doc --no-deps --workspace \
|
||||
--exclude wasmtime-cli \
|
||||
--exclude test-programs \
|
||||
--exclude cranelift-codegen-meta
|
||||
--exclude cranelift-codegen-meta \
|
||||
--features component-model
|
||||
- run: cargo doc --package cranelift-codegen-meta --document-private-items
|
||||
|
||||
# Assemble the documentation, and always upload it as an artifact for
|
||||
|
||||
2
Cargo.lock
generated
2
Cargo.lock
generated
@@ -3423,6 +3423,7 @@ dependencies = [
|
||||
"wasmtime-jit",
|
||||
"wasmtime-runtime",
|
||||
"wasmtime-wasi",
|
||||
"wasmtime-winch",
|
||||
"wat",
|
||||
"windows-sys",
|
||||
]
|
||||
@@ -3872,6 +3873,7 @@ dependencies = [
|
||||
"wasmtime-cranelift-shared",
|
||||
"wasmtime-environ",
|
||||
"winch-codegen",
|
||||
"winch-environ",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
@@ -50,7 +50,7 @@ rustix = { workspace = true, features = ["mm", "param"] }
|
||||
|
||||
[dev-dependencies]
|
||||
# depend again on wasmtime to activate its default features for tests
|
||||
wasmtime = { workspace = true, features = ['component-model', 'async', 'default'] }
|
||||
wasmtime = { workspace = true, features = ['component-model', 'async', 'default', 'winch'] }
|
||||
env_logger = { workspace = true }
|
||||
log = { workspace = true }
|
||||
filecheck = "0.5.0"
|
||||
@@ -121,6 +121,7 @@ wasmtime-cache = { path = "crates/cache", version = "=8.0.0" }
|
||||
wasmtime-cli-flags = { path = "crates/cli-flags", version = "=8.0.0" }
|
||||
wasmtime-cranelift = { path = "crates/cranelift", version = "=8.0.0" }
|
||||
wasmtime-cranelift-shared = { path = "crates/cranelift-shared", version = "=8.0.0" }
|
||||
wasmtime-winch = { path = "crates/winch", version = "=8.0.0" }
|
||||
wasmtime-environ = { path = "crates/environ", version = "=8.0.0" }
|
||||
wasmtime-explorer = { path = "crates/explorer", version = "=8.0.0" }
|
||||
wasmtime-fiber = { path = "crates/fiber", version = "=8.0.0" }
|
||||
@@ -226,6 +227,7 @@ component-model = [
|
||||
"wasmtime-wast/component-model",
|
||||
"wasmtime-cli-flags/component-model"
|
||||
]
|
||||
winch = ["wasmtime/winch"]
|
||||
|
||||
# Stub feature that does nothing, for Cargo-features compatibility: the new
|
||||
# backend is the default now.
|
||||
|
||||
@@ -20,7 +20,7 @@ use anyhow::{bail, Result};
|
||||
use clap::Parser;
|
||||
use std::collections::HashMap;
|
||||
use std::path::PathBuf;
|
||||
use wasmtime::{Config, ProfilingStrategy};
|
||||
use wasmtime::{Config, ProfilingStrategy, Strategy};
|
||||
|
||||
pub const SUPPORTED_WASM_FEATURES: &[(&str, &str)] = &[
|
||||
("all", "enables all supported WebAssembly features"),
|
||||
@@ -240,6 +240,12 @@ pub struct CommonOptions {
|
||||
/// performance cost.
|
||||
#[clap(long)]
|
||||
pub relaxed_simd_deterministic: bool,
|
||||
/// Explicitly specify the name of the compiler to use for WebAssembly.
|
||||
///
|
||||
/// Currently only `cranelift` and `winch` are supported, but not all builds
|
||||
/// of Wasmtime have both built in.
|
||||
#[clap(long)]
|
||||
pub compiler: Option<String>,
|
||||
}
|
||||
|
||||
impl CommonOptions {
|
||||
@@ -258,6 +264,13 @@ impl CommonOptions {
|
||||
pub fn config(&self, target: Option<&str>) -> Result<Config> {
|
||||
let mut config = Config::new();
|
||||
|
||||
config.strategy(match self.compiler.as_deref() {
|
||||
None => Strategy::Auto,
|
||||
Some("cranelift") => Strategy::Cranelift,
|
||||
Some("winch") => Strategy::Winch,
|
||||
Some(s) => bail!("unknown compiler: {s}"),
|
||||
});
|
||||
|
||||
// Set the target before setting any cranelift options, since the
|
||||
// target will reset any target-specific options.
|
||||
if let Some(target) = target {
|
||||
|
||||
@@ -12,6 +12,9 @@ rust-version.workspace = true
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
rustdoc-args = ["--cfg", "nightlydoc"]
|
||||
# Docs.rs will use the `component-model` feature for documentation;
|
||||
# so this feature also passed in to the `cargo doc` invocation in CI.
|
||||
# See .github/workflows/main.yml
|
||||
features = ["component-model"]
|
||||
|
||||
[dependencies]
|
||||
@@ -21,6 +24,7 @@ wasmtime-jit = { workspace = true }
|
||||
wasmtime-cache = { workspace = true, optional = true }
|
||||
wasmtime-fiber = { workspace = true, optional = true }
|
||||
wasmtime-cranelift = { workspace = true, optional = true }
|
||||
wasmtime-winch = { workspace = true, optional = true }
|
||||
wasmtime-component-macro = { workspace = true, optional = true }
|
||||
wasmtime-component-util = { workspace = true, optional = true }
|
||||
target-lexicon = { workspace = true }
|
||||
@@ -73,6 +77,11 @@ default = [
|
||||
# precompiled WebAssembly modules.
|
||||
cranelift = ["dep:wasmtime-cranelift"]
|
||||
|
||||
# Enables support for winch, the WebAssembly baseline compiler. The Winch compiler
|
||||
# strategy in `Config` will be available. It is currently in active development
|
||||
# and shouldn't be used in production applications.
|
||||
winch = ["dep:wasmtime-winch"]
|
||||
|
||||
# Enables support for incremental compilation cache to be enabled in `Config`.
|
||||
incremental-cache = ["wasmtime-cranelift?/incremental-cache"]
|
||||
|
||||
@@ -103,7 +112,10 @@ pooling-allocator = ["wasmtime-runtime/pooling-allocator"]
|
||||
# Enables support for all architectures in Cranelift, allowing
|
||||
# cross-compilation using the `wasmtime` crate's API, notably the
|
||||
# `Engine::precompile_module` function.
|
||||
all-arch = ["wasmtime-cranelift?/all-arch"]
|
||||
all-arch = [
|
||||
"wasmtime-cranelift?/all-arch",
|
||||
"wasmtime-winch?/all-arch",
|
||||
]
|
||||
|
||||
# Enables trap handling using POSIX signals instead of Mach exceptions on MacOS.
|
||||
# It is useful for applications that do not bind their own exception ports and
|
||||
@@ -116,8 +128,10 @@ posix-signals-on-macos = ["wasmtime-runtime/posix-signals-on-macos"]
|
||||
component-model = [
|
||||
"wasmtime-environ/component-model",
|
||||
"wasmtime-cranelift?/component-model",
|
||||
"wasmtime-winch?/component-model",
|
||||
"wasmtime-runtime/component-model",
|
||||
"dep:wasmtime-component-macro",
|
||||
"dep:wasmtime-component-util",
|
||||
"dep:encoding_rs",
|
||||
]
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ fn main() {
|
||||
// frequently then all rustdoc attributes also need to be updated with the
|
||||
// new condition to ensure the documentation accurately reflects when an API
|
||||
// is available.
|
||||
if cfg!(feature = "cranelift") {
|
||||
if cfg!(feature = "cranelift") || cfg!(feature = "winch") {
|
||||
println!("cargo:rustc-cfg=compiler");
|
||||
}
|
||||
|
||||
|
||||
@@ -85,7 +85,7 @@ impl Component {
|
||||
//
|
||||
// FIXME: need to write more docs here.
|
||||
#[cfg(compiler)]
|
||||
#[cfg_attr(nightlydoc, doc(cfg(feature = "cranelift")))] // see build.rs
|
||||
#[cfg_attr(nightlydoc, doc(cfg(any(feature = "cranelift", feature = "winch"))))] // see build.rs
|
||||
pub fn new(engine: &Engine, bytes: impl AsRef<[u8]>) -> Result<Component> {
|
||||
let bytes = bytes.as_ref();
|
||||
#[cfg(feature = "wat")]
|
||||
@@ -98,7 +98,7 @@ impl Component {
|
||||
//
|
||||
// FIXME: need to write more docs here.
|
||||
#[cfg(compiler)]
|
||||
#[cfg_attr(nightlydoc, doc(cfg(feature = "cranelift")))] // see build.rs
|
||||
#[cfg_attr(nightlydoc, doc(cfg(any(feature = "cranelift", feature = "winch"))))] // see build.rs
|
||||
pub fn from_file(engine: &Engine, file: impl AsRef<Path>) -> Result<Component> {
|
||||
match Self::new(
|
||||
engine,
|
||||
@@ -124,7 +124,7 @@ impl Component {
|
||||
//
|
||||
// FIXME: need to write more docs here.
|
||||
#[cfg(compiler)]
|
||||
#[cfg_attr(nightlydoc, doc(cfg(feature = "cranelift")))] // see build.rs
|
||||
#[cfg_attr(nightlydoc, doc(cfg(any(feature = "cranelift", feature = "winch"))))] // see build.rs
|
||||
pub fn from_binary(engine: &Engine, binary: &[u8]) -> Result<Component> {
|
||||
engine
|
||||
.check_compatible_with_native_host()
|
||||
|
||||
@@ -220,7 +220,7 @@ impl Config {
|
||||
///
|
||||
/// This method will error if the given target triple is not supported.
|
||||
#[cfg(compiler)]
|
||||
#[cfg_attr(nightlydoc, doc(cfg(feature = "cranelift")))] // see build.rs
|
||||
#[cfg_attr(nightlydoc, doc(cfg(any(feature = "cranelift", feature = "winch"))))] // see build.rs
|
||||
pub fn target(&mut self, target: &str) -> Result<&mut Self> {
|
||||
self.compiler_config.target =
|
||||
Some(target_lexicon::Triple::from_str(target).map_err(|e| anyhow::anyhow!(e))?);
|
||||
@@ -820,7 +820,7 @@ impl Config {
|
||||
///
|
||||
/// The default value for this is `Strategy::Auto`.
|
||||
#[cfg(compiler)]
|
||||
#[cfg_attr(nightlydoc, doc(cfg(feature = "cranelift")))] // see build.rs
|
||||
#[cfg_attr(nightlydoc, doc(cfg(any(feature = "cranelift", feature = "winch"))))] // see build.rs
|
||||
pub fn strategy(&mut self, strategy: Strategy) -> &mut Self {
|
||||
self.compiler_config.strategy = strategy;
|
||||
self
|
||||
@@ -854,7 +854,7 @@ impl Config {
|
||||
///
|
||||
/// The default value for this is `false`
|
||||
#[cfg(compiler)]
|
||||
#[cfg_attr(nightlydoc, doc(cfg(feature = "cranelift")))] // see build.rs
|
||||
#[cfg_attr(nightlydoc, doc(cfg(any(feature = "cranelift", feature = "winch"))))] // see build.rs
|
||||
pub fn cranelift_debug_verifier(&mut self, enable: bool) -> &mut Self {
|
||||
let val = if enable { "true" } else { "false" };
|
||||
self.compiler_config
|
||||
@@ -871,7 +871,7 @@ impl Config {
|
||||
///
|
||||
/// The default value for this is `OptLevel::None`.
|
||||
#[cfg(compiler)]
|
||||
#[cfg_attr(nightlydoc, doc(cfg(feature = "cranelift")))] // see build.rs
|
||||
#[cfg_attr(nightlydoc, doc(cfg(any(feature = "cranelift", feature = "winch"))))] // see build.rs
|
||||
pub fn cranelift_opt_level(&mut self, level: OptLevel) -> &mut Self {
|
||||
let val = match level {
|
||||
OptLevel::None => "none",
|
||||
@@ -895,7 +895,7 @@ impl Config {
|
||||
///
|
||||
/// The default value for this is `true`.
|
||||
#[cfg(compiler)]
|
||||
#[cfg_attr(nightlydoc, doc(cfg(feature = "cranelift")))] // see build.rs
|
||||
#[cfg_attr(nightlydoc, doc(cfg(any(feature = "cranelift", feature = "winch"))))] // see build.rs
|
||||
#[deprecated(
|
||||
since = "5.0.0",
|
||||
note = "egraphs will be the default and this method will be removed in a future version."
|
||||
@@ -917,7 +917,7 @@ impl Config {
|
||||
///
|
||||
/// The default value for this is `false`
|
||||
#[cfg(compiler)]
|
||||
#[cfg_attr(nightlydoc, doc(cfg(feature = "cranelift")))] // see build.rs
|
||||
#[cfg_attr(nightlydoc, doc(cfg(any(feature = "cranelift", feature = "winch"))))] // see build.rs
|
||||
pub fn cranelift_nan_canonicalization(&mut self, enable: bool) -> &mut Self {
|
||||
let val = if enable { "true" } else { "false" };
|
||||
self.compiler_config
|
||||
@@ -943,7 +943,7 @@ impl Config {
|
||||
/// cause `Engine::new` fail if the flag's name does not exist, or the value is not appropriate
|
||||
/// for the flag type.
|
||||
#[cfg(compiler)]
|
||||
#[cfg_attr(nightlydoc, doc(cfg(feature = "cranelift")))] // see build.rs
|
||||
#[cfg_attr(nightlydoc, doc(cfg(any(feature = "cranelift", feature = "winch"))))] // see build.rs
|
||||
pub unsafe fn cranelift_flag_enable(&mut self, flag: &str) -> &mut Self {
|
||||
self.compiler_config.flags.insert(flag.to_string());
|
||||
self
|
||||
@@ -969,7 +969,7 @@ impl Config {
|
||||
/// For example, feature `wasm_backtrace` will set `unwind_info` to `true`, but if it's
|
||||
/// manually set to false then it will fail.
|
||||
#[cfg(compiler)]
|
||||
#[cfg_attr(nightlydoc, doc(cfg(feature = "cranelift")))] // see build.rs
|
||||
#[cfg_attr(nightlydoc, doc(cfg(any(feature = "cranelift", feature = "winch"))))] // see build.rs
|
||||
pub unsafe fn cranelift_flag_set(&mut self, name: &str, value: &str) -> &mut Self {
|
||||
self.compiler_config
|
||||
.settings
|
||||
@@ -1546,7 +1546,18 @@ impl Config {
|
||||
#[cfg(compiler)]
|
||||
pub(crate) fn build_compiler(&mut self) -> Result<Box<dyn wasmtime_environ::Compiler>> {
|
||||
let mut compiler = match self.compiler_config.strategy {
|
||||
Strategy::Auto | Strategy::Cranelift => wasmtime_cranelift::builder(),
|
||||
#[cfg(feature = "cranelift")]
|
||||
Strategy::Auto => wasmtime_cranelift::builder(),
|
||||
#[cfg(all(feature = "winch", not(feature = "cranelift")))]
|
||||
Strategy::Auto => wasmtime_winch::builder(),
|
||||
#[cfg(feature = "cranelift")]
|
||||
Strategy::Cranelift => wasmtime_cranelift::builder(),
|
||||
#[cfg(not(feature = "cranelift"))]
|
||||
Strategy::Cranelift => bail!("cranelift support not compiled in"),
|
||||
#[cfg(feature = "winch")]
|
||||
Strategy::Winch => wasmtime_winch::builder(),
|
||||
#[cfg(not(feature = "winch"))]
|
||||
Strategy::Winch => bail!("winch support not compiled in"),
|
||||
};
|
||||
|
||||
if let Some(target) = &self.compiler_config.target {
|
||||
@@ -1711,6 +1722,10 @@ pub enum Strategy {
|
||||
/// Currently the default backend, Cranelift aims to be a reasonably fast
|
||||
/// code generator which generates high quality machine code.
|
||||
Cranelift,
|
||||
|
||||
/// A baseline compiler for WebAssembly, currently under active development and not ready for
|
||||
/// production applications.
|
||||
Winch,
|
||||
}
|
||||
|
||||
/// Possible optimization levels for the Cranelift codegen backend.
|
||||
|
||||
@@ -218,7 +218,7 @@ impl Engine {
|
||||
/// [binary]: https://webassembly.github.io/spec/core/binary/index.html
|
||||
/// [text]: https://webassembly.github.io/spec/core/text/index.html
|
||||
#[cfg(compiler)]
|
||||
#[cfg_attr(nightlydoc, doc(cfg(feature = "cranelift")))] // see build.rs
|
||||
#[cfg_attr(nightlydoc, doc(cfg(any(feature = "cranelift", feature = "winch"))))] // see build.rs
|
||||
pub fn precompile_module(&self, bytes: &[u8]) -> Result<Vec<u8>> {
|
||||
#[cfg(feature = "wat")]
|
||||
let bytes = wat::parse_bytes(&bytes)?;
|
||||
@@ -229,7 +229,7 @@ impl Engine {
|
||||
/// Same as [`Engine::precompile_module`] except for a
|
||||
/// [`Component`](crate::component::Component)
|
||||
#[cfg(compiler)]
|
||||
#[cfg_attr(nightlydoc, doc(cfg(feature = "cranelift")))] // see build.rs
|
||||
#[cfg_attr(nightlydoc, doc(cfg(any(feature = "cranelift", feature = "winch"))))] // see build.rs
|
||||
#[cfg(feature = "component-model")]
|
||||
#[cfg_attr(nightlydoc, doc(cfg(feature = "component-model")))]
|
||||
pub fn precompile_component(&self, bytes: &[u8]) -> Result<Vec<u8>> {
|
||||
|
||||
@@ -346,7 +346,7 @@ impl Func {
|
||||
///
|
||||
/// [`Trap`]: crate::Trap
|
||||
#[cfg(compiler)]
|
||||
#[cfg_attr(nightlydoc, doc(cfg(feature = "cranelift")))] // see build.rs
|
||||
#[cfg_attr(nightlydoc, doc(cfg(any(feature = "cranelift", feature = "winch"))))] // see build.rs
|
||||
pub fn new<T>(
|
||||
store: impl AsContextMut<Data = T>,
|
||||
ty: FuncType,
|
||||
@@ -384,7 +384,7 @@ impl Func {
|
||||
/// the `func` provided correctly interprets the argument types provided to
|
||||
/// it, or that the results it produces will be of the correct type.
|
||||
#[cfg(compiler)]
|
||||
#[cfg_attr(nightlydoc, doc(cfg(feature = "cranelift")))] // see build.rs
|
||||
#[cfg_attr(nightlydoc, doc(cfg(any(feature = "cranelift", feature = "winch"))))] // see build.rs
|
||||
pub unsafe fn new_unchecked<T>(
|
||||
mut store: impl AsContextMut<Data = T>,
|
||||
ty: FuncType,
|
||||
|
||||
@@ -275,7 +275,7 @@ impl<T> Linker<T> {
|
||||
/// # }
|
||||
/// ```
|
||||
#[cfg(compiler)]
|
||||
#[cfg_attr(nightlydoc, doc(cfg(feature = "cranelift")))] // see build.rs
|
||||
#[cfg_attr(nightlydoc, doc(cfg(any(feature = "cranelift", feature = "winch"))))] // see build.rs
|
||||
pub fn define_unknown_imports_as_traps(&mut self, module: &Module) -> anyhow::Result<()> {
|
||||
for import in module.imports() {
|
||||
if let Err(import_err) = self._get_by_import(&import) {
|
||||
@@ -311,7 +311,7 @@ impl<T> Linker<T> {
|
||||
/// # }
|
||||
/// ```
|
||||
#[cfg(compiler)]
|
||||
#[cfg_attr(nightlydoc, doc(cfg(feature = "cranelift")))] // see build.rs
|
||||
#[cfg_attr(nightlydoc, doc(cfg(any(feature = "cranelift", feature = "winch"))))] // see build.rs
|
||||
pub fn define_unknown_imports_as_default_values(
|
||||
&mut self,
|
||||
module: &Module,
|
||||
@@ -416,7 +416,7 @@ impl<T> Linker<T> {
|
||||
///
|
||||
/// For more information see [`Linker::func_wrap`].
|
||||
#[cfg(compiler)]
|
||||
#[cfg_attr(nightlydoc, doc(cfg(feature = "cranelift")))] // see build.rs
|
||||
#[cfg_attr(nightlydoc, doc(cfg(any(feature = "cranelift", feature = "winch"))))] // see build.rs
|
||||
pub fn func_new(
|
||||
&mut self,
|
||||
module: &str,
|
||||
@@ -434,7 +434,7 @@ impl<T> Linker<T> {
|
||||
///
|
||||
/// For more information see [`Linker::func_wrap`].
|
||||
#[cfg(compiler)]
|
||||
#[cfg_attr(nightlydoc, doc(cfg(feature = "cranelift")))] // see build.rs
|
||||
#[cfg_attr(nightlydoc, doc(cfg(any(feature = "cranelift", feature = "winch"))))] // see build.rs
|
||||
pub unsafe fn func_new_unchecked(
|
||||
&mut self,
|
||||
module: &str,
|
||||
@@ -754,7 +754,7 @@ impl<T> Linker<T> {
|
||||
/// # }
|
||||
/// ```
|
||||
#[cfg(compiler)]
|
||||
#[cfg_attr(nightlydoc, doc(cfg(feature = "cranelift")))] // see build.rs
|
||||
#[cfg_attr(nightlydoc, doc(cfg(any(feature = "cranelift", feature = "winch"))))] // see build.rs
|
||||
pub fn module(
|
||||
&mut self,
|
||||
mut store: impl AsContextMut<Data = T>,
|
||||
|
||||
@@ -196,7 +196,7 @@ impl Module {
|
||||
/// # }
|
||||
/// ```
|
||||
#[cfg(compiler)]
|
||||
#[cfg_attr(nightlydoc, doc(cfg(feature = "cranelift")))] // see build.rs
|
||||
#[cfg_attr(nightlydoc, doc(cfg(any(feature = "cranelift", feature = "winch"))))] // see build.rs
|
||||
pub fn new(engine: &Engine, bytes: impl AsRef<[u8]>) -> Result<Module> {
|
||||
let bytes = bytes.as_ref();
|
||||
#[cfg(feature = "wat")]
|
||||
@@ -233,7 +233,7 @@ impl Module {
|
||||
/// # }
|
||||
/// ```
|
||||
#[cfg(compiler)]
|
||||
#[cfg_attr(nightlydoc, doc(cfg(feature = "cranelift")))] // see build.rs
|
||||
#[cfg_attr(nightlydoc, doc(cfg(any(feature = "cranelift", feature = "winch"))))] // see build.rs
|
||||
pub fn from_file(engine: &Engine, file: impl AsRef<Path>) -> Result<Module> {
|
||||
match Self::new(
|
||||
engine,
|
||||
@@ -286,7 +286,7 @@ impl Module {
|
||||
/// # }
|
||||
/// ```
|
||||
#[cfg(compiler)]
|
||||
#[cfg_attr(nightlydoc, doc(cfg(feature = "cranelift")))] // see build.rs
|
||||
#[cfg_attr(nightlydoc, doc(cfg(any(feature = "cranelift", feature = "winch"))))] // see build.rs
|
||||
pub fn from_binary(engine: &Engine, binary: &[u8]) -> Result<Module> {
|
||||
engine
|
||||
.check_compatible_with_native_host()
|
||||
@@ -358,7 +358,7 @@ impl Module {
|
||||
/// reflect the current state of the file, not necessarily the origianl
|
||||
/// state of the file.
|
||||
#[cfg(compiler)]
|
||||
#[cfg_attr(nightlydoc, doc(cfg(feature = "cranelift")))] // see build.rs
|
||||
#[cfg_attr(nightlydoc, doc(cfg(any(feature = "cranelift", feature = "winch"))))] // see build.rs
|
||||
pub unsafe fn from_trusted_file(engine: &Engine, file: impl AsRef<Path>) -> Result<Module> {
|
||||
let mmap = MmapVec::from_file(file.as_ref())?;
|
||||
if &mmap[0..4] == b"\x7fELF" {
|
||||
@@ -738,7 +738,7 @@ impl Module {
|
||||
/// this method can be useful to get the serialized version without
|
||||
/// compiling twice.
|
||||
#[cfg(compiler)]
|
||||
#[cfg_attr(nightlydoc, doc(cfg(feature = "cranelift")))] // see build.rs
|
||||
#[cfg_attr(nightlydoc, doc(cfg(any(feature = "cranelift", feature = "winch"))))] // see build.rs
|
||||
pub fn serialize(&self) -> Result<Vec<u8>> {
|
||||
// The current representation of compiled modules within a compiled
|
||||
// component means that it cannot be serialized. The mmap returned here
|
||||
|
||||
@@ -9,6 +9,7 @@ repository = "https://github.com/bytecodealliance/wasmtime"
|
||||
|
||||
[dependencies]
|
||||
winch-codegen = { workspace = true }
|
||||
winch-environ = { workspace = true }
|
||||
target-lexicon = { workspace = true }
|
||||
wasmtime-environ = { workspace = true }
|
||||
anyhow = { workspace = true }
|
||||
@@ -19,6 +20,5 @@ wasmparser = { workspace = true }
|
||||
gimli = { workspace = true }
|
||||
|
||||
[features]
|
||||
default = ["component-model"]
|
||||
component-model = ["wasmtime-environ/component-model"]
|
||||
all-arch = ["winch-codegen/all-arch"]
|
||||
|
||||
@@ -1,50 +1,136 @@
|
||||
use anyhow::Result;
|
||||
use cranelift_codegen::{Final, MachBufferFinalized};
|
||||
use object::write::{Object, SymbolId};
|
||||
use std::any::Any;
|
||||
use std::sync::Mutex;
|
||||
use wasmparser::FuncValidatorAllocations;
|
||||
use wasmtime_cranelift_shared::obj::ModuleTextBuilder;
|
||||
use wasmtime_environ::{
|
||||
CompileError, DefinedFuncIndex, FuncIndex, FunctionBodyData, FunctionLoc, ModuleTranslation,
|
||||
ModuleTypes, PrimaryMap, Tunables, WasmFunctionInfo,
|
||||
CompileError, DefinedFuncIndex, FilePos, FuncIndex, FunctionBodyData, FunctionLoc,
|
||||
ModuleTranslation, ModuleTypes, PrimaryMap, Tunables, WasmFunctionInfo,
|
||||
};
|
||||
use winch_codegen::TargetIsa;
|
||||
use winch_environ::FuncEnv;
|
||||
|
||||
pub(crate) struct Compiler {
|
||||
isa: Box<dyn TargetIsa>,
|
||||
allocations: Mutex<Vec<FuncValidatorAllocations>>,
|
||||
}
|
||||
|
||||
struct CompiledFunction(MachBufferFinalized<Final>);
|
||||
|
||||
impl Compiler {
|
||||
pub fn new(isa: Box<dyn TargetIsa>) -> Self {
|
||||
Self { isa }
|
||||
Self {
|
||||
isa,
|
||||
allocations: Mutex::new(Vec::new()),
|
||||
}
|
||||
}
|
||||
|
||||
fn take_allocations(&self) -> FuncValidatorAllocations {
|
||||
self.allocations
|
||||
.lock()
|
||||
.unwrap()
|
||||
.pop()
|
||||
.unwrap_or_else(Default::default)
|
||||
}
|
||||
|
||||
fn save_allocations(&self, allocs: FuncValidatorAllocations) {
|
||||
self.allocations.lock().unwrap().push(allocs)
|
||||
}
|
||||
}
|
||||
|
||||
impl wasmtime_environ::Compiler for Compiler {
|
||||
fn compile_function(
|
||||
&self,
|
||||
_translation: &ModuleTranslation<'_>,
|
||||
_index: DefinedFuncIndex,
|
||||
_data: FunctionBodyData<'_>,
|
||||
translation: &ModuleTranslation<'_>,
|
||||
index: DefinedFuncIndex,
|
||||
data: FunctionBodyData<'_>,
|
||||
_tunables: &Tunables,
|
||||
_types: &ModuleTypes,
|
||||
) -> Result<(WasmFunctionInfo, Box<dyn Any + Send>), CompileError> {
|
||||
todo!()
|
||||
let index = translation.module.func_index(index);
|
||||
let sig = translation.get_types().function_at(index.as_u32()).unwrap();
|
||||
let FunctionBodyData { body, validator } = data;
|
||||
let start_srcloc = FilePos::new(
|
||||
body.get_binary_reader()
|
||||
.original_position()
|
||||
.try_into()
|
||||
.unwrap(),
|
||||
);
|
||||
let mut validator = validator.into_validator(self.take_allocations());
|
||||
let env = FuncEnv::new(&translation.module, translation.get_types(), &self.isa);
|
||||
let buffer = self
|
||||
.isa
|
||||
.compile_function(&sig, &body, &env, &mut validator)
|
||||
.map_err(|e| CompileError::Codegen(format!("{e:?}")));
|
||||
self.save_allocations(validator.into_allocations());
|
||||
let buffer = buffer?;
|
||||
|
||||
Ok((
|
||||
WasmFunctionInfo {
|
||||
start_srcloc,
|
||||
stack_maps: Box::new([]),
|
||||
},
|
||||
Box::new(CompiledFunction(buffer)),
|
||||
))
|
||||
}
|
||||
|
||||
fn compile_host_to_wasm_trampoline(
|
||||
&self,
|
||||
_ty: &wasmtime_environ::WasmFuncType,
|
||||
ty: &wasmtime_environ::WasmFuncType,
|
||||
) -> Result<Box<dyn Any + Send>, CompileError> {
|
||||
todo!()
|
||||
let wasm_ty = wasmparser::FuncType::new(
|
||||
ty.params().iter().copied().map(Into::into),
|
||||
ty.returns().iter().copied().map(Into::into),
|
||||
);
|
||||
|
||||
let buffer = self
|
||||
.isa
|
||||
.host_to_wasm_trampoline(&wasm_ty)
|
||||
.map_err(|e| CompileError::Codegen(format!("{:?}", e)))?;
|
||||
|
||||
Ok(Box::new(CompiledFunction(buffer)))
|
||||
}
|
||||
|
||||
fn append_code(
|
||||
&self,
|
||||
_obj: &mut Object<'static>,
|
||||
_funcs: &[(String, Box<dyn Any + Send>)],
|
||||
obj: &mut Object<'static>,
|
||||
funcs: &[(String, Box<dyn Any + Send>)],
|
||||
_tunables: &Tunables,
|
||||
_resolve_reloc: &dyn Fn(usize, FuncIndex) -> usize,
|
||||
resolve_reloc: &dyn Fn(usize, FuncIndex) -> usize,
|
||||
) -> Result<Vec<(SymbolId, FunctionLoc)>> {
|
||||
assert!(_funcs.is_empty());
|
||||
Ok(Vec::new())
|
||||
let mut builder =
|
||||
ModuleTextBuilder::new(obj, self, self.isa.text_section_builder(funcs.len()));
|
||||
|
||||
let mut ret = Vec::with_capacity(funcs.len());
|
||||
for (i, (sym, func)) in funcs.iter().enumerate() {
|
||||
let func = &func.downcast_ref::<CompiledFunction>().unwrap().0;
|
||||
|
||||
// TODO: Implement copying over this data into the
|
||||
// `ModuleTextBuilder` type. Note that this should probably be
|
||||
// deduplicated with the cranelift implementation in the long run.
|
||||
assert!(func.relocs().is_empty());
|
||||
assert!(func.traps().is_empty());
|
||||
assert!(func.stack_maps().is_empty());
|
||||
|
||||
let (sym, range) = builder.append_func(
|
||||
&sym,
|
||||
func.data(),
|
||||
self.function_alignment(),
|
||||
None,
|
||||
&[],
|
||||
|idx| resolve_reloc(i, idx),
|
||||
);
|
||||
|
||||
let info = FunctionLoc {
|
||||
start: u32::try_from(range.start).unwrap(),
|
||||
length: u32::try_from(range.end - range.start).unwrap(),
|
||||
};
|
||||
ret.push((sym, info));
|
||||
}
|
||||
builder.finish();
|
||||
Ok(ret)
|
||||
}
|
||||
|
||||
fn emit_trampoline_obj(
|
||||
|
||||
@@ -56,8 +56,8 @@ const CRATES_TO_PUBLISH: &[&str] = &[
|
||||
"wasmtime-cranelift",
|
||||
"wasmtime-jit",
|
||||
"wasmtime-cache",
|
||||
"wasmtime-winch",
|
||||
"winch-environ",
|
||||
"wasmtime-winch",
|
||||
"wasmtime",
|
||||
// wasi-common/wiggle
|
||||
"wiggle",
|
||||
|
||||
@@ -34,6 +34,9 @@ mod traps;
|
||||
mod wait_notify;
|
||||
mod wasi_testsuite;
|
||||
mod wast;
|
||||
// Currently Winch is only supported in x86_64 unix platforms.
|
||||
#[cfg(all(target_arch = "x86_64", target_family = "unix"))]
|
||||
mod winch;
|
||||
|
||||
/// A helper to compile a module in a new store with reference types enabled.
|
||||
pub(crate) fn ref_types_module(
|
||||
|
||||
100
tests/all/winch.rs
Normal file
100
tests/all/winch.rs
Normal file
@@ -0,0 +1,100 @@
|
||||
use anyhow::Result;
|
||||
use wasmtime::*;
|
||||
|
||||
#[test]
|
||||
fn compiles_with_winch() -> Result<()> {
|
||||
let mut c = Config::new();
|
||||
|
||||
c.strategy(Strategy::Winch);
|
||||
|
||||
let engine = Engine::new(&c)?;
|
||||
|
||||
// Winch only supports a very basic function signature for now while it's being developed.
|
||||
let test_mod = r#"
|
||||
(module
|
||||
(func $test (result i32)
|
||||
(i32.const 42)
|
||||
)
|
||||
(export "test" (func $test))
|
||||
)
|
||||
"#;
|
||||
|
||||
let mut store = Store::new(&engine, ());
|
||||
|
||||
let module = Module::new(&engine, test_mod)?;
|
||||
|
||||
let instance = Instance::new(&mut store, &module, &[])?;
|
||||
|
||||
let f = instance
|
||||
.get_func(&mut store, "test")
|
||||
.ok_or(anyhow::anyhow!("test function not found"))?;
|
||||
|
||||
let mut returns = vec![Val::null(); 1];
|
||||
|
||||
// Winch doesn't support calling typed functions at the moment.
|
||||
f.call(&mut store, &[], &mut returns)?;
|
||||
|
||||
assert_eq!(returns.len(), 1);
|
||||
assert_eq!(returns[0].unwrap_i32(), 42);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn compiles_with_winch_stack_arguments() -> Result<()> {
|
||||
let mut c = Config::new();
|
||||
|
||||
c.strategy(Strategy::Winch);
|
||||
|
||||
let engine = Engine::new(&c)?;
|
||||
|
||||
// Winch only supports a very basic function signature for now while it's being developed.
|
||||
let test_mod = r#"
|
||||
(module
|
||||
(func $sum10 (param $arg_1 i32) (param $arg_2 i32) (param $arg_3 i32) (param $arg_4 i32) (param $arg_5 i32) (param $arg_6 i32) (param $arg_7 i32) (param $arg_8 i32) (param $arg_9 i32) (param $arg_10 i32) (result i32)
|
||||
local.get $arg_1
|
||||
local.get $arg_2
|
||||
i32.add
|
||||
local.get $arg_3
|
||||
i32.add
|
||||
local.get $arg_4
|
||||
i32.add
|
||||
local.get $arg_5
|
||||
i32.add
|
||||
local.get $arg_6
|
||||
i32.add
|
||||
local.get $arg_7
|
||||
i32.add
|
||||
local.get $arg_8
|
||||
i32.add
|
||||
local.get $arg_9
|
||||
i32.add
|
||||
local.get $arg_10
|
||||
i32.add)
|
||||
(export "sum10" (func $sum10))
|
||||
)
|
||||
"#;
|
||||
|
||||
let mut store = Store::new(&engine, ());
|
||||
|
||||
let module = Module::new(&engine, test_mod)?;
|
||||
|
||||
let instance = Instance::new(&mut store, &module, &[])?;
|
||||
|
||||
let f = instance
|
||||
.get_func(&mut store, "sum10")
|
||||
.ok_or(anyhow::anyhow!("sum10 function not found"))?;
|
||||
|
||||
let mut returns = vec![Val::null(); 1];
|
||||
|
||||
// create a new Val array with ten 1s
|
||||
let args = vec![Val::I32(1); 10];
|
||||
|
||||
// Winch doesn't support calling typed functions at the moment.
|
||||
f.call(&mut store, &args, &mut returns)?;
|
||||
|
||||
assert_eq!(returns.len(), 1);
|
||||
assert_eq!(returns[0].unwrap_i32(), 10);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -4,7 +4,7 @@ use wasmparser::ValType;
|
||||
/// Slots for stack arguments are addressed from the frame pointer.
|
||||
/// Slots for function-defined locals and for registers are addressed
|
||||
/// from the stack pointer.
|
||||
#[derive(Eq, PartialEq)]
|
||||
#[derive(Clone, Eq, PartialEq)]
|
||||
enum Base {
|
||||
FP,
|
||||
SP,
|
||||
@@ -14,6 +14,7 @@ enum Base {
|
||||
///
|
||||
/// Represents the type, location and addressing mode of a local
|
||||
/// in the stack's local and argument area.
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct LocalSlot {
|
||||
/// The offset of the local slot.
|
||||
pub offset: u32,
|
||||
|
||||
@@ -210,3 +210,10 @@ where
|
||||
let alignment_mask = alignment - 1.into();
|
||||
(value + alignment_mask) & !alignment_mask
|
||||
}
|
||||
|
||||
/// Calculates the delta needed to adjust a function's frame plus some
|
||||
/// addend to a given alignment.
|
||||
pub(crate) fn calculate_frame_adjustment(frame_size: u32, addend: u32, alignment: u32) -> u32 {
|
||||
let total = frame_size + addend;
|
||||
(alignment - (total % alignment)) % alignment
|
||||
}
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
//! calling convention, see [ABI].
|
||||
use super::CodeGenContext;
|
||||
use crate::{
|
||||
abi::{align_to, ABIArg, ABIResult, ABISig, ABI},
|
||||
masm::{MacroAssembler, OperandSize},
|
||||
abi::{align_to, calculate_frame_adjustment, ABIArg, ABIResult, ABISig, ABI},
|
||||
masm::{CalleeKind, MacroAssembler, OperandSize},
|
||||
reg::Reg,
|
||||
stack::Val,
|
||||
};
|
||||
@@ -39,7 +39,7 @@ pub(crate) struct FnCall<'a> {
|
||||
/// │ │
|
||||
/// │ Stack space created by any previous spills │
|
||||
/// │ from the value stack; and which memory values │
|
||||
/// │ are used as function arguments. │
|
||||
/// │ are used as function arguments. │
|
||||
/// │ │
|
||||
/// ├──────────────────────────────────────────────────┤ ---> The Wasm value stack at this point in time would look like:
|
||||
/// │ │ [ Reg | Reg | Mem(offset) | Mem(offset) ]
|
||||
@@ -51,7 +51,7 @@ pub(crate) struct FnCall<'a> {
|
||||
/// ├─────────────────────────────────────────────────┬┤ ---> The Wasm value stack at this point in time would look like:
|
||||
/// │ │ [ Mem(offset) | Mem(offset) | Mem(offset) | Mem(offset) ]
|
||||
/// │ │ Assuming that the callee takes 4 arguments, we calculate
|
||||
/// │ │ 2 spilled registers + 2 memory values; all of which will be used
|
||||
/// │ │ 2 spilled registers + 2 memory values; all of which will be used
|
||||
/// │ Stack space allocated for │ as arguments to the call via `assign_args`, thus the memory they represent is
|
||||
/// │ the callee function arguments in the stack; │ is considered to be consumed by the call.
|
||||
/// │ represented by `arg_stack_space` │
|
||||
@@ -59,7 +59,7 @@ pub(crate) struct FnCall<'a> {
|
||||
/// │ │
|
||||
/// │ │
|
||||
/// └──────────────────────────────────────────────────┘ ------> Stack pointer when emitting the call
|
||||
///
|
||||
///
|
||||
total_stack_space: u32,
|
||||
/// The total stack space needed for the callee arguments on the
|
||||
/// stack, including any adjustments to the function's frame and
|
||||
@@ -161,7 +161,7 @@ impl<'a> FnCall<'a> {
|
||||
) {
|
||||
masm.reserve_stack(self.arg_stack_space);
|
||||
self.assign_args(context, masm, <A as ABI>::scratch_reg());
|
||||
masm.call(callee);
|
||||
masm.call(CalleeKind::Direct(callee));
|
||||
masm.free_stack(self.total_stack_space);
|
||||
context.drop_last(self.abi_sig.params.len());
|
||||
// The stack pointer at the end of the function call
|
||||
@@ -213,10 +213,3 @@ impl<'a> FnCall<'a> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Calculates the delta needed to adjust a function's frame plus some
|
||||
/// addend to a given alignment.
|
||||
fn calculate_frame_adjustment(frame_size: u32, addend: u32, alignment: u32) -> u32 {
|
||||
let total = frame_size + addend;
|
||||
(alignment - (total % alignment)) % alignment
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ mod context;
|
||||
pub(crate) use context::*;
|
||||
mod env;
|
||||
pub use env::*;
|
||||
mod call;
|
||||
pub mod call;
|
||||
|
||||
/// The code generation abstraction.
|
||||
pub(crate) struct CodeGen<'a, A, M>
|
||||
@@ -59,7 +59,7 @@ where
|
||||
pub fn emit(
|
||||
&mut self,
|
||||
body: &mut BinaryReader<'a>,
|
||||
validator: FuncValidator<ValidatorResources>,
|
||||
validator: &mut FuncValidator<ValidatorResources>,
|
||||
) -> Result<()> {
|
||||
self.emit_start()
|
||||
.and_then(|_| self.emit_body(body, validator))
|
||||
@@ -78,7 +78,7 @@ where
|
||||
fn emit_body(
|
||||
&mut self,
|
||||
body: &mut BinaryReader<'a>,
|
||||
mut validator: FuncValidator<ValidatorResources>,
|
||||
validator: &mut FuncValidator<ValidatorResources>,
|
||||
) -> Result<()> {
|
||||
self.spill_register_arguments();
|
||||
let defined_locals_range = &self.context.frame.defined_locals_range;
|
||||
|
||||
@@ -20,6 +20,47 @@ impl DefinedLocalsRange {
|
||||
}
|
||||
}
|
||||
|
||||
/// An abstraction to read the defined locals from the WASM binary for a function.
|
||||
#[derive(Default)]
|
||||
pub(crate) struct DefinedLocals {
|
||||
/// The defined locals for a function.
|
||||
pub defined_locals: Locals,
|
||||
/// The size of the defined locals.
|
||||
pub stack_size: u32,
|
||||
}
|
||||
|
||||
impl DefinedLocals {
|
||||
/// Compute the local slots for a WASM function.
|
||||
pub fn new(
|
||||
reader: &mut BinaryReader<'_>,
|
||||
validator: &mut FuncValidator<ValidatorResources>,
|
||||
) -> Result<Self> {
|
||||
let mut next_stack = 0;
|
||||
// The first 32 bits of a WASM binary function describe the number of locals
|
||||
let local_count = reader.read_var_u32()?;
|
||||
let mut slots: Locals = Default::default();
|
||||
|
||||
for _ in 0..local_count {
|
||||
let position = reader.original_position();
|
||||
let count = reader.read_var_u32()?;
|
||||
let ty = reader.read()?;
|
||||
validator.define_locals(position, count, ty)?;
|
||||
|
||||
let ty: ValType = ty.try_into()?;
|
||||
for _ in 0..count {
|
||||
let ty_size = ty_size(&ty);
|
||||
next_stack = align_to(next_stack, ty_size) + ty_size;
|
||||
slots.push(LocalSlot::new(ty, next_stack));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
defined_locals: slots,
|
||||
stack_size: next_stack,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Frame handler abstraction.
|
||||
pub(crate) struct Frame {
|
||||
/// The size of the entire local area; the arguments plus the function defined locals.
|
||||
@@ -37,22 +78,29 @@ pub(crate) struct Frame {
|
||||
|
||||
impl Frame {
|
||||
/// Allocate a new Frame.
|
||||
pub fn new<A: ABI>(
|
||||
sig: &ABISig,
|
||||
body: &mut BinaryReader<'_>,
|
||||
validator: &mut FuncValidator<ValidatorResources>,
|
||||
abi: &A,
|
||||
) -> Result<Self> {
|
||||
pub fn new<A: ABI>(sig: &ABISig, defined_locals: &DefinedLocals, abi: &A) -> Result<Self> {
|
||||
let (mut locals, defined_locals_start) = Self::compute_arg_slots(sig, abi)?;
|
||||
let (defined_slots, defined_locals_end) =
|
||||
Self::compute_defined_slots(body, validator, defined_locals_start)?;
|
||||
locals.extend(defined_slots);
|
||||
let locals_size = align_to(defined_locals_end, abi.stack_align().into());
|
||||
|
||||
// The defined locals have a zero-based offset by default
|
||||
// so we need to add the defined locals start to the offset.
|
||||
locals.extend(
|
||||
defined_locals
|
||||
.defined_locals
|
||||
.iter()
|
||||
.map(|l| LocalSlot::new(l.ty, l.offset + defined_locals_start)),
|
||||
);
|
||||
|
||||
let locals_size = align_to(
|
||||
defined_locals_start + defined_locals.stack_size,
|
||||
abi.stack_align().into(),
|
||||
);
|
||||
|
||||
Ok(Self {
|
||||
locals,
|
||||
locals_size,
|
||||
defined_locals_range: DefinedLocalsRange(defined_locals_start..defined_locals_end),
|
||||
defined_locals_range: DefinedLocalsRange(
|
||||
defined_locals_start..defined_locals.stack_size,
|
||||
),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -115,30 +163,4 @@ impl Frame {
|
||||
ABIArg::Stack { ty, offset } => LocalSlot::stack_arg(*ty, offset + arg_base_offset),
|
||||
}
|
||||
}
|
||||
|
||||
fn compute_defined_slots(
|
||||
reader: &mut BinaryReader<'_>,
|
||||
validator: &mut FuncValidator<ValidatorResources>,
|
||||
next_stack: u32,
|
||||
) -> Result<(Locals, u32)> {
|
||||
let mut next_stack = next_stack;
|
||||
let local_count = reader.read_var_u32()?;
|
||||
let mut slots: Locals = Default::default();
|
||||
|
||||
for _ in 0..local_count {
|
||||
let position = reader.original_position();
|
||||
let count = reader.read_var_u32()?;
|
||||
let ty = reader.read()?;
|
||||
validator.define_locals(position, count, ty)?;
|
||||
|
||||
let ty: ValType = ty.try_into()?;
|
||||
for _ in 0..count {
|
||||
let ty_size = ty_size(&ty);
|
||||
next_stack = align_to(next_stack, ty_size) + ty_size;
|
||||
slots.push(LocalSlot::new(ty, next_stack));
|
||||
}
|
||||
}
|
||||
|
||||
Ok((slots, next_stack))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ use crate::{
|
||||
abi::local::LocalSlot,
|
||||
codegen::CodeGenContext,
|
||||
isa::reg::Reg,
|
||||
masm::{DivKind, MacroAssembler as Masm, OperandSize, RegImm, RemKind},
|
||||
masm::{CalleeKind, DivKind, MacroAssembler as Masm, OperandSize, RegImm, RemKind},
|
||||
};
|
||||
use cranelift_codegen::{settings, Final, MachBufferFinalized};
|
||||
|
||||
@@ -136,7 +136,7 @@ impl Masm for MacroAssembler {
|
||||
self.asm.str(src, dst, size);
|
||||
}
|
||||
|
||||
fn call(&mut self, _callee: u32) {
|
||||
fn call(&mut self, _callee: CalleeKind) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
@@ -148,7 +148,7 @@ impl Masm for MacroAssembler {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn sp_offset(&mut self) -> u32 {
|
||||
fn sp_offset(&self) -> u32 {
|
||||
self.sp_offset
|
||||
}
|
||||
|
||||
@@ -194,6 +194,10 @@ impl Masm for MacroAssembler {
|
||||
|
||||
self.sp_offset
|
||||
}
|
||||
|
||||
fn address_from_reg(&self, reg: Reg, offset: u32) -> Self::Address {
|
||||
Address::offset(reg, offset as i64)
|
||||
}
|
||||
}
|
||||
|
||||
impl MacroAssembler {
|
||||
|
||||
@@ -2,7 +2,7 @@ use self::regs::{scratch, ALL_GPR};
|
||||
use crate::{
|
||||
abi::ABI,
|
||||
codegen::{CodeGen, CodeGenContext},
|
||||
frame::Frame,
|
||||
frame::{DefinedLocals, Frame},
|
||||
isa::{Builder, TargetIsa},
|
||||
masm::MacroAssembler,
|
||||
regalloc::RegAlloc,
|
||||
@@ -86,14 +86,16 @@ impl TargetIsa for Aarch64 {
|
||||
sig: &FuncType,
|
||||
body: &FunctionBody,
|
||||
env: &dyn FuncEnv,
|
||||
mut validator: FuncValidator<ValidatorResources>,
|
||||
validator: &mut FuncValidator<ValidatorResources>,
|
||||
) -> Result<MachBufferFinalized<Final>> {
|
||||
let mut body = body.get_binary_reader();
|
||||
let mut masm = Aarch64Masm::new(self.shared_flags.clone());
|
||||
let stack = Stack::new();
|
||||
let abi = abi::Aarch64ABI::default();
|
||||
let abi_sig = abi.sig(sig);
|
||||
let frame = Frame::new(&abi_sig, &mut body, &mut validator, &abi)?;
|
||||
|
||||
let defined_locals = DefinedLocals::new(&mut body, validator)?;
|
||||
let frame = Frame::new(&abi_sig, &defined_locals, &abi)?;
|
||||
// TODO: Add floating point bitmask
|
||||
let regalloc = RegAlloc::new(RegSet::new(ALL_GPR, 0), scratch());
|
||||
let codegen_context = CodeGenContext::new(regalloc, stack, &frame);
|
||||
@@ -113,4 +115,8 @@ impl TargetIsa for Aarch64 {
|
||||
// See `cranelift_codegen::isa::TargetIsa::function_alignment`.
|
||||
32
|
||||
}
|
||||
|
||||
fn host_to_wasm_trampoline(&self, _ty: &FuncType) -> Result<MachBufferFinalized<Final>> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,12 +90,13 @@ pub trait TargetIsa: Send + Sync {
|
||||
false
|
||||
}
|
||||
|
||||
/// Compile a function.
|
||||
fn compile_function(
|
||||
&self,
|
||||
sig: &FuncType,
|
||||
body: &FunctionBody,
|
||||
env: &dyn FuncEnv,
|
||||
validator: FuncValidator<ValidatorResources>,
|
||||
validator: &mut FuncValidator<ValidatorResources>,
|
||||
) -> Result<MachBufferFinalized<Final>>;
|
||||
|
||||
/// Get the default calling convention of the underlying target triple.
|
||||
@@ -119,6 +120,9 @@ pub trait TargetIsa: Send + Sync {
|
||||
|
||||
/// See `cranelift_codegen::isa::TargetIsa::function_alignment`.
|
||||
fn function_alignment(&self) -> u32;
|
||||
|
||||
/// Generate a trampoline that can be used to call a wasm function from wasmtime.
|
||||
fn host_to_wasm_trampoline(&self, ty: &FuncType) -> Result<MachBufferFinalized<Final>>;
|
||||
}
|
||||
|
||||
impl Debug for &dyn TargetIsa {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
use crate::{
|
||||
isa::reg::Reg,
|
||||
masm::{DivKind, OperandSize, RemKind},
|
||||
masm::{CalleeKind, DivKind, OperandSize, RemKind},
|
||||
};
|
||||
use cranelift_codegen::{
|
||||
entity::EntityRef,
|
||||
@@ -469,17 +469,31 @@ impl Assembler {
|
||||
});
|
||||
}
|
||||
|
||||
/// Direct function call to a user defined function.
|
||||
pub fn call(&mut self, callee: u32) {
|
||||
let dest = ExternalName::user(UserExternalNameRef::new(callee as usize));
|
||||
self.emit(Inst::CallKnown {
|
||||
dest,
|
||||
info: Box::new(CallInfo {
|
||||
uses: smallvec![],
|
||||
defs: smallvec![],
|
||||
clobbers: Default::default(),
|
||||
opcode: Opcode::Call,
|
||||
}),
|
||||
});
|
||||
pub fn call(&mut self, callee: CalleeKind) {
|
||||
match callee {
|
||||
CalleeKind::Indirect(reg) => {
|
||||
self.emit(Inst::CallUnknown {
|
||||
dest: RegMem::reg(reg.into()),
|
||||
info: Box::new(CallInfo {
|
||||
uses: smallvec![],
|
||||
defs: smallvec![],
|
||||
clobbers: Default::default(),
|
||||
opcode: Opcode::Call,
|
||||
}),
|
||||
});
|
||||
}
|
||||
CalleeKind::Direct(index) => {
|
||||
let dest = ExternalName::user(UserExternalNameRef::new(index as usize));
|
||||
self.emit(Inst::CallKnown {
|
||||
dest,
|
||||
info: Box::new(CallInfo {
|
||||
uses: smallvec![],
|
||||
defs: smallvec![],
|
||||
clobbers: Default::default(),
|
||||
opcode: Opcode::Call,
|
||||
}),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,9 +3,9 @@ use super::{
|
||||
asm::{Assembler, Operand},
|
||||
regs::{self, rbp, rsp},
|
||||
};
|
||||
use crate::isa::reg::Reg;
|
||||
use crate::masm::{DivKind, MacroAssembler as Masm, OperandSize, RegImm, RemKind};
|
||||
use crate::{abi::LocalSlot, codegen::CodeGenContext, stack::Val};
|
||||
use crate::{isa::reg::Reg, masm::CalleeKind};
|
||||
use cranelift_codegen::{isa::x64::settings as x64_settings, settings, Final, MachBufferFinalized};
|
||||
|
||||
/// x64 MacroAssembler.
|
||||
@@ -114,7 +114,7 @@ impl Masm for MacroAssembler {
|
||||
self.decrement_sp(8);
|
||||
}
|
||||
|
||||
fn call(&mut self, callee: u32) {
|
||||
fn call(&mut self, callee: CalleeKind) {
|
||||
self.asm.call(callee);
|
||||
}
|
||||
|
||||
@@ -124,7 +124,7 @@ impl Masm for MacroAssembler {
|
||||
self.asm.mov(src, dst, size);
|
||||
}
|
||||
|
||||
fn sp_offset(&mut self) -> u32 {
|
||||
fn sp_offset(&self) -> u32 {
|
||||
self.sp_offset
|
||||
}
|
||||
|
||||
@@ -236,6 +236,10 @@ impl Masm for MacroAssembler {
|
||||
fn finalize(self) -> MachBufferFinalized<Final> {
|
||||
self.asm.finalize()
|
||||
}
|
||||
|
||||
fn address_from_reg(&self, reg: Reg, offset: u32) -> Self::Address {
|
||||
Address::offset(reg, offset)
|
||||
}
|
||||
}
|
||||
|
||||
impl MacroAssembler {
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
use crate::abi::ABI;
|
||||
use crate::codegen::{CodeGen, CodeGenContext};
|
||||
use crate::frame::Frame;
|
||||
use crate::{
|
||||
abi::ABI,
|
||||
codegen::{CodeGen, CodeGenContext},
|
||||
};
|
||||
|
||||
use crate::frame::{DefinedLocals, Frame};
|
||||
use crate::isa::x64::masm::MacroAssembler as X64Masm;
|
||||
use crate::masm::MacroAssembler;
|
||||
use crate::regalloc::RegAlloc;
|
||||
use crate::stack::Stack;
|
||||
use crate::trampoline::Trampoline;
|
||||
use crate::FuncEnv;
|
||||
use crate::{
|
||||
isa::{Builder, TargetIsa},
|
||||
@@ -87,14 +91,16 @@ impl TargetIsa for X64 {
|
||||
sig: &FuncType,
|
||||
body: &FunctionBody,
|
||||
env: &dyn FuncEnv,
|
||||
mut validator: FuncValidator<ValidatorResources>,
|
||||
validator: &mut FuncValidator<ValidatorResources>,
|
||||
) -> Result<MachBufferFinalized<Final>> {
|
||||
let mut body = body.get_binary_reader();
|
||||
let mut masm = X64Masm::new(self.shared_flags.clone(), self.isa_flags.clone());
|
||||
let stack = Stack::new();
|
||||
let abi = abi::X64ABI::default();
|
||||
let abi_sig = abi.sig(sig);
|
||||
let frame = Frame::new(&abi_sig, &mut body, &mut validator, &abi)?;
|
||||
|
||||
let defined_locals = DefinedLocals::new(&mut body, validator)?;
|
||||
let frame = Frame::new(&abi_sig, &defined_locals, &abi)?;
|
||||
// TODO Add in floating point bitmask
|
||||
let regalloc = RegAlloc::new(RegSet::new(ALL_GPR, 0), regs::scratch());
|
||||
let codegen_context = CodeGenContext::new(regalloc, stack, &frame);
|
||||
@@ -113,4 +119,15 @@ impl TargetIsa for X64 {
|
||||
// See `cranelift_codegen`'s value of this for more information.
|
||||
16
|
||||
}
|
||||
|
||||
fn host_to_wasm_trampoline(&self, ty: &FuncType) -> Result<MachBufferFinalized<Final>> {
|
||||
let abi = abi::X64ABI::default();
|
||||
let mut masm = X64Masm::new(self.shared_flags.clone(), self.isa_flags.clone());
|
||||
|
||||
let mut trampoline = Trampoline::new(&mut masm, &abi, regs::scratch(), regs::argv());
|
||||
|
||||
trampoline.emit_host_to_wasm(ty);
|
||||
|
||||
Ok(masm.finalize())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,6 +81,18 @@ pub(crate) fn scratch() -> Reg {
|
||||
r11()
|
||||
}
|
||||
|
||||
/// This register is used as a scratch register, in the context of trampolines only,
|
||||
/// where we assume that callee-saved registers are given the correct handling
|
||||
/// according to the system ABI. r12 is chosen given that it's a callee-saved,
|
||||
/// non-argument register.
|
||||
///
|
||||
/// In the context of all other internal functions, this register is not excluded
|
||||
/// from register allocation, so no extra assumptions should be made regarding
|
||||
/// its availability.
|
||||
pub(crate) fn argv() -> Reg {
|
||||
r12()
|
||||
}
|
||||
|
||||
fn fpr(enc: u8) -> Reg {
|
||||
Reg::new(PReg::new(enc as usize, RegClass::Float))
|
||||
}
|
||||
|
||||
@@ -16,4 +16,5 @@ mod masm;
|
||||
mod regalloc;
|
||||
mod regset;
|
||||
mod stack;
|
||||
mod trampoline;
|
||||
mod visitor;
|
||||
|
||||
@@ -39,6 +39,13 @@ pub(crate) enum RegImm {
|
||||
Imm(i64),
|
||||
}
|
||||
|
||||
pub(crate) enum CalleeKind {
|
||||
/// A function call to a raw address.
|
||||
Indirect(Reg),
|
||||
/// A function call to a local function.
|
||||
Direct(u32),
|
||||
}
|
||||
|
||||
impl RegImm {
|
||||
/// Register constructor.
|
||||
pub fn reg(r: Reg) -> Self {
|
||||
@@ -102,11 +109,14 @@ pub(crate) trait MacroAssembler {
|
||||
/// current position of the stack pointer (e.g. [sp + offset].
|
||||
fn address_at_sp(&self, offset: u32) -> Self::Address;
|
||||
|
||||
/// Emit a function call to a locally defined function.
|
||||
fn call(&mut self, callee: u32);
|
||||
/// Construct an address that is relative to the given register.
|
||||
fn address_from_reg(&self, reg: Reg, offset: u32) -> Self::Address;
|
||||
|
||||
/// Emit a function call to either a local or external function.
|
||||
fn call(&mut self, callee: CalleeKind);
|
||||
|
||||
/// Get stack pointer offset.
|
||||
fn sp_offset(&mut self) -> u32;
|
||||
fn sp_offset(&self) -> u32;
|
||||
|
||||
/// Perform a stack store.
|
||||
fn store(&mut self, src: RegImm, dst: Self::Address, size: OperandSize);
|
||||
|
||||
176
winch/codegen/src/trampoline.rs
Normal file
176
winch/codegen/src/trampoline.rs
Normal file
@@ -0,0 +1,176 @@
|
||||
use crate::{
|
||||
abi::{align_to, calculate_frame_adjustment, ABIArg, ABIResult, ABI},
|
||||
masm::{CalleeKind, MacroAssembler, OperandSize, RegImm},
|
||||
reg::Reg,
|
||||
};
|
||||
use std::mem;
|
||||
use wasmparser::{FuncType, ValType};
|
||||
|
||||
/// A trampoline to provide interopt between different calling conventions.
|
||||
pub(crate) struct Trampoline<'a, A, M>
|
||||
where
|
||||
A: ABI,
|
||||
M: MacroAssembler,
|
||||
{
|
||||
/// The macro assembler.
|
||||
masm: &'a mut M,
|
||||
/// The ABI.
|
||||
abi: &'a A,
|
||||
/// The main scratch register for the current architecture. It is not allocatable for the callee.
|
||||
scratch_reg: Reg,
|
||||
/// A second scratch register. This will be allocatable for the callee, so it can only be used
|
||||
/// after the callee-saved registers are on the stack.
|
||||
alloc_scratch_reg: Reg,
|
||||
}
|
||||
|
||||
impl<'a, A, M> Trampoline<'a, A, M>
|
||||
where
|
||||
A: ABI,
|
||||
M: MacroAssembler,
|
||||
{
|
||||
/// Create a new trampoline.
|
||||
pub fn new(masm: &'a mut M, abi: &'a A, scratch_reg: Reg, alloc_scratch_reg: Reg) -> Self {
|
||||
Self {
|
||||
masm,
|
||||
abi,
|
||||
scratch_reg,
|
||||
alloc_scratch_reg,
|
||||
}
|
||||
}
|
||||
|
||||
/// Emit the host to wasm trampoline.
|
||||
pub fn emit_host_to_wasm(&mut self, ty: &FuncType) {
|
||||
// The host to wasm trampoline is currently hard coded (see vmcontext.rs in the
|
||||
// wasmtime-runtime crate, VMTrampoline).
|
||||
// The first two parameters are VMContexts (not used at this time).
|
||||
// The third parameter is the function pointer to call.
|
||||
// The fourth parameter is an address to storage space for both the return value and the
|
||||
// arguments to the function.
|
||||
let trampoline_ty = FuncType::new(
|
||||
vec![ValType::I64, ValType::I64, ValType::I64, ValType::I64],
|
||||
vec![],
|
||||
);
|
||||
|
||||
// TODO: We should be passing a calling convention here so the signature can determine the
|
||||
// correct location of arguments. When we fully support system ABIs, this will need to be
|
||||
// updated.
|
||||
let trampoline_sig = self.abi.sig(&trampoline_ty);
|
||||
|
||||
// Hard-coding the size in bytes of the trampoline arguments since it's static, based on
|
||||
// the current signature we should always have 4 arguments, each of which is 8 bytes.
|
||||
let trampoline_arg_size = 32;
|
||||
|
||||
let callee_sig = self.abi.sig(ty);
|
||||
|
||||
let val_ptr = if let ABIArg::Reg { reg, ty: _ty } = &trampoline_sig.params[3] {
|
||||
Ok(RegImm::reg(*reg))
|
||||
} else {
|
||||
Err(anyhow::anyhow!("Expected the val ptr to be in a register"))
|
||||
}
|
||||
.unwrap();
|
||||
|
||||
self.masm.prologue();
|
||||
|
||||
// TODO: When we include support for passing calling conventions, we need to update this to
|
||||
// adhere to the system ABI. Currently, callee-saved registers are not preserved while we
|
||||
// are building this out.
|
||||
|
||||
let mut trampoline_arg_offsets: [u32; 4] = [0; 4];
|
||||
|
||||
trampoline_sig
|
||||
.params
|
||||
.iter()
|
||||
.enumerate()
|
||||
.for_each(|(i, param)| {
|
||||
if let ABIArg::Reg { reg, ty: _ty } = param {
|
||||
let offset = self.masm.push(*reg);
|
||||
trampoline_arg_offsets[i] = offset;
|
||||
}
|
||||
});
|
||||
|
||||
let val_ptr_offset = trampoline_arg_offsets[3];
|
||||
let func_ptr_offset = trampoline_arg_offsets[2];
|
||||
|
||||
self.masm.mov(
|
||||
val_ptr,
|
||||
RegImm::reg(self.scratch_reg),
|
||||
crate::masm::OperandSize::S64,
|
||||
);
|
||||
|
||||
// How much we need to adjust the stack pointer by to account for the alignment
|
||||
// required by the ISA.
|
||||
let delta = calculate_frame_adjustment(
|
||||
self.masm.sp_offset(),
|
||||
self.abi.arg_base_offset() as u32,
|
||||
self.abi.call_stack_align() as u32,
|
||||
);
|
||||
|
||||
// The total amount of stack space we need to reserve for the arguments.
|
||||
let total_arg_stack_space = align_to(
|
||||
callee_sig.stack_bytes + delta,
|
||||
self.abi.call_stack_align() as u32,
|
||||
);
|
||||
|
||||
self.masm.reserve_stack(total_arg_stack_space);
|
||||
|
||||
// The max size a value can be when reading from the params memory location.
|
||||
let value_size = mem::size_of::<u128>();
|
||||
|
||||
callee_sig.params.iter().enumerate().for_each(|(i, param)| {
|
||||
let value_offset = (i * value_size) as u32;
|
||||
|
||||
match param {
|
||||
ABIArg::Reg { reg, ty } => self.masm.load(
|
||||
self.masm.address_from_reg(self.scratch_reg, value_offset),
|
||||
*reg,
|
||||
(*ty).into(),
|
||||
),
|
||||
ABIArg::Stack { offset, ty } => {
|
||||
self.masm.load(
|
||||
self.masm.address_from_reg(self.scratch_reg, value_offset),
|
||||
self.alloc_scratch_reg,
|
||||
(*ty).into(),
|
||||
);
|
||||
self.masm.store(
|
||||
RegImm::reg(self.alloc_scratch_reg),
|
||||
self.masm.address_at_sp(*offset),
|
||||
(*ty).into(),
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Move the function pointer from it's stack location into a scratch register.
|
||||
self.masm.load(
|
||||
self.masm.address_from_sp(func_ptr_offset),
|
||||
self.scratch_reg,
|
||||
OperandSize::S64,
|
||||
);
|
||||
|
||||
// Call the function that was passed into the trampoline.
|
||||
self.masm.call(CalleeKind::Indirect(self.scratch_reg));
|
||||
|
||||
self.masm.free_stack(total_arg_stack_space);
|
||||
|
||||
// Move the val ptr back into the scratch register so we can load the return values.
|
||||
self.masm.load(
|
||||
self.masm.address_from_sp(val_ptr_offset),
|
||||
self.scratch_reg,
|
||||
OperandSize::S64,
|
||||
);
|
||||
|
||||
// Move the return values into the value ptr.
|
||||
// We are only support a single return value at this time.
|
||||
let ABIResult::Reg { reg, ty } = &callee_sig.result;
|
||||
self.masm.store(
|
||||
RegImm::reg(*reg),
|
||||
self.masm.address_from_reg(self.scratch_reg, 0),
|
||||
(*ty).unwrap().into(),
|
||||
);
|
||||
|
||||
// TODO: Once we support system ABIs better, callee-saved registers will need to be
|
||||
// restored here.
|
||||
|
||||
self.masm.epilogue(trampoline_arg_size);
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,7 @@ pub struct FuncEnv<'a> {
|
||||
/// Type information about a module, once it has been validated.
|
||||
pub types: &'a Types,
|
||||
/// The current ISA.
|
||||
pub isa: &'a dyn TargetIsa,
|
||||
pub isa: &'a Box<dyn TargetIsa>,
|
||||
}
|
||||
|
||||
impl<'a> winch_codegen::FuncEnv for FuncEnv<'a> {
|
||||
@@ -35,7 +35,7 @@ impl<'a> winch_codegen::FuncEnv for FuncEnv<'a> {
|
||||
|
||||
impl<'a> FuncEnv<'a> {
|
||||
/// Create a new function environment.
|
||||
pub fn new(module: &'a Module, types: &'a Types, isa: &'a dyn TargetIsa) -> Self {
|
||||
pub fn new(module: &'a Module, types: &'a Types, isa: &'a Box<dyn TargetIsa>) -> Self {
|
||||
Self { module, types, isa }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ use target_lexicon::Architecture;
|
||||
use winch_codegen::TargetIsa;
|
||||
|
||||
/// Disassemble and print a machine code buffer.
|
||||
pub fn disasm(bytes: &[u8], isa: &dyn TargetIsa) -> Result<Vec<String>> {
|
||||
pub fn disasm(bytes: &[u8], isa: &Box<dyn TargetIsa>) -> Result<Vec<String>> {
|
||||
let dis = disassembler_for(isa)?;
|
||||
let insts = dis.disasm_all(bytes, 0x0).unwrap();
|
||||
|
||||
@@ -44,7 +44,7 @@ pub fn disasm(bytes: &[u8], isa: &dyn TargetIsa) -> Result<Vec<String>> {
|
||||
Ok(disassembled_lines)
|
||||
}
|
||||
|
||||
fn disassembler_for(isa: &dyn TargetIsa) -> Result<Capstone> {
|
||||
fn disassembler_for(isa: &Box<dyn TargetIsa>) -> Result<Capstone> {
|
||||
let disasm = match isa.triple().architecture {
|
||||
Architecture::X86_64 => Capstone::new()
|
||||
.x86()
|
||||
|
||||
@@ -109,7 +109,7 @@ mod test {
|
||||
let body_inputs = std::mem::take(&mut translation.function_body_inputs);
|
||||
let module = &translation.module;
|
||||
let types = translation.get_types();
|
||||
let env = FuncEnv::new(module, &types, &*isa);
|
||||
let env = FuncEnv::new(module, &types, &isa);
|
||||
|
||||
let binding = body_inputs
|
||||
.into_iter()
|
||||
@@ -151,11 +151,11 @@ mod test {
|
||||
.function_at(index.as_u32())
|
||||
.expect(&format!("function type at index {:?}", index.as_u32()));
|
||||
let FunctionBodyData { body, validator } = f.1;
|
||||
let validator = validator.into_validator(Default::default());
|
||||
let mut validator = validator.into_validator(Default::default());
|
||||
|
||||
let buffer = env
|
||||
.isa
|
||||
.compile_function(&sig, &body, env, validator)
|
||||
.compile_function(&sig, &body, env, &mut validator)
|
||||
.expect("Couldn't compile function");
|
||||
|
||||
disasm(buffer.data(), env.isa).unwrap()
|
||||
|
||||
@@ -41,7 +41,7 @@ pub fn run(opt: &Options) -> Result<()> {
|
||||
let body_inputs = std::mem::take(&mut translation.function_body_inputs);
|
||||
let module = &translation.module;
|
||||
let types = translation.get_types();
|
||||
let env = FuncEnv::new(module, &types, &*isa);
|
||||
let env = FuncEnv::new(module, &types, &isa);
|
||||
|
||||
body_inputs
|
||||
.into_iter()
|
||||
@@ -57,10 +57,10 @@ fn compile(env: &FuncEnv, f: (DefinedFuncIndex, FunctionBodyData<'_>)) -> Result
|
||||
.function_at(index.as_u32())
|
||||
.expect(&format!("function type at index {:?}", index.as_u32()));
|
||||
let FunctionBodyData { body, validator } = f.1;
|
||||
let validator = validator.into_validator(Default::default());
|
||||
let mut validator = validator.into_validator(Default::default());
|
||||
let buffer = env
|
||||
.isa
|
||||
.compile_function(&sig, &body, env, validator)
|
||||
.compile_function(&sig, &body, env, &mut validator)
|
||||
.expect("Couldn't compile function");
|
||||
|
||||
println!("Disassembly for function: {}", index.as_u32());
|
||||
@@ -68,5 +68,15 @@ fn compile(env: &FuncEnv, f: (DefinedFuncIndex, FunctionBodyData<'_>)) -> Result
|
||||
.iter()
|
||||
.for_each(|s| println!("{}", s));
|
||||
|
||||
let buffer = env
|
||||
.isa
|
||||
.host_to_wasm_trampoline(sig)
|
||||
.expect("Couldn't compile trampoline");
|
||||
|
||||
println!("Disassembly for trampoline: {}", index.as_u32());
|
||||
disasm(buffer.data(), env.isa)?
|
||||
.iter()
|
||||
.for_each(|s| println!("{}", s));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user