From ad63b668e22e21c352b852f3119ae98a7acf99f1 Mon Sep 17 00:00:00 2001 From: Jemma Issroff Date: Mon, 3 Oct 2022 11:14:32 -0400 Subject: [PATCH] Revert "Revert "This commit implements the Object Shapes technique in CRuby."" This reverts commit 9a6803c90b817f70389cae10d60b50ad752da48f. --- bootstraptest/test_attr.rb | 16 + common.mk | 322 ++++++++++ compile.c | 26 +- debug_counter.h | 9 +- ext/coverage/depend | 4 + ext/objspace/depend | 5 + gc.c | 50 +- include/ruby/internal/core/robject.h | 3 +- include/ruby/internal/fl_type.h | 19 +- inits.c | 1 + internal.h | 3 - internal/class.h | 11 +- internal/object.h | 22 - internal/variable.h | 5 + iseq.c | 14 +- lib/mjit/compiler.rb | 117 ++-- marshal.c | 10 +- misc/lldb_cruby.py | 1 + mjit_c.rb | 35 +- mjit_compiler.h | 2 +- object.c | 46 +- ractor_core.h | 6 +- shape.c | 523 ++++++++++++++++ shape.h | 150 +++++ test/-ext-/marshal/test_internal_ivar.rb | 1 + test/ruby/test_mjit.rb | 4 +- test/ruby/test_object.rb | 9 + test/ruby/test_shapes.rb | 173 ++++++ tool/mjit/bindgen.rb | 10 +- variable.c | 751 +++++++++++------------ variable.h | 10 +- vm.c | 31 + vm_callinfo.h | 108 +++- vm_core.h | 11 +- vm_eval.c | 4 +- vm_insnhelper.c | 499 ++++++++++----- yjit/bindgen/src/main.rs | 7 + yjit/src/asm/x86_64/mod.rs | 2 +- yjit/src/codegen.rs | 133 ++-- yjit/src/cruby.rs | 12 +- yjit/src/cruby_bindings.inc.rs | 39 +- 41 files changed, 2311 insertions(+), 893 deletions(-) create mode 100644 shape.c create mode 100644 shape.h create mode 100644 test/ruby/test_shapes.rb diff --git a/bootstraptest/test_attr.rb b/bootstraptest/test_attr.rb index 721a847145..3cb9d3eb39 100644 --- a/bootstraptest/test_attr.rb +++ b/bootstraptest/test_attr.rb @@ -34,3 +34,19 @@ assert_equal %{ok}, %{ print "ok" end }, '[ruby-core:15120]' + +assert_equal %{ok}, %{ + class Big + attr_reader :foo + def initialize + @foo = "ok" + end + end + + obj = Big.new + 100.times do |i| + obj.instance_variable_set(:"@ivar_\#{i}", i) + end + + Big.new.foo +} diff --git a/common.mk b/common.mk index c56817d957..0fe5217566 100644 --- a/common.mk +++ b/common.mk @@ -136,6 +136,7 @@ COMMONOBJS = array.$(OBJEXT) \ regsyntax.$(OBJEXT) \ ruby.$(OBJEXT) \ scheduler.$(OBJEXT) \ + shape.$(OBJEXT) \ signal.$(OBJEXT) \ sprintf.$(OBJEXT) \ st.$(OBJEXT) \ @@ -1832,6 +1833,7 @@ array.$(OBJEXT): $(top_srcdir)/internal/proc.h array.$(OBJEXT): $(top_srcdir)/internal/rational.h array.$(OBJEXT): $(top_srcdir)/internal/serial.h array.$(OBJEXT): $(top_srcdir)/internal/static_assert.h +array.$(OBJEXT): $(top_srcdir)/internal/variable.h array.$(OBJEXT): $(top_srcdir)/internal/vm.h array.$(OBJEXT): $(top_srcdir)/internal/warnings.h array.$(OBJEXT): {$(VPATH)}array.c @@ -1848,6 +1850,7 @@ array.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h array.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h array.$(OBJEXT): {$(VPATH)}builtin.h array.$(OBJEXT): {$(VPATH)}config.h +array.$(OBJEXT): {$(VPATH)}constant.h array.$(OBJEXT): {$(VPATH)}debug_counter.h array.$(OBJEXT): {$(VPATH)}defines.h array.$(OBJEXT): {$(VPATH)}encoding.h @@ -2010,6 +2013,7 @@ array.$(OBJEXT): {$(VPATH)}oniguruma.h array.$(OBJEXT): {$(VPATH)}probes.dmyh array.$(OBJEXT): {$(VPATH)}probes.h array.$(OBJEXT): {$(VPATH)}ruby_assert.h +array.$(OBJEXT): {$(VPATH)}shape.h array.$(OBJEXT): {$(VPATH)}st.h array.$(OBJEXT): {$(VPATH)}subst.h array.$(OBJEXT): {$(VPATH)}transient_heap.h @@ -2028,6 +2032,7 @@ ast.$(OBJEXT): $(top_srcdir)/internal/parse.h ast.$(OBJEXT): $(top_srcdir)/internal/serial.h ast.$(OBJEXT): $(top_srcdir)/internal/static_assert.h ast.$(OBJEXT): $(top_srcdir)/internal/symbol.h +ast.$(OBJEXT): $(top_srcdir)/internal/variable.h ast.$(OBJEXT): $(top_srcdir)/internal/vm.h ast.$(OBJEXT): $(top_srcdir)/internal/warnings.h ast.$(OBJEXT): {$(VPATH)}assert.h @@ -2045,9 +2050,11 @@ ast.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h ast.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h ast.$(OBJEXT): {$(VPATH)}builtin.h ast.$(OBJEXT): {$(VPATH)}config.h +ast.$(OBJEXT): {$(VPATH)}constant.h ast.$(OBJEXT): {$(VPATH)}defines.h ast.$(OBJEXT): {$(VPATH)}encoding.h ast.$(OBJEXT): {$(VPATH)}id.h +ast.$(OBJEXT): {$(VPATH)}id_table.h ast.$(OBJEXT): {$(VPATH)}intern.h ast.$(OBJEXT): {$(VPATH)}internal.h ast.$(OBJEXT): {$(VPATH)}internal/abi.h @@ -2207,6 +2214,7 @@ ast.$(OBJEXT): {$(VPATH)}onigmo.h ast.$(OBJEXT): {$(VPATH)}oniguruma.h ast.$(OBJEXT): {$(VPATH)}ruby_assert.h ast.$(OBJEXT): {$(VPATH)}ruby_atomic.h +ast.$(OBJEXT): {$(VPATH)}shape.h ast.$(OBJEXT): {$(VPATH)}st.h ast.$(OBJEXT): {$(VPATH)}subst.h ast.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h @@ -2390,6 +2398,7 @@ bignum.$(OBJEXT): {$(VPATH)}internal/warning_push.h bignum.$(OBJEXT): {$(VPATH)}internal/xmalloc.h bignum.$(OBJEXT): {$(VPATH)}missing.h bignum.$(OBJEXT): {$(VPATH)}ruby_assert.h +bignum.$(OBJEXT): {$(VPATH)}shape.h bignum.$(OBJEXT): {$(VPATH)}st.h bignum.$(OBJEXT): {$(VPATH)}subst.h bignum.$(OBJEXT): {$(VPATH)}thread.h @@ -2405,6 +2414,7 @@ builtin.$(OBJEXT): $(top_srcdir)/internal/gc.h builtin.$(OBJEXT): $(top_srcdir)/internal/imemo.h builtin.$(OBJEXT): $(top_srcdir)/internal/serial.h builtin.$(OBJEXT): $(top_srcdir)/internal/static_assert.h +builtin.$(OBJEXT): $(top_srcdir)/internal/variable.h builtin.$(OBJEXT): $(top_srcdir)/internal/vm.h builtin.$(OBJEXT): $(top_srcdir)/internal/warnings.h builtin.$(OBJEXT): {$(VPATH)}assert.h @@ -2422,8 +2432,10 @@ builtin.$(OBJEXT): {$(VPATH)}builtin.c builtin.$(OBJEXT): {$(VPATH)}builtin.h builtin.$(OBJEXT): {$(VPATH)}builtin_binary.inc builtin.$(OBJEXT): {$(VPATH)}config.h +builtin.$(OBJEXT): {$(VPATH)}constant.h builtin.$(OBJEXT): {$(VPATH)}defines.h builtin.$(OBJEXT): {$(VPATH)}id.h +builtin.$(OBJEXT): {$(VPATH)}id_table.h builtin.$(OBJEXT): {$(VPATH)}intern.h builtin.$(OBJEXT): {$(VPATH)}internal.h builtin.$(OBJEXT): {$(VPATH)}internal/abi.h @@ -2572,6 +2584,7 @@ builtin.$(OBJEXT): {$(VPATH)}missing.h builtin.$(OBJEXT): {$(VPATH)}node.h builtin.$(OBJEXT): {$(VPATH)}ruby_assert.h builtin.$(OBJEXT): {$(VPATH)}ruby_atomic.h +builtin.$(OBJEXT): {$(VPATH)}shape.h builtin.$(OBJEXT): {$(VPATH)}st.h builtin.$(OBJEXT): {$(VPATH)}subst.h builtin.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h @@ -2774,6 +2787,7 @@ class.$(OBJEXT): {$(VPATH)}onigmo.h class.$(OBJEXT): {$(VPATH)}oniguruma.h class.$(OBJEXT): {$(VPATH)}ruby_assert.h class.$(OBJEXT): {$(VPATH)}ruby_atomic.h +class.$(OBJEXT): {$(VPATH)}shape.h class.$(OBJEXT): {$(VPATH)}st.h class.$(OBJEXT): {$(VPATH)}subst.h class.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h @@ -3177,6 +3191,7 @@ compile.$(OBJEXT): {$(VPATH)}re.h compile.$(OBJEXT): {$(VPATH)}regex.h compile.$(OBJEXT): {$(VPATH)}ruby_assert.h compile.$(OBJEXT): {$(VPATH)}ruby_atomic.h +compile.$(OBJEXT): {$(VPATH)}shape.h compile.$(OBJEXT): {$(VPATH)}st.h compile.$(OBJEXT): {$(VPATH)}subst.h compile.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h @@ -3201,6 +3216,7 @@ complex.$(OBJEXT): $(top_srcdir)/internal/object.h complex.$(OBJEXT): $(top_srcdir)/internal/rational.h complex.$(OBJEXT): $(top_srcdir)/internal/serial.h complex.$(OBJEXT): $(top_srcdir)/internal/static_assert.h +complex.$(OBJEXT): $(top_srcdir)/internal/variable.h complex.$(OBJEXT): $(top_srcdir)/internal/vm.h complex.$(OBJEXT): $(top_srcdir)/internal/warnings.h complex.$(OBJEXT): {$(VPATH)}assert.h @@ -3215,6 +3231,7 @@ complex.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h complex.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h complex.$(OBJEXT): {$(VPATH)}complex.c complex.$(OBJEXT): {$(VPATH)}config.h +complex.$(OBJEXT): {$(VPATH)}constant.h complex.$(OBJEXT): {$(VPATH)}defines.h complex.$(OBJEXT): {$(VPATH)}id.h complex.$(OBJEXT): {$(VPATH)}id_table.h @@ -3362,6 +3379,7 @@ complex.$(OBJEXT): {$(VPATH)}internal/warning_push.h complex.$(OBJEXT): {$(VPATH)}internal/xmalloc.h complex.$(OBJEXT): {$(VPATH)}missing.h complex.$(OBJEXT): {$(VPATH)}ruby_assert.h +complex.$(OBJEXT): {$(VPATH)}shape.h complex.$(OBJEXT): {$(VPATH)}st.h complex.$(OBJEXT): {$(VPATH)}subst.h cont.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h @@ -3379,6 +3397,7 @@ cont.$(OBJEXT): $(top_srcdir)/internal/proc.h cont.$(OBJEXT): $(top_srcdir)/internal/sanitizers.h cont.$(OBJEXT): $(top_srcdir)/internal/serial.h cont.$(OBJEXT): $(top_srcdir)/internal/static_assert.h +cont.$(OBJEXT): $(top_srcdir)/internal/variable.h cont.$(OBJEXT): $(top_srcdir)/internal/vm.h cont.$(OBJEXT): $(top_srcdir)/internal/warnings.h cont.$(OBJEXT): {$(VPATH)}$(COROUTINE_H) @@ -3394,6 +3413,7 @@ cont.$(OBJEXT): {$(VPATH)}backward/2/long_long.h cont.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h cont.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h cont.$(OBJEXT): {$(VPATH)}config.h +cont.$(OBJEXT): {$(VPATH)}constant.h cont.$(OBJEXT): {$(VPATH)}cont.c cont.$(OBJEXT): {$(VPATH)}debug_counter.h cont.$(OBJEXT): {$(VPATH)}defines.h @@ -3552,6 +3572,7 @@ cont.$(OBJEXT): {$(VPATH)}ractor.h cont.$(OBJEXT): {$(VPATH)}ractor_core.h cont.$(OBJEXT): {$(VPATH)}ruby_assert.h cont.$(OBJEXT): {$(VPATH)}ruby_atomic.h +cont.$(OBJEXT): {$(VPATH)}shape.h cont.$(OBJEXT): {$(VPATH)}st.h cont.$(OBJEXT): {$(VPATH)}subst.h cont.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h @@ -3572,6 +3593,7 @@ debug.$(OBJEXT): $(top_srcdir)/internal/imemo.h debug.$(OBJEXT): $(top_srcdir)/internal/serial.h debug.$(OBJEXT): $(top_srcdir)/internal/signal.h debug.$(OBJEXT): $(top_srcdir)/internal/static_assert.h +debug.$(OBJEXT): $(top_srcdir)/internal/variable.h debug.$(OBJEXT): $(top_srcdir)/internal/vm.h debug.$(OBJEXT): $(top_srcdir)/internal/warnings.h debug.$(OBJEXT): {$(VPATH)}assert.h @@ -3586,6 +3608,7 @@ debug.$(OBJEXT): {$(VPATH)}backward/2/long_long.h debug.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h debug.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h debug.$(OBJEXT): {$(VPATH)}config.h +debug.$(OBJEXT): {$(VPATH)}constant.h debug.$(OBJEXT): {$(VPATH)}debug.c debug.$(OBJEXT): {$(VPATH)}debug_counter.h debug.$(OBJEXT): {$(VPATH)}defines.h @@ -3756,6 +3779,7 @@ debug.$(OBJEXT): {$(VPATH)}ractor.h debug.$(OBJEXT): {$(VPATH)}ractor_core.h debug.$(OBJEXT): {$(VPATH)}ruby_assert.h debug.$(OBJEXT): {$(VPATH)}ruby_atomic.h +debug.$(OBJEXT): {$(VPATH)}shape.h debug.$(OBJEXT): {$(VPATH)}st.h debug.$(OBJEXT): {$(VPATH)}subst.h debug.$(OBJEXT): {$(VPATH)}symbol.h @@ -3940,6 +3964,7 @@ dir.$(OBJEXT): $(top_srcdir)/internal/object.h dir.$(OBJEXT): $(top_srcdir)/internal/serial.h dir.$(OBJEXT): $(top_srcdir)/internal/static_assert.h dir.$(OBJEXT): $(top_srcdir)/internal/string.h +dir.$(OBJEXT): $(top_srcdir)/internal/variable.h dir.$(OBJEXT): $(top_srcdir)/internal/vm.h dir.$(OBJEXT): $(top_srcdir)/internal/warnings.h dir.$(OBJEXT): {$(VPATH)}assert.h @@ -3954,6 +3979,7 @@ dir.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h dir.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h dir.$(OBJEXT): {$(VPATH)}builtin.h dir.$(OBJEXT): {$(VPATH)}config.h +dir.$(OBJEXT): {$(VPATH)}constant.h dir.$(OBJEXT): {$(VPATH)}defines.h dir.$(OBJEXT): {$(VPATH)}dir.c dir.$(OBJEXT): {$(VPATH)}dir.rbinc @@ -4116,6 +4142,7 @@ dir.$(OBJEXT): {$(VPATH)}io.h dir.$(OBJEXT): {$(VPATH)}missing.h dir.$(OBJEXT): {$(VPATH)}onigmo.h dir.$(OBJEXT): {$(VPATH)}oniguruma.h +dir.$(OBJEXT): {$(VPATH)}shape.h dir.$(OBJEXT): {$(VPATH)}st.h dir.$(OBJEXT): {$(VPATH)}subst.h dir.$(OBJEXT): {$(VPATH)}thread.h @@ -5441,6 +5468,7 @@ encoding.$(OBJEXT): $(top_srcdir)/internal/object.h encoding.$(OBJEXT): $(top_srcdir)/internal/serial.h encoding.$(OBJEXT): $(top_srcdir)/internal/static_assert.h encoding.$(OBJEXT): $(top_srcdir)/internal/string.h +encoding.$(OBJEXT): $(top_srcdir)/internal/variable.h encoding.$(OBJEXT): $(top_srcdir)/internal/vm.h encoding.$(OBJEXT): $(top_srcdir)/internal/warnings.h encoding.$(OBJEXT): {$(VPATH)}assert.h @@ -5454,6 +5482,7 @@ encoding.$(OBJEXT): {$(VPATH)}backward/2/long_long.h encoding.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h encoding.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h encoding.$(OBJEXT): {$(VPATH)}config.h +encoding.$(OBJEXT): {$(VPATH)}constant.h encoding.$(OBJEXT): {$(VPATH)}debug_counter.h encoding.$(OBJEXT): {$(VPATH)}defines.h encoding.$(OBJEXT): {$(VPATH)}encindex.h @@ -5616,6 +5645,7 @@ encoding.$(OBJEXT): {$(VPATH)}onigmo.h encoding.$(OBJEXT): {$(VPATH)}oniguruma.h encoding.$(OBJEXT): {$(VPATH)}regenc.h encoding.$(OBJEXT): {$(VPATH)}ruby_assert.h +encoding.$(OBJEXT): {$(VPATH)}shape.h encoding.$(OBJEXT): {$(VPATH)}st.h encoding.$(OBJEXT): {$(VPATH)}subst.h encoding.$(OBJEXT): {$(VPATH)}util.h @@ -5640,6 +5670,7 @@ enum.$(OBJEXT): $(top_srcdir)/internal/rational.h enum.$(OBJEXT): $(top_srcdir)/internal/re.h enum.$(OBJEXT): $(top_srcdir)/internal/serial.h enum.$(OBJEXT): $(top_srcdir)/internal/static_assert.h +enum.$(OBJEXT): $(top_srcdir)/internal/variable.h enum.$(OBJEXT): $(top_srcdir)/internal/vm.h enum.$(OBJEXT): $(top_srcdir)/internal/warnings.h enum.$(OBJEXT): {$(VPATH)}assert.h @@ -5653,6 +5684,7 @@ enum.$(OBJEXT): {$(VPATH)}backward/2/long_long.h enum.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h enum.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h enum.$(OBJEXT): {$(VPATH)}config.h +enum.$(OBJEXT): {$(VPATH)}constant.h enum.$(OBJEXT): {$(VPATH)}defines.h enum.$(OBJEXT): {$(VPATH)}encoding.h enum.$(OBJEXT): {$(VPATH)}enum.c @@ -5813,6 +5845,7 @@ enum.$(OBJEXT): {$(VPATH)}missing.h enum.$(OBJEXT): {$(VPATH)}onigmo.h enum.$(OBJEXT): {$(VPATH)}oniguruma.h enum.$(OBJEXT): {$(VPATH)}ruby_assert.h +enum.$(OBJEXT): {$(VPATH)}shape.h enum.$(OBJEXT): {$(VPATH)}st.h enum.$(OBJEXT): {$(VPATH)}subst.h enum.$(OBJEXT): {$(VPATH)}symbol.h @@ -6212,6 +6245,7 @@ error.$(OBJEXT): {$(VPATH)}onigmo.h error.$(OBJEXT): {$(VPATH)}oniguruma.h error.$(OBJEXT): {$(VPATH)}ruby_assert.h error.$(OBJEXT): {$(VPATH)}ruby_atomic.h +error.$(OBJEXT): {$(VPATH)}shape.h error.$(OBJEXT): {$(VPATH)}st.h error.$(OBJEXT): {$(VPATH)}subst.h error.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h @@ -6433,6 +6467,7 @@ eval.$(OBJEXT): {$(VPATH)}ractor.h eval.$(OBJEXT): {$(VPATH)}ractor_core.h eval.$(OBJEXT): {$(VPATH)}ruby_assert.h eval.$(OBJEXT): {$(VPATH)}ruby_atomic.h +eval.$(OBJEXT): {$(VPATH)}shape.h eval.$(OBJEXT): {$(VPATH)}st.h eval.$(OBJEXT): {$(VPATH)}subst.h eval.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h @@ -6473,6 +6508,7 @@ file.$(OBJEXT): $(top_srcdir)/internal/serial.h file.$(OBJEXT): $(top_srcdir)/internal/static_assert.h file.$(OBJEXT): $(top_srcdir)/internal/string.h file.$(OBJEXT): $(top_srcdir)/internal/thread.h +file.$(OBJEXT): $(top_srcdir)/internal/variable.h file.$(OBJEXT): $(top_srcdir)/internal/vm.h file.$(OBJEXT): $(top_srcdir)/internal/warnings.h file.$(OBJEXT): {$(VPATH)}assert.h @@ -6486,6 +6522,7 @@ file.$(OBJEXT): {$(VPATH)}backward/2/long_long.h file.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h file.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h file.$(OBJEXT): {$(VPATH)}config.h +file.$(OBJEXT): {$(VPATH)}constant.h file.$(OBJEXT): {$(VPATH)}defines.h file.$(OBJEXT): {$(VPATH)}dln.h file.$(OBJEXT): {$(VPATH)}encindex.h @@ -6648,6 +6685,7 @@ file.$(OBJEXT): {$(VPATH)}io.h file.$(OBJEXT): {$(VPATH)}missing.h file.$(OBJEXT): {$(VPATH)}onigmo.h file.$(OBJEXT): {$(VPATH)}oniguruma.h +file.$(OBJEXT): {$(VPATH)}shape.h file.$(OBJEXT): {$(VPATH)}st.h file.$(OBJEXT): {$(VPATH)}subst.h file.$(OBJEXT): {$(VPATH)}thread.h @@ -6880,6 +6918,7 @@ gc.$(OBJEXT): {$(VPATH)}regex.h gc.$(OBJEXT): {$(VPATH)}regint.h gc.$(OBJEXT): {$(VPATH)}ruby_assert.h gc.$(OBJEXT): {$(VPATH)}ruby_atomic.h +gc.$(OBJEXT): {$(VPATH)}shape.h gc.$(OBJEXT): {$(VPATH)}st.h gc.$(OBJEXT): {$(VPATH)}subst.h gc.$(OBJEXT): {$(VPATH)}symbol.h @@ -6905,6 +6944,7 @@ goruby.$(OBJEXT): $(top_srcdir)/internal/gc.h goruby.$(OBJEXT): $(top_srcdir)/internal/imemo.h goruby.$(OBJEXT): $(top_srcdir)/internal/serial.h goruby.$(OBJEXT): $(top_srcdir)/internal/static_assert.h +goruby.$(OBJEXT): $(top_srcdir)/internal/variable.h goruby.$(OBJEXT): $(top_srcdir)/internal/vm.h goruby.$(OBJEXT): $(top_srcdir)/internal/warnings.h goruby.$(OBJEXT): {$(VPATH)}assert.h @@ -6920,11 +6960,13 @@ goruby.$(OBJEXT): {$(VPATH)}backward/2/long_long.h goruby.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h goruby.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h goruby.$(OBJEXT): {$(VPATH)}config.h +goruby.$(OBJEXT): {$(VPATH)}constant.h goruby.$(OBJEXT): {$(VPATH)}defines.h goruby.$(OBJEXT): {$(VPATH)}golf_prelude.c goruby.$(OBJEXT): {$(VPATH)}golf_prelude.rb goruby.$(OBJEXT): {$(VPATH)}goruby.c goruby.$(OBJEXT): {$(VPATH)}id.h +goruby.$(OBJEXT): {$(VPATH)}id_table.h goruby.$(OBJEXT): {$(VPATH)}intern.h goruby.$(OBJEXT): {$(VPATH)}internal.h goruby.$(OBJEXT): {$(VPATH)}internal/abi.h @@ -7074,6 +7116,7 @@ goruby.$(OBJEXT): {$(VPATH)}missing.h goruby.$(OBJEXT): {$(VPATH)}node.h goruby.$(OBJEXT): {$(VPATH)}ruby_assert.h goruby.$(OBJEXT): {$(VPATH)}ruby_atomic.h +goruby.$(OBJEXT): {$(VPATH)}shape.h goruby.$(OBJEXT): {$(VPATH)}st.h goruby.$(OBJEXT): {$(VPATH)}subst.h goruby.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h @@ -7099,6 +7142,7 @@ hash.$(OBJEXT): $(top_srcdir)/internal/string.h hash.$(OBJEXT): $(top_srcdir)/internal/symbol.h hash.$(OBJEXT): $(top_srcdir)/internal/thread.h hash.$(OBJEXT): $(top_srcdir)/internal/time.h +hash.$(OBJEXT): $(top_srcdir)/internal/variable.h hash.$(OBJEXT): $(top_srcdir)/internal/vm.h hash.$(OBJEXT): $(top_srcdir)/internal/warnings.h hash.$(OBJEXT): {$(VPATH)}assert.h @@ -7112,6 +7156,7 @@ hash.$(OBJEXT): {$(VPATH)}backward/2/long_long.h hash.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h hash.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h hash.$(OBJEXT): {$(VPATH)}config.h +hash.$(OBJEXT): {$(VPATH)}constant.h hash.$(OBJEXT): {$(VPATH)}debug_counter.h hash.$(OBJEXT): {$(VPATH)}defines.h hash.$(OBJEXT): {$(VPATH)}encoding.h @@ -7276,6 +7321,7 @@ hash.$(OBJEXT): {$(VPATH)}probes.dmyh hash.$(OBJEXT): {$(VPATH)}probes.h hash.$(OBJEXT): {$(VPATH)}ractor.h hash.$(OBJEXT): {$(VPATH)}ruby_assert.h +hash.$(OBJEXT): {$(VPATH)}shape.h hash.$(OBJEXT): {$(VPATH)}st.h hash.$(OBJEXT): {$(VPATH)}subst.h hash.$(OBJEXT): {$(VPATH)}symbol.h @@ -7660,6 +7706,7 @@ io.$(OBJEXT): {$(VPATH)}oniguruma.h io.$(OBJEXT): {$(VPATH)}ractor.h io.$(OBJEXT): {$(VPATH)}ruby_assert.h io.$(OBJEXT): {$(VPATH)}ruby_atomic.h +io.$(OBJEXT): {$(VPATH)}shape.h io.$(OBJEXT): {$(VPATH)}st.h io.$(OBJEXT): {$(VPATH)}subst.h io.$(OBJEXT): {$(VPATH)}thread.h @@ -8062,6 +8109,7 @@ iseq.$(OBJEXT): {$(VPATH)}oniguruma.h iseq.$(OBJEXT): {$(VPATH)}ractor.h iseq.$(OBJEXT): {$(VPATH)}ruby_assert.h iseq.$(OBJEXT): {$(VPATH)}ruby_atomic.h +iseq.$(OBJEXT): {$(VPATH)}shape.h iseq.$(OBJEXT): {$(VPATH)}st.h iseq.$(OBJEXT): {$(VPATH)}subst.h iseq.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h @@ -8274,6 +8322,7 @@ load.$(OBJEXT): {$(VPATH)}probes.dmyh load.$(OBJEXT): {$(VPATH)}probes.h load.$(OBJEXT): {$(VPATH)}ruby_assert.h load.$(OBJEXT): {$(VPATH)}ruby_atomic.h +load.$(OBJEXT): {$(VPATH)}shape.h load.$(OBJEXT): {$(VPATH)}st.h load.$(OBJEXT): {$(VPATH)}subst.h load.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h @@ -8792,6 +8841,7 @@ marshal.$(OBJEXT): $(top_srcdir)/internal/string.h marshal.$(OBJEXT): $(top_srcdir)/internal/struct.h marshal.$(OBJEXT): $(top_srcdir)/internal/symbol.h marshal.$(OBJEXT): $(top_srcdir)/internal/util.h +marshal.$(OBJEXT): $(top_srcdir)/internal/variable.h marshal.$(OBJEXT): $(top_srcdir)/internal/vm.h marshal.$(OBJEXT): $(top_srcdir)/internal/warnings.h marshal.$(OBJEXT): {$(VPATH)}assert.h @@ -8806,6 +8856,7 @@ marshal.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h marshal.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h marshal.$(OBJEXT): {$(VPATH)}builtin.h marshal.$(OBJEXT): {$(VPATH)}config.h +marshal.$(OBJEXT): {$(VPATH)}constant.h marshal.$(OBJEXT): {$(VPATH)}defines.h marshal.$(OBJEXT): {$(VPATH)}encindex.h marshal.$(OBJEXT): {$(VPATH)}encoding.h @@ -8967,6 +9018,7 @@ marshal.$(OBJEXT): {$(VPATH)}marshal.rbinc marshal.$(OBJEXT): {$(VPATH)}missing.h marshal.$(OBJEXT): {$(VPATH)}onigmo.h marshal.$(OBJEXT): {$(VPATH)}oniguruma.h +marshal.$(OBJEXT): {$(VPATH)}shape.h marshal.$(OBJEXT): {$(VPATH)}st.h marshal.$(OBJEXT): {$(VPATH)}subst.h marshal.$(OBJEXT): {$(VPATH)}util.h @@ -8980,6 +9032,7 @@ math.$(OBJEXT): $(top_srcdir)/internal/math.h math.$(OBJEXT): $(top_srcdir)/internal/object.h math.$(OBJEXT): $(top_srcdir)/internal/serial.h math.$(OBJEXT): $(top_srcdir)/internal/static_assert.h +math.$(OBJEXT): $(top_srcdir)/internal/variable.h math.$(OBJEXT): $(top_srcdir)/internal/vm.h math.$(OBJEXT): $(top_srcdir)/internal/warnings.h math.$(OBJEXT): {$(VPATH)}assert.h @@ -8993,6 +9046,7 @@ math.$(OBJEXT): {$(VPATH)}backward/2/long_long.h math.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h math.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h math.$(OBJEXT): {$(VPATH)}config.h +math.$(OBJEXT): {$(VPATH)}constant.h math.$(OBJEXT): {$(VPATH)}defines.h math.$(OBJEXT): {$(VPATH)}id_table.h math.$(OBJEXT): {$(VPATH)}intern.h @@ -9139,6 +9193,7 @@ math.$(OBJEXT): {$(VPATH)}internal/warning_push.h math.$(OBJEXT): {$(VPATH)}internal/xmalloc.h math.$(OBJEXT): {$(VPATH)}math.c math.$(OBJEXT): {$(VPATH)}missing.h +math.$(OBJEXT): {$(VPATH)}shape.h math.$(OBJEXT): {$(VPATH)}st.h math.$(OBJEXT): {$(VPATH)}subst.h memory_view.$(OBJEXT): $(hdrdir)/ruby/ruby.h @@ -9320,6 +9375,7 @@ miniinit.$(OBJEXT): $(top_srcdir)/internal/gc.h miniinit.$(OBJEXT): $(top_srcdir)/internal/imemo.h miniinit.$(OBJEXT): $(top_srcdir)/internal/serial.h miniinit.$(OBJEXT): $(top_srcdir)/internal/static_assert.h +miniinit.$(OBJEXT): $(top_srcdir)/internal/variable.h miniinit.$(OBJEXT): $(top_srcdir)/internal/vm.h miniinit.$(OBJEXT): $(top_srcdir)/internal/warnings.h miniinit.$(OBJEXT): {$(VPATH)}array.rb @@ -9337,12 +9393,14 @@ miniinit.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h miniinit.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h miniinit.$(OBJEXT): {$(VPATH)}builtin.h miniinit.$(OBJEXT): {$(VPATH)}config.h +miniinit.$(OBJEXT): {$(VPATH)}constant.h miniinit.$(OBJEXT): {$(VPATH)}defines.h miniinit.$(OBJEXT): {$(VPATH)}dir.rb miniinit.$(OBJEXT): {$(VPATH)}encoding.h miniinit.$(OBJEXT): {$(VPATH)}gc.rb miniinit.$(OBJEXT): {$(VPATH)}gem_prelude.rb miniinit.$(OBJEXT): {$(VPATH)}id.h +miniinit.$(OBJEXT): {$(VPATH)}id_table.h miniinit.$(OBJEXT): {$(VPATH)}intern.h miniinit.$(OBJEXT): {$(VPATH)}internal.h miniinit.$(OBJEXT): {$(VPATH)}internal/abi.h @@ -9516,6 +9574,7 @@ miniinit.$(OBJEXT): {$(VPATH)}prelude.rb miniinit.$(OBJEXT): {$(VPATH)}ractor.rb miniinit.$(OBJEXT): {$(VPATH)}ruby_assert.h miniinit.$(OBJEXT): {$(VPATH)}ruby_atomic.h +miniinit.$(OBJEXT): {$(VPATH)}shape.h miniinit.$(OBJEXT): {$(VPATH)}st.h miniinit.$(OBJEXT): {$(VPATH)}subst.h miniinit.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h @@ -9547,6 +9606,7 @@ mjit.$(OBJEXT): $(top_srcdir)/internal/imemo.h mjit.$(OBJEXT): $(top_srcdir)/internal/process.h mjit.$(OBJEXT): $(top_srcdir)/internal/serial.h mjit.$(OBJEXT): $(top_srcdir)/internal/static_assert.h +mjit.$(OBJEXT): $(top_srcdir)/internal/variable.h mjit.$(OBJEXT): $(top_srcdir)/internal/vm.h mjit.$(OBJEXT): $(top_srcdir)/internal/warnings.h mjit.$(OBJEXT): {$(VPATH)}assert.h @@ -9741,6 +9801,7 @@ mjit.$(OBJEXT): {$(VPATH)}ractor.h mjit.$(OBJEXT): {$(VPATH)}ractor_core.h mjit.$(OBJEXT): {$(VPATH)}ruby_assert.h mjit.$(OBJEXT): {$(VPATH)}ruby_atomic.h +mjit.$(OBJEXT): {$(VPATH)}shape.h mjit.$(OBJEXT): {$(VPATH)}st.h mjit.$(OBJEXT): {$(VPATH)}subst.h mjit.$(OBJEXT): {$(VPATH)}thread.h @@ -9951,6 +10012,7 @@ mjit_compiler.$(OBJEXT): {$(VPATH)}mjit_unit.h mjit_compiler.$(OBJEXT): {$(VPATH)}node.h mjit_compiler.$(OBJEXT): {$(VPATH)}ruby_assert.h mjit_compiler.$(OBJEXT): {$(VPATH)}ruby_atomic.h +mjit_compiler.$(OBJEXT): {$(VPATH)}shape.h mjit_compiler.$(OBJEXT): {$(VPATH)}st.h mjit_compiler.$(OBJEXT): {$(VPATH)}subst.h mjit_compiler.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h @@ -10140,6 +10202,7 @@ node.$(OBJEXT): {$(VPATH)}node.c node.$(OBJEXT): {$(VPATH)}node.h node.$(OBJEXT): {$(VPATH)}ruby_assert.h node.$(OBJEXT): {$(VPATH)}ruby_atomic.h +node.$(OBJEXT): {$(VPATH)}shape.h node.$(OBJEXT): {$(VPATH)}st.h node.$(OBJEXT): {$(VPATH)}subst.h node.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h @@ -10342,6 +10405,7 @@ numeric.$(OBJEXT): {$(VPATH)}numeric.rbinc numeric.$(OBJEXT): {$(VPATH)}onigmo.h numeric.$(OBJEXT): {$(VPATH)}oniguruma.h numeric.$(OBJEXT): {$(VPATH)}ruby_assert.h +numeric.$(OBJEXT): {$(VPATH)}shape.h numeric.$(OBJEXT): {$(VPATH)}st.h numeric.$(OBJEXT): {$(VPATH)}subst.h numeric.$(OBJEXT): {$(VPATH)}util.h @@ -10543,6 +10607,7 @@ object.$(OBJEXT): {$(VPATH)}onigmo.h object.$(OBJEXT): {$(VPATH)}oniguruma.h object.$(OBJEXT): {$(VPATH)}probes.dmyh object.$(OBJEXT): {$(VPATH)}probes.h +object.$(OBJEXT): {$(VPATH)}shape.h object.$(OBJEXT): {$(VPATH)}st.h object.$(OBJEXT): {$(VPATH)}subst.h object.$(OBJEXT): {$(VPATH)}util.h @@ -10961,6 +11026,7 @@ proc.$(OBJEXT): $(top_srcdir)/internal/serial.h proc.$(OBJEXT): $(top_srcdir)/internal/static_assert.h proc.$(OBJEXT): $(top_srcdir)/internal/string.h proc.$(OBJEXT): $(top_srcdir)/internal/symbol.h +proc.$(OBJEXT): $(top_srcdir)/internal/variable.h proc.$(OBJEXT): $(top_srcdir)/internal/vm.h proc.$(OBJEXT): $(top_srcdir)/internal/warnings.h proc.$(OBJEXT): {$(VPATH)}assert.h @@ -10975,6 +11041,7 @@ proc.$(OBJEXT): {$(VPATH)}backward/2/long_long.h proc.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h proc.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h proc.$(OBJEXT): {$(VPATH)}config.h +proc.$(OBJEXT): {$(VPATH)}constant.h proc.$(OBJEXT): {$(VPATH)}defines.h proc.$(OBJEXT): {$(VPATH)}encoding.h proc.$(OBJEXT): {$(VPATH)}eval_intern.h @@ -11141,6 +11208,7 @@ proc.$(OBJEXT): {$(VPATH)}oniguruma.h proc.$(OBJEXT): {$(VPATH)}proc.c proc.$(OBJEXT): {$(VPATH)}ruby_assert.h proc.$(OBJEXT): {$(VPATH)}ruby_atomic.h +proc.$(OBJEXT): {$(VPATH)}shape.h proc.$(OBJEXT): {$(VPATH)}st.h proc.$(OBJEXT): {$(VPATH)}subst.h proc.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h @@ -11360,6 +11428,7 @@ process.$(OBJEXT): {$(VPATH)}process.c process.$(OBJEXT): {$(VPATH)}ractor.h process.$(OBJEXT): {$(VPATH)}ruby_assert.h process.$(OBJEXT): {$(VPATH)}ruby_atomic.h +process.$(OBJEXT): {$(VPATH)}shape.h process.$(OBJEXT): {$(VPATH)}st.h process.$(OBJEXT): {$(VPATH)}subst.h process.$(OBJEXT): {$(VPATH)}thread.h @@ -11390,6 +11459,7 @@ ractor.$(OBJEXT): $(top_srcdir)/internal/static_assert.h ractor.$(OBJEXT): $(top_srcdir)/internal/string.h ractor.$(OBJEXT): $(top_srcdir)/internal/struct.h ractor.$(OBJEXT): $(top_srcdir)/internal/thread.h +ractor.$(OBJEXT): $(top_srcdir)/internal/variable.h ractor.$(OBJEXT): $(top_srcdir)/internal/vm.h ractor.$(OBJEXT): $(top_srcdir)/internal/warnings.h ractor.$(OBJEXT): {$(VPATH)}assert.h @@ -11405,6 +11475,7 @@ ractor.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h ractor.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h ractor.$(OBJEXT): {$(VPATH)}builtin.h ractor.$(OBJEXT): {$(VPATH)}config.h +ractor.$(OBJEXT): {$(VPATH)}constant.h ractor.$(OBJEXT): {$(VPATH)}debug_counter.h ractor.$(OBJEXT): {$(VPATH)}defines.h ractor.$(OBJEXT): {$(VPATH)}encoding.h @@ -11574,6 +11645,7 @@ ractor.$(OBJEXT): {$(VPATH)}ractor.rbinc ractor.$(OBJEXT): {$(VPATH)}ractor_core.h ractor.$(OBJEXT): {$(VPATH)}ruby_assert.h ractor.$(OBJEXT): {$(VPATH)}ruby_atomic.h +ractor.$(OBJEXT): {$(VPATH)}shape.h ractor.$(OBJEXT): {$(VPATH)}st.h ractor.$(OBJEXT): {$(VPATH)}subst.h ractor.$(OBJEXT): {$(VPATH)}thread.h @@ -11971,6 +12043,7 @@ rational.$(OBJEXT): $(top_srcdir)/internal/object.h rational.$(OBJEXT): $(top_srcdir)/internal/rational.h rational.$(OBJEXT): $(top_srcdir)/internal/serial.h rational.$(OBJEXT): $(top_srcdir)/internal/static_assert.h +rational.$(OBJEXT): $(top_srcdir)/internal/variable.h rational.$(OBJEXT): $(top_srcdir)/internal/vm.h rational.$(OBJEXT): $(top_srcdir)/internal/warnings.h rational.$(OBJEXT): {$(VPATH)}assert.h @@ -11984,6 +12057,7 @@ rational.$(OBJEXT): {$(VPATH)}backward/2/long_long.h rational.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h rational.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h rational.$(OBJEXT): {$(VPATH)}config.h +rational.$(OBJEXT): {$(VPATH)}constant.h rational.$(OBJEXT): {$(VPATH)}defines.h rational.$(OBJEXT): {$(VPATH)}id.h rational.$(OBJEXT): {$(VPATH)}id_table.h @@ -12132,6 +12206,7 @@ rational.$(OBJEXT): {$(VPATH)}internal/xmalloc.h rational.$(OBJEXT): {$(VPATH)}missing.h rational.$(OBJEXT): {$(VPATH)}rational.c rational.$(OBJEXT): {$(VPATH)}ruby_assert.h +rational.$(OBJEXT): {$(VPATH)}shape.h rational.$(OBJEXT): {$(VPATH)}st.h rational.$(OBJEXT): {$(VPATH)}subst.h re.$(OBJEXT): $(hdrdir)/ruby.h @@ -12329,6 +12404,7 @@ re.$(OBJEXT): {$(VPATH)}re.h re.$(OBJEXT): {$(VPATH)}regenc.h re.$(OBJEXT): {$(VPATH)}regex.h re.$(OBJEXT): {$(VPATH)}regint.h +re.$(OBJEXT): {$(VPATH)}shape.h re.$(OBJEXT): {$(VPATH)}st.h re.$(OBJEXT): {$(VPATH)}subst.h re.$(OBJEXT): {$(VPATH)}util.h @@ -13526,6 +13602,7 @@ ruby.$(OBJEXT): {$(VPATH)}oniguruma.h ruby.$(OBJEXT): {$(VPATH)}ruby.c ruby.$(OBJEXT): {$(VPATH)}ruby_assert.h ruby.$(OBJEXT): {$(VPATH)}ruby_atomic.h +ruby.$(OBJEXT): {$(VPATH)}shape.h ruby.$(OBJEXT): {$(VPATH)}st.h ruby.$(OBJEXT): {$(VPATH)}subst.h ruby.$(OBJEXT): {$(VPATH)}thread.h @@ -13547,6 +13624,7 @@ scheduler.$(OBJEXT): $(top_srcdir)/internal/imemo.h scheduler.$(OBJEXT): $(top_srcdir)/internal/serial.h scheduler.$(OBJEXT): $(top_srcdir)/internal/static_assert.h scheduler.$(OBJEXT): $(top_srcdir)/internal/thread.h +scheduler.$(OBJEXT): $(top_srcdir)/internal/variable.h scheduler.$(OBJEXT): $(top_srcdir)/internal/vm.h scheduler.$(OBJEXT): $(top_srcdir)/internal/warnings.h scheduler.$(OBJEXT): {$(VPATH)}assert.h @@ -13561,10 +13639,12 @@ scheduler.$(OBJEXT): {$(VPATH)}backward/2/long_long.h scheduler.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h scheduler.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h scheduler.$(OBJEXT): {$(VPATH)}config.h +scheduler.$(OBJEXT): {$(VPATH)}constant.h scheduler.$(OBJEXT): {$(VPATH)}defines.h scheduler.$(OBJEXT): {$(VPATH)}encoding.h scheduler.$(OBJEXT): {$(VPATH)}fiber/scheduler.h scheduler.$(OBJEXT): {$(VPATH)}id.h +scheduler.$(OBJEXT): {$(VPATH)}id_table.h scheduler.$(OBJEXT): {$(VPATH)}intern.h scheduler.$(OBJEXT): {$(VPATH)}internal.h scheduler.$(OBJEXT): {$(VPATH)}internal/abi.h @@ -13726,6 +13806,7 @@ scheduler.$(OBJEXT): {$(VPATH)}oniguruma.h scheduler.$(OBJEXT): {$(VPATH)}ruby_assert.h scheduler.$(OBJEXT): {$(VPATH)}ruby_atomic.h scheduler.$(OBJEXT): {$(VPATH)}scheduler.c +scheduler.$(OBJEXT): {$(VPATH)}shape.h scheduler.$(OBJEXT): {$(VPATH)}st.h scheduler.$(OBJEXT): {$(VPATH)}subst.h scheduler.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h @@ -13891,6 +13972,208 @@ setproctitle.$(OBJEXT): {$(VPATH)}setproctitle.c setproctitle.$(OBJEXT): {$(VPATH)}st.h setproctitle.$(OBJEXT): {$(VPATH)}subst.h setproctitle.$(OBJEXT): {$(VPATH)}util.h +shape.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h +shape.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h +shape.$(OBJEXT): $(CCAN_DIR)/list/list.h +shape.$(OBJEXT): $(CCAN_DIR)/str/str.h +shape.$(OBJEXT): $(hdrdir)/ruby/ruby.h +shape.$(OBJEXT): $(top_srcdir)/internal/array.h +shape.$(OBJEXT): $(top_srcdir)/internal/class.h +shape.$(OBJEXT): $(top_srcdir)/internal/compilers.h +shape.$(OBJEXT): $(top_srcdir)/internal/gc.h +shape.$(OBJEXT): $(top_srcdir)/internal/imemo.h +shape.$(OBJEXT): $(top_srcdir)/internal/serial.h +shape.$(OBJEXT): $(top_srcdir)/internal/static_assert.h +shape.$(OBJEXT): $(top_srcdir)/internal/symbol.h +shape.$(OBJEXT): $(top_srcdir)/internal/variable.h +shape.$(OBJEXT): $(top_srcdir)/internal/vm.h +shape.$(OBJEXT): $(top_srcdir)/internal/warnings.h +shape.$(OBJEXT): {$(VPATH)}assert.h +shape.$(OBJEXT): {$(VPATH)}atomic.h +shape.$(OBJEXT): {$(VPATH)}backward/2/assume.h +shape.$(OBJEXT): {$(VPATH)}backward/2/attributes.h +shape.$(OBJEXT): {$(VPATH)}backward/2/bool.h +shape.$(OBJEXT): {$(VPATH)}backward/2/gcc_version_since.h +shape.$(OBJEXT): {$(VPATH)}backward/2/inttypes.h +shape.$(OBJEXT): {$(VPATH)}backward/2/limits.h +shape.$(OBJEXT): {$(VPATH)}backward/2/long_long.h +shape.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h +shape.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h +shape.$(OBJEXT): {$(VPATH)}config.h +shape.$(OBJEXT): {$(VPATH)}constant.h +shape.$(OBJEXT): {$(VPATH)}debug_counter.h +shape.$(OBJEXT): {$(VPATH)}defines.h +shape.$(OBJEXT): {$(VPATH)}encoding.h +shape.$(OBJEXT): {$(VPATH)}id.h +shape.$(OBJEXT): {$(VPATH)}id_table.h +shape.$(OBJEXT): {$(VPATH)}intern.h +shape.$(OBJEXT): {$(VPATH)}internal.h +shape.$(OBJEXT): {$(VPATH)}internal/abi.h +shape.$(OBJEXT): {$(VPATH)}internal/anyargs.h +shape.$(OBJEXT): {$(VPATH)}internal/arithmetic.h +shape.$(OBJEXT): {$(VPATH)}internal/arithmetic/char.h +shape.$(OBJEXT): {$(VPATH)}internal/arithmetic/double.h +shape.$(OBJEXT): {$(VPATH)}internal/arithmetic/fixnum.h +shape.$(OBJEXT): {$(VPATH)}internal/arithmetic/gid_t.h +shape.$(OBJEXT): {$(VPATH)}internal/arithmetic/int.h +shape.$(OBJEXT): {$(VPATH)}internal/arithmetic/intptr_t.h +shape.$(OBJEXT): {$(VPATH)}internal/arithmetic/long.h +shape.$(OBJEXT): {$(VPATH)}internal/arithmetic/long_long.h +shape.$(OBJEXT): {$(VPATH)}internal/arithmetic/mode_t.h +shape.$(OBJEXT): {$(VPATH)}internal/arithmetic/off_t.h +shape.$(OBJEXT): {$(VPATH)}internal/arithmetic/pid_t.h +shape.$(OBJEXT): {$(VPATH)}internal/arithmetic/short.h +shape.$(OBJEXT): {$(VPATH)}internal/arithmetic/size_t.h +shape.$(OBJEXT): {$(VPATH)}internal/arithmetic/st_data_t.h +shape.$(OBJEXT): {$(VPATH)}internal/arithmetic/uid_t.h +shape.$(OBJEXT): {$(VPATH)}internal/assume.h +shape.$(OBJEXT): {$(VPATH)}internal/attr/alloc_size.h +shape.$(OBJEXT): {$(VPATH)}internal/attr/artificial.h +shape.$(OBJEXT): {$(VPATH)}internal/attr/cold.h +shape.$(OBJEXT): {$(VPATH)}internal/attr/const.h +shape.$(OBJEXT): {$(VPATH)}internal/attr/constexpr.h +shape.$(OBJEXT): {$(VPATH)}internal/attr/deprecated.h +shape.$(OBJEXT): {$(VPATH)}internal/attr/diagnose_if.h +shape.$(OBJEXT): {$(VPATH)}internal/attr/enum_extensibility.h +shape.$(OBJEXT): {$(VPATH)}internal/attr/error.h +shape.$(OBJEXT): {$(VPATH)}internal/attr/flag_enum.h +shape.$(OBJEXT): {$(VPATH)}internal/attr/forceinline.h +shape.$(OBJEXT): {$(VPATH)}internal/attr/format.h +shape.$(OBJEXT): {$(VPATH)}internal/attr/maybe_unused.h +shape.$(OBJEXT): {$(VPATH)}internal/attr/noalias.h +shape.$(OBJEXT): {$(VPATH)}internal/attr/nodiscard.h +shape.$(OBJEXT): {$(VPATH)}internal/attr/noexcept.h +shape.$(OBJEXT): {$(VPATH)}internal/attr/noinline.h +shape.$(OBJEXT): {$(VPATH)}internal/attr/nonnull.h +shape.$(OBJEXT): {$(VPATH)}internal/attr/noreturn.h +shape.$(OBJEXT): {$(VPATH)}internal/attr/pure.h +shape.$(OBJEXT): {$(VPATH)}internal/attr/restrict.h +shape.$(OBJEXT): {$(VPATH)}internal/attr/returns_nonnull.h +shape.$(OBJEXT): {$(VPATH)}internal/attr/warning.h +shape.$(OBJEXT): {$(VPATH)}internal/attr/weakref.h +shape.$(OBJEXT): {$(VPATH)}internal/cast.h +shape.$(OBJEXT): {$(VPATH)}internal/compiler_is.h +shape.$(OBJEXT): {$(VPATH)}internal/compiler_is/apple.h +shape.$(OBJEXT): {$(VPATH)}internal/compiler_is/clang.h +shape.$(OBJEXT): {$(VPATH)}internal/compiler_is/gcc.h +shape.$(OBJEXT): {$(VPATH)}internal/compiler_is/intel.h +shape.$(OBJEXT): {$(VPATH)}internal/compiler_is/msvc.h +shape.$(OBJEXT): {$(VPATH)}internal/compiler_is/sunpro.h +shape.$(OBJEXT): {$(VPATH)}internal/compiler_since.h +shape.$(OBJEXT): {$(VPATH)}internal/config.h +shape.$(OBJEXT): {$(VPATH)}internal/constant_p.h +shape.$(OBJEXT): {$(VPATH)}internal/core.h +shape.$(OBJEXT): {$(VPATH)}internal/core/rarray.h +shape.$(OBJEXT): {$(VPATH)}internal/core/rbasic.h +shape.$(OBJEXT): {$(VPATH)}internal/core/rbignum.h +shape.$(OBJEXT): {$(VPATH)}internal/core/rclass.h +shape.$(OBJEXT): {$(VPATH)}internal/core/rdata.h +shape.$(OBJEXT): {$(VPATH)}internal/core/rfile.h +shape.$(OBJEXT): {$(VPATH)}internal/core/rhash.h +shape.$(OBJEXT): {$(VPATH)}internal/core/robject.h +shape.$(OBJEXT): {$(VPATH)}internal/core/rregexp.h +shape.$(OBJEXT): {$(VPATH)}internal/core/rstring.h +shape.$(OBJEXT): {$(VPATH)}internal/core/rstruct.h +shape.$(OBJEXT): {$(VPATH)}internal/core/rtypeddata.h +shape.$(OBJEXT): {$(VPATH)}internal/ctype.h +shape.$(OBJEXT): {$(VPATH)}internal/dllexport.h +shape.$(OBJEXT): {$(VPATH)}internal/dosish.h +shape.$(OBJEXT): {$(VPATH)}internal/encoding/coderange.h +shape.$(OBJEXT): {$(VPATH)}internal/encoding/ctype.h +shape.$(OBJEXT): {$(VPATH)}internal/encoding/encoding.h +shape.$(OBJEXT): {$(VPATH)}internal/encoding/pathname.h +shape.$(OBJEXT): {$(VPATH)}internal/encoding/re.h +shape.$(OBJEXT): {$(VPATH)}internal/encoding/sprintf.h +shape.$(OBJEXT): {$(VPATH)}internal/encoding/string.h +shape.$(OBJEXT): {$(VPATH)}internal/encoding/symbol.h +shape.$(OBJEXT): {$(VPATH)}internal/encoding/transcode.h +shape.$(OBJEXT): {$(VPATH)}internal/error.h +shape.$(OBJEXT): {$(VPATH)}internal/eval.h +shape.$(OBJEXT): {$(VPATH)}internal/event.h +shape.$(OBJEXT): {$(VPATH)}internal/fl_type.h +shape.$(OBJEXT): {$(VPATH)}internal/gc.h +shape.$(OBJEXT): {$(VPATH)}internal/glob.h +shape.$(OBJEXT): {$(VPATH)}internal/globals.h +shape.$(OBJEXT): {$(VPATH)}internal/has/attribute.h +shape.$(OBJEXT): {$(VPATH)}internal/has/builtin.h +shape.$(OBJEXT): {$(VPATH)}internal/has/c_attribute.h +shape.$(OBJEXT): {$(VPATH)}internal/has/cpp_attribute.h +shape.$(OBJEXT): {$(VPATH)}internal/has/declspec_attribute.h +shape.$(OBJEXT): {$(VPATH)}internal/has/extension.h +shape.$(OBJEXT): {$(VPATH)}internal/has/feature.h +shape.$(OBJEXT): {$(VPATH)}internal/has/warning.h +shape.$(OBJEXT): {$(VPATH)}internal/intern/array.h +shape.$(OBJEXT): {$(VPATH)}internal/intern/bignum.h +shape.$(OBJEXT): {$(VPATH)}internal/intern/class.h +shape.$(OBJEXT): {$(VPATH)}internal/intern/compar.h +shape.$(OBJEXT): {$(VPATH)}internal/intern/complex.h +shape.$(OBJEXT): {$(VPATH)}internal/intern/cont.h +shape.$(OBJEXT): {$(VPATH)}internal/intern/dir.h +shape.$(OBJEXT): {$(VPATH)}internal/intern/enum.h +shape.$(OBJEXT): {$(VPATH)}internal/intern/enumerator.h +shape.$(OBJEXT): {$(VPATH)}internal/intern/error.h +shape.$(OBJEXT): {$(VPATH)}internal/intern/eval.h +shape.$(OBJEXT): {$(VPATH)}internal/intern/file.h +shape.$(OBJEXT): {$(VPATH)}internal/intern/gc.h +shape.$(OBJEXT): {$(VPATH)}internal/intern/hash.h +shape.$(OBJEXT): {$(VPATH)}internal/intern/io.h +shape.$(OBJEXT): {$(VPATH)}internal/intern/load.h +shape.$(OBJEXT): {$(VPATH)}internal/intern/marshal.h +shape.$(OBJEXT): {$(VPATH)}internal/intern/numeric.h +shape.$(OBJEXT): {$(VPATH)}internal/intern/object.h +shape.$(OBJEXT): {$(VPATH)}internal/intern/parse.h +shape.$(OBJEXT): {$(VPATH)}internal/intern/proc.h +shape.$(OBJEXT): {$(VPATH)}internal/intern/process.h +shape.$(OBJEXT): {$(VPATH)}internal/intern/random.h +shape.$(OBJEXT): {$(VPATH)}internal/intern/range.h +shape.$(OBJEXT): {$(VPATH)}internal/intern/rational.h +shape.$(OBJEXT): {$(VPATH)}internal/intern/re.h +shape.$(OBJEXT): {$(VPATH)}internal/intern/ruby.h +shape.$(OBJEXT): {$(VPATH)}internal/intern/select.h +shape.$(OBJEXT): {$(VPATH)}internal/intern/select/largesize.h +shape.$(OBJEXT): {$(VPATH)}internal/intern/signal.h +shape.$(OBJEXT): {$(VPATH)}internal/intern/sprintf.h +shape.$(OBJEXT): {$(VPATH)}internal/intern/string.h +shape.$(OBJEXT): {$(VPATH)}internal/intern/struct.h +shape.$(OBJEXT): {$(VPATH)}internal/intern/thread.h +shape.$(OBJEXT): {$(VPATH)}internal/intern/time.h +shape.$(OBJEXT): {$(VPATH)}internal/intern/variable.h +shape.$(OBJEXT): {$(VPATH)}internal/intern/vm.h +shape.$(OBJEXT): {$(VPATH)}internal/interpreter.h +shape.$(OBJEXT): {$(VPATH)}internal/iterator.h +shape.$(OBJEXT): {$(VPATH)}internal/memory.h +shape.$(OBJEXT): {$(VPATH)}internal/method.h +shape.$(OBJEXT): {$(VPATH)}internal/module.h +shape.$(OBJEXT): {$(VPATH)}internal/newobj.h +shape.$(OBJEXT): {$(VPATH)}internal/rgengc.h +shape.$(OBJEXT): {$(VPATH)}internal/scan_args.h +shape.$(OBJEXT): {$(VPATH)}internal/special_consts.h +shape.$(OBJEXT): {$(VPATH)}internal/static_assert.h +shape.$(OBJEXT): {$(VPATH)}internal/stdalign.h +shape.$(OBJEXT): {$(VPATH)}internal/stdbool.h +shape.$(OBJEXT): {$(VPATH)}internal/symbol.h +shape.$(OBJEXT): {$(VPATH)}internal/value.h +shape.$(OBJEXT): {$(VPATH)}internal/value_type.h +shape.$(OBJEXT): {$(VPATH)}internal/variable.h +shape.$(OBJEXT): {$(VPATH)}internal/warning_push.h +shape.$(OBJEXT): {$(VPATH)}internal/xmalloc.h +shape.$(OBJEXT): {$(VPATH)}method.h +shape.$(OBJEXT): {$(VPATH)}missing.h +shape.$(OBJEXT): {$(VPATH)}node.h +shape.$(OBJEXT): {$(VPATH)}onigmo.h +shape.$(OBJEXT): {$(VPATH)}oniguruma.h +shape.$(OBJEXT): {$(VPATH)}ruby_assert.h +shape.$(OBJEXT): {$(VPATH)}ruby_atomic.h +shape.$(OBJEXT): {$(VPATH)}shape.c +shape.$(OBJEXT): {$(VPATH)}shape.h +shape.$(OBJEXT): {$(VPATH)}st.h +shape.$(OBJEXT): {$(VPATH)}subst.h +shape.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h +shape.$(OBJEXT): {$(VPATH)}thread_native.h +shape.$(OBJEXT): {$(VPATH)}vm_core.h +shape.$(OBJEXT): {$(VPATH)}vm_debug.h +shape.$(OBJEXT): {$(VPATH)}vm_opts.h +shape.$(OBJEXT): {$(VPATH)}vm_sync.h signal.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h signal.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h signal.$(OBJEXT): $(CCAN_DIR)/list/list.h @@ -13907,6 +14190,7 @@ signal.$(OBJEXT): $(top_srcdir)/internal/signal.h signal.$(OBJEXT): $(top_srcdir)/internal/static_assert.h signal.$(OBJEXT): $(top_srcdir)/internal/string.h signal.$(OBJEXT): $(top_srcdir)/internal/thread.h +signal.$(OBJEXT): $(top_srcdir)/internal/variable.h signal.$(OBJEXT): $(top_srcdir)/internal/vm.h signal.$(OBJEXT): $(top_srcdir)/internal/warnings.h signal.$(OBJEXT): {$(VPATH)}assert.h @@ -13921,6 +14205,7 @@ signal.$(OBJEXT): {$(VPATH)}backward/2/long_long.h signal.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h signal.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h signal.$(OBJEXT): {$(VPATH)}config.h +signal.$(OBJEXT): {$(VPATH)}constant.h signal.$(OBJEXT): {$(VPATH)}debug_counter.h signal.$(OBJEXT): {$(VPATH)}defines.h signal.$(OBJEXT): {$(VPATH)}encoding.h @@ -14087,6 +14372,7 @@ signal.$(OBJEXT): {$(VPATH)}ractor.h signal.$(OBJEXT): {$(VPATH)}ractor_core.h signal.$(OBJEXT): {$(VPATH)}ruby_assert.h signal.$(OBJEXT): {$(VPATH)}ruby_atomic.h +signal.$(OBJEXT): {$(VPATH)}shape.h signal.$(OBJEXT): {$(VPATH)}signal.c signal.$(OBJEXT): {$(VPATH)}st.h signal.$(OBJEXT): {$(VPATH)}subst.h @@ -14111,6 +14397,7 @@ sprintf.$(OBJEXT): $(top_srcdir)/internal/serial.h sprintf.$(OBJEXT): $(top_srcdir)/internal/static_assert.h sprintf.$(OBJEXT): $(top_srcdir)/internal/string.h sprintf.$(OBJEXT): $(top_srcdir)/internal/symbol.h +sprintf.$(OBJEXT): $(top_srcdir)/internal/variable.h sprintf.$(OBJEXT): $(top_srcdir)/internal/vm.h sprintf.$(OBJEXT): $(top_srcdir)/internal/warnings.h sprintf.$(OBJEXT): {$(VPATH)}assert.h @@ -14124,6 +14411,7 @@ sprintf.$(OBJEXT): {$(VPATH)}backward/2/long_long.h sprintf.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h sprintf.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h sprintf.$(OBJEXT): {$(VPATH)}config.h +sprintf.$(OBJEXT): {$(VPATH)}constant.h sprintf.$(OBJEXT): {$(VPATH)}defines.h sprintf.$(OBJEXT): {$(VPATH)}encoding.h sprintf.$(OBJEXT): {$(VPATH)}id.h @@ -14285,6 +14573,7 @@ sprintf.$(OBJEXT): {$(VPATH)}onigmo.h sprintf.$(OBJEXT): {$(VPATH)}oniguruma.h sprintf.$(OBJEXT): {$(VPATH)}re.h sprintf.$(OBJEXT): {$(VPATH)}regex.h +sprintf.$(OBJEXT): {$(VPATH)}shape.h sprintf.$(OBJEXT): {$(VPATH)}sprintf.c sprintf.$(OBJEXT): {$(VPATH)}st.h sprintf.$(OBJEXT): {$(VPATH)}subst.h @@ -14653,6 +14942,7 @@ string.$(OBJEXT): $(top_srcdir)/internal/serial.h string.$(OBJEXT): $(top_srcdir)/internal/static_assert.h string.$(OBJEXT): $(top_srcdir)/internal/string.h string.$(OBJEXT): $(top_srcdir)/internal/transcode.h +string.$(OBJEXT): $(top_srcdir)/internal/variable.h string.$(OBJEXT): $(top_srcdir)/internal/vm.h string.$(OBJEXT): $(top_srcdir)/internal/warnings.h string.$(OBJEXT): {$(VPATH)}assert.h @@ -14667,6 +14957,7 @@ string.$(OBJEXT): {$(VPATH)}backward/2/long_long.h string.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h string.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h string.$(OBJEXT): {$(VPATH)}config.h +string.$(OBJEXT): {$(VPATH)}constant.h string.$(OBJEXT): {$(VPATH)}debug_counter.h string.$(OBJEXT): {$(VPATH)}defines.h string.$(OBJEXT): {$(VPATH)}encindex.h @@ -14834,6 +15125,7 @@ string.$(OBJEXT): {$(VPATH)}probes.h string.$(OBJEXT): {$(VPATH)}re.h string.$(OBJEXT): {$(VPATH)}regex.h string.$(OBJEXT): {$(VPATH)}ruby_assert.h +string.$(OBJEXT): {$(VPATH)}shape.h string.$(OBJEXT): {$(VPATH)}st.h string.$(OBJEXT): {$(VPATH)}string.c string.$(OBJEXT): {$(VPATH)}subst.h @@ -14890,6 +15182,7 @@ struct.$(OBJEXT): $(top_srcdir)/internal/static_assert.h struct.$(OBJEXT): $(top_srcdir)/internal/string.h struct.$(OBJEXT): $(top_srcdir)/internal/struct.h struct.$(OBJEXT): $(top_srcdir)/internal/symbol.h +struct.$(OBJEXT): $(top_srcdir)/internal/variable.h struct.$(OBJEXT): $(top_srcdir)/internal/vm.h struct.$(OBJEXT): $(top_srcdir)/internal/warnings.h struct.$(OBJEXT): {$(VPATH)}assert.h @@ -14905,6 +15198,7 @@ struct.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h struct.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h struct.$(OBJEXT): {$(VPATH)}builtin.h struct.$(OBJEXT): {$(VPATH)}config.h +struct.$(OBJEXT): {$(VPATH)}constant.h struct.$(OBJEXT): {$(VPATH)}defines.h struct.$(OBJEXT): {$(VPATH)}encoding.h struct.$(OBJEXT): {$(VPATH)}id.h @@ -15067,6 +15361,7 @@ struct.$(OBJEXT): {$(VPATH)}onigmo.h struct.$(OBJEXT): {$(VPATH)}oniguruma.h struct.$(OBJEXT): {$(VPATH)}ruby_assert.h struct.$(OBJEXT): {$(VPATH)}ruby_atomic.h +struct.$(OBJEXT): {$(VPATH)}shape.h struct.$(OBJEXT): {$(VPATH)}st.h struct.$(OBJEXT): {$(VPATH)}struct.c struct.$(OBJEXT): {$(VPATH)}subst.h @@ -15086,6 +15381,7 @@ symbol.$(OBJEXT): $(top_srcdir)/internal/serial.h symbol.$(OBJEXT): $(top_srcdir)/internal/static_assert.h symbol.$(OBJEXT): $(top_srcdir)/internal/string.h symbol.$(OBJEXT): $(top_srcdir)/internal/symbol.h +symbol.$(OBJEXT): $(top_srcdir)/internal/variable.h symbol.$(OBJEXT): $(top_srcdir)/internal/vm.h symbol.$(OBJEXT): $(top_srcdir)/internal/warnings.h symbol.$(OBJEXT): {$(VPATH)}assert.h @@ -15099,6 +15395,7 @@ symbol.$(OBJEXT): {$(VPATH)}backward/2/long_long.h symbol.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h symbol.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h symbol.$(OBJEXT): {$(VPATH)}config.h +symbol.$(OBJEXT): {$(VPATH)}constant.h symbol.$(OBJEXT): {$(VPATH)}debug_counter.h symbol.$(OBJEXT): {$(VPATH)}defines.h symbol.$(OBJEXT): {$(VPATH)}encoding.h @@ -15264,6 +15561,7 @@ symbol.$(OBJEXT): {$(VPATH)}oniguruma.h symbol.$(OBJEXT): {$(VPATH)}probes.dmyh symbol.$(OBJEXT): {$(VPATH)}probes.h symbol.$(OBJEXT): {$(VPATH)}ruby_assert.h +symbol.$(OBJEXT): {$(VPATH)}shape.h symbol.$(OBJEXT): {$(VPATH)}st.h symbol.$(OBJEXT): {$(VPATH)}subst.h symbol.$(OBJEXT): {$(VPATH)}symbol.c @@ -15294,6 +15592,7 @@ thread.$(OBJEXT): $(top_srcdir)/internal/static_assert.h thread.$(OBJEXT): $(top_srcdir)/internal/string.h thread.$(OBJEXT): $(top_srcdir)/internal/thread.h thread.$(OBJEXT): $(top_srcdir)/internal/time.h +thread.$(OBJEXT): $(top_srcdir)/internal/variable.h thread.$(OBJEXT): $(top_srcdir)/internal/vm.h thread.$(OBJEXT): $(top_srcdir)/internal/warnings.h thread.$(OBJEXT): {$(VPATH)}assert.h @@ -15309,6 +15608,7 @@ thread.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h thread.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h thread.$(OBJEXT): {$(VPATH)}builtin.h thread.$(OBJEXT): {$(VPATH)}config.h +thread.$(OBJEXT): {$(VPATH)}constant.h thread.$(OBJEXT): {$(VPATH)}debug.h thread.$(OBJEXT): {$(VPATH)}debug_counter.h thread.$(OBJEXT): {$(VPATH)}defines.h @@ -15482,6 +15782,7 @@ thread.$(OBJEXT): {$(VPATH)}ractor.h thread.$(OBJEXT): {$(VPATH)}ractor_core.h thread.$(OBJEXT): {$(VPATH)}ruby_assert.h thread.$(OBJEXT): {$(VPATH)}ruby_atomic.h +thread.$(OBJEXT): {$(VPATH)}shape.h thread.$(OBJEXT): {$(VPATH)}st.h thread.$(OBJEXT): {$(VPATH)}subst.h thread.$(OBJEXT): {$(VPATH)}thread.c @@ -15702,6 +16003,7 @@ transcode.$(OBJEXT): $(top_srcdir)/internal/serial.h transcode.$(OBJEXT): $(top_srcdir)/internal/static_assert.h transcode.$(OBJEXT): $(top_srcdir)/internal/string.h transcode.$(OBJEXT): $(top_srcdir)/internal/transcode.h +transcode.$(OBJEXT): $(top_srcdir)/internal/variable.h transcode.$(OBJEXT): $(top_srcdir)/internal/warnings.h transcode.$(OBJEXT): {$(VPATH)}assert.h transcode.$(OBJEXT): {$(VPATH)}backward/2/assume.h @@ -15714,6 +16016,7 @@ transcode.$(OBJEXT): {$(VPATH)}backward/2/long_long.h transcode.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h transcode.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h transcode.$(OBJEXT): {$(VPATH)}config.h +transcode.$(OBJEXT): {$(VPATH)}constant.h transcode.$(OBJEXT): {$(VPATH)}defines.h transcode.$(OBJEXT): {$(VPATH)}encoding.h transcode.$(OBJEXT): {$(VPATH)}id.h @@ -15872,6 +16175,7 @@ transcode.$(OBJEXT): {$(VPATH)}internal/xmalloc.h transcode.$(OBJEXT): {$(VPATH)}missing.h transcode.$(OBJEXT): {$(VPATH)}onigmo.h transcode.$(OBJEXT): {$(VPATH)}oniguruma.h +transcode.$(OBJEXT): {$(VPATH)}shape.h transcode.$(OBJEXT): {$(VPATH)}st.h transcode.$(OBJEXT): {$(VPATH)}subst.h transcode.$(OBJEXT): {$(VPATH)}transcode.c @@ -16421,6 +16725,7 @@ variable.$(OBJEXT): {$(VPATH)}ractor.h variable.$(OBJEXT): {$(VPATH)}ractor_core.h variable.$(OBJEXT): {$(VPATH)}ruby_assert.h variable.$(OBJEXT): {$(VPATH)}ruby_atomic.h +variable.$(OBJEXT): {$(VPATH)}shape.h variable.$(OBJEXT): {$(VPATH)}st.h variable.$(OBJEXT): {$(VPATH)}subst.h variable.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h @@ -16446,6 +16751,7 @@ version.$(OBJEXT): $(top_srcdir)/internal/gc.h version.$(OBJEXT): $(top_srcdir)/internal/imemo.h version.$(OBJEXT): $(top_srcdir)/internal/serial.h version.$(OBJEXT): $(top_srcdir)/internal/static_assert.h +version.$(OBJEXT): $(top_srcdir)/internal/variable.h version.$(OBJEXT): $(top_srcdir)/internal/vm.h version.$(OBJEXT): $(top_srcdir)/internal/warnings.h version.$(OBJEXT): $(top_srcdir)/revision.h @@ -16462,9 +16768,11 @@ version.$(OBJEXT): {$(VPATH)}backward/2/long_long.h version.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h version.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h version.$(OBJEXT): {$(VPATH)}config.h +version.$(OBJEXT): {$(VPATH)}constant.h version.$(OBJEXT): {$(VPATH)}debug_counter.h version.$(OBJEXT): {$(VPATH)}defines.h version.$(OBJEXT): {$(VPATH)}id.h +version.$(OBJEXT): {$(VPATH)}id_table.h version.$(OBJEXT): {$(VPATH)}intern.h version.$(OBJEXT): {$(VPATH)}internal.h version.$(OBJEXT): {$(VPATH)}internal/abi.h @@ -16613,6 +16921,7 @@ version.$(OBJEXT): {$(VPATH)}mjit.h version.$(OBJEXT): {$(VPATH)}node.h version.$(OBJEXT): {$(VPATH)}ruby_assert.h version.$(OBJEXT): {$(VPATH)}ruby_atomic.h +version.$(OBJEXT): {$(VPATH)}shape.h version.$(OBJEXT): {$(VPATH)}st.h version.$(OBJEXT): {$(VPATH)}subst.h version.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h @@ -16848,6 +17157,7 @@ vm.$(OBJEXT): {$(VPATH)}ractor.h vm.$(OBJEXT): {$(VPATH)}ractor_core.h vm.$(OBJEXT): {$(VPATH)}ruby_assert.h vm.$(OBJEXT): {$(VPATH)}ruby_atomic.h +vm.$(OBJEXT): {$(VPATH)}shape.h vm.$(OBJEXT): {$(VPATH)}st.h vm.$(OBJEXT): {$(VPATH)}subst.h vm.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h @@ -16884,6 +17194,7 @@ vm_backtrace.$(OBJEXT): $(top_srcdir)/internal/imemo.h vm_backtrace.$(OBJEXT): $(top_srcdir)/internal/serial.h vm_backtrace.$(OBJEXT): $(top_srcdir)/internal/static_assert.h vm_backtrace.$(OBJEXT): $(top_srcdir)/internal/string.h +vm_backtrace.$(OBJEXT): $(top_srcdir)/internal/variable.h vm_backtrace.$(OBJEXT): $(top_srcdir)/internal/vm.h vm_backtrace.$(OBJEXT): $(top_srcdir)/internal/warnings.h vm_backtrace.$(OBJEXT): {$(VPATH)}assert.h @@ -16898,11 +17209,13 @@ vm_backtrace.$(OBJEXT): {$(VPATH)}backward/2/long_long.h vm_backtrace.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h vm_backtrace.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h vm_backtrace.$(OBJEXT): {$(VPATH)}config.h +vm_backtrace.$(OBJEXT): {$(VPATH)}constant.h vm_backtrace.$(OBJEXT): {$(VPATH)}debug.h vm_backtrace.$(OBJEXT): {$(VPATH)}defines.h vm_backtrace.$(OBJEXT): {$(VPATH)}encoding.h vm_backtrace.$(OBJEXT): {$(VPATH)}eval_intern.h vm_backtrace.$(OBJEXT): {$(VPATH)}id.h +vm_backtrace.$(OBJEXT): {$(VPATH)}id_table.h vm_backtrace.$(OBJEXT): {$(VPATH)}intern.h vm_backtrace.$(OBJEXT): {$(VPATH)}internal.h vm_backtrace.$(OBJEXT): {$(VPATH)}internal/abi.h @@ -17062,6 +17375,7 @@ vm_backtrace.$(OBJEXT): {$(VPATH)}onigmo.h vm_backtrace.$(OBJEXT): {$(VPATH)}oniguruma.h vm_backtrace.$(OBJEXT): {$(VPATH)}ruby_assert.h vm_backtrace.$(OBJEXT): {$(VPATH)}ruby_atomic.h +vm_backtrace.$(OBJEXT): {$(VPATH)}shape.h vm_backtrace.$(OBJEXT): {$(VPATH)}st.h vm_backtrace.$(OBJEXT): {$(VPATH)}subst.h vm_backtrace.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h @@ -17252,6 +17566,7 @@ vm_dump.$(OBJEXT): {$(VPATH)}ractor.h vm_dump.$(OBJEXT): {$(VPATH)}ractor_core.h vm_dump.$(OBJEXT): {$(VPATH)}ruby_assert.h vm_dump.$(OBJEXT): {$(VPATH)}ruby_atomic.h +vm_dump.$(OBJEXT): {$(VPATH)}shape.h vm_dump.$(OBJEXT): {$(VPATH)}st.h vm_dump.$(OBJEXT): {$(VPATH)}subst.h vm_dump.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h @@ -17271,6 +17586,7 @@ vm_sync.$(OBJEXT): $(top_srcdir)/internal/gc.h vm_sync.$(OBJEXT): $(top_srcdir)/internal/imemo.h vm_sync.$(OBJEXT): $(top_srcdir)/internal/serial.h vm_sync.$(OBJEXT): $(top_srcdir)/internal/static_assert.h +vm_sync.$(OBJEXT): $(top_srcdir)/internal/variable.h vm_sync.$(OBJEXT): $(top_srcdir)/internal/vm.h vm_sync.$(OBJEXT): $(top_srcdir)/internal/warnings.h vm_sync.$(OBJEXT): {$(VPATH)}assert.h @@ -17285,6 +17601,7 @@ vm_sync.$(OBJEXT): {$(VPATH)}backward/2/long_long.h vm_sync.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h vm_sync.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h vm_sync.$(OBJEXT): {$(VPATH)}config.h +vm_sync.$(OBJEXT): {$(VPATH)}constant.h vm_sync.$(OBJEXT): {$(VPATH)}debug_counter.h vm_sync.$(OBJEXT): {$(VPATH)}defines.h vm_sync.$(OBJEXT): {$(VPATH)}gc.h @@ -17439,6 +17756,7 @@ vm_sync.$(OBJEXT): {$(VPATH)}ractor.h vm_sync.$(OBJEXT): {$(VPATH)}ractor_core.h vm_sync.$(OBJEXT): {$(VPATH)}ruby_assert.h vm_sync.$(OBJEXT): {$(VPATH)}ruby_atomic.h +vm_sync.$(OBJEXT): {$(VPATH)}shape.h vm_sync.$(OBJEXT): {$(VPATH)}st.h vm_sync.$(OBJEXT): {$(VPATH)}subst.h vm_sync.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h @@ -17462,6 +17780,7 @@ vm_trace.$(OBJEXT): $(top_srcdir)/internal/imemo.h vm_trace.$(OBJEXT): $(top_srcdir)/internal/serial.h vm_trace.$(OBJEXT): $(top_srcdir)/internal/static_assert.h vm_trace.$(OBJEXT): $(top_srcdir)/internal/symbol.h +vm_trace.$(OBJEXT): $(top_srcdir)/internal/variable.h vm_trace.$(OBJEXT): $(top_srcdir)/internal/vm.h vm_trace.$(OBJEXT): $(top_srcdir)/internal/warnings.h vm_trace.$(OBJEXT): {$(VPATH)}assert.h @@ -17477,12 +17796,14 @@ vm_trace.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h vm_trace.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h vm_trace.$(OBJEXT): {$(VPATH)}builtin.h vm_trace.$(OBJEXT): {$(VPATH)}config.h +vm_trace.$(OBJEXT): {$(VPATH)}constant.h vm_trace.$(OBJEXT): {$(VPATH)}debug.h vm_trace.$(OBJEXT): {$(VPATH)}debug_counter.h vm_trace.$(OBJEXT): {$(VPATH)}defines.h vm_trace.$(OBJEXT): {$(VPATH)}encoding.h vm_trace.$(OBJEXT): {$(VPATH)}eval_intern.h vm_trace.$(OBJEXT): {$(VPATH)}id.h +vm_trace.$(OBJEXT): {$(VPATH)}id_table.h vm_trace.$(OBJEXT): {$(VPATH)}intern.h vm_trace.$(OBJEXT): {$(VPATH)}internal.h vm_trace.$(OBJEXT): {$(VPATH)}internal/abi.h @@ -17644,6 +17965,7 @@ vm_trace.$(OBJEXT): {$(VPATH)}oniguruma.h vm_trace.$(OBJEXT): {$(VPATH)}ractor.h vm_trace.$(OBJEXT): {$(VPATH)}ruby_assert.h vm_trace.$(OBJEXT): {$(VPATH)}ruby_atomic.h +vm_trace.$(OBJEXT): {$(VPATH)}shape.h vm_trace.$(OBJEXT): {$(VPATH)}st.h vm_trace.$(OBJEXT): {$(VPATH)}subst.h vm_trace.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h diff --git a/compile.c b/compile.c index a5da919c0a..9051ecfcd6 100644 --- a/compile.c +++ b/compile.c @@ -2058,20 +2058,7 @@ cdhash_set_label_i(VALUE key, VALUE val, VALUE ptr) static inline VALUE get_ivar_ic_value(rb_iseq_t *iseq,ID id) { - VALUE val; - struct rb_id_table *tbl = ISEQ_COMPILE_DATA(iseq)->ivar_cache_table; - if (tbl) { - if (rb_id_table_lookup(tbl,id,&val)) { - return val; - } - } - else { - tbl = rb_id_table_create(1); - ISEQ_COMPILE_DATA(iseq)->ivar_cache_table = tbl; - } - val = INT2FIX(ISEQ_BODY(iseq)->ivc_size++); - rb_id_table_insert(tbl,id,val); - return val; + return INT2FIX(ISEQ_BODY(iseq)->ivc_size++); } static inline VALUE @@ -2472,9 +2459,13 @@ iseq_set_sequence(rb_iseq_t *iseq, LINK_ANCHOR *const anchor) generated_iseq[code_index + 1 + j] = (VALUE)ic; } break; + case TS_IVC: /* inline ivar cache */ + { + unsigned int ic_index = FIX2UINT(operands[j]); + vm_ic_attr_index_initialize(((IVC)&body->is_entries[ic_index]), INVALID_SHAPE_ID); + } case TS_ISE: /* inline storage entry: `once` insn */ case TS_ICVARC: /* inline cvar cache */ - case TS_IVC: /* inline ivar cache */ { unsigned int ic_index = FIX2UINT(operands[j]); IC ic = &ISEQ_IS_ENTRY_START(body, type)[ic_index].ic_cache; @@ -11514,6 +11505,11 @@ ibf_load_code(const struct ibf_load *load, rb_iseq_t *iseq, ibf_offset_t bytecod ISE ic = ISEQ_IS_ENTRY_START(load_body, operand_type) + op; code[code_index] = (VALUE)ic; + + if (operand_type == TS_IVC) { + vm_ic_attr_index_initialize(((IVC)code[code_index]), INVALID_SHAPE_ID); + } + } break; case TS_CALLDATA: diff --git a/debug_counter.h b/debug_counter.h index c6f4176e97..f3a799913b 100644 --- a/debug_counter.h +++ b/debug_counter.h @@ -130,7 +130,6 @@ RB_DEBUG_COUNTER(frame_C2R) /* instance variable counts * * * ivar_get_ic_hit/miss: ivar_get inline cache (ic) hit/miss counts (VM insn) - * * ivar_get_ic_miss_serial: ivar_get ic miss reason by serial (VM insn) * * ivar_get_ic_miss_unset: ... by unset (VM insn) * * ivar_get_ic_miss_noobject: ... by "not T_OBJECT" (VM insn) * * ivar_set_...: same counts with ivar_set (VM insn) @@ -140,17 +139,17 @@ RB_DEBUG_COUNTER(frame_C2R) */ RB_DEBUG_COUNTER(ivar_get_ic_hit) RB_DEBUG_COUNTER(ivar_get_ic_miss) -RB_DEBUG_COUNTER(ivar_get_ic_miss_serial) -RB_DEBUG_COUNTER(ivar_get_ic_miss_unset) RB_DEBUG_COUNTER(ivar_get_ic_miss_noobject) RB_DEBUG_COUNTER(ivar_set_ic_hit) RB_DEBUG_COUNTER(ivar_set_ic_miss) -RB_DEBUG_COUNTER(ivar_set_ic_miss_serial) -RB_DEBUG_COUNTER(ivar_set_ic_miss_unset) RB_DEBUG_COUNTER(ivar_set_ic_miss_iv_hit) RB_DEBUG_COUNTER(ivar_set_ic_miss_noobject) RB_DEBUG_COUNTER(ivar_get_base) RB_DEBUG_COUNTER(ivar_set_base) +RB_DEBUG_COUNTER(ivar_get_ic_miss_set) +RB_DEBUG_COUNTER(ivar_get_cc_miss_set) +RB_DEBUG_COUNTER(ivar_get_ic_miss_unset) +RB_DEBUG_COUNTER(ivar_get_cc_miss_unset) /* local variable counts * diff --git a/ext/coverage/depend b/ext/coverage/depend index 57d368d3f5..719c6c6e79 100644 --- a/ext/coverage/depend +++ b/ext/coverage/depend @@ -165,7 +165,9 @@ coverage.o: $(top_srcdir)/ccan/check_type/check_type.h coverage.o: $(top_srcdir)/ccan/container_of/container_of.h coverage.o: $(top_srcdir)/ccan/list/list.h coverage.o: $(top_srcdir)/ccan/str/str.h +coverage.o: $(top_srcdir)/constant.h coverage.o: $(top_srcdir)/gc.h +coverage.o: $(top_srcdir)/id_table.h coverage.o: $(top_srcdir)/internal.h coverage.o: $(top_srcdir)/internal/array.h coverage.o: $(top_srcdir)/internal/compilers.h @@ -176,12 +178,14 @@ coverage.o: $(top_srcdir)/internal/sanitizers.h coverage.o: $(top_srcdir)/internal/serial.h coverage.o: $(top_srcdir)/internal/static_assert.h coverage.o: $(top_srcdir)/internal/thread.h +coverage.o: $(top_srcdir)/internal/variable.h coverage.o: $(top_srcdir)/internal/vm.h coverage.o: $(top_srcdir)/internal/warnings.h coverage.o: $(top_srcdir)/method.h coverage.o: $(top_srcdir)/node.h coverage.o: $(top_srcdir)/ruby_assert.h coverage.o: $(top_srcdir)/ruby_atomic.h +coverage.o: $(top_srcdir)/shape.h coverage.o: $(top_srcdir)/thread_pthread.h coverage.o: $(top_srcdir)/vm_core.h coverage.o: $(top_srcdir)/vm_opts.h diff --git a/ext/objspace/depend b/ext/objspace/depend index c4da8031cc..88c66a232b 100644 --- a/ext/objspace/depend +++ b/ext/objspace/depend @@ -350,6 +350,7 @@ objspace.o: $(top_srcdir)/internal/serial.h objspace.o: $(top_srcdir)/internal/static_assert.h objspace.o: $(top_srcdir)/internal/warnings.h objspace.o: $(top_srcdir)/node.h +objspace.o: $(top_srcdir)/shape.h objspace.o: $(top_srcdir)/symbol.h objspace.o: objspace.c objspace.o: {$(VPATH)}id.h @@ -533,7 +534,9 @@ objspace_dump.o: $(top_srcdir)/ccan/check_type/check_type.h objspace_dump.o: $(top_srcdir)/ccan/container_of/container_of.h objspace_dump.o: $(top_srcdir)/ccan/list/list.h objspace_dump.o: $(top_srcdir)/ccan/str/str.h +objspace_dump.o: $(top_srcdir)/constant.h objspace_dump.o: $(top_srcdir)/gc.h +objspace_dump.o: $(top_srcdir)/id_table.h objspace_dump.o: $(top_srcdir)/internal.h objspace_dump.o: $(top_srcdir)/internal/array.h objspace_dump.o: $(top_srcdir)/internal/compilers.h @@ -544,12 +547,14 @@ objspace_dump.o: $(top_srcdir)/internal/sanitizers.h objspace_dump.o: $(top_srcdir)/internal/serial.h objspace_dump.o: $(top_srcdir)/internal/static_assert.h objspace_dump.o: $(top_srcdir)/internal/string.h +objspace_dump.o: $(top_srcdir)/internal/variable.h objspace_dump.o: $(top_srcdir)/internal/vm.h objspace_dump.o: $(top_srcdir)/internal/warnings.h objspace_dump.o: $(top_srcdir)/method.h objspace_dump.o: $(top_srcdir)/node.h objspace_dump.o: $(top_srcdir)/ruby_assert.h objspace_dump.o: $(top_srcdir)/ruby_atomic.h +objspace_dump.o: $(top_srcdir)/shape.h objspace_dump.o: $(top_srcdir)/thread_pthread.h objspace_dump.o: $(top_srcdir)/vm_core.h objspace_dump.o: $(top_srcdir)/vm_opts.h diff --git a/gc.c b/gc.c index 916584ed35..a5e37b72e2 100644 --- a/gc.c +++ b/gc.c @@ -2895,8 +2895,7 @@ rb_class_instance_allocate_internal(VALUE klass, VALUE flags, bool wb_protected) GC_ASSERT((flags & RUBY_T_MASK) == T_OBJECT); GC_ASSERT(flags & ROBJECT_EMBED); - st_table *index_tbl = RCLASS_IV_INDEX_TBL(klass); - uint32_t index_tbl_num_entries = index_tbl == NULL ? 0 : (uint32_t)index_tbl->num_entries; + uint32_t index_tbl_num_entries = RCLASS_EXT(klass)->max_iv_count; size_t size; bool embed = true; @@ -2931,7 +2930,7 @@ rb_class_instance_allocate_internal(VALUE klass, VALUE flags, bool wb_protected) #endif } else { - rb_init_iv_list(obj); + rb_ensure_iv_list_size(obj, 0, index_tbl_num_entries); } return obj; @@ -3206,20 +3205,6 @@ rb_free_const_table(struct rb_id_table *tbl) rb_id_table_free(tbl); } -static int -free_iv_index_tbl_free_i(st_data_t key, st_data_t value, st_data_t data) -{ - xfree((void *)value); - return ST_CONTINUE; -} - -static void -iv_index_tbl_free(struct st_table *tbl) -{ - st_foreach(tbl, free_iv_index_tbl_free_i, 0); - st_free_table(tbl); -} - // alive: if false, target pointers can be freed already. // To check it, we need objspace parameter. static void @@ -3435,6 +3420,16 @@ obj_free(rb_objspace_t *objspace, VALUE obj) RB_DEBUG_COUNTER_INC(obj_obj_transient); } else { + rb_shape_t *shape = rb_shape_get_shape_by_id(ROBJECT_SHAPE_ID(obj)); + if (shape) { + VALUE klass = RBASIC_CLASS(obj); + + // Increment max_iv_count if applicable, used to determine size pool allocation + uint32_t num_of_ivs = shape->iv_count; + if (RCLASS_EXT(klass)->max_iv_count < num_of_ivs) { + RCLASS_EXT(klass)->max_iv_count = num_of_ivs; + } + } xfree(RANY(obj)->as.object.as.heap.ivptr); RB_DEBUG_COUNTER_INC(obj_obj_ptr); } @@ -3449,9 +3444,6 @@ obj_free(rb_objspace_t *objspace, VALUE obj) if (RCLASS_CONST_TBL(obj)) { rb_free_const_table(RCLASS_CONST_TBL(obj)); } - if (RCLASS_IV_INDEX_TBL(obj)) { - iv_index_tbl_free(RCLASS_IV_INDEX_TBL(obj)); - } if (RCLASS_CVC_TBL(obj)) { rb_id_table_foreach_values(RCLASS_CVC_TBL(obj), cvar_table_free_i, NULL); rb_id_table_free(RCLASS_CVC_TBL(obj)); @@ -4873,10 +4865,6 @@ obj_memsize_of(VALUE obj, int use_all_types) if (RCLASS_CVC_TBL(obj)) { size += rb_id_table_memsize(RCLASS_CVC_TBL(obj)); } - if (RCLASS_IV_INDEX_TBL(obj)) { - // TODO: more correct value - size += st_memsize(RCLASS_IV_INDEX_TBL(obj)); - } if (RCLASS_EXT(obj)->iv_tbl) { size += st_memsize(RCLASS_EXT(obj)->iv_tbl); } @@ -10408,15 +10396,6 @@ update_subclass_entries(rb_objspace_t *objspace, rb_subclass_entry_t *entry) } } -static int -update_iv_index_tbl_i(st_data_t key, st_data_t value, st_data_t arg) -{ - rb_objspace_t *objspace = (rb_objspace_t *)arg; - struct rb_iv_index_tbl_entry *ent = (struct rb_iv_index_tbl_entry *)value; - UPDATE_IF_MOVED(objspace, ent->class_value); - return ST_CONTINUE; -} - static void update_class_ext(rb_objspace_t *objspace, rb_classext_t *ext) { @@ -10424,11 +10403,6 @@ update_class_ext(rb_objspace_t *objspace, rb_classext_t *ext) UPDATE_IF_MOVED(objspace, ext->includer); UPDATE_IF_MOVED(objspace, ext->refined_class); update_subclass_entries(objspace, ext->subclasses); - - // ext->iv_index_tbl - if (ext->iv_index_tbl) { - st_foreach(ext->iv_index_tbl, update_iv_index_tbl_i, (st_data_t)objspace); - } } static void diff --git a/include/ruby/internal/core/robject.h b/include/ruby/internal/core/robject.h index 7823061d8f..bec0b45fd4 100644 --- a/include/ruby/internal/core/robject.h +++ b/include/ruby/internal/core/robject.h @@ -46,7 +46,6 @@ #define ROBJECT_EMBED ROBJECT_EMBED #define ROBJECT_NUMIV ROBJECT_NUMIV #define ROBJECT_IVPTR ROBJECT_IVPTR -#define ROBJECT_IV_INDEX_TBL ROBJECT_IV_INDEX_TBL /** @endcond */ /** @@ -132,7 +131,7 @@ struct RObject { * * This is a shortcut for `RCLASS_IV_INDEX_TBL(rb_obj_class(obj))`. */ - struct st_table *iv_index_tbl; + struct rb_id_table *iv_index_tbl; } heap; #if USE_RVARGC diff --git a/include/ruby/internal/fl_type.h b/include/ruby/internal/fl_type.h index c51bd2e9d9..7383426b23 100644 --- a/include/ruby/internal/fl_type.h +++ b/include/ruby/internal/fl_type.h @@ -941,21 +941,8 @@ RB_OBJ_FREEZE_RAW(VALUE obj) RB_FL_SET_RAW(obj, RUBY_FL_FREEZE); } -/** - * Prevents further modifications to the given object. ::rb_eFrozenError shall - * be raised if modification is attempted. - * - * @param[out] x Object in question. - */ -static inline void -rb_obj_freeze_inline(VALUE x) -{ - if (RB_FL_ABLE(x)) { - RB_OBJ_FREEZE_RAW(x); - if (RBASIC_CLASS(x) && !(RBASIC(x)->flags & RUBY_FL_SINGLETON)) { - rb_freeze_singleton_class(x); - } - } -} +RUBY_SYMBOL_EXPORT_BEGIN +void rb_obj_freeze_inline(VALUE obj); +RUBY_SYMBOL_EXPORT_END #endif /* RBIMPL_FL_TYPE_H */ diff --git a/inits.c b/inits.c index 9decba3c11..d1204c1324 100644 --- a/inits.c +++ b/inits.c @@ -77,6 +77,7 @@ rb_call_inits(void) CALL(vm_stack_canary); CALL(ast); CALL(gc_stress); + CALL(shape); // enable builtin loading CALL(builtin); diff --git a/internal.h b/internal.h index 0740ae99e5..695c9cfb7e 100644 --- a/internal.h +++ b/internal.h @@ -48,9 +48,6 @@ #undef RHASH_TBL #undef RHASH_EMPTY_P -/* internal/object.h */ -#undef ROBJECT_IV_INDEX_TBL - /* internal/struct.h */ #undef RSTRUCT_LEN #undef RSTRUCT_PTR diff --git a/internal/class.h b/internal/class.h index ae680564a6..4a3e09ddc7 100644 --- a/internal/class.h +++ b/internal/class.h @@ -14,6 +14,7 @@ #include "ruby/internal/stdbool.h" /* for bool */ #include "ruby/intern.h" /* for rb_alloc_func_t */ #include "ruby/ruby.h" /* for struct RBasic */ +#include "shape.h" #ifdef RCLASS_SUPER # undef RCLASS_SUPER @@ -27,8 +28,8 @@ struct rb_subclass_entry { struct rb_iv_index_tbl_entry { uint32_t index; - rb_serial_t class_serial; - VALUE class_value; + shape_id_t source_shape_id; + shape_id_t dest_shape_id; }; struct rb_cvar_class_tbl_entry { @@ -38,7 +39,6 @@ struct rb_cvar_class_tbl_entry { }; struct rb_classext_struct { - struct st_table *iv_index_tbl; // ID -> struct rb_iv_index_tbl_entry struct st_table *iv_tbl; #if SIZEOF_SERIAL_T == SIZEOF_VALUE /* otherwise m_tbl is in struct RClass */ struct rb_id_table *m_tbl; @@ -64,6 +64,10 @@ struct rb_classext_struct { const VALUE refined_class; rb_alloc_func_t allocator; const VALUE includer; + uint32_t max_iv_count; +#if !SHAPE_IN_BASIC_FLAGS + shape_id_t shape_id; +#endif }; struct RClass { @@ -102,7 +106,6 @@ typedef struct rb_classext_struct rb_classext_t; #define RCLASS_CALLABLE_M_TBL(c) (RCLASS_EXT(c)->callable_m_tbl) #define RCLASS_CC_TBL(c) (RCLASS_EXT(c)->cc_tbl) #define RCLASS_CVC_TBL(c) (RCLASS_EXT(c)->cvc_tbl) -#define RCLASS_IV_INDEX_TBL(c) (RCLASS_EXT(c)->iv_index_tbl) #define RCLASS_ORIGIN(c) (RCLASS_EXT(c)->origin_) #define RCLASS_REFINED_CLASS(c) (RCLASS_EXT(c)->refined_class) #if SIZEOF_SERIAL_T == SIZEOF_VALUE diff --git a/internal/object.h b/internal/object.h index 88f3a44bc6..7b54e13dd2 100644 --- a/internal/object.h +++ b/internal/object.h @@ -9,11 +9,6 @@ * @brief Internal header for Object. */ #include "ruby/ruby.h" /* for VALUE */ -#include "internal/class.h" /* for RCLASS_IV_INDEX_TBL */ - -#ifdef ROBJECT_IV_INDEX_TBL -# undef ROBJECT_IV_INDEX_TBL -#endif /* object.c */ VALUE rb_class_search_ancestor(VALUE klass, VALUE super); @@ -26,7 +21,6 @@ int rb_bool_expected(VALUE, const char *, int raise); static inline void RBASIC_CLEAR_CLASS(VALUE obj); static inline void RBASIC_SET_CLASS_RAW(VALUE obj, VALUE klass); static inline void RBASIC_SET_CLASS(VALUE obj, VALUE klass); -static inline struct st_table *ROBJECT_IV_INDEX_TBL_inline(VALUE obj); RUBY_SYMBOL_EXPORT_BEGIN /* object.c (export) */ @@ -64,20 +58,4 @@ RBASIC_SET_CLASS(VALUE obj, VALUE klass) RBASIC_SET_CLASS_RAW(obj, klass); RB_OBJ_WRITTEN(obj, oldv, klass); } - -RBIMPL_ATTR_PURE() -static inline struct st_table * -ROBJECT_IV_INDEX_TBL_inline(VALUE obj) -{ - if (RB_FL_ANY_RAW(obj, ROBJECT_EMBED)) { - VALUE klass = rb_obj_class(obj); - return RCLASS_IV_INDEX_TBL(klass); - } - else { - const struct RObject *const ptr = ROBJECT(obj); - return ptr->as.heap.iv_index_tbl; - } -} -#define ROBJECT_IV_INDEX_TBL ROBJECT_IV_INDEX_TBL_inline - #endif /* INTERNAL_OBJECT_H */ diff --git a/internal/variable.h b/internal/variable.h index 1a19e8964b..47037a3392 100644 --- a/internal/variable.h +++ b/internal/variable.h @@ -37,6 +37,9 @@ static inline void ROBJ_TRANSIENT_SET(VALUE obj); static inline void ROBJ_TRANSIENT_UNSET(VALUE obj); uint32_t rb_obj_ensure_iv_index_mapping(VALUE obj, ID id); +struct gen_ivtbl; +int rb_gen_ivtbl_get(VALUE obj, ID id, struct gen_ivtbl **ivtbl); + RUBY_SYMBOL_EXPORT_BEGIN /* variable.c (export) */ void rb_mark_generic_ivar(VALUE); @@ -52,6 +55,8 @@ VALUE rb_gvar_set(ID, VALUE); VALUE rb_gvar_defined(ID); void rb_const_warn_if_deprecated(const rb_const_entry_t *, VALUE, ID); void rb_init_iv_list(VALUE obj); +void rb_ensure_iv_list_size(VALUE obj, uint32_t len, uint32_t newsize); +struct gen_ivtbl * rb_ensure_generic_iv_list_size(VALUE obj, uint32_t newsize); MJIT_SYMBOL_EXPORT_END static inline bool diff --git a/iseq.c b/iseq.c index aea781038b..5b1d9de106 100644 --- a/iseq.c +++ b/iseq.c @@ -230,18 +230,8 @@ rb_iseq_each_value(const rb_iseq_t *iseq, iseq_value_itr_t * func, void *data) union iseq_inline_storage_entry *is_entries = body->is_entries; if (body->is_entries) { - // IVC entries - for (unsigned int i = 0; i < body->ivc_size; i++, is_entries++) { - IVC ivc = (IVC)is_entries; - if (ivc->entry) { - RUBY_ASSERT(!RB_TYPE_P(ivc->entry->class_value, T_NONE)); - - VALUE nv = func(data, ivc->entry->class_value); - if (ivc->entry->class_value != nv) { - ivc->entry->class_value = nv; - } - } - } + // Skip iterating over ivc caches + is_entries += body->ivc_size; // ICVARC entries for (unsigned int i = 0; i < body->icvarc_size; i++, is_entries++) { diff --git a/lib/mjit/compiler.rb b/lib/mjit/compiler.rb index 49f28ab690..06f018c934 100644 --- a/lib/mjit/compiler.rb +++ b/lib/mjit/compiler.rb @@ -73,23 +73,6 @@ module RubyVM::MJIT src << "#undef GET_SELF\n" src << "#define GET_SELF() cfp_self\n" - # Generate merged ivar guards first if needed - if !status.compile_info.disable_ivar_cache && status.merge_ivar_guards_p - src << " if (UNLIKELY(!(RB_TYPE_P(GET_SELF(), T_OBJECT) && (rb_serial_t)#{status.ivar_serial} == RCLASS_SERIAL(RBASIC(GET_SELF())->klass) &&" - if USE_RVARGC - src << "#{status.max_ivar_index} < ROBJECT_NUMIV(GET_SELF())" # index < ROBJECT_NUMIV(obj) - else - if status.max_ivar_index >= ROBJECT_EMBED_LEN_MAX - src << "#{status.max_ivar_index} < ROBJECT_NUMIV(GET_SELF())" # index < ROBJECT_NUMIV(obj) && !RB_FL_ANY_RAW(obj, ROBJECT_EMBED) - else - src << "ROBJECT_EMBED_LEN_MAX == ROBJECT_NUMIV(GET_SELF())" # index < ROBJECT_NUMIV(obj) && RB_FL_ANY_RAW(obj, ROBJECT_EMBED) - end - end - src << "))) {\n" - src << " goto ivar_cancel;\n" - src << " }\n" - end - # Simulate `opt_pc` in setup_parameters_complex. Other PCs which may be passed by catch tables # are not considered since vm_exec doesn't call jit_exec for catch tables. if iseq.body.param.flags.has_opt @@ -103,6 +86,13 @@ module RubyVM::MJIT src << " }\n" end + # Generate merged ivar guards first if needed + if !status.compile_info.disable_ivar_cache && status.merge_ivar_guards_p + src << " if (UNLIKELY(!(RB_TYPE_P(GET_SELF(), T_OBJECT)))) {" + src << " goto ivar_cancel;\n" + src << " }\n" + end + C.fprintf(f, src) compile_insns(0, 0, status, iseq.body, f) compile_cancel_handler(f, iseq.body, status) @@ -363,52 +353,37 @@ module RubyVM::MJIT ic_copy = (status.is_entries + (C.iseq_inline_storage_entry.new(operands[1]) - body.is_entries)).iv_cache src = +'' - if !status.compile_info.disable_ivar_cache && ic_copy.entry + if !status.compile_info.disable_ivar_cache && ic_copy.source_shape_id != C.INVALID_SHAPE_ID # JIT: optimize away motion of sp and pc. This path does not call rb_warning() and so it's always leaf and not `handles_sp`. # compile_pc_and_sp(src, insn, stack_size, sp_inc, local_stack_p, next_pos) # JIT: prepare vm_getivar/vm_setivar arguments and variables src << "{\n" src << " VALUE obj = GET_SELF();\n" - src << " const uint32_t index = #{ic_copy.entry.index};\n" - if status.merge_ivar_guards_p - # JIT: Access ivar without checking these VM_ASSERTed prerequisites as we checked them in the beginning of `mjit_compile_body` - src << " VM_ASSERT(RB_TYPE_P(obj, T_OBJECT));\n" - src << " VM_ASSERT((rb_serial_t)#{ic_copy.entry.class_serial} == RCLASS_SERIAL(RBASIC(obj)->klass));\n" - src << " VM_ASSERT(index < ROBJECT_NUMIV(obj));\n" - if insn_name == :setinstancevariable - if USE_RVARGC - src << " if (LIKELY(!RB_OBJ_FROZEN_RAW(obj) && index < ROBJECT_NUMIV(obj))) {\n" - src << " RB_OBJ_WRITE(obj, &ROBJECT_IVPTR(obj)[index], stack[#{stack_size - 1}]);\n" - else - heap_ivar_p = status.max_ivar_index >= ROBJECT_EMBED_LEN_MAX - src << " if (LIKELY(!RB_OBJ_FROZEN_RAW(obj) && #{heap_ivar_p ? 'true' : 'RB_FL_ANY_RAW(obj, ROBJECT_EMBED)'})) {\n" - src << " RB_OBJ_WRITE(obj, &ROBJECT(obj)->as.#{heap_ivar_p ? 'heap.ivptr[index]' : 'ary[index]'}, stack[#{stack_size - 1}]);\n" - end - src << " }\n" - else - src << " VALUE val;\n" - if USE_RVARGC - src << " if (LIKELY(index < ROBJECT_NUMIV(obj) && (val = ROBJECT_IVPTR(obj)[index]) != Qundef)) {\n" - else - heap_ivar_p = status.max_ivar_index >= ROBJECT_EMBED_LEN_MAX - src << " if (LIKELY(#{heap_ivar_p ? 'true' : 'RB_FL_ANY_RAW(obj, ROBJECT_EMBED)'} && (val = ROBJECT(obj)->as.#{heap_ivar_p ? 'heap.ivptr[index]' : 'ary[index]'}) != Qundef)) {\n" - end - src << " stack[#{stack_size}] = val;\n" - src << " }\n" - end + src << " const shape_id_t source_shape_id = (rb_serial_t)#{ic_copy.source_shape_id};\n" + # JIT: cache hit path of vm_getivar/vm_setivar, or cancel JIT (recompile it with exivar) + if insn_name == :setinstancevariable + src << " const uint32_t index = #{ic_copy.attr_index - 1};\n" + src << " const shape_id_t dest_shape_id = (rb_serial_t)#{ic_copy.dest_shape_id};\n" + src << " if (source_shape_id == ROBJECT_SHAPE_ID(obj) && \n" + src << " dest_shape_id != ROBJECT_SHAPE_ID(obj)) {\n" + src << " if (UNLIKELY(index >= ROBJECT_NUMIV(obj))) {\n" + src << " rb_init_iv_list(obj);\n" + src << " }\n" + src << " ROBJECT_SET_SHAPE_ID(obj, dest_shape_id);\n" + src << " VALUE *ptr = ROBJECT_IVPTR(obj);\n" + src << " RB_OBJ_WRITE(obj, &ptr[index], stack[#{stack_size - 1}]);\n" + src << " }\n" else - src << " const rb_serial_t ic_serial = (rb_serial_t)#{ic_copy.entry.class_serial};\n" - # JIT: cache hit path of vm_getivar/vm_setivar, or cancel JIT (recompile it with exivar) - if insn_name == :setinstancevariable - src << " if (LIKELY(RB_TYPE_P(obj, T_OBJECT) && ic_serial == RCLASS_SERIAL(RBASIC(obj)->klass) && index < ROBJECT_NUMIV(obj) && !RB_OBJ_FROZEN_RAW(obj))) {\n" - src << " VALUE *ptr = ROBJECT_IVPTR(obj);\n" - src << " RB_OBJ_WRITE(obj, &ptr[index], stack[#{stack_size - 1}]);\n" + if ic_copy.attr_index == 0 # cache hit, but uninitialized iv + src << " /* Uninitialized instance variable */\n" + src << " if (source_shape_id == ROBJECT_SHAPE_ID(obj)) {\n" + src << " stack[#{stack_size}] = Qnil;\n" src << " }\n" else - src << " VALUE val;\n" - src << " if (LIKELY(RB_TYPE_P(obj, T_OBJECT) && ic_serial == RCLASS_SERIAL(RBASIC(obj)->klass) && index < ROBJECT_NUMIV(obj) && (val = ROBJECT_IVPTR(obj)[index]) != Qundef)) {\n" - src << " stack[#{stack_size}] = val;\n" + src << " const uint32_t index = #{ic_copy.attr_index - 1};\n" + src << " if (source_shape_id == ROBJECT_SHAPE_ID(obj)) {\n" + src << " stack[#{stack_size}] = ROBJECT_IVPTR(obj)[index];\n" src << " }\n" end end @@ -419,20 +394,19 @@ module RubyVM::MJIT src << " }\n" src << "}\n" return src - elsif insn_name == :getinstancevariable && !status.compile_info.disable_exivar_cache && ic_copy.entry + elsif insn_name == :getinstancevariable && !status.compile_info.disable_exivar_cache && ic_copy.source_shape_id != C.INVALID_SHAPE_ID # JIT: optimize away motion of sp and pc. This path does not call rb_warning() and so it's always leaf and not `handles_sp`. # compile_pc_and_sp(src, insn, stack_size, sp_inc, local_stack_p, next_pos) # JIT: prepare vm_getivar's arguments and variables src << "{\n" src << " VALUE obj = GET_SELF();\n" - src << " const rb_serial_t ic_serial = (rb_serial_t)#{ic_copy.entry.class_serial};\n" - src << " const uint32_t index = #{ic_copy.entry.index};\n" + src << " const shape_id_t source_shape_id = (rb_serial_t)#{ic_copy.source_shape_id};\n" + src << " const uint32_t index = #{ic_copy.attr_index - 1};\n" # JIT: cache hit path of vm_getivar, or cancel JIT (recompile it without any ivar optimization) src << " struct gen_ivtbl *ivtbl;\n" - src << " VALUE val;\n" - src << " if (LIKELY(FL_TEST_RAW(obj, FL_EXIVAR) && ic_serial == RCLASS_SERIAL(RBASIC(obj)->klass) && rb_ivar_generic_ivtbl_lookup(obj, &ivtbl) && index < ivtbl->numiv && (val = ivtbl->ivptr[index]) != Qundef)) {\n" - src << " stack[#{stack_size}] = val;\n" + src << " if (LIKELY(FL_TEST_RAW(obj, FL_EXIVAR) && source_shape_id == rb_shape_get_shape_id(obj) && rb_ivar_generic_ivtbl_lookup(obj, &ivtbl))) {\n" + src << " stack[#{stack_size}] = ivtbl->ivptr[index];\n" src << " }\n" src << " else {\n" src << " reg_cfp->pc = original_body_iseq + #{pos};\n" @@ -832,35 +806,16 @@ module RubyVM::MJIT def init_ivar_compile_status(body, status) C.mjit_capture_is_entries(body, status.is_entries) - num_ivars = 0 pos = 0 - status.max_ivar_index = 0 - status.ivar_serial = 0 while pos < body.iseq_size insn = INSNS.fetch(C.rb_vm_insn_decode(body.iseq_encoded[pos])) if insn.name == :getinstancevariable || insn.name == :setinstancevariable - ic = body.iseq_encoded[pos+2] - ic_copy = (status.is_entries + (C.iseq_inline_storage_entry.new(ic) - body.is_entries)).iv_cache - if ic_copy.entry # Only initialized (ic_serial > 0) IVCs are optimized - num_ivars += 1 - - if status.max_ivar_index < ic_copy.entry.index - status.max_ivar_index = ic_copy.entry.index - end - - if status.ivar_serial == 0 - status.ivar_serial = ic_copy.entry.class_serial - elsif status.ivar_serial != ic_copy.entry.class_serial - # Multiple classes have used this ISeq. Give up assuming one serial. - status.merge_ivar_guards_p = false - return - end - end + status.merge_ivar_guards_p = true + return end pos += insn.len end - status.merge_ivar_guards_p = status.ivar_serial > 0 && num_ivars >= 2 end # Expand simple macro that doesn't require dynamic C code. diff --git a/marshal.c b/marshal.c index e4b40c0607..59edbfe53a 100644 --- a/marshal.c +++ b/marshal.c @@ -39,6 +39,7 @@ #include "ruby/st.h" #include "ruby/util.h" #include "builtin.h" +#include "shape.h" #define BITSPERSHORT (2*CHAR_BIT) #define SHORTMASK ((1<num_ivar) { - rb_raise(rb_eRuntimeError, "instance variable added to %"PRIsVALUE" instance", - CLASS_OF(arg->obj)); - } --ivarg->num_ivar; w_symbol(ID2SYM(id), arg->arg); w_object(value, arg->arg, arg->limit); @@ -720,6 +717,7 @@ has_ivars(VALUE obj, VALUE encname, VALUE *ivobj) static void w_ivar_each(VALUE obj, st_index_t num, struct dump_call_arg *arg) { + shape_id_t shape_id = rb_shape_get_shape_id(arg->obj); struct w_ivar_arg ivarg = {arg, num}; if (!num) return; rb_ivar_foreach(obj, w_obj_each, (st_data_t)&ivarg); @@ -727,6 +725,10 @@ w_ivar_each(VALUE obj, st_index_t num, struct dump_call_arg *arg) rb_raise(rb_eRuntimeError, "instance variable removed from %"PRIsVALUE" instance", CLASS_OF(arg->obj)); } + if (shape_id != rb_shape_get_shape_id(arg->obj)) { + rb_raise(rb_eRuntimeError, "instance variable added to %"PRIsVALUE" instance", + CLASS_OF(arg->obj)); + } } static void diff --git a/misc/lldb_cruby.py b/misc/lldb_cruby.py index 595d54dfab..9ef9d3967b 100755 --- a/misc/lldb_cruby.py +++ b/misc/lldb_cruby.py @@ -418,6 +418,7 @@ def lldb_inspect(debugger, target, result, val): elif flType == RUBY_T_IMEMO: # I'm not sure how to get IMEMO_MASK out of lldb. It's not in globals() imemo_type = (flags >> RUBY_FL_USHIFT) & 0x0F # IMEMO_MASK + print("T_IMEMO: ", file=result) append_command_output(debugger, "p (enum imemo_type) %d" % imemo_type, result) append_command_output(debugger, "p *(struct MEMO *) %0#x" % val.GetValueAsUnsigned(), result) diff --git a/mjit_c.rb b/mjit_c.rb index d8e5628bda..0f8e11cbe5 100644 --- a/mjit_c.rb +++ b/mjit_c.rb @@ -5,6 +5,10 @@ module RubyVM::MJIT C = Object.new class << C + def SHAPE_BITS + RubyVM::Shape::SHAPE_BITS + end + def ROBJECT_EMBED_LEN_MAX Primitive.cexpr! 'INT2NUM(RBIMPL_EMBED_LEN_MAX_OF(VALUE))' end @@ -165,6 +169,14 @@ module RubyVM::MJIT Primitive.cexpr! %q{ INT2NUM(VM_METHOD_TYPE_ISEQ) } end + def C.INVALID_SHAPE_ID + Primitive.cexpr! %q{ ULONG2NUM(INVALID_SHAPE_ID) } + end + + def C.SHAPE_MASK + Primitive.cexpr! %q{ ULONG2NUM(SHAPE_MASK) } + end + def C.CALL_DATA @CALL_DATA ||= self.rb_call_data end @@ -181,6 +193,10 @@ module RubyVM::MJIT @RB_BUILTIN ||= self.rb_builtin_function end + def C.attr_index_t + @attr_index_t ||= CType::Immediate.parse("uint32_t") + end + def C.compile_branch @compile_branch ||= CType::Struct.new( "compile_branch", Primitive.cexpr!("SIZEOF(struct compile_branch)"), @@ -201,7 +217,6 @@ module RubyVM::MJIT compiled_id: [CType::Immediate.parse("int"), Primitive.cexpr!("OFFSETOF((*((struct compile_status *)NULL)), compiled_id)")], compile_info: [CType::Pointer.new { self.rb_mjit_compile_info }, Primitive.cexpr!("OFFSETOF((*((struct compile_status *)NULL)), compile_info)")], merge_ivar_guards_p: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct compile_status *)NULL)), merge_ivar_guards_p)")], - ivar_serial: [self.rb_serial_t, Primitive.cexpr!("OFFSETOF((*((struct compile_status *)NULL)), ivar_serial)")], max_ivar_index: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct compile_status *)NULL)), max_ivar_index)")], inlined_iseqs: [CType::Pointer.new { CType::Pointer.new { self.rb_iseq_constant_body } }, Primitive.cexpr!("OFFSETOF((*((struct compile_status *)NULL)), inlined_iseqs)")], inline_context: [self.inlined_call_context, Primitive.cexpr!("OFFSETOF((*((struct compile_status *)NULL)), inline_context)")], @@ -240,7 +255,9 @@ module RubyVM::MJIT def C.iseq_inline_iv_cache_entry @iseq_inline_iv_cache_entry ||= CType::Struct.new( "iseq_inline_iv_cache_entry", Primitive.cexpr!("SIZEOF(struct iseq_inline_iv_cache_entry)"), - entry: [CType::Pointer.new { self.rb_iv_index_tbl_entry }, Primitive.cexpr!("OFFSETOF((*((struct iseq_inline_iv_cache_entry *)NULL)), entry)")], + source_shape_id: [self.shape_id_t, Primitive.cexpr!("OFFSETOF((*((struct iseq_inline_iv_cache_entry *)NULL)), source_shape_id)")], + dest_shape_id: [self.shape_id_t, Primitive.cexpr!("OFFSETOF((*((struct iseq_inline_iv_cache_entry *)NULL)), dest_shape_id)")], + attr_index: [self.attr_index_t, Primitive.cexpr!("OFFSETOF((*((struct iseq_inline_iv_cache_entry *)NULL)), attr_index)")], ) end @@ -313,7 +330,11 @@ module RubyVM::MJIT call_: [self.vm_call_handler, Primitive.cexpr!("OFFSETOF((*((struct rb_callcache *)NULL)), call_)")], aux_: [CType::Union.new( "", Primitive.cexpr!("SIZEOF(((struct rb_callcache *)NULL)->aux_)"), - attr_index: CType::Immediate.parse("unsigned int"), + attr: CType::Struct.new( + "", Primitive.cexpr!("SIZEOF(((struct rb_callcache *)NULL)->aux_.attr)"), + index: [self.attr_index_t, Primitive.cexpr!("OFFSETOF(((struct rb_callcache *)NULL)->aux_.attr, index)")], + dest_shape_id: [self.shape_id_t, Primitive.cexpr!("OFFSETOF(((struct rb_callcache *)NULL)->aux_.attr, dest_shape_id)")], + ), method_missing_reason: self.method_missing_reason, v: self.VALUE, ), Primitive.cexpr!("OFFSETOF((*((struct rb_callcache *)NULL)), aux_)")], @@ -503,8 +524,8 @@ module RubyVM::MJIT @rb_iv_index_tbl_entry ||= CType::Struct.new( "rb_iv_index_tbl_entry", Primitive.cexpr!("SIZEOF(struct rb_iv_index_tbl_entry)"), index: [CType::Immediate.parse("uint32_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_iv_index_tbl_entry *)NULL)), index)")], - class_serial: [self.rb_serial_t, Primitive.cexpr!("OFFSETOF((*((struct rb_iv_index_tbl_entry *)NULL)), class_serial)")], - class_value: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_iv_index_tbl_entry *)NULL)), class_value)")], + source_shape_id: [self.shape_id_t, Primitive.cexpr!("OFFSETOF((*((struct rb_iv_index_tbl_entry *)NULL)), source_shape_id)")], + dest_shape_id: [self.shape_id_t, Primitive.cexpr!("OFFSETOF((*((struct rb_iv_index_tbl_entry *)NULL)), dest_shape_id)")], ) end @@ -577,6 +598,10 @@ module RubyVM::MJIT @VALUE ||= CType::Immediate.find(Primitive.cexpr!("SIZEOF(VALUE)"), Primitive.cexpr!("SIGNED_TYPE_P(VALUE)")) end + def C.shape_id_t + @shape_id_t ||= CType::Immediate.find(Primitive.cexpr!("SIZEOF(shape_id_t)"), Primitive.cexpr!("SIGNED_TYPE_P(shape_id_t)")) + end + def C._Bool CType::Bool.new end diff --git a/mjit_compiler.h b/mjit_compiler.h index da79054420..b465be00fd 100644 --- a/mjit_compiler.h +++ b/mjit_compiler.h @@ -8,6 +8,7 @@ #include "builtin.h" #include "mjit.h" #include "mjit_unit.h" +#include "shape.h" // Macros to check if a position is already compiled using compile_status.stack_size_for_pos #define NOT_COMPILED_STACK_SIZE -1 @@ -48,7 +49,6 @@ struct compile_status { // Mutated optimization levels struct rb_mjit_compile_info *compile_info; bool merge_ivar_guards_p; // If true, merge guards of ivar accesses - rb_serial_t ivar_serial; // ic_serial of IVC in is_entries (used only when merge_ivar_guards_p) size_t max_ivar_index; // Max IVC index in is_entries (used only when merge_ivar_guards_p) // If `inlined_iseqs[pos]` is not NULL, `mjit_compile_body` tries to inline ISeq there. const struct rb_iseq_constant_body **inlined_iseqs; diff --git a/object.c b/object.c index c47d9ab3b2..648946beb3 100644 --- a/object.c +++ b/object.c @@ -39,6 +39,7 @@ #include "ruby/util.h" #include "ruby/assert.h" #include "builtin.h" +#include "shape.h" /*! * \addtogroup object @@ -271,9 +272,33 @@ rb_obj_copy_ivar(VALUE dest, VALUE obj) VALUE *src_buf = ROBJECT_IVPTR(obj); uint32_t dest_len = ROBJECT_NUMIV(dest); uint32_t src_len = ROBJECT_NUMIV(obj); - uint32_t len = dest_len < src_len ? dest_len : src_len; + uint32_t max_len = dest_len < src_len ? src_len : dest_len; - MEMCPY(dest_buf, src_buf, VALUE, len); + rb_ensure_iv_list_size(dest, dest_len, max_len); + + dest_len = ROBJECT_NUMIV(dest); + uint32_t min_len = dest_len > src_len ? src_len : dest_len; + + if (RBASIC(obj)->flags & ROBJECT_EMBED) { + src_buf = ROBJECT(obj)->as.ary; + + // embedded -> embedded + if (RBASIC(dest)->flags & ROBJECT_EMBED) { + dest_buf = ROBJECT(dest)->as.ary; + } + // embedded -> extended + else { + dest_buf = ROBJECT(dest)->as.heap.ivptr; + } + } + // extended -> extended + else { + RUBY_ASSERT(!(RBASIC(dest)->flags & ROBJECT_EMBED)); + dest_buf = ROBJECT(dest)->as.heap.ivptr; + src_buf = ROBJECT(obj)->as.heap.ivptr; + } + + MEMCPY(dest_buf, src_buf, VALUE, min_len); } static void @@ -283,10 +308,23 @@ init_copy(VALUE dest, VALUE obj) rb_raise(rb_eTypeError, "[bug] frozen object (%s) allocated", rb_obj_classname(dest)); } RBASIC(dest)->flags &= ~(T_MASK|FL_EXIVAR); + // Copies the shape id from obj to dest RBASIC(dest)->flags |= RBASIC(obj)->flags & (T_MASK|FL_EXIVAR); rb_copy_wb_protected_attribute(dest, obj); rb_copy_generic_ivar(dest, obj); rb_gc_copy_finalizer(dest, obj); + + rb_shape_t *shape_to_set = rb_shape_get_shape(obj); + + // If the object is frozen, the "dup"'d object will *not* be frozen, + // so we need to copy the frozen shape's parent to the new object. + if (rb_shape_frozen_shape_p(shape_to_set)) { + shape_to_set = shape_to_set->parent; + } + + // shape ids are different + rb_shape_set_shape(dest, shape_to_set); + if (RB_TYPE_P(obj, T_OBJECT)) { rb_obj_copy_ivar(dest, obj); } @@ -392,6 +430,9 @@ mutable_obj_clone(VALUE obj, VALUE kwfreeze) case Qnil: rb_funcall(clone, id_init_clone, 1, obj); RBASIC(clone)->flags |= RBASIC(obj)->flags & FL_FREEZE; + if (RB_OBJ_FROZEN(obj)) { + rb_shape_transition_shape_frozen(clone); + } break; case Qtrue: { @@ -407,6 +448,7 @@ mutable_obj_clone(VALUE obj, VALUE kwfreeze) argv[1] = freeze_true_hash; rb_funcallv_kw(clone, id_init_clone, 2, argv, RB_PASS_KEYWORDS); RBASIC(clone)->flags |= FL_FREEZE; + rb_shape_transition_shape_frozen(clone); break; } case Qfalse: diff --git a/ractor_core.h b/ractor_core.h index a065f5f809..9d4b8387c7 100644 --- a/ractor_core.h +++ b/ractor_core.h @@ -289,11 +289,13 @@ rb_ractor_id(const rb_ractor_t *r) #if RACTOR_CHECK_MODE > 0 uint32_t rb_ractor_current_id(void); +// If ractor check mode is enabled, shape bits needs to be smaller +STATIC_ASSERT(shape_bits, SHAPE_BITS == 16); static inline void rb_ractor_setup_belonging_to(VALUE obj, uint32_t rid) { - VALUE flags = RBASIC(obj)->flags & 0xffffffff; // 4B + VALUE flags = RBASIC(obj)->flags & 0xffff0000ffffffff; // 4B RBASIC(obj)->flags = flags | ((VALUE)rid << 32); } @@ -310,7 +312,7 @@ rb_ractor_belonging(VALUE obj) return 0; } else { - return RBASIC(obj)->flags >> 32; + return RBASIC(obj)->flags >> 32 & 0xFFFF; } } diff --git a/shape.c b/shape.c new file mode 100644 index 0000000000..a6a5adf854 --- /dev/null +++ b/shape.c @@ -0,0 +1,523 @@ +#include "vm_core.h" +#include "vm_sync.h" +#include "shape.h" +#include "internal/class.h" +#include "internal/symbol.h" +#include "internal/variable.h" +#include + +/* + * Shape getters + */ +static rb_shape_t* +rb_shape_get_root_shape(void) { + return GET_VM()->root_shape; +} + +shape_id_t +rb_shape_id(rb_shape_t * shape) +{ + return (shape_id_t)(shape - GET_VM()->shape_list); +} + +static rb_shape_t* +rb_shape_get_frozen_root_shape(void) { + return GET_VM()->frozen_root_shape; +} + +bool +rb_shape_root_shape_p(rb_shape_t* shape) { + return shape == rb_shape_get_root_shape(); +} + +rb_shape_t* +rb_shape_get_shape_by_id(shape_id_t shape_id) +{ + RUBY_ASSERT(shape_id != INVALID_SHAPE_ID); + + rb_vm_t *vm = GET_VM(); + rb_shape_t *shape = &vm->shape_list[shape_id]; + return shape; +} + +rb_shape_t* +rb_shape_get_shape_by_id_without_assertion(shape_id_t shape_id) +{ + RUBY_ASSERT(shape_id != INVALID_SHAPE_ID); + + rb_vm_t *vm = GET_VM(); + rb_shape_t *shape = &vm->shape_list[shape_id]; + return shape; +} + +#if !SHAPE_IN_BASIC_FLAGS +static inline shape_id_t +RCLASS_SHAPE_ID(VALUE obj) +{ + return RCLASS_EXT(obj)->shape_id; +} + +shape_id_t rb_generic_shape_id(VALUE obj); +#endif + +shape_id_t +rb_shape_get_shape_id(VALUE obj) +{ + if (RB_SPECIAL_CONST_P(obj)) { + return FROZEN_ROOT_SHAPE_ID; + } + +#if SHAPE_IN_BASIC_FLAGS + return RBASIC_SHAPE_ID(obj); +#else + switch (BUILTIN_TYPE(obj)) { + case T_OBJECT: + return ROBJECT_SHAPE_ID(obj); + break; + case T_CLASS: + case T_MODULE: + return RCLASS_SHAPE_ID(obj); + default: + return rb_generic_shape_id(obj); + } +#endif +} + +rb_shape_t* +rb_shape_get_shape(VALUE obj) +{ + return rb_shape_get_shape_by_id(rb_shape_get_shape_id(obj)); +} + +static rb_shape_t * +rb_shape_lookup_id(rb_shape_t* shape, ID id, enum shape_type shape_type) { + while (shape->parent) { + if (shape->edge_name == id) { + // If the shape type is different, we don't + // want this to count as a "found" ID + if (shape_type == (enum shape_type)shape->type) { + return shape; + } + else { + return NULL; + } + } + shape = shape->parent; + } + return NULL; +} + +static rb_shape_t* +get_next_shape_internal(rb_shape_t* shape, ID id, VALUE obj, enum shape_type shape_type) +{ + rb_shape_t *res = NULL; + RUBY_ASSERT(SHAPE_FROZEN != (enum shape_type)shape->type); + RB_VM_LOCK_ENTER(); + { + if (rb_shape_lookup_id(shape, id, shape_type)) { + // If shape already contains the ivar that is being set, we'll return shape + res = shape; + } + else { + if (!shape->edges) { + shape->edges = rb_id_table_create(0); + } + + // Lookup the shape in edges - if there's already an edge and a corresponding shape for it, + // we can return that. Otherwise, we'll need to get a new shape + if (!rb_id_table_lookup(shape->edges, id, (VALUE *)&res)) { + // In this case, the shape exists, but the shape is garbage, so we need to recreate it + if (res) { + rb_id_table_delete(shape->edges, id); + res->parent = NULL; + } + + rb_shape_t * new_shape = rb_shape_alloc(id, shape); + + new_shape->type = (uint8_t)shape_type; + + switch(shape_type) { + case SHAPE_IVAR: + new_shape->iv_count = new_shape->parent->iv_count + 1; + + // Check if we should update max_iv_count on the object's class + if (BUILTIN_TYPE(obj) == T_OBJECT) { + VALUE klass = rb_obj_class(obj); + if (new_shape->iv_count > RCLASS_EXT(klass)->max_iv_count) { + RCLASS_EXT(klass)->max_iv_count = new_shape->iv_count; + } + } + break; + case SHAPE_IVAR_UNDEF: + case SHAPE_FROZEN: + new_shape->iv_count = new_shape->parent->iv_count; + break; + case SHAPE_ROOT: + rb_bug("Unreachable"); + break; + } + + rb_id_table_insert(shape->edges, id, (VALUE)new_shape); + + res = new_shape; + } + } + } + RB_VM_LOCK_LEAVE(); + return res; +} + +MJIT_FUNC_EXPORTED int +rb_shape_frozen_shape_p(rb_shape_t* shape) +{ + return SHAPE_FROZEN == (enum shape_type)shape->type; +} + +void +rb_shape_transition_shape_remove_ivar(VALUE obj, ID id, rb_shape_t *shape) +{ + rb_shape_t* next_shape = get_next_shape_internal(shape, id, obj, SHAPE_IVAR_UNDEF); + + if (shape == next_shape) { + return; + } + + rb_shape_set_shape(obj, next_shape); +} + +void +rb_shape_transition_shape_frozen(VALUE obj) +{ + rb_shape_t* shape = rb_shape_get_shape(obj); + RUBY_ASSERT(shape); + RUBY_ASSERT(RB_OBJ_FROZEN(obj)); + + if (rb_shape_frozen_shape_p(shape)) { + return; + } + + rb_shape_t* next_shape; + + if (shape == rb_shape_get_root_shape()) { + switch(BUILTIN_TYPE(obj)) { + case T_OBJECT: + case T_CLASS: + case T_MODULE: + break; + default: + return; + } + next_shape = rb_shape_get_frozen_root_shape(); + } + else { + static ID id_frozen; + if (!id_frozen) { + id_frozen = rb_make_internal_id(); + } + + next_shape = get_next_shape_internal(shape, (ID)id_frozen, obj, SHAPE_FROZEN); + } + + RUBY_ASSERT(next_shape); + rb_shape_set_shape(obj, next_shape); +} + +void +rb_shape_transition_shape(VALUE obj, ID id, rb_shape_t *shape) +{ + rb_shape_t* next_shape = rb_shape_get_next(shape, obj, id); + if (shape == next_shape) { + return; + } + rb_shape_set_shape(obj, next_shape); +} + +rb_shape_t* +rb_shape_get_next(rb_shape_t* shape, VALUE obj, ID id) +{ + return get_next_shape_internal(shape, id, obj, SHAPE_IVAR); +} + +bool +rb_shape_get_iv_index(rb_shape_t * shape, ID id, attr_index_t *value) { + while (shape->parent) { + if (shape->edge_name == id) { + enum shape_type shape_type; + shape_type = (enum shape_type)shape->type; + + switch(shape_type) { + case SHAPE_IVAR: + RUBY_ASSERT(shape->iv_count > 0); + *value = shape->iv_count - 1; + return true; + case SHAPE_IVAR_UNDEF: + case SHAPE_ROOT: + return false; + case SHAPE_FROZEN: + rb_bug("Ivar should not exist on frozen transition\n"); + } + } + shape = shape->parent; + } + return false; +} + +static rb_shape_t * +shape_alloc(void) +{ + rb_vm_t *vm = GET_VM(); + shape_id_t shape_id = vm->next_shape_id; + vm->next_shape_id++; + + if (shape_id == MAX_SHAPE_ID) { + // TODO: Make an OutOfShapesError ?? + rb_bug("Out of shapes\n"); + } + + return &GET_VM()->shape_list[shape_id]; +} + +rb_shape_t * +rb_shape_alloc(ID edge_name, rb_shape_t * parent) +{ + rb_shape_t * shape = shape_alloc(); + + shape->edge_name = edge_name; + shape->iv_count = 0; + shape->parent = parent; + + return shape; +} + +MJIT_FUNC_EXPORTED void +rb_shape_set_shape(VALUE obj, rb_shape_t* shape) +{ + rb_shape_set_shape_id(obj, rb_shape_id(shape)); +} + +VALUE rb_cShape; + +/* + * Exposing Shape to Ruby via RubyVM.debug_shape + */ +static const rb_data_type_t shape_data_type = { + "Shape", + {NULL, NULL, NULL,}, + 0, 0, RUBY_TYPED_FREE_IMMEDIATELY|RUBY_TYPED_WB_PROTECTED +}; + +static VALUE +rb_wrapped_shape_id(VALUE self) { + rb_shape_t * shape; + TypedData_Get_Struct(self, rb_shape_t, &shape_data_type, shape); + return INT2NUM(rb_shape_id(shape)); +} + +static VALUE +rb_shape_type(VALUE self) { + rb_shape_t * shape; + TypedData_Get_Struct(self, rb_shape_t, &shape_data_type, shape); + return INT2NUM(shape->type); +} + +static VALUE +rb_shape_parent_id(VALUE self) +{ + rb_shape_t * shape; + TypedData_Get_Struct(self, rb_shape_t, &shape_data_type, shape); + if (shape->parent) { + return INT2NUM(rb_shape_id(shape->parent)); + } + else { + return Qnil; + } +} + +static VALUE parse_key(ID key) { + if ((key & RUBY_ID_INTERNAL) == RUBY_ID_INTERNAL) { + return LONG2NUM(key); + } else { + return ID2SYM(key); + } +} + +static VALUE +rb_shape_t_to_rb_cShape(rb_shape_t *shape) { + union { const rb_shape_t *in; void *out; } deconst; + VALUE res; + deconst.in = shape; + res = TypedData_Wrap_Struct(rb_cShape, &shape_data_type, deconst.out); + + return res; +} + +static enum rb_id_table_iterator_result rb_edges_to_hash(ID key, VALUE value, void *ref) +{ + rb_hash_aset(*(VALUE *)ref, parse_key(key), rb_shape_t_to_rb_cShape((rb_shape_t*)value)); + return ID_TABLE_CONTINUE; +} + +static VALUE +rb_shape_edges(VALUE self) +{ + rb_shape_t* shape; + TypedData_Get_Struct(self, rb_shape_t, &shape_data_type, shape); + + VALUE hash = rb_hash_new(); + + if (shape->edges) { + rb_id_table_foreach(shape->edges, rb_edges_to_hash, &hash); + } + + return hash; +} + +static VALUE +rb_shape_edge_name(VALUE self) +{ + rb_shape_t* shape; + TypedData_Get_Struct(self, rb_shape_t, &shape_data_type, shape); + + if (shape->edge_name) { + return ID2SYM(shape->edge_name); + } + else { + return Qnil; + } +} + +static VALUE +rb_shape_iv_count(VALUE self) +{ + rb_shape_t* shape; + TypedData_Get_Struct(self, rb_shape_t, &shape_data_type, shape); + + return INT2NUM(shape->iv_count); +} + +static VALUE +rb_shape_export_depth(VALUE self) +{ + rb_shape_t* shape; + TypedData_Get_Struct(self, rb_shape_t, &shape_data_type, shape); + + unsigned int depth = 0; + while (shape->parent) { + depth++; + shape = shape->parent; + } + return INT2NUM(depth); +} + +static VALUE +rb_shape_parent(VALUE self) +{ + rb_shape_t * shape; + TypedData_Get_Struct(self, rb_shape_t, &shape_data_type, shape); + if (shape->parent) { + return rb_shape_t_to_rb_cShape(shape->parent); + } + else { + return Qnil; + } +} + +VALUE rb_shape_debug_shape(VALUE self, VALUE obj) { + return rb_shape_t_to_rb_cShape(rb_shape_get_shape(obj)); +} + +VALUE rb_shape_debug_root_shape(VALUE self) { + return rb_shape_t_to_rb_cShape(rb_shape_get_root_shape()); +} + +VALUE rb_shape_debug_frozen_root_shape(VALUE self) { + return rb_shape_t_to_rb_cShape(rb_shape_get_frozen_root_shape()); +} + +VALUE rb_obj_shape(rb_shape_t* shape); + +static enum rb_id_table_iterator_result collect_keys_and_values(ID key, VALUE value, void *ref) +{ + rb_hash_aset(*(VALUE *)ref, parse_key(key), rb_obj_shape((rb_shape_t*)value)); + return ID_TABLE_CONTINUE; +} + +static VALUE edges(struct rb_id_table* edges) +{ + VALUE hash = rb_hash_new(); + if (edges) + rb_id_table_foreach(edges, collect_keys_and_values, &hash); + return hash; +} + +VALUE rb_obj_shape(rb_shape_t* shape) { + VALUE rb_shape = rb_hash_new(); + + rb_hash_aset(rb_shape, ID2SYM(rb_intern("id")), INT2NUM(rb_shape_id(shape))); + rb_hash_aset(rb_shape, ID2SYM(rb_intern("edges")), edges(shape->edges)); + + if (shape == rb_shape_get_root_shape()) { + rb_hash_aset(rb_shape, ID2SYM(rb_intern("parent_id")), INT2NUM(ROOT_SHAPE_ID)); + } + else { + rb_hash_aset(rb_shape, ID2SYM(rb_intern("parent_id")), INT2NUM(rb_shape_id(shape->parent))); + } + + rb_hash_aset(rb_shape, ID2SYM(rb_intern("edge_name")), rb_id2str(shape->edge_name)); + return rb_shape; +} + +static VALUE shape_transition_tree(VALUE self) { + return rb_obj_shape(rb_shape_get_root_shape()); +} + +static VALUE shape_count(VALUE self) { + int shape_count = 0; + rb_vm_t *vm = GET_VM(); + for(shape_id_t i = 0; i < vm->next_shape_id; i++) { + if(rb_shape_get_shape_by_id_without_assertion(i)) { + shape_count++; + } + } + return INT2NUM(shape_count); +} + +static VALUE +shape_max_shape_count(VALUE self) +{ + return INT2NUM(GET_VM()->next_shape_id); +} + +VALUE +rb_shape_flags_mask(void) +{ + return SHAPE_FLAG_MASK; +} + +void +Init_shape(void) +{ + rb_cShape = rb_define_class_under(rb_cRubyVM, "Shape", rb_cObject); + rb_undef_alloc_func(rb_cShape); + + rb_define_method(rb_cShape, "parent_id", rb_shape_parent_id, 0); + rb_define_method(rb_cShape, "parent", rb_shape_parent, 0); + rb_define_method(rb_cShape, "edges", rb_shape_edges, 0); + rb_define_method(rb_cShape, "edge_name", rb_shape_edge_name, 0); + rb_define_method(rb_cShape, "iv_count", rb_shape_iv_count, 0); + rb_define_method(rb_cShape, "depth", rb_shape_export_depth, 0); + rb_define_method(rb_cShape, "id", rb_wrapped_shape_id, 0); + rb_define_method(rb_cShape, "type", rb_shape_type, 0); + rb_define_const(rb_cShape, "SHAPE_ROOT", INT2NUM(SHAPE_ROOT)); + rb_define_const(rb_cShape, "SHAPE_IVAR", INT2NUM(SHAPE_IVAR)); + rb_define_const(rb_cShape, "SHAPE_IVAR_UNDEF", INT2NUM(SHAPE_IVAR_UNDEF)); + rb_define_const(rb_cShape, "SHAPE_FROZEN", INT2NUM(SHAPE_FROZEN)); + rb_define_const(rb_cShape, "SHAPE_BITS", INT2NUM(SHAPE_BITS)); + + rb_define_module_function(rb_cRubyVM, "debug_shape_transition_tree", shape_transition_tree, 0); + rb_define_module_function(rb_cRubyVM, "debug_shape_count", shape_count, 0); + rb_define_singleton_method(rb_cRubyVM, "debug_shape", rb_shape_debug_shape, 1); + rb_define_singleton_method(rb_cRubyVM, "debug_max_shape_count", shape_max_shape_count, 0); + rb_define_singleton_method(rb_cRubyVM, "debug_root_shape", rb_shape_debug_root_shape, 0); + rb_define_singleton_method(rb_cRubyVM, "debug_frozen_root_shape", rb_shape_debug_frozen_root_shape, 0); +} diff --git a/shape.h b/shape.h new file mode 100644 index 0000000000..66b8e580c9 --- /dev/null +++ b/shape.h @@ -0,0 +1,150 @@ +#ifndef RUBY_SHAPE_H +#define RUBY_SHAPE_H +#if (SIZEOF_UINT64_T == SIZEOF_VALUE) +#define SIZEOF_SHAPE_T 4 +#define SHAPE_IN_BASIC_FLAGS 1 +typedef uint32_t attr_index_t; +#else +#define SIZEOF_SHAPE_T 2 +#define SHAPE_IN_BASIC_FLAGS 0 +typedef uint16_t attr_index_t; +#endif + +#define MAX_IVARS (attr_index_t)(-1) + +#if RUBY_DEBUG || (defined(VM_CHECK_MODE) && VM_CHECK_MODE > 0) +# if SIZEOF_SHAPE_T == 4 +typedef uint32_t shape_id_t; +# define SHAPE_BITS 16 +# else +typedef uint16_t shape_id_t; +# define SHAPE_BITS 16 +# endif +#else +# if SIZEOF_SHAPE_T == 4 +typedef uint32_t shape_id_t; +# define SHAPE_BITS 32 +# else +typedef uint16_t shape_id_t; +# define SHAPE_BITS 16 +# endif +#endif + +# define SHAPE_MASK (((uintptr_t)1 << SHAPE_BITS) - 1) +# define SHAPE_FLAG_MASK (((VALUE)-1) >> SHAPE_BITS) + +# define SHAPE_FLAG_SHIFT ((SIZEOF_VALUE * 8) - SHAPE_BITS) + +# define SHAPE_BITMAP_SIZE 16384 + +# define MAX_SHAPE_ID (SHAPE_MASK - 1) +# define INVALID_SHAPE_ID SHAPE_MASK +# define ROOT_SHAPE_ID 0x0 +# define FROZEN_ROOT_SHAPE_ID 0x1 + +struct rb_shape { + struct rb_shape * parent; // Pointer to the parent + struct rb_id_table * edges; // id_table from ID (ivar) to next shape + ID edge_name; // ID (ivar) for transition from parent to rb_shape + attr_index_t iv_count; + uint8_t type; +}; + +typedef struct rb_shape rb_shape_t; + +enum shape_type { + SHAPE_ROOT, + SHAPE_IVAR, + SHAPE_FROZEN, + SHAPE_IVAR_UNDEF, +}; + +static inline shape_id_t +IMEMO_CACHED_SHAPE_ID(VALUE cc) +{ + RBIMPL_ASSERT_TYPE((VALUE)cc, RUBY_T_IMEMO); + return (shape_id_t)(SHAPE_MASK & (RBASIC(cc)->flags >> SHAPE_FLAG_SHIFT)); +} + +static inline void +IMEMO_SET_CACHED_SHAPE_ID(VALUE cc, shape_id_t shape_id) +{ + RBIMPL_ASSERT_TYPE((VALUE)cc, RUBY_T_IMEMO); + RBASIC(cc)->flags &= SHAPE_FLAG_MASK; + RBASIC(cc)->flags |= ((VALUE)(shape_id) << SHAPE_FLAG_SHIFT); +} + +#if SHAPE_IN_BASIC_FLAGS +static inline shape_id_t +RBASIC_SHAPE_ID(VALUE obj) +{ + RUBY_ASSERT(!RB_SPECIAL_CONST_P(obj)); + return (shape_id_t)(SHAPE_MASK & ((RBASIC(obj)->flags) >> SHAPE_FLAG_SHIFT)); +} + +static inline void +RBASIC_SET_SHAPE_ID(VALUE obj, shape_id_t shape_id) +{ + // Ractors are occupying the upper 32 bits of flags, but only in debug mode + // Object shapes are occupying top bits + RBASIC(obj)->flags &= SHAPE_FLAG_MASK; + RBASIC(obj)->flags |= ((VALUE)(shape_id) << SHAPE_FLAG_SHIFT); +} + +static inline shape_id_t +ROBJECT_SHAPE_ID(VALUE obj) +{ + RBIMPL_ASSERT_TYPE(obj, RUBY_T_OBJECT); + return RBASIC_SHAPE_ID(obj); +} + +static inline void +ROBJECT_SET_SHAPE_ID(VALUE obj, shape_id_t shape_id) +{ + RBIMPL_ASSERT_TYPE(obj, RUBY_T_OBJECT); + RBASIC_SET_SHAPE_ID(obj, shape_id); +} + +#else + +static inline shape_id_t +ROBJECT_SHAPE_ID(VALUE obj) +{ + RBIMPL_ASSERT_TYPE(obj, RUBY_T_OBJECT); + return (shape_id_t)(SHAPE_MASK & (RBASIC(obj)->flags >> SHAPE_FLAG_SHIFT)); +} + +static inline void +ROBJECT_SET_SHAPE_ID(VALUE obj, shape_id_t shape_id) +{ + RBASIC(obj)->flags &= SHAPE_FLAG_MASK; + RBASIC(obj)->flags |= ((VALUE)(shape_id) << SHAPE_FLAG_SHIFT); +} +#endif + +bool rb_shape_root_shape_p(rb_shape_t* shape); + +rb_shape_t* rb_shape_get_shape_by_id_without_assertion(shape_id_t shape_id); + +MJIT_SYMBOL_EXPORT_BEGIN +rb_shape_t* rb_shape_get_shape_by_id(shape_id_t shape_id); +void rb_shape_set_shape(VALUE obj, rb_shape_t* shape); +shape_id_t rb_shape_get_shape_id(VALUE obj); +rb_shape_t* rb_shape_get_shape(VALUE obj); +int rb_shape_frozen_shape_p(rb_shape_t* shape); +void rb_shape_transition_shape_frozen(VALUE obj); +void rb_shape_transition_shape_remove_ivar(VALUE obj, ID id, rb_shape_t *shape); +void rb_shape_transition_shape(VALUE obj, ID id, rb_shape_t *shape); +rb_shape_t* rb_shape_get_next(rb_shape_t* shape, VALUE obj, ID id); +bool rb_shape_get_iv_index(rb_shape_t * shape, ID id, attr_index_t * value); +shape_id_t rb_shape_id(rb_shape_t * shape); +MJIT_SYMBOL_EXPORT_END + +rb_shape_t * rb_shape_alloc(ID edge_name, rb_shape_t * parent); + +bool rb_shape_set_shape_id(VALUE obj, shape_id_t shape_id); + +VALUE rb_obj_debug_shape(VALUE self, VALUE obj); +VALUE rb_shape_flags_mask(void); + +#endif diff --git a/test/-ext-/marshal/test_internal_ivar.rb b/test/-ext-/marshal/test_internal_ivar.rb index a32138f6e8..9359c7f113 100644 --- a/test/-ext-/marshal/test_internal_ivar.rb +++ b/test/-ext-/marshal/test_internal_ivar.rb @@ -7,6 +7,7 @@ module Bug end module Bug::Marshal class TestInternalIVar < Test::Unit::TestCase def test_marshal + pend "We don't support IVs with ID of 0" v = InternalIVar.new("hello", "world", "bye") assert_equal("hello", v.normal) assert_equal("world", v.internal) diff --git a/test/ruby/test_mjit.rb b/test/ruby/test_mjit.rb index e49195f763..4c6cc6f39f 100644 --- a/test/ruby/test_mjit.rb +++ b/test/ruby/test_mjit.rb @@ -831,7 +831,7 @@ class TestMJIT < Test::Unit::TestCase end def test_inlined_exivar - assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: "aaa", success_count: 3, recompile_count: 1, min_calls: 2) + assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: "aaa", success_count: 4, recompile_count: 2, min_calls: 2) begin; class Foo < Hash def initialize @@ -850,7 +850,7 @@ class TestMJIT < Test::Unit::TestCase end def test_inlined_undefined_ivar - assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: "bbb", success_count: 3, min_calls: 3) + assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: "bbb", success_count: 2, min_calls: 2) begin; class Foo def initialize diff --git a/test/ruby/test_object.rb b/test/ruby/test_object.rb index 83208bbcdb..a9d5d4b13e 100644 --- a/test/ruby/test_object.rb +++ b/test/ruby/test_object.rb @@ -993,4 +993,13 @@ class TestObject < Test::Unit::TestCase end EOS end + + def test_frozen_inspect + obj = Object.new + obj.instance_variable_set(:@a, "a") + ins = obj.inspect + obj.freeze + + assert_equal(ins, obj.inspect) + end end diff --git a/test/ruby/test_shapes.rb b/test/ruby/test_shapes.rb new file mode 100644 index 0000000000..807d485354 --- /dev/null +++ b/test/ruby/test_shapes.rb @@ -0,0 +1,173 @@ +# frozen_string_literal: false +require 'test/unit' + +# These test the functionality of object shapes +class TestShapes < Test::Unit::TestCase + class Example + def initialize + @a = 1 + end + end + + class RemoveAndAdd + def add_foo + @foo = 1 + end + + def remove + remove_instance_variable(:@foo) + end + + def add_bar + @bar = 1 + end + end + + # RubyVM.debug_shape returns new instances of shape objects for + # each call. This helper method allows us to define equality for + # shapes + def assert_shape_equal(shape1, shape2) + assert_equal(shape1.id, shape2.id) + assert_equal(shape1.parent_id, shape2.parent_id) + assert_equal(shape1.depth, shape2.depth) + assert_equal(shape1.type, shape2.type) + end + + def refute_shape_equal(shape1, shape2) + refute_equal(shape1.id, shape2.id) + end + + def test_iv_index + example = RemoveAndAdd.new + shape = RubyVM.debug_shape(example) + assert_equal 0, shape.iv_count + + example.add_foo # makes a transition + new_shape = RubyVM.debug_shape(example) + assert_equal([:@foo], example.instance_variables) + assert_equal(shape.id, new_shape.parent.id) + assert_equal(1, new_shape.iv_count) + + example.remove # makes a transition + remove_shape = RubyVM.debug_shape(example) + assert_equal([], example.instance_variables) + assert_equal(new_shape.id, remove_shape.parent.id) + assert_equal(1, remove_shape.iv_count) + + example.add_bar # makes a transition + bar_shape = RubyVM.debug_shape(example) + assert_equal([:@bar], example.instance_variables) + assert_equal(remove_shape.id, bar_shape.parent.id) + assert_equal(2, bar_shape.iv_count) + end + + def test_new_obj_has_root_shape + assert_shape_equal(RubyVM.debug_root_shape, RubyVM.debug_shape(Object.new)) + end + + def test_frozen_new_obj_has_frozen_root_shape + assert_shape_equal( + RubyVM.debug_frozen_root_shape, + RubyVM.debug_shape(Object.new.freeze) + ) + end + + def test_str_has_root_shape + assert_shape_equal(RubyVM.debug_root_shape, RubyVM.debug_shape("")) + end + + def test_array_has_root_shape + assert_shape_equal(RubyVM.debug_root_shape, RubyVM.debug_shape([])) + end + + def test_hash_has_root_shape + assert_shape_equal(RubyVM.debug_root_shape, RubyVM.debug_shape({})) + end + + def test_true_has_frozen_root_shape + assert_shape_equal(RubyVM.debug_frozen_root_shape, RubyVM.debug_shape(true)) + end + + def test_nil_has_frozen_root_shape + assert_shape_equal(RubyVM.debug_frozen_root_shape, RubyVM.debug_shape(nil)) + end + + def test_basic_shape_transition + obj = Example.new + refute_equal(RubyVM.debug_root_shape, RubyVM.debug_shape(obj)) + assert_shape_equal(RubyVM.debug_root_shape.edges[:@a], RubyVM.debug_shape(obj)) + assert_equal(obj.instance_variable_get(:@a), 1) + end + + def test_different_objects_make_same_transition + obj = Example.new + obj2 = "" + obj2.instance_variable_set(:@a, 1) + assert_shape_equal(RubyVM.debug_shape(obj), RubyVM.debug_shape(obj2)) + end + + def test_duplicating_objects + obj = Example.new + obj2 = obj.dup + assert_shape_equal(RubyVM.debug_shape(obj), RubyVM.debug_shape(obj2)) + end + + def test_freezing_and_duplicating_object + obj = Object.new.freeze + obj2 = obj.dup + refute_predicate(obj2, :frozen?) + # dup'd objects shouldn't be frozen, and the shape should be the + # parent shape of the copied object + assert_equal(RubyVM.debug_shape(obj).parent.id, RubyVM.debug_shape(obj2).id) + end + + def test_freezing_and_duplicating_object_with_ivars + obj = Example.new.freeze + obj2 = obj.dup + refute_predicate(obj2, :frozen?) + refute_shape_equal(RubyVM.debug_shape(obj), RubyVM.debug_shape(obj2)) + assert_equal(obj2.instance_variable_get(:@a), 1) + end + + def test_freezing_and_duplicating_string_with_ivars + str = "str" + str.instance_variable_set(:@a, 1) + str.freeze + str2 = str.dup + refute_predicate(str2, :frozen?) + refute_equal(RubyVM.debug_shape(str).id, RubyVM.debug_shape(str2).id) + assert_equal(str2.instance_variable_get(:@a), 1) + end + + def test_freezing_and_cloning_objects + obj = Object.new.freeze + obj2 = obj.clone(freeze: true) + assert_predicate(obj2, :frozen?) + assert_shape_equal(RubyVM.debug_shape(obj), RubyVM.debug_shape(obj2)) + end + + def test_freezing_and_cloning_object_with_ivars + obj = Example.new.freeze + obj2 = obj.clone(freeze: true) + assert_predicate(obj2, :frozen?) + assert_shape_equal(RubyVM.debug_shape(obj), RubyVM.debug_shape(obj2)) + assert_equal(obj2.instance_variable_get(:@a), 1) + end + + def test_freezing_and_cloning_string + str = "str".freeze + str2 = str.clone(freeze: true) + assert_predicate(str2, :frozen?) + assert_shape_equal(RubyVM.debug_shape(str), RubyVM.debug_shape(str2)) + end + + def test_freezing_and_cloning_string_with_ivars + str = "str" + str.instance_variable_set(:@a, 1) + str.freeze + str2 = str.clone(freeze: true) + assert_predicate(str2, :frozen?) + assert_shape_equal(RubyVM.debug_shape(str), RubyVM.debug_shape(str2)) + assert_equal(str2.instance_variable_get(:@a), 1) + end +end diff --git a/tool/mjit/bindgen.rb b/tool/mjit/bindgen.rb index d0f9bf527b..8c21d42449 100755 --- a/tool/mjit/bindgen.rb +++ b/tool/mjit/bindgen.rb @@ -341,12 +341,17 @@ generator = BindingGenerator.new( VM_METHOD_TYPE_CFUNC VM_METHOD_TYPE_ISEQ ], + ULONG: %w[ + INVALID_SHAPE_ID + SHAPE_MASK + ], }, types: %w[ CALL_DATA IC IVC RB_BUILTIN + attr_index_t compile_branch compile_status inlined_call_context @@ -360,10 +365,10 @@ generator = BindingGenerator.new( rb_callable_method_entry_struct rb_callcache rb_callinfo - rb_cref_t rb_control_frame_t - rb_execution_context_t + rb_cref_t rb_execution_context_struct + rb_execution_context_t rb_iseq_constant_body rb_iseq_location_t rb_iseq_struct @@ -378,6 +383,7 @@ generator = BindingGenerator.new( ], dynamic_types: %w[ VALUE + shape_id_t ], skip_fields: { 'rb_execution_context_struct.machine': %w[regs], # differs between macOS and Linux diff --git a/variable.c b/variable.c index 056a1000b8..b5e95b7f1c 100644 --- a/variable.c +++ b/variable.c @@ -34,6 +34,7 @@ #include "ruby/st.h" #include "ruby/util.h" #include "transient_heap.h" +#include "shape.h" #include "variable.h" #include "vm_core.h" #include "ractor_core.h" @@ -63,12 +64,9 @@ static VALUE rb_const_search(VALUE klass, ID id, int exclude, int recurse, int v static st_table *generic_iv_tbl_; struct ivar_update { - union { - st_table *iv_index_tbl; - struct gen_ivtbl *ivtbl; - } u; - st_data_t index; - int iv_extended; + struct gen_ivtbl *ivtbl; + uint32_t iv_index; + rb_shape_t* shape; }; void @@ -896,30 +894,6 @@ rb_alias_variable(ID name1, ID name2) entry1->var = entry2->var; } -static bool -iv_index_tbl_lookup(struct st_table *tbl, ID id, uint32_t *indexp) -{ - st_data_t ent_data; - int r; - - if (tbl == NULL) return false; - - RB_VM_LOCK_ENTER(); - { - r = st_lookup(tbl, (st_data_t)id, &ent_data); - } - RB_VM_LOCK_LEAVE(); - - if (r) { - struct rb_iv_index_tbl_entry *ent = (void *)ent_data; - *indexp = ent->index; - return true; - } - else { - return false; - } -} - static void IVAR_ACCESSOR_SHOULD_BE_MAIN_RACTOR(ID id) { @@ -957,7 +931,20 @@ generic_ivtbl_no_ractor_check(VALUE obj) } static int -gen_ivtbl_get(VALUE obj, ID id, struct gen_ivtbl **ivtbl) +gen_ivtbl_get_unlocked(VALUE obj, ID id, struct gen_ivtbl **ivtbl) +{ + st_data_t data; + + if (st_lookup(generic_ivtbl(obj, id, false), (st_data_t)obj, &data)) { + *ivtbl = (struct gen_ivtbl *)data; + return 1; + } + + return 0; +} + +MJIT_FUNC_EXPORTED int +rb_gen_ivtbl_get(VALUE obj, ID id, struct gen_ivtbl **ivtbl) { st_data_t data; int r = 0; @@ -977,63 +964,7 @@ gen_ivtbl_get(VALUE obj, ID id, struct gen_ivtbl **ivtbl) MJIT_FUNC_EXPORTED int rb_ivar_generic_ivtbl_lookup(VALUE obj, struct gen_ivtbl **ivtbl) { - return gen_ivtbl_get(obj, 0, ivtbl); -} - -MJIT_FUNC_EXPORTED VALUE -rb_ivar_generic_lookup_with_index(VALUE obj, ID id, uint32_t index) -{ - struct gen_ivtbl *ivtbl; - - if (gen_ivtbl_get(obj, id, &ivtbl)) { - if (LIKELY(index < ivtbl->numiv)) { - VALUE val = ivtbl->ivptr[index]; - return val; - } - } - - return Qundef; -} - -static VALUE -generic_ivar_delete(VALUE obj, ID id, VALUE undef) -{ - struct gen_ivtbl *ivtbl; - - if (gen_ivtbl_get(obj, id, &ivtbl)) { - st_table *iv_index_tbl = RCLASS_IV_INDEX_TBL(rb_obj_class(obj)); - uint32_t index; - - if (iv_index_tbl && iv_index_tbl_lookup(iv_index_tbl, id, &index)) { - if (index < ivtbl->numiv) { - VALUE ret = ivtbl->ivptr[index]; - - ivtbl->ivptr[index] = Qundef; - return ret == Qundef ? undef : ret; - } - } - } - return undef; -} - -static VALUE -generic_ivar_get(VALUE obj, ID id, VALUE undef) -{ - struct gen_ivtbl *ivtbl; - - if (gen_ivtbl_get(obj, id, &ivtbl)) { - st_table *iv_index_tbl = RCLASS_IV_INDEX_TBL(rb_obj_class(obj)); - uint32_t index; - - if (iv_index_tbl && iv_index_tbl_lookup(iv_index_tbl, id, &index)) { - if (index < ivtbl->numiv) { - VALUE ret = ivtbl->ivptr[index]; - - return ret == Qundef ? undef : ret; - } - } - } - return undef; + return rb_gen_ivtbl_get(obj, 0, ivtbl); } static size_t @@ -1045,6 +976,8 @@ gen_ivtbl_bytes(size_t n) static struct gen_ivtbl * gen_ivtbl_resize(struct gen_ivtbl *old, uint32_t n) { + RUBY_ASSERT(n > 0); + uint32_t len = old ? old->numiv : 0; struct gen_ivtbl *ivtbl = xrealloc(old, gen_ivtbl_bytes(n)); @@ -1069,18 +1002,6 @@ gen_ivtbl_dup(const struct gen_ivtbl *orig) } #endif -static uint32_t -iv_index_tbl_newsize(struct ivar_update *ivup) -{ - if (!ivup->iv_extended) { - return (uint32_t)ivup->u.iv_index_tbl->num_entries; - } - else { - uint32_t index = (uint32_t)ivup->index; /* should not overflow */ - return (index+1) + (index+1)/4; /* (index+1)*1.25 */ - } -} - static int generic_ivar_update(st_data_t *k, st_data_t *v, st_data_t u, int existing) { @@ -1091,53 +1012,22 @@ generic_ivar_update(st_data_t *k, st_data_t *v, st_data_t u, int existing) if (existing) { ivtbl = (struct gen_ivtbl *)*v; - if (ivup->index < ivtbl->numiv) { - ivup->u.ivtbl = ivtbl; + if (ivup->iv_index < ivtbl->numiv) { + ivup->ivtbl = ivtbl; return ST_STOP; } } FL_SET((VALUE)*k, FL_EXIVAR); - uint32_t newsize = iv_index_tbl_newsize(ivup); - ivtbl = gen_ivtbl_resize(ivtbl, newsize); + ivtbl = gen_ivtbl_resize(ivtbl, ivup->shape->iv_count); + // Reinsert in to the hash table because ivtbl might be a newly resized chunk of memory *v = (st_data_t)ivtbl; - ivup->u.ivtbl = ivtbl; + ivup->ivtbl = ivtbl; +#if !SHAPE_IN_BASIC_FLAGS + ivtbl->shape_id = rb_shape_id(ivup->shape); +#endif return ST_CONTINUE; } -static VALUE -generic_ivar_defined(VALUE obj, ID id) -{ - struct gen_ivtbl *ivtbl; - st_table *iv_index_tbl = RCLASS_IV_INDEX_TBL(rb_obj_class(obj)); - uint32_t index; - - if (!iv_index_tbl_lookup(iv_index_tbl, id, &index)) return Qfalse; - if (!gen_ivtbl_get(obj, id, &ivtbl)) return Qfalse; - - return RBOOL((index < ivtbl->numiv) && (ivtbl->ivptr[index] != Qundef)); -} - -static int -generic_ivar_remove(VALUE obj, ID id, VALUE *valp) -{ - struct gen_ivtbl *ivtbl; - uint32_t index; - st_table *iv_index_tbl = RCLASS_IV_INDEX_TBL(rb_obj_class(obj)); - - if (!iv_index_tbl) return 0; - if (!iv_index_tbl_lookup(iv_index_tbl, id, &index)) return 0; - if (!gen_ivtbl_get(obj, id, &ivtbl)) return 0; - - if (index < ivtbl->numiv) { - if (ivtbl->ivptr[index] != Qundef) { - *valp = ivtbl->ivptr[index]; - ivtbl->ivptr[index] = Qundef; - return 1; - } - } - return 0; -} - static void gen_ivtbl_mark(const struct gen_ivtbl *ivtbl) { @@ -1153,7 +1043,7 @@ rb_mark_generic_ivar(VALUE obj) { struct gen_ivtbl *ivtbl; - if (gen_ivtbl_get(obj, 0, &ivtbl)) { + if (rb_gen_ivtbl_get(obj, 0, &ivtbl)) { gen_ivtbl_mark(ivtbl); } } @@ -1182,11 +1072,35 @@ rb_generic_ivar_memsize(VALUE obj) { struct gen_ivtbl *ivtbl; - if (gen_ivtbl_get(obj, 0, &ivtbl)) + if (rb_gen_ivtbl_get(obj, 0, &ivtbl)) return gen_ivtbl_bytes(ivtbl->numiv); return 0; } +#if !SHAPE_IN_BASIC_FLAGS +MJIT_FUNC_EXPORTED shape_id_t +rb_generic_shape_id(VALUE obj) +{ + struct gen_ivtbl *ivtbl = 0; + shape_id_t shape_id = 0; + + RB_VM_LOCK_ENTER(); + { + st_table* global_iv_table = generic_ivtbl(obj, 0, false); + + if (global_iv_table && st_lookup(global_iv_table, obj, (st_data_t *)&ivtbl)) { + shape_id = ivtbl->shape_id; + } + else if (OBJ_FROZEN(obj)) { + shape_id = FROZEN_ROOT_SHAPE_ID; + } + } + RB_VM_LOCK_LEAVE(); + + return shape_id; +} +#endif + static size_t gen_ivtbl_count(const struct gen_ivtbl *ivtbl) { @@ -1254,23 +1168,16 @@ VALUE rb_ivar_lookup(VALUE obj, ID id, VALUE undef) { if (SPECIAL_CONST_P(obj)) return undef; - switch (BUILTIN_TYPE(obj)) { - case T_OBJECT: - { - uint32_t index; - uint32_t len = ROBJECT_NUMIV(obj); - VALUE *ptr = ROBJECT_IVPTR(obj); - VALUE val; - if (iv_index_tbl_lookup(ROBJECT_IV_INDEX_TBL(obj), id, &index) && - index < len && - (val = ptr[index]) != Qundef) { - return val; - } - else { - break; - } - } + shape_id_t shape_id; + VALUE * ivar_list; + rb_shape_t * shape; + +#if SHAPE_IN_BASIC_FLAGS + shape_id = RBASIC_SHAPE_ID(obj); +#endif + + switch (BUILTIN_TYPE(obj)) { case T_CLASS: case T_MODULE: { @@ -1287,14 +1194,37 @@ rb_ivar_lookup(VALUE obj, ID id, VALUE undef) return val; } else { - break; + return undef; } } + case T_OBJECT: + { +#if !SHAPE_IN_BASIC_FLAGS + shape_id = ROBJECT_SHAPE_ID(obj); +#endif + ivar_list = ROBJECT_IVPTR(obj); + break; + } default: - if (FL_TEST(obj, FL_EXIVAR)) - return generic_ivar_get(obj, id, undef); + if (FL_TEST_RAW(obj, FL_EXIVAR)) { + struct gen_ivtbl *ivtbl; + rb_gen_ivtbl_get(obj, id, &ivtbl); +#if !SHAPE_IN_BASIC_FLAGS + shape_id = ivtbl->shape_id; +#endif + ivar_list = ivtbl->ivptr; + } else { + return undef; + } break; } + + attr_index_t index = 0; + shape = rb_shape_get_shape_by_id(shape_id); + if (rb_shape_get_iv_index(shape, id, &index)) { + return ivar_list[index]; + } + return undef; } @@ -1315,26 +1245,12 @@ rb_attr_get(VALUE obj, ID id) static VALUE rb_ivar_delete(VALUE obj, ID id, VALUE undef) { - VALUE *ptr; - struct st_table *iv_index_tbl; - uint32_t len, index; - rb_check_frozen(obj); - switch (BUILTIN_TYPE(obj)) { - case T_OBJECT: - len = ROBJECT_NUMIV(obj); - ptr = ROBJECT_IVPTR(obj); - iv_index_tbl = ROBJECT_IV_INDEX_TBL(obj); - if (iv_index_tbl_lookup(iv_index_tbl, id, &index) && - index < len) { - VALUE val = ptr[index]; - ptr[index] = Qundef; - if (val != Qundef) { - return val; - } - } - break; + VALUE val = Qnil; + attr_index_t index; + + switch (BUILTIN_TYPE(obj)) { case T_CLASS: case T_MODULE: IVAR_ACCESSOR_SHOULD_BE_MAIN_RACTOR(id); @@ -1345,11 +1261,33 @@ rb_ivar_delete(VALUE obj, ID id, VALUE undef) } } break; - default: - if (FL_TEST(obj, FL_EXIVAR)) - return generic_ivar_delete(obj, id, undef); + case T_OBJECT: { + rb_shape_t * shape = rb_shape_get_shape(obj); + if (rb_shape_get_iv_index(shape, id, &index)) { + rb_shape_transition_shape_remove_ivar(obj, id, shape); + val = ROBJECT_IVPTR(obj)[index]; + ROBJECT_IVPTR(obj)[index] = Qundef; + return val; + } + break; + } + default: { + rb_shape_t * shape = rb_shape_get_shape(obj); + + if (rb_shape_get_iv_index(shape, id, &index)) { + rb_shape_transition_shape_remove_ivar(obj, id, shape); + struct gen_ivtbl *ivtbl; + rb_gen_ivtbl_get(obj, id, &ivtbl); + val = ivtbl->ivptr[index]; + ivtbl->ivptr[index] = Qundef; + return val; + } + + break; + } } + return undef; } @@ -1359,67 +1297,31 @@ rb_attr_delete(VALUE obj, ID id) return rb_ivar_delete(obj, id, Qnil); } -static st_table * -iv_index_tbl_make(VALUE obj, VALUE klass) -{ - st_table *iv_index_tbl; - - if (UNLIKELY(!klass)) { - rb_raise(rb_eTypeError, "hidden object cannot have instance variables"); - } - - if ((iv_index_tbl = RCLASS_IV_INDEX_TBL(klass)) == NULL) { - RB_VM_LOCK_ENTER(); - if ((iv_index_tbl = RCLASS_IV_INDEX_TBL(klass)) == NULL) { - iv_index_tbl = RCLASS_IV_INDEX_TBL(klass) = st_init_numtable(); - } - RB_VM_LOCK_LEAVE(); - } - - return iv_index_tbl; -} - -static void -iv_index_tbl_extend(struct ivar_update *ivup, ID id, VALUE klass) -{ - ASSERT_vm_locking(); - st_data_t ent_data; - struct rb_iv_index_tbl_entry *ent; - - if (st_lookup(ivup->u.iv_index_tbl, (st_data_t)id, &ent_data)) { - ent = (void *)ent_data; - ivup->index = ent->index; - return; - } - if (ivup->u.iv_index_tbl->num_entries >= INT_MAX) { - rb_raise(rb_eArgError, "too many instance variables"); - } - ent = ALLOC(struct rb_iv_index_tbl_entry); - ent->index = ivup->index = (uint32_t)ivup->u.iv_index_tbl->num_entries; - ent->class_value = klass; - ent->class_serial = RCLASS_SERIAL(klass); - st_add_direct(ivup->u.iv_index_tbl, (st_data_t)id, (st_data_t)ent); - ivup->iv_extended = 1; -} - static void generic_ivar_set(VALUE obj, ID id, VALUE val) { - VALUE klass = rb_obj_class(obj); struct ivar_update ivup; - ivup.iv_extended = 0; - ivup.u.iv_index_tbl = iv_index_tbl_make(obj, klass); + // The returned shape will have `id` in its iv_table + rb_shape_t * shape = rb_shape_get_next(rb_shape_get_shape(obj), obj, id); + ivup.shape = shape; RB_VM_LOCK_ENTER(); { - iv_index_tbl_extend(&ivup, id, klass); - st_update(generic_ivtbl(obj, id, false), (st_data_t)obj, generic_ivar_update, - (st_data_t)&ivup); + attr_index_t ent_data; + if (rb_shape_get_iv_index(shape, id, &ent_data)) { + ivup.iv_index = (uint32_t) ent_data; + } + else { + rb_bug("unreachable. Shape was not found for id: %s", rb_id2name(id)); + } + + st_update(generic_ivtbl(obj, id, false), (st_data_t)obj, generic_ivar_update, (st_data_t)&ivup); } RB_VM_LOCK_LEAVE(); - ivup.u.ivtbl->ivptr[ivup.index] = val; + ivup.ivtbl->ivptr[ivup.iv_index] = val; + rb_shape_set_shape(obj, shape); RB_OBJ_WRITTEN(obj, Qundef, val); } @@ -1486,8 +1388,8 @@ rb_obj_transient_heap_evacuate(VALUE obj, int promote) } #endif -static void -init_iv_list(VALUE obj, uint32_t len, uint32_t newsize, st_table *index_tbl) +void +rb_ensure_iv_list_size(VALUE obj, uint32_t len, uint32_t newsize) { VALUE *ptr = ROBJECT_IVPTR(obj); VALUE *newptr; @@ -1510,35 +1412,34 @@ init_iv_list(VALUE obj, uint32_t len, uint32_t newsize, st_table *index_tbl) #else ROBJECT(obj)->as.heap.numiv = newsize; #endif - ROBJECT(obj)->as.heap.iv_index_tbl = index_tbl; +} + +struct gen_ivtbl * +rb_ensure_generic_iv_list_size(VALUE obj, uint32_t newsize) +{ + struct gen_ivtbl * ivtbl = 0; + + RB_VM_LOCK_ENTER(); + { + if (UNLIKELY(!gen_ivtbl_get_unlocked(obj, 0, &ivtbl) || newsize > ivtbl->numiv)) { + ivtbl = gen_ivtbl_resize(ivtbl, newsize); + st_insert(generic_ivtbl_no_ractor_check(obj), (st_data_t)obj, (st_data_t)ivtbl); + FL_SET_RAW(obj, FL_EXIVAR); + } + } + RB_VM_LOCK_LEAVE(); + + RUBY_ASSERT(ivtbl); + + return ivtbl; } void rb_init_iv_list(VALUE obj) { - st_table *index_tbl = ROBJECT_IV_INDEX_TBL(obj); - uint32_t newsize = (uint32_t)index_tbl->num_entries; + uint32_t newsize = rb_shape_get_shape(obj)->iv_count * 2.0; uint32_t len = ROBJECT_NUMIV(obj); - init_iv_list(obj, len, newsize, index_tbl); -} - -// Retrieve or create the id-to-index mapping for a given object and an -// instance variable name. -static struct ivar_update -obj_ensure_iv_index_mapping(VALUE obj, ID id) -{ - VALUE klass = rb_obj_class(obj); - struct ivar_update ivup; - ivup.iv_extended = 0; - ivup.u.iv_index_tbl = iv_index_tbl_make(obj, klass); - - RB_VM_LOCK_ENTER(); - { - iv_index_tbl_extend(&ivup, id, klass); - } - RB_VM_LOCK_LEAVE(); - - return ivup; + rb_ensure_iv_list_size(obj, len, newsize < len ? len : newsize); } // Return the instance variable index for a given name and T_OBJECT object. The @@ -1552,28 +1453,110 @@ uint32_t rb_obj_ensure_iv_index_mapping(VALUE obj, ID id) { RUBY_ASSERT(RB_TYPE_P(obj, T_OBJECT)); - // This uint32_t cast shouldn't lose information as it's checked in - // iv_index_tbl_extend(). The index is stored as an uint32_t in - // struct rb_iv_index_tbl_entry. - return (uint32_t)obj_ensure_iv_index_mapping(obj, id).index; + attr_index_t index; + + // Ensure there is a transition for IVAR +id+ + rb_shape_transition_shape(obj, id, rb_shape_get_shape_by_id(ROBJECT_SHAPE_ID(obj))); + + // Get the current shape + rb_shape_t * shape = rb_shape_get_shape_by_id(ROBJECT_SHAPE_ID(obj)); + + if (!rb_shape_get_iv_index(shape, id, &index)) { + rb_bug("unreachable. Shape was not found for id: %s", rb_id2name(id)); + } + + uint32_t len = ROBJECT_NUMIV(obj); + if (len <= index) { + uint32_t newsize = (shape->iv_count + 1) * 1.25; + rb_ensure_iv_list_size(obj, len, newsize); + } + RUBY_ASSERT(index <= ROBJECT_NUMIV(obj)); + return index; } static VALUE obj_ivar_set(VALUE obj, ID id, VALUE val) { - uint32_t len; - struct ivar_update ivup = obj_ensure_iv_index_mapping(obj, id); - - len = ROBJECT_NUMIV(obj); - if (len <= ivup.index) { - uint32_t newsize = iv_index_tbl_newsize(&ivup); - init_iv_list(obj, len, newsize, ivup.u.iv_index_tbl); - } - RB_OBJ_WRITE(obj, &ROBJECT_IVPTR(obj)[ivup.index], val); - + attr_index_t index = rb_obj_ensure_iv_index_mapping(obj, id); + RB_OBJ_WRITE(obj, &ROBJECT_IVPTR(obj)[index], val); return val; } +/* Set the instance variable +val+ on object +obj+ at ivar name +id+. + * This function only works with T_OBJECT objects, so make sure + * +obj+ is of type T_OBJECT before using this function. + */ +VALUE +rb_vm_set_ivar_id(VALUE obj, ID id, VALUE val) +{ + rb_check_frozen_internal(obj); + obj_ivar_set(obj, id, val); + return val; +} + +bool +rb_shape_set_shape_id(VALUE obj, shape_id_t shape_id) +{ + if (rb_shape_get_shape_id(obj) == shape_id) { + return false; + } + +#if SHAPE_IN_BASIC_FLAGS + RBASIC_SET_SHAPE_ID(obj, shape_id); +#else + switch (BUILTIN_TYPE(obj)) { + case T_OBJECT: + ROBJECT_SET_SHAPE_ID(obj, shape_id); + break; + case T_CLASS: + case T_MODULE: + { + RCLASS_EXT(obj)->shape_id = shape_id; + break; + } + default: + { + if (shape_id != FROZEN_ROOT_SHAPE_ID) { + struct gen_ivtbl *ivtbl = 0; + RB_VM_LOCK_ENTER(); + { + st_table* global_iv_table = generic_ivtbl(obj, 0, false); + + if (st_lookup(global_iv_table, obj, (st_data_t *)&ivtbl)) { + ivtbl->shape_id = shape_id; + } + else { + rb_bug("Expected shape_id entry in global iv table"); + } + } + RB_VM_LOCK_LEAVE(); + } + } + } +#endif + + return true; +} + +/** + * Prevents further modifications to the given object. ::rb_eFrozenError shall + * be raised if modification is attempted. + * + * @param[out] x Object in question. + */ +void rb_obj_freeze_inline(VALUE x) +{ + if (RB_FL_ABLE(x)) { + RB_OBJ_FREEZE_RAW(x); + + rb_shape_transition_shape_frozen(x); + + if (RBASIC_CLASS(x) && !(RBASIC(x)->flags & RUBY_FL_SINGLETON)) { + rb_freeze_singleton_class(x); + } + } +} + static void ivar_set(VALUE obj, ID id, VALUE val) { @@ -1581,10 +1564,14 @@ ivar_set(VALUE obj, ID id, VALUE val) switch (BUILTIN_TYPE(obj)) { case T_OBJECT: - obj_ivar_set(obj, id, val); - break; + { + obj_ivar_set(obj, id, val); + break; + } case T_CLASS: case T_MODULE: + // TODO: Transition shapes on classes + //rb_shape_transition_shape(obj, id, rb_shape_get_shape_by_id(RCLASS_SHAPE_ID(obj))); IVAR_ACCESSOR_SHOULD_BE_MAIN_RACTOR(id); rb_class_ivar_set(obj, id, val); break; @@ -1614,161 +1601,86 @@ rb_ivar_set_internal(VALUE obj, ID id, VALUE val) VALUE rb_ivar_defined(VALUE obj, ID id) { - VALUE val; - struct st_table *iv_index_tbl; - uint32_t index; + attr_index_t index; if (SPECIAL_CONST_P(obj)) return Qfalse; switch (BUILTIN_TYPE(obj)) { - case T_OBJECT: - iv_index_tbl = ROBJECT_IV_INDEX_TBL(obj); - if (iv_index_tbl_lookup(iv_index_tbl, id, &index) && - index < ROBJECT_NUMIV(obj) && - (val = ROBJECT_IVPTR(obj)[index]) != Qundef) { - return Qtrue; - } - break; case T_CLASS: case T_MODULE: - if (RCLASS_IV_TBL(obj) && lock_st_is_member(RCLASS_IV_TBL(obj), (st_data_t)id)) + if (RCLASS_IV_TBL(obj) && lock_st_is_member(RCLASS_IV_TBL(obj), (st_data_t)id)) { return Qtrue; - break; + } + else { + return Qfalse; + } default: - if (FL_TEST(obj, FL_EXIVAR)) - return generic_ivar_defined(obj, id); - break; + return RBOOL(rb_shape_get_iv_index(rb_shape_get_shape(obj), id, &index)); } - return Qfalse; } typedef int rb_ivar_foreach_callback_func(ID key, VALUE val, st_data_t arg); st_data_t rb_st_nth_key(st_table *tab, st_index_t index); -static ID -iv_index_tbl_nth_id(st_table *iv_index_tbl, uint32_t index) -{ - st_data_t key; - RB_VM_LOCK_ENTER(); - { - key = rb_st_nth_key(iv_index_tbl, index); +static void +iterate_over_shapes_with_callback(rb_shape_t *shape, VALUE* iv_list, rb_ivar_foreach_callback_func *callback, st_data_t arg) { + switch ((enum shape_type)shape->type) { + case SHAPE_ROOT: + return; + case SHAPE_IVAR: + iterate_over_shapes_with_callback(shape->parent, iv_list, callback, arg); + VALUE val = iv_list[shape->iv_count - 1]; + if (val != Qundef) { + callback(shape->edge_name, val, arg); + } + return; + case SHAPE_IVAR_UNDEF: + case SHAPE_FROZEN: + iterate_over_shapes_with_callback(shape->parent, iv_list, callback, arg); + return; } - RB_VM_LOCK_LEAVE(); - return (ID)key; -} - -static inline bool -ivar_each_i(st_table *iv_index_tbl, VALUE val, uint32_t i, rb_ivar_foreach_callback_func *func, st_data_t arg) -{ - if (val != Qundef) { - ID id = iv_index_tbl_nth_id(iv_index_tbl, i); - switch (func(id, val, arg)) { - case ST_CHECK: - case ST_CONTINUE: - break; - case ST_STOP: - return true; - default: - rb_bug("unreachable"); - } - } - return false; } static void obj_ivar_each(VALUE obj, rb_ivar_foreach_callback_func *func, st_data_t arg) { - st_table *iv_index_tbl = ROBJECT_IV_INDEX_TBL(obj); - if (!iv_index_tbl) return; - uint32_t i=0; - - for (i=0; i < ROBJECT_NUMIV(obj); i++) { - VALUE val = ROBJECT_IVPTR(obj)[i]; - if (ivar_each_i(iv_index_tbl, val, i, func, arg)) { - return; - } - } + rb_shape_t* shape = rb_shape_get_shape(obj); + iterate_over_shapes_with_callback(shape, ROBJECT_IVPTR(obj), func, arg); } static void gen_ivar_each(VALUE obj, rb_ivar_foreach_callback_func *func, st_data_t arg) { + rb_shape_t *shape = rb_shape_get_shape(obj); struct gen_ivtbl *ivtbl; - st_table *iv_index_tbl = RCLASS_IV_INDEX_TBL(rb_obj_class(obj)); - if (!iv_index_tbl) return; - if (!gen_ivtbl_get(obj, 0, &ivtbl)) return; + if (!rb_gen_ivtbl_get(obj, 0, &ivtbl)) return; - for (uint32_t i=0; inumiv; i++) { - VALUE val = ivtbl->ivptr[i]; - if (ivar_each_i(iv_index_tbl, val, i, func, arg)) { - return; - } - } -} - -struct givar_copy { - VALUE obj; - VALUE klass; - st_table *iv_index_tbl; - struct gen_ivtbl *ivtbl; -}; - -static int -gen_ivar_copy(ID id, VALUE val, st_data_t arg) -{ - struct givar_copy *c = (struct givar_copy *)arg; - struct ivar_update ivup; - - ivup.iv_extended = 0; - ivup.u.iv_index_tbl = c->iv_index_tbl; - - RB_VM_LOCK_ENTER(); - { - iv_index_tbl_extend(&ivup, id, c->klass); - } - RB_VM_LOCK_LEAVE(); - - if (ivup.index >= c->ivtbl->numiv) { - uint32_t newsize = iv_index_tbl_newsize(&ivup); - c->ivtbl = gen_ivtbl_resize(c->ivtbl, newsize); - } - c->ivtbl->ivptr[ivup.index] = val; - - RB_OBJ_WRITTEN(c->obj, Qundef, val); - - return ST_CONTINUE; + iterate_over_shapes_with_callback(shape, ivtbl->ivptr, func, arg); } void rb_copy_generic_ivar(VALUE clone, VALUE obj) { - struct gen_ivtbl *ivtbl; + struct gen_ivtbl *obj_ivtbl; + struct gen_ivtbl *new_ivtbl; rb_check_frozen(clone); if (!FL_TEST(obj, FL_EXIVAR)) { goto clear; } - if (gen_ivtbl_get(obj, 0, &ivtbl)) { - struct givar_copy c; - uint32_t i; - if (gen_ivtbl_count(ivtbl) == 0) + if (rb_gen_ivtbl_get(obj, 0, &obj_ivtbl)) { + if (gen_ivtbl_count(obj_ivtbl) == 0) goto clear; - if (gen_ivtbl_get(clone, 0, &c.ivtbl)) { - for (i = 0; i < c.ivtbl->numiv; i++) - c.ivtbl->ivptr[i] = Qundef; - } - else { - c.ivtbl = gen_ivtbl_resize(0, ivtbl->numiv); - FL_SET(clone, FL_EXIVAR); + new_ivtbl = gen_ivtbl_resize(0, obj_ivtbl->numiv); + FL_SET(clone, FL_EXIVAR); + + for (uint32_t i=0; inumiv; i++) { + new_ivtbl->ivptr[i] = obj_ivtbl->ivptr[i]; + RB_OBJ_WRITTEN(clone, Qundef, &new_ivtbl[i]); } - VALUE klass = rb_obj_class(clone); - c.iv_index_tbl = iv_index_tbl_make(clone, klass); - c.obj = clone; - c.klass = klass; - gen_ivar_each(obj, gen_ivar_copy, (st_data_t)&c); /* * c.ivtbl may change in gen_ivar_copy due to realloc, * no need to free @@ -1776,9 +1688,17 @@ rb_copy_generic_ivar(VALUE clone, VALUE obj) RB_VM_LOCK_ENTER(); { generic_ivtbl_no_ractor_check(clone); - st_insert(generic_ivtbl_no_ractor_check(obj), (st_data_t)clone, (st_data_t)c.ivtbl); + st_insert(generic_ivtbl_no_ractor_check(obj), (st_data_t)clone, (st_data_t)new_ivtbl); } RB_VM_LOCK_LEAVE(); + + rb_shape_t * obj_shape = rb_shape_get_shape(obj); + if (rb_shape_frozen_shape_p(obj_shape)) { + rb_shape_set_shape(clone, obj_shape->parent); + } + else { + rb_shape_set_shape(clone, obj_shape); + } } return; @@ -1846,7 +1766,7 @@ rb_ivar_count(VALUE obj) switch (BUILTIN_TYPE(obj)) { case T_OBJECT: - if (ROBJECT_IV_INDEX_TBL(obj) != 0) { + if (rb_shape_get_shape(obj)->iv_count > 0) { st_index_t i, count, num = ROBJECT_NUMIV(obj); const VALUE *const ivptr = ROBJECT_IVPTR(obj); for (i = count = 0; i < num; ++i) { @@ -1867,7 +1787,7 @@ rb_ivar_count(VALUE obj) if (FL_TEST(obj, FL_EXIVAR)) { struct gen_ivtbl *ivtbl; - if (gen_ivtbl_get(obj, 0, &ivtbl)) { + if (rb_gen_ivtbl_get(obj, 0, &ivtbl)) { return gen_ivtbl_count(ivtbl); } } @@ -1965,40 +1885,53 @@ rb_obj_remove_instance_variable(VALUE obj, VALUE name) { VALUE val = Qnil; const ID id = id_for_var(obj, name, an, instance); - st_data_t n, v; - struct st_table *iv_index_tbl; - uint32_t index; + // Frozen check comes here because it's expected that we raise a + // NameError (from the id_for_var check) before we raise a FrozenError rb_check_frozen(obj); + + attr_index_t index; + if (!id) { goto not_defined; } switch (BUILTIN_TYPE(obj)) { - case T_OBJECT: - iv_index_tbl = ROBJECT_IV_INDEX_TBL(obj); - if (iv_index_tbl_lookup(iv_index_tbl, id, &index) && - index < ROBJECT_NUMIV(obj) && - (val = ROBJECT_IVPTR(obj)[index]) != Qundef) { - ROBJECT_IVPTR(obj)[index] = Qundef; - return val; - } - break; case T_CLASS: case T_MODULE: IVAR_ACCESSOR_SHOULD_BE_MAIN_RACTOR(id); - n = id; - if (RCLASS_IV_TBL(obj) && lock_st_delete(RCLASS_IV_TBL(obj), &n, &v)) { - return (VALUE)v; - } - break; - default: - if (FL_TEST(obj, FL_EXIVAR)) { - if (generic_ivar_remove(obj, id, &val)) { - return val; + if (RCLASS_IV_TBL(obj)) { + st_data_t id_data = (st_data_t)id, val; + if (lock_st_delete(RCLASS_IV_TBL(obj), &id_data, &val)) { + return (VALUE)val; } } break; + case T_OBJECT: { + rb_shape_t * shape = rb_shape_get_shape(obj); + if (rb_shape_get_iv_index(shape, id, &index)) { + rb_shape_transition_shape_remove_ivar(obj, id, shape); + val = ROBJECT_IVPTR(obj)[index]; + ROBJECT_IVPTR(obj)[index] = Qundef; + return val; + } + + break; + } + default: { + rb_shape_t * shape = rb_shape_get_shape(obj); + + if (rb_shape_get_iv_index(shape, id, &index)) { + rb_shape_transition_shape_remove_ivar(obj, id, shape); + struct gen_ivtbl *ivtbl; + rb_gen_ivtbl_get(obj, id, &ivtbl); + val = ivtbl->ivptr[index]; + ivtbl->ivptr[index] = Qundef; + return val; + } + + break; + } } not_defined: diff --git a/variable.h b/variable.h index 55596b00de..314ac82df0 100644 --- a/variable.h +++ b/variable.h @@ -11,11 +11,19 @@ /* per-object */ struct gen_ivtbl { +#if !SHAPE_IN_BASIC_FLAGS + uint16_t shape_id; +#endif uint32_t numiv; VALUE ivptr[FLEX_ARY_LEN]; }; int rb_ivar_generic_ivtbl_lookup(VALUE obj, struct gen_ivtbl **); -VALUE rb_ivar_generic_lookup_with_index(VALUE obj, ID id, uint32_t index); + +#include "shape.h" +#if !SHAPE_IN_BASIC_FLAGS +shape_id_t rb_generic_shape_id(VALUE obj); +#endif + #endif /* RUBY_TOPLEVEL_VARIABLE_H */ diff --git a/vm.c b/vm.c index 0de461392f..a52d3efa7b 100644 --- a/vm.c +++ b/vm.c @@ -26,6 +26,7 @@ #include "internal/thread.h" #include "internal/vm.h" #include "internal/sanitizers.h" +#include "internal/variable.h" #include "iseq.h" #include "mjit.h" #include "yjit.h" @@ -4021,6 +4022,11 @@ Init_BareVM(void) rb_native_cond_initialize(&vm->ractor.sync.terminate_cond); } +#ifndef _WIN32 +#include +#include +#endif + void Init_vm_objects(void) { @@ -4032,6 +4038,31 @@ Init_vm_objects(void) vm->mark_object_ary = rb_ary_hidden_new(128); vm->loading_table = st_init_strtable(); vm->frozen_strings = st_init_table_with_size(&rb_fstring_hash_type, 10000); + +#if HAVE_MMAP + vm->shape_list = (rb_shape_t *)mmap(NULL, rb_size_mul_or_raise(SHAPE_BITMAP_SIZE * 32, sizeof(rb_shape_t), rb_eRuntimeError), + PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (vm->shape_list == MAP_FAILED) { + vm->shape_list = 0; + } +#else + vm->shape_list = xcalloc(SHAPE_BITMAP_SIZE * 32, sizeof(rb_shape_t)); +#endif + + if (!vm->shape_list) { + rb_memerror(); + } + + // Root shape + vm->root_shape = rb_shape_alloc(0, 0); + RUBY_ASSERT(rb_shape_id(vm->root_shape) == ROOT_SHAPE_ID); + + // Frozen root shape + vm->frozen_root_shape = rb_shape_alloc(rb_make_internal_id(), vm->root_shape); + vm->frozen_root_shape->type = (uint8_t)SHAPE_FROZEN; + RUBY_ASSERT(rb_shape_id(vm->frozen_root_shape) == FROZEN_ROOT_SHAPE_ID); + + vm->next_shape_id = 2; } /* Stub for builtin function when not building YJIT units*/ diff --git a/vm_callinfo.h b/vm_callinfo.h index fd2215be7d..e5b04c0709 100644 --- a/vm_callinfo.h +++ b/vm_callinfo.h @@ -10,6 +10,7 @@ #include "debug_counter.h" #include "internal/class.h" +#include "shape.h" enum vm_call_flag_bits { VM_CALL_ARGS_SPLAT_bit, /* m(*args) */ @@ -284,14 +285,32 @@ struct rb_callcache { const vm_call_handler call_; union { - const unsigned int attr_index; + struct { + const attr_index_t index; + shape_id_t dest_shape_id; + } attr; const enum method_missing_reason method_missing_reason; /* used by method_missing */ VALUE v; } aux_; }; -#define VM_CALLCACHE_UNMARKABLE IMEMO_FL_USER0 -#define VM_CALLCACHE_ON_STACK IMEMO_FL_USER1 +#define VM_CALLCACHE_UNMARKABLE FL_FREEZE +#define VM_CALLCACHE_ON_STACK FL_EXIVAR + +extern const struct rb_callcache *rb_vm_empty_cc(void); +extern const struct rb_callcache *rb_vm_empty_cc_for_super(void); + +#define vm_cc_empty() rb_vm_empty_cc() + +static inline void +vm_cc_attr_index_initialize(const struct rb_callcache *cc, shape_id_t shape_id) +{ + VM_ASSERT(IMEMO_TYPE_P(cc, imemo_callcache)); + VM_ASSERT(cc != vm_cc_empty()); + IMEMO_SET_CACHED_SHAPE_ID((VALUE)cc, shape_id); + *(attr_index_t *)&cc->aux_.attr.index = 0; + *(shape_id_t *)&cc->aux_.attr.dest_shape_id = shape_id; +} static inline const struct rb_callcache * vm_cc_new(VALUE klass, @@ -299,6 +318,7 @@ vm_cc_new(VALUE klass, vm_call_handler call) { const struct rb_callcache *cc = (const struct rb_callcache *)rb_imemo_new(imemo_callcache, (VALUE)cme, (VALUE)call, 0, klass); + vm_cc_attr_index_initialize(cc, INVALID_SHAPE_ID); RB_DEBUG_COUNTER_INC(cc_new); return cc; } @@ -350,30 +370,71 @@ vm_cc_call(const struct rb_callcache *cc) return cc->call_; } -static inline unsigned int +static inline attr_index_t vm_cc_attr_index(const struct rb_callcache *cc) { VM_ASSERT(IMEMO_TYPE_P(cc, imemo_callcache)); - return cc->aux_.attr_index - 1; + return cc->aux_.attr.index - 1; } static inline bool vm_cc_attr_index_p(const struct rb_callcache *cc) { VM_ASSERT(IMEMO_TYPE_P(cc, imemo_callcache)); - return cc->aux_.attr_index > 0; + return cc->aux_.attr.index != 0; } -static inline uint32_t -vm_ic_entry_index(const struct iseq_inline_iv_cache_entry *ic) +static inline shape_id_t +vm_cc_attr_index_source_shape_id(const struct rb_callcache *cc) { - return ic->entry->index; + VM_ASSERT(IMEMO_TYPE_P(cc, imemo_callcache)); + + return IMEMO_CACHED_SHAPE_ID((VALUE)cc); +} + +static inline shape_id_t +vm_cc_attr_shape_id(const struct rb_callcache *cc) +{ + VM_ASSERT(IMEMO_TYPE_P(cc, imemo_callcache)); + return vm_cc_attr_index_source_shape_id(cc); +} + +static inline shape_id_t +vm_cc_attr_index_dest_shape_id(const struct rb_callcache *cc) +{ + VM_ASSERT(IMEMO_TYPE_P(cc, imemo_callcache)); + + return cc->aux_.attr.dest_shape_id; +} + +static inline attr_index_t +vm_ic_attr_index(const struct iseq_inline_iv_cache_entry *ic) +{ + return ic->attr_index - 1; } static inline bool -vm_ic_entry_p(const struct iseq_inline_iv_cache_entry *ic) +vm_ic_attr_index_p(const struct iseq_inline_iv_cache_entry *ic) { - return ic->entry; + return ic->attr_index > 0; +} + +static inline shape_id_t +vm_ic_attr_shape_id(const struct iseq_inline_iv_cache_entry *ic) +{ + return ic->source_shape_id; +} + +static inline shape_id_t +vm_ic_attr_index_source_shape_id(const struct iseq_inline_iv_cache_entry *ic) +{ + return ic->source_shape_id; +} + +static inline shape_id_t +vm_ic_attr_index_dest_shape_id(const struct iseq_inline_iv_cache_entry *ic) +{ + return ic->dest_shape_id; } static inline unsigned int @@ -407,10 +468,6 @@ vm_cc_valid_p(const struct rb_callcache *cc, const rb_callable_method_entry_t *c } } -extern const struct rb_callcache *rb_vm_empty_cc(void); -extern const struct rb_callcache *rb_vm_empty_cc_for_super(void); -#define vm_cc_empty() rb_vm_empty_cc() - /* callcache: mutate */ static inline void @@ -422,26 +479,29 @@ vm_cc_call_set(const struct rb_callcache *cc, vm_call_handler call) } static inline void -vm_cc_attr_index_set(const struct rb_callcache *cc, int index) +vm_cc_attr_index_set(const struct rb_callcache *cc, attr_index_t index, shape_id_t source_shape_id, shape_id_t dest_shape_id) { VM_ASSERT(IMEMO_TYPE_P(cc, imemo_callcache)); VM_ASSERT(cc != vm_cc_empty()); - *(int *)&cc->aux_.attr_index = index + 1; + IMEMO_SET_CACHED_SHAPE_ID((VALUE)cc, source_shape_id); + *(attr_index_t *)&cc->aux_.attr.index = (index + 1); + *(shape_id_t *)&cc->aux_.attr.dest_shape_id = dest_shape_id; } static inline void -vm_ic_entry_set(struct iseq_inline_iv_cache_entry *ic, struct rb_iv_index_tbl_entry *entry, const rb_iseq_t *iseq) +vm_ic_attr_index_set(const rb_iseq_t *iseq, const struct iseq_inline_iv_cache_entry *ic, attr_index_t index, shape_id_t source_shape_id, shape_id_t dest_shape_id) { - ic->entry = entry; - RB_OBJ_WRITTEN(iseq, Qundef, entry->class_value); + *(shape_id_t *)&ic->source_shape_id = source_shape_id; + *(shape_id_t *)&ic->dest_shape_id = dest_shape_id; + *(attr_index_t *)&ic->attr_index = index + 1; } static inline void -vm_cc_attr_index_initialize(const struct rb_callcache *cc) +vm_ic_attr_index_initialize(const struct iseq_inline_iv_cache_entry *ic, shape_id_t shape_id) { - VM_ASSERT(IMEMO_TYPE_P(cc, imemo_callcache)); - VM_ASSERT(cc != vm_cc_empty()); - *(int *)&cc->aux_.attr_index = 0; + *(shape_id_t *)&ic->source_shape_id = shape_id; + *(shape_id_t *)&ic->dest_shape_id = shape_id; + *(attr_index_t *)&ic->attr_index = 0; } static inline void diff --git a/vm_core.h b/vm_core.h index e11838a59a..4dd873c1f8 100644 --- a/vm_core.h +++ b/vm_core.h @@ -99,6 +99,7 @@ extern int ruby_assert_critical_section_entered; #include "ruby/st.h" #include "ruby_atomic.h" #include "vm_opts.h" +#include "shape.h" #include "ruby/thread_native.h" @@ -272,7 +273,9 @@ struct iseq_inline_constant_cache { }; struct iseq_inline_iv_cache_entry { - struct rb_iv_index_tbl_entry *entry; + shape_id_t source_shape_id; + shape_id_t dest_shape_id; + attr_index_t attr_index; }; struct iseq_inline_cvar_cache_entry { @@ -687,6 +690,12 @@ typedef struct rb_vm_struct { VALUE mark_object_ary; const VALUE special_exceptions[ruby_special_error_count]; + /* object shapes */ + rb_shape_t *shape_list; + rb_shape_t *root_shape; + rb_shape_t *frozen_root_shape; + shape_id_t next_shape_id; + /* load */ VALUE top_self; VALUE load_path; diff --git a/vm_eval.c b/vm_eval.c index a71688c6bc..eddc6f80da 100644 --- a/vm_eval.c +++ b/vm_eval.c @@ -48,7 +48,7 @@ rb_vm_call0(rb_execution_context_t *ec, VALUE recv, ID id, int argc, const VALUE { struct rb_calling_info calling = { .ci = &VM_CI_ON_STACK(id, kw_splat ? VM_CALL_KW_SPLAT : 0, argc, NULL), - .cc = &VM_CC_ON_STACK(Qfalse, vm_call_general, { 0 }, cme), + .cc = &VM_CC_ON_STACK(Qfalse, vm_call_general, {{ 0 }}, cme), .block_handler = vm_passed_block_handler(ec), .recv = recv, .argc = argc, @@ -90,7 +90,7 @@ vm_call0_cc(rb_execution_context_t *ec, VALUE recv, ID id, int argc, const VALUE static VALUE vm_call0_cme(rb_execution_context_t *ec, struct rb_calling_info *calling, const VALUE *argv, const rb_callable_method_entry_t *cme) { - calling->cc = &VM_CC_ON_STACK(Qfalse, vm_call_general, { 0 }, cme); + calling->cc = &VM_CC_ON_STACK(Qfalse, vm_call_general, {{ 0 }}, cme); return vm_call0_body(ec, calling, argv); } diff --git a/vm_insnhelper.c b/vm_insnhelper.c index 03796d2ad1..b8cb8c1fdd 100644 --- a/vm_insnhelper.c +++ b/vm_insnhelper.c @@ -50,6 +50,11 @@ MJIT_STATIC VALUE ruby_vm_special_exception_copy(VALUE exc) { VALUE e = rb_obj_alloc(rb_class_real(RBASIC_CLASS(exc))); + rb_shape_t * shape = rb_shape_get_shape(exc); + if (rb_shape_frozen_shape_p(shape)) { + shape = shape->parent; + } + rb_shape_set_shape(e, shape); rb_obj_copy_ivar(e, exc); return e; } @@ -1086,35 +1091,17 @@ vm_get_cvar_base(const rb_cref_t *cref, const rb_control_frame_t *cfp, int top_l return klass; } -static bool -iv_index_tbl_lookup(struct st_table *iv_index_tbl, ID id, struct rb_iv_index_tbl_entry **ent) -{ - int found; - st_data_t ent_data; - - if (iv_index_tbl == NULL) return false; - - RB_VM_LOCK_ENTER(); - { - found = st_lookup(iv_index_tbl, (st_data_t)id, &ent_data); - } - RB_VM_LOCK_LEAVE(); - if (found) *ent = (struct rb_iv_index_tbl_entry *)ent_data; - - return found ? true : false; -} - -ALWAYS_INLINE(static void fill_ivar_cache(const rb_iseq_t *iseq, IVC ic, const struct rb_callcache *cc, int is_attr, struct rb_iv_index_tbl_entry *ent)); - +ALWAYS_INLINE(static void fill_ivar_cache(const rb_iseq_t *iseq, IVC ic, const struct rb_callcache *cc, int is_attr, attr_index_t index, shape_id_t shape_id)); static inline void -fill_ivar_cache(const rb_iseq_t *iseq, IVC ic, const struct rb_callcache *cc, int is_attr, struct rb_iv_index_tbl_entry *ent) +fill_ivar_cache(const rb_iseq_t *iseq, IVC ic, const struct rb_callcache *cc, int is_attr, attr_index_t index, shape_id_t shape_id) { - // fill cache - if (!is_attr) { - vm_ic_entry_set(ic, ent, iseq); + if (is_attr) { + if (vm_cc_markable(cc)) { + vm_cc_attr_index_set(cc, index, shape_id, shape_id); + } } else { - vm_cc_attr_index_set(cc, ent->index); + vm_ic_attr_index_set(iseq, ic, index, shape_id, shape_id); } } @@ -1129,68 +1116,120 @@ vm_getivar(VALUE obj, ID id, const rb_iseq_t *iseq, IVC ic, const struct rb_call { #if OPT_IC_FOR_IVAR VALUE val = Qundef; + shape_id_t shape_id; + VALUE * ivar_list; if (SPECIAL_CONST_P(obj)) { - // frozen? + return Qnil; } - else if (LIKELY(is_attr ? - RB_DEBUG_COUNTER_INC_UNLESS(ivar_get_ic_miss_unset, vm_cc_attr_index_p(cc)) : - RB_DEBUG_COUNTER_INC_UNLESS(ivar_get_ic_miss_serial, vm_ic_entry_p(ic) && ic->entry->class_serial == RCLASS_SERIAL(RBASIC(obj)->klass)))) { - uint32_t index = !is_attr ? vm_ic_entry_index(ic): (vm_cc_attr_index(cc)); - RB_DEBUG_COUNTER_INC(ivar_get_ic_hit); +#if SHAPE_IN_BASIC_FLAGS + shape_id = RBASIC_SHAPE_ID(obj); +#endif - if (LIKELY(BUILTIN_TYPE(obj) == T_OBJECT) && - LIKELY(index < ROBJECT_NUMIV(obj))) { - val = ROBJECT_IVPTR(obj)[index]; + switch (BUILTIN_TYPE(obj)) { + case T_OBJECT: + ivar_list = ROBJECT_IVPTR(obj); + VM_ASSERT(rb_ractor_shareable_p(obj) ? rb_ractor_shareable_p(val) : true); - VM_ASSERT(ractor_object_incidental_shareable_p(obj, val)); - } - else if (FL_TEST_RAW(obj, FL_EXIVAR)) { - val = rb_ivar_generic_lookup_with_index(obj, id, index); - } +#if !SHAPE_IN_BASIC_FLAGS + shape_id = ROBJECT_SHAPE_ID(obj); +#endif + break; + case T_CLASS: + case T_MODULE: + { + goto general_path; + } + default: + if (FL_TEST_RAW(obj, FL_EXIVAR)) { + struct gen_ivtbl *ivtbl; + rb_gen_ivtbl_get(obj, id, &ivtbl); +#if !SHAPE_IN_BASIC_FLAGS + shape_id = ivtbl->shape_id; +#endif + ivar_list = ivtbl->ivptr; + } else { + return Qnil; + } + } - goto ret; + shape_id_t cached_id; + + if (is_attr) { + cached_id = vm_cc_attr_shape_id(cc); } else { - struct rb_iv_index_tbl_entry *ent; + cached_id = vm_ic_attr_shape_id(ic); + } - if (BUILTIN_TYPE(obj) == T_OBJECT) { - struct st_table *iv_index_tbl = ROBJECT_IV_INDEX_TBL(obj); + attr_index_t index; - if (iv_index_tbl && iv_index_tbl_lookup(iv_index_tbl, id, &ent)) { - fill_ivar_cache(iseq, ic, cc, is_attr, ent); + if (LIKELY(cached_id == shape_id)) { + RB_DEBUG_COUNTER_INC(ivar_get_ic_hit); - // get value - if (ent->index < ROBJECT_NUMIV(obj)) { - val = ROBJECT_IVPTR(obj)[ent->index]; - - VM_ASSERT(ractor_object_incidental_shareable_p(obj, val)); - } - } + if (is_attr && vm_cc_attr_index_p(cc)) { + index = vm_cc_attr_index(cc); } - else if (FL_TEST_RAW(obj, FL_EXIVAR)) { - struct st_table *iv_index_tbl = RCLASS_IV_INDEX_TBL(rb_obj_class(obj)); - - if (iv_index_tbl && iv_index_tbl_lookup(iv_index_tbl, id, &ent)) { - fill_ivar_cache(iseq, ic, cc, is_attr, ent); - val = rb_ivar_generic_lookup_with_index(obj, id, ent->index); - } - } - else { - // T_CLASS / T_MODULE - goto general_path; - } - - ret: - if (LIKELY(val != Qundef)) { - return val; + else if (!is_attr && vm_ic_attr_index_p(ic)) { + index = vm_ic_attr_index(ic); } else { return Qnil; } + + val = ivar_list[index]; + VM_ASSERT(BUILTIN_TYPE(obj) == T_OBJECT && rb_ractor_shareable_p(obj) ? rb_ractor_shareable_p(val) : true); } - general_path: + else { // cache miss case +#if RUBY_DEBUG + if (is_attr) { + if (cached_id != INVALID_SHAPE_ID) { + RB_DEBUG_COUNTER_INC(ivar_get_cc_miss_set); + } else { + RB_DEBUG_COUNTER_INC(ivar_get_cc_miss_unset); + } + } + else { + if (cached_id != INVALID_SHAPE_ID) { + RB_DEBUG_COUNTER_INC(ivar_get_ic_miss_set); + } else { + RB_DEBUG_COUNTER_INC(ivar_get_ic_miss_unset); + } + } +#endif + + attr_index_t index; + rb_shape_t *shape = rb_shape_get_shape_by_id(shape_id); + + if (rb_shape_get_iv_index(shape, id, &index)) { + // This fills in the cache with the shared cache object. + // "ent" is the shared cache object + fill_ivar_cache(iseq, ic, cc, is_attr, index, shape_id); + + // We fetched the ivar list above + val = ivar_list[index]; + } + else { + if (is_attr) { + if (vm_cc_markable(cc)) { + vm_cc_attr_index_initialize(cc, shape_id); + } + } + else { + vm_ic_attr_index_initialize(ic, shape_id); + } + + val = Qnil; + } + + } + + RUBY_ASSERT(val != Qundef); + + return val; + +general_path: #endif /* OPT_IC_FOR_IVAR */ RB_DEBUG_COUNTER_INC(ivar_get_ic_miss); @@ -1202,6 +1241,20 @@ vm_getivar(VALUE obj, ID id, const rb_iseq_t *iseq, IVC ic, const struct rb_call } } +static void +populate_cache(attr_index_t index, shape_id_t shape_id, shape_id_t next_shape_id, ID id, const rb_iseq_t *iseq, IVC ic, const struct rb_callcache *cc, bool is_attr) +{ + // Cache population code + if (is_attr) { + if (vm_cc_markable(cc)) { + vm_cc_attr_index_set(cc, index, shape_id, next_shape_id); + } + } + else { + vm_ic_attr_index_set(iseq, ic, index, shape_id, next_shape_id); + } +} + ALWAYS_INLINE(static VALUE vm_setivar_slowpath(VALUE obj, ID id, VALUE val, const rb_iseq_t *iseq, IVC ic, const struct rb_callcache *cc, int is_attr)); NOINLINE(static VALUE vm_setivar_slowpath_ivar(VALUE obj, ID id, VALUE val, const rb_iseq_t *iseq, IVC ic)); NOINLINE(static VALUE vm_setivar_slowpath_attr(VALUE obj, ID id, VALUE val, const struct rb_callcache *cc)); @@ -1209,35 +1262,72 @@ NOINLINE(static VALUE vm_setivar_slowpath_attr(VALUE obj, ID id, VALUE val, cons static VALUE vm_setivar_slowpath(VALUE obj, ID id, VALUE val, const rb_iseq_t *iseq, IVC ic, const struct rb_callcache *cc, int is_attr) { - rb_check_frozen_internal(obj); - #if OPT_IC_FOR_IVAR - if (RB_TYPE_P(obj, T_OBJECT)) { - struct st_table *iv_index_tbl = ROBJECT_IV_INDEX_TBL(obj); - struct rb_iv_index_tbl_entry *ent; + switch (BUILTIN_TYPE(obj)) { + case T_OBJECT: + { + rb_check_frozen_internal(obj); - if (iv_index_tbl_lookup(iv_index_tbl, id, &ent)) { - if (!is_attr) { - vm_ic_entry_set(ic, ent, iseq); - } - else if (ent->index >= INT_MAX) { - rb_raise(rb_eArgError, "too many instance variables"); - } - else { - vm_cc_attr_index_set(cc, (int)(ent->index)); - } + attr_index_t index; - uint32_t index = ent->index; + uint32_t num_iv = ROBJECT_NUMIV(obj); + rb_shape_t* shape = rb_shape_get_shape(obj); + shape_id_t current_shape_id = ROBJECT_SHAPE_ID(obj); + shape_id_t next_shape_id = current_shape_id; - if (UNLIKELY(index >= ROBJECT_NUMIV(obj))) { - rb_init_iv_list(obj); + rb_shape_t* next_shape = rb_shape_get_next(shape, obj, id); + + if (shape != next_shape) { + rb_shape_set_shape(obj, next_shape); + next_shape_id = ROBJECT_SHAPE_ID(obj); + } + + if (rb_shape_get_iv_index(next_shape, id, &index)) { // based off the hash stored in the transition tree + if (index >= MAX_IVARS) { + rb_raise(rb_eArgError, "too many instance variables"); + } + + populate_cache(index, current_shape_id, next_shape_id, id, iseq, ic, cc, is_attr); + } + else { + rb_bug("Didn't find instance variable %s\n", rb_id2name(id)); + } + + // Ensure the IV buffer is wide enough to store the IV + if (UNLIKELY(index >= num_iv)) { + rb_init_iv_list(obj); + } + + VALUE *ptr = ROBJECT_IVPTR(obj); + RB_OBJ_WRITE(obj, &ptr[index], val); + RB_DEBUG_COUNTER_INC(ivar_set_ic_miss_iv_hit); + + return val; } - VALUE *ptr = ROBJECT_IVPTR(obj); - RB_OBJ_WRITE(obj, &ptr[index], val); - RB_DEBUG_COUNTER_INC(ivar_set_ic_miss_iv_hit); + case T_CLASS: + case T_MODULE: + break; + default: + { + shape_id_t shape_id = rb_shape_get_shape_id(obj); + rb_ivar_set(obj, id, val); + shape_id_t next_shape_id = rb_shape_get_shape_id(obj); + rb_shape_t *next_shape = rb_shape_get_shape_by_id(next_shape_id); + attr_index_t index; - return val; - } + if (rb_shape_get_iv_index(next_shape, id, &index)) { // based off the hash stored in the transition tree + if (index >= MAX_IVARS) { + rb_raise(rb_eArgError, "too many instance variables"); + } + + populate_cache(index, shape_id, next_shape_id, id, iseq, ic, cc, is_attr); + } + else { + rb_bug("didn't find the id\n"); + } + + return val; + } } #endif RB_DEBUG_COUNTER_INC(ivar_set_ic_miss); @@ -1256,39 +1346,94 @@ vm_setivar_slowpath_attr(VALUE obj, ID id, VALUE val, const struct rb_callcache return vm_setivar_slowpath(obj, id, val, NULL, NULL, cc, true); } +NOINLINE(static VALUE vm_setivar_default(VALUE obj, ID id, VALUE val, shape_id_t source_shape_id, shape_id_t dest_shape_id, attr_index_t index)); +static VALUE +vm_setivar_default(VALUE obj, ID id, VALUE val, shape_id_t source_shape_id, shape_id_t dest_shape_id, attr_index_t index) +{ +#if SHAPE_IN_BASIC_FLAGS + shape_id_t shape_id = RBASIC_SHAPE_ID(obj); +#else + shape_id_t shape_id = rb_generic_shape_id(obj); +#endif + + // Cache hit case + if (shape_id == source_shape_id) { + RUBY_ASSERT(dest_shape_id != INVALID_SHAPE_ID && shape_id != INVALID_SHAPE_ID); + + struct gen_ivtbl *ivtbl = 0; + if (dest_shape_id != shape_id) { + ivtbl = rb_ensure_generic_iv_list_size(obj, index + 1); +#if SHAPE_IN_BASIC_FLAGS + RBASIC_SET_SHAPE_ID(obj, dest_shape_id); +#else + ivtbl->shape_id = dest_shape_id; +#endif + } + else { + // Just get the IV table + rb_gen_ivtbl_get(obj, 0, &ivtbl); + } + + VALUE *ptr = ivtbl->ivptr; + + RB_OBJ_WRITE(obj, &ptr[index], val); + + RB_DEBUG_COUNTER_INC(ivar_set_ic_hit); + + return val; + } + + return Qundef; +} + static inline VALUE -vm_setivar(VALUE obj, ID id, VALUE val, const rb_iseq_t *iseq, IVC ic, const struct rb_callcache *cc, int is_attr) +vm_setivar(VALUE obj, ID id, VALUE val, shape_id_t source_shape_id, shape_id_t dest_shape_id, attr_index_t index) { #if OPT_IC_FOR_IVAR - if (LIKELY(RB_TYPE_P(obj, T_OBJECT)) && - LIKELY(!RB_OBJ_FROZEN_RAW(obj))) { + switch (BUILTIN_TYPE(obj)) { + case T_OBJECT: + { + VM_ASSERT(!rb_ractor_shareable_p(obj) || rb_obj_frozen_p(obj)); + // If object's shape id is the same as the source + // then do the shape transition and write the ivar + // If object's shape id is the same as the dest + // then write the ivar + shape_id_t shape_id = ROBJECT_SHAPE_ID(obj); - VM_ASSERT(!rb_ractor_shareable_p(obj)); + // Do we have a cache hit *and* is the CC intitialized + if (shape_id == source_shape_id) { + RUBY_ASSERT(dest_shape_id != INVALID_SHAPE_ID && shape_id != INVALID_SHAPE_ID); - if (LIKELY( - (!is_attr && RB_DEBUG_COUNTER_INC_UNLESS(ivar_set_ic_miss_serial, vm_ic_entry_p(ic) && ic->entry->class_serial == RCLASS_SERIAL(RBASIC(obj)->klass))) || - ( is_attr && RB_DEBUG_COUNTER_INC_UNLESS(ivar_set_ic_miss_unset, vm_cc_attr_index_p(cc))))) { - uint32_t index = !is_attr ? vm_ic_entry_index(ic) : vm_cc_attr_index(cc); + VM_ASSERT(!rb_ractor_shareable_p(obj)); - if (UNLIKELY(index >= ROBJECT_NUMIV(obj))) { - rb_init_iv_list(obj); + if (dest_shape_id != shape_id) { + if (UNLIKELY(index >= ROBJECT_NUMIV(obj))) { + rb_init_iv_list(obj); + } + ROBJECT_SET_SHAPE_ID(obj, dest_shape_id); + } + + RUBY_ASSERT(index < ROBJECT_NUMIV(obj)); + + VALUE *ptr = ROBJECT_IVPTR(obj); + + RB_OBJ_WRITE(obj, &ptr[index], val); + + RB_DEBUG_COUNTER_INC(ivar_set_ic_hit); + + return val; + } } - VALUE *ptr = ROBJECT_IVPTR(obj); - RB_OBJ_WRITE(obj, &ptr[index], val); - RB_DEBUG_COUNTER_INC(ivar_set_ic_hit); - return val; /* inline cache hit */ - } - } - else { + break; + case T_CLASS: + case T_MODULE: RB_DEBUG_COUNTER_INC(ivar_set_ic_miss_noobject); + default: + break; } + + return Qundef; #endif /* OPT_IC_FOR_IVAR */ - if (is_attr) { - return vm_setivar_slowpath_attr(obj, id, val, cc); - } - else { - return vm_setivar_slowpath_ivar(obj, id, val, iseq, ic); - } } static VALUE @@ -1383,7 +1528,22 @@ vm_getinstancevariable(const rb_iseq_t *iseq, VALUE obj, ID id, IVC ic) static inline void vm_setinstancevariable(const rb_iseq_t *iseq, VALUE obj, ID id, VALUE val, IVC ic) { - vm_setivar(obj, id, val, iseq, ic, 0, 0); + shape_id_t source_shape_id = vm_ic_attr_index_source_shape_id(ic); + attr_index_t index = vm_ic_attr_index(ic); + shape_id_t dest_shape_id = vm_ic_attr_index_dest_shape_id(ic); + if (UNLIKELY(vm_setivar(obj, id, val, source_shape_id, dest_shape_id, index) == Qundef)) { + switch (BUILTIN_TYPE(obj)) { + case T_OBJECT: + case T_CLASS: + case T_MODULE: + break; + default: + if (vm_setivar_default(obj, id, val, source_shape_id, dest_shape_id, index) != Qundef) { + return; + } + } + vm_setivar_slowpath_ivar(obj, id, val, iseq, ic); + } } void @@ -1392,28 +1552,6 @@ rb_vm_setinstancevariable(const rb_iseq_t *iseq, VALUE obj, ID id, VALUE val, IV vm_setinstancevariable(iseq, obj, id, val, ic); } -/* Set the instance variable +val+ on object +obj+ at the +index+. - * This function only works with T_OBJECT objects, so make sure - * +obj+ is of type T_OBJECT before using this function. - */ -VALUE -rb_vm_set_ivar_idx(VALUE obj, uint32_t index, VALUE val) -{ - RUBY_ASSERT(RB_TYPE_P(obj, T_OBJECT)); - - rb_check_frozen_internal(obj); - - VM_ASSERT(!rb_ractor_shareable_p(obj)); - - if (UNLIKELY(index >= ROBJECT_NUMIV(obj))) { - rb_init_iv_list(obj); - } - VALUE *ptr = ROBJECT_IVPTR(obj); - RB_OBJ_WRITE(obj, &ptr[index], val); - - return val; -} - static VALUE vm_throw_continue(const rb_execution_context_t *ec, VALUE err) { @@ -3106,17 +3244,45 @@ vm_call_ivar(rb_execution_context_t *ec, rb_control_frame_t *cfp, struct rb_call const struct rb_callcache *cc = calling->cc; RB_DEBUG_COUNTER_INC(ccf_ivar); cfp->sp -= 1; - return vm_getivar(calling->recv, vm_cc_cme(cc)->def->body.attr.id, NULL, NULL, cc, TRUE); + VALUE ivar = vm_getivar(calling->recv, vm_cc_cme(cc)->def->body.attr.id, NULL, NULL, cc, TRUE); + return ivar; +} + +static VALUE +vm_call_attrset_direct(rb_execution_context_t *ec, rb_control_frame_t *cfp, const struct rb_callcache *cc, VALUE obj) +{ + RB_DEBUG_COUNTER_INC(ccf_attrset); + VALUE val = *(cfp->sp - 1); + cfp->sp -= 2; + shape_id_t source_shape_id = vm_cc_attr_index_source_shape_id(cc); + attr_index_t index = vm_cc_attr_index(cc); + shape_id_t dest_shape_id = vm_cc_attr_index_dest_shape_id(cc); + ID id = vm_cc_cme(cc)->def->body.attr.id; + rb_check_frozen_internal(obj); + VALUE res = vm_setivar(obj, id, val, source_shape_id, dest_shape_id, index); + if (res == Qundef) { + switch (BUILTIN_TYPE(obj)) { + case T_OBJECT: + case T_CLASS: + case T_MODULE: + break; + default: + { + res = vm_setivar_default(obj, id, val, source_shape_id, dest_shape_id, index); + if (res != Qundef) { + return res; + } + } + } + res = vm_setivar_slowpath_attr(obj, id, val, cc); + } + return res; } static VALUE vm_call_attrset(rb_execution_context_t *ec, rb_control_frame_t *cfp, struct rb_calling_info *calling) { - const struct rb_callcache *cc = calling->cc; - RB_DEBUG_COUNTER_INC(ccf_attrset); - VALUE val = *(cfp->sp - 1); - cfp->sp -= 2; - return vm_setivar(calling->recv, vm_cc_cme(cc)->def->body.attr.id, val, NULL, NULL, cc, 1); + return vm_call_attrset_direct(ec, cfp, calling->cc, calling->recv); } bool @@ -3225,7 +3391,7 @@ vm_call_alias(rb_execution_context_t *ec, rb_control_frame_t *cfp, struct rb_cal { calling->cc = &VM_CC_ON_STACK(Qundef, vm_call_general, - { 0 }, + {{0}}, aliased_callable_method_entry(vm_cc_cme(calling->cc))); return vm_call_method_each_type(ec, cfp, calling); @@ -3395,7 +3561,7 @@ vm_call_method_missing_body(rb_execution_context_t *ec, rb_control_frame_t *reg_ ec->method_missing_reason = reason; calling->ci = &VM_CI_ON_STACK(idMethodMissing, flag, argc, vm_ci_kwarg(orig_ci)); - calling->cc = &VM_CC_ON_STACK(Qundef, vm_call_general, { 0 }, + calling->cc = &VM_CC_ON_STACK(Qundef, vm_call_general, {{ 0 }}, rb_callable_method_entry_without_refinements(CLASS_OF(calling->recv), idMethodMissing, NULL)); return vm_call_method(ec, reg_cfp, calling); } @@ -3421,7 +3587,7 @@ vm_call_zsuper(rb_execution_context_t *ec, rb_control_frame_t *cfp, struct rb_ca cme = refined_method_callable_without_refinement(cme); } - calling->cc = &VM_CC_ON_STACK(Qundef, vm_call_general, { 0 }, cme); + calling->cc = &VM_CC_ON_STACK(Qundef, vm_call_general, {{ 0 }}, cme); return vm_call_method_each_type(ec, cfp, calling); } @@ -3528,7 +3694,7 @@ search_refined_method(rb_execution_context_t *ec, rb_control_frame_t *cfp, struc static VALUE vm_call_refined(rb_execution_context_t *ec, rb_control_frame_t *cfp, struct rb_calling_info *calling) { - struct rb_callcache *ref_cc = &VM_CC_ON_STACK(Qundef, vm_call_general, { 0 }, + struct rb_callcache *ref_cc = &VM_CC_ON_STACK(Qundef, vm_call_general, {{ 0 }}, search_refined_method(ec, cfp, calling)); if (vm_cc_cme(ref_cc)) { @@ -3708,18 +3874,45 @@ vm_call_method_each_type(rb_execution_context_t *ec, rb_control_frame_t *cfp, st CALLER_REMOVE_EMPTY_KW_SPLAT(cfp, calling, ci); rb_check_arity(calling->argc, 1, 1); - vm_cc_attr_index_initialize(cc); + const unsigned int aset_mask = (VM_CALL_ARGS_SPLAT | VM_CALL_KW_SPLAT | VM_CALL_KWARG); - VM_CALL_METHOD_ATTR(v, - vm_call_attrset(ec, cfp, calling), - CC_SET_FASTPATH(cc, vm_call_attrset, !(vm_ci_flag(ci) & aset_mask))); + + if (vm_cc_markable(cc)) { + vm_cc_attr_index_initialize(cc, INVALID_SHAPE_ID); + VM_CALL_METHOD_ATTR(v, + vm_call_attrset_direct(ec, cfp, cc, calling->recv), + CC_SET_FASTPATH(cc, vm_call_attrset, !(vm_ci_flag(ci) & aset_mask))); + } else { + cc = &((struct rb_callcache) { + .flags = T_IMEMO | + (imemo_callcache << FL_USHIFT) | + VM_CALLCACHE_UNMARKABLE | + ((VALUE)INVALID_SHAPE_ID << SHAPE_FLAG_SHIFT) | + VM_CALLCACHE_ON_STACK, + .klass = cc->klass, + .cme_ = cc->cme_, + .call_ = cc->call_, + .aux_ = { + .attr = { + .index = 0, + .dest_shape_id = INVALID_SHAPE_ID, + } + }, + }); + + VM_CALL_METHOD_ATTR(v, + vm_call_attrset_direct(ec, cfp, cc, calling->recv), + CC_SET_FASTPATH(cc, vm_call_attrset, !(vm_ci_flag(ci) & aset_mask))); + } return v; case VM_METHOD_TYPE_IVAR: CALLER_SETUP_ARG(cfp, calling, ci); CALLER_REMOVE_EMPTY_KW_SPLAT(cfp, calling, ci); rb_check_arity(calling->argc, 0, 0); - vm_cc_attr_index_initialize(cc); + if (vm_cc_markable(cc)) { + vm_cc_attr_index_initialize(cc, INVALID_SHAPE_ID); + } const unsigned int ivar_mask = (VM_CALL_ARGS_SPLAT | VM_CALL_KW_SPLAT); VM_CALL_METHOD_ATTR(v, vm_call_ivar(ec, cfp, calling), diff --git a/yjit/bindgen/src/main.rs b/yjit/bindgen/src/main.rs index ebeeab14b1..7ae9f06adc 100644 --- a/yjit/bindgen/src/main.rs +++ b/yjit/bindgen/src/main.rs @@ -40,6 +40,7 @@ fn main() { .header("internal.h") .header("internal/re.h") .header("include/ruby/ruby.h") + .header("shape.h") .header("vm_core.h") .header("vm_callinfo.h") @@ -81,6 +82,12 @@ fn main() { // This function prints info about a value and is useful for debugging .allowlist_function("rb_obj_info_dump") + // From shape.h + .allowlist_function("rb_shape_get_shape_id") + .allowlist_function("rb_shape_get_shape_by_id") + .allowlist_function("rb_shape_flags_mask") + .allowlist_function("rb_shape_get_iv_index") + // From ruby/internal/intern/object.h .allowlist_function("rb_obj_is_kind_of") diff --git a/yjit/src/asm/x86_64/mod.rs b/yjit/src/asm/x86_64/mod.rs index d310e3bf12..42d97b7e80 100644 --- a/yjit/src/asm/x86_64/mod.rs +++ b/yjit/src/asm/x86_64/mod.rs @@ -617,7 +617,7 @@ fn write_rm_multi(cb: &mut CodeBlock, op_mem_reg8: u8, op_mem_reg_pref: u8, op_r write_rm(cb, sz_pref, rex_w, X86Opnd::None, opnd0, op_ext_imm, &[op_mem_imm_lrg]); cb.write_int(uimm.value, if opnd_size > 32 { 32 } else { opnd_size.into() }); } else { - panic!("immediate value too large"); + panic!("immediate value too large (num_bits={})", num_bits); } }, _ => unreachable!() diff --git a/yjit/src/codegen.rs b/yjit/src/codegen.rs index 22634a7de0..f453f133d9 100644 --- a/yjit/src/codegen.rs +++ b/yjit/src/codegen.rs @@ -1938,11 +1938,9 @@ fn gen_set_ivar( let val_opnd = ctx.stack_pop(1); let recv_opnd = ctx.stack_pop(1); - let ivar_index: u32 = unsafe { rb_obj_ensure_iv_index_mapping(recv, ivar_name) }; - - // Call rb_vm_set_ivar_idx with the receiver, the index of the ivar, and the value + // Call rb_vm_set_ivar_id with the receiver, the ivar name, and the value let val = asm.ccall( - rb_vm_set_ivar_idx as *const u8, + rb_vm_set_ivar_id as *const u8, vec![ recv_opnd, ivar_index.into(), @@ -2023,81 +2021,82 @@ fn gen_get_ivar( return EndBlock; } - // FIXME: Mapping the index could fail when there is too many ivar names. If we're - // compiling for a branch stub that can cause the exception to be thrown from the - // wrong PC. - let ivar_index = - unsafe { rb_obj_ensure_iv_index_mapping(comptime_receiver, ivar_name) }.as_usize(); + let ivar_index = unsafe { + let shape_id = comptime_receiver.shape_of(); + let shape = rb_shape_get_shape_by_id(shape_id); + let mut ivar_index: u32 = 0; + if rb_shape_get_iv_index(shape, ivar_name, &mut ivar_index) { + Some(ivar_index as usize) + } else { + None + } + }; + + // must be before stack_pop + let recv_type = ctx.get_opnd_type(recv_opnd); + + // Upgrade type + if !recv_type.is_heap() { + ctx.upgrade_opnd_type(recv_opnd, Type::UnknownHeap); + } // Pop receiver if it's on the temp stack if recv_opnd != SelfOpnd { ctx.stack_pop(1); } - if USE_RVARGC != 0 { - // Check that the ivar table is big enough - // Check that the slot is inside the ivar table (num_slots > index) - let num_slots = Opnd::mem(32, recv, ROBJECT_OFFSET_NUMIV); - asm.cmp(num_slots, Opnd::UImm(ivar_index as u64)); - asm.jbe(counted_exit!(ocb, side_exit, getivar_idx_out_of_range).into()); + // Guard heap object + if !recv_type.is_heap() { + guard_object_is_heap(asm, recv, side_exit); } // Compile time self is embedded and the ivar index lands within the object - let test_result = unsafe { FL_TEST_RAW(comptime_receiver, VALUE(ROBJECT_EMBED.as_usize())) != VALUE(0) }; - if test_result { + let embed_test_result = unsafe { FL_TEST_RAW(comptime_receiver, VALUE(ROBJECT_EMBED.as_usize())) != VALUE(0) }; + + let flags_mask: usize = unsafe { rb_shape_flags_mask() }.as_usize(); + let expected_flags_mask: usize = (RUBY_T_MASK as usize) | !flags_mask | (ROBJECT_EMBED as usize); + let expected_flags = comptime_receiver.builtin_flags() & expected_flags_mask; + + // Combined guard for all flags: shape, embeddedness, and T_OBJECT + let flags_opnd = Opnd::mem(64, recv, RUBY_OFFSET_RBASIC_FLAGS); + + asm.comment("guard shape, embedded, and T_OBJECT"); + let flags_opnd = asm.and(flags_opnd, Opnd::UImm(expected_flags_mask as u64)); + asm.cmp(flags_opnd, Opnd::UImm(expected_flags as u64)); + jit_chain_guard( + JCC_JNE, + jit, + &starting_context, + asm, + ocb, + max_chain_depth, + side_exit, + ); + + // If there is no IVAR index, then the ivar was undefined + // when we entered the compiler. That means we can just return + // nil for this shape + iv name + if ivar_index.is_none() { + let out_opnd = ctx.stack_push(Type::Nil); + asm.mov(out_opnd, Qnil.into()); + } else if embed_test_result { // See ROBJECT_IVPTR() from include/ruby/internal/core/robject.h - // Guard that self is embedded - // TODO: BT and JC is shorter - asm.comment("guard embedded getivar"); - let flags_opnd = Opnd::mem(64, recv, RUBY_OFFSET_RBASIC_FLAGS); - asm.test(flags_opnd, Opnd::UImm(ROBJECT_EMBED as u64)); - let side_exit = counted_exit!(ocb, side_exit, getivar_megamorphic); - jit_chain_guard( - JCC_JZ, - jit, - &starting_context, - asm, - ocb, - max_chain_depth, - side_exit, - ); - // Load the variable - let offs = ROBJECT_OFFSET_AS_ARY + (ivar_index * SIZEOF_VALUE) as i32; + let offs = ROBJECT_OFFSET_AS_ARY + (ivar_index.unwrap() * SIZEOF_VALUE) as i32; let ivar_opnd = Opnd::mem(64, recv, offs); - // Guard that the variable is not Qundef - asm.cmp(ivar_opnd, Qundef.into()); - let out_val = asm.csel_e(Qnil.into(), ivar_opnd); - // Push the ivar on the stack let out_opnd = ctx.stack_push(Type::Unknown); - asm.mov(out_opnd, out_val); + asm.mov(out_opnd, ivar_opnd); } else { // Compile time value is *not* embedded. - // Guard that value is *not* embedded - // See ROBJECT_IVPTR() from include/ruby/internal/core/robject.h - asm.comment("guard extended getivar"); - let flags_opnd = Opnd::mem(64, recv, RUBY_OFFSET_RBASIC_FLAGS); - asm.test(flags_opnd, Opnd::UImm(ROBJECT_EMBED as u64)); - let megamorphic_side_exit = counted_exit!(ocb, side_exit, getivar_megamorphic); - jit_chain_guard( - JCC_JNZ, - jit, - &starting_context, - asm, - ocb, - max_chain_depth, - megamorphic_side_exit, - ); - if USE_RVARGC == 0 { // Check that the extended table is big enough // Check that the slot is inside the extended table (num_slots > index) let num_slots = Opnd::mem(32, recv, ROBJECT_OFFSET_NUMIV); - asm.cmp(num_slots, Opnd::UImm(ivar_index as u64)); + asm.cmp(num_slots, Opnd::UImm(ivar_index.unwrap() as u64)); asm.jbe(counted_exit!(ocb, side_exit, getivar_idx_out_of_range).into()); } @@ -2105,15 +2104,10 @@ fn gen_get_ivar( let tbl_opnd = asm.load(Opnd::mem(64, recv, ROBJECT_OFFSET_AS_HEAP_IVPTR)); // Read the ivar from the extended table - let ivar_opnd = Opnd::mem(64, tbl_opnd, (SIZEOF_VALUE * ivar_index) as i32); + let ivar_opnd = Opnd::mem(64, tbl_opnd, (SIZEOF_VALUE * ivar_index.unwrap()) as i32); - // Check that the ivar is not Qundef - asm.cmp(ivar_opnd, Qundef.into()); - let out_val = asm.csel_ne(ivar_opnd, Qnil.into()); - - // Push the ivar on the stack let out_opnd = ctx.stack_push(Type::Unknown); - asm.mov(out_opnd, out_val); + asm.mov(out_opnd, ivar_opnd); } // Jump to next instruction. This allows guard chains to share the same successor. @@ -2136,25 +2130,12 @@ fn gen_getinstancevariable( let ivar_name = jit_get_arg(jit, 0).as_u64(); let comptime_val = jit_peek_at_self(jit); - let comptime_val_klass = comptime_val.class_of(); // Generate a side exit let side_exit = get_side_exit(jit, ocb, ctx); // Guard that the receiver has the same class as the one from compile time. let self_asm_opnd = Opnd::mem(64, CFP, RUBY_OFFSET_CFP_SELF); - jit_guard_known_klass( - jit, - ctx, - asm, - ocb, - comptime_val_klass, - self_asm_opnd, - SelfOpnd, - comptime_val, - GET_IVAR_MAX_DEPTH, - side_exit, - ); gen_get_ivar( jit, diff --git a/yjit/src/cruby.rs b/yjit/src/cruby.rs index c379034a4c..4307937707 100644 --- a/yjit/src/cruby.rs +++ b/yjit/src/cruby.rs @@ -120,7 +120,7 @@ extern "C" { obj: VALUE, v: VALUE, ) -> bool; - pub fn rb_vm_set_ivar_idx(obj: VALUE, idx: u32, val: VALUE) -> VALUE; + pub fn rb_vm_set_ivar_id(obj: VALUE, idx: u32, val: VALUE) -> VALUE; pub fn rb_vm_setinstancevariable(iseq: IseqPtr, obj: VALUE, id: ID, val: VALUE, ic: IVC); pub fn rb_aliased_callable_method_entry( me: *const rb_callable_method_entry_t, @@ -354,18 +354,26 @@ impl VALUE { /// Read the flags bits from the RBasic object, then return a Ruby type enum (e.g. RUBY_T_ARRAY) pub fn builtin_type(self) -> ruby_value_type { + (self.builtin_flags() & (RUBY_T_MASK as usize)) as ruby_value_type + } + + pub fn builtin_flags(self) -> usize { assert!(!self.special_const_p()); let VALUE(cval) = self; let rbasic_ptr = cval as *const RBasic; let flags_bits: usize = unsafe { (*rbasic_ptr).flags }.as_usize(); - (flags_bits & (RUBY_T_MASK as usize)) as ruby_value_type + return flags_bits; } pub fn class_of(self) -> VALUE { unsafe { CLASS_OF(self) } } + pub fn shape_of(self) -> u32 { + unsafe { rb_shape_get_shape_id(self) } + } + pub fn as_isize(self) -> isize { let VALUE(is) = self; is as isize diff --git a/yjit/src/cruby_bindings.inc.rs b/yjit/src/cruby_bindings.inc.rs index 3bffc30731..a00c44f05a 100644 --- a/yjit/src/cruby_bindings.inc.rs +++ b/yjit/src/cruby_bindings.inc.rs @@ -402,6 +402,29 @@ extern "C" { extern "C" { pub fn rb_reg_new_ary(ary: VALUE, options: ::std::os::raw::c_int) -> VALUE; } +pub type attr_index_t = u32; +pub type shape_id_t = u32; +#[repr(C)] +pub struct rb_shape { + pub parent: *mut rb_shape, + pub edges: *mut rb_id_table, + pub edge_name: ID, + pub iv_count: attr_index_t, + pub type_: u8, +} +pub type rb_shape_t = rb_shape; +extern "C" { + pub fn rb_shape_get_shape_by_id(shape_id: shape_id_t) -> *mut rb_shape_t; +} +extern "C" { + pub fn rb_shape_get_shape_id(obj: VALUE) -> shape_id_t; +} +extern "C" { + pub fn rb_shape_get_iv_index(shape: *mut rb_shape_t, id: ID, value: *mut attr_index_t) -> bool; +} +extern "C" { + pub fn rb_shape_flags_mask() -> VALUE; +} pub const idDot2: ruby_method_ids = 128; pub const idDot3: ruby_method_ids = 129; pub const idUPlus: ruby_method_ids = 132; @@ -719,6 +742,11 @@ pub const OPTIMIZED_METHOD_TYPE_STRUCT_AREF: method_optimized_type = 3; pub const OPTIMIZED_METHOD_TYPE_STRUCT_ASET: method_optimized_type = 4; pub const OPTIMIZED_METHOD_TYPE__MAX: method_optimized_type = 5; pub type method_optimized_type = u32; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct rb_id_table { + _unused: [u8; 0], +} extern "C" { pub fn rb_method_entry_at(obj: VALUE, id: ID) -> *const rb_method_entry_t; } @@ -747,9 +775,10 @@ pub struct iseq_inline_constant_cache { pub segments: *const ID, } #[repr(C)] -#[derive(Debug, Copy, Clone)] pub struct iseq_inline_iv_cache_entry { - pub entry: *mut rb_iv_index_tbl_entry, + pub source_shape_id: shape_id_t, + pub dest_shape_id: shape_id_t, + pub attr_index: attr_index_t, } #[repr(C)] #[derive(Debug, Copy, Clone)] @@ -938,12 +967,6 @@ extern "C" { ) -> *const rb_callable_method_entry_t; } #[repr(C)] -pub struct rb_iv_index_tbl_entry { - pub index: u32, - pub class_serial: rb_serial_t, - pub class_value: VALUE, -} -#[repr(C)] pub struct rb_cvar_class_tbl_entry { pub index: u32, pub global_cvar_state: rb_serial_t,