Skip to content

Commit 13baf4b

Browse files
committed
cmd/compile: encourage inlining of functions with single-call bodies
This is a simple tweak to allow a bit more mid-stack inlining. In cases like this: func f() { g() } We'd really like to inline f into its callers. It can't hurt. We implement this optimization by making calls a bit cheaper, enough to afford a single call in the function body, but not 2. The remaining budget allows for some argument modification, or perhaps a wrapping conditional: func f(x int) { g(x, 0) } func f(x int) { if x > 0 { g() } } Update #19348 Change-Id: Ifb1ea0dd1db216c3fd5c453c31c3355561fe406f Reviewed-on: https://go-review.googlesource.com/c/147361 Reviewed-by: Austin Clements <[email protected]> Reviewed-by: David Chase <[email protected]>
1 parent be5f646 commit 13baf4b

File tree

8 files changed

+43
-13
lines changed

8 files changed

+43
-13
lines changed

src/cmd/compile/internal/gc/inl.go

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,10 @@ import (
3838
const (
3939
inlineMaxBudget = 80
4040
inlineExtraAppendCost = 0
41-
inlineExtraCallCost = inlineMaxBudget // default is do not inline, -l=4 enables by using 1 instead.
42-
inlineExtraPanicCost = 1 // do not penalize inlining panics.
43-
inlineExtraThrowCost = inlineMaxBudget // with current (2018-05/1.11) code, inlining runtime.throw does not help.
41+
// default is to inline if there's at most one call. -l=4 overrides this by using 1 instead.
42+
inlineExtraCallCost = inlineMaxBudget * 3 / 4
43+
inlineExtraPanicCost = 1 // do not penalize inlining panics.
44+
inlineExtraThrowCost = inlineMaxBudget // with current (2018-05/1.11) code, inlining runtime.throw does not help.
4445

4546
inlineBigFunctionNodes = 5000 // Functions with this many nodes are considered "big".
4647
inlineBigFunctionMaxCost = 20 // Max cost of inlinee when inlining into a "big" function.
@@ -141,6 +142,13 @@ func caninl(fn *Node) {
141142
return
142143
}
143144

145+
// If marked as "go:uintptrescapes", don't inline, since the
146+
// escape information is lost during inlining.
147+
if fn.Func.Pragma&UintptrEscapes != 0 {
148+
reason = "marked as having an escaping uintptr argument"
149+
return
150+
}
151+
144152
// The nowritebarrierrec checker currently works at function
145153
// granularity, so inlining yeswritebarrierrec functions can
146154
// confuse it (#22342). As a workaround, disallow inlining

src/runtime/extern.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,7 @@ func Caller(skip int) (pc uintptr, file string, line int, ok bool) {
202202
// directly is discouraged, as is using FuncForPC on any of the
203203
// returned PCs, since these cannot account for inlining or return
204204
// program counter adjustment.
205+
//go:noinline
205206
func Callers(skip int, pc []uintptr) int {
206207
// runtime.callers uses pc.array==nil as a signal
207208
// to print a stack trace. Pick off 0-length pc here

src/runtime/runtime-gdb_test.go

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -181,12 +181,11 @@ func testGdbPython(t *testing.T, cgo bool) {
181181
}
182182
args = append(args,
183183
"-ex", "set python print-stack full",
184-
"-ex", "br fmt.Println",
184+
"-ex", "br main.go:15",
185185
"-ex", "run",
186186
"-ex", "echo BEGIN info goroutines\n",
187187
"-ex", "info goroutines",
188188
"-ex", "echo END\n",
189-
"-ex", "up", // up from fmt.Println to main
190189
"-ex", "echo BEGIN print mapvar\n",
191190
"-ex", "print mapvar",
192191
"-ex", "echo END\n",
@@ -196,14 +195,13 @@ func testGdbPython(t *testing.T, cgo bool) {
196195
"-ex", "echo BEGIN info locals\n",
197196
"-ex", "info locals",
198197
"-ex", "echo END\n",
199-
"-ex", "down", // back to fmt.Println (goroutine 2 below only works at bottom of stack. TODO: fix that)
200198
"-ex", "echo BEGIN goroutine 1 bt\n",
201199
"-ex", "goroutine 1 bt",
202200
"-ex", "echo END\n",
203201
"-ex", "echo BEGIN goroutine 2 bt\n",
204202
"-ex", "goroutine 2 bt",
205203
"-ex", "echo END\n",
206-
"-ex", "clear fmt.Println", // clear the previous break point
204+
"-ex", "clear main.go:15", // clear the previous break point
207205
"-ex", fmt.Sprintf("br main.go:%d", nLines), // new break point at the end of main
208206
"-ex", "c",
209207
"-ex", "echo BEGIN goroutine 1 bt at the end\n",
@@ -274,7 +272,7 @@ func testGdbPython(t *testing.T, cgo bool) {
274272
t.Fatalf("info locals failed: %s", bl)
275273
}
276274

277-
btGoroutine1Re := regexp.MustCompile(`(?m)^#0\s+(0x[0-9a-f]+\s+in\s+)?fmt\.Println.+at`)
275+
btGoroutine1Re := regexp.MustCompile(`(?m)^#0\s+(0x[0-9a-f]+\s+in\s+)?main\.main.+at`)
278276
if bl := blocks["goroutine 1 bt"]; !btGoroutine1Re.MatchString(bl) {
279277
t.Fatalf("goroutine 1 bt failed: %s", bl)
280278
}

src/runtime/stack_test.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -595,6 +595,9 @@ func (s structWithMethod) callers() []uintptr {
595595
return pc[:Callers(0, pc)]
596596
}
597597

598+
// The noinline prevents this function from being inlined
599+
// into a wrapper. TODO: remove this when issue 28640 is fixed.
600+
//go:noinline
598601
func (s structWithMethod) stack() string {
599602
buf := make([]byte, 4<<10)
600603
return string(buf[:Stack(buf, false)])

test/closure3.dir/main.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,8 @@ func main() {
238238
if c != 4 {
239239
ppanic("c != 4")
240240
}
241+
for i := 0; i < 10; i++ { // prevent inlining
242+
}
241243
}()
242244
}()
243245
if c != 4 {

test/fixedbugs/issue7921.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ func bufferNoEscape4() []byte {
4646
return b.Bytes() // ERROR "inlining call" "b does not escape"
4747
}
4848

49-
func bufferNoEscape5() {
49+
func bufferNoEscape5() { // ERROR "can inline bufferNoEscape5"
5050
b := bytes.NewBuffer(make([]byte, 0, 128)) // ERROR "inlining call" "make\(\[\]byte, 0, 128\) does not escape" "&bytes.Buffer literal does not escape"
5151
useBuffer(b)
5252
}

test/inline.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ package foo
1111

1212
import (
1313
"errors"
14+
"runtime"
1415
"unsafe"
1516
)
1617

@@ -162,3 +163,20 @@ func k() (T, int, int) { return T{}, 0, 0 } // ERROR "can inline k"
162163
func _() { // ERROR "can inline _"
163164
T.meth(k()) // ERROR "inlining call to k" "inlining call to T.meth"
164165
}
166+
167+
func small1() { // ERROR "can inline small1"
168+
runtime.GC()
169+
}
170+
func small2() int { // ERROR "can inline small2"
171+
return runtime.GOMAXPROCS(0)
172+
}
173+
func small3(t T) { // ERROR "can inline small3"
174+
t.meth2(3, 5)
175+
}
176+
func small4(t T) { // not inlineable - has 2 calls.
177+
t.meth2(runtime.GOMAXPROCS(0), 5)
178+
}
179+
func (T) meth2(int, int) { // not inlineable - has 2 calls.
180+
runtime.GC()
181+
runtime.GC()
182+
}

test/live_syscall.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,23 +17,23 @@ import (
1717

1818
func f(uintptr) // ERROR "f assuming arg#1 is unsafe uintptr"
1919

20-
func g() {
20+
func g() { // ERROR "can inline g"
2121
var t int
2222
f(uintptr(unsafe.Pointer(&t))) // ERROR "live at call to f: .?autotmp" "g &t does not escape" "stack object .autotmp_[0-9]+ unsafe.Pointer$"
2323
}
2424

25-
func h() {
25+
func h() { // ERROR "can inline h"
2626
var v int
2727
syscall.Syscall(0, 1, uintptr(unsafe.Pointer(&v)), 2) // ERROR "live at call to Syscall: .?autotmp" "h &v does not escape" "stack object .autotmp_[0-9]+ unsafe.Pointer$"
2828
}
2929

30-
func i() {
30+
func i() { // ERROR "can inline i"
3131
var t int
3232
p := unsafe.Pointer(&t) // ERROR "i &t does not escape"
3333
f(uintptr(p)) // ERROR "live at call to f: .?autotmp" "stack object .autotmp_[0-9]+ unsafe.Pointer$"
3434
}
3535

36-
func j() {
36+
func j() { // ERROR "can inline j"
3737
var v int
3838
p := unsafe.Pointer(&v) // ERROR "j &v does not escape"
3939
syscall.Syscall(0, 1, uintptr(p), 2) // ERROR "live at call to Syscall: .?autotmp" "stack object .autotmp_[0-9]+ unsafe.Pointer$"

0 commit comments

Comments
 (0)