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
This commit is contained in:
Ian Lance Taylor 2013-11-23 19:01:57 +00:00 committed by Ian Lance Taylor
parent 7c42f52b74
commit 18768faed3
6 changed files with 202 additions and 57 deletions

View file

@ -1,3 +1,8 @@
2013-11-23 Ian Lance Taylor <iant@google.com>
* go-gcc.cc (Gcc_backend::function_type): Add result_struct
parameter.
2013-11-22 Andrew MacLeod <amacleod@redhat.com>
* go-gcc.cc: Add required include files from gimple.h.

View file

@ -158,6 +158,7 @@ class Gcc_backend : public Backend
function_type(const Btyped_identifier&,
const std::vector<Btyped_identifier>&,
const std::vector<Btyped_identifier>&,
Btype*,
const Location);
Btype*
@ -493,7 +494,8 @@ Btype*
Gcc_backend::function_type(const Btyped_identifier& receiver,
const std::vector<Btyped_identifier>& parameters,
const std::vector<Btyped_identifier>& 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<Btyped_identifier>::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();

View file

@ -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<Btyped_identifier>& parameters,
const std::vector<Btyped_identifier>& 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,

View file

@ -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.

View file

@ -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<Backend::Btyped_identifier> 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<Typed_identifier_list*, Btype*> val;
val.first = this->results_;
val.second = NULL;
std::pair<Results_structs::iterator, bool> 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<Backend::Btyped_identifier>* 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<Backend::Btyped_identifier> mfields(type->methods()->size() + 1);
Location loc = this->location();
std::vector<Backend::Btyped_identifier>
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<Backend::Btyped_identifier>* 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;

View file

@ -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_;
};