compiler, runtime: Use runtime functions to pass closure value.

This changes the compiler and runtime to not pass a closure
value as the last argument, but to instead pass it via
__go_set_closure and retrieve it via __go_get_closure.  This
eliminates the need for function descriptor wrapper functions.
It will make it possible to retrieve the closure value in a
reflect.MakeFunc function.

From-SVN: r202233
This commit is contained in:
Ian Lance Taylor 2013-09-03 21:52:37 +00:00
parent 3b18bc426a
commit 05a7d56678
15 changed files with 237 additions and 343 deletions

View file

@ -1382,7 +1382,7 @@ Expression::make_func_reference(Named_object* function, Expression* closure,
Func_descriptor_expression::Func_descriptor_expression(Named_object* fn)
: Expression(EXPRESSION_FUNC_DESCRIPTOR, fn->location()),
fn_(fn), dfn_(NULL), dvar_(NULL)
fn_(fn), dvar_(NULL)
{
go_assert(!fn->is_function() || !fn->func_value()->needs_closure());
}
@ -1417,18 +1417,6 @@ Func_descriptor_expression::do_type()
return Func_descriptor_expression::descriptor_type;
}
// Copy a Func_descriptor_expression;
Expression*
Func_descriptor_expression::do_copy()
{
Func_descriptor_expression* fde =
Expression::make_func_descriptor(this->fn_);
if (this->dfn_ != NULL)
fde->set_descriptor_wrapper(this->dfn_);
return fde;
}
// The tree for a function descriptor.
tree
@ -1455,11 +1443,8 @@ Func_descriptor_expression::do_get_tree(Translate_context* context)
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);
}
bvar = context->backend()->immutable_struct_reference(var_name, btype,
loc);
else
{
Location bloc = Linemap::predeclared_location();
@ -1469,8 +1454,7 @@ Func_descriptor_expression::do_get_tree(Translate_context* context)
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));
vals->push_back(Expression::make_func_code_reference(this->fn_, bloc));
Expression* init =
Expression::make_struct_composite_literal(this->type(), vals, bloc);
Translate_context bcontext(gogo, NULL, NULL, NULL);
@ -6792,8 +6776,8 @@ Bound_method_expression::create_thunk(Gogo* gogo, const Method* method,
}
Struct_field_list* sfl = new Struct_field_list();
// The type here is wrong--it should be new_fntype. But we don't
// have new_fntype yet, and it doesn't really matter.
// The type here is wrong--it should be the C function type. But it
// doesn't really matter.
Type* vt = Type::make_pointer_type(Type::make_void_type());
sfl->push_back(Struct_field(Typed_identifier("fn.0", vt, loc)));
sfl->push_back(Struct_field(Typed_identifier("val.1",
@ -6802,17 +6786,17 @@ Bound_method_expression::create_thunk(Gogo* gogo, const Method* method,
Type* closure_type = Type::make_struct_type(sfl, loc);
closure_type = Type::make_pointer_type(closure_type);
Function_type* new_fntype = orig_fntype->copy_with_closure(closure_type);
Function_type* new_fntype = orig_fntype->copy_with_names();
Named_object* new_no = gogo->start_function(Gogo::thunk_name(), new_fntype,
false, loc);
gogo->start_block(loc);
Variable* cvar = new Variable(closure_type, NULL, false, false, false, loc);
cvar->set_is_used();
Named_object* cp = Named_object::make_variable("$closure", NULL, cvar);
new_no->func_value()->set_closure_var(cp);
Named_object* cp = gogo->lookup("closure.0", NULL);
go_assert(cp != NULL
&& cp->is_variable()
&& cp->var_value()->is_parameter());
gogo->start_block(loc);
// Field 0 of the closure is the function code pointer, field 1 is
// the value on which to invoke the method.
@ -6831,7 +6815,7 @@ Bound_method_expression::create_thunk(Gogo* gogo, const Method* method,
const Typed_identifier_list* new_params = new_fntype->parameters();
args = new Expression_list();
for (Typed_identifier_list::const_iterator p = new_params->begin();
p + 1 != new_params->end();
p != new_params->end();
++p)
{
Named_object* p_no = gogo->lookup(p->name(), NULL);
@ -9729,21 +9713,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;
bool has_closure_arg;
if (has_closure)
closure_arg = 1;
has_closure_arg = true;
else if (func != NULL)
closure_arg = 0;
has_closure_arg = false;
else if (is_interface_method)
closure_arg = 0;
has_closure_arg = false;
else
closure_arg = 1;
has_closure_arg = true;
int nargs;
tree* args;
if (this->args_ == NULL || this->args_->empty())
{
nargs = (is_interface_method ? 1 : 0) + closure_arg;
nargs = is_interface_method ? 1 : 0;
args = nargs == 0 ? NULL : new tree[nargs];
}
else if (fntype->parameters() == NULL || fntype->parameters()->empty())
@ -9752,7 +9736,7 @@ Call_expression::do_get_tree(Translate_context* context)
go_assert(!is_interface_method
&& fntype->is_method()
&& this->args_->size() == 1);
nargs = 1 + closure_arg;
nargs = 1;
args = new tree[nargs];
args[0] = this->args_->front()->get_tree(context);
}
@ -9763,7 +9747,6 @@ 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();
@ -9787,7 +9770,7 @@ Call_expression::do_get_tree(Translate_context* context)
return error_mark_node;
}
go_assert(pp == params->end());
go_assert(i + closure_arg == nargs);
go_assert(i == nargs);
}
tree fntype_tree = type_to_tree(fntype->get_backend(gogo));
@ -9806,21 +9789,23 @@ Call_expression::do_get_tree(Translate_context* context)
return error_mark_node;
tree fn;
tree closure_tree;
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)
if (!has_closure)
closure_tree = NULL_TREE;
else
{
go_assert(closure_arg == 1 && nargs > 0);
args[nargs - 1] = func->closure()->get_tree(context);
closure_tree = func->closure()->get_tree(context);
if (closure_tree == error_mark_node)
return error_mark_node;
}
}
else if (!is_interface_method)
{
tree closure_tree = this->fn_->get_tree(context);
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,
@ -9834,8 +9819,6 @@ Call_expression::do_get_tree(Translate_context* context)
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
{
@ -9843,7 +9826,7 @@ Call_expression::do_get_tree(Translate_context* context)
&args[0]);
if (fn == error_mark_node)
return error_mark_node;
go_assert(closure_arg == 0);
closure_tree = NULL_TREE;
}
if (fn == error_mark_node || TREE_TYPE(fn) == error_mark_node)
@ -9894,6 +9877,32 @@ Call_expression::do_get_tree(Translate_context* context)
if (func == NULL)
fn = save_expr(fn);
if (!has_closure_arg)
go_assert(closure_tree == NULL_TREE);
else
{
// Pass the closure argument by calling the function function
// __go_set_closure. In the order_evaluations pass we have
// ensured that if any parameters contain call expressions, they
// will have been moved out to temporary variables.
go_assert(closure_tree != NULL_TREE);
closure_tree = fold_convert_loc(location.gcc_location(), ptr_type_node,
closure_tree);
static tree set_closure_fndecl;
tree set_closure = Gogo::call_builtin(&set_closure_fndecl,
location,
"__go_set_closure",
1,
void_type_node,
ptr_type_node,
closure_tree);
if (set_closure == error_mark_node)
return error_mark_node;
fn = build2_loc(location.gcc_location(), COMPOUND_EXPR,
TREE_TYPE(fn), set_closure, fn);
}
tree ret = build_call_array(excess_type != NULL_TREE ? excess_type : rettype,
fn, nargs, args);
delete[] args;
@ -11609,25 +11618,25 @@ Interface_field_reference_expression::create_thunk(Gogo* gogo,
return Named_object::make_erroneous_name(Gogo::thunk_name());
Struct_field_list* sfl = new Struct_field_list();
// The type here is wrong--it should be new_fntype. But we don't
// have new_fntype yet, and it doesn't really matter.
// The type here is wrong--it should be the C function type. But it
// doesn't really matter.
Type* vt = Type::make_pointer_type(Type::make_void_type());
sfl->push_back(Struct_field(Typed_identifier("fn.0", vt, loc)));
sfl->push_back(Struct_field(Typed_identifier("val.1", type, loc)));
Type* closure_type = Type::make_struct_type(sfl, loc);
closure_type = Type::make_pointer_type(closure_type);
Function_type* new_fntype = orig_fntype->copy_with_closure(closure_type);
Function_type* new_fntype = orig_fntype->copy_with_names();
Named_object* new_no = gogo->start_function(Gogo::thunk_name(), new_fntype,
false, loc);
gogo->start_block(loc);
Variable* cvar = new Variable(closure_type, NULL, false, false, false, loc);
cvar->set_is_used();
Named_object* cp = Named_object::make_variable("$closure", NULL, cvar);
new_no->func_value()->set_closure_var(cp);
Named_object* cp = gogo->lookup("closure.0", NULL);
go_assert(cp != NULL
&& cp->is_variable()
&& cp->var_value()->is_parameter());
gogo->start_block(loc);
// Field 0 of the closure is the function code pointer, field 1 is
// the value on which to invoke the method.
@ -11647,7 +11656,7 @@ Interface_field_reference_expression::create_thunk(Gogo* gogo,
const Typed_identifier_list* new_params = new_fntype->parameters();
args = new Expression_list();
for (Typed_identifier_list::const_iterator p = new_params->begin();
p + 1 != new_params->end();
p != new_params->end();
++p)
{
Named_object* p_no = gogo->lookup(p->name(), NULL);

View file

@ -1570,14 +1570,6 @@ class Func_descriptor_expression : public Expression
public:
Func_descriptor_expression(Named_object* fn);
// Set the descriptor wrapper.
void
set_descriptor_wrapper(Named_object* dfn)
{
go_assert(this->dfn_ == NULL);
this->dfn_ = dfn;
}
// Make the function descriptor type, so that it can be converted.
static void
make_func_descriptor_type();
@ -1594,7 +1586,8 @@ class Func_descriptor_expression : public Expression
{ }
Expression*
do_copy();
do_copy()
{ return Expression::make_func_descriptor(this->fn_); }
bool
do_is_addressable() const
@ -1612,8 +1605,6 @@ class Func_descriptor_expression : public Expression
// The function for which this is the descriptor.
Named_object* fn_;
// The descriptor function.
Named_object* dfn_;
// The descriptor variable.
Bvariable* dvar_;
};

View file

@ -1289,30 +1289,6 @@ Function::get_or_make_decl(Gogo* gogo, Named_object* no, tree id)
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)
@ -1423,26 +1399,6 @@ Function_declaration::get_or_make_decl(Gogo* gogo, Named_object* no, tree id)
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;
@ -1659,8 +1615,13 @@ 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.
*pp = NULL_TREE;
DECL_ARGUMENTS(fndecl) = params;
// If we need a closure variable, fetch it by calling a runtime
// function. The caller will have called __go_set_closure before
// the function call.
if (this->closure_var_ != NULL)
{
Bvariable* bvar =
@ -1668,25 +1629,25 @@ Function::build_tree(Gogo* gogo, Named_object* 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);
go_assert(TREE_CODE(var_decl) == VAR_DECL);
static tree get_closure_fndecl;
tree get_closure = Gogo::call_builtin(&get_closure_fndecl,
this->location_,
"__go_get_closure",
0,
ptr_type_node);
// Mark the __go_get_closure function as pure, since it
// depends only on the global variable g.
DECL_PURE_P(get_closure_fndecl) = 1;
get_closure = fold_convert_loc(this->location_.gcc_location(),
TREE_TYPE(var_decl), get_closure);
DECL_INITIAL(var_decl) = get_closure;
DECL_CHAIN(var_decl) = declare_vars;
declare_vars = var_decl;
}
}
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;
if (this->block_ != NULL)
{

View file

@ -1770,8 +1770,8 @@ Create_function_descriptors::function(Named_object* no)
if (no->is_function()
&& no->func_value()->enclosing() == NULL
&& !no->func_value()->is_method()
&& !no->func_value()->is_descriptor_wrapper()
&& !Gogo::is_hidden_name(no->name()))
&& !Gogo::is_hidden_name(no->name())
&& !Gogo::is_thunk(no))
no->func_value()->descriptor(this->gogo_, no);
return TRAVERSE_CONTINUE;
@ -2541,13 +2541,38 @@ Order_eval::statement(Block* block, size_t* pindex, Statement* s)
return TRAVERSE_CONTINUE;
// If there is only one expression with a side-effect, we can
// usually leave it in place. However, for an assignment statement,
// we need to evaluate an expression on the right hand side before
// we evaluate any index expression on the left hand side, so for
// that case we always move the expression. Otherwise we mishandle
// m[0] = len(m) where m is a map.
if (c == 1 && s->classification() != Statement::STATEMENT_ASSIGNMENT)
return TRAVERSE_CONTINUE;
// usually leave it in place.
if (c == 1)
{
switch (s->classification())
{
case Statement::STATEMENT_ASSIGNMENT:
// For an assignment statement, we need to evaluate an
// expression on the right hand side before we evaluate any
// index expression on the left hand side, so for that case
// we always move the expression. Otherwise we mishandle
// m[0] = len(m) where m is a map.
break;
case Statement::STATEMENT_EXPRESSION:
{
// If this is a call statement that doesn't return any
// values, it will not have been counted as a value to
// move. We need to move any subexpressions in case they
// are themselves call statements that require passing a
// closure.
Expression* expr = s->expression_statement()->expr();
if (expr->call_expression() != NULL
&& expr->call_expression()->result_count() == 0)
break;
return TRAVERSE_CONTINUE;
}
default:
// We can leave the expression in place.
return TRAVERSE_CONTINUE;
}
}
bool is_thunk = s->thunk_statement() != NULL;
for (Find_eval_ordering::const_iterator p = find_eval_ordering.begin();
@ -2803,7 +2828,7 @@ Build_recover_thunks::function(Named_object* orig_no)
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,
true, false, location);
false, false, location);
snprintf(buf, sizeof buf, "closure.%u", count);
++count;
Named_object* new_closure_no = Named_object::make_variable(buf, NULL,
@ -3275,7 +3300,7 @@ Function::Function(Function_type* type, Function* enclosing, Block* block,
local_type_count_(0), descriptor_(NULL), fndecl_(NULL), defer_stack_(NULL),
is_sink_(false), results_are_named_(false), nointerface_(false),
calls_recover_(false), is_recover_thunk_(false), has_recover_thunk_(false),
in_unique_section_(false), is_descriptor_wrapper_(false)
in_unique_section_(false)
{
}
@ -3357,9 +3382,9 @@ Function::closure_var()
Struct_field_list* sfl = new Struct_field_list;
Type* struct_type = Type::make_struct_type(sfl, loc);
Variable* var = new Variable(Type::make_pointer_type(struct_type),
NULL, false, true, false, loc);
NULL, false, false, false, loc);
var->set_is_used();
this->closure_var_ = Named_object::make_variable("closure", NULL, var);
this->closure_var_ = Named_object::make_variable("$closure", NULL, var);
// Note that the new variable is not in any binding contour.
}
return this->closure_var_;
@ -3562,99 +3587,16 @@ 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();
Type* vt = Type::make_pointer_type(Type::make_void_type());
Function_type* new_fntype = orig_fntype->copy_with_closure(vt);
std::string name = no->name() + "$descriptorfn";
Named_object* dno = gogo->start_function(name, new_fntype, false, loc);
dno->func_value()->is_descriptor_wrapper_ = true;
// Put the wrapper in a unique section so that it can be discarded
// by the linker if it is not needed. Every top-level function will
// get a wrapper, in case there is a reference other than a call
// from some other package, but most will not need one.
dno->func_value()->set_in_unique_section();
gogo->start_block(loc);
Expression* fn = Expression::make_func_reference(no, NULL, loc);
// Call the function begin wrapped, passing all of the arguments
// except for the last one (the last argument is the ignored
// closure).
const Typed_identifier_list* orig_params = orig_fntype->parameters();
Expression_list* args;
if (orig_params == NULL || orig_params->empty())
args = NULL;
else
{
const Typed_identifier_list* new_params = new_fntype->parameters();
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 = Statement::make_return_from_call(call, loc);
gogo->add_statement(s);
Block* b = gogo->finish_block(loc);
gogo->add_block(b, loc);
gogo->lower_block(dno, b);
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)
Function::descriptor(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)
{
// Make and record the descriptor first, so that when we lower
// the descriptor wrapper we don't try to make it again.
Func_descriptor_expression* descriptor =
Expression::make_func_descriptor(no);
this->descriptor_ = descriptor;
if (no->package() == NULL
&& !Linemap::is_predeclared_location(no->location()))
{
Named_object* dno = Function::make_descriptor_wrapper(gogo, no,
this->type_);
descriptor->set_descriptor_wrapper(dno);
}
}
this->descriptor_ = Expression::make_func_descriptor(no);
return this->descriptor_;
}
@ -4193,24 +4135,11 @@ Bindings_snapshot::check_goto_defs(Location loc, const Block* block,
// Return the function descriptor.
Expression*
Function_declaration::descriptor(Gogo* gogo, Named_object* no)
Function_declaration::descriptor(Gogo*, Named_object* no)
{
go_assert(!this->fntype_->is_method());
if (this->descriptor_ == NULL)
{
// Make and record the descriptor first, so that when we lower
// the descriptor wrapper we don't try to make it again.
Func_descriptor_expression* descriptor =
Expression::make_func_descriptor(no);
this->descriptor_ = descriptor;
if (no->package() == NULL
&& !Linemap::is_predeclared_location(no->location()))
{
Named_object* dno = Function::make_descriptor_wrapper(gogo, no,
this->fntype_);
descriptor->set_descriptor_wrapper(dno);
}
}
this->descriptor_ = Expression::make_func_descriptor(no);
return this->descriptor_;
}

View file

@ -1050,12 +1050,6 @@ 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
@ -1085,10 +1079,6 @@ class Function
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);
@ -1190,9 +1180,6 @@ 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.

