Coverage Report

Created: 2025-12-09 07:28

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