]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/compile/internal/types: refactor struct size calculation
authorMatthew Dempsky <mdempsky@google.com>
Sun, 20 Aug 2023 20:14:44 +0000 (13:14 -0700)
committerMatthew Dempsky <mdempsky@google.com>
Tue, 22 Aug 2023 20:43:19 +0000 (20:43 +0000)
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>
src/cmd/compile/internal/types/size.go

index d0ad2b13d5c3077423ab03a23876f547f5d886e1..59cf97079573b995a924e300de3df8b79e259c2e 100644 (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))
 
-               w := f.Type.width
-               if w < 0 {
-                       base.Fatalf("invalid width %d", f.Type.width)
-               }
-               if w == 0 {
-                       lastzero = o
+               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)
+                       }
                }
-               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
+
+       // 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))
 
-// 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)
+       t.width = size
+       t.align = maxAlign
 }
 
 func (t *Type) widthCalculated() bool {