ZJIT: Create HeapObject Type (#14140)

This is a counterpoint to the Immediate type and it represents all BasicObject subclasses except for the several immediate objects.

If we know something is a HeapObject, we know we can treat it as an RBasic pointer.
This commit is contained in:
Max Bernstein 2025-08-07 12:11:55 -07:00 committed by GitHub
parent 1aabd2cb36
commit 363ad0ad17
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 44 additions and 12 deletions

View file

@ -5566,7 +5566,7 @@ mod opt_tests {
fn test@<compiled>:5:
bb0(v0:BasicObject):
PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010)
v6:BasicObject[class_exact*:Object@VALUE(0x1000)] = GuardType v0, BasicObject[class_exact*:Object@VALUE(0x1000)]
v6:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v0, HeapObject[class_exact*:Object@VALUE(0x1000)]
v7:BasicObject = SendWithoutBlockDirect v6, :foo (0x1038)
Return v7
"#]]);
@ -5606,7 +5606,7 @@ mod opt_tests {
fn test@<compiled>:6:
bb0(v0:BasicObject):
PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010)
v6:BasicObject[class_exact*:Object@VALUE(0x1000)] = GuardType v0, BasicObject[class_exact*:Object@VALUE(0x1000)]
v6:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v0, HeapObject[class_exact*:Object@VALUE(0x1000)]
v7:BasicObject = SendWithoutBlockDirect v6, :foo (0x1038)
Return v7
"#]]);
@ -5625,7 +5625,7 @@ mod opt_tests {
bb0(v0:BasicObject):
v2:Fixnum[3] = Const Value(3)
PatchPoint MethodRedefined(Object@0x1000, Integer@0x1008, cme:0x1010)
v7:BasicObject[class_exact*:Object@VALUE(0x1000)] = GuardType v0, BasicObject[class_exact*:Object@VALUE(0x1000)]
v7:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v0, HeapObject[class_exact*:Object@VALUE(0x1000)]
v8:BasicObject = SendWithoutBlockDirect v7, :Integer (0x1038), v2
Return v8
"#]]);
@ -5647,7 +5647,7 @@ mod opt_tests {
v2:Fixnum[1] = Const Value(1)
v3:Fixnum[2] = Const Value(2)
PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010)
v8:BasicObject[class_exact*:Object@VALUE(0x1000)] = GuardType v0, BasicObject[class_exact*:Object@VALUE(0x1000)]
v8:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v0, HeapObject[class_exact*:Object@VALUE(0x1000)]
v9:BasicObject = SendWithoutBlockDirect v8, :foo (0x1038), v2, v3
Return v9
"#]]);
@ -5670,10 +5670,10 @@ mod opt_tests {
fn test@<compiled>:7:
bb0(v0:BasicObject):
PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010)
v8:BasicObject[class_exact*:Object@VALUE(0x1000)] = GuardType v0, BasicObject[class_exact*:Object@VALUE(0x1000)]
v8:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v0, HeapObject[class_exact*:Object@VALUE(0x1000)]
v9:BasicObject = SendWithoutBlockDirect v8, :foo (0x1038)
PatchPoint MethodRedefined(Object@0x1000, bar@0x1040, cme:0x1048)
v11:BasicObject[class_exact*:Object@VALUE(0x1000)] = GuardType v0, BasicObject[class_exact*:Object@VALUE(0x1000)]
v11:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v0, HeapObject[class_exact*:Object@VALUE(0x1000)]
v12:BasicObject = SendWithoutBlockDirect v11, :bar (0x1038)
Return v12
"#]]);
@ -6475,7 +6475,7 @@ mod opt_tests {
fn test@<compiled>:8:
bb0(v0:BasicObject, v1:BasicObject):
PatchPoint MethodRedefined(C@0x1000, foo@0x1008, cme:0x1010)
v7:BasicObject[class_exact:C] = GuardType v1, BasicObject[class_exact:C]
v7:HeapObject[class_exact:C] = GuardType v1, HeapObject[class_exact:C]
v8:BasicObject = SendWithoutBlockDirect v7, :foo (0x1038)
Return v8
"#]]);
@ -7428,7 +7428,7 @@ mod opt_tests {
fn test@<compiled>:3:
bb0(v0:BasicObject):
PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010)
v6:BasicObject[class_exact*:Object@VALUE(0x1000)] = GuardType v0, BasicObject[class_exact*:Object@VALUE(0x1000)]
v6:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v0, HeapObject[class_exact*:Object@VALUE(0x1000)]
v7:BasicObject = SendWithoutBlockDirect v6, :foo (0x1038)
Return v7
"#]]);
@ -7497,7 +7497,7 @@ mod opt_tests {
fn test@<compiled>:6:
bb0(v0:BasicObject, v1:BasicObject):
PatchPoint MethodRedefined(C@0x1000, foo@0x1008, cme:0x1010)
v7:BasicObject[class_exact:C] = GuardType v1, BasicObject[class_exact:C]
v7:HeapObject[class_exact:C] = GuardType v1, HeapObject[class_exact:C]
v8:BasicObject = GetIvar v7, :@foo
Return v8
"#]]);
@ -7518,7 +7518,7 @@ mod opt_tests {
fn test@<compiled>:6:
bb0(v0:BasicObject, v1:BasicObject):
PatchPoint MethodRedefined(C@0x1000, foo@0x1008, cme:0x1010)
v7:BasicObject[class_exact:C] = GuardType v1, BasicObject[class_exact:C]
v7:HeapObject[class_exact:C] = GuardType v1, HeapObject[class_exact:C]
v8:BasicObject = GetIvar v7, :@foo
Return v8
"#]]);

View file

@ -156,6 +156,8 @@ add_union "BuiltinExact", $builtin_exact
add_union "Subclass", $subclass
add_union "BoolExact", [true_exact.name, false_exact.name]
add_union "Immediate", [fixnum.name, flonum.name, static_sym.name, nil_exact.name, true_exact.name, false_exact.name, undef_.name]
$bits["HeapObject"] = ["BasicObject & !Immediate"]
$numeric_bits["HeapObject"] = $numeric_bits["BasicObject"] & ~$numeric_bits["Immediate"]
# ===== Finished generating the DAG; write Rust code =====

View file

@ -38,6 +38,7 @@ mod bits {
pub const HashExact: u64 = 1u64 << 23;
pub const HashSubclass: u64 = 1u64 << 24;
pub const HeapFloat: u64 = 1u64 << 25;
pub const HeapObject: u64 = BasicObject & !Immediate;
pub const Immediate: u64 = FalseClass | Fixnum | Flonum | NilClass | StaticSymbol | TrueClass | Undef;
pub const Integer: u64 = Bignum | Fixnum;
pub const Module: u64 = Class | ModuleExact | ModuleSubclass;
@ -65,7 +66,7 @@ mod bits {
pub const Symbol: u64 = DynamicSymbol | StaticSymbol;
pub const TrueClass: u64 = 1u64 << 40;
pub const Undef: u64 = 1u64 << 41;
pub const AllBitPatterns: [(&'static str, u64); 65] = [
pub const AllBitPatterns: [(&'static str, u64); 66] = [
("Any", Any),
("RubyValue", RubyValue),
("Immediate", Immediate),
@ -75,6 +76,7 @@ mod bits {
("BuiltinExact", BuiltinExact),
("BoolExact", BoolExact),
("TrueClass", TrueClass),
("HeapObject", HeapObject),
("String", String),
("Subclass", Subclass),
("StringSubclass", StringSubclass),
@ -174,6 +176,7 @@ pub mod types {
pub const HashExact: Type = Type::from_bits(bits::HashExact);
pub const HashSubclass: Type = Type::from_bits(bits::HashSubclass);
pub const HeapFloat: Type = Type::from_bits(bits::HeapFloat);
pub const HeapObject: Type = Type::from_bits(bits::HeapObject);
pub const Immediate: Type = Type::from_bits(bits::Immediate);
pub const Integer: Type = Type::from_bits(bits::Integer);
pub const Module: Type = Type::from_bits(bits::Module);

View file

@ -248,7 +248,7 @@ impl Type {
else if val.class() == unsafe { rb_cString } { types::StringExact }
else {
// TODO(max): Add more cases for inferring type bits from built-in types
Type { bits: bits::BasicObject, spec: Specialization::TypeExact(val.class()) }
Type { bits: bits::HeapObject, spec: Specialization::TypeExact(val.class()) }
}
}
@ -583,6 +583,7 @@ mod tests {
assert_subtype(Type::fixnum(123), types::Immediate);
assert_subtype(types::Fixnum, types::Immediate);
assert_not_subtype(types::Bignum, types::Immediate);
assert_not_subtype(types::Integer, types::Immediate);
assert_subtype(types::NilClass, types::Immediate);
assert_subtype(types::TrueClass, types::Immediate);
assert_subtype(types::FalseClass, types::Immediate);
@ -592,6 +593,32 @@ mod tests {
assert_not_subtype(types::HeapFloat, types::Immediate);
}
#[test]
fn heap_object() {
assert_not_subtype(Type::fixnum(123), types::HeapObject);
assert_not_subtype(types::Fixnum, types::HeapObject);
assert_subtype(types::Bignum, types::HeapObject);
assert_not_subtype(types::Integer, types::HeapObject);
assert_not_subtype(types::NilClass, types::HeapObject);
assert_not_subtype(types::TrueClass, types::HeapObject);
assert_not_subtype(types::FalseClass, types::HeapObject);
assert_not_subtype(types::StaticSymbol, types::HeapObject);
assert_subtype(types::DynamicSymbol, types::HeapObject);
assert_not_subtype(types::Flonum, types::HeapObject);
assert_subtype(types::HeapFloat, types::HeapObject);
assert_not_subtype(types::BasicObject, types::HeapObject);
assert_not_subtype(types::Object, types::HeapObject);
assert_not_subtype(types::Immediate, types::HeapObject);
assert_not_subtype(types::HeapObject, types::Immediate);
crate::cruby::with_rubyvm(|| {
let left = Type::from_value(rust_str_to_ruby("hello"));
let right = Type::from_value(rust_str_to_ruby("world"));
assert_subtype(left, types::HeapObject);
assert_subtype(right, types::HeapObject);
assert_subtype(left.union(right), types::HeapObject);
});
}
#[test]
fn fixnum_has_ruby_object() {
assert_eq!(Type::fixnum(3).ruby_object(), Some(VALUE::fixnum_from_usize(3)));