Skip to content

Commit 97c4ad4

Browse files
committed
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 <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: David Chase <[email protected]>
1 parent d91f7e6 commit 97c4ad4

File tree

3 files changed

+1415
-15
lines changed

3 files changed

+1415
-15
lines changed

src/cmd/compile/internal/gc/esc.go

+28-15
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,16 @@ import (
4141
// not escape, then new(T) can be rewritten into a stack allocation.
4242
// The same is true of slice literals.
4343

44+
// If newescape is true, then escape.go drives escape analysis instead
45+
// of esc.go.
46+
var newescape bool
47+
4448
func escapes(all []*Node) {
45-
visitBottomUp(all, escAnalyze)
49+
esc := escAnalyze
50+
if newescape {
51+
esc = escapeFuncs
52+
}
53+
visitBottomUp(all, esc)
4654
}
4755

4856
const (
@@ -393,7 +401,7 @@ func escAnalyze(all []*Node, recursive bool) {
393401
// for all top level functions, tag the typenodes corresponding to the param nodes
394402
for _, n := range all {
395403
if n.Op == ODCLFUNC {
396-
e.esctag(n)
404+
esctag(n)
397405
}
398406
}
399407

@@ -516,7 +524,7 @@ func (e *EscState) esclist(l Nodes, parent *Node) {
516524
}
517525
}
518526

519-
func (e *EscState) isSliceSelfAssign(dst, src *Node) bool {
527+
func isSliceSelfAssign(dst, src *Node) bool {
520528
// Detect the following special case.
521529
//
522530
// func (b *Buffer) Foo() {
@@ -566,8 +574,8 @@ func (e *EscState) isSliceSelfAssign(dst, src *Node) bool {
566574

567575
// isSelfAssign reports whether assignment from src to dst can
568576
// be ignored by the escape analysis as it's effectively a self-assignment.
569-
func (e *EscState) isSelfAssign(dst, src *Node) bool {
570-
if e.isSliceSelfAssign(dst, src) {
577+
func isSelfAssign(dst, src *Node) bool {
578+
if isSliceSelfAssign(dst, src) {
571579
return true
572580
}
573581

@@ -589,7 +597,7 @@ func (e *EscState) isSelfAssign(dst, src *Node) bool {
589597
case ODOT, ODOTPTR:
590598
// Safe trailing accessors that are permitted to differ.
591599
case OINDEX:
592-
if e.mayAffectMemory(dst.Right) || e.mayAffectMemory(src.Right) {
600+
if mayAffectMemory(dst.Right) || mayAffectMemory(src.Right) {
593601
return false
594602
}
595603
default:
@@ -602,7 +610,7 @@ func (e *EscState) isSelfAssign(dst, src *Node) bool {
602610

603611
// mayAffectMemory reports whether n evaluation may affect program memory state.
604612
// If expression can't affect it, then it can be safely ignored by the escape analysis.
605-
func (e *EscState) mayAffectMemory(n *Node) bool {
613+
func mayAffectMemory(n *Node) bool {
606614
// We may want to use "memory safe" black list instead of general
607615
// "side-effect free", which can include all calls and other ops
608616
// that can affect allocate or change global state.
@@ -616,18 +624,26 @@ func (e *EscState) mayAffectMemory(n *Node) bool {
616624

617625
// Left+Right group.
618626
case OINDEX, OADD, OSUB, OOR, OXOR, OMUL, OLSH, ORSH, OAND, OANDNOT, ODIV, OMOD:
619-
return e.mayAffectMemory(n.Left) || e.mayAffectMemory(n.Right)
627+
return mayAffectMemory(n.Left) || mayAffectMemory(n.Right)
620628

621629
// Left group.
622630
case ODOT, ODOTPTR, ODEREF, OCONVNOP, OCONV, OLEN, OCAP,
623631
ONOT, OBITNOT, OPLUS, ONEG, OALIGNOF, OOFFSETOF, OSIZEOF:
624-
return e.mayAffectMemory(n.Left)
632+
return mayAffectMemory(n.Left)
625633

626634
default:
627635
return true
628636
}
629637
}
630638

639+
func mustHeapAlloc(n *Node) bool {
640+
// TODO(mdempsky): Cleanup this mess.
641+
return n.Type != nil &&
642+
(n.Type.Width > maxStackVarSize ||
643+
(n.Op == ONEW || n.Op == OPTRLIT) && n.Type.Elem().Width >= maxImplicitStackVarSize ||
644+
n.Op == OMAKESLICE && !isSmallMakeSlice(n))
645+
}
646+
631647
func (e *EscState) esc(n *Node, parent *Node) {
632648
if n == nil {
633649
return
@@ -658,10 +674,7 @@ func (e *EscState) esc(n *Node, parent *Node) {
658674
// Big stuff and non-constant-sized stuff escapes unconditionally.
659675
// "Big" conditions that were scattered around in walk have been
660676
// gathered here.
661-
if n.Esc != EscHeap && n.Type != nil &&
662-
(n.Type.Width > maxStackVarSize ||
663-
(n.Op == ONEW || n.Op == OPTRLIT) && n.Type.Elem().Width >= maxImplicitStackVarSize ||
664-
n.Op == OMAKESLICE && !isSmallMakeSlice(n)) {
677+
if n.Esc != EscHeap && mustHeapAlloc(n) {
665678
// isSmallMakeSlice returns false for non-constant len/cap.
666679
// If that's the case, print a more accurate escape reason.
667680
var msgVerb, escapeMsg string
@@ -756,7 +769,7 @@ opSwitch:
756769

757770
case OAS, OASOP:
758771
// Filter out some no-op assignments for escape analysis.
759-
if e.isSelfAssign(n.Left, n.Right) {
772+
if isSelfAssign(n.Left, n.Right) {
760773
if Debug['m'] != 0 {
761774
Warnl(n.Pos, "%v ignoring self-assignment in %S", e.curfnSym(n), n)
762775
}
@@ -2182,7 +2195,7 @@ const unsafeUintptrTag = "unsafe-uintptr"
21822195
// marked go:uintptrescapes.
21832196
const uintptrEscapesTag = "uintptr-escapes"
21842197

2185-
func (e *EscState) esctag(fn *Node) {
2198+
func esctag(fn *Node) {
21862199
fn.Esc = EscFuncTagged
21872200

21882201
name := func(s *types.Sym, narg int) string {

0 commit comments

Comments
 (0)