...

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