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
This commit is contained in:
parent
8535d5aa16
commit
39c0aa5f74
@ -1,4 +1,4 @@
|
||||
951c83af46375019b2fe262635746368a6b9c4ba
|
||||
e4d8ccaed06f81683e79774ede6c61949f6df8b8
|
||||
|
||||
The first line of this file holds the git revision number of the last
|
||||
merge done from the gofrontend repository.
|
||||
|
@ -64,6 +64,7 @@ Gogo::Gogo(Backend* backend, Linemap* linemap, int, int pointer_size)
|
||||
named_types_are_converted_(false),
|
||||
analysis_sets_(),
|
||||
gc_roots_(),
|
||||
type_descriptors_(),
|
||||
imported_inlinable_functions_(),
|
||||
imported_inline_functions_()
|
||||
{
|
||||
@ -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));
|
||||
}
|
||||
|
||||
// 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.
|
||||
|
||||
Named_object*
|
||||
@ -1411,7 +1545,6 @@ Gogo::write_globals()
|
||||
{
|
||||
init_fndecl = this->initialization_function_decl();
|
||||
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.
|
||||
@ -1585,6 +1718,22 @@ Gogo::write_globals()
|
||||
++p)
|
||||
(*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.
|
||||
this->register_gc_vars(var_gc, init_stmts, init_bfn);
|
||||
|
||||
|
@ -617,6 +617,11 @@ class Gogo
|
||||
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.
|
||||
void
|
||||
traverse(Traverse*);
|
||||
@ -901,6 +906,14 @@ class Gogo
|
||||
std::string
|
||||
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.
|
||||
std::string
|
||||
gc_symbol_name(Type*);
|
||||
@ -967,6 +980,15 @@ class Gogo
|
||||
std::vector<Bstatement*>&,
|
||||
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
|
||||
propagate_writebarrierrec();
|
||||
|
||||
@ -1108,6 +1130,8 @@ class Gogo
|
||||
std::vector<Analysis_set> analysis_sets_;
|
||||
// A list of objects to add to the 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
|
||||
// want to inline.
|
||||
std::vector<Named_object*> imported_inlinable_functions_;
|
||||
|
@ -146,6 +146,12 @@
|
||||
// and is named __go_init_main. For other packages it is
|
||||
// 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, such as "struct { pkgpath.i int; J int }". In
|
||||
// this representation unexported names use their pkgpath, exported
|
||||
@ -985,6 +991,23 @@ Gogo::type_descriptor_name(Type* type, Named_type* nt)
|
||||
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
|
||||
// initialize the gcdata field of a type descriptor. This is a local
|
||||
// name never referenced outside of this assembly file. (Note that
|
||||
|
@ -212,6 +212,10 @@ DEF_GO_RUNTIME(GROWSLICE, "runtime.growslice",
|
||||
// Register roots (global variables) for the garbage collector.
|
||||
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.
|
||||
DEF_GO_RUNTIME(NEW, "runtime.newobject", P1(TYPE), R1(POINTER))
|
||||
|
@ -1413,6 +1413,23 @@ Type::make_type_descriptor_var(Gogo* gogo)
|
||||
var_name, false, is_common,
|
||||
initializer_btype, loc,
|
||||
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
|
||||
|
@ -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
|
||||
|
@ -6,7 +6,11 @@
|
||||
|
||||
package runtime
|
||||
|
||||
import "unsafe"
|
||||
import (
|
||||
"runtime/internal/atomic"
|
||||
"runtime/internal/sys"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type _type struct {
|
||||
size uintptr
|
||||
@ -45,19 +49,8 @@ func (t *_type) pkgpath() string {
|
||||
}
|
||||
|
||||
// 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 {
|
||||
switch {
|
||||
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()
|
||||
}
|
||||
return t1 == t2
|
||||
}
|
||||
|
||||
type method struct {
|
||||
@ -164,3 +157,62 @@ type structtype struct {
|
||||
typ _type
|
||||
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]))
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user