diff options
author | Ian Lance Taylor <ian@gcc.gnu.org> | 2019-06-03 23:37:04 +0000 |
---|---|---|
committer | Ian Lance Taylor <ian@gcc.gnu.org> | 2019-06-03 23:37:04 +0000 |
commit | 39c0aa5f74be114ec472a97a12409067b74ac0dc (patch) | |
tree | 19c16f5db5a78699200253d35ce390c429f76126 /libgo/go/reflect | |
parent | 8535d5aa16a895ba54ddb9c9453f093ad42f505e (diff) | |
download | gcc-39c0aa5f74be114ec472a97a12409067b74ac0dc.zip gcc-39c0aa5f74be114ec472a97a12409067b74ac0dc.tar.gz gcc-39c0aa5f74be114ec472a97a12409067b74ac0dc.tar.bz2 |
compiler, runtime, reflect: generate unique type descriptors
Currently, the compiler already generates common symbols for type
descriptors, so the type descriptors are unique. However, when a
type is created through reflection, it is not deduplicated with
compiler-generated types. As a consequence, we cannot assume type
descriptors are unique, and cannot use pointer equality to
compare them. Also, when constructing a reflect.Type, it has to
go through a canonicalization map, which introduces overhead to
reflect.TypeOf, and lock contentions in concurrent programs.
In order for the reflect package to deduplicate types with
compiler-created types, we register all the compiler-created type
descriptors at startup time. The reflect package, when it needs
to create a type, looks up the registry of compiler-created types
before creates a new one. There is no lock contention since the
registry is read-only after initialization.
This lets us get rid of the canonicalization map, and also makes
it possible to compare type descriptors with pointer equality.
Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/179598
From-SVN: r271894
Diffstat (limited to 'libgo/go/reflect')
-rw-r--r-- | libgo/go/reflect/type.go | 131 |
1 files changed, 65 insertions, 66 deletions
diff --git a/libgo/go/reflect/type.go b/libgo/go/reflect/type.go index fb2e5d4..8493d87 100644 --- a/libgo/go/reflect/type.go +++ b/libgo/go/reflect/type.go @@ -1105,15 +1105,14 @@ func (t *rtype) ptrTo() *rtype { return &pi.(*ptrType).rtype } + // Look in known types. s := "*" + *t.string - - canonicalTypeLock.RLock() - r, ok := canonicalType[s] - canonicalTypeLock.RUnlock() - if ok { - p := (*ptrType)(unsafe.Pointer(r.(*rtype))) - pi, _ := ptrMap.LoadOrStore(t, p) - return &pi.(*ptrType).rtype + if tt := lookupType(s); tt != nil { + p := (*ptrType)(unsafe.Pointer(tt)) + if p.elem == t { + pi, _ := ptrMap.LoadOrStore(t, p) + return &pi.(*ptrType).rtype + } } // Create a new ptrType starting with the description @@ -1138,10 +1137,7 @@ func (t *rtype) ptrTo() *rtype { pp.ptrToThis = nil pp.elem = t - q := canonicalize(&pp.rtype) - p := (*ptrType)(unsafe.Pointer(q.(*rtype))) - - pi, _ := ptrMap.LoadOrStore(t, p) + pi, _ := ptrMap.LoadOrStore(t, &pp) return &pi.(*ptrType).rtype } @@ -1447,6 +1443,13 @@ func ChanOf(dir ChanDir, t Type) Type { case BothDir: s = "chan " + *typ.string } + if tt := lookupType(s); tt != nil { + ch := (*chanType)(unsafe.Pointer(tt)) + if ch.elem == typ && ch.dir == uintptr(dir) { + ti, _ := lookupCache.LoadOrStore(ckey, tt) + return ti.(Type) + } + } // Make a channel type. var ichan interface{} = (chan unsafe.Pointer)(nil) @@ -1472,10 +1475,8 @@ func ChanOf(dir ChanDir, t Type) Type { ch.uncommonType = nil ch.ptrToThis = nil - // Canonicalize before storing in lookupCache - ti := toType(&ch.rtype) - lookupCache.Store(ckey, ti.(*rtype)) - return ti + ti, _ := lookupCache.LoadOrStore(ckey, &ch.rtype) + return ti.(Type) } func ismapkey(*rtype) bool // implemented in runtime @@ -1502,6 +1503,13 @@ func MapOf(key, elem Type) Type { // Look in known types. s := "map[" + *ktyp.string + "]" + *etyp.string + if tt := lookupType(s); tt != nil { + mt := (*mapType)(unsafe.Pointer(tt)) + if mt.key == ktyp && mt.elem == etyp { + ti, _ := lookupCache.LoadOrStore(ckey, tt) + return ti.(Type) + } + } // Make a map type. // Note: flag values must match those used in the TMAP case @@ -1544,10 +1552,8 @@ func MapOf(key, elem Type) Type { mt.flags |= 16 } - // Canonicalize before storing in lookupCache - ti := toType(&mt.rtype) - lookupCache.Store(ckey, ti.(*rtype)) - return ti + ti, _ := lookupCache.LoadOrStore(ckey, &mt.rtype) + return ti.(Type) } // FuncOf returns the function type with the given argument and result types. @@ -1625,15 +1631,17 @@ func FuncOf(in, out []Type, variadic bool) Type { } str := funcStr(ft) + if tt := lookupType(str); tt != nil { + if haveIdenticalUnderlyingType(&ft.rtype, tt, true) { + return addToCache(tt) + } + } // Populate the remaining fields of ft and store in cache. ft.string = &str ft.uncommonType = nil ft.ptrToThis = nil - - // Canonicalize before storing in funcLookupCache - tc := toType(&ft.rtype) - return addToCache(tc.(*rtype)) + return addToCache(&ft.rtype) } // funcStr builds a string representation of a funcType. @@ -1873,6 +1881,13 @@ func SliceOf(t Type) Type { // Look in known types. s := "[]" + *typ.string + if tt := lookupType(s); tt != nil { + slice := (*sliceType)(unsafe.Pointer(tt)) + if slice.elem == typ { + ti, _ := lookupCache.LoadOrStore(ckey, tt) + return ti.(Type) + } + } // Make a slice type. var islice interface{} = ([]unsafe.Pointer)(nil) @@ -1888,10 +1903,8 @@ func SliceOf(t Type) Type { slice.uncommonType = nil slice.ptrToThis = nil - // Canonicalize before storing in lookupCache - ti := toType(&slice.rtype) - lookupCache.Store(ckey, ti.(*rtype)) - return ti + ti, _ := lookupCache.LoadOrStore(ckey, &slice.rtype) + return ti.(Type) } // The structLookupCache caches StructOf lookups. @@ -2106,6 +2119,13 @@ func StructOf(fields []StructField) Type { return t } + // Look in known types. + if tt := lookupType(str); tt != nil { + if haveIdenticalUnderlyingType(&typ.rtype, tt, true) { + return addToCache(tt) + } + } + typ.string = &str typ.hash = hash typ.size = size @@ -2214,10 +2234,7 @@ func StructOf(fields []StructField) Type { typ.uncommonType = nil typ.ptrToThis = nil - - // Canonicalize before storing in structLookupCache - ti := toType(&typ.rtype) - return addToCache(ti.(*rtype)) + return addToCache(&typ.rtype) } func runtimeStructField(field StructField) structField { @@ -2300,6 +2317,13 @@ func ArrayOf(count int, elem Type) Type { // Look in known types. s := "[" + strconv.Itoa(count) + "]" + *typ.string + if tt := lookupType(s); tt != nil { + array := (*arrayType)(unsafe.Pointer(tt)) + if array.elem == typ { + ti, _ := lookupCache.LoadOrStore(ckey, tt) + return ti.(Type) + } + } // Make an array type. var iarray interface{} = [1]unsafe.Pointer{} @@ -2451,10 +2475,8 @@ func ArrayOf(count int, elem Type) Type { } } - // Canonicalize before storing in lookupCache - ti := toType(&array.rtype) - lookupCache.Store(ckey, ti.(*rtype)) - return ti + ti, _ := lookupCache.LoadOrStore(ckey, &array.rtype) + return ti.(Type) } func appendVarint(x []byte, v uintptr) []byte { @@ -2466,42 +2488,19 @@ func appendVarint(x []byte, v uintptr) []byte { } // toType converts from a *rtype to a Type that can be returned -// to the client of package reflect. In gc, the only concern is that -// a nil *rtype must be replaced by a nil Type, but in gccgo this -// function takes care of ensuring that multiple *rtype for the same -// type are coalesced into a single Type. -var canonicalType = make(map[string]Type) - -var canonicalTypeLock sync.RWMutex - -func canonicalize(t Type) Type { - if t == nil { - return nil - } - s := t.rawString() - canonicalTypeLock.RLock() - if r, ok := canonicalType[s]; ok { - canonicalTypeLock.RUnlock() - return r - } - canonicalTypeLock.RUnlock() - canonicalTypeLock.Lock() - if r, ok := canonicalType[s]; ok { - canonicalTypeLock.Unlock() - return r - } - canonicalType[s] = t - canonicalTypeLock.Unlock() - return t -} - +// to the client of package reflect. The only concern is that +// a nil *rtype must be replaced by a nil Type. func toType(p *rtype) Type { if p == nil { return nil } - return canonicalize(p) + return p } +// Look up a compiler-generated type descriptor. +// Implemented in runtime. +func lookupType(s string) *rtype + // ifaceIndir reports whether t is stored indirectly in an interface value. func ifaceIndir(t *rtype) bool { return t.kind&kindDirectIface == 0 |