1
0
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:
Matthew Dempsky 2023-08-20 13:14:44 -07:00
parent 56b3b244fd
commit 1f544ef0eb

View File

@ -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 {