]> Cypherpunks.ru repositories - gostls13.git/commitdiff
cmd/compile/internal/gc: handle array slice self-assign in esc.go
authorIskander Sharipov <iskander.sharipov@intel.com>
Tue, 4 Sep 2018 20:14:53 +0000 (23:14 +0300)
committerIskander Sharipov <iskander.sharipov@intel.com>
Mon, 17 Sep 2018 11:14:58 +0000 (11:14 +0000)
Instead of skipping all OSLICEARR, skip only ones with non-pointer
array type. For pointers to arrays, it's safe to apply the
self-assignment slicing optimizations.

Refactored the matching code into separate function for readability.

This is an extension to already existing optimization.

On its own, it does not improve any code under std, but
it opens some new optimization opportunities. One
of them is described in the referenced issue.

Updates #7921

Change-Id: I08ac660d3ef80eb15fd7933fb73cf53ded9333ad
Reviewed-on: https://go-review.googlesource.com/133375
Run-TryBot: Iskander Sharipov <iskander.sharipov@intel.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Cherry Zhang <cherryyz@google.com>
src/cmd/compile/internal/gc/esc.go
test/escape2.go
test/escape2n.go

index 254427be4fd92007c98e9f222f93aae086b12d53..cd85a38eb6c42bb04f4aa0d87702823b7371c658 100644 (file)
@@ -654,9 +654,67 @@ func (e *EscState) esclist(l Nodes, parent *Node) {
        }
 }
 
+func (e *EscState) isSliceSelfAssign(dst, src *Node) bool {
+       // Detect the following special case.
+       //
+       //      func (b *Buffer) Foo() {
+       //              n, m := ...
+       //              b.buf = b.buf[n:m]
+       //      }
+       //
+       // This assignment is a no-op for escape analysis,
+       // it does not store any new pointers into b that were not already there.
+       // However, without this special case b will escape, because we assign to OIND/ODOTPTR.
+       // Here we assume that the statement will not contain calls,
+       // that is, that order will move any calls to init.
+       // Otherwise base ONAME value could change between the moments
+       // when we evaluate it for dst and for src.
+
+       // dst is ONAME dereference.
+       if dst.Op != OIND && dst.Op != ODOTPTR || dst.Left.Op != ONAME {
+               return false
+       }
+       // src is a slice operation.
+       switch src.Op {
+       case OSLICE, OSLICE3, OSLICESTR:
+               // OK.
+       case OSLICEARR, OSLICE3ARR:
+               // Since arrays are embedded into containing object,
+               // slice of non-pointer array will introduce a new pointer into b that was not already there
+               // (pointer to b itself). After such assignment, if b contents escape,
+               // b escapes as well. If we ignore such OSLICEARR, we will conclude
+               // that b does not escape when b contents do.
+               //
+               // Pointer to an array is OK since it's not stored inside b directly.
+               // For slicing an array (not pointer to array), there is an implicit OADDR.
+               // We check that to determine non-pointer array slicing.
+               if src.Left.Op == OADDR {
+                       return false
+               }
+       default:
+               return false
+       }
+       // slice is applied to ONAME dereference.
+       if src.Left.Op != OIND && src.Left.Op != ODOTPTR || src.Left.Left.Op != ONAME {
+               return false
+       }
+       // dst and src reference the same base ONAME.
+       return dst.Left == src.Left.Left
+}
+
 // isSelfAssign reports whether assignment from src to dst can
 // be ignored by the escape analysis as it's effectively a self-assignment.
 func (e *EscState) isSelfAssign(dst, src *Node) bool {
+       // Detect trivial assignments that assign back to the same object.
+       //
+       // It covers these cases:
+       //      val.x = val.y
+       //      val.x[i] = val.y[j]
+       //      val.x1.x2 = val.x1.y2
+       //      ... etc
+       //
+       // These assignments do not change assigned object lifetime.
+
        if dst == nil || src == nil || dst.Op != src.Op {
                return false
        }
@@ -830,48 +888,15 @@ opSwitch:
                        }
                }
 
-       // Filter out the following special case.
-       //
-       //      func (b *Buffer) Foo() {
-       //              n, m := ...
-       //              b.buf = b.buf[n:m]
-       //      }
-       //
-       // This assignment is a no-op for escape analysis,
-       // it does not store any new pointers into b that were not already there.
-       // However, without this special case b will escape, because we assign to OIND/ODOTPTR.
        case OAS, OASOP:
