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