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 /*
6 * select
7 */
8
9 #include "go.h"
10
11 void
12 typecheckselect(Node *sel)
13 {
14 Node *ncase, *n, *def;
15 NodeList *l;
16 int lno, count;
17
18 def = nil;
19 lno = setlineno(sel);
20 count = 0;
21 typechecklist(sel->ninit, Etop);
22 for(l=sel->list; l; l=l->next) {
23 count++;
24 ncase = l->n;
25 setlineno(ncase);
26 if(ncase->op != OXCASE)
27 fatal("typecheckselect %O", ncase->op);
28
29 if(ncase->list == nil) {
30 // default
31 if(def != N)
32 yyerror("multiple defaults in select (first at %L)", def->lineno);
33 else
34 def = ncase;
35 } else if(ncase->list->next) {
36 yyerror("select cases cannot be lists");
37 } else {
38 n = typecheck(&ncase->list->n, Etop);
39 ncase->left = n;
40 ncase->list = nil;
41 setlineno(n);
42 switch(n->op) {
43 default:
44 yyerror("select case must be receive, send or assign recv");
45 break;
46
47 case OAS:
48 // convert x = <-c into OSELRECV(x, <-c).
49 // remove implicit conversions; the eventual assignment
50 // will reintroduce them.
51 if((n->right->op == OCONVNOP || n->right->op == OCONVIFACE) && n->right->implicit)
52 n->right = n->right->left;
53
54 if(n->right->op != ORECV) {
55 yyerror("select assignment must have receive on right hand side");
56 break;
57 }
58 n->op = OSELRECV;
59 break;
60
61 case OAS2RECV:
62 // convert x, ok = <-c into OSELRECV(x, <-c) with ntest=ok
63 if(n->right->op != ORECV) {
64 yyerror("select assignment must have receive on right hand side");
65 break;
66 }
67 n->op = OSELRECV2;
68 n->left = n->list->n;
69 n->ntest = n->list->next->n;
70 n->right = n->rlist->n;
71 break;
72
73 case ORECV:
74 // convert <-c into OSELRECV(N, <-c)
75 n = nod(OSELRECV, N, n);
76 ncase->left = n;
77 break;
78
79 case OSEND:
80 break;
81 }
82 }
83 typechecklist(ncase->nbody, Etop);
84 }
85 sel->xoffset = count;
86 lineno = lno;
87 }
88
89 void
90 walkselect(Node *sel)
91 {
92 int lno, i;
93 Node *n, *r, *a, *tmp, *var, *cas, *dflt, *ch;
94 NodeList *l, *init;
95
96 if(sel->list == nil && sel->xoffset != 0)
97 fatal("double walkselect"); // already rewrote
98
99 lno = setlineno(sel);
100 i = count(sel->list);
101
102 // optimization: zero-case select
103 if(i == 0) {
104 sel->nbody = list1(mkcall("block", nil, nil));
105 goto out;
106 }
107
108 // optimization: one-case select: single op.
109 if(i == 1) {
110 cas = sel->list->n;
111 setlineno(cas);
112 l = cas->ninit;
113 if(cas->left != N) { // not default:
114 n = cas->left;
115 l = concat(l, n->ninit);
116 n->ninit = nil;
117 switch(n->op) {
118 default:
119 fatal("select %O", n->op);
120
121 case OSEND:
122 ch = cheapexpr(n->left, &l);
123 n->left = ch;
124 break;
125
126 case OSELRECV:
127 r = n->right;
128 ch = cheapexpr(r->left, &l);
129 r->left = ch;
130
131 if(n->left == N)
132 n = r;
133 else {
134 n = nod(OAS, n->left, r);
135 typecheck(&n, Etop);
136 }
137 break;
138
139 case OSELRECV2:
140 r = n->right;
141 ch = cheapexpr(r->left, &l);
142 r->left = ch;
143
144 a = nod(OAS2, N, N);
145 a->list = n->list;
146 a->rlist = n->rlist;
147 n = a;
148 typecheck(&n, Etop);
149 break;
150 }
151
152 // if ch == nil { block() }; n;
153 a = nod(OIF, N, N);
154 a->ntest = nod(OEQ, ch, nodnil());
155 a->nbody = list1(mkcall("block", nil, &l));
156 typecheck(&a, Etop);
157 l = list(l, a);
158 l = list(l, n);
159 }
160 l = concat(l, cas->nbody);
161 sel->nbody = l;
162 goto out;
163 }
164
165 // introduce temporary variables for OSELRECV where needed.
166 // this rewrite is used by both the general code and the next optimization.
167 for(l=sel->list; l; l=l->next) {
168 cas = l->n;
169 setlineno(cas);
170 n = cas->left;
171 if(n == N)
172 continue;
173 switch(n->op) {
174 case OSELRECV:
175 case OSELRECV2:
176 ch = n->right->left;
177
178 // If we can use the address of the target without
179 // violating addressability or order of operations, do so.
180 // Otherwise introduce a temporary.
181 // Also introduce a temporary for := variables that escape,
182 // so that we can delay the heap allocation until the case
183 // is selected.
184 if(n->op == OSELRECV2) {
185 if(n->ntest == N || isblank(n->ntest))
186 n->ntest = nodnil();
187 else if(n->ntest->op == ONAME &&
188 (!n->colas || (n->ntest->class&PHEAP) == 0) &&
189 convertop(types[TBOOL], n->ntest->type, nil) == OCONVNOP) {
190 n->ntest = nod(OADDR, n->ntest, N);
191 n->ntest->etype = 1; // pointer does not escape
192 typecheck(&n->ntest, Erv);
193 } else {
194 tmp = nod(OXXX, N, N);
195 tempname(tmp, types[TBOOL]);
196 a = nod(OADDR, tmp, N);
197 a->etype = 1; // pointer does not escape
198 typecheck(&a, Erv);
199 r = nod(OAS, n->ntest, tmp);
200 typecheck(&r, Etop);
201 cas->nbody = concat(list1(r), cas->nbody);
202 n->ntest = a;
203 }
204 }
205
206 if(n->left == N || isblank(n->left))
207 n->left = nodnil();
208 else if(n->left->op == ONAME &&
209 (!n->colas || (n->left->class&PHEAP) == 0) &&
210 convertop(ch->type->type, n->left->type, nil) == OCONVNOP) {
211 n->left = nod(OADDR, n->left, N);
212 n->left->etype = 1; // pointer does not escape
213 typecheck(&n->left, Erv);
214 } else {
215 tmp = nod(OXXX, N, N);
216 tempname(tmp, ch->type->type);
217 a = nod(OADDR, tmp, N);
218 a->etype = 1; // pointer does not escape
219 typecheck(&a, Erv);
220 r = nod(OAS, n->left, tmp);
221 typecheck(&r, Etop);
222 cas->nbody = concat(list1(r), cas->nbody);
223 n->left = a;
224 }
225
226 cas->nbody = concat(n->ninit, cas->nbody);
227 n->ninit = nil;
228 break;
229 }
230 }
231
232 // optimization: two-case select but one is default: single non-blocking op.
233 if(i == 2 && (sel->list->n->left == nil || sel->list->next->n->left == nil)) {
234 if(sel->list->n->left == nil) {
235 cas = sel->list->next->n;
236 dflt = sel->list->n;
237 } else {
238 dflt = sel->list->next->n;
239 cas = sel->list->n;
240 }
241
242 n = cas->left;
243 setlineno(n);
244 r = nod(OIF, N, N);
245 r->ninit = cas->ninit;
246 switch(n->op) {
247 default:
248 fatal("select %O", n->op);
249
250 case OSEND:
251 // if c != nil && selectnbsend(c, v) { body } else { default body }
252 ch = cheapexpr(n->left, &r->ninit);
253 r->ntest = mkcall1(chanfn("selectnbsend", 2, ch->type),
254 types[TBOOL], &r->ninit, typename(ch->type), ch, n->right);
255 break;
256
257 case OSELRECV:
258 // if c != nil && selectnbrecv(&v, c) { body } else { default body }
259 r = nod(OIF, N, N);
260 r->ninit = cas->ninit;
261 ch = cheapexpr(n->right->left, &r->ninit);
262 r->ntest = mkcall1(chanfn("selectnbrecv", 2, ch->type),
263 types[TBOOL], &r->ninit, typename(ch->type), n->left, ch);
264 break;
265
266 case OSELRECV2:
267 // if c != nil && selectnbrecv2(&v, c) { body } else { default body }
268 r = nod(OIF, N, N);
269 r->ninit = cas->ninit;
270 ch = cheapexpr(n->right->left, &r->ninit);
271 r->ntest = mkcall1(chanfn("selectnbrecv2", 2, ch->type),
272 types[TBOOL], &r->ninit, typename(ch->type), n->left, n->ntest, ch);
273 break;
274 }
275 typecheck(&r->ntest, Erv);
276 r->nbody = cas->nbody;
277 r->nelse = concat(dflt->ninit, dflt->nbody);
278 sel->nbody = list1(r);
279 goto out;
280 }
281
282 init = sel->ninit;
283 sel->ninit = nil;
284
285 // generate sel-struct
286 setlineno(sel);
287 var = nod(OXXX, N, N);
288 tempname(var, ptrto(types[TUINT8]));
289 r = nod(OAS, var, mkcall("newselect", var->type, nil, nodintconst(sel->xoffset)));
290 typecheck(&r, Etop);
291 init = list(init, r);
292
293 // register cases
294 for(l=sel->list; l; l=l->next) {
295 cas = l->n;
296 setlineno(cas);
297 n = cas->left;
298 r = nod(OIF, N, N);
299 r->nbody = cas->ninit;
300 cas->ninit = nil;
301 if(n != nil) {
302 r->nbody = concat(r->nbody, n->ninit);
303 n->ninit = nil;
304 }
305 if(n == nil) {
306 // selectdefault(sel *byte);
307 r->ntest = mkcall("selectdefault", types[TBOOL], &init, var);
308 } else {
309 switch(n->op) {
310 default:
311 fatal("select %O", n->op);
312
313 case OSEND:
314 // selectsend(sel *byte, hchan *chan any, elem *any) (selected bool);
315 n->left = safeexpr(n->left, &r->ninit);
316 n->right = localexpr(n->right, n->left->type->type, &r->ninit);
317 n->right = nod(OADDR, n->right, N);
318 n->right->etype = 1; // pointer does not escape
319 typecheck(&n->right, Erv);
320 r->ntest = mkcall1(chanfn("selectsend", 2, n->left->type), types[TBOOL],
321 &init, var, n->left, n->right);
322 break;
323
324 case OSELRECV:
325 // selectrecv(sel *byte, hchan *chan any, elem *any) (selected bool);
326 r->ntest = mkcall1(chanfn("selectrecv", 2, n->right->left->type), types[TBOOL],
327 &init, var, n->right->left, n->left);
328 break;
329
330 case OSELRECV2:
331 // selectrecv2(sel *byte, hchan *chan any, elem *any, received *bool) (selected bool);
332 r->ntest = mkcall1(chanfn("selectrecv2", 2, n->right->left->type), types[TBOOL],
333 &init, var, n->right->left, n->left, n->ntest);
334 break;
335 }
336 }
337 r->nbody = concat(r->nbody, cas->nbody);
338 r->nbody = list(r->nbody, nod(OBREAK, N, N));
339 init = list(init, r);
340 }
341
342 // run the select
343 setlineno(sel);
344 init = list(init, mkcall("selectgo", T, nil, var));
345 sel->nbody = init;
346
347 out:
348 sel->list = nil;
349 walkstmtlist(sel->nbody);
350 lineno = lno;
351 }