Skip to content

Commit 2f684ba

Browse files
authored
winch: Implement Fuel-Based Interruption (#9472)
* winch: Implement Fuel-Based Interruption Closes: #8090 This commit introduces the initial implementation of fuel-based interruption in Winch. To maintain consistency with existing fuel semantics, this implementation closely follows the Wasmtime/Cranelift approach, with the following exception: * Local Fuel Cache: Given Winch's emphasis on compilation speed, this implementation does not optimize for minimizing loads and stores. As a result, checking and incrementing fuel currently requires explicit loads and stores. Future optimizations may be considered to improve this aspect. This commit also includes a small refactoring in the visitor, which introduces more generic "visitor hook" which enable handling the invariants that need to happen before and after emitting machine code for each Wasm operator. * Use the vmruntime limits directly * Remove unsupported fuel warning
1 parent 6fdff5c commit 2f684ba

File tree

9 files changed

+266
-97
lines changed

9 files changed

+266
-97
lines changed

crates/winch/src/builder.rs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -69,10 +69,6 @@ impl CompilerBuilder for Builder {
6969
bail!("Winch does not currently support epoch based interruption");
7070
}
7171

72-
if tunables.consume_fuel {
73-
bail!("Winch does not currently support fuel based interruption");
74-
}
75-
7672
if tunables.generate_native_debuginfo {
7773
bail!("Winch does not currently support generating native debug information");
7874
}

crates/winch/src/compiler.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ impl wasmtime_environ::Compiler for Compiler {
109109
types,
110110
&mut context.builtins,
111111
&mut validator,
112+
&self.tunables,
112113
)
113114
.map_err(|e| CompileError::Codegen(format!("{e:?}")));
114115
self.save_context(context, validator.into_allocations());

tests/all/fuel.rs

Lines changed: 27 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,15 @@ impl<'a> Parse<'a> for FuelWast<'a> {
2424
}
2525
}
2626

