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")
+ }
+ }
+ }
+}