diff --git a/src/arena.rs b/src/arena.rs index d4e949ba..1b5cd3e5 100644 --- a/src/arena.rs +++ b/src/arena.rs @@ -633,16 +633,22 @@ mod tests { #[test] fn heap_cell_value_const_cast() { let mut wam = MockWAM::new(); + #[cfg(target_pointer_width = "32")] - let const_value = HeapCellValue::from(ConsPtr::build_with( - std::ptr::without_provenance(0x0000_0431), - ConsPtrMaskTag::Cons, - )); + assert_eq!(ConsPtr::NICHE_SHIFT, 0); + + #[cfg(not(target_pointer_width = "32"))] + assert_eq!(ConsPtr::NICHE_SHIFT, 3); + + #[cfg(target_pointer_width = "32")] + let dummy_ptr: *const ArenaHeader = std::ptr::without_provenance(0x0000_0438); + #[cfg(target_pointer_width = "64")] - let const_value = HeapCellValue::from(ConsPtr::build_with( - std::ptr::without_provenance(0x0000_5555_ff00_0431), - ConsPtrMaskTag::Cons, - )); + let dummy_ptr: *const ArenaHeader = std::ptr::without_provenance(0x0000_5555_ff00_0438); + + assert!(dummy_ptr.is_aligned()); + + let const_value = HeapCellValue::from_arena_header_ptr(dummy_ptr); match const_value.to_untyped_arena_ptr() { Some(arena_ptr) => { @@ -657,8 +663,7 @@ mod tests { } let stream = Stream::from_static_string("test", &mut wam.machine_st.arena); - let stream_cell = - HeapCellValue::from(ConsPtr::build_with(stream.as_ptr(), ConsPtrMaskTag::Cons)); + let stream_cell = HeapCellValue::from_arena_header_ptr(stream.as_ptr()); match stream_cell.to_untyped_arena_ptr() { Some(arena_ptr) => { @@ -727,7 +732,7 @@ mod tests { Some(untyped_arena_ptr) => { assert_eq!( Some(big_rat_ptr.header_ptr()), - Some(untyped_arena_ptr.into()), + Some(untyped_arena_ptr.get_ptr()), ); } None => { diff --git a/src/machine/gc.rs b/src/machine/gc.rs index 7c79eba0..0aaf127d 100644 --- a/src/machine/gc.rs +++ b/src/machine/gc.rs @@ -690,8 +690,7 @@ mod tests { // term is: [a, ] let stream = Stream::from_static_string("test", &mut wam.machine_st.arena); - let stream_cell = - HeapCellValue::from(ConsPtr::build_with(stream.as_ptr(), ConsPtrMaskTag::Cons)); + let stream_cell = HeapCellValue::from_arena_header_ptr(stream.as_ptr()); let mut writer = wam.machine_st.heap.reserve(16).unwrap(); diff --git a/src/macros.rs b/src/macros.rs index 3d88e437..c63240ba 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -139,12 +139,7 @@ macro_rules! typed_arena_ptr_as_cell { macro_rules! raw_ptr_as_cell { ($ptr:expr) => {{ - // Cell is 64-bit, but raw ptr is 32-bit in 32-bit systems - let ptr: *const _ = $ptr; - // This needs to expose provenance because it needs to be turned back into a pointer - // in contexts where there is no available provenance locally. For example, in - // `ConsPtr::as_ptr`. - HeapCellValue::from_ptr_addr(ptr.expose_provenance()) + HeapCellValue::from_arena_header_ptr($ptr) }}; } diff --git a/src/types.rs b/src/types.rs index 7399f8eb..994a6a27 100644 --- a/src/types.rs +++ b/src/types.rs @@ -16,6 +16,7 @@ use std::ops::{Add, Sub, SubAssign}; use dashu::{Integer, Rational}; +// Variant tag MUST be odd for all but Cons #[derive(Specifier, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] #[repr(u8)] #[bits = 6] @@ -61,19 +62,46 @@ pub struct ConsPtr { } impl ConsPtr { + // ConstPtr.ptr has 61 bits if usize on the current arch is larger than that + // use the niche provided by the alignment of ArenaHeader. + // The niece is that the log2(alignment) least significant bits are always 0, so we can shift the + // address down by that many places without losing information. + // That way with an alignment of 8 a 64-bit address fits into 61-bits after shifting it down by 3 places + // and as a result be can store it without loss in ConsPtr.ptr + pub(crate) const NICHE_SHIFT: u32 = if usize::BITS > 61 { + std::mem::align_of::().ilog2() + } else { + 0 + }; + #[inline(always)] - pub fn build_with(ptr: *const ArenaHeader, tag: ConsPtrMaskTag) -> Self { + pub fn from_ptr(ptr: *const ArenaHeader) -> Self { + Self::build_with(ptr, ConsPtrMaskTag::Cons) + } + + #[inline(always)] + fn build_with(ptr: *const ArenaHeader, tag: ConsPtrMaskTag) -> Self { + let mut addr = u64::try_from(ptr.expose_provenance()) + .expect("pointer address {ptr:p} should fit into u64"); + + debug_assert_eq!(addr % std::mem::align_of::() as u64, 0); + + addr >>= Self::NICHE_SHIFT; + ConsPtr::new() - .with_ptr(ptr.expose_provenance() as u64) + .with_ptr(addr) .with_f(false) .with_m(false) .with_tag(tag) } #[inline(always)] - pub fn as_ptr(self) -> *mut u8 { - let addr: u64 = self.ptr(); - std::ptr::with_exposed_provenance_mut(addr as usize) + pub fn as_ptr(self) -> *const ArenaHeader { + let mut addr: u64 = self.ptr(); + + addr <<= Self::NICHE_SHIFT; + + std::ptr::with_exposed_provenance(addr as usize) } #[inline(always)] @@ -336,7 +364,7 @@ where { #[inline] fn from(arena_ptr: TypedArenaPtr) -> HeapCellValue { - HeapCellValue::from(arena_ptr.header_ptr().expose_provenance() as u64) + HeapCellValue::from_arena_header_ptr(arena_ptr.header_ptr()) } } @@ -357,18 +385,6 @@ impl From for HeapCellValue { } } -impl From for HeapCellValue { - #[inline(always)] - fn from(cons_ptr: ConsPtr) -> HeapCellValue { - HeapCellValue::from_bytes( - ConsPtr::from(cons_ptr.as_ptr().expose_provenance() as u64) - .with_tag(ConsPtrMaskTag::Cons) - .with_m(false) - .into_bytes(), - ) - } -} - impl From<(Number, &mut Arena)> for HeapCellValue { #[inline(always)] fn from((n, arena): (Number, &mut Arena)) -> HeapCellValue { @@ -558,12 +574,12 @@ impl HeapCellValue { } #[inline] - pub fn from_ptr_addr(ptr_bytes: usize) -> Self { - HeapCellValue::from_bytes((ptr_bytes as u64).to_ne_bytes()) + pub fn from_arena_header_ptr(ptr: *const ArenaHeader) -> Self { + HeapCellValue::from_bytes(ConsPtr::from_ptr(ptr).into_bytes()) } - pub fn to_ptr_addr(self) -> usize { - u64::from_ne_bytes(self.into_bytes()) as usize + pub fn to_arena_header_ptr(self) -> *const ArenaHeader { + ConsPtr::from_bytes(self.into_bytes()).as_ptr() } #[inline] @@ -707,21 +723,14 @@ const_assert!(mem::size_of::() == 8); impl From<*const ArenaHeader> for UntypedArenaPtr { #[inline] fn from(ptr: *const ArenaHeader) -> UntypedArenaPtr { - UntypedArenaPtr::build_with(ptr.expose_provenance()) - } -} - -impl From<*const IndexPtr> for UntypedArenaPtr { - #[inline] - fn from(ptr: *const IndexPtr) -> UntypedArenaPtr { - UntypedArenaPtr::build_with(ptr.expose_provenance()) + UntypedArenaPtr::from_bytes(ConsPtr::from_ptr(ptr).into_bytes()) } } impl From for *const ArenaHeader { #[inline] fn from(ptr: UntypedArenaPtr) -> *const ArenaHeader { - ptr.get_ptr().cast::() + ptr.get_ptr() } } @@ -732,9 +741,8 @@ impl UntypedArenaPtr { } #[inline] - pub fn get_ptr(self) -> *const u8 { - let addr: u64 = self.ptr(); - std::ptr::with_exposed_provenance(addr as usize) + pub fn get_ptr(self) -> *const ArenaHeader { + ConsPtr::from_bytes(self.into_bytes()).as_ptr() } #[inline] @@ -748,7 +756,7 @@ impl UntypedArenaPtr { #[inline] pub fn payload_offset(self) -> *const u8 { - unsafe { self.get_ptr().add(size_of::()) } + unsafe { self.get_ptr().byte_add(size_of::()).cast() } } /// # Safety