| // 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 | |
| import ( | |
| "unsafe" | |
| ) | |
| // Type is the runtime representation of a Go type. | |
| // | |
| // Be careful about accessing this type at build time, as the version | |
| // of this type in the compiler/linker may not have the same layout | |
| // as the version in the target binary, due to pointer width | |
| // differences and any experiments. Use cmd/compile/internal/rttype | |
| // or the functions in compiletype.go to access this type instead. | |
| // (TODO: this admonition applies to every type in this package. | |
| // Put it in some shared location?) | |
| type Type struct { | |
| Size_ uintptr | |
| PtrBytes uintptr // number of (prefix) bytes in the type that can contain pointers | |
| Hash uint32 // hash of type; avoids computation in hash tables | |
| TFlag TFlag // extra type information flags | |
| Align_ uint8 // alignment of variable with this type | |
| FieldAlign_ uint8 // alignment of struct field with this type | |
| Kind_ Kind // what kind of type this is (string, int, ...) | |
| // function for comparing objects of this type | |
| // (ptr to object A, ptr to object B) -> ==? | |
| Equal func(unsafe.Pointer, unsafe.Pointer) bool | |
| // GCData stores the GC type data for the garbage collector. | |
| // Normally, GCData points to a bitmask that describes the | |
| // ptr/nonptr fields of the type. The bitmask will have at | |
| // least PtrBytes/ptrSize bits. | |
| // If the TFlagGCMaskOnDemand bit is set, GCData is instead a | |
| // **byte and the pointer to the bitmask is one dereference away. | |
| // The runtime will build the bitmask if needed. | |
| // (See runtime/type.go:getGCMask.) | |
| // Note: multiple types may have the same value of GCData, | |
| // including when TFlagGCMaskOnDemand is set. The types will, of course, | |
| // have the same pointer layout (but not necessarily the same size). | |
| GCData *byte | |
| Str NameOff // string form | |
| PtrToThis TypeOff // type for pointer to this type, may be zero | |
| } | |
| // A Kind represents the specific kind of type that a Type represents. | |
| // The zero Kind is not a valid kind. | |
| type Kind uint8 | |
| const ( | |
| Invalid Kind = iota | |
| Bool | |
| Int | |
| Int8 | |
| Int16 | |
| Int32 | |
| Int64 | |
| Uint | |
| Uint8 | |
| Uint16 | |
| Uint32 | |
| Uint64 | |
| Uintptr | |
| Float32 | |
| Float64 | |
| Complex64 | |
| Complex128 | |
| Array | |
| Chan | |
| Func | |
| Interface | |
| Map | |
| Pointer | |
| Slice | |
| String | |
| Struct | |
| UnsafePointer | |
| ) | |
| // TFlag is used by a Type to signal what extra type information is | |
| // available in the memory directly following the Type value. | |
| type TFlag uint8 | |
| const ( | |
| // TFlagUncommon means that there is a data with a type, UncommonType, | |
| // just beyond the shared-per-type common data. That is, the data | |
| // for struct types will store their UncommonType at one offset, the | |
| // data for interface types will store their UncommonType at a different | |
| // offset. UncommonType is always accessed via a pointer that is computed | |
| // using trust-us-we-are-the-implementors pointer arithmetic. | |
| // | |
| // For example, if t.Kind() == Struct and t.tflag&TFlagUncommon != 0, | |
| // then t has UncommonType data and it can be accessed as: | |
| // | |
| // type structTypeUncommon struct { | |
| // structType | |
| // u UncommonType | |
| // } | |
| // u := &(*structTypeUncommon)(unsafe.Pointer(t)).u | |
| TFlagUncommon TFlag = 1 << 0 | |
| // TFlagExtraStar means the name in the str field has an | |
| // extraneous '*' prefix. This is because for most types T in | |
| // a program, the type *T also exists and reusing the str data | |
| // saves binary size. | |
| TFlagExtraStar TFlag = 1 << 1 | |
| // TFlagNamed means the type has a name. | |
| TFlagNamed TFlag = 1 << 2 | |
| // TFlagRegularMemory means that equal and hash functions can treat | |
| // this type as a single region of t.size bytes. | |
| TFlagRegularMemory TFlag = 1 << 3 | |
| // TFlagGCMaskOnDemand means that the GC pointer bitmask will be | |
| // computed on demand at runtime instead of being precomputed at | |
| // compile time. If this flag is set, the GCData field effectively | |
| // has type **byte instead of *byte. The runtime will store a | |
| // pointer to the GC pointer bitmask in *GCData. | |
| TFlagGCMaskOnDemand TFlag = 1 << 4 | |
| // TFlagDirectIface means that a value of this type is stored directly | |
| // in the data field of an interface, instead of indirectly. | |
| // This flag is just a cached computation of Size_ == PtrBytes == goarch.PtrSize. | |
| TFlagDirectIface TFlag = 1 << 5 | |
| // Leaving this breadcrumb behind for dlv. It should not be used, and no | |
| // Kind should be big enough to set this bit. | |
| KindDirectIface Kind = 1 << 5 | |
| ) | |
| // NameOff is the offset to a name from moduledata.types. See resolveNameOff in runtime. | |
| type NameOff int32 | |
| // TypeOff is the offset to a type from moduledata.types. See resolveTypeOff in runtime. | |
| type TypeOff int32 | |
| // TextOff is an offset from the top of a text section. See (rtype).textOff in runtime. | |
| type TextOff int32 | |
| // String returns the name of k. | |
| func (k Kind) String() string { | |
| if int(k) < len(kindNames) { | |
| return kindNames[k] | |
| } | |
| return kindNames[0] | |
| } | |
| var kindNames = []string{ | |
| Invalid: "invalid", | |
| Bool: "bool", | |
| Int: "int", | |
| Int8: "int8", | |
| Int16: "int16", | |
| Int32: "int32", | |
| Int64: "int64", | |
| Uint: "uint", | |
| Uint8: "uint8", | |
| Uint16: "uint16", | |
| Uint32: "uint32", | |
| Uint64: "uint64", | |
| Uintptr: "uintptr", | |
| Float32: "float32", | |
| Float64: "float64", | |
| Complex64: "complex64", | |
| Complex128: "complex128", | |
| Array: "array", | |
| Chan: "chan", | |
| Func: "func", | |
| Interface: "interface", | |
| Map: "map", | |
| Pointer: "ptr", | |
| Slice: "slice", | |
| String: "string", | |
| Struct: "struct", | |
| UnsafePointer: "unsafe.Pointer", | |
| } | |
| // TypeOf returns the abi.Type of some value. | |
| func TypeOf(a any) *Type { | |
| eface := *(*EmptyInterface)(unsafe.Pointer(&a)) | |
| // Types are either static (for compiler-created types) or | |
| // heap-allocated but always reachable (for reflection-created | |
| // types, held in the central map). So there is no need to | |
| // escape types. noescape here help avoid unnecessary escape | |
| // of v. | |
| return (*Type)(NoEscape(unsafe.Pointer(eface.Type))) | |
| } | |
| // TypeFor returns the abi.Type for a type parameter. | |
| func TypeFor[T any]() *Type { | |
| return (*PtrType)(unsafe.Pointer(TypeOf((*T)(nil)))).Elem | |
| } | |
| func (t *Type) Kind() Kind { return t.Kind_ } | |
| func (t *Type) HasName() bool { | |
| return t.TFlag&TFlagNamed != 0 | |
| } | |
| // Pointers reports whether t contains pointers. | |
| func (t *Type) Pointers() bool { return t.PtrBytes != 0 } | |
| // IsDirectIface reports whether t is stored directly in an interface value. | |
| func (t *Type) IsDirectIface() bool { | |
| return t.TFlag&TFlagDirectIface != 0 | |
| } | |
| func (t *Type) GcSlice(begin, end uintptr) []byte { | |
| if t.TFlag&TFlagGCMaskOnDemand != 0 { | |
| panic("GcSlice can't handle on-demand gcdata types") | |
| } | |
| return unsafe.Slice(t.GCData, int(end))[begin:] | |
| } | |
| // Method on non-interface type | |
| type Method struct { | |
| Name NameOff // name of method | |
| Mtyp TypeOff // method type (without receiver) | |
| Ifn TextOff // fn used in interface call (one-word receiver) | |
| Tfn TextOff // fn used for normal method call | |
| } | |
| // UncommonType is present only for defined types or types with methods | |
| // (if T is a defined type, the uncommonTypes for T and *T have methods). | |
| // Using a pointer to this struct reduces the overall size required | |
| // to describe a non-defined type with no methods. | |
| type UncommonType struct { | |
| PkgPath NameOff // import path; empty for built-in types like int, string | |
| Mcount uint16 // number of methods | |
| Xcount uint16 // number of exported methods | |
| Moff uint32 // offset from this uncommontype to [mcount]Method | |
| _ uint32 // unused | |
| } | |
| func (t *UncommonType) Methods() []Method { | |
| if t.Mcount == 0 { | |
| return nil | |
| } | |
| return (*[1 << 16]Method)(addChecked(unsafe.Pointer(t), uintptr(t.Moff), "t.mcount > 0"))[:t.Mcount:t.Mcount] | |
| } | |
| func (t *UncommonType) ExportedMethods() []Method { | |
| if t.Xcount == 0 { | |
| return nil | |
| } | |
| return (*[1 << 16]Method)(addChecked(unsafe.Pointer(t), uintptr(t.Moff), "t.xcount > 0"))[:t.Xcount:t.Xcount] | |
| } | |
| // addChecked returns p+x. | |
| // | |
| // The whySafe string is ignored, so that the function still inlines | |
| // as efficiently as p+x, but all call sites should use the string to | |
| // record why the addition is safe, which is to say why the addition | |
| // does not cause x to advance to the very end of p's allocation | |
| // and therefore point incorrectly at the next block in memory. | |
| func addChecked(p unsafe.Pointer, x uintptr, whySafe string) unsafe.Pointer { | |
| return unsafe.Pointer(uintptr(p) + x) | |
| } | |
| // Imethod represents a method on an interface type | |
| type Imethod struct { | |
| Name NameOff // name of method | |
| Typ TypeOff // .(*FuncType) underneath | |
| } | |
| // ArrayType represents a fixed array type. | |
| type ArrayType struct { | |
| Type | |
| Elem *Type // array element type | |
| Slice *Type // slice type | |
| Len uintptr | |
| } | |
| // Len returns the length of t if t is an array type, otherwise 0 | |
| func (t *Type) Len() int { | |
| if t.Kind() == Array { | |
| return int((*ArrayType)(unsafe.Pointer(t)).Len) | |
| } | |
| return 0 | |
| } | |
| func (t *Type) Common() *Type { | |
| return t | |
| } | |
| type ChanDir int | |
| const ( | |
| RecvDir ChanDir = 1 << iota // <-chan | |
| SendDir // chan<- | |
| BothDir = RecvDir | SendDir // chan | |
| InvalidDir ChanDir = 0 | |
| ) | |
| // ChanType represents a channel type | |
| type ChanType struct { | |
| Type | |
| Elem *Type | |
| Dir ChanDir | |
| } | |
| type structTypeUncommon struct { | |
| StructType | |
| u UncommonType | |
| } | |
| // ChanDir returns the direction of t if t is a channel type, otherwise InvalidDir (0). | |
| func (t *Type) ChanDir() ChanDir { | |
| if t.Kind() == Chan { | |
| ch := (*ChanType)(unsafe.Pointer(t)) | |
| return ch.Dir | |
| } | |
| return InvalidDir | |
| } | |
| // Uncommon returns a pointer to T's "uncommon" data if there is any, otherwise nil | |
| func (t *Type) Uncommon() *UncommonType { | |
| if t.TFlag&TFlagUncommon == 0 { | |
| return nil | |
| } | |
| switch t.Kind() { | |
| case Struct: | |
| return &(*structTypeUncommon)(unsafe.Pointer(t)).u | |
| case Pointer: | |
| type u struct { | |
| PtrType | |
| u UncommonType | |
| } | |
| return &(*u)(unsafe.Pointer(t)).u | |
| case Func: | |
| type u struct { | |
| FuncType | |
| u UncommonType | |
| } | |
| return &(*u)(unsafe.Pointer(t)).u | |
| case Slice: | |
| type u struct { | |
| SliceType | |
| u UncommonType | |
| } | |
| return &(*u)(unsafe.Pointer(t)).u | |
| case Array: | |
| type u struct { | |
| ArrayType | |
| u UncommonType | |
| } | |
| return &(*u)(unsafe.Pointer(t)).u | |
| case Chan: | |
| type u struct { | |
| ChanType | |
| u UncommonType | |
| } | |
| return &(*u)(unsafe.Pointer(t)).u | |
| case Map: | |
| type u struct { | |
| MapType | |
| u UncommonType | |
| } | |
| return &(*u)(unsafe.Pointer(t)).u | |
| case Interface: | |
| type u struct { | |
| InterfaceType | |
| u UncommonType | |
| } | |
| return &(*u)(unsafe.Pointer(t)).u | |
| default: | |
| type u struct { | |
| Type | |
| u UncommonType | |
| } | |
| return &(*u)(unsafe.Pointer(t)).u | |
| } | |
| } | |
| // Elem returns the element type for t if t is an array, channel, map, pointer, or slice, otherwise nil. | |
| func (t *Type) Elem() *Type { | |
| switch t.Kind() { | |
| case Array: | |
| tt := (*ArrayType)(unsafe.Pointer(t)) | |
| return tt.Elem | |
| case Chan: | |
| tt := (*ChanType)(unsafe.Pointer(t)) | |
| return tt.Elem | |
| case Map: | |
| tt := (*MapType)(unsafe.Pointer(t)) | |
| return tt.Elem | |
| case Pointer: | |
| tt := (*PtrType)(unsafe.Pointer(t)) | |
| return tt.Elem | |
| case Slice: | |
| tt := (*SliceType)(unsafe.Pointer(t)) | |
| return tt.Elem | |
| } | |
| return nil | |
| } | |
| // StructType returns t cast to a *StructType, or nil if its tag does not match. | |
| func (t *Type) StructType() *StructType { | |
| if t.Kind() != Struct { | |
| return nil | |
| } | |
| return (*StructType)(unsafe.Pointer(t)) | |
| } | |
| // MapType returns t cast to a *MapType, or nil if its tag does not match. | |
| func (t *Type) MapType() *MapType { | |
| if t.Kind() != Map { | |
| return nil | |
| } | |
| return (*MapType)(unsafe.Pointer(t)) | |
| } | |
| // ArrayType returns t cast to a *ArrayType, or nil if its tag does not match. | |
| func (t *Type) ArrayType() *ArrayType { | |
| if t.Kind() != Array { | |
| return nil | |
| } | |
| return (*ArrayType)(unsafe.Pointer(t)) | |
| } | |
| // FuncType returns t cast to a *FuncType, or nil if its tag does not match. | |
| func (t *Type) FuncType() *FuncType { | |
| if t.Kind() != Func { | |
| return nil | |
| } | |
| return (*FuncType)(unsafe.Pointer(t)) | |
| } | |
| // InterfaceType returns t cast to a *InterfaceType, or nil if its tag does not match. | |
| func (t *Type) InterfaceType() *InterfaceType { | |
| if t.Kind() != Interface { | |
| return nil | |
| } | |
| return (*InterfaceType)(unsafe.Pointer(t)) | |
| } | |
| // Size returns the size of data with type t. | |
| func (t *Type) Size() uintptr { return t.Size_ } | |
| // Align returns the alignment of data with type t. | |
| func (t *Type) Align() int { return int(t.Align_) } | |
| func (t *Type) FieldAlign() int { return int(t.FieldAlign_) } | |
| type InterfaceType struct { | |
| Type | |
| PkgPath Name // import path | |
| Methods []Imethod // sorted by hash | |
| } | |
| func (t *Type) ExportedMethods() []Method { | |
| ut := t.Uncommon() | |
| if ut == nil { | |
| return nil | |
| } | |
| return ut.ExportedMethods() | |
| } | |
| func (t *Type) NumMethod() int { | |
| if t.Kind() == Interface { | |
| tt := (*InterfaceType)(unsafe.Pointer(t)) | |
| return tt.NumMethod() | |
| } | |
| return len(t.ExportedMethods()) | |
| } | |
| // NumMethod returns the number of interface methods in the type's method set. | |
| func (t *InterfaceType) NumMethod() int { return len(t.Methods) } | |
| func (t *Type) Key() *Type { | |
| if t.Kind() == Map { | |
| return (*MapType)(unsafe.Pointer(t)).Key | |
| } | |
| return nil | |
| } | |
| type SliceType struct { | |
| Type | |
| Elem *Type // slice element type | |
| } | |
| // FuncType represents a function type. | |
| // | |
| // A *Type for each in and out parameter is stored in an array that | |
| // directly follows the funcType (and possibly its uncommonType). So | |
| // a function type with one method, one input, and one output is: | |
| // | |
| // struct { | |
| // funcType | |
| // uncommonType | |
| // [2]*rtype // [0] is in, [1] is out | |
| // } | |
| type FuncType struct { | |
| Type | |
| InCount uint16 | |
| OutCount uint16 // top bit is set if last input parameter is ... | |
| } | |
| func (t *FuncType) In(i int) *Type { | |
| return t.InSlice()[i] | |
| } | |
| func (t *FuncType) NumIn() int { | |
| return int(t.InCount) | |
| } | |
| func (t *FuncType) NumOut() int { | |
| return int(t.OutCount & (1<<15 - 1)) | |
| } | |
| func (t *FuncType) Out(i int) *Type { | |
| return (t.OutSlice()[i]) | |
| } | |
| func (t *FuncType) InSlice() []*Type { | |
| uadd := unsafe.Sizeof(*t) | |
| if t.TFlag&TFlagUncommon != 0 { | |
| uadd += unsafe.Sizeof(UncommonType{}) | |
| } | |
| if t.InCount == 0 { | |
| return nil | |
| } | |
| return (*[1 << 16]*Type)(addChecked(unsafe.Pointer(t), uadd, "t.inCount > 0"))[:t.InCount:t.InCount] | |
| } | |
| func (t *FuncType) OutSlice() []*Type { | |
| outCount := uint16(t.NumOut()) | |
| if outCount == 0 { | |
| return nil | |
| } | |
| uadd := unsafe.Sizeof(*t) | |
| if t.TFlag&TFlagUncommon != 0 { | |
| uadd += unsafe.Sizeof(UncommonType{}) | |
| } | |
| return (*[1 << 17]*Type)(addChecked(unsafe.Pointer(t), uadd, "outCount > 0"))[t.InCount : t.InCount+outCount : t.InCount+outCount] | |
| } | |
| func (t *FuncType) IsVariadic() bool { | |
| return t.OutCount&(1<<15) != 0 | |
| } | |
| type PtrType struct { | |
| Type | |
| Elem *Type // pointer element (pointed at) type | |
| } | |
| type StructField struct { | |
| Name Name // name is always non-empty | |
| Typ *Type // type of field | |
| Offset uintptr // byte offset of field | |
| } | |
| func (f *StructField) Embedded() bool { | |
| return f.Name.IsEmbedded() | |
| } | |
| type StructType struct { | |
| Type | |
| PkgPath Name | |
| Fields []StructField | |
| } | |
| // Name is an encoded type Name with optional extra data. | |
| // | |
| // The first byte is a bit field containing: | |
| // | |
| // 1<<0 the name is exported | |
| // 1<<1 tag data follows the name | |
| // 1<<2 pkgPath nameOff follows the name and tag | |
| // 1<<3 the name is of an embedded (a.k.a. anonymous) field | |
| // | |
| // Following that, there is a varint-encoded length of the name, | |
| // followed by the name itself. | |
| // | |
| // If tag data is present, it also has a varint-encoded length | |
| // followed by the tag itself. | |
| // | |
| // If the import path follows, then 4 bytes at the end of | |
| // the data form a nameOff. The import path is only set for concrete | |
| // methods that are defined in a different package than their type. | |
| // | |
| // If a name starts with "*", then the exported bit represents | |
| // whether the pointed to type is exported. | |
| // | |
| // Note: this encoding must match here and in: | |
| // cmd/compile/internal/reflectdata/reflect.go | |
| // cmd/link/internal/ld/decodesym.go | |
| type Name struct { | |
| Bytes *byte | |
| } | |
| // DataChecked does pointer arithmetic on n's Bytes, and that arithmetic is asserted to | |
| // be safe for the reason in whySafe (which can appear in a backtrace, etc.) | |
| func (n Name) DataChecked(off int, whySafe string) *byte { | |
| return (*byte)(addChecked(unsafe.Pointer(n.Bytes), uintptr(off), whySafe)) | |
| } | |
| // Data does pointer arithmetic on n's Bytes, and that arithmetic is asserted to | |
| // be safe because the runtime made the call (other packages use DataChecked) | |
| func (n Name) Data(off int) *byte { | |
| return (*byte)(addChecked(unsafe.Pointer(n.Bytes), uintptr(off), "the runtime doesn't need to give you a reason")) | |
| } | |
| // IsExported returns "is n exported?" | |
| func (n Name) IsExported() bool { | |
| return (*n.Bytes)&(1<<0) != 0 | |
| } | |
| // HasTag returns true iff there is tag data following this name | |
| func (n Name) HasTag() bool { | |
| return (*n.Bytes)&(1<<1) != 0 | |
| } | |
| // IsEmbedded returns true iff n is embedded (an anonymous field). | |
| func (n Name) IsEmbedded() bool { | |
| return (*n.Bytes)&(1<<3) != 0 | |
| } | |
| // ReadVarint parses a varint as encoded by encoding/binary. | |
| // It returns the number of encoded bytes and the encoded value. | |
| func (n Name) ReadVarint(off int) (int, int) { | |
| v := 0 | |
| for i := 0; ; i++ { | |
| x := *n.DataChecked(off+i, "read varint") | |
| v += int(x&0x7f) << (7 * i) | |
| if x&0x80 == 0 { | |
| return i + 1, v | |
| } | |
| } | |
| } | |
| // IsBlank indicates whether n is "_". | |
| func (n Name) IsBlank() bool { | |
| if n.Bytes == nil { | |
| return false | |
| } | |
| _, l := n.ReadVarint(1) | |
| return l == 1 && *n.Data(2) == '_' | |
| } | |
| // writeVarint writes n to buf in varint form. Returns the | |
| // number of bytes written. n must be nonnegative. | |
| // Writes at most 10 bytes. | |
| func writeVarint(buf []byte, n int) int { | |
| for i := 0; ; i++ { | |
| b := byte(n & 0x7f) | |
| n >>= 7 | |
| if n == 0 { | |
| buf[i] = b | |
| return i + 1 | |
| } | |
| buf[i] = b | 0x80 | |
| } | |
| } | |
| // Name returns the name of n, or empty if it does not actually have a name. | |
| func (n Name) Name() string { | |
| if n.Bytes == nil { | |
| return "" | |
| } | |
| i, l := n.ReadVarint(1) | |
| return unsafe.String(n.DataChecked(1+i, "non-empty string"), l) | |
| } | |
| // Tag returns the tag string for n, or empty if there is none. | |
| func (n Name) Tag() string { | |
| if !n.HasTag() { | |
| return "" | |
| } | |
| i, l := n.ReadVarint(1) | |
| i2, l2 := n.ReadVarint(1 + i + l) | |
| return unsafe.String(n.DataChecked(1+i+l+i2, "non-empty string"), l2) | |
| } | |
| func NewName(n, tag string, exported, embedded bool) Name { | |
| if len(n) >= 1<<29 { | |
| panic("abi.NewName: name too long: " + n[:1024] + "...") | |
| } | |
| if len(tag) >= 1<<29 { | |
| panic("abi.NewName: tag too long: " + tag[:1024] + "...") | |
| } | |
| var nameLen [10]byte | |
| var tagLen [10]byte | |
| nameLenLen := writeVarint(nameLen[:], len(n)) | |
| tagLenLen := writeVarint(tagLen[:], len(tag)) | |
| var bits byte | |
| l := 1 + nameLenLen + len(n) | |
| if exported { | |
| bits |= 1 << 0 | |
| } | |
| if len(tag) > 0 { | |
| l += tagLenLen + len(tag) | |
| bits |= 1 << 1 | |
| } | |
| if embedded { | |
| bits |= 1 << 3 | |
| } | |
| b := make([]byte, l) | |
| b[0] = bits | |
| copy(b[1:], nameLen[:nameLenLen]) | |
| copy(b[1+nameLenLen:], n) | |
| if len(tag) > 0 { | |
| tb := b[1+nameLenLen+len(n):] | |
| copy(tb, tagLen[:tagLenLen]) | |
| copy(tb[tagLenLen:], tag) | |
| } | |
| return Name{Bytes: &b[0]} | |
| } | |
| const ( | |
| TraceArgsLimit = 10 // print no more than 10 args/components | |
| TraceArgsMaxDepth = 5 // no more than 5 layers of nesting | |
| // maxLen is a (conservative) upper bound of the byte stream length. For | |
| // each arg/component, it has no more than 2 bytes of data (size, offset), | |
| // and no more than one {, }, ... at each level (it cannot have both the | |
| // data and ... unless it is the last one, just be conservative). Plus 1 | |
| // for _endSeq. | |
| TraceArgsMaxLen = (TraceArgsMaxDepth*3+2)*TraceArgsLimit + 1 | |
| ) | |
| // Populate the data. | |
| // The data is a stream of bytes, which contains the offsets and sizes of the | |
| // non-aggregate arguments or non-aggregate fields/elements of aggregate-typed | |
| // arguments, along with special "operators". Specifically, | |
| // - for each non-aggregate arg/field/element, its offset from FP (1 byte) and | |
| // size (1 byte) | |
| // - special operators: | |
| // - 0xff - end of sequence | |
| // - 0xfe - print { (at the start of an aggregate-typed argument) | |
| // - 0xfd - print } (at the end of an aggregate-typed argument) | |
| // - 0xfc - print ... (more args/fields/elements) | |
| // - 0xfb - print _ (offset too large) | |
| const ( | |
| TraceArgsEndSeq = 0xff | |
| TraceArgsStartAgg = 0xfe | |
| TraceArgsEndAgg = 0xfd | |
| TraceArgsDotdotdot = 0xfc | |
| TraceArgsOffsetTooLarge = 0xfb | |
| TraceArgsSpecial = 0xf0 // above this are operators, below this are ordinary offsets | |
| ) | |
| // MaxPtrmaskBytes is the maximum length of a GC ptrmask bitmap, | |
| // which holds 1-bit entries describing where pointers are in a given type. | |
| // Above this length, the GC information is recorded as a GC program, | |
| // which can express repetition compactly. In either form, the | |
| // information is used by the runtime to initialize the heap bitmap, | |
| // and for large types (like 128 or more words), they are roughly the | |
| // same speed. GC programs are never much larger and often more | |
| // compact. (If large arrays are involved, they can be arbitrarily | |
| // more compact.) | |
| // | |
| // The cutoff must be large enough that any allocation large enough to | |
| // use a GC program is large enough that it does not share heap bitmap | |
| // bytes with any other objects, allowing the GC program execution to | |
| // assume an aligned start and not use atomic operations. In the current | |
| // runtime, this means all malloc size classes larger than the cutoff must | |
| // be multiples of four words. On 32-bit systems that's 16 bytes, and | |
| // all size classes >= 16 bytes are 16-byte aligned, so no real constraint. | |
| // On 64-bit systems, that's 32 bytes, and 32-byte alignment is guaranteed | |
| // for size classes >= 256 bytes. On a 64-bit system, 256 bytes allocated | |
| // is 32 pointers, the bits for which fit in 4 bytes. So MaxPtrmaskBytes | |
| // must be >= 4. | |
| // | |
| // We used to use 16 because the GC programs do have some constant overhead | |
| // to get started, and processing 128 pointers seems to be enough to | |
| // amortize that overhead well. | |
| // | |
| // To make sure that the runtime's chansend can call typeBitsBulkBarrier, | |
| // we raised the limit to 2048, so that even 32-bit systems are guaranteed to | |
| // use bitmaps for objects up to 64 kB in size. | |
| const MaxPtrmaskBytes = 2048 | |