github.com/d4l3k/go@v0.0.0-20151015000803-65fc379daeda/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  /*
     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  			Fatalf("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.List = list1(n.List.Next.N)
    69  				n.Right = n.Rlist.N
    70  				n.Rlist = nil
    71  
    72  				// convert <-c into OSELRECV(N, <-c)
    73  			case ORECV:
    74  				n = Nod(OSELRECV, nil, n)
    75  
    76  				n.Typecheck = 1
    77  				ncase.Left = n
    78  
    79  			case OSEND:
    80  				break
    81  			}
    82  		}
    83  
    84  		typechecklist(ncase.Nbody, Etop)
    85  	}
    86  
    87  	sel.Xoffset = int64(count)
    88  	lineno = int32(lno)
    89  }
    90  
    91  func walkselect(sel *Node) {
    92  	if sel.List == nil && sel.Xoffset != 0 {
    93  		Fatalf("double walkselect") // already rewrote
    94  	}
    95  
    96  	lno := int(setlineno(sel))
    97  	i := count(sel.List)
    98  
    99  	// optimization: zero-case select
   100  	var init *NodeList
   101  	var r *Node
   102  	var n *Node
   103  	var var_ *Node
   104  	var selv *Node
   105  	var cas *Node
   106  	if i == 0 {
   107  		sel.Nbody = list1(mkcall("block", nil, nil))
   108  		goto out
   109  	}
   110  
   111  	// optimization: one-case select: single op.
   112  	// TODO(rsc): Reenable optimization once order.c can handle it.
   113  	// golang.org/issue/7672.
   114  	if i == 1 {
   115  		cas := sel.List.N
   116  		setlineno(cas)
   117  		l := cas.Ninit
   118  		if cas.Left != nil { // not default:
   119  			n := cas.Left
   120  			l = concat(l, n.Ninit)
   121  			n.Ninit = nil
   122  			var ch *Node
   123  			switch n.Op {
   124  			default:
   125  				Fatalf("select %v", Oconv(int(n.Op), 0))
   126  
   127  				// ok already
   128  			case OSEND:
   129  				ch = n.Left
   130  
   131  			case OSELRECV, OSELRECV2:
   132  				ch = n.Right.Left
   133  				if n.Op == OSELRECV || n.List == nil {
   134  					if n.Left == nil {
   135  						n = n.Right
   136  					} else {
   137  						n.Op = OAS
   138  					}
   139  					break
   140  				}
   141  
   142  				if n.Left == nil {
   143  					typecheck(&nblank, Erv|Easgn)
   144  					n.Left = nblank
   145  				}
   146  
   147  				n.Op = OAS2
   148  				n.List = concat(list1(n.Left), n.List)
   149  				n.Rlist = list1(n.Right)
   150  				n.Right = nil
   151  				n.Left = nil
   152  				n.Typecheck = 0
   153  				typecheck(&n, Etop)
   154  			}
   155  
   156  			// if ch == nil { block() }; n;
   157  			a := Nod(OIF, nil, nil)
   158  
   159  			a.Left = Nod(OEQ, ch, nodnil())
   160  			a.Nbody = list1(mkcall("block", nil, &l))
   161  			typecheck(&a, Etop)
   162  			l = list(l, a)
   163  			l = list(l, n)
   164  		}
   165  
   166  		l = concat(l, cas.Nbody)
   167  		sel.Nbody = l
   168  		goto out
   169  	}
   170  
   171  	// convert case value arguments to addresses.
   172  	// this rewrite is used by both the general code and the next optimization.
   173  	for l := sel.List; l != nil; l = l.Next {
   174  		cas = l.N
   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  			typecheck(&n.Right, Erv)
   184  
   185  		case OSELRECV, OSELRECV2:
   186  			if n.Op == OSELRECV2 && n.List == nil {
   187  				n.Op = OSELRECV
   188  			}
   189  			if n.Op == OSELRECV2 {
   190  				n.List.N = Nod(OADDR, n.List.N, nil)
   191  				typecheck(&n.List.N, Erv)
   192  			}
   193  
   194  			if n.Left == nil {
   195  				n.Left = nodnil()
   196  			} else {
   197  				n.Left = Nod(OADDR, n.Left, nil)
   198  				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.N.Left == nil || sel.List.Next.N.Left == nil) {
   205  		var cas *Node
   206  		var dflt *Node
   207  		if sel.List.N.Left == nil {
   208  			cas = sel.List.Next.N
   209  			dflt = sel.List.N
   210  		} else {
   211  			dflt = sel.List.Next.N
   212  			cas = sel.List.N
   213  		}
   214  
   215  		n := cas.Left
   216  		setlineno(n)
   217  		r := Nod(OIF, nil, nil)
   218  		r.Ninit = cas.Ninit
   219  		switch n.Op {
   220  		default:
   221  			Fatalf("select %v", Oconv(int(n.Op), 0))
   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 = cas.Ninit
   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 = cas.Ninit
   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.N, ch)
   244  		}
   245  
   246  		typecheck(&r.Left, Erv)
   247  		r.Nbody = cas.Nbody
   248  		r.Rlist = concat(dflt.Ninit, dflt.Nbody)
   249  		sel.Nbody = list1(r)
   250  		goto out
   251  	}
   252  
   253  	init = sel.Ninit
   254  	sel.Ninit = nil
   255  
   256  	// generate sel-struct
   257  	setlineno(sel)
   258  
   259  	selv = temp(selecttype(int32(sel.Xoffset)))
   260  	r = Nod(OAS, selv, nil)
   261  	typecheck(&r, Etop)
   262  	init = list(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  	typecheck(&r, Etop)
   266  	init = list(init, r)
   267  
   268  	// register cases
   269  	for l := sel.List; l != nil; l = l.Next {
   270  		cas = l.N
   271  		setlineno(cas)
   272  		n = cas.Left
   273  		r = Nod(OIF, nil, nil)
   274  		r.Ninit = cas.Ninit
   275  		cas.Ninit = nil
   276  		if n != nil {
   277  			r.Ninit = concat(r.Ninit, n.Ninit)
   278  			n.Ninit = nil
   279  		}
   280  
   281  		if n == nil {
   282  			// selectdefault(sel *byte);
   283  			r.Left = mkcall("selectdefault", Types[TBOOL], &r.Ninit, var_)
   284  		} else {
   285  			switch n.Op {
   286  			default:
   287  				Fatalf("select %v", Oconv(int(n.Op), 0))
   288  
   289  				// selectsend(sel *byte, hchan *chan any, elem *any) (selected bool);
   290  			case OSEND:
   291  				r.Left = mkcall1(chanfn("selectsend", 2, n.Left.Type), Types[TBOOL], &r.Ninit, var_, n.Left, n.Right)
   292  
   293  				// selectrecv(sel *byte, hchan *chan any, elem *any) (selected bool);
   294  			case OSELRECV:
   295  				r.Left = mkcall1(chanfn("selectrecv", 2, n.Right.Left.Type), Types[TBOOL], &r.Ninit, var_, n.Right.Left, n.Left)
   296  
   297  				// selectrecv2(sel *byte, hchan *chan any, elem *any, received *bool) (selected bool);
   298  			case OSELRECV2:
   299  				r.Left = mkcall1(chanfn("selectrecv2", 2, n.Right.Left.Type), Types[TBOOL], &r.Ninit, var_, n.Right.Left, n.Left, n.List.N)
   300  			}
   301  		}
   302  
   303  		// selv is no longer alive after use.
   304  		r.Nbody = list(r.Nbody, Nod(OVARKILL, selv, nil))
   305  
   306  		r.Nbody = concat(r.Nbody, cas.Nbody)
   307  		r.Nbody = list(r.Nbody, Nod(OBREAK, nil, nil))
   308  		init = list(init, r)
   309  	}
   310  
   311  	// run the select
   312  	setlineno(sel)
   313  
   314  	init = list(init, mkcall("selectgo", nil, nil, var_))
   315  	sel.Nbody = init
   316  
   317  out:
   318  	sel.List = nil
   319  	walkstmtlist(sel.Nbody)
   320  	lineno = int32(lno)
   321  }
   322  
   323  // Keep in sync with src/runtime/runtime2.go and src/runtime/select.go.
   324  func selecttype(size int32) *Type {
   325  	// TODO(dvyukov): it's possible to generate SudoG and Scase only once
   326  	// and then cache; and also cache Select per size.
   327  	sudog := Nod(OTSTRUCT, nil, nil)
   328  
   329  	sudog.List = list(sudog.List, Nod(ODCLFIELD, newname(Lookup("g")), typenod(Ptrto(Types[TUINT8]))))
   330  	sudog.List = list(sudog.List, Nod(ODCLFIELD, newname(Lookup("selectdone")), typenod(Ptrto(Types[TUINT8]))))
   331  	sudog.List = list(sudog.List, Nod(ODCLFIELD, newname(Lookup("next")), typenod(Ptrto(Types[TUINT8]))))
   332  	sudog.List = list(sudog.List, Nod(ODCLFIELD, newname(Lookup("prev")), typenod(Ptrto(Types[TUINT8]))))
   333  	sudog.List = list(sudog.List, Nod(ODCLFIELD, newname(Lookup("elem")), typenod(Ptrto(Types[TUINT8]))))
   334  	sudog.List = list(sudog.List, Nod(ODCLFIELD, newname(Lookup("releasetime")), typenod(Types[TUINT64])))
   335  	sudog.List = list(sudog.List, Nod(ODCLFIELD, newname(Lookup("nrelease")), typenod(Types[TINT32])))
   336  	sudog.List = list(sudog.List, Nod(ODCLFIELD, newname(Lookup("waitlink")), typenod(Ptrto(Types[TUINT8]))))
   337  	typecheck(&sudog, Etype)
   338  	sudog.Type.Noalg = true
   339  	sudog.Type.Local = true
   340  
   341  	scase := Nod(OTSTRUCT, nil, nil)
   342  	scase.List = list(scase.List, Nod(ODCLFIELD, newname(Lookup("elem")), typenod(Ptrto(Types[TUINT8]))))
   343  	scase.List = list(scase.List, Nod(ODCLFIELD, newname(Lookup("chan")), typenod(Ptrto(Types[TUINT8]))))
   344  	scase.List = list(scase.List, Nod(ODCLFIELD, newname(Lookup("pc")), typenod(Types[TUINTPTR])))
   345  	scase.List = list(scase.List, Nod(ODCLFIELD, newname(Lookup("kind")), typenod(Types[TUINT16])))
   346  	scase.List = list(scase.List, Nod(ODCLFIELD, newname(Lookup("so")), typenod(Types[TUINT16])))
   347  	scase.List = list(scase.List, Nod(ODCLFIELD, newname(Lookup("receivedp")), typenod(Ptrto(Types[TUINT8]))))
   348  	scase.List = list(scase.List, Nod(ODCLFIELD, newname(Lookup("releasetime")), typenod(Types[TUINT64])))
   349  	typecheck(&scase, Etype)
   350  	scase.Type.Noalg = true
   351  	scase.Type.Local = true
   352  
   353  	sel := Nod(OTSTRUCT, nil, nil)
   354  	sel.List = list(sel.List, Nod(ODCLFIELD, newname(Lookup("tcase")), typenod(Types[TUINT16])))
   355  	sel.List = list(sel.List, Nod(ODCLFIELD, newname(Lookup("ncase")), typenod(Types[TUINT16])))
   356  	sel.List = list(sel.List, Nod(ODCLFIELD, newname(Lookup("pollorder")), typenod(Ptrto(Types[TUINT8]))))
   357  	sel.List = list(sel.List, Nod(ODCLFIELD, newname(Lookup("lockorder")), typenod(Ptrto(Types[TUINT8]))))
   358  	arr := Nod(OTARRAY, Nodintconst(int64(size)), scase)
   359  	sel.List = list(sel.List, Nod(ODCLFIELD, newname(Lookup("scase")), arr))
   360  	arr = Nod(OTARRAY, Nodintconst(int64(size)), typenod(Ptrto(Types[TUINT8])))
   361  	sel.List = list(sel.List, Nod(ODCLFIELD, newname(Lookup("lockorderarr")), arr))
   362  	arr = Nod(OTARRAY, Nodintconst(int64(size)), typenod(Types[TUINT16]))
   363  	sel.List = list(sel.List, Nod(ODCLFIELD, newname(Lookup("pollorderarr")), arr))
   364  	typecheck(&sel, Etype)
   365  	sel.Type.Noalg = true
   366  	sel.Type.Local = true
   367  
   368  	return sel.Type
   369  }