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