Black Lives Matter. Support the Equal Justice Initiative.

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

View as plain text