• Dmitry Vyukov's avatar
    cmd/gc: fix escape analysis of closures · 878a86a1
    Dmitry Vyukov authored
    Fixes #10353
    
    See test/escape2.go:issue10353. Previously new(int) did not escape to heap,
    and so heap-allcated closure was referencing a stack var. This breaks
    the invariant that heap must not contain pointers to stack.
    
    Look at the following program:
    
    package main
    
    func main() {
    	foo(new(int))
    	bar(new(int))
    }
    
    func foo(x *int) func() {
    	return func() {
    		println(*x)
    	}
    }
    
    // Models what foo effectively does.
    func bar(x *int) *C {
    	return &C{x}
    }
    
    type C struct {
    	x *int
    }
    
    Without this patch escape analysis works as follows:
    
    $ go build -gcflags="-m -m -m -l" esc.go
    escflood:1: dst ~r1 scope:foo[0]
    escwalk: level:0 depth:0  func literal( l(9) f(1) esc(no) ld(1)) scope:foo[1]
    /tmp/live2.go:9: func literal escapes to heap
    escwalk: level:0 depth:1 	 x( l(8) class(PPARAM) f(1) esc(no) ld(1)) scope:foo[1]
    /tmp/live2.go:8: leaking param: x to result ~r1
    
    escflood:2: dst ~r1 scope:bar[0]
    escwalk: level:0 depth:0  &C literal( l(15) esc(no) ld(1)) scope:bar[1]
    /tmp/live2.go:15: &C literal escapes to heap
    escwalk: level:-1 depth:1 	 &C literal( l(15)) scope:bar[0]
    escwalk: level:-1 depth:2 		 x( l(14) class(PPARAM) f(1) esc(no) ld(1)) scope:bar[1]
    /tmp/live2.go:14: leaking param: x
    
    /tmp/live2.go:5: new(int) escapes to heap
    /tmp/live2.go:4: main new(int) does not escape
    
    new(int) does not escape while being captured by the closure.
    With this patch escape analysis of foo and bar works similarly:
    
    $ go build -gcflags="-m -m -m -l" esc.go
    escflood:1: dst ~r1 scope:foo[0]
    escwalk: level:0 depth:0  &(func literal)( l(9)) scope:foo[0]
    escwalk: level:-1 depth:1 	 func literal( l(9) f(1) esc(no) ld(1)) scope:foo[1]
    /tmp/live2.go:9: func literal escapes to heap
    escwalk: level:-1 depth:2 		 x( l(8) class(PPARAM) f(1) esc(no) ld(1)) scope:foo[1]
    /tmp/live2.go:8: leaking param: x
    
    escflood:2: dst ~r1 scope:bar[0]
    escwalk: level:0 depth:0  &C literal( l(15) esc(no) ld(1)) scope:bar[1]
    /tmp/live2.go:15: &C literal escapes to heap
    escwalk: level:-1 depth:1 	 &C literal( l(15)) scope:bar[0]
    escwalk: level:-1 depth:2 		 x( l(14) class(PPARAM) f(1) esc(no) ld(1)) scope:bar[1]
    /tmp/live2.go:14: leaking param: x
    
    /tmp/live2.go:4: new(int) escapes to heap
    /tmp/live2.go:5: new(int) escapes to heap
    
    Change-Id: Ifd14b7ae3fc11820e3b5eb31eb07f35a22ed0932
    Reviewed-on: https://go-review.googlesource.com/8408Reviewed-by: default avatarRuss Cox <rsc@golang.org>
    Run-TryBot: Dmitry Vyukov <dvyukov@google.com>
    TryBot-Result: Gobot Gobot <gobot@golang.org>
    878a86a1
esc.go 36.3 KB