/src/wasm-tools/crates/wit-parser/src/sizealign.rs
Line | Count | Source |
1 | | use alloc::format; |
2 | | use alloc::string::String; |
3 | | use alloc::vec::Vec; |
4 | | use core::{ |
5 | | cmp::Ordering, |
6 | | num::NonZeroUsize, |
7 | | ops::{Add, AddAssign}, |
8 | | }; |
9 | | |
10 | | use crate::{FlagsRepr, Int, Resolve, Type, TypeDef, TypeDefKind}; |
11 | | |
12 | | /// Architecture specific alignment |
13 | | #[derive(Eq, PartialEq, Clone, Copy)] |
14 | | pub enum Alignment { |
15 | | /// This represents 4 byte alignment on 32bit and 8 byte alignment on 64bit architectures |
16 | | Pointer, |
17 | | /// This alignment is architecture independent (derived from integer or float types) |
18 | | Bytes(NonZeroUsize), |
19 | | } |
20 | | |
21 | | impl Default for Alignment { |
22 | 476 | fn default() -> Self { |
23 | 476 | Alignment::Bytes(NonZeroUsize::new(1).unwrap()) |
24 | 476 | } |
25 | | } |
26 | | |
27 | | impl core::fmt::Debug for Alignment { |
28 | 0 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { |
29 | 0 | match self { |
30 | 0 | Alignment::Pointer => f.write_str("ptr"), |
31 | 0 | Alignment::Bytes(b) => f.write_fmt(format_args!("{}", b.get())), |
32 | | } |
33 | 0 | } |
34 | | } |
35 | | |
36 | | impl PartialOrd for Alignment { |
37 | 1.51k | fn partial_cmp(&self, other: &Self) -> Option<Ordering> { |
38 | 1.51k | Some(self.cmp(other)) |
39 | 1.51k | } |
40 | | } |
41 | | |
42 | | impl Ord for Alignment { |
43 | | /// Needed for determining the max alignment of an object from its parts. |
44 | | /// The ordering is: Bytes(1) < Bytes(2) < Bytes(4) < Pointer < Bytes(8) |
45 | | /// as a Pointer is either four or eight byte aligned, depending on the architecture |
46 | 1.51k | fn cmp(&self, other: &Self) -> Ordering { |
47 | 1.51k | match (self, other) { |
48 | 1 | (Alignment::Pointer, Alignment::Pointer) => Ordering::Equal, |
49 | 66 | (Alignment::Pointer, Alignment::Bytes(b)) => { |
50 | 66 | if b.get() > 4 { |
51 | 7 | Ordering::Less |
52 | | } else { |
53 | 59 | Ordering::Greater |
54 | | } |
55 | | } |
56 | 109 | (Alignment::Bytes(b), Alignment::Pointer) => { |
57 | 109 | if b.get() > 4 { |
58 | 2 | Ordering::Greater |
59 | | } else { |
60 | 107 | Ordering::Less |
61 | | } |
62 | | } |
63 | 1.34k | (Alignment::Bytes(a), Alignment::Bytes(b)) => a.cmp(b), |
64 | | } |
65 | 1.51k | } |
66 | | } |
67 | | |
68 | | impl Alignment { |
69 | | /// for easy migration this gives you the value for wasm32 |
70 | 600 | pub fn align_wasm32(&self) -> usize { |
71 | 600 | match self { |
72 | 105 | Alignment::Pointer => 4, |
73 | 495 | Alignment::Bytes(bytes) => bytes.get(), |
74 | | } |
75 | 600 | } |
76 | | |
77 | 600 | pub fn align_wasm64(&self) -> usize { |
78 | 600 | match self { |
79 | 105 | Alignment::Pointer => 8, |
80 | 495 | Alignment::Bytes(bytes) => bytes.get(), |
81 | | } |
82 | 600 | } |
83 | | |
84 | 0 | pub fn format(&self, ptrsize_expr: &str) -> String { |
85 | 0 | match self { |
86 | 0 | Alignment::Pointer => ptrsize_expr.into(), |
87 | 0 | Alignment::Bytes(bytes) => format!("{}", bytes.get()), |
88 | | } |
89 | 0 | } |
90 | | } |
91 | | |
92 | | /// Architecture specific measurement of position, |
93 | | /// the combined amount in bytes is |
94 | | /// `bytes + pointers * core::mem::size_of::<*const u8>()` |
95 | | #[derive(Default, Clone, Copy, Eq, PartialEq)] |
96 | | pub struct ArchitectureSize { |
97 | | /// architecture independent bytes |
98 | | pub bytes: usize, |
99 | | /// amount of pointer sized units to add |
100 | | pub pointers: usize, |
101 | | } |
102 | | |
103 | | impl Add<ArchitectureSize> for ArchitectureSize { |
104 | | type Output = ArchitectureSize; |
105 | | |
106 | 2.23k | fn add(self, rhs: ArchitectureSize) -> Self::Output { |
107 | 2.23k | ArchitectureSize::new(self.bytes + rhs.bytes, self.pointers + rhs.pointers) |
108 | 2.23k | } |
109 | | } |
110 | | |
111 | | impl AddAssign<ArchitectureSize> for ArchitectureSize { |
112 | 0 | fn add_assign(&mut self, rhs: ArchitectureSize) { |
113 | 0 | self.bytes += rhs.bytes; |
114 | 0 | self.pointers += rhs.pointers; |
115 | 0 | } |
116 | | } |
117 | | |
118 | | impl From<Alignment> for ArchitectureSize { |
119 | 257 | fn from(align: Alignment) -> Self { |
120 | 257 | match align { |
121 | 257 | Alignment::Bytes(bytes) => ArchitectureSize::new(bytes.get(), 0), |
122 | 0 | Alignment::Pointer => ArchitectureSize::new(0, 1), |
123 | | } |
124 | 257 | } |
125 | | } |
126 | | |
127 | | impl core::fmt::Debug for ArchitectureSize { |
128 | 0 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { |
129 | 0 | f.write_str(&self.format("ptrsz")) |
130 | 0 | } |
131 | | } |
132 | | |
133 | | impl ArchitectureSize { |
134 | 6.97k | pub fn new(bytes: usize, pointers: usize) -> Self { |
135 | 6.97k | Self { bytes, pointers } |
136 | 6.97k | } |
137 | | |
138 | 272 | pub fn max<B: core::borrow::Borrow<Self>>(&self, other: B) -> Self { |
139 | 272 | let other = other.borrow(); |
140 | 272 | let self32 = self.size_wasm32(); |
141 | 272 | let self64 = self.size_wasm64(); |
142 | 272 | let other32 = other.size_wasm32(); |
143 | 272 | let other64 = other.size_wasm64(); |
144 | 272 | if self32 >= other32 && self64 >= other64 { |
145 | 89 | *self |
146 | 183 | } else if self32 <= other32 && self64 <= other64 { |
147 | 183 | *other |
148 | | } else { |
149 | | // we can assume a combination of bytes and pointers, so align to at least pointer size |
150 | 0 | let new32 = align_to(self32.max(other32), 4); |
151 | 0 | let new64 = align_to(self64.max(other64), 8); |
152 | 0 | ArchitectureSize::new(new32 + new32 - new64, (new64 - new32) / 4) |
153 | | } |
154 | 272 | } |
155 | | |
156 | 0 | pub fn add_bytes(&self, b: usize) -> Self { |
157 | 0 | Self::new(self.bytes + b, self.pointers) |
158 | 0 | } |
159 | | |
160 | | /// The effective offset/size is |
161 | | /// `constant_bytes() + core::mem::size_of::<*const u8>() * pointers_to_add()` |
162 | 0 | pub fn constant_bytes(&self) -> usize { |
163 | 0 | self.bytes |
164 | 0 | } |
165 | | |
166 | 0 | pub fn pointers_to_add(&self) -> usize { |
167 | 0 | self.pointers |
168 | 0 | } |
169 | | |
170 | | /// Shortcut for compatibility with previous versions |
171 | 2.18k | pub fn size_wasm32(&self) -> usize { |
172 | 2.18k | self.bytes + self.pointers * 4 |
173 | 2.18k | } |
174 | | |
175 | 2.18k | pub fn size_wasm64(&self) -> usize { |
176 | 2.18k | self.bytes + self.pointers * 8 |
177 | 2.18k | } |
178 | | |
179 | | /// prefer this over >0 |
180 | 0 | pub fn is_empty(&self) -> bool { |
181 | 0 | self.bytes == 0 && self.pointers == 0 |
182 | 0 | } |
183 | | |
184 | | // create a suitable expression in bytes from a pointer size argument |
185 | 0 | pub fn format(&self, ptrsize_expr: &str) -> String { |
186 | 0 | self.format_term(ptrsize_expr, false) |
187 | 0 | } |
188 | | |
189 | | // create a suitable expression in bytes from a pointer size argument, |
190 | | // extended API with optional brackets around the sum |
191 | 0 | pub fn format_term(&self, ptrsize_expr: &str, suppress_brackets: bool) -> String { |
192 | 0 | if self.pointers != 0 { |
193 | 0 | if self.bytes > 0 { |
194 | | // both |
195 | 0 | if suppress_brackets { |
196 | 0 | format!( |
197 | | "{}+{}*{ptrsize_expr}", |
198 | 0 | self.constant_bytes(), |
199 | 0 | self.pointers_to_add() |
200 | | ) |
201 | | } else { |
202 | 0 | format!( |
203 | | "({}+{}*{ptrsize_expr})", |
204 | 0 | self.constant_bytes(), |
205 | 0 | self.pointers_to_add() |
206 | | ) |
207 | | } |
208 | 0 | } else if self.pointers == 1 { |
209 | | // one pointer |
210 | 0 | ptrsize_expr.into() |
211 | | } else { |
212 | | // only pointer |
213 | 0 | if suppress_brackets { |
214 | 0 | format!("{}*{ptrsize_expr}", self.pointers_to_add()) |
215 | | } else { |
216 | 0 | format!("({}*{ptrsize_expr})", self.pointers_to_add()) |
217 | | } |
218 | | } |
219 | | } else { |
220 | | // only bytes |
221 | 0 | format!("{}", self.constant_bytes()) |
222 | | } |
223 | 0 | } |
224 | | } |
225 | | |
226 | | /// Information per structure element |
227 | | #[derive(Default)] |
228 | | pub struct ElementInfo { |
229 | | pub size: ArchitectureSize, |
230 | | pub align: Alignment, |
231 | | } |
232 | | |
233 | | impl From<Alignment> for ElementInfo { |
234 | 257 | fn from(align: Alignment) -> Self { |
235 | 257 | ElementInfo { |
236 | 257 | size: align.into(), |
237 | 257 | align, |
238 | 257 | } |
239 | 257 | } |
240 | | } |
241 | | |
242 | | impl ElementInfo { |
243 | 571 | fn new(size: ArchitectureSize, align: Alignment) -> Self { |
244 | 571 | Self { size, align } |
245 | 571 | } |
246 | | } |
247 | | |
248 | | /// Collect size and alignment for sub-elements of a structure |
249 | | #[derive(Default)] |
250 | | pub struct SizeAlign { |
251 | | map: Vec<ElementInfo>, |
252 | | } |
253 | | |
254 | | impl SizeAlign { |
255 | 596 | pub fn fill(&mut self, resolve: &Resolve) -> anyhow::Result<()> { |
256 | 596 | self.map = Vec::new(); |
257 | 600 | for (_, ty) in resolve.types.iter() { |
258 | 600 | let pair = self.calculate(ty)?; |
259 | 600 | self.map.push(pair); |
260 | | } |
261 | 596 | Ok(()) |
262 | 596 | } |
263 | | |
264 | 600 | fn calculate(&self, ty: &TypeDef) -> anyhow::Result<ElementInfo> { |
265 | 600 | Ok(match &ty.kind { |
266 | 40 | TypeDefKind::Type(t) => ElementInfo::new(self.size(t), self.align(t)), |
267 | 0 | TypeDefKind::FixedLengthList(t, size) => { |
268 | 0 | let field_align = self.align(t); |
269 | 0 | let field_size = self.size(t); |
270 | 0 | let bytes = field_size.bytes.checked_mul(*size as usize).ok_or_else(|| { |
271 | 0 | anyhow::anyhow!( |
272 | | "size of fixed-length list of {size} elements overflows the target architecture's address space" |
273 | | ) |
274 | 0 | })?; |
275 | 0 | let pointers = field_size.pointers.checked_mul(*size as usize).ok_or_else(|| { |
276 | 0 | anyhow::anyhow!( |
277 | | "size of fixed-length list of {size} elements overflows the target architecture's address space" |
278 | | ) |
279 | 0 | })?; |
280 | 0 | ElementInfo::new(ArchitectureSize::new(bytes, pointers), field_align) |
281 | | } |
282 | | TypeDefKind::List(_) => { |
283 | 59 | ElementInfo::new(ArchitectureSize::new(0, 2), Alignment::Pointer) |
284 | | } |
285 | | TypeDefKind::Map(_, _) => { |
286 | 0 | ElementInfo::new(ArchitectureSize::new(0, 2), Alignment::Pointer) |
287 | | } |
288 | 39 | TypeDefKind::Record(r) => self.record(r.fields.iter().map(|f| &f.ty)), |
289 | 209 | TypeDefKind::Tuple(t) => self.record(t.types.iter()), |
290 | 20 | TypeDefKind::Flags(f) => match f.repr() { |
291 | 20 | FlagsRepr::U8 => int_size_align(Int::U8), |
292 | 0 | FlagsRepr::U16 => int_size_align(Int::U16), |
293 | 0 | FlagsRepr::U32(n) => ElementInfo::new( |
294 | 0 | ArchitectureSize::new(n * 4, 0), |
295 | 0 | Alignment::Bytes(NonZeroUsize::new(4).unwrap()), |
296 | | ), |
297 | | }, |
298 | 63 | TypeDefKind::Variant(v) => self.variant(v.tag(), v.cases.iter().map(|c| c.ty.as_ref())), |
299 | 9 | TypeDefKind::Enum(e) => self.variant(e.tag(), []), |
300 | 70 | TypeDefKind::Option(t) => self.variant(Int::U8, [Some(t)]), |
301 | 101 | TypeDefKind::Result(r) => self.variant(Int::U8, [r.ok.as_ref(), r.err.as_ref()]), |
302 | | // A resource is represented as an index. |
303 | | // A future is represented as an index. |
304 | | // A stream is represented as an index. |
305 | | // An error is represented as an index. |
306 | | TypeDefKind::Handle(_) | TypeDefKind::Future(_) | TypeDefKind::Stream(_) => { |
307 | 9 | int_size_align(Int::U32) |
308 | | } |
309 | | // This shouldn't be used for anything since raw resources aren't part of the ABI -- just handles to |
310 | | // them. |
311 | 20 | TypeDefKind::Resource => ElementInfo::new( |
312 | 20 | ArchitectureSize::new(usize::MAX, 0), |
313 | 20 | Alignment::Bytes(NonZeroUsize::new(usize::MAX).unwrap()), |
314 | | ), |
315 | 0 | TypeDefKind::Unknown => unreachable!(), |
316 | | }) |
317 | 600 | } |
318 | | |
319 | 2.94k | pub fn size(&self, ty: &Type) -> ArchitectureSize { |
320 | 2.94k | match ty { |
321 | 1.46k | Type::Bool | Type::U8 | Type::S8 => ArchitectureSize::new(1, 0), |
322 | 55 | Type::U16 | Type::S16 => ArchitectureSize::new(2, 0), |
323 | | Type::U32 | Type::S32 | Type::F32 | Type::Char | Type::ErrorContext => { |
324 | 65 | ArchitectureSize::new(4, 0) |
325 | | } |
326 | 103 | Type::U64 | Type::S64 | Type::F64 => ArchitectureSize::new(8, 0), |
327 | 6 | Type::String => ArchitectureSize::new(0, 2), |
328 | 1.24k | Type::Id(id) => self.map[id.index()].size, |
329 | | } |
330 | 2.94k | } |
331 | | |
332 | 2.97k | pub fn align(&self, ty: &Type) -> Alignment { |
333 | 2.97k | match ty { |
334 | 1.47k | Type::Bool | Type::U8 | Type::S8 => Alignment::Bytes(NonZeroUsize::new(1).unwrap()), |
335 | 56 | Type::U16 | Type::S16 => Alignment::Bytes(NonZeroUsize::new(2).unwrap()), |
336 | | Type::U32 | Type::S32 | Type::F32 | Type::Char | Type::ErrorContext => { |
337 | 75 | Alignment::Bytes(NonZeroUsize::new(4).unwrap()) |
338 | | } |
339 | 105 | Type::U64 | Type::S64 | Type::F64 => Alignment::Bytes(NonZeroUsize::new(8).unwrap()), |
340 | 6 | Type::String => Alignment::Pointer, |
341 | 1.25k | Type::Id(id) => self.map[id.index()].align, |
342 | | } |
343 | 2.97k | } |
344 | | |
345 | 248 | pub fn field_offsets<'a>( |
346 | 248 | &self, |
347 | 248 | types: impl IntoIterator<Item = &'a Type>, |
348 | 248 | ) -> Vec<(ArchitectureSize, &'a Type)> { |
349 | 248 | let mut cur = ArchitectureSize::default(); |
350 | 248 | types |
351 | 248 | .into_iter() |
352 | 1.01k | .map(|ty| { |
353 | 1.01k | let ret = align_to_arch(cur, self.align(ty)); |
354 | 1.01k | cur = ret + self.size(ty); |
355 | 1.01k | (ret, ty) |
356 | 1.01k | }) <wit_parser::sizealign::SizeAlign>::field_offsets::<core::slice::iter::Iter<wit_parser::Type>>::{closure#0}Line | Count | Source | 352 | 916 | .map(|ty| { | 353 | 916 | let ret = align_to_arch(cur, self.align(ty)); | 354 | 916 | cur = ret + self.size(ty); | 355 | 916 | (ret, ty) | 356 | 916 | }) |
<wit_parser::sizealign::SizeAlign>::field_offsets::<core::iter::adapters::map::Map<core::slice::iter::Iter<wit_parser::Field>, wasm_tools_fuzz::wit64::run::{closure#3}>>::{closure#0}Line | Count | Source | 352 | 99 | .map(|ty| { | 353 | 99 | let ret = align_to_arch(cur, self.align(ty)); | 354 | 99 | cur = ret + self.size(ty); | 355 | 99 | (ret, ty) | 356 | 99 | }) |
Unexecuted instantiation: <wit_parser::sizealign::SizeAlign>::field_offsets::<_>::{closure#0} |
357 | 248 | .collect() |
358 | 248 | } <wit_parser::sizealign::SizeAlign>::field_offsets::<core::slice::iter::Iter<wit_parser::Type>> Line | Count | Source | 345 | 209 | pub fn field_offsets<'a>( | 346 | 209 | &self, | 347 | 209 | types: impl IntoIterator<Item = &'a Type>, | 348 | 209 | ) -> Vec<(ArchitectureSize, &'a Type)> { | 349 | 209 | let mut cur = ArchitectureSize::default(); | 350 | 209 | types | 351 | 209 | .into_iter() | 352 | 209 | .map(|ty| { | 353 | | let ret = align_to_arch(cur, self.align(ty)); | 354 | | cur = ret + self.size(ty); | 355 | | (ret, ty) | 356 | | }) | 357 | 209 | .collect() | 358 | 209 | } |
<wit_parser::sizealign::SizeAlign>::field_offsets::<core::iter::adapters::map::Map<core::slice::iter::Iter<wit_parser::Field>, wasm_tools_fuzz::wit64::run::{closure#3}>>Line | Count | Source | 345 | 39 | pub fn field_offsets<'a>( | 346 | 39 | &self, | 347 | 39 | types: impl IntoIterator<Item = &'a Type>, | 348 | 39 | ) -> Vec<(ArchitectureSize, &'a Type)> { | 349 | 39 | let mut cur = ArchitectureSize::default(); | 350 | 39 | types | 351 | 39 | .into_iter() | 352 | 39 | .map(|ty| { | 353 | | let ret = align_to_arch(cur, self.align(ty)); | 354 | | cur = ret + self.size(ty); | 355 | | (ret, ty) | 356 | | }) | 357 | 39 | .collect() | 358 | 39 | } |
Unexecuted instantiation: <wit_parser::sizealign::SizeAlign>::field_offsets::<_> |
359 | | |
360 | 24 | pub fn payload_offset<'a>( |
361 | 24 | &self, |
362 | 24 | tag: Int, |
363 | 24 | cases: impl IntoIterator<Item = Option<&'a Type>>, |
364 | 24 | ) -> ArchitectureSize { |
365 | 24 | let mut max_align = Alignment::default(); |
366 | 63 | for ty in cases { |
367 | 63 | if let Some(ty) = ty { |
368 | 28 | max_align = max_align.max(self.align(ty)); |
369 | 35 | } |
370 | | } |
371 | 24 | let tag_size = int_size_align(tag).size; |
372 | 24 | align_to_arch(tag_size, max_align) |
373 | 24 | } <wit_parser::sizealign::SizeAlign>::payload_offset::<core::iter::adapters::map::Map<core::slice::iter::Iter<wit_parser::Case>, wasm_tools_fuzz::wit64::run::{closure#6}>>Line | Count | Source | 360 | 24 | pub fn payload_offset<'a>( | 361 | 24 | &self, | 362 | 24 | tag: Int, | 363 | 24 | cases: impl IntoIterator<Item = Option<&'a Type>>, | 364 | 24 | ) -> ArchitectureSize { | 365 | 24 | let mut max_align = Alignment::default(); | 366 | 63 | for ty in cases { | 367 | 63 | if let Some(ty) = ty { | 368 | 28 | max_align = max_align.max(self.align(ty)); | 369 | 35 | } | 370 | | } | 371 | 24 | let tag_size = int_size_align(tag).size; | 372 | 24 | align_to_arch(tag_size, max_align) | 373 | 24 | } |
Unexecuted instantiation: <wit_parser::sizealign::SizeAlign>::payload_offset::<_> |
374 | | |
375 | 248 | pub fn record<'a>(&self, types: impl IntoIterator<Item = &'a Type>) -> ElementInfo { |
376 | 248 | let mut size = ArchitectureSize::default(); |
377 | 248 | let mut align = Alignment::default(); |
378 | 1.01k | for ty in types { |
379 | 1.01k | let field_size = self.size(ty); |
380 | 1.01k | let field_align = self.align(ty); |
381 | 1.01k | size = align_to_arch(size, field_align) + field_size; |
382 | 1.01k | align = align.max(field_align); |
383 | 1.01k | } |
384 | 248 | ElementInfo::new(align_to_arch(size, align), align) |
385 | 248 | } <wit_parser::sizealign::SizeAlign>::record::<core::slice::iter::Iter<wit_parser::Type>> Line | Count | Source | 375 | 209 | pub fn record<'a>(&self, types: impl IntoIterator<Item = &'a Type>) -> ElementInfo { | 376 | 209 | let mut size = ArchitectureSize::default(); | 377 | 209 | let mut align = Alignment::default(); | 378 | 916 | for ty in types { | 379 | 916 | let field_size = self.size(ty); | 380 | 916 | let field_align = self.align(ty); | 381 | 916 | size = align_to_arch(size, field_align) + field_size; | 382 | 916 | align = align.max(field_align); | 383 | 916 | } | 384 | 209 | ElementInfo::new(align_to_arch(size, align), align) | 385 | 209 | } |
<wit_parser::sizealign::SizeAlign>::record::<core::iter::adapters::map::Map<core::slice::iter::Iter<wit_parser::Field>, <wit_parser::sizealign::SizeAlign>::calculate::{closure#2}>>Line | Count | Source | 375 | 39 | pub fn record<'a>(&self, types: impl IntoIterator<Item = &'a Type>) -> ElementInfo { | 376 | 39 | let mut size = ArchitectureSize::default(); | 377 | 39 | let mut align = Alignment::default(); | 378 | 99 | for ty in types { | 379 | 99 | let field_size = self.size(ty); | 380 | 99 | let field_align = self.align(ty); | 381 | 99 | size = align_to_arch(size, field_align) + field_size; | 382 | 99 | align = align.max(field_align); | 383 | 99 | } | 384 | 39 | ElementInfo::new(align_to_arch(size, align), align) | 385 | 39 | } |
|
386 | | |
387 | 0 | pub fn params<'a>(&self, types: impl IntoIterator<Item = &'a Type>) -> ElementInfo { |
388 | 0 | self.record(types.into_iter()) |
389 | 0 | } |
390 | | |
391 | 204 | fn variant<'a>( |
392 | 204 | &self, |
393 | 204 | tag: Int, |
394 | 204 | types: impl IntoIterator<Item = Option<&'a Type>>, |
395 | 204 | ) -> ElementInfo { |
396 | | let ElementInfo { |
397 | 204 | size: discrim_size, |
398 | 204 | align: discrim_align, |
399 | 204 | } = int_size_align(tag); |
400 | 204 | let mut case_size = ArchitectureSize::default(); |
401 | 204 | let mut case_align = Alignment::default(); |
402 | 335 | for ty in types { |
403 | 335 | if let Some(ty) = ty { |
404 | 272 | case_size = case_size.max(&self.size(ty)); |
405 | 272 | case_align = case_align.max(self.align(ty)); |
406 | 272 | } |
407 | | } |
408 | 204 | let align = discrim_align.max(case_align); |
409 | 204 | let discrim_aligned = align_to_arch(discrim_size, case_align); |
410 | 204 | let size_sum = discrim_aligned + case_size; |
411 | 204 | ElementInfo::new(align_to_arch(size_sum, align), align) |
412 | 204 | } <wit_parser::sizealign::SizeAlign>::variant::<[core::option::Option<&wit_parser::Type>; 0]> Line | Count | Source | 391 | 9 | fn variant<'a>( | 392 | 9 | &self, | 393 | 9 | tag: Int, | 394 | 9 | types: impl IntoIterator<Item = Option<&'a Type>>, | 395 | 9 | ) -> ElementInfo { | 396 | | let ElementInfo { | 397 | 9 | size: discrim_size, | 398 | 9 | align: discrim_align, | 399 | 9 | } = int_size_align(tag); | 400 | 9 | let mut case_size = ArchitectureSize::default(); | 401 | 9 | let mut case_align = Alignment::default(); | 402 | 9 | for ty in types { | 403 | 0 | if let Some(ty) = ty { | 404 | 0 | case_size = case_size.max(&self.size(ty)); | 405 | 0 | case_align = case_align.max(self.align(ty)); | 406 | 0 | } | 407 | | } | 408 | 9 | let align = discrim_align.max(case_align); | 409 | 9 | let discrim_aligned = align_to_arch(discrim_size, case_align); | 410 | 9 | let size_sum = discrim_aligned + case_size; | 411 | 9 | ElementInfo::new(align_to_arch(size_sum, align), align) | 412 | 9 | } |
<wit_parser::sizealign::SizeAlign>::variant::<[core::option::Option<&wit_parser::Type>; 1]> Line | Count | Source | 391 | 70 | fn variant<'a>( | 392 | 70 | &self, | 393 | 70 | tag: Int, | 394 | 70 | types: impl IntoIterator<Item = Option<&'a Type>>, | 395 | 70 | ) -> ElementInfo { | 396 | | let ElementInfo { | 397 | 70 | size: discrim_size, | 398 | 70 | align: discrim_align, | 399 | 70 | } = int_size_align(tag); | 400 | 70 | let mut case_size = ArchitectureSize::default(); | 401 | 70 | let mut case_align = Alignment::default(); | 402 | 70 | for ty in types { | 403 | 70 | if let Some(ty) = ty { | 404 | 70 | case_size = case_size.max(&self.size(ty)); | 405 | 70 | case_align = case_align.max(self.align(ty)); | 406 | 70 | } | 407 | | } | 408 | 70 | let align = discrim_align.max(case_align); | 409 | 70 | let discrim_aligned = align_to_arch(discrim_size, case_align); | 410 | 70 | let size_sum = discrim_aligned + case_size; | 411 | 70 | ElementInfo::new(align_to_arch(size_sum, align), align) | 412 | 70 | } |
<wit_parser::sizealign::SizeAlign>::variant::<[core::option::Option<&wit_parser::Type>; 2]> Line | Count | Source | 391 | 101 | fn variant<'a>( | 392 | 101 | &self, | 393 | 101 | tag: Int, | 394 | 101 | types: impl IntoIterator<Item = Option<&'a Type>>, | 395 | 101 | ) -> ElementInfo { | 396 | | let ElementInfo { | 397 | 101 | size: discrim_size, | 398 | 101 | align: discrim_align, | 399 | 101 | } = int_size_align(tag); | 400 | 101 | let mut case_size = ArchitectureSize::default(); | 401 | 101 | let mut case_align = Alignment::default(); | 402 | 202 | for ty in types { | 403 | 202 | if let Some(ty) = ty { | 404 | 174 | case_size = case_size.max(&self.size(ty)); | 405 | 174 | case_align = case_align.max(self.align(ty)); | 406 | 174 | } | 407 | | } | 408 | 101 | let align = discrim_align.max(case_align); | 409 | 101 | let discrim_aligned = align_to_arch(discrim_size, case_align); | 410 | 101 | let size_sum = discrim_aligned + case_size; | 411 | 101 | ElementInfo::new(align_to_arch(size_sum, align), align) | 412 | 101 | } |
<wit_parser::sizealign::SizeAlign>::variant::<core::iter::adapters::map::Map<core::slice::iter::Iter<wit_parser::Case>, <wit_parser::sizealign::SizeAlign>::calculate::{closure#3}>>Line | Count | Source | 391 | 24 | fn variant<'a>( | 392 | 24 | &self, | 393 | 24 | tag: Int, | 394 | 24 | types: impl IntoIterator<Item = Option<&'a Type>>, | 395 | 24 | ) -> ElementInfo { | 396 | | let ElementInfo { | 397 | 24 | size: discrim_size, | 398 | 24 | align: discrim_align, | 399 | 24 | } = int_size_align(tag); | 400 | 24 | let mut case_size = ArchitectureSize::default(); | 401 | 24 | let mut case_align = Alignment::default(); | 402 | 63 | for ty in types { | 403 | 63 | if let Some(ty) = ty { | 404 | 28 | case_size = case_size.max(&self.size(ty)); | 405 | 28 | case_align = case_align.max(self.align(ty)); | 406 | 35 | } | 407 | | } | 408 | 24 | let align = discrim_align.max(case_align); | 409 | 24 | let discrim_aligned = align_to_arch(discrim_size, case_align); | 410 | 24 | let size_sum = discrim_aligned + case_size; | 411 | 24 | ElementInfo::new(align_to_arch(size_sum, align), align) | 412 | 24 | } |
|
413 | | } |
414 | | |
415 | 257 | fn int_size_align(i: Int) -> ElementInfo { |
416 | 257 | match i { |
417 | 248 | Int::U8 => Alignment::Bytes(NonZeroUsize::new(1).unwrap()), |
418 | 0 | Int::U16 => Alignment::Bytes(NonZeroUsize::new(2).unwrap()), |
419 | 9 | Int::U32 => Alignment::Bytes(NonZeroUsize::new(4).unwrap()), |
420 | 0 | Int::U64 => Alignment::Bytes(NonZeroUsize::new(8).unwrap()), |
421 | | } |
422 | 257 | .into() |
423 | 257 | } |
424 | | |
425 | | /// Increase `val` to a multiple of `align`; |
426 | | /// `align` must be a power of two |
427 | 2.86k | pub(crate) fn align_to(val: usize, align: usize) -> usize { |
428 | 2.86k | (val + align - 1) & !(align - 1) |
429 | 2.86k | } |
430 | | |
431 | | /// Increase `val` to a multiple of `align`, with special handling for pointers; |
432 | | /// `align` must be a power of two or `Alignment::Pointer` |
433 | 2.71k | pub fn align_to_arch(val: ArchitectureSize, align: Alignment) -> ArchitectureSize { |
434 | 2.71k | match align { |
435 | | Alignment::Pointer => { |
436 | 151 | let new32 = align_to(val.bytes, 4); |
437 | 151 | if new32 != align_to(new32, 8) { |
438 | 64 | ArchitectureSize::new(new32 - 4, val.pointers + 1) |
439 | | } else { |
440 | 87 | ArchitectureSize::new(new32, val.pointers) |
441 | | } |
442 | | } |
443 | 2.55k | Alignment::Bytes(align_bytes) => { |
444 | 2.55k | let align_bytes = align_bytes.get(); |
445 | 2.55k | if align_bytes > 4 && (val.pointers & 1) != 0 { |
446 | 3 | let new_bytes = align_to(val.bytes, align_bytes); |
447 | 3 | if (new_bytes - val.bytes) >= 4 { |
448 | | // up to four extra bytes fit together with a the extra 32 bit pointer |
449 | | // and the 64 bit pointer is always 8 bytes (so no change in value) |
450 | 1 | ArchitectureSize::new(new_bytes - 8, val.pointers + 1) |
451 | | } else { |
452 | | // there is no room to combine, so the odd pointer aligns to 8 bytes |
453 | 2 | ArchitectureSize::new(new_bytes + 8, val.pointers - 1) |
454 | | } |
455 | | } else { |
456 | 2.55k | ArchitectureSize::new(align_to(val.bytes, align_bytes), val.pointers) |
457 | | } |
458 | | } |
459 | | } |
460 | 2.71k | } |
461 | | |
462 | | #[cfg(test)] |
463 | | mod test { |
464 | | use super::*; |
465 | | use alloc::string::ToString; |
466 | | use alloc::vec; |
467 | | |
468 | | #[test] |
469 | | fn align() { |
470 | | // u8 + ptr |
471 | | assert_eq!( |
472 | | align_to_arch(ArchitectureSize::new(1, 0), Alignment::Pointer), |
473 | | ArchitectureSize::new(0, 1) |
474 | | ); |
475 | | // u8 + u64 |
476 | | assert_eq!( |
477 | | align_to_arch( |
478 | | ArchitectureSize::new(1, 0), |
479 | | Alignment::Bytes(NonZeroUsize::new(8).unwrap()) |
480 | | ), |
481 | | ArchitectureSize::new(8, 0) |
482 | | ); |
483 | | // u8 + u32 |
484 | | assert_eq!( |
485 | | align_to_arch( |
486 | | ArchitectureSize::new(1, 0), |
487 | | Alignment::Bytes(NonZeroUsize::new(4).unwrap()) |
488 | | ), |
489 | | ArchitectureSize::new(4, 0) |
490 | | ); |
491 | | // ptr + u64 |
492 | | assert_eq!( |
493 | | align_to_arch( |
494 | | ArchitectureSize::new(0, 1), |
495 | | Alignment::Bytes(NonZeroUsize::new(8).unwrap()) |
496 | | ), |
497 | | ArchitectureSize::new(8, 0) |
498 | | ); |
499 | | // u32 + ptr |
500 | | assert_eq!( |
501 | | align_to_arch(ArchitectureSize::new(4, 0), Alignment::Pointer), |
502 | | ArchitectureSize::new(0, 1) |
503 | | ); |
504 | | // u32, ptr + u64 |
505 | | assert_eq!( |
506 | | align_to_arch( |
507 | | ArchitectureSize::new(0, 2), |
508 | | Alignment::Bytes(NonZeroUsize::new(8).unwrap()) |
509 | | ), |
510 | | ArchitectureSize::new(0, 2) |
511 | | ); |
512 | | // ptr, u8 + u64 |
513 | | assert_eq!( |
514 | | align_to_arch( |
515 | | ArchitectureSize::new(1, 1), |
516 | | Alignment::Bytes(NonZeroUsize::new(8).unwrap()) |
517 | | ), |
518 | | ArchitectureSize::new(0, 2) |
519 | | ); |
520 | | // ptr, u8 + ptr |
521 | | assert_eq!( |
522 | | align_to_arch(ArchitectureSize::new(1, 1), Alignment::Pointer), |
523 | | ArchitectureSize::new(0, 2) |
524 | | ); |
525 | | // ptr, ptr, u8 + u64 |
526 | | assert_eq!( |
527 | | align_to_arch( |
528 | | ArchitectureSize::new(1, 2), |
529 | | Alignment::Bytes(NonZeroUsize::new(8).unwrap()) |
530 | | ), |
531 | | ArchitectureSize::new(8, 2) |
532 | | ); |
533 | | assert_eq!( |
534 | | align_to_arch( |
535 | | ArchitectureSize::new(30, 3), |
536 | | Alignment::Bytes(NonZeroUsize::new(8).unwrap()) |
537 | | ), |
538 | | ArchitectureSize::new(40, 2) |
539 | | ); |
540 | | |
541 | | assert_eq!( |
542 | | ArchitectureSize::new(12, 0).max(&ArchitectureSize::new(0, 2)), |
543 | | ArchitectureSize::new(8, 1) |
544 | | ); |
545 | | assert_eq!( |
546 | | ArchitectureSize::new(10, 0).max(&ArchitectureSize::new(0, 2)), |
547 | | ArchitectureSize::new(8, 1) |
548 | | ); |
549 | | |
550 | | assert_eq!( |
551 | | align_to_arch( |
552 | | ArchitectureSize::new(2, 0), |
553 | | Alignment::Bytes(NonZeroUsize::new(8).unwrap()) |
554 | | ), |
555 | | ArchitectureSize::new(8, 0) |
556 | | ); |
557 | | assert_eq!( |
558 | | align_to_arch(ArchitectureSize::new(2, 0), Alignment::Pointer), |
559 | | ArchitectureSize::new(0, 1) |
560 | | ); |
561 | | } |
562 | | |
563 | | #[test] |
564 | | fn resource_size() { |
565 | | // keep it identical to the old behavior |
566 | | let obj = SizeAlign::default(); |
567 | | let elem = obj |
568 | | .calculate(&TypeDef { |
569 | | name: None, |
570 | | kind: TypeDefKind::Resource, |
571 | | owner: crate::TypeOwner::None, |
572 | | docs: Default::default(), |
573 | | stability: Default::default(), |
574 | | span: Default::default(), |
575 | | external_id: Default::default(), |
576 | | }) |
577 | | .unwrap(); |
578 | | assert_eq!(elem.size, ArchitectureSize::new(usize::MAX, 0)); |
579 | | assert_eq!( |
580 | | elem.align, |
581 | | Alignment::Bytes(NonZeroUsize::new(usize::MAX).unwrap()) |
582 | | ); |
583 | | } |
584 | | #[test] |
585 | | fn result_ptr_10() { |
586 | | let mut obj = SizeAlign::default(); |
587 | | let mut resolve = Resolve::default(); |
588 | | let tuple = crate::Tuple { |
589 | | types: vec![Type::U16, Type::U16, Type::U16, Type::U16, Type::U16], |
590 | | }; |
591 | | let id = resolve.types.alloc(TypeDef { |
592 | | name: None, |
593 | | kind: TypeDefKind::Tuple(tuple), |
594 | | owner: crate::TypeOwner::None, |
595 | | docs: Default::default(), |
596 | | stability: Default::default(), |
597 | | span: Default::default(), |
598 | | external_id: Default::default(), |
599 | | }); |
600 | | obj.fill(&resolve).unwrap(); |
601 | | let my_result = crate::Result_ { |
602 | | ok: Some(Type::String), |
603 | | err: Some(Type::Id(id)), |
604 | | }; |
605 | | let elem = obj |
606 | | .calculate(&TypeDef { |
607 | | name: None, |
608 | | kind: TypeDefKind::Result(my_result), |
609 | | owner: crate::TypeOwner::None, |
610 | | docs: Default::default(), |
611 | | stability: Default::default(), |
612 | | span: Default::default(), |
613 | | external_id: Default::default(), |
614 | | }) |
615 | | .unwrap(); |
616 | | assert_eq!(elem.size, ArchitectureSize::new(8, 2)); |
617 | | assert_eq!(elem.align, Alignment::Pointer); |
618 | | } |
619 | | |
620 | | #[test] |
621 | | fn fixed_length_list_size_overflow_returns_error_instead_of_panicking() { |
622 | | // Regression test: a fixed-length list whose element-size * length |
623 | | // overflows `usize` used to panic via `.unwrap()` on a `None` from |
624 | | // `checked_mul`. It must now return an `Err` instead. |
625 | | let mut obj = SizeAlign::default(); |
626 | | let mut resolve = Resolve::default(); |
627 | | |
628 | | // `type a = list<u64, 4294967295>;` |
629 | | let a = resolve.types.alloc(TypeDef { |
630 | | name: None, |
631 | | kind: TypeDefKind::FixedLengthList(Type::U64, u32::MAX), |
632 | | owner: crate::TypeOwner::None, |
633 | | docs: Default::default(), |
634 | | stability: Default::default(), |
635 | | span: Default::default(), |
636 | | external_id: Default::default(), |
637 | | }); |
638 | | // `type b = list<a, 4294967295>;` -- `a`'s size (8 * u32::MAX) times |
639 | | // `u32::MAX` again overflows `usize` on both 32- and 64-bit targets. |
640 | | resolve.types.alloc(TypeDef { |
641 | | name: None, |
642 | | kind: TypeDefKind::FixedLengthList(Type::Id(a), u32::MAX), |
643 | | owner: crate::TypeOwner::None, |
644 | | docs: Default::default(), |
645 | | stability: Default::default(), |
646 | | span: Default::default(), |
647 | | external_id: Default::default(), |
648 | | }); |
649 | | |
650 | | let err = obj.fill(&resolve).unwrap_err(); |
651 | | assert!(err.to_string().contains("overflows")); |
652 | | |
653 | | // A benign, non-overflowing fixed-length list still computes fine. |
654 | | let mut obj = SizeAlign::default(); |
655 | | let mut resolve = Resolve::default(); |
656 | | resolve.types.alloc(TypeDef { |
657 | | name: None, |
658 | | kind: TypeDefKind::FixedLengthList(Type::U64, 2), |
659 | | owner: crate::TypeOwner::None, |
660 | | docs: Default::default(), |
661 | | stability: Default::default(), |
662 | | span: Default::default(), |
663 | | external_id: Default::default(), |
664 | | }); |
665 | | obj.fill(&resolve).unwrap(); |
666 | | } |
667 | | #[test] |
668 | | fn result_ptr_64bit() { |
669 | | let obj = SizeAlign::default(); |
670 | | let my_record = crate::Record { |
671 | | fields: vec![ |
672 | | crate::Field { |
673 | | name: String::new(), |
674 | | ty: Type::String, |
675 | | docs: Default::default(), |
676 | | span: Default::default(), |
677 | | }, |
678 | | crate::Field { |
679 | | name: String::new(), |
680 | | ty: Type::U64, |
681 | | docs: Default::default(), |
682 | | span: Default::default(), |
683 | | }, |
684 | | ], |
685 | | }; |
686 | | let elem = obj |
687 | | .calculate(&TypeDef { |
688 | | name: None, |
689 | | kind: TypeDefKind::Record(my_record), |
690 | | owner: crate::TypeOwner::None, |
691 | | docs: Default::default(), |
692 | | stability: Default::default(), |
693 | | span: Default::default(), |
694 | | external_id: Default::default(), |
695 | | }) |
696 | | .unwrap(); |
697 | | assert_eq!(elem.size, ArchitectureSize::new(8, 2)); |
698 | | assert_eq!(elem.align, Alignment::Bytes(NonZeroUsize::new(8).unwrap())); |
699 | | } |
700 | | } |