Coverage Report

Created: 2026-06-21 07:19

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 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
5.31k
    fn default() -> Self {
23
5.31k
        Alignment::Bytes(NonZeroUsize::new(1).unwrap())
24
5.31k
    }
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
16.6k
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
38
16.6k
        Some(self.cmp(other))
39
16.6k
    }
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
16.6k
    fn cmp(&self, other: &Self) -> Ordering {
47
16.6k
        match (self, other) {
48
18
            (Alignment::Pointer, Alignment::Pointer) => Ordering::Equal,
49
255
            (Alignment::Pointer, Alignment::Bytes(b)) => {
50
255
                if b.get() > 4 {
51
16
                    Ordering::Less
52
                } else {
53
239
                    Ordering::Greater
54
                }
55
            }
56
142
            (Alignment::Bytes(b), Alignment::Pointer) => {
57
142
                if b.get() > 4 {
58
11
                    Ordering::Greater
59
                } else {
60
131
                    Ordering::Less
61
                }
62
            }
63
16.2k
            (Alignment::Bytes(a), Alignment::Bytes(b)) => a.cmp(b),
64
        }
65
16.6k
    }
66
}
67
68
impl Alignment {
69
    /// for easy migration this gives you the value for wasm32
70
8.39k
    pub fn align_wasm32(&self) -> usize {
71
8.39k
        match self {
72
466
            Alignment::Pointer => 4,
73
7.92k
            Alignment::Bytes(bytes) => bytes.get(),
74
        }
75
8.39k
    }
76
77
8.39k
    pub fn align_wasm64(&self) -> usize {
78
8.39k
        match self {
79
466
            Alignment::Pointer => 8,
80
7.92k
            Alignment::Bytes(bytes) => bytes.get(),
81
        }
82
8.39k
    }
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
10.5k
    fn add(self, rhs: ArchitectureSize) -> Self::Output {
107
10.5k
        ArchitectureSize::new(self.bytes + rhs.bytes, self.pointers + rhs.pointers)
108
10.5k
    }
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
4.64k
    fn from(align: Alignment) -> Self {
120
4.64k
        match align {
121
4.64k
            Alignment::Bytes(bytes) => ArchitectureSize::new(bytes.get(), 0),
122
0
            Alignment::Pointer => ArchitectureSize::new(0, 1),
123
        }
124
4.64k
    }
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
41.9k
    pub fn new(bytes: usize, pointers: usize) -> Self {
135
41.9k
        Self { bytes, pointers }
136
41.9k
    }
137
138
6.38k
    pub fn max<B: core::borrow::Borrow<Self>>(&self, other: B) -> Self {
139
6.38k
        let other = other.borrow();
140
6.38k
        let self32 = self.size_wasm32();
141
6.38k
        let self64 = self.size_wasm64();
142
6.38k
        let other32 = other.size_wasm32();
143
6.38k
        let other64 = other.size_wasm64();
144
6.38k
        if self32 >= other32 && self64 >= other64 {
145
3.46k
            *self
146
2.92k
        } else if self32 <= other32 && self64 <= other64 {
147
2.92k
            *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
6.38k
    }
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
25.6k
    pub fn size_wasm32(&self) -> usize {
172
25.6k
        self.bytes + self.pointers * 4
173
25.6k
    }
174
175
25.6k
    pub fn size_wasm64(&self) -> usize {
176
25.6k
        self.bytes + self.pointers * 8
177
25.6k
    }
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
4.64k
    fn from(align: Alignment) -> Self {
235
4.64k
        ElementInfo {
236
4.64k
            size: align.into(),
237
4.64k
            align,
238
4.64k
        }
239
4.64k
    }
240
}
241
242
impl ElementInfo {
243
8.09k
    fn new(size: ArchitectureSize, align: Alignment) -> Self {
244
8.09k
        Self { size, align }
245
8.09k
    }
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
1.14k
    pub fn fill(&mut self, resolve: &Resolve) {
256
1.14k
        self.map = Vec::new();
257
8.39k
        for (_, ty) in resolve.types.iter() {
258
8.39k
            let pair = self.calculate(ty);
259
8.39k
            self.map.push(pair);
260
8.39k
        }
261
1.14k
    }
262
263
8.39k
    fn calculate(&self, ty: &TypeDef) -> ElementInfo {
264
8.39k
        match &ty.kind {
265
3.18k
            TypeDefKind::Type(t) => ElementInfo::new(self.size(t), self.align(t)),
266
0
            TypeDefKind::FixedLengthList(t, size) => {
267
0
                let field_align = self.align(t);
268
0
                let field_size = self.size(t);
269
0
                ElementInfo::new(
270
0
                    ArchitectureSize::new(
271
0
                        field_size.bytes.checked_mul(*size as usize).unwrap(),
272
0
                        field_size.pointers.checked_mul(*size as usize).unwrap(),
273
                    ),
274
0
                    field_align,
275
                )
276
            }
277
            TypeDefKind::List(_) => {
278
317
                ElementInfo::new(ArchitectureSize::new(0, 2), Alignment::Pointer)
279
            }
280
            TypeDefKind::Map(_, _) => {
281
0
                ElementInfo::new(ArchitectureSize::new(0, 2), Alignment::Pointer)
282
            }
283
332
            TypeDefKind::Record(r) => self.record(r.fields.iter().map(|f| &f.ty)),
284
639
            TypeDefKind::Tuple(t) => self.record(t.types.iter()),
285
246
            TypeDefKind::Flags(f) => match f.repr() {
286
231
                FlagsRepr::U8 => int_size_align(Int::U8),
287
15
                FlagsRepr::U16 => int_size_align(Int::U16),
288
0
                FlagsRepr::U32(n) => ElementInfo::new(
289
0
                    ArchitectureSize::new(n * 4, 0),
290
0
                    Alignment::Bytes(NonZeroUsize::new(4).unwrap()),
291
                ),
292
            },
293
3.98k
            TypeDefKind::Variant(v) => self.variant(v.tag(), v.cases.iter().map(|c| c.ty.as_ref())),
294
567
            TypeDefKind::Enum(e) => self.variant(e.tag(), []),
295
398
            TypeDefKind::Option(t) => self.variant(Int::U8, [Some(t)]),
296
1.51k
            TypeDefKind::Result(r) => self.variant(Int::U8, [r.ok.as_ref(), r.err.as_ref()]),
297
            // A resource is represented as an index.
298
            // A future is represented as an index.
299
            // A stream is represented as an index.
300
            // An error is represented as an index.
301
            TypeDefKind::Handle(_) | TypeDefKind::Future(_) | TypeDefKind::Stream(_) => {
302
57
                int_size_align(Int::U32)
303
            }
304
            // This shouldn't be used for anything since raw resources aren't part of the ABI -- just handles to
305
            // them.
306
211
            TypeDefKind::Resource => ElementInfo::new(
307
211
                ArchitectureSize::new(usize::MAX, 0),
308
211
                Alignment::Bytes(NonZeroUsize::new(usize::MAX).unwrap()),
309
            ),
310
0
            TypeDefKind::Unknown => unreachable!(),
311
        }
312
8.39k
    }
313
314
25.0k
    pub fn size(&self, ty: &Type) -> ArchitectureSize {
315
25.0k
        match ty {
316
5.62k
            Type::Bool | Type::U8 | Type::S8 => ArchitectureSize::new(1, 0),
317
297
            Type::U16 | Type::S16 => ArchitectureSize::new(2, 0),
318
            Type::U32 | Type::S32 | Type::F32 | Type::Char | Type::ErrorContext => {
319
1.60k
                ArchitectureSize::new(4, 0)
320
            }
321
2.86k
            Type::U64 | Type::S64 | Type::F64 => ArchitectureSize::new(8, 0),
322
72
            Type::String => ArchitectureSize::new(0, 2),
323
14.5k
            Type::Id(id) => self.map[id.index()].size,
324
        }
325
25.0k
    }
326
327
28.4k
    pub fn align(&self, ty: &Type) -> Alignment {
328
28.4k
        match ty {
329
6.10k
            Type::Bool | Type::U8 | Type::S8 => Alignment::Bytes(NonZeroUsize::new(1).unwrap()),
330
350
            Type::U16 | Type::S16 => Alignment::Bytes(NonZeroUsize::new(2).unwrap()),
331
            Type::U32 | Type::S32 | Type::F32 | Type::Char | Type::ErrorContext => {
332
2.00k
                Alignment::Bytes(NonZeroUsize::new(4).unwrap())
333
            }
334
5.23k
            Type::U64 | Type::S64 | Type::F64 => Alignment::Bytes(NonZeroUsize::new(8).unwrap()),
335
80
            Type::String => Alignment::Pointer,
336
14.6k
            Type::Id(id) => self.map[id.index()].align,
337
        }
338
28.4k
    }
339
340
971
    pub fn field_offsets<'a>(
341
971
        &self,
342
971
        types: impl IntoIterator<Item = &'a Type>,
343
971
    ) -> Vec<(ArchitectureSize, &'a Type)> {
344
971
        let mut cur = ArchitectureSize::default();
345
971
        types
346
971
            .into_iter()
347
3.54k
            .map(|ty| {
348
3.54k
                let ret = align_to_arch(cur, self.align(ty));
349
3.54k
                cur = ret + self.size(ty);
350
3.54k
                (ret, ty)
351
3.54k
            })
<wit_parser::sizealign::SizeAlign>::field_offsets::<core::slice::iter::Iter<wit_parser::Type>>::{closure#0}
Line
Count
Source
347
2.33k
            .map(|ty| {
348
2.33k
                let ret = align_to_arch(cur, self.align(ty));
349
2.33k
                cur = ret + self.size(ty);
350
2.33k
                (ret, ty)
351
2.33k
            })
<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
347
1.21k
            .map(|ty| {
348
1.21k
                let ret = align_to_arch(cur, self.align(ty));
349
1.21k
                cur = ret + self.size(ty);
350
1.21k
                (ret, ty)
351
1.21k
            })
Unexecuted instantiation: <wit_parser::sizealign::SizeAlign>::field_offsets::<_>::{closure#0}
352
971
            .collect()
353
971
    }
<wit_parser::sizealign::SizeAlign>::field_offsets::<core::slice::iter::Iter<wit_parser::Type>>
Line
Count
Source
340
639
    pub fn field_offsets<'a>(
341
639
        &self,
342
639
        types: impl IntoIterator<Item = &'a Type>,
343
639
    ) -> Vec<(ArchitectureSize, &'a Type)> {
344
639
        let mut cur = ArchitectureSize::default();
345
639
        types
346
639
            .into_iter()
347
639
            .map(|ty| {
348
                let ret = align_to_arch(cur, self.align(ty));
349
                cur = ret + self.size(ty);
350
                (ret, ty)
351
            })
352
639
            .collect()
353
639
    }
<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
340
332
    pub fn field_offsets<'a>(
341
332
        &self,
342
332
        types: impl IntoIterator<Item = &'a Type>,
343
332
    ) -> Vec<(ArchitectureSize, &'a Type)> {
344
332
        let mut cur = ArchitectureSize::default();
345
332
        types
346
332
            .into_iter()
347
332
            .map(|ty| {
348
                let ret = align_to_arch(cur, self.align(ty));
349
                cur = ret + self.size(ty);
350
                (ret, ty)
351
            })
352
332
            .collect()
353
332
    }
Unexecuted instantiation: <wit_parser::sizealign::SizeAlign>::field_offsets::<_>
354
355
929
    pub fn payload_offset<'a>(
356
929
        &self,
357
929
        tag: Int,
358
929
        cases: impl IntoIterator<Item = Option<&'a Type>>,
359
929
    ) -> ArchitectureSize {
360
929
        let mut max_align = Alignment::default();
361
3.98k
        for ty in cases {
362
3.98k
            if let Some(ty) = ty {
363
3.35k
                max_align = max_align.max(self.align(ty));
364
3.35k
            }
365
        }
366
929
        let tag_size = int_size_align(tag).size;
367
929
        align_to_arch(tag_size, max_align)
368
929
    }
