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  }