witnext2/
sizealign.rs

1use crate::abi::Direction;
2use crate::{Int, Interface, Record, RecordKind, Type, TypeDef, TypeDefKind, Variant};
3
4#[derive(Default)]
5pub struct SizeAlign {
6    map: Vec<(usize, usize)>,
7}
8
9impl SizeAlign {
10    pub fn fill(&mut self, dir: Direction, iface: &Interface) {
11        self.map = vec![(0, 0); iface.types.len()];
12        for ty in iface.topological_types() {
13            let pair = self.calculate(dir, &iface.types[ty]);
14            self.map[ty.index()] = pair;
15        }
16    }
17
18    fn calculate(&self, dir: Direction, ty: &TypeDef) -> (usize, usize) {
19        match &ty.kind {
20            TypeDefKind::Type(t) => (self.size(t), self.align(t)),
21            TypeDefKind::List(_) => (8, 4),
22            TypeDefKind::Pointer(_) | TypeDefKind::ConstPointer(_) => (4, 4),
23            TypeDefKind::PushBuffer(_) | TypeDefKind::PullBuffer(_) => match dir {
24                Direction::Import => (12, 4),
25                Direction::Export => (4, 4),
26            },
27            TypeDefKind::Record(r) => {
28                if let RecordKind::Flags(repr) = r.kind {
29                    return match repr {
30                        Some(i) => int_size_align(i),
31                        None if r.fields.len() <= 8 => (1, 1),
32                        None if r.fields.len() <= 16 => (2, 2),
33                        None if r.fields.len() <= 32 => (4, 4),
34                        None if r.fields.len() <= 64 => (8, 8),
35                        None => (r.num_i32s() * 4, 4),
36                    };
37                }
38                let mut size = 0;
39                let mut align = 1;
40                for f in r.fields.iter() {
41                    let field_size = self.size(&f.ty);
42                    let field_align = self.align(&f.ty);
43                    size = align_to(size, field_align) + field_size;
44                    align = align.max(field_align);
45                }
46                (align_to(size, align), align)
47            }
48            TypeDefKind::Variant(v) => {
49                let (discrim_size, discrim_align) = int_size_align(v.tag);
50                let mut size = discrim_size;
51                let mut align = discrim_align;
52                for c in v.cases.iter() {
53                    if let Some(ty) = &c.ty {
54                        let case_size = self.size(ty);
55                        let case_align = self.align(ty);
56                        align = align.max(case_align);
57                        size = size.max(align_to(discrim_size, case_align) + case_size);
58                    }
59                }
60                (size, align)
61            }
62        }
63    }
64
65    pub fn size(&self, ty: &Type) -> usize {
66        match ty {
67            Type::U8 | Type::S8 | Type::CChar => 1,
68            Type::U16 | Type::S16 => 2,
69            Type::U32 | Type::S32 | Type::F32 | Type::Char | Type::Handle(_) | Type::Usize => 4,
70            Type::U64 | Type::S64 | Type::F64 => 8,
71            Type::Id(id) => self.map[id.index()].0,
72        }
73    }
74
75    pub fn align(&self, ty: &Type) -> usize {
76        match ty {
77            Type::U8 | Type::S8 | Type::CChar => 1,
78            Type::U16 | Type::S16 => 2,
79            Type::U32 | Type::S32 | Type::F32 | Type::Char | Type::Handle(_) | Type::Usize => 4,
80            Type::U64 | Type::S64 | Type::F64 => 8,
81            Type::Id(id) => self.map[id.index()].1,
82        }
83    }
84
85    pub fn field_offsets(&self, record: &Record) -> Vec<usize> {
86        let mut cur = 0;
87        record
88            .fields
89            .iter()
90            .map(|field| {
91                let ret = align_to(cur, self.align(&field.ty));
92                cur = ret + self.size(&field.ty);
93                ret
94            })
95            .collect()
96    }
97
98    pub fn payload_offset(&self, variant: &Variant) -> usize {
99        let mut max_align = 1;
100        for c in variant.cases.iter() {
101            if let Some(ty) = &c.ty {
102                max_align = max_align.max(self.align(ty));
103            }
104        }
105        let tag_size = int_size_align(variant.tag).0;
106        align_to(tag_size, max_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
119fn align_to(val: usize, align: usize) -> usize {
120    (val + align - 1) & !(align - 1)
121}