1
0
mirror of https://github.com/golang/go synced 2024-11-25 13:17:56 -07:00

binary search on type switches.

new feature 'case nil:' in type switch
will match iff the interface is nil.

R=r
OCL=26404
CL=26404
This commit is contained in:
Ken Thompson 2009-03-17 13:58:38 -07:00
parent e9f4fb2839
commit 0f469a99a3
8 changed files with 273 additions and 128 deletions

View File

@ -661,7 +661,7 @@ dumpsigt(Type *progt, Type *ifacet, Type *rcvrt, Type *methodt, Sym *s)
a = nil; a = nil;
o = 0; o = 0;
oldlist = nil; oldlist = nil;
sighash = typehash(progt, 0); sighash = typehash(progt, 1, 0);
for(f=methodt->method; f!=T; f=f->down) { for(f=methodt->method; f!=T; f=f->down) {
if(f->type->etype != TFUNC) if(f->type->etype != TFUNC)
continue; continue;
@ -678,7 +678,7 @@ dumpsigt(Type *progt, Type *ifacet, Type *rcvrt, Type *methodt, Sym *s)
a = b; a = b;
a->name = method->name; 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)) if(!exportname(a->name))
a->hash += PRIME10*stringhash(package); a->hash += PRIME10*stringhash(package);
a->perm = o; a->perm = o;
@ -735,7 +735,7 @@ dumpsigt(Type *progt, Type *ifacet, Type *rcvrt, Type *methodt, Sym *s)
// base of type signature contains parameters // base of type signature contains parameters
ginsatoa(widthptr, stringo); // name ginsatoa(widthptr, stringo); // name
ot = rnd(ot, widthptr)+widthptr; // skip link 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(wi, sighash); // mhash
gensatac(ws, progt->width); // width gensatac(ws, progt->width); // width
gensatac(ws, algtype(progt)); // algorithm gensatac(ws, algtype(progt)); // algorithm
@ -815,7 +815,7 @@ dumpsigi(Type *t, Sym *s)
a = b; a = b;
a->name = s1->name; 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)) if(!exportname(a->name))
a->hash += PRIME10*stringhash(package); a->hash += PRIME10*stringhash(package);
a->perm = o; a->perm = o;

View File

@ -26,6 +26,7 @@ char *sysimport =
"func sys.ifaceI2I (sigi *uint8, iface any) (ret any)\n" "func sys.ifaceI2I (sigi *uint8, iface any) (ret any)\n"
"func sys.ifaceI2I2 (sigi *uint8, iface any) (ret any, ok bool)\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.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.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.mapaccess1 (hmap map[any] any, key any) (val any)\n"
"func sys.mapaccess2 (hmap map[any] any, key any) (val any, pres bool)\n" "func sys.mapaccess2 (hmap map[any] any, key any) (val any, pres bool)\n"

View File

@ -659,7 +659,7 @@ int eqtype(Type*, Type*, int);
int eqtypenoname(Type*, Type*); int eqtypenoname(Type*, Type*);
void argtype(Node*, Type*); void argtype(Node*, Type*);
int eqargs(Type*, Type*); int eqargs(Type*, Type*);
uint32 typehash(Type*, int); uint32 typehash(Type*, int, int);
void frame(int); void frame(int);
Node* dobad(void); Node* dobad(void);
Node* nodintconst(int64); Node* nodintconst(int64);

View File

@ -484,6 +484,13 @@ complex_stmt:
// right will point to next case // right will point to next case
// done in casebody() // done in casebody()
poptodcl(); 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); $$ = nod(OXCASE, $2, N);
} }
| LCASE name '=' expr ':' | LCASE name '=' expr ':'
@ -821,7 +828,6 @@ pexpr:
| LNIL | LNIL
{ {
Val v; Val v;
v.ctype = CTNIL; v.ctype = CTNIL;
$$ = nodlit(v); $$ = nodlit(v);
} }

View File

