mirror of
https://github.com/golang/go
synced 2024-11-26 20:11:26 -07:00
cmd/compile/internal/types: refactor struct size calculation
This CL simplifies how struct sizes and field offsets are calculated. Change-Id: If4af778cb49218d295277df596e45bdd8b23ed9d Reviewed-on: https://go-review.googlesource.com/c/go/+/521276 Run-TryBot: Matthew Dempsky <mdempsky@google.com> TryBot-Result: Gopher Robot <gobot@golang.org> Reviewed-by: Than McIntosh <thanm@google.com> Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
This commit is contained in:
parent
56b3b244fd
commit
1f544ef0eb
@ -157,85 +157,41 @@ func expandiface(t *Type) {
|
||||
t.SetAllMethods(methods)
|
||||
}
|
||||
|
||||
func calcStructOffset(errtype *Type, t *Type, o int64, flag int) int64 {
|
||||
// flag is 0 (receiver), 1 (actual struct), or RegSize (in/out parameters)
|
||||
isStruct := flag == 1
|
||||
starto := o
|
||||
maxalign := int32(flag)
|
||||
if maxalign < 1 {
|
||||
maxalign = 1
|
||||
}
|
||||
// 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 duplicated in go/types and cmd/compile/internal/types2.
|
||||
if isStruct && t.NumFields() == 0 && t.Sym() != nil && t.Sym().Name == "align64" && isAtomicStdPkg(t.Sym().Pkg) {
|
||||
maxalign = 8
|
||||
}
|
||||
lastzero := int64(0)
|
||||
for _, f := range t.Fields() {
|
||||
if f.Type == nil {
|
||||
// broken field, just skip it so that other valid fields
|
||||
// get a width.
|
||||
continue
|
||||
}
|
||||
|
||||
// calcStructOffset computes the offsets of a sequence of fields,
|
||||
// starting at the given offset. It returns the resulting offset and
|
||||
// maximum field alignment.
|
||||
func calcStructOffset(t *Type, fields []*Field, offset int64) int64 {
|
||||
for _, f := range fields {
|
||||
CalcSize(f.Type)
|
||||
// If type T contains a field F marked as not-in-heap,
|
||||
// then T must also be a not-in-heap type. Otherwise,
|
||||
// you could heap allocate T and then get a pointer F,
|
||||
// which would be a heap pointer to a not-in-heap type.
|
||||
if f.Type.NotInHeap() {
|
||||
t.SetNotInHeap(true)
|
||||
}
|
||||
if int32(f.Type.align) > maxalign {
|
||||
maxalign = int32(f.Type.align)
|
||||
}
|
||||
if f.Type.align > 0 {
|
||||
o = RoundUp(o, int64(f.Type.align))
|
||||
}
|
||||
if isStruct { // For receiver/args/results, do not set, it depends on ABI
|
||||
f.Offset = o
|
||||
offset = RoundUp(offset, int64(f.Type.align))
|
||||
|
||||
if t.IsStruct() { // param offsets depend on ABI
|
||||
f.Offset = offset
|
||||
|
||||
// If type T contains a field F marked as not-in-heap,
|
||||
// then T must also be a not-in-heap type. Otherwise,
|
||||
// you could heap allocate T and then get a pointer F,
|
||||
// which would be a heap pointer to a not-in-heap type.
|
||||
if f.Type.NotInHeap() {
|
||||
t.SetNotInHeap(true)
|
||||
}
|
||||
}
|
||||
|
||||
w := f.Type.width
|
||||
if w < 0 {
|
||||
base.Fatalf("invalid width %d", f.Type.width)
|
||||
}
|
||||
if w == 0 {
|
||||
lastzero = o
|
||||
}
|
||||
o += w
|
||||
offset += f.Type.width
|
||||
|
||||
maxwidth := MaxWidth
|
||||
// On 32-bit systems, reflect tables impose an additional constraint
|
||||
// that each field start offset must fit in 31 bits.
|
||||
if maxwidth < 1<<32 {
|
||||
maxwidth = 1<<31 - 1
|
||||
}
|
||||
if o >= maxwidth {
|
||||
base.ErrorfAt(typePos(errtype), 0, "type %L too large", errtype)
|
||||
o = 8 // small but nonzero
|
||||
if offset >= maxwidth {
|
||||
base.ErrorfAt(typePos(t), 0, "type %L too large", t)
|
||||
offset = 8 // small but nonzero
|
||||
}
|
||||
}
|
||||
|
||||
// For nonzero-sized structs which end in a zero-sized thing, we add
|
||||
// an extra byte of padding to the type. This padding ensures that
|
||||
// taking the address of the zero-sized thing can't manufacture a
|
||||
// pointer to the next object in the heap. See issue 9401.
|
||||
if flag == 1 && o > starto && o == lastzero {
|
||||
o++
|
||||
}
|
||||
|
||||
// final width is rounded
|
||||
if flag != 0 {
|
||||
o = RoundUp(o, int64(maxalign))
|
||||
}
|
||||
t.align = uint8(maxalign)
|
||||
|
||||
// type width only includes back to first field's offset
|
||||
t.width = o - starto
|
||||
|
||||
return o
|
||||
return offset
|
||||
}
|
||||
|
||||
func isAtomicStdPkg(p *Pkg) bool {
|
||||
@ -411,11 +367,8 @@ func CalcSize(t *Type) {
|
||||
if t.IsFuncArgStruct() {
|
||||
base.Fatalf("CalcSize fn struct %v", t)
|
||||
}
|
||||
// Recognize and mark runtime/internal/sys.nih as not-in-heap.
|
||||
if sym := t.Sym(); sym != nil && sym.Pkg.Path == "runtime/internal/sys" && sym.Name == "nih" {
|
||||
t.SetNotInHeap(true)
|
||||
}
|
||||
w = calcStructOffset(t, t, 0, 1)
|
||||
CalcStructSize(t)
|
||||
w = t.width
|
||||
|
||||
// make fake type to check later to
|
||||
// trigger function argument computation.
|
||||
@ -428,13 +381,13 @@ func CalcSize(t *Type) {
|
||||
// compute their widths as side-effect.
|
||||
case TFUNCARGS:
|
||||
t1 := t.FuncArgs()
|
||||
w = calcStructOffset(t1, t1.recvsTuple(), 0, 0)
|
||||
w = calcStructOffset(t1, t1.paramsTuple(), w, RegSize)
|
||||
w = calcStructOffset(t1, t1.ResultsTuple(), w, RegSize)
|
||||
// TODO(mdempsky): Should package abi be responsible for computing argwid?
|
||||
w = calcStructOffset(t1, t1.Recvs(), 0)
|
||||
w = calcStructOffset(t1, t1.Params(), w)
|
||||
w = RoundUp(w, int64(RegSize))
|
||||
w = calcStructOffset(t1, t1.Results(), w)
|
||||
w = RoundUp(w, int64(RegSize))
|
||||
t1.extra.(*Func).Argwid = w
|
||||
if w%int64(RegSize) != 0 {
|
||||
base.Warn("bad type %v %d\n", t1, w)
|
||||
}
|
||||
t.align = 1
|
||||
}
|
||||
|
||||
@ -455,19 +408,47 @@ func CalcSize(t *Type) {
|
||||
ResumeCheckSize()
|
||||
}
|
||||
|
||||
// CalcStructSize calculates the size of s,
|
||||
// filling in s.Width and s.Align,
|
||||
// CalcStructSize calculates the size of t,
|
||||
// filling in t.width and t.align,
|
||||
// even if size calculation is otherwise disabled.
|
||||
func CalcStructSize(s *Type) {
|
||||
s.width = calcStructOffset(s, s, 0, 1) // sets align
|
||||
}
|
||||
func CalcStructSize(t *Type) {
|
||||
var maxAlign uint8 = 1
|
||||
|
||||
// RecalcSize is like CalcSize, but recalculates t's size even if it
|
||||
// has already been calculated before. It does not recalculate other
|
||||
// types.
|
||||
func RecalcSize(t *Type) {
|
||||
t.align = 0
|
||||
CalcSize(t)
|
||||
// Recognize special types. This logic is duplicated in go/types and
|
||||
// cmd/compile/internal/types2.
|
||||
if sym := t.Sym(); sym != nil {
|
||||
switch {
|
||||
case sym.Name == "align64" && isAtomicStdPkg(sym.Pkg):
|
||||
maxAlign = 8
|
||||
case sym.Pkg.Path == "runtime/internal/sys" && sym.Name == "nih":
|
||||
t.SetNotInHeap(true)
|
||||
}
|
||||
}
|
||||
|
||||
fields := t.Fields()
|
||||
size := calcStructOffset(t, fields, 0)
|
||||
|
||||
// For non-zero-sized structs which end in a zero-sized field, we
|
||||
// add an extra byte of padding to the type. This padding ensures
|
||||
// that taking the address of a zero-sized field can't manufacture a
|
||||
// pointer to the next object in the heap. See issue 9401.
|
||||
if size > 0 && fields[len(fields)-1].Type.width == 0 {
|
||||
size++
|
||||
}
|
||||
|
||||
// The alignment of a struct type is the maximum alignment of its
|
||||
// field types.
|
||||
for _, field := range fields {
|
||||
if align := field.Type.align; align > maxAlign {
|
||||
maxAlign = align
|
||||
}
|
||||
}
|
||||
|
||||
// Final size includes trailing padding.
|
||||
size = RoundUp(size, int64(maxAlign))
|
||||
|
||||
t.width = size
|
||||
t.align = maxAlign
|
||||
}
|
||||
|
||||
func (t *Type) widthCalculated() bool {
|
||||
|
Loading…
Reference in New Issue
Block a user