11use std:: {
22 alloc:: { alloc, dealloc, handle_alloc_error, Layout } ,
33 borrow:: { Borrow , BorrowMut } ,
4+ iter:: repeat_n,
45 ptr:: NonNull ,
56} ;
67
@@ -44,15 +45,16 @@ pub struct InterpretedInstance<'a, F, Ctx> {
4445 #[ allow( dead_code) ]
4546 pre_compute_buf : AlignedBuf ,
4647 /// Instruction table of function pointers and pointers to the pre-computed buffer. Indexed by
47- /// `pc_index = (pc - pc_base) / DEFAULT_PC_STEP`.
48+ /// `pc_index = pc / DEFAULT_PC_STEP`.
49+ /// SAFETY: The first `pc_base / DEFAULT_PC_STEP` entries will be unreachable. We do this to
50+ /// avoid needing to subtract `pc_base` during runtime.
4851 pre_compute_insns : Vec < PreComputeInstruction < ' a , F , Ctx > > ,
4952 #[ cfg( feature = "tco" ) ]
5053 pre_compute_max_size : usize ,
5154 /// Handler function pointers for tail call optimization.
5255 #[ cfg( feature = "tco" ) ]
5356 handlers : Vec < Handler < F , Ctx > > ,
5457
55- pc_base : u32 ,
5658 pc_start : u32 ,
5759
5860 init_memory : SparseMemoryImage ,
@@ -84,11 +86,7 @@ macro_rules! run {
8486 #[ cfg( not( feature = "tco" ) ) ]
8587 unsafe {
8688 tracing:: debug!( "execute_trampoline" ) ;
87- execute_trampoline(
88- $interpreter. pc_base,
89- & mut $exec_state,
90- & $interpreter. pre_compute_insns,
91- ) ;
89+ execute_trampoline( & mut $exec_state, & $interpreter. pre_compute_insns) ;
9290 }
9391 #[ cfg( feature = "tco" ) ]
9492 {
@@ -151,21 +149,19 @@ where
151149 {
152150 let program = & exe. program ;
153151 let pre_compute_max_size = get_pre_compute_max_size ( program, inventory) ;
154- let mut pre_compute_buf = alloc_pre_compute_buf ( program. len ( ) , pre_compute_max_size) ;
152+ let mut pre_compute_buf = alloc_pre_compute_buf ( program, pre_compute_max_size) ;
155153 let mut split_pre_compute_buf =
156154 split_pre_compute_buf ( program, & mut pre_compute_buf, pre_compute_max_size) ;
157155 let pre_compute_insns = get_pre_compute_instructions :: < F , Ctx , E > (
158156 program,
159157 inventory,
160158 & mut split_pre_compute_buf,
161159 ) ?;
162- let pc_base = program. pc_base ;
163160 let pc_start = exe. pc_start ;
164161 let init_memory = exe. init_memory . clone ( ) ;
165162 #[ cfg( feature = "tco" ) ]
166- let handlers = program
167- . instructions_and_debug_infos
168- . iter ( )
163+ let handlers = repeat_n ( & None , get_pc_index ( program. pc_base ) )
164+ . chain ( program. instructions_and_debug_infos . iter ( ) )
169165 . zip_eq ( split_pre_compute_buf. iter_mut ( ) )
170166 . enumerate ( )
171167 . map (
@@ -191,7 +187,6 @@ where
191187 system_config : inventory. config ( ) . clone ( ) ,
192188 pre_compute_buf,
193189 pre_compute_insns,
194- pc_base,
195190 pc_start,
196191 init_memory,
197192 #[ cfg( feature = "tco" ) ]
@@ -209,7 +204,7 @@ where
209204 #[ cfg( feature = "tco" ) ]
210205 #[ inline( always) ]
211206 pub fn get_pre_compute ( & self , pc : u32 ) -> & [ u8 ] {
212- let pc_idx = get_pc_index ( self . pc_base , pc) ;
207+ let pc_idx = get_pc_index ( pc) ;
213208 // SAFETY:
214209 // - we assume that pc is in bounds
215210 // - pre_compute_buf is allocated for pre_compute_max_size * program_len bytes, with each
@@ -228,14 +223,6 @@ where
228223 }
229224 }
230225
231- pub fn pc_out_of_bounds_err ( & self , pc : u32 ) -> ExecutionError {
232- ExecutionError :: PcOutOfBounds {
233- pc,
234- pc_base : self . pc_base ,
235- program_len : self . pre_compute_insns . len ( ) ,
236- }
237- }
238-
239226 #[ cfg( feature = "tco" ) ]
240227 #[ inline( always) ]
241228 pub fn get_handler ( & self , pc : u32 ) -> Option < Handler < F , Ctx > > {
@@ -261,7 +248,7 @@ where
261248 {
262249 let program = & exe. program ;
263250 let pre_compute_max_size = get_metered_pre_compute_max_size ( program, inventory) ;
264- let mut pre_compute_buf = alloc_pre_compute_buf ( program. len ( ) , pre_compute_max_size) ;
251+ let mut pre_compute_buf = alloc_pre_compute_buf ( program, pre_compute_max_size) ;
265252 let mut split_pre_compute_buf =
266253 split_pre_compute_buf ( program, & mut pre_compute_buf, pre_compute_max_size) ;
267254 let pre_compute_insns = get_metered_pre_compute_instructions :: < F , Ctx , E > (
@@ -271,13 +258,11 @@ where
271258 & mut split_pre_compute_buf,
272259 ) ?;
273260
274- let pc_base = program. pc_base ;
275261 let pc_start = exe. pc_start ;
276262 let init_memory = exe. init_memory . clone ( ) ;
277263 #[ cfg( feature = "tco" ) ]
278- let handlers = program
279- . instructions_and_debug_infos
280- . iter ( )
264+ let handlers = repeat_n ( & None , get_pc_index ( program. pc_base ) )
265+ . chain ( program. instructions_and_debug_infos . iter ( ) )
281266 . zip_eq ( split_pre_compute_buf. iter_mut ( ) )
282267 . enumerate ( )
283268 . map (
@@ -305,7 +290,6 @@ where
305290 system_config : inventory. config ( ) . clone ( ) ,
306291 pre_compute_buf,
307292 pre_compute_insns,
308- pc_base,
309293 pc_start,
310294 init_memory,
311295 #[ cfg( feature = "tco" ) ]
@@ -448,8 +432,10 @@ where
448432 }
449433}
450434
451- fn alloc_pre_compute_buf ( program_len : usize , pre_compute_max_size : usize ) -> AlignedBuf {
452- let buf_len = program_len * pre_compute_max_size;
435+ fn alloc_pre_compute_buf < F > ( program : & Program < F > , pre_compute_max_size : usize ) -> AlignedBuf {
436+ let base_idx = get_pc_index ( program. pc_base ) ;
437+ let padded_program_len = base_idx + program. instructions_and_debug_infos . len ( ) ;
438+ let buf_len = padded_program_len * pre_compute_max_size;
453439 AlignedBuf :: uninit ( buf_len, pre_compute_max_size)
454440}
455441
@@ -458,8 +444,9 @@ fn split_pre_compute_buf<'a, F>(
458444 pre_compute_buf : & ' a mut AlignedBuf ,
459445 pre_compute_max_size : usize ,
460446) -> Vec < & ' a mut [ u8 ] > {
461- let program_len = program. instructions_and_debug_infos . len ( ) ;
462- let buf_len = program_len * pre_compute_max_size;
447+ let base_idx = get_pc_index ( program. pc_base ) ;
448+ let padded_program_len = base_idx + program. instructions_and_debug_infos . len ( ) ;
449+ let buf_len = padded_program_len * pre_compute_max_size;
463450 // SAFETY:
464451 // - pre_compute_buf.ptr was allocated with exactly buf_len bytes
465452 // - lifetime 'a ensures the returned slices don't outlive the AlignedBuf
@@ -475,7 +462,6 @@ fn split_pre_compute_buf<'a, F>(
475462/// The `fn_ptrs` pointer to pre-computed buffers that outlive this function.
476463#[ inline( always) ]
477464unsafe fn execute_trampoline < F : PrimeField32 , Ctx : ExecutionCtxTrait > (
478- pc_base : u32 ,
479465 vm_state : & mut VmExecState < F , GuestMemory , Ctx > ,
480466 fn_ptrs : & [ PreComputeInstruction < F , Ctx > ] ,
481467) {
@@ -487,16 +473,12 @@ unsafe fn execute_trampoline<F: PrimeField32, Ctx: ExecutionCtxTrait>(
487473 if Ctx :: should_suspend ( vm_state) {
488474 break ;
489475 }
490- let pc_index = get_pc_index ( pc_base , vm_state. pc ) ;
476+ let pc_index = get_pc_index ( vm_state. pc ) ;
491477 if let Some ( inst) = fn_ptrs. get ( pc_index) {
492478 // SAFETY: pre_compute assumed to live long enough
493479 unsafe { ( inst. handler ) ( inst. pre_compute , vm_state) } ;
494480 } else {
495- vm_state. exit_code = Err ( ExecutionError :: PcOutOfBounds {
496- pc : vm_state. pc ,
497- pc_base,
498- program_len : fn_ptrs. len ( ) ,
499- } ) ;
481+ vm_state. exit_code = Err ( ExecutionError :: PcOutOfBounds ( vm_state. pc ) ) ;
500482 }
501483 }
502484 if vm_state
@@ -509,8 +491,8 @@ unsafe fn execute_trampoline<F: PrimeField32, Ctx: ExecutionCtxTrait>(
509491}
510492
511493#[ inline( always) ]
512- pub fn get_pc_index ( pc_base : u32 , pc : u32 ) -> usize {
513- ( ( pc - pc_base ) / DEFAULT_PC_STEP ) as usize
494+ pub fn get_pc_index ( pc : u32 ) -> usize {
495+ ( pc / DEFAULT_PC_STEP ) as usize
514496}
515497
516498/// Bytes allocated according to the given Layout
@@ -647,15 +629,19 @@ where
647629 Ctx : ExecutionCtxTrait ,
648630 E : Executor < F > ,
649631{
650- program
651- . instructions_and_debug_infos
652- . iter ( )
632+ let unreachable_handler: ExecuteFunc < F , Ctx > = |_, vm_state| {
633+ vm_state. exit_code = Err ( ExecutionError :: Unreachable ( vm_state. pc ) ) ;
634+ } ;
635+
636+ repeat_n ( & None , get_pc_index ( program. pc_base ) )
637+ . chain ( program. instructions_and_debug_infos . iter ( ) )
653638 . zip_eq ( pre_compute. iter_mut ( ) )
654639 . enumerate ( )
655640 . map ( |( i, ( inst_opt, buf) ) | {
656- // SAFETY: we cast to raw pointer and then borrow to remove the lifetime. This is safe
657- // only in the current context because `buf` comes from `pre_compute_buf` which will
658- // outlive the returned `PreComputeInstruction`s.
641+ // SAFETY: we cast to raw pointer and then borrow to remove the lifetime. This
642+ // is safe only in the current context because `buf` comes
643+ // from `pre_compute_buf` which will outlive the returned
644+ // `PreComputeInstruction`s.
659645 let buf: & mut [ u8 ] = unsafe { & mut * ( * buf as * mut [ u8 ] ) } ;
660646 let pre_inst = if let Some ( ( inst, _) ) = inst_opt {
661647 tracing:: trace!( "get_pre_compute_instruction {inst:?}" ) ;
@@ -679,9 +665,7 @@ where
679665 } else {
680666 // Dead instruction at this pc
681667 PreComputeInstruction {
682- handler : |_, vm_state| {
683- vm_state. exit_code = Err ( ExecutionError :: Unreachable ( vm_state. pc ) ) ;
684- } ,
668+ handler : unreachable_handler,
685669 pre_compute : buf,
686670 }
687671 } ;
@@ -701,15 +685,18 @@ where
701685 Ctx : MeteredExecutionCtxTrait ,
702686 E : MeteredExecutor < F > ,
703687{
704- program
705- . instructions_and_debug_infos
706- . iter ( )
688+ let unreachable_handler: ExecuteFunc < F , Ctx > = |_, vm_state| {
689+ vm_state. exit_code = Err ( ExecutionError :: Unreachable ( vm_state. pc ) ) ;
690+ } ;
691+ repeat_n ( & None , get_pc_index ( program. pc_base ) )
692+ . chain ( program. instructions_and_debug_infos . iter ( ) )
707693 . zip_eq ( pre_compute. iter_mut ( ) )
708694 . enumerate ( )
709695 . map ( |( i, ( inst_opt, buf) ) | {
710- // SAFETY: we cast to raw pointer and then borrow to remove the lifetime. This is safe
711- // only in the current context because `buf` comes from `pre_compute_buf` which will
712- // outlive the returned `PreComputeInstruction`s.
696+ // SAFETY: we cast to raw pointer and then borrow to remove the lifetime. This
697+ // is safe only in the current context because `buf` comes
698+ // from `pre_compute_buf` which will outlive the returned
699+ // `PreComputeInstruction`s.
713700 let buf: & mut [ u8 ] = unsafe { & mut * ( * buf as * mut [ u8 ] ) } ;
714701 let pre_inst = if let Some ( ( inst, _) ) = inst_opt {
715702 tracing:: trace!( "get_metered_pre_compute_instruction {inst:?}" ) ;
@@ -738,9 +725,7 @@ where
738725 }
739726 } else {
740727 PreComputeInstruction {
741- handler : |_, vm_state| {
742- vm_state. exit_code = Err ( ExecutionError :: Unreachable ( vm_state. pc ) ) ;
743- } ,
728+ handler : unreachable_handler,
744729 pre_compute : buf,
745730 }
746731 } ;
0 commit comments