]>
Commit | Line | Data |
---|---|---|
af146490 ILT |
1 | // Copyright 2013 The Go Authors. All rights reserved. |
2 | // Use of this source code is governed by a BSD-style | |
3 | // license that can be found in the LICENSE file. | |
4 | ||
5 | // This file implements Sizes. | |
6 | ||
7 | package types | |
8 | ||
9 | // Sizes defines the sizing functions for package unsafe. | |
10 | type Sizes interface { | |
11 | // Alignof returns the alignment of a variable of type T. | |
12 | // Alignof must implement the alignment guarantees required by the spec. | |
13 | Alignof(T Type) int64 | |
14 | ||
15 | // Offsetsof returns the offsets of the given struct fields, in bytes. | |
16 | // Offsetsof must implement the offset guarantees required by the spec. | |
17 | Offsetsof(fields []*Var) []int64 | |
18 | ||
19 | // Sizeof returns the size of a variable of type T. | |
20 | // Sizeof must implement the size guarantees required by the spec. | |
21 | Sizeof(T Type) int64 | |
22 | } | |
23 | ||
24 | // StdSizes is a convenience type for creating commonly used Sizes. | |
25 | // It makes the following simplifying assumptions: | |
26 | // | |
27 | // - The size of explicitly sized basic types (int16, etc.) is the | |
28 | // specified size. | |
29 | // - The size of strings and interfaces is 2*WordSize. | |
30 | // - The size of slices is 3*WordSize. | |
31 | // - The size of an array of n elements corresponds to the size of | |
32 | // a struct of n consecutive fields of the array's element type. | |
33 | // - The size of a struct is the offset of the last field plus that | |
34 | // field's size. As with all element types, if the struct is used | |
35 | // in an array its size must first be aligned to a multiple of the | |
36 | // struct's alignment. | |
37 | // - All other types have size WordSize. | |
38 | // - Arrays and structs are aligned per spec definition; all other | |
39 | // types are naturally aligned with a maximum alignment MaxAlign. | |
40 | // | |
41 | // *StdSizes implements Sizes. | |
42 | // | |
43 | type StdSizes struct { | |
44 | WordSize int64 // word size in bytes - must be >= 4 (32bits) | |
45 | MaxAlign int64 // maximum alignment in bytes - must be >= 1 | |
46 | } | |
47 | ||
48 | func (s *StdSizes) Alignof(T Type) int64 { | |
49 | // For arrays and structs, alignment is defined in terms | |
50 | // of alignment of the elements and fields, respectively. | |
51 | switch t := T.Underlying().(type) { | |
52 | case *Array: | |
53 | // spec: "For a variable x of array type: unsafe.Alignof(x) | |
54 | // is the same as unsafe.Alignof(x[0]), but at least 1." | |
55 | return s.Alignof(t.elem) | |
56 | case *Struct: | |
57 | // spec: "For a variable x of struct type: unsafe.Alignof(x) | |
58 | // is the largest of the values unsafe.Alignof(x.f) for each | |
59 | // field f of x, but at least 1." | |
60 | max := int64(1) | |
61 | for _, f := range t.fields { | |
62 | if a := s.Alignof(f.typ); a > max { | |
63 | max = a | |
64 | } | |
65 | } | |
66 | return max | |
c2047754 ILT |
67 | case *Slice, *Interface: |
68 | // Multiword data structures are effectively structs | |
69 | // in which each element has size WordSize. | |
70 | return s.WordSize | |
71 | case *Basic: | |
72 | // Strings are like slices and interfaces. | |
73 | if t.Info()&IsString != 0 { | |
74 | return s.WordSize | |
75 | } | |
af146490 ILT |
76 | } |
77 | a := s.Sizeof(T) // may be 0 | |
78 | // spec: "For a variable x of any type: unsafe.Alignof(x) is at least 1." | |
79 | if a < 1 { | |
80 | return 1 | |
81 | } | |
c2047754 ILT |
82 | // complex{64,128} are aligned like [2]float{32,64}. |
83 | if isComplex(T) { | |
84 | a /= 2 | |
85 | } | |
af146490 ILT |
86 | if a > s.MaxAlign { |
87 | return s.MaxAlign | |
88 | } | |
89 | return a | |
90 | } | |
91 | ||
92 | func (s *StdSizes) Offsetsof(fields []*Var) []int64 { | |
93 | offsets := make([]int64, len(fields)) | |
94 | var o int64 | |
95 | for i, f := range fields { | |
96 | a := s.Alignof(f.typ) | |
97 | o = align(o, a) | |
98 | offsets[i] = o | |
99 | o += s.Sizeof(f.typ) | |
100 | } | |
101 | return offsets | |
102 | } | |
103 | ||
104 | var basicSizes = [...]byte{ | |
105 | Bool: 1, | |
106 | Int8: 1, | |
107 | Int16: 2, | |
108 | Int32: 4, | |
109 | Int64: 8, | |
110 | Uint8: 1, | |
111 | Uint16: 2, | |
112 | Uint32: 4, | |
113 | Uint64: 8, | |
114 | Float32: 4, | |
115 | Float64: 8, | |
116 | Complex64: 8, | |
117 | Complex128: 16, | |
118 | } | |
119 | ||
120 | func (s *StdSizes) Sizeof(T Type) int64 { | |
121 | switch t := T.Underlying().(type) { | |
122 | case *Basic: | |
123 | assert(isTyped(T)) | |
124 | k := t.kind | |
125 | if int(k) < len(basicSizes) { | |
126 | if s := basicSizes[k]; s > 0 { | |
127 | return int64(s) | |
128 | } | |
129 | } | |
130 | if k == String { | |
131 | return s.WordSize * 2 | |
132 | } | |
133 | case *Array: | |
134 | n := t.len | |
135 | if n == 0 { | |
136 | return 0 | |
137 | } | |
138 | a := s.Alignof(t.elem) | |
139 | z := s.Sizeof(t.elem) | |
140 | return align(z, a)*(n-1) + z | |
141 | case *Slice: | |
142 | return s.WordSize * 3 | |
143 | case *Struct: | |
144 | n := t.NumFields() | |
145 | if n == 0 { | |
146 | return 0 | |
147 | } | |
c2047754 ILT |
148 | offsets := s.Offsetsof(t.fields) |
149 | return offsets[n-1] + s.Sizeof(t.fields[n-1].typ) | |
af146490 ILT |
150 | case *Interface: |
151 | return s.WordSize * 2 | |
152 | } | |
153 | return s.WordSize // catch-all | |
154 | } | |
155 | ||
bc998d03 ILT |
156 | // common architecture word sizes and alignments |
157 | var gcArchSizes = map[string]*StdSizes{ | |
158 | "386": {4, 4}, | |
159 | "arm": {4, 4}, | |
160 | "arm64": {8, 8}, | |
161 | "amd64": {8, 8}, | |
162 | "amd64p32": {4, 8}, | |
163 | "mips": {4, 4}, | |
164 | "mipsle": {4, 4}, | |
165 | "mips64": {8, 8}, | |
166 | "mips64le": {8, 8}, | |
167 | "ppc64": {8, 8}, | |
168 | "ppc64le": {8, 8}, | |
169 | "s390x": {8, 8}, | |
87cbbc45 | 170 | "wasm": {8, 8}, |
bc998d03 ILT |
171 | // When adding more architectures here, |
172 | // update the doc string of SizesFor below. | |
173 | } | |
174 | ||
175 | // SizesFor returns the Sizes used by a compiler for an architecture. | |
176 | // The result is nil if a compiler/architecture pair is not known. | |
177 | // | |
178 | // Supported architectures for compiler "gc": | |
179 | // "386", "arm", "arm64", "amd64", "amd64p32", "mips", "mipsle", | |
87cbbc45 | 180 | // "mips64", "mips64le", "ppc64", "ppc64le", "s390x", "wasm". |
bc998d03 | 181 | func SizesFor(compiler, arch string) Sizes { |
ffad1c54 ILT |
182 | var m map[string]*StdSizes |
183 | switch compiler { | |
184 | case "gc": | |
185 | m = gcArchSizes | |
186 | case "gccgo": | |
187 | m = gccgoArchSizes | |
188 | default: | |
bc998d03 ILT |
189 | return nil |
190 | } | |
ffad1c54 | 191 | s, ok := m[arch] |
bc998d03 ILT |
192 | if !ok { |
193 | return nil | |
194 | } | |
195 | return s | |
196 | } | |
197 | ||
af146490 | 198 | // stdSizes is used if Config.Sizes == nil. |
bc998d03 | 199 | var stdSizes = SizesFor("gc", "amd64") |
af146490 ILT |
200 | |
201 | func (conf *Config) alignof(T Type) int64 { | |
202 | if s := conf.Sizes; s != nil { | |
203 | if a := s.Alignof(T); a >= 1 { | |
204 | return a | |
205 | } | |
206 | panic("Config.Sizes.Alignof returned an alignment < 1") | |
207 | } | |
208 | return stdSizes.Alignof(T) | |
209 | } | |
210 | ||
211 | func (conf *Config) offsetsof(T *Struct) []int64 { | |
f98dd1a3 ILT |
212 | var offsets []int64 |
213 | if T.NumFields() > 0 { | |
af146490 ILT |
214 | // compute offsets on demand |
215 | if s := conf.Sizes; s != nil { | |
c2047754 ILT |
216 | offsets = s.Offsetsof(T.fields) |
217 | // sanity checks | |
218 | if len(offsets) != T.NumFields() { | |
219 | panic("Config.Sizes.Offsetsof returned the wrong number of offsets") | |
220 | } | |
221 | for _, o := range offsets { | |
222 | if o < 0 { | |
223 | panic("Config.Sizes.Offsetsof returned an offset < 0") | |
af146490 ILT |
224 | } |
225 | } | |
226 | } else { | |
c2047754 | 227 | offsets = stdSizes.Offsetsof(T.fields) |
af146490 | 228 | } |
af146490 ILT |
229 | } |
230 | return offsets | |
231 | } | |
232 | ||
233 | // offsetof returns the offset of the field specified via | |
234 | // the index sequence relative to typ. All embedded fields | |
235 | // must be structs (rather than pointer to structs). | |
236 | func (conf *Config) offsetof(typ Type, index []int) int64 { | |
237 | var o int64 | |
238 | for _, i := range index { | |
239 | s := typ.Underlying().(*Struct) | |
240 | o += conf.offsetsof(s)[i] | |
241 | typ = s.fields[i].typ | |
242 | } | |
243 | return o | |
244 | } | |
245 | ||
246 | func (conf *Config) sizeof(T Type) int64 { | |
247 | if s := conf.Sizes; s != nil { | |
248 | if z := s.Sizeof(T); z >= 0 { | |
249 | return z | |
250 | } | |
251 | panic("Config.Sizes.Sizeof returned a size < 0") | |
252 | } | |
253 | return stdSizes.Sizeof(T) | |
254 | } | |
255 | ||
256 | // align returns the smallest y >= x such that y % a == 0. | |
257 | func align(x, a int64) int64 { | |
258 | y := x + a - 1 | |
259 | return y - y%a | |
260 | } |