<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
355
929
    pub fn payload_offset<'a>(
356
929
        &self,
357
929
        tag: Int,
358
929
        cases: impl IntoIterator<Item = Option<&'a Type>>,
359
929
    ) -> ArchitectureSize {
360
929
        let mut max_align = Alignment::default();
361
3.98k
        for ty in cases {
362
3.98k
            if let Some(ty) = ty {
363
3.35k
                max_align = max_align.max(self.align(ty));
364
3.35k
            }
365
        }
366
929
        let tag_size = int_size_align(tag).size;
367
929
        align_to_arch(tag_size, max_align)
368
929
    }
Unexecuted instantiation: <wit_parser::sizealign::SizeAlign>::payload_offset::<_>
369
370
971
    pub fn record<'a>(&self, types: impl IntoIterator<Item = &'a Type>) -> ElementInfo {
371
971
        let mut size = ArchitectureSize::default();
372
971
        let mut align = Alignment::default();
373
3.54k
        for ty in types {
374
3.54k
            let field_size = self.size(ty);
375
3.54k
            let field_align = self.align(ty);
376
3.54k
            size = align_to_arch(size, field_align) + field_size;
377
3.54k
            align = align.max(field_align);
378
3.54k
        }
379
971
        ElementInfo::new(align_to_arch(size, align), align)
380
971
    }
