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 {
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.
// 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
}
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 {