gc: optimize interface ==, !=

If the values being compared have different concrete types,
then they're clearly unequal without needing to invoke the
actual interface compare routine.  This speeds tests for
specific values, like if err == io.EOF, by about 3x.

benchmark                  old ns/op    new ns/op    delta
BenchmarkIfaceCmp100             843          287  -65.95%
BenchmarkIfaceCmpNil100          184          182   -1.09%

Fixes #2591.

R=ken2
CC=golang-dev
https://golang.org/cl/5651073
diff --git a/src/cmd/5g/cgen.c b/src/cmd/5g/cgen.c
index 4912dcd..cccef94 100644
--- a/src/cmd/5g/cgen.c
+++ b/src/cmd/5g/cgen.c
@@ -64,6 +64,9 @@
 		if(isslice(n->left->type))
 			n->addable = n->left->addable;
 		break;
+	case OITAB:
+		n->addable = n->left->addable;
+		break;
 	}
 
 	// if both are addressable, move
@@ -280,6 +283,20 @@
 		regfree(&n1);
 		break;
 
+	case OITAB:
+		// itable of interface value
+		igen(nl, &n1, res);
+		n1.op = OREGISTER;	// was OINDREG
+		regalloc(&n2, n->type, &n1);
+		n1.op = OINDREG;
+		n1.type = n->type;
+		n1.xoffset = 0;
+		gmove(&n1, &n2);
+		gmove(&n2, res);
+		regfree(&n1);
+		regfree(&n2);
+		break;
+
 	case OLEN:
 		if(istype(nl->type, TMAP) || istype(nl->type, TCHAN)) {
 			// map has len in the first 32-bit word.
diff --git a/src/cmd/5g/gsubr.c b/src/cmd/5g/gsubr.c
index d8460ff..94caeb0 100644
--- a/src/cmd/5g/gsubr.c
+++ b/src/cmd/5g/gsubr.c
@@ -346,6 +346,8 @@
 	return 0;
 }
 
