blob: 5816428debc2390a41f2ef21a724dca62df82998 [file] [log] [blame]
Russ Cox8c195bd2015-02-13 14:40:36 -05001// 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
5package gc
6
7/*
8 * select
9 */
10func typecheckselect(sel *Node) {
11 var ncase *Node
12 var n *Node
Russ Cox8c195bd2015-02-13 14:40:36 -050013
Russ Cox175929b2015-03-02 14:22:05 -050014 var def *Node
Russ Cox382b44e2015-02-23 16:07:24 -050015 lno := int(setlineno(sel))
16 count := 0
Russ Cox8c195bd2015-02-13 14:40:36 -050017 typechecklist(sel.Ninit, Etop)
Russ Cox382b44e2015-02-23 16:07:24 -050018 for l := sel.List; l != nil; l = l.Next {
Russ Cox8c195bd2015-02-13 14:40:36 -050019 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:
Dave Cheney44e90312015-03-06 21:18:41 +110048 if (n.Right.Op == OCONVNOP || n.Right.Op == OCONVIFACE) && n.Right.Implicit {
Russ Cox8c195bd2015-02-13 14:40:36 -050049 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.Ntest = n.List.Next.N
69 n.List = nil
70 n.Right = n.Rlist.N
71 n.Rlist = nil
72
73 // convert <-c into OSELRECV(N, <-c)
74 case ORECV:
75 n = Nod(OSELRECV, nil, n)
76
77 n.Typecheck = 1
78 ncase.Left = n
79
80 case OSEND:
81 break
82 }
83 }
84
85 typechecklist(ncase.Nbody, Etop)
86 }
87
88 sel.Xoffset = int64(count)
89 lineno = int32(lno)
90}
91
92func walkselect(sel *Node) {
Russ Cox8c195bd2015-02-13 14:40:36 -050093 if sel.List == nil && sel.Xoffset != 0 {
94 Fatal("double walkselect") // already rewrote
95 }
96
Russ Cox382b44e2015-02-23 16:07:24 -050097 lno := int(setlineno(sel))
98 i := count(sel.List)
Russ Cox8c195bd2015-02-13 14:40:36 -050099
100 // optimization: zero-case select
Russ Cox382b44e2015-02-23 16:07:24 -0500101 var init *NodeList
102 var r *Node
103 var n *Node
104 var var_ *Node
105 var selv *Node
106 var cas *Node
Russ Cox8c195bd2015-02-13 14:40:36 -0500107 if i == 0 {
108 sel.Nbody = list1(mkcall("block", nil, nil))
109 goto out
110 }
111
112 // optimization: one-case select: single op.
113 // TODO(rsc): Reenable optimization once order.c can handle it.
114 // golang.org/issue/7672.
115 if i == 1 {
Russ Cox382b44e2015-02-23 16:07:24 -0500116 cas := sel.List.N
Russ Cox8c195bd2015-02-13 14:40:36 -0500117 setlineno(cas)
Russ Cox382b44e2015-02-23 16:07:24 -0500118 l := cas.Ninit
Russ Cox8c195bd2015-02-13 14:40:36 -0500119 if cas.Left != nil { // not default:
Russ Cox382b44e2015-02-23 16:07:24 -0500120 n := cas.Left
Russ Cox8c195bd2015-02-13 14:40:36 -0500121 l = concat(l, n.Ninit)
122 n.Ninit = nil
Russ Cox382b44e2015-02-23 16:07:24 -0500123 var ch *Node
Russ Cox8c195bd2015-02-13 14:40:36 -0500124 switch n.Op {
125 default:
126 Fatal("select %v", Oconv(int(n.Op), 0))
Russ Cox8c195bd2015-02-13 14:40:36 -0500127
128 // ok already
129 case OSEND:
130 ch = n.Left
131
132 case OSELRECV,
133 OSELRECV2:
134 ch = n.Right.Left
135 if n.Op == OSELRECV || n.Ntest == nil {
136 if n.Left == nil {
137 n = n.Right
138 } else {
139 n.Op = OAS
140 }
141 break
142 }
143
144 if n.Left == nil {
145 typecheck(&nblank, Erv|Easgn)
146 n.Left = nblank
147 }
148
149 n.Op = OAS2
150 n.List = list(list1(n.Left), n.Ntest)
151 n.Rlist = list1(n.Right)
152 n.Right = nil
153 n.Left = nil
154 n.Ntest = nil
155 n.Typecheck = 0
156 typecheck(&n, Etop)
157 }
158
159 // if ch == nil { block() }; n;
Russ Cox382b44e2015-02-23 16:07:24 -0500160 a := Nod(OIF, nil, nil)
Russ Cox8c195bd2015-02-13 14:40:36 -0500161
162 a.Ntest = Nod(OEQ, ch, nodnil())
163 a.Nbody = list1(mkcall("block", nil, &l))
164 typecheck(&a, Etop)
165 l = list(l, a)
166 l = list(l, n)
167 }
168
169 l = concat(l, cas.Nbody)
170 sel.Nbody = l
171 goto out
172 }
173
174 // convert case value arguments to addresses.
175 // this rewrite is used by both the general code and the next optimization.
Russ Cox382b44e2015-02-23 16:07:24 -0500176 for l := sel.List; l != nil; l = l.Next {
Russ Cox8c195bd2015-02-13 14:40:36 -0500177 cas = l.N
178 setlineno(cas)
179 n = cas.Left
180 if n == nil {
181 continue
182 }
183 switch n.Op {
184 case OSEND:
185 n.Right = Nod(OADDR, n.Right, nil)
186 typecheck(&n.Right, Erv)
187
188 case OSELRECV,
189 OSELRECV2:
190 if n.Op == OSELRECV2 && n.Ntest == nil {
191 n.Op = OSELRECV
192 }
193 if n.Op == OSELRECV2 {
194 n.Ntest = Nod(OADDR, n.Ntest, nil)
195 typecheck(&n.Ntest, Erv)
196 }
197
198 if n.Left == nil {
199 n.Left = nodnil()
200 } else {
201 n.Left = Nod(OADDR, n.Left, nil)
202 typecheck(&n.Left, Erv)
203 }
204 }
205 }
206
207 // optimization: two-case select but one is default: single non-blocking op.
208 if i == 2 && (sel.List.N.Left == nil || sel.List.Next.N.Left == nil) {
Russ Cox382b44e2015-02-23 16:07:24 -0500209 var cas *Node
210 var dflt *Node
Russ Cox8c195bd2015-02-13 14:40:36 -0500211 if sel.List.N.Left == nil {
212 cas = sel.List.Next.N
213 dflt = sel.List.N
214 } else {
215 dflt = sel.List.Next.N
216 cas = sel.List.N
217 }
218
Russ Cox382b44e2015-02-23 16:07:24 -0500219 n := cas.Left
Russ Cox8c195bd2015-02-13 14:40:36 -0500220 setlineno(n)
Russ Cox382b44e2015-02-23 16:07:24 -0500221 r := Nod(OIF, nil, nil)
Russ Cox8c195bd2015-02-13 14:40:36 -0500222 r.Ninit = cas.Ninit
223 switch n.Op {
224 default:
225 Fatal("select %v", Oconv(int(n.Op), 0))
Russ Cox8c195bd2015-02-13 14:40:36 -0500226
227 // if selectnbsend(c, v) { body } else { default body }
228 case OSEND:
Russ Cox382b44e2015-02-23 16:07:24 -0500229 ch := n.Left
Russ Cox8c195bd2015-02-13 14:40:36 -0500230
231 r.Ntest = mkcall1(chanfn("selectnbsend", 2, ch.Type), Types[TBOOL], &r.Ninit, typename(ch.Type), ch, n.Right)
232
233 // if c != nil && selectnbrecv(&v, c) { body } else { default body }
234 case OSELRECV:
235 r = Nod(OIF, nil, nil)
236
237 r.Ninit = cas.Ninit
Russ Cox382b44e2015-02-23 16:07:24 -0500238 ch := n.Right.Left
Russ Cox8c195bd2015-02-13 14:40:36 -0500239 r.Ntest = mkcall1(chanfn("selectnbrecv", 2, ch.Type), Types[TBOOL], &r.Ninit, typename(ch.Type), n.Left, ch)
240
241 // if c != nil && selectnbrecv2(&v, c) { body } else { default body }
242 case OSELRECV2:
243 r = Nod(OIF, nil, nil)
244
245 r.Ninit = cas.Ninit
Russ Cox382b44e2015-02-23 16:07:24 -0500246 ch := n.Right.Left
Russ Cox8c195bd2015-02-13 14:40:36 -0500247 r.Ntest = mkcall1(chanfn("selectnbrecv2", 2, ch.Type), Types[TBOOL], &r.Ninit, typename(ch.Type), n.Left, n.Ntest, ch)
248 }
249
250 typecheck(&r.Ntest, Erv)
251 r.Nbody = cas.Nbody
252 r.Nelse = concat(dflt.Ninit, dflt.Nbody)
253 sel.Nbody = list1(r)
254 goto out
255 }
256
257 init = sel.Ninit
258 sel.Ninit = nil
259
260 // generate sel-struct
261 setlineno(sel)
262
263 selv = temp(selecttype(int32(sel.Xoffset)))
264 r = Nod(OAS, selv, nil)
265 typecheck(&r, Etop)
266 init = list(init, r)
267 var_ = conv(conv(Nod(OADDR, selv, nil), Types[TUNSAFEPTR]), Ptrto(Types[TUINT8]))
268 r = mkcall("newselect", nil, nil, var_, Nodintconst(selv.Type.Width), Nodintconst(sel.Xoffset))
269 typecheck(&r, Etop)
270 init = list(init, r)
271
272 // register cases
Russ Cox382b44e2015-02-23 16:07:24 -0500273 for l := sel.List; l != nil; l = l.Next {
Russ Cox8c195bd2015-02-13 14:40:36 -0500274 cas = l.N
275 setlineno(cas)
276 n = cas.Left
277 r = Nod(OIF, nil, nil)
278 r.Ninit = cas.Ninit
279 cas.Ninit = nil
280 if n != nil {
281 r.Ninit = concat(r.Ninit, n.Ninit)
282 n.Ninit = nil
283 }
284
285 if n == nil {
286 // selectdefault(sel *byte);
287 r.Ntest = mkcall("selectdefault", Types[TBOOL], &r.Ninit, var_)
288 } else {
289 switch n.Op {
290 default:
291 Fatal("select %v", Oconv(int(n.Op), 0))
Russ Cox8c195bd2015-02-13 14:40:36 -0500292
293 // selectsend(sel *byte, hchan *chan any, elem *any) (selected bool);
294 case OSEND:
295 r.Ntest = mkcall1(chanfn("selectsend", 2, n.Left.Type), Types[TBOOL], &r.Ninit, var_, n.Left, n.Right)
296
297 // selectrecv(sel *byte, hchan *chan any, elem *any) (selected bool);
298 case OSELRECV:
299 r.Ntest = mkcall1(chanfn("selectrecv", 2, n.Right.Left.Type), Types[TBOOL], &r.Ninit, var_, n.Right.Left, n.Left)
300
301 // selectrecv2(sel *byte, hchan *chan any, elem *any, received *bool) (selected bool);
302 case OSELRECV2:
303 r.Ntest = mkcall1(chanfn("selectrecv2", 2, n.Right.Left.Type), Types[TBOOL], &r.Ninit, var_, n.Right.Left, n.Left, n.Ntest)
304 }
305 }
306
307 // selv is no longer alive after use.
308 r.Nbody = list(r.Nbody, Nod(OVARKILL, selv, nil))
309
310 r.Nbody = concat(r.Nbody, cas.Nbody)
311 r.Nbody = list(r.Nbody, Nod(OBREAK, nil, nil))
312 init = list(init, r)
313 }
314
315 // run the select
316 setlineno(sel)
317
318 init = list(init, mkcall("selectgo", nil, nil, var_))
319 sel.Nbody = init
320
321out:
322 sel.List = nil
323 walkstmtlist(sel.Nbody)
324 lineno = int32(lno)
325}
326
327// Keep in sync with src/runtime/chan.h.
328func selecttype(size int32) *Type {
Russ Cox8c195bd2015-02-13 14:40:36 -0500329 // TODO(dvyukov): it's possible to generate SudoG and Scase only once
330 // and then cache; and also cache Select per size.
Russ Cox382b44e2015-02-23 16:07:24 -0500331 sudog := Nod(OTSTRUCT, nil, nil)
Russ Cox8c195bd2015-02-13 14:40:36 -0500332
333 sudog.List = list(sudog.List, Nod(ODCLFIELD, newname(Lookup("g")), typenod(Ptrto(Types[TUINT8]))))
334 sudog.List = list(sudog.List, Nod(ODCLFIELD, newname(Lookup("selectdone")), typenod(Ptrto(Types[TUINT8]))))
335 sudog.List = list(sudog.List, Nod(ODCLFIELD, newname(Lookup("next")), typenod(Ptrto(Types[TUINT8]))))
336 sudog.List = list(sudog.List, Nod(ODCLFIELD, newname(Lookup("prev")), typenod(Ptrto(Types[TUINT8]))))
337 sudog.List = list(sudog.List, Nod(ODCLFIELD, newname(Lookup("elem")), typenod(Ptrto(Types[TUINT8]))))
338 sudog.List = list(sudog.List, Nod(ODCLFIELD, newname(Lookup("releasetime")), typenod(Types[TUINT64])))
339 sudog.List = list(sudog.List, Nod(ODCLFIELD, newname(Lookup("nrelease")), typenod(Types[TINT32])))
340 sudog.List = list(sudog.List, Nod(ODCLFIELD, newname(Lookup("waitlink")), typenod(Ptrto(Types[TUINT8]))))
341 typecheck(&sudog, Etype)
342 sudog.Type.Noalg = 1
Dave Cheneye4981812015-03-10 09:58:01 +1100343 sudog.Type.Local = true
Russ Cox8c195bd2015-02-13 14:40:36 -0500344
Russ Cox382b44e2015-02-23 16:07:24 -0500345 scase := Nod(OTSTRUCT, nil, nil)
Russ Cox8c195bd2015-02-13 14:40:36 -0500346 scase.List = list(scase.List, Nod(ODCLFIELD, newname(Lookup("elem")), typenod(Ptrto(Types[TUINT8]))))
347 scase.List = list(scase.List, Nod(ODCLFIELD, newname(Lookup("chan")), typenod(Ptrto(Types[TUINT8]))))
348 scase.List = list(scase.List, Nod(ODCLFIELD, newname(Lookup("pc")), typenod(Types[TUINTPTR])))
349 scase.List = list(scase.List, Nod(ODCLFIELD, newname(Lookup("kind")), typenod(Types[TUINT16])))
350 scase.List = list(scase.List, Nod(ODCLFIELD, newname(Lookup("so")), typenod(Types[TUINT16])))
351 scase.List = list(scase.List, Nod(ODCLFIELD, newname(Lookup("receivedp")), typenod(Ptrto(Types[TUINT8]))))
352 scase.List = list(scase.List, Nod(ODCLFIELD, newname(Lookup("releasetime")), typenod(Types[TUINT64])))
353 typecheck(&scase, Etype)
354 scase.Type.Noalg = 1
Dave Cheneye4981812015-03-10 09:58:01 +1100355 scase.Type.Local = true
Russ Cox8c195bd2015-02-13 14:40:36 -0500356
Russ Cox382b44e2015-02-23 16:07:24 -0500357 sel := Nod(OTSTRUCT, nil, nil)
Russ Cox8c195bd2015-02-13 14:40:36 -0500358 sel.List = list(sel.List, Nod(ODCLFIELD, newname(Lookup("tcase")), typenod(Types[TUINT16])))
359 sel.List = list(sel.List, Nod(ODCLFIELD, newname(Lookup("ncase")), typenod(Types[TUINT16])))
360 sel.List = list(sel.List, Nod(ODCLFIELD, newname(Lookup("pollorder")), typenod(Ptrto(Types[TUINT8]))))
361 sel.List = list(sel.List, Nod(ODCLFIELD, newname(Lookup("lockorder")), typenod(Ptrto(Types[TUINT8]))))
Russ Cox382b44e2015-02-23 16:07:24 -0500362 arr := Nod(OTARRAY, Nodintconst(int64(size)), scase)
Russ Cox8c195bd2015-02-13 14:40:36 -0500363 sel.List = list(sel.List, Nod(ODCLFIELD, newname(Lookup("scase")), arr))
364 arr = Nod(OTARRAY, Nodintconst(int64(size)), typenod(Ptrto(Types[TUINT8])))
365 sel.List = list(sel.List, Nod(ODCLFIELD, newname(Lookup("lockorderarr")), arr))
366 arr = Nod(OTARRAY, Nodintconst(int64(size)), typenod(Types[TUINT16]))
367 sel.List = list(sel.List, Nod(ODCLFIELD, newname(Lookup("pollorderarr")), arr))
368 typecheck(&sel, Etype)
369 sel.Type.Noalg = 1
Dave Cheneye4981812015-03-10 09:58:01 +1100370 sel.Type.Local = true
Russ Cox8c195bd2015-02-13 14:40:36 -0500371
372 return sel.Type
373}