cmd/compile: update escape analysis tests for newescape
The new escape analysis implementation tries to emit debugging
diagnostics that are compatible with the existing implementation, but
there's a handful of cases that are easier to handle by updating the
test expectations instead.
For regress tests that need updating, the original file is copied to
oldescapeXXX.go.go with -newescape=false added to the //errorcheck
line, while the file is updated in place with -newescape=true and new
test requirements.
Notable test changes:
1) escape_because.go looks for a lot of detailed internal debugging
messages that are fairly particular to how esc.go works and that I
haven't attempted to port over to escape.go yet.
2) There are a lot of "leaking param: x to result ~r1 level=-1"
messages for code like
func(p *int) *T { return &T{p} }
that were simply wrong. Here &T must be heap allocated unconditionally
(because it's being returned); and since p is stored into it, p
escapes unconditionally too. esc.go incorrectly reports that p escapes
conditionally only if the returned pointer escaped.
3) esc.go used to print each "leaking param" analysis result as it
discovered them, which could lead to redundant messages (e.g., that a
param leaks at level=0 and level=1). escape.go instead prints
everything at the end, once it knows the shortest path to each sink.
4) esc.go didn't precisely model direct-interface types, resulting in
some values unnecessarily escaping to the heap when stored into
non-escaping interface values.
5) For functions written in assembly, esc.go only printed "does not
escape" messages, whereas escape.go prints "does not escape" or
"leaking param" as appropriate, consistent with the behavior for
functions written in Go.
6) 12 tests included "BAD" annotations identifying cases where esc.go
was unnecessarily heap allocating something. These are all fixed by
escape.go.
Updates #23109.
Change-Id: Iabc9eb14c94c9cadde3b183478d1fd54f013502f
Reviewed-on: https://go-review.googlesource.com/c/go/+/170447
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: David Chase <drchase@google.com>
2019-04-02 15:44:13 -06:00
// errorcheck -0 -m -m -l -newescape=false
2016-02-29 08:43:18 -07:00
2016-04-10 15:32:26 -06:00
// Copyright 2015 The Go Authors. All rights reserved.
2016-02-29 08:43:18 -07:00
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Note the doubled -m; this tests the "because" explanations for escapes,
// and is likely to be annoyingly fragile under compiler change.
// As long as the explanations look reasonably sane, meaning eyeball verify output of
// go build -gcflags '-l -m -m' escape_because.go
// and investigate changes, feel free to update with
// go run run.go -update_errors -- escape_because.go
package main
func main ( ) {
}
var sink interface { }
type pair struct {
x , y * int
}
type Pairy interface {
EqualParts ( ) bool
}
func ( p * pair ) EqualParts ( ) bool { // ERROR "\(\*pair\).EqualParts p does not escape$"
return p != nil && ( p . x == p . y || * p . x == * p . y )
}
2016-10-21 10:01:52 -06:00
func f1 ( p * int ) { // ERROR "from \[3\]\*int literal \(array literal element\) at escape_because.go:34$" "from a \(assigned\) at escape_because.go:34$" "from a \(interface-converted\) at escape_because.go:35$" "from sink \(assigned to top level variable\) at escape_because.go:35$" "leaking param: p$"
2016-02-29 08:43:18 -07:00
a := [ 3 ] * int { p , nil , nil }
2016-10-21 10:01:52 -06:00
sink = a // ERROR "a escapes to heap$" "from sink \(assigned to top level variable\) at escape_because.go:35$"
2016-02-29 08:43:18 -07:00
}
2016-10-21 10:01:52 -06:00
func f2 ( q * int ) { // ERROR "from &u \(address-of\) at escape_because.go:43$" "from &u \(interface-converted\) at escape_because.go:43$" "from pair literal \(struct literal element\) at escape_because.go:41$" "from s \(assigned\) at escape_because.go:40$" "from sink \(assigned to top level variable\) at escape_because.go:43$" "from t \(assigned\) at escape_because.go:41$" "from u \(assigned\) at escape_because.go:42$" "leaking param: q$"
2016-02-29 08:43:18 -07:00
s := q
t := pair { s , nil }
u := t // ERROR "moved to heap: u$"
2019-04-01 12:58:33 -06:00
sink = & u // ERROR "&u escapes to heap$" "from sink \(assigned to top level variable\) at escape_because.go:43$"
2016-02-29 08:43:18 -07:00
}
cmd/compile: flow interface data to heap if CONVIFACE of a non-direct interface escapes
Consider the following code:
func f(x []*T) interface{} {
return x
}
It returns an interface that holds a heap copy of x (by calling
convT2I or friend), therefore x escape to heap. The current
escape analysis only recognizes that x flows to the result. This
is not sufficient, since if the result does not escape, x's
content may be stack allocated and this will result a
heap-to-stack pointer, which is bad.
Fix this by realizing that if a CONVIFACE escapes and we're
converting from a non-direct interface type, the data needs to
escape to heap.
Running "toolstash -cmp" on std & cmd, the generated machine code
are identical for all packages. However, the export data (escape
tags) differ in the following packages. It looks to me that all
are similar to the "f" above, where the parameter should escape
to heap.
io/ioutil/ioutil.go:118
old: leaking param: r to result ~r1 level=0
new: leaking param: r
image/image.go:943
old: leaking param: p to result ~r0 level=1
new: leaking param content: p
net/url/url.go:200
old: leaking param: s to result ~r2 level=0
new: leaking param: s
(as a consequence)
net/url/url.go:183
old: leaking param: s to result ~r1 level=0
new: leaking param: s
net/url/url.go:194
old: leaking param: s to result ~r1 level=0
new: leaking param: s
net/url/url.go:699
old: leaking param: u to result ~r0 level=1
new: leaking param: u
net/url/url.go:775
old: (*URL).String u does not escape
new: leaking param content: u
net/url/url.go:1038
old: leaking param: u to result ~r0 level=1
new: leaking param: u
net/url/url.go:1099
old: (*URL).MarshalBinary u does not escape
new: leaking param content: u
flag/flag.go:235
old: leaking param: s to result ~r0 level=1
new: leaking param content: s
go/scanner/errors.go:105
old: leaking param: p to result ~r0 level=0
new: leaking param: p
database/sql/sql.go:204
old: leaking param: ns to result ~r0 level=0
new: leaking param: ns
go/constant/value.go:303
old: leaking param: re to result ~r2 level=0, leaking param: im to result ~r2 level=0
new: leaking param: re, leaking param: im
go/constant/value.go:846
old: leaking param: x to result ~r1 level=0
new: leaking param: x
encoding/xml/xml.go:518
old: leaking param: d to result ~r1 level=2
new: leaking param content: d
encoding/xml/xml.go:122
old: leaking param: leaking param: t to result ~r1 level=0
new: leaking param: t
crypto/x509/verify.go:506
old: leaking param: c to result ~r8 level=0
new: leaking param: c
crypto/x509/verify.go:563
old: leaking param: c to result ~r3 level=0, leaking param content: c
new: leaking param: c
crypto/x509/verify.go:615
old: (nothing)
new: leaking closure reference c
crypto/x509/verify.go:996
old: leaking param: c to result ~r1 level=0, leaking param content: c
new: leaking param: c
net/http/filetransport.go:30
old: leaking param: fs to result ~r1 level=0
new: leaking param: fs
net/http/h2_bundle.go:2684
old: leaking param: mh to result ~r0 level=2
new: leaking param content: mh
net/http/h2_bundle.go:7352
old: http2checkConnHeaders req does not escape
new: leaking param content: req
net/http/pprof/pprof.go:221
old: leaking param: name to result ~r1 level=0
new: leaking param: name
cmd/internal/bio/must.go:21
old: leaking param: w to result ~r1 level=0
new: leaking param: w
Fixes #29353.
Change-Id: I7e7798ae773728028b0dcae5bccb3ada51189c68
Reviewed-on: https://go-review.googlesource.com/c/162829
Run-TryBot: Cherry Zhang <cherryyz@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
Reviewed-by: David Chase <drchase@google.com>
2019-02-17 21:12:55 -07:00
func f3 ( r * int ) interface { } { // ERROR "from \[\]\*int literal \(slice-literal-element\) at escape_because.go:47$" "from c \(assigned\) at escape_because.go:47$" "from c \(interface-converted\) at escape_because.go:48$" "from ~r1 \(return\) at escape_because.go:48$" "leaking param: r"
2016-10-21 10:01:52 -06:00
c := [ ] * int { r } // ERROR "\[\]\*int literal escapes to heap$" "from c \(assigned\) at escape_because.go:47$" "from c \(interface-converted\) at escape_because.go:48$" "from ~r1 \(return\) at escape_because.go:48$"
return c // "return" // ERROR "c escapes to heap$" "from ~r1 \(return\) at escape_because.go:48$"
2016-02-29 08:43:18 -07:00
}
func f4 ( a * int , s [ ] * int ) int { // ERROR "from \*s \(indirection\) at escape_because.go:51$" "from append\(s, a\) \(appended to slice\) at escape_because.go:52$" "from append\(s, a\) \(appendee slice\) at escape_because.go:52$" "leaking param content: s$" "leaking param: a$"
s = append ( s , a )
return * ( s [ 0 ] )
}
func f5 ( s1 , s2 [ ] * int ) int { // ERROR "from \*s1 \(indirection\) at escape_because.go:56$" "from \*s2 \(indirection\) at escape_because.go:56$" "from append\(s1, s2...\) \(appended slice...\) at escape_because.go:57$" "from append\(s1, s2...\) \(appendee slice\) at escape_because.go:57$" "leaking param content: s1$" "leaking param content: s2$"
s1 = append ( s1 , s2 ... )
return * ( s1 [ 0 ] )
}
func f6 ( x , y * int ) bool { // ERROR "f6 x does not escape$" "f6 y does not escape$"
p := pair { x , y }
var P Pairy = & p // ERROR "f6 &p does not escape$"
pp := P . ( * pair )
return pp . EqualParts ( )
}
func f7 ( x map [ int ] * int , y int ) * int { // ERROR "f7 x does not escape$"
z , ok := x [ y ]
if ! ok {
return nil
}
return z
}
2016-10-21 10:01:52 -06:00
func f8 ( x int , y * int ) * int { // ERROR "from ~r2 \(return\) at escape_because.go:78$" "from ~r2 \(returned from recursive function\) at escape_because.go:76$" "leaking param: y$" "moved to heap: x$"
2016-02-29 08:43:18 -07:00
if x <= 0 {
return y
}
x --
2019-04-01 12:58:33 -06:00
return f8 ( * y , & x )
2016-02-29 08:43:18 -07:00
}
2016-10-21 10:01:52 -06:00
func f9 ( x int , y ... * int ) * int { // ERROR "from y\[0\] \(dot of pointer\) at escape_because.go:86$" "from ~r2 \(return\) at escape_because.go:86$" "from ~r2 \(returned from recursive function\) at escape_because.go:84$" "leaking param content: y$" "leaking param: y to result ~r2 level=1$" "moved to heap: x$"
2016-02-29 08:43:18 -07:00
if x <= 0 {
return y [ 0 ]
}
x --
2019-04-01 12:58:33 -06:00
return f9 ( * y [ 0 ] , & x ) // ERROR "f9 ... argument does not escape$"
2016-02-29 08:43:18 -07:00
}
func f10 ( x map [ * int ] * int , y , z * int ) * int { // ERROR "f10 x does not escape$" "from x\[y\] \(key of map put\) at escape_because.go:93$" "from x\[y\] \(value of map put\) at escape_because.go:93$" "leaking param: y$" "leaking param: z$"
x [ y ] = z
return z
}
func f11 ( x map [ * int ] * int , y , z * int ) map [ * int ] * int { // ERROR "f11 x does not escape$" "from map\[\*int\]\*int literal \(map literal key\) at escape_because.go:98$" "from map\[\*int\]\*int literal \(map literal value\) at escape_because.go:98$" "leaking param: y$" "leaking param: z$"
2016-10-21 10:01:52 -06:00
return map [ * int ] * int { y : z } // ERROR "from ~r3 \(return\) at escape_because.go:98$" "map\[\*int\]\*int literal escapes to heap$"
}
func f12 ( ) {
b := [ ] byte ( "test" ) // ERROR "\(\[\]byte\)\(.test.\) escapes to heap$" "from b \(assigned\) at escape_because.go:102$" "from b \(passed to call\[argument escapes\]\) at escape_because.go:103$"
escape ( b )
}
func escape ( b [ ] byte ) { // ERROR "from panic\(b\) \(panic\) at escape_because.go:107$" "leaking param: b$"
panic ( b )
}
func f13 ( ) {
b := [ ] byte ( "test" ) // ERROR "\(\[\]byte\)\(.test.\) escapes to heap$" "from .out0 \(passed-to-and-returned-from-call\) at escape_because.go:112$" "from b \(assigned\) at escape_because.go:111$" "from c \(assigned\) at escape_because.go:112$" "from c \(passed to call\[argument escapes\]\) at escape_because.go:113$"
c := transmit ( b )
escape ( c )
}
2018-09-04 08:35:55 -06:00
func transmit ( b [ ] byte ) [ ] byte { // ERROR "from ~r1 \(return\) at escape_because.go:117$" "leaking param: b to result ~r1 level=0$"
2016-10-21 10:01:52 -06:00
return b
2016-02-29 08:43:18 -07:00
}
2018-03-28 04:19:46 -06:00
func f14 ( ) {
n := 32
s1 := make ( [ ] int , n ) // ERROR "make\(\[\]int, n\) escapes to heap" "from make\(\[\]int, n\) \(non-constant size\)"
s2 := make ( [ ] int , 0 , n ) // ERROR "make\(\[\]int, 0, n\) escapes to heap" "from make\(\[\]int, 0, n\) \(non-constant size\)"
_ , _ = s1 , s2
}
2018-09-04 08:35:55 -06:00
func leakParams ( p1 , p2 * int ) ( * int , * int ) { // ERROR "leaking param: p1 to result ~r2 level=0$" "from ~r2 \(return\) at escape_because.go:128$" "leaking param: p2 to result ~r3 level=0$" "from ~r3 \(return\) at escape_because.go:128$"
2018-08-14 10:22:16 -06:00
return p1 , p2
}
func leakThroughOAS2 ( ) {
// See #26987.
i := 0 // ERROR "moved to heap: i$"
j := 0 // ERROR "moved to heap: j$"
2019-04-01 12:58:33 -06:00
sink , sink = & i , & j // ERROR "&i escapes to heap$" "from sink \(assign-pair\) at escape_because.go:135$" "&j escapes to heap$"
2018-08-14 10:22:16 -06:00
}
func leakThroughOAS2FUNC ( ) {
// See #26987.
i := 0 // ERROR "moved to heap: i$"
j := 0
2019-04-01 12:58:33 -06:00
sink , _ = leakParams ( & i , & j )
2018-08-14 10:22:16 -06:00
}
2016-02-29 08:43:18 -07:00
// The list below is all of the why-escapes messages seen building the escape analysis tests.
/ *
for i in escape * go ; do echo compile $ i ; go build - gcflags ' - l - m - m ' $ i > & ` basename $i .go ` . log ; done
grep ' from . * at ' escape * . log | sed - e ' s / ^ . * ( \ ( [ ^ ( ) ] * \ ) ) [ ^ ( ) ] * $ / \ 1 / ' | sort - u
* /
// sed RE above assumes that (reason) is the last parenthesized phrase in the line,
// and that none of the reasons contains any parentheses
/ *
... arg to recursive call
address - of
appended slice ...
appended to slice
appendee slice
arg to ...
arg to recursive call
array - element - equals
2018-03-29 06:35:57 -06:00
array literal element
2016-02-29 08:43:18 -07:00
assigned
assigned to top level variable
2018-03-29 06:35:57 -06:00
assign - pair - dot - type
assign - pair - func - call
2016-02-29 08:43:18 -07:00
captured by a closure
2018-03-29 06:35:57 -06:00
captured by called closure
2016-02-29 08:43:18 -07:00
dot
dot - equals
2018-03-29 06:35:57 -06:00
dot of pointer
2016-02-29 08:43:18 -07:00
fixed - array - index - of
go func arg
indirection
interface - converted
key of map put
map literal key
map literal value
2018-03-29 06:35:57 -06:00
non - constant size
panic
2016-02-29 08:43:18 -07:00
parameter to indirect call
2018-03-29 06:35:57 -06:00
passed - to - and - returned - from - call
2016-10-21 10:01:52 -06:00
passed to call [ argument content escapes ]
passed to call [ argument escapes ]
2016-02-29 08:43:18 -07:00
pointer literal
range - deref
receiver in indirect call
return
returned from recursive function
slice - element - equals
slice - literal - element
star - dot - equals
star - equals
struct literal element
too large for stack
value of map put
* /
// Expected, but not yet seen (they may be unreachable):
/ *
append - first - arg
assign - pair - mapr
assign - pair - receive
call receiver
map index
pointer literal [ assign ]
slice literal element
* /