wasmer_wit_parser/
sizealign.rs

1use crate::{FlagsRepr, Int, Interface, Type, TypeDef, TypeDefKind};
2
3#[derive(Default)]
4pub struct SizeAlign {
5    map: Vec<(usize, usize)>,
6}
7
8impl SizeAlign {
9    pub fn fill(&mut self, iface: &Interface) {
10        self.map = vec![(0, 0); iface.types.len()];
11        for ty in iface.topological_types() {
12            let pair = self.calculate(&iface.types[ty]);
13            self.map[ty.index()] = pair;
14        }
15    }
16
17    fn calculate(&self, ty: &TypeDef) -> (usize, usize) {
18        match &ty.kind {
19            TypeDefKind::Type(t) => (self.size(t), self.align(t)),
20            TypeDefKind::List(_) => (8, 4),
21            TypeDefKind::Record(r) => self.record(r.fields.iter().map(|f| &f.ty)),
22            TypeDefKind::Tuple(t) => self.record(t.types.iter()),
23            TypeDefKind::Flags(f) => match f.repr() {
24                FlagsRepr::U8 => (1, 1),
25                FlagsRepr::U16 => (2, 2),
26                FlagsRepr::U32(n) => (n * 4, 4),
27            },
28            TypeDefKind::Variant(v) => self.variant(v.tag(), v.cases.iter().map(|c| &c.ty)),
29            TypeDefKind::Enum(e) => self.variant(e.tag(), []),
30            TypeDefKind::Option(t) => self.variant(Int::U8, [&Type::Unit, t]),
31            TypeDefKind::Expected(e) => self.variant(Int::U8, [&e.ok, &e.err]),
32            TypeDefKind::Union(u) => self.variant(u.tag(), u.cases.iter().map(|c| &c.ty)),
33            // A future is represented as an index.
34            TypeDefKind::Future(_) => (4, 4),
35            // A stream is represented as an index.
36            TypeDefKind::Stream(_) => (4, 4),
37        }
38    }
39
40    pub fn size(&self, ty: &Type) -> usize {
41        match ty {
42            Type::Unit => 0,
43            Type::Bool | Type::U8 | Type::S8 => 1,
44            Type::U16 | Type::S16 => 2,
45            Type::U32 | Type::S32 | Type::Float32 | Type::Char | Type::Handle(_) => 4,
46            Type::U64 | Type::S64 | Type::Float64 | Type::String => 8,
47            Type::Id(id) => self.map[id.index()].0,
48        }
49    }
50
51    pub fn align(&self, ty: &Type) -> usize {
52        match ty {
53            Type::Unit | Type::Bool | Type::U8 | Type::S8 => 1,
54            Type::U16 | Type::S16 => 2,
55            Type::U32 | Type::S32 | Type::Float32 | Type::Char | Type::Handle(_) | Type::String => {
56                4
57            }
58            Type::U64 | Type::S64 | Type::Float64 => 8,
59            Type::Id(id) => self.map[id.index()].1,
60        }
61    }
62
63    pub fn field_offsets<'a>(&self, types: impl IntoIterator<Item = &'a Type>) -> Vec<usize> {
64        let mut cur = 0;
65        types
66            .into_iter()
67            .map(|ty| {
68                let ret = align_to(cur, self.align(ty));
69                cur = ret + self.size(ty);
70                ret
71            })
72            .collect()
73    }
74
75    pub fn payload_offset<'a>(&self, tag: Int, cases: impl IntoIterator<Item = &'a Type>) -> usize {
76        let mut max_align = 1;
77        for ty in cases {
78            max_align = max_align.max(self.align(ty));
79        }
80        let tag_size = int_size_align(tag).0;
81        align_to(tag_size, max_align)
82    }
83
84    pub fn record<'a>(&self, types: impl Iterator<Item = &'a Type>) -> (usize, usize) {
85        let mut size = 0;
86        let mut align = 1;
87        for ty in types {
88            let field_size = self.size(ty);
89            let field_align = self.align(ty);
90            size = align_to(size, field_align) + field_size;
91            align = align.max(field_align);
92        }
93        (align_to(size, align), align)
94    }
95
96    fn variant<'a>(&self, tag: Int, types: impl IntoIterator<Item = &'a Type>) -> (usize, usize) {
97        let (discrim_size, discrim_align) = int_size_align(tag);
98        let mut size = discrim_size;
99        let mut align = discrim_align;
100        for ty in types {
101            let case_size = self.size(ty);
102            let case_align = self.align(ty);
103            align = align.max(case_align);
104            size = size.max(align_to(discrim_size, case_align) + case_size);
105        }
106        (size, align)
107    }
108}
109
110fn int_size_align(i: Int) -> (usize, usize) {
111    match i {
112        Int::U8 => (1, 1),
113        Int::U16 => (2, 2),
114        Int::U32 => (4, 4),
115        Int::U64 => (8, 8),
116    }
117}
118
119pub(crate) fn align_to(val: usize, align: usize) -> usize {
120    (val + align - 1) & !(align - 1)
121}