github.com/ader1990/go@v0.0.0-20140630135419-8c24447fa791/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->list = nil; 73 n->right = n->rlist->n; 74 n->rlist = nil; 75 break; 76 77 case ORECV: 78 // convert <-c into OSELRECV(N, <-c) 79 n = nod(OSELRECV, N, n); 80 n->typecheck = 1; 81 ncase->left = n; 82 break; 83 84 case OSEND: 85 break; 86 } 87 } 88 typechecklist(ncase->nbody, Etop); 89 } 90 sel->xoffset = count; 91 lineno = lno; 92 } 93 94 void 95 walkselect(Node *sel) 96 { 97 int lno, i; 98 Node *n, *r, *a, *var, *cas, *dflt, *ch; 99 NodeList *l, *init; 100 101 if(sel->list == nil && sel->xoffset != 0) 102 fatal("double walkselect"); // already rewrote 103 104 lno = setlineno(sel); 105 i = count(sel->list); 106 107 // optimization: zero-case select 108 if(i == 0) { 109 sel->nbody = list1(mkcall("block", nil, nil)); 110 goto out; 111 } 112 113 // optimization: one-case select: single op. 114 // TODO(rsc): Reenable optimization once order.c can handle it. 115 // golang.org/issue/7672. 116 if(i == 1) { 117 cas = sel->list->n; 118 setlineno(cas); 119 l = cas->ninit; 120 if(cas->left != N) { // not default: 121 n = cas->left; 122 l = concat(l, n->ninit); 123 n->ninit = nil; 124 switch(n->op) { 125 default: 126 fatal("select %O", n->op); 127 128 case OSEND: 129 // ok already 130 ch = n->left; 131 break; 132 133 case OSELRECV: 134 ch = n->right->left; 135 Selrecv1: 136 if(n->left == N) 137 n = n->right; 138 else 139 n->op = OAS; 140 break; 141 142 case OSELRECV2: 143 ch = n->right->left; 144 if(n->ntest == N) 145 goto Selrecv1; 146 if(n->left == N) { 147 typecheck(&nblank, Erv | Easgn); 148 n->left = nblank; 149 } 150 n->op = OAS2; 151 n->list = list(list1(n->left), n->ntest); 152 n->rlist = list1(n->right); 153 n->right = N; 154 n->left = N; 155 n->ntest = N; 156 n->typecheck = 0; 157 typecheck(&n, Etop); 158 break; 159 } 160 161 // if ch == nil { block() }; n; 162 a = nod(OIF, N, N); 163 a->ntest = nod(OEQ, ch, nodnil()); 164 a->nbody = list1(mkcall("block", nil, &l)); 165 typecheck(&a, Etop); 166 l = list(l, a); 167 l = list(l, n); 168 } 169 l = concat(l, cas->nbody); 170 sel->nbody = l; 171 goto out; 172 } 173 174 // convert case value arguments to addresses. 175 // this rewrite is used by both the general code and the next optimization. 176 for(l=sel->list; l; l=l->next) { 177 cas = l->n; 178 setlineno(cas); 179 n = cas->left; 180 if(n == N) 181 continue; 182 switch(n->op) { 183 case OSEND: 184 n->right = nod(OADDR, n->right, N); 185 typecheck(&n->right, Erv); 186 break; 187 case OSELRECV: 188 case OSELRECV2: 189 if(n->op == OSELRECV2 && n->ntest == N) 190 n->op = OSELRECV; 191 if(n->op == OSELRECV2) { 192 n->ntest = nod(OADDR, n->ntest, N); 193 typecheck(&n->ntest, Erv); 194 } 195 if(n->left == N) 196 n->left = nodnil(); 197 else { 198 n->left = nod(OADDR, n->left, N); 199 typecheck(&n->left, Erv); 200 } 201 break; 202 } 203 } 204 205 // optimization: two-case select but one is default: single non-blocking op. 206 if(i == 2 && (sel->list->n->left == nil || sel->list->next->n->left == nil)) { 207 if(sel->list->n->left == nil) { 208 cas = sel->list->next->n; 209 dflt = sel->list->n; 210 } else { 211 dflt = sel->list->next->n; 212 cas = sel->list->n; 213 } 214 215 n = cas->left; 216 setlineno(n); 217 r = nod(OIF, N, N); 218 r->ninit = cas->ninit; 219 switch(n->op) { 220 default: 221 fatal("select %O", n->op); 222 223 case OSEND: 224 // if selectnbsend(c, v) { body } else { default body } 225 ch = n->left; 226 r->ntest = mkcall1(chanfn("selectnbsend", 2, ch->type), 227 types[TBOOL], &r->ninit, typename(ch->type), ch, n->right); 228 break; 229 230 case OSELRECV: 231 // if c != nil && selectnbrecv(&v, c) { body } else { default body } 232 r = nod(OIF, N, N); 233 r->ninit = cas->ninit; 234 ch = n->right->left; 235 r->ntest = mkcall1(chanfn("selectnbrecv", 2, ch->type), 236 types[TBOOL], &r->ninit, typename(ch->type), n->left, ch); 237 break; 238 239 case OSELRECV2: 240 // if c != nil && selectnbrecv2(&v, c) { body } else { default body } 241 r = nod(OIF, N, N); 242 r->ninit = cas->ninit; 243 ch = n->right->left; 244 r->ntest = mkcall1(chanfn("selectnbrecv2", 2, ch->type), 245 types[TBOOL], &r->ninit, typename(ch->type), n->left, n->ntest, ch); 246 break; 247 } 248 typecheck(&r->ntest, Erv); 249 r->nbody = cas->nbody; 250 r->nelse = concat(dflt->ninit, dflt->nbody); 251 sel->nbody = list1(r); 252 goto out; 253 } 254 255 init = sel->ninit; 256 sel->ninit = nil; 257 258 // generate sel-struct 259 setlineno(sel); 260 var = temp(ptrto(types[TUINT8])); 261 r = nod(OAS, var, mkcall("newselect", var->type, nil, nodintconst(sel->xoffset))); 262 typecheck(&r, Etop); 263 init = list(init, r); 264 265 // register cases 266 for(l=sel->list; l; l=l->next) { 267 cas = l->n; 268 setlineno(cas); 269 n = cas->left; 270 r = nod(OIF, N, N); 271 r->ninit = cas->ninit; 272 cas->ninit = nil; 273 if(n != nil) { 274 r->ninit = concat(r->ninit, n->ninit); 275 n->ninit = nil; 276 } 277 if(n == nil) { 278 // selectdefault(sel *byte); 279 r->ntest = mkcall("selectdefault", types[TBOOL], &r->ninit, var); 280 } else { 281 switch(n->op) { 282 default: 283 fatal("select %O", n->op); 284 285 case OSEND: 286 // selectsend(sel *byte, hchan *chan any, elem *any) (selected bool); 287 r->ntest = mkcall1(chanfn("selectsend", 2, n->left->type), types[TBOOL], 288 &r->ninit, var, n->left, n->right); 289 break; 290 291 case OSELRECV: 292 // selectrecv(sel *byte, hchan *chan any, elem *any) (selected bool); 293 r->ntest = mkcall1(chanfn("selectrecv", 2, n->right->left->type), types[TBOOL], 294 &r->ninit, var, n->right->left, n->left); 295 break; 296 297 case OSELRECV2: 298 // selectrecv2(sel *byte, hchan *chan any, elem *any, received *bool) (selected bool); 299 r->ntest = mkcall1(chanfn("selectrecv2", 2, n->right->left->type), types[TBOOL], 300 &r->ninit, var, n->right->left, n->left, n->ntest); 301 break; 302 } 303 } 304 r->nbody = concat(r->nbody, cas->nbody); 305 r->nbody = list(r->nbody, nod(OBREAK, N, N)); 306 init = list(init, r); 307 } 308 309 // run the select 310 setlineno(sel); 311 init = list(init, mkcall("selectgo", T, nil, var)); 312 sel->nbody = init; 313 314 out: 315 sel->list = nil; 316 walkstmtlist(sel->nbody); 317 lineno = lno; 318 }