27-
#[test]
27+
#[wasmtime_test]
2828
#[cfg_attr(miri, ignore)]
29-
fn run() -> Result<()> {
29+
fn run(config: &mut Config) -> Result<()> {
30+
config.consume_fuel(true);
3031
let test = std::fs::read_to_string("tests/all/fuel.wast")?;
3132
let buf = ParseBuffer::new(&test)?;
3233
let mut wast = parser::parse::<FuelWast<'_>>(&buf)?;
3334
for (span, fuel, module) in wast.assertions.iter_mut() {
34-
let consumed = fuel_consumed(&module.encode()?);
35+
let consumed = fuel_consumed(&config, &module.encode()?);
3536
if consumed == *fuel {
3637
continue;
3738
}
@@ -47,9 +48,7 @@ fn run() -> Result<()> {
4748
Ok(())
4849
}
4950

50-
fn fuel_consumed(wasm: &[u8]) -> u64 {
51-
let mut config = Config::new();
52-
config.consume_fuel(true);
51+
fn fuel_consumed(config: &Config, wasm: &[u8]) -> u64 {
5352
let engine = Engine::new(&config).unwrap();
5453
let module = Module::new(&engine, wasm).unwrap();
5554
let mut store = Store::new(&engine, ());
@@ -58,10 +57,12 @@ fn fuel_consumed(wasm: &[u8]) -> u64 {
5857
u64::MAX - store.get_fuel().unwrap()
5958
}
6059

61-
#[test]
60+
#[wasmtime_test]
6261
#[cfg_attr(miri, ignore)]
63-
fn iloop() {
62+
fn iloop(config: &mut Config) -> Result<()> {
63+
config.consume_fuel(true);
6464
iloop_aborts(
65+
&config,
6566
r#"
6667
(module
6768
(start 0)
@@ -70,6 +71,7 @@ fn iloop() {
7071
"#,
7172
);
7273
iloop_aborts(
74+
&config,
7375
r#"
7476
(module
7577
(start 0)
@@ -78,6 +80,7 @@ fn iloop() {
7880
"#,
7981
);
8082
iloop_aborts(
83+
&config,
8184
r#"
8285
(module
8386
(start 0)
@@ -86,6 +89,7 @@ fn iloop() {
8689
"#,
8790
);
8891
iloop_aborts(
92+
&config,
8993
r#"
9094
(module
9195
(start 0)
@@ -110,21 +114,20 @@ fn iloop() {
110114
"#,
111115
);
112116

113-
fn iloop_aborts(wat: &str) {
114-
let mut config = Config::new();
115-
config.consume_fuel(true);
117+
fn iloop_aborts(config: &Config, wat: &str) {
116118
let engine = Engine::new(&config).unwrap();
117119
let module = Module::new(&engine, wat).unwrap();
118120
let mut store = Store::new(&engine, ());
119121
store.set_fuel(10_000).unwrap();
120122
let error = Instance::new(&mut store, &module, &[]).err().unwrap();
121123
assert_eq!(error.downcast::<Trap>().unwrap(), Trap::OutOfFuel);
122124
}
125+
126+
Ok(())
123127
}
124128

125-
#[test]
126-
fn manual_fuel() {
127-
let mut config = Config::new();
129+
#[wasmtime_test]
130+
fn manual_fuel(config: &mut Config) {
128131
config.consume_fuel(true);
129132
let engine = Engine::new(&config).unwrap();
130133
let mut store = Store::new(&engine, ());
@@ -134,11 +137,10 @@ fn manual_fuel() {
134137
assert_eq!(store.get_fuel().ok(), Some(1));
135138
}
136139

137-
#[test]
140+
#[wasmtime_test]
138141
#[cfg_attr(miri, ignore)]
139-
fn host_function_consumes_all() {
142+
fn host_function_consumes_all(config: &mut Config) {
140143
const FUEL: u64 = 10_000;
141-
let mut config = Config::new();
142144
config.consume_fuel(true);
143145
let engine = Engine::new(&config).unwrap();
144146
let module = Module::new(
@@ -167,20 +169,18 @@ fn host_function_consumes_all() {
167169
assert_eq!(trap.downcast::<Trap>().unwrap(), Trap::OutOfFuel);
168170
}
169171

170-
#[test]
171-
fn manual_edge_cases() {
172-
let mut config = Config::new();
172+
#[wasmtime_test]
173+
fn manual_edge_cases(config: &mut Config) {
173174
config.consume_fuel(true);
174175
let engine = Engine::new(&config).unwrap();
175176
let mut store = Store::new(&engine, ());
176177
store.set_fuel(u64::MAX).unwrap();
177178
assert_eq!(store.get_fuel().unwrap(), u64::MAX);
178179
}
179180

180-
#[test]
181+
#[wasmtime_test]
181182
#[cfg_attr(miri, ignore)]
182-
fn unconditionally_trapping_memory_accesses_save_fuel_before_trapping() {
183-
let mut config = Config::new();
183+
fn unconditionally_trapping_memory_accesses_save_fuel_before_trapping(config: &mut Config) {
184184
config.consume_fuel(true);
185185
config.static_memory_maximum_size(0x1_0000);
186186

@@ -221,10 +221,11 @@ fn unconditionally_trapping_memory_accesses_save_fuel_before_trapping() {
221221
assert!(consumed_fuel > 0);
222222
}
223223

224-
#[test]
224+
#[wasmtime_test]
225225
#[cfg_attr(miri, ignore)]
226-
fn get_fuel_clamps_at_zero() -> Result<()> {
227-
let engine = Engine::new(Config::new().consume_fuel(true))?;
226+
fn get_fuel_clamps_at_zero(config: &mut Config) -> Result<()> {
227+
config.consume_fuel(true);
228+
let engine = Engine::new(config)?;
228229
let mut store = Store::new(&engine, ());
229230
let module = Module::new(
230231
&engine,
@@ -257,21 +258,3 @@ fn get_fuel_clamps_at_zero() -> Result<()> {
257258

258259
Ok(())
259260
}
260-
261-
#[wasmtime_test(strategies(not(Cranelift)))]
262-
#[cfg_attr(miri, ignore)]
263-
fn ensure_compatibility_between_winch_and_fuel(config: &mut Config) -> Result<()> {
264-
config.consume_fuel(true);
265-
let result = Engine::new(&config);
266-
match result {
267-
Ok(_) => anyhow::bail!("Expected incompatibility between fuel and Winch"),
268-
Err(e) => {
269-
assert_eq!(
270-
e.to_string(),
271-
"Winch does not currently support fuel based interruption"
272-
);
273-
}
274-
}
275-
276-
Ok(())
277-
}

winch/codegen/src/codegen/env.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ pub enum HeapStyle {
6262
#[derive(Debug, Copy, Clone)]
6363
pub struct HeapData {
6464
/// The offset to the base of the heap.
65-
/// Relative to the VMContext pointer if the WebAssembly memory is locally
65+
/// Relative to the `VMContext` pointer if the WebAssembly memory is locally
6666
/// defined. Else this is relative to the location of the imported WebAssembly
6767
/// memory location.
6868
pub offset: u32,

0 commit comments

Comments
 (0)