compiler, runtime: simplify select and channel operations
authorIan Lance Taylor <ian@gcc.gnu.org>
Thu, 14 Sep 2017 03:57:18 +0000 (03:57 +0000)
committerIan Lance Taylor <ian@gcc.gnu.org>
Thu, 14 Sep 2017 03:57:18 +0000 (03:57 +0000)
    In preparation for upgrading libgo to the 1.9 release, this
    approximately incorporates https://golang.org/cl/37661 and
    https://golang.org/cl/38351.

    CL 37661 changed the gc compiler such that the select statement simply
    returns an integer which is then used as the argument for a switch.
    Since gccgo already worked that way, this just adjusts the switch code
    to look like the gc switch code by removing the explicit case index
    expression and calculating it from the order of calls to selectsend,
    selectrecv, and selectdefault.

    CL 38351 simplifies the channel code by not passing the unused channel
    type descriptor pointer.

    Reviewed-on: https://go-review.googlesource.com/62730

From-SVN: r252749

gcc/go/gofrontend/MERGE
gcc/go/gofrontend/expressions.cc
gcc/go/gofrontend/runtime.def
gcc/go/gofrontend/statements.cc
gcc/go/gofrontend/statements.h
libgo/go/reflect/value.go
libgo/go/runtime/chan.go
libgo/go/runtime/select.go

index 7c78efc6f67035a51a357cb336c75c89d247c72f..b07bce8518714158299ff2df980c20599616befe 100644 (file)
@@ -1,4 +1,4 @@
-0176cbc6dbd2170bfe2eb8904b80ddfe4c946997
+199f175f4239d1ca6d7e80d08639955d41c3b09f
 
 The first line of this file holds the git revision number of the last
 merge done from the gofrontend repository.
index 82de4d815ab43f19d19d905738135c95d35aa965..88162327b5fb1153e3e58ae45dafcb9c52b2bce7 100644 (file)
@@ -14463,15 +14463,14 @@ Receive_expression::do_get_backend(Translate_context* context)
       go_assert(this->channel_->type()->is_error());
       return context->backend()->error_expression();
     }
-  Expression* td = Expression::make_type_descriptor(channel_type, loc);
 
   Expression* recv_ref =
     Expression::make_temporary_reference(this->temp_receiver_, loc);
   Expression* recv_addr =
     Expression::make_temporary_reference(this->temp_receiver_, loc);
   recv_addr = Expression::make_unary(OPERATOR_AND, recv_addr, loc);
-  Expression* recv = Runtime::make_call(Runtime::CHANRECV1, loc, 3,
-                                       td, this->channel_, recv_addr);
+  Expression* recv = Runtime::make_call(Runtime::CHANRECV1, loc, 2,
+                                       this->channel_, recv_addr);
   return Expression::make_compound(recv, recv_ref, loc)->get_backend(context);
 }
 
index 635b7febd7732ca36c9b78cdaba8ccdff72d5116..6df53498d720072507370e2e469fe77c6bf94dd6 100644 (file)
@@ -137,39 +137,31 @@ DEF_GO_RUNTIME(MAPITERNEXT, "runtime.mapiternext", P1(POINTER), R0())
 DEF_GO_RUNTIME(MAKECHAN, "runtime.makechan", P2(TYPE, INT64), R1(CHAN))
 
 // Send a value on a channel.
-DEF_GO_RUNTIME(CHANSEND, "runtime.chansend1", P3(TYPE, CHAN, POINTER), R0())
+DEF_GO_RUNTIME(CHANSEND, "runtime.chansend1", P2(CHAN, POINTER), R0())
 
 // Receive a value from a channel.
-DEF_GO_RUNTIME(CHANRECV1, "runtime.chanrecv1", P3(TYPE, CHAN, POINTER), R0())
+DEF_GO_RUNTIME(CHANRECV1, "runtime.chanrecv1", P2(CHAN, POINTER), R0())
 
 // Receive a value from a channel returning whether it is closed.
