Skip to content

Commit 6ae9163

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 c691095 commit 6ae9163

2 files changed

Lines changed: 203 additions & 0 deletions

File tree

test/ruby/test_zjit.rb

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

918+
def test_module_name_with_guard_passes
919+
assert_compiles '"Integer"', %q{
920+
def test(mod)
921+
mod.name
922+
end
923+
924+
test(String)
925+
test(Integer)
926+
}, call_threshold: 2
927+
end
928+
929+
def test_module_name_with_guard_fallthrough
930+
# This test demonstrates that the guard side exit works correctly
931+
# In this case, when we call with a non-Class object, it should fall back to interpreter
932+
assert_compiles '["String", "Integer", "Bar"]', %q{
933+
class MyClass
934+
def name = "Bar"
935+
end
936+
937+
def test(mod)
938+
mod.name
939+
end
940+
941+
results = []
942+
results << test(String)
943+
results << test(Integer)
944+
results << test(MyClass.new)
945+
946+
results
947+
}, call_threshold: 2
948+
end
949+
950+
def test_string_bytesize_with_guard
951+
assert_compiles '5', %q{
952+
def test(str)
953+
str.bytesize
954+
end
955+
956+
test('hello')
957+
test('world')
958+
}, call_threshold: 2
959+
end
960+
961+
def test_nil_value_nil_opt_with_guard
962+
assert_compiles 'true', %q{
963+
def test(val) = val.nil?
964+
965+
test(nil)
966+
test(nil)
967+
}, call_threshold: 2, insns: [:opt_nil_p]
968+
end
969+
970+
def test_nil_value_nil_opt_with_guard_fallthrough
971+
assert_compiles 'false', %q{
972+
def test(val) = val.nil?
973+
974+
test(nil)
975+
test(nil)
976+
test(1)
977+
}, call_threshold: 2, insns: [:opt_nil_p]
978+
end
979+
980+
def test_true_nil_opt_with_guard
981+
assert_compiles 'false', %q{
982+
def test(val) = val.nil?
983+
984+
test(true)
985+
test(true)
986+
}, call_threshold: 2, insns: [:opt_nil_p]
987+
end
988+
989+
def test_true_nil_opt_with_guard_fallthrough
990+
assert_compiles 'true', %q{
991+
def test(val) = val.nil?
992+
993+
test(true)
994+
test(true)
995+
test(nil)
996+
}, call_threshold: 2, insns: [:opt_nil_p]
997+
end
998+
999+
def test_false_nil_opt_with_guard
1000+
assert_compiles 'false', %q{
1001+
def test(val) = val.nil?
1002+
1003+
test(false)
1004+
test(false)
1005+
}, call_threshold: 2, insns: [:opt_nil_p]
1006+
end
1007+
1008+
def test_false_nil_opt_with_guard_fallthrough
1009+
assert_compiles 'true', %q{
1010+
def test(val) = val.nil?
1011+
1012+
test(false)
1013+
test(false)
1014+
test(nil)
1015+
}, call_threshold: 2, insns: [:opt_nil_p]
1016+
end
1017+
1018+
def test_integer_nil_opt_with_guard
1019+
assert_compiles 'false', %q{
1020+
def test(val) = val.nil?
1021+
1022+
test(1)
1023+
test(2)
1024+
}, call_threshold: 2, insns: [:opt_nil_p]
1025+
end
1026+
1027+
def test_integer_nil_opt_with_guard_fallthrough
1028+
assert_compiles 'true', %q{
1029+
def test(val) = val.nil?
1030+
1031+
test(1)
1032+
test(2)
1033+
test(nil)
1034+
}, call_threshold: 2, insns: [:opt_nil_p]
1035+
end
1036+
1037+
def test_float_nil_opt_with_guard
1038+
assert_compiles 'false', %q{
1039+
def test(val) = val.nil?
1040+
1041+
test(1.0)
1042+
test(2.0)
1043+
}, call_threshold: 2, insns: [:opt_nil_p]
1044+
end
1045+
1046+
def test_float_nil_opt_with_guard_fallthrough
1047+
assert_compiles 'true', %q{
1048+
def test(val) = val.nil?
1049+
1050+
test(1.0)
1051+
test(2.0)
1052+
test(nil)
1053+
}, call_threshold: 2, insns: [:opt_nil_p]
1054+
end
1055+
1056+
def test_symbol_nil_opt_with_guard
1057+
assert_compiles 'false', %q{
1058+
def test(val) = val.nil?
1059+
1060+
test(:foo)
1061+
test(:bar)
1062+
}, call_threshold: 2, insns: [:opt_nil_p]
1063+
end
1064+
1065+
def test_symbol_nil_opt_with_guard_fallthrough
1066+
assert_compiles 'true', %q{
1067+
def test(val) = val.nil?
1068+
1069+
test(:foo)
1070+
test(:bar)
1071+
test(nil)
1072+
}, call_threshold: 2, insns: [:opt_nil_p]
1073+
end
1074+
1075+
def test_class_nil_opt_with_guard
1076+
assert_compiles 'false', %q{
1077+
def test(val) = val.nil?
1078+
1079+
test(String)
1080+
test(Integer)
1081+
}, call_threshold: 2, insns: [:opt_nil_p]
1082+
end
1083+
1084+
def test_class_nil_opt_with_guard_fallthrough
1085+
assert_compiles 'true', %q{
1086+
def test(val) = val.nil?
1087+
1088+
test(String)
1089+
test(Integer)
1090+
test(nil)
1091+
}, call_threshold: 2, insns: [:opt_nil_p]
1092+
end
1093+
1094+
def test_module_nil_opt_with_guard
1095+
assert_compiles 'false', %q{
1096+
def test(val) = val.nil?
1097+
1098+
test(Enumerable)
1099+
test(Kernel)
1100+
}, call_threshold: 2, insns: [:opt_nil_p]
1101+
end
1102+
1103+
def test_module_nil_opt_with_guard_fallthrough
1104+
assert_compiles 'true', %q{
1105+
def test(val) = val.nil?
1106+
1107+
test(Enumerable)
1108+
test(Kernel)
1109+
test(nil)
1110+
}, call_threshold: 2, insns: [:opt_nil_p]
1111+
end
1112+
9181113
private
9191114

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

zjit/src/codegen.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -974,6 +974,14 @@ fn gen_guard_type(jit: &mut JITState, asm: &mut Assembler, val: lir::Opnd, guard
974974
// Check if opnd is Fixnum
975975
asm.test(val, Opnd::UImm(RUBY_FIXNUM_FLAG as u64));
976976
asm.jz(side_exit(jit, state)?);
977+
} else if let Some(expected_class) = guard_type.runtime_exact_ruby_class() {
978+
asm_comment!(asm, "guard exact class");
979+
980+
// Get the class of the value
981+
let klass = asm.ccall(rb_yarv_class_of as *const u8, vec![val]);
982+
983+
asm.cmp(klass, Opnd::Value(expected_class));
984+
asm.jne(side_exit(jit, state)?);
977985
} else {
978986
unimplemented!("unsupported type: {guard_type}");
979987
}

0 commit comments

Comments
 (0)