@@ -939,6 +939,14 @@ pub enum ValidationError {
939939 DuplicateInstruction ( BlockId , InsnId ) ,
940940}
941941
942+ fn can_direct_send ( iseq : * const rb_iseq_t ) -> bool {
943+ if unsafe { rb_get_iseq_flags_has_rest ( iseq) } { false }
944+ else if unsafe { rb_get_iseq_flags_has_opt ( iseq) } { false }
945+ else if unsafe { rb_get_iseq_flags_has_kw ( iseq) } { false }
946+ else if unsafe { rb_get_iseq_flags_has_kwrest ( iseq) } { false }
947+ else if unsafe { rb_get_iseq_flags_has_block ( iseq) } { false }
948+ else { true }
949+ }
942950
943951/// A [`Function`], which is analogous to a Ruby ISeq, is a control-flow graph of [`Block`]s
944952/// containing instructions.
@@ -1507,8 +1515,13 @@ impl Function {
15071515 // TODO(max): Allow non-iseq; cache cme
15081516 self . push_insn_id ( block, insn_id) ; continue ;
15091517 }
1510- self . push_insn ( block, Insn :: PatchPoint { invariant : Invariant :: MethodRedefined { klass, method : mid } , state } ) ;
1518+ // Only specialize positional-positional calls
1519+ // TODO(max): Handle other kinds of parameter passing
15111520 let iseq = unsafe { get_def_iseq_ptr ( ( * cme) . def ) } ;
1521+ if !can_direct_send ( iseq) {
1522+ self . push_insn_id ( block, insn_id) ; continue ;
1523+ }
1524+ self . push_insn ( block, Insn :: PatchPoint { invariant : Invariant :: MethodRedefined { klass, method : mid } , state } ) ;
15121525 if let Some ( expected) = guard_equal_to {
15131526 self_val = self . push_insn ( block, Insn :: GuardBitEquals { val : self_val, expected, state } ) ;
15141527 }
@@ -6313,6 +6326,88 @@ mod opt_tests {
63136326 "# ] ] ) ;
63146327 }
63156328
6329+ #[ test]
6330+ fn dont_specialize_call_to_iseq_with_opt ( ) {
6331+ eval ( "
6332+ def foo(arg=1) = 1
6333+ def test = foo 1
6334+ test
6335+ test
6336+ " ) ;
6337+ assert_optimized_method_hir ( "test" , expect ! [ [ r#"
6338+ fn test@<compiled>:3:
6339+ bb0(v0:BasicObject):
6340+ v2:Fixnum[1] = Const Value(1)
6341+ v4:BasicObject = SendWithoutBlock v0, :foo, v2
6342+ Return v4
6343+ "# ] ] ) ;
6344+ }
6345+
6346+ #[ test]
6347+ fn dont_specialize_call_to_iseq_with_block ( ) {
6348+ eval ( "
6349+ def foo(&block) = 1
6350+ def test = foo {|| }
6351+ test
6352+ test
6353+ " ) ;
6354+ assert_optimized_method_hir ( "test" , expect ! [ [ r#"
6355+ fn test@<compiled>:3:
6356+ bb0(v0:BasicObject):
6357+ v3:BasicObject = Send v0, 0x1000, :foo
6358+ Return v3
6359+ "# ] ] ) ;
6360+ }
6361+
6362+ #[ test]
6363+ fn dont_specialize_call_to_iseq_with_rest ( ) {
6364+ eval ( "
6365+ def foo(*args) = 1
6366+ def test = foo 1
6367+ test
6368+ test
6369+ " ) ;
6370+ assert_optimized_method_hir ( "test" , expect ! [ [ r#"
6371+ fn test@<compiled>:3:
6372+ bb0(v0:BasicObject):
6373+ v2:Fixnum[1] = Const Value(1)
6374+ v4:BasicObject = SendWithoutBlock v0, :foo, v2
6375+ Return v4
6376+ "# ] ] ) ;
6377+ }
6378+
6379+ #[ test]
6380+ fn dont_specialize_call_to_iseq_with_kw ( ) {
6381+ eval ( "
6382+ def foo(a:) = 1
6383+ def test = foo(a: 1)
6384+ test
6385+ test
6386+ " ) ;
6387+ assert_optimized_method_hir ( "test" , expect ! [ [ r#"
6388+ fn test@<compiled>:3:
6389+ bb0(v0:BasicObject):
6390+ v2:Fixnum[1] = Const Value(1)
6391+ SideExit UnknownCallType
6392+ "# ] ] ) ;
6393+ }
6394+
6395+ #[ test]
6396+ fn dont_specialize_call_to_iseq_with_kwrest ( ) {
6397+ eval ( "
6398+ def foo(**args) = 1
6399+ def test = foo(a: 1)
6400+ test
6401+ test
6402+ " ) ;
6403+ assert_optimized_method_hir ( "test" , expect ! [ [ r#"
6404+ fn test@<compiled>:3:
6405+ bb0(v0:BasicObject):
6406+ v2:Fixnum[1] = Const Value(1)
6407+ SideExit UnknownCallType
6408+ "# ] ] ) ;
6409+ }
6410+
63166411 #[ test]
63176412 fn string_bytesize_simple ( ) {
63186413 eval ( "
0 commit comments