Skip to content

Commit 974c324

Browse files
authored
Simplify ABI selection and tail call integration (bytecodealliance#9061)
This commit simplifies the selection of an ABI for wasm functions now that all Cranelift backends implement tail calls. All wasm functions use the `Tail` calling convention except when the `winch_callable` tunable is enabled meaning that Winch-generated functions are being called. This then additionally simplifies the activation of the tail call proposal. It's not unconditionally active and the same across all compilers. The Winch compiler is updated to return an error for unsupported instructions rather than panicking so the embedder API is suitable for feeding unsupported modules to Winch. This means that tail calls in Winch behaves similarly to GC in Cranelift or other unsupported proposals like SIMD in Winch.
1 parent 4718f68 commit 974c324

File tree

9 files changed

+40
-84
lines changed

9 files changed

+40
-84
lines changed

crates/cranelift/src/lib.rs

Lines changed: 19 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -158,47 +158,26 @@ fn wasm_call_signature(
158158
wasm_func_ty: &WasmFuncType,
159159
tunables: &Tunables,
160160
) -> ir::Signature {
161-
// NB: this calling convention in the near future is expected to be
162-
// unconditionally switched to the "tail" calling convention once all
163-
// platforms have support for tail calls.
161+
// The default calling convention is `CallConv::Tail` to enable the use of
162+
// tail calls in modules when needed. Note that this is used even if the
163+
// tail call proposal is disabled in wasm. This is not interacted with on
164+
// the host so it's purely an internal detail of wasm itself.
164165
//
165-
// Also note that the calling convention for wasm functions is purely an
166-
// internal implementation detail of cranelift and Wasmtime. Native Rust
167-
// code does not interact with raw wasm functions and instead always
168-
// operates through trampolines either using the `array_call_signature` or
169-
// `native_call_signature` where the default platform ABI is used.
170-
let call_conv = match isa.triple().architecture {
171-
// If the tail calls proposal is enabled, we must use the tail calling
172-
// convention. We don't use it by default yet because of
173-
// https://github.com/bytecodealliance/wasmtime/issues/6759
174-
_ if tunables.tail_callable => {
175-
assert!(
176-
!tunables.winch_callable,
177-
"Winch doesn't support the WebAssembly tail call proposal",
178-
);
179-
180-
CallConv::Tail
181-
}
182-
183-
// The winch calling convention is only implemented for x64 and aarch64
184-
arch if tunables.winch_callable => {
185-
assert!(
186-
matches!(arch, Architecture::X86_64 | Architecture::Aarch64(_)),
187-
"The Winch calling convention is only implemented for x86_64 and aarch64"
188-
);
189-
CallConv::Winch
190-
}
191-
192-
// On s390x the "wasmtime" calling convention is used to give vectors
193-
// little-endian lane order at the ABI layer which should reduce the
194-
// need for conversion when operating on vector function arguments. By
195-
// default vectors on s390x are otherwise in big-endian lane order which
196-
// would require conversions.
197-
Architecture::S390x => CallConv::WasmtimeSystemV,
198-
199-
// All other platforms pick "fast" as the calling convention since it's
200-
// presumably, well, the fastest.
201-
_ => CallConv::Fast,
166+
// The Winch calling convention is used instead when generating trampolines
167+
// which call Winch-generated functions. The winch calling convention is
168+
// only implemented for x64 and aarch64, so assert that here and panic on
169+
// other architectures.
170+
let call_conv = if tunables.winch_callable {
171+
assert!(
172+
matches!(
173+
isa.triple().architecture,
174+
Architecture::X86_64 | Architecture::Aarch64(_)
175+
),
176+
"The Winch calling convention is only implemented for x86_64 and aarch64"
177+
);
178+
CallConv::Winch
179+
} else {
180+
CallConv::Tail
202181
};
203182
let mut sig = blank_sig(isa, call_conv);
204183
let cvt = |ty: &WasmValType| ir::AbiParam::new(value_type(isa, *ty));

crates/environ/src/tunables.rs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,6 @@ pub struct Tunables {
6060
/// be deterministic.
6161
pub relaxed_simd_deterministic: bool,
6262

63-
/// Whether or not Wasm functions can be tail-called or not.
64-
pub tail_callable: bool,
65-
6663
/// Whether or not Wasm functions target the winch abi.
6764
pub winch_callable: bool,
6865
}
@@ -115,7 +112,6 @@ impl Tunables {
115112
generate_address_map: true,
116113
debug_adapter_modules: false,
117114
relaxed_simd_deterministic: false,
118-
tail_callable: false,
119115
winch_callable: false,
120116
}
121117
}

crates/wasmtime/src/config.rs

Lines changed: 0 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,6 @@ struct ConfigTunables {
150150
generate_address_map: Option<bool>,
151151
debug_adapter_modules: Option<bool>,
152152
relaxed_simd_deterministic: Option<bool>,
153-
tail_callable: Option<bool>,
154153
}
155154

156155
/// User-provided configuration for the compiler.
@@ -288,10 +287,6 @@ impl Config {
288287
ret.wasm_simd(true);
289288
ret.wasm_backtrace_details(WasmBacktraceDetails::Environment);
290289

291-
// This is on-by-default in `wasmparser` since it's a stage 4+ proposal
292-
// but it's not implemented in Wasmtime yet so disable it.
293-
ret.features.set(WasmFeatures::TAIL_CALL, false);
294-
295290
ret
296291
}
297292

@@ -721,7 +716,6 @@ impl Config {
721716
/// [WebAssembly tail calls proposal]: https://github.com/WebAssembly/tail-call
722717
pub fn wasm_tail_call(&mut self, enable: bool) -> &mut Self {
723718
self.features.set(WasmFeatures::TAIL_CALL, enable);
724-
self.tunables.tail_callable = Some(enable);
725719
self
726720
}
727721

@@ -1729,20 +1723,6 @@ impl Config {
17291723
self
17301724
}
17311725

1732-
pub(crate) fn conditionally_enable_defaults(&mut self) {
1733-
// If tail calls were not explicitly enabled/disabled (i.e. tail_callable is None), enable
1734-
// them if we are targeting a backend that supports them. Currently the Cranelift
1735-
// compilation strategy is the only one that supports tail calls.
1736-
if self.tunables.tail_callable.is_none() {
1737-
#[cfg(feature = "cranelift")]
1738-
let default_tail_calls = self.compiler_config.strategy == Some(Strategy::Cranelift);
1739-
#[cfg(not(feature = "cranelift"))]
1740-
let default_tail_calls = false;
1741-
1742-
self.wasm_tail_call(default_tail_calls);
1743-
}
1744-
}
1745-
17461726
pub(crate) fn validate(&self) -> Result<Tunables> {
17471727
if self.features.contains(WasmFeatures::REFERENCE_TYPES)
17481728
&& !self.features.contains(WasmFeatures::BULK_MEMORY)
@@ -1813,18 +1793,13 @@ impl Config {
18131793
generate_address_map
18141794
debug_adapter_modules
18151795
relaxed_simd_deterministic
1816-
tail_callable
18171796
}
18181797

18191798
// If we're going to compile with winch, we must use the winch calling convention.
18201799
#[cfg(any(feature = "cranelift", feature = "winch"))]
18211800
{
18221801
tunables.winch_callable = self.compiler_config.strategy == Some(Strategy::Winch);
18231802

1824-
if tunables.winch_callable && tunables.tail_callable {
1825-
bail!("Winch does not support the WebAssembly tail call proposal");
1826-
}
1827-
18281803
if tunables.winch_callable && !tunables.table_lazy_init {
18291804
bail!("Winch requires the table-lazy-init configuration option");
18301805
}

crates/wasmtime/src/engine.rs

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -98,12 +98,7 @@ impl Engine {
9898
crate::runtime::vm::debug_builtins::ensure_exported();
9999
}
100100

101-
let config = {
102-
let mut config = config.clone();
103-
config.conditionally_enable_defaults();
104-
config
105-
};
106-
101+
let config = config.clone();
107102
let tunables = config.validate()?;
108103

109104
#[cfg(any(feature = "cranelift", feature = "winch"))]

crates/wasmtime/src/engine/serialization.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -366,7 +366,6 @@ impl Metadata<'_> {
366366
guard_before_linear_memory,
367367
table_lazy_init,
368368
relaxed_simd_deterministic,
369-
tail_callable,
370369
winch_callable,
371370

372371
// This doesn't affect compilation, it's just a runtime setting.
@@ -429,7 +428,6 @@ impl Metadata<'_> {
429428
other.relaxed_simd_deterministic,
430429
"relaxed simd deterministic semantics",
431430
)?;
432-
Self::check_bool(tail_callable, other.tail_callable, "WebAssembly tail calls")?;
433431
Self::check_bool(
434432
winch_callable,
435433
other.winch_callable,

tests/all/defaults.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,40 +3,47 @@ use wasmtime::*;
33
#[test]
44
#[cfg_attr(miri, ignore)]
55
fn test_tail_call_default() -> Result<()> {
6-
for (expected, cfg) in [
6+
for (line, expected, cfg) in [
77
(
8+
line!(),
89
true,
910
Config::new()
1011
.strategy(Strategy::Cranelift)
1112
.target("x86_64")?,
1213
),
1314
(
15+
line!(),
1416
true,
1517
Config::new()
1618
.strategy(Strategy::Cranelift)
1719
.target("aarch64")?,
1820
),
1921
(
22+
line!(),
2023
true,
2124
Config::new()
2225
.strategy(Strategy::Cranelift)
2326
.target("riscv64")?,
2427
),
2528
(
29+
line!(),
2630
true,
2731
Config::new()
2832
.strategy(Strategy::Cranelift)
2933
.target("s390x")?,
3034
),
3135
(
36+
line!(),
3237
false,
3338
Config::new().strategy(Strategy::Winch).target("x86_64")?,
3439
),
3540
(
41+
line!(),
3642
false,
3743
Config::new().strategy(Strategy::Winch).target("aarch64")?,
3844
),
3945
(
46+
line!(),
4047
false,
4148
Config::new()
4249
.strategy(Strategy::Cranelift)
@@ -54,7 +61,7 @@ fn test_tail_call_default() -> Result<()> {
5461

5562
let result = engine.precompile_module(wat.as_bytes()).map(|_| ());
5663

57-
eprintln!("for config {cfg:?}, got: {result:?}");
64+
eprintln!("for config on line {line}, got: {result:?}");
5865

5966
assert_eq!(expected, result.is_ok());
6067
}

tests/all/module.rs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -270,10 +270,6 @@ fn tail_call_defaults() -> Result<()> {
270270
wasm_with_tail_calls,
271271
);
272272
assert!(err.is_err());
273-
274-
// can't enable with winch
275-
let err = Engine::new(Config::new().strategy(Strategy::Winch).wasm_tail_call(true));
276-
assert!(err.is_err());
277273
}
278274
Ok(())
279275
}

winch/codegen/src/codegen/mod.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,10 @@ where
7272

7373
/// Information about the source code location.
7474
pub source_location: SourceLocation,
75+
76+
/// Flag indicating whether during translation an unsupported instruction
77+
/// was found.
78+
pub found_unsupported_instruction: Option<&'static str>,
7579
}
7680

7781
impl<'a, 'translation, 'data, M> CodeGen<'a, 'translation, 'data, M>
@@ -91,6 +95,7 @@ where
9195
env,
9296
source_location: Default::default(),
9397
control_frames: Default::default(),
98+
found_unsupported_instruction: None,
9499
}
95100
}
96101

@@ -226,6 +231,10 @@ where
226231
self,
227232
offset,
228233
))??;
234+
235+
if let Some(insn) = self.found_unsupported_instruction {
236+
anyhow::bail!("unsupported instruction in Winch: {insn}")
237+
}
229238
}
230239
validator.finish(body.original_position())?;
231240
return Ok(());

winch/codegen/src/visitor.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@ macro_rules! def_unsupported {
3636

3737
fn $visit(&mut self $($(,$arg: $argty)*)?) -> Self::Output {
3838
$($(let _ = $arg;)*)?
39-
todo!(stringify!($op))
39+
40+
self.found_unsupported_instruction = Some(stringify!($op));
4041
}
4142
);
4243
)*

0 commit comments

Comments
 (0)