diff options
-rw-r--r-- | src/cmd/6g/obj.c | 8 | ||||
-rw-r--r-- | src/cmd/gc/builtin.c.boot | 1 | ||||
-rw-r--r-- | src/cmd/gc/go.h | 2 | ||||
-rw-r--r-- | src/cmd/gc/go.y | 8 | ||||
-rw-r--r-- | src/cmd/gc/subr.c | 19 | ||||
-rw-r--r-- | src/cmd/gc/swt.c | 345 | ||||
-rw-r--r-- | src/cmd/gc/sys.go | 1 | ||||
-rw-r--r-- | src/runtime/iface.c | 17 |
8 files changed, 273 insertions, 128 deletions
diff --git a/src/cmd/6g/obj.c b/src/cmd/6g/obj.c index 50d7244a4..af375a1d4 100644 --- a/src/cmd/6g/obj.c +++ b/src/cmd/6g/obj.c @@ -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; diff --git a/src/cmd/gc/builtin.c.boot b/src/cmd/gc/builtin.c.boot index 9245936dd..07b0c828f 100644 --- a/src/cmd/gc/builtin.c.boot +++ b/src/cmd/gc/builtin.c.boot @@ -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" diff --git a/src/cmd/gc/go.h b/src/cmd/gc/go.h index 46c99baeb..458a37ad1 100644 --- a/src/cmd/gc/go.h +++ b/src/cmd/gc/go.h @@ -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); diff --git a/src/cmd/gc/go.y b/src/cmd/gc/go.y index fd347177f..233b76cdd 100644 --- a/src/cmd/gc/go.y +++ b/src/cmd/gc/go.y @@ -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); } diff --git a/src/cmd/gc/subr.c b/src/cmd/gc/subr.c index a112849a8..eda84820a 100644 --- a/src/cmd/gc/subr.c +++ b/src/cmd/gc/subr.c @@ -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; diff --git a/src/cmd/gc/swt.c b/src/cmd/gc/swt.c index 408904ff1..e4bd27166 100644 --- a/src/cmd/gc/swt.c +++ b/src/cmd/gc/swt.c @@ -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; +} diff --git a/src/cmd/gc/sys.go b/src/cmd/gc/sys.go index a2ef1d259..c86a9f528 100644 --- a/src/cmd/gc/sys.go +++ b/src/cmd/gc/sys.go @@ -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, diff --git a/src/runtime/iface.c b/src/runtime/iface.c index 5526ca7ec..4da62de4f 100644 --- a/src/runtime/iface.c +++ b/src/runtime/iface.c @@ -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) { |