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,22 +86,14 @@ 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
{
95
93
tracing:: debug!( "execute_tco" ) ;
96
- let handler = $interpreter. get_handler( $exec_state. pc) . ok_or(
97
- ExecutionError :: PcOutOfBounds {
98
- pc: $exec_state. pc,
99
- pc_base: $interpreter. pc_base,
100
- program_len: $interpreter. handlers. len( ) ,
101
- } ,
102
- ) ?;
94
+ let handler = $interpreter
95
+ . get_handler( $exec_state. pc)
96
+ . ok_or( ExecutionError :: PcOutOfBounds ( $exec_state. pc) ) ?;
103
97
// SAFETY:
104
98
// - handler is generated by Executor, MeteredExecutor traits
105
99
// - it is the responsibility of each Executor to ensure handler is safe given a
@@ -151,27 +145,25 @@ where
151
145
{
152
146
let program = & exe. program ;
153
147
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) ;
148
+ let mut pre_compute_buf = alloc_pre_compute_buf ( program, pre_compute_max_size) ;
155
149
let mut split_pre_compute_buf =
156
150
split_pre_compute_buf ( program, & mut pre_compute_buf, pre_compute_max_size) ;
157
151
let pre_compute_insns = get_pre_compute_instructions :: < F , Ctx , E > (
158
152
program,
159
153
inventory,
160
154
& mut split_pre_compute_buf,
161
155
) ?;
162
- let pc_base = program. pc_base ;
163
156
let pc_start = exe. pc_start ;
164
157
let init_memory = exe. init_memory . clone ( ) ;
165
158
#[ cfg( feature = "tco" ) ]
166
- let handlers = program
167
- . instructions_and_debug_infos
168
- . iter ( )
159
+ let handlers = repeat_n ( & None , get_pc_index ( program. pc_base ) )
160
+ . chain ( program. instructions_and_debug_infos . iter ( ) )
169
161
. zip_eq ( split_pre_compute_buf. iter_mut ( ) )
170
162
. enumerate ( )
171
163
. map (
172
164
|( pc_idx, ( inst_opt, pre_compute) ) | -> Result < Handler < F , Ctx > , StaticProgramError > {
173
165
if let Some ( ( inst, _) ) = inst_opt {
174
- let pc = pc_base + pc_idx as u32 * DEFAULT_PC_STEP ;
166
+ let pc = pc_idx as u32 * DEFAULT_PC_STEP ;
175
167
if get_system_opcode_handler :: < F , Ctx > ( inst, pre_compute) . is_some ( ) {
176
168
Ok ( terminate_execute_e12_tco_handler)
177
169
} else {
@@ -191,7 +183,6 @@ where
191
183
system_config : inventory. config ( ) . clone ( ) ,
192
184
pre_compute_buf,
193
185
pre_compute_insns,
194
- pc_base,
195
186
pc_start,
196
187
init_memory,
197
188
#[ cfg( feature = "tco" ) ]
@@ -209,7 +200,7 @@ where
209
200
#[ cfg( feature = "tco" ) ]
210
201
#[ inline( always) ]
211
202
pub fn get_pre_compute ( & self , pc : u32 ) -> & [ u8 ] {
212
- let pc_idx = get_pc_index ( self . pc_base , pc) ;
203
+ let pc_idx = get_pc_index ( pc) ;
213
204
// SAFETY:
214
205
// - we assume that pc is in bounds
215
206
// - pre_compute_buf is allocated for pre_compute_max_size * program_len bytes, with each
@@ -228,18 +219,10 @@ where
228
219
}
229
220
}
230
221
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
222
#[ cfg( feature = "tco" ) ]
240
223
#[ inline( always) ]
241
224
pub fn get_handler ( & self , pc : u32 ) -> Option < Handler < F , Ctx > > {
242
- let pc_idx = get_pc_index ( self . pc_base , pc) ;
225
+ let pc_idx = get_pc_index ( pc) ;
243
226
self . handlers . get ( pc_idx) . copied ( )
244
227
}
245
228
}
@@ -261,7 +244,7 @@ where
261
244
{
262
245
let program = & exe. program ;
263
246
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) ;
247
+ let mut pre_compute_buf = alloc_pre_compute_buf ( program, pre_compute_max_size) ;
265
248
let mut split_pre_compute_buf =
266
249
split_pre_compute_buf ( program, & mut pre_compute_buf, pre_compute_max_size) ;
267
250
let pre_compute_insns = get_metered_pre_compute_instructions :: < F , Ctx , E > (
@@ -271,19 +254,17 @@ where
271
254
& mut split_pre_compute_buf,
272
255
) ?;
273
256
274
- let pc_base = program. pc_base ;
275
257
let pc_start = exe. pc_start ;
276
258
let init_memory = exe. init_memory . clone ( ) ;
277
259
#[ cfg( feature = "tco" ) ]
278
- let handlers = program
279
- . instructions_and_debug_infos
280
- . iter ( )
260
+ let handlers = repeat_n ( & None , get_pc_index ( program. pc_base ) )
261
+ . chain ( program. instructions_and_debug_infos . iter ( ) )
281
262
. zip_eq ( split_pre_compute_buf. iter_mut ( ) )
282
263
. enumerate ( )
283
264
. map (
284
265
|( pc_idx, ( inst_opt, pre_compute) ) | -> Result < Handler < F , Ctx > , StaticProgramError > {
285
266
if let Some ( ( inst, _) ) = inst_opt {
286
- let pc = pc_base + pc_idx as u32 * DEFAULT_PC_STEP ;
267
+ let pc = pc_idx as u32 * DEFAULT_PC_STEP ;
287
268
if get_system_opcode_handler :: < F , Ctx > ( inst, pre_compute) . is_some ( ) {
288
269
Ok ( terminate_execute_e12_tco_handler)
289
270
} else {
@@ -305,7 +286,6 @@ where
305
286
system_config : inventory. config ( ) . clone ( ) ,
306
287
pre_compute_buf,
307
288
pre_compute_insns,
308
- pc_base,
309
289
pc_start,
310
290
init_memory,
311
291
#[ cfg( feature = "tco" ) ]
@@ -448,8 +428,10 @@ where
448
428
}
449
429
}
450
430
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;
431
+ fn alloc_pre_compute_buf < F > ( program : & Program < F > , pre_compute_max_size : usize ) -> AlignedBuf {
432
+ let base_idx = get_pc_index ( program. pc_base ) ;
433
+ let padded_program_len = base_idx + program. instructions_and_debug_infos . len ( ) ;
434
+ let buf_len = padded_program_len * pre_compute_max_size;
453
435
AlignedBuf :: uninit ( buf_len, pre_compute_max_size)
454
436
}
455
437
@@ -458,8 +440,9 @@ fn split_pre_compute_buf<'a, F>(
458
440
pre_compute_buf : & ' a mut AlignedBuf ,
459
441
pre_compute_max_size : usize ,
460
442
) -> Vec < & ' a mut [ u8 ] > {
461
- let program_len = program. instructions_and_debug_infos . len ( ) ;
462
- let buf_len = program_len * pre_compute_max_size;
443
+ let base_idx = get_pc_index ( program. pc_base ) ;
444
+ let padded_program_len = base_idx + program. instructions_and_debug_infos . len ( ) ;
445
+ let buf_len = padded_program_len * pre_compute_max_size;
463
446
// SAFETY:
464
447
// - pre_compute_buf.ptr was allocated with exactly buf_len bytes
465
448
// - lifetime 'a ensures the returned slices don't outlive the AlignedBuf
@@ -475,7 +458,6 @@ fn split_pre_compute_buf<'a, F>(
475
458
/// The `fn_ptrs` pointer to pre-computed buffers that outlive this function.
476
459
#[ inline( always) ]
477
460
unsafe fn execute_trampoline < F : PrimeField32 , Ctx : ExecutionCtxTrait > (
478
- pc_base : u32 ,
479
461
vm_state : & mut VmExecState < F , GuestMemory , Ctx > ,
480
462
fn_ptrs : & [ PreComputeInstruction < F , Ctx > ] ,
481
463
) {
@@ -487,16 +469,12 @@ unsafe fn execute_trampoline<F: PrimeField32, Ctx: ExecutionCtxTrait>(
487
469
if Ctx :: should_suspend ( vm_state) {
488
470
break ;
489
471
}
490
- let pc_index = get_pc_index ( pc_base , vm_state. pc ) ;
472
+ let pc_index = get_pc_index ( vm_state. pc ) ;
491
473
if let Some ( inst) = fn_ptrs. get ( pc_index) {
492
474
// SAFETY: pre_compute assumed to live long enough
493
475
unsafe { ( inst. handler ) ( inst. pre_compute , vm_state) } ;
494
476
} else {
495
- vm_state. exit_code = Err ( ExecutionError :: PcOutOfBounds {
496
- pc : vm_state. pc ,
497
- pc_base,
498
- program_len : fn_ptrs. len ( ) ,
499
- } ) ;
477
+ vm_state. exit_code = Err ( ExecutionError :: PcOutOfBounds ( vm_state. pc ) ) ;
500
478
}
501
479
}
502
480
if vm_state
@@ -509,8 +487,8 @@ unsafe fn execute_trampoline<F: PrimeField32, Ctx: ExecutionCtxTrait>(
509
487
}
510
488
511
489
#[ inline( always) ]
512
- pub fn get_pc_index ( pc_base : u32 , pc : u32 ) -> usize {
513
- ( ( pc - pc_base ) / DEFAULT_PC_STEP ) as usize
490
+ pub fn get_pc_index ( pc : u32 ) -> usize {
491
+ ( pc / DEFAULT_PC_STEP ) as usize
514
492
}
515
493
516
494
/// Bytes allocated according to the given Layout
@@ -647,15 +625,19 @@ where
647
625
Ctx : ExecutionCtxTrait ,
648
626
E : Executor < F > ,
649
627
{
650
- program
651
- . instructions_and_debug_infos
652
- . iter ( )
628
+ let unreachable_handler: ExecuteFunc < F , Ctx > = |_, vm_state| {
629
+ vm_state. exit_code = Err ( ExecutionError :: Unreachable ( vm_state. pc ) ) ;
630
+ } ;
631
+
632
+ repeat_n ( & None , get_pc_index ( program. pc_base ) )
633
+ . chain ( program. instructions_and_debug_infos . iter ( ) )
653
634
. zip_eq ( pre_compute. iter_mut ( ) )
654
635
. enumerate ( )
655
636
. 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.
637
+ // SAFETY: we cast to raw pointer and then borrow to remove the lifetime. This
638
+ // is safe only in the current context because `buf` comes
639
+ // from `pre_compute_buf` which will outlive the returned
640
+ // `PreComputeInstruction`s.
659
641
let buf: & mut [ u8 ] = unsafe { & mut * ( * buf as * mut [ u8 ] ) } ;
660
642
let pre_inst = if let Some ( ( inst, _) ) = inst_opt {
661
643
tracing:: trace!( "get_pre_compute_instruction {inst:?}" ) ;
@@ -679,9 +661,7 @@ where
679
661
} else {
680
662
// Dead instruction at this pc
681
663
PreComputeInstruction {
682
- handler : |_, vm_state| {
683
- vm_state. exit_code = Err ( ExecutionError :: Unreachable ( vm_state. pc ) ) ;
684
- } ,
664
+ handler : unreachable_handler,
685
665
pre_compute : buf,
686
666
}
687
667
} ;
@@ -701,15 +681,18 @@ where
701
681
Ctx : MeteredExecutionCtxTrait ,
702
682
E : MeteredExecutor < F > ,
703
683
{
704
- program
705
- . instructions_and_debug_infos
706
- . iter ( )
684
+ let unreachable_handler: ExecuteFunc < F , Ctx > = |_, vm_state| {
685
+ vm_state. exit_code = Err ( ExecutionError :: Unreachable ( vm_state. pc ) ) ;
686
+ } ;
687
+ repeat_n ( & None , get_pc_index ( program. pc_base ) )
688
+ . chain ( program. instructions_and_debug_infos . iter ( ) )
707
689
. zip_eq ( pre_compute. iter_mut ( ) )
708
690
. enumerate ( )
709
691
. 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.
692
+ // SAFETY: we cast to raw pointer and then borrow to remove the lifetime. This
693
+ // is safe only in the current context because `buf` comes
694
+ // from `pre_compute_buf` which will outlive the returned
695
+ // `PreComputeInstruction`s.
713
696
let buf: & mut [ u8 ] = unsafe { & mut * ( * buf as * mut [ u8 ] ) } ;
714
697
let pre_inst = if let Some ( ( inst, _) ) = inst_opt {
715
698
tracing:: trace!( "get_metered_pre_compute_instruction {inst:?}" ) ;
@@ -738,9 +721,7 @@ where
738
721
}
739
722
} else {
740
723
PreComputeInstruction {
741
- handler : |_, vm_state| {
742
- vm_state. exit_code = Err ( ExecutionError :: Unreachable ( vm_state. pc ) ) ;
743
- } ,
724
+ handler : unreachable_handler,
744
725
pre_compute : buf,
745
726
}
746
727
} ;
0 commit comments