}
uint32
-typehash(Type *at, int d)
+typehash(Type *at, int addsym, int d)
{
uint32 h;
Type *t;
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:
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;
}
// 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;
}
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;
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
Iter save;
Node *name, *bool, *cas;
Node *t, *a;
-//dump("exprswitch before", sw->nbody->left);
cas = N;
name = N;
loop:
if(t == N) {
sw->nbody->left = rev(cas);
-//dump("exprswitch after", sw->nbody->left);
return;
}
// 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;
}
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
switch(arg) {
default:
// not bool const
- a = binarysw(t, &save, name);
+ a = exprbsw(t, &save, name);
if(a != N)
break;
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.
* init statement is nothing important
*/
walktype(sw->ntest, Erv);
-//print("after walkwalks\n");
/*
* pass 0,1,2,3
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)
{
// 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;
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;
+}