mirror of
https://github.com/golang/go
synced 2024-11-26 03:57:57 -07:00
cmd/compile/internal/gc: handle array slice self-assign in esc.go
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>
This commit is contained in:
parent
2d82465d18
commit
859cf7fc0f
@ -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)
|
||||
|
@ -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$"
|
||||
|
@ -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$"
|
||||
|
Loading…
Reference in New Issue
Block a user