Commit 3216e0ce authored by Martin Möhrmann's avatar Martin Möhrmann

cmd/compile: replace eqstring with memequal

eqstring is only called for strings with equal lengths.
Instead of pushing a pointer and length for each argument string
on the stack we can omit pushing one of the lengths on the stack.

Changing eqstrings signature to eqstring(*uint8, *uint8, int) bool
to implement the above optimization would make it very similar to the
existing memequal(*any, *any, uintptr) bool function.

Since string lengths are positive we can avoid code redundancy and
use memequal instead of using eqstring with an optimized signature.

go command binary size reduced by 4128 bytes on amd64.

name                          old time/op    new time/op    delta
CompareStringEqual              6.03ns ± 1%    5.71ns ± 1%   -5.23%  (p=0.000 n=19+18)
CompareStringIdentical          2.88ns ± 1%    3.22ns ± 7%  +11.86%  (p=0.000 n=20+20)
CompareStringSameLength         4.31ns ± 1%    4.01ns ± 1%   -7.17%  (p=0.000 n=19+19)
CompareStringDifferentLength    0.29ns ± 2%    0.29ns ± 2%     ~     (p=1.000 n=20+20)
CompareStringBigUnaligned       64.3µs ± 2%    64.1µs ± 3%     ~     (p=0.164 n=20+19)
CompareStringBig                61.9µs ± 1%    61.6µs ± 2%   -0.46%  (p=0.033 n=20+19)

