diff options
author | David Chase <drchase@google.com> | 2015-03-26 16:36:15 -0400 |
---|---|---|
committer | David Chase <drchase@google.com> | 2015-05-01 13:47:20 +0000 |
commit | 7fbb1b36c37ac49db78042adc7533fb4ab83a4bc (patch) | |
tree | 053b665f5469d0ba1ad6bf82dd7f4469818bd2d6 /test/escape_param.go | |
parent | 4044adedf7eb8c3ab89f00479965be62e029f350 (diff) | |
download | go-git-7fbb1b36c37ac49db78042adc7533fb4ab83a4bc.tar.gz |
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>
Diffstat (limited to 'test/escape_param.go')
-rw-r--r-- | test/escape_param.go | 87 |
1 files changed, 57 insertions, 30 deletions
diff --git a/test/escape_param.go b/test/escape_param.go index 91ad437d86..cfbcd51ca7 100644 --- a/test/escape_param.go +++ b/test/escape_param.go @@ -14,7 +14,7 @@ package escape var sink interface{} // in -> out -func param0(p *int) *int { // ERROR "leaking param: p to result ~r1$" +func param0(p *int) *int { // ERROR "leaking param: p to result ~r1" return p } @@ -29,7 +29,7 @@ func caller0b() { } // in, in -> out, out -func param1(p1, p2 *int) (*int, *int) { // ERROR "leaking param: p1 to result ~r2$" "leaking param: p2 to result ~r3$" +func param1(p1, p2 *int) (*int, *int) { // ERROR "leaking param: p1 to result ~r2" "leaking param: p2 to result ~r3" return p1, p2 } @@ -64,23 +64,23 @@ type Pair struct { p2 *int } -func param3(p *Pair) { // ERROR "leaking param: p$" +func param3(p *Pair) { // ERROR "leaking param content: p$" p.p1 = p.p2 } func caller3a() { i := 0 // ERROR "moved to heap: i$" j := 0 // ERROR "moved to heap: j$" - p := Pair{&i, &j} // ERROR "&i escapes to heap$" "&j escapes to heap$" "moved to heap: p$" - param3(&p) // ERROR "&p escapes to heap$" + p := Pair{&i, &j} // ERROR "&i escapes to heap$" "&j escapes to heap$" + param3(&p) // ERROR "caller3a &p does not escape" _ = p } func caller3b() { i := 0 // ERROR "moved to heap: i$" j := 0 // ERROR "moved to heap: j$" - p := Pair{&i, &j} // ERROR "&i escapes to heap$" "&j escapes to heap$" "moved to heap: p$" - param3(&p) // ERROR "&p escapes to heap$" + p := Pair{&i, &j} // ERROR "&i escapes to heap$" "&j escapes to heap$" + param3(&p) // ERROR "caller3b &p does not escape" sink = p // ERROR "p escapes to heap$" } @@ -114,27 +114,27 @@ func caller5() { } // *in -> heap -func param6(i ***int) { // ERROR "leaking param: i$" +func param6(i ***int) { // ERROR "leaking param content: i$" sink = *i // ERROR "\*i escapes to heap$" } func caller6a() { i := 0 // ERROR "moved to heap: i$" p := &i // ERROR "&i escapes to heap$" "moved to heap: p$" - p2 := &p // ERROR "&p escapes to heap$" "moved to heap: p2$" - param6(&p2) // ERROR "&p2 escapes to heap$" + p2 := &p // ERROR "&p escapes to heap$" + param6(&p2) // ERROR "caller6a &p2 does not escape" } // **in -> heap -func param7(i ***int) { // ERROR "leaking param: i$" +func param7(i ***int) { // ERROR "leaking param content: i$" sink = **i // ERROR "\* \(\*i\) escapes to heap" } func caller7() { i := 0 // ERROR "moved to heap: i$" p := &i // ERROR "&i escapes to heap$" "moved to heap: p$" - p2 := &p // ERROR "&p escapes to heap$" "moved to heap: p2$" - param7(&p2) // ERROR "&p2 escapes to heap$" + p2 := &p // ERROR "&p escapes to heap$" + param7(&p2) // ERROR "caller7 &p2 does not escape" } // **in -> heap @@ -149,14 +149,14 @@ func caller8() { } // *in -> out -func param9(p ***int) **int { // ERROR "param9 leaking param p content to result ~r1$" +func param9(p ***int) **int { // ERROR "leaking param: p to result ~r1 level=1" return *p } func caller9a() { - i := 0 // ERROR "moved to heap: i$" - p := &i // ERROR "&i escapes to heap$" "moved to heap: p$" - p2 := &p // ERROR "&p escapes to heap$" + i := 0 + p := &i // ERROR "caller9a &i does not escape" + p2 := &p // ERROR "caller9a &p does not escape" _ = param9(&p2) // ERROR "caller9a &p2 does not escape$" } @@ -168,33 +168,33 @@ func caller9b() { } // **in -> out -func param10(p ***int) *int { // ERROR "param10 leaking param p content to result ~r1$" +func param10(p ***int) *int { // ERROR "leaking param: p to result ~r1 level=2" return **p } func caller10a() { - i := 0 // ERROR "moved to heap: i$" - p := &i // ERROR "&i escapes to heap$" "moved to heap: p$" - p2 := &p // ERROR "&p escapes to heap$" + i := 0 + p := &i // ERROR "caller10a &i does not escape" + p2 := &p // ERROR "caller10a &p does not escape" _ = param10(&p2) // ERROR "caller10a &p2 does not escape$" } func caller10b() { i := 0 // ERROR "moved to heap: i$" - p := &i // ERROR "&i escapes to heap$" "moved to heap: p$" - p2 := &p // ERROR "&p escapes to heap$" + p := &i // ERROR "&i escapes to heap$" + p2 := &p // ERROR "caller10b &p does not escape$" sink = param10(&p2) // ERROR "caller10b &p2 does not escape$" "param10\(&p2\) escapes to heap" } -// &in -> out +// in escapes to heap (address of param taken and returned) func param11(i **int) ***int { // ERROR "moved to heap: i$" return &i // ERROR "&i escapes to heap$" } func caller11a() { - i := 0 // ERROR "moved to heap: i$" - p := &i // ERROR "&i escapes to heap$" "moved to heap: p$" - _ = param11(&p) // ERROR "&p escapes to heap$" + i := 0 // ERROR "moved to heap: i" + p := &i // ERROR "moved to heap: p" "&i escapes to heap" + _ = param11(&p) // ERROR "&p escapes to heap" } func caller11b() { @@ -203,10 +203,17 @@ func caller11b() { sink = param11(&p) // ERROR "&p escapes to heap$" "param11\(&p\) escapes to heap" } -func caller11c() { +func caller11c() { // GOOD i := 0 // ERROR "moved to heap: i$" - p := &i // ERROR "&i escapes to heap$" "moved to heap: p$" - sink = *param11(&p) // ERROR "&p escapes to heap$" "\*param11\(&p\) escapes to heap" + p := &i // ERROR "moved to heap: p" "&i escapes to heap" + sink = *param11(&p) // ERROR "&p escapes to heap" "\*param11\(&p\) escapes to heap" +} + +func caller11d() { + i := 0 // ERROR "moved to heap: i$" + p := &i // ERROR "&i escapes to heap" "moved to heap: p" + p2 := &p // ERROR "&p escapes to heap" + sink = param11(p2) // ERROR "param11\(p2\) escapes to heap" } // &in -> rcvr @@ -324,3 +331,23 @@ func caller13h() { v.param13(&i) // ERROR "&i escapes to heap$" sink = **v.p // ERROR "\* \(\*v\.p\) escapes to heap" } + +type Node struct { + p *Node +} + +var Sink *Node + +func f(x *Node) { // ERROR "leaking param content: x" + Sink = &Node{x.p} // ERROR "&Node literal escapes to heap" +} + +func g(x *Node) *Node { // ERROR "leaking param: x to result ~r1 level=0" + return &Node{x.p} // ERROR "&Node literal escapes to heap" +} + +func h(x *Node) { // ERROR "leaking param: x" + y := &Node{x} // ERROR "h &Node literal does not escape" + Sink = g(y) + f(y) +} |