github.com/razvanm/vanadium-go-1.3@v0.0.0-20160721203343-4a65068e5915/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  static Type* selecttype(int32 size);
    14  
    15  void
    16  typecheckselect(Node *sel)
    17  {
    18  	Node *ncase, *n, *def;
    19  	NodeList *l;
    20  	int lno, count;
    21  
    22  	def = nil;
    23  	lno = setlineno(sel);
    24  	count = 0;
    25  	typechecklist(sel->ninit, Etop);
    26  	for(l=sel->list; l; l=l->next) {
    27  		count++;
    28  		ncase = l->n;
    29  		setlineno(ncase);
    30  		if(ncase->op != OXCASE)
    31  			fatal("typecheckselect %O", ncase->op);
    32  
    33  		if(ncase->list == nil) {
    34  			// default
    35  			if(def != N)
    36  				yyerror("multiple defaults in select (first at %L)", def->lineno);
    37  			else
    38  				def = ncase;
    39  		} else if(ncase->list->next) {
    40  			yyerror("select cases cannot be lists");
    41  		} else {
    42  			n = typecheck(&ncase->list->n, Etop);
    43  			ncase->left = n;
    44  			ncase->list = nil;
    45  			setlineno(n);
    46  			switch(n->op) {
    47  			default:
    48  				yyerror("select case must be receive, send or assign recv");
    49  				break;
    50  
    51  			case OAS:
    52  				// convert x = <-c into OSELRECV(x, <-c).
    53  				// remove implicit conversions; the eventual assignment
    54  				// will reintroduce them.
    55  				if((n->right->op == OCONVNOP || n->right->op == OCONVIFACE) && n->right->implicit)
    56  					n->right = n->right->left;
    57  
    58  				if(n->right->op != ORECV) {
    59  					yyerror("select assignment must have receive on right hand side");
    60  					break;
    61  				}
    62  				n->op = OSELRECV;
    63  				break;
    64  
    65  			case OAS2RECV:
    66  				// convert x, ok = <-c into OSELRECV2(x, <-c) with ntest=ok
    67  				if(n->rlist->n->op != ORECV) {
    68  					yyerror("select assignment must have receive on right hand side");
    69  					break;
    70  				}
    71  				n->op = OSELRECV2;
    72  				n->left = n->list->n;
    73  				n->ntest = n->list->next->n;
    74  				n->list = nil;
    75  				n->right = n->rlist->n;
    76  				n->rlist = nil;
    77  				break;
    78  
    79  			case ORECV:
    80  				// convert <-c into OSELRECV(N, <-c)
    81  				n = nod(OSELRECV, N, n);
    82  				n->typecheck = 1;
    83  				ncase->left = n;
    84  				break;
    85  
    86  			case OSEND:
    87  				break;
    88  			}
    89  		}
    90  		typechecklist(ncase->nbody, Etop);
    91  	}
    92  	sel->xoffset = count;
    93  	lineno = lno;
    94  }
    95  
    96  void
    97  walkselect(Node *sel)
    98  {
    99  	int lno, i;
   100  	Node *n, *r, *a, *var, *selv, *cas, *dflt, *ch;
   101  	NodeList *l, *init;
   102  	
   103  	if(sel->list == nil && sel->xoffset != 0)
   104  		fatal("double walkselect");	// already rewrote
   105  	
   106  	lno = setlineno(sel);
   107  	i = count(sel->list);
   108  	
   109  	// optimization: zero-case select
   110  	if(i == 0) {
   111  		sel->nbody = list1(mkcall("block", nil, nil));
   112  		goto out;
   113  	}
   114  
   115  	// optimization: one-case select: single op.
   116  	// TODO(rsc): Reenable optimization once order.c can handle it.
   117  	// golang.org/issue/7672.
   118  	if(i == 1) {
   119  		cas = sel->list->n;
   120  		setlineno(cas);
   121  		l = cas->ninit;
   122  		if(cas->left != N) {  // not default:
   123  			n = cas->left;
   124  			l = concat(l, n->ninit);
   125  			n->ninit = nil;
   126  			switch(n->op) {
   127  			default:
   128  				fatal("select %O", n->op);
   129  
   130  			case OSEND:
   131  				// ok already
   132  				ch = n->left;
   133  				break;
   134  
   135  			case OSELRECV:
   136  				ch = n->right->left;
   137  			Selrecv1:
   138  				if(n->left == N)
   139  					n = n->right;
   140  				else
   141  					n->op = OAS;
   142  				break;
   143  			
   144  			case OSELRECV2:
   145  				ch = n->right->left;
   146  				if(n->ntest == N)
   147  					goto Selrecv1;
   148  				if(n->left == N) {
   149  					typecheck(&nblank, Erv | Easgn);
   150  					n->left = nblank;
   151  				}
   152  				n->op = OAS2;
   153  				n->list = list(list1(n->left), n->ntest);
   154  				n->rlist = list1(n->right);
   155  				n->right = N;
   156  				n->left = N;
   157  				n->ntest = N;
   158  				n->typecheck = 0;
   159  				typecheck(&n, Etop);
   160  				break;
   161  			}
   162  
   163  			// if ch == nil { block() }; n;
   164  			a = nod(OIF, N, N);
   165  			a->ntest = nod(OEQ, ch, nodnil());
   166  			a->nbody = list1(mkcall("block", nil, &l));
   167  			typecheck(&a, Etop);
   168  			l = list(l, a);
   169  			l = list(l, n);
   170  		}
   171  		l = concat(l, cas->nbody);
   172  		sel->nbody = l;
   173  		goto out;
   174  	}
   175  
   176  	// convert case value arguments to addresses.
   177  	// this rewrite is used by both the general code and the next optimization.
   178  	for(l=sel->list; l; l=l->next) {
   179  		cas = l->n;
   180  		setlineno(cas);
   181  		n = cas->left;
   182  		if(n == N)
   183  			continue;
   184  		switch(n->op) {
   185  		case OSEND:
   186  			n->right = nod(OADDR, n->right, N);
   187  			typecheck(&n->right, Erv);
   188  			break;
   189  		case OSELRECV:
   190  		case OSELRECV2:
   191  			if(n->op == OSELRECV2 && n->ntest == N)
   192  				n->op = OSELRECV;
   193  			if(n->op == OSELRECV2) {
   194  				n->ntest = nod(OADDR, n->ntest, N);
   195  				typecheck(&n->ntest, Erv);
   196  			}
   197  			if(n->left == N)
   198  				n->left = nodnil();
   199  			else {
   200  				n->left = nod(OADDR, n->left, N);
   201  				typecheck(&n->left, Erv);
   202  			}			
   203  			break;
   204  		}
   205  	}
   206  
   207  	// optimization: two-case select but one is default: single non-blocking op.
   208  	if(i == 2 && (sel->list->n->left == nil || sel->list->next->n->left == nil)) {
   209  		if(sel->list->n->left == nil) {
   210  			cas = sel->list->next->n;
   211  			dflt = sel->list->n;
   212  		} else {
   213  			dflt = sel->list->next->n;
   214  			cas = sel->list->n;
   215  		}
   216  		
   217  		n = cas->left;
   218  		setlineno(n);
   219  		r = nod(OIF, N, N);
   220  		r->ninit = cas->ninit;
   221  		switch(n->op) {
   222  		default:
   223  			fatal("select %O", n->op);
   224  
   225  		case OSEND:
   226  			// if selectnbsend(c, v) { body } else { default body }
   227  			ch = n->left;
   228  			r->ntest = mkcall1(chanfn("selectnbsend", 2, ch->type),
   229  					types[TBOOL], &r->ninit, typename(ch->type), ch, n->right);
   230  			break;
   231  			
   232  		case OSELRECV:
   233  			// if c != nil && selectnbrecv(&v, c) { body } else { default body }
   234  			r = nod(OIF, N, N);
   235  			r->ninit = cas->ninit;
   236  			ch = n->right->left;
   237  			r->ntest = mkcall1(chanfn("selectnbrecv", 2, ch->type),
   238  					types[TBOOL], &r->ninit, typename(ch->type), n->left, ch);
   239  			break;
   240  
   241  		case OSELRECV2:
   242  			// if c != nil && selectnbrecv2(&v, c) { body } else { default body }
   243  			r = nod(OIF, N, N);
   244  			r->ninit = cas->ninit;
   245  			ch = n->right->left;
   246  			r->ntest = mkcall1(chanfn("selectnbrecv2", 2, ch->type),
   247  					types[TBOOL], &r->ninit, typename(ch->type), n->left, n->ntest, ch);
   248  			break;
   249  		}
   250  		typecheck(&r->ntest, Erv);
   251  		r->nbody = cas->nbody;
   252  		r->nelse = concat(dflt->ninit, dflt->nbody);
   253  		sel->nbody = list1(r);
   254  		goto out;
   255  	}		
   256  
   257  	init = sel->ninit;
   258  	sel->ninit = nil;
   259  
   260  	// generate sel-struct
   261  	setlineno(sel);
   262  	selv = temp(selecttype(sel->xoffset));
   263  	r = nod(OAS, selv, N);
   264  	typecheck(&r, Etop);
   265  	init = list(init, r);
   266  	var = conv(conv(nod(OADDR, selv, N), types[TUNSAFEPTR]), ptrto(types[TUINT8]));
   267  	r = mkcall("newselect", T, nil, var, nodintconst(selv->type->width), nodintconst(sel->xoffset));
   268  	typecheck(&r, Etop);
   269  	init = list(init, r);
   270  
   271  	// register cases
   272  	for(l=sel->list; l; l=l->next) {
   273  		cas = l->n;
   274  		setlineno(cas);
   275  		n = cas->left;
   276  		r = nod(OIF, N, N);
   277  		r->ninit = cas->ninit;
   278  		cas->ninit = nil;
   279  		if(n != nil) {
   280  			r->ninit = concat(r->ninit, n->ninit);
   281  			n->ninit = nil;
   282  		}
   283  		if(n == nil) {
   284  			// selectdefault(sel *byte);
   285  			r->ntest = mkcall("selectdefault", types[TBOOL], &r->ninit, var);
   286  		} else {
   287  			switch(n->op) {
   288  			default:
   289  				fatal("select %O", n->op);
   290  	
   291  			case OSEND:
   292  				// selectsend(sel *byte, hchan *chan any, elem *any) (selected bool);
   293  				r->ntest = mkcall1(chanfn("selectsend", 2, n->left->type), types[TBOOL],
   294  					&r->ninit, var, n->left, n->right);
   295  				break;
   296  
   297  			case OSELRECV:
   298  				// selectrecv(sel *byte, hchan *chan any, elem *any) (selected bool);
   299  				r->ntest = mkcall1(chanfn("selectrecv", 2, n->right->left->type), types[TBOOL],
   300  					&r->ninit, var, n->right->left, n->left);
   301  				break;
   302  
   303  			case OSELRECV2:
   304  				// selectrecv2(sel *byte, hchan *chan any, elem *any, received *bool) (selected bool);
   305  				r->ntest = mkcall1(chanfn("selectrecv2", 2, n->right->left->type), types[TBOOL],
   306  					&r->ninit, var, n->right->left, n->left, n->ntest);
   307  				break;
   308  			}
   309  		}
   310  		// selv is no longer alive after use.
   311  		r->nbody = list(r->nbody, nod(OVARKILL, selv, N));
   312  		r->nbody = concat(r->nbody, cas->nbody);
   313  		r->nbody = list(r->nbody, nod(OBREAK, N, N));
   314  		init = list(init, r);
   315  	}
   316  
   317  	// run the select
   318  	setlineno(sel);
   319  	init = list(init, mkcall("selectgo", T, nil, var));
   320  	sel->nbody = init;
   321  
   322  out:
   323  	sel->list = nil;
   324  	walkstmtlist(sel->nbody);
   325  	lineno = lno;
   326  }
   327  
   328  // Keep in sync with src/runtime/chan.h.
   329  static Type*
   330  selecttype(int32 size)
   331  {
   332  	Node *sel, *sudog, *scase, *arr;
   333  
   334  	// TODO(dvyukov): it's possible to generate SudoG and Scase only once
   335  	// and then cache; and also cache Select per size.
   336  	sudog = nod(OTSTRUCT, N, N);
   337  	sudog->list = list(sudog->list, nod(ODCLFIELD, newname(lookup("g")), typenod(ptrto(types[TUINT8]))));
   338  	sudog->list = list(sudog->list, nod(ODCLFIELD, newname(lookup("selectdone")), typenod(ptrto(types[TUINT8]))));
   339  	sudog->list = list(sudog->list, nod(ODCLFIELD, newname(lookup("link")), typenod(ptrto(types[TUINT8]))));
   340  	sudog->list = list(sudog->list, nod(ODCLFIELD, newname(lookup("prev")), typenod(ptrto(types[TUINT8]))));
   341  	sudog->list = list(sudog->list, nod(ODCLFIELD, newname(lookup("elem")), typenod(ptrto(types[TUINT8]))));
   342  	sudog->list = list(sudog->list, nod(ODCLFIELD, newname(lookup("releasetime")), typenod(types[TUINT64])));
   343  	sudog->list = list(sudog->list, nod(ODCLFIELD, newname(lookup("nrelease")), typenod(types[TINT32])));
   344  	sudog->list = list(sudog->list, nod(ODCLFIELD, newname(lookup("waitlink")), typenod(ptrto(types[TUINT8]))));
   345  	typecheck(&sudog, Etype);
   346  	sudog->type->noalg = 1;
   347  	sudog->type->local = 1;
   348  
   349  	scase = nod(OTSTRUCT, N, N);
   350  	scase->list = list(scase->list, nod(ODCLFIELD, newname(lookup("elem")), typenod(ptrto(types[TUINT8]))));
   351  	scase->list = list(scase->list, nod(ODCLFIELD, newname(lookup("chan")), typenod(ptrto(types[TUINT8]))));
   352  	scase->list = list(scase->list, nod(ODCLFIELD, newname(lookup("pc")), typenod(types[TUINTPTR])));
   353  	scase->list = list(scase->list, nod(ODCLFIELD, newname(lookup("kind")), typenod(types[TUINT16])));
   354  	scase->list = list(scase->list, nod(ODCLFIELD, newname(lookup("so")), typenod(types[TUINT16])));
   355  	scase->list = list(scase->list, nod(ODCLFIELD, newname(lookup("receivedp")), typenod(ptrto(types[TUINT8]))));
   356  	scase->list = list(scase->list, nod(ODCLFIELD, newname(lookup("releasetime")), typenod(types[TUINT64])));
   357  	typecheck(&scase, Etype);
   358  	scase->type->noalg = 1;
   359  	scase->type->local = 1;
   360  
   361  	sel = nod(OTSTRUCT, N, N);
   362  	sel->list = list(sel->list, nod(ODCLFIELD, newname(lookup("tcase")), typenod(types[TUINT16])));
   363  	sel->list = list(sel->list, nod(ODCLFIELD, newname(lookup("ncase")), typenod(types[TUINT16])));
   364  	sel->list = list(sel->list, nod(ODCLFIELD, newname(lookup("pollorder")), typenod(ptrto(types[TUINT8]))));
   365  	sel->list = list(sel->list, nod(ODCLFIELD, newname(lookup("lockorder")), typenod(ptrto(types[TUINT8]))));
   366  	arr = nod(OTARRAY, nodintconst(size), scase);
   367  	sel->list = list(sel->list, nod(ODCLFIELD, newname(lookup("scase")), arr));
   368  	arr = nod(OTARRAY, nodintconst(size), typenod(ptrto(types[TUINT8])));
   369  	sel->list = list(sel->list, nod(ODCLFIELD, newname(lookup("lockorderarr")), arr));
   370  	arr = nod(OTARRAY, nodintconst(size), typenod(types[TUINT16]));
   371  	sel->list = list(sel->list, nod(ODCLFIELD, newname(lookup("pollorderarr")), arr));
   372  	typecheck(&sel, Etype);
   373  	sel->type->noalg = 1;
   374  	sel->type->local = 1;
   375  
   376  	return sel->type;
   377  }