Skip to content

Commit 22c16b4

Browse files
committed
cmd/gc: ignore re-slicing in escape analysis
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 <[email protected]> Reviewed-by: Russ Cox <[email protected]>
1 parent 1b87f01 commit 22c16b4

File tree

3 files changed

+104
-0
lines changed

3 files changed

+104
-0
lines changed

src/cmd/gc/esc.c

+30
Original file line numberDiff line numberDiff line change
@@ -511,6 +511,36 @@ esc(EscState *e, Node *n, Node *up)
511511

512512
case OAS:
513513
case OASOP:
514+
// Filter out the following special case.
515+
//
516+
// func (b *Buffer) Foo() {
517+
// n, m := ...
518+
// b.buf = b.buf[n:m]
519+
// }
520+
//
521+
// This assignment is a no-op for escape analysis,
522+
// it does not store any new pointers into b that were not already there.
523+
// However, without this special case b will escape, because we assign to OIND/ODOTPTR.
524+
if((n->left->op == OIND || n->left->op == ODOTPTR) && n->left->left->op == ONAME && // dst is ONAME dereference
525+
(n->right->op == OSLICE || n->right->op == OSLICE3 || n->right->op == OSLICESTR) && // src is slice operation
526+
(n->right->left->op == OIND || n->right->left->op == ODOTPTR) && n->right->left->left->op == ONAME && // slice is applied to ONAME dereference
527+
n->left->left == n->right->left->left) { // dst and src reference the same base ONAME
528+
// Here we also assume that the statement will not contain calls,
529+
// that is, that order will move any calls to init.
530+
// Otherwise base ONAME value could change between the moments
531+
// when we evaluate it for dst and for src.
532+
//
533+
// Note, this optimization does not apply to OSLICEARR,
534+
// because it does introduce a new pointer into b that was not already there
535+
// (pointer to b itself). After such assignment, if b contents escape,
536+
// b escapes as well. If we ignore such OSLICEARR, we will conclude
537+
// that b does not escape when b contents do.
538+
if(debug['m']) {
539+
warnl(n->lineno, "%S ignoring self-assignment to %hN",
540+
(n->curfn && n->curfn->nname) ? n->curfn->nname->sym : S, n->left);
541+
}
542+
break;
543+
}
514544
escassign(e, n->left, n->right);
515545
break;
516546

test/escape2.go

+37
Original file line numberDiff line numberDiff line change
@@ -1519,3 +1519,40 @@ func ptrlitEscape() {
15191519
x := &Lit{&i} // ERROR "&Lit literal escapes to heap" "&i escapes to heap"
15201520
sink = x
15211521
}
1522+
1523+
// self-assignments
1524+
1525+
type Buffer struct {
1526+
arr [64]byte
1527+
buf1 []byte
1528+
buf2 []byte
1529+
str1 string
1530+
str2 string
1531+
}
1532+
1533+
func (b *Buffer) foo() { // ERROR "b does not escape"
1534+
b.buf1 = b.buf1[1:2] // ERROR "ignoring self-assignment to b.buf1"
1535+
b.buf1 = b.buf1[1:2:3] // ERROR "ignoring self-assignment to b.buf1"
1536+
b.buf1 = b.buf2[1:2] // ERROR "ignoring self-assignment to b.buf1"
1537+
b.buf1 = b.buf2[1:2:3] // ERROR "ignoring self-assignment to b.buf1"
1538+
}
1539+
1540+
func (b *Buffer) bar() { // ERROR "leaking param: b"
1541+
b.buf1 = b.arr[1:2] // ERROR "b.arr escapes to heap"
1542+
}
1543+
1544+
func (b *Buffer) baz() { // ERROR "b does not escape"
1545+
b.str1 = b.str1[1:2] // ERROR "ignoring self-assignment to b.str1"
1546+
b.str1 = b.str2[1:2] // ERROR "ignoring self-assignment to b.str1"
1547+
}
1548+
1549+
func (b *Buffer) bat() { // ERROR "leaking param: b"
1550+
o := new(Buffer) // ERROR "new\(Buffer\) escapes to heap"
1551+
o.buf1 = b.buf1[1:2]
1552+
sink = o
1553+
}
1554+
1555+
func quux(sp *string, bp *[]byte) { // ERROR "sp does not escape" "bp does not escape"
1556+
*sp = (*sp)[1:2] // ERROR "quux ignoring self-assignment to \*sp"
1557+
*bp = (*bp)[1:2] // ERROR "quux ignoring self-assignment to \*bp"
1558+
}

test/escape2n.go

+37
Original file line numberDiff line numberDiff line change
@@ -1519,3 +1519,40 @@ func ptrlitEscape() {
15191519
x := &Lit{&i} // ERROR "&Lit literal escapes to heap" "&i escapes to heap"
15201520
sink = x
15211521
}
1522+
1523+
// self-assignments
1524+
1525+
type Buffer struct {
1526+
arr [64]byte
1527+
buf1 []byte
1528+
buf2 []byte
1529+
str1 string
1530+
str2 string
1531+
}
1532+
1533+
func (b *Buffer) foo() { // ERROR "b does not escape"
1534+
b.buf1 = b.buf1[1:2] // ERROR "ignoring self-assignment to b.buf1"
1535+
b.buf1 = b.buf1[1:2:3] // ERROR "ignoring self-assignment to b.buf1"
1536+
b.buf1 = b.buf2[1:2] // ERROR "ignoring self-assignment to b.buf1"
1537+
b.buf1 = b.buf2[1:2:3] // ERROR "ignoring self-assignment to b.buf1"
1538+
}
1539+
1540+
func (b *Buffer) bar() { // ERROR "leaking param: b"
1541+
b.buf1 = b.arr[1:2] // ERROR "b.arr escapes to heap"
1542+
}
1543+
1544+
func (b *Buffer) baz() { // ERROR "b does not escape"
1545+
b.str1 = b.str1[1:2] // ERROR "ignoring self-assignment to b.str1"
1546+
b.str1 = b.str2[1:2] // ERROR "ignoring self-assignment to b.str1"
1547+
}
1548+
1549+
func (b *Buffer) bat() { // ERROR "leaking param: b"
1550+
o := new(Buffer) // ERROR "new\(Buffer\) escapes to heap"
1551+
o.buf1 = b.buf1[1:2]
1552+
sink = o
1553+
}
1554+
1555+
func quux(sp *string, bp *[]byte) { // ERROR "sp does not escape" "bp does not escape"
1556+
*sp = (*sp)[1:2] // ERROR "quux ignoring self-assignment to \*sp"
1557+
*bp = (*bp)[1:2] // ERROR "quux ignoring self-assignment to \*bp"
1558+
}

0 commit comments

Comments
 (0)