aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gcc/go/gofrontend/MERGE2
-rw-r--r--gcc/go/gofrontend/runtime.def4
-rw-r--r--gcc/go/gofrontend/statements.cc131
-rw-r--r--gcc/go/gofrontend/statements.h48
-rw-r--r--gcc/go/gofrontend/types.cc7
-rw-r--r--libgo/go/runtime/select.go93
6 files changed, 182 insertions, 103 deletions
diff --git a/gcc/go/gofrontend/MERGE b/gcc/go/gofrontend/MERGE
index bb537f1..c70b60c 100644
--- a/gcc/go/gofrontend/MERGE
+++ b/gcc/go/gofrontend/MERGE
@@ -1,4 +1,4 @@
-eca96e39cb895805b617e0e1f184f893ed3e46bb
+d091cd25a5894ac751fe1868197648fc486cf322
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/runtime.def b/gcc/go/gofrontend/runtime.def
index 4b606a6..7ab94a3 100644
--- a/gcc/go/gofrontend/runtime.def
+++ b/gcc/go/gofrontend/runtime.def
@@ -195,8 +195,8 @@ DEF_GO_RUNTIME(CHANRECV2, "runtime.chanrecv2", P2(CHAN, POINTER), R1(BOOL))
// 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))
+DEF_GO_RUNTIME(SELECTGO, "runtime.selectgo",
+ P5(POINTER, POINTER, INT, INT, BOOL), R2(INT, BOOL))
// Non-blocking send a value on a channel, used for two-case select
// statement with a default case.
diff --git a/gcc/go/gofrontend/statements.cc b/gcc/go/gofrontend/statements.cc
index 398b8fd..da0e084 100644
--- a/gcc/go/gofrontend/statements.cc
+++ b/gcc/go/gofrontend/statements.cc
@@ -5291,22 +5291,23 @@ Select_clauses::Select_clause::traverse(Traverse* traverse)
void
Select_clauses::Select_clause::lower(Gogo* gogo, Named_object* function,
Block* b, Temporary_statement* scases,
- size_t index, Temporary_statement* recvok)
+ int index, Temporary_statement* recvok)
{
Location loc = this->location_;
- 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);
+ this->set_case_index(index);
if (this->is_default_)
{
go_assert(this->channel_ == NULL && this->val_ == NULL);
- this->lower_default(b, scase);
this->is_lowered_ = true;
return;
}
+ Expression* scase = Expression::make_temporary_reference(scases, loc);
+ Expression* index_expr = Expression::make_integer_sl(index, NULL, loc);
+ scase = Expression::make_array_index(scase, index_expr, NULL, NULL, loc);
+
// Evaluate the channel before the select statement.
Temporary_statement* channel_temp = Statement::make_temporary(NULL,
this->channel_,
@@ -5326,15 +5327,6 @@ Select_clauses::Select_clause::lower(Gogo* gogo, Named_object* function,
this->val_ = NULL;
}
-// Lower a default clause in a select statement.
-
-void
-Select_clauses::Select_clause::lower_default(Block* b, Expression* scase)
-{
- Location loc = this->location_;
- this->set_case(b, scase, Expression::make_nil(loc), NULL, caseDefault);
-}
-
// Lower a send clause in a select statement.
void
@@ -5366,7 +5358,7 @@ Select_clauses::Select_clause::lower_send(Block* b, Expression* scase,
Type* unsafe_pointer_type = Type::make_pointer_type(Type::make_void_type());
valaddr = Expression::make_cast(unsafe_pointer_type, valaddr, loc);
- this->set_case(b, scase, chanref, valaddr, caseSend);
+ this->set_case(b, scase, chanref, valaddr);
}
// Lower a receive clause in a select statement.
@@ -5392,7 +5384,7 @@ Select_clauses::Select_clause::lower_recv(Gogo* gogo, Named_object* function,
Type* unsafe_pointer_type = Type::make_pointer_type(Type::make_void_type());
valaddr = Expression::make_cast(unsafe_pointer_type, valaddr, loc);
- this->set_case(b, scase, chanref, valaddr, caseRecv);
+ this->set_case(b, scase, chanref, valaddr);
// If the block of statements is executed, arrange for the received
// value to move from VAL to the place where the statements expect
@@ -5447,8 +5439,7 @@ void
Select_clauses::Select_clause::set_case(Block* b,
Expression* scase,
Expression* chanref,
- Expression* elem,
- int kind)
+ Expression* elem)
{
Location loc = this->location_;
Struct_type* scase_type = scase->type()->struct_type();
@@ -5469,14 +5460,6 @@ Select_clauses::Select_clause::set_case(Block* b,
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.
@@ -5577,6 +5560,19 @@ Select_clauses::Select_clause::dump_clause(
// Class Select_clauses.
+// Whether there is a default case.
+
+bool
+Select_clauses::has_default() const
+{
+ for (Clauses::const_iterator p = this->clauses_.begin();
+ p != this->clauses_.end();
+ ++p)
+ if (p->is_default())
+ return true;
+ return false;
+}
+
// Traversal.
int
@@ -5594,17 +5590,60 @@ Select_clauses::traverse(Traverse* traverse)
// 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.
+// receive statements to the clauses. This builds the entries in the
+// local array of scase values. It sets *P_SEND_COUNT and
+// *P_RECV_COUNT.
void
Select_clauses::lower(Gogo* gogo, Named_object* function, Block* b,
- Temporary_statement* scases, Temporary_statement* recvok)
+ Temporary_statement* scases, Temporary_statement* recvok,
+ int *p_send_count, int *p_recv_count)
{
- size_t i = 0;
+ int send_count = 0;
+ int recv_count = 0;
+ bool has_default = false;
for (Clauses::iterator p = this->clauses_.begin();
p != this->clauses_.end();
- ++p, ++i)
- p->lower(gogo, function, b, scases, i, recvok);
+ ++p)
+ {
+ if (p->is_default())
+ has_default = true;
+ else if (p->is_send())
+ ++send_count;
+ else
+ ++recv_count;
+ }
+
+ *p_send_count = send_count;
+ *p_recv_count = recv_count;
+
+ int send_index = 0;
+ int recv_index = send_count;
+ for (Clauses::iterator p = this->clauses_.begin();
+ p != this->clauses_.end();
+ ++p)
+ {
+ int index;
+ if (p->is_default())
+ index = -1;
+ else if (p->is_send())
+ {
+ index = send_index;
+ ++send_index;
+ }
+ else
+ {
+ index = recv_index;
+ ++recv_index;
+ }
+
+ p->lower(gogo, function, b, scases, index, recvok);
+ }
+
+ go_assert(send_index == send_count);
+ go_assert(recv_index == send_count + recv_count);
+ go_assert(static_cast<size_t>(recv_index + (has_default ? 1 : 0))
+ == this->size());
}
// Determine types.
@@ -5664,7 +5703,8 @@ Select_clauses::get_backend(Translate_context* context,
p != this->clauses_.end();
++p, ++i)
{
- Expression* index_expr = Expression::make_integer_ul(i, int_type,
+ Expression* index_expr = Expression::make_integer_sl(p->case_index(),
+ int_type,
location);
cases[i].push_back(index_expr->get_backend(context));
@@ -5749,6 +5789,7 @@ Select_statement::do_lower(Gogo* gogo, Named_object* function,
Block* b = new Block(enclosing, loc);
int ncases = this->clauses_->size();
+ bool has_default = this->clauses_->has_default();
// Zero-case select. Just block the execution.
if (ncases == 0)
@@ -5766,11 +5807,13 @@ Select_statement::do_lower(Gogo* gogo, Named_object* function,
// Two-case select with one default case. It is a non-blocking
// send/receive.
- if (ncases == 2
- && (this->clauses_->at(0).is_default()
- || this->clauses_->at(1).is_default()))
+ if (ncases == 2 && has_default)
return this->lower_two_case(b);
+ // We don't allocate an entry in scases for the default case.
+ if (has_default)
+ --ncases;
+
Type* scase_type = Channel_type::select_case_type();
Expression* ncases_expr =
Expression::make_integer_ul(ncases, NULL,
@@ -5803,7 +5846,10 @@ Select_statement::do_lower(Gogo* gogo, Named_object* function,
b->add_statement(recvok);
// Initialize the scases array.
- this->clauses_->lower(gogo, function, b, scases, recvok);
+ int send_count;
+ int recv_count;
+ this->clauses_->lower(gogo, function, b, scases, recvok, &send_count,
+ &recv_count);
// Build the call to selectgo. Later, in do_get_backend, we will
// build a switch on the result that branches to the various cases.
@@ -5817,11 +5863,18 @@ Select_statement::do_lower(Gogo* gogo, Named_object* function,
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);
+ Expression* send_count_expr = Expression::make_integer_sl(send_count,
+ int_type,
+ loc);
+ Expression* recv_count_expr = Expression::make_integer_sl(recv_count,
+ int_type,
+ loc);
+ Expression* block_expr = Expression::make_boolean(!has_default, loc);
- Call_expression* call = Runtime::make_call(Runtime::SELECTGO, loc, 3,
+ Call_expression* call = Runtime::make_call(Runtime::SELECTGO, loc, 5,
scases_ref, order_ref,
- count_expr);
+ send_count_expr, recv_count_expr,
+ block_expr);
Expression* result = Expression::make_call_result(call, 0);
Expression* ref = Expression::make_temporary_reference(this->index_, loc);
diff --git a/gcc/go/gofrontend/statements.h b/gcc/go/gofrontend/statements.h
index 47092b4..c08b493 100644
--- a/gcc/go/gofrontend/statements.h
+++ b/gcc/go/gofrontend/statements.h
@@ -1092,6 +1092,9 @@ class Select_clauses
size() const
{ return this->clauses_.size(); }
+ bool
+ has_default() const;
+
// Traverse the select clauses.
int
traverse(Traverse*);
@@ -1099,7 +1102,7 @@ class Select_clauses
// Lower statements.
void
lower(Gogo*, Named_object*, Block*, Temporary_statement*,
- Temporary_statement*);
+ Temporary_statement*, int* send_count, int* recv_count);
// Determine types.
void
@@ -1138,8 +1141,9 @@ class Select_clauses
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)
+ closedvar_(closedvar), statements_(statements), case_index_(0),
+ location_(location), is_send_(is_send), is_default_(is_default),
+ is_lowered_(false), is_case_index_set_(false)
{ go_assert(is_default ? channel == NULL : channel != NULL); }
// Traverse the select clause.
@@ -1148,7 +1152,7 @@ class Select_clauses
// Lower statements.
void
- lower(Gogo*, Named_object*, Block*, Temporary_statement*, size_t,
+ lower(Gogo*, Named_object*, Block*, Temporary_statement*, int,
Temporary_statement*);
// Determine types.
@@ -1210,6 +1214,23 @@ class Select_clauses
location() const
{ return this->location_; }
+ // Return the case index for this clause.
+ int
+ case_index() const
+ {
+ go_assert(this->is_case_index_set_);
+ return this->case_index_;
+ }
+
+ // Set the case index.
+ void
+ set_case_index(int i)
+ {
+ go_assert(!this->is_case_index_set_);
+ this->case_index_ = i;
+ this->is_case_index_set_ = true;
+ }
+
// Whether this clause may fall through to the statement which
// follows the overall select statement.
bool
@@ -1224,17 +1245,6 @@ 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*);
-
void
lower_send(Block*, Expression*, Expression*);
@@ -1243,7 +1253,7 @@ class Select_clauses
Temporary_statement*);
void
- set_case(Block*, Expression*, Expression*, Expression*, int);
+ set_case(Block*, Expression*, Expression*, Expression*);
// The channel.
Expression* channel_;
@@ -1259,6 +1269,10 @@ class Select_clauses
Named_object* closedvar_;
// The statements to execute.
Block* statements_;
+ // The index of this clause in the switch statement. If
+ // runtime.selectgo returns this index, this clause has been
+ // chosen.
+ int case_index_;
// The location of this clause.
Location location_;
// Whether this is a send or a receive.
@@ -1267,6 +1281,8 @@ class Select_clauses
bool is_default_;
// Whether this has been lowered.
bool is_lowered_;
+ // Whether the case index has been set.
+ bool is_case_index_set_;
};
Select_clause&
diff --git a/gcc/go/gofrontend/types.cc b/gcc/go/gofrontend/types.cc
index 12e7830..16f0eb5 100644
--- a/gcc/go/gofrontend/types.cc
+++ b/gcc/go/gofrontend/types.cc
@@ -8905,13 +8905,10 @@ 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");
- Type* int64_type = Type::lookup_integer_type("int64");
scase_type =
- Type::make_builtin_struct_type(3,
+ Type::make_builtin_struct_type(2,
"c", unsafe_pointer_type,
- "elem", unsafe_pointer_type,
- "kind", uint16_type);
+ "elem", unsafe_pointer_type);
scase_type->set_is_struct_incomparable();
}
return scase_type;
diff --git a/libgo/go/runtime/select.go b/libgo/go/runtime/select.go
index c31aa34..45cc284 100644
--- a/libgo/go/runtime/select.go
+++ b/libgo/go/runtime/select.go
@@ -18,23 +18,12 @@ import (
const debugSelect = false
-// scase.kind values.
-// Known to compiler.
-// Changes here must also be made in src/cmd/compile/internal/gc/select.go's walkselectcases.
-const (
- caseNil = iota
- caseRecv
- caseSend
- caseDefault
-)
-
// Select case descriptor.
// Known to compiler.
// Changes here must also be made in src/cmd/internal/gc/select.go's scasetype.
type scase struct {
c *hchan // chan
elem unsafe.Pointer // data element
- kind uint16
}
func sellock(scases []scase, lockorder []uint16) {
@@ -121,7 +110,7 @@ func block() {
// ordinal position of its respective select{recv,send,default} call.
// Also, if the chosen scase was a receive operation, it reports whether
// a value was received.
-func selectgo(cas0 *scase, order0 *uint16, ncases int) (int, bool) {
+func selectgo(cas0 *scase, order0 *uint16, nsends, nrecvs int, block bool) (int, bool) {
if debugSelect {
print("select: cas0=", cas0, "\n")
}
@@ -131,6 +120,7 @@ func selectgo(cas0 *scase, order0 *uint16, ncases int) (int, bool) {
cas1 := (*[1 << 16]scase)(unsafe.Pointer(cas0))
order1 := (*[1 << 17]uint16)(unsafe.Pointer(order0))
+ ncases := nsends + nrecvs
scases := cas1[:ncases:ncases]
pollorder := order1[:ncases:ncases]
lockorder := order1[ncases:][:ncases:ncases]
@@ -154,16 +144,12 @@ func selectgo(cas0 *scase, order0 *uint16, ncases int) (int, bool) {
}
// generate permuted order
- dfli := -1
norder := 0
for i := range scases {
cas := &scases[i]
// Omit cases without channels from the poll and lock orders.
if cas.c == nil {
- if cas.kind == caseDefault {
- dfli = i
- }
cas.elem = nil // allow GC
continue
}
@@ -246,8 +232,7 @@ func selectgo(cas0 *scase, order0 *uint16, ncases int) (int, bool) {
cas = &scases[casi]
c = cas.c
- switch cas.kind {
- case caseRecv:
+ if casi >= nsends {
sg = c.sendq.dequeue()
if sg != nil {
goto recv
@@ -258,8 +243,7 @@ func selectgo(cas0 *scase, order0 *uint16, ncases int) (int, bool) {
if c.closed != 0 {
goto rclose
}
-
- case caseSend:
+ } else {
if c.closed != 0 {
goto sclose
}
@@ -273,9 +257,9 @@ func selectgo(cas0 *scase, order0 *uint16, ncases int) (int, bool) {
}
}
- if dfli >= 0 {
+ if !block {
selunlock(scases, lockorder)
- casi = dfli
+ casi = -1
goto retc
}
@@ -304,12 +288,10 @@ func selectgo(cas0 *scase, order0 *uint16, ncases int) (int, bool) {
*nextp = sg
nextp = &sg.waitlink
- switch cas.kind {
- case caseRecv:
- c.recvq.enqueue(sg)
-
- case caseSend:
+ if casi < nsends {
c.sendq.enqueue(sg)
+ } else {
+ c.recvq.enqueue(sg)
}
}
@@ -357,7 +339,7 @@ func selectgo(cas0 *scase, order0 *uint16, ncases int) (int, bool) {
}
} else {
c = k.c
- if k.kind == caseSend {
+ if int(casei) < nsends {
c.sendq.dequeueSudoG(sglist)
} else {
c.recvq.dequeueSudoG(sglist)
@@ -376,13 +358,15 @@ func selectgo(cas0 *scase, order0 *uint16, ncases int) (int, bool) {
c = cas.c
if debugSelect {
- print("wait-return: cas0=", cas0, " c=", c, " cas=", cas, " kind=", cas.kind, "\n")
+ print("wait-return: cas0=", cas0, " c=", c, " cas=", cas, " send=", casi < nsends, "\n")
}
- if cas.kind == caseRecv {
+ if casi < nsends {
+ if !caseSuccess {
+ goto sclose
+ }
+ } else {
recvOK = caseSuccess
- } else if cas.kind == caseSend && !caseSuccess {
- goto sclose
}
selunlock(scases, lockorder)
@@ -451,7 +435,7 @@ retc:
// Check preemption, since unlike gc we don't check on every call.
// A test case for this one is BenchmarkPingPongHog in proc_test.go.
- if dfli >= 0 && getg().preempt {
+ if block && getg().preempt {
checkPreempt()
}
@@ -492,20 +476,49 @@ func reflect_rselect(cases []runtimeSelect) (int, bool) {
block()
}
sel := make([]scase, len(cases))
- order := make([]uint16, 2*len(cases))
- for i := range cases {
- rc := &cases[i]
+ orig := make([]int, len(cases))
+ nsends, nrecvs := 0, 0
+ dflt := -1
+ for i, rc := range cases {
+ var j int
switch rc.dir {
case selectDefault:
- sel[i] = scase{kind: caseDefault}
+ dflt = i
+ continue
case selectSend:
- sel[i] = scase{kind: caseSend, c: rc.ch, elem: rc.val}
+ j = nsends
+ nsends++
case selectRecv:
- sel[i] = scase{kind: caseRecv, c: rc.ch, elem: rc.val}
+ nrecvs++
+ j = len(cases) - nrecvs
}
+
+ sel[j] = scase{c: rc.ch, elem: rc.val}
+ orig[j] = i
+ }
+
+ // Only a default case.
+ if nsends+nrecvs == 0 {
+ return dflt, false
}
- return selectgo(&sel[0], &order[0], len(cases))
+ // Compact sel and orig if necessary.
+ if nsends+nrecvs < len(cases) {
+ copy(sel[nsends:], sel[len(cases)-nrecvs:])
+ copy(orig[nsends:], orig[len(cases)-nrecvs:])
+ }
+
+ order := make([]uint16, 2*(nsends+nrecvs))
+
+ chosen, recvOK := selectgo(&sel[0], &order[0], nsends, nrecvs, dflt == -1)
+
+ // Translate chosen back to caller's ordering.
+ if chosen < 0 {
+ chosen = dflt
+ } else {
+ chosen = orig[chosen]
+ }
+ return chosen, recvOK
}
func (q *waitq) dequeueSudoG(sgp *sudog) {