]> Cypherpunks repositories - gostls13.git/commitdiff
rewrote switch
authorKen Thompson <ken@golang.org>
Mon, 23 Mar 2009 03:54:21 +0000 (20:54 -0700)
committerKen Thompson <ken@golang.org>
Mon, 23 Mar 2009 03:54:21 +0000 (20:54 -0700)
fixed bug 141

R=r
OCL=26627
CL=26627

src/cmd/gc/go.y
src/cmd/gc/swt.c

index 233b76cdd7c147321f733c1aab8c2b763d2bc515..d6155f9f243848726e2134b72dc65e26e0d49ead 100644 (file)
@@ -19,7 +19,7 @@
 %token <sym>           LMAP LCHAN LINTERFACE LFUNC LSTRUCT
 %token <sym>           LCOLAS LFALL LRETURN LDDD
 %token <sym>           LLEN LCAP LPANIC LPANICN LPRINT LPRINTN
-%token <sym>           LVAR LTYPE LCONST LCONVERT LSELECT LMAKE LNEW
+%token <sym>           LVAR LTYPE LCONST LSELECT LMAKE LNEW
 %token <sym>           LFOR LIF LELSE LSWITCH LCASE LDEFAULT
 %token <sym>           LBREAK LCONTINUE LGO LGOTO LRANGE
 %token <sym>           LNIL LTRUE LFALSE LIOTA
index 9d9977e78f7adf923583b9362292fc7dc7818d8d..59065b6f0632fb536b0b5f1714319d2d33590908 100644 (file)
@@ -11,30 +11,222 @@ enum
        Sfalse,
        Stype,
 
-       Ncase   = 4,    // needed to binary search
+       Tdefault,       // default case
+       Texprconst,     // normal constant case
+       Texprvar,       // normal variable case
+       Ttypenil,       // case nil
+       Ttypeconst,     // type hashes
+       Ttypevar,       // interface type
+
+       Ncase   = 4,    // count needed to split
 };
-Node*  exprbsw(Node *t, Iter *save, Node *name);
-void   typeswitch(Node *sw);
 
 typedef        struct  Case    Case;
 struct Case
 {
        Node*   node;           // points at case statement
        uint32  hash;           // hash of a type switch
-       uint8   uniq;           // first of multiple identical hashes
+       uint8   type;           // type of case
        uint8   diag;           // suppress multiple diagnostics
+       uint16  ordinal;        // position in switch
        Case*   link;           // linked list to link
 };
 #define        C       ((Case*)nil)
 
