typs[96] = types.NewPtr(typs[6])
typs[97] = functype(nil, []*Node{anonfield(typs[3]), anonfield(typs[96]), anonfield(typs[84])}, []*Node{anonfield(typs[6])})
typs[98] = functype(nil, []*Node{anonfield(typs[63])}, nil)
- typs[99] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[1]), anonfield(typs[63]), anonfield(typs[15])}, []*Node{anonfield(typs[15]), anonfield(typs[6])})
+ typs[99] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[1]), anonfield(typs[63]), anonfield(typs[15]), anonfield(typs[15]), anonfield(typs[6])}, []*Node{anonfield(typs[15]), anonfield(typs[6])})
typs[100] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[15]), anonfield(typs[15])}, []*Node{anonfield(typs[7])})
typs[101] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[22]), anonfield(typs[22])}, []*Node{anonfield(typs[7])})
typs[102] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[15]), anonfield(typs[15]), anonfield(typs[7])}, []*Node{anonfield(typs[7])})
func selectnbrecv2(elem *any, received *bool, hchan <-chan any) bool
func selectsetpc(pc *uintptr)
-func selectgo(cas0 *byte, order0 *byte, pc0 *uintptr, ncases int) (int, bool)
+func selectgo(cas0 *byte, order0 *byte, pc0 *uintptr, nsends int, nrecvs int, block bool) (int, bool)
func block()
func makeslice(typ *byte, len int, cap int) unsafe.Pointer
}
func walkselectcases(cases *Nodes) []*Node {
- n := cases.Len()
+ ncas := cases.Len()
sellineno := lineno
// optimization: zero-case select
- if n == 0 {
+ if ncas == 0 {
return []*Node{mkcall("block", nil, nil)}
}
// optimization: one-case select: single op.
- // TODO(rsc): Reenable optimization once order.go can handle it.
- // golang.org/issue/7672.
- if n == 1 {
+ if ncas == 1 {
cas := cases.First()
setlineno(cas)
l := cas.Ninit.Slice()
// convert case value arguments to addresses.
// this rewrite is used by both the general code and the next optimization.
+ var dflt *Node
for _, cas := range cases.Slice() {
setlineno(cas)
n := cas.Left
if n == nil {
+ dflt = cas
continue
}
switch n.Op {
}
// optimization: two-case select but one is default: single non-blocking op.
- if n == 2 && (cases.First().Left == nil || cases.Second().Left == nil) {
- var cas *Node
- var dflt *Node
- if cases.First().Left == nil {
+ if ncas == 2 && dflt != nil {
+ cas := cases.First()
+ if cas == dflt {
cas = cases.Second()
- dflt = cases.First()
- } else {
- dflt = cases.Second()
- cas = cases.First()
}
n := cas.Left
return []*Node{r, nod(OBREAK, nil, nil)}
}
+ if dflt != nil {
+ ncas--
+ }
+ casorder := make([]*Node, ncas)
+ nsends, nrecvs := 0, 0
+
var init []*Node
// generate sel-struct
lineno = sellineno
- selv := temp(types.NewArray(scasetype(), int64(n)))
+ selv := temp(types.NewArray(scasetype(), int64(ncas)))
r := nod(OAS, selv, nil)
r = typecheck(r, ctxStmt)
init = append(init, r)
- order := temp(types.NewArray(types.Types[TUINT16], 2*int64(n)))
+ order := temp(types.NewArray(types.Types[TUINT16], 2*int64(ncas)))
r = nod(OAS, order, nil)
r = typecheck(r, ctxStmt)
init = append(init, r)
var pc0, pcs *Node
if flag_race {
- pcs = temp(types.NewArray(types.Types[TUINTPTR], int64(n)))
+ pcs = temp(types.NewArray(types.Types[TUINTPTR], int64(ncas)))
pc0 = typecheck(nod(OADDR, nod(OINDEX, pcs, nodintconst(0)), nil), ctxExpr)
} else {
pc0 = nodnil()
}
// register cases
- for i, cas := range cases.Slice() {
+ for _, cas := range cases.Slice() {
setlineno(cas)
init = append(init, cas.Ninit.Slice()...)
cas.Ninit.Set(nil)
- // Keep in sync with runtime/select.go.
- const (
- caseNil = iota
- caseRecv
- caseSend
- caseDefault
- )
+ n := cas.Left
+ if n == nil { // default:
+ continue
+ }
+ var i int
var c, elem *Node
- var kind int64 = caseDefault
-
- if n := cas.Left; n != nil {
- init = append(init, n.Ninit.Slice()...)
-
- switch n.Op {
- default:
- Fatalf("select %v", n.Op)
- case OSEND:
- kind = caseSend
- c = n.Left
- elem = n.Right
- case OSELRECV, OSELRECV2:
- kind = caseRecv
- c = n.Right.Left
- elem = n.Left
- }
+ switch n.Op {
+ default:
+ Fatalf("select %v", n.Op)
+ case OSEND:
+ i = nsends
+ nsends++
+ c = n.Left
+ elem = n.Right
+ case OSELRECV, OSELRECV2:
+ nrecvs++
+ i = ncas - nrecvs
+ c = n.Right.Left
+ elem = n.Left
}
+ casorder[i] = cas
+
setField := func(f string, val *Node) {
r := nod(OAS, nodSym(ODOT, nod(OINDEX, selv, nodintconst(int64(i))), lookup(f)), val)
r = typecheck(r, ctxStmt)
init = append(init, r)
}
- setField("kind", nodintconst(kind))
- if c != nil {
- c = convnop(c, types.Types[TUNSAFEPTR])
- setField("c", c)
- }
+ c = convnop(c, types.Types[TUNSAFEPTR])
+ setField("c", c)
if elem != nil {
elem = convnop(elem, types.Types[TUNSAFEPTR])
setField("elem", elem)
init = append(init, r)
}
}
+ if nsends+nrecvs != ncas {
+ Fatalf("walkselectcases: miscount: %v + %v != %v", nsends, nrecvs, ncas)
+ }
// run the select
lineno = sellineno
r = nod(OAS2, nil, nil)
r.List.Set2(chosen, recvOK)
fn := syslook("selectgo")
- r.Rlist.Set1(mkcall1(fn, fn.Type.Results(), nil, bytePtrToIndex(selv, 0), bytePtrToIndex(order, 0), pc0, nodintconst(int64(n))))
+ r.Rlist.Set1(mkcall1(fn, fn.Type.Results(), nil, bytePtrToIndex(selv, 0), bytePtrToIndex(order, 0), pc0, nodintconst(int64(nsends)), nodintconst(int64(nrecvs)), nodbool(dflt == nil)))
r = typecheck(r, ctxStmt)
init = append(init, r)
}
// dispatch cases
- for i, cas := range cases.Slice() {
- setlineno(cas)
-
- cond := nod(OEQ, chosen, nodintconst(int64(i)))
+ dispatch := func(cond, cas *Node) {
cond = typecheck(cond, ctxExpr)
cond = defaultlit(cond, nil)
- r = nod(OIF, cond, nil)
+ r := nod(OIF, cond, nil)
if n := cas.Left; n != nil && n.Op == OSELRECV2 {
x := nod(OAS, n.List.First(), recvOK)
init = append(init, r)
}
+ if dflt != nil {
+ setlineno(dflt)
+ dispatch(nod(OLT, chosen, nodintconst(0)), dflt)
+ }
+ for i, cas := range casorder {
+ setlineno(cas)
+ dispatch(nod(OEQ, chosen, nodintconst(int64(i))), cas)
+ }
+
return init
}
scase = tostruct([]*Node{
namedfield("c", types.Types[TUNSAFEPTR]),
namedfield("elem", types.Types[TUNSAFEPTR]),
- namedfield("kind", types.Types[TUINT16]),
})
scase.SetNoalg(true)
}
_, _, _ = Select(sCases)
}
+func TestSelectNop(t *testing.T) {
+ // "select { default: }" should always return the default case.
+ chosen, _, _ := Select([]SelectCase{{Dir: SelectDefault}})
+ if chosen != 0 {
+ t.Fatalf("expected Select to return 0, but got %#v", chosen)
+ }
+}
+
func BenchmarkSelect(b *testing.B) {
channel := make(chan int)
close(channel)
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
}
var (
// 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, pc0 *uintptr, ncases int) (int, bool) {
+func selectgo(cas0 *scase, order0 *uint16, pc0 *uintptr, nsends, nrecvs int, block bool) (int, bool) {
if debugSelect {
print("select: cas0=", cas0, "\n")
}
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]
// optimizing (and needing to test).
// 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
}
cas = &scases[casi]
c = cas.c
- switch cas.kind {
- case caseRecv:
+ if casi >= nsends {
sg = c.sendq.dequeue()
if sg != nil {
goto recv
if c.closed != 0 {
goto rclose
}
-
- case caseSend:
+ } else {
if raceenabled {
racereadpc(c.raceaddr(), casePC(casi), chansendpc)
}
}
}
- if dfli >= 0 {
+ if !block {
selunlock(scases, lockorder)
- casi = dfli
+ casi = -1
goto retc
}
*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)
}
}
}
} else {
c = k.c
- if k.kind == caseSend {
+ if int(casei) < nsends {
c.sendq.dequeueSudoG(sglist)
} else {
c.recvq.dequeueSudoG(sglist)
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
}
if raceenabled {
- if cas.kind == caseRecv && cas.elem != nil {
- raceWriteObjectPC(c.elemtype, cas.elem, casePC(casi), chanrecvpc)
- } else if cas.kind == caseSend {
+ if casi < nsends {
raceReadObjectPC(c.elemtype, cas.elem, casePC(casi), chansendpc)
+ } else if cas.elem != nil {
+ raceWriteObjectPC(c.elemtype, cas.elem, casePC(casi), chanrecvpc)
}
}
if msanenabled {
- if cas.kind == caseRecv && cas.elem != nil {
- msanwrite(cas.elem, c.elemtype.size)
- } else if cas.kind == caseSend {
+ if casi < nsends {
msanread(cas.elem, c.elemtype.size)
+ } else if cas.elem != nil {
+ msanwrite(cas.elem, c.elemtype.size)
}
}
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
+ }
+
+ // 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))
var pc0 *uintptr
if raceenabled {
- pcs := make([]uintptr, len(cases))
+ pcs := make([]uintptr, nsends+nrecvs)
for i := range pcs {
selectsetpc(&pcs[i])
}
pc0 = &pcs[0]
}
- return selectgo(&sel[0], &order[0], pc0, len(cases))
+ chosen, recvOK := selectgo(&sel[0], &order[0], pc0, 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) {