<wit_parser::sizealign::SizeAlign>::record::<core::slice::iter::Iter<wit_parser::Type>>
Line
Count
Source
370
639
    pub fn record<'a>(&self, types: impl IntoIterator<Item = &'a Type>) -> ElementInfo {
371
639
        let mut size = ArchitectureSize::default();
372
639
        let mut align = Alignment::default();
373
2.33k
        for ty in types {
374
2.33k
            let field_size = self.size(ty);
375
2.33k
            let field_align = self.align(ty);
376
2.33k
            size = align_to_arch(size, field_align) + field_size;
377
2.33k
            align = align.max(field_align);
378
2.33k
        }
379
639
        ElementInfo::new(align_to_arch(size, align), align)
380
639
    }
<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
370
332
    pub fn record<'a>(&self, types: impl IntoIterator<Item = &'a Type>) -> ElementInfo {
371
332
        let mut size = ArchitectureSize::default();
372
332
        let mut align = Alignment::default();
373
1.21k
        for ty in types {
374
1.21k
            let field_size = self.size(ty);
375
1.21k
            let field_align = self.align(ty);
376
1.21k
            size = align_to_arch(size, field_align) + field_size;
377
1.21k
            align = align.max(field_align);
378
1.21k
        }
379
332
        ElementInfo::new(align_to_arch(size, align), align)
380
332
    }
