diff options
| author | Jean Boussier <jean.boussier@gmail.com> | 2026-01-11 14:49:36 +0100 |
|---|---|---|
| committer | Jean Boussier <jean.boussier@gmail.com> | 2026-04-29 11:26:55 +0900 |
| commit | 3345854fa882f6bd70bbd7853010b43b9c8fe73d (patch) | |
| tree | 04af393893cc4d109bfb0d6831fcb18807020082 /test | |
| parent | 23215de78eafecaee48ff481321531ce194d1cd4 (diff) | |
Add YJIT test for outdated comment
As of Ruby 4.0, the YJIT comment isn't quite correct because we now
store the reference to the `imemo/fields` object inline.
Which means that a Struct of precisely 78 members (max embeddable on
64bit archs) can cohexist in embedded and heap shapes as demonstrated
by the added tests.
If the comment was correct I would expect we could easily crash
YJIT, but I'm unable to, so I suspect there is a guard I'm not
seeing that already handle that.
Diffstat (limited to 'test')
| -rw-r--r-- | test/ruby/test_struct.rb | 8 | ||||
| -rw-r--r-- | test/ruby/test_yjit.rb | 34 |
2 files changed, 41 insertions, 1 deletions
diff --git a/test/ruby/test_struct.rb b/test/ruby/test_struct.rb index 294498c30f..b37f4dba97 100644 --- a/test/ruby/test_struct.rb +++ b/test/ruby/test_struct.rb @@ -41,8 +41,14 @@ module TestStruct end end + MAX_EMBEDDED_MEMBERS = ( + GC::INTERNAL_CONSTANTS[:RVARGC_MAX_ALLOCATE_SIZE] - + GC::INTERNAL_CONSTANTS[:RBASIC_SIZE] - + GC::INTERNAL_CONSTANTS[:RVALUE_OVERHEAD] + ) / RbConfig::SIZEOF["void*"] + def test_larger_than_largest_pool - count = (GC::INTERNAL_CONSTANTS[:RVARGC_MAX_ALLOCATE_SIZE] / RbConfig::SIZEOF["void*"]) + 1 + count = MAX_EMBEDDED_MEMBERS + 1 list = Array(0..count) klass = @Struct.new(*list.map { |i| :"a_#{i}"}) struct = klass.new(*list) diff --git a/test/ruby/test_yjit.rb b/test/ruby/test_yjit.rb index 721d070399..0d7fe66e1c 100644 --- a/test/ruby/test_yjit.rb +++ b/test/ruby/test_yjit.rb @@ -644,6 +644,40 @@ class TestYJIT < Test::Unit::TestCase RUBY end + STRUCT_MAX_EMBEDDED_MEMBERS = ( + GC::INTERNAL_CONSTANTS[:RVARGC_MAX_ALLOCATE_SIZE] - + GC::INTERNAL_CONSTANTS[:RBASIC_SIZE] - + GC::INTERNAL_CONSTANTS[:RVALUE_OVERHEAD] + ) / RbConfig::SIZEOF["void*"] + + def test_spilled_struct_aref + omit("FIXME: https://github.com/Shopify/ruby/issues/977") + assert_compiles(<<~RUBY) + LargeStruct = Struct.new(:foo, :bar, *(#{STRUCT_MAX_EMBEDDED_MEMBERS} - 2).times.map { :"m_\#{it}" }) + + def foo(obj) + foo = obj.foo + raise "Expected 1, got: \#{foo}" unless foo == 1 + bar = obj.bar + raise "Expected 2, got: \#{bar}" unless bar == 2 + end + + embedded_struct = LargeStruct.new(1, 2) + # Bump RCLASS_MAX_IV_COUNT for LargeStruct + embedded_struct.instance_variable_set(:@test, 1) + + # Next allocation reserves space for the imemo/fields reference. + heap_struct = LargeStruct.new(1, 2) + + RubyVM::YJIT.reset_stats! + + foo(embedded_struct) + foo(embedded_struct) + foo(heap_struct) + foo(heap_struct) + RUBY + end + def test_struct_aset assert_compiles(<<~RUBY) def foo(obj) |
