Source file src/pkg/cmd/compile/internal/gc/select.go
1
2
3
4
5 package gc
6
7 import "cmd/compile/internal/types"
8
9
10 func typecheckselect(sel *Node) {
11 var def *Node
12 lno := setlineno(sel)
13 typecheckslice(sel.Ninit.Slice(), ctxStmt)
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
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(), ctxStmt))
31 n := ncase.List.First()
32 ncase.Left = n
33 ncase.List.Set(nil)
34 switch n.Op {
35 default:
36 pos := n.Pos
37 if n.Op == ONAME {
38
39
40
41
42 pos = ncase.Pos
43 }
44 yyerrorl(pos, "select case must be receive, send or assign recv")
45
46
47
48
49 case OAS:
50 if (n.Right.Op == OCONVNOP || n.Right.Op == OCONVIFACE) && n.Right.Implicit() {
51 n.Right = n.Right.Left
52 }
53
54 if n.Right.Op != ORECV {
55 yyerrorl(n.Pos, "select assignment must have receive on right hand side")
56 break
57 }
58
59 n.Op = OSELRECV
60
61
62 case OAS2RECV:
63 if n.Rlist.First().Op != ORECV {
64 yyerrorl(n.Pos, "select assignment must have receive on right hand side")
65 break
66 }
67
68 n.Op = OSELRECV2
69 n.Left = n.List.First()
70 n.List.Set1(n.List.Second())
71 n.Right = n.Rlist.First()
72 n.Rlist.Set(nil)
73
74
75 case ORECV:
76 n = nodl(n.Pos, OSELRECV, nil, n)
77
78 n.SetTypecheck(1)
79 ncase.Left = n
80
81 case OSEND:
82 break
83 }
84 }
85
86 typecheckslice(ncase.Nbody.Slice(), ctxStmt)
87 }
88
89 lineno = lno
90 }
91
92 func walkselect(sel *Node) {
93 lno := setlineno(sel)
94 if sel.Nbody.Len() != 0 {
95 Fatalf("double walkselect")
96 }
97
98 init := sel.Ninit.Slice()
99 sel.Ninit.Set(nil)
100
101 init = append(init, walkselectcases(&sel.List)...)
102 sel.List.Set(nil)
103
104 sel.Nbody.Set(init)
105 walkstmtlist(sel.Nbody.Slice())
106
107 lineno = lno
108 }
109
110 func walkselectcases(cases *Nodes) []*Node {
111 n := cases.Len()
112 sellineno := lineno
113
114
115 if n == 0 {
116 return []*Node{mkcall("block", nil, nil)}
117 }
118
119
120
121
122 if n == 1 {
123 cas := cases.First()
124 setlineno(cas)
125 l := cas.Ninit.Slice()
126 if cas.Left != nil {
127 n := cas.Left
128 l = append(l, n.Ninit.Slice()...)
129 n.Ninit.Set(nil)
130 var ch *Node
131 switch n.Op {
132 default:
133 Fatalf("select %v", n.Op)
134
135
136 case OSEND:
137 ch = n.Left
138
139 case OSELRECV, OSELRECV2:
140 ch = n.Right.Left
141 if n.Op == OSELRECV || n.List.Len() == 0 {
142 if n.Left == nil {
143 n = n.Right
144 } else {
145 n.Op = OAS
146 }
147 break
148 }
149
150 if n.Left == nil {
151 nblank = typecheck(nblank, ctxExpr|ctxAssign)
152 n.Left = nblank
153 }
154
155 n.Op = OAS2
156 n.List.Prepend(n.Left)
157 n.Rlist.Set1(n.Right)
158 n.Right = nil
159 n.Left = nil
160 n.SetTypecheck(0)
161 n = typecheck(n, ctxStmt)
162 }
163
164
165 a := nod(OIF, nil, nil)
166
167 a.Left = nod(OEQ, ch, nodnil())
168 var ln Nodes
169 ln.Set(l)
170 a.Nbody.Set1(mkcall("block", nil, &ln))
171 l = ln.Slice()
172 a = typecheck(a, ctxStmt)
173 l = append(l, a, n)
174 }
175
176 l = append(l, cas.Nbody.Slice()...)
177 l = append(l, nod(OBREAK, nil, nil))
178 return l
179 }
180
181
182
183 for _, cas := range cases.Slice() {
184 setlineno(cas)
185 n := cas.Left
186 if n == nil {
187 continue
188 }
189 switch n.Op {
190 case OSEND:
191 n.Right = nod(OADDR, n.Right, nil)
192 n.Right = typecheck(n.Right, ctxExpr)
193
194 case OSELRECV, OSELRECV2:
195 if n.Op == OSELRECV2 && n.List.Len() == 0 {
196 n.Op = OSELRECV
197 }
198
199 if n.Left != nil {
200 n.Left = nod(OADDR, n.Left, nil)
201 n.Left = typecheck(n.Left, ctxExpr)
202 }
203 }
204 }
205
206
207 if n == 2 && (cases.First().Left == nil || cases.Second().Left == nil) {
208 var cas *Node
209 var dflt *Node
210 if cases.First().Left == nil {
211 cas = cases.Second()
212 dflt = cases.First()
213 } else {
214 dflt = cases.Second()
215 cas = cases.First()
216 }
217
218 n := cas.Left
219 setlineno(n)
220 r := nod(OIF, nil, nil)
221 r.Ninit.Set(cas.Ninit.Slice())
222 switch n.Op {
223 default:
224 Fatalf("select %v", n.Op)
225
226 case OSEND:
227
228 ch := n.Left
229 r.Left = mkcall1(chanfn("selectnbsend", 2, ch.Type), types.Types[TBOOL], &r.Ninit, ch, n.Right)
230
231 case OSELRECV:
232
233 r = nod(OIF, nil, nil)
234 r.Ninit.Set(cas.Ninit.Slice())
235 ch := n.Right.Left
236 elem := n.Left
237 if elem == nil {
238 elem = nodnil()
239 }
240 r.Left = mkcall1(chanfn("selectnbrecv", 2, ch.Type), types.Types[TBOOL], &r.Ninit, elem, ch)
241
242 case OSELRECV2:
243
244 r = nod(OIF, nil, nil)
245 r.Ninit.Set(cas.Ninit.Slice())
246 ch := n.Right.Left
247 elem := n.Left
248 if elem == nil {
249 elem = nodnil()
250 }
251 receivedp := nod(OADDR, n.List.First(), nil)
252 receivedp = typecheck(receivedp, ctxExpr)
253 r.Left = mkcall1(chanfn("selectnbrecv2", 2, ch.Type), types.Types[TBOOL], &r.Ninit, elem, receivedp, ch)
254 }
255
256 r.Left = typecheck(r.Left, ctxExpr)
257 r.Nbody.Set(cas.Nbody.Slice())
258 r.Rlist.Set(append(dflt.Ninit.Slice(), dflt.Nbody.Slice()...))
259 return []*Node{r, nod(OBREAK, nil, nil)}
260 }
261
262 var init []*Node
263
264
265 lineno = sellineno
266 selv := temp(types.NewArray(scasetype(), int64(n)))
267 r := nod(OAS, selv, nil)
268 r = typecheck(r, ctxStmt)
269 init = append(init, r)
270
271 order := temp(types.NewArray(types.Types[TUINT16], 2*int64(n)))
272 r = nod(OAS, order, nil)
273 r = typecheck(r, ctxStmt)
274 init = append(init, r)
275
276
277 for i, cas := range cases.Slice() {
278 setlineno(cas)
279
280 init = append(init, cas.Ninit.Slice()...)
281 cas.Ninit.Set(nil)
282
283
284 const (
285 caseNil = iota
286 caseRecv
287 caseSend
288 caseDefault
289 )
290
291 var c, elem *Node
292 var kind int64 = caseDefault
293
294 if n := cas.Left; n != nil {
295 init = append(init, n.Ninit.Slice()...)
296
297 switch n.Op {
298 default:
299 Fatalf("select %v", n.Op)
300 case OSEND:
301 kind = caseSend
302 c = n.Left
303 elem = n.Right
304 case OSELRECV, OSELRECV2:
305 kind = caseRecv
306 c = n.Right.Left
307 elem = n.Left
308 }
309 }
310
311 setField := func(f string, val *Node) {
312 r := nod(OAS, nodSym(ODOT, nod(OINDEX, selv, nodintconst(int64(i))), lookup(f)), val)
313 r = typecheck(r, ctxStmt)
314 init = append(init, r)
315 }
316
317 setField("kind", nodintconst(kind))
318 if c != nil {
319 c = convnop(c, types.Types[TUNSAFEPTR])
320 setField("c", c)
321 }
322 if elem != nil {
323 elem = convnop(elem, types.Types[TUNSAFEPTR])
324 setField("elem", elem)
325 }
326
327
328
329 if instrumenting {
330 r = mkcall("selectsetpc", nil, nil, bytePtrToIndex(selv, int64(i)))
331 init = append(init, r)
332 }
333 }
334
335
336 lineno = sellineno
337 chosen := temp(types.Types[TINT])
338 recvOK := temp(types.Types[TBOOL])
339 r = nod(OAS2, nil, nil)
340 r.List.Set2(chosen, recvOK)
341 fn := syslook("selectgo")
342 r.Rlist.Set1(mkcall1(fn, fn.Type.Results(), nil, bytePtrToIndex(selv, 0), bytePtrToIndex(order, 0), nodintconst(int64(n))))
343 r = typecheck(r, ctxStmt)
344 init = append(init, r)
345
346
347 init = append(init, nod(OVARKILL, selv, nil))
348 init = append(init, nod(OVARKILL, order, nil))
349
350
351 for i, cas := range cases.Slice() {
352 setlineno(cas)
353
354 cond := nod(OEQ, chosen, nodintconst(int64(i)))
355 cond = typecheck(cond, ctxExpr)
356 cond = defaultlit(cond, nil)
357
358 r = nod(OIF, cond, nil)
359
360 if n := cas.Left; n != nil && n.Op == OSELRECV2 {
361 x := nod(OAS, n.List.First(), recvOK)
362 x = typecheck(x, ctxStmt)
363 r.Nbody.Append(x)
364 }
365
366 r.Nbody.AppendNodes(&cas.Nbody)
367 r.Nbody.Append(nod(OBREAK, nil, nil))
368 init = append(init, r)
369 }
370
371 return init
372 }
373
374
375 func bytePtrToIndex(n *Node, i int64) *Node {
376 s := nod(OADDR, nod(OINDEX, n, nodintconst(i)), nil)
377 t := types.NewPtr(types.Types[TUINT8])
378 return convnop(s, t)
379 }
380
381 var scase *types.Type
382
383
384 func scasetype() *types.Type {
385 if scase == nil {
386 scase = tostruct([]*Node{
387 namedfield("c", types.Types[TUNSAFEPTR]),
388 namedfield("elem", types.Types[TUNSAFEPTR]),
389 namedfield("kind", types.Types[TUINT16]),
390 namedfield("pc", types.Types[TUINTPTR]),
391 namedfield("releasetime", types.Types[TUINT64]),
392 })
393 scase.SetNoalg(true)
394 }
395 return scase
396 }
397
View as plain text