diff options
Diffstat (limited to 'gcc/go')
-rw-r--r-- | gcc/go/ChangeLog | 6 | ||||
-rw-r--r-- | gcc/go/go-gcc.cc | 15 | ||||
-rw-r--r-- | gcc/go/gofrontend/backend.h | 35 | ||||
-rw-r--r-- | gcc/go/gofrontend/expressions.cc | 505 | ||||
-rw-r--r-- | gcc/go/gofrontend/expressions.h | 39 | ||||
-rw-r--r-- | gcc/go/gofrontend/gogo-tree.cc | 232 | ||||
-rw-r--r-- | gcc/go/gofrontend/gogo.cc | 214 | ||||
-rw-r--r-- | gcc/go/gofrontend/gogo.h | 61 | ||||
-rw-r--r-- | gcc/go/gofrontend/parse.cc | 24 | ||||
-rw-r--r-- | gcc/go/gofrontend/runtime.def | 5 | ||||
-rw-r--r-- | gcc/go/gofrontend/statements.cc | 15 | ||||
-rw-r--r-- | gcc/go/gofrontend/types.cc | 80 | ||||
-rw-r--r-- | gcc/go/gofrontend/types.h | 18 |
13 files changed, 957 insertions, 292 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 <iant@google.com> + + * go-gcc.cc (Gcc_backend::immutable_struct): Add is_hidden + parameter. + (Gcc_backend::immutable_struct_set_init): Likewise. + 2013-05-16 Jason Merrill <jason@redhat.com> * 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<Btyped_identifier>& 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<Named_object*> 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<Named_object*>::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<std::string, Package*> 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<Enclosing_vars::iterator, bool> 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<Backend::Btyped_identifier> 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<Backend::Btyped_identifier> 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<Backend::Btyped_identifier> 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; } |