summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
authorThan McIntosh <thanm@google.com>2023-04-04 18:31:46 -0400
committerThan McIntosh <thanm@google.com>2023-05-05 21:04:48 +0000
commit445e520d494aec4f4f34d6a67a0a6231a44fe1b5 (patch)
tree27acaf9bddba4bfee2cd40b3646528de1cba22df /test
parent89138ce740bf72c7a6c0eae5aa281f10094637cf (diff)
downloadgo-git-445e520d494aec4f4f34d6a67a0a6231a44fe1b5.tar.gz
cmd/compile: allow more inlining of functions that construct closures
[This is a roll-forward of CL 479095, which was reverted due to a bad interaction between inlining and escape analysis, then later fixed first with an attempt in CL 482355, then again in CL 484859, and then one more time with CL 492135.] Currently, when the inliner is determining if a function is inlineable, it descends into the bodies of closures constructed by that function. This has several unfortunate consequences: - If the closure contains a disallowed operation (e.g., a defer), then the outer function can't be inlined. It makes sense that the *closure* can't be inlined in this case, but it doesn't make sense to punish the function that constructs the closure. - The hairiness of the closure counts against the inlining budget of the outer function. Since we currently copy the closure body when inlining the outer function, this makes sense from the perspective of export data size and binary size, but ultimately doesn't make much sense from the perspective of what should be inlineable. - Since the inliner walks into every closure created by an outer function in addition to starting a walk at every closure, this adds an n^2 factor to inlinability analysis. This CL simply drops this behavior. In std, this makes 57 more functions inlinable, and disallows inlining for 10 (due to the basic instability of our bottom-up inlining approach), for an net increase of 47 inlinable functions (+0.6%). This will help significantly with the performance of the functions to be added for #56102, which have a somewhat complicated nesting of closures with a performance-critical fast path. The downside of this seems to be a potential increase in export data and text size, but the practical impact of this seems to be negligible: │ before │ after │ │ bytes │ bytes vs base │ Go/binary 15.12Mi ± 0% 15.14Mi ± 0% +0.16% (n=1) Go/text 5.220Mi ± 0% 5.237Mi ± 0% +0.32% (n=1) Compile/binary 22.92Mi ± 0% 22.94Mi ± 0% +0.07% (n=1) Compile/text 8.428Mi ± 0% 8.435Mi ± 0% +0.08% (n=1) Change-Id: I5f75fcceb177f05853996b75184a486528eafe96 Reviewed-on: https://go-review.googlesource.com/c/go/+/492017 Reviewed-by: Matthew Dempsky <mdempsky@google.com> TryBot-Result: Gopher Robot <gobot@golang.org> Run-TryBot: Than McIntosh <thanm@google.com> Reviewed-by: Cherry Mui <cherryyz@google.com> Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Diffstat (limited to 'test')
-rw-r--r--test/closure3.dir/main.go26
1 files changed, 15 insertions, 11 deletions
diff --git a/test/closure3.dir/main.go b/test/closure3.dir/main.go
index 4d02a4d10e..04a669206e 100644
--- a/test/closure3.dir/main.go
+++ b/test/closure3.dir/main.go
@@ -232,15 +232,15 @@ func main() {
{
c := 3
- func() { // ERROR "func literal does not escape"
+ func() { // ERROR "can inline main.func26"
c = 4
- func() { // ERROR "func literal does not escape"
+ func() {
if c != 4 {
ppanic("c != 4")
}
recover() // prevent inlining
}()
- }()
+ }() // ERROR "inlining call to main.func26" "func literal does not escape"
if c != 4 {
ppanic("c != 4")
}
@@ -248,33 +248,37 @@ func main() {
{
a := 2
- if r := func(x int) int { // ERROR "func literal does not escape"
+ // This has an unfortunate exponential growth, where as we visit each
+ // function, we inline the inner closure, and that constructs a new
+ // function for any closures inside the inner function, and then we
+ // revisit those. E.g., func34 and func36 are constructed by the inliner.
+ if r := func(x int) int { // ERROR "can inline main.func27"
b := 3
- return func(y int) int { // ERROR "can inline main.func27.1"
+ return func(y int) int { // ERROR "can inline main.func27.1" "can inline main.func34"
c := 5
- return func(z int) int { // ERROR "can inline main.func27.1.1" "can inline main.func27.(func)?2"
+ return func(z int) int { // ERROR "can inline main.func27.1.1" "can inline main.func27.(func)?2" "can inline main.func34.1" "can inline main.func36"
return a*x + b*y + c*z
}(10) // ERROR "inlining call to main.func27.1.1"
}(100) // ERROR "inlining call to main.func27.1" "inlining call to main.func27.(func)?2"
- }(1000); r != 2350 {
+ }(1000); r != 2350 { // ERROR "inlining call to main.func27" "inlining call to main.func34" "inlining call to main.func36"
ppanic("r != 2350")
}
}
{
a := 2
- if r := func(x int) int { // ERROR "func literal does not escape"
+ if r := func(x int) int { // ERROR "can inline main.func28"
b := 3
- return func(y int) int { // ERROR "can inline main.func28.1"
+ return func(y int) int { // ERROR "can inline main.func28.1" "can inline main.func35"
c := 5
- func(z int) { // ERROR "can inline main.func28.1.1" "can inline main.func28.(func)?2"
+ func(z int) { // ERROR "can inline main.func28.1.1" "can inline main.func28.(func)?2" "can inline main.func35.1" "can inline main.func37"
a = a * x
b = b * y
c = c * z
}(10) // ERROR "inlining call to main.func28.1.1"
return a + c
}(100) + b // ERROR "inlining call to main.func28.1" "inlining call to main.func28.(func)?2"
- }(1000); r != 2350 {
+ }(1000); r != 2350 { // ERROR "inlining call to main.func28" "inlining call to main.func35" "inlining call to main.func37"
ppanic("r != 2350")
}
if a != 2000 {