1
1
use std:: {
2
2
alloc:: { alloc, dealloc, handle_alloc_error, Layout } ,
3
3
borrow:: { Borrow , BorrowMut } ,
4
+ iter:: repeat_n,
4
5
ptr:: NonNull ,
5
6
} ;
6
7
@@ -44,15 +45,16 @@ pub struct InterpretedInstance<'a, F, Ctx> {
44
45
#[ allow( dead_code) ]
45
46
pre_compute_buf : AlignedBuf ,
46
47
/// 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.
48
51
pre_compute_insns : Vec < PreComputeInstruction < ' a , F , Ctx > > ,
49
52
#[ cfg( feature = "tco" ) ]
50
53
pre_compute_max_size : usize ,
51
54
/// Handler function pointers for tail call optimization.
52
55
#[ cfg( feature = "tco" ) ]
53
56
handlers : Vec < Handler < F , Ctx > > ,
54
57
55
- pc_base : u32 ,
56
58
pc_start : u32 ,
57
59
58
60
init_memory : SparseMemoryImage ,
@@ -84,11 +86,7 @@ macro_rules! run {
84
86
#[ cfg( not( feature = "tco" ) ) ]
85
87
unsafe {
86
88
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) ;
92
90
}
93
91
#[ cfg( feature = "tco" ) ]
94
92
{
@@ -151,21 +149,19 @@ where
151
149
{
152
150
let program = & exe. program ;
153
151
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) ;
155
153
let mut split_pre_compute_buf =
156
154
split_pre_compute_buf ( program, & mut pre_compute_buf, pre_compute_max_size) ;
157
155
let pre_compute_insns = get_pre_compute_instructions :: < F , Ctx , E > (
158
156
program,
159
157
inventory,
160
158
& mut split_pre_compute_buf,
161
159
) ?;
162
- let pc_base = program. pc_base ;
163
160
let pc_start = exe. pc_start ;
164
161
let init_memory = exe. init_memory . clone ( ) ;
165
162
#[ 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 ( ) )
169
165
. zip_eq ( split_pre_compute_buf. iter_mut ( ) )
170
166
. enumerate ( )
171
167
. map (
@@ -191,7 +187,6 @@ where
191
187
system_config : inventory. config ( ) . clone ( ) ,
192
188
pre_compute_buf,
193
189
pre_compute_insns,
194
- pc_base,
195
190
pc_start,
196
191
init_memory,
197
192
#[ cfg( feature = "tco" ) ]
@@ -209,7 +204,7 @@ where
209
204
#[ cfg( feature = "tco" ) ]
210
205
#[ inline( always) ]
211
206
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) ;
213
208
// SAFETY:
214
209
// - we assume that pc is in bounds
215
210
// - pre_compute_buf is allocated for pre_compute_max_size * program_len bytes, with each
@@ -228,14 +223,6 @@ where
228
223
}
229
224
}
230
225
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
-
239
226
#[ cfg( feature = "tco" ) ]
240
227
#[ inline( always) ]
241
228
pub fn get_handler ( & self , pc : u32 ) -> Option < Handler < F , Ctx > > {
@@ -261,7 +248,7 @@ where
261
248
{
262
249
let program = & exe. program ;
263
250
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) ;
265
252
let mut split_pre_compute_buf =
266
253
split_pre_compute_buf ( program, & mut pre_compute_buf, pre_compute_max_size) ;
267
254
let pre_compute_insns = get_metered_pre_compute_instructions :: < F , Ctx , E > (
@@ -271,13 +258,11 @@ where
271
258
& mut split_pre_compute_buf,
272
259
) ?;
273
260
274
- let pc_base = program. pc_base ;
275
261
let pc_start = exe. pc_start ;
276
262
let init_memory = exe. init_memory . clone ( ) ;
277
263
#[ 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 ( ) )
281
266
. zip_eq ( split_pre_compute_buf. iter_mut ( ) )
282
267
. enumerate ( )
283
268
. map (
@@ -305,7 +290,6 @@ where
305
290
system_config : inventory. config ( ) . clone ( ) ,
306
291
pre_compute_buf,
307
292
pre_compute_insns,
308
- pc_base,
309
293
pc_start,
310
294
init_memory,
311
295
#[ cfg( feature = "tco" ) ]
@@ -448,8 +432,10 @@ where
448
432
}
449
433
}
450
434
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;
453
439
AlignedBuf :: uninit ( buf_len, pre_compute_max_size)
454
440
}
455
441
@@ -458,8 +444,9 @@ fn split_pre_compute_buf<'a, F>(
458
444
pre_compute_buf : & ' a mut AlignedBuf ,
459
445
pre_compute_max_size : usize ,
460
446
) -> 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;
463
450
// SAFETY:
464
451
// - pre_compute_buf.ptr was allocated with exactly buf_len bytes
465
452
// - lifetime 'a ensures the returned slices don't outlive the AlignedBuf
@@ -475,7 +462,6 @@ fn split_pre_compute_buf<'a, F>(
475
462
/// The `fn_ptrs` pointer to pre-computed buffers that outlive this function.
476
463
#[ inline( always) ]
477
464
unsafe fn execute_trampoline < F : PrimeField32 , Ctx : ExecutionCtxTrait > (
478
- pc_base : u32 ,
479
465
vm_state : & mut VmExecState < F , GuestMemory , Ctx > ,
480
466
fn_ptrs : & [ PreComputeInstruction < F , Ctx > ] ,
481
467
) {
@@ -487,16 +473,12 @@ unsafe fn execute_trampoline<F: PrimeField32, Ctx: ExecutionCtxTrait>(
487
473
if Ctx :: should_suspend ( vm_state) {
488
474
break ;
489
475
}
490
- let pc_index = get_pc_index ( pc_base , vm_state. pc ) ;
476
+ let pc_index = get_pc_index ( vm_state. pc ) ;
491
477
if let Some ( inst) = fn_ptrs. get ( pc_index) {
492
478
// SAFETY: pre_compute assumed to live long enough
493
479
unsafe { ( inst. handler ) ( inst. pre_compute , vm_state) } ;
494
480
} 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 ) ) ;
500
482
}
501
483
}
502
484
if vm_state
@@ -509,8 +491,8 @@ unsafe fn execute_trampoline<F: PrimeField32, Ctx: ExecutionCtxTrait>(
509
491
}
510
492
511
493
#[ 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
514
496
}
515
497
516
498
/// Bytes allocated according to the given Layout
@@ -647,15 +629,19 @@ where
647
629
Ctx : ExecutionCtxTrait ,
648
630
E : Executor < F > ,
649
631
{
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 ( ) )
653
638
. zip_eq ( pre_compute. iter_mut ( ) )
654
639
. enumerate ( )
655
640
. 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.
659
645
let buf: & mut [ u8 ] = unsafe { & mut * ( * buf as * mut [ u8 ] ) } ;
660
646
let pre_inst = if let Some ( ( inst, _) ) = inst_opt {
661
647
tracing:: trace!( "get_pre_compute_instruction {inst:?}" ) ;
@@ -679,9 +665,7 @@ where
679
665
} else {
680
666
// Dead instruction at this pc
681
667
PreComputeInstruction {
682
- handler : |_, vm_state| {
683
- vm_state. exit_code = Err ( ExecutionError :: Unreachable ( vm_state. pc ) ) ;
684
- } ,
668
+ handler : unreachable_handler,
685
669
pre_compute : buf,
686
670
}
687
671
} ;
@@ -701,15 +685,18 @@ where
701
685
Ctx : MeteredExecutionCtxTrait ,
702
686
E : MeteredExecutor < F > ,
703
687
{
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 ( ) )
707
693
. zip_eq ( pre_compute. iter_mut ( ) )
708
694
. enumerate ( )
709
695
. 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.
713
700
let buf: & mut [ u8 ] = unsafe { & mut * ( * buf as * mut [ u8 ] ) } ;
714
701
let pre_inst = if let Some ( ( inst, _) ) = inst_opt {
715
702
tracing:: trace!( "get_metered_pre_compute_instruction {inst:?}" ) ;
@@ -738,9 +725,7 @@ where
738
725
}
739
726
} else {
740
727
PreComputeInstruction {
741
- handler : |_, vm_state| {
742
- vm_state. exit_code = Err ( ExecutionError :: Unreachable ( vm_state. pc ) ) ;
743
- } ,
728
+ handler : unreachable_handler,
744
729
pre_compute : buf,
745
730
}
746
731
} ;
0 commit comments