1use crate::header::{GcColor, Generation};
26use std::sync::atomic::{AtomicU8, Ordering};
27
28#[derive(Debug, Clone, Copy, PartialEq, Eq)]
30pub enum MaskingMode {
31 ArmTbi,
33 X86Lam57,
35 Software,
37}
38
39static CACHED_MASKING_MODE: AtomicU8 = AtomicU8::new(0);
43
44pub fn cached_masking_mode() -> MaskingMode {
46 let cached = CACHED_MASKING_MODE.load(Ordering::Relaxed);
47 match cached {
48 1 => MaskingMode::Software,
49 2 => MaskingMode::ArmTbi,
50 3 => MaskingMode::X86Lam57,
51 _ => {
52 let mode = detect_masking_mode();
53 let val = match mode {
54 MaskingMode::Software => 1,
55 MaskingMode::ArmTbi => 2,
56 MaskingMode::X86Lam57 => 3,
57 };
58 CACHED_MASKING_MODE.store(val, Ordering::Relaxed);
59 mode
60 }
61 }
62}
63
64pub fn has_x86_lam() -> bool {
66 cached_masking_mode() == MaskingMode::X86Lam57
67}
68
69const ADDRESS_MASK_48: u64 = 0x0000_FFFF_FFFF_FFFF; const ADDRESS_MASK_56: u64 = 0x00FF_FFFF_FFFF_FFFF; const ADDRESS_MASK_57: u64 = 0x01FF_FFFF_FFFF_FFFF; const COLOR_SHIFT: u32 = 62;
76const GEN_SHIFT: u32 = 61;
77
78pub fn detect_masking_mode() -> MaskingMode {
80 #[cfg(target_arch = "aarch64")]
81 {
82 return MaskingMode::ArmTbi;
84 }
85
86 #[cfg(target_arch = "x86_64")]
87 {
88 if try_enable_lam57() {
90 return MaskingMode::X86Lam57;
91 }
92 }
93
94 MaskingMode::Software
95}
96
97#[cfg(target_arch = "x86_64")]
99fn try_enable_lam57() -> bool {
100 false
104}
105
106#[inline(always)]
111pub fn mask_ptr(tagged: *mut u8, mode: MaskingMode) -> *mut u8 {
112 let addr = tagged as u64;
113 let masked = match mode {
114 MaskingMode::ArmTbi => addr & ADDRESS_MASK_56,
115 MaskingMode::X86Lam57 => addr & ADDRESS_MASK_57,
116 MaskingMode::Software => addr & ADDRESS_MASK_48,
117 };
118 masked as *mut u8
119}
120
121#[inline(always)]
123pub fn tag_ptr(ptr: *mut u8, color: GcColor, generation: Generation) -> *mut u8 {
124 let addr = ptr as u64;
125 let color_bits = (color as u64) << COLOR_SHIFT;
126 let gen_bit = (generation as u64) << GEN_SHIFT;
127 (addr | color_bits | gen_bit) as *mut u8
128}
129
130#[inline(always)]
132pub fn read_color(tagged: *mut u8) -> GcColor {
133 let bits = tagged as u64;
134 match (bits >> COLOR_SHIFT) & 0b11 {
135 0 => GcColor::White,
136 1 => GcColor::Gray,
137 2 => GcColor::Black,
138 _ => GcColor::White,
139 }
140}
141
142#[inline(always)]
144pub fn read_generation(tagged: *mut u8) -> Generation {
145 let bits = tagged as u64;
146 if (bits >> GEN_SHIFT) & 1 == 0 {
147 Generation::Young
148 } else {
149 Generation::Old
150 }
151}
152
153#[cfg(test)]
154mod tests {
155 use super::*;
156
157 #[test]
158 fn test_software_mask_strips_upper_bits() {
159 let raw = 0xDEAD_0000_1234_5678_u64 as *mut u8;
160 let masked = mask_ptr(raw, MaskingMode::Software);
161 assert_eq!(masked as u64, 0x0000_0000_1234_5678);
162 }
163
164 #[test]
165 fn test_tag_and_read_color() {
166 let raw = 0x0000_0000_1234_5678_u64 as *mut u8;
167
168 let tagged = tag_ptr(raw, GcColor::Gray, Generation::Young);
169 assert_eq!(read_color(tagged), GcColor::Gray);
170 assert_eq!(read_generation(tagged), Generation::Young);
171
172 let unmasked = mask_ptr(tagged, MaskingMode::Software);
174 assert_eq!(
176 unmasked as u64 & 0x0000_FFFF_FFFF_FFFF,
177 0x0000_0000_1234_5678
178 );
179 }
180
181 #[test]
182 fn test_tag_and_read_generation() {
183 let raw = 0x0000_0000_ABCD_EF00_u64 as *mut u8;
184 let tagged = tag_ptr(raw, GcColor::Black, Generation::Old);
185 assert_eq!(read_color(tagged), GcColor::Black);
186 assert_eq!(read_generation(tagged), Generation::Old);
187 }
188}