381
382
0
    pub fn params<'a>(&self, types: impl IntoIterator<Item = &'a Type>) -> ElementInfo {
383
0
        self.record(types.into_iter())
384
0
    }
385
386
3.41k
    fn variant<'a>(
387
3.41k
        &self,
388
3.41k
        tag: Int,
389
3.41k
        types: impl IntoIterator<Item = Option<&'a Type>>,
390
3.41k
    ) -> ElementInfo {
391
        let ElementInfo {
392
3.41k
            size: discrim_size,
393
3.41k
            align: discrim_align,
394
3.41k
        } = int_size_align(tag);
395
3.41k
        let mut case_size = ArchitectureSize::default();
396
3.41k
        let mut case_align = Alignment::default();
397
7.41k
        for ty in types {
398
7.41k
            if let Some(ty) = ty {
399
6.38k
                case_size = case_size.max(&self.size(ty));
400
6.38k
                case_align = case_align.max(self.align(ty));
401
6.38k
            }
402
        }
403
3.41k
        let align = discrim_align.max(case_align);
404
3.41k
        let discrim_aligned = align_to_arch(discrim_size, case_align);
405
3.41k
        let size_sum = discrim_aligned + case_size;
406
3.41k
        ElementInfo::new(align_to_arch(size_sum, align), align)
407
3.41k
    }
<wit_parser::sizealign::SizeAlign>::variant::<[core::option::Option<&wit_parser::Type>; 0]>
Line
Count
Source
386
567
    fn variant<'a>(
387
567
        &self,
388
567
        tag: Int,
389
567
        types: impl IntoIterator<Item = Option<&'a Type>>,
390
567
    ) -> ElementInfo {
391
        let ElementInfo {
392
567
            size: discrim_size,
393
567
            align: discrim_align,
394
567
        } = int_size_align(tag);
395
567
        let mut case_size = ArchitectureSize::default();
396
567
        let mut case_align = Alignment::default();
397
567
        for ty in types {
398
0
            if let Some(ty) = ty {
399
0
                case_size = case_size.max(&self.size(ty));
400
0
                case_align = case_align.max(self.align(ty));
401
0
            }
402
        }
403
567
        let align = discrim_align.max(case_align);
404
567
        let discrim_aligned = align_to_arch(discrim_size, case_align);
405
567
        let size_sum = discrim_aligned + case_size;
406
567
        ElementInfo::new(align_to_arch(size_sum, align), align)
407
567
    }
