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