|
7 | 7 | #![allow(clippy::match_like_matches_macro)] |
8 | 8 | use crate::{ |
9 | 9 | backend::lir::C_ARG_OPNDS, |
10 | | - cast::IntoUsize, codegen::local_idx_to_ep_offset, cruby::*, invariants::has_singleton_class_of, payload::{get_or_create_iseq_payload, IseqPayload}, options::{debug, get_option, DumpHIR}, state::ZJITState, json::Json |
| 10 | + cast::IntoUsize, codegen::local_idx_to_ep_offset, cruby::*, invariants::{self, has_singleton_class_of}, payload::{get_or_create_iseq_payload, IseqPayload}, options::{debug, get_option, DumpHIR}, state::ZJITState, json::Json |
11 | 11 | }; |
12 | 12 | use std::{ |
13 | 13 | cell::RefCell, collections::{BTreeSet, HashMap, HashSet, VecDeque}, ffi::{c_void, c_uint, c_int, CStr}, fmt::Display, mem::{align_of, size_of}, ptr, slice::Iter |
@@ -151,6 +151,9 @@ pub enum Invariant { |
151 | 151 | NoSingletonClass { |
152 | 152 | klass: VALUE, |
153 | 153 | }, |
| 154 | + /// Only the root box is active, so we can safely read from the prime classext. |
| 155 | + /// Invalidated if a non-root box duplicates any classext. |
| 156 | + RootBoxOnly, |
154 | 157 | } |
155 | 158 |
|
156 | 159 | impl Invariant { |
@@ -290,6 +293,7 @@ impl<'a> std::fmt::Display for InvariantPrinter<'a> { |
290 | 293 | class_name, |
291 | 294 | self.ptr_map.map_ptr(klass.as_ptr::<VALUE>())) |
292 | 295 | } |
| 296 | + Invariant::RootBoxOnly => write!(f, "RootBoxOnly"), |
293 | 297 | } |
294 | 298 | } |
295 | 299 | } |
@@ -2182,6 +2186,17 @@ impl Function { |
2182 | 2186 | } |
2183 | 2187 | } |
2184 | 2188 |
|
| 2189 | + /// Assume that only the root box is active, so we can safely read from the prime classext. |
| 2190 | + /// Returns true if safe to assume so and emits a PatchPoint. |
| 2191 | + pub fn assume_root_box(&mut self, block: BlockId, state: InsnId) -> bool { |
| 2192 | + if invariants::non_root_box_created() { |
| 2193 | + false |
| 2194 | + } else { |
| 2195 | + self.push_insn(block, Insn::PatchPoint { invariant: Invariant::RootBoxOnly, state }); |
| 2196 | + true |
| 2197 | + } |
| 2198 | + } |
| 2199 | + |
2185 | 2200 | /// Assume that objects of a given class will have no singleton class. |
2186 | 2201 | /// Returns true if safe to assume so and emits a PatchPoint. |
2187 | 2202 | /// Returns false if we've already seen a singleton class for this class, |
@@ -3899,10 +3914,52 @@ impl Function { |
3899 | 3914 | // entered the compiler. That means we can just return nil for this |
3900 | 3915 | // shape + iv name |
3901 | 3916 | self.push_insn(block, Insn::Const { val: Const::Value(Qnil) }) |
| 3917 | + } else if recv_type.flags().is_t_class_or_module() { |
| 3918 | + // Class/module ivar: load from prime classext's fields_obj |
| 3919 | + if self.assume_root_box(block, state) { |
| 3920 | + // Root box only: load directly from prime classext |
| 3921 | + let fields_obj = self.push_insn(block, Insn::LoadField { |
| 3922 | + recv: self_val, id: ID!(_fields_obj), |
| 3923 | + offset: RCLASS_OFFSET_PRIME_FIELDS_OBJ as i32, |
| 3924 | + return_type: types::RubyValue, |
| 3925 | + }); |
| 3926 | + if recv_type.flags().is_fields_embedded() { |
| 3927 | + let offset = ROBJECT_OFFSET_AS_ARY as i32 |
| 3928 | + + (SIZEOF_VALUE * ivar_index.to_usize()) as i32; |
| 3929 | + self.push_insn(block, Insn::LoadField { |
| 3930 | + recv: fields_obj, id, offset, |
| 3931 | + return_type: types::BasicObject, |
| 3932 | + }) |
| 3933 | + } else { |
| 3934 | + let ptr = self.push_insn(block, Insn::LoadField { |
| 3935 | + recv: fields_obj, id: ID!(_as_heap), |
| 3936 | + offset: ROBJECT_OFFSET_AS_HEAP_FIELDS as i32, |
| 3937 | + return_type: types::CPtr, |
| 3938 | + }); |
| 3939 | + let offset = SIZEOF_VALUE_I32 * ivar_index as i32; |
| 3940 | + self.push_insn(block, Insn::LoadField { |
| 3941 | + recv: ptr, id, offset, |
| 3942 | + return_type: types::BasicObject, |
| 3943 | + }) |
| 3944 | + } |
| 3945 | + } else { |
| 3946 | + // Non-root box active: fall back to C call |
| 3947 | + // NOTE: it's fine to use rb_ivar_get_at_no_ractor_check because |
| 3948 | + // getinstancevariable does assume_single_ractor_mode() |
| 3949 | + let ivar_index_insn = self.push_insn(block, Insn::Const { val: Const::CUInt16(ivar_index as u16) }); |
| 3950 | + self.push_insn(block, Insn::CCall { |
| 3951 | + cfunc: rb_ivar_get_at_no_ractor_check as *const u8, |
| 3952 | + recv: self_val, |
| 3953 | + args: vec![ivar_index_insn], |
| 3954 | + name: ID!(rb_ivar_get_at_no_ractor_check), |
| 3955 | + return_type: types::BasicObject, |
| 3956 | + elidable: true }) |
| 3957 | + } |
3902 | 3958 | } else if !recv_type.flags().is_t_object() { |
| 3959 | + // Non-T_OBJECT, non-class/module (e.g. T_DATA): fall back to C call |
3903 | 3960 | // NOTE: it's fine to use rb_ivar_get_at_no_ractor_check because |
3904 | 3961 | // getinstancevariable does assume_single_ractor_mode() |
3905 | | - let ivar_index_insn: InsnId = self.push_insn(block, Insn::Const { val: Const::CUInt16(ivar_index as u16) }); |
| 3962 | + let ivar_index_insn = self.push_insn(block, Insn::Const { val: Const::CUInt16(ivar_index as u16) }); |
3906 | 3963 | self.push_insn(block, Insn::CCall { |
3907 | 3964 | cfunc: rb_ivar_get_at_no_ractor_check as *const u8, |
3908 | 3965 | recv: self_val, |
|
0 commit comments