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
diff --git a/src/cmd/6g/obj.c b/src/cmd/6g/obj.c
index 50d7244..af375a1 100644
--- a/src/cmd/6g/obj.c
+++ b/src/cmd/6g/obj.c
@@ -661,7 +661,7 @@
 	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 @@
 		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 @@
 	// 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 @@
 		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 9245936..07b0c82 100644
--- a/src/cmd/gc/builtin.c.boot
+++ b/src/cmd/gc/builtin.c.boot
@@ -26,6 +26,7 @@
 	"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 46c99ba..458a37a 100644
--- a/src/cmd/gc/go.h
+++ b/src/cmd/gc/go.h
@@ -659,7 +659,7 @@
 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 fd34717..233b76c 100644
--- a/src/cmd/gc/go.y
+++ b/src/cmd/gc/go.y
@@ -484,6 +484,13 @@
 		// 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 @@
 |	LNIL
 	{
 		Val v;
-
 		v.ctype = CTNIL;
 		$$ = nodlit(v);
 	}
diff --git a/src/cmd/gc/subr.c b/src/cmd/gc/subr.c
index a112849..eda8482 100644
--- a/src/cmd/gc/subr.c
+++ b/src/cmd/gc/subr.c
@@ -1919,7 +1919,7 @@
 }
 
 uint32
-typehash(Type *at, int d)
+typehash(Type *at, int addsym, int d)
 {
 	uint32 h;
 	Type *t;
@@ -1931,20 +1931,23 @@
 
 	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 @@
 		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 @@
 	// 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 @@
 
 	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 408904f..e4bd271 100644
--- a/src/cmd/gc/swt.c
+++ b/src/cmd/gc/swt.c
@@ -10,8 +10,22 @@
 	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 @@
 	Iter save;
 	Node *name, *bool, *cas;
 	Node *t, *a;
-//dump("exprswitch before", sw->nbody->left);
 
 	cas = N;
 	name = N;
@@ -280,7 +293,6 @@
 loop:
 	if(t == N) {
 		sw->nbody->left = rev(cas);
-//dump("exprswitch after", sw->nbody->left);
 		return;
 	}
 
@@ -295,7 +307,6 @@
 	// 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 @@
 			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 @@
 	switch(arg) {
 	default:
 		// not bool const
-		a = binarysw(t, &save, name);
+		a = exprbsw(t, &save, name);
 		if(a != N)
 			break;
 
@@ -351,96 +361,12 @@
 	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 @@
 	 * init statement is nothing important
 	 */
 	walktype(sw->ntest, Erv);
-//print("after walkwalks\n");
 
 	/*
 	 * pass 0,1,2,3
@@ -492,32 +417,14 @@
 		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 @@
 	// 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 @@
 
 	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 a2ef1d2..c86a9f5 100644
--- a/src/cmd/gc/sys.go
+++ b/src/cmd/gc/sys.go
@@ -36,6 +36,7 @@
 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 5526ca7..4da62de 100644
--- a/src/runtime/iface.c
+++ b/src/runtime/iface.c
@@ -532,6 +532,23 @@
 	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)
 {