// Copyright 2023 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 abi // These functions are the build-time version of the Go type data structures. // Their contents must be kept in sync with their definitions. // Because the host and target type sizes can differ, the compiler and // linker cannot use the host information that they might get from // either unsafe.Sizeof and Alignof, nor runtime, reflect, or reflectlite. // CommonSize returns sizeof(Type) for a compilation target with a given ptrSize func CommonSize(ptrSize int) int { return 4*ptrSize + 8 + 8 } // StructFieldSize returns sizeof(StructField) for a compilation target with a given ptrSize func StructFieldSize(ptrSize int) int { return 3 * ptrSize } // UncommonSize returns sizeof(UncommonType). This currently does not depend on ptrSize. // This exported function is in an internal package, so it may change to depend on ptrSize in the future. func UncommonSize() uint64 { return 4 + 2 + 2 + 4 + 4 } // IMethodSize returns sizeof(IMethod) for a compilation target with a given ptrSize func IMethodSize(ptrSize int) int { return 4 + 4 } // KindOff returns the offset of Type.Kind_ for a compilation target with a given ptrSize func KindOff(ptrSize int) int { return 2*ptrSize + 7 } // SizeOff returns the offset of Type.Size_ for a compilation target with a given ptrSize func SizeOff(ptrSize int) int { return 0 } // PtrBytes returns the offset of Type.PtrBytes for a compilation target with a given ptrSize func PtrBytesOff(ptrSize int) int { return ptrSize } // TFlagOff returns the offset of Type.TFlag for a compilation target with a given ptrSize func TFlagOff(ptrSize int) int { return 2*ptrSize + 4 } // Offset is for computing offsets of type data structures at compile/link time; // the target platform may not be the host platform. Its state includes the // current offset, necessary alignment for the sequence of types, and the size // of pointers and alignment of slices, interfaces, and strings (this is for tearing- // resistant access to these types, if/when that is supported). type Offset struct { off uint64 // the current offset align uint8 // the required alignmentof the container ptrSize uint8 // the size of a pointer in bytes sliceAlign uint8 // the alignment of slices (and interfaces and strings) } // NewOffset returns a new Offset with offset 0 and alignment 1. func NewOffset(ptrSize uint8, twoWordAlignSlices bool) Offset { if twoWordAlignSlices { return Offset{off: 0, align: 1, ptrSize: ptrSize, sliceAlign: 2 * ptrSize} } return Offset{off: 0, align: 1, ptrSize: ptrSize, sliceAlign: ptrSize} } func assertIsAPowerOfTwo(x uint8) { if x == 0 { panic("Zero is not a power of two") } if x&-x == x { return } panic("Not a power of two") } // InitializedOffset returns a new Offset with specified offset, alignment, pointer size, and slice alignment. func InitializedOffset(off int, align uint8, ptrSize uint8, twoWordAlignSlices bool) Offset { assertIsAPowerOfTwo(align) o0 := NewOffset(ptrSize, twoWordAlignSlices) o0.off = uint64(off) o0.align = align return o0 } func (o Offset) align_(a uint8) Offset { o.off = (o.off + uint64(a) - 1) & ^(uint64(a) - 1) if o.align < a { o.align = a } return o } // Align returns the offset obtained by aligning offset to a multiple of a. // a must be a power of two. func (o Offset) Align(a uint8) Offset { assertIsAPowerOfTwo(a) return o.align_(a) } // plus returns the offset obtained by appending a power-of-2-sized-and-aligned object to o. func (o Offset) plus(x uint64) Offset { o = o.align_(uint8(x)) o.off += x return o } // D8 returns the offset obtained by appending an 8-bit field to o. func (o Offset) D8() Offset { return o.plus(1) } // D16 returns the offset obtained by appending a 16-bit field to o. func (o Offset) D16() Offset { return o.plus(2) } // D32 returns the offset obtained by appending a 32-bit field to o. func (o Offset) D32() Offset { return o.plus(4) } // D64 returns the offset obtained by appending a 64-bit field to o. func (o Offset) D64() Offset { return o.plus(8) } // D64 returns the offset obtained by appending a pointer field to o. func (o Offset) P() Offset { if o.ptrSize == 0 { panic("This offset has no defined pointer size") } return o.plus(uint64(o.ptrSize)) } // Slice returns the offset obtained by appending a slice field to o. func (o Offset) Slice() Offset { o = o.align_(o.sliceAlign) o.off += 3 * uint64(o.ptrSize) // There's been discussion of whether slices should be 2-word aligned to allow // use of aligned 2-word load/store to prevent tearing, this is future proofing. // In general, for purposes of struct layout (and very likely default C layout // compatibility) the "size" of a Go type is rounded up to its alignment. return o.Align(o.sliceAlign) } // String returns the offset obtained by appending a string field to o. func (o Offset) String() Offset { o = o.align_(o.sliceAlign) o.off += 2 * uint64(o.ptrSize) return o // We "know" it needs no further alignment } // Interface returns the offset obtained by appending an interface field to o. func (o Offset) Interface() Offset { o = o.align_(o.sliceAlign) o.off += 2 * uint64(o.ptrSize) return o // We "know" it needs no further alignment } // Offset returns the struct-aligned offset (size) of o. // This is at least as large as the current internal offset; it may be larger. func (o Offset) Offset() uint64 { return o.Align(o.align).off } func (o Offset) PlusUncommon() Offset { o.off += UncommonSize() return o } // CommonOffset returns the Offset to the data after the common portion of type data structures. func CommonOffset(ptrSize int, twoWordAlignSlices bool) Offset { return InitializedOffset(CommonSize(ptrSize), uint8(ptrSize), uint8(ptrSize), twoWordAlignSlices) }