Skip to content

Commit ccb5032

Browse files
committed
perf: pass the tail-call handlers' arguments in six registers on Windows x64
Windows' calling convention passes four integer arguments in registers, so every dispatch stored the Unbudgeted handlers' last two and the next handler loaded them back. On Windows x64 the handlers now use the System V convention, which passes six, so the 32-bit height stays in a register. The instruction still goes through the stack: under the System V rules this enum is passed in memory. A CI job runs the tests with tail calls on Windows.
1 parent d42907d commit ccb5032

2 files changed

Lines changed: 18 additions & 4 deletions

File tree

‎.github/workflows/test.yaml‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,11 @@ jobs:
7474
rust: stable
7575
name: "Windows x86_64"
7676
target: x86_64-pc-windows-msvc
77+
- os: windows-2025
78+
rust: nightly
79+
name: "Windows x86_64 (nightly tail calls)"
80+
target: x86_64-pc-windows-msvc
81+
args: "--features tinywasm/nightly-tail-calls"
7782
- os: ubuntu-26.04-arm
7883
rust: stable
7984
name: "Linux arm64"

‎crates/tinywasm/src/interpreter/executor/dispatch_become.rs‎

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,21 @@ struct Bounded;
1313
// On arm64_32 (watchOS) the Rust ABI passes an aggregate larger than a pointer, the 8-byte
1414
// `Instruction`, by reference, so every dispatch would store it and the next handler load it back.
1515
// The C convention passes it in a register; `C-unwind` still lets a host function's panic unwind.
16-
// Elsewhere the handlers keep the Rust ABI, which passes eight integer arguments in registers on
17-
// arm64, six on x86-64 System V (as many as the Unbudgeted handlers take) and four on Windows x64.
16+
// On Windows x64 the handlers use the System V convention (`sysv64-unwind`), which passes six
17+
// integer arguments in registers where Windows' own passes four, so a dispatch no longer stores the
18+
// height for the next handler to load back. The `Instruction` still goes through the stack there:
19+
// the System V rules pass this enum in memory. Elsewhere the handlers keep the Rust ABI, which
20+
// passes eight integer arguments in registers on arm64 and six on x86-64 System V, with the
21+
// `Instruction` in one of them.
1822
macro_rules! handler_fn {
1923
($(#[$meta:meta])* fn $($rest:tt)*) => {
2024
#[cfg(all(target_arch = "aarch64", target_pointer_width = "32"))]
2125
#[allow(improper_ctypes_definitions)]
2226
$(#[$meta])* extern "C-unwind" fn $($rest)*
23-
#[cfg(not(all(target_arch = "aarch64", target_pointer_width = "32")))]
27+
#[cfg(all(target_arch = "x86_64", windows))]
28+
#[allow(improper_ctypes_definitions)]
29+
$(#[$meta])* extern "sysv64-unwind" fn $($rest)*
30+
#[cfg(not(any(all(target_arch = "aarch64", target_pointer_width = "32"), all(target_arch = "x86_64", windows))))]
2431
$(#[$meta])* fn $($rest)*
2532
};
2633
}
@@ -37,7 +44,9 @@ macro_rules! handler_types {
3744
}
3845
#[cfg(all(target_arch = "aarch64", target_pointer_width = "32"))]
3946
handler_types!("C-unwind");
40-
#[cfg(not(all(target_arch = "aarch64", target_pointer_width = "32")))]
47+
#[cfg(all(target_arch = "x86_64", windows))]
48+
handler_types!("sysv64-unwind");
49+
#[cfg(not(any(all(target_arch = "aarch64", target_pointer_width = "32"), all(target_arch = "x86_64", windows))))]
4150
handler_types!();
4251

4352
macro_rules! define_unbudgeted_tail_dispatch {

0 commit comments

Comments
 (0)