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