aboutsummaryrefslogtreecommitdiff
path: root/libgo/go/reflect
diff options
context:
space:
mode:
authorIan Lance Taylor <ian@gcc.gnu.org>2019-06-03 23:37:04 +0000
committerIan Lance Taylor <ian@gcc.gnu.org>2019-06-03 23:37:04 +0000
commit39c0aa5f74be114ec472a97a12409067b74ac0dc (patch)
tree19c16f5db5a78699200253d35ce390c429f76126 /libgo/go/reflect
parent8535d5aa16a895ba54ddb9c9453f093ad42f505e (diff)
downloadgcc-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.go131
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