diff --git a/src/cmd/compile/internal/gc/walk.go b/src/cmd/compile/internal/gc/walk.go index 1ba2f7ba4b..8173a2e0cb 100644 --- a/src/cmd/compile/internal/gc/walk.go +++ b/src/cmd/compile/internal/gc/walk.go @@ -3178,14 +3178,17 @@ func walkcompare(n *Node, init *Nodes) *Node { // Must be comparison of array or struct. // Otherwise back end handles it. + // While we're here, decide whether to + // inline or call an eq alg. t := n.Left.Type - + var inline bool switch t.Etype { default: return n - - case TARRAY, TSTRUCT: - break + case TARRAY: + inline = t.NumElem() <= 1 || (t.NumElem() <= 4 && issimple[t.Elem().Etype]) + case TSTRUCT: + inline = t.NumFields() <= 4 } cmpl := n.Left @@ -3201,103 +3204,75 @@ func walkcompare(n *Node, init *Nodes) *Node { Fatalf("arguments of comparison must be lvalues - %v %v", cmpl, cmpr) } - l = temp(Ptrto(t)) - a := Nod(OAS, l, Nod(OADDR, cmpl, nil)) - a.Right.Etype = 1 // addr does not escape - a = typecheck(a, Etop) - init.Append(a) + // Chose not to inline. Call equality function directly. + if !inline { + // eq algs take pointers + pl := temp(Ptrto(t)) + al := Nod(OAS, pl, Nod(OADDR, cmpl, nil)) + al.Right.Etype = 1 // addr does not escape + al = typecheck(al, Etop) + init.Append(al) - r = temp(Ptrto(t)) - a = Nod(OAS, r, Nod(OADDR, cmpr, nil)) - a.Right.Etype = 1 // addr does not escape - a = typecheck(a, Etop) - init.Append(a) + pr := temp(Ptrto(t)) + ar := Nod(OAS, pr, Nod(OADDR, cmpr, nil)) + ar.Right.Etype = 1 // addr does not escape + ar = typecheck(ar, Etop) + init.Append(ar) - var andor Op = OANDAND + var needsize int + call := Nod(OCALL, eqfor(t, &needsize), nil) + call.List.Append(pl) + call.List.Append(pr) + if needsize != 0 { + call.List.Append(Nodintconst(t.Width)) + } + res := call + if n.Op != OEQ { + res = Nod(ONOT, res, nil) + } + n = finishcompare(n, res, init) + return n + } + + // inline: build boolean expression comparing element by element + andor := OANDAND if n.Op == ONE { andor = OOROR } - var expr *Node - if t.Etype == TARRAY && t.NumElem() <= 4 && issimple[t.Elem().Etype] { - // Four or fewer elements of a basic type. - // Unroll comparisons. - var li *Node - var ri *Node - for i := 0; int64(i) < t.NumElem(); i++ { - li = Nod(OINDEX, l, Nodintconst(int64(i))) - ri = Nod(OINDEX, r, Nodintconst(int64(i))) - a = Nod(n.Op, li, ri) - if expr == nil { - expr = a - } else { - expr = Nod(andor, expr, a) - } - } - + compare := func(el, er *Node) { + a := Nod(n.Op, el, er) if expr == nil { - expr = Nodbool(n.Op == OEQ) - } - n = finishcompare(n, expr, init) - return n - } - - if t.Etype == TARRAY { - // Zero- or single-element array, of any type. - switch t.NumElem() { - case 0: - n = finishcompare(n, Nodbool(n.Op == OEQ), init) - return n - case 1: - l0 := Nod(OINDEX, l, Nodintconst(0)) - r0 := Nod(OINDEX, r, Nodintconst(0)) - a := Nod(n.Op, l0, r0) - n = finishcompare(n, a, init) - return n + expr = a + } else { + expr = Nod(andor, expr, a) } } - - if t.IsStruct() && t.NumFields() <= 4 { - // Struct of four or fewer fields. - // Inline comparisons. - var li *Node - var ri *Node - for _, t1 := range t.Fields().Slice() { - if isblanksym(t1.Sym) { + cmpl = safeexpr(cmpl, init) + cmpr = safeexpr(cmpr, init) + if t.IsStruct() { + for _, f := range t.Fields().Slice() { + sym := f.Sym + if isblanksym(sym) { continue } - li = NodSym(OXDOT, l, t1.Sym) - ri = NodSym(OXDOT, r, t1.Sym) - a = Nod(n.Op, li, ri) - if expr == nil { - expr = a - } else { - expr = Nod(andor, expr, a) - } + compare( + NodSym(OXDOT, cmpl, sym), + NodSym(OXDOT, cmpr, sym), + ) } - - if expr == nil { - expr = Nodbool(n.Op == OEQ) + } else { + for i := 0; int64(i) < t.NumElem(); i++ { + compare( + Nod(OINDEX, cmpl, Nodintconst(int64(i))), + Nod(OINDEX, cmpr, Nodintconst(int64(i))), + ) } - n = finishcompare(n, expr, init) - return n } - - // Chose not to inline. Call equality function directly. - var needsize int - call := Nod(OCALL, eqfor(t, &needsize), nil) - - call.List.Append(l) - call.List.Append(r) - if needsize != 0 { - call.List.Append(Nodintconst(t.Width)) + if expr == nil { + expr = Nodbool(n.Op == OEQ) } - r = call - if n.Op != OEQ { - r = Nod(ONOT, r, nil) - } - - n = finishcompare(n, r, init) + n = finishcompare(n, expr, init) return n } diff --git a/test/fixedbugs/issue15303.go b/test/fixedbugs/issue15303.go new file mode 100644 index 0000000000..c8dfa30dfb --- /dev/null +++ b/test/fixedbugs/issue15303.go @@ -0,0 +1,24 @@ +// run + +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Ensure that inlined struct/array comparisons have the right side-effects. + +package main + +import "os" + +func main() { + var x int + f := func() (r [4]int) { + x++ + return + } + _ = f() == f() + if x != 2 { + println("f evaluated ", x, " times, want 2") + os.Exit(1) + } +}