Commit f91cc3bd authored by Russ Cox's avatar Russ Cox

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
parent a7b83f22
...@@ -64,6 +64,9 @@ cgen(Node *n, Node *res) ...@@ -64,6 +64,9 @@ cgen(Node *n, Node *res)
if(isslice(n->left->type)) if(isslice(n->left->type))
n->addable = n->left->addable; n->addable = n->left->addable;
break; break;
case OITAB:
n->addable = n->left->addable;
break;
} }
// if both are addressable, move // if both are addressable, move
...@@ -280,6 +283,20 @@ cgen(Node *n, Node *res) ...@@ -280,6 +283,20 @@ cgen(Node *n, Node *res)
regfree(&n1); regfree(&n1);
break; 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: case OLEN:
if(istype(nl->type, TMAP) || istype(nl->type, TCHAN)) { if(istype(nl->type, TMAP) || istype(nl->type, TCHAN)) {
// map has len in the first 32-bit word. // map has len in the first 32-bit word.
......
...@@ -346,6 +346,8 @@ anyregalloc(void) ...@@ -346,6 +346,8 @@ anyregalloc(void)
return 0; return 0;
} }
uintptr regpc[REGALLOC_RMAX+1];
/* /*
* allocate register of type t, leave in n. * allocate register of type t, leave in n.
* if o != N, o is desired fixed register. * if o != N, o is desired fixed register.
...@@ -389,9 +391,12 @@ regalloc(Node *n, Type *t, Node *o) ...@@ -389,9 +391,12 @@ regalloc(Node *n, Type *t, Node *o)
goto out; goto out;
} }
for(i=REGALLOC_R0; i<=REGALLOC_RMAX; i++) for(i=REGALLOC_R0; i<=REGALLOC_RMAX; i++)
if(reg[i] == 0) if(reg[i] == 0) {
regpc[i] = (uintptr)getcallerpc(&n);
goto out; goto out;
}
for(i=REGALLOC_R0; i<=REGALLOC_RMAX; i++)
print("%d %p\n", i, regpc[i]);
yyerror("out of fixed registers"); yyerror("out of fixed registers");
goto err; goto err;
...@@ -451,6 +456,8 @@ regfree(Node *n) ...@@ -451,6 +456,8 @@ regfree(Node *n)
if(reg[i] <= 0) if(reg[i] <= 0)
fatal("regfree: reg not allocated"); fatal("regfree: reg not allocated");
reg[i]--; reg[i]--;
if(reg[i] == 0)
regpc[i] = 0;
} }
/* /*
...@@ -1347,6 +1354,16 @@ naddr(Node *n, Addr *a, int canemitcode) ...@@ -1347,6 +1354,16 @@ naddr(Node *n, Addr *a, int canemitcode)
} }
break; 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: case OLEN:
// len of string or slice // len of string or slice
naddr(n->left, a, canemitcode); naddr(n->left, a, canemitcode);
......
...@@ -125,6 +125,9 @@ cgen(Node *n, Node *res) ...@@ -125,6 +125,9 @@ cgen(Node *n, Node *res)
if(isslice(n->left->type)) if(isslice(n->left->type))
n->addable = n->left->addable; n->addable = n->left->addable;
break; break;
case OITAB:
n->addable = n->left->addable;
break;
} }
if(complexop(n, res)) { if(complexop(n, res)) {
...@@ -259,6 +262,14 @@ cgen(Node *n, Node *res) ...@@ -259,6 +262,14 @@ cgen(Node *n, Node *res)
gmove(&n1, res); gmove(&n1, res);
regfree(&n1); regfree(&n1);
break; 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: case OLEN:
if(istype(nl->type, TMAP) || istype(nl->type, TCHAN)) { if(istype(nl->type, TMAP) || istype(nl->type, TCHAN)) {
......
...@@ -563,6 +563,7 @@ int ...@@ -563,6 +563,7 @@ int
ismem(Node *n) ismem(Node *n)
{ {
switch(n->op) { switch(n->op) {
case OITAB:
case OLEN: case OLEN:
case OCAP: case OCAP:
case OINDREG: case OINDREG:
...@@ -1219,6 +1220,17 @@ naddr(Node *n, Addr *a, int canemitcode) ...@@ -1219,6 +1220,17 @@ naddr(Node *n, Addr *a, int canemitcode)
break; break;
} }
fatal("naddr: OADDR\n"); 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: case OLEN:
// len of string or slice // len of string or slice
......
...@@ -98,6 +98,9 @@ cgen(Node *n, Node *res) ...@@ -98,6 +98,9 @@ cgen(Node *n, Node *res)
if(isslice(n->left->type)) if(isslice(n->left->type))
n->addable = n->left->addable; n->addable = n->left->addable;
break; break;
case OITAB:
n->addable = n->left->addable;
break;
} }
// if both are addressable, move // if both are addressable, move
...@@ -252,6 +255,13 @@ cgen(Node *n, Node *res) ...@@ -252,6 +255,13 @@ cgen(Node *n, Node *res)
regfree(&n1); regfree(&n1);
break; break;
case OITAB:
igen(nl, &n1, res);
n1.type = ptrto(types[TUINTPTR]);
gmove(&n1, res);
regfree(&n1);
break;
case OLEN: case OLEN:
if(istype(nl->type, TMAP) || istype(nl->type, TCHAN)) { if(istype(nl->type, TMAP) || istype(nl->type, TCHAN)) {
// map has len in the first 32-bit word. // map has len in the first 32-bit word.
......
...@@ -1041,6 +1041,7 @@ int ...@@ -1041,6 +1041,7 @@ int
ismem(Node *n) ismem(Node *n)
{ {
switch(n->op) { switch(n->op) {
case OITAB:
case OLEN: case OLEN:
case OCAP: case OCAP:
case OINDREG: case OINDREG:
...@@ -1926,6 +1927,17 @@ naddr(Node *n, Addr *a, int canemitcode) ...@@ -1926,6 +1927,17 @@ naddr(Node *n, Addr *a, int canemitcode)
break; break;
} }
fatal("naddr: OADDR\n"); 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: case OLEN:
// len of string or slice // len of string or slice
......
...@@ -484,6 +484,7 @@ enum ...@@ -484,6 +484,7 @@ enum
ODDD, ODDD,
ODDDARG, ODDDARG,
OINLCALL, // intermediary representation of an inlined call OINLCALL, // intermediary representation of an inlined call
OITAB, // itable word of interface value
// for back ends // for back ends
OCMP, ODEC, OEXTEND, OINC, OREGISTER, OINDREG, OCMP, ODEC, OEXTEND, OINC, OREGISTER, OINDREG,
......
...@@ -1304,6 +1304,16 @@ reswitch: ...@@ -1304,6 +1304,16 @@ reswitch:
if(n->type == T) if(n->type == T)
goto error; goto error;
goto ret; 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 * statements
......
...@@ -432,6 +432,10 @@ walkexpr(Node **np, NodeList **init) ...@@ -432,6 +432,10 @@ walkexpr(Node **np, NodeList **init)
walkexpr(&n->left, init); walkexpr(&n->left, init);
goto ret; goto ret;
case OITAB:
walkexpr(&n->left, init);
goto ret;
case OLEN: case OLEN:
case OCAP: case OCAP:
walkexpr(&n->left, init); walkexpr(&n->left, init);
...@@ -1176,10 +1180,21 @@ walkexpr(Node **np, NodeList **init) ...@@ -1176,10 +1180,21 @@ walkexpr(Node **np, NodeList **init)
argtype(fn, n->right->type); argtype(fn, n->right->type);
argtype(fn, n->left->type); argtype(fn, n->left->type);
r = mkcall1(fn, n->type, init, n->left, n->right); r = mkcall1(fn, n->type, init, n->left, n->right);
if(n->etype == ONE) { if(n->etype == ONE)
r = nod(ONOT, r, N); 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; n = r;
goto ret; goto ret;
......
// 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")
}
}
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment