mirror of
https://github.com/ruby/ruby.git
synced 2026-01-27 04:24:23 +00: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
@ -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…
x
Reference in New Issue
Block a user