github.com/tinygo-org/tinygo@v0.31.3-0.20240404173401-90b0bf646c27/compiler/sizes.go (about) 1 package compiler 2 3 import ( 4 "go/types" 5 ) 6 7 // The code in this file has been copied from 8 // https://golang.org/src/go/types/sizes.go and modified to allow for int and 9 // pointer sizes to differ. 10 // The original license can be found here: 11 // https://golang.org/LICENSE 12 13 type stdSizes struct { 14 IntSize int64 15 PtrSize int64 16 MaxAlign int64 17 } 18 19 func (s *stdSizes) Alignof(T types.Type) int64 { 20 // For arrays and structs, alignment is defined in terms 21 // of alignment of the elements and fields, respectively. 22 switch t := T.Underlying().(type) { 23 case *types.Array: 24 // spec: "For a variable x of array type: unsafe.Alignof(x) 25 // is the same as unsafe.Alignof(x[0]), but at least 1." 26 return s.Alignof(t.Elem()) 27 case *types.Struct: 28 // spec: "For a variable x of struct type: unsafe.Alignof(x) 29 // is the largest of the values unsafe.Alignof(x.f) for each 30 // field f of x, but at least 1." 31 max := int64(1) 32 for i := 0; i < t.NumFields(); i++ { 33 f := t.Field(i) 34 if a := s.Alignof(f.Type()); a > max { 35 max = a 36 } 37 } 38 return max 39 case *types.Slice, *types.Interface: 40 // Multiword data structures are effectively structs 41 // in which each element has size WordSize. 42 return s.PtrSize 43 case *types.Basic: 44 // Strings are like slices and interfaces. 45 if t.Info()&types.IsString != 0 { 46 return s.PtrSize 47 } 48 case *types.Signature: 49 // Even though functions in tinygo are 2 pointers, they are not 2 pointer aligned 50 return s.PtrSize 51 } 52 53 a := s.Sizeof(T) // may be 0 54 // spec: "For a variable x of any type: unsafe.Alignof(x) is at least 1." 55 if a < 1 { 56 return 1 57 } 58 // complex{64,128} are aligned like [2]float{32,64}. 59 if t, ok := T.Underlying().(*types.Basic); ok && t.Info()&types.IsComplex != 0 { 60 a /= 2 61 } 62 if a > s.MaxAlign { 63 return s.MaxAlign 64 } 65 return a 66 } 67 68 func (s *stdSizes) Offsetsof(fields []*types.Var) []int64 { 69 offsets := make([]int64, len(fields)) 70 var o int64 71 for i, f := range fields { 72 a := s.Alignof(f.Type()) 73 o = align(o, a) 74 offsets[i] = o 75 o += s.Sizeof(f.Type()) 76 } 77 return offsets 78 } 79 80 var basicSizes = [...]byte{ 81 types.Bool: 1, 82 types.Int8: 1, 83 types.Int16: 2, 84 types.Int32: 4, 85 types.Int64: 8, 86 types.Uint8: 1, 87 types.Uint16: 2, 88 types.Uint32: 4, 89 types.Uint64: 8, 90 types.Float32: 4, 91 types.Float64: 8, 92 types.Complex64: 8, 93 types.Complex128: 16, 94 } 95 96 func (s *stdSizes) Sizeof(T types.Type) int64 { 97 switch t := T.Underlying().(type) { 98 case *types.Basic: 99 k := t.Kind() 100 if int(k) < len(basicSizes) { 101 if s := basicSizes[k]; s > 0 { 102 return int64(s) 103 } 104 } 105 if k == types.String { 106 return s.PtrSize * 2 107 } 108 if k == types.Int || k == types.Uint { 109 return s.IntSize 110 } 111 if k == types.Uintptr { 112 return s.PtrSize 113 } 114 if k == types.UnsafePointer { 115 return s.PtrSize 116 } 117 if k == types.Invalid { 118 return 0 // only relevant when there is a type error somewhere 119 } 120 panic("unknown basic type: " + t.String()) 121 case *types.Array: 122 n := t.Len() 123 if n <= 0 { 124 return 0 125 } 126 // n > 0 127 a := s.Alignof(t.Elem()) 128 z := s.Sizeof(t.Elem()) 129 return align(z, a)*(n-1) + z 130 case *types.Slice: 131 return s.PtrSize * 3 132 case *types.Struct: 133 n := t.NumFields() 134 if n == 0 { 135 return 0 136 } 137 fields := make([]*types.Var, t.NumFields()) 138 maxAlign := int64(1) 139 for i := range fields { 140 field := t.Field(i) 141 fields[i] = field 142 al := s.Alignof(field.Type()) 143 if al > maxAlign { 144 maxAlign = al 145 } 146 } 147 // Pick the size that fits this struct and add some alignment. Some 148 // structs have some extra padding at the end which should also be taken 149 // care of: 150 // struct { int32 n; byte b } 151 offsets := s.Offsetsof(fields) 152 return align(offsets[n-1]+s.Sizeof(fields[n-1].Type()), maxAlign) 153 case *types.Interface: 154 return s.PtrSize * 2 155 case *types.Pointer, *types.Chan, *types.Map: 156 return s.PtrSize 157 case *types.Signature: 158 // Func values in TinyGo are two words in size. 159 return s.PtrSize * 2 160 default: 161 panic("unknown type: " + t.String()) 162 } 163 } 164 165 // align returns the smallest y >= x such that y % a == 0. 166 func align(x, a int64) int64 { 167 y := x + a - 1 168 return y - y%a 169 }