• 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: 's avatarRuss Cox <rsc@golang.org>
    Run-TryBot: Dmitry Vyukov <dvyukov@google.com>
    TryBot-Result: Gobot Gobot <gobot@golang.org>
    878a86a1
Name
Last commit
Last update
..
bench Loading commit data...
bugs Loading commit data...
chan Loading commit data...
ddd2.dir Loading commit data...
dwarf Loading commit data...
fixedbugs Loading commit data...
import2.dir Loading commit data...
import4.dir Loading commit data...
interface Loading commit data...
ken Loading commit data...
method4.dir Loading commit data...
safe Loading commit data...
stress Loading commit data...
syntax Loading commit data...
235.go Loading commit data...
64bit.go Loading commit data...
alias.go Loading commit data...
alias1.go Loading commit data...
append.go Loading commit data...
args.go Loading commit data...
assign.go Loading commit data...
assign1.go Loading commit data...
bigalg.go Loading commit data...
bigmap.go Loading commit data...
blank.go Loading commit data...
blank1.go Loading commit data...
bom.go Loading commit data...
bombad.go Loading commit data...
bounds.go Loading commit data...
chancap.go Loading commit data...
chanlinear.go Loading commit data...
char_lit.go Loading commit data...
char_lit1.go Loading commit data...
clearfat.go Loading commit data...
closedchan.go Loading commit data...
closure.go Loading commit data...
closure1.go Loading commit data...
closure2.go Loading commit data...
cmp.go Loading commit data...
cmp6.go Loading commit data...
cmplx.go Loading commit data...
cmplxdivide.c Loading commit data...
cmplxdivide.go Loading commit data...
cmplxdivide1.go Loading commit data...
complit.go Loading commit data...
complit1.go Loading commit data...
compos.go Loading commit data...
const.go Loading commit data...
const1.go Loading commit data...
const2.go Loading commit data...
const3.go Loading commit data...
const4.go Loading commit data...
const5.go Loading commit data...
const6.go Loading commit data...
convT2X.go Loading commit data...
convert.go Loading commit data...
convert1.go Loading commit data...
convert3.go Loading commit data...
convlit.go Loading commit data...
convlit1.go Loading commit data...
copy.go Loading commit data...
crlf.go Loading commit data...
ddd.go Loading commit data...
ddd1.go Loading commit data...
ddd2.go Loading commit data...
decl.go Loading commit data...
declbad.go Loading commit data...
defer.go Loading commit data...
deferfin.go Loading commit data...
deferprint.go Loading commit data...
deferprint.out Loading commit data...
divide.go Loading commit data...
divmod.go Loading commit data...
empty.go Loading commit data...
env.go Loading commit data...
eof.go Loading commit data...
eof1.go Loading commit data...
errchk Loading commit data...
escape.go Loading commit data...
escape2.go Loading commit data...
escape2n.go Loading commit data...
escape3.go Loading commit data...
escape4.go Loading commit data...
escape5.go Loading commit data...
escape_closure.go Loading commit data...
escape_field.go Loading commit data...
escape_iface.go Loading commit data...
escape_indir.go Loading commit data...
escape_level.go Loading commit data...
escape_map.go Loading commit data...
escape_param.go Loading commit data...
escape_slice.go Loading commit data...
fibo.go Loading commit data...
float_lit.go Loading commit data...
float_lit2.go Loading commit data...
float_lit3.go Loading commit data...
floatcmp.go Loading commit data...
for.go Loading commit data...
func.go Loading commit data...
func1.go Loading commit data...
func2.go Loading commit data...
func3.go Loading commit data...
func4.go Loading commit data...
func5.go Loading commit data...
func6.go Loading commit data...
func7.go Loading commit data...
func8.go Loading commit data...
funcdup.go Loading commit data...
funcdup2.go Loading commit data...
gc.go Loading commit data...
gc1.go Loading commit data...
gc2.go Loading commit data...
gcstring.go Loading commit data...
goprint.go Loading commit data...
goprint.out Loading commit data...
goto.go Loading commit data...
helloworld.go Loading commit data...
helloworld.out Loading commit data...
if.go Loading commit data...
import.go Loading commit data...
import1.go Loading commit data...
import2.go Loading commit data...
import4.go Loading commit data...
import5.go Loading commit data...
index.go Loading commit data...
index0.go Loading commit data...
index1.go Loading commit data...
index2.go Loading commit data...
indirect.go Loading commit data...
indirect1.go Loading commit data...
init.go Loading commit data...
init1.go Loading commit data...
initcomma.go Loading commit data...
initialize.go Loading commit data...
initializerr.go Loading commit data...
inline.go Loading commit data...
int_lit.go Loading commit data...
intcvt.go Loading commit data...
iota.go Loading commit data...
label.go Loading commit data...
label1.go Loading commit data...
linkx.go Loading commit data...
linkx_run.go Loading commit data...
literal.go Loading commit data...
live.go Loading commit data...
live1.go Loading commit data...
live2.go Loading commit data...
mallocfin.go Loading commit data...
map.go Loading commit data...
map1.go Loading commit data...
maplinear.go Loading commit data...
method.go Loading commit data...
method1.go Loading commit data...
method2.go Loading commit data...
method3.go Loading commit data...
method4.go Loading commit data...
method5.go Loading commit data...
named.go Loading commit data...
named1.go Loading commit data...
nil.go Loading commit data...
nilcheck.go Loading commit data...
nilptr.go Loading commit data...
nilptr2.go Loading commit data...
nilptr3.go Loading commit data...
nilptr4.go Loading commit data...
nosplit.go Loading commit data...
nul1.go Loading commit data...
parentype.go Loading commit data...
peano.go Loading commit data...
print.go Loading commit data...
print.out Loading commit data...
printbig.go Loading commit data...
printbig.out Loading commit data...
range.go Loading commit data...
recover.go Loading commit data...
recover1.go Loading commit data...
recover2.go Loading commit data...
recover3.go Loading commit data...
recover4.go Loading commit data...
rename.go Loading commit data...
rename1.go Loading commit data...
reorder.go Loading commit data...
reorder2.go Loading commit data...
return.go Loading commit data...
rotate.go Loading commit data...
rotate0.go Loading commit data...
rotate1.go Loading commit data...
rotate2.go Loading commit data...
rotate3.go Loading commit data...
run.go Loading commit data...
rune.go Loading commit data...
runtime.go Loading commit data...
shift1.go Loading commit data...
shift2.go Loading commit data...
sieve.go Loading commit data...
sigchld.go Loading commit data...
sigchld.out Loading commit data...
simassign.go Loading commit data...
sinit.go Loading commit data...
sinit_run.go Loading commit data...
sizeof.go Loading commit data...
slice3.go Loading commit data...
slice3err.go Loading commit data...
slicecap.go Loading commit data...
solitaire.go Loading commit data...
stack.go Loading commit data...
string_lit.go Loading commit data...
stringrange.go Loading commit data...
struct0.go Loading commit data...
switch.go Loading commit data...
switch3.go Loading commit data...
switch4.go Loading commit data...
tinyfin.go Loading commit data...
torture.go Loading commit data...
turing.go Loading commit data...
typecheck.go Loading commit data...
typeswitch.go Loading commit data...
typeswitch1.go Loading commit data...
typeswitch2.go Loading commit data...
typeswitch3.go Loading commit data...
undef.go Loading commit data...
utf.go Loading commit data...
varerr.go Loading commit data...
varinit.go Loading commit data...
zerodivide.go Loading commit data...