mirror of
https://github.com/golang/go
synced 2024-11-17 16:24:42 -07:00
cmd/compile: add new escape analysis implementation
This CL adds a new escape analysis implementation, which can be enabled through the -newescape compiler flag. This implementation focuses on simplicity, but in the process ends up using less memory, speeding up some compile-times, fixing memory corruption issues, and overall significantly improving escape analysis results. Updates #23109. Change-Id: I6176d9a7ae9d80adb0208d4112b8a1e1f4c9143a Reviewed-on: https://go-review.googlesource.com/c/go/+/170322 Run-TryBot: Matthew Dempsky <mdempsky@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: David Chase <drchase@google.com>
This commit is contained in:
parent
d91f7e6637
commit
97c4ad4327
@ -41,8 +41,16 @@ import (
|
|||||||
// not escape, then new(T) can be rewritten into a stack allocation.
|
// not escape, then new(T) can be rewritten into a stack allocation.
|
||||||
// The same is true of slice literals.
|
// The same is true of slice literals.
|
||||||
|
|
||||||
|
// If newescape is true, then escape.go drives escape analysis instead
|
||||||
|
// of esc.go.
|
||||||
|
var newescape bool
|
||||||
|
|
||||||
func escapes(all []*Node) {
|
func escapes(all []*Node) {
|
||||||
visitBottomUp(all, escAnalyze)
|
esc := escAnalyze
|
||||||
|
if newescape {
|
||||||
|
esc = escapeFuncs
|
||||||
|
}
|
||||||
|
visitBottomUp(all, esc)
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -393,7 +401,7 @@ func escAnalyze(all []*Node, recursive bool) {
|
|||||||
// for all top level functions, tag the typenodes corresponding to the param nodes
|
// for all top level functions, tag the typenodes corresponding to the param nodes
|
||||||
for _, n := range all {
|
for _, n := range all {
|
||||||
if n.Op == ODCLFUNC {
|
if n.Op == ODCLFUNC {
|
||||||
e.esctag(n)
|
esctag(n)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -516,7 +524,7 @@ func (e *EscState) esclist(l Nodes, parent *Node) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *EscState) isSliceSelfAssign(dst, src *Node) bool {
|
func isSliceSelfAssign(dst, src *Node) bool {
|
||||||
// Detect the following special case.
|
// Detect the following special case.
|
||||||
//
|
//
|
||||||
// func (b *Buffer) Foo() {
|
// func (b *Buffer) Foo() {
|
||||||
@ -566,8 +574,8 @@ func (e *EscState) isSliceSelfAssign(dst, src *Node) bool {
|
|||||||
|
|
||||||
// isSelfAssign reports whether assignment from src to dst can
|
// isSelfAssign reports whether assignment from src to dst can
|
||||||
// be ignored by the escape analysis as it's effectively a self-assignment.
|
// be ignored by the escape analysis as it's effectively a self-assignment.
|
||||||
func (e *EscState) isSelfAssign(dst, src *Node) bool {
|
func isSelfAssign(dst, src *Node) bool {
|
||||||
if e.isSliceSelfAssign(dst, src) {
|
if isSliceSelfAssign(dst, src) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -589,7 +597,7 @@ func (e *EscState) isSelfAssign(dst, src *Node) bool {
|
|||||||
case ODOT, ODOTPTR:
|
case ODOT, ODOTPTR:
|
||||||
// Safe trailing accessors that are permitted to differ.
|
// Safe trailing accessors that are permitted to differ.
|
||||||
case OINDEX:
|
case OINDEX:
|
||||||
if e.mayAffectMemory(dst.Right) || e.mayAffectMemory(src.Right) {
|
if mayAffectMemory(dst.Right) || mayAffectMemory(src.Right) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
@ -602,7 +610,7 @@ func (e *EscState) isSelfAssign(dst, src *Node) bool {
|
|||||||
|
|
||||||
// mayAffectMemory reports whether n evaluation may affect program memory state.
|
// mayAffectMemory reports whether n evaluation may affect program memory state.
|
||||||
// If expression can't affect it, then it can be safely ignored by the escape analysis.
|
// If expression can't affect it, then it can be safely ignored by the escape analysis.
|
||||||
func (e *EscState) mayAffectMemory(n *Node) bool {
|
func mayAffectMemory(n *Node) bool {
|
||||||
// We may want to use "memory safe" black list instead of general
|
// We may want to use "memory safe" black list instead of general
|
||||||
// "side-effect free", which can include all calls and other ops
|
// "side-effect free", which can include all calls and other ops
|
||||||
// that can affect allocate or change global state.
|
// that can affect allocate or change global state.
|
||||||
@ -616,18 +624,26 @@ func (e *EscState) mayAffectMemory(n *Node) bool {
|
|||||||
|
|
||||||
// Left+Right group.
|
// Left+Right group.
|
||||||
case OINDEX, OADD, OSUB, OOR, OXOR, OMUL, OLSH, ORSH, OAND, OANDNOT, ODIV, OMOD:
|
case OINDEX, OADD, OSUB, OOR, OXOR, OMUL, OLSH, ORSH, OAND, OANDNOT, ODIV, OMOD:
|
||||||
return e.mayAffectMemory(n.Left) || e.mayAffectMemory(n.Right)
|
return mayAffectMemory(n.Left) || mayAffectMemory(n.Right)
|
||||||
|
|
||||||
// Left group.
|
// Left group.
|
||||||
case ODOT, ODOTPTR, ODEREF, OCONVNOP, OCONV, OLEN, OCAP,
|
case ODOT, ODOTPTR, ODEREF, OCONVNOP, OCONV, OLEN, OCAP,
|
||||||
ONOT, OBITNOT, OPLUS, ONEG, OALIGNOF, OOFFSETOF, OSIZEOF:
|
ONOT, OBITNOT, OPLUS, ONEG, OALIGNOF, OOFFSETOF, OSIZEOF:
|
||||||
return e.mayAffectMemory(n.Left)
|
return mayAffectMemory(n.Left)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func mustHeapAlloc(n *Node) bool {
|
||||||
|
// TODO(mdempsky): Cleanup this mess.
|
||||||
|
return n.Type != nil &&
|
||||||
|
(n.Type.Width > maxStackVarSize ||
|
||||||
|
(n.Op == ONEW || n.Op == OPTRLIT) && n.Type.Elem().Width >= maxImplicitStackVarSize ||
|
||||||
|
n.Op == OMAKESLICE && !isSmallMakeSlice(n))
|
||||||
|
}
|
||||||
|
|
||||||
func (e *EscState) esc(n *Node, parent *Node) {
|
func (e *EscState) esc(n *Node, parent *Node) {
|
||||||
if n == nil {
|
if n == nil {
|
||||||
return
|
return
|
||||||
@ -658,10 +674,7 @@ func (e *EscState) esc(n *Node, parent *Node) {
|
|||||||
// Big stuff and non-constant-sized stuff escapes unconditionally.
|
// Big stuff and non-constant-sized stuff escapes unconditionally.
|
||||||
// "Big" conditions that were scattered around in walk have been
|
// "Big" conditions that were scattered around in walk have been
|
||||||
// gathered here.
|
// gathered here.
|
||||||
if n.Esc != EscHeap && n.Type != nil &&
|
if n.Esc != EscHeap && mustHeapAlloc(n) {
|
||||||
(n.Type.Width > maxStackVarSize ||
|
|
||||||
(n.Op == ONEW || n.Op == OPTRLIT) && n.Type.Elem().Width >= maxImplicitStackVarSize ||
|
|
||||||
n.Op == OMAKESLICE && !isSmallMakeSlice(n)) {
|
|
||||||
// isSmallMakeSlice returns false for non-constant len/cap.
|
// isSmallMakeSlice returns false for non-constant len/cap.
|
||||||
// If that's the case, print a more accurate escape reason.
|
// If that's the case, print a more accurate escape reason.
|
||||||
var msgVerb, escapeMsg string
|
var msgVerb, escapeMsg string
|
||||||
@ -756,7 +769,7 @@ opSwitch:
|
|||||||
|
|
||||||
case OAS, OASOP:
|
case OAS, OASOP:
|
||||||
// Filter out some no-op assignments for escape analysis.
|
// Filter out some no-op assignments for escape analysis.
|
||||||
if e.isSelfAssign(n.Left, n.Right) {
|
if isSelfAssign(n.Left, n.Right) {
|
||||||
if Debug['m'] != 0 {
|
if Debug['m'] != 0 {
|
||||||
Warnl(n.Pos, "%v ignoring self-assignment in %S", e.curfnSym(n), n)
|
Warnl(n.Pos, "%v ignoring self-assignment in %S", e.curfnSym(n), n)
|
||||||
}
|
}
|
||||||
@ -2182,7 +2195,7 @@ const unsafeUintptrTag = "unsafe-uintptr"
|
|||||||
// marked go:uintptrescapes.
|
// marked go:uintptrescapes.
|
||||||
const uintptrEscapesTag = "uintptr-escapes"
|
const uintptrEscapesTag = "uintptr-escapes"
|
||||||
|
|
||||||
func (e *EscState) esctag(fn *Node) {
|
func esctag(fn *Node) {
|
||||||
fn.Esc = EscFuncTagged
|
fn.Esc = EscFuncTagged
|
||||||
|
|
||||||
name := func(s *types.Sym, narg int) string {
|
name := func(s *types.Sym, narg int) string {
|
||||||
|
1386
src/cmd/compile/internal/gc/escape.go
Normal file
1386
src/cmd/compile/internal/gc/escape.go
Normal file
File diff suppressed because it is too large
Load Diff
@ -253,6 +253,7 @@ func Main(archInit func(*Arch)) {
|
|||||||
flag.StringVar(&blockprofile, "blockprofile", "", "write block profile to `file`")
|
flag.StringVar(&blockprofile, "blockprofile", "", "write block profile to `file`")
|
||||||
flag.StringVar(&mutexprofile, "mutexprofile", "", "write mutex profile to `file`")
|
flag.StringVar(&mutexprofile, "mutexprofile", "", "write mutex profile to `file`")
|
||||||
flag.StringVar(&benchfile, "bench", "", "append benchmark times to `file`")
|
flag.StringVar(&benchfile, "bench", "", "append benchmark times to `file`")
|
||||||
|
flag.BoolVar(&newescape, "newescape", false, "enable new escape analysis")
|
||||||
objabi.Flagparse(usage)
|
objabi.Flagparse(usage)
|
||||||
|
|
||||||
// Record flags that affect the build result. (And don't
|
// Record flags that affect the build result. (And don't
|
||||||
|
Loading…
Reference in New Issue
Block a user