Skip to content

Commit c8fd574

Browse files
committed
ZJIT: Support guarding *Exact types
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.
1 parent ad7d75c commit c8fd574

3 files changed

Lines changed: 74 additions & 0 deletions

File tree

test/ruby/test_zjit.rb

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -915,6 +915,30 @@ def test_instruction_order
915915
end
916916
end
917917

918+
def test_module_name_with_guard
919+
assert_compiles '["String", "Integer"]', %q{
920+
def test(modules)
921+
modules.map do |mod|
922+
mod.name
923+
end
924+
end
925+
926+
test([String, Integer])
927+
}, call_threshold: 2
928+
end
929+
930+
def test_string_bytesize_with_guard
931+
assert_compiles '[3, 3]', %q{
932+
def test(strings)
933+
strings.map do |string|
934+
string.bytesize
935+
end
936+
end
937+
938+
test(["foo", "bar"])
939+
}, call_threshold: 2
940+
end
941+
918942
private
919943

920944
# Assert that every method call in `test_script` can be compiled by ZJIT

zjit/src/codegen.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -979,6 +979,16 @@ fn gen_guard_type(jit: &mut JITState, asm: &mut Assembler, val: lir::Opnd, guard
979979
// Check if opnd is Fixnum
980980
asm.test(val, Opnd::UImm(RUBY_FIXNUM_FLAG as u64));
981981
asm.jz(side_exit(jit, state)?);
982+
} else if let Some(expected_class) = guard_type.runtime_exact_ruby_class() {
983+
// Guard for exact class types (e.g., ClassExact, ArrayExact, StringExact, etc.)
984+
asm_comment!(asm, "guard exact class");
985+
986+
// Get the class of the value
987+
let klass = asm.ccall(rb_yarv_class_of as *const u8, vec![val]);
988+
989+
// Compare with expected class
990+
asm.cmp(klass, Opnd::UImm(expected_class.into()));
991+
asm.jne(side_exit(jit, state)?);
982992
} else {
983993
unimplemented!("unsupported type: {guard_type}");
984994
}

zjit/src/hir.rs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6517,4 +6517,44 @@ mod opt_tests {
65176517
Return v5
65186518
"#]]);
65196519
}
6520+
6521+
#[test]
6522+
fn test_guard_class_for_module_name() {
6523+
eval("
6524+
def test(mod)
6525+
mod.name
6526+
end
6527+
# Profile with Class objects
6528+
test(String)
6529+
test(Integer)
6530+
");
6531+
assert_optimized_method_hir("test", expect![[r#"
6532+
fn test:
6533+
bb0(v0:BasicObject, v1:BasicObject):
6534+
PatchPoint MethodRedefined(Class@0x1000, name@0x1008)
6535+
v7:ClassExact = GuardType v1, ClassExact
6536+
v8:StringExact|NilClassExact = CCall name@0x1010, v7
6537+
Return v8
6538+
"#]]);
6539+
}
6540+
6541+
#[test]
6542+
fn test_guard_string_for_bytesize() {
6543+
eval("
6544+
def test(str)
6545+
str.bytesize
6546+
end
6547+
# Profile with String objects
6548+
test('hello')
6549+
test('world')
6550+
");
6551+
assert_optimized_method_hir("test", expect![[r#"
6552+
fn test:
6553+
bb0(v0:BasicObject, v1:BasicObject):
6554+
PatchPoint MethodRedefined(String@0x1000, bytesize@0x1008)
6555+
v7:StringExact = GuardType v1, StringExact
6556+
v8:Fixnum = CCall bytesize@0x1010, v7
6557+
Return v8
6558+
"#]]);
6559+
}
65206560
}

0 commit comments

Comments
 (0)