use crate::header::{GcColor, Generation};
use std::sync::atomic::{AtomicU8, Ordering};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MaskingMode {
ArmTbi,
X86Lam57,
Software,
}
static CACHED_MASKING_MODE: AtomicU8 = AtomicU8::new(0);
pub fn cached_masking_mode() -> MaskingMode {
let cached = CACHED_MASKING_MODE.load(Ordering::Relaxed);
match cached {
1 => MaskingMode::Software,
2 => MaskingMode::ArmTbi,
3 => MaskingMode::X86Lam57,
_ => {
let mode = detect_masking_mode();
let val = match mode {
MaskingMode::Software => 1,
MaskingMode::ArmTbi => 2,
MaskingMode::X86Lam57 => 3,
};
CACHED_MASKING_MODE.store(val, Ordering::Relaxed);
mode
}
}
}
pub fn has_x86_lam() -> bool {
cached_masking_mode() == MaskingMode::X86Lam57
}
const 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;
const GEN_SHIFT: u32 = 61;
pub fn detect_masking_mode() -> MaskingMode {
#[cfg(target_arch = "aarch64")]
{
return MaskingMode::ArmTbi;
}
#[cfg(target_arch = "x86_64")]
{
if try_enable_lam57() {
return MaskingMode::X86Lam57;
}
}
MaskingMode::Software
}
#[cfg(target_arch = "x86_64")]
fn try_enable_lam57() -> bool {
false
}
#[inline(always)]
pub fn mask_ptr(tagged: *mut u8, mode: MaskingMode) -> *mut u8 {
let addr = tagged as u64;
let masked = match mode {
MaskingMode::ArmTbi => addr & ADDRESS_MASK_56,
MaskingMode::X86Lam57 => addr & ADDRESS_MASK_57,
MaskingMode::Software => addr & ADDRESS_MASK_48,
};
masked as *mut u8
}
#[inline(always)]
pub fn tag_ptr(ptr: *mut u8, color: GcColor, generation: Generation) -> *mut u8 {
let addr = ptr as u64;
let color_bits = (color as u64) << COLOR_SHIFT;
let gen_bit = (generation as u64) << GEN_SHIFT;
(addr | color_bits | gen_bit) as *mut u8
}
#[inline(always)]
pub fn read_color(tagged: *mut u8) -> GcColor {
let bits = tagged as u64;
match (bits >> COLOR_SHIFT) & 0b11 {
0 => GcColor::White,
1 => GcColor::Gray,
2 => GcColor::Black,
_ => GcColor::White,
}
}
#[inline(always)]
pub fn read_generation(tagged: *mut u8) -> Generation {
let bits = tagged as u64;
if (bits >> GEN_SHIFT) & 1 == 0 {
Generation::Young
} else {
Generation::Old
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_software_mask_strips_upper_bits() {
let raw = 0xDEAD_0000_1234_5678_u64 as *mut u8;
let masked = mask_ptr(raw, MaskingMode::Software);
assert_eq!(masked as u64, 0x0000_0000_1234_5678);
}
#[test]
fn test_tag_and_read_color() {
let raw = 0x0000_0000_1234_5678_u64 as *mut u8;
let tagged = tag_ptr(raw, GcColor::Gray, Generation::Young);
assert_eq!(read_color(tagged), GcColor::Gray);
assert_eq!(read_generation(tagged), Generation::Young);
let unmasked = mask_ptr(tagged, MaskingMode::Software);
assert_eq!(
unmasked as u64 & 0x0000_FFFF_FFFF_FFFF,
0x0000_0000_1234_5678
);
}
#[test]
fn test_tag_and_read_generation() {
let raw = 0x0000_0000_ABCD_EF00_u64 as *mut u8;
let tagged = tag_ptr(raw, GcColor::Black, Generation::Old);
assert_eq!(read_color(tagged), GcColor::Black);
assert_eq!(read_generation(tagged), Generation::Old);
}
}