blob: 23f206267547f4fd467fc91a93f0ab3998c2db5c [file] [log] [blame]
// Copyright 2021 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.
package noder
import (
"fmt"
"cmd/compile/internal/types"
"cmd/compile/internal/types2"
)
// Code below based on go/types.StdSizes.
// Intentional differences are marked with "gc:".
type gcSizes struct{}
func (s *gcSizes) Alignof(T types2.Type) int64 {
// For arrays and structs, alignment is defined in terms
// of alignment of the elements and fields, respectively.
switch t := T.Underlying().(type) {
case *types2.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 *types2.Struct:
// 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 i, nf := 0, t.NumFields(); i < nf; i++ {
if a := s.Alignof(t.Field(i).Type()); a > max {
max = a
}
}
return max
case *types2.Slice, *types2.Interface:
// Multiword data structures are effectively structs
// in which each element has size PtrSize.
return int64(types.PtrSize)
case *types2.Basic:
// Strings are like slices and interfaces.
if t.Info()&types2.IsString != 0 {
return int64(types.PtrSize)
}
}
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 > int64(types.RegSize) {
return int64(types.RegSize)
}
return a
}
func isComplex(T types2.Type) bool {
basic, ok := T.Underlying().(*types2.Basic)
return ok && basic.Info()&types2.IsComplex != 0
}
func (s *gcSizes) Offsetsof(fields []*types2.Var) []int64 {
offsets := make([]int64, len(fields))
var o int64
for i, f := range fields {
typ := f.Type()
a := s.Alignof(typ)
o = types.Rnd(o, a)
offsets[i] = o
o += s.Sizeof(typ)
}
return offsets
}
func (s *gcSizes) Sizeof(T types2.Type) int64 {
switch t := T.Underlying().(type) {
case *types2.Basic:
k := t.Kind()
if int(k) < len(basicSizes) {
if s := basicSizes[k]; s > 0 {
return int64(s)
}
}
switch k {
case types2.String:
return int64(types.PtrSize) * 2
case types2.Int, types2.Uint, types2.Uintptr, types2.UnsafePointer:
return int64(types.PtrSize)
}
panic(fmt.Sprintf("unimplemented basic: %v (kind %v)", T, k))
case *types2.Array:
n := t.Len()
if n <= 0 {
return 0
}
// n > 0
// gc: Size includes alignment padding.
return s.Sizeof(t.Elem()) * n
case *types2.Slice:
return int64(types.PtrSize) * 3
case *types2.Struct:
n := t.NumFields()
if n == 0 {
return 0
}
fields := make([]*types2.Var, n)
for i := range fields {
fields[i] = t.Field(i)
}
offsets := s.Offsetsof(fields)
// gc: The last field of a non-zero-sized struct is not allowed to
// have size 0.
last := s.Sizeof(fields[n-1].Type())
if last == 0 && offsets[n-1] > 0 {
last = 1
}
// gc: Size includes alignment padding.
return types.Rnd(offsets[n-1]+last, s.Alignof(t))
case *types2.Interface:
return int64(types.PtrSize) * 2
case *types2.Chan, *types2.Map, *types2.Pointer, *types2.Signature:
return int64(types.PtrSize)
default:
panic(fmt.Sprintf("unimplemented type: %T", t))
}
}
var basicSizes = [...]byte{
types2.Bool: 1,
types2.Int8: 1,
types2.Int16: 2,
types2.Int32: 4,
types2.Int64: 8,
types2.Uint8: 1,
types2.Uint16: 2,
types2.Uint32: 4,
types2.Uint64: 8,
types2.Float32: 4,
types2.Float64: 8,
types2.Complex64: 8,
types2.Complex128: 16,
}