From 18768faed3875e1d33b122fbf867788add95f056 Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Sat, 23 Nov 2013 19:01:57 +0000 Subject: [PATCH] compiler: Fix backend representation of calls to interface methods. Also unify all identical result parameter sets into a single struct type, and fix the use of backend function pointers. * go-gcc.cc (Gcc_backend::function_type): Add result_struct parameter. From-SVN: r205316 --- gcc/go/ChangeLog | 5 + gcc/go/go-gcc.cc | 29 +----- gcc/go/gofrontend/backend.h | 15 ++- gcc/go/gofrontend/expressions.cc | 7 +- gcc/go/gofrontend/types.cc | 164 ++++++++++++++++++++++++++----- gcc/go/gofrontend/types.h | 39 +++++++- 6 files changed, 202 insertions(+), 57 deletions(-) diff --git a/gcc/go/ChangeLog b/gcc/go/ChangeLog index d2e35737cbc..a7dafac8e74 100644 --- a/gcc/go/ChangeLog +++ b/gcc/go/ChangeLog @@ -1,3 +1,8 @@ +2013-11-23 Ian Lance Taylor + + * go-gcc.cc (Gcc_backend::function_type): Add result_struct + parameter. + 2013-11-22 Andrew MacLeod * go-gcc.cc: Add required include files from gimple.h. diff --git a/gcc/go/go-gcc.cc b/gcc/go/go-gcc.cc index 50e31fff91f..939be20c349 100644 --- a/gcc/go/go-gcc.cc +++ b/gcc/go/go-gcc.cc @@ -158,6 +158,7 @@ class Gcc_backend : public Backend function_type(const Btyped_identifier&, const std::vector&, const std::vector&, + Btype*, const Location); Btype* @@ -493,7 +494,8 @@ Btype* Gcc_backend::function_type(const Btyped_identifier& receiver, const std::vector& parameters, const std::vector& results, - Location location) + Btype* result_struct, + Location) { tree args = NULL_TREE; tree* pp = &args; @@ -528,29 +530,8 @@ Gcc_backend::function_type(const Btyped_identifier& receiver, result = results.front().btype->get_tree(); else { - result = make_node(RECORD_TYPE); - tree field_trees = NULL_TREE; - pp = &field_trees; - for (std::vector::const_iterator p = results.begin(); - p != results.end(); - ++p) - { - const std::string name = (p->name.empty() - ? "UNNAMED" - : p->name); - tree name_tree = get_identifier_from_string(name); - tree field_type_tree = p->btype->get_tree(); - if (field_type_tree == error_mark_node) - return this->error_type(); - gcc_assert(TYPE_SIZE(field_type_tree) != NULL_TREE); - tree field = build_decl(location.gcc_location(), FIELD_DECL, - name_tree, field_type_tree); - DECL_CONTEXT(field) = result; - *pp = field; - pp = &DECL_CHAIN(field); - } - TYPE_FIELDS(result) = field_trees; - layout_type(result); + gcc_assert(result_struct != NULL); + result = result_struct->get_tree(); } if (result == error_mark_node) return this->error_type(); diff --git a/gcc/go/gofrontend/backend.h b/gcc/go/gofrontend/backend.h index 6f2c321e09e..8344da40120 100644 --- a/gcc/go/gofrontend/backend.h +++ b/gcc/go/gofrontend/backend.h @@ -101,11 +101,15 @@ class Backend // is provided so that the names are available. This should return // not the type of a Go function (which is a pointer to a struct) // but the type of a C function pointer (which will be used as the - // type of the first field of the struct). + // type of the first field of the struct). If there is more than + // one result, RESULT_STRUCT is a struct type to hold the results, + // and RESULTS may be ignored; if there are zero or one results, + // RESULT_STRUCT is NULL. virtual Btype* function_type(const Btyped_identifier& receiver, const std::vector& parameters, const std::vector& results, + Btype* result_struct, Location location) = 0; // Get a struct type. @@ -121,10 +125,11 @@ class Backend // NAME is the name of the type, and the location is where the named // type is defined. This function is also used for unnamed function // types with multiple results, in which case the type has no name - // and NAME will be empty. FOR_FUNCTION is true if this is for a Go - // function type, which corresponds to a C/C++ pointer to function - // type. The return value will later be passed as the first - // parameter to set_placeholder_pointer_type or + // and NAME will be empty. FOR_FUNCTION is true if this is for a C + // pointer to function type. A Go func type is represented as a + // pointer to a struct, and the first field of the struct is a C + // pointer to function. The return value will later be passed as + // the first parameter to set_placeholder_pointer_type or // set_placeholder_function_type. virtual Btype* placeholder_pointer_type(const std::string& name, Location, diff --git a/gcc/go/gofrontend/expressions.cc b/gcc/go/gofrontend/expressions.cc index dd2bee429fa..4f9368ed255 100644 --- a/gcc/go/gofrontend/expressions.cc +++ b/gcc/go/gofrontend/expressions.cc @@ -9863,8 +9863,11 @@ Call_expression::do_get_tree(Translate_context* context) fndecl = TREE_OPERAND(fndecl, 0); // Add a type cast in case the type of the function is a recursive - // type which refers to itself. - if (!DECL_P(fndecl) || !DECL_IS_BUILTIN(fndecl)) + // type which refers to itself. We don't do this for an interface + // method because 1) an interface method never refers to itself, so + // we always have a function type here; 2) we pass an extra first + // argument to an interface method, so fnfield_type is not correct. + if ((!DECL_P(fndecl) || !DECL_IS_BUILTIN(fndecl)) && !is_interface_method) fn = fold_convert_loc(location.gcc_location(), fnfield_type, fn); // This is to support builtin math functions when using 80387 math. diff --git a/gcc/go/gofrontend/types.cc b/gcc/go/gofrontend/types.cc index 4f2bd0b3860..28a5b3201a2 100644 --- a/gcc/go/gofrontend/types.cc +++ b/gcc/go/gofrontend/types.cc @@ -1059,8 +1059,9 @@ Type::get_backend_placeholder(Gogo* gogo) { case TYPE_FUNCTION: { + // A Go function type is a pointer to a struct type. Location loc = this->function_type()->location(); - bt = gogo->backend()->placeholder_pointer_type("", loc, true); + bt = gogo->backend()->placeholder_pointer_type("", loc, false); } break; @@ -1153,7 +1154,7 @@ Type::finish_backend(Gogo* gogo, Btype *placeholder) case TYPE_FUNCTION: { Btype* bt = this->do_get_backend(gogo); - if (!gogo->backend()->set_placeholder_function_type(placeholder, bt)) + if (!gogo->backend()->set_placeholder_pointer_type(placeholder, bt)) go_assert(saw_errors()); } break; @@ -3378,6 +3379,48 @@ Function_type::do_hash_for_method(Gogo* gogo) const return ret; } +// Hash result parameters. + +unsigned int +Function_type::Results_hash::operator()(const Typed_identifier_list* t) const +{ + unsigned int hash = 0; + for (Typed_identifier_list::const_iterator p = t->begin(); + p != t->end(); + ++p) + { + hash <<= 2; + hash = Type::hash_string(p->name(), hash); + hash += p->type()->hash_for_method(NULL); + } + return hash; +} + +// Compare result parameters so that can map identical result +// parameters to a single struct type. + +bool +Function_type::Results_equal::operator()(const Typed_identifier_list* a, + const Typed_identifier_list* b) const +{ + if (a->size() != b->size()) + return false; + Typed_identifier_list::const_iterator pa = a->begin(); + for (Typed_identifier_list::const_iterator pb = b->begin(); + pb != b->end(); + ++pa, ++pb) + { + if (pa->name() != pb->name() + || !Type::are_identical(pa->type(), pb->type(), true, NULL)) + return false; + } + return true; +} + +// Hash from results to a backend struct type. + +Function_type::Results_structs Function_type::results_structs; + // Get the backend representation for a function type. Btype* @@ -3416,12 +3459,14 @@ Function_type::get_backend_fntype(Gogo* gogo) } std::vector bresults; + Btype* bresult_struct = NULL; if (this->results_ != NULL) { bresults.resize(this->results_->size()); size_t i = 0; for (Typed_identifier_list::const_iterator p = - this->results_->begin(); p != this->results_->end(); + this->results_->begin(); + p != this->results_->end(); ++p, ++i) { bresults[i].name = Gogo::unpack_hidden_name(p->name()); @@ -3429,10 +3474,42 @@ Function_type::get_backend_fntype(Gogo* gogo) bresults[i].location = p->location(); } go_assert(i == bresults.size()); + + if (this->results_->size() > 1) + { + // Use the same results struct for all functions that + // return the same set of results. This is useful to + // unify calls to interface methods with other calls. + std::pair val; + val.first = this->results_; + val.second = NULL; + std::pair ins = + Function_type::results_structs.insert(val); + if (ins.second) + { + // Build a new struct type. + Struct_field_list* sfl = new Struct_field_list; + for (Typed_identifier_list::const_iterator p = + this->results_->begin(); + p != this->results_->end(); + ++p) + { + Typed_identifier tid = *p; + if (tid.name().empty()) + tid = Typed_identifier("UNNAMED", tid.type(), + tid.location()); + sfl->push_back(Struct_field(tid)); + } + Struct_type* st = Type::make_struct_type(sfl, + this->location()); + ins.first->second = st->get_backend(gogo); + } + bresult_struct = ins.first->second; + } } this->fnbtype_ = gogo->backend()->function_type(breceiver, bparameters, - bresults, + bresults, bresult_struct, this->location()); } @@ -7134,18 +7211,18 @@ Interface_type::get_backend_empty_interface_type(Gogo* gogo) return empty_interface_type; } -// Return the fields of a non-empty interface type. This is not -// declared in types.h so that types.h doesn't have to #include -// backend.h. +// Return a pointer to the backend representation of the method table. -static void -get_backend_interface_fields(Gogo* gogo, Interface_type* type, - bool use_placeholder, - std::vector* bfields) +Btype* +Interface_type::get_backend_methods(Gogo* gogo) { - Location loc = type->location(); + if (this->bmethods_ != NULL && !this->bmethods_is_placeholder_) + return this->bmethods_; - std::vector mfields(type->methods()->size() + 1); + Location loc = this->location(); + + std::vector + mfields(this->all_methods_->size() + 1); Type* pdt = Type::make_type_descriptor_ptr_type(); mfields[0].name = "__type_descriptor"; @@ -7154,8 +7231,8 @@ get_backend_interface_fields(Gogo* gogo, Interface_type* type, std::string last_name = ""; size_t i = 1; - for (Typed_identifier_list::const_iterator p = type->methods()->begin(); - p != type->methods()->end(); + for (Typed_identifier_list::const_iterator p = this->all_methods_->begin(); + p != this->all_methods_->end(); ++p, ++i) { // The type of the method in Go only includes the parameters. @@ -7186,21 +7263,56 @@ get_backend_interface_fields(Gogo* gogo, Interface_type* type, ft->location()); mfields[i].name = Gogo::unpack_hidden_name(p->name()); - mfields[i].btype = (use_placeholder - ? mft->get_backend_placeholder(gogo) - : mft->get_backend(gogo)); + mfields[i].btype = mft->get_backend_fntype(gogo); mfields[i].location = loc; + // Sanity check: the names should be sorted. go_assert(p->name() > last_name); last_name = p->name(); } - Btype* methods = gogo->backend()->struct_type(mfields); + Btype* st = gogo->backend()->struct_type(mfields); + Btype* ret = gogo->backend()->pointer_type(st); + + if (this->bmethods_ != NULL && this->bmethods_is_placeholder_) + gogo->backend()->set_placeholder_pointer_type(this->bmethods_, ret); + this->bmethods_ = ret; + this->bmethods_is_placeholder_ = false; + return ret; +} + +// Return a placeholder for the pointer to the backend methods table. + +Btype* +Interface_type::get_backend_methods_placeholder(Gogo* gogo) +{ + if (this->bmethods_ == NULL) + { + Location loc = this->location(); + this->bmethods_ = gogo->backend()->placeholder_pointer_type("", loc, + false); + this->bmethods_is_placeholder_ = true; + } + return this->bmethods_; +} + +// Return the fields of a non-empty interface type. This is not +// declared in types.h so that types.h doesn't have to #include +// backend.h. + +static void +get_backend_interface_fields(Gogo* gogo, Interface_type* type, + bool use_placeholder, + std::vector* bfields) +{ + Location loc = type->location(); bfields->resize(2); (*bfields)[0].name = "__methods"; - (*bfields)[0].btype = gogo->backend()->pointer_type(methods); + (*bfields)[0].btype = (use_placeholder + ? type->get_backend_methods_placeholder(gogo) + : type->get_backend_methods(gogo)); (*bfields)[0].location = loc; Type* vt = Type::make_pointer_type(Type::make_void_type()); @@ -7241,7 +7353,7 @@ Interface_type::do_get_backend(Gogo* gogo) void Interface_type::finish_backend_methods(Gogo* gogo) { - if (!this->interface_type()->is_empty()) + if (!this->is_empty()) { const Typed_identifier_list* methods = this->methods(); if (methods != NULL) @@ -7251,6 +7363,10 @@ Interface_type::finish_backend_methods(Gogo* gogo) ++p) p->type()->get_backend(gogo); } + + // Getting the backend methods now will set the placeholder + // pointer. + this->get_backend_methods(gogo); } } @@ -8542,14 +8658,14 @@ Named_type::do_get_backend(Gogo* gogo) if (this->seen_in_get_backend_) { this->is_circular_ = true; - return gogo->backend()->circular_pointer_type(bt, true); + return gogo->backend()->circular_pointer_type(bt, false); } this->seen_in_get_backend_ = true; bt1 = Type::get_named_base_btype(gogo, base); this->seen_in_get_backend_ = false; if (this->is_circular_) - bt1 = gogo->backend()->circular_pointer_type(bt, true); - if (!gogo->backend()->set_placeholder_function_type(bt, bt1)) + bt1 = gogo->backend()->circular_pointer_type(bt, false); + if (!gogo->backend()->set_placeholder_pointer_type(bt, bt1)) bt = gogo->backend()->error_type(); return bt; diff --git a/gcc/go/gofrontend/types.h b/gcc/go/gofrontend/types.h index 12a7c8579b2..d1a739af354 100644 --- a/gcc/go/gofrontend/types.h +++ b/gcc/go/gofrontend/types.h @@ -1840,6 +1840,27 @@ class Function_type : public Type type_descriptor_params(Type*, const Typed_identifier*, const Typed_identifier_list*); + // A mapping from a list of result types to a backend struct type. + class Results_hash + { + public: + unsigned int + operator()(const Typed_identifier_list*) const; + }; + + class Results_equal + { + public: + bool + operator()(const Typed_identifier_list*, + const Typed_identifier_list*) const; + }; + + typedef Unordered_map_hash(Typed_identifier_list*, Btype*, + Results_hash, Results_equal) Results_structs; + + static Results_structs results_structs; + // The receiver name and type. This will be NULL for a normal // function, non-NULL for a method. Typed_identifier* receiver_; @@ -2552,8 +2573,9 @@ class Interface_type : public Type Interface_type(Typed_identifier_list* methods, Location location) : Type(TYPE_INTERFACE), parse_methods_(methods), all_methods_(NULL), location_(location), - interface_btype_(NULL), assume_identical_(NULL), - methods_are_finalized_(false), seen_(false) + interface_btype_(NULL), bmethods_(NULL), assume_identical_(NULL), + methods_are_finalized_(false), bmethods_is_placeholder_(false), + seen_(false) { go_assert(methods == NULL || !methods->empty()); } // The location where the interface type was defined. @@ -2620,6 +2642,15 @@ class Interface_type : public Type static Btype* get_backend_empty_interface_type(Gogo*); + // Get a pointer to the backend representation of the method table. + Btype* + get_backend_methods(Gogo*); + + // Return a placeholder for the backend representation of the + // pointer to the method table. + Btype* + get_backend_methods_placeholder(Gogo*); + // Finish the backend representation of the method types. void finish_backend_methods(Gogo*); @@ -2686,11 +2717,15 @@ class Interface_type : public Type Location location_; // The backend representation of this type during backend conversion. Btype* interface_btype_; + // The backend representation of the pointer to the method table. + Btype* bmethods_; // A list of interface types assumed to be identical during // interface comparison. mutable Assume_identical* assume_identical_; // Whether the methods have been finalized. bool methods_are_finalized_; + // Whether the bmethods_ field is a placeholder. + bool bmethods_is_placeholder_; // Used to avoid endless recursion in do_mangled_name. mutable bool seen_; };