View file

@ -1658,46 +1658,23 @@ Statement::make_tuple_type_guard_assignment(Expression* val, Expression* ok,
location);
}
// An expression statement.
// Class Expression_statement.
class Expression_statement : public Statement
// Constructor.
Expression_statement::Expression_statement(Expression* expr, bool is_ignored)
: Statement(STATEMENT_EXPRESSION, expr->location()),
expr_(expr), is_ignored_(is_ignored)
{
public:
Expression_statement(Expression* expr, bool is_ignored)
: Statement(STATEMENT_EXPRESSION, expr->location()),
expr_(expr), is_ignored_(is_ignored)
{ }
}
Expression*
expr()
{ return this->expr_; }
// Determine types.
protected:
int
do_traverse(Traverse* traverse)
{ return this->traverse_expression(traverse, &this->expr_); }
void
do_determine_types()
{ this->expr_->determine_type_no_context(); }
void
do_check_types(Gogo*);
bool
do_may_fall_through() const;
Bstatement*
do_get_backend(Translate_context* context);
void
do_dump_statement(Ast_dump_context*) const;
private:
Expression* expr_;
// Whether the value of this expression is being explicitly ignored.
bool is_ignored_;
};
void
Expression_statement::do_determine_types()
{
this->expr_->determine_type_no_context();
}
// Check the types of an expression statement. The only check we do
// is to possibly give an error about discarding the value of the

