...

Source file src/go/types/sizes.go

Documentation: go/types

     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  type StdSizes struct {
    43  	WordSize int64 // word size in bytes - must be >= 4 (32bits)
    44  	MaxAlign int64 // maximum alignment in bytes - must be >= 1
    45  }
    46  
    47  func (s *StdSizes) Alignof(T Type) int64 {
    48  	// For arrays and structs, alignment is defined in terms
    49  	// of alignment of the elements and fields, respectively.
    50  	switch t := under(T).(type) {
    51  	case *Array:
    52  		// spec: "For a variable x of array type: unsafe.Alignof(x)
    53  		// is the same as unsafe.Alignof(x[0]), but at least 1."
    54  		return s.Alignof(t.elem)
    55  	case *Struct:
    56  		if len(t.fields) == 0 && isSyncAtomicAlign64(T) {
    57  			// Special case: sync/atomic.align64 is an
    58  			// empty struct we recognize as a signal that
    59  			// the struct it contains must be
    60  			// 64-bit-aligned.
    61  			//
    62  			// This logic is equivalent to the logic in
    63  			// cmd/compile/internal/types/size.go:calcStructOffset
    64  			return 8
    65  		}
    66  
    67  		// spec: "For a variable x of struct type: unsafe.Alignof(x)
    68  		// is the largest of the values unsafe.Alignof(x.f) for each
    69  		// field f of x, but at least 1."
    70  		max := int64(1)
    71  		for _, f := range t.fields {
    72  			if a := s.Alignof(f.typ); a > max {
    73  				max = a
    74  			}
    75  		}
    76  		return max
    77  	case *Slice, *Interface:
    78  		// Multiword data structures are effectively structs
    79  		// in which each element has size WordSize.
    80  		// Type parameters lead to variable sizes/alignments;
    81  		// StdSizes.Alignof won't be called for them.
    82  		assert(!isTypeParam(T))
    83  		return s.WordSize
    84  	case *Basic:
    85  		// Strings are like slices and interfaces.
    86  		if t.Info()&IsString != 0 {
    87  			return s.WordSize
    88  		}
    89  	case *TypeParam, *Union:
    90  		unreachable()
    91  	}
    92  	a := s.Sizeof(T) // may be 0
    93  	// spec: "For a variable x of any type: unsafe.Alignof(x) is at least 1."
    94  	if a < 1 {
    95  		return 1
    96  	}
    97  	// complex{64,128} are aligned like [2]float{32,64}.
    98  	if isComplex(T) {
    99  		a /= 2
   100  	}
   101  	if a > s.MaxAlign {
   102  		return s.MaxAlign
   103  	}
   104  	return a
   105  }
   106  
   107  func isSyncAtomicAlign64(T Type) bool {
   108  	named, ok := T.(*Named)
   109  	if !ok {
   110  		return false
   111  	}
   112  	obj := named.Obj()
   113  	return obj.Name() == "align64" &&
   114  		obj.Pkg() != nil &&
   115  		(obj.Pkg().Path() == "sync/atomic" ||
   116  			obj.Pkg().Path() == "runtime/internal/atomic")
   117  }
   118  
   119  func (s *StdSizes) Offsetsof(fields []*Var) []int64 {
   120  	offsets := make([]int64, len(fields))
   121  	var o int64
   122  	for i, f := range fields {
   123  		a := s.Alignof(f.typ)
   124  		o = align(o, a)
   125  		offsets[i] = o
   126  		o += s.Sizeof(f.typ)
   127  	}
   128  	return offsets
   129  }
   130  
   131  var basicSizes = [...]byte{
   132  	Bool:       1,
   133  	Int8:       1,
   134  	Int16:      2,
   135  	Int32:      4,
   136  	Int64:      8,
   137  	Uint8:      1,
   138  	Uint16:     2,
   139  	Uint32:     4,
   140  	Uint64:     8,
   141  	Float32:    4,
   142  	Float64:    8,
   143  	Complex64:  8,
   144  	Complex128: 16,
   145  }
   146  
   147  func (s *StdSizes) Sizeof(T Type) int64 {
   148  	switch t := under(T).(type) {
   149  	case *Basic:
   150  		assert(isTyped(T))
   151  		k := t.kind
   152  		if int(k) < len(basicSizes) {
   153  			if s := basicSizes[k]; s > 0 {
   154  				return int64(s)
   155  			}
   156  		}
   157  		if k == String {
   158  			return s.WordSize * 2
   159  		}
   160  	case *Array:
   161  		n := t.len
   162  		if n <= 0 {
   163  			return 0
   164  		}
   165  		// n > 0
   166  		a := s.Alignof(t.elem)
   167  		z := s.Sizeof(t.elem)
   168  		return align(z, a)*(n-1) + z
   169  	case *Slice:
   170  		return s.WordSize * 3
   171  	case *Struct:
   172  		n := t.NumFields()
   173  		if n == 0 {
   174  			return 0
   175  		}
   176  		offsets := s.Offsetsof(t.fields)
   177  		return offsets[n-1] + s.Sizeof(t.fields[n-1].typ)
   178  	case *Interface:
   179  		// Type parameters lead to variable sizes/alignments;
   180  		// StdSizes.Sizeof won't be called for them.
   181  		assert(!isTypeParam(T))
   182  		return s.WordSize * 2
   183  	case *TypeParam, *Union:
   184  		unreachable()
   185  	}
   186  	return s.WordSize // catch-all
   187  }
   188  
   189  // common architecture word sizes and alignments
   190  var gcArchSizes = map[string]*StdSizes{
   191  	"386":      {4, 4},
   192  	"amd64":    {8, 8},
   193  	"amd64p32": {4, 8},
   194  	"arm":      {4, 4},
   195  	"arm64":    {8, 8},
   196  	"loong64":  {8, 8},
   197  	"mips":     {4, 4},
   198  	"mipsle":   {4, 4},
   199  	"mips64":   {8, 8},
   200  	"mips64le": {8, 8},
   201  	"ppc64":    {8, 8},
   202  	"ppc64le":  {8, 8},
   203  	"riscv64":  {8, 8},
   204  	"s390x":    {8, 8},
   205  	"sparc64":  {8, 8},
   206  	"wasm":     {8, 8},
   207  	// When adding more architectures here,
   208  	// update the doc string of SizesFor below.
   209  }
   210  
   211  // SizesFor returns the Sizes used by a compiler for an architecture.
   212  // The result is nil if a compiler/architecture pair is not known.
   213  //
   214  // Supported architectures for compiler "gc":
   215  // "386", "amd64", "amd64p32", "arm", "arm64", "loong64", "mips", "mipsle",
   216  // "mips64", "mips64le", "ppc64", "ppc64le", "riscv64", "s390x", "sparc64", "wasm".
   217  func SizesFor(compiler, arch string) Sizes {
   218  	var m map[string]*StdSizes
   219  	switch compiler {
   220  	case "gc":
   221  		m = gcArchSizes
   222  	case "gccgo":
   223  		m = gccgoArchSizes
   224  	default:
   225  		return nil
   226  	}
   227  	s, ok := m[arch]
   228  	if !ok {
   229  		return nil
   230  	}
   231  	return s
   232  }
   233  
   234  // stdSizes is used if Config.Sizes == nil.
   235  var stdSizes = SizesFor("gc", "amd64")
   236  
   237  func (conf *Config) alignof(T Type) int64 {
   238  	if s := conf.Sizes; s != nil {
   239  		if a := s.Alignof(T); a >= 1 {
   240  			return a
   241  		}
   242  		panic("Config.Sizes.Alignof returned an alignment < 1")
   243  	}
   244  	return stdSizes.Alignof(T)
   245  }
   246  
   247  func (conf *Config) offsetsof(T *Struct) []int64 {
   248  	var offsets []int64
   249  	if T.NumFields() > 0 {
   250  		// compute offsets on demand
   251  		if s := conf.Sizes; s != nil {
   252  			offsets = s.Offsetsof(T.fields)
   253  			// sanity checks
   254  			if len(offsets) != T.NumFields() {
   255  				panic("Config.Sizes.Offsetsof returned the wrong number of offsets")
   256  			}
   257  			for _, o := range offsets {
   258  				if o < 0 {
   259  					panic("Config.Sizes.Offsetsof returned an offset < 0")
   260  				}
   261  			}
   262  		} else {
   263  			offsets = stdSizes.Offsetsof(T.fields)
   264  		}
   265  	}
   266  	return offsets
   267  }
   268  
   269  // offsetof returns the offset of the field specified via
   270  // the index sequence relative to typ. All embedded fields
   271  // must be structs (rather than pointer to structs).
   272  func (conf *Config) offsetof(typ Type, index []int) int64 {
   273  	var o int64
   274  	for _, i := range index {
   275  		s := under(typ).(*Struct)
   276  		o += conf.offsetsof(s)[i]
   277  		typ = s.fields[i].typ
   278  	}
   279  	return o
   280  }
   281  
   282  func (conf *Config) sizeof(T Type) int64 {
   283  	if s := conf.Sizes; s != nil {
   284  		if z := s.Sizeof(T); z >= 0 {
   285  			return z
   286  		}
   287  		panic("Config.Sizes.Sizeof returned a size < 0")
   288  	}
   289  	return stdSizes.Sizeof(T)
   290  }
   291  
   292  // align returns the smallest y >= x such that y % a == 0.
   293  func align(x, a int64) int64 {
   294  	y := x + a - 1
   295  	return y - y%a
   296  }
   297  

View as plain text