From 79648bde2d8c7bb70f4cd4f0dbe5c37450d2d603 Mon Sep 17 00:00:00 2001 From: Josh Bleecher Snyder Date: Fri, 24 Apr 2020 09:49:35 -0700 Subject: [PATCH] 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 TryBot-Result: Gobot Gobot Reviewed-by: Brad Fitzpatrick --- src/cmd/compile/internal/gc/alg.go | 33 +++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/src/cmd/compile/internal/gc/alg.go b/src/cmd/compile/internal/gc/alg.go index 404df12609..fcf14768fb 100644 --- a/src/cmd/compile/internal/gc/alg.go +++ b/src/cmd/compile/internal/gc/alg.go @@ -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) -- 2.50.0