aboutsummaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
Diffstat (limited to 'gcc')
-rw-r--r--gcc/go/gofrontend/expressions.cc9
-rw-r--r--gcc/go/gofrontend/expressions.h9
-rw-r--r--gcc/go/gofrontend/gogo-tree.cc27
-rw-r--r--gcc/go/gofrontend/gogo.h7
-rw-r--r--gcc/go/gofrontend/parse.cc2
-rw-r--r--gcc/go/gofrontend/runtime.cc16
-rw-r--r--gcc/go/gofrontend/runtime.def50
-rw-r--r--gcc/go/gofrontend/runtime.h5
-rw-r--r--gcc/go/gofrontend/statements.cc544
-rw-r--r--gcc/go/gofrontend/statements.h83
10 files changed, 341 insertions, 411 deletions
diff --git a/gcc/go/gofrontend/expressions.cc b/gcc/go/gofrontend/expressions.cc
index a2cf33e..a80c823 100644
--- a/gcc/go/gofrontend/expressions.cc
+++ b/gcc/go/gofrontend/expressions.cc
@@ -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.
diff --git a/gcc/go/gofrontend/expressions.h b/gcc/go/gofrontend/expressions.h
index 74d1281..6da507b 100644
--- a/gcc/go/gofrontend/expressions.h
+++ b/gcc/go/gofrontend/expressions.h
@@ -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)
diff --git a/gcc/go/gofrontend/gogo-tree.cc b/gcc/go/gofrontend/gogo-tree.cc
index 0e77f5d..dd66a7f 100644
--- a/gcc/go/gofrontend/gogo-tree.cc
+++ b/gcc/go/gofrontend/gogo-tree.cc
@@ -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
diff --git a/gcc/go/gofrontend/gogo.h b/gcc/go/gofrontend/gogo.h
index 9d3b37a..6efce18 100644
--- a/gcc/go/gofrontend/gogo.h
+++ b/gcc/go/gofrontend/gogo.h
@@ -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);
diff --git a/gcc/go/gofrontend/parse.cc b/gcc/go/gofrontend/parse.cc
index 6228868..37a9782 100644
--- a/gcc/go/gofrontend/parse.cc
+++ b/gcc/go/gofrontend/parse.cc
@@ -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);
}
diff --git a/gcc/go/gofrontend/runtime.cc b/gcc/go/gofrontend/runtime.cc
index 42f1e78..bffefbb 100644
--- a/gcc/go/gofrontend/runtime.cc
+++ b/gcc/go/gofrontend/runtime.cc
@@ -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);
-}
diff --git a/gcc/go/gofrontend/runtime.def b/gcc/go/gofrontend/runtime.def
index d742e5b..fe842c9 100644
--- a/gcc/go/gofrontend/runtime.def
+++ b/gcc/go/gofrontend/runtime.def
@@ -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))
+
+
+// Start building a select statement.
+DEF_GO_RUNTIME(NEWSELECT, "runtime.newselect", P1(INT), R1(POINTER))
-// Receive a value from a channel returning whether it is closed, for select.
-DEF_GO_RUNTIME(CHANRECV3, "runtime.chanrecv3", P2(CHAN, POINTER), R1(BOOL))
+// 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))
diff --git a/gcc/go/gofrontend/runtime.h b/gcc/go/gofrontend/runtime.h
index 3cd4034..be5dcbe 100644
--- a/gcc/go/gofrontend/runtime.h
+++ b/gcc/go/gofrontend/runtime.h
@@ -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);
diff --git a/gcc/go/gofrontend/statements.cc b/gcc/go/gofrontend/statements.cc
index 835a0cc..964b394 100644
--- a/gcc/go/gofrontend/statements.cc
+++ b/gcc/go/gofrontend/statements.cc
@@ -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,134 +4487,178 @@ 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);
-
- // 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);
- }
+ Expression* chanref = Expression::make_temporary_reference(channel_temp,
+ loc);
- // 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);
+ this->lower_send(b, selref, chanref, index_expr);
+ else
+ this->lower_recv(gogo, function, b, selref, chanref, index_expr);
- // We have to put S in STATEMENTS_, because that is where the
- // variables are declared.
+ // Now all references should be handled through the statements, not
+ // through here.
+ this->is_lowered_ = true;
+ this->val_ = NULL;
+ this->var_ = NULL;
+}
- go_assert(this->statements_ != NULL);
+// Lower a default clause in a select statement.
- // Skip the variable declaration statements themselves.
- size_t skip = 1;
- if (this->var_ != NULL)
- skip = 2;
+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));
+}
- // 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);
+// Lower a send clause in a select statement.
- this->statements_->insert_statement_before(skip, s);
+void
+Select_clauses::Select_clause::lower_send(Block* b, Expression* selref,
+ Expression* chanref,
+ Expression* index_expr)
+{
+ Location loc = this->location_;
- // We have to lower STATEMENTS_ again, to lower the tuple
- // receive assignment we just added.
- gogo->lower_block(function, this->statements_);
- }
+ 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
{
- 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));
- }
+ 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);
}
- // Lower any statements we just created.
- gogo->lower_block(function, init);
+ b->add_statement(Statement::make_statement(call, true));
- if (this->statements_ != NULL)
- init->add_statement(Statement::make_block_statement(this->statements_,
- loc));
+ // If the block of statements is executed, arrange for the received
+ // value to move from VAL to the place where the statements expect
+ // it.
- this->statements_ = init;
+ Block* init = NULL;
- // Now all references should be handled through the statements, not
- // through here.
- this->is_lowered_ = true;
- this->val_ = NULL;
- this->var_ = 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.
@@ -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;
- }
-
- 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();
- }
-
- Expression* c = p->channel();
- c = Expression::make_unsafe_cast(runtime_chan_type, c, p->location());
- chan_init->push_back(c);
+ 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)));
- is_send_init->push_back(Expression::make_boolean(p->is_send(),
- p->location()));
- }
+ 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);
- 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);
diff --git a/gcc/go/gofrontend/statements.h b/gcc/go/gofrontend/statements.h
index 16914f1..4548ba6 100644
--- a/gcc/go/gofrontend/statements.h
+++ b/gcc/go/gofrontend/statements.h
@@ -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.