diff --git a/NEWS.md b/NEWS.md index 820a152ff09d5b..2b021e7cddff83 100644 --- a/NEWS.md +++ b/NEWS.md @@ -60,6 +60,7 @@ releases. * typeprof 0.31.1 * debug 1.11.1 * mutex_m 0.3.0 +* resolv-replace 0.2.0 * rdoc 7.1.0 ### RubyGems and Bundler diff --git a/array.rb b/array.rb index 03663dbb0b7ff1..81beff0b1c4016 100644 --- a/array.rb +++ b/array.rb @@ -245,7 +245,8 @@ def map # :nodoc: value = nil result = Primitive.ary_sized_alloc while Primitive.cexpr!(%q{ ary_fetch_next(self, LOCAL_PTR(_i), LOCAL_PTR(value)) }) - result << yield(value) + value = yield(value) + Primitive.cexpr!(%q{ rb_ary_push(result, value) }) end result end @@ -270,7 +271,9 @@ def select # :nodoc: value = nil result = Primitive.ary_sized_alloc while Primitive.cexpr!(%q{ ary_fetch_next(self, LOCAL_PTR(_i), LOCAL_PTR(value)) }) - result << value if yield value + if yield value + Primitive.cexpr!(%q{ rb_ary_push(result, value) }) + end end result end diff --git a/configure.ac b/configure.ac index 90326e2926fc33..4e7367804d3d6b 100644 --- a/configure.ac +++ b/configure.ac @@ -546,7 +546,6 @@ AS_CASE(["$target_os"], ]) rb_cv_binary_elf=no : ${enable_shared=yes} - AS_IF([$WINDRES --version | grep LLVM > /dev/null], [USE_LLVM_WINDRES=yes], [USE_LLVM_WINDRES=no]) ], [hiuxmpp*], [AC_DEFINE(__HIUX_MPP__)]) # by TOYODA Eizi @@ -4414,7 +4413,6 @@ AC_SUBST(MINIOBJS) AC_SUBST(THREAD_MODEL) AC_SUBST(COROUTINE_TYPE, ${coroutine_type}) AC_SUBST(PLATFORM_DIR) -AC_SUBST(USE_LLVM_WINDRES) firstmf=`echo $FIRSTMAKEFILE | sed 's/:.*//'` firsttmpl=`echo $FIRSTMAKEFILE | sed 's/.*://'` diff --git a/cygwin/GNUmakefile.in b/cygwin/GNUmakefile.in index 8e83d73040fe8c..109baa747d9dc9 100644 --- a/cygwin/GNUmakefile.in +++ b/cygwin/GNUmakefile.in @@ -6,14 +6,9 @@ MUNICODE_FLAG := $(if $(filter mingw%,$(target_os)),-municode) override EXE_LDFLAGS += $(MUNICODE_FLAG) DLLWRAP = @DLLWRAP@ --target=$(target_os) --driver-name="$(CC)" -ifeq (@USE_LLVM_WINDRES@,yes) # USE_LLVM_WINDRES - # llvm-windres fails when preprocessor options are added - windres-cpp := -else - windres-cpp := $(CPP) -xc - windres-cpp := --preprocessor=$(firstword $(windres-cpp)) \ - $(addprefix --preprocessor-arg=,$(wordlist 2,$(words $(windres-cpp)),$(windres-cpp))) -endif +windres-cpp := $(CPP) -xc +windres-cpp := --preprocessor=$(firstword $(windres-cpp)) \ + $(addprefix --preprocessor-arg=,$(wordlist 2,$(words $(windres-cpp)),$(windres-cpp))) WINDRES = @WINDRES@ $(windres-cpp) -DRC_INVOKED STRIP = @STRIP@ diff --git a/gems/bundled_gems b/gems/bundled_gems index 1caac2448aa0bf..c8414dee7532cc 100644 --- a/gems/bundled_gems +++ b/gems/bundled_gems @@ -28,7 +28,7 @@ base64 0.3.0 https://github.com/ruby/base64 bigdecimal 4.0.1 https://github.com/ruby/bigdecimal observer 0.1.2 https://github.com/ruby/observer abbrev 0.1.2 https://github.com/ruby/abbrev -resolv-replace 0.1.1 https://github.com/ruby/resolv-replace +resolv-replace 0.2.0 https://github.com/ruby/resolv-replace rinda 0.2.0 https://github.com/ruby/rinda drb 2.2.3 https://github.com/ruby/drb nkf 0.2.0 https://github.com/ruby/nkf diff --git a/insns.def b/insns.def index 7df36726157455..ceeaf4128e9abf 100644 --- a/insns.def +++ b/insns.def @@ -1092,6 +1092,7 @@ invokesuper (VALUE val) // attr rb_snum_t sp_inc = sp_inc_of_sendish(cd->ci); // attr rb_snum_t comptime_sp_inc = sp_inc_of_sendish(ci); +// attr bool zjit_profile = true; { VALUE bh = vm_caller_setup_arg_block(ec, GET_CFP(), cd->ci, blockiseq, true); val = vm_sendish(ec, GET_CFP(), cd, bh, mexp_search_super); diff --git a/lib/bundled_gems.rb b/lib/bundled_gems.rb index 3ac10aa25606d3..85f23b2596bed7 100644 --- a/lib/bundled_gems.rb +++ b/lib/bundled_gems.rb @@ -122,6 +122,10 @@ def self.warning?(name, specs: nil) false end + if suppress_list = Thread.current[:__bundled_gems_warning_suppression] + return if suppress_list.include?(name) || suppress_list.include?(feature) + end + return if specs.include?(name) # Don't warn if a hyphenated gem provides this feature @@ -207,19 +211,28 @@ def self.force_activate(gem) require "bundler" Bundler.reset! + # Build and activate a temporary definition containing the original gems + the requested gem builder = Bundler::Dsl.new - if Bundler::SharedHelpers.in_bundle? - if Bundler.locked_gems - Bundler.locked_gems.specs.each{|spec| builder.gem spec.name, spec.version.to_s } - elsif Bundler.definition.gemfiles.size > 0 - Bundler.definition.gemfiles.each{|gemfile| builder.eval_gemfile(gemfile) } + lockfile = nil + if Bundler::SharedHelpers.in_bundle? && Bundler.definition.gemfiles.size > 0 + Bundler.definition.gemfiles.each {|gemfile| builder.eval_gemfile(gemfile) } + lockfile = begin + Bundler.default_lockfile + rescue Bundler::GemfileNotFound + nil end + else + # Fake BUNDLE_GEMFILE and BUNDLE_LOCKFILE to let checks pass + orig_gemfile = ENV["BUNDLE_GEMFILE"] + orig_lockfile = ENV["BUNDLE_LOCKFILE"] + Bundler::SharedHelpers.set_env "BUNDLE_GEMFILE", "Gemfile" + Bundler::SharedHelpers.set_env "BUNDLE_LOCKFILE", "Gemfile.lock" end builder.gem gem - definition = builder.to_definition(nil, true) + definition = builder.to_definition(lockfile, nil) definition.validate_runtime! begin @@ -235,6 +248,8 @@ def self.force_activate(gem) rescue Bundler::GemNotFound warn "Failed to activate #{gem}, please install it with 'gem install #{gem}'" ensure + ENV['BUNDLE_GEMFILE'] = orig_gemfile if orig_gemfile + ENV['BUNDLE_LOCKFILE'] = orig_lockfile if orig_lockfile Bundler.ui = orig_ui Bundler::Definition.no_lock = orig_no_lock end diff --git a/prelude.rb b/prelude.rb index 7b5a7668982a93..b6c610dd5818dc 100644 --- a/prelude.rb +++ b/prelude.rb @@ -1,6 +1,9 @@ class Binding # :nodoc: def irb(...) + suppress = Thread.current[:__bundled_gems_warning_suppression] + Thread.current[:__bundled_gems_warning_suppression] = ['reline', 'rdoc'] + begin require 'irb' rescue LoadError, Gem::LoadError @@ -8,6 +11,8 @@ def irb(...) require 'irb' end irb(...) + ensure + Thread.current[:__bundled_gems_warning_suppression] = suppress end # suppress redefinition warning diff --git a/proc.c b/proc.c index b3159e14b743d8..5f23e5fed6a6df 100644 --- a/proc.c +++ b/proc.c @@ -2371,29 +2371,29 @@ rb_obj_singleton_method(VALUE obj, VALUE vid) * * Returns an +UnboundMethod+ representing the given * instance method in _mod_. + * See +UnboundMethod+ about how to utilize it * - * class Interpreter - * def do_a() print "there, "; end - * def do_d() print "Hello "; end - * def do_e() print "!\n"; end - * def do_v() print "Dave"; end - * Dispatcher = { - * "a" => instance_method(:do_a), - * "d" => instance_method(:do_d), - * "e" => instance_method(:do_e), - * "v" => instance_method(:do_v) - * } - * def interpret(string) - * string.each_char {|b| Dispatcher[b].bind(self).call } - * end - * end + * class Person + * def initialize(name) + * @name = name + * end + * + * def hi + * puts "Hi, I'm #{@name}!" + * end + * end + * + * dave = Person.new('Dave') + * thomas = Person.new('Thomas') * - * interpreter = Interpreter.new - * interpreter.interpret('dave') + * hi = Person.instance_method(:hi) + * hi.bind_call(dave) + * hi.bind_call(thomas) * * produces: * - * Hello there, Dave! + * Hi, I'm Dave! + * Hi, I'm Thomas! */ static VALUE diff --git a/spec/bundled_gems_spec.rb b/spec/bundled_gems_spec.rb index e86b5ce6e8c0a7..d985ddad412e01 100644 --- a/spec/bundled_gems_spec.rb +++ b/spec/bundled_gems_spec.rb @@ -393,6 +393,30 @@ def my end end + context "with bundler/inline" do + it "foo is available on LOAD_PATH" do + build_lib "foo", "1.0.0" do |s| + s.write "lib/foo.rb", "puts :foo" + end + + script <<-RUBY, env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo1.to_s } + #!/usr/bin/env ruby + gemfile do + source "https://gem.repo1" + path "#{lib_path}" do + gem "foo", "1.0.0" + end + end + + Gem::BUNDLED_GEMS.force_activate("csv") + puts $LOAD_PATH + RUBY + + expect(err).to include("gem install csv") + expect(out).to include("foo-1.0.0/lib") + end + end + context "without bundle environment" do it "warns about installation requirement" do expect_any_instance_of(Object).to receive(:warn) diff --git a/test/ruby/test_array.rb b/test/ruby/test_array.rb index d93b86e7953b1a..04e15b6d87db72 100644 --- a/test/ruby/test_array.rb +++ b/test/ruby/test_array.rb @@ -1336,6 +1336,28 @@ def test_prepend assert_equal(@cls[@cls[1,2], nil, 'dog', 'cat'], a.prepend(@cls[1, 2])) end + def test_tolerant_to_redefinition + *code = __FILE__, __LINE__+1, "#{<<-"{#"}\n#{<<-'};'}" + {# + module M + def <<(a) + super(a * 2) + end + end + class Array; prepend M; end + ary = [*1..10] + mapped = ary.map {|i| i} + selected = ary.select {true} + module M + remove_method :<< + end + assert_equal(ary, mapped) + assert_equal(ary, selected) + }; + assert_separately(%w[--disable-yjit], *code) + assert_separately(%w[--enable-yjit], *code) + end + def test_push a = @cls[1, 2, 3] assert_equal(@cls[1, 2, 3, 4, 5], a.push(4, 5)) diff --git a/test/ruby/test_yjit.rb b/test/ruby/test_yjit.rb index 53b056d3359c85..d6b9b75648e744 100644 --- a/test/ruby/test_yjit.rb +++ b/test/ruby/test_yjit.rb @@ -1804,6 +1804,18 @@ def bar(w:, x:, y:, z:, **kwrest) = yield kwrest RUBY end + def test_yjit_dump_insns + # Testing that this undocumented debugging feature doesn't crash + args = [ + '--yjit-call-threshold=1', + '--yjit-dump-insns', + '-e def foo(case:) = {case:}[:case]', + '-e foo(case:0)', + ] + _out, _err, status = invoke_ruby(args, '', true, true) + assert_not_predicate(status, :signaled?) + end + private def code_gc_helpers diff --git a/test/ruby/test_zjit.rb b/test/ruby/test_zjit.rb index cf3c46b3ed56a7..6e46346d5af983 100644 --- a/test/ruby/test_zjit.rb +++ b/test/ruby/test_zjit.rb @@ -843,6 +843,483 @@ def test } end + def test_invokesuper_to_iseq + assert_compiles '["B", "A"]', %q{ + class A + def foo + "A" + end + end + + class B < A + def foo + ["B", super] + end + end + + def test + B.new.foo + end + + test # profile invokesuper + test # compile + run compiled code + }, call_threshold: 2 + end + + def test_invokesuper_with_args + assert_compiles '["B", 11]', %q{ + class A + def foo(x) + x * 2 + end + end + + class B < A + def foo(x) + ["B", super(x) + 1] + end + end + + def test + B.new.foo(5) + end + + test # profile invokesuper + test # compile + run compiled code + }, call_threshold: 2 + end + + # Test super with explicit args when callee has rest parameter. + # This should fall back to dynamic dispatch since we can't handle rest params yet. + def test_invokesuper_with_args_to_rest_param + assert_compiles '["B", "a", ["b", "c"]]', %q{ + class A + def foo(x, *rest) + [x, rest] + end + end + + class B < A + def foo(x, y, z) + ["B", *super(x, y, z)] + end + end + + def test + B.new.foo("a", "b", "c") + end + + test # profile invokesuper + test # compile + run compiled code + }, call_threshold: 2 + end + + def test_invokesuper_with_block + assert_compiles '["B", "from_block"]', %q{ + class A + def foo + block_given? ? yield : "no_block" + end + end + + class B < A + def foo + ["B", super { "from_block" }] + end + end + + def test + B.new.foo + end + + test # profile invokesuper + test # compile + run compiled code + }, call_threshold: 2 + end + + def test_invokesuper_to_cfunc + assert_compiles '["MyArray", 3]', %q{ + class MyArray < Array + def length + ["MyArray", super] + end + end + + def test + MyArray.new([1, 2, 3]).length + end + + test # profile invokesuper + test # compile + run compiled code + }, call_threshold: 2 + end + + def test_invokesuper_multilevel + assert_compiles '["C", ["B", "A"]]', %q{ + class A + def foo + "A" + end + end + + class B < A + def foo + ["B", super] + end + end + + class C < B + def foo + ["C", super] + end + end + + def test + C.new.foo + end + + test # profile invokesuper + test # compile + run compiled code + }, call_threshold: 2 + end + + # Test implicit block forwarding - super without explicit block should forward caller's block + # Note: We call test twice to ensure ZJIT compiles it before the final call that we check + def test_invokesuper_forwards_block_implicitly + assert_compiles '["B", "forwarded_block"]', %q{ + class A + def foo + block_given? ? yield : "no_block" + end + end + + class B < A + def foo + ["B", super] # should forward the block from caller + end + end + + def test + B.new.foo { "forwarded_block" } + end + + test # profile invokesuper + test # compile + run compiled code + }, call_threshold: 2 + end + + # Test implicit block forwarding with explicit arguments + def test_invokesuper_forwards_block_implicitly_with_args + assert_compiles '["B", ["arg_value", "forwarded"]]', %q{ + class A + def foo(x) + [x, (block_given? ? yield : "no_block")] + end + end + + class B < A + def foo(x) + ["B", super(x)] # explicit args, but block should still be forwarded + end + end + + def test + B.new.foo("arg_value") { "forwarded" } + end + + test # profile + test # compile + run compiled code + }, call_threshold: 2 + end + + # Test implicit block forwarding when no block is given (should not fail) + def test_invokesuper_forwards_block_implicitly_no_block_given + assert_compiles '["B", "no_block"]', %q{ + class A + def foo + block_given? ? yield : "no_block" + end + end + + class B < A + def foo + ["B", super] # no block given by caller + end + end + + def test + B.new.foo # called without a block + end + + test # profile + test # compile + run compiled code + }, call_threshold: 2 + end + + # Test implicit block forwarding through multiple inheritance levels + def test_invokesuper_forwards_block_implicitly_multilevel + assert_compiles '["C", ["B", "deep_block"]]', %q{ + class A + def foo + block_given? ? yield : "no_block" + end + end + + class B < A + def foo + ["B", super] # forwards block to A + end + end + + class C < B + def foo + ["C", super] # forwards block to B, which forwards to A + end + end + + def test + C.new.foo { "deep_block" } + end + + test # profile + test # compile + run compiled code + }, call_threshold: 2 + end + + # Test implicit block forwarding with block parameter syntax + def test_invokesuper_forwards_block_param + assert_compiles '["B", "block_param_forwarded"]', %q{ + class A + def foo + block_given? ? yield : "no_block" + end + end + + class B < A + def foo(&block) + ["B", super] # should forward &block implicitly + end + end + + def test + B.new.foo { "block_param_forwarded" } + end + + test # profile + test # compile + run compiled code + }, call_threshold: 2 + end + + def test_invokesuper_with_blockarg + assert_compiles '["B", "different block"]', %q{ + class A + def foo + block_given? ? yield : "no block" + end + end + + class B < A + def foo(&blk) + other_block = proc { "different block" } + ["B", super(&other_block)] + end + end + + def test + B.new.foo { "passed block" } + end + + test # profile + test # compile + run compiled code + }, call_threshold: 2 + end + + def test_invokesuper_with_symbol_to_proc + assert_compiles '["B", [3, 5, 7]]', %q{ + class A + def foo(items, &blk) + items.map(&blk) + end + end + + class B < A + def foo(items) + ["B", super(items, &:succ)] + end + end + + def test + B.new.foo([2, 4, 6]) + end + + test # profile + test # compile + run compiled code + }, call_threshold: 2 + end + + def test_invokesuper_with_splat + assert_compiles '["B", 6]', %q{ + class A + def foo(a, b, c) + a + b + c + end + end + + class B < A + def foo(*args) + ["B", super(*args)] + end + end + + def test + B.new.foo(1, 2, 3) + end + + test # profile + test # compile + run compiled code + }, call_threshold: 2 + end + + def test_invokesuper_with_kwargs + assert_compiles '["B", "x=1, y=2"]', %q{ + class A + def foo(x:, y:) + "x=#{x}, y=#{y}" + end + end + + class B < A + def foo(x:, y:) + ["B", super(x: x, y: y)] + end + end + + def test + B.new.foo(x: 1, y: 2) + end + + test # profile + test # compile + run compiled code + }, call_threshold: 2 + end + + def test_invokesuper_with_kw_splat + assert_compiles '["B", "x=1, y=2"]', %q{ + class A + def foo(x:, y:) + "x=#{x}, y=#{y}" + end + end + + class B < A + def foo(**kwargs) + ["B", super(**kwargs)] + end + end + + def test + B.new.foo(x: 1, y: 2) + end + + test # profile + test # compile + run compiled code + }, call_threshold: 2 + end + + # Test that including a module after compilation correctly changes the super target. + # The included module's method should be called, not the original super target. + def test_invokesuper_with_include + assert_compiles '["B", "M"]', %q{ + class A + def foo + "A" + end + end + + class B < A + def foo + ["B", super] + end + end + + def test + B.new.foo + end + + test # profile invokesuper (super -> A#foo) + test # compile with super -> A#foo + + # Now include a module in B that defines foo - super should go to M#foo instead + module M + def foo + "M" + end + end + B.include(M) + + test # should call M#foo, not A#foo + }, call_threshold: 2 + end + + # Test that prepending a module after compilation correctly changes the super target. + # The prepended module's method should be called, not the original super target. + def test_invokesuper_with_prepend + assert_compiles '["B", "M"]', %q{ + class A + def foo + "A" + end + end + + class B < A + def foo + ["B", super] + end + end + + def test + B.new.foo + end + + test # profile invokesuper (super -> A#foo) + test # compile with super -> A#foo + + # Now prepend a module that defines foo - super should go to M#foo instead + module M + def foo + "M" + end + end + A.prepend(M) + + test # should call M#foo, not A#foo + }, call_threshold: 2 + end + + # Test super with positional and keyword arguments (pattern from chunky_png) + def test_invokesuper_with_keyword_args + assert_compiles '{content: "image data"}', %q{ + class A + def foo(attributes = {}) + @attributes = attributes + end + end + + class B < A + def foo(content = '') + super(content: content) + end + end + + def test + B.new.foo("image data") + end + + test + test + }, call_threshold: 2 + end + def test_invokebuiltin # Not using assert_compiles due to register spill assert_runs '["."]', %q{ @@ -1683,6 +2160,54 @@ def test(x) = [1,2,3][x] }, call_threshold: 2, insns: [:opt_aref] end + def test_array_fixnum_aref_negative_index + assert_compiles '3', %q{ + def test(x) = [1,2,3][x] + test(-1) + test(-1) + }, call_threshold: 2, insns: [:opt_aref] + end + + def test_array_fixnum_aref_out_of_bounds_positive + assert_compiles 'nil', %q{ + def test(x) = [1,2,3][x] + test(10) + test(10) + }, call_threshold: 2, insns: [:opt_aref] + end + + def test_array_fixnum_aref_out_of_bounds_negative + assert_compiles 'nil', %q{ + def test(x) = [1,2,3][x] + test(-10) + test(-10) + }, call_threshold: 2, insns: [:opt_aref] + end + + def test_array_fixnum_aref_array_subclass + assert_compiles '3', %q{ + class MyArray < Array; end + def test(arr, idx) = arr[idx] + arr = MyArray[1,2,3] + test(arr, 2) + arr = MyArray[1,2,3] + test(arr, 2) + }, call_threshold: 2, insns: [:opt_aref] + end + + def test_array_aref_non_fixnum_index + assert_compiles 'TypeError', %q{ + def test(arr, idx) = arr[idx] + test([1,2,3], 1) + test([1,2,3], 1) + begin + test([1,2,3], "1") + rescue => e + e.class + end + }, call_threshold: 2 + end + def test_array_fixnum_aset assert_compiles '[1, 2, 7]', %q{ def test(arr, idx) diff --git a/tool/lib/core_assertions.rb b/tool/lib/core_assertions.rb index 419704448ba2fa..e29a0e3c25a0e3 100644 --- a/tool/lib/core_assertions.rb +++ b/tool/lib/core_assertions.rb @@ -384,11 +384,10 @@ def assert_separately(args, file = nil, line = nil, src, ignore_stderr: nil, **o end raise if $! abort = status.coredump? || (status.signaled? && ABORT_SIGNALS.include?(status.termsig)) - assertions = 0 marshal_error = nil assert(!abort, FailDesc[status, nil, stderr]) res.scan(/^\n(.*?)\n(?=<\/error id="#{token_re}">$)/m) do - assertions += $1.to_i + self._assertions += $1.to_i res = Marshal.load($2.unpack1("m")) or next rescue => marshal_error ignore_stderr = nil diff --git a/tool/test/testunit/test_assertion.rb b/tool/test/testunit/test_assertion.rb index 7b1f28a8698b15..d9bdc8f3c55394 100644 --- a/tool/test/testunit/test_assertion.rb +++ b/tool/test/testunit/test_assertion.rb @@ -17,6 +17,29 @@ def test_timeout_separately end end + def test_assertion_count_separately + beginning = self._assertions + + assert_separately([], "") + assertions_at_nothing = self._assertions - beginning + + prev_assertions = self._assertions + assertions_at_nothing + assert_separately([], "assert true") + assert_equal(1, self._assertions - prev_assertions) + + omit unless Process.respond_to?(:fork) + prev_assertions = self._assertions + assertions_at_nothing + assert_separately([], "Process.fork {assert true}; assert true") + assert_equal(2, self._assertions - prev_assertions) + + prev_assertions = self._assertions + assertions_at_nothing + # TODO: assertions before `fork` are counted twice; it is possible + # to reset `_assertions` at `Process._fork`, but the hook can + # interfere in other tests. + assert_separately([], "assert true; Process.fork {assert true}") + assert_equal(3, self._assertions - prev_assertions) + end + def return_in_assert_raise assert_raise(RuntimeError) do return diff --git a/weakmap.c b/weakmap.c index b027604f5e83fe..80ef29b4cce8bc 100644 --- a/weakmap.c +++ b/weakmap.c @@ -207,6 +207,17 @@ wmap_inspect_i(st_data_t k, st_data_t v, st_data_t data) return ST_CONTINUE; } +/* call-seq: + * inspect -> new_string + * + * Returns a new string containing the \WeakMap entries: + * + * m = ObjectSpace::WeakMap.new + * m["one"] = 1 + * m["two"] = 2 + * m.inspect + * # => "# => 1, # => 2>" + */ static VALUE wmap_inspect(VALUE self) { diff --git a/yjit/src/backend/arm64/mod.rs b/yjit/src/backend/arm64/mod.rs index 6fa8fef627df0c..4486b46e363efa 100644 --- a/yjit/src/backend/arm64/mod.rs +++ b/yjit/src/backend/arm64/mod.rs @@ -1155,8 +1155,8 @@ impl Assembler let regs = Assembler::get_caller_save_regs(); // Pop the state/flags register - msr(cb, SystemRegister::NZCV, Self::SCRATCH0); emit_pop(cb, Self::SCRATCH0); + msr(cb, SystemRegister::NZCV, Self::SCRATCH0); for reg in regs.into_iter().rev() { emit_pop(cb, A64Opnd::Reg(reg)); @@ -1419,7 +1419,7 @@ mod tests { fn test_emit_cpop_all() { let (mut asm, mut cb) = setup_asm(); - asm.cpop_all(); + asm.cpop_all(crate::core::RegMapping::default()); asm.compile_with_num_regs(&mut cb, 0); } diff --git a/yjit/src/backend/ir.rs b/yjit/src/backend/ir.rs index 8205d6de76bd2f..3fb67bc7cc1584 100644 --- a/yjit/src/backend/ir.rs +++ b/yjit/src/backend/ir.rs @@ -1824,12 +1824,12 @@ impl Assembler { out } - pub fn cpop_all(&mut self) { + pub fn cpop_all(&mut self, reg_mapping: RegMapping) { self.push_insn(Insn::CPopAll); // Re-enable ccall's RegMappings assertion disabled by cpush_all. // cpush_all + cpop_all preserve all stack temp registers, so it's safe. - self.set_reg_mapping(self.ctx.get_reg_mapping()); + self.set_reg_mapping(reg_mapping); } pub fn cpop_into(&mut self, opnd: Opnd) { @@ -1840,14 +1840,16 @@ impl Assembler { self.push_insn(Insn::CPush(opnd)); } - pub fn cpush_all(&mut self) { + pub fn cpush_all(&mut self) -> RegMapping { self.push_insn(Insn::CPushAll); // Mark all temps as not being in registers. // Temps will be marked back as being in registers by cpop_all. // We assume that cpush_all + cpop_all are used for C functions in utils.rs // that don't require spill_regs for GC. + let mapping = self.ctx.get_reg_mapping(); self.set_reg_mapping(RegMapping::default()); + mapping } pub fn cret(&mut self, opnd: Opnd) { diff --git a/yjit/src/codegen.rs b/yjit/src/codegen.rs index 1cf8247a2a905a..0fbca85716dff0 100644 --- a/yjit/src/codegen.rs +++ b/yjit/src/codegen.rs @@ -8314,7 +8314,7 @@ fn gen_send_iseq( // We also do this after spill_regs() to avoid doubly spilling the same thing on asm.ccall(). if get_option!(gen_stats) { // Protect caller-saved registers in case they're used for arguments - asm.cpush_all(); + let mapping = asm.cpush_all(); // Assemble the ISEQ name string let name_str = get_iseq_name(iseq); @@ -8324,7 +8324,7 @@ fn gen_send_iseq( // Increment the counter for this cfunc asm.ccall(incr_iseq_counter as *const u8, vec![iseq_idx.into()]); - asm.cpop_all(); + asm.cpop_all(mapping); } // The callee might change locals through Kernel#binding and other means. diff --git a/yjit/src/cruby_bindings.inc.rs b/yjit/src/cruby_bindings.inc.rs index 61dbf9b5c35929..9fbcf2169f8779 100644 --- a/yjit/src/cruby_bindings.inc.rs +++ b/yjit/src/cruby_bindings.inc.rs @@ -924,30 +924,31 @@ pub const YARVINSN_zjit_send: ruby_vminsn_type = 221; pub const YARVINSN_zjit_opt_send_without_block: ruby_vminsn_type = 222; pub const YARVINSN_zjit_objtostring: ruby_vminsn_type = 223; pub const YARVINSN_zjit_opt_nil_p: ruby_vminsn_type = 224; -pub const YARVINSN_zjit_invokeblock: ruby_vminsn_type = 225; -pub const YARVINSN_zjit_opt_plus: ruby_vminsn_type = 226; -pub const YARVINSN_zjit_opt_minus: ruby_vminsn_type = 227; -pub const YARVINSN_zjit_opt_mult: ruby_vminsn_type = 228; -pub const YARVINSN_zjit_opt_div: ruby_vminsn_type = 229; -pub const YARVINSN_zjit_opt_mod: ruby_vminsn_type = 230; -pub const YARVINSN_zjit_opt_eq: ruby_vminsn_type = 231; -pub const YARVINSN_zjit_opt_neq: ruby_vminsn_type = 232; -pub const YARVINSN_zjit_opt_lt: ruby_vminsn_type = 233; -pub const YARVINSN_zjit_opt_le: ruby_vminsn_type = 234; -pub const YARVINSN_zjit_opt_gt: ruby_vminsn_type = 235; -pub const YARVINSN_zjit_opt_ge: ruby_vminsn_type = 236; -pub const YARVINSN_zjit_opt_ltlt: ruby_vminsn_type = 237; -pub const YARVINSN_zjit_opt_and: ruby_vminsn_type = 238; -pub const YARVINSN_zjit_opt_or: ruby_vminsn_type = 239; -pub const YARVINSN_zjit_opt_aref: ruby_vminsn_type = 240; -pub const YARVINSN_zjit_opt_aset: ruby_vminsn_type = 241; -pub const YARVINSN_zjit_opt_length: ruby_vminsn_type = 242; -pub const YARVINSN_zjit_opt_size: ruby_vminsn_type = 243; -pub const YARVINSN_zjit_opt_empty_p: ruby_vminsn_type = 244; -pub const YARVINSN_zjit_opt_succ: ruby_vminsn_type = 245; -pub const YARVINSN_zjit_opt_not: ruby_vminsn_type = 246; -pub const YARVINSN_zjit_opt_regexpmatch2: ruby_vminsn_type = 247; -pub const VM_INSTRUCTION_SIZE: ruby_vminsn_type = 248; +pub const YARVINSN_zjit_invokesuper: ruby_vminsn_type = 225; +pub const YARVINSN_zjit_invokeblock: ruby_vminsn_type = 226; +pub const YARVINSN_zjit_opt_plus: ruby_vminsn_type = 227; +pub const YARVINSN_zjit_opt_minus: ruby_vminsn_type = 228; +pub const YARVINSN_zjit_opt_mult: ruby_vminsn_type = 229; +pub const YARVINSN_zjit_opt_div: ruby_vminsn_type = 230; +pub const YARVINSN_zjit_opt_mod: ruby_vminsn_type = 231; +pub const YARVINSN_zjit_opt_eq: ruby_vminsn_type = 232; +pub const YARVINSN_zjit_opt_neq: ruby_vminsn_type = 233; +pub const YARVINSN_zjit_opt_lt: ruby_vminsn_type = 234; +pub const YARVINSN_zjit_opt_le: ruby_vminsn_type = 235; +pub const YARVINSN_zjit_opt_gt: ruby_vminsn_type = 236; +pub const YARVINSN_zjit_opt_ge: ruby_vminsn_type = 237; +pub const YARVINSN_zjit_opt_ltlt: ruby_vminsn_type = 238; +pub const YARVINSN_zjit_opt_and: ruby_vminsn_type = 239; +pub const YARVINSN_zjit_opt_or: ruby_vminsn_type = 240; +pub const YARVINSN_zjit_opt_aref: ruby_vminsn_type = 241; +pub const YARVINSN_zjit_opt_aset: ruby_vminsn_type = 242; +pub const YARVINSN_zjit_opt_length: ruby_vminsn_type = 243; +pub const YARVINSN_zjit_opt_size: ruby_vminsn_type = 244; +pub const YARVINSN_zjit_opt_empty_p: ruby_vminsn_type = 245; +pub const YARVINSN_zjit_opt_succ: ruby_vminsn_type = 246; +pub const YARVINSN_zjit_opt_not: ruby_vminsn_type = 247; +pub const YARVINSN_zjit_opt_regexpmatch2: ruby_vminsn_type = 248; +pub const VM_INSTRUCTION_SIZE: ruby_vminsn_type = 249; pub type ruby_vminsn_type = u32; pub type rb_iseq_callback = ::std::option::Option< unsafe extern "C" fn(arg1: *const rb_iseq_t, arg2: *mut ::std::os::raw::c_void), diff --git a/yjit/src/utils.rs b/yjit/src/utils.rs index 43fd90142f6e5e..251628fabfae69 100644 --- a/yjit/src/utils.rs +++ b/yjit/src/utils.rs @@ -160,8 +160,6 @@ pub fn print_int(asm: &mut Assembler, opnd: Opnd) { } } - asm.cpush_all(); - let argument = match opnd { Opnd::Mem(_) | Opnd::Reg(_) | Opnd::InsnOut { .. } => { // Sign-extend the value if necessary @@ -176,7 +174,6 @@ pub fn print_int(asm: &mut Assembler, opnd: Opnd) { }; asm.ccall(print_int_fn as *const u8, vec![argument]); - asm.cpop_all(); } /// Generate code to print a pointer @@ -189,9 +186,7 @@ pub fn print_ptr(asm: &mut Assembler, opnd: Opnd) { assert!(opnd.rm_num_bits() == 64); - asm.cpush_all(); asm.ccall(print_ptr_fn as *const u8, vec![opnd]); - asm.cpop_all(); } /// Generate code to print a value @@ -204,9 +199,7 @@ pub fn print_value(asm: &mut Assembler, opnd: Opnd) { assert!(matches!(opnd, Opnd::Value(_))); - asm.cpush_all(); asm.ccall(print_value_fn as *const u8, vec![opnd]); - asm.cpop_all(); } /// Generate code to print constant string to stdout @@ -221,7 +214,6 @@ pub fn print_str(asm: &mut Assembler, str: &str) { } } - asm.cpush_all(); let string_data = asm.new_label("string_data"); let after_string = asm.new_label("after_string"); @@ -233,8 +225,6 @@ pub fn print_str(asm: &mut Assembler, str: &str) { let opnd = asm.lea_jump_target(string_data); asm.ccall(print_str_cfun as *const u8, vec![opnd, Opnd::UImm(str.len() as u64)]); - - asm.cpop_all(); } pub fn stdout_supports_colors() -> bool { diff --git a/zjit.rb b/zjit.rb index e2aa55f764eb9a..0bd6c1b96d36d0 100644 --- a/zjit.rb +++ b/zjit.rb @@ -184,6 +184,7 @@ def stats_string # Show fallback counters, ordered by the typical amount of fallbacks for the prefix at the time print_counters_with_prefix(prefix: 'unspecialized_send_def_type_', prompt: 'not optimized method types for send', buf:, stats:, limit: 20) print_counters_with_prefix(prefix: 'unspecialized_send_without_block_def_type_', prompt: 'not optimized method types for send_without_block', buf:, stats:, limit: 20) + print_counters_with_prefix(prefix: 'unspecialized_super_def_type_', prompt: 'not optimized method types for super', buf:, stats:, limit: 20) print_counters_with_prefix(prefix: 'uncategorized_fallback_yarv_insn_', prompt: 'instructions with uncategorized fallback reason', buf:, stats:, limit: 20) print_counters_with_prefix(prefix: 'send_fallback_', prompt: 'send fallback reasons', buf:, stats:, limit: 20) print_counters_with_prefix(prefix: 'setivar_fallback_', prompt: 'setivar fallback reasons', buf:, stats:, limit: 5) diff --git a/zjit/src/codegen.rs b/zjit/src/codegen.rs index 4a186d960c062c..1c453aed771055 100644 --- a/zjit/src/codegen.rs +++ b/zjit/src/codegen.rs @@ -15,7 +15,7 @@ use crate::invariants::{ use crate::gc::append_gc_offsets; use crate::payload::{get_or_create_iseq_payload, IseqCodePtrs, IseqVersion, IseqVersionRef, IseqStatus}; use crate::state::ZJITState; -use crate::stats::{CompileError, exit_counter_for_compile_error, exit_counter_for_unhandled_hir_insn, incr_counter, incr_counter_by, send_fallback_counter, send_fallback_counter_for_method_type, send_fallback_counter_ptr_for_opcode, send_without_block_fallback_counter_for_method_type, send_without_block_fallback_counter_for_optimized_method_type}; +use crate::stats::{CompileError, exit_counter_for_compile_error, exit_counter_for_unhandled_hir_insn, incr_counter, incr_counter_by, send_fallback_counter, send_fallback_counter_for_method_type, send_fallback_counter_for_super_method_type, send_fallback_counter_ptr_for_opcode, send_without_block_fallback_counter_for_method_type, send_without_block_fallback_counter_for_optimized_method_type}; use crate::stats::{counter_ptr, with_time_stat, Counter, Counter::{compile_time_ns, exit_compile_error}}; use crate::{asm::CodeBlock, cruby::*, options::debug, virtualmem::CodePtr}; use crate::backend::lir::{self, Assembler, C_ARG_OPNDS, C_RET_OPND, CFP, EC, NATIVE_BASE_PTR, NATIVE_STACK_PTR, Opnd, SP, SideExit, Target, asm_ccall, asm_comment}; @@ -374,7 +374,7 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio Insn::NewRange { low, high, flag, state } => gen_new_range(jit, asm, opnd!(low), opnd!(high), *flag, &function.frame_state(*state)), Insn::NewRangeFixnum { low, high, flag, state } => gen_new_range_fixnum(asm, opnd!(low), opnd!(high), *flag, &function.frame_state(*state)), Insn::ArrayDup { val, state } => gen_array_dup(asm, opnd!(val), &function.frame_state(*state)), - Insn::ArrayArefFixnum { array, index, .. } => gen_aref_fixnum(asm, opnd!(array), opnd!(index)), + Insn::ArrayAref { array, index, .. } => gen_array_aref(asm, opnd!(array), opnd!(index)), Insn::ArrayAset { array, index, val } => { no_output!(gen_array_aset(asm, opnd!(array), opnd!(index), opnd!(val))) } @@ -401,7 +401,7 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio &Insn::Send { cd, blockiseq, state, reason, .. } => gen_send(jit, asm, cd, blockiseq, &function.frame_state(state), reason), &Insn::SendForward { cd, blockiseq, state, reason, .. } => gen_send_forward(jit, asm, cd, blockiseq, &function.frame_state(state), reason), &Insn::SendWithoutBlock { cd, state, reason, .. } => gen_send_without_block(jit, asm, cd, &function.frame_state(state), reason), - Insn::SendWithoutBlockDirect { cme, iseq, recv, args, state, .. } => gen_send_without_block_direct(cb, jit, asm, *cme, *iseq, opnd!(recv), opnds!(args), &function.frame_state(*state)), + Insn::SendWithoutBlockDirect { cme, iseq, recv, args, state, .. } => gen_send_iseq_direct(cb, jit, asm, *cme, *iseq, opnd!(recv), opnds!(args), &function.frame_state(*state), None), &Insn::InvokeSuper { cd, blockiseq, state, reason, .. } => gen_invokesuper(jit, asm, cd, blockiseq, &function.frame_state(state), reason), &Insn::InvokeBlock { cd, state, reason, .. } => gen_invokeblock(jit, asm, cd, &function.frame_state(state), reason), // Ensure we have enough room fit ec, self, and arguments @@ -453,6 +453,8 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio Insn::GuardNotShared { recv, state } => gen_guard_not_shared(jit, asm, opnd!(recv), &function.frame_state(*state)), &Insn::GuardLess { left, right, state } => gen_guard_less(jit, asm, opnd!(left), opnd!(right), &function.frame_state(state)), &Insn::GuardGreaterEq { left, right, state } => gen_guard_greater_eq(jit, asm, opnd!(left), opnd!(right), &function.frame_state(state)), + &Insn::GuardSuperMethodEntry { cme, state } => no_output!(gen_guard_super_method_entry(jit, asm, cme, &function.frame_state(state))), + Insn::GetBlockHandler => gen_get_block_handler(jit, asm), Insn::PatchPoint { invariant, state } => no_output!(gen_patch_point(jit, asm, invariant, &function.frame_state(*state))), Insn::CCall { cfunc, recv, args, name, return_type: _, elidable: _ } => gen_ccall(asm, *cfunc, *name, opnd!(recv), opnds!(args)), // Give up CCallWithFrame for 7+ args since asm.ccall() supports at most 6 args (recv + args). @@ -715,6 +717,29 @@ fn gen_guard_greater_eq(jit: &JITState, asm: &mut Assembler, left: Opnd, right: left } +/// Guard that the method entry at ep[VM_ENV_DATA_INDEX_ME_CREF] matches the expected CME. +/// This ensures we're calling super from the expected method context. +fn gen_guard_super_method_entry( + jit: &JITState, + asm: &mut Assembler, + cme: *const rb_callable_method_entry_t, + state: &FrameState, +) { + asm_comment!(asm, "guard super method entry"); + let lep = gen_get_lep(jit, asm); + let ep_me_opnd = Opnd::mem(64, lep, SIZEOF_VALUE_I32 * VM_ENV_DATA_INDEX_ME_CREF); + let ep_me = asm.load(ep_me_opnd); + asm.cmp(ep_me, Opnd::UImm(cme as u64)); + asm.jne(side_exit(jit, state, SideExitReason::GuardSuperMethodEntry)); +} + +/// Get the block handler from ep[VM_ENV_DATA_INDEX_SPECVAL] at the local EP (LEP). +fn gen_get_block_handler(jit: &JITState, asm: &mut Assembler) -> Opnd { + asm_comment!(asm, "get block handler from LEP"); + let lep = gen_get_lep(jit, asm); + asm.load(Opnd::mem(64, lep, SIZEOF_VALUE_I32 * VM_ENV_DATA_INDEX_SPECVAL)) +} + fn gen_get_constant_path(jit: &JITState, asm: &mut Assembler, ic: *const iseq_inline_constant_cache, state: &FrameState) -> Opnd { unsafe extern "C" { fn rb_vm_opt_getconstant_path(ec: EcPtr, cfp: CfpPtr, ic: *const iseq_inline_constant_cache) -> VALUE; @@ -1321,8 +1346,10 @@ fn gen_send_without_block( ) } -/// Compile a direct jump to an ISEQ call without block -fn gen_send_without_block_direct( +/// Compile a direct call to an ISEQ method. +/// If `block_handler` is provided, it's used as the specval for the new frame (for forwarding blocks). +/// Otherwise, `VM_BLOCK_HANDLER_NONE` is used. +fn gen_send_iseq_direct( cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, @@ -1331,6 +1358,7 @@ fn gen_send_without_block_direct( recv: Opnd, args: Vec, state: &FrameState, + block_handler: Option, ) -> lir::Opnd { gen_incr_counter(asm, Counter::iseq_optimized_send_count); @@ -1357,7 +1385,8 @@ fn gen_send_without_block_direct( let bmethod_specval = (capture.ep.addr() | 1).into(); (bmethod_frame_type, bmethod_specval) } else { - (VM_FRAME_MAGIC_METHOD | VM_ENV_FLAG_LOCAL, VM_BLOCK_HANDLER_NONE.into()) + let specval = block_handler.unwrap_or_else(|| VM_BLOCK_HANDLER_NONE.into()); + (VM_FRAME_MAGIC_METHOD | VM_ENV_FLAG_LOCAL, specval) }; // Set up the new frame @@ -1531,13 +1560,17 @@ fn gen_new_array( } /// Compile array access (`array[index]`) -fn gen_aref_fixnum( +fn gen_array_aref( asm: &mut Assembler, array: Opnd, index: Opnd, ) -> lir::Opnd { - let unboxed_idx = asm.rshift(index, Opnd::UImm(1)); - asm_ccall!(asm, rb_ary_entry, array, unboxed_idx) + let unboxed_idx = asm.load(index); + let array = asm.load(array); + let array_ptr = gen_array_ptr(asm, array); + let elem_offset = asm.lshift(unboxed_idx, Opnd::UImm(SIZEOF_VALUE.trailing_zeros() as u64)); + let elem_ptr = asm.add(array_ptr, elem_offset); + asm.load(Opnd::mem(VALUE_BITS, elem_ptr, 0)) } fn gen_array_aset( @@ -2131,6 +2164,9 @@ fn gen_incr_send_fallback_counter(asm: &mut Assembler, reason: SendFallbackReaso SendNotOptimizedMethodType(method_type) => { gen_incr_counter(asm, send_fallback_counter_for_method_type(method_type)); } + SuperNotOptimizedMethodType(method_type) => { + gen_incr_counter(asm, send_fallback_counter_for_super_method_type(method_type)); + } _ => {} } } diff --git a/zjit/src/cruby_bindings.inc.rs b/zjit/src/cruby_bindings.inc.rs index 5d4fed0c3ac18d..2201bdcffee776 100644 --- a/zjit/src/cruby_bindings.inc.rs +++ b/zjit/src/cruby_bindings.inc.rs @@ -1713,30 +1713,31 @@ pub const YARVINSN_zjit_send: ruby_vminsn_type = 221; pub const YARVINSN_zjit_opt_send_without_block: ruby_vminsn_type = 222; pub const YARVINSN_zjit_objtostring: ruby_vminsn_type = 223; pub const YARVINSN_zjit_opt_nil_p: ruby_vminsn_type = 224; -pub const YARVINSN_zjit_invokeblock: ruby_vminsn_type = 225; -pub const YARVINSN_zjit_opt_plus: ruby_vminsn_type = 226; -pub const YARVINSN_zjit_opt_minus: ruby_vminsn_type = 227; -pub const YARVINSN_zjit_opt_mult: ruby_vminsn_type = 228; -pub const YARVINSN_zjit_opt_div: ruby_vminsn_type = 229; -pub const YARVINSN_zjit_opt_mod: ruby_vminsn_type = 230; -pub const YARVINSN_zjit_opt_eq: ruby_vminsn_type = 231; -pub const YARVINSN_zjit_opt_neq: ruby_vminsn_type = 232; -pub const YARVINSN_zjit_opt_lt: ruby_vminsn_type = 233; -pub const YARVINSN_zjit_opt_le: ruby_vminsn_type = 234; -pub const YARVINSN_zjit_opt_gt: ruby_vminsn_type = 235; -pub const YARVINSN_zjit_opt_ge: ruby_vminsn_type = 236; -pub const YARVINSN_zjit_opt_ltlt: ruby_vminsn_type = 237; -pub const YARVINSN_zjit_opt_and: ruby_vminsn_type = 238; -pub const YARVINSN_zjit_opt_or: ruby_vminsn_type = 239; -pub const YARVINSN_zjit_opt_aref: ruby_vminsn_type = 240; -pub const YARVINSN_zjit_opt_aset: ruby_vminsn_type = 241; -pub const YARVINSN_zjit_opt_length: ruby_vminsn_type = 242; -pub const YARVINSN_zjit_opt_size: ruby_vminsn_type = 243; -pub const YARVINSN_zjit_opt_empty_p: ruby_vminsn_type = 244; -pub const YARVINSN_zjit_opt_succ: ruby_vminsn_type = 245; -pub const YARVINSN_zjit_opt_not: ruby_vminsn_type = 246; -pub const YARVINSN_zjit_opt_regexpmatch2: ruby_vminsn_type = 247; -pub const VM_INSTRUCTION_SIZE: ruby_vminsn_type = 248; +pub const YARVINSN_zjit_invokesuper: ruby_vminsn_type = 225; +pub const YARVINSN_zjit_invokeblock: ruby_vminsn_type = 226; +pub const YARVINSN_zjit_opt_plus: ruby_vminsn_type = 227; +pub const YARVINSN_zjit_opt_minus: ruby_vminsn_type = 228; +pub const YARVINSN_zjit_opt_mult: ruby_vminsn_type = 229; +pub const YARVINSN_zjit_opt_div: ruby_vminsn_type = 230; +pub const YARVINSN_zjit_opt_mod: ruby_vminsn_type = 231; +pub const YARVINSN_zjit_opt_eq: ruby_vminsn_type = 232; +pub const YARVINSN_zjit_opt_neq: ruby_vminsn_type = 233; +pub const YARVINSN_zjit_opt_lt: ruby_vminsn_type = 234; +pub const YARVINSN_zjit_opt_le: ruby_vminsn_type = 235; +pub const YARVINSN_zjit_opt_gt: ruby_vminsn_type = 236; +pub const YARVINSN_zjit_opt_ge: ruby_vminsn_type = 237; +pub const YARVINSN_zjit_opt_ltlt: ruby_vminsn_type = 238; +pub const YARVINSN_zjit_opt_and: ruby_vminsn_type = 239; +pub const YARVINSN_zjit_opt_or: ruby_vminsn_type = 240; +pub const YARVINSN_zjit_opt_aref: ruby_vminsn_type = 241; +pub const YARVINSN_zjit_opt_aset: ruby_vminsn_type = 242; +pub const YARVINSN_zjit_opt_length: ruby_vminsn_type = 243; +pub const YARVINSN_zjit_opt_size: ruby_vminsn_type = 244; +pub const YARVINSN_zjit_opt_empty_p: ruby_vminsn_type = 245; +pub const YARVINSN_zjit_opt_succ: ruby_vminsn_type = 246; +pub const YARVINSN_zjit_opt_not: ruby_vminsn_type = 247; +pub const YARVINSN_zjit_opt_regexpmatch2: ruby_vminsn_type = 248; +pub const VM_INSTRUCTION_SIZE: ruby_vminsn_type = 249; pub type ruby_vminsn_type = u32; pub type rb_iseq_callback = ::std::option::Option< unsafe extern "C" fn(arg1: *const rb_iseq_t, arg2: *mut ::std::os::raw::c_void), diff --git a/zjit/src/cruby_methods.rs b/zjit/src/cruby_methods.rs index b8082024728acc..357c8b0c122b8d 100644 --- a/zjit/src/cruby_methods.rs +++ b/zjit/src/cruby_methods.rs @@ -326,7 +326,12 @@ fn inline_array_aref(fun: &mut hir::Function, block: hir::BlockId, recv: hir::In if let &[index] = args { if fun.likely_a(index, types::Fixnum, state) { let index = fun.coerce_to(block, index, types::Fixnum, state); - let result = fun.push_insn(block, hir::Insn::ArrayArefFixnum { array: recv, index }); + let index = fun.push_insn(block, hir::Insn::UnboxFixnum { val: index }); + let length = fun.push_insn(block, hir::Insn::ArrayLength { array: recv }); + let index = fun.push_insn(block, hir::Insn::GuardLess { left: index, right: length, state }); + let zero = fun.push_insn(block, hir::Insn::Const { val: hir::Const::CInt64(0) }); + let index = fun.push_insn(block, hir::Insn::GuardGreaterEq { left: index, right: zero, state }); + let result = fun.push_insn(block, hir::Insn::ArrayAref { array: recv, index }); return Some(result); } } @@ -857,6 +862,10 @@ fn inline_kernel_respond_to_p( } (_, _) => return None, // not public and include_all not known, can't compile }; + // Check singleton class assumption first, before emitting other patchpoints + if !fun.assume_no_singleton_classes(block, recv_class, state) { + return None; + } fun.push_insn(block, hir::Insn::PatchPoint { invariant: hir::Invariant::NoTracePoint, state }); fun.push_insn(block, hir::Insn::PatchPoint { invariant: hir::Invariant::MethodRedefined { @@ -865,11 +874,6 @@ fn inline_kernel_respond_to_p( cme: target_cme }, state }); - if recv_class.instance_can_have_singleton_class() { - fun.push_insn(block, hir::Insn::PatchPoint { - invariant: hir::Invariant::NoSingletonClass { klass: recv_class }, state - }); - } Some(fun.push_insn(block, hir::Insn::Const { val: hir::Const::Value(result) })) } diff --git a/zjit/src/hir.rs b/zjit/src/hir.rs index 7a7d03d75d5a18..97a0097073d7bd 100644 --- a/zjit/src/hir.rs +++ b/zjit/src/hir.rs @@ -7,7 +7,7 @@ #![allow(clippy::match_like_matches_macro)] use crate::{ backend::lir::C_ARG_OPNDS, - cast::IntoUsize, codegen::local_idx_to_ep_offset, cruby::*, payload::{get_or_create_iseq_payload, IseqPayload}, options::{debug, get_option, DumpHIR}, state::ZJITState, json::Json + cast::IntoUsize, codegen::local_idx_to_ep_offset, cruby::*, invariants::has_singleton_class_of, payload::{get_or_create_iseq_payload, IseqPayload}, options::{debug, get_option, DumpHIR}, state::ZJITState, json::Json }; use std::{ cell::RefCell, collections::{BTreeSet, HashMap, HashSet, VecDeque}, ffi::{c_void, c_uint, c_int, CStr}, fmt::Display, mem::{align_of, size_of}, ptr, slice::Iter @@ -484,6 +484,7 @@ pub enum SideExitReason { UnhandledHIRInsn(InsnId), UnhandledYARVInsn(u32), UnhandledCallType(CallType), + UnhandledBlockArg, TooManyKeywordParameters, FixnumAddOverflow, FixnumSubOverflow, @@ -497,6 +498,7 @@ pub enum SideExitReason { GuardNotShared, GuardLess, GuardGreaterEq, + GuardSuperMethodEntry, PatchPoint(Invariant), CalleeSideExit, ObjToStringFallback, @@ -644,6 +646,25 @@ pub enum SendFallbackReason { ComplexArgPass, /// Caller has keyword arguments but callee doesn't expect them; need to convert to hash. UnexpectedKeywordArgs, + /// A singleton class has been seen for the receiver class, so we skip the optimization + /// to avoid an invalidation loop. + SingletonClassSeen, + /// The super call is passed a block that the optimizer does not support. + SuperCallWithBlock, + /// The profiled super class cannot be found. + SuperClassNotFound, + /// The `super` call uses a complex argument pattern that the optimizer does not support. + SuperComplexArgsPass, + /// The cached target of a `super` call could not be found. + SuperTargetNotFound, + /// Attempted to specialize a `super` call that doesn't have profile data. + SuperNoProfiles, + /// Cannot optimize the `super` call due to the target method. + SuperNotOptimizedMethodType(MethodType), + /// The `super` call is polymorpic. + SuperPolymorphic, + /// The `super` target call uses a complex argument pattern that the optimizer does not support. + SuperTargetComplexArgsPass, /// Initial fallback reason for every instruction, which should be mutated to /// a more actionable reason when an attempt to specialize the instruction fails. Uncategorized(ruby_vminsn_type), @@ -680,6 +701,15 @@ impl Display for SendFallbackReason { ArgcParamMismatch => write!(f, "Argument count does not match parameter count"), ComplexArgPass => write!(f, "Complex argument passing"), UnexpectedKeywordArgs => write!(f, "Unexpected Keyword Args"), + SingletonClassSeen => write!(f, "Singleton class previously created for receiver class"), + SuperCallWithBlock => write!(f, "super: call made with a block"), + SuperClassNotFound => write!(f, "super: profiled class cannot be found"), + SuperComplexArgsPass => write!(f, "super: complex argument passing to `super` call"), + SuperNoProfiles => write!(f, "super: no profile data available"), + SuperNotOptimizedMethodType(method_type) => write!(f, "super: unsupported target method type {:?}", method_type), + SuperPolymorphic => write!(f, "super: polymorphic call site"), + SuperTargetNotFound => write!(f, "super: profiled target method cannot be found"), + SuperTargetComplexArgsPass => write!(f, "super: complex argument passing to `super` target call"), Uncategorized(insn) => write!(f, "Uncategorized({})", insn_name(*insn as usize)), } } @@ -729,7 +759,7 @@ pub enum Insn { ArrayExtend { left: InsnId, right: InsnId, state: InsnId }, /// Push `val` onto `array`, where `array` is already `Array`. ArrayPush { array: InsnId, val: InsnId, state: InsnId }, - ArrayArefFixnum { array: InsnId, index: InsnId }, + ArrayAref { array: InsnId, index: InsnId }, ArrayAset { array: InsnId, index: InsnId, val: InsnId }, ArrayPop { array: InsnId, state: InsnId }, /// Return the length of the array as a C `long` ([`types::CInt64`]) @@ -971,6 +1001,11 @@ pub enum Insn { GuardGreaterEq { left: InsnId, right: InsnId, state: InsnId }, /// Side-exit if left is not less than right (both operands are C long). GuardLess { left: InsnId, right: InsnId, state: InsnId }, + /// Side-exit if the method entry at ep[VM_ENV_DATA_INDEX_ME_CREF] doesn't match the expected CME. + /// Used to ensure super calls are made from the expected method context. + GuardSuperMethodEntry { cme: *const rb_callable_method_entry_t, state: InsnId }, + /// Get the block handler from ep[VM_ENV_DATA_INDEX_SPECVAL] at the local EP (LEP). + GetBlockHandler, /// Generate no code (or padding if necessary) and insert a patch point /// that can be rewritten to a side exit when the Invariant is broken. @@ -999,7 +1034,7 @@ impl Insn { | Insn::PatchPoint { .. } | Insn::SetIvar { .. } | Insn::SetClassVar { .. } | Insn::ArrayExtend { .. } | Insn::ArrayPush { .. } | Insn::SideExit { .. } | Insn::SetGlobal { .. } | Insn::SetLocal { .. } | Insn::Throw { .. } | Insn::IncrCounter(_) | Insn::IncrCounterPtr { .. } - | Insn::CheckInterrupts { .. } | Insn::GuardBlockParamProxy { .. } + | Insn::CheckInterrupts { .. } | Insn::GuardBlockParamProxy { .. } | Insn::GuardSuperMethodEntry { .. } | Insn::StoreField { .. } | Insn::WriteBarrier { .. } | Insn::HashAset { .. } | Insn::ArrayAset { .. } => false, _ => true, @@ -1128,8 +1163,8 @@ impl<'a> std::fmt::Display for InsnPrinter<'a> { } Ok(()) } - Insn::ArrayArefFixnum { array, index, .. } => { - write!(f, "ArrayArefFixnum {array}, {index}") + Insn::ArrayAref { array, index, .. } => { + write!(f, "ArrayAref {array}, {index}") } Insn::ArrayAset { array, index, val, ..} => { write!(f, "ArrayAset {array}, {index}, {val}") @@ -1349,6 +1384,8 @@ impl<'a> std::fmt::Display for InsnPrinter<'a> { Insn::GuardNotShared { recv, .. } => write!(f, "GuardNotShared {recv}"), Insn::GuardLess { left, right, .. } => write!(f, "GuardLess {left}, {right}"), Insn::GuardGreaterEq { left, right, .. } => write!(f, "GuardGreaterEq {left}, {right}"), + Insn::GuardSuperMethodEntry { cme, .. } => write!(f, "GuardSuperMethodEntry {:p}", self.ptr_map.map_ptr(cme)), + Insn::GetBlockHandler => write!(f, "GetBlockHandler"), Insn::PatchPoint { invariant, .. } => { write!(f, "PatchPoint {}", invariant.print(self.ptr_map)) }, Insn::GetConstantPath { ic, .. } => { write!(f, "GetConstantPath {:p}", self.ptr_map.map_ptr(ic)) }, Insn::IsBlockGiven => { write!(f, "IsBlockGiven") }, @@ -1891,6 +1928,24 @@ impl Function { } } + /// Assume that objects of a given class will have no singleton class. + /// Returns true if safe to assume so and emits a PatchPoint. + /// Returns false if we've already seen a singleton class for this class, + /// to avoid an invalidation loop. + pub fn assume_no_singleton_classes(&mut self, block: BlockId, klass: VALUE, state: InsnId) -> bool { + if !klass.instance_can_have_singleton_class() { + // This class can never have a singleton class, so no patchpoint needed. + return true; + } + if has_singleton_class_of(klass) { + // We've seen a singleton class for this klass. Disable the optimization + // to avoid an invalidation loop. + return false; + } + self.push_insn(block, Insn::PatchPoint { invariant: Invariant::NoSingletonClass { klass }, state }); + true + } + /// Return a copy of the instruction where the instruction and its operands have been read from /// the union-find table (to find the current most-optimized version of this instruction). See /// [`UnionFind`] for more. @@ -1993,6 +2048,8 @@ impl Function { &GuardNotShared { recv, state } => GuardNotShared { recv: find!(recv), state }, &GuardGreaterEq { left, right, state } => GuardGreaterEq { left: find!(left), right: find!(right), state }, &GuardLess { left, right, state } => GuardLess { left: find!(left), right: find!(right), state }, + &GuardSuperMethodEntry { cme, state } => GuardSuperMethodEntry { cme, state }, + &GetBlockHandler => GetBlockHandler, &FixnumAdd { left, right, state } => FixnumAdd { left: find!(left), right: find!(right), state }, &FixnumSub { left, right, state } => FixnumSub { left: find!(left), right: find!(right), state }, &FixnumMult { left, right, state } => FixnumMult { left: find!(left), right: find!(right), state }, @@ -2093,7 +2150,7 @@ impl Function { &NewHash { ref elements, state } => NewHash { elements: find_vec!(elements), state: find!(state) }, &NewRange { low, high, flag, state } => NewRange { low: find!(low), high: find!(high), flag, state: find!(state) }, &NewRangeFixnum { low, high, flag, state } => NewRangeFixnum { low: find!(low), high: find!(high), flag, state: find!(state) }, - &ArrayArefFixnum { array, index } => ArrayArefFixnum { array: find!(array), index: find!(index) }, + &ArrayAref { array, index } => ArrayAref { array: find!(array), index: find!(index) }, &ArrayAset { array, index, val } => ArrayAset { array: find!(array), index: find!(index), val: find!(val) }, &ArrayPop { array, state } => ArrayPop { array: find!(array), state: find!(state) }, &ArrayLength { array } => ArrayLength { array: find!(array) }, @@ -2165,8 +2222,9 @@ impl Function { Insn::SetGlobal { .. } | Insn::Jump(_) | Insn::EntryPoint { .. } | Insn::IfTrue { .. } | Insn::IfFalse { .. } | Insn::Return { .. } | Insn::Throw { .. } | Insn::PatchPoint { .. } | Insn::SetIvar { .. } | Insn::SetClassVar { .. } | Insn::ArrayExtend { .. } - | Insn::ArrayPush { .. } | Insn::SideExit { .. } | Insn::SetLocal { .. } | Insn::IncrCounter(_) - | Insn::CheckInterrupts { .. } | Insn::GuardBlockParamProxy { .. } | Insn::IncrCounterPtr { .. } + | Insn::ArrayPush { .. } | Insn::SideExit { .. } | Insn::SetLocal { .. } + | Insn::IncrCounter(_) | Insn::IncrCounterPtr { .. } + | Insn::CheckInterrupts { .. } | Insn::GuardBlockParamProxy { .. } | Insn::GuardSuperMethodEntry { .. } | Insn::StoreField { .. } | Insn::WriteBarrier { .. } | Insn::HashAset { .. } | Insn::ArrayAset { .. } => panic!("Cannot infer type of instruction with no output: {}. See Insn::has_output().", self.insns[insn.0]), Insn::Const { val: Const::Value(val) } => Type::from_value(*val), @@ -2193,7 +2251,10 @@ impl Function { Insn::IsBitNotEqual { .. } => types::CBool, Insn::BoxBool { .. } => types::BoolExact, Insn::BoxFixnum { .. } => types::Fixnum, - Insn::UnboxFixnum { .. } => types::CInt64, + Insn::UnboxFixnum { val } => self + .type_of(*val) + .fixnum_value() + .map_or(types::CInt64, |fixnum| Type::from_cint(types::CInt64, fixnum)), Insn::FixnumAref { .. } => types::Fixnum, Insn::StringCopy { .. } => types::StringExact, Insn::StringIntern { .. } => types::Symbol, @@ -2205,7 +2266,7 @@ impl Function { Insn::ToRegexp { .. } => types::RegexpExact, Insn::NewArray { .. } => types::ArrayExact, Insn::ArrayDup { .. } => types::ArrayExact, - Insn::ArrayArefFixnum { .. } => types::BasicObject, + Insn::ArrayAref { .. } => types::BasicObject, Insn::ArrayPop { .. } => types::BasicObject, Insn::ArrayLength { .. } => types::CInt64, Insn::HashAref { .. } => types::BasicObject, @@ -2274,6 +2335,7 @@ impl Function { Insn::AnyToString { .. } => types::String, Insn::GetLocal { rest_param: true, .. } => types::ArrayExact, Insn::GetLocal { .. } => types::BasicObject, + Insn::GetBlockHandler => types::RubyValue, // The type of Snapshot doesn't really matter; it's never materialized. It's used only // as a reference for FrameState, which we use to generate side-exit code. Insn::Snapshot { .. } => types::Any, @@ -2531,8 +2593,8 @@ impl Function { return false; } self.gen_patch_points_for_optimized_ccall(block, class, method_id, cme, state); - if class.instance_can_have_singleton_class() { - self.push_insn(block, Insn::PatchPoint { invariant: Invariant::NoSingletonClass { klass: class }, state }); + if !self.assume_no_singleton_classes(block, class, state) { + return false; } true } @@ -2702,10 +2764,12 @@ impl Function { if !can_direct_send(self, block, iseq, insn_id, args.as_slice()) { self.push_insn_id(block, insn_id); continue; } - self.push_insn(block, Insn::PatchPoint { invariant: Invariant::MethodRedefined { klass, method: mid, cme }, state }); - if klass.instance_can_have_singleton_class() { - self.push_insn(block, Insn::PatchPoint { invariant: Invariant::NoSingletonClass { klass }, state }); + // Check singleton class assumption first, before emitting other patchpoints + if !self.assume_no_singleton_classes(block, klass, state) { + self.set_dynamic_send_reason(insn_id, SingletonClassSeen); + self.push_insn_id(block, insn_id); continue; } + self.push_insn(block, Insn::PatchPoint { invariant: Invariant::MethodRedefined { klass, method: mid, cme }, state }); if let Some(profiled_type) = profiled_type { recv = self.push_insn(block, Insn::GuardType { val: recv, guard_type: Type::from_profiled_type(profiled_type), state }); } @@ -2754,10 +2818,12 @@ impl Function { // TODO(alan): Turn this into a ractor belonging guard to work better in multi ractor mode. self.push_insn_id(block, insn_id); continue; } - self.push_insn(block, Insn::PatchPoint { invariant: Invariant::MethodRedefined { klass, method: mid, cme }, state }); - if klass.instance_can_have_singleton_class() { - self.push_insn(block, Insn::PatchPoint { invariant: Invariant::NoSingletonClass { klass }, state }); + // Check singleton class assumption first, before emitting other patchpoints + if !self.assume_no_singleton_classes(block, klass, state) { + self.set_dynamic_send_reason(insn_id, SingletonClassSeen); + self.push_insn_id(block, insn_id); continue; } + self.push_insn(block, Insn::PatchPoint { invariant: Invariant::MethodRedefined { klass, method: mid, cme }, state }); if let Some(profiled_type) = profiled_type { recv = self.push_insn(block, Insn::GuardType { val: recv, guard_type: Type::from_profiled_type(profiled_type), state }); @@ -2788,11 +2854,13 @@ impl Function { if self.is_metaclass(klass) && !self.assume_single_ractor_mode(block, state) { self.push_insn_id(block, insn_id); continue; } + // Check singleton class assumption first, before emitting other patchpoints + if !self.assume_no_singleton_classes(block, klass, state) { + self.set_dynamic_send_reason(insn_id, SingletonClassSeen); + self.push_insn_id(block, insn_id); continue; + } self.push_insn(block, Insn::PatchPoint { invariant: Invariant::MethodRedefined { klass, method: mid, cme }, state }); - if klass.instance_can_have_singleton_class() { - self.push_insn(block, Insn::PatchPoint { invariant: Invariant::NoSingletonClass { klass }, state }); - } if let Some(profiled_type) = profiled_type { recv = self.push_insn(block, Insn::GuardType { val: recv, guard_type: Type::from_profiled_type(profiled_type), state }); } @@ -2835,10 +2903,12 @@ impl Function { // No (monomorphic/skewed polymorphic) profile info self.push_insn_id(block, insn_id); continue; }; - self.push_insn(block, Insn::PatchPoint { invariant: Invariant::MethodRedefined { klass, method: mid, cme }, state }); - if klass.instance_can_have_singleton_class() { - self.push_insn(block, Insn::PatchPoint { invariant: Invariant::NoSingletonClass { klass }, state }); + // Check singleton class assumption first, before emitting other patchpoints + if !self.assume_no_singleton_classes(block, klass, state) { + self.set_dynamic_send_reason(insn_id, SingletonClassSeen); + self.push_insn_id(block, insn_id); continue; } + self.push_insn(block, Insn::PatchPoint { invariant: Invariant::MethodRedefined { klass, method: mid, cme }, state }); if let Some(profiled_type) = profiled_type { recv = self.push_insn(block, Insn::GuardType { val: recv, guard_type: Type::from_profiled_type(profiled_type), state }); } @@ -3030,6 +3100,120 @@ impl Function { self.push_insn_id(block, insn_id); }; } + Insn::InvokeSuper { recv, cd, blockiseq, args, state, .. } => { + // Don't handle calls with literal blocks (e.g., super { ... }) + if !blockiseq.is_null() { + self.push_insn_id(block, insn_id); + self.set_dynamic_send_reason(insn_id, SuperCallWithBlock); + continue; + } + + let ci = unsafe { get_call_data_ci(cd) }; + let flags = unsafe { rb_vm_ci_flag(ci) }; + assert!(flags & VM_CALL_FCALL != 0); + + // Reject calls with complex argument handling. + let complex_arg_types = VM_CALL_ARGS_SPLAT + | VM_CALL_KW_SPLAT + | VM_CALL_KWARG + | VM_CALL_ARGS_BLOCKARG + | VM_CALL_FORWARDING; + + if (flags & complex_arg_types) != 0 { + self.push_insn_id(block, insn_id); + self.set_dynamic_send_reason(insn_id, SuperComplexArgsPass); + continue; + } + + let frame_state = self.frame_state(state); + + // Get the profiled CME from the current method. + let Some(profiles) = self.profiles.as_ref() else { + self.push_insn_id(block, insn_id); + self.set_dynamic_send_reason(insn_id, SuperNoProfiles); + continue; + }; + + let Some(current_cme) = profiles.payload.profile.get_super_method_entry(frame_state.insn_idx) else { + self.push_insn_id(block, insn_id); + + // The absence of the super CME could be due to a missing profile, but + // if we've made it this far the value would have been deleted, indicating + // that the call is at least polymorphic and possibly megamorphic. + self.set_dynamic_send_reason(insn_id, SuperPolymorphic); + continue; + }; + + // Get defined_class and method ID from the profiled CME. + let current_defined_class = unsafe { (*current_cme).defined_class }; + let mid = unsafe { get_def_original_id((*current_cme).def) }; + + // Compute superclass: RCLASS_SUPER(RCLASS_ORIGIN(defined_class)) + let superclass = unsafe { rb_class_get_superclass(RCLASS_ORIGIN(current_defined_class)) }; + if superclass.nil_p() { + self.push_insn_id(block, insn_id); + self.set_dynamic_send_reason(insn_id, SuperClassNotFound); + continue; + } + + // Look up the super method. + let super_cme = unsafe { rb_callable_method_entry(superclass, mid) }; + if super_cme.is_null() { + self.push_insn_id(block, insn_id); + self.set_dynamic_send_reason(insn_id, SuperTargetNotFound); + continue; + } + + // Check if it's an ISEQ method; bail if it isn't. + let def_type = unsafe { get_cme_def_type(super_cme) }; + if def_type != VM_METHOD_TYPE_ISEQ { + self.push_insn_id(block, insn_id); + self.set_dynamic_send_reason(insn_id, SuperNotOptimizedMethodType(MethodType::from(def_type))); + continue; + } + + // Check if the super method's parameters support direct send. + // If not, we can't do direct dispatch. + let super_iseq = unsafe { get_def_iseq_ptr((*super_cme).def) }; + if !can_direct_send(self, block, super_iseq, insn_id, args.as_slice()) { + self.push_insn_id(block, insn_id); + self.set_dynamic_send_reason(insn_id, SuperTargetComplexArgsPass); + continue; + } + + // Add PatchPoint for method redefinition. + self.push_insn(block, Insn::PatchPoint { + invariant: Invariant::MethodRedefined { + klass: unsafe { (*super_cme).defined_class }, + method: mid, + cme: super_cme + }, + state + }); + + // Guard that we're calling `super` from the expected method context. + self.push_insn(block, Insn::GuardSuperMethodEntry { cme: current_cme, state }); + + // Guard that no block is being passed (implicit or explicit). + let block_handler = self.push_insn(block, Insn::GetBlockHandler); + self.push_insn(block, Insn::GuardBitEquals { + val: block_handler, + expected: Const::Value(VALUE(VM_BLOCK_HANDLER_NONE as usize)), + reason: SideExitReason::UnhandledBlockArg, + state + }); + + // Use SendWithoutBlockDirect with the super method's CME and ISEQ. + let send_direct = self.push_insn(block, Insn::SendWithoutBlockDirect { + recv, + cd, + cme: super_cme, + iseq: super_iseq, + args, + state + }); + self.make_equal_to(insn_id, send_direct); + } _ => { self.push_insn_id(block, insn_id); } } } @@ -3375,11 +3559,14 @@ impl Function { return Err(()); } + // Check singleton class assumption first, before emitting other patchpoints + if !fun.assume_no_singleton_classes(block, recv_class, state) { + fun.set_dynamic_send_reason(send_insn_id, SingletonClassSeen); + return Err(()); + } + // Commit to the replacement. Put PatchPoint. fun.gen_patch_points_for_optimized_ccall(block, recv_class, method_id, cme, state); - if recv_class.instance_can_have_singleton_class() { - fun.push_insn(block, Insn::PatchPoint { invariant: Invariant::NoSingletonClass { klass: recv_class }, state }); - } if let Some(profiled_type) = profiled_type { // Guard receiver class @@ -3410,11 +3597,15 @@ impl Function { -1 => { // The method gets a pointer to the first argument // func(int argc, VALUE *argv, VALUE recv) - fun.gen_patch_points_for_optimized_ccall(block, recv_class, method_id, cme, state); - if recv_class.instance_can_have_singleton_class() { - fun.push_insn(block, Insn::PatchPoint { invariant: Invariant::NoSingletonClass { klass: recv_class }, state }); + // Check singleton class assumption first, before emitting other patchpoints + if !fun.assume_no_singleton_classes(block, recv_class, state) { + fun.set_dynamic_send_reason(send_insn_id, SingletonClassSeen); + return Err(()); } + + fun.gen_patch_points_for_optimized_ccall(block, recv_class, method_id, cme, state); + if let Some(profiled_type) = profiled_type { // Guard receiver class recv = fun.push_insn(block, Insn::GuardType { val: recv, guard_type: Type::from_profiled_type(profiled_type), state }); @@ -3522,11 +3713,14 @@ impl Function { return Err(()); } + // Check singleton class assumption first, before emitting other patchpoints + if !fun.assume_no_singleton_classes(block, recv_class, state) { + fun.set_dynamic_send_reason(send_insn_id, SingletonClassSeen); + return Err(()); + } + // Commit to the replacement. Put PatchPoint. fun.gen_patch_points_for_optimized_ccall(block, recv_class, method_id, cme, state); - if recv_class.instance_can_have_singleton_class() { - fun.push_insn(block, Insn::PatchPoint { invariant: Invariant::NoSingletonClass { klass: recv_class }, state }); - } let props = ZJITState::get_method_annotations().get_cfunc_properties(cme); if props.is_none() && get_option!(stats) { @@ -3599,11 +3793,14 @@ impl Function { fun.set_dynamic_send_reason(send_insn_id, ComplexArgPass); return Err(()); } else { + // Check singleton class assumption first, before emitting other patchpoints + if !fun.assume_no_singleton_classes(block, recv_class, state) { + fun.set_dynamic_send_reason(send_insn_id, SingletonClassSeen); + return Err(()); + } + fun.gen_patch_points_for_optimized_ccall(block, recv_class, method_id, cme, state); - if recv_class.instance_can_have_singleton_class() { - fun.push_insn(block, Insn::PatchPoint { invariant: Invariant::NoSingletonClass { klass: recv_class }, state }); - } if let Some(profiled_type) = profiled_type { // Guard receiver class recv = fun.push_insn(block, Insn::GuardType { val: recv, guard_type: Type::from_profiled_type(profiled_type), state }); @@ -3879,15 +4076,16 @@ impl Function { _ => None, }) } - Insn::ArrayArefFixnum { array, index } if self.type_of(array).ruby_object_known() - && self.type_of(index).ruby_object_known() => { + Insn::ArrayAref { array, index } + if self.type_of(array).ruby_object_known() + && self.type_of(index).is_subtype(types::CInt64) => { let array_obj = self.type_of(array).ruby_object().unwrap(); - if array_obj.is_frozen() { - let index = self.type_of(index).fixnum_value().unwrap(); - let val = unsafe { rb_yarv_ary_entry_internal(array_obj, index) }; - self.new_insn(Insn::Const { val: Const::Value(val) }) - } else { - insn_id + match (array_obj.is_frozen(), self.type_of(index).cint64_value()) { + (true, Some(index)) => { + let val = unsafe { rb_yarv_ary_entry_internal(array_obj, index) }; + self.new_insn(Insn::Const { val: Const::Value(val) }) + } + _ => insn_id, } } Insn::Test { val } if self.type_of(val).is_known_falsy() => { @@ -3934,6 +4132,7 @@ impl Function { | &Insn::LoadEC | &Insn::LoadSelf | &Insn::GetLocal { .. } + | &Insn::GetBlockHandler | &Insn::PutSpecialObject { .. } | &Insn::IsBlockGiven | &Insn::IncrCounter(_) @@ -4076,7 +4275,7 @@ impl Function { worklist.push_back(val); worklist.push_back(state); } - &Insn::ArrayArefFixnum { array, index } => { + &Insn::ArrayAref { array, index } => { worklist.push_back(array); worklist.push_back(index); } @@ -4162,6 +4361,7 @@ impl Function { worklist.push_back(val); } &Insn::GuardBlockParamProxy { state, .. } | + &Insn::GuardSuperMethodEntry { state, .. } | &Insn::GetGlobal { state, .. } | &Insn::GetSpecialSymbol { state, .. } | &Insn::GetSpecialNumber { state, .. } | @@ -4677,6 +4877,8 @@ impl Function { | Insn::Jump { .. } | Insn::EntryPoint { .. } | Insn::GuardBlockParamProxy { .. } + | Insn::GuardSuperMethodEntry { .. } + | Insn::GetBlockHandler | Insn::PatchPoint { .. } | Insn::SideExit { .. } | Insn::IncrCounter { .. } @@ -4797,9 +4999,9 @@ impl Function { | Insn::ArrayLength { array, .. } => { self.assert_subtype(insn_id, array, types::Array) } - Insn::ArrayArefFixnum { array, index } => { + Insn::ArrayAref { array, index } => { self.assert_subtype(insn_id, array, types::Array)?; - self.assert_subtype(insn_id, index, types::Fixnum) + self.assert_subtype(insn_id, index, types::CInt64) } Insn::ArrayAset { array, index, .. } => { self.assert_subtype(insn_id, array, types::ArrayExact)?; @@ -5354,7 +5556,8 @@ fn unspecializable_c_call_type(flags: u32) -> bool { fn unspecializable_call_type(flags: u32) -> bool { ((flags & VM_CALL_ARGS_SPLAT) != 0) || ((flags & VM_CALL_KW_SPLAT) != 0) || - ((flags & VM_CALL_ARGS_BLOCKARG) != 0) + ((flags & VM_CALL_ARGS_BLOCKARG) != 0) || + ((flags & VM_CALL_FORWARDING) != 0) } /// We have IseqPayload, which keeps track of HIR Types in the interpreter, but this is not useful @@ -6342,10 +6545,10 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result { let length = fun.push_insn(block, Insn::ArrayLength { array }); fun.push_insn(block, Insn::GuardBitEquals { val: length, expected: Const::CInt64(num as i64), reason: SideExitReason::ExpandArray, state: exit_id }); for i in (0..num).rev() { - // TODO(max): Add a short-cut path for long indices into an array where the - // index is known to be in-bounds - let index = fun.push_insn(block, Insn::Const { val: Const::Value(VALUE::fixnum_from_usize(i.try_into().unwrap())) }); - let element = fun.push_insn(block, Insn::ArrayArefFixnum { array, index }); + // We do not emit a length guard here because in-bounds is already + // ensured by the expandarray length check above. + let index = fun.push_insn(block, Insn::Const { val: Const::CInt64(i.try_into().unwrap()) }); + let element = fun.push_insn(block, Insn::ArrayAref { array, index }); state.stack_push(element); } } diff --git a/zjit/src/hir/opt_tests.rs b/zjit/src/hir/opt_tests.rs index 8d49353b30eaf4..08aa4474219032 100644 --- a/zjit/src/hir/opt_tests.rs +++ b/zjit/src/hir/opt_tests.rs @@ -557,8 +557,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): - PatchPoint MethodRedefined(CustomEq@0x1000, !=@0x1008, cme:0x1010) PatchPoint NoSingletonClass(CustomEq@0x1000) + PatchPoint MethodRedefined(CustomEq@0x1000, !=@0x1008, cme:0x1010) v28:HeapObject[class_exact:CustomEq] = GuardType v9, HeapObject[class_exact:CustomEq] v29:BoolExact = CCallWithFrame v28, :BasicObject#!=@0x1038, v9 v20:NilClass = Const Value(nil) @@ -696,8 +696,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): - PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) v18:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] v19:BasicObject = SendWithoutBlockDirect v18, :foo (0x1038) CheckInterrupts @@ -724,8 +724,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): - PatchPoint MethodRedefined(Object@0x1000, baz@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint MethodRedefined(Object@0x1000, baz@0x1008, cme:0x1010) v18:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] IncrCounter inline_iseq_optimized_send_count v21:Fixnum[1] = Const Value(1) @@ -752,8 +752,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): - PatchPoint MethodRedefined(Object@0x1000, baz@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint MethodRedefined(Object@0x1000, baz@0x1008, cme:0x1010) v19:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] IncrCounter inline_cfunc_optimized_send_count CheckInterrupts @@ -790,8 +790,8 @@ mod hir_opt_tests { Jump bb2(v4) bb2(v6:BasicObject): v11:Fixnum[1] = Const Value(1) - PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) v20:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] v21:BasicObject = SendWithoutBlockDirect v20, :foo (0x1038), v11 CheckInterrupts @@ -819,8 +819,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): - PatchPoint MethodRedefined(C@0x1000, fun_new_map@0x1008, cme:0x1010) PatchPoint NoSingletonClass(C@0x1000) + PatchPoint MethodRedefined(C@0x1000, fun_new_map@0x1008, cme:0x1010) v23:ArraySubclass[class_exact:C] = GuardType v9, ArraySubclass[class_exact:C] v24:BasicObject = CCallWithFrame v23, :C#fun_new_map@0x1038, block=0x1040 v15:BasicObject = GetLocal :o, l0, EP@3 @@ -876,8 +876,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): - PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) v18:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] v19:BasicObject = SendWithoutBlockDirect v18, :foo (0x1038) CheckInterrupts @@ -904,8 +904,8 @@ mod hir_opt_tests { Jump bb2(v4) bb2(v6:BasicObject): v11:Fixnum[3] = Const Value(3) - PatchPoint MethodRedefined(Object@0x1000, Integer@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint MethodRedefined(Object@0x1000, Integer@0x1008, cme:0x1010) v20:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] v21:BasicObject = SendWithoutBlockDirect v20, :Integer (0x1038), v11 CheckInterrupts @@ -934,8 +934,8 @@ mod hir_opt_tests { bb2(v6:BasicObject): v11:Fixnum[1] = Const Value(1) v13:Fixnum[2] = Const Value(2) - PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) v22:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] v23:BasicObject = SendWithoutBlockDirect v22, :foo (0x1038), v11, v13 CheckInterrupts @@ -964,12 +964,12 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): - PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) v23:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] v24:BasicObject = SendWithoutBlockDirect v23, :foo (0x1038) - PatchPoint MethodRedefined(Object@0x1000, bar@0x1040, cme:0x1048) PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint MethodRedefined(Object@0x1000, bar@0x1040, cme:0x1048) v27:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] v28:BasicObject = SendWithoutBlockDirect v27, :bar (0x1038) CheckInterrupts @@ -994,8 +994,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): - PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) v18:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] v19:BasicObject = SendWithoutBlockDirect v18, :foo (0x1038) CheckInterrupts @@ -1021,8 +1021,8 @@ mod hir_opt_tests { Jump bb2(v4) bb2(v6:BasicObject): v11:Fixnum[3] = Const Value(3) - PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) v20:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] v21:BasicObject = SendWithoutBlockDirect v20, :foo (0x1038), v11 CheckInterrupts @@ -1049,8 +1049,8 @@ mod hir_opt_tests { bb2(v6:BasicObject): v11:Fixnum[3] = Const Value(3) v13:Fixnum[4] = Const Value(4) - PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) v22:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] v23:BasicObject = SendWithoutBlockDirect v22, :foo (0x1038), v11, v13 CheckInterrupts @@ -1078,8 +1078,8 @@ mod hir_opt_tests { bb2(v6:BasicObject): v11:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) v12:StringExact = StringCopy v11 - PatchPoint MethodRedefined(Object@0x1008, puts@0x1010, cme:0x1018) PatchPoint NoSingletonClass(Object@0x1008) + PatchPoint MethodRedefined(Object@0x1008, puts@0x1010, cme:0x1018) v22:HeapObject[class_exact*:Object@VALUE(0x1008)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1008)] v23:BasicObject = CCallVariadic v22, :Kernel#puts@0x1040, v12 CheckInterrupts @@ -1603,13 +1603,18 @@ mod hir_opt_tests { Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): v14:Fixnum[0] = Const Value(0) - PatchPoint MethodRedefined(Array@0x1000, []@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Array@0x1000) + PatchPoint MethodRedefined(Array@0x1000, []@0x1008, cme:0x1010) v25:ArrayExact = GuardType v9, ArrayExact - v26:BasicObject = ArrayArefFixnum v25, v14 + v26:CInt64[0] = UnboxFixnum v14 + v27:CInt64 = ArrayLength v25 + v28:CInt64[0] = GuardLess v26, v27 + v29:CInt64[0] = Const CInt64(0) + v30:CInt64[0] = GuardGreaterEq v28, v29 + v31:BasicObject = ArrayAref v25, v30 IncrCounter inline_cfunc_optimized_send_count CheckInterrupts - Return v26 + Return v31 "); assert_snapshot!(inspect("test [1,2,3]"), @"1"); } @@ -1633,8 +1638,8 @@ mod hir_opt_tests { Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): v14:Fixnum[0] = Const Value(0) - PatchPoint MethodRedefined(Hash@0x1000, []@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Hash@0x1000) + PatchPoint MethodRedefined(Hash@0x1000, []@0x1008, cme:0x1010) v25:HashExact = GuardType v9, HashExact v26:BasicObject = HashAref v25, v14 IncrCounter inline_cfunc_optimized_send_count @@ -2318,8 +2323,8 @@ mod hir_opt_tests { Jump bb2(v4) bb2(v6:BasicObject): v10:ArrayExact = NewArray - PatchPoint MethodRedefined(Array@0x1000, itself@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Array@0x1000) + PatchPoint MethodRedefined(Array@0x1000, itself@0x1008, cme:0x1010) IncrCounter inline_cfunc_optimized_send_count CheckInterrupts Return v10 @@ -2347,8 +2352,8 @@ mod hir_opt_tests { Jump bb2(v5, v6) bb2(v8:BasicObject, v9:NilClass): v13:ArrayExact = NewArray - PatchPoint MethodRedefined(Array@0x1000, itself@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Array@0x1000) + PatchPoint MethodRedefined(Array@0x1000, itself@0x1008, cme:0x1010) IncrCounter inline_cfunc_optimized_send_count PatchPoint NoEPEscape(test) v21:Fixnum[1] = Const Value(1) @@ -2382,8 +2387,8 @@ mod hir_opt_tests { PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, M) v29:ModuleExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - PatchPoint MethodRedefined(Module@0x1010, name@0x1018, cme:0x1020) PatchPoint NoSingletonClass(Module@0x1010) + PatchPoint MethodRedefined(Module@0x1010, name@0x1018, cme:0x1020) IncrCounter inline_cfunc_optimized_send_count v34:StringExact|NilClass = CCall v29, :Module#name@0x1048 PatchPoint NoEPEscape(test) @@ -2412,8 +2417,8 @@ mod hir_opt_tests { Jump bb2(v4) bb2(v6:BasicObject): v10:ArrayExact = NewArray - PatchPoint MethodRedefined(Array@0x1000, length@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Array@0x1000) + PatchPoint MethodRedefined(Array@0x1000, length@0x1008, cme:0x1010) IncrCounter inline_cfunc_optimized_send_count v17:Fixnum[5] = Const Value(5) CheckInterrupts @@ -2553,8 +2558,8 @@ mod hir_opt_tests { Jump bb2(v4) bb2(v6:BasicObject): v10:ArrayExact = NewArray - PatchPoint MethodRedefined(Array@0x1000, size@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Array@0x1000) + PatchPoint MethodRedefined(Array@0x1000, size@0x1008, cme:0x1010) IncrCounter inline_cfunc_optimized_send_count v17:Fixnum[5] = Const Value(5) CheckInterrupts @@ -2604,8 +2609,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): - PatchPoint MethodRedefined(Object@0x1000, block_given?@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint MethodRedefined(Object@0x1000, block_given?@0x1008, cme:0x1010) v19:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] v20:BoolExact = IsBlockGiven IncrCounter inline_cfunc_optimized_send_count @@ -2630,8 +2635,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): - PatchPoint MethodRedefined(Object@0x1000, block_given?@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint MethodRedefined(Object@0x1000, block_given?@0x1008, cme:0x1010) v19:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] v20:BoolExact = IsBlockGiven IncrCounter inline_cfunc_optimized_send_count @@ -2659,8 +2664,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): - PatchPoint MethodRedefined(Object@0x1000, block_given?@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint MethodRedefined(Object@0x1000, block_given?@0x1008, cme:0x1010) v23:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] IncrCounter inline_cfunc_optimized_send_count v15:Fixnum[5] = Const Value(5) @@ -2717,8 +2722,8 @@ mod hir_opt_tests { bb2(v10:BasicObject, v11:BasicObject, v12:NilClass): v16:ArrayExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) v17:ArrayExact = ArrayDup v16 - PatchPoint MethodRedefined(Array@0x1008, first@0x1010, cme:0x1018) PatchPoint NoSingletonClass(Array@0x1008) + PatchPoint MethodRedefined(Array@0x1008, first@0x1010, cme:0x1018) IncrCounter inline_iseq_optimized_send_count v31:BasicObject = InvokeBuiltin leaf , v17 CheckInterrupts @@ -2747,8 +2752,8 @@ mod hir_opt_tests { PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, M) v20:ModuleExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - PatchPoint MethodRedefined(Module@0x1010, class@0x1018, cme:0x1020) PatchPoint NoSingletonClass(Module@0x1010) + PatchPoint MethodRedefined(Module@0x1010, class@0x1018, cme:0x1020) IncrCounter inline_iseq_optimized_send_count v26:Class[Module@0x1010] = Const Value(VALUE(0x1010)) IncrCounter inline_cfunc_optimized_send_count @@ -2781,8 +2786,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): - PatchPoint MethodRedefined(C@0x1000, foo@0x1008, cme:0x1010) PatchPoint NoSingletonClass(C@0x1000) + PatchPoint MethodRedefined(C@0x1000, foo@0x1008, cme:0x1010) v21:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] v22:BasicObject = SendWithoutBlockDirect v21, :foo (0x1038) CheckInterrupts @@ -2919,8 +2924,8 @@ mod hir_opt_tests { bb2(v6:BasicObject): v11:Fixnum[1] = Const Value(1) v13:Fixnum[2] = Const Value(2) - PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) v22:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] v24:BasicObject = SendWithoutBlockDirect v22, :foo (0x1038), v11, v13 CheckInterrupts @@ -2949,8 +2954,8 @@ mod hir_opt_tests { v11:Fixnum[3] = Const Value(3) v13:Fixnum[1] = Const Value(1) v15:Fixnum[2] = Const Value(2) - PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) v24:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] v26:BasicObject = SendWithoutBlockDirect v24, :foo (0x1038), v13, v15, v11 CheckInterrupts @@ -2979,8 +2984,8 @@ mod hir_opt_tests { v11:Fixnum[0] = Const Value(0) v13:Fixnum[2] = Const Value(2) v15:Fixnum[1] = Const Value(1) - PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) v24:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] v26:BasicObject = SendWithoutBlockDirect v24, :foo (0x1038), v11, v15, v13 CheckInterrupts @@ -3036,16 +3041,16 @@ mod hir_opt_tests { v11:Fixnum[1] = Const Value(1) v13:Fixnum[3] = Const Value(3) v15:Fixnum[4] = Const Value(4) - PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) v37:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] v39:BasicObject = SendWithoutBlockDirect v37, :foo (0x1038), v11, v13, v15 v20:Fixnum[1] = Const Value(1) v22:Fixnum[2] = Const Value(2) v24:Fixnum[4] = Const Value(4) v26:Fixnum[3] = Const Value(3) - PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) v42:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] v44:BasicObject = SendWithoutBlockDirect v42, :foo (0x1038), v20, v22, v26, v24 v30:ArrayExact = NewArray v39, v44 @@ -3367,8 +3372,8 @@ mod hir_opt_tests { v13:NilClass = Const Value(nil) PatchPoint MethodRedefined(C@0x1008, new@0x1009, cme:0x1010) v46:HeapObject[class_exact:C] = ObjectAllocClass C:VALUE(0x1008) - PatchPoint MethodRedefined(C@0x1008, initialize@0x1038, cme:0x1040) PatchPoint NoSingletonClass(C@0x1008) + PatchPoint MethodRedefined(C@0x1008, initialize@0x1038, cme:0x1040) v50:NilClass = Const Value(nil) IncrCounter inline_cfunc_optimized_send_count CheckInterrupts @@ -3405,8 +3410,8 @@ mod hir_opt_tests { v16:Fixnum[1] = Const Value(1) PatchPoint MethodRedefined(C@0x1008, new@0x1009, cme:0x1010) v49:HeapObject[class_exact:C] = ObjectAllocClass C:VALUE(0x1008) - PatchPoint MethodRedefined(C@0x1008, initialize@0x1038, cme:0x1040) PatchPoint NoSingletonClass(C@0x1008) + PatchPoint MethodRedefined(C@0x1008, initialize@0x1038, cme:0x1040) v52:BasicObject = SendWithoutBlockDirect v49, :initialize (0x1068), v16 CheckInterrupts CheckInterrupts @@ -3436,8 +3441,8 @@ mod hir_opt_tests { v13:NilClass = Const Value(nil) PatchPoint MethodRedefined(Object@0x1008, new@0x1009, cme:0x1010) v46:ObjectExact = ObjectAllocClass Object:VALUE(0x1008) - PatchPoint MethodRedefined(Object@0x1008, initialize@0x1038, cme:0x1040) PatchPoint NoSingletonClass(Object@0x1008) + PatchPoint MethodRedefined(Object@0x1008, initialize@0x1038, cme:0x1040) v50:NilClass = Const Value(nil) IncrCounter inline_cfunc_optimized_send_count CheckInterrupts @@ -3468,8 +3473,8 @@ mod hir_opt_tests { v13:NilClass = Const Value(nil) PatchPoint MethodRedefined(BasicObject@0x1008, new@0x1009, cme:0x1010) v46:BasicObjectExact = ObjectAllocClass BasicObject:VALUE(0x1008) - PatchPoint MethodRedefined(BasicObject@0x1008, initialize@0x1038, cme:0x1040) PatchPoint NoSingletonClass(BasicObject@0x1008) + PatchPoint MethodRedefined(BasicObject@0x1008, initialize@0x1038, cme:0x1040) v50:NilClass = Const Value(nil) IncrCounter inline_cfunc_optimized_send_count CheckInterrupts @@ -3532,8 +3537,8 @@ mod hir_opt_tests { v13:NilClass = Const Value(nil) v16:Fixnum[1] = Const Value(1) PatchPoint MethodRedefined(Array@0x1008, new@0x1009, cme:0x1010) - PatchPoint MethodRedefined(Class@0x1038, new@0x1009, cme:0x1010) PatchPoint NoSingletonClass(Class@0x1038) + PatchPoint MethodRedefined(Class@0x1038, new@0x1009, cme:0x1010) v57:BasicObject = CCallVariadic v46, :Array.new@0x1040, v16 CheckInterrupts Return v57 @@ -3562,8 +3567,8 @@ mod hir_opt_tests { v13:NilClass = Const Value(nil) PatchPoint MethodRedefined(Set@0x1008, new@0x1009, cme:0x1010) v18:HeapBasicObject = ObjectAlloc v43 - PatchPoint MethodRedefined(Set@0x1008, initialize@0x1038, cme:0x1040) PatchPoint NoSingletonClass(Set@0x1008) + PatchPoint MethodRedefined(Set@0x1008, initialize@0x1038, cme:0x1040) v49:SetExact = GuardType v18, SetExact v50:BasicObject = CCallVariadic v49, :Set#initialize@0x1068 CheckInterrupts @@ -3593,8 +3598,8 @@ mod hir_opt_tests { v43:Class[String@0x1008] = Const Value(VALUE(0x1008)) v13:NilClass = Const Value(nil) PatchPoint MethodRedefined(String@0x1008, new@0x1009, cme:0x1010) - PatchPoint MethodRedefined(Class@0x1038, new@0x1009, cme:0x1010) PatchPoint NoSingletonClass(Class@0x1038) + PatchPoint MethodRedefined(Class@0x1038, new@0x1009, cme:0x1010) v54:BasicObject = CCallVariadic v43, :String.new@0x1040 CheckInterrupts Return v54 @@ -3625,8 +3630,8 @@ mod hir_opt_tests { v17:StringExact = StringCopy v16 PatchPoint MethodRedefined(Regexp@0x1008, new@0x1018, cme:0x1020) v50:RegexpExact = ObjectAllocClass Regexp:VALUE(0x1008) - PatchPoint MethodRedefined(Regexp@0x1008, initialize@0x1048, cme:0x1050) PatchPoint NoSingletonClass(Regexp@0x1008) + PatchPoint MethodRedefined(Regexp@0x1008, initialize@0x1048, cme:0x1050) v54:BasicObject = CCallVariadic v50, :Regexp#initialize@0x1078, v17 CheckInterrupts CheckInterrupts @@ -3652,8 +3657,8 @@ mod hir_opt_tests { Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): v18:ArrayExact = NewArray v11, v12 - PatchPoint MethodRedefined(Array@0x1000, length@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Array@0x1000) + PatchPoint MethodRedefined(Array@0x1000, length@0x1008, cme:0x1010) v29:CInt64 = ArrayLength v18 v30:Fixnum = BoxFixnum v29 IncrCounter inline_cfunc_optimized_send_count @@ -3680,8 +3685,8 @@ mod hir_opt_tests { Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): v18:ArrayExact = NewArray v11, v12 - PatchPoint MethodRedefined(Array@0x1000, size@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Array@0x1000) + PatchPoint MethodRedefined(Array@0x1000, size@0x1008, cme:0x1010) v29:CInt64 = ArrayLength v18 v30:Fixnum = BoxFixnum v29 IncrCounter inline_cfunc_optimized_send_count @@ -4220,8 +4225,8 @@ mod hir_opt_tests { Jump bb2(v4) bb2(v6:BasicObject): v10:HashExact = NewHash - PatchPoint MethodRedefined(Hash@0x1000, dup@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Hash@0x1000) + PatchPoint MethodRedefined(Hash@0x1000, dup@0x1008, cme:0x1010) v22:BasicObject = CCallWithFrame v10, :Kernel#dup@0x1038 v14:BasicObject = SendWithoutBlock v22, :freeze # SendFallbackReason: Uncategorized(opt_send_without_block) CheckInterrupts @@ -4313,8 +4318,8 @@ mod hir_opt_tests { Jump bb2(v4) bb2(v6:BasicObject): v10:ArrayExact = NewArray - PatchPoint MethodRedefined(Array@0x1000, dup@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Array@0x1000) + PatchPoint MethodRedefined(Array@0x1000, dup@0x1008, cme:0x1010) v22:BasicObject = CCallWithFrame v10, :Kernel#dup@0x1038 v14:BasicObject = SendWithoutBlock v22, :freeze # SendFallbackReason: Uncategorized(opt_send_without_block) CheckInterrupts @@ -4407,8 +4412,8 @@ mod hir_opt_tests { bb2(v6:BasicObject): v10:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) v11:StringExact = StringCopy v10 - PatchPoint MethodRedefined(String@0x1008, dup@0x1010, cme:0x1018) PatchPoint NoSingletonClass(String@0x1008) + PatchPoint MethodRedefined(String@0x1008, dup@0x1010, cme:0x1018) v23:BasicObject = CCallWithFrame v11, :String#dup@0x1040 v15:BasicObject = SendWithoutBlock v23, :freeze # SendFallbackReason: Uncategorized(opt_send_without_block) CheckInterrupts @@ -4502,8 +4507,8 @@ mod hir_opt_tests { bb2(v6:BasicObject): v10:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) v11:StringExact = StringCopy v10 - PatchPoint MethodRedefined(String@0x1008, dup@0x1010, cme:0x1018) PatchPoint NoSingletonClass(String@0x1008) + PatchPoint MethodRedefined(String@0x1008, dup@0x1010, cme:0x1018) v23:BasicObject = CCallWithFrame v11, :String#dup@0x1040 v15:BasicObject = SendWithoutBlock v23, :-@ # SendFallbackReason: Uncategorized(opt_send_without_block) CheckInterrupts @@ -4643,8 +4648,8 @@ mod hir_opt_tests { bb2(v8:BasicObject, v9:BasicObject): v13:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) v26:ArrayExact = GuardType v9, ArrayExact - PatchPoint MethodRedefined(Array@0x1008, to_s@0x1010, cme:0x1018) PatchPoint NoSingletonClass(Array@0x1008) + PatchPoint MethodRedefined(Array@0x1008, to_s@0x1010, cme:0x1018) v31:BasicObject = CCallWithFrame v26, :Array#to_s@0x1040 v19:String = AnyToString v9, str: v31 v21:StringExact = StringConcat v13, v19 @@ -4732,12 +4737,17 @@ mod hir_opt_tests { PatchPoint StableConstantNames(0x1000, S) v23:ArrayExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) v13:Fixnum[0] = Const Value(0) - PatchPoint MethodRedefined(Array@0x1010, []@0x1018, cme:0x1020) PatchPoint NoSingletonClass(Array@0x1010) - v27:BasicObject = ArrayArefFixnum v23, v13 + PatchPoint MethodRedefined(Array@0x1010, []@0x1018, cme:0x1020) + v27:CInt64[0] = UnboxFixnum v13 + v28:CInt64 = ArrayLength v23 + v29:CInt64[0] = GuardLess v27, v28 + v30:CInt64[0] = Const CInt64(0) + v31:CInt64[0] = GuardGreaterEq v29, v30 + v32:BasicObject = ArrayAref v23, v31 IncrCounter inline_cfunc_optimized_send_count CheckInterrupts - Return v27 + Return v32 "); // TODO(max): Check the result of `S[0] = 5; test` using `inspect` to make sure that we // actually do the load at run-time. @@ -4761,12 +4771,17 @@ mod hir_opt_tests { PatchPoint BOPRedefined(ARRAY_REDEFINED_OP_FLAG, BOP_FREEZE) v11:ArrayExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) v13:Fixnum[1] = Const Value(1) - PatchPoint MethodRedefined(Array@0x1008, []@0x1010, cme:0x1018) PatchPoint NoSingletonClass(Array@0x1008) - v26:Fixnum[5] = Const Value(5) + PatchPoint MethodRedefined(Array@0x1008, []@0x1010, cme:0x1018) + v24:CInt64[1] = UnboxFixnum v13 + v25:CInt64 = ArrayLength v11 + v26:CInt64[1] = GuardLess v24, v25 + v27:CInt64[0] = Const CInt64(0) + v28:CInt64[1] = GuardGreaterEq v26, v27 + v31:Fixnum[5] = Const Value(5) IncrCounter inline_cfunc_optimized_send_count CheckInterrupts - Return v26 + Return v31 "); } @@ -4788,12 +4803,17 @@ mod hir_opt_tests { PatchPoint BOPRedefined(ARRAY_REDEFINED_OP_FLAG, BOP_FREEZE) v11:ArrayExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) v13:Fixnum[-3] = Const Value(-3) - PatchPoint MethodRedefined(Array@0x1008, []@0x1010, cme:0x1018) PatchPoint NoSingletonClass(Array@0x1008) - v26:Fixnum[4] = Const Value(4) + PatchPoint MethodRedefined(Array@0x1008, []@0x1010, cme:0x1018) + v24:CInt64[-3] = UnboxFixnum v13 + v25:CInt64 = ArrayLength v11 + v26:CInt64[-3] = GuardLess v24, v25 + v27:CInt64[0] = Const CInt64(0) + v28:CInt64[-3] = GuardGreaterEq v26, v27 + v31:Fixnum[4] = Const Value(4) IncrCounter inline_cfunc_optimized_send_count CheckInterrupts - Return v26 + Return v31 "); } @@ -4815,12 +4835,17 @@ mod hir_opt_tests { PatchPoint BOPRedefined(ARRAY_REDEFINED_OP_FLAG, BOP_FREEZE) v11:ArrayExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) v13:Fixnum[-10] = Const Value(-10) - PatchPoint MethodRedefined(Array@0x1008, []@0x1010, cme:0x1018) PatchPoint NoSingletonClass(Array@0x1008) - v26:NilClass = Const Value(nil) + PatchPoint MethodRedefined(Array@0x1008, []@0x1010, cme:0x1018) + v24:CInt64[-10] = UnboxFixnum v13 + v25:CInt64 = ArrayLength v11 + v26:CInt64[-10] = GuardLess v24, v25 + v27:CInt64[0] = Const CInt64(0) + v28:CInt64[-10] = GuardGreaterEq v26, v27 + v31:NilClass = Const Value(nil) IncrCounter inline_cfunc_optimized_send_count CheckInterrupts - Return v26 + Return v31 "); } @@ -4842,12 +4867,17 @@ mod hir_opt_tests { PatchPoint BOPRedefined(ARRAY_REDEFINED_OP_FLAG, BOP_FREEZE) v11:ArrayExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) v13:Fixnum[10] = Const Value(10) - PatchPoint MethodRedefined(Array@0x1008, []@0x1010, cme:0x1018) PatchPoint NoSingletonClass(Array@0x1008) - v26:NilClass = Const Value(nil) + PatchPoint MethodRedefined(Array@0x1008, []@0x1010, cme:0x1018) + v24:CInt64[10] = UnboxFixnum v13 + v25:CInt64 = ArrayLength v11 + v26:CInt64[10] = GuardLess v24, v25 + v27:CInt64[0] = Const CInt64(0) + v28:CInt64[10] = GuardGreaterEq v26, v27 + v31:NilClass = Const Value(nil) IncrCounter inline_cfunc_optimized_send_count CheckInterrupts - Return v26 + Return v31 "); } @@ -4872,8 +4902,8 @@ mod hir_opt_tests { PatchPoint BOPRedefined(ARRAY_REDEFINED_OP_FLAG, BOP_FREEZE) v11:ArrayExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) v13:Fixnum[10] = Const Value(10) - PatchPoint MethodRedefined(Array@0x1008, []@0x1010, cme:0x1018) PatchPoint NoSingletonClass(Array@0x1008) + PatchPoint MethodRedefined(Array@0x1008, []@0x1010, cme:0x1018) v23:BasicObject = SendWithoutBlockDirect v11, :[] (0x1040), v13 CheckInterrupts Return v23 @@ -4930,8 +4960,8 @@ mod hir_opt_tests { bb2(v6:BasicObject): v10:ArrayExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) v11:ArrayExact = ArrayDup v10 - PatchPoint MethodRedefined(Array@0x1008, max@0x1010, cme:0x1018) PatchPoint NoSingletonClass(Array@0x1008) + PatchPoint MethodRedefined(Array@0x1008, max@0x1010, cme:0x1018) v20:BasicObject = SendWithoutBlockDirect v11, :max (0x1040) CheckInterrupts Return v20 @@ -5007,14 +5037,14 @@ mod hir_opt_tests { Jump bb2(v4) bb2(v6:BasicObject): PatchPoint SingleRactorMode - PatchPoint MethodRedefined(Object@0x1000, zero@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint MethodRedefined(Object@0x1000, zero@0x1008, cme:0x1010) v22:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] IncrCounter inline_iseq_optimized_send_count v30:StaticSymbol[:b] = Const Value(VALUE(0x1038)) PatchPoint SingleRactorMode - PatchPoint MethodRedefined(Object@0x1000, one@0x1040, cme:0x1048) PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint MethodRedefined(Object@0x1000, one@0x1040, cme:0x1048) v27:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] IncrCounter inline_iseq_optimized_send_count CheckInterrupts @@ -5094,8 +5124,8 @@ mod hir_opt_tests { PatchPoint StableConstantNames(0x1000, Foo) v22:Class[Foo@0x1008] = Const Value(VALUE(0x1008)) v13:Fixnum[100] = Const Value(100) - PatchPoint MethodRedefined(Class@0x1010, identity@0x1018, cme:0x1020) PatchPoint NoSingletonClass(Class@0x1010) + PatchPoint MethodRedefined(Class@0x1010, identity@0x1018, cme:0x1020) IncrCounter inline_iseq_optimized_send_count CheckInterrupts Return v13 @@ -5384,8 +5414,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): - PatchPoint MethodRedefined(String@0x1000, nil?@0x1008, cme:0x1010) PatchPoint NoSingletonClass(String@0x1000) + PatchPoint MethodRedefined(String@0x1000, nil?@0x1008, cme:0x1010) v23:StringExact = GuardType v9, StringExact v24:FalseClass = Const Value(false) IncrCounter inline_cfunc_optimized_send_count @@ -5412,8 +5442,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): - PatchPoint MethodRedefined(Array@0x1000, !@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Array@0x1000) + PatchPoint MethodRedefined(Array@0x1000, !@0x1008, cme:0x1010) v23:ArrayExact = GuardType v9, ArrayExact v24:FalseClass = Const Value(false) IncrCounter inline_cfunc_optimized_send_count @@ -5534,8 +5564,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): - PatchPoint MethodRedefined(Array@0x1000, empty?@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Array@0x1000) + PatchPoint MethodRedefined(Array@0x1000, empty?@0x1008, cme:0x1010) v23:ArrayExact = GuardType v9, ArrayExact v24:CInt64 = ArrayLength v23 v25:CInt64[0] = Const CInt64(0) @@ -5565,8 +5595,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): - PatchPoint MethodRedefined(Hash@0x1000, empty?@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Hash@0x1000) + PatchPoint MethodRedefined(Hash@0x1000, empty?@0x1008, cme:0x1010) v23:HashExact = GuardType v9, HashExact IncrCounter inline_cfunc_optimized_send_count v25:BoolExact = CCall v23, :Hash#empty?@0x1038 @@ -5595,8 +5625,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - PatchPoint MethodRedefined(C@0x1000, ==@0x1008, cme:0x1010) PatchPoint NoSingletonClass(C@0x1000) + PatchPoint MethodRedefined(C@0x1000, ==@0x1008, cme:0x1010) v27:HeapObject[class_exact:C] = GuardType v11, HeapObject[class_exact:C] v28:CBool = IsBitEqual v27, v12 v29:BoolExact = BoxBool v28 @@ -5683,8 +5713,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): - PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) v18:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] IncrCounter inline_iseq_optimized_send_count v21:NilClass = Const Value(nil) @@ -5719,8 +5749,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): - PatchPoint MethodRedefined(C@0x1000, foo@0x1008, cme:0x1010) PatchPoint NoSingletonClass(C@0x1000) + PatchPoint MethodRedefined(C@0x1000, foo@0x1008, cme:0x1010) v21:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] v24:CShape = LoadField v21, :_shape_id@0x1038 v25:CShape[0x1039] = GuardBitEquals v24, CShape(0x1039) @@ -5759,8 +5789,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): - PatchPoint MethodRedefined(C@0x1000, foo@0x1008, cme:0x1010) PatchPoint NoSingletonClass(C@0x1000) + PatchPoint MethodRedefined(C@0x1000, foo@0x1008, cme:0x1010) v21:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] v24:CShape = LoadField v21, :_shape_id@0x1038 v25:CShape[0x1039] = GuardBitEquals v24, CShape(0x1039) @@ -5986,8 +6016,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): - PatchPoint MethodRedefined(C@0x1000, foo@0x1008, cme:0x1010) PatchPoint NoSingletonClass(C@0x1000) + PatchPoint MethodRedefined(C@0x1000, foo@0x1008, cme:0x1010) v21:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] IncrCounter getivar_fallback_too_complex v22:BasicObject = GetIvar v21, :@foo @@ -6014,8 +6044,8 @@ mod hir_opt_tests { bb2(v6:BasicObject): v10:ArrayExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) v11:ArrayExact = ArrayDup v10 - PatchPoint MethodRedefined(Array@0x1008, map@0x1010, cme:0x1018) PatchPoint NoSingletonClass(Array@0x1008) + PatchPoint MethodRedefined(Array@0x1008, map@0x1010, cme:0x1018) v21:BasicObject = CCallWithFrame v11, :Array#map@0x1040, block=0x1048 CheckInterrupts Return v21 @@ -6056,8 +6086,8 @@ mod hir_opt_tests { PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1010, B) v39:ArrayExact[VALUE(0x1018)] = Const Value(VALUE(0x1018)) - PatchPoint MethodRedefined(Array@0x1020, zip@0x1028, cme:0x1030) PatchPoint NoSingletonClass(Array@0x1020) + PatchPoint MethodRedefined(Array@0x1020, zip@0x1028, cme:0x1030) v43:BasicObject = CCallVariadic v36, :zip@0x1058, v39 v25:BasicObject = GetLocal :result, l0, EP@3 v29:BasicObject = GetLocal :result, l0, EP@3 @@ -6144,8 +6174,8 @@ mod hir_opt_tests { PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, O) v20:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - PatchPoint MethodRedefined(C@0x1010, foo@0x1018, cme:0x1020) PatchPoint NoSingletonClass(C@0x1010) + PatchPoint MethodRedefined(C@0x1010, foo@0x1018, cme:0x1020) v25:CShape = LoadField v20, :_shape_id@0x1048 v26:CShape[0x1049] = GuardBitEquals v25, CShape(0x1049) v27:NilClass = Const Value(nil) @@ -6179,8 +6209,8 @@ mod hir_opt_tests { PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, O) v20:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - PatchPoint MethodRedefined(C@0x1010, foo@0x1018, cme:0x1020) PatchPoint NoSingletonClass(C@0x1010) + PatchPoint MethodRedefined(C@0x1010, foo@0x1018, cme:0x1020) v25:CShape = LoadField v20, :_shape_id@0x1048 v26:CShape[0x1049] = GuardBitEquals v25, CShape(0x1049) v27:NilClass = Const Value(nil) @@ -6211,8 +6241,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): - PatchPoint MethodRedefined(C@0x1000, foo@0x1008, cme:0x1010) PatchPoint NoSingletonClass(C@0x1000) + PatchPoint MethodRedefined(C@0x1000, foo@0x1008, cme:0x1010) v21:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] v24:CShape = LoadField v21, :_shape_id@0x1038 v25:CShape[0x1039] = GuardBitEquals v24, CShape(0x1039) @@ -6244,8 +6274,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): - PatchPoint MethodRedefined(C@0x1000, foo@0x1008, cme:0x1010) PatchPoint NoSingletonClass(C@0x1000) + PatchPoint MethodRedefined(C@0x1000, foo@0x1008, cme:0x1010) v21:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] v24:CShape = LoadField v21, :_shape_id@0x1038 v25:CShape[0x1039] = GuardBitEquals v24, CShape(0x1039) @@ -6346,8 +6376,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): - PatchPoint MethodRedefined(C@0x1000, foo@0x1008, cme:0x1010) PatchPoint NoSingletonClass(C@0x1000) + PatchPoint MethodRedefined(C@0x1000, foo@0x1008, cme:0x1010) v21:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] v22:BasicObject = LoadField v21, :foo@0x1038 CheckInterrupts @@ -6374,8 +6404,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): - PatchPoint MethodRedefined(C@0x1000, foo@0x1008, cme:0x1010) PatchPoint NoSingletonClass(C@0x1000) + PatchPoint MethodRedefined(C@0x1000, foo@0x1008, cme:0x1010) v21:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] v22:CPtr = LoadField v21, :_as_heap@0x1038 v23:BasicObject = LoadField v22, :foo@0x1039 @@ -6406,8 +6436,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): - PatchPoint MethodRedefined(C@0x1000, foo@0x1008, cme:0x1010) PatchPoint NoSingletonClass(C@0x1000) + PatchPoint MethodRedefined(C@0x1000, foo@0x1008, cme:0x1010) v25:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] v18:Fixnum[5] = Const Value(5) CheckInterrupts @@ -6436,8 +6466,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - PatchPoint MethodRedefined(C@0x1000, foo=@0x1008, cme:0x1010) PatchPoint NoSingletonClass(C@0x1000) + PatchPoint MethodRedefined(C@0x1000, foo=@0x1008, cme:0x1010) v29:HeapObject[class_exact:C] = GuardType v11, HeapObject[class_exact:C] v30:HeapObject[class_exact:C] = GuardNotFrozen v29 StoreField v30, :foo=@0x1038, v12 @@ -6468,8 +6498,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - PatchPoint MethodRedefined(C@0x1000, foo=@0x1008, cme:0x1010) PatchPoint NoSingletonClass(C@0x1000) + PatchPoint MethodRedefined(C@0x1000, foo=@0x1008, cme:0x1010) v29:HeapObject[class_exact:C] = GuardType v11, HeapObject[class_exact:C] v30:HeapObject[class_exact:C] = GuardNotFrozen v29 v31:CPtr = LoadField v30, :_as_heap@0x1038 @@ -6496,8 +6526,8 @@ mod hir_opt_tests { Jump bb2(v4) bb2(v6:BasicObject): v10:ArrayExact = NewArray - PatchPoint MethodRedefined(Array@0x1000, reverse@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Array@0x1000) + PatchPoint MethodRedefined(Array@0x1000, reverse@0x1008, cme:0x1010) v20:ArrayExact = CCallWithFrame v10, :Array#reverse@0x1038 CheckInterrupts Return v20 @@ -6523,8 +6553,8 @@ mod hir_opt_tests { Jump bb2(v4) bb2(v6:BasicObject): v10:ArrayExact = NewArray - PatchPoint MethodRedefined(Array@0x1000, reverse@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Array@0x1000) + PatchPoint MethodRedefined(Array@0x1000, reverse@0x1008, cme:0x1010) v16:Fixnum[5] = Const Value(5) CheckInterrupts Return v16 @@ -6549,8 +6579,8 @@ mod hir_opt_tests { v10:ArrayExact = NewArray v12:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) v13:StringExact = StringCopy v12 - PatchPoint MethodRedefined(Array@0x1008, join@0x1010, cme:0x1018) PatchPoint NoSingletonClass(Array@0x1008) + PatchPoint MethodRedefined(Array@0x1008, join@0x1010, cme:0x1018) v23:StringExact = CCallVariadic v10, :Array#join@0x1040, v13 CheckInterrupts Return v23 @@ -6574,8 +6604,8 @@ mod hir_opt_tests { bb2(v6:BasicObject): v10:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) v11:StringExact = StringCopy v10 - PatchPoint MethodRedefined(String@0x1008, to_s@0x1010, cme:0x1018) PatchPoint NoSingletonClass(String@0x1008) + PatchPoint MethodRedefined(String@0x1008, to_s@0x1010, cme:0x1018) IncrCounter inline_cfunc_optimized_send_count CheckInterrupts Return v11 @@ -6599,8 +6629,8 @@ mod hir_opt_tests { bb2(v6:BasicObject): v10:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) v11:StringExact = StringCopy v10 - PatchPoint MethodRedefined(String@0x1008, to_s@0x1010, cme:0x1018) PatchPoint NoSingletonClass(String@0x1008) + PatchPoint MethodRedefined(String@0x1008, to_s@0x1010, cme:0x1018) IncrCounter inline_cfunc_optimized_send_count CheckInterrupts Return v11 @@ -6624,8 +6654,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): - PatchPoint MethodRedefined(String@0x1000, to_s@0x1008, cme:0x1010) PatchPoint NoSingletonClass(String@0x1000) + PatchPoint MethodRedefined(String@0x1000, to_s@0x1008, cme:0x1010) v22:StringExact = GuardType v9, StringExact IncrCounter inline_cfunc_optimized_send_count CheckInterrupts @@ -6733,12 +6763,17 @@ mod hir_opt_tests { v13:ArrayExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) v14:ArrayExact = ArrayDup v13 v19:Fixnum[0] = Const Value(0) - PatchPoint MethodRedefined(Array@0x1008, []@0x1010, cme:0x1018) PatchPoint NoSingletonClass(Array@0x1008) - v30:BasicObject = ArrayArefFixnum v14, v19 + PatchPoint MethodRedefined(Array@0x1008, []@0x1010, cme:0x1018) + v30:CInt64[0] = UnboxFixnum v19 + v31:CInt64 = ArrayLength v14 + v32:CInt64[0] = GuardLess v30, v31 + v33:CInt64[0] = Const CInt64(0) + v34:CInt64[0] = GuardGreaterEq v32, v33 + v35:BasicObject = ArrayAref v14, v34 IncrCounter inline_cfunc_optimized_send_count CheckInterrupts - Return v30 + Return v35 "); } @@ -6762,14 +6797,19 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - PatchPoint MethodRedefined(Array@0x1000, []@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Array@0x1000) + PatchPoint MethodRedefined(Array@0x1000, []@0x1008, cme:0x1010) v27:ArrayExact = GuardType v11, ArrayExact v28:Fixnum = GuardType v12, Fixnum - v29:BasicObject = ArrayArefFixnum v27, v28 + v29:CInt64 = UnboxFixnum v28 + v30:CInt64 = ArrayLength v27 + v31:CInt64 = GuardLess v29, v30 + v32:CInt64[0] = Const CInt64(0) + v33:CInt64 = GuardGreaterEq v31, v32 + v34:BasicObject = ArrayAref v27, v33 IncrCounter inline_cfunc_optimized_send_count CheckInterrupts - Return v29 + Return v34 "); } @@ -6794,14 +6834,19 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - PatchPoint MethodRedefined(C@0x1000, []@0x1008, cme:0x1010) PatchPoint NoSingletonClass(C@0x1000) + PatchPoint MethodRedefined(C@0x1000, []@0x1008, cme:0x1010) v27:ArraySubclass[class_exact:C] = GuardType v11, ArraySubclass[class_exact:C] v28:Fixnum = GuardType v12, Fixnum - v29:BasicObject = ArrayArefFixnum v27, v28 + v29:CInt64 = UnboxFixnum v28 + v30:CInt64 = ArrayLength v27 + v31:CInt64 = GuardLess v29, v30 + v32:CInt64[0] = Const CInt64(0) + v33:CInt64 = GuardGreaterEq v31, v32 + v34:BasicObject = ArrayAref v27, v33 IncrCounter inline_cfunc_optimized_send_count CheckInterrupts - Return v29 + Return v34 "); } @@ -6828,8 +6873,8 @@ mod hir_opt_tests { v13:HashExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) v14:HashExact = HashDup v13 v19:Fixnum[1] = Const Value(1) - PatchPoint MethodRedefined(Hash@0x1008, []@0x1010, cme:0x1018) PatchPoint NoSingletonClass(Hash@0x1008) + PatchPoint MethodRedefined(Hash@0x1008, []@0x1010, cme:0x1018) v30:BasicObject = HashAref v14, v19 IncrCounter inline_cfunc_optimized_send_count CheckInterrupts @@ -6857,8 +6902,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - PatchPoint MethodRedefined(Hash@0x1000, []@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Hash@0x1000) + PatchPoint MethodRedefined(Hash@0x1000, []@0x1008, cme:0x1010) v27:HashExact = GuardType v11, HashExact v28:BasicObject = HashAref v27, v12 IncrCounter inline_cfunc_optimized_send_count @@ -6888,8 +6933,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - PatchPoint MethodRedefined(C@0x1000, []@0x1008, cme:0x1010) PatchPoint NoSingletonClass(C@0x1000) + PatchPoint MethodRedefined(C@0x1000, []@0x1008, cme:0x1010) v27:HashSubclass[class_exact:C] = GuardType v11, HashSubclass[class_exact:C] v28:BasicObject = CCallWithFrame v27, :Hash#[]@0x1038, v12 CheckInterrupts @@ -6918,8 +6963,8 @@ mod hir_opt_tests { PatchPoint StableConstantNames(0x1000, H) v23:HashExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) v13:StaticSymbol[:a] = Const Value(VALUE(0x1010)) - PatchPoint MethodRedefined(Hash@0x1018, []@0x1020, cme:0x1028) PatchPoint NoSingletonClass(Hash@0x1018) + PatchPoint MethodRedefined(Hash@0x1018, []@0x1020, cme:0x1028) v27:BasicObject = HashAref v23, v13 IncrCounter inline_cfunc_optimized_send_count CheckInterrupts @@ -6951,8 +6996,8 @@ mod hir_opt_tests { PatchPoint NoEPEscape(test) v22:Fixnum[1] = Const Value(1) v24:Fixnum[3] = Const Value(3) - PatchPoint MethodRedefined(Hash@0x1000, []=@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Hash@0x1000) + PatchPoint MethodRedefined(Hash@0x1000, []=@0x1008, cme:0x1010) HashAset v13, v22, v24 IncrCounter inline_cfunc_optimized_send_count CheckInterrupts @@ -6981,8 +7026,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v7, v8, v9, v10) bb2(v12:BasicObject, v13:BasicObject, v14:BasicObject, v15:BasicObject): - PatchPoint MethodRedefined(Hash@0x1000, []=@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Hash@0x1000) + PatchPoint MethodRedefined(Hash@0x1000, []=@0x1008, cme:0x1010) v35:HashExact = GuardType v13, HashExact HashAset v35, v14, v15 IncrCounter inline_cfunc_optimized_send_count @@ -7013,8 +7058,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v7, v8, v9, v10) bb2(v12:BasicObject, v13:BasicObject, v14:BasicObject, v15:BasicObject): - PatchPoint MethodRedefined(C@0x1000, []=@0x1008, cme:0x1010) PatchPoint NoSingletonClass(C@0x1000) + PatchPoint MethodRedefined(C@0x1000, []=@0x1008, cme:0x1010) v35:HashSubclass[class_exact:C] = GuardType v13, HashSubclass[class_exact:C] v36:BasicObject = CCallWithFrame v35, :Hash#[]=@0x1038, v14, v15 CheckInterrupts @@ -7041,8 +7086,8 @@ mod hir_opt_tests { PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, Thread) v20:Class[Thread@0x1008] = Const Value(VALUE(0x1008)) - PatchPoint MethodRedefined(Class@0x1010, current@0x1018, cme:0x1020) PatchPoint NoSingletonClass(Class@0x1010) + PatchPoint MethodRedefined(Class@0x1010, current@0x1018, cme:0x1020) v24:CPtr = LoadEC v25:CPtr = LoadField v24, :thread_ptr@0x1048 v26:BasicObject = LoadField v25, :self@0x1049 @@ -7073,16 +7118,16 @@ mod hir_opt_tests { bb2(v8:BasicObject, v9:BasicObject): v16:Fixnum[1] = Const Value(1) v18:Fixnum[10] = Const Value(10) - PatchPoint MethodRedefined(Array@0x1000, []=@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Array@0x1000) + PatchPoint MethodRedefined(Array@0x1000, []=@0x1008, cme:0x1010) v31:ArrayExact = GuardType v9, ArrayExact v32:ArrayExact = GuardNotFrozen v31 v33:ArrayExact = GuardNotShared v32 - v34:CInt64 = UnboxFixnum v16 + v34:CInt64[1] = UnboxFixnum v16 v35:CInt64 = ArrayLength v33 - v36:CInt64 = GuardLess v34, v35 + v36:CInt64[1] = GuardLess v34, v35 v37:CInt64[0] = Const CInt64(0) - v38:CInt64 = GuardGreaterEq v36, v37 + v38:CInt64[1] = GuardGreaterEq v36, v37 ArrayAset v33, v38, v18 WriteBarrier v33, v18 IncrCounter inline_cfunc_optimized_send_count @@ -7112,8 +7157,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v7, v8, v9, v10) bb2(v12:BasicObject, v13:BasicObject, v14:BasicObject, v15:BasicObject): - PatchPoint MethodRedefined(Array@0x1000, []=@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Array@0x1000) + PatchPoint MethodRedefined(Array@0x1000, []=@0x1008, cme:0x1010) v35:ArrayExact = GuardType v13, ArrayExact v36:Fixnum = GuardType v14, Fixnum v37:ArrayExact = GuardNotFrozen v35 @@ -7154,8 +7199,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v7, v8, v9, v10) bb2(v12:BasicObject, v13:BasicObject, v14:BasicObject, v15:BasicObject): - PatchPoint MethodRedefined(MyArray@0x1000, []=@0x1008, cme:0x1010) PatchPoint NoSingletonClass(MyArray@0x1000) + PatchPoint MethodRedefined(MyArray@0x1000, []=@0x1008, cme:0x1010) v35:ArraySubclass[class_exact:MyArray] = GuardType v13, ArraySubclass[class_exact:MyArray] v36:BasicObject = CCallVariadic v35, :Array#[]=@0x1038, v14, v15 CheckInterrupts @@ -7183,8 +7228,8 @@ mod hir_opt_tests { Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): v14:Fixnum[1] = Const Value(1) - PatchPoint MethodRedefined(Array@0x1000, <<@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Array@0x1000) + PatchPoint MethodRedefined(Array@0x1000, <<@0x1008, cme:0x1010) v25:ArrayExact = GuardType v9, ArrayExact ArrayPush v25, v14 IncrCounter inline_cfunc_optimized_send_count @@ -7213,8 +7258,8 @@ mod hir_opt_tests { Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): v14:Fixnum[1] = Const Value(1) - PatchPoint MethodRedefined(Array@0x1000, push@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Array@0x1000) + PatchPoint MethodRedefined(Array@0x1000, push@0x1008, cme:0x1010) v24:ArrayExact = GuardType v9, ArrayExact ArrayPush v24, v14 IncrCounter inline_cfunc_optimized_send_count @@ -7245,8 +7290,8 @@ mod hir_opt_tests { v14:Fixnum[1] = Const Value(1) v16:Fixnum[2] = Const Value(2) v18:Fixnum[3] = Const Value(3) - PatchPoint MethodRedefined(Array@0x1000, push@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Array@0x1000) + PatchPoint MethodRedefined(Array@0x1000, push@0x1008, cme:0x1010) v28:ArrayExact = GuardType v9, ArrayExact v29:BasicObject = CCallVariadic v28, :Array#push@0x1038, v14, v16, v18 CheckInterrupts @@ -7272,8 +7317,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): - PatchPoint MethodRedefined(Array@0x1000, length@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Array@0x1000) + PatchPoint MethodRedefined(Array@0x1000, length@0x1008, cme:0x1010) v23:ArrayExact = GuardType v9, ArrayExact v24:CInt64 = ArrayLength v23 v25:Fixnum = BoxFixnum v24 @@ -7301,8 +7346,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): - PatchPoint MethodRedefined(Array@0x1000, size@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Array@0x1000) + PatchPoint MethodRedefined(Array@0x1000, size@0x1008, cme:0x1010) v23:ArrayExact = GuardType v9, ArrayExact v24:CInt64 = ArrayLength v23 v25:Fixnum = BoxFixnum v24 @@ -7331,8 +7376,8 @@ mod hir_opt_tests { Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): v14:RegexpExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) - PatchPoint MethodRedefined(String@0x1008, =~@0x1010, cme:0x1018) PatchPoint NoSingletonClass(String@0x1008) + PatchPoint MethodRedefined(String@0x1008, =~@0x1010, cme:0x1018) v25:StringExact = GuardType v9, StringExact v26:BasicObject = CCallWithFrame v25, :String#=~@0x1040, v14 CheckInterrupts @@ -7358,8 +7403,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - PatchPoint MethodRedefined(String@0x1000, getbyte@0x1008, cme:0x1010) PatchPoint NoSingletonClass(String@0x1000) + PatchPoint MethodRedefined(String@0x1000, getbyte@0x1008, cme:0x1010) v26:StringExact = GuardType v11, StringExact v27:Fixnum = GuardType v12, Fixnum v28:CInt64 = UnboxFixnum v27 @@ -7395,8 +7440,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - PatchPoint MethodRedefined(String@0x1000, getbyte@0x1008, cme:0x1010) PatchPoint NoSingletonClass(String@0x1000) + PatchPoint MethodRedefined(String@0x1000, getbyte@0x1008, cme:0x1010) v30:StringExact = GuardType v11, StringExact v31:Fixnum = GuardType v12, Fixnum v32:CInt64 = UnboxFixnum v31 @@ -7432,8 +7477,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v7, v8, v9, v10) bb2(v12:BasicObject, v13:BasicObject, v14:BasicObject, v15:BasicObject): - PatchPoint MethodRedefined(String@0x1000, setbyte@0x1008, cme:0x1010) PatchPoint NoSingletonClass(String@0x1000) + PatchPoint MethodRedefined(String@0x1000, setbyte@0x1008, cme:0x1010) v30:StringExact = GuardType v13, StringExact v31:Fixnum = GuardType v14, Fixnum v32:Fixnum = GuardType v15, Fixnum @@ -7473,8 +7518,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v7, v8, v9, v10) bb2(v12:BasicObject, v13:BasicObject, v14:BasicObject, v15:BasicObject): - PatchPoint MethodRedefined(MyString@0x1000, setbyte@0x1008, cme:0x1010) PatchPoint NoSingletonClass(MyString@0x1000) + PatchPoint MethodRedefined(MyString@0x1000, setbyte@0x1008, cme:0x1010) v30:StringSubclass[class_exact:MyString] = GuardType v13, StringSubclass[class_exact:MyString] v31:Fixnum = GuardType v14, Fixnum v32:Fixnum = GuardType v15, Fixnum @@ -7512,8 +7557,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v7, v8, v9, v10) bb2(v12:BasicObject, v13:BasicObject, v14:BasicObject, v15:BasicObject): - PatchPoint MethodRedefined(String@0x1000, setbyte@0x1008, cme:0x1010) PatchPoint NoSingletonClass(String@0x1000) + PatchPoint MethodRedefined(String@0x1000, setbyte@0x1008, cme:0x1010) v30:StringExact = GuardType v13, StringExact v31:BasicObject = CCallWithFrame v30, :String#setbyte@0x1038, v14, v15 CheckInterrupts @@ -7540,8 +7585,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): - PatchPoint MethodRedefined(String@0x1000, empty?@0x1008, cme:0x1010) PatchPoint NoSingletonClass(String@0x1000) + PatchPoint MethodRedefined(String@0x1000, empty?@0x1008, cme:0x1010) v23:StringExact = GuardType v9, StringExact v24:CInt64 = LoadField v23, :len@0x1038 v25:CInt64[0] = Const CInt64(0) @@ -7573,8 +7618,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): - PatchPoint MethodRedefined(String@0x1000, empty?@0x1008, cme:0x1010) PatchPoint NoSingletonClass(String@0x1000) + PatchPoint MethodRedefined(String@0x1000, empty?@0x1008, cme:0x1010) v27:StringExact = GuardType v9, StringExact IncrCounter inline_cfunc_optimized_send_count v19:Fixnum[4] = Const Value(4) @@ -7869,8 +7914,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - PatchPoint MethodRedefined(String@0x1000, <<@0x1008, cme:0x1010) PatchPoint NoSingletonClass(String@0x1000) + PatchPoint MethodRedefined(String@0x1000, <<@0x1008, cme:0x1010) v27:StringExact = GuardType v11, StringExact v28:String = GuardType v12, String v29:StringExact = StringAppend v27, v28 @@ -7898,8 +7943,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - PatchPoint MethodRedefined(String@0x1000, <<@0x1008, cme:0x1010) PatchPoint NoSingletonClass(String@0x1000) + PatchPoint MethodRedefined(String@0x1000, <<@0x1008, cme:0x1010) v27:StringExact = GuardType v11, StringExact v28:Fixnum = GuardType v12, Fixnum v29:StringExact = StringAppendCodepoint v27, v28 @@ -7929,8 +7974,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - PatchPoint MethodRedefined(String@0x1000, <<@0x1008, cme:0x1010) PatchPoint NoSingletonClass(String@0x1000) + PatchPoint MethodRedefined(String@0x1000, <<@0x1008, cme:0x1010) v27:StringExact = GuardType v11, StringExact v28:String = GuardType v12, String v29:StringExact = StringAppend v27, v28 @@ -7960,8 +8005,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - PatchPoint MethodRedefined(MyString@0x1000, <<@0x1008, cme:0x1010) PatchPoint NoSingletonClass(MyString@0x1000) + PatchPoint MethodRedefined(MyString@0x1000, <<@0x1008, cme:0x1010) v27:StringSubclass[class_exact:MyString] = GuardType v11, StringSubclass[class_exact:MyString] v28:BasicObject = CCallWithFrame v27, :String#<<@0x1038, v12 CheckInterrupts @@ -7987,8 +8032,8 @@ mod hir_opt_tests { v10:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) v11:StringExact = StringCopy v10 v13:StaticSymbol[:a] = Const Value(VALUE(0x1008)) - PatchPoint MethodRedefined(String@0x1010, <<@0x1018, cme:0x1020) PatchPoint NoSingletonClass(String@0x1010) + PatchPoint MethodRedefined(String@0x1010, <<@0x1018, cme:0x1020) v24:BasicObject = CCallWithFrame v11, :String#<<@0x1048, v13 CheckInterrupts Return v24 @@ -8038,8 +8083,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): - PatchPoint MethodRedefined(String@0x1000, ascii_only?@0x1008, cme:0x1010) PatchPoint NoSingletonClass(String@0x1000) + PatchPoint MethodRedefined(String@0x1000, ascii_only?@0x1008, cme:0x1010) v22:StringExact = GuardType v9, StringExact IncrCounter inline_cfunc_optimized_send_count v24:BoolExact = CCall v22, :String#ascii_only?@0x1038 @@ -8265,8 +8310,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): - PatchPoint MethodRedefined(Hash@0x1000, size@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Hash@0x1000) + PatchPoint MethodRedefined(Hash@0x1000, size@0x1008, cme:0x1010) v23:HashExact = GuardType v9, HashExact IncrCounter inline_cfunc_optimized_send_count v25:Fixnum = CCall v23, :Hash#size@0x1038 @@ -8295,8 +8340,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): - PatchPoint MethodRedefined(Hash@0x1000, size@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Hash@0x1000) + PatchPoint MethodRedefined(Hash@0x1000, size@0x1008, cme:0x1010) v27:HashExact = GuardType v9, HashExact IncrCounter inline_cfunc_optimized_send_count v19:Fixnum[5] = Const Value(5) @@ -8326,11 +8371,11 @@ mod hir_opt_tests { Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): v14:StaticSymbol[:foo] = Const Value(VALUE(0x1000)) - PatchPoint MethodRedefined(C@0x1008, respond_to?@0x1010, cme:0x1018) PatchPoint NoSingletonClass(C@0x1008) + PatchPoint MethodRedefined(C@0x1008, respond_to?@0x1010, cme:0x1018) v24:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] - PatchPoint MethodRedefined(C@0x1008, foo@0x1040, cme:0x1048) PatchPoint NoSingletonClass(C@0x1008) + PatchPoint MethodRedefined(C@0x1008, foo@0x1040, cme:0x1048) v28:TrueClass = Const Value(true) IncrCounter inline_cfunc_optimized_send_count CheckInterrupts @@ -8358,12 +8403,12 @@ mod hir_opt_tests { Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): v14:StaticSymbol[:foo] = Const Value(VALUE(0x1000)) - PatchPoint MethodRedefined(C@0x1008, respond_to?@0x1010, cme:0x1018) PatchPoint NoSingletonClass(C@0x1008) + PatchPoint MethodRedefined(C@0x1008, respond_to?@0x1010, cme:0x1018) v24:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] PatchPoint MethodRedefined(C@0x1008, respond_to_missing?@0x1040, cme:0x1048) - PatchPoint MethodRedefined(C@0x1008, foo@0x1070, cme:0x1078) PatchPoint NoSingletonClass(C@0x1008) + PatchPoint MethodRedefined(C@0x1008, foo@0x1070, cme:0x1078) v30:FalseClass = Const Value(false) IncrCounter inline_cfunc_optimized_send_count CheckInterrupts @@ -8393,11 +8438,11 @@ mod hir_opt_tests { Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): v14:StaticSymbol[:foo] = Const Value(VALUE(0x1000)) - PatchPoint MethodRedefined(C@0x1008, respond_to?@0x1010, cme:0x1018) PatchPoint NoSingletonClass(C@0x1008) + PatchPoint MethodRedefined(C@0x1008, respond_to?@0x1010, cme:0x1018) v24:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] - PatchPoint MethodRedefined(C@0x1008, foo@0x1040, cme:0x1048) PatchPoint NoSingletonClass(C@0x1008) + PatchPoint MethodRedefined(C@0x1008, foo@0x1040, cme:0x1048) v28:FalseClass = Const Value(false) IncrCounter inline_cfunc_optimized_send_count CheckInterrupts @@ -8428,11 +8473,11 @@ mod hir_opt_tests { bb2(v8:BasicObject, v9:BasicObject): v14:StaticSymbol[:foo] = Const Value(VALUE(0x1000)) v16:FalseClass = Const Value(false) - PatchPoint MethodRedefined(C@0x1008, respond_to?@0x1010, cme:0x1018) PatchPoint NoSingletonClass(C@0x1008) + PatchPoint MethodRedefined(C@0x1008, respond_to?@0x1010, cme:0x1018) v26:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] - PatchPoint MethodRedefined(C@0x1008, foo@0x1040, cme:0x1048) PatchPoint NoSingletonClass(C@0x1008) + PatchPoint MethodRedefined(C@0x1008, foo@0x1040, cme:0x1048) v30:FalseClass = Const Value(false) IncrCounter inline_cfunc_optimized_send_count CheckInterrupts @@ -8463,11 +8508,11 @@ mod hir_opt_tests { bb2(v8:BasicObject, v9:BasicObject): v14:StaticSymbol[:foo] = Const Value(VALUE(0x1000)) v16:NilClass = Const Value(nil) - PatchPoint MethodRedefined(C@0x1008, respond_to?@0x1010, cme:0x1018) PatchPoint NoSingletonClass(C@0x1008) + PatchPoint MethodRedefined(C@0x1008, respond_to?@0x1010, cme:0x1018) v26:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] - PatchPoint MethodRedefined(C@0x1008, foo@0x1040, cme:0x1048) PatchPoint NoSingletonClass(C@0x1008) + PatchPoint MethodRedefined(C@0x1008, foo@0x1040, cme:0x1048) v30:FalseClass = Const Value(false) IncrCounter inline_cfunc_optimized_send_count CheckInterrupts @@ -8498,11 +8543,11 @@ mod hir_opt_tests { bb2(v8:BasicObject, v9:BasicObject): v14:StaticSymbol[:foo] = Const Value(VALUE(0x1000)) v16:TrueClass = Const Value(true) - PatchPoint MethodRedefined(C@0x1008, respond_to?@0x1010, cme:0x1018) PatchPoint NoSingletonClass(C@0x1008) + PatchPoint MethodRedefined(C@0x1008, respond_to?@0x1010, cme:0x1018) v26:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] - PatchPoint MethodRedefined(C@0x1008, foo@0x1040, cme:0x1048) PatchPoint NoSingletonClass(C@0x1008) + PatchPoint MethodRedefined(C@0x1008, foo@0x1040, cme:0x1048) v30:TrueClass = Const Value(true) IncrCounter inline_cfunc_optimized_send_count CheckInterrupts @@ -8532,11 +8577,11 @@ mod hir_opt_tests { bb2(v8:BasicObject, v9:BasicObject): v14:StaticSymbol[:foo] = Const Value(VALUE(0x1000)) v16:Fixnum[4] = Const Value(4) - PatchPoint MethodRedefined(C@0x1008, respond_to?@0x1010, cme:0x1018) PatchPoint NoSingletonClass(C@0x1008) + PatchPoint MethodRedefined(C@0x1008, respond_to?@0x1010, cme:0x1018) v26:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] - PatchPoint MethodRedefined(C@0x1008, foo@0x1040, cme:0x1048) PatchPoint NoSingletonClass(C@0x1008) + PatchPoint MethodRedefined(C@0x1008, foo@0x1040, cme:0x1048) v30:TrueClass = Const Value(true) IncrCounter inline_cfunc_optimized_send_count CheckInterrupts @@ -8566,11 +8611,11 @@ mod hir_opt_tests { bb2(v8:BasicObject, v9:BasicObject): v14:StaticSymbol[:foo] = Const Value(VALUE(0x1000)) v16:NilClass = Const Value(nil) - PatchPoint MethodRedefined(C@0x1008, respond_to?@0x1010, cme:0x1018) PatchPoint NoSingletonClass(C@0x1008) + PatchPoint MethodRedefined(C@0x1008, respond_to?@0x1010, cme:0x1018) v26:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] - PatchPoint MethodRedefined(C@0x1008, foo@0x1040, cme:0x1048) PatchPoint NoSingletonClass(C@0x1008) + PatchPoint MethodRedefined(C@0x1008, foo@0x1040, cme:0x1048) v30:TrueClass = Const Value(true) IncrCounter inline_cfunc_optimized_send_count CheckInterrupts @@ -8598,12 +8643,12 @@ mod hir_opt_tests { Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): v14:StaticSymbol[:foo] = Const Value(VALUE(0x1000)) - PatchPoint MethodRedefined(C@0x1008, respond_to?@0x1010, cme:0x1018) PatchPoint NoSingletonClass(C@0x1008) + PatchPoint MethodRedefined(C@0x1008, respond_to?@0x1010, cme:0x1018) v24:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] PatchPoint MethodRedefined(C@0x1008, respond_to_missing?@0x1040, cme:0x1048) - PatchPoint MethodRedefined(C@0x1008, foo@0x1070, cme:0x1078) PatchPoint NoSingletonClass(C@0x1008) + PatchPoint MethodRedefined(C@0x1008, foo@0x1070, cme:0x1078) v30:FalseClass = Const Value(false) IncrCounter inline_cfunc_optimized_send_count CheckInterrupts @@ -8634,8 +8679,8 @@ mod hir_opt_tests { Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): v14:StaticSymbol[:foo] = Const Value(VALUE(0x1000)) - PatchPoint MethodRedefined(C@0x1008, respond_to?@0x1010, cme:0x1018) PatchPoint NoSingletonClass(C@0x1008) + PatchPoint MethodRedefined(C@0x1008, respond_to?@0x1010, cme:0x1018) v24:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] v25:BasicObject = CCallVariadic v24, :Kernel#respond_to?@0x1040, v14 CheckInterrupts @@ -8660,8 +8705,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): - PatchPoint MethodRedefined(Object@0x1000, callee@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint MethodRedefined(Object@0x1000, callee@0x1008, cme:0x1010) v18:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] IncrCounter inline_iseq_optimized_send_count CheckInterrupts @@ -8687,8 +8732,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): - PatchPoint MethodRedefined(Object@0x1000, callee@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint MethodRedefined(Object@0x1000, callee@0x1008, cme:0x1010) v18:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] IncrCounter inline_iseq_optimized_send_count v21:StringExact[VALUE(0x1038)] = Const Value(VALUE(0x1038)) @@ -8714,8 +8759,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): - PatchPoint MethodRedefined(Object@0x1000, callee@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint MethodRedefined(Object@0x1000, callee@0x1008, cme:0x1010) v18:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] IncrCounter inline_iseq_optimized_send_count v21:NilClass = Const Value(nil) @@ -8741,8 +8786,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): - PatchPoint MethodRedefined(Object@0x1000, callee@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint MethodRedefined(Object@0x1000, callee@0x1008, cme:0x1010) v18:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] IncrCounter inline_iseq_optimized_send_count v21:TrueClass = Const Value(true) @@ -8768,8 +8813,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): - PatchPoint MethodRedefined(Object@0x1000, callee@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint MethodRedefined(Object@0x1000, callee@0x1008, cme:0x1010) v18:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] IncrCounter inline_iseq_optimized_send_count v21:FalseClass = Const Value(false) @@ -8795,8 +8840,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): - PatchPoint MethodRedefined(Object@0x1000, callee@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint MethodRedefined(Object@0x1000, callee@0x1008, cme:0x1010) v18:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] IncrCounter inline_iseq_optimized_send_count v21:Fixnum[0] = Const Value(0) @@ -8822,8 +8867,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): - PatchPoint MethodRedefined(Object@0x1000, callee@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint MethodRedefined(Object@0x1000, callee@0x1008, cme:0x1010) v18:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] IncrCounter inline_iseq_optimized_send_count v21:Fixnum[1] = Const Value(1) @@ -8850,8 +8895,8 @@ mod hir_opt_tests { Jump bb2(v4) bb2(v6:BasicObject): v11:Fixnum[3] = Const Value(3) - PatchPoint MethodRedefined(Object@0x1000, callee@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint MethodRedefined(Object@0x1000, callee@0x1008, cme:0x1010) v20:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] IncrCounter inline_iseq_optimized_send_count CheckInterrupts @@ -8879,8 +8924,8 @@ mod hir_opt_tests { v11:Fixnum[1] = Const Value(1) v13:Fixnum[2] = Const Value(2) v15:Fixnum[3] = Const Value(3) - PatchPoint MethodRedefined(Object@0x1000, callee@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint MethodRedefined(Object@0x1000, callee@0x1008, cme:0x1010) v24:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] IncrCounter inline_iseq_optimized_send_count CheckInterrupts @@ -9001,8 +9046,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - PatchPoint MethodRedefined(String@0x1000, ==@0x1008, cme:0x1010) PatchPoint NoSingletonClass(String@0x1000) + PatchPoint MethodRedefined(String@0x1000, ==@0x1008, cme:0x1010) v27:StringExact = GuardType v11, StringExact v28:String = GuardType v12, String v29:BoolExact = CCall v27, :String#==@0x1038, v28 @@ -9032,8 +9077,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - PatchPoint MethodRedefined(C@0x1000, ==@0x1008, cme:0x1010) PatchPoint NoSingletonClass(C@0x1000) + PatchPoint MethodRedefined(C@0x1000, ==@0x1008, cme:0x1010) v27:StringSubclass[class_exact:C] = GuardType v11, StringSubclass[class_exact:C] v28:String = GuardType v12, String v29:BoolExact = CCall v27, :String#==@0x1038, v28 @@ -9063,8 +9108,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - PatchPoint MethodRedefined(String@0x1000, ==@0x1008, cme:0x1010) PatchPoint NoSingletonClass(String@0x1000) + PatchPoint MethodRedefined(String@0x1000, ==@0x1008, cme:0x1010) v27:StringExact = GuardType v11, StringExact v28:String = GuardType v12, String v29:BoolExact = CCall v27, :String#==@0x1038, v28 @@ -9092,8 +9137,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - PatchPoint MethodRedefined(String@0x1000, ===@0x1008, cme:0x1010) PatchPoint NoSingletonClass(String@0x1000) + PatchPoint MethodRedefined(String@0x1000, ===@0x1008, cme:0x1010) v26:StringExact = GuardType v11, StringExact v27:String = GuardType v12, String v28:BoolExact = CCall v26, :String#==@0x1038, v27 @@ -9123,8 +9168,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - PatchPoint MethodRedefined(C@0x1000, ===@0x1008, cme:0x1010) PatchPoint NoSingletonClass(C@0x1000) + PatchPoint MethodRedefined(C@0x1000, ===@0x1008, cme:0x1010) v26:StringSubclass[class_exact:C] = GuardType v11, StringSubclass[class_exact:C] v27:String = GuardType v12, String v28:BoolExact = CCall v26, :String#==@0x1038, v27 @@ -9154,8 +9199,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - PatchPoint MethodRedefined(String@0x1000, ===@0x1008, cme:0x1010) PatchPoint NoSingletonClass(String@0x1000) + PatchPoint MethodRedefined(String@0x1000, ===@0x1008, cme:0x1010) v26:StringExact = GuardType v11, StringExact v27:String = GuardType v12, String v28:BoolExact = CCall v26, :String#==@0x1038, v27 @@ -9184,8 +9229,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): - PatchPoint MethodRedefined(String@0x1000, size@0x1008, cme:0x1010) PatchPoint NoSingletonClass(String@0x1000) + PatchPoint MethodRedefined(String@0x1000, size@0x1008, cme:0x1010) v23:StringExact = GuardType v9, StringExact IncrCounter inline_cfunc_optimized_send_count v25:Fixnum = CCall v23, :String#size@0x1038 @@ -9214,8 +9259,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): - PatchPoint MethodRedefined(String@0x1000, size@0x1008, cme:0x1010) PatchPoint NoSingletonClass(String@0x1000) + PatchPoint MethodRedefined(String@0x1000, size@0x1008, cme:0x1010) v27:StringExact = GuardType v9, StringExact IncrCounter inline_cfunc_optimized_send_count v19:Fixnum[5] = Const Value(5) @@ -9243,8 +9288,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): - PatchPoint MethodRedefined(String@0x1000, bytesize@0x1008, cme:0x1010) PatchPoint NoSingletonClass(String@0x1000) + PatchPoint MethodRedefined(String@0x1000, bytesize@0x1008, cme:0x1010) v22:StringExact = GuardType v9, StringExact v23:CInt64 = LoadField v22, :len@0x1038 v24:Fixnum = BoxFixnum v23 @@ -9274,8 +9319,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): - PatchPoint MethodRedefined(String@0x1000, bytesize@0x1008, cme:0x1010) PatchPoint NoSingletonClass(String@0x1000) + PatchPoint MethodRedefined(String@0x1000, bytesize@0x1008, cme:0x1010) v26:StringExact = GuardType v9, StringExact IncrCounter inline_cfunc_optimized_send_count v18:Fixnum[5] = Const Value(5) @@ -9303,8 +9348,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): - PatchPoint MethodRedefined(String@0x1000, length@0x1008, cme:0x1010) PatchPoint NoSingletonClass(String@0x1000) + PatchPoint MethodRedefined(String@0x1000, length@0x1008, cme:0x1010) v23:StringExact = GuardType v9, StringExact IncrCounter inline_cfunc_optimized_send_count v25:Fixnum = CCall v23, :String#length@0x1038 @@ -9334,8 +9379,8 @@ mod hir_opt_tests { PatchPoint StableConstantNames(0x1000, String) v26:Class[String@0x1008] = Const Value(VALUE(0x1008)) PatchPoint NoEPEscape(test) - PatchPoint MethodRedefined(Class@0x1010, ===@0x1018, cme:0x1020) PatchPoint NoSingletonClass(Class@0x1010) + PatchPoint MethodRedefined(Class@0x1010, ===@0x1018, cme:0x1020) v30:BoolExact = IsA v9, v26 IncrCounter inline_cfunc_optimized_send_count CheckInterrupts @@ -9364,8 +9409,8 @@ mod hir_opt_tests { PatchPoint StableConstantNames(0x1000, Kernel) v26:ModuleExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) PatchPoint NoEPEscape(test) - PatchPoint MethodRedefined(Module@0x1010, ===@0x1018, cme:0x1020) PatchPoint NoSingletonClass(Module@0x1010) + PatchPoint MethodRedefined(Module@0x1010, ===@0x1018, cme:0x1020) IncrCounter inline_cfunc_optimized_send_count v31:BoolExact = CCall v26, :Module#===@0x1048, v9 CheckInterrupts @@ -9393,8 +9438,8 @@ mod hir_opt_tests { PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, String) v24:Class[String@0x1008] = Const Value(VALUE(0x1008)) - PatchPoint MethodRedefined(String@0x1008, is_a?@0x1009, cme:0x1010) PatchPoint NoSingletonClass(String@0x1008) + PatchPoint MethodRedefined(String@0x1008, is_a?@0x1009, cme:0x1010) v28:StringExact = GuardType v9, StringExact v29:BoolExact = IsA v28, v24 IncrCounter inline_cfunc_optimized_send_count @@ -9423,8 +9468,8 @@ mod hir_opt_tests { PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, Kernel) v24:ModuleExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - PatchPoint MethodRedefined(String@0x1010, is_a?@0x1018, cme:0x1020) PatchPoint NoSingletonClass(String@0x1010) + PatchPoint MethodRedefined(String@0x1010, is_a?@0x1018, cme:0x1020) v28:StringExact = GuardType v9, StringExact v29:BasicObject = CCallWithFrame v28, :Kernel#is_a?@0x1048, v24 CheckInterrupts @@ -9455,8 +9500,8 @@ mod hir_opt_tests { PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, Integer) v28:Class[Integer@0x1008] = Const Value(VALUE(0x1008)) - PatchPoint MethodRedefined(String@0x1010, is_a?@0x1018, cme:0x1020) PatchPoint NoSingletonClass(String@0x1010) + PatchPoint MethodRedefined(String@0x1010, is_a?@0x1018, cme:0x1020) v32:StringExact = GuardType v9, StringExact IncrCounter inline_cfunc_optimized_send_count v21:Fixnum[5] = Const Value(5) @@ -9489,8 +9534,8 @@ mod hir_opt_tests { PatchPoint StableConstantNames(0x1000, Integer) v30:Class[Integer@0x1008] = Const Value(VALUE(0x1008)) PatchPoint NoEPEscape(test) - PatchPoint MethodRedefined(Class@0x1010, ===@0x1018, cme:0x1020) PatchPoint NoSingletonClass(Class@0x1010) + PatchPoint MethodRedefined(Class@0x1010, ===@0x1018, cme:0x1020) IncrCounter inline_cfunc_optimized_send_count v23:Fixnum[5] = Const Value(5) CheckInterrupts @@ -9518,8 +9563,8 @@ mod hir_opt_tests { PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, String) v24:Class[String@0x1008] = Const Value(VALUE(0x1008)) - PatchPoint MethodRedefined(String@0x1008, kind_of?@0x1009, cme:0x1010) PatchPoint NoSingletonClass(String@0x1008) + PatchPoint MethodRedefined(String@0x1008, kind_of?@0x1009, cme:0x1010) v28:StringExact = GuardType v9, StringExact v29:BoolExact = IsA v28, v24 IncrCounter inline_cfunc_optimized_send_count @@ -9548,8 +9593,8 @@ mod hir_opt_tests { PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, Kernel) v24:ModuleExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - PatchPoint MethodRedefined(String@0x1010, kind_of?@0x1018, cme:0x1020) PatchPoint NoSingletonClass(String@0x1010) + PatchPoint MethodRedefined(String@0x1010, kind_of?@0x1018, cme:0x1020) v28:StringExact = GuardType v9, StringExact v29:BasicObject = CCallWithFrame v28, :Kernel#kind_of?@0x1048, v24 CheckInterrupts @@ -9580,8 +9625,8 @@ mod hir_opt_tests { PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, Integer) v28:Class[Integer@0x1008] = Const Value(VALUE(0x1008)) - PatchPoint MethodRedefined(String@0x1010, kind_of?@0x1018, cme:0x1020) PatchPoint NoSingletonClass(String@0x1010) + PatchPoint MethodRedefined(String@0x1010, kind_of?@0x1018, cme:0x1020) v32:StringExact = GuardType v9, StringExact IncrCounter inline_cfunc_optimized_send_count v21:Fixnum[5] = Const Value(5) @@ -9662,8 +9707,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): - PatchPoint MethodRedefined(String@0x1000, length@0x1008, cme:0x1010) PatchPoint NoSingletonClass(String@0x1000) + PatchPoint MethodRedefined(String@0x1000, length@0x1008, cme:0x1010) v27:StringExact = GuardType v9, StringExact IncrCounter inline_cfunc_optimized_send_count v19:Fixnum[4] = Const Value(4) @@ -9702,18 +9747,18 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): - PatchPoint MethodRedefined(C@0x1000, class@0x1008, cme:0x1010) PatchPoint NoSingletonClass(C@0x1000) + PatchPoint MethodRedefined(C@0x1000, class@0x1008, cme:0x1010) v40:HeapObject[class_exact:C] = GuardType v6, HeapObject[class_exact:C] IncrCounter inline_iseq_optimized_send_count v44:Class[C@0x1000] = Const Value(VALUE(0x1000)) IncrCounter inline_cfunc_optimized_send_count v13:StaticSymbol[:_lex_actions] = Const Value(VALUE(0x1038)) v15:TrueClass = Const Value(true) + PatchPoint NoSingletonClass(Class@0x1040) PatchPoint MethodRedefined(Class@0x1040, respond_to?@0x1048, cme:0x1050) PatchPoint NoSingletonClass(Class@0x1040) PatchPoint MethodRedefined(Class@0x1040, _lex_actions@0x1078, cme:0x1080) - PatchPoint NoSingletonClass(Class@0x1040) v52:TrueClass = Const Value(true) IncrCounter inline_cfunc_optimized_send_count CheckInterrupts @@ -9741,14 +9786,14 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): - PatchPoint MethodRedefined(C@0x1000, class@0x1008, cme:0x1010) PatchPoint NoSingletonClass(C@0x1000) + PatchPoint MethodRedefined(C@0x1000, class@0x1008, cme:0x1010) v23:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] IncrCounter inline_iseq_optimized_send_count v27:Class[C@0x1000] = Const Value(VALUE(0x1000)) IncrCounter inline_cfunc_optimized_send_count - PatchPoint MethodRedefined(Class@0x1038, name@0x1040, cme:0x1048) PatchPoint NoSingletonClass(Class@0x1038) + PatchPoint MethodRedefined(Class@0x1038, name@0x1040, cme:0x1048) IncrCounter inline_cfunc_optimized_send_count v33:StringExact|NilClass = CCall v27, :Module#name@0x1070 CheckInterrupts @@ -9774,8 +9819,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): - PatchPoint MethodRedefined(C@0x1000, class@0x1008, cme:0x1010) PatchPoint NoSingletonClass(C@0x1000) + PatchPoint MethodRedefined(C@0x1000, class@0x1008, cme:0x1010) v21:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] IncrCounter inline_iseq_optimized_send_count v25:Class[C@0x1000] = Const Value(VALUE(0x1000)) @@ -9827,8 +9872,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): - PatchPoint MethodRedefined(Object@0x1000, class@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint MethodRedefined(Object@0x1000, class@0x1008, cme:0x1010) v18:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] IncrCounter inline_iseq_optimized_send_count v22:Class[Object@0x1038] = Const Value(VALUE(0x1038)) @@ -9883,8 +9928,8 @@ mod hir_opt_tests { v59:CShape[0x1003] = Const CShape(0x1003) StoreField v54, :_shape_id@0x1000, v59 v43:Class[VMFrozenCore] = Const Value(VALUE(0x1008)) - PatchPoint MethodRedefined(Class@0x1010, lambda@0x1018, cme:0x1020) PatchPoint NoSingletonClass(Class@0x1010) + PatchPoint MethodRedefined(Class@0x1010, lambda@0x1018, cme:0x1020) v64:BasicObject = CCallWithFrame v43, :RubyVM::FrozenCore.lambda@0x1048, block=0x1050 v46:BasicObject = GetLocal :a, l0, EP@6 v47:BasicObject = GetLocal :_b, l0, EP@5 @@ -9925,8 +9970,8 @@ mod hir_opt_tests { PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, FROZEN_OBJ) v20:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - PatchPoint MethodRedefined(TestFrozen@0x1010, a@0x1018, cme:0x1020) PatchPoint NoSingletonClass(TestFrozen@0x1010) + PatchPoint MethodRedefined(TestFrozen@0x1010, a@0x1018, cme:0x1020) v29:Fixnum[1] = Const Value(1) CheckInterrupts Return v29 @@ -9965,8 +10010,8 @@ mod hir_opt_tests { PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, MULTI_FROZEN) v20:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - PatchPoint MethodRedefined(TestMultiIvars@0x1010, b@0x1018, cme:0x1020) PatchPoint NoSingletonClass(TestMultiIvars@0x1010) + PatchPoint MethodRedefined(TestMultiIvars@0x1010, b@0x1018, cme:0x1020) v29:Fixnum[20] = Const Value(20) CheckInterrupts Return v29 @@ -10003,8 +10048,8 @@ mod hir_opt_tests { PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, FROZEN_STR) v20:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - PatchPoint MethodRedefined(TestFrozenStr@0x1010, name@0x1018, cme:0x1020) PatchPoint NoSingletonClass(TestFrozenStr@0x1010) + PatchPoint MethodRedefined(TestFrozenStr@0x1010, name@0x1018, cme:0x1020) v29:StringExact[VALUE(0x1048)] = Const Value(VALUE(0x1048)) CheckInterrupts Return v29 @@ -10041,8 +10086,8 @@ mod hir_opt_tests { PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, FROZEN_NIL) v20:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - PatchPoint MethodRedefined(TestFrozenNil@0x1010, value@0x1018, cme:0x1020) PatchPoint NoSingletonClass(TestFrozenNil@0x1010) + PatchPoint MethodRedefined(TestFrozenNil@0x1010, value@0x1018, cme:0x1020) v29:NilClass = Const Value(nil) CheckInterrupts Return v29 @@ -10079,8 +10124,8 @@ mod hir_opt_tests { PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, UNFROZEN_OBJ) v20:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - PatchPoint MethodRedefined(TestUnfrozen@0x1010, a@0x1018, cme:0x1020) PatchPoint NoSingletonClass(TestUnfrozen@0x1010) + PatchPoint MethodRedefined(TestUnfrozen@0x1010, a@0x1018, cme:0x1020) v25:CShape = LoadField v20, :_shape_id@0x1048 v26:CShape[0x1049] = GuardBitEquals v25, CShape(0x1049) v27:BasicObject = LoadField v20, :@a@0x104a @@ -10119,8 +10164,8 @@ mod hir_opt_tests { PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, FROZEN_READER) v20:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - PatchPoint MethodRedefined(TestAttrReader@0x1010, value@0x1018, cme:0x1020) PatchPoint NoSingletonClass(TestAttrReader@0x1010) + PatchPoint MethodRedefined(TestAttrReader@0x1010, value@0x1018, cme:0x1020) v29:Fixnum[42] = Const Value(42) CheckInterrupts Return v29 @@ -10157,8 +10202,8 @@ mod hir_opt_tests { PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, FROZEN_SYM) v20:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - PatchPoint MethodRedefined(TestFrozenSym@0x1010, sym@0x1018, cme:0x1020) PatchPoint NoSingletonClass(TestFrozenSym@0x1010) + PatchPoint MethodRedefined(TestFrozenSym@0x1010, sym@0x1018, cme:0x1020) v29:StaticSymbol[:hello] = Const Value(VALUE(0x1048)) CheckInterrupts Return v29 @@ -10195,8 +10240,8 @@ mod hir_opt_tests { PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, FROZEN_TRUE) v20:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - PatchPoint MethodRedefined(TestFrozenBool@0x1010, flag@0x1018, cme:0x1020) PatchPoint NoSingletonClass(TestFrozenBool@0x1010) + PatchPoint MethodRedefined(TestFrozenBool@0x1010, flag@0x1018, cme:0x1020) v29:TrueClass = Const Value(true) CheckInterrupts Return v29 @@ -10230,8 +10275,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): - PatchPoint MethodRedefined(TestDynamic@0x1000, val@0x1008, cme:0x1010) PatchPoint NoSingletonClass(TestDynamic@0x1000) + PatchPoint MethodRedefined(TestDynamic@0x1000, val@0x1008, cme:0x1010) v21:HeapObject[class_exact:TestDynamic] = GuardType v9, HeapObject[class_exact:TestDynamic] v24:CShape = LoadField v21, :_shape_id@0x1038 v25:CShape[0x1039] = GuardBitEquals v24, CShape(0x1039) @@ -10272,14 +10317,14 @@ mod hir_opt_tests { PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, NESTED_FROZEN) v28:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - PatchPoint MethodRedefined(TestNestedAccess@0x1010, x@0x1018, cme:0x1020) PatchPoint NoSingletonClass(TestNestedAccess@0x1010) + PatchPoint MethodRedefined(TestNestedAccess@0x1010, x@0x1018, cme:0x1020) v53:Fixnum[100] = Const Value(100) PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1048, NESTED_FROZEN) v34:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - PatchPoint MethodRedefined(TestNestedAccess@0x1010, y@0x1050, cme:0x1058) PatchPoint NoSingletonClass(TestNestedAccess@0x1010) + PatchPoint MethodRedefined(TestNestedAccess@0x1010, y@0x1050, cme:0x1058) v55:Fixnum[200] = Const Value(200) PatchPoint MethodRedefined(Integer@0x1080, +@0x1088, cme:0x1090) v56:Fixnum[300] = Const Value(300) @@ -10309,8 +10354,8 @@ mod hir_opt_tests { PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, S) v20:StringExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - PatchPoint MethodRedefined(String@0x1010, bytesize@0x1018, cme:0x1020) PatchPoint NoSingletonClass(String@0x1010) + PatchPoint MethodRedefined(String@0x1010, bytesize@0x1018, cme:0x1020) v24:CInt64 = LoadField v20, :len@0x1048 v25:Fixnum = BoxFixnum v24 IncrCounter inline_cfunc_optimized_send_count @@ -10338,8 +10383,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): - PatchPoint MethodRedefined(C@0x1000, secret@0x1008, cme:0x1010) PatchPoint NoSingletonClass(C@0x1000) + PatchPoint MethodRedefined(C@0x1000, secret@0x1008, cme:0x1010) v18:HeapObject[class_exact:C] = GuardType v6, HeapObject[class_exact:C] IncrCounter inline_iseq_optimized_send_count v21:Fixnum[42] = Const Value(42) @@ -10395,8 +10440,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): - PatchPoint MethodRedefined(BasicObject@0x1000, initialize@0x1008, cme:0x1010) PatchPoint NoSingletonClass(BasicObject@0x1000) + PatchPoint MethodRedefined(BasicObject@0x1000, initialize@0x1008, cme:0x1010) v20:BasicObjectExact = GuardType v6, BasicObjectExact v21:NilClass = Const Value(nil) IncrCounter inline_cfunc_optimized_send_count @@ -10477,8 +10522,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): - PatchPoint MethodRedefined(C@0x1000, secret@0x1008, cme:0x1010) PatchPoint NoSingletonClass(C@0x1000) + PatchPoint MethodRedefined(C@0x1000, secret@0x1008, cme:0x1010) v18:HeapObject[class_exact:C] = GuardType v6, HeapObject[class_exact:C] IncrCounter inline_iseq_optimized_send_count v21:Fixnum[42] = Const Value(42) @@ -10515,4 +10560,384 @@ mod hir_opt_tests { Return v13 "); } + + // Test that when a singleton class has been seen for a class, we skip the + // NoSingletonClass optimization to avoid an invalidation loop. + #[test] + fn test_skip_optimization_after_singleton_class_seen() { + // First, trigger the singleton class callback for String by creating a singleton class. + // This should mark String as having had a singleton class seen. + eval(r#" + "hello".singleton_class + "#); + + // Now define and compile a method that would normally be optimized with NoSingletonClass. + // Since String has had a singleton class, the optimization should be skipped and we + // should fall back to SendWithoutBlock. + eval(r#" + def test(s) + s.length + end + test("asdf") + "#); + + // The output should NOT have NoSingletonClass patchpoint for String, and should + // fall back to SendWithoutBlock instead of the optimized CCall path. + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal :s, l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + v15:BasicObject = SendWithoutBlock v9, :length # SendFallbackReason: Singleton class previously created for receiver class + CheckInterrupts + Return v15 + "); + } + + #[test] + fn test_invokesuper_to_iseq_optimizes_to_direct() { + eval(" + class A + def foo + 'A' + end + end + + class B < A + def foo + super + end + end + + B.new.foo; B.new.foo + "); + + // A Ruby method as the target of `super` should optimize provided no block is given. + let hir = hir_string_proc("B.new.method(:foo)"); + assert!(!hir.contains("InvokeSuper "), "InvokeSuper should optimize to SendWithoutBlockDirect but got:\n{hir}"); + assert!(hir.contains("SendWithoutBlockDirect"), "Should optimize to SendWithoutBlockDirect for call without args or block:\n{hir}"); + + assert_snapshot!(hir, @r" + fn foo@:10: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + PatchPoint MethodRedefined(A@0x1000, foo@0x1008, cme:0x1010) + GuardSuperMethodEntry 0x1038 + v18:RubyValue = GetBlockHandler + v19:FalseClass = GuardBitEquals v18, Value(false) + v20:BasicObject = SendWithoutBlockDirect v6, :foo (0x1040) + CheckInterrupts + Return v20 + "); + } + + #[test] + fn test_invokesuper_with_positional_args_optimizes_to_direct() { + eval(" + class A + def foo(x) + x * 2 + end + end + + class B < A + def foo(x) + super(x) + 1 + end + end + + B.new.foo(5); B.new.foo(5) + "); + + let hir = hir_string_proc("B.new.method(:foo)"); + assert!(!hir.contains("InvokeSuper "), "InvokeSuper should optimize to SendWithoutBlockDirect but got:\n{hir}"); + assert!(hir.contains("SendWithoutBlockDirect"), "Should optimize to SendWithoutBlockDirect for call without args or block:\n{hir}"); + + assert_snapshot!(hir, @r" + fn foo@:10: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal :x, l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + PatchPoint MethodRedefined(A@0x1000, foo@0x1008, cme:0x1010) + GuardSuperMethodEntry 0x1038 + v27:RubyValue = GetBlockHandler + v28:FalseClass = GuardBitEquals v27, Value(false) + v29:BasicObject = SendWithoutBlockDirect v8, :foo (0x1040), v9 + v17:Fixnum[1] = Const Value(1) + PatchPoint MethodRedefined(Integer@0x1048, +@0x1050, cme:0x1058) + v32:Fixnum = GuardType v29, Fixnum + v33:Fixnum = FixnumAdd v32, v17 + IncrCounter inline_cfunc_optimized_send_count + CheckInterrupts + Return v33 + "); + } + + #[test] + fn test_invokesuper_with_forwarded_splat_args_remains_invokesuper() { + eval(" + class A + def foo(x) + x * 2 + end + end + + class B < A + def foo(*x) + super + end + end + + B.new.foo(5); B.new.foo(5) + "); + + let hir = hir_string_proc("B.new.method(:foo)"); + assert!(hir.contains("InvokeSuper "), "Expected unoptimized InvokeSuper but got:\n{hir}"); + assert!(!hir.contains("SendWithoutBlockDirect"), "Should not optimize to SendWithoutBlockDirect for explicit blockarg:\n{hir}"); + + assert_snapshot!(hir, @r" + fn foo@:10: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:ArrayExact = GetLocal :x, l0, SP@4, * + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:ArrayExact): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:ArrayExact): + v15:ArrayExact = ToArray v9 + v17:BasicObject = InvokeSuper v8, 0x1000, v15 # SendFallbackReason: super: complex argument passing to `super` call + CheckInterrupts + Return v17 + "); + } + + #[test] + fn test_invokesuper_with_block_literal_remains_invokesuper() { + eval(" + class A + def foo + block_given? ? yield : 'no block' + end + end + + class B < A + def foo + super { 'from subclass' } + end + end + + B.new.foo; B.new.foo + "); + + let hir = hir_string_proc("B.new.method(:foo)"); + assert!(hir.contains("InvokeSuper "), "Expected unoptimized InvokeSuper but got:\n{hir}"); + assert!(!hir.contains("SendWithoutBlockDirect"), "Should not optimize to SendWithoutBlockDirect for block literal:\n{hir}"); + + // With a block, we don't optimize to SendWithoutBlockDirect + assert_snapshot!(hir, @r" + fn foo@:10: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v11:BasicObject = InvokeSuper v6, 0x1000 # SendFallbackReason: super: call made with a block + CheckInterrupts + Return v11 + "); + } + + #[test] + fn test_invokesuper_to_cfunc_remains_invokesuper() { + eval(" + class MyArray < Array + def length + super + end + end + + MyArray.new.length; MyArray.new.length + "); + + let hir = hir_string_proc("MyArray.new.method(:length)"); + assert!(hir.contains("InvokeSuper "), "Expected unoptimized InvokeSuper but got:\n{hir}"); + assert!(!hir.contains("SendWithoutBlockDirect"), "Should not optimize to SendWithoutBlockDirect for CFUNC:\n{hir}"); + + assert_snapshot!(hir, @r" + fn length@:4: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v11:BasicObject = InvokeSuper v6, 0x1000 # SendFallbackReason: super: unsupported target method type Cfunc + CheckInterrupts + Return v11 + "); + } + + #[test] + fn test_invokesuper_with_blockarg_remains_invokesuper() { + eval(" + class A + def foo + block_given? ? yield : 'no block' + end + end + + class B < A + def foo(&blk) + other_block = proc { 'different block' } + super(&other_block) + end + end + + B.new.foo { 'passed' }; B.new.foo { 'passed' } + "); + + let hir = hir_string_proc("B.new.method(:foo)"); + assert!(hir.contains("InvokeSuper "), "Expected unoptimized InvokeSuper but got:\n{hir}"); + assert!(!hir.contains("SendWithoutBlockDirect"), "Should not optimize to SendWithoutBlockDirect for explicit blockarg:\n{hir}"); + + assert_snapshot!(hir, @r" + fn foo@:10: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal :blk, l0, SP@5 + v3:NilClass = Const Value(nil) + Jump bb2(v1, v2, v3) + bb1(v6:BasicObject, v7:BasicObject): + EntryPoint JIT(0) + v8:NilClass = Const Value(nil) + Jump bb2(v6, v7, v8) + bb2(v10:BasicObject, v11:BasicObject, v12:NilClass): + PatchPoint NoSingletonClass(B@0x1000) + PatchPoint MethodRedefined(B@0x1000, proc@0x1008, cme:0x1010) + v35:HeapObject[class_exact:B] = GuardType v10, HeapObject[class_exact:B] + v36:BasicObject = CCallWithFrame v35, :Kernel#proc@0x1038, block=0x1040 + v18:BasicObject = GetLocal :blk, l0, EP@4 + SetLocal :other_block, l0, EP@3, v36 + v25:BasicObject = GetLocal :other_block, l0, EP@3 + v27:BasicObject = InvokeSuper v10, 0x1048, v25 # SendFallbackReason: super: complex argument passing to `super` call + CheckInterrupts + Return v27 + "); + } + + #[test] + fn test_invokesuper_with_symbol_to_proc_remains_invokesuper() { + eval(" + class A + def foo(items, &blk) + items.map(&blk) + end + end + + class B < A + def foo(items) + super(items, &:succ) + end + end + + B.new.foo([1, 2, 3]); B.new.foo([1, 2, 3]) + "); + + let hir = hir_string_proc("B.new.method(:foo)"); + assert!(hir.contains("InvokeSuper "), "Expected unoptimized InvokeSuper but got:\n{hir}"); + assert!(!hir.contains("SendWithoutBlockDirect"), "Should not optimize to SendWithoutBlockDirect for symbol-to-proc:\n{hir}"); + + assert_snapshot!(hir, @r" + fn foo@:10: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal :items, l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + v15:StaticSymbol[:succ] = Const Value(VALUE(0x1000)) + v17:BasicObject = InvokeSuper v8, 0x1008, v9, v15 # SendFallbackReason: super: complex argument passing to `super` call + CheckInterrupts + Return v17 + "); + } + + #[test] + fn test_invokesuper_with_keyword_args_remains_invokesuper() { + eval(" + class A + def foo(attributes = {}) + @attributes = attributes + end + end + + class B < A + def foo(content = '') + super(content: content) + end + end + + B.new.foo('image data'); B.new.foo('image data') + "); + + let hir = hir_string_proc("B.new.method(:foo)"); + assert!(hir.contains("InvokeSuper "), "Expected unoptimized InvokeSuper but got:\n{hir}"); + + assert_snapshot!(hir, @r" + fn foo@:9: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal :content, l0, SP@4 + v3:CPtr = LoadPC + v4:CPtr[CPtr(0x1000)] = Const CPtr(0x1008) + v5:CBool = IsBitEqual v3, v4 + IfTrue v5, bb2(v1, v2) + Jump bb4(v1, v2) + bb1(v9:BasicObject): + EntryPoint JIT(0) + v10:NilClass = Const Value(nil) + Jump bb2(v9, v10) + bb2(v16:BasicObject, v17:BasicObject): + v20:StringExact[VALUE(0x1010)] = Const Value(VALUE(0x1010)) + v21:StringExact = StringCopy v20 + Jump bb4(v16, v21) + bb3(v13:BasicObject, v14:BasicObject): + EntryPoint JIT(1) + Jump bb4(v13, v14) + bb4(v24:BasicObject, v25:BasicObject): + v31:BasicObject = InvokeSuper v24, 0x1018, v25 # SendFallbackReason: super: complex argument passing to `super` call + CheckInterrupts + Return v31 + "); + } } diff --git a/zjit/src/hir/tests.rs b/zjit/src/hir/tests.rs index 2ae31e862dc0ef..1ce5488f4731a7 100644 --- a/zjit/src/hir/tests.rs +++ b/zjit/src/hir/tests.rs @@ -76,8 +76,8 @@ mod snapshot_tests { v13:Fixnum[1] = Const Value(1) v15:Fixnum[2] = Const Value(2) v16:Any = Snapshot FrameState { pc: 0x1008, stack: [v6, v11, v13, v15], locals: [] } - PatchPoint MethodRedefined(Object@0x1010, foo@0x1018, cme:0x1020) PatchPoint NoSingletonClass(Object@0x1010) + PatchPoint MethodRedefined(Object@0x1010, foo@0x1018, cme:0x1020) v24:HeapObject[class_exact*:Object@VALUE(0x1010)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1010)] v25:Any = Snapshot FrameState { pc: 0x1008, stack: [v6, v13, v15, v11], locals: [] } v26:BasicObject = SendWithoutBlockDirect v24, :foo (0x1048), v13, v15, v11 @@ -111,8 +111,8 @@ mod snapshot_tests { v11:Fixnum[1] = Const Value(1) v13:Fixnum[2] = Const Value(2) v14:Any = Snapshot FrameState { pc: 0x1008, stack: [v6, v11, v13], locals: [] } - PatchPoint MethodRedefined(Object@0x1010, foo@0x1018, cme:0x1020) PatchPoint NoSingletonClass(Object@0x1010) + PatchPoint MethodRedefined(Object@0x1010, foo@0x1018, cme:0x1020) v22:HeapObject[class_exact*:Object@VALUE(0x1010)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1010)] v23:Any = Snapshot FrameState { pc: 0x1008, stack: [v6, v11, v13], locals: [] } v24:BasicObject = SendWithoutBlockDirect v22, :foo (0x1048), v11, v13 @@ -3565,10 +3565,10 @@ pub mod hir_build_tests { v21:ArrayExact = GuardType v13, ArrayExact v22:CInt64 = ArrayLength v21 v23:CInt64[2] = GuardBitEquals v22, CInt64(2) - v24:Fixnum[1] = Const Value(1) - v25:BasicObject = ArrayArefFixnum v21, v24 - v26:Fixnum[0] = Const Value(0) - v27:BasicObject = ArrayArefFixnum v21, v26 + v24:CInt64[1] = Const CInt64(1) + v25:BasicObject = ArrayAref v21, v24 + v26:CInt64[0] = Const CInt64(0) + v27:BasicObject = ArrayAref v21, v26 PatchPoint NoEPEscape(test) CheckInterrupts Return v13 diff --git a/zjit/src/hir_type/mod.rs b/zjit/src/hir_type/mod.rs index 061fe1657885a4..cc6a208bcd413e 100644 --- a/zjit/src/hir_type/mod.rs +++ b/zjit/src/hir_type/mod.rs @@ -395,6 +395,13 @@ impl Type { } } + pub fn cint64_value(&self) -> Option { + match (self.is_subtype(types::CInt64), &self.spec) { + (true, Specialization::Int(val)) => Some(*val as i64), + _ => None, + } + } + /// Return true if the Type has object specialization and false otherwise. pub fn ruby_object_known(&self) -> bool { matches!(self.spec, Specialization::Object(_)) diff --git a/zjit/src/invariants.rs b/zjit/src/invariants.rs index 749e0a9e1d38d9..d183eb18abccb8 100644 --- a/zjit/src/invariants.rs +++ b/zjit/src/invariants.rs @@ -436,6 +436,16 @@ pub extern "C" fn rb_zjit_tracing_invalidate_all() { }); } +/// Returns true if we've seen a singleton class of a given class since boot. +/// This is used to avoid an invalidation loop where we repeatedly compile code +/// that assumes no singleton class, only to have it invalidated. +pub fn has_singleton_class_of(klass: VALUE) -> bool { + ZJITState::get_invariants() + .no_singleton_class_patch_points + .get(&klass) + .map_or(false, |patch_points| patch_points.is_empty()) +} + #[unsafe(no_mangle)] pub extern "C" fn rb_zjit_invalidate_no_singleton_class(klass: VALUE) { if !zjit_enabled_p() { @@ -444,11 +454,22 @@ pub extern "C" fn rb_zjit_invalidate_no_singleton_class(klass: VALUE) { with_vm_lock(src_loc!(), || { let invariants = ZJITState::get_invariants(); - if let Some(patch_points) = invariants.no_singleton_class_patch_points.remove(&klass) { - let cb = ZJITState::get_code_block(); - debug!("Singleton class created for {:?}", klass); - compile_patch_points!(cb, patch_points, "Singleton class created for {:?}", klass); - cb.mark_all_executable(); + match invariants.no_singleton_class_patch_points.get_mut(&klass) { + Some(patch_points) => { + // Invalidate existing patch points and let has_singleton_class_of() + // return true when they are compiled again + let patch_points = mem::take(patch_points); + if !patch_points.is_empty() { + let cb = ZJITState::get_code_block(); + debug!("Singleton class created for {:?}", klass); + compile_patch_points!(cb, patch_points, "Singleton class created for {:?}", klass); + cb.mark_all_executable(); + } + } + None => { + // Let has_singleton_class_of() return true for this class + invariants.no_singleton_class_patch_points.insert(klass, HashSet::new()); + } } }); } diff --git a/zjit/src/profile.rs b/zjit/src/profile.rs index 867d97641be92f..7a584afd6fd1f6 100644 --- a/zjit/src/profile.rs +++ b/zjit/src/profile.rs @@ -3,6 +3,7 @@ // We use the YARV bytecode constants which have a CRuby-style name #![allow(non_upper_case_globals)] +use std::collections::HashMap; use crate::{cruby::*, payload::get_or_create_iseq_payload, options::{get_option, NumProfiles}}; use crate::distribution::{Distribution, DistributionSummary}; use crate::stats::Counter::profile_time_ns; @@ -90,6 +91,7 @@ fn profile_insn(bare_opcode: ruby_vminsn_type, ec: EcPtr) { YARVINSN_opt_size => profile_operands(profiler, profile, 1), YARVINSN_opt_succ => profile_operands(profiler, profile, 1), YARVINSN_invokeblock => profile_block_handler(profiler, profile), + YARVINSN_invokesuper => profile_invokesuper(profiler, profile), YARVINSN_opt_send_without_block | YARVINSN_send => { let cd: *const rb_call_data = profiler.insn_opnd(0).as_ptr(); let argc = unsafe { vm_ci_argc((*cd).ci) }; @@ -153,6 +155,37 @@ fn profile_block_handler(profiler: &mut Profiler, profile: &mut IseqProfile) { types[0].observe(ty); } +fn profile_invokesuper(profiler: &mut Profiler, profile: &mut IseqProfile) { + let cme = unsafe { rb_vm_frame_method_entry(profiler.cfp) }; + let cme_value = VALUE(cme as usize); // CME is a T_IMEMO, which is a VALUE + + match profile.super_cme.get(&profiler.insn_idx) { + None => { + // If `None`, then this is our first time looking at `super` for this instruction. + profile.super_cme.insert(profiler.insn_idx, Some(cme_value)); + }, + Some(Some(existing_cme)) => { + // Check if the stored method entry is the same as the current one. If it isn't, then + // mark the call site as polymorphic. + if *existing_cme != cme_value { + profile.super_cme.insert(profiler.insn_idx, None); + } + } + Some(None) => { + // We've visited this instruction and explicitly stored `None` to mark the call site + // as polymorphic. + } + } + + unsafe { rb_gc_writebarrier(profiler.iseq.into(), cme_value) }; + + let cd: *const rb_call_data = profiler.insn_opnd(0).as_ptr(); + let argc = unsafe { vm_ci_argc((*cd).ci) }; + + // Profile all the arguments and self (+1). + profile_operands(profiler, profile, (argc + 1) as usize); +} + #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct Flags(u32); @@ -324,6 +357,9 @@ pub struct IseqProfile { /// Number of profiled executions for each YARV instruction, indexed by the instruction index num_profiles: Vec, + + /// Method entries for `super` calls (stored as VALUE to be GC-safe) + super_cme: HashMap> } impl IseqProfile { @@ -331,6 +367,7 @@ impl IseqProfile { Self { opnd_types: vec![vec![]; iseq_size as usize], num_profiles: vec![0; iseq_size as usize], + super_cme: HashMap::new(), } } @@ -339,6 +376,11 @@ impl IseqProfile { self.opnd_types.get(insn_idx).map(|v| &**v) } + pub fn get_super_method_entry(&self, insn_idx: usize) -> Option<*const rb_callable_method_entry_t> { + self.super_cme.get(&insn_idx) + .and_then(|opt| opt.map(|v| v.0 as *const rb_callable_method_entry_t)) + } + /// Run a given callback with every object in IseqProfile pub fn each_object(&self, callback: impl Fn(VALUE)) { for operands in &self.opnd_types { @@ -349,9 +391,15 @@ impl IseqProfile { } } } + + for cme_value in self.super_cme.values() { + if let Some(cme) = cme_value { + callback(*cme); + } + } } - /// Run a given callback with a mutable reference to every object in IseqProfile + /// Run a given callback with a mutable reference to every object in IseqProfile. pub fn each_object_mut(&mut self, callback: impl Fn(&mut VALUE)) { for operands in &mut self.opnd_types { for distribution in operands { @@ -361,6 +409,13 @@ impl IseqProfile { } } } + + // Update CME references if they move during compaction. + for cme_value in self.super_cme.values_mut() { + if let Some(cme) = cme_value { + callback(cme); + } + } } } diff --git a/zjit/src/stats.rs b/zjit/src/stats.rs index 01bd6e2f597ec8..506bd82686a3f2 100644 --- a/zjit/src/stats.rs +++ b/zjit/src/stats.rs @@ -175,6 +175,7 @@ make_counters! { exit_unhandled_tailcall, exit_unhandled_splat, exit_unhandled_kwarg, + exit_unhandled_block_arg, exit_unknown_special_variable, exit_unhandled_hir_insn, exit_unhandled_yarv_insn, @@ -195,6 +196,7 @@ make_counters! { exit_guard_not_shared_failure, exit_guard_less_failure, exit_guard_greater_eq_failure, + exit_guard_super_method_entry, exit_patchpoint_bop_redefined, exit_patchpoint_method_redefined, exit_patchpoint_stable_constant_names, @@ -241,10 +243,21 @@ make_counters! { send_fallback_one_or_more_complex_arg_pass, // Caller has keyword arguments but callee doesn't expect them. send_fallback_unexpected_keyword_args, + // Singleton class previously created for receiver class. + send_fallback_singleton_class_seen, send_fallback_bmethod_non_iseq_proc, send_fallback_obj_to_string_not_string, send_fallback_send_cfunc_variadic, send_fallback_send_cfunc_array_variadic, + send_fallback_super_call_with_block, + send_fallback_super_class_not_found, + send_fallback_super_complex_args_pass, + send_fallback_super_fallback_no_profile, + send_fallback_super_not_optimized_method_type, + send_fallback_super_polymorphic, + send_fallback_super_target_not_found, + send_fallback_super_target_complex_args_pass, + send_fallback_cannot_send_direct, send_fallback_uncategorized, } @@ -355,6 +368,21 @@ make_counters! { unspecialized_send_def_type_refined, unspecialized_send_def_type_null, + // Super call def_type related to send fallback to dynamic dispatch + unspecialized_super_def_type_iseq, + unspecialized_super_def_type_cfunc, + unspecialized_super_def_type_attrset, + unspecialized_super_def_type_ivar, + unspecialized_super_def_type_bmethod, + unspecialized_super_def_type_zsuper, + unspecialized_super_def_type_alias, + unspecialized_super_def_type_undef, + unspecialized_super_def_type_not_implemented, + unspecialized_super_def_type_optimized, + unspecialized_super_def_type_missing, + unspecialized_super_def_type_refined, + unspecialized_super_def_type_null, + // Unsupported parameter features complex_arg_pass_param_rest, complex_arg_pass_param_post, @@ -501,6 +529,7 @@ pub fn side_exit_counter(reason: crate::hir::SideExitReason) -> Counter { UnknownSpecialVariable(_) => exit_unknown_special_variable, UnhandledHIRInsn(_) => exit_unhandled_hir_insn, UnhandledYARVInsn(_) => exit_unhandled_yarv_insn, + UnhandledBlockArg => exit_unhandled_block_arg, FixnumAddOverflow => exit_fixnum_add_overflow, FixnumSubOverflow => exit_fixnum_sub_overflow, FixnumMultOverflow => exit_fixnum_mult_overflow, @@ -516,6 +545,7 @@ pub fn side_exit_counter(reason: crate::hir::SideExitReason) -> Counter { GuardNotShared => exit_guard_not_shared_failure, GuardLess => exit_guard_less_failure, GuardGreaterEq => exit_guard_greater_eq_failure, + GuardSuperMethodEntry => exit_guard_super_method_entry, CalleeSideExit => exit_callee_side_exit, ObjToStringFallback => exit_obj_to_string_fallback, Interrupt => exit_interrupt, @@ -573,12 +603,21 @@ pub fn send_fallback_counter(reason: crate::hir::SendFallbackReason) -> Counter SendCfuncArrayVariadic => send_fallback_send_cfunc_array_variadic, ComplexArgPass => send_fallback_one_or_more_complex_arg_pass, UnexpectedKeywordArgs => send_fallback_unexpected_keyword_args, + SingletonClassSeen => send_fallback_singleton_class_seen, ArgcParamMismatch => send_fallback_argc_param_mismatch, BmethodNonIseqProc => send_fallback_bmethod_non_iseq_proc, SendNotOptimizedMethodType(_) => send_fallback_send_not_optimized_method_type, SendNotOptimizedNeedPermission => send_fallback_send_not_optimized_need_permission, CCallWithFrameTooManyArgs => send_fallback_ccall_with_frame_too_many_args, ObjToStringNotString => send_fallback_obj_to_string_not_string, + SuperCallWithBlock => send_fallback_super_call_with_block, + SuperClassNotFound => send_fallback_super_class_not_found, + SuperComplexArgsPass => send_fallback_super_complex_args_pass, + SuperNoProfiles => send_fallback_super_fallback_no_profile, + SuperNotOptimizedMethodType(_) => send_fallback_super_not_optimized_method_type, + SuperPolymorphic => send_fallback_super_polymorphic, + SuperTargetNotFound => send_fallback_super_target_not_found, + SuperTargetComplexArgsPass => send_fallback_super_target_complex_args_pass, Uncategorized(_) => send_fallback_uncategorized, } } @@ -638,6 +677,27 @@ pub fn send_fallback_counter_for_method_type(method_type: crate::hir::MethodType } } +pub fn send_fallback_counter_for_super_method_type(method_type: crate::hir::MethodType) -> Counter { + use crate::hir::MethodType::*; + use crate::stats::Counter::*; + + match method_type { + Iseq => unspecialized_super_def_type_iseq, + Cfunc => unspecialized_super_def_type_cfunc, + Attrset => unspecialized_super_def_type_attrset, + Ivar => unspecialized_super_def_type_ivar, + Bmethod => unspecialized_super_def_type_bmethod, + Zsuper => unspecialized_super_def_type_zsuper, + Alias => unspecialized_super_def_type_alias, + Undefined => unspecialized_super_def_type_undef, + NotImplemented => unspecialized_super_def_type_not_implemented, + Optimized => unspecialized_super_def_type_optimized, + Missing => unspecialized_super_def_type_missing, + Refined => unspecialized_super_def_type_refined, + Null => unspecialized_super_def_type_null, + } +} + /// Primitive called in zjit.rb. Zero out all the counters. #[unsafe(no_mangle)] pub extern "C" fn rb_zjit_reset_stats_bang(_ec: EcPtr, _self: VALUE) -> VALUE {