]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/compile: eliminate some array equality alg loops
authorJosh Bleecher Snyder <josharian@gmail.com>
Sun, 26 Apr 2020 01:14:42 +0000 (18:14 -0700)
committerJosh Bleecher Snyder <josharian@gmail.com>
Mon, 27 Apr 2020 17:34:32 +0000 (17:34 +0000)
type T [3]string

Prior to this change, we generated this equality alg for T:

func eqT(p, q *T) (r bool) {
    for i := range *p {
        if len(p[i]) == len(q[i]) {
        } else {
            return
        }
    }
    for j := range *p {
        if runtime.memeq(p[j].ptr, q[j].ptr, len(p[j])) {
        } else {
            return
        }
    }
    return true
}

That first loop can be profitably eliminated;
it's cheaper to spell out 3 length equality checks.

We now generate:

func eqT(p, q *T) (r bool) {
    if len(p[0]) == len(q[0]) &&
        len(p[1]) == len(q[1]) &&
        len(p[2]) == len(q[2]) {
    } else {
        return
    }
    for i := 0; i < len(p); i++ {
        if runtime.memeq(p[j].ptr, q[j].ptr, len(p[j])) {
        } else {
            return
        }
    }
    return true
}

We now also eliminate loops for small float arrays as well,
and for any array of size 1.

These cutoffs were selected to minimize code size on amd64
at this moment, for lack of a more compelling methodology.
Any smallish number would do.

The switch from range loops to plain for loops allowed me
to use a temp instead of a named var, which eliminated
a pointless argument to checkAll.
The code to construct them is also a bit clearer, in my opinion.

Change-Id: I1bdd8ee4a2739d00806e66b17a4e76b46e71231a
Reviewed-on: https://go-review.googlesource.com/c/go/+/230210
Run-TryBot: Josh Bleecher Snyder <josharian@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
src/cmd/compile/internal/gc/alg.go

