go sizes 源码

2022-07-15 浏览 (1227)

golang sizes 代码

文件路径:/src/cmd/compile/internal/types2/sizes.go

/ Copyright 2013 The Go Authors. All rights reserved.
/ Use of this source code is governed by a BSD-style
/ license that can be found in the LICENSE file.

/ This file implements Sizes.

package types2

/ Sizes defines the sizing functions for package unsafe.
type Sizes interface {
	/ Alignof returns the alignment of a variable of type T.
	/ Alignof must implement the alignment guarantees required by the spec.
	Alignof(T Type) int64

	/ Offsetsof returns the offsets of the given struct fields, in bytes.
	/ Offsetsof must implement the offset guarantees required by the spec.
	Offsetsof(fields []*Var) []int64

	/ Sizeof returns the size of a variable of type T.
	/ Sizeof must implement the size guarantees required by the spec.
	Sizeof(T Type) int64
}

/ StdSizes is a convenience type for creating commonly used Sizes.
/ It makes the following simplifying assumptions:
/
/   - The size of explicitly sized basic types (int16, etc.) is the
/     specified size.
/   - The size of strings and interfaces is 2*WordSize.
/   - The size of slices is 3*WordSize.
/   - The size of an array of n elements corresponds to the size of
/     a struct of n consecutive fields of the array's element type.
/   - The size of a struct is the offset of the last field plus that
/     field's size. As with all element types, if the struct is used
/     in an array its size must first be aligned to a multiple of the
/     struct's alignment.
/   - All other types have size WordSize.
/   - Arrays and structs are aligned per spec definition; all other
/     types are naturally aligned with a maximum alignment MaxAlign.
/
/ *StdSizes implements Sizes.
type StdSizes struct {
	WordSize int64 / word size in bytes - must be >= 4 (32bits)
	MaxAlign int64 / maximum alignment in bytes - must be >= 1
}

func (s *StdSizes) Alignof(T Type) int64 {
	/ For arrays and structs, alignment is defined in terms
	/ of alignment of the elements and fields, respectively.
	switch t := under(T).(type) {
	case *Array:
		/ spec: "For a variable x of array type: unsafe.Alignof(x)
		/ is the same as unsafe.Alignof(x[0]), but at least 1."
		return s.Alignof(t.elem)
	case *Struct:
		if len(t.fields) == 0 && isSyncAtomicAlign64(T) {
			/ Special case: sync/atomic.align64 is an
			/ empty struct we recognize as a signal that
			/ the struct it contains must be
			/ 64-bit-aligned.
			/
			/ This logic is equivalent to the logic in
			/ cmd/compile/internal/types/size.go:calcStructOffset
			return 8
		}

		/ spec: "For a variable x of struct type: unsafe.Alignof(x)
		/ is the largest of the values unsafe.Alignof(x.f) for each
		/ field f of x, but at least 1."
		max := int64(1)
		for _, f := range t.fields {
			if a := s.Alignof(f.typ); a > max {
				max = a
			}
		}
		return max
	case *Slice, *Interface:
		/ Multiword data structures are effectively structs
		/ in which each element has size WordSize.
		/ Type parameters lead to variable sizes/alignments;
		/ StdSizes.Alignof won't be called for them.
		assert(!isTypeParam(T))
		return s.WordSize
	case *Basic:
		/ Strings are like slices and interfaces.
		if t.Info()&IsString != 0 {
			return s.WordSize
		}
	case *TypeParam, *Union:
		unreachable()
	}
	a := s.Sizeof(T) / may be 0
	/ spec: "For a variable x of any type: unsafe.Alignof(x) is at least 1."
	if a < 1 {
		return 1
	}
	/ complex{64,128} are aligned like [2]float{32,64}.
	if isComplex(T) {
		a /= 2
	}
	if a > s.MaxAlign {
		return s.MaxAlign
	}
	return a
}

func isSyncAtomicAlign64(T Type) bool {
	named, ok := T.(*Named)
	if !ok {
		return false
	}
	obj := named.Obj()
	return obj.Name() == "align64" &&
		obj.Pkg() != nil &&
		(obj.Pkg().Path() == "sync/atomic" ||
			obj.Pkg().Path() == "runtime/internal/atomic")
}

func (s *StdSizes) Offsetsof(fields []*Var) []int64 {
	offsets := make([]int64, len(fields))
	var o int64
	for i, f := range fields {
		a := s.Alignof(f.typ)
		o = align(o, a)
		offsets[i] = o
		o += s.Sizeof(f.typ)
	}
	return offsets
}

var basicSizes = [...]byte{
	Bool:       1,
	Int8:       1,
	Int16:      2,
	Int32:      4,
	Int64:      8,
	Uint8:      1,
	Uint16:     2,
	Uint32:     4,
	Uint64:     8,
	Float32:    4,
	Float64:    8,
	Complex64:  8,
	Complex128: 16,
}

