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