@@ -3,11 +3,18 @@ use super::*;
33struct Unbudgeted ;
44struct Bounded ;
55
6+ // The last argument of both handler types is the height of the 32-bit value stack, which most
7+ // instructions push to or pop from. Each handler writes it back to the stack before its body runs
8+ // and reads it again before dispatching, so no handler loads the height its predecessor stored.
9+ // Most keep it in a register in between. A few load it back before dispatching: the 8- and 16-lane
10+ // SIMD ops, and the fused `LoadLocal*` handlers, whose hot path joins a cold call.
11+ //
612// Between two handlers only what the calling convention passes in registers stays out of memory.
713// On arm64_32 (watchOS) the Rust ABI passes an aggregate larger than a pointer, the 8-byte
814// `Instruction`, by reference, so every dispatch would store it and the next handler load it back.
915// The C convention passes it in a register; `C-unwind` still lets a host function's panic unwind.
10- // Elsewhere the handlers keep the Rust ABI.
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.
1118macro_rules! handler_fn {
1219 ( $( #[ $meta: meta] ) * fn $( $rest: tt) * ) => {
1320 #[ cfg( all( target_arch = "aarch64" , target_pointer_width = "32" ) ) ]
@@ -23,9 +30,9 @@ macro_rules! handler_types {
2330 // Both sides are Rust, so the C convention's view of these types need not be FFI-safe.
2431 #[ allow( improper_ctypes_definitions) ]
2532 type UnbudgetedHandler =
26- for <' store, ' module> $( extern $abi) ? fn ( & mut Executor <' store, ' module>, & [ Instruction ] , usize , Instruction ) -> ExecResult <( ) >;
33+ for <' store, ' module> $( extern $abi) ? fn ( & mut Executor <' store, ' module>, & [ Instruction ] , usize , Instruction , usize ) -> ExecResult <( ) >;
2734 #[ allow( improper_ctypes_definitions) ]
28- type BoundedHandler = for <' store, ' module> $( extern $abi) ? fn ( & mut Executor <' store, ' module>, usize , Instruction , u32 ) -> ExecResult <( ) >;
35+ type BoundedHandler = for <' store, ' module> $( extern $abi) ? fn ( & mut Executor <' store, ' module>, usize , Instruction , u32 , usize ) -> ExecResult <( ) >;
2936 } ;
3037}
3138#[ cfg( all( target_arch = "aarch64" , target_pointer_width = "32" ) ) ]
@@ -53,15 +60,17 @@ macro_rules! define_unbudgeted_tail_dispatch {
5360 instructions: & [ Instruction ] ,
5461 $instr_ptr: usize ,
5562 instruction: Instruction ,
63+ height32: usize ,
5664 ) -> ExecResult <( ) > {
5765 macro_rules! $dispatch_next {
5866 ( $next_instr_ptr: expr) => { {
5967 let next_instr_ptr = $next_instr_ptr;
68+ let height32 = $executor. value_stack. stack_32. len( ) ;
6069 let Some ( & next) = instructions. get( next_instr_ptr) else {
61- become Self :: invalid_instr_ptr( $executor, instructions, next_instr_ptr, instruction) ;
70+ become Self :: invalid_instr_ptr( $executor, instructions, next_instr_ptr, instruction, height32 ) ;
6271 } ;
6372 let handler = Self :: handler_for( next. opcode( ) ) ;
64- become handler( $executor, instructions, next_instr_ptr, next) ;
73+ become handler( $executor, instructions, next_instr_ptr, next, height32 ) ;
6574 } } ;
6675 }
6776 macro_rules! $dispatch_flow {
@@ -83,11 +92,12 @@ macro_rules! define_unbudgeted_tail_dispatch {
8392 }
8493 use tinywasm_types:: Instruction :: * ;
8594 $( let $variant( $( $arg) ,* ) = & instruction else {
86- become Self :: handler_mismatch( $executor, instructions, $instr_ptr, instruction) ;
95+ become Self :: handler_mismatch( $executor, instructions, $instr_ptr, instruction, height32 ) ;
8796 } ; ) ?
8897 $( let $variant { $( $field) ,* } = & instruction else {
89- become Self :: handler_mismatch( $executor, instructions, $instr_ptr, instruction) ;
98+ become Self :: handler_mismatch( $executor, instructions, $instr_ptr, instruction, height32 ) ;
9099 } ; ) ?
100+ $executor. value_stack. stack_32. set_len( height32) ;
91101 $body;
92102 $dispatch_next!( $instr_ptr + 1 )
93103 }
@@ -115,10 +125,12 @@ macro_rules! define_bounded_tail_dispatch {
115125 $instr_ptr: usize ,
116126 instruction: Instruction ,
117127 instructions_until_checkpoint: u32 ,
128+ height32: usize ,
118129 ) -> ExecResult <( ) > {
119130 macro_rules! $dispatch_next {
120131 ( $next_instr_ptr: expr) => { {
121132 let next_instr_ptr = $next_instr_ptr;
133+ let height32 = $executor. value_stack. stack_32. len( ) ;
122134 if instructions_until_checkpoint == 0 {
123135 return cold!( {
124136 $executor. cf. instr_ptr = next_instr_ptr;
@@ -127,10 +139,10 @@ macro_rules! define_bounded_tail_dispatch {
127139 }
128140
129141 let Some ( & next) = $executor. func. instructions. get( next_instr_ptr) else {
130- become Self :: invalid_instr_ptr( $executor, next_instr_ptr, instruction, instructions_until_checkpoint) ;
142+ become Self :: invalid_instr_ptr( $executor, next_instr_ptr, instruction, instructions_until_checkpoint, height32 ) ;
131143 } ;
132144 let handler = Self :: handler_for( next. opcode( ) ) ;
133- become handler( $executor, next_instr_ptr, next, instructions_until_checkpoint - 1 ) ;
145+ become handler( $executor, next_instr_ptr, next, instructions_until_checkpoint - 1 , height32 ) ;
134146 } } ;
135147 }
136148 macro_rules! $dispatch_flow {
@@ -150,11 +162,12 @@ macro_rules! define_bounded_tail_dispatch {
150162 }
151163 use tinywasm_types:: Instruction :: * ;
152164 $( let $variant( $( $arg) ,* ) = & instruction else {
153- become Self :: handler_mismatch( $executor, $instr_ptr, instruction, instructions_until_checkpoint) ;
165+ become Self :: handler_mismatch( $executor, $instr_ptr, instruction, instructions_until_checkpoint, height32 ) ;
154166 } ; ) ?
155167 $( let $variant { $( $field) ,* } = & instruction else {
156- become Self :: handler_mismatch( $executor, $instr_ptr, instruction, instructions_until_checkpoint) ;
168+ become Self :: handler_mismatch( $executor, $instr_ptr, instruction, instructions_until_checkpoint, height32 ) ;
157169 } ; ) ?
170+ $executor. value_stack. stack_32. set_len( height32) ;
158171 $body;
159172 $dispatch_next!( $instr_ptr + 1 )
160173 }
@@ -171,15 +184,15 @@ impl Unbudgeted {
171184 handler_fn ! {
172185 #[ cold]
173186 #[ inline( never) ]
174- fn handler_mismatch( _: & mut Executor <' _, ' _>, _: & [ Instruction ] , _: usize , _: Instruction ) -> ExecResult <( ) > {
187+ fn handler_mismatch( _: & mut Executor <' _, ' _>, _: & [ Instruction ] , _: usize , _: Instruction , _ : usize ) -> ExecResult <( ) > {
175188 unreachable!( "instruction handler mismatch" )
176189 }
177190 }
178191
179192 handler_fn ! {
180193 #[ cold]
181194 #[ inline( never) ]
182- fn invalid_instr_ptr( _: & mut Executor <' _, ' _>, _: & [ Instruction ] , instr_ptr: usize , _: Instruction ) -> ExecResult <( ) > {
195+ fn invalid_instr_ptr( _: & mut Executor <' _, ' _>, _: & [ Instruction ] , instr_ptr: usize , _: Instruction , _ : usize ) -> ExecResult <( ) > {
183196 unreachable!( "instruction pointer {instr_ptr} out of range, this is a bug" )
184197 }
185198 }
@@ -193,15 +206,15 @@ impl Bounded {
193206 handler_fn ! {
194207 #[ cold]
195208 #[ inline( never) ]
196- fn handler_mismatch( _: & mut Executor <' _, ' _>, _: usize , _: Instruction , _: u32 ) -> ExecResult <( ) > {
209+ fn handler_mismatch( _: & mut Executor <' _, ' _>, _: usize , _: Instruction , _: u32 , _ : usize ) -> ExecResult <( ) > {
197210 unreachable!( "instruction handler mismatch" )
198211 }
199212 }
200213
201214 handler_fn ! {
202215 #[ cold]
203216 #[ inline( never) ]
204- fn invalid_instr_ptr( _: & mut Executor <' _, ' _>, instr_ptr: usize , _: Instruction , _: u32 ) -> ExecResult <( ) > {
217+ fn invalid_instr_ptr( _: & mut Executor <' _, ' _>, instr_ptr: usize , _: Instruction , _: u32 , _ : usize ) -> ExecResult <( ) > {
205218 unreachable!( "instruction pointer {instr_ptr} out of range, this is a bug" )
206219 }
207220 }
@@ -212,7 +225,8 @@ impl Bounded {
212225 let instr_ptr = executor. cf . instr_ptr ;
213226 let instruction = executor. func . instructions [ instr_ptr] ;
214227 let handler = Self :: handler_for ( instruction. opcode ( ) ) ;
215- handler ( executor, instr_ptr, instruction, chunk_left - 1 )
228+ let height32 = executor. value_stack . stack_32 . len ( ) ;
229+ handler ( executor, instr_ptr, instruction, chunk_left - 1 , height32)
216230 }
217231}
218232
@@ -226,7 +240,8 @@ impl Executor<'_, '_> {
226240 let instr_ptr = self . cf . instr_ptr ;
227241 let instruction = instructions[ instr_ptr] ;
228242 let handler = Unbudgeted :: handler_for ( instruction. opcode ( ) ) ;
229- handler ( & mut self , instructions, instr_ptr, instruction) ?;
243+ let height32 = self . value_stack . stack_32 . len ( ) ;
244+ handler ( & mut self , instructions, instr_ptr, instruction, height32) ?;
230245 if self . completed || self . left {
231246 return Ok ( self . left ( ) ) ;
232247 }
0 commit comments