github.com/ltltlt/go-source-code@v0.0.0-20190830023027-95be009773aa/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  	lineno = lno
    82  }
    83  
    84  func walkselect(sel *Node) {
    85  	lno := setlineno(sel)
    86  	if sel.Nbody.Len() != 0 {
    87  		Fatalf("double walkselect")
    88  	}
    89  
    90  	init := sel.Ninit.Slice()
    91  	sel.Ninit.Set(nil)
    92  
    93  	init = append(init, walkselectcases(&sel.List)...)
    94  	sel.List.Set(nil)
    95  
    96  	sel.Nbody.Set(init)
    97  	walkstmtlist(sel.Nbody.Slice())
    98  
    99  	lineno = lno
   100  }
   101  
   102  func walkselectcases(cases *Nodes) []*Node {
   103  	n := cases.Len()
   104  	sellineno := lineno
   105  
   106  	// optimization: zero-case select
   107  	if n == 0 {
   108  		return []*Node{mkcall("block", nil, nil)}
   109  	}
   110  
   111  	// optimization: one-case select: single op.
   112  	// TODO(rsc): Reenable optimization once order.go can handle it.
   113  	// golang.org/issue/7672.
   114  	if n == 1 {
   115  		cas := cases.First()
   116  		setlineno(cas)
   117  		l := cas.Ninit.Slice()
   118  		if cas.Left != nil { // not default:
   119  			n := cas.Left
   120  			l = append(l, n.Ninit.Slice()...)
   121  			n.Ninit.Set(nil)
   122  			var ch *Node
   123  			switch n.Op {
   124  			default:
   125  				Fatalf("select %v", n.Op)
   126  
   127  				// ok already
   128  			case OSEND:
   129  				ch = n.Left
   130  
   131  			case OSELRECV, OSELRECV2:
   132  				ch = n.Right.Left
   133  				if n.Op == OSELRECV || n.List.Len() == 0 {
   134  					if n.Left == nil {
   135  						n = n.Right
   136  					} else {
   137  						n.Op = OAS
   138  					}
   139  					break
   140  				}
   141  
   142  				if n.Left == nil {
   143  					nblank = typecheck(nblank, Erv|Easgn)
   144  					n.Left = nblank
   145  				}
   146  
   147  				n.Op = OAS2
   148  				n.List.Prepend(n.Left)
   149  				n.Rlist.Set1(n.Right)
   150  				n.Right = nil
   151  				n.Left = nil
   152  				n.SetTypecheck(0)
   153  				n = typecheck(n, Etop)
   154  			}
   155  
   156  			// if ch == nil { block() }; n;
   157  			a := nod(OIF, nil, nil)
   158  
   159  			a.Left = nod(OEQ, ch, nodnil())
   160  			var ln Nodes
   161  			ln.Set(l)
   162  			a.Nbody.Set1(mkcall("block", nil, &ln))
   163  			l = ln.Slice()
   164  			a = typecheck(a, Etop)
   165  			l = append(l, a, n)
   166  		}
   167  
   168  		l = append(l, cas.Nbody.Slice()...)
   169  		l = append(l, nod(OBREAK, nil, nil))
   170  		return l
   171  	}
   172  
   173  	// convert case value arguments to addresses.
   174  	// this rewrite is used by both the general code and the next optimization.
   175  	for _, cas := range cases.Slice() {
   176  		setlineno(cas)
   177  		n := cas.Left
   178  		if n == nil {
   179  			continue
   180  		}
   181  		switch n.Op {
   182  		case OSEND:
   183  			n.Right = nod(OADDR, n.Right, nil)
   184  			n.Right = typecheck(n.Right, Erv)
   185  
   186  		case OSELRECV, OSELRECV2:
   187  			if n.Op == OSELRECV2 && n.List.Len() == 0 {
   188  				n.Op = OSELRECV
   189  			}
   190  			if n.Op == OSELRECV2 {
   191  				n.List.SetFirst(nod(OADDR, n.List.First(), nil))
   192  				n.List.SetFirst(typecheck(n.List.First(), Erv))
   193  			}
   194  
   195  			if n.Left == nil {
   196  				n.Left = nodnil()
   197  			} else {
   198  				n.Left = nod(OADDR, n.Left, nil)
   199  				n.Left = typecheck(n.Left, Erv)
   200  			}
   201  		}
   202  	}
   203  
   204  	// optimization: two-case select but one is default: single non-blocking op.
   205  	if n == 2 && (cases.First().Left == nil || cases.Second().Left == nil) {
   206  		var cas *Node
   207  		var dflt *Node
   208  		if cases.First().Left == nil {
   209  			cas = cases.Second()
   210  			dflt = cases.First()
   211  		} else {
   212  			dflt = cases.Second()
   213  			cas = cases.First()
   214  		}
   215  
   216  		n := cas.Left
   217  		setlineno(n)
   218  		r := nod(OIF, nil, nil)
   219  		r.Ninit.Set(cas.Ninit.Slice())
   220  		switch n.Op {
   221  		default:
   222  			Fatalf("select %v", n.Op)
   223  
   224  		case OSEND:
   225  			// if selectnbsend(c, v) { body } else { default body }
   226  			ch := n.Left
   227  			r.Left = mkcall1(chanfn("selectnbsend", 2, ch.Type), types.Types[TBOOL], &r.Ninit, ch, n.Right)
   228  
   229  		case OSELRECV:
   230  			// if c != nil && selectnbrecv(&v, c) { body } else { default body }
   231  			r = nod(OIF, nil, nil)
   232  			r.Ninit.Set(cas.Ninit.Slice())
   233  			ch := n.Right.Left
   234  			r.Left = mkcall1(chanfn("selectnbrecv", 2, ch.Type), types.Types[TBOOL], &r.Ninit, n.Left, ch)
   235  
   236  		case OSELRECV2:
   237  			// if c != nil && selectnbrecv2(&v, c) { body } else { default body }
   238  			r = nod(OIF, nil, nil)
   239  			r.Ninit.Set(cas.Ninit.Slice())
   240  			ch := n.Right.Left
   241  			r.Left = mkcall1(chanfn("selectnbrecv2", 2, ch.Type), types.Types[TBOOL], &r.Ninit, n.Left, n.List.First(), ch)
   242  		}
   243  
   244  		r.Left = typecheck(r.Left, Erv)
   245  		r.Nbody.Set(cas.Nbody.Slice())
   246  		r.Rlist.Set(append(dflt.Ninit.Slice(), dflt.Nbody.Slice()...))
   247  		return []*Node{r, nod(OBREAK, nil, nil)}
   248  	}
   249  
   250  	var init []*Node
   251  
   252  	// generate sel-struct
   253  	lineno = sellineno
   254  	selv := temp(selecttype(int64(n)))
   255  	r := nod(OAS, selv, nil)
   256  	r = typecheck(r, Etop)
   257  	init = append(init, r)
   258  	var_ := conv(conv(nod(OADDR, selv, nil), types.Types[TUNSAFEPTR]), types.NewPtr(types.Types[TUINT8]))
   259  	r = mkcall("newselect", nil, nil, var_, nodintconst(selv.Type.Width), nodintconst(int64(n)))
   260  	r = typecheck(r, Etop)
   261  	init = append(init, r)
   262  
   263  	// register cases
   264  	for _, cas := range cases.Slice() {
   265  		setlineno(cas)
   266  
   267  		init = append(init, cas.Ninit.Slice()...)
   268  		cas.Ninit.Set(nil)
   269  
   270  		var x *Node
   271  		if n := cas.Left; n != nil {
   272  			init = append(init, n.Ninit.Slice()...)
   273  
   274  			switch n.Op {
   275  			default:
   276  				Fatalf("select %v", n.Op)
   277  			case OSEND:
   278  				// selectsend(sel *byte, hchan *chan any, elem *any)
   279  				x = mkcall1(chanfn("selectsend", 2, n.Left.Type), nil, nil, var_, n.Left, n.Right)
   280  			case OSELRECV:
   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, nodnil())
   283  			case OSELRECV2:
   284  				// selectrecv(sel *byte, hchan *chan any, elem *any, received *bool)
   285  				x = mkcall1(chanfn("selectrecv", 2, n.Right.Left.Type), nil, nil, var_, n.Right.Left, n.Left, n.List.First())
   286  			}
   287  		} else {
   288  			// selectdefault(sel *byte)
   289  			x = mkcall("selectdefault", nil, nil, var_)
   290  		}
   291  
   292  		init = append(init, x)
   293  	}
   294  
   295  	// run the select
   296  	lineno = sellineno
   297  	chosen := temp(types.Types[TINT])
   298  	r = nod(OAS, chosen, mkcall("selectgo", types.Types[TINT], nil, var_))
   299  	r = typecheck(r, Etop)
   300  	init = append(init, r)
   301  
   302  	// selv is no longer alive after selectgo.
   303  	init = append(init, nod(OVARKILL, selv, nil))
   304  
   305  	// dispatch cases
   306  	for i, cas := range cases.Slice() {
   307  		setlineno(cas)
   308  
   309  		cond := nod(OEQ, chosen, nodintconst(int64(i)))
   310  		cond = typecheck(cond, Erv)
   311  
   312  		r = nod(OIF, cond, nil)
   313  		r.Nbody.AppendNodes(&cas.Nbody)
   314  		r.Nbody.Append(nod(OBREAK, nil, nil))
   315  		init = append(init, r)
   316  	}
   317  
   318  	return init
   319  }
   320  
   321  // Keep in sync with src/runtime/select.go.
   322  func selecttype(size int64) *types.Type {
   323  	// TODO(dvyukov): it's possible to generate Scase only once
   324  	// and then cache; and also cache Select per size.
   325  
   326  	scase := tostruct([]*Node{
   327  		namedfield("elem", types.NewPtr(types.Types[TUINT8])),
   328  		namedfield("chan", types.NewPtr(types.Types[TUINT8])),
   329  		namedfield("pc", types.Types[TUINTPTR]),
   330  		namedfield("kind", types.Types[TUINT16]),
   331  		namedfield("receivedp", types.NewPtr(types.Types[TUINT8])),
   332  		namedfield("releasetime", types.Types[TUINT64]),
   333  	})
   334  	scase.SetNoalg(true)
   335  
   336  	sel := tostruct([]*Node{
   337  		namedfield("tcase", types.Types[TUINT16]),
   338  		namedfield("ncase", types.Types[TUINT16]),
   339  		namedfield("pollorder", types.NewPtr(types.Types[TUINT8])),
   340  		namedfield("lockorder", types.NewPtr(types.Types[TUINT8])),
   341  		namedfield("scase", types.NewArray(scase, size)),
   342  		namedfield("lockorderarr", types.NewArray(types.Types[TUINT16], size)),
   343  		namedfield("pollorderarr", types.NewArray(types.Types[TUINT16], size)),
   344  	})
   345  	sel.SetNoalg(true)
   346  
   347  	return sel
   348  }