@@ -642,6 +642,7 @@ pub enum Insn {
642642 state : InsnId ,
643643 } ,
644644 Send { recv : InsnId , cd : * const rb_call_data , blockiseq : IseqPtr , args : Vec < InsnId > , state : InsnId } ,
645+ SendForward { recv : InsnId , cd : * const rb_call_data , blockiseq : IseqPtr , args : Vec < InsnId > , state : InsnId } ,
645646 InvokeSuper { recv : InsnId , cd : * const rb_call_data , blockiseq : IseqPtr , args : Vec < InsnId > , state : InsnId } ,
646647 InvokeBlock { cd : * const rb_call_data , args : Vec < InsnId > , state : InsnId } ,
647648
@@ -902,6 +903,13 @@ impl<'a> std::fmt::Display for InsnPrinter<'a> {
902903 }
903904 Ok ( ( ) )
904905 }
906+ Insn :: SendForward { cd, args, blockiseq, .. } => {
907+ write ! ( f, "SendForward {:p}, :{}" , self . ptr_map. map_ptr( blockiseq) , ruby_call_method_name( * cd) ) ?;
908+ for arg in args {
909+ write ! ( f, ", {arg}" ) ?;
910+ }
911+ Ok ( ( ) )
912+ }
905913 Insn :: InvokeSuper { recv, blockiseq, args, .. } => {
906914 write ! ( f, "InvokeSuper {recv}, {:p}" , self . ptr_map. map_ptr( blockiseq) ) ?;
907915 for arg in args {
@@ -1422,6 +1430,13 @@ impl Function {
14221430 args : find_vec ! ( args) ,
14231431 state,
14241432 } ,
1433+ & SendForward { recv, cd, blockiseq, ref args, state } => SendForward {
1434+ recv : find ! ( recv) ,
1435+ cd,
1436+ blockiseq,
1437+ args : find_vec ! ( args) ,
1438+ state,
1439+ } ,
14251440 & InvokeSuper { recv, cd, blockiseq, ref args, state } => InvokeSuper {
14261441 recv : find ! ( recv) ,
14271442 cd,
@@ -1552,6 +1567,7 @@ impl Function {
15521567 Insn :: SendWithoutBlock { .. } => types:: BasicObject ,
15531568 Insn :: SendWithoutBlockDirect { .. } => types:: BasicObject ,
15541569 Insn :: Send { .. } => types:: BasicObject ,
1570+ Insn :: SendForward { .. } => types:: BasicObject ,
15551571 Insn :: InvokeSuper { .. } => types:: BasicObject ,
15561572 Insn :: InvokeBlock { .. } => types:: BasicObject ,
15571573 Insn :: InvokeBuiltin { return_type, .. } => return_type. unwrap_or ( types:: BasicObject ) ,
@@ -2430,6 +2446,7 @@ impl Function {
24302446 worklist. push_back ( state) ;
24312447 }
24322448 & Insn :: Send { recv, ref args, state, .. }
2449+ | & Insn :: SendForward { recv, ref args, state, .. }
24332450 | & Insn :: SendWithoutBlock { recv, ref args, state, .. }
24342451 | & Insn :: CCallVariadic { recv, ref args, state, .. }
24352452 | & Insn :: SendWithoutBlockDirect { recv, ref args, state, .. }
@@ -3774,13 +3791,44 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
37743791 let send = fun. push_insn ( block, Insn :: Send { recv, cd, blockiseq, args, state : exit_id } ) ;
37753792 state. stack_push ( send) ;
37763793
3777- // Reload locals that may have been modified by the blockiseq.
3778- // TODO: Avoid reloading locals that are not referenced by the blockiseq
3779- // or not used after this. Max thinks we could eventually DCE them.
3780- for local_idx in 0 ..state. locals . len ( ) {
3781- let ep_offset = local_idx_to_ep_offset ( iseq, local_idx) as u32 ;
3782- let val = fun. push_insn ( block, Insn :: GetLocal { ep_offset, level : 0 } ) ;
3783- state. setlocal ( ep_offset, val) ;
3794+ if !blockiseq. is_null ( ) {
3795+ // Reload locals that may have been modified by the blockiseq.
3796+ // TODO: Avoid reloading locals that are not referenced by the blockiseq
3797+ // or not used after this. Max thinks we could eventually DCE them.
3798+ for local_idx in 0 ..state. locals . len ( ) {
3799+ let ep_offset = local_idx_to_ep_offset ( iseq, local_idx) as u32 ;
3800+ let val = fun. push_insn ( block, Insn :: GetLocal { ep_offset, level : 0 } ) ;
3801+ state. setlocal ( ep_offset, val) ;
3802+ }
3803+ }
3804+ }
3805+ YARVINSN_sendforward => {
3806+ let cd: * const rb_call_data = get_arg ( pc, 0 ) . as_ptr ( ) ;
3807+ let blockiseq: IseqPtr = get_arg ( pc, 1 ) . as_iseq ( ) ;
3808+ let call_info = unsafe { rb_get_call_data_ci ( cd) } ;
3809+ let flags = unsafe { rb_vm_ci_flag ( call_info) } ;
3810+ let forwarding = ( flags & VM_CALL_FORWARDING ) != 0 ;
3811+ if let Err ( call_type) = unhandled_call_type ( flags) {
3812+ // Can't handle the call type; side-exit into the interpreter
3813+ let exit_id = fun. push_insn ( block, Insn :: Snapshot { state : exit_state } ) ;
3814+ fun. push_insn ( block, Insn :: SideExit { state : exit_id, reason : SideExitReason :: UnhandledCallType ( call_type) } ) ;
3815+ break ; // End the block
3816+ }
3817+ let argc = unsafe { vm_ci_argc ( ( * cd) . ci ) } ;
3818+
3819+ let args = state. stack_pop_n ( argc as usize + usize:: from ( forwarding) ) ?;
3820+ let recv = state. stack_pop ( ) ?;
3821+ let exit_id = fun. push_insn ( block, Insn :: Snapshot { state : exit_state } ) ;
3822+ let send_forward = fun. push_insn ( block, Insn :: SendForward { recv, cd, blockiseq, args, state : exit_id } ) ;
3823+ state. stack_push ( send_forward) ;
3824+
3825+ if !blockiseq. is_null ( ) {
3826+ // Reload locals that may have been modified by the blockiseq.
3827+ for local_idx in 0 ..state. locals . len ( ) {
3828+ let ep_offset = local_idx_to_ep_offset ( iseq, local_idx) as u32 ;
3829+ let val = fun. push_insn ( block, Insn :: GetLocal { ep_offset, level : 0 } ) ;
3830+ state. setlocal ( ep_offset, val) ;
3831+ }
37843832 }
37853833 }
37863834 YARVINSN_invokesuper => {
@@ -5315,7 +5363,6 @@ mod tests {
53155363 fn test@<compiled>:2:
53165364 bb0(v0:BasicObject, v1:BasicObject):
53175365 v6:BasicObject = Send v0, 0x1000, :foo, v1
5318- v7:BasicObject = GetLocal l0, EP@3
53195366 CheckInterrupts
53205367 Return v6
53215368 " ) ;
@@ -5457,14 +5504,33 @@ mod tests {
54575504 }
54585505
54595506 #[ test]
5460- fn test_cant_compile_forwarding ( ) {
5507+ fn test_compile_forwarding ( ) {
54615508 eval ( "
54625509 def test(...) = foo(...)
54635510 " ) ;
54645511 assert_snapshot ! ( hir_string( "test" ) , @r"
54655512 fn test@<compiled>:2:
54665513 bb0(v0:BasicObject, v1:BasicObject):
5467- SideExit UnhandledYARVInsn(sendforward)
5514+ v6:BasicObject = SendForward 0x1000, :foo, v1
5515+ CheckInterrupts
5516+ Return v6
5517+ " ) ;
5518+ }
5519+
5520+ #[ test]
5521+ fn test_compile_triple_dots_with_positional_args ( ) {
5522+ eval ( "
5523+ def test(a, ...) = foo(a, ...)
5524+ " ) ;
5525+ assert_snapshot ! ( hir_string( "test" ) , @r"
5526+ fn test@<compiled>:2:
5527+ bb0(v0:BasicObject, v1:BasicObject, v2:ArrayExact, v3:BasicObject, v4:BasicObject):
5528+ v5:NilClass = Const Value(nil)
5529+ v10:ArrayExact = ToArray v2
5530+ PatchPoint NoEPEscape(test)
5531+ GuardBlockParamProxy l0
5532+ v15:BasicObject[BlockParamProxy] = Const Value(VALUE(0x1000))
5533+ SideExit UnhandledYARVInsn(splatkw)
54685534 " ) ;
54695535 }
54705536
@@ -8274,7 +8340,6 @@ mod opt_tests {
82748340 GuardBlockParamProxy l0
82758341 v7:BasicObject[BlockParamProxy] = Const Value(VALUE(0x1000))
82768342 v9:BasicObject = Send v0, 0x1008, :tap, v7
8277- v10:BasicObject = GetLocal l0, EP@3
82788343 CheckInterrupts
82798344 Return v9
82808345 " ) ;
0 commit comments