github.com/razvanm/vanadium-go-1.3@v0.0.0-20160721203343-4a65068e5915/src/cmd/gc/select.c (about) 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 <u.h> 10 #include <libc.h> 11 #include "go.h" 12 13 static Type* selecttype(int32 size); 14 15 void 16 typecheckselect(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) { 47 default: 48 yyerror("select case must be receive, send or assign recv"); 49 break; 50 51 case OAS: 52 // convert x = <-c into OSELRECV(x, <-c). 53 // remove implicit conversions; the eventual assignment 54 // will reintroduce them. 55 if((n->right->op == OCONVNOP || n->right->op == OCONVIFACE) && n->right->implicit) 56 n->right = n->right->left; 57 58 if(n->right->op != ORECV) { 59 yyerror("select assignment must have receive on right hand side"); 60 break; 61 } 62 n->op = OSELRECV; 63 break; 64 65 case OAS2RECV: 66 // convert x, ok = <-c into OSELRECV2(x, <-c) with ntest=ok 67 if(n->rlist->n->op != ORECV) { 68 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; 74 n->list = nil; 75 n->right = n->rlist->n; 76 n->rlist = nil; 77 break; 78 79 case ORECV: 80 // convert <-c into OSELRECV(N, <-c) 81 n = nod(OSELRECV, N, n); 82 n->typecheck = 1; 83 ncase->left = n; 84 break; 85 86 case OSEND: 87 break; 88 } 89 } 90 typechecklist(ncase->nbody, Etop); 91 } 92 sel->xoffset = count; 93 lineno = lno; 94 } 95 96 void 97 walkselect(Node *sel) 98 { 99 int lno, i; 100 Node *n, *r, *a, *var, *selv, *cas, *dflt, *ch; 101 NodeList *l, *init; 102 103 if(sel->list == nil && sel->xoffset != 0) 104 fatal("double walkselect"); // already rewrote 105 106 lno = setlineno(sel); 107 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. 116 // TODO(rsc): Reenable optimization once order.c can handle it. 117 // golang.org/issue/7672. 118 if(i == 1) { 119 cas = sel->list->n; 120 setlineno(cas); 121 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: 131 // ok already 132 ch = n->left; 133 break; 134 135 case OSELRECV: 136 ch = n->right->left; 137 Selrecv1: 138 if(n->left == N) 139 n = n->right; 140 else 141 n->op = OAS; 142 break; 143 144 case OSELRECV2: 145 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; 159 typecheck(&n, Etop); 160 break; 161 } 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 176 // convert case value arguments to addresses. 177 // 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; 180 setlineno(cas); 181 n = cas->left; 182 if(n == N) 183 continue; 184 switch(n->op) { 185 case OSEND: 186 n->right = nod(OADDR, n->right, N); 187 typecheck(&n->right, Erv); 188 break; 189 case OSELRECV: 190 case OSELRECV2: 191 if(n->op == OSELRECV2 && n->ntest == N) 192 n->op = OSELRECV; 193 if(n->op == OSELRECV2) { 194 n->ntest = nod(OADDR, n->ntest, N); 195 typecheck(&n->ntest, Erv); 196 } 197 if(n->left == N) 198 n->left = nodnil(); 199 else { 200 n->left = nod(OADDR, n->left, N); 201 typecheck(&n->left, Erv); 202 } 203 break; 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)) { 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; 218 setlineno(n); 219 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: 226 // if selectnbsend(c, v) { body } else { default body } 227 ch = n->left; 228 r->ntest = mkcall1(chanfn("selectnbsend", 2, ch->type), 229 types[TBOOL], &r->ninit, typename(ch->type), ch, n->right); 230 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; 236 ch = n->right->left; 237 r->ntest = mkcall1(chanfn("selectnbrecv", 2, ch->type), 238 types[TBOOL], &r->ninit, typename(ch->type), n->left, ch); 239 break; 240 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; 245 ch = n->right->left; 246 r->ntest = mkcall1(chanfn("selectnbrecv2", 2, ch->type), 247 types[TBOOL], &r->ninit, typename(ch->type), n->left, n->ntest, ch); 248 break; 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 selv = temp(selecttype(sel->xoffset)); 263 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)); 268 typecheck(&r, Etop); 269 init = list(init, r); 270 271 // register cases 272 for(l=sel->list; l; l=l->next) { 273 cas = l->n; 274 setlineno(cas); 275 n = cas->left; 276 r = nod(OIF, N, N); 277 r->ninit = cas->ninit; 278 cas->ninit = nil; 279 if(n != nil) { 280 r->ninit = concat(r->ninit, n->ninit); 281 n->ninit = nil; 282 } 283 if(n == nil) { 284 // selectdefault(sel *byte); 285 r->ntest = mkcall("selectdefault", types[TBOOL], &r->ninit, var); 286 } else { 287 switch(n->op) { 288 default: 289 fatal("select %O", n->op); 290 291 case OSEND: 292 // selectsend(sel *byte, hchan *chan any, elem *any) (selected bool); 293 r->ntest = mkcall1(chanfn("selectsend", 2, n->left->type), types[TBOOL], 294 &r->ninit, var, n->left, n->right); 295 break; 296 297 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], 300 &r->ninit, var, n->right->left, n->left); 301 break; 302 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], 306 &r->ninit, var, n->right->left, n->left, n->ntest); 307 break; 308 } 309 } 310 // selv is no longer alive after use. 311 r->nbody = list(r->nbody, nod(OVARKILL, selv, N)); 312 r->nbody = concat(r->nbody, cas->nbody); 313 r->nbody = list(r->nbody, nod(OBREAK, N, N)); 314 init = list(init, r); 315 } 316 317 // run the select 318 setlineno(sel); 319 init = list(init, mkcall("selectgo", T, nil, var)); 320 sel->nbody = init; 321 322 out: 323 sel->list = nil; 324 walkstmtlist(sel->nbody); 325 lineno = lno; 326 } 327 328 // Keep in sync with src/runtime/chan.h. 329 static Type* 330 selecttype(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])))); 339 sudog->list = list(sudog->list, nod(ODCLFIELD, newname(lookup("link")), typenod(ptrto(types[TUINT8])))); 340 sudog->list = list(sudog->list, nod(ODCLFIELD, newname(lookup("prev")), typenod(ptrto(types[TUINT8])))); 341 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]))); 343 sudog->list = list(sudog->list, nod(ODCLFIELD, newname(lookup("nrelease")), typenod(types[TINT32]))); 344 sudog->list = list(sudog->list, nod(ODCLFIELD, newname(lookup("waitlink")), typenod(ptrto(types[TUINT8])))); 345 typecheck(&sudog, Etype); 346 sudog->type->noalg = 1; 347 sudog->type->local = 1; 348 349 scase = nod(OTSTRUCT, N, N); 350 scase->list = list(scase->list, nod(ODCLFIELD, newname(lookup("elem")), typenod(ptrto(types[TUINT8])))); 351 scase->list = list(scase->list, nod(ODCLFIELD, newname(lookup("chan")), typenod(ptrto(types[TUINT8])))); 352 scase->list = list(scase->list, nod(ODCLFIELD, newname(lookup("pc")), typenod(types[TUINTPTR]))); 353 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])))); 356 scase->list = list(scase->list, nod(ODCLFIELD, newname(lookup("releasetime")), typenod(types[TUINT64]))); 357 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 }