View file

@ -17,6 +17,7 @@ class Function;
class Unnamed_label;
class Temporary_statement;
class Variable_declaration_statement;
class Expression_statement;
class Return_statement;
class Thunk_statement;
class Label_statement;
@ -329,6 +330,14 @@ class Statement
STATEMENT_VARIABLE_DECLARATION>();
}
// If this is an expression statement, return it. Otherwise return
// NULL.
Expression_statement*
expression_statement()
{
return this->convert<Expression_statement, STATEMENT_EXPRESSION>();
}
// If this is a return statement, return it. Otherwise return NULL.
Return_statement*
return_statement()
@ -636,6 +645,43 @@ class Return_statement : public Statement
bool is_lowered_;
};
// An expression statement.
class Expression_statement : public Statement
{
public:
Expression_statement(Expression* expr, bool is_ignored);
Expression*
expr()
{ return this->expr_; }
protected:
int
do_traverse(Traverse* traverse)
{ return this->traverse_expression(traverse, &this->expr_); }
void
do_determine_types();
void
do_check_types(Gogo*);
bool
do_may_fall_through() const;
Bstatement*
do_get_backend(Translate_context* context);
void
do_dump_statement(Ast_dump_context*) const;
private:
Expression* expr_;
// Whether the value of this expression is being explicitly ignored.
bool is_ignored_;
};
// A send statement.
class Send_statement : public Statement

