diff options
| author | Stan Lo <stan.lo@shopify.com> | 2025-10-02 17:03:25 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-10-02 09:03:25 -0700 |
| commit | 2ed5a02fcca4da4acf4c8c3d7ee4c392fc18d948 (patch) | |
| tree | 834a2702d97afc92dcd2c905cace45cff46be5e2 /test/ruby | |
| parent | 81f253577a77a934bfa02a33d80ca2a7c6af9a04 (diff) | |
ZJIT: Add `NoSingletonClass` patch point (#14680)
* ZJIT: Add NoSingletonClass patch point
This patch point makes sure that when the object has a singleton class,
the JIT code is invalidated. As of now, this is only needed for C call
optimization.
In YJIT, the singleton class guard only applies to Array, Hash, and String.
But in ZJIT, we may optimize C calls from gems (e.g. `sqlite3`). So the
patch point needs to be applied to a broader range of classes.
* ZJIT: Only generate NoSingletonClass guard when the type can have singleton class
* ZJIT: Update or forget NoSingletonClass patch point when needed
Diffstat (limited to 'test/ruby')
| -rw-r--r-- | test/ruby/test_zjit.rb | 59 |
1 files changed, 59 insertions, 0 deletions
diff --git a/test/ruby/test_zjit.rb b/test/ruby/test_zjit.rb index 937cf44e19..83af7347d6 100644 --- a/test/ruby/test_zjit.rb +++ b/test/ruby/test_zjit.rb @@ -2912,6 +2912,65 @@ class TestZJIT < Test::Unit::TestCase }, call_threshold: 2, insns: [:opt_new] end + def test_singleton_class_invalidation_annotated_ccall + assert_compiles '[false, true]', %q{ + def define_singleton(obj, define) + if define + # Wrap in C method frame to avoid exiting JIT on defineclass + [nil].reverse_each do + class << obj + def ==(_) + true + end + end + end + end + false + end + + def test(define) + obj = BasicObject.new + # This == call gets compiled to a CCall + obj == define_singleton(obj, define) + end + + result = [] + result << test(false) # Compiles BasicObject#== + result << test(true) # Should use singleton#== now + result + }, call_threshold: 2 + end + + def test_singleton_class_invalidation_optimized_variadic_ccall + assert_compiles '[1, 1000]', %q{ + def define_singleton(arr, define) + if define + # Wrap in C method frame to avoid exiting JIT on defineclass + [nil].reverse_each do + class << arr + def push(x) + super(x * 1000) + end + end + end + end + 1 + end + + def test(define) + arr = [] + val = define_singleton(arr, define) + arr.push(val) # This CCall should be invalidated if singleton was defined + arr[0] + end + + result = [] + result << test(false) # Compiles Array#push as CCall + result << test(true) # Singleton defined, CCall should be invalidated + result + }, call_threshold: 2 + end + private # Assert that every method call in `test_script` can be compiled by ZJIT |
