...

Source file src/go/types/sizes.go

     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 := T.Underlying().(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 := T.Underlying().(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 *Interface:
   152			return s.WordSize * 2
   153		}
   154		return s.WordSize // catch-all
   155	}
   156	
   157	// common architecture word sizes and alignments
   158	var gcArchSizes = map[string]*StdSizes{
   159		"386":      {4, 4},
   160		"arm":      {4, 4},
   161		"arm64":    {8, 8},
   162		"amd64":    {8, 8},
   163		"amd64p32": {4, 8},
   164		"mips":     {4, 4},
   165		"mipsle":   {4, 4},
   166		"mips64":   {8, 8},
   167		"mips64le": {8, 8},
   168		"ppc64":    {8, 8},
   169		"ppc64le":  {8, 8},
   170		"riscv64":  {8, 8},
   171		"s390x":    {8, 8},
   172		"sparc64":  {8, 8},
   173		"wasm":     {8, 8},
   174		// When adding more architectures here,
   175		// update the doc string of SizesFor below.
   176	}
   177	
   178	// SizesFor returns the Sizes used by a compiler for an architecture.
   179	// The result is nil if a compiler/architecture pair is not known.
   180	//
   181	// Supported architectures for compiler "gc":
   182	// "386", "arm", "arm64", "amd64", "amd64p32", "mips", "mipsle",
   183	// "mips64", "mips64le", "ppc64", "ppc64le", "riscv64", "s390x", "sparc64", "wasm".
   184	func SizesFor(compiler, arch string) Sizes {
   185		var m map[string]*StdSizes
   186		switch compiler {
   187		case "gc":
   188			m = gcArchSizes
   189		case "gccgo":
   190			m = gccgoArchSizes
   191		default:
   192			return nil
   193		}
   194		s, ok := m[arch]
   195		if !ok {
   196			return nil
   197		}
   198		return s
   199	}
   200	
   201	// stdSizes is used if Config.Sizes == nil.
   202	var stdSizes = SizesFor("gc", "amd64")
   203	
   204	func (conf *Config) alignof(T Type) int64 {
   205		if s := conf.Sizes; s != nil {
   206			if a := s.Alignof(T); a >= 1 {
   207				return a
   208			}
   209			panic("Config.Sizes.Alignof returned an alignment < 1")
   210		}
   211		return stdSizes.Alignof(T)
   212	}
   213	
   214	func (conf *Config) offsetsof(T *Struct) []int64 {
   215		var offsets []int64
   216		if T.NumFields() > 0 {
   217			// compute offsets on demand
   218			if s := conf.Sizes; s != nil {
   219				offsets = s.Offsetsof(T.fields)
   220				// sanity checks
   221				if len(offsets) != T.NumFields() {
   222					panic("Config.Sizes.Offsetsof returned the wrong number of offsets")
   223				}
   224				for _, o := range offsets {
   225					if o < 0 {
   226						panic("Config.Sizes.Offsetsof returned an offset < 0")
   227					}
   228				}
   229			} else {
   230				offsets = stdSizes.Offsetsof(T.fields)
   231			}
   232		}
   233		return offsets
   234	}
   235	
   236	// offsetof returns the offset of the field specified via
   237	// the index sequence relative to typ. All embedded fields
   238	// must be structs (rather than pointer to structs).
   239	func (conf *Config) offsetof(typ Type, index []int) int64 {
   240		var o int64
   241		for _, i := range index {
   242			s := typ.Underlying().(*Struct)
   243			o += conf.offsetsof(s)[i]
   244			typ = s.fields[i].typ
   245		}
   246		return o
   247	}
   248	
   249	func (conf *Config) sizeof(T Type) int64 {
   250		if s := conf.Sizes; s != nil {
   251			if z := s.Sizeof(T); z >= 0 {
   252				return z
   253			}
   254			panic("Config.Sizes.Sizeof returned a size < 0")
   255		}
   256		return stdSizes.Sizeof(T)
   257	}
   258	
   259	// align returns the smallest y >= x such that y % a == 0.
   260	func align(x, a int64) int64 {
   261		y := x + a - 1
   262		return y - y%a
   263	}
   264	

View as plain text