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:
parent
25e00ab674
commit
fdbc38a6e8
29 changed files with 1061 additions and 345 deletions
|
@ -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.
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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_;
|
||||
};
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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_;
|
||||
};
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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())
|
||||
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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 \
|
||||
|
|
|
@ -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
2
libgo/configure
vendored
|
@ -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
|
||||
|
|
|
@ -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(, ..)
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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++ {
|
||||
|
|
|
@ -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)),
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
Loading…
Add table
Reference in a new issue