]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/compile: make runtime calls last in eq algs
authorJosh Bleecher Snyder <josharian@gmail.com>
Fri, 24 Apr 2020 16:49:35 +0000 (09:49 -0700)
committerJosh Bleecher Snyder <josharian@gmail.com>
Mon, 27 Apr 2020 17:19:01 +0000 (17:19 +0000)
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>
src/cmd/compile/internal/gc/alg.go

index 404df12609f8d351985d4d4715d54f9cbe7aac25..fcf14768fbdc222c13a00905ecf1886dc506f0bb 100644 (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)