Source file src/go/types/sizes.go

     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  			return s.WordSize * 2
   179  		}
   180  	case *Array:
   181  		n := t.len
   182  		if n <= 0 {
   183  			return 0
   184  		}
   185  		// n > 0
   186  		esize := s.Sizeof(t.elem)
   187  		if esize < 0 {
   188  			return -1 // element too large
   189  		}
   190  		if esize == 0 {
   191  			return 0 // 0-size element
   192  		}
   193  		// esize > 0
   194  		a := s.Alignof(t.elem)
   195  		ea := align(esize, a) // possibly < 0 if align overflows
   196  		if ea < 0 {
   197  			return -1
   198  		}
   199  		// ea >= 1
   200  		n1 := n - 1 // n1 >= 0
   201  		// Final size is ea*n1 + esize; and size must be <= maxInt64.
   202  		const maxInt64 = 1<<63 - 1
   203  		if n1 > 0 && ea > maxInt64/n1 {
   204  			return -1 // ea*n1 overflows
   205  		}
   206  		return ea*n1 + esize // may still overflow to < 0 which is ok
   207  	case *Slice:
   208  		return s.WordSize * 3
   209  	case *Struct:
   210  		n := t.NumFields()
   211  		if n == 0 {
   212  			return 0
   213  		}
   214  		offsets := s.Offsetsof(t.fields)
   215  		offs := offsets[n-1]
   216  		size := s.Sizeof(t.fields[n-1].typ)
   217  		if offs < 0 || size < 0 {
   218  			return -1 // type too large
   219  		}
   220  		return offs + size // may overflow to < 0 which is ok
   221  	case *Interface:
   222  		// Type parameters lead to variable sizes/alignments;
   223  		// StdSizes.Sizeof won't be called for them.
   224  		assert(!isTypeParam(T))
   225  		return s.WordSize * 2
   226  	case *TypeParam, *Union:
   227  		panic("unreachable")
   228  	}
   229  	return s.WordSize // catch-all
   230  }
   231  
   232  // common architecture word sizes and alignments
   233  var gcArchSizes = map[string]*gcSizes{
   234  	"386":      {4, 4},
   235  	"amd64":    {8, 8},
   236  	"amd64p32": {4, 8},
   237  	"arm":      {4, 4},
   238  	"arm64":    {8, 8},
   239  	"loong64":  {8, 8},
   240  	"mips":     {4, 4},
   241  	"mipsle":   {4, 4},
   242  	"mips64":   {8, 8},
   243  	"mips64le": {8, 8},
   244  	"ppc64":    {8, 8},
   245  	"ppc64le":  {8, 8},
   246  	"riscv64":  {8, 8},
   247  	"s390x":    {8, 8},
   248  	"sparc64":  {8, 8},
   249  	"wasm":     {8, 8},
   250  	// When adding more architectures here,
   251  	// update the doc string of SizesFor below.
   252  }
   253  
   254  // SizesFor returns the Sizes used by a compiler for an architecture.
   255  // The result is nil if a compiler/architecture pair is not known.
   256  //
   257  // Supported architectures for compiler "gc":
   258  // "386", "amd64", "amd64p32", "arm", "arm64", "loong64", "mips", "mipsle",
   259  // "mips64", "mips64le", "ppc64", "ppc64le", "riscv64", "s390x", "sparc64", "wasm".
   260  func SizesFor(compiler, arch string) Sizes {
   261  	switch compiler {
   262  	case "gc":
   263  		if s := gcSizesFor(compiler, arch); s != nil {
   264  			return Sizes(s)
   265  		}
   266  	case "gccgo":
   267  		if s, ok := gccgoArchSizes[arch]; ok {
   268  			return Sizes(s)
   269  		}
   270  	}
   271  	return nil
   272  }
   273  
   274  // stdSizes is used if Config.Sizes == nil.
   275  var stdSizes = SizesFor("gc", "amd64")
   276  
   277  func (conf *Config) alignof(T Type) int64 {
   278  	f := stdSizes.Alignof
   279  	if conf.Sizes != nil {
   280  		f = conf.Sizes.Alignof
   281  	}
   282  	if a := f(T); a >= 1 {
   283  		return a
   284  	}
   285  	panic("implementation of alignof returned an alignment < 1")
   286  }
   287  
   288  func (conf *Config) offsetsof(T *Struct) []int64 {
   289  	var offsets []int64
   290  	if T.NumFields() > 0 {
   291  		// compute offsets on demand
   292  		f := stdSizes.Offsetsof
   293  		if conf.Sizes != nil {
   294  			f = conf.Sizes.Offsetsof
   295  		}
   296  		offsets = f(T.fields)
   297  		// sanity checks
   298  		if len(offsets) != T.NumFields() {
   299  			panic("implementation of offsetsof returned the wrong number of offsets")
   300  		}
   301  	}
   302  	return offsets
   303  }
   304  
   305  // offsetof returns the offset of the field specified via
   306  // the index sequence relative to T. All embedded fields
   307  // must be structs (rather than pointers to structs).
   308  // If the offset is too large (because T is too large),
   309  // the result is negative.
   310  func (conf *Config) offsetof(T Type, index []int) int64 {
   311  	var offs int64
   312  	for _, i := range index {
   313  		s := under(T).(*Struct)
   314  		d := conf.offsetsof(s)[i]
   315  		if d < 0 {
   316  			return -1
   317  		}
   318  		offs += d
   319  		if offs < 0 {
   320  			return -1
   321  		}
   322  		T = s.fields[i].typ
   323  	}
   324  	return offs
   325  }
   326  
   327  // sizeof returns the size of T.
   328  // If T is too large, the result is negative.
   329  func (conf *Config) sizeof(T Type) int64 {
   330  	f := stdSizes.Sizeof
   331  	if conf.Sizes != nil {
   332  		f = conf.Sizes.Sizeof
   333  	}
   334  	return f(T)
   335  }
   336  
   337  // align returns the smallest y >= x such that y % a == 0.
   338  // a must be within 1 and 8 and it must be a power of 2.
   339  // The result may be negative due to overflow.
   340  func align(x, a int64) int64 {
   341  	assert(x >= 0 && 1 <= a && a <= 8 && a&(a-1) == 0)
   342  	return (x + a - 1) &^ (a - 1)
   343  }
   344  

View as plain text