-               if (n.Left.Op == OIND || n.Left.Op == ODOTPTR) && n.Left.Left.Op == ONAME && // dst is ONAME dereference
-                       (n.Right.Op == OSLICE || n.Right.Op == OSLICE3 || n.Right.Op == OSLICESTR) && // src is slice operation
-                       (n.Right.Left.Op == OIND || n.Right.Left.Op == ODOTPTR) && n.Right.Left.Left.Op == ONAME && // slice is applied to ONAME dereference
-                       n.Left.Left == n.Right.Left.Left { // dst and src reference the same base ONAME
-
-                       // Here we also assume that the statement will not contain calls,
-                       // that is, that order will move any calls to init.
-                       // Otherwise base ONAME value could change between the moments
-                       // when we evaluate it for dst and for src.
-                       //
-                       // Note, this optimization does not apply to OSLICEARR,
-                       // because it does introduce a new pointer into b that was not already there
-                       // (pointer to b itself). After such assignment, if b contents escape,
-                       // b escapes as well. If we ignore such OSLICEARR, we will conclude
-                       // that b does not escape when b contents do.
+               // Filter out some no-op assignments for escape analysis.
+               if e.isSliceSelfAssign(n.Left, n.Right) {
                        if Debug['m'] != 0 {
                                Warnl(n.Pos, "%v ignoring self-assignment to %S", e.curfnSym(n), n.Left)
                        }
 
                        break
                }
-
-               // Also skip trivial assignments that assign back to the same object.
-               //
-               // It covers these cases:
-               //      val.x = val.y
-               //      val.x[i] = val.y[j]
-               //      val.x1.x2 = val.x1.y2
-               //      ... etc
-               //
-               // These assignments do not change assigned object lifetime.
                if e.isSelfAssign(n.Left, n.Right) {
                        if Debug['m'] != 0 {
                                Warnl(n.Pos, "%v ignoring self-assignment in %S", e.curfnSym(n), n)
index ef3d6a88bf3822977a2a191b8b2a9b0e71be611f..5c4c803249f6de9b14930dadceb13bd0c7f88dd2 100644 (file)
@@ -1593,11 +1593,12 @@ func ptrlitEscape() {
 // self-assignments
 
 type Buffer struct {
-       arr  [64]byte
-       buf1 []byte
-       buf2 []byte
-       str1 string
-       str2 string
+       arr    [64]byte
+       arrPtr *[64]byte
+       buf1   []byte
+       buf2   []byte
+       str1   string
+       str2   string
 }
 
 func (b *Buffer) foo() { // ERROR "\(\*Buffer\).foo b does not escape$"
@@ -1611,6 +1612,11 @@ func (b *Buffer) bar() { // ERROR "leaking param: b$"
        b.buf1 = b.arr[1:2] // ERROR "b.arr escapes to heap$"
 }
 
+func (b *Buffer) arrayPtr() { // ERROR "\(\*Buffer\).arrayPtr b does not escape"
+       b.buf1 = b.arrPtr[1:2]   // ERROR "\(\*Buffer\).arrayPtr ignoring self-assignment to b.buf1"
+       b.buf1 = b.arrPtr[1:2:3] // ERROR "\(\*Buffer\).arrayPtr ignoring self-assignment to b.buf1"
+}
+
 func (b *Buffer) baz() { // ERROR "\(\*Buffer\).baz b does not escape$"
        b.str1 = b.str1[1:2] // ERROR "\(\*Buffer\).baz ignoring self-assignment to b.str1$"
        b.str1 = b.str2[1:2] // ERROR "\(\*Buffer\).baz ignoring self-assignment to b.str1$"
index b1130d3c3c9d3d2fa2e0f2bcbbe55cf270807ca9..4b1ca1eab8b7b7fd2c7d298546e2130857404f6f 100644 (file)
@@ -1593,11 +1593,12 @@ func ptrlitEscape() {
 // self-assignments
 
 type Buffer struct {
-       arr  [64]byte
-       buf1 []byte
-       buf2 []byte
-       str1 string
-       str2 string
+       arr    [64]byte
+       arrPtr *[64]byte
+       buf1   []byte
+       buf2   []byte
+       str1   string
+       str2   string
 }
 
 func (b *Buffer) foo() { // ERROR "\(\*Buffer\).foo b does not escape$"
@@ -1611,6 +1612,11 @@ func (b *Buffer) bar() { // ERROR "leaking param: b$"
        b.buf1 = b.arr[1:2] // ERROR "b.arr escapes to heap$"
 }
 
+func (b *Buffer) arrayPtr() { // ERROR "\(\*Buffer\).arrayPtr b does not escape"
+       b.buf1 = b.arrPtr[1:2]   // ERROR "\(\*Buffer\).arrayPtr ignoring self-assignment to b.buf1"
+       b.buf1 = b.arrPtr[1:2:3] // ERROR "\(\*Buffer\).arrayPtr ignoring self-assignment to b.buf1"
+}
+
 func (b *Buffer) baz() { // ERROR "\(\*Buffer\).baz b does not escape$"
        b.str1 = b.str1[1:2] // ERROR "\(\*Buffer\).baz ignoring self-assignment to b.str1$"
        b.str1 = b.str2[1:2] // ERROR "\(\*Buffer\).baz ignoring self-assignment to b.str1$"