From: Russ Cox Date: Tue, 4 Aug 2009 19:57:48 +0000 (-0700) Subject: move select into its own file. X-Git-Tag: weekly.2009-11-06~982 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=f7a867e1a6151485c3a4686d37dc80a5c40d4589;p=gostls13.git move select into its own file. split into typecheck + walk R=ken OCL=32726 CL=32726 --- diff --git a/src/cmd/gc/Makefile b/src/cmd/gc/Makefile index 9273e3a0fe..ec97020554 100644 --- a/src/cmd/gc/Makefile +++ b/src/cmd/gc/Makefile @@ -36,6 +36,7 @@ OFILES=\ obj.$O\ print.$O\ typecheck.$O\ + select.$O\ $(LIB): $(OFILES) ar rsc $(LIB) $(OFILES) diff --git a/src/cmd/gc/go.h b/src/cmd/gc/go.h index 4b81dd9599..71d34682b6 100644 --- a/src/cmd/gc/go.h +++ b/src/cmd/gc/go.h @@ -363,6 +363,7 @@ enum OSLICE, OSLICEARR, OSLICESTR, ORECV, ORUNESTR, + OSELRECV, // stmts OBLOCK, @@ -973,6 +974,9 @@ void walkswitch(Node*); void walkselect(Node*); void walkdot(Node*, NodeList**); void walkexpr(Node**, NodeList**); +Node* mkcall(char*, Type*, NodeList**, ...); +Node* mkcall1(Node*, Type*, NodeList**, ...); +Node* chanfn(char*, int, Type*); Node* ascompatee1(int, Node*, Node*, NodeList**); NodeList* ascompatee(int, NodeList*, NodeList*, NodeList**); NodeList* ascompatet(int, NodeList*, Type**, int, NodeList**); @@ -1000,6 +1004,7 @@ void walkdeflist(NodeList*); void walkdef(Node*); void typechecklist(NodeList*, int); void typecheckswitch(Node*); +void typecheckselect(Node*); Node* typecheckconv(Node*, Node*, Type*, int); Node* typecheck(Node**, int); diff --git a/src/cmd/gc/select.c b/src/cmd/gc/select.c new file mode 100644 index 0000000000..2fd63cc7c4 --- /dev/null +++ b/src/cmd/gc/select.c @@ -0,0 +1,163 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +/* + * select + */ + +#include "go.h" + +/* + * declare v in + * case v := <-chan // select and switch + * called during parse + */ +Node* +selectas(Node *name, Node *expr, NodeList **init) +{ + Type *t; + + if(expr == N || expr->op != ORECV) + goto bad; + + walkexpr(&expr->left, init); + t = expr->left->type; + if(t == T) + goto bad; + if(t->etype != TCHAN) + goto bad; + t = t->type; + return old2new(name, t, init); + +bad: + return name; +} + +void +typecheckselect(Node *sel) +{ + Node *ncase, *n, *def; + NodeList *l; + int lno, count; + + def = nil; + lno = setlineno(sel); + count = 0; + typechecklist(sel->ninit, Etop); + for(l=sel->list; l; l=l->next) { + count++; + ncase = l->n; + setlineno(ncase); + if(ncase->op != OXCASE) + fatal("typecheckselect %O", ncase->op); + + if(ncase->list == nil) { + // default + if(def != N) + yyerror("multiple defaults in select (first at %L)", def->lineno); + else + def = ncase; + } else if(ncase->list->next) { + yyerror("select cases cannot be lists"); + } else { + n = typecheck(&ncase->list->n, Etop); + ncase->left = n; + ncase->list = nil; + setlineno(n); + switch(n->op) { + case OAS: + // convert x = <-c into OSELRECV(x, c) + if(n->right->op != ORECV) { + yyerror("select assignment must have receive on right hand side"); + break; + } + n->op = OSELRECV; + n->right = n->right->left; + break; + + case ORECV: + // convert <-c into OSELRECV(N, c) + n->op = OSELRECV; + n->right = n->left; + n->left = N; + break; + + case OSEND: + break; + } + } + typechecklist(ncase->nbody, Etop); + } + sel->xoffset = count; + if(count == 0) + yyerror("empty select"); + lineno = lno; +} + +void +walkselect(Node *sel) +{ + int lno; + Node *n, *ncase, *r, *a, *tmp, *var; + NodeList *l, *init; + + lno = setlineno(sel); + init = sel->ninit; + sel->ninit = nil; + + // generate sel-struct + var = nod(OXXX, N, N); + tempname(var, ptrto(types[TUINT8])); + r = nod(OAS, var, mkcall("newselect", var->type, nil, nodintconst(sel->xoffset))); + typecheck(&r, Etop); + init = list(init, r); + + if(sel->list == nil) + fatal("double walkselect"); // already rewrote + + // register cases + for(l=sel->list; l; l=l->next) { + ncase = l->n; + n = ncase->left; + r = nod(OIF, N, N); + r->nbody = ncase->ninit; + ncase->ninit = nil; + if(n == nil) { + // selectdefault(sel *byte); + r->ntest = mkcall("selectdefault", types[TBOOL], &init, var); + } else if(n->op == OSEND) { + // selectsend(sel *byte, hchan *chan any, elem any) (selected bool); + r->ntest = mkcall1(chanfn("selectsend", 2, n->left->type), types[TBOOL], &init, var, n->left, n->right); + } else if(n->op == OSELRECV) { + tmp = N; + if(n->left == N) + a = nodnil(); + else { + // introduce temporary until we're sure this will succeed. + tmp = nod(OXXX, N, N); + tempname(tmp, n->left->type); + a = nod(OADDR, tmp, N); + } + // selectrecv(sel *byte, hchan *chan any, elem *any) (selected bool); + r->ntest = mkcall1(chanfn("selectrecv", 2, n->right->type), types[TBOOL], &init, var, n->right, a); + if(tmp != N) { + a = nod(OAS, n->left, tmp); + typecheck(&a, Etop); + r->nbody = list(r->nbody, a); + } + } else + fatal("select %O", n->op); + r->nbody = concat(r->nbody, ncase->nbody); + r->nbody = list(r->nbody, nod(OBREAK, N, N)); + init = list(init, r); + } + + // run the select + init = list(init, mkcall("selectgo", T, nil, var)); + sel->nbody = init; + sel->list = nil; + walkstmtlist(init); + + lineno = lno; +} diff --git a/src/cmd/gc/subr.c b/src/cmd/gc/subr.c index 52cd3a09da..be99a2afc5 100644 --- a/src/cmd/gc/subr.c +++ b/src/cmd/gc/subr.c @@ -575,6 +575,15 @@ dodump(Node *n, int dep) print("%O%J\n", n->op, n); dodump(n->left, dep+1); break; + + case OXCASE: + print("%N\n", n); + dodump(n->left, dep+1); + dodump(n->right, dep+1); + indent(dep); + print("%O-nbody\n", n->op); + dodumplist(n->nbody, dep+1); + break; } if(n->ntype != nil) { diff --git a/src/cmd/gc/typecheck.c b/src/cmd/gc/typecheck.c index b8139c3d8c..66b2b6a87f 100644 --- a/src/cmd/gc/typecheck.c +++ b/src/cmd/gc/typecheck.c @@ -12,7 +12,6 @@ * * TODO: * trailing ... section of function calls - * select * range */ @@ -859,9 +858,7 @@ reswitch: goto ret; case OSELECT: - typechecklist(n->ninit, Etop); - typecheck(&n->ntest, Erv); - typechecklist(n->list, Etop); + typecheckselect(n); goto ret; case OSWITCH: @@ -1757,4 +1754,3 @@ typecheckas2(Node *n) mismatch: yyerror("assignment count mismatch: %d = %d", cl, cr); } - diff --git a/src/cmd/gc/walk.c b/src/cmd/gc/walk.c index ad7c4254d5..a77163323c 100644 --- a/src/cmd/gc/walk.c +++ b/src/cmd/gc/walk.c @@ -5,10 +5,7 @@ #include "go.h" static Node* walkprint(Node*, NodeList**); -static Node* mkcall(char*, Type*, NodeList**, ...); -static Node* mkcall1(Node*, Type*, NodeList**, ...); static Node* conv(Node*, Type*); -static Node* chanfn(char*, int, Type*); static Node* mapfn(char*, Type*); static Node* makenewvar(Type*, NodeList**, Node**); enum @@ -929,6 +926,7 @@ makenewvar(Type *t, NodeList **init, Node **nstar) return nvar; } +// TODO(rsc): cut void walkdottype(Node *n, NodeList **init) { @@ -942,6 +940,7 @@ walkdottype(Node *n, NodeList **init) } } +// TODO(rsc): cut void walkconv(Node **np, NodeList **init) { @@ -991,232 +990,6 @@ bad: yyerror("invalid %s: %T to %T", what, l->type, t); } -Node* -selcase(Node *n, Node *var, NodeList **init) -{ - Node *a, *r, *c; - Type *t; - - if(n->list == nil) - goto dflt; - c = n->list->n; - if(c->op == ORECV) - goto recv; - - walkexpr(&c->left, init); // chan - walkexpr(&c->right, init); // elem - - t = fixchan(c->left->type); - if(t == T) - return N; - - if(!(t->chan & Csend)) { - yyerror("cannot send on %T", t); - return N; - } - - convlit(&c->right, t->type); - - // selectsend(sel *byte, hchan *chan any, elem any) (selected bool); - a = mkcall1(chanfn("selectsend", 2, t), types[TBOOL], init, var, c->left, c->right); - goto out; - -recv: - if(c->right != N) - goto recv2; - - walkexpr(&c->left, init); // chan - - t = fixchan(c->left->type); - if(t == T) - return N; - - if(!(t->chan & Crecv)) { - yyerror("cannot receive from %T", t); - return N; - } - - // selectrecv(sel *byte, hchan *chan any, elem *any) (selected bool); - a = mkcall1(chanfn("selectrecv", 2, t), types[TBOOL], init, var, c->left, nodnil()); - goto out; - -recv2: - walkexpr(&c->right, init); // chan - - t = fixchan(c->right->type); - if(t == T) - return N; - - if(!(t->chan & Crecv)) { - yyerror("cannot receive from %T", t); - return N; - } - - walkexpr(&c->left, init); - - // selectrecv(sel *byte, hchan *chan any, elem *any) (selected bool); - a = mkcall1(chanfn("selectrecv", 2, t), types[TBOOL], init, var, c->right, nod(OADDR, c->left, N)); - goto out; - -dflt: - // selectdefault(sel *byte); - a = mkcall("selectdefault", types[TBOOL], init, var); - goto out; - -out: - r = nod(OIF, N, N); - r->ntest = a; - - return r; -} - -/* - * enumerate the special cases - * of the case statement: - * case v := <-chan // select and switch - */ -Node* -selectas(Node *name, Node *expr, NodeList **init) -{ - Type *t; - - if(expr == N || expr->op != ORECV) - goto bad; - - walkexpr(&expr->left, init); - t = expr->left->type; - if(t == T) - goto bad; - if(t->etype != TCHAN) - goto bad; - t = t->type; - return old2new(name, t, init); - -bad: - return name; -} - -void -walkselect(Node *sel) -{ - Node *n, *l, *oc, *on, *r; - Node *var, *def; - NodeList *res, *bod, *nbod, *init, *ln; - int count, op; - int32 lno; - - lno = setlineno(sel); - - init = nil; - - // generate sel-struct - var = nod(OXXX, N, N); - tempname(var, ptrto(types[TUINT8])); - - if(sel->list == nil) { - yyerror("empty select"); - return; - } - - count = 0; // number of cases - res = nil; // entire select body - bod = nil; // body of each case - oc = N; // last case - def = N; // default case - for(ln=sel->list; ln; ln=ln->next) { - n = ln->n; - setlineno(n); - if(n->op != OXCASE) - fatal("walkselect %O", n->op); - - count++; - l = N; - if(n->list == nil) { - op = ORECV; // actual value not used - if(def != N) - yyerror("repeated default; first at %L", def->lineno); - def = n; - } else { - l = n->list->n; - op = l->op; - if(n->list->next) { - yyerror("select cases cannot be lists"); - continue; - } - } - - nbod = nil; - switch(op) { - default: - yyerror("select cases must be send, recv or default %O", op); - continue; - - case OAS: - // convert new syntax (a=recv(chan)) to (recv(a,chan)) - if(l->right == N || l->right->op != ORECV) { - yyerror("select cases must be send, recv or default %O", l->right->op); - break; - } - r = l->right; // rcv - r->right = r->left; - r->left = l->left; - n->list->n = r; - - // convert case x := foo: body - // to case tmp := foo: x := tmp; body. - // if x escapes and must be allocated - // on the heap, this delays the allocation - // until after the select has chosen this branch. - if(n->ninit != nil && n->ninit->n->op == ODCL) { - on = nod(OXXX, N, N); - tempname(on, l->left->type); - on->sym = lookup("!tmpselect!"); - r->left = on; - on = nod(OAS, l->left, on); - typecheck(&on, Etop); - nbod = list(n->ninit, on); - n->ninit = nil; - } - break; - - case OSEND: - case OSENDNB: - case ORECV: - break; - } - - nbod = concat(nbod, n->nbody); - nbod = list(nbod, nod(OBREAK, N, N)); - n->nbody = nil; - - oc = selcase(n, var, &init); - if(oc != N) { - oc->nbody = nbod; - res = list(res, oc); - } - } - setlineno(sel); - - // selectgo(sel *byte); - res = list(res, mkcall("selectgo", T, nil, var)); - - // newselect(size uint32) (sel *byte); - r = nod(OAS, var, mkcall("newselect", var->type, nil, nodintconst(count))); - typecheck(&r, Etop); - typechecklist(res, Etop); - - sel->ninit = list1(r); - sel->nbody = res; - sel->left = N; - - walkstmtlist(sel->ninit); - walkstmtlist(sel->nbody); -//dump("sel", sel); - - sel->ninit = concat(sel->ninit, init); - lineno = lno; -} - Node* ascompatee1(int op, Node *l, Node *r, NodeList **init) { @@ -2805,7 +2578,7 @@ vmkcall(Node *fn, Type *t, NodeList **init, va_list va) return r; } -static Node* +Node* mkcall(char *name, Type *t, NodeList **init, ...) { Node *r; @@ -2817,7 +2590,7 @@ mkcall(char *name, Type *t, NodeList **init, ...) return r; } -static Node* +Node* mkcall1(Node *fn, Type *t, NodeList **init, ...) { Node *r; @@ -2840,7 +2613,7 @@ conv(Node *n, Type *t) return n; } -static Node* +Node* chanfn(char *name, int n, Type *t) { Node *fn;