<wit_parser::sizealign::SizeAlign>::variant::<[core::option::Option<&wit_parser::Type>; 1]>
Line
Count
Source
386
398
    fn variant<'a>(
387
398
        &self,
388
398
        tag: Int,
389
398
        types: impl IntoIterator<Item = Option<&'a Type>>,
390
398
    ) -> ElementInfo {
391
        let ElementInfo {
392
398
            size: discrim_size,
393
398
            align: discrim_align,
394
398
        } = int_size_align(tag);
395
398
        let mut case_size = ArchitectureSize::default();
396
398
        let mut case_align = Alignment::default();
397
398
        for ty in types {
398
398
            if let Some(ty) = ty {
399
398
                case_size = case_size.max(&self.size(ty));
400
398
                case_align = case_align.max(self.align(ty));
401
398
            }
402
        }
403
398
        let align = discrim_align.max(case_align);
404
398
        let discrim_aligned = align_to_arch(discrim_size, case_align);
405
398
        let size_sum = discrim_aligned + case_size;
406
398
        ElementInfo::new(align_to_arch(size_sum, align), align)
407
398
    }
<wit_parser::sizealign::SizeAlign>::variant::<[core::option::Option<&wit_parser::Type>; 2]>
Line
Count
Source
386
1.51k
    fn variant<'a>(
387
1.51k
        &self,
388
1.51k
        tag: Int,
389
1.51k
        types: impl IntoIterator<Item = Option<&'a Type>>,
390
1.51k
    ) -> ElementInfo {
391
        let ElementInfo {
392
1.51k
            size: discrim_size,
393
1.51k
            align: discrim_align,
394
1.51k
        } = int_size_align(tag);
395
1.51k
        let mut case_size = ArchitectureSize::default();
396
1.51k
        let mut case_align = Alignment::default();
397
3.03k
        for ty in types {
398
3.03k
            if let Some(ty) = ty {
399
2.63k
                case_size = case_size.max(&self.size(ty));
400
2.63k
                case_align = case_align.max(self.align(ty));
401
2.63k
            }
402
        }
403
1.51k
        let align = discrim_align.max(case_align);
404
1.51k
        let discrim_aligned = align_to_arch(discrim_size, case_align);
405
1.51k
        let size_sum = discrim_aligned + case_size;
406
1.51k
        ElementInfo::new(align_to_arch(size_sum, align), align)
407
1.51k
    }
<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
386
929
    fn variant<'a>(
387
929
        &self,
388
929
        tag: Int,
389
929
        types: impl IntoIterator<Item = Option<&'a Type>>,
390
929
    ) -> ElementInfo {
391
        let ElementInfo {
392
929
            size: discrim_size,
393
929
            align: discrim_align,
394
929
        } = int_size_align(tag);
395
929
        let mut case_size = ArchitectureSize::default();
396
929
        let mut case_align = Alignment::default();
397
3.98k
        for ty in types {
398
3.98k
            if let Some(ty) = ty {
399
3.35k
                case_size = case_size.max(&self.size(ty));
400
3.35k
                case_align = case_align.max(self.align(ty));
401
3.35k
            }
402
        }
403
929
        let align = discrim_align.max(case_align);
404
929
        let discrim_aligned = align_to_arch(discrim_size, case_align);
405
929
        let size_sum = discrim_aligned + case_size;
406
929
        ElementInfo::new(align_to_arch(size_sum, align), align)
407
929
    }
