github.com/prysmaticlabs/prysm@v1.4.4/tools/analyzers/maligned/maligned.go (about) 1 // Originally from https://github.com/mdempsky/maligned, adapted to work with nogo. 2 3 // Copyright 2013 The Go Authors. All rights reserved. 4 // Use of this source code is governed by a BSD-style 5 // license that can be found in the LICENSE file. 6 7 package maligned 8 9 import ( 10 "fmt" 11 "go/build" 12 "go/token" 13 "go/types" 14 "sort" 15 "strings" 16 ) 17 18 func malign(_ token.Pos, str *types.Struct) error { 19 wordSize := int64(8) 20 maxAlign := int64(8) 21 switch build.Default.GOARCH { 22 case "386", "arm": 23 wordSize, maxAlign = 4, 4 24 case "amd64p32": 25 wordSize = 4 26 } 27 28 s := gcSizes{wordSize, maxAlign} 29 sz, opt := s.Sizeof(str), optimalSize(str, &s) 30 if sz != opt { 31 return fmt.Errorf("maligned struct of size %d could be %d if fields were sorted by type. See: https://bit.ly/malignedstruct\n\nOptimal sort order is\n%s", sz, opt, optimalSort(s, str)) 32 } 33 return nil 34 } 35 36 func optimalSort(sizes gcSizes, str *types.Struct) string { 37 nf := str.NumFields() 38 fields := make([]*types.Var, nf) 39 alignofs := make([]int64, nf) 40 sizeofs := make([]int64, nf) 41 for i := 0; i < nf; i++ { 42 fields[i] = str.Field(i) 43 ft := fields[i].Type() 44 alignofs[i] = sizes.Alignof(ft) 45 sizeofs[i] = sizes.Sizeof(ft) 46 } 47 sort.Sort(&byAlignAndSize{fields, alignofs, sizeofs}) 48 // Reverse fields 49 for left, right := 0, len(fields)-1; left < right; left, right = left+1, right-1 { 50 fields[left], fields[right] = fields[right], fields[left] 51 } 52 ss := make([]string, nf) 53 for i, f := range fields { 54 ss[i] = fmt.Sprintf(" %s %s", f.Name(), f.Type().String()) 55 } 56 57 return strings.Join(ss, "\n") 58 } 59 60 func optimalSize(str *types.Struct, sizes *gcSizes) int64 { 61 nf := str.NumFields() 62 fields := make([]*types.Var, nf) 63 alignofs := make([]int64, nf) 64 sizeofs := make([]int64, nf) 65 for i := 0; i < nf; i++ { 66 fields[i] = str.Field(i) 67 ft := fields[i].Type() 68 alignofs[i] = sizes.Alignof(ft) 69 sizeofs[i] = sizes.Sizeof(ft) 70 } 71 sort.Sort(&byAlignAndSize{fields, alignofs, sizeofs}) 72 return sizes.Sizeof(types.NewStruct(fields, nil)) 73 } 74 75 type byAlignAndSize struct { 76 fields []*types.Var 77 alignofs []int64 78 sizeofs []int64 79 } 80 81 // Len -- 82 func (s *byAlignAndSize) Len() int { return len(s.fields) } 83 84 // Swap -- 85 func (s *byAlignAndSize) Swap(i, j int) { 86 s.fields[i], s.fields[j] = s.fields[j], s.fields[i] 87 s.alignofs[i], s.alignofs[j] = s.alignofs[j], s.alignofs[i] 88 s.sizeofs[i], s.sizeofs[j] = s.sizeofs[j], s.sizeofs[i] 89 } 90 91 // Less -- 92 func (s *byAlignAndSize) Less(i, j int) bool { 93 // Place zero sized objects before non-zero sized objects. 94 if s.sizeofs[i] == 0 && s.sizeofs[j] != 0 { 95 return true 96 } 97 if s.sizeofs[j] == 0 && s.sizeofs[i] != 0 { 98 return false 99 } 100 101 // Next, place more tightly aligned objects before less tightly aligned objects. 102 if s.alignofs[i] != s.alignofs[j] { 103 return s.alignofs[i] > s.alignofs[j] 104 } 105 106 // Lastly, order by size. 107 if s.sizeofs[i] != s.sizeofs[j] { 108 return s.sizeofs[i] > s.sizeofs[j] 109 } 110 111 return false 112 } 113 114 // Code below based on go/types.StdSizes. 115 116 type gcSizes struct { 117 WordSize int64 118 MaxAlign int64 119 } 120 121 // Alignof -- 122 func (s *gcSizes) Alignof(T types.Type) int64 { 123 // NOTE: On amd64, complex64 is 8 byte aligned, 124 // even though float32 is only 4 byte aligned. 125 126 // For arrays and structs, alignment is defined in terms 127 // of alignment of the elements and fields, respectively. 128 switch t := T.Underlying().(type) { 129 case *types.Array: 130 // spec: "For a variable x of array type: unsafe.Alignof(x) 131 // is the same as unsafe.Alignof(x[0]), but at least 1." 132 return s.Alignof(t.Elem()) 133 case *types.Struct: 134 // spec: "For a variable x of struct type: unsafe.Alignof(x) 135 // is the largest of the values unsafe.Alignof(x.f) for each 136 // field f of x, but at least 1." 137 max := int64(1) 138 for i, nf := 0, t.NumFields(); i < nf; i++ { 139 if a := s.Alignof(t.Field(i).Type()); a > max { 140 max = a 141 } 142 } 143 return max 144 } 145 a := s.Sizeof(T) // may be 0 146 // spec: "For a variable x of any type: unsafe.Alignof(x) is at least 1." 147 if a < 1 { 148 return 1 149 } 150 if a > s.MaxAlign { 151 return s.MaxAlign 152 } 153 return a 154 } 155 156 var basicSizes = [...]byte{ 157 types.Bool: 1, 158 types.Int8: 1, 159 types.Int16: 2, 160 types.Int32: 4, 161 types.Int64: 8, 162 types.Uint8: 1, 163 types.Uint16: 2, 164 types.Uint32: 4, 165 types.Uint64: 8, 166 types.Float32: 4, 167 types.Float64: 8, 168 types.Complex64: 8, 169 types.Complex128: 16, 170 } 171 172 // Sizeof -- 173 func (s *gcSizes) Sizeof(T types.Type) int64 { 174 switch t := T.Underlying().(type) { 175 case *types.Basic: 176 k := t.Kind() 177 if int(k) < len(basicSizes) { 178 if s := basicSizes[k]; s > 0 { 179 return int64(s) 180 } 181 } 182 if k == types.String { 183 return s.WordSize * 2 184 } 185 case *types.Array: 186 n := t.Len() 187 if n == 0 { 188 return 0 189 } 190 a := s.Alignof(t.Elem()) 191 z := s.Sizeof(t.Elem()) 192 return align(z, a)*(n-1) + z 193 case *types.Slice: 194 return s.WordSize * 3 195 case *types.Struct: 196 nf := t.NumFields() 197 if nf == 0 { 198 return 0 199 } 200 201 var o int64 202 max := int64(1) 203 for i := 0; i < nf; i++ { 204 ft := t.Field(i).Type() 205 a, sz := s.Alignof(ft), s.Sizeof(ft) 206 if a > max { 207 max = a 208 } 209 if i == nf-1 && sz == 0 && o != 0 { 210 sz = 1 211 } 212 o = align(o, a) + sz 213 } 214 return align(o, max) 215 case *types.Interface: 216 return s.WordSize * 2 217 } 218 return s.WordSize // catch-all 219 } 220 221 // align returns the smallest y >= x such that y % a == 0. 222 func align(x, a int64) int64 { 223 y := x + a - 1 224 return y - y%a 225 }