mirror of
git://gcc.gnu.org/git/gcc.git
synced 2025-03-31 12:51:15 +08:00
compiler/runtime: Copy channel implementation from master library.
From-SVN: r181874
This commit is contained in:
parent
04f7a48edd
commit
3e68d6d75a
@ -13493,12 +13493,18 @@ Receive_expression::do_check_types(Gogo*)
|
||||
tree
|
||||
Receive_expression::do_get_tree(Translate_context* context)
|
||||
{
|
||||
Location loc = this->location();
|
||||
|
||||
Channel_type* channel_type = this->channel_->type()->channel_type();
|
||||
if (channel_type == NULL)
|
||||
{
|
||||
go_assert(this->channel_->type()->is_error());
|
||||
return error_mark_node;
|
||||
}
|
||||
|
||||
Expression* td = Expression::make_type_descriptor(channel_type, loc);
|
||||
tree td_tree = td->get_tree(context);
|
||||
|
||||
Type* element_type = channel_type->element_type();
|
||||
Btype* element_type_btype = element_type->get_backend(context->gogo());
|
||||
tree element_type_tree = type_to_tree(element_type_btype);
|
||||
@ -13507,8 +13513,7 @@ Receive_expression::do_get_tree(Translate_context* context)
|
||||
if (element_type_tree == error_mark_node || channel == error_mark_node)
|
||||
return error_mark_node;
|
||||
|
||||
return Gogo::receive_from_channel(element_type_tree, channel,
|
||||
this->for_select_, this->location());
|
||||
return Gogo::receive_from_channel(element_type_tree, td_tree, channel, loc);
|
||||
}
|
||||
|
||||
// Dump ast representation for a receive expression.
|
||||
|
@ -1947,7 +1947,7 @@ class Receive_expression : public Expression
|
||||
public:
|
||||
Receive_expression(Expression* channel, Location location)
|
||||
: Expression(EXPRESSION_RECEIVE, location),
|
||||
channel_(channel), for_select_(false)
|
||||
channel_(channel)
|
||||
{ }
|
||||
|
||||
// Return the channel.
|
||||
@ -1955,11 +1955,6 @@ class Receive_expression : public Expression
|
||||
channel()
|
||||
{ return this->channel_; }
|
||||
|
||||
// Note that this is for a select statement.
|
||||
void
|
||||
set_for_select()
|
||||
{ this->for_select_ = true; }
|
||||
|
||||
protected:
|
||||
int
|
||||
do_traverse(Traverse* traverse)
|
||||
@ -1998,8 +1993,6 @@ class Receive_expression : public Expression
|
||||
private:
|
||||
// The channel from which we are receiving.
|
||||
Expression* channel_;
|
||||
// Whether this is for a select statement.
|
||||
bool for_select_;
|
||||
};
|
||||
|
||||
#endif // !defined(GO_EXPRESSIONS_H)
|
||||
|
@ -2201,13 +2201,12 @@ Gogo::runtime_error(int code, Location location)
|
||||
}
|
||||
|
||||
// Return a tree for receiving a value of type TYPE_TREE on CHANNEL.
|
||||
// This does a blocking receive and returns the value read from the
|
||||
// channel. If FOR_SELECT is true, this is being done because it was
|
||||
// chosen in a select statement.
|
||||
// TYPE_DESCRIPTOR_TREE is the channel's type descriptor. This does a
|
||||
// blocking receive and returns the value read from the channel.
|
||||
|
||||
tree
|
||||
Gogo::receive_from_channel(tree type_tree, tree channel, bool for_select,
|
||||
Location location)
|
||||
Gogo::receive_from_channel(tree type_tree, tree type_descriptor_tree,
|
||||
tree channel, Location location)
|
||||
{
|
||||
if (type_tree == error_mark_node || channel == error_mark_node)
|
||||
return error_mark_node;
|
||||
@ -2222,12 +2221,10 @@ Gogo::receive_from_channel(tree type_tree, tree channel, bool for_select,
|
||||
"__go_receive_small",
|
||||
2,
|
||||
uint64_type_node,
|
||||
TREE_TYPE(type_descriptor_tree),
|
||||
type_descriptor_tree,
|
||||
ptr_type_node,
|
||||
channel,
|
||||
boolean_type_node,
|
||||
(for_select
|
||||
? boolean_true_node
|
||||
: boolean_false_node));
|
||||
channel);
|
||||
if (call == error_mark_node)
|
||||
return error_mark_node;
|
||||
// This can panic if there are too many operations on a closed
|
||||
@ -2253,15 +2250,13 @@ Gogo::receive_from_channel(tree type_tree, tree channel, bool for_select,
|
||||
location,
|
||||
"__go_receive_big",
|
||||
3,
|
||||
boolean_type_node,
|
||||
void_type_node,
|
||||
TREE_TYPE(type_descriptor_tree),
|
||||
type_descriptor_tree,
|
||||
ptr_type_node,
|
||||
channel,
|
||||
ptr_type_node,
|
||||
tmpaddr,
|
||||
boolean_type_node,
|
||||
(for_select
|
||||
? boolean_true_node
|
||||
: boolean_false_node));
|
||||
tmpaddr);
|
||||
if (call == error_mark_node)
|
||||
return error_mark_node;
|
||||
// This can panic if there are too many operations on a closed
|
||||
|
@ -527,14 +527,9 @@ class Gogo
|
||||
|
||||
// Receive a value from a channel.
|
||||
static tree
|
||||
receive_from_channel(tree type_tree, tree channel, bool for_select,
|
||||
receive_from_channel(tree type_tree, tree type_descriptor_tree, tree channel,
|
||||
Location);
|
||||
|
||||
// Return a tree for receiving an integer on a channel.
|
||||
static tree
|
||||
receive_as_64bit_integer(tree type, tree channel, bool blocking,
|
||||
bool for_select);
|
||||
|
||||
// Make a trampoline which calls FNADDR passing CLOSURE.
|
||||
tree
|
||||
make_trampoline(tree fnaddr, tree closure, Location);
|
||||
|
@ -1780,7 +1780,6 @@ Parse::init_vars_from_receive(const Typed_identifier_list* vars, Type* type,
|
||||
Statement* s = Statement::make_tuple_receive_assignment(val_var,
|
||||
received_var,
|
||||
receive->channel(),
|
||||
false,
|
||||
location);
|
||||
|
||||
if (!this->gogo_->in_global_scope())
|
||||
@ -3769,7 +3768,6 @@ Parse::tuple_assignment(Expression_list* lhs, Range_clause* p_range_clause)
|
||||
Expression* channel = receive->channel();
|
||||
Statement* s = Statement::make_tuple_receive_assignment(val, success,
|
||||
channel,
|
||||
false,
|
||||
location);
|
||||
this->gogo_->add_statement(s);
|
||||
}
|
||||
|
@ -54,8 +54,6 @@ enum Runtime_function_type
|
||||
RFT_MAPITER,
|
||||
// Go type chan any, C type struct __go_channel *.
|
||||
RFT_CHAN,
|
||||
// Go type *chan any, C type struct __go_channel **.
|
||||
RFT_CHANPTR,
|
||||
// Go type non-empty interface, C type struct __go_interface.
|
||||
RFT_IFACE,
|
||||
// Go type interface{}, C type struct __go_empty_interface.
|
||||
@ -148,10 +146,6 @@ runtime_function_type(Runtime_function_type bft)
|
||||
t = Type::make_channel_type(true, true, Type::make_void_type());
|
||||
break;
|
||||
|
||||
case RFT_CHANPTR:
|
||||
t = Type::make_pointer_type(runtime_function_type(RFT_CHAN));
|
||||
break;
|
||||
|
||||
case RFT_IFACE:
|
||||
{
|
||||
Typed_identifier_list* methods = new Typed_identifier_list();
|
||||
@ -223,7 +217,6 @@ convert_to_runtime_function_type(Runtime_function_type bft, Expression* e,
|
||||
case RFT_SLICE:
|
||||
case RFT_MAP:
|
||||
case RFT_CHAN:
|
||||
case RFT_CHANPTR:
|
||||
case RFT_IFACE:
|
||||
case RFT_EFACE:
|
||||
return Expression::make_unsafe_cast(runtime_function_type(bft), e, loc);
|
||||
@ -393,12 +386,3 @@ Runtime::map_iteration_type()
|
||||
|
||||
return Type::make_array_type(runtime_function_type(RFT_POINTER), iexpr);
|
||||
}
|
||||
|
||||
// Return the type used to pass a list of general channels to the
|
||||
// select runtime function.
|
||||
|
||||
Type*
|
||||
Runtime::chanptr_type()
|
||||
{
|
||||
return runtime_function_type(RFT_CHANPTR);
|
||||
}
|
||||
|
@ -121,31 +121,44 @@ DEF_GO_RUNTIME(CHAN_LEN, "__go_chan_len", P1(CHAN), R1(INT))
|
||||
DEF_GO_RUNTIME(CHAN_CAP, "__go_chan_cap", P1(CHAN), R1(INT))
|
||||
|
||||
// Send a small value on a channel.
|
||||
DEF_GO_RUNTIME(SEND_SMALL, "__go_send_small", P3(CHAN, UINT64, BOOL), R0())
|
||||
|
||||
// Send a small value on a channel without blocking.
|
||||
DEF_GO_RUNTIME(SEND_NONBLOCKING_SMALL, "__go_send_nonblocking_small",
|
||||
P2(CHAN, UINT64), R1(BOOL))
|
||||
DEF_GO_RUNTIME(SEND_SMALL, "__go_send_small", P3(TYPE, CHAN, UINT64), R0())
|
||||
|
||||
// Send a big value on a channel.
|
||||
DEF_GO_RUNTIME(SEND_BIG, "__go_send_big", P3(CHAN, POINTER, BOOL), R0())
|
||||
|
||||
// Send a big value on a channel without blocking.
|
||||
DEF_GO_RUNTIME(SEND_NONBLOCKING_BIG, "__go_send_nonblocking_big",
|
||||
P2(CHAN, POINTER), R1(BOOL))
|
||||
DEF_GO_RUNTIME(SEND_BIG, "__go_send_big", P3(TYPE, CHAN, POINTER), R0())
|
||||
|
||||
// Receive a small value from a channel.
|
||||
DEF_GO_RUNTIME(RECEIVE_SMALL, "__go_receive_small", P2(CHAN, BOOL), R1(UINT64))
|
||||
DEF_GO_RUNTIME(RECEIVE_SMALL, "__go_receive_small", P2(TYPE, CHAN), R1(UINT64))
|
||||
|
||||
// Receive a big value from a channel.
|
||||
DEF_GO_RUNTIME(RECEIVE_BIG, "__go_receive_big", P3(CHAN, POINTER, BOOL),
|
||||
R1(BOOL))
|
||||
DEF_GO_RUNTIME(RECEIVE_BIG, "__go_receive_big", P3(TYPE, CHAN, POINTER), R0())
|
||||
|
||||
// Receive a value from a channel returning whether it is closed.
|
||||
DEF_GO_RUNTIME(CHANRECV2, "runtime.chanrecv2", P2(CHAN, POINTER), R1(BOOL))
|
||||
DEF_GO_RUNTIME(CHANRECV2, "runtime.chanrecv2", P3(TYPE, CHAN, POINTER),
|
||||
R1(BOOL))
|
||||
|
||||
// Receive a value from a channel returning whether it is closed, for select.
|
||||
DEF_GO_RUNTIME(CHANRECV3, "runtime.chanrecv3", P2(CHAN, POINTER), R1(BOOL))
|
||||
|
||||
// Start building a select statement.
|
||||
DEF_GO_RUNTIME(NEWSELECT, "runtime.newselect", P1(INT), R1(POINTER))
|
||||
|
||||
// Add a default clause to a select statement.
|
||||
DEF_GO_RUNTIME(SELECTDEFAULT, "runtime.selectdefault", P2(POINTER, INT), R0())
|
||||
|
||||
// Add a send clause to a select statement.
|
||||
DEF_GO_RUNTIME(SELECTSEND, "runtime.selectsend",
|
||||
P4(POINTER, CHAN, POINTER, INT), R0())
|
||||
|
||||
// Add a receive clause to a select statement, for a clause which does
|
||||
// not check whether the channel is closed.
|
||||
DEF_GO_RUNTIME(SELECTRECV, "runtime.selectrecv",
|
||||
P4(POINTER, CHAN, POINTER, INT), R0())
|
||||
|
||||
// Add a receive clause to a select statement, for a clause which does
|
||||
// check whether the channel is closed.
|
||||
DEF_GO_RUNTIME(SELECTRECV2, "runtime.selectrecv2",
|
||||
P5(POINTER, CHAN, POINTER, BOOLPTR, INT), R0())
|
||||
|
||||
// Run a select, returning the index of the selected clause.
|
||||
DEF_GO_RUNTIME(SELECTGO, "runtime.selectgo", P1(POINTER), R1(INT))
|
||||
|
||||
|
||||
// Panic.
|
||||
@ -213,11 +226,6 @@ DEF_GO_RUNTIME(GO, "__go_go", P2(FUNC_PTR, POINTER), R0())
|
||||
DEF_GO_RUNTIME(DEFER, "__go_defer", P3(BOOLPTR, FUNC_PTR, POINTER), R0())
|
||||
|
||||
|
||||
// Run a select statement.
|
||||
DEF_GO_RUNTIME(SELECT, "__go_select", P4(UINTPTR, BOOL, CHANPTR, BOOLPTR),
|
||||
R1(UINTPTR))
|
||||
|
||||
|
||||
// Convert an empty interface to an empty interface, returning ok.
|
||||
DEF_GO_RUNTIME(IFACEE2E2, "runtime.ifaceE2E2", P1(EFACE), R2(EFACE, BOOL))
|
||||
|
||||
|
@ -43,11 +43,6 @@ class Runtime
|
||||
static Type*
|
||||
map_iteration_type();
|
||||
|
||||
// Return the type used to pass a list of general channels to the
|
||||
// select runtime function.
|
||||
static Type*
|
||||
chanptr_type();
|
||||
|
||||
private:
|
||||
static Named_object*
|
||||
runtime_declaration(Function);
|
||||
|
@ -1329,10 +1329,9 @@ class Tuple_receive_assignment_statement : public Statement
|
||||
{
|
||||
public:
|
||||
Tuple_receive_assignment_statement(Expression* val, Expression* closed,
|
||||
Expression* channel, bool for_select,
|
||||
Location location)
|
||||
Expression* channel, Location location)
|
||||
: Statement(STATEMENT_TUPLE_RECEIVE_ASSIGNMENT, location),
|
||||
val_(val), closed_(closed), channel_(channel), for_select_(for_select)
|
||||
val_(val), closed_(closed), channel_(channel)
|
||||
{ }
|
||||
|
||||
protected:
|
||||
@ -1360,8 +1359,6 @@ class Tuple_receive_assignment_statement : public Statement
|
||||
Expression* closed_;
|
||||
// The channel on which we receive the value.
|
||||
Expression* channel_;
|
||||
// Whether this is for a select statement.
|
||||
bool for_select_;
|
||||
};
|
||||
|
||||
// Traversal.
|
||||
@ -1414,14 +1411,14 @@ Tuple_receive_assignment_statement::do_lower(Gogo*, Named_object*,
|
||||
Statement::make_temporary(Type::lookup_bool_type(), NULL, loc);
|
||||
b->add_statement(closed_temp);
|
||||
|
||||
// closed_temp = chanrecv[23](channel, &val_temp)
|
||||
// closed_temp = chanrecv2(type, channel, &val_temp)
|
||||
Expression* td = Expression::make_type_descriptor(this->channel_->type(),
|
||||
loc);
|
||||
Temporary_reference_expression* ref =
|
||||
Expression::make_temporary_reference(val_temp, loc);
|
||||
Expression* p2 = Expression::make_unary(OPERATOR_AND, ref, loc);
|
||||
Expression* call = Runtime::make_call((this->for_select_
|
||||
? Runtime::CHANRECV3
|
||||
: Runtime::CHANRECV2),
|
||||
loc, 2, this->channel_, p2);
|
||||
Expression* call = Runtime::make_call(Runtime::CHANRECV2,
|
||||
loc, 3, td, this->channel_, p2);
|
||||
ref = Expression::make_temporary_reference(closed_temp, loc);
|
||||
ref->set_is_lvalue();
|
||||
Statement* s = Statement::make_assignment(ref, call, loc);
|
||||
@ -1460,11 +1457,10 @@ Tuple_receive_assignment_statement::do_dump_statement(
|
||||
Statement*
|
||||
Statement::make_tuple_receive_assignment(Expression* val, Expression* closed,
|
||||
Expression* channel,
|
||||
bool for_select,
|
||||
Location location)
|
||||
{
|
||||
return new Tuple_receive_assignment_statement(val, closed, channel,
|
||||
for_select, location);
|
||||
location);
|
||||
}
|
||||
|
||||
// An assignment to a pair of values from a type guard. This is a
|
||||
@ -4391,9 +4387,11 @@ Send_statement::do_get_backend(Translate_context* context)
|
||||
&& val->temporary_reference_expression() == NULL)
|
||||
can_take_address = false;
|
||||
|
||||
Expression* td = Expression::make_type_descriptor(this->channel_->type(),
|
||||
loc);
|
||||
|
||||
Runtime::Function code;
|
||||
Bstatement* btemp = NULL;
|
||||
Expression* call;
|
||||
if (is_small)
|
||||
{
|
||||
// Type is small enough to handle as uint64.
|
||||
@ -4421,8 +4419,7 @@ Send_statement::do_get_backend(Translate_context* context)
|
||||
btemp = temp->get_backend(context);
|
||||
}
|
||||
|
||||
call = Runtime::make_call(code, loc, 3, this->channel_, val,
|
||||
Expression::make_boolean(this->for_select_, loc));
|
||||
Expression* call = Runtime::make_call(code, loc, 3, td, this->channel_, val);
|
||||
|
||||
context->gogo()->lower_expression(context->function(), NULL, &call);
|
||||
Bexpression* bcall = tree_to_expr(call->get_tree(context));
|
||||
@ -4490,128 +4487,42 @@ Select_clauses::Select_clause::traverse(Traverse* traverse)
|
||||
return TRAVERSE_CONTINUE;
|
||||
}
|
||||
|
||||
// Lowering. Here we pull out the channel and the send values, to
|
||||
// enforce the order of evaluation. We also add explicit send and
|
||||
// receive statements to the clauses.
|
||||
// Lowering. We call a function to register this clause, and arrange
|
||||
// to set any variables in any receive clause.
|
||||
|
||||
void
|
||||
Select_clauses::Select_clause::lower(Gogo* gogo, Named_object* function,
|
||||
Block* b)
|
||||
Block* b, Temporary_statement* sel)
|
||||
{
|
||||
Location loc = this->location_;
|
||||
|
||||
Expression* selref = Expression::make_temporary_reference(sel, loc);
|
||||
|
||||
mpz_t ival;
|
||||
mpz_init_set_ui(ival, this->index_);
|
||||
Expression* index_expr = Expression::make_integer(&ival, NULL, loc);
|
||||
mpz_clear(ival);
|
||||
|
||||
if (this->is_default_)
|
||||
{
|
||||
go_assert(this->channel_ == NULL && this->val_ == NULL);
|
||||
this->lower_default(b, selref, index_expr);
|
||||
this->is_lowered_ = true;
|
||||
return;
|
||||
}
|
||||
|
||||
Location loc = this->location_;
|
||||
|
||||
// Evaluate the channel before the select statement.
|
||||
Temporary_statement* channel_temp = Statement::make_temporary(NULL,
|
||||
this->channel_,
|
||||
loc);
|
||||
b->add_statement(channel_temp);
|
||||
this->channel_ = Expression::make_temporary_reference(channel_temp, loc);
|
||||
Expression* chanref = Expression::make_temporary_reference(channel_temp,
|
||||
loc);
|
||||
|
||||
// If this is a send clause, evaluate the value to send before the
|
||||
// select statement.
|
||||
Temporary_statement* val_temp = NULL;
|
||||
if (this->is_send_ && !this->val_->is_constant())
|
||||
{
|
||||
val_temp = Statement::make_temporary(NULL, this->val_, loc);
|
||||
b->add_statement(val_temp);
|
||||
}
|
||||
|
||||
// Add the send or receive before the rest of the statements if any.
|
||||
Block *init = new Block(b, loc);
|
||||
Expression* ref = Expression::make_temporary_reference(channel_temp, loc);
|
||||
if (this->is_send_)
|
||||
{
|
||||
Expression* ref2;
|
||||
if (val_temp == NULL)
|
||||
ref2 = this->val_;
|
||||
else
|
||||
ref2 = Expression::make_temporary_reference(val_temp, loc);
|
||||
Send_statement* send = Statement::make_send_statement(ref, ref2, loc);
|
||||
send->set_for_select();
|
||||
init->add_statement(send);
|
||||
}
|
||||
else if (this->closed_ != NULL && !this->closed_->is_sink_expression())
|
||||
{
|
||||
go_assert(this->var_ == NULL && this->closedvar_ == NULL);
|
||||
if (this->val_ == NULL)
|
||||
this->val_ = Expression::make_sink(loc);
|
||||
Statement* s = Statement::make_tuple_receive_assignment(this->val_,
|
||||
this->closed_,
|
||||
ref, true, loc);
|
||||
init->add_statement(s);
|
||||
}
|
||||
else if (this->closedvar_ != NULL)
|
||||
{
|
||||
go_assert(this->val_ == NULL);
|
||||
Expression* val;
|
||||
if (this->var_ == NULL)
|
||||
val = Expression::make_sink(loc);
|
||||
else
|
||||
val = Expression::make_var_reference(this->var_, loc);
|
||||
Expression* closed = Expression::make_var_reference(this->closedvar_,
|
||||
loc);
|
||||
Statement* s = Statement::make_tuple_receive_assignment(val, closed, ref,
|
||||
true, loc);
|
||||
|
||||
// We have to put S in STATEMENTS_, because that is where the
|
||||
// variables are declared.
|
||||
|
||||
go_assert(this->statements_ != NULL);
|
||||
|
||||
// Skip the variable declaration statements themselves.
|
||||
size_t skip = 1;
|
||||
if (this->var_ != NULL)
|
||||
skip = 2;
|
||||
|
||||
// Verify that we are only skipping variable declarations.
|
||||
size_t i = 0;
|
||||
for (Block::iterator p = this->statements_->begin();
|
||||
i < skip && p != this->statements_->end();
|
||||
++p, ++i)
|
||||
go_assert((*p)->variable_declaration_statement() != NULL);
|
||||
|
||||
this->statements_->insert_statement_before(skip, s);
|
||||
|
||||
// We have to lower STATEMENTS_ again, to lower the tuple
|
||||
// receive assignment we just added.
|
||||
gogo->lower_block(function, this->statements_);
|
||||
}
|
||||
this->lower_send(b, selref, chanref, index_expr);
|
||||
else
|
||||
{
|
||||
Receive_expression* recv = Expression::make_receive(ref, loc);
|
||||
recv->set_for_select();
|
||||
if (this->val_ != NULL)
|
||||
{
|
||||
go_assert(this->var_ == NULL);
|
||||
init->add_statement(Statement::make_assignment(this->val_, recv,
|
||||
loc));
|
||||
}
|
||||
else if (this->var_ != NULL)
|
||||
{
|
||||
this->var_->var_value()->set_init(recv);
|
||||
this->var_->var_value()->clear_type_from_chan_element();
|
||||
}
|
||||
else
|
||||
{
|
||||
init->add_statement(Statement::make_statement(recv, true));
|
||||
}
|
||||
}
|
||||
|
||||
// Lower any statements we just created.
|
||||
gogo->lower_block(function, init);
|
||||
|
||||
if (this->statements_ != NULL)
|
||||
init->add_statement(Statement::make_block_statement(this->statements_,
|
||||
loc));
|
||||
|
||||
this->statements_ = init;
|
||||
this->lower_recv(gogo, function, b, selref, chanref, index_expr);
|
||||
|
||||
// Now all references should be handled through the statements, not
|
||||
// through here.
|
||||
@ -4620,6 +4531,136 @@ Select_clauses::Select_clause::lower(Gogo* gogo, Named_object* function,
|
||||
this->var_ = NULL;
|
||||
}
|
||||
|
||||
// Lower a default clause in a select statement.
|
||||
|
||||
void
|
||||
Select_clauses::Select_clause::lower_default(Block* b, Expression* selref,
|
||||
Expression* index_expr)
|
||||
{
|
||||
Location loc = this->location_;
|
||||
Expression* call = Runtime::make_call(Runtime::SELECTDEFAULT, loc, 2, selref,
|
||||
index_expr);
|
||||
b->add_statement(Statement::make_statement(call, true));
|
||||
}
|
||||
|
||||
// Lower a send clause in a select statement.
|
||||
|
||||
void
|
||||
Select_clauses::Select_clause::lower_send(Block* b, Expression* selref,
|
||||
Expression* chanref,
|
||||
Expression* index_expr)
|
||||
{
|
||||
Location loc = this->location_;
|
||||
|
||||
Channel_type* ct = this->channel_->type()->channel_type();
|
||||
if (ct == NULL)
|
||||
return;
|
||||
|
||||
Type* valtype = ct->element_type();
|
||||
|
||||
// Note that copying the value to a temporary here means that we
|
||||
// evaluate the send values in the required order.
|
||||
Temporary_statement* val = Statement::make_temporary(valtype, this->val_,
|
||||
loc);
|
||||
b->add_statement(val);
|
||||
|
||||
Expression* valref = Expression::make_temporary_reference(val, loc);
|
||||
Expression* valaddr = Expression::make_unary(OPERATOR_AND, valref, loc);
|
||||
|
||||
Expression* call = Runtime::make_call(Runtime::SELECTSEND, loc, 4, selref,
|
||||
chanref, valaddr, index_expr);
|
||||
b->add_statement(Statement::make_statement(call, true));
|
||||
}
|
||||
|
||||
// Lower a receive clause in a select statement.
|
||||
|
||||
void
|
||||
Select_clauses::Select_clause::lower_recv(Gogo* gogo, Named_object* function,
|
||||
Block* b, Expression* selref,
|
||||
Expression* chanref,
|
||||
Expression* index_expr)
|
||||
{
|
||||
Location loc = this->location_;
|
||||
|
||||
Channel_type* ct = this->channel_->type()->channel_type();
|
||||
if (ct == NULL)
|
||||
return;
|
||||
|
||||
Type* valtype = ct->element_type();
|
||||
Temporary_statement* val = Statement::make_temporary(valtype, NULL, loc);
|
||||
b->add_statement(val);
|
||||
|
||||
Expression* valref = Expression::make_temporary_reference(val, loc);
|
||||
Expression* valaddr = Expression::make_unary(OPERATOR_AND, valref, loc);
|
||||
|
||||
Temporary_statement* closed_temp = NULL;
|
||||
|
||||
Expression* call;
|
||||
if (this->closed_ == NULL && this->closedvar_ == NULL)
|
||||
call = Runtime::make_call(Runtime::SELECTRECV, loc, 4, selref, chanref,
|
||||
valaddr, index_expr);
|
||||
else
|
||||
{
|
||||
closed_temp = Statement::make_temporary(Type::lookup_bool_type(), NULL,
|
||||
loc);
|
||||
b->add_statement(closed_temp);
|
||||
Expression* cref = Expression::make_temporary_reference(closed_temp,
|
||||
loc);
|
||||
Expression* caddr = Expression::make_unary(OPERATOR_AND, cref, loc);
|
||||
call = Runtime::make_call(Runtime::SELECTRECV2, loc, 5, selref, chanref,
|
||||
valaddr, caddr, index_expr);
|
||||
}
|
||||
|
||||
b->add_statement(Statement::make_statement(call, true));
|
||||
|
||||
// If the block of statements is executed, arrange for the received
|
||||
// value to move from VAL to the place where the statements expect
|
||||
// it.
|
||||
|
||||
Block* init = NULL;
|
||||
|
||||
if (this->var_ != NULL)
|
||||
{
|
||||
go_assert(this->val_ == NULL);
|
||||
valref = Expression::make_temporary_reference(val, loc);
|
||||
this->var_->var_value()->set_init(valref);
|
||||
this->var_->var_value()->clear_type_from_chan_element();
|
||||
}
|
||||
else if (this->val_ != NULL && !this->val_->is_sink_expression())
|
||||
{
|
||||
init = new Block(b, loc);
|
||||
valref = Expression::make_temporary_reference(val, loc);
|
||||
init->add_statement(Statement::make_assignment(this->val_, valref, loc));
|
||||
}
|
||||
|
||||
if (this->closedvar_ != NULL)
|
||||
{
|
||||
go_assert(this->closed_ == NULL);
|
||||
Expression* cref = Expression::make_temporary_reference(closed_temp,
|
||||
loc);
|
||||
this->closedvar_->var_value()->set_init(cref);
|
||||
}
|
||||
else if (this->closed_ != NULL && !this->closed_->is_sink_expression())
|
||||
{
|
||||
if (init == NULL)
|
||||
init = new Block(b, loc);
|
||||
Expression* cref = Expression::make_temporary_reference(closed_temp,
|
||||
loc);
|
||||
init->add_statement(Statement::make_assignment(this->closed_, cref,
|
||||
loc));
|
||||
}
|
||||
|
||||
if (init != NULL)
|
||||
{
|
||||
gogo->lower_block(function, init);
|
||||
|
||||
if (this->statements_ != NULL)
|
||||
init->add_statement(Statement::make_block_statement(this->statements_,
|
||||
loc));
|
||||
this->statements_ = init;
|
||||
}
|
||||
}
|
||||
|
||||
// Determine types.
|
||||
|
||||
void
|
||||
@ -4630,6 +4671,27 @@ Select_clauses::Select_clause::determine_types()
|
||||
this->statements_->determine_types();
|
||||
}
|
||||
|
||||
// Check types.
|
||||
|
||||
void
|
||||
Select_clauses::Select_clause::check_types()
|
||||
{
|
||||
if (this->is_default_)
|
||||
return;
|
||||
|
||||
Channel_type* ct = this->channel_->type()->channel_type();
|
||||
if (ct == NULL)
|
||||
{
|
||||
error_at(this->channel_->location(), "expected channel");
|
||||
return;
|
||||
}
|
||||
|
||||
if (this->is_send_ && !ct->may_send())
|
||||
error_at(this->location(), "invalid send on receive-only channel");
|
||||
else if (!this->is_send_ && !ct->may_receive())
|
||||
error_at(this->location(), "invalid receive on send-only channel");
|
||||
}
|
||||
|
||||
// Whether this clause may fall through to the statement which follows
|
||||
// the overall select statement.
|
||||
|
||||
@ -4717,12 +4779,13 @@ Select_clauses::traverse(Traverse* traverse)
|
||||
// receive statements to the clauses.
|
||||
|
||||
void
|
||||
Select_clauses::lower(Gogo* gogo, Named_object* function, Block* b)
|
||||
Select_clauses::lower(Gogo* gogo, Named_object* function, Block* b,
|
||||
Temporary_statement* sel)
|
||||
{
|
||||
for (Clauses::iterator p = this->clauses_.begin();
|
||||
p != this->clauses_.end();
|
||||
++p)
|
||||
p->lower(gogo, function, b);
|
||||
p->lower(gogo, function, b, sel);
|
||||
}
|
||||
|
||||
// Determine types.
|
||||
@ -4736,6 +4799,17 @@ Select_clauses::determine_types()
|
||||
p->determine_types();
|
||||
}
|
||||
|
||||
// Check types.
|
||||
|
||||
void
|
||||
Select_clauses::check_types()
|
||||
{
|
||||
for (Clauses::iterator p = this->clauses_.begin();
|
||||
p != this->clauses_.end();
|
||||
++p)
|
||||
p->check_types();
|
||||
}
|
||||
|
||||
// Return whether these select clauses fall through to the statement
|
||||
// following the overall select statement.
|
||||
|
||||
@ -4750,179 +4824,55 @@ Select_clauses::may_fall_through() const
|
||||
return false;
|
||||
}
|
||||
|
||||
// Convert to the backend representation. We build a call to
|
||||
// size_t __go_select(size_t count, _Bool has_default,
|
||||
// channel* channels, _Bool* is_send)
|
||||
//
|
||||
// There are COUNT entries in the CHANNELS and IS_SEND arrays. The
|
||||
// value in the IS_SEND array is true for send, false for receive.
|
||||
// __go_select returns an integer from 0 to COUNT, inclusive. A
|
||||
// return of 0 means that the default case should be run; this only
|
||||
// happens if HAS_DEFAULT is non-zero. Otherwise the number indicates
|
||||
// the case to run.
|
||||
|
||||
// FIXME: This doesn't handle channels which send interface types
|
||||
// where the receiver has a static type which matches that interface.
|
||||
// Convert to the backend representation. We have already accumulated
|
||||
// all the select information. Now we call selectgo, which will
|
||||
// return the index of the clause to execute.
|
||||
|
||||
Bstatement*
|
||||
Select_clauses::get_backend(Translate_context* context,
|
||||
Temporary_statement* sel,
|
||||
Unnamed_label *break_label,
|
||||
Location location)
|
||||
{
|
||||
size_t count = this->clauses_.size();
|
||||
std::vector<std::vector<Bexpression*> > cases(count);
|
||||
std::vector<Bstatement*> clauses(count);
|
||||
|
||||
Expression_list* chan_init = new Expression_list();
|
||||
chan_init->reserve(count);
|
||||
|
||||
Expression_list* is_send_init = new Expression_list();
|
||||
is_send_init->reserve(count);
|
||||
|
||||
Select_clause *default_clause = NULL;
|
||||
|
||||
Type* runtime_chanptr_type = Runtime::chanptr_type();
|
||||
Type* runtime_chan_type = runtime_chanptr_type->points_to();
|
||||
|
||||
int i = 0;
|
||||
for (Clauses::iterator p = this->clauses_.begin();
|
||||
p != this->clauses_.end();
|
||||
++p)
|
||||
++p, ++i)
|
||||
{
|
||||
if (p->is_default())
|
||||
{
|
||||
default_clause = &*p;
|
||||
--count;
|
||||
continue;
|
||||
}
|
||||
int index = p->index();
|
||||
mpz_t ival;
|
||||
mpz_init_set_ui(ival, index);
|
||||
Expression* index_expr = Expression::make_integer(&ival, NULL, location);
|
||||
mpz_clear(ival);
|
||||
cases[i].push_back(tree_to_expr(index_expr->get_tree(context)));
|
||||
|
||||
if (p->channel()->type()->channel_type() == NULL)
|
||||
{
|
||||
// We should have given an error in the send or receive
|
||||
// statement we created via lowering.
|
||||
go_assert(saw_errors());
|
||||
return context->backend()->error_statement();
|
||||
}
|
||||
Bstatement* s = p->get_statements_backend(context);
|
||||
Location gloc = (p->statements() == NULL
|
||||
? p->location()
|
||||
: p->statements()->end_location());
|
||||
Bstatement* g = break_label->get_goto(context, gloc);
|
||||
|
||||
Expression* c = p->channel();
|
||||
c = Expression::make_unsafe_cast(runtime_chan_type, c, p->location());
|
||||
chan_init->push_back(c);
|
||||
|
||||
is_send_init->push_back(Expression::make_boolean(p->is_send(),
|
||||
p->location()));
|
||||
}
|
||||
|
||||
if (chan_init->empty())
|
||||
{
|
||||
go_assert(count == 0);
|
||||
Bstatement* s;
|
||||
Bstatement* ldef = break_label->get_definition(context);
|
||||
if (default_clause != NULL)
|
||||
{
|
||||
// There is a default clause and no cases. Just execute the
|
||||
// default clause.
|
||||
s = default_clause->get_statements_backend(context);
|
||||
}
|
||||
else
|
||||
{
|
||||
// There isn't even a default clause. In this case select
|
||||
// pauses forever. Call the runtime function with nils.
|
||||
mpz_t zval;
|
||||
mpz_init_set_ui(zval, 0);
|
||||
Expression* zero = Expression::make_integer(&zval, NULL, location);
|
||||
mpz_clear(zval);
|
||||
Expression* default_arg = Expression::make_boolean(false, location);
|
||||
Expression* nil1 = Expression::make_nil(location);
|
||||
Expression* nil2 = nil1->copy();
|
||||
Expression* call = Runtime::make_call(Runtime::SELECT, location, 4,
|
||||
zero, default_arg, nil1, nil2);
|
||||
context->gogo()->lower_expression(context->function(), NULL, &call);
|
||||
Bexpression* bcall = tree_to_expr(call->get_tree(context));
|
||||
s = context->backend()->expression_statement(bcall);
|
||||
}
|
||||
if (s == NULL)
|
||||
return ldef;
|
||||
return context->backend()->compound_statement(s, ldef);
|
||||
clauses[i] = g;
|
||||
else
|
||||
clauses[i] = context->backend()->compound_statement(s, g);
|
||||
}
|
||||
go_assert(count > 0);
|
||||
|
||||
std::vector<Bstatement*> statements;
|
||||
|
||||
mpz_t ival;
|
||||
mpz_init_set_ui(ival, count);
|
||||
Expression* ecount = Expression::make_integer(&ival, NULL, location);
|
||||
mpz_clear(ival);
|
||||
|
||||
Type* chan_array_type = Type::make_array_type(runtime_chan_type, ecount);
|
||||
Expression* chans = Expression::make_composite_literal(chan_array_type, 0,
|
||||
false, chan_init,
|
||||
location);
|
||||
context->gogo()->lower_expression(context->function(), NULL, &chans);
|
||||
Temporary_statement* chan_temp = Statement::make_temporary(chan_array_type,
|
||||
chans,
|
||||
location);
|
||||
statements.push_back(chan_temp->get_backend(context));
|
||||
|
||||
Type* is_send_array_type = Type::make_array_type(Type::lookup_bool_type(),
|
||||
ecount->copy());
|
||||
Expression* is_sends = Expression::make_composite_literal(is_send_array_type,
|
||||
0, false,
|
||||
is_send_init,
|
||||
location);
|
||||
context->gogo()->lower_expression(context->function(), NULL, &is_sends);
|
||||
Temporary_statement* is_send_temp =
|
||||
Statement::make_temporary(is_send_array_type, is_sends, location);
|
||||
statements.push_back(is_send_temp->get_backend(context));
|
||||
|
||||
mpz_init_set_ui(ival, 0);
|
||||
Expression* zero = Expression::make_integer(&ival, NULL, location);
|
||||
mpz_clear(ival);
|
||||
|
||||
Expression* ref = Expression::make_temporary_reference(chan_temp, location);
|
||||
Expression* chan_arg = Expression::make_array_index(ref, zero, NULL,
|
||||
location);
|
||||
chan_arg = Expression::make_unary(OPERATOR_AND, chan_arg, location);
|
||||
chan_arg = Expression::make_unsafe_cast(runtime_chanptr_type, chan_arg,
|
||||
location);
|
||||
|
||||
ref = Expression::make_temporary_reference(is_send_temp, location);
|
||||
Expression* is_send_arg = Expression::make_array_index(ref, zero->copy(),
|
||||
NULL, location);
|
||||
is_send_arg = Expression::make_unary(OPERATOR_AND, is_send_arg, location);
|
||||
|
||||
Expression* default_arg = Expression::make_boolean(default_clause != NULL,
|
||||
location);
|
||||
Expression* call = Runtime::make_call(Runtime::SELECT, location, 4,
|
||||
ecount->copy(), default_arg,
|
||||
chan_arg, is_send_arg);
|
||||
Expression* selref = Expression::make_temporary_reference(sel, location);
|
||||
Expression* call = Runtime::make_call(Runtime::SELECTGO, location, 1,
|
||||
selref);
|
||||
context->gogo()->lower_expression(context->function(), NULL, &call);
|
||||
Bexpression* bcall = tree_to_expr(call->get_tree(context));
|
||||
|
||||
std::vector<std::vector<Bexpression*> > cases;
|
||||
std::vector<Bstatement*> clauses;
|
||||
if (count == 0)
|
||||
return context->backend()->expression_statement(bcall);
|
||||
|
||||
cases.resize(count + (default_clause != NULL ? 1 : 0));
|
||||
clauses.resize(count + (default_clause != NULL ? 1 : 0));
|
||||
|
||||
int index = 0;
|
||||
|
||||
if (default_clause != NULL)
|
||||
{
|
||||
this->add_clause_backend(context, location, index, 0, default_clause,
|
||||
break_label, &cases, &clauses);
|
||||
++index;
|
||||
}
|
||||
|
||||
int i = 1;
|
||||
for (Clauses::iterator p = this->clauses_.begin();
|
||||
p != this->clauses_.end();
|
||||
++p)
|
||||
{
|
||||
if (!p->is_default())
|
||||
{
|
||||
this->add_clause_backend(context, location, index, i, &*p,
|
||||
break_label, &cases, &clauses);
|
||||
++i;
|
||||
++index;
|
||||
}
|
||||
}
|
||||
std::vector<Bstatement*> statements;
|
||||
statements.reserve(2);
|
||||
|
||||
Bstatement* switch_stmt = context->backend()->switch_statement(bcall,
|
||||
cases,
|
||||
@ -4935,39 +4885,6 @@ Select_clauses::get_backend(Translate_context* context,
|
||||
|
||||
return context->backend()->statement_list(statements);
|
||||
}
|
||||
|
||||
// Add CLAUSE to CASES/CLAUSES at INDEX.
|
||||
|
||||
void
|
||||
Select_clauses::add_clause_backend(
|
||||
Translate_context* context,
|
||||
Location location,
|
||||
int index,
|
||||
int case_value,
|
||||
Select_clause* clause,
|
||||
Unnamed_label* bottom_label,
|
||||
std::vector<std::vector<Bexpression*> > *cases,
|
||||
std::vector<Bstatement*>* clauses)
|
||||
{
|
||||
mpz_t ival;
|
||||
mpz_init_set_ui(ival, case_value);
|
||||
Expression* e = Expression::make_integer(&ival, NULL, location);
|
||||
mpz_clear(ival);
|
||||
(*cases)[index].push_back(tree_to_expr(e->get_tree(context)));
|
||||
|
||||
Bstatement* s = clause->get_statements_backend(context);
|
||||
|
||||
Location gloc = (clause->statements() == NULL
|
||||
? clause->location()
|
||||
: clause->statements()->end_location());
|
||||
Bstatement* g = bottom_label->get_goto(context, gloc);
|
||||
|
||||
if (s == NULL)
|
||||
(*clauses)[index] = g;
|
||||
else
|
||||
(*clauses)[index] = context->backend()->compound_statement(s, g);
|
||||
}
|
||||
|
||||
// Dump the AST representation for select clauses.
|
||||
|
||||
void
|
||||
@ -5003,11 +4920,28 @@ Select_statement::do_lower(Gogo* gogo, Named_object* function,
|
||||
{
|
||||
if (this->is_lowered_)
|
||||
return this;
|
||||
Block* b = new Block(enclosing, this->location());
|
||||
this->clauses_->lower(gogo, function, b);
|
||||
|
||||
Location loc = this->location();
|
||||
|
||||
Block* b = new Block(enclosing, loc);
|
||||
|
||||
go_assert(this->sel_ == NULL);
|
||||
|
||||
mpz_t ival;
|
||||
mpz_init_set_ui(ival, this->clauses_->size());
|
||||
Expression* size_expr = Expression::make_integer(&ival, NULL, loc);
|
||||
mpz_clear(ival);
|
||||
|
||||
Expression* call = Runtime::make_call(Runtime::NEWSELECT, loc, 1, size_expr);
|
||||
|
||||
this->sel_ = Statement::make_temporary(NULL, call, loc);
|
||||
b->add_statement(this->sel_);
|
||||
|
||||
this->clauses_->lower(gogo, function, b, this->sel_);
|
||||
this->is_lowered_ = true;
|
||||
b->add_statement(this);
|
||||
return Statement::make_block_statement(b, this->location());
|
||||
|
||||
return Statement::make_block_statement(b, loc);
|
||||
}
|
||||
|
||||
// Return the backend representation for a select statement.
|
||||
@ -5015,7 +4949,7 @@ Select_statement::do_lower(Gogo* gogo, Named_object* function,
|
||||
Bstatement*
|
||||
Select_statement::do_get_backend(Translate_context* context)
|
||||
{
|
||||
return this->clauses_->get_backend(context, this->break_label(),
|
||||
return this->clauses_->get_backend(context, this->sel_, this->break_label(),
|
||||
this->location());
|
||||
}
|
||||
|
||||
@ -5790,7 +5724,7 @@ For_range_statement::lower_range_channel(Gogo*,
|
||||
Expression::make_temporary_reference(ok_temp, loc);
|
||||
oref->set_is_lvalue();
|
||||
Statement* s = Statement::make_tuple_receive_assignment(iref, oref, cref,
|
||||
false, loc);
|
||||
loc);
|
||||
iter_init->add_statement(s);
|
||||
|
||||
Block* then_block = new Block(iter_init, loc);
|
||||
|
@ -165,12 +165,10 @@ class Statement
|
||||
Expression* should_set, Location);
|
||||
|
||||
// Make an assignment from a nonblocking receive to a pair of
|
||||
// variables. FOR_SELECT is true is this is being created for a
|
||||
// case x, ok := <-c in a select statement.
|
||||
// variables.
|
||||
static Statement*
|
||||
make_tuple_receive_assignment(Expression* val, Expression* closed,
|
||||
Expression* channel, bool for_select,
|
||||
Location);
|
||||
Expression* channel, Location);
|
||||
|
||||
// Make an assignment from a type guard to a pair of variables.
|
||||
static Statement*
|
||||
@ -634,14 +632,9 @@ class Send_statement : public Statement
|
||||
Send_statement(Expression* channel, Expression* val,
|
||||
Location location)
|
||||
: Statement(STATEMENT_SEND, location),
|
||||
channel_(channel), val_(val), for_select_(false)
|
||||
channel_(channel), val_(val)
|
||||
{ }
|
||||
|
||||
// Note that this is for a select statement.
|
||||
void
|
||||
set_for_select()
|
||||
{ this->for_select_ = true; }
|
||||
|
||||
protected:
|
||||
int
|
||||
do_traverse(Traverse* traverse);
|
||||
@ -663,8 +656,6 @@ class Send_statement : public Statement
|
||||
Expression* channel_;
|
||||
// The value to send.
|
||||
Expression* val_;
|
||||
// Whether this is for a select statement.
|
||||
bool for_select_;
|
||||
};
|
||||
|
||||
// Select_clauses holds the clauses of a select statement. This is
|
||||
@ -693,23 +684,32 @@ class Select_clauses
|
||||
Named_object* var, Named_object* closedvar, bool is_default,
|
||||
Block* statements, Location location)
|
||||
{
|
||||
this->clauses_.push_back(Select_clause(is_send, channel, val, closed, var,
|
||||
closedvar, is_default, statements,
|
||||
location));
|
||||
int index = static_cast<int>(this->clauses_.size());
|
||||
this->clauses_.push_back(Select_clause(index, is_send, channel, val,
|
||||
closed, var, closedvar, is_default,
|
||||
statements, location));
|
||||
}
|
||||
|
||||
size_t
|
||||
size() const
|
||||
{ return this->clauses_.size(); }
|
||||
|
||||
// Traverse the select clauses.
|
||||
int
|
||||
traverse(Traverse*);
|
||||
|
||||
// Lower statements.
|
||||
void
|
||||
lower(Gogo*, Named_object*, Block*);
|
||||
lower(Gogo*, Named_object*, Block*, Temporary_statement*);
|
||||
|
||||
// Determine types.
|
||||
void
|
||||
determine_types();
|
||||
|
||||
// Check types.
|
||||
void
|
||||
check_types();
|
||||
|
||||
// Whether the select clauses may fall through to the statement
|
||||
// which follows the overall select statement.
|
||||
bool
|
||||
@ -717,7 +717,8 @@ class Select_clauses
|
||||
|
||||
// Convert to the backend representation.
|
||||
Bstatement*
|
||||
get_backend(Translate_context*, Unnamed_label* break_label, Location);
|
||||
get_backend(Translate_context*, Temporary_statement* sel,
|
||||
Unnamed_label* break_label, Location);
|
||||
|
||||
// Dump AST representation.
|
||||
void
|
||||
@ -734,27 +735,37 @@ class Select_clauses
|
||||
is_default_(false)
|
||||
{ }
|
||||
|
||||
Select_clause(bool is_send, Expression* channel, Expression* val,
|
||||
Expression* closed, Named_object* var,
|
||||
Select_clause(int index, bool is_send, Expression* channel,
|
||||
Expression* val, Expression* closed, Named_object* var,
|
||||
Named_object* closedvar, bool is_default, Block* statements,
|
||||
Location location)
|
||||
: channel_(channel), val_(val), closed_(closed), var_(var),
|
||||
closedvar_(closedvar), statements_(statements), location_(location),
|
||||
is_send_(is_send), is_default_(is_default), is_lowered_(false)
|
||||
: index_(index), channel_(channel), val_(val), closed_(closed),
|
||||
var_(var), closedvar_(closedvar), statements_(statements),
|
||||
location_(location), is_send_(is_send), is_default_(is_default),
|
||||
is_lowered_(false)
|
||||
{ go_assert(is_default ? channel == NULL : channel != NULL); }
|
||||
|
||||
// Return the index of this clause.
|
||||
int
|
||||
index() const
|
||||
{ return this->index_; }
|
||||
|
||||
// Traverse the select clause.
|
||||
int
|
||||
traverse(Traverse*);
|
||||
|
||||
// Lower statements.
|
||||
void
|
||||
lower(Gogo*, Named_object*, Block*);
|
||||
lower(Gogo*, Named_object*, Block*, Temporary_statement*);
|
||||
|
||||
// Determine types.
|
||||
void
|
||||
determine_types();
|
||||
|
||||
// Check types.
|
||||
void
|
||||
check_types();
|
||||
|
||||
// Return true if this is the default clause.
|
||||
bool
|
||||
is_default() const
|
||||
@ -798,6 +809,18 @@ class Select_clauses
|
||||
dump_clause(Ast_dump_context*) const;
|
||||
|
||||
private:
|
||||
void
|
||||
lower_default(Block*, Expression*, Expression*);
|
||||
|
||||
void
|
||||
lower_send(Block*, Expression*, Expression*, Expression*);
|
||||
|
||||
void
|
||||
lower_recv(Gogo*, Named_object*, Block*, Expression*, Expression*,
|
||||
Expression*);
|
||||
|
||||
// The index of this case in the generated switch statement.
|
||||
int index_;
|
||||
// The channel.
|
||||
Expression* channel_;
|
||||
// The value to send or the lvalue to receive into.
|
||||
@ -822,12 +845,6 @@ class Select_clauses
|
||||
bool is_lowered_;
|
||||
};
|
||||
|
||||
void
|
||||
add_clause_backend(Translate_context*, Location, int index,
|
||||
int case_value, Select_clause*, Unnamed_label*,
|
||||
std::vector<std::vector<Bexpression*> >* cases,
|
||||
std::vector<Bstatement*>* clauses);
|
||||
|
||||
typedef std::vector<Select_clause> Clauses;
|
||||
|
||||
Clauses clauses_;
|
||||
@ -840,7 +857,7 @@ class Select_statement : public Statement
|
||||
public:
|
||||
Select_statement(Location location)
|
||||
: Statement(STATEMENT_SELECT, location),
|
||||
clauses_(NULL), break_label_(NULL), is_lowered_(false)
|
||||
clauses_(NULL), sel_(NULL), break_label_(NULL), is_lowered_(false)
|
||||
{ }
|
||||
|
||||
// Add the clauses.
|
||||
@ -867,6 +884,10 @@ class Select_statement : public Statement
|
||||
do_determine_types()
|
||||
{ this->clauses_->determine_types(); }
|
||||
|
||||
void
|
||||
do_check_types(Gogo*)
|
||||
{ this->clauses_->check_types(); }
|
||||
|
||||
bool
|
||||
do_may_fall_through() const
|
||||
{ return this->clauses_->may_fall_through(); }
|
||||
@ -880,6 +901,8 @@ class Select_statement : public Statement
|
||||
private:
|
||||
// The select clauses.
|
||||
Select_clauses* clauses_;
|
||||
// A temporary which holds the select structure we build up at runtime.
|
||||
Temporary_statement* sel_;
|
||||
// The break label.
|
||||
Unnamed_label* break_label_;
|
||||
// Whether this statement has been lowered.
|
||||
|
@ -409,10 +409,7 @@ runtime_files = \
|
||||
runtime/go-caller.c \
|
||||
runtime/go-can-convert-interface.c \
|
||||
runtime/go-cgo.c \
|
||||
runtime/go-chan-cap.c \
|
||||
runtime/go-chan-len.c \
|
||||
runtime/go-check-interface.c \
|
||||
runtime/go-close.c \
|
||||
runtime/go-construct-map.c \
|
||||
runtime/go-convert-interface.c \
|
||||
runtime/go-copy.c \
|
||||
@ -432,27 +429,16 @@ runtime_files = \
|
||||
runtime/go-map-len.c \
|
||||
runtime/go-map-range.c \
|
||||
runtime/go-nanotime.c \
|
||||
runtime/go-new-channel.c \
|
||||
runtime/go-new-map.c \
|
||||
runtime/go-new.c \
|
||||
runtime/go-panic.c \
|
||||
runtime/go-print.c \
|
||||
runtime/go-rec-big.c \
|
||||
runtime/go-rec-nb-big.c \
|
||||
runtime/go-rec-nb-small.c \
|
||||
runtime/go-rec-small.c \
|
||||
runtime/go-recover.c \
|
||||
runtime/go-reflect.c \
|
||||
runtime/go-reflect-call.c \
|
||||
runtime/go-reflect-chan.c \
|
||||
runtime/go-reflect-map.c \
|
||||
runtime/go-rune.c \
|
||||
runtime/go-runtime-error.c \
|
||||
runtime/go-select.c \
|
||||
runtime/go-send-big.c \
|
||||
runtime/go-send-nb-big.c \
|
||||
runtime/go-send-nb-small.c \
|
||||
runtime/go-send-small.c \
|
||||
runtime/go-setenv.c \
|
||||
runtime/go-signal.c \
|
||||
runtime/go-strcmp.c \
|
||||
@ -473,6 +459,7 @@ runtime_files = \
|
||||
runtime/go-unsafe-newarray.c \
|
||||
runtime/go-unsafe-pointer.c \
|
||||
runtime/go-unwind.c \
|
||||
runtime/chan.c \
|
||||
runtime/cpuprof.c \
|
||||
$(runtime_lock_files) \
|
||||
runtime/mcache.c \
|
||||
@ -488,7 +475,6 @@ runtime_files = \
|
||||
runtime/thread.c \
|
||||
runtime/yield.c \
|
||||
$(rtems_task_variable_add_file) \
|
||||
chan.c \
|
||||
iface.c \
|
||||
malloc.c \
|
||||
map.c \
|
||||
|
@ -183,8 +183,7 @@ am__libgo_la_SOURCES_DIST = runtime/go-append.c runtime/go-assert.c \
|
||||
runtime/go-assert-interface.c \
|
||||
runtime/go-byte-array-to-string.c runtime/go-breakpoint.c \
|
||||
runtime/go-caller.c runtime/go-can-convert-interface.c \
|
||||
runtime/go-cgo.c runtime/go-chan-cap.c runtime/go-chan-len.c \
|
||||
runtime/go-check-interface.c runtime/go-close.c \
|
||||
runtime/go-cgo.c runtime/go-check-interface.c \
|
||||
runtime/go-construct-map.c runtime/go-convert-interface.c \
|
||||
runtime/go-copy.c runtime/go-defer.c \
|
||||
runtime/go-deferred-recover.c runtime/go-eface-compare.c \
|
||||
@ -195,17 +194,11 @@ am__libgo_la_SOURCES_DIST = runtime/go-append.c runtime/go-assert.c \
|
||||
runtime/go-interface-val-compare.c runtime/go-make-slice.c \
|
||||
runtime/go-map-delete.c runtime/go-map-index.c \
|
||||
runtime/go-map-len.c runtime/go-map-range.c \
|
||||
runtime/go-nanotime.c runtime/go-new-channel.c \
|
||||
runtime/go-new-map.c runtime/go-new.c runtime/go-panic.c \
|
||||
runtime/go-print.c runtime/go-rec-big.c \
|
||||
runtime/go-rec-nb-big.c runtime/go-rec-nb-small.c \
|
||||
runtime/go-rec-small.c runtime/go-recover.c \
|
||||
runtime/go-nanotime.c runtime/go-new-map.c runtime/go-new.c \
|
||||
runtime/go-panic.c runtime/go-print.c runtime/go-recover.c \
|
||||
runtime/go-reflect.c runtime/go-reflect-call.c \
|
||||
runtime/go-reflect-chan.c runtime/go-reflect-map.c \
|
||||
runtime/go-rune.c runtime/go-runtime-error.c \
|
||||
runtime/go-select.c runtime/go-send-big.c \
|
||||
runtime/go-send-nb-big.c runtime/go-send-nb-small.c \
|
||||
runtime/go-send-small.c runtime/go-setenv.c \
|
||||
runtime/go-reflect-map.c runtime/go-rune.c \
|
||||
runtime/go-runtime-error.c runtime/go-setenv.c \
|
||||
runtime/go-signal.c runtime/go-strcmp.c \
|
||||
runtime/go-string-to-byte-array.c \
|
||||
runtime/go-string-to-int-array.c runtime/go-strplus.c \
|
||||
@ -215,15 +208,15 @@ am__libgo_la_SOURCES_DIST = runtime/go-append.c runtime/go-assert.c \
|
||||
runtime/go-type-string.c runtime/go-typedesc-equal.c \
|
||||
runtime/go-typestring.c runtime/go-unreflect.c \
|
||||
runtime/go-unsafe-new.c runtime/go-unsafe-newarray.c \
|
||||
runtime/go-unsafe-pointer.c runtime/go-unwind.c \
|
||||
runtime/go-unsafe-pointer.c runtime/go-unwind.c runtime/chan.c \
|
||||
runtime/cpuprof.c runtime/lock_sema.c runtime/thread-sema.c \
|
||||
runtime/lock_futex.c runtime/thread-linux.c runtime/mcache.c \
|
||||
runtime/mcentral.c runtime/mem_posix_memalign.c runtime/mem.c \
|
||||
runtime/mfinal.c runtime/mfixalloc.c runtime/mgc0.c \
|
||||
runtime/mheap.c runtime/msize.c runtime/proc.c \
|
||||
runtime/runtime.c runtime/thread.c runtime/yield.c \
|
||||
runtime/rtems-task-variable-add.c chan.c iface.c malloc.c \
|
||||
map.c mprof.c reflect.c runtime1.c sema.c sigqueue.c string.c
|
||||
runtime/rtems-task-variable-add.c iface.c malloc.c map.c \
|
||||
mprof.c reflect.c runtime1.c sema.c sigqueue.c string.c
|
||||
@LIBGO_IS_LINUX_FALSE@am__objects_1 = lock_sema.lo thread-sema.lo
|
||||
@LIBGO_IS_LINUX_TRUE@am__objects_1 = lock_futex.lo thread-linux.lo
|
||||
@HAVE_SYS_MMAN_H_FALSE@am__objects_2 = mem_posix_memalign.lo
|
||||
@ -231,8 +224,7 @@ am__libgo_la_SOURCES_DIST = runtime/go-append.c runtime/go-assert.c \
|
||||
@LIBGO_IS_RTEMS_TRUE@am__objects_3 = rtems-task-variable-add.lo
|
||||
am__objects_4 = go-append.lo go-assert.lo go-assert-interface.lo \
|
||||
go-byte-array-to-string.lo go-breakpoint.lo go-caller.lo \
|
||||
go-can-convert-interface.lo go-cgo.lo go-chan-cap.lo \
|
||||
go-chan-len.lo go-check-interface.lo go-close.lo \
|
||||
go-can-convert-interface.lo go-cgo.lo go-check-interface.lo \
|
||||
go-construct-map.lo go-convert-interface.lo go-copy.lo \
|
||||
go-defer.lo go-deferred-recover.lo go-eface-compare.lo \
|
||||
go-eface-val-compare.lo go-getgoroot.lo \
|
||||
@ -240,23 +232,20 @@ am__objects_4 = go-append.lo go-assert.lo go-assert-interface.lo \
|
||||
go-interface-compare.lo go-interface-eface-compare.lo \
|
||||
go-interface-val-compare.lo go-make-slice.lo go-map-delete.lo \
|
||||
go-map-index.lo go-map-len.lo go-map-range.lo go-nanotime.lo \
|
||||
go-new-channel.lo go-new-map.lo go-new.lo go-panic.lo \
|
||||
go-print.lo go-rec-big.lo go-rec-nb-big.lo go-rec-nb-small.lo \
|
||||
go-rec-small.lo go-recover.lo go-reflect.lo go-reflect-call.lo \
|
||||
go-reflect-chan.lo go-reflect-map.lo go-rune.lo \
|
||||
go-runtime-error.lo go-select.lo go-send-big.lo \
|
||||
go-send-nb-big.lo go-send-nb-small.lo go-send-small.lo \
|
||||
go-setenv.lo go-signal.lo go-strcmp.lo \
|
||||
go-new-map.lo go-new.lo go-panic.lo go-print.lo go-recover.lo \
|
||||
go-reflect.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-trampoline.lo go-type-eface.lo \
|
||||
go-type-error.lo go-type-identity.lo go-type-interface.lo \
|
||||
go-type-string.lo go-typedesc-equal.lo go-typestring.lo \
|
||||
go-unreflect.lo go-unsafe-new.lo go-unsafe-newarray.lo \
|
||||
go-unsafe-pointer.lo go-unwind.lo cpuprof.lo $(am__objects_1) \
|
||||
mcache.lo mcentral.lo $(am__objects_2) mfinal.lo mfixalloc.lo \
|
||||
mgc0.lo mheap.lo msize.lo proc.lo runtime.lo thread.lo \
|
||||
yield.lo $(am__objects_3) chan.lo iface.lo malloc.lo map.lo \
|
||||
mprof.lo reflect.lo runtime1.lo sema.lo sigqueue.lo string.lo
|
||||
go-unsafe-pointer.lo go-unwind.lo chan.lo cpuprof.lo \
|
||||
$(am__objects_1) mcache.lo mcentral.lo $(am__objects_2) \
|
||||
mfinal.lo mfixalloc.lo mgc0.lo mheap.lo msize.lo proc.lo \
|
||||
runtime.lo thread.lo yield.lo $(am__objects_3) iface.lo \
|
||||
malloc.lo map.lo mprof.lo reflect.lo runtime1.lo sema.lo \
|
||||
sigqueue.lo string.lo
|
||||
am_libgo_la_OBJECTS = $(am__objects_4)
|
||||
libgo_la_OBJECTS = $(am_libgo_la_OBJECTS)
|
||||
libgo_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
|
||||
@ -836,10 +825,7 @@ runtime_files = \
|
||||
runtime/go-caller.c \
|
||||
runtime/go-can-convert-interface.c \
|
||||
runtime/go-cgo.c \
|
||||
runtime/go-chan-cap.c \
|
||||
runtime/go-chan-len.c \
|
||||
runtime/go-check-interface.c \
|
||||
runtime/go-close.c \
|
||||
runtime/go-construct-map.c \
|
||||
runtime/go-convert-interface.c \
|
||||
runtime/go-copy.c \
|
||||
@ -859,27 +845,16 @@ runtime_files = \
|
||||
runtime/go-map-len.c \
|
||||
runtime/go-map-range.c \
|
||||
runtime/go-nanotime.c \
|
||||
runtime/go-new-channel.c \
|
||||
runtime/go-new-map.c \
|
||||
runtime/go-new.c \
|
||||
runtime/go-panic.c \
|
||||
runtime/go-print.c \
|
||||
runtime/go-rec-big.c \
|
||||
runtime/go-rec-nb-big.c \
|
||||
runtime/go-rec-nb-small.c \
|
||||
runtime/go-rec-small.c \
|
||||
runtime/go-recover.c \
|
||||
runtime/go-reflect.c \
|
||||
runtime/go-reflect-call.c \
|
||||
runtime/go-reflect-chan.c \
|
||||
runtime/go-reflect-map.c \
|
||||
runtime/go-rune.c \
|
||||
runtime/go-runtime-error.c \
|
||||
runtime/go-select.c \
|
||||
runtime/go-send-big.c \
|
||||
runtime/go-send-nb-big.c \
|
||||
runtime/go-send-nb-small.c \
|
||||
runtime/go-send-small.c \
|
||||
runtime/go-setenv.c \
|
||||
runtime/go-signal.c \
|
||||
runtime/go-strcmp.c \
|
||||
@ -900,6 +875,7 @@ runtime_files = \
|
||||
runtime/go-unsafe-newarray.c \
|
||||
runtime/go-unsafe-pointer.c \
|
||||
runtime/go-unwind.c \
|
||||
runtime/chan.c \
|
||||
runtime/cpuprof.c \
|
||||
$(runtime_lock_files) \
|
||||
runtime/mcache.c \
|
||||
@ -915,7 +891,6 @@ runtime_files = \
|
||||
runtime/thread.c \
|
||||
runtime/yield.c \
|
||||
$(rtems_task_variable_add_file) \
|
||||
chan.c \
|
||||
iface.c \
|
||||
malloc.c \
|
||||
map.c \
|
||||
@ -2461,10 +2436,7 @@ distclean-compile:
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-caller.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-can-convert-interface.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-cgo.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-chan-cap.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-chan-len.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-check-interface.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-close.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-construct-map.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-convert-interface.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-copy.Plo@am__quote@
|
||||
@ -2485,27 +2457,16 @@ distclean-compile:
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-map-len.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-map-range.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-nanotime.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-new-channel.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-new-map.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-new.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-panic.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-print.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-rec-big.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-rec-nb-big.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-rec-nb-small.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-rec-small.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-recover.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-reflect-call.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-reflect-chan.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-reflect-map.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-reflect.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-rune.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-runtime-error.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-select.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-send-big.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-send-nb-big.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-send-nb-small.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-send-small.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-setenv.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-signal.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-strcmp.Plo@am__quote@
|
||||
@ -2645,20 +2606,6 @@ go-cgo.lo: runtime/go-cgo.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-cgo.lo `test -f 'runtime/go-cgo.c' || echo '$(srcdir)/'`runtime/go-cgo.c
|
||||
|
||||
go-chan-cap.lo: runtime/go-chan-cap.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-chan-cap.lo -MD -MP -MF $(DEPDIR)/go-chan-cap.Tpo -c -o go-chan-cap.lo `test -f 'runtime/go-chan-cap.c' || echo '$(srcdir)/'`runtime/go-chan-cap.c
|
||||
@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/go-chan-cap.Tpo $(DEPDIR)/go-chan-cap.Plo
|
||||
@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='runtime/go-chan-cap.c' object='go-chan-cap.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-chan-cap.lo `test -f 'runtime/go-chan-cap.c' || echo '$(srcdir)/'`runtime/go-chan-cap.c
|
||||
|
||||
go-chan-len.lo: runtime/go-chan-len.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-chan-len.lo -MD -MP -MF $(DEPDIR)/go-chan-len.Tpo -c -o go-chan-len.lo `test -f 'runtime/go-chan-len.c' || echo '$(srcdir)/'`runtime/go-chan-len.c
|
||||
@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/go-chan-len.Tpo $(DEPDIR)/go-chan-len.Plo
|
||||
@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='runtime/go-chan-len.c' object='go-chan-len.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-chan-len.lo `test -f 'runtime/go-chan-len.c' || echo '$(srcdir)/'`runtime/go-chan-len.c
|
||||
|
||||
go-check-interface.lo: runtime/go-check-interface.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-check-interface.lo -MD -MP -MF $(DEPDIR)/go-check-interface.Tpo -c -o go-check-interface.lo `test -f 'runtime/go-check-interface.c' || echo '$(srcdir)/'`runtime/go-check-interface.c
|
||||
@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/go-check-interface.Tpo $(DEPDIR)/go-check-interface.Plo
|
||||
@ -2666,13 +2613,6 @@ go-check-interface.lo: runtime/go-check-interface.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-check-interface.lo `test -f 'runtime/go-check-interface.c' || echo '$(srcdir)/'`runtime/go-check-interface.c
|
||||
|
||||
go-close.lo: runtime/go-close.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-close.lo -MD -MP -MF $(DEPDIR)/go-close.Tpo -c -o go-close.lo `test -f 'runtime/go-close.c' || echo '$(srcdir)/'`runtime/go-close.c
|
||||
@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/go-close.Tpo $(DEPDIR)/go-close.Plo
|
||||
@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='runtime/go-close.c' object='go-close.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-close.lo `test -f 'runtime/go-close.c' || echo '$(srcdir)/'`runtime/go-close.c
|
||||
|
||||
go-construct-map.lo: runtime/go-construct-map.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-construct-map.lo -MD -MP -MF $(DEPDIR)/go-construct-map.Tpo -c -o go-construct-map.lo `test -f 'runtime/go-construct-map.c' || echo '$(srcdir)/'`runtime/go-construct-map.c
|
||||
@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/go-construct-map.Tpo $(DEPDIR)/go-construct-map.Plo
|
||||
@ -2806,13 +2746,6 @@ go-nanotime.lo: runtime/go-nanotime.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-nanotime.lo `test -f 'runtime/go-nanotime.c' || echo '$(srcdir)/'`runtime/go-nanotime.c
|
||||
|
||||
go-new-channel.lo: runtime/go-new-channel.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-new-channel.lo -MD -MP -MF $(DEPDIR)/go-new-channel.Tpo -c -o go-new-channel.lo `test -f 'runtime/go-new-channel.c' || echo '$(srcdir)/'`runtime/go-new-channel.c
|
||||
@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/go-new-channel.Tpo $(DEPDIR)/go-new-channel.Plo
|
||||
@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='runtime/go-new-channel.c' object='go-new-channel.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-new-channel.lo `test -f 'runtime/go-new-channel.c' || echo '$(srcdir)/'`runtime/go-new-channel.c
|
||||
|
||||
go-new-map.lo: runtime/go-new-map.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-new-map.lo -MD -MP -MF $(DEPDIR)/go-new-map.Tpo -c -o go-new-map.lo `test -f 'runtime/go-new-map.c' || echo '$(srcdir)/'`runtime/go-new-map.c
|
||||
@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/go-new-map.Tpo $(DEPDIR)/go-new-map.Plo
|
||||
@ -2841,34 +2774,6 @@ go-print.lo: runtime/go-print.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-print.lo `test -f 'runtime/go-print.c' || echo '$(srcdir)/'`runtime/go-print.c
|
||||
|
||||
go-rec-big.lo: runtime/go-rec-big.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-rec-big.lo -MD -MP -MF $(DEPDIR)/go-rec-big.Tpo -c -o go-rec-big.lo `test -f 'runtime/go-rec-big.c' || echo '$(srcdir)/'`runtime/go-rec-big.c
|
||||
@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/go-rec-big.Tpo $(DEPDIR)/go-rec-big.Plo
|
||||
@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='runtime/go-rec-big.c' object='go-rec-big.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-rec-big.lo `test -f 'runtime/go-rec-big.c' || echo '$(srcdir)/'`runtime/go-rec-big.c
|
||||
|
||||
go-rec-nb-big.lo: runtime/go-rec-nb-big.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-rec-nb-big.lo -MD -MP -MF $(DEPDIR)/go-rec-nb-big.Tpo -c -o go-rec-nb-big.lo `test -f 'runtime/go-rec-nb-big.c' || echo '$(srcdir)/'`runtime/go-rec-nb-big.c
|
||||
@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/go-rec-nb-big.Tpo $(DEPDIR)/go-rec-nb-big.Plo
|
||||
@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='runtime/go-rec-nb-big.c' object='go-rec-nb-big.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-rec-nb-big.lo `test -f 'runtime/go-rec-nb-big.c' || echo '$(srcdir)/'`runtime/go-rec-nb-big.c
|
||||
|
||||
go-rec-nb-small.lo: runtime/go-rec-nb-small.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-rec-nb-small.lo -MD -MP -MF $(DEPDIR)/go-rec-nb-small.Tpo -c -o go-rec-nb-small.lo `test -f 'runtime/go-rec-nb-small.c' || echo '$(srcdir)/'`runtime/go-rec-nb-small.c
|
||||
@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/go-rec-nb-small.Tpo $(DEPDIR)/go-rec-nb-small.Plo
|
||||
@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='runtime/go-rec-nb-small.c' object='go-rec-nb-small.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-rec-nb-small.lo `test -f 'runtime/go-rec-nb-small.c' || echo '$(srcdir)/'`runtime/go-rec-nb-small.c
|
||||
|
||||
go-rec-small.lo: runtime/go-rec-small.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-rec-small.lo -MD -MP -MF $(DEPDIR)/go-rec-small.Tpo -c -o go-rec-small.lo `test -f 'runtime/go-rec-small.c' || echo '$(srcdir)/'`runtime/go-rec-small.c
|
||||
@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/go-rec-small.Tpo $(DEPDIR)/go-rec-small.Plo
|
||||
@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='runtime/go-rec-small.c' object='go-rec-small.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-rec-small.lo `test -f 'runtime/go-rec-small.c' || echo '$(srcdir)/'`runtime/go-rec-small.c
|
||||
|
||||
go-recover.lo: runtime/go-recover.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-recover.lo -MD -MP -MF $(DEPDIR)/go-recover.Tpo -c -o go-recover.lo `test -f 'runtime/go-recover.c' || echo '$(srcdir)/'`runtime/go-recover.c
|
||||
@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/go-recover.Tpo $(DEPDIR)/go-recover.Plo
|
||||
@ -2890,13 +2795,6 @@ go-reflect-call.lo: runtime/go-reflect-call.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-reflect-call.lo `test -f 'runtime/go-reflect-call.c' || echo '$(srcdir)/'`runtime/go-reflect-call.c
|
||||
|
||||
go-reflect-chan.lo: runtime/go-reflect-chan.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-reflect-chan.lo -MD -MP -MF $(DEPDIR)/go-reflect-chan.Tpo -c -o go-reflect-chan.lo `test -f 'runtime/go-reflect-chan.c' || echo '$(srcdir)/'`runtime/go-reflect-chan.c
|
||||
@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/go-reflect-chan.Tpo $(DEPDIR)/go-reflect-chan.Plo
|
||||
@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='runtime/go-reflect-chan.c' object='go-reflect-chan.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-reflect-chan.lo `test -f 'runtime/go-reflect-chan.c' || echo '$(srcdir)/'`runtime/go-reflect-chan.c
|
||||
|
||||
go-reflect-map.lo: runtime/go-reflect-map.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-reflect-map.lo -MD -MP -MF $(DEPDIR)/go-reflect-map.Tpo -c -o go-reflect-map.lo `test -f 'runtime/go-reflect-map.c' || echo '$(srcdir)/'`runtime/go-reflect-map.c
|
||||
@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/go-reflect-map.Tpo $(DEPDIR)/go-reflect-map.Plo
|
||||
@ -2918,41 +2816,6 @@ go-runtime-error.lo: runtime/go-runtime-error.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-runtime-error.lo `test -f 'runtime/go-runtime-error.c' || echo '$(srcdir)/'`runtime/go-runtime-error.c
|
||||
|
||||
go-select.lo: runtime/go-select.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-select.lo -MD -MP -MF $(DEPDIR)/go-select.Tpo -c -o go-select.lo `test -f 'runtime/go-select.c' || echo '$(srcdir)/'`runtime/go-select.c
|
||||
@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/go-select.Tpo $(DEPDIR)/go-select.Plo
|
||||
@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='runtime/go-select.c' object='go-select.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-select.lo `test -f 'runtime/go-select.c' || echo '$(srcdir)/'`runtime/go-select.c
|
||||
|
||||
go-send-big.lo: runtime/go-send-big.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-send-big.lo -MD -MP -MF $(DEPDIR)/go-send-big.Tpo -c -o go-send-big.lo `test -f 'runtime/go-send-big.c' || echo '$(srcdir)/'`runtime/go-send-big.c
|
||||
@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/go-send-big.Tpo $(DEPDIR)/go-send-big.Plo
|
||||
@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='runtime/go-send-big.c' object='go-send-big.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-send-big.lo `test -f 'runtime/go-send-big.c' || echo '$(srcdir)/'`runtime/go-send-big.c
|
||||
|
||||
go-send-nb-big.lo: runtime/go-send-nb-big.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-send-nb-big.lo -MD -MP -MF $(DEPDIR)/go-send-nb-big.Tpo -c -o go-send-nb-big.lo `test -f 'runtime/go-send-nb-big.c' || echo '$(srcdir)/'`runtime/go-send-nb-big.c
|
||||
@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/go-send-nb-big.Tpo $(DEPDIR)/go-send-nb-big.Plo
|
||||
@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='runtime/go-send-nb-big.c' object='go-send-nb-big.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-send-nb-big.lo `test -f 'runtime/go-send-nb-big.c' || echo '$(srcdir)/'`runtime/go-send-nb-big.c
|
||||
|
||||
go-send-nb-small.lo: runtime/go-send-nb-small.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-send-nb-small.lo -MD -MP -MF $(DEPDIR)/go-send-nb-small.Tpo -c -o go-send-nb-small.lo `test -f 'runtime/go-send-nb-small.c' || echo '$(srcdir)/'`runtime/go-send-nb-small.c
|
||||
@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/go-send-nb-small.Tpo $(DEPDIR)/go-send-nb-small.Plo
|
||||
@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='runtime/go-send-nb-small.c' object='go-send-nb-small.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-send-nb-small.lo `test -f 'runtime/go-send-nb-small.c' || echo '$(srcdir)/'`runtime/go-send-nb-small.c
|
||||
|
||||
go-send-small.lo: runtime/go-send-small.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-send-small.lo -MD -MP -MF $(DEPDIR)/go-send-small.Tpo -c -o go-send-small.lo `test -f 'runtime/go-send-small.c' || echo '$(srcdir)/'`runtime/go-send-small.c
|
||||
@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/go-send-small.Tpo $(DEPDIR)/go-send-small.Plo
|
||||
@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='runtime/go-send-small.c' object='go-send-small.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-send-small.lo `test -f 'runtime/go-send-small.c' || echo '$(srcdir)/'`runtime/go-send-small.c
|
||||
|
||||
go-setenv.lo: runtime/go-setenv.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-setenv.lo -MD -MP -MF $(DEPDIR)/go-setenv.Tpo -c -o go-setenv.lo `test -f 'runtime/go-setenv.c' || echo '$(srcdir)/'`runtime/go-setenv.c
|
||||
@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/go-setenv.Tpo $(DEPDIR)/go-setenv.Plo
|
||||
@ -3093,6 +2956,13 @@ go-unwind.lo: runtime/go-unwind.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-unwind.lo `test -f 'runtime/go-unwind.c' || echo '$(srcdir)/'`runtime/go-unwind.c
|
||||
|
||||
chan.lo: runtime/chan.c
|
||||
@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT chan.lo -MD -MP -MF $(DEPDIR)/chan.Tpo -c -o chan.lo `test -f 'runtime/chan.c' || echo '$(srcdir)/'`runtime/chan.c
|
||||
@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/chan.Tpo $(DEPDIR)/chan.Plo
|
||||
@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='runtime/chan.c' object='chan.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 chan.lo `test -f 'runtime/chan.c' || echo '$(srcdir)/'`runtime/chan.c
|
||||
|
||||
cpuprof.lo: runtime/cpuprof.c
|
||||
@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT cpuprof.lo -MD -MP -MF $(DEPDIR)/cpuprof.Tpo -c -o cpuprof.lo `test -f 'runtime/cpuprof.c' || echo '$(srcdir)/'`runtime/cpuprof.c
|
||||
@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/cpuprof.Tpo $(DEPDIR)/cpuprof.Plo
|
||||
|
1248
libgo/runtime/chan.c
Normal file
1248
libgo/runtime/chan.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,57 +0,0 @@
|
||||
// Copyright 2010 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package runtime
|
||||
#include "config.h"
|
||||
#include "channel.h"
|
||||
|
||||
#define nil NULL
|
||||
|
||||
typedef _Bool bool;
|
||||
typedef unsigned char byte;
|
||||
typedef struct __go_channel chan;
|
||||
|
||||
/* Do a channel receive with closed status. */
|
||||
|
||||
func chanrecv2(c *chan, val *byte) (received bool) {
|
||||
uintptr_t element_size = c == nil ? 0 : c->element_type->__size;
|
||||
if (element_size > 8) {
|
||||
return __go_receive_big(c, val, 0);
|
||||
} else {
|
||||
union {
|
||||
char b[8];
|
||||
uint64_t v;
|
||||
} u;
|
||||
|
||||
u.v = __go_receive_small_closed(c, 0, &received);
|
||||
#ifndef WORDS_BIGENDIAN
|
||||
__builtin_memcpy(val, u.b, element_size);
|
||||
#else
|
||||
__builtin_memcpy(val, u.b + 8 - element_size, element_size);
|
||||
#endif
|
||||
return received;
|
||||
}
|
||||
}
|
||||
|
||||
/* Do a channel receive with closed status for a select statement. */
|
||||
|
||||
func chanrecv3(c *chan, val *byte) (received bool) {
|
||||
uintptr_t element_size = c->element_type->__size;
|
||||
if (element_size > 8) {
|
||||
return __go_receive_big(c, val, 1);
|
||||
} else {
|
||||
union {
|
||||
char b[8];
|
||||
uint64_t v;
|
||||
} u;
|
||||
|
||||
u.v = __go_receive_small_closed(c, 1, &received);
|
||||
#ifndef WORDS_BIGENDIAN
|
||||
__builtin_memcpy(val, u.b, element_size);
|
||||
#else
|
||||
__builtin_memcpy(val, u.b + 8 - element_size, element_size);
|
||||
#endif
|
||||
return received;
|
||||
}
|
||||
}
|
@ -1,152 +0,0 @@
|
||||
/* channel.h -- the channel type for Go.
|
||||
|
||||
Copyright 2009 The Go Authors. All rights reserved.
|
||||
Use of this source code is governed by a BSD-style
|
||||
license that can be found in the LICENSE file. */
|
||||
|
||||
#include <stdint.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include "go-type.h"
|
||||
|
||||
/* This structure is used when a select is waiting for a synchronous
|
||||
channel. */
|
||||
|
||||
struct __go_channel_select
|
||||
{
|
||||
/* A pointer to the next select waiting for this channel. */
|
||||
struct __go_channel_select *next;
|
||||
/* A pointer to the channel which this select will use. This starts
|
||||
out as NULL and is set to the first channel which synchs up with
|
||||
this one. This variable to which this points may only be
|
||||
accessed when __go_select_data_mutex is held. */
|
||||
struct __go_channel **selected;
|
||||
/* A pointer to a variable which must be set to true if the
|
||||
goroutine which sets *SELECTED wants to read from the channel,
|
||||
false if it wants to write to it. */
|
||||
_Bool *is_read;
|
||||
};
|
||||
|
||||
/* A channel is a pointer to this structure. */
|
||||
|
||||
struct __go_channel
|
||||
{
|
||||
/* A mutex to control access to the channel. */
|
||||
pthread_mutex_t lock;
|
||||
/* A condition variable. This is signalled when data is added to
|
||||
the channel and when data is removed from the channel. */
|
||||
pthread_cond_t cond;
|
||||
/* The type of elements on this channel. */
|
||||
const struct __go_type_descriptor *element_type;
|
||||
/* True if a goroutine is waiting to send on a synchronous
|
||||
channel. */
|
||||
_Bool waiting_to_send;
|
||||
/* True if a goroutine is waiting to receive on a synchronous
|
||||
channel. */
|
||||
_Bool waiting_to_receive;
|
||||
/* True if this channel was selected for send in a select statement.
|
||||
This looks out all other sends. */
|
||||
_Bool selected_for_send;
|
||||
/* True if this channel was selected for receive in a select
|
||||
statement. This locks out all other receives. */
|
||||
_Bool selected_for_receive;
|
||||
/* True if this channel has been closed. */
|
||||
_Bool is_closed;
|
||||
/* The list of select statements waiting to send on a synchronous
|
||||
channel. */
|
||||
struct __go_channel_select *select_send_queue;
|
||||
/* The list of select statements waiting to receive on a synchronous
|
||||
channel. */
|
||||
struct __go_channel_select *select_receive_queue;
|
||||
/* If a select statement is waiting for this channel, it sets these
|
||||
pointers. When something happens on the channel, the channel
|
||||
locks the mutex, signals the condition, and unlocks the
|
||||
mutex. */
|
||||
pthread_mutex_t *select_mutex;
|
||||
pthread_cond_t *select_cond;
|
||||
/* The number of entries in the circular buffer. */
|
||||
unsigned int num_entries;
|
||||
/* Where to store the next value. */
|
||||
unsigned int next_store;
|
||||
/* Where to fetch the next value. If next_fetch == next_store, the
|
||||
buffer is empty. If next_store + 1 == next_fetch, the buffer is
|
||||
full. */
|
||||
unsigned int next_fetch;
|
||||
/* The circular buffer. */
|
||||
uint64_t data[];
|
||||
};
|
||||
|
||||
/* Try to link up with the structure generated by the frontend. */
|
||||
typedef struct __go_channel __go_channel;
|
||||
|
||||
/* The mutex used to control access to the value pointed to by the
|
||||
__go_channel_select selected field. No additional mutexes may be
|
||||
acquired while this mutex is held. */
|
||||
extern pthread_mutex_t __go_select_data_mutex;
|
||||
|
||||
extern struct __go_channel *
|
||||
__go_new_channel (const struct __go_type_descriptor *, uintptr_t);
|
||||
|
||||
extern _Bool __go_synch_with_select (struct __go_channel *, _Bool);
|
||||
|
||||
extern void __go_broadcast_to_select (struct __go_channel *);
|
||||
|
||||
extern void __go_send_acquire (struct __go_channel *, _Bool);
|
||||
|
||||
extern _Bool __go_send_nonblocking_acquire (struct __go_channel *);
|
||||
|
||||
extern void __go_send_release (struct __go_channel *);
|
||||
|
||||
extern void __go_send_small (struct __go_channel *, uint64_t, _Bool);
|
||||
|
||||
extern _Bool __go_send_nonblocking_small (struct __go_channel *, uint64_t);
|
||||
|
||||
extern void __go_send_big (struct __go_channel *, const void *, _Bool);
|
||||
|
||||
extern _Bool __go_send_nonblocking_big (struct __go_channel *, const void *);
|
||||
|
||||
extern _Bool __go_receive_acquire (struct __go_channel *, _Bool);
|
||||
|
||||
#define RECEIVE_NONBLOCKING_ACQUIRE_DATA 0
|
||||
#define RECEIVE_NONBLOCKING_ACQUIRE_NODATA 1
|
||||
#define RECEIVE_NONBLOCKING_ACQUIRE_CLOSED 2
|
||||
|
||||
extern int __go_receive_nonblocking_acquire (struct __go_channel *);
|
||||
|
||||
extern uint64_t __go_receive_small (struct __go_channel *, _Bool);
|
||||
|
||||
extern uint64_t __go_receive_small_closed (struct __go_channel *, _Bool,
|
||||
_Bool *);
|
||||
|
||||
extern void __go_receive_release (struct __go_channel *);
|
||||
|
||||
struct __go_receive_nonblocking_small
|
||||
{
|
||||
/* Value read from channel, or 0. */
|
||||
uint64_t __val;
|
||||
/* True if value was read from channel. */
|
||||
_Bool __success;
|
||||
/* True if channel is closed. */
|
||||
_Bool __closed;
|
||||
};
|
||||
|
||||
extern struct __go_receive_nonblocking_small
|
||||
__go_receive_nonblocking_small (struct __go_channel *);
|
||||
|
||||
extern _Bool __go_receive_big (struct __go_channel *, void *, _Bool);
|
||||
|
||||
extern _Bool __go_receive_nonblocking_big (struct __go_channel *, void *,
|
||||
_Bool *);
|
||||
|
||||
extern void __go_unlock_and_notify_selects (struct __go_channel *);
|
||||
|
||||
extern _Bool __go_builtin_closed (struct __go_channel *);
|
||||
|
||||
extern void __go_builtin_close (struct __go_channel *);
|
||||
|
||||
extern int __go_chan_len (struct __go_channel *);
|
||||
|
||||
extern int __go_chan_cap (struct __go_channel *);
|
||||
|
||||
extern uintptr_t __go_select (uintptr_t, _Bool, struct __go_channel **,
|
||||
_Bool *);
|
@ -1,41 +0,0 @@
|
||||
/* go-chan-cap.c -- the cap function applied to a channel.
|
||||
|
||||
Copyright 2009 The Go Authors. All rights reserved.
|
||||
Use of this source code is governed by a BSD-style
|
||||
license that can be found in the LICENSE file. */
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "go-assert.h"
|
||||
#include "channel.h"
|
||||
|
||||
/* Return the cap function applied to a channel--the size of the
|
||||
buffer. This could be done inline but I'm doing it as a function
|
||||
for now to make it easy to change the channel structure. */
|
||||
|
||||
int
|
||||
__go_chan_cap (struct __go_channel *channel)
|
||||
{
|
||||
int i;
|
||||
int ret;
|
||||
|
||||
if (channel == NULL)
|
||||
return 0;
|
||||
|
||||
i = pthread_mutex_lock (&channel->lock);
|
||||
__go_assert (i == 0);
|
||||
|
||||
if (channel->num_entries == 0)
|
||||
ret = 0;
|
||||
else
|
||||
{
|
||||
/* One slot is always unused. We added 1 when we created the
|
||||
channel. */
|
||||
ret = channel->num_entries - 1;
|
||||
}
|
||||
|
||||
i = pthread_mutex_unlock (&channel->lock);
|
||||
__go_assert (i == 0);
|
||||
|
||||
return ret;
|
||||
}
|
@ -1,41 +0,0 @@
|
||||
/* go-chan-len.c -- the len function applied to a channel.
|
||||
|
||||
Copyright 2009 The Go Authors. All rights reserved.
|
||||
Use of this source code is governed by a BSD-style
|
||||
license that can be found in the LICENSE file. */
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "go-assert.h"
|
||||
#include "channel.h"
|
||||
|
||||
/* Return the len function applied to a channel--the number of
|
||||
elements in the buffer. This could be done inline but I'm doing it
|
||||
as a function for now to make it easy to change the channel
|
||||
structure. */
|
||||
|
||||
int
|
||||
__go_chan_len (struct __go_channel *channel)
|
||||
{
|
||||
int i;
|
||||
int ret;
|
||||
|
||||
if (channel == NULL)
|
||||
return 0;
|
||||
|
||||
i = pthread_mutex_lock (&channel->lock);
|
||||
__go_assert (i == 0);
|
||||
|
||||
if (channel->num_entries == 0)
|
||||
ret = 0;
|
||||
else if (channel->next_fetch == channel->next_store)
|
||||
ret = 0;
|
||||
else
|
||||
ret = ((channel->next_store + channel->num_entries - channel->next_fetch)
|
||||
% channel->num_entries);
|
||||
|
||||
i = pthread_mutex_unlock (&channel->lock);
|
||||
__go_assert (i == 0);
|
||||
|
||||
return ret;
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
/* go-close.c -- the builtin close function.
|
||||
|
||||
Copyright 2009 The Go Authors. All rights reserved.
|
||||
Use of this source code is governed by a BSD-style
|
||||
license that can be found in the LICENSE file. */
|
||||
|
||||
#include "runtime.h"
|
||||
#include "go-assert.h"
|
||||
#include "go-panic.h"
|
||||
#include "channel.h"
|
||||
|
||||
/* Close a channel. After a channel is closed, sends are no longer
|
||||
permitted. Receives always return zero. */
|
||||
|
||||
void
|
||||
__go_builtin_close (struct __go_channel *channel)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (channel == NULL)
|
||||
runtime_panicstring ("close of nil channel");
|
||||
|
||||
i = pthread_mutex_lock (&channel->lock);
|
||||
__go_assert (i == 0);
|
||||
|
||||
while (channel->selected_for_send)
|
||||
runtime_cond_wait (&channel->cond, &channel->lock);
|
||||
|
||||
if (channel->is_closed)
|
||||
{
|
||||
i = pthread_mutex_unlock (&channel->lock);
|
||||
__go_assert (i == 0);
|
||||
runtime_panicstring ("close of closed channel");
|
||||
}
|
||||
|
||||
channel->is_closed = 1;
|
||||
|
||||
i = pthread_cond_broadcast (&channel->cond);
|
||||
__go_assert (i == 0);
|
||||
|
||||
__go_unlock_and_notify_selects (channel);
|
||||
}
|
@ -1,70 +0,0 @@
|
||||
/* go-new-channel.c -- allocate a new channel.
|
||||
|
||||
Copyright 2009 The Go Authors. All rights reserved.
|
||||
Use of this source code is governed by a BSD-style
|
||||
license that can be found in the LICENSE file. */
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "runtime.h"
|
||||
#include "go-alloc.h"
|
||||
#include "go-assert.h"
|
||||
#include "channel.h"
|
||||
|
||||
struct __go_channel*
|
||||
__go_new_channel (const struct __go_type_descriptor *channel_type,
|
||||
uintptr_t entries)
|
||||
{
|
||||
const struct __go_channel_type *ctd;
|
||||
const struct __go_type_descriptor *element_type;
|
||||
uintptr_t element_size;
|
||||
int ientries;
|
||||
struct __go_channel* ret;
|
||||
size_t alloc_size;
|
||||
int i;
|
||||
|
||||
__go_assert (channel_type->__code == GO_CHAN);
|
||||
ctd = (const struct __go_channel_type *) channel_type;
|
||||
element_type = ctd->__element_type;
|
||||
|
||||
element_size = element_type->__size;
|
||||
|
||||
ientries = (int) entries;
|
||||
if (ientries < 0
|
||||
|| (uintptr_t) ientries != entries
|
||||
|| (element_size > 0 && entries > (uintptr_t) -1 / element_size))
|
||||
runtime_panicstring ("chan size out of range");
|
||||
|
||||
alloc_size = (element_size + sizeof (uint64_t) - 1) / sizeof (uint64_t);
|
||||
|
||||
/* We use a circular buffer which means that when next_fetch ==
|
||||
next_store we don't know whether the buffer is empty or full. So
|
||||
we allocate an extra space, and always leave a space open.
|
||||
FIXME. */
|
||||
if (entries != 0)
|
||||
++entries;
|
||||
|
||||
ret = (struct __go_channel*) __go_alloc (sizeof (struct __go_channel)
|
||||
+ ((entries == 0 ? 1 : entries)
|
||||
* alloc_size
|
||||
* sizeof (uint64_t)));
|
||||
i = pthread_mutex_init (&ret->lock, NULL);
|
||||
__go_assert (i == 0);
|
||||
i = pthread_cond_init (&ret->cond, NULL);
|
||||
__go_assert (i == 0);
|
||||
ret->element_type = element_type;
|
||||
ret->waiting_to_send = 0;
|
||||
ret->waiting_to_receive = 0;
|
||||
ret->selected_for_send = 0;
|
||||
ret->selected_for_receive = 0;
|
||||
ret->is_closed = 0;
|
||||
ret->select_send_queue = NULL;
|
||||
ret->select_receive_queue = NULL;
|
||||
ret->select_mutex = NULL;
|
||||
ret->select_cond = NULL;
|
||||
ret->num_entries = entries;
|
||||
ret->next_store = 0;
|
||||
ret->next_fetch = 0;
|
||||
return ret;
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
/* go-rec-big.c -- receive something larger than 64 bits on a channel.
|
||||
|
||||
Copyright 2009 The Go Authors. All rights reserved.
|
||||
Use of this source code is governed by a BSD-style
|
||||
license that can be found in the LICENSE file. */
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "go-panic.h"
|
||||
#include "channel.h"
|
||||
|
||||
/* Returns true if a value was received, false if the channel is
|
||||
closed. */
|
||||
|
||||
_Bool
|
||||
__go_receive_big (struct __go_channel *channel, void *val, _Bool for_select)
|
||||
{
|
||||
uintptr_t element_size;
|
||||
size_t alloc_size;
|
||||
size_t offset;
|
||||
|
||||
if (channel == NULL)
|
||||
{
|
||||
/* Block forever. */
|
||||
__go_select (0, 0, NULL, NULL);
|
||||
}
|
||||
|
||||
element_size = channel->element_type->__size;
|
||||
alloc_size = (element_size + sizeof (uint64_t) - 1) / sizeof (uint64_t);
|
||||
|
||||
if (!__go_receive_acquire (channel, for_select))
|
||||
{
|
||||
__builtin_memset (val, 0, element_size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
offset = channel->next_fetch * alloc_size;
|
||||
__builtin_memcpy (val, &channel->data[offset], element_size);
|
||||
|
||||
__go_receive_release (channel);
|
||||
|
||||
return 1;
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
/* go-rec-nb-big.c -- nonblocking receive of something big on a channel.
|
||||
|
||||
Copyright 2009 The Go Authors. All rights reserved.
|
||||
Use of this source code is governed by a BSD-style
|
||||
license that can be found in the LICENSE file. */
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "channel.h"
|
||||
|
||||
/* Return true if a value was received, false if not. */
|
||||
|
||||
_Bool
|
||||
__go_receive_nonblocking_big (struct __go_channel* channel, void *val,
|
||||
_Bool *closed)
|
||||
{
|
||||
uintptr_t element_size;
|
||||
size_t alloc_size;
|
||||
size_t offset;
|
||||
|
||||
if (channel == NULL)
|
||||
{
|
||||
if (closed != NULL)
|
||||
*closed = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
element_size = channel->element_type->__size;
|
||||
alloc_size = (element_size + sizeof (uint64_t) - 1) / sizeof (uint64_t);
|
||||
|
||||
int data = __go_receive_nonblocking_acquire (channel);
|
||||
if (data != RECEIVE_NONBLOCKING_ACQUIRE_DATA)
|
||||
{
|
||||
__builtin_memset (val, 0, element_size);
|
||||
if (closed != NULL)
|
||||
*closed = data == RECEIVE_NONBLOCKING_ACQUIRE_CLOSED;
|
||||
return 0;
|
||||
}
|
||||
|
||||
offset = channel->next_fetch * alloc_size;
|
||||
__builtin_memcpy (val, &channel->data[offset], element_size);
|
||||
|
||||
__go_receive_release (channel);
|
||||
|
||||
return 1;
|
||||
}
|
@ -1,123 +0,0 @@
|
||||
/* go-rec-nb-small.c -- nonblocking receive of something smal on a channel.
|
||||
|
||||
Copyright 2009 The Go Authors. All rights reserved.
|
||||
Use of this source code is governed by a BSD-style
|
||||
license that can be found in the LICENSE file. */
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "runtime.h"
|
||||
#include "go-assert.h"
|
||||
#include "go-panic.h"
|
||||
#include "channel.h"
|
||||
|
||||
/* Prepare to receive something on a nonblocking channel. */
|
||||
|
||||
int
|
||||
__go_receive_nonblocking_acquire (struct __go_channel *channel)
|
||||
{
|
||||
int i;
|
||||
_Bool has_data;
|
||||
|
||||
i = pthread_mutex_lock (&channel->lock);
|
||||
__go_assert (i == 0);
|
||||
|
||||
while (channel->selected_for_receive)
|
||||
runtime_cond_wait (&channel->cond, &channel->lock);
|
||||
|
||||
if (channel->is_closed
|
||||
&& (channel->num_entries == 0
|
||||
? channel->next_store == 0
|
||||
: channel->next_fetch == channel->next_store))
|
||||
{
|
||||
__go_unlock_and_notify_selects (channel);
|
||||
return RECEIVE_NONBLOCKING_ACQUIRE_CLOSED;
|
||||
}
|
||||
|
||||
if (channel->num_entries > 0)
|
||||
has_data = channel->next_fetch != channel->next_store;
|
||||
else
|
||||
{
|
||||
if (channel->waiting_to_receive)
|
||||
{
|
||||
/* Some other goroutine is already waiting for data on this
|
||||
channel, so we can't pick it up. */
|
||||
has_data = 0;
|
||||
}
|
||||
else if (channel->next_store > 0)
|
||||
{
|
||||
/* There is data on the channel. */
|
||||
has_data = 1;
|
||||
}
|
||||
else if (__go_synch_with_select (channel, 0))
|
||||
{
|
||||
/* We synched up with a select sending data, so there will
|
||||
be data for us shortly. Tell the select to go, and then
|
||||
wait for the data. */
|
||||
__go_broadcast_to_select (channel);
|
||||
|
||||
while (channel->next_store == 0)
|
||||
runtime_cond_wait (&channel->cond, &channel->lock);
|
||||
|
||||
has_data = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Otherwise there is no data. */
|
||||
has_data = 0;
|
||||
}
|
||||
|
||||
if (has_data)
|
||||
{
|
||||
channel->waiting_to_receive = 1;
|
||||
__go_assert (channel->next_store == 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (!has_data)
|
||||
{
|
||||
i = pthread_mutex_unlock (&channel->lock);
|
||||
__go_assert (i == 0);
|
||||
return RECEIVE_NONBLOCKING_ACQUIRE_NODATA;
|
||||
}
|
||||
|
||||
return RECEIVE_NONBLOCKING_ACQUIRE_DATA;
|
||||
}
|
||||
|
||||
/* Receive something 64 bits or smaller on a nonblocking channel. */
|
||||
|
||||
struct __go_receive_nonblocking_small
|
||||
__go_receive_nonblocking_small (struct __go_channel *channel)
|
||||
{
|
||||
uintptr_t element_size;
|
||||
struct __go_receive_nonblocking_small ret;
|
||||
|
||||
if (channel == NULL)
|
||||
{
|
||||
ret.__val = 0;
|
||||
ret.__success = 0;
|
||||
ret.__closed = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
element_size = channel->element_type->__size;
|
||||
__go_assert (element_size <= sizeof (uint64_t));
|
||||
|
||||
int data = __go_receive_nonblocking_acquire (channel);
|
||||
if (data != RECEIVE_NONBLOCKING_ACQUIRE_DATA)
|
||||
{
|
||||
ret.__val = 0;
|
||||
ret.__success = 0;
|
||||
ret.__closed = data == RECEIVE_NONBLOCKING_ACQUIRE_CLOSED;
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret.__val = channel->data[channel->next_fetch];
|
||||
|
||||
__go_receive_release (channel);
|
||||
|
||||
ret.__success = 1;
|
||||
ret.__closed = 0;
|
||||
|
||||
return ret;
|
||||
}
|
@ -1,304 +0,0 @@
|
||||
/* go-rec-small.c -- receive something smaller than 64 bits on a channel.
|
||||
|
||||
Copyright 2009 The Go Authors. All rights reserved.
|
||||
Use of this source code is governed by a BSD-style
|
||||
license that can be found in the LICENSE file. */
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "runtime.h"
|
||||
#include "go-assert.h"
|
||||
#include "go-panic.h"
|
||||
#include "channel.h"
|
||||
|
||||
/* This mutex controls access to the selected field of struct
|
||||
__go_channel_select. While this mutex is held, no other mutexes
|
||||
may be acquired. */
|
||||
|
||||
pthread_mutex_t __go_select_data_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
/* Try to synchronize with a select waiting on a sychronized channel.
|
||||
This is used by a send or receive. The channel is locked. This
|
||||
returns true if it was able to synch. */
|
||||
|
||||
_Bool
|
||||
__go_synch_with_select (struct __go_channel *channel, _Bool is_send)
|
||||
{
|
||||
struct __go_channel_select *p;
|
||||
int i;
|
||||
|
||||
__go_assert (channel->num_entries == 0);
|
||||
|
||||
i = pthread_mutex_lock (&__go_select_data_mutex);
|
||||
__go_assert (i == 0);
|
||||
|
||||
for (p = (is_send
|
||||
? channel->select_receive_queue
|
||||
: channel->select_send_queue);
|
||||
p != NULL;
|
||||
p = p->next)
|
||||
{
|
||||
if (*p->selected == NULL)
|
||||
{
|
||||
*p->selected = channel;
|
||||
*p->is_read = !is_send;
|
||||
if (is_send)
|
||||
channel->selected_for_receive = 1;
|
||||
else
|
||||
channel->selected_for_send = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
i = pthread_mutex_unlock (&__go_select_data_mutex);
|
||||
__go_assert (i == 0);
|
||||
|
||||
/* The caller is responsible for signalling the select condition
|
||||
variable so that the other select knows that something has
|
||||
changed. We can't signal it here because we can't acquire the
|
||||
select mutex while we hold a channel lock. */
|
||||
|
||||
return p != NULL;
|
||||
}
|
||||
|
||||
/* If we synch with a select, then we need to signal the select that
|
||||
something has changed. This requires grabbing the select mutex,
|
||||
which can only be done when the channel is unlocked. This routine
|
||||
does the signalling. It is called with the channel locked. It
|
||||
unlocks the channel, broadcasts the signal and relocks the
|
||||
channel. */
|
||||
|
||||
void
|
||||
__go_broadcast_to_select (struct __go_channel *channel)
|
||||
{
|
||||
pthread_mutex_t *select_mutex;
|
||||
pthread_cond_t *select_cond;
|
||||
int i;
|
||||
|
||||
select_mutex = channel->select_mutex;
|
||||
select_cond = channel->select_cond;
|
||||
|
||||
i = pthread_mutex_unlock (&channel->lock);
|
||||
__go_assert (i == 0);
|
||||
|
||||
__go_assert (select_mutex != NULL && select_cond != NULL);
|
||||
|
||||
i = pthread_mutex_lock (select_mutex);
|
||||
__go_assert (i == 0);
|
||||
|
||||
i = pthread_cond_broadcast (select_cond);
|
||||
__go_assert (i == 0);
|
||||
|
||||
i = pthread_mutex_unlock (select_mutex);
|
||||
__go_assert (i == 0);
|
||||
|
||||
i = pthread_mutex_lock (&channel->lock);
|
||||
__go_assert (i == 0);
|
||||
}
|
||||
|
||||
/* Prepare to receive something on a channel. Return true if the
|
||||
channel is acquired (which implies that there is data available),
|
||||
false if it is closed. */
|
||||
|
||||
_Bool
|
||||
__go_receive_acquire (struct __go_channel *channel, _Bool for_select)
|
||||
{
|
||||
int i;
|
||||
_Bool my_wait_lock;
|
||||
_Bool synched_with_select;
|
||||
|
||||
my_wait_lock = 0;
|
||||
synched_with_select = 0;
|
||||
|
||||
i = pthread_mutex_lock (&channel->lock);
|
||||
__go_assert (i == 0);
|
||||
|
||||
while (1)
|
||||
{
|
||||
_Bool need_broadcast;
|
||||
|
||||
need_broadcast = 0;
|
||||
|
||||
/* Check whether the channel is closed. */
|
||||
if (channel->is_closed
|
||||
&& (channel->num_entries == 0
|
||||
? channel->next_store == 0
|
||||
: channel->next_fetch == channel->next_store))
|
||||
{
|
||||
channel->selected_for_receive = 0;
|
||||
__go_unlock_and_notify_selects (channel);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* If somebody else has the channel locked for receiving, we
|
||||
have to wait. If FOR_SELECT is true, then we are the one
|
||||
with the lock. */
|
||||
if (!channel->selected_for_receive || for_select)
|
||||
{
|
||||
if (channel->num_entries == 0)
|
||||
{
|
||||
/* If somebody else is waiting to receive, we have to
|
||||
wait. */
|
||||
if (!channel->waiting_to_receive || my_wait_lock)
|
||||
{
|
||||
_Bool was_marked;
|
||||
|
||||
/* Lock the channel so that we get to receive
|
||||
next. */
|
||||
was_marked = channel->waiting_to_receive;
|
||||
channel->waiting_to_receive = 1;
|
||||
my_wait_lock = 1;
|
||||
|
||||
/* See if there is a value to receive. */
|
||||
if (channel->next_store > 0)
|
||||
return 1;
|
||||
|
||||
/* If we haven't already done so, try to synch with
|
||||
a select waiting to send on this channel. If we
|
||||
have already synched with a select, we are just
|
||||
looping until the select eventually causes
|
||||
something to be sent. */
|
||||
if (!synched_with_select && !for_select)
|
||||
{
|
||||
if (__go_synch_with_select (channel, 0))
|
||||
{
|
||||
synched_with_select = 1;
|
||||
need_broadcast = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* If we marked the channel as waiting, we need to
|
||||
signal, because something changed. It needs to
|
||||
be a broadcast since there might be other
|
||||
receivers waiting. */
|
||||
if (!was_marked)
|
||||
{
|
||||
i = pthread_cond_broadcast (&channel->cond);
|
||||
__go_assert (i == 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* If there is a value on the channel, we are OK. */
|
||||
if (channel->next_fetch != channel->next_store)
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* If we just synched with a select, then we need to signal the
|
||||
select condition variable. We can only do that if we unlock
|
||||
the channel. So we need to unlock, signal, lock, and go
|
||||
around the loop again without waiting. */
|
||||
if (need_broadcast)
|
||||
{
|
||||
__go_broadcast_to_select (channel);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Wait for something to change, then loop around and try
|
||||
again. */
|
||||
|
||||
runtime_cond_wait (&channel->cond, &channel->lock);
|
||||
}
|
||||
}
|
||||
|
||||
/* Finished receiving something on a channel. */
|
||||
|
||||
void
|
||||
__go_receive_release (struct __go_channel *channel)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (channel->num_entries != 0)
|
||||
channel->next_fetch = (channel->next_fetch + 1) % channel->num_entries;
|
||||
else
|
||||
{
|
||||
/* For a synchronous receiver, we tell the sender that we picked
|
||||
up the value by setting the next_store field back to 0.
|
||||
Using the mutexes should implement a memory barrier. */
|
||||
__go_assert (channel->next_store == 1);
|
||||
channel->next_store = 0;
|
||||
|
||||
channel->waiting_to_receive = 0;
|
||||
}
|
||||
|
||||
channel->selected_for_receive = 0;
|
||||
|
||||
/* This is a broadcast to make sure that a synchronous sender sees
|
||||
it. */
|
||||
i = pthread_cond_broadcast (&channel->cond);
|
||||
__go_assert (i == 0);
|
||||
|
||||
__go_unlock_and_notify_selects (channel);
|
||||
}
|
||||
|
||||
/* Unlock a channel and notify any waiting selects that something
|
||||
happened. */
|
||||
|
||||
void
|
||||
__go_unlock_and_notify_selects (struct __go_channel *channel)
|
||||
{
|
||||
pthread_mutex_t* select_mutex;
|
||||
pthread_cond_t* select_cond;
|
||||
int i;
|
||||
|
||||
select_mutex = channel->select_mutex;
|
||||
select_cond = channel->select_cond;
|
||||
|
||||
i = pthread_mutex_unlock (&channel->lock);
|
||||
__go_assert (i == 0);
|
||||
|
||||
if (select_mutex != NULL)
|
||||
{
|
||||
i = pthread_mutex_lock (select_mutex);
|
||||
__go_assert (i == 0);
|
||||
i = pthread_cond_broadcast (select_cond);
|
||||
__go_assert (i == 0);
|
||||
i = pthread_mutex_unlock (select_mutex);
|
||||
__go_assert (i == 0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Receive something 64 bits or smaller on a channel. */
|
||||
|
||||
uint64_t
|
||||
__go_receive_small_closed (struct __go_channel *channel, _Bool for_select,
|
||||
_Bool *received)
|
||||
{
|
||||
uintptr_t element_size;
|
||||
uint64_t ret;
|
||||
|
||||
if (channel == NULL)
|
||||
{
|
||||
/* Block forever. */
|
||||
__go_select (0, 0, NULL, NULL);
|
||||
}
|
||||
|
||||
element_size = channel->element_type->__size;
|
||||
__go_assert (element_size <= sizeof (uint64_t));
|
||||
|
||||
if (!__go_receive_acquire (channel, for_select))
|
||||
{
|
||||
if (received != NULL)
|
||||
*received = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = channel->data[channel->next_fetch];
|
||||
|
||||
__go_receive_release (channel);
|
||||
|
||||
if (received != NULL)
|
||||
*received = 1;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Called by the compiler. */
|
||||
|
||||
uint64_t
|
||||
__go_receive_small (struct __go_channel *channel, _Bool for_select)
|
||||
{
|
||||
return __go_receive_small_closed (channel, for_select, NULL);
|
||||
}
|
@ -1,200 +0,0 @@
|
||||
/* go-reflect-chan.c -- channel reflection support for Go.
|
||||
|
||||
Copyright 2009 The Go Authors. All rights reserved.
|
||||
Use of this source code is governed by a BSD-style
|
||||
license that can be found in the LICENSE file. */
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "go-alloc.h"
|
||||
#include "go-assert.h"
|
||||
#include "go-panic.h"
|
||||
#include "go-type.h"
|
||||
#include "channel.h"
|
||||
|
||||
/* This file implements support for reflection on channels. These
|
||||
functions are called from reflect/value.go. */
|
||||
|
||||
extern uintptr_t makechan (const struct __go_type_descriptor *, uint32_t)
|
||||
asm ("libgo_reflect.reflect.makechan");
|
||||
|
||||
uintptr_t
|
||||
makechan (const struct __go_type_descriptor *typ, uint32_t size)
|
||||
{
|
||||
struct __go_channel *channel;
|
||||
void *ret;
|
||||
|
||||
channel = __go_new_channel (typ, size);
|
||||
|
||||
ret = __go_alloc (sizeof (void *));
|
||||
__builtin_memcpy (ret, &channel, sizeof (void *));
|
||||
return (uintptr_t) ret;
|
||||
}
|
||||
|
||||
extern _Bool chansend (struct __go_channel_type *, uintptr_t, uintptr_t, _Bool)
|
||||
asm ("libgo_reflect.reflect.chansend");
|
||||
|
||||
_Bool
|
||||
chansend (struct __go_channel_type *ct, uintptr_t ch, uintptr_t val_i,
|
||||
_Bool nb)
|
||||
{
|
||||
struct __go_channel *channel = (struct __go_channel *) ch;
|
||||
uintptr_t element_size;
|
||||
void *pv;
|
||||
|
||||
__go_assert (ct->__common.__code == GO_CHAN);
|
||||
|
||||
if (__go_is_pointer_type (ct->__element_type))
|
||||
pv = &val_i;
|
||||
else
|
||||
pv = (void *) val_i;
|
||||
|
||||
element_size = ct->__element_type->__size;
|
||||
if (element_size <= sizeof (uint64_t))
|
||||
{
|
||||
union
|
||||
{
|
||||
char b[sizeof (uint64_t)];
|
||||
uint64_t v;
|
||||
} u;
|
||||
|
||||
__builtin_memset (u.b, 0, sizeof (uint64_t));
|
||||
#ifndef WORDS_BIGENDIAN
|
||||
__builtin_memcpy (u.b, pv, element_size);
|
||||
#else
|
||||
__builtin_memcpy (u.b + sizeof (uint64_t) - element_size, pv,
|
||||
element_size);
|
||||
#endif
|
||||
if (nb)
|
||||
return __go_send_nonblocking_small (channel, u.v);
|
||||
else
|
||||
{
|
||||
__go_send_small (channel, u.v, 0);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (nb)
|
||||
return __go_send_nonblocking_big (channel, pv);
|
||||
else
|
||||
{
|
||||
__go_send_big (channel, pv, 0);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct chanrecv_ret
|
||||
{
|
||||
uintptr_t val;
|
||||
_Bool selected;
|
||||
_Bool received;
|
||||
};
|
||||
|
||||
extern struct chanrecv_ret chanrecv (struct __go_channel_type *, uintptr_t,
|
||||
_Bool)
|
||||
asm ("libgo_reflect.reflect.chanrecv");
|
||||
|
||||
struct chanrecv_ret
|
||||
chanrecv (struct __go_channel_type *ct, uintptr_t ch, _Bool nb)
|
||||
{
|
||||
struct __go_channel *channel = (struct __go_channel *) ch;
|
||||
void *pv;
|
||||
uintptr_t element_size;
|
||||
struct chanrecv_ret ret;
|
||||
|
||||
__go_assert (ct->__common.__code == GO_CHAN);
|
||||
|
||||
element_size = ct->__element_type->__size;
|
||||
|
||||
if (__go_is_pointer_type (ct->__element_type))
|
||||
pv = &ret.val;
|
||||
else
|
||||
{
|
||||
pv = __go_alloc (element_size);
|
||||
ret.val = (uintptr_t) pv;
|
||||
}
|
||||
|
||||
if (element_size <= sizeof (uint64_t))
|
||||
{
|
||||
union
|
||||
{
|
||||
char b[sizeof (uint64_t)];
|
||||
uint64_t v;
|
||||
} u;
|
||||
|
||||
if (!nb)
|
||||
{
|
||||
u.v = __go_receive_small_closed (channel, 0, &ret.received);
|
||||
ret.selected = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
struct __go_receive_nonblocking_small s;
|
||||
|
||||
s = __go_receive_nonblocking_small (channel);
|
||||
ret.selected = s.__success || s.__closed;
|
||||
ret.received = s.__success;
|
||||
u.v = s.__val;
|
||||
}
|
||||
|
||||
#ifndef WORDS_BIGENDIAN
|
||||
__builtin_memcpy (pv, u.b, element_size);
|
||||
#else
|
||||
__builtin_memcpy (pv, u.b + sizeof (uint64_t) - element_size,
|
||||
element_size);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!nb)
|
||||
{
|
||||
ret.received = __go_receive_big (channel, pv, 0);
|
||||
ret.selected = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
_Bool got;
|
||||
_Bool closed;
|
||||
|
||||
got = __go_receive_nonblocking_big (channel, pv, &closed);
|
||||
ret.selected = got || closed;
|
||||
ret.received = got;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
extern void chanclose (uintptr_t) asm ("libgo_reflect.reflect.chanclose");
|
||||
|
||||
void
|
||||
chanclose (uintptr_t ch)
|
||||
{
|
||||
struct __go_channel *channel = (struct __go_channel *) ch;
|
||||
|
||||
__go_builtin_close (channel);
|
||||
}
|
||||
|
||||
extern int32_t chanlen (uintptr_t) asm ("libgo_reflect.reflect.chanlen");
|
||||
|
||||
int32_t
|
||||
chanlen (uintptr_t ch)
|
||||
{
|
||||
struct __go_channel *channel = (struct __go_channel *) ch;
|
||||
|
||||
return (int32_t) __go_chan_len (channel);
|
||||
}
|
||||
|
||||
extern int32_t chancap (uintptr_t) asm ("libgo_reflect.reflect.chancap");
|
||||
|
||||
int32_t
|
||||
chancap (uintptr_t ch)
|
||||
{
|
||||
struct __go_channel *channel = (struct __go_channel *) ch;
|
||||
|
||||
return (int32_t) __go_chan_cap (channel);
|
||||
}
|
@ -1,758 +0,0 @@
|
||||
/* go-select.c -- implement select.
|
||||
|
||||
Copyright 2009 The Go Authors. All rights reserved.
|
||||
Use of this source code is governed by a BSD-style
|
||||
license that can be found in the LICENSE file. */
|
||||
|
||||
#include <pthread.h>
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "runtime.h"
|
||||
#include "config.h"
|
||||
#include "go-assert.h"
|
||||
#include "channel.h"
|
||||
|
||||
/* __go_select builds an array of these structures. */
|
||||
|
||||
struct select_channel
|
||||
{
|
||||
/* The channel being selected. */
|
||||
struct __go_channel* channel;
|
||||
/* If this channel is selected, the value to return. */
|
||||
uintptr_t retval;
|
||||
/* If this channel is a duplicate of one which appears earlier in
|
||||
the array, this is the array index of the earlier channel. This
|
||||
is -1UL if this is not a dup. */
|
||||
uintptr_t dup_index;
|
||||
/* An entry to put on the send or receive queue. */
|
||||
struct __go_channel_select queue_entry;
|
||||
/* True if selected for send. */
|
||||
_Bool is_send;
|
||||
/* True if channel is ready--it has data to receive or space to
|
||||
send. */
|
||||
_Bool is_ready;
|
||||
};
|
||||
|
||||
/* This mutex controls access to __go_select_cond. This mutex may not
|
||||
be acquired if any channel locks are held. */
|
||||
|
||||
static pthread_mutex_t __go_select_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
/* When we have to wait for channels, we tell them to trigger this
|
||||
condition variable when they send or receive something. */
|
||||
|
||||
static pthread_cond_t __go_select_cond = PTHREAD_COND_INITIALIZER;
|
||||
|
||||
/* Sort the channels by address. This avoids deadlock when multiple
|
||||
selects are running on overlapping sets of channels. */
|
||||
|
||||
static int
|
||||
channel_sort (const void *p1, const void *p2)
|
||||
{
|
||||
const struct select_channel *c1 = (const struct select_channel *) p1;
|
||||
const struct select_channel *c2 = (const struct select_channel *) p2;
|
||||
|
||||
if ((uintptr_t) c1->channel < (uintptr_t) c2->channel)
|
||||
return -1;
|
||||
else if ((uintptr_t) c1->channel > (uintptr_t) c2->channel)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Return whether there is an entry on QUEUE which can be used for a
|
||||
synchronous send or receive. */
|
||||
|
||||
static _Bool
|
||||
is_queue_ready (struct __go_channel_select *queue)
|
||||
{
|
||||
int x;
|
||||
|
||||
if (queue == NULL)
|
||||
return 0;
|
||||
|
||||
x = pthread_mutex_lock (&__go_select_data_mutex);
|
||||
__go_assert (x == 0);
|
||||
|
||||
while (queue != NULL)
|
||||
{
|
||||
if (*queue->selected == NULL)
|
||||
break;
|
||||
queue = queue->next;
|
||||
}
|
||||
|
||||
x = pthread_mutex_unlock (&__go_select_data_mutex);
|
||||
__go_assert (x == 0);
|
||||
|
||||
return queue != NULL;
|
||||
}
|
||||
|
||||
/* Return whether CHAN is ready. If IS_SEND is true check whether it
|
||||
has space to send, otherwise check whether it has a value to
|
||||
receive. */
|
||||
|
||||
static _Bool
|
||||
is_channel_ready (struct __go_channel* channel, _Bool is_send)
|
||||
{
|
||||
if (is_send)
|
||||
{
|
||||
if (channel->selected_for_send)
|
||||
return 0;
|
||||
if (channel->is_closed)
|
||||
return 1;
|
||||
if (channel->num_entries > 0)
|
||||
{
|
||||
/* An asynchronous channel is ready for sending if there is
|
||||
room in the buffer. */
|
||||
return ((channel->next_store + 1) % channel->num_entries
|
||||
!= channel->next_fetch);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (channel->waiting_to_send)
|
||||
{
|
||||
/* Some other goroutine is waiting to send on this
|
||||
channel, so we can't. */
|
||||
return 0;
|
||||
}
|
||||
if (channel->waiting_to_receive)
|
||||
{
|
||||
/* Some other goroutine is waiting to receive a value,
|
||||
so we can send one. */
|
||||
return 1;
|
||||
}
|
||||
if (is_queue_ready (channel->select_receive_queue))
|
||||
{
|
||||
/* There is a select statement waiting to synchronize
|
||||
with this one. */
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (channel->selected_for_receive)
|
||||
return 0;
|
||||
if (channel->is_closed)
|
||||
return 1;
|
||||
if (channel->num_entries > 0)
|
||||
{
|
||||
/* An asynchronous channel is ready for receiving if there
|
||||
is a value in the buffer. */
|
||||
return channel->next_fetch != channel->next_store;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (channel->waiting_to_receive)
|
||||
{
|
||||
/* Some other goroutine is waiting to receive from this
|
||||
channel, so it is not ready for us to receive. */
|
||||
return 0;
|
||||
}
|
||||
if (channel->next_store > 0)
|
||||
{
|
||||
/* There is data on the channel. */
|
||||
return 1;
|
||||
}
|
||||
if (is_queue_ready (channel->select_send_queue))
|
||||
{
|
||||
/* There is a select statement waiting to synchronize
|
||||
with this one. */
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Mark a channel as selected. The channel is locked. IS_SELECTED is
|
||||
true if the channel was selected for us by another goroutine. We
|
||||
set *NEEDS_BROADCAST if we need to broadcast on the select
|
||||
condition variable. Return true if we got it. */
|
||||
|
||||
static _Bool
|
||||
mark_channel_selected (struct __go_channel *channel, _Bool is_send,
|
||||
_Bool is_selected, _Bool *needs_broadcast)
|
||||
{
|
||||
if (channel->num_entries == 0)
|
||||
{
|
||||
/* This is a synchronous channel. If there is no goroutine
|
||||
currently waiting, but there is another select waiting, then
|
||||
we need to tell that select to use this channel. That may
|
||||
fail--there may be no other goroutines currently waiting--as
|
||||
a third goroutine may already have claimed the select. */
|
||||
if (!is_selected
|
||||
&& !channel->is_closed
|
||||
&& (is_send
|
||||
? !channel->waiting_to_receive
|
||||
: channel->next_store == 0))
|
||||
{
|
||||
int x;
|
||||
struct __go_channel_select *queue;
|
||||
|
||||
x = pthread_mutex_lock (&__go_select_data_mutex);
|
||||
__go_assert (x == 0);
|
||||
|
||||
queue = (is_send
|
||||
? channel->select_receive_queue
|
||||
: channel->select_send_queue);
|
||||
__go_assert (queue != NULL);
|
||||
|
||||
while (queue != NULL)
|
||||
{
|
||||
if (*queue->selected == NULL)
|
||||
{
|
||||
*queue->selected = channel;
|
||||
*queue->is_read = !is_send;
|
||||
break;
|
||||
}
|
||||
queue = queue->next;
|
||||
}
|
||||
|
||||
x = pthread_mutex_unlock (&__go_select_data_mutex);
|
||||
__go_assert (x == 0);
|
||||
|
||||
if (queue == NULL)
|
||||
return 0;
|
||||
|
||||
if (is_send)
|
||||
channel->selected_for_receive = 1;
|
||||
else
|
||||
channel->selected_for_send = 1;
|
||||
|
||||
/* We are going to have to tell the other select that there
|
||||
is something to do. */
|
||||
*needs_broadcast = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_send)
|
||||
channel->selected_for_send = 1;
|
||||
else
|
||||
channel->selected_for_receive = 1;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Mark a channel to indicate that a select is waiting. The channel
|
||||
is locked. */
|
||||
|
||||
static void
|
||||
mark_select_waiting (struct select_channel *sc,
|
||||
struct __go_channel **selected_pointer,
|
||||
_Bool *selected_for_read_pointer)
|
||||
{
|
||||
struct __go_channel *channel = sc->channel;
|
||||
_Bool is_send = sc->is_send;
|
||||
|
||||
if (channel->num_entries == 0)
|
||||
{
|
||||
struct __go_channel_select **pp;
|
||||
|
||||
pp = (is_send
|
||||
? &channel->select_send_queue
|
||||
: &channel->select_receive_queue);
|
||||
|
||||
/* Add an entry to the queue of selects on this channel. */
|
||||
sc->queue_entry.next = *pp;
|
||||
sc->queue_entry.selected = selected_pointer;
|
||||
sc->queue_entry.is_read = selected_for_read_pointer;
|
||||
|
||||
*pp = &sc->queue_entry;
|
||||
}
|
||||
|
||||
channel->select_mutex = &__go_select_mutex;
|
||||
channel->select_cond = &__go_select_cond;
|
||||
|
||||
/* We never actually clear the select_mutex and select_cond fields.
|
||||
In order to clear them safely, we would need to have some way of
|
||||
knowing when no select is waiting for the channel. Thus we
|
||||
introduce a bit of inefficiency for every channel that select
|
||||
needs to wait for. This is harmless other than the performance
|
||||
cost. */
|
||||
}
|
||||
|
||||
/* Remove the entry for this select waiting on this channel. The
|
||||
channel is locked. We check both queues, because the channel may
|
||||
be selected for both reading and writing. */
|
||||
|
||||
static void
|
||||
clear_select_waiting (struct select_channel *sc,
|
||||
struct __go_channel **selected_pointer)
|
||||
{
|
||||
struct __go_channel *channel = sc->channel;
|
||||
|
||||
if (channel->num_entries == 0)
|
||||
{
|
||||
_Bool found;
|
||||
struct __go_channel_select **pp;
|
||||
|
||||
found = 0;
|
||||
|
||||
for (pp = &channel->select_send_queue; *pp != NULL; pp = &(*pp)->next)
|
||||
{
|
||||
if ((*pp)->selected == selected_pointer)
|
||||
{
|
||||
*pp = (*pp)->next;
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (pp = &channel->select_receive_queue; *pp != NULL; pp = &(*pp)->next)
|
||||
{
|
||||
if ((*pp)->selected == selected_pointer)
|
||||
{
|
||||
*pp = (*pp)->next;
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
__go_assert (found);
|
||||
}
|
||||
}
|
||||
|
||||
/* Look through the list of channels to see which ones are ready.
|
||||
Lock each channels, and set the is_ready flag. Return the number
|
||||
of ready channels. */
|
||||
|
||||
static uintptr_t
|
||||
lock_channels_find_ready (struct select_channel *channels, uintptr_t count)
|
||||
{
|
||||
uintptr_t ready_count;
|
||||
uintptr_t i;
|
||||
|
||||
ready_count = 0;
|
||||
for (i = 0; i < count; ++i)
|
||||
{
|
||||
struct __go_channel *channel = channels[i].channel;
|
||||
_Bool is_send = channels[i].is_send;
|
||||
uintptr_t dup_index = channels[i].dup_index;
|
||||
int x;
|
||||
|
||||
if (channel == NULL)
|
||||
continue;
|
||||
|
||||
if (dup_index != (uintptr_t) -1UL)
|
||||
{
|
||||
if (channels[dup_index].is_ready)
|
||||
{
|
||||
channels[i].is_ready = 1;
|
||||
++ready_count;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
x = pthread_mutex_lock (&channel->lock);
|
||||
__go_assert (x == 0);
|
||||
|
||||
if (is_channel_ready (channel, is_send))
|
||||
{
|
||||
channels[i].is_ready = 1;
|
||||
++ready_count;
|
||||
}
|
||||
}
|
||||
|
||||
return ready_count;
|
||||
}
|
||||
|
||||
/* The channel we are going to select has been forced by some other
|
||||
goroutine. SELECTED_CHANNEL is the channel we will use,
|
||||
SELECTED_FOR_READ is whether the other goroutine wants to read from
|
||||
the channel. Note that the channel could be specified multiple
|
||||
times in this select, so we must mark each appropriate entry for
|
||||
this channel as ready. Every other channel is marked as not ready.
|
||||
All the channels are locked before this routine is called. This
|
||||
returns the number of ready channels. */
|
||||
|
||||
uintptr_t
|
||||
force_selected_channel_ready (struct select_channel *channels, uintptr_t count,
|
||||
struct __go_channel *selected_channel,
|
||||
_Bool selected_for_read)
|
||||
{
|
||||
uintptr_t ready_count;
|
||||
uintptr_t i;
|
||||
|
||||
ready_count = 0;
|
||||
for (i = 0; i < count; ++i)
|
||||
{
|
||||
struct __go_channel *channel = channels[i].channel;
|
||||
_Bool is_send = channels[i].is_send;
|
||||
|
||||
if (channel == NULL)
|
||||
continue;
|
||||
|
||||
if (channel != selected_channel
|
||||
|| (is_send ? !selected_for_read : selected_for_read))
|
||||
channels[i].is_ready = 0;
|
||||
else
|
||||
{
|
||||
channels[i].is_ready = 1;
|
||||
++ready_count;
|
||||
}
|
||||
}
|
||||
__go_assert (ready_count > 0);
|
||||
return ready_count;
|
||||
}
|
||||
|
||||
/* Unlock all the channels. */
|
||||
|
||||
static void
|
||||
unlock_channels (struct select_channel *channels, uintptr_t count)
|
||||
{
|
||||
uintptr_t i;
|
||||
int x;
|
||||
|
||||
for (i = 0; i < count; ++i)
|
||||
{
|
||||
struct __go_channel *channel = channels[i].channel;
|
||||
|
||||
if (channel == NULL)
|
||||
continue;
|
||||
|
||||
if (channels[i].dup_index != (uintptr_t) -1UL)
|
||||
continue;
|
||||
|
||||
x = pthread_mutex_unlock (&channel->lock);
|
||||
__go_assert (x == 0);
|
||||
}
|
||||
}
|
||||
|
||||
/* At least one channel is ready. Randomly pick a channel to return.
|
||||
Unlock all the channels. IS_SELECTED is true if the channel was
|
||||
picked for us by some other goroutine. If SELECTED_POINTER is not
|
||||
NULL, remove it from the queue for all the channels. Return the
|
||||
retval field of the selected channel. This will return 0 if we
|
||||
can't use the selected channel, because it relied on synchronizing
|
||||
with some other select, and that select already synchronized with a
|
||||
different channel. */
|
||||
|
||||
static uintptr_t
|
||||
unlock_channels_and_select (struct select_channel *channels,
|
||||
uintptr_t count, uintptr_t ready_count,
|
||||
_Bool is_selected,
|
||||
struct __go_channel **selected_pointer)
|
||||
{
|
||||
uintptr_t selected;
|
||||
uintptr_t ret;
|
||||
_Bool needs_broadcast;
|
||||
uintptr_t i;
|
||||
int x;
|
||||
|
||||
/* Pick which channel we are going to return. */
|
||||
#if defined(HAVE_RANDOM)
|
||||
selected = (uintptr_t) random () % ready_count;
|
||||
#else
|
||||
selected = (uintptr_t) rand () % ready_count;
|
||||
#endif
|
||||
ret = 0;
|
||||
needs_broadcast = 0;
|
||||
|
||||
/* Look at the channels in reverse order so that we don't unlock a
|
||||
duplicated channel until we have seen all its dups. */
|
||||
for (i = 0; i < count; ++i)
|
||||
{
|
||||
uintptr_t j = count - i - 1;
|
||||
struct __go_channel *channel = channels[j].channel;
|
||||
_Bool is_send = channels[j].is_send;
|
||||
|
||||
if (channel == NULL)
|
||||
continue;
|
||||
|
||||
if (channels[j].is_ready)
|
||||
{
|
||||
if (selected == 0)
|
||||
{
|
||||
if (mark_channel_selected (channel, is_send, is_selected,
|
||||
&needs_broadcast))
|
||||
ret = channels[j].retval;
|
||||
}
|
||||
|
||||
--selected;
|
||||
}
|
||||
|
||||
if (channels[j].dup_index == (uintptr_t) -1UL)
|
||||
{
|
||||
if (selected_pointer != NULL)
|
||||
clear_select_waiting (&channels[j], selected_pointer);
|
||||
|
||||
x = pthread_mutex_unlock (&channel->lock);
|
||||
__go_assert (x == 0);
|
||||
}
|
||||
}
|
||||
|
||||
/* The NEEDS_BROADCAST variable is set if we are synchronizing with
|
||||
some other select statement. We can't do the actual broadcast
|
||||
until we have unlocked all the channels. */
|
||||
|
||||
if (needs_broadcast)
|
||||
{
|
||||
x = pthread_mutex_lock (&__go_select_mutex);
|
||||
__go_assert (x == 0);
|
||||
|
||||
x = pthread_cond_broadcast (&__go_select_cond);
|
||||
__go_assert (x == 0);
|
||||
|
||||
x = pthread_mutex_unlock (&__go_select_mutex);
|
||||
__go_assert (x == 0);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Mark all channels to show that we are waiting for them. This is
|
||||
called with the select mutex held, but none of the channels are
|
||||
locked. This returns true if some channel was found to be
|
||||
ready. */
|
||||
|
||||
static _Bool
|
||||
mark_all_channels_waiting (struct select_channel* channels, uintptr_t count,
|
||||
struct __go_channel **selected_pointer,
|
||||
_Bool *selected_for_read_pointer)
|
||||
{
|
||||
_Bool ret;
|
||||
int x;
|
||||
uintptr_t i;
|
||||
|
||||
ret = 0;
|
||||
for (i = 0; i < count; ++i)
|
||||
{
|
||||
struct __go_channel *channel = channels[i].channel;
|
||||
_Bool is_send = channels[i].is_send;
|
||||
|
||||
if (channel == NULL)
|
||||
continue;
|
||||
|
||||
if (channels[i].dup_index != (uintptr_t) -1UL)
|
||||
{
|
||||
uintptr_t j;
|
||||
|
||||
/* A channel may be selected for both read and write. */
|
||||
if (channels[channels[i].dup_index].is_send == is_send)
|
||||
continue;
|
||||
else
|
||||
{
|
||||
for (j = channels[i].dup_index + 1; j < i; ++j)
|
||||
{
|
||||
if (channels[j].channel == channel
|
||||
&& channels[j].is_send == is_send)
|
||||
break;
|
||||
}
|
||||
if (j < i)
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
x = pthread_mutex_lock (&channel->lock);
|
||||
__go_assert (x == 0);
|
||||
|
||||
/* To avoid a race condition, we have to check again whether the
|
||||
channel is ready. It may have become ready since we did the
|
||||
first set of checks but before we acquired the select mutex.
|
||||
If we don't check here, we could sleep forever on the select
|
||||
condition variable. */
|
||||
if (is_channel_ready (channel, is_send))
|
||||
ret = 1;
|
||||
|
||||
/* If SELECTED_POINTER is NULL, then we have already marked the
|
||||
channel as waiting. */
|
||||
if (selected_pointer != NULL)
|
||||
mark_select_waiting (&channels[i], selected_pointer,
|
||||
selected_for_read_pointer);
|
||||
|
||||
x = pthread_mutex_unlock (&channel->lock);
|
||||
__go_assert (x == 0);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Implement select. This is called by the compiler-generated code
|
||||
with pairs of arguments: a pointer to a channel, and an int which
|
||||
is non-zero for send, zero for receive. */
|
||||
|
||||
uintptr_t
|
||||
__go_select (uintptr_t count, _Bool has_default,
|
||||
struct __go_channel **channel_args, _Bool *is_send_args)
|
||||
{
|
||||
struct select_channel stack_buffer[16];
|
||||
struct select_channel *allocated_buffer;
|
||||
struct select_channel *channels;
|
||||
uintptr_t i;
|
||||
int x;
|
||||
struct __go_channel *selected_channel;
|
||||
_Bool selected_for_read;
|
||||
_Bool is_queued;
|
||||
|
||||
if (count < sizeof stack_buffer / sizeof stack_buffer[0])
|
||||
{
|
||||
channels = &stack_buffer[0];
|
||||
allocated_buffer = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
allocated_buffer = ((struct select_channel *)
|
||||
malloc (count * sizeof (struct select_channel)));
|
||||
channels = allocated_buffer;
|
||||
}
|
||||
|
||||
for (i = 0; i < count; ++i)
|
||||
{
|
||||
struct __go_channel *channel_arg = channel_args[i];
|
||||
_Bool is_send = is_send_args[i];
|
||||
|
||||
channels[i].channel = (struct __go_channel*) channel_arg;
|
||||
channels[i].retval = i + 1;
|
||||
channels[i].dup_index = (uintptr_t) -1UL;
|
||||
channels[i].queue_entry.next = NULL;
|
||||
channels[i].queue_entry.selected = NULL;
|
||||
channels[i].is_send = is_send;
|
||||
channels[i].is_ready = 0;
|
||||
}
|
||||
|
||||
qsort (channels, count, sizeof (struct select_channel), channel_sort);
|
||||
|
||||
for (i = 0; i < count; ++i)
|
||||
{
|
||||
uintptr_t j;
|
||||
|
||||
for (j = 0; j < i; ++j)
|
||||
{
|
||||
if (channels[j].channel == channels[i].channel)
|
||||
{
|
||||
channels[i].dup_index = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* SELECT_CHANNEL is used to select synchronized channels. If no
|
||||
channels are ready, we store a pointer to this variable on the
|
||||
select queue for each synchronized channel. Because the variable
|
||||
may be set by channel operations running in other goroutines,
|
||||
SELECT_CHANNEL may only be accessed when all the channels are
|
||||
locked and/or when the select_data_mutex is locked. */
|
||||
selected_channel = NULL;
|
||||
|
||||
/* SELECTED_FOR_READ is set to true if SELECTED_CHANNEL was set by a
|
||||
goroutine which wants to read from the channel. The access
|
||||
restrictions for this are like those for SELECTED_CHANNEL. */
|
||||
selected_for_read = 0;
|
||||
|
||||
/* IS_QUEUED is true if we have queued up this select on the queues
|
||||
for any associated synchronous channels. We only do this if no
|
||||
channels are ready the first time around the loop. */
|
||||
is_queued = 0;
|
||||
|
||||
while (1)
|
||||
{
|
||||
int ready_count;
|
||||
_Bool is_selected;
|
||||
|
||||
/* Lock all channels, identify which ones are ready. */
|
||||
ready_count = lock_channels_find_ready (channels, count);
|
||||
|
||||
/* All the channels are locked, so we can look at
|
||||
SELECTED_CHANNEL. If it is not NULL, then our choice has
|
||||
been forced by some other goroutine. This can only happen
|
||||
after the first time through the loop. */
|
||||
is_selected = selected_channel != NULL;
|
||||
if (is_selected)
|
||||
ready_count = force_selected_channel_ready (channels, count,
|
||||
selected_channel,
|
||||
selected_for_read);
|
||||
|
||||
if (ready_count > 0)
|
||||
{
|
||||
uintptr_t ret;
|
||||
|
||||
ret = unlock_channels_and_select (channels, count, ready_count,
|
||||
is_selected,
|
||||
(is_queued
|
||||
? &selected_channel
|
||||
: NULL));
|
||||
|
||||
/* If RET is zero, it means that the channel we picked
|
||||
turned out not to be ready, because some other select
|
||||
grabbed it during our traversal. Loop around and try
|
||||
again. */
|
||||
if (ret == 0)
|
||||
{
|
||||
is_queued = 0;
|
||||
/* We are no longer on any channel queues, so it is safe
|
||||
to touch SELECTED_CHANNEL here. It must be NULL,
|
||||
because otherwise that would somebody has promised to
|
||||
synch up with us and then failed to do so. */
|
||||
__go_assert (selected_channel == NULL);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (allocated_buffer != NULL)
|
||||
free (allocated_buffer);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* No channels were ready. */
|
||||
|
||||
unlock_channels (channels, count);
|
||||
|
||||
if (has_default)
|
||||
{
|
||||
/* Use the default clause. */
|
||||
if (allocated_buffer != NULL)
|
||||
free (allocated_buffer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This is a blocking select. Grab the select lock, tell all
|
||||
the channels to notify us when something happens, and wait
|
||||
for something to happen. */
|
||||
|
||||
x = pthread_mutex_lock (&__go_select_mutex);
|
||||
__go_assert (x == 0);
|
||||
|
||||
/* Check whether CHANNEL_SELECTED was set while the channels
|
||||
were unlocked. If it was set, then we can simply loop around
|
||||
again. We need to check this while the select mutex is held.
|
||||
It is possible that something will set CHANNEL_SELECTED while
|
||||
we mark the channels as waiting. If this happens, that
|
||||
goroutine is required to signal the select condition
|
||||
variable, which means acquiring the select mutex. Since we
|
||||
have the select mutex locked ourselves, we can not miss that
|
||||
signal. */
|
||||
|
||||
x = pthread_mutex_lock (&__go_select_data_mutex);
|
||||
__go_assert (x == 0);
|
||||
|
||||
is_selected = selected_channel != NULL;
|
||||
|
||||
x = pthread_mutex_unlock (&__go_select_data_mutex);
|
||||
__go_assert (x == 0);
|
||||
|
||||
if (!is_selected)
|
||||
{
|
||||
/* Mark the channels as waiting, and check whether they have
|
||||
become ready. */
|
||||
if (!mark_all_channels_waiting (channels, count,
|
||||
(is_queued
|
||||
? NULL
|
||||
: &selected_channel),
|
||||
(is_queued
|
||||
? NULL
|
||||
: &selected_for_read)))
|
||||
runtime_cond_wait (&__go_select_cond, &__go_select_mutex);
|
||||
|
||||
is_queued = 1;
|
||||
}
|
||||
|
||||
x = pthread_mutex_unlock (&__go_select_mutex);
|
||||
__go_assert (x == 0);
|
||||
}
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
/* go-send-big.c -- send something bigger than uint64_t on a channel.
|
||||
|
||||
Copyright 2009 The Go Authors. All rights reserved.
|
||||
Use of this source code is governed by a BSD-style
|
||||
license that can be found in the LICENSE file. */
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "go-panic.h"
|
||||
#include "channel.h"
|
||||
|
||||
void
|
||||
__go_send_big (struct __go_channel* channel, const void *val, _Bool for_select)
|
||||
{
|
||||
uintptr_t element_size;
|
||||
size_t alloc_size;
|
||||
size_t offset;
|
||||
|
||||
if (channel == NULL)
|
||||
{
|
||||
// Block forever.
|
||||
__go_select (0, 0, NULL, NULL);
|
||||
}
|
||||
|
||||
element_size = channel->element_type->__size;
|
||||
alloc_size = (element_size + sizeof (uint64_t) - 1) / sizeof (uint64_t);
|
||||
|
||||
__go_send_acquire (channel, for_select);
|
||||
|
||||
offset = channel->next_store * alloc_size;
|
||||
__builtin_memcpy (&channel->data[offset], val, element_size);
|
||||
|
||||
__go_send_release (channel);
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
/* go-send-nb-big.c -- nonblocking send of something big on a channel.
|
||||
|
||||
Copyright 2009 The Go Authors. All rights reserved.
|
||||
Use of this source code is governed by a BSD-style
|
||||
license that can be found in the LICENSE file. */
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "channel.h"
|
||||
|
||||
_Bool
|
||||
__go_send_nonblocking_big (struct __go_channel* channel, const void *val)
|
||||
{
|
||||
uintptr_t element_size;
|
||||
size_t alloc_size;
|
||||
size_t offset;
|
||||
|
||||
if (channel == NULL)
|
||||
return 0;
|
||||
|
||||
element_size = channel->element_type->__size;
|
||||
alloc_size = (element_size + sizeof (uint64_t) - 1) / sizeof (uint64_t);
|
||||
|
||||
if (!__go_send_nonblocking_acquire (channel))
|
||||
return 0;
|
||||
|
||||
offset = channel->next_store * alloc_size;
|
||||
__builtin_memcpy (&channel->data[offset], val, element_size);
|
||||
|
||||
__go_send_release (channel);
|
||||
|
||||
return 1;
|
||||
}
|
@ -1,107 +0,0 @@
|
||||
/* go-send-nb-small.c -- nonblocking send of something small on a channel.
|
||||
|
||||
Copyright 2009 The Go Authors. All rights reserved.
|
||||
Use of this source code is governed by a BSD-style
|
||||
license that can be found in the LICENSE file. */
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "runtime.h"
|
||||
#include "go-assert.h"
|
||||
#include "go-panic.h"
|
||||
#include "channel.h"
|
||||
|
||||
/* Prepare to send something on a nonblocking channel. Return true if
|
||||
we acquired the channel, false if we did not acquire it because
|
||||
there is no space to send a value. */
|
||||
|
||||
_Bool
|
||||
__go_send_nonblocking_acquire (struct __go_channel *channel)
|
||||
{
|
||||
int i;
|
||||
_Bool has_space;
|
||||
|
||||
i = pthread_mutex_lock (&channel->lock);
|
||||
__go_assert (i == 0);
|
||||
|
||||
while (channel->selected_for_send)
|
||||
runtime_cond_wait (&channel->cond, &channel->lock);
|
||||
|
||||
if (channel->is_closed)
|
||||
{
|
||||
i = pthread_mutex_unlock (&channel->lock);
|
||||
__go_assert (i == 0);
|
||||
runtime_panicstring ("send on closed channel");
|
||||
}
|
||||
|
||||
if (channel->num_entries > 0)
|
||||
has_space = ((channel->next_store + 1) % channel->num_entries
|
||||
!= channel->next_fetch);
|
||||
else
|
||||
{
|
||||
/* This is a synchronous channel. If somebody is current
|
||||
sending, then we can't send. Otherwise, see if somebody is
|
||||
waiting to receive, or see if we can synch with a select. */
|
||||
if (channel->waiting_to_send)
|
||||
{
|
||||
/* Some other goroutine is currently sending on this
|
||||
channel, which means that we can't. */
|
||||
has_space = 0;
|
||||
}
|
||||
else if (channel->waiting_to_receive)
|
||||
{
|
||||
/* Some other goroutine is waiting to receive a value, so we
|
||||
can send directly to them. */
|
||||
has_space = 1;
|
||||
}
|
||||
else if (__go_synch_with_select (channel, 1))
|
||||
{
|
||||
/* We found a select waiting to receive data, so we can send
|
||||
to that. */
|
||||
__go_broadcast_to_select (channel);
|
||||
has_space = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Otherwise, we can't send, because nobody is waiting to
|
||||
receive. */
|
||||
has_space = 0;
|
||||
}
|
||||
|
||||
if (has_space)
|
||||
{
|
||||
channel->waiting_to_send = 1;
|
||||
__go_assert (channel->next_store == 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (!has_space)
|
||||
{
|
||||
i = pthread_mutex_unlock (&channel->lock);
|
||||
__go_assert (i == 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Send something 64 bits or smaller on a channel. */
|
||||
|
||||
_Bool
|
||||
__go_send_nonblocking_small (struct __go_channel *channel, uint64_t val)
|
||||
{
|
||||
if (channel == NULL)
|
||||
return 0;
|
||||
|
||||
__go_assert (channel->element_type->__size <= sizeof (uint64_t));
|
||||
|
||||
if (!__go_send_nonblocking_acquire (channel))
|
||||
return 0;
|
||||
|
||||
channel->data[channel->next_store] = val;
|
||||
|
||||
__go_send_release (channel);
|
||||
|
||||
return 1;
|
||||
}
|
@ -1,159 +0,0 @@
|
||||
/* go-send-small.c -- send something 64 bits or smaller on a channel.
|
||||
|
||||
Copyright 2009 The Go Authors. All rights reserved.
|
||||
Use of this source code is governed by a BSD-style
|
||||
license that can be found in the LICENSE file. */
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "runtime.h"
|
||||
#include "go-assert.h"
|
||||
#include "go-panic.h"
|
||||
#include "channel.h"
|
||||
|
||||
/* Prepare to send something on a channel. FOR_SELECT is true if this
|
||||
call is being made after a select statement returned with this
|
||||
channel selected. */
|
||||
|
||||
void
|
||||
__go_send_acquire (struct __go_channel *channel, _Bool for_select)
|
||||
{
|
||||
int i;
|
||||
|
||||
i = pthread_mutex_lock (&channel->lock);
|
||||
__go_assert (i == 0);
|
||||
|
||||
while (1)
|
||||
{
|
||||
if (channel->is_closed)
|
||||
{
|
||||
if (for_select)
|
||||
channel->selected_for_send = 0;
|
||||
i = pthread_mutex_unlock (&channel->lock);
|
||||
__go_assert (i == 0);
|
||||
runtime_panicstring ("send on closed channel");
|
||||
}
|
||||
|
||||
/* If somebody else has the channel locked for sending, we have
|
||||
to wait. If FOR_SELECT is true, then we are the one with the
|
||||
lock. */
|
||||
if (!channel->selected_for_send || for_select)
|
||||
{
|
||||
if (channel->num_entries == 0)
|
||||
{
|
||||
/* This is a synchronous channel. If nobody else is
|
||||
waiting to send, we grab the channel and tell the
|
||||
caller to send the data. We will then wait for a
|
||||
receiver. */
|
||||
if (!channel->waiting_to_send)
|
||||
{
|
||||
__go_assert (channel->next_store == 0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* If there is room on the channel, we are OK. */
|
||||
if ((channel->next_store + 1) % channel->num_entries
|
||||
!= channel->next_fetch)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Wait for something to change, then loop around and try
|
||||
again. */
|
||||
|
||||
runtime_cond_wait (&channel->cond, &channel->lock);
|
||||
}
|
||||
}
|
||||
|
||||
/* Finished sending something on a channel. */
|
||||
|
||||
void
|
||||
__go_send_release (struct __go_channel *channel)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (channel->num_entries != 0)
|
||||
{
|
||||
/* This is a buffered channel. Bump the store count and signal
|
||||
the condition variable. */
|
||||
channel->next_store = (channel->next_store + 1) % channel->num_entries;
|
||||
|
||||
i = pthread_cond_signal (&channel->cond);
|
||||
__go_assert (i == 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
_Bool synched_with_select;
|
||||
|
||||
/* This is a synchronous channel. Indicate that we have a value
|
||||
waiting. */
|
||||
channel->next_store = 1;
|
||||
channel->waiting_to_send = 1;
|
||||
|
||||
/* Tell everybody else to do something. This has to be a
|
||||
broadcast because we might have both senders and receivers
|
||||
waiting on the condition, but senders won't send another
|
||||
signal. */
|
||||
i = pthread_cond_broadcast (&channel->cond);
|
||||
__go_assert (i == 0);
|
||||
|
||||
/* Wait until the value is received. */
|
||||
synched_with_select = 0;
|
||||
while (1)
|
||||
{
|
||||
if (channel->next_store == 0)
|
||||
break;
|
||||
|
||||
/* If nobody is currently waiting to receive, try to synch
|
||||
up with a select. */
|
||||
if (!channel->waiting_to_receive && !synched_with_select)
|
||||
{
|
||||
if (__go_synch_with_select (channel, 1))
|
||||
{
|
||||
synched_with_select = 1;
|
||||
__go_broadcast_to_select (channel);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
runtime_cond_wait (&channel->cond, &channel->lock);
|
||||
}
|
||||
|
||||
channel->waiting_to_send = 0;
|
||||
|
||||
/* Using the mutexes should implement a memory barrier. */
|
||||
|
||||
/* We have to signal again since we cleared the waiting_to_send
|
||||
field. This has to be a broadcast because both senders and
|
||||
receivers might be waiting, but only senders will be able to
|
||||
act. */
|
||||
i = pthread_cond_broadcast (&channel->cond);
|
||||
__go_assert (i == 0);
|
||||
}
|
||||
|
||||
channel->selected_for_send = 0;
|
||||
|
||||
__go_unlock_and_notify_selects (channel);
|
||||
}
|
||||
|
||||
/* Send something 64 bits or smaller on a channel. */
|
||||
|
||||
void
|
||||
__go_send_small (struct __go_channel *channel, uint64_t val, _Bool for_select)
|
||||
{
|
||||
if (channel == NULL)
|
||||
{
|
||||
// Block forever.
|
||||
__go_select (0, 0, NULL, NULL);
|
||||
}
|
||||
|
||||
__go_assert (channel->element_type->__size <= sizeof (uint64_t));
|
||||
|
||||
__go_send_acquire (channel, for_select);
|
||||
|
||||
channel->data[channel->next_store] = val;
|
||||
|
||||
__go_send_release (channel);
|
||||
}
|
@ -55,14 +55,15 @@ typedef struct M M;
|
||||
typedef union Note Note;
|
||||
typedef struct MCache MCache;
|
||||
typedef struct FixAlloc FixAlloc;
|
||||
typedef struct Hchan Hchan;
|
||||
|
||||
typedef struct __go_defer_stack Defer;
|
||||
typedef struct __go_open_array Slice;
|
||||
typedef struct __go_string String;
|
||||
typedef struct __go_interface Iface;
|
||||
typedef struct __go_empty_interface Eface;
|
||||
typedef struct __go_type_descriptor Type;
|
||||
typedef struct __go_defer_stack Defer;
|
||||
typedef struct __go_panic_stack Panic;
|
||||
typedef struct __go_open_array Slice;
|
||||
typedef struct __go_string String;
|
||||
|
||||
typedef struct __go_func_type FuncType;
|
||||
typedef struct __go_map_type MapType;
|
||||
@ -131,6 +132,7 @@ struct G
|
||||
bool fromgogo; // reached from gogo
|
||||
int16 status;
|
||||
int32 goid;
|
||||
uint32 selgen; // valid sudog pointer
|
||||
const char* waitreason; // if status==Gwaiting
|
||||
G* schedlink;
|
||||
bool readyonstop;
|
||||
|
Loading…
x
Reference in New Issue
Block a user