1 use crate::ast::*;
2 use std::collections::HashMap;
3
4 #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
5 pub struct SizeAlign {
6 pub size: usize,
7 pub align: usize,
8 }
9
10 pub trait Layout {
mem_size_align(&self) -> SizeAlign11 fn mem_size_align(&self) -> SizeAlign;
mem_size(&self) -> usize12 fn mem_size(&self) -> usize {
13 self.mem_size_align().size
14 }
mem_align(&self) -> usize15 fn mem_align(&self) -> usize {
16 self.mem_size_align().align
17 }
18 }
19
20 impl TypeRef {
layout(&self, cache: &mut HashMap<TypeRef, SizeAlign>) -> SizeAlign21 fn layout(&self, cache: &mut HashMap<TypeRef, SizeAlign>) -> SizeAlign {
22 if let Some(hit) = cache.get(self) {
23 return *hit;
24 }
25 let layout = match &self {
26 TypeRef::Name(nt) => nt.layout(cache),
27 TypeRef::Value(v) => v.layout(cache),
28 };
29 cache.insert(self.clone(), layout);
30 layout
31 }
32 }
33
34 impl Layout for TypeRef {
mem_size_align(&self) -> SizeAlign35 fn mem_size_align(&self) -> SizeAlign {
36 let mut cache = HashMap::new();
37 self.layout(&mut cache)
38 }
39 }
40
41 impl NamedType {
layout(&self, cache: &mut HashMap<TypeRef, SizeAlign>) -> SizeAlign42 fn layout(&self, cache: &mut HashMap<TypeRef, SizeAlign>) -> SizeAlign {
43 self.tref.layout(cache)
44 }
45 }
46 impl Layout for NamedType {
mem_size_align(&self) -> SizeAlign47 fn mem_size_align(&self) -> SizeAlign {
48 let mut cache = HashMap::new();
49 self.layout(&mut cache)
50 }
51 }
52
53 impl Type {
layout(&self, cache: &mut HashMap<TypeRef, SizeAlign>) -> SizeAlign54 fn layout(&self, cache: &mut HashMap<TypeRef, SizeAlign>) -> SizeAlign {
55 match &self {
56 Type::Enum(e) => e.repr.mem_size_align(),
57 Type::Int(i) => i.repr.mem_size_align(),
58 Type::Flags(f) => f.repr.mem_size_align(),
59 Type::Struct(s) => s.layout(cache),
60 Type::Union(u) => u.layout(cache),
61 Type::Handle(h) => h.mem_size_align(),
62 Type::Array { .. } => BuiltinType::String.mem_size_align(),
63 Type::Pointer { .. } | Type::ConstPointer { .. } => BuiltinType::U32.mem_size_align(),
64 Type::Builtin(b) => b.mem_size_align(),
65 }
66 }
67 }
68
69 impl Layout for Type {
mem_size_align(&self) -> SizeAlign70 fn mem_size_align(&self) -> SizeAlign {
71 let mut cache = HashMap::new();
72 self.layout(&mut cache)
73 }
74 }
75
76 impl Layout for IntRepr {
mem_size_align(&self) -> SizeAlign77 fn mem_size_align(&self) -> SizeAlign {
78 match self {
79 IntRepr::U8 => BuiltinType::U8.mem_size_align(),
80 IntRepr::U16 => BuiltinType::U16.mem_size_align(),
81 IntRepr::U32 => BuiltinType::U32.mem_size_align(),
82 IntRepr::U64 => BuiltinType::U64.mem_size_align(),
83 }
84 }
85 }
86
87 pub struct StructMemberLayout<'a> {
88 pub member: &'a StructMember,
89 pub offset: usize,
90 }
91
92 impl StructDatatype {
member_layout(&self) -> Vec<StructMemberLayout>93 pub fn member_layout(&self) -> Vec<StructMemberLayout> {
94 self.member_layout_(&mut HashMap::new())
95 }
96
member_layout_(&self, cache: &mut HashMap<TypeRef, SizeAlign>) -> Vec<StructMemberLayout>97 fn member_layout_(&self, cache: &mut HashMap<TypeRef, SizeAlign>) -> Vec<StructMemberLayout> {
98 let mut members = Vec::new();
99 let mut offset = 0;
100 for m in self.members.iter() {
101 let sa = m.tref.layout(cache);
102 offset = align_to(offset, sa.align);
103 members.push(StructMemberLayout { member: m, offset });
104 offset += sa.size;
105 }
106 members
107 }
108
layout(&self, cache: &mut HashMap<TypeRef, SizeAlign>) -> SizeAlign109 fn layout(&self, cache: &mut HashMap<TypeRef, SizeAlign>) -> SizeAlign {
110 let members = self.member_layout_(cache);
111 let align = members
112 .iter()
113 .map(|m| m.member.tref.layout(cache).align)
114 .max()
115 .expect("nonzero struct members");
116 let last = members.last().expect("nonzero struct members");
117 let size = last.offset + last.member.tref.layout(cache).size;
118 let size = align_to(size, align);
119 SizeAlign { size, align }
120 }
121 }
122
123 impl Layout for StructDatatype {
mem_size_align(&self) -> SizeAlign124 fn mem_size_align(&self) -> SizeAlign {
125 let mut cache = HashMap::new();
126 self.layout(&mut cache)
127 }
128 }
129
130 /// If the next free byte in the struct is `offs`, and the next
131 /// element has alignment `alignment`, determine the offset at
132 /// which to place that element.
align_to(offs: usize, alignment: usize) -> usize133 fn align_to(offs: usize, alignment: usize) -> usize {
134 offs + alignment - 1 - ((offs + alignment - 1) % alignment)
135 }
136
137 #[cfg(test)]
138 mod test {
139 use super::align_to;
140 #[test]
align()141 fn align() {
142 assert_eq!(0, align_to(0, 1));
143 assert_eq!(0, align_to(0, 2));
144 assert_eq!(0, align_to(0, 4));
145 assert_eq!(0, align_to(0, 8));
146
147 assert_eq!(1, align_to(1, 1));
148 assert_eq!(2, align_to(1, 2));
149 assert_eq!(4, align_to(1, 4));
150 assert_eq!(8, align_to(1, 8));
151
152 assert_eq!(2, align_to(2, 1));
153 assert_eq!(2, align_to(2, 2));
154 assert_eq!(4, align_to(2, 4));
155 assert_eq!(8, align_to(2, 8));
156
157 assert_eq!(5, align_to(5, 1));
158 assert_eq!(6, align_to(5, 2));
159 assert_eq!(8, align_to(5, 4));
160 assert_eq!(8, align_to(5, 8));
161 }
162 }
163
164 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
165 pub struct UnionLayout {
166 pub tag_size: usize,
167 pub tag_align: usize,
168 pub contents_offset: usize,
169 pub contents_size: usize,
170 pub contents_align: usize,
171 }
172
173 impl Layout for UnionLayout {
mem_size_align(&self) -> SizeAlign174 fn mem_size_align(&self) -> SizeAlign {
175 let align = std::cmp::max(self.tag_align, self.contents_align);
176 let size = align_to(self.contents_offset + self.contents_size, align);
177 SizeAlign { size, align }
178 }
179 }
180
181 impl UnionDatatype {
union_layout(&self) -> UnionLayout182 pub fn union_layout(&self) -> UnionLayout {
183 let mut cache = HashMap::new();
184 self.union_layout_(&mut cache)
185 }
union_layout_(&self, cache: &mut HashMap<TypeRef, SizeAlign>) -> UnionLayout186 fn union_layout_(&self, cache: &mut HashMap<TypeRef, SizeAlign>) -> UnionLayout {
187 let tag = self.tag.layout(cache);
188
189 let variant_sas = self
190 .variants
191 .iter()
192 .filter_map(|v| v.tref.as_ref().map(|t| t.layout(cache)))
193 .collect::<Vec<SizeAlign>>();
194
195 let contents_size = variant_sas.iter().map(|sa| sa.size).max().unwrap_or(0);
196 let contents_align = variant_sas.iter().map(|sa| sa.align).max().unwrap_or(1);
197
198 UnionLayout {
199 tag_size: tag.size,
200 tag_align: tag.align,
201 contents_offset: align_to(tag.size, contents_align),
202 contents_size,
203 contents_align,
204 }
205 }
206
layout(&self, cache: &mut HashMap<TypeRef, SizeAlign>) -> SizeAlign207 fn layout(&self, cache: &mut HashMap<TypeRef, SizeAlign>) -> SizeAlign {
208 self.union_layout_(cache).mem_size_align()
209 }
210 }
211
212 impl Layout for UnionDatatype {
mem_size_align(&self) -> SizeAlign213 fn mem_size_align(&self) -> SizeAlign {
214 let mut cache = HashMap::new();
215 self.layout(&mut cache)
216 }
217 }
218
219 impl Layout for HandleDatatype {
mem_size_align(&self) -> SizeAlign220 fn mem_size_align(&self) -> SizeAlign {
221 BuiltinType::U32.mem_size_align()
222 }
223 }
224
225 impl Layout for BuiltinType {
mem_size_align(&self) -> SizeAlign226 fn mem_size_align(&self) -> SizeAlign {
227 match self {
228 BuiltinType::String => SizeAlign { size: 8, align: 4 }, // Pointer and Length
229 BuiltinType::U8 | BuiltinType::S8 | BuiltinType::Char8 => {
230 SizeAlign { size: 1, align: 1 }
231 }
232 BuiltinType::U16 | BuiltinType::S16 => SizeAlign { size: 2, align: 2 },
233 BuiltinType::USize | BuiltinType::U32 | BuiltinType::S32 | BuiltinType::F32 => {
234 SizeAlign { size: 4, align: 4 }
235 }
236 BuiltinType::U64 | BuiltinType::S64 | BuiltinType::F64 => {
237 SizeAlign { size: 8, align: 8 }
238 }
239 }
240 }
241 }
242