Treat all T_DATA the same in zjit

This commit is contained in:
John Hawthorn committed 2026-05-27 16:18:43 -07:00
1 parent 594ae9f073
commit c195319c0d
12 files changed
+43 -48
Notes: git 2026-05-27 23:19:23 +00:00

No files matched your search

+3 -3
View File
@@ -34,8 +34,8 @@ enum jit_bindgen_constants {
// Field offset for prime classext's fields_obj from a class pointer
RCLASS_OFFSET_PRIME_FIELDS_OBJ = offsetof(struct RClass_and_rb_classext_t, classext.fields_obj),
// Field offset for fields_obj in RTypedData
RTYPEDDATA_OFFSET_FIELDS_OBJ = offsetof(struct RTypedData, fields_obj),
// Field offset for fields_obj in T_DATA
TDATA_OFFSET_FIELDS_OBJ = offsetof(struct RTypedData, fields_obj),
// Field offsets for the RString struct
RUBY_OFFSET_RSTRING_LEN = offsetof(struct RString, len),
@@ -568,7 +568,7 @@ rb_jit_class_fields_embedded_p(VALUE klass)
}
bool
rb_jit_typed_data_fields_embedded_p(VALUE obj)
rb_jit_data_fields_embedded_p(VALUE obj)
{
VALUE fields_obj = RTYPEDDATA(obj)->fields_obj;
return !fields_obj || !FL_TEST_RAW(fields_obj, OBJ_FIELD_HEAP);
+1 -1
View File
@@ -986,7 +986,7 @@ pub type rb_seq_param_keyword_struct =
pub const ROBJECT_OFFSET_AS_HEAP_FIELDS: jit_bindgen_constants = 16;
pub const ROBJECT_OFFSET_AS_ARY: jit_bindgen_constants = 16;
pub const RCLASS_OFFSET_PRIME_FIELDS_OBJ: jit_bindgen_constants = 40;
pub const RTYPEDDATA_OFFSET_FIELDS_OBJ: jit_bindgen_constants = 16;
pub const TDATA_OFFSET_FIELDS_OBJ: jit_bindgen_constants = 16;
pub const RUBY_OFFSET_RSTRING_LEN: jit_bindgen_constants = 16;
pub const RB_SHAPE_FLAG_SHIFT: jit_bindgen_constants = 32;
pub const RUBY_OFFSET_EC_CFP: jit_bindgen_constants = 16;
+1 -2
View File
@@ -321,8 +321,7 @@ fn main() {
.allowlist_function("rb_jit_shape_complex_p")
.allowlist_function("rb_jit_multi_ractor_p")
.allowlist_function("rb_jit_class_fields_embedded_p")
.allowlist_function("rb_jit_typed_data_p")
.allowlist_function("rb_jit_typed_data_fields_embedded_p")
.allowlist_function("rb_jit_data_fields_embedded_p")
.allowlist_function("rb_jit_vm_lock_then_barrier")
.allowlist_function("rb_jit_vm_unlock")
.allowlist_function("rb_jit_for_each_iseq")
+4 -4
View File
@@ -2551,7 +2551,7 @@ fn gen_guard_type(jit: &mut JITState, asm: &mut Assembler, val: lir::Opnd, guard
asm.cmp(klass, Opnd::Value(expected_class));
asm.jne(jit, side_exit);
} else if guard_type.is_subtype(types::TypedTData) {
} else if guard_type.is_subtype(types::TData) {
let side = side_exit(jit, state, GuardType(guard_type));
// Check special constant
@@ -2562,11 +2562,11 @@ fn gen_guard_type(jit: &mut JITState, asm: &mut Assembler, val: lir::Opnd, guard
asm.cmp(val, Qfalse.into());
asm.je(jit, side.clone());
// Check the builtin type and RUBY_TYPED_FL_IS_TYPED_DATA with mask and compare
// Check the T_DATA builtin type.
let val = asm.load_mem(val);
let flags = asm.load(Opnd::mem(VALUE_BITS, val, RUBY_OFFSET_RBASIC_FLAGS));
let mask = RUBY_T_MASK.to_usize() | RUBY_TYPED_FL_IS_TYPED_DATA.to_usize();
let expected = RUBY_T_DATA.to_usize() | RUBY_TYPED_FL_IS_TYPED_DATA.to_usize();
let mask = RUBY_T_MASK.to_usize();
let expected = RUBY_T_DATA.to_usize();
let masked = asm.and(flags, mask.into());
asm.cmp(masked, expected.into());
asm.jne(jit, side);
+4 -6
View File
@@ -608,15 +608,13 @@ impl VALUE {
unsafe { rb_jit_class_fields_embedded_p(self) }
}
/// Typed `T_DATA` made from `TypedData_Make_Struct()` (e.g. Thread, ARGF)
pub fn typed_data_p(self) -> bool {
pub fn data_p(self) -> bool {
!self.special_const_p() &&
self.builtin_type() == RUBY_T_DATA &&
0 != (self.builtin_flags() & RUBY_TYPED_FL_IS_TYPED_DATA.to_usize())
self.builtin_type() == RUBY_T_DATA
}
pub fn typed_data_fields_embedded_p(self) -> bool {
unsafe { rb_jit_typed_data_fields_embedded_p(self) }
pub fn data_fields_embedded_p(self) -> bool {
unsafe { rb_jit_data_fields_embedded_p(self) }
}
pub fn as_fixnum(self) -> i64 {
+2 -2
View File
@@ -1929,7 +1929,7 @@ pub type zjit_struct_offsets = u32;
pub const ROBJECT_OFFSET_AS_HEAP_FIELDS: jit_bindgen_constants = 16;
pub const ROBJECT_OFFSET_AS_ARY: jit_bindgen_constants = 16;
pub const RCLASS_OFFSET_PRIME_FIELDS_OBJ: jit_bindgen_constants = 40;
pub const RTYPEDDATA_OFFSET_FIELDS_OBJ: jit_bindgen_constants = 16;
pub const TDATA_OFFSET_FIELDS_OBJ: jit_bindgen_constants = 16;
pub const RUBY_OFFSET_RSTRING_LEN: jit_bindgen_constants = 16;
pub const RB_SHAPE_FLAG_SHIFT: jit_bindgen_constants = 32;
pub const RUBY_OFFSET_EC_CFP: jit_bindgen_constants = 16;
@@ -2294,7 +2294,7 @@ unsafe extern "C" {
pub fn rb_jit_shape_complex_p(shape_id: shape_id_t) -> bool;
pub fn rb_jit_multi_ractor_p() -> bool;
pub fn rb_jit_class_fields_embedded_p(klass: VALUE) -> bool;
pub fn rb_jit_typed_data_fields_embedded_p(obj: VALUE) -> bool;
pub fn rb_jit_data_fields_embedded_p(obj: VALUE) -> bool;
pub fn rb_jit_vm_lock_then_barrier(
recursive_lock_level: *mut ::std::os::raw::c_uint,
file: *const ::std::os::raw::c_char,
+4 -5
View File
@@ -4336,8 +4336,8 @@ impl Function {
self.push_insn(block, Insn::GuardType { val: recv, guard_type: types::Class, state })
} else if recv_type.flags().is_t_module() {
self.push_insn(block, Insn::GuardType { val: recv, guard_type: types::Module, state })
} else if recv_type.flags().is_typed_data() {
self.push_insn(block, Insn::GuardType { val: recv, guard_type: types::TypedTData, state })
} else if recv_type.flags().is_t_data() {
self.push_insn(block, Insn::GuardType { val: recv, guard_type: types::TData, state })
} else {
// HeapBasicObject is wider than T_OBJECT, but shapes for T_OBJECTs are in a pool of
// its own and are guaranteed to be different from shapes of any other T_* types. So
@@ -4373,11 +4373,10 @@ impl Function {
});
return self.load_ivar_from_fields(block, fields_obj, recv_type.flags().is_fields_embedded(), id, ivar_index);
}
if recv_type.flags().is_typed_data() {
// Typed T_DATA: load from fields_obj at fixed offset in RTypedData
if recv_type.flags().is_t_data() {
let fields_obj = self.push_insn(block, Insn::LoadField {
recv: self_val, id: FieldName::fields_obj,
offset: RTYPEDDATA_OFFSET_FIELDS_OBJ as i32,
offset: TDATA_OFFSET_FIELDS_OBJ as i32,
return_type: types::RubyValue,
});
return self.load_ivar_from_fields(block, fields_obj, recv_type.flags().is_fields_embedded(), id, ivar_index);
+7 -7
View File
@@ -7912,8 +7912,8 @@ mod hir_opt_tests {
}
#[test]
fn test_optimize_getivar_on_typed_data() {
// Thread is typed T_DATA: uses LoadField chain via RTypedData fields_obj
fn test_optimize_getivar_on_t_data() {
// T_DATA uses fields_obj for instance variables.
eval("
class C < Thread
def test = @a
@@ -7936,7 +7936,7 @@ mod hir_opt_tests {
Jump bb3(v4)
bb3(v6:BasicObject):
PatchPoint SingleRactorMode
v17:TypedTData = GuardType v6, TypedTData
v17:TData = GuardType v6, TData
v18:CShape = LoadField v17, :shape_id@0x1000
v19:CShape[0x1001] = GuardBitEquals v18, CShape(0x1001) recompile
v20:RubyValue = LoadField v17, :fields_obj@0x1002
@@ -7947,8 +7947,8 @@ mod hir_opt_tests {
}
#[test]
fn test_optimize_getivar_on_typed_data_complex_fields() {
// Typed T_DATA with enough ivars to force heap field storage
fn test_optimize_getivar_on_t_data_complex_fields() {
// T_DATA with enough ivars to force heap field storage
eval("
class C < Thread
def test = @var1000
@@ -8260,7 +8260,7 @@ mod hir_opt_tests {
}
#[test]
fn test_getivar_polymorphic_t_class_and_typed_data() {
fn test_getivar_polymorphic_t_class_and_t_data() {
set_call_threshold(3);
eval(r#"
module Reader
@@ -8295,7 +8295,7 @@ mod hir_opt_tests {
PatchPoint SingleRactorMode
v11:HeapBasicObject = GuardType v6, HeapBasicObject
v12:CUInt64 = LoadField v11, :RBASIC_FLAGS@0x1000
v14:CUInt64[0xffffffff0000005f] = Const CUInt64(0xffffffff0000005f)
v14:CUInt64[0xffffffff0000001f] = Const CUInt64(0xffffffff0000001f)
v15:CPtr[CPtr(0x1001)] = Const CPtr(0x1001)
v16 = RefineType v15, CUInt64
v17:CInt64 = IntAnd v12, v14
+3 -4
View File
@@ -134,10 +134,9 @@ nil_exact = final_type "NilClass", c_name: "rb_cNilClass"
true_exact = final_type "TrueClass", c_name: "rb_cTrueClass"
false_exact = final_type "FalseClass", c_name: "rb_cFalseClass"
# Typed T_DATA objects (RTYPEDDATA_P). These have a distinct memory layout
# for field access (fields_obj at a fixed offset in RTypedData). These
# don't have a common class ancestor below BasicObject.
basic_object.subtype "TypedTData"
# T_DATA objects have a distinct memory layout for field access and don't have a
# common class ancestor below BasicObject.
basic_object.subtype "TData"
# Build the cvalue object universe. This is for C-level types that may be
# passed around when calling into the Ruby VM or after some strength reduction
+4 -4
View File
@@ -4,7 +4,7 @@ mod bits {
pub const Array: u64 = ArrayExact | ArraySubclass;
pub const ArrayExact: u64 = 1u64 << 0;
pub const ArraySubclass: u64 = 1u64 << 1;
pub const BasicObject: u64 = BasicObjectExact | BasicObjectSubclass | Object | TypedTData;
pub const BasicObject: u64 = BasicObjectExact | BasicObjectSubclass | Object | TData;
pub const BasicObjectExact: u64 = 1u64 << 2;
pub const BasicObjectSubclass: u64 = 1u64 << 3;
pub const Bignum: u64 = 1u64 << 4;
@@ -76,7 +76,7 @@ mod bits {
pub const Symbol: u64 = DynamicSymbol | StaticSymbol;
pub const TrueClass: u64 = 1u64 << 45;
pub const Truthy: u64 = BasicObject & !Falsy;
pub const TypedTData: u64 = 1u64 << 46;
pub const TData: u64 = 1u64 << 46;
pub const Undef: u64 = 1u64 << 47;
pub const AllBitPatterns: [(&str, u64); 78] = [
("Any", Any),
@@ -87,7 +87,7 @@ mod bits {
("NotNil", NotNil),
("Truthy", Truthy),
("HeapBasicObject", HeapBasicObject),
("TypedTData", TypedTData),
("TData", TData),
("Object", Object),
("BuiltinExact", BuiltinExact),
("BoolExact", BoolExact),
@@ -238,7 +238,7 @@ pub mod types {
pub const Symbol: Type = Type::from_bits(bits::Symbol);
pub const TrueClass: Type = Type::from_bits(bits::TrueClass);
pub const Truthy: Type = Type::from_bits(bits::Truthy);
pub const TypedTData: Type = Type::from_bits(bits::TypedTData);
pub const TData: Type = Type::from_bits(bits::TData);
pub const Undef: Type = Type::from_bits(bits::Undef);
pub const ExactBitsAndClass: [(u64, *const VALUE); 17] = [
(bits::ObjectExact, &raw const crate::cruby::rb_cObject),
+2 -2
View File
@@ -214,7 +214,7 @@ impl Type {
else if val.class_of() == unsafe { rb_cSymbol } { bits::DynamicSymbol }
else if let Some(bits) = Self::bits_from_exact_class(val.class_of()) { bits }
else if let Some(bits) = Self::bits_from_subclass(val.class_of()) { bits }
else if val.typed_data_p() { bits::TypedTData }
else if val.data_p() { bits::TData }
else {
unreachable!("Class {} is not a subclass of BasicObject! Don't know what to do.",
get_class_name(val.class_of()))
@@ -445,7 +445,7 @@ impl Type {
} else if self.bit_equal(types::Hash) {
Some(cruby::RUBY_T_HASH)
} else {
// Note that types::TypedTData is narrower than T_DATA, so not here.
// T_DATA uses a specialized guard, so not here.
None
}
}
+8 -8
View File
@@ -219,8 +219,8 @@ impl Flags {
const IS_T_CLASS: u32 = 1 << 6;
/// Object is a T_MODULE
const IS_T_MODULE: u32 = 1 << 7;
/// Object is a typed T_DATA (RTYPEDDATA_P)
const IS_TYPED_DATA: u32 = 1 << 8;
/// Object is a T_DATA
const IS_T_DATA: u32 = 1 << 8;
pub fn none() -> Self { Self(Self::NONE) }
@@ -233,7 +233,7 @@ impl Flags {
pub fn is_fields_embedded(self) -> bool { (self.0 & Self::IS_FIELDS_EMBEDDED) != 0 }
pub fn is_t_class(self) -> bool { (self.0 & Self::IS_T_CLASS) != 0 }
pub fn is_t_module(self) -> bool { (self.0 & Self::IS_T_MODULE) != 0 }
pub fn is_typed_data(self) -> bool { (self.0 & Self::IS_TYPED_DATA) != 0 }
pub fn is_t_data(self) -> bool { (self.0 & Self::IS_T_DATA) != 0 }
}
/// opt_send_without_block/opt_plus/... should store:
@@ -322,9 +322,9 @@ impl ProfiledType {
flags.0 |= Flags::IS_FIELDS_EMBEDDED;
}
}
if obj.typed_data_p() {
flags.0 |= Flags::IS_TYPED_DATA;
if obj.typed_data_fields_embedded_p() {
if obj.data_p() {
flags.0 |= Flags::IS_T_DATA;
if obj.data_fields_embedded_p() {
flags.0 |= Flags::IS_FIELDS_EMBEDDED;
}
}
@@ -365,8 +365,8 @@ impl ProfiledType {
(RUBY_T_CLASS, RUBY_T_MASK)
} else if self.flags().is_t_module() {
(RUBY_T_MODULE, RUBY_T_MASK)
} else if self.flags().is_typed_data() {
(RUBY_T_DATA | RUBY_TYPED_FL_IS_TYPED_DATA, RUBY_T_MASK | RUBY_TYPED_FL_IS_TYPED_DATA)
} else if self.flags().is_t_data() {
(RUBY_T_DATA, RUBY_T_MASK)
} else {
(0, 0)
};