| // 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 // enumeration for C |
| // 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. |
| // If the KindGCProg bit is set in kind, GCData is a GC program. |
| // Otherwise it is a ptrmask bitmap. See mbitmap.go for details. |
| 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 |
| ) |
| |
| const ( |
| // TODO (khr, drchase) why aren't these in TFlag? Investigate, fix if possible. |
| KindDirectIface Kind = 1 << 5 |
| KindGCProg Kind = 1 << 6 // Type.gc points to GC program |
| KindMask Kind = (1 << 5) - 1 |
| ) |
| |
| // 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 |
| |
| // TFlagUnrolledBitmap marks special types that are unrolled-bitmap |
| // versions of types with GC programs. |
| // These types need to be deallocated when the underlying object |
| // is freed. |
| TFlagUnrolledBitmap TFlag = 1 << 4 |
| ) |
| |
| // 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", |
| } |
| |
| func (t *Type) Kind() Kind { return t.Kind_ & KindMask } |
| |
| 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 } |
| |
| // IfaceIndir reports whether t is stored indirectly in an interface value. |
| func (t *Type) IfaceIndir() bool { |
| return t.Kind_&KindDirectIface == 0 |
| } |
| |
| // isDirectIface reports whether t is stored directly in an interface value. |
| func (t *Type) IsDirectIface() bool { |
| return t.Kind_&KindDirectIface != 0 |
| } |
| |
| func (t *Type) GcSlice(begin, end uintptr) []byte { |
| 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) } |
| |
| type MapType struct { |
| Type |
| Key *Type |
| Elem *Type |
| Bucket *Type // internal type representing a hash bucket |
| // function for hashing keys (ptr to key, seed) -> hash |
| Hasher func(unsafe.Pointer, uintptr) uintptr |
| KeySize uint8 // size of key slot |
| ValueSize uint8 // size of elem slot |
| BucketSize uint16 // size of bucket |
| Flags uint32 |
| } |
| |
| // Note: flag values must match those used in the TMAP case |
| // in ../cmd/compile/internal/reflectdata/reflect.go:writeType. |
| func (mt *MapType) IndirectKey() bool { // store ptr to key instead of key itself |
| return mt.Flags&1 != 0 |
| } |
| func (mt *MapType) IndirectElem() bool { // store ptr to elem instead of elem itself |
| return mt.Flags&2 != 0 |
| } |
| func (mt *MapType) ReflexiveKey() bool { // true if k==k for all keys |
| return mt.Flags&4 != 0 |
| } |
| func (mt *MapType) NeedKeyUpdate() bool { // true if we need to update key on an overwrite |
| return mt.Flags&8 != 0 |
| } |
| func (mt *MapType) HashMightPanic() bool { // true if hash function might panic |
| return mt.Flags&16 != 0 |
| } |
| |
| 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 tag string for n, or empty if there is none. |
| 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 |