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
}