Change-Id: Ice15f3b937c981f0d3bc8479a9ea0d10658ac8df
Reviewed-on: https://go-review.googlesource.com/53650
Run-TryBot: Martin Möhrmann <moehrmann@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: 's avatarKeith Randall <khr@golang.org>
parent d05a1238
This diff is collapsed.
......@@ -48,7 +48,6 @@ func concatstring5(*[32]byte, string, string, string, string, string) string
func concatstrings(*[32]byte, []string) string
func cmpstring(string, string) int
func eqstring(string, string) bool
func intstring(*[4]byte, int64) string
func slicebytetostring(*[32]byte, []byte) string
func slicebytetostringtmp([]byte) string
......
......@@ -1369,18 +1369,27 @@ opswitch:
n.Left = cheapexpr(n.Left, init)
n.Right = cheapexpr(n.Right, init)
r = mkcall("eqstring", types.Types[TBOOL], init, conv(n.Left, types.Types[TSTRING]), conv(n.Right, types.Types[TSTRING]))
// quick check of len before full compare for == or !=
// eqstring assumes that the lengths are equal
lstr := conv(n.Left, types.Types[TSTRING])
rstr := conv(n.Right, types.Types[TSTRING])
lptr := nod(OSPTR, lstr, nil)
rptr := nod(OSPTR, rstr, nil)
llen := conv(nod(OLEN, lstr, nil), types.Types[TUINTPTR])
rlen := conv(nod(OLEN, rstr, nil), types.Types[TUINTPTR])
fn := syslook("memequal")
fn = substArgTypes(fn, types.Types[TUINT8], types.Types[TUINT8])
r = mkcall1(fn, types.Types[TBOOL], init, lptr, rptr, llen)
// quick check of len before full compare for == or !=.
// memequal then tests equality up to length len.
// TODO(marvin): Fix Node.EType type union.
if Op(n.Etype) == OEQ {
// len(left) == len(right) && eqstring(left, right)
r = nod(OANDAND, nod(OEQ, nod(OLEN, n.Left, nil), nod(OLEN, n.Right, nil)), r)
// len(left) == len(right) && memequal(left, right, len)
r = nod(OANDAND, nod(OEQ, llen, rlen), r)
} else {
// len(left) != len(right) || !eqstring(left, right)
// len(left) != len(right) || !memequal(left, right, len)
r = nod(ONOT, r, nil)
r = nod(OOROR, nod(ONE, nod(OLEN, n.Left, nil), nod(OLEN, n.Right, nil)), r)
r = nod(OOROR, nod(ONE, llen, rlen), r)
}
r = typecheck(r, Erv)
......
......@@ -1306,23 +1306,6 @@ eq:
MOVB $1, ret+8(FP)
RET
// eqstring tests whether two strings are equal.
// The compiler guarantees that strings passed
// to eqstring have equal length.
// See runtime_test.go:eqstring_generic for
// equivalent Go code.
TEXT runtime·eqstring(SB),NOSPLIT,$0-17
MOVL s1_base+0(FP), SI
MOVL s2_base+8(FP), DI
CMPL SI, DI
JEQ same
MOVL s1_len+4(FP), BX
LEAL ret+16(FP), AX
JMP runtime·memeqbody(SB)
same:
MOVB $1, ret+16(FP)
RET
TEXT bytes·Equal(SB),NOSPLIT,$0-25
MOVL a_len+4(FP), BX
MOVL b_len+16(FP), CX
......
......@@ -1326,23 +1326,6 @@ eq:
MOVB $1, ret+16(FP)
RET
// eqstring tests whether two strings are equal.
// The compiler guarantees that strings passed
// to eqstring have equal length.
// See runtime_test.go:eqstring_generic for
// equivalent Go code.
TEXT runtime·eqstring(SB),NOSPLIT,$0-33
MOVQ s1_base+0(FP), SI
MOVQ s2_base+16(FP), DI
CMPQ SI, DI
JEQ eq
MOVQ s1_len+8(FP), BX
LEAQ ret+32(FP), AX
JMP runtime·memeqbody(SB)
eq:
MOVB $1, ret+32(FP)
RET
// a in SI
// b in DI
// count in BX
......
......@@ -641,24 +641,6 @@ eq:
MOVB $1, ret+8(FP)
RET
// eqstring tests whether two strings are equal.
// The compiler guarantees that strings passed
// to eqstring have equal length.
// See runtime_test.go:eqstring_generic for
// equivalent Go code.
TEXT runtime·eqstring(SB),NOSPLIT,$0-17
MOVL s1_base+0(FP), SI
MOVL s2_base+8(FP), DI
CMPL SI, DI
JEQ same
MOVL s1_len+4(FP), BX
CALL runtime·memeqbody(SB)
MOVB AX, ret+16(FP)
RET
same:
MOVB $1, ret+16(FP)
RET
// a in SI
// b in DI
// count in BX
......
......@@ -813,31 +813,6 @@ samebytes:
MOVW R0, (R7)
RET
// eqstring tests whether two strings are equal.
// The compiler guarantees that strings passed
// to eqstring have equal length.
// See runtime_test.go:eqstring_generic for
// equivalent Go code.
TEXT runtime·eqstring(SB),NOSPLIT,$-4-17
MOVW s1_base+0(FP), R2
MOVW s2_base+8(FP), R3
MOVW $1, R8
MOVB R8, ret+16(FP)
CMP R2, R3
RET.EQ
MOVW s1_len+4(FP), R0
ADD R2, R0, R6
loop:
CMP R2, R6
RET.EQ
MOVBU.P 1(R2), R4
MOVBU.P 1(R3), R5
CMP R4, R5
BEQ loop
MOVW $0, R8
MOVB R8, ret+16(FP)
RET
// TODO: share code with memequal?
TEXT bytes·Equal(SB),NOSPLIT,$0-25
MOVW a_len+4(FP), R1
......
......@@ -806,31 +806,6 @@ samebytes:
MOVD R4, (R7)
RET
// eqstring tests whether two strings are equal.
// The compiler guarantees that strings passed
// to eqstring have equal length.
// See runtime_test.go:eqstring_generic for
// equivalent Go code.
TEXT runtime·eqstring(SB),NOSPLIT,$0-33
MOVD s1_base+0(FP), R0
MOVD s1_len+8(FP), R1
MOVD s2_base+16(FP), R2
ADD R0, R1 // end
loop:
CMP R0, R1
BEQ equal // reaches the end
MOVBU.P 1(R0), R4
MOVBU.P 1(R2), R5
CMP R4, R5
BEQ loop
notequal:
MOVB ZR, ret+32(FP)
RET
equal:
MOVD $1, R0
MOVB R0, ret+32(FP)
RET
//
// functions for other packages
//
......
......@@ -679,31 +679,6 @@ eq:
MOVB R1, ret+16(FP)
RET
// eqstring tests whether two strings are equal.
// The compiler guarantees that strings passed
// to eqstring have equal length.
// See runtime_test.go:eqstring_generic for
// equivalent Go code.
TEXT runtime·eqstring(SB),NOSPLIT,$0-33
MOVV s1_base+0(FP), R1
MOVV s2_base+16(FP), R2
MOVV $1, R3
MOVB R3, ret+32(FP)
BNE R1, R2, 2(PC)
RET
MOVV s1_len+8(FP), R3
ADDV R1, R3, R4
loop:
BNE R1, R4, 2(PC)
RET
MOVBU (R1), R6
ADDV $1, R1
MOVBU (R2), R7
ADDV $1, R2
BEQ R6, R7, loop
MOVB R0, ret+32(FP)
RET
// TODO: share code with memequal?
TEXT bytes·Equal(SB),NOSPLIT,$0-49
MOVV a_len+8(FP), R3
......
......@@ -695,31 +695,6 @@ eq:
MOVB R1, ret+8(FP)
RET
// eqstring tests whether two strings are equal.
// The compiler guarantees that strings passed
// to eqstring have equal length.
// See runtime_test.go:eqstring_generic for
// equivalent Go code.
TEXT runtime·eqstring(SB),NOSPLIT,$0-17
MOVW s1_base+0(FP), R1
MOVW s2_base+8(FP), R2
MOVW $1, R3
MOVBU R3, ret+16(FP)
BNE R1, R2, 2(PC)
RET
MOVW s1_len+4(FP), R3
ADDU R1, R3, R4
loop:
BNE R1, R4, 2(PC)
RET
MOVBU (R1), R6
ADDU $1, R1
MOVBU (R2), R7
ADDU $1, R2
BEQ R6, R7, loop
MOVB R0, ret+16(FP)
RET
TEXT bytes·Equal(SB),NOSPLIT,$0-25
MOVW a_len+4(FP), R3
MOVW b_len+16(FP), R4
......
......@@ -1057,24 +1057,6 @@ equal:
MOVD $1, R9
RET
// eqstring tests whether two strings are equal.
// The compiler guarantees that strings passed
// to eqstring have equal length.
// See runtime_test.go:eqstring_generic for
// equivalent Go code.
TEXT runtime·eqstring(SB),NOSPLIT,$0-33
MOVD s1_base+0(FP), R3
MOVD s2_base+16(FP), R4
MOVD $1, R5
MOVB R5, ret+32(FP)
CMP R3, R4
BNE 2(PC)
RET
MOVD s1_len+8(FP), R5
BL runtime·memeqbody(SB)
MOVB R9, ret+32(FP)
RET
TEXT bytes·Equal(SB),NOSPLIT,$0-49
MOVD a_len+8(FP), R4
MOVD b_len+32(FP), R5
......
......@@ -704,18 +704,6 @@ TEXT runtime·memequal_varlen(SB),NOSPLIT|NOFRAME,$0-17
LA ret+16(FP), R7
BR runtime·memeqbody(SB)
// eqstring tests whether two strings are equal.
// The compiler guarantees that strings passed
// to eqstring have equal length.
// See runtime_test.go:eqstring_generic for
// equivalent Go code.
TEXT runtime·eqstring(SB),NOSPLIT|NOFRAME,$0-33
MOVD s1_base+0(FP), R3
MOVD s1_len+8(FP), R6
MOVD s2_base+16(FP), R5
LA ret+32(FP), R7
BR runtime·memeqbody(SB)
TEXT bytes·Equal(SB),NOSPLIT|NOFRAME,$0-49
MOVD a_len+8(FP), R2
MOVD b_len+32(FP), R6
......
......@@ -196,9 +196,9 @@ func eqstring_generic(s1, s2 string) bool {
}
func TestEqString(t *testing.T) {
// This isn't really an exhaustive test of eqstring, it's
// This isn't really an exhaustive test of == on strings, it's
// just a convenient way of documenting (via eqstring_generic)
// what eqstring does.
// what == does.
s := []string{
"",
"a",
......@@ -213,7 +213,7 @@ func TestEqString(t *testing.T) {
x := s1 == s2
y := eqstring_generic(s1, s2)
if x != y {
t.Errorf(`eqstring("%s","%s") = %t, want %t`, s1, s2, x, y)
t.Errorf(`("%s" == "%s") = %t, want %t`, s1, s2, x, y)
}
}
}
......
......@@ -300,7 +300,6 @@ func round(n, a uintptr) uintptr {
func checkASM() bool
func memequal_varlen(a, b unsafe.Pointer) bool
func eqstring(s1, s2 string) bool
// bool2int returns 0 if x is false or 1 if x is true.
func bool2int(x bool) int {
......
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