use super::{err_stack_overflow, StackOffsets};
use crate::{
core::{ReadAs, UntypedVal, WriteAs},
engine::code_map::CompiledFuncRef,
ir::Slot,
TrapCode,
};
use alloc::vec::Vec;
use core::{
fmt::{self, Debug},
mem::{self, MaybeUninit},
ops::Range,
ptr,
slice,
};
#[cfg(doc)]
use super::calls::CallFrame;
#[cfg(doc)]
use crate::engine::EngineFunc;
pub struct ValueStack {
values: Vec<UntypedVal>,
max_len: usize,
}
impl ValueStack {
pub const DEFAULT_MIN_HEIGHT: usize = 1024;
pub const DEFAULT_MAX_HEIGHT: usize = 1024 * Self::DEFAULT_MIN_HEIGHT;
}
impl Debug for ValueStack {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ValueStack")
.field("max_len", &self.max_len)
.field("entries", &&self.values[..])
.finish()
}
}
#[cfg(test)]
impl PartialEq for ValueStack {
fn eq(&self, other: &Self) -> bool {
self.values == other.values
}
}
#[cfg(test)]
impl Eq for ValueStack {}
impl Default for ValueStack {
fn default() -> Self {
const REGISTER_SIZE: usize = mem::size_of::<UntypedVal>();
Self::new(
Self::DEFAULT_MIN_HEIGHT / REGISTER_SIZE,
Self::DEFAULT_MAX_HEIGHT / REGISTER_SIZE,
)
}
}
impl ValueStack {
pub fn new(initial_len: usize, maximum_len: usize) -> Self {
assert!(
initial_len > 0,
"cannot initialize the value stack with zero length",
);
assert!(
initial_len <= maximum_len,
"initial value stack length is greater than maximum value stack length",
);
Self {
values: Vec::with_capacity(initial_len),
max_len: maximum_len,
}
}
pub fn empty() -> Self {
Self {
values: Vec::new(),
max_len: 0,
}
}
pub fn reset(&mut self) {
self.values.clear();
}
pub fn root_stack_ptr(&mut self) -> FrameSlots {
FrameSlots::new(self.values.as_mut_ptr())
}
pub unsafe fn stack_ptr_at(&mut self, offset: impl Into<ValueStackOffset>) -> FrameSlots {
let ptr = self.values.as_mut_ptr().add(offset.into().0);
FrameSlots::new(ptr)
}
pub fn capacity(&self) -> usize {
debug_assert!(self.values.len() <= self.values.capacity());
self.values.capacity()
}
#[inline(always)]
pub fn extend_by(
&mut self,
additional: usize,
on_resize: impl FnOnce(&mut Self),
) -> Result<&mut [MaybeUninit<UntypedVal>], TrapCode> {
if additional >= self.max_len() - self.len() {
return Err(err_stack_overflow());
}
let prev_capacity = self.capacity();
self.values.reserve(additional);
if prev_capacity != self.capacity() {
on_resize(self);
}
let spare = self.values.spare_capacity_mut().as_mut_ptr();
unsafe { self.values.set_len(self.values.len() + additional) };
Ok(unsafe { slice::from_raw_parts_mut(spare, additional) })
}
#[inline(always)]
fn len(&self) -> usize {
debug_assert!(self.values.len() <= self.max_len);
self.values.len()
}
#[inline(always)]
fn max_len(&self) -> usize {
debug_assert!(self.values.len() <= self.max_len);
self.max_len
}
#[inline(always)]
pub fn drop(&mut self, amount: usize) {
assert!(self.len() >= amount);
unsafe { self.values.set_len(self.len() - amount) };
}
#[inline(always)]
pub fn drop_return(&mut self, amount: usize) -> &[UntypedVal] {
let len = self.len();
let dropped = unsafe { self.values.get_unchecked(len - amount..) }.as_ptr();
self.drop(amount);
unsafe { slice::from_raw_parts(dropped, amount) }
}
#[inline(always)]
pub fn truncate(&mut self, new_len: impl Into<ValueStackOffset>) {
let new_len = new_len.into().0;
assert!(new_len <= self.len());
unsafe { self.values.set_len(new_len) };
}
pub fn alloc_call_frame(
&mut self,
func: CompiledFuncRef,
on_resize: impl FnMut(&mut Self),
) -> Result<(FrameParams, StackOffsets), TrapCode> {
let len_stack_slots = func.len_stack_slots();
let len_consts = func.consts().len();
let len = self.len();
let mut spare = self
.extend_by(len_stack_slots as usize, on_resize)?
.iter_mut();
(&mut spare)
.zip(func.consts())
.for_each(|(uninit, const_value)| {
uninit.write(*const_value);
});
let params = FrameParams::new(spare.into_slice());
let frame = ValueStackOffset(len);
let base = ValueStackOffset(len + len_consts);
Ok((
params,
StackOffsets {
base: BaseValueStackOffset(base),
frame: FrameValueStackOffset(frame),
},
))
}
#[inline(always)]
pub fn as_slice(&self) -> &[UntypedVal] {
self.values.as_slice()
}
#[inline(always)]
pub fn as_slice_mut(&mut self) -> &mut [UntypedVal] {
self.values.as_mut_slice()
}
#[inline(always)]
pub fn drain(&mut self, from: FrameValueStackOffset, to: FrameValueStackOffset) -> usize {
debug_assert!(from <= to);
let from = from.0 .0;
let to = to.0 .0;
debug_assert!(from <= self.len());
debug_assert!(to <= self.len());
let len_drained = to - from;
self.values.drain(from..to);
len_drained
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct ValueStackOffset(usize);
impl From<FrameValueStackOffset> for ValueStackOffset {
fn from(offset: FrameValueStackOffset) -> Self {
offset.0
}
}
impl From<BaseValueStackOffset> for ValueStackOffset {
fn from(offset: BaseValueStackOffset) -> Self {
offset.0
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct FrameValueStackOffset(ValueStackOffset);
impl FrameValueStackOffset {
pub(super) fn new(index: usize) -> Self {
Self(ValueStackOffset(index))
}
}
impl From<FrameValueStackOffset> for usize {
fn from(offset: FrameValueStackOffset) -> usize {
offset.0 .0
}
}
impl From<ValueStackOffset> for FrameValueStackOffset {
fn from(offset: ValueStackOffset) -> Self {
Self(offset)
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct BaseValueStackOffset(ValueStackOffset);
impl BaseValueStackOffset {
pub(super) fn new(index: usize) -> Self {
Self(ValueStackOffset(index))
}
}
impl From<BaseValueStackOffset> for usize {
fn from(offset: BaseValueStackOffset) -> usize {
offset.0 .0
}
}
pub struct FrameParams {
range: Range<*mut MaybeUninit<UntypedVal>>,
}
impl FrameParams {
pub fn new(ptr: &mut [MaybeUninit<UntypedVal>]) -> Self {
Self {
range: ptr.as_mut_ptr_range(),
}
}
pub unsafe fn init_next(&mut self, value: UntypedVal) {
self.range.start.write(MaybeUninit::new(value));
self.range.start = self.range.start.add(1);
}
pub fn init_zeroes(mut self) {
debug_assert!(
self.range.start <= self.range.end,
"failed to zero-initialize `FrameParams`: start = {:?}, end = {:?}",
self.range.start,
self.range.end,
);
while !core::ptr::eq(self.range.start, self.range.end) {
unsafe { self.init_next(UntypedVal::from(0_u64)) }
}
}
}
pub struct FrameSlots {
ptr: *mut UntypedVal,
}
impl Debug for FrameSlots {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}", &self.ptr)
}
}
impl FrameSlots {
fn new(ptr: *mut UntypedVal) -> Self {
Self { ptr }
}
pub unsafe fn get(&self, slot: Slot) -> UntypedVal {
ptr::read(self.register_offset(slot))
}
pub unsafe fn read_as<T>(&self, slot: Slot) -> T
where
UntypedVal: ReadAs<T>,
{
UntypedVal::read_as(&*self.register_offset(slot))
}
pub unsafe fn set(&mut self, slot: Slot, value: UntypedVal) {
ptr::write(self.register_offset(slot), value)
}
pub unsafe fn write_as<T>(&mut self, slot: Slot, value: T)
where
UntypedVal: WriteAs<T>,
{
let val: &mut UntypedVal = &mut *self.register_offset(slot);
val.write_as(value);
}
unsafe fn register_offset(&self, slot: Slot) -> *mut UntypedVal {
unsafe { self.ptr.offset(isize::from(i16::from(slot))) }
}
}