From cf83d29d8cdb403145a7dea1f7754e4e94ab32ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20Leegwater=20Sim=C3=B5es?= Date: Mon, 13 May 2024 20:53:18 +0200 Subject: [PATCH 1/2] Allow configuring fuel cost per operator --- Cargo.lock | 1 + crates/cranelift/src/func_environ.rs | 18 +---- crates/environ/Cargo.toml | 1 + crates/environ/src/tunables.rs | 80 +++++++++++++++++++++ crates/wasmtime/src/config.rs | 11 +++ crates/wasmtime/src/engine/serialization.rs | 31 +++++++- 6 files changed, 124 insertions(+), 18 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bb57e91c1d2d..09e9aaed82c7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3659,6 +3659,7 @@ dependencies = [ "indexmap 2.0.0", "log", "object 0.33.0", + "paste", "rustc-demangle", "serde", "serde_derive", diff --git a/crates/cranelift/src/func_environ.rs b/crates/cranelift/src/func_environ.rs index 5e02e721c562..64843589e24f 100644 --- a/crates/cranelift/src/func_environ.rs +++ b/crates/cranelift/src/func_environ.rs @@ -318,23 +318,7 @@ impl<'module_environment> FuncEnvironment<'module_environment> { return; } - self.fuel_consumed += match op { - // Nop and drop generate no code, so don't consume fuel for them. - Operator::Nop | Operator::Drop => 0, - - // Control flow may create branches, but is generally cheap and - // free, so don't consume fuel. Note the lack of `if` since some - // cost is incurred with the conditional check. - Operator::Block { .. } - | Operator::Loop { .. } - | Operator::Unreachable - | Operator::Return - | Operator::Else - | Operator::End => 0, - - // everything else, just call it one operation. - _ => 1, - }; + self.fuel_consumed += self.tunables.operator_cost.cost(op); match op { // Exiting a function (via a return or unreachable) or otherwise diff --git a/crates/environ/Cargo.toml b/crates/environ/Cargo.toml index c0a78b943593..7eeef1a43ec7 100644 --- a/crates/environ/Cargo.toml +++ b/crates/environ/Cargo.toml @@ -21,6 +21,7 @@ cranelift-entity = { workspace = true } wasmtime-types = { workspace = true } wasmparser = { workspace = true } indexmap = { workspace = true, features = ["serde"] } +paste = "1.0.3" thiserror = { workspace = true } serde = { workspace = true } serde_derive = { workspace = true } diff --git a/crates/environ/src/tunables.rs b/crates/environ/src/tunables.rs index fc932212f493..19cee0c19970 100644 --- a/crates/environ/src/tunables.rs +++ b/crates/environ/src/tunables.rs @@ -1,6 +1,8 @@ use anyhow::{anyhow, bail, Result}; +use paste::paste; use serde_derive::{Deserialize, Serialize}; use target_lexicon::{PointerWidth, Triple}; +use wasmparser::Operator; /// Tunable parameters for WebAssembly compilation. #[derive(Clone, Hash, Serialize, Deserialize, Debug)] @@ -31,6 +33,9 @@ pub struct Tunables { /// will be consumed every time a wasm instruction is executed. pub consume_fuel: bool, + /// The cost of each operator. If fuel is not enabled, this is ignored. + pub operator_cost: OperatorCost, + /// Whether or not we use epoch-based interruption. pub epoch_interruption: bool, @@ -102,6 +107,7 @@ impl Tunables { generate_native_debuginfo: false, parse_wasm_debuginfo: true, consume_fuel: false, + operator_cost: OperatorCost::default(), epoch_interruption: false, static_memory_bound_is_maximum: false, guard_before_linear_memory: true, @@ -156,3 +162,77 @@ impl Tunables { } } } + +macro_rules! default_cost { + // Nop and drop generate no code, so don't consume fuel for them. + (Nop) => { + 0 + }; + (Drop) => { + 0 + }; + + // Control flow may create branches, but is generally cheap and + // free, so don't consume fuel. + (Block) => { + 0 + }; + (Loop) => { + 0 + }; + (Unreachable) => { + 0 + }; + (Return) => { + 0 + }; + (Else) => { + 0 + }; + (End) => { + 0 + }; + + // Everything else, just call it one operation. + ($op:ident) => { + 1 + }; +} + +macro_rules! define_operator_cost { + ($( @$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident)*) => { + paste! { + /// The fuel cost of each operator. + #[derive(Clone, Copy, Hash, Serialize, Deserialize, Debug)] + #[allow(missing_docs, non_snake_case)] + pub struct OperatorCost { + $( + pub $op: i64, + )* + } + + impl OperatorCost { + /// Returns the cost of the given operator. + pub fn cost(&self, op: &Operator) -> i64 { + match op { + $( + Operator::$op $({ $($arg: [<_ $arg>]),* })? => self.$op, + )* + } + } + } + + impl Default for OperatorCost { + fn default() -> Self { + Self { + $( + $op: default_cost!($op), + )* + } + } + } + } + } +} + +wasmparser::for_each_operator!(define_operator_cost); diff --git a/crates/wasmtime/src/config.rs b/crates/wasmtime/src/config.rs index 2070c1c0c795..27dbc6020f3e 100644 --- a/crates/wasmtime/src/config.rs +++ b/crates/wasmtime/src/config.rs @@ -10,6 +10,7 @@ use target_lexicon::Architecture; use wasmparser::WasmFeatures; #[cfg(feature = "cache")] use wasmtime_cache::CacheConfig; +pub use wasmtime_environ::OperatorCost; use wasmtime_environ::Tunables; #[cfg(feature = "runtime")] @@ -144,6 +145,7 @@ struct ConfigTunables { generate_native_debuginfo: Option, parse_wasm_debuginfo: Option, consume_fuel: Option, + operator_cost: Option, epoch_interruption: Option, static_memory_bound_is_maximum: Option, guard_before_linear_memory: Option, @@ -522,6 +524,14 @@ impl Config { self } + /// Configures the cost of each operator in WebAssembly, in "fuel units". + /// + /// This is only relevant when [`Config::consume_fuel`] is enabled. + pub fn operator_cost(&mut self, cost: OperatorCost) -> &mut Self { + self.tunables.operator_cost = Some(cost); + self + } + /// Enables epoch-based interruption. /// /// When executing code in async mode, we sometimes want to @@ -1725,6 +1735,7 @@ impl Config { generate_native_debuginfo parse_wasm_debuginfo consume_fuel + operator_cost epoch_interruption static_memory_bound_is_maximum guard_before_linear_memory diff --git a/crates/wasmtime/src/engine/serialization.rs b/crates/wasmtime/src/engine/serialization.rs index 07855c608afd..2c4845a98542 100644 --- a/crates/wasmtime/src/engine/serialization.rs +++ b/crates/wasmtime/src/engine/serialization.rs @@ -29,7 +29,7 @@ use object::{File, FileFlags, Object as _, ObjectSection, SectionKind}; use serde_derive::{Deserialize, Serialize}; use std::str::FromStr; use wasmtime_environ::obj; -use wasmtime_environ::{FlagValue, ObjectKind, Tunables}; +use wasmtime_environ::{FlagValue, ObjectKind, OperatorCost, Tunables}; const VERSION: u8 = 0; @@ -330,6 +330,31 @@ impl Metadata<'_> { ); } + fn check_cost(consume_fuel: bool, found: &OperatorCost, expected: &OperatorCost) -> Result<()> { + if !consume_fuel { + return Ok(()); + } + + macro_rules! bail_when_different { + ($( @$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident)*) => { + $( + if found.$op != expected.$op { + bail!( + "Module was compiled with a cost of {} for opcode {}, but {} was found and fuel is enabled", + found.$op, + stringify!($op), + expected.$op, + ); + } + )* + }; + } + + wasmparser::for_each_operator!(bail_when_different); + + Ok(()) + } + fn check_tunables(&mut self, other: &Tunables) -> Result<()> { let Tunables { static_memory_bound, @@ -338,6 +363,7 @@ impl Metadata<'_> { generate_native_debuginfo, parse_wasm_debuginfo, consume_fuel, + ref operator_cost, epoch_interruption, static_memory_bound_is_maximum, guard_before_linear_memory, @@ -383,7 +409,10 @@ impl Metadata<'_> { other.parse_wasm_debuginfo, "WebAssembly backtrace support", )?; + Self::check_bool(consume_fuel, other.consume_fuel, "fuel support")?; + Self::check_cost(other.consume_fuel, operator_cost, &other.operator_cost)?; + Self::check_bool( epoch_interruption, other.epoch_interruption, From ba21a5b0827c38b48d2ab9461c800edba95a7dd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20Leegwater=20Sim=C3=B5es?= Date: Mon, 13 May 2024 21:00:35 +0200 Subject: [PATCH 2/2] Rename more crates to `dusk-*` --- Cargo.lock | 120 ++++++++++++++++++------------------ Cargo.toml | 4 +- crates/cranelift/Cargo.toml | 2 +- crates/environ/Cargo.toml | 2 +- 4 files changed, 64 insertions(+), 64 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 09e9aaed82c7..0b8aa68d8630 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1035,6 +1035,8 @@ dependencies = [ "bincode", "bumpalo", "cfg-if", + "dusk-wasmtime-cranelift", + "dusk-wasmtime-environ", "encoding_rs", "fxprof-processed-profile", "gimli", @@ -1059,8 +1061,6 @@ dependencies = [ "wasmtime-cache", "wasmtime-component-macro", "wasmtime-component-util", - "wasmtime-cranelift", - "wasmtime-environ", "wasmtime-fiber", "wasmtime-jit-debug", "wasmtime-jit-icache-coherence", @@ -1071,6 +1071,56 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "dusk-wasmtime-cranelift" +version = "20.0.0" +dependencies = [ + "anyhow", + "cfg-if", + "cranelift-codegen", + "cranelift-control", + "cranelift-entity", + "cranelift-frontend", + "cranelift-native", + "cranelift-wasm", + "dusk-wasmtime-environ", + "gimli", + "log", + "object 0.33.0", + "target-lexicon", + "thiserror", + "wasmparser 0.202.0", + "wasmtime-versioned-export-macros", +] + +[[package]] +name = "dusk-wasmtime-environ" +version = "20.0.0" +dependencies = [ + "anyhow", + "bincode", + "clap", + "cpp_demangle", + "cranelift-entity", + "env_logger", + "gimli", + "indexmap 2.0.0", + "log", + "object 0.33.0", + "paste", + "rustc-demangle", + "serde", + "serde_derive", + "target-lexicon", + "thiserror", + "wasm-encoder 0.202.0", + "wasmparser 0.202.0", + "wasmprinter", + "wasmtime-component-util", + "wasmtime-types", + "wat", +] + [[package]] name = "egg" version = "0.6.0" @@ -3543,6 +3593,8 @@ dependencies = [ "cranelift-reader", "criterion", "dusk-wasmtime", + "dusk-wasmtime-cranelift", + "dusk-wasmtime-environ", "env_logger", "filecheck", "http", @@ -3575,8 +3627,6 @@ dependencies = [ "wasmtime-cache", "wasmtime-cli-flags", "wasmtime-component-util", - "wasmtime-cranelift", - "wasmtime-environ", "wasmtime-explorer", "wasmtime-runtime", "wasmtime-wasi", @@ -3623,67 +3673,17 @@ dependencies = [ name = "wasmtime-component-util" version = "20.0.0" -[[package]] -name = "wasmtime-cranelift" -version = "20.0.0" -dependencies = [ - "anyhow", - "cfg-if", - "cranelift-codegen", - "cranelift-control", - "cranelift-entity", - "cranelift-frontend", - "cranelift-native", - "cranelift-wasm", - "gimli", - "log", - "object 0.33.0", - "target-lexicon", - "thiserror", - "wasmparser 0.202.0", - "wasmtime-environ", - "wasmtime-versioned-export-macros", -] - -[[package]] -name = "wasmtime-environ" -version = "20.0.0" -dependencies = [ - "anyhow", - "bincode", - "clap", - "cpp_demangle", - "cranelift-entity", - "env_logger", - "gimli", - "indexmap 2.0.0", - "log", - "object 0.33.0", - "paste", - "rustc-demangle", - "serde", - "serde_derive", - "target-lexicon", - "thiserror", - "wasm-encoder 0.202.0", - "wasmparser 0.202.0", - "wasmprinter", - "wasmtime-component-util", - "wasmtime-types", - "wat", -] - [[package]] name = "wasmtime-environ-fuzz" version = "0.0.0" dependencies = [ "arbitrary", "component-fuzz-util", + "dusk-wasmtime-environ", "env_logger", "libfuzzer-sys", "wasmparser 0.202.0", "wasmprinter", - "wasmtime-environ", "wat", ] @@ -3796,6 +3796,7 @@ dependencies = [ "anyhow", "cc", "cfg-if", + "dusk-wasmtime-environ", "encoding_rs", "indexmap 2.0.0", "libc", @@ -3812,7 +3813,6 @@ dependencies = [ "sptr", "wasm-encoder 0.202.0", "wasmtime-asm-macros", - "wasmtime-environ", "wasmtime-fiber", "wasmtime-jit-debug", "wasmtime-slab", @@ -3949,12 +3949,12 @@ version = "20.0.0" dependencies = [ "anyhow", "cranelift-codegen", + "dusk-wasmtime-cranelift", + "dusk-wasmtime-environ", "gimli", "object 0.33.0", "target-lexicon", "wasmparser 0.202.0", - "wasmtime-cranelift", - "wasmtime-environ", "winch-codegen", ] @@ -4126,13 +4126,13 @@ version = "0.18.0" dependencies = [ "anyhow", "cranelift-codegen", + "dusk-wasmtime-cranelift", + "dusk-wasmtime-environ", "gimli", "regalloc2", "smallvec", "target-lexicon", "wasmparser 0.202.0", - "wasmtime-cranelift", - "wasmtime-environ", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index a349cb33ffaa..51be988dad57 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -163,9 +163,9 @@ wasmtime = { path = "crates/wasmtime", package = "dusk-wasmtime", version = "20. wasmtime-c-api-macros = { path = "crates/c-api-macros", version = "=20.0.0" } wasmtime-cache = { path = "crates/cache", version = "=20.0.0" } wasmtime-cli-flags = { path = "crates/cli-flags", version = "=20.0.0" } -wasmtime-cranelift = { path = "crates/cranelift", version = "=20.0.0" } +wasmtime-cranelift = { path = "crates/cranelift", package = "dusk-wasmtime-cranelift", version = "=20.0.0" } wasmtime-winch = { path = "crates/winch", version = "=20.0.0" } -wasmtime-environ = { path = "crates/environ", version = "=20.0.0" } +wasmtime-environ = { path = "crates/environ", package = "dusk-wasmtime-environ", version = "=20.0.0" } wasmtime-explorer = { path = "crates/explorer", version = "=20.0.0" } wasmtime-fiber = { path = "crates/fiber", version = "=20.0.0" } wasmtime-types = { path = "crates/types", version = "20.0.0" } diff --git a/crates/cranelift/Cargo.toml b/crates/cranelift/Cargo.toml index 2d3eac7c8bff..3af7d3c87cda 100644 --- a/crates/cranelift/Cargo.toml +++ b/crates/cranelift/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "wasmtime-cranelift" +name = "dusk-wasmtime-cranelift" version.workspace = true authors.workspace = true description = "Integration between Cranelift and Wasmtime" diff --git a/crates/environ/Cargo.toml b/crates/environ/Cargo.toml index 7eeef1a43ec7..bd11afa38422 100644 --- a/crates/environ/Cargo.toml +++ b/crates/environ/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "wasmtime-environ" +name = "dusk-wasmtime-environ" version.workspace = true authors.workspace = true description = "Standalone environment support for WebAsssembly code in Cranelift"