use super::{CompositeBody, IrField, IrHeap, IrStorage, IrVal};
pub(crate) const REF_WIDTH: usize = 4;
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub(crate) enum ScalarKind {
I8,
I16,
I32,
I64,
F32,
F64,
V128,
}
impl ScalarKind {
pub(crate) fn width(self) -> usize {
match self {
ScalarKind::I8 => 1,
ScalarKind::I16 => 2,
ScalarKind::I32 | ScalarKind::F32 => 4,
ScalarKind::I64 | ScalarKind::F64 => 8,
ScalarKind::V128 => 16,
}
}
pub(crate) fn is_packed(self) -> bool {
matches!(self, ScalarKind::I8 | ScalarKind::I16)
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub(crate) enum RefKind {
Func,
Extern,
Any,
Exn,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub(crate) enum Slot {
Scalar { offset: usize, kind: ScalarKind },
Ref { offset: usize, kind: RefKind },
}
impl Slot {
pub(crate) fn offset(self) -> usize {
match self {
Slot::Scalar { offset, .. } | Slot::Ref { offset, .. } => offset,
}
}
pub(crate) fn width(self) -> usize {
match self {
Slot::Scalar { kind, .. } => kind.width(),
Slot::Ref { .. } => REF_WIDTH,
}
}
}
#[derive(Clone, Debug)]
pub(crate) enum Layout {
Struct { fields: Box<[Slot]>, size: usize },
Array { elem: Slot, stride: usize },
}
impl Layout {
pub(crate) fn from_body(body: &CompositeBody) -> Option<Layout> {
match body {
CompositeBody::Func { .. } => None,
CompositeBody::Struct(fields) => {
let mut offset = 0;
let slots: Vec<Slot> = fields
.iter()
.map(|f| {
let slot = field_slot(f, offset);
offset += slot.width();
slot
})
.collect();
Some(Layout::Struct {
fields: slots.into_boxed_slice(),
size: offset,
})
}
CompositeBody::Array(f) => {
let elem = field_slot(f, 0);
Some(Layout::Array {
elem,
stride: elem.width(),
})
}
}
}
pub(crate) fn for_struct(fields: &[crate::value::FieldType]) -> Layout {
let mut offset = 0;
let slots: Vec<Slot> = fields
.iter()
.map(|f| {
let slot = pub_field_slot(f, offset);
offset += slot.width();
slot
})
.collect();
Layout::Struct {
fields: slots.into_boxed_slice(),
size: offset,
}
}
pub(crate) fn for_array(field: &crate::value::FieldType) -> Layout {
let elem = pub_field_slot(field, 0);
Layout::Array {
elem,
stride: elem.width(),
}
}
pub(crate) fn field(&self, i: usize) -> Slot {
match self {
Layout::Struct { fields, .. } => fields[i],
Layout::Array { .. } => unreachable!("field() on an array layout"),
}
}
pub(crate) fn get_field(&self, i: usize) -> Option<Slot> {
match self {
Layout::Struct { fields, .. } => fields.get(i).copied(),
Layout::Array { .. } => None,
}
}
pub(crate) fn elem_at(&self, i: usize) -> Slot {
match self {
Layout::Array { elem, stride } => with_offset(*elem, i * stride),
Layout::Struct { .. } => unreachable!("elem_at() on a struct layout"),
}
}
pub(crate) fn stride(&self) -> usize {
match self {
Layout::Array { stride, .. } => *stride,
Layout::Struct { .. } => unreachable!("stride() on a struct layout"),
}
}
pub(crate) fn body_size(&self, len: usize) -> usize {
match self {
Layout::Struct { size, .. } => *size,
Layout::Array { stride, .. } => len * stride,
}
}
}
fn field_slot(f: &IrField, offset: usize) -> Slot {
match &f.storage {
IrStorage::I8 => Slot::Scalar {
offset,
kind: ScalarKind::I8,
},
IrStorage::I16 => Slot::Scalar {
offset,
kind: ScalarKind::I16,
},
IrStorage::Val(IrVal::Ref { heap, .. }) => Slot::Ref {
offset,
kind: ref_kind(heap),
},
IrStorage::Val(v) => Slot::Scalar {
offset,
kind: num_kind(v),
},
}
}
fn num_kind(v: &IrVal) -> ScalarKind {
match v {
IrVal::I32 => ScalarKind::I32,
IrVal::I64 => ScalarKind::I64,
IrVal::F32 => ScalarKind::F32,
IrVal::F64 => ScalarKind::F64,
IrVal::V128 => ScalarKind::V128,
IrVal::Ref { .. } => unreachable!("ref handled as a Ref slot"),
}
}
fn ref_kind(heap: &IrHeap) -> RefKind {
use super::AggKind;
match heap {
IrHeap::Func | IrHeap::NoFunc | IrHeap::Concrete(_, AggKind::Func) => RefKind::Func,
IrHeap::Extern | IrHeap::NoExtern => RefKind::Extern,
IrHeap::Exn | IrHeap::NoExn => RefKind::Exn,
_ => RefKind::Any,
}
}
fn pub_field_slot(f: &crate::value::FieldType, offset: usize) -> Slot {
use crate::value::{StorageType, ValType};
match f.element_type() {
StorageType::I8 => Slot::Scalar {
offset,
kind: ScalarKind::I8,
},
StorageType::I16 => Slot::Scalar {
offset,
kind: ScalarKind::I16,
},
StorageType::ValType(ValType::Ref(rt)) => Slot::Ref {
offset,
kind: pub_ref_kind(rt.heap_type()),
},
StorageType::ValType(v) => Slot::Scalar {
offset,
kind: pub_num_kind(v),
},
}
}
fn pub_num_kind(v: &crate::value::ValType) -> ScalarKind {
use crate::value::ValType;
match v {
ValType::I32 => ScalarKind::I32,
ValType::I64 => ScalarKind::I64,
ValType::F32 => ScalarKind::F32,
ValType::F64 => ScalarKind::F64,
ValType::V128 => ScalarKind::V128,
ValType::Ref(_) => unreachable!("ref handled as a Ref slot"),
}
}
fn pub_ref_kind(heap: &crate::value::HeapType) -> RefKind {
use crate::value::HeapType as H;
match heap {
H::Func | H::NoFunc | H::ConcreteFunc(_) => RefKind::Func,
H::Extern | H::NoExtern => RefKind::Extern,
H::Exn | H::NoExn => RefKind::Exn,
_ => RefKind::Any,
}
}
fn with_offset(slot: Slot, offset: usize) -> Slot {
match slot {
Slot::Scalar { kind, .. } => Slot::Scalar { offset, kind },
Slot::Ref { kind, .. } => Slot::Ref { offset, kind },
}
}
#[cfg(test)]
#[path = "layout_tests.rs"]
mod tests;