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:
parent
3b18bc426a
commit
05a7d56678
15 changed files with 237 additions and 343 deletions
|
@ -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);
|
||||
|
|
|
@ -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_;
|
||||
};
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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_;
|
||||
}
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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, ¶m, nil);
|
||||
f->fn = nil;
|
||||
f->arg = nil;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
Loading…
Add table
Reference in a new issue