mmtk/util/heap/
space_descriptor.rs

1use bytemuck::Zeroable;
2
3use crate::util::constants::*;
4use crate::util::heap::layout::vm_layout::{self, vm_layout};
5use crate::util::Address;
6use std::sync::atomic::{AtomicUsize, Ordering};
7
8const TYPE_BITS: usize = 2;
9#[allow(unused)]
10const TYPE_SHARED: usize = 0;
11const TYPE_CONTIGUOUS: usize = 1;
12const TYPE_CONTIGUOUS_HI: usize = 3;
13const TYPE_MASK: usize = (1 << TYPE_BITS) - 1;
14const SIZE_SHIFT: usize = TYPE_BITS;
15const SIZE_BITS: usize = 10;
16const SIZE_MASK: usize = ((1 << SIZE_BITS) - 1) << SIZE_SHIFT;
17const EXPONENT_SHIFT: usize = SIZE_SHIFT + SIZE_BITS;
18const EXPONENT_BITS: usize = 5;
19const EXPONENT_MASK: usize = ((1 << EXPONENT_BITS) - 1) << EXPONENT_SHIFT;
20const MANTISSA_SHIFT: usize = EXPONENT_SHIFT + EXPONENT_BITS;
21const MANTISSA_BITS: usize = 14;
22const BASE_EXPONENT: usize = BITS_IN_INT - MANTISSA_BITS;
23
24const INDEX_MASK: usize = !TYPE_MASK;
25const INDEX_SHIFT: usize = TYPE_BITS;
26
27static DISCONTIGUOUS_SPACE_INDEX: AtomicUsize = AtomicUsize::new(DISCONTIG_INDEX_INCREMENT);
28const DISCONTIG_INDEX_INCREMENT: usize = 1 << TYPE_BITS;
29
30#[derive(Copy, Clone, PartialEq, Debug)]
31#[repr(transparent)]
32pub struct SpaceDescriptor(usize);
33
34unsafe impl Zeroable for SpaceDescriptor {}
35
36impl SpaceDescriptor {
37    pub const UNINITIALIZED: Self = SpaceDescriptor(0);
38
39    pub fn create_descriptor_from_heap_range(start: Address, end: Address) -> SpaceDescriptor {
40        let top = end == vm_layout().heap_end;
41        if vm_layout().force_use_contiguous_spaces {
42            let space_index = if start > vm_layout().heap_end {
43                usize::MAX
44            } else {
45                start >> vm_layout().space_shift_64()
46            };
47            let flags = if top {
48                TYPE_CONTIGUOUS_HI
49            } else {
50                TYPE_CONTIGUOUS
51            };
52            return SpaceDescriptor((space_index << INDEX_SHIFT) | flags);
53        }
54        let chunks = (end - start) >> vm_layout::LOG_BYTES_IN_CHUNK;
55        debug_assert!(!start.is_zero() && chunks > 0 && chunks < (1 << SIZE_BITS));
56        let mut tmp = start >> BASE_EXPONENT;
57        let mut exponent = 0;
58        while (tmp != 0) && ((tmp & 1) == 0) {
59            tmp >>= 1;
60            exponent += 1;
61        }
62        let mantissa = tmp;
63        debug_assert!((tmp << (BASE_EXPONENT + exponent)) == start.as_usize());
64        SpaceDescriptor(
65            (mantissa << MANTISSA_SHIFT)
66                | (exponent << EXPONENT_SHIFT)
67                | (chunks << SIZE_SHIFT)
68                | (if top {
69                    TYPE_CONTIGUOUS_HI
70                } else {
71                    TYPE_CONTIGUOUS
72                }),
73        )
74    }
75
76    pub fn create_descriptor() -> SpaceDescriptor {
77        let next =
78            DISCONTIGUOUS_SPACE_INDEX.fetch_add(DISCONTIG_INDEX_INCREMENT, Ordering::Relaxed);
79        let ret = SpaceDescriptor(next);
80        debug_assert!(!ret.is_contiguous());
81        ret
82    }
83
84    pub fn is_empty(self) -> bool {
85        self.0 == SpaceDescriptor::UNINITIALIZED.0
86    }
87
88    pub fn is_contiguous(self) -> bool {
89        (self.0 & TYPE_CONTIGUOUS) == TYPE_CONTIGUOUS
90    }
91
92    pub fn is_contiguous_hi(self) -> bool {
93        (self.0 & TYPE_MASK) == TYPE_CONTIGUOUS_HI
94    }
95
96    pub fn get_start(self) -> Address {
97        if !vm_layout().force_use_contiguous_spaces {
98            // For 64-bit discontiguous space, use 32-bit start address
99            self.get_start_32()
100        } else {
101            unsafe { Address::from_usize(self.get_index() << vm_layout().log_space_extent) }
102        }
103    }
104
105    fn get_start_32(self) -> Address {
106        debug_assert!(self.is_contiguous());
107
108        let descriptor = self.0;
109        let mantissa = descriptor >> MANTISSA_SHIFT;
110        let exponent = (descriptor & EXPONENT_MASK) >> EXPONENT_SHIFT;
111        unsafe { Address::from_usize(mantissa << (BASE_EXPONENT + exponent)) }
112    }
113
114    #[cfg(target_pointer_width = "64")]
115    pub fn get_extent(self) -> usize {
116        if !vm_layout().force_use_contiguous_spaces {
117            // For 64-bit discontiguous space, use 32-bit extent
118            self.get_extent_32()
119        } else {
120            vm_layout().space_size_64()
121        }
122    }
123
124    #[cfg(target_pointer_width = "32")]
125    pub fn get_extent(self) -> usize {
126        self.get_extent_32()
127    }
128
129    fn get_extent_32(self) -> usize {
130        debug_assert!(self.is_contiguous());
131        let chunks = (self.0 & SIZE_MASK) >> SIZE_SHIFT;
132        chunks << vm_layout::LOG_BYTES_IN_CHUNK
133    }
134
135    pub fn get_index(self) -> usize {
136        (self.0 & INDEX_MASK) >> INDEX_SHIFT
137    }
138}
139
140#[cfg(test)]
141mod tests {
142    use super::*;
143    use crate::util::heap::layout::vm_layout::*;
144
145    #[test]
146    fn create_discontiguous_descriptor() {
147        let d1 = SpaceDescriptor::create_descriptor();
148        assert!(!d1.is_empty());
149        assert!(!d1.is_contiguous());
150        assert!(!d1.is_contiguous_hi());
151
152        let d2 = SpaceDescriptor::create_descriptor();
153        assert!(!d2.is_empty());
154        assert!(!d2.is_contiguous());
155        assert!(!d2.is_contiguous_hi());
156    }
157
158    const TEST_SPACE_SIZE: usize = BYTES_IN_CHUNK * 10;
159
160    #[test]
161    fn create_contiguous_descriptor_at_heap_start() {
162        let d = SpaceDescriptor::create_descriptor_from_heap_range(
163            vm_layout().heap_start,
164            vm_layout().heap_start + TEST_SPACE_SIZE,
165        );
166        assert!(!d.is_empty());
167        assert!(d.is_contiguous());
168        assert!(!d.is_contiguous_hi());
169        assert_eq!(d.get_start(), vm_layout().heap_start);
170        if cfg!(target_pointer_width = "64") {
171            assert_eq!(d.get_extent(), vm_layout().space_size_64());
172        } else {
173            assert_eq!(d.get_extent(), TEST_SPACE_SIZE);
174        }
175    }
176
177    #[test]
178    fn create_contiguous_descriptor_in_heap() {
179        let d = SpaceDescriptor::create_descriptor_from_heap_range(
180            vm_layout().heap_start + TEST_SPACE_SIZE,
181            vm_layout().heap_start + TEST_SPACE_SIZE * 2,
182        );
183        assert!(!d.is_empty());
184        assert!(d.is_contiguous());
185        assert!(!d.is_contiguous_hi());
186        if cfg!(target_pointer_width = "64") {
187            assert_eq!(d.get_start(), vm_layout().heap_start);
188            assert_eq!(d.get_extent(), vm_layout().space_size_64());
189        } else {
190            assert_eq!(d.get_start(), vm_layout().heap_start + TEST_SPACE_SIZE);
191            assert_eq!(d.get_extent(), TEST_SPACE_SIZE);
192        }
193    }
194
195    #[test]
196    fn create_contiguous_descriptor_at_heap_end() {
197        let d = SpaceDescriptor::create_descriptor_from_heap_range(
198            vm_layout().heap_end - TEST_SPACE_SIZE,
199            vm_layout().heap_end,
200        );
201        assert!(!d.is_empty());
202        assert!(d.is_contiguous());
203        assert!(d.is_contiguous_hi());
204        if cfg!(target_pointer_width = "64") {
205            assert_eq!(
206                d.get_start(),
207                vm_layout().heap_end - vm_layout().space_size_64()
208            );
209            assert_eq!(d.get_extent(), vm_layout().space_size_64());
210        } else {
211            assert_eq!(d.get_start(), vm_layout().heap_end - TEST_SPACE_SIZE);
212            assert_eq!(d.get_extent(), TEST_SPACE_SIZE);
213        }
214    }
215}