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 import "cmd/compile/internal/types"
      8 
      9 // select
     10 func typecheckselect(sel *Node) {
     11 	var def *Node
     12 	lno := setlineno(sel)
     13 	typecheckslice(sel.Ninit.Slice(), Etop)
     14 	for _, ncase := range sel.List.Slice() {
     15 		if ncase.Op != OXCASE {
     16 			setlineno(ncase)
     17 			Fatalf("typecheckselect %v", ncase.Op)
     18 		}
     19 
     20 		if ncase.List.Len() == 0 {
     21 			// default
     22 			if def != nil {
     23 				yyerrorl(ncase.Pos, "multiple defaults in select (first at %v)", def.Line())
     24 			} else {
     25 				def = ncase
     26 			}
     27 		} else if ncase.List.Len() > 1 {
     28 			yyerrorl(ncase.Pos, "select cases cannot be lists")
     29 		} else {
     30 			ncase.List.SetFirst(typecheck(ncase.List.First(), Etop))
     31 			n := ncase.List.First()
     32 			ncase.Left = n
     33 			ncase.List.Set(nil)
     34 			switch n.Op {
     35 			default:
     36 				yyerrorl(n.Pos, "select case must be receive, send or assign recv")
     37 
     38 			// convert x = <-c into OSELRECV(x, <-c).
     39 			// remove implicit conversions; the eventual assignment
     40 			// will reintroduce them.
     41 			case OAS:
     42 				if (n.Right.Op == OCONVNOP || n.Right.Op == OCONVIFACE) && n.Right.Implicit() {
     43 					n.Right = n.Right.Left
     44 				}
     45 
     46 				if n.Right.Op != ORECV {
     47 					yyerrorl(n.Pos, "select assignment must have receive on right hand side")
     48 					break
     49 				}
     50 
     51 				n.Op = OSELRECV
     52 
     53 				// convert x, ok = <-c into OSELRECV2(x, <-c) with ntest=ok
     54 			case OAS2RECV:
     55 				if n.Rlist.First().Op != ORECV {
     56 					yyerrorl(n.Pos, "select assignment must have receive on right hand side")
     57 					break
     58 				}
     59 
     60 				n.Op = OSELRECV2
     61 				n.Left = n.List.First()
     62 				n.List.Set1(n.List.Second())
     63 				n.Right = n.Rlist.First()
     64 				n.Rlist.Set(nil)
     65 
     66 				// convert <-c into OSELRECV(N, <-c)
     67 			case ORECV:
     68 				n = nodl(n.Pos, OSELRECV, nil, n)
     69 
     70 				n.SetTypecheck(1)
     71 				ncase.Left = n
     72 
     73 			case OSEND:
     74 				break
     75 			}
     76 		}
     77 
     78 		typecheckslice(ncase.Nbody.Slice(), Etop)
     79 	}
     80 
     81 	lineno = lno
     82 }
     83 
     84 func walkselect(sel *Node) {
     85 	lno := setlineno(sel)
     86 	if sel.Nbody.Len() != 0 {
     87 		Fatalf("double walkselect")
     88 	}
     89 
     90 	init := sel.Ninit.Slice()
     91 	sel.Ninit.Set(nil)
     92 
     93 	init = append(init, walkselectcases(&sel.List)...)
     94 	sel.List.Set(nil)
     95 
     96 	sel.Nbody.Set(init)
     97 	walkstmtlist(sel.Nbody.Slice())
     98 
     99 	lineno = lno
    100 }
    101 
    102 func walkselectcases(cases *Nodes) []*Node {
    103 	n := cases.Len()
    104 	sellineno := lineno
    105 
    106 	// optimization: zero-case select
    107 	if n == 0 {
    108 		return []*Node{mkcall("block", nil, nil)}
    109 	}
    110 
    111 	// optimization: one-case select: single op.
    112 	// TODO(rsc): Reenable optimization once order.go can handle it.
    113 	// golang.org/issue/7672.
    114 	if n == 1 {
    115 		cas := cases.First()
    116 		setlineno(cas)
    117 		l := cas.Ninit.Slice()
    118 		if cas.Left != nil { // not default:
    119 			n := cas.Left
    120 			l = append(l, n.Ninit.Slice()...)
    121 			n.Ninit.Set(nil)
    122 			var ch *Node
    123 			switch n.Op {
    124 			default:
    125 				Fatalf("select %v", n.Op)
    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.Len() == 0 {
    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 					nblank = typecheck(nblank, Erv|Easgn)
    144 					n.Left = nblank
    145 				}
    146 
    147 				n.Op = OAS2
    148 				n.List.Prepend(n.Left)
    149 				n.Rlist.Set1(n.Right)
    150 				n.Right = nil
    151 				n.Left = nil
    152 				n.SetTypecheck(0)
    153 				n = 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 			var ln Nodes
    161 			ln.Set(l)
    162 			a.Nbody.Set1(mkcall("block", nil, &ln))
    163 			l = ln.Slice()
    164 			a = typecheck(a, Etop)
    165 			l = append(l, a, n)
    166 		}
    167 
    168 		l = append(l, cas.Nbody.Slice()...)
    169 		l = append(l, nod(OBREAK, nil, nil))
    170 		return l
    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 _, cas := range cases.Slice() {
    176 		setlineno(cas)
    177 		n := cas.Left
    178 		if n == nil {
    179 			continue
    180 		}
    181 		switch n.Op {
    182 		case OSEND:
    183 			n.Right = nod(OADDR, n.Right, nil)
    184 			n.Right = typecheck(n.Right, Erv)
    185 
    186 		case OSELRECV, OSELRECV2:
    187 			if n.Op == OSELRECV2 && n.List.Len() == 0 {
    188 				n.Op = OSELRECV
    189 			}
    190 			if n.Op == OSELRECV2 {
    191 				n.List.SetFirst(nod(OADDR, n.List.First(), nil))
    192 				n.List.SetFirst(typecheck(n.List.First(), Erv))
    193 			}
    194 
    195 			if n.Left == nil {
    196 				n.Left = nodnil()
    197 			} else {
    198 				n.Left = nod(OADDR, n.Left, nil)
    199 				n.Left = typecheck(n.Left, Erv)
    200 			}
    201 		}
    202 	}
    203 
    204 	// optimization: two-case select but one is default: single non-blocking op.
    205 	if n == 2 && (cases.First().Left == nil || cases.Second().Left == nil) {
    206 		var cas *Node
    207 		var dflt *Node
    208 		if cases.First().Left == nil {
    209 			cas = cases.Second()
    210 			dflt = cases.First()
    211 		} else {
    212 			dflt = cases.Second()
    213 			cas = cases.First()
    214 		}
    215 
    216 		n := cas.Left
    217 		setlineno(n)
    218 		r := nod(OIF, nil, nil)
    219 		r.Ninit.Set(cas.Ninit.Slice())
    220 		switch n.Op {
    221 		default:
    222 			Fatalf("select %v", n.Op)
    223 
    224 		case OSEND:
    225 			// if selectnbsend(c, v) { body } else { default body }
    226 			ch := n.Left
    227 			r.Left = mkcall1(chanfn("selectnbsend", 2, ch.Type), types.Types[TBOOL], &r.Ninit, ch, n.Right)
    228 
    229 		case OSELRECV:
    230 			// if c != nil && selectnbrecv(&v, c) { body } else { default body }
    231 			r = nod(OIF, nil, nil)
    232 			r.Ninit.Set(cas.Ninit.Slice())
    233 			ch := n.Right.Left
    234 			r.Left = mkcall1(chanfn("selectnbrecv", 2, ch.Type), types.Types[TBOOL], &r.Ninit, n.Left, ch)
    235 
    236 		case OSELRECV2:
    237 			// if c != nil && selectnbrecv2(&v, c) { body } else { default body }
    238 			r = nod(OIF, nil, nil)
    239 			r.Ninit.Set(cas.Ninit.Slice())
    240 			ch := n.Right.Left
    241 			r.Left = mkcall1(chanfn("selectnbrecv2", 2, ch.Type), types.Types[TBOOL], &r.Ninit, n.Left, n.List.First(), ch)
    242 		}
    243 
    244 		r.Left = typecheck(r.Left, Erv)
    245 		r.Nbody.Set(cas.Nbody.Slice())
    246 		r.Rlist.Set(append(dflt.Ninit.Slice(), dflt.Nbody.Slice()...))
    247 		return []*Node{r, nod(OBREAK, nil, nil)}
    248 	}
    249 
    250 	var init []*Node
    251 
    252 	// generate sel-struct
    253 	lineno = sellineno
    254 	selv := temp(selecttype(int64(n)))
    255 	r := nod(OAS, selv, nil)
    256 	r = typecheck(r, Etop)
    257 	init = append(init, r)
    258 	var_ := conv(conv(nod(OADDR, selv, nil), types.Types[TUNSAFEPTR]), types.NewPtr(types.Types[TUINT8]))
    259 	r = mkcall("newselect", nil, nil, var_, nodintconst(selv.Type.Width), nodintconst(int64(n)))
    260 	r = typecheck(r, Etop)
    261 	init = append(init, r)
    262 
    263 	// register cases
    264 	for _, cas := range cases.Slice() {
    265 		setlineno(cas)
    266 
    267 		init = append(init, cas.Ninit.Slice()...)
    268 		cas.Ninit.Set(nil)
    269 
    270 		var x *Node
    271 		if n := cas.Left; n != nil {
    272 			init = append(init, n.Ninit.Slice()...)
    273 
    274 			switch n.Op {
    275 			default:
    276 				Fatalf("select %v", n.Op)
    277 			case OSEND:
    278 				// selectsend(sel *byte, hchan *chan any, elem *any)
    279 				x = mkcall1(chanfn("selectsend", 2, n.Left.Type), nil, nil, var_, n.Left, n.Right)
    280 			case OSELRECV:
    281 				// selectrecv(sel *byte, hchan *chan any, elem *any, received *bool)
    282 				x = mkcall1(chanfn("selectrecv", 2, n.Right.Left.Type), nil, nil, var_, n.Right.Left, n.Left, nodnil())
    283 			case OSELRECV2:
    284 				// selectrecv(sel *byte, hchan *chan any, elem *any, received *bool)
    285 				x = mkcall1(chanfn("selectrecv", 2, n.Right.Left.Type), nil, nil, var_, n.Right.Left, n.Left, n.List.First())
    286 			}
    287 		} else {
    288 			// selectdefault(sel *byte)
    289 			x = mkcall("selectdefault", nil, nil, var_)
    290 		}
    291 
    292 		init = append(init, x)
    293 	}
    294 
    295 	// run the select
    296 	lineno = sellineno
    297 	chosen := temp(types.Types[TINT])
    298 	r = nod(OAS, chosen, mkcall("selectgo", types.Types[TINT], nil, var_))
    299 	r = typecheck(r, Etop)
    300 	init = append(init, r)
    301 
    302 	// selv is no longer alive after selectgo.
    303 	init = append(init, nod(OVARKILL, selv, nil))
    304 
    305 	// dispatch cases
    306 	for i, cas := range cases.Slice() {
    307 		setlineno(cas)
    308 
    309 		cond := nod(OEQ, chosen, nodintconst(int64(i)))
    310 		cond = typecheck(cond, Erv)
    311 
    312 		r = nod(OIF, cond, nil)
    313 		r.Nbody.AppendNodes(&cas.Nbody)
    314 		r.Nbody.Append(nod(OBREAK, nil, nil))
    315 		init = append(init, r)
    316 	}
    317 
    318 	return init
    319 }
    320 
    321 // Keep in sync with src/runtime/select.go.
    322 func selecttype(size int64) *types.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 := tostruct([]*Node{
    327 		namedfield("elem", types.NewPtr(types.Types[TUINT8])),
    328 		namedfield("chan", types.NewPtr(types.Types[TUINT8])),
    329 		namedfield("pc", types.Types[TUINTPTR]),
    330 		namedfield("kind", types.Types[TUINT16]),
    331 		namedfield("receivedp", types.NewPtr(types.Types[TUINT8])),
    332 		namedfield("releasetime", types.Types[TUINT64]),
    333 	})
    334 	scase.SetNoalg(true)
    335 
    336 	sel := tostruct([]*Node{
    337 		namedfield("tcase", types.Types[TUINT16]),
    338 		namedfield("ncase", types.Types[TUINT16]),
    339 		namedfield("pollorder", types.NewPtr(types.Types[TUINT8])),
    340 		namedfield("lockorder", types.NewPtr(types.Types[TUINT8])),
    341 		namedfield("scase", types.NewArray(scase, size)),
    342 		namedfield("lockorderarr", types.NewArray(types.Types[TUINT16], size)),
    343 		namedfield("pollorderarr", types.NewArray(types.Types[TUINT16], size)),
    344 	})
    345 	sel.SetNoalg(true)
    346 
    347 	return sel
    348 }
    349