Skip to content

Commit 2287d95

Browse files
dr2chasebroady
authored andcommitted
[release-branch.go1.7] cmd/compile: escape analysis needs to run "flood" to fixed point
In some cases the members of the root set from which flood runs themselves escape, without their referents being also tagged as escaping. Fix this by reflooding from those roots whose escape increases, and also enhance the "leak" test to include reachability from a heap-escaped root. Fixes #17318. Change-Id: Ied1e75cee17ede8ca72a8b9302ce8201641ec593 Reviewed-on: https://go-review.googlesource.com/30693 Run-TryBot: David Chase <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Russ Cox <[email protected]> Reviewed-on: https://go-review.googlesource.com/31290 Reviewed-by: Brad Fitzpatrick <[email protected]> Reviewed-by: David Chase <[email protected]>
1 parent a2f37b7 commit 2287d95

File tree

2 files changed

+68
-0
lines changed

2 files changed

+68
-0
lines changed

src/cmd/compile/internal/gc/esc.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -472,9 +472,29 @@ func escAnalyze(all []*Node, recursive bool) {
472472

473473
// visit the upstream of each dst, mark address nodes with
474474
// addrescapes, mark parameters unsafe
475+
escapes := make([]uint16, len(e.dsts))
476+
for i, n := range e.dsts {
477+
escapes[i] = n.Esc
478+
}
475479
for _, n := range e.dsts {
476480
escflood(e, n)
477481
}
482+
for {
483+
done := true
484+
for i, n := range e.dsts {
485+
if n.Esc != escapes[i] {
486+
done = false
487+
if Debug['m'] > 2 {
488+
Warnl(n.Lineno, "Reflooding %v %S", e.curfnSym(n), n)
489+
}
490+
escapes[i] = n.Esc
491+
escflood(e, n)
492+
}
493+
}
494+
if done {
495+
break
496+
}
497+
}
478498

479499
// for all top level functions, tag the typenodes corresponding to the param nodes
480500
for _, n := range all {
@@ -1796,6 +1816,7 @@ func escwalkBody(e *EscState, level Level, dst *Node, src *Node, step *EscStep,
17961816
}
17971817

17981818
leaks = level.int() <= 0 && level.guaranteedDereference() <= 0 && dstE.Escloopdepth < modSrcLoopdepth
1819+
leaks = leaks || level.int() <= 0 && dst.Esc&EscMask == EscHeap
17991820

18001821
osrcesc = src.Esc
18011822
switch src.Op {

test/fixedbugs/issue17318.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// errorcheck -0 -N -m -l
2+
3+
// Copyright 2016 The Go Authors. All rights reserved.
4+
// Use of this source code is governed by a BSD-style
5+
// license that can be found in the LICENSE file.
6+
7+
// The escape analyzer needs to run till its root set settles
8+
// (this is not that often, it turns out).
9+
// This test is likely to become stale because the leak depends
10+
// on a spurious-escape bug -- return an interface as a named
11+
// output parameter appears to cause the called closure to escape,
12+
// where returning it as a regular type does not.
13+
14+
package main
15+
16+
import (
17+
"fmt"
18+
)
19+
20+
type closure func(i, j int) ent
21+
22+
type ent int
23+
24+
func (e ent) String() string {
25+
return fmt.Sprintf("%d", int(e)) // ERROR "ent.String ... argument does not escape$" "int\(e\) escapes to heap$"
26+
}
27+
28+
//go:noinline
29+
func foo(ops closure, j int) (err fmt.Stringer) { // ERROR "leaking param: ops$" "leaking param: ops to result err level=0$"
30+
enqueue := func(i int) fmt.Stringer { // ERROR "func literal escapes to heap$"
31+
return ops(i, j) // ERROR "ops\(i, j\) escapes to heap$"
32+
}
33+
err = enqueue(4)
34+
if err != nil {
35+
return err
36+
}
37+
return // return result of enqueue, a fmt.Stringer
38+
}
39+
40+
func main() {
41+
// 3 identical functions, to get different escape behavior.
42+
f := func(i, j int) ent { // ERROR "func literal escapes to heap$"
43+
return ent(i + j)
44+
}
45+
i := foo(f, 3).(ent)
46+
fmt.Printf("foo(f,3)=%d\n", int(i)) // ERROR "int\(i\) escapes to heap$" "main ... argument does not escape$"
47+
}

0 commit comments

Comments
 (0)