1
0
mirror of https://github.com/golang/go synced 2024-11-22 01:14:40 -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;
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;

View 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"

View 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);

View 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);
}

View 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;

View File

@ -10,8 +10,22 @@ enum
Strue,
Sfalse,
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
@ -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;
}

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 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,

View 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)
{