1 package types
2 3 // Type layout for unsafe.Sizeof/Alignof/Offsetof.
4 //
5 // The type checker's const evaluator and the SSA builder's const folder both
6 // need byte sizes, alignments, and struct field offsets. This is the single
7 // source of truth for both. Layout follows the Go rules for the Moxie type
8 // set: string/slice are three words, interface and func values are two words,
9 // map/chan/pointer are one word, arrays and structs are laid out with
10 // per-field alignment padding.
11 //
12 // Words are the TARGET's width, not the host's. Codegen places struct fields
13 // with LLVM's target data, so a folded unsafe.Sizeof/Offsetof that assumed
14 // 64-bit pointers would disagree with the addresses the same compiler emits;
15 // on wasm32 that made unsafe.Sizeof of a two-pointer struct 16 instead of 8
16 // while the struct itself was 8 bytes.
17 18 import "git.smesh.lol/moxie/pkg/mxutil"
19 20 // ptrSize is the target's pointer width in bytes. Only wasm is 32-bit among
21 // the targets this compiler emits.
22 func ptrSize() (n int64) {
23 if mxutil.TargetArch() == "wasm" {
24 return 4
25 }
26 return 8
27 }
28 29 func alignUp(n, a int64) (r int64) {
30 if a <= 1 {
31 return n
32 }
33 rem := n % a
34 if rem == 0 {
35 return n
36 }
37 return n + (a - rem)
38 }
39 40 func basicSize(k BasicKind) (sz int64) {
41 switch k {
42 case Bool, Int8, Uint8:
43 return 1
44 case Int16, Uint16:
45 return 2
46 case Int32, Uint32, Float32:
47 return 4
48 case Int64, Uint64, Float64:
49 return 8
50 case Uintptr, UnsafePointer:
51 return ptrSize()
52 case TCString, UntypedString:
53 return ptrSize() * 3
54 }
55 return 8
56 }
57 58 func basicAlign(k BasicKind) (a int64) {
59 switch k {
60 case Bool, Int8, Uint8:
61 return 1
62 case Int16, Uint16:
63 return 2
64 case Int32, Uint32, Float32:
65 return 4
66 case Int64, Uint64, Float64:
67 // 64-bit scalars stay 8-aligned on the wasm32 target too: the
68 // alignment the backend uses for i64 is 8, whatever the pointer
69 // width is.
70 return 8
71 case Uintptr, UnsafePointer, TCString, UntypedString:
72 return ptrSize()
73 }
74 return 8
75 }
76 77 // TypeSize returns the byte size of t.
78 func TypeSize(t Type) (sz int64) {
79 u := SafeUnderlying(t)
80 if u == nil {
81 return ptrSize()
82 }
83 switch v := u.(type) {
84 case *Basic:
85 return basicSize(v.Kind)
86 case *Pointer:
87 return ptrSize()
88 case *Slice:
89 return ptrSize() * 3
90 case *Signature:
91 return ptrSize() * 2
92 case *TCInterface:
93 return ptrSize() * 2
94 case *TCMap, *TCChan:
95 return ptrSize()
96 case *Array:
97 return TypeSize(v.Elem) * v.Len
98 case *TCStruct:
99 total := int64(0)
100 maxa := int64(1)
101 for i := int32(0); i < v.NumFields(); i++ {
102 ft := v.Field(i).Typ
103 a := TypeAlign(ft)
104 if a > maxa {
105 maxa = a
106 }
107 total = alignUp(total, a)
108 total += TypeSize(ft)
109 }
110 return alignUp(total, maxa)
111 }
112 return ptrSize()
113 }
114 115 // TypeAlign returns the alignment of t.
116 func TypeAlign(t Type) (a int64) {
117 u := SafeUnderlying(t)
118 if u == nil {
119 return ptrSize()
120 }
121 switch v := u.(type) {
122 case *Basic:
123 return basicAlign(v.Kind)
124 case *Array:
125 return TypeAlign(v.Elem)
126 case *TCStruct:
127 a = 1
128 for i := int32(0); i < v.NumFields(); i++ {
129 fa := TypeAlign(v.Field(i).Typ)
130 if fa > a {
131 a = fa
132 }
133 }
134 return a
135 }
136 return ptrSize()
137 }
138 139 // StructFieldOffset returns the byte offset of field idx in st, following the
140 // same layout TypeSize uses.
141 func StructFieldOffset(st *TCStruct, idx int32) (off int64) {
142 off = 0
143 for i := int32(0); i < idx && i < st.NumFields(); i++ {
144 ft := st.Field(i).Typ
145 off = alignUp(off, TypeAlign(ft))
146 off += TypeSize(ft)
147 }
148 if idx >= 0 && idx < st.NumFields() {
149 off = alignUp(off, TypeAlign(st.Field(idx).Typ))
150 }
151 return off
152 }
153