use super::{LocalIdx, LocalsHead, Operand, Reset};
use crate::{core::TypedVal, engine::translator::utils::Instr, Error, ValType};
use alloc::vec::Vec;
use core::{num::NonZero, slice};
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct OperandIdx(NonZero<usize>);
impl From<OperandIdx> for usize {
fn from(value: OperandIdx) -> Self {
value.0.get().wrapping_sub(1)
}
}
impl From<usize> for OperandIdx {
fn from(value: usize) -> Self {
let Some(operand_idx) = NonZero::new(value.wrapping_add(1)) else {
panic!("out of bounds `OperandIdx`: {value}")
};
Self(operand_idx)
}
}
#[derive(Debug, Copy, Clone)]
pub enum StackOperand {
Local {
local_index: LocalIdx,
ty: ValType,
prev_local: Option<OperandIdx>,
next_local: Option<OperandIdx>,
},
Temp {
ty: ValType,
instr: Option<Instr>,
},
Immediate {
val: TypedVal,
},
}
impl StackOperand {
pub fn ty(&self) -> ValType {
match self {
StackOperand::Temp { ty, .. } => *ty,
StackOperand::Immediate { val } => val.ty(),
StackOperand::Local { ty, .. } => *ty,
}
}
}
#[derive(Debug, Default)]
pub struct OperandStack {
operands: Vec<StackOperand>,
local_heads: LocalsHead,
max_height: usize,
len_locals: usize,
}
impl Reset for OperandStack {
fn reset(&mut self) {
self.operands.clear();
self.local_heads.reset();
self.max_height = 0;
self.len_locals = 0;
}
}
impl OperandStack {
pub fn register_locals(&mut self, amount: usize) -> Result<(), Error> {
self.local_heads.register(amount)?;
Ok(())
}
pub fn height(&self) -> usize {
self.operands.len()
}
pub fn max_height(&self) -> usize {
self.max_height
}
fn update_max_stack_height(&mut self) {
self.max_height = core::cmp::max(self.max_height, self.height());
}
fn next_index(&self) -> OperandIdx {
OperandIdx::from(self.operands.len())
}
fn depth_to_index(&self, depth: usize) -> OperandIdx {
OperandIdx::from(self.height() - depth - 1)
}
pub fn push_operand(&mut self, operand: Operand) -> Result<OperandIdx, Error> {
match operand {
Operand::Local(operand) => self.push_local(operand.local_index(), operand.ty()),
Operand::Temp(operand) => self.push_temp(operand.ty(), operand.instr()),
Operand::Immediate(operand) => self.push_immediate(operand.val()),
}
}
pub fn push_local(&mut self, local_index: LocalIdx, ty: ValType) -> Result<OperandIdx, Error> {
let operand_index = self.next_index();
let next_local = self
.local_heads
.replace_first(local_index, Some(operand_index));
if let Some(next_local) = next_local {
self.update_prev_local(next_local, Some(operand_index));
}
self.operands.push(StackOperand::Local {
local_index,
ty,
prev_local: None,
next_local,
});
self.update_max_stack_height();
self.len_locals += 1;
Ok(operand_index)
}
#[inline]
pub fn push_temp(&mut self, ty: ValType, instr: Option<Instr>) -> Result<OperandIdx, Error> {
let idx = self.next_index();
self.operands.push(StackOperand::Temp { ty, instr });
self.update_max_stack_height();
Ok(idx)
}
#[inline]
pub fn push_immediate(&mut self, value: impl Into<TypedVal>) -> Result<OperandIdx, Error> {
let idx = self.next_index();
self.operands
.push(StackOperand::Immediate { val: value.into() });
self.update_max_stack_height();
Ok(idx)
}
pub fn peek(&self, n: usize) -> PeekedOperands<'_> {
let len_operands = self.operands.len();
let first_index = len_operands - n;
let Some(operands) = self.operands.get(first_index..) else {
return PeekedOperands::empty();
};
PeekedOperands {
index: first_index,
operands: operands.iter(),
}
}
#[inline]
pub fn pop(&mut self) -> Operand {
let Some(operand) = self.operands.pop() else {
panic!("tried to pop operand from empty stack");
};
let index = self.next_index();
self.try_unlink_local(operand);
Operand::new(index, operand)
}
#[inline]
pub fn get(&self, depth: usize) -> Operand {
let index = self.depth_to_index(depth);
let operand = self.get_at(index);
Operand::new(index, operand)
}
#[inline]
fn get_at(&self, index: OperandIdx) -> StackOperand {
self.operands[usize::from(index)]
}
#[must_use]
pub fn operand_to_temp(&mut self, depth: usize) -> Operand {
let index = self.depth_to_index(depth);
let operand = self.operand_to_temp_at(index);
Operand::new(index, operand)
}
#[must_use]
fn operand_to_temp_at(&mut self, index: OperandIdx) -> StackOperand {
let operand = self.get_at(index);
let ty = operand.ty();
self.try_unlink_local(operand);
self.operands[usize::from(index)] = StackOperand::Temp { ty, instr: None };
operand
}
#[must_use]
pub fn preserve_locals(&mut self, local_index: LocalIdx) -> PreservedLocalsIter<'_> {
let index = self.local_heads.replace_first(local_index, None);
PreservedLocalsIter {
operands: self,
index,
}
}
#[must_use]
pub fn preserve_all_locals(&mut self) -> PreservedAllLocalsIter<'_> {
let index = self.operands.len();
PreservedAllLocalsIter {
operands: self,
index,
}
}
#[inline]
fn try_unlink_local(&mut self, operand: StackOperand) {
let StackOperand::Local {
local_index,
prev_local,
next_local,
..
} = operand
else {
return;
};
self.unlink_local(local_index, prev_local, next_local);
}
fn unlink_local(
&mut self,
local_index: LocalIdx,
prev_local: Option<OperandIdx>,
next_local: Option<OperandIdx>,
) {
if let Some(prev_local) = prev_local {
self.update_next_local(prev_local, next_local);
} else {
self.local_heads.replace_first(local_index, next_local);
}
if let Some(next_local) = next_local {
self.update_prev_local(next_local, prev_local);
}
self.len_locals -= 1;
}
fn update_prev_local(&mut self, local_index: OperandIdx, prev_index: Option<OperandIdx>) {
match self.operands.get_mut(usize::from(local_index)) {
Some(StackOperand::Local { prev_local, .. }) => {
*prev_local = prev_index;
}
entry => panic!("expected `StackOperand::Local` but found: {entry:?}"),
}
}
fn update_next_local(&mut self, local_index: OperandIdx, next_index: Option<OperandIdx>) {
match self.operands.get_mut(usize::from(local_index)) {
Some(StackOperand::Local { next_local, .. }) => {
*next_local = next_index;
}
entry => panic!("expected `StackOperand::Local` but found: {entry:?}"),
}
}
}
#[derive(Debug)]
pub struct PreservedAllLocalsIter<'stack> {
operands: &'stack mut OperandStack,
index: usize,
}
impl PreservedAllLocalsIter<'_> {
fn has_remaining_locals(&self) -> bool {
self.operands.len_locals != 0
}
fn find_next_local(&mut self) -> Option<usize> {
let mut index = self.index;
loop {
index -= 1;
let opd = self.operands.operands.get(index)?;
if let StackOperand::Local { .. } = opd {
return Some(index);
}
}
}
}
impl Iterator for PreservedAllLocalsIter<'_> {
type Item = Operand;
fn next(&mut self) -> Option<Self::Item> {
if !self.has_remaining_locals() {
return None;
}
self.index = self.find_next_local()?;
let index = OperandIdx::from(self.index);
let operand = self.operands.operand_to_temp_at(index);
debug_assert!(matches!(operand, StackOperand::Local { .. }));
Some(Operand::new(index, operand))
}
}
#[derive(Debug)]
pub struct PreservedLocalsIter<'stack> {
operands: &'stack mut OperandStack,
index: Option<OperandIdx>,
}
impl Iterator for PreservedLocalsIter<'_> {
type Item = OperandIdx;
fn next(&mut self) -> Option<Self::Item> {
let index = self.index?;
let operand = self.operands.operand_to_temp_at(index);
self.index = match operand {
StackOperand::Local { next_local, .. } => next_local,
op => panic!("expected `StackOperand::Local` but found: {op:?}"),
};
Some(index)
}
}
#[derive(Debug)]
pub struct PeekedOperands<'stack> {
index: usize,
operands: slice::Iter<'stack, StackOperand>,
}
impl<'stack> PeekedOperands<'stack> {
pub fn empty() -> Self {
Self {
index: 0,
operands: [].iter(),
}
}
}
impl Iterator for PeekedOperands<'_> {
type Item = Operand;
fn next(&mut self) -> Option<Self::Item> {
let operand = self.operands.next().copied()?;
let index = OperandIdx::from(self.index);
self.index += 1;
Some(Operand::new(index, operand))
}
}
impl ExactSizeIterator for PeekedOperands<'_> {
fn len(&self) -> usize {
self.operands.len()
}
}