408
}
409
410
4.64k
fn int_size_align(i: Int) -> ElementInfo {
411
4.64k
    match i {
412
4.57k
        Int::U8 => Alignment::Bytes(NonZeroUsize::new(1).unwrap()),
413
15
        Int::U16 => Alignment::Bytes(NonZeroUsize::new(2).unwrap()),
414
57
        Int::U32 => Alignment::Bytes(NonZeroUsize::new(4).unwrap()),
415
0
        Int::U64 => Alignment::Bytes(NonZeroUsize::new(8).unwrap()),
416
    }
417
4.64k
    .into()
418
4.64k
}
419
420
/// Increase `val` to a multiple of `align`;
421
/// `align` must be a power of two
422
16.1k
pub(crate) fn align_to(val: usize, align: usize) -> usize {
423
16.1k
    (val + align - 1) & !(align - 1)
424
16.1k
}
425
426
/// Increase `val` to a multiple of `align`, with special handling for pointers;
427
/// `align` must be a power of two or `Alignment::Pointer`
428
15.8k
pub fn align_to_arch(val: ArchitectureSize, align: Alignment) -> ArchitectureSize {
429
15.8k
    match align {
430
        Alignment::Pointer => {
431
352
            let new32 = align_to(val.bytes, 4);
432
352
            if new32 != align_to(new32, 8) {
433
140
                ArchitectureSize::new(new32 - 4, val.pointers + 1)
434
            } else {
435
212
                ArchitectureSize::new(new32, val.pointers)
436
            }
437
        }
438
15.4k
        Alignment::Bytes(align_bytes) => {
439
15.4k
            let align_bytes = align_bytes.get();
440
15.4k
            if align_bytes > 4 && (val.pointers & 1) != 0 {
441
11
                let new_bytes = align_to(val.bytes, align_bytes);
442
11
                if (new_bytes - val.bytes) >= 4 {
443
                    // up to four extra bytes fit together with a the extra 32 bit pointer
444
                    // and the 64 bit pointer is always 8 bytes (so no change in value)
445
5
                    ArchitectureSize::new(new_bytes - 8, val.pointers + 1)
446
                } else {
447
                    // there is no room to combine, so the odd pointer aligns to 8 bytes
448
6
                    ArchitectureSize::new(new_bytes + 8, val.pointers - 1)
449
                }
450
            } else {
451
15.4k
                ArchitectureSize::new(align_to(val.bytes, align_bytes), val.pointers)
452
            }
453
        }
454
    }
455
15.8k
}
456
457
#[cfg(test)]
458
mod test {
459
    use super::*;
460
    use alloc::vec;
461
462
    #[test]
463
    fn align() {
464
        // u8 + ptr
465
        assert_eq!(
466
            align_to_arch(ArchitectureSize::new(1, 0), Alignment::Pointer),
467
            ArchitectureSize::new(0, 1)
468
        );
469
        // u8 + u64
470
        assert_eq!(
471
            align_to_arch(
472
                ArchitectureSize::new(1, 0),
473
                Alignment::Bytes(NonZeroUsize::new(8).unwrap())
474
            ),
475
            ArchitectureSize::new(8, 0)
476
        );
477
        // u8 + u32
478
        assert_eq!(
479
            align_to_arch(
480
                ArchitectureSize::new(1, 0),
481
                Alignment::Bytes(NonZeroUsize::new(4).unwrap())
482
            ),
483
            ArchitectureSize::new(4, 0)
484
        );
485
        // ptr + u64
486
        assert_eq!(
487
            align_to_arch(
488
                ArchitectureSize::new(0, 1),
489
                Alignment::Bytes(NonZeroUsize::new(8).unwrap())
490
            ),
491
            ArchitectureSize::new(8, 0)
492
        );
493
        // u32 + ptr
494
        assert_eq!(
495
            align_to_arch(ArchitectureSize::new(4, 0), Alignment::Pointer),
496
            ArchitectureSize::new(0, 1)
497
        );
498
        // u32, ptr + u64
499
        assert_eq!(
500
            align_to_arch(
501
                ArchitectureSize::new(0, 2),
502
                Alignment::Bytes(NonZeroUsize::new(8).unwrap())
503
            ),
504
            ArchitectureSize::new(0, 2)
505
        );
506
        // ptr, u8 + u64
507
        assert_eq!(
508
            align_to_arch(
509
                ArchitectureSize::new(1, 1),
510
                Alignment::Bytes(NonZeroUsize::new(8).unwrap())
511
            ),
512
            ArchitectureSize::new(0, 2)
513
        );
514
        // ptr, u8 + ptr
515
        assert_eq!(
516
            align_to_arch(ArchitectureSize::new(1, 1), Alignment::Pointer),
517
            ArchitectureSize::new(0, 2)
518
        );
519
        // ptr, ptr, u8 + u64
520
        assert_eq!(
521
            align_to_arch(
522
                ArchitectureSize::new(1, 2),
523
                Alignment::Bytes(NonZeroUsize::new(8).unwrap())
524
            ),
525
            ArchitectureSize::new(8, 2)
526
        );
527
        assert_eq!(
528
            align_to_arch(
529
                ArchitectureSize::new(30, 3),
530
                Alignment::Bytes(NonZeroUsize::new(8).unwrap())
531
            ),
532
            ArchitectureSize::new(40, 2)
533
        );
534
535
        assert_eq!(
536
            ArchitectureSize::new(12, 0).max(&ArchitectureSize::new(0, 2)),
537
            ArchitectureSize::new(8, 1)
538
        );
539
        assert_eq!(
540
            ArchitectureSize::new(10, 0).max(&ArchitectureSize::new(0, 2)),
541
            ArchitectureSize::new(8, 1)
542
        );
543
544
        assert_eq!(
545
            align_to_arch(
546
                ArchitectureSize::new(2, 0),
547
                Alignment::Bytes(NonZeroUsize::new(8).unwrap())
548
            ),
549
            ArchitectureSize::new(8, 0)
550
        );
551
        assert_eq!(
552
            align_to_arch(ArchitectureSize::new(2, 0), Alignment::Pointer),
553
            ArchitectureSize::new(0, 1)
554
        );
555
    }
556
557
    #[test]
558
    fn resource_size() {
559
        // keep it identical to the old behavior
560
        let obj = SizeAlign::default();
561
        let elem = obj.calculate(&TypeDef {
562
            name: None,
563
            kind: TypeDefKind::Resource,
564
            owner: crate::TypeOwner::None,
565
            docs: Default::default(),
566
            stability: Default::default(),
567
            span: Default::default(),
568
        });
569
        assert_eq!(elem.size, ArchitectureSize::new(usize::MAX, 0));
570
        assert_eq!(
571
            elem.align,
572
            Alignment::Bytes(NonZeroUsize::new(usize::MAX).unwrap())
573
        );
574
    }
575
    #[test]
576
    fn result_ptr_10() {
577
        let mut obj = SizeAlign::default();
578
        let mut resolve = Resolve::default();
579
        let tuple = crate::Tuple {
580
            types: vec![Type::U16, Type::U16, Type::U16, Type::U16, Type::U16],
581
        };
582
        let id = resolve.types.alloc(TypeDef {
583
            name: None,
584
            kind: TypeDefKind::Tuple(tuple),
585
            owner: crate::TypeOwner::None,
586
            docs: Default::default(),
587
            stability: Default::default(),
588
            span: Default::default(),
589
        });
590
        obj.fill(&resolve);
591
        let my_result = crate::Result_ {
592
            ok: Some(Type::String),
593
            err: Some(Type::Id(id)),
594
        };
595
        let elem = obj.calculate(&TypeDef {
596
            name: None,
597
            kind: TypeDefKind::Result(my_result),
598
            owner: crate::TypeOwner::None,
599
            docs: Default::default(),
600
            stability: Default::default(),
601
            span: Default::default(),
602
        });
603
        assert_eq!(elem.size, ArchitectureSize::new(8, 2));
604
        assert_eq!(elem.align, Alignment::Pointer);
605
    }
606
    #[test]
607
    fn result_ptr_64bit() {
608
        let obj = SizeAlign::default();
609
        let my_record = crate::Record {
610
            fields: vec![
611
                crate::Field {
612
                    name: String::new(),
613
                    ty: Type::String,
614
                    docs: Default::default(),
615
                    span: Default::default(),
616
                },
617
                crate::Field {
618
                    name: String::new(),
619
                    ty: Type::U64,
620
                    docs: Default::default(),
621
                    span: Default::default(),
622
                },
623
            ],
624
        };
625
        let elem = obj.calculate(&TypeDef {
626
            name: None,
627
            kind: TypeDefKind::Record(my_record),
628
            owner: crate::TypeOwner::None,
629
            docs: Default::default(),
630
            stability: Default::default(),
631
            span: Default::default(),
632
        });
633
        assert_eq!(elem.size, ArchitectureSize::new(8, 2));
634
        assert_eq!(elem.align, Alignment::Bytes(NonZeroUsize::new(8).unwrap()));
635
    }
636
}