func (s *StdSizes) Sizeof(T Type) int64 {
	switch t := under(T).(type) {
	case *Basic:
		assert(isTyped(T))
		k := t.kind
		if int(k) < len(basicSizes) {
			if s := basicSizes[k]; s > 0 {
				return int64(s)
			}
		}
		if k == String {
			return s.WordSize * 2
		}
	case *Array:
		n := t.len
		if n <= 0 {
			return 0
		}
		/ n > 0
		a := s.Alignof(t.elem)
		z := s.Sizeof(t.elem)
		return align(z, a)*(n-1) + z
	case *Slice:
		return s.WordSize * 3
	case *Struct:
		n := t.NumFields()
		if n == 0 {
			return 0
		}
		offsets := s.Offsetsof(t.fields)
		return offsets[n-1] + s.Sizeof(t.fields[n-1].typ)
	case *Interface:
		/ Type parameters lead to variable sizes/alignments;
		/ StdSizes.Sizeof won't be called for them.
		assert(!isTypeParam(T))
		return s.WordSize * 2
	case *TypeParam, *Union:
		unreachable()
	}
	return s.WordSize / catch-all
}

/ common architecture word sizes and alignments
var gcArchSizes = map[string]*StdSizes{
	"386":      {4, 4},
	"amd64":    {8, 8},
	"amd64p32": {4, 8},
	"arm":      {4, 4},
	"arm64":    {8, 8},
	"loong64":  {8, 8},
	"mips":     {4, 4},
	"mipsle":   {4, 4},
	"mips64":   {8, 8},
	"mips64le": {8, 8},
	"ppc64":    {8, 8},
	"ppc64le":  {8, 8},
	"riscv64":  {8, 8},
	"s390x":    {8, 8},
	"sparc64":  {8, 8},
	"wasm":     {8, 8},
	/ When adding more architectures here,
	/ update the doc string of SizesFor below.
}

/ SizesFor returns the Sizes used by a compiler for an architecture.
/ The result is nil if a compiler/architecture pair is not known.
/
/ Supported architectures for compiler "gc":
/ "386", "amd64", "amd64p32", "arm", "arm64", "loong64", "mips", "mipsle",
/ "mips64", "mips64le", "ppc64", "ppc64le", "riscv64", "s390x", "sparc64", "wasm".
func SizesFor(compiler, arch string) Sizes {
	var m map[string]*StdSizes
	switch compiler {
	case "gc":
		m = gcArchSizes
	case "gccgo":
		m = gccgoArchSizes
	default:
		return nil
	}
	s, ok := m[arch]
	if !ok {
		return nil
	}
	return s
}

/ stdSizes is used if Config.Sizes == nil.
var stdSizes = SizesFor("gc", "amd64")

func (conf *Config) alignof(T Type) int64 {
	if s := conf.Sizes; s != nil {
		if a := s.Alignof(T); a >= 1 {
			return a
		}
		panic("Config.Sizes.Alignof returned an alignment < 1")
	}
	return stdSizes.Alignof(T)
}

func (conf *Config) offsetsof(T *Struct) []int64 {
	var offsets []int64
	if T.NumFields() > 0 {
		/ compute offsets on demand
		if s := conf.Sizes; s != nil {
			offsets = s.Offsetsof(T.fields)
			/ sanity checks
			if len(offsets) != T.NumFields() {
				panic("Config.Sizes.Offsetsof returned the wrong number of offsets")
			}
			for _, o := range offsets {
				if o < 0 {
					panic("Config.Sizes.Offsetsof returned an offset < 0")
				}
			}
		} else {
			offsets = stdSizes.Offsetsof(T.fields)
		}
	}
	return offsets
}

/ offsetof returns the offset of the field specified via
/ the index sequence relative to typ. All embedded fields
/ must be structs (rather than pointer to structs).
func (conf *Config) offsetof(typ Type, index []int) int64 {
	var o int64
	for _, i := range index {
		s := under(typ).(*Struct)
		o += conf.offsetsof(s)[i]
		typ = s.fields[i].typ
	}
	return o
}

func (conf *Config) sizeof(T Type) int64 {
	if s := conf.Sizes; s != nil {
		if z := s.Sizeof(T); z >= 0 {
			return z
		}
		panic("Config.Sizes.Sizeof returned a size < 0")
	}
	return stdSizes.Sizeof(T)
}

/ align returns the smallest y >= x such that y % a == 0.
func align(x, a int64) int64 {
	y := x + a - 1
	return y - y%a
}

相关信息

go 源码目录

相关文章

go api 源码

go api_test 源码

go array 源码

go assignments 源码

go basic 源码

go builtins 源码

go builtins_test 源码

go call 源码

go chan 源码

go check 源码

^