Skip to content

Commit 0bc94a8

Browse files
committed
cmd/compile: when inlining ==, don’t take the address of the values
This CL reworks walkcompare for clarity and concision. It also makes one significant functional change. (The functional change is hard to separate cleanly from the cleanup, so I just did them together.) When inlining and unrolling an equality comparison for a small struct or array, compare the elements like: a[0] == b[0] && a[1] == b[1] rather than pa := &a pb := &b pa[0] == pb[0] && pa[1] == pb[1] The result is the same, but taking the address and working through the indirect forces the backends to generate less efficient code. This is only an improvement with the SSA backend. However, every port but s390x now has a working SSA backend, and switching to the SSA backend by default everywhere is a priority for Go 1.8. It thus seems reasonable to start to prioritize SSA performance over the old backend. Updates #15303 Sample code: type T struct { a, b int8 } func g(a T) bool { return a == T{1, 2} } SSA before: "".g t=1 size=80 args=0x10 locals=0x8 0x0000 00000 (badeq.go:7) TEXT "".g(SB), $8-16 0x0000 00000 (badeq.go:7) SUBQ $8, SP 0x0004 00004 (badeq.go:7) FUNCDATA $0, gclocals·23e8278e2b69a3a75fa59b23c49ed6ad(SB) 0x0004 00004 (badeq.go:7) FUNCDATA $1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB) 0x0004 00004 (badeq.go:8) MOVBLZX "".a+16(FP), AX 0x0009 00009 (badeq.go:8) MOVB AL, "".autotmp_0+6(SP) 0x000d 00013 (badeq.go:8) MOVBLZX "".a+17(FP), AX 0x0012 00018 (badeq.go:8) MOVB AL, "".autotmp_0+7(SP) 0x0016 00022 (badeq.go:8) MOVB $0, "".autotmp_1+4(SP) 0x001b 00027 (badeq.go:8) MOVB $1, "".autotmp_1+4(SP) 0x0020 00032 (badeq.go:8) MOVB $2, "".autotmp_1+5(SP) 0x0025 00037 (badeq.go:8) MOVBLZX "".autotmp_0+6(SP), AX 0x002a 00042 (badeq.go:8) MOVBLZX "".autotmp_1+4(SP), CX 0x002f 00047 (badeq.go:8) CMPB AL, CL 0x0031 00049 (badeq.go:8) JNE 70 0x0033 00051 (badeq.go:8) MOVBLZX "".autotmp_0+7(SP), AX 0x0038 00056 (badeq.go:8) CMPB AL, $2 0x003a 00058 (badeq.go:8) SETEQ AL 0x003d 00061 (badeq.go:8) MOVB AL, "".~r1+24(FP) 0x0041 00065 (badeq.go:8) ADDQ $8, SP 0x0045 00069 (badeq.go:8) RET 0x0046 00070 (badeq.go:8) MOVB $0, AL 0x0048 00072 (badeq.go:8) JMP 61 SSA after: "".g t=1 size=32 args=0x10 locals=0x0 0x0000 00000 (badeq.go:7) TEXT "".g(SB), $0-16 0x0000 00000 (badeq.go:7) NOP 0x0000 00000 (badeq.go:7) NOP 0x0000 00000 (badeq.go:7) FUNCDATA $0, gclocals·23e8278e2b69a3a75fa59b23c49ed6ad(SB) 0x0000 00000 (badeq.go:7) FUNCDATA $1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB) 0x0000 00000 (badeq.go:8) MOVBLZX "".a+8(FP), AX 0x0005 00005 (badeq.go:8) CMPB AL, $1 0x0007 00007 (badeq.go:8) JNE 25 0x0009 00009 (badeq.go:8) MOVBLZX "".a+9(FP), CX 0x000e 00014 (badeq.go:8) CMPB CL, $2 0x0011 00017 (badeq.go:8) SETEQ AL 0x0014 00020 (badeq.go:8) MOVB AL, "".~r1+16(FP) 0x0018 00024 (badeq.go:8) RET 0x0019 00025 (badeq.go:8) MOVB $0, AL 0x001b 00027 (badeq.go:8) JMP 20 Change-Id: I120185d58012b7bbcdb1ec01225b5b08d0855d86 Reviewed-on: https://go-review.googlesource.com/22277 Run-TryBot: Josh Bleecher Snyder <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Matthew Dempsky <[email protected]>
1 parent 157fc45 commit 0bc94a8

