blob: 5d3b71164a54b6fe2fbc7256f0690614928d633c [file] [log] [blame]
Russ Coxf7a867e2009-08-04 12:57:48 -07001// 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
Russ Cox61f84a22011-08-25 16:25:10 -04009#include <u.h>
10#include <libc.h>
Russ Coxf7a867e2009-08-04 12:57:48 -070011#include "go.h"
12
Dmitriy Vyukov40d7d5a2014-07-20 15:07:10 +040013static Type* selecttype(int32 size);
14
Russ Coxf7a867e2009-08-04 12:57:48 -070015void
16typecheckselect(Node *sel)
17{
18 Node *ncase, *n, *def;
19 NodeList *l;
20 int lno, count;
21
22 def = nil;
23 lno = setlineno(sel);
24 count = 0;
25 typechecklist(sel->ninit, Etop);
26 for(l=sel->list; l; l=l->next) {
27 count++;
28 ncase = l->n;
29 setlineno(ncase);
30 if(ncase->op != OXCASE)
31 fatal("typecheckselect %O", ncase->op);
32
33 if(ncase->list == nil) {
34 // default
35 if(def != N)
36 yyerror("multiple defaults in select (first at %L)", def->lineno);
37 else
38 def = ncase;
39 } else if(ncase->list->next) {
40 yyerror("select cases cannot be lists");
41 } else {
42 n = typecheck(&ncase->list->n, Etop);
43 ncase->left = n;
44 ncase->list = nil;
45 setlineno(n);
46 switch(n->op) {
Russ Cox54b40372009-08-05 00:42:44 -070047 default:
Russ Cox0db8d3d2010-10-20 16:38:25 -040048 yyerror("select case must be receive, send or assign recv");
Russ Cox54b40372009-08-05 00:42:44 -070049 break;
50
Russ Coxf7a867e2009-08-04 12:57:48 -070051 case OAS:
Russ Cox50387922011-01-30 16:07:57 -050052 // convert x = <-c into OSELRECV(x, <-c).
53 // remove implicit conversions; the eventual assignment
54 // will reintroduce them.
Russ Cox0db8d3d2010-10-20 16:38:25 -040055 if((n->right->op == OCONVNOP || n->right->op == OCONVIFACE) && n->right->implicit)
56 n->right = n->right->left;
Russ Cox50387922011-01-30 16:07:57 -050057
Russ Coxf7a867e2009-08-04 12:57:48 -070058 if(n->right->op != ORECV) {
59 yyerror("select assignment must have receive on right hand side");
60 break;
61 }
62 n->op = OSELRECV;
Russ Coxf7a867e2009-08-04 12:57:48 -070063 break;
64
Russ Cox8bf34e32011-03-11 14:47:26 -050065 case OAS2RECV:
Luuk van Dijk847b61b2011-08-24 19:07:08 +020066 // convert x, ok = <-c into OSELRECV2(x, <-c) with ntest=ok
Luuk van Dijke7f89fc2012-10-22 10:01:14 +020067 if(n->rlist->n->op != ORECV) {
Russ Cox8bf34e32011-03-11 14:47:26 -050068 yyerror("select assignment must have receive on right hand side");
69 break;
70 }
71 n->op = OSELRECV2;
72 n->left = n->list->n;
73 n->ntest = n->list->next->n;
Russ Cox96d90d02014-04-02 14:09:42 -040074 n->list = nil;
Russ Cox8bf34e32011-03-11 14:47:26 -050075 n->right = n->rlist->n;
Luuk van Dijke7f89fc2012-10-22 10:01:14 +020076 n->rlist = nil;
Russ Cox8bf34e32011-03-11 14:47:26 -050077 break;
78
Russ Coxf7a867e2009-08-04 12:57:48 -070079 case ORECV:
Russ Cox50387922011-01-30 16:07:57 -050080 // convert <-c into OSELRECV(N, <-c)
81 n = nod(OSELRECV, N, n);
Luuk van Dijk847b61b2011-08-24 19:07:08 +020082 n->typecheck = 1;
Russ Cox50387922011-01-30 16:07:57 -050083 ncase->left = n;
Russ Coxf7a867e2009-08-04 12:57:48 -070084 break;
85
86 case OSEND:
87 break;
88 }
89 }
90 typechecklist(ncase->nbody, Etop);
91 }
92 sel->xoffset = count;
Russ Coxf7a867e2009-08-04 12:57:48 -070093 lineno = lno;
94}
95
96void
97walkselect(Node *sel)
98{
Russ Cox50387922011-01-30 16:07:57 -050099 int lno, i;
Dmitriy Vyukov40d7d5a2014-07-20 15:07:10 +0400100 Node *n, *r, *a, *var, *selv, *cas, *dflt, *ch;
Russ Coxf7a867e2009-08-04 12:57:48 -0700101 NodeList *l, *init;
Russ Cox50387922011-01-30 16:07:57 -0500102
103 if(sel->list == nil && sel->xoffset != 0)
104 fatal("double walkselect"); // already rewrote
105
Russ Coxf7a867e2009-08-04 12:57:48 -0700106 lno = setlineno(sel);
Russ Cox50387922011-01-30 16:07:57 -0500107 i = count(sel->list);
108
109 // optimization: zero-case select
110 if(i == 0) {
111 sel->nbody = list1(mkcall("block", nil, nil));
112 goto out;
113 }
114
115 // optimization: one-case select: single op.
Russ Coxb700cb42014-04-01 13:31:38 -0400116 // TODO(rsc): Reenable optimization once order.c can handle it.
117 // golang.org/issue/7672.
Russ Cox96d90d02014-04-02 14:09:42 -0400118 if(i == 1) {
Russ Cox50387922011-01-30 16:07:57 -0500119 cas = sel->list->n;
Russ Coxcce10da2011-07-26 00:52:17 -0400120 setlineno(cas);
Russ Cox50387922011-01-30 16:07:57 -0500121 l = cas->ninit;
122 if(cas->left != N) { // not default:
123 n = cas->left;
124 l = concat(l, n->ninit);
125 n->ninit = nil;
126 switch(n->op) {
127 default:
128 fatal("select %O", n->op);
129
130 case OSEND:
Russ Cox96d90d02014-04-02 14:09:42 -0400131 // ok already
132 ch = n->left;
Russ Cox50387922011-01-30 16:07:57 -0500133 break;
134
135 case OSELRECV:
Russ Cox96d90d02014-04-02 14:09:42 -0400136 ch = n->right->left;
137 Selrecv1:
Russ Cox50387922011-01-30 16:07:57 -0500138 if(n->left == N)
Russ Cox96d90d02014-04-02 14:09:42 -0400139 n = n->right;
140 else
141 n->op = OAS;
Russ Cox50387922011-01-30 16:07:57 -0500142 break;
Russ Cox8bf34e32011-03-11 14:47:26 -0500143
144 case OSELRECV2:
Russ Cox96d90d02014-04-02 14:09:42 -0400145 ch = n->right->left;
146 if(n->ntest == N)
147 goto Selrecv1;
148 if(n->left == N) {
149 typecheck(&nblank, Erv | Easgn);
150 n->left = nblank;
151 }
152 n->op = OAS2;
153 n->list = list(list1(n->left), n->ntest);
154 n->rlist = list1(n->right);
155 n->right = N;
156 n->left = N;
157 n->ntest = N;
158 n->typecheck = 0;
Russ Cox8bf34e32011-03-11 14:47:26 -0500159 typecheck(&n, Etop);
160 break;
Russ Cox50387922011-01-30 16:07:57 -0500161 }
162
163 // if ch == nil { block() }; n;
164 a = nod(OIF, N, N);
165 a->ntest = nod(OEQ, ch, nodnil());
166 a->nbody = list1(mkcall("block", nil, &l));
167 typecheck(&a, Etop);
168 l = list(l, a);
169 l = list(l, n);
170 }
171 l = concat(l, cas->nbody);
172 sel->nbody = l;
173 goto out;
174 }
175
Russ Cox96d90d02014-04-02 14:09:42 -0400176 // convert case value arguments to addresses.
Russ Cox50387922011-01-30 16:07:57 -0500177 // this rewrite is used by both the general code and the next optimization.
178 for(l=sel->list; l; l=l->next) {
179 cas = l->n;
Russ Coxcce10da2011-07-26 00:52:17 -0400180 setlineno(cas);
Russ Cox50387922011-01-30 16:07:57 -0500181 n = cas->left;
182 if(n == N)
183 continue;
184 switch(n->op) {
Russ Cox96d90d02014-04-02 14:09:42 -0400185 case OSEND:
186 n->right = nod(OADDR, n->right, N);
187 typecheck(&n->right, Erv);
188 break;
Russ Cox50387922011-01-30 16:07:57 -0500189 case OSELRECV:
Russ Cox8bf34e32011-03-11 14:47:26 -0500190 case OSELRECV2:
Russ Cox96d90d02014-04-02 14:09:42 -0400191 if(n->op == OSELRECV2 && n->ntest == N)
192 n->op = OSELRECV;
Russ Cox8bf34e32011-03-11 14:47:26 -0500193 if(n->op == OSELRECV2) {
Russ Cox96d90d02014-04-02 14:09:42 -0400194 n->ntest = nod(OADDR, n->ntest, N);
195 typecheck(&n->ntest, Erv);
Russ Cox8bf34e32011-03-11 14:47:26 -0500196 }
Russ Cox96d90d02014-04-02 14:09:42 -0400197 if(n->left == N)
Russ Cox50387922011-01-30 16:07:57 -0500198 n->left = nodnil();
Russ Cox96d90d02014-04-02 14:09:42 -0400199 else {
Russ Cox50387922011-01-30 16:07:57 -0500200 n->left = nod(OADDR, n->left, N);
Russ Cox50387922011-01-30 16:07:57 -0500201 typecheck(&n->left, Erv);
Russ Cox96d90d02014-04-02 14:09:42 -0400202 }
Russ Cox8bf34e32011-03-11 14:47:26 -0500203 break;
Russ Cox50387922011-01-30 16:07:57 -0500204 }
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)) {
209 if(sel->list->n->left == nil) {
210 cas = sel->list->next->n;
211 dflt = sel->list->n;
212 } else {
213 dflt = sel->list->next->n;
214 cas = sel->list->n;
215 }
216
217 n = cas->left;
Russ Coxcce10da2011-07-26 00:52:17 -0400218 setlineno(n);
Russ Cox50387922011-01-30 16:07:57 -0500219 r = nod(OIF, N, N);
220 r->ninit = cas->ninit;
221 switch(n->op) {
222 default:
223 fatal("select %O", n->op);
224
225 case OSEND:
Russ Cox96d90d02014-04-02 14:09:42 -0400226 // if selectnbsend(c, v) { body } else { default body }
227 ch = n->left;
Russ Cox3770b0e2011-08-17 15:54:17 -0400228 r->ntest = mkcall1(chanfn("selectnbsend", 2, ch->type),
Russ Cox96d90d02014-04-02 14:09:42 -0400229 types[TBOOL], &r->ninit, typename(ch->type), ch, n->right);
Russ Cox50387922011-01-30 16:07:57 -0500230 break;
231
232 case OSELRECV:
233 // if c != nil && selectnbrecv(&v, c) { body } else { default body }
234 r = nod(OIF, N, N);
235 r->ninit = cas->ninit;
Russ Cox96d90d02014-04-02 14:09:42 -0400236 ch = n->right->left;
Russ Cox3770b0e2011-08-17 15:54:17 -0400237 r->ntest = mkcall1(chanfn("selectnbrecv", 2, ch->type),
238 types[TBOOL], &r->ninit, typename(ch->type), n->left, ch);
Russ Cox50387922011-01-30 16:07:57 -0500239 break;
Russ Cox8bf34e32011-03-11 14:47:26 -0500240
241 case OSELRECV2:
242 // if c != nil && selectnbrecv2(&v, c) { body } else { default body }
243 r = nod(OIF, N, N);
244 r->ninit = cas->ninit;
Russ Cox96d90d02014-04-02 14:09:42 -0400245 ch = n->right->left;
Russ Cox3770b0e2011-08-17 15:54:17 -0400246 r->ntest = mkcall1(chanfn("selectnbrecv2", 2, ch->type),
247 types[TBOOL], &r->ninit, typename(ch->type), n->left, n->ntest, ch);
Russ Cox8bf34e32011-03-11 14:47:26 -0500248 break;
Russ Cox50387922011-01-30 16:07:57 -0500249 }
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
Russ Coxf7a867e2009-08-04 12:57:48 -0700257 init = sel->ninit;
258 sel->ninit = nil;
259
260 // generate sel-struct
Russ Coxcce10da2011-07-26 00:52:17 -0400261 setlineno(sel);
Dmitriy Vyukov40d7d5a2014-07-20 15:07:10 +0400262 selv = temp(selecttype(sel->xoffset));
Dmitriy Vyukov40d7d5a2014-07-20 15:07:10 +0400263 r = nod(OAS, selv, N);
264 typecheck(&r, Etop);
265 init = list(init, r);
266 var = conv(conv(nod(OADDR, selv, N), types[TUNSAFEPTR]), ptrto(types[TUINT8]));
267 r = mkcall("newselect", T, nil, var, nodintconst(selv->type->width), nodintconst(sel->xoffset));
Russ Coxf7a867e2009-08-04 12:57:48 -0700268 typecheck(&r, Etop);
269 init = list(init, r);
270
Russ Coxf7a867e2009-08-04 12:57:48 -0700271 // register cases
272 for(l=sel->list; l; l=l->next) {
Russ Cox50387922011-01-30 16:07:57 -0500273 cas = l->n;
Russ Coxcce10da2011-07-26 00:52:17 -0400274 setlineno(cas);
Russ Cox50387922011-01-30 16:07:57 -0500275 n = cas->left;
Russ Coxf7a867e2009-08-04 12:57:48 -0700276 r = nod(OIF, N, N);
Russ Cox1b3244e2012-12-22 16:46:01 -0500277 r->ninit = cas->ninit;
Russ Cox50387922011-01-30 16:07:57 -0500278 cas->ninit = nil;
Russ Cox54b40372009-08-05 00:42:44 -0700279 if(n != nil) {
Russ Cox1b3244e2012-12-22 16:46:01 -0500280 r->ninit = concat(r->ninit, n->ninit);
Russ Cox54b40372009-08-05 00:42:44 -0700281 n->ninit = nil;
282 }
Russ Coxf7a867e2009-08-04 12:57:48 -0700283 if(n == nil) {
284 // selectdefault(sel *byte);
Russ Cox1b3244e2012-12-22 16:46:01 -0500285 r->ntest = mkcall("selectdefault", types[TBOOL], &r->ninit, var);
Russ Cox50387922011-01-30 16:07:57 -0500286 } else {
287 switch(n->op) {
288 default:
289 fatal("select %O", n->op);
290
291 case OSEND:
Dmitriy Vyukov6b2ec062011-07-21 13:57:13 -0400292 // selectsend(sel *byte, hchan *chan any, elem *any) (selected bool);
Russ Cox50387922011-01-30 16:07:57 -0500293 r->ntest = mkcall1(chanfn("selectsend", 2, n->left->type), types[TBOOL],
Russ Cox1b3244e2012-12-22 16:46:01 -0500294 &r->ninit, var, n->left, n->right);
Russ Cox50387922011-01-30 16:07:57 -0500295 break;
Russ Cox8bf34e32011-03-11 14:47:26 -0500296
Russ Cox50387922011-01-30 16:07:57 -0500297 case OSELRECV:
298 // selectrecv(sel *byte, hchan *chan any, elem *any) (selected bool);
299 r->ntest = mkcall1(chanfn("selectrecv", 2, n->right->left->type), types[TBOOL],
Russ Cox1b3244e2012-12-22 16:46:01 -0500300 &r->ninit, var, n->right->left, n->left);
Russ Cox50387922011-01-30 16:07:57 -0500301 break;
Russ Cox8bf34e32011-03-11 14:47:26 -0500302
303 case OSELRECV2:
304 // selectrecv2(sel *byte, hchan *chan any, elem *any, received *bool) (selected bool);
305 r->ntest = mkcall1(chanfn("selectrecv2", 2, n->right->left->type), types[TBOOL],
Russ Cox1b3244e2012-12-22 16:46:01 -0500306 &r->ninit, var, n->right->left, n->left, n->ntest);
Russ Cox8bf34e32011-03-11 14:47:26 -0500307 break;
Russ Coxf7a867e2009-08-04 12:57:48 -0700308 }
Russ Cox50387922011-01-30 16:07:57 -0500309 }
Dmitriy Vyukov40d7d5a2014-07-20 15:07:10 +0400310 // selv is no longer alive after use.
311 r->nbody = list(r->nbody, nod(OVARKILL, selv, N));
Russ Cox50387922011-01-30 16:07:57 -0500312 r->nbody = concat(r->nbody, cas->nbody);
Russ Coxf7a867e2009-08-04 12:57:48 -0700313 r->nbody = list(r->nbody, nod(OBREAK, N, N));
314 init = list(init, r);
315 }
316
317 // run the select
Russ Coxcce10da2011-07-26 00:52:17 -0400318 setlineno(sel);
Russ Coxf7a867e2009-08-04 12:57:48 -0700319 init = list(init, mkcall("selectgo", T, nil, var));
320 sel->nbody = init;
Russ Coxf7a867e2009-08-04 12:57:48 -0700321
Russ Cox50387922011-01-30 16:07:57 -0500322out:
323 sel->list = nil;
324 walkstmtlist(sel->nbody);
Russ Coxf7a867e2009-08-04 12:57:48 -0700325 lineno = lno;
326}
Dmitriy Vyukov40d7d5a2014-07-20 15:07:10 +0400327
Russ Cox220a6de2014-09-08 00:06:45 -0400328// Keep in sync with src/runtime/chan.h.
Dmitriy Vyukov40d7d5a2014-07-20 15:07:10 +0400329static Type*
330selecttype(int32 size)
331{
332 Node *sel, *sudog, *scase, *arr;
333
334 // TODO(dvyukov): it's possible to generate SudoG and Scase only once
335 // and then cache; and also cache Select per size.
336 sudog = nod(OTSTRUCT, N, N);
337 sudog->list = list(sudog->list, nod(ODCLFIELD, newname(lookup("g")), typenod(ptrto(types[TUINT8]))));
338 sudog->list = list(sudog->list, nod(ODCLFIELD, newname(lookup("selectdone")), typenod(ptrto(types[TUINT8]))));
Keith Randall8eb8b402014-12-08 10:11:08 -0800339 sudog->list = list(sudog->list, nod(ODCLFIELD, newname(lookup("next")), typenod(ptrto(types[TUINT8]))));
Dmitriy Vyukovebac0e62014-08-25 20:12:26 +0400340 sudog->list = list(sudog->list, nod(ODCLFIELD, newname(lookup("prev")), typenod(ptrto(types[TUINT8]))));
Dmitriy Vyukov40d7d5a2014-07-20 15:07:10 +0400341 sudog->list = list(sudog->list, nod(ODCLFIELD, newname(lookup("elem")), typenod(ptrto(types[TUINT8]))));
342 sudog->list = list(sudog->list, nod(ODCLFIELD, newname(lookup("releasetime")), typenod(types[TUINT64])));
Dmitriy Vyukov684de042014-08-21 20:41:09 +0400343 sudog->list = list(sudog->list, nod(ODCLFIELD, newname(lookup("nrelease")), typenod(types[TINT32])));
Keith Randall9a1e1422014-08-24 12:31:03 +0400344 sudog->list = list(sudog->list, nod(ODCLFIELD, newname(lookup("waitlink")), typenod(ptrto(types[TUINT8]))));
Dmitriy Vyukov40d7d5a2014-07-20 15:07:10 +0400345 typecheck(&sudog, Etype);
346 sudog->type->noalg = 1;
347 sudog->type->local = 1;
348
349 scase = nod(OTSTRUCT, N, N);
Keith Randall1d8fa7f2014-09-02 14:13:29 -0700350 scase->list = list(scase->list, nod(ODCLFIELD, newname(lookup("elem")), typenod(ptrto(types[TUINT8]))));
Dmitriy Vyukov40d7d5a2014-07-20 15:07:10 +0400351 scase->list = list(scase->list, nod(ODCLFIELD, newname(lookup("chan")), typenod(ptrto(types[TUINT8]))));
Dmitriy Vyukov88b78b42014-07-20 18:52:46 +0400352 scase->list = list(scase->list, nod(ODCLFIELD, newname(lookup("pc")), typenod(types[TUINTPTR])));
Dmitriy Vyukov40d7d5a2014-07-20 15:07:10 +0400353 scase->list = list(scase->list, nod(ODCLFIELD, newname(lookup("kind")), typenod(types[TUINT16])));
354 scase->list = list(scase->list, nod(ODCLFIELD, newname(lookup("so")), typenod(types[TUINT16])));
355 scase->list = list(scase->list, nod(ODCLFIELD, newname(lookup("receivedp")), typenod(ptrto(types[TUINT8]))));
Keith Randall1d8fa7f2014-09-02 14:13:29 -0700356 scase->list = list(scase->list, nod(ODCLFIELD, newname(lookup("releasetime")), typenod(types[TUINT64])));
Dmitriy Vyukov40d7d5a2014-07-20 15:07:10 +0400357 typecheck(&scase, Etype);
358 scase->type->noalg = 1;
359 scase->type->local = 1;
360
361 sel = nod(OTSTRUCT, N, N);
362 sel->list = list(sel->list, nod(ODCLFIELD, newname(lookup("tcase")), typenod(types[TUINT16])));
363 sel->list = list(sel->list, nod(ODCLFIELD, newname(lookup("ncase")), typenod(types[TUINT16])));
364 sel->list = list(sel->list, nod(ODCLFIELD, newname(lookup("pollorder")), typenod(ptrto(types[TUINT8]))));
365 sel->list = list(sel->list, nod(ODCLFIELD, newname(lookup("lockorder")), typenod(ptrto(types[TUINT8]))));
366 arr = nod(OTARRAY, nodintconst(size), scase);
367 sel->list = list(sel->list, nod(ODCLFIELD, newname(lookup("scase")), arr));
368 arr = nod(OTARRAY, nodintconst(size), typenod(ptrto(types[TUINT8])));
369 sel->list = list(sel->list, nod(ODCLFIELD, newname(lookup("lockorderarr")), arr));
370 arr = nod(OTARRAY, nodintconst(size), typenod(types[TUINT16]));
371 sel->list = list(sel->list, nod(ODCLFIELD, newname(lookup("pollorderarr")), arr));
372 typecheck(&sel, Etype);
373 sel->type->noalg = 1;
374 sel->type->local = 1;
375
376 return sel->type;
377}