+uintptr regpc[REGALLOC_RMAX+1];
+
 /*
  * allocate register of type t, leave in n.
  * if o != N, o is desired fixed register.
@@ -389,9 +391,12 @@
 				goto out;
 		}
 		for(i=REGALLOC_R0; i<=REGALLOC_RMAX; i++)
-			if(reg[i] == 0)
+			if(reg[i] == 0) {
+				regpc[i] = (uintptr)getcallerpc(&n);
 				goto out;
-
+			}
+		for(i=REGALLOC_R0; i<=REGALLOC_RMAX; i++)
+			print("%d %p\n", i, regpc[i]);
 		yyerror("out of fixed registers");
 		goto err;
 
@@ -451,6 +456,8 @@
 	if(reg[i] <= 0)
 		fatal("regfree: reg not allocated");
 	reg[i]--;
+	if(reg[i] == 0)
+		regpc[i] = 0;
 }
 
 /*
@@ -1347,6 +1354,16 @@
 		}
 		break;
 
+	case OITAB:
+		// itable of interface value
+		naddr(n->left, a, canemitcode);
+		a->etype = TINT32;
+		if(a->type == D_CONST && a->offset == 0)
+			break;	// len(nil)
+		if(a->offset >= unmappedzero && a->offset-Array_nel < unmappedzero)
+			checkoffset(a, canemitcode);
+		break;
+
 	case OLEN:
 		// len of string or slice
 		naddr(n->left, a, canemitcode);
diff --git a/src/cmd/6g/cgen.c b/src/cmd/6g/cgen.c
index 2521b02..00334e7 100644
--- a/src/cmd/6g/cgen.c
+++ b/src/cmd/6g/cgen.c
@@ -125,6 +125,9 @@
 		if(isslice(n->left->type))
 			n->addable = n->left->addable;
 		break;
+	case OITAB:
+		n->addable = n->left->addable;
+		break;
 	}
 
 	if(complexop(n, res)) {
@@ -259,6 +262,14 @@
 		gmove(&n1, res);
 		regfree(&n1);
 		break;
+	
+	case OITAB:
+		// interface table is first word of interface value
+		igen(nl, &n1, res);
+		n1.type = n->type;
+		gmove(&n1, res);
+		regfree(&n1);
+		break;
 
 	case OLEN:
 		if(istype(nl->type, TMAP) || istype(nl->type, TCHAN)) {
diff --git a/src/cmd/6g/gsubr.c b/src/cmd/6g/gsubr.c
index 22fea9b..02df69a 100644
--- a/src/cmd/6g/gsubr.c
+++ b/src/cmd/6g/gsubr.c
@@ -563,6 +563,7 @@
 ismem(Node *n)
 {
 	switch(n->op) {
+	case OITAB:
 	case OLEN:
 	case OCAP:
 	case OINDREG:
@@ -1219,6 +1220,17 @@
 				break;
 			}
 		fatal("naddr: OADDR\n");
+	
+	case OITAB:
+		// itable of interface value
+		naddr(n->left, a, canemitcode);
+		if(a->type == D_CONST && a->offset == 0)
+			break;  // itab(nil)
+		a->etype = tptr;
+		a->width = widthptr;
+		if(a->offset >= unmappedzero && a->offset-Array_nel < unmappedzero)
+			checkoffset(a, canemitcode);
+		break;
 
 	case OLEN:
 		// len of string or slice
diff --git a/src/cmd/8g/cgen.c b/src/cmd/8g/cgen.c
index 7dd3a7b..5d8be46 100644
--- a/src/cmd/8g/cgen.c
+++ b/src/cmd/8g/cgen.c
@@ -98,6 +98,9 @@
 		if(isslice(n->left->type))
 			n->addable = n->left->addable;
 		break;
+	case OITAB:
+		n->addable = n->left->addable;
+		break;
 	}
 
 	// if both are addressable, move
@@ -252,6 +255,13 @@
 		regfree(&n1);
 		break;
 
+	case OITAB:
+		igen(nl, &n1, res);
+		n1.type = ptrto(types[TUINTPTR]);
+		gmove(&n1, res);
+		regfree(&n1);
+		break;
+
 	case OLEN:
 		if(istype(nl->type, TMAP) || istype(nl->type, TCHAN)) {
 			// map has len in the first 32-bit word.
diff --git a/src/cmd/8g/gsubr.c b/src/cmd/8g/gsubr.c
index 44dcd50..dd35c51 100644
--- a/src/cmd/8g/gsubr.c
+++ b/src/cmd/8g/gsubr.c
@@ -1041,6 +1041,7 @@
 ismem(Node *n)
 {
 	switch(n->op) {
+	case OITAB:
 	case OLEN:
 	case OCAP:
 	case OINDREG:
@@ -1926,6 +1927,17 @@
 				break;
 			}
 		fatal("naddr: OADDR\n");
+	
+	case OITAB:
+		// itable of interface value
+		naddr(n->left, a, canemitcode);
+		if(a->type == D_CONST && a->offset == 0)
+			break;	// len(nil)
+		a->etype = tptr;
+		a->width = widthptr;
+		if(a->offset >= unmappedzero && a->offset-Array_nel < unmappedzero)
+			checkoffset(a, canemitcode);
+		break;
 
 	case OLEN:
 		// len of string or slice
diff --git a/src/cmd/gc/go.h b/src/cmd/gc/go.h
index 6b0709c..0fde506 100644
--- a/src/cmd/gc/go.h
+++ b/src/cmd/gc/go.h
@@ -484,6 +484,7 @@
 	ODDD,
 	ODDDARG,
 	OINLCALL,	// intermediary representation of an inlined call
+	OITAB,	// itable word of interface value
 
 	// for back ends
 	OCMP, ODEC, OEXTEND, OINC, OREGISTER, OINDREG,
diff --git a/src/cmd/gc/typecheck.c b/src/cmd/gc/typecheck.c
index edd6b72..91f5458 100644
--- a/src/cmd/gc/typecheck.c
+++ b/src/cmd/gc/typecheck.c
@@ -1304,6 +1304,16 @@
 		if(n->type == T)
 			goto error;
 		goto ret;
+	
+	case OITAB:
+		ok |= Erv;
+		typecheck(&n->left, Erv);
+		if((t = n->left->type) == T)
+			goto error;
+		if(t->etype != TINTER)
+			fatal("OITAB of %T", t);
+		n->type = ptrto(types[TUINTPTR]);
+		goto ret;
 
 	/*
 	 * statements
diff --git a/src/cmd/gc/walk.c b/src/cmd/gc/walk.c
index 37691f0..0118c08 100644
--- a/src/cmd/gc/walk.c
+++ b/src/cmd/gc/walk.c
@@ -432,6 +432,10 @@
 		walkexpr(&n->left, init);
 		goto ret;
 
+	case OITAB:
+		walkexpr(&n->left, init);
+		goto ret;
+
 	case OLEN:
 	case OCAP:
 		walkexpr(&n->left, init);
@@ -1176,10 +1180,21 @@
 		argtype(fn, n->right->type);
 		argtype(fn, n->left->type);
 		r = mkcall1(fn, n->type, init, n->left, n->right);
-		if(n->etype == ONE) {
+		if(n->etype == ONE)
 			r = nod(ONOT, r, N);
-			typecheck(&r, Erv);
-		}
+		
+		// check itable/type before full compare.
+		if(n->etype == OEQ)
+			r = nod(OANDAND, nod(OEQ, nod(OITAB, n->left, N), nod(OITAB, n->right, N)), r);
+		else
+			r = nod(OOROR, nod(ONE, nod(OITAB, n->left, N), nod(OITAB, n->right, N)), r);
+		typecheck(&r, Erv);
+		walkexpr(&r, nil);
+
+		n = r;
+		goto ret;
+
+	
 		n = r;
 		goto ret;
 
diff --git a/src/pkg/runtime/runtime_test.go b/src/pkg/runtime/runtime_test.go
new file mode 100644
index 0000000..d68b363
--- /dev/null
+++ b/src/pkg/runtime/runtime_test.go
@@ -0,0 +1,40 @@
+// Copyright 2012 The Go Authors.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package runtime_test
+
+import (
+	"io"
+	"testing"
+)
+
+var errf error
+
+func errfn() error {
+	return errf
+}
+
+func errfn1() error {
+	return io.EOF
+}
+
+func BenchmarkIfaceCmp100(b *testing.B) {
+	for i := 0; i < b.N; i++ {
+		for j := 0; j < 100; j++ {
+			if errfn() == io.EOF {
+				b.Fatal("bad comparison")
+			}
+		}
+	}
+}
+
+func BenchmarkIfaceCmpNil100(b *testing.B) {
+	for i := 0; i < b.N; i++ {
+		for j := 0; j < 100; j++ {
+			if errfn1() == nil {
+				b.Fatal("bad comparison")
+			}
+		}
+	}
+}