github.com/gagliardetto/golang-go@v0.0.0-20201020153340-53909ea70814/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 "github.com/gagliardetto/golang-go/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 != OCASE {
    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.Right.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  
    72  				// convert <-c into OSELRECV(N, <-c)
    73  			case ORECV:
    74  				n = nodl(n.Pos, OSELRECV, nil, n)
    75  
    76  				n.SetTypecheck(1)
    77  				ncase.Left = n
    78  
    79  			case OSEND:
    80  				break
    81  			}
    82  		}
    83  
    84  		typecheckslice(ncase.Nbody.Slice(), ctxStmt)
    85  	}
    86  
    87  	lineno = lno
    88  }
    89  
    90  func walkselect(sel *Node) {
    91  	lno := setlineno(sel)
    92  	if sel.Nbody.Len() != 0 {
    93  		Fatalf("double walkselect")
    94  	}
    95  
    96  	init := sel.Ninit.Slice()
    97  	sel.Ninit.Set(nil)
    98  
    99  	init = append(init, walkselectcases(&sel.List)...)
   100  	sel.List.Set(nil)
   101  
   102  	sel.Nbody.Set(init)
   103  	walkstmtlist(sel.Nbody.Slice())
   104  
   105  	lineno = lno
   106  }
   107  
   108  func walkselectcases(cases *Nodes) []*Node {
   109  	n := cases.Len()
   110  	sellineno := lineno
   111  
   112  	// optimization: zero-case select
   113  	if n == 0 {
   114  		return []*Node{mkcall("block", nil, nil)}
   115  	}
   116  
   117  	// optimization: one-case select: single op.
   118  	// TODO(rsc): Reenable optimization once order.go can handle it.
   119  	// golang.org/issue/7672.
   120  	if n == 1 {
   121  		cas := cases.First()
   122  		setlineno(cas)
   123  		l := cas.Ninit.Slice()
   124  		if cas.Left != nil { // not default:
   125  			n := cas.Left
   126  			l = append(l, n.Ninit.Slice()...)
   127  			n.Ninit.Set(nil)
   128  			var ch *Node
   129  			switch n.Op {
   130  			default:
   131  				Fatalf("select %v", n.Op)
   132  
   133  				// ok already
   134  			case OSEND:
   135  				ch = n.Left
   136  
   137  			case OSELRECV, OSELRECV2:
   138  				ch = n.Right.Left
   139  				if n.Op == OSELRECV || n.List.Len() == 0 {
   140  					if n.Left == nil {
   141  						n = n.Right
   142  					} else {
   143  						n.Op = OAS
   144  					}
   145  					break
   146  				}
   147  
   148  				if n.Left == nil {
   149  					nblank = typecheck(nblank, ctxExpr|ctxAssign)
   150  					n.Left = nblank
   151  				}
   152  
   153  				n.Op = OAS2
   154  				n.List.Prepend(n.Left)
   155  				n.Rlist.Set1(n.Right)
   156  				n.Right = nil
   157  				n.Left = nil
   158  				n.SetTypecheck(0)
   159  				n = typecheck(n, ctxStmt)
   160  			}
   161  
   162  			// if ch == nil { block() }; n;
   163  			a := nod(OIF, nil, nil)
   164  
   165  			a.Left = nod(OEQ, ch, nodnil())
   166  			var ln Nodes
   167  			ln.Set(l)
   168  			a.Nbody.Set1(mkcall("block", nil, &ln))
   169  			l = ln.Slice()
   170  			a = typecheck(a, ctxStmt)
   171  			l = append(l, a, n)
   172  		}
   173  
   174  		l = append(l, cas.Nbody.Slice()...)
   175  		l = append(l, nod(OBREAK, nil, nil))
   176  		return l
   177  	}
   178  
   179  	// convert case value arguments to addresses.
   180  	// this rewrite is used by both the general code and the next optimization.
   181  	for _, cas := range cases.Slice() {
   182  		setlineno(cas)
   183  		n := cas.Left
   184  		if n == nil {
   185  			continue
   186  		}
   187  		switch n.Op {
   188  		case OSEND:
   189  			n.Right = nod(OADDR, n.Right, nil)
   190  			n.Right = typecheck(n.Right, ctxExpr)
   191  
   192  		case OSELRECV, OSELRECV2:
   193  			if n.Op == OSELRECV2 && n.List.Len() == 0 {
   194  				n.Op = OSELRECV
   195  			}
   196  
   197  			if n.Left != nil {
   198  				n.Left = nod(OADDR, n.Left, nil)
   199  				n.Left = typecheck(n.Left, ctxExpr)
   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 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  			elem := n.Left
   235  			if elem == nil {
   236  				elem = nodnil()
   237  			}
   238  			r.Left = mkcall1(chanfn("selectnbrecv", 2, ch.Type), types.Types[TBOOL], &r.Ninit, elem, ch)
   239  
   240  		case OSELRECV2:
   241  			// if selectnbrecv2(&v, &received, c) { body } else { default body }
   242  			r = nod(OIF, nil, nil)
   243  			r.Ninit.Set(cas.Ninit.Slice())
   244  			ch := n.Right.Left
   245  			elem := n.Left
   246  			if elem == nil {
   247  				elem = nodnil()
   248  			}
   249  			receivedp := nod(OADDR, n.List.First(), nil)
   250  			receivedp = typecheck(receivedp, ctxExpr)
   251  			r.Left = mkcall1(chanfn("selectnbrecv2", 2, ch.Type), types.Types[TBOOL], &r.Ninit, elem, receivedp, ch)
   252  		}
   253  
   254  		r.Left = typecheck(r.Left, ctxExpr)
   255  		r.Nbody.Set(cas.Nbody.Slice())
   256  		r.Rlist.Set(append(dflt.Ninit.Slice(), dflt.Nbody.Slice()...))
   257  		return []*Node{r, nod(OBREAK, nil, nil)}
   258  	}
   259  
   260  	var init []*Node
   261  
   262  	// generate sel-struct
   263  	lineno = sellineno
   264  	selv := temp(types.NewArray(scasetype(), int64(n)))
   265  	r := nod(OAS, selv, nil)
   266  	r = typecheck(r, ctxStmt)
   267  	init = append(init, r)
   268  
   269  	order := temp(types.NewArray(types.Types[TUINT16], 2*int64(n)))
   270  	r = nod(OAS, order, nil)
   271  	r = typecheck(r, ctxStmt)
   272  	init = append(init, r)
   273  
   274  	// register cases
   275  	for i, cas := range cases.Slice() {
   276  		setlineno(cas)
   277  
   278  		init = append(init, cas.Ninit.Slice()...)
   279  		cas.Ninit.Set(nil)
   280  
   281  		// Keep in sync with runtime/select.go.
   282  		const (
   283  			caseNil = iota
   284  			caseRecv
   285  			caseSend
   286  			caseDefault
   287  		)
   288  
   289  		var c, elem *Node
   290  		var kind int64 = caseDefault
   291  
   292  		if n := cas.Left; n != nil {
   293  			init = append(init, n.Ninit.Slice()...)
   294  
   295  			switch n.Op {
   296  			default:
   297  				Fatalf("select %v", n.Op)
   298  			case OSEND:
   299  				kind = caseSend
   300  				c = n.Left
   301  				elem = n.Right
   302  			case OSELRECV, OSELRECV2:
   303  				kind = caseRecv
   304  				c = n.Right.Left
   305  				elem = n.Left
   306  			}
   307  		}
   308  
   309  		setField := func(f string, val *Node) {
   310  			r := nod(OAS, nodSym(ODOT, nod(OINDEX, selv, nodintconst(int64(i))), lookup(f)), val)
   311  			r = typecheck(r, ctxStmt)
   312  			init = append(init, r)
   313  		}
   314  
   315  		setField("kind", nodintconst(kind))
   316  		if c != nil {
   317  			c = convnop(c, types.Types[TUNSAFEPTR])
   318  			setField("c", c)
   319  		}
   320  		if elem != nil {
   321  			elem = convnop(elem, types.Types[TUNSAFEPTR])
   322  			setField("elem", elem)
   323  		}
   324  
   325  		// TODO(mdempsky): There should be a cleaner way to
   326  		// handle this.
   327  		if instrumenting {
   328  			r = mkcall("selectsetpc", nil, nil, bytePtrToIndex(selv, int64(i)))
   329  			init = append(init, r)
   330  		}
   331  	}
   332  
   333  	// run the select
   334  	lineno = sellineno
   335  	chosen := temp(types.Types[TINT])
   336  	recvOK := temp(types.Types[TBOOL])
   337  	r = nod(OAS2, nil, nil)
   338  	r.List.Set2(chosen, recvOK)
   339  	fn := syslook("selectgo")
   340  	r.Rlist.Set1(mkcall1(fn, fn.Type.Results(), nil, bytePtrToIndex(selv, 0), bytePtrToIndex(order, 0), nodintconst(int64(n))))
   341  	r = typecheck(r, ctxStmt)
   342  	init = append(init, r)
   343  
   344  	// selv and order are no longer alive after selectgo.
   345  	init = append(init, nod(OVARKILL, selv, nil))
   346  	init = append(init, nod(OVARKILL, order, nil))
   347  
   348  	// dispatch cases
   349  	for i, cas := range cases.Slice() {
   350  		setlineno(cas)
   351  
   352  		cond := nod(OEQ, chosen, nodintconst(int64(i)))
   353  		cond = typecheck(cond, ctxExpr)
   354  		cond = defaultlit(cond, nil)
   355  
   356  		r = nod(OIF, cond, nil)
   357  
   358  		if n := cas.Left; n != nil && n.Op == OSELRECV2 {
   359  			x := nod(OAS, n.List.First(), recvOK)
   360  			x = typecheck(x, ctxStmt)
   361  			r.Nbody.Append(x)
   362  		}
   363  
   364  		r.Nbody.AppendNodes(&cas.Nbody)
   365  		r.Nbody.Append(nod(OBREAK, nil, nil))
   366  		init = append(init, r)
   367  	}
   368  
   369  	return init
   370  }
   371  
   372  // bytePtrToIndex returns a Node representing "(*byte)(&n[i])".
   373  func bytePtrToIndex(n *Node, i int64) *Node {
   374  	s := nod(OADDR, nod(OINDEX, n, nodintconst(i)), nil)
   375  	t := types.NewPtr(types.Types[TUINT8])
   376  	return convnop(s, t)
   377  }
   378  
   379  var scase *types.Type
   380  
   381  // Keep in sync with src/runtime/select.go.
   382  func scasetype() *types.Type {
   383  	if scase == nil {
   384  		scase = tostruct([]*Node{
   385  			namedfield("c", types.Types[TUNSAFEPTR]),
   386  			namedfield("elem", types.Types[TUNSAFEPTR]),
   387  			namedfield("kind", types.Types[TUINT16]),
   388  			namedfield("pc", types.Types[TUINTPTR]),
   389  			namedfield("releasetime", types.Types[TINT64]),
   390  		})
   391  		scase.SetNoalg(true)
   392  	}
   393  	return scase
   394  }