blob: 9e75198b7ca0793300414d0b65ec790e993ef561 [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 {
Håvard Haugen3c9fa382015-08-30 23:10:03 +020023 Fatalf("typecheckselect %v", Oconv(int(ncase.Op), 0))
Russ Cox8c195bd2015-02-13 14:40:36 -050024 }
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
Russ Cox66be1482015-05-26 21:30:20 -040068 n.List = list1(n.List.Next.N)
Russ Cox8c195bd2015-02-13 14:40:36 -050069 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
91func walkselect(sel *Node) {
Russ Cox8c195bd2015-02-13 14:40:36 -050092 if sel.List == nil && sel.Xoffset != 0 {
Håvard Haugen3c9fa382015-08-30 23:10:03 +020093 Fatalf("double walkselect") // already rewrote
Russ Cox8c195bd2015-02-13 14:40:36 -050094 }
95
Russ Cox382b44e2015-02-23 16:07:24 -050096 lno := int(setlineno(sel))
97 i := count(sel.List)
Russ Cox8c195bd2015-02-13 14:40:36 -050098
99 // optimization: zero-case select
Russ Cox382b44e2015-02-23 16:07:24 -0500100 var init *NodeList
101 var r *Node
102 var n *Node
103 var var_ *Node
104 var selv *Node
105 var cas *Node
Russ Cox8c195bd2015-02-13 14:40:36 -0500106 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 {
Russ Cox382b44e2015-02-23 16:07:24 -0500115 cas := sel.List.N
Russ Cox8c195bd2015-02-13 14:40:36 -0500116 setlineno(cas)
Russ Cox382b44e2015-02-23 16:07:24 -0500117 l := cas.Ninit
Russ Cox8c195bd2015-02-13 14:40:36 -0500118 if cas.Left != nil { // not default:
Russ Cox382b44e2015-02-23 16:07:24 -0500119 n := cas.Left
Russ Cox8c195bd2015-02-13 14:40:36 -0500120 l = concat(l, n.Ninit)
121 n.Ninit = nil
Russ Cox382b44e2015-02-23 16:07:24 -0500122 var ch *Node
Russ Cox8c195bd2015-02-13 14:40:36 -0500123 switch n.Op {
124 default:
Håvard Haugen3c9fa382015-08-30 23:10:03 +0200125 Fatalf("select %v", Oconv(int(n.Op), 0))
Russ Cox8c195bd2015-02-13 14:40:36 -0500126
127 // ok already
128 case OSEND:
129 ch = n.Left
130
Josh Bleecher Snyderb09925b2015-04-01 09:38:44 -0700131 case OSELRECV, OSELRECV2:
Russ Cox8c195bd2015-02-13 14:40:36 -0500132 ch = n.Right.Left
Russ Cox66be1482015-05-26 21:30:20 -0400133 if n.Op == OSELRECV || n.List == nil {
Russ Cox8c195bd2015-02-13 14:40:36 -0500134 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
Russ Cox66be1482015-05-26 21:30:20 -0400148 n.List = concat(list1(n.Left), n.List)
Russ Cox8c195bd2015-02-13 14:40:36 -0500149 n.Rlist = list1(n.Right)
150 n.Right = nil
151 n.Left = nil
Russ Cox8c195bd2015-02-13 14:40:36 -0500152 n.Typecheck = 0
153 typecheck(&n, Etop)
154 }
155
156 // if ch == nil { block() }; n;
Russ Cox382b44e2015-02-23 16:07:24 -0500157 a := Nod(OIF, nil, nil)
Russ Cox8c195bd2015-02-13 14:40:36 -0500158
Russ Cox66be1482015-05-26 21:30:20 -0400159 a.Left = Nod(OEQ, ch, nodnil())
Russ Cox8c195bd2015-02-13 14:40:36 -0500160 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.
Russ Cox382b44e2015-02-23 16:07:24 -0500173 for l := sel.List; l != nil; l = l.Next {
Russ Cox8c195bd2015-02-13 14:40:36 -0500174 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
Josh Bleecher Snyderb09925b2015-04-01 09:38:44 -0700185 case OSELRECV, OSELRECV2:
Russ Cox66be1482015-05-26 21:30:20 -0400186 if n.Op == OSELRECV2 && n.List == nil {
Russ Cox8c195bd2015-02-13 14:40:36 -0500187 n.Op = OSELRECV
188 }
189 if n.Op == OSELRECV2 {
Russ Cox66be1482015-05-26 21:30:20 -0400190 n.List.N = Nod(OADDR, n.List.N, nil)
191 typecheck(&n.List.N, Erv)
Russ Cox8c195bd2015-02-13 14:40:36 -0500192 }
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) {
Russ Cox382b44e2015-02-23 16:07:24 -0500205 var cas *Node
206 var dflt *Node
Russ Cox8c195bd2015-02-13 14:40:36 -0500207 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
Russ Cox382b44e2015-02-23 16:07:24 -0500215 n := cas.Left
Russ Cox8c195bd2015-02-13 14:40:36 -0500216 setlineno(n)
Russ Cox382b44e2015-02-23 16:07:24 -0500217 r := Nod(OIF, nil, nil)
Russ Cox8c195bd2015-02-13 14:40:36 -0500218 r.Ninit = cas.Ninit
219 switch n.Op {
220 default:
Håvard Haugen3c9fa382015-08-30 23:10:03 +0200221 Fatalf("select %v", Oconv(int(n.Op), 0))
Russ Cox8c195bd2015-02-13 14:40:36 -0500222
223 // if selectnbsend(c, v) { body } else { default body }
224 case OSEND:
Russ Cox382b44e2015-02-23 16:07:24 -0500225 ch := n.Left
Russ Cox8c195bd2015-02-13 14:40:36 -0500226
Russ Cox66be1482015-05-26 21:30:20 -0400227 r.Left = mkcall1(chanfn("selectnbsend", 2, ch.Type), Types[TBOOL], &r.Ninit, typename(ch.Type), ch, n.Right)
Russ Cox8c195bd2015-02-13 14:40:36 -0500228
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
Russ Cox382b44e2015-02-23 16:07:24 -0500234 ch := n.Right.Left
Russ Cox66be1482015-05-26 21:30:20 -0400235 r.Left = mkcall1(chanfn("selectnbrecv", 2, ch.Type), Types[TBOOL], &r.Ninit, typename(ch.Type), n.Left, ch)
Russ Cox8c195bd2015-02-13 14:40:36 -0500236
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
Russ Cox382b44e2015-02-23 16:07:24 -0500242 ch := n.Right.Left
Russ Cox66be1482015-05-26 21:30:20 -0400243 r.Left = mkcall1(chanfn("selectnbrecv2", 2, ch.Type), Types[TBOOL], &r.Ninit, typename(ch.Type), n.Left, n.List.N, ch)
Russ Cox8c195bd2015-02-13 14:40:36 -0500244 }
245
Russ Cox66be1482015-05-26 21:30:20 -0400246 typecheck(&r.Left, Erv)
Russ Cox8c195bd2015-02-13 14:40:36 -0500247 r.Nbody = cas.Nbody
Russ Coxffef1802015-05-22 01:16:52 -0400248 r.Rlist = concat(dflt.Ninit, dflt.Nbody)
Russ Cox8c195bd2015-02-13 14:40:36 -0500249 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
Russ Cox382b44e2015-02-23 16:07:24 -0500269 for l := sel.List; l != nil; l = l.Next {
Russ Cox8c195bd2015-02-13 14:40:36 -0500270 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);
Russ Cox66be1482015-05-26 21:30:20 -0400283 r.Left = mkcall("selectdefault", Types[TBOOL], &r.Ninit, var_)
Russ Cox8c195bd2015-02-13 14:40:36 -0500284 } else {
285 switch n.Op {
286 default:
Håvard Haugen3c9fa382015-08-30 23:10:03 +0200287 Fatalf("select %v", Oconv(int(n.Op), 0))
Russ Cox8c195bd2015-02-13 14:40:36 -0500288
289 // selectsend(sel *byte, hchan *chan any, elem *any) (selected bool);
290 case OSEND:
Russ Cox66be1482015-05-26 21:30:20 -0400291 r.Left = mkcall1(chanfn("selectsend", 2, n.Left.Type), Types[TBOOL], &r.Ninit, var_, n.Left, n.Right)
Russ Cox8c195bd2015-02-13 14:40:36 -0500292
293 // selectrecv(sel *byte, hchan *chan any, elem *any) (selected bool);
294 case OSELRECV:
Russ Cox66be1482015-05-26 21:30:20 -0400295 r.Left = mkcall1(chanfn("selectrecv", 2, n.Right.Left.Type), Types[TBOOL], &r.Ninit, var_, n.Right.Left, n.Left)
Russ Cox8c195bd2015-02-13 14:40:36 -0500296
297 // selectrecv2(sel *byte, hchan *chan any, elem *any, received *bool) (selected bool);
298 case OSELRECV2:
Russ Cox66be1482015-05-26 21:30:20 -0400299 r.Left = mkcall1(chanfn("selectrecv2", 2, n.Right.Left.Type), Types[TBOOL], &r.Ninit, var_, n.Right.Left, n.Left, n.List.N)
Russ Cox8c195bd2015-02-13 14:40:36 -0500300 }
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
317out:
318 sel.List = nil
319 walkstmtlist(sel.Nbody)
320 lineno = int32(lno)
321}
322
Keith Randallcd5b1442015-03-11 12:58:47 -0700323// Keep in sync with src/runtime/runtime2.go and src/runtime/select.go.
Russ Cox8c195bd2015-02-13 14:40:36 -0500324func selecttype(size int32) *Type {
Russ Cox8c195bd2015-02-13 14:40:36 -0500325 // TODO(dvyukov): it's possible to generate SudoG and Scase only once
326 // and then cache; and also cache Select per size.
Russ Cox382b44e2015-02-23 16:07:24 -0500327 sudog := Nod(OTSTRUCT, nil, nil)
Russ Cox8c195bd2015-02-13 14:40:36 -0500328
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
Dave Cheneye4981812015-03-10 09:58:01 +1100339 sudog.Type.Local = true
Russ Cox8c195bd2015-02-13 14:40:36 -0500340
Russ Cox382b44e2015-02-23 16:07:24 -0500341 scase := Nod(OTSTRUCT, nil, nil)
Russ Cox8c195bd2015-02-13 14:40:36 -0500342 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
Dave Cheneye4981812015-03-10 09:58:01 +1100351 scase.Type.Local = true
Russ Cox8c195bd2015-02-13 14:40:36 -0500352
Russ Cox382b44e2015-02-23 16:07:24 -0500353 sel := Nod(OTSTRUCT, nil, nil)
Russ Cox8c195bd2015-02-13 14:40:36 -0500354 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]))))
Russ Cox382b44e2015-02-23 16:07:24 -0500358 arr := Nod(OTARRAY, Nodintconst(int64(size)), scase)
Russ Cox8c195bd2015-02-13 14:40:36 -0500359 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
Dave Cheneye4981812015-03-10 09:58:01 +1100366 sel.Type.Local = true
Russ Cox8c195bd2015-02-13 14:40:36 -0500367
368 return sel.Type
369}