Skip to content

Commit 0d88ed5

Browse files
committed
Support inference of ClassExact type
1 parent 7349a1a commit 0d88ed5

4 files changed

Lines changed: 152 additions & 37 deletions

File tree

zjit/src/hir.rs

Lines changed: 79 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5731,6 +5731,82 @@ mod opt_tests {
57315731
"#]]);
57325732
}
57335733

5734+
#[test]
5735+
fn normal_class_type_inference() {
5736+
eval("
5737+
class C; end
5738+
def test = C
5739+
test # Warm the constant cache
5740+
");
5741+
assert_optimized_method_hir("test", expect![[r#"
5742+
fn test:
5743+
bb0(v0:BasicObject):
5744+
PatchPoint SingleRactorMode
5745+
PatchPoint StableConstantNames(0x1000, C)
5746+
v7:ClassExact[VALUE(0x1008)] = Const Value(VALUE(0x1008))
5747+
Return v7
5748+
"#]]);
5749+
}
5750+
5751+
#[test]
5752+
fn core_classes_type_inference() {
5753+
eval("
5754+
def test = [String, Class, BasicObject]
5755+
test # Warm the constant cache
5756+
");
5757+
assert_optimized_method_hir("test", expect![[r#"
5758+
fn test:
5759+
bb0(v0:BasicObject):
5760+
PatchPoint SingleRactorMode
5761+
PatchPoint StableConstantNames(0x1000, String)
5762+
v13:ClassExact[VALUE(0x1008)] = Const Value(VALUE(0x1008))
5763+
PatchPoint SingleRactorMode
5764+
PatchPoint StableConstantNames(0x1010, Class)
5765+
v16:ClassExact[VALUE(0x1018)] = Const Value(VALUE(0x1018))
5766+
PatchPoint SingleRactorMode
5767+
PatchPoint StableConstantNames(0x1020, BasicObject)
5768+
v19:ClassExact[VALUE(0x1028)] = Const Value(VALUE(0x1028))
5769+
v9:ArrayExact = NewArray v13, v16, v19
5770+
Return v9
5771+
"#]]);
5772+
}
5773+
5774+
#[test]
5775+
fn normal_module_instances_not_class_exact() {
5776+
eval("
5777+
def test = [Enumerable, Kernel]
5778+
test # Warm the constant cache
5779+
");
5780+
assert_optimized_method_hir("test", expect![[r#"
5781+
fn test:
5782+
bb0(v0:BasicObject):
5783+
PatchPoint SingleRactorMode
5784+
PatchPoint StableConstantNames(0x1000, Enumerable)
5785+
v11:BasicObject[VALUE(0x1008)] = Const Value(VALUE(0x1008))
5786+
PatchPoint SingleRactorMode
5787+
PatchPoint StableConstantNames(0x1010, Kernel)
5788+
v14:BasicObject[VALUE(0x1018)] = Const Value(VALUE(0x1018))
5789+
v7:ArrayExact = NewArray v11, v14
5790+
Return v7
5791+
"#]]);
5792+
}
5793+
5794+
#[test]
5795+
fn core_module_constant_not_class_exact() {
5796+
eval("
5797+
def test = Module
5798+
test # Warm the constant cache
5799+
");
5800+
assert_optimized_method_hir("test", expect![[r#"
5801+
fn test:
5802+
bb0(v0:BasicObject):
5803+
PatchPoint SingleRactorMode
5804+
PatchPoint StableConstantNames(0x1000, Module)
5805+
v7:ClassExact[VALUE(0x1008)] = Const Value(VALUE(0x1008))
5806+
Return v7
5807+
"#]]);
5808+
}
5809+
57345810
#[test]
57355811
fn eliminate_array_size() {
57365812
eval("
@@ -5880,7 +5956,7 @@ mod opt_tests {
58805956
bb0(v0:BasicObject):
58815957
PatchPoint SingleRactorMode
58825958
PatchPoint StableConstantNames(0x1000, Foo::Bar::C)
5883-
v7:BasicObject[VALUE(0x1008)] = Const Value(VALUE(0x1008))
5959+
v7:ClassExact[VALUE(0x1008)] = Const Value(VALUE(0x1008))
58845960
Return v7
58855961
"#]]);
58865962
}
@@ -5897,7 +5973,7 @@ mod opt_tests {
58975973
bb0(v0:BasicObject):
58985974
PatchPoint SingleRactorMode
58995975
PatchPoint StableConstantNames(0x1000, C)
5900-
v20:BasicObject[VALUE(0x1008)] = Const Value(VALUE(0x1008))
5976+
v20:ClassExact[VALUE(0x1008)] = Const Value(VALUE(0x1008))
59015977
v4:NilClassExact = Const Value(nil)
59025978
v11:BasicObject = SendWithoutBlock v20, :new
59035979
Return v11
@@ -5920,7 +5996,7 @@ mod opt_tests {
59205996
bb0(v0:BasicObject):
59215997
PatchPoint SingleRactorMode
59225998
PatchPoint StableConstantNames(0x1000, C)
5923-
v22:BasicObject[VALUE(0x1008)] = Const Value(VALUE(0x1008))
5999+
v22:ClassExact[VALUE(0x1008)] = Const Value(VALUE(0x1008))
59246000
v4:NilClassExact = Const Value(nil)
59256001
v5:Fixnum[1] = Const Value(1)
59266002
v13:BasicObject = SendWithoutBlock v22, :new, v5

zjit/src/hir_type/gen_hir_type.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ def base_type name
7474
base_type "Range"
7575
base_type "Set"
7676
base_type "Regexp"
77+
base_type "Class"
7778

7879
(integer, integer_exact) = base_type "Integer"
7980
# CRuby partitions Integer into immediate and non-immediate variants.

zjit/src/hir_type/hir_type.inc.rs

Lines changed: 41 additions & 32 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

zjit/src/hir_type/mod.rs

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#![allow(non_upper_case_globals)]
2-
use crate::cruby::{Qfalse, Qnil, Qtrue, VALUE, RUBY_T_ARRAY, RUBY_T_STRING, RUBY_T_HASH};
3-
use crate::cruby::{rb_cInteger, rb_cFloat, rb_cArray, rb_cHash, rb_cString, rb_cSymbol, rb_cObject, rb_cTrueClass, rb_cFalseClass, rb_cNilClass, rb_cRange, rb_cSet, rb_cRegexp};
2+
use crate::cruby::{Qfalse, Qnil, Qtrue, VALUE, RUBY_T_ARRAY, RUBY_T_STRING, RUBY_T_HASH, RUBY_T_CLASS};
3+
use crate::cruby::{rb_cInteger, rb_cFloat, rb_cArray, rb_cHash, rb_cString, rb_cSymbol, rb_cObject, rb_cTrueClass, rb_cFalseClass, rb_cNilClass, rb_cRange, rb_cSet, rb_cRegexp, rb_cClass, rb_cModule};
44
use crate::cruby::ClassRelationship;
55
use crate::cruby::get_class_name;
66
use crate::cruby::ruby_sym_to_rust_string;
@@ -145,6 +145,30 @@ fn is_range_exact(val: VALUE) -> bool {
145145
val.class_of() == unsafe { rb_cRange }
146146
}
147147

148+
fn is_class_exact(val: VALUE) -> bool {
149+
// Only consider objects with RUBY_T_CLASS type
150+
if val.builtin_type() != RUBY_T_CLASS {
151+
return false;
152+
}
153+
154+
// rb_mRubyVMFrozenCore is created with rb_class_new, so it has type RUBY_T_CLASS
155+
// but zjit shouldn't treat it as a class
156+
if val == unsafe { rb_mRubyVMFrozenCore } {
157+
return false;
158+
}
159+
160+
// Instances of Module (but not Class) should not be considered ClassExact
161+
// This excludes things like Enumerable, Comparable, etc.
162+
// But allows Module itself (which is an instance of Class)
163+
if val.class_of() == unsafe { rb_cModule } {
164+
return false;
165+
}
166+
167+
// Since we've already excluded modules and special objects,
168+
// any remaining RUBY_T_CLASS objects should be classes
169+
true
170+
}
171+
148172
impl Type {
149173
/// Create a `Type` from the given integer.
150174
pub const fn fixnum(val: i64) -> Type {
@@ -197,6 +221,9 @@ impl Type {
197221
else if is_string_exact(val) {
198222
Type { bits: bits::StringExact, spec: Specialization::Object(val) }
199223
}
224+
else if is_class_exact(val) {
225+
Type { bits: bits::ClassExact, spec: Specialization::Object(val) }
226+
}
200227
else if val.class_of() == unsafe { rb_cRegexp } {
201228
Type { bits: bits::RegexpExact, spec: Specialization::Object(val) }
202229
}
@@ -288,6 +315,7 @@ impl Type {
288315

289316
fn is_builtin(class: VALUE) -> bool {
290317
if class == unsafe { rb_cArray } { return true; }
318+
if class == unsafe { rb_cClass } { return true; }
291319
if class == unsafe { rb_cFalseClass } { return true; }
292320
if class == unsafe { rb_cFloat } { return true; }
293321
if class == unsafe { rb_cHash } { return true; }
@@ -396,6 +424,7 @@ impl Type {
396424
return Some(val);
397425
}
398426
if self.is_subtype(types::ArrayExact) { return Some(unsafe { rb_cArray }); }
427+
if self.is_subtype(types::ClassExact) { return Some(unsafe { rb_cClass }); }
399428
if self.is_subtype(types::FalseClassExact) { return Some(unsafe { rb_cFalseClass }); }
400429
if self.is_subtype(types::FloatExact) { return Some(unsafe { rb_cFloat }); }
401430
if self.is_subtype(types::HashExact) { return Some(unsafe { rb_cHash }); }

0 commit comments

Comments
 (0)