index bda9ab5bff7c53789616eb65ce241736257cc7ab..835e7e73ba33efcf7559d4db0593d42d460337f6 100644 (file)
@@ -519,40 +519,75 @@ func geneq(t *types.Type) *obj.LSym {
                Fatalf("geneq %v", t)
 
        case TARRAY:
-               // rangedCheck generates:
+               nelem := t.NumElem()
+
+               // checkAll generates code to check the equality of all array elements.
+               // If unroll is greater than nelem, checkAll generates:
+               //
+               // if eq(p[0], q[0]) && eq(p[1], q[1]) && ... {
+               // } else {
+               //   return
+               // }
+               //
+               // And so on.
                //
-               // for idx := range *p {
+               // Otherwise it generates:
+               //
+               // for i := 0; i < nelem; i++ {
                //   if eq(p[i], q[i]) {
                //   } else {
                //     return
                //   }
                // }
-               rangedCheck := func(idx string, eq func(pi, qi *Node) *Node) {
-                       // for idx := range *p
-                       nrange := nod(ORANGE, nil, nod(ODEREF, np, nil))
-                       ni := newname(lookup(idx))
-                       ni.Type = types.Types[TINT]
-                       nrange.List.Set1(ni)
-                       nrange.SetColas(true)
-                       colasdefn(nrange.List.Slice(), nrange)
-                       ni = nrange.List.First()
-
-                       // pi := p[i]
-                       pi := nod(OINDEX, np, ni)
-                       pi.SetBounded(true)
-                       pi.Type = t.Elem()
-                       // qi := q[i]
-                       qi := nod(OINDEX, nq, ni)
-                       qi.SetBounded(true)
-                       qi.Type = t.Elem()
+               //
+               // TODO(josharian): consider doing some loop unrolling
+               // for larger nelem as well, processing a few elements at a time in a loop.
+               checkAll := func(unroll int64, eq func(pi, qi *Node) *Node) {
+                       // checkIdx generates a node to check for equality at index i.
+                       checkIdx := func(i *Node) *Node {
+                               // pi := p[i]
+                               pi := nod(OINDEX, np, i)
+                               pi.SetBounded(true)
+                               pi.Type = t.Elem()
+                               // qi := q[i]
+                               qi := nod(OINDEX, nq, i)
+                               qi.SetBounded(true)
+                               qi.Type = t.Elem()
+                               return eq(pi, qi)
+                       }
 
+                       if nelem <= unroll {
+                               // Generate a series of checks.
+                               var cond *Node
+                               for i := int64(0); i < nelem; i++ {
+                                       c := nodintconst(i)
+                                       check := checkIdx(c)
+                                       if cond == nil {
+                                               cond = check
+                                               continue
+                                       }
+                                       cond = nod(OANDAND, cond, check)
+                               }
+                               nif := nod(OIF, cond, nil)
+                               nif.Rlist.Append(nod(ORETURN, nil, nil))
+                               fn.Nbody.Append(nif)
+                               return
+                       }
+
+                       // Generate a for loop.
+                       // for i := 0; i < nelem; i++
+                       i := temp(types.Types[TINT])
+                       init := nod(OAS, i, nodintconst(0))
+                       cond := nod(OLT, i, nodintconst(nelem))
+                       post := nod(OAS, i, nod(OADD, i, nodintconst(1)))
+                       loop := nod(OFOR, cond, post)
+                       loop.Ninit.Append(init)
                        // if eq(pi, qi) {} else { return }
-                       cmp := eq(pi, qi)
-                       nif := nod(OIF, cmp, nil)
-                       ret := nod(ORETURN, nil, nil)
-                       nif.Rlist.Append(ret)
-                       nrange.Nbody.Append(nif)
-                       fn.Nbody.Append(nrange)
+                       check := checkIdx(i)
+                       nif := nod(OIF, check, nil)
+                       nif.Rlist.Append(nod(ORETURN, nil, nil))
+                       loop.Nbody.Append(nif)
+                       fn.Nbody.Append(loop)
                }
 
                switch t.Elem().Etype {
@@ -560,14 +595,14 @@ func geneq(t *types.Type) *obj.LSym {
                        // Do two loops. First, check that all the types match (cheap).
                        // Second, check that all the data match (expensive).
                        // TODO: when the array size is small, unroll the tab match checks.
-                       rangedCheck("i", func(pi, qi *Node) *Node {
+                       checkAll(3, func(pi, qi *Node) *Node {
                                // Compare types.
                                pi = typecheck(pi, ctxExpr)
                                qi = typecheck(qi, ctxExpr)
                                eqtab, _ := eqinterface(pi, qi)
                                return eqtab
                        })
-                       rangedCheck("j", func(pi, qi *Node) *Node {
+                       checkAll(1, func(pi, qi *Node) *Node {
                                // Compare data.
                                pi = typecheck(pi, ctxExpr)
                                qi = typecheck(qi, ctxExpr)
@@ -578,23 +613,24 @@ func geneq(t *types.Type) *obj.LSym {
                        // Do two loops. First, check that all the lengths match (cheap).
                        // Second, check that all the contents match (expensive).
                        // TODO: when the array size is small, unroll the length match checks.
-                       rangedCheck("i", func(pi, qi *Node) *Node {
+                       checkAll(3, func(pi, qi *Node) *Node {
                                // Compare lengths.
                                eqlen, _ := eqstring(pi, qi)
                                return eqlen
                        })
-                       rangedCheck("j", func(pi, qi *Node) *Node {
+                       checkAll(1, func(pi, qi *Node) *Node {
                                // Compare contents.
                                _, eqmem := eqstring(pi, qi)
                                return eqmem
                        })
+               case TFLOAT32, TFLOAT64:
+                       checkAll(2, func(pi, qi *Node) *Node {
+                               // p[i] == q[i]
+                               return nod(OEQ, pi, qi)
+                       })
+               // TODO: pick apart structs, do them piecemeal too
                default:
-                       // An array of pure memory would be handled by the standard memequal,
-                       // so the element type must not be pure memory.
-                       // Loop over each element, checking for equality.
-                       // TODO(josharian): For element types that don't involve
-                       // a function call, such as floats, unroll when array size is small.
-                       rangedCheck("i", func(pi, qi *Node) *Node {
+                       checkAll(1, func(pi, qi *Node) *Node {
                                // p[i] == q[i]
                                return nod(OEQ, pi, qi)
                        })