use core::marker::PhantomData; use std::ptr::NonNull; use crate::machine::heap::AllocError; use crate::raw_block::*; use crate::types::*; use std::mem; use std::ops::{Index, IndexMut}; use std::ptr; impl RawBlockTraits for Stack { #[inline] fn init_size() -> usize { 10 * 1024 * 1024 } #[inline] fn align() -> usize { mem::align_of::() .max(mem::align_of::()) .max(mem::align_of::()) } } #[inline(always)] pub const fn prelude_size() -> usize { mem::size_of::() } #[derive(Debug)] pub struct Stack { buf: RawBlock, _marker: PhantomData, } #[derive(Debug)] pub(crate) struct AndFramePrelude { pub(crate) num_cells: usize, pub(crate) e: usize, pub(crate) cp: usize, } #[derive(Debug)] pub(crate) struct AndFrame { pub(crate) prelude: AndFramePrelude, } impl AndFrame { pub(crate) fn size_of(num_cells: usize) -> usize { prelude_size::() + num_cells * mem::size_of::() } } impl Index for AndFrame { type Output = HeapCellValue; fn index(&self, index: usize) -> &Self::Output { let prelude_offset = prelude_size::(); let index_offset = (index - 1) * mem::size_of::(); unsafe { let ptr = self as *const crate::machine::stack::AndFrame as *const u8; // This address falls outside the provenance for self, therefore we have to get it // from exposed provenance. &*std::ptr::with_exposed_provenance(ptr.addr() + prelude_offset + index_offset) } } } impl IndexMut for AndFrame { fn index_mut(&mut self, index: usize) -> &mut Self::Output { let prelude_offset = prelude_size::(); let index_offset = (index - 1) * mem::size_of::(); unsafe { let ptr = self as *mut crate::machine::stack::AndFrame as *mut u8; // This address falls outside the provenance for self, therefore we have to get it // from exposed provenance. &mut *std::ptr::with_exposed_provenance_mut(ptr.addr() + prelude_offset + index_offset) } } } impl Index for Stack { type Output = HeapCellValue; #[inline] fn index(&self, index: usize) -> &Self::Output { unsafe { let ptr = self.buf.get_unchecked(index); &*ptr.cast::() } } } impl IndexMut for Stack { #[inline] fn index_mut(&mut self, index: usize) -> &mut Self::Output { unsafe { let ptr = self.buf.get_unchecked(index); &mut *ptr.cast_mut().cast::() } } } #[derive(Debug)] pub(crate) struct OrFramePrelude { pub(crate) num_cells: usize, pub(crate) e: usize, pub(crate) cp: usize, pub(crate) b: usize, pub(crate) bp: usize, pub(crate) boip: u32, pub(crate) biip: u32, pub(crate) tr: usize, pub(crate) h: usize, pub(crate) b0: usize, pub(crate) attr_var_queue_len: usize, } #[derive(Debug)] pub(crate) struct OrFrame { pub(crate) prelude: OrFramePrelude, } impl Index for OrFrame { type Output = HeapCellValue; #[inline] fn index(&self, index: usize) -> &Self::Output { let prelude_offset = prelude_size::(); let index_offset = index * mem::size_of::(); unsafe { let ptr = self as *const crate::machine::stack::OrFrame as *const u8; // This address falls outside the provenance for self, therefore we have to get it // from exposed provenance. &*std::ptr::with_exposed_provenance(ptr.addr() + prelude_offset + index_offset) } } } impl IndexMut for OrFrame { #[inline] fn index_mut(&mut self, index: usize) -> &mut Self::Output { let prelude_offset = prelude_size::(); let index_offset = index * mem::size_of::(); unsafe { let ptr = self as *mut crate::machine::stack::OrFrame as *mut u8; // This address falls outside the provenance for self, therefore we have to get it // from exposed provenance. &mut *std::ptr::with_exposed_provenance_mut(ptr.addr() + prelude_offset + index_offset) } } } impl OrFrame { pub(crate) fn size_of(num_cells: usize) -> usize { prelude_size::() + num_cells * mem::size_of::() } } impl Stack { pub(crate) fn new() -> Result { Ok(Stack { buf: RawBlock::new()?, _marker: PhantomData, }) } #[inline(always)] unsafe fn alloc(&mut self, frame_size: usize) -> Result, AllocError> { loop { unsafe { let ptr = self.buf.alloc(frame_size); if let Some(ptr) = NonNull::new(ptr) { return Ok(ptr); } self.buf.grow()?; } } } pub(crate) fn allocate_and_frame(&mut self, num_cells: usize) -> Result { let frame_size = AndFrame::size_of(num_cells); unsafe { let e = self.buf.used_bytes(); let new_ptr = self.alloc(frame_size)?; let mut offset = prelude_size::(); for idx in 0..num_cells { let cell_ptr = new_ptr.add(offset).cast::(); ptr::write(cell_ptr.as_ptr(), stack_loc_as_cell!(AndFrame, e, idx + 1)); // Because in the Index and IndexMut implementations we need to get this from // exposed provenance, we need to expose the provenance here, even though we don't // actually use the value for anything. This is a reminder that `expose_provenance` // isn't just a cast from a pointer to an integer but has actual side effects. cell_ptr.expose_provenance(); offset += mem::size_of::(); } let and_frame = self.index_and_frame_mut(e); and_frame.prelude.num_cells = num_cells; Ok(e) } } pub(crate) fn top(&self) -> usize { self.buf.used_bytes() } pub(crate) fn allocate_or_frame(&mut self, num_cells: usize) -> Result { let frame_size = OrFrame::size_of(num_cells); unsafe { let b = self.buf.used_bytes(); let new_ptr = self.alloc(frame_size)?; let mut offset = prelude_size::(); for idx in 0..num_cells { let cell_ptr = new_ptr.byte_add(offset).cast::(); ptr::write(cell_ptr.as_ptr(), stack_loc_as_cell!(OrFrame, b, idx)); // Because in the Index and IndexMut implementations we need to get this from // exposed provenance, we need to expose the provenance here, even though we don't // actually use the value for anything. This is a reminder that `expose_provenance` // isn't just a cast from a pointer to an integer but has actual side effects. cell_ptr.expose_provenance(); offset += mem::size_of::(); } let or_frame = self.index_or_frame_mut(b); or_frame.prelude.num_cells = num_cells; Ok(b) } } fn get_raw(&self, index: usize) -> *const u8 { debug_assert!(index < self.buf.used_bytes()); unsafe { self.buf.get_unchecked(index) } } #[inline(always)] pub(crate) fn index_and_frame(&self, e: usize) -> &AndFrame { let ptr = self.get_raw(e); unsafe { &*ptr.cast::() } } #[inline(always)] pub(crate) fn index_and_frame_mut(&mut self, e: usize) -> &mut AndFrame { let ptr = self.get_raw(e); unsafe { &mut *ptr.cast_mut().cast::() } } #[inline(always)] pub(crate) fn index_or_frame(&self, b: usize) -> &OrFrame { let ptr = self.get_raw(b); unsafe { &*ptr.cast::() } } #[inline(always)] pub(crate) fn index_or_frame_mut(&mut self, b: usize) -> &mut OrFrame { let ptr = self.get_raw(b); unsafe { &mut *ptr.cast_mut().cast::() } } /// # Safety /// /// The stack must contain a valid OrFrame at [`self.top()`](Self::top), /// which can only be achieved by allocating it in the first place and later truncating the stack. /// /// No allocation must have been done since the last call to [`truncate()`](Self::truncate). #[inline(always)] pub(crate) unsafe fn index_dangling_or_frame(&self) -> &OrFrame { unsafe { let ptr = self.buf.get_unchecked(self.top()); &*ptr.cast::() } } #[inline(always)] pub(crate) fn truncate(&mut self, b: usize) { self.buf.shift_back(b); } } #[cfg(test)] mod tests { use super::*; use crate::machine::mock_wam::*; #[test] fn stack_tests() { let mut wam = MockWAM::new(); let e = wam.machine_st.stack.allocate_and_frame(10).unwrap(); // create an AND frame! let and_frame = wam.machine_st.stack.index_and_frame_mut(e); assert_eq!( e, 0 // 10 * mem::size_of::() + prelude_size::() ); assert_eq!(and_frame.prelude.num_cells, 10); for idx in 0..10 { assert_eq!(and_frame[idx + 1], stack_loc_as_cell!(AndFrame, e, idx + 1)); } and_frame[5] = empty_list_as_cell!(); assert_eq!(and_frame[5], empty_list_as_cell!()); let b = wam.machine_st.stack.allocate_or_frame(5).unwrap(); let or_frame = wam.machine_st.stack.index_or_frame_mut(b); for idx in 0..5 { assert_eq!(or_frame[idx], stack_loc_as_cell!(OrFrame, b, idx)); } let next_e = wam.machine_st.stack.allocate_and_frame(9).unwrap(); // create an AND frame! let and_frame = wam.machine_st.stack.index_and_frame_mut(next_e); for idx in 0..9 { assert_eq!( and_frame[idx + 1], stack_loc_as_cell!(AndFrame, next_e, idx + 1) ); } let and_frame = wam.machine_st.stack.index_and_frame(e); assert_eq!(and_frame[5], empty_list_as_cell!()); assert_eq!( wam.machine_st.stack[stack_loc!(AndFrame, e, 5)], empty_list_as_cell!() ); } }