View file

@ -3390,10 +3390,7 @@ 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.
// function.
Location loc = this->location();
Btype* struct_type =
@ -3415,15 +3412,9 @@ Function_type::do_get_backend(Gogo* gogo)
}
std::vector<Backend::Btyped_identifier> bparameters;
size_t last;
if (this->parameters_ == NULL)
if (this->parameters_ != NULL)
{
bparameters.resize(1);
last = 0;
}
else
{
bparameters.resize(this->parameters_->size() + 1);
bparameters.resize(this->parameters_->size());
size_t i = 0;
for (Typed_identifier_list::const_iterator p = this->parameters_->begin();
p != this->parameters_->end();
@ -3433,12 +3424,8 @@ Function_type::do_get_backend(Gogo* gogo)
bparameters[i].btype = p->type()->get_backend(gogo);
bparameters[i].location = p->location();
}
last = i;
go_assert(i == bparameters.size());
}
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)
@ -3840,7 +3827,7 @@ Function_type::copy_with_receiver(Type* receiver_type) const
// closure parameter.
Function_type*
Function_type::copy_with_closure(Type* closure_type) const
Function_type::copy_with_names() const
{
Typed_identifier_list* new_params = new Typed_identifier_list();
const Typed_identifier_list* orig_params = this->parameters_;
@ -3858,8 +3845,6 @@ Function_type::copy_with_closure(Type* closure_type) const
p->location()));
}
}
new_params->push_back(Typed_identifier("closure.0", closure_type,
this->location_));
const Typed_identifier_list* orig_results = this->results_;
Typed_identifier_list* new_results;

