From 8381eda7ff1e5a2874d708573654e64a4efcfb4f Mon Sep 17 00:00:00 2001 From: ian Date: Tue, 18 Jun 2013 23:49:49 +0000 Subject: [PATCH] compiler, runtime: Use function descriptors. This changes the representation of a Go value of function type from being a pointer to function code (like a C function pointer) to being a pointer to a struct. The first field of the struct points to the function code. The remaining fields, if any, are the addresses of variables referenced in enclosing functions. For each call to a function, the address of the function descriptor is passed as the last argument. This lets us avoid generating trampolines, and removes the use of writable/executable sections of the heap. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@200181 138bc75d-0d04-0410-961f-82ee72b054a4 --- gcc/go/ChangeLog | 6 + gcc/go/go-gcc.cc | 15 +- gcc/go/gofrontend/backend.h | 35 +-- gcc/go/gofrontend/expressions.cc | 505 ++++++++++++++++++++++++++++++++------- gcc/go/gofrontend/expressions.h | 39 ++- gcc/go/gofrontend/gogo-tree.cc | 232 ++++++++++-------- gcc/go/gofrontend/gogo.cc | 214 ++++++++++++++++- gcc/go/gofrontend/gogo.h | 61 ++++- gcc/go/gofrontend/parse.cc | 24 +- gcc/go/gofrontend/runtime.def | 5 - gcc/go/gofrontend/statements.cc | 15 +- gcc/go/gofrontend/types.cc | 80 ++++--- gcc/go/gofrontend/types.h | 18 +- libgo/Makefile.am | 1 - libgo/Makefile.in | 11 +- libgo/configure | 2 +- libgo/configure.ac | 2 +- libgo/go/reflect/all_test.go | 3 + libgo/go/reflect/type.go | 6 +- libgo/go/reflect/value.go | 39 ++- libgo/go/runtime/extern.go | 3 - libgo/go/runtime/parfor_test.go | 9 + libgo/runtime/go-reflect-call.c | 33 ++- libgo/runtime/malloc.h | 3 +- libgo/runtime/mfinal.c | 8 +- libgo/runtime/mgc0.c | 10 +- libgo/runtime/parfor.c | 2 +- libgo/runtime/runtime.h | 12 +- libgo/runtime/time.goc | 13 +- 29 files changed, 1061 insertions(+), 345 deletions(-) diff --git a/gcc/go/ChangeLog b/gcc/go/ChangeLog index aadca50dd8a..15df7047cd9 100644 --- a/gcc/go/ChangeLog +++ b/gcc/go/ChangeLog @@ -1,3 +1,9 @@ +2013-06-18 Ian Lance Taylor + + * go-gcc.cc (Gcc_backend::immutable_struct): Add is_hidden + parameter. + (Gcc_backend::immutable_struct_set_init): Likewise. + 2013-05-16 Jason Merrill * Make-lang.in (go1$(exeext)): Use link mutex. diff --git a/gcc/go/go-gcc.cc b/gcc/go/go-gcc.cc index a6acf1374ba..bd2d0dd0829 100644 --- a/gcc/go/go-gcc.cc +++ b/gcc/go/go-gcc.cc @@ -287,10 +287,10 @@ class Gcc_backend : public Backend Location, Bstatement**); Bvariable* - immutable_struct(const std::string&, bool, Btype*, Location); + immutable_struct(const std::string&, bool, bool, Btype*, Location); void - immutable_struct_set_init(Bvariable*, const std::string&, bool, Btype*, + immutable_struct_set_init(Bvariable*, const std::string&, bool, bool, Btype*, Location, Bexpression*); Bvariable* @@ -1454,8 +1454,8 @@ Gcc_backend::temporary_variable(Bfunction* function, Bblock* bblock, // Create a named immutable initialized data structure. Bvariable* -Gcc_backend::immutable_struct(const std::string& name, bool, Btype* btype, - Location location) +Gcc_backend::immutable_struct(const std::string& name, bool, bool, + Btype* btype, Location location) { tree type_tree = btype->get_tree(); if (type_tree == error_mark_node) @@ -1482,7 +1482,7 @@ Gcc_backend::immutable_struct(const std::string& name, bool, Btype* btype, void Gcc_backend::immutable_struct_set_init(Bvariable* var, const std::string&, - bool is_common, Btype*, + bool is_hidden, bool is_common, Btype*, Location, Bexpression* initializer) { @@ -1495,7 +1495,10 @@ Gcc_backend::immutable_struct_set_init(Bvariable* var, const std::string&, // We can't call make_decl_one_only until we set DECL_INITIAL. if (!is_common) - TREE_PUBLIC(decl) = 1; + { + if (!is_hidden) + TREE_PUBLIC(decl) = 1; + } else { make_decl_one_only(decl, DECL_ASSEMBLER_NAME(decl)); diff --git a/gcc/go/gofrontend/backend.h b/gcc/go/gofrontend/backend.h index fe6db743cf0..ac29b03e453 100644 --- a/gcc/go/gofrontend/backend.h +++ b/gcc/go/gofrontend/backend.h @@ -95,7 +95,10 @@ class Backend // Get a function type. The receiver, parameter, and results are // generated from the types in the Function_type. The Function_type - // is provided so that the names are available. + // 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). virtual Btype* function_type(const Btyped_identifier& receiver, const std::vector& parameters, @@ -388,18 +391,22 @@ class Backend Bstatement** pstatement) = 0; // Create a named immutable initialized data structure. This is - // used for type descriptors and map descriptors. This returns a - // Bvariable because it corresponds to an initialized const global - // variable in C. + // used for type descriptors, map descriptors, and function + // descriptors. This returns a Bvariable because it corresponds to + // an initialized const variable in C. // // NAME is the name to use for the initialized global variable which // this call will create. // + // IS_HIDDEN will be true if the descriptor should only be visible + // within the current object. + // // IS_COMMON is true if NAME may be defined by several packages, and // the linker should merge all such definitions. If IS_COMMON is // false, NAME should be defined in only one file. In general // IS_COMMON will be true for the type descriptor of an unnamed type - // or a builtin type. + // or a builtin type. IS_HIDDEN and IS_COMMON will never both be + // true. // // TYPE will be a struct type; the type of the returned expression // must be a pointer to this struct type. @@ -409,20 +416,20 @@ class Backend // address. After calling this the frontend will call // immutable_struct_set_init. virtual Bvariable* - immutable_struct(const std::string& name, bool is_common, Btype* type, - Location) = 0; + immutable_struct(const std::string& name, bool is_hidden, bool is_common, + Btype* type, Location) = 0; // Set the initial value of a variable created by immutable_struct. - // The NAME, IS_COMMON, TYPE, and location parameters are the same - // ones passed to immutable_struct. INITIALIZER will be a composite - // literal of type TYPE. It will not contain any function calls or - // anything else which can not be put into a read-only data section. - // It may contain the address of variables created by + // The NAME, IS_HIDDEN, IS_COMMON, TYPE, and location parameters are + // the same ones passed to immutable_struct. INITIALIZER will be a + // composite literal of type TYPE. It will not contain any function + // calls or anything else that can not be put into a read-only data + // section. It may contain the address of variables created by // immutable_struct. virtual void immutable_struct_set_init(Bvariable*, const std::string& name, - bool is_common, Btype* type, Location, - Bexpression* initializer) = 0; + bool is_hidden, bool is_common, Btype* type, + Location, Bexpression* initializer) = 0; // Create a reference to a named immutable initialized data // structure defined in some other package. This will be a diff --git a/gcc/go/gofrontend/expressions.cc b/gcc/go/gofrontend/expressions.cc index afc183207f7..9c1efb3e043 100644 --- a/gcc/go/gofrontend/expressions.cc +++ b/gcc/go/gofrontend/expressions.cc @@ -1242,6 +1242,24 @@ Func_expression::do_traverse(Traverse* traverse) : Expression::traverse(&this->closure_, traverse)); } +// Lower a function reference. If this reference is not called +// directly, make sure there is a function descriptor. + +Expression* +Func_expression::do_lower(Gogo* gogo, Named_object*, Statement_inserter*, int) +{ + // Make sure that the descriptor exists. FIXME: If the function is + // only ever called, and is never referenced otherwise, then we + // don't need the descriptor. We could do that with another pass + // over the tree. + if (this->closure_ == NULL + && this->function_->is_function() + && !this->function_->func_value()->is_method()) + this->function_->func_value()->descriptor(gogo, this->function_); + + return this; +} + // Return the type of a function expression. Type* @@ -1255,17 +1273,16 @@ Func_expression::do_type() go_unreachable(); } -// Get the tree for a function expression without evaluating the -// closure. +// Get the tree for the code of a function expression. tree -Func_expression::get_tree_without_closure(Gogo* gogo) +Func_expression::get_code_pointer(Gogo* gogo, Named_object* no, Location loc) { Function_type* fntype; - if (this->function_->is_function()) - fntype = this->function_->func_value()->type(); - else if (this->function_->is_function_declaration()) - fntype = this->function_->func_declaration_value()->type(); + if (no->is_function()) + fntype = no->func_value()->type(); + else if (no->is_function_declaration()) + fntype = no->func_declaration_value()->type(); else go_unreachable(); @@ -1273,14 +1290,12 @@ Func_expression::get_tree_without_closure(Gogo* gogo) // can't take their address. if (fntype->is_builtin()) { - error_at(this->location(), + error_at(loc, "invalid use of special builtin function %qs; must be called", - this->function_->name().c_str()); + no->message_name().c_str()); return error_mark_node; } - Named_object* no = this->function_; - tree id = no->get_id(gogo); if (id == error_mark_node) return error_mark_node; @@ -1296,46 +1311,55 @@ Func_expression::get_tree_without_closure(Gogo* gogo) if (fndecl == error_mark_node) return error_mark_node; - return build_fold_addr_expr_loc(this->location().gcc_location(), fndecl); + return build_fold_addr_expr_loc(loc.gcc_location(), fndecl); } // Get the tree for a function expression. This is used when we take -// the address of a function rather than simply calling it. If the -// function has a closure, we must use a trampoline. +// the address of a function rather than simply calling it. A func +// value is represented as a pointer to a block of memory. The first +// word of that memory is a pointer to the function code. The +// remaining parts of that memory are the addresses of variables that +// the function closes over. tree Func_expression::do_get_tree(Translate_context* context) { - Gogo* gogo = context->gogo(); - - tree fnaddr = this->get_tree_without_closure(gogo); - if (fnaddr == error_mark_node) - return error_mark_node; - - go_assert(TREE_CODE(fnaddr) == ADDR_EXPR - && TREE_CODE(TREE_OPERAND(fnaddr, 0)) == FUNCTION_DECL); - TREE_ADDRESSABLE(TREE_OPERAND(fnaddr, 0)) = 1; - - // If there is no closure, that is all have to do. + // If there is no closure, just use the function descriptor. if (this->closure_ == NULL) - return fnaddr; - - go_assert(this->function_->func_value()->enclosing() != NULL); + { + Gogo* gogo = context->gogo(); + Named_object* no = this->function_; + Expression* descriptor; + if (no->is_function()) + descriptor = no->func_value()->descriptor(gogo, no); + else if (no->is_function_declaration()) + { + if (no->func_declaration_value()->type()->is_builtin()) + { + error_at(this->location(), + ("invalid use of special builtin function %qs; " + "must be called"), + no->message_name().c_str()); + return error_mark_node; + } + descriptor = no->func_declaration_value()->descriptor(gogo, no); + } + else + go_unreachable(); - // Get the value of the closure. This will be a pointer to space - // allocated on the heap. - tree closure_tree = this->closure_->get_tree(context); - if (closure_tree == error_mark_node) - return error_mark_node; - go_assert(POINTER_TYPE_P(TREE_TYPE(closure_tree))); + tree dtree = descriptor->get_tree(context); + if (dtree == error_mark_node) + return error_mark_node; + return build_fold_addr_expr_loc(this->location().gcc_location(), dtree); + } - // Now we need to build some code on the heap. This code will load - // the static chain pointer with the closure and then jump to the - // body of the function. The normal gcc approach is to build the - // code on the stack. Unfortunately we can not do that, as Go - // permits us to return the function pointer. + go_assert(this->function_->func_value()->enclosing() != NULL); - return gogo->make_trampoline(fnaddr, closure_tree, this->location()); + // If there is a closure, then the closure is itself the function + // expression. It is a pointer to a struct whose first field points + // to the function code and whose remaining fields are the addresses + // of the closed-over variables. + return this->closure_->get_tree(context); } // Ast dump for function. @@ -1361,6 +1385,215 @@ Expression::make_func_reference(Named_object* function, Expression* closure, return new Func_expression(function, closure, location); } +// A function descriptor. A function descriptor is a struct with a +// single field pointing to the function code. This is used for +// functions without closures. + +class Func_descriptor_expression : public Expression +{ + public: + Func_descriptor_expression(Named_object* fn, Named_object* dfn) + : Expression(EXPRESSION_FUNC_DESCRIPTOR, fn->location()), + fn_(fn), dfn_(dfn), dvar_(NULL) + { + go_assert(!fn->is_function() || !fn->func_value()->needs_closure()); + } + + // Make the function descriptor type, so that it can be converted. + static void + make_func_descriptor_type(); + + protected: + int + do_traverse(Traverse*) + { return TRAVERSE_CONTINUE; } + + Type* + do_type(); + + void + do_determine_type(const Type_context*) + { } + + Expression* + do_copy() + { return Expression::make_func_descriptor(this->fn_, this->dfn_); } + + bool + do_is_addressable() const + { return true; } + + tree + do_get_tree(Translate_context*); + + void + do_dump_expression(Ast_dump_context* context) const + { context->ostream() << "[descriptor " << this->fn_->name() << "]"; } + + private: + // The type of all function descriptors. + static Type* descriptor_type; + + // The function for which this is the descriptor. + Named_object* fn_; + // The descriptor function. + Named_object* dfn_; + // The descriptor variable. + Bvariable* dvar_; +}; + +// All function descriptors have the same type. + +Type* Func_descriptor_expression::descriptor_type; + +void +Func_descriptor_expression::make_func_descriptor_type() +{ + if (Func_descriptor_expression::descriptor_type != NULL) + return; + Type* uintptr_type = Type::lookup_integer_type("uintptr"); + Type* struct_type = Type::make_builtin_struct_type(1, "code", uintptr_type); + Func_descriptor_expression::descriptor_type = + Type::make_builtin_named_type("functionDescriptor", struct_type); +} + +Type* +Func_descriptor_expression::do_type() +{ + Func_descriptor_expression::make_func_descriptor_type(); + return Func_descriptor_expression::descriptor_type; +} + +// The tree for a function descriptor. + +tree +Func_descriptor_expression::do_get_tree(Translate_context* context) +{ + if (this->dvar_ != NULL) + return var_to_tree(this->dvar_); + + Gogo* gogo = context->gogo(); + Named_object* no = this->fn_; + Location loc = no->location(); + + std::string var_name; + if (no->package() == NULL) + var_name = gogo->pkgpath_symbol(); + else + var_name = no->package()->pkgpath_symbol(); + var_name.push_back('.'); + var_name.append(Gogo::unpack_hidden_name(no->name())); + var_name.append("$descriptor"); + + Btype* btype = this->type()->get_backend(gogo); + + Bvariable* bvar; + if (no->package() != NULL + || Linemap::is_predeclared_location(no->location())) + { + bvar = context->backend()->immutable_struct_reference(var_name, btype, + loc); + go_assert(this->dfn_ == NULL); + } + else + { + Location bloc = Linemap::predeclared_location(); + bool is_hidden = ((no->is_function() + && no->func_value()->enclosing() != NULL) + || Gogo::is_thunk(no)); + bvar = context->backend()->immutable_struct(var_name, is_hidden, false, + btype, bloc); + Expression_list* vals = new Expression_list(); + go_assert(this->dfn_ != NULL); + vals->push_back(Expression::make_func_code_reference(this->dfn_, bloc)); + Expression* init = + Expression::make_struct_composite_literal(this->type(), vals, bloc); + Translate_context bcontext(gogo, NULL, NULL, NULL); + bcontext.set_is_const(); + Bexpression* binit = tree_to_expr(init->get_tree(&bcontext)); + context->backend()->immutable_struct_set_init(bvar, var_name, is_hidden, + false, btype, bloc, binit); + } + + this->dvar_ = bvar; + return var_to_tree(bvar); +} + +// Make a function descriptor expression. + +Expression* +Expression::make_func_descriptor(Named_object* fn, Named_object* dfn) +{ + return new Func_descriptor_expression(fn, dfn); +} + +// Make the function descriptor type, so that it can be converted. + +void +Expression::make_func_descriptor_type() +{ + Func_descriptor_expression::make_func_descriptor_type(); +} + +// A reference to just the code of a function. + +class Func_code_reference_expression : public Expression +{ + public: + Func_code_reference_expression(Named_object* function, Location location) + : Expression(EXPRESSION_FUNC_CODE_REFERENCE, location), + function_(function) + { } + + protected: + int + do_traverse(Traverse*) + { return TRAVERSE_CONTINUE; } + + Type* + do_type() + { return Type::make_pointer_type(Type::make_void_type()); } + + void + do_determine_type(const Type_context*) + { } + + Expression* + do_copy() + { + return Expression::make_func_code_reference(this->function_, + this->location()); + } + + tree + do_get_tree(Translate_context*); + + void + do_dump_expression(Ast_dump_context* context) const + { context->ostream() << "[raw " << this->function_->name() << "]" ; } + + private: + // The function. + Named_object* function_; +}; + +// Get the tree for a reference to function code. + +tree +Func_code_reference_expression::do_get_tree(Translate_context* context) +{ + return Func_expression::get_code_pointer(context->gogo(), this->function_, + this->location()); +} + +// Make a reference to the code of a function. + +Expression* +Expression::make_func_code_reference(Named_object* function, Location location) +{ + return new Func_code_reference_expression(function, location); +} + // Class Unknown_expression. // Return the name of an unknown expression. @@ -8521,6 +8754,74 @@ Builtin_call_expression::do_export(Export* exp) const // Class Call_expression. +// A Go function can be viewed in a couple of different ways. The +// code of a Go function becomes a backend function with parameters +// whose types are simply the backend representation of the Go types. +// If there are multiple results, they are returned as a backend +// struct. + +// However, when Go code refers to a function other than simply +// calling it, the backend type of that function is actually a struct. +// The first field of the struct points to the Go function code +// (sometimes a wrapper as described below). The remaining fields +// hold addresses of closed-over variables. This struct is called a +// closure. + +// There are a few cases to consider. + +// A direct function call of a known function in package scope. In +// this case there are no closed-over variables, and we know the name +// of the function code. We can simply produce a backend call to the +// function directly, and not worry about the closure. + +// A direct function call of a known function literal. In this case +// we know the function code and we know the closure. We generate the +// function code such that it expects an additional final argument of +// the closure type. We pass the closure as the last argument, after +// the other arguments. + +// An indirect function call. In this case we have a closure. We +// load the pointer to the function code from the first field of the +// closure. We pass the address of the closure as the last argument. + +// A call to a method of an interface. Type methods are always at +// package scope, so we call the function directly, and don't worry +// about the closure. + +// This means that for a function at package scope we have two cases. +// One is the direct call, which has no closure. The other is the +// indirect call, which does have a closure. We can't simply ignore +// the closure, even though it is the last argument, because that will +// fail on targets where the function pops its arguments. So when +// generating a closure for a package-scope function we set the +// function code pointer in the closure to point to a wrapper +// function. This wrapper function accepts a final argument that +// points to the closure, ignores it, and calls the real function as a +// direct function call. This wrapper will normally be efficient, and +// can often simply be a tail call to the real function. + +// We don't use GCC's static chain pointer because 1) we don't need +// it; 2) GCC only permits using a static chain to call a known +// function, so we can't use it for an indirect call anyhow. Since we +// can't use it for an indirect call, we may as well not worry about +// using it for a direct call either. + +// We pass the closure last rather than first because it means that +// the function wrapper we put into a closure for a package-scope +// function can normally just be a tail call to the real function. + +// For method expressions we generate a wrapper that loads the +// receiver from the closure and then calls the method. This +// unfortunately forces reshuffling the arguments, since there is a +// new first argument, but we can't avoid reshuffling either for +// method expressions or for indirect calls of package-scope +// functions, and since the latter are more common we reshuffle for +// method expressions. + +// Note that the Go code retains the Go types. The extra final +// argument only appears when we convert to the backend +// representation. + // Traversal. int @@ -9129,11 +9430,21 @@ Call_expression::do_get_tree(Translate_context* context) const bool has_closure = func != NULL && func->closure() != NULL; const bool is_interface_method = interface_method != NULL; + int closure_arg; + if (has_closure) + closure_arg = 1; + else if (func != NULL) + closure_arg = 0; + else if (is_interface_method) + closure_arg = 0; + else + closure_arg = 1; + int nargs; tree* args; if (this->args_ == NULL || this->args_->empty()) { - nargs = is_interface_method ? 1 : 0; + nargs = (is_interface_method ? 1 : 0) + closure_arg; args = nargs == 0 ? NULL : new tree[nargs]; } else if (fntype->parameters() == NULL || fntype->parameters()->empty()) @@ -9142,7 +9453,7 @@ Call_expression::do_get_tree(Translate_context* context) go_assert(!is_interface_method && fntype->is_method() && this->args_->size() == 1); - nargs = 1; + nargs = 1 + closure_arg; args = new tree[nargs]; args[0] = this->args_->front()->get_tree(context); } @@ -9153,6 +9464,7 @@ Call_expression::do_get_tree(Translate_context* context) nargs = this->args_->size(); int i = is_interface_method ? 1 : 0; nargs += i; + nargs += closure_arg; args = new tree[nargs]; Typed_identifier_list::const_iterator pp = params->begin(); @@ -9173,36 +9485,71 @@ Call_expression::do_get_tree(Translate_context* context) arg_val, location); if (args[i] == error_mark_node) - { - delete[] args; - return error_mark_node; - } + return error_mark_node; } go_assert(pp == params->end()); - go_assert(i == nargs); + go_assert(i + closure_arg == nargs); } - tree rettype = TREE_TYPE(TREE_TYPE(type_to_tree(fntype->get_backend(gogo)))); + tree fntype_tree = type_to_tree(fntype->get_backend(gogo)); + if (fntype_tree == error_mark_node) + return error_mark_node; + go_assert(POINTER_TYPE_P(fntype_tree)); + if (TREE_TYPE(fntype_tree) == error_mark_node) + return error_mark_node; + go_assert(TREE_CODE(TREE_TYPE(fntype_tree)) == RECORD_TYPE); + tree fnfield_type = TREE_TYPE(TYPE_FIELDS(TREE_TYPE(fntype_tree))); + if (fnfield_type == error_mark_node) + return error_mark_node; + go_assert(FUNCTION_POINTER_TYPE_P(fnfield_type)); + tree rettype = TREE_TYPE(TREE_TYPE(fnfield_type)); if (rettype == error_mark_node) - { - delete[] args; - return error_mark_node; - } + return error_mark_node; tree fn; - if (has_closure) - fn = func->get_tree_without_closure(gogo); + if (func != NULL) + { + Named_object* no = func->named_object(); + go_assert(!no->is_function() + || !no->func_value()->is_descriptor_wrapper()); + fn = Func_expression::get_code_pointer(gogo, no, location); + if (has_closure) + { + go_assert(closure_arg == 1 && nargs > 0); + args[nargs - 1] = func->closure()->get_tree(context); + } + } else if (!is_interface_method) - fn = this->fn_->get_tree(context); + { + tree closure_tree = this->fn_->get_tree(context); + if (closure_tree == error_mark_node) + return error_mark_node; + tree fnc = fold_convert_loc(location.gcc_location(), fntype_tree, + closure_tree); + go_assert(POINTER_TYPE_P(TREE_TYPE(fnc)) + && (TREE_CODE(TREE_TYPE(TREE_TYPE(fnc))) + == RECORD_TYPE)); + tree field = TYPE_FIELDS(TREE_TYPE(TREE_TYPE(fnc))); + fn = fold_build3_loc(location.gcc_location(), COMPONENT_REF, + TREE_TYPE(field), + build_fold_indirect_ref_loc(location.gcc_location(), + fnc), + field, NULL_TREE); + go_assert(closure_arg == 1 && nargs > 0); + args[nargs - 1] = closure_tree; + } else - fn = this->interface_method_function(context, interface_method, &args[0]); - - if (fn == error_mark_node || TREE_TYPE(fn) == error_mark_node) { - delete[] args; - return error_mark_node; + fn = this->interface_method_function(context, interface_method, + &args[0]); + if (fn == error_mark_node) + return error_mark_node; + go_assert(closure_arg == 0); } + if (fn == error_mark_node || TREE_TYPE(fn) == error_mark_node) + return error_mark_node; + tree fndecl = fn; if (TREE_CODE(fndecl) == ADDR_EXPR) fndecl = TREE_OPERAND(fndecl, 0); @@ -9210,12 +9557,7 @@ Call_expression::do_get_tree(Translate_context* context) // 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)) - { - tree fnt = type_to_tree(fntype->get_backend(gogo)); - if (fnt == error_mark_node) - return error_mark_node; - fn = fold_convert_loc(location.gcc_location(), fnt, fn); - } + fn = fold_convert_loc(location.gcc_location(), fnfield_type, fn); // This is to support builtin math functions when using 80387 math. tree excess_type = NULL_TREE; @@ -9259,13 +9601,6 @@ Call_expression::do_get_tree(Translate_context* context) SET_EXPR_LOCATION(ret, location.gcc_location()); - if (has_closure) - { - tree closure_tree = func->closure()->get_tree(context); - if (closure_tree != error_mark_node) - CALL_EXPR_STATIC_CHAIN(ret) = closure_tree; - } - // If this is a recursive function type which returns itself, as in // type F func() F // we have used ptr_type_node for the return type. Add a cast here @@ -9286,24 +9621,6 @@ Call_expression::do_get_tree(Translate_context* context) if (this->results_ != NULL) ret = this->set_results(context, ret); - // We can't unwind the stack past a call to nil, so we need to - // insert an explicit check so that the panic can be recovered. - if (func == NULL) - { - tree compare = fold_build2_loc(location.gcc_location(), EQ_EXPR, - boolean_type_node, fn, - fold_convert_loc(location.gcc_location(), - TREE_TYPE(fn), - null_pointer_node)); - tree crash = build3_loc(location.gcc_location(), COND_EXPR, - void_type_node, compare, - gogo->runtime_error(RUNTIME_ERROR_NIL_DEREFERENCE, - location), - NULL_TREE); - ret = fold_build2_loc(location.gcc_location(), COMPOUND_EXPR, - TREE_TYPE(ret), crash, ret); - } - this->tree_ = ret; return ret; @@ -11126,8 +11443,10 @@ Selector_expression::lower_method_expression(Gogo* gogo) // as their first argument. If this is for a pointer type, we can // simply reuse the existing function. We use an internal hack to // get the right type. - - if (method != NULL && is_pointer) + // FIXME: This optimization is disabled because it doesn't yet work + // with function descriptors when the method expression is not + // directly called. + if (method != NULL && is_pointer && false) { Named_object* mno = (method->needs_stub_method() ? method->stub_object() diff --git a/gcc/go/gofrontend/expressions.h b/gcc/go/gofrontend/expressions.h index 36f4c0d79f2..ed3909c1bc1 100644 --- a/gcc/go/gofrontend/expressions.h +++ b/gcc/go/gofrontend/expressions.h @@ -67,6 +67,8 @@ class Expression EXPRESSION_SET_AND_USE_TEMPORARY, EXPRESSION_SINK, EXPRESSION_FUNC_REFERENCE, + EXPRESSION_FUNC_DESCRIPTOR, + EXPRESSION_FUNC_CODE_REFERENCE, EXPRESSION_UNKNOWN_REFERENCE, EXPRESSION_BOOLEAN, EXPRESSION_STRING, @@ -150,10 +152,25 @@ class Expression static Expression* make_sink(Location); - // Make a reference to a function in an expression. + // Make a reference to a function in an expression. This returns a + // pointer to the struct holding the address of the function + // followed by any closed-over variables. static Expression* make_func_reference(Named_object*, Expression* closure, Location); + // Make a function descriptor, an immutable struct with a single + // field that points to the function code. This may only be used + // with functions that do not have closures. FN is the function for + // which we are making the descriptor. DFN is the descriptor + // function wrapper. + static Expression* + make_func_descriptor(Named_object* fn, Named_object* dfn); + + // Make a reference to the code of a function. This is used to set + // descriptor and closure fields. + static Expression* + make_func_code_reference(Named_object*, Location); + // Make a reference to an unknown name. In a correct program this // will always be lowered to a real const/var/func reference. static Unknown_expression* @@ -523,6 +540,11 @@ class Expression bool is_local_variable() const; + // Make the builtin function descriptor type, so that it can be + // converted. + static void + make_func_descriptor_type(); + // Traverse an expression. static int traverse(Expression**, Traverse*); @@ -1484,7 +1506,7 @@ class Func_expression : public Expression { } // Return the object associated with the function. - const Named_object* + Named_object* named_object() const { return this->function_; } @@ -1494,14 +1516,17 @@ class Func_expression : public Expression closure() { return this->closure_; } - // Return a tree for this function without evaluating the closure. - tree - get_tree_without_closure(Gogo*); + // Return a tree for the code for a function. + static tree + get_code_pointer(Gogo*, Named_object* function, Location loc); protected: int do_traverse(Traverse*); + Expression* + do_lower(Gogo*, Named_object*, Statement_inserter*, int); + Type* do_type(); @@ -1532,8 +1557,8 @@ class Func_expression : public Expression // The function itself. Named_object* function_; // A closure. This is normally NULL. For a nested function, it may - // be a heap-allocated struct holding pointers to all the variables - // referenced by this function and defined in enclosing functions. + // be a struct holding pointers to all the variables referenced by + // this function and defined in enclosing functions. Expression* closure_; }; diff --git a/gcc/go/gofrontend/gogo-tree.cc b/gcc/go/gofrontend/gogo-tree.cc index b9840451955..cd54f2bdeed 100644 --- a/gcc/go/gofrontend/gogo-tree.cc +++ b/gcc/go/gofrontend/gogo-tree.cc @@ -755,6 +755,18 @@ Gogo::write_globals() this->build_interface_method_tables(); Bindings* bindings = this->current_bindings(); + + for (Bindings::const_declarations_iterator p = bindings->begin_declarations(); + p != bindings->end_declarations(); + ++p) + { + // If any function declarations needed a descriptor, make sure + // we build it. + Named_object* no = p->second; + if (no->is_function_declaration()) + no->func_declaration_value()->build_backend_descriptor(this); + } + size_t count_definitions = bindings->size_definitions(); size_t count = count_definitions; @@ -782,6 +794,8 @@ Gogo::write_globals() { Named_object* no = *p; + go_assert(i < count); + go_assert(!no->is_type_declaration() && !no->is_function_declaration()); // There is nothing to do for a package. if (no->is_package()) @@ -1255,14 +1269,47 @@ Function::get_or_make_decl(Gogo* gogo, Named_object* no, tree id) if (this->fndecl_ == NULL_TREE) { tree functype = type_to_tree(this->type_->get_backend(gogo)); + + if (functype != error_mark_node) + { + // The type of a function comes back as a pointer to a + // struct whose first field is the function, but we want the + // real function type for a function declaration. + go_assert(POINTER_TYPE_P(functype) + && TREE_CODE(TREE_TYPE(functype)) == RECORD_TYPE); + functype = TREE_TYPE(TYPE_FIELDS(TREE_TYPE(functype))); + go_assert(FUNCTION_POINTER_TYPE_P(functype)); + functype = TREE_TYPE(functype); + + // In the struct, the function type always has a trailing + // closure argument. For the function body, we only use + // that trailing arg if this is a function literal or if it + // is a wrapper created to store in a descriptor. Remove it + // in that case. + if (this->enclosing_ == NULL && !this->is_descriptor_wrapper_) + { + tree old_params = TYPE_ARG_TYPES(functype); + go_assert(old_params != NULL_TREE + && old_params != void_list_node); + tree new_params = NULL_TREE; + tree *pp = &new_params; + while (TREE_CHAIN (old_params) != void_list_node) + { + tree p = TREE_VALUE(old_params); + go_assert(TYPE_P(p)); + *pp = tree_cons(NULL_TREE, p, NULL_TREE); + pp = &TREE_CHAIN(*pp); + old_params = TREE_CHAIN (old_params); + } + *pp = void_list_node; + functype = build_function_type(TREE_TYPE(functype), new_params); + } + } + if (functype == error_mark_node) this->fndecl_ = error_mark_node; else { - // The type of a function comes back as a pointer, but we - // want the real function type for a function declaration. - go_assert(POINTER_TYPE_P(functype)); - functype = TREE_TYPE(functype); tree decl = build_decl(this->location().gcc_location(), FUNCTION_DECL, id, functype); @@ -1308,9 +1355,6 @@ Function::get_or_make_decl(Gogo* gogo, Named_object* no, tree id) DECL_CONTEXT(resdecl) = decl; DECL_RESULT(decl) = resdecl; - if (this->enclosing_ != NULL) - DECL_STATIC_CHAIN(decl) = 1; - // If a function calls the predeclared recover function, we // can't inline it, because recover behaves differently in a // function passed directly to defer. If this is a recover @@ -1333,29 +1377,6 @@ Function::get_or_make_decl(Gogo* gogo, Named_object* no, tree id) resolve_unique_section (decl, 0, 1); go_preserve_from_gc(decl); - - if (this->closure_var_ != NULL) - { - push_struct_function(decl); - - Bvariable* bvar = this->closure_var_->get_backend_variable(gogo, - no); - tree closure_decl = var_to_tree(bvar); - if (closure_decl == error_mark_node) - this->fndecl_ = error_mark_node; - else - { - DECL_ARTIFICIAL(closure_decl) = 1; - DECL_IGNORED_P(closure_decl) = 1; - TREE_USED(closure_decl) = 1; - DECL_ARG_TYPE(closure_decl) = TREE_TYPE(closure_decl); - TREE_READONLY(closure_decl) = 1; - - DECL_STRUCT_FUNCTION(decl)->static_chain_decl = closure_decl; - } - - pop_cfun(); - } } } return this->fndecl_; @@ -1382,15 +1403,44 @@ Function_declaration::get_or_make_decl(Gogo* gogo, Named_object* no, tree id) } tree functype = type_to_tree(this->fntype_->get_backend(gogo)); + + if (functype != error_mark_node) + { + // The type of a function comes back as a pointer to a + // struct whose first field is the function, but we want the + // real function type for a function declaration. + go_assert(POINTER_TYPE_P(functype) + && TREE_CODE(TREE_TYPE(functype)) == RECORD_TYPE); + functype = TREE_TYPE(TYPE_FIELDS(TREE_TYPE(functype))); + go_assert(FUNCTION_POINTER_TYPE_P(functype)); + functype = TREE_TYPE(functype); + + // In the struct, the function type always has a trailing + // closure argument. Here we are referring to the function + // code directly, and we know it is not a function literal, + // and we know it is not a wrapper created to store in a + // descriptor. Remove that trailing argument. + tree old_params = TYPE_ARG_TYPES(functype); + go_assert(old_params != NULL_TREE && old_params != void_list_node); + tree new_params = NULL_TREE; + tree *pp = &new_params; + while (TREE_CHAIN (old_params) != void_list_node) + { + tree p = TREE_VALUE(old_params); + go_assert(TYPE_P(p)); + *pp = tree_cons(NULL_TREE, p, NULL_TREE); + pp = &TREE_CHAIN(*pp); + old_params = TREE_CHAIN (old_params); + } + *pp = void_list_node; + functype = build_function_type(TREE_TYPE(functype), new_params); + } + tree decl; if (functype == error_mark_node) decl = error_mark_node; else { - // The type of a function comes back as a pointer, but we - // want the real function type for a function declaration. - go_assert(POINTER_TYPE_P(functype)); - functype = TREE_TYPE(functype); decl = build_decl(this->location().gcc_location(), FUNCTION_DECL, id, functype); TREE_PUBLIC(decl) = 1; @@ -1599,6 +1649,32 @@ Function::build_tree(Gogo* gogo, Named_object* named_function) } } } + + // The closure variable is passed last, if this is a function + // literal or a descriptor wrapper. + if (this->closure_var_ != NULL) + { + Bvariable* bvar = + this->closure_var_->get_backend_variable(gogo, named_function); + tree var_decl = var_to_tree(bvar); + if (var_decl != error_mark_node) + { + go_assert(TREE_CODE(var_decl) == PARM_DECL); + *pp = var_decl; + pp = &DECL_CHAIN(*pp); + } + } + else if (this->enclosing_ != NULL || this->is_descriptor_wrapper_) + { + tree parm_decl = build_decl(this->location_.gcc_location(), PARM_DECL, + get_identifier("$closure"), + const_ptr_type_node); + DECL_CONTEXT(parm_decl) = current_function_decl; + DECL_ARG_TYPE(parm_decl) = const_ptr_type_node; + *pp = parm_decl; + pp = &DECL_CHAIN(*pp); + } + *pp = NULL_TREE; DECL_ARGUMENTS(fndecl) = params; @@ -1681,6 +1757,13 @@ Function::build_tree(Gogo* gogo, Named_object* named_function) DECL_SAVED_TREE(fndecl) = code; } + + // If we created a descriptor for the function, make sure we emit it. + if (this->descriptor_ != NULL) + { + Translate_context context(gogo, NULL, NULL, NULL); + this->descriptor_->get_tree(&context); + } } // Build the wrappers around function code needed if the function has @@ -1844,6 +1927,20 @@ Function::return_value(Gogo* gogo, Named_object* named_function, } } +// Build the descriptor for a function declaration. This won't +// necessarily happen if the package has just a declaration for the +// function and no other reference to it, but we may still need the +// descriptor for references from other packages. +void +Function_declaration::build_backend_descriptor(Gogo* gogo) +{ + if (this->descriptor_ != NULL) + { + Translate_context context(gogo, NULL, NULL, NULL); + this->descriptor_->get_tree(&context); + } +} + // Return the integer type to use for a size. GO_EXTERN_C @@ -2437,70 +2534,3 @@ Gogo::receive_from_channel(tree type_tree, tree type_descriptor_tree, build2(COMPOUND_EXPR, type_tree, call, tmp)); } } - -// Return the type of a function trampoline. This is like -// get_trampoline_type in tree-nested.c. - -tree -Gogo::trampoline_type_tree() -{ - static tree type_tree; - if (type_tree == NULL_TREE) - { - unsigned int size; - unsigned int align; - go_trampoline_info(&size, &align); - tree t = build_index_type(build_int_cst(integer_type_node, size - 1)); - t = build_array_type(char_type_node, t); - - type_tree = Gogo::builtin_struct(NULL, "__go_trampoline", NULL_TREE, 1, - "__data", t); - t = TYPE_FIELDS(type_tree); - DECL_ALIGN(t) = align; - DECL_USER_ALIGN(t) = 1; - - go_preserve_from_gc(type_tree); - } - return type_tree; -} - -// Make a trampoline which calls FNADDR passing CLOSURE. - -tree -Gogo::make_trampoline(tree fnaddr, tree closure, Location location) -{ - tree trampoline_type = Gogo::trampoline_type_tree(); - tree trampoline_size = TYPE_SIZE_UNIT(trampoline_type); - - closure = save_expr(closure); - - // We allocate the trampoline using a special function which will - // mark it as executable. - static tree trampoline_fndecl; - tree x = Gogo::call_builtin(&trampoline_fndecl, - location, - "__go_allocate_trampoline", - 2, - ptr_type_node, - size_type_node, - trampoline_size, - ptr_type_node, - fold_convert_loc(location.gcc_location(), - ptr_type_node, closure)); - if (x == error_mark_node) - return error_mark_node; - - x = save_expr(x); - - // Initialize the trampoline. - tree calldecl = builtin_decl_implicit(BUILT_IN_INIT_HEAP_TRAMPOLINE); - tree ini = build_call_expr(calldecl, 3, x, fnaddr, closure); - - // On some targets the trampoline address needs to be adjusted. For - // example, when compiling in Thumb mode on the ARM, the address - // needs to have the low bit set. - x = build_call_expr(builtin_decl_explicit(BUILT_IN_ADJUST_TRAMPOLINE), 1, x); - x = fold_convert(TREE_TYPE(fnaddr), x); - - return build2(COMPOUND_EXPR, TREE_TYPE(x), ini, x); -} diff --git a/gcc/go/gofrontend/gogo.cc b/gcc/go/gofrontend/gogo.cc index 80ae8d46494..e4c21be2dda 100644 --- a/gcc/go/gofrontend/gogo.cc +++ b/gcc/go/gofrontend/gogo.cc @@ -364,7 +364,7 @@ Gogo::set_package_name(const std::string& package_name, // Declare "main" as a function which takes no parameters and // returns no value. Location uloc = Linemap::unknown_location(); - this->declare_function("main", + this->declare_function(Gogo::pack_hidden_name("main", false), Type::make_function_type (NULL, NULL, NULL, uloc), uloc); } @@ -1599,14 +1599,23 @@ Lower_parse_tree::constant(Named_object* no, bool) return TRAVERSE_CONTINUE; } -// Lower function closure types. Record the function while lowering -// it, so that we can pass it down when lowering an expression. +// Lower the body of a function, and set the closure type. Record the +// function while lowering it, so that we can pass it down when +// lowering an expression. int Lower_parse_tree::function(Named_object* no) { no->func_value()->set_closure_type(); + // Make sure that every externally visible function has a + // descriptor, so that packages that import this one can refer to + // it. + if (!Gogo::is_hidden_name(no->name()) + && !no->func_value()->is_method() + && !no->func_value()->is_descriptor_wrapper()) + no->func_value()->descriptor(this->gogo_, no); + go_assert(this->function_ == NULL); this->function_ = no; int t = no->func_value()->traverse(this); @@ -1694,6 +1703,28 @@ Lower_parse_tree::expression(Expression** pexpr) void Gogo::lower_parse_tree() { + // Create a function descriptor for any function that is declared in + // this package. This is so that we have a descriptor for functions + // written in assembly. Gather the descriptors first so that we + // don't add declarations while looping over them. + std::vector fndecls; + Bindings* b = this->package_->bindings(); + for (Bindings::const_declarations_iterator p = b->begin_declarations(); + p != b->end_declarations(); + ++p) + { + Named_object* no = p->second; + if (no->is_function_declaration() + && !no->func_declaration_value()->type()->is_method() + && !Linemap::is_predeclared_location(no->location())) + fndecls.push_back(no); + } + for (std::vector::const_iterator p = fndecls.begin(); + p != fndecls.end(); + ++p) + (*p)->func_declaration_value()->descriptor(this, *p); + fndecls.clear(); + Lower_parse_tree lower_parse_tree(this, NULL); this->traverse(&lower_parse_tree); } @@ -2643,6 +2674,13 @@ Build_recover_thunks::function(Named_object* orig_no) Expression* closure = NULL; if (orig_func->needs_closure()) { + // For the new function we are creating, declare a new parameter + // variable NEW_CLOSURE_NO and set it to be the closure variable + // of the function. This will be set to the closure value + // passed in by the caller. Then pass a reference to this + // variable as the closure value when calling the original + // function. In other words, simply pass the closure value + // through the thunk we are creating. Named_object* orig_closure_no = orig_func->closure_var(); Variable* orig_closure_var = orig_closure_no->var_value(); Variable* new_var = new Variable(orig_closure_var->type(), NULL, false, @@ -3101,6 +3139,7 @@ Gogo::convert_named_types() Map_type::make_map_descriptor_type(); Channel_type::make_chan_type_descriptor_type(); Interface_type::make_interface_type_descriptor_type(); + Expression::make_func_descriptor_type(); Type::convert_builtin_named_types(this); Runtime::convert_types(this); @@ -3128,10 +3167,10 @@ Function::Function(Function_type* type, Function* enclosing, Block* block, Location location) : type_(type), enclosing_(enclosing), results_(NULL), closure_var_(NULL), block_(block), location_(location), labels_(), - local_type_count_(0), fndecl_(NULL), defer_stack_(NULL), + local_type_count_(0), descriptor_(NULL), fndecl_(NULL), defer_stack_(NULL), results_are_named_(false), nointerface_(false), calls_recover_(false), is_recover_thunk_(false), has_recover_thunk_(false), - in_unique_section_(false) + in_unique_section_(false), is_descriptor_wrapper_(false) { } @@ -3206,6 +3245,7 @@ Function::closure_var() { if (this->closure_var_ == NULL) { + go_assert(this->descriptor_ == NULL); // We don't know the type of the variable yet. We add fields as // we find them. Location loc = this->type_->location(); @@ -3229,6 +3269,13 @@ Function::set_closure_type() return; Named_object* closure = this->closure_var_; Struct_type* st = closure->var_value()->type()->deref()->struct_type(); + + // The first field of a closure is always a pointer to the function + // code. + Type* voidptr_type = Type::make_pointer_type(Type::make_void_type()); + st->push_field(Struct_field(Typed_identifier(".$f", voidptr_type, + this->location_))); + unsigned int index = 0; for (Closure_fields::const_iterator p = this->closure_fields_.begin(); p != this->closure_fields_.end(); @@ -3410,6 +3457,136 @@ Function::determine_types() this->block_->determine_types(); } +// Build a wrapper function for a function descriptor. A function +// descriptor refers to a function that takes a closure as its last +// argument. In this case there will be no closure, but an indirect +// call will pass nil as the last argument. We need to build a +// wrapper function that accepts and discards that last argument, so +// that cases like -mrtd will work correctly. In most cases the +// wrapper function will simply be a jump. + +Named_object* +Function::make_descriptor_wrapper(Gogo* gogo, Named_object* no, + Function_type* orig_fntype) +{ + Location loc = no->location(); + + Typed_identifier_list* new_params = new Typed_identifier_list(); + const Typed_identifier_list* orig_params = orig_fntype->parameters(); + if (orig_params != NULL && !orig_params->empty()) + { + static int count; + char buf[50]; + for (Typed_identifier_list::const_iterator p = orig_params->begin(); + p != orig_params->end(); + ++p) + { + snprintf(buf, sizeof buf, "pt.%u", count); + ++count; + new_params->push_back(Typed_identifier(buf, p->type(), + p->location())); + } + } + Type* vt = Type::make_pointer_type(Type::make_void_type()); + new_params->push_back(Typed_identifier("closure.0", vt, loc)); + + const Typed_identifier_list* orig_results = orig_fntype->results(); + Typed_identifier_list* new_results; + if (orig_results == NULL || orig_results->empty()) + new_results = NULL; + else + { + new_results = new Typed_identifier_list(); + for (Typed_identifier_list::const_iterator p = orig_results->begin(); + p != orig_results->end(); + ++p) + new_results->push_back(Typed_identifier("", p->type(), + p->location())); + } + + Function_type* new_fntype = Type::make_function_type(NULL, new_params, + new_results, + loc); + + std::string name = no->name() + "$descriptorfn"; + Named_object* dno = gogo->start_function(name, new_fntype, false, loc); + dno->func_value()->is_descriptor_wrapper_ = true; + + gogo->start_block(loc); + + Expression* fn = Expression::make_func_reference(no, NULL, loc); + + // Call the wrapper function, passing all of the arguments except + // for the last one (the last argument is the ignored closure). + Expression_list* args; + if (orig_params == NULL || orig_params->empty()) + args = NULL; + else + { + args = new Expression_list(); + for (Typed_identifier_list::const_iterator p = new_params->begin(); + p + 1 != new_params->end(); + ++p) + { + Named_object* p_no = gogo->lookup(p->name(), NULL); + go_assert(p_no != NULL + && p_no->is_variable() + && p_no->var_value()->is_parameter()); + args->push_back(Expression::make_var_reference(p_no, loc)); + } + } + + Call_expression* call = Expression::make_call(fn, args, + orig_fntype->is_varargs(), + loc); + call->set_varargs_are_lowered(); + + Statement* s; + if (orig_results == NULL || orig_results->empty()) + s = Statement::make_statement(call, true); + else + { + Expression_list* vals = new Expression_list(); + size_t rc = orig_results->size(); + if (rc == 1) + vals->push_back(call); + else + { + for (size_t i = 0; i < rc; ++i) + vals->push_back(Expression::make_call_result(call, i)); + } + s = Statement::make_return_statement(vals, loc); + } + + gogo->add_statement(s); + gogo->add_block(gogo->finish_block(loc), loc); + gogo->finish_function(loc); + + return dno; +} + +// Return the function descriptor, the value you get when you refer to +// the function in Go code without calling it. + +Expression* +Function::descriptor(Gogo* gogo, Named_object* no) +{ + go_assert(!this->is_method()); + go_assert(this->closure_var_ == NULL); + go_assert(!this->is_descriptor_wrapper_); + if (this->descriptor_ == NULL) + { + Named_object* dno; + if (no->package() != NULL + || Linemap::is_predeclared_location(no->location())) + dno = NULL; + else + dno = Function::make_descriptor_wrapper(gogo, no, this->type_); + this->descriptor_ = Expression::make_func_descriptor(no, dno); + } + return this->descriptor_; +} + // Get a pointer to the variable representing the defer stack for this // function, making it if necessary. The value of the variable is set // by the runtime routines to true if the function is returning, @@ -3940,6 +4117,27 @@ Bindings_snapshot::check_goto_defs(Location loc, const Block* block, } } +// Class Function_declaration. + +// Return the function descriptor. + +Expression* +Function_declaration::descriptor(Gogo* gogo, Named_object* no) +{ + go_assert(!this->fntype_->is_method()); + if (this->descriptor_ == NULL) + { + Named_object* dno; + if (no->package() != NULL + || Linemap::is_predeclared_location(no->location())) + dno = NULL; + else + dno = Function::make_descriptor_wrapper(gogo, no, this->fntype_); + this->descriptor_ = Expression::make_func_descriptor(no, dno); + } + return this->descriptor_; +} + // Class Variable. Variable::Variable(Type* type, Expression* init, bool is_global, @@ -4755,6 +4953,12 @@ void Named_object::set_function_value(Function* function) { go_assert(this->classification_ == NAMED_OBJECT_FUNC_DECLARATION); + if (this->func_declaration_value()->has_descriptor()) + { + Expression* descriptor = + this->func_declaration_value()->descriptor(NULL, NULL); + function->set_descriptor(descriptor); + } this->classification_ = NAMED_OBJECT_FUNC; // FIXME: We should free the old value. this->u_.func_value = function; diff --git a/gcc/go/gofrontend/gogo.h b/gcc/go/gofrontend/gogo.h index f96ffcdfdb9..ec43ef67f08 100644 --- a/gcc/go/gofrontend/gogo.h +++ b/gcc/go/gofrontend/gogo.h @@ -614,10 +614,6 @@ class Gogo receive_from_channel(tree type_tree, tree type_descriptor_tree, tree channel, Location); - // Make a trampoline which calls FNADDR passing CLOSURE. - tree - make_trampoline(tree fnaddr, tree closure, Location); - private: // During parsing, we keep a stack of functions. Each function on // the stack is one that we are currently parsing. For each @@ -669,10 +665,6 @@ class Gogo tree ptr_go_string_constant_tree(const std::string&); - // Return the type of a trampoline. - static tree - trampoline_type_tree(); - // Type used to map import names to packages. typedef std::map Imports; @@ -1046,6 +1038,12 @@ class Function set_in_unique_section() { this->in_unique_section_ = true; } + // Whether this function was created as a descriptor wrapper for + // another function. + bool + is_descriptor_wrapper() const + { return this->is_descriptor_wrapper_; } + // Swap with another function. Used only for the thunk which calls // recover. void @@ -1059,6 +1057,26 @@ class Function void determine_types(); + // Return an expression for the function descriptor, given the named + // object for this function. This may only be called for functions + // without a closure. This will be an immutable struct with one + // field that points to the function's code. + Expression* + descriptor(Gogo*, Named_object*); + + // Set the descriptor for this function. This is used when a + // function declaration is followed by a function definition. + void + set_descriptor(Expression* descriptor) + { + go_assert(this->descriptor_ == NULL); + this->descriptor_ = descriptor; + } + + // Build a descriptor wrapper function. + static Named_object* + make_descriptor_wrapper(Gogo*, Named_object*, Function_type*); + // Return the function's decl given an identifier. tree get_or_make_decl(Gogo*, Named_object*, tree id); @@ -1137,6 +1155,8 @@ class Function Labels labels_; // The number of local types defined in this function. unsigned int local_type_count_; + // The function descriptor, if any. + Expression* descriptor_; // The function decl. tree fndecl_; // The defer stack variable. A pointer to this variable is used to @@ -1156,6 +1176,9 @@ class Function // True if this function should be put in a unique section. This is // turned on for field tracking. bool in_unique_section_ : 1; + // True if this is a function wrapper created to put in a function + // descriptor. + bool is_descriptor_wrapper_ : 1; }; // A snapshot of the current binding state. @@ -1198,7 +1221,8 @@ class Function_declaration { public: Function_declaration(Function_type* fntype, Location location) - : fntype_(fntype), location_(location), asm_name_(), fndecl_(NULL) + : fntype_(fntype), location_(location), asm_name_(), descriptor_(NULL), + fndecl_(NULL) { } Function_type* @@ -1218,10 +1242,27 @@ class Function_declaration set_asm_name(const std::string& asm_name) { this->asm_name_ = asm_name; } + // Return an expression for the function descriptor, given the named + // object for this function. This may only be called for functions + // without a closure. This will be an immutable struct with one + // field that points to the function's code. + Expression* + descriptor(Gogo*, Named_object*); + + // Return true if we have created a descriptor for this declaration. + bool + has_descriptor() const + { return this->descriptor_ != NULL; } + // Return a decl for the function given an identifier. tree get_or_make_decl(Gogo*, Named_object*, tree id); + // If there is a descriptor, build it into the backend + // representation. + void + build_backend_descriptor(Gogo*); + // Export a function declaration. void export_func(Export* exp, const std::string& name) const @@ -1235,6 +1276,8 @@ class Function_declaration // The assembler name: this is the name to use in references to the // function. This is normally empty. std::string asm_name_; + // The function descriptor, if any. + Expression* descriptor_; // The function decl if needed. tree fndecl_; }; diff --git a/gcc/go/gofrontend/parse.cc b/gcc/go/gofrontend/parse.cc index d7a18d023be..b562a78ad00 100644 --- a/gcc/go/gofrontend/parse.cc +++ b/gcc/go/gofrontend/parse.cc @@ -2627,7 +2627,11 @@ Parse::enclosing_var_reference(Named_object* in_function, Named_object* var, Named_object* this_function = this->gogo_->current_function(); Named_object* closure = this_function->func_value()->closure_var(); - Enclosing_var ev(var, in_function, this->enclosing_vars_.size()); + // The last argument to the Enclosing_var constructor is the index + // of this variable in the closure. We add 1 to the current number + // of enclosed variables, because the first field in the closure + // points to the function code. + Enclosing_var ev(var, in_function, this->enclosing_vars_.size() + 1); std::pair ins = this->enclosing_vars_.insert(ev); if (ins.second) @@ -2882,8 +2886,9 @@ Parse::function_lit() // Create a closure for the nested function FUNCTION. This is based // on ENCLOSING_VARS, which is a list of all variables defined in // enclosing functions and referenced from FUNCTION. A closure is the -// address of a struct which contains the addresses of all the -// referenced variables. This returns NULL if no closure is required. +// address of a struct which point to the real function code and +// contains the addresses of all the referenced variables. This +// returns NULL if no closure is required. Expression* Parse::create_closure(Named_object* function, Enclosing_vars* enclosing_vars, @@ -2899,16 +2904,25 @@ Parse::create_closure(Named_object* function, Enclosing_vars* enclosing_vars, for (Enclosing_vars::const_iterator p = enclosing_vars->begin(); p != enclosing_vars->end(); ++p) - ev[p->index()] = *p; + { + // Subtract 1 because index 0 is the function code. + ev[p->index() - 1] = *p; + } // Build an initializer for a composite literal of the closure's // type. Named_object* enclosing_function = this->gogo_->current_function(); Expression_list* initializer = new Expression_list; + + initializer->push_back(Expression::make_func_code_reference(function, + location)); + for (size_t i = 0; i < enclosing_var_count; ++i) { - go_assert(ev[i].index() == i); + // Add 1 to i because the first field in the closure is a + // pointer to the function code. + go_assert(ev[i].index() == i + 1); Named_object* var = ev[i].var(); Expression* ref; if (ev[i].in_function() == enclosing_function) diff --git a/gcc/go/gofrontend/runtime.def b/gcc/go/gofrontend/runtime.def index 0d9ff0380ac..0d3fd3c43f1 100644 --- a/gcc/go/gofrontend/runtime.def +++ b/gcc/go/gofrontend/runtime.def @@ -224,11 +224,6 @@ DEF_GO_RUNTIME(NEW, "__go_new", P1(UINTPTR), R1(POINTER)) DEF_GO_RUNTIME(NEW_NOPOINTERS, "__go_new_nopointers", P1(UINTPTR), R1(POINTER)) -// Allocate a trampoline for a function literal. -DEF_GO_RUNTIME(ALLOCATE_GO_TRAMPOLINE, "__go_allocate_trampoline", - P2(UINTPTR, POINTER), R1(POINTER)) - - // Start a new goroutine. DEF_GO_RUNTIME(GO, "__go_go", P2(FUNC_PTR, POINTER), R0()) diff --git a/gcc/go/gofrontend/statements.cc b/gcc/go/gofrontend/statements.cc index c9c62cedaaf..7e4c56181f1 100644 --- a/gcc/go/gofrontend/statements.cc +++ b/gcc/go/gofrontend/statements.cc @@ -1959,10 +1959,15 @@ Thunk_statement::is_simple(Function_type* fntype) const && results->begin()->type()->points_to() == NULL))) return false; - // If this calls something which is not a simple function, then we + // If this calls something that is not a simple function, then we // need a thunk. Expression* fn = this->call_->call_expression()->fn(); - if (fn->interface_field_reference_expression() != NULL) + if (fn->func_expression() == NULL) + return false; + + // If the function uses a closure, then we need a thunk. FIXME: We + // could accept a zero argument function with a closure. + if (fn->func_expression()->closure() != NULL) return false; return true; @@ -2502,7 +2507,11 @@ Thunk_statement::get_fn_and_arg(Expression** pfn, Expression** parg) Call_expression* ce = this->call_->call_expression(); - *pfn = ce->fn(); + Expression* fn = ce->fn(); + Func_expression* fe = fn->func_expression(); + go_assert(fe != NULL); + *pfn = Expression::make_func_code_reference(fe->named_object(), + fe->location()); const Expression_list* args = ce->args(); if (args == NULL || args->empty()) diff --git a/gcc/go/gofrontend/types.cc b/gcc/go/gofrontend/types.cc index 4fbb162edd1..ae25ec9434a 100644 --- a/gcc/go/gofrontend/types.cc +++ b/gcc/go/gofrontend/types.cc @@ -1298,8 +1298,8 @@ Type::make_type_descriptor_var(Gogo* gogo) // converting INITIALIZER. this->type_descriptor_var_ = - gogo->backend()->immutable_struct(var_name, is_common, initializer_btype, - loc); + gogo->backend()->immutable_struct(var_name, false, is_common, + initializer_btype, loc); if (phash != NULL) *phash = this->type_descriptor_var_; @@ -1308,7 +1308,7 @@ Type::make_type_descriptor_var(Gogo* gogo) Bexpression* binitializer = tree_to_expr(initializer->get_tree(&context)); gogo->backend()->immutable_struct_set_init(this->type_descriptor_var_, - var_name, is_common, + var_name, false, is_common, initializer_btype, loc, binitializer); } @@ -1528,26 +1528,6 @@ Type::make_type_descriptor_type() // The type descriptor type. - Typed_identifier_list* params = new Typed_identifier_list(); - params->push_back(Typed_identifier("key", unsafe_pointer_type, bloc)); - params->push_back(Typed_identifier("key_size", uintptr_type, bloc)); - - Typed_identifier_list* results = new Typed_identifier_list(); - results->push_back(Typed_identifier("", uintptr_type, bloc)); - - Type* hashfn_type = Type::make_function_type(NULL, params, results, bloc); - - params = new Typed_identifier_list(); - params->push_back(Typed_identifier("key1", unsafe_pointer_type, bloc)); - params->push_back(Typed_identifier("key2", unsafe_pointer_type, bloc)); - params->push_back(Typed_identifier("key_size", uintptr_type, bloc)); - - results = new Typed_identifier_list(); - results->push_back(Typed_identifier("", Type::lookup_bool_type(), bloc)); - - Type* equalfn_type = Type::make_function_type(NULL, params, results, - bloc); - Struct_type* type_descriptor_type = Type::make_builtin_struct_type(10, "Kind", uint8_type, @@ -1555,8 +1535,8 @@ Type::make_type_descriptor_type() "fieldAlign", uint8_type, "size", uintptr_type, "hash", uint32_type, - "hashfn", hashfn_type, - "equalfn", equalfn_type, + "hashfn", uintptr_type, + "equalfn", uintptr_type, "string", pointer_string_type, "", pointer_uncommon_type, "ptrToThis", @@ -1946,8 +1926,8 @@ Type::type_descriptor_constructor(Gogo* gogo, int runtime_type_kind, Named_object* equal_fn; this->type_functions(gogo, name, hash_fntype, equal_fntype, &hash_fn, &equal_fn); - vals->push_back(Expression::make_func_reference(hash_fn, NULL, bloc)); - vals->push_back(Expression::make_func_reference(equal_fn, NULL, bloc)); + vals->push_back(Expression::make_func_code_reference(hash_fn, bloc)); + vals->push_back(Expression::make_func_code_reference(equal_fn, bloc)); ++p; go_assert(p->is_field_name("string")); @@ -2207,7 +2187,7 @@ Type::method_constructor(Gogo*, Type* method_type, ++p; go_assert(p->is_field_name("tfn")); - vals->push_back(Expression::make_func_reference(no, NULL, bloc)); + vals->push_back(Expression::make_func_code_reference(no, bloc)); ++p; go_assert(p == fields->end()); @@ -3407,6 +3387,18 @@ Function_type::do_hash_for_method(Gogo* gogo) const Btype* Function_type::do_get_backend(Gogo* gogo) { + // When we do anything with a function value other than call it, it + // is represented as a pointer to a struct whose first field is the + // actual function. So that is what we return as the type of a Go + // function. The function stored in the first field always that + // takes one additional trailing argument: the closure pointer. For + // a top-level function, this additional argument will only be + // passed when invoking the function indirectly, via the struct. + + Location loc = this->location(); + Btype* struct_type = gogo->backend()->placeholder_struct_type("", loc); + Btype* ptr_struct_type = gogo->backend()->pointer_type(struct_type); + Backend::Btyped_identifier breceiver; if (this->receiver_ != NULL) { @@ -3422,9 +3414,15 @@ Function_type::do_get_backend(Gogo* gogo) } std::vector bparameters; - if (this->parameters_ != NULL) + size_t last; + if (this->parameters_ == NULL) + { + bparameters.resize(1); + last = 0; + } + else { - bparameters.resize(this->parameters_->size()); + bparameters.resize(this->parameters_->size() + 1); size_t i = 0; for (Typed_identifier_list::const_iterator p = this->parameters_->begin(); p != this->parameters_->end(); @@ -3434,8 +3432,12 @@ Function_type::do_get_backend(Gogo* gogo) bparameters[i].btype = p->type()->get_backend(gogo); bparameters[i].location = p->location(); } - go_assert(i == bparameters.size()); + last = i; } + go_assert(last + 1 == bparameters.size()); + bparameters[last].name = "$closure"; + bparameters[last].btype = ptr_struct_type; + bparameters[last].location = loc; std::vector bresults; if (this->results_ != NULL) @@ -3453,8 +3455,15 @@ Function_type::do_get_backend(Gogo* gogo) go_assert(i == bresults.size()); } - return gogo->backend()->function_type(breceiver, bparameters, bresults, - this->location()); + Btype* fntype = gogo->backend()->function_type(breceiver, bparameters, + bresults, loc); + std::vector fields(1); + fields[0].name = "code"; + fields[0].btype = fntype; + fields[0].location = loc; + if (!gogo->backend()->set_placeholder_struct_type(struct_type, fields)) + return gogo->backend()->error_type(); + return ptr_struct_type; } // The type of a function type descriptor. @@ -6228,7 +6237,8 @@ Map_type::map_descriptor(Gogo* gogo) std::string mangled_name = "__go_map_" + this->mangled_name(gogo); Btype* map_descriptor_btype = map_descriptor_type->get_backend(gogo); - Bvariable* bvar = gogo->backend()->immutable_struct(mangled_name, true, + Bvariable* bvar = gogo->backend()->immutable_struct(mangled_name, false, + true, map_descriptor_btype, bloc); @@ -6236,7 +6246,7 @@ Map_type::map_descriptor(Gogo* gogo) context.set_is_const(); Bexpression* binitializer = tree_to_expr(initializer->get_tree(&context)); - gogo->backend()->immutable_struct_set_init(bvar, mangled_name, true, + gogo->backend()->immutable_struct_set_init(bvar, mangled_name, false, true, map_descriptor_btype, bloc, binitializer); diff --git a/gcc/go/gofrontend/types.h b/gcc/go/gofrontend/types.h index 3922a634f59..91d472931a6 100644 --- a/gcc/go/gofrontend/types.h +++ b/gcc/go/gofrontend/types.h @@ -523,6 +523,14 @@ class Type static Type* make_forward_declaration(Named_object*); + // Make a builtin struct type from a list of fields. + static Struct_type* + make_builtin_struct_type(int nfields, ...); + + // Make a builtin named type. + static Named_type* + make_builtin_named_type(const char* name, Type* type); + // Traverse a type. static int traverse(Type*, Traverse*); @@ -1035,14 +1043,6 @@ class Type type_descriptor_constructor(Gogo*, int runtime_type_kind, Named_type*, const Methods*, bool only_value_methods); - // Make a builtin struct type from a list of fields. - static Struct_type* - make_builtin_struct_type(int nfields, ...); - - // Make a builtin named type. - static Named_type* - make_builtin_named_type(const char* name, Type* type); - // For the benefit of child class reflection string generation. void append_reflection(const Type* type, Gogo* gogo, std::string* ret) const @@ -1796,7 +1796,7 @@ class Function_type : public Type int do_traverse(Traverse*); - // A trampoline function has a pointer which matters for GC. + // A function descriptor may be allocated on the heap. bool do_has_pointer() const { return true; } diff --git a/libgo/Makefile.am b/libgo/Makefile.am index ea90318cee1..d54448c42a0 100644 --- a/libgo/Makefile.am +++ b/libgo/Makefile.am @@ -487,7 +487,6 @@ runtime_files = \ runtime/go-strplus.c \ runtime/go-strslice.c \ runtime/go-traceback.c \ - runtime/go-trampoline.c \ runtime/go-type-complex.c \ runtime/go-type-eface.c \ runtime/go-type-error.c \ diff --git a/libgo/Makefile.in b/libgo/Makefile.in index 2ec60d3894d..08eae578e51 100644 --- a/libgo/Makefile.in +++ b/libgo/Makefile.in @@ -208,7 +208,7 @@ am__objects_5 = go-append.lo go-assert.lo go-assert-interface.lo \ go-reflect-call.lo go-reflect-map.lo go-rune.lo \ go-runtime-error.lo go-setenv.lo go-signal.lo go-strcmp.lo \ go-string-to-byte-array.lo go-string-to-int-array.lo \ - go-strplus.lo go-strslice.lo go-traceback.lo go-trampoline.lo \ + go-strplus.lo go-strslice.lo go-traceback.lo \ go-type-complex.lo go-type-eface.lo go-type-error.lo \ go-type-float.lo go-type-identity.lo go-type-interface.lo \ go-type-string.lo go-typedesc-equal.lo go-typestring.lo \ @@ -822,7 +822,6 @@ runtime_files = \ runtime/go-strplus.c \ runtime/go-strslice.c \ runtime/go-traceback.c \ - runtime/go-trampoline.c \ runtime/go-type-complex.c \ runtime/go-type-eface.c \ runtime/go-type-error.c \ @@ -2519,7 +2518,6 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-strplus.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-strslice.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-traceback.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-trampoline.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-type-complex.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-type-eface.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-type-error.Plo@am__quote@ @@ -2959,13 +2957,6 @@ go-traceback.lo: runtime/go-traceback.c @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o go-traceback.lo `test -f 'runtime/go-traceback.c' || echo '$(srcdir)/'`runtime/go-traceback.c -go-trampoline.lo: runtime/go-trampoline.c -@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT go-trampoline.lo -MD -MP -MF $(DEPDIR)/go-trampoline.Tpo -c -o go-trampoline.lo `test -f 'runtime/go-trampoline.c' || echo '$(srcdir)/'`runtime/go-trampoline.c -@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/go-trampoline.Tpo $(DEPDIR)/go-trampoline.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='runtime/go-trampoline.c' object='go-trampoline.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o go-trampoline.lo `test -f 'runtime/go-trampoline.c' || echo '$(srcdir)/'`runtime/go-trampoline.c - go-type-complex.lo: runtime/go-type-complex.c @am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT go-type-complex.lo -MD -MP -MF $(DEPDIR)/go-type-complex.Tpo -c -o go-type-complex.lo `test -f 'runtime/go-type-complex.c' || echo '$(srcdir)/'`runtime/go-type-complex.c @am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/go-type-complex.Tpo $(DEPDIR)/go-type-complex.Plo diff --git a/libgo/configure b/libgo/configure index e1d37dc2d7b..5c943ff5589 100755 --- a/libgo/configure +++ b/libgo/configure @@ -2496,7 +2496,7 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu ac_config_headers="$ac_config_headers config.h" -libtool_VERSION=3:1:0 +libtool_VERSION=5:0:0 # Default to --enable-multilib diff --git a/libgo/configure.ac b/libgo/configure.ac index 58cc045734c..002aa88675c 100644 --- a/libgo/configure.ac +++ b/libgo/configure.ac @@ -11,7 +11,7 @@ AC_INIT(package-unused, version-unused,, libgo) AC_CONFIG_SRCDIR(Makefile.am) AC_CONFIG_HEADER(config.h) -libtool_VERSION=3:1:0 +libtool_VERSION=5:0:0 AC_SUBST(libtool_VERSION) AM_ENABLE_MULTILIB(, ..) diff --git a/libgo/go/reflect/all_test.go b/libgo/go/reflect/all_test.go index 8a3602347fd..5a2ae2eee72 100644 --- a/libgo/go/reflect/all_test.go +++ b/libgo/go/reflect/all_test.go @@ -1891,6 +1891,7 @@ func (*inner) m() {} func (*outer) m() {} func TestNestedMethods(t *testing.T) { + t.Skip("fails on gccgo due to function wrappers") typ := TypeOf((*outer)(nil)) if typ.NumMethod() != 1 || typ.Method(0).Func.Pointer() != ValueOf((*outer).m).Pointer() { t.Errorf("Wrong method table for outer: (m=%p)", (*outer).m) @@ -1915,6 +1916,7 @@ func (i *InnerInt) M() int { } func TestEmbeddedMethods(t *testing.T) { + /* This part of the test fails on gccgo due to function wrappers. typ := TypeOf((*OuterInt)(nil)) if typ.NumMethod() != 1 || typ.Method(0).Func.Pointer() != ValueOf((*OuterInt).M).Pointer() { t.Errorf("Wrong method table for OuterInt: (m=%p)", (*OuterInt).M) @@ -1923,6 +1925,7 @@ func TestEmbeddedMethods(t *testing.T) { t.Errorf("\t%d: %s %#x\n", i, m.Name, m.Func.Pointer()) } } + */ i := &InnerInt{3} if v := ValueOf(i).Method(0).Call(nil)[0].Int(); v != 3 { diff --git a/libgo/go/reflect/type.go b/libgo/go/reflect/type.go index 9e65870990f..b909177a42a 100644 --- a/libgo/go/reflect/type.go +++ b/libgo/go/reflect/type.go @@ -243,8 +243,8 @@ type rtype struct { size uintptr // size in bytes hash uint32 // hash of type; avoids computation in hash tables - hashfn func(unsafe.Pointer, uintptr) // hash function - equalfn func(unsafe.Pointer, unsafe.Pointer, uintptr) // equality function + hashfn uintptr // hash function code + equalfn uintptr // equality function code string *string // string form; unnecessary but undeniably useful *uncommonType // (relatively) uncommon fields @@ -485,7 +485,7 @@ func (t *uncommonType) Method(i int) (m Method) { mt := p.typ m.Type = toType(mt) x := new(unsafe.Pointer) - *x = p.tfn + *x = unsafe.Pointer(&p.tfn) m.Func = Value{mt, unsafe.Pointer(x), fl | flagIndir} m.Index = i return diff --git a/libgo/go/reflect/value.go b/libgo/go/reflect/value.go index 15f571509b9..f8126e676d8 100644 --- a/libgo/go/reflect/value.go +++ b/libgo/go/reflect/value.go @@ -377,7 +377,7 @@ func (v Value) call(method string, in []Value) []Value { if iface.itab == nil { panic(method + " of method on nil interface value") } - fn = iface.itab.fun[i] + fn = unsafe.Pointer(&iface.itab.fun[i]) rcvr = iface.word } else { ut := v.typ.uncommon() @@ -388,7 +388,7 @@ func (v Value) call(method string, in []Value) []Value { if m.pkgPath != nil { panic(method + " of unexported method") } - fn = m.tfn + fn = unsafe.Pointer(&m.tfn) t = m.mtyp rcvr = v.iword() } @@ -462,6 +462,10 @@ func (v Value) call(method string, in []Value) []Value { if v.flag&flagMethod != 0 { nin++ } + firstPointer := len(in) > 0 && Kind(t.In(0).(*rtype).kind) != Ptr && v.flag&flagMethod == 0 && isMethod(v.typ) + if v.flag&flagMethod == 0 && !firstPointer { + nin++ + } params := make([]unsafe.Pointer, nin) off := 0 if v.flag&flagMethod != 0 { @@ -471,7 +475,6 @@ func (v Value) call(method string, in []Value) []Value { params[0] = unsafe.Pointer(p) off = 1 } - first_pointer := false for i, pv := range in { pv.mustBeExported() targ := t.In(i).(*rtype) @@ -483,14 +486,17 @@ func (v Value) call(method string, in []Value) []Value { } else { params[off] = pv.val } - if i == 0 && Kind(targ.kind) != Ptr && v.flag&flagMethod == 0 && isMethod(v.typ) { + if i == 0 && firstPointer { p := new(unsafe.Pointer) *p = params[off] params[off] = unsafe.Pointer(p) - first_pointer = true } off++ } + if v.flag&flagMethod == 0 && !firstPointer { + // Closure argument. + params[off] = unsafe.Pointer(&fn) + } ret := make([]Value, nout) results := make([]unsafe.Pointer, nout) @@ -509,7 +515,7 @@ func (v Value) call(method string, in []Value) []Value { pr = &results[0] } - call(t, fn, v.flag&flagMethod != 0, first_pointer, pp, pr) + call(t, fn, v.flag&flagMethod != 0, firstPointer, pp, pr) return ret } @@ -1209,18 +1215,35 @@ func (v Value) OverflowUint(x uint64) bool { // code using reflect cannot obtain unsafe.Pointers // without importing the unsafe package explicitly. // It panics if v's Kind is not Chan, Func, Map, Ptr, Slice, or UnsafePointer. +// +// If v's Kind is Func, the returned pointer is an underlying +// code pointer, but not necessarily enough to identify a +// single function uniquely. The only guarantee is that the +// result is zero if and only if v is a nil func Value. func (v Value) Pointer() uintptr { k := v.kind() switch k { - case Chan, Func, Map, Ptr, UnsafePointer: - if k == Func && v.flag&flagMethod != 0 { + case Chan, Map, Ptr, UnsafePointer: + p := v.val + if v.flag&flagIndir != 0 { + p = *(*unsafe.Pointer)(p) + } + return uintptr(p) + case Func: + if v.flag&flagMethod != 0 { panic("reflect.Value.Pointer of method Value") } p := v.val if v.flag&flagIndir != 0 { p = *(*unsafe.Pointer)(p) } + // Non-nil func value points at data block. + // First word of data block is actual code. + if p != nil { + p = *(*unsafe.Pointer)(p) + } return uintptr(p) + case Slice: return (*SliceHeader)(v.val).Data } diff --git a/libgo/go/runtime/extern.go b/libgo/go/runtime/extern.go index 2a90113a3a9..6e91ef56b70 100644 --- a/libgo/go/runtime/extern.go +++ b/libgo/go/runtime/extern.go @@ -59,9 +59,6 @@ func (f *Func) FileLine(pc uintptr) (file string, line int) { // implemented in symtab.c func funcline_go(*Func, uintptr) (string, int) -// mid returns the current OS thread (m) id. -func mid() uint32 - // SetFinalizer sets the finalizer associated with x to f. // When the garbage collector finds an unreachable block // with an associated finalizer, it clears the association and runs diff --git a/libgo/go/runtime/parfor_test.go b/libgo/go/runtime/parfor_test.go index b382b76a7b2..4c69a68ceea 100644 --- a/libgo/go/runtime/parfor_test.go +++ b/libgo/go/runtime/parfor_test.go @@ -13,6 +13,8 @@ import ( "unsafe" ) +var gdata []uint64 + // Simple serial sanity test for parallelfor. func TestParFor(t *testing.T) { const P = 1 @@ -22,7 +24,12 @@ func TestParFor(t *testing.T) { data[i] = i } desc := NewParFor(P) + // Avoid making func a closure: parfor cannot invoke them. + // Since it doesn't happen in the C code, it's not worth doing + // just for the test. + gdata = data ParForSetup(desc, P, N, nil, true, func(desc *ParFor, i uint32) { + data := gdata data[i] = data[i]*data[i] + 1 }) ParForDo(desc) @@ -111,7 +118,9 @@ func TestParForParallel(t *testing.T) { P := GOMAXPROCS(-1) c := make(chan bool, P) desc := NewParFor(uint32(P)) + gdata = data ParForSetup(desc, uint32(P), uint32(N), nil, false, func(desc *ParFor, i uint32) { + data := gdata data[i] = data[i]*data[i] + 1 }) for p := 1; p < P; p++ { diff --git a/libgo/runtime/go-reflect-call.c b/libgo/runtime/go-reflect-call.c index a66f92868f8..83b9eba0436 100644 --- a/libgo/runtime/go-reflect-call.c +++ b/libgo/runtime/go-reflect-call.c @@ -302,7 +302,9 @@ go_func_to_cif (const struct __go_func_type *func, _Bool is_interface, in_types = ((const struct __go_type_descriptor **) func->__in.__values); - num_args = num_params + (is_interface ? 1 : 0); + num_args = (num_params + + (is_interface ? 1 : 0) + + (!is_interface && !is_method ? 1 : 0)); args = (ffi_type **) __go_alloc (num_args * sizeof (ffi_type *)); i = 0; off = 0; @@ -319,6 +321,12 @@ go_func_to_cif (const struct __go_func_type *func, _Bool is_interface, for (; i < num_params; ++i) args[i + off] = go_type_to_ffi (in_types[i]); + if (!is_interface && !is_method) + { + // There is a closure argument, a pointer. + args[i + off] = &ffi_type_pointer; + } + rettype = go_func_return_ffi (func); status = ffi_prep_cif (cif, FFI_DEFAULT_ABI, num_args, rettype, args); @@ -491,11 +499,24 @@ go_set_results (const struct __go_func_type *func, unsigned char *call_result, } /* Call a function. The type of the function is FUNC_TYPE, and the - address is FUNC_ADDR. PARAMS is an array of parameter addresses. - RESULTS is an array of result addresses. */ + closure is FUNC_VAL. PARAMS is an array of parameter addresses. + RESULTS is an array of result addresses. + + If IS_INTERFACE is true this is a call to an interface method and + the first argument is the receiver, which is always a pointer. + This argument, the receiver, is not described in FUNC_TYPE. + + If IS_METHOD is true this is a call to a method expression. The + first argument is the receiver. It is described in FUNC_TYPE, but + regardless of FUNC_TYPE, it is passed as a pointer. + + If neither IS_INTERFACE nor IS_METHOD is true then we are calling a + function indirectly, and the caller is responsible for passing a + trailing closure argument, a pointer, which is not described in + FUNC_TYPE. */ void -reflect_call (const struct __go_func_type *func_type, const void *func_addr, +reflect_call (const struct __go_func_type *func_type, FuncVal *func_val, _Bool is_interface, _Bool is_method, void **params, void **results) { @@ -507,7 +528,7 @@ reflect_call (const struct __go_func_type *func_type, const void *func_addr, call_result = (unsigned char *) malloc (go_results_size (func_type)); - ffi_call (&cif, func_addr, call_result, params); + ffi_call (&cif, func_val->fn, call_result, params); /* Some day we may need to free result values if RESULTS is NULL. */ @@ -521,7 +542,7 @@ reflect_call (const struct __go_func_type *func_type, const void *func_addr, void reflect_call (const struct __go_func_type *func_type __attribute__ ((unused)), - const void *func_addr __attribute__ ((unused)), + FuncVal *func_val __attribute__ ((unused)), _Bool is_interface __attribute__ ((unused)), _Bool is_method __attribute__ ((unused)), void **params __attribute__ ((unused)), diff --git a/libgo/runtime/malloc.h b/libgo/runtime/malloc.h index a8207742031..7ebb762450e 100644 --- a/libgo/runtime/malloc.h +++ b/libgo/runtime/malloc.h @@ -485,7 +485,7 @@ void runtime_helpgc(int32 nproc); void runtime_gchelper(void); struct __go_func_type; -bool runtime_getfinalizer(void *p, bool del, void (**fn)(void*), const struct __go_func_type **ft); +bool runtime_getfinalizer(void *p, bool del, FuncVal **fn, const struct __go_func_type **ft); void runtime_walkfintab(void (*fn)(void*), void (*scan)(Obj)); enum @@ -505,4 +505,3 @@ void runtime_gc_itab_ptr(Eface*); void runtime_memorydump(void); void runtime_time_scan(void (*)(Obj)); -void runtime_trampoline_scan(void (*)(Obj)); diff --git a/libgo/runtime/mfinal.c b/libgo/runtime/mfinal.c index 7c906daf347..407092bf392 100644 --- a/libgo/runtime/mfinal.c +++ b/libgo/runtime/mfinal.c @@ -11,7 +11,7 @@ enum { debug = 0 }; typedef struct Fin Fin; struct Fin { - void (*fn)(void*); + FuncVal *fn; const struct __go_func_type *ft; }; @@ -42,7 +42,7 @@ static struct { } fintab[TABSZ]; static void -addfintab(Fintab *t, void *k, void (*fn)(void*), const struct __go_func_type *ft) +addfintab(Fintab *t, void *k, FuncVal *fn, const struct __go_func_type *ft) { int32 i, j; @@ -137,7 +137,7 @@ resizefintab(Fintab *tab) } bool -runtime_addfinalizer(void *p, void (*f)(void*), const struct __go_func_type *ft) +runtime_addfinalizer(void *p, FuncVal *f, const struct __go_func_type *ft) { Fintab *tab; byte *base; @@ -175,7 +175,7 @@ runtime_addfinalizer(void *p, void (*f)(void*), const struct __go_func_type *ft) // get finalizer; if del, delete finalizer. // caller is responsible for updating RefHasFinalizer (special) bit. bool -runtime_getfinalizer(void *p, bool del, void (**fn)(void*), const struct __go_func_type **ft) +runtime_getfinalizer(void *p, bool del, FuncVal **fn, const struct __go_func_type **ft) { Fintab *tab; bool res; diff --git a/libgo/runtime/mgc0.c b/libgo/runtime/mgc0.c index ffbe2cefb2d..88283ccab2b 100644 --- a/libgo/runtime/mgc0.c +++ b/libgo/runtime/mgc0.c @@ -120,7 +120,7 @@ struct Workbuf typedef struct Finalizer Finalizer; struct Finalizer { - void (*fn)(void*); + FuncVal *fn; void *arg; const struct __go_func_type *ft; }; @@ -1130,7 +1130,6 @@ addroots(void) addroot((Obj){(byte*)&runtime_allm, sizeof runtime_allm, 0}); runtime_MProf_Mark(addroot); runtime_time_scan(addroot); - runtime_trampoline_scan(addroot); // MSpan.types allspans = runtime_mheap.allspans; @@ -1182,7 +1181,7 @@ addroots(void) static bool handlespecial(byte *p, uintptr size) { - void (*fn)(void*); + FuncVal *fn; const struct __go_func_type *ft; FinBlock *block; Finalizer *f; @@ -1731,11 +1730,12 @@ runfinq(void* dummy __attribute__ ((unused))) for(; fb; fb=next) { next = fb->next; for(i=0; i<(uint32)fb->cnt; i++) { - void *params[1]; + void *params[2]; f = &fb->fin[i]; params[0] = &f->arg; - reflect_call(f->ft, (void*)f->fn, 0, 0, params, nil); + params[1] = f; + reflect_call(f->ft, f->fn, 0, 0, params, nil); f->fn = nil; f->arg = nil; } diff --git a/libgo/runtime/parfor.c b/libgo/runtime/parfor.c index 591b968c039..65ca586eadb 100644 --- a/libgo/runtime/parfor.c +++ b/libgo/runtime/parfor.c @@ -83,7 +83,7 @@ void runtime_parforsetup2(ParFor *, uint32, uint32, void *, bool, void *) void runtime_parforsetup2(ParFor *desc, uint32 nthr, uint32 n, void *ctx, bool wait, void *body) { - runtime_parforsetup(desc, nthr, n, ctx, wait, (void(*)(ParFor*, uint32))body); + runtime_parforsetup(desc, nthr, n, ctx, wait, *(void(**)(ParFor*, uint32))body); } void diff --git a/libgo/runtime/runtime.h b/libgo/runtime/runtime.h index 9392df162bf..959220d734c 100644 --- a/libgo/runtime/runtime.h +++ b/libgo/runtime/runtime.h @@ -57,6 +57,7 @@ typedef struct G G; typedef union Lock Lock; typedef struct M M; typedef union Note Note; +typedef struct FuncVal FuncVal; typedef struct SigTab SigTab; typedef struct MCache MCache; typedef struct FixAlloc FixAlloc; @@ -147,6 +148,11 @@ struct String const byte* str; intgo len; }; +struct FuncVal +{ + void (*fn)(void); + // variable-size, fn-specific data here +}; struct GCStats { // the struct must consist of only uint64's, @@ -313,7 +319,7 @@ struct Timer // a well-behaved function and not block. int64 when; int64 period; - void (*f)(int64, Eface); + FuncVal *fv; Eface arg; }; @@ -540,7 +546,7 @@ void runtime_printslice(Slice); void runtime_printcomplex(__complex double); struct __go_func_type; -void reflect_call(const struct __go_func_type *, const void *, _Bool, _Bool, +void reflect_call(const struct __go_func_type *, FuncVal *, _Bool, _Bool, void **, void **) __asm__ (GOSYM_PREFIX "reflect.call"); @@ -570,7 +576,7 @@ void free(void *v); #define PREFETCH(p) __builtin_prefetch(p) struct __go_func_type; -bool runtime_addfinalizer(void*, void(*fn)(void*), const struct __go_func_type *); +bool runtime_addfinalizer(void*, FuncVal *fn, const struct __go_func_type *); #define runtime_getcallersp(p) __builtin_frame_address(1) int32 runtime_mcount(void); int32 runtime_gcount(void); diff --git a/libgo/runtime/time.goc b/libgo/runtime/time.goc index 9a5cbdfed3b..e9f087ac884 100644 --- a/libgo/runtime/time.goc +++ b/libgo/runtime/time.goc @@ -49,13 +49,16 @@ static void siftdown(int32); // Ready the goroutine e.data. static void -ready(int64 now, Eface e) +ready(int64 now, Eface e, void *closure) { USED(now); + USED(closure); runtime_ready(e.__object); } +static FuncVal readyv = {(void(*)(void))ready}; + // Put the current goroutine to sleep for ns nanoseconds. void runtime_tsleep(int64 ns, const char *reason) @@ -70,7 +73,7 @@ runtime_tsleep(int64 ns, const char *reason) t.when = runtime_nanotime() + ns; t.period = 0; - t.f = ready; + t.fv = &readyv; t.arg.__object = g; runtime_lock(&timers); addtimer(&t); @@ -158,7 +161,7 @@ timerproc(void* dummy __attribute__ ((unused))) { int64 delta, now; Timer *t; - void (*f)(int64, Eface); + void (*f)(int64, Eface, void *); Eface arg; for(;;) { @@ -184,12 +187,12 @@ timerproc(void* dummy __attribute__ ((unused))) siftdown(0); t->i = -1; // mark as removed } - f = t->f; + f = (void*)t->fv->fn; arg = t->arg; runtime_unlock(&timers); if(raceenabled) runtime_raceacquire(t); - f(now, arg); + f(now, arg, &t->fv); runtime_lock(&timers); } if(delta < 0) { -- 2.11.4.GIT