sizes.go raw

   1  // Code generated by "go test -run=Generate -write=all"; DO NOT EDIT.
   2  // Source: ../../cmd/compile/internal/types2/sizes.go
   3  
   4  // Copyright 2013 The Go Authors. All rights reserved.
   5  // Use of this source code is governed by a BSD-style
   6  // license that can be found in the LICENSE file.
   7  
   8  // This file implements Sizes.
   9  
  10  package types
  11  
  12  // Sizes defines the sizing functions for package unsafe.
  13  type Sizes interface {
  14  	// Alignof returns the alignment of a variable of type T.
  15  	// Alignof must implement the alignment guarantees required by the spec.
  16  	// The result must be >= 1.
  17  	Alignof(T Type) int64
  18  
  19  	// Offsetsof returns the offsets of the given struct fields, in bytes.
  20  	// Offsetsof must implement the offset guarantees required by the spec.
  21  	// A negative entry in the result indicates that the struct is too large.
  22  	Offsetsof(fields []*Var) []int64
  23  
  24  	// Sizeof returns the size of a variable of type T.
  25  	// Sizeof must implement the size guarantees required by the spec.
  26  	// A negative result indicates that T is too large.
  27  	Sizeof(T Type) int64
  28  }
  29  
  30  // StdSizes is a convenience type for creating commonly used Sizes.
  31  // It makes the following simplifying assumptions:
  32  //
  33  //   - The size of explicitly sized basic types (int16, etc.) is the
  34  //     specified size.
  35  //   - The size of strings and interfaces is 2*WordSize.
  36  //   - The size of slices is 3*WordSize.
  37  //   - The size of an array of n elements corresponds to the size of
  38  //     a struct of n consecutive fields of the array's element type.
  39  //   - The size of a struct is the offset of the last field plus that
  40  //     field's size. As with all element types, if the struct is used
  41  //     in an array its size must first be aligned to a multiple of the
  42  //     struct's alignment.
  43  //   - All other types have size WordSize.
  44  //   - Arrays and structs are aligned per spec definition; all other
  45  //     types are naturally aligned with a maximum alignment MaxAlign.
  46  //
  47  // *StdSizes implements Sizes.
  48  type StdSizes struct {
  49  	WordSize int64 // word size in bytes - must be >= 4 (32bits)
  50  	MaxAlign int64 // maximum alignment in bytes - must be >= 1
  51  }
  52  
  53  func (s *StdSizes) Alignof(T Type) (result int64) {
  54  	defer func() {
  55  		assert(result >= 1)
  56  	}()
  57  
  58  	// For arrays and structs, alignment is defined in terms
  59  	// of alignment of the elements and fields, respectively.
  60  	switch t := under(T).(type) {
  61  	case *Array:
  62  		// spec: "For a variable x of array type: unsafe.Alignof(x)
  63  		// is the same as unsafe.Alignof(x[0]), but at least 1."
  64  		return s.Alignof(t.elem)
  65  	case *Struct:
  66  		if len(t.fields) == 0 && _IsSyncAtomicAlign64(T) {
  67  			// Special case: sync/atomic.align64 is an
  68  			// empty struct we recognize as a signal that
  69  			// the struct it contains must be
  70  			// 64-bit-aligned.
  71  			//
  72  			// This logic is equivalent to the logic in
  73  			// cmd/compile/internal/types/size.go:calcStructOffset
  74  			return 8
  75  		}
  76  
  77  		// spec: "For a variable x of struct type: unsafe.Alignof(x)
  78  		// is the largest of the values unsafe.Alignof(x.f) for each
  79  		// field f of x, but at least 1."
  80  		max := int64(1)
  81  		for _, f := range t.fields {
  82  			if a := s.Alignof(f.typ); a > max {
  83  				max = a
  84  			}
  85  		}
  86  		return max
  87  	case *Slice, *Interface:
  88  		// Multiword data structures are effectively structs
  89  		// in which each element has size WordSize.
  90  		// Type parameters lead to variable sizes/alignments;
  91  		// StdSizes.Alignof won't be called for them.
  92  		assert(!isTypeParam(T))
  93  		return s.WordSize
  94  	case *Basic:
  95  		// Strings are like slices and interfaces.
  96  		if t.Info()&IsString != 0 {
  97  			return s.WordSize
  98  		}
  99  	case *TypeParam, *Union:
 100  		panic("unreachable")
 101  	}
 102  	a := s.Sizeof(T) // may be 0 or negative
 103  	// spec: "For a variable x of any type: unsafe.Alignof(x) is at least 1."
 104  	if a < 1 {
 105  		return 1
 106  	}
 107  	// complex{64,128} are aligned like [2]float{32,64}.
 108  	if isComplex(T) {
 109  		a /= 2
 110  	}
 111  	if a > s.MaxAlign {
 112  		return s.MaxAlign
 113  	}
 114  	return a
 115  }
 116  
 117  func _IsSyncAtomicAlign64(T Type) bool {
 118  	named := asNamed(T)
 119  	if named == nil {
 120  		return false
 121  	}
 122  	obj := named.Obj()
 123  	return obj.Name() == "align64" &&
 124  		obj.Pkg() != nil &&
 125  		(obj.Pkg().Path() == "sync/atomic" ||
 126  			obj.Pkg().Path() == "internal/runtime/atomic")
 127  }
 128  
 129  func (s *StdSizes) Offsetsof(fields []*Var) []int64 {
 130  	offsets := make([]int64, len(fields))
 131  	var offs int64
 132  	for i, f := range fields {
 133  		if offs < 0 {
 134  			// all remaining offsets are too large
 135  			offsets[i] = -1
 136  			continue
 137  		}
 138  		// offs >= 0
 139  		a := s.Alignof(f.typ)
 140  		offs = align(offs, a) // possibly < 0 if align overflows
 141  		offsets[i] = offs
 142  		if d := s.Sizeof(f.typ); d >= 0 && offs >= 0 {
 143  			offs += d // ok to overflow to < 0
 144  		} else {
 145  			offs = -1 // f.typ or offs is too large
 146  		}
 147  	}
 148  	return offsets
 149  }
 150  
 151  var basicSizes = [...]byte{
 152  	Bool:       1,
 153  	Int8:       1,
 154  	Int16:      2,
 155  	Int32:      4,
 156  	Int64:      8,
 157  	Uint8:      1,
 158  	Uint16:     2,
 159  	Uint32:     4,
 160  	Uint64:     8,
 161  	Float32:    4,
 162  	Float64:    8,
 163  	Complex64:  8,
 164  	Complex128: 16,
 165  }
 166  
 167  func (s *StdSizes) Sizeof(T Type) int64 {
 168  	switch t := under(T).(type) {
 169  	case *Basic:
 170  		assert(isTyped(T))
 171  		k := t.kind
 172  		if int(k) < len(basicSizes) {
 173  			if s := basicSizes[k]; s > 0 {
 174  				return int64(s)
 175  			}
 176  		}
 177  		if k == String {
 178  			// Moxie: string=[]byte — 3-word struct (ptr, len, cap).
 179  			return s.WordSize * 3
 180  		}
 181  	case *Array:
 182  		n := t.len
 183  		if n <= 0 {
 184  			return 0
 185  		}
 186  		// n > 0
 187  		esize := s.Sizeof(t.elem)
 188  		if esize < 0 {
 189  			return -1 // element too large
 190  		}
 191  		if esize == 0 {
 192  			return 0 // 0-size element
 193  		}
 194  		// esize > 0
 195  		a := s.Alignof(t.elem)
 196  		ea := align(esize, a) // possibly < 0 if align overflows
 197  		if ea < 0 {
 198  			return -1
 199  		}
 200  		// ea >= 1
 201  		n1 := n - 1 // n1 >= 0
 202  		// Final size is ea*n1 + esize; and size must be <= maxInt64.
 203  		const maxInt64 = 1<<63 - 1
 204  		if n1 > 0 && ea > maxInt64/n1 {
 205  			return -1 // ea*n1 overflows
 206  		}
 207  		return ea*n1 + esize // may still overflow to < 0 which is ok
 208  	case *Slice:
 209  		return s.WordSize * 3
 210  	case *Struct:
 211  		n := t.NumFields()
 212  		if n == 0 {
 213  			return 0
 214  		}
 215  		offsets := s.Offsetsof(t.fields)
 216  		offs := offsets[n-1]
 217  		size := s.Sizeof(t.fields[n-1].typ)
 218  		if offs < 0 || size < 0 {
 219  			return -1 // type too large
 220  		}
 221  		return offs + size // may overflow to < 0 which is ok
 222  	case *Interface:
 223  		// Type parameters lead to variable sizes/alignments;
 224  		// StdSizes.Sizeof won't be called for them.
 225  		assert(!isTypeParam(T))
 226  		return s.WordSize * 2
 227  	case *TypeParam, *Union:
 228  		panic("unreachable")
 229  	}
 230  	return s.WordSize // catch-all
 231  }
 232  
 233  // common architecture word sizes and alignments
 234  var gcArchSizes = map[string]*gcSizes{
 235  	"386":      {4, 4},
 236  	"amd64":    {8, 8},
 237  	"amd64p32": {4, 8},
 238  	"arm":      {4, 4},
 239  	"arm64":    {8, 8},
 240  	"loong64":  {8, 8},
 241  	"mips":     {4, 4},
 242  	"mipsle":   {4, 4},
 243  	"mips64":   {8, 8},
 244  	"mips64le": {8, 8},
 245  	"ppc64":    {8, 8},
 246  	"ppc64le":  {8, 8},
 247  	"riscv64":  {8, 8},
 248  	"s390x":    {8, 8},
 249  	"sparc64":  {8, 8},
 250  	"wasm":     {8, 8},
 251  	// When adding more architectures here,
 252  	// update the doc string of SizesFor below.
 253  }
 254  
 255  // SizesFor returns the Sizes used by a compiler for an architecture.
 256  // The result is nil if a compiler/architecture pair is not known.
 257  //
 258  // Supported architectures for compiler "gc":
 259  // "386", "amd64", "amd64p32", "arm", "arm64", "loong64", "mips", "mipsle",
 260  // "mips64", "mips64le", "ppc64", "ppc64le", "riscv64", "s390x", "sparc64", "wasm".
 261  func SizesFor(compiler, arch string) Sizes {
 262  	switch compiler {
 263  	case "gc":
 264  		if s := gcSizesFor(compiler, arch); s != nil {
 265  			return Sizes(s)
 266  		}
 267  	case "gccgo":
 268  		if s, ok := gccgoArchSizes[arch]; ok {
 269  			return Sizes(s)
 270  		}
 271  	}
 272  	return nil
 273  }
 274  
 275  // stdSizes is used if Config.Sizes == nil.
 276  var stdSizes = SizesFor("gc", "amd64")
 277  
 278  func (conf *Config) alignof(T Type) int64 {
 279  	f := stdSizes.Alignof
 280  	if conf.Sizes != nil {
 281  		f = conf.Sizes.Alignof
 282  	}
 283  	if a := f(T); a >= 1 {
 284  		return a
 285  	}
 286  	panic("implementation of alignof returned an alignment < 1")
 287  }
 288  
 289  func (conf *Config) offsetsof(T *Struct) []int64 {
 290  	var offsets []int64
 291  	if T.NumFields() > 0 {
 292  		// compute offsets on demand
 293  		f := stdSizes.Offsetsof
 294  		if conf.Sizes != nil {
 295  			f = conf.Sizes.Offsetsof
 296  		}
 297  		offsets = f(T.fields)
 298  		// sanity checks
 299  		if len(offsets) != T.NumFields() {
 300  			panic("implementation of offsetsof returned the wrong number of offsets")
 301  		}
 302  	}
 303  	return offsets
 304  }
 305  
 306  // offsetof returns the offset of the field specified via
 307  // the index sequence relative to T. All embedded fields
 308  // must be structs (rather than pointers to structs).
 309  // If the offset is too large (because T is too large),
 310  // the result is negative.
 311  func (conf *Config) offsetof(T Type, index []int) int64 {
 312  	var offs int64
 313  	for _, i := range index {
 314  		s := under(T).(*Struct)
 315  		d := conf.offsetsof(s)[i]
 316  		if d < 0 {
 317  			return -1
 318  		}
 319  		offs += d
 320  		if offs < 0 {
 321  			return -1
 322  		}
 323  		T = s.fields[i].typ
 324  	}
 325  	return offs
 326  }
 327  
 328  // sizeof returns the size of T.
 329  // If T is too large, the result is negative.
 330  func (conf *Config) sizeof(T Type) int64 {
 331  	f := stdSizes.Sizeof
 332  	if conf.Sizes != nil {
 333  		f = conf.Sizes.Sizeof
 334  	}
 335  	return f(T)
 336  }
 337  
 338  // align returns the smallest y >= x such that y % a == 0.
 339  // a must be within 1 and 8 and it must be a power of 2.
 340  // The result may be negative due to overflow.
 341  func align(x, a int64) int64 {
 342  	assert(x >= 0 && 1 <= a && a <= 8 && a&(a-1) == 0)
 343  	return (x + a - 1) &^ (a - 1)
 344  }
 345