runtime: eliminate scase.kind field
authorIan Lance Taylor <iant@golang.org>
Tue, 22 Dec 2020 19:04:35 +0000 (11:04 -0800)
committerIan Lance Taylor <iant@golang.org>
Tue, 22 Dec 2020 22:42:22 +0000 (14:42 -0800)
This is the gofrontend version of https://golang.org/cl/245125.

Original CL description:

    Currently, we include a "kind" field on scase to distinguish the three
    kinds of cases in a select statement: sends, receives, and defaults.

    This commit removes by kind field by instead arranging for the
    compiler to always place sends before receives, and to provide their
    counts separately. It also passes an explicit "block bool" parameter
    to avoid needing to include a default case in the array.

    It's safe to shuffle cases like this because the runtime will
    randomize the order they're polled in anyway.

    For golang/go#40410.

This is being brought over to gofrontend as a step toward upgrading to
Go1.16beta1.

Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/279735

gcc/go/gofrontend/MERGE
gcc/go/gofrontend/runtime.def
gcc/go/gofrontend/statements.cc
gcc/go/gofrontend/statements.h
gcc/go/gofrontend/types.cc
libgo/go/runtime/select.go

index bb537f152b9c0f81d45d2d0afd037d2421b0d4c3..c70b60c657fc393c7f0468fc30cccde74baf1f3f 100644 (file)
@@ -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.
index 4b606a6c00c6f6922978013f35c3ffb0068d1f54..7ab94a3ffbca9c9346fa1a5d5809bc9de98825d0 100644 (file)
@@ -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.
index 398b8fd82f2202e314367e012f2f6acd0d647e63..da0e0843a735eab5272a8b6155cdf6ece44eec84 100644 (file)
@@ -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);
index 47092b4912a394d0c04ce61b23ac360e18510902..c08b493e6c96d49df2f849e17e3cc54b62e164ee 100644 (file)
@@ -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&
index 12e783010db3291421bec3a3db802bbacb28c406..16f0eb59a503a540bfe6cf1e8bfa4e62398e5f57 100644 (file)
@@ -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;
index c31aa344b552f1e39d29fc83e580cf49907e64e1..45cc28479459963fdc8e8a1e9e5c02b979787847 100644 (file)
@@ -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) {