github.com/megatontech/mynoteforgo@v0.0.0-20200507084910-5d0c6ea6e890/源码/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(), ctxStmt)
    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(), ctxStmt))
    31  			n := ncase.List.First()
    32  			ncase.Left = n
    33  			ncase.List.Set(nil)
    34  			switch n.Op {
    35  			default:
    36  				pos := n.Pos
    37  				if n.Op == ONAME {
    38  					// We don't have the right position for ONAME nodes (see #15459 and
    39  					// others). Using ncase.Pos for now as it will provide the correct
    40  					// line number (assuming the expression follows the "case" keyword
    41  					// on the same line). This matches the approach before 1.10.
    42  					pos = ncase.Pos
    43  				}
    44  				yyerrorl(pos, "select case must be receive, send or assign recv")
    45  
    46  			// convert x = <-c into OSELRECV(x, <-c).
    47  			// remove implicit conversions; the eventual assignment
    48  			// will reintroduce them.
    49  			case OAS:
    50  				if (n.Right.Op == OCONVNOP || n.Right.Op == OCONVIFACE) && n.Right.Implicit() {
    51  					n.Right = n.Right.Left
    52  				}
    53  
    54  				if n.Right.Op != ORECV {
    55  					yyerrorl(n.Pos, "select assignment must have receive on right hand side")
    56  					break
    57  				}
    58  
    59  				n.Op = OSELRECV
    60  
    61  				// convert x, ok = <-c into OSELRECV2(x, <-c) with ntest=ok
    62  			case OAS2RECV:
    63  				if n.Rlist.First().Op != ORECV {
    64  					yyerrorl(n.Pos, "select assignment must have receive on right hand side")
    65  					break
    66  				}
    67  
    68  				n.Op = OSELRECV2
    69  				n.Left = n.List.First()
    70  				n.List.Set1(n.List.Second())
    71  				n.Right = n.Rlist.First()
    72  				n.Rlist.Set(nil)
    73  
    74  				// convert <-c into OSELRECV(N, <-c)
    75  			case ORECV:
    76  				n = nodl(n.Pos, OSELRECV, nil, n)
    77  
    78  				n.SetTypecheck(1)
    79  				ncase.Left = n
    80  
    81  			case OSEND:
    82  				break
    83  			}
    84  		}
    85  
    86  		typecheckslice(ncase.Nbody.Slice(), ctxStmt)
    87  	}
    88  
    89  	lineno = lno
    90  }
    91  
    92  func walkselect(sel *Node) {
    93  	lno := setlineno(sel)
    94  	if sel.Nbody.Len() != 0 {
    95  		Fatalf("double walkselect")
    96  	}
    97  
    98  	init := sel.Ninit.Slice()
    99  	sel.Ninit.Set(nil)
   100  
   101  	init = append(init, walkselectcases(&sel.List)...)
   102  	sel.List.Set(nil)
   103  
   104  	sel.Nbody.Set(init)
   105  	walkstmtlist(sel.Nbody.Slice())
   106  
   107  	lineno = lno
   108  }
   109  
   110  func walkselectcases(cases *Nodes) []*Node {
   111  	n := cases.Len()
   112  	sellineno := lineno
   113  
   114  	// optimization: zero-case select
   115  	if n == 0 {
   116  		return []*Node{mkcall("block", nil, nil)}
   117  	}
   118  
   119  	// optimization: one-case select: single op.
   120  	// TODO(rsc): Reenable optimization once order.go can handle it.
   121  	// golang.org/issue/7672.
   122  	if n == 1 {
   123  		cas := cases.First()
   124  		setlineno(cas)
   125  		l := cas.Ninit.Slice()
   126  		if cas.Left != nil { // not default:
   127  			n := cas.Left
   128  			l = append(l, n.Ninit.Slice()...)
   129  			n.Ninit.Set(nil)
   130  			var ch *Node
   131  			switch n.Op {
   132  			default:
   133  				Fatalf("select %v", n.Op)
   134  
   135  				// ok already
   136  			case OSEND:
   137  				ch = n.Left
   138  
   139  			case OSELRECV, OSELRECV2:
   140  				ch = n.Right.Left
   141  				if n.Op == OSELRECV || n.List.Len() == 0 {
   142  					if n.Left == nil {
   143  						n = n.Right
   144  					} else {
   145  						n.Op = OAS
   146  					}
   147  					break
   148  				}
   149  
   150  				if n.Left == nil {
   151  					nblank = typecheck(nblank, ctxExpr|ctxAssign)
   152  					n.Left = nblank
   153  				}
   154  
   155  				n.Op = OAS2
   156  				n.List.Prepend(n.Left)
   157  				n.Rlist.Set1(n.Right)
   158  				n.Right = nil
   159  				n.Left = nil
   160  				n.SetTypecheck(0)
   161  				n = typecheck(n, ctxStmt)
   162  			}
   163  
   164  			// if ch == nil { block() }; n;
   165  			a := nod(OIF, nil, nil)
   166  
   167  			a.Left = nod(OEQ, ch, nodnil())
   168  			var ln Nodes
   169  			ln.Set(l)
   170  			a.Nbody.Set1(mkcall("block", nil, &ln))
   171  			l = ln.Slice()
   172  			a = typecheck(a, ctxStmt)
   173  			l = append(l, a, n)
   174  		}
   175  
   176  		l = append(l, cas.Nbody.Slice()...)
   177  		l = append(l, nod(OBREAK, nil, nil))
   178  		return l
   179  	}
   180  
   181  	// convert case value arguments to addresses.
   182  	// this rewrite is used by both the general code and the next optimization.
   183  	for _, cas := range cases.Slice() {
   184  		setlineno(cas)
   185  		n := cas.Left
   186  		if n == nil {
   187  			continue
   188  		}
   189  		switch n.Op {
   190  		case OSEND:
   191  			n.Right = nod(OADDR, n.Right, nil)
   192  			n.Right = typecheck(n.Right, ctxExpr)
   193  
   194  		case OSELRECV, OSELRECV2:
   195  			if n.Op == OSELRECV2 && n.List.Len() == 0 {
   196  				n.Op = OSELRECV
   197  			}
   198  
   199  			if n.Left != nil {
   200  				n.Left = nod(OADDR, n.Left, nil)
   201  				n.Left = typecheck(n.Left, ctxExpr)
   202  			}
   203  		}
   204  	}
   205  
   206  	// optimization: two-case select but one is default: single non-blocking op.
   207  	if n == 2 && (cases.First().Left == nil || cases.Second().Left == nil) {
   208  		var cas *Node
   209  		var dflt *Node
   210  		if cases.First().Left == nil {
   211  			cas = cases.Second()
   212  			dflt = cases.First()
   213  		} else {
   214  			dflt = cases.Second()
   215  			cas = cases.First()
   216  		}
   217  
   218  		n := cas.Left
   219  		setlineno(n)
   220  		r := nod(OIF, nil, nil)
   221  		r.Ninit.Set(cas.Ninit.Slice())
   222  		switch n.Op {
   223  		default:
   224  			Fatalf("select %v", n.Op)
   225  
   226  		case OSEND:
   227  			// if selectnbsend(c, v) { body } else { default body }
   228  			ch := n.Left
   229  			r.Left = mkcall1(chanfn("selectnbsend", 2, ch.Type), types.Types[TBOOL], &r.Ninit, ch, n.Right)
   230  
   231  		case OSELRECV:
   232  			// if selectnbrecv(&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  			elem := n.Left
   237  			if elem == nil {
   238  				elem = nodnil()
   239  			}
   240  			r.Left = mkcall1(chanfn("selectnbrecv", 2, ch.Type), types.Types[TBOOL], &r.Ninit, elem, ch)
   241  
   242  		case OSELRECV2:
   243  			// if selectnbrecv2(&v, &received, c) { body } else { default body }
   244  			r = nod(OIF, nil, nil)
   245  			r.Ninit.Set(cas.Ninit.Slice())
   246  			ch := n.Right.Left
   247  			elem := n.Left
   248  			if elem == nil {
   249  				elem = nodnil()
   250  			}
   251  			receivedp := nod(OADDR, n.List.First(), nil)
   252  			receivedp = typecheck(receivedp, ctxExpr)
   253  			r.Left = mkcall1(chanfn("selectnbrecv2", 2, ch.Type), types.Types[TBOOL], &r.Ninit, elem, receivedp, ch)
   254  		}
   255  
   256  		r.Left = typecheck(r.Left, ctxExpr)
   257  		r.Nbody.Set(cas.Nbody.Slice())
   258  		r.Rlist.Set(append(dflt.Ninit.Slice(), dflt.Nbody.Slice()...))
   259  		return []*Node{r, nod(OBREAK, nil, nil)}
   260  	}
   261  
   262  	var init []*Node
   263  
   264  	// generate sel-struct
   265  	lineno = sellineno
   266  	selv := temp(types.NewArray(scasetype(), int64(n)))
   267  	r := nod(OAS, selv, nil)
   268  	r = typecheck(r, ctxStmt)
   269  	init = append(init, r)
   270  
   271  	order := temp(types.NewArray(types.Types[TUINT16], 2*int64(n)))
   272  	r = nod(OAS, order, nil)
   273  	r = typecheck(r, ctxStmt)
   274  	init = append(init, r)
   275  
   276  	// register cases
   277  	for i, cas := range cases.Slice() {
   278  		setlineno(cas)
   279  
   280  		init = append(init, cas.Ninit.Slice()...)
   281  		cas.Ninit.Set(nil)
   282  
   283  		// Keep in sync with runtime/select.go.
   284  		const (
   285  			caseNil = iota
   286  			caseRecv
   287  			caseSend
   288  			caseDefault
   289  		)
   290  
   291  		var c, elem *Node
   292  		var kind int64 = caseDefault
   293  
   294  		if n := cas.Left; n != nil {
   295  			init = append(init, n.Ninit.Slice()...)
   296  
   297  			switch n.Op {
   298  			default:
   299  				Fatalf("select %v", n.Op)
   300  			case OSEND:
   301  				kind = caseSend
   302  				c = n.Left
   303  				elem = n.Right
   304  			case OSELRECV, OSELRECV2:
   305  				kind = caseRecv
   306  				c = n.Right.Left
   307  				elem = n.Left
   308  			}
   309  		}
   310  
   311  		setField := func(f string, val *Node) {
   312  			r := nod(OAS, nodSym(ODOT, nod(OINDEX, selv, nodintconst(int64(i))), lookup(f)), val)
   313  			r = typecheck(r, ctxStmt)
   314  			init = append(init, r)
   315  		}
   316  
   317  		setField("kind", nodintconst(kind))
   318  		if c != nil {
   319  			c = convnop(c, types.Types[TUNSAFEPTR])
   320  			setField("c", c)
   321  		}
   322  		if elem != nil {
   323  			elem = convnop(elem, types.Types[TUNSAFEPTR])
   324  			setField("elem", elem)
   325  		}
   326  
   327  		// TODO(mdempsky): There should be a cleaner way to
   328  		// handle this.
   329  		if instrumenting {
   330  			r = mkcall("selectsetpc", nil, nil, bytePtrToIndex(selv, int64(i)))
   331  			init = append(init, r)
   332  		}
   333  	}
   334  
   335  	// run the select
   336  	lineno = sellineno
   337  	chosen := temp(types.Types[TINT])
   338  	recvOK := temp(types.Types[TBOOL])
   339  	r = nod(OAS2, nil, nil)
   340  	r.List.Set2(chosen, recvOK)
   341  	fn := syslook("selectgo")
   342  	r.Rlist.Set1(mkcall1(fn, fn.Type.Results(), nil, bytePtrToIndex(selv, 0), bytePtrToIndex(order, 0), nodintconst(int64(n))))
   343  	r = typecheck(r, ctxStmt)
   344  	init = append(init, r)
   345  
   346  	// selv and order are no longer alive after selectgo.
   347  	init = append(init, nod(OVARKILL, selv, nil))
   348  	init = append(init, nod(OVARKILL, order, nil))
   349  
   350  	// dispatch cases
   351  	for i, cas := range cases.Slice() {
   352  		setlineno(cas)
   353  
   354  		cond := nod(OEQ, chosen, nodintconst(int64(i)))
   355  		cond = typecheck(cond, ctxExpr)
   356  		cond = defaultlit(cond, nil)
   357  
   358  		r = nod(OIF, cond, nil)
   359  
   360  		if n := cas.Left; n != nil && n.Op == OSELRECV2 {
   361  			x := nod(OAS, n.List.First(), recvOK)
   362  			x = typecheck(x, ctxStmt)
   363  			r.Nbody.Append(x)
   364  		}
   365  
   366  		r.Nbody.AppendNodes(&cas.Nbody)
   367  		r.Nbody.Append(nod(OBREAK, nil, nil))
   368  		init = append(init, r)
   369  	}
   370  
   371  	return init
   372  }
   373  
   374  // bytePtrToIndex returns a Node representing "(*byte)(&n[i])".
   375  func bytePtrToIndex(n *Node, i int64) *Node {
   376  	s := nod(OADDR, nod(OINDEX, n, nodintconst(i)), nil)
   377  	t := types.NewPtr(types.Types[TUINT8])
   378  	return convnop(s, t)
   379  }
   380  
   381  var scase *types.Type
   382  
   383  // Keep in sync with src/runtime/select.go.
   384  func scasetype() *types.Type {
   385  	if scase == nil {
   386  		scase = tostruct([]*Node{
   387  			namedfield("c", types.Types[TUNSAFEPTR]),
   388  			namedfield("elem", types.Types[TUNSAFEPTR]),
   389  			namedfield("kind", types.Types[TUINT16]),
   390  			namedfield("pc", types.Types[TUINTPTR]),
   391  			namedfield("releasetime", types.Types[TUINT64]),
   392  		})
   393  		scase.SetNoalg(true)
   394  	}
   395  	return scase
   396  }