]> Cypherpunks repositories - gostls13.git/commitdiff
binary search on type switches.
authorKen Thompson <ken@golang.org>
Tue, 17 Mar 2009 20:58:38 +0000 (13:58 -0700)
committerKen Thompson <ken@golang.org>
Tue, 17 Mar 2009 20:58:38 +0000 (13:58 -0700)
new feature 'case nil:' in type switch
will match iff the interface is nil.

R=r
OCL=26404
CL=26404

src/cmd/6g/obj.c
src/cmd/gc/builtin.c.boot
src/cmd/gc/go.h
src/cmd/gc/go.y
src/cmd/gc/subr.c
src/cmd/gc/swt.c
src/cmd/gc/sys.go
src/runtime/iface.c

index 50d7244a424d51a6eed11c330ea4fd6a9b527b49..af375a1d455c9f1f6aadbffdfddf6d13076eee7a 100644 (file)
@@ -661,7 +661,7 @@ dumpsigt(Type *progt, Type *ifacet, Type *rcvrt, Type *methodt, Sym *s)
        a = nil;
        o = 0;
        oldlist = nil;
-       sighash = typehash(progt, 0);
+       sighash = typehash(progt, 1, 0);
        for(f=methodt->method; f!=T; f=f->down) {
                if(f->type->etype != TFUNC)
                        continue;
@@ -678,7 +678,7 @@ dumpsigt(Type *progt, Type *ifacet, Type *rcvrt, Type *methodt, Sym *s)
                a = b;
 
                a->name = method->name;
-               a->hash = PRIME8*stringhash(a->name) + PRIME9*typehash(f->type, 0);
+               a->hash = PRIME8*stringhash(a->name) + PRIME9*typehash(f->type, 0, 0);
                if(!exportname(a->name))
                        a->hash += PRIME10*stringhash(package);
                a->perm = o;
@@ -735,7 +735,7 @@ dumpsigt(Type *progt, Type *ifacet, Type *rcvrt, Type *methodt, Sym *s)
        // base of type signature contains parameters
        ginsatoa(widthptr, stringo);            // name
        ot = rnd(ot, widthptr)+widthptr;        // skip link
-       gensatac(wi, typehash(progt, 0));       // thash
+       gensatac(wi, typehash(progt, 1, 0));    // thash
        gensatac(wi, sighash);                  // mhash
        gensatac(ws, progt->width);             // width
        gensatac(ws, algtype(progt));           // algorithm
@@ -815,7 +815,7 @@ dumpsigi(Type *t, Sym *s)
                a = b;
 
                a->name = s1->name;
-               a->hash = PRIME8*stringhash(a->name) + PRIME9*typehash(f->type, 0);
+               a->hash = PRIME8*stringhash(a->name) + PRIME9*typehash(f->type, 0, 0);
                if(!exportname(a->name))
                        a->hash += PRIME10*stringhash(package);
                a->perm = o;
index 9245936dd285d547ebcca7c1d8927a77d73d5ffa..07b0c828fbadeff8ae2264c0b0222321d1af9457 100644 (file)
@@ -26,6 +26,7 @@ char *sysimport =
        "func sys.ifaceI2I (sigi *uint8, iface any) (ret any)\n"
        "func sys.ifaceI2I2 (sigi *uint8, iface any) (ret any, ok bool)\n"
        "func sys.ifaceeq (i1 any, i2 any) (ret bool)\n"
+       "func sys.ifacethash (i1 any) (ret uint32)\n"
        "func sys.newmap (keysize int, valsize int, keyalg int, valalg int, hint int) (hmap map[any] any)\n"
        "func sys.mapaccess1 (hmap map[any] any, key any) (val any)\n"
        "func sys.mapaccess2 (hmap map[any] any, key any) (val any, pres bool)\n"
index 46c99baebaa7c11d68d836421bd710c05198f120..458a37ad1c33257a821d1b20d66fb84b32f69104 100644 (file)
@@ -659,7 +659,7 @@ int eqtype(Type*, Type*, int);
 int    eqtypenoname(Type*, Type*);
 void   argtype(Node*, Type*);
 int    eqargs(Type*, Type*);
-uint32 typehash(Type*, int);
+uint32 typehash(Type*, int, int);
 void   frame(int);
 Node*  dobad(void);
 Node*  nodintconst(int64);
index fd347177f2084825d76e394b7bd2414601cc2722..233b76cdd7c147321f733c1aab8c2b763d2bc515 100644 (file)
@@ -484,6 +484,13 @@ complex_stmt:
                // right will point to next case
                // done in casebody()
                poptodcl();
+               if(typeswvar != N && typeswvar->right != N)
+               if($2->op == OLITERAL && $2->val.ctype == CTNIL) {
+                       // this version in type switch case nil
+                       $$ = nod(OTYPESW, N, N);
+                       $$ = nod(OXCASE, $$, N);
+                       break;
+               }
                $$ = nod(OXCASE, $2, N);
        }
 |      LCASE name '=' expr ':'
@@ -821,7 +828,6 @@ pexpr:
 |      LNIL
        {
                Val v;
-
                v.ctype = CTNIL;
                $$ = nodlit(v);
        }
index a112849a8e3bb71c9d2a79f39ff5b847c463f4c9..eda84820a2bb46e8022deef06f457f73ef9b3d2b 100644 (file)
@@ -1919,7 +1919,7 @@ eqargs(Type *t1, Type *t2)
 }
 
 uint32
-typehash(Type *at, int d)
+typehash(Type *at, int addsym, int d)
 {
        uint32 h;
        Type *t;
@@ -1931,20 +1931,23 @@ typehash(Type *at, int d)
 
        h = at->etype*PRIME4;
 
+       if(addsym && at->sym != S)
+               h += stringhash(at->sym->name);
+
        switch(at->etype) {
        default:
-               h += PRIME5 * typehash(at->type, d+1);
+               h += PRIME5 * typehash(at->type, addsym, d+1);
                break;
 
        case TINTER:
                // botch -- should be sorted?
                for(t=at->type; t!=T; t=t->down)
-                       h += PRIME6 * typehash(t, d+1);
+                       h += PRIME6 * typehash(t, addsym, d+1);
                break;
 
        case TSTRUCT:
                for(t=at->type; t!=T; t=t->down)
-                       h += PRIME7 * typehash(t, d+1);
+                       h += PRIME7 * typehash(t, addsym, d+1);
                break;
 
        case TFUNC:
@@ -1953,7 +1956,7 @@ typehash(Type *at, int d)
                if(t != T)
                        t = t->down;
                for(; t!=T; t=t->down)
-                       h += PRIME7 * typehash(t, d+1);
+                       h += PRIME7 * typehash(t, addsym, d+1);
                break;
        }
 
@@ -2756,9 +2759,9 @@ ifaceokT2I(Type *t0, Type *iface, Type **m)
        // so we can both be wrong together.
 
        for(im=iface->type; im; im=im->down) {
-               imhash = typehash(im, 0);
+               imhash = typehash(im, 0, 0);
                tm = ifacelookdot(im->sym, t);
-               if(tm == T || typehash(tm, 0) != imhash) {
+               if(tm == T || typehash(tm, 0, 0) != imhash) {
                        *m = im;
                        return 0;
                }
@@ -2778,7 +2781,7 @@ ifaceokI2I(Type *i1, Type *i2, Type **m)
 
        for(m2=i2->type; m2; m2=m2->down) {
                for(m1=i1->type; m1; m1=m1->down)
-                       if(m1->sym == m2->sym && typehash(m1, 0) == typehash(m2, 0))
+                       if(m1->sym == m2->sym && typehash(m1, 0, 0) == typehash(m2, 0, 0))
                                goto found;
                *m = m2;
                return 0;
index 408904ff12b8b7f7854751843bcaf1664020137d..e4bd2716656b41e2f9a94f6986fd8d46b661acb7 100644 (file)
@@ -10,8 +10,22 @@ enum
        Strue,
        Sfalse,
        Stype,
+
+       Ncase   = 4,    // needed to binary search
+};
+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   diag;           // suppress multiple diagnostics
+       Case*   link;           // linked list to link
 };
-Node*  binarysw(Node *t, Iter *save, Node *name);
+#define        C       ((Case*)nil)
 
 /*
  * walktype
@@ -263,7 +277,6 @@ exprswitch(Node *sw, int arg)
        Iter save;
        Node *name, *bool, *cas;
        Node *t, *a;
-//dump("exprswitch before", sw->nbody->left);
 
        cas = N;
        name = N;
@@ -280,7 +293,6 @@ exprswitch(Node *sw, int arg)
 loop:
        if(t == N) {
                sw->nbody->left = rev(cas);
-//dump("exprswitch after", sw->nbody->left);
                return;
        }
 
@@ -295,7 +307,6 @@ loop:
        // this should be done better to prevent
        // multiple (unused) heap allocations per switch.
        if(t->ninit != N && t->ninit->op == ODCL) {
-//dump("exprswitch case init", t->ninit);
                cas = list(cas, t->ninit);
                t->ninit = N;
        }
@@ -305,7 +316,6 @@ loop:
                        bool = nod(OXXX, N, N);
                        tempname(bool, types[TBOOL]);
                }
-//dump("oas", t);
                t->left->left = nod(OLIST, t->left->left, bool);
                cas = list(cas, t->left);               // v,bool = rhs
 
@@ -324,7 +334,7 @@ loop:
        switch(arg) {
        default:
                // not bool const
-               a = binarysw(t, &save, name);
+               a = exprbsw(t, &save, name);
                if(a != N)
                        break;
 
@@ -351,96 +361,12 @@ loop:
        goto loop;
 }
 
-/*
- * convert switch of the form
- *     switch v := i.(type) { case t1: ..; case t2: ..; }
- * into if statements
- */
-void
-typeswitch(Node *sw)
-{
-       Iter save;
-       Node *face, *bool, *cas;
-       Node *t, *a, *b;
-
-//dump("typeswitch", sw);
-
-       walktype(sw->ntest->right, Erv);
-       if(!istype(sw->ntest->right->type, TINTER)) {
-               yyerror("type switch must be on an interface");
-               return;
-       }
-       walkcases(sw, sw0, Stype);
-
-       /*
-        * predeclare variables for the interface var
-        * and the boolean var
-        */
-       face = nod(OXXX, N, N);
-       tempname(face, sw->ntest->right->type);
-       cas = nod(OAS, face, sw->ntest->right);
-
-       bool = nod(OXXX, N, N);
-       tempname(bool, types[TBOOL]);
-
-       t = listfirst(&save, &sw->nbody->left);
-
-loop:
-       if(t == N) {
-               sw->nbody->left = rev(cas);
-               walkstate(sw->nbody);
-//dump("done", sw->nbody->left);
-               return;
-       }
-
-       if(t->left == N) {
-               cas = list(cas, t->right);              // goto default
-               t = listnext(&save);
-               goto loop;
-       }
-       if(t->left->op != OTYPESW) {
-               t = listnext(&save);
-               goto loop;
-       }
-
-       // 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.
-       // not worth doing now -- make a binary search
-       // on contents of signature instead.
-       if(t->ninit != N && t->ninit->op == ODCL) {
-//dump("typeswitch case init", t->ninit);
-               cas = list(cas, t->ninit);
-               t->ninit = N;
-       }
-
-       a = t->left->left;              // var
-       a = nod(OLIST, a, bool);        // var,bool
-
-       b = nod(ODOTTYPE, face, N);
-       b->type = t->left->left->type;  // interface.(type)
-
-       a = nod(OAS, a, b);             // var,bool = interface.(type)
-       cas = list(cas, a);
-
-       a = nod(OIF, N, N);
-       a->ntest = bool;
-       a->nbody = t->right;            // if bool { goto l }
-       cas = list(cas, a);
-
-       t = listnext(&save);
-       goto loop;
-}
-
 void
 walkswitch(Node *sw)
 {
        Type *t;
        int arg;
 
-//dump("walkswitch", sw);
-
        /*
         * reorder the body into (OLIST, cases, statements)
         * cases have OGOTO into statements.
@@ -476,7 +402,6 @@ walkswitch(Node *sw)
         * init statement is nothing important
         */
        walktype(sw->ntest, Erv);
-//print("after walkwalks\n");
 
        /*
         * pass 0,1,2,3
@@ -492,32 +417,14 @@ walkswitch(Node *sw)
                return;
        walkcases(sw, sw3, arg);
        convlit(sw->ntest, t);
-//print("after walkcases\n");
 
        /*
         * convert the switch into OIF statements
         */
        exprswitch(sw, arg);
        walkstate(sw->nbody);
-//print("normal done\n");
 }
 
-/*
- * binary search on cases
- */
-enum
-{
-       Ncase   = 4,    // needed to binary search
-};
-
-typedef        struct  Case    Case;
-struct Case
-{
-       Node*   node;           // points at case statement
-       Case*   link;           // linked list to link
-};
-#define        C       ((Case*)nil)
-
 int
 iscaseconst(Node *t)
 {
@@ -662,18 +569,18 @@ constsw(Case *c0, int ncase, Node *name)
        // find center and recur
        c = c0;
        n = ncase>>1;
-       for(i=0; i<n; i++)
+       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+1, name);      // include center
-       a->nelse = constsw(c->link, ncase-n-1, name);   // exclude center
+       a->nbody = constsw(c0, n, name);                // include center
+       a->nelse = constsw(c->link, ncase-n, name);     // exclude center
        return a;
 }
 
 Node*
-binarysw(Node *t, Iter *save, Node *name)
+exprbsw(Node *t, Iter *save, Node *name)
 {
        Case *c, *c1;
        int i, ncase;
@@ -701,6 +608,216 @@ binarysw(Node *t, Iter *save, Node *name)
 
        c = csort(c, casecmp);
        a = constsw(c, ncase, name);
-//dump("bin", a);
        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)
+{
+       Case *c1, *c2;
+       Type *t1, *t2;
+       char buf1[NSYMB], buf2[NSYMB];
+       int ncase;
+
+       ncase = 0;
+       while(c != C) {
+               c->uniq = 1;
+               ncase++;
+
+               for(c1=c->link; c1!=C; c1=c1->link) {
+                       if(c->hash != c1->hash)
+                               break;
+
+                       // 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;
+}
+
+Case*
+nextuniq(Case *c)
+{
+       for(c=c->link; c!=C; c=c->link)
+               if(c->uniq)
+                       return c;
+       return C;
+}
+
+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;
+
+       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);
+
+                               a = nod(OIF, N, N);
+                               a->ntest = boolname;
+                               a->nbody = t->right;            // if bool { goto l }
+                               cmp = list(cmp, a);
+                       }
+                       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;
+               }
+               cas = list(cas, gotodefault);
+               return rev(cas);
+       }
+
+       // find the middle and recur
+       c = c0;
+       n = ncase>>1;
+       for(i=1; i<n; i++)
+               c = nextuniq(c);
+       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);
+       return a;
+}
+
+/*
+ * convert switch of the form
+ *     switch v := i.(type) { case t1: ..; case t2: ..; }
+ * into if statements
+ */
+void
+typeswitch(Node *sw)
+{
+       Iter save;
+       Node *cas;
+       Node *t, *a;
+       Case *c, *c1;
+       int ncase;
+
+       walktype(sw->ntest->right, Erv);
+       if(!istype(sw->ntest->right->type, TINTER)) {
+               yyerror("type switch must be on an interface");
+               return;
+       }
+       walkcases(sw, sw0, Stype);
+       cas = N;
+
+       /*
+        * predeclare temporary variables
+        * and the boolean var
+        */
+       facename = nod(OXXX, N, N);
+       tempname(facename, sw->ntest->right->type);
+       a = nod(OAS, facename, sw->ntest->right);
+       cas = list(cas, a);
+
+       boolname = nod(OXXX, N, N);
+       tempname(boolname, types[TBOOL]);
+
+       hashname = nod(OXXX, N, N);
+       tempname(hashname, types[TUINT32]);
+
+       a = syslook("ifacethash", 1);
+       argtype(a, sw->ntest->right->type);
+       a = nod(OCALL, a, sw->ntest->right);
+       a = nod(OAS, hashname, a);
+       cas = list(cas, a);
+
+       gotodefault = N;
+
+       c = C;
+       t = listfirst(&save, &sw->nbody->left);
+
+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));
+               walkstate(sw->nbody);
+               return;
+       }
+       if(t->left == N) {
+               gotodefault = t->right;
+               t = listnext(&save);
+               goto loop;
+       }
+       if(t->left->op != OTYPESW) {
+               t = listnext(&save);
+               goto loop;
+       }
+
+       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;
+
+       t = listnext(&save);
+       goto loop;
+}
index a2ef1d2592334da03bc314c2b895473c61a0b630..c86a9f528bb5cb403d885441d849ac6a371e41fd 100644 (file)
@@ -36,6 +36,7 @@ func  ifaceI2T2(sigt *byte, iface any) (ret any, ok bool);
 func   ifaceI2I(sigi *byte, iface any) (ret any);
 func   ifaceI2I2(sigi *byte, iface any) (ret any, ok bool);
 func   ifaceeq(i1 any, i2 any) (ret bool);
+func   ifacethash(i1 any) (ret uint32);
 
 func   newmap(keysize int, valsize int,
                        keyalg int, valalg int,
index 5526ca7ecf4bd598b76436eb9e3406895adc9d52..4da62de4f6178d7b8b817ae1618576ad22ceeb57 100644 (file)
@@ -532,6 +532,23 @@ sys·ifaceeq(Iface i1, Iface i2, bool ret)
        FLUSH(&ret);
 }
 
+// ifacethash(i1 any) (ret uint32);
+void
+sys·ifacethash(Iface i1, uint32 ret)
+{
+       Itype *im;
+       Sigt *st;
+
+       ret = 0;
+       im = i1.type;
+       if(im != nil) {
+               st = im->sigt;
+               if(st != nil)
+                       ret = st->thash;
+       }
+       FLUSH(&ret);
+}
+
 void
 sys·printinter(Iface i)
 {