From 84cdf51de42f823b4ed0d65ef20ab0142607917b Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Thu, 13 Sep 2018 21:32:24 +0000 Subject: [PATCH] compiler, runtime: open code select This is the gofrontend version of https://golang.org/cl/37933, https://golang.org/cl/37934, and https://golang.org/cl/37935. Open code the initialization of select cases. This is a step toward updating libgo to the 1.11 release. Reviewed-on: https://go-review.googlesource.com/135000 From-SVN: r264290 --- gcc/go/gofrontend/MERGE | 2 +- gcc/go/gofrontend/escape.cc | 25 +-- gcc/go/gofrontend/runtime.def | 20 +-- gcc/go/gofrontend/statements.cc | 232 ++++++++++++++++++---------- gcc/go/gofrontend/statements.h | 50 ++++-- gcc/go/gofrontend/types.cc | 46 ++---- gcc/go/gofrontend/types.h | 2 +- libgo/go/runtime/select.go | 266 ++++++++------------------------ 8 files changed, 278 insertions(+), 365 deletions(-) diff --git a/gcc/go/gofrontend/MERGE b/gcc/go/gofrontend/MERGE index 3d640b2dc08..6650b03ec67 100644 --- a/gcc/go/gofrontend/MERGE +++ b/gcc/go/gofrontend/MERGE @@ -1,4 +1,4 @@ -f68c03e509b26e7f483f2800eb70a5fbf3f74d0b +f2cd046a4e0d681c3d21ee547b437d3eab8af268 The first line of this file holds the git revision number of the last merge done from the gofrontend repository. diff --git a/gcc/go/gofrontend/escape.cc b/gcc/go/gofrontend/escape.cc index e32ae5b475c..9c31525eb95 100644 --- a/gcc/go/gofrontend/escape.cc +++ b/gcc/go/gofrontend/escape.cc @@ -1361,7 +1361,13 @@ Escape_analysis_assign::statement(Block*, size_t*, Statement* s) { Expression* init = s->temporary_statement()->init(); if (init != NULL) - this->assign(Node::make_node(s), Node::make_node(init)); + { + Node* n = Node::make_node(init); + if (s->temporary_statement()->value_escapes()) + this->assign(this->context_->sink(), n); + else + this->assign(Node::make_node(s), n); + } } break; @@ -1616,15 +1622,6 @@ Escape_analysis_assign::expression(Expression** pexpr) } break; - case Runtime::SELECTSEND: - { - // Send to a channel, lose track. The last argument is - // the address of the value to send. - Node* arg_node = Node::make_node(call->args()->back()); - this->assign_deref(this->context_->sink(), arg_node); - } - break; - case Runtime::IFACEE2T2: case Runtime::IFACEI2T2: { @@ -2228,8 +2225,12 @@ Escape_analysis_assign::assign(Node* dst, Node* src) case Expression::EXPRESSION_TEMPORARY_REFERENCE: { // Temporary is tracked through the underlying Temporary_statement. - Statement* t = dst->expr()->temporary_reference_expression()->statement(); - dst = Node::make_node(t); + Temporary_statement* t = + dst->expr()->temporary_reference_expression()->statement(); + if (t->value_escapes()) + dst = this->context_->sink(); + else + dst = Node::make_node(t); } break; diff --git a/gcc/go/gofrontend/runtime.def b/gcc/go/gofrontend/runtime.def index fdb159e5505..1486cd85f27 100644 --- a/gcc/go/gofrontend/runtime.def +++ b/gcc/go/gofrontend/runtime.def @@ -152,22 +152,10 @@ DEF_GO_RUNTIME(CHANRECV1, "runtime.chanrecv1", P2(CHAN, POINTER), R0()) DEF_GO_RUNTIME(CHANRECV2, "runtime.chanrecv2", P2(CHAN, POINTER), R1(BOOL)) -// Start building a select statement. -DEF_GO_RUNTIME(NEWSELECT, "runtime.newselect", P3(POINTER, INT64, INT32), R0()) - -// Add a default clause to a select statement. -DEF_GO_RUNTIME(SELECTDEFAULT, "runtime.selectdefault", P1(POINTER), R0()) - -// Add a send clause to a select statement. -DEF_GO_RUNTIME(SELECTSEND, "runtime.selectsend", P3(POINTER, CHAN, POINTER), - R0()) - -// Add a receive clause to a select statement. -DEF_GO_RUNTIME(SELECTRECV, "runtime.selectrecv", - P4(POINTER, CHAN, POINTER, BOOLPTR), R0()) - -// Run a select, returning the index of the selected clause. -DEF_GO_RUNTIME(SELECTGO, "runtime.selectgo", P1(POINTER), R1(INT)) +// Run a select, returning the index of the selected clause and +// whether that channel received a value. +DEF_GO_RUNTIME(SELECTGO, "runtime.selectgo", P3(POINTER, POINTER, INT), + R2(INT, BOOL)) // Panic. diff --git a/gcc/go/gofrontend/statements.cc b/gcc/go/gofrontend/statements.cc index 19a07d462ba..1df676f53c2 100644 --- a/gcc/go/gofrontend/statements.cc +++ b/gcc/go/gofrontend/statements.cc @@ -4548,17 +4548,19 @@ Select_clauses::Select_clause::traverse(Traverse* traverse) void Select_clauses::Select_clause::lower(Gogo* gogo, Named_object* function, - Block* b, Temporary_statement* sel) + Block* b, Temporary_statement* scases, + size_t index, Temporary_statement* recvok) { Location loc = this->location_; - Expression* selref = Expression::make_temporary_reference(sel, loc); - selref = Expression::make_unary(OPERATOR_AND, selref, loc); + Expression* scase = Expression::make_temporary_reference(scases, loc); + Expression* index_expr = Expression::make_integer_ul(index, NULL, loc); + scase = Expression::make_array_index(scase, index_expr, NULL, NULL, loc); if (this->is_default_) { go_assert(this->channel_ == NULL && this->val_ == NULL); - this->lower_default(b, selref); + this->lower_default(b, scase); this->is_lowered_ = true; return; } @@ -4572,9 +4574,9 @@ Select_clauses::Select_clause::lower(Gogo* gogo, Named_object* function, loc); if (this->is_send_) - this->lower_send(b, selref, chanref); + this->lower_send(b, scase, chanref); else - this->lower_recv(gogo, function, b, selref, chanref); + this->lower_recv(gogo, function, b, scase, chanref, recvok); // Now all references should be handled through the statements, not // through here. @@ -4585,18 +4587,16 @@ Select_clauses::Select_clause::lower(Gogo* gogo, Named_object* function, // Lower a default clause in a select statement. void -Select_clauses::Select_clause::lower_default(Block* b, Expression* selref) +Select_clauses::Select_clause::lower_default(Block* b, Expression* scase) { Location loc = this->location_; - Expression* call = Runtime::make_call(Runtime::SELECTDEFAULT, loc, 1, - selref); - b->add_statement(Statement::make_statement(call, true)); + this->set_case(b, scase, Expression::make_nil(loc), NULL, caseDefault); } // Lower a send clause in a select statement. void -Select_clauses::Select_clause::lower_send(Block* b, Expression* selref, +Select_clauses::Select_clause::lower_send(Block* b, Expression* scase, Expression* chanref) { Location loc = this->location_; @@ -4611,22 +4611,29 @@ Select_clauses::Select_clause::lower_send(Block* b, Expression* selref, // evaluate the send values in the required order. Temporary_statement* val = Statement::make_temporary(valtype, this->val_, loc); + // The value here escapes, because it might be sent on a channel. + // We record that via the Temporary_statement, so that the escape + // analysis pass can pick it up. The gc compiler lowers select + // statements after escape analysis, so it doesn't have to worry + // about this. + val->set_value_escapes(); b->add_statement(val); Expression* valref = Expression::make_temporary_reference(val, loc); Expression* valaddr = Expression::make_unary(OPERATOR_AND, valref, loc); + Type* unsafe_pointer_type = Type::make_pointer_type(Type::make_void_type()); + valaddr = Expression::make_cast(unsafe_pointer_type, valaddr, loc); - Expression* call = Runtime::make_call(Runtime::SELECTSEND, loc, 3, selref, - chanref, valaddr); - b->add_statement(Statement::make_statement(call, true)); + this->set_case(b, scase, chanref, valaddr, caseSend); } // 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) + Block* b, Expression* scase, + Expression* chanref, + Temporary_statement* recvok) { Location loc = this->location_; @@ -4640,26 +4647,10 @@ Select_clauses::Select_clause::lower_recv(Gogo* gogo, Named_object* function, Expression* valref = Expression::make_temporary_reference(val, loc); Expression* valaddr = Expression::make_unary(OPERATOR_AND, valref, loc); + Type* unsafe_pointer_type = Type::make_pointer_type(Type::make_void_type()); + valaddr = Expression::make_cast(unsafe_pointer_type, valaddr, loc); - Temporary_statement* closed_temp = NULL; - - Expression* caddr; - if (this->closed_ == NULL && this->closedvar_ == NULL) - caddr = Expression::make_nil(loc); - 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); - caddr = Expression::make_unary(OPERATOR_AND, cref, loc); - } - - Expression* call = Runtime::make_call(Runtime::SELECTRECV, loc, 4, selref, - chanref, valaddr, caddr); - - b->add_statement(Statement::make_statement(call, true)); + this->set_case(b, scase, chanref, valaddr, caseRecv); // If the block of statements is executed, arrange for the received // value to move from VAL to the place where the statements expect @@ -4684,16 +4675,14 @@ Select_clauses::Select_clause::lower_recv(Gogo* gogo, Named_object* function, if (this->closedvar_ != NULL) { go_assert(this->closed_ == NULL); - Expression* cref = Expression::make_temporary_reference(closed_temp, - loc); + Expression* cref = Expression::make_temporary_reference(recvok, 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); + Expression* cref = Expression::make_temporary_reference(recvok, loc); init->add_statement(Statement::make_assignment(this->closed_, cref, loc)); } @@ -4709,6 +4698,45 @@ Select_clauses::Select_clause::lower_recv(Gogo* gogo, Named_object* function, } } +// Set the fields of an scase struct, an element in the array that we +// pass to the runtime function selectgo. + +void +Select_clauses::Select_clause::set_case(Block* b, + Expression* scase, + Expression* chanref, + Expression* elem, + int kind) +{ + Location loc = this->location_; + Struct_type* scase_type = scase->type()->struct_type(); + + int field_index = 0; + go_assert(scase_type->field(field_index)->is_field_name("c")); + Expression* ref = Expression::make_field_reference(scase, field_index, loc); + Type* unsafe_pointer_type = Type::make_pointer_type(Type::make_void_type()); + chanref = Expression::make_unsafe_cast(unsafe_pointer_type, chanref, loc); + Statement* s = Statement::make_assignment(ref, chanref, loc); + b->add_statement(s); + + if (elem != NULL) + { + field_index = 1; + go_assert(scase_type->field(field_index)->is_field_name("elem")); + ref = Expression::make_field_reference(scase->copy(), field_index, loc); + s = Statement::make_assignment(ref, elem, loc); + b->add_statement(s); + } + + field_index = 2; + go_assert(scase_type->field(field_index)->is_field_name("kind")); + Type* uint16_type = Type::lookup_integer_type("uint16"); + Expression* k = Expression::make_integer_ul(kind, uint16_type, loc); + ref = Expression::make_field_reference(scase->copy(), field_index, loc); + s = Statement::make_assignment(ref, k, loc); + b->add_statement(s); +} + // Determine types. void @@ -4828,12 +4856,13 @@ Select_clauses::traverse(Traverse* traverse) void Select_clauses::lower(Gogo* gogo, Named_object* function, Block* b, - Temporary_statement* sel) + Temporary_statement* scases, Temporary_statement* recvok) { + size_t i = 0; for (Clauses::iterator p = this->clauses_.begin(); p != this->clauses_.end(); - ++p) - p->lower(gogo, function, b, sel); + ++p, ++i) + p->lower(gogo, function, b, scases, i, recvok); } // Determine types. @@ -4872,13 +4901,13 @@ Select_clauses::may_fall_through() const return false; } -// 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. +// Convert to the backend representation. Assemble the clauses and +// build a switch statement on the index value returned by the call to +// selectgo. Bstatement* Select_clauses::get_backend(Translate_context* context, - Temporary_statement* sel, + Temporary_statement* index, Unnamed_label *break_label, Location location) { @@ -4909,21 +4938,14 @@ Select_clauses::get_backend(Translate_context* context, clauses[i] = context->backend()->compound_statement(s, g); } - Expression* selref = Expression::make_temporary_reference(sel, location); - selref = Expression::make_unary(OPERATOR_AND, selref, location); - Expression* call = Runtime::make_call(Runtime::SELECTGO, location, 1, - selref); - context->gogo()->lower_expression(context->function(), NULL, &call); - Bexpression* bcall = call->get_backend(context); - - if (count == 0) - { - Bfunction* bfunction = context->function()->func_value()->get_decl(); - return context->backend()->expression_statement(bfunction, bcall); - } + Expression* ref = Expression::make_temporary_reference(index, location); + Bexpression* bindex = ref->get_backend(context); Bfunction* bfunction = context->function()->func_value()->get_decl(); + if (count == 0) + return context->backend()->expression_statement(bfunction, bindex); + Expression* crash = Runtime::make_call(Runtime::UNREACHABLE, location, 0); Bexpression* bcrash = crash->get_backend(context); clauses[count] = context->backend()->expression_statement(bfunction, bcrash); @@ -4932,7 +4954,7 @@ Select_clauses::get_backend(Translate_context* context, statements.reserve(2); Bstatement* switch_stmt = context->backend()->switch_statement(bfunction, - bcall, + bindex, cases, clauses, location); @@ -4943,6 +4965,7 @@ Select_clauses::get_backend(Translate_context* context, return context->backend()->statement_list(statements); } + // Dump the AST representation for select clauses. void @@ -4967,10 +4990,10 @@ Select_statement::break_label() return this->break_label_; } -// Lower a select statement. This will still return a select -// statement, but it will be modified to implement the order of -// evaluation rules, and to include the send and receive statements as -// explicit statements in the clauses. +// Lower a select statement. This will return a block containing this +// select statement. The block will implement the order of evaluation +// rules, include the send and receive statements as explicit +// statements in the clauses, and call the runtime selectgo function. Statement* Select_statement::do_lower(Gogo* gogo, Named_object* function, @@ -4983,30 +5006,69 @@ Select_statement::do_lower(Gogo* gogo, Named_object* function, Block* b = new Block(enclosing, loc); - go_assert(this->sel_ == NULL); - int ncases = this->clauses_->size(); - Type* selstruct_type = Channel_type::select_type(ncases); - this->sel_ = Statement::make_temporary(selstruct_type, NULL, loc); - b->add_statement(this->sel_); + Type* scase_type = Channel_type::select_case_type(); + Expression* ncases_expr = + Expression::make_integer_ul(ncases, NULL, + Linemap::predeclared_location()); + Array_type* scases_type = Type::make_array_type(scase_type, ncases_expr); + scases_type->set_is_array_incomparable(); + + Temporary_statement* scases = Statement::make_temporary(scases_type, NULL, + loc); + b->add_statement(scases); - int64_t selstruct_size; - if (!selstruct_type->backend_type_size(gogo, &selstruct_size)) - { - go_assert(saw_errors()); - return Statement::make_error_statement(loc); - } + Expression* ncases2_expr = + Expression::make_integer_ul(ncases * 2, NULL, + Linemap::predeclared_location()); + Type* uint16_type = Type::lookup_integer_type("uint16"); + Array_type* order_type = Type::make_array_type(uint16_type, ncases2_expr); + order_type->set_is_array_incomparable(); + + Temporary_statement* order = Statement::make_temporary(order_type, NULL, + loc); + b->add_statement(order); - Expression* ref = Expression::make_temporary_reference(this->sel_, loc); - ref = Expression::make_unary(OPERATOR_AND, ref, loc); - Expression* selstruct_size_expr = - Expression::make_integer_int64(selstruct_size, NULL, loc); - Expression* size_expr = Expression::make_integer_ul(ncases, NULL, loc); - Expression* call = Runtime::make_call(Runtime::NEWSELECT, loc, 3, - ref, selstruct_size_expr, size_expr); - b->add_statement(Statement::make_statement(call, true)); + Type* int_type = Type::lookup_integer_type("int"); + this->index_ = Statement::make_temporary(int_type, NULL, loc); + b->add_statement(this->index_); + + Type* bool_type = Type::lookup_bool_type(); + Temporary_statement* recvok = Statement::make_temporary(bool_type, NULL, + loc); + b->add_statement(recvok); + + // Initialize the scases array. + this->clauses_->lower(gogo, function, b, scases, recvok); + + // Build the call to selectgo. Later, in do_get_backend, we will + // build a switch on the result that branches to the various cases. + + Expression* scases_ref = Expression::make_temporary_reference(scases, loc); + scases_ref = Expression::make_unary(OPERATOR_AND, scases_ref, loc); + Type* unsafe_pointer_type = Type::make_pointer_type(Type::make_void_type()); + scases_ref = Expression::make_cast(unsafe_pointer_type, scases_ref, loc); + + Expression* order_ref = Expression::make_temporary_reference(order, loc); + order_ref = Expression::make_unary(OPERATOR_AND, order_ref, loc); + order_ref = Expression::make_cast(unsafe_pointer_type, order_ref, loc); + + Expression* count_expr = Expression::make_integer_ul(ncases, int_type, loc); + + Call_expression* call = Runtime::make_call(Runtime::SELECTGO, loc, 3, + scases_ref, order_ref, + count_expr); + + Expression* result = Expression::make_call_result(call, 0); + Expression* ref = Expression::make_temporary_reference(this->index_, loc); + Statement* s = Statement::make_assignment(ref, result, loc); + b->add_statement(s); + + result = Expression::make_call_result(call, 1); + ref = Expression::make_temporary_reference(recvok, loc); + s = Statement::make_assignment(ref, result, loc); + b->add_statement(s); - this->clauses_->lower(gogo, function, b, this->sel_); this->is_lowered_ = true; b->add_statement(this); @@ -5031,8 +5093,8 @@ Select_statement::do_may_fall_through() const Bstatement* Select_statement::do_get_backend(Translate_context* context) { - return this->clauses_->get_backend(context, this->sel_, this->break_label(), - this->location()); + return this->clauses_->get_backend(context, this->index_, + this->break_label(), this->location()); } // Dump the AST representation for a select statement. diff --git a/gcc/go/gofrontend/statements.h b/gcc/go/gofrontend/statements.h index c9ab4d6fac3..81b26b3e5a9 100644 --- a/gcc/go/gofrontend/statements.h +++ b/gcc/go/gofrontend/statements.h @@ -622,7 +622,8 @@ class Temporary_statement : public Statement public: Temporary_statement(Type* type, Expression* init, Location location) : Statement(STATEMENT_TEMPORARY, location), - type_(type), init_(init), bvariable_(NULL), is_address_taken_(false) + type_(type), init_(init), bvariable_(NULL), is_address_taken_(false), + value_escapes_(false) { } // Return the type of the temporary variable. @@ -640,6 +641,16 @@ class Temporary_statement : public Statement set_is_address_taken() { this->is_address_taken_ = true; } + // Whether the value escapes. + bool + value_escapes() const + { return this->value_escapes_; } + + // Record that the value escapes. + void + set_value_escapes() + { this->value_escapes_ = true; } + // Return the temporary variable. This should not be called until // after the statement itself has been converted. Bvariable* @@ -676,6 +687,9 @@ class Temporary_statement : public Statement Bvariable* bvariable_; // True if something takes the address of this temporary variable. bool is_address_taken_; + // True if the value assigned to this temporary variable escapes. + // This is used for select statements. + bool value_escapes_; }; // A variable declaration. This marks the point in the code where a @@ -851,7 +865,7 @@ class Send_statement : public Statement Expression* channel() - { return this->channel_; } + { return this->channel_; } Expression* val() @@ -924,7 +938,8 @@ class Select_clauses // Lower statements. void - lower(Gogo*, Named_object*, Block*, Temporary_statement*); + lower(Gogo*, Named_object*, Block*, Temporary_statement*, + Temporary_statement*); // Determine types. void @@ -941,7 +956,7 @@ class Select_clauses // Convert to the backend representation. Bstatement* - get_backend(Translate_context*, Temporary_statement* sel, + get_backend(Translate_context*, Temporary_statement* index, Unnamed_label* break_label, Location); // Dump AST representation. @@ -974,7 +989,8 @@ class Select_clauses // Lower statements. void - lower(Gogo*, Named_object*, Block*, Temporary_statement*); + lower(Gogo*, Named_object*, Block*, Temporary_statement*, size_t, + Temporary_statement*); // Determine types. void @@ -1027,6 +1043,14 @@ class Select_clauses dump_clause(Ast_dump_context*) const; private: + // These values must match the values in libgo/go/runtime/select.go. + enum + { + caseRecv = 1, + caseSend = 2, + caseDefault = 3, + }; + void lower_default(Block*, Expression*); @@ -1034,7 +1058,11 @@ class Select_clauses lower_send(Block*, Expression*, Expression*); void - lower_recv(Gogo*, Named_object*, Block*, Expression*, Expression*); + lower_recv(Gogo*, Named_object*, Block*, Expression*, Expression*, + Temporary_statement*); + + void + set_case(Block*, Expression*, Expression*, Expression*, int); // The channel. Expression* channel_; @@ -1072,7 +1100,7 @@ class Select_statement : public Statement public: Select_statement(Location location) : Statement(STATEMENT_SELECT, location), - clauses_(NULL), sel_(NULL), break_label_(NULL), is_lowered_(false) + clauses_(NULL), index_(NULL), break_label_(NULL), is_lowered_(false) { } // Add the clauses. @@ -1115,8 +1143,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_; + // A temporary that holds the index value returned by selectgo. + Temporary_statement* index_; // The break label. Unnamed_label* break_label_; // Whether this statement has been lowered. @@ -1609,7 +1637,7 @@ class Case_clauses // Dump the AST representation to a dump context. void dump_clauses(Ast_dump_context*) const; - + private: // For a constant switch we need to keep a record of constants we // have already seen. @@ -1683,7 +1711,7 @@ class Case_clauses // Dump the AST representation to a dump context. void dump_clause(Ast_dump_context*) const; - + private: // The list of case expressions. Expression_list* cases_; diff --git a/gcc/go/gofrontend/types.cc b/gcc/go/gofrontend/types.cc index 00098dabef8..cc07bfc25bd 100644 --- a/gcc/go/gofrontend/types.cc +++ b/gcc/go/gofrontend/types.cc @@ -8557,51 +8557,29 @@ Channel_type::do_import(Import* imp) return Type::make_channel_type(may_send, may_receive, element_type); } -// Return the type to manage a select statement with ncases case -// statements. A value of this type is allocated on the stack. This -// must match the type hselect in libgo/go/runtime/select.go. +// Return the type that the runtime package uses for one case of a +// select statement. An array of values of this type is allocated on +// the stack. This must match scase in libgo/go/runtime/select.go. Type* -Channel_type::select_type(int ncases) +Channel_type::select_case_type() { - Type* unsafe_pointer_type = Type::make_pointer_type(Type::make_void_type()); - Type* uint16_type = Type::lookup_integer_type("uint16"); - static Struct_type* scase_type; if (scase_type == NULL) { - Type* uintptr_type = Type::lookup_integer_type("uintptr"); - Type* uint64_type = Type::lookup_integer_type("uint64"); + Type* unsafe_pointer_type = + Type::make_pointer_type(Type::make_void_type()); + Type* uint16_type = Type::lookup_integer_type("uint16"); + Type* int64_type = Type::lookup_integer_type("int64"); scase_type = - Type::make_builtin_struct_type(7, + Type::make_builtin_struct_type(4, + "c", unsafe_pointer_type, "elem", unsafe_pointer_type, - "chan", unsafe_pointer_type, - "pc", uintptr_type, "kind", uint16_type, - "index", uint16_type, - "receivedp", unsafe_pointer_type, - "releasetime", uint64_type); + "releasetime", int64_type); scase_type->set_is_struct_incomparable(); } - - Expression* ncases_expr = - Expression::make_integer_ul(ncases, NULL, Linemap::predeclared_location()); - Array_type* scases = Type::make_array_type(scase_type, ncases_expr); - scases->set_is_array_incomparable(); - Array_type* order = Type::make_array_type(uint16_type, ncases_expr); - order->set_is_array_incomparable(); - - Struct_type* ret = - Type::make_builtin_struct_type(7, - "tcase", uint16_type, - "ncase", uint16_type, - "pollorder", unsafe_pointer_type, - "lockorder", unsafe_pointer_type, - "scase", scases, - "lockorderarr", order, - "pollorderarr", order); - ret->set_is_struct_incomparable(); - return ret; + return scase_type; } // Make a new channel type. diff --git a/gcc/go/gofrontend/types.h b/gcc/go/gofrontend/types.h index 0f85c3a2b9b..e011236cba5 100644 --- a/gcc/go/gofrontend/types.h +++ b/gcc/go/gofrontend/types.h @@ -2981,7 +2981,7 @@ class Channel_type : public Type make_chan_type_descriptor_type(); static Type* - select_type(int ncases); + select_case_type(); protected: int diff --git a/libgo/go/runtime/select.go b/libgo/go/runtime/select.go index 1c5124b4704..9dab052e3bf 100644 --- a/libgo/go/runtime/select.go +++ b/libgo/go/runtime/select.go @@ -7,146 +7,36 @@ package runtime // This file contains the implementation of Go select statements. import ( - "runtime/internal/sys" "unsafe" ) // For gccgo, use go:linkname to rename compiler-called functions to // themselves, so that the compiler will export them. // -//go:linkname newselect runtime.newselect -//go:linkname selectdefault runtime.selectdefault -//go:linkname selectsend runtime.selectsend -//go:linkname selectrecv runtime.selectrecv //go:linkname selectgo runtime.selectgo const debugSelect = false +// scase.kind values. +// Known to compiler. +// Changes here must also be made in src/cmd/compile/internal/gc/select.go's walkselect. const ( - // scase.kind caseNil = iota caseRecv caseSend caseDefault ) -// Select statement header. -// Known to compiler. -// Changes here must also be made in src/cmd/internal/gc/select.go's selecttype. -type hselect struct { - tcase uint16 // total count of scase[] - ncase uint16 // currently filled scase[] - pollorder *uint16 // case poll order - lockorder *uint16 // channel lock order - scase [1]scase // one per case (in order of appearance) -} - // Select case descriptor. // Known to compiler. -// Changes here must also be made in src/cmd/internal/gc/select.go's selecttype. +// Changes here must also be made in src/cmd/internal/gc/select.go's scasetype. type scase struct { - elem unsafe.Pointer // data element c *hchan // chan - pc uintptr // return pc (for race detector / msan) + elem unsafe.Pointer // data element kind uint16 - receivedp *bool // pointer to received bool, if any releasetime int64 } -var ( - chansendpc = funcPC(chansend) - chanrecvpc = funcPC(chanrecv) -) - -func selectsize(size uintptr) uintptr { - selsize := unsafe.Sizeof(hselect{}) + - (size-1)*unsafe.Sizeof(hselect{}.scase[0]) + - size*unsafe.Sizeof(*hselect{}.lockorder) + - size*unsafe.Sizeof(*hselect{}.pollorder) - return round(selsize, sys.Int64Align) -} - -func newselect(sel *hselect, selsize int64, size int32) { - if selsize != int64(selectsize(uintptr(size))) { - print("runtime: bad select size ", selsize, ", want ", selectsize(uintptr(size)), "\n") - throw("bad select size") - } - if size != int32(uint16(size)) { - throw("select size too large") - } - sel.tcase = uint16(size) - sel.ncase = 0 - sel.lockorder = (*uint16)(add(unsafe.Pointer(&sel.scase), uintptr(size)*unsafe.Sizeof(hselect{}.scase[0]))) - sel.pollorder = (*uint16)(add(unsafe.Pointer(sel.lockorder), uintptr(size)*unsafe.Sizeof(*hselect{}.lockorder))) - - // For gccgo the temporary variable will not have been zeroed. - memclrNoHeapPointers(unsafe.Pointer(&sel.scase), uintptr(size)*unsafe.Sizeof(hselect{}.scase[0])+uintptr(size)*unsafe.Sizeof(*hselect{}.lockorder)+uintptr(size)*unsafe.Sizeof(*hselect{}.pollorder)) - - if debugSelect { - print("newselect s=", sel, " size=", size, "\n") - } -} - -func selectsend(sel *hselect, c *hchan, elem unsafe.Pointer) { - pc := getcallerpc() - i := sel.ncase - if i >= sel.tcase { - throw("selectsend: too many cases") - } - sel.ncase = i + 1 - if c == nil { - return - } - cas := (*scase)(add(unsafe.Pointer(&sel.scase), uintptr(i)*unsafe.Sizeof(sel.scase[0]))) - cas.pc = pc - cas.c = c - cas.kind = caseSend - cas.elem = elem - - if debugSelect { - print("selectsend s=", sel, " pc=", hex(cas.pc), " chan=", cas.c, "\n") - } -} - -func selectrecv(sel *hselect, c *hchan, elem unsafe.Pointer, received *bool) { - pc := getcallerpc() - i := sel.ncase - if i >= sel.tcase { - throw("selectrecv: too many cases") - } - sel.ncase = i + 1 - if c == nil { - return - } - cas := (*scase)(add(unsafe.Pointer(&sel.scase), uintptr(i)*unsafe.Sizeof(sel.scase[0]))) - cas.pc = pc - cas.c = c - cas.kind = caseRecv - cas.elem = elem - cas.receivedp = received - - if debugSelect { - print("selectrecv s=", sel, " pc=", hex(cas.pc), " chan=", cas.c, "\n") - } -} - -func selectdefault(sel *hselect) { - pc := getcallerpc() - i := sel.ncase - if i >= sel.tcase { - throw("selectdefault: too many cases") - } - sel.ncase = i + 1 - cas := (*scase)(add(unsafe.Pointer(&sel.scase), uintptr(i)*unsafe.Sizeof(sel.scase[0]))) - cas.pc = pc - cas.c = nil - cas.kind = caseDefault - - if debugSelect { - print("selectdefault s=", sel, " pc=", hex(cas.pc), "\n") - } -} - func sellock(scases []scase, lockorder []uint16) { var c *hchan for _, o := range lockorder { @@ -209,26 +99,39 @@ func block() { // selectgo implements the select statement. // -// *sel is on the current goroutine's stack (regardless of any -// escaping in selectgo). +// cas0 points to an array of type [ncases]scase, and order0 points to +// an array of type [2*ncases]uint16. Both reside on the goroutine's +// stack (regardless of any escaping in selectgo). // // selectgo returns the index of the chosen scase, which matches the // ordinal position of its respective select{recv,send,default} call. -func selectgo(sel *hselect) int { +// Also, if the chosen scase was a receive operation, it returns whether +// a value was received. +func selectgo(cas0 *scase, order0 *uint16, ncases int) (int, bool) { if debugSelect { - print("select: sel=", sel, "\n") - } - if sel.ncase != sel.tcase { - throw("selectgo: case count mismatch") + print("select: cas0=", cas0, "\n") } - scaseslice := slice{unsafe.Pointer(&sel.scase), int(sel.ncase), int(sel.ncase)} - scases := *(*[]scase)(unsafe.Pointer(&scaseslice)) + cas1 := (*[1 << 16]scase)(unsafe.Pointer(cas0)) + order1 := (*[1 << 17]uint16)(unsafe.Pointer(order0)) + + scases := cas1[:ncases:ncases] + pollorder := order1[:ncases:ncases] + lockorder := order1[ncases:][:ncases:ncases] + + // Replace send/receive cases involving nil channels with + // caseNil so logic below can assume non-nil channel. + for i := range scases { + cas := &scases[i] + if cas.c == nil && cas.kind != caseDefault { + *cas = scase{} + } + } var t0 int64 if blockprofilerate > 0 { t0 = cputicks() - for i := 0; i < int(sel.ncase); i++ { + for i := 0; i < ncases; i++ { scases[i].releasetime = -1 } } @@ -241,10 +144,13 @@ func selectgo(sel *hselect) int { // cases correctly, and they are rare enough not to bother // optimizing (and needing to test). + // needed for gccgo, which doesn't zero pollorder + if ncases > 0 { + pollorder[0] = 0 + } + // generate permuted order - pollslice := slice{unsafe.Pointer(sel.pollorder), int(sel.ncase), int(sel.ncase)} - pollorder := *(*[]uint16)(unsafe.Pointer(&pollslice)) - for i := 1; i < int(sel.ncase); i++ { + for i := 1; i < ncases; i++ { j := fastrandn(uint32(i + 1)) pollorder[i] = pollorder[j] pollorder[j] = uint16(i) @@ -252,9 +158,7 @@ func selectgo(sel *hselect) int { // sort the cases by Hchan address to get the locking order. // simple heap sort, to guarantee n log n time and constant stack footprint. - lockslice := slice{unsafe.Pointer(sel.lockorder), int(sel.ncase), int(sel.ncase)} - lockorder := *(*[]uint16)(unsafe.Pointer(&lockslice)) - for i := 0; i < int(sel.ncase); i++ { + for i := 0; i < ncases; i++ { j := i // Start with the pollorder to permute cases on the same channel. c := scases[pollorder[i]].c @@ -265,7 +169,7 @@ func selectgo(sel *hselect) int { } lockorder[j] = pollorder[i] } - for i := int(sel.ncase) - 1; i >= 0; i-- { + for i := ncases - 1; i >= 0; i-- { o := lockorder[i] c := scases[o].c lockorder[i] = lockorder[0] @@ -287,14 +191,15 @@ func selectgo(sel *hselect) int { } lockorder[j] = o } - /* - for i := 0; i+1 < int(sel.ncase); i++ { + + if debugSelect { + for i := 0; i+1 < ncases; i++ { if scases[lockorder[i]].c.sortkey() > scases[lockorder[i+1]].c.sortkey() { print("i=", i, " x=", lockorder[i], " y=", lockorder[i+1], "\n") throw("select: broken sort") } } - */ + } // lock all the channels involved in the select sellock(scases, lockorder) @@ -316,7 +221,8 @@ loop: var dfl *scase var casi int var cas *scase - for i := 0; i < int(sel.ncase); i++ { + var recvOK bool + for i := 0; i < ncases; i++ { casi = int(pollorder[i]) cas = &scases[casi] c = cas.c @@ -338,9 +244,6 @@ loop: } case caseSend: - if raceenabled { - racereadpc(unsafe.Pointer(c), cas.pc, chansendpc) - } if c.closed != 0 { goto sclose } @@ -469,26 +372,11 @@ loop: c = cas.c if debugSelect { - print("wait-return: sel=", sel, " c=", c, " cas=", cas, " kind=", cas.kind, "\n") + print("wait-return: cas0=", cas0, " c=", c, " cas=", cas, " kind=", cas.kind, "\n") } - if cas.kind == caseRecv && cas.receivedp != nil { - *cas.receivedp = true - } - - if raceenabled { - if cas.kind == caseRecv && cas.elem != nil { - raceWriteObjectPC(c.elemtype, cas.elem, cas.pc, chanrecvpc) - } else if cas.kind == caseSend { - raceReadObjectPC(c.elemtype, cas.elem, cas.pc, chansendpc) - } - } - if msanenabled { - if cas.kind == caseRecv && cas.elem != nil { - msanwrite(cas.elem, c.elemtype.size) - } else if cas.kind == caseSend { - msanread(cas.elem, c.elemtype.size) - } + if cas.kind == caseRecv { + recvOK = true } selunlock(scases, lockorder) @@ -496,19 +384,7 @@ loop: bufrecv: // can receive from buffer - if raceenabled { - if cas.elem != nil { - raceWriteObjectPC(c.elemtype, cas.elem, cas.pc, chanrecvpc) - } - raceacquire(chanbuf(c, c.recvx)) - racerelease(chanbuf(c, c.recvx)) - } - if msanenabled && cas.elem != nil { - msanwrite(cas.elem, c.elemtype.size) - } - if cas.receivedp != nil { - *cas.receivedp = true - } + recvOK = true qp = chanbuf(c, c.recvx) if cas.elem != nil { typedmemmove(c.elemtype, cas.elem, qp) @@ -524,14 +400,6 @@ bufrecv: bufsend: // can send to buffer - if raceenabled { - raceacquire(chanbuf(c, c.sendx)) - racerelease(chanbuf(c, c.sendx)) - raceReadObjectPC(c.elemtype, cas.elem, cas.pc, chansendpc) - } - if msanenabled { - msanread(cas.elem, c.elemtype.size) - } typedmemmove(c.elemtype, chanbuf(c, c.sendx), cas.elem) c.sendx++ if c.sendx == c.dataqsiz { @@ -545,19 +413,15 @@ recv: // can receive from sleeping sender (sg) recv(c, sg, cas.elem, func() { selunlock(scases, lockorder) }, 2) if debugSelect { - print("syncrecv: sel=", sel, " c=", c, "\n") - } - if cas.receivedp != nil { - *cas.receivedp = true + print("syncrecv: cas0=", cas0, " c=", c, "\n") } + recvOK = true goto retc rclose: // read at end of closed channel selunlock(scases, lockorder) - if cas.receivedp != nil { - *cas.receivedp = false - } + recvOK = false if cas.elem != nil { typedmemclr(c.elemtype, cas.elem) } @@ -568,15 +432,9 @@ rclose: send: // can send to a sleeping receiver (sg) - if raceenabled { - raceReadObjectPC(c.elemtype, cas.elem, cas.pc, chansendpc) - } - if msanenabled { - msanread(cas.elem, c.elemtype.size) - } send(c, sg, cas.elem, func() { selunlock(scases, lockorder) }, 2) if debugSelect { - print("syncsend: sel=", sel, " c=", c, "\n") + print("syncsend: cas0=", cas0, " c=", c, "\n") } goto retc @@ -591,7 +449,7 @@ retc: checkPreempt() } - return casi + return casi, recvOK sclose: // send on closed channel @@ -625,27 +483,25 @@ const ( ) //go:linkname reflect_rselect reflect.rselect -func reflect_rselect(cases []runtimeSelect) (chosen int, recvOK bool) { - // flagNoScan is safe here, because all objects are also referenced from cases. - size := selectsize(uintptr(len(cases))) - sel := (*hselect)(mallocgc(size, nil, true)) - newselect(sel, int64(size), int32(len(cases))) - r := new(bool) +func reflect_rselect(cases []runtimeSelect) (int, bool) { + if len(cases) == 0 { + block() + } + sel := make([]scase, len(cases)) + order := make([]uint16, 2*len(cases)) for i := range cases { rc := &cases[i] switch rc.dir { case selectDefault: - selectdefault(sel) + sel[i] = scase{kind: caseDefault} case selectSend: - selectsend(sel, rc.ch, rc.val) + sel[i] = scase{kind: caseSend, c: rc.ch, elem: rc.val} case selectRecv: - selectrecv(sel, rc.ch, rc.val, r) + sel[i] = scase{kind: caseRecv, c: rc.ch, elem: rc.val} } } - chosen = selectgo(sel) - recvOK = *r - return + return selectgo(&sel[0], &order[0], len(cases)) } func (q *waitq) dequeueSudoG(sgp *sudog) { -- 2.30.2