1
0
mirror of https://github.com/golang/go synced 2024-11-18 16:04:44 -07:00

cmd/compile: make runtime calls last in eq algs

type T struct {
    f float64
    a [64]uint64
    g float64
}

Prior to this change, the generated equality algorithm for T was:

func eqT(p, q *T) bool {
    return p.f == q.f && runtime.memequal(p.a, q.a, 512) && p.g == q.g
}

In handwritten code, we would normally put the cheapest checks first.
This change takes a step in that direction. We now generate:

func eqT(p, q *T) bool {
    return p.f == q.f && p.g == q.g && runtime.memequal(p.a, q.a, 512)
}

For most types, this also generates considerably shorter code. Examples:

runtime
.eq."".mstats 406 -> 391  (-3.69%)
.eq.""._func 114 -> 101  (-11.40%)
.eq."".itab 115 -> 102  (-11.30%)
.eq."".scase 125 -> 116  (-7.20%)
.eq."".traceStack 119 -> 102  (-14.29%)
.eq."".gcControllerState 169 -> 161  (-4.73%)
.eq."".sweepdata 121 -> 112  (-7.44%)

However, for types in which we make unwise choices about inlining
memory-only comparisons (#38494), this generates longer code.

Example:

cmd/internal/obj
.eq."".objWriter 211 -> 214  (+1.42%)
.eq."".Addr 185 -> 187  (+1.08%)

Fortunately, such cases are not common.

Change-Id: I47a27da93c1f88ec71fa350c192f36b29548a217
Reviewed-on: https://go-review.googlesource.com/c/go/+/230203
Run-TryBot: Josh Bleecher Snyder <josharian@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
This commit is contained in:
Josh Bleecher Snyder 2020-04-24 09:49:35 -07:00
parent 1cf357981e
commit 79648bde2d

View File

@ -8,6 +8,7 @@ import (
"cmd/compile/internal/types"
"cmd/internal/obj"
"fmt"
"sort"
)
// AlgKind describes the kind of algorithms used for comparing and
@ -553,13 +554,15 @@ func geneq(t *types.Type) *obj.LSym {
fn.Nbody.Append(ret)
case TSTRUCT:
var cond *Node
// Build a list of conditions to satisfy.
// Track their order so that we can preserve aspects of that order.
type nodeIdx struct {
n *Node
idx int
}
var conds []nodeIdx
and := func(n *Node) {
if cond == nil {
cond = n
return
}
cond = nod(OANDAND, cond, n)
conds = append(conds, nodeIdx{n: n, idx: len(conds)})
}
// Walk the struct using memequal for runs of AMEM
@ -597,8 +600,24 @@ func geneq(t *types.Type) *obj.LSym {
i = next
}
if cond == nil {
// Sort conditions to put runtime calls last.
// Preserve the rest of the ordering.
sort.SliceStable(conds, func(i, j int) bool {
x, y := conds[i], conds[j]
if (x.n.Op != OCALL) == (y.n.Op != OCALL) {
return x.idx < y.idx
}
return x.n.Op != OCALL
})
var cond *Node
if len(conds) == 0 {
cond = nodbool(true)
} else {
cond = conds[0].n
for _, c := range conds[1:] {
cond = nod(OANDAND, cond, c.n)
}
}
ret := nod(ORETURN, nil, nil)