1use crate::object::shape::{Shape, ShapeId};
2use std::ptr::NonNull;
3
4#[derive(Clone, Copy)]
5struct InlineCacheRead {
6 shape_id: u32,
7 offset: u32,
8 proto_ptr: usize,
9}
10
11impl InlineCacheRead {
12 #[inline(always)]
13 const fn empty() -> Self {
14 Self {
15 shape_id: u32::MAX,
16 offset: 0,
17 proto_ptr: 0,
18 }
19 }
20}
21
22#[derive(Clone, Copy)]
23struct InlineCacheWrite {
24 shape_id: u32,
25 offset: u32,
26 new_shape: usize,
27 no_accessor: u8,
28}
29
30impl InlineCacheWrite {
31 #[inline(always)]
32 const fn empty() -> Self {
33 Self {
34 shape_id: u32::MAX,
35 offset: 0,
36 new_shape: 0,
37 no_accessor: 0,
38 }
39 }
40}
41
42unsafe impl Send for InlineCacheWrite {}
43unsafe impl Sync for InlineCacheWrite {}
44
45const IC_POLY: usize = 2;
46
47#[derive(Clone, Debug)]
48pub struct InlineCache {
49 reads: [InlineCacheRead; IC_POLY],
50
51 write: InlineCacheWrite,
52}
53
54impl std::fmt::Debug for InlineCacheRead {
55 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
56 write!(
57 f,
58 "ICRead(shape={}, off={}, proto={})",
59 self.shape_id as usize, self.offset, self.proto_ptr
60 )
61 }
62}
63
64impl std::fmt::Debug for InlineCacheWrite {
65 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
66 write!(
67 f,
68 "ICWrite(shape={}, off={}, new_shape={})",
69 self.shape_id as usize, self.offset, self.new_shape
70 )
71 }
72}
73
74impl InlineCache {
75 pub fn new() -> Self {
76 InlineCache {
77 reads: [InlineCacheRead::empty(); IC_POLY],
78 write: InlineCacheWrite::empty(),
79 }
80 }
81
82 #[inline(always)]
83 pub fn get(&self, shape_id: ShapeId) -> Option<(u32, Option<usize>)> {
84 let sid = shape_id.0 as u32;
85 let r0 = &self.reads[0];
86 if r0.shape_id == sid {
87 return Some((
88 r0.offset,
89 if r0.proto_ptr == 0 {
90 None
91 } else {
92 Some(r0.proto_ptr)
93 },
94 ));
95 }
96 let r1 = &self.reads[1];
97 if r1.shape_id == sid {
98 return Some((
99 r1.offset,
100 if r1.proto_ptr == 0 {
101 None
102 } else {
103 Some(r1.proto_ptr)
104 },
105 ));
106 }
107 None
108 }
109
110 #[inline(always)]
111 pub fn get_transition(&self, shape_id: ShapeId) -> Option<(u32, *const Shape)> {
112 let w = &self.write;
113 if w.shape_id == shape_id.0 as u32 {
114 Some((w.offset, w.new_shape as *const Shape))
115 } else {
116 None
117 }
118 }
119
120 pub fn insert(&mut self, shape_id: ShapeId, offset: u32, proto_ptr: Option<usize>) {
121 self.reads[1] = self.reads[0];
122 self.reads[0] = InlineCacheRead {
123 shape_id: shape_id.0 as u32,
124 offset,
125 proto_ptr: proto_ptr.unwrap_or(0),
126 };
127 }
128
129 pub fn insert_transition_null(&mut self, shape_id: ShapeId, offset: u32) {
130 self.write = InlineCacheWrite {
131 shape_id: shape_id.0 as u32,
132 offset,
133 new_shape: 0,
134 no_accessor: 0,
135 };
136 }
137
138 pub fn insert_transition(
139 &mut self,
140 pre_shape_id: ShapeId,
141 offset: u32,
142 new_shape: NonNull<Shape>,
143 ) {
144 self.write = InlineCacheWrite {
145 shape_id: pre_shape_id.0 as u32,
146 offset,
147 new_shape: new_shape.as_ptr() as usize,
148 no_accessor: 0,
149 };
150 }
151}
152
153impl Default for InlineCache {
154 fn default() -> Self {
155 Self::new()
156 }
157}
158
159#[derive(Clone, Debug)]
160pub struct InlineCacheTable {
161 caches: Vec<InlineCache>,
162}
163
164impl InlineCacheTable {
165 pub fn new() -> Self {
166 InlineCacheTable { caches: Vec::new() }
167 }
168
169 #[inline(always)]
170 pub fn ensure_capacity(&mut self, len: usize) {
171 if self.caches.len() < len {
172 let new_len = (self.caches.len() * 2).max(len);
173 self.caches.resize(new_len, InlineCache::new());
174 }
175 }
176
177 pub fn preallocate(&mut self, len: usize) {
178 if self.caches.len() < len {
179 self.caches.resize(len, InlineCache::new());
180 }
181 }
182
183 #[inline(always)]
184 pub fn get(&self, pc: usize) -> Option<&InlineCache> {
185 self.caches.get(pc)
186 }
187
188 #[inline(always)]
189 pub fn get_mut(&mut self, pc: usize) -> Option<&mut InlineCache> {
190 self.caches.get_mut(pc)
191 }
192
193 #[inline(always)]
194 pub fn get_own_fast(&self, pc: usize, shape_id: usize) -> Option<u32> {
195 if let Some(ic) = self.caches.get(pc) {
196 let r = ic.reads[0];
197 if r.shape_id == shape_id as u32 && r.proto_ptr == 0 {
198 return Some(r.offset);
199 }
200 }
201 None
202 }
203
204 #[inline(always)]
205 pub fn get_reads0_fast(&self, pc: usize, shape_id: usize) -> Option<(u32, usize)> {
206 if let Some(ic) = self.caches.get(pc) {
207 let r = ic.reads[0];
208 if r.shape_id == shape_id as u32 {
209 return Some((r.offset, r.proto_ptr));
210 }
211 }
212 None
213 }
214
215 #[inline(always)]
216 pub fn get_reads0_values(&self, pc: usize, shape_id: usize) -> (bool, u32, usize) {
217 if let Some(ic) = self.caches.get(pc) {
218 let r = &ic.reads[0];
219 if r.shape_id == shape_id as u32 {
220 return (true, r.offset, r.proto_ptr);
221 }
222 }
223 (false, 0, 0)
224 }
225
226 #[inline(always)]
227 pub fn get_reads123_values(&self, pc: usize, shape_id: usize) -> (bool, u32, usize) {
228 if let Some(ic) = self.caches.get(pc) {
229 let sid = shape_id as u32;
230 let r1 = ic.reads[1];
231 if r1.shape_id == sid {
232 return (true, r1.offset, r1.proto_ptr);
233 }
234 }
235 (false, 0, 0)
236 }
237
238 #[inline(always)]
239 pub fn set_own_fast(&self, pc: usize, shape_id: usize) -> Option<u32> {
240 if let Some(ic) = self.caches.get(pc) {
241 let w = &ic.write;
242 if w.shape_id == shape_id as u32 && w.new_shape == 0 {
243 return Some(w.offset);
244 }
245 }
246 None
247 }
248
249 #[inline(always)]
250 pub fn get_write_no_accessor(&self, pc: usize, shape_id: ShapeId) -> bool {
251 if let Some(ic) = self.caches.get(pc) {
252 let w = &ic.write;
253 if w.shape_id == shape_id.0 as u32 && w.no_accessor != 0 {
254 return true;
255 }
256 }
257 false
258 }
259
260 #[inline(always)]
261 pub fn set_write_no_accessor(&mut self, pc: usize) {
262 if let Some(ic) = self.caches.get_mut(pc) {
263 ic.write.no_accessor = 1;
264 }
265 }
266
267 #[inline(always)]
268 pub fn get_global_cache(&self, pc: usize, global_shape_id: usize, atom_id: u32) -> Option<u32> {
269 if let Some(ic) = self.caches.get(pc) {
270 let r = &ic.reads[0];
271
272 if r.shape_id == global_shape_id as u32 && r.proto_ptr == atom_id as usize + 1 {
273 return Some(r.offset);
274 }
275 }
276 None
277 }
278
279 #[inline(always)]
280 pub fn insert_global_cache(
281 &mut self,
282 pc: usize,
283 global_shape_id: usize,
284 offset: u32,
285 atom_id: u32,
286 ) {
287 self.ensure_capacity(pc + 1);
288 if let Some(ic) = self.caches.get_mut(pc) {
289 ic.reads[0] = InlineCacheRead {
290 shape_id: global_shape_id as u32,
291 offset,
292 proto_ptr: atom_id as usize + 1,
293 };
294 }
295 }
296}
297
298impl Default for InlineCacheTable {
299 fn default() -> Self {
300 Self::new()
301 }
302}
303
304#[cfg(test)]
305mod tests {
306 use super::*;
307 use crate::object::shape::ShapeId;
308 use std::sync::atomic::{AtomicUsize, Ordering};
309
310 fn test_shape_id() -> ShapeId {
311 static NEXT_ID: AtomicUsize = AtomicUsize::new(1);
312 ShapeId(NEXT_ID.fetch_add(1, Ordering::Relaxed))
313 }
314
315 #[test]
316 fn test_inline_cache_basic() {
317 let mut ic = InlineCache::new();
318 let shape_id = test_shape_id();
319
320 assert!(ic.get(shape_id).is_none());
321
322 ic.insert(shape_id, 42, None);
323 assert_eq!(ic.get(shape_id), Some((42, None)));
324 }
325
326 #[test]
327 fn test_inline_cache_multiple_shapes() {
328 let mut ic = InlineCache::new();
329 let shape1 = test_shape_id();
330 let shape2 = test_shape_id();
331
332 ic.insert(shape1, 10, None);
333 assert_eq!(ic.get(shape1), Some((10, None)));
334
335 ic.insert(shape2, 20, None);
336 assert_eq!(ic.get(shape1), Some((10, None)));
337 assert_eq!(ic.get(shape2), Some((20, None)));
338 }
339
340 #[test]
341 fn test_inline_cache_eviction() {
342 let mut ic = InlineCache::new();
343 let mut shapes = Vec::new();
344
345 for i in 0..IC_POLY {
346 let s = test_shape_id();
347 shapes.push(s);
348 ic.insert(s, i as u32, None);
349 }
350
351 for (i, &s) in shapes.iter().enumerate() {
352 assert_eq!(ic.get(s), Some((i as u32, None)));
353 }
354
355 let new_shape = test_shape_id();
356 ic.insert(new_shape, 100, None);
357 assert_eq!(ic.get(new_shape), Some((100, None)));
358
359 assert_eq!(ic.get(shapes[0]), None);
360
361 for i in 1..IC_POLY {
362 assert_eq!(ic.get(shapes[i]), Some((i as u32, None)));
363 }
364 }
365
366 #[test]
367 fn test_inline_cache_prototype() {
368 let mut ic = InlineCache::new();
369 let shape_id = test_shape_id();
370 let proto = 0x1234usize;
371
372 ic.insert(shape_id, 7, Some(proto));
373 assert_eq!(ic.get(shape_id), Some((7, Some(proto))));
374 }
375}