use super::{LocalIdx, Operand, Reset};
use crate::{
Error,
ValType,
engine::{
TranslationError,
translator::{func::LocalOperand, utils::required_cells_for_ty},
},
ir::{BoundedSlotSpan, Slot, SlotSpan},
};
use alloc::vec::Vec;
#[cfg(doc)]
use super::Stack;
pub trait IntoLocalIdx: Copy {
fn into_local_idx(self) -> LocalIdx;
}
impl IntoLocalIdx for LocalIdx {
#[inline]
fn into_local_idx(self) -> LocalIdx {
self
}
}
impl IntoLocalIdx for LocalOperand {
#[inline]
fn into_local_idx(self) -> LocalIdx {
self.local_index()
}
}
impl IntoLocalIdx for &'_ LocalOperand {
#[inline]
fn into_local_idx(self) -> LocalIdx {
self.local_index()
}
}
pub trait LocalValType {
fn ty(self) -> ValType;
}
impl LocalValType for LocalOperand {
fn ty(self) -> ValType {
LocalOperand::ty(&self)
}
}
impl LocalValType for &'_ LocalOperand {
fn ty(self) -> ValType {
LocalOperand::ty(self)
}
}
#[derive(Debug, Default)]
pub struct StackLayout {
local_offsets: Vec<u16>,
min_temp_offset: u16,
}
impl Reset for StackLayout {
fn reset(&mut self) {
self.local_offsets.clear();
self.min_temp_offset = 0;
}
}
impl StackLayout {
fn len_locals(&self) -> usize {
self.local_offsets.len()
}
pub fn register_locals(&mut self, amount: usize, ty: ValType) -> Result<(), Error> {
let cells_per_local = required_cells_for_ty(ty);
let err_too_many_slots = || Error::from(TranslationError::AllocatedTooManySlots);
let delta_offset = amount
.checked_mul(usize::from(cells_per_local))
.ok_or_else(err_too_many_slots)?;
usize::from(self.min_temp_offset)
.checked_add(delta_offset)
.filter(|&new_max| new_max <= usize::from(u16::MAX))
.ok_or_else(err_too_many_slots)?;
for _ in 0..amount {
self.local_offsets.push(self.min_temp_offset);
self.min_temp_offset += cells_per_local;
}
Ok(())
}
#[must_use]
pub fn stack_space(&self, slot: Slot) -> StackSpace {
let index = u16::from(slot);
if index < self.min_temp_offset {
return StackSpace::Local;
}
StackSpace::Temp
}
pub fn operand_to_slot(&mut self, operand: Operand) -> Result<Slot, Error> {
match operand {
Operand::Local(operand) => self.local_to_slot(operand),
Operand::Temp(operand) => Ok(operand.temp_slots().head()),
Operand::Immediate(operand) => {
panic!("cannot convert `ImmediateOperand` to stack `Slot` but got: {operand:?}")
}
}
}
#[inline]
pub fn local_to_slot(&self, item: impl IntoLocalIdx) -> Result<Slot, Error> {
let index = item.into_local_idx();
debug_assert!(
(u32::from(index) as usize) < self.len_locals(),
"out of bounds local operand index: {index:?}"
);
let Ok(index) = u16::try_from(u32::from(index)) else {
return Err(Error::from(TranslationError::AllocatedTooManySlots));
};
let offset = self.local_offsets[usize::from(index)];
Ok(Slot::from(offset))
}
#[inline]
pub fn local_to_slots<L>(&self, item: L) -> Result<BoundedSlotSpan, Error>
where
L: IntoLocalIdx + LocalValType,
{
let head = self.local_to_slot(item)?;
let len = required_cells_for_ty(item.ty());
Ok(BoundedSlotSpan::new(SlotSpan::new(head), len))
}
}
#[derive(Debug, Copy, Clone)]
pub enum StackSpace {
Local,
Temp,
}