1
0
mirror of https://github.com/golang/go synced 2024-11-17 11:54:54 -07:00
go/test/closure3.dir/main.go
Than McIntosh 39986d28e4 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 since fixed in CL 482355.]

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)

Updates #56102.

Change-Id: I1f4fc96c71609c8feb59fecdb92b69ba7e3b5b41
Reviewed-on: https://go-review.googlesource.com/c/go/+/482356
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Run-TryBot: Than McIntosh <thanm@google.com>
Reviewed-by: Cherry Mui <cherryyz@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
2023-04-07 15:12:08 +00:00

294 lines
8.0 KiB
Go

// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Check correctness of various closure corner cases
// that are expected to be inlined
package main
var ok bool
var sink int
func main() {
{
if x := func() int { // ERROR "can inline main.func1"
return 1
}(); x != 1 { // ERROR "inlining call to main.func1"
ppanic("x != 1")
}
if x := func() int { // ERROR "can inline main.func2" "func literal does not escape"
return 1
}; x() != 1 { // ERROR "inlining call to main.func2"
ppanic("x() != 1")
}
}
{
if y := func(x int) int { // ERROR "can inline main.func3"
return x + 2
}(40); y != 42 { // ERROR "inlining call to main.func3"
ppanic("y != 42")
}
if y := func(x int) int { // ERROR "can inline main.func4" "func literal does not escape"
return x + 2
}; y(40) != 42 { // ERROR "inlining call to main.func4"
ppanic("y(40) != 42")
}
}
{
y := func(x int) int { // ERROR "can inline main.func5" "func literal does not escape"
return x + 2
}
y = func(x int) int { // ERROR "can inline main.func6" "func literal does not escape"
return x + 1
}
if y(40) != 41 {
ppanic("y(40) != 41")
}
}
{
func() { // ERROR "func literal does not escape"
y := func(x int) int { // ERROR "can inline main.func7.1" "func literal does not escape"
return x + 2
}
y = func(x int) int { // ERROR "can inline main.func7.2" "func literal does not escape"
return x + 1
}
if y(40) != 41 {
ppanic("y(40) != 41")
}
}()
}
{
y := func(x int) int { // ERROR "can inline main.func8" "func literal does not escape"
return x + 2
}
y, sink = func(x int) int { // ERROR "can inline main.func9" "func literal does not escape"
return x + 1
}, 42
if y(40) != 41 {
ppanic("y(40) != 41")
}
}
{
func() { // ERROR "func literal does not escape"
y := func(x int) int { // ERROR "can inline main.func10.1" "func literal does not escape"
return x + 2
}
y, sink = func(x int) int { // ERROR "can inline main.func10.2" "func literal does not escape"
return x + 1
}, 42
if y(40) != 41 {
ppanic("y(40) != 41")
}
}()
}
{
y := func(x int) int { // ERROR "can inline main.func11" "func literal does not escape"
return x + 2
}
y, sink = func() (func(int) int, int) { // ERROR "can inline main.func12"
return func(x int) int { // ERROR "can inline main.func12"
return x + 1
}, 42
}() // ERROR "func literal does not escape" "inlining call to main.func12"
if y(40) != 41 {
ppanic("y(40) != 41")
}
}
{
func() { // ERROR "func literal does not escape"
y := func(x int) int { // ERROR "func literal does not escape" "can inline main.func13.1"
return x + 2
}
y, sink = func() (func(int) int, int) { // ERROR "can inline main.func13.2"
return func(x int) int { // ERROR "can inline main.func13.2"
return x + 1
}, 42
}() // ERROR "func literal does not escape" "inlining call to main.func13.2"
if y(40) != 41 {
ppanic("y(40) != 41")
}
}()
}
{
y := func(x int) int { // ERROR "can inline main.func14" "func literal does not escape"
return x + 2
}
y, ok = map[int]func(int) int{ // ERROR "does not escape"
0: func(x int) int { return x + 1 }, // ERROR "can inline main.func15" "func literal escapes"
}[0]
if y(40) != 41 {
ppanic("y(40) != 41")
}
}
{
func() { // ERROR "func literal does not escape"
y := func(x int) int { // ERROR "can inline main.func16.1" "func literal does not escape"
return x + 2
}
y, ok = map[int]func(int) int{ // ERROR "does not escape"
0: func(x int) int { return x + 1 }, // ERROR "can inline main.func16.2" "func literal escapes"
}[0]
if y(40) != 41 {
ppanic("y(40) != 41")
}
}()
}
{
y := func(x int) int { // ERROR "can inline main.func17" "func literal does not escape"
return x + 2
}
y, ok = interface{}(func(x int) int { // ERROR "can inline main.func18" "does not escape"
return x + 1
}).(func(int) int)
if y(40) != 41 {
ppanic("y(40) != 41")
}
}
{
func() { // ERROR "func literal does not escape"
y := func(x int) int { // ERROR "can inline main.func19.1" "func literal does not escape"
return x + 2
}
y, ok = interface{}(func(x int) int { // ERROR "can inline main.func19.2" "does not escape"
return x + 1
}).(func(int) int)
if y(40) != 41 {
ppanic("y(40) != 41")
}
}()
}
{
x := 42
if y := func() int { // ERROR "can inline main.func20"
return x
}(); y != 42 { // ERROR "inlining call to main.func20"
ppanic("y != 42")
}
if y := func() int { // ERROR "can inline main.func21" "func literal does not escape"
return x
}; y() != 42 { // ERROR "inlining call to main.func21"
ppanic("y() != 42")
}
}
{
x := 42
if z := func(y int) int { // ERROR "can inline main.func22"
return func() int { // ERROR "can inline main.func22.1" "can inline main.func30"
return x + y
}() // ERROR "inlining call to main.func22.1"
}(1); z != 43 { // ERROR "inlining call to main.func22" "inlining call to main.func30"
ppanic("z != 43")
}
if z := func(y int) int { // ERROR "func literal does not escape" "can inline main.func23"
return func() int { // ERROR "can inline main.func23.1" "can inline main.func31"
return x + y
}() // ERROR "inlining call to main.func23.1"
}; z(1) != 43 { // ERROR "inlining call to main.func23" "inlining call to main.func31"
ppanic("z(1) != 43")
}
}
{
a := 1
func() { // ERROR "can inline main.func24"
func() { // ERROR "can inline main.func24" "can inline main.func32"
a = 2
}() // ERROR "inlining call to main.func24"
}() // ERROR "inlining call to main.func24" "inlining call to main.func32"
if a != 2 {
ppanic("a != 2")
}
}
{
b := 2
func(b int) { // ERROR "func literal does not escape"
func() { // ERROR "can inline main.func25.1"
b = 3
}() // ERROR "inlining call to main.func25.1"
if b != 3 {
ppanic("b != 3")
}
}(b)
if b != 2 {
ppanic("b != 2")
}
}
{
c := 3
func() { // ERROR "can inline main.func26"
c = 4
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")
}
}
{
a := 2
// 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" "can inline main.func34"
c := 5
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 { // 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 "can inline main.func28"
b := 3
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" "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 { // ERROR "inlining call to main.func28" "inlining call to main.func35" "inlining call to main.func37"
ppanic("r != 2350")
}
if a != 2000 {
ppanic("a != 2000")
}
}
}
//go:noinline
func ppanic(s string) { // ERROR "leaking param: s"
panic(s) // ERROR "s escapes to heap"
}