-DEF_GO_RUNTIME(CHANRECV2, "runtime.chanrecv2", P3(TYPE, CHAN, POINTER),
-              R1(BOOL))
+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",
-              P2(POINTER, INT32), R0())
+DEF_GO_RUNTIME(SELECTDEFAULT, "runtime.selectdefault", P1(POINTER), R0())
 
 // Add a send clause to a select statement.
-DEF_GO_RUNTIME(SELECTSEND, "runtime.selectsend",
-              P4(POINTER, CHAN, POINTER, INT32), R0())
+DEF_GO_RUNTIME(SELECTSEND, "runtime.selectsend", P3(POINTER, CHAN, POINTER),
+              R0())
 
-// Add a receive clause to a select statement, for a clause which does
-// not check whether the channel is closed.
+// Add a receive clause to a select statement.
 DEF_GO_RUNTIME(SELECTRECV, "runtime.selectrecv",
-              P4(POINTER, CHAN, POINTER, INT32), 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, INT32), R0())
+              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(INT32))
+DEF_GO_RUNTIME(SELECTGO, "runtime.selectgo", P1(POINTER), R1(INT))
 
 
 // Panic.
index e59217672866ce70b9501c71d6a7debe158eeb40..eb370f844812c7fce41faf253286a8617f77318b 100644 (file)
@@ -1517,14 +1517,12 @@ Tuple_receive_assignment_statement::do_lower(Gogo*, Named_object*,
                              NULL, loc);
   b->add_statement(closed_temp);
 
-  // closed_temp = chanrecv2(type, channel, &val_temp)
-  Expression* td = Expression::make_type_descriptor(this->channel_->type(),
-                                                   loc);
+  // closed_temp = chanrecv2(channel, &val_temp)
   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(Runtime::CHANRECV2,
-                                       loc, 3, td, this->channel_, p2);
+                                       loc, 2, this->channel_, p2);
   ref = Expression::make_temporary_reference(closed_temp, loc);
   ref->set_is_lvalue();
   Statement* s = Statement::make_assignment(ref, call, loc);
@@ -4516,9 +4514,6 @@ 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);
-
   Bstatement* btemp = NULL;
   if (can_take_address)
     {
@@ -4539,7 +4534,7 @@ Send_statement::do_get_backend(Translate_context* context)
       btemp = temp->get_backend(context);
     }
 
