2015-02-19 05:57:03 -07:00
|
|
|
// errorcheck -0 -m -l
|
|
|
|
|
2016-04-10 15:32:26 -06:00
|
|
|
// Copyright 2015 The Go Authors. All rights reserved.
|
2015-02-19 05:57:03 -07:00
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
|
|
|
// Test escape analysis for closure arguments.
|
|
|
|
|
|
|
|
package escape
|
|
|
|
|
|
|
|
var sink interface{}
|
|
|
|
|
|
|
|
func ClosureCallArgs0() {
|
|
|
|
x := 0 // ERROR "moved to heap: x"
|
|
|
|
func(p *int) { // ERROR "p does not escape" "func literal does not escape"
|
|
|
|
*p = 1
|
|
|
|
// BAD: x should not escape to heap here
|
|
|
|
}(&x) // ERROR "&x escapes to heap"
|
|
|
|
}
|
|
|
|
|
|
|
|
func ClosureCallArgs1() {
|
|
|
|
x := 0 // ERROR "moved to heap: x"
|
|
|
|
for {
|
|
|
|
func(p *int) { // ERROR "p does not escape" "func literal does not escape"
|
|
|
|
*p = 1
|
|
|
|
// BAD: x should not escape to heap here
|
|
|
|
}(&x) // ERROR "&x escapes to heap"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func ClosureCallArgs2() {
|
|
|
|
for {
|
|
|
|
// BAD: x should not escape here
|
|
|
|
x := 0 // ERROR "moved to heap: x"
|
|
|
|
func(p *int) { // ERROR "p does not escape" "func literal does not escape"
|
|
|
|
*p = 1
|
|
|
|
}(&x) // ERROR "&x escapes to heap"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func ClosureCallArgs3() {
|
|
|
|
x := 0 // ERROR "moved to heap: x"
|
|
|
|
func(p *int) { // ERROR "leaking param: p" "func literal does not escape"
|
2015-02-19 06:27:32 -07:00
|
|
|
sink = p // ERROR "p escapes to heap"
|
2015-02-19 05:57:03 -07:00
|
|
|
}(&x) // ERROR "&x escapes to heap"
|
|
|
|
}
|
|
|
|
|
|
|
|
func ClosureCallArgs4() {
|
|
|
|
// BAD: x should not leak here
|
|
|
|
x := 0 // ERROR "moved to heap: x"
|
|
|
|
_ = func(p *int) *int { // ERROR "leaking param: p to result ~r1" "func literal does not escape"
|
|
|
|
return p
|
|
|
|
}(&x) // ERROR "&x escapes to heap"
|
|
|
|
}
|
|
|
|
|
|
|
|
func ClosureCallArgs5() {
|
|
|
|
x := 0 // ERROR "moved to heap: x"
|
cmd/compile/internal/syntax: establish principled position information
Until now, the parser set the position for each Node to the position of
the first token belonging to that node. For compatibility with the now
defunct gc parser, in many places that position information was modified
when the gcCompat flag was set (which it was, by default). Furthermore,
in some places, position information was not set at all.
This change removes the gcCompat flag and all associated code, and sets
position information for all nodes in a more principled way, as proposed
by mdempsky (see #16943 for details). Specifically, the position of a
node may not be at the very beginning of the respective production. For
instance for an Operation `a + b`, the position associated with the node
is the position of the `+`. Thus, for `a + b + c` we now get different
positions for the two additions.
This change does not pass toolstash -cmp because position information
recorded in export data and pcline tables is different. There are no
other functional changes.
Added test suite testing the position of all nodes.
Fixes #16943.
Change-Id: I3fc02bf096bc3b3d7d2fa655dfd4714a1a0eb90c
Reviewed-on: https://go-review.googlesource.com/37017
Run-TryBot: Robert Griesemer <gri@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2017-02-13 17:00:53 -07:00
|
|
|
sink = func(p *int) *int { // ERROR "leaking param: p to result ~r1" "func literal does not escape" "\(func literal\)\(&x\) escapes to heap"
|
2015-02-19 05:57:03 -07:00
|
|
|
return p
|
cmd/compile/internal/syntax: establish principled position information
Until now, the parser set the position for each Node to the position of
the first token belonging to that node. For compatibility with the now
defunct gc parser, in many places that position information was modified
when the gcCompat flag was set (which it was, by default). Furthermore,
in some places, position information was not set at all.
This change removes the gcCompat flag and all associated code, and sets
position information for all nodes in a more principled way, as proposed
by mdempsky (see #16943 for details). Specifically, the position of a
node may not be at the very beginning of the respective production. For
instance for an Operation `a + b`, the position associated with the node
is the position of the `+`. Thus, for `a + b + c` we now get different
positions for the two additions.
This change does not pass toolstash -cmp because position information
recorded in export data and pcline tables is different. There are no
other functional changes.
Added test suite testing the position of all nodes.
Fixes #16943.
Change-Id: I3fc02bf096bc3b3d7d2fa655dfd4714a1a0eb90c
Reviewed-on: https://go-review.googlesource.com/37017
Run-TryBot: Robert Griesemer <gri@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2017-02-13 17:00:53 -07:00
|
|
|
}(&x) // ERROR "&x escapes to heap"
|
2015-02-19 05:57:03 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
func ClosureCallArgs6() {
|
|
|
|
x := 0 // ERROR "moved to heap: x"
|
|
|
|
func(p *int) { // ERROR "moved to heap: p" "func literal does not escape"
|
|
|
|
sink = &p // ERROR "&p escapes to heap"
|
|
|
|
}(&x) // ERROR "&x escapes to heap"
|
|
|
|
}
|
|
|
|
|
|
|
|
func ClosureCallArgs7() {
|
|
|
|
var pp *int
|
|
|
|
for {
|
|
|
|
x := 0 // ERROR "moved to heap: x"
|
|
|
|
func(p *int) { // ERROR "leaking param: p" "func literal does not escape"
|
|
|
|
pp = p
|
|
|
|
}(&x) // ERROR "&x escapes to heap"
|
|
|
|
}
|
|
|
|
_ = pp
|
|
|
|
}
|
|
|
|
|
|
|
|
func ClosureCallArgs8() {
|
|
|
|
x := 0 // ERROR "moved to heap: x"
|
|
|
|
defer func(p *int) { // ERROR "p does not escape" "func literal does not escape"
|
|
|
|
*p = 1
|
|
|
|
// BAD: x should not escape to heap here
|
|
|
|
}(&x) // ERROR "&x escapes to heap"
|
|
|
|
}
|
|
|
|
|
|
|
|
func ClosureCallArgs9() {
|
|
|
|
// BAD: x should not leak
|
|
|
|
x := 0 // ERROR "moved to heap: x"
|
|
|
|
for {
|
|
|
|
defer func(p *int) { // ERROR "func literal escapes to heap" "p does not escape"
|
|
|
|
*p = 1
|
|
|
|
}(&x) // ERROR "&x escapes to heap"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func ClosureCallArgs10() {
|
|
|
|
for {
|
|
|
|
x := 0 // ERROR "moved to heap: x"
|
|
|
|
defer func(p *int) { // ERROR "func literal escapes to heap" "p does not escape"
|
|
|
|
*p = 1
|
|
|
|
}(&x) // ERROR "&x escapes to heap"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func ClosureCallArgs11() {
|
|
|
|
x := 0 // ERROR "moved to heap: x"
|
|
|
|
defer func(p *int) { // ERROR "leaking param: p" "func literal does not escape"
|
2015-02-19 06:27:32 -07:00
|
|
|
sink = p // ERROR "p escapes to heap"
|
2015-02-19 05:57:03 -07:00
|
|
|
}(&x) // ERROR "&x escapes to heap"
|
|
|
|
}
|
|
|
|
|
|
|
|
func ClosureCallArgs12() {
|
|
|
|
// BAD: x should not leak
|
|
|
|
x := 0 // ERROR "moved to heap: x"
|
|
|
|
defer func(p *int) *int { // ERROR "leaking param: p to result ~r1" "func literal does not escape"
|
|
|
|
return p
|
|
|
|
}(&x) // ERROR "&x escapes to heap"
|
|
|
|
}
|
|
|
|
|
|
|
|
func ClosureCallArgs13() {
|
|
|
|
x := 0 // ERROR "moved to heap: x"
|
|
|
|
defer func(p *int) { // ERROR "moved to heap: p" "func literal does not escape"
|
|
|
|
sink = &p // ERROR "&p escapes to heap"
|
|
|
|
}(&x) // ERROR "&x escapes to heap"
|
|
|
|
}
|
|
|
|
|
|
|
|
func ClosureCallArgs14() {
|
|
|
|
x := 0 // ERROR "moved to heap: x"
|
|
|
|
// BAD: &x should not escape here
|
|
|
|
p := &x // ERROR "moved to heap: p" "&x escapes to heap"
|
cmd/internal/gc: improve flow of input params to output params
This includes the following information in the per-function summary:
outK = paramJ encoded in outK bits for paramJ
outK = *paramJ encoded in outK bits for paramJ
heap = paramJ EscHeap
heap = *paramJ EscContentEscapes
Note that (currently) if the address of a parameter is taken and
returned, necessarily a heap allocation occurred to contain that
reference, and the heap can never refer to stack, therefore the
parameter and everything downstream from it escapes to the heap.
The per-function summary information now has a tuneable number of bits
(2 is probably noticeably better than 1, 3 is likely overkill, but it
is now easy to check and the -m debugging output includes information
that allows you to figure out if more would be better.)
A new test was added to check pointer flow through struct-typed and
*struct-typed parameters and returns; some of these are sensitive to
the number of summary bits, and ought to yield better results with a
more competent escape analysis algorithm. Another new test checks
(some) correctness with array parameters, results, and operations.
The old analysis inferred a piece of plan9 runtime was non-escaping by
counteracting overconservative analysis with buggy analysis; with the
bug fixed, the result was too conservative (and it's not easy to fix
in this framework) so the source code was tweaked to get the desired
result. A test was added against the discovered bug.
The escape analysis was further improved splitting the "level" into
3 parts, one tracking the conventional "level" and the other two
computing the highest-level-suffix-from-copy, which is used to
generally model the cancelling effect of indirection applied to
address-of.
With the improved escape analysis enabled, it was necessary to
modify one of the runtime tests because it now attempts to allocate
too much on the (small, fixed-size) G0 (system) stack and this
failed the test.
Compiling src/std after touching src/runtime/*.go with -m logging
turned on shows 420 fewer heap allocation sites (10538 vs 10968).
Profiling allocations in src/html/template with
for i in {1..5} ;
do go tool 6g -memprofile=mastx.${i}.prof -memprofilerate=1 *.go;
go tool pprof -alloc_objects -text mastx.${i}.prof ;
done
showed a 15% reduction in allocations performed by the compiler.
Update #3753
Update #4720
Fixes #10466
Change-Id: I0fd97d5f5ac527b45f49e2218d158a6e89951432
Reviewed-on: https://go-review.googlesource.com/8202
Run-TryBot: David Chase <drchase@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Russ Cox <rsc@golang.org>
2015-03-26 14:36:15 -06:00
|
|
|
_ = func(p **int) *int { // ERROR "leaking param: p to result ~r1 level=1" "func literal does not escape"
|
2015-02-19 05:57:03 -07:00
|
|
|
return *p
|
|
|
|
// BAD: p should not escape here
|
|
|
|
}(&p) // ERROR "&p escapes to heap"
|
|
|
|
}
|
|
|
|
|
|
|
|
func ClosureCallArgs15() {
|
|
|
|
x := 0 // ERROR "moved to heap: x"
|
|
|
|
p := &x // ERROR "moved to heap: p" "&x escapes to heap"
|
cmd/compile/internal/syntax: establish principled position information
Until now, the parser set the position for each Node to the position of
the first token belonging to that node. For compatibility with the now
defunct gc parser, in many places that position information was modified
when the gcCompat flag was set (which it was, by default). Furthermore,
in some places, position information was not set at all.
This change removes the gcCompat flag and all associated code, and sets
position information for all nodes in a more principled way, as proposed
by mdempsky (see #16943 for details). Specifically, the position of a
node may not be at the very beginning of the respective production. For
instance for an Operation `a + b`, the position associated with the node
is the position of the `+`. Thus, for `a + b + c` we now get different
positions for the two additions.
This change does not pass toolstash -cmp because position information
recorded in export data and pcline tables is different. There are no
other functional changes.
Added test suite testing the position of all nodes.
Fixes #16943.
Change-Id: I3fc02bf096bc3b3d7d2fa655dfd4714a1a0eb90c
Reviewed-on: https://go-review.googlesource.com/37017
Run-TryBot: Robert Griesemer <gri@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2017-02-13 17:00:53 -07:00
|
|
|
sink = func(p **int) *int { // ERROR "leaking param: p to result ~r1 level=1" "func literal does not escape" "\(func literal\)\(&p\) escapes to heap"
|
2015-02-19 05:57:03 -07:00
|
|
|
return *p
|
|
|
|
// BAD: p should not escape here
|
cmd/compile/internal/syntax: establish principled position information
Until now, the parser set the position for each Node to the position of
the first token belonging to that node. For compatibility with the now
defunct gc parser, in many places that position information was modified
when the gcCompat flag was set (which it was, by default). Furthermore,
in some places, position information was not set at all.
This change removes the gcCompat flag and all associated code, and sets
position information for all nodes in a more principled way, as proposed
by mdempsky (see #16943 for details). Specifically, the position of a
node may not be at the very beginning of the respective production. For
instance for an Operation `a + b`, the position associated with the node
is the position of the `+`. Thus, for `a + b + c` we now get different
positions for the two additions.
This change does not pass toolstash -cmp because position information
recorded in export data and pcline tables is different. There are no
other functional changes.
Added test suite testing the position of all nodes.
Fixes #16943.
Change-Id: I3fc02bf096bc3b3d7d2fa655dfd4714a1a0eb90c
Reviewed-on: https://go-review.googlesource.com/37017
Run-TryBot: Robert Griesemer <gri@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2017-02-13 17:00:53 -07:00
|
|
|
}(&p) // ERROR "&p escapes to heap"
|
2015-02-19 05:57:03 -07:00
|
|
|
}
|
2016-03-01 14:53:37 -07:00
|
|
|
|
|
|
|
func ClosureLeak1(s string) string { // ERROR "ClosureLeak1 s does not escape"
|
|
|
|
t := s + "YYYY" // ERROR "escapes to heap"
|
|
|
|
return ClosureLeak1a(t) // ERROR "ClosureLeak1 ... argument does not escape"
|
|
|
|
}
|
|
|
|
|
|
|
|
// See #14409 -- returning part of captured var leaks it.
|
|
|
|
func ClosureLeak1a(a ...string) string { // ERROR "leaking param: a to result ~r1 level=1"
|
|
|
|
return func() string { // ERROR "ClosureLeak1a func literal does not escape"
|
|
|
|
return a[0]
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
|
|
|
|
func ClosureLeak2(s string) string { // ERROR "ClosureLeak2 s does not escape"
|
|
|
|
t := s + "YYYY" // ERROR "escapes to heap"
|
|
|
|
c := ClosureLeak2a(t) // ERROR "ClosureLeak2 ... argument does not escape"
|
|
|
|
return c
|
|
|
|
}
|
|
|
|
func ClosureLeak2a(a ...string) string { // ERROR "leaking param: a to result ~r1 level=1"
|
|
|
|
return ClosureLeak2b(func() string { // ERROR "ClosureLeak2a func literal does not escape"
|
|
|
|
return a[0]
|
|
|
|
})
|
|
|
|
}
|
|
|
|
func ClosureLeak2b(f func() string) string { // ERROR "leaking param: f to result ~r1 level=1"
|
|
|
|
return f()
|
|
|
|
}
|