@@ -1379,6 +1379,7 @@ enum IseqReturn {
13791379 Value(VALUE),
13801380 LocalVariable(u32),
13811381 Receiver,
1382+ InvokeLeafBuiltin(rb_builtin_function),
13821383}
13831384
13841385unsafe extern "C" {
@@ -1390,10 +1391,6 @@ fn iseq_get_return_value(iseq: IseqPtr, captured_opnd: Option<InsnId>, ci_flags:
13901391 // Expect only two instructions and one possible operand
13911392 // NOTE: If an ISEQ has an optional keyword parameter with a default value that requires
13921393 // computation, the ISEQ will always have more than two instructions and won't be inlined.
1393- let iseq_size = unsafe { get_iseq_encoded_size(iseq) };
1394- if !(2..=3).contains(&iseq_size) {
1395- return None;
1396- }
13971394
13981395 // Get the first two instructions
13991396 let first_insn = iseq_opcode_at_idx(iseq, 0);
@@ -1436,6 +1433,16 @@ fn iseq_get_return_value(iseq: IseqPtr, captured_opnd: Option<InsnId>, ci_flags:
14361433 YARVINSN_putobject_INT2FIX_1_ => Some(IseqReturn::Value(VALUE::fixnum_from_usize(1))),
14371434 // We don't support invokeblock for now. Such ISEQs are likely not used by blocks anyway.
14381435 YARVINSN_putself if captured_opnd.is_none() => Some(IseqReturn::Receiver),
1436+ YARVINSN_opt_invokebuiltin_delegate_leave => {
1437+ let pc = unsafe { rb_iseq_pc_at_idx(iseq, 0) };
1438+ let bf: rb_builtin_function = unsafe { *get_arg(pc, 0).as_ptr() };
1439+ let argc = bf.argc as usize;
1440+ if argc != 0 { return None; }
1441+ let builtin_attrs = unsafe { rb_jit_iseq_builtin_attrs(iseq) };
1442+ let leaf = builtin_attrs & BUILTIN_ATTR_LEAF != 0;
1443+ if !leaf { return None; }
1444+ Some(IseqReturn::InvokeLeafBuiltin(bf))
1445+ }
14391446 _ => None,
14401447 }
14411448}
@@ -2437,7 +2444,7 @@ impl Function {
24372444 for insn_id in old_insns {
24382445 match self.find(insn_id) {
24392446 // Reject block ISEQs to avoid autosplat and other block parameter complications.
2440- Insn::SendWithoutBlockDirect { recv, iseq, cd, args, .. } => {
2447+ Insn::SendWithoutBlockDirect { recv, iseq, cd, args, state, .. } => {
24412448 let call_info = unsafe { (*cd).ci };
24422449 let ci_flags = unsafe { vm_ci_flag(call_info) };
24432450 // .send call is not currently supported for builtins
@@ -2461,6 +2468,17 @@ impl Function {
24612468 self.push_insn(block, Insn::IncrCounter(Counter::inline_iseq_optimized_send_count));
24622469 self.make_equal_to(insn_id, recv);
24632470 }
2471+ IseqReturn::InvokeLeafBuiltin(bf) => {
2472+ self.push_insn(block, Insn::IncrCounter(Counter::inline_iseq_optimized_send_count));
2473+ let replacement = self.push_insn(block, Insn::InvokeBuiltin {
2474+ bf,
2475+ args: vec![recv],
2476+ state,
2477+ leaf: true,
2478+ return_type: None,
2479+ });
2480+ self.make_equal_to(insn_id, replacement);
2481+ }
24642482 }
24652483 }
24662484 _ => { self.push_insn_id(block, insn_id); }
@@ -10955,9 +10973,10 @@ mod opt_tests {
1095510973 bb2(v8:BasicObject, v9:BasicObject):
1095610974 v13:Fixnum[1] = Const Value(1)
1095710975 PatchPoint MethodRedefined(Integer@0x1000, zero?@0x1008, cme:0x1010)
10958- v22:BasicObject = SendWithoutBlockDirect v13, :zero? (0x1038)
10976+ IncrCounter inline_iseq_optimized_send_count
10977+ v24:BasicObject = InvokeBuiltin leaf _bi285, v13
1095910978 CheckInterrupts
10960- Return v22
10979+ Return v24
1096110980 ");
1096210981 }
1096310982
@@ -10986,9 +11005,10 @@ mod opt_tests {
1098611005 v18:ArrayExact = ArrayDup v16
1098711006 PatchPoint MethodRedefined(Array@0x1008, first@0x1010, cme:0x1018)
1098811007 PatchPoint NoSingletonClass(Array@0x1008)
10989- v30:BasicObject = SendWithoutBlockDirect v18, :first (0x1040)
11008+ IncrCounter inline_iseq_optimized_send_count
11009+ v32:BasicObject = InvokeBuiltin leaf _bi132, v18
1099011010 CheckInterrupts
10991- Return v30
11011+ Return v32
1099211012 ");
1099311013 }
1099411014
@@ -11015,9 +11035,10 @@ mod opt_tests {
1101511035 v21:ModuleExact[VALUE(0x1008)] = Const Value(VALUE(0x1008))
1101611036 PatchPoint MethodRedefined(Module@0x1010, class@0x1018, cme:0x1020)
1101711037 PatchPoint NoSingletonClass(Module@0x1010)
11018- v24:BasicObject = SendWithoutBlockDirect v21, :class (0x1048)
11038+ IncrCounter inline_iseq_optimized_send_count
11039+ v26:BasicObject = InvokeBuiltin leaf _bi20, v21
1101911040 CheckInterrupts
11020- Return v24
11041+ Return v26
1102111042 ");
1102211043 }
1102311044
@@ -15345,6 +15366,58 @@ mod opt_tests {
1534515366 ");
1534615367 }
1534715368
15369+ #[test]
15370+ fn test_inline_symbol_name() {
15371+ eval("
15372+ def test(x) = x.to_s
15373+ test(:foo)
15374+ ");
15375+ assert_snapshot!(hir_string("test"), @r"
15376+ fn test@<compiled>:2:
15377+ bb0():
15378+ EntryPoint interpreter
15379+ v1:BasicObject = LoadSelf
15380+ v2:BasicObject = GetLocal l0, SP@4
15381+ Jump bb2(v1, v2)
15382+ bb1(v5:BasicObject, v6:BasicObject):
15383+ EntryPoint JIT(0)
15384+ Jump bb2(v5, v6)
15385+ bb2(v8:BasicObject, v9:BasicObject):
15386+ PatchPoint MethodRedefined(Symbol@0x1000, to_s@0x1008, cme:0x1010)
15387+ v21:StaticSymbol = GuardType v9, StaticSymbol
15388+ IncrCounter inline_iseq_optimized_send_count
15389+ v24:BasicObject = InvokeBuiltin leaf _bi12, v21
15390+ CheckInterrupts
15391+ Return v24
15392+ ");
15393+ }
15394+
15395+ #[test]
15396+ fn test_inline_symbol_to_s() {
15397+ eval("
15398+ def test(x) = x.to_s
15399+ test(:foo)
15400+ ");
15401+ assert_snapshot!(hir_string("test"), @r"
15402+ fn test@<compiled>:2:
15403+ bb0():
15404+ EntryPoint interpreter
15405+ v1:BasicObject = LoadSelf
15406+ v2:BasicObject = GetLocal l0, SP@4
15407+ Jump bb2(v1, v2)
15408+ bb1(v5:BasicObject, v6:BasicObject):
15409+ EntryPoint JIT(0)
15410+ Jump bb2(v5, v6)
15411+ bb2(v8:BasicObject, v9:BasicObject):
15412+ PatchPoint MethodRedefined(Symbol@0x1000, to_s@0x1008, cme:0x1010)
15413+ v21:StaticSymbol = GuardType v9, StaticSymbol
15414+ IncrCounter inline_iseq_optimized_send_count
15415+ v24:BasicObject = InvokeBuiltin leaf _bi12, v21
15416+ CheckInterrupts
15417+ Return v24
15418+ ");
15419+ }
15420+
1534815421 #[test]
1534915422 fn test_optimize_stringexact_eq_stringexact() {
1535015423 eval(r#"
0 commit comments