Skip to content

Commit 1e2d47a

Browse files
committed
perf: keep the bounded handlers in Windows x64's four argument registers
Windows x64 passes four integer arguments in registers. The bounded handlers (fuel and time budgets) take four besides the 32-bit height, so passing the height put a fifth argument on the stack under either convention: the height under the Rust ABI, the instruction under System V, which passes this enum in memory. On Windows x64 the bounded handlers keep the Rust ABI and leave the height out: they read it from the stack before dispatching, as before the height was passed. The unbudgeted handlers keep the System V convention and the height.
1 parent 1c30d4d commit 1e2d47a

1 file changed

Lines changed: 57 additions & 15 deletions

File tree

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

Lines changed: 57 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,12 @@ 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-
// 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
16+
// On Windows x64 the unbudgeted handlers use the System V convention (`sysv64-unwind`), which
17+
// passes six integer arguments in registers where Windows' own passes four, so a dispatch no longer
18+
// stores the height for the next handler to load back. The `Instruction` still goes through the
19+
// stack there: the System V rules pass this enum in memory. The bounded handlers keep the Rust ABI
20+
// on Windows x64 and do not pass the height, so their four arguments, the `Instruction` among them,
21+
// stay in Windows' four argument registers. Elsewhere the handlers keep the Rust ABI, which
2022
// passes eight integer arguments in registers on arm64 and six on x86-64 System V, with the
2123
// `Instruction` in one of them.
2224
macro_rules! handler_fn {
@@ -32,17 +34,57 @@ macro_rules! handler_fn {
3234
};
3335
}
3436

37+
macro_rules! bounded_handler_fn {
38+
($(#[$meta:meta])* fn $($rest:tt)*) => {
39+
#[cfg(all(target_arch = "aarch64", target_pointer_width = "32"))]
40+
#[allow(improper_ctypes_definitions)]
41+
$(#[$meta])* extern "C-unwind" fn $($rest)*
42+
#[cfg(not(all(target_arch = "aarch64", target_pointer_width = "32")))]
43+
$(#[$meta])* fn $($rest)*
44+
};
45+
}
46+
47+
/// The height a bounded handler passes on: nothing on Windows x64.
48+
#[cfg(not(all(target_arch = "x86_64", windows)))]
49+
type BoundedHeight = usize;
50+
#[cfg(all(target_arch = "x86_64", windows))]
51+
type BoundedHeight = ();
52+
53+
#[inline(always)]
54+
fn bounded_height(executor: &Executor<'_>) -> BoundedHeight {
55+
#[cfg(not(all(target_arch = "x86_64", windows)))]
56+
return executor.value_stack.stack_32.len();
57+
#[cfg(all(target_arch = "x86_64", windows))]
58+
{
59+
let _ = executor;
60+
}
61+
}
62+
63+
/// Writes a passed height back to the stack.
64+
#[inline(always)]
65+
fn enter_bounded_height(executor: &mut Executor<'_>, height32: BoundedHeight) {
66+
#[cfg(not(all(target_arch = "x86_64", windows)))]
67+
executor.value_stack.stack_32.set_len(height32);
68+
#[cfg(all(target_arch = "x86_64", windows))]
69+
{
70+
let _ = (executor, height32);
71+
}
72+
}
73+
3574
macro_rules! handler_types {
3675
($($abi:literal)?) => {
3776
// Both sides are Rust, so the C convention's view of these types need not be FFI-safe.
3877
#[allow(improper_ctypes_definitions)]
3978
type UnbudgetedHandler =
4079
for<'store> $(extern $abi)? fn(&mut Executor<'store>, &[Instruction], usize, Instruction, usize) -> ExecResult<()>;
41-
#[allow(improper_ctypes_definitions)]
42-
type BoundedHandler = for<'store> $(extern $abi)? fn(&mut Executor<'store>, usize, Instruction, u32, usize) -> ExecResult<()>;
4380
};
4481
}
4582
#[cfg(all(target_arch = "aarch64", target_pointer_width = "32"))]
83+
#[allow(improper_ctypes_definitions)]
84+
type BoundedHandler = for<'store> extern "C-unwind" fn(&mut Executor<'store>, usize, Instruction, u32, BoundedHeight) -> ExecResult<()>;
85+
#[cfg(not(all(target_arch = "aarch64", target_pointer_width = "32")))]
86+
type BoundedHandler = for<'store> fn(&mut Executor<'store>, usize, Instruction, u32, BoundedHeight) -> ExecResult<()>;
87+
#[cfg(all(target_arch = "aarch64", target_pointer_width = "32"))]
4688
handler_types!("C-unwind");
4789
#[cfg(all(target_arch = "x86_64", windows))]
4890
handler_types!("sysv64-unwind");
@@ -122,19 +164,19 @@ macro_rules! define_bounded_tail_dispatch {
122164
HANDLERS[opcode as usize]
123165
}
124166

