package abi import ( "go/token" "go/types" "log" "github.com/goplus/llgo/runtime/abi" ) // Builds a type representing a Bucket structure for // the given map type. This type is not visible to users - // we include only enough information to generate a correct GC // program for it. // Make sure this stays in sync with runtime/map.go. // // A "bucket" is a "struct" { // tophash [BUCKETSIZE]uint8 // keys [BUCKETSIZE]keyType // elems [BUCKETSIZE]elemType // overflow *bucket // } const ( BUCKETSIZE = abi.MapBucketCount MAXKEYSIZE = abi.MapMaxKeyBytes MAXELEMSIZE = abi.MapMaxElemBytes ) func makefield(name string, t types.Type) *types.Var { return types.NewField(token.NoPos, nil, name, t, false) } // MapBucketType makes the map bucket type given the type of the map. func MapBucketType(t *types.Map, sizes types.Sizes) types.Type { keytype := t.Key() elemtype := t.Elem() if sizes.Sizeof(keytype) > MAXKEYSIZE { keytype = types.NewPointer(keytype) } if sizes.Sizeof(elemtype) > MAXELEMSIZE { elemtype = types.NewPointer(elemtype) } field := make([]*types.Var, 0, 5) // The first field is: uint8 topbits[BUCKETSIZE]. arr := types.NewArray(types.Typ[types.Uint8], BUCKETSIZE) field = append(field, makefield("topbits", arr)) arr = types.NewArray(keytype, BUCKETSIZE) //arr.SetNoalg(true) keys := makefield("keys", arr) field = append(field, keys) arr = types.NewArray(elemtype, BUCKETSIZE) //arr.SetNoalg(true) elems := makefield("elems", arr) field = append(field, elems) // If keys and elems have no pointers, the map implementation // can keep a list of overflow pointers on the side so that // buckets can be marked as having no pointers. // Arrange for the bucket to have no pointers by changing // the type of the overflow field to uintptr in this case. // See comment on hmap.overflow in runtime/map.go. otyp := types.Typ[types.UnsafePointer] if !HasPtrData(elemtype) && !HasPtrData(keytype) { otyp = types.Typ[types.Uintptr] } overflow := makefield("overflow", otyp) field = append(field, overflow) // link up fields bucket := types.NewStruct(field[:], nil) // Check invariants that map code depends on. if !types.Comparable(t.Key()) { log.Fatalf("unsupported map key type for %v", t) } if BUCKETSIZE < 8 { log.Fatalf("bucket size %d too small for proper alignment %d", BUCKETSIZE, 8) } if uint8(sizes.Alignof(keytype)) > BUCKETSIZE { log.Fatalf("key align too big for %v", t) } if uint8(sizes.Alignof(elemtype)) > BUCKETSIZE { log.Fatalf("elem align %d too big for %v, BUCKETSIZE=%d", sizes.Alignof(elemtype), t, BUCKETSIZE) } if sizes.Alignof(keytype) > MAXKEYSIZE { log.Fatalf("key size too large for %v", t) } if sizes.Alignof(elemtype) > MAXELEMSIZE { log.Fatalf("elem size too large for %v", t) } if sizes.Alignof(t.Key()) > MAXKEYSIZE && !isPointer(keytype) { log.Fatalf("key indirect incorrect for %v", t) } if sizes.Alignof(t.Elem()) > MAXELEMSIZE && !isPointer(elemtype) { log.Fatalf("elem indirect incorrect for %v", t) } if sizes.Sizeof(keytype)%sizes.Alignof(keytype) != 0 { log.Fatalf("key size not a multiple of key align for %v", t) } if sizes.Sizeof(elemtype)%sizes.Alignof(elemtype) != 0 { log.Fatalf("elem size not a multiple of elem align for %v", t) } if uint8(sizes.Alignof(bucket))%uint8(sizes.Alignof(keytype)) != 0 { log.Fatalf("bucket align not multiple of key align %v", t) } if uint8(sizes.Alignof(bucket))%uint8(sizes.Alignof(elemtype)) != 0 { log.Fatalf("bucket align not multiple of elem align %v", t) } offs := sizes.Offsetsof(field) if offs[1]%sizes.Alignof(keytype) != 0 { log.Fatalf("bad alignment of keys in bmap for %v", t) } if offs[2]%sizes.Alignof(elemtype) != 0 { log.Fatalf("bad alignment of elems in bmap for %v", t) } // // Double-check that overflow field is final memory in struct, // // with no padding at end. // if overflow.Offset != bucket.Size()-int64(types.PtrSize) { // log.Fatalf("bad offset of overflow in bmap for %v, overflow.Offset=%d, bucket.Size()-int64(types.PtrSize)=%d", // t, overflow.Offset, bucket.Size()-int64(types.PtrSize)) // } return bucket } func isPointer(t types.Type) (ok bool) { _, ok = t.Underlying().(*types.Pointer) return } func MapTypeFlags(t *types.Map, sizes types.Sizes) (flags int) { if sizes.Sizeof(t.Key()) > MAXKEYSIZE { flags |= 1 // indirect key } if sizes.Sizeof(t.Elem()) > MAXELEMSIZE { flags |= 2 // indirect value } if IsReflexive(t.Key()) { flags |= 4 // reflexive key } if needkeyupdate(t.Key()) { flags |= 8 // need key update } if hashMightPanic(t.Key()) { flags |= 16 // hash might panic } return } // $GOROOT/src/cmd/compile/internal/reflectdata/reflect.go // func MapBucketType(t *types.Type) *types.Type { // if t.MapType().Bucket != nil { // return t.MapType().Bucket // } // keytype := t.Key() // elemtype := t.Elem() // types.CalcSize(keytype) // types.CalcSize(elemtype) // if keytype.Size() > MAXKEYSIZE { // keytype = types.NewPtr(keytype) // } // if elemtype.Size() > MAXELEMSIZE { // elemtype = types.NewPtr(elemtype) // } // field := make([]*types.Field, 0, 5) // // The first field is: uint8 topbits[BUCKETSIZE]. // arr := types.NewArray(types.Types[types.TUINT8], BUCKETSIZE) // field = append(field, makefield("topbits", arr)) // arr = types.NewArray(keytype, BUCKETSIZE) // arr.SetNoalg(true) // keys := makefield("keys", arr) // field = append(field, keys) // arr = types.NewArray(elemtype, BUCKETSIZE) // arr.SetNoalg(true) // elems := makefield("elems", arr) // field = append(field, elems) // // If keys and elems have no pointers, the map implementation // // can keep a list of overflow pointers on the side so that // // buckets can be marked as having no pointers. // // Arrange for the bucket to have no pointers by changing // // the type of the overflow field to uintptr in this case. // // See comment on hmap.overflow in runtime/map.go. // otyp := types.Types[types.TUNSAFEPTR] // if !elemtype.HasPointers() && !keytype.HasPointers() { // otyp = types.Types[types.TUINTPTR] // } // overflow := makefield("overflow", otyp) // field = append(field, overflow) // // link up fields // bucket := types.NewStruct(field[:]) // bucket.SetNoalg(true) // types.CalcSize(bucket) // // Check invariants that map code depends on. // if !types.IsComparable(t.Key()) { // base.Fatalf("unsupported map key type for %v", t) // } // if BUCKETSIZE < 8 { // base.Fatalf("bucket size %d too small for proper alignment %d", BUCKETSIZE, 8) // } // if uint8(keytype.Alignment()) > BUCKETSIZE { // base.Fatalf("key align too big for %v", t) // } // if uint8(elemtype.Alignment()) > BUCKETSIZE { // base.Fatalf("elem align %d too big for %v, BUCKETSIZE=%d", elemtype.Alignment(), t, BUCKETSIZE) // } // if keytype.Size() > MAXKEYSIZE { // base.Fatalf("key size too large for %v", t) // } // if elemtype.Size() > MAXELEMSIZE { // base.Fatalf("elem size too large for %v", t) // } // if t.Key().Size() > MAXKEYSIZE && !keytype.IsPtr() { // base.Fatalf("key indirect incorrect for %v", t) // } // if t.Elem().Size() > MAXELEMSIZE && !elemtype.IsPtr() { // base.Fatalf("elem indirect incorrect for %v", t) // } // if keytype.Size()%keytype.Alignment() != 0 { // base.Fatalf("key size not a multiple of key align for %v", t) // } // if elemtype.Size()%elemtype.Alignment() != 0 { // base.Fatalf("elem size not a multiple of elem align for %v", t) // } // if uint8(bucket.Alignment())%uint8(keytype.Alignment()) != 0 { // base.Fatalf("bucket align not multiple of key align %v", t) // } // if uint8(bucket.Alignment())%uint8(elemtype.Alignment()) != 0 { // base.Fatalf("bucket align not multiple of elem align %v", t) // } // if keys.Offset%keytype.Alignment() != 0 { // base.Fatalf("bad alignment of keys in bmap for %v", t) // } // if elems.Offset%elemtype.Alignment() != 0 { // base.Fatalf("bad alignment of elems in bmap for %v", t) // } // // Double-check that overflow field is final memory in struct, // // with no padding at end. // if overflow.Offset != bucket.Size()-int64(types.PtrSize) { // base.Fatalf("bad offset of overflow in bmap for %v, overflow.Offset=%d, bucket.Size()-int64(types.PtrSize)=%d", // t, overflow.Offset, bucket.Size()-int64(types.PtrSize)) // } // t.MapType().Bucket = bucket // bucket.StructType().Map = t // return bucket // } // PtrDataSize returns the length in bytes of the prefix of t // containing pointer data. Anything after this offset is scalar data. // // PtrDataSize is only defined for actual Go types. It's an error to // use it on compiler-internal types (e.g., TSSA, TRESULTS). func HasPtrData(t types.Type) bool { switch t := t.Underlying().(type) { case *types.Basic: switch t.Kind() { case types.String: return true case types.UnsafePointer: return true default: return false } case *types.Pointer: return true case *types.Signature, *types.Chan, *types.Map: return true case *types.Interface: return true case *types.Slice: return true case *types.Array: if t.Len() == 0 { return false } return HasPtrData(t.Elem()) case *types.Struct: for i := 0; i < t.NumFields(); i++ { if HasPtrData(t.Field(i).Type()) { return true } } return false default: log.Fatalf("PtrDataSize: unexpected type, %v", t) return false } } // $GOROOT/src/cmd/compile/internal/types/type.go // func PtrDataSize(t *Type) int64 { // switch t.Kind() { // case TBOOL, TINT8, TUINT8, TINT16, TUINT16, TINT32, // TUINT32, TINT64, TUINT64, TINT, TUINT, // TUINTPTR, TCOMPLEX64, TCOMPLEX128, TFLOAT32, TFLOAT64: // return 0 // case TPTR: // if t.Elem().NotInHeap() { // return 0 // } // return int64(PtrSize) // case TUNSAFEPTR, TFUNC, TCHAN, TMAP: // return int64(PtrSize) // case TSTRING: // // struct { byte *str; intgo len; } // return int64(PtrSize) // case TINTER: // // struct { Itab *tab; void *data; } or // // struct { Type *type; void *data; } // // Note: see comment in typebits.Set // return 2 * int64(PtrSize) // case TSLICE: // if t.Elem().NotInHeap() { // return 0 // } // // struct { byte *array; uintgo len; uintgo cap; } // return int64(PtrSize) // case TARRAY: // if t.NumElem() == 0 { // return 0 // } // // t.NumElem() > 0 // size := PtrDataSize(t.Elem()) // if size == 0 { // return 0 // } // return (t.NumElem()-1)*t.Elem().Size() + size // case TSTRUCT: // // Find the last field that has pointers, if any. // fs := t.Fields().Slice() // for i := len(fs) - 1; i >= 0; i-- { // if size := PtrDataSize(fs[i].Type); size > 0 { // return fs[i].Offset + size // } // } // return 0 // case TSSA: // if t != TypeInt128 { // base.Fatalf("PtrDataSize: unexpected ssa type %v", t) // } // return 0 // default: // base.Fatalf("PtrDataSize: unexpected type, %v", t) // return 0 // } // } // IsReflexive reports whether t has a reflexive equality operator. // That is, if x==x for all x of type t. func IsReflexive(t types.Type) bool { switch t := t.Underlying().(type) { case *types.Basic: switch t.Kind() { case types.Float32, types.Float64, types.Complex64, types.Complex128: return false default: return true } case *types.Pointer, *types.Chan: return true case *types.Interface: return false case *types.Array: return IsReflexive(t.Elem()) case *types.Struct: for i, n := 0, t.NumFields(); i < n; i++ { if !IsReflexive(t.Field(i).Type()) { return false } } return true default: log.Fatalf("bad type for map key: %v", t) return false } } // $GOROOT/src/cmd/compile/internal/types/type.go // func IsReflexive(t *Type) bool { // switch t.Kind() { // case TBOOL, // TINT, // TUINT, // TINT8, // TUINT8, // TINT16, // TUINT16, // TINT32, // TUINT32, // TINT64, // TUINT64, // TUINTPTR, // TPTR, // TUNSAFEPTR, // TSTRING, // TCHAN: // return true // case TFLOAT32, // TFLOAT64, // TCOMPLEX64, // TCOMPLEX128, // TINTER: // return false // case TARRAY: // return IsReflexive(t.Elem()) // case TSTRUCT: // for _, t1 := range t.Fields().Slice() { // if !IsReflexive(t1.Type) { // return false // } // } // return true // default: // base.Fatalf("bad type for map key: %v", t) // return false // } // } // $GOROOT/src/cmd/compile/internal/types/type.go // needkeyupdate reports whether map updates with t as a key // need the key to be updated. func needkeyupdate(t types.Type) bool { switch t := t.Underlying().(type) { case *types.Basic: switch t.Kind() { case types.Float32, types.Float64, types.Complex64, types.Complex128: return true // floats and complex can be +0/-0 case types.String: return true // strings might have smaller backing stores default: return false } case *types.Interface: return true case *types.Pointer, *types.Chan: return false case *types.Array: return needkeyupdate(t.Elem()) case *types.Struct: for i, n := 0, t.NumFields(); i < n; i++ { if needkeyupdate(t.Field(i).Type()) { return true } } return false default: log.Fatalf("bad type for map key: %v", t) return true } } // $GOROOT/src/cmd/compile/internal/reflectdata/reflect.go // func needkeyupdate(t *types.Type) bool { // switch t.Kind() { // case types.TBOOL, types.TINT, types.TUINT, types.TINT8, types.TUINT8, types.TINT16, types.TUINT16, types.TINT32, types.TUINT32, // types.TINT64, types.TUINT64, types.TUINTPTR, types.TPTR, types.TUNSAFEPTR, types.TCHAN: // return false // case types.TFLOAT32, types.TFLOAT64, types.TCOMPLEX64, types.TCOMPLEX128, // floats and complex can be +0/-0 // types.TINTER, // types.TSTRING: // strings might have smaller backing stores // return true // case types.TARRAY: // return needkeyupdate(t.Elem()) // case types.TSTRUCT: // for _, t1 := range t.Fields().Slice() { // if needkeyupdate(t1.Type) { // return true // } // } // return false // default: // base.Fatalf("bad type for map key: %v", t) // return true // } // } // hashMightPanic reports whether the hash of a map key of type t might panic. func hashMightPanic(t types.Type) bool { switch t := t.Underlying().(type) { case *types.Interface: return true case *types.Array: return hashMightPanic(t.Elem()) case *types.Struct: for i, n := 0, t.NumFields(); i < n; i++ { if hashMightPanic(t.Field(i).Type()) { return true } } return false default: return false } } // $GOROOT/src/cmd/compile/internal/reflectdata/reflect.go // func hashMightPanic(t *types.Type) bool { // switch t.Kind() { // case types.TINTER: // return true // case types.TARRAY: // return hashMightPanic(t.Elem()) // case types.TSTRUCT: // for _, t1 := range t.Fields().Slice() { // if hashMightPanic(t1.Type) { // return true // } // } // return false // default: // return false // } // }