ZJIT: Compile opt_new to slow-path SendWithoutBlock (#13216)
authorMax Bernstein <[email protected]>
Wed, 30 Apr 2025 21:48:12 +0000 (30 17:48 -0400)
committerGitHub <[email protected]>
Wed, 30 Apr 2025 21:48:12 +0000 (30 14:48 -0700)
zjit/src/hir.rs

index d46f5f4..f4ecb46 100644 (file)
@@ -1600,6 +1600,10 @@ fn compute_jump_targets(iseq: *const rb_iseq_t) -> Vec<u32> {
                 let offset = get_arg(pc, 0).as_i64();
                 jump_targets.insert(insn_idx_at_offset(insn_idx, offset));
             }
+            YARVINSN_opt_new => {
+                let offset = get_arg(pc, 1).as_i64();
+                jump_targets.insert(insn_idx_at_offset(insn_idx, offset));
+            }
             YARVINSN_leave | YARVINSN_opt_invokebuiltin_delegate_leave => {
                 if insn_idx < iseq_size {
                     jump_targets.insert(insn_idx);
@@ -1800,6 +1804,17 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
                     });
                     queue.push_back((state.clone(), target, target_idx));
                 }
+                YARVINSN_opt_new => {
+                    let offset = get_arg(pc, 1).as_i64();
+                    // TODO(max): Check interrupts
+                    let target_idx = insn_idx_at_offset(insn_idx, offset);
+                    let target = insn_idx_to_block[&target_idx];
+                    // Skip the fast-path and go straight to the fallback code. We will let the
+                    // optimizer take care of the converting Class#new->alloc+initialize instead.
+                    fun.push_insn(block, Insn::Jump(BranchEdge { target, args: state.as_args() }));
+                    queue.push_back((state.clone(), target, target_idx));
+                    break;  // Don't enqueue the next block as a successor
+                }
                 YARVINSN_jump => {
                     let offset = get_arg(pc, 0).as_i64();
                     // TODO(max): Check interrupts
@@ -2796,6 +2811,26 @@ mod tests {
         ");
         assert_compile_fails("test", ParseError::UnknownOpcode("sendforward".into()))
     }
+
+    #[test]
+    fn test_opt_new() {
+        eval("
+            class C; end
+            def test = C.new
+        ");
+        assert_method_hir("test",  expect![[r#"
+            fn test:
+            bb0():
+              v1:BasicObject = GetConstantPath 0x1000
+              v2:NilClassExact = Const Value(nil)
+              Jump bb1(v2, v1)
+            bb1(v4:NilClassExact, v5:BasicObject):
+              v8:BasicObject = SendWithoutBlock v5, :new
+              Jump bb2(v8, v4)
+            bb2(v10:BasicObject, v11:NilClassExact):
+              Return v10
+        "#]]);
+    }
 }
 
 #[cfg(test)]
@@ -3688,4 +3723,55 @@ mod opt_tests {
               Return v5
         "#]]);
     }
+
+    #[test]
+    fn test_opt_new_no_initialize() {
+        eval("
+            class C; end
+            def test = C.new
+            test
+        ");
+        assert_optimized_method_hir("test",  expect![[r#"
+            fn test:
+            bb0():
+              PatchPoint SingleRactorMode
+              PatchPoint StableConstantNames(0x1000, C)
+              v16:BasicObject[VALUE(0x1008)] = Const Value(VALUE(0x1008))
+              v2:NilClassExact = Const Value(nil)
+              Jump bb1(v2, v16)
+            bb1(v4:NilClassExact, v5:BasicObject[VALUE(0x1008)]):
+              v8:BasicObject = SendWithoutBlock v5, :new
+              Jump bb2(v8, v4)
+            bb2(v10:BasicObject, v11:NilClassExact):
+              Return v10
+        "#]]);
+    }
+
+    #[test]
+    fn test_opt_new_initialize() {
+        eval("
+            class C
+              def initialize x
+                @x = x
+              end
+            end
+            def test = C.new 1
+            test
+        ");
+        assert_optimized_method_hir("test",  expect![[r#"
+            fn test:
+            bb0():
+              PatchPoint SingleRactorMode
+              PatchPoint StableConstantNames(0x1000, C)
+              v18:BasicObject[VALUE(0x1008)] = Const Value(VALUE(0x1008))
+              v2:NilClassExact = Const Value(nil)
+              v3:Fixnum[1] = Const Value(1)
+              Jump bb1(v2, v18, v3)
+            bb1(v5:NilClassExact, v6:BasicObject[VALUE(0x1008)], v7:Fixnum[1]):
+              v10:BasicObject = SendWithoutBlock v6, :new, v7
+              Jump bb2(v10, v5)
+            bb2(v12:BasicObject, v13:NilClassExact):
+              Return v12
+        "#]]);
+    }
 }