File tree

2 files changed

+86
-87
lines changed

2 files changed

+86
-87
lines changed

src/cmd/compile/internal/gc/walk.go

+62-87
Original file line numberDiff line numberDiff line change
@@ -3178,14 +3178,17 @@ func walkcompare(n *Node, init *Nodes) *Node {
31783178

31793179
// Must be comparison of array or struct.
31803180
// Otherwise back end handles it.
3181+
// While we're here, decide whether to
3182+
// inline or call an eq alg.
31813183
t := n.Left.Type
3182-
3184+
var inline bool
31833185
switch t.Etype {
31843186
default:
31853187
return n
3186-
3187-
case TARRAY, TSTRUCT:
3188-
break
3188+
case TARRAY:
3189+
inline = t.NumElem() <= 1 || (t.NumElem() <= 4 && issimple[t.Elem().Etype])
3190+
case TSTRUCT:
3191+
inline = t.NumFields() <= 4
31893192
}
31903193

31913194
cmpl := n.Left
@@ -3201,103 +3204,75 @@ func walkcompare(n *Node, init *Nodes) *Node {
32013204
Fatalf("arguments of comparison must be lvalues - %v %v", cmpl, cmpr)
32023205
}
32033206

3204-
l = temp(Ptrto(t))
3205-
a := Nod(OAS, l, Nod(OADDR, cmpl, nil))
3206-
a.Right.Etype = 1 // addr does not escape
3207-
a = typecheck(a, Etop)
3208-
init.Append(a)
3209-
3210-
r = temp(Ptrto(t))
3211-
a = Nod(OAS, r, Nod(OADDR, cmpr, nil))
3212-
a.Right.Etype = 1 // addr does not escape
3213-
a = typecheck(a, Etop)
3214-
init.Append(a)
3207+
// Chose not to inline. Call equality function directly.
3208+
if !inline {
3209+
// eq algs take pointers
3210+
pl := temp(Ptrto(t))
3211+
al := Nod(OAS, pl, Nod(OADDR, cmpl, nil))
3212+
al.Right.Etype = 1 // addr does not escape
3213+
al = typecheck(al, Etop)
3214+
init.Append(al)
3215+
3216+
pr := temp(Ptrto(t))
3217+
ar := Nod(OAS, pr, Nod(OADDR, cmpr, nil))
3218+
ar.Right.Etype = 1 // addr does not escape
3219+
ar = typecheck(ar, Etop)
3220+
init.Append(ar)
3221+
3222+
var needsize int
3223+
call := Nod(OCALL, eqfor(t, &needsize), nil)
3224+
call.List.Append(pl)
3225+
call.List.Append(pr)
3226+
if needsize != 0 {
3227+
call.List.Append(Nodintconst(t.Width))
3228+
}
3229+
res := call
3230+
if n.Op != OEQ {
3231+
res = Nod(ONOT, res, nil)
3232+
}
3233+
n = finishcompare(n, res, init)
3234+
return n
3235+
}
32153236

3216-
var andor Op = OANDAND
3237+
// inline: build boolean expression comparing element by element
3238+
andor := OANDAND
32173239
if n.Op == ONE {
32183240
andor = OOROR
32193241
}
3220-
32213242
var expr *Node
3222-
if t.Etype == TARRAY && t.NumElem() <= 4 && issimple[t.Elem().Etype] {
3223-
// Four or fewer elements of a basic type.
3224-
// Unroll comparisons.
3225-
var li *Node
3226-
var ri *Node
3227-
for i := 0; int64(i) < t.NumElem(); i++ {
3228-
li = Nod(OINDEX, l, Nodintconst(int64(i)))
3229-
ri = Nod(OINDEX, r, Nodintconst(int64(i)))
3230-
a = Nod(n.Op, li, ri)
3231-
if expr == nil {
3232-
expr = a
3233-
} else {
3234-
expr = Nod(andor, expr, a)
3235-
}
3236-
}
3237-
3243+
compare := func(el, er *Node) {
3244+
a := Nod(n.Op, el, er)
32383245
if expr == nil {
3239-
expr = Nodbool(n.Op == OEQ)
3240-
}
3241-
n = finishcompare(n, expr, init)
3242-
return n
3243-
}
3244-
3245-
if t.Etype == TARRAY {
3246-
// Zero- or single-element array, of any type.
3247-
switch t.NumElem() {
3248-
case 0:
3249-
n = finishcompare(n, Nodbool(n.Op == OEQ), init)
3250-
return n
3251-
case 1:
3252-
l0 := Nod(OINDEX, l, Nodintconst(0))
3253-
r0 := Nod(OINDEX, r, Nodintconst(0))
3254-
a := Nod(n.Op, l0, r0)
3255-
n = finishcompare(n, a, init)
3256-
return n
3246+
expr = a
3247+
} else {
3248+
expr = Nod(andor, expr, a)
32573249
}
32583250
}
3259-
3260-
if t.IsStruct() && t.NumFields() <= 4 {
3261-
// Struct of four or fewer fields.
3262-
// Inline comparisons.
3263-
var li *Node
3264-
var ri *Node
3265-
for _, t1 := range t.Fields().Slice() {
3266-
if isblanksym(t1.Sym) {
3251+
cmpl = safeexpr(cmpl, init)
3252+
cmpr = safeexpr(cmpr, init)
3253+
if t.IsStruct() {
3254+
for _, f := range t.Fields().Slice() {
3255+
sym := f.Sym
3256+
if isblanksym(sym) {
32673257
continue
32683258
}
3269-
li = NodSym(OXDOT, l, t1.Sym)
3270-
ri = NodSym(OXDOT, r, t1.Sym)
3271-
a = Nod(n.Op, li, ri)
3272-
if expr == nil {
3273-
expr = a
3274-
} else {
3275-
expr = Nod(andor, expr, a)
3276-
}
3259+
compare(
3260+
NodSym(OXDOT, cmpl, sym),
3261+
NodSym(OXDOT, cmpr, sym),
3262+
)
32773263
}
3278-
3279-
if expr == nil {
3280-
expr = Nodbool(n.Op == OEQ)
3264+
} else {
3265+
for i := 0; int64(i) < t.NumElem(); i++ {
3266+
compare(
3267+
Nod(OINDEX, cmpl, Nodintconst(int64(i))),
3268+
Nod(OINDEX, cmpr, Nodintconst(int64(i))),
3269+
)
32813270
}
3282-
n = finishcompare(n, expr, init)
3283-
return n
32843271
}
3285-
3286-
// Chose not to inline. Call equality function directly.
3287-
var needsize int
3288-
call := Nod(OCALL, eqfor(t, &needsize), nil)
3289-
3290-
call.List.Append(l)
3291-
call.List.Append(r)
3292-
if needsize != 0 {
3293-
call.List.Append(Nodintconst(t.Width))
3272+
if expr == nil {
3273+
expr = Nodbool(n.Op == OEQ)
32943274
}
3295-
r = call
3296-
if n.Op != OEQ {
3297-
r = Nod(ONOT, r, nil)
3298-
}
3299-
3300-
n = finishcompare(n, r, init)
3275+
n = finishcompare(n, expr, init)
33013276
return n
33023277
}
33033278

test/fixedbugs/issue15303.go

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// run
2+
3+
// Copyright 2016 The Go Authors. All rights reserved.
4+
// Use of this source code is governed by a BSD-style
5+
// license that can be found in the LICENSE file.
6+
7+
// Ensure that inlined struct/array comparisons have the right side-effects.
8+
9+
package main
10+
11+
import "os"
12+
13+
func main() {
14+
var x int
15+
f := func() (r [4]int) {
16+
x++
17+
return
18+
}
19+
_ = f() == f()
20+
if x != 2 {
21+
println("f evaluated ", x, " times, want 2")
22+
os.Exit(1)
23+
}
24+
}

0 commit comments

Comments
 (0)