diff options
Diffstat (limited to 'gcc')
-rw-r--r-- | gcc/go/gofrontend/expressions.cc | 9 | ||||
-rw-r--r-- | gcc/go/gofrontend/expressions.h | 9 | ||||
-rw-r--r-- | gcc/go/gofrontend/gogo-tree.cc | 27 | ||||
-rw-r--r-- | gcc/go/gofrontend/gogo.h | 7 | ||||
-rw-r--r-- | gcc/go/gofrontend/parse.cc | 2 | ||||
-rw-r--r-- | gcc/go/gofrontend/runtime.cc | 16 | ||||
-rw-r--r-- | gcc/go/gofrontend/runtime.def | 50 | ||||
-rw-r--r-- | gcc/go/gofrontend/runtime.h | 5 | ||||
-rw-r--r-- | gcc/go/gofrontend/statements.cc | 544 | ||||
-rw-r--r-- | gcc/go/gofrontend/statements.h | 83 |
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. |