Home | History | Annotate | Download | only in gc
      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.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 		Fatal("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 				Fatal("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 			Fatal("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 				Fatal("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 = 1
    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 = 1
    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 = 1
    366 	sel.Type.Local = true
    367 
    368 	return sel.Type
    369 }
    370