Commit 39c0aa5f by Ian Lance Taylor

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
parent 8535d5aa
951c83af46375019b2fe262635746368a6b9c4ba e4d8ccaed06f81683e79774ede6c61949f6df8b8
The first line of this file holds the git revision number of the last The first line of this file holds the git revision number of the last
merge done from the gofrontend repository. merge done from the gofrontend repository.
...@@ -64,6 +64,7 @@ Gogo::Gogo(Backend* backend, Linemap* linemap, int, int pointer_size) ...@@ -64,6 +64,7 @@ Gogo::Gogo(Backend* backend, Linemap* linemap, int, int pointer_size)
named_types_are_converted_(false), named_types_are_converted_(false),
analysis_sets_(), analysis_sets_(),
gc_roots_(), gc_roots_(),
type_descriptors_(),
imported_inlinable_functions_(), imported_inlinable_functions_(),
imported_inline_functions_() imported_inline_functions_()
{ {
...@@ -903,6 +904,139 @@ Gogo::register_gc_vars(const std::vector<Named_object*>& var_gc, ...@@ -903,6 +904,139 @@ Gogo::register_gc_vars(const std::vector<Named_object*>& var_gc,
init_stmts.push_back(this->backend()->expression_statement(init_bfn, bcall)); init_stmts.push_back(this->backend()->expression_statement(init_bfn, bcall));
} }
// Build the list of type descriptors defined in this package. This is to help
// the reflect package to find compiler-generated types.
// type typeDescriptorList struct {
// count int
// types [...]unsafe.Pointer
// }
static Struct_type*
type_descriptor_list_type(unsigned long len)
{
Location builtin_loc = Linemap::predeclared_location();
Type* int_type = Type::lookup_integer_type("int");
Type* ptr_type = Type::make_pointer_type(Type::make_void_type());
// Avoid creating zero-length type.
unsigned long nelems = (len != 0 ? len : 1);
Expression* len_expr = Expression::make_integer_ul(nelems, NULL,
builtin_loc);
Array_type* array_type = Type::make_array_type(ptr_type, len_expr);
array_type->set_is_array_incomparable();
Struct_type* list_type =
Type::make_builtin_struct_type(2, "count", int_type,
"types", array_type);
return list_type;
}
void
Gogo::build_type_descriptor_list()
{
// Create the list type
Location builtin_loc = Linemap::predeclared_location();
unsigned long len = this->type_descriptors_.size();
Struct_type* list_type = type_descriptor_list_type(len);
Btype* bt = list_type->get_backend(this);
Btype* bat = list_type->field(1)->type()->get_backend(this);
// Create the variable
std::string name = this->type_descriptor_list_symbol(this->package_);
Bvariable* bv = this->backend()->implicit_variable(name, name, bt,
false, true, false,
0);
// Build the initializer
std::vector<unsigned long> indexes;
std::vector<Bexpression*> vals;
std::vector<Type*>::iterator p = this->type_descriptors_.begin();
for (unsigned long i = 0; i < len; ++i, ++p)
{
Bexpression* bexpr = (*p)->type_descriptor_pointer(this,
builtin_loc);
indexes.push_back(i);
vals.push_back(bexpr);
}
Bexpression* barray =
this->backend()->array_constructor_expression(bat, indexes, vals,
builtin_loc);
Translate_context context(this, NULL, NULL, NULL);
std::vector<Bexpression*> fields;
Expression* len_expr = Expression::make_integer_ul(len, NULL,
builtin_loc);
fields.push_back(len_expr->get_backend(&context));
fields.push_back(barray);
Bexpression* binit =
this->backend()->constructor_expression(bt, fields, builtin_loc);
this->backend()->implicit_variable_set_init(bv, name, bt, false,
true, false, binit);
}
// Register the type descriptors with the runtime. This is to help
// the reflect package to find compiler-generated types.
void
Gogo::register_type_descriptors(std::vector<Bstatement*>& init_stmts,
Bfunction* init_bfn)
{
// Create the list type
Location builtin_loc = Linemap::predeclared_location();
Struct_type* list_type = type_descriptor_list_type(1);
Btype* bt = list_type->get_backend(this);
// Build a list of lists.
std::vector<unsigned long> indexes;
std::vector<Bexpression*> vals;
unsigned long i = 0;
for (Packages::iterator it = this->packages_.begin();
it != this->packages_.end();
++it)
{
if (it->second->pkgpath() == "unsafe")
continue;
std::string name = this->type_descriptor_list_symbol(it->second);
Bvariable* bv =
this->backend()->implicit_variable_reference(name, name, bt);
Bexpression* bexpr = this->backend()->var_expression(bv, builtin_loc);
bexpr = this->backend()->address_expression(bexpr, builtin_loc);
indexes.push_back(i);
vals.push_back(bexpr);
i++;
}
Expression* len_expr = Expression::make_integer_ul(i, NULL, builtin_loc);
Type* list_ptr_type = Type::make_pointer_type(list_type);
Type* list_array_type = Type::make_array_type(list_ptr_type, len_expr);
Btype* bat = list_array_type->get_backend(this);
Bexpression* barray =
this->backend()->array_constructor_expression(bat, indexes, vals,
builtin_loc);
// Create a variable holding the list.
std::string name = this->typelists_symbol();
Bvariable* bv = this->backend()->implicit_variable(name, name, bat,
true, true, false,
0);
this->backend()->implicit_variable_set_init(bv, name, bat, true, true,
false, barray);
// Build the call in main package's init function.
Translate_context context(this, NULL, NULL, NULL);
Bexpression* bexpr = this->backend()->var_expression(bv, builtin_loc);
bexpr = this->backend()->address_expression(bexpr, builtin_loc);
Type* array_ptr_type = Type::make_pointer_type(list_array_type);
Expression* expr = Expression::make_backend(bexpr, array_ptr_type,
builtin_loc);
expr = Runtime::make_call(Runtime::REGISTER_TYPE_DESCRIPTORS,
builtin_loc, 2, len_expr->copy(), expr);
Bexpression* bcall = expr->get_backend(&context);
init_stmts.push_back(this->backend()->expression_statement(init_bfn,
bcall));
}
// Build the decl for the initialization function. // Build the decl for the initialization function.
Named_object* Named_object*
...@@ -1411,7 +1545,6 @@ Gogo::write_globals() ...@@ -1411,7 +1545,6 @@ Gogo::write_globals()
{ {
init_fndecl = this->initialization_function_decl(); init_fndecl = this->initialization_function_decl();
init_bfn = init_fndecl->func_value()->get_or_make_decl(this, init_fndecl); init_bfn = init_fndecl->func_value()->get_or_make_decl(this, init_fndecl);
this->init_imports(init_stmts, init_bfn);
} }
// A list of variable initializations. // A list of variable initializations.
...@@ -1585,6 +1718,22 @@ Gogo::write_globals() ...@@ -1585,6 +1718,22 @@ Gogo::write_globals()
++p) ++p)
(*p)->get_backend(this, const_decls, type_decls, func_decls); (*p)->get_backend(this, const_decls, type_decls, func_decls);
// Build the list of type descriptors.
this->build_type_descriptor_list();
if (this->is_main_package())
{
// Register the type descriptor lists, so that at run time
// the reflect package can find compiler-created types, and
// deduplicate if the same type is created with reflection.
// This needs to be done before calling any package's init
// function, as it may create type through reflection.
this->register_type_descriptors(init_stmts, init_bfn);
// Initialize imported packages.
this->init_imports(init_stmts, init_bfn);
}
// Register global variables with the garbage collector. // Register global variables with the garbage collector.
this->register_gc_vars(var_gc, init_stmts, init_bfn); this->register_gc_vars(var_gc, init_stmts, init_bfn);
......
...@@ -617,6 +617,11 @@ class Gogo ...@@ -617,6 +617,11 @@ class Gogo
this->gc_roots_.push_back(expr); this->gc_roots_.push_back(expr);
} }
// Add a type to the descriptor list.
void
add_type_descriptor(Type* type)
{ this->type_descriptors_.push_back(type); }
// Traverse the tree. See the Traverse class. // Traverse the tree. See the Traverse class.
void void
traverse(Traverse*); traverse(Traverse*);
...@@ -901,6 +906,14 @@ class Gogo ...@@ -901,6 +906,14 @@ class Gogo
std::string std::string
type_descriptor_name(Type*, Named_type*); type_descriptor_name(Type*, Named_type*);
// Return the name of the type descriptor list symbol of a package.
std::string
type_descriptor_list_symbol(Package*);
// Return the name of the list of all type descriptor lists.
std::string
typelists_symbol();
// Return the assembler name for the GC symbol for a type. // Return the assembler name for the GC symbol for a type.
std::string std::string
gc_symbol_name(Type*); gc_symbol_name(Type*);
...@@ -967,6 +980,15 @@ class Gogo ...@@ -967,6 +980,15 @@ class Gogo
std::vector<Bstatement*>&, std::vector<Bstatement*>&,
Bfunction* init_bfunction); Bfunction* init_bfunction);
// Build the list of type descriptors.
void
build_type_descriptor_list();
// Register the type descriptors with the runtime.
void
register_type_descriptors(std::vector<Bstatement*>&,
Bfunction* init_bfunction);
void void
propagate_writebarrierrec(); propagate_writebarrierrec();
...@@ -1108,6 +1130,8 @@ class Gogo ...@@ -1108,6 +1130,8 @@ class Gogo
std::vector<Analysis_set> analysis_sets_; std::vector<Analysis_set> analysis_sets_;
// A list of objects to add to the GC roots. // A list of objects to add to the GC roots.
std::vector<Expression*> gc_roots_; std::vector<Expression*> gc_roots_;
// A list of type descriptors that we need to register.
std::vector<Type*> type_descriptors_;
// A list of function declarations with imported bodies that we may // A list of function declarations with imported bodies that we may
// want to inline. // want to inline.
std::vector<Named_object*> imported_inlinable_functions_; std::vector<Named_object*> imported_inlinable_functions_;
......
...@@ -146,6 +146,12 @@ ...@@ -146,6 +146,12 @@
// and is named __go_init_main. For other packages it is // and is named __go_init_main. For other packages it is
// PKGPATH..import. // PKGPATH..import.
// //
// In each pacakge there is a list of all the type descriptors defined
// in this package. The name of the list is PKGPATH..types.
//
// In the main package it gathers all the type descriptor lists in a
// single list, named go..typelists.
//
// The type literal encoding is essentially a single line version of // The type literal encoding is essentially a single line version of
// the type literal, such as "struct { pkgpath.i int; J int }". In // the type literal, such as "struct { pkgpath.i int; J int }". In
// this representation unexported names use their pkgpath, exported // this representation unexported names use their pkgpath, exported
...@@ -985,6 +991,23 @@ Gogo::type_descriptor_name(Type* type, Named_type* nt) ...@@ -985,6 +991,23 @@ Gogo::type_descriptor_name(Type* type, Named_type* nt)
return ret; return ret;
} }
// Return the name of the type descriptor list symbol of a package.
std::string
Gogo::type_descriptor_list_symbol(Package* pkg)
{
return pkg->pkgpath_symbol() + "..types";
}
// Return the name of the list of all type descriptor lists. This is
// only used in the main package.
std::string
Gogo::typelists_symbol()
{
return "go..typelists";
}
// Return the name for the GC symbol for a type. This is used to // Return the name for the GC symbol for a type. This is used to
// initialize the gcdata field of a type descriptor. This is a local // initialize the gcdata field of a type descriptor. This is a local
// name never referenced outside of this assembly file. (Note that // name never referenced outside of this assembly file. (Note that
......
...@@ -212,6 +212,10 @@ DEF_GO_RUNTIME(GROWSLICE, "runtime.growslice", ...@@ -212,6 +212,10 @@ DEF_GO_RUNTIME(GROWSLICE, "runtime.growslice",
// Register roots (global variables) for the garbage collector. // Register roots (global variables) for the garbage collector.
DEF_GO_RUNTIME(REGISTER_GC_ROOTS, "runtime.registerGCRoots", P1(POINTER), R0()) DEF_GO_RUNTIME(REGISTER_GC_ROOTS, "runtime.registerGCRoots", P1(POINTER), R0())
// Register type descriptors.
DEF_GO_RUNTIME(REGISTER_TYPE_DESCRIPTORS, "runtime.registerTypeDescriptors",
P2(INT, POINTER), R0())
// Allocate memory. // Allocate memory.
DEF_GO_RUNTIME(NEW, "runtime.newobject", P1(TYPE), R1(POINTER)) DEF_GO_RUNTIME(NEW, "runtime.newobject", P1(TYPE), R1(POINTER))
......
...@@ -1413,6 +1413,23 @@ Type::make_type_descriptor_var(Gogo* gogo) ...@@ -1413,6 +1413,23 @@ Type::make_type_descriptor_var(Gogo* gogo)
var_name, false, is_common, var_name, false, is_common,
initializer_btype, loc, initializer_btype, loc,
binitializer); binitializer);
// For types that may be created by reflection, add it to the
// list of which we will register the type descriptor to the
// runtime.
// Do not add generated incomparable array/struct types, see
// issue #22605.
if (is_common
&& (this->points_to() != NULL
|| this->channel_type() != NULL
|| this->map_type() != NULL
|| this->function_type() != NULL
|| this->is_slice_type()
|| (this->struct_type() != NULL
&& !this->struct_type()->is_struct_incomparable())
|| (this->array_type() != NULL
&& !this->array_type()->is_array_incomparable())))
gogo->add_type_descriptor(this);
} }
// Return true if this type descriptor is defined in a different // Return true if this type descriptor is defined in a different
......
...@@ -1105,16 +1105,15 @@ func (t *rtype) ptrTo() *rtype { ...@@ -1105,16 +1105,15 @@ func (t *rtype) ptrTo() *rtype {
return &pi.(*ptrType).rtype return &pi.(*ptrType).rtype
} }
// Look in known types.
s := "*" + *t.string s := "*" + *t.string
if tt := lookupType(s); tt != nil {
canonicalTypeLock.RLock() p := (*ptrType)(unsafe.Pointer(tt))
r, ok := canonicalType[s] if p.elem == t {
canonicalTypeLock.RUnlock()
if ok {
p := (*ptrType)(unsafe.Pointer(r.(*rtype)))
pi, _ := ptrMap.LoadOrStore(t, p) pi, _ := ptrMap.LoadOrStore(t, p)
return &pi.(*ptrType).rtype return &pi.(*ptrType).rtype
} }
}
// Create a new ptrType starting with the description // Create a new ptrType starting with the description
// of an *unsafe.Pointer. // of an *unsafe.Pointer.
...@@ -1138,10 +1137,7 @@ func (t *rtype) ptrTo() *rtype { ...@@ -1138,10 +1137,7 @@ func (t *rtype) ptrTo() *rtype {
pp.ptrToThis = nil pp.ptrToThis = nil
pp.elem = t pp.elem = t
q := canonicalize(&pp.rtype) pi, _ := ptrMap.LoadOrStore(t, &pp)
p := (*ptrType)(unsafe.Pointer(q.(*rtype)))
pi, _ := ptrMap.LoadOrStore(t, p)
return &pi.(*ptrType).rtype return &pi.(*ptrType).rtype
} }
...@@ -1447,6 +1443,13 @@ func ChanOf(dir ChanDir, t Type) Type { ...@@ -1447,6 +1443,13 @@ func ChanOf(dir ChanDir, t Type) Type {
case BothDir: case BothDir:
s = "chan " + *typ.string 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. // Make a channel type.
var ichan interface{} = (chan unsafe.Pointer)(nil) var ichan interface{} = (chan unsafe.Pointer)(nil)
...@@ -1472,10 +1475,8 @@ func ChanOf(dir ChanDir, t Type) Type { ...@@ -1472,10 +1475,8 @@ func ChanOf(dir ChanDir, t Type) Type {
ch.uncommonType = nil ch.uncommonType = nil
ch.ptrToThis = nil ch.ptrToThis = nil
// Canonicalize before storing in lookupCache ti, _ := lookupCache.LoadOrStore(ckey, &ch.rtype)
ti := toType(&ch.rtype) return ti.(Type)
lookupCache.Store(ckey, ti.(*rtype))
return ti
} }
func ismapkey(*rtype) bool // implemented in runtime func ismapkey(*rtype) bool // implemented in runtime
...@@ -1502,6 +1503,13 @@ func MapOf(key, elem Type) Type { ...@@ -1502,6 +1503,13 @@ func MapOf(key, elem Type) Type {
// Look in known types. // Look in known types.
s := "map[" + *ktyp.string + "]" + *etyp.string 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. // Make a map type.
// Note: flag values must match those used in the TMAP case // Note: flag values must match those used in the TMAP case
...@@ -1544,10 +1552,8 @@ func MapOf(key, elem Type) Type { ...@@ -1544,10 +1552,8 @@ func MapOf(key, elem Type) Type {
mt.flags |= 16 mt.flags |= 16
} }
// Canonicalize before storing in lookupCache ti, _ := lookupCache.LoadOrStore(ckey, &mt.rtype)
ti := toType(&mt.rtype) return ti.(Type)
lookupCache.Store(ckey, ti.(*rtype))
return ti
} }
// FuncOf returns the function type with the given argument and result types. // FuncOf returns the function type with the given argument and result types.
...@@ -1625,15 +1631,17 @@ func FuncOf(in, out []Type, variadic bool) Type { ...@@ -1625,15 +1631,17 @@ func FuncOf(in, out []Type, variadic bool) Type {
} }
str := funcStr(ft) 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. // Populate the remaining fields of ft and store in cache.
ft.string = &str ft.string = &str
ft.uncommonType = nil ft.uncommonType = nil
ft.ptrToThis = nil ft.ptrToThis = nil
return addToCache(&ft.rtype)
// Canonicalize before storing in funcLookupCache
tc := toType(&ft.rtype)
return addToCache(tc.(*rtype))
} }
// funcStr builds a string representation of a funcType. // funcStr builds a string representation of a funcType.
...@@ -1873,6 +1881,13 @@ func SliceOf(t Type) Type { ...@@ -1873,6 +1881,13 @@ func SliceOf(t Type) Type {
// Look in known types. // Look in known types.
s := "[]" + *typ.string 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. // Make a slice type.
var islice interface{} = ([]unsafe.Pointer)(nil) var islice interface{} = ([]unsafe.Pointer)(nil)
...@@ -1888,10 +1903,8 @@ func SliceOf(t Type) Type { ...@@ -1888,10 +1903,8 @@ func SliceOf(t Type) Type {
slice.uncommonType = nil slice.uncommonType = nil
slice.ptrToThis = nil slice.ptrToThis = nil
// Canonicalize before storing in lookupCache ti, _ := lookupCache.LoadOrStore(ckey, &slice.rtype)
ti := toType(&slice.rtype) return ti.(Type)
lookupCache.Store(ckey, ti.(*rtype))
return ti
} }
// The structLookupCache caches StructOf lookups. // The structLookupCache caches StructOf lookups.
...@@ -2106,6 +2119,13 @@ func StructOf(fields []StructField) Type { ...@@ -2106,6 +2119,13 @@ func StructOf(fields []StructField) Type {
return t 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.string = &str
typ.hash = hash typ.hash = hash
typ.size = size typ.size = size
...@@ -2214,10 +2234,7 @@ func StructOf(fields []StructField) Type { ...@@ -2214,10 +2234,7 @@ func StructOf(fields []StructField) Type {
typ.uncommonType = nil typ.uncommonType = nil
typ.ptrToThis = nil typ.ptrToThis = nil
return addToCache(&typ.rtype)
// Canonicalize before storing in structLookupCache
ti := toType(&typ.rtype)
return addToCache(ti.(*rtype))
} }
func runtimeStructField(field StructField) structField { func runtimeStructField(field StructField) structField {
...@@ -2300,6 +2317,13 @@ func ArrayOf(count int, elem Type) Type { ...@@ -2300,6 +2317,13 @@ func ArrayOf(count int, elem Type) Type {
// Look in known types. // Look in known types.
s := "[" + strconv.Itoa(count) + "]" + *typ.string 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. // Make an array type.
var iarray interface{} = [1]unsafe.Pointer{} var iarray interface{} = [1]unsafe.Pointer{}
...@@ -2451,10 +2475,8 @@ func ArrayOf(count int, elem Type) Type { ...@@ -2451,10 +2475,8 @@ func ArrayOf(count int, elem Type) Type {
} }
} }
// Canonicalize before storing in lookupCache ti, _ := lookupCache.LoadOrStore(ckey, &array.rtype)
ti := toType(&array.rtype) return ti.(Type)
lookupCache.Store(ckey, ti.(*rtype))
return ti
} }
func appendVarint(x []byte, v uintptr) []byte { func appendVarint(x []byte, v uintptr) []byte {
...@@ -2466,42 +2488,19 @@ 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 // 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 // to the client of package reflect. The only concern is that
// a nil *rtype must be replaced by a nil Type, but in gccgo this // a nil *rtype must be replaced by a nil Type.
// 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
}
func toType(p *rtype) Type { func toType(p *rtype) Type {
if p == nil { if p == nil {
return 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. // ifaceIndir reports whether t is stored indirectly in an interface value.
func ifaceIndir(t *rtype) bool { func ifaceIndir(t *rtype) bool {
return t.kind&kindDirectIface == 0 return t.kind&kindDirectIface == 0
......
...@@ -6,7 +6,11 @@ ...@@ -6,7 +6,11 @@
package runtime package runtime
import "unsafe" import (
"runtime/internal/atomic"
"runtime/internal/sys"
"unsafe"
)
type _type struct { type _type struct {
size uintptr size uintptr
...@@ -45,19 +49,8 @@ func (t *_type) pkgpath() string { ...@@ -45,19 +49,8 @@ func (t *_type) pkgpath() string {
} }
// Return whether two type descriptors are equal. // Return whether two type descriptors are equal.
// This is gccgo-specific, as gccgo, unlike gc, permits multiple
// independent descriptors for a single type.
func eqtype(t1, t2 *_type) bool { func eqtype(t1, t2 *_type) bool {
switch { return t1 == t2
case t1 == t2:
return true
case t1 == nil || t2 == nil:
return false
case t1.kind != t2.kind || t1.hash != t2.hash:
return false
default:
return t1.string() == t2.string()
}
} }
type method struct { type method struct {
...@@ -164,3 +157,62 @@ type structtype struct { ...@@ -164,3 +157,62 @@ type structtype struct {
typ _type typ _type
fields []structfield fields []structfield
} }
// typeDescriptorList holds a list of type descriptors generated
// by the compiler. This is used for the compiler to register
// type descriptors to the runtime.
// The layout is known to the compiler.
//go:notinheap
type typeDescriptorList struct {
count int
types [1]uintptr // variable length
}
// typelist holds all type descriptors generated by the comiler.
// This is for the reflect package to deduplicate type descriptors
// when it creates a type that is also a compiler-generated type.
var typelist struct {
initialized uint32
lists []*typeDescriptorList // one element per package
types map[string]uintptr // map from a type's string to *_type, lazily populated
// TODO: use a sorted array instead?
}
var typelistLock mutex
// The compiler generates a call of this function in the main
// package's init function, to register compiler-generated
// type descriptors.
// p points to a list of *typeDescriptorList, n is the length
// of the list.
//go:linkname registerTypeDescriptors runtime.registerTypeDescriptors
func registerTypeDescriptors(n int, p unsafe.Pointer) {
*(*slice)(unsafe.Pointer(&typelist.lists)) = slice{p, n, n}
}
// The reflect package uses this function to look up a compiler-
// generated type descriptor.
//go:linkname reflect_lookupType reflect.lookupType
func reflect_lookupType(s string) *_type {
// Lazy initialization. We don't need to do this if we never create
// types through reflection.
if atomic.Load(&typelist.initialized) == 0 {
lock(&typelistLock)
if atomic.Load(&typelist.initialized) == 0 {
n := 0
for _, list := range typelist.lists {
n += list.count
}
typelist.types = make(map[string]uintptr, n)
for _, list := range typelist.lists {
for i := 0; i < list.count; i++ {
typ := *(**_type)(add(unsafe.Pointer(&list.types), uintptr(i)*sys.PtrSize))
typelist.types[typ.string()] = uintptr(unsafe.Pointer(typ))
}
}
atomic.Store(&typelist.initialized, 1)
}
unlock(&typelistLock)
}
return (*_type)(unsafe.Pointer(typelist.types[s]))
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment