From edb95b13a3789617095f9c4f1d4b8e9bcee23344 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Chris=20Hasi=C5=84ski?= Date: Sun, 12 Apr 2026 13:56:45 +0200 Subject: ZJIT: Add HIR tests and benchmarks for numeric predicate annotations Add snapshot tests verifying correct HIR generation for each annotated method: - Float cfuncs (nan?, finite?, infinite?) emit CCall with BoolExact or Fixnum|NilClass return type - Integer builtins (zero?, even?, odd?) emit InvokeBuiltin with BoolExact return type - Float builtins (zero?, positive?, negative?) emit InvokeBuiltin with BoolExact return type Add benchmark yml files for Float and Integer predicates to the benchmark/ directory. --- benchmark/float_predicate.yml | 12 +++ benchmark/integer_predicate.yml | 9 ++ zjit/src/hir/opt_tests.rs | 224 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 245 insertions(+) create mode 100644 benchmark/float_predicate.yml create mode 100644 benchmark/integer_predicate.yml diff --git a/benchmark/float_predicate.yml b/benchmark/float_predicate.yml new file mode 100644 index 0000000000..b946937666 --- /dev/null +++ b/benchmark/float_predicate.yml @@ -0,0 +1,12 @@ +prelude: | + floats = [1.0, -1.0, 0.0, Float::NAN, Float::INFINITY, -Float::INFINITY] + +benchmark: + float_nan?: floats.each { |f| f.nan? } + float_finite?: floats.each { |f| f.finite? } + float_infinite?: floats.each { |f| f.infinite? } + float_zero?: floats.each { |f| f.zero? } + float_positive?: floats.each { |f| f.positive? } + float_negative?: floats.each { |f| f.negative? } + +loop_count: 1000000 diff --git a/benchmark/integer_predicate.yml b/benchmark/integer_predicate.yml new file mode 100644 index 0000000000..7c05ff2587 --- /dev/null +++ b/benchmark/integer_predicate.yml @@ -0,0 +1,9 @@ +prelude: | + nums = (0..9).to_a + +benchmark: + integer_zero?: nums.each { |n| n.zero? } + integer_even?: nums.each { |n| n.even? } + integer_odd?: nums.each { |n| n.odd? } + +loop_count: 1000000 diff --git a/zjit/src/hir/opt_tests.rs b/zjit/src/hir/opt_tests.rs index 0bc1b6cd0d..b14df62461 100644 --- a/zjit/src/hir/opt_tests.rs +++ b/zjit/src/hir/opt_tests.rs @@ -15647,4 +15647,228 @@ mod hir_opt_tests { Jump bb6(v81, v114) "); } + + #[test] + fn test_float_nan_p_annotation() { + eval(r#" + def test(x) = x.nan? + test(1.0) + "#); + assert_snapshot!(hir_string("test"), @" + fn test@:2: + bb1(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:CPtr = LoadSP + v3:BasicObject = LoadField v2, :x@0x1000 + Jump bb3(v1, v3) + bb2(): + EntryPoint JIT(0) + v6:BasicObject = LoadArg :self@0 + v7:BasicObject = LoadArg :x@1 + Jump bb3(v6, v7) + bb3(v9:BasicObject, v10:BasicObject): + PatchPoint MethodRedefined(Float@0x1008, nan?@0x1010, cme:0x1018) + v23:Flonum = GuardType v10, Flonum + v24:BoolExact = CCall v23, :Float#nan?@0x1040 + CheckInterrupts + Return v24 + "); + } + + #[test] + fn test_float_finite_p_annotation() { + eval(r#" + def test(x) = x.finite? + test(1.0) + "#); + assert_snapshot!(hir_string("test"), @" + fn test@:2: + bb1(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:CPtr = LoadSP + v3:BasicObject = LoadField v2, :x@0x1000 + Jump bb3(v1, v3) + bb2(): + EntryPoint JIT(0) + v6:BasicObject = LoadArg :self@0 + v7:BasicObject = LoadArg :x@1 + Jump bb3(v6, v7) + bb3(v9:BasicObject, v10:BasicObject): + PatchPoint MethodRedefined(Float@0x1008, finite?@0x1010, cme:0x1018) + v23:Flonum = GuardType v10, Flonum + v24:BoolExact = CCall v23, :Float#finite?@0x1040 + CheckInterrupts + Return v24 + "); + } + + #[test] + fn test_float_infinite_p_annotation() { + eval(r#" + def test(x) = x.infinite? + test(1.0) + "#); + assert_snapshot!(hir_string("test"), @" + fn test@:2: + bb1(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:CPtr = LoadSP + v3:BasicObject = LoadField v2, :x@0x1000 + Jump bb3(v1, v3) + bb2(): + EntryPoint JIT(0) + v6:BasicObject = LoadArg :self@0 + v7:BasicObject = LoadArg :x@1 + Jump bb3(v6, v7) + bb3(v9:BasicObject, v10:BasicObject): + PatchPoint MethodRedefined(Float@0x1008, infinite?@0x1010, cme:0x1018) + v23:Flonum = GuardType v10, Flonum + v24:NilClass|Fixnum = CCall v23, :Float#infinite?@0x1040 + CheckInterrupts + Return v24 + "); + } + + #[test] + fn test_integer_even_p_annotation() { + eval(r#" + def test(x) = x.even? + test(2) + "#); + assert_snapshot!(hir_string("test"), @" + fn test@:2: + bb1(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:CPtr = LoadSP + v3:BasicObject = LoadField v2, :x@0x1000 + Jump bb3(v1, v3) + bb2(): + EntryPoint JIT(0) + v6:BasicObject = LoadArg :self@0 + v7:BasicObject = LoadArg :x@1 + Jump bb3(v6, v7) + bb3(v9:BasicObject, v10:BasicObject): + PatchPoint MethodRedefined(Integer@0x1008, even?@0x1010, cme:0x1018) + v22:Fixnum = GuardType v10, Fixnum + v24:BoolExact = InvokeBuiltin leaf , v22 + CheckInterrupts + Return v24 + "); + } + + #[test] + fn test_integer_odd_p_annotation() { + eval(r#" + def test(x) = x.odd? + test(3) + "#); + assert_snapshot!(hir_string("test"), @" + fn test@:2: + bb1(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:CPtr = LoadSP + v3:BasicObject = LoadField v2, :x@0x1000 + Jump bb3(v1, v3) + bb2(): + EntryPoint JIT(0) + v6:BasicObject = LoadArg :self@0 + v7:BasicObject = LoadArg :x@1 + Jump bb3(v6, v7) + bb3(v9:BasicObject, v10:BasicObject): + PatchPoint MethodRedefined(Integer@0x1008, odd?@0x1010, cme:0x1018) + v22:Fixnum = GuardType v10, Fixnum + v24:BoolExact = InvokeBuiltin leaf , v22 + CheckInterrupts + Return v24 + "); + } + + #[test] + fn test_float_zero_p_annotation() { + eval(r#" + def test(x) = x.zero? + test(1.0) + "#); + assert_snapshot!(hir_string("test"), @" + fn test@:2: + bb1(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:CPtr = LoadSP + v3:BasicObject = LoadField v2, :x@0x1000 + Jump bb3(v1, v3) + bb2(): + EntryPoint JIT(0) + v6:BasicObject = LoadArg :self@0 + v7:BasicObject = LoadArg :x@1 + Jump bb3(v6, v7) + bb3(v9:BasicObject, v10:BasicObject): + PatchPoint MethodRedefined(Float@0x1008, zero?@0x1010, cme:0x1018) + v22:Flonum = GuardType v10, Flonum + v24:BoolExact = InvokeBuiltin leaf , v22 + CheckInterrupts + Return v24 + "); + } + + #[test] + fn test_float_positive_p_annotation() { + eval(r#" + def test(x) = x.positive? + test(1.0) + "#); + assert_snapshot!(hir_string("test"), @" + fn test@:2: + bb1(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:CPtr = LoadSP + v3:BasicObject = LoadField v2, :x@0x1000 + Jump bb3(v1, v3) + bb2(): + EntryPoint JIT(0) + v6:BasicObject = LoadArg :self@0 + v7:BasicObject = LoadArg :x@1 + Jump bb3(v6, v7) + bb3(v9:BasicObject, v10:BasicObject): + PatchPoint MethodRedefined(Float@0x1008, positive?@0x1010, cme:0x1018) + v22:Flonum = GuardType v10, Flonum + v24:BoolExact = InvokeBuiltin leaf , v22 + CheckInterrupts + Return v24 + "); + } + + #[test] + fn test_float_negative_p_annotation() { + eval(r#" + def test(x) = x.negative? + test(-1.0) + "#); + assert_snapshot!(hir_string("test"), @" + fn test@:2: + bb1(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:CPtr = LoadSP + v3:BasicObject = LoadField v2, :x@0x1000 + Jump bb3(v1, v3) + bb2(): + EntryPoint JIT(0) + v6:BasicObject = LoadArg :self@0 + v7:BasicObject = LoadArg :x@1 + Jump bb3(v6, v7) + bb3(v9:BasicObject, v10:BasicObject): + PatchPoint MethodRedefined(Float@0x1008, negative?@0x1010, cme:0x1018) + v22:Flonum = GuardType v10, Flonum + v24:BoolExact = InvokeBuiltin leaf , v22 + CheckInterrupts + Return v24 + "); + } } -- cgit v1.2.3