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  }