@@ -282,6 +282,9 @@ fn try_compile_ssa_trace(
282282 let call_value_block = call_value_exits
283283 . contains_key ( & exit. id )
284284 . then ( || b. create_block ( ) ) ;
285+ let interrupt_block = ( interrupt_settings. is_some ( )
286+ && internal_links. iter ( ) . any ( |link| link. exit == exit. id ) )
287+ . then ( || b. create_block ( ) ) ;
285288 for value in & inputs {
286289 let Some ( repr) = value_reprs. get ( value) . copied ( ) else {
287290 return Ok ( None ) ;
@@ -294,13 +297,17 @@ fn try_compile_ssa_trace(
294297 if let Some ( call_value_block) = call_value_block {
295298 b. append_block_param ( call_value_block, ty) ;
296299 }
300+ if let Some ( interrupt_block) = interrupt_block {
301+ b. append_block_param ( interrupt_block, ty) ;
302+ }
297303 }
298304 exit_specs. insert (
299305 exit. id ,
300306 SsaExitLowering {
301307 trace_exit_block,
302308 halted_block,
303309 call_value_block,
310+ interrupt_block,
304311 inputs,
305312 } ,
306313 ) ;
@@ -330,6 +337,8 @@ fn try_compile_ssa_trace(
330337 ssa. entry_stack_depth ,
331338 entry_local_count,
332339 ) ?;
340+ let interrupt_ops_to_advance = u32:: try_from ( trace. op_names . len ( ) . max ( 1 ) )
341+ . map_err ( |_| VmError :: JitNative ( "trace op count exceeds u32" . to_string ( ) ) ) ?;
333342 let lower_ctx = SsaLowerCtx {
334343 vm_ptr,
335344 active_stack_base,
@@ -349,6 +358,8 @@ fn try_compile_ssa_trace(
349358 borrowed_array_gets : & borrowed_array_gets,
350359 value_reprs : & value_reprs,
351360 tagged_constant_addrs : & tagged_constant_addrs,
361+ interrupt_settings,
362+ interrupt_ops_to_advance,
352363 } ;
353364 let root_ip = b. ins ( ) . iconst (
354365 pointer_type,
@@ -376,8 +387,6 @@ fn try_compile_ssa_trace(
376387 b. ins ( ) . jump ( entry_handle, & entry_args) ;
377388
378389 let charge_blocks = ssa_interrupt_charge_blocks ( ssa) ;
379- let ops_to_advance = u32:: try_from ( trace. op_names . len ( ) . max ( 1 ) )
380- . map_err ( |_| VmError :: JitNative ( "trace op count exceeds u32" . to_string ( ) ) ) ?;
381390 for block in & ssa. blocks {
382391 let handle = * block_handles
383392 . get ( & block. id )
@@ -388,15 +397,16 @@ fn try_compile_ssa_trace(
388397 for ( param, lowered) in block. params . iter ( ) . zip ( block_params. iter ( ) . copied ( ) ) {
389398 values. insert ( param. value . id , lowered) ;
390399 }
391- if charge_blocks. contains ( & block. id )
400+ if internal_links. is_empty ( )
401+ && charge_blocks. contains ( & block. id )
392402 && let Some ( settings) = interrupt_settings
393403 {
394404 emit_interrupt_tick_inline (
395405 & mut b,
396406 vm_ptr,
397407 exit_block,
398408 offsets,
399- ops_to_advance ,
409+ lower_ctx . interrupt_ops_to_advance ,
400410 settings,
401411 ) ;
402412 }
@@ -446,6 +456,9 @@ fn try_compile_ssa_trace(
446456 } ,
447457 ) ?;
448458 }
459+ if spec. interrupt_block . is_some ( ) {
460+ lower_ssa_exit_block ( & mut b, lower_ctx, exit, spec, SsaExitAction :: InterruptYield ) ?;
461+ }
449462 }
450463
451464 b. switch_to_block ( exit_block) ;
@@ -500,6 +513,7 @@ struct SsaExitLowering {
500513 trace_exit_block : Block ,
501514 halted_block : Block ,
502515 call_value_block : Option < Block > ,
516+ interrupt_block : Option < Block > ,
503517 inputs : Vec < SsaValueId > ,
504518}
505519
@@ -514,6 +528,7 @@ enum SsaExitAction {
514528 call_ip : usize ,
515529 resume_ip : usize ,
516530 } ,
531+ InterruptYield ,
517532}
518533
519534#[ derive( Clone , Copy ) ]
@@ -636,6 +651,8 @@ struct SsaLowerCtx<'a> {
636651 borrowed_array_gets : & ' a BTreeSet < SsaValueId > ,
637652 value_reprs : & ' a HashMap < SsaValueId , SsaValueRepr > ,
638653 tagged_constant_addrs : & ' a HashMap < SsaValueId , usize > ,
654+ interrupt_settings : Option < NativeInterruptSettings > ,
655+ interrupt_ops_to_advance : u32 ,
639656}
640657
641658#[ derive( Clone , Copy ) ]
@@ -3209,7 +3226,7 @@ fn lower_ssa_terminator(
32093226 let condition = * values
32103227 . get ( condition)
32113228 . ok_or_else ( || VmError :: JitNative ( "SSA branch condition missing" . to_string ( ) ) ) ?;
3212- let true_target = ssa_branch_target_block (
3229+ let ( true_block , true_args , true_pending ) = ssa_branch_target_block (
32133230 b,
32143231 ctx,
32153232 if_true,
@@ -3218,7 +3235,7 @@ fn lower_ssa_terminator(
32183235 exit_specs,
32193236 internal_links,
32203237 ) ?;
3221- let false_target = ssa_branch_target_block (
3238+ let ( false_block , false_args , false_pending ) = ssa_branch_target_block (
32223239 b,
32233240 ctx,
32243241 if_false,
@@ -3227,25 +3244,42 @@ fn lower_ssa_terminator(
32273244 exit_specs,
32283245 internal_links,
32293246 ) ?;
3230- let true_args = ssa_block_args ( true_target. 1 ) ;
3231- let false_args = ssa_block_args ( false_target. 1 ) ;
3232- b. ins ( ) . brif (
3233- condition,
3234- true_target. 0 ,
3235- & true_args,
3236- false_target. 0 ,
3237- & false_args,
3238- ) ;
3247+ let true_args = ssa_block_args ( true_args) ;
3248+ let false_args = ssa_block_args ( false_args) ;
3249+ b. ins ( )
3250+ . brif ( condition, true_block, & true_args, false_block, & false_args) ;
3251+ if let Some ( pending) = true_pending {
3252+ lower_pending_internal_link_gate ( b, ctx, pending) ;
3253+ }
3254+ if let Some ( pending) = false_pending {
3255+ lower_pending_internal_link_gate ( b, ctx, pending) ;
3256+ }
32393257 }
32403258 SsaTerminator :: Exit { exit } => {
32413259 if let Some ( ( link, slots) ) = internal_links. find ( * exit) {
32423260 let handle = * block_handles. get ( & link. child_entry ) . ok_or_else ( || {
32433261 VmError :: JitNative ( "fused SSA child entry block missing" . to_string ( ) )
32443262 } ) ?;
3245- let args = lower_internal_link_args ( b, ctx, link, slots, values) ?;
3246- increment_native_region_edge_count ( b, ctx) ;
3263+ let child_args = lower_internal_link_args ( b, ctx, link, slots, values) ?;
3264+ let spec = exit_specs. get ( exit) . ok_or_else ( || {
3265+ VmError :: JitNative ( "SSA internal exit lowering missing" . to_string ( ) )
3266+ } ) ?;
3267+ let restore_args = spec
3268+ . inputs
3269+ . iter ( )
3270+ . map ( |value| {
3271+ values. get ( value) . copied ( ) . ok_or_else ( || {
3272+ VmError :: JitNative ( "SSA internal restore arg missing" . to_string ( ) )
3273+ } )
3274+ } )
3275+ . collect :: < VmResult < Vec < _ > > > ( ) ?;
3276+ let ( target, args, pending) =
3277+ lower_internal_link_target ( b, ctx, handle, child_args, spec, restore_args) ?;
32473278 let args = ssa_block_args ( args) ;
3248- b. ins ( ) . jump ( handle, & args) ;
3279+ b. ins ( ) . jump ( target, & args) ;
3280+ if let Some ( pending) = pending {
3281+ lower_pending_internal_link_gate ( b, ctx, pending) ;
3282+ }
32493283 return Ok ( ( ) ) ;
32503284 }
32513285 let spec = exit_specs
@@ -3311,7 +3345,11 @@ fn ssa_branch_target_block(
33113345 block_handles : & HashMap < crate :: vm:: jit:: ir:: SsaBlockId , Block > ,
33123346 exit_specs : & HashMap < SsaExitId , SsaExitLowering > ,
33133347 internal_links : InternalRegionLinks < ' _ > ,
3314- ) -> VmResult < ( Block , Vec < cranelift_codegen:: ir:: Value > ) > {
3348+ ) -> VmResult < (
3349+ Block ,
3350+ Vec < cranelift_codegen:: ir:: Value > ,
3351+ Option < PendingInternalLinkGate > ,
3352+ ) > {
33153353 match target {
33163354 SsaBranchTarget :: Block { target, args } => {
33173355 let handle = * block_handles
@@ -3326,16 +3364,29 @@ fn ssa_branch_target_block(
33263364 . ok_or_else ( || VmError :: JitNative ( "SSA branch arg missing" . to_string ( ) ) )
33273365 } )
33283366 . collect :: < VmResult < Vec < _ > > > ( ) ?;
3329- Ok ( ( handle, lowered_args) )
3367+ Ok ( ( handle, lowered_args, None ) )
33303368 }
33313369 SsaBranchTarget :: Exit ( exit) => {
33323370 if let Some ( ( link, slots) ) = internal_links. find ( * exit) {
33333371 let handle = * block_handles. get ( & link. child_entry ) . ok_or_else ( || {
33343372 VmError :: JitNative ( "fused SSA child entry block missing" . to_string ( ) )
33353373 } ) ?;
3336- let args = lower_internal_link_args ( b, ctx, link, slots, values) ?;
3337- increment_native_region_edge_count ( b, ctx) ;
3338- return Ok ( ( handle, args) ) ;
3374+ let child_args = lower_internal_link_args ( b, ctx, link, slots, values) ?;
3375+ let spec = exit_specs. get ( exit) . ok_or_else ( || {
3376+ VmError :: JitNative ( "SSA internal branch exit lowering missing" . to_string ( ) )
3377+ } ) ?;
3378+ let restore_args = spec
3379+ . inputs
3380+ . iter ( )
3381+ . map ( |value| {
3382+ values. get ( value) . copied ( ) . ok_or_else ( || {
3383+ VmError :: JitNative (
3384+ "SSA internal branch restore arg missing" . to_string ( ) ,
3385+ )
3386+ } )
3387+ } )
3388+ . collect :: < VmResult < Vec < _ > > > ( ) ?;
3389+ return lower_internal_link_target ( b, ctx, handle, child_args, spec, restore_args) ;
33393390 }
33403391 let spec = exit_specs. get ( exit) . ok_or_else ( || {
33413392 VmError :: JitNative ( "SSA branch exit lowering missing" . to_string ( ) )
@@ -3350,11 +3401,85 @@ fn ssa_branch_target_block(
33503401 . ok_or_else ( || VmError :: JitNative ( "SSA exit arg missing" . to_string ( ) ) )
33513402 } )
33523403 . collect :: < VmResult < Vec < _ > > > ( ) ?;
3353- Ok ( ( spec. trace_exit_block , lowered_args) )
3404+ Ok ( ( spec. trace_exit_block , lowered_args, None ) )
33543405 }
33553406 }
33563407}
33573408
3409+ struct PendingInternalLinkGate {
3410+ gate_block : Block ,
3411+ child_handle : Block ,
3412+ child_arg_count : usize ,
3413+ interrupt_block : Block ,
3414+ }
3415+
3416+ fn lower_internal_link_target (
3417+ b : & mut FunctionBuilder ,
3418+ ctx : SsaLowerCtx < ' _ > ,
3419+ child_handle : Block ,
3420+ child_args : Vec < cranelift_codegen:: ir:: Value > ,
3421+ exit_spec : & SsaExitLowering ,
3422+ restore_args : Vec < cranelift_codegen:: ir:: Value > ,
3423+ ) -> VmResult < (
3424+ Block ,
3425+ Vec < cranelift_codegen:: ir:: Value > ,
3426+ Option < PendingInternalLinkGate > ,
3427+ ) > {
3428+ let Some ( _settings) = ctx. interrupt_settings else {
3429+ increment_native_region_edge_count ( b, ctx) ;
3430+ return Ok ( ( child_handle, child_args, None ) ) ;
3431+ } ;
3432+ let gate_block = b. create_block ( ) ;
3433+ for value in child_args. iter ( ) . chain ( & restore_args) {
3434+ b. append_block_param ( gate_block, b. func . dfg . value_type ( * value) ) ;
3435+ }
3436+ let child_arg_count = child_args. len ( ) ;
3437+ let mut gate_args = child_args;
3438+ gate_args. extend ( restore_args) ;
3439+ let interrupt_block = exit_spec
3440+ . interrupt_block
3441+ . ok_or_else ( || VmError :: JitNative ( "SSA internal interrupt block missing" . to_string ( ) ) ) ?;
3442+ Ok ( (
3443+ gate_block,
3444+ gate_args,
3445+ Some ( PendingInternalLinkGate {
3446+ gate_block,
3447+ child_handle,
3448+ child_arg_count,
3449+ interrupt_block,
3450+ } ) ,
3451+ ) )
3452+ }
3453+
3454+ fn lower_pending_internal_link_gate (
3455+ b : & mut FunctionBuilder ,
3456+ ctx : SsaLowerCtx < ' _ > ,
3457+ pending : PendingInternalLinkGate ,
3458+ ) {
3459+ let settings = ctx
3460+ . interrupt_settings
3461+ . expect ( "pending internal link gate requires interrupt settings" ) ;
3462+ b. switch_to_block ( pending. gate_block ) ;
3463+ let gate_params = b. block_params ( pending. gate_block ) . to_vec ( ) ;
3464+ let interrupt_status_block = b. create_block ( ) ;
3465+ b. append_block_param ( interrupt_status_block, types:: I32 ) ;
3466+ emit_interrupt_tick_inline (
3467+ b,
3468+ ctx. vm_ptr ,
3469+ interrupt_status_block,
3470+ ctx. offsets ,
3471+ ctx. interrupt_ops_to_advance ,
3472+ settings,
3473+ ) ;
3474+ increment_native_region_edge_count ( b, ctx) ;
3475+ let child_params = ssa_block_args ( gate_params[ ..pending. child_arg_count ] . to_vec ( ) ) ;
3476+ b. ins ( ) . jump ( pending. child_handle , & child_params) ;
3477+
3478+ b. switch_to_block ( interrupt_status_block) ;
3479+ let restore_params = ssa_block_args ( gate_params[ pending. child_arg_count ..] . to_vec ( ) ) ;
3480+ b. ins ( ) . jump ( pending. interrupt_block , & restore_params) ;
3481+ }
3482+
33583483fn increment_native_region_edge_count ( b : & mut FunctionBuilder , ctx : SsaLowerCtx < ' _ > ) {
33593484 let count = b. ins ( ) . load (
33603485 types:: I64 ,
@@ -3401,6 +3526,7 @@ fn ssa_exit_action_status(
34013526 action : SsaExitAction ,
34023527) -> VmResult < cranelift_codegen:: ir:: Value > {
34033528 match action {
3529+ SsaExitAction :: InterruptYield => Ok ( b. ins ( ) . iconst ( types:: I32 , STATUS_OUT_OF_FUEL as i64 ) ) ,
34043530 SsaExitAction :: Return => ssa_leave_frame_status (
34053531 b,
34063532 pointer_type,
@@ -3482,6 +3608,9 @@ fn lower_ssa_exit_block(
34823608 SsaExitAction :: CallValue { .. } => spec
34833609 . call_value_block
34843610 . ok_or_else ( || VmError :: JitNative ( "SSA call-value exit block missing" . to_string ( ) ) ) ?,
3611+ SsaExitAction :: InterruptYield => spec
3612+ . interrupt_block
3613+ . ok_or_else ( || VmError :: JitNative ( "SSA interrupt exit block missing" . to_string ( ) ) ) ?,
34853614 } ;
34863615 b. switch_to_block ( block) ;
34873616 let block_params = b. block_params ( block) . to_vec ( ) ;
0 commit comments