View file

@ -1789,11 +1789,11 @@ class Function_type : public Type
Function_type*
copy_with_receiver(Type*) const;
// Return a copy of this type ignoring any receiver and adding a
// final closure parameter of type CLOSURE_TYPE. This is used when
// creating descriptors.
// Return a copy of this type ignoring any receiver and using dummy
// names for all parameters. This is used for thunks for method
// values.
Function_type*
copy_with_closure(Type* closure_type) const;
copy_with_names() const;
static Type*
make_function_type_descriptor_type();

View file

@ -434,9 +434,6 @@ func (v Value) call(op string, in []Value) []Value {
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 {
@ -464,10 +461,6 @@ func (v Value) call(op string, in []Value) []Value {
}
off++
}
if v.flag&flagMethod == 0 && !firstPointer {
// Closure argument.
params[off] = unsafe.Pointer(&fn)
}
ret := make([]Value, nout)
results := make([]unsafe.Pointer, nout)

View file

@ -302,9 +302,7 @@ 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)
+ (!is_interface && !is_method ? 1 : 0));
num_args = num_params + (is_interface ? 1 : 0);
args = (ffi_type **) __go_alloc (num_args * sizeof (ffi_type *));
i = 0;
off = 0;
@ -321,12 +319,6 @@ 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);
@ -511,9 +503,8 @@ go_set_results (const struct __go_func_type *func, unsigned char *call_result,
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. */
function indirectly, and we must pass a closure pointer via
__go_set_closure. The pointer to pass is simply FUNC_VAL. */
void
reflect_call (const struct __go_func_type *func_type, FuncVal *func_val,
@ -528,6 +519,8 @@ reflect_call (const struct __go_func_type *func_type, FuncVal *func_val,
call_result = (unsigned char *) malloc (go_results_size (func_type));
if (!is_interface && !is_method)
__go_set_closure (func_val);
ffi_call (&cif, func_val->fn, call_result, params);
/* Some day we may need to free result values if RESULTS is

View file

@ -2263,12 +2263,11 @@ runfinq(void* dummy __attribute__ ((unused)))
for(; fb; fb=next) {
next = fb->next;
for(i=0; i<(uint32)fb->cnt; i++) {
void *params[2];
void *param;
f = &fb->fin[i];
params[0] = &f->arg;
params[1] = f;
reflect_call(f->ft, f->fn, 0, 0, params, nil);
param = &f->arg;
reflect_call(f->ft, f->fn, 0, 0, &param, nil);
f->fn = nil;
f->arg = nil;
}

View file

@ -2832,3 +2832,23 @@ runtime_proc_scan(void (*addroot)(Obj))
{
addroot((Obj){(byte*)&runtime_sched, sizeof runtime_sched, 0});
}
// When a function calls a closure, it passes the closure value to
// __go_set_closure immediately before the function call. When a
// function uses a closure, it calls __go_get_closure immediately on
// function entry. This is a hack, but it will work on any system.
// It would be better to use the static chain register when there is
// one. It is also worth considering expanding these functions
// directly in the compiler.
void
__go_set_closure(void* v)
{
g->closure = v;
}
void *
__go_get_closure(void)
{
return g->closure;
}

View file

@ -190,6 +190,7 @@ struct Location
struct G
{
void* closure; // Closure value.
Defer* defer;
Panic* panic;
void* exception; // current exception being thrown
@ -759,3 +760,6 @@ extern void runtime_main(void*);
int32 getproccount(void);
#define PREFETCH(p) __builtin_prefetch(p)
void __go_set_closure(void*);
void* __go_get_closure(void);

View file

@ -46,10 +46,9 @@ static void siftdown(int32);
// Ready the goroutine e.data.
static void
ready(int64 now, Eface e, void *closure)
ready(int64 now, Eface e)
{
USED(now);
USED(closure);
runtime_ready(e.__object);
}
@ -166,7 +165,7 @@ timerproc(void* dummy __attribute__ ((unused)))
{
int64 delta, now;
Timer *t;
void (*f)(int64, Eface, void *);
void (*f)(int64, Eface);
Eface arg;
for(;;) {
@ -197,7 +196,8 @@ timerproc(void* dummy __attribute__ ((unused)))
runtime_unlock(&timers);
if(raceenabled)
runtime_raceacquire(t);
f(now, arg, &t->fv);
__go_set_closure(t->fv);
f(now, arg);
runtime_lock(&timers);
}
if(delta < 0) {