diff options
author | Hugues Bruant <hugues.bruant@gmail.com> | 2017-09-18 14:54:10 -0700 |
---|---|---|
committer | Daniel Martà <mvdan@mvdan.cc> | 2017-10-11 22:32:36 +0000 |
commit | 4f70a2a699d23bb47eae36c5170086688d2fa764 (patch) | |
tree | d764c2417d55ecf69bd8ae20822e9c5fafff9893 /test/inline.go | |
parent | 44d9e96da9b7625be81f2c7eacf73fcc609874ce (diff) | |
download | go-4f70a2a699d23bb47eae36c5170086688d2fa764.tar.gz go-4f70a2a699d23bb47eae36c5170086688d2fa764.zip |
cmd/compile: inline calls to local closures
Calls to a closure held in a local, non-escaping,
variable can be inlined, provided the closure body
can be inlined and the variable is never written to.
The current implementation has the following limitations:
- closures with captured variables are not inlined because
doing so naively triggers invariant violation in the SSA
phase
- re-assignment check is currently approximated by checking
the Addrtaken property of the variable which should be safe
but may miss optimization opportunities if the address is
not used for a write before the invocation
Updates #15561
Change-Id: I508cad5d28f027bd7e933b1f793c14dcfef8b5a1
Reviewed-on: https://go-review.googlesource.com/65071
Run-TryBot: Daniel Martà <mvdan@mvdan.cc>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Hugues Bruant <hugues.bruant@gmail.com>
Reviewed-by: Keith Randall <khr@golang.org>
Diffstat (limited to 'test/inline.go')
-rw-r--r-- | test/inline.go | 49 |
1 files changed, 48 insertions, 1 deletions
diff --git a/test/inline.go b/test/inline.go index 7bb86ef8b2..7d8b2ceba9 100644 --- a/test/inline.go +++ b/test/inline.go @@ -9,7 +9,10 @@ package foo -import "unsafe" +import ( + "errors" + "unsafe" +) func add2(p *byte, n uintptr) *byte { // ERROR "can inline add2" "leaking param: p to result" return (*byte)(add1(unsafe.Pointer(p), n)) // ERROR "inlining call to add1" @@ -46,6 +49,50 @@ func j(x int) int { // ERROR "can inline j" } } +var somethingWrong error = errors.New("something went wrong") + +// local closures can be inlined +func l(x, y int) (int, int, error) { + e := func(err error) (int, int, error) { // ERROR "can inline l.func1" "func literal does not escape" "leaking param: err to result" + return 0, 0, err + } + if x == y { + e(somethingWrong) // ERROR "inlining call to l.func1" + } + return y, x, nil +} + +// any re-assignment prevents closure inlining +func m() int { + foo := func() int { return 1 } // ERROR "can inline m.func1" "func literal does not escape" + x := foo() + foo = func() int { return 2 } // ERROR "can inline m.func2" "func literal does not escape" + return x + foo() +} + +// address taking prevents closure inlining +func n() int { + foo := func() int { return 1 } // ERROR "can inline n.func1" "func literal does not escape" + bar := &foo // ERROR "&foo does not escape" + x := (*bar)() + foo() + return x +} + +// make sure assignment inside closure is detected +func o() int { + foo := func() int { return 1 } // ERROR "can inline o.func1" "func literal does not escape" + func(x int) { // ERROR "func literal does not escape" + if x > 10 { + foo = func() int { return 2 } // ERROR "can inline o.func2" "func literal escapes" + } + }(11) + return foo() +} + +func p() int { + return func() int { return 42 }() // ERROR "can inline p.func1" "inlining call to p.func1" +} + // can't currently inline functions with a break statement func switchBreak(x, y int) int { var n int |