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
This commit is contained in:
Ian Lance Taylor 2013-06-18 23:49:49 +00:00 committed by Ian Lance Taylor
parent 25e00ab674
commit fdbc38a6e8
29 changed files with 1061 additions and 345 deletions

View File

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

View File

@ -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));

View File

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

View File

@ -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;
{
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();
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);
}
go_assert(this->function_->func_value()->enclosing() != NULL);
// 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)));
// 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.
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,35 +9485,70 @@ 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]);
{
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)
{
delete[] args;
return error_mark_node;
}
return error_mark_node;
tree fndecl = fn;
if (TREE_CODE(fndecl) == ADDR_EXPR)
@ -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()

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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())

View File

@ -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())

View File

@ -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(this->parameters_->size());
bparameters.resize(1);
last = 0;
}
else
{
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);

View File

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

View File

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

View File

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

2
libgo/configure vendored
View File

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

View File

@ -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(, ..)

View File

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

View File

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

View File

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

View File

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

View File

@ -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++ {

View File

@ -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)),

View File

@ -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));

View File

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

View File

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

View File

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

View File

@ -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);

View File

@ -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) {