1use std::marker::PhantomData;
2use std::sync::atomic::{AtomicU32, AtomicUsize};
3
4use crate::util::linear_scan::Region;
5use crate::util::{metadata::side_metadata::address_to_meta_address, Address};
6use crate::{
7 policy::immix::{block::Block, line::Line},
8 util::{metadata::side_metadata::SideMetadataSpec, ObjectReference},
9 vm::*,
10};
11use atomic::Ordering;
12
13pub const LOG_REF_COUNT_BITS: usize = 1;
15pub const REF_COUNT_BITS: u8 = 1 << LOG_REF_COUNT_BITS;
17pub const REF_COUNT_MASK: u8 = (((1u16 << REF_COUNT_BITS) - 1) & 0xff) as u8;
19pub const MAX_REF_COUNT: u8 = REF_COUNT_MASK;
22
23pub const LOG_MIN_OBJECT_SIZE: usize = crate::util::constants::LOG_MIN_OBJECT_SIZE as _;
25pub const MIN_OBJECT_SIZE: usize = 1 << LOG_MIN_OBJECT_SIZE;
27
28pub const RC_STRADDLE_LINES: SideMetadataSpec =
31 crate::util::metadata::side_metadata::spec_defs::RC_STRADDLE_LINES;
32
33pub const RC_TABLE: SideMetadataSpec = crate::util::metadata::side_metadata::spec_defs::RC_TABLE;
35
36static INC_BUFFER_SIZE: AtomicUsize = AtomicUsize::new(0);
37
38static TOTAL_INCS_PACKETS: AtomicU32 = AtomicU32::new(0);
39
40static TOTAL_INCS: AtomicU32 = AtomicU32::new(0);
41static ROOT_INCS: AtomicU32 = AtomicU32::new(0);
42static MATURE_INCS: AtomicU32 = AtomicU32::new(0);
43static NURSERY_INCS: AtomicU32 = AtomicU32::new(0);
44static FAST_NURSERY_INCS: AtomicU32 = AtomicU32::new(0);
45static LOS_INCS: AtomicU32 = AtomicU32::new(0);
46
47static PROMOTED_OBJECTS: AtomicU32 = AtomicU32::new(0);
48static PROMOTED_SCALARS: [AtomicU32; 3] = [AtomicU32::new(0), AtomicU32::new(0), AtomicU32::new(0)];
49static PROMOTED_PRIM_ARRAYS: [AtomicU32; 3] =
50 [AtomicU32::new(0), AtomicU32::new(0), AtomicU32::new(0)];
51static PROMOTED_OBJECT_ARRAYS: [AtomicU32; 3] =
52 [AtomicU32::new(0), AtomicU32::new(0), AtomicU32::new(0)];
53
54#[repr(transparent)]
57#[derive(Debug, Copy)]
58pub struct RefCountHelper<VM: VMBinding>(PhantomData<VM>);
59
60impl<VM: VMBinding> RefCountHelper<VM> {
61 pub const NEW: Self = Self(PhantomData);
63 pub const SANITY: bool = cfg!(debug_assertions) || cfg!(feature = "sanity");
65
66 pub fn inc_buffer_size(&self) -> usize {
69 INC_BUFFER_SIZE.load(Ordering::Relaxed)
70 }
71
72 pub fn increase_inc_buffer_size(&self, delta: usize) {
74 #[allow(deprecated)]
77 let _ = INC_BUFFER_SIZE.fetch_update(Ordering::Relaxed, Ordering::Relaxed, |x| {
78 Some(x.saturating_add(delta))
79 });
80 }
81
82 pub fn reset_inc_buffer_size(&self) {
84 INC_BUFFER_SIZE.store(0, Ordering::Relaxed)
85 }
86
87 pub fn fetch_update(
90 &self,
91 o: ObjectReference,
92 f: impl FnMut(u8) -> Option<u8>,
93 ) -> Result<u8, u8> {
94 RC_TABLE.fetch_update_atomic(o.to_raw_address(), Ordering::Relaxed, Ordering::Relaxed, f)
95 }
96
97 pub fn is_stuck(&self, o: ObjectReference) -> bool {
99 self.count(o) == MAX_REF_COUNT
100 }
101
102 pub fn stick(&self, o: ObjectReference) -> Result<u8, u8> {
105 self.fetch_update(o, |x| {
106 debug_assert!(x <= MAX_REF_COUNT);
107 if x == MAX_REF_COUNT {
108 None
109 } else {
110 Some(MAX_REF_COUNT)
111 }
112 })
113 }
114
115 pub fn inc(&self, o: ObjectReference) -> Result<u8, u8> {
118 #[cfg(feature = "vo_bit")]
119 debug_assert!(
120 crate::util::metadata::vo_bit::is_vo_bit_set(o),
121 "{o}: VO bit not set",
122 );
123
124 self.fetch_update(o, |x| {
125 debug_assert!(x <= MAX_REF_COUNT);
126 if x == MAX_REF_COUNT {
127 None
128 } else {
129 Some(x + 1)
130 }
131 })
132 }
133
134 pub fn dec(&self, o: ObjectReference) -> Result<u8, u8> {
137 #[cfg(feature = "vo_bit")]
138 debug_assert!(
139 crate::util::metadata::vo_bit::is_vo_bit_set(o),
140 "{o}: VO bit not set",
141 );
142
143 self.fetch_update(o, |x| {
144 debug_assert!(x <= MAX_REF_COUNT);
145 if x == 0 || x == MAX_REF_COUNT
146 {
148 None
149 } else {
150 Some(x - 1)
151 }
152 })
153 }
154
155 pub fn set(&self, o: ObjectReference, count: u8) {
157 RC_TABLE.store_atomic(o.to_raw_address(), count, Ordering::Relaxed)
158 }
159
160 pub fn set_relaxed(&self, o: ObjectReference, count: u8) {
163 unsafe { RC_TABLE.store(o.to_raw_address(), count) }
164 }
165
166 pub fn set_line_relaxed(&self, line: Line, count: u8) {
169 unsafe { RC_TABLE.store(line.start(), count) }
170 }
171
172 pub fn count(&self, o: ObjectReference) -> u8 {
174 RC_TABLE.load_atomic(o.to_raw_address(), Ordering::Relaxed)
175 }
176
177 pub fn count_by_address(&self, addr: Address) -> u8 {
181 RC_TABLE.load_atomic(addr, Ordering::Relaxed)
182 }
183
184 pub fn object_or_line_is_dead(&self, o: ObjectReference) -> bool {
187 RC_TABLE.load_byte(o.to_raw_address()) == 0
188 }
189
190 pub fn rc_table_range<UInt: Sized>(&self, b: Block) -> &'static [UInt] {
193 debug_assert!({
194 let log_bits_in_uint: usize =
195 (std::mem::size_of::<UInt>() << 3).trailing_zeros() as usize;
196 Block::LOG_BYTES - super::rc::LOG_MIN_OBJECT_SIZE + super::rc::LOG_REF_COUNT_BITS
197 >= log_bits_in_uint
198 });
199 let start = address_to_meta_address(&super::rc::RC_TABLE, b.start()).to_ptr::<UInt>();
200 let limit = address_to_meta_address(&super::rc::RC_TABLE, b.end()).to_ptr::<UInt>();
201 let rc_table = unsafe { std::slice::from_raw_parts(start, limit.offset_from(start) as _) };
202 rc_table
203 }
204
205 #[allow(unused)]
207 pub fn is_dead(&self, o: ObjectReference) -> bool {
208 let v: u8 = RC_TABLE.load_atomic(o.to_raw_address(), Ordering::Relaxed);
209 v == 0
210 }
211
212 pub fn is_dead_or_stuck(&self, o: ObjectReference) -> bool {
215 let v: u8 = RC_TABLE.load_atomic(o.to_raw_address(), Ordering::Relaxed);
216 v == 0 || v == MAX_REF_COUNT
217 }
218
219 pub fn object_is_in_straddle_line_no_rc_check(&self, o: ObjectReference) -> bool {
221 unsafe { RC_STRADDLE_LINES.load::<u8>(o.to_raw_address()) != 0 }
224 }
225
226 pub fn object_is_in_straddle_line(&self, o: ObjectReference) -> bool {
229 let line = Line::from_unaligned_address(o.to_raw_address());
230 self.count(o) != 0 && unsafe { RC_STRADDLE_LINES.load::<u8>(line.start()) != 0 }
231 }
232
233 fn mark_straddle_object_with_size(&self, o: ObjectReference, size: usize) {
234 debug_assert!(size > Line::BYTES);
235 let start = o.to_object_start::<VM>();
236 let end = start + size;
237 let start_line = Line::from_unaligned_address(o.to_raw_address()).next();
243 let end_line = Line::from_unaligned_address(end);
244 let mut line = start_line;
249 while line < end_line {
250 unsafe { RC_STRADDLE_LINES.store(line.start(), 1u8) };
251 self.set_line_relaxed(line, 1);
252 line = line.next();
253 }
254 }
255
256 pub fn mark_straddle_object(&self, o: ObjectReference) {
259 let size = VM::VMObjectModel::get_current_size(o);
260 self.mark_straddle_object_with_size(o, size)
261 }
262
263 pub fn unmark_straddle_object(&self, o: ObjectReference) {
266 let size = VM::VMObjectModel::get_current_size(o);
268 if size > Line::BYTES {
269 let start = o.to_object_start::<VM>();
270 let end = start + size;
271 let start_line = Line::from_unaligned_address(o.to_raw_address()).next();
273 let end_line = Line::from_unaligned_address(end);
274 let mut line = start_line;
279 while line < end_line {
280 self.set_line_relaxed(line, 0);
281 unsafe { RC_STRADDLE_LINES.store(line.start(), 0u8) };
282 line = line.next();
283 }
284 }
285 }
286
287 pub fn assert_zero_ref_count(&self, o: ObjectReference) {
290 let size = VM::VMObjectModel::get_current_size(o);
291 for i in (0..size).step_by(MIN_OBJECT_SIZE) {
292 let a = o.to_raw_address() + i;
293 assert_eq!(0, self.count_by_address(a));
294 }
295 }
296
297 pub fn promote(&self, o: ObjectReference) {
300 let size = o.get_size::<VM>();
301 if size > Line::BYTES {
302 self.mark_straddle_object_with_size(o, size);
303 }
304 }
305
306 pub fn promote_with_size(&self, o: ObjectReference, size: usize) {
309 if size > Line::BYTES {
310 self.mark_straddle_object_with_size(o, size);
311 }
312 }
313}
314
315impl<VM: VMBinding> Clone for RefCountHelper<VM> {
316 fn clone(&self) -> Self {
317 Self(PhantomData)
318 }
319}