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(StructLayout),
Array(ArrayLayout),
}
#[derive(Clone, Debug)]
pub(crate) struct StructLayout {
fields: Box<[Slot]>,
size: usize,
}
#[derive(Copy, Clone, Debug)]
pub(crate) struct ArrayLayout {
elem: Slot,
stride: usize,
}
impl Layout {
pub(crate) fn from_body(body: &CompositeBody) -> Option<Layout> {
match body {
CompositeBody::Func { .. } => None,
CompositeBody::Struct(fields) => Some(Layout::Struct(StructLayout::of(
fields.iter().map(|f| |offset| field_slot(f, offset)),
))),
CompositeBody::Array(f) => Some(Layout::Array(ArrayLayout::of(field_slot(f, 0)))),
}
}
pub(crate) fn for_struct(fields: &[crate::value::FieldType]) -> StructLayout {
StructLayout::of(fields.iter().map(|f| |offset| pub_field_slot(f, offset)))
}
pub(crate) fn for_array(field: &crate::value::FieldType) -> ArrayLayout {
ArrayLayout::of(pub_field_slot(field, 0))
}
pub(crate) fn as_struct(&self) -> Option<&StructLayout> {
match self {
Layout::Struct(layout) => Some(layout),
Layout::Array(_) => None,
}
}
pub(crate) fn as_array(&self) -> Option<ArrayLayout> {
match self {
Layout::Array(layout) => Some(*layout),
Layout::Struct(_) => None,
}
}
}
impl StructLayout {
fn of<F: FnOnce(usize) -> Slot>(slot_at: impl Iterator<Item = F>) -> StructLayout {
let mut size = 0;
let fields = slot_at
.map(|slot_at| {
let slot = slot_at(size);
size += slot.width();
slot
})
.collect();
StructLayout { fields, size }
}
pub(crate) fn fields(&self) -> &[Slot] {
&self.fields
}
pub(crate) fn field(&self, i: usize) -> Option<Slot> {
self.fields.get(i).copied()
}
pub(crate) fn size(&self) -> usize {
self.size
}
}
impl ArrayLayout {
fn of(elem: Slot) -> ArrayLayout {
ArrayLayout {
elem,
stride: elem.width(),
}
}
pub(crate) fn elem_at(self, i: usize) -> Slot {
with_offset(self.elem, i * self.stride)
}
pub(crate) fn stride(self) -> usize {
self.stride
}
pub(crate) fn body_size(self, len: usize) -> usize {
len * self.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(v) => val_slot(v, offset),
}
}
fn val_slot(v: &IrVal, offset: usize) -> Slot {
let kind = match v {
IrVal::I32 => ScalarKind::I32,
IrVal::I64 => ScalarKind::I64,
IrVal::F32 => ScalarKind::F32,
IrVal::F64 => ScalarKind::F64,
IrVal::V128 => ScalarKind::V128,
IrVal::Ref { heap, .. } => {
return Slot::Ref {
offset,
kind: ref_kind(heap),
}
}
};
Slot::Scalar { offset, kind }
}
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;
match f.element_type() {
StorageType::I8 => Slot::Scalar {
offset,
kind: ScalarKind::I8,
},
StorageType::I16 => Slot::Scalar {
offset,
kind: ScalarKind::I16,
},
StorageType::ValType(v) => pub_val_slot(v, offset),
}
}
fn pub_val_slot(v: &crate::value::ValType, offset: usize) -> Slot {
use crate::value::ValType;
let kind = match v {
ValType::I32 => ScalarKind::I32,
ValType::I64 => ScalarKind::I64,
ValType::F32 => ScalarKind::F32,
ValType::F64 => ScalarKind::F64,
ValType::V128 => ScalarKind::V128,
ValType::Ref(rt) => {
return Slot::Ref {
offset,
kind: pub_ref_kind(rt.heap_type()),
}
}
};
Slot::Scalar { offset, kind }
}
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;