use crate::{codegen::CodeGenError, isa::reg::Reg, masm::StackSlot};
use anyhow::{Result, anyhow};
use smallvec::SmallVec;
use wasmparser::{Ieee32, Ieee64};
use wasmtime_environ::WasmValType;
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
pub struct TypedReg {
pub reg: Reg,
pub ty: WasmValType,
}
impl TypedReg {
pub fn new(ty: WasmValType, reg: Reg) -> Self {
Self { ty, reg }
}
pub fn i64(reg: Reg) -> Self {
Self {
ty: WasmValType::I64,
reg,
}
}
pub fn i32(reg: Reg) -> Self {
Self {
ty: WasmValType::I32,
reg,
}
}
pub fn f64(reg: Reg) -> Self {
Self {
ty: WasmValType::F64,
reg,
}
}
pub fn f32(reg: Reg) -> Self {
Self {
ty: WasmValType::F32,
reg,
}
}
pub fn v128(reg: Reg) -> Self {
Self {
ty: WasmValType::V128,
reg,
}
}
}
impl From<TypedReg> for Reg {
fn from(tr: TypedReg) -> Self {
tr.reg
}
}
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
pub struct Local {
pub index: u32,
pub ty: WasmValType,
}
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
pub struct Memory {
pub ty: WasmValType,
pub slot: StackSlot,
}
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
pub(crate) enum Val {
I32(i32),
I64(i64),
F32(Ieee32),
F64(Ieee64),
V128(i128),
Reg(TypedReg),
Local(Local),
Memory(Memory),
}
impl From<TypedReg> for Val {
fn from(tr: TypedReg) -> Self {
Val::Reg(tr)
}
}
impl From<Local> for Val {
fn from(local: Local) -> Self {
Val::Local(local)
}
}
impl From<Memory> for Val {
fn from(mem: Memory) -> Self {
Val::Memory(mem)
}
}
impl TryFrom<u32> for Val {
type Error = anyhow::Error;
fn try_from(value: u32) -> Result<Self, Self::Error> {
i32::try_from(value).map(Val::i32).map_err(Into::into)
}
}
impl Val {
pub fn i32(v: i32) -> Self {
Self::I32(v)
}
pub fn i64(v: i64) -> Self {
Self::I64(v)
}
pub fn f32(v: Ieee32) -> Self {
Self::F32(v)
}
pub fn f64(v: Ieee64) -> Self {
Self::F64(v)
}
pub fn v128(v: i128) -> Self {
Self::V128(v)
}
pub fn reg(reg: Reg, ty: WasmValType) -> Self {
Self::Reg(TypedReg { reg, ty })
}
pub fn local(index: u32, ty: WasmValType) -> Self {
Self::Local(Local { index, ty })
}
pub fn mem(ty: WasmValType, slot: StackSlot) -> Self {
Self::Memory(Memory { ty, slot })
}
pub fn is_reg(&self) -> bool {
match *self {
Self::Reg(_) => true,
_ => false,
}
}
pub fn is_mem(&self) -> bool {
match *self {
Self::Memory(_) => true,
_ => false,
}
}
pub fn is_const(&self) -> bool {
match *self {
Val::I32(_) | Val::I64(_) | Val::F32(_) | Val::F64(_) | Val::V128(_) => true,
_ => false,
}
}
pub fn is_local_at_index(&self, index: u32) -> bool {
match *self {
Self::Local(Local { index: i, .. }) if i == index => true,
_ => false,
}
}
pub fn unwrap_reg(&self) -> TypedReg {
match self {
Self::Reg(tr) => *tr,
v => panic!("expected value {v:?} to be a register"),
}
}
pub fn unwrap_i32(&self) -> i32 {
match self {
Self::I32(v) => *v,
v => panic!("expected value {v:?} to be i32"),
}
}
pub fn unwrap_i64(&self) -> i64 {
match self {
Self::I64(v) => *v,
v => panic!("expected value {v:?} to be i64"),
}
}
pub fn unwrap_f32(&self) -> Ieee32 {
match self {
Self::F32(v) => *v,
v => panic!("expected value {v:?} to be f32"),
}
}
pub fn unwrap_f64(&self) -> Ieee64 {
match self {
Self::F64(v) => *v,
v => panic!("expected value {v:?} to be f64"),
}
}
pub fn unwrap_mem(&self) -> Memory {
match self {
Self::Memory(m) => *m,
v => panic!("expected value {v:?} to be a Memory"),
}
}
pub fn is_i32_const(&self) -> bool {
match *self {
Self::I32(_) => true,
_ => false,
}
}
pub fn is_i64_const(&self) -> bool {
match *self {
Self::I64(_) => true,
_ => false,
}
}
pub fn is_f32_const(&self) -> bool {
match *self {
Self::F32(_) => true,
_ => false,
}
}
pub fn is_f64_const(&self) -> bool {
match *self {
Self::F64(_) => true,
_ => false,
}
}
pub fn ty(&self) -> WasmValType {
match self {
Val::I32(_) => WasmValType::I32,
Val::I64(_) => WasmValType::I64,
Val::F32(_) => WasmValType::F32,
Val::F64(_) => WasmValType::F64,
Val::V128(_) => WasmValType::V128,
Val::Reg(r) => r.ty,
Val::Memory(m) => m.ty,
Val::Local(l) => l.ty,
}
}
}
#[derive(Default, Debug)]
pub(crate) struct Stack {
inner: SmallVec<[Val; 64]>,
}
impl Stack {
pub fn new() -> Self {
Self {
inner: Default::default(),
}
}
pub fn ensure_index_at(&self, n: usize) -> Result<usize> {
if self.len() >= n {
Ok(self.len() - n)
} else {
Err(anyhow!(CodeGenError::missing_values_in_stack()))
}
}
pub fn contains_latent_local(&self, index: u32) -> bool {
self.inner
.iter()
.rev()
.skip(1)
.take_while(|v| !v.is_mem())
.any(|v| v.is_local_at_index(index))
}
pub fn extend(&mut self, values: impl IntoIterator<Item = Val>) {
self.inner.extend(values);
}
pub fn insert_many(&mut self, at: usize, values: &[Val]) {
debug_assert!(at <= self.len());
if at == self.len() {
self.inner.extend_from_slice(values);
} else {
self.inner.insert_from_slice(at, values);
}
}
pub fn len(&self) -> usize {
self.inner.len()
}
pub fn push(&mut self, val: Val) {
self.inner.push(val);
}
pub fn peek(&self) -> Option<&Val> {
self.inner.last()
}
pub fn peekn(&self, n: usize) -> impl Iterator<Item = &Val> + '_ {
let len = self.len();
assert!(n <= len);
let partition = len - n;
self.inner[partition..].into_iter()
}
pub fn pop(&mut self) -> Option<Val> {
self.inner.pop()
}
pub fn pop_i32_const(&mut self) -> Option<i32> {
match self.peek() {
Some(v) => v.is_i32_const().then(|| self.pop().unwrap().unwrap_i32()),
_ => None,
}
}
pub fn pop_i64_const(&mut self) -> Option<i64> {
match self.peek() {
Some(v) => v.is_i64_const().then(|| self.pop().unwrap().unwrap_i64()),
_ => None,
}
}
pub fn pop_f32_const(&mut self) -> Option<Ieee32> {
match self.peek() {
Some(v) => v.is_f32_const().then(|| self.pop().unwrap().unwrap_f32()),
_ => None,
}
}
pub fn pop_f64_const(&mut self) -> Option<Ieee64> {
match self.peek() {
Some(v) => v.is_f64_const().then(|| self.pop().unwrap().unwrap_f64()),
_ => None,
}
}
pub fn pop_reg(&mut self) -> Option<TypedReg> {
match self.peek() {
Some(v) => v.is_reg().then(|| self.pop().unwrap().unwrap_reg()),
_ => None,
}
}
pub fn pop_named_reg(&mut self, reg: Reg) -> Option<TypedReg> {
match self.peek() {
Some(v) => {
(v.is_reg() && v.unwrap_reg().reg == reg).then(|| self.pop().unwrap().unwrap_reg())
}
_ => None,
}
}
pub fn inner_mut(&mut self) -> &mut SmallVec<[Val; 64]> {
&mut self.inner
}
pub fn inner(&self) -> &SmallVec<[Val; 64]> {
&self.inner
}
pub fn sizeof(&self, top: usize) -> u32 {
self.peekn(top).fold(0, |acc, v| {
if v.is_mem() {
acc + v.unwrap_mem().slot.size
} else {
acc
}
})
}
}
#[cfg(test)]
mod tests {
use super::{Stack, Val};
use crate::isa::reg::Reg;
use wasmtime_environ::WasmValType;
#[test]
fn test_pop_i32_const() {
let mut stack = Stack::new();
stack.push(Val::i32(33i32));
assert_eq!(33, stack.pop_i32_const().unwrap());
stack.push(Val::local(10, WasmValType::I32));
assert!(stack.pop_i32_const().is_none());
}
#[test]
fn test_pop_reg() {
let mut stack = Stack::new();
let reg = Reg::int(2usize);
stack.push(Val::reg(reg, WasmValType::I32));
stack.push(Val::i32(4));
assert_eq!(None, stack.pop_reg());
let _ = stack.pop().unwrap();
assert_eq!(reg, stack.pop_reg().unwrap().reg);
}
#[test]
fn test_pop_named_reg() {
let mut stack = Stack::new();
let reg = Reg::int(2usize);
stack.push(Val::reg(reg, WasmValType::I32));
stack.push(Val::reg(Reg::int(4), WasmValType::I32));
assert_eq!(None, stack.pop_named_reg(reg));
let _ = stack.pop().unwrap();
assert_eq!(reg, stack.pop_named_reg(reg).unwrap().reg);
}
}