mirror of
https://github.com/ruby/ruby.git
synced 2025-08-15 13:39:04 +02:00
ZJIT: Support guarding *Exact types (#13797)
ZJIT already can generate guard type instructions for *Exact types. For example: ``` def test(strings) strings.map do |string| string.bytesize end end test(["foo", "bar"]) ``` ``` HIR: fn block in test: bb0(v0:BasicObject, v1:BasicObject): PatchPoint MethodRedefined(String@0x1014be890, bytesize@0x19f1) v7:StringExact = GuardType v1, StringExact v8:Fixnum = CCall bytesize@0x16fa4cc18, v7 Return v8 ``` But zjit only supported guarding fixnums so this script would panic. This commit adds support for guarding *Exact types.
This commit is contained in:
parent
5aaedc052c
commit
e9cd3060ac
2 changed files with 203 additions and 0 deletions
|
@ -915,6 +915,201 @@ class TestZJIT < Test::Unit::TestCase
|
|||
end
|
||||
end
|
||||
|
||||
def test_module_name_with_guard_passes
|
||||
assert_compiles '"Integer"', %q{
|
||||
def test(mod)
|
||||
mod.name
|
||||
end
|
||||
|
||||
test(String)
|
||||
test(Integer)
|
||||
}, call_threshold: 2
|
||||
end
|
||||
|
||||
def test_module_name_with_guard_fallthrough
|
||||
# This test demonstrates that the guard side exit works correctly
|
||||
# In this case, when we call with a non-Class object, it should fall back to interpreter
|
||||
assert_compiles '["String", "Integer", "Bar"]', %q{
|
||||
class MyClass
|
||||
def name = "Bar"
|
||||
end
|
||||
|
||||
def test(mod)
|
||||
mod.name
|
||||
end
|
||||
|
||||
results = []
|
||||
results << test(String)
|
||||
results << test(Integer)
|
||||
results << test(MyClass.new)
|
||||
|
||||
results
|
||||
}, call_threshold: 2
|
||||
end
|
||||
|
||||
def test_string_bytesize_with_guard
|
||||
assert_compiles '5', %q{
|
||||
def test(str)
|
||||
str.bytesize
|
||||
end
|
||||
|
||||
test('hello')
|
||||
test('world')
|
||||
}, call_threshold: 2
|
||||
end
|
||||
|
||||
def test_nil_value_nil_opt_with_guard
|
||||
assert_compiles 'true', %q{
|
||||
def test(val) = val.nil?
|
||||
|
||||
test(nil)
|
||||
test(nil)
|
||||
}, call_threshold: 2, insns: [:opt_nil_p]
|
||||
end
|
||||
|
||||
def test_nil_value_nil_opt_with_guard_fallthrough
|
||||
assert_compiles 'false', %q{
|
||||
def test(val) = val.nil?
|
||||
|
||||
test(nil)
|
||||
test(nil)
|
||||
test(1)
|
||||
}, call_threshold: 2, insns: [:opt_nil_p]
|
||||
end
|
||||
|
||||
def test_true_nil_opt_with_guard
|
||||
assert_compiles 'false', %q{
|
||||
def test(val) = val.nil?
|
||||
|
||||
test(true)
|
||||
test(true)
|
||||
}, call_threshold: 2, insns: [:opt_nil_p]
|
||||
end
|
||||
|
||||
def test_true_nil_opt_with_guard_fallthrough
|
||||
assert_compiles 'true', %q{
|
||||
def test(val) = val.nil?
|
||||
|
||||
test(true)
|
||||
test(true)
|
||||
test(nil)
|
||||
}, call_threshold: 2, insns: [:opt_nil_p]
|
||||
end
|
||||
|
||||
def test_false_nil_opt_with_guard
|
||||
assert_compiles 'false', %q{
|
||||
def test(val) = val.nil?
|
||||
|
||||
test(false)
|
||||
test(false)
|
||||
}, call_threshold: 2, insns: [:opt_nil_p]
|
||||
end
|
||||
|
||||
def test_false_nil_opt_with_guard_fallthrough
|
||||
assert_compiles 'true', %q{
|
||||
def test(val) = val.nil?
|
||||
|
||||
test(false)
|
||||
test(false)
|
||||
test(nil)
|
||||
}, call_threshold: 2, insns: [:opt_nil_p]
|
||||
end
|
||||
|
||||
def test_integer_nil_opt_with_guard
|
||||
assert_compiles 'false', %q{
|
||||
def test(val) = val.nil?
|
||||
|
||||
test(1)
|
||||
test(2)
|
||||
}, call_threshold: 2, insns: [:opt_nil_p]
|
||||
end
|
||||
|
||||
def test_integer_nil_opt_with_guard_fallthrough
|
||||
assert_compiles 'true', %q{
|
||||
def test(val) = val.nil?
|
||||
|
||||
test(1)
|
||||
test(2)
|
||||
test(nil)
|
||||
}, call_threshold: 2, insns: [:opt_nil_p]
|
||||
end
|
||||
|
||||
def test_float_nil_opt_with_guard
|
||||
assert_compiles 'false', %q{
|
||||
def test(val) = val.nil?
|
||||
|
||||
test(1.0)
|
||||
test(2.0)
|
||||
}, call_threshold: 2, insns: [:opt_nil_p]
|
||||
end
|
||||
|
||||
def test_float_nil_opt_with_guard_fallthrough
|
||||
assert_compiles 'true', %q{
|
||||
def test(val) = val.nil?
|
||||
|
||||
test(1.0)
|
||||
test(2.0)
|
||||
test(nil)
|
||||
}, call_threshold: 2, insns: [:opt_nil_p]
|
||||
end
|
||||
|
||||
def test_symbol_nil_opt_with_guard
|
||||
assert_compiles 'false', %q{
|
||||
def test(val) = val.nil?
|
||||
|
||||
test(:foo)
|
||||
test(:bar)
|
||||
}, call_threshold: 2, insns: [:opt_nil_p]
|
||||
end
|
||||
|
||||
def test_symbol_nil_opt_with_guard_fallthrough
|
||||
assert_compiles 'true', %q{
|
||||
def test(val) = val.nil?
|
||||
|
||||
test(:foo)
|
||||
test(:bar)
|
||||
test(nil)
|
||||
}, call_threshold: 2, insns: [:opt_nil_p]
|
||||
end
|
||||
|
||||
def test_class_nil_opt_with_guard
|
||||
assert_compiles 'false', %q{
|
||||
def test(val) = val.nil?
|
||||
|
||||
test(String)
|
||||
test(Integer)
|
||||
}, call_threshold: 2, insns: [:opt_nil_p]
|
||||
end
|
||||
|
||||
def test_class_nil_opt_with_guard_fallthrough
|
||||
assert_compiles 'true', %q{
|
||||
def test(val) = val.nil?
|
||||
|
||||
test(String)
|
||||
test(Integer)
|
||||
test(nil)
|
||||
}, call_threshold: 2, insns: [:opt_nil_p]
|
||||
end
|
||||
|
||||
def test_module_nil_opt_with_guard
|
||||
assert_compiles 'false', %q{
|
||||
def test(val) = val.nil?
|
||||
|
||||
test(Enumerable)
|
||||
test(Kernel)
|
||||
}, call_threshold: 2, insns: [:opt_nil_p]
|
||||
end
|
||||
|
||||
def test_module_nil_opt_with_guard_fallthrough
|
||||
assert_compiles 'true', %q{
|
||||
def test(val) = val.nil?
|
||||
|
||||
test(Enumerable)
|
||||
test(Kernel)
|
||||
test(nil)
|
||||
}, call_threshold: 2, insns: [:opt_nil_p]
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Assert that every method call in `test_script` can be compiled by ZJIT
|
||||
|
|
|
@ -974,6 +974,14 @@ fn gen_guard_type(jit: &mut JITState, asm: &mut Assembler, val: lir::Opnd, guard
|
|||
// Check if opnd is Fixnum
|
||||
asm.test(val, Opnd::UImm(RUBY_FIXNUM_FLAG as u64));
|
||||
asm.jz(side_exit(jit, state)?);
|
||||
} else if let Some(expected_class) = guard_type.runtime_exact_ruby_class() {
|
||||
asm_comment!(asm, "guard exact class");
|
||||
|
||||
// Get the class of the value
|
||||
let klass = asm.ccall(rb_yarv_class_of as *const u8, vec![val]);
|
||||
|
||||
asm.cmp(klass, Opnd::Value(expected_class));
|
||||
asm.jne(side_exit(jit, state)?);
|
||||
} else {
|
||||
unimplemented!("unsupported type: {guard_type}");
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue