Source file test/newinline.go

     1  // errorcheckwithauto -0 -m -d=inlfuncswithclosures=1
     2  
     3  //go:build goexperiment.newinliner
     4  
     5  // Copyright 2023 The Go Authors. All rights reserved.
     6  // Use of this source code is governed by a BSD-style
     7  // license that can be found in the LICENSE file.
     8  
     9  // Test, using compiler diagnostic flags, that inlining is working.
    10  // Compiles but does not run.
    11  
    12  package foo
    13  
    14  import (
    15  	"errors"
    16  	"runtime"
    17  	"unsafe"
    18  )
    19  
    20  func add2(p *byte, n uintptr) *byte { // ERROR "can inline add2" "leaking param: p to result"
    21  	return (*byte)(add1(unsafe.Pointer(p), n)) // ERROR "inlining call to add1"
    22  }
    23  
    24  func add1(p unsafe.Pointer, x uintptr) unsafe.Pointer { // ERROR "can inline add1" "leaking param: p to result"
    25  	return unsafe.Pointer(uintptr(p) + x)
    26  }
    27  
    28  func f(x *byte) *byte { // ERROR "can inline f" "leaking param: x to result"
    29  	return add2(x, 1) // ERROR "inlining call to add2" "inlining call to add1"
    30  }
    31  
    32  //go:noinline
    33  func g(x int) int {
    34  	return x + 1
    35  }
    36  
    37  func h(x int) int { // ERROR "can inline h"
    38  	return x + 2
    39  }
    40  
    41  func i(x int) int { // ERROR "can inline i"
    42  	const y = 2
    43  	return x + y
    44  }
    45  
    46  func j(x int) int { // ERROR "can inline j"
    47  	switch {
    48  	case x > 0:
    49  		return x + 2
    50  	default:
    51  		return x + 1
    52  	}
    53  }
    54  
    55  func f2() int { // ERROR "can inline f2"
    56  	tmp1 := h
    57  	tmp2 := tmp1
    58  	return tmp2(0) // ERROR "inlining call to h"
    59  }
    60  
    61  var abc = errors.New("abc") // ERROR "inlining call to errors.New"
    62  
    63  var somethingWrong error
    64  
    65  // local closures can be inlined
    66  func l(x, y int) (int, int, error) { // ERROR "can inline l"
    67  	e := func(err error) (int, int, error) { // ERROR "can inline l.func1" "func literal does not escape" "leaking param: err to result"
    68  		return 0, 0, err
    69  	}
    70  	if x == y {
    71  		e(somethingWrong) // ERROR "inlining call to l.func1"
    72  	} else {
    73  		f := e
    74  		f(nil) // ERROR "inlining call to l.func1"
    75  	}
    76  	return y, x, nil
    77  }
    78  
    79  // any re-assignment prevents closure inlining
    80  func m() int {
    81  	foo := func() int { return 1 } // ERROR "can inline m.func1" "func literal does not escape"
    82  	x := foo()
    83  	foo = func() int { return 2 } // ERROR "can inline m.func2" "func literal does not escape"
    84  	return x + foo()
    85  }
    86  
    87  // address taking prevents closure inlining
    88  func n() int { // ERROR "can inline n"
    89  	foo := func() int { return 1 } // ERROR "can inline n.func1" "func literal does not escape"
    90  	bar := &foo
    91  	x := (*bar)() + foo()
    92  	return x
    93  }
    94  
    95  // make sure assignment inside closure is detected
    96  func o() int { // ERROR "can inline o"
    97  	foo := func() int { return 1 } // ERROR "can inline o.func1" "func literal does not escape"
    98  	func(x int) {                  // ERROR "can inline o.func2"
    99  		if x > 10 {
   100  			foo = func() int { return 2 } // ERROR "can inline o.func2"
   101  		}
   102  	}(11) // ERROR "func literal does not escape" "inlining call to o.func2"
   103  	return foo()
   104  }
   105  
   106  func p() int { // ERROR "can inline p"
   107  	return func() int { return 42 }() // ERROR "can inline p.func1" "inlining call to p.func1"
   108  }
   109  
   110  func q(x int) int { // ERROR "can inline q"
   111  	foo := func() int { return x * 2 } // ERROR "can inline q.func1" "func literal does not escape"
   112  	return foo()                       // ERROR "inlining call to q.func1"
   113  }
   114  
   115  func r(z int) int { // ERROR "can inline r"
   116  	foo := func(x int) int { // ERROR "can inline r.func1" "func literal does not escape"
   117  		return x + z
   118  	}
   119  	bar := func(x int) int { // ERROR "func literal does not escape" "can inline r.func2"
   120  		return x + func(y int) int { // ERROR "can inline r.func2.1" "can inline r.r.func2.func3"
   121  			return 2*y + x*z
   122  		}(x) // ERROR "inlining call to r.func2.1"
   123  	}
   124  	return foo(42) + bar(42) // ERROR "inlining call to r.func1" "inlining call to r.func2" "inlining call to r.r.func2.func3"
   125  }
   126  
   127  func s0(x int) int { // ERROR "can inline s0"
   128  	foo := func() { // ERROR "can inline s0.func1" "func literal does not escape"
   129  		x = x + 1
   130  	}
   131  	foo() // ERROR "inlining call to s0.func1"
   132  	return x
   133  }
   134  
   135  func s1(x int) int { // ERROR "can inline s1"
   136  	foo := func() int { // ERROR "can inline s1.func1" "func literal does not escape"
   137  		return x
   138  	}
   139  	x = x + 1
   140  	return foo() // ERROR "inlining call to s1.func1"
   141  }
   142  
   143  func switchBreak(x, y int) int { // ERROR "can inline switchBreak"
   144  	var n int
   145  	switch x {
   146  	case 0:
   147  		n = 1
   148  	Done:
   149  		switch y {
   150  		case 0:
   151  			n += 10
   152  			break Done
   153  		}
   154  		n = 2
   155  	}
   156  	return n
   157  }
   158  
   159  func switchType(x interface{}) int { // ERROR "can inline switchType" "x does not escape"
   160  	switch x.(type) {
   161  	case int:
   162  		return x.(int)
   163  	default:
   164  		return 0
   165  	}
   166  }
   167  
   168  // Test that switches on constant things, with constant cases, only cost anything for
   169  // the case that matches. See issue 50253.
   170  func switchConst1(p func(string)) { // ERROR "can inline switchConst" "p does not escape"
   171  	const c = 1
   172  	switch c {
   173  	case 0:
   174  		p("zero")
   175  	case 1:
   176  		p("one")
   177  	case 2:
   178  		p("two")
   179  	default:
   180  		p("other")
   181  	}
   182  }
   183  
   184  func switchConst2() string { // ERROR "can inline switchConst2"
   185  	switch runtime.GOOS {
   186  	case "linux":
   187  		return "Leenooks"
   188  	case "windows":
   189  		return "Windoze"
   190  	case "darwin":
   191  		return "MackBone"
   192  	case "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "40", "41", "42", "43", "44", "45", "46", "47", "48", "49", "50", "51", "52", "53", "54", "55", "56", "57", "58", "59", "60", "61", "62", "63", "64", "65", "66", "67", "68", "69", "70", "71", "72", "73", "74", "75", "76", "77", "78", "79", "80", "81", "82", "83", "84", "85", "86", "87", "88", "89", "90", "91", "92", "93", "94", "95", "96", "97", "98", "99", "100":
   193  		return "Numbers"
   194  	default:
   195  		return "oh nose!"
   196  	}
   197  }
   198  func switchConst3() string { // ERROR "can inline switchConst3"
   199  	switch runtime.GOOS {
   200  	case "Linux":
   201  		panic("Linux")
   202  	case "Windows":
   203  		panic("Windows")
   204  	case "Darwin":
   205  		panic("Darwin")
   206  	case "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "40", "41", "42", "43", "44", "45", "46", "47", "48", "49", "50", "51", "52", "53", "54", "55", "56", "57", "58", "59", "60", "61", "62", "63", "64", "65", "66", "67", "68", "69", "70", "71", "72", "73", "74", "75", "76", "77", "78", "79", "80", "81", "82", "83", "84", "85", "86", "87", "88", "89", "90", "91", "92", "93", "94", "95", "96", "97", "98", "99", "100":
   207  		panic("Numbers")
   208  	default:
   209  		return "oh nose!"
   210  	}
   211  }
   212  func switchConst4() { // ERROR "can inline switchConst4"
   213  	const intSize = 32 << (^uint(0) >> 63)
   214  	want := func() string { // ERROR "can inline switchConst4.func1"
   215  		switch intSize {
   216  		case 32:
   217  			return "32"
   218  		case 64:
   219  			return "64"
   220  		default:
   221  			panic("unreachable")
   222  		}
   223  	}() // ERROR "inlining call to switchConst4.func1"
   224  	_ = want
   225  }
   226  
   227  func inlineRangeIntoMe(data []int) { // ERROR "can inline inlineRangeIntoMe" "data does not escape"
   228  	rangeFunc(data, 12) // ERROR "inlining call to rangeFunc"
   229  }
   230  
   231  func rangeFunc(xs []int, b int) int { // ERROR "can inline rangeFunc" "xs does not escape"
   232  	for i, x := range xs {
   233  		if x == b {
   234  			return i
   235  		}
   236  	}
   237  	return -1
   238  }
   239  
   240  type T struct{}
   241  
   242  func (T) meth(int, int) {} // ERROR "can inline T.meth"
   243  
   244  func k() (T, int, int) { return T{}, 0, 0 } // ERROR "can inline k"
   245  
   246  func f3() { // ERROR "can inline f3"
   247  	T.meth(k()) // ERROR "inlining call to k" "inlining call to T.meth"
   248  	// ERRORAUTO "inlining call to T.meth"
   249  }
   250  
   251  func small1() { // ERROR "can inline small1"
   252  	runtime.GC()
   253  }
   254  func small2() int { // ERROR "can inline small2"
   255  	return runtime.GOMAXPROCS(0)
   256  }
   257  func small3(t T) { // ERROR "can inline small3"
   258  	t.meth2(3, 5)
   259  }
   260  func small4(t T) { // ERROR "can inline small4"
   261  	t.meth2(runtime.GOMAXPROCS(0), 5)
   262  }
   263  func (T) meth2(int, int) { // ERROR "can inline T.meth2"
   264  	runtime.GC()
   265  	runtime.GC()
   266  }
   267  
   268  // Issue #29737 - make sure we can do inlining for a chain of recursive functions
   269  func ee() { // ERROR "can inline ee"
   270  	ff(100) // ERROR "inlining call to ff" "inlining call to gg" "inlining call to hh"
   271  }
   272  
   273  func ff(x int) { // ERROR "can inline ff"
   274  	if x < 0 {
   275  		return
   276  	}
   277  	gg(x - 1) // ERROR "inlining call to gg" "inlining call to hh"
   278  }
   279  func gg(x int) { // ERROR "can inline gg"
   280  	hh(x - 1) // ERROR "inlining call to hh" "inlining call to ff"
   281  }
   282  func hh(x int) { // ERROR "can inline hh"
   283  	ff(x - 1) // ERROR "inlining call to ff" "inlining call to gg"
   284  }
   285  
   286  // Issue #14768 - make sure we can inline for loops.
   287  func for1(fn func() bool) { // ERROR "can inline for1" "fn does not escape"
   288  	for {
   289  		if fn() {
   290  			break
   291  		} else {
   292  			continue
   293  		}
   294  	}
   295  }
   296  
   297  func for2(fn func() bool) { // ERROR "can inline for2" "fn does not escape"
   298  Loop:
   299  	for {
   300  		if fn() {
   301  			break Loop
   302  		} else {
   303  			continue Loop
   304  		}
   305  	}
   306  }
   307  
   308  // Issue #18493 - make sure we can do inlining of functions with a method value
   309  type T1 struct{}
   310  
   311  func (a T1) meth(val int) int { // ERROR "can inline T1.meth"
   312  	return val + 5
   313  }
   314  
   315  func getMeth(t1 T1) func(int) int { // ERROR "can inline getMeth"
   316  	return t1.meth // ERROR "t1.meth escapes to heap"
   317  	// ERRORAUTO "inlining call to T1.meth"
   318  }
   319  
   320  func ii() { // ERROR "can inline ii"
   321  	var t1 T1
   322  	f := getMeth(t1) // ERROR "inlining call to getMeth" "t1.meth does not escape"
   323  	_ = f(3)
   324  }
   325  
   326  // Issue #42194 - make sure that functions evaluated in
   327  // go and defer statements can be inlined.
   328  func gd1(int) {
   329  	defer gd1(gd2()) // ERROR "inlining call to gd2" "can inline gd1.deferwrap1"
   330  	defer gd3()()    // ERROR "inlining call to gd3"
   331  	go gd1(gd2())    // ERROR "inlining call to gd2" "can inline gd1.gowrap2"
   332  	go gd3()()       // ERROR "inlining call to gd3"
   333  }
   334  
   335  func gd2() int { // ERROR "can inline gd2"
   336  	return 1
   337  }
   338  
   339  func gd3() func() { // ERROR "can inline gd3"
   340  	return ii
   341  }
   342  
   343  // Issue #42788 - ensure ODEREF OCONVNOP* OADDR is low cost.
   344  func EncodeQuad(d []uint32, x [6]float32) { // ERROR "can inline EncodeQuad" "d does not escape"
   345  	_ = d[:6]
   346  	d[0] = float32bits(x[0]) // ERROR "inlining call to float32bits"
   347  	d[1] = float32bits(x[1]) // ERROR "inlining call to float32bits"
   348  	d[2] = float32bits(x[2]) // ERROR "inlining call to float32bits"
   349  	d[3] = float32bits(x[3]) // ERROR "inlining call to float32bits"
   350  	d[4] = float32bits(x[4]) // ERROR "inlining call to float32bits"
   351  	d[5] = float32bits(x[5]) // ERROR "inlining call to float32bits"
   352  }
   353  
   354  // float32bits is a copy of math.Float32bits to ensure that
   355  // these tests pass with `-gcflags=-l`.
   356  func float32bits(f float32) uint32 { // ERROR "can inline float32bits"
   357  	return *(*uint32)(unsafe.Pointer(&f))
   358  }
   359  
   360  // Ensure OCONVNOP is zero cost.
   361  func Conv(v uint64) uint64 { // ERROR "can inline Conv"
   362  	return conv2(conv2(conv2(v))) // ERROR "inlining call to (conv1|conv2)"
   363  }
   364  func conv2(v uint64) uint64 { // ERROR "can inline conv2"
   365  	return conv1(conv1(conv1(conv1(v)))) // ERROR "inlining call to conv1"
   366  }
   367  func conv1(v uint64) uint64 { // ERROR "can inline conv1"
   368  	return uint64(uint64(uint64(uint64(uint64(uint64(uint64(uint64(uint64(uint64(uint64(v)))))))))))
   369  }
   370  
   371  func select1(x, y chan bool) int { // ERROR "can inline select1" "x does not escape" "y does not escape"
   372  	select {
   373  	case <-x:
   374  		return 1
   375  	case <-y:
   376  		return 2
   377  	}
   378  }
   379  
   380  func select2(x, y chan bool) { // ERROR "can inline select2" "x does not escape" "y does not escape"
   381  loop: // test that labeled select can be inlined.
   382  	select {
   383  	case <-x:
   384  		break loop
   385  	case <-y:
   386  	}
   387  }
   388  
   389  func inlineSelect2(x, y chan bool) { // ERROR "can inline inlineSelect2" ERROR "x does not escape" "y does not escape"
   390  loop:
   391  	for i := 0; i < 5; i++ {
   392  		if i == 3 {
   393  			break loop
   394  		}
   395  		select2(x, y) // ERROR "inlining call to select2"
   396  	}
   397  }
   398  

View as plain text