@@ -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> $( extern $abi) ? fn ( & mut Executor <' store>, & [ Instruction ] , usize , Instruction ) -> ExecResult <( ) >;
33+ for <' store> $( extern $abi) ? fn ( & mut Executor <' store>, & [ Instruction ] , usize , Instruction , usize ) -> ExecResult <( ) >;
2734 #[ allow( improper_ctypes_definitions) ]
28- type BoundedHandler = for <' store> $( extern $abi) ? fn ( & mut Executor <' store>, usize , Instruction , u32 ) -> ExecResult <( ) >;
35+ type BoundedHandler = for <' store> $( extern $abi) ? fn ( & mut Executor <' store>, 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 {
@@ -78,11 +87,12 @@ macro_rules! define_unbudgeted_tail_dispatch {
7887 }
7988 use tinywasm_types:: Instruction :: * ;
8089 $( let $variant( $( $arg) ,* ) = & instruction else {
81- become Self :: handler_mismatch( $executor, instructions, $instr_ptr, instruction) ;
90+ become Self :: handler_mismatch( $executor, instructions, $instr_ptr, instruction, height32 ) ;
8291 } ; ) ?
8392 $( let $variant { $( $field) ,* } = & instruction else {
84- become Self :: handler_mismatch( $executor, instructions, $instr_ptr, instruction) ;
93+ become Self :: handler_mismatch( $executor, instructions, $instr_ptr, instruction, height32 ) ;
8594 } ; ) ?
95+ $executor. value_stack. stack_32. set_len( height32) ;
8696 $body;
8797 $dispatch_next!( $instr_ptr + 1 )
8898 }
@@ -110,10 +120,12 @@ macro_rules! define_bounded_tail_dispatch {
110120 $instr_ptr: usize ,
111121 instruction: Instruction ,
112122 instructions_until_checkpoint: u32 ,
123+ height32: usize ,
113124 ) -> ExecResult <( ) > {
114125 macro_rules! $dispatch_next {
115126 ( $next_instr_ptr: expr) => { {
116127 let next_instr_ptr = $next_instr_ptr;
128+ let height32 = $executor. value_stack. stack_32. len( ) ;
117129 if instructions_until_checkpoint == 0 {
118130 return cold!( {
119131 $executor. cf. instr_ptr = next_instr_ptr;
@@ -122,10 +134,10 @@ macro_rules! define_bounded_tail_dispatch {
122134 }
123135
124136 let Some ( & next) = $executor. func. instructions. get( next_instr_ptr) else {
125- become Self :: invalid_instr_ptr( $executor, next_instr_ptr, instruction, instructions_until_checkpoint) ;
137+ become Self :: invalid_instr_ptr( $executor, next_instr_ptr, instruction, instructions_until_checkpoint, height32 ) ;
126138 } ;
127139 let handler = Self :: handler_for( next. opcode( ) ) ;
128- become handler( $executor, next_instr_ptr, next, instructions_until_checkpoint - 1 ) ;
140+ become handler( $executor, next_instr_ptr, next, instructions_until_checkpoint - 1 , height32 ) ;
129141 } } ;
130142 }
131143 macro_rules! $dispatch_flow {
@@ -141,11 +153,12 @@ macro_rules! define_bounded_tail_dispatch {
141153 }
142154 use tinywasm_types:: Instruction :: * ;
143155 $( let $variant( $( $arg) ,* ) = & instruction else {
144- become Self :: handler_mismatch( $executor, $instr_ptr, instruction, instructions_until_checkpoint) ;
156+ become Self :: handler_mismatch( $executor, $instr_ptr, instruction, instructions_until_checkpoint, height32 ) ;
145157 } ; ) ?
146158 $( let $variant { $( $field) ,* } = & instruction else {
147- become Self :: handler_mismatch( $executor, $instr_ptr, instruction, instructions_until_checkpoint) ;
159+ become Self :: handler_mismatch( $executor, $instr_ptr, instruction, instructions_until_checkpoint, height32 ) ;
148160 } ; ) ?
161+ $executor. value_stack. stack_32. set_len( height32) ;
149162 $body;
150163 $dispatch_next!( $instr_ptr + 1 )
151164 }
@@ -162,15 +175,15 @@ impl Unbudgeted {
162175 handler_fn ! {
163176 #[ cold]
164177 #[ inline( never) ]
165- fn handler_mismatch( _: & mut Executor <' _>, _: & [ Instruction ] , _: usize , _: Instruction ) -> ExecResult <( ) > {
178+ fn handler_mismatch( _: & mut Executor <' _>, _: & [ Instruction ] , _: usize , _: Instruction , _ : usize ) -> ExecResult <( ) > {
166179 unreachable!( "instruction handler mismatch" )
167180 }
168181 }
169182
170183 handler_fn ! {
171184 #[ cold]
172185 #[ inline( never) ]
173- fn invalid_instr_ptr( _: & mut Executor <' _>, _: & [ Instruction ] , instr_ptr: usize , _: Instruction ) -> ExecResult <( ) > {
186+ fn invalid_instr_ptr( _: & mut Executor <' _>, _: & [ Instruction ] , instr_ptr: usize , _: Instruction , _ : usize ) -> ExecResult <( ) > {
174187 unreachable!( "instruction pointer {instr_ptr} out of range, this is a bug" )
175188 }
176189 }
@@ -184,15 +197,15 @@ impl Bounded {
184197 handler_fn ! {
185198 #[ cold]
186199 #[ inline( never) ]
187- fn handler_mismatch( _: & mut Executor <' _>, _: usize , _: Instruction , _: u32 ) -> ExecResult <( ) > {
200+ fn handler_mismatch( _: & mut Executor <' _>, _: usize , _: Instruction , _: u32 , _ : usize ) -> ExecResult <( ) > {
188201 unreachable!( "instruction handler mismatch" )
189202 }
190203 }
191204
192205 handler_fn ! {
193206 #[ cold]
194207 #[ inline( never) ]
195- fn invalid_instr_ptr( _: & mut Executor <' _>, instr_ptr: usize , _: Instruction , _: u32 ) -> ExecResult <( ) > {
208+ fn invalid_instr_ptr( _: & mut Executor <' _>, instr_ptr: usize , _: Instruction , _: u32 , _ : usize ) -> ExecResult <( ) > {
196209 unreachable!( "instruction pointer {instr_ptr} out of range, this is a bug" )
197210 }
198211 }
@@ -202,7 +215,8 @@ impl Bounded {
202215 let instr_ptr = executor. cf . instr_ptr ;
203216 let instruction = executor. func . instructions [ instr_ptr] ;
204217 let handler = Self :: handler_for ( instruction. opcode ( ) ) ;
205- handler ( executor, instr_ptr, instruction, CHECKPOINT_INTERVAL - 1 )
218+ let height32 = executor. value_stack . stack_32 . len ( ) ;
219+ handler ( executor, instr_ptr, instruction, CHECKPOINT_INTERVAL - 1 , height32)
206220 }
207221}
208222
@@ -215,7 +229,8 @@ impl<'store> Executor<'store> {
215229 let instr_ptr = self . cf . instr_ptr ;
216230 let instruction = instructions[ instr_ptr] ;
217231 let handler = Unbudgeted :: handler_for ( instruction. opcode ( ) ) ;
218- handler ( & mut self , instructions, instr_ptr, instruction) ?;
232+ let height32 = self . value_stack . stack_32 . len ( ) ;
233+ handler ( & mut self , instructions, instr_ptr, instruction, height32) ?;
219234 if self . completed {
220235 return Ok ( ( ) ) ;
221236 }
0 commit comments