-  Expression* call = Runtime::make_call(Runtime::CHANSEND, loc, 3, td,
+  Expression* call = Runtime::make_call(Runtime::CHANSEND, loc, 2,
                                        this->channel_, val);
 
   context->gogo()->lower_expression(context->function(), NULL, &call);
@@ -4621,13 +4616,10 @@ Select_clauses::Select_clause::lower(Gogo* gogo, Named_object* function,
   Expression* selref = Expression::make_temporary_reference(sel, loc);
   selref = Expression::make_unary(OPERATOR_AND, selref, loc);
 
-  Expression* index_expr = Expression::make_integer_ul(this->index_, NULL,
-                                                      loc);
-
   if (this->is_default_)
     {
       go_assert(this->channel_ == NULL && this->val_ == NULL);
-      this->lower_default(b, selref, index_expr);
+      this->lower_default(b, selref);
       this->is_lowered_ = true;
       return;
     }
@@ -4641,9 +4633,9 @@ Select_clauses::Select_clause::lower(Gogo* gogo, Named_object* function,
                                                             loc);
 
   if (this->is_send_)
-    this->lower_send(b, selref, chanref, index_expr);
+    this->lower_send(b, selref, chanref);
   else
-    this->lower_recv(gogo, function, b, selref, chanref, index_expr);
+    this->lower_recv(gogo, function, b, selref, chanref);
 
   // Now all references should be handled through the statements, not
   // through here.
@@ -4654,12 +4646,11 @@ 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,
-                                            Expression* index_expr)
+Select_clauses::Select_clause::lower_default(Block* b, Expression* selref)
 {
   Location loc = this->location_;
-  Expression* call = Runtime::make_call(Runtime::SELECTDEFAULT, loc, 2, selref,
-                                       index_expr);
+  Expression* call = Runtime::make_call(Runtime::SELECTDEFAULT, loc, 1,
+                                       selref);
   b->add_statement(Statement::make_statement(call, true));
 }
 
@@ -4667,8 +4658,7 @@ Select_clauses::Select_clause::lower_default(Block* b, Expression* selref,
 
 void
 Select_clauses::Select_clause::lower_send(Block* b, Expression* selref,
-                                         Expression* chanref,
-                                         Expression* index_expr)
+                                         Expression* chanref)
 {
   Location loc = this->location_;
 
@@ -4687,8 +4677,8 @@ Select_clauses::Select_clause::lower_send(Block* b, Expression* selref,
   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);
+  Expression* call = Runtime::make_call(Runtime::SELECTSEND, loc, 3, selref,
+                                       chanref, valaddr);
   b->add_statement(Statement::make_statement(call, true));
 }
 
@@ -4697,8 +4687,7 @@ Select_clauses::Select_clause::lower_send(Block* b, Expression* selref,
 void
 Select_clauses::Select_clause::lower_recv(Gogo* gogo, Named_object* function,
                                          Block* b, Expression* selref,
-                                         Expression* chanref,
-                                         Expression* index_expr)
+                                         Expression* chanref)
 {
   Location loc = this->location_;
 
@@ -4715,10 +4704,9 @@ Select_clauses::Select_clause::lower_recv(Gogo* gogo, Named_object* function,
 
   Temporary_statement* closed_temp = NULL;
 
-  Expression* call;
+  Expression* caddr;
   if (this->closed_ == NULL && this->closedvar_ == NULL)
-    call = Runtime::make_call(Runtime::SELECTRECV, loc, 4, selref, chanref,
-                             valaddr, index_expr);
+    caddr = Expression::make_nil(loc);
   else
     {
       closed_temp = Statement::make_temporary(Type::lookup_bool_type(), NULL,
@@ -4726,11 +4714,12 @@ Select_clauses::Select_clause::lower_recv(Gogo* gogo, Named_object* function,
       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);
+      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));
 
   // If the block of statements is executed, arrange for the received
@@ -4958,15 +4947,14 @@ Select_clauses::get_backend(Translate_context* context,
   std::vector<std::vector<Bexpression*> > cases(count);
   std::vector<Bstatement*> clauses(count);
 
-  Type* int32_type = Type::lookup_integer_type("int32");
+  Type* int_type = Type::lookup_integer_type("int");
 
   int i = 0;
   for (Clauses::iterator p = this->clauses_.begin();
        p != this->clauses_.end();
        ++p, ++i)
     {
-      int index = p->index();
-      Expression* index_expr = Expression::make_integer_ul(index, int32_type,
+      Expression* index_expr = Expression::make_integer_ul(i, int_type,
                                                           location);
       cases[i].push_back(index_expr->get_backend(context));
 
index dac99de4cae0e1f4c768e195d2fa733f0bd7d8d2..852ab43ce2a2e5eab6285efb548bc2995fcfc694 100644 (file)
@@ -899,10 +899,9 @@ class Select_clauses
       Named_object* var, Named_object* closedvar, bool is_default,
       Block* statements, Location 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));
+    this->clauses_.push_back(Select_clause(is_send, channel, val, closed, var,
+                                          closedvar, is_default, statements,
+                                          location));
   }
 
   size_t
@@ -950,21 +949,15 @@ class Select_clauses
        is_default_(false)
     { }
 
-    Select_clause(int index, bool is_send, Expression* channel,
-                 Expression* val, Expression* closed, Named_object* var,
+    Select_clause(bool is_send, Expression* channel, Expression* val,
+                 Expression* closed, Named_object* var,
                  Named_object* closedvar, bool is_default, Block* statements,
                  Location location)
-      : 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)
+      : 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*);
@@ -1025,17 +1018,14 @@ class Select_clauses
 
    private:
     void
-    lower_default(Block*, Expression*, Expression*);
+    lower_default(Block*, Expression*);
 
     void
-    lower_send(Block*, Expression*, Expression*, Expression*);
+    lower_send(Block*, Expression*, Expression*);
 
     void
-    lower_recv(Gogo*, Named_object*, Block*, Expression*, Expression*,
-              Expression*);
+    lower_recv(Gogo*, Named_object*, Block*, 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.
index 208bb2f4c01b2a61da10e3759f8204aab45032c5..8f6a93b3848fe0d8a0a5cc28fc75068db80d6d60 100644 (file)
@@ -1160,7 +1160,7 @@ func (v Value) recv(nb bool) (val Value, ok bool) {
        } else {
                p = unsafe.Pointer(&val.ptr)
        }
-       selected, ok := chanrecv(v.typ, v.pointer(), nb, p)
+       selected, ok := chanrecv(v.pointer(), nb, p)
        if !selected {
                val = Value{}
        }
@@ -1191,7 +1191,7 @@ func (v Value) send(x Value, nb bool) (selected bool) {
        } else {
                p = unsafe.Pointer(&x.ptr)
        }
-       return chansend(v.typ, v.pointer(), p, nb)
+       return chansend(v.pointer(), p, nb)
 }
 
 // Set assigns x to the value v.
@@ -2327,10 +2327,10 @@ func chanlen(ch unsafe.Pointer) int
 // (due to the escapes() call in ValueOf).
 
 //go:noescape
-func chanrecv(t *rtype, ch unsafe.Pointer, nb bool, val unsafe.Pointer) (selected, received bool)
+func chanrecv(ch unsafe.Pointer, nb bool, val unsafe.Pointer) (selected, received bool)
 
 //go:noescape
-func chansend(t *rtype, ch unsafe.Pointer, val unsafe.Pointer, nb bool) bool
+func chansend(ch unsafe.Pointer, val unsafe.Pointer, nb bool) bool
 
 func makechan(typ *rtype, size uint64) (ch unsafe.Pointer)
 func makemap(t *rtype) (m unsafe.Pointer)
index a9574dd8ac7eb7ff7ec434705c740deb2f48b2ee..d2470bd0442d77678ffe7c46123288f52cfec84b 100644 (file)
@@ -118,8 +118,8 @@ func chanbuf(c *hchan, i uint) unsafe.Pointer {
 
 // entry point for c <- x from compiled code
 //go:nosplit
-func chansend1(t *chantype, c *hchan, elem unsafe.Pointer) {
-       chansend(t, c, elem, true, getcallerpc(unsafe.Pointer(&t)))
+func chansend1(c *hchan, elem unsafe.Pointer) {
+       chansend(c, elem, true, getcallerpc(unsafe.Pointer(&c)))
 }
 
 /*
@@ -134,14 +134,7 @@ func chansend1(t *chantype, c *hchan, elem unsafe.Pointer) {
  * been closed.  it is easiest to loop and re-run
  * the operation; we'll see that it's now closed.
  */
-func chansend(t *chantype, c *hchan, ep unsafe.Pointer, block bool, callerpc uintptr) bool {
-       if raceenabled {
-               raceReadObjectPC(t.elem, ep, callerpc, funcPC(chansend))
-       }
-       if msanenabled {
-               msanread(ep, t.elem.size)
-       }
-
+func chansend(c *hchan, ep unsafe.Pointer, block bool, callerpc uintptr) bool {
        if c == nil {
                if !block {
                        return false
@@ -400,13 +393,13 @@ func closechan(c *hchan) {
 
 // entry points for <- c from compiled code
 //go:nosplit
-func chanrecv1(t *chantype, c *hchan, elem unsafe.Pointer) {
-       chanrecv(t, c, elem, true)
+func chanrecv1(c *hchan, elem unsafe.Pointer) {
+       chanrecv(c, elem, true)
 }
 
 //go:nosplit
-func chanrecv2(t *chantype, c *hchan, elem unsafe.Pointer) (received bool) {
-       _, received = chanrecv(t, c, elem, true)
+func chanrecv2(c *hchan, elem unsafe.Pointer) (received bool) {
+       _, received = chanrecv(c, elem, true)
        return
 }
 
@@ -416,7 +409,7 @@ func chanrecv2(t *chantype, c *hchan, elem unsafe.Pointer) (received bool) {
 // Otherwise, if c is closed, zeros *ep and returns (true, false).
 // Otherwise, fills in *ep with an element and returns (true, true).
 // A non-nil ep must point to the heap or the caller's stack.
-func chanrecv(t *chantype, c *hchan, ep unsafe.Pointer, block bool) (selected, received bool) {
+func chanrecv(c *hchan, ep unsafe.Pointer, block bool) (selected, received bool) {
        // raceenabled: don't need to check ep, as it is always on the stack
        // or is new memory allocated by reflect.
 
@@ -609,8 +602,8 @@ func recv(c *hchan, sg *sudog, ep unsafe.Pointer, unlockf func()) {
 //             ... bar
 //     }
 //
-func selectnbsend(t *chantype, c *hchan, elem unsafe.Pointer) (selected bool) {
-       return chansend(t, c, elem, false, getcallerpc(unsafe.Pointer(&t)))
+func selectnbsend(c *hchan, elem unsafe.Pointer) (selected bool) {
+       return chansend(c, elem, false, getcallerpc(unsafe.Pointer(&c)))
 }
 
 // compiler implements
@@ -630,8 +623,8 @@ func selectnbsend(t *chantype, c *hchan, elem unsafe.Pointer) (selected bool) {
 //             ... bar
 //     }
 //
-func selectnbrecv(t *chantype, elem unsafe.Pointer, c *hchan) (selected bool) {
-       selected, _ = chanrecv(t, c, elem, false)
+func selectnbrecv(elem unsafe.Pointer, c *hchan) (selected bool) {
+       selected, _ = chanrecv(c, elem, false)
        return
 }
 
@@ -652,20 +645,20 @@ func selectnbrecv(t *chantype, elem unsafe.Pointer, c *hchan) (selected bool) {
 //             ... bar
 //     }
 //
-func selectnbrecv2(t *chantype, elem unsafe.Pointer, received *bool, c *hchan) (selected bool) {
+func selectnbrecv2(elem unsafe.Pointer, received *bool, c *hchan) (selected bool) {
        // TODO(khr): just return 2 values from this function, now that it is in Go.
-       selected, *received = chanrecv(t, c, elem, false)
+       selected, *received = chanrecv(c, elem, false)
        return
 }
 
 //go:linkname reflect_chansend reflect.chansend
-func reflect_chansend(t *chantype, c *hchan, elem unsafe.Pointer, nb bool) (selected bool) {
-       return chansend(t, c, elem, !nb, getcallerpc(unsafe.Pointer(&t)))
+func reflect_chansend(c *hchan, elem unsafe.Pointer, nb bool) (selected bool) {
+       return chansend(c, elem, !nb, getcallerpc(unsafe.Pointer(&c)))
 }
 
 //go:linkname reflect_chanrecv reflect.chanrecv
-func reflect_chanrecv(t *chantype, c *hchan, nb bool, elem unsafe.Pointer) (selected bool, received bool) {
-       return chanrecv(t, c, elem, !nb)
+func reflect_chanrecv(c *hchan, nb bool, elem unsafe.Pointer) (selected bool, received bool) {
+       return chanrecv(c, elem, !nb)
 }
 
 //go:linkname reflect_chanlen reflect.chanlen
index 62c404949f14ee84619a1e14f349e5b9d41832de..f0cad1bf9906a25d4a3c64ed63c8b959d317fc89 100644 (file)
@@ -18,14 +18,14 @@ import (
 //go:linkname selectdefault runtime.selectdefault
 //go:linkname selectsend runtime.selectsend
 //go:linkname selectrecv runtime.selectrecv
-//go:linkname selectrecv2 runtime.selectrecv2
 //go:linkname selectgo runtime.selectgo
 
-const (
-       debugSelect = false
+const debugSelect = false
 
+const (
        // scase.kind
-       caseRecv = iota
+       caseNil = iota
+       caseRecv
        caseSend
        caseDefault
 )
@@ -47,10 +47,9 @@ type hselect struct {
 type scase struct {
        elem        unsafe.Pointer // data element
        c           *hchan         // chan
-       pc          uintptr        // return pc
+       pc          uintptr        // return pc (for race detector / msan)
        kind        uint16
-       index       uint16 // case index
-       receivedp   *bool  // pointer to received bool (recv2)
+       receivedp   *bool // pointer to received bool, if any
        releasetime int64
 }
 
@@ -88,88 +87,64 @@ func newselect(sel *hselect, selsize int64, size int32) {
        }
 }
 
-func selectsend(sel *hselect, c *hchan, elem unsafe.Pointer, index int32) {
-       // nil cases do not compete
-       if c != nil {
-               selectsendImpl(sel, c, getcallerpc(unsafe.Pointer(&sel)), elem, index)
-       }
-       return
-}
-
-// cut in half to give stack a chance to split
-func selectsendImpl(sel *hselect, c *hchan, pc uintptr, elem unsafe.Pointer, index int32) {
+func selectsend(sel *hselect, c *hchan, elem unsafe.Pointer) {
+       pc := getcallerpc(unsafe.Pointer(&sel))
        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.index = uint16(index)
        cas.kind = caseSend
        cas.elem = elem
 
        if debugSelect {
-               print("selectsend s=", sel, " pc=", hex(cas.pc), " chan=", cas.c, " index=", cas.index, "\n")
-       }
-}
-
-func selectrecv(sel *hselect, c *hchan, elem unsafe.Pointer, index int32) {
-       // nil cases do not compete
-       if c != nil {
-               selectrecvImpl(sel, c, getcallerpc(unsafe.Pointer(&sel)), elem, nil, index)
+               print("selectsend s=", sel, " pc=", hex(cas.pc), " chan=", cas.c, "\n")
        }
-       return
 }
 
-func selectrecv2(sel *hselect, c *hchan, elem unsafe.Pointer, received *bool, index int32) {
-       // nil cases do not compete
-       if c != nil {
-               selectrecvImpl(sel, c, getcallerpc(unsafe.Pointer(&sel)), elem, received, index)
-       }
-       return
-}
-
-func selectrecvImpl(sel *hselect, c *hchan, pc uintptr, elem unsafe.Pointer, received *bool, index int32) {
+func selectrecv(sel *hselect, c *hchan, elem unsafe.Pointer, received *bool) {
+       pc := getcallerpc(unsafe.Pointer(&sel))
        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.index = uint16(index)
        cas.kind = caseRecv
        cas.elem = elem
        cas.receivedp = received
 
        if debugSelect {
-               print("selectrecv s=", sel, " pc=", hex(cas.pc), " chan=", cas.c, " index=", cas.index, "\n")
+               print("selectrecv s=", sel, " pc=", hex(cas.pc), " chan=", cas.c, "\n")
        }
 }
 
-func selectdefault(sel *hselect, index int32) {
-       selectdefaultImpl(sel, getcallerpc(unsafe.Pointer(&sel)), index)
-       return
-}
-
-func selectdefaultImpl(sel *hselect, callerpc uintptr, index int32) {
+func selectdefault(sel *hselect) {
+       pc := getcallerpc(unsafe.Pointer(&sel))
        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 = callerpc
+       cas.pc = pc
        cas.c = nil
-       cas.index = uint16(index)
        cas.kind = caseDefault
 
        if debugSelect {
-               print("selectdefault s=", sel, " pc=", hex(cas.pc), " index=", cas.index, "\n")
+               print("selectdefault s=", sel, " pc=", hex(cas.pc), "\n")
        }
 }
 
@@ -193,14 +168,11 @@ func selunlock(scases []scase, lockorder []uint16) {
        // the G that calls select runnable again and schedules it for execution.
        // When the G runs on another M, it locks all the locks and frees sel.
        // Now if the first M touches sel, it will access freed memory.
-       n := len(scases)
-       r := 0
-       // skip the default case
-       if n > 0 && scases[lockorder[0]].c == nil {
-               r = 1
-       }
-       for i := n - 1; i >= r; i-- {
+       for i := len(scases) - 1; i >= 0; i-- {
                c := scases[lockorder[i]].c
+               if c == nil {
+                       break
+               }
                if i > 0 && c == scases[lockorder[i-1]].c {
                        continue // will unlock it on the next iteration
                }
@@ -241,20 +213,15 @@ func block() {
 // *sel is on the current goroutine's stack (regardless of any
 // escaping in selectgo).
 //
-// selectgo does not return. Instead, it overwrites its return PC and
-// returns directly to the triggered select case. Because of this, it
-// cannot appear at the top of a split stack.
-func selectgo(sel *hselect) int32 {
-       _, index := selectgoImpl(sel)
-       return int32(index)
-}
-
-// selectgoImpl returns scase.pc and scase.so for the select
-// case which fired.
-func selectgoImpl(sel *hselect) (uintptr, uint16) {
+// 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 {
        if debugSelect {
                print("select: sel=", sel, "\n")
        }
+       if sel.ncase != sel.tcase {
+               throw("selectgo: case count mismatch")
+       }
 
        scaseslice := slice{unsafe.Pointer(&sel.scase), int(sel.ncase), int(sel.ncase)}
        scases := *(*[]scase)(unsafe.Pointer(&scaseslice))
@@ -347,13 +314,19 @@ func selectgoImpl(sel *hselect) (uintptr, uint16) {
 
 loop:
        // pass 1 - look for something already waiting
+       var dfli int
        var dfl *scase
+       var casi int
        var cas *scase
        for i := 0; i < int(sel.ncase); i++ {
-               cas = &scases[pollorder[i]]
+               casi = int(pollorder[i])
+               cas = &scases[casi]
                c = cas.c
 
                switch cas.kind {
+               case caseNil:
+                       continue
+
                case caseRecv:
                        sg = c.sendq.dequeue()
                        if sg != nil {
@@ -382,12 +355,14 @@ loop:
                        }
 
                case caseDefault:
+                       dfli = casi
                        dfl = cas
                }
        }
 
        if dfl != nil {
                selunlock(scases, lockorder)
+               casi = dfli
                cas = dfl
                goto retc
        }
@@ -400,7 +375,11 @@ loop:
        }
        nextp = &gp.waiting
        for _, casei := range lockorder {
-               cas = &scases[casei]
+               casi = int(casei)
+               cas = &scases[casi]
+               if cas.kind == caseNil {
+                       continue
+               }
                c = cas.c
                sg := acquireSudog()
                sg.g = gp
@@ -494,6 +473,7 @@ loop:
        // otherwise they stack up on quiet channels
        // record the successful case, if any.
        // We singly-linked up the SudoGs in lock order.
+       casi = -1
        cas = nil
        sglist = gp.waiting
        // Clear all elem before unlinking from gp.waiting.
@@ -506,11 +486,15 @@ loop:
 
        for _, casei := range lockorder {
                k = &scases[casei]
+               if k.kind == caseNil {
+                       continue
+               }
                if sglist.releasetime > 0 {
                        k.releasetime = sglist.releasetime
                }
                if sg == sglist {
                        // sg has already been dequeued by the G that woke us up.
+                       casi = int(casei)
                        cas = k
                } else {
                        c = k.c
@@ -659,7 +643,7 @@ retc:
        if cas.releasetime > 0 {
                blockevent(cas.releasetime-t0, 2)
        }
-       return cas.pc, cas.index
+       return casi
 
 sclose:
        // send on closed channel
@@ -703,22 +687,15 @@ func reflect_rselect(cases []runtimeSelect) (chosen int, recvOK bool) {
                rc := &cases[i]
                switch rc.dir {
                case selectDefault:
-                       selectdefaultImpl(sel, uintptr(i), 0)
+                       selectdefault(sel)
                case selectSend:
-                       if rc.ch == nil {
-                               break
-                       }
-                       selectsendImpl(sel, rc.ch, uintptr(i), rc.val, 0)
+                       selectsend(sel, rc.ch, rc.val)
                case selectRecv:
-                       if rc.ch == nil {
-                               break
-                       }
-                       selectrecvImpl(sel, rc.ch, uintptr(i), rc.val, r, 0)
+                       selectrecv(sel, rc.ch, rc.val, r)
                }
        }
 
-       pc, _ := selectgoImpl(sel)
-       chosen = int(pc)
+       chosen = selectgo(sel)
        recvOK = *r
        return
 }