From 8db30094fce210cd193b571d7c4b7e5702ed1ca9 Mon Sep 17 00:00:00 2001 From: Alan Wu Date: Fri, 31 Oct 2025 18:58:21 -0400 Subject: ZJIT: Fix incorrect elision of call to BasicObject#!= rb_obj_not_equal() uses rb_funcall(), so it's not `no_gc`, `leaf`, nor `elidable`. --- zjit/src/cruby_methods.rs | 2 +- zjit/src/hir/opt_tests.rs | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/zjit/src/cruby_methods.rs b/zjit/src/cruby_methods.rs index bd3409ff07..0af3be1819 100644 --- a/zjit/src/cruby_methods.rs +++ b/zjit/src/cruby_methods.rs @@ -221,7 +221,7 @@ pub fn init() -> Annotations { annotate!(rb_mKernel, "respond_to?", inline_kernel_respond_to_p); annotate!(rb_cBasicObject, "==", inline_basic_object_eq, types::BoolExact, no_gc, leaf, elidable); annotate!(rb_cBasicObject, "!", types::BoolExact, no_gc, leaf, elidable); - annotate!(rb_cBasicObject, "!=", inline_basic_object_neq, types::BoolExact, no_gc, leaf, elidable); + annotate!(rb_cBasicObject, "!=", inline_basic_object_neq, types::BoolExact); annotate!(rb_cBasicObject, "initialize", inline_basic_object_initialize); annotate!(rb_cInteger, "succ", inline_integer_succ); annotate!(rb_cInteger, "^", inline_integer_xor); diff --git a/zjit/src/hir/opt_tests.rs b/zjit/src/hir/opt_tests.rs index 8a9acb59bb..d0b3203ac1 100644 --- a/zjit/src/hir/opt_tests.rs +++ b/zjit/src/hir/opt_tests.rs @@ -502,6 +502,52 @@ mod hir_opt_tests { "); } + #[test] + fn neq_with_side_effect_not_elided () { + let result = eval(" + class CustomEq + attr_reader :count + + def ==(o) + @count = @count.to_i + 1 + self.equal?(o) + end + end + + def test(object) + # intentionally unused, but also can't assign to underscore + object != object + nil + end + + custom = CustomEq.new + test(custom) + test(custom) + + custom.count + "); + assert_eq!(VALUE::fixnum_from_usize(2), result); + assert_snapshot!(hir_string("test"), @r" + fn test@:13: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + PatchPoint MethodRedefined(CustomEq@0x1000, !=@0x1008, cme:0x1010) + PatchPoint NoSingletonClass(CustomEq@0x1000) + v28:HeapObject[class_exact:CustomEq] = GuardType v9, HeapObject[class_exact:CustomEq] + v29:BoolExact = CCallWithFrame !=@0x1038, v28, v9 + v19:NilClass = Const Value(nil) + CheckInterrupts + Return v19 + "); + } + #[test] fn test_replace_guard_if_known_fixnum() { eval(" -- cgit v1.2.3