]> Repositorios git - scryer-prolog.git/commitdiff
don't erase ptr type early when construction a Cons HeapCellValue
authorSkgland <[email protected]>
Wed, 22 Apr 2026 21:22:27 +0000 (23:22 +0200)
committerSkgland <[email protected]>
Sat, 25 Apr 2026 16:12:31 +0000 (18:12 +0200)
rather than passing an address as usize pass the ArenaHeader pointer
similarly don't return a u8 ptr but use a ArenaHeader pointer instead

Don't convert the pointer to a ConsPtr by going through native endian
bytes in between.
We are exploiting the fact that the 3 least significant bytes are zero
for pointer to types of alignment 8 and we expect these to line up with
the f, m, and tag field at the end of the ConsPtr struct, but using
native endiannes for this would only work on big endian systems.

src/arena.rs
src/machine/gc.rs
src/macros.rs
src/types.rs

index d4e949ba3f0b46ee319bfac6d1e7d054c7555764..1b5cd3e5a808b37c47b754dd62c3893165b453f2 100644 (file)
@@ -633,16 +633,22 @@ mod tests {
     #[test]
     fn heap_cell_value_const_cast() {
         let mut wam = MockWAM::new();
+
+        #[cfg(target_pointer_width = "32")]
+        assert_eq!(ConsPtr::NICHE_SHIFT, 0);
+
+        #[cfg(not(target_pointer_width = "32"))]
+        assert_eq!(ConsPtr::NICHE_SHIFT, 3);
+
         #[cfg(target_pointer_width = "32")]
-        let const_value = HeapCellValue::from(ConsPtr::build_with(
-            std::ptr::without_provenance(0x0000_0431),
-            ConsPtrMaskTag::Cons,
-        ));
+        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 => {
index 7c79eba0a297e533998e7b1df8139fa6c09d776a..0aaf127db7d57cb54daa2056349ea2d3ccf4a660 100644 (file)
@@ -690,8 +690,7 @@ mod tests {
 
         // term is: [a, <stream ptr>]
         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();
 
index 3d88e437eab6bbd4741834b766bd475e0d02e791..c63240bad722b9d8098cae307ce5c2065567909c 100644 (file)
@@ -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)
     }};
 }
 
index 7399f8ebae473396f633638534aa0a36b3b18342..994a6a27dcfc4bebec03967f1cff9225663c1ae8 100644 (file)
@@ -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::<ArenaHeader>().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::<ArenaHeader>() 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<T>) -> 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<CodeIndexOffset> for HeapCellValue {
     }
 }
 
-impl From<ConsPtr> 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::<UntypedArenaPtr>() == 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<UntypedArenaPtr> for *const ArenaHeader {
     #[inline]
     fn from(ptr: UntypedArenaPtr) -> *const ArenaHeader {
-        ptr.get_ptr().cast::<ArenaHeader>()
+        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::<ArenaHeader>()) }
+        unsafe { self.get_ptr().byte_add(size_of::<ArenaHeader>()).cast() }
     }
 
     /// # Safety