@@ -3,8 +3,10 @@ use super::*;
33struct Unbudgeted ;
44struct Bounded ;
55
6- type UnbudgetedHandler = for <' store > fn ( & mut Executor < ' store > , & [ Instruction ] , usize , Instruction ) -> ExecResult < ( ) > ;
7- type BoundedHandler = for <' store > fn ( & mut Executor < ' store > , usize , Instruction , u32 ) -> ExecResult < ( ) > ;
6+ type UnbudgetedHandler =
7+ for <' store , ' module > fn ( & mut Executor < ' store , ' module > , & [ Instruction ] , usize , Instruction ) -> ExecResult < ( ) > ;
8+ type BoundedHandler =
9+ for <' store , ' module > fn ( & mut Executor < ' store , ' module > , usize , Instruction , u32 ) -> ExecResult < ( ) > ;
810
911macro_rules! define_unbudgeted_tail_dispatch {
1012 ( $executor: ident, $instr_ptr: ident, $dispatch_next: ident, $dispatch_flow: ident;
@@ -22,7 +24,7 @@ macro_rules! define_unbudgeted_tail_dispatch {
2224 $(
2325 #[ allow( non_snake_case, unreachable_code, unused_imports, unused_macros, unused_variables) ]
2426 fn $variant(
25- $executor: & mut Executor <' _>,
27+ $executor: & mut Executor <' _, ' _ >,
2628 instructions: & [ Instruction ] ,
2729 $instr_ptr: usize ,
2830 instruction: Instruction ,
@@ -45,7 +47,12 @@ macro_rules! define_unbudgeted_tail_dispatch {
4547 $executor. cf. instr_ptr = next_instr_ptr;
4648 return Ok ( ( ) ) ;
4749 } ,
48- ExecFlow :: Complete => return cold!( { $executor. completed = true ; Ok ( ( ) ) } ) ,
50+ ExecFlow :: Complete => return cold!( {
51+ if !$executor. left {
52+ $executor. completed = true ;
53+ }
54+ Ok ( ( ) )
55+ } ) ,
4956 }
5057 } } ;
5158 }
@@ -79,7 +86,7 @@ macro_rules! define_bounded_tail_dispatch {
7986 $(
8087 #[ allow( non_snake_case, unreachable_code, unused_imports, unused_macros, unused_variables) ]
8188 fn $variant(
82- $executor: & mut Executor <' _>,
89+ $executor: & mut Executor <' _, ' _ >,
8390 $instr_ptr: usize ,
8491 instruction: Instruction ,
8592 instructions_until_checkpoint: u32 ,
@@ -106,7 +113,11 @@ macro_rules! define_bounded_tail_dispatch {
106113 match $flow. next_instr_ptr( ) {
107114 Some ( next_instr_ptr) => $dispatch_next!( next_instr_ptr) ,
108115 None => return cold!( {
109- $executor. completed = true ;
116+ if $executor. left {
117+ $executor. chunk_left = instructions_until_checkpoint;
118+ } else {
119+ $executor. completed = true ;
120+ }
110121 Ok ( ( ) )
111122 } ) ,
112123 }
@@ -134,13 +145,13 @@ impl Unbudgeted {
134145
135146 #[ cold]
136147 #[ inline( never) ]
137- fn handler_mismatch ( _: & mut Executor < ' _ > , _: & [ Instruction ] , _: usize , _: Instruction ) -> ExecResult < ( ) > {
148+ fn handler_mismatch ( _: & mut Executor < ' _ , ' _ > , _: & [ Instruction ] , _: usize , _: Instruction ) -> ExecResult < ( ) > {
138149 unreachable ! ( "instruction handler mismatch" )
139150 }
140151
141152 #[ cold]
142153 #[ inline( never) ]
143- fn invalid_instr_ptr ( _: & mut Executor < ' _ > , _: & [ Instruction ] , instr_ptr : usize , _: Instruction ) -> ExecResult < ( ) > {
154+ fn invalid_instr_ptr ( _: & mut Executor < ' _ , ' _ > , _: & [ Instruction ] , instr_ptr : usize , _: Instruction ) -> ExecResult < ( ) > {
144155 unreachable ! ( "instruction pointer {instr_ptr} out of range, this is a bug" )
145156 }
146157}
@@ -152,79 +163,92 @@ impl Bounded {
152163
153164 #[ cold]
154165 #[ inline( never) ]
155- fn handler_mismatch ( _: & mut Executor < ' _ > , _: usize , _: Instruction , _: u32 ) -> ExecResult < ( ) > {
166+ fn handler_mismatch ( _: & mut Executor < ' _ , ' _ > , _: usize , _: Instruction , _: u32 ) -> ExecResult < ( ) > {
156167 unreachable ! ( "instruction handler mismatch" )
157168 }
158169
159170 #[ cold]
160171 #[ inline( never) ]
161- fn invalid_instr_ptr ( _: & mut Executor < ' _ > , instr_ptr : usize , _: Instruction , _: u32 ) -> ExecResult < ( ) > {
172+ fn invalid_instr_ptr ( _: & mut Executor < ' _ , ' _ > , instr_ptr : usize , _: Instruction , _: u32 ) -> ExecResult < ( ) > {
162173 unreachable ! ( "instruction pointer {instr_ptr} out of range, this is a bug" )
163174 }
164175
176+ /// Runs up to `chunk_left` (at least 1) instructions from `executor.cf`.
165177 #[ inline( always) ]
166- fn run ( executor : & mut Executor < ' _ > ) -> ExecResult < ( ) > {
178+ fn run ( executor : & mut Executor < ' _ , ' _ > , chunk_left : u32 ) -> ExecResult < ( ) > {
167179 let instr_ptr = executor. cf . instr_ptr ;
168180 let instruction = executor. func . instructions [ instr_ptr] ;
169181 let handler = Self :: handler_for ( instruction. opcode ( ) ) ;
170- handler ( executor, instr_ptr, instruction, CHECKPOINT_INTERVAL - 1 )
182+ handler ( executor, instr_ptr, instruction, chunk_left - 1 )
171183 }
172184}
173185
174- impl < ' store > Executor < ' store > {
186+ impl Executor < ' _ , ' _ > {
187+ /// Runs until the call completes (`None`) or continues in another module instance's frame.
175188 #[ inline( always) ]
176- pub ( crate ) fn run_to_completion ( mut self ) -> Result < ( ) > {
189+ pub ( crate ) fn run_to_completion ( mut self ) -> Result < Option < CallFrame > > {
177190 loop {
178- let func = self . func . clone ( ) ;
191+ let func = self . func ;
179192 let instructions = & func. instructions ;
180193 let instr_ptr = self . cf . instr_ptr ;
181194 let instruction = instructions[ instr_ptr] ;
182195 let handler = Unbudgeted :: handler_for ( instruction. opcode ( ) ) ;
183196 handler ( & mut self , instructions, instr_ptr, instruction) ?;
184- if self . completed {
185- return Ok ( ( ) ) ;
197+ if self . completed || self . left {
198+ return Ok ( self . left ( ) ) ;
186199 }
187200 }
188201 }
189202
203+ /// Runs `chunk_left` instructions, then checkpoint by checkpoint until `time_budget` has
204+ /// elapsed since `start`.
190205 #[ cfg( feature = "std" ) ]
191206 #[ inline( always) ]
192- pub ( crate ) fn run_with_time_budget ( mut self , time_budget : core:: time:: Duration ) -> Result < ExecState > {
193- use crate :: std:: time:: Instant ;
194-
195- if time_budget. is_zero ( ) {
196- return Ok ( ExecState :: Suspended ( self . cf ) ) ;
197- }
198- let start = Instant :: now ( ) ;
199-
207+ pub ( crate ) fn run_with_time_budget (
208+ mut self ,
209+ start : crate :: std:: time:: Instant ,
210+ time_budget : core:: time:: Duration ,
211+ mut chunk_left : u32 ,
212+ ) -> Result < RunEnd > {
200213 loop {
201- Bounded :: run ( & mut self ) ?;
202- if self . completed {
203- return cold ! ( Ok ( ExecState :: Completed ) ) ;
214+ if chunk_left != 0 {
215+ Bounded :: run ( & mut self , chunk_left) ?;
216+ if let Some ( end) = self . run_end ( ) {
217+ return Ok ( end) ;
218+ }
204219 }
220+ chunk_left = CHECKPOINT_INTERVAL ;
205221 if start. elapsed ( ) >= time_budget {
206- return cold ! ( Ok ( ExecState :: Suspended ( self . cf) ) ) ;
222+ return cold ! ( Ok ( RunEnd :: State ( ExecState :: Suspended ( self . cf) ) ) ) ;
207223 }
208224 }
209225 }
210226
227+ /// Runs `chunk_left` instructions, then checkpoint by checkpoint until the store's fuel is out.
211228 #[ inline( always) ]
212- pub ( crate ) fn run_with_fuel ( mut self , fuel : u32 ) -> Result < ExecState > {
229+ pub ( crate ) fn run_with_fuel ( mut self , mut chunk_left : u32 ) -> Result < RunEnd > {
213230 self . fuel_metered = true ;
214- self . store . execution_fuel = fuel;
215- if self . store . execution_fuel == 0 {
216- return Ok ( ExecState :: Suspended ( self . cf ) ) ;
217- }
218-
219231 loop {
220- Bounded :: run ( & mut self ) ?;
221- if self . completed {
222- return cold ! ( Ok ( ExecState :: Completed ) ) ;
232+ if chunk_left != 0 {
233+ Bounded :: run ( & mut self , chunk_left) ?;
234+ if let Some ( end) = self . run_end ( ) {
235+ return Ok ( end) ;
236+ }
223237 }
238+ chunk_left = CHECKPOINT_INTERVAL ;
224239 self . store . execution_fuel = self . store . execution_fuel . saturating_sub ( CHECKPOINT_INTERVAL ) ;
225240 if self . store . execution_fuel == 0 {
226- return cold ! ( Ok ( ExecState :: Suspended ( self . cf) ) ) ;
241+ return cold ! ( Ok ( RunEnd :: State ( ExecState :: Suspended ( self . cf) ) ) ) ;
227242 }
228243 }
229244 }
245+
246+ /// How a bounded chain that stopped ended, unless it stopped at a checkpoint.
247+ #[ inline( always) ]
248+ fn run_end ( & self ) -> Option < RunEnd > {
249+ if self . completed {
250+ return cold ! ( Some ( RunEnd :: State ( ExecState :: Completed ) ) ) ;
251+ }
252+ self . left ( ) . map ( |frame| RunEnd :: Left ( frame, self . chunk_left ) )
253+ }
230254}
0 commit comments