aboutsummaryrefslogtreecommitdiff
path: root/gcc
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 /gcc
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 'gcc')
-rw-r--r--gcc/go/gofrontend/MERGE2
-rw-r--r--gcc/go/gofrontend/gogo.cc151
-rw-r--r--gcc/go/gofrontend/gogo.h24
-rw-r--r--gcc/go/gofrontend/names.cc23
-rw-r--r--gcc/go/gofrontend/runtime.def4
-rw-r--r--gcc/go/gofrontend/types.cc17
6 files changed, 219 insertions, 2 deletions
diff --git a/gcc/go/gofrontend/MERGE b/gcc/go/gofrontend/MERGE
index 1b2fd70..167f3d3 100644
--- a/gcc/go/gofrontend/MERGE
+++ b/gcc/go/gofrontend/MERGE
@@ -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.
diff --git a/gcc/go/gofrontend/gogo.cc b/gcc/go/gofrontend/gogo.cc
index b97367c..ce9bffb 100644
--- a/gcc/go/gofrontend/gogo.cc
+++ b/gcc/go/gofrontend/gogo.cc
@@ -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);
diff --git a/gcc/go/gofrontend/gogo.h b/gcc/go/gofrontend/gogo.h
index 5b77d6d..91e3bdf 100644
--- a/gcc/go/gofrontend/gogo.h
+++ b/gcc/go/gofrontend/gogo.h
@@ -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_;
diff --git a/gcc/go/gofrontend/names.cc b/gcc/go/gofrontend/names.cc
index d9ae5910..e109cfc 100644
--- a/gcc/go/gofrontend/names.cc
+++ b/gcc/go/gofrontend/names.cc
@@ -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
diff --git a/gcc/go/gofrontend/runtime.def b/gcc/go/gofrontend/runtime.def
index 226eeac..b0e6861 100644
--- a/gcc/go/gofrontend/runtime.def
+++ b/gcc/go/gofrontend/runtime.def
@@ -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))
diff --git a/gcc/go/gofrontend/types.cc b/gcc/go/gofrontend/types.cc
index 1b96dc1..011a7af 100644
--- a/gcc/go/gofrontend/types.cc
+++ b/gcc/go/gofrontend/types.cc
@@ -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