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 // 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.Prepend(n.Left)
    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 }
    355