use super::Translator;
use crate::error::InternalError;
use crate::module::op::{Op, Slot};
const MAX_LOCAL_ENTRIES: usize = 16;
impl Translator<'_> {
pub(super) fn push(&mut self, n: u32) {
let Some(height) = self.height.checked_add(n) else {
self.error
.get_or_insert(InternalError::Compiler("operand height overflow"));
return;
};
for h in self.height..height {
let slot = self.slot_of(h);
self.stack.push(slot);
}
self.height = height;
self.max_operands = self.max_operands.max(height);
}
pub(super) fn produce(&mut self, n: u32, dst: Slot) {
self.pop(n);
self.push_known(dst);
}
#[inline]
fn push_known(&mut self, slot: Slot) {
let Some(height) = self.height.checked_add(1) else {
self.error
.get_or_insert(InternalError::Compiler("operand height overflow"));
return;
};
self.stack.push(slot);
self.height = height;
self.max_operands = self.max_operands.max(height);
}
#[inline]
pub(super) fn push_local(&mut self, local: u32) {
let height = self.height;
if self.local_heights.len() < MAX_LOCAL_ENTRIES {
self.local_heights.push(height);
self.push_known(local);
} else {
self.push(1);
let own = self.slot_of(height);
self.emit(Op::Copy(own, local));
}
}
#[inline]
pub(super) fn pop(&mut self, n: u32) {
let Some(height) = self.height.checked_sub(n) else {
self.error
.get_or_insert(InternalError::Compiler("operand height underflow"));
return;
};
self.truncate_stack(height);
}
pub(super) fn reset_height(&mut self, base: u32, n: u32) {
self.truncate_stack(base);
self.push(n);
}
pub(super) fn reset_after_label(&mut self, base: u32, n: u32) {
let n = if self.reachable { n } else { 0 };
self.reset_height(base, n);
}
pub(super) fn block_base(&mut self, params: u32) -> u32 {
if self.reachable {
self.height_below(params)
} else {
self.height.saturating_sub(params)
}
}
#[inline]
fn truncate_stack(&mut self, height: u32) {
if height > self.height {
self.pad_stack(height);
return;
}
while self.local_heights.last().is_some_and(|&h| h >= height) {
self.local_heights.pop();
}
self.stack.truncate(height as usize);
self.height = height;
}
#[cold]
fn pad_stack(&mut self, height: u32) {
self.push(height.saturating_sub(self.height));
}
pub(super) fn operand(&mut self, depth: u32) -> Slot {
let index = self.stack.len().checked_sub(depth as usize + 1);
if let Some(&slot) = index.and_then(|i| self.stack.get(i)) {
return slot;
}
self.error
.get_or_insert(InternalError::Compiler("operand stack underflow"));
0
}
pub(super) fn materialize(&mut self, n: u32) {
let from = self.height_below(n);
while let Some(&h) = self.local_heights.last().filter(|&&h| h >= from) {
self.local_heights.pop();
self.materialize_at(h);
}
}
pub(super) fn materialize_locals(&mut self) {
self.materialize(self.height);
}
pub(super) fn preserve(&mut self, local: u32) {
let mut kept = 0;
for i in 0..self.local_heights.len() {
let Some(&h) = self.local_heights.get(i) else {
break;
};
if self.stack.get(h as usize) == Some(&local) {
self.materialize_at(h);
} else if let Some(slot) = self.local_heights.get_mut(kept) {
*slot = h;
kept += 1;
}
}
self.local_heights.truncate(kept);
}
fn reads_at(&self, i: usize) -> Option<u32> {
let h = *self.local_heights.get(i)?;
self.stack.get(h as usize).copied()
}
pub(super) fn reads_local(&self, local: u32) -> bool {
(0..self.local_heights.len()).any(|i| self.reads_at(i) == Some(local))
}
fn materialize_at(&mut self, h: u32) {
let own = self.slot_of(h);
let Some(entry) = self.stack.get_mut(h as usize) else {
return;
};
let src = std::mem::replace(entry, own);
self.emit(Op::Copy(own, src));
}
pub(super) fn push_entry(&mut self, slot: Slot) {
if slot < self.n_locals {
self.push_local(slot);
} else {
self.push(1);
}
}
pub(super) fn slot_of(&mut self, height: u32) -> Slot {
self.n_locals.checked_add(height).unwrap_or_else(|| {
self.error
.get_or_insert(InternalError::Compiler("operand slot overflow"));
0
})
}
}