aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIan Lance Taylor <iant@google.com>2013-06-18 23:49:49 +0000
committerIan Lance Taylor <ian@gcc.gnu.org>2013-06-18 23:49:49 +0000
commitfdbc38a6e8d7c920eea6c6231c7fe2c987fa8aa2 (patch)
tree1a7d38cd8be5484451189338ed6f4b76d8521f31
parent25e00ab67444a01dce446e95308521d1a73f8232 (diff)
downloadgcc-fdbc38a6e8d7c920eea6c6231c7fe2c987fa8aa2.zip
gcc-fdbc38a6e8d7c920eea6c6231c7fe2c987fa8aa2.tar.gz
gcc-fdbc38a6e8d7c920eea6c6231c7fe2c987fa8aa2.tar.bz2
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. From-SVN: r200181
-rw-r--r--gcc/go/ChangeLog6
-rw-r--r--gcc/go/go-gcc.cc15
-rw-r--r--gcc/go/gofrontend/backend.h35
-rw-r--r--gcc/go/gofrontend/expressions.cc505
-rw-r--r--gcc/go/gofrontend/expressions.h39
-rw-r--r--gcc/go/gofrontend/gogo-tree.cc232
-rw-r--r--gcc/go/gofrontend/gogo.cc214
-rw-r--r--gcc/go/gofrontend/gogo.h61
-rw-r--r--gcc/go/gofrontend/parse.cc24
-rw-r--r--gcc/go/gofrontend/runtime.def5
-rw-r--r--gcc/go/gofrontend/statements.cc15
-rw-r--r--gcc/go/gofrontend/types.cc80
-rw-r--r--gcc/go/gofrontend/types.h18
-rw-r--r--libgo/Makefile.am1
-rw-r--r--libgo/Makefile.in11
-rwxr-xr-xlibgo/configure2
-rw-r--r--libgo/configure.ac2
-rw-r--r--libgo/go/reflect/all_test.go3
-rw-r--r--libgo/go/reflect/type.go6
-rw-r--r--libgo/go/reflect/value.go39
-rw-r--r--libgo/go/runtime/extern.go3
-rw-r--r--libgo/go/runtime/parfor_test.go9
-rw-r--r--libgo/runtime/go-reflect-call.c33
-rw-r--r--libgo/runtime/malloc.h3
-rw-r--r--libgo/runtime/mfinal.c8
-rw-r--r--libgo/runtime/mgc0.c10
-rw-r--r--libgo/runtime/parfor.c2
-rw-r--r--libgo/runtime/runtime.h12
-rw-r--r--libgo/runtime/time.goc13
29 files changed, 1061 insertions, 345 deletions
diff --git a/gcc/go/ChangeLog b/gcc/go/ChangeLog
index aadca50..15df704 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 a6acf13..bd2d0dd 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 fe6db74..ac29b03 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 afc1832..9c1efb3 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 36f4c0d..ed3909c 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 b984045..cd54f2b 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 80ae8d4..e4c21be 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 f96ffcd..ec43ef6 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 d7a18d0..b562a78 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 0d9ff03..0d3fd3c 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 c9c62ce..7e4c561 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 4fbb162..ae25ec9 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 3922a63..91d4729 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 ea90318..d54448c 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 2ec60d3..08eae57 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 e1d37dc..5c943ff 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 58cc045..002aa88 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 8a36023..5a2ae2e 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 9e65870..b909177 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 15f5715..f8126e6 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 2a90113..6e91ef5 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 b382b76..4c69a68 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 a66f928..83b9eba 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 a820774..7ebb762 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 7c906da..407092b 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 ffbe2ce..88283cc 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 591b968..65ca586 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 9392df1..959220d 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 9a5cbdf..e9f087a 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) {