mirror of
https://github.com/ruby/ruby.git
synced 2026-10-28 16:36:20 +08:00
Treat all T_DATA the same in zjit
This commit is contained in:
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
@@ -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);
|
||||
|
||||
Generated
+1
-1
@@ -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;
|
||||
|
||||
@@ -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
@@ -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
@@ -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 {
|
||||
|
||||
Generated
+2
-2
@@ -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
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Generated
+4
-4
@@ -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),
|
||||
|
||||
@@ -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
@@ -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)
|
||||
};
|
||||
|
||||
Reference in new issue
Block a user