125-
$(handler_fn! {
167+
$(bounded_handler_fn! {
126168
#[allow(non_snake_case, unreachable_code, unused_imports, unused_macros, unused_variables)]
127169
fn $variant(
128170
$executor: &mut Executor<'_>,
129171
$instr_ptr: usize,
130172
instruction: Instruction,
131173
instructions_until_checkpoint: u32,
132-
height32: usize,
174+
height32: BoundedHeight,
133175
) -> ExecResult<()> {
134176
macro_rules! $dispatch_next {
135177
($next_instr_ptr:expr) => {{
136178
let next_instr_ptr = $next_instr_ptr;
137-
let height32 = $executor.value_stack.stack_32.len();
179+
let height32 = bounded_height($executor);
138180
if instructions_until_checkpoint == 0 {
139181
return cold!({
140182
$executor.cf.instr_ptr = next_instr_ptr;
@@ -167,7 +209,7 @@ macro_rules! define_bounded_tail_dispatch {
167209
$(let $variant { $($field),* } = &instruction else {
168210
become Self::handler_mismatch($executor, $instr_ptr, instruction, instructions_until_checkpoint, height32);
169211
};)?
170-
$executor.value_stack.stack_32.set_len(height32);
212+
enter_bounded_height($executor, height32);
171213
$body;
172214
$dispatch_next!($instr_ptr + 1)
173215
}
@@ -203,18 +245,18 @@ impl Bounded {
203245

204246
// Tail-called like `Unbudgeted`'s.
205247

206-
handler_fn! {
248+
bounded_handler_fn! {
207249
#[cold]
208250
#[inline(never)]
209-
fn handler_mismatch(_: &mut Executor<'_>, _: usize, _: Instruction, _: u32, _: usize) -> ExecResult<()> {
251+
fn handler_mismatch(_: &mut Executor<'_>, _: usize, _: Instruction, _: u32, _: BoundedHeight) -> ExecResult<()> {
210252
unreachable!("instruction handler mismatch")
211253
}
212254
}
213255

214-
handler_fn! {
256+
bounded_handler_fn! {
215257
#[cold]
216258
#[inline(never)]
217-
fn invalid_instr_ptr(_: &mut Executor<'_>, instr_ptr: usize, _: Instruction, _: u32, _: usize) -> ExecResult<()> {
259+
fn invalid_instr_ptr(_: &mut Executor<'_>, instr_ptr: usize, _: Instruction, _: u32, _: BoundedHeight) -> ExecResult<()> {
218260
unreachable!("instruction pointer {instr_ptr} out of range, this is a bug")
219261
}
220262
}
@@ -224,7 +266,7 @@ impl Bounded {
224266
let instr_ptr = executor.cf.instr_ptr;
225267
let instruction = executor.func.instructions[instr_ptr];
226268
let handler = Self::handler_for(instruction.opcode());
227-
let height32 = executor.value_stack.stack_32.len();
269+
let height32 = bounded_height(executor);
228270
handler(executor, instr_ptr, instruction, CHECKPOINT_INTERVAL - 1, height32)
229271
}
230272
}

0 commit comments

Comments
 (0)