]> Cypherpunks.ru repositories - gostls13.git/commitdiff
cmd/gc: ignore re-slicing in escape analysis
authorDmitry Vyukov <dvyukov@google.com>
Fri, 16 Jan 2015 20:30:35 +0000 (23:30 +0300)
committerDmitry Vyukov <dvyukov@google.com>
Wed, 28 Jan 2015 17:37:55 +0000 (17:37 +0000)
Escape analysis treats everything assigned to OIND/ODOTPTR as escaping.
As the result b escapes in the following code:

func (b *Buffer) Foo() {
n, m := ...
b.buf = b.buf[n:m]
}

This change recognizes such assignments and ignores them.

Update issue #9043.
Update issue #7921.

There are two similar cases in std lib that benefit from this optimization.
First is in archive/zip:

type readBuf []byte
func (b *readBuf) uint32() uint32 {
v := binary.LittleEndian.Uint32(*b)
*b = (*b)[4:]
return v
}

Second is in time:

type data struct {
p     []byte
error bool
}

func (d *data) read(n int) []byte {
if len(d.p) < n {
d.p = nil
d.error = true
return nil
}
p := d.p[0:n]
d.p = d.p[n:]
return p
}

benchmark                         old ns/op     new ns/op     delta
BenchmarkCompressedZipGarbage     32431724      32217851      -0.66%

benchmark                         old allocs     new allocs     delta
BenchmarkCompressedZipGarbage     153            143            -6.54%

Change-Id: Ia6cd32744e02e36d6d8c19f402f8451101711626
Reviewed-on: https://go-review.googlesource.com/3162
Reviewed-by: Keith Randall <khr@golang.org>
Reviewed-by: Russ Cox <rsc@golang.org>
src/cmd/gc/esc.c
test/escape2.go
test/escape2n.go

index 2fdbd998702028c688feef4e8c560ce1e9cbc119..59b00bfa52ee7417ed931662e96f6833dc653d65 100644 (file)
@@ -511,6 +511,36 @@ esc(EscState *e, Node *n, Node *up)
 
        case OAS:
        case OASOP:
+               // 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.
+               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.
+                       if(debug['m']) {
+                               warnl(n->lineno, "%S ignoring self-assignment to %hN",
+                                       (n->curfn && n->curfn->nname) ? n->curfn->nname->sym : S, n->left);
+                       }
+                       break;
+               }
                escassign(e, n->left, n->right);
                break;
 
index 357ce4a8a85c5cd4a3b8da3d00fa31b5f3a294ec..507a815044b8a665973225dbdd7873d55be12369 100644 (file)
@@ -1519,3 +1519,40 @@ func ptrlitEscape() {
        x := &Lit{&i} // ERROR "&Lit literal escapes to heap" "&i escapes to heap"
        sink = x
 }
+
+// self-assignments
+
+type Buffer struct {
+       arr  [64]byte
+       buf1 []byte
+       buf2 []byte
+       str1 string
+       str2 string
+}
+
+func (b *Buffer) foo() { // ERROR "b does not escape"
+       b.buf1 = b.buf1[1:2]   // ERROR "ignoring self-assignment to b.buf1"
+       b.buf1 = b.buf1[1:2:3] // ERROR "ignoring self-assignment to b.buf1"
+       b.buf1 = b.buf2[1:2]   // ERROR "ignoring self-assignment to b.buf1"
+       b.buf1 = b.buf2[1:2:3] // ERROR "ignoring self-assignment to b.buf1"
+}
+
+func (b *Buffer) bar() { // ERROR "leaking param: b"
+       b.buf1 = b.arr[1:2] // ERROR "b.arr escapes to heap"
+}
+
+func (b *Buffer) baz() { // ERROR "b does not escape"
+       b.str1 = b.str1[1:2] // ERROR "ignoring self-assignment to b.str1"
+       b.str1 = b.str2[1:2] // ERROR "ignoring self-assignment to b.str1"
+}
+
+func (b *Buffer) bat() { // ERROR "leaking param: b"
+       o := new(Buffer) // ERROR "new\(Buffer\) escapes to heap"
+       o.buf1 = b.buf1[1:2]
+       sink = o
+}
+
+func quux(sp *string, bp *[]byte) { // ERROR "sp does not escape" "bp does not escape"
+       *sp = (*sp)[1:2] // ERROR "quux ignoring self-assignment to \*sp"
+       *bp = (*bp)[1:2] // ERROR "quux ignoring self-assignment to \*bp"
+}
index 3e9bb709c9a82aad5bd6fc6b6fddc01c51255cd8..e514bde59e89e84b68517e0cc1677454f144fb5f 100644 (file)
@@ -1519,3 +1519,40 @@ func ptrlitEscape() {
        x := &Lit{&i} // ERROR "&Lit literal escapes to heap" "&i escapes to heap"
        sink = x
 }
+
+// self-assignments
+
+type Buffer struct {
+       arr  [64]byte
+       buf1 []byte
+       buf2 []byte
+       str1 string
+       str2 string
+}
+
+func (b *Buffer) foo() { // ERROR "b does not escape"
+       b.buf1 = b.buf1[1:2]   // ERROR "ignoring self-assignment to b.buf1"
+       b.buf1 = b.buf1[1:2:3] // ERROR "ignoring self-assignment to b.buf1"
+       b.buf1 = b.buf2[1:2]   // ERROR "ignoring self-assignment to b.buf1"
+       b.buf1 = b.buf2[1:2:3] // ERROR "ignoring self-assignment to b.buf1"
+}
+
+func (b *Buffer) bar() { // ERROR "leaking param: b"
+       b.buf1 = b.arr[1:2] // ERROR "b.arr escapes to heap"
+}
+
+func (b *Buffer) baz() { // ERROR "b does not escape"
+       b.str1 = b.str1[1:2] // ERROR "ignoring self-assignment to b.str1"
+       b.str1 = b.str2[1:2] // ERROR "ignoring self-assignment to b.str1"
+}
+
+func (b *Buffer) bat() { // ERROR "leaking param: b"
+       o := new(Buffer) // ERROR "new\(Buffer\) escapes to heap"
+       o.buf1 = b.buf1[1:2]
+       sink = o
+}
+
+func quux(sp *string, bp *[]byte) { // ERROR "sp does not escape" "bp does not escape"
+       *sp = (*sp)[1:2] // ERROR "quux ignoring self-assignment to \*sp"
+       *bp = (*bp)[1:2] // ERROR "quux ignoring self-assignment to \*bp"
+}