The Go Programming Language

Text file src/cmd/gc/select.c

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

release.r60.3. Except as noted, this content is licensed under a Creative Commons Attribution 3.0 License.