1use alloc::format;
2use alloc::string::String;
3use alloc::vec::Vec;
4use core::{
5 cmp::Ordering,
6 num::NonZeroUsize,
7 ops::{Add, AddAssign},
8};
9
10use crate::{FlagsRepr, Int, Resolve, Type, TypeDef, TypeDefKind};
11
12#[derive(Eq, PartialEq, Clone, Copy)]
14pub enum Alignment {
15 Pointer,
17 Bytes(NonZeroUsize),
19}
20
21impl Default for Alignment {
22 fn default() -> Self {
23 Alignment::Bytes(NonZeroUsize::new(1).unwrap())
24 }
25}
26
27impl core::fmt::Debug for Alignment {
28 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
29 match self {
30 Alignment::Pointer => f.write_str("ptr"),
31 Alignment::Bytes(b) => f.write_fmt(format_args!("{}", b.get())),
32 }
33 }
34}
35
36impl PartialOrd for Alignment {
37 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
38 Some(self.cmp(other))
39 }
40}
41
42impl Ord for Alignment {
43 fn cmp(&self, other: &Self) -> Ordering {
47 match (self, other) {
48 (Alignment::Pointer, Alignment::Pointer) => Ordering::Equal,
49 (Alignment::Pointer, Alignment::Bytes(b)) => {
50 if b.get() > 4 {
51 Ordering::Less
52 } else {
53 Ordering::Greater
54 }
55 }
56 (Alignment::Bytes(b), Alignment::Pointer) => {
57 if b.get() > 4 {
58 Ordering::Greater
59 } else {
60 Ordering::Less
61 }
62 }
63 (Alignment::Bytes(a), Alignment::Bytes(b)) => a.cmp(b),
64 }
65 }
66}
67
68impl Alignment {
69 pub fn align_wasm32(&self) -> usize {
71 match self {
72 Alignment::Pointer => 4,
73 Alignment::Bytes(bytes) => bytes.get(),
74 }
75 }
76
77 pub fn align_wasm64(&self) -> usize {
78 match self {
79 Alignment::Pointer => 8,
80 Alignment::Bytes(bytes) => bytes.get(),
81 }
82 }
83
84 pub fn format(&self, ptrsize_expr: &str) -> String {
85 match self {
86 Alignment::Pointer => ptrsize_expr.into(),
87 Alignment::Bytes(bytes) => format!("{}", bytes.get()),
88 }
89 }
90}
91
92#[derive(Default, Clone, Copy, Eq, PartialEq)]
96pub struct ArchitectureSize {
97 pub bytes: usize,
99 pub pointers: usize,
101}
102
103impl Add<ArchitectureSize> for ArchitectureSize {
104 type Output = ArchitectureSize;
105
106 fn add(self, rhs: ArchitectureSize) -> Self::Output {
107 ArchitectureSize::new(self.bytes + rhs.bytes, self.pointers + rhs.pointers)
108 }
109}
110
111impl AddAssign<ArchitectureSize> for ArchitectureSize {
112 fn add_assign(&mut self, rhs: ArchitectureSize) {
113 self.bytes += rhs.bytes;
114 self.pointers += rhs.pointers;
115 }
116}
117
118impl From<Alignment> for ArchitectureSize {
119 fn from(align: Alignment) -> Self {
120 match align {
121 Alignment::Bytes(bytes) => ArchitectureSize::new(bytes.get(), 0),
122 Alignment::Pointer => ArchitectureSize::new(0, 1),
123 }
124 }
125}
126
127impl core::fmt::Debug for ArchitectureSize {
128 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
129 f.write_str(&self.format("ptrsz"))
130 }
131}
132
133impl ArchitectureSize {
134 pub fn new(bytes: usize, pointers: usize) -> Self {
135 Self { bytes, pointers }
136 }
137
138 pub fn max<B: core::borrow::Borrow<Self>>(&self, other: B) -> Self {
139 let other = other.borrow();
140 let self32 = self.size_wasm32();
141 let self64 = self.size_wasm64();
142 let other32 = other.size_wasm32();
143 let other64 = other.size_wasm64();
144 if self32 >= other32 && self64 >= other64 {
145 *self
146 } else if self32 <= other32 && self64 <= other64 {
147 *other
148 } else {
149 let new32 = align_to(self32.max(other32), 4);
151 let new64 = align_to(self64.max(other64), 8);
152 ArchitectureSize::new(new32 + new32 - new64, (new64 - new32) / 4)
153 }
154 }
155
156 pub fn add_bytes(&self, b: usize) -> Self {
157 Self::new(self.bytes + b, self.pointers)
158 }
159
160 pub fn constant_bytes(&self) -> usize {
163 self.bytes
164 }
165
166 pub fn pointers_to_add(&self) -> usize {
167 self.pointers
168 }
169
170 pub fn size_wasm32(&self) -> usize {
172 self.bytes + self.pointers * 4
173 }
174
175 pub fn size_wasm64(&self) -> usize {
176 self.bytes + self.pointers * 8
177 }
178
179 pub fn is_empty(&self) -> bool {
181 self.bytes == 0 && self.pointers == 0
182 }
183
184 pub fn format(&self, ptrsize_expr: &str) -> String {
186 self.format_term(ptrsize_expr, false)
187 }
188
189 pub fn format_term(&self, ptrsize_expr: &str, suppress_brackets: bool) -> String {
192 if self.pointers != 0 {
193 if self.bytes > 0 {
194 if suppress_brackets {
196 format!(
197 "{}+{}*{ptrsize_expr}",
198 self.constant_bytes(),
199 self.pointers_to_add()
200 )
201 } else {
202 format!(
203 "({}+{}*{ptrsize_expr})",
204 self.constant_bytes(),
205 self.pointers_to_add()
206 )
207 }
208 } else if self.pointers == 1 {
209 ptrsize_expr.into()
211 } else {
212 if suppress_brackets {
214 format!("{}*{ptrsize_expr}", self.pointers_to_add())
215 } else {
216 format!("({}*{ptrsize_expr})", self.pointers_to_add())
217 }
218 }
219 } else {
220 format!("{}", self.constant_bytes())
222 }
223 }
224}
225
226#[derive(Default)]
228pub struct ElementInfo {
229 pub size: ArchitectureSize,
230 pub align: Alignment,
231}
232
233impl From<Alignment> for ElementInfo {
234 fn from(align: Alignment) -> Self {
235 ElementInfo {
236 size: align.into(),
237 align,
238 }
239 }
240}
241
242impl ElementInfo {
243 fn new(size: ArchitectureSize, align: Alignment) -> Self {
244 Self { size, align }
245 }
246}
247
248#[derive(Default)]
250pub struct SizeAlign {
251 map: Vec<ElementInfo>,
252}
253
254impl SizeAlign {
255 pub fn fill(&mut self, resolve: &Resolve) -> anyhow::Result<()> {
256 self.map = Vec::new();
257 for (_, ty) in resolve.types.iter() {
258 let pair = self.calculate(ty)?;
259 self.map.push(pair);
260 }
261 Ok(())
262 }
263
264 fn calculate(&self, ty: &TypeDef) -> anyhow::Result<ElementInfo> {
265 Ok(match &ty.kind {
266 TypeDefKind::Type(t) => ElementInfo::new(self.size(t), self.align(t)),
267 TypeDefKind::FixedLengthList(t, size) => {
268 let field_align = self.align(t);
269 let field_size = self.size(t);
270 let bytes = field_size.bytes.checked_mul(*size as usize).ok_or_else(|| {
271 anyhow::anyhow!(
272 "size of fixed-length list of {size} elements overflows the target architecture's address space"
273 )
274 })?;
275 let pointers = field_size.pointers.checked_mul(*size as usize).ok_or_else(|| {
276 anyhow::anyhow!(
277 "size of fixed-length list of {size} elements overflows the target architecture's address space"
278 )
279 })?;
280 ElementInfo::new(ArchitectureSize::new(bytes, pointers), field_align)
281 }
282 TypeDefKind::List(_) => {
283 ElementInfo::new(ArchitectureSize::new(0, 2), Alignment::Pointer)
284 }
285 TypeDefKind::Map(_, _) => {
286 ElementInfo::new(ArchitectureSize::new(0, 2), Alignment::Pointer)
287 }
288 TypeDefKind::Record(r) => self.record(r.fields.iter().map(|f| &f.ty)),
289 TypeDefKind::Tuple(t) => self.record(t.types.iter()),
290 TypeDefKind::Flags(f) => match f.repr() {
291 FlagsRepr::U8 => int_size_align(Int::U8),
292 FlagsRepr::U16 => int_size_align(Int::U16),
293 FlagsRepr::U32(n) => ElementInfo::new(
294 ArchitectureSize::new(n * 4, 0),
295 Alignment::Bytes(NonZeroUsize::new(4).unwrap()),
296 ),
297 },
298 TypeDefKind::Variant(v) => self.variant(v.tag(), v.cases.iter().map(|c| c.ty.as_ref())),
299 TypeDefKind::Enum(e) => self.variant(e.tag(), []),
300 TypeDefKind::Option(t) => self.variant(Int::U8, [Some(t)]),
301 TypeDefKind::Result(r) => self.variant(Int::U8, [r.ok.as_ref(), r.err.as_ref()]),
302 TypeDefKind::Handle(_) | TypeDefKind::Future(_) | TypeDefKind::Stream(_) => {
307 int_size_align(Int::U32)
308 }
309 TypeDefKind::Resource => ElementInfo::new(
312 ArchitectureSize::new(usize::MAX, 0),
313 Alignment::Bytes(NonZeroUsize::new(usize::MAX).unwrap()),
314 ),
315 TypeDefKind::Unknown => unreachable!(),
316 })
317 }
318
319 pub fn size(&self, ty: &Type) -> ArchitectureSize {
320 match ty {
321 Type::Bool | Type::U8 | Type::S8 => ArchitectureSize::new(1, 0),
322 Type::U16 | Type::S16 => ArchitectureSize::new(2, 0),
323 Type::U32 | Type::S32 | Type::F32 | Type::Char | Type::ErrorContext => {
324 ArchitectureSize::new(4, 0)
325 }
326 Type::U64 | Type::S64 | Type::F64 => ArchitectureSize::new(8, 0),
327 Type::String => ArchitectureSize::new(0, 2),
328 Type::Id(id) => self.map[id.index()].size,
329 }
330 }
331
332 pub fn align(&self, ty: &Type) -> Alignment {
333 match ty {
334 Type::Bool | Type::U8 | Type::S8 => Alignment::Bytes(NonZeroUsize::new(1).unwrap()),
335 Type::U16 | Type::S16 => Alignment::Bytes(NonZeroUsize::new(2).unwrap()),
336 Type::U32 | Type::S32 | Type::F32 | Type::Char | Type::ErrorContext => {
337 Alignment::Bytes(NonZeroUsize::new(4).unwrap())
338 }
339 Type::U64 | Type::S64 | Type::F64 => Alignment::Bytes(NonZeroUsize::new(8).unwrap()),
340 Type::String => Alignment::Pointer,
341 Type::Id(id) => self.map[id.index()].align,
342 }
343 }
344
345 pub fn field_offsets<'a>(
346 &self,
347 types: impl IntoIterator<Item = &'a Type>,
348 ) -> Vec<(ArchitectureSize, &'a Type)> {
349 let mut cur = ArchitectureSize::default();
350 types
351 .into_iter()
352 .map(|ty| {
353 let ret = align_to_arch(cur, self.align(ty));
354 cur = ret + self.size(ty);
355 (ret, ty)
356 })
357 .collect()
358 }
359
360 pub fn payload_offset<'a>(
361 &self,
362 tag: Int,
363 cases: impl IntoIterator<Item = Option<&'a Type>>,
364 ) -> ArchitectureSize {
365 let mut max_align = Alignment::default();
366 for ty in cases {
367 if let Some(ty) = ty {
368 max_align = max_align.max(self.align(ty));
369 }
370 }
371 let tag_size = int_size_align(tag).size;
372 align_to_arch(tag_size, max_align)
373 }
374
375 pub fn record<'a>(&self, types: impl IntoIterator<Item = &'a Type>) -> ElementInfo {
376 let mut size = ArchitectureSize::default();
377 let mut align = Alignment::default();
378 for ty in types {
379 let field_size = self.size(ty);
380 let field_align = self.align(ty);
381 size = align_to_arch(size, field_align) + field_size;
382 align = align.max(field_align);
383 }
384 ElementInfo::new(align_to_arch(size, align), align)
385 }
386
387 pub fn params<'a>(&self, types: impl IntoIterator<Item = &'a Type>) -> ElementInfo {
388 self.record(types.into_iter())
389 }
390
391 fn variant<'a>(
392 &self,
393 tag: Int,
394 types: impl IntoIterator<Item = Option<&'a Type>>,
395 ) -> ElementInfo {
396 let ElementInfo {
397 size: discrim_size,
398 align: discrim_align,
399 } = int_size_align(tag);
400 let mut case_size = ArchitectureSize::default();
401 let mut case_align = Alignment::default();
402 for ty in types {
403 if let Some(ty) = ty {
404 case_size = case_size.max(&self.size(ty));
405 case_align = case_align.max(self.align(ty));
406 }
407 }
408 let align = discrim_align.max(case_align);
409 let discrim_aligned = align_to_arch(discrim_size, case_align);
410 let size_sum = discrim_aligned + case_size;
411 ElementInfo::new(align_to_arch(size_sum, align), align)
412 }
413}
414
415fn int_size_align(i: Int) -> ElementInfo {
416 match i {
417 Int::U8 => Alignment::Bytes(NonZeroUsize::new(1).unwrap()),
418 Int::U16 => Alignment::Bytes(NonZeroUsize::new(2).unwrap()),
419 Int::U32 => Alignment::Bytes(NonZeroUsize::new(4).unwrap()),
420 Int::U64 => Alignment::Bytes(NonZeroUsize::new(8).unwrap()),
421 }
422 .into()
423}
424
425pub(crate) fn align_to(val: usize, align: usize) -> usize {
428 (val + align - 1) & !(align - 1)
429}
430
431pub fn align_to_arch(val: ArchitectureSize, align: Alignment) -> ArchitectureSize {
434 match align {
435 Alignment::Pointer => {
436 let new32 = align_to(val.bytes, 4);
437 if new32 != align_to(new32, 8) {
438 ArchitectureSize::new(new32 - 4, val.pointers + 1)
439 } else {
440 ArchitectureSize::new(new32, val.pointers)
441 }
442 }
443 Alignment::Bytes(align_bytes) => {
444 let align_bytes = align_bytes.get();
445 if align_bytes > 4 && (val.pointers & 1) != 0 {
446 let new_bytes = align_to(val.bytes, align_bytes);
447 if (new_bytes - val.bytes) >= 4 {
448 ArchitectureSize::new(new_bytes - 8, val.pointers + 1)
451 } else {
452 ArchitectureSize::new(new_bytes + 8, val.pointers - 1)
454 }
455 } else {
456 ArchitectureSize::new(align_to(val.bytes, align_bytes), val.pointers)
457 }
458 }
459 }
460}
461
462#[cfg(test)]
463mod test {
464 use super::*;
465 use alloc::string::ToString;
466 use alloc::vec;
467
468 #[test]
469 fn align() {
470 assert_eq!(
472 align_to_arch(ArchitectureSize::new(1, 0), Alignment::Pointer),
473 ArchitectureSize::new(0, 1)
474 );
475 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 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 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 assert_eq!(
501 align_to_arch(ArchitectureSize::new(4, 0), Alignment::Pointer),
502 ArchitectureSize::new(0, 1)
503 );
504 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 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 assert_eq!(
522 align_to_arch(ArchitectureSize::new(1, 1), Alignment::Pointer),
523 ArchitectureSize::new(0, 2)
524 );
525 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 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 let mut obj = SizeAlign::default();
626 let mut resolve = Resolve::default();
627
628 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 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 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}