github.com/goproxy0/go@v0.0.0-20171111080102-49cc0c489d2c/src/cmd/compile/internal/gc/select.go (about)

     1  // Copyright 2009 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package gc
     6  
     7  import "cmd/compile/internal/types"
     8  
     9  // select
    10  func typecheckselect(sel *Node) {
    11  	var def *Node
    12  	lno := setlineno(sel)
    13  	typecheckslice(sel.Ninit.Slice(), Etop)
    14  	for _, ncase := range sel.List.Slice() {
    15  		if ncase.Op != OXCASE {
    16  			setlineno(ncase)
    17  			Fatalf("typecheckselect %v", ncase.Op)
    18  		}
    19  
    20  		if ncase.List.Len() == 0 {
    21  			// default
    22  			if def != nil {
    23  				yyerrorl(ncase.Pos, "multiple defaults in select (first at %v)", def.Line())
    24  			} else {
    25  				def = ncase
    26  			}
    27  		} else if ncase.List.Len() > 1 {
    28  			yyerrorl(ncase.Pos, "select cases cannot be lists")
    29  		} else {
    30  			ncase.List.SetFirst(typecheck(ncase.List.First(), Etop))
    31  			n := ncase.List.First()
    32  			ncase.Left = n
    33  			ncase.List.Set(nil)
    34  			switch n.Op {
    35  			default:
    36  				yyerrorl(n.Pos, "select case must be receive, send or assign recv")
    37  
    38  			// convert x = <-c into OSELRECV(x, <-c).
    39  			// remove implicit conversions; the eventual assignment
    40  			// will reintroduce them.
    41  			case OAS:
    42  				if (n.Right.Op == OCONVNOP || n.Right.Op == OCONVIFACE) && n.Right.Implicit() {
    43  					n.Right = n.Right.Left
    44  				}
    45  
    46  				if n.Right.Op != ORECV {
    47  					yyerrorl(n.Pos, "select assignment must have receive on right hand side")
    48  					break
    49  				}
    50  
    51  				n.Op = OSELRECV
    52  
    53  				// convert x, ok = <-c into OSELRECV2(x, <-c) with ntest=ok
    54  			case OAS2RECV:
    55  				if n.Rlist.First().Op != ORECV {
    56  					yyerrorl(n.Pos, "select assignment must have receive on right hand side")
    57  					break
    58  				}
    59  
    60  				n.Op = OSELRECV2
    61  				n.Left = n.List.First()
    62  				n.List.Set1(n.List.Second())
    63  				n.Right = n.Rlist.First()
    64  				n.Rlist.Set(nil)
    65  
    66  				// convert <-c into OSELRECV(N, <-c)
    67  			case ORECV:
    68  				n = nodl(n.Pos, OSELRECV, nil, n)
    69  
    70  				n.SetTypecheck(1)
    71  				ncase.Left = n
    72  
    73  			case OSEND:
    74  				break
    75  			}
    76  		}
    77  
    78  		typecheckslice(ncase.Nbody.Slice(), Etop)
    79  	}
    80  
    81  	sel.Xoffset = int64(sel.List.Len())
    82  	lineno = lno
    83  }
    84  
    85  func walkselect(sel *Node) {
    86  	if sel.List.Len() == 0 && sel.Xoffset != 0 {
    87  		Fatalf("double walkselect") // already rewrote
    88  	}
    89  
    90  	lno := setlineno(sel)
    91  	i := sel.List.Len()
    92  
    93  	// optimization: zero-case select
    94  	var init []*Node
    95  	var r *Node
    96  	var n *Node
    97  	var var_ *Node
    98  	var selv *Node
    99  	var chosen *Node
   100  	if i == 0 {
   101  		sel.Nbody.Set1(mkcall("block", nil, nil))
   102  		goto out
   103  	}
   104  
   105  	// optimization: one-case select: single op.
   106  	// TODO(rsc): Reenable optimization once order.go can handle it.
   107  	// golang.org/issue/7672.
   108  	if i == 1 {
   109  		cas := sel.List.First()
   110  		setlineno(cas)
   111  		l := cas.Ninit.Slice()
   112  		if cas.Left != nil { // not default:
   113  			n := cas.Left
   114  			l = append(l, n.Ninit.Slice()...)
   115  			n.Ninit.Set(nil)
   116  			var ch *Node
   117  			switch n.Op {
   118  			default:
   119  				Fatalf("select %v", n.Op)
   120  
   121  				// ok already
   122  			case OSEND:
   123  				ch = n.Left
   124  
   125  			case OSELRECV, OSELRECV2:
   126  				ch = n.Right.Left
   127  				if n.Op == OSELRECV || n.List.Len() == 0 {
   128  					if n.Left == nil {
   129  						n = n.Right
   130  					} else {
   131  						n.Op = OAS
   132  					}
   133  					break
   134  				}
   135  
   136  				if n.Left == nil {
   137  					nblank = typecheck(nblank, Erv|Easgn)
   138  					n.Left = nblank
   139  				}
   140  
   141  				n.Op = OAS2
   142  				n.List.Prepend(n.Left)
   143  				n.Rlist.Set1(n.Right)
   144  				n.Right = nil
   145  				n.Left = nil
   146  				n.SetTypecheck(0)
   147  				n = typecheck(n, Etop)
   148  			}
   149  
   150  			// if ch == nil { block() }; n;
   151  			a := nod(OIF, nil, nil)
   152  
   153  			a.Left = nod(OEQ, ch, nodnil())
   154  			var ln Nodes
   155  			ln.Set(l)
   156  			a.Nbody.Set1(mkcall("block", nil, &ln))
   157  			l = ln.Slice()
   158  			a = typecheck(a, Etop)
   159  			l = append(l, a, n)
   160  		}
   161  
   162  		l = append(l, cas.Nbody.Slice()...)
   163  		l = append(l, nod(OBREAK, nil, nil))
   164  		sel.Nbody.Set(l)
   165  		goto out
   166  	}
   167  
   168  	// convert case value arguments to addresses.
   169  	// this rewrite is used by both the general code and the next optimization.
   170  	for _, cas := range sel.List.Slice() {
   171  		setlineno(cas)
   172  		n = cas.Left
   173  		if n == nil {
   174  			continue
   175  		}
   176  		switch n.Op {
   177  		case OSEND:
   178  			n.Right = nod(OADDR, n.Right, nil)
   179  			n.Right = typecheck(n.Right, Erv)
   180  
   181  		case OSELRECV, OSELRECV2:
   182  			if n.Op == OSELRECV2 && n.List.Len() == 0 {
   183  				n.Op = OSELRECV
   184  			}
   185  			if n.Op == OSELRECV2 {
   186  				n.List.SetFirst(nod(OADDR, n.List.First(), nil))
   187  				n.List.SetFirst(typecheck(n.List.First(), Erv))
   188  			}
   189  
   190  			if n.Left == nil {
   191  				n.Left = nodnil()
   192  			} else {
   193  				n.Left = nod(OADDR, n.Left, nil)
   194  				n.Left = typecheck(n.Left, Erv)
   195  			}
   196  		}
   197  	}
   198  
   199  	// optimization: two-case select but one is default: single non-blocking op.
   200  	if i == 2 && (sel.List.First().Left == nil || sel.List.Second().Left == nil) {
   201  		var cas *Node
   202  		var dflt *Node
   203  		if sel.List.First().Left == nil {
   204  			cas = sel.List.Second()
   205  			dflt = sel.List.First()
   206  		} else {
   207  			dflt = sel.List.Second()
   208  			cas = sel.List.First()
   209  		}
   210  
   211  		n := cas.Left
   212  		setlineno(n)
   213  		r := nod(OIF, nil, nil)
   214  		r.Ninit.Set(cas.Ninit.Slice())
   215  		switch n.Op {
   216  		default:
   217  			Fatalf("select %v", n.Op)
   218  
   219  		case OSEND:
   220  			// if selectnbsend(c, v) { body } else { default body }
   221  			ch := n.Left
   222  			r.Left = mkcall1(chanfn("selectnbsend", 2, ch.Type), types.Types[TBOOL], &r.Ninit, ch, n.Right)
   223  
   224  		case OSELRECV:
   225  			// if c != nil && selectnbrecv(&v, c) { body } else { default body }
   226  			r = nod(OIF, nil, nil)
   227  			r.Ninit.Set(cas.Ninit.Slice())
   228  			ch := n.Right.Left
   229  			r.Left = mkcall1(chanfn("selectnbrecv", 2, ch.Type), types.Types[TBOOL], &r.Ninit, n.Left, ch)
   230  
   231  		case OSELRECV2:
   232  			// if c != nil && selectnbrecv2(&v, c) { body } else { default body }
   233  			r = nod(OIF, nil, nil)
   234  			r.Ninit.Set(cas.Ninit.Slice())
   235  			ch := n.Right.Left
   236  			r.Left = mkcall1(chanfn("selectnbrecv2", 2, ch.Type), types.Types[TBOOL], &r.Ninit, n.Left, n.List.First(), ch)
   237  		}
   238  
   239  		r.Left = typecheck(r.Left, Erv)
   240  		r.Nbody.Set(cas.Nbody.Slice())
   241  		r.Rlist.Set(append(dflt.Ninit.Slice(), dflt.Nbody.Slice()...))
   242  		sel.Nbody.Set2(r, nod(OBREAK, nil, nil))
   243  		goto out
   244  	}
   245  
   246  	init = sel.Ninit.Slice()
   247  	sel.Ninit.Set(nil)
   248  
   249  	// generate sel-struct
   250  	setlineno(sel)
   251  	selv = temp(selecttype(sel.Xoffset))
   252  	r = nod(OAS, selv, nil)
   253  	r = typecheck(r, Etop)
   254  	init = append(init, r)
   255  	var_ = conv(conv(nod(OADDR, selv, nil), types.Types[TUNSAFEPTR]), types.NewPtr(types.Types[TUINT8]))
   256  	r = mkcall("newselect", nil, nil, var_, nodintconst(selv.Type.Width), nodintconst(sel.Xoffset))
   257  	r = typecheck(r, Etop)
   258  	init = append(init, r)
   259  
   260  	// register cases
   261  	for _, cas := range sel.List.Slice() {
   262  		setlineno(cas)
   263  
   264  		init = append(init, cas.Ninit.Slice()...)
   265  		cas.Ninit.Set(nil)
   266  
   267  		var x *Node
   268  		if n := cas.Left; n != nil {
   269  			init = append(init, n.Ninit.Slice()...)
   270  
   271  			switch n.Op {
   272  			default:
   273  				Fatalf("select %v", n.Op)
   274  			case OSEND:
   275  				// selectsend(sel *byte, hchan *chan any, elem *any)
   276  				x = mkcall1(chanfn("selectsend", 2, n.Left.Type), nil, nil, var_, n.Left, n.Right)
   277  			case OSELRECV:
   278  				// selectrecv(sel *byte, hchan *chan any, elem *any, received *bool)
   279  				x = mkcall1(chanfn("selectrecv", 2, n.Right.Left.Type), nil, nil, var_, n.Right.Left, n.Left, nodnil())
   280  			case OSELRECV2:
   281  				// selectrecv(sel *byte, hchan *chan any, elem *any, received *bool)
   282  				x = mkcall1(chanfn("selectrecv", 2, n.Right.Left.Type), nil, nil, var_, n.Right.Left, n.Left, n.List.First())
   283  			}
   284  		} else {
   285  			// selectdefault(sel *byte)
   286  			x = mkcall("selectdefault", nil, nil, var_)
   287  		}
   288  
   289  		init = append(init, x)
   290  	}
   291  
   292  	// run the select
   293  	setlineno(sel)
   294  	chosen = temp(types.Types[TINT])
   295  	r = nod(OAS, chosen, mkcall("selectgo", types.Types[TINT], nil, var_))
   296  	r = typecheck(r, Etop)
   297  	init = append(init, r)
   298  
   299  	// selv is no longer alive after selectgo.
   300  	init = append(init, nod(OVARKILL, selv, nil))
   301  
   302  	// dispatch cases
   303  	for i, cas := range sel.List.Slice() {
   304  		setlineno(cas)
   305  
   306  		cond := nod(OEQ, chosen, nodintconst(int64(i)))
   307  		cond = typecheck(cond, Erv)
   308  
   309  		r = nod(OIF, cond, nil)
   310  		r.Nbody.AppendNodes(&cas.Nbody)
   311  		r.Nbody.Append(nod(OBREAK, nil, nil))
   312  		init = append(init, r)
   313  	}
   314  
   315  	sel.Nbody.Set(init)
   316  
   317  out:
   318  	sel.List.Set(nil)
   319  	walkstmtlist(sel.Nbody.Slice())
   320  	lineno = lno
   321  }
   322  
   323  // Keep in sync with src/runtime/select.go.
   324  func selecttype(size int64) *types.Type {
   325  	// TODO(dvyukov): it's possible to generate Scase only once
   326  	// and then cache; and also cache Select per size.
   327  
   328  	scase := tostruct([]*Node{
   329  		namedfield("elem", types.NewPtr(types.Types[TUINT8])),
   330  		namedfield("chan", types.NewPtr(types.Types[TUINT8])),
   331  		namedfield("pc", types.Types[TUINTPTR]),
   332  		namedfield("kind", types.Types[TUINT16]),
   333  		namedfield("receivedp", types.NewPtr(types.Types[TUINT8])),
   334  		namedfield("releasetime", types.Types[TUINT64]),
   335  	})
   336  	scase.SetNoalg(true)
   337  
   338  	sel := tostruct([]*Node{
   339  		namedfield("tcase", types.Types[TUINT16]),
   340  		namedfield("ncase", types.Types[TUINT16]),
   341  		namedfield("pollorder", types.NewPtr(types.Types[TUINT8])),
   342  		namedfield("lockorder", types.NewPtr(types.Types[TUINT8])),
   343  		namedfield("scase", types.NewArray(scase, size)),
   344  		namedfield("lockorderarr", types.NewArray(types.Types[TUINT16], size)),
   345  		namedfield("pollorderarr", types.NewArray(types.Types[TUINT16], size)),
   346  	})
   347  	sel.SetNoalg(true)
   348  
   349  	return sel
   350  }