+void
+dumpcase(Case *c0)
+{
+       Case *c;
+
+       for(c=c0; c!=C; c=c->link) {
+               switch(c->type) {
+               case Tdefault:
+                       print("case-default\n");
+                       print(" ord=%d\n", c->ordinal);
+                       break;
+               case Texprconst:
+                       print("case-exprconst\n");
+                       print(" ord=%d\n", c->ordinal);
+                       break;
+               case Texprvar:
+                       print("case-exprvar\n");
+                       print(" ord=%d\n", c->ordinal);
+                       print(" op=%O\n", c->node->left->op);
+                       break;
+               case Ttypenil:
+                       print("case-typenil\n");
+                       print(" ord=%d\n", c->ordinal);
+                       break;
+               case Ttypeconst:
+                       print("case-typeconst\n");
+                       print(" ord=%d\n", c->ordinal);
+                       print(" hash=%ux\n", c->hash);
+                       break;
+               case Ttypevar:
+                       print("case-typevar\n");
+                       print(" ord=%d\n", c->ordinal);
+                       break;
+               default:
+                       print("case-???\n");
+                       print(" ord=%d\n", c->ordinal);
+                       print(" op=%O\n", c->node->left->op);
+                       print(" hash=%ux\n", c->hash);
+                       break;
+               }
+       }
+       print("\n");
+}
+
+static int
+ordlcmp(Case *c1, Case *c2)
+{
+       // sort default first
+       if(c1->type == Tdefault)
+               return -1;
+       if(c2->type == Tdefault)
+               return +1;
+
+       // sort nil second
+       if(c1->type == Ttypenil)
+               return -1;
+       if(c2->type == Ttypenil)
+               return +1;
+
+       // sort by ordinal
+       if(c1->ordinal > c2->ordinal)
+               return +1;
+       if(c1->ordinal < c2->ordinal)
+               return -1;
+       return 0;
+}
+
+static int
+exprcmp(Case *c1, Case *c2)
+{
+       int ct, n;
+       Node *n1, *n2;
+
+       // sort non-constants last
+       if(c1->type != Texprconst)
+               return +1;
+       if(c2->type != Texprconst)
+               return -1;
+
+       n1 = c1->node->left;
+       n2 = c2->node->left;
+
+       ct = n1->val.ctype;
+       if(ct != n2->val.ctype)
+               fatal("exprcmp");
+
+       // sort by constant value
+       n = 0;
+       switch(ct) {
+       case CTFLT:
+               n = mpcmpfltflt(n1->val.u.fval, n2->val.u.fval);
+               break;
+       case CTINT:
+               n = mpcmpfixfix(n1->val.u.xval, n2->val.u.xval);
+               break;
+       case CTSTR:
+               n = cmpslit(n1, n2);
+               break;
+       }
+
+       return n;
+}
+
+static int
+typecmp(Case *c1, Case *c2)
+{
+
+       // sort non-constants last
+       if(c1->type != Ttypeconst)
+               return +1;
+       if(c2->type != Ttypeconst)
+               return -1;
+
+       // sort by hash code
+       if(c1->hash > c2->hash)
+               return +1;
+       if(c1->hash < c2->hash)
+               return -1;
+       return 0;
+}
+
+static Case*
+csort(Case *l, int(*f)(Case*, Case*))
+{
+       Case *l1, *l2, *le;
+
+       if(l == C || l->link == C)
+               return l;
+
+       l1 = l;
+       l2 = l;
+       for(;;) {
+               l2 = l2->link;
+               if(l2 == C)
+                       break;
+               l2 = l2->link;
+               if(l2 == C)
+                       break;
+               l1 = l1->link;
+       }
+
+       l2 = l1->link;
+       l1->link = C;
+       l1 = csort(l, f);
+       l2 = csort(l2, f);
+
+       /* set up lead element */
+       if((*f)(l1, l2) < 0) {
+               l = l1;
+               l1 = l1->link;
+       } else {
+               l = l2;
+               l2 = l2->link;
+       }
+       le = l;
+
+       for(;;) {
+               if(l1 == C) {
+                       while(l2) {
+                               le->link = l2;
+                               le = l2;
+                               l2 = l2->link;
+                       }
+                       le->link = C;
+                       break;
+               }
+               if(l2 == C) {
+                       while(l1) {
+                               le->link = l1;
+                               le = l1;
+                               l1 = l1->link;
+                       }
+                       break;
+               }
+               if((*f)(l1, l2) < 0) {
+                       le->link = l1;
+                       le = l1;
+                       l1 = l1->link;
+               } else {
+                       le->link = l2;
+                       le = l2;
+                       l2 = l2->link;
+               }
+       }
+       le->link = C;
+       return l;
+}
+
 /*
  * walktype
  */
 Type*
 sw0(Node *c, Type *place, int arg)
 {
-       Node *r;
-
        if(c == N)
                return T;
        switch(c->op) {
@@ -149,14 +341,14 @@ void
 casebody(Node *sw)
 {
        Iter save;
-       Node *os, *oc, *t, *c;
+       Node *os, *oc, *n, *c;
        Node *cas, *stat, *def;
        Node *go, *br;
        int32 lno;
 
        lno = setlineno(sw);
-       t = listfirst(&save, &sw->nbody);
-       if(t == N || t->op == OEMPTY) {
+       n = listfirst(&save, &sw->nbody);
+       if(n == N || n->op == OEMPTY) {
                sw->nbody = nod(OLIST, N, N);
                return;
        }
@@ -169,7 +361,7 @@ casebody(Node *sw)
        br = nod(OBREAK, N, N);
 
 loop:
-       if(t == N) {
+       if(n == N) {
                if(oc == N && os != N)
                        yyerror("first switch statement must be a case");
 
@@ -183,170 +375,229 @@ loop:
                return;
        }
 
-       lno = setlineno(t);
+       lno = setlineno(n);
 
-       switch(t->op) {
-       case OXCASE:
-               t->op = OCASE;
-               if(oc == N && os != N)
-                       yyerror("first switch statement must be a case");
+       if(n->op != OXCASE) {
+               stat = list(stat, n);
+               os = n;
+               goto next;
+       }
 
-               // botch - shouldnt fall thru declaration
-               if(os != N && os->op == OXFALL)
-                       os->op = OFALL;
-               else
-                       stat = list(stat, br);
+       n->op = OCASE;
+       if(oc == N && os != N)
+               yyerror("first switch statement must be a case");
 
-               go = nod(OGOTO, newlabel(), N);
+       // botch - shouldnt fall thru declaration
+       if(os != N && os->op == OXFALL)
+               os->op = OFALL;
+       else
+               stat = list(stat, br);
 
-               c = t->left;
-               if(c == N) {
-                       if(def != N)
-                               yyerror("more than one default case");
+       go = nod(OGOTO, newlabel(), N);
 
-                       // reuse original default case
-                       t->right = go;
-                       def = t;
-               }
+       c = n->left;
+       if(c == N) {
+               if(def != N)
+                       yyerror("more than one default case");
 
-               // 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;
+               // reuse original default case
+               n->right = go;
+               def = n;
+       }
 
-       default:
-               stat = list(stat, t);
-               os = t;
-               break;
+       // expand multi-valued cases
+       for(; c!=N; c=c->right) {
+               if(c->op != OLIST) {
+                       // reuse original case
+                       n->left = c;
+                       n->right = go;
+                       cas = list(cas, n);
+                       break;
+               }
+               cas = list(cas, nod(OCASE, c->left, go));
        }
-       t = listnext(&save);
+       stat = list(stat, nod(OLABEL, go->left, N));
+       oc = n;
+       os = N;
+       goto next;
+
+next:
+       n = listnext(&save);
        goto loop;
 }
 
-/*
- * rebulid case statements into if .. goto
- */
-void
-exprswitch(Node *sw, int arg)
+Case*
+mkcaselist(Node *sw, int arg)
 {
        Iter save;
-       Node *name, *bool, *cas;
-       Node *t, *a;
-
-       cas = N;
-       name = N;
-       bool = N;
+       Node *n;
+       Case *c, *c1;
+       int ord;
 
-       if(arg != Strue && arg != Sfalse) {
-               name = nod(OXXX, N, N);
-               tempname(name, sw->ntest->type);
-               cas = nod(OAS, name, sw->ntest);
-       }
+       c = C;
+       ord = 0;
 
-       t = listfirst(&save, &sw->nbody->left);
+       n = listfirst(&save, &sw->nbody->left);
 
 loop:
-       if(t == N) {
-               sw->nbody->left = rev(cas);
-               return;
-       }
+       if(n == N)
+               goto done;
 
-       if(t->left == N) {
-               cas = list(cas, t->right);              // goto default
-               t = listnext(&save);
-               goto loop;
-       }
+       c1 = mal(sizeof(*c1));
+       c1->link = c;
+       c = c1;
+
+       ord++;
+       c->ordinal = ord;
+       c->node = n;
 
-       // pull out the dcl in case this
-       // variable is allocated on the heap.
-       // this should be done better to prevent
-       // multiple (unused) heap allocations per switch.
-       if(t->ninit != N && t->ninit->op == ODCL) {
-               cas = list(cas, t->ninit);
-               t->ninit = N;
+       if(n->left == N) {
+               c->type = Tdefault;
+               goto next;
        }
 
        switch(arg) {
-       default:
-               // not bool const
-               a = exprbsw(t, &save, name);
-               if(a != N)
-                       break;
+       case Stype:
+               c->hash = 0;
+               if(n->left->left == N) {
+                       c->type = Ttypenil;
+                       goto next;
+               }
+               if(istype(n->left->left->type, TINTER)) {
+                       c->type = Ttypevar;
+                       goto next;
+               }
 
-               a = nod(OIF, N, N);
-               a->ntest = nod(OEQ, name, t->left);     // if name == val
-               a->nbody = t->right;                    // then goto l
-               break;
+               c->hash = typehash(n->left->left->type, 1, 0);
+               c->type = Ttypeconst;
+               goto next;
 
+       case Snorm:
        case Strue:
-               a = nod(OIF, N, N);
-               a->ntest = t->left;                     // if val
-               a->nbody = t->right;                    // then goto l
-               break;
+       case Sfalse:
+               c->type = Texprvar;
+               switch(consttype(n->left)) {
+               case CTFLT:
+               case CTINT:
+               case CTSTR:
+                       c->type = Texprconst;
+               }
+               goto next;
+       }
+next:
+       n = listnext(&save);
+       goto loop;
 
+done:
+       if(c == C)
+               return C;
+
+       // sort by value and diagnose duplicate cases
+       switch(arg) {
+       case Stype:
+               c = csort(c, typecmp);
+               for(c1=c; c1->link!=C; c1=c1->link) {
+                       if(typecmp(c1, c1->link) != 0)
+                               continue;
+                       setlineno(c1->link->node);
+                       yyerror("duplicate case in switch");
+                       print("    previous case at %L\n",
+                               c1->node->lineno);
+               }
+               break;
+       case Snorm:
+       case Strue:
        case Sfalse:
-               a = nod(OIF, N, N);
-               a->ntest = nod(ONOT, t->left, N);       // if !val
-               a->nbody = t->right;                    // then goto l
+               c = csort(c, exprcmp);
+               for(c1=c; c1->link!=C; c1=c1->link) {
+                       if(exprcmp(c1, c1->link) != 0)
+                               continue;
+                       setlineno(c1->link->node);
+                       yyerror("duplicate case in switch");
+                       print("    previous case at %L\n",
+                               c1->node->lineno);
+               }
                break;
        }
-       cas = list(cas, a);
 
-       t = listnext(&save);
-       goto loop;
+       // put list back in processing order
+       c = csort(c, ordlcmp);
+       return c;
+}
+
+static Node*   exprname;
+
+Node*
+exprbsw(Case *c0, int ncase, int arg)
+{
+       Node *cas;
+       Node *a, *n;
+       Case *c;
+       int i, half;
+
+       cas = N;
+       if(ncase < Ncase) {
+               for(i=0; i<ncase; i++) {
+                       n = c0->node;
+
+                       switch(arg) {
+                       case Strue:
+                               a = nod(OIF, N, N);
+                               a->ntest = n->left;                     // if val
+                               a->nbody = n->right;                    // then goto l
+                               break;
+
+                       case Sfalse:
+                               a = nod(OIF, N, N);
+                               a->ntest = nod(ONOT, n->left, N);       // if !val
+                               a->nbody = n->right;                    // then goto l
+                               break;
+
+                       default:
+                               a = nod(OIF, N, N);
+                               a->ntest = nod(OEQ, exprname, n->left); // if name == val
+                               a->nbody = n->right;                    // then goto l
+                               break;
+                       }
+
+                       cas = list(cas, a);
+                       c0 = c0->link;
+               }
+               return cas;
+       }
+
+       // find the middle and recur
+       c = c0;
+       half = ncase>>1;
+       for(i=1; i<half; i++)
+               c = c->link;
+       a = nod(OIF, N, N);
+       a->ntest = nod(OLE, exprname, c->node->left);
+       a->nbody = exprbsw(c0, half, arg);
+       a->nelse = exprbsw(c->link, ncase-half, arg);
+       return a;
 }
 
+/*
+ * normal (expression) switch.
+ * rebulid case statements into if .. goto
+ */
 void
-walkswitch(Node *sw)
+exprswitch(Node *sw)
 {
+       Node *def, *cas;
+       Node *a;
+       Case *c0, *c, *c1;
        Type *t;
-       int arg;
+       int arg, ncase;
 
-       /*
-        * reorder the body into (OLIST, cases, statements)
-        * cases have OGOTO into statements.
-        * both have inserted OBREAK statements
-        */
-       walkstate(sw->ninit);
-       if(sw->ntest == N)
-               sw->ntest = nodbool(1);
-       casebody(sw);
 
-       /*
-        * classify the switch test
-        * Strue or Sfalse if the test is a bool constant
-        *      this allows cases to be map/chan/interface assignments
-        *      as well as (boolean) expressions
-        * Stype if the test is v := interface.(type)
-        *      this forces all cases to be types
-        * Snorm otherwise
-        *      all cases are expressions
-        */
-       if(sw->ntest->op == OTYPESW) {
-               typeswitch(sw);
-               return;
-       }
        arg = Snorm;
        if(isconst(sw->ntest, CTBOOL)) {
                arg = Strue;
                if(sw->ntest->val.u.bval == 0)
                        arg = Sfalse;
        }
-
-       /*
-        * init statement is nothing important
-        */
        walktype(sw->ntest, Erv);
 
        /*
@@ -364,326 +615,138 @@ walkswitch(Node *sw)
        walkcases(sw, sw3, arg);
        convlit(sw->ntest, t);
 
+
        /*
         * convert the switch into OIF statements
         */
-       exprswitch(sw, arg);
-       walkstate(sw->nbody);
-}
-
-int
-iscaseconst(Node *t)
-{
-       if(t == N || t->left == N)
-               return 0;
-       switch(consttype(t->left)) {
-       case CTFLT:
-       case CTINT:
-       case CTSTR:
-               return 1;
+       exprname = N;
+       cas = N;
+       if(arg != Strue && arg != Sfalse) {
+               exprname = nod(OXXX, N, N);
+               tempname(exprname, sw->ntest->type);
+               cas = nod(OAS, exprname, sw->ntest);
        }
-       return 0;
-}
 
-int
-countcase(Node *t, Iter save)
-{
-       int n;
-
-       // note that the iter is by value,
-       // so cases are not really consumed
-       for(n=0;; n++) {
-               if(!iscaseconst(t))
-                       return n;
-               t = listnext(&save);
+       c0 = mkcaselist(sw, arg);
+       if(c0 != C && c0->type == Tdefault) {
+               def = c0->node->right;
+               c0 = c0->link;
+       } else {
+               def = nod(OBREAK, N, N);
        }
-}
-
-Case*
-csort(Case *l, int(*f)(Case*, Case*))
-{
-       Case *l1, *l2, *le;
 
-       if(l == C || l->link == C)
-               return l;
-
-       l1 = l;
-       l2 = l;
-       for(;;) {
-               l2 = l2->link;
-               if(l2 == C)
-                       break;
-               l2 = l2->link;
-               if(l2 == C)
-                       break;
-               l1 = l1->link;
+loop:
+       if(c0 == C) {
+               cas = list(cas, def);
+               sw->nbody->left = rev(cas);
+               walkstate(sw->nbody);
+               return;
        }
 
-       l2 = l1->link;
-       l1->link = C;
-       l1 = csort(l, f);
-       l2 = csort(l2, f);
-
-       /* set up lead element */
-       if((*f)(l1, l2) < 0) {
-               l = l1;
-               l1 = l1->link;
-       } else {
-               l = l2;
-               l2 = l2->link;
+       // deal with the variables one-at-a-time
+       if(c0->type != Texprconst) {
+               a = exprbsw(c0, 1, arg);
+               cas = list(cas, a);
+               c0 = c0->link;
+               goto loop;
        }
-       le = l;
 
-       for(;;) {
-               if(l1 == C) {
-                       while(l2) {
-                               le->link = l2;
-                               le = l2;
-                               l2 = l2->link;
-                       }
-                       le->link = C;
-                       break;
-               }
-               if(l2 == C) {
-                       while(l1) {
-                               le->link = l1;
-                               le = l1;
-                               l1 = l1->link;
-                       }
+       // do binary search on run of constants
+       ncase = 1;
+       for(c=c0; c->link!=C; c=c->link) {
+               if(c->link->type != Texprconst)
                        break;
-               }
-               if((*f)(l1, l2) < 0) {
-                       le->link = l1;
-                       le = l1;
-                       l1 = l1->link;
-               } else {
-                       le->link = l2;
-                       le = l2;
-                       l2 = l2->link;
-               }
+               ncase++;
        }
-       le->link = C;
-       return l;
-}
 
-int
-casecmp(Case *c1, Case *c2)
-{
-       int ct;
-       Node *n1, *n2;
+       // break the chain at the count
+       c1 = c->link;
+       c->link = C;
 
-       n1 = c1->node->left;
-       n2 = c2->node->left;
-
-       ct = n1->val.ctype;
-       if(ct != n2->val.ctype)
-               fatal("casecmp1");
+       // sort and compile constants
+       c0 = csort(c0, exprcmp);
+       a = exprbsw(c0, ncase, arg);
+       cas = list(cas, a);
 
-       switch(ct) {
-       case CTFLT:
-               return mpcmpfltflt(n1->val.u.fval, n2->val.u.fval);
-       case CTINT:
-               return mpcmpfixfix(n1->val.u.xval, n2->val.u.xval);
-       case CTSTR:
-               return cmpslit(n1, n2);
-       }
+       c0 = c1;
+       goto loop;
 
-       fatal("casecmp2");
-       return 0;
 }
 
-Node*
-constsw(Case *c0, int ncase, Node *name)
-{
-       Node *cas, *a;
-       Case *c;
-       int i, n;
-
-       // small number do sequentially
-       if(ncase < Ncase) {
-               cas = N;
-               for(i=0; i<ncase; i++) {
-                       a = nod(OIF, N, N);
-                       a->ntest = nod(OEQ, name, c0->node->left);
-                       a->nbody = c0->node->right;
-                       cas = list(cas, a);
-                       c0 = c0->link;
-               }
-               return rev(cas);
-       }
-
-       // find center and recur
-       c = c0;
-       n = ncase>>1;
-       for(i=1; i<n; i++)
-               c = c->link;
-
-       a = nod(OIF, N, N);
-       a->ntest = nod(OLE, name, c->node->left);
-       a->nbody = constsw(c0, n, name);                // include center
-       a->nelse = constsw(c->link, ncase-n, name);     // exclude center
-       return a;
-}
+static Node*   hashname;
+static Node*   facename;
+static Node*   boolname;
 
 Node*
-exprbsw(Node *t, Iter *save, Node *name)
-{
-       Case *c, *c1;
-       int i, ncase;
-       Node *a;
-
-       ncase = countcase(t, *save);
-       if(ncase < Ncase)
-               return N;
-
-       c = C;
-       for(i=1; i<ncase; i++) {
-               c1 = mal(sizeof(*c1));
-               c1->link = c;
-               c1->node = t;
-               c = c1;
-
-               t = listnext(save);
-       }
-
-       // last one shouldnt consume the iter
-       c1 = mal(sizeof(*c1));
-       c1->link = c;
-       c1->node = t;
-       c = c1;
-
-       c = csort(c, casecmp);
-       a = constsw(c, ncase, name);
-       return a;
-}
-
-int
-hashcmp(Case *c1, Case *c2)
-{
-
-       if(c1->hash > c2->hash)
-               return +1;
-       if(c1->hash < c2->hash)
-               return -1;
-       return 0;
-}
-
-int
-counthash(Case *c)
+typeone(Node *t)
 {
-       Case *c1, *c2;
-       Type *t1, *t2;
-       char buf1[NSYMB], buf2[NSYMB];
-       int ncase;
+       Node *a, *b;
 
-       ncase = 0;
-       while(c != C) {
-               c->uniq = 1;
-               ncase++;
+       a = t->left->left;              // var
+       a = nod(OLIST, a, boolname);    // var,bool
 
-               for(c1=c->link; c1!=C; c1=c1->link) {
-                       if(c->hash != c1->hash)
-                               break;
+       b = nod(ODOTTYPE, facename, N);
+       b->type = t->left->left->type;  // interface.(type)
 
-                       // c1 is a non-unique hash
-                       // compare its type to all types c upto c1
-                       for(c2=c; c2!=c1; c2=c2->link) {
-                               if(c->diag)
-                                       continue;
-                               t1 = c1->node->left->left->type;
-                               t2 = c2->node->left->left->type;
-                               if(!eqtype(t1, t2, 0))
-                                       continue;
-                               snprint(buf1, sizeof(buf1), "%#T", t1);
-                               snprint(buf2, sizeof(buf2), "%#T", t2);
-                               if(strcmp(buf1, buf2) != 0)
-                                       continue;
-                               setlineno(c1->node);
-                               yyerror("duplicate type case: %T\n", t1);
-                               c->diag = 1;
-                       }
-               }
-               c = c1;
-       }
-       return ncase;
-}
+       a = nod(OAS, a, b);             // var,bool = interface.(type)
 
-Case*
-nextuniq(Case *c)
-{
-       for(c=c->link; c!=C; c=c->link)
-               if(c->uniq)
-                       return c;
-       return C;
+       b = nod(OIF, N, N);
+       b->ntest = boolname;
+       b->nbody = t->right;            // if bool { goto l }
+       return list(a, b);
 }
 
-static Node*   hashname;
-static Node*   facename;
-static Node*   boolname;
-static Node*   gotodefault;
-
 Node*
 typebsw(Case *c0, int ncase)
 {
-       Node *cas, *cmp;
-       Node *a, *b, *t;
-       Case *c, *c1;
-       int i, n;
+       Node *cas;
+       Node *a, *n;
+       Case *c;
+       int i, half;
+       Val v;
 
        cas = N;
 
        if(ncase < Ncase) {
                for(i=0; i<ncase; i++) {
-                       c1 = nextuniq(c0);
-                       cmp = N;
-                       for(c=c0; c!=c1; c=c->link) {
-                               t = c->node;
-
-                               if(t->left->left == N) {
-                                       // case nil
-                                       Val v;
-                                       v.ctype = CTNIL;
-                                       a = nod(OIF, N, N);
-                                       a->ntest = nod(OEQ, facename, nodlit(v));
-                                       a->nbody = t->right;            // if i==nil { goto l }
-                                       cmp = list(cmp, a);
-                                       continue;
-                               }
-
-                               a = t->left->left;              // var
-                               a = nod(OLIST, a, boolname);    // var,bool
-
-                               b = nod(ODOTTYPE, facename, N);
-                               b->type = t->left->left->type;  // interface.(type)
-
-                               a = nod(OAS, a, b);             // var,bool = interface.(type)
-                               cmp = list(cmp, a);
+                       n = c0->node;
 
+                       switch(c0->type) {
+
+                       case Ttypenil:
+                               v.ctype = CTNIL;
                                a = nod(OIF, N, N);
-                               a->ntest = boolname;
-                               a->nbody = t->right;            // if bool { goto l }
-                               cmp = list(cmp, a);
+                               a->ntest = nod(OEQ, facename, nodlit(v));
+                               a->nbody = n->right;            // if i==nil { goto l }
+                               cas = list(cas, a);
+                               break;
+
+                       case Ttypevar:
+                               a = typeone(n);
+                               cas = list(cas, a);
+                               break;
+
+                       case Ttypeconst:
+                               a = nod(OIF, N, N);
+                               a->ntest = nod(OEQ, hashname, nodintconst(c0->hash));
+                               a->nbody = rev(typeone(n));
+                               cas = list(cas, a);
+                               break;
                        }
-                       cmp = list(cmp, gotodefault);
-                       a = nod(OIF, N, N);
-                       a->ntest = nod(OEQ, hashname, nodintconst(c0->hash));
-                       a->nbody = rev(cmp);
-                       cas = list(cas, a);
-                       c0 = c1;
+                       c0 = c0->link;
                }
-               cas = list(cas, gotodefault);
-               return rev(cas);
+               return cas;
        }
 
        // find the middle and recur
        c = c0;
-       n = ncase>>1;
-       for(i=1; i<n; i++)
-               c = nextuniq(c);
+       half = ncase>>1;
+       for(i=1; i<half; i++)
+               c = c->link;
        a = nod(OIF, N, N);
        a->ntest = nod(OLE, hashname, nodintconst(c->hash));
-       a->nbody = typebsw(c0, n);
-       a->nelse = typebsw(nextuniq(c), ncase-n);
+       a->nbody = typebsw(c0, half);
+       a->nelse = typebsw(c->link, ncase-half);
        return a;
 }
 
@@ -695,10 +758,9 @@ typebsw(Case *c0, int ncase)
 void
 typeswitch(Node *sw)
 {
-       Iter save;
-       Node *cas;
-       Node *t, *a;
-       Case *c, *c1;
+       Node *cas, *def;
+       Node *a;
+       Case *c, *c0, *c1;
        int ncase;
 
        walktype(sw->ntest->right, Erv);
@@ -730,40 +792,68 @@ typeswitch(Node *sw)
        a = nod(OAS, hashname, a);
        cas = list(cas, a);
 
-       gotodefault = N;
-
-       c = C;
-       t = listfirst(&save, &sw->nbody->left);
+       c0 = mkcaselist(sw, Stype);
+       if(c0 != C && c0->type == Tdefault) {
+               def = c0->node->right;
+               c0 = c0->link;
+       } else {
+               def = nod(OBREAK, N, N);
+       }
 
 loop:
-       if(t == N) {
-               if(gotodefault == N)
-                       gotodefault = nod(OBREAK, N, N);
-               c = csort(c, hashcmp);
-               ncase = counthash(c);
-               a = typebsw(c, ncase);
-               sw->nbody->left = list(rev(cas), rev(a));
+       if(c0 == C) {
+               cas = list(cas, def);
+               sw->nbody->left = rev(cas);
                walkstate(sw->nbody);
                return;
        }
-       if(t->left == N) {
-               gotodefault = t->right;
-               t = listnext(&save);
+
+       // deal with the variables one-at-a-time
+       if(c0->type != Ttypeconst) {
+               a = typebsw(c0, 1);
+               cas = list(cas, a);
+               c0 = c0->link;
                goto loop;
        }
-       if(t->left->op != OTYPESW) {
-               t = listnext(&save);
-               goto loop;
+
+       // do binary search on run of constants
+       ncase = 1;
+       for(c=c0; c->link!=C; c=c->link) {
+               if(c->link->type != Ttypeconst)
+                       break;
+               ncase++;
        }
 
-       c1 = mal(sizeof(*c));
-       c1->link = c;
-       c1->node = t;
-       c1->hash = 0;
-       if(t->left->left != N)
-               c1->hash = typehash(t->left->left->type, 1, 0);
-       c = c1;
+       // break the chain at the count
+       c1 = c->link;
+       c->link = C;
+
+       // sort and compile constants
+       c0 = csort(c0, typecmp);
+       a = typebsw(c0, ncase);
+       cas = list(cas, a);
 
-       t = listnext(&save);
+       c0 = c1;
        goto loop;
 }
+
+void
+walkswitch(Node *sw)
+{
+
+       /*
+        * reorder the body into (OLIST, cases, statements)
+        * cases have OGOTO into statements.
+        * both have inserted OBREAK statements
+        */
+       walkstate(sw->ninit);
+       if(sw->ntest == N)
+               sw->ntest = nodbool(1);
+       casebody(sw);
+
+       if(sw->ntest->op == OTYPESW) {
+               typeswitch(sw);
+               return;
+       }
+       exprswitch(sw);
+}