Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 0841ec6

Browse files
committed
Add more code_gc tests
1 parent ddff0d1 commit 0841ec6

File tree

1 file changed

+99
-19
lines changed

1 file changed

+99
-19
lines changed

test/ruby/test_yjit.rb

+99-19
Original file line numberDiff line numberDiff line change
@@ -826,7 +826,100 @@ def foo
826826
end
827827

828828
def test_code_gc
829-
assert_compiles(<<~'RUBY', exits: :any, result: :ok)
829+
assert_compiles(code_gc_helpers + <<~'RUBY', exits: :any, result: :ok)
830+
return :not_paged unless add_pages(100) # prepare freeable pages
831+
code_gc # first code GC
832+
return :not_compiled1 unless compiles { nil } # should be JITable again
833+
834+
code_gc # second code GC
835+
return :not_compiled2 unless compiles { nil } # should be JITable again
836+
837+
code_gc_count = RubyVM::YJIT.runtime_stats[:code_gc_count]
838+
return :"code_gc_#{code_gc_count}" if code_gc_count && code_gc_count != 2
839+
840+
:ok
841+
RUBY
842+
end
843+
844+
def test_on_stack_code_gc_call
845+
assert_compiles(code_gc_helpers + <<~'RUBY', exits: :any, result: :ok)
846+
fiber = Fiber.new {
847+
# Loop to call the same basic block again after Fiber.yield
848+
while true
849+
Fiber.yield(nil.to_i)
850+
end
851+
}
852+
853+
return :not_paged1 unless add_pages(400) # go to a page without initial ocb code
854+
return :broken_resume1 if fiber.resume != 0 # JIT the fiber
855+
code_gc # first code GC, which should not free the fiber page
856+
return :broken_resume2 if fiber.resume != 0 # The code should be still callable
857+
858+
code_gc_count = RubyVM::YJIT.runtime_stats[:code_gc_count]
859+
return :"code_gc_#{code_gc_count}" if code_gc_count && code_gc_count != 1
860+
861+
:ok
862+
RUBY
863+
end
864+
865+
def test_on_stack_code_gc_twice
866+
assert_compiles(code_gc_helpers + <<~'RUBY', exits: :any, result: :ok)
867+
fiber = Fiber.new {
868+
# Loop to call the same basic block again after Fiber.yield
869+
while Fiber.yield(nil.to_i); end
870+
}
871+
872+
return :not_paged1 unless add_pages(400) # go to a page without initial ocb code
873+
return :broken_resume1 if fiber.resume(true) != 0 # JIT the fiber
874+
code_gc # first code GC, which should not free the fiber page
875+
876+
return :not_paged2 unless add_pages(300) # add some stuff to be freed
877+
# Not calling fiber.resume here to test the case that the YJIT payload loses some
878+
# information at the previous code GC. The payload should still be there, and
879+
# thus we could know the fiber ISEQ is still on stack on this second code GC.
880+
code_gc # second code GC, which should still not free the fiber page
881+
882+
return :not_paged3 unless add_pages(200) # attempt to overwrite the fiber page (it shouldn't)
883+
return :broken_resume2 if fiber.resume(true) != 0 # The fiber code should be still fine
884+
885+
return :broken_resume3 if fiber.resume(false) != nil # terminate the fiber
886+
code_gc # third code GC, freeing a page that used to be on stack
887+
888+
return :not_paged4 unless add_pages(100) # check everything still works
889+
890+
code_gc_count = RubyVM::YJIT.runtime_stats[:code_gc_count]
891+
return :"code_gc_#{code_gc_count}" if code_gc_count && code_gc_count != 3
892+
893+
:ok
894+
RUBY
895+
end
896+
897+
def test_code_gc_with_many_iseqs
898+
assert_compiles(code_gc_helpers + <<~'RUBY', exits: :any, result: :ok, mem_size: 1)
899+
fiber = Fiber.new {
900+
# Loop to call the same basic block again after Fiber.yield
901+
while true
902+
Fiber.yield(nil.to_i)
903+
end
904+
}
905+
906+
return :not_paged1 unless add_pages(500) # use some pages
907+
return :broken_resume1 if fiber.resume != 0 # leave an on-stack code as well
908+
909+
return :not_gc if add_pages(2000) # use a whole lot of pages to run out of 1MiB
910+
return :broken_resume2 if fiber.resume != 0 # on-stack code should be callable
911+
912+
code_gc_count = RubyVM::YJIT.runtime_stats[:code_gc_count]
913+
return :"code_gc_#{code_gc_count}" if code_gc_count && code_gc_count == 0
914+
915+
:ok
916+
RUBY
917+
end
918+
919+
private
920+
921+
def code_gc_helpers
922+
<<~'RUBY'
830923
def compiles(&block)
831924
failures = RubyVM::YJIT.runtime_stats[:compilation_failure]
832925
block.call
@@ -835,37 +928,23 @@ def compiles(&block)
835928
836929
def add_pages(num_jits)
837930
pages = RubyVM::YJIT.runtime_stats[:compiled_page_count]
838-
100.times { return false unless eval('compiles { nil.to_i }') }
931+
num_jits.times { return false unless eval('compiles { nil.to_i }') }
839932
pages.nil? || pages < RubyVM::YJIT.runtime_stats[:compiled_page_count]
840933
end
841934
842935
def code_gc
843936
RubyVM::YJIT.simulate_oom! # bump write_pos
844937
eval('proc { nil }.call') # trigger code GC
845938
end
846-
847-
return :not_paged unless add_pages(100) # prepare freeable pages
848-
code_gc # first code GC
849-
return :not_compiled1 unless compiles { nil } # should be JITable again
850-
851-
code_gc # second code GC
852-
return :not_compiled2 unless compiles { nil } # should be JITable again
853-
854-
code_gc_count = RubyVM::YJIT.runtime_stats[:code_gc_count]
855-
return :"code_gc_#{code_gc_count}" if code_gc_count && code_gc_count != 2
856-
857-
:ok
858939
RUBY
859940
end
860941

861-
private
862-
863942
def assert_no_exits(script)
864943
assert_compiles(script)
865944
end
866945

867946
ANY = Object.new
868-
def assert_compiles(test_script, insns: [], call_threshold: 1, stdout: nil, exits: {}, result: ANY, frozen_string_literal: nil)
947+
def assert_compiles(test_script, insns: [], call_threshold: 1, stdout: nil, exits: {}, result: ANY, frozen_string_literal: nil, mem_size: nil)
869948
reset_stats = <<~RUBY
870949
RubyVM::YJIT.runtime_stats
871950
RubyVM::YJIT.reset_stats!
@@ -899,7 +978,7 @@ def collect_insns(iseq)
899978
#{write_results}
900979
RUBY
901980

902-
status, out, err, stats = eval_with_jit(script, call_threshold: call_threshold)
981+
status, out, err, stats = eval_with_jit(script, call_threshold:, mem_size:)
903982

904983
assert status.success?, "exited with status #{status.to_i}, stderr:\n#{err}"
905984

@@ -953,12 +1032,13 @@ def script_shell_encode(s)
9531032
s.chars.map { |c| c.ascii_only? ? c : "\\u%x" % c.codepoints[0] }.join
9541033
end
9551034

956-
def eval_with_jit(script, call_threshold: 1, timeout: 1000)
1035+
def eval_with_jit(script, call_threshold: 1, timeout: 1000, mem_size: nil)
9571036
args = [
9581037
"--disable-gems",
9591038
"--yjit-call-threshold=#{call_threshold}",
9601039
"--yjit-stats"
9611040
]
1041+
args << "--yjit-exec-mem-size=#{mem_size}" if mem_size
9621042
args << "-e" << script_shell_encode(script)
9631043
stats_r, stats_w = IO.pipe
9641044
out, err, status = EnvUtil.invoke_ruby(args,

0 commit comments

Comments
 (0)