- func walkselectcases(cases *Nodes) []*Node {
- ncas := cases.Len()
- sellineno := lineno
-
- // optimization: zero-case select
- // 针对没有case的select优化
- if ncas == 0 {
- return []*Node{mkcall("block", nil, nil)}
- }
-
- // optimization: one-case select: single op.
- // 针对1个case(单个操作)select的优化
- if ncas == 1 {
- cas := cases.First()
- setlineno(cas)
- l := cas.Ninit.Slice()
- if cas.Left != nil { // not default: 非default case
- n := cas.Left // 获取case表达式
- l = append(l, n.Ninit.Slice()...)
- n.Ninit.Set(nil)
- switch n.Op {
- default:
- Fatalf("select %v", n.Op)
-
- case OSEND: // Left <- Right
- // already ok
- // n中已包含left/right
-
- case OSELRECV, OSELRECV2: // OSELRECV(Left = <-Right.Left) OSELRECV2(List = <-Right.Left)
- if n.Op == OSELRECV || n.List.Len() == 0 { // 左侧有0或1个接收者
- if n.Left == nil { // 没有接收者
- n = n.Right // 只需保留右侧
- } else { //
- n.Op = OAS // 只有一个接收者,更新Op为OAS
- }
- break
- }
-
- if n.Left == nil { // 检查是否表达式或赋值
- nblank = typecheck(nblank, ctxExpr|ctxAssign)
- n.Left = nblank
- }
-
- n.Op = OAS2 // OSELRECV2多个接收者
- n.List.Prepend(n.Left) // 将left放在前面
- n.Rlist.Set1(n.Right)
- n.Right = nil
- n.Left = nil
- n.SetTypecheck(0)
- n = typecheck(n, ctxStmt)
- }
-
- l = append(l, n)
- }
-
- l = append(l, cas.Nbody.Slice()...) // case内的处理
- l = append(l, nod(OBREAK, nil, nil)) // 添加break
- return l
- }
-
- // 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 {
- case OSEND:
- n.Right = nod(OADDR, n.Right, nil)
- n.Right = typecheck(n.Right, ctxExpr)
-
- case OSELRECV, OSELRECV2:
- if n.Op == OSELRECV2 && n.List.Len() == 0 {
- n.Op = OSELRECV
- }
-
- if n.Left != nil {
- n.Left = nod(OADDR, n.Left, nil)
- n.Left = typecheck(n.Left, ctxExpr)
- }
- }
- }
-
- // optimization: two-case select but one is default: single non-blocking op.
- if ncas == 2 && dflt != nil {
- cas := cases.First()
- if cas == dflt {
- cas = cases.Second()
- }
-
- n := cas.Left
- setlineno(n)
- r := nod(OIF, nil, nil)
- r.Ninit.Set(cas.Ninit.Slice())
- switch n.Op {
- default:
- Fatalf("select %v", n.Op)
-
- case OSEND:
- // if selectnbsend(c, v) { body } else { default body }
- ch := n.Left
- r.Left = mkcall1(chanfn("selectnbsend", 2, ch.Type), types.Types[TBOOL], &r.Ninit, ch, n.Right)
-
- case OSELRECV:
- // if selectnbrecv(&v, c) { body } else { default body }
- ch := n.Right.Left
- elem := n.Left
- if elem == nil {
- elem = nodnil()
- }
- r.Left = mkcall1(chanfn("selectnbrecv", 2, ch.Type), types.Types[TBOOL], &r.Ninit, elem, ch)
-
- case OSELRECV2:
- // if selectnbrecv2(&v, &received, c) { body } else { default body }
- ch := n.Right.Left
- elem := n.Left
- if elem == nil {
- elem = nodnil()
- }
- receivedp := nod(OADDR, n.List.First(), nil)
- receivedp = typecheck(receivedp, ctxExpr)
- r.Left = mkcall1(chanfn("selectnbrecv2", 2, ch.Type), types.Types[TBOOL], &r.Ninit, elem, receivedp, ch)
- }
-
- r.Left = typecheck(r.Left, ctxExpr)
- r.Nbody.Set(cas.Nbody.Slice())
- r.Rlist.Set(append(dflt.Ninit.Slice(), dflt.Nbody.Slice()...))
- 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(ncas)))
- r := nod(OAS, selv, nil)
- r = typecheck(r, ctxStmt)
- init = append(init, r)
-
- // No initialization for order; runtime.selectgo is responsible for that.
- order := temp(types.NewArray(types.Types[TUINT16], 2*int64(ncas)))
-
- var pc0, pcs *Node
- if flag_race {
- 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 _, cas := range cases.Slice() {
- setlineno(cas)
-
- init = append(init, cas.Ninit.Slice()...)
- cas.Ninit.Set(nil)
-
- n := cas.Left
- if n == nil { // default:
- continue
- }
-
- var i int
- var c, elem *Node
- 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)
- }
-
- c = convnop(c, types.Types[TUNSAFEPTR])
- setField("c", c)
- if elem != nil {
- elem = convnop(elem, types.Types[TUNSAFEPTR])
- setField("elem", elem)
- }
-
- // TODO(mdempsky): There should be a cleaner way to
- // handle this.
- if flag_race {
- r = mkcall("selectsetpc", nil, nil, nod(OADDR, nod(OINDEX, pcs, nodintconst(int64(i))), nil))
- init = append(init, r)
- }
- }
- if nsends+nrecvs != ncas {
- Fatalf("walkselectcases: miscount: %v + %v != %v", nsends, nrecvs, ncas)
- }
-
- // run the select
- lineno = sellineno
- chosen := temp(types.Types[TINT])
- recvOK := temp(types.Types[TBOOL])
- 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(nsends)), nodintconst(int64(nrecvs)), nodbool(dflt == nil)))
- r = typecheck(r, ctxStmt)
- init = append(init, r)
-
- // selv and order are no longer alive after selectgo.
- init = append(init, nod(OVARKILL, selv, nil))
- init = append(init, nod(OVARKILL, order, nil))
- if flag_race {
- init = append(init, nod(OVARKILL, pcs, nil))
- }
-
- // dispatch cases
- dispatch := func(cond, cas *Node) {
- cond = typecheck(cond, ctxExpr)
- cond = defaultlit(cond, nil)
-
- r := nod(OIF, cond, nil)
-
- if n := cas.Left; n != nil && n.Op == OSELRECV2 {
- x := nod(OAS, n.List.First(), recvOK)
- x = typecheck(x, ctxStmt)
- r.Nbody.Append(x)
- }
-
- r.Nbody.AppendNodes(&cas.Nbody)
- r.Nbody.Append(nod(OBREAK, nil, nil))
- 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
- }