@ -1919,7 +1919,7 @@ eqargs(Type *t1, Type *t2)
} }
uint32 uint32
typehash(Type *at, int d) typehash(Type *at, int addsym, int d)
{ {
uint32 h; uint32 h;
Type *t; Type *t;
@ -1931,20 +1931,23 @@ typehash(Type *at, int d)
h = at->etype*PRIME4; h = at->etype*PRIME4;
if(addsym && at->sym != S)
h += stringhash(at->sym->name);
switch(at->etype) { switch(at->etype) {
default: default:
h += PRIME5 * typehash(at->type, d+1); h += PRIME5 * typehash(at->type, addsym, d+1);
break; break;
case TINTER: case TINTER:
// botch -- should be sorted? // botch -- should be sorted?
for(t=at->type; t!=T; t=t->down) for(t=at->type; t!=T; t=t->down)
h += PRIME6 * typehash(t, d+1); h += PRIME6 * typehash(t, addsym, d+1);
break; break;
case TSTRUCT: case TSTRUCT:
for(t=at->type; t!=T; t=t->down) for(t=at->type; t!=T; t=t->down)
h += PRIME7 * typehash(t, d+1); h += PRIME7 * typehash(t, addsym, d+1);
break; break;
case TFUNC: case TFUNC:
@ -1953,7 +1956,7 @@ typehash(Type *at, int d)
if(t != T) if(t != T)
t = t->down; t = t->down;
for(; 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; break;
} }
@ -2756,9 +2759,9 @@ ifaceokT2I(Type *t0, Type *iface, Type **m)
// so we can both be wrong together. // so we can both be wrong together.
for(im=iface->type; im; im=im->down) { for(im=iface->type; im; im=im->down) {
imhash = typehash(im, 0); imhash = typehash(im, 0, 0);
tm = ifacelookdot(im->sym, t); tm = ifacelookdot(im->sym, t);
if(tm == T || typehash(tm, 0) != imhash) { if(tm == T || typehash(tm, 0, 0) != imhash) {
*m = im; *m = im;
return 0; return 0;
} }
@ -2778,7 +2781,7 @@ ifaceokI2I(Type *i1, Type *i2, Type **m)
for(m2=i2->type; m2; m2=m2->down) { for(m2=i2->type; m2; m2=m2->down) {
for(m1=i1->type; m1; m1=m1->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; goto found;
*m = m2; *m = m2;
return 0; return 0;

View File

@ -10,8 +10,22 @@ enum
Strue, Strue,
Sfalse, Sfalse,
Stype, Stype,
Ncase = 4, // needed to binary search
}; };
Node* binarysw(Node *t, Iter *save, Node *name); 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
};
#define C ((Case*)nil)
/* /*
* walktype * walktype
@ -263,7 +277,6 @@ exprswitch(Node *sw, int arg)
Iter save; Iter save;
Node *name, *bool, *cas; Node *name, *bool, *cas;
Node *t, *a; Node *t, *a;
//dump("exprswitch before", sw->nbody->left);
cas = N; cas = N;
name = N; name = N;
@ -280,7 +293,6 @@ exprswitch(Node *sw, int arg)
loop: loop:
if(t == N) { if(t == N) {
sw->nbody->left = rev(cas); sw->nbody->left = rev(cas);
//dump("exprswitch after", sw->nbody->left);
return; return;
} }
@ -295,7 +307,6 @@ loop:
// this should be done better to prevent // this should be done better to prevent
// multiple (unused) heap allocations per switch. // multiple (unused) heap allocations per switch.
if(t->ninit != N && t->ninit->op == ODCL) { if(t->ninit != N && t->ninit->op == ODCL) {
//dump("exprswitch case init", t->ninit);
cas = list(cas, t->ninit); cas = list(cas, t->ninit);
t->ninit = N; t->ninit = N;
} }
@ -305,7 +316,6 @@ loop:
bool = nod(OXXX, N, N); bool = nod(OXXX, N, N);
tempname(bool, types[TBOOL]); tempname(bool, types[TBOOL]);
} }
//dump("oas", t);
t->left->left = nod(OLIST, t->left->left, bool); t->left->left = nod(OLIST, t->left->left, bool);
cas = list(cas, t->left); // v,bool = rhs cas = list(cas, t->left); // v,bool = rhs
@ -324,7 +334,7 @@ loop:
switch(arg) { switch(arg) {
default: default:
// not bool const // not bool const
a = binarysw(t, &save, name); a = exprbsw(t, &save, name);
if(a != N) if(a != N)
break; break;
@ -351,96 +361,12 @@ loop:
goto 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 void
walkswitch(Node *sw) walkswitch(Node *sw)
{ {
Type *t; Type *t;
int arg; int arg;
//dump("walkswitch", sw);
/* /*
* reorder the body into (OLIST, cases, statements) * reorder the body into (OLIST, cases, statements)
* cases have OGOTO into statements. * cases have OGOTO into statements.
@ -476,7 +402,6 @@ walkswitch(Node *sw)
* init statement is nothing important * init statement is nothing important
*/ */
walktype(sw->ntest, Erv); walktype(sw->ntest, Erv);
//print("after walkwalks\n");
/* /*
* pass 0,1,2,3 * pass 0,1,2,3
@ -492,32 +417,14 @@ walkswitch(Node *sw)
return; return;
walkcases(sw, sw3, arg); walkcases(sw, sw3, arg);
convlit(sw->ntest, t); convlit(sw->ntest, t);
//print("after walkcases\n");
/* /*
* convert the switch into OIF statements * convert the switch into OIF statements
*/ */
exprswitch(sw, arg); exprswitch(sw, arg);
walkstate(sw->nbody); 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 int
iscaseconst(Node *t) iscaseconst(Node *t)
{ {
@ -662,18 +569,18 @@ constsw(Case *c0, int ncase, Node *name)
// find center and recur // find center and recur
c = c0; c = c0;
n = ncase>>1; n = ncase>>1;
for(i=0; i<n; i++) for(i=1; i<n; i++)
c = c->link; c = c->link;
a = nod(OIF, N, N); a = nod(OIF, N, N);
a->ntest = nod(OLE, name, c->node->left); a->ntest = nod(OLE, name, c->node->left);
a->nbody = constsw(c0, n+1, name); // include center a->nbody = constsw(c0, n, name); // include center
a->nelse = constsw(c->link, ncase-n-1, name); // exclude center a->nelse = constsw(c->link, ncase-n, name); // exclude center
return a; return a;
} }
Node* Node*
binarysw(Node *t, Iter *save, Node *name) exprbsw(Node *t, Iter *save, Node *name)
{ {
Case *c, *c1; Case *c, *c1;
int i, ncase; int i, ncase;
@ -701,6 +608,216 @@ binarysw(Node *t, Iter *save, Node *name)
c = csort(c, casecmp); c = csort(c, casecmp);
a = constsw(c, ncase, name); a = constsw(c, ncase, name);
//dump("bin", a);
return 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;
}

View File

@ -36,6 +36,7 @@ func ifaceI2T2(sigt *byte, iface any) (ret any, ok bool);
func ifaceI2I(sigi *byte, iface any) (ret any); func ifaceI2I(sigi *byte, iface any) (ret any);
func ifaceI2I2(sigi *byte, iface any) (ret any, ok bool); func ifaceI2I2(sigi *byte, iface any) (ret any, ok bool);
func ifaceeq(i1 any, i2 any) (ret bool); func ifaceeq(i1 any, i2 any) (ret bool);
func ifacethash(i1 any) (ret uint32);
func newmap(keysize int, valsize int, func newmap(keysize int, valsize int,
keyalg int, valalg int, keyalg int, valalg int,

View File

@ -532,6 +532,23 @@ sys·ifaceeq(Iface i1, Iface i2, bool ret)
FLUSH(&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 void
sys·printinter(Iface i) sys·printinter(Iface i)
{ {