mirror of
https://github.com/golang/go.git
synced 2025-05-05 23:53:05 +00:00
cmd/compile: improve escape analysis of known calls
Escape analysis is currently very naive about identifying calls to known functions: it only recognizes direct calls to a declared function, or direct calls to a closure. This CL adds a new "staticValue" helper function that can trace back through local variables that were initialized and never reassigned based on a similar optimization already used by inlining. (And to be used by inlining in a followup CL.) Updates #41474. Change-Id: I8204fd3b1e150ab77a27f583985cf099a8572b2e Reviewed-on: https://go-review.googlesource.com/c/go/+/256458 Run-TryBot: Matthew Dempsky <mdempsky@google.com> TryBot-Result: Go Bot <gobot@golang.org> Trust: Matthew Dempsky <mdempsky@google.com> Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com> Reviewed-by: David Chase <drchase@google.com>
This commit is contained in:
parent
cced777026
commit
c0417df156
@ -771,10 +771,11 @@ func (e *Escape) call(ks []EscHole, call, where *Node) {
|
|||||||
var fn *Node
|
var fn *Node
|
||||||
switch call.Op {
|
switch call.Op {
|
||||||
case OCALLFUNC:
|
case OCALLFUNC:
|
||||||
if call.Left.Op == ONAME && call.Left.Class() == PFUNC {
|
switch v := staticValue(call.Left); {
|
||||||
fn = call.Left
|
case v.Op == ONAME && v.Class() == PFUNC:
|
||||||
} else if call.Left.Op == OCLOSURE {
|
fn = v
|
||||||
fn = call.Left.Func.Closure.Func.Nname
|
case v.Op == OCLOSURE:
|
||||||
|
fn = v.Func.Closure.Func.Nname
|
||||||
}
|
}
|
||||||
case OCALLMETH:
|
case OCALLMETH:
|
||||||
fn = asNode(call.Left.Type.FuncType().Nname)
|
fn = asNode(call.Left.Type.FuncType().Nname)
|
||||||
|
@ -751,6 +751,55 @@ func inlinableClosure(n *Node) *Node {
|
|||||||
return f
|
return f
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func staticValue(n *Node) *Node {
|
||||||
|
for {
|
||||||
|
n1 := staticValue1(n)
|
||||||
|
if n1 == nil {
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
n = n1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// staticValue1 implements a simple SSA-like optimization.
|
||||||
|
func staticValue1(n *Node) *Node {
|
||||||
|
if n.Op != ONAME || n.Class() != PAUTO || n.Name.Addrtaken() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
defn := n.Name.Defn
|
||||||
|
if defn == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var rhs *Node
|
||||||
|
FindRHS:
|
||||||
|
switch defn.Op {
|
||||||
|
case OAS:
|
||||||
|
rhs = defn.Right
|
||||||
|
case OAS2:
|
||||||
|
for i, lhs := range defn.List.Slice() {
|
||||||
|
if lhs == n {
|
||||||
|
rhs = defn.Rlist.Index(i)
|
||||||
|
break FindRHS
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Fatalf("%v missing from LHS of %v", n, defn)
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if rhs == nil {
|
||||||
|
Fatalf("RHS is nil: %v", defn)
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe, _ := reassigned(n)
|
||||||
|
if unsafe {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return rhs
|
||||||
|
}
|
||||||
|
|
||||||
// reassigned takes an ONAME node, walks the function in which it is defined, and returns a boolean
|
// reassigned takes an ONAME node, walks the function in which it is defined, and returns a boolean
|
||||||
// indicating whether the name has any assignments other than its declaration.
|
// indicating whether the name has any assignments other than its declaration.
|
||||||
// The second return value is the first such assignment encountered in the walk, if any. It is mostly
|
// The second return value is the first such assignment encountered in the walk, if any. It is mostly
|
||||||
|
@ -50,7 +50,7 @@ func ClosureCallArgs4() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func ClosureCallArgs5() {
|
func ClosureCallArgs5() {
|
||||||
x := 0 // ERROR "moved to heap: x"
|
x := 0 // ERROR "moved to heap: x"
|
||||||
// TODO(mdempsky): We get "leaking param: p" here because the new escape analysis pass
|
// TODO(mdempsky): We get "leaking param: p" here because the new escape analysis pass
|
||||||
// can tell that p flows directly to sink, but it's a little weird. Re-evaluate.
|
// can tell that p flows directly to sink, but it's a little weird. Re-evaluate.
|
||||||
sink = func(p *int) *int { // ERROR "leaking param: p" "func literal does not escape"
|
sink = func(p *int) *int { // ERROR "leaking param: p" "func literal does not escape"
|
||||||
@ -132,7 +132,7 @@ func ClosureCallArgs14() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func ClosureCallArgs15() {
|
func ClosureCallArgs15() {
|
||||||
x := 0 // ERROR "moved to heap: x"
|
x := 0 // ERROR "moved to heap: x"
|
||||||
p := &x
|
p := &x
|
||||||
sink = func(p **int) *int { // ERROR "leaking param content: p" "func literal does not escape"
|
sink = func(p **int) *int { // ERROR "leaking param content: p" "func literal does not escape"
|
||||||
return *p
|
return *p
|
||||||
@ -164,3 +164,16 @@ func ClosureLeak2a(a ...string) string { // ERROR "leaking param content: a"
|
|||||||
func ClosureLeak2b(f func() string) string { // ERROR "f does not escape"
|
func ClosureLeak2b(f func() string) string { // ERROR "f does not escape"
|
||||||
return f()
|
return f()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ClosureIndirect() {
|
||||||
|
f := func(p *int) {} // ERROR "p does not escape" "func literal does not escape"
|
||||||
|
f(new(int)) // ERROR "new\(int\) does not escape"
|
||||||
|
|
||||||
|
g := f
|
||||||
|
g(new(int)) // ERROR "new\(int\) does not escape"
|
||||||
|
|
||||||
|
h := nopFunc
|
||||||
|
h(new(int)) // ERROR "new\(int\) does not escape"
|
||||||
|
}
|
||||||
|
|
||||||
|
func nopFunc(p *int) {} // ERROR "p does not escape"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user