...

Source file src/pkg/cmd/compile/internal/gc/select.go

     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	}
   397	

View as plain text