summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMax Bernstein <rubybugs@bernsteinbear.com>2025-08-07 12:11:55 -0700
committerGitHub <noreply@github.com>2025-08-07 15:11:55 -0400
commit363ad0ad17c8ce36035c64ee3267ec680423bc68 (patch)
tree68e5917ea647567053895b3904be76e6fa1decaa
parent1aabd2cb365ff0a236609fe1c4ab193d16027757 (diff)
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.
-rw-r--r--zjit/src/hir.rs20
-rw-r--r--zjit/src/hir_type/gen_hir_type.rb2
-rw-r--r--zjit/src/hir_type/hir_type.inc.rs5
-rw-r--r--zjit/src/hir_type/mod.rs29
4 files changed, 44 insertions, 12 deletions
diff --git a/zjit/src/hir.rs b/zjit/src/hir.rs
index f4dae3f0ec..f98aa846cd 100644
--- a/zjit/src/hir.rs
+++ b/zjit/src/hir.rs
@@ -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
"#]]);
diff --git a/zjit/src/hir_type/gen_hir_type.rb b/zjit/src/hir_type/gen_hir_type.rb
index 6857678982..15aa68a600 100644
--- a/zjit/src/hir_type/gen_hir_type.rb
+++ b/zjit/src/hir_type/gen_hir_type.rb
@@ -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 =====
diff --git a/zjit/src/hir_type/hir_type.inc.rs b/zjit/src/hir_type/hir_type.inc.rs
index 68039c7f53..5850874080 100644
--- a/zjit/src/hir_type/hir_type.inc.rs
+++ b/zjit/src/hir_type/hir_type.inc.rs
@@ -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);
diff --git a/zjit/src/hir_type/mod.rs b/zjit/src/hir_type/mod.rs
index 84679c419d..c5e7aa87c6 100644
--- a/zjit/src/hir_type/mod.rs
+++ b/zjit/src/hir_type/mod.rs
@@ -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);
@@ -593,6 +594,32 @@ mod tests {
}
#[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)));
assert_eq!(types::Fixnum.ruby_object(), None);