diff options
author | Ken Thompson <ken@golang.org> | 2009-03-05 15:49:34 -0800 |
---|---|---|
committer | Ken Thompson <ken@golang.org> | 2009-03-05 15:49:34 -0800 |
commit | f0e229f9510cf3ec9d8e9c332fcc8aa4a865a2ef (patch) | |
tree | c30dad9603ef2231028f0f2a5d0e67097f2b6bd7 /src | |
parent | d63ac64fe49b9b16ad5b0dc694e239b9af6e7e49 (diff) | |
download | golang-f0e229f9510cf3ec9d8e9c332fcc8aa4a865a2ef.tar.gz |
new switch implementation
in preparation of type switch.
no functional change (yet).
R=r
OCL=25784
CL=25788
Diffstat (limited to 'src')
-rw-r--r-- | src/cmd/6g/gen.c | 279 | ||||
-rw-r--r-- | src/cmd/6g/gg.h | 11 | ||||
-rw-r--r-- | src/cmd/gc/Makefile | 1 | ||||
-rw-r--r-- | src/cmd/gc/go.h | 3 | ||||
-rw-r--r-- | src/cmd/gc/subr.c | 13 | ||||
-rw-r--r-- | src/cmd/gc/swt.c | 333 | ||||
-rw-r--r-- | src/cmd/gc/walk.c | 195 |
7 files changed, 394 insertions, 441 deletions
diff --git a/src/cmd/6g/gen.c b/src/cmd/6g/gen.c index d14ad30bb..9bc6126ab 100644 --- a/src/cmd/6g/gen.c +++ b/src/cmd/6g/gen.c @@ -260,8 +260,8 @@ loop: break; case OFOR: - p1 = gbranch(AJMP, T); // goto test sbreak = breakpc; + p1 = gbranch(AJMP, T); // goto test breakpc = gbranch(AJMP, T); // break: goto done scontin = continpc; continpc = pc; @@ -299,15 +299,15 @@ loop: break; case OSWITCH: - p1 = gbranch(AJMP, T); // goto test sbreak = breakpc; + p1 = gbranch(AJMP, T); // goto test breakpc = gbranch(AJMP, T); // break: goto done patch(p1, pc); // test: if(labloop != L) { labloop->op = OFOR; labloop->breakpc = breakpc; } - swgen(n); // switch(test) body + gen(n->nbody, L); // switch(test) body patch(breakpc, pc); // done: breakpc = sbreak; break; @@ -367,279 +367,6 @@ ret: lineno = lno; } -Case* -csort(Case *l, int(*f)(Case*, Case*)) -{ - Case *l1, *l2, *le; - - if(l == 0 || l->slink == 0) - return l; - - l1 = l; - l2 = l; - for(;;) { - l2 = l2->slink; - if(l2 == 0) - break; - l2 = l2->slink; - if(l2 == 0) - break; - l1 = l1->slink; - } - - l2 = l1->slink; - l1->slink = 0; - l1 = csort(l, f); - l2 = csort(l2, f); - - /* set up lead element */ - if((*f)(l1, l2) < 0) { - l = l1; - l1 = l1->slink; - } else { - l = l2; - l2 = l2->slink; - } - le = l; - - for(;;) { - if(l1 == 0) { - while(l2) { - le->slink = l2; - le = l2; - l2 = l2->slink; - } - le->slink = 0; - break; - } - if(l2 == 0) { - while(l1) { - le->slink = l1; - le = l1; - l1 = l1->slink; - } - break; - } - if((*f)(l1, l2) < 0) { - le->slink = l1; - le = l1; - l1 = l1->slink; - } else { - le->slink = l2; - le = l2; - l2 = l2->slink; - } - } - le->slink = 0; - return l; -} - -int -casecmp(Case *c1, Case *c2) -{ - int w; - - w = whatis(c1->scase); - if(w != whatis(c2->scase)) - fatal("casecmp1"); - - switch(w) { - case Wlitfloat: - return mpcmpfltflt(c1->scase->val.u.fval, c2->scase->val.u.fval); - case Wlitint: - return mpcmpfixfix(c1->scase->val.u.xval, c2->scase->val.u.xval); - case Wlitstr: - return cmpslit(c1->scase, c2->scase); -// case Wlitbool: -// case Wlitnil: - } - - fatal("casecmp2"); - return 0; -} - -void -swconst(Case *sa, int nc, Node *n1, Node *tmp) -{ - Case *s, *sb; - Prog *p1, *p2, *p3; - int n; - - // small number of cases -- - // test them sequentially - if(nc < 4) { - for(s=sa; s!=C; s=s->slink) { - setlineno(s->scase); - memset(n1, 0, sizeof(*n1)); - n1->op = OEQ; - n1->left = tmp; - n1->right = s->scase; - walktype(n1, Erv); - bgen(n1, 1, s->sprog); - } - return; - } - - // large number of cases -- - // find the middle and recur on each half - - n = nc/2; - for(s=sa; s!=C; s=s->slink) { - n--; - if(n == 0) - break; - } - n = nc/2; - sb = s->slink; - s->slink = C; - - p1 = gbranch(AJMP, T); // goto midcmp - p2 = pc; // low half of switch - swconst(sa, n, n1, tmp); - - p3 = gbranch(AJMP, T); // goto end - patch(p1, pc); - - setlineno(s->scase); - memset(n1, 0, sizeof(*n1)); - n1->op = OLE; - n1->left = tmp; - n1->right = s->scase; - walktype(n1, Erv); - bgen(n1, 1, p2); - - swconst(sb, nc-n, n1, tmp); // high half of switch - patch(p3, pc); -} - -void -swgen(Node *n) -{ - Node *c1, *c2; - Node n1, tmp; - Case *s0, *se, *s, *sa; - Prog *p1, *dflt; - int32 lno; - int any, nc; - Iter save1, save2; - -// botch - put most of this code in -// walk. gen binary search for -// sequence of constant cases - - lno = setlineno(n); - - p1 = gbranch(AJMP, T); - s0 = C; - se = C; - - // walk thru the body placing breaks - // and labels into the case statements - - any = 0; - dflt = P; - c1 = listfirst(&save1, &n->nbody); - while(c1 != N) { - setlineno(c1); - if(c1->op == OEMPTY) - break; - if(c1->op != OCASE) { - if(s0 == C && dflt == P) - yyerror("unreachable statements in a switch"); - gen(c1, L); - - any = 1; - if(c1->op == OFALL) - any = 0; - c1 = listnext(&save1); - continue; - } - - // put in the break between cases - if(any) - patch(gbranch(AJMP, T), breakpc); - any = 1; - - // loop over case expressions - c2 = listfirst(&save2, &c1->left); - if(c2 == N) - dflt = pc; - - while(c2 != N) { - s = mal(sizeof(*s)); - if(s0 == C) - s0 = s; - else - se->slink = s; - se = s; - - s->scase = c2; // case expression - s->sprog = pc; // where to go - - c2 = listnext(&save2); - } - - c1 = listnext(&save1); - } - - lineno = lno; - - if(any) - patch(gbranch(AJMP, T), breakpc); - - patch(p1, pc); - - if(n->ntest != N) - if(n->ntest->ninit != N) - gen(n->ntest->ninit, L); - tempname(&tmp, n->ntest->type); - cgen(n->ntest, &tmp); - - sa = C; // base of constant cases - nc = 0; - for(s=s0; s!=C; s=s->slink) { - switch(whatis(s->scase)) { - case Wlitfloat: - case Wlitint: - case Wlitstr: -// case Wlitbool: -// case Wlitnil: - nc++; - if(sa == C) - sa = s; - se = s; - continue; - } - if(sa != C) { - se->slink = C; - sa = csort(sa, casecmp); - swconst(sa, nc, &n1, &tmp); - nc = 0; - sa = C; - } - setlineno(s->scase); - memset(&n1, 0, sizeof(n1)); - n1.op = OEQ; - n1.left = &tmp; - n1.right = s->scase; - walktype(&n1, Erv); - bgen(&n1, 1, s->sprog); - } - if(sa != C) { - se->slink = C; - sa = csort(sa, casecmp); - swconst(sa, nc, &n1, &tmp); - } - if(dflt != P) { - patch(gbranch(AJMP, T), dflt); - goto ret; - } - patch(gbranch(AJMP, T), breakpc); - -ret: - lineno = lno; -} - void inarggen(void) { diff --git a/src/cmd/6g/gg.h b/src/cmd/6g/gg.h index a44f104b8..741527c43 100644 --- a/src/cmd/6g/gg.h +++ b/src/cmd/6g/gg.h @@ -64,15 +64,6 @@ struct Sig Sig* link; }; -typedef struct Case Case; -struct Case -{ - Prog* sprog; - Node* scase; - Case* slink; -}; -#define C ((Case*)0) - typedef struct Pool Pool; struct Pool { @@ -143,8 +134,6 @@ EXTERN int sizeof_Array; // runtime sizeof(Array) void compile(Node*); void proglist(void); void gen(Node*, Label*); -void swgen(Node*); -void selgen(Node*); Node* lookdot(Node*, Node*, int); void inarggen(void); void cgen_as(Node*, Node*); diff --git a/src/cmd/gc/Makefile b/src/cmd/gc/Makefile index 1ab449768..4237e972a 100644 --- a/src/cmd/gc/Makefile +++ b/src/cmd/gc/Makefile @@ -21,6 +21,7 @@ OFILES=\ dcl.$O\ export.$O\ walk.$O\ + swt.$O\ const.$O\ mparith1.$O\ mparith2.$O\ diff --git a/src/cmd/gc/go.h b/src/cmd/gc/go.h index 76e440db4..dfd975fba 100644 --- a/src/cmd/gc/go.h +++ b/src/cmd/gc/go.h @@ -812,8 +812,7 @@ void walktype(Node*, int); void walkconv(Node*); void walkas(Node*); void walkbool(Node*); -Type* walkswitch(Node*, Type*(*)(Node*, Type*)); -int casebody(Node*); +void walkswitch(Node*); void walkselect(Node*); int whatis(Node*); void walkdot(Node*); diff --git a/src/cmd/gc/subr.c b/src/cmd/gc/subr.c index bb2c31ebd..aa8b01a53 100644 --- a/src/cmd/gc/subr.c +++ b/src/cmd/gc/subr.c @@ -547,8 +547,11 @@ loop: return; case OCASE: - // the right side points to the next case - print("%O%J\n", n->op, n); + // the right side points to label of the body + if(n->right != N && n->right->op == OGOTO && n->right->left->op == ONAME) + print("%O%J GOTO %N\n", n->op, n, n->right->left); + else + print("%O%J\n", n->op, n); dodump(n->left, dep+1); return; } @@ -654,8 +657,8 @@ opnames[] = [OCMP] = "CMP", [OFALL] = "FALL", [OCOMPOS] = "COMPOS", - [ODOTTYPE] = "DOTTYPE", - [OCONV] = "CONV", + [ODOTTYPE] = "DOTTYPE", + [OCONV] = "CONV", [OCOM] = "COM", [OCONST] = "CONST", [OCONTINUE] = "CONTINUE", @@ -724,7 +727,7 @@ opnames[] = [OPRINT] = "PRINT", [OPRINTN] = "PRINTN", [OPARAM] = "PARAM", - [ODCL] = "DCL", + [ODCL] = "DCL", [OXXX] = "XXX", }; diff --git a/src/cmd/gc/swt.c b/src/cmd/gc/swt.c new file mode 100644 index 000000000..dc3266532 --- /dev/null +++ b/src/cmd/gc/swt.c @@ -0,0 +1,333 @@ +// 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. + +#include "go.h" + +/* + * walktype + */ +Type* +sw0(Node *c, Type *place) +{ + Node *r; + + if(c == N) + return T; + if(c->op != OAS) { + walktype(c, Erv); + return T; + } + walktype(c->left, Elv); + + r = c->right; + if(c == N) + return T; + + switch(r->op) { + default: + goto bad; + case ORECV: + // <-chan + walktype(r->left, Erv); + if(!istype(r->left->type, TCHAN)) + goto bad; + break; + case OINDEX: + // map[e] + walktype(r->left, Erv); + if(!istype(r->left->type, TMAP)) + goto bad; + break; + case ODOTTYPE: + // interface.(type) + walktype(r->left, Erv); + if(!istype(r->left->type, TINTER)) + goto bad; + break; + } + c->type = types[TBOOL]; + return T; + +bad: + yyerror("inappropriate assignment in a case statement"); + return T; +} + +/* + * return the first type + */ +Type* +sw1(Node *c, Type *place) +{ + if(place == T) + return c->type; + return place; +} + +/* + * return a suitable type + */ +Type* +sw2(Node *c, Type *place) +{ + return types[TINT]; // botch +} + +/* + * check that switch type + * is compat with all the cases + */ +Type* +sw3(Node *c, Type *place) +{ + if(place == T) + return c->type; + if(c->type == T) + c->type = place; + convlit(c, place); + if(!ascompat(place, c->type)) + badtype(OSWITCH, place, c->type); + return place; +} + +/* + * over all cases, call paramenter function. + * four passes of these are used to allocate + * types to cases and switch + */ +Type* +walkcases(Node *sw, Type*(*call)(Node*, Type*)) +{ + Iter save; + Node *n; + Type *place; + int32 lno; + + lno = setlineno(sw); + place = call(sw->ntest, T); + + n = listfirst(&save, &sw->nbody->left); + if(n->op == OEMPTY) + return T; + +loop: + if(n == N) { + lineno = lno; + return place; + } + + if(n->op != OCASE) + fatal("walkcases: not case %O\n", n->op); + + if(n->left != N) { + setlineno(n->left); + place = call(n->left, place); + } + n = listnext(&save); + goto loop; +} + +Node* +newlabel() +{ + static int label; + + label++; + snprint(namebuf, sizeof(namebuf), "%.6d", label); + return newname(lookup(namebuf)); +} + +/* + * build separate list of statements and cases + * make labels between cases and statements + * deal with fallthrough, break, unreachable statements + */ +void +casebody(Node *sw) +{ + Iter save; + Node *os, *oc, *t, *c; + Node *cas, *stat, *def; + Node *go, *br; + int32 lno; + + lno = setlineno(sw); + t = listfirst(&save, &sw->nbody); + if(t == N || t->op == OEMPTY) { + sw->nbody = nod(OLIST, N, N); + return; + } + + cas = N; // cases + stat = N; // statements + def = N; // defaults + os = N; // last statement + oc = N; // last case + br = nod(OBREAK, N, N); + +loop: + + if(t == N) { + if(oc == N && os != N) + yyerror("first switch statement must be a case"); + + stat = list(stat, br); + cas = list(cas, def); + + sw->nbody = nod(OLIST, rev(cas), rev(stat)); +//dump("case", sw->nbody->left); +//dump("stat", sw->nbody->right); + lineno = lno; + return; + } + + lno = setlineno(t); + + switch(t->op) { + case OXCASE: + t->op = OCASE; + if(oc == N && os != N) + yyerror("first switch statement must be a case"); + + if(os != N && os->op == OXFALL) + os->op = OFALL; + else + stat = list(stat, br); + + go = nod(OGOTO, newlabel(), N); + + c = t->left; + if(c == N) { + if(def != N) + yyerror("more than one default case"); + + // reuse original default case + t->right = go; + def = t; + } + + // expand multi-valued cases + for(; c!=N; c=c->right) { + if(c->op != OLIST) { + // reuse original case + t->left = c; + t->right = go; + cas = list(cas, t); + break; + } + cas = list(cas, nod(OCASE, c->left, go)); + } + stat = list(stat, nod(OLABEL, go->left, N)); + oc = t; + os = N; + break; + + default: + stat = list(stat, t); + os = t; + break; + } + t = listnext(&save); + goto loop; +} + +/* + * rebulid case statements into if .. goto + */ +void +prepsw(Node *sw) +{ + Iter save; + Node *name, *cas; + Node *t, *a; + int bool; + + bool = 0; + if(whatis(sw->ntest) == Wlitbool) { + bool = 1; // true + if(sw->ntest->val.u.xval == 0) + bool = 2; // false + } + + cas = N; + name = N; + if(bool == 0) { + name = nod(OXXX, N, N); + tempname(name, sw->ntest->type); + cas = nod(OAS, name, sw->ntest); + } + + t = listfirst(&save, &sw->nbody->left); + +loop: + if(t == N) { + sw->nbody->left = rev(cas); + walkstate(sw->nbody->left); +//dump("case", sw->nbody->left); + return; + } + + if(t->left == N) { + cas = list(cas, t->right); // goto default + t = listnext(&save); + goto loop; + } + + a = nod(OIF, N, N); + a->nbody = t->right; // then goto l + + switch(bool) { + default: + // not bool const + a->ntest = nod(OEQ, name, t->left); // if name == val + break; + + case 1: + // bool true + a->ntest = t->left; // if val + break; + + case 2: + // bool false + a->ntest = nod(ONOT, t->left, N); // if !val + break; + } + cas = list(cas, a); + + t = listnext(&save); + goto loop; +} + +void +walkswitch(Node *n) +{ + Type *t; + + casebody(n); + if(n->ntest == N) + n->ntest = booltrue; + + walkstate(n->ninit); + walktype(n->ntest, Erv); + walkstate(n->nbody); + + // walktype + walkcases(n, sw0); + + // find common type + t = n->ntest->type; + if(t == T) + t = walkcases(n, sw1); + + // if that fails pick a type + if(t == T) + t = walkcases(n, sw2); + + // set the type on all literals + if(t != T) { + walkcases(n, sw3); + convlit(n->ntest, t); + prepsw(n); + } +} diff --git a/src/cmd/gc/walk.c b/src/cmd/gc/walk.c index 3ae0f52f7..d82dfd4eb 100644 --- a/src/cmd/gc/walk.c +++ b/src/cmd/gc/walk.c @@ -4,9 +4,6 @@ #include "go.h" -static Type* sw1(Node*, Type*); -static Type* sw2(Node*, Type*); -static Type* sw3(Node*, Type*); static Node* curfn; enum @@ -318,26 +315,7 @@ loop: if(top != Etop) goto nottop; - casebody(n->nbody); - if(n->ntest == N) - n->ntest = booltrue; - walkstate(n->ninit); - walktype(n->ntest, Erv); - walkstate(n->nbody); - - // find common type - if(n->ntest->type == T) - n->ntest->type = walkswitch(n, sw1); - - // if that fails pick a type - if(n->ntest->type == T) - n->ntest->type = walkswitch(n, sw2); - - // set the type on all literals - if(n->ntest->type != T) - walkswitch(n, sw3); - walktype(n->ntest, Erv); // BOTCH is this right - walktype(n->nincr, Erv); + walkswitch(n); goto ret; case OSELECT: @@ -577,7 +555,6 @@ loop: case OCASE: if(top != Etop) goto nottop; - walktype(n->left, Erv); walkstate(n->right); goto ret; @@ -1251,124 +1228,11 @@ walkconv(Node *n) bad: if(l->type != T) yyerror("invalid conversion: %T to %T", l->type, t); - else if(n->left->op == OLIST) + else + if(n->left->op == OLIST) yyerror("invalid type for composite literal: %T", t); } - -/* - * return the first type - */ -Type* -sw1(Node *c, Type *place) -{ - if(place == T) - return c->type; - return place; -} - -/* - * return a suitable type - */ -Type* -sw2(Node *c, Type *place) -{ - return types[TINT]; // botch -} - -/* - * check that switch type - * is compat with all the cases - */ -Type* -sw3(Node *c, Type *place) -{ - if(place == T) - return c->type; - if(c->type == T) - c->type = place; - convlit(c, place); - if(!ascompat(place, c->type)) - badtype(OSWITCH, place, c->type); - return place; -} - -Type* -walkswitch(Node *sw, Type*(*call)(Node*, Type*)) -{ - Node *n, *c; - Type *place; - place = call(sw->ntest, T); - - setlineno(sw); - - n = sw->nbody; - if(n->op == OLIST) - n = n->left; - if(n->op == OEMPTY) - return T; - - for(; n!=N; n=n->right) { - if(n->op != OCASE) - fatal("walkswitch: not case %O\n", n->op); - for(c=n->left; c!=N; c=c->right) { - if(c->op != OLIST) { - setlineno(c); - place = call(c, place); - break; - } - setlineno(c); - place = call(c->left, place); - } - } - return place; -} - -int -casebody(Node *n) -{ - Node *oc, *ot, *t; - Iter save; - - /* - * look to see if statements at top level have - * case labels attached to them. convert the illegal - * ops XFALL and XCASE into legal ops FALL and CASE. - * all unconverted ops will thus be caught as illegal - */ - - oc = N; // last case statement - ot = N; // last statement (look for XFALL) - t = listfirst(&save, &n); - -loop: - if(t == N) { - /* empty switch */ - if(oc == N) - return 0; - return 1; - } - if(t->op == OXCASE) { - /* rewrite and link top level cases */ - t->op = OCASE; - if(oc != N) - oc->right = t; - oc = t; - - /* rewrite top fall that preceed case */ - if(ot != N && ot->op == OXFALL) - ot->op = OFALL; - } - - /* if first statement is not case */ - if(oc == N) - return 0; - - ot = t; - t = listnext(&save); - goto loop; -} - Node* selcase(Node *n, Node *var) { @@ -1477,21 +1341,58 @@ out: return r; } +/* + * enumerate the special cases + * of the case statement: + * case v := <-chan // select and switch + * case v := map[] // switch + * case v := interface.(TYPE) // switch + */ Node* selectas(Node *name, Node *expr) { Node *a; Type *t; - if(expr == N || expr->op != ORECV) - goto bad; - walktype(expr->left, Erv); - t = expr->left->type; - if(t == T) + if(expr == N) goto bad; - if(t->etype != TCHAN) + switch(expr->op) { + default: +//dump("case", expr); goto bad; - a = old2new(name, t->type); + + case ORECV: + walktype(expr->left, Erv); + t = expr->left->type; + if(t == T) + goto bad; + if(t->etype != TCHAN) + goto bad; + t = t->type; + break; + + case OINDEX: + walktype(expr->left, Erv); + walktype(expr->right, Erv); + t = expr->left->type; + if(t == T) + goto bad; + if(t->etype != TMAP) + goto bad; + t = t->type; + break; + + case ODOTTYPE: + walktype(expr->left, Erv); + t = expr->left->type; + if(t == T) + goto bad; + if(t->etype != TINTER) + goto bad; + t = expr->type; + break; + } + a = old2new(name, t); return a; bad: @@ -1523,7 +1424,7 @@ walkselect(Node *sel) oc = N; // last case def = N; // default case - for(count=0; n!=N; n=listnext(&iter)) { + for(; n!=N; n=listnext(&